@jarenjs/json 0.9.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 (57) hide show
  1. package/ARCHITECTURE.md +175 -0
  2. package/LICENSE +21 -0
  3. package/README.md +471 -0
  4. package/dist/types/basic.d.ts +32 -0
  5. package/dist/types/index.d.ts +4 -0
  6. package/dist/types/jslt/dispatch.d.ts +11 -0
  7. package/dist/types/jslt/errors.d.ts +18 -0
  8. package/dist/types/jslt/index.d.ts +53 -0
  9. package/dist/types/jslt/stylesheet.d.ts +8 -0
  10. package/dist/types/jtlt/desugar.d.ts +19 -0
  11. package/dist/types/jtlt/errors.d.ts +18 -0
  12. package/dist/types/jtlt/index.d.ts +57 -0
  13. package/dist/types/jtlt/template.d.ts +8 -0
  14. package/dist/types/jtlt/writer.d.ts +6 -0
  15. package/dist/types/path.d.ts +235 -0
  16. package/dist/types/pointer.d.ts +114 -0
  17. package/dist/types/query/compile.d.ts +21 -0
  18. package/dist/types/query/errors.d.ts +18 -0
  19. package/dist/types/query/index.d.ts +70 -0
  20. package/dist/types/query/normalize.d.ts +68 -0
  21. package/dist/types/query/operators.d.ts +424 -0
  22. package/dist/types/query/runtime.d.ts +93 -0
  23. package/dist/types/segments.d.ts +62 -0
  24. package/dist/types/xquery/index.d.ts +19 -0
  25. package/dist/types/xquery/parse.d.ts +20 -0
  26. package/docs/JSLT-FORMAT.md +861 -0
  27. package/docs/JSLT-PRELUDE.md +159 -0
  28. package/docs/JTLT-FORMAT.md +659 -0
  29. package/docs/QUERY-FORMAT.md +1221 -0
  30. package/docs/XQUERY-FRONTEND.md +321 -0
  31. package/package.json +81 -0
  32. package/schemas/jaren-jslt.draft-07.schema.json +776 -0
  33. package/schemas/jaren-jslt.schema.json +776 -0
  34. package/schemas/jaren-query.draft-07.schema.json +613 -0
  35. package/schemas/jaren-query.schema.json +375 -0
  36. package/src/basic.js +300 -0
  37. package/src/index.js +4 -0
  38. package/src/jslt/dispatch.js +934 -0
  39. package/src/jslt/errors.js +34 -0
  40. package/src/jslt/index.js +121 -0
  41. package/src/jslt/stylesheet.js +234 -0
  42. package/src/jtlt/desugar.js +231 -0
  43. package/src/jtlt/errors.js +34 -0
  44. package/src/jtlt/index.js +155 -0
  45. package/src/jtlt/template.js +130 -0
  46. package/src/jtlt/writer.js +110 -0
  47. package/src/path.js +977 -0
  48. package/src/pointer.js +453 -0
  49. package/src/query/compile.js +817 -0
  50. package/src/query/errors.js +33 -0
  51. package/src/query/index.js +150 -0
  52. package/src/query/normalize.js +1047 -0
  53. package/src/query/operators.js +1253 -0
  54. package/src/query/runtime.js +233 -0
  55. package/src/segments.js +627 -0
  56. package/src/xquery/index.js +35 -0
  57. package/src/xquery/parse.js +1647 -0
