@jarenjs/json 0.9.2 → 0.34.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.
Files changed (74) hide show
  1. package/ARCHITECTURE.md +86 -13
  2. package/README.md +248 -23
  3. package/dist/types/canonical.d.ts +37 -0
  4. package/dist/types/cow.d.ts +28 -0
  5. package/dist/types/errors.d.ts +45 -0
  6. package/dist/types/index.d.ts +3 -0
  7. package/dist/types/jslt/errors.d.ts +15 -8
  8. package/dist/types/jslt/index.d.ts +22 -0
  9. package/dist/types/jslt/packs/finance.d.ts +119 -0
  10. package/dist/types/jslt/packs/index.d.ts +310 -0
  11. package/dist/types/jslt/packs/math.d.ts +159 -0
  12. package/dist/types/jslt/packs/stats.d.ts +48 -0
  13. package/dist/types/jslt/registry.d.ts +65 -0
  14. package/dist/types/jtlt/errors.d.ts +3 -6
  15. package/dist/types/option-variants.d.ts +29 -0
  16. package/dist/types/patch.d.ts +214 -0
  17. package/dist/types/path.d.ts +139 -9
  18. package/dist/types/pointer.d.ts +100 -9
  19. package/dist/types/query/compile.d.ts +12 -0
  20. package/dist/types/query/errors.d.ts +72 -8
  21. package/dist/types/query/index.d.ts +317 -25
  22. package/dist/types/query/normalize.d.ts +24 -0
  23. package/dist/types/query/operators.d.ts +241 -1
  24. package/dist/types/query/runtime.d.ts +5 -8
  25. package/dist/types/query/types.d.ts +34 -0
  26. package/dist/types/segments.d.ts +31 -0
  27. package/dist/types/write.d.ts +204 -0
  28. package/dist/types/xquery/parse.d.ts +2 -3
  29. package/docs/JSLT-FORMAT.md +74 -3
  30. package/docs/JSLT-PRELUDE.md +1 -1
  31. package/docs/QUERY-FORMAT.md +695 -33
  32. package/package.json +18 -4
  33. package/schemas/geojson.draft-07.schema.json +323 -0
  34. package/schemas/geojson.jaren.schema.json +863 -0
  35. package/schemas/geojson.schema.json +172 -0
  36. package/schemas/jaren-jslt.authoring.schema.json +142 -0
  37. package/schemas/jaren-jslt.draft-07.schema.json +152 -11
  38. package/schemas/jaren-jslt.llm-profile.schema.json +782 -0
  39. package/schemas/jaren-jslt.schema.json +152 -11
  40. package/schemas/jaren-query.draft-07.schema.json +152 -11
  41. package/schemas/jaren-query.llm-profile.schema.json +619 -0
  42. package/schemas/jaren-query.schema.json +82 -15
  43. package/src/basic.js +1 -1
  44. package/src/canonical.js +170 -0
  45. package/src/cow.js +106 -0
  46. package/src/errors.js +68 -0
  47. package/src/index.js +3 -0
  48. package/src/jslt/dispatch.js +178 -28
  49. package/src/jslt/errors.js +19 -14
  50. package/src/jslt/index.js +37 -29
  51. package/src/jslt/packs/finance.js +49 -0
  52. package/src/jslt/packs/index.js +18 -0
  53. package/src/jslt/packs/math.js +46 -0
  54. package/src/jslt/packs/stats.js +65 -0
  55. package/src/jslt/registry.js +200 -0
  56. package/src/jslt/stylesheet.js +14 -23
  57. package/src/jtlt/desugar.js +2 -3
  58. package/src/jtlt/errors.js +6 -12
  59. package/src/jtlt/index.js +12 -29
  60. package/src/jtlt/template.js +9 -18
  61. package/src/option-variants.js +54 -0
  62. package/src/patch.js +1052 -0
  63. package/src/path.js +319 -52
  64. package/src/pointer.js +225 -44
  65. package/src/query/compile.js +790 -75
  66. package/src/query/errors.js +72 -12
  67. package/src/query/index.js +274 -42
  68. package/src/query/normalize.js +489 -78
  69. package/src/query/operators.js +620 -23
  70. package/src/query/runtime.js +5 -19
  71. package/src/query/types.js +213 -0
  72. package/src/segments.js +409 -64
  73. package/src/write.js +660 -0
  74. package/src/xquery/parse.js +37 -53
