fhirpath-ts 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +12 -5
  2. package/dist/analyzer/analyze-dto.d.ts.map +1 -1
  3. package/dist/analyzer/analyze-dto.js +14 -8
  4. package/dist/analyzer/analyze-dto.js.map +1 -1
  5. package/dist/analyzer/analyze.d.ts +23 -12
  6. package/dist/analyzer/analyze.d.ts.map +1 -1
  7. package/dist/analyzer/analyze.js +138 -46
  8. package/dist/analyzer/analyze.js.map +1 -1
  9. package/dist/analyzer/declarations.d.ts +6 -0
  10. package/dist/analyzer/declarations.d.ts.map +1 -1
  11. package/dist/analyzer/declarations.js +7 -0
  12. package/dist/analyzer/declarations.js.map +1 -1
  13. package/dist/analyzer/expression-policy.d.ts +88 -14
  14. package/dist/analyzer/expression-policy.d.ts.map +1 -1
  15. package/dist/analyzer/expression-policy.js +296 -41
  16. package/dist/analyzer/expression-policy.js.map +1 -1
  17. package/dist/analyzer/operator-rules.d.ts.map +1 -1
  18. package/dist/analyzer/operator-rules.js +7 -6
  19. package/dist/analyzer/operator-rules.js.map +1 -1
  20. package/dist/analyzer/signatures.d.ts +49 -0
  21. package/dist/analyzer/signatures.d.ts.map +1 -1
  22. package/dist/analyzer/signatures.js +76 -31
  23. package/dist/analyzer/signatures.js.map +1 -1
  24. package/dist/analyzer/source-options.d.ts +17 -0
  25. package/dist/analyzer/source-options.d.ts.map +1 -0
  26. package/dist/analyzer/source-options.js +3 -0
  27. package/dist/analyzer/source-options.js.map +1 -0
  28. package/dist/api/dto.d.ts +9 -4
  29. package/dist/api/dto.d.ts.map +1 -1
  30. package/dist/api/dto.js +14 -3
  31. package/dist/api/dto.js.map +1 -1
  32. package/dist/api/strict.d.ts.map +1 -1
  33. package/dist/api/strict.js +8 -3
  34. package/dist/api/strict.js.map +1 -1
  35. package/dist/cli/dto-check.d.ts +9 -0
  36. package/dist/cli/dto-check.d.ts.map +1 -1
  37. package/dist/cli/dto-check.js +49 -6
  38. package/dist/cli/dto-check.js.map +1 -1
  39. package/dist/cli/fhirpath-check.js +161 -31
  40. package/dist/cli/fhirpath-check.js.map +1 -1
  41. package/dist/eslint/index.d.ts.map +1 -1
  42. package/dist/eslint/index.js +13 -8
  43. package/dist/eslint/index.js.map +1 -1
  44. package/dist/sites/index.d.ts +31 -2
  45. package/dist/sites/index.d.ts.map +1 -1
  46. package/dist/sites/index.js +351 -30
  47. package/dist/sites/index.js.map +1 -1
  48. package/dist/typed/infer.d.ts +2 -0
  49. package/dist/typed/infer.d.ts.map +1 -1
  50. package/package.json +1 -1
  51. package/src/analyzer/analyze-dto.ts +19 -9
  52. package/src/analyzer/analyze.ts +186 -54
  53. package/src/analyzer/declarations.ts +12 -0
  54. package/src/analyzer/expression-policy.ts +423 -47
  55. package/src/analyzer/operator-rules.ts +7 -6
  56. package/src/analyzer/signatures.ts +100 -36
  57. package/src/analyzer/source-options.ts +19 -0
  58. package/src/api/dto.ts +35 -9
  59. package/src/api/strict.ts +8 -3
  60. package/src/cli/dto-check.ts +56 -6
  61. package/src/cli/fhirpath-check.ts +180 -34
  62. package/src/eslint/index.ts +15 -12
  63. package/src/sites/index.ts +428 -40
  64. package/src/typed/infer.ts +2 -2
package/README.md CHANGED
@@ -202,12 +202,12 @@ statusRows // [{ label: 'Active', tone: 'info' }, ..., { label: 'Unknown', tone:
202
202
 
203
203
  ### Join related resources
204
204
 
205
- Pass lookup data through `env`. Use `vars` to find the related item once per
206
- row. `%context` is the resource for the current row.
205
+ Pass the related resources through `env` and declare their types with
206
+ `envTypes`. Use `vars` to find the related item once per row. `%context` is the
207
+ resource for the current row. With the types declared, `fhirpath-check`
208
+ verifies every path, including the join itself.
207
209
 
