@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.
@@ -35,16 +35,16 @@ var __runInitializers = (this && this.__runInitializers) || function (thisArg, i
35
35
  import { BorshError, deserialize, field, serialize, variant, } from "@dao-xyz/borsh";
36
36
  import { AccessError } from "@peerbit/crypto";
37
37
  import { Context, NotFoundError, } from "@peerbit/document-interface";
38
- import { extractDocumentFieldSimple, initializeDocumentRust, planDocumentContext, planDocumentContextBatch, tryPlanDocumentContext, tryPlanDocumentContextBatch, } from "./native-rust.js";
39
38
  import * as indexerTypes from "@peerbit/indexer-interface";
40
39
  import { Entry, EntryType, LamportClock, ShallowEntry, ShallowMeta, Timestamp, entryV0PlainPayloadDataFromStorage, } from "@peerbit/log";
41
40
  import { logger as loggerFn } from "@peerbit/logger";
42
41
  import { Program } from "@peerbit/program";
43
- import { SharedLog, } from "@peerbit/shared-log";
44
- import { detachCanPerformCallbackProperties, detachEntryPayloadForCallback, } from "./callback-detachment.js";
42
+ import { PersistedDeliveryError, SharedLog, } from "@peerbit/shared-log";
43
+ import { detachCanPerformCallbackProperties, detachEntryForCallback, detachEntryPayloadForCallback, } from "./callback-detachment.js";
45
44
  import { MAX_BATCH_SIZE } from "./constants.js";
45
+ import { extractDocumentFieldSimple, initializeDocumentRust, planDocumentContext, planDocumentContextBatch, tryPlanDocumentContext, tryPlanDocumentContextBatch, } from "./native-rust.js";
46
46
  import { BORSH_ENCODING_OPERATION, DeleteOperation, PutOperation, PutWithKeyOperation, coerceDeleteOperation, isDeleteOperation, isPutOperation, } from "./operation.js";
47
- import { createCanPerformPolicyEvaluator, createCanPerformDeletePolicyEvaluator, getCanPerformPolicyDescriptor, canPerformPolicyDeleteFieldPaths, canPerformPolicyNeedsDeleteValue, canPerformPolicyNeedsPreviousEntries, canPerformPolicyPutNeedsEntryPublicKeys, canPerformPolicySignedByFieldPaths, } from "./policy.js";
47
+ import { canPerformPolicyDeleteFieldPaths, canPerformPolicyNeedsDeleteValue, canPerformPolicyNeedsPreviousEntries, canPerformPolicyPutNeedsEntryPublicKeys, canPerformPolicySignedByFieldPaths, createCanPerformDeletePolicyEvaluator, createCanPerformPolicyEvaluator, getCanPerformPolicyDescriptor, } from "./policy.js";
48
48
  import { isResultIndexedValue } from "./result-shape.js";
49
49
  import { DocumentIndex, INDEX_CONTEXT_SHAPE, coerceWithContext, coerceWithIndexed, coerceWithLazyIndexed, encodeContextSuffix as encodeDocumentContextSuffix, } from "./search.js";
50
50
  import { getDocumentTransformDescriptor, } from "./transform.js";
@@ -111,6 +111,54 @@ const encodePutOperationPayload = (data) => {
111
111
  return encoded;
112
112
  };
113
113
  const toContextBigInt = (value) => typeof value === "bigint" ? value : BigInt(value);
114
+ const hasPersistedDelivery = (options) => typeof options?.delivery === "object" &&
115
+ options.delivery.reliability === "persisted";
116
+ const withoutPersistedDelivery = (options) => {
117
+ if (!hasPersistedDelivery(options)) {
118
+ return options;
119
+ }
120
+ return {
121
+ ...options,
122
+ target: "none",
123
+ delivery: false,
124
+ replicate: false,
125
+ };
126
+ };
127
+ const runAfterDocumentCommit = (options, committedHashes, fn) => {
128
+ if (!hasPersistedDelivery(options))
129
+ return fn();
130
+ const resolvedHashes = typeof committedHashes === "function" ? committedHashes() : committedHashes;
131
+ const hashes = typeof resolvedHashes === "string" ? [resolvedHashes] : resolvedHashes;
132
+ try {
133
+ const result = fn();
134
+ if (isPromiseLike(result)) {
135
+ return result.catch((error) => {
136
+ throw new PersistedDeliveryError(error, hashes);
137
+ });
138
+ }
139
+ return result;
140
+ }
141
+ catch (error) {
142
+ throw new PersistedDeliveryError(error, hashes);
143
+ }
144
+ };
145
+ const runWithTrustedLocalCommitEvidence = (options, evidence, fn) => {
146
+ if (!evidence || !hasPersistedDelivery(options))
147
+ return fn();
148
+ const classify = (error) => {
149
+ if (evidence.committedHashes.size > 0) {
150
+ throw new PersistedDeliveryError(error, evidence.committedHashes);
151
+ }
152
+ throw error;
153
+ };
154
+ try {
155
+ const result = fn();
156
+ return isPromiseLike(result) ? result.catch(classify) : result;
157
+ }
158
+ catch (error) {
159
+ return classify(error);
160
+ }
161
+ };
114
162
  const NATIVE_LOCAL_PUT_OPTIONS = Object.freeze({
115
163
  replicate: false,
116
164
  target: "none",
@@ -136,6 +184,7 @@ const cachedNativeLocalPutOptions = (options) => {
136
184
  }
137
185
  return options.unique === true ? NATIVE_LOCAL_UNIQUE_PUT_OPTIONS : undefined;
138
186
  };
187
+ const persistedDeliveryAlreadySettled = new WeakSet();
139
188
  class CompatDocumentBackend {
140
189
  putImpl;
141
190
  putManyImpl;
@@ -186,15 +235,7 @@ class NativeDocumentBackend {
186
235
  useNativeExistingDocumentContext,
187
236
  requiredPreviousSignerPublicKey,
188
237
  existing,
189
- }), (documentAppendCommit) => mapMaybePromise(this.context.handlePreparedPlainPutCommit(documentAppendCommit), () => {
190
- this.context.keepEntry(documentAppendCommit.append.hash);
191
- return {
192
- get entry() {
193
- return documentAppendCommit.entry;
194
- },
195
- removed: documentAppendCommit.removed,
196
- };
197
- }));
238
+ }), (documentAppendCommit) => this.context.finishPreparedPlainPutCommit(documentAppendCommit, options));
198
239
  };
