@jarenjs/forms 0.86.0 → 0.89.0

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.
package/README.md CHANGED
@@ -451,6 +451,8 @@ paths in its query documents (plus its own location). That over-approximates
451
451
  every read, and soundly: the only way into the document is such a path, and a
452
452
  `$let`/`$for` variable can only hold what one of them produced.
453
453
 
454
+ Nested filters contribute their root reads too. Replacing the compiled rule set
455
+ invalidates the memo's results and template keys, even when the data is unchanged.
454
456
  The memo diffs the previous document against the new one — reference-equal
455
457
  subtrees are skipped whole, so an immutable edit costs O(change). A host that
456
458
  already knows what it wrote can skip even that with `memo.touch(pointer)`,
@@ -127,6 +127,7 @@ export declare function evaluateFormRules(compiled: CompiledRules, data: any, ca
127
127
  export type RuleMemo = {
128
128
  data: any;
129
129
  catalog: any;
130
+ compiled?: CompiledRules | null;
130
131
  results: Record<string, RuleResult> | null;
131
132
  keys: string[][] | null;
132
133
  /**
@@ -144,6 +145,7 @@ export type RuleMemo = {
144
145
  * @typedef {object} RuleMemo
145
146
  * @property {any} data
146
147
  * @property {any} catalog
148
+ * @property {CompiledRules|null} [compiled]
147
149
  * @property {Record<string, RuleResult>|null} results
148
150
  * @property {string[][]|null} keys
149
151
  * @property {string[]|null} touched - Pointers declared through
@@ -152,8 +154,8 @@ export type RuleMemo = {
152
154
  */
153
155
  /**
154
156
  * Create an empty rule memo. One per form session: it is bound to the
155
- * document lineage it has seen, so sharing it between two forms would
156
- * diff unrelated documents (correct, but pointlessly expensive).
157
+ * document lineage and compiled rules it has seen. Switching compiled
158
+ * rules rebuilds the memo, including the keys written by item templates.
157
159
  *
158
160
  * `memo.touch(pointer)` declares a write before the next evaluation.
159
161
  * It is an optimization AND a promise: the evaluation then trusts the
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jarenjs/forms",
3
3
  "private": false,
4
- "version": "0.86.0",
4
+ "version": "0.89.0",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "types": "./dist/types/index.d.ts",
@@ -47,9 +47,9 @@
47
47
  "prepack": "npm run build:types"
48
48
  },
49
49
  "dependencies": {
50
- "@jarenjs/core": "^0.86.0",
51
- "@jarenjs/formats": "^0.86.0",
52
- "@jarenjs/json": "^0.86.0",
53
- "@jarenjs/validate": "^0.86.0"
50
+ "@jarenjs/core": "^0.89.0",
51
+ "@jarenjs/formats": "^0.89.0",
52
+ "@jarenjs/json": "^0.89.0",
53
+ "@jarenjs/validate": "^0.89.0"
54
54
  }
55
55
  }
package/src/deps.js CHANGED
@@ -137,7 +137,12 @@ function addPath(source, out) {
137
137
  catch {
138
138
  return; // a variable-rooted path, or not a path at all
139
139
  }
140
- out.add(prefixOf(ast.segments));
140
+ collectPathQueries(ast, out);
141
+ }
142
+
143
+ /** Visit every nested filter, including those on a relative query. */
144
+ function collectPathQueries(ast, out) {
145
+ if (ast.relative !== true) out.add(prefixOf(ast.segments));
141
146
  for (const segment of ast.segments) {
142
147
  for (const selector of segment.selectors) {
143
148
  if (selector.kind === 'filter') collectFilterQueries(selector.expr, out);
@@ -168,8 +173,7 @@ function prefixOf(segments) {
168
173
  function collectFilterQueries(node, out) {
169
174
  if (node === null || typeof node !== 'object') return;
170
175
  if (node.query !== undefined && Array.isArray(node.query.segments)) {
171
- if (node.query.relative !== true) out.add(prefixOf(node.query.segments));
172
- else collectFilterQueries(node.query, out);
176
+ collectPathQueries(node.query, out);
173
177
  }
174
178
  for (const key of ['operands', 'operand', 'left', 'right', 'args', 'expr']) {
175
179
  const child = node[key];
package/src/rules.js CHANGED
@@ -416,8 +416,8 @@ function expandItemRule(rule, data, node, partIndex, pointer, ext, results, cata
416
416
  */
417
417
  export function evaluateFormRules(compiled, data, catalog = undefined, memo = undefined) {
418
418
  const rules = compiled.rules;
419
- // a catalog swap (a locale switch) invalidates every rendered message
420
- const reuse = memo !== undefined && memo.results !== null && memo.catalog === catalog;
419
+ // A rule-set or catalog swap invalidates the entire result map and its keys.
420
+ const reuse = memo !== undefined && memo.results !== null && memo.catalog === catalog && memo.compiled === compiled;
421
421
  // A declared write is taken at its word; otherwise the documents are
422
422
  // diffed. Diffing is the honest default — it needs nothing from the
423
423
  // caller — but it must scan the members of every container that
@@ -473,6 +473,7 @@ export function evaluateFormRules(compiled, data, catalog = undefined, memo = un
473
473
  if (memo !== undefined) {
474
474
  memo.data = data;
475
475
  memo.catalog = catalog;
476
+ memo.compiled = compiled;
476
477
  memo.results = results;
477
478
  memo.keys = keys;
478
479
  }
@@ -498,6 +499,7 @@ function evaluateRule(rule, data, ext, results, catalog, written) {
498
499
  * @typedef {object} RuleMemo
499
500
  * @property {any} data
500
501
  * @property {any} catalog
502
+ * @property {CompiledRules|null} [compiled]
501
503
  * @property {Record<string, RuleResult>|null} results
502
504
  * @property {string[][]|null} keys
503
505
  * @property {string[]|null} touched - Pointers declared through
@@ -507,8 +509,8 @@ function evaluateRule(rule, data, ext, results, catalog, written) {
507
509
 
508
510
  /**
509
511
  * Create an empty rule memo. One per form session: it is bound to the
510
- * document lineage it has seen, so sharing it between two forms would
511
- * diff unrelated documents (correct, but pointlessly expensive).
512
+ * document lineage and compiled rules it has seen. Switching compiled
513
+ * rules rebuilds the memo, including the keys written by item templates.
512
514
  *
513
515
  * `memo.touch(pointer)` declares a write before the next evaluation.
514
516
  * It is an optimization AND a promise: the evaluation then trusts the
@@ -526,6 +528,7 @@ export function createRuleMemo() {
526
528
  const memo = {
527
529
  data: undefined,
528
530
  catalog: undefined,
531
+ compiled: null,
529
532
  results: null,
530
533
  keys: null,
531
534
  touched: null,