|
1 | 1 | # AGENTS.md |
2 | 2 |
|
3 | 3 | ## Project map (read this first) |
4 | | -- This project implements the FHIRPath language in JavaScript and supports DSTU2, STU3, R4, and R5 model contexts. |
5 | | -- Entry point is `src/fhirpath.js`: public API is `parse`, `compile`, `evaluate`, `resolveInternalTypes`, `types`. |
6 | | -- `src/fhirpath.js` also exports `version`, `FP_Decimal`, `ucumUtils`, and `util` (used by external callers and custom function implementations). |
7 | | -- Evaluation flow is `parse()` -> AST -> `_compile()` -> `applyParsedPath()` -> `engine.doEval()`; see `src/fhirpath.js` and `src/parser/index.js`. |
8 | | -- Core behavior is table-driven: `engine.invocationTable` in `src/fhirpath.js` maps function/operator names to handlers and arity checks. |
9 | | -- Most function families live in separate modules (e.g. `src/aggregate.js`, `src/filtering.js`, `src/math.js`) and export an `engine` object. |
10 | | -- FHIR version-specific typing/model metadata is in `fhir-context/{dstu2,stu3,r4,r5}` and is required for choice-type/type resolution. |
11 | | -- CLI entry is `bin/fhirpath`; behavior is covered by `test/bin_fhirpath.test.js`. |
| 4 | + |
| 5 | +- This project implements the FHIRPath language in JavaScript and supports |
| 6 | + DSTU2, STU3, R4, and R5 model contexts. |
| 7 | +- Entry point is `src/fhirpath.js`: public API is `parse`, `compile`, |
| 8 | + `evaluate`, `resolveInternalTypes`, `types`. |
| 9 | +- `src/fhirpath.js` also exports `version`, `FP_Decimal`, `ucumUtils`, and |
| 10 | + `util` (used by external callers and custom function implementations). |
| 11 | +- Evaluation flow is `parse()` -> AST -> `_compile()` -> `applyParsedPath()` -> |
| 12 | + `engine.doEval()`; see `src/fhirpath.js` and `src/parser/index.js`. |
| 13 | +- Core behavior is table-driven: `engine.invocationTable` in `src/fhirpath.js` |
| 14 | + maps function/operator names to handlers and arity checks. |
| 15 | +- Most function families live in separate modules (e.g. `src/aggregate.js`, |
| 16 | + `src/filtering.js`, `src/math.js`) and export an `engine` object. |
| 17 | +- FHIR version-specific typing/model metadata is in |
| 18 | + `fhir-context/{dstu2,stu3,r4,r5}` and is required for choice-type/type |
| 19 | + resolution. |
| 20 | +- CLI entry is `bin/fhirpath`; behavior is covered by |
| 21 | + `test/bin_fhirpath.test.js`. |
12 | 22 |
|
13 | 23 | ## Architecture patterns that matter |
14 | | -- Internal values are wrapped as `ResourceNode` / `FP_Type`; final conversion happens in `prepareEvalResult()` (`src/fhirpath.js`). |
15 | | -- `evaluate()` mutates input resources by attaching hidden `__path__` metadata (documented in `README.md`); do not assume input objects stay pristine. |
16 | | -- Base-path evaluation for partial resources uses `{base, expression}` and model normalization via `model.pathsDefinedElsewhere`/`model.path2Type` in `_compile()`. |
17 | | -- Async operations (`resolve`, `memberOf`, `%terminologies.*`) require `options.async`; guards are enforced via `util.checkAllowAsync` (see `src/additional.js`, `src/terminologies.js`). |
18 | | -- `%factory.*` is implemented as a class with its own invocation table in `src/factory.js` and injected as `%factory` in eval context. |
| 24 | + |
| 25 | +- Internal values are wrapped as `ResourceNode` / `FP_Type`; final conversion |
| 26 | + happens in `prepareEvalResult()` (`src/fhirpath.js`). |
| 27 | +- `evaluate()` mutates input resources by attaching hidden `__path__` metadata ( |
| 28 | + documented in `README.md`); do not assume input objects stay pristine. |
| 29 | +- Base-path evaluation for partial resources uses `{base, expression}` and model |
| 30 | + normalization via `model.pathsDefinedElsewhere`/`model.path2Type` in |
| 31 | + `_compile()`. |
| 32 | +- Async operations (`resolve`, `memberOf`, `%terminologies.*`) require |
| 33 | + `options.async`; guards are enforced via `util.checkAllowAsync` (see |
| 34 | + `src/additional.js`, `src/terminologies.js`). |
| 35 | +- For mixed sync/async flows, `util.checkAllowAsync` belongs in the function |
| 36 | + that creates an async promise; do not add it to pure Promise combiners such |
| 37 | + as `sort` using `Promise.all(...)`. |
| 38 | +- `evaluate()`/`compile()` accept `options.debugger` for step-level tracing; |
| 39 | + the callback receives `(ctx, parentData, result, node)` after each eval step |
| 40 | + (see `engine.doEvalSync()` in `src/fhirpath.js`). |
| 41 | +- With `resolveInternalTypes: false`, `prepareEvalResult()` keeps |
| 42 | + `ResourceNode` wrappers in output (instead of flattening to plain values), so |
| 43 | + downstream evaluations can retain type/path metadata. |
| 44 | +- `%factory.*` is implemented as a class with its own invocation table in |
| 45 | + `src/factory.js` and injected as `%factory` in eval context. |
19 | 46 |
|
20 | 47 | ## Testing workflow (project-specific) |
21 | | -- Spec-style unit tests are YAML-driven: `test/fhirpath.test.js` loads `test/cases/*.yaml` and generates Jest tests dynamically. |
22 | | -- `test/cases/fhir-r4.yaml` and `test/cases/fhir-r5.yaml` are generated from `converter/dataset`; refresh them with `node converter/bin/index.js -s` (skip download, use local dataset files). |
23 | | -- Additional Jest suites in `test/*.test.js` cover APIs and behaviors outside YAML cases (e.g. `test/async-functions.test.js`, `test/user-invocation-table.test.js`, `test/bin_fhirpath.test.js`). |
24 | | -- Many tests run in both math modes automatically (`preciseMath` true/false) unless a case sets `preciseMath` explicitly. |
25 | | -- `npm run test:unit` runs Jest three times across time zones (`default`, `America/New_York`, `Europe/Paris`) to catch datetime regressions. |
26 | | -- Test helpers in `test/test_utils.js` auto-load models (`r5`, `r4`, `stu3`, `dstu2`) and deep-clone input resources to avoid cross-test pollution. |
27 | | -- Add new spec-style cases in `test/cases/*.yaml`; use keys like `model`, `inputfile`, `variables`, `context`, `error`, `result`. |
28 | | -- Type declaration checks use `npm run test:tsd` with tests in `test/typescript/fhirpath.test-d.ts`. |
29 | | -- End-to-end browser checks run via Cypress under `test/cypress/` (script: `npm run test:e2e`, which builds the demo first). |
| 48 | + |
| 49 | +- Spec-style unit tests are YAML-driven: `test/fhirpath.test.js` loads |
| 50 | + `test/cases/*.yaml` and generates Jest tests dynamically. |
| 51 | +- `test/cases/fhir-r4.yaml` and `test/cases/fhir-r5.yaml` are generated from |
| 52 | + `converter/dataset`; refresh them with `node converter/bin/index.js -s` (skip |
| 53 | + download, use local dataset files). |
| 54 | +- Additional Jest suites in `test/*.test.js` cover APIs and behaviors outside |
| 55 | + YAML cases (e.g. `test/async-functions.test.js`, |
| 56 | + `test/user-invocation-table.test.js`, `test/bin_fhirpath.test.js`). |
| 57 | +- Many tests run in both math modes automatically (`preciseMath` true/false) |
| 58 | + unless a case sets `preciseMath` explicitly. |
| 59 | +- `npm run test:unit` runs Jest three times across time zones (`default`, |
| 60 | + `America/New_York`, `Europe/Paris`) to catch datetime regressions. |
| 61 | +- Test helpers in `test/test_utils.js` auto-load models (`r5`, `r4`, `stu3`, |
| 62 | + `dstu2`) and deep-clone input resources to avoid cross-test pollution. |
| 63 | +- Add new spec-style cases in `test/cases/*.yaml`; use keys like `model`, |
| 64 | + `inputfile`, `variables`, `context`, `error`, `result`. |
| 65 | +- Type declaration checks use `npm run test:tsd` with tests in |
| 66 | + `test/typescript/fhirpath.test-d.ts`. |
| 67 | +- End-to-end browser checks run via Cypress under `test/cypress/` (script: |
| 68 | + `npm run test:e2e`, which builds the demo first). |
30 | 69 |
|
31 | 70 | ## Build/dev commands you will actually use |
32 | | -- Install: `npm install` (if `node` is missing in this environment, run `source bashrc.fhirpath` and retry). |
33 | | -- Lint: `npm run lint` (targets `src/parser/index.js`, `src/*.js`, `converter/`). |
| 71 | + |
| 72 | +- Install: `npm install` (if `node` is missing in this environment, run |
| 73 | + `source bashrc.fhirpath` and retry). |
| 74 | +- Lint: `npm run lint` (targets `src/parser/index.js`, `src/*.js`, |
| 75 | + `converter/`). |
34 | 76 | - Unit tests: `npm run test:unit`; debugger mode: `npm run test:unit:debug`. |
35 | 77 | - Type tests: `npm run test:tsd`. |
36 | 78 | - Demo build: `npm run build:demo` (`npm run build` + `demo` webpack build). |
37 | 79 | - E2E tests: `npm run test:e2e` (builds demo + runs Cypress). |
38 | 80 | - Full CI-like local run: `npm test` (lint + tsd + unit + e2e). |
39 | | -- Parser regeneration: `npm run generateParser` (uses `src/parser/FHIRPath.g4`, `antlr-4.9.3-complete.jar`, then `scripts/fix-parser-imports.js`). |
40 | | -- Browser artifacts: `npm run build` (webpack outputs in `browser-build/`, plus zipped distributable). |
| 81 | +- Parser regeneration: `npm run generateParser` (uses `src/parser/FHIRPath.g4`, |
| 82 | + `antlr-4.9.3-complete.jar`, then `scripts/fix-parser-imports.js`). |
| 83 | +- Browser artifacts: `npm run build` (webpack outputs in `browser-build/`, plus |
| 84 | + zipped distributable). |
41 | 85 | - Performance comparison: `npm run compare-performance` (`test/benchmark.js`). |
| 86 | + Use `-- -r <git-ref>` to compare against a local baseline commit and check the |
| 87 | + generated report at `test/benchmark/results/compare-performance-report.html`. |
42 | 88 |
|
43 | 89 | ## Sandbox and escalation |
44 | | -- If a command that is important to the task fails due to sandbox restrictions (for example EPERM/spawn permission errors), rerun it with escalated permissions and include a brief justification request. |
| 90 | + |
| 91 | +- If a command that is important to the task fails due to sandbox restrictions ( |
| 92 | + for example EPERM/spawn permission errors), rerun it with escalated |
| 93 | + permissions and include a brief justification request. |
45 | 94 |
|
46 | 95 | ## Conventions to preserve when editing |
47 | | -- Performance is a top priority: prefer lower-allocation, lower-overhead designs, and treat measurable performance regressions as blockers unless explicitly approved. |
48 | | -- Use CommonJS (`require`/`module.exports`) and 2-space indentation (see `eslint.config.js`). |
49 | | -- Use modern JavaScript syntax (`const`/`let`, arrow functions, destructuring) where it matches existing files. |
50 | | -- In AI chat responses, prefer project-relative file references in `path:line` format (e.g. `src/fhirpath.js:19`). |
51 | | -- Keep two blank lines between declarations and above JSDoc blocks. |
52 | | -- Treat JSDoc as part of a declaration: keep two blank lines above the JSDoc block, and no blank line between JSDoc and the declaration. |
53 | | -- Keep project layout conventions: core logic in `src/`, FHIR-version-specific metadata in `fhir-context/`, tests in `test/`, and docs in `docs/` or `README.md`. |
54 | | -- New FHIRPath functions are typically added as module functions plus registration in `engine.invocationTable` (`src/fhirpath.js`). |
55 | | -- For feature work, update tests together with implementation (`test/cases/*.yaml` for spec-style behavior, plus `test/*.test.js` when behavior is API-specific). |
56 | | -- For custom function hooks, follow `userInvocationTable` conventions in `_compile()`; use `internalStructures: true` only if handler needs raw `ResourceNode` access. |
57 | | -- For async server calls, thread options via `terminologyUrl`, `fhirServerUrl`, `httpHeaders`, and optional `signal` (see `docs/auth.md`, `docs/abort.md`). |
58 | | -- If public API signatures or exports change, update `src/fhirpath.d.ts` and validate with `npm run test:tsd` (`test/typescript/fhirpath.test-d.ts`). |
59 | | -- Keep behavior standards-compliant with FHIRPath and aligned with the selected FHIR model version when model-aware behavior is involved. |
60 | | -- Contributors **MUST NOT** hand-edit `test/cases/fhir-r4.yaml` or `test/cases/fhir-r5.yaml` (these files are generated). |
| 96 | + |
| 97 | +- Performance is a top priority: prefer lower-allocation, lower-overhead |
| 98 | + designs, and treat measurable performance regressions as blockers unless |
| 99 | + explicitly approved. |
| 100 | +- Use CommonJS (`require`/`module.exports`) and 2-space indentation (see |
| 101 | + `eslint.config.js`). |
| 102 | +- When generating code, keep lines to no more than 80 characters when practical. |
| 103 | +- Use modern JavaScript syntax (`const`/`let`, arrow functions, destructuring) |
| 104 | + where it matches existing files. |
| 105 | +- In AI chat responses, prefer project-relative file references in `path:line` |
| 106 | + format (e.g. `src/fhirpath.js:19`). |
| 107 | +- Blank lines: keep two between declarations; for JSDoc, keep two above the |
| 108 | + block and none between the block and its declaration. |
| 109 | +- In authored `test/*.test.js` files, keep two blank lines before and after |
| 110 | + `describe(...)` blocks and between `it(...)` blocks; inside each |
| 111 | + `describe(...)`, keep one blank line before the first `it(...)` and after |
| 112 | + the last `it(...)`. |
| 113 | +- Keep project layout conventions: core logic in `src/`, FHIR-version-specific |
| 114 | + metadata in `fhir-context/`, tests in `test/`, and docs in `docs/` or |
| 115 | + `README.md`. |
| 116 | +- New FHIRPath functions are typically added as module functions plus |
| 117 | + registration in `engine.invocationTable` (`src/fhirpath.js`). |
| 118 | +- For feature work, update tests together with implementation ( |
| 119 | + `test/cases/*.yaml` for spec-style behavior, plus `test/*.test.js` when |
| 120 | + behavior is API-specific). |
| 121 | +- For custom function hooks, follow `userInvocationTable` conventions in |
| 122 | + `_compile()`; use `internalStructures: true` only if handler needs raw |
| 123 | + `ResourceNode` access. |
| 124 | +- For async server calls, thread options via `terminologyUrl`, `fhirServerUrl`, |
| 125 | + `httpHeaders`, and optional `signal` (see `docs/auth.md`, `docs/abort.md`). |
| 126 | +- If public API signatures or exports change, update `src/fhirpath.d.ts` and |
| 127 | + validate with `npm run test:tsd` (`test/typescript/fhirpath.test-d.ts`). |
| 128 | +- Keep behavior standards-compliant with FHIRPath and aligned with the selected |
| 129 | + FHIR model version when model-aware behavior is involved. |
| 130 | +- Contributors **MUST NOT** hand-edit `test/cases/fhir-r4.yaml` or |
| 131 | + `test/cases/fhir-r5.yaml` (these files are generated). |
0 commit comments