{"_id":"@drguptavivek/who-2022-va","name":"@drguptavivek/who-2022-va","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@drguptavivek/who-2022-va","version":"0.1.0","type":"module","description":"Independent JavaScript implementation of the 2022 WHO verbal autopsy instrument.","license":"MIT","author":{"name":"Vivek Gupta"},"repository":{"type":"git","url":"git+https://github.com/drguptavivek/WHO-va-2022.git"},"homepage":"https://github.com/drguptavivek/WHO-va-2022#readme","bugs":{"url":"https://github.com/drguptavivek/WHO-va-2022/issues"},"keywords":["verbal-autopsy","epidemiology","public-health","react","react-native","web-component"],"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./native":{"types":"./dist/native.d.ts","react-native":"./dist/native.js","import":"./dist/native.js"},"./web":{"types":"./dist/web.d.ts","browser":"./dist/web.js","import":"./dist/web.js"},"./web-component":{"types":"./dist/web-component.d.ts","browser":"./dist/web-component.js","import":"./dist/web-component.js"}},"scripts":{"dev":"vite demo --host 127.0.0.1","build:demo":"vite build demo","test":"vitest run","test:watch":"vitest","test:e2e":"playwright test","test:e2e:headed":"E2E_SLOW_MO=${E2E_SLOW_MO:-800} playwright test --headed --workers=1","test:e2e:report":"playwright show-report","typecheck":"tsc --noEmit","build":"tsup","check":"pnpm typecheck && pnpm test && pnpm build","prepublishOnly":"pnpm check"},"publishConfig":{"access":"public"},"engines":{"node":">=18"},"peerDependencies":{"react":">=18","react-dom":">=18","react-native":">=0.73","react-native-web":">=0.19"},"peerDependenciesMeta":{"react":{"optional":true},"react-dom":{"optional":true},"react-native":{"optional":true},"react-native-web":{"optional":true}},"devDependencies":{"@playwright/test":"^1.61.1","@types/node":"^24.0.0","@types/react":"^19.2.0","@types/react-dom":"^19.2.0","@types/react-native-web":"0.19.2","exceljs":"^4.4.0","jsdom":"^29.1.1","react":"^19.2.7","react-dom":"^19.2.7","react-native":"^0.86.0","react-native-web":"^0.21.2","tsup":"^8.5.1","tsx":"^4.20.0","typescript":"^5.9.3","vite":"^8.1.5","vitest":"^4.1.10"},"packageManager":"pnpm@11.9.0","dependencies":{"pdfjs-dist":"^6.1.200"},"gitHead":"8fee02bd8e2ecf878d9c94c4e15b8e6d02556142","_id":"@drguptavivek/who-2022-va@0.1.0","_nodeVersion":"24.15.0","_npmVersion":"11.14.1","dist":{"integrity":"sha512-zyYDF0nBp+mBA2Y1O8d2aMn79SzZJahut9dP6h6T3LWx6SFAX3Y1hxyO16aUxZo3GYFzmX17gAyVr1e9UN55BA==","shasum":"833138924d1ae357845e853eb2656a053c6be916","tarball":"https://registry.npmjs.org/@drguptavivek/who-2022-va/-/who-2022-va-0.1.0.tgz","fileCount":28,"unpackedSize":3406466,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCOcbNTptyN3yVkV4klMYSi3BUyGa3x/RRDIQGOlJNH0gIgQVQ4m91pMiLhp0nGB3Y5LvYByCvcn6VK4Dsxne9BZTs="}]},"_npmUser":{"name":"drguptavivek","email":"drguptavivek@yahoo.com"},"directories":{},"maintainers":[{"name":"drguptavivek","email":"drguptavivek@yahoo.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/who-2022-va_0.1.0_1784346888935_0.18314449171445246"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-18T03:54:48.844Z","0.1.0":"2026-07-18T03:54:49.102Z","modified":"2026-07-18T03:54:49.296Z"},"maintainers":[{"name":"drguptavivek","email":"drguptavivek@yahoo.com"}],"description":"Independent JavaScript implementation of the 2022 WHO verbal autopsy instrument.","homepage":"https://github.com/drguptavivek/WHO-va-2022#readme","keywords":["verbal-autopsy","epidemiology","public-health","react","react-native","web-component"],"repository":{"type":"git","url":"git+https://github.com/drguptavivek/WHO-va-2022.git"},"author":{"name":"Vivek Gupta"},"bugs":{"url":"https://github.com/drguptavivek/WHO-va-2022/issues"},"license":"MIT","readme":"# 2022 WHO Verbal Autopsy Instrument for JavaScript\n\nAn independent, React Native-first implementation of the **2022 WHO Verbal Autopsy instrument V1.1**. One checked-in, platform-neutral JSON contract drives Expo, React Native, React Native Web, non-React websites, field validation, and submission validation.\n\nThis project is not affiliated with, sponsored by, or endorsed by the World Health Organization (WHO). “WHO” identifies the source instrument only. See [Licensing and attribution](#licensing-and-attribution).\n\nThe runtime and package build do **not** use SurveyJS, Excel, or XLSForm generation. The WHO workbook and its `exceljs` compiler are retained only as development fixtures for source-conformance tests; they never write the canonical JSON contract.\n\n## Documentation\n\n- [Developer guide](https://github.com/drguptavivek/WHO-va-2022/blob/main/docs/development.md) — setup, repository map, common workflows, testing, and contribution rules\n- [API reference](https://github.com/drguptavivek/WHO-va-2022/blob/main/docs/api.md) — entry points and the main headless, form, draft, localization, and attachment APIs\n- [Architecture](https://github.com/drguptavivek/WHO-va-2022/blob/main/docs/architecture.md) — executable contract, runtime boundaries, shared behavior, and platform services\n- [Attachment processing](https://github.com/drguptavivek/WHO-va-2022/blob/main/docs/attachments.md) — validation policy, browser/native lifecycles, and the server boundary\n\n## Installation\n\n```bash\nnpm install @drguptavivek/who-2022-va\n```\n\nThe headless entry point has no UI peer requirement. Install the peers for the UI entry point you use:\n\n```bash\n# React web or the web component\nnpm install react react-dom react-native-web\n\n# Expo / React Native (normally already supplied by the host application)\nnpm install react react-native\n```\n\n![2022 WHO Verbal Autopsy form in English](https://raw.githubusercontent.com/drguptavivek/WHO-va-2022/main/docs/images/form-english.jpg)\n\n## Package entry points\n\n| Import | Purpose |\n| --- | --- |\n| `@drguptavivek/who-2022-va` | Headless instrument, expression, session, and submission APIs |\n| `@drguptavivek/who-2022-va/native` | Expo and React Native `WhoVaForm` |\n| `@drguptavivek/who-2022-va/web` | React web `WhoVaForm`, rendered through React Native Web |\n| `@drguptavivek/who-2022-va/web-component` | `<who-va-2022-form>` wrapper for non-React web apps |\n\n## Runtime performance\n\nThe canonical JSON remains one offline artifact, but the `/native` and `/web` form entry points no longer parse it when the application bundle starts. They dynamically load and cache it when a form first needs the default instrument. The root `@drguptavivek/who-2022-va` entry keeps the synchronous `whoVa2022Instrument` export for headless and server compatibility; applications concerned about startup cost can import `loadWhoVa2022Instrument` instead.\n\nOpening the first default form still parses the complete instrument once. The runtime does not fetch a section at a time: relevance, calculations, navigation, and final validation can refer to questions in other sections, and the instrument must continue working completely offline. This trades a single predictable parse for simpler, reliable interviews; only the current section's controls are rendered. Further section chunking should be considered only if profiling the target low-spec devices shows this deferred parse is still material.\n\nOnce an instrument is used, the runtime builds shared indexes for questions by name, questions by section, sections by name, and calculated questions.\n\nIndexes are cached by instrument identity in a `WeakMap`. Sessions and translated instruments can be released normally, while repeated lookups avoid scanning all 450 questions.\n\nCalculations run once after an answer changes and once for full submission validation. Relevance checks reuse that calculated state instead of recalculating the 38 derived fields for every question.\n\nSession snapshots compute the visible section list once and render only its current questions. Answer-preview filtering is memoized and runs only while the preview is open.\n\nField validation remains real time. Type, choice, and constraint errors appear as the interviewer enters an answer and clear immediately after correction; Next and Complete still perform section or full-form validation.\n\nHost-provided language modules are loaded on demand. Translation application structurally shares every unchanged section, question, and choice with the English instrument. JavaScript runtimes normally retain imported language modules in their module cache; applications can bound the loader's translated-instrument cache independently.\n\n## Expo / React Native\n\n```tsx\nimport { SafeAreaView } from \"react-native\";\nimport { WhoVaForm, type WhoVaDraftStore } from \"@drguptavivek/who-2022-va/native\";\n\nconst draftStore: WhoVaDraftStore = {\n  async save(draft) {\n    // Use Expo SQLite, AsyncStorage, MMKV, or the app database here.\n    await appStorage.set(`who-va-2022:draft:${draft.id}`, JSON.stringify(draft));\n  }\n};\n\nexport default function App() {\n  return (\n    <SafeAreaView style={{ flex: 1 }}>\n      <WhoVaForm\n        initialData={{ Id10010c: \"INT-001\" }}\n        draftStore={draftStore}\n        platform={{\n          pickDate: async (_question, _data, currentValue) => {\n            // Open the host app's native date picker and return YYYY-MM-DD.\n            return openNativeDatePicker(currentValue);\n          },\n          captureAudio: async () => {\n            // Connect Expo Audio or the host application's recorder here.\n            return { uri: \"file:///recordings/va.m4a\", mimeType: \"audio/mp4\" };\n          },\n          captureImage: async () => openCamera(),\n          selectImage: async () => openImageLibrary(),\n          selectFile: async (_question, _data, acceptedMimeTypes) => {\n            // Connect Expo DocumentPicker and restrict it to acceptedMimeTypes.\n            return openDocumentPicker(acceptedMimeTypes);\n          }\n        }}\n        onDraftSaved={(draft) => console.log(`Saved ${draft.id}`)}\n        onComplete={(result) => {\n          if (result.valid) queueForSubmission(result.data);\n        }}\n      />\n    </SafeAreaView>\n  );\n}\n```\n\nDate picking, audio capture, and draft storage are injected by the host so the module does not force a particular Expo SDK, database, upload service, or file lifecycle. The Save draft button and every Next/Complete press write a UUID-addressed envelope through `draftStore`. If `pickDate` is omitted on native, full-date questions remain usable as validated text inputs with alphabetic, localized months; order follows the locale (`DD-MMM-YYYY`, `MMM-DD-YYYY`, or `YYYY-MMM-DD`). Stored answers always use canonical `YYYY-MM-DD`. Web renderers use the browser's locale-aware native calendar control automatically. Date fields with the WHO `year` appearance, such as `Id10024`, use a four-digit year input rather than the full-date control.\n\n## Reusable question controls\n\nBoth `/web` and `/native` export `WhoVaQuestionControls`. Its named components are `Text`, `Integer`, `Date`, `SingleChoice`, `MultipleChoice`, `Confirm`, `Audio`, `Image`, `File`, `Note`, `Calculated`, and `System`; `Control` dispatches from a question's canonical `control` value. These components accept the same question/value/data/issues/onAnswer contract and can be used outside the full form.\n\nWHO text questions with `appearance: \"multiline\"`, including the detailed open narrative, use a tall multiline input. Image controls support camera/library selection, preview, hide/view, 90-degree rotation, zoom, replace, and remove. File controls request PDF MIME type only and support replace/remove. Web supplies browser selectors; Expo/React Native hosts connect camera, image-library, and document-picker functions through `platform`.\n\nAttachment answers are durable references, not embedded base64 data. Browser images are decoded directly from the selected `Blob`, resized and JPEG-encoded, and stored in IndexedDB; the answer JSON stores only an ID and metadata. Native adapters can provide `inspect(uri)` so the original camera file is decoded by the platform without first copying all of its bytes into the JavaScript heap. The processed native file remains a URI that the host can stream to its upload service.\n\nThe default limits protect low-memory devices: images are capped at 10 MB and 16 megapixels before processing, then reduced to at most 2048 pixels on the longest edge and 2 MB. PDFs are capped at 5 MB and 10 pages; the original PDF is replaced by JPEG page images no larger than 1600 pixels on the longest edge. Host applications may choose stricter image limits.\n\n## React web\n\n```tsx\nimport { WhoVaForm } from \"@drguptavivek/who-2022-va/web\";\n\nexport function VerbalAutopsyPage() {\n  return <WhoVaForm onDraftSaved={(draft) => console.log(draft.id)} onComplete={(result) => submit(result.data)} />;\n}\n```\n\nWeb uses `localStorage` by default under `who-va-2022:draft:<uuid>`. Pass `draftId` to continue overwriting a known draft, or pass a custom `draftStore` to use another persistence layer. Audio questions use the browser microphone: press **Record audio**, then **Stop and save recording**. The browser will request microphone permission, and recording requires a secure context (`https://` or localhost).\n\nUpload a stored browser attachment as a `Blob`; do not convert it to base64:\n\n```ts\nimport { loadWhoVaWebAttachmentBlob } from \"@drguptavivek/who-2022-va/web\";\n\nconst blob = await loadWhoVaWebAttachmentBlob(attachmentReference);\nif (blob) {\n  const body = new FormData();\n  body.append(\"file\", blob, attachmentReference.name);\n  await fetch(\"/api/attachments\", { method: \"POST\", body });\n}\n```\n\nAfter loading all drafts that must remain on the device, orphaned IndexedDB binaries can be removed explicitly:\n\n```ts\nimport { cleanupWhoVaWebAttachments } from \"@drguptavivek/who-2022-va/web\";\n\nawait cleanupWhoVaWebAttachments(allRetainedDrafts.map((draft) => draft.data));\n```\n\nOnly call cleanup with the complete set of retained drafts/answers; omitted references are treated as deleted. Object URLs used for previews are revoked when controls release them.\n\n`localStorage` is unencrypted browser storage. For production VA data, provide a secured storage adapter with the application's encryption, access-control, retention, and device-loss protections.\n\n## Any web application\n\n```ts\nimport { defineWhoVaElement } from \"@drguptavivek/who-2022-va/web-component\";\n\ndefineWhoVaElement();\n```\n\n```html\n<who-va-2022-form locale=\"en\"></who-va-2022-form>\n\n<script type=\"module\">\n  const form = document.querySelector(\"who-va-2022-form\");\n  form.setData(savedDraft);\n  form.addEventListener(\"who-va-draft-saved\", event => console.log(event.detail.id));\n  form.addEventListener(\"who-va-complete\", event => submit(event.detail.data));\n  const assessment = form.validate();\n</script>\n```\n\nThe element exposes `getData()`, `setData(data)`, `getDraftId()`, `validate()`, and `complete()`. Set a `draft-id` attribute before attaching the element to continue a known UUID; otherwise it generates one.\n\n## Adding languages\n\nThe package ships only the unmodified English source instrument as a built-in language. `WHO_VA_2022_LANGUAGES` therefore contains only `en`. Host applications can use the localization APIs with translations for which they have the necessary rights or permissions.\n\nThe preferred runtime layout is one independent file per language. Each file is JSON-serializable and owns that locale's section labels, question text, hints, guidance, choice labels, constraint messages, and optional form UI strings:\n\n```ts\n// languages/fr.ts (languages/fr.json has the same data shape)\nimport type { WhoVaLanguageFile } from \"@drguptavivek/who-2022-va\";\n\nexport default {\n  locale: \"fr\",\n  instrument: {\n    sections: { va_interviewer: \"Enquêteur AV\" },\n    questions: {\n      Id10021: {\n        label: \"Quand la personne décédée est-elle née ?\",\n        hint: \"Utilisez la meilleure date disponible\",\n        constraintMessage: \"Saisissez une date de naissance valide\",\n        choices: { \"1\": \"Oui\", \"0\": \"Non\" }\n      }\n    }\n  },\n  ui: {\n    next: \"Suivant\",\n    required: \"{label} est obligatoire\"\n  }\n} satisfies WhoVaLanguageFile;\n```\n\nRegister dynamic imports once. No language file is downloaded until it is selected; loaded files are cached. A regional request such as `fr-CA` falls back to the `fr` file, then to the base English instrument.\n\n```tsx\nimport { createWhoVaLanguageLoader, whoVa2022Instrument } from \"@drguptavivek/who-2022-va\";\nimport { WhoVaForm } from \"@drguptavivek/who-2022-va/web\";\n\nconst loadLanguage = createWhoVaLanguageLoader(whoVa2022Instrument, {\n  fr: () => import(\"./languages/fr.js\"),\n  sw: () => import(\"./languages/sw.js\")\n});\n\nconst language = await loadLanguage(selectedLocale);\n\n<WhoVaForm\n  instrument={language.instrument}\n  locale={language.locale}\n  uiTranslations={language.uiTranslations}\n/>;\n```\n\nStable question names and choice values are never translated, so saved submissions and branching logic remain compatible across languages. Constraint expressions are also shared; each language file supplies only the interviewer-facing `constraintMessage`.\n\nChanging the loaded `instrument` and `locale` props switches the active language without discarding the current session answers.\n\n### Adding translations manually\n\nXLSForm is not required. A translation may be partial, so another question, hint, choice, or constraint message can be added whenever it becomes available:\n\n```ts\nimport { whoVa2022Instrument, withInstrumentTranslation } from \"@drguptavivek/who-2022-va\";\n\nconst instrument = withInstrumentTranslation(whoVa2022Instrument, \"fr\", {\n  questions: {\n    Id10021: {\n      label: \"Quand la personne décédée est-elle née ?\",\n      hint: \"Utilisez la meilleure date disponible\"\n    },\n    Id10022: {\n      choices: {\n        \"1\": \"Oui\",\n        \"0\": \"Non\"\n      },\n      constraintMessage: \"Sélectionnez une réponse valide\"\n    }\n  }\n});\n```\n\nOnly the supplied fields are added. Existing translations are preserved, untranslated fields fall back to English, and question IDs, choice values, calculations, relevance rules, and stored answers are unchanged. The same `instrument` object can be passed directly to `WhoVaForm`, or the translation object can be placed under `instrument` in a lazy `WhoVaLanguageFile` as shown above.\n\nTranslations of the source questionnaire may be adaptations under its CC BY-ND 3.0 IGO licence. Confirm that you have the necessary permission before distributing a translated instrument.\n\n## Web theming\n\nThe React web renderer and web component expose namespaced CSS custom properties. Set them on `:root`, an application wrapper, or one form instance; no component fork or `!important` override is needed.\n\n```css\n.my-va-page {\n  --who-2022-web-color-brand: #275dad;\n  --who-2022-web-color-brand-deep: #173b70;\n  --who-2022-web-color-brand-soft: #eaf1fb;\n  --who-2022-web-color-canvas: #f5f7fb;\n  --who-2022-web-color-surface: #ffffff;\n  --who-2022-web-color-ink: #172033;\n  --who-2022-web-color-muted: #5c667a;\n  --who-2022-web-color-border: #dce2eb;\n  --who-2022-web-color-control-border: #9aa8bc;\n  --who-2022-web-radius-control: 10px;\n  --who-2022-web-radius-card: 16px;\n  --who-2022-web-form-max-width: 48rem;\n  --who-2022-web-form-padding: clamp(1rem, 2.5vw, 1.5rem);\n}\n```\n\n```html\n<section class=\"my-va-page\">\n  <who-va-2022-form locale=\"en\"></who-va-2022-form>\n</section>\n```\n\nAll web theme properties use the `--who-2022-web-` namespace. Less commonly needed tokens cover guidance, danger, and image-preview colors: `color-ink-subtle`, `color-guidance`, `color-danger`, `color-danger-border`, `color-danger-strong`, `color-danger-soft`, and `color-image-background`.\n\nThe form is mobile-first: it fills the available width with responsive padding, then stops growing at `--who-2022-web-form-max-width` (default `48rem`). The same cap applies in portrait and landscape, preventing long question text and controls from stretching across widescreen displays. Set the token on a wrapper or individual form when a host application needs a narrower measure. `--who-2022-web-form-padding` controls both gutters with one CSS length; `--who-2022-web-form-padding-inline` and `--who-2022-web-form-padding-block` can override the horizontal and vertical gutters independently.\n\n## Headless validation\n\n```ts\nimport { validateSubmission, whoVa2022Instrument } from \"@drguptavivek/who-2022-va\";\n\nconst assessment = validateSubmission(whoVa2022Instrument, incomingPayload);\nif (!assessment.valid) {\n  return { status: 422, issues: assessment.issues };\n}\n\nawait save(assessment.data);\n```\n\nThis is the same validator used by native and web sessions. Interactive sessions also run field validation after each accepted answer, so users see constraint errors before pressing Next.\n\nWhen Next or Complete finds validation issues, the shared renderer aligns the first invalid question at the top of the screen and highlights its error card. Web also focuses the first interactive control; Expo and React Native use the question's measured content position with `ScrollView.scrollTo`.\n\n## Development and tests\n\n```bash\npnpm dev          # launch the browser preview at http://127.0.0.1:5173\npnpm typecheck\npnpm test         # includes source-conformance and runtime tests\npnpm test:e2e     # run Chromium form automation with trace, video, and HTML report\npnpm test:e2e:headed # watch each action at 800 ms and each form sequentially\npnpm test:e2e:report # open the most recent Playwright report\npnpm build\npnpm check\n```\n\nInstall the pinned Playwright browser once with `pnpm exec playwright install chromium`. The browser suite enters answers through the rendered controls, captures validation errors and corrected states, verifies the visible age summary and calculated values derived from `Id10021`, and confirms valid paths can advance without alerts.\n\nCanonical artifacts:\n\n- `src/generated/who-va-2022.instrument.json` — authoritative runtime instrument\n- `src/generated/who-va-2022.question-audit.json` — human-reviewable question matrix retained alongside the contract\n\nThe package build reads only the checked-in JSON. Tests may compile the retained XLSForm in memory to detect source drift and verify type, coded values, requiredness, relevance AST, constraints, calculations, field validation, and isolated submission validation. The test compiler never regenerates or modifies the JSON.\n\nSee the [developer guide](https://github.com/drguptavivek/WHO-va-2022/blob/main/docs/development.md) for the full contributor workflow and [Architecture](https://github.com/drguptavivek/WHO-va-2022/blob/main/docs/architecture.md) for the trust boundaries and extension points.\n\n## Licensing and attribution\n\nThe original software implementation is released under the [MIT License](LICENSE).\n\nThe questionnaire content remains copyright World Health Organization and is identified by WHO as licensed under [CC BY-ND 3.0 IGO](https://creativecommons.org/licenses/by-nd/3.0/igo/). See [NOTICE](NOTICE) for attribution, licence boundaries, and the non-endorsement statement.\n","readmeFilename":"README.md","_rev":"1-83daf811cd532dbd7071344ccf917e85"}