199
240
  const assertPolicyAndCommit = (existingContext, previousSignerPublicKey) => {
200
241
  const existingHead = this.context.getIndexedContextHead(existingContext);
@@ -239,6 +280,9 @@ class NativeDocumentBackend {
239
280
  }
240
281
  const prepared = docs.map((doc) => this.context.preparePlainPut(doc));
241
282
  if (this.context.hasDuplicatePreparedPutKeys(prepared)) {
283
+ if (hasPersistedDelivery(options)) {
284
+ throw this.context.nativeModeError("requires distinct document keys for persisted putMany");
285
+ }
242
286
  const results = [];
243
287
  for (const doc of docs) {
244
288
  results.push(await this.put(doc, putOptions));
@@ -318,17 +362,7 @@ class NativeDocumentBackend {
318
362
  if (!documentAppendCommit) {
319
363
  throw this.context.nativeModeError("requires native batched payload append support");
320
364
  }
321
- return mapMaybePromise(this.context.handlePreparedPlainPutManyCommit(documentAppendCommit), () => {
322
- for (const commit of documentAppendCommit.commits) {
323
- this.context.keepEntry(commit.append.hash);
324
- }
325
- return {
326
- get entries() {
327
- return documentAppendCommit.entries;
328
- },
329
- removed: documentAppendCommit.removed,
330
- };
331
- });
365
+ return this.context.finishPreparedPlainPutManyCommit(documentAppendCommit, options);
332
366
  });
333
367
  }
334
368
  del(id, options) {
@@ -337,6 +371,10 @@ class NativeDocumentBackend {
337
371
  }
338
372
  }
339
373
  const nativeDocumentContextFactsAsContext = (facts) => facts;
374
+ // Keep the public putMany result shape unchanged while carrying the native
375
+ // commit/coordinate facts to the internal persisted-delivery seam. The result
376
+ // remains the lifetime owner of the lazy full-entry materializer.
377
+ const persistedDocumentAppendDelivery = new WeakMap();
340
378
  const asTrustedDocumentSharedLog = (log) => log;
341
379
  const documentIndexStoreKey = (id) => {
342
380
  const key = indexerTypes.toIdeable(id);
@@ -430,11 +468,7 @@ let Documents = (() => {
430
468
  const boundMethods = new Map();
431
469
  const detachedFromEntry = (entry) => {
432
470
  const fromEntry = Reflect.get(domain, "fromEntry", domain);
433
- return Reflect.apply(fromEntry, domain, [
434
- entry instanceof Entry
435
- ? detachEntryPayloadForCallback(entry)
436
- : entry,
437
- ]);
471
+ return Reflect.apply(fromEntry, domain, [detachEntryForCallback(entry)]);
438
472
  };
439
473
  const facade = Object.create(Object.getPrototypeOf(domain));
440
474
  let callbackDomain;
@@ -490,6 +524,22 @@ let Documents = (() => {
490
524
  });
491
525
  return callbackDomain;
492
526
  }
527
+ detachTrimEntryCallback(option) {
528
+ const filter = option?.filter;
529
+ if (!option || !filter?.canTrim) {
530
+ return option;
531
+ }
532
+ const canTrim = filter.canTrim;
533
+ return {
534
+ ...option,
535
+ filter: {
536
+ ...filter,
537
+ canTrim: function (entry) {
538
+ return Reflect.apply(canTrim, this, [detachEntryForCallback(entry)]);
539
+ },
540
+ },
541
+ };
542
+ }
493
543
  createNativeDocumentBackendContext() {
494
544
  return {
495
545
  assertPlainPutSupported: (doc, options) => {
@@ -519,12 +569,9 @@ let Documents = (() => {
519
569
  },
520
570
  commitNativeDocumentAppend: (input) => this.commitNativeDocumentAppend(input),
521
571
  commitNativeDocumentAppendMany: (input) => this.commitNativeDocumentAppendMany(input),
522
- handlePreparedPlainPutCommit: (commit) => this.handlePreparedPlainPutCommit(commit),
523
- handlePreparedPlainPutManyCommit: (commit) => this.handlePreparedPlainPutManyCommit(commit),
572
+ finishPreparedPlainPutCommit: (commit, options) => this.finishPreparedPlainPutCommit(commit, options),
573
+ finishPreparedPlainPutManyCommit: (commit, options) => this.finishPreparedPlainPutManyCommit(commit, options),
524
574
  deleteDocument: (id, options) => this.delNativeDocumentBackend(id, options),
525
- keepEntry: (hash) => {
526
- this.keepCache?.add(hash);
527
- },
528
575
  nativeModeError: (message) => this.nativeModeError(message),
529
576
  };
530
577
  }
@@ -715,10 +762,14 @@ let Documents = (() => {
715
762
  if (options?.replicate === true) {
716
763
  unsupported.push("replicated put");
717
764
  }
718
- if (options?.target && options.target !== "none") {
765
+ if (options?.target &&
766
+ options.target !== "none" &&
767
+ !(hasPersistedDelivery(options) && options.target === "replicators")) {
719
768
  unsupported.push("non-local target");
720
769
  }
721
- if (options?.delivery !== undefined && options.delivery !== false) {
770
+ if (options?.delivery !== undefined &&
771
+ options.delivery !== false &&
772
+ !hasPersistedDelivery(options)) {
722
773
  unsupported.push("delivery");
723
774
  }
724
775
  if (options?.checkRemote) {
@@ -793,7 +844,8 @@ let Documents = (() => {
793
844
  return this.nativeFieldValueMatchesPublicKey(value, localPublicKey);
794
845
  }
795
846
  nativeFieldValueMatchesPublicKey(value, publicKey) {
796
- const localRawPublicKey = publicKey.publicKey;
847
+ const localRawPublicKey = publicKey
848
+ .publicKey;
797
849
  return (value instanceof Uint8Array &&
798
850
  (bytesEqual(value, publicKey.bytes) ||
799
851
  (localRawPublicKey ? bytesEqual(value, localRawPublicKey) : false)));
@@ -949,6 +1001,9 @@ let Documents = (() => {
949
1001
  return;
950
1002
  }
951
1003
  const unsupported = this.unsupportedNativePutOptions(options);
1004
+ if (options && hasPersistedDelivery(options)) {
1005
+ unsupported.push("delivery");
1006
+ }
952
1007
  if (options?.unique !== undefined) {
953
1008
  unsupported.push("unique delete");
954
1009
  }
@@ -992,6 +1047,9 @@ let Documents = (() => {
992
1047
  if (!this.isNativeMode()) {
993
1048
  return options;
994
1049
  }
1050
+ if (hasPersistedDelivery(options)) {
1051
+ return options;
1052
+ }
995
1053
  if (options?.replicate === false && options.target === "none") {
996
1054
  return options;
997
1055
  }
@@ -1320,8 +1378,7 @@ let Documents = (() => {
1320
1378
  this._documentInternalChangeListenerCount = Math.max(0, this._documentChangeListenerCount - changeListenersBeforeIndexOpen);
1321
1379
  this._nativeDocumentFieldExtractionPlans ??= new Map();
1322
1380
  this._nativeDocumentFieldExtractionPlans.clear();
1323
- this._nativeDocumentIdExtractionPlan =
1324
- asTrustedDocumentIndex(this._index).getNativeDocumentFieldExtractionPlan(idProperty);
1381
+ this._nativeDocumentIdExtractionPlan = asTrustedDocumentIndex(this._index).getNativeDocumentFieldExtractionPlan(idProperty);
1325
1382
  // B12: the historical document->log compatibility mapping (6 -> log v8,
1326
1383
  // 7 -> log v9) is retired; the rejection at the top of open() fires for
1327
1384
  // any defined value before this point.
@@ -1371,11 +1428,7 @@ let Documents = (() => {
1371
1428
  const keep = options?.keep;
1372
1429
  keepFunction = keep
1373
1430
  ? function (entry) {
1374
- return Reflect.apply(keep, this, [
1375
- entry instanceof Entry
1376
- ? detachEntryPayloadForCallback(entry)
1377
- : entry,
1378
- ]);
1431
+ return Reflect.apply(keep, this, [detachEntryForCallback(entry)]);
1379
1432
  }
1380
1433
  : undefined;
1381
1434
  }
@@ -1384,7 +1437,7 @@ let Documents = (() => {
1384
1437
  canReplicate: options?.canReplicate,
1385
1438
  canAppend: this.canAppend.bind(this),
1386
1439
  onChange: this.handleChanges.bind(this),
1387
- trim: options?.log?.trim,
1440
+ trim: this.detachTrimEntryCallback(options?.log?.trim),
1388
1441
  appendDurability: options?.appendDurability,
1389
1442
  nativeBackbone: options?.nativeBackbone,
1390
1443
  nativeGraph: options?.nativeGraph,
@@ -1650,7 +1703,8 @@ let Documents = (() => {
1650
1703
  : reference.document);
1651
1704
  }
1652
1705
  else {
1653
- keyValue = await this.getNativeDocumentIdFromPutOperation(putOperation);
1706
+ keyValue =
1707
+ await this.getNativeDocumentIdFromPutOperation(putOperation);
1654
1708
  if (keyValue == null) {
1655
1709
  if (this.isNativeMode()) {
1656
1710
  return false;
@@ -1941,7 +1995,28 @@ let Documents = (() => {
1941
1995
  };
1942
1996
  }
1943
1997
  async put(doc, options) {
1944
- return this._documentBackend.put(doc, options);
1998
+ if (hasPersistedDelivery(options)) {
1999
+ asTrustedDocumentSharedLog(this.log).assertPersistedDeliveryOptions(options);
2000
+ }
2001
+ const result = await this._documentBackend.put(doc, options);
2002
+ if (!hasPersistedDelivery(options) ||
2003
+ persistedDeliveryAlreadySettled.has(result)) {
2004
+ return result;
2005
+ }
2006
+ const entry = result.entry;
2007
+ await this.deliverPersistedDocumentEntries([entry], options);
2008
+ return {
2009
+ get entry() {
2010
+ return entry;
2011
+ },
2012
+ removed: result.removed,
2013
+ };
2014
+ }
2015
+ deliverPersistedDocumentEntries(entries, options) {
2016
+ return asTrustedDocumentSharedLog(this.log).deliverPersistedEntries(entries, options);
2017
+ }
2018
+ deliverPersistedDocumentAppendCommits(delivery, options) {
2019
+ return asTrustedDocumentSharedLog(this.log).deliverPersistedAppendCommits(delivery.appendCommits, delivery.materializeEntries, options);
1945
2020
  }
1946
2021
  async putCompatDocumentBackend(doc, options) {
1947
2022
  const putOptions = this.normalizeNativeModePutOptions(options);
@@ -1971,6 +2046,7 @@ let Documents = (() => {
1971
2046
  const operation = "operation" in prepared
1972
2047
  ? prepared.operation
1973
2048
  : new PutOperation({ data: prepared.encodedDocument });
2049
+ const persistedDelivery = hasPersistedDelivery(putOptions);
1974
2050
  const appended = await this.log.append(operation, {
1975
2051
  ...putOptions,
1976
2052
  meta: {
@@ -1983,32 +2059,73 @@ let Documents = (() => {
1983
2059
  operation,
1984
2060
  });
1985
2061
  },
1986
- onChange: (change) => {
1987
- return this.handleChanges(change, {
2062
+ onChange: persistedDelivery
2063
+ ? (change) => runAfterDocumentCommit(putOptions, () => change.added.map(({ entry }) => entry.hash), () => mapMaybePromise(this.handleChanges(change, {
1988
2064
  document: prepared.document,
1989
2065
  operation,
1990
2066
  key: prepared.key,
1991
2067
  unique: putOptions?.unique,
1992
2068
  existing: existingLocalContext,
1993
- });
1994
- },
2069
+ }), () => {
2070
+ this.keepCache?.add(change.added[0].entry.hash);
2071
+ }))
2072
+ : (change) => this.handleChanges(change, {
2073
+ document: prepared.document,
2074
+ operation,
2075
+ key: prepared.key,
2076
+ unique: putOptions?.unique,
2077
+ existing: existingLocalContext,
2078
+ }),
1995
2079
  replicate: putOptions?.replicate,
1996
2080
  });
1997
2081
  this.keepCache?.add(appended.entry.hash);
2082
+ if (persistedDelivery) {
2083
+ persistedDeliveryAlreadySettled.add(appended);
2084
+ }
1998
2085
  return appended;
1999
2086
  }
2000
2087
  async putMany(docs, options) {
2001
- return this._documentBackend.putMany(docs, options);
2088
+ if (hasPersistedDelivery(options)) {
2089
+ asTrustedDocumentSharedLog(this.log).assertPersistedDeliveryOptions(options);
2090
+ }
2091
+ const result = await this._documentBackend.putMany(docs, options);
2092
+ if (!hasPersistedDelivery(options)) {
2093
+ return result;
2094
+ }
2095
+ const appendDelivery = persistedDocumentAppendDelivery.get(result);
2096
+ if (appendDelivery) {
2097
+ persistedDocumentAppendDelivery.delete(result);
2098
+ await this.deliverPersistedDocumentAppendCommits(appendDelivery, options);
2099
+ let entries;
2100
+ return {
2101
+ get entries() {
2102
+ return (entries ??= result.entries);
2103
+ },
2104
+ removed: result.removed,
2105
+ };
2106
+ }
2107
+ const entries = result.entries;
2108
+ if (entries.length === 0) {
2109
+ return result;
2110
+ }
2111
+ await this.deliverPersistedDocumentEntries(entries, options);
2112
+ return { entries, removed: result.removed };
2002
2113
  }
2003
2114
  async putManyCompatDocumentBackend(docs, options) {
2004
2115
  if (docs.length === 0) {
2005
2116
  return { entries: [], removed: [] };
2006
2117
  }
2007
2118
  if (!this.canUsePlainPutManyFastPath(docs, options)) {
2119
+ if (hasPersistedDelivery(options)) {
2120
+ throw new Error("persisted putMany requires the independent batched document path");
2121
+ }
2008
2122
  return this.putManySequential(docs, options);
2009
2123
  }
2010
2124
  const prepared = docs.map((doc) => this.preparePlainPut(doc));
2011
2125
  if (this.hasDuplicatePreparedPutKeys(prepared)) {
2126
+ if (hasPersistedDelivery(options)) {
2127
+ throw new Error("persisted putMany requires distinct document keys");
2128
+ }
2012
2129
  return this.putManySequential(docs, options);
2013
2130
  }
2014
2131
  const documentAppendCommit = await this.commitNativeDocumentAppendMany({
@@ -2024,18 +2141,12 @@ let Documents = (() => {
2024
2141
  options,
2025
2142
  });
2026
2143
  if (!documentAppendCommit) {
2144
+ if (hasPersistedDelivery(options)) {
2145
+ throw new Error("persisted putMany requires native batched payload append support");
2146
+ }
2027
2147
  return this.putManySequential(docs, options);
2028
2148
  }
2029
- await this.handlePreparedPlainPutManyCommit(documentAppendCommit);
2030
- for (const commit of documentAppendCommit.commits) {
2031
- this.keepCache?.add(commit.append.hash);
2032
- }
2033
- return {
2034
- get entries() {
2035
- return documentAppendCommit.entries;
2036
- },
2037
- removed: documentAppendCommit.removed,
2038
- };
2149
+ return await this.finishPreparedPlainPutManyCommit(documentAppendCommit, options);
2039
2150
  }
2040
2151
  async putManySequential(docs, options) {
2041
2152
  const entries = [];
@@ -2058,6 +2169,7 @@ let Documents = (() => {
2058
2169
  return false;
2059
2170
  }
2060
2171
  canUsePlainPutFastPath(doc, options) {
2172
+ const persistedDelivery = hasPersistedDelivery(options);
2061
2173
  return (this._mode !== "compat" &&
2062
2174
  this.canPerformAllowsPlainPutFastPath(doc) &&
2063
2175
  !this.immutable &&
@@ -2077,16 +2189,26 @@ let Documents = (() => {
2077
2189
  !options?.meta?.timestamp &&
2078
2190
  !options?.meta?.gidSeed &&
2079
2191
  options?.replicate !== true &&
2080
- (!options?.target || options.target === "none") &&
2081
- (options?.delivery === undefined || options.delivery === false) &&
2192
+ (!options?.target ||
2193
+ options.target === "none" ||
2194
+ (persistedDelivery && options.target === "replicators")) &&
2195
+ (options?.delivery === undefined ||
2196
+ options.delivery === false ||
2197
+ persistedDelivery) &&
2082
2198
  !options?.checkRemote &&
2083
2199
  options?.replicas === undefined);
2084
2200
  }
2085
2201
  canUsePlainPutManyFastPath(docs, options) {
2202
+ const persistedDelivery = hasPersistedDelivery(options);
2086
2203
  return (options?.unique === true &&
2087
2204
  options?.replicate !== true &&
2088
- options?.target === "none" &&
2089
- (options?.delivery === undefined || options.delivery === false) &&
2205
+ (options?.target === "none" ||
2206
+ (persistedDelivery &&
2207
+ (options.target === undefined ||
2208
+ options.target === "replicators"))) &&
2209
+ (options?.delivery === undefined ||
2210
+ options.delivery === false ||
2211
+ persistedDelivery) &&
2090
2212
  docs.every((doc) => this.canUsePlainPutFastPath(doc, options)));
2091
2213
  }
2092
2214
  async createPlainPutCommitPlan(prepared, existingHead, existingLocalContext, options, assumePlainPutFastPath = false) {
@@ -2135,41 +2257,43 @@ let Documents = (() => {
2135
2257
  options,
2136
2258
  unique: plan.unique,
2137
2259
  existing: plan.existing,
2138
- }), (documentAppendCommit) => {
2139
- const handled = plan.useGenericChangeHandler
2140
- ? this.handleChanges({
2141
- added: [{ head: true, entry: documentAppendCommit.entry }],
2142
- removed: documentAppendCommit.removed,
2143
- }, {
2144
- document: plan.document,
2145
- operation: documentAppendCommit.operation ??
2146
- plan.operation ??
2147
- new PutOperation({ data: plan.encodedDocument }),
2148
- key: plan.key,
2149
- unique: plan.unique,
2150
- existing: plan.existing,
2151
- })
2152
- : this.handlePreparedPlainPutCommit(documentAppendCommit);
2153
- return mapMaybePromise(handled, () => {
2154
- this.keepCache?.add(documentAppendCommit.append.hash);
2155
- return {
2156
- get entry() {
2157
- return documentAppendCommit.entry;
2260
+ }), (documentAppendCommit) => this.finishPreparedPlainPutCommit(documentAppendCommit, options, () => plan.useGenericChangeHandler
2261
+ ? this.handleChanges({
2262
+ added: [
2263
+ {
2264
+ head: true,
2265
+ entry: documentAppendCommit.entry,
2158
2266
  },
2159
- removed: documentAppendCommit.removed,
2160
- };
2161
- });
2162
- });
2267
+ ],
2268
+ removed: documentAppendCommit.removed,
2269
+ }, {
2270
+ document: plan.document,
2271
+ operation: documentAppendCommit.operation ??
2272
+ plan.operation ??
2273
+ new PutOperation({ data: plan.encodedDocument }),
2274
+ key: plan.key,
2275
+ unique: plan.unique,
2276
+ existing: plan.existing,
2277
+ })
2278
+ : this.handlePreparedPlainPutCommit(documentAppendCommit)));
2163
2279
  }
2164
2280
  commitNativeDocumentAppend(input) {
2281
+ if (!hasPersistedDelivery(input.options)) {
2282
+ return this.commitNativeDocumentAppendWithEvidence(input, undefined);
2283
+ }
2284
+ const localCommitEvidence = { committedHashes: new Set() };
2285
+ return runWithTrustedLocalCommitEvidence(input.options, localCommitEvidence, () => this.commitNativeDocumentAppendWithEvidence(input, localCommitEvidence));
2286
+ }
2287
+ commitNativeDocumentAppendWithEvidence(input, localCommitEvidence) {
2165
2288
  const trustedLog = asTrustedDocumentSharedLog(this.log);
2289
+ const localAppendOptions = withoutPersistedDelivery(input.options);
2166
2290
  const appendOptions = {
2167
- ...input.options,
2291
+ ...localAppendOptions,
2168
2292
  meta: {
2169
2293
  next: input.next,
2170
- ...input.options?.meta,
2294
+ ...localAppendOptions?.meta,
2171
2295
  },
2172
- replicate: input.options?.replicate,
2296
+ replicate: localAppendOptions?.replicate,
2173
2297
  };
2174
2298
  const prepareNativeDocumentIndexWithAppendFacts = this.createNativeBackboneDocumentIndexAppendFactsPreparer(input);
2175
2299
  const preferAppendFactsDocumentIndex = this.isNativeMode() && !!prepareNativeDocumentIndexWithAppendFacts;
@@ -2190,6 +2314,7 @@ let Documents = (() => {
2190
2314
  resolveTrimmedEntries: input.resolveTrimmedEntries,
2191
2315
  payloadData: input.operationPayloadBytes,
2192
2316
  useNativeExistingDocumentContext: input.useNativeExistingDocumentContext,
2317
+ ...(localCommitEvidence ? { localCommitEvidence } : undefined),
2193
2318
  ...(nativeDocumentIndexCommit
2194
2319
  ? {
2195
2320
  nativeBackboneDocumentIndex: this.toNativeBackboneDocumentIndexCommitInput(input, nativeDocumentIndexCommit),
@@ -2211,14 +2336,14 @@ let Documents = (() => {
2211
2336
  if (this.isNativeMode()) {
2212
2337
  throw this.nativeModeError("requires payload-backed put operations");
2213
2338
  }
2214
- return mapMaybePromise(trustedLog.appendLocallyPrepared(input.operation, appendOptions, appendProperties), (appended) => this.createNativeCheckedDocumentAppendCommitFacts(input, appended, committedNativeDocumentIndex));
2339
+ return mapMaybePromise(trustedLog.appendLocallyPrepared(input.operation, appendOptions, appendProperties), (appended) => runAfterDocumentCommit(input.options, appended.appendCommit.hash, () => this.createNativeCheckedDocumentAppendCommitFacts(input, appended, committedNativeDocumentIndex)));
2215
2340
  }
2216
2341
  const commitOnlyAppend = this.isNativeMode()
2217
2342
  ? trustedLog.appendStrictNativeDocumentPayloadCommitOnly(input.operationPayloadBytes, appendOptions, appendProperties)
2218
2343
  : trustedLog.appendLocallyPreparedPayloadCommitOnly(input.operationPayloadBytes, appendOptions, appendProperties);
2219
2344
  return mapMaybePromise(commitOnlyAppend, (commitOnly) => {
2220
2345
  if (commitOnly) {
2221
- return this.createNativeCheckedDocumentAppendCommitFacts(input, commitOnly, committedNativeDocumentIndex);
2346
+ return runAfterDocumentCommit(input.options, commitOnly.appendCommit.hash, () => this.createNativeCheckedDocumentAppendCommitFacts(input, commitOnly, committedNativeDocumentIndex));
2222
2347
  }
2223
2348
  if (this.isNativeMode()) {
2224
2349
  throw this.nativeModeError("requires native payload commit-only append");
@@ -2296,16 +2421,24 @@ let Documents = (() => {
2296
2421
  appended = await trustedLog.appendLocallyPreparedPayload(input.operationPayloadBytes, appendOptions, appendProperties);
2297
2422
  }
2298
2423
  catch (error) {
2299
- if (!(error instanceof Error) ||
2424
+ if (appendProperties.localCommitEvidence?.committedHashes.size ||
2425
+ !(error instanceof Error) ||
2300
2426
  error.message !==
2301
2427
  "appendLocallyPrepared payload-only path requires native append support") {
2302
2428
  throw error;
2303
2429
  }
2304
2430
  appended = await trustedLog.appendLocallyPrepared(new PutOperation({ data: input.documentBytes }), appendOptions, appendProperties);
2305
2431
  }
2306
- return this.createDocumentAppendCommitFacts(input, appended, nativeBackboneDocumentIndex);
2432
+ return await runAfterDocumentCommit(input.options, appended.appendCommit.hash, () => this.createDocumentAppendCommitFacts(input, appended, nativeBackboneDocumentIndex));
2307
2433
  }
2308
2434
  async commitNativeDocumentAppendMany(input) {
2435
+ if (!hasPersistedDelivery(input.options)) {
2436
+ return this.commitNativeDocumentAppendManyWithEvidence(input, undefined);
2437
+ }
2438
+ const localCommitEvidence = { committedHashes: new Set() };
2439
+ return await runWithTrustedLocalCommitEvidence(input.options, localCommitEvidence, () => this.commitNativeDocumentAppendManyWithEvidence(input, localCommitEvidence));
2440
+ }
2441
+ async commitNativeDocumentAppendManyWithEvidence(input, localCommitEvidence) {
2309
2442
  const trustedLog = asTrustedDocumentSharedLog(this.log);
2310
2443
  const nativeBackboneDocumentIndexes = await this.prepareNativeBackboneDocumentIndexCommitBatch(input.puts);
2311
2444
  const nativeBackboneDocumentIndexInputs = nativeBackboneDocumentIndexes?.map((commit, index) => this.toNativeBackboneDocumentIndexCommitInput(input.puts[index], commit, input.useNativeExistingDocumentContext === true));
@@ -2324,14 +2457,12 @@ let Documents = (() => {
2324
2457
  }
2325
2458
  return [next];
2326
2459
  });
2327
- const appended = await trustedLog.appendLocallyPreparedPayloadsManyIndependent(input.puts.map((put) => put.operationPayloadBytes), {
2328
- ...input.options,
2329
- replicate: input.options?.replicate,
2330
- }, {
2460
+ const appended = await trustedLog.appendLocallyPreparedPayloadsManyIndependent(input.puts.map((put) => put.operationPayloadBytes), withoutPersistedDelivery(input.options), {
2331
2461
  resolveTrimmedEntries: input.resolveTrimmedEntries,
2332
2462
  nexts,
2333
2463
  nativeBackboneDocumentIndexes: nativeBackboneDocumentIndexInputs,
2334
2464
  retainMaterializationBytes: this._hasLogTrim,
2465
+ ...(localCommitEvidence ? { localCommitEvidence } : undefined),
2335
2466
  });
2336
2467
  if (!appended) {
2337
2468
  if (this.isNativeMode()) {
@@ -2339,36 +2470,38 @@ let Documents = (() => {
2339
2470
  }
2340
2471
  return undefined;
2341
2472
  }
2342
- const appendInputs = input.puts.map((put, index) => ({
2343
- input: nativeBackboneDocumentIndexes?.[index]
2344
- ? {
2345
- ...put,
2346
- nativeBackboneDocumentIndex: nativeBackboneDocumentIndexes[index],
2347
- }
2348
- : put,
2349
- appended: (() => {
2350
- const materializeEntry = appended.materializeEntries?.[index];
2351
- let entry;
2352
- return {
2353
- get entry() {
2354
- return (entry ??= materializeEntry
2355
- ? materializeEntry()
2356
- : appended.entries[index]);
2357
- },
2358
- removed: [],
2359
- appendCommit: appended.appendCommits[index],
2360
- };
2361
- })(),
2362
- }));
2363
- const commits = await this.createDocumentAppendCommitFactsBatch(appendInputs);
2364
- let entries;
2365
- return {
2366
- get entries() {
2367
- return (entries ??= commits.map((commit) => commit.entry));
2368
- },
2369
- removed: appended.removed,
2370
- commits,
2371
- };
2473
+ return await runAfterDocumentCommit(input.options, () => appended.appendCommits.map((commit) => commit.hash), async () => {
2474
+ const appendInputs = input.puts.map((put, index) => ({
2475
+ input: nativeBackboneDocumentIndexes?.[index]
2476
+ ? {
2477
+ ...put,
2478
+ nativeBackboneDocumentIndex: nativeBackboneDocumentIndexes[index],
2479
+ }
2480
+ : put,
2481
+ appended: (() => {
2482
+ const materializeEntry = appended.materializeEntries?.[index];
2483
+ let entry;
2484
+ return {
2485
+ get entry() {
2486
+ return (entry ??= materializeEntry
2487
+ ? materializeEntry()
2488
+ : appended.entries[index]);
2489
+ },
2490
+ removed: [],
2491
+ appendCommit: appended.appendCommits[index],
2492
+ };
2493
+ })(),
2494
+ }));
2495
+ const commits = await this.createDocumentAppendCommitFactsBatch(appendInputs);
2496
+ let entries;
2497
+ return {
2498
+ get entries() {
2499
+ return (entries ??= commits.map((commit) => commit.entry));
2500
+ },
2501
+ removed: appended.removed,
2502
+ commits,
2503
+ };
2504
+ });
2372
2505
  }
2373
2506
  prepareNativeBackboneDocumentIndexCommitBatch(inputs) {
2374
2507
  if (!this._nativeBackboneDocumentIndexEnabled || inputs.length === 0) {
@@ -2659,6 +2792,40 @@ let Documents = (() => {
2659
2792
  }),
2660
2793
  });
2661
2794
  }
2795
+ finishPreparedPlainPutCommit(commit, options, handle = () => this.handlePreparedPlainPutCommit(commit)) {
2796
+ return runAfterDocumentCommit(options, commit.append.hash, () => mapMaybePromise(handle(), () => {
2797
+ this.keepCache?.add(commit.append.hash);
2798
+ const persistedEntry = hasPersistedDelivery(options)
2799
+ ? commit.entry
2800
+ : undefined;
2801
+ return {
2802
+ get entry() {
2803
+ return persistedEntry ?? commit.entry;
2804
+ },
2805
+ removed: commit.removed,
2806
+ };
2807
+ }));
2808
+ }
2809
+ finishPreparedPlainPutManyCommit(commit, options) {
2810
+ return runAfterDocumentCommit(options, () => commit.commits.map((item) => item.append.hash), () => mapMaybePromise(this.handlePreparedPlainPutManyCommit(commit), () => {
2811
+ for (const item of commit.commits) {
2812
+ this.keepCache?.add(item.append.hash);
2813
+ }
2814
+ const result = {
2815
+ get entries() {
2816
+ return commit.entries;
2817
+ },
2818
+ removed: commit.removed,
2819
+ };
2820
+ if (hasPersistedDelivery(options)) {
2821
+ persistedDocumentAppendDelivery.set(result, {
2822
+ appendCommits: commit.commits.map((item) => item.append),
2823
+ materializeEntries: () => commit.entries,
2824
+ });
2825
+ }
2826
+ return result;
2827
+ }));
2828
+ }
2662
2829
  handlePreparedPlainPutCommit(commit) {
2663
2830
  const shouldPrepareChange = this.hasDocumentChangeConsumers();
2664
2831
  const removedAlreadyHandled = commit.nativeBackboneDocumentIndexTrimmedHeadsProcessed === true;
@@ -2935,8 +3102,7 @@ let Documents = (() => {
2935
3102
  }
2936
3103
  }
2937
3104
  async handlePreparedPlainPutManyCommit(commit) {
2938
- if (!this.hasDocumentChangeConsumers() &&
2939
- commit.removed.length === 0) {
3105
+ if (!this.hasDocumentChangeConsumers() && commit.removed.length === 0) {
2940
3106
  const stored = await asTrustedDocumentIndex(this._index)._putManyPreparedNativeBackboneDocumentIndexStored(commit.commits.map((put) => {
2941
3107
  const existing = put.unique || put.existing === null ? null : put.existing;
2942
3108
  return {
@@ -2985,16 +3151,15 @@ let Documents = (() => {
2985
3151
  encodedValueParts: put.contextualEncodedValueParts,
2986
3152
  },
2987
3153
  })));
2988
- indexedDocuments ??=
2989
- await asTrustedDocumentIndex(this._index)._putManyPreparedNativeBackboneDocumentIndexWithContext(putsToIndex.map((put) => ({
2990
- value: put.document,
2991
- id: put.key,
2992
- context: put.context,
2993
- nativeDocumentIndex: put.nativeBackboneDocumentIndex,
2994
- options: {
2995
- replace: put.replace,
2996
- },
2997
- })));
3154
+ indexedDocuments ??= await asTrustedDocumentIndex(this._index)._putManyPreparedNativeBackboneDocumentIndexWithContext(putsToIndex.map((put) => ({
3155
+ value: put.document,
3156
+ id: put.key,
3157
+ context: put.context,
3158
+ nativeDocumentIndex: put.nativeBackboneDocumentIndex,
3159
+ options: {
3160
+ replace: put.replace,
3161
+ },
3162
+ })));
2998
3163
  if (indexedDocuments) {
2999
3164
  documentsChanged.added.push(...indexedDocuments);
3000
3165
  }
@@ -3297,6 +3462,9 @@ let Documents = (() => {
3297
3462
  return resolved ? resolved : undefined;
3298
3463
  }
3299
3464
  async del(id, options) {
3465
+ if (hasPersistedDelivery(options)) {
3466
+ throw new Error("persisted delivery is not supported for document deletes");
3467
+ }
3300
3468
  return this._documentBackend.del(id, options);
3301
3469
  }
3302
3470
  async delCompatDocumentBackend(id, options) {
@@ -3481,8 +3649,7 @@ let Documents = (() => {
3481
3649
  }
3482
3650
  const existing = reference?.unique || reference?.existing === null
3483
3651
  ? null
3484
- : isReferencedAppendEntry &&
3485
- reference?.existing !== undefined
3652
+ : isReferencedAppendEntry && reference?.existing !== undefined
3486
3653
  ? reference.existing
3487
3654
  : this.getNativeModeIndexedContext(key) || null;
3488
3655
  if (!this.strictHistory && existing) {