@peerbit/document 15.0.15 → 15.0.17

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,
@@ -53,6 +44,16 @@ import {
53
44
  import { MAX_BATCH_SIZE } from "./constants.js";
54
45
  import type { CustomDocumentDomain } from "./domain.js";
55
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";
56
57
  import {
57
58
  BORSH_ENCODING_OPERATION,
58
59
  DeleteOperation,
@@ -66,14 +67,14 @@ import {
66
67
  import {
67
68
  type CanPerformPolicyDescriptor,
68
69
  type CanPerformPolicyEvaluator,
69
- createCanPerformPolicyEvaluator,
70
- createCanPerformDeletePolicyEvaluator,
71
- getCanPerformPolicyDescriptor,
72
70
  canPerformPolicyDeleteFieldPaths,
73
71
  canPerformPolicyNeedsDeleteValue,
74
72
  canPerformPolicyNeedsPreviousEntries,
75
73
  canPerformPolicyPutNeedsEntryPublicKeys,
76
74
  canPerformPolicySignedByFieldPaths,
75
+ createCanPerformDeletePolicyEvaluator,
76
+ createCanPerformPolicyEvaluator,
77
+ getCanPerformPolicyDescriptor,
77
78
  } from "./policy.js";
78
79
  import { isResultIndexedValue } from "./result-shape.js";
79
80
  import {
@@ -239,6 +240,73 @@ type DocumentPutOptions = SharedAppendOptions<Operation> & {
239
240
  checkRemote?: boolean;
240
241
  };
241
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
+
242
310
  const NATIVE_LOCAL_PUT_OPTIONS = Object.freeze({
243
311
  replicate: false,
244
312
  target: "none" as const,
@@ -268,6 +336,8 @@ const cachedNativeLocalPutOptions = (
268
336
  return options.unique === true ? NATIVE_LOCAL_UNIQUE_PUT_OPTIONS : undefined;
269
337
  };
270
338
 
339
+ const persistedDeliveryAlreadySettled = new WeakSet<object>();
340
+
271
341
  type DocumentPutResult = {
272
342
  readonly entry: Entry<Operation>;
273
343
  removed: ShallowOrFullEntry<Operation>[];
@@ -337,17 +407,18 @@ type NativeDocumentBackendContext<T, I extends Record<string, any>> = {
337
407
  commitNativeDocumentAppendMany(
338
408
  input: NativeDocumentAppendManyCommitInput<T, I>,
339
409
  ): MaybePromise<DocumentAppendManyCommitFacts<T, I> | undefined>;
340
- handlePreparedPlainPutCommit(
410
+ finishPreparedPlainPutCommit(
341
411
  commit: NativeDocumentAppendTransaction<T, I>,
342
- ): MaybePromise<void>;
343
- handlePreparedPlainPutManyCommit(
412
+ options: DocumentPutOptions | undefined,
413
+ ): MaybePromise<DocumentPutResult>;
414
+ finishPreparedPlainPutManyCommit(
344
415
  commit: DocumentAppendManyCommitFacts<T, I>,
345
- ): MaybePromise<void>;
416
+ options: DocumentPutOptions | undefined,
417
+ ): MaybePromise<DocumentPutManyResult>;
346
418
  deleteDocument(
347
419
  id: indexerTypes.Ideable | indexerTypes.IdKey,
348
420
  options?: DocumentPutOptions,
349
421
  ): MaybePromise<DocumentDeleteResult>;
350
- keepEntry(hash: string): void;
351
422
  nativeModeError(message: string): NativeDocumentModeError;
352
423
  };
353
424
 
@@ -434,17 +505,9 @@ class NativeDocumentBackend<T, I extends Record<string, any>>
434
505
  existing,
435
506
  }),
436
507
  (documentAppendCommit) =>
437
- mapMaybePromise(
438
- this.context.handlePreparedPlainPutCommit(documentAppendCommit),
439
- () => {
440
- this.context.keepEntry(documentAppendCommit.append.hash);
441
- return {
442
- get entry() {
443
- return documentAppendCommit.entry;
444
- },
445
- removed: documentAppendCommit.removed,
446
- };
447
- },
508
+ this.context.finishPreparedPlainPutCommit(
509
+ documentAppendCommit,
510
+ options,
448
511
  ),
449
512
  );
450
513
  };
@@ -527,6 +590,11 @@ class NativeDocumentBackend<T, I extends Record<string, any>>
527
590
  }
528
591
  const prepared = docs.map((doc) => this.context.preparePlainPut(doc));
529
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
+ }
530
598
  const results: DocumentPutResult[] = [];
531
599
  for (const doc of docs) {
532
600
  results.push(await this.put(doc, putOptions));
@@ -604,45 +672,35 @@ class NativeDocumentBackend<T, I extends Record<string, any>>
604
672
  ),
605
673
  ),
606
674
  );
607
- }
608
- return mapMaybePromise(
609
- this.context.commitNativeDocumentAppendMany({
610
- puts: prepared.map((item, index) => ({
611
- document: item.document,
612
- key: item.key,
613
- documentBytes: item.encodedDocument,
614
- operationPayloadBytes: item.operationPayloadBytes,
615
- unique: putOptions?.unique,
616
- requiredPreviousSignerPublicKey,
617
- existing: existingContexts
618
- ? (existingContexts[index] ?? null)
619
- : useNativeExistingDocumentContext
620
- ? undefined
621
- : null,
622
- })),
623
- resolveTrimmedEntries: this.context.shouldResolveTrimmedEntries(),
624
- options: putOptions,
625
- useNativeExistingDocumentContext,
626
- }),
627
- (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) => {
628
696
  if (!documentAppendCommit) {
629
697
  throw this.context.nativeModeError(
630
698
  "requires native batched payload append support",
631
699
  );
632
700
  }
633
- return mapMaybePromise(
634
- this.context.handlePreparedPlainPutManyCommit(documentAppendCommit),
635
- () => {
636
- for (const commit of documentAppendCommit.commits) {
637
- this.context.keepEntry(commit.append.hash);
638
- }
639
- return {
640
- get entries() {
641
- return documentAppendCommit.entries;
642
- },
643
- removed: documentAppendCommit.removed,
644
- };
645
- },
701
+ return this.context.finishPreparedPlainPutManyCommit(
702
+ documentAppendCommit,
703
+ options,
646
704
  );
647
705
  },
648
706
  );
@@ -746,6 +804,7 @@ type TrustedDocumentSharedLogAppendProperties = {
746
804
  ) => NativeBackboneDocumentIndexCommitInput | undefined;
747
805
  useNativeExistingDocumentContext?: boolean;
748
806
  nativeBackboneDocumentDeleteKey?: string;
807
+ localCommitEvidence?: TrustedLocalCommitEvidence;
749
808
  };
