@peers-app/peers-sdk 0.26.6 → 0.27.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 (182) hide show
  1. package/README.md +3 -1
  2. package/bin/peers-package.js +13 -0
  3. package/define/package.json +4 -0
  4. package/dist/contracts/__tests__/action-dispatch.test.js +56 -76
  5. package/dist/contracts/__tests__/also-implements.test.d.ts +1 -0
  6. package/dist/contracts/__tests__/also-implements.test.js +110 -0
  7. package/dist/contracts/__tests__/builder.test.js +45 -0
  8. package/dist/contracts/__tests__/contract-resolvers.test.js +134 -4
  9. package/dist/contracts/__tests__/frozen-contracts.test.d.ts +1 -0
  10. package/dist/contracts/__tests__/frozen-contracts.test.js +191 -0
  11. package/dist/contracts/__tests__/generic-tool-dispatch.test.js +13 -56
  12. package/dist/contracts/__tests__/host-contract-client.test.d.ts +1 -0
  13. package/dist/contracts/__tests__/host-contract-client.test.js +793 -0
  14. package/dist/contracts/__tests__/host-contract-transport.test.js +15 -0
  15. package/dist/contracts/__tests__/registry.test.js +68 -0
  16. package/dist/contracts/__tests__/target-dispatch.d.ts +11 -0
  17. package/dist/contracts/__tests__/target-dispatch.js +24 -0
  18. package/dist/contracts/__tests__/tool-catalog-service.test.js +37 -1
  19. package/dist/contracts/__tests__/tool-catalog.test.js +12 -3
  20. package/dist/contracts/__tests__/validate.test.js +177 -7
  21. package/dist/contracts/__tests__/workflow-context.test.js +0 -11
  22. package/dist/contracts/builder.d.ts +16 -2
  23. package/dist/contracts/builder.js +44 -3
  24. package/dist/contracts/contract-definition-schema.d.ts +251 -0
  25. package/dist/contracts/contract-definition-schema.js +48 -0
  26. package/dist/contracts/contract-provider-router.d.ts +2 -0
  27. package/dist/contracts/contract-provider-router.js +5 -2
  28. package/dist/contracts/contract-proxy.d.ts +52 -18
  29. package/dist/contracts/contract-proxy.js +144 -203
  30. package/dist/contracts/contract-resolvers.d.ts +6 -4
  31. package/dist/contracts/contract-resolvers.js +32 -9
  32. package/dist/contracts/frozen-contracts.d.ts +59 -0
  33. package/dist/contracts/frozen-contracts.js +86 -0
  34. package/dist/contracts/host-contract-client.d.ts +134 -0
  35. package/dist/contracts/host-contract-client.js +396 -0
  36. package/dist/contracts/host-contract-transport.d.ts +2 -0
  37. package/dist/contracts/host-contract-transport.js +8 -0
  38. package/dist/contracts/index.d.ts +6 -4
  39. package/dist/contracts/index.js +11 -5
  40. package/dist/contracts/registry.d.ts +31 -1
  41. package/dist/contracts/registry.js +100 -6
  42. package/dist/contracts/system/contract-catalog-contract.d.ts +61 -6
  43. package/dist/contracts/system/contract-catalog-contract.js +17 -5
  44. package/dist/contracts/tool-catalog-service.js +26 -6
  45. package/dist/contracts/tool-catalog.d.ts +1 -1
  46. package/dist/contracts/tool-catalog.js +4 -1
  47. package/dist/contracts/types.d.ts +32 -2
  48. package/dist/contracts/validate.d.ts +16 -1
  49. package/dist/contracts/validate.js +139 -25
  50. package/dist/data/change-tracking.d.ts +17 -5
  51. package/dist/data/change-tracking.js +22 -6
  52. package/dist/data/change-tracking.test.d.ts +1 -0
  53. package/dist/data/change-tracking.test.js +62 -0
  54. package/dist/data/orm/sql.data-source.d.ts +9 -0
  55. package/dist/data/orm/sql.data-source.js +47 -2
  56. package/dist/data/orm/sql.data-source.test.js +142 -0
  57. package/dist/data/orm/table-container.js +9 -9
  58. package/dist/data/orm/table-container.test.js +44 -0
  59. package/dist/data/orm/types.d.ts +41 -1
  60. package/dist/data/orm/types.js +159 -28
  61. package/dist/data/orm/types.test.js +129 -0
  62. package/dist/data/peer-types.d.ts +8 -62
  63. package/dist/data/persistent-vars.d.ts +4 -1
  64. package/dist/data/persistent-vars.js +7 -2
  65. package/dist/data/tools.d.ts +29 -221
  66. package/dist/define/also-implements.test.d.ts +1 -0
  67. package/dist/define/also-implements.test.js +123 -0
  68. package/dist/define/context.d.ts +77 -0
  69. package/dist/define/context.js +2 -0
  70. package/dist/define/contract-ref.d.ts +177 -0
  71. package/dist/define/contract-ref.js +67 -0
  72. package/dist/define/declare-contract.d.ts +167 -0
  73. package/dist/define/declare-contract.js +220 -0
  74. package/dist/define/declare-contract.test.d.ts +1 -0
  75. package/dist/define/declare-contract.test.js +573 -0
  76. package/dist/define/define-package.d.ts +203 -0
  77. package/dist/define/define-package.js +289 -0
  78. package/dist/define/define.test.d.ts +1 -0
  79. package/dist/define/define.test.js +1010 -0
  80. package/dist/define/empty-fields.d.ts +15 -0
  81. package/dist/define/empty-fields.js +134 -0
  82. package/dist/define/empty-fields.test.d.ts +1 -0
  83. package/dist/define/empty-fields.test.js +55 -0
  84. package/dist/define/events-observables.test.d.ts +1 -0
  85. package/dist/define/events-observables.test.js +1013 -0
  86. package/dist/define/events.d.ts +81 -0
  87. package/dist/define/events.js +81 -0
  88. package/dist/define/generated-field.d.ts +20 -0
  89. package/dist/define/generated-field.js +25 -0
  90. package/dist/define/index.d.ts +28 -0
  91. package/dist/define/index.js +62 -0
  92. package/dist/define/lazy-members.d.ts +8 -0
  93. package/dist/define/lazy-members.js +38 -0
  94. package/dist/define/manifest.d.ts +63 -0
  95. package/dist/define/manifest.js +653 -0
  96. package/dist/define/observables.d.ts +146 -0
  97. package/dist/define/observables.js +187 -0
  98. package/dist/define/serve-members.d.ts +21 -0
  99. package/dist/define/serve-members.js +152 -0
  100. package/dist/define/serve-members.test.d.ts +1 -0
  101. package/dist/define/serve-members.test.js +630 -0
  102. package/dist/define/serve.d.ts +22 -0
  103. package/dist/define/serve.js +58 -0
  104. package/dist/define/system-contracts.d.ts +41 -0
  105. package/dist/define/system-contracts.js +21 -0
  106. package/dist/define/table-handle.d.ts +13 -0
  107. package/dist/define/table-handle.js +36 -0
  108. package/dist/define/table.d.ts +128 -0
  109. package/dist/define/table.js +122 -0
  110. package/dist/define/tool.d.ts +129 -0
  111. package/dist/define/tool.js +91 -0
  112. package/dist/define/vars.d.ts +82 -0
  113. package/dist/define/vars.js +256 -0
  114. package/dist/define/vars.test.d.ts +1 -0
  115. package/dist/define/vars.test.js +39 -0
  116. package/dist/define/wire-value.d.ts +12 -0
  117. package/dist/define/wire-value.js +76 -0
  118. package/dist/define/worker-state.d.ts +15 -0
  119. package/dist/define/worker-state.js +45 -0
  120. package/dist/device/tx-encoding.test.js +10 -0
  121. package/dist/index.d.ts +3 -0
  122. package/dist/index.js +5 -1
  123. package/dist/observable.d.ts +42 -2
  124. package/dist/observable.js +25 -7
  125. package/dist/package-build/build-package.d.ts +60 -0
  126. package/dist/package-build/build-package.js +312 -0
  127. package/dist/package-build/build-package.test.d.ts +1 -0
  128. package/dist/package-build/build-package.test.js +204 -0
  129. package/dist/package-build/cli.d.ts +7 -0
  130. package/dist/package-build/cli.js +218 -0
  131. package/dist/package-build/contract-add.d.ts +35 -0
  132. package/dist/package-build/contract-add.js +97 -0
  133. package/dist/package-build/contract-add.test.d.ts +1 -0
  134. package/dist/package-build/contract-add.test.js +467 -0
  135. package/dist/package-build/contract-source.d.ts +59 -0
  136. package/dist/package-build/contract-source.js +471 -0
  137. package/dist/package-build/freeze-contracts.d.ts +56 -0
  138. package/dist/package-build/freeze-contracts.js +198 -0
  139. package/dist/package-build/freeze-contracts.test.d.ts +1 -0
  140. package/dist/package-build/freeze-contracts.test.js +355 -0
  141. package/dist/package-installer/package-creator.d.ts +4 -0
  142. package/dist/package-installer/package-creator.js +57 -16
  143. package/dist/package-installer/package-creator.test.js +86 -0
  144. package/dist/package-loader/contract-members.d.ts +53 -0
  145. package/dist/package-loader/contract-members.js +31 -0
  146. package/dist/package-loader/contract-package-loader.js +5 -1
  147. package/dist/package-loader/contract-package-loader.test.js +11 -0
  148. package/dist/package-loader/host-authority-loading.test.js +9 -5
  149. package/dist/package-loader/index.d.ts +1 -0
  150. package/dist/package-loader/index.js +1 -0
  151. package/dist/package-loader/isolated-artifact.d.ts +80 -17
  152. package/dist/package-loader/isolated-artifact.js +257 -51
  153. package/dist/package-loader/isolated-artifact.test.js +294 -21
  154. package/dist/package-loader/isolated-runtime.d.ts +22 -7
  155. package/dist/package-loader/isolated-table-gateway.js +30 -11
  156. package/dist/package-loader/isolated-table-gateway.test.js +243 -0
  157. package/dist/package-loader/package-loader.d.ts +8 -1
  158. package/dist/package-loader/package-loader.js +39 -20
  159. package/dist/package-loader/package-loader.test.js +47 -0
  160. package/dist/peer-id.d.ts +25 -0
  161. package/dist/peer-id.js +99 -0
  162. package/dist/serial-json.compat.test.d.ts +1 -0
  163. package/dist/serial-json.compat.test.js +369 -0
  164. package/dist/serial-json.d.ts +41 -5
  165. package/dist/serial-json.js +216 -157
  166. package/dist/serial-json.oracle.d.ts +21 -0
  167. package/dist/serial-json.oracle.js +222 -0
  168. package/dist/serial-json.test.js +315 -0
  169. package/dist/testing/index.d.ts +4 -0
  170. package/dist/testing/index.js +8 -0
  171. package/dist/testing/mock-host-contract.d.ts +123 -0
  172. package/dist/testing/mock-host-contract.js +434 -0
  173. package/dist/testing/mock-host-contract.test.d.ts +1 -0
  174. package/dist/testing/mock-host-contract.test.js +782 -0
  175. package/dist/types/field-type.d.ts +35 -18
  176. package/dist/types/field-type.js +4 -1
  177. package/dist/types/zod-types.d.ts +9 -0
  178. package/dist/types/zod-types.js +8 -2
  179. package/dist/utils.d.ts +1 -25
  180. package/dist/utils.js +10 -90
  181. package/package.json +9 -2
  182. package/testing/package.json +4 -0