@@ -0,0 +1,233 @@
1
+ //#region Jaren JSON Query sequence runtime
2
+ // The tagged sequence representation of the query engine (QUERY-FORMAT.md
3
+ // section 2.1). Items are JSON values, and JSON arrays ARE items, so a
4
+ // sequence needs a representation that can never be confused with a value:
5
+ //
6
+ // - the empty sequence is the exported `EMPTY` singleton symbol;
7
+ // - a singleton sequence is the raw item itself (no wrapper, no
8
+ // allocation - "singleton = item", spec section 2.1 rule 5);
9
+ // - a sequence of two or more items is a `Seq` instance.
10
+ //
11
+ // Seqs are always flat: a Seq never contains another Seq, EMPTY, or fewer
12
+ // than two items. All constructors go through `seqOf`/`appendItem`, which
13
+ // maintain the invariant; `assertSeqInvariant` checks it in tests.
14
+
15
+ import { JsonQueryRuntimeError } from './errors.js';
16
+
17
+ /**
18
+ * The empty sequence `()` (same singleton-sentinel pattern as
19
+ * `JSONPATH_NOTHING`).
20
+ */
21
+ export const EMPTY = Symbol('JsonQuery.Empty');
22
+
23
+ /**
24
+ * A sequence of two or more items. Never constructed directly by
25
+ * operator code - use `seqOf` so the flatness invariant holds.
26
+ */
27
+ export class Seq {
28
+ constructor(items) {
29
+ this.items = items;
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Build a sequence value from a flat accumulator array of items.
35
+ * Returns EMPTY for zero items, the raw item for one, a Seq otherwise.
36
+ * The array is adopted, not copied; callers hand over ownership.
37
+ * @param {any[]} items - flat array of items (no Seq, no EMPTY inside)
38
+ * @returns {any} EMPTY, a single item, or a Seq
39
+ */
40
+ export function seqOf(items) {
41
+ const len = items.length;
42
+ if (len === 0)
43
+ return EMPTY;
44
+ if (len === 1)
45
+ return items[0];
46
+ return new Seq(items);
47
+ }
48
+
49
+ /**
50
+ * Append a sequence value's items to a plain accumulator array
51
+ * (the shared flattening step of array constructors and `$seq`).
52
+ * @param {any[]} list - accumulator array of items
53
+ * @param {any} v - a sequence value (EMPTY, item, or Seq)
54
+ */
55
+ export function appendItem(list, v) {
56
+ if (v === EMPTY)
57
+ return;
58
+ if (v instanceof Seq) {
59
+ const items = v.items;
60
+ for (let i = 0; i < items.length; i++)
61
+ list.push(items[i]);
62
+ return;
63
+ }
64
+ list.push(v);
65
+ }
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
+ /**
85
+ * Number of items in a sequence value.
86
+ * @param {any} v - a sequence value (EMPTY, item, or Seq)
87
+ * @returns {number}
88
+ */
89
+ export function itemCount(v) {
90
+ if (v === EMPTY)
91
+ return 0;
92
+ if (v instanceof Seq)
93
+ return v.items.length;
94
+ return 1;
95
+ }
96
+
97
+ /**
98
+ * First item of a sequence value, or EMPTY for the empty sequence.
99
+ * @param {any} v - a sequence value (EMPTY, item, or Seq)
100
+ * @returns {any}
101
+ */
102
+ export function firstItem(v) {
103
+ if (v instanceof Seq)
104
+ return v.items[0];
105
+ return v;
106
+ }
107
+
108
+ /**
109
+ * Effective boolean value of a sequence value per the EBV table of
110
+ * QUERY-FORMAT.md section 2.2 (D3: singleton array/object is true).
111
+ * @param {any} v - a sequence value (EMPTY, item, or Seq)
112
+ * @param {string} docPath - RFC 6901 pointer for the JQ2003 error
113
+ * @returns {boolean}
114
+ * @throws {JsonQueryRuntimeError} JQ2003 on a sequence of two or more items
115
+ */
116
+ export function ebv(v, docPath) {
117
+ if (v === EMPTY)
118
+ return false;
119
+ switch (typeof v) {
120
+ case 'boolean':
121
+ return v;
122
+ case 'number':
123
+ return v === v && v !== 0; // false for 0, -0, NaN
124
+ case 'string':
125
+ return v.length !== 0;
126
+ default:
127
+ if (v === null)
128
+ return false;
129
+ if (v instanceof Seq)
130
+ throw new JsonQueryRuntimeError('JQ2003',
131
+ 'the effective boolean value of a sequence of two or more items is undefined', docPath);
132
+ return true; // array or object (D3)
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Describe a sequence value for a runtime error message (non-normative,
138
+ * human-readable).
139
+ * @param {any} v - a sequence value (EMPTY, item, or Seq)
140
+ * @returns {string}
141
+ */
142
+ export function describeItem(v) {
143
+ if (v instanceof Seq)
144
+ return `a sequence of ${v.items.length} items`;
145
+ if (v === EMPTY)
146
+ return 'the empty sequence';
147
+ if (v === null)
148
+ return 'null';
149
+ if (Array.isArray(v))
150
+ return 'an array';
151
+ const t = typeof v;
152
+ return t === 'object' ? 'an object' : `a ${t}`;
153
+ }
154
+
155
+ /**
156
+ * Deterministic serialization of one JSON item, for `$groupby` keys
157
+ * (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:
160
+ *
161
+ * - object members serialize sorted by key (code-unit order), so key
162
+ * order never matters;
163
+ * - `-0` normalizes to `0` (D2: `-0` equals `0`);
164
+ * - strings serialize via `JSON.stringify` (its escape discipline means
165
+ * no raw control character ever appears in the output, so a control
166
+ * character is safe as a composite-key separator);
167
+ * - numbers serialize bare via `String(n)` — `NaN` and `±Infinity`
168
+ * (reachable through `$div`) serialize as `NaN`/`Infinity`, which
169
+ * cannot collide with quoted strings. Note this makes `NaN` group
170
+ * with `NaN`, the XQuery grouping rule, even though `NaN` never
171
+ * equals itself under `$eq`.
172
+ *
173
+ * @param {any} value - a JSON item (not EMPTY, not a Seq)
174
+ * @returns {string} a deterministic serialization for grouping
175
+ */
176
+ export function stableKeyString(value) {
177
+ switch (typeof value) {
178
+ case 'string':
179
+ return JSON.stringify(value);
180
+ case 'number':
181
+ return value === 0 ? '0' : String(value); // normalizes -0
182
+ case 'boolean':
183
+ return value ? 'true' : 'false';
184
+ default:
185
+ break;
186
+ }
187
+ if (value === null)
188
+ return 'null';
189
+ if (Array.isArray(value)) {
190
+ let s = '[';
191
+ for (let i = 0; i < value.length; i++) {
192
+ if (i > 0)
193
+ s += ',';
194
+ s += stableKeyString(value[i]);
195
+ }
196
+ return s + ']';
197
+ }
198
+ const keys = Object.keys(value).sort();
199
+ let s = '{';
200
+ for (let i = 0; i < keys.length; i++) {
201
+ if (i > 0)
202
+ s += ',';
203
+ s += JSON.stringify(keys[i]) + ':' + stableKeyString(value[keys[i]]);
204
+ }
205
+ return s + '}';
206
+ }
207
+
208
+ /**
209
+ * Debug-only invariant check: asserts a sequence value is well-formed
210
+ * (a Seq holds 2+ items and contains no nested Seq or EMPTY). Used by
211
+ * tests; never called on hot paths.
212
+ * @param {any} v - a sequence value to check
213
+ * @returns {any} v itself when well-formed
214
+ * @throws {Error} when the flatness invariant is violated
215
+ */
216
+ export function assertSeqInvariant(v) {
217
+ if (v instanceof Seq) {
218
+ if (v.items.length < 2)
219
+ throw new Error(`Seq invariant violated: ${v.items.length} item(s) in a Seq`);
220
+ for (let i = 0; i < v.items.length; i++) {
221
+ const item = v.items[i];
222
+ if (item instanceof Seq)
223
+ throw new Error(`Seq invariant violated: nested Seq at index ${i}`);
224
+ if (item === EMPTY)
225
+ throw new Error(`Seq invariant violated: EMPTY inside a Seq at index ${i}`);
226
+ if (item === undefined)
227
+ throw new Error(`Seq invariant violated: undefined inside a Seq at index ${i}`);
228
+ }
229
+ }
230
+ return v;
231
+ }
232
+
233
+ //#endregion