circuitjson-toolkit 1.3.0 → 1.4.1

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.
@@ -0,0 +1,610 @@
1
+ /**
2
+ * Memoizes named computations and dynamically tracks their observed input paths.
3
+ */
4
+ export class SelfAdjustingComputation {
5
+ /** @type {Map<string, { dependencies: object[], value: any, readerRoots: Set<PropertyKey>, readsWholeInput: boolean }>} */
6
+ #computations
7
+
8
+ /** @type {Map<PropertyKey, Set<string>>} */
9
+ #readersByRoot
10
+
11
+ /** @type {Set<string>} */
12
+ #wholeInputReaders
13
+
14
+ /** @type {object | null} */
15
+ #propagationInput
16
+
17
+ /** @type {Set<string> | null} */
18
+ #affectedComputations
19
+
20
+ /** @type {(value: object, path: PropertyKey[]) => boolean} */
21
+ #isAtomic
22
+
23
+ /** @type {Map<symbol, number>} */
24
+ #symbolIds
25
+
26
+ /** @type {number} */
27
+ #nextSymbolId
28
+
29
+ /**
30
+ * @param {{ isAtomic?: (value: object, path: PropertyKey[]) => boolean }} [options] Dependency-tracking options.
31
+ */
32
+ constructor(options = {}) {
33
+ this.#computations = new Map()
34
+ this.#readersByRoot = new Map()
35
+ this.#wholeInputReaders = new Set()
36
+ this.#propagationInput = null
37
+ this.#affectedComputations = null
38
+ this.#isAtomic =
39
+ typeof options.isAtomic === 'function'
40
+ ? options.isAtomic
41
+ : () => false
42
+ this.#symbolIds = new Map()
43
+ this.#nextSymbolId = 1
44
+ }
45
+
46
+ /**
47
+ * Propagates changed modifiable roots through named computations in order.
48
+ * A null change set performs dependency validation for every existing trace.
49
+ * @param {object} input Current input snapshot.
50
+ * @param {PropertyKey[][] | null} changedPaths Changed input paths, or null when unknown.
51
+ * @param {{ name: string, computation: (trackedInput: object) => any }[]} computations Ordered computations.
52
+ * @returns {Map<string, { value: any, recomputed: boolean }>} Results by computation name.
53
+ */
54
+ propagate(input, changedPaths, computations) {
55
+ this.#validateInput(input)
56
+ if (!Array.isArray(computations)) {
57
+ throw new TypeError(
58
+ 'Self-adjusting propagation requires an ordered computation array.'
59
+ )
60
+ }
61
+
62
+ this.#propagationInput = input
63
+ this.#affectedComputations =
64
+ changedPaths === null
65
+ ? null
66
+ : this.#findAffectedComputations(changedPaths)
67
+ const results = new Map()
68
+ try {
69
+ for (const entry of computations) {
70
+ if (!entry || typeof entry !== 'object') {
71
+ throw new TypeError(
72
+ 'Self-adjusting propagation entries must be objects.'
73
+ )
74
+ }
75
+ const key = String(entry.name)
76
+ results.set(key, this.evaluate(key, input, entry.computation))
77
+ }
78
+ return results
79
+ } finally {
80
+ this.#propagationInput = null
81
+ this.#affectedComputations = null
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Evaluates one named computation or reuses its last successful result.
87
+ * @param {string} name Stable computation name.
88
+ * @param {object} input Current input snapshot.
89
+ * @param {(trackedInput: object) => any} computation Synchronous computation.
90
+ * @returns {{ value: any, recomputed: boolean }} Evaluation result.
91
+ */
92
+ evaluate(name, input, computation) {
93
+ this.#validateInput(input)
94
+ if (typeof computation !== 'function') {
95
+ throw new TypeError(
96
+ 'Self-adjusting computation requires a computation function.'
97
+ )
98
+ }
99
+
100
+ const key = String(name)
101
+ const previous = this.#computations.get(key)
102
+ if (previous && !this.#isPotentiallyAffected(key, input)) {
103
+ return { value: previous.value, recomputed: false }
104
+ }
105
+ if (previous && this.#dependenciesMatch(previous.dependencies, input)) {
106
+ return { value: previous.value, recomputed: false }
107
+ }
108
+
109
+ const trace = this.#trace(input, computation)
110
+ this.#replaceTrace(key, trace)
111
+ return { value: trace.value, recomputed: true }
112
+ }
113
+
114
+ /**
115
+ * Removes one named trace and its reader-list entries.
116
+ * @param {string} name Stable computation name.
117
+ * @returns {boolean} Whether a trace was removed.
118
+ */
119
+ forget(name) {
120
+ const key = String(name)
121
+ if (!this.#computations.has(key)) return false
122
+ this.#removeTrace(key)
123
+ return true
124
+ }
125
+
126
+ /**
127
+ * Removes all traces and reader-list entries.
128
+ * @returns {void}
129
+ */
130
+ clear() {
131
+ this.#computations.clear()
132
+ this.#readersByRoot.clear()
133
+ this.#wholeInputReaders.clear()
134
+ this.#propagationInput = null
135
+ this.#affectedComputations = null
136
+ }
137
+
138
+ /**
139
+ * Returns bounded trace-storage counts for diagnostics and tests.
140
+ * @returns {{ computations: number, dependencies: number, readerEdges: number }} Trace statistics.
141
+ */
142
+ getStatistics() {
143
+ return {
144
+ computations: this.#computations.size,
145
+ dependencies: [...this.#computations.values()].reduce(
146
+ (total, trace) => total + trace.dependencies.length,
147
+ 0
148
+ ),
149
+ readerEdges:
150
+ [...this.#readersByRoot.values()].reduce(
151
+ (total, readers) => total + readers.size,
152
+ 0
153
+ ) + this.#wholeInputReaders.size
154
+ }
155
+ }
156
+
157
+ /**
158
+ * Validates one root input snapshot.
159
+ * @param {unknown} input Candidate input.
160
+ * @returns {void}
161
+ */
162
+ #validateInput(input) {
163
+ if (!input || typeof input !== 'object') {
164
+ throw new TypeError(
165
+ 'Self-adjusting computation input must be an object.'
166
+ )
167
+ }
168
+ }
169
+
170
+ /**
171
+ * Finds prior computations that read a changed root.
172
+ * @param {PropertyKey[][]} changedPaths Changed input paths.
173
+ * @returns {Set<string>} Potentially affected computation names.
174
+ */
175
+ #findAffectedComputations(changedPaths) {
176
+ if (!Array.isArray(changedPaths)) {
177
+ throw new TypeError(
178
+ 'Self-adjusting change sets must be arrays of property paths.'
179
+ )
180
+ }
181
+
182
+ const affected = new Set(this.#wholeInputReaders)
183
+ for (const path of changedPaths) {
184
+ if (!Array.isArray(path)) {
185
+ throw new TypeError(
186
+ 'Self-adjusting change-set entries must be property paths.'
187
+ )
188
+ }
189
+ if (path.length === 0) {
190
+ return new Set(this.#computations.keys())
191
+ }
192
+ const readers = this.#readersByRoot.get(path[0])
193
+ readers?.forEach((name) => affected.add(name))
194
+ }
195
+ return affected
196
+ }
197
+
198
+ /**
199
+ * Returns whether a prior trace must be checked in the active propagation.
200
+ * @param {string} name Computation name.
201
+ * @param {object} input Current input snapshot.
202
+ * @returns {boolean} Whether dependency validation is required.
203
+ */
204
+ #isPotentiallyAffected(name, input) {
205
+ return !(
206
+ this.#propagationInput === input &&
207
+ this.#affectedComputations instanceof Set &&
208
+ !this.#affectedComputations.has(name)
209
+ )
210
+ }
211
+
212
+ /**
213
+ * Replaces a trace and its dynamic reader-list entries atomically.
214
+ * @param {string} name Computation name.
215
+ * @param {{ dependencies: object[], value: any }} trace New successful trace.
216
+ * @returns {void}
217
+ */
218
+ #replaceTrace(name, trace) {
219
+ this.#removeTrace(name)
220
+ const readerRoots = new Set()
221
+ let readsWholeInput = false
222
+ for (const dependency of trace.dependencies) {
223
+ if (dependency.path.length === 0) {
224
+ readsWholeInput = true
225
+ } else {
226
+ readerRoots.add(dependency.path[0])
227
+ }
228
+ }
229
+ const storedTrace = {
230
+ ...trace,
231
+ readerRoots,
232
+ readsWholeInput
233
+ }
234
+ this.#computations.set(name, storedTrace)
235
+ readerRoots.forEach((root) => {
236
+ const readers = this.#readersByRoot.get(root) || new Set()
237
+ readers.add(name)
238
+ this.#readersByRoot.set(root, readers)
239
+ })
240
+ if (readsWholeInput) this.#wholeInputReaders.add(name)
241
+ }
242
+
243
+ /**
244
+ * Removes one stored trace and all of its reverse dependency edges.
245
+ * @param {string} name Computation name.
246
+ * @returns {void}
247
+ */
248
+ #removeTrace(name) {
249
+ const previous = this.#computations.get(name)
250
+ if (!previous) return
251
+ previous.readerRoots.forEach((root) => {
252
+ const readers = this.#readersByRoot.get(root)
253
+ readers?.delete(name)
254
+ if (readers?.size === 0) this.#readersByRoot.delete(root)
255
+ })
256
+ if (previous.readsWholeInput) this.#wholeInputReaders.delete(name)
257
+ this.#computations.delete(name)
258
+ }
259
+
260
+ /**
261
+ * Executes one computation while collecting its dynamic dependencies.
262
+ * @param {object} input Raw input snapshot.
263
+ * @param {(trackedInput: object) => any} computation Computation callback.
264
+ * @returns {{ dependencies: object[], value: any }} Successful trace.
265
+ */
266
+ #trace(input, computation) {
267
+ const dependencies = new Map()
268
+ const proxyCache = new WeakMap()
269
+ const proxyMetadata = new WeakMap()
270
+ const trackedInput = this.#createProxy({
271
+ target: input,
272
+ path: [],
273
+ dependencies,
274
+ proxyCache,
275
+ proxyMetadata
276
+ })
277
+ const trackedValue = computation(trackedInput)
278
+ if (SelfAdjustingComputation.#isPromiseLike(trackedValue)) {
279
+ throw new TypeError(
280
+ 'Self-adjusting computations must be synchronous so dependency tracing cannot escape.'
281
+ )
282
+ }
283
+ const metadata =
284
+ trackedValue &&
285
+ (typeof trackedValue === 'object' ||
286
+ typeof trackedValue === 'function')
287
+ ? proxyMetadata.get(trackedValue)
288
+ : null
289
+ if (metadata) {
290
+ this.#record(dependencies, {
291
+ type: 'value',
292
+ path: metadata.path,
293
+ expected: metadata.target
294
+ })
295
+ }
296
+
297
+ return {
298
+ dependencies: [...dependencies.values()],
299
+ value: metadata?.target ?? trackedValue
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Creates a read-tracking proxy for one traversable input container.
305
+ * @param {{ target: object, path: PropertyKey[], dependencies: Map<string, object>, proxyCache: WeakMap<object, Map<string, object>>, proxyMetadata: WeakMap<object, { target: object, path: PropertyKey[] }> }} context Tracking context.
306
+ * @returns {object} Read-tracking proxy.
307
+ */
308
+ #createProxy(context) {
309
+ const pathKey = this.#pathKey(context.path)
310
+ const cachedByPath = context.proxyCache.get(context.target)
311
+ if (cachedByPath?.has(pathKey)) {
312
+ return cachedByPath.get(pathKey)
313
+ }
314
+
315
+ const proxy = new Proxy(context.target, {
316
+ get: (target, property) =>
317
+ this.#readProperty(context, target, property),
318
+ has: (target, property) =>
319
+ this.#readPresence(context, target, property),
320
+ ownKeys: (target) => this.#readKeys(context, target),
321
+ getOwnPropertyDescriptor: (target, property) =>
322
+ this.#readDescriptor(context, target, property),
323
+ set: () => this.#rejectWrite(),
324
+ defineProperty: () => this.#rejectWrite(),
325
+ deleteProperty: () => this.#rejectWrite(),
326
+ setPrototypeOf: () => this.#rejectWrite(),
327
+ preventExtensions: () => this.#rejectWrite()
328
+ })
329
+ const nextCachedByPath = cachedByPath || new Map()
330
+ nextCachedByPath.set(pathKey, proxy)
331
+ if (!cachedByPath) {
332
+ context.proxyCache.set(context.target, nextCachedByPath)
333
+ }
334
+ context.proxyMetadata.set(proxy, {
335
+ target: context.target,
336
+ path: [...context.path]
337
+ })
338
+ return proxy
339
+ }
340
+
341
+ /**
342
+ * Reads and records one property dependency.
343
+ * @param {object} context Tracking context.
344
+ * @param {object} target Current raw target.
345
+ * @param {PropertyKey} property Requested property.
346
+ * @returns {any} Raw atomic value or tracked container.
347
+ */
348
+ #readProperty(context, target, property) {
349
+ const value = Reflect.get(target, property, target)
350
+ const path = [...context.path, property]
351
+ if (this.#isTraversable(value, path)) {
352
+ this.#record(context.dependencies, {
353
+ type: 'kind',
354
+ path,
355
+ expected: SelfAdjustingComputation.#valueKind(value)
356
+ })
357
+ return this.#createProxy({ ...context, target: value, path })
358
+ }
359
+
360
+ this.#record(context.dependencies, {
361
+ type: 'value',
362
+ path,
363
+ expected: value
364
+ })
365
+ return value
366
+ }
367
+
368
+ /**
369
+ * Reads and records a property-existence dependency.
370
+ * @param {object} context Tracking context.
371
+ * @param {object} target Current raw target.
372
+ * @param {PropertyKey} property Requested property.
373
+ * @returns {boolean} Whether the property exists.
374
+ */
375
+ #readPresence(context, target, property) {
376
+ const path = [...context.path, property]
377
+ const expected = Reflect.has(target, property)
378
+ this.#record(context.dependencies, {
379
+ type: 'has',
380
+ path,
381
+ expected
382
+ })
383
+ return expected
384
+ }
385
+
386
+ /**
387
+ * Reads and records the ordered own-key set for one container.
388
+ * @param {object} context Tracking context.
389
+ * @param {object} target Current raw target.
390
+ * @returns {PropertyKey[]} Own keys.
391
+ */
392
+ #readKeys(context, target) {
393
+ const expected = Reflect.ownKeys(target)
394
+ this.#record(context.dependencies, {
395
+ type: 'keys',
396
+ path: [...context.path],
397
+ expected
398
+ })
399
+ return expected
400
+ }
401
+
402
+ /**
403
+ * Reads and records whether one own property is enumerable.
404
+ * @param {object} context Tracking context.
405
+ * @param {object} target Current raw target.
406
+ * @param {PropertyKey} property Requested property.
407
+ * @returns {PropertyDescriptor | undefined} Raw property descriptor.
408
+ */
409
+ #readDescriptor(context, target, property) {
410
+ const descriptor = Reflect.getOwnPropertyDescriptor(target, property)
411
+ this.#record(context.dependencies, {
412
+ type: 'descriptor',
413
+ path: [...context.path, property],
414
+ expected: SelfAdjustingComputation.#descriptorState(descriptor)
415
+ })
416
+ return descriptor
417
+ }
418
+
419
+ /**
420
+ * Returns whether a value should expose nested dependency reads.
421
+ * @param {any} value Candidate value.
422
+ * @param {PropertyKey[]} path Input path.
423
+ * @returns {boolean} Whether the value is a traversable container.
424
+ */
425
+ #isTraversable(value, path) {
426
+ if (!value || typeof value !== 'object') return false
427
+ if (this.#isAtomic(value, [...path])) return false
428
+ if (Array.isArray(value)) return true
429
+ const prototype = Reflect.getPrototypeOf(value)
430
+ return prototype === Object.prototype || prototype === null
431
+ }
432
+
433
+ /**
434
+ * Stores or replaces one dependency by type and input path.
435
+ * @param {Map<string, object>} dependencies Dependency registry.
436
+ * @param {{ type: string, path: PropertyKey[], expected: any }} dependency Dependency record.
437
+ * @returns {void}
438
+ */
439
+ #record(dependencies, dependency) {
440
+ dependencies.set(
441
+ dependency.type + ':' + this.#pathKey(dependency.path),
442
+ dependency
443
+ )
444
+ }
445
+
446
+ /**
447
+ * Returns whether every dependency still matches a new input.
448
+ * @param {object[]} dependencies Previous successful dependencies.
449
+ * @param {object} input Current raw input.
450
+ * @returns {boolean} Whether the computation can be reused.
451
+ */
452
+ #dependenciesMatch(dependencies, input) {
453
+ return dependencies.every((dependency) =>
454
+ this.#dependencyMatches(dependency, input)
455
+ )
456
+ }
457
+
458
+ /**
459
+ * Compares one recorded dependency against a new input.
460
+ * @param {{ type: string, path: PropertyKey[], expected: any }} dependency Recorded dependency.
461
+ * @param {object} input Current raw input.
462
+ * @returns {boolean} Whether the dependency is unchanged.
463
+ */
464
+ #dependencyMatches(dependency, input) {
465
+ if (dependency.type === 'has') {
466
+ const parent = this.#readPath(input, dependency.path.slice(0, -1))
467
+ return (
468
+ parent.found &&
469
+ Reflect.has(Object(parent.value), dependency.path.at(-1)) ===
470
+ dependency.expected
471
+ )
472
+ }
473
+
474
+ const current = this.#readPath(input, dependency.path)
475
+ if (dependency.type === 'value') {
476
+ return (
477
+ current.found && Object.is(current.value, dependency.expected)
478
+ )
479
+ }
480
+ if (dependency.type === 'kind') {
481
+ return (
482
+ current.found &&
483
+ SelfAdjustingComputation.#valueKind(current.value) ===
484
+ dependency.expected
485
+ )
486
+ }
487
+ if (dependency.type === 'keys') {
488
+ return (
489
+ current.found &&
490
+ current.value !== null &&
491
+ typeof current.value === 'object' &&
492
+ SelfAdjustingComputation.#sameKeys(
493
+ Reflect.ownKeys(current.value),
494
+ dependency.expected
495
+ )
496
+ )
497
+ }
498
+ if (dependency.type === 'descriptor') {
499
+ const parent = this.#readPath(input, dependency.path.slice(0, -1))
500
+ if (!parent.found || parent.value === null) return false
501
+ const descriptor = Reflect.getOwnPropertyDescriptor(
502
+ Object(parent.value),
503
+ dependency.path.at(-1)
504
+ )
505
+ return (
506
+ SelfAdjustingComputation.#descriptorState(descriptor) ===
507
+ dependency.expected
508
+ )
509
+ }
510
+ return false
511
+ }
512
+
513
+ /**
514
+ * Resolves one raw input path without invoking dependency tracking.
515
+ * @param {object} input Root input.
516
+ * @param {PropertyKey[]} path Input path.
517
+ * @returns {{ found: boolean, value: any }} Resolved value.
518
+ */
519
+ #readPath(input, path) {
520
+ let value = input
521
+ for (const property of path) {
522
+ if (
523
+ value === null ||
524
+ (typeof value !== 'object' && typeof value !== 'function')
525
+ ) {
526
+ return { found: false, value: undefined }
527
+ }
528
+ value = Reflect.get(value, property, value)
529
+ }
530
+ return { found: true, value }
531
+ }
532
+
533
+ /**
534
+ * Creates a collision-free string key for a property path.
535
+ * @param {PropertyKey[]} path Input path.
536
+ * @returns {string} Registry key.
537
+ */
538
+ #pathKey(path) {
539
+ return path
540
+ .map((property) => {
541
+ if (typeof property === 'symbol') {
542
+ if (!this.#symbolIds.has(property)) {
543
+ this.#symbolIds.set(property, this.#nextSymbolId)
544
+ this.#nextSymbolId += 1
545
+ }
546
+ return 'y' + this.#symbolIds.get(property)
547
+ }
548
+ const text = String(property)
549
+ return 's' + text.length + ':' + text
550
+ })
551
+ .join('|')
552
+ }
553
+
554
+ /**
555
+ * Rejects mutation through a tracked snapshot.
556
+ * @returns {never}
557
+ */
558
+ #rejectWrite() {
559
+ throw new TypeError(
560
+ 'Self-adjusting computation inputs are read-only while tracked.'
561
+ )
562
+ }
563
+
564
+ /**
565
+ * Returns the comparison category for one container value.
566
+ * @param {any} value Candidate value.
567
+ * @returns {string} Value category.
568
+ */
569
+ static #valueKind(value) {
570
+ if (value === null) return 'null'
571
+ if (Array.isArray(value)) return 'array'
572
+ return typeof value
573
+ }
574
+
575
+ /**
576
+ * Returns the dependency-relevant state of a property descriptor.
577
+ * @param {PropertyDescriptor | undefined} descriptor Property descriptor.
578
+ * @returns {string} Descriptor state.
579
+ */
580
+ static #descriptorState(descriptor) {
581
+ if (!descriptor) return 'missing'
582
+ return descriptor.enumerable ? 'enumerable' : 'non-enumerable'
583
+ }
584
+
585
+ /**
586
+ * Compares two ordered property-key arrays.
587
+ * @param {PropertyKey[]} left Current keys.
588
+ * @param {PropertyKey[]} right Recorded keys.
589
+ * @returns {boolean} Whether both key sets and orders match.
590
+ */
591
+ static #sameKeys(left, right) {
592
+ return (
593
+ left.length === right.length &&
594
+ left.every((key, index) => Object.is(key, right[index]))
595
+ )
596
+ }
597
+
598
+ /**
599
+ * Returns whether a computation result is a promise or thenable.
600
+ * @param {unknown} value Candidate result.
601
+ * @returns {boolean} Whether the result is asynchronous.
602
+ */
603
+ static #isPromiseLike(value) {
604
+ return Boolean(
605
+ value &&
606
+ (typeof value === 'object' || typeof value === 'function') &&
607
+ typeof value.then === 'function'
608
+ )
609
+ }
610
+ }
@@ -241,6 +241,44 @@ export class BinaryDataSnapshot {
241
241
  }