@@ -0,0 +1,369 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_vm_1 = require("node:vm");
4
+ const msgpack_1 = require("@msgpack/msgpack");
5
+ const serial_json_1 = require("./serial-json");
6
+ const oracle = require("./serial-json.oracle");
7
+ // Strings and keys that exercise every marker the decoder interprets. "__WINDOW" is left out
8
+ // because the oracle decodes it to `window`, which throws in Node.
9
+ const STRINGS = [
10
+ "",
11
+ "plain",
12
+ "héllo 世界 🚀",
13
+ "$dollar",
14
+ "__",
15
+ "__UNDEFINED",
16
+ "__NAN",
17
+ "__INFINITY",
18
+ "__DATE 2023-01-01T00:00:00.000Z",
19
+ "__DATE garbage",
20
+ "__REGEXP /a.c/g",
21
+ "__ERROR Error: x\n at nowhere",
22
+ "__UINT8ARRAY AQID",
23
+ "__BUFFER AQID",
24
+ "__HTML <b>x</b>",
25
+ "__FUNCTION f(){}",
26
+ "__duplicate_ref_obj_7",
27
+ "__duplicate_ref_ary_3",
28
+ "__this_ref:__duplicate_ref_ary_5",
29
+ "__DOLLAR_x",
30
+ ];
31
+ const KEYS = ["a", "b", "c", "0", "$a", "$", "__DOLLAR_b", "__this_ref", "constructor", "toString"];
32
+ const NUMBERS = [0, 1, -1, 1.5, -0, NaN, Infinity, -Infinity, 1e21, Number.MAX_SAFE_INTEGER];
33
+ /** Deterministic PRNG (mulberry32), so a failure names a reproducible seed. */
34
+ function prng(seed) {
35
+ let state = seed;
36
+ return () => {
37
+ state = (state + 0x6d2b79f5) | 0;
38
+ let t = Math.imul(state ^ (state >>> 15), 1 | state);
39
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
40
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
41
+ };
42
+ }
43
+ /** A random value graph mixing extended types, marker-like strings, shared nodes and cycles. */
44
+ function generate(seed) {
45
+ const random = prng(seed);
46
+ const pick = (items) => items[Math.floor(random() * items.length)];
47
+ const pool = [];
48
+ const ancestors = [];
49
+ function leaf() {
50
+ switch (Math.floor(random() * 11)) {
51
+ case 0:
52
+ case 1:
53
+ return pick(STRINGS);
54
+ case 2:
55
+ return pick(NUMBERS);
56
+ case 3:
57
+ return random() < 0.5;
58
+ case 4:
59
+ return null;
60
+ case 5:
61
+ return undefined;
62
+ case 6:
63
+ return new Date(Math.floor(random() * 4e12));
64
+ case 7:
65
+ return random() < 0.5 ? new Uint8Array([1, 2, 3]) : Buffer.from([4, 5]);
66
+ case 8:
67
+ return new RegExp(pick(["a.c", "^x$", "[0-9]+"]), pick(["", "g", "gi"]));
68
+ case 9:
69
+ return new Error(pick(["boom", ""]));
70
+ default:
71
+ return () => 1;
72
+ }
73
+ }
74
+ function value(depth) {
75
+ const roll = random();
76
+ if (roll < 0.06 && pool.length)
77
+ return pick(pool);
78
+ if (roll < 0.09 && ancestors.length)
79
+ return pick(ancestors);
80
+ if (depth <= 0 || roll > 0.45)
81
+ return leaf();
82
+ const container = random() < 0.5 ? [] : {};
83
+ ancestors.push(container);
84
+ const size = Math.floor(random() * 5);
85
+ for (let i = 0; i < size; i++) {
86
+ const child = value(depth - 1);
87
+ if (Array.isArray(container))
88
+ container.push(child);
89
+ else
90
+ container[pick(KEYS)] = child;
91
+ }
92
+ ancestors.pop();
93
+ pool.push(container);
94
+ return container;
95
+ }
96
+ // Top level is always a container, as in columns, change values and messages.
97
+ let root = value(4);
98
+ while (root === null || typeof root !== "object" || root instanceof Date)
99
+ root = value(4);
100
+ return root;
101
+ }
102
+ function deepFreeze(value) {
103
+ // Typed arrays with elements can't be frozen; nothing writes to them anyway.
104
+ if (value && typeof value === "object" && !ArrayBuffer.isView(value) && !Object.isFrozen(value)) {
105
+ Object.freeze(value);
106
+ for (const child of Object.values(value))
107
+ deepFreeze(child);
108
+ }
109
+ return value;
110
+ }
111
+ /**
112
+ * First difference between two value graphs, or null. Compares types, prototypes, values, key
113
+ * order, and sharing: a node shared (or cyclic) in one graph must be shared the same way in the other.
114
+ */
115
+ function graphDiff(actual, expected, path = "$", pairs = new Map(), reversePairs = new Map()) {
116
+ if (typeof actual !== typeof expected)
117
+ return `${path}: ${typeof actual} vs ${typeof expected}`;
118
+ if (typeof actual === "number")
119
+ return Object.is(actual, expected) ? null : `${path}: ${actual} vs ${expected}`;
120
+ if (actual === null || expected === null || typeof actual !== "object") {
121
+ return actual === expected ? null : `${path}: ${String(actual)} vs ${String(expected)}`;
122
+ }
123
+ const a = actual;
124
+ const b = expected;
125
+ if (pairs.has(a) || reversePairs.has(b)) {
126
+ return pairs.get(a) === b ? null : `${path}: sharing differs`;
127
+ }
128
+ pairs.set(a, b);
129
+ reversePairs.set(b, a);
130
+ if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b))
131
+ return `${path}: prototype differs`;
132
+ if (Array.isArray(a) && a.length !== b.length)
133
+ return `${path}: array length differs`;
134
+ if (a instanceof Date)
135
+ return Object.is(a.getTime(), b.getTime()) ? null : `${path}: date differs`;
136
+ if (a instanceof RegExp)
137
+ return String(a) === String(b) ? null : `${path}: regexp differs`;
138
+ if (a instanceof Error)
139
+ return a.stack === b.stack ? null : `${path}: error stack differs`;
140
+ if (a instanceof Uint8Array)
141
+ return Buffer.compare(a, b) === 0 ? null : `${path}: bytes differ`;
142
+ const keysA = Object.keys(a);
143
+ const keysB = Object.keys(b);
144
+ if (keysA.length !== keysB.length || keysA.some((key, index) => key !== keysB[index])) {
145
+ return `${path}: keys [${keysA}] vs [${keysB}]`;
146
+ }
147
+ for (const key of keysA) {
148
+ const diff = graphDiff(a[key], b[key], `${path}.${key}`, pairs, reversePairs);
149
+ if (diff)
150
+ return diff;
151
+ }
152
+ return null;
153
+ }
154
+ const SEEDS = Array.from({ length: 1500 }, (_, i) => i + 1);
155
+ /**
156
+ * True when an encoded tree tags an object with an `_ary_` marker or an array with an `_obj_`
157
+ * one. toJSON only writes that when a user string collides with a marker; the live decoder no
158
+ * longer merges such a pair (see "does not let an object fill an array target" in
159
+ * serial-json.test.ts), so those seeds are compared separately.
160
+ */
161
+ function hasMixedKindTag(encoded) {
162
+ if (Array.isArray(encoded)) {
163
+ const [first] = encoded;
164
+ if (typeof first === "string" && first.startsWith("__this_ref:") && first.includes("_obj_")) {
165
+ return true;
166
+ }
167
+ return encoded.some(hasMixedKindTag);
168
+ }
169
+ if (encoded && typeof encoded === "object") {
170
+ const tag = encoded.__this_ref;
171
+ if (typeof tag === "string" && tag.includes("_ary_"))
172
+ return true;
173
+ return Object.values(encoded).some(hasMixedKindTag);
174
+ }
175
+ return false;
176
+ }
177
+ describe("serial-json compatibility with the frozen oracle", () => {
178
+ // The corpus holds marker-like strings with no target; the live decoder warns about those.
179
+ beforeAll(() => {
180
+ jest.spyOn(console, "warn").mockImplementation(() => undefined);
181
+ });
182
+ afterAll(() => {
183
+ jest.restoreAllMocks();
184
+ });
185
+ it("detects differences in values, key order, sparse lengths and sharing", () => {
186
+ const shared = {};
187
+ expect(graphDiff({ x: 1 }, { x: 2 })).not.toBeNull();
188
+ expect(graphDiff({ x: 1, y: 2 }, { y: 2, x: 1 })).not.toBeNull();
189
+ expect(graphDiff({ "a\u0000b": undefined }, { a: undefined, b: undefined })).not.toBeNull();
190
+ expect(graphDiff([], new Array(1))).not.toBeNull();
191
+ expect(graphDiff([shared, shared], [{}, {}])).not.toBeNull();
192
+ expect(graphDiff([{}, {}], [shared, shared])).not.toBeNull();
193
+ const cycle = {};
194
+ cycle.self = cycle;
195
+ expect(graphDiff(cycle, { self: {} })).not.toBeNull();
196
+ });
197
+ it.each([
198
+ ["sparse arrays", () => ({ a: new Array(3), b: Object.assign(new Array(3), { 0: 1, 2: 3 }) })],
199
+ ["null prototypes", () => Object.assign(Object.create(null), { a: 1, $b: {} })],
200
+ ["boxed strings", () => ({ a: new String("__DATE 2000-01-01") })],
201
+ [
202
+ "another realm",
203
+ () => (0, node_vm_1.runInNewContext)("({ a: [1, , 3], d: new Date(0), r: /x/g, u: new Uint8Array([1]) })"),
204
+ ],
205
+ [
206
+ "getters that change later properties",
207
+ () => {
208
+ const value = {
209
+ a: {
210
+ get x() {
211
+ value.b = 2;
212
+ return value;
213
+ },
214
+ },
215
+ b: 1,
216
+ };
217
+ return value;
218
+ },
219
+ ],
220
+ [
221
+ "callables inheriting from RegExp",
222
+ () => {
223
+ const value = () => 1;
224
+ Object.setPrototypeOf(value, RegExp.prototype);
225
+ value.toString = () => "/x/g";
226
+ return value;
227
+ },
228
+ ],
229
+ ])("encodes %s exactly like the oracle", (_name, makeValue) => {
230
+ expect((0, serial_json_1.toJSONString)(makeValue())).toBe(oracle.toJSONString(makeValue()));
231
+ expect(graphDiff((0, serial_json_1.toJSON)(makeValue()), oracle.toJSON(makeValue()))).toBeNull();
232
+ });
233
+ // A tag that names a target of the other kind (object vs array) is an intended difference; see
234
+ // serial-json.test.ts.
235
+ it.each([
236
+ [["__this_ref:__duplicate_ref_obj_1", 1, 2], "__duplicate_ref_obj_1"],
237
+ [
238
+ { __this_ref: "__duplicate_ref_obj_1", a: 1 },
239
+ { __this_ref: "__duplicate_ref_obj_1", b: 2 },
240
+ "__duplicate_ref_obj_1",
241
+ ],
242
+ [
243
+ "__duplicate_ref_ary_1",
244
+ ["__this_ref:__duplicate_ref_ary_1", "first", "extra"],
245
+ ["__this_ref:__duplicate_ref_ary_1", "last"],
246
+ ],
247
+ ["__this_ref:__duplicate_ref_ary_1:ignored", "__duplicate_ref_ary_1"],
248
+ [
249
+ { __this_ref: 1, a: 1 },
250
+ { __this_ref: "1", b: 2 },
251
+ ],
252
+ ].map((value) => [value]))("decodes unusual and repeated tags: %j", (value) => {
253
+ expect(graphDiff((0, serial_json_1.fromJSON)(deepFreeze(value)), oracle.fromJSON(value))).toBeNull();
254
+ });
255
+ it.each([
256
+ "plain",
257
+ "__UNDEFINED",
258
+ "__NAN",
259
+ "__DATE 2000-01-01",
260
+ "__REGEXP /x/g",
261
+ "__BUFFER AQID",
262
+ "__duplicate_ref_obj_boxed",
263
+ ])("decodes boxed strings like the oracle: %s", (text) => {
264
+ expect(graphDiff((0, serial_json_1.fromJSON)(new String(text)), oracle.fromJSON(new String(text)))).toBeNull();
265
+ });
266
+ it("recognizes a boxed array tag", () => {
267
+ const value = [new String("__this_ref:__duplicate_ref_ary_1"), "__duplicate_ref_ary_1"];
268
+ expect(graphDiff((0, serial_json_1.fromJSON)(value), oracle.fromJSON(value))).toBeNull();
269
+ });
270
+ it("generates graphs with sharing, cycles and every marker", () => {
271
+ // Guards the corpus itself: if the generator stops producing these, the checks below go soft.
272
+ let sharing = false;
273
+ let cycles = false;
274
+ const visit = (value, seen, ancestors) => {
275
+ if (!value || typeof value !== "object")
276
+ return;
277
+ if (ancestors.has(value))
278
+ cycles = true;
279
+ if (seen.has(value)) {
280
+ sharing = true;
281
+ return;
282
+ }
283
+ seen.add(value);
284
+ ancestors.add(value);
285
+ for (const child of Object.values(value))
286
+ visit(child, seen, ancestors);
287
+ ancestors.delete(value);
288
+ };
289
+ for (const seed of SEEDS)
290
+ visit(generate(seed), new Set(), new Set());
291
+ expect(sharing).toBe(true);
292
+ expect(cycles).toBe(true);
293
+ const encoded = SEEDS.map((seed) => oracle.toJSONString(generate(seed))).join("\n");
294
+ for (const marker of [
295
+ "__this_ref",
296
+ "__duplicate_ref_ary_",
297
+ "__DOLLAR_",
298
+ "__DATE",
299
+ "__ERROR",
300
+ "__BUFFER",
301
+ "__UINT8ARRAY",
302
+ "__UNDEFINED",
303
+ "__NAN",
304
+ "__INFINITY",
305
+ "__REGEXP",
306
+ "__FUNCTION",
307
+ ]) {
308
+ expect(encoded).toContain(marker);
309
+ }
310
+ });
311
+ it("encodes byte-identically, as text and as an in-memory tree", () => {
312
+ for (const seed of SEEDS) {
313
+ const value = generate(seed);
314
+ expect({ seed, text: (0, serial_json_1.toJSONString)(value) }).toEqual({
315
+ seed,
316
+ text: oracle.toJSONString(value),
317
+ });
318
+ expect({ seed, diff: graphDiff((0, serial_json_1.toJSON)(value), oracle.toJSON(value)) }).toEqual({
319
+ seed,
320
+ diff: null,
321
+ });
322
+ }
323
+ });
324
+ const MIXED_KIND_SEEDS = new Set(SEEDS.filter((seed) => hasMixedKindTag(oracle.toJSON(generate(seed)))));
325
+ it("has a few mixed-kind tags from marker-like strings, and decodes them differently", () => {
326
+ expect(MIXED_KIND_SEEDS.size).toBeGreaterThan(0);
327
+ expect(MIXED_KIND_SEEDS.size).toBeLessThan(SEEDS.length / 20);
328
+ let differing = 0;
329
+ for (const seed of MIXED_KIND_SEEDS) {
330
+ const text = oracle.toJSONString(generate(seed));
331
+ if (graphDiff((0, serial_json_1.fromJSON)(JSON.parse(text)), oracle.fromJSON(JSON.parse(text))))
332
+ differing++;
333
+ }
334
+ expect(differing).toBeGreaterThan(0);
335
+ });
336
+ it("decodes the oracle's JSON text the same way, without mutating it", () => {
337
+ for (const seed of SEEDS) {
338
+ if (MIXED_KIND_SEEDS.has(seed))
339
+ continue;
340
+ const text = oracle.toJSONString(generate(seed));
341
+ const decoded = (0, serial_json_1.fromJSON)(deepFreeze(JSON.parse(text)));
342
+ const expected = oracle.fromJSON(deepFreeze(JSON.parse(text)));
343
+ expect({ seed, diff: graphDiff(decoded, expected) }).toEqual({ seed, diff: null });
344
+ }
345
+ });
346
+ it("decodes msgpack-decoded trees (the device wire) the same way", () => {
347
+ for (const seed of SEEDS) {
348
+ if (MIXED_KIND_SEEDS.has(seed))
349
+ continue;
350
+ const bytes = (0, msgpack_1.encode)(oracle.toJSON(generate(seed)));
351
+ const decoded = (0, serial_json_1.fromJSON)(deepFreeze((0, msgpack_1.decode)(bytes)));
352
+ const expected = oracle.fromJSON(deepFreeze((0, msgpack_1.decode)(bytes)));
353
+ expect({ seed, diff: graphDiff(decoded, expected) }).toEqual({ seed, diff: null });
354
+ }
355
+ });
356
+ it("produces output the oracle decodes the same way as its own", () => {
357
+ for (const seed of SEEDS) {
358
+ const value = generate(seed);
359
+ const decoded = oracle.fromJSON(JSON.parse((0, serial_json_1.toJSONString)(value)));
360
+ const expected = oracle.fromJSON(JSON.parse(oracle.toJSONString(value)));
361
+ expect({ seed, diff: graphDiff(decoded, expected) }).toEqual({ seed, diff: null });
362
+ }
363
+ });
364
+ it("never mutates the value it encodes", () => {
365
+ for (const seed of SEEDS.slice(0, 300)) {
366
+ expect(() => (0, serial_json_1.toJSONString)(deepFreeze(generate(seed)))).not.toThrow();
367
+ }
368
+ });
369
+ });
@@ -1,13 +1,49 @@
1
- /** True for plain object literals (excludes array, Date, and null). */
1
+ /**
2
+ * True for any non-null object or function that isn't an array or a Date. Despite the name, class
3
+ * instances, Maps and RegExps count.
4
+ */
2
5
  export declare function isObject(x: any): x is Record<string, any>;
