@peerbit/document 15.0.14 → 15.0.16

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/src/program.ts CHANGED
@@ -12,16 +12,6 @@ import {
12
12
  NotFoundError,
13
13
  type ResultIndexedValue,
14
14
  } from "@peerbit/document-interface";
15
- import {
16
- type SimpleDocumentFieldExtractionPlan,
17
- type SimpleDocumentProjectionPlan,
18
- extractDocumentFieldSimple,
19
- initializeDocumentRust,
20
- planDocumentContext,
21
- planDocumentContextBatch,
22
- tryPlanDocumentContext,
23
- tryPlanDocumentContextBatch,
24
- } from "./native-rust.js";
25
15
  import type { QueryCacheOptions } from "@peerbit/indexer-cache";
26
16
  import * as indexerTypes from "@peerbit/indexer-interface";
27
17
  import {
@@ -40,6 +30,7 @@ import { logger as loggerFn } from "@peerbit/logger";
40
30
  import { Program, type ProgramEvents } from "@peerbit/program";
41
31
  import {
42
32
  type EntryReplicated,
33
+ PersistedDeliveryError,
43
34
  type ReplicationDomain,
44
35
  type SharedAppendOptions,
45
36
  SharedLog,
@@ -47,11 +38,22 @@ import {
47
38
  } from "@peerbit/shared-log";
48
39
  import {
49
40
  detachCanPerformCallbackProperties,
41
+ detachEntryForCallback,
50
42
  detachEntryPayloadForCallback,
51
43
  } from "./callback-detachment.js";
52
44
  import { MAX_BATCH_SIZE } from "./constants.js";
53
45
  import type { CustomDocumentDomain } from "./domain.js";
54
46
  import type { DocumentEvents, DocumentsChange } from "./events.js";
47
+ import {
48
+ type SimpleDocumentFieldExtractionPlan,
49
+ type SimpleDocumentProjectionPlan,
50
+ extractDocumentFieldSimple,
51
+ initializeDocumentRust,
52
+ planDocumentContext,
53
+ planDocumentContextBatch,
54
+ tryPlanDocumentContext,
55
+ tryPlanDocumentContextBatch,
56
+ } from "./native-rust.js";
55
57
  import {
56
58
  BORSH_ENCODING_OPERATION,
57
59
  DeleteOperation,
@@ -65,14 +67,14 @@ import {
65
67
  import {
66
68
  type CanPerformPolicyDescriptor,
67
69
  type CanPerformPolicyEvaluator,
68
- createCanPerformPolicyEvaluator,
69
- createCanPerformDeletePolicyEvaluator,
70
- getCanPerformPolicyDescriptor,
71
70
  canPerformPolicyDeleteFieldPaths,
72
71
  canPerformPolicyNeedsDeleteValue,
73
72
  canPerformPolicyNeedsPreviousEntries,
74
73
  canPerformPolicyPutNeedsEntryPublicKeys,
75
74
  canPerformPolicySignedByFieldPaths,
75
+ createCanPerformDeletePolicyEvaluator,
76
+ createCanPerformPolicyEvaluator,
77
+ getCanPerformPolicyDescriptor,
76
78
  } from "./policy.js";
77
79
  import { isResultIndexedValue } from "./result-shape.js";
78
80
  import {
@@ -238,6 +240,73 @@ type DocumentPutOptions = SharedAppendOptions<Operation> & {
238
240
  checkRemote?: boolean;
239
241
  };
240
242
 
243
+ type TrustedLocalCommitEvidence = {
244
+ committedHashes: Set<string>;
245
+ };
246
+
247
+ const hasPersistedDelivery = (
248
+ options: SharedAppendOptions<Operation> | undefined,
249
+ ): boolean =>
250
+ typeof options?.delivery === "object" &&
251
+ options.delivery.reliability === "persisted";
252
+
253
+ const withoutPersistedDelivery = (
254
+ options: DocumentPutOptions | undefined,
255
+ ): DocumentPutOptions | undefined => {
256
+ if (!hasPersistedDelivery(options)) {
257
+ return options;
258
+ }
259
+ return {
260
+ ...options,
261
+ target: "none",
262
+ delivery: false,
263
+ replicate: false,
264
+ } as DocumentPutOptions;
265
+ };
266
+
267
+ const runAfterDocumentCommit = <T>(
268
+ options: DocumentPutOptions | undefined,
269
+ committedHashes: string | Iterable<string> | (() => Iterable<string>),
270
+ fn: () => MaybePromise<T>,
271
+ ): MaybePromise<T> => {
272
+ if (!hasPersistedDelivery(options)) return fn();
273
+ const resolvedHashes =
274
+ typeof committedHashes === "function" ? committedHashes() : committedHashes;
275
+ const hashes =
276
+ typeof resolvedHashes === "string" ? [resolvedHashes] : resolvedHashes;
277
+ try {
278
+ const result = fn();
279
+ if (isPromiseLike(result)) {
280
+ return result.catch((error) => {
281
+ throw new PersistedDeliveryError(error, hashes);
282
+ });
283
+ }
284
+ return result;
285
+ } catch (error) {
286
+ throw new PersistedDeliveryError(error, hashes);
287
+ }
288
+ };
289
+
290
+ const runWithTrustedLocalCommitEvidence = <T>(
291
+ options: DocumentPutOptions | undefined,
292
+ evidence: TrustedLocalCommitEvidence | undefined,
293
+ fn: () => MaybePromise<T>,
294
+ ): MaybePromise<T> => {
295
+ if (!evidence || !hasPersistedDelivery(options)) return fn();
296
+ const classify = (error: unknown): never => {
297
+ if (evidence.committedHashes.size > 0) {
298
+ throw new PersistedDeliveryError(error, evidence.committedHashes);
299
+ }
300
+ throw error;
301
+ };
302
+ try {
303
+ const result = fn();
304
+ return isPromiseLike(result) ? result.catch(classify) : result;
305
+ } catch (error) {
306
+ return classify(error);
307
+ }
308
+ };
309
+
241
310
  const NATIVE_LOCAL_PUT_OPTIONS = Object.freeze({
242
311
  replicate: false,
243
312
  target: "none" as const,
@@ -267,6 +336,8 @@ const cachedNativeLocalPutOptions = (
267
336
  return options.unique === true ? NATIVE_LOCAL_UNIQUE_PUT_OPTIONS : undefined;
268
337
  };
269
338
 
339
+ const persistedDeliveryAlreadySettled = new WeakSet<object>();
340
+
270
341
  type DocumentPutResult = {
271
342
  readonly entry: Entry<Operation>;
272
343
  removed: ShallowOrFullEntry<Operation>[];
@@ -336,17 +407,18 @@ type NativeDocumentBackendContext<T, I extends Record<string, any>> = {
336
407
  commitNativeDocumentAppendMany(
337
408
  input: NativeDocumentAppendManyCommitInput<T, I>,
338
409
  ): MaybePromise<DocumentAppendManyCommitFacts<T, I> | undefined>;
339
- handlePreparedPlainPutCommit(
410
+ finishPreparedPlainPutCommit(
340
411
  commit: NativeDocumentAppendTransaction<T, I>,
341
- ): MaybePromise<void>;
342
- handlePreparedPlainPutManyCommit(
412
+ options: DocumentPutOptions | undefined,
413
+ ): MaybePromise<DocumentPutResult>;
414
+ finishPreparedPlainPutManyCommit(
343
415
  commit: DocumentAppendManyCommitFacts<T, I>,
344
- ): MaybePromise<void>;
416
+ options: DocumentPutOptions | undefined,
417
+ ): MaybePromise<DocumentPutManyResult>;
345
418
  deleteDocument(
346
419
  id: indexerTypes.Ideable | indexerTypes.IdKey,
347
420
  options?: DocumentPutOptions,
348
421
  ): MaybePromise<DocumentDeleteResult>;
349
- keepEntry(hash: string): void;
350
422
  nativeModeError(message: string): NativeDocumentModeError;
351
423
  };
352
424
 
@@ -433,17 +505,9 @@ class NativeDocumentBackend<T, I extends Record<string, any>>
433
505
  existing,
434
506
  }),
435
507
  (documentAppendCommit) =>
436
- mapMaybePromise(
437
- this.context.handlePreparedPlainPutCommit(documentAppendCommit),
438
- () => {
439
- this.context.keepEntry(documentAppendCommit.append.hash);
440
- return {
441
- get entry() {
442
- return documentAppendCommit.entry;
443
- },
444
- removed: documentAppendCommit.removed,
445
- };
446
- },
508
+ this.context.finishPreparedPlainPutCommit(
509
+ documentAppendCommit,
510
+ options,
447
511
  ),
448
512
  );
449
513
  };
@@ -526,6 +590,11 @@ class NativeDocumentBackend<T, I extends Record<string, any>>
526
590
  }
527
591
  const prepared = docs.map((doc) => this.context.preparePlainPut(doc));
528
592
  if (this.context.hasDuplicatePreparedPutKeys(prepared)) {
593
+ if (hasPersistedDelivery(options)) {
594
+ throw this.context.nativeModeError(
595
+ "requires distinct document keys for persisted putMany",
596
+ );
597
+ }
529
598
  const results: DocumentPutResult[] = [];
530
599
  for (const doc of docs) {
531
600
  results.push(await this.put(doc, putOptions));
@@ -603,45 +672,35 @@ class NativeDocumentBackend<T, I extends Record<string, any>>
603
672
  ),
604
673
  ),
605
674
  );
606
- }
607
- return mapMaybePromise(
608
- this.context.commitNativeDocumentAppendMany({
609
- puts: prepared.map((item, index) => ({
610
- document: item.document,
611
- key: item.key,
612
- documentBytes: item.encodedDocument,
613
- operationPayloadBytes: item.operationPayloadBytes,
614
- unique: putOptions?.unique,
615
- requiredPreviousSignerPublicKey,
616
- existing: existingContexts
617
- ? (existingContexts[index] ?? null)
618
- : useNativeExistingDocumentContext
619
- ? undefined
620
- : null,
621
- })),
622
- resolveTrimmedEntries: this.context.shouldResolveTrimmedEntries(),
623
- options: putOptions,
624
- useNativeExistingDocumentContext,
625
- }),
626
- (documentAppendCommit) => {
675
+ }
676
+ return mapMaybePromise(
677
+ this.context.commitNativeDocumentAppendMany({
678
+ puts: prepared.map((item, index) => ({
679
+ document: item.document,
680
+ key: item.key,
681
+ documentBytes: item.encodedDocument,
682
+ operationPayloadBytes: item.operationPayloadBytes,
683
+ unique: putOptions?.unique,
684
+ requiredPreviousSignerPublicKey,
685
+ existing: existingContexts
686
+ ? (existingContexts[index] ?? null)
687
+ : useNativeExistingDocumentContext
688
+ ? undefined
689
+ : null,
690
+ })),
691
+ resolveTrimmedEntries: this.context.shouldResolveTrimmedEntries(),
692
+ options: putOptions,
693
+ useNativeExistingDocumentContext,
694
+ }),
695
+ (documentAppendCommit) => {
627
696
  if (!documentAppendCommit) {
628
697
  throw this.context.nativeModeError(
629
698
  "requires native batched payload append support",
630
699
  );
631
700
  }
632
- return mapMaybePromise(
633
- this.context.handlePreparedPlainPutManyCommit(documentAppendCommit),
634
- () => {
635
- for (const commit of documentAppendCommit.commits) {
636
- this.context.keepEntry(commit.append.hash);
637
- }
638
- return {
639
- get entries() {
640
- return documentAppendCommit.entries;
641
- },
642
- removed: documentAppendCommit.removed,
643
- };
644
- },
701
+ return this.context.finishPreparedPlainPutManyCommit(
702
+ documentAppendCommit,
703
+ options,
645
704
  );
646
705
  },
647
706
  );
@@ -745,6 +804,7 @@ type TrustedDocumentSharedLogAppendProperties = {
745
804
  ) => NativeBackboneDocumentIndexCommitInput | undefined;
746
805
  useNativeExistingDocumentContext?: boolean;
747
806
  nativeBackboneDocumentDeleteKey?: string;
807
+ localCommitEvidence?: TrustedLocalCommitEvidence;
748
808
  };
749
809
 
750
810
  type TrustedDocumentSharedLogAppendManyProperties = {
@@ -752,6 +812,7 @@ type TrustedDocumentSharedLogAppendManyProperties = {
752
812
  nexts?: ShallowOrFullEntry<Operation>[][];
753
813
  nativeBackboneDocumentIndexes?: NativeBackboneDocumentIndexCommitInput[];
754
814
  retainMaterializationBytes?: boolean;
815
+ localCommitEvidence?: TrustedLocalCommitEvidence;
755
816
  };
756
817
 
757
818
  type TrustedDocumentSharedLogAppendManyResult = {
@@ -761,8 +822,33 @@ type TrustedDocumentSharedLogAppendManyResult = {
761
822
  appendCommits: LocalAppendCommitFacts[];
762
823
  };
763
824
 
825
+ type PersistedDocumentAppendDelivery = {
826
+ appendCommits: LocalAppendCommitFacts[];
827
+ materializeEntries: () => Entry<Operation>[];
828
+ };
829
+
830
+ // Keep the public putMany result shape unchanged while carrying the native
831
+ // commit/coordinate facts to the internal persisted-delivery seam. The result
832
+ // remains the lifetime owner of the lazy full-entry materializer.
833
+ const persistedDocumentAppendDelivery = new WeakMap<
834
+ object,
835
+ PersistedDocumentAppendDelivery
836
+ >();
837
+
764
838
  type TrustedDocumentSharedLog = {
765
839
  finishNativeStrictDurableDocumentRecovery(): Promise<void>;
840
+ assertPersistedDeliveryOptions(
841
+ options?: SharedAppendOptions<Operation>,
842
+ ): void;
843
+ deliverPersistedEntries(
844
+ entries: Entry<Operation>[],
845
+ options: SharedAppendOptions<Operation>,
846
+ ): Promise<void>;
847
+ deliverPersistedAppendCommits(
848
+ appendCommits: LocalAppendCommitFacts[],
849
+ materializeEntries: () => Entry<Operation>[],
850
+ options: SharedAppendOptions<Operation>,
851
+ ): Promise<void>;
766
852
  appendLocallyPrepared(
767
853
  data: Operation,
768
854
  options?: SharedAppendOptions<Operation>,
@@ -995,8 +1081,7 @@ const asTrustedDocumentIndex = <
995
1081
  D extends ReplicationDomain<any, Operation, any>,
996
1082
  >(
997
1083
  index: DocumentIndex<T, I, D>,
998
- ): TrustedDocumentIndex<T, I> =>
999
- index as unknown as TrustedDocumentIndex<T, I>;
1084
+ ): TrustedDocumentIndex<T, I> => index as unknown as TrustedDocumentIndex<T, I>;
1000
1085
 
1001
1086
  type NativeDocumentAppendCommitInput<
1002
1087
  T,
@@ -1162,11 +1247,7 @@ export class Documents<
1162
1247
  "fromEntry",
1163
1248
  domain,
1164
1249
  ) as CustomDocumentDomain<InferR<D>>["fromEntry"];
1165
- return Reflect.apply(fromEntry, domain, [
1166
- entry instanceof Entry
1167
- ? detachEntryPayloadForCallback(entry)
1168
- : entry,
1169
- ]);
1250
+ return Reflect.apply(fromEntry, domain, [detachEntryForCallback(entry)]);
1170
1251
  };
1171
1252
  const facade = Object.create(
1172
1253
  Object.getPrototypeOf(domain),
@@ -1225,6 +1306,25 @@ export class Documents<
1225
1306
  return callbackDomain;
1226
1307
  }
1227
1308
 
1309
+ private detachTrimEntryCallback(
1310
+ option: TrimOptions | undefined,
1311
+ ): TrimOptions | undefined {
1312
+ const filter = option?.filter;
1313
+ if (!option || !filter?.canTrim) {
1314
+ return option;
1315
+ }
1316
+ const canTrim = filter.canTrim;
1317
+ return {
1318
+ ...option,
1319
+ filter: {
1320
+ ...filter,
1321
+ canTrim: function (this: unknown, entry: ShallowEntry) {
1322
+ return Reflect.apply(canTrim, this, [detachEntryForCallback(entry)]);
1323
+ },
1324
+ },
1325
+ } as TrimOptions;
1326
+ }
1327
+
1228
1328
  private createNativeDocumentBackendContext(): NativeDocumentBackendContext<
1229
1329
  T,
1230
1330
  I
@@ -1272,15 +1372,12 @@ export class Documents<
1272
1372
  this.commitNativeDocumentAppend(input),
1273
1373
  commitNativeDocumentAppendMany: (input) =>
1274
1374
  this.commitNativeDocumentAppendMany(input),
1275
- handlePreparedPlainPutCommit: (commit) =>
1276
- this.handlePreparedPlainPutCommit(commit),
1277
- handlePreparedPlainPutManyCommit: (commit) =>
1278
- this.handlePreparedPlainPutManyCommit(commit),
1375
+ finishPreparedPlainPutCommit: (commit, options) =>
1376
+ this.finishPreparedPlainPutCommit(commit, options),
1377
+ finishPreparedPlainPutManyCommit: (commit, options) =>
1378
+ this.finishPreparedPlainPutManyCommit(commit, options),
1279
1379
  deleteDocument: (id, options) =>
1280
1380
  this.delNativeDocumentBackend(id, options),
1281
- keepEntry: (hash) => {
1282
- this.keepCache?.add(hash);
1283
- },
1284
1381
  nativeModeError: (message) => this.nativeModeError(message),
1285
1382
  };
1286
1383
  }
@@ -1312,10 +1409,7 @@ export class Documents<
1312
1409
  const nativeIndexTransformDescriptor =
1313
1410
  typeof indexTransform?.transform === "function"
1314
1411
  ? getDocumentTransformDescriptor(
1315
- indexTransform.transform as DocumentTransformer<
1316
- unknown,
1317
- unknown
1318
- >,
1412
+ indexTransform.transform as DocumentTransformer<unknown, unknown>,
1319
1413
  )
1320
1414
  : undefined;
1321
1415
 
@@ -1421,7 +1515,9 @@ export class Documents<
1421
1515
  );
1422
1516
  }