242
242
  }
243
243
 
244
+ /**
245
+ * Copies one bounded byte range into an existing isolated byte array.
246
+ * @param {{ buffer: ArrayBuffer | SharedArrayBuffer, byteOffset: number, byteLength: number }} range Captured intrinsic range.
247
+ * @param {Uint8Array} target Isolated destination bytes.
248
+ * @param {number} offset Visible-range byte offset.
249
+ * @param {number} count Number of bytes to copy.
250
+ * @returns {void}
251
+ * @internal
252
+ */
253
+ static copyBytesInto(range, target, offset, count) {
254
+ if (
255
+ !(target instanceof Uint8Array) ||
256
+ !Number.isSafeInteger(offset) ||
257
+ !Number.isSafeInteger(count) ||
258
+ offset < 0 ||
259
+ count < 0 ||
260
+ offset + count > range?.byteLength ||
261
+ target.byteLength !== range?.byteLength
262
+ ) {
263
+ throw new TypeError('Invalid binary copy range.')
264
+ }
265
+ try {
266
+ const source = new Uint8Array(
267
+ range.buffer,
268
+ range.byteOffset + offset,
269
+ count
270
+ )
271
+ const destination = new Uint8Array(
272
+ target.buffer,
273
+ target.byteOffset + offset,
274
+ count
275
+ )
276
+ UINT8_ARRAY_SET.call(destination, source)
277
+ } catch {
278
+ throw new TypeError('Binary data changed during capture.')
279
+ }
280
+ }
281
+
244
282
  /**
245
283
  * Copies one binary metadata value while retaining its common view type.
246
284
  * @param {unknown} value Binary metadata.
@@ -261,6 +299,27 @@ export class BinaryDataSnapshot {
261
299
  return new Constructor(bytes.buffer)
262
300
  }
263
301
 
302
+ /**
303
+ * Restores one common binary view type from completely isolated bytes.
304
+ * @param {unknown} value Original binary value used only for view type.
305
+ * @param {{ kind: 'buffer' | 'typed-array' | 'data-view' }} range Captured intrinsic kind.
306
+ * @param {Uint8Array} bytes Completely copied isolated bytes.
307
+ * @returns {ArrayBuffer | Uint8Array | DataView} Isolated binary clone.
308
+ * @internal
309
+ */
310
+ static cloneFromBytes(value, range, bytes) {
311
+ if (!(bytes instanceof Uint8Array)) {
312
+ throw new TypeError('Expected isolated binary bytes.')
313
+ }
314
+ if (range.kind === 'buffer') return bytes.buffer
315
+ if (range.kind === 'data-view') return new DataView(bytes.buffer)
316
+ const Constructor = BinaryDataSnapshot.#typedArrayConstructor(value)
317
+ if (!Constructor || Constructor === Uint8Array) return bytes
318
+ const bytesPerElement = Constructor.BYTES_PER_ELEMENT
319
+ if (bytes.byteLength % bytesPerElement !== 0) return bytes
320
+ return new Constructor(bytes.buffer)
321
+ }
322
+
264
323
  /**
265
324
  * Calls a captured buffer length getter as a brand check.
266
325
  * @param {Function | null | undefined} getter Intrinsic getter.