3
6
  /**
4
- * Clones a value into a JSON-serializable tree: Dates, Buffers, cycles, `undefined`, `NaN`, RegExp, etc. use sentinel strings.
5
- * Shared references are preserved with `__duplicate_ref_*` and `__this_ref` markers for {@link fromJSON}.
7
+ * Encodes a value as a JSON-safe tree, using in-band sentinel strings for what JSON can't hold:
8
+ * - `undefined`, `NaN`, `Infinity` → `"__UNDEFINED"`, `"__NAN"`, `"__INFINITY"`
9
+ * - Date → `"__DATE <ISO>"`, RegExp → `"__REGEXP /src/flags"`, Error → `"__ERROR <stack>"`
10
+ * - Buffer → `"__BUFFER <base64>"`, Uint8Array → `"__UINT8ARRAY <base64>"`
11
+ * - functions → `"__FUNCTION <source>"` (one-way: {@link fromJSON} leaves the string)
12
+ * - keys starting with `$` → `__DOLLAR_…`
13
+ *
14
+ * A shared object or array (including a cycle) is copied once: the first copy is tagged with a
15
+ * `__this_ref` key (objects) or a `"__this_ref:<marker>"` first element (arrays), and every other
16
+ * occurrence becomes the `__duplicate_ref_obj_N` / `__duplicate_ref_ary_N` marker string. The
17
+ * markers only resolve when the whole tree is decoded together.
18
+ *
19
+ * Map, Set and class instances become plain objects, `-Infinity` and `-0` survive the tree but
20
+ * not `JSON.stringify`. BigInt and symbols remain in the tree; JSON text throws on BigInt and
21
+ * drops symbols (or writes null in arrays). Invalid Dates throw, and own `__proto__` keys aren't
22
+ * emitted. Strings that look like sentinels aren't escaped, so they decode as the value they spell.
23
+ *
24
+ * The output is signed (`canonicalJSONString` in keys.ts), so it must stay byte-identical across
25
+ * versions; `serial-json.compat.test.ts` checks it against a frozen copy.
26
+ * @returns A new tree; the input is never modified. Top-level primitives are returned as-is.
6
27
  */
