@knitli/openapi-mcp 0.0.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 (116) hide show
  1. package/LICENSE-APACHE +202 -0
  2. package/LICENSE-MIT +21 -0
  3. package/README.md +362 -0
  4. package/dist/cli.d.ts +2 -0
  5. package/dist/cli.js +249 -0
  6. package/dist/compile.d.ts +14 -0
  7. package/dist/compile.js +132 -0
  8. package/dist/compiler.d.ts +13 -0
  9. package/dist/compiler.js +7 -0
  10. package/dist/conformance/fixtures.d.ts +24 -0
  11. package/dist/conformance/fixtures.js +119 -0
  12. package/dist/conformance/index.d.ts +2 -0
  13. package/dist/conformance/index.js +2 -0
  14. package/dist/conformance/runtime-suite.d.ts +31 -0
  15. package/dist/conformance/runtime-suite.js +343 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.js +1 -0
  18. package/dist/load.d.ts +31 -0
  19. package/dist/load.js +20 -0
  20. package/dist/operations.d.ts +27 -0
  21. package/dist/operations.js +231 -0
  22. package/dist/permissions.d.ts +31 -0
  23. package/dist/permissions.js +89 -0
  24. package/dist/release/compile-release.d.ts +33 -0
  25. package/dist/release/compile-release.js +1620 -0
  26. package/dist/release/load-v4.d.ts +29 -0
  27. package/dist/release/load-v4.js +297 -0
  28. package/dist/release/manifest-builder.d.ts +19 -0
  29. package/dist/release/manifest-builder.js +28 -0
  30. package/dist/release/publish.d.ts +45 -0
  31. package/dist/release/publish.js +346 -0
  32. package/dist/release/reference-map.d.ts +53 -0
  33. package/dist/release/reference-map.js +347 -0
  34. package/dist/release/schema-v4.d.ts +5 -0
  35. package/dist/release/schema-v4.js +91 -0
  36. package/dist/runtime/action-permit.d.ts +11 -0
  37. package/dist/runtime/action-permit.js +1 -0
  38. package/dist/runtime/classify.d.ts +16 -0
  39. package/dist/runtime/classify.js +279 -0
  40. package/dist/runtime/credential-binding.d.ts +3 -0
  41. package/dist/runtime/credential-binding.js +101 -0
  42. package/dist/runtime/digest.d.ts +8 -0
  43. package/dist/runtime/digest.js +80 -0
  44. package/dist/runtime/errors.d.ts +14 -0
  45. package/dist/runtime/errors.js +15 -0
  46. package/dist/runtime/index.d.ts +20 -0
  47. package/dist/runtime/index.js +14 -0
  48. package/dist/runtime/manifest.d.ts +33 -0
  49. package/dist/runtime/manifest.js +489 -0
  50. package/dist/runtime/prepared-call.d.ts +39 -0
  51. package/dist/runtime/prepared-call.js +566 -0
  52. package/dist/runtime/references.d.ts +7 -0
  53. package/dist/runtime/references.js +197 -0
  54. package/dist/runtime/runtime.d.ts +25 -0
  55. package/dist/runtime/runtime.js +1272 -0
  56. package/dist/runtime/schema-keywords.d.ts +2 -0
  57. package/dist/runtime/schema-keywords.js +30 -0
  58. package/dist/runtime/schema-resolver.d.ts +10 -0
  59. package/dist/runtime/schema-resolver.js +237 -0
  60. package/dist/runtime/serialize.d.ts +12 -0
  61. package/dist/runtime/serialize.js +877 -0
  62. package/dist/runtime/store.d.ts +52 -0
  63. package/dist/runtime/store.js +582 -0
  64. package/dist/runtime/strict-json.d.ts +19 -0
  65. package/dist/runtime/strict-json.js +539 -0
  66. package/dist/runtime/types.d.ts +457 -0
  67. package/dist/runtime/types.js +1 -0
  68. package/dist/runtime/verify-record.d.ts +6 -0
  69. package/dist/runtime/verify-record.js +492 -0
  70. package/dist/runtime/versions.d.ts +63 -0
  71. package/dist/runtime/versions.js +86 -0
  72. package/dist/safety.d.ts +28 -0
  73. package/dist/safety.js +60 -0
  74. package/dist/schema.d.ts +5 -0
  75. package/dist/schema.js +52 -0
  76. package/dist/schemas.d.ts +9 -0
  77. package/dist/schemas.js +16 -0
  78. package/dist/sign.d.ts +20 -0
  79. package/dist/sign.js +55 -0
  80. package/dist/sqlite/auth.d.ts +33 -0
  81. package/dist/sqlite/auth.js +973 -0
  82. package/dist/sqlite/catalog-store.d.ts +20 -0
  83. package/dist/sqlite/catalog-store.js +370 -0
  84. package/dist/sqlite/destination-guard.d.ts +24 -0
  85. package/dist/sqlite/destination-guard.js +140 -0
  86. package/dist/sqlite/generation-store.d.ts +12 -0
  87. package/dist/sqlite/generation-store.js +721 -0
  88. package/dist/sqlite/guarded-fetch.d.ts +24 -0
  89. package/dist/sqlite/guarded-fetch.js +626 -0
  90. package/dist/sqlite/index.d.ts +5 -0
  91. package/dist/sqlite/index.js +5 -0
  92. package/dist/sqlite/oauth-resource.d.ts +2 -0
  93. package/dist/sqlite/oauth-resource.js +15 -0
  94. package/dist/sqlite/release-completion.d.ts +5 -0
  95. package/dist/sqlite/release-completion.js +80 -0
  96. package/dist/stdio/action-broker.d.ts +15 -0
  97. package/dist/stdio/action-broker.js +136 -0
  98. package/dist/stdio/authorizer.d.ts +30 -0
  99. package/dist/stdio/authorizer.js +484 -0
  100. package/dist/stdio/config.d.ts +77 -0
  101. package/dist/stdio/config.js +144 -0
  102. package/dist/stdio/exact-policy.d.ts +41 -0
  103. package/dist/stdio/exact-policy.js +361 -0
  104. package/dist/stdio/index.d.ts +6 -0
  105. package/dist/stdio/index.js +148 -0
  106. package/dist/stdio/render.d.ts +8 -0
  107. package/dist/stdio/render.js +253 -0
  108. package/dist/stdio/request-state.d.ts +18 -0
  109. package/dist/stdio/request-state.js +79 -0
  110. package/dist/stdio/search.d.ts +10 -0
  111. package/dist/stdio/search.js +76 -0
  112. package/dist/stdio/server.d.ts +29 -0
  113. package/dist/stdio/server.js +309 -0
  114. package/dist/types.d.ts +32 -0
  115. package/dist/types.js +1 -0
  116. package/package.json +131 -0
