@peerbit/document 15.0.13 → 15.0.15

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,848 @@
1
+ import type { PublicSignKey, SignatureWithKey } from "@peerbit/crypto";
2
+ import * as indexerTypes from "@peerbit/indexer-interface";
3
+ import { Entry } from "@peerbit/log";
4
+ import {
5
+ DeleteByStringKeyOperation,
6
+ DeleteOperation,
7
+ type Operation,
8
+ PutOperation,
9
+ PutWithKeyOperation,
10
+ } from "./operation.js";
11
+
12
+ type LazyPropertyFactories = Map<PropertyKey, () => unknown>;
13
+
14
+ const isPromiseLike = (value: unknown): value is PromiseLike<unknown> =>
15
+ !!value &&
16
+ (typeof value === "object" || typeof value === "function") &&
17
+ typeof (value as { then?: unknown }).then === "function";
18
+
19
+ const mapMaybePromise = (
20
+ value: unknown,
21
+ map: (value: any) => unknown,
22
+ ): unknown => (isPromiseLike(value) ? value.then(map) : map(value));
23
+
24
+ const copyOptionalBytes = (
25
+ bytes: Uint8Array | undefined,
26
+ ): Uint8Array | undefined =>
27
+ bytes == null ? undefined : new Uint8Array(bytes);
28
+
29
+ const preserveArrayIntegrity = <T>(source: readonly T[], copy: T[]): T[] => {
30
+ if (Object.isFrozen(source)) {
31
+ Object.freeze(copy);
32
+ } else if (Object.isSealed(source)) {
33
+ Object.seal(copy);
34
+ } else if (!Object.isExtensible(source)) {
35
+ Object.preventExtensions(copy);
36
+ }
37
+ return copy;
38
+ };
39
+
40
+ const detachArray = <T>(
41
+ values: readonly T[],
42
+ detach: (value: T) => T = (value) => value,
43
+ ): T[] => preserveArrayIntegrity(values, values.map(detach));
44
+
45
+ const getInheritedPropertyDescriptor = (
46
+ value: object,
47
+ key: PropertyKey,
48
+ ): PropertyDescriptor | undefined => {
49
+ let prototype = Object.getPrototypeOf(value) as object | null;
50
+ while (prototype) {
51
+ const descriptor = Reflect.getOwnPropertyDescriptor(prototype, key);
52
+ if (descriptor) {
53
+ return descriptor;
54
+ }
55
+ prototype = Object.getPrototypeOf(prototype) as object | null;
56
+ }
57
+ return undefined;
58
+ };
59
+
60
+ /**
61
+ * Clone an object without resolving selected nested values. For normal
62
+ * objects the selected fields become callback-local lazy accessors. The clone
63
+ * receives the source's frozen/sealed/non-extensible state only after those
64
+ * accessors are installed, so a no-touch callback never resolves lazy source
65
+ * fields.
66
+ */
67
+ const cloneWithLazyProperties = <T extends object>(
68
+ value: T,
69
+ factories: LazyPropertyFactories,
70
+ ): T => {
71
+ const descriptors = Object.getOwnPropertyDescriptors(value) as Record<
72
+ PropertyKey,
73
+ PropertyDescriptor
74
+ >;
75
+ const extensible = Object.isExtensible(value);
76
+ for (const [key, factory] of factories) {
77
+ const ownDescriptor = descriptors[key];
78
+ const descriptor =
79
+ ownDescriptor ?? getInheritedPropertyDescriptor(value, key);
80
+ let resolved = false;
81
+ let detachedValue: unknown;
82
+ const writable = descriptor
83
+ ? (ownDescriptor != null || extensible) &&
84
+ ("value" in descriptor
85
+ ? descriptor.writable !== false
86
+ : descriptor.set != null)
87
+ : extensible;
88
+ descriptors[key] = {
89
+ configurable: descriptor?.configurable ?? true,
90
+ enumerable: descriptor?.enumerable ?? true,
91
+ get() {
92
+ if (!resolved) {
93
+ detachedValue = factory();
94
+ resolved = true;
95
+ }
96
+ return detachedValue;
97
+ },
98
+ ...(writable
99
+ ? {
100
+ set(nextValue: unknown) {
101
+ detachedValue = nextValue;
102
+ resolved = true;
103
+ },
104
+ }
105
+ : {}),
106
+ };
107
+ }
108
+ const clone = Object.create(Object.getPrototypeOf(value), descriptors) as T;
109
+ if (Object.isFrozen(value)) {
110
+ Object.freeze(clone);
111
+ } else if (Object.isSealed(value)) {
112
+ Object.seal(clone);
113
+ } else if (!Object.isExtensible(value)) {
114
+ Object.preventExtensions(clone);
115
+ }
116
+ return clone;
117
+ };
118
+
119
+ const addOwnByteFactories = (
120
+ value: object,
121
+ factories: LazyPropertyFactories,
122
+ ): void => {
123
+ const descriptors = Object.getOwnPropertyDescriptors(value) as Record<
124
+ PropertyKey,
125
+ PropertyDescriptor
126
+ >;
127
+ for (const key of Reflect.ownKeys(descriptors)) {
128
+ const descriptor = descriptors[key];
129
+ if (descriptor && "value" in descriptor) {
130
+ const bytes = descriptor.value;
131
+ if (bytes instanceof Uint8Array) {
132
+ factories.set(key, () => new Uint8Array(bytes));
133
+ }
134
+ }
135
+ }
136
+ };
137
+
138
+ const addOwnArrayFactories = (
139
+ value: object,
140
+ factories: LazyPropertyFactories,
141
+ ): void => {
142
+ const descriptors = Object.getOwnPropertyDescriptors(value) as Record<
143
+ PropertyKey,
144
+ PropertyDescriptor
145
+ >;
146
+ for (const key of Reflect.ownKeys(descriptors)) {
147
+ const descriptor = descriptors[key];
148
+ if (
149
+ descriptor &&
150
+ "value" in descriptor &&
151
+ Array.isArray(descriptor.value)
152
+ ) {
153
+ const array = descriptor.value as unknown[];
154
+ factories.set(key, () =>
155
+ detachArray(array, (item) =>
156
+ item instanceof Uint8Array ? new Uint8Array(item) : item,
157
+ ),
158
+ );
159
+ }
160
+ }
161
+ };
162
+
163
+ const detachedCallbackEntryLikes = new WeakSet<object>();
164
+
165
+ const createEntryDetachmentContext = () => {
166
+ let metadata: WeakMap<object, object> | undefined;
167
+ let clocks: WeakMap<object, object> | undefined;
168
+ let timestamps: WeakMap<object, object> | undefined;
169
+ let signatures: WeakMap<object, object> | undefined;
170
+ let publicKeys: WeakMap<object, object> | undefined;
171
+ let entryLikes: WeakMap<object, object> | undefined;
172
+ let arrays: WeakMap<object, unknown[]> | undefined;
173
+
174
+ const detachValues = <T>(
175
+ values: readonly T[],
176
+ detach: (value: T) => T = (value) => value,
177
+ ): T[] => {
178
+ const cached = arrays?.get(values);
179
+ if (cached) {
180
+ return cached as T[];
181
+ }
182
+ const clone = detachArray(values, detach);
183
+ (arrays ??= new WeakMap()).set(values, clone);
184
+ return clone;
185
+ };
186
+
187
+ const detachTimestamp = <T>(value: T): T => {
188
+ if (!value || typeof value !== "object") {
189
+ return value;
190
+ }
191
+ const cached = timestamps?.get(value);
192
+ if (cached) {
193
+ return cached as T;
194
+ }
195
+ const factories: LazyPropertyFactories = new Map();
196
+ addOwnByteFactories(value, factories);
197
+ const clone = cloneWithLazyProperties(value, factories);
198
+ (timestamps ??= new WeakMap()).set(value, clone);
199
+ return clone;
200
+ };
201
+
202
+ const detachClock = <T>(value: T): T => {
203
+ if (!value || typeof value !== "object") {
204
+ return value;
205
+ }
206
+ const cached = clocks?.get(value);
207
+ if (cached) {
208
+ return cached as T;
209
+ }
210
+ const factories: LazyPropertyFactories = new Map();
211
+ addOwnByteFactories(value, factories);
212
+ if (Reflect.has(value, "id") && !factories.has("id")) {
213
+ factories.set("id", () =>
214
+ copyOptionalBytes(
215
+ Reflect.get(value, "id", value) as Uint8Array | undefined,
216
+ ),
217
+ );
218
+ }
219
+ if (Reflect.has(value, "timestamp")) {
220
+ factories.set("timestamp", () =>
221
+ detachTimestamp(Reflect.get(value, "timestamp", value)),
222
+ );
223
+ }
224
+ const clone = cloneWithLazyProperties(value, factories);
225
+ (clocks ??= new WeakMap()).set(value, clone);
226
+ return clone;
227
+ };
228
+
229
+ const detachMeta = <T>(value: T): T => {
230
+ if (!value || typeof value !== "object") {
231
+ return value;
232
+ }
233
+ const cached = metadata?.get(value);
234
+ if (cached) {
235
+ return cached as T;
236
+ }
237
+ const factories: LazyPropertyFactories = new Map();
238
+ addOwnByteFactories(value, factories);
239
+ if (Reflect.has(value, "data") && !factories.has("data")) {
240
+ factories.set("data", () =>
241
+ copyOptionalBytes(
242
+ Reflect.get(value, "data", value) as Uint8Array | undefined,
243
+ ),
244
+ );
245
+ }
246
+ if (Reflect.has(value, "clock")) {
247
+ factories.set("clock", () =>
248
+ detachClock(Reflect.get(value, "clock", value)),
249
+ );
250
+ }
251
+ if (Reflect.has(value, "next")) {
252
+ factories.set("next", () => {
253
+ const next = Reflect.get(value, "next", value);
254
+ return Array.isArray(next) ? detachValues(next) : next;
255
+ });
256
+ }
257
+ const clone = cloneWithLazyProperties(value, factories);
258
+ (metadata ??= new WeakMap()).set(value, clone);
259
+ return clone;
260
+ };
261
+
262
+ const detachPublicKey = <T>(value: T): T => {
263
+ if (!value || typeof value !== "object") {
264
+ return value;
265
+ }
266
+ const cached = publicKeys?.get(value);
267
+ if (cached) {
268
+ return cached as T;
269
+ }
270
+ const factories: LazyPropertyFactories = new Map();
271
+ addOwnByteFactories(value, factories);
272
+ const clone = cloneWithLazyProperties(value, factories);
273
+ (publicKeys ??= new WeakMap()).set(value, clone);
274
+ return clone;
275
+ };
276
+
277
+ const detachSignature = <T>(value: T): T => {
278
+ if (!value || typeof value !== "object") {
279
+ return value;
280
+ }
281
+ const cached = signatures?.get(value);
282
+ if (cached) {
283
+ return cached as T;
284
+ }
285
+ const factories: LazyPropertyFactories = new Map();
286
+ addOwnByteFactories(value, factories);
287
+ if (Reflect.has(value, "publicKey")) {
288
+ factories.set("publicKey", () =>
289
+ detachPublicKey(Reflect.get(value, "publicKey", value)),
290
+ );
291
+ }
292
+ const clone = cloneWithLazyProperties(value, factories);
293
+ (signatures ??= new WeakMap()).set(value, clone);
294
+ return clone;
295
+ };
296
+
297
+ const detachSignatures = (
298
+ values: readonly SignatureWithKey[],
299
+ ): SignatureWithKey[] => detachValues(values, detachSignature);
300
+
301
+ const detachPublicKeys = (
302
+ values: readonly PublicSignKey[],
303
+ ): PublicSignKey[] => detachValues(values, detachPublicKey);
304
+
305
+ const detachEntryLike = <T extends object>(value: T): T => {
306
+ if (detachedCallbackEntryLikes.has(value)) {
307
+ return value;
308
+ }
309
+ const cached = entryLikes?.get(value);
310
+ if (cached) {
311
+ return cached as T;
312
+ }
313
+ const factories: LazyPropertyFactories = new Map();
314
+ addOwnByteFactories(value, factories);
315
+ addOwnArrayFactories(value, factories);
316
+ if (Reflect.has(value, "meta")) {
317
+ factories.set("meta", () =>
318
+ detachMeta(Reflect.get(value, "meta", value)),
319
+ );
320
+ }
321
+ for (const methodName of ["getMetaBytes", "getHashDigestBytes"] as const) {
322
+ const method = Reflect.get(value, methodName, value);
323
+ if (typeof method === "function") {
324
+ factories.set(
325
+ methodName,
326
+ () =>
327
+ (...args: unknown[]) =>
328
+ mapMaybePromise(
329
+ Reflect.apply(method, value, args),
330
+ copyOptionalBytes,
331
+ ),
332
+ );
333
+ }
334
+ }
335
+ const clone = cloneWithLazyProperties(value, factories);
336
+ (entryLikes ??= new WeakMap()).set(value, clone);
337
+ detachedCallbackEntryLikes.add(clone);
338
+ return clone;
339
+ };
340
+
341
+ return {
342
+ detachArray: detachValues,
343
+ detachClock,
344
+ detachEntryLike,
345
+ detachMeta,
346
+ detachPublicKeys,
347
+ detachSignatures,
348
+ };
349
+ };
350
+
351
+ type EntryDetachmentContext = ReturnType<typeof createEntryDetachmentContext>;
352
+ type EntryDetachmentContextProvider = () => EntryDetachmentContext;
353
+
354
+ const detachPreparedAppendJoinFactsForCallback = (
355
+ facts: object,
356
+ canonicalEntry: Entry<any>,
357
+ callbackEntry: Entry<any>,
358
+ getContext: EntryDetachmentContextProvider,
359
+ ): object => {
360
+ const factories: LazyPropertyFactories = new Map();
361
+ addOwnByteFactories(facts, factories);
362
+ if (Reflect.has(facts, "bytes")) {
363
+ factories.set("bytes", () =>
364
+ copyOptionalBytes(
365
+ Reflect.get(facts, "bytes", facts) as Uint8Array | undefined,
366
+ ),
367
+ );
368
+ }
369
+ if (Reflect.has(facts, "meta")) {
370
+ factories.set("meta", () =>
371
+ getContext().detachMeta(Reflect.get(facts, "meta", facts)),
372
+ );
373
+ }
374
+ const getShallowEntry = Reflect.get(facts, "getShallowEntry", facts);
375
+ if (typeof getShallowEntry === "function") {
376
+ factories.set("getShallowEntry", () => (...args: unknown[]) => {
377
+ const shallow = Reflect.apply(getShallowEntry, facts, args) as unknown;
378
+ return shallow && typeof shallow === "object"
379
+ ? getContext().detachEntryLike(shallow)
380
+ : shallow;
381
+ });
382
+ }
383
+ const materializeEntry = Reflect.get(facts, "materializeEntry", facts);
384
+ if (typeof materializeEntry === "function") {
385
+ factories.set(
386
+ "materializeEntry",
387
+ () =>
388
+ (...args: unknown[]) =>
389
+ mapMaybePromise(
390
+ Reflect.apply(materializeEntry, facts, args),
391
+ (materialized) =>
392
+ materialized === canonicalEntry
393
+ ? callbackEntry
394
+ : materialized instanceof Entry
395
+ ? detachEntryPayloadForCallbackWithContext(
396
+ materialized,
397
+ getContext,
398
+ )
399
+ : materialized && typeof materialized === "object"
400
+ ? getContext().detachEntryLike(materialized)
401
+ : materialized,
402
+ ),
403
+ );
404
+ }
405
+ return cloneWithLazyProperties(facts, factories);
406
+ };
407
+
408
+ /**
409
+ * Borsh byte fields are input-buffer views. Clone only fields that can expose
410
+ * verified operation bytes while retaining the operation's exact prototype,
411
+ * subclass fields, symbols, and property descriptors.
412
+ */
413
+ export const detachOperationBytes = <T extends Operation>(operation: T): T => {
414
+ const cloneWithField = <V extends object>(
415
+ value: V,
416
+ key: PropertyKey,
417
+ replacement: unknown,
418
+ ): V => {
419
+ const descriptors = Object.getOwnPropertyDescriptors(value) as Record<
420
+ PropertyKey,
421
+ PropertyDescriptor
422
+ >;
423
+ descriptors[key] = {
424
+ ...descriptors[key],
425
+ value: replacement,
426
+ };
427
+ const clone = Object.create(Object.getPrototypeOf(value), descriptors) as V;
428
+ if (!Object.isExtensible(value)) {
429
+ Object.preventExtensions(clone);
430
+ }
431
+ return clone;
432
+ };
433
+ if (operation instanceof PutWithKeyOperation) {
434
+ return cloneWithField(
435
+ operation,
436
+ "data",
437
+ new Uint8Array(operation.data),
438
+ ) as T;
439
+ }
440
+ if (operation instanceof PutOperation) {
441
+ return cloneWithField(
442
+ operation,
443
+ "data",
444
+ new Uint8Array(operation.data),
445
+ ) as T;
446
+ }
447
+ if (operation instanceof DeleteOperation) {
448
+ const key = cloneWithField(
449
+ operation.key,
450
+ "key",
451
+ operation.key instanceof indexerTypes.Uint8ArrayKey
452
+ ? new Uint8Array(operation.key.key)
453
+ : operation.key.key,
454
+ );
455
+ return cloneWithField(operation, "key", key) as T;
456
+ }
457
+ if (operation instanceof DeleteByStringKeyOperation) {
458
+ return cloneWithField(operation, "key", operation.key) as T;
459
+ }
460
+ return operation;
461
+ };
462
+
463
+ type LazyCallbackProperty = "operation" | "entry";
464
+ type LazyCallbackState = {
465
+ operation?: Operation;
466
+ entry?: Entry<any>;
467
+ operationPending: boolean;
468
+ entryPending: boolean;
469
+ };
470
+
471
+ const lazyCallbackStates = new WeakMap<object, LazyCallbackState>();
472
+
473
+ const isLazyCallbackProperty = (
474
+ property: PropertyKey,
475
+ ): property is LazyCallbackProperty =>
476
+ property === "operation" || property === "entry";
477
+
478
+ const settleCallbackProperty = (
479
+ target: object,
480
+ state: LazyCallbackState,
481
+ property: LazyCallbackProperty,
482
+ ): void => {
483
+ state[`${property}Pending`] = false;
484
+ state[property] = undefined;
485
+ if (!state.operationPending && !state.entryPending) {
486
+ lazyCallbackStates.delete(target);
487
+ }
488
+ };
489
+
490
+ const materializeCallbackProperty = (
491
+ target: object,
492
+ property: LazyCallbackProperty,
493
+ ): void => {
494
+ const state = lazyCallbackStates.get(target);
495
+ if (!state || !state[`${property}Pending`]) {
496
+ return;
497
+ }
498
+ const value =
499
+ property === "operation"
500
+ ? detachOperationBytes(state.operation!)
501
+ : detachEntryPayloadForCallback(state.entry!);
502
+ Reflect.set(target, property, value, target);
503
+ settleCallbackProperty(target, state, property);
504
+ };
505
+
506
+ const lazyCallbackHandler: ProxyHandler<any> = {
507
+ get(target, property, receiver) {
508
+ if (isLazyCallbackProperty(property)) {
509
+ materializeCallbackProperty(target, property);
510
+ }
511
+ return Reflect.get(target, property, receiver);
512
+ },
513
+ set(target, property, value) {
514
+ const updated = Reflect.set(target, property, value, target);
515
+ if (updated && isLazyCallbackProperty(property)) {
516
+ const state = lazyCallbackStates.get(target);
517
+ if (state) {
518
+ settleCallbackProperty(target, state, property);
519
+ }
520
+ }
521
+ return updated;
522
+ },
523
+ deleteProperty(target, property) {
524
+ const deleted = Reflect.deleteProperty(target, property);
525
+ if (deleted && isLazyCallbackProperty(property)) {
526
+ const state = lazyCallbackStates.get(target);
527
+ if (state) {
528
+ settleCallbackProperty(target, state, property);
529
+ }
530
+ }
531
+ return deleted;
532
+ },
533
+ defineProperty(target, property, descriptor) {
534
+ if (isLazyCallbackProperty(property)) {
535
+ const replacesValue =
536
+ "value" in descriptor || "get" in descriptor || "set" in descriptor;
537
+ if (!replacesValue) {
538
+ materializeCallbackProperty(target, property);
539
+ }
540
+ const defined = Reflect.defineProperty(target, property, descriptor);
541
+ if (defined && replacesValue) {
542
+ const state = lazyCallbackStates.get(target);
543
+ if (state) {
544
+ settleCallbackProperty(target, state, property);
545
+ }
546
+ }
547
+ return defined;
548
+ }
549
+ return Reflect.defineProperty(target, property, descriptor);
550
+ },
551
+ getOwnPropertyDescriptor(target, property) {
552
+ if (isLazyCallbackProperty(property)) {
553
+ materializeCallbackProperty(target, property);
554
+ }
555
+ return Reflect.getOwnPropertyDescriptor(target, property);
556
+ },
557
+ };
558
+
559
+ export const detachCanPerformCallbackProperties = <
560
+ T extends { operation: Operation; entry: Entry<any> },
561
+ >(
562
+ properties: T,
563
+ ): T => {
564
+ lazyCallbackStates.set(properties, {
565
+ operation: properties.operation,
566
+ entry: properties.entry,
567
+ operationPending: true,
568
+ entryPending: true,
569
+ });
570
+ Reflect.set(properties, "operation", undefined, properties);
571
+ Reflect.set(properties, "entry", undefined, properties);
572
+ return new Proxy(properties, lazyCallbackHandler);
573
+ };
574
+
575
+ /**
576
+ * Return an Entry view whose public accessors cannot expose verified payload,
577
+ * metadata, signature, public-key, or hash-digest buffers to application
578
+ * callbacks. Nested copies remain lazy, including metadata byte fields.
579
+ */
580
+ const detachedCallbackEntries = new WeakSet<object>();
581
+
582
+ const detachEntryPayloadForCallbackWithContext = <T>(
583
+ entry: Entry<T>,
584
+ getContext: EntryDetachmentContextProvider,
585
+ ): Entry<T> => {
586
+ if (detachedCallbackEntries.has(entry)) {
587
+ return entry;
588
+ }
589
+ let detachedPayload: any;
590
+ let detachedMaterialized: Entry<T> | undefined;
591
+ let callbackEntry: Entry<T>;
592
+ const boundMethods = new Map<
593
+ PropertyKey,
594
+ { source: Function; bound: Function }
595
+ >();
596
+ const copyPayload = (payload: any) => {
597
+ if (detachedPayload) {
598
+ return detachedPayload;
599
+ }
600
+ const data = new Uint8Array(payload.data);
601
+ const descriptors = Object.getOwnPropertyDescriptors(payload) as Record<
602
+ PropertyKey,
603
+ PropertyDescriptor
604
+ >;
605
+ descriptors.data = {
606
+ ...descriptors.data,
607
+ value: data,
608
+ };
609
+ descriptors._value = {
610
+ configurable: true,
611
+ enumerable: false,
612
+ writable: true,
613
+ ...descriptors._value,
614
+ value: payload.isDecoded ? payload.encoding.decoder(data) : undefined,
615
+ };
616
+ detachedPayload = Object.create(
617
+ Object.getPrototypeOf(payload),
618
+ descriptors,
619
+ );
620
+ if (!Object.isExtensible(payload)) {
621
+ Object.preventExtensions(detachedPayload);
622
+ }
623
+ return detachedPayload;
624
+ };
625
+ const getPayload = async () => {
626
+ const entryWithPayloadMethod = entry as Entry<T> & {
627
+ getPayload?: () => unknown;
628
+ };
629
+ if (typeof entryWithPayloadMethod.getPayload === "function") {
630
+ return copyPayload(await entryWithPayloadMethod.getPayload.call(entry));
631
+ }
632
+ const materialized = entry.toMaterialized();
633
+ if (materialized !== entry) {
634
+ const materializedWithPayloadMethod = materialized as Entry<T> & {
635
+ getPayload?: () => unknown;
636
+ };
637
+ if (typeof materializedWithPayloadMethod.getPayload === "function") {
638
+ return copyPayload(
639
+ await materializedWithPayloadMethod.getPayload.call(materialized),
640
+ );
641
+ }
642
+ return copyPayload(materialized.payload);
643
+ }
644
+ return copyPayload(entry.payload);
645
+ };
646
+ const getPayloadValue = async () => {
647
+ const payload = await getPayload();
648
+ return payload.isDecoded ? payload.value : payload.getValue();
649
+ };
650
+ callbackEntry = new Proxy(entry, {
651
+ get(target, property) {
652
+ if (property === "payload") {
653
+ return copyPayload(entry.payload);
654
+ }
655
+ if (property === "meta") {
656
+ return getContext().detachMeta(Reflect.get(target, property, target));
657
+ }
658
+ if (property === "next") {
659
+ const next = Reflect.get(target, property, target);
660
+ return Array.isArray(next) ? getContext().detachArray(next) : next;
661
+ }
662
+ if (property === "signatures") {
663
+ return getContext().detachSignatures(
664
+ Reflect.get(target, property, target),
665
+ );
666
+ }
667
+ if (property === "publicKeys") {
668
+ return getContext().detachPublicKeys(
669
+ Reflect.get(target, property, target),
670
+ );
671
+ }
672
+ if (
673
+ property === "getPayload" &&
674
+ typeof (entry as Entry<T> & { getPayload?: unknown }).getPayload ===
675
+ "function"
676
+ ) {
677
+ return getPayload;
678
+ }
679
+ if (property === "getPayloadValue") {
680
+ return getPayloadValue;
681
+ }
682
+ if (property === "getMeta") {
683
+ const getMeta = Reflect.get(target, property, target);
684
+ if (typeof getMeta === "function") {
685
+ return (...args: unknown[]) =>
686
+ mapMaybePromise(
687
+ Reflect.apply(getMeta, target, args),
688
+ getContext().detachMeta,
689
+ );
690
+ }
691
+ }
692
+ if (property === "getClock") {
693
+ const getClock = Reflect.get(target, property, target);
694
+ if (typeof getClock === "function") {
695
+ return (...args: unknown[]) =>
696
+ mapMaybePromise(
697
+ Reflect.apply(getClock, target, args),
698
+ getContext().detachClock,
699
+ );
700
+ }
701
+ }
702
+ if (property === "getNext") {
703
+ const getNext = Reflect.get(target, property, target);
704
+ if (typeof getNext === "function") {
705
+ return (...args: unknown[]) =>
706
+ mapMaybePromise(Reflect.apply(getNext, target, args), (next) =>
707
+ Array.isArray(next) ? getContext().detachArray(next) : next,
708
+ );
709
+ }
710
+ }
711
+ if (property === "getSignatures") {
712
+ const getSignatures = Reflect.get(target, property, target);
713
+ if (typeof getSignatures === "function") {
714
+ return (...args: unknown[]) =>
715
+ mapMaybePromise(
716
+ Reflect.apply(getSignatures, target, args),
717
+ getContext().detachSignatures,
718
+ );
719
+ }
720
+ }
721
+ if (property === "getPublicKeys") {
722
+ const getPublicKeys = Reflect.get(target, property, target);
723
+ if (typeof getPublicKeys === "function") {
724
+ return (...args: unknown[]) =>
725
+ mapMaybePromise(
726
+ Reflect.apply(getPublicKeys, target, args),
727
+ getContext().detachPublicKeys,
728
+ );
729
+ }
730
+ }
731
+ if (property === "getMetaBytes" || property === "getHashDigestBytes") {
732
+ const getBytes = Reflect.get(target, property, target);
733
+ if (typeof getBytes === "function") {
734
+ return (...args: unknown[]) =>
735
+ mapMaybePromise(
736
+ Reflect.apply(getBytes, target, args),
737
+ copyOptionalBytes,
738
+ );
739
+ }
740
+ }
741
+ if (property === "__peerbitNext") {
742
+ const next = Reflect.get(target, property, target);
743
+ return Array.isArray(next) ? getContext().detachArray(next) : next;
744
+ }
745
+ if (property === "getStorageBytes") {
746
+ return () => new Uint8Array(entry.getStorageBytes());
747
+ }
748
+ if (property === "toShallow") {
749
+ const toShallow = Reflect.get(target, property, target);
750
+ if (typeof toShallow === "function") {
751
+ return (...args: unknown[]) => {
752
+ const shallow = Reflect.apply(toShallow, target, args) as unknown;
753
+ return shallow && typeof shallow === "object"
754
+ ? getContext().detachEntryLike(shallow)
755
+ : shallow;
756
+ };
757
+ }
758
+ }
759
+ if (property === "valueOf") {
760
+ return () => callbackEntry;
761
+ }
762
+ if (property === "toMaterialized") {
763
+ return () => {
764
+ const materialized = entry.toMaterialized();
765
+ if (materialized === entry) {
766
+ return callbackEntry;
767
+ }
768
+ return (detachedMaterialized ??=
769
+ detachEntryPayloadForCallbackWithContext(materialized, getContext));
770
+ };
771
+ }
772
+ if (property === "toSignable") {
773
+ return () =>
774
+ detachEntryPayloadForCallbackWithContext(
775
+ entry.toSignable(),
776
+ getContext,
777
+ );
778
+ }
779
+ if (property === "toPreparedAppendJoinFacts") {
780
+ const toPreparedAppendJoinFacts = Reflect.get(target, property, target);
781
+ if (typeof toPreparedAppendJoinFacts === "function") {
782
+ return (...args: unknown[]) => {
783
+ const facts = Reflect.apply(
784
+ toPreparedAppendJoinFacts,
785
+ target,
786
+ args,
787
+ ) as unknown;
788
+ return facts && typeof facts === "object"
789
+ ? detachPreparedAppendJoinFactsForCallback(
790
+ facts,
791
+ target,
792
+ callbackEntry,
793
+ getContext,
794
+ )
795
+ : facts;
796
+ };
797
+ }
798
+ }
799
+ if (property === "init") {
800
+ return (properties: Parameters<Entry<T>["init"]>[0]) => {
801
+ entry.init(properties);
802
+ return callbackEntry;
803
+ };
804
+ }
805
+ const value = Reflect.get(target, property, target);
806
+ if (property === "constructor" || typeof value !== "function") {
807
+ return value;
808
+ }
809
+ const existing = boundMethods.get(property);
810
+ if (existing && existing.source === value) {
811
+ return existing.bound;
812
+ }
813
+ const bound = value.bind(target);
814
+ boundMethods.set(property, { source: value, bound });
815
+ return bound;
816
+ },
817
+ set(target, property, value) {
818
+ return Reflect.set(target, property, value, target);
819
+ },
820
+ });
821
+ detachedCallbackEntries.add(callbackEntry);
822
+ return callbackEntry;
823
+ };
824
+
825
+ export const detachEntryPayloadForCallback = <T>(entry: Entry<T>): Entry<T> => {
826
+ let context: EntryDetachmentContext | undefined;
827
+ return detachEntryPayloadForCallbackWithContext(
828
+ entry,
829
+ () => (context ??= createEntryDetachmentContext()),
830
+ );
831
+ };
832
+
833
+ /** Detach full, shallow, and replicated entries at document callback edges. */
834
+ export const detachEntryForCallback = <T>(entry: T): T => {
835
+ if (!entry || typeof entry !== "object") {
836
+ return entry;
837
+ }
838
+ return (
839
+ entry instanceof Entry
840
+ ? detachEntryPayloadForCallback(entry)
841
+ : createEntryDetachmentContext().detachEntryLike(entry)
842
+ ) as T;
843
+ };
844
+
845
+ /** Give arbitrary document transforms signer objects they cannot retain/mutate. */
846
+ export const detachPublicKeysForCallback = (
847
+ keys: readonly PublicSignKey[],
848
+ ): PublicSignKey[] => createEntryDetachmentContext().detachPublicKeys(keys);