1423
1517
  if (
1424
- !asTrustedDocumentIndex(this._index).canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts()
1518
+ !asTrustedDocumentIndex(
1519
+ this._index,
1520
+ ).canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts()
1425
1521
  ) {
1426
1522
  throw this.nativeModeError(
1427
1523
  "requires a native-compatible document index transform",
@@ -1470,9 +1566,7 @@ export class Documents<
1470
1566
  private nativePlainPutPolicyNeedsPreviousEntries(): boolean {
1471
1567
  return (
1472
1568
  !!this._optionCanPerformNativePolicy &&
1473
- canPerformPolicyNeedsPreviousEntries(
1474
- this._optionCanPerformNativePolicy,
1475
- )
1569
+ canPerformPolicyNeedsPreviousEntries(this._optionCanPerformNativePolicy)
1476
1570
  );
1477
1571
  }
1478
1572
 
@@ -1535,10 +1629,18 @@ export class Documents<
1535
1629
  if (options?.replicate === true) {
1536
1630
  unsupported.push("replicated put");
1537
1631
  }
1538
- if (options?.target && options.target !== "none") {
1632
+ if (
1633
+ options?.target &&
1634
+ options.target !== "none" &&
1635
+ !(hasPersistedDelivery(options) && options.target === "replicators")
1636
+ ) {
1539
1637
  unsupported.push("non-local target");
1540
1638
  }
1541
- if (options?.delivery !== undefined && options.delivery !== false) {
1639
+ if (
1640
+ options?.delivery !== undefined &&
1641
+ options.delivery !== false &&
1642
+ !hasPersistedDelivery(options)
1643
+ ) {
1542
1644
  unsupported.push("delivery");
1543
1645
  }
1544
1646
  if (options?.checkRemote) {
@@ -1669,9 +1771,8 @@ export class Documents<
1669
1771
  value: unknown,
1670
1772
  publicKey: PublicSignKey,
1671
1773
  ): boolean {
1672
- const localRawPublicKey = (
1673
- publicKey as { publicKey?: Uint8Array }
1674
- ).publicKey;
1774
+ const localRawPublicKey = (publicKey as { publicKey?: Uint8Array })
1775
+ .publicKey;
1675
1776
  return (
1676
1777
  value instanceof Uint8Array &&
1677
1778
  (bytesEqual(value, publicKey.bytes) ||
@@ -1913,7 +2014,9 @@ export class Documents<
1913
2014
  if (Program.isPrototypeOf(this._clazz)) {
1914
2015
  unsupported.push("program-valued document type");
1915
2016
  }
1916
- if (!asTrustedDocumentIndex(this._index).canUseNativeBackboneContextualBatch()) {
2017
+ if (
2018
+ !asTrustedDocumentIndex(this._index).canUseNativeBackboneContextualBatch()
2019
+ ) {
1917
2020
  unsupported.push("native batch document index");
1918
2021
  }
1919
2022
  if (unsupported.length > 0) {
@@ -1926,6 +2029,9 @@ export class Documents<
1926
2029
  return;
1927
2030
  }
1928
2031
  const unsupported = this.unsupportedNativePutOptions(options);
2032
+ if (options && hasPersistedDelivery(options)) {
2033
+ unsupported.push("delivery");
2034
+ }
1929
2035
  if (options?.unique !== undefined) {
1930
2036
  unsupported.push("unique delete");
1931
2037
  }
@@ -1961,17 +2067,15 @@ export class Documents<
1961
2067
  );
1962
2068
  }
1963
2069
  let deleteValue: T | undefined;
1964
- if (
1965
- canPerformPolicyNeedsDeleteValue(
1966
- this._optionCanPerformNativePolicy,
1967
- )
1968
- ) {
2070
+ if (canPerformPolicyNeedsDeleteValue(this._optionCanPerformNativePolicy)) {
1969
2071
  deleteValue = await properties.getExistingDocument?.();
1970
2072
  if (deleteValue === undefined) {
1971
2073
  const existingEntry = await properties.getExistingEntry();
1972
2074
  const existingOperation = await existingEntry.getPayloadValue();
1973
2075
  if (isPutOperation(existingOperation)) {
1974
- deleteValue = this._index.valueEncoding.decoder(existingOperation.data);
2076
+ deleteValue = this._index.valueEncoding.decoder(
2077
+ existingOperation.data,
2078
+ );
1975
2079
  }
1976
2080
  }
1977
2081
  }
@@ -1987,6 +2091,9 @@ export class Documents<
1987
2091
  if (!this.isNativeMode()) {
1988
2092
  return options;
1989
2093
  }
2094
+ if (hasPersistedDelivery(options)) {
2095
+ return options;
2096
+ }
1990
2097
  if (options?.replicate === false && options.target === "none") {
1991
2098
  return options;
1992
2099
  }
@@ -2129,9 +2236,11 @@ export class Documents<
2129
2236
  | indexerTypes.IndexedResult<IndexedContextOnly<I>>
2130
2237
  | undefined;
2131
2238
  try {
2132
- const result = (orderedSession
2133
- ? orderedSession.get(key, { shape: INDEX_CONTEXT_SHAPE })
2134
- : this.getLocalIndexedContext(key)) as MaybePromise<LocalIndexedContext>;
2239
+ const result = (
2240
+ orderedSession
2241
+ ? orderedSession.get(key, { shape: INDEX_CONTEXT_SHAPE })
2242
+ : this.getLocalIndexedContext(key)
2243
+ ) as MaybePromise<LocalIndexedContext>;
2135
2244
  return (
2136
2245
  isPromiseLike(result)
2137
2246
  ? result.catch((error) => this.recoverClosedIndexContextRead(error))
@@ -2204,9 +2313,7 @@ export class Documents<
2204
2313
 
2205
2314
  private getNativeIndexedContext(
2206
2315
  key: indexerTypes.IdKey,
2207
- ):
2208
- | indexerTypes.IndexedResult<IndexedContextOnly<I>>
2209
- | undefined {
2316
+ ): indexerTypes.IndexedResult<IndexedContextOnly<I>> | undefined {
2210
2317
  const nativeBackbone = this.getSharedLogNativeBackbone<
2211
2318
  | {
2212
2319
  documentContext?: (
@@ -2269,9 +2376,7 @@ export class Documents<
2269
2376
  }
2270
2377
  const nativeBackbone = this.getSharedLogNativeBackbone<
2271
2378
  | {
2272
- documentContextsAndPreviousSignaturePublicKeys?: (
2273
- keys: string[],
2274
- ) =>
2379
+ documentContextsAndPreviousSignaturePublicKeys?: (keys: string[]) =>
2275
2380
  | Array<{
2276
2381
  context?: {
2277
2382
  created: bigint;
@@ -2299,9 +2404,7 @@ export class Documents<
2299
2404
  ? {
2300
2405
  id: keys[index]!,
2301
2406
  value: {
2302
- __context: nativeDocumentContextFactsAsContext(
2303
- row.context,
2304
- ),
2407
+ __context: nativeDocumentContextFactsAsContext(row.context),
2305
2408
  } as IndexedContextOnly<I>,
2306
2409
  }
2307
2410
  : undefined,
@@ -2492,8 +2595,9 @@ export class Documents<
2492
2595
  );
2493
2596
  this._nativeDocumentFieldExtractionPlans ??= new Map();
2494
2597
  this._nativeDocumentFieldExtractionPlans.clear();
2495
- this._nativeDocumentIdExtractionPlan =
2496
- asTrustedDocumentIndex(this._index).getNativeDocumentFieldExtractionPlan(idProperty);
2598
+ this._nativeDocumentIdExtractionPlan = asTrustedDocumentIndex(
2599
+ this._index,
2600
+ ).getNativeDocumentFieldExtractionPlan(idProperty);
2497
2601
 
2498
2602
  // B12: the historical document->log compatibility mapping (6 -> log v8,
2499
2603
  // 7 -> log v9) is retired; the rejection at the top of open() fires for
@@ -2549,11 +2653,7 @@ export class Documents<
2549
2653
  const keep = options?.keep;
2550
2654
  keepFunction = keep
2551
2655
  ? function (this: unknown, entry) {
2552
- return Reflect.apply(keep, this, [
2553
- entry instanceof Entry
2554
- ? detachEntryPayloadForCallback(entry)
2555
- : entry,
2556
- ]);
2656
+ return Reflect.apply(keep, this, [detachEntryForCallback(entry)]);
2557
2657
  }
2558
2658
  : undefined;
2559
2659
  }
@@ -2563,7 +2663,7 @@ export class Documents<
2563
2663
  canReplicate: options?.canReplicate,
2564
2664
  canAppend: this.canAppend.bind(this),
2565
2665
  onChange: this.handleChanges.bind(this),
2566
- trim: options?.log?.trim,
2666
+ trim: this.detachTrimEntryCallback(options?.log?.trim),
2567
2667
  appendDurability: options?.appendDurability,
2568
2668
  nativeBackbone: options?.nativeBackbone,
2569
2669
  nativeGraph: options?.nativeGraph,
@@ -2714,9 +2814,7 @@ export class Documents<
2714
2814
  const deleteValue =
2715
2815
  this._optionCanPerformNativePolicy &&
2716
2816
  isDeleteOperation(operation) &&
2717
- canPerformPolicyNeedsDeleteValue(
2718
- this._optionCanPerformNativePolicy,
2719
- )
2817
+ canPerformPolicyNeedsDeleteValue(this._optionCanPerformNativePolicy)
2720
2818
  ? await this.resolveCanPerformDeleteValue(operation)
2721
2819
  : undefined;
2722
2820
  if (
@@ -2932,7 +3030,8 @@ export class Documents<
2932
3030
  : reference.document,
2933
3031
  );
2934
3032
  } else {
2935
- keyValue = await this.getNativeDocumentIdFromPutOperation(putOperation);
3033
+ keyValue =
3034
+ await this.getNativeDocumentIdFromPutOperation(putOperation);
2936
3035
  if (keyValue == null) {
2937
3036
  if (this.isNativeMode()) {
2938
3037
  return false;
@@ -3085,7 +3184,6 @@ export class Documents<
3085
3184
  } else {
3086
3185
  throw new Error("Unsupported operation");
3087
3186
  }
3088
-
3089
3187
  } catch (error) {
3090
3188
  if (error instanceof AccessError) {
3091
3189
  return false; // we cant index because we can not decrypt
@@ -3191,7 +3289,9 @@ export class Documents<
3191
3289
  if (plans.has(key)) {
3192
3290
  return plans.get(key);
3193
3291
  }
3194
- const plan = asTrustedDocumentIndex(this._index).getNativeDocumentFieldExtractionPlan(path);
3292
+ const plan = asTrustedDocumentIndex(
3293
+ this._index,
3294
+ ).getNativeDocumentFieldExtractionPlan(path);
3195
3295
  plans.set(key, plan);
3196
3296
  return plan;
3197
3297
  }
@@ -3289,7 +3389,47 @@ export class Documents<
3289
3389
  doc: T,
3290
3390
  options?: DocumentPutOptions,
3291
3391
  ): Promise<DocumentPutResult> {
3292
- return this._documentBackend.put(doc, options);
3392
+ if (hasPersistedDelivery(options)) {
3393
+ asTrustedDocumentSharedLog(this.log).assertPersistedDeliveryOptions(
3394
+ options,
3395
+ );
3396
+ }
3397
+ const result = await this._documentBackend.put(doc, options);
3398
+ if (
3399
+ !hasPersistedDelivery(options) ||
3400
+ persistedDeliveryAlreadySettled.has(result)
3401
+ ) {
3402
+ return result;
3403
+ }
3404
+ const entry = result.entry;
3405
+ await this.deliverPersistedDocumentEntries([entry], options!);
3406
+ return {
3407
+ get entry() {
3408
+ return entry;
3409
+ },
3410
+ removed: result.removed,
3411
+ };
3412
+ }
3413
+
3414
+ private deliverPersistedDocumentEntries(
3415
+ entries: Entry<Operation>[],
3416
+ options: DocumentPutOptions,
3417
+ ): Promise<void> {
3418
+ return asTrustedDocumentSharedLog(this.log).deliverPersistedEntries(
3419
+ entries,
3420
+ options,
3421
+ );
3422
+ }
3423
+
3424
+ private deliverPersistedDocumentAppendCommits(
3425
+ delivery: PersistedDocumentAppendDelivery,
3426
+ options: DocumentPutOptions,
3427
+ ): Promise<void> {
3428
+ return asTrustedDocumentSharedLog(this.log).deliverPersistedAppendCommits(
3429
+ delivery.appendCommits,
3430
+ delivery.materializeEntries,
3431
+ options,
3432
+ );
3293
3433
  }
3294
3434
 
3295
3435
  private async putCompatDocumentBackend(
@@ -3335,6 +3475,7 @@ export class Documents<
3335
3475
  "operation" in prepared
3336
3476
  ? prepared.operation
3337
3477
  : new PutOperation({ data: prepared.encodedDocument });
3478
+ const persistedDelivery = hasPersistedDelivery(putOptions);
3338
3479
  const appended = await this.log.append(operation, {
3339
3480
  ...putOptions,
3340
3481
  meta: {
@@ -3347,18 +3488,39 @@ export class Documents<
3347
3488
  operation,
3348
3489
  });
3349
3490
  },
3350
- onChange: (change) => {
3351
- return this.handleChanges(change, {
3352
- document: prepared.document,
3353
- operation,
3354
- key: prepared.key,
3355
- unique: putOptions?.unique,
3356
- existing: existingLocalContext,
3357
- });
3358
- },
3491
+ onChange: persistedDelivery
3492
+ ? (change) =>
3493
+ runAfterDocumentCommit(
3494
+ putOptions,
3495
+ () => change.added.map(({ entry }) => entry.hash),
3496
+ () =>
3497
+ mapMaybePromise(
3498
+ this.handleChanges(change, {
3499
+ document: prepared.document,
3500
+ operation,
3501
+ key: prepared.key,
3502
+ unique: putOptions?.unique,
3503
+ existing: existingLocalContext,
3504
+ }),
3505
+ () => {
3506
+ this.keepCache?.add(change.added[0]!.entry.hash);
3507
+ },
3508
+ ),
3509
+ )
3510
+ : (change) =>
3511
+ this.handleChanges(change, {
3512
+ document: prepared.document,
3513
+ operation,
3514
+ key: prepared.key,
3515
+ unique: putOptions?.unique,
3516
+ existing: existingLocalContext,
3517
+ }),
3359
3518
  replicate: putOptions?.replicate,
3360
3519
  });
3361
3520
  this.keepCache?.add(appended.entry.hash);
3521
+ if (persistedDelivery) {
3522
+ persistedDeliveryAlreadySettled.add(appended);
3523
+ }
3362
3524
  return appended;
3363
3525
  }
3364
3526
 
@@ -3366,7 +3528,36 @@ export class Documents<
3366
3528
  docs: T[],
3367
3529
  options?: DocumentPutOptions,
3368
3530
  ): Promise<DocumentPutManyResult> {
3369
- return this._documentBackend.putMany(docs, options);
3531
+ if (hasPersistedDelivery(options)) {
3532
+ asTrustedDocumentSharedLog(this.log).assertPersistedDeliveryOptions(
3533
+ options,
3534
+ );
3535
+ }
3536
+ const result = await this._documentBackend.putMany(docs, options);
3537
+ if (!hasPersistedDelivery(options)) {
3538
+ return result;
3539
+ }
3540
+ const appendDelivery = persistedDocumentAppendDelivery.get(result);
3541
+ if (appendDelivery) {
3542
+ persistedDocumentAppendDelivery.delete(result);
3543
+ await this.deliverPersistedDocumentAppendCommits(
3544
+ appendDelivery,
3545
+ options!,
3546
+ );
3547
+ let entries: Entry<Operation>[] | undefined;
3548
+ return {
3549
+ get entries() {
3550
+ return (entries ??= result.entries);
3551
+ },
3552
+ removed: result.removed,
3553
+ };
3554
+ }
3555
+ const entries = result.entries;
3556
+ if (entries.length === 0) {
3557
+ return result;
3558
+ }
3559
+ await this.deliverPersistedDocumentEntries(entries, options!);
3560
+ return { entries, removed: result.removed };
3370
3561
  }
3371
3562
 
3372
3563
  private async putManyCompatDocumentBackend(
@@ -3377,11 +3568,19 @@ export class Documents<
3377
3568
  return { entries: [], removed: [] };
3378
3569
  }
3379
3570
  if (!this.canUsePlainPutManyFastPath(docs, options)) {
3571
+ if (hasPersistedDelivery(options)) {
3572
+ throw new Error(
3573
+ "persisted putMany requires the independent batched document path",
3574
+ );
3575
+ }
3380
3576
  return this.putManySequential(docs, options);
3381
3577
  }
3382
3578
 
3383
3579
  const prepared = docs.map((doc) => this.preparePlainPut(doc));
3384
3580
  if (this.hasDuplicatePreparedPutKeys(prepared)) {
3581
+ if (hasPersistedDelivery(options)) {
3582
+ throw new Error("persisted putMany requires distinct document keys");
3583
+ }
3385
3584
  return this.putManySequential(docs, options);
3386
3585
  }
3387
3586
 
@@ -3398,19 +3597,17 @@ export class Documents<
3398
3597
  options,
3399
3598
  });
3400
3599
  if (!documentAppendCommit) {
3600
+ if (hasPersistedDelivery(options)) {
3601
+ throw new Error(
3602
+ "persisted putMany requires native batched payload append support",
3603
+ );
3604
+ }
3401
3605
  return this.putManySequential(docs, options);
3402
3606
  }
3403
-
3404
- await this.handlePreparedPlainPutManyCommit(documentAppendCommit);
3405
- for (const commit of documentAppendCommit.commits) {
3406
- this.keepCache?.add(commit.append.hash);
3407
- }
3408
- return {
3409
- get entries() {
3410
- return documentAppendCommit.entries;
3411
- },
3412
- removed: documentAppendCommit.removed,
3413
- };
3607
+ return await this.finishPreparedPlainPutManyCommit(
3608
+ documentAppendCommit,
3609
+ options,
3610
+ );
3414
3611
  }
3415
3612
 
3416
3613
  private async putManySequential(
@@ -3447,6 +3644,7 @@ export class Documents<
3447
3644
  doc: T,
3448
3645
  options?: DocumentPutOptions,
3449
3646
  ): boolean {
3647
+ const persistedDelivery = hasPersistedDelivery(options);
3450
3648
  return (
3451
3649
  this._mode !== "compat" &&
3452
3650
  this.canPerformAllowsPlainPutFastPath(doc) &&
@@ -3467,8 +3665,12 @@ export class Documents<
3467
3665
  !options?.meta?.timestamp &&
3468
3666
  !options?.meta?.gidSeed &&
3469
3667
  options?.replicate !== true &&
3470
- (!options?.target || options.target === "none") &&
3471
- (options?.delivery === undefined || options.delivery === false) &&
3668
+ (!options?.target ||
3669
+ options.target === "none" ||
3670
+ (persistedDelivery && options.target === "replicators")) &&
3671
+ (options?.delivery === undefined ||
3672
+ options.delivery === false ||
3673
+ persistedDelivery) &&
3472
3674
  !options?.checkRemote &&
3473
3675
  options?.replicas === undefined
3474
3676
  );
@@ -3478,11 +3680,17 @@ export class Documents<
3478
3680
  docs: T[],
3479
3681
  options?: DocumentPutOptions,
3480
3682
  ): boolean {
3683
+ const persistedDelivery = hasPersistedDelivery(options);
3481
3684
  return (
3482
3685
  options?.unique === true &&
3483
3686
  options?.replicate !== true &&
3484
- options?.target === "none" &&
3485
- (options?.delivery === undefined || options.delivery === false) &&
3687
+ (options?.target === "none" ||
3688
+ (persistedDelivery &&
3689
+ (options.target === undefined ||
3690
+ options.target === "replicators"))) &&
3691
+ (options?.delivery === undefined ||
3692
+ options.delivery === false ||
3693
+ persistedDelivery) &&
3486
3694
  docs.every((doc) => this.canUsePlainPutFastPath(doc, options))
3487
3695
  );
3488
3696
  }
@@ -3555,49 +3763,63 @@ export class Documents<
3555
3763
  unique: plan.unique,
3556
3764
  existing: plan.existing,
3557
3765
  }),
3558
- (documentAppendCommit) => {
3559
- const handled = plan.useGenericChangeHandler
3560
- ? this.handleChanges(
3561
- {
3562
- added: [{ head: true, entry: documentAppendCommit.entry }],
3563
- removed: documentAppendCommit.removed,
3564
- },
3565
- {
3566
- document: plan.document,
3567
- operation:
3568
- documentAppendCommit.operation ??
3569
- plan.operation ??
3570
- new PutOperation({ data: plan.encodedDocument }),
3571
- key: plan.key,
3572
- unique: plan.unique,
3573
- existing: plan.existing,
3574
- },
3575
- )
3576
- : this.handlePreparedPlainPutCommit(documentAppendCommit);
3577
- return mapMaybePromise(handled, () => {
3578
- this.keepCache?.add(documentAppendCommit.append.hash);
3579
- return {
3580
- get entry() {
3581
- return documentAppendCommit.entry;
3582
- },
3583
- removed: documentAppendCommit.removed,
3584
- };
3585
- });
3586
- },
3766
+ (documentAppendCommit) =>
3767
+ this.finishPreparedPlainPutCommit(documentAppendCommit, options, () =>
3768
+ plan.useGenericChangeHandler
3769
+ ? this.handleChanges(
3770
+ {
3771
+ added: [
3772
+ {
3773
+ head: true,
3774
+ entry: documentAppendCommit.entry,
3775
+ },
3776
+ ],
3777
+ removed: documentAppendCommit.removed,
3778
+ },
3779
+ {
3780
+ document: plan.document,
3781
+ operation:
3782
+ documentAppendCommit.operation ??
3783
+ plan.operation ??
3784
+ new PutOperation({ data: plan.encodedDocument }),
3785
+ key: plan.key,
3786
+ unique: plan.unique,
3787
+ existing: plan.existing,
3788
+ },
3789
+ )
3790
+ : this.handlePreparedPlainPutCommit(documentAppendCommit),
3791
+ ),
3587
3792
  );
3588
3793
  }
3589
3794
 
3590
3795
  private commitNativeDocumentAppend(
3591
3796
  input: NativeDocumentAppendCommitInput<T, I>,
3797
+ ): MaybePromise<NativeDocumentAppendTransaction<T, I>> {
3798
+ if (!hasPersistedDelivery(input.options)) {
3799
+ return this.commitNativeDocumentAppendWithEvidence(input, undefined);
3800
+ }
3801
+ const localCommitEvidence = { committedHashes: new Set<string>() };
3802
+ return runWithTrustedLocalCommitEvidence(
3803
+ input.options,
3804
+ localCommitEvidence,
3805
+ () =>
3806
+ this.commitNativeDocumentAppendWithEvidence(input, localCommitEvidence),
3807
+ );
3808
+ }
3809
+
3810
+ private commitNativeDocumentAppendWithEvidence(
3811
+ input: NativeDocumentAppendCommitInput<T, I>,
3812
+ localCommitEvidence: TrustedLocalCommitEvidence | undefined,
3592
3813
  ): MaybePromise<NativeDocumentAppendTransaction<T, I>> {
3593
3814
  const trustedLog = asTrustedDocumentSharedLog(this.log);
3815
+ const localAppendOptions = withoutPersistedDelivery(input.options);
3594
3816
  const appendOptions = {
3595
- ...input.options,
3817
+ ...localAppendOptions,
3596
3818
  meta: {
3597
3819
  next: input.next,
3598
- ...input.options?.meta,
3820
+ ...localAppendOptions?.meta,
3599
3821
  },
3600
- replicate: input.options?.replicate,
3822
+ replicate: localAppendOptions?.replicate,
3601
3823
  };
3602
3824
  const prepareNativeDocumentIndexWithAppendFacts =
3603
3825
  this.createNativeBackboneDocumentIndexAppendFactsPreparer(input);
@@ -3626,6 +3848,7 @@ export class Documents<
3626
3848
  payloadData: input.operationPayloadBytes,
3627
3849
  useNativeExistingDocumentContext:
3628
3850
  input.useNativeExistingDocumentContext,
3851
+ ...(localCommitEvidence ? { localCommitEvidence } : undefined),
3629
3852
  ...(nativeDocumentIndexCommit
3630
3853
  ? {
3631
3854
  nativeBackboneDocumentIndex:
@@ -3665,10 +3888,15 @@ export class Documents<
3665
3888
  appendProperties,
3666
3889
  ),
3667
3890
  (appended) =>
3668
- this.createNativeCheckedDocumentAppendCommitFacts(
3669
- input,
3670
- appended,
3671
- committedNativeDocumentIndex,
3891
+ runAfterDocumentCommit(
3892
+ input.options,
3893
+ appended.appendCommit.hash,
3894
+ () =>
3895
+ this.createNativeCheckedDocumentAppendCommitFacts(
3896
+ input,
3897
+ appended,
3898
+ committedNativeDocumentIndex,
3899
+ ),
3672
3900
  ),
3673
3901
  );
3674
3902
  }
@@ -3683,29 +3911,31 @@ export class Documents<
3683
3911
  appendOptions,
3684
3912
  appendProperties,
3685
3913
  );
3686
- return mapMaybePromise(
3687
- commitOnlyAppend,
3688
- (commitOnly) => {
3689
- if (commitOnly) {
3690
- return this.createNativeCheckedDocumentAppendCommitFacts(
3691
- input,
3692
- commitOnly,
3693
- committedNativeDocumentIndex,
3694
- );
3695
- }
3696
- if (this.isNativeMode()) {
3697
- throw this.nativeModeError(
3698
- "requires native payload commit-only append",
3699
- );
3700
- }
3701
- return this.commitNativeDocumentAppendPayloadFallback(
3702
- input,
3703
- appendOptions,
3704
- appendProperties,
3705
- committedNativeDocumentIndex,
3914
+ return mapMaybePromise(commitOnlyAppend, (commitOnly) => {
3915
+ if (commitOnly) {
3916
+ return runAfterDocumentCommit(
3917
+ input.options,
3918
+ commitOnly.appendCommit.hash,
3919
+ () =>
3920
+ this.createNativeCheckedDocumentAppendCommitFacts(
3921
+ input,
3922
+ commitOnly,
3923
+ committedNativeDocumentIndex,
3924
+ ),
3706
3925
  );
3707
- },
3708
- );
3926
+ }
3927
+ if (this.isNativeMode()) {
3928
+ throw this.nativeModeError(
3929
+ "requires native payload commit-only append",
3930
+ );
3931
+ }
3932
+ return this.commitNativeDocumentAppendPayloadFallback(
3933
+ input,
3934
+ appendOptions,
3935
+ appendProperties,
3936
+ committedNativeDocumentIndex,
3937
+ );
3938
+ });
3709
3939
  },
3710
3940
  );
3711
3941
  }