@@ -0,0 +1,539 @@
1
+ import { OpenApiMcpError } from "./errors.js";
2
+ import { DEFAULT_RUNTIME_LIMITS } from "./versions.js";
3
+ export const DEFAULT_STRICT_JSON_LIMITS = {
4
+ maxBytes: DEFAULT_RUNTIME_LIMITS.maxManifestBytes,
5
+ maxDepth: DEFAULT_RUNTIME_LIMITS.maxJsonDepth,
6
+ maxKeys: DEFAULT_RUNTIME_LIMITS.maxManifestRecords,
7
+ };
8
+ const forbiddenKeys = new Set(["__proto__", "prototype", "constructor"]);
9
+ function invalidJson(message) {
10
+ return new OpenApiMcpError("MANIFEST_INVALID", message);
11
+ }
12
+ function assertWellFormedString(value) {
13
+ for (let index = 0; index < value.length; index += 1) {
14
+ const code = value.charCodeAt(index);
15
+ if (code >= 0xd800 && code <= 0xdbff) {
16
+ const next = value.charCodeAt(index + 1);
17
+ if (!(next >= 0xdc00 && next <= 0xdfff)) {
18
+ throw invalidJson("JSON string contains an unpaired surrogate");
19
+ }
20
+ index += 1;
21
+ }
22
+ else if (code >= 0xdc00 && code <= 0xdfff) {
23
+ throw invalidJson("JSON string contains an unpaired surrogate");
24
+ }
25
+ }
26
+ }
27
+ class StrictJsonParser {
28
+ text;
29
+ limits;
30
+ #position = 0;
31
+ #keyCount = 0;
32
+ constructor(text, limits) {
33
+ this.text = text;
34
+ this.limits = limits;
35
+ }
36
+ parse() {
37
+ this.skipWhitespace();
38
+ const value = this.parseValue(0);
39
+ this.skipWhitespace();
40
+ if (this.#position !== this.text.length) {
41
+ throw invalidJson("JSON input has trailing characters");
42
+ }
43
+ return value;
44
+ }
45
+ parseValue(depth) {
46
+ const token = this.text[this.#position];
47
+ switch (token) {
48
+ case "{":
49
+ return this.parseObject(depth);
50
+ case "[":
51
+ return this.parseArray(depth);
52
+ case '"':
53
+ return this.parseString();
54
+ case "t":
55
+ return this.parseLiteral("true", true);
56
+ case "f":
57
+ return this.parseLiteral("false", false);
58
+ case "n":
59
+ return this.parseLiteral("null", null);
60
+ default:
61
+ if (token === "-" ||
62
+ (token !== undefined && token >= "0" && token <= "9")) {
63
+ return this.parseNumber();
64
+ }
65
+ throw invalidJson("JSON input has an invalid value");
66
+ }
67
+ }
68
+ parseObject(depth) {
69
+ this.assertContainerDepth(depth);
70
+ this.#position += 1;
71
+ this.skipWhitespace();
72
+ const object = Object.create(null);
73
+ if (this.consume("}"))
74
+ return object;
75
+ while (true) {
76
+ if (this.text[this.#position] !== '"') {
77
+ throw invalidJson("JSON object key must be a string");
78
+ }
79
+ const key = this.parseString();
80
+ if (forbiddenKeys.has(key)) {
81
+ throw invalidJson("JSON object contains a forbidden prototype key");
82
+ }
83
+ if (Object.hasOwn(object, key)) {
84
+ throw invalidJson("JSON object contains a duplicate key");
85
+ }
86
+ this.#keyCount += 1;
87
+ if (this.#keyCount > this.limits.maxKeys) {
88
+ throw invalidJson("JSON input exceeds its key limit");
89
+ }
90
+ this.skipWhitespace();
91
+ this.expect(":");
92
+ this.skipWhitespace();
93
+ object[key] = this.parseValue(depth + 1);
94
+ this.skipWhitespace();
95
+ if (this.consume("}"))
96
+ return object;
97
+ this.expect(",");
98
+ this.skipWhitespace();
99
+ }
100
+ }
101
+ parseArray(depth) {
102
+ this.assertContainerDepth(depth);
103
+ this.#position += 1;
104
+ this.skipWhitespace();
105
+ const values = [];
106
+ if (this.consume("]"))
107
+ return values;
108
+ while (true) {
109
+ values.push(this.parseValue(depth + 1));
110
+ this.skipWhitespace();
111
+ if (this.consume("]"))
112
+ return values;
113
+ this.expect(",");
114
+ this.skipWhitespace();
115
+ }
116
+ }
117
+ parseString() {
118
+ this.expect('"');
119
+ let value = "";
120
+ while (this.#position < this.text.length) {
121
+ const character = this.text[this.#position];
122
+ if (character === '"') {
123
+ this.#position += 1;
124
+ assertWellFormedString(value);
125
+ return value;
126
+ }
127
+ if (character === "\\") {
128
+ this.#position += 1;
129
+ const escapeSequence = this.text[this.#position];
130
+ this.#position += 1;
131
+ switch (escapeSequence) {
132
+ case '"':
133
+ case "\\":
134
+ case "/":
135
+ value += escapeSequence;
136
+ break;
137
+ case "b":
138
+ value += "\b";
139
+ break;
140
+ case "f":
141
+ value += "\f";
142
+ break;
143
+ case "n":
144
+ value += "\n";
145
+ break;
146
+ case "r":
147
+ value += "\r";
148
+ break;
149
+ case "t":
150
+ value += "\t";
151
+ break;
152
+ case "u": {
153
+ const hex = this.text.slice(this.#position, this.#position + 4);
154
+ if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
155
+ throw invalidJson("JSON string has an invalid Unicode escape");
156
+ }
157
+ value += String.fromCharCode(Number.parseInt(hex, 16));
158
+ this.#position += 4;
159
+ break;
160
+ }
161
+ default:
162
+ throw invalidJson("JSON string has an invalid escape");
163
+ }
164
+ continue;
165
+ }
166
+ if (character === undefined || character.charCodeAt(0) < 0x20) {
167
+ throw invalidJson("JSON string has an unescaped control character");
168
+ }
169
+ value += character;
170
+ this.#position += 1;
171
+ }
172
+ throw invalidJson("JSON string is unterminated");
173
+ }
174
+ parseNumber() {
175
+ const match = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?/.exec(this.text.slice(this.#position));
176
+ if (match === null)
177
+ throw invalidJson("JSON input has an invalid number");
178
+ this.#position += match[0].length;
179
+ const number = Number(match[0]);
180
+ if (!Number.isFinite(number)) {
181
+ throw invalidJson("JSON input has a non-finite number");
182
+ }
183
+ return number;
184
+ }
185
+ parseLiteral(literal, value) {
186
+ if (!this.text.startsWith(literal, this.#position)) {
187
+ throw invalidJson("JSON input has an invalid literal");
188
+ }
189
+ this.#position += literal.length;
190
+ return value;
191
+ }
192
+ assertContainerDepth(depth) {
193
+ if (depth >= this.limits.maxDepth) {
194
+ throw invalidJson("JSON input exceeds its nesting depth limit");
195
+ }
196
+ }
197
+ skipWhitespace() {
198
+ while (/[\x20\t\n\r]/.test(this.text[this.#position] ?? "")) {
199
+ this.#position += 1;
200
+ }
201
+ }
202
+ consume(character) {
203
+ if (this.text[this.#position] !== character)
204
+ return false;
205
+ this.#position += 1;
206
+ return true;
207
+ }
208
+ expect(character) {
209
+ if (!this.consume(character)) {
210
+ throw invalidJson(`JSON input expected ${character}`);
211
+ }
212
+ }
213
+ }
214
+ /** Parse JSON without giving duplicate or prototype keys JavaScript object semantics. */
215
+ export function parseJsonStrict(text, limits = DEFAULT_STRICT_JSON_LIMITS) {
216
+ if (!Number.isSafeInteger(limits.maxBytes) || limits.maxBytes < 0) {
217
+ throw invalidJson("JSON byte limit is invalid");
218
+ }
219
+ if (!Number.isSafeInteger(limits.maxDepth) || limits.maxDepth < 1) {
220
+ throw invalidJson("JSON depth limit is invalid");
221
+ }
222
+ if (!Number.isSafeInteger(limits.maxKeys) || limits.maxKeys < 0) {
223
+ throw invalidJson("JSON key limit is invalid");
224
+ }
225
+ if (new TextEncoder().encode(text).byteLength > limits.maxBytes) {
226
+ throw invalidJson("JSON input exceeds its byte limit");
227
+ }
228
+ return new StrictJsonParser(text, limits).parse();
229
+ }
230
+ /** Deterministic JSON serialization for logical signed and hashed values. */
231
+ export function canonicalJson(value) {
232
+ return serializeCanonical(value, new Set());
233
+ }
234
+ class BoundedCanonicalWriter {
235
+ limits;
236
+ #chunks = [];
237
+ #ancestors = new Set();
238
+ #segment = "";
239
+ #bytes = 0;
240
+ #nodes = 0;
241
+ constructor(limits) {
242
+ this.limits = limits;
243
+ }
244
+ serialize(value) {
245
+ this.#value(value, 0);
246
+ this.#flush();
247
+ return this.#chunks.join("");
248
+ }
249
+ #write(fragment, bytes = fragment.length) {
250
+ if (this.#bytes + bytes > this.limits.maxBytes) {
251
+ throw invalidJson("Canonical JSON exceeds its byte limit");
252
+ }
253
+ this.#bytes += bytes;
254
+ this.#segment += fragment;
255
+ if (this.#segment.length >= 4096)
256
+ this.#flush();
257
+ }
258
+ #flush() {
259
+ if (this.#segment.length === 0)
260
+ return;
261
+ this.#chunks.push(this.#segment);
262
+ this.#segment = "";
263
+ }
264
+ #string(value) {
265
+ assertWellFormedString(value);
266
+ this.#write('"');
267
+ for (let index = 0; index < value.length; index += 1) {
268
+ const code = value.charCodeAt(index);
269
+ if (code === 0x22)
270
+ this.#write('\\"');
271
+ else if (code === 0x5c)
272
+ this.#write("\\\\");
273
+ else if (code === 0x08)
274
+ this.#write("\\b");
275
+ else if (code === 0x0c)
276
+ this.#write("\\f");
277
+ else if (code === 0x0a)
278
+ this.#write("\\n");
279
+ else if (code === 0x0d)
280
+ this.#write("\\r");
281
+ else if (code === 0x09)
282
+ this.#write("\\t");
283
+ else if (code < 0x20)
284
+ this.#write(`\\u${code.toString(16).padStart(4, "0")}`);
285
+ else if (code <= 0x7f)
286
+ this.#write(value[index]);
287
+ else if (code <= 0x7ff)
288
+ this.#write(value[index], 2);
289
+ else if (code >= 0xd800 && code <= 0xdbff) {
290
+ this.#write(value.slice(index, index + 2), 4);
291
+ index += 1;
292
+ }
293
+ else
294
+ this.#write(value[index], 3);
295
+ }
296
+ this.#write('"');
297
+ }
298
+ #container(value, depth) {
299
+ if (depth >= this.limits.maxDepth) {
300
+ throw invalidJson("Canonical JSON exceeds its nesting depth limit");
301
+ }
302
+ if (this.#ancestors.has(value)) {
303
+ throw invalidJson("Canonical JSON does not allow cycles");
304
+ }
305
+ this.#ancestors.add(value);
306
+ }
307
+ #value(value, depth) {
308
+ this.#nodes += 1;
309
+ if (this.#nodes > this.limits.maxNodes) {
310
+ throw invalidJson("Canonical JSON exceeds its traversal limit");
311
+ }
312
+ if (value === null) {
313
+ this.#write("null");
314
+ return;
315
+ }
316
+ if (typeof value === "boolean") {
317
+ this.#write(value ? "true" : "false");
318
+ return;
319
+ }
320
+ if (typeof value === "number") {
321
+ if (!Number.isFinite(value))
322
+ throw invalidJson("Canonical JSON does not allow non-finite numbers");
323
+ this.#write(JSON.stringify(value));
324
+ return;
325
+ }
326
+ if (typeof value === "string") {
327
+ this.#string(value);
328
+ return;
329
+ }
330
+ if (Array.isArray(value)) {
331
+ if (Object.getPrototypeOf(value) !== Array.prototype) {
332
+ throw invalidJson("Canonical JSON array has an invalid prototype");
333
+ }
334
+ if (this.#nodes + value.length > this.limits.maxNodes) {
335
+ throw invalidJson("Canonical JSON exceeds its traversal limit");
336
+ }
337
+ for (const key of Reflect.ownKeys(value)) {
338
+ if (typeof key !== "string")
339
+ throw invalidJson("Canonical JSON array has a symbol property");
340
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
341
+ if (descriptor === undefined || !("value" in descriptor)) {
342
+ throw invalidJson("Canonical JSON array has an accessor property");
343
+ }
344
+ if (key === "length") {
345
+ if (descriptor.enumerable || descriptor.value !== value.length) {
346
+ throw invalidJson("Canonical JSON array has an invalid length property");
347
+ }
348
+ }
349
+ else if (!descriptor.enumerable ||
350
+ !isCanonicalArrayIndex(key, value.length)) {
351
+ throw invalidJson("Canonical JSON array has a noncanonical own property");
352
+ }
353
+ }
354
+ this.#container(value, depth);
355
+ try {
356
+ this.#write("[");
357
+ for (let index = 0; index < value.length; index += 1) {
358
+ if (index > 0)
359
+ this.#write(",");
360
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
361
+ if (descriptor === undefined || !("value" in descriptor)) {
362
+ throw invalidJson("Canonical JSON does not allow sparse arrays");
363
+ }
364
+ this.#value(descriptor.value, depth + 1);
365
+ }
366
+ this.#write("]");
367
+ }
368
+ finally {
369
+ this.#ancestors.delete(value);
370
+ }
371
+ return;
372
+ }
373
+ const prototype = Object.getPrototypeOf(value);
374
+ if (prototype !== null && prototype !== Object.prototype) {
375
+ throw invalidJson("Canonical JSON object has an invalid prototype");
376
+ }
377
+ const keys = [];
378
+ for (const key of Reflect.ownKeys(value)) {
379
+ if (typeof key !== "string")
380
+ throw invalidJson("Canonical JSON object has a symbol property");
381
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
382
+ if (descriptor === undefined ||
383
+ !descriptor.enumerable ||
384
+ !("value" in descriptor)) {
385
+ throw invalidJson("Canonical JSON object has an invalid property");
386
+ }
387
+ if (forbiddenKeys.has(key))
388
+ throw invalidJson("Canonical JSON object contains a forbidden prototype key");
389
+ keys.push(key);
390
+ if (this.#nodes + keys.length > this.limits.maxNodes) {
391
+ throw invalidJson("Canonical JSON exceeds its traversal limit");
392
+ }
393
+ }
394
+ keys.sort();
395
+ this.#container(value, depth);
396
+ try {
397
+ this.#write("{");
398
+ for (let index = 0; index < keys.length; index += 1) {
399
+ if (index > 0)
400
+ this.#write(",");
401
+ const key = keys[index];
402
+ this.#string(key);
403
+ this.#write(":");
404
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
405
+ if (descriptor === undefined || !("value" in descriptor)) {
406
+ throw invalidJson("Canonical JSON object has an accessor property");
407
+ }
408
+ this.#value(descriptor.value, depth + 1);
409
+ }
410
+ this.#write("}");
411
+ }
412
+ finally {
413
+ this.#ancestors.delete(value);
414
+ }
415
+ }
416
+ }
417
+ /** Canonicalize hostile values while bounding output and traversal before allocation. */
418
+ export function canonicalJsonBounded(value, limits) {
419
+ if (!Number.isSafeInteger(limits.maxBytes) || limits.maxBytes < 0) {
420
+ throw invalidJson("Canonical JSON byte limit is invalid");
421
+ }
422
+ if (!Number.isSafeInteger(limits.maxDepth) || limits.maxDepth < 1) {
423
+ throw invalidJson("Canonical JSON depth limit is invalid");
424
+ }
425
+ if (!Number.isSafeInteger(limits.maxNodes) || limits.maxNodes < 1) {
426
+ throw invalidJson("Canonical JSON traversal limit is invalid");
427
+ }
428
+ return new BoundedCanonicalWriter(limits).serialize(value);
429
+ }
430
+ function serializeCanonical(value, ancestors) {
431
+ if (value === null)
432
+ return "null";
433
+ if (typeof value === "boolean")
434
+ return value ? "true" : "false";
435
+ if (typeof value === "number") {
436
+ if (!Number.isFinite(value)) {
437
+ throw invalidJson("Canonical JSON does not allow non-finite numbers");
438
+ }
439
+ return JSON.stringify(value);
440
+ }
441
+ if (typeof value === "string") {
442
+ assertWellFormedString(value);
443
+ return JSON.stringify(value);
444
+ }
445
+ if (Array.isArray(value)) {
446
+ if (Object.getPrototypeOf(value) !== Array.prototype) {
447
+ throw invalidJson("Canonical JSON array has an invalid prototype");
448
+ }
449
+ for (const key of Reflect.ownKeys(value)) {
450
+ if (typeof key !== "string") {
451
+ throw invalidJson("Canonical JSON array has a symbol property");
452
+ }
453
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
454
+ if (descriptor === undefined || !("value" in descriptor)) {
455
+ throw invalidJson("Canonical JSON array has an accessor property");
456
+ }
457
+ if (key === "length") {
458
+ if (descriptor.enumerable || descriptor.value !== value.length) {
459
+ throw invalidJson("Canonical JSON array has an invalid length property");
460
+ }
461
+ continue;
462
+ }
463
+ if (!descriptor.enumerable || !isCanonicalArrayIndex(key, value.length)) {
464
+ throw invalidJson("Canonical JSON array has a noncanonical own property");
465
+ }
466
+ }
467
+ if (ancestors.has(value))
468
+ throw invalidJson("Canonical JSON does not allow cycles");
469
+ ancestors.add(value);
470
+ try {
471
+ const values = [];
472
+ for (let index = 0; index < value.length; index += 1) {
473
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
474
+ if (descriptor === undefined) {
475
+ throw invalidJson("Canonical JSON does not allow sparse arrays");
476
+ }
477
+ if (!("value" in descriptor)) {
478
+ throw invalidJson("Canonical JSON array has an accessor property");
479
+ }
480
+ values.push(serializeCanonical(descriptor.value, ancestors));
481
+ }
482
+ return `[${values.join(",")}]`;
483
+ }
484
+ finally {
485
+ ancestors.delete(value);
486
+ }
487
+ }
488
+ if (typeof value !== "object" || value === null) {
489
+ throw invalidJson("Canonical JSON value is invalid");
490
+ }
491
+ const prototype = Object.getPrototypeOf(value);
492
+ if (prototype !== null && prototype !== Object.prototype) {
493
+ throw invalidJson("Canonical JSON object has an invalid prototype");
494
+ }
495
+ if (ancestors.has(value))
496
+ throw invalidJson("Canonical JSON does not allow cycles");
497
+ ancestors.add(value);
498
+ try {
499
+ const keys = [];
500
+ for (const key of Reflect.ownKeys(value)) {
501
+ if (typeof key !== "string") {
502
+ throw invalidJson("Canonical JSON object has a symbol property");
503
+ }
504
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
505
+ if (descriptor === undefined || !descriptor.enumerable) {
506
+ throw invalidJson("Canonical JSON object has a non-enumerable property");
507
+ }
508
+ if (!("value" in descriptor)) {
509
+ throw invalidJson("Canonical JSON object has an accessor property");
510
+ }
511
+ keys.push(key);
512
+ }
513
+ keys.sort();
514
+ const properties = keys.map((key) => {
515
+ if (forbiddenKeys.has(key)) {
516
+ throw invalidJson("Canonical JSON object contains a forbidden prototype key");
517
+ }
518
+ assertWellFormedString(key);
519
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
520
+ if (descriptor === undefined || !("value" in descriptor)) {
521
+ throw invalidJson("Canonical JSON object has an accessor property");
522
+ }
523
+ return `${JSON.stringify(key)}:${serializeCanonical(descriptor.value, ancestors)}`;
524
+ });
525
+ return `{${properties.join(",")}}`;
526
+ }
527
+ finally {
528
+ ancestors.delete(value);
529
+ }
530
+ }
531
+ function isCanonicalArrayIndex(key, length) {
532
+ if (!/^(?:0|[1-9]\d*)$/.test(key))
533
+ return false;
534
+ const index = Number(key);
535
+ return (Number.isSafeInteger(index) &&
536
+ index >= 0 &&
537
+ index < length &&
538
+ String(index) === key);
539
+ }