208
210
  ```ts
209
- const reports = [...reportsByOrderId].map(([orderId, report]) => ({ orderId, report }))
210
-
211
211
  r4.project(
212
212
  orders,
213
213
  {
@@ -219,11 +219,18 @@ r4.project(
219
219
  },
220
220
  {
221
221
  env: { reports },
222
- vars: { report: '%reports.where(orderId = %context.id).report' },
222
+ envTypes: { reports: { type: 'DiagnosticReport', collection: true } },
223
+ vars: { report: "%reports.where(basedOn.reference = 'ServiceRequest/' + %context.id).first()" },
224
+ varTypes: { report: { type: 'DiagnosticReport' } },
223
225
  },
224
226
  )
225
227
  ```
226
228
 
229
+ When the join key is not on the resource, join in TypeScript instead and keep
230
+ each expression rooted in its own typed resource. Untyped values in `env`
231
+ cannot be verified: paths through them are reported as `unchecked-navigation`
232
+ warnings. See [Static checking](docs/static-checking.md).
233
+
227
234
  ### Follow references in a Bundle
228
235
 
229
236
  `resolve()` follows contained references and references to another Bundle entry.
@@ -1 +1 @@
1
- {"version":3,"file":"analyze-dto.d.ts","sourceRoot":"","sources":["../../src/analyzer/analyze-dto.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,QAAQ,EAAiB,MAAM,eAAe,CAAA;AAE7E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAA;AAEjE,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EAEtB,MAAM,cAAc,CAAA;AAGrB,2FAA2F;AAC3F,MAAM,WAAW,aAAc,SAAQ,kBAAkB;IACvD,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAA;IACd,uFAAuF;IACvF,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,uFAAuF;AACvF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE;QACjB,KAAK,CAAC,EAAE,aAAa,CAAA;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;QAC5C,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,QAAQ,CAAC,EAAE,wBAAwB,CAAA;QACnC,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,QAAQ,CAAC,EAAE,wBAAwB,CAAA;KACpC,CAAA;CACF;AAED,uFAAuF;AACvF,MAAM,WAAW,cAAe,SAAQ,eAAe;IACrD,QAAQ,CAAC,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAA;CACnC;AAED,8FAA8F;AAC9F,MAAM,WAAW,iBAAkB,SAAQ,cAAc;IACvD,kFAAkF;IAClF,MAAM,CAAC,EAAE,eAAe,CAAA;CACzB;AA4BD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE,cAAc,GACvB,CAAC,aAAa,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,CAIrC;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,aAAa,EAAE,CA+CtF"}
1
+ {"version":3,"file":"analyze-dto.d.ts","sourceRoot":"","sources":["../../src/analyzer/analyze-dto.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,QAAQ,EAAiB,MAAM,eAAe,CAAA;AAE7E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAA;AAEjE,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EAEtB,MAAM,cAAc,CAAA;AAGrB,2FAA2F;AAC3F,MAAM,WAAW,aAAc,SAAQ,kBAAkB;IACvD,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAA;IACd,uFAAuF;IACvF,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,uFAAuF;AACvF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE;QACjB,KAAK,CAAC,EAAE,aAAa,CAAA;QACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAA;QAC5C,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,QAAQ,CAAC,EAAE,wBAAwB,CAAA;QACnC,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,QAAQ,CAAC,EAAE,wBAAwB,CAAA;KACpC,CAAA;CACF;AAED,uFAAuF;AACvF,MAAM,WAAW,cAAe,SAAQ,eAAe;IACrD,QAAQ,CAAC,IAAI,EAAE,SAAS,QAAQ,EAAE,CAAA;CACnC;AAED,8FAA8F;AAC9F,MAAM,WAAW,iBAAkB,SAAQ,cAAc;IACvD,kFAAkF;IAClF,MAAM,CAAC,EAAE,eAAe,CAAA;CACzB;AA4BD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,cAAc,EACtB,OAAO,CAAC,EAAE,cAAc,GACvB,CAAC,aAAa,GAAG;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,CAIrC;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,aAAa,EAAE,CAyDtF"}
@@ -3,7 +3,7 @@ import { dtoDefinition } from "../api/dto.js";
3
3
  import { bareEnvironmentName } from "../engine/context.js";
4
4
  import { valueKindOfTypeName } from "../values/type-compat.js";
5
5
  import { analyzeExpressionDetailed, } from "./analyze.js";
6
- import { analyzerEnvironmentVariables, analyzerVariables } from "./declarations.js";
6
+ import { analyzerEnvironmentVariables, analyzerVariables, PROJECT_ROW_VARIABLES } from "./declarations.js";
7
7
  /**
8
8
  * The analyzer context for a DTO: what its engine supplies, with the caller's
9
9
  * own options layered on top *per name* — one function or variable a host
@@ -49,13 +49,11 @@ export function analyzeDto(dto, options) {
49
49
  const definition = dtoDefinition(dto);
50
50
  const context = contextOf(options);
51
51
  const inputType = context.inputType ?? definition.fhirType;
52
- const declared = {
53
- rowIndex: { types: ['System.Integer'], single: true },
54
- rowTotal: { types: ['System.Integer'], single: true },
55
- };
56
- for (const name of [...Object.keys(definition.env ?? {}), ...(definition.callerEnv ?? [])]) {
52
+ const declared = { ...PROJECT_ROW_VARIABLES };
53
+ for (const name of [...Object.keys(definition.env ?? {}), ...definition.callerEnvNames]) {
57
54
  declared[bareEnvironmentName(name)] = {};
58
55
  }
56
+ Object.assign(declared, analyzerVariables(undefined, definition.callerEnvTypes));
59
57
  const diagnostics = [];
60
58
  const analyze = (member, expression, column) => {
61
59
  const perExpression = {
@@ -79,12 +77,20 @@ export function analyzeDto(dto, options) {
79
77
  span: { start: 0, end: expression.length, line: 1, column: 1 },
80
78
  });
81
79
  }
80
+ return result;
82
81
  };
83
82
  for (const [name, value] of Object.entries(definition.vars ?? {})) {
84
83
  const source = typeof value === 'string' ? value : sourceOf(value);
85
84
  if (source !== undefined) {
86
- // A var declares no type, so there is nothing to cross-check against.
87
- analyze(`vars.${bareEnvironmentName(name)}`, source);
85
+ // A var has no declared result to cross-check, but its inferred result is
86
+ // the input state for later vars and columns.
87
+ const result = analyze(`vars.${bareEnvironmentName(name)}`, source);
88
+ declared[bareEnvironmentName(name)] = {
89
+ ...(result.types !== undefined && { types: result.types }),
90
+ ...(result.single !== undefined && { single: result.single }),
91
+ ...(result.ordered !== undefined && { ordered: result.ordered }),
92
+ };
93
+ continue;
88
94
  }
89
95
  declared[bareEnvironmentName(name)] = {};
90
96
  }
@@ -1 +1 @@
1
- {"version":3,"file":"analyze-dto.js","sourceRoot":"","sources":["../../src/analyzer/analyze-dto.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"analyze-dto.js","sourceRoot":"","sources":["../../src/analyzer/analyze-dto.ts"],"names":[],"mappings":"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"}
@@ -51,6 +51,8 @@ export interface AnalyzeOptions {
51
51
  functions?: Record<string, DeclaredFunction>;
52
52
  /** Host-supplied environment variables by name (with or without the leading `%`). */
53
53
  variables?: Record<string, DeclaredVariable>;
54
+ /** Report navigation that remains unchecked because a declared host variable has no type. */
55
+ reportUnchecked?: boolean;
54
56
  }
55
57
  /** Internal strict-analysis options whose variables may retain exact runtime focus types. */
56
58
  export interface RuntimeAnalyzeOptions extends Omit<AnalyzeOptions, 'variables'> {
@@ -59,16 +61,17 @@ export interface RuntimeAnalyzeOptions extends Omit<AnalyzeOptions, 'variables'>
59
61
  export interface AnalysisDetails {
60
62
  diagnostics: AnalyzerDiagnostic[];
61
63
  /**
62
- * The expression's inferred result: canonical type names and cardinality, the
63
- * same StaticState the checks run on. `types: undefined` means the analyzer
64
- * cannot see through the expression (an unknown region), and `single`
65
- * undefined means the cardinality is unknown — neither is an error. Lets a
66
- * caller cross-check a declared type against what the expression really
67
- * yields (see `analyzeDto`).
64
+ * The expression's inferred result: canonical type names, cardinality, and
65
+ * ordering, the same StaticState the checks run on. `types: undefined` means
66
+ * the analyzer cannot see through the expression (an unknown region), and an
67
+ * undefined `single` or `ordered` means that fact is unknown — none of these
68
+ * is an error. Lets a caller cross-check a declared type against what the
69
+ * expression really yields (see `analyzeDto`).
68
70
  */
69
71
  result: {
70
72
  types: string[] | undefined;
71
73
  single: boolean | undefined;
74
+ ordered: boolean | undefined;
72
75
  };
73
76
  /**
74
77
  * `Type.element` paths the expression reads (local type names), deduped in
@@ -81,6 +84,7 @@ export interface AnalysisDetails {
81
84
  export interface AnalyzerRoot {
82
85
  types: string[] | undefined;
83
86
  single: boolean | undefined;
87
+ ordered: boolean | undefined;
84
88
  /** Canonical runtime types in focus order; an empty array means an exactly empty focus. */
85
89
  exactTypes?: string[];
86
90
  }
@@ -93,17 +97,24 @@ export interface AnalyzerRoot {
93
97
  export declare function analyzeExpression(expression: string, options?: AnalyzeOptions): AnalyzerDiagnostic[];
94
98
  /**
95
99
  * Analyzes one source site with only facts visible in that file. A declared root
96
- * does not prove which variables the later call provides. DTO sites may also
97
- * receive variables and functions from base classes, other modules, or
98
- * `project()`. Such sites omit unknown-variable diagnostics and report an
99
- * unknown function only when it resembles a local column. A DTO without a known
100
- * root receives syntax checks only. Use `analyzeDto` when the class and engine
101
- * are loaded.
100
+ * does not prove which variables the later call provides. A site whose call
101
+ * binds variables the source cannot name (`openVariables`) may resolve any
102
+ * `%variable` at runtime, so an unresolved one is reported as an
103
+ * `unchecked-variable` warning under `reportUnchecked` instead of an error.
104
+ * DTO sites may also receive variables and functions from base classes, other
105
+ * modules, or `project()`. Such sites omit unknown-variable diagnostics and
106
+ * report an unknown function only when it resembles a local column. A DTO
107
+ * without a known root receives syntax checks only. Use `analyzeDto` when the
108
+ * class and engine are loaded.
102
109
  */
103
110
  export declare function analyzeSite(site: {
104
111
  expression: string;
105
112
  inputType?: string;
106
113
  dto?: true;
114
+ /** Variables declared by the expression's call site, such as inline env/vars options. */
115
+ variables?: Readonly<Record<string, DeclaredVariable>>;
116
+ /** The call also binds variables the source cannot name — a computed key or spread in env/vars. */
117
+ openVariables?: true;
107
118
  /** Functions the site's file declares — a DTO's `@column` fields (see `columnFunctionDeclaration`). */
108
119
  functions?: Readonly<Record<string, DeclaredFunction>>;
109
120
  }, options?: AnalyzeOptions): AnalyzerDiagnostic[];
@@ -1 +1 @@
1
- {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/analyzer/analyze.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/analyzer/analyze.ts"],"names":[],"mappings":"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"}
@@ -8,14 +8,16 @@ import { FHIR_PRIMITIVE_TO_SYSTEM, typeLocalName } from "../values/typed-value.j
8
8
  import { analyzerEnvironmentVariables, isRuntimeAnalyzerVariable, runtimeAnalyzerEnvironmentVariables, } from "./declarations.js";
9
9
  import { applyOperatorResultRule, applyTypeOperatorResultRule } from "./operator-rules.js";
10
10
  import { hasNestedUnboundedQuantifier } from "./regex-safety.js";
11
- import { applyResultRule, FUNCTION_SIGNATURES, singleAnd, unionStates, withSingle, } from "./signatures.js";
12
- const UNKNOWN = { types: undefined, single: undefined };
11
+ import { applyResultRule, FUNCTION_SIGNATURES, singleAnd, singleState, singletonOrder, unionStates, withSingle, } from "./signatures.js";
12
+ import { SOURCE_VARIABLE_DEFAULTS } from "./source-options.js";
13
+ const UNKNOWN = { types: undefined, single: undefined, ordered: undefined };
13
14
  /** A runtime-analyzed function result no longer has exact facts about its input focus. */
14
15
  function callResult(state, runtime) {
15
16
  return runtime
16
17
  ? {
17
18
  types: state.types,
18
19
  single: state.single,
20
+ ordered: state.ordered,
19
21
  ...(state.targets !== undefined && { targets: state.targets }),
20
22
  }
21
23
  : state;
@@ -37,23 +39,48 @@ export function analyzeExpression(expression, options) {
37
39
  }
38
40
  /**
39
41
  * Analyzes one source site with only facts visible in that file. A declared root
40
- * does not prove which variables the later call provides. DTO sites may also
41
- * receive variables and functions from base classes, other modules, or
42
- * `project()`. Such sites omit unknown-variable diagnostics and report an
43
- * unknown function only when it resembles a local column. A DTO without a known
44
- * root receives syntax checks only. Use `analyzeDto` when the class and engine
45
- * are loaded.
42
+ * does not prove which variables the later call provides. A site whose call
43
+ * binds variables the source cannot name (`openVariables`) may resolve any
44
+ * `%variable` at runtime, so an unresolved one is reported as an
45
+ * `unchecked-variable` warning under `reportUnchecked` instead of an error.
46
+ * DTO sites may also receive variables and functions from base classes, other
47
+ * modules, or `project()`. Such sites omit unknown-variable diagnostics and
48
+ * report an unknown function only when it resembles a local column. A DTO
49
+ * without a known root receives syntax checks only. Use `analyzeDto` when the
50
+ * class and engine are loaded.
46
51
  */
47
52
  export function analyzeSite(site, options) {
53
+ const sourceVariables = options?.[SOURCE_VARIABLE_DEFAULTS];
54
+ const variablePlan = site.variablePlan;
48
55
  const declared = { ...site.functions, ...options?.functions };
56
+ const variables = sourceSiteVariables(site.variables, variablePlan, options?.variables, sourceVariables);
49
57
  const merged = {
50
58
  ...options,
51
59
  ...(site.inputType !== undefined && { inputType: site.inputType }),
52
60
  ...(Object.keys(declared).length > 0 && { functions: declared }),
61
+ ...(variables !== undefined && { variables }),
53
62
  };
54
63
  const diagnostics = analyzeExpression(site.expression, merged);
55
64
  if (site.dto !== true) {
56
- return site.inputType === undefined
65
+ if (site.openVariables === true) {
66
+ return diagnostics.flatMap(diagnostic => {
67
+ if (diagnostic.code !== 'unknown-variable') {
68
+ return [diagnostic];
69
+ }
70
+ if (options?.reportUnchecked !== true) {
71
+ return [];
72
+ }
73
+ return [
74
+ {
75
+ ...diagnostic,
76
+ severity: 'warning',
77
+ code: 'unchecked-variable',
78
+ message: `Environment variable %${diagnostic.name} was not checked: the call's env/vars names are not fully visible in source`,
79
+ },
80
+ ];
81
+ });
82
+ }
83
+ return site.inputType === undefined || options?.variables !== undefined
57
84
  ? diagnostics
58
85
  : diagnostics.filter(diagnostic => diagnostic.code !== 'unknown-variable');
59
86
  }
@@ -71,6 +98,33 @@ export function analyzeSite(site, options) {
71
98
  return diagnostic.name !== undefined && nearestName(diagnostic.name, columns) !== undefined;
72
99
  });
73
100
  }
101
+ /** Recreate strict evaluation's vars prefix without flattening away engine/default insertion order. */
102
+ function sourceSiteVariables(siteEnvironment, plan, engineEnvironment, defaults) {
103
+ if (siteEnvironment === undefined &&
104
+ engineEnvironment === undefined &&
105
+ defaults === undefined &&
106
+ plan === undefined) {
107
+ return undefined;
108
+ }
109
+ const variables = { ...engineEnvironment, ...siteEnvironment };
110
+ const declarations = plan?.inheritsDeclarations === false ? plan.declarations : { ...defaults?.declarations, ...plan?.declarations };
111
+ const valueNames = [...(defaults?.values ?? [])];
112
+ for (const name of plan?.values ?? []) {
113
+ if (!valueNames.includes(name)) {
114
+ valueNames.push(name);
115
+ }
116
+ }
117
+ const end = plan?.before === undefined ? valueNames.length : valueNames.indexOf(plan.before);
118
+ for (const name of valueNames.slice(0, end < 0 ? valueNames.length : end)) {
119
+ variables[name] = declarations[name] ?? {};
120
+ }
121
+ if (plan?.before === undefined) {
122
+ for (const [name, declaration] of Object.entries(declarations)) {
123
+ variables[name] ??= declaration;
124
+ }
125
+ }
126
+ return variables;
127
+ }
74
128
  /** `analyzeExpression` plus the element paths the expression touches and its inferred result type. */
75
129
  export function analyzeExpressionDetailed(expression, options) {
76
130
  let ast;
@@ -86,7 +140,7 @@ export function analyzeExpressionDetailed(expression, options) {
86
140
  return {
87
141
  diagnostics: [{ severity: 'error', code: 'syntax', message: error.message, span: error.span }],
88
142
  elementDependencies: [],
89
- result: { types: undefined, single: undefined },
143
+ result: { types: undefined, single: undefined, ordered: undefined },
90
144
  };
91
145
  }
92
146
  return analyzeAstDetailed(ast, options);
@@ -98,7 +152,7 @@ function analyzeAstDetailed(ast, options, root) {
98
152
  return {
99
153
  diagnostics: analyzer.diagnostics,
100
154
  elementDependencies: [...analyzer.dependencies],
101
- result: { types: state.types, single: state.single },
155
+ result: { types: state.types, single: state.single, ordered: state.ordered },
102
156
  };
103
157
  }
104
158
  /** Analyze a parsed expression using facts from the actual evaluation focus. */
@@ -116,15 +170,19 @@ class Analyzer {
116
170
  customFunctions;
117
171
  declaredVariables;
118
172
  activeExpressionFunctions = new Set();
173
+ reportUnchecked;
119
174
  constructor(options, root) {
120
175
  this.model = options?.model;
121
176
  this.runtime = root !== undefined;
177
+ this.reportUnchecked = options?.reportUnchecked === true;
122
178
  const inputType = options?.inputType;
179
+ // The root focus is a real collection, so its order is defined even when
180
+ // its types and cardinality are not.
123
181
  this.root =
124
182
  root ??
125
183
  (inputType === undefined
126
- ? { types: undefined, single: undefined }
127
- : { types: [this.model?.resolveType(inputType) ?? inputType], single: true });
184
+ ? { types: undefined, single: undefined, ordered: true }
185
+ : singleState([this.model?.resolveType(inputType) ?? inputType]));
128
186
  this.customFunctions = new Map(Object.entries(options?.functions ?? {}).map(([name, declared]) => [
129
187
  name,
130
188
  ('overloads' in declared ? declared.overloads : [declared]).map(declaration => resolvedDeclaration(declaration, this.model, this.runtime)),
@@ -135,6 +193,7 @@ class Analyzer {
135
193
  return {
136
194
  types: this.root.types,
137
195
  single: this.root.single,
196
+ ordered: this.root.ordered,
138
197
  rawInput: true,
139
198
  ...(this.root.exactTypes !== undefined && { exactTypes: this.root.exactTypes }),
140
199
  };
@@ -144,24 +203,21 @@ class Analyzer {
144
203
  case 'null':
145
204
  // The empty literal `{}` is statically at most one item, so it satisfies
146
205
  // singleton operands (`{} + 1`, `{} and true` are spec-legal).
147
- return { types: [], single: true };
206
+ return singleState([]);
148
207
  case 'boolean':
149
- return { types: ['System.Boolean'], single: true };
208
+ return singleState(['System.Boolean']);
150
209
  case 'string':
151
- return { types: ['System.String'], single: true };
210
+ return singleState(['System.String']);
152
211
  case 'number':
153
- return {
154
- types: [node.isLong ? 'System.Long' : node.isDecimal ? 'System.Decimal' : 'System.Integer'],
155
- single: true,
156
- };
212
+ return singleState([node.isLong ? 'System.Long' : node.isDecimal ? 'System.Decimal' : 'System.Integer']);
157
213
  case 'date':
158
- return { types: ['System.Date'], single: true };
214
+ return singleState(['System.Date']);
159
215
  case 'dateTime':
160
- return { types: ['System.DateTime'], single: true };
216
+ return singleState(['System.DateTime']);
161
217
  case 'time':
162
- return { types: ['System.Time'], single: true };
218
+ return singleState(['System.Time']);
163
219
  case 'quantity':
164
- return { types: ['System.Quantity'], single: true };
220
+ return singleState(['System.Quantity']);
165
221
  case 'external':
166
222
  return this.walkExternal(node, scope);
167
223
  case 'special':
@@ -175,6 +231,7 @@ class Analyzer {
175
231
  case 'indexer': {
176
232
  const target = this.walk(node.target, input, scope);
177
233
  const index = this.walk(node.index, input, scope);
234
+ this.requireOrdered(target, node.target.span, 'The indexer');
178
235
  this.requireKind(index, 'Numeric', node.index.span, 'the indexer expects a single Integer');
179
236
  return withSingle(target, true);
180
237
  }
@@ -210,7 +267,7 @@ class Analyzer {
210
267
  }
211
268
  const declared = this.declaredVariables.get(node.name);
212
269
  if (declared !== undefined) {
213
- return this.declaredVariableState(declared);
270
+ return this.declaredVariableState(declared, node.name);
214
271
  }
215
272
  switch (node.name) {
216
273
  case 'context':
@@ -221,13 +278,13 @@ class Analyzer {
221
278
  case 'ucum':
222
279
  case 'sct':
223
280
  case 'loinc':
224
- return { types: ['System.String'], single: true };
281
+ return singleState(['System.String']);
225
282
  default:
226
283
  break;
227
284
  }
228
285
  // FHIR-defined families expand to HL7 urls: %`vs-[name]`, %`ext-[name]`.
229
286
  if (node.name.startsWith('vs-') || node.name.startsWith('ext-')) {
230
- return { types: ['System.String'], single: true };
287
+ return singleState(['System.String']);
231
288
  }
232
289
  // A dynamically-named defineVariable() earlier in the chain may have bound
233
290
  // this name, so reporting it as undefined could be wrong — stay quiet.
@@ -240,7 +297,7 @@ class Analyzer {
240
297
  if (name === 'this') {
241
298
  return this.frames.at(-1) ?? this.rootState();
242
299
  }
243
- return name === 'index' ? { types: ['System.Integer'], single: true } : UNKNOWN;
300
+ return name === 'index' ? singleState(['System.Integer']) : UNKNOWN;
244
301
  }
245
302
  /**
246
303
  * A quantity's components (spec §4: `value` and `unit`), the one System type
@@ -255,7 +312,7 @@ class Analyzer {
255
312
  walkIdentifier(node, input) {
256
313
  // Navigating from a statically empty input yields empty — nothing to check.
257
314
  if (input.types !== undefined && input.types.length === 0) {
258
- return { types: [], single: true };
315
+ return singleState([]);
259
316
  }
260
317
  const exactTypes = input.exactTypes?.filter(type => rootTypeMatches(this.model, type, node.name));
261
318
  if (exactTypes !== undefined && exactTypes.length > 0) {
@@ -263,6 +320,7 @@ class Analyzer {
263
320
  return {
264
321
  types: types === undefined ? undefined : [...new Set(types)],
265
322
  single: input.single,
323
+ ordered: singletonOrder(input.single, input.ordered),
266
324
  exactTypes,
267
325
  ...(input.rawInput === true && { rawInput: true }),
268
326
  };
@@ -273,14 +331,16 @@ class Analyzer {
273
331
  const asType = this.model?.resolveType(node.name) ?? resolveSystemTypeName(node.name);
274
332
  if (asType !== undefined) {
275
333
  return {
276
- types: [asType],
277
- single: true,
334
+ ...singleState([asType]),
278
335
  ...(input.exactTypes !== undefined && {
279
336
  exactTypes: input.exactTypes.filter(type => this.model?.isSubtypeOf(type, asType) === true),
280
337
  }),
281
338
  };
282
339
  }
283
- return UNKNOWN;
340
+ if (input.opaqueVariable !== undefined && this.reportUnchecked) {
341
+ this.report('unchecked-navigation', `Element '${node.name}' after untyped environment variable %${input.opaqueVariable} was not checked; declare the variable's type`, node.span, 'warning', node.name);
342
+ }
343
+ return { ...UNKNOWN, ordered: input.ordered };
284
344
  }
285
345
  // Root rule: an identifier naming the (super)type of the context is the context.
286
346
  {
@@ -335,9 +395,14 @@ class Analyzer {
335
395
  : didYouMean(node.name, this.elementNames(input.types));
336
396
  this.report('unknown-element', `Element '${node.name}' is not defined on ${input.types.join(' | ')}${hint}`, node.span, 'error', node.name);
337
397
  }
338
- return UNKNOWN;
398
+ return { ...UNKNOWN, ordered: input.ordered };
339
399
  }
340
- const state = { types: [...new Set(found)], single: singleAnd(input.single, !isCollection) };
400
+ const single = singleAnd(input.single, !isCollection);
401
+ const state = {
402
+ types: [...new Set(found)],
403
+ single,
404
+ ordered: singletonOrder(single, input.ordered),
405
+ };
341
406
  if (targets !== undefined && targets.length > 0) {
342
407
  state.targets = [...new Set(targets)];
343
408
  }
@@ -436,7 +501,11 @@ class Analyzer {
436
501
  // ofType(X) filters and as(X) casts: both narrow to the named type,
437
502
  // intersected with the known candidates.
438
503
  if ((node.name === 'ofType' || node.name === 'as') && typeTarget !== undefined) {
439
- return { types: this.narrowTypes(input, typeTarget, node.span), single: input.single };
504
+ return {
505
+ types: this.narrowTypes(input, typeTarget, node.span),
506
+ single: input.single,
507
+ ordered: singletonOrder(input.single, input.ordered),
508
+ };
440
509
  }
441
510
  const expressionResult = this.expressionFunctionResult(node.name, custom, input, scope);
442
511
  if (expressionResult !== undefined) {
@@ -458,7 +527,7 @@ class Analyzer {
458
527
  const bodyInput = this.expressionBodyInput(declaration, input);
459
528
  if (declaration.criteria === true) {
460
529
  this.walkExpressionFunction(name, expression, declaration.variables, bodyInput, scope);
461
- return { types: ['System.Boolean'], single: true };
530
+ return singleState(['System.Boolean']);
462
531
  }
463
532
  if (declaration.signature?.result === undefined) {
464
533
  return this.walkExpressionFunction(name, expression, declaration.variables, bodyInput, scope);
@@ -483,6 +552,7 @@ class Analyzer {
483
552
  return {
484
553
  types: [...new Set(types)],
485
554
  single: callInput.single,
555
+ ordered: callInput.ordered,
486
556
  ...(callInput.targets !== undefined && { targets: callInput.targets }),
487
557
  };
488
558
  }
@@ -505,7 +575,7 @@ class Analyzer {
505
575
  const bare = bareEnvironmentName(declaredName);
506
576
  // defineVariable() values have priority over environment overlays at runtime.
507
577
  if (!functionScope.vars.has(bare)) {
508
- functionScope.vars.set(bare, this.declaredVariableState(variable));
578
+ functionScope.vars.set(bare, this.declaredVariableState(variable, bare));
509
579
  }
510
580
  }
511
581
  const diagnosticCount = this.diagnostics.length;
@@ -529,13 +599,15 @@ class Analyzer {
529
599
  }
530
600
  }
531
601
  /** Convert one declared environment value into the same state used for scoped variables. */
532
- declaredVariableState(declared) {
602
+ declaredVariableState(declared, name) {
533
603
  const exactTypes = this.runtime && isRuntimeAnalyzerVariable(declared) ? declared.exactTypes : undefined;
534
604
  return {
535
605
  types: declared.types?.map(type => this.canonicalize(type)),
536
606
  single: declared.single,
607
+ ordered: singletonOrder(declared.single, declared.ordered),
537
608
  ...(declared.targets !== undefined && { targets: declared.targets.map(type => this.canonicalize(type)) }),
538
609
  ...(exactTypes !== undefined && { exactTypes }),
610
+ ...(declared.types === undefined && name !== undefined && { opaqueVariable: name }),
539
611
  };
540
612
  }
541
613
  /**
@@ -574,11 +646,14 @@ class Analyzer {
574
646
  this.report('wrong-arity', `Function '${node.name}' expects ${describeArity(minArity, maxArity)}, got ${node.args.length}`, node.span);
575
647
  }
576
648
  }
577
- /** The signature's input constraints: cardinality, value kind, and declared types. */
649
+ /** The signature's input constraints: cardinality, ordering, value kind, and declared types. */
578
650
  checkCallInput(node, signature, input) {
579
651
  if (!signature.input) {
580
652
  return;
581
653
  }
654
+ if (signature.input.ordered === true) {
655
+ this.requireOrdered(input, node.span, `${node.name}()`);
656
+ }
582
657
  // A function written for one type (a DTO's `@column`), called on a focus
583
658
  // that can never be that type. `unsatisfiedInput` holds the same rule the
584
659
  // engine applies, so the two halves agree on what counts as a mistake.
@@ -590,7 +665,7 @@ class Analyzer {
590
665
  this.report('singleton-required', `${node.name}() expects a single item as input, but this is a collection (spec §11)${NARROW_HINT}`, node.span);
591
666
  }
592
667
  if (signature.input.kind) {
593
- this.requireKind({ types: input.types, single: true }, signature.input.kind, node.span, `${node.name}() expects a ${signature.input.kind} input`);
668
+ this.requireKind(singleState(input.types), signature.input.kind, node.span, `${node.name}() expects a ${signature.input.kind} input`);
594
669
  }
595
670
  }
596
671
  /**
@@ -692,14 +767,14 @@ class Analyzer {
692
767
  return undefined;
693
768
  }
694
769
  const types = declared.result?.types?.map(type => this.canonicalize(type));
695
- const single = declared.result?.single;
696
770
  return {
697
771
  ...(declared.input !== undefined && { input: declared.input }),
698
772
  ...(declared.args !== undefined && { args: declared.args }),
699
773
  result: {
700
774
  kind: 'fixed',
701
775
  ...(types !== undefined && { types }),
702
- ...(single !== undefined && { single }),
776
+ ...(declared.result?.single !== undefined && { single: declared.result.single }),
777
+ ...(declared.result?.ordered !== undefined && { ordered: declared.result.ordered }),
703
778
  },
704
779
  };
705
780
  }
@@ -831,6 +906,12 @@ class Analyzer {
831
906
  this.report('singleton-required', `${message}${NARROW_HINT}`, span);
832
907
  }
833
908
  }
909
+ /** Report an order-dependent operation only when the input is provably unordered. */
910
+ requireOrdered(state, span, subject) {
911
+ if (state.ordered === false && state.single !== true) {
912
+ this.report('order-dependent', `${subject} depends on collection order, but the input collection has no defined order`, span);
913
+ }
914
+ }
834
915
  walkTypeOp(node, input, scope) {
835
916
  const operand = this.walk(node.operand, input, scope);
836
917
  this.requireSingle(operand, node.operand.span, `'${node.operator}' expects a single item operand`);
@@ -959,26 +1040,37 @@ function mergedDeclaration(candidates) {
959
1040
  }),
960
1041
  };
961
1042
  }
962
- /** Every type any declaration accepts, or undefined when one of them accepts anything. */
1043
+ /**
1044
+ * Every type any declaration accepts, or no type constraint when one of them
1045
+ * accepts anything. An ordered input stays required when every candidate
1046
+ * requires it — whichever declaration the call resolves to would reject an
1047
+ * unordered focus, so keeping the requirement cannot flag valid code.
1048
+ */
963
1049
  function mergedInput(candidates) {
1050
+ const ordered = candidates.every(candidate => candidate.signature?.input?.ordered === true);
964
1051
  const declared = candidates.map(candidate => candidate.signature?.input?.types);
965
- if (declared.some(types => types === undefined)) {
1052
+ const types = declared.some(types => types === undefined)
1053
+ ? undefined
1054
+ : [...new Set(declared.flatMap(types => types ?? []))];
1055
+ if (types === undefined && !ordered) {
966
1056
  return undefined;
967
1057
  }
968
- return { types: [...new Set(declared.flatMap(types => types ?? []))] };
1058
+ return { ...(types !== undefined && { types }), ...(ordered && { ordered: true }) };
969
1059
  }
970
1060
  /** The union of the declarations' results, unknown as soon as one of them is. */
971
1061
  function mergedResult(candidates) {
972
1062
  const union = unionStates(candidates.map(candidate => ({
973
1063
  types: candidate.signature?.result?.types === undefined ? undefined : [...candidate.signature.result.types],
974
1064
  single: candidate.signature?.result?.single,
1065
+ ordered: singletonOrder(candidate.signature?.result?.single, candidate.signature?.result?.ordered),
975
1066
  })));
976
- if (union.types === undefined && union.single === undefined) {
1067
+ if (union.types === undefined && union.single === undefined && union.ordered === undefined) {
977
1068
  return undefined;
978
1069
  }
979
1070
  return {
980
1071
  ...(union.types !== undefined && { types: union.types }),
981
1072
  ...(union.single !== undefined && { single: union.single }),
1073
+ ...(union.ordered !== undefined && { ordered: union.ordered }),
982
1074
  };
983
1075
  }
984
1076
  function typeSpecifierParts(node) {