@@ -3743,7 +3973,7 @@ export class Documents<
3743
3973
  input: NativeDocumentAppendCommitFactsInput<T, I>,
3744
3974
  commit: PreparedNativeBackboneDocumentIndexCommit<I>,
3745
3975
  useLatestContext = false,
3746
- ): NativeBackboneDocumentIndexCommitInput {
3976
+ ): NativeBackboneDocumentIndexCommitInput {
3747
3977
  const canUsePlainPutPayload =
3748
3978
  commit.usePlainPutPayload === true ||
3749
3979
  (!!input.operationPayloadBytes && !!commit.projection);
@@ -3760,8 +3990,7 @@ export class Documents<
3760
3990
  !this.hasDocumentChangeConsumers() &&
3761
3991
  this._index.canGetIndexedKeyByHead(),
3762
3992
  useLatestContext,
3763
- requiredPreviousSignerPublicKey:
3764
- input.requiredPreviousSignerPublicKey,
3993
+ requiredPreviousSignerPublicKey: input.requiredPreviousSignerPublicKey,
3765
3994
  };
3766
3995
  }
3767
3996
 
@@ -3771,7 +4000,9 @@ export class Documents<
3771
4000
  if (!this._nativeBackboneDocumentIndexEnabled) {
3772
4001
  return;
3773
4002
  }