750
809
 
751
810
  type TrustedDocumentSharedLogAppendManyProperties = {
@@ -753,6 +812,7 @@ type TrustedDocumentSharedLogAppendManyProperties = {
753
812
  nexts?: ShallowOrFullEntry<Operation>[][];
754
813
  nativeBackboneDocumentIndexes?: NativeBackboneDocumentIndexCommitInput[];
755
814
  retainMaterializationBytes?: boolean;
815
+ localCommitEvidence?: TrustedLocalCommitEvidence;
756
816
  };
757
817
 
758
818
  type TrustedDocumentSharedLogAppendManyResult = {
@@ -762,8 +822,33 @@ type TrustedDocumentSharedLogAppendManyResult = {
762
822
  appendCommits: LocalAppendCommitFacts[];
763
823
  };
764
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
+
765
838
  type TrustedDocumentSharedLog = {
766
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>;
767
852
  appendLocallyPrepared(
768
853
  data: Operation,
769
854
  options?: SharedAppendOptions<Operation>,
@@ -996,8 +1081,7 @@ const asTrustedDocumentIndex = <
996
1081
  D extends ReplicationDomain<any, Operation, any>,
997
1082
  >(
998
1083
  index: DocumentIndex<T, I, D>,
999
- ): TrustedDocumentIndex<T, I> =>
1000
- index as unknown as TrustedDocumentIndex<T, I>;
1084
+ ): TrustedDocumentIndex<T, I> => index as unknown as TrustedDocumentIndex<T, I>;
1001
1085
 
1002
1086
  type NativeDocumentAppendCommitInput<
1003
1087
  T,
@@ -1288,15 +1372,12 @@ export class Documents<
1288
1372
  this.commitNativeDocumentAppend(input),
1289
1373
  commitNativeDocumentAppendMany: (input) =>
1290
1374
  this.commitNativeDocumentAppendMany(input),
1291
- handlePreparedPlainPutCommit: (commit) =>
1292
- this.handlePreparedPlainPutCommit(commit),
1293
- handlePreparedPlainPutManyCommit: (commit) =>
1294
- this.handlePreparedPlainPutManyCommit(commit),
1375
+ finishPreparedPlainPutCommit: (commit, options) =>
1376
+ this.finishPreparedPlainPutCommit(commit, options),
1377
+ finishPreparedPlainPutManyCommit: (commit, options) =>
1378
+ this.finishPreparedPlainPutManyCommit(commit, options),
1295
1379
  deleteDocument: (id, options) =>
1296
1380
  this.delNativeDocumentBackend(id, options),
1297
- keepEntry: (hash) => {
1298
- this.keepCache?.add(hash);
1299
- },
1300
1381
  nativeModeError: (message) => this.nativeModeError(message),
1301
1382
  };
1302
1383
  }
@@ -1328,10 +1409,7 @@ export class Documents<
1328
1409
  const nativeIndexTransformDescriptor =
1329
1410
  typeof indexTransform?.transform === "function"
1330
1411
  ? getDocumentTransformDescriptor(
1331
- indexTransform.transform as DocumentTransformer<
1332
- unknown,
1333
- unknown
1334
- >,
1412
+ indexTransform.transform as DocumentTransformer<unknown, unknown>,
1335
1413
  )
1336
1414
  : undefined;
1337
1415
 
@@ -1437,7 +1515,9 @@ export class Documents<
1437
1515
  );
1438
1516
  }
