@carbonenginejs/runtime-resource 0.18.1 → 0.19.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.
@@ -32,7 +32,7 @@ class CjsPickleProtocol0Reader {
32
32
  this.#bytes = normalizeBytes(input);
33
33
  this.#limits = normalizeLimits(options.limits ?? options);
34
34
  if (this.#bytes.byteLength > this.#limits.maxInputBytes) {
35
- throw pickleError("CJS_PICKLE_LIMIT_EXCEEDED", `Pickle input exceeds maxInputBytes (${this.#limits.maxInputBytes}).`, 0);
35
+ throw pickleError("CJS_PICKLE_FORMAT_LIMIT_EXCEEDED", `Pickle input exceeds maxInputBytes (${this.#limits.maxInputBytes}).`, 0);
36
36
  }
37
37
  }
38
38
 
@@ -78,6 +78,13 @@ function decode(bytes, limits) {
78
78
  memo: new Map(),
79
79
  offset: 0,
80
80
  operations: 0,
81
+ // Every global marker created, and separately those no REDUCE has consumed.
82
+ // A marker is a decoding artifact, never data: it may sit on the stack and
83
+ // in the memo on its way to a REDUCE, and it may reach nothing else.
84
+ globalMarkers: new WeakSet(),
85
+ pendingGlobals: new Set(),
86
+ // Properties built by REDUCE across the WHOLE decode, not per container.
87
+ rebuiltItems: 0,
81
88
  stack: []
82
89
  };