3774
- return asTrustedDocumentIndex(this._index).prepareNativeBackboneDocumentIndexCommit(
4003
+ return asTrustedDocumentIndex(
4004
+ this._index,
4005
+ ).prepareNativeBackboneDocumentIndexCommit(
3775
4006
  input.document,
3776
4007
  input.documentBytes,
3777
4008
  { entryPublicKeys: [this.log.log.identity.publicKey] },
@@ -3787,7 +4018,9 @@ export class Documents<
3787
4018
  | undefined {
3788
4019
  if (
3789
4020
  !this._nativeBackboneDocumentIndexEnabled ||
3790
- !asTrustedDocumentIndex(this._index).canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts()
4021
+ !asTrustedDocumentIndex(
4022
+ this._index,
4023
+ ).canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts()
3791
4024
  ) {
3792
4025
  return;
3793
4026
  }
@@ -3806,7 +4039,9 @@ export class Documents<
3806
4039
  gid: appendFacts.gid,
3807
4040
  size: appendFacts.payloadSize,
3808
4041
  });
3809
- return asTrustedDocumentIndex(this._index).prepareNativeBackboneDocumentIndexCommitWithAppendFacts(
4042
+ return asTrustedDocumentIndex(
4043
+ this._index,
4044
+ ).prepareNativeBackboneDocumentIndexCommitWithAppendFacts(
3810
4045
  input.document,
3811
4046
  input.documentBytes,
3812
4047
  context,
@@ -3822,6 +4057,7 @@ export class Documents<
3822
4057
  skipMissingNextJoin: boolean;
3823
4058
  resolveTrimmedEntries: boolean;
3824
4059
  payloadData: Uint8Array;
4060
+ localCommitEvidence?: TrustedLocalCommitEvidence;
3825
4061
  prepareNativeBackboneDocumentIndex?: (
3826
4062
  facts: NativeBackboneDocumentIndexAppendFactsInput,
3827
4063
  ) => NativeBackboneDocumentIndexCommitInput | undefined;
@@ -3841,6 +4077,7 @@ export class Documents<
3841
4077
  );
3842
4078
  } catch (error) {
3843
4079
  if (
4080
+ appendProperties.localCommitEvidence?.committedHashes.size ||
3844
4081
  !(error instanceof Error) ||
3845
4082
  error.message !==
3846
4083
  "appendLocallyPrepared payload-only path requires native append support"
@@ -3853,15 +4090,39 @@ export class Documents<
3853
4090
  appendProperties,
3854
4091
  );
3855
4092
  }
3856
- return this.createDocumentAppendCommitFacts(
3857
- input,
3858
- appended,
3859
- nativeBackboneDocumentIndex,
4093
+ return await runAfterDocumentCommit(
4094
+ input.options,
4095
+ appended.appendCommit.hash,
4096
+ () =>
4097
+ this.createDocumentAppendCommitFacts(
4098
+ input,
4099
+ appended,
4100
+ nativeBackboneDocumentIndex,
4101
+ ),
3860
4102
  );
3861
4103
  }
3862
4104
 
3863
4105
  private async commitNativeDocumentAppendMany(
3864
4106
  input: NativeDocumentAppendManyCommitInput<T, I>,
4107
+ ): Promise<DocumentAppendManyCommitFacts<T, I> | undefined> {
4108
+ if (!hasPersistedDelivery(input.options)) {
4109
+ return this.commitNativeDocumentAppendManyWithEvidence(input, undefined);
4110
+ }
4111
+ const localCommitEvidence = { committedHashes: new Set<string>() };
4112
+ return await runWithTrustedLocalCommitEvidence(
4113
+ input.options,
4114
+ localCommitEvidence,
4115
+ () =>
4116
+ this.commitNativeDocumentAppendManyWithEvidence(
4117
+ input,
4118
+ localCommitEvidence,
4119
+ ),
4120
+ );
4121
+ }
4122
+
4123
+ private async commitNativeDocumentAppendManyWithEvidence(
4124
+ input: NativeDocumentAppendManyCommitInput<T, I>,
4125
+ localCommitEvidence: TrustedLocalCommitEvidence | undefined,
3865
4126
  ): Promise<DocumentAppendManyCommitFacts<T, I> | undefined> {
3866
4127
  const trustedLog = asTrustedDocumentSharedLog(this.log);
3867
4128
  const nativeBackboneDocumentIndexes =
@@ -3892,20 +4153,18 @@ export class Documents<
3892
4153
  }
3893
4154
  return [next];
3894
4155
  });