1439
1517
  if (
1440
- !asTrustedDocumentIndex(this._index).canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts()
1518
+ !asTrustedDocumentIndex(
1519
+ this._index,
1520
+ ).canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts()
1441
1521
  ) {
1442
1522
  throw this.nativeModeError(
1443
1523
  "requires a native-compatible document index transform",
@@ -1486,9 +1566,7 @@ export class Documents<
1486
1566
  private nativePlainPutPolicyNeedsPreviousEntries(): boolean {
1487
1567
  return (
1488
1568
  !!this._optionCanPerformNativePolicy &&
1489
- canPerformPolicyNeedsPreviousEntries(
1490
- this._optionCanPerformNativePolicy,
1491
- )
1569
+ canPerformPolicyNeedsPreviousEntries(this._optionCanPerformNativePolicy)
1492
1570
  );
1493
1571
  }
1494
1572
 
@@ -1551,10 +1629,18 @@ export class Documents<
1551
1629
  if (options?.replicate === true) {
1552
1630
  unsupported.push("replicated put");
1553
1631
  }
1554
- if (options?.target && options.target !== "none") {
1632
+ if (
1633
+ options?.target &&
1634
+ options.target !== "none" &&
1635
+ !(hasPersistedDelivery(options) && options.target === "replicators")
1636
+ ) {
1555
1637
  unsupported.push("non-local target");
1556
1638
  }
1557
- if (options?.delivery !== undefined && options.delivery !== false) {
1639
+ if (
1640
+ options?.delivery !== undefined &&
1641
+ options.delivery !== false &&
1642
+ !hasPersistedDelivery(options)
1643
+ ) {
1558
1644
  unsupported.push("delivery");
1559
1645
  }
1560
1646
  if (options?.checkRemote) {
@@ -1685,9 +1771,8 @@ export class Documents<
1685
1771
  value: unknown,
1686
1772
  publicKey: PublicSignKey,
1687
1773
  ): boolean {
1688
- const localRawPublicKey = (
1689
- publicKey as { publicKey?: Uint8Array }
1690
- ).publicKey;
1774
+ const localRawPublicKey = (publicKey as { publicKey?: Uint8Array })
1775
+ .publicKey;
1691
1776
  return (
1692
1777
  value instanceof Uint8Array &&
1693
1778
  (bytesEqual(value, publicKey.bytes) ||
@@ -1929,7 +2014,9 @@ export class Documents<
1929
2014
  if (Program.isPrototypeOf(this._clazz)) {
1930
2015
  unsupported.push("program-valued document type");
1931
2016
  }
1932
- if (!asTrustedDocumentIndex(this._index).canUseNativeBackboneContextualBatch()) {
2017
+ if (
2018
+ !asTrustedDocumentIndex(this._index).canUseNativeBackboneContextualBatch()
2019
+ ) {
1933
2020
  unsupported.push("native batch document index");
1934
2021
  }
1935
2022
  if (unsupported.length > 0) {
@@ -1942,6 +2029,9 @@ export class Documents<
1942
2029
  return;
1943
2030
  }
1944
2031
  const unsupported = this.unsupportedNativePutOptions(options);
2032
+ if (options && hasPersistedDelivery(options)) {
2033
+ unsupported.push("delivery");
2034
+ }
1945
2035
  if (options?.unique !== undefined) {
1946
2036
  unsupported.push("unique delete");
1947
2037
  }
@@ -1977,17 +2067,15 @@ export class Documents<
1977
2067
  );
1978
2068
  }
1979
2069
  let deleteValue: T | undefined;
1980
- if (
1981
- canPerformPolicyNeedsDeleteValue(
1982
- this._optionCanPerformNativePolicy,
1983
- )
1984
- ) {
2070
+ if (canPerformPolicyNeedsDeleteValue(this._optionCanPerformNativePolicy)) {
1985
2071
  deleteValue = await properties.getExistingDocument?.();
1986
2072
  if (deleteValue === undefined) {
1987
2073
  const existingEntry = await properties.getExistingEntry();
1988
2074
  const existingOperation = await existingEntry.getPayloadValue();
1989
2075
  if (isPutOperation(existingOperation)) {
1990
- deleteValue = this._index.valueEncoding.decoder(existingOperation.data);
2076
+ deleteValue = this._index.valueEncoding.decoder(
2077
+ existingOperation.data,
2078
+ );
1991
2079
  }
1992
2080
  }
1993
2081
  }
@@ -2003,6 +2091,9 @@ export class Documents<
2003
2091
  if (!this.isNativeMode()) {
2004
2092
  return options;
2005
2093
  }
2094
+ if (hasPersistedDelivery(options)) {
2095
+ return options;
2096
+ }
2006
2097
  if (options?.replicate === false && options.target === "none") {
2007
2098
  return options;
2008
2099
  }
@@ -2145,9 +2236,11 @@ export class Documents<
2145
2236
  | indexerTypes.IndexedResult<IndexedContextOnly<I>>
2146
2237
  | undefined;
2147
2238
  try {
2148
- const result = (orderedSession
2149
- ? orderedSession.get(key, { shape: INDEX_CONTEXT_SHAPE })
2150
- : 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>;
2151
2244
  return (
2152
2245
  isPromiseLike(result)
2153
2246
  ? result.catch((error) => this.recoverClosedIndexContextRead(error))
@@ -2220,9 +2313,7 @@ export class Documents<
2220
2313
 
2221
2314
  private getNativeIndexedContext(
2222
2315
  key: indexerTypes.IdKey,
2223
- ):
2224
- | indexerTypes.IndexedResult<IndexedContextOnly<I>>
2225
- | undefined {
2316
+ ): indexerTypes.IndexedResult<IndexedContextOnly<I>> | undefined {
2226
2317
  const nativeBackbone = this.getSharedLogNativeBackbone<
2227
2318
  | {
2228
2319
  documentContext?: (
@@ -2285,9 +2376,7 @@ export class Documents<
2285
2376
  }
2286
2377
  const nativeBackbone = this.getSharedLogNativeBackbone<
2287
2378
  | {
2288
- documentContextsAndPreviousSignaturePublicKeys?: (
2289
- keys: string[],
2290
- ) =>
2379
+ documentContextsAndPreviousSignaturePublicKeys?: (keys: string[]) =>
2291
2380
  | Array<{
2292
2381
  context?: {
2293
2382
  created: bigint;
@@ -2315,9 +2404,7 @@ export class Documents<
2315
2404
  ? {
2316
2405
  id: keys[index]!,
2317
2406
  value: {
2318
- __context: nativeDocumentContextFactsAsContext(
2319
- row.context,
2320
- ),
2407
+ __context: nativeDocumentContextFactsAsContext(row.context),
2321
2408
  } as IndexedContextOnly<I>,
2322
2409
  }
2323
2410
  : undefined,
@@ -2508,8 +2595,9 @@ export class Documents<
2508
2595
  );
2509
2596
  this._nativeDocumentFieldExtractionPlans ??= new Map();
2510
2597
  this._nativeDocumentFieldExtractionPlans.clear();
2511
- this._nativeDocumentIdExtractionPlan =
2512
- asTrustedDocumentIndex(this._index).getNativeDocumentFieldExtractionPlan(idProperty);
2598
+ this._nativeDocumentIdExtractionPlan = asTrustedDocumentIndex(
2599
+ this._index,
2600
+ ).getNativeDocumentFieldExtractionPlan(idProperty);
2513
2601
 
2514
2602
  // B12: the historical document->log compatibility mapping (6 -> log v8,
2515
2603
  // 7 -> log v9) is retired; the rejection at the top of open() fires for
@@ -2726,9 +2814,7 @@ export class Documents<
2726
2814
  const deleteValue =
2727
2815
  this._optionCanPerformNativePolicy &&
2728
2816
  isDeleteOperation(operation) &&
2729
- canPerformPolicyNeedsDeleteValue(
2730
- this._optionCanPerformNativePolicy,
2731
- )
2817
+ canPerformPolicyNeedsDeleteValue(this._optionCanPerformNativePolicy)
2732
2818
  ? await this.resolveCanPerformDeleteValue(operation)
2733
2819
  : undefined;
2734
2820
  if (
@@ -2944,7 +3030,8 @@ export class Documents<
2944
3030
  : reference.document,
2945
3031
  );
2946
3032
  } else {
2947
- keyValue = await this.getNativeDocumentIdFromPutOperation(putOperation);
3033
+ keyValue =
3034
+ await this.getNativeDocumentIdFromPutOperation(putOperation);
2948
3035
  if (keyValue == null) {
2949
3036
  if (this.isNativeMode()) {
2950
3037
  return false;
@@ -3097,7 +3184,6 @@ export class Documents<
3097
3184
  } else {
3098
3185
  throw new Error("Unsupported operation");
3099
3186
  }
3100
-
3101
3187
  } catch (error) {
3102
3188
  if (error instanceof AccessError) {
3103
3189
  return false; // we cant index because we can not decrypt
@@ -3203,7 +3289,9 @@ export class Documents<
3203
3289
  if (plans.has(key)) {
3204
3290
  return plans.get(key);
3205
3291
  }
3206
- const plan = asTrustedDocumentIndex(this._index).getNativeDocumentFieldExtractionPlan(path);
3292
+ const plan = asTrustedDocumentIndex(
3293
+ this._index,
3294
+ ).getNativeDocumentFieldExtractionPlan(path);
3207
3295
  plans.set(key, plan);
3208
3296
  return plan;
3209
3297
  }
@@ -3301,7 +3389,47 @@ export class Documents<
3301
3389
  doc: T,
3302
3390
  options?: DocumentPutOptions,
3303
3391
  ): Promise<DocumentPutResult> {
3304
- 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
+ );
3305
3433
  }
3306
3434
 
3307
3435
  private async putCompatDocumentBackend(
@@ -3347,6 +3475,7 @@ export class Documents<
3347
3475
  "operation" in prepared
3348
3476
  ? prepared.operation
3349
3477
  : new PutOperation({ data: prepared.encodedDocument });
3478
+ const persistedDelivery = hasPersistedDelivery(putOptions);
3350
3479
  const appended = await this.log.append(operation, {
3351
3480
  ...putOptions,
3352
3481
  meta: {
@@ -3359,18 +3488,39 @@ export class Documents<
3359
3488
  operation,
3360
3489
  });
3361
3490
  },
3362
- onChange: (change) => {
3363
- return this.handleChanges(change, {
3364
- document: prepared.document,
3365
- operation,
3366
- key: prepared.key,
3367
- unique: putOptions?.unique,
3368
- existing: existingLocalContext,
3369
- });
3370
- },
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
+ }),
3371
3518
  replicate: putOptions?.replicate,
3372
3519
  });
3373
3520
  this.keepCache?.add(appended.entry.hash);
3521
+ if (persistedDelivery) {
3522
+ persistedDeliveryAlreadySettled.add(appended);
3523
+ }
3374
3524
  return appended;
3375
3525
  }
3376
3526
 
@@ -3378,7 +3528,36 @@ export class Documents<
3378
3528
  docs: T[],
3379
3529
  options?: DocumentPutOptions,
3380
3530
  ): Promise<DocumentPutManyResult> {
3381
- 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 };
3382
3561
  }
3383
3562
 
3384
3563
  private async putManyCompatDocumentBackend(
@@ -3389,11 +3568,19 @@ export class Documents<
3389
3568
  return { entries: [], removed: [] };
3390
3569
  }
3391
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
+ }
3392
3576
  return this.putManySequential(docs, options);
3393
3577
  }
3394
3578
 
3395
3579
  const prepared = docs.map((doc) => this.preparePlainPut(doc));
3396
3580
  if (this.hasDuplicatePreparedPutKeys(prepared)) {
3581
+ if (hasPersistedDelivery(options)) {
3582
+ throw new Error("persisted putMany requires distinct document keys");
3583
+ }
3397
3584
  return this.putManySequential(docs, options);
3398
3585
  }
3399
3586
 
@@ -3410,19 +3597,17 @@ export class Documents<
3410
3597
  options,
3411
3598
  });
3412
3599
  if (!documentAppendCommit) {
3600
+ if (hasPersistedDelivery(options)) {
3601
+ throw new Error(
3602
+ "persisted putMany requires native batched payload append support",
3603
+ );
3604
+ }
3413
3605
  return this.putManySequential(docs, options);
3414
3606
  }
3415
-
3416
- await this.handlePreparedPlainPutManyCommit(documentAppendCommit);
3417
- for (const commit of documentAppendCommit.commits) {
3418
- this.keepCache?.add(commit.append.hash);
3419
- }
3420
- return {
3421
- get entries() {
3422
- return documentAppendCommit.entries;
3423
- },
3424
- removed: documentAppendCommit.removed,
3425
- };
3607
+ return await this.finishPreparedPlainPutManyCommit(
3608
+ documentAppendCommit,
3609
+ options,
3610
+ );
3426
3611
  }
3427
3612
 
3428
3613
  private async putManySequential(
@@ -3459,6 +3644,7 @@ export class Documents<
3459
3644
  doc: T,
3460
3645
  options?: DocumentPutOptions,
3461
3646
  ): boolean {
3647
+ const persistedDelivery = hasPersistedDelivery(options);
3462
3648
  return (
3463
3649
  this._mode !== "compat" &&
3464
3650
  this.canPerformAllowsPlainPutFastPath(doc) &&
@@ -3479,8 +3665,12 @@ export class Documents<
3479
3665
  !options?.meta?.timestamp &&
3480
3666
  !options?.meta?.gidSeed &&
3481
3667
  options?.replicate !== true &&
3482
- (!options?.target || options.target === "none") &&
3483
- (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) &&
3484
3674
  !options?.checkRemote &&
3485
3675
  options?.replicas === undefined
3486
3676
  );
@@ -3490,11 +3680,17 @@ export class Documents<
3490
3680
  docs: T[],
3491
3681
  options?: DocumentPutOptions,
3492
3682
  ): boolean {
3683
+ const persistedDelivery = hasPersistedDelivery(options);
3493
3684
  return (
3494
3685
  options?.unique === true &&
3495
3686
  options?.replicate !== true &&
3496
- options?.target === "none" &&
3497
- (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) &&
3498
3694
  docs.every((doc) => this.canUsePlainPutFastPath(doc, options))
3499
3695
  );
3500
3696
  }
@@ -3567,49 +3763,63 @@ export class Documents<
3567
3763
  unique: plan.unique,
3568
3764
  existing: plan.existing,
3569
3765
  }),