83
90
  while (state.offset < bytes.byteLength) {
@@ -85,7 +92,7 @@ function decode(bytes, limits) {
85
92
  const opcode = bytes[state.offset++];
86
93
  state.operations += 1;
87
94
  if (state.operations > limits.maxOperations) {
88
- throw pickleError("CJS_PICKLE_LIMIT_EXCEEDED", `Pickle operation count exceeds maxOperations (${limits.maxOperations}).`, opcodeOffset);
95
+ throw pickleError("CJS_PICKLE_FORMAT_LIMIT_EXCEEDED", `Pickle operation count exceeds maxOperations (${limits.maxOperations}).`, opcodeOffset);
89
96
  }
90
97
  switch (opcode) {
91
98
  case 0x28:
@@ -148,29 +155,43 @@ function decode(bytes, limits) {
148
155
  // SETITEM
149
156
  setItem(state, opcodeOffset);
150
157
  break;
158
+ case 0x63:
159
+ // GLOBAL
160
+ push(state, readGlobal(state, opcodeOffset), opcodeOffset);
161
+ break;
162
+ case 0x52:
163
+ // REDUCE
164
+ reduce(state, opcodeOffset);
165
+ break;
151
166
  default:
152
- throw pickleError("CJS_PICKLE_OPCODE_UNSUPPORTED", `Data-only pickle protocol 0 rejects opcode ${displayOpcode(opcode)}.`, opcodeOffset);
167
+ throw pickleError("CJS_PICKLE_FORMAT_OPCODE_UNSUPPORTED", `Data-only pickle protocol 0 rejects opcode ${displayOpcode(opcode)}.`, opcodeOffset);
153
168
  }
154
169
  }
155
- throw pickleError("CJS_PICKLE_STOP_MISSING", "Pickle input ended without a STOP opcode.", state.offset);
170
+ throw pickleError("CJS_PICKLE_FORMAT_STOP_MISSING", "Pickle input ended without a STOP opcode.", state.offset);
156
171
  }
157
172
  function stop(state, offset) {
173
+ // A GLOBAL that no REDUCE consumed would otherwise reach the caller as an
174
+ // empty object, indistinguishable from an empty dictionary.
175
+ if (state.pendingGlobals.size) {
176
+ throw pickleError("CJS_PICKLE_FORMAT_GLOBAL_UNSUPPORTED", "Pickle names a global that no REDUCE consumes.", offset);
177
+ }
178
+ rejectGlobalMarker(state, state.stack[0], offset);
158
179
  if (state.marks.length || state.stack.length !== 1 || state.stack[0] === MARK) {
159
- throw pickleError("CJS_PICKLE_STACK_INVALID", "Pickle STOP requires one completed value and no open marks.", offset);
180
+ throw pickleError("CJS_PICKLE_FORMAT_STACK_INVALID", "Pickle STOP requires one completed value and no open marks.", offset);
160
181
  }
161
182
  if (state.offset !== state.bytes.byteLength) {
162
- throw pickleError("CJS_PICKLE_TRAILING_DATA", "Pickle input contains bytes after STOP.", state.offset);
183
+ throw pickleError("CJS_PICKLE_FORMAT_TRAILING_DATA", "Pickle input contains bytes after STOP.", state.offset);
163
184
  }
164
185
  return state.stack[0];
165
186
  }
166
187
  function readFloat(state, offset) {
167
188
  const value = readAsciiLine(state, state.limits.maxStringBytes, offset);
168
189
  if (!/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/u.test(value)) {
169
- throw pickleError("CJS_PICKLE_NUMBER_INVALID", `Pickle FLOAT value is invalid: ${JSON.stringify(value)}.`, offset);
190
+ throw pickleError("CJS_PICKLE_FORMAT_NUMBER_INVALID", `Pickle FLOAT value is invalid: ${JSON.stringify(value)}.`, offset);
170
191
  }
171
192
  const result = Number(value);
172
193
  if (!Number.isFinite(result)) {
173
- throw pickleError("CJS_PICKLE_NUMBER_INVALID", "Pickle FLOAT must be finite for JSON-compatible output.", offset);
194
+ throw pickleError("CJS_PICKLE_FORMAT_NUMBER_INVALID", "Pickle FLOAT must be finite for JSON-compatible output.", offset);
174
195
  }
175
196
  return result;
176
197
  }
@@ -180,7 +201,7 @@ function readInteger(state, offset, isLong) {
180
201
  if (!isLong && value === "01") return true;
181
202
  if (isLong && value.endsWith("L")) value = value.slice(0, -1);
182
203
  if (!/^[+-]?\d+$/u.test(value)) {
183
- throw pickleError("CJS_PICKLE_NUMBER_INVALID", `Pickle integer value is invalid: ${JSON.stringify(value)}.`, offset);
204
+ throw pickleError("CJS_PICKLE_FORMAT_NUMBER_INVALID", `Pickle integer value is invalid: ${JSON.stringify(value)}.`, offset);
184
205
  }
185
206
  const result = BigInt(value);
186
207
  if (result >= BigInt(Number.MIN_SAFE_INTEGER) && result <= BigInt(Number.MAX_SAFE_INTEGER)) {
@@ -191,11 +212,11 @@ function readInteger(state, offset, isLong) {
191
212
  function readString(state, offset) {
192
213
  const bytes = readLine(state, state.limits.maxStringBytes, offset);
193
214
  if (bytes.byteLength < 2) {
194
- throw pickleError("CJS_PICKLE_STRING_INVALID", "Pickle STRING must be a quoted Python string literal.", offset);
215
+ throw pickleError("CJS_PICKLE_FORMAT_STRING_INVALID", "Pickle STRING must be a quoted Python string literal.", offset);
195
216
  }
196
217
  const quote = bytes[0];
197
218
  if (quote !== 0x27 && quote !== 0x22 || bytes[bytes.byteLength - 1] !== quote) {
198
- throw pickleError("CJS_PICKLE_STRING_INVALID", "Pickle STRING must use matching single or double quotes.", offset);
219
+ throw pickleError("CJS_PICKLE_FORMAT_STRING_INVALID", "Pickle STRING must use matching single or double quotes.", offset);
199
220
  }
200
221
  const result = [];
201
222
  for (let index = 1; index < bytes.byteLength - 1; index += 1) {
@@ -206,7 +227,7 @@ function readString(state, offset) {
206
227
  }
207
228
  index += 1;
208
229
  if (index >= bytes.byteLength - 1) {
209
- throw pickleError("CJS_PICKLE_STRING_INVALID", "Pickle STRING ends with an incomplete escape.", offset);
230
+ throw pickleError("CJS_PICKLE_FORMAT_STRING_INVALID", "Pickle STRING ends with an incomplete escape.", offset);
210
231
  }
211
232
  const escaped = bytes[index];
212
233
  const simple = decodeSimpleEscape(escaped);
@@ -223,7 +244,7 @@ function readString(state, offset) {
223
244
  }
224
245
  result.push(String.fromCharCode(Number.parseInt(digits, 8)));
225
246
  } else {
226
- throw pickleError("CJS_PICKLE_STRING_INVALID", `Pickle STRING contains unsupported escape \\${String.fromCharCode(escaped)}.`, offset);
247
+ throw pickleError("CJS_PICKLE_FORMAT_STRING_INVALID", `Pickle STRING contains unsupported escape \\${String.fromCharCode(escaped)}.`, offset);
227
248
  }
228
249
  }
229
250
  return result.join("");
@@ -244,7 +265,7 @@ function readUnicode(state, offset) {
244
265
  } else if (escaped === 0x55) {
245
266
  const codePoint = readHex(bytes, index + 2, 8, offset);
246
267
  if (codePoint > 0x10ffff) {
247
- throw pickleError("CJS_PICKLE_STRING_INVALID", `Pickle UNICODE code point is out of range: ${codePoint}.`, offset);
268
+ throw pickleError("CJS_PICKLE_FORMAT_STRING_INVALID", `Pickle UNICODE code point is out of range: ${codePoint}.`, offset);
248
269
  }
249
270
  result.push(String.fromCodePoint(codePoint));
250
271
  index += 9;
@@ -264,7 +285,7 @@ function readSequence(state, offset, isList) {
264
285
  function readDictionary(state, offset) {
265
286
  const values = popMarkedValues(state, offset);
266
287
  if (values.length % 2 !== 0) {
267
- throw pickleError("CJS_PICKLE_CONTAINER_INVALID", "Pickle DICT requires key/value pairs.", offset);
288
+ throw pickleError("CJS_PICKLE_FORMAT_CONTAINER_INVALID", "Pickle DICT requires key/value pairs.", offset);
268
289
  }
269
290
  requireContainerLimit(state, values.length / 2, offset);
270
291
  const result = {};
@@ -278,10 +299,10 @@ function readDictionary(state, offset) {
278
299
  }
279
300
  function append(state, offset) {
280
301
  requireStack(state, 2, offset);
281
- const value = state.stack.pop();
302
+ const value = rejectGlobalMarker(state, state.stack.pop(), offset);
282
303
  const target = state.stack[state.stack.length - 1];
283
304
  if (!Array.isArray(target) || !state.lists.has(target)) {
284
- throw pickleError("CJS_PICKLE_CONTAINER_INVALID", "Pickle APPEND target must be a list.", offset);
305
+ throw pickleError("CJS_PICKLE_FORMAT_CONTAINER_INVALID", "Pickle APPEND target must be a list.", offset);
285
306
  }
286
307
  requireContainerLimit(state, target.length + 1, offset);
287
308
  target.push(value);
@@ -289,11 +310,11 @@ function append(state, offset) {
289
310
  }
290
311
  function setItem(state, offset) {
291
312
  requireStack(state, 3, offset);
292
- const value = state.stack.pop();
313
+ const value = rejectGlobalMarker(state, state.stack.pop(), offset);
293
314
  const key = state.stack.pop();
294
315
  const target = state.stack[state.stack.length - 1];
295
316
  if (!isDictionary(target)) {
296
- throw pickleError("CJS_PICKLE_CONTAINER_INVALID", "Pickle SETITEM target must be a dictionary.", offset);
317
+ throw pickleError("CJS_PICKLE_FORMAT_CONTAINER_INVALID", "Pickle SETITEM target must be a dictionary.", offset);
297
318
  }
298
319
  const count = defineDictionaryValue(state, target, key, value, state.containers.get(target) ?? Object.keys(target).length, offset);
299
320
  requireContainerLimit(state, count, offset);
@@ -304,7 +325,7 @@ function defineDictionaryValue(state, target, key, value, count, offset) {
304
325
  const keyTypes = state.dictionaryKeys.get(target) ?? new Map();
305
326
  const previousType = keyTypes.get(normalized.value);
306
327
  if (previousType && previousType !== normalized.type) {
307
- throw pickleError("CJS_PICKLE_CONTAINER_INVALID", `Pickle dictionary keys collide after JSON normalization: ${JSON.stringify(normalized.value)}.`, offset);
328
+ throw pickleError("CJS_PICKLE_FORMAT_CONTAINER_INVALID", `Pickle dictionary keys collide after JSON normalization: ${JSON.stringify(normalized.value)}.`, offset);
308
329
  }
309
330
  const exists = Object.hasOwn(target, normalized.value);
310
331
  Object.defineProperty(target, normalized.value, {
@@ -328,63 +349,203 @@ function normalizeDictionaryKey(key, offset) {
328
349
  value: String(key)
329
350
  };
330
351
  }
331
- throw pickleError("CJS_PICKLE_CONTAINER_INVALID", "Data-only pickle dictionaries require string or safe-integer keys.", offset);
352
+ throw pickleError("CJS_PICKLE_FORMAT_CONTAINER_INVALID", "Data-only pickle dictionaries require string or safe-integer keys.", offset);
332
353
  }
333
354
  function putMemo(state, offset) {
334
355
  requireStack(state, 1, offset);
335
356
  if (state.stack[state.stack.length - 1] === MARK) {
336
- throw pickleError("CJS_PICKLE_MARK_INVALID", "Pickle MARK cannot be stored in the memo.", offset);
357
+ throw pickleError("CJS_PICKLE_FORMAT_MARK_INVALID", "Pickle MARK cannot be stored in the memo.", offset);
337
358
  }
338
359
  const id = readMemoID(state, offset);
339
360
  if (!state.memo.has(id) && state.memo.size >= state.limits.maxMemoEntries) {
340
- throw pickleError("CJS_PICKLE_LIMIT_EXCEEDED", `Pickle memo exceeds maxMemoEntries (${state.limits.maxMemoEntries}).`, offset);
361
+ throw pickleError("CJS_PICKLE_FORMAT_LIMIT_EXCEEDED", `Pickle memo exceeds maxMemoEntries (${state.limits.maxMemoEntries}).`, offset);
341
362
  }
342
363
  state.memo.set(id, state.stack[state.stack.length - 1]);
343
364
  }
344
365
  function getMemo(state, offset) {
345
366
  const id = readMemoID(state, offset);
346
367
  if (!state.memo.has(id)) {
347
- throw pickleError("CJS_PICKLE_MEMO_INVALID", `Pickle memo entry ${id} does not exist.`, offset);
368
+ throw pickleError("CJS_PICKLE_FORMAT_MEMO_INVALID", `Pickle memo entry ${id} does not exist.`, offset);
348
369
  }
349
370
  push(state, state.memo.get(id), offset);
350
371
  }
351
372
  function readMemoID(state, offset) {
352
373
  const value = readAsciiLine(state, 64, offset);
353
374
  if (!/^\d+$/u.test(value)) {
354
- throw pickleError("CJS_PICKLE_MEMO_INVALID", `Pickle memo ID is invalid: ${JSON.stringify(value)}.`, offset);
375
+ throw pickleError("CJS_PICKLE_FORMAT_MEMO_INVALID", `Pickle memo ID is invalid: ${JSON.stringify(value)}.`, offset);
355
376
  }
356
377
  const result = Number(value);
357
378
  if (!Number.isSafeInteger(result) || result > state.limits.maxMemoID) {
358
- throw pickleError("CJS_PICKLE_LIMIT_EXCEEDED", `Pickle memo ID exceeds maxMemoID (${state.limits.maxMemoID}).`, offset);
379
+ throw pickleError("CJS_PICKLE_FORMAT_LIMIT_EXCEEDED", `Pickle memo ID exceeds maxMemoID (${state.limits.maxMemoID}).`, offset);
359
380
  }
360
381
  return result;
361
382
  }
383
+
384
+ /**
385
+ * The one closed set of globals this reader will name, and how each rebuilds.
386
+ *
387
+ * `GLOBAL` is the opcode that makes a pickle dangerous: it names a module and an
388
+ * attribute for the unpickler to import, and `REDUCE` then calls it. The general
389
+ * form stays refused, and nothing here imports, resolves or invokes anything.
390
+ * What this table does instead is recognize a fixed name and build the plain
391
+ * data it stands for.
392
+ *
393
+ * `collections.OrderedDict` earns its place because it is not a behaviour, it is
394
+ * a dictionary that remembers insertion order — and a JavaScript object already
395
+ * does. It is also, measured across every self-describing container CCP ships,
396
+ * **the only global any of them uses**: 25 files, one name, once each. They use
397
+ * it because a schema's attribute order is its field order, which is exactly the
398
+ * property an ordinary dict would lose.
399
+ *
400
+ * **Adding to this table is not a small change.** A name belongs here only if
401
+ * reconstructing it is pure data with no behaviour of its own, and the entry has
402
+ * to build that data directly rather than defer to anything callable.
403
+ */
404
+ const REBUILDABLE_GLOBALS = new Map([["collections.OrderedDict", RebuildOrderedDict]]);
405
+ const GLOBAL_NAME = Symbol("pickle-global");
406
+
407
+ /** Reads a GLOBAL, and refuses every name outside the closed set above. */
408
+ function readGlobal(state, offset) {
409
+ const module = decodeAscii(readLine(state, state.limits.maxStringBytes, offset));
410
+ const attribute = decodeAscii(readLine(state, state.limits.maxStringBytes, offset));
411
+ const name = `${module}.${attribute}`;
412
+ if (!REBUILDABLE_GLOBALS.has(name)) {
413
+ throw pickleError("CJS_PICKLE_FORMAT_GLOBAL_UNSUPPORTED", `Data-only pickle protocol 0 rejects the global ${JSON.stringify(name)}. ` + "Only a closed set of pure-data containers can be rebuilt, and this is not one.", offset);
414
+ }
415
+ const marker = {
416
+ [GLOBAL_NAME]: name
417
+ };
418
+ state.globalMarkers.add(marker);
419
+ state.pendingGlobals.add(marker);
420
+ return marker;
421
+ }
422
+
423
+ /** Rebuilds one allowed global from its arguments. Calls nothing. */
424
+ function reduce(state, offset) {
425
+ const args = state.stack.pop();
426
+ const callable = state.stack.pop();
427
+ const name = callable && typeof callable === "object" ? callable[GLOBAL_NAME] : undefined;
428
+ if (!name || !REBUILDABLE_GLOBALS.has(name)) {
429
+ throw pickleError("CJS_PICKLE_FORMAT_REDUCE_INVALID", "Pickle REDUCE applies only to a global this reader can rebuild.", offset);
430
+ }
431
+ if (!Array.isArray(args)) {
432
+ throw pickleError("CJS_PICKLE_FORMAT_REDUCE_INVALID", "Pickle REDUCE requires an argument tuple.", offset);
433
+ }
434
+ state.pendingGlobals.delete(callable);
435
+ push(state, REBUILDABLE_GLOBALS.get(name)(args, state, offset), offset);
436
+ }
437
+
438
+ /**
439
+ * Rebuilds `OrderedDict(pairs)` as a plain object.
440
+ *
441
+ * JavaScript preserves the insertion order of string keys, but NOT of keys that
442
+ * look like array indices — those sort ahead of everything else, in ascending
443
+ * numeric order. Refusing every numeric key was too blunt: real containers use
444
+ * them, and where they already ascend the object's order is the source's order
445
+ * and nothing is lost.
446
+ *
447
+ * So the order is checked rather than the keys. The result is compared against
448
+ * the order it was built in, and only a dictionary JavaScript would actually
449
+ * reorder is refused.
450
+ */
451
+ function RebuildOrderedDict(args, state, offset) {
452
+ const pairs = args.length ? args[0] : [];
453
+ if (!Array.isArray(pairs)) {
454
+ throw pickleError("CJS_PICKLE_FORMAT_REDUCE_INVALID", "An ordered dictionary is rebuilt from a list of key/value pairs.", offset);
455
+ }
456
+ requireContainerLimit(state, pairs.length, offset);
457
+ const order = [];
458
+
459
+ // A per-container check is not enough here. REDUCE is the only path that
460
+ // builds N properties for a constant number of opcodes, so a memoized pair
461
+ // list rebuilt in a loop multiplies `maxOperations` by `maxContainerItems`
462
+ // instead of being bounded by either. A decode-wide budget is what bounds it.
463
+ state.rebuiltItems += pairs.length;
464
+ if (state.rebuiltItems > state.limits.maxContainerItems) {
465
+ throw pickleError("CJS_PICKLE_FORMAT_LIMIT_EXCEEDED", `Rebuilt items exceed maxContainerItems (${state.limits.maxContainerItems}) across the decode.`, offset);
466
+ }
467
+ const result = {};
468
+ for (const pair of pairs) {
469
+ if (!Array.isArray(pair) || pair.length !== 2) {
470
+ throw pickleError("CJS_PICKLE_FORMAT_REDUCE_INVALID", "An ordered dictionary entry must be a key/value pair.", offset);
471
+ }
472
+ const key = pair[0];
473
+ if (typeof key !== "string") {
474
+ throw pickleError("CJS_PICKLE_FORMAT_REDUCE_INVALID", "An ordered dictionary key must be a string.", offset);
475
+ }
476
+ order.push(key);
477
+
478
+ // Defined rather than assigned, as the dictionary path already does. A
479
+ // plain assignment to `__proto__` sets the object's prototype instead of
480
+ // storing a property: the field silently disappears from the decoded record
481
+ // and, if its value is an object, becomes a phantom the JSON never shows.
482
+ Object.defineProperty(result, key, {
483
+ configurable: true,
484
+ enumerable: true,
485
+ value: pair[1],
486
+ writable: true
487
+ });
488
+ }
489
+
490
+ // Order is the one thing this type exists to carry, so it is checked rather
491
+ // than assumed. A repeated key keeps its first position, which is what both
492
+ // Python and JavaScript do.
493
+ const expected = [...new Set(order)];
494
+ const kept = Object.keys(result);
495
+ if (kept.length !== expected.length || kept.some((key, index) => key !== expected[index])) {
496
+ throw pickleError("CJS_PICKLE_FORMAT_REDUCE_INVALID", "An ordered dictionary's key order would not survive as a JavaScript object.", offset);
497
+ }
498
+ return result;
499
+ }
500
+
501
+ /** Decodes a GLOBAL's module or attribute line, which is always ASCII. */
502
+ function decodeAscii(bytes) {
503
+ let result = "";
504
+ for (const byte of bytes) result += String.fromCharCode(byte);
505
+ return result.trim();
506
+ }
507
+
508
+ /**
509
+ * Refuses a global marker anywhere a decoded value is stored or returned.
510
+ *
511
+ * Consuming a marker with REDUCE is the only thing it is for. Appended to a
512
+ * list, set as a dictionary value or left as the result, it would reach the
513
+ * caller as `{}` - indistinguishable from an empty dictionary, and buryable
514
+ * anywhere in the graph through the memo.
515
+ */
516
+ function rejectGlobalMarker(state, value, offset) {
517
+ if (value && typeof value === "object" && state.globalMarkers.has(value)) {
518
+ throw pickleError("CJS_PICKLE_FORMAT_GLOBAL_UNSUPPORTED", "A pickle global is only usable as the target of a REDUCE.", offset);
519
+ }
520
+ return value;
521
+ }
362
522
  function popMarkedValues(state, offset) {
363
523
  if (!state.marks.length) {
364
- throw pickleError("CJS_PICKLE_MARK_INVALID", "Pickle container has no matching MARK.", offset);
524
+ throw pickleError("CJS_PICKLE_FORMAT_MARK_INVALID", "Pickle container has no matching MARK.", offset);
365
525
  }
366
526
  const mark = state.marks.pop();
367
527
  if (state.stack[mark] !== MARK) {
368
- throw pickleError("CJS_PICKLE_MARK_INVALID", "Pickle MARK stack is inconsistent.", offset);
528
+ throw pickleError("CJS_PICKLE_FORMAT_MARK_INVALID", "Pickle MARK stack is inconsistent.", offset);
369
529
  }
370
530
  const values = state.stack.slice(mark + 1);
371
531
  state.stack.length = mark;
532
+ for (const value of values) rejectGlobalMarker(state, value, offset);
372
533
  return values;
373
534
  }
374
535
  function push(state, value, offset) {
375
536
  if (state.stack.length >= state.limits.maxStackDepth) {
376
- throw pickleError("CJS_PICKLE_LIMIT_EXCEEDED", `Pickle stack exceeds maxStackDepth (${state.limits.maxStackDepth}).`, offset);
537
+ throw pickleError("CJS_PICKLE_FORMAT_LIMIT_EXCEEDED", `Pickle stack exceeds maxStackDepth (${state.limits.maxStackDepth}).`, offset);
377
538
  }
378
539
  state.stack.push(value);
379
540
  }
380
541
  function requireStack(state, count, offset) {
381
542
  if (state.stack.length < count) {
382
- throw pickleError("CJS_PICKLE_STACK_INVALID", `Pickle opcode requires ${count} stack values.`, offset);
543
+ throw pickleError("CJS_PICKLE_FORMAT_STACK_INVALID", `Pickle opcode requires ${count} stack values.`, offset);
383
544
  }
384
545
  }
385
546
  function requireContainerLimit(state, count, offset) {
386
547
  if (count > state.limits.maxContainerItems) {
387
- throw pickleError("CJS_PICKLE_LIMIT_EXCEEDED", `Pickle container exceeds maxContainerItems (${state.limits.maxContainerItems}).`, offset);
548
+ throw pickleError("CJS_PICKLE_FORMAT_LIMIT_EXCEEDED", `Pickle container exceeds maxContainerItems (${state.limits.maxContainerItems}).`, offset);
388
549
  }
389
550
  }
390
551
  function readAsciiLine(state, limit, offset) {
@@ -392,7 +553,7 @@ function readAsciiLine(state, limit, offset) {
392
553
  let result = "";
393
554
  for (const byte of bytes) {
394
555
  if (byte > 0x7f) {
395
- throw pickleError("CJS_PICKLE_STRING_INVALID", "Pickle control line must contain ASCII bytes.", offset);
556
+ throw pickleError("CJS_PICKLE_FORMAT_STRING_INVALID", "Pickle control line must contain ASCII bytes.", offset);
396
557
  }
397
558
  result += String.fromCharCode(byte);
398
559
  }
@@ -403,11 +564,11 @@ function readLine(state, limit, offset) {
403
564
  while (state.offset < state.bytes.byteLength && state.bytes[state.offset] !== 0x0a) {
404
565
  state.offset += 1;
405
566
  if (state.offset - start > limit) {
406
- throw pickleError("CJS_PICKLE_LIMIT_EXCEEDED", `Pickle line exceeds its ${limit}-byte limit.`, offset);
567
+ throw pickleError("CJS_PICKLE_FORMAT_LIMIT_EXCEEDED", `Pickle line exceeds its ${limit}-byte limit.`, offset);
407
568
  }
408
569
  }
409
570
  if (state.offset >= state.bytes.byteLength) {
410
- throw pickleError("CJS_PICKLE_EOF", "Pickle line is missing its newline terminator.", offset);
571
+ throw pickleError("CJS_PICKLE_FORMAT_EOF", "Pickle line is missing its newline terminator.", offset);
411
572
  }
412
573
  const result = state.bytes.subarray(start, state.offset);
413
574
  state.offset += 1;
@@ -415,13 +576,13 @@ function readLine(state, limit, offset) {
415
576
  }
416
577
  function readHex(bytes, start, length, offset) {
417
578
  if (start + length > bytes.byteLength) {
418
- throw pickleError("CJS_PICKLE_STRING_INVALID", "Pickle escape sequence is truncated.", offset);
579
+ throw pickleError("CJS_PICKLE_FORMAT_STRING_INVALID", "Pickle escape sequence is truncated.", offset);
419
580
  }
420
581
  let result = 0;
421
582
  for (let index = 0; index < length; index += 1) {
422
583
  const value = hexValue(bytes[start + index]);
423
584
  if (value === -1) {
424
- throw pickleError("CJS_PICKLE_STRING_INVALID", "Pickle escape sequence contains a non-hexadecimal digit.", offset);
585
+ throw pickleError("CJS_PICKLE_FORMAT_STRING_INVALID", "Pickle escape sequence contains a non-hexadecimal digit.", offset);
425
586
  }
426
587
  result = result * 16 + value;
427
588
  }
@@ -463,12 +624,12 @@ function assertJSONCompatible(value) {
463
624
  }
464
625
  if (typeof current === "number") {
465
626
  if (!Number.isFinite(current)) {
466
- throw pickleError("CJS_PICKLE_JSON_INVALID", "Pickle output contains a non-finite number.", null);
627
+ throw pickleError("CJS_PICKLE_FORMAT_JSON_INVALID", "Pickle output contains a non-finite number.", null);
467
628
  }
468
629
  continue;
469
630
  }
470
631
  if (!current || typeof current !== "object") {
471
- throw pickleError("CJS_PICKLE_JSON_INVALID", `Pickle output contains unsupported ${typeof current} data.`, null);
632
+ throw pickleError("CJS_PICKLE_FORMAT_JSON_INVALID", `Pickle output contains unsupported ${typeof current} data.`, null);
472
633
  }
473
634
  if (item.exit) {
474
635
  active.delete(current);
@@ -477,7 +638,7 @@ function assertJSONCompatible(value) {
477
638
  }
478
639
  if (verified.has(current)) continue;
479
640
  if (active.has(current)) {
480
- throw pickleError("CJS_PICKLE_JSON_INVALID", "Pickle output contains a cyclic reference.", null);
641
+ throw pickleError("CJS_PICKLE_FORMAT_JSON_INVALID", "Pickle output contains a cyclic reference.", null);
481
642
  }
482
643
  active.add(current);
483
644
  pending.push({
@@ -500,7 +661,7 @@ function assertJSONCompatible(value) {
500
661
  });
501
662
  }
502
663
  } else {
503
- throw pickleError("CJS_PICKLE_JSON_INVALID", "Pickle output contains a non-plain object.", null);
664
+ throw pickleError("CJS_PICKLE_FORMAT_JSON_INVALID", "Pickle output contains a non-plain object.", null);
504
665
  }
505
666
  }
506
667
  }
@@ -509,22 +670,22 @@ function normalizeBytes(input) {
509
670
  if (ArrayBuffer.isView(input)) {
510
671
  return new Uint8Array(input.buffer, input.byteOffset, input.byteLength);
511
672
  }
512
- throw pickleError("CJS_PICKLE_INPUT_INVALID", "Pickle input must be an ArrayBuffer or an ArrayBuffer view.", 0);
673
+ throw pickleError("CJS_PICKLE_FORMAT_INPUT_INVALID", "Pickle input must be an ArrayBuffer or an ArrayBuffer view.", 0);
513
674
  }
514
675
  function normalizeLimits(options) {
515
676
  if (!options || typeof options !== "object" || Array.isArray(options)) {
516
- throw pickleError("CJS_PICKLE_LIMIT_INVALID", "Pickle limits must be an object.", 0);
677
+ throw pickleError("CJS_PICKLE_FORMAT_LIMIT_INVALID", "Pickle limits must be an object.", 0);
517
678
  }
518
679
  for (const name of Object.keys(options)) {
519
680
  if (!LIMIT_NAMES.includes(name)) {
520
- throw pickleError("CJS_PICKLE_LIMIT_INVALID", `Pickle limits contain unknown value ${JSON.stringify(name)}.`, 0);
681
+ throw pickleError("CJS_PICKLE_FORMAT_LIMIT_INVALID", `Pickle limits contain unknown value ${JSON.stringify(name)}.`, 0);
521
682
  }
522
683
  }
523
684
  const result = {};
524
685
  for (const name of LIMIT_NAMES) {
525
686
  const value = options[name] ?? PICKLE_PROTOCOL_0_LIMITS[name];
526
687
  if (!Number.isSafeInteger(value) || value <= 0) {
527
- throw pickleError("CJS_PICKLE_LIMIT_INVALID", `Pickle ${name} must be a positive safe integer.`, 0);
688
+ throw pickleError("CJS_PICKLE_FORMAT_LIMIT_INVALID", `Pickle ${name} must be a positive safe integer.`, 0);
528
689
  }
529
690
  result[name] = value;
530
691
  }