3895
- const appended = await trustedLog.appendLocallyPreparedPayloadsManyIndependent(
3896
- input.puts.map((put) => put.operationPayloadBytes),
3897
- {
3898
- ...input.options,
3899
- replicate: input.options?.replicate,
3900
- },
3901
- {
3902
- resolveTrimmedEntries: input.resolveTrimmedEntries,
3903
- nexts,
3904
- nativeBackboneDocumentIndexes:
3905
- nativeBackboneDocumentIndexInputs,
3906
- retainMaterializationBytes: this._hasLogTrim,
3907
- },
3908
- );
4156
+ const appended =
4157
+ await trustedLog.appendLocallyPreparedPayloadsManyIndependent(
4158
+ input.puts.map((put) => put.operationPayloadBytes),
4159
+ withoutPersistedDelivery(input.options),
4160
+ {
4161
+ resolveTrimmedEntries: input.resolveTrimmedEntries,
4162
+ nexts,
4163
+ nativeBackboneDocumentIndexes: nativeBackboneDocumentIndexInputs,
4164
+ retainMaterializationBytes: this._hasLogTrim,
4165
+ ...(localCommitEvidence ? { localCommitEvidence } : undefined),
4166
+ },
4167
+ );
3909
4168
  if (!appended) {
3910
4169
  if (this.isNativeMode()) {
3911
4170
  throw this.nativeModeError(
@@ -3914,38 +4173,44 @@ export class Documents<
3914
4173
  }
3915
4174
  return undefined;
3916
4175
  }
3917
- const appendInputs = input.puts.map((put, index) => ({
3918
- input: nativeBackboneDocumentIndexes?.[index]
3919
- ? {
3920
- ...put,
3921
- nativeBackboneDocumentIndex:
3922
- nativeBackboneDocumentIndexes[index],
3923
- }
3924
- : put,
3925
- appended: (() => {
3926
- const materializeEntry = appended.materializeEntries?.[index];
3927
- let entry: Entry<Operation> | undefined;
4176
+ return await runAfterDocumentCommit(
4177
+ input.options,
4178
+ () => appended.appendCommits.map((commit) => commit.hash),
4179
+ async () => {
4180
+ const appendInputs = input.puts.map((put, index) => ({
4181
+ input: nativeBackboneDocumentIndexes?.[index]
4182
+ ? {
4183
+ ...put,
4184
+ nativeBackboneDocumentIndex:
4185
+ nativeBackboneDocumentIndexes[index],
4186
+ }
4187
+ : put,
4188
+ appended: (() => {
4189
+ const materializeEntry = appended.materializeEntries?.[index];
4190
+ let entry: Entry<Operation> | undefined;
4191
+ return {
4192
+ get entry() {
4193
+ return (entry ??= materializeEntry
4194
+ ? materializeEntry()
4195
+ : appended.entries[index]!);
4196
+ },
4197
+ removed: [],
4198
+ appendCommit: appended.appendCommits[index]!,
4199
+ };
4200
+ })(),
4201
+ }));
4202
+ const commits =
4203
+ await this.createDocumentAppendCommitFactsBatch(appendInputs);
4204
+ let entries: Entry<Operation>[] | undefined;
3928
4205
  return {
3929
- get entry() {
3930
- return (entry ??= materializeEntry
3931
- ? materializeEntry()
3932
- : appended.entries[index]!);
4206
+ get entries() {
4207
+ return (entries ??= commits.map((commit) => commit.entry));
3933
4208
  },
3934
- removed: [],
3935
- appendCommit: appended.appendCommits[index]!,
4209
+ removed: appended.removed,
4210
+ commits,
3936
4211
  };
3937
- })(),
3938
- }));
3939
- const commits =
3940
- await this.createDocumentAppendCommitFactsBatch(appendInputs);
3941
- let entries: Entry<Operation>[] | undefined;
3942
- return {
3943
- get entries() {
3944
- return (entries ??= commits.map((commit) => commit.entry));
3945
4212
  },
3946
- removed: appended.removed,
3947
- commits,
3948
- };
4213
+ );
3949
4214
  }
3950
4215
 
3951
4216
  private prepareNativeBackboneDocumentIndexCommitBatch(
@@ -3968,9 +4233,7 @@ export class Documents<
3968
4233
  firstAsyncCommit,
3969
4234
  ...inputs
3970
4235
  .slice(firstAsyncIndex + 1)
3971
- .map((input) =>
3972
- this.prepareNativeBackboneDocumentIndexCommit(input),
3973
- ),
4236
+ .map((input) => this.prepareNativeBackboneDocumentIndexCommit(input)),
3974
4237
  ]).then((resolvedCommits) => {
3975
4238
  for (const commit of resolvedCommits) {
3976
4239
  if (!commit) {
@@ -3981,9 +4244,7 @@ export class Documents<
3981
4244
  return commits;
3982
4245
  });
3983
4246
  for (let i = 0; i < inputs.length; i++) {
3984
- const commit = this.prepareNativeBackboneDocumentIndexCommit(
3985
- inputs[i]!,
3986
- );
4247
+ const commit = this.prepareNativeBackboneDocumentIndexCommit(inputs[i]!);
3987
4248
  if (isPromiseLike(commit)) {
3988
4249
  return finishAsync(i, commit);
3989
4250
  }
@@ -4191,16 +4452,16 @@ export class Documents<
4191
4452
  suffix: contextAccessors.getContextBytes(),
4192
4453
  });
4193
4454
  },
4194
- nativeBackboneDocumentIndexCommitted:
4195
- appended.appendCommit.nativeBackboneDocumentIndexCommitted,
4196
- nativeBackboneDocumentIndexTrimmedHeadsProcessed:
4197
- appended.appendCommit.nativeBackboneDocumentIndexTrimmedHeadsProcessed,
4198
- get nativeBackboneDocumentIndex() {
4199
- return getNativeBackboneDocumentIndex();
4200
- },
4201
- unique: input.unique,
4202
- existing: input.existing,
4203
- };
4455
+ nativeBackboneDocumentIndexCommitted:
4456
+ appended.appendCommit.nativeBackboneDocumentIndexCommitted,
4457
+ nativeBackboneDocumentIndexTrimmedHeadsProcessed:
4458
+ appended.appendCommit.nativeBackboneDocumentIndexTrimmedHeadsProcessed,
4459
+ get nativeBackboneDocumentIndex() {
4460
+ return getNativeBackboneDocumentIndex();
4461
+ },
4462
+ unique: input.unique,
4463
+ existing: input.existing,
4464
+ };
4204
4465
  }