3570
- (documentAppendCommit) => {
3571
- const handled = plan.useGenericChangeHandler
3572
- ? this.handleChanges(
3573
- {
3574
- added: [{ head: true, entry: documentAppendCommit.entry }],
3575
- removed: documentAppendCommit.removed,
3576
- },
3577
- {
3578
- document: plan.document,
3579
- operation:
3580
- documentAppendCommit.operation ??
3581
- plan.operation ??
3582
- new PutOperation({ data: plan.encodedDocument }),
3583
- key: plan.key,
3584
- unique: plan.unique,
3585
- existing: plan.existing,
3586
- },
3587
- )
3588
- : this.handlePreparedPlainPutCommit(documentAppendCommit);
3589
- return mapMaybePromise(handled, () => {
3590
- this.keepCache?.add(documentAppendCommit.append.hash);
3591
- return {
3592
- get entry() {
3593
- return documentAppendCommit.entry;
3594
- },
3595
- removed: documentAppendCommit.removed,
3596
- };
3597
- });
3598
- },
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
+ ),
3599
3792
  );
3600
3793
  }
3601
3794
 
3602
3795
  private commitNativeDocumentAppend(
3603
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,
3604
3813
  ): MaybePromise<NativeDocumentAppendTransaction<T, I>> {
3605
3814
  const trustedLog = asTrustedDocumentSharedLog(this.log);
3815
+ const localAppendOptions = withoutPersistedDelivery(input.options);
3606
3816
  const appendOptions = {
3607
- ...input.options,
3817
+ ...localAppendOptions,
3608
3818
  meta: {
3609
3819
  next: input.next,
3610
- ...input.options?.meta,
3820
+ ...localAppendOptions?.meta,
3611
3821
  },
3612
- replicate: input.options?.replicate,
3822
+ replicate: localAppendOptions?.replicate,
3613
3823
  };
3614
3824
  const prepareNativeDocumentIndexWithAppendFacts =
3615
3825
  this.createNativeBackboneDocumentIndexAppendFactsPreparer(input);
@@ -3638,6 +3848,7 @@ export class Documents<
3638
3848
  payloadData: input.operationPayloadBytes,
3639
3849
  useNativeExistingDocumentContext:
3640
3850
  input.useNativeExistingDocumentContext,
3851
+ ...(localCommitEvidence ? { localCommitEvidence } : undefined),
3641
3852
  ...(nativeDocumentIndexCommit
3642
3853
  ? {
3643
3854
  nativeBackboneDocumentIndex:
@@ -3677,10 +3888,15 @@ export class Documents<
3677
3888
  appendProperties,
3678
3889
  ),
3679
3890
  (appended) =>
3680
- this.createNativeCheckedDocumentAppendCommitFacts(
3681
- input,
3682
- appended,
3683
- committedNativeDocumentIndex,
3891
+ runAfterDocumentCommit(
3892
+ input.options,
3893
+ appended.appendCommit.hash,
3894
+ () =>
3895
+ this.createNativeCheckedDocumentAppendCommitFacts(
3896
+ input,
3897
+ appended,
3898
+ committedNativeDocumentIndex,
3899
+ ),
3684
3900
  ),
3685
3901
  );
3686
3902
  }
@@ -3695,29 +3911,31 @@ export class Documents<
3695
3911
  appendOptions,
3696
3912
  appendProperties,
3697
3913
  );