7
28
  export declare function toJSON(obj: any): any;
8
29
  /**
9
- * Reverses {@link toJSON}: restores special values, shared references, and keys escaped with `__DOLLAR_`.
10
- * @param _externalReferences Reserved for future use (function revival, etc.)
30
+ * Reverses {@link toJSON}: restores the sentinel values, shared objects and cycles, and keys
31
+ * escaped with `__DOLLAR_`.
32
+ *
33
+ * Input may come from another device. Shared-reference tags never resolve to a prototype or to a
34
+ * target of the other kind (object vs array), `__proto__` keys are dropped, and `__HTML` /
35
+ * `__WINDOW` stay plain strings. A marker with no accepted target in the same tree decodes to an
36
+ * empty object or array and warns once per distinct marker, up to 100 warnings per process.
37
+ *
38
+ * Only arrays and objects are walked. Values that are already decoded (Date, RegExp, Error, Buffer
39
+ * and any other typed array or DataView) are returned as they are, so decoding a partly decoded
40
+ * tree preserves these instances. Marker-like strings may decode again; live cycles aren't supported.
41
+ * @param obj Parsed JSON, a msgpack-decoded tree, or a mix with decoded values. Never modified.
42
+ * @param _externalReferences Unused. Callers pass `.map` indexes into it, so it must stay ignored.
43
+ * @returns A new tree for walked arrays and objects. Passed-through values retain their identity
44
+ * and properties.
45
+ * @throws SyntaxError for a `__REGEXP` sentinel that isn't a valid regular expression; RangeError
46
+ * when the tree is nested deeper than the call stack allows.
11
47
  */
12
48
  export declare function fromJSON(obj: any, _externalReferences?: any): any;
13
49
  /**