4205
4466
 
4206
4467
  private async createDocumentAppendCommitFactsBatch(
@@ -4214,9 +4475,7 @@ export class Documents<
4214
4475
  const nativePreviousContext =
4215
4476
  append.documentPreviousContext == null
4216
4477
  ? undefined
4217
- : nativeDocumentContextFactsAsContext(
4218
- append.documentPreviousContext,
4219
- );
4478
+ : nativeDocumentContextFactsAsContext(append.documentPreviousContext);
4220
4479
  const nativePreviousIndexedContext = nativePreviousContext
4221
4480
  ? ({
4222
4481
  id: input.key,
@@ -4252,9 +4511,7 @@ export class Documents<
4252
4511
  const nativePreviousContext =
4253
4512
  append.documentPreviousContext == null
4254
4513
  ? undefined
4255
- : nativeDocumentContextFactsAsContext(
4256
- append.documentPreviousContext,
4257
- );
4514
+ : nativeDocumentContextFactsAsContext(append.documentPreviousContext);
4258
4515
  const nativePreviousIndexedContext = nativePreviousContext
4259
4516
  ? ({
4260
4517
  id: row.input.key,
@@ -4268,7 +4525,7 @@ export class Documents<
4268
4525
  ? {
4269
4526
  ...row.input,
4270
4527
  existing: nativePreviousIndexedContext,
4271
- }
4528
+ }
4272
4529
  : row.input;
4273
4530
  const contextPlan = contextPlans?.[index];
4274
4531
  if (!contextPlan) {
@@ -4319,7 +4576,9 @@ export class Documents<
4319
4576
  (append.nativeBackboneDocumentIndexCommitted
4320
4577
  ? undefined
4321
4578
  : this._nativeBackboneDocumentIndexEnabled
4322
- ? asTrustedDocumentIndex(this._index).prepareNativeBackboneDocumentIndexCommitWithAppendFacts(
4579
+ ? asTrustedDocumentIndex(
4580
+ this._index,
4581
+ ).prepareNativeBackboneDocumentIndexCommitWithAppendFacts(
4323
4582
  input.document,
4324
4583
  input.documentBytes,
4325
4584
  context,
@@ -4393,6 +4652,56 @@ export class Documents<
4393
4652
  });
4394
4653
  }
4395
4654
 
4655
+ private finishPreparedPlainPutCommit(
4656
+ commit: DocumentAppendCommitFacts<T, I>,
4657
+ options: DocumentPutOptions | undefined,
4658
+ handle = () => this.handlePreparedPlainPutCommit(commit),
4659
+ ): MaybePromise<DocumentPutResult> {
4660
+ return runAfterDocumentCommit(options, commit.append.hash, () =>
4661
+ mapMaybePromise(handle(), () => {
4662
+ this.keepCache?.add(commit.append.hash);
4663
+ const persistedEntry = hasPersistedDelivery(options)
4664
+ ? commit.entry
4665
+ : undefined;
4666
+ return {
4667
+ get entry() {
4668
+ return persistedEntry ?? commit.entry;
4669
+ },
4670
+ removed: commit.removed,
4671
+ };
4672
+ }),
4673
+ );
4674
+ }
4675
+
4676
+ private finishPreparedPlainPutManyCommit(
4677
+ commit: DocumentAppendManyCommitFacts<T, I>,
4678
+ options: DocumentPutOptions | undefined,
4679
+ ): MaybePromise<DocumentPutManyResult> {
4680
+ return runAfterDocumentCommit(
4681
+ options,
4682
+ () => commit.commits.map((item) => item.append.hash),
4683
+ () =>
4684
+ mapMaybePromise(this.handlePreparedPlainPutManyCommit(commit), () => {
4685
+ for (const item of commit.commits) {
4686
+ this.keepCache?.add(item.append.hash);
4687
+ }
4688
+ const result: DocumentPutManyResult = {
4689
+ get entries() {
4690
+ return commit.entries;
4691
+ },
4692
+ removed: commit.removed,
4693
+ };
4694
+ if (hasPersistedDelivery(options)) {
4695
+ persistedDocumentAppendDelivery.set(result, {
4696
+ appendCommits: commit.commits.map((item) => item.append),
4697
+ materializeEntries: () => commit.entries,
4698
+ });
4699
+ }
4700
+ return result;
4701
+ }),
4702
+ );
4703
+ }
4704
+
4396
4705
  private handlePreparedPlainPutCommit(
4397
4706
  commit: DocumentAppendCommitFacts<T, I>,
4398
4707
  ): MaybePromise<void> {
@@ -4413,7 +4722,9 @@ export class Documents<
4413
4722
  if (this._mode === "native") {
4414
4723
  return true;
4415
4724
  }
4416
- return asTrustedDocumentIndex(this._index)._persistPreparedNativeBackboneDocumentIndexStoredWithContext(
4725
+ return asTrustedDocumentIndex(
4726
+ this._index,
4727
+ )._persistPreparedNativeBackboneDocumentIndexStoredWithContext(
4417
4728
  commit.key,
4418
4729
  commit.context,
4419
4730
  commit.nativeBackboneDocumentIndex,
@@ -4585,7 +4896,10 @@ export class Documents<
4585
4896
  modified.add(commit.key.primitive);
4586
4897
  return finishRemoved();
4587
4898
  }
4588
- const withContext = coerceWithContext(commit.document, commit.context);
4899
+ const withContext = coerceWithContext(
4900
+ commit.document,
4901
+ commit.context,
4902
+ );
4589
4903
  if (commit.nativeBackboneDocumentIndex?.indexable) {
4590
4904
  return finishIndexed(
4591
4905
  coerceWithIndexed(
@@ -4612,16 +4926,17 @@ export class Documents<
4612
4926
  : mapMaybePromise(persisted, finishCommitted);
4613
4927
  }
4614
4928
  if (commit.nativeBackboneDocumentIndex) {
4615
- const nativePreparedIndexPut =
4616
- asTrustedDocumentIndex(this._index)._putPreparedNativeBackboneDocumentIndexWithContext(
4617
- commit.document,
4618
- commit.key,
4619
- commit.context,
4620
- commit.nativeBackboneDocumentIndex,
4621
- {
4622
- replace: existing != null,
4623
- },
4624
- );
4929
+ const nativePreparedIndexPut = asTrustedDocumentIndex(
4930
+ this._index,
4931
+ )._putPreparedNativeBackboneDocumentIndexWithContext(
4932
+ commit.document,
4933
+ commit.key,
4934
+ commit.context,
4935
+ commit.nativeBackboneDocumentIndex,
4936
+ {
4937
+ replace: existing != null,
4938
+ },
4939
+ );
4625
4940
  if (nativePreparedIndexPut !== undefined) {
4626
4941
  return mapMaybePromise(nativePreparedIndexPut, finishIndexed);
4627
4942
  }
@@ -4819,27 +5134,25 @@ export class Documents<
4819
5134
  private async handlePreparedPlainPutManyCommit(
4820
5135
  commit: DocumentAppendManyCommitFacts<T, I>,
4821
5136
  ): Promise<void> {
4822
- if (
4823
- !this.hasDocumentChangeConsumers() &&
4824
- commit.removed.length === 0
4825
- ) {
4826
- const stored =
4827
- await asTrustedDocumentIndex(this._index)._putManyPreparedNativeBackboneDocumentIndexStored(
4828
- commit.commits.map((put) => {
4829
- const existing =
4830
- put.unique || put.existing === null ? null : put.existing;
4831
- return {
4832
- value: put.document,
4833
- id: put.key,
4834
- context: put.context,
4835
- encodedValueParts: put.contextualEncodedValueParts,
4836
- nativeDocumentIndex: put.nativeBackboneDocumentIndex,
4837
- options: {
4838
- replace: existing != null,
4839
- },
4840
- };
4841
- }),
4842
- );
5137
+ if (!this.hasDocumentChangeConsumers() && commit.removed.length === 0) {
5138
+ const stored = await asTrustedDocumentIndex(
5139
+ this._index,
5140
+ )._putManyPreparedNativeBackboneDocumentIndexStored(
5141
+ commit.commits.map((put) => {
5142
+ const existing =
5143
+ put.unique || put.existing === null ? null : put.existing;
5144
+ return {
5145
+ value: put.document,
5146
+ id: put.key,
5147
+ context: put.context,
5148
+ encodedValueParts: put.contextualEncodedValueParts,
5149
+ nativeDocumentIndex: put.nativeBackboneDocumentIndex,
5150
+ options: {
5151
+ replace: existing != null,
5152
+ },
5153
+ };
5154
+ }),
5155
+ );
4843
5156
  if (stored === true) {
4844
5157
  return;
4845
5158
  }
@@ -4891,18 +5204,19 @@ export class Documents<
4891
5204
  },
4892
5205
  })),
4893
5206
  );
4894
- indexedDocuments ??=
4895
- await asTrustedDocumentIndex(this._index)._putManyPreparedNativeBackboneDocumentIndexWithContext(
4896
- putsToIndex.map((put) => ({
4897
- value: put.document,
4898
- id: put.key,
4899
- context: put.context,
4900
- nativeDocumentIndex: put.nativeBackboneDocumentIndex,
4901
- options: {
4902
- replace: put.replace,
4903
- },
4904
- })),
4905
- );
5207
+ indexedDocuments ??= await asTrustedDocumentIndex(
5208
+ this._index,
5209
+ )._putManyPreparedNativeBackboneDocumentIndexWithContext(
5210
+ putsToIndex.map((put) => ({
5211
+ value: put.document,
5212
+ id: put.key,
5213
+ context: put.context,
5214
+ nativeDocumentIndex: put.nativeBackboneDocumentIndex,
5215
+ options: {
5216
+ replace: put.replace,
5217
+ },
5218
+ })),
5219
+ );
4906
5220
  if (indexedDocuments) {
4907
5221
  documentsChanged.added.push(...indexedDocuments);
4908
5222
  } else {
@@ -4994,11 +5308,13 @@ export class Documents<
4994
5308
  ): Promise<boolean> {
4995
5309
  const key = await this._index.getIdentityIndexedKeyByHead(head);
4996
5310
  if (key) {
4997
- if (await this.collectRemovedDocumentChangeFromIndexedKey(
4998
- key,
4999
- modified,
5000
- documentsChanged,
5001
- )) {
5311
+ if (
5312
+ await this.collectRemovedDocumentChangeFromIndexedKey(
5313
+ key,
5314
+ modified,
5315
+ documentsChanged,
5316
+ )
5317
+ ) {
5002
5318
  return true;
5003
5319
  }
5004
5320
  }
@@ -5272,17 +5588,20 @@ export class Documents<
5272
5588
  gid: entry.meta.gid,
5273
5589
  size: encodePutOperationPayload(payload.data).byteLength,
5274
5590
  });
5275
- const nativeDocumentIndex =
5276
- asTrustedDocumentIndex(this._index).prepareNativeBackboneDocumentIndexCommitWithAppendFacts(
5277
- value,
5278
- payload.data,
5279
- context,
5280
- { entryPublicKeys: entry.publicKeys },
5281
- );
5591
+ const nativeDocumentIndex = asTrustedDocumentIndex(
5592
+ this._index,
5593
+ ).prepareNativeBackboneDocumentIndexCommitWithAppendFacts(
5594
+ value,
5595
+ payload.data,
5596
+ context,
5597
+ { entryPublicKeys: entry.publicKeys },
5598
+ );
5282
5599
  if (!nativeDocumentIndex) {
5283
5600
  return;
5284
5601
  }
5285
- return asTrustedDocumentIndex(this._index)._putPreparedNativeBackboneDocumentIndexWithContext(
5602
+ return asTrustedDocumentIndex(
5603
+ this._index,
5604
+ )._putPreparedNativeBackboneDocumentIndexWithContext(
5286
5605
  value,
5287
5606
  key,
5288
5607
  context,
@@ -5311,16 +5630,19 @@ export class Documents<
5311
5630
  gid: entry.meta.gid,
5312
5631
  size: encodePutOperationPayload(payload.data).byteLength,
5313
5632
  });
5314
- const nativeDocumentIndex =
5315
- asTrustedDocumentIndex(this._index).prepareNativeBackboneDocumentIndexStoredCommitWithAppendFacts(
5316
- payload.data,
5317
- context,
5318
- { entryPublicKeys: entry.publicKeys },
5319
- );
5633
+ const nativeDocumentIndex = asTrustedDocumentIndex(
5634
+ this._index,
5635
+ ).prepareNativeBackboneDocumentIndexStoredCommitWithAppendFacts(
5636
+ payload.data,
5637
+ context,
5638
+ { entryPublicKeys: entry.publicKeys },
5639
+ );
5320
5640
  if (!nativeDocumentIndex) {
5321
5641
  return;
5322
5642
  }
5323
- return asTrustedDocumentIndex(this._index)._putPreparedNativeBackboneDocumentIndexStoredWithContext(
5643
+ return asTrustedDocumentIndex(
5644
+ this._index,
5645
+ )._putPreparedNativeBackboneDocumentIndexStoredWithContext(
5324
5646
  key,
5325
5647
  context,
5326
5648
  nativeDocumentIndex,
@@ -5345,6 +5667,11 @@ export class Documents<
5345
5667
  id: indexerTypes.Ideable | indexerTypes.IdKey,
5346
5668
  options?: SharedAppendOptions<Operation>,
5347
5669
  ) {
5670
+ if (hasPersistedDelivery(options)) {
5671
+ throw new Error(
5672
+ "persisted delivery is not supported for document deletes",
5673
+ );
5674
+ }
5348
5675
  return this._documentBackend.del(id, options);
5349
5676
  }
5350
5677
 
@@ -5488,11 +5815,11 @@ export class Documents<
5488
5815
  },
5489
5816
  removed: appended.removed,
5490
5817
  };
5491
- if (appended.appendCommit.nativeBackboneDocumentDeleteCommitted) {
5492
- this._index.clearResolvedCacheForKeys([key]);
5493
- } else {
5494
- await this._index.delManyMaybe([key]);
5495
- }
5818
+ if (appended.appendCommit.nativeBackboneDocumentDeleteCommitted) {
5819
+ this._index.clearResolvedCacheForKeys([key]);
5820
+ } else {
5821
+ await this._index.delManyMaybe([key]);
5822
+ }
5496
5823
  if (documentsChanged && removedDocument) {
5497
5824
  documentsChanged.removed.push(removedDocument);
5498
5825
  this.dispatchDocumentChangeIfObserved(documentsChanged);
@@ -5592,8 +5919,7 @@ export class Documents<
5592
5919
  const existing =
5593
5920
  reference?.unique || reference?.existing === null
5594
5921
  ? null
5595
- : isReferencedAppendEntry &&
5596
- reference?.existing !== undefined
5922
+ : isReferencedAppendEntry && reference?.existing !== undefined
5597
5923
  ? reference.existing
5598
5924
  : this.getNativeModeIndexedContext(key) || null;
5599
5925
  if (!this.strictHistory && existing) {
@@ -5622,9 +5948,7 @@ export class Documents<
5622
5948
  let value =
5623
5949
  isReferencedAppendEntry && reference?.document
5624
5950
  ? reference.document
5625
- : this.index.valueEncoding.decoder(
5626
- new Uint8Array(payload.data),
5627
- );
5951
+ : this.index.valueEncoding.decoder(new Uint8Array(payload.data));
5628
5952
 
5629
5953
  // get index key from value
5630
5954
  const key =