3698
- return mapMaybePromise(
3699
- commitOnlyAppend,
3700
- (commitOnly) => {
3701
- if (commitOnly) {
3702
- return this.createNativeCheckedDocumentAppendCommitFacts(
3703
- input,
3704
- commitOnly,
3705
- committedNativeDocumentIndex,
3706
- );
3707
- }
3708
- if (this.isNativeMode()) {
3709
- throw this.nativeModeError(
3710
- "requires native payload commit-only append",
3711
- );
3712
- }
3713
- return this.commitNativeDocumentAppendPayloadFallback(
3714
- input,
3715
- appendOptions,
3716
- appendProperties,
3717
- 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
+ ),
3718
3925
  );
3719
- },
3720
- );
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
+ });
3721
3939
  },
3722
3940
  );
3723
3941
  }
@@ -3755,7 +3973,7 @@ export class Documents<
3755
3973
  input: NativeDocumentAppendCommitFactsInput<T, I>,
3756
3974
  commit: PreparedNativeBackboneDocumentIndexCommit<I>,
3757
3975
  useLatestContext = false,
3758
- ): NativeBackboneDocumentIndexCommitInput {
3976
+ ): NativeBackboneDocumentIndexCommitInput {
3759
3977
  const canUsePlainPutPayload =
3760
3978
  commit.usePlainPutPayload === true ||
3761
3979
  (!!input.operationPayloadBytes && !!commit.projection);
@@ -3772,8 +3990,7 @@ export class Documents<
3772
3990
  !this.hasDocumentChangeConsumers() &&
3773
3991
  this._index.canGetIndexedKeyByHead(),
3774
3992
  useLatestContext,
3775
- requiredPreviousSignerPublicKey:
3776
- input.requiredPreviousSignerPublicKey,
3993
+ requiredPreviousSignerPublicKey: input.requiredPreviousSignerPublicKey,
3777
3994
  };
3778
3995
  }
3779
3996
 
@@ -3783,7 +4000,9 @@ export class Documents<
3783
4000
  if (!this._nativeBackboneDocumentIndexEnabled) {
3784
4001
  return;
3785
4002
  }
