circuitjson-toolkit 1.2.0 → 1.3.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.
- package/README.md +25 -6
- package/docs/api.md +28 -5
- package/docs/model-format.md +12 -8
- package/docs/release-notes-v1.2.1.md +26 -0
- package/docs/release-notes-v1.3.0.md +45 -0
- package/docs/testing.md +14 -0
- package/package.json +4 -2
- package/src/core/context/BinaryDataSnapshot.mjs +183 -43
- package/src/core/context/CircuitJsonDocumentContext.mjs +31 -4
- package/src/core/context/CircuitJsonReadOnlyDocument.mjs +22 -9
- package/src/core/context/CircuitJsonValidationProof.mjs +4 -2
- package/src/core/context/ProtectedExtensionBinaryBoundary.mjs +2 -2
- package/src/core/context/StructuredDataSnapshot.mjs +11 -6
- package/src/core/worker/WorkerRequestData.mjs +401 -55
- package/src/core/worker/WorkerResultShape.mjs +222 -0
|
@@ -2,6 +2,7 @@ import { CircuitJsonValidationProof } from '../context/CircuitJsonValidationProo
|
|
|
2
2
|
import { CircuitJsonReadOnlyDocument } from '../context/CircuitJsonReadOnlyDocument.mjs'
|
|
3
3
|
import { ProtectedExtensionBinaryBoundary } from '../context/ProtectedExtensionBinaryBoundary.mjs'
|
|
4
4
|
import { RuntimeProxyBoundary } from '../contracts/RuntimeProxyBoundary.mjs'
|
|
5
|
+
import { WorkerResultShape } from './WorkerResultShape.mjs'
|
|
5
6
|
|
|
6
7
|
const ARRAY_BUFFER_BYTE_LENGTH_GETTER = Object.getOwnPropertyDescriptor(
|
|
7
8
|
ArrayBuffer.prototype,
|
|
@@ -56,6 +57,11 @@ const MAX_REQUEST_DEPTH = 256
|
|
|
56
57
|
const MAX_REQUEST_VALUES = 100_000
|
|
57
58
|
const MAX_RESULT_BYTES = 250_000_000
|
|
58
59
|
const MAX_RESULT_VALUES = 2_000_000
|
|
60
|
+
const MAX_DOCUMENT_RESULT_VALUES = 5_000_000
|
|
61
|
+
// Project documents retain the single-result ceiling independently while this
|
|
62
|
+
// aggregate ceiling bounds total worker traversal regardless of document count.
|
|
63
|
+
const MAX_PROJECT_RESULT_VALUES = 8_000_000
|
|
64
|
+
const MAX_PROJECT_RESULT_BYTES = 256 * 1024 * 1024
|
|
59
65
|
const TYPED_ARRAYS = new Map(
|
|
60
66
|
[
|
|
61
67
|
Int8Array,
|
|
@@ -166,14 +172,28 @@ export class WorkerRequestData {
|
|
|
166
172
|
const state = {
|
|
167
173
|
bytes: 0,
|
|
168
174
|
copyBinary: limits.copyBinary,
|
|
175
|
+
documentAccounted: null,
|
|
176
|
+
documentBytes: null,
|
|
177
|
+
documentValues: null,
|
|
169
178
|
maxBytes: limits.bytes,
|
|
179
|
+
maxDocumentBytes: limits.bytes,
|
|
180
|
+
maxDocumentValues: MAX_DOCUMENT_RESULT_VALUES,
|
|
181
|
+
maxUnscopedBytes: limits.bytes,
|
|
182
|
+
maxUnscopedValues: limits.values,
|
|
170
183
|
maxValues: limits.values,
|
|
171
184
|
output: limits.output,
|
|
172
185
|
prepared: new WeakMap(),
|
|
186
|
+
projectDocuments: null,
|
|
187
|
+
projectResult: false,
|
|
188
|
+
reaccountValues: 0,
|
|
173
189
|
strictDescriptors: limits.strictDescriptors,
|
|
174
190
|
transfer: { items: [], seen: new Set() },
|
|
175
191
|
transferInput: limits.transferInput,
|
|
176
192
|
trustProof: limits.trustProof,
|
|
193
|
+
unscopedAccounted: null,
|
|
194
|
+
unscopedBytes: 0,
|
|
195
|
+
unscopedIncomplete: new WeakSet(),
|
|
196
|
+
unscopedValues: 0,
|
|
177
197
|
values: 0,
|
|
178
198
|
visiting: new WeakSet()
|
|
179
199
|
}
|
|
@@ -223,8 +243,26 @@ export class WorkerRequestData {
|
|
|
223
243
|
if (state.visiting.has(value)) {
|
|
224
244
|
throw new TypeError('Worker request data must not be cyclic.')
|
|
225
245
|
}
|
|
226
|
-
if (state.prepared.has(value))
|
|
246
|
+
if (state.prepared.has(value)) {
|
|
247
|
+
const accounted = WorkerRequestData.#accountedScope(state)
|
|
248
|
+
if (accounted && !accounted.has(value)) {
|
|
249
|
+
WorkerRequestData.#accountPreparedValue(value, state, depth)
|
|
250
|
+
} else if (
|
|
251
|
+
accounted === state.unscopedAccounted &&
|
|
252
|
+
state.unscopedIncomplete.delete(value)
|
|
253
|
+
) {
|
|
254
|
+
// The documents array itself was already charged unscoped.
|
|
255
|
+
WorkerRequestData.#accountPreparedContainer(
|
|
256
|
+
value,
|
|
257
|
+
state,
|
|
258
|
+
depth,
|
|
259
|
+
false
|
|
260
|
+
)
|
|
261
|
+
}
|
|
262
|
+
return state.prepared.get(value)
|
|
263
|
+
}
|
|
227
264
|
WorkerRequestData.#reserveValues(state, 1)
|
|
265
|
+
WorkerRequestData.#accountedScope(state)?.add(value)
|
|
228
266
|
state.visiting.add(value)
|
|
229
267
|
try {
|
|
230
268
|
const bufferLength = WorkerRequestData.#bufferLength(value)
|
|
@@ -305,18 +343,32 @@ export class WorkerRequestData {
|
|
|
305
343
|
'Worker requests may contain only plain data objects.'
|
|
306
344
|
)
|
|
307
345
|
}
|
|
308
|
-
const keys =
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
346
|
+
const keys = WorkerResultShape.keys(
|
|
347
|
+
descriptors,
|
|
348
|
+
state.output,
|
|
349
|
+
state.strictDescriptors
|
|
350
|
+
)
|
|
351
|
+
const projectResult =
|
|
352
|
+
depth === 0 &&
|
|
353
|
+
state.output &&
|
|
354
|
+
WorkerResultShape.project(descriptors, keys)
|
|
355
|
+
if (projectResult) {
|
|
356
|
+
state.maxBytes = MAX_PROJECT_RESULT_BYTES
|
|
357
|
+
state.maxValues = MAX_PROJECT_RESULT_VALUES
|
|
358
|
+
state.projectResult = true
|
|
359
|
+
state.unscopedAccounted = new WeakSet([value])
|
|
360
|
+
state.unscopedBytes = state.bytes
|
|
361
|
+
state.unscopedValues = state.values
|
|
362
|
+
}
|
|
363
|
+
const canonicalDocumentResult =
|
|
364
|
+
state.output && WorkerResultShape.document(descriptors, keys)
|
|
365
|
+
if (depth === 0 && canonicalDocumentResult) {
|
|
366
|
+
state.maxValues = MAX_DOCUMENT_RESULT_VALUES
|
|
367
|
+
}
|
|
368
|
+
const provenDocumentResult =
|
|
316
369
|
state.output &&
|
|
317
370
|
state.trustProof &&
|
|
318
|
-
|
|
319
|
-
'ecad-toolkit.document.v1' &&
|
|
371
|
+
canonicalDocumentResult &&
|
|
320
372
|
CircuitJsonValidationProof.has(value)
|
|
321
373
|
WorkerRequestData.#reserveValues(state, keys.length)
|
|
322
374
|
const prepared = prototype === null ? Object.create(null) : {}
|
|
@@ -330,13 +382,19 @@ export class WorkerRequestData {
|
|
|
330
382
|
configurable: true,
|
|
331
383
|
enumerable: true,
|
|
332
384
|
value:
|
|
333
|
-
|
|
334
|
-
? WorkerRequestData.#
|
|
335
|
-
: WorkerRequestData.#descriptorValue(
|
|
385
|
+
projectResult && key === 'documents'
|
|
386
|
+
? WorkerRequestData.#projectDocuments(
|
|
336
387
|
descriptors[key],
|
|
337
388
|
state,
|
|
338
389
|
depth
|
|
339
|
-
)
|
|
390
|
+
)
|
|
391
|
+
: provenDocumentResult && key === 'model'
|
|
392
|
+
? WorkerRequestData.#documentModel(descriptors[key])
|
|
393
|
+
: WorkerRequestData.#descriptorValue(
|
|
394
|
+
descriptors[key],
|
|
395
|
+
state,
|
|
396
|
+
depth
|
|
397
|
+
),
|
|
340
398
|
writable: true
|
|
341
399
|
})
|
|
342
400
|
}
|
|
@@ -353,40 +411,113 @@ export class WorkerRequestData {
|
|
|
353
411
|
* @returns {any[]} Prepared array.
|
|
354
412
|
*/
|
|
355
413
|
static #prepareArray(value, prototype, descriptors, state, depth) {
|
|
356
|
-
const
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
state.output
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
414
|
+
const length = WorkerResultShape.arrayLength(
|
|
415
|
+
prototype,
|
|
416
|
+
descriptors,
|
|
417
|
+
state.output,
|
|
418
|
+
state.strictDescriptors
|
|
419
|
+
)
|
|
420
|
+
WorkerRequestData.#reserveValues(state, length)
|
|
421
|
+
const prepared = new Array(length)
|
|
422
|
+
state.prepared.set(value, prepared)
|
|
423
|
+
const projectDocuments = state.projectDocuments === value
|
|
424
|
+
for (let index = 0; index < length; index += 1) {
|
|
425
|
+
prepared[index] = projectDocuments
|
|
426
|
+
? WorkerRequestData.#projectDocument(
|
|
427
|
+
descriptors[String(index)],
|
|
428
|
+
state,
|
|
429
|
+
depth
|
|
365
430
|
)
|
|
366
|
-
:
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
431
|
+
: WorkerRequestData.#descriptorValue(
|
|
432
|
+
descriptors[String(index)],
|
|
433
|
+
state,
|
|
434
|
+
depth
|
|
435
|
+
)
|
|
436
|
+
}
|
|
437
|
+
return prepared
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/** @param {PropertyDescriptor | undefined} descriptor Descriptor. @param {Record<string, any>} state State. @param {number} depth Depth. @returns {unknown} Documents. */
|
|
441
|
+
static #projectDocuments(descriptor, state, depth) {
|
|
442
|
+
const value = WorkerRequestData.#descriptorDataValue(descriptor, state)
|
|
443
|
+
const reused = state.prepared.has(value)
|
|
444
|
+
state.projectDocuments = value
|
|
445
|
+
try {
|
|
446
|
+
const prepared = WorkerRequestData.#prepareValue(
|
|
447
|
+
value,
|
|
448
|
+
state,
|
|
449
|
+
depth + 1
|
|
450
|
+
)
|
|
451
|
+
if (reused) {
|
|
452
|
+
WorkerRequestData.#accountProjectDocuments(
|
|
453
|
+
value,
|
|
454
|
+
state,
|
|
455
|
+
depth + 1
|
|
456
|
+
)
|
|
457
|
+
} else {
|
|
458
|
+
// Descendants were charged only to their document scopes.
|
|
459
|
+
state.unscopedIncomplete.add(value)
|
|
460
|
+
}
|
|
461
|
+
return prepared
|
|
462
|
+
} finally {
|
|
463
|
+
state.projectDocuments = null
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/** @param {object} value Documents. @param {Record<string, any>} state State. @param {number} depth Depth. @returns {void} */
|
|
468
|
+
static #accountProjectDocuments(value, state, depth) {
|
|
469
|
+
let prototype
|
|
470
|
+
let descriptors
|
|
471
|
+
try {
|
|
472
|
+
prototype = Object.getPrototypeOf(value)
|
|
473
|
+
descriptors = Object.getOwnPropertyDescriptors(value)
|
|
474
|
+
} catch {
|
|
375
475
|
throw new TypeError(
|
|
376
|
-
'Worker request
|
|
476
|
+
'Worker request data could not be inspected safely.'
|
|
377
477
|
)
|
|
378
478
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
479
|
+
const length = WorkerResultShape.arrayLength(
|
|
480
|
+
prototype,
|
|
481
|
+
descriptors,
|
|
482
|
+
state.output,
|
|
483
|
+
state.strictDescriptors
|
|
484
|
+
)
|
|
382
485
|
for (let index = 0; index < length; index += 1) {
|
|
383
|
-
|
|
486
|
+
WorkerRequestData.#projectDocument(
|
|
384
487
|
descriptors[String(index)],
|
|
385
488
|
state,
|
|
386
489
|
depth
|
|
387
490
|
)
|
|
388
491
|
}
|
|
389
|
-
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Prepares one project document under the normal single-result ceiling.
|
|
496
|
+
* @param {PropertyDescriptor | undefined} descriptor Document descriptor.
|
|
497
|
+
* @param {Record<string, any>} state Traversal state.
|
|
498
|
+
* @param {number} depth Parent depth.
|
|
499
|
+
* @returns {unknown} Prepared document.
|
|
500
|
+
*/
|
|
501
|
+
static #projectDocument(descriptor, state, depth) {
|
|
502
|
+
const previousMaxDocumentValues = state.maxDocumentValues
|
|
503
|
+
const document = WorkerRequestData.#dataValue(descriptor)
|
|
504
|
+
state.maxDocumentValues = WorkerResultShape.documentCandidate(
|
|
505
|
+
document,
|
|
506
|
+
state.strictDescriptors
|
|
507
|
+
)
|
|
508
|
+
? MAX_DOCUMENT_RESULT_VALUES
|
|
509
|
+
: state.maxUnscopedValues
|
|
510
|
+
state.documentAccounted = new WeakSet()
|
|
511
|
+
state.documentBytes = 0
|
|
512
|
+
state.documentValues = 0
|
|
513
|
+
try {
|
|
514
|
+
return WorkerRequestData.#descriptorValue(descriptor, state, depth)
|
|
515
|
+
} finally {
|
|
516
|
+
state.documentAccounted = null
|
|
517
|
+
state.documentBytes = null
|
|
518
|
+
state.documentValues = null
|
|
519
|
+
state.maxDocumentValues = previousMaxDocumentValues
|
|
520
|
+
}
|
|
390
521
|
}
|
|
391
522
|
|
|
392
523
|
/**
|
|
@@ -397,24 +528,30 @@ export class WorkerRequestData {
|
|
|
397
528
|
* @returns {unknown} Prepared value.
|
|
398
529
|
*/
|
|
399
530
|
static #descriptorValue(descriptor, state, depth) {
|
|
531
|
+
return WorkerRequestData.#prepareValue(
|
|
532
|
+
WorkerRequestData.#descriptorDataValue(descriptor, state),
|
|
533
|
+
state,
|
|
534
|
+
depth + 1
|
|
535
|
+
)
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/**
|
|
539
|
+
* Reads one enumerable descriptor through trusted internal boundaries only.
|
|
540
|
+
* @param {PropertyDescriptor | undefined} descriptor Descriptor candidate.
|
|
541
|
+
* @param {Record<string, any>} state Traversal state.
|
|
542
|
+
* @returns {unknown} Descriptor data value.
|
|
543
|
+
*/
|
|
544
|
+
static #descriptorDataValue(descriptor, state) {
|
|
400
545
|
if (state.trustProof) {
|
|
401
546
|
const protectedData =
|
|
402
547
|
CircuitJsonReadOnlyDocument.readProtectedAssetData(descriptor)
|
|
403
548
|
if (protectedData.trusted) {
|
|
404
|
-
return
|
|
405
|
-
protectedData.value,
|
|
406
|
-
state,
|
|
407
|
-
depth + 1
|
|
408
|
-
)
|
|
549
|
+
return protectedData.value
|
|
409
550
|
}
|
|
410
551
|
const protectedExtensionBinary =
|
|
411
552
|
ProtectedExtensionBinaryBoundary.read(descriptor)
|
|
412
553
|
if (protectedExtensionBinary.trusted) {
|
|
413
|
-
return
|
|
414
|
-
protectedExtensionBinary.value,
|
|
415
|
-
state,
|
|
416
|
-
depth + 1
|
|
417
|
-
)
|
|
554
|
+
return protectedExtensionBinary.value
|
|
418
555
|
}
|
|
419
556
|
}
|
|
420
557
|
if (
|
|
@@ -426,8 +563,138 @@ export class WorkerRequestData {
|
|
|
426
563
|
'Worker requests may contain only enumerable data properties.'
|
|
427
564
|
)
|
|
428
565
|
}
|
|
429
|
-
return
|
|
430
|
-
|
|
566
|
+
return descriptor.value
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Charges one previously prepared graph to the active project document.
|
|
571
|
+
* @param {unknown} value Reused prepared value.
|
|
572
|
+
* @param {Record<string, any>} state Traversal state.
|
|
573
|
+
* @param {number} depth Container depth.
|
|
574
|
+
* @returns {void}
|
|
575
|
+
*/
|
|
576
|
+
static #accountPreparedValue(value, state, depth) {
|
|
577
|
+
const type = typeof value
|
|
578
|
+
if (
|
|
579
|
+
value === null ||
|
|
580
|
+
['undefined', 'boolean', 'number', 'bigint'].includes(type)
|
|
581
|
+
) {
|
|
582
|
+
return
|
|
583
|
+
}
|
|
584
|
+
if (type === 'string') {
|
|
585
|
+
WorkerRequestData.#reserveAccountedBytes(state, value.length * 2)
|
|
586
|
+
return
|
|
587
|
+
}
|
|
588
|
+
if (type !== 'object') {
|
|
589
|
+
throw new TypeError(
|
|
590
|
+
'Worker requests may contain only clone-safe data.'
|
|
591
|
+
)
|
|
592
|
+
}
|
|
593
|
+
RuntimeProxyBoundary.assert(value, 'Worker request data')
|
|
594
|
+
if (depth > MAX_REQUEST_DEPTH) {
|
|
595
|
+
throw new TypeError('Worker request data is nested too deeply.')
|
|
596
|
+
}
|
|
597
|
+
const accounted = WorkerRequestData.#accountedScope(state)
|
|
598
|
+
if (accounted.has(value)) return
|
|
599
|
+
accounted.add(value)
|
|
600
|
+
WorkerRequestData.#reserveAccountedValues(state, 1)
|
|
601
|
+
const bufferLength = WorkerRequestData.#bufferLength(value)
|
|
602
|
+
if (bufferLength !== null) {
|
|
603
|
+
WorkerRequestData.#reserveAccountedBytes(state, bufferLength)
|
|
604
|
+
return
|
|
605
|
+
}
|
|
606
|
+
const sharedLength = WorkerRequestData.#sharedBufferLength(value)
|
|
607
|
+
if (sharedLength !== null) {
|
|
608
|
+
WorkerRequestData.#reserveAccountedBytes(state, sharedLength)
|
|
609
|
+
return
|
|
610
|
+
}
|
|
611
|
+
const view = WorkerRequestData.#view(value)
|
|
612
|
+
if (view) {
|
|
613
|
+
WorkerRequestData.#reserveAccountedBytes(state, view.byteLength)
|
|
614
|
+
return
|
|
615
|
+
}
|
|
616
|
+
WorkerRequestData.#accountPreparedContainer(value, state, depth)
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/** @param {object} value Container. @param {Record<string, any>} state State. @param {number} depth Depth. @param {boolean} [accountEntries] Whether to charge its own entries. @returns {void} */
|
|
620
|
+
static #accountPreparedContainer(
|
|
621
|
+
value,
|
|
622
|
+
state,
|
|
623
|
+
depth,
|
|
624
|
+
accountEntries = true
|
|
625
|
+
) {
|
|
626
|
+
let prototype
|
|
627
|
+
let descriptors
|
|
628
|
+
try {
|
|
629
|
+
prototype = Object.getPrototypeOf(value)
|
|
630
|
+
descriptors = Object.getOwnPropertyDescriptors(value)
|
|
631
|
+
} catch {
|
|
632
|
+
throw new TypeError(
|
|
633
|
+
'Worker request data could not be inspected safely.'
|
|
634
|
+
)
|
|
635
|
+
}
|
|
636
|
+
if (Array.isArray(value)) {
|
|
637
|
+
const length = WorkerResultShape.arrayLength(
|
|
638
|
+
prototype,
|
|
639
|
+
descriptors,
|
|
640
|
+
state.output,
|
|
641
|
+
state.strictDescriptors
|
|
642
|
+
)
|
|
643
|
+
if (accountEntries) {
|
|
644
|
+
WorkerRequestData.#reserveAccountedValues(state, length)
|
|
645
|
+
}
|
|
646
|
+
for (let index = 0; index < length; index += 1) {
|
|
647
|
+
WorkerRequestData.#accountDescriptor(
|
|
648
|
+
descriptors[String(index)],
|
|
649
|
+
state,
|
|
650
|
+
depth
|
|
651
|
+
)
|
|
652
|
+
}
|
|
653
|
+
return
|
|
654
|
+
}
|
|
655
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
656
|
+
throw new TypeError(
|
|
657
|
+
'Worker requests may contain only plain data objects.'
|
|
658
|
+
)
|
|
659
|
+
}
|
|
660
|
+
const keys = WorkerResultShape.keys(
|
|
661
|
+
descriptors,
|
|
662
|
+
state.output,
|
|
663
|
+
state.strictDescriptors
|
|
664
|
+
)
|
|
665
|
+
const provenDocumentResult =
|
|
666
|
+
state.output &&
|
|
667
|
+
state.trustProof &&
|
|
668
|
+
WorkerResultShape.document(descriptors, keys) &&
|
|
669
|
+
CircuitJsonValidationProof.has(value)
|
|
670
|
+
if (accountEntries) {
|
|
671
|
+
WorkerRequestData.#reserveAccountedValues(state, keys.length)
|
|
672
|
+
}
|
|
673
|
+
for (const key of keys) {
|
|
674
|
+
if (typeof key !== 'string') {
|
|
675
|
+
throw new TypeError('Worker request keys must be strings.')
|
|
676
|
+
}
|
|
677
|
+
WorkerRequestData.#reserveAccountedBytes(state, key.length * 2)
|
|
678
|
+
if (!(provenDocumentResult && key === 'model')) {
|
|
679
|
+
WorkerRequestData.#accountDescriptor(
|
|
680
|
+
descriptors[key],
|
|
681
|
+
state,
|
|
682
|
+
depth
|
|
683
|
+
)
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Charges one reused descriptor to the active document.
|
|
690
|
+
* @param {PropertyDescriptor | undefined} descriptor Descriptor candidate.
|
|
691
|
+
* @param {Record<string, any>} state Traversal state.
|
|
692
|
+
* @param {number} depth Parent depth.
|
|
693
|
+
* @returns {void}
|
|
694
|
+
*/
|
|
695
|
+
static #accountDescriptor(descriptor, state, depth) {
|
|
696
|
+
WorkerRequestData.#accountPreparedValue(
|
|
697
|
+
WorkerRequestData.#descriptorDataValue(descriptor, state),
|
|
431
698
|
state,
|
|
432
699
|
depth + 1
|
|
433
700
|
)
|
|
@@ -624,27 +891,106 @@ export class WorkerRequestData {
|
|
|
624
891
|
transfer.items.push(value)
|
|
625
892
|
}
|
|
626
893
|
|
|
627
|
-
/** @param {
|
|
894
|
+
/** @param {Record<string, any>} state State. @returns {WeakSet<object> | null} Active identities. */
|
|
895
|
+
static #accountedScope(state) {
|
|
896
|
+
if (state.documentAccounted) return state.documentAccounted
|
|
897
|
+
return state.projectResult ? state.unscopedAccounted : null
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/** @param {{ documentBytes: number | null, maxDocumentBytes: number, maxUnscopedBytes: number, projectResult: boolean, unscopedBytes: number }} state State. @param {number} count Bytes. */
|
|
901
|
+
static #reserveAccountedBytes(state, count) {
|
|
902
|
+
const invalid = !Number.isSafeInteger(count) || count < 0
|
|
903
|
+
if (state.documentBytes !== null) {
|
|
904
|
+
if (
|
|
905
|
+
invalid ||
|
|
906
|
+
state.documentBytes + count > state.maxDocumentBytes
|
|
907
|
+
) {
|
|
908
|
+
throw new TypeError(
|
|
909
|
+
'Worker request data exceeds its byte limit.'
|
|
910
|
+
)
|
|
911
|
+
}
|
|
912
|
+
state.documentBytes += count
|
|
913
|
+
return
|
|
914
|
+
}
|
|
915
|
+
if (
|
|
916
|
+
invalid ||
|
|
917
|
+
!state.projectResult ||
|
|
918
|
+
state.unscopedBytes + count > state.maxUnscopedBytes
|
|
919
|
+
) {
|
|
920
|
+
throw new TypeError('Worker request data exceeds its byte limit.')
|
|
921
|
+
}
|
|
922
|
+
state.unscopedBytes += count
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
/** @param {{ documentValues: number | null, maxDocumentValues: number, maxUnscopedValues: number, maxValues: number, projectResult: boolean, reaccountValues: number, unscopedValues: number }} state State. @param {number} count Values. */
|
|
926
|
+
static #reserveAccountedValues(state, count) {
|
|
927
|
+
const invalid =
|
|
928
|
+
!Number.isSafeInteger(count) ||
|
|
929
|
+
count < 0 ||
|
|
930
|
+
state.reaccountValues + count > state.maxValues
|
|
931
|
+
if (state.documentValues !== null) {
|
|
932
|
+
if (
|
|
933
|
+
invalid ||
|
|
934
|
+
state.documentValues + count > state.maxDocumentValues
|
|
935
|
+
) {
|
|
936
|
+
throw new TypeError('Worker request data is too large.')
|
|
937
|
+
}
|
|
938
|
+
state.documentValues += count
|
|
939
|
+
state.reaccountValues += count
|
|
940
|
+
return
|
|
941
|
+
}
|
|
942
|
+
if (
|
|
943
|
+
invalid ||
|
|
944
|
+
!state.projectResult ||
|
|
945
|
+
state.unscopedValues + count > state.maxUnscopedValues
|
|
946
|
+
) {
|
|
947
|
+
throw new TypeError('Worker request data is too large.')
|
|
948
|
+
}
|
|
949
|
+
state.unscopedValues += count
|
|
950
|
+
state.reaccountValues += count
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/** @param {{ bytes: number, documentBytes: number | null, maxBytes: number, maxDocumentBytes: number, maxUnscopedBytes: number, projectResult: boolean, unscopedBytes: number }} state State. @param {number} count Bytes. */
|
|
628
954
|
static #reserveBytes(state, count) {
|
|
955
|
+
const documentBytes = state.documentBytes
|
|
629
956
|
if (
|
|
630
957
|
!Number.isSafeInteger(count) ||
|
|
631
958
|
count < 0 ||
|
|
632
|
-
state.bytes + count > state.maxBytes
|
|
959
|
+
state.bytes + count > state.maxBytes ||
|
|
960
|
+
(documentBytes !== null
|
|
961
|
+
? documentBytes + count > state.maxDocumentBytes
|
|
962
|
+
: state.projectResult &&
|
|
963
|
+
state.unscopedBytes + count > state.maxUnscopedBytes)
|
|
633
964
|
) {
|
|
634
965
|
throw new TypeError('Worker request data exceeds its byte limit.')
|
|
635
966
|
}
|
|
636
967
|
state.bytes += count
|
|
968
|
+
if (documentBytes !== null) {
|
|
969
|
+
state.documentBytes += count
|
|
970
|
+
} else if (state.projectResult) {
|
|
971
|
+
state.unscopedBytes += count
|
|
972
|
+
}
|
|
637
973
|
}
|
|
638
974
|
|
|
639
|
-
/** @param {{ values: number }} state State. @param {number} count Values. */
|
|
975
|
+
/** @param {{ documentValues: number | null, maxDocumentValues: number, maxUnscopedValues: number, maxValues: number, projectResult: boolean, unscopedValues: number, values: number }} state State. @param {number} count Values. */
|
|
640
976
|
static #reserveValues(state, count) {
|
|
977
|
+
const documentValues = state.documentValues
|
|
641
978
|
if (
|
|
642
979
|
!Number.isSafeInteger(count) ||
|
|
643
980
|
count < 0 ||
|
|
644
|
-
state.values + count > state.maxValues
|
|
981
|
+
state.values + count > state.maxValues ||
|
|
982
|
+
(documentValues !== null
|
|
983
|
+
? documentValues + count > state.maxDocumentValues
|
|
984
|
+
: state.projectResult &&
|
|
985
|
+
state.unscopedValues + count > state.maxUnscopedValues)
|
|
645
986
|
) {
|
|
646
987
|
throw new TypeError('Worker request data is too large.')
|
|
647
988
|
}
|
|
648
989
|
state.values += count
|
|
990
|
+
if (documentValues !== null) {
|
|
991
|
+
state.documentValues += count
|
|
992
|
+
} else if (state.projectResult) {
|
|
993
|
+
state.unscopedValues += count
|
|
994
|
+
}
|
|
649
995
|
}
|
|
650
996
|
}
|