@@ -64,23 +64,6 @@ export function appendItem(list, v) {
64
64
  list.push(v);
65
65
  }
66
66
 
67
- /**
68
- * Invoke `fn(item)` for each item of a sequence value, in order.
69
- * @param {any} v - a sequence value (EMPTY, item, or Seq)
70
- * @param {(item: any) => void} fn
71
- */
72
- export function forEachItem(v, fn) {
73
- if (v === EMPTY)
74
- return;
75
- if (v instanceof Seq) {
76
- const items = v.items;
77
- for (let i = 0; i < items.length; i++)
78
- fn(items[i]);
79
- return;
80
- }
81
- fn(v);
82
- }
83
-
84
67
  /**
85
68
  * Number of items in a sequence value.
86
69
  * @param {any} v - a sequence value (EMPTY, item, or Seq)
@@ -155,8 +138,11 @@ export function describeItem(v) {
155
138
  /**
156
139
  * Deterministic serialization of one JSON item, for `$groupby` keys
157
140
  * (QUERY-FORMAT.md section 6.5) — **engine-internal**, not an interchange
158
- * format (related to the roadmap's canonical-JSON item). It exists solely
159
- * so that deep-equal items (D2) map to the same string:
141
+ * format. For interchange (hashing, signing) use `canonicalizeJson`
142
+ * (canonical.js, RFC 8785), which is deliberately NOT this function: JCS
143
+ * rejects `NaN`/`Infinity` outright, where grouping needs them to be
144
+ * representable so that `NaN` groups with `NaN`. This exists solely so
145
+ * that deep-equal items (D2) map to the same string:
160
146
  *
161
147
  * - object members serialize sorted by key (code-unit order), so key
162
148
  * order never matters;
@@ -0,0 +1,213 @@
1
+ //@ts-check
2
+ /**
3
+ * @file Optional type annotation over the published normalized form
4
+ * (QUERY-FORMAT.md Appendix C.8). The AST carries cardinality but no
5
+ * value types; a consumer that owns a type source — a store's JSON
6
+ * Schema, a model document — supplies `typeOf(pathNode)` and gets back
7
+ * a NEW frozen tree mirroring the input with a `type` tag on every
8
+ * node. The pass never mutates its input and never runs during
9
+ * compilation.
10
+ *
11
+ * The lattice is small and closed: `unknown` is the top and always a
12
+ * safe answer; a wrong tag is a defect. Operators propagate through the
13
+ * registry's declared `resultType` families (comparison, arithmetic,
14
+ * string, aggregate); everything undeclared yields `unknown` rather
15
+ * than a guess.
16
+ */
17
+
18
+ import { OPERATORS } from './operators.js';
19
+ import { CARD_ONE } from './normalize.js';
20
+
21
+ /** The closed set of type-tag names, sorted. */
22
+ export const TYPE_TAGS = Object.freeze([
23
+ 'array', 'boolean', 'integer', 'null', 'number', 'object', 'string', 'unknown',
24
+ ]);
25
+
26
+ // Interned tag objects: one frozen { type, optional } per combination,
27
+ // so annotated trees share tags by identity and comparisons are cheap.
28
+ const TAGS = (() => {
29
+ /** @type {Record<string, Readonly<{type: string, optional: boolean}>>} */
30
+ const table = {};
31
+ for (const type of TYPE_TAGS) {
32
+ table[`${type}:0`] = Object.freeze({ type, optional: false });
33
+ table[`${type}:1`] = Object.freeze({ type, optional: true });
34
+ }
35
+ return Object.freeze(table);
36
+ })();
37
+
38
+ /**
39
+ * The interned tag for a type name and optionality.
40
+ * @param {string} type - one of {@link TYPE_TAGS}
41
+ * @param {boolean} optional
42
+ */
43
+ function tagOf(type, optional) {
44
+ return TAGS[`${type}:${optional ? 1 : 0}`];
45
+ }
46
+
47
+ /**
48
+ * Join two tag names: equal names join to themselves, `integer` widens
49
+ * into `number`, anything else joins to `unknown` (the top).
50
+ * @param {string} a
51
+ * @param {string} b
52
+ * @returns {string}
53
+ */
54
+ function joinTagName(a, b) {
55
+ if (a === b) return a;
56
+ if ((a === 'integer' && b === 'number') || (a === 'number' && b === 'integer'))
57
+ return 'number';
58
+ return 'unknown';
59
+ }
60
+
61
+ /** The tag name of a literal JSON value. @param {any} value */
62
+ function literalTagName(value) {
63
+ if (value === null) return 'null';
64
+ switch (typeof value) {
65
+ case 'boolean': return 'boolean';
66
+ case 'number': return Number.isInteger(value) ? 'integer' : 'number';
67
+ case 'string': return 'string';
68
+ default: return Array.isArray(value) ? 'array' : 'object';
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Normalize whatever the caller's `typeOf` returned into an interned
74
+ * tag: a bare tag name (optionality derived from the node's own
75
+ * cardinality), a `{ type, optional }` object, or `null`/`undefined`
76
+ * for "unknown". Anything outside the lattice is `unknown` — a safe
77
+ * answer, never a throw, because a sloppy hook must not fail analysis.
78
+ * @param {any} answer
79
+ * @param {any} node
80
+ */
81
+ function normalizeHookAnswer(answer, node) {
82
+ const fallback = node.card !== CARD_ONE;
83
+ if (answer == null) return tagOf('unknown', fallback);
84
+ if (typeof answer === 'string')
85
+ return TYPE_TAGS.includes(answer) ? tagOf(answer, fallback) : tagOf('unknown', fallback);
86
+ const type = TYPE_TAGS.includes(answer.type) ? answer.type : 'unknown';
87
+ return tagOf(type, answer.optional === true);
88
+ }
89
+
90
+ /**
91
+ * Annotate a normalized analysis with type tags (Appendix C.8): a NEW
92
+ * frozen analysis whose tree mirrors the input with `type` on every
93
+ * node. The input analysis and its tree are never mutated.
94
+ * @param {{ astVersion: number, root: any }} analysis - an
95
+ * `analyzeQuery` result (or any record carrying the frozen `root`)
96
+ * @param {{ typeOf?: (pathNode: any) => any }} [hooks] - `typeOf`
97
+ * answers for `path` nodes: a tag name, a `{ type, optional }`
98
+ * object, or `null` for unknown
99
+ * @returns {any} the annotated analysis
100
+ */
101
+ export function annotateTypes(analysis, hooks = {}) {
102
+ const typeOf = typeof hooks.typeOf === 'function' ? hooks.typeOf : null;
103
+
104
+ /** @param {any} node @returns {any} */
105
+ function annotate(node) {
106
+ switch (node.kind) {
107
+ case 'literal':
108
+ return Object.freeze({ ...node, type: tagOf(literalTagName(node.value), false) });
109
+ case 'var':
110
+ return Object.freeze({ ...node, type: tagOf('unknown', node.card !== CARD_ONE) });
111
+ case 'path': {
112
+ const answer = typeOf === null ? null : typeOf(node);
113
+ return Object.freeze({ ...node, type: normalizeHookAnswer(answer, node) });
114
+ }
115
+ case 'object':
116
+ return Object.freeze({
117
+ ...node,
118
+ entries: Object.freeze(node.entries.map(
119
+ (e) => Object.freeze({ ...e, expr: annotate(e.expr) }))),
120
+ type: tagOf('object', false),
121
+ });
122
+ case 'map':
123
+ return Object.freeze({
124
+ ...node,
125
+ pairs: Object.freeze(node.pairs.map(
126
+ (p) => Object.freeze({ ...p, key: annotate(p.key), value: annotate(p.value) }))),
127
+ type: tagOf('object', false),
128
+ });
129
+ case 'array':
130
+ return Object.freeze({
131
+ ...node,
132
+ elements: Object.freeze(node.elements.map(annotate)),
133
+ type: tagOf('array', false),
134
+ });
135
+ case 'raw':
136
+ return Object.freeze({ ...node, type: tagOf('unknown', false) });
137
+ case 'op': {
138
+ const args = Object.freeze(node.args.map(annotate));
139
+ // a registered operator (options.extensions / the JSLT registry)
140
+ // carries its entry on the node — fall back to it so a pack op's
141
+ // declared resultType flows through the analyzer, exactly as
142
+ // compileOp resolves `OPERATORS[name] ?? node.entry`
143
+ const entry = OPERATORS[node.name] ?? node.entry;
144
+ const resultType = entry !== undefined && typeof entry.resultType === 'function'
145
+ ? entry.resultType(args.map((a) => a.type))
146
+ : null;
147
+ const name = typeof resultType === 'string' && TYPE_TAGS.includes(resultType)
148
+ ? resultType
149
+ : 'unknown';
150
+ return Object.freeze({ ...node, args, type: tagOf(name, node.card !== CARD_ONE) });
151
+ }
152
+ case 'call':
153
+ return Object.freeze({
154
+ ...node,
155
+ args: Object.freeze(node.args.map(annotate)),
156
+ type: tagOf('unknown', true),
157
+ });
158
+ case 'let': {
159
+ const bindings = Object.freeze(node.bindings.map(
160
+ (b) => Object.freeze({ ...b, expr: annotate(b.expr) })));
161
+ const ret = annotate(node.ret);
162
+ return Object.freeze({
163
+ ...node, bindings, ret,
164
+ type: tagOf(ret.type.type, node.card !== CARD_ONE),
165
+ });
166
+ }
167
+ case 'quant':
168
+ return Object.freeze({
169
+ ...node,
170
+ bindings: Object.freeze(node.bindings.map(
171
+ (b) => Object.freeze({ ...b, expr: annotate(b.expr) }))),
172
+ satisfies: annotate(node.satisfies),
173
+ type: tagOf('boolean', false),
174
+ });
175
+ default: { // 'flwor'
176
+ const fold = node.fold === null
177
+ ? null
178
+ : Object.freeze({ ...node.fold, expr: annotate(node.fold.expr) });
179
+ const forBindings = Object.freeze(node.forBindings.map(
180
+ (b) => Object.freeze({ ...b, expr: annotate(b.expr) })));
181
+ const letBindings = Object.freeze(node.letBindings.map(
182
+ (b) => Object.freeze({ ...b, expr: annotate(b.expr) })));
183
+ const where = node.where === null ? null : annotate(node.where);
184
+ const groupby = node.groupby === null
185
+ ? null
186
+ : Object.freeze({
187
+ ...node.groupby,
188
+ keys: Object.freeze(node.groupby.keys.map(
189
+ (k) => Object.freeze({ ...k, expr: annotate(k.expr) }))),
190
+ });
191
+ const orderby = node.orderby === null
192
+ ? null
193
+ : Object.freeze({
194
+ ...node.orderby,
195
+ specs: Object.freeze(node.orderby.specs.map(
196
+ (s) => Object.freeze({ ...s, key: annotate(s.key) }))),
197
+ });
198
+ const ret = annotate(node.ret);
199
+ // the phrase's ITEM type: the $return item's tag (joined with the
200
+ // accumulator's for $fold — either can be the final value)
201
+ const itemTag = fold === null
202
+ ? ret.type.type
203
+ : joinTagName(fold.expr.type.type, ret.type.type);
204
+ return Object.freeze({
205
+ ...node, fold, forBindings, letBindings, where, groupby, orderby, ret,
206
+ type: tagOf(itemTag, node.card !== CARD_ONE),
207
+ });
208
+ }
209
+ }
210
+ }
211
+
212
+ return Object.freeze({ ...analysis, root: annotate(analysis.root) });
213
+ }