3786
- return asTrustedDocumentIndex(this._index).prepareNativeBackboneDocumentIndexCommit(
4003
+ return asTrustedDocumentIndex(
4004
+ this._index,
4005
+ ).prepareNativeBackboneDocumentIndexCommit(
3787
4006
  input.document,
3788
4007
  input.documentBytes,
3789
4008
  { entryPublicKeys: [this.log.log.identity.publicKey] },
@@ -3799,7 +4018,9 @@ export class Documents<
3799
4018
  | undefined {
3800
4019
  if (
3801
4020
  !this._nativeBackboneDocumentIndexEnabled ||
3802
- !asTrustedDocumentIndex(this._index).canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts()
4021
+ !asTrustedDocumentIndex(
4022
+ this._index,
4023
+ ).canPrepareNativeBackboneDocumentIndexCommitWithAppendFacts()
3803
4024
  ) {
3804
4025
  return;
3805
4026
  }
@@ -3818,7 +4039,9 @@ export class Documents<
3818
4039
  gid: appendFacts.gid,
3819
4040
  size: appendFacts.payloadSize,
3820
4041
  });
3821
- return asTrustedDocumentIndex(this._index).prepareNativeBackboneDocumentIndexCommitWithAppendFacts(
4042
+ return asTrustedDocumentIndex(
4043
+ this._index,
4044
+ ).prepareNativeBackboneDocumentIndexCommitWithAppendFacts(
3822
4045
  input.document,
3823
4046
  input.documentBytes,
3824
4047
  context,
@@ -3834,6 +4057,7 @@ export class Documents<
3834
4057
  skipMissingNextJoin: boolean;
3835
4058
  resolveTrimmedEntries: boolean;
3836
4059
  payloadData: Uint8Array;
4060
+ localCommitEvidence?: TrustedLocalCommitEvidence;
3837
4061
  prepareNativeBackboneDocumentIndex?: (
3838
4062
  facts: NativeBackboneDocumentIndexAppendFactsInput,
3839
4063
  ) => NativeBackboneDocumentIndexCommitInput | undefined;
@@ -3853,6 +4077,7 @@ export class Documents<
3853
4077
  );
3854
4078
  } catch (error) {
3855
4079
  if (
4080
+ appendProperties.localCommitEvidence?.committedHashes.size ||
3856
4081
  !(error instanceof Error) ||
3857
4082
  error.message !==
3858
4083
  "appendLocallyPrepared payload-only path requires native append support"
@@ -3865,15 +4090,39 @@ export class Documents<
3865
4090
  appendProperties,
3866
4091
  );
3867
4092
  }
3868
- return this.createDocumentAppendCommitFacts(
3869
- input,
3870
- appended,
3871
- nativeBackboneDocumentIndex,
4093
+ return await runAfterDocumentCommit(
4094
+ input.options,
4095
+ appended.appendCommit.hash,
4096
+ () =>
4097
+ this.createDocumentAppendCommitFacts(
4098
+ input,
4099
+ appended,
4100
+ nativeBackboneDocumentIndex,
4101
+ ),
3872
4102
  );
3873
4103
  }
3874
4104
 
3875
4105
  private async commitNativeDocumentAppendMany(
3876
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,
3877
4126
  ): Promise<DocumentAppendManyCommitFacts<T, I> | undefined> {
3878
4127
  const trustedLog = asTrustedDocumentSharedLog(this.log);
3879
4128
  const nativeBackboneDocumentIndexes =
@@ -3904,20 +4153,18 @@ export class Documents<
3904
4153
  }
3905
4154
  return [next];
3906
4155
  });
3907
- const appended = await trustedLog.appendLocallyPreparedPayloadsManyIndependent(
3908
- input.puts.map((put) => put.operationPayloadBytes),
3909
- {
3910
- ...input.options,
3911
- replicate: input.options?.replicate,
3912
- },
3913
- {
3914
- resolveTrimmedEntries: input.resolveTrimmedEntries,
3915
- nexts,
3916
- nativeBackboneDocumentIndexes:
3917
- nativeBackboneDocumentIndexInputs,
3918
- retainMaterializationBytes: this._hasLogTrim,
3919
- },
3920
- );
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
+ );
3921
4168
  if (!appended) {
3922
4169
  if (this.isNativeMode()) {
3923
4170
  throw this.nativeModeError(
@@ -3926,38 +4173,44 @@ export class Documents<
3926
4173
  }
3927
4174
  return undefined;
3928
4175
  }
3929
- const appendInputs = input.puts.map((put, index) => ({
3930
- input: nativeBackboneDocumentIndexes?.[index]
3931
- ? {
3932
- ...put,
3933
- nativeBackboneDocumentIndex:
3934
- nativeBackboneDocumentIndexes[index],
3935
- }
3936
- : put,
3937
- appended: (() => {
3938
- const materializeEntry = appended.materializeEntries?.[index];
3939
- 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;
3940
4205
  return {
3941
- get entry() {
3942
- return (entry ??= materializeEntry
3943
- ? materializeEntry()
3944
- : appended.entries[index]!);
4206
+ get entries() {
4207
+ return (entries ??= commits.map((commit) => commit.entry));
3945
4208
  },
3946
- removed: [],
3947
- appendCommit: appended.appendCommits[index]!,
4209
+ removed: appended.removed,
4210
+ commits,
3948
4211
  };
3949
- })(),
3950
- }));
3951
- const commits =
3952
- await this.createDocumentAppendCommitFactsBatch(appendInputs);
3953
- let entries: Entry<Operation>[] | undefined;
3954
- return {
3955
- get entries() {
3956
- return (entries ??= commits.map((commit) => commit.entry));
3957
4212
  },
3958
- removed: appended.removed,
3959
- commits,
3960
- };
4213
+ );
3961
4214
  }
3962
4215
 
3963
4216
  private prepareNativeBackboneDocumentIndexCommitBatch(
@@ -3980,9 +4233,7 @@ export class Documents<
3980
4233
  firstAsyncCommit,
3981
4234
  ...inputs
3982
4235
  .slice(firstAsyncIndex + 1)
3983
- .map((input) =>
3984
- this.prepareNativeBackboneDocumentIndexCommit(input),
3985
- ),
4236
+ .map((input) => this.prepareNativeBackboneDocumentIndexCommit(input)),
3986
4237
  ]).then((resolvedCommits) => {
3987
4238
  for (const commit of resolvedCommits) {
3988
4239
  if (!commit) {
@@ -3993,9 +4244,7 @@ export class Documents<
3993
4244
  return commits;
3994
4245
  });
3995
4246
  for (let i = 0; i < inputs.length; i++) {
3996
- const commit = this.prepareNativeBackboneDocumentIndexCommit(
3997
- inputs[i]!,
3998
- );
4247
+ const commit = this.prepareNativeBackboneDocumentIndexCommit(inputs[i]!);
3999
4248
  if (isPromiseLike(commit)) {
4000
4249
  return finishAsync(i, commit);
4001
4250
  }
@@ -4203,16 +4452,16 @@ export class Documents<
4203
4452
  suffix: contextAccessors.getContextBytes(),
4204
4453
  });
4205
4454
  },
4206
- nativeBackboneDocumentIndexCommitted:
4207
- appended.appendCommit.nativeBackboneDocumentIndexCommitted,
4208
- nativeBackboneDocumentIndexTrimmedHeadsProcessed:
4209
- appended.appendCommit.nativeBackboneDocumentIndexTrimmedHeadsProcessed,
4210
- get nativeBackboneDocumentIndex() {
4211
- return getNativeBackboneDocumentIndex();
4212
- },
4213
- unique: input.unique,
4214
- existing: input.existing,
4215
- };
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
+ };
4216
4465
  }
4217
4466
 
4218
4467
  private async createDocumentAppendCommitFactsBatch(
@@ -4226,9 +4475,7 @@ export class Documents<
4226
4475
  const nativePreviousContext =
4227
4476
  append.documentPreviousContext == null
4228
4477
  ? undefined
4229
- : nativeDocumentContextFactsAsContext(
4230
- append.documentPreviousContext,
4231
- );
4478
+ : nativeDocumentContextFactsAsContext(append.documentPreviousContext);
4232
4479
  const nativePreviousIndexedContext = nativePreviousContext
4233
4480
  ? ({
4234
4481
  id: input.key,
@@ -4264,9 +4511,7 @@ export class Documents<
4264
4511
  const nativePreviousContext =
4265
4512
  append.documentPreviousContext == null
4266
4513
  ? undefined
4267
- : nativeDocumentContextFactsAsContext(
4268
- append.documentPreviousContext,
4269
- );
4514
+ : nativeDocumentContextFactsAsContext(append.documentPreviousContext);
4270
4515
  const nativePreviousIndexedContext = nativePreviousContext
4271
4516
  ? ({
4272
4517
  id: row.input.key,
@@ -4280,7 +4525,7 @@ export class Documents<
4280
4525
  ? {
4281
4526
  ...row.input,
4282
4527
  existing: nativePreviousIndexedContext,
4283
- }
4528
+ }
4284
4529
  : row.input;
4285
4530
  const contextPlan = contextPlans?.[index];
4286
4531
  if (!contextPlan) {
@@ -4331,7 +4576,9 @@ export class Documents<
4331
4576
  (append.nativeBackboneDocumentIndexCommitted
4332
4577
  ? undefined
4333
4578
  : this._nativeBackboneDocumentIndexEnabled
4334
- ? asTrustedDocumentIndex(this._index).prepareNativeBackboneDocumentIndexCommitWithAppendFacts(
4579
+ ? asTrustedDocumentIndex(
4580
+ this._index,
4581
+ ).prepareNativeBackboneDocumentIndexCommitWithAppendFacts(
4335
4582
  input.document,
4336
4583
  input.documentBytes,
4337
4584
  context,
@@ -4405,6 +4652,56 @@ export class Documents<
4405
4652
  });
4406
4653
  }
4407
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
+
4408
4705
  private handlePreparedPlainPutCommit(
4409
4706
  commit: DocumentAppendCommitFacts<T, I>,
4410
4707
  ): MaybePromise<void> {
@@ -4425,7 +4722,9 @@ export class Documents<
4425
4722
  if (this._mode === "native") {
4426
4723
  return true;
4427
4724
  }
4428
- return asTrustedDocumentIndex(this._index)._persistPreparedNativeBackboneDocumentIndexStoredWithContext(
4725
+ return asTrustedDocumentIndex(
4726
+ this._index,
4727
+ )._persistPreparedNativeBackboneDocumentIndexStoredWithContext(
4429
4728
  commit.key,
4430
4729
  commit.context,
4431
4730
  commit.nativeBackboneDocumentIndex,
@@ -4597,7 +4896,10 @@ export class Documents<
4597
4896
  modified.add(commit.key.primitive);
4598
4897
  return finishRemoved();
4599
4898
  }
4600
- const withContext = coerceWithContext(commit.document, commit.context);
4899
+ const withContext = coerceWithContext(
4900
+ commit.document,
4901
+ commit.context,
4902
+ );
4601
4903
  if (commit.nativeBackboneDocumentIndex?.indexable) {
4602
4904
  return finishIndexed(
4603
4905
  coerceWithIndexed(
@@ -4624,16 +4926,17 @@ export class Documents<
4624
4926
  : mapMaybePromise(persisted, finishCommitted);
4625
4927
  }
4626
4928
  if (commit.nativeBackboneDocumentIndex) {
4627
- const nativePreparedIndexPut =
4628
- asTrustedDocumentIndex(this._index)._putPreparedNativeBackboneDocumentIndexWithContext(
4629
- commit.document,
4630
- commit.key,
4631
- commit.context,
4632
- commit.nativeBackboneDocumentIndex,
4633
- {
4634
- replace: existing != null,
4635
- },
4636
- );
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
+ );
4637
4940
  if (nativePreparedIndexPut !== undefined) {
4638
4941
  return mapMaybePromise(nativePreparedIndexPut, finishIndexed);
4639
4942
  }
@@ -4831,27 +5134,25 @@ export class Documents<
4831
5134
  private async handlePreparedPlainPutManyCommit(
4832
5135
  commit: DocumentAppendManyCommitFacts<T, I>,
4833
5136
  ): Promise<void> {
4834
- if (
4835
- !this.hasDocumentChangeConsumers() &&
4836
- commit.removed.length === 0
4837
- ) {
4838
- const stored =
4839
- await asTrustedDocumentIndex(this._index)._putManyPreparedNativeBackboneDocumentIndexStored(
4840
- commit.commits.map((put) => {
4841
- const existing =
4842
- put.unique || put.existing === null ? null : put.existing;
4843
- return {
4844
- value: put.document,
4845
- id: put.key,
4846
- context: put.context,
4847
- encodedValueParts: put.contextualEncodedValueParts,
4848
- nativeDocumentIndex: put.nativeBackboneDocumentIndex,
4849
- options: {
4850
- replace: existing != null,
4851
- },
4852
- };
4853
- }),
4854
- );
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
+ );
4855
5156
  if (stored === true) {
4856
5157
  return;
4857
5158
  }
@@ -4903,18 +5204,19 @@ export class Documents<
4903
5204
  },
4904
5205
  })),
4905
5206
  );
4906
- indexedDocuments ??=
4907
- await asTrustedDocumentIndex(this._index)._putManyPreparedNativeBackboneDocumentIndexWithContext(
4908
- putsToIndex.map((put) => ({
4909
- value: put.document,
4910
- id: put.key,
4911
- context: put.context,
4912
- nativeDocumentIndex: put.nativeBackboneDocumentIndex,
4913
- options: {
4914
- replace: put.replace,
4915
- },
4916
- })),
4917
- );
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
+ );
4918
5220
  if (indexedDocuments) {
4919
5221
  documentsChanged.added.push(...indexedDocuments);
4920
5222
  } else {
@@ -5006,11 +5308,13 @@ export class Documents<
5006
5308
  ): Promise<boolean> {
5007
5309
  const key = await this._index.getIdentityIndexedKeyByHead(head);
5008
5310
  if (key) {
5009
- if (await this.collectRemovedDocumentChangeFromIndexedKey(
5010
- key,
5011
- modified,
5012
- documentsChanged,
5013
- )) {
5311
+ if (
5312
+ await this.collectRemovedDocumentChangeFromIndexedKey(
5313
+ key,
5314
+ modified,
5315
+ documentsChanged,
5316
+ )
5317
+ ) {
5014
5318
  return true;
5015
5319
  }
5016
5320
  }
@@ -5284,17 +5588,20 @@ export class Documents<
5284
5588
  gid: entry.meta.gid,
5285
5589
  size: encodePutOperationPayload(payload.data).byteLength,
5286
5590
  });
5287
- const nativeDocumentIndex =
5288
- asTrustedDocumentIndex(this._index).prepareNativeBackboneDocumentIndexCommitWithAppendFacts(
5289
- value,
5290
- payload.data,
5291
- context,
5292
- { entryPublicKeys: entry.publicKeys },
5293
- );
5591
+ const nativeDocumentIndex = asTrustedDocumentIndex(
5592
+ this._index,
5593
+ ).prepareNativeBackboneDocumentIndexCommitWithAppendFacts(
5594
+ value,
5595
+ payload.data,
5596
+ context,
5597
+ { entryPublicKeys: entry.publicKeys },
5598
+ );
5294
5599
  if (!nativeDocumentIndex) {
5295
5600
  return;
5296
5601
  }
5297
- return asTrustedDocumentIndex(this._index)._putPreparedNativeBackboneDocumentIndexWithContext(
5602
+ return asTrustedDocumentIndex(
5603
+ this._index,
5604
+ )._putPreparedNativeBackboneDocumentIndexWithContext(
5298
5605
  value,
5299
5606
  key,
5300
5607
  context,
@@ -5323,16 +5630,19 @@ export class Documents<
5323
5630
  gid: entry.meta.gid,
5324
5631
  size: encodePutOperationPayload(payload.data).byteLength,
5325
5632
  });
5326
- const nativeDocumentIndex =
5327
- asTrustedDocumentIndex(this._index).prepareNativeBackboneDocumentIndexStoredCommitWithAppendFacts(
5328
- payload.data,
5329
- context,
5330
- { entryPublicKeys: entry.publicKeys },
5331
- );
5633
+ const nativeDocumentIndex = asTrustedDocumentIndex(
5634
+ this._index,
5635
+ ).prepareNativeBackboneDocumentIndexStoredCommitWithAppendFacts(
5636
+ payload.data,
5637
+ context,
5638
+ { entryPublicKeys: entry.publicKeys },
5639
+ );
5332
5640
  if (!nativeDocumentIndex) {
5333
5641
  return;
5334
5642
  }
5335
- return asTrustedDocumentIndex(this._index)._putPreparedNativeBackboneDocumentIndexStoredWithContext(
5643
+ return asTrustedDocumentIndex(
5644
+ this._index,
5645
+ )._putPreparedNativeBackboneDocumentIndexStoredWithContext(
5336
5646
  key,
5337
5647
  context,
5338
5648
  nativeDocumentIndex,
@@ -5357,6 +5667,11 @@ export class Documents<
5357
5667
  id: indexerTypes.Ideable | indexerTypes.IdKey,
5358
5668
  options?: SharedAppendOptions<Operation>,
5359
5669
  ) {
5670
+ if (hasPersistedDelivery(options)) {
5671
+ throw new Error(
5672
+ "persisted delivery is not supported for document deletes",
5673
+ );
5674
+ }
5360
5675
  return this._documentBackend.del(id, options);
5361
5676
  }
5362
5677
 
@@ -5500,11 +5815,11 @@ export class Documents<
5500
5815
  },
5501
5816
  removed: appended.removed,
5502
5817
  };
5503
- if (appended.appendCommit.nativeBackboneDocumentDeleteCommitted) {
5504
- this._index.clearResolvedCacheForKeys([key]);
5505
- } else {
5506
- await this._index.delManyMaybe([key]);
5507
- }
5818
+ if (appended.appendCommit.nativeBackboneDocumentDeleteCommitted) {
5819
+ this._index.clearResolvedCacheForKeys([key]);
5820
+ } else {
5821
+ await this._index.delManyMaybe([key]);
5822
+ }
5508
5823
  if (documentsChanged && removedDocument) {
5509
5824
  documentsChanged.removed.push(removedDocument);
5510
5825
  this.dispatchDocumentChangeIfObserved(documentsChanged);
@@ -5604,8 +5919,7 @@ export class Documents<
5604
5919
  const existing =
5605
5920
  reference?.unique || reference?.existing === null
5606
5921
  ? null
5607
- : isReferencedAppendEntry &&
5608
- reference?.existing !== undefined
5922
+ : isReferencedAppendEntry && reference?.existing !== undefined
5609
5923
  ? reference.existing
5610
5924
  : this.getNativeModeIndexedContext(key) || null;
5611
5925
  if (!this.strictHistory && existing) {
@@ -5634,9 +5948,7 @@ export class Documents<
5634
5948
  let value =
5635
5949
  isReferencedAppendEntry && reference?.document
5636
5950
  ? reference.document
5637
- : this.index.valueEncoding.decoder(
5638
- new Uint8Array(payload.data),
5639
- );
5951
+ : this.index.valueEncoding.decoder(new Uint8Array(payload.data));
5640
5952
 
5641
5953
  // get index key from value
5642
5954
  const key =