@powerhousedao/reactor 6.2.2-dev.4 → 6.2.2-dev.40

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/dist/index.js CHANGED
@@ -1,10 +1,10 @@
1
- import { A as parsePagingOptions, C as DuplicateManifestError, D as DocumentDeletedError, E as ModuleNotFoundError, O as DocumentNotFoundError, S as CollectionMembershipCache, T as InvalidModuleError, _ as KyselyWriteCache, a as createForwardingPoolInstrumentation, b as createConsistencyToken, c as DuplicateOperationError, d as KyselyKeyframeStore, f as DocumentModelRegistry, g as EventBus, h as KyselyExecutionScope, i as runMigrations, j as throwIfAborted, k as matchesScope, l as OptimisticLockError, m as DriveCollectionId, n as REACTOR_SCHEMA, o as instrumentPgPool, p as SimpleJobExecutor, r as getMigrationStatus, s as KyselyOperationStore, t as DEFAULT_DRIVE_CONTAINER_TYPES, u as RevisionMismatchError, v as KyselyOperationIndex, w as DuplicateModuleError, x as createEmptyConsistencyToken, y as DocumentMetaCache } from "./drive-container-types-DpJp2AmE.js";
1
+ import { A as OptimisticLockError, B as ExcessiveReshuffleError, C as DocumentMetaCache, D as APPEND_CONDITION_FAILED_PREFIX, E as CollectionMembershipCache, F as ModuleNotFoundError, H as matchesScope, I as AuthTimestampNotMonotonicError, L as AuthorizationDeniedError, M as DuplicateManifestError, N as DuplicateModuleError, O as AppendConditionFailedError, P as InvalidModuleError, R as DocumentDeletedError, S as KyselyOperationIndex, T as createEmptyConsistencyToken, U as parsePagingOptions, V as InvalidOperationTimestampError, W as throwIfAborted, _ as KyselyExecutionScope, a as createForwardingPoolInstrumentation, b as EventBus, c as KyselyKeyframeStore, d as DriveCollectionId, f as decideAtHead, g as authDecisionModel, h as buildDecisionModel, i as runMigrations, j as RevisionMismatchError, k as DuplicateOperationError, l as DocumentModelRegistry, m as documentDecisionModel, n as REACTOR_SCHEMA, o as instrumentPgPool, p as selectDecisionModel, r as getMigrationStatus, s as KyselyOperationStore, t as DEFAULT_DRIVE_CONTAINER_TYPES, u as SimpleJobExecutor, v as FLAG_PREREQUISITES, w as createConsistencyToken, x as KyselyWriteCache, y as validateFeatureFlags, z as DocumentNotFoundError } from "./drive-container-types-BJCKXJwH.js";
2
2
  import { n as ReactorEventTypes, t as EventBusAggregateError } from "./types-DMKLa0Ok.js";
3
3
  import { i as WorkerInitFailedError, r as WorkerExitedError, t as WorkerAbortTimeoutError } from "./errors-D3S6Eysd.js";
4
- import { a as ReadModelCoordinator, i as KyselyDocumentView, n as ConsistencyTracker, o as BaseReadModel, r as makeConsistencyKey, t as KyselyDocumentIndexer } from "./document-indexer-FGJmRAdX.js";
4
+ import { a as ReadModelCoordinator, i as KyselyDocumentView, n as ConsistencyTracker, o as BaseReadModel, r as makeConsistencyKey, t as KyselyDocumentIndexer } from "./document-indexer-C0GB0b8Q.js";
5
5
  import { n as errorToInfo, r as sanitizeArg, t as createForwardingLogger } from "./forwarding-logger-BBkMSxuJ.js";
6
- import { t as workerEntryPath } from "./worker-DBJOv8Gp.js";
7
- import { actions, actions as documentActions, createPresignedHeader, generateId, hashDocumentStateForScope, replayDocument } from "@powerhousedao/shared/document-model";
6
+ import { t as workerEntryPath } from "./worker-jEJW6_j7.js";
7
+ import { actions, actions as documentActions, createPresignedHeader, decide, garbageCollect, generateId, hashDocumentStateForScope, replayDocument, sortOperations } from "@powerhousedao/shared/document-model";
8
8
  import { addFile, addFolder, copyNode, deleteNode, driveCreateDocument, generateNodesCopy, getDescendants, handleTargetNameCollisions, isFileNode, isFolderNode, moveNode, updateNode } from "@powerhousedao/shared/document-drive";
9
9
  import { v4 } from "uuid";
10
10
  import { ConsoleLogger, childLogger } from "document-model";
@@ -190,10 +190,12 @@ function topologicalSort(jobs) {
190
190
  */
191
191
  function toErrorInfo$1(error) {
192
192
  if (error instanceof Error) return {
193
+ name: error.name,
193
194
  message: error.message,
194
195
  stack: error.stack || (/* @__PURE__ */ new Error()).stack || ""
195
196
  };
196
197
  return {
198
+ name: "Error",
197
199
  message: error,
198
200
  stack: (/* @__PURE__ */ new Error()).stack || ""
199
201
  };
@@ -680,6 +682,45 @@ let DocumentChangeType = /* @__PURE__ */ function(DocumentChangeType) {
680
682
  return DocumentChangeType;
681
683
  }({});
682
684
  //#endregion
685
+ //#region src/client/util.ts
686
+ const ALWAYS_READABLE_SCOPES = new Set(["auth", "document"]);
687
+ function authSubjectFromSigner(signer) {
688
+ return {
689
+ address: signer.user?.address,
690
+ key: signer.app?.key
691
+ };
692
+ }
693
+ function canReadScope(auth, subject, scope) {
694
+ return ALWAYS_READABLE_SCOPES.has(scope) || decide(auth, subject, {
695
+ verb: "read",
696
+ scope
697
+ }) === "allow";
698
+ }
699
+ function withAuthScope(view) {
700
+ if (view?.scopes && view.scopes.length > 0) return {
701
+ ...view,
702
+ scopes: [...new Set([...view.scopes, "auth"])]
703
+ };
704
+ return view;
705
+ }
706
+ function filterReadableScopes(document, subject) {
707
+ const state = document.state;
708
+ if (!state) return document;
709
+ const auth = document.state.auth;
710
+ const readable = (scope) => canReadScope(auth, subject, scope);
711
+ return {
712
+ ...document,
713
+ state: keepReadableScopes(state, readable),
714
+ initialState: keepReadableScopes(document.initialState, readable)
715
+ };
716
+ }
717
+ function keepReadableScopes(scopes, readable) {
718
+ if (!scopes) return scopes;
719
+ const kept = {};
720
+ for (const scope of Object.keys(scopes)) if (readable(scope)) kept[scope] = scopes[scope];
721
+ return kept;
722
+ }
723
+ //#endregion
683
724
  //#region src/client/reactor-client.ts
684
725
  /**
685
726
  * ReactorClient implementation that wraps lower-level APIs to provide
@@ -711,6 +752,9 @@ var ReactorClient = class {
711
752
  this.drives = new DriveClient(this, logger, reactor, signer);
712
753
  this.logger.verbose("ReactorClient initialized");
713
754
  }
755
+ readSubject(subject) {
756
+ return subject ?? authSubjectFromSigner(this.signer);
757
+ }
714
758
  /**
715
759
  * Retrieves a list of document model modules.
716
760
  */
@@ -734,15 +778,15 @@ var ReactorClient = class {
734
778
  */
735
779
  async get(identifier, view, signal) {
736
780
  this.logger.verbose("get(@identifier, @view)", identifier, view);
737
- return await this.reactor.getByIdOrSlug(identifier, view, void 0, signal);
781
+ return filterReadableScopes(await this.reactor.getByIdOrSlug(identifier, withAuthScope(view), void 0, signal), this.readSubject(view?.subject));
738
782
  }
739
783
  /**
740
784
  * Resolves an identifier (id or slug) to the canonical document id, using the
741
785
  * same lookup as the data path. Resolves against the "main" branch. Throws if
742
786
  * the identifier cannot be resolved or is ambiguous.
743
787
  */
744
- async resolveIdOrSlug(identifier, signal) {
745
- return this.documentView.resolveIdOrSlug(identifier, void 0, void 0, signal);
788
+ async resolveIdOrSlug(identifier, view, signal) {
789
+ return this.documentView.resolveIdOrSlug(identifier, view, void 0, signal);
746
790
  }
747
791
  /**
748
792
  * Retrieves operations for a document
@@ -750,8 +794,15 @@ var ReactorClient = class {
750
794
  async getOperations(documentIdentifier, view, filter, paging, signal) {
751
795
  this.logger.verbose("getOperations(@documentIdentifier, @view, @filter, @paging)", documentIdentifier, view, filter, paging);
752
796
  const documentId = await this.documentView.resolveIdOrSlug(documentIdentifier, view, void 0, signal);
753
- if (paging?.cursor && isCompositeCursor(paging.cursor)) return this.getOperationsWithCompositeCursor(documentId, view, filter, paging, signal);
797
+ const authDoc = await this.reactor.getByIdOrSlug(documentId, {
798
+ scopes: ["auth"],
799
+ branch: view?.branch
800
+ }, void 0, signal);
801
+ const subject = this.readSubject(view?.subject);
802
+ const canRead = (scope) => canReadScope(authDoc?.state.auth, subject, scope);
803
+ if (paging?.cursor && isCompositeCursor(paging.cursor)) return this.getOperationsWithCompositeCursor(documentId, view, filter, paging, signal, canRead);
754
804
  const operationsByScope = await this.reactor.getOperations(documentId, view, filter, paging, void 0, signal);
805
+ for (const scope of Object.keys(operationsByScope)) if (!canRead(scope)) delete operationsByScope[scope];
755
806
  const scopeEntries = Object.entries(operationsByScope);
756
807
  const effectivePaging = paging || {
757
808
  cursor: "0",
@@ -779,11 +830,12 @@ var ReactorClient = class {
779
830
  nextCursor: Object.keys(activeCursors).length > 0 ? encodeCompositeCursor(activeCursors) : void 0
780
831
  };
781
832
  }
782
- async getOperationsWithCompositeCursor(documentId, view, filter, paging, signal) {
833
+ async getOperationsWithCompositeCursor(documentId, view, filter, paging, signal, canRead) {
783
834
  const scopeCursors = decodeCompositeCursor(paging.cursor);
784
835
  const allOperations = [];
785
836
  const activeCursors = {};
786
837
  for (const [scopeName, cursor] of Object.entries(scopeCursors)) {
838
+ if (!canRead(scopeName)) continue;
787
839
  const scopeView = {
788
840
  ...view,
789
841
  scopes: [scopeName]
@@ -817,7 +869,7 @@ var ReactorClient = class {
817
869
  limit: 0
818
870
  }
819
871
  };
820
- return this.reactor.find({ ids: targetIds }, view, paging, void 0, signal);
872
+ return this.find({ ids: targetIds }, view, paging, signal);
821
873
  }
822
874
  /**
823
875
  * Retrieves incoming relationships of a given type to a target document.
@@ -833,14 +885,19 @@ var ReactorClient = class {
833
885
  limit: 0
834
886
  }
835
887
  };
836
- return this.reactor.find({ ids: sourceIds }, view, paging, void 0, signal);
888
+ return this.find({ ids: sourceIds }, view, paging, signal);
837
889
  }
838
890
  /**
839
891
  * Filters documents by criteria and returns a list of them
840
892
  */
841
893
  async find(search, view, paging, signal) {
842
894
  this.logger.verbose("find(@search, @view, @paging)", search, view, paging);
843
- return this.reactor.find(search, view, paging, void 0, signal);
895
+ const results = await this.reactor.find(search, withAuthScope(view), paging, void 0, signal);
896
+ const readSubject = this.readSubject(view?.subject);
897
+ return {
898
+ ...results,
899
+ results: results.results.map((doc) => filterReadableScopes(doc, readSubject))
900
+ };
844
901
  }
845
902
  /**
846
903
  * Creates a document and waits for completion
@@ -1078,13 +1135,15 @@ var ReactorClient = class {
1078
1135
  */
1079
1136
  subscribe(search, callback, view) {
1080
1137
  this.logger.verbose("subscribe(@search, @view)", search, view);
1138
+ const subject = this.readSubject(view?.subject);
1139
+ const readable = (document) => filterReadableScopes(document, subject);
1081
1140
  const unsubscribeCreated = this.subscriptionManager.onDocumentCreated((result) => {
1082
1141
  (async () => {
1083
1142
  try {
1084
- const documents = await Promise.all(result.results.map((id) => this.reactor.get(id, view, void 0, void 0)));
1143
+ const documents = await Promise.all(result.results.map((id) => this.reactor.get(id, withAuthScope(view), void 0, void 0)));
1085
1144
  callback({
1086
1145
  type: DocumentChangeType.Created,
1087
- documents
1146
+ documents: documents.map(readable)
1088
1147
  });
1089
1148
  } catch {}
1090
1149
  })();
@@ -1099,7 +1158,7 @@ var ReactorClient = class {
1099
1158
  const unsubscribeUpdated = this.subscriptionManager.onDocumentStateUpdated((result) => {
1100
1159
  callback({
1101
1160
  type: DocumentChangeType.Updated,
1102
- documents: result.results
1161
+ documents: result.results.map(readable)
1103
1162
  });
1104
1163
  }, search, view);
1105
1164
  const unsubscribeRelationship = this.subscriptionManager.onRelationshipChanged((parentId, childId, changeType) => {
@@ -1144,17 +1203,33 @@ let JobQueueState = /* @__PURE__ */ function(JobQueueState) {
1144
1203
  return JobQueueState;
1145
1204
  }({});
1146
1205
  /**
1206
+ * How a retry is accounted against the job's retry limit.
1207
+ * - `CountAgainstLimit` (default): a fault; the job eventually exhausts its
1208
+ * retries and fails terminally.
1209
+ * - `ExemptFromLimit`: not a fault, so the attempt is not charged to the job.
1210
+ * Used for concurrency conflicts, where the retry does new work.
1211
+ */
1212
+ let RetryAccounting = /* @__PURE__ */ function(RetryAccounting) {
1213
+ RetryAccounting["CountAgainstLimit"] = "count-against-limit";
1214
+ RetryAccounting["ExemptFromLimit"] = "exempt-from-limit";
1215
+ return RetryAccounting;
1216
+ }({});
1217
+ /**
1147
1218
  * Event types for the queue system
1148
1219
  */
1149
1220
  const QueueEventTypes = { JOB_AVAILABLE: 1e4 };
1150
1221
  //#endregion
1151
1222
  //#region src/executor/job-result-handler.ts
1223
+ /** Conflict retries a job may take without charging its retry limit. */
1224
+ const MAX_EXEMPT_CONFLICT_RETRIES = 20;
1152
1225
  function toErrorInfo(error) {
1153
1226
  if (error instanceof Error) return {
1227
+ name: error.name,
1154
1228
  message: error.message,
1155
1229
  stack: error.stack || (/* @__PURE__ */ new Error()).stack || ""
1156
1230
  };
1157
1231
  return {
1232
+ name: "Error",
1158
1233
  message: error,
1159
1234
  stack: (/* @__PURE__ */ new Error()).stack || ""
1160
1235
  };
@@ -1187,12 +1262,19 @@ var JobResultHandler = class {
1187
1262
  } catch {}
1188
1263
  }
1189
1264
  }
1265
+ if (result.error && AppendConditionFailedError.isError(result.error) && this.countConflicts(handle.job) < MAX_EXEMPT_CONFLICT_RETRIES) {
1266
+ const errorInfo = toErrorInfo(result.error);
1267
+ try {
1268
+ await this.queue.retryJob(handle.job.id, errorInfo, RetryAccounting.ExemptFromLimit);
1269
+ return;
1270
+ } catch {}
1271
+ }
1190
1272
  if (result.error && DocumentNotFoundError.isError(result.error)) {
1191
1273
  handle.defer();
1192
1274
  callbacks.deferJob(handle.job.documentId, handle.job);
1193
1275
  return;
1194
1276
  }
1195
- if (result.error && DocumentDeletedError.isError(result.error)) {
1277
+ if (result.error && (DocumentDeletedError.isError(result.error) || AuthorizationDeniedError.isError(result.error) || AuthTimestampNotMonotonicError.isError(result.error) || InvalidOperationTimestampError.isError(result.error) || ExcessiveReshuffleError.isError(result.error))) {
1196
1278
  const errorInfo = toErrorInfo(result.error);
1197
1279
  this.jobTracker.markFailed(handle.job.id, errorInfo, handle.job);
1198
1280
  this.eventBus.emit(ReactorEventTypes.JOB_FAILED, {
@@ -1230,6 +1312,12 @@ var JobResultHandler = class {
1230
1312
  handle.fail(fullErrorInfo);
1231
1313
  }
1232
1314
  }
1315
+ /** How many times this job has already lost an append-condition race. */
1316
+ countConflicts(job) {
1317
+ let conflicts = 0;
1318
+ for (const error of job.errorHistory) if (AppendConditionFailedError.isFailureMessage(error.message)) conflicts++;
1319
+ return conflicts;
1320
+ }
1233
1321
  hasCreateDocumentAction(job) {
1234
1322
  for (const action of job.actions) if (action.type === "CREATE_DOCUMENT") return true;
1235
1323
  for (const operation of job.operations) if (operation.action.type === "CREATE_DOCUMENT") return true;
@@ -1245,6 +1333,7 @@ var JobResultHandler = class {
1245
1333
  stackLines.push(`[Attempt ${index + 1}] Stack trace:\n${error.stack}`);
1246
1334
  });
1247
1335
  return {
1336
+ name: currentError.name,
1248
1337
  message: messageLines.join("\n"),
1249
1338
  stack: stackLines.join("\n\n")
1250
1339
  };
@@ -1850,6 +1939,7 @@ var InMemoryJobTracker = class {
1850
1939
  }
1851
1940
  handleJobFailed(event) {
1852
1941
  this.markFailed(event.jobId, {
1942
+ name: event.error.name,
1853
1943
  message: event.error.message,
1854
1944
  stack: event.error.stack || ""
1855
1945
  }, event.job);
@@ -2311,10 +2401,12 @@ var InMemoryQueue = class {
2311
2401
  }
2312
2402
  toErrorInfo(error) {
2313
2403
  if (error instanceof Error) return {
2404
+ name: error.name,
2314
2405
  message: error.message,
2315
2406
  stack: error.stack || (/* @__PURE__ */ new Error()).stack || ""
2316
2407
  };
2317
2408
  return {
2409
+ name: "Error",
2318
2410
  message: error,
2319
2411
  stack: (/* @__PURE__ */ new Error()).stack || ""
2320
2412
  };
@@ -2402,6 +2494,7 @@ var InMemoryQueue = class {
2402
2494
  await this.resolver.ensureModelLoaded(documentType);
2403
2495
  } catch {
2404
2496
  await this.failJob(job.id, {
2497
+ name: "Error",
2405
2498
  message: `Failed to load document model for type: ${documentType}`,
2406
2499
  stack: (/* @__PURE__ */ new Error()).stack || ""
2407
2500
  });
@@ -2587,7 +2680,7 @@ var InMemoryQueue = class {
2587
2680
  if (documentId) this.markJobComplete(jobId, documentId);
2588
2681
  this.jobIndex.delete(jobId);
2589
2682
  }
2590
- async retryJob(jobId, error) {
2683
+ async retryJob(jobId, error, accounting = RetryAccounting.CountAgainstLimit) {
2591
2684
  const job = this.jobIndex.get(jobId);
2592
2685
  if (!job) return;
2593
2686
  job.lastError = error;
@@ -2596,9 +2689,10 @@ var InMemoryQueue = class {
2596
2689
  this.jobIndex.delete(jobId);
2597
2690
  this.jobIdToQueueKey.delete(jobId);
2598
2691
  if (error) job.errorHistory.push(error);
2692
+ const retryCount = job.retryCount || 0;
2599
2693
  const updatedJob = {
2600
2694
  ...job,
2601
- retryCount: (job.retryCount || 0) + 1,
2695
+ retryCount: accounting === RetryAccounting.CountAgainstLimit ? retryCount + 1 : retryCount,
2602
2696
  lastError: error
2603
2697
  };
2604
2698
  await this.enqueue(updatedJob);
@@ -3437,11 +3531,18 @@ var PollingChannelError = class extends Error {
3437
3531
  var ChannelError = class extends Error {
3438
3532
  source;
3439
3533
  error;
3440
- constructor(source, error) {
3534
+ /**
3535
+ * The classification when something other than the error carries it. Absent
3536
+ * means derive it from `error.name`; a dead letter mirrored from a peer sets it,
3537
+ * because only the message crosses the wire.
3538
+ */
3539
+ errorType;
3540
+ constructor(source, error, errorType) {
3441
3541
  super(`ChannelError[${source}]: ${error.message}`);
3442
3542
  this.name = "ChannelError";
3443
3543
  this.source = source;
3444
3544
  this.error = error;
3545
+ this.errorType = errorType;
3445
3546
  }
3446
3547
  };
3447
3548
  //#endregion
@@ -3646,6 +3747,7 @@ function toOperationWithContext(entry) {
3646
3747
  skip: entry.skip,
3647
3748
  hash: entry.hash,
3648
3749
  timestampUtcMs: entry.timestampUtcMs,
3750
+ deniedReason: entry.deniedReason,
3649
3751
  action: entry.action
3650
3752
  },
3651
3753
  context: {
@@ -3811,6 +3913,33 @@ function splitComponent(items, maxSize) {
3811
3913
  for (let i = 0; i < sorted.length; i += maxSize) chunks.push(sorted.slice(i, i + maxSize));
3812
3914
  return chunks;
3813
3915
  }
3916
+ /**
3917
+ * Classifies a failure by error name rather than `instanceof`, because a failure
3918
+ * that crossed the pooled-worker boundary arrives as plain data.
3919
+ */
3920
+ function classifyJobFailure(errorName) {
3921
+ switch (errorName) {
3922
+ case "AuthTimestampNotMonotonicError": return "AUTH_TIMESTAMP_NOT_MONOTONIC";
3923
+ case "InvalidOperationTimestampError": return "INVALID_TIMESTAMP";
3924
+ case "ExcessiveReshuffleError": return "EXCESSIVE_SHUFFLE";
3925
+ case "InvalidSignatureError": return "SIGNATURE_INVALID";
3926
+ case "HashMismatchError": return "HASH_MISMATCH";
3927
+ default: return "UNCLASSIFIED";
3928
+ }
3929
+ }
3930
+ /** The explicit type when something else carried it, else derived by name. */
3931
+ function syncOperationErrorType(error) {
3932
+ return error?.errorType ?? classifyJobFailure(error?.error?.name ?? "Error");
3933
+ }
3934
+ /** Dead-letter types that must not stop the document syncing. */
3935
+ const NON_QUARANTINING_ERROR_TYPES = new Set(["AUTH_TIMESTAMP_NOT_MONOTONIC"]);
3936
+ /**
3937
+ * A held auth operation must not quarantine: reconciling the two policies needs
3938
+ * the traffic a quarantine would stop.
3939
+ */
3940
+ function quarantinesDocument(errorType) {
3941
+ return !NON_QUARANTINING_ERROR_TYPES.has(errorType);
3942
+ }
3814
3943
  //#endregion
3815
3944
  //#region src/sync/channels/interval-poll-timer.ts
3816
3945
  const DEFAULT_CONFIG = {
@@ -3973,6 +4102,7 @@ function serializeEnvelope(envelope) {
3973
4102
  hash: opWithContext.operation.hash,
3974
4103
  skip: opWithContext.operation.skip,
3975
4104
  error: opWithContext.operation.error,
4105
+ ...opWithContext.operation.deniedReason !== void 0 ? { deniedReason: opWithContext.operation.deniedReason } : {},
3976
4106
  id: opWithContext.operation.id,
3977
4107
  action: serializeAction(opWithContext.operation.action)
3978
4108
  },
@@ -4000,21 +4130,33 @@ function deserializeSignature(sig) {
4000
4130
  return sig.split(", ");
4001
4131
  }
4002
4132
  /**
4003
- * Deserializes signatures in an operation's signer context from strings back to tuples.
4004
- *
4005
- * When operations are transported via GraphQL, signatures are serialized as comma-separated
4006
- * strings. This function restores them to the Signature tuple format required for verification.
4133
+ * `isDenied` tests strictly against undefined, so a null left in place would mark
4134
+ * every synced operation denied. The key is removed rather than set to undefined,
4135
+ * so an operation that arrived without it stays identical to the one sent.
4007
4136
  */
4008
- function deserializeOperationSignatures(opWithContext) {
4009
- const signer = opWithContext.operation.action.context?.signer;
4010
- if (!signer?.signatures || signer.signatures.length === 0) return opWithContext;
4137
+ function normalizeAbsentFields(operation) {
4138
+ const wire = operation;
4139
+ if (wire.error !== null && wire.deniedReason !== null) return operation;
4140
+ const normalized = { ...operation };
4141
+ if (wire.error === null) delete normalized.error;
4142
+ if (wire.deniedReason === null) delete normalized.deniedReason;
4143
+ return normalized;
4144
+ }
4145
+ /** Restores signature tuples and null-valued optional fields to undefined. */
4146
+ function deserializeOperation(opWithContext) {
4147
+ const operation = normalizeAbsentFields(opWithContext.operation);
4148
+ const signer = operation.action.context?.signer;
4149
+ if (!signer?.signatures || signer.signatures.length === 0) return {
4150
+ ...opWithContext,
4151
+ operation
4152
+ };
4011
4153
  const deserializedSignatures = signer.signatures.map(deserializeSignature);
4012
4154
  const deserializedOperation = {
4013
- ...opWithContext.operation,
4155
+ ...operation,
4014
4156
  action: {
4015
- ...opWithContext.operation.action,
4157
+ ...operation.action,
4016
4158
  context: {
4017
- ...opWithContext.operation.action.context,
4159
+ ...operation.action.context,
4018
4160
  signer: {
4019
4161
  ...signer,
4020
4162
  signatures: deserializedSignatures
@@ -4041,7 +4183,7 @@ function deserializeOperationSignatures(opWithContext) {
4041
4183
  */
4042
4184
  function envelopesToSyncOperations(envelope, remoteName) {
4043
4185
  if (!envelope.operations || envelope.operations.length === 0) return [];
4044
- return batchOperationsByDocument(envelope.operations.map(deserializeOperationSignatures)).map((batch) => {
4186
+ return batchOperationsByDocument(envelope.operations.map(deserializeOperation)).map((batch) => {
4045
4187
  return new SyncOperation(`syncop-${envelope.channelMeta.id}-${Date.now()}-${syncOpCounter++}`, envelope.key ?? "", (envelope.dependsOn ?? []).filter(Boolean), remoteName, batch.documentId, [batch.scope], batch.branch, batch.operations);
4046
4188
  });
4047
4189
  }
@@ -4053,6 +4195,12 @@ const getLatestAppliedOrdinal = (syncOps) => {
4053
4195
  //#endregion
4054
4196
  //#region src/sync/channels/gql-req-channel.ts
4055
4197
  /**
4198
+ * Fields the auth projection added to the sync schema. A remote that predates
4199
+ * them rejects the whole query for naming one, so they are selected only while
4200
+ * the remote is known to serve them.
4201
+ */
4202
+ const DECISION_FIELDS = ["deniedReason", "errorType"];
4203
+ /**
4056
4204
  * GraphQL-based synchronization channel for network communication between reactors.
4057
4205
  */
4058
4206
  var GqlRequestChannel = class {
@@ -4079,6 +4227,8 @@ var GqlRequestChannel = class {
4079
4227
  isPushing = false;
4080
4228
  pendingDrain = false;
4081
4229
  receivingPages = false;
4230
+ /** Cleared for good the first time the remote rejects {@link DECISION_FIELDS}. */
4231
+ peerServesDecisionFields = true;
4082
4232
  isRecovering = false;
4083
4233
  connectionState = "connecting";
4084
4234
  /** Latest unrecoverable error was an auth rejection; cleared on connect. */
@@ -4257,7 +4407,7 @@ var GqlRequestChannel = class {
4257
4407
  const syncOps = [];
4258
4408
  for (const dl of deadLetters) {
4259
4409
  const syncOp = new SyncOperation(crypto.randomUUID(), dl.jobId, [], this.remoteName, dl.documentId, dl.scopes, dl.branch, []);
4260
- syncOp.failed(new ChannelError(ChannelErrorSource.Outbox, new Error(dl.error)));
4410
+ syncOp.failed(new ChannelError(ChannelErrorSource.Outbox, new Error(dl.error), dl.errorType ?? void 0));
4261
4411
  syncOps.push(syncOp);
4262
4412
  }
4263
4413
  this.deadLetter.add(...syncOps);
@@ -4349,7 +4499,44 @@ var GqlRequestChannel = class {
4349
4499
  * Queries the remote GraphQL endpoint for sync envelopes.
4350
4500
  */
4351
4501
  async pollSyncEnvelopes(ackOrdinal, latestOrdinal) {
4352
- const query = `
4502
+ const variables = {
4503
+ channelId: this.channelId,
4504
+ outboxAck: ackOrdinal,
4505
+ outboxLatest: latestOrdinal
4506
+ };
4507
+ let response;
4508
+ try {
4509
+ response = await this.executeGraphQL(this.pollQuery(this.peerServesDecisionFields), variables);
4510
+ } catch (error) {
4511
+ if (!this.rejectsDecisionFields(error)) throw error;
4512
+ this.logger.warn("Remote @channelId does not serve deniedReason/errorType; polling without them. The remote is on an older schema, so it has neither to report.", this.channelId);
4513
+ this.peerServesDecisionFields = false;
4514
+ response = await this.executeGraphQL(this.pollQuery(false), variables);
4515
+ }
4516
+ return {
4517
+ envelopes: response.pollSyncEnvelopes.envelopes,
4518
+ ackOrdinal: response.pollSyncEnvelopes.ackOrdinal,
4519
+ deadLetters: response.pollSyncEnvelopes.deadLetters ?? [],
4520
+ hasMore: response.pollSyncEnvelopes.hasMore
4521
+ };
4522
+ }
4523
+ /**
4524
+ * True when the remote rejected the query for naming a field it does not
4525
+ * have. Selecting an unknown field fails validation for the whole query, so
4526
+ * an unhandled one takes the channel's polling down until the process
4527
+ * restarts rather than degrading.
4528
+ */
4529
+ rejectsDecisionFields(error) {
4530
+ if (!this.peerServesDecisionFields) return false;
4531
+ if (!(error instanceof GraphQLRequestError) || error.category !== "graphql") return false;
4532
+ return DECISION_FIELDS.some((field) => error.message.includes(field));
4533
+ }
4534
+ /**
4535
+ * The poll query. `withDecisionFields` selects the two fields added with the
4536
+ * auth projection; a remote on the previous schema is polled without them.
4537
+ */
4538
+ pollQuery(withDecisionFields) {
4539
+ return `
4353
4540
  query PollSyncEnvelopes($channelId: String!, $outboxAck: Int!, $outboxLatest: Int!) {
4354
4541
  pollSyncEnvelopes(channelId: $channelId, outboxAck: $outboxAck, outboxLatest: $outboxLatest) {
4355
4542
  envelopes {
@@ -4364,6 +4551,7 @@ var GqlRequestChannel = class {
4364
4551
  hash
4365
4552
  skip
4366
4553
  error
4554
+ ${withDecisionFields ? "deniedReason" : ""}
4367
4555
  id
4368
4556
  action {
4369
4557
  id
@@ -4407,6 +4595,7 @@ var GqlRequestChannel = class {
4407
4595
  deadLetters {
4408
4596
  documentId
4409
4597
  error
4598
+ ${withDecisionFields ? "errorType" : ""}
4410
4599
  jobId
4411
4600
  branch
4412
4601
  scopes
@@ -4416,18 +4605,6 @@ var GqlRequestChannel = class {
4416
4605
  }
4417
4606
  }
4418
4607
  `;
4419
- const variables = {
4420
- channelId: this.channelId,
4421
- outboxAck: ackOrdinal,
4422
- outboxLatest: latestOrdinal
4423
- };
4424
- const response = await this.executeGraphQL(query, variables);
4425
- return {
4426
- envelopes: response.pollSyncEnvelopes.envelopes,
4427
- ackOrdinal: response.pollSyncEnvelopes.ackOrdinal,
4428
- deadLetters: response.pollSyncEnvelopes.deadLetters ?? [],
4429
- hasMore: response.pollSyncEnvelopes.hasMore
4430
- };
4431
4608
  }
4432
4609
  /**
4433
4610
  * Registers or updates this channel on the remote server via GraphQL mutation.
@@ -4909,7 +5086,8 @@ function rowToDeadLetterRecord(row) {
4909
5086
  branch: row.branch,
4910
5087
  operations: row.operations,
4911
5088
  errorSource: row.error_source,
4912
- errorMessage: row.error_message
5089
+ errorMessage: row.error_message,
5090
+ errorType: row.error_type ?? "UNCLASSIFIED"
4913
5091
  };
4914
5092
  }
4915
5093
  function deadLetterRecordToRow(record) {
@@ -4923,7 +5101,8 @@ function deadLetterRecordToRow(record) {
4923
5101
  branch: record.branch,
4924
5102
  operations: JSON.stringify(record.operations),
4925
5103
  error_source: record.errorSource,
4926
- error_message: record.errorMessage
5104
+ error_message: record.errorMessage,
5105
+ error_type: record.errorType
4927
5106
  };
4928
5107
  }
4929
5108
  /**
@@ -4980,9 +5159,11 @@ var KyselySyncDeadLetterStorage = class {
4980
5159
  }
4981
5160
  async listQuarantinedDocumentIds(signal) {
4982
5161
  if (signal?.aborted) throw new Error("Operation aborted");
4983
- const rows = await this.db.selectFrom("sync_dead_letters").select("document_id").distinct().execute();
5162
+ const rows = await this.db.selectFrom("sync_dead_letters").select(["document_id", "error_type"]).distinct().execute();
4984
5163
  if (signal?.aborted) throw new Error("Operation aborted");
4985
- return rows.map((row) => row.document_id);
5164
+ const quarantined = /* @__PURE__ */ new Set();
5165
+ for (const row of rows) if (quarantinesDocument(row.error_type ?? "UNCLASSIFIED")) quarantined.add(row.document_id);
5166
+ return [...quarantined];
4986
5167
  }
4987
5168
  };
4988
5169
  //#endregion
@@ -5655,7 +5836,8 @@ var SyncManager = class {
5655
5836
  remote.channel.deadLetter.onAdded((syncOps) => {
5656
5837
  for (const syncOp of syncOps) {
5657
5838
  this.logger.error("Dead letter (@remote, @documentId, @jobId, @error, @dependencies)", remote.meta.name, syncOp.documentId, syncOp.jobId, syncOp.error?.message ?? "unknown", syncOp.jobDependencies);
5658
- this.quarantinedDocumentIds.add(syncOp.documentId);
5839
+ const errorType = syncOperationErrorType(syncOp.error);
5840
+ if (quarantinesDocument(errorType)) this.quarantinedDocumentIds.add(syncOp.documentId);
5659
5841
  const record = {
5660
5842
  id: syncOp.id,
5661
5843
  jobId: syncOp.jobId,
@@ -5666,7 +5848,8 @@ var SyncManager = class {
5666
5848
  branch: syncOp.branch,
5667
5849
  operations: syncOp.operations,
5668
5850
  errorSource: syncOp.error?.source ?? ChannelErrorSource.None,
5669
- errorMessage: syncOp.error?.error.message ?? "unknown"
5851
+ errorMessage: syncOp.error?.error.message ?? "unknown",
5852
+ errorType
5670
5853
  };
5671
5854
  this.deadLetterStorage.add(record).catch((err) => {
5672
5855
  this.logger.error("Failed to persist dead letter (@id, @error)", record.id, err instanceof Error ? err.message : String(err));
@@ -5676,7 +5859,8 @@ var SyncManager = class {
5676
5859
  jobId: record.jobId,
5677
5860
  remoteName: record.remoteName,
5678
5861
  documentId: record.documentId,
5679
- errorSource: record.errorSource
5862
+ errorSource: record.errorSource,
5863
+ errorType: record.errorType
5680
5864
  }).catch(() => {});
5681
5865
  }
5682
5866
  const items = remote.channel.deadLetter.items;
@@ -5703,7 +5887,7 @@ var SyncManager = class {
5703
5887
  const syncOps = [];
5704
5888
  for (const record of records) {
5705
5889
  const syncOp = new SyncOperation(record.id, record.jobId, record.jobDependencies, record.remoteName, record.documentId, record.scopes, record.branch, record.operations);
5706
- syncOp.failed(new ChannelError(record.errorSource, new Error(record.errorMessage)));
5890
+ syncOp.failed(new ChannelError(record.errorSource, new Error(record.errorMessage), record.errorType));
5707
5891
  syncOps.push(syncOp);
5708
5892
  }
5709
5893
  remote.channel.deadLetter.add(...syncOps);
@@ -5784,8 +5968,7 @@ var SyncManager = class {
5784
5968
  if (completedJobInfo.status === JobStatus.FAILED) {
5785
5969
  const errorMessage = completedJobInfo.error?.message || "Unknown error";
5786
5970
  this.logger.error("Failed to apply operations from inbox (@remote, @documentId, @jobId, @error)", remote.meta.name, syncOp.documentId, completedJobInfo.id, errorMessage);
5787
- const error = new ChannelError(ChannelErrorSource.Inbox, /* @__PURE__ */ new Error(`Failed to apply operations: ${errorMessage}`));
5788
- syncOp.failed(error);
5971
+ syncOp.failed(this.inboxFailure(completedJobInfo.error));
5789
5972
  remote.channel.deadLetter.add(syncOp);
5790
5973
  } else syncOp.executed();
5791
5974
  remote.channel.inbox.remove(syncOp);
@@ -5862,14 +6045,26 @@ var SyncManager = class {
5862
6045
  }
5863
6046
  if (this.isShutdown) return;
5864
6047
  if (completedJobInfo.status === JobStatus.FAILED) {
5865
- const errorMessage = completedJobInfo.error?.message || "Unknown error";
5866
- const channelError = new ChannelError(ChannelErrorSource.Inbox, /* @__PURE__ */ new Error(`Failed to apply operations: ${errorMessage}`));
5867
- syncOp.failed(channelError);
6048
+ syncOp.failed(this.inboxFailure(completedJobInfo.error));
5868
6049
  remote.channel.deadLetter.add(syncOp);
5869
6050
  } else syncOp.executed();
5870
6051
  remote.channel.inbox.remove(syncOp);
5871
6052
  }
5872
6053
  }
6054
+ /**
6055
+ * The dead letter for a load job the executor failed.
6056
+ *
6057
+ * The classification is passed explicitly because it cannot be recovered
6058
+ * downstream: the wrapper carries the failure's message, not the failure, so
6059
+ * deriving it from the wrapper's own name would classify every one of these
6060
+ * as unclassified and quarantine the document. A held auth operation must
6061
+ * keep syncing, because reconciling the two policies needs the traffic a
6062
+ * quarantine would stop.
6063
+ */
6064
+ inboxFailure(error) {
6065
+ const message = error?.message || "Unknown error";
6066
+ return new ChannelError(ChannelErrorSource.Inbox, /* @__PURE__ */ new Error(`Failed to apply operations: ${message}`), classifyJobFailure(error?.name ?? "Error"));
6067
+ }
5873
6068
  async updateOutbox(remote, ackOrdinal, mode = OutboxMode.Backfill, signal) {
5874
6069
  const composedSignal = signal ? AbortSignal.any([signal, this.abortController.signal]) : this.abortController.signal;
5875
6070
  let maxOrdinal = ackOrdinal;
@@ -6769,6 +6964,8 @@ var ReactorBuilder = class {
6769
6964
  }
6770
6965
  async buildModule() {
6771
6966
  if (!this.logger) this.logger = new ConsoleLogger(["reactor"]);
6967
+ validateFeatureFlags(this.executorConfig.featureFlags ?? {}, FLAG_PREREQUISITES);
6968
+ if (this.projectionShardConfig !== void 0 && this.readModelFactories.length > 0) throw new Error("withProjectionShards does not support read models registered through withReadModelFactory; projection workers cannot receive host-only factory dependencies");
6772
6969
  const resolvedSources = await resolveModelSources(this.documentModelSources);
6773
6970
  if (this.workerPool) {
6774
6971
  if (resolvedSources.manifest.length === 0) throw new Error("withWorkerPool requires at least one worker-importable document-model source ({ filePath } or { packageName }).");
@@ -6826,7 +7023,7 @@ var ReactorBuilder = class {
6826
7023
  await executorManager.start(executorStartCount);
6827
7024
  const readModelInstances = Array.from(new Set([...this.readModels]));
6828
7025
  const documentViewConsistencyTracker = new ConsistencyTracker();
6829
- const documentView = new KyselyDocumentView(database, operationStore, operationIndex, writeCache, documentViewConsistencyTracker);
7026
+ const documentView = new KyselyDocumentView(database, operationStore, operationIndex, writeCache, documentViewConsistencyTracker, this.executorConfig.featureFlags?.documentDecisions ?? false);
6830
7027
  try {
6831
7028
  await documentView.init();
6832
7029
  } catch (error) {
@@ -6852,6 +7049,7 @@ var ReactorBuilder = class {
6852
7049
  }
6853
7050
  for (const factory of this.readModelFactories) {
6854
7051
  const readModel = await factory({
7052
+ documentModelRegistry,
6855
7053
  operationIndex,
6856
7054
  writeCache,
6857
7055
  processorManagerConsistencyTracker
@@ -6962,9 +7160,9 @@ var ReactorBuilder = class {
6962
7160
  */
6963
7161
  async createDefaultWorkerFactory(numWorkers, db, signatureVerifier) {
6964
7162
  const [{ WorkerHandle }, { createThreadTransport }, { workerEntryPath }] = await Promise.all([
6965
- import("./worker-handle-B1w03nRA.js"),
7163
+ import("./worker-handle-CrERzl8s.js"),
6966
7164
  import("./transport-ByGviWdZ.js"),
6967
- import("./worker-DBJOv8Gp.js").then((n) => n.n)
7165
+ import("./worker-jEJW6_j7.js").then((n) => n.n)
6968
7166
  ]);
6969
7167
  const poolConfig = {
6970
7168
  enabled: true,
@@ -6985,7 +7183,8 @@ var ReactorBuilder = class {
6985
7183
  poolConfig,
6986
7184
  db,
6987
7185
  signatureVerifier,
6988
- models
7186
+ models,
7187
+ executorConfig: this.executorConfig
6989
7188
  },
6990
7189
  logger,
6991
7190
  poolInstrumentation
@@ -7225,6 +7424,43 @@ function driveIdFromUrl(url) {
7225
7424
  return url.split("/").pop() ?? "";
7226
7425
  }
7227
7426
  //#endregion
7427
+ //#region src/decision/stream-order.ts
7428
+ /**
7429
+ * The first pair of effective operations whose stored order contradicts their
7430
+ * timestamps, or undefined when the stream is in position order.
7431
+ *
7432
+ * Such a stream cannot be walked, and the auth stream is never reshuffled once
7433
+ * the monotonic rule is on, so run this before enabling enforcement on a fleet.
7434
+ *
7435
+ * `requireStrict` additionally rejects a tie, which is what the auth stream's
7436
+ * monotonic rule requires and what the walk alone does not care about.
7437
+ */
7438
+ function firstOutOfOrderPair(operations, options) {
7439
+ const requireStrict = options?.requireStrict ?? false;
7440
+ const effective = garbageCollect(sortOperations([...operations]));
7441
+ for (let i = 1; i < effective.length; i++) {
7442
+ const previous = effective[i - 1];
7443
+ const current = effective[i];
7444
+ const previousAt = Date.parse(previous.timestampUtcMs);
7445
+ const currentAt = Date.parse(current.timestampUtcMs);
7446
+ if (currentAt < previousAt) return {
7447
+ previous,
7448
+ current,
7449
+ kind: "descending"
7450
+ };
7451
+ if (requireStrict && currentAt === previousAt) return {
7452
+ previous,
7453
+ current,
7454
+ kind: "tied"
7455
+ };
7456
+ }
7457
+ }
7458
+ //#endregion
7459
+ //#region src/read-models/interfaces.ts
7460
+ function supportsLiveReadModelRegistration(coordinator) {
7461
+ return "addReadModel" in coordinator && typeof coordinator.addReadModel === "function";
7462
+ }
7463
+ //#endregion
7228
7464
  //#region src/admin/passthrough-keyframe-store.ts
7229
7465
  const passthroughKeyframeStore = {
7230
7466
  putKeyframe: () => Promise.resolve(),
@@ -7250,6 +7486,7 @@ var DocumentIntegrityService = class {
7250
7486
  async validateDocument(documentId, branch = "main", signal) {
7251
7487
  const keyframeIssues = [];
7252
7488
  const snapshotIssues = [];
7489
+ const streamOrderIssues = await this.findStreamOrderIssues(documentId, branch, signal);
7253
7490
  const replayCache = new KyselyWriteCache(passthroughKeyframeStore, this.operationStore, this.documentModelRegistry, {
7254
7491
  maxDocuments: 1,
7255
7492
  ringBufferSize: 1,
@@ -7276,9 +7513,10 @@ var DocumentIntegrityService = class {
7276
7513
  } catch {
7277
7514
  return {
7278
7515
  documentId,
7279
- isConsistent: keyframeIssues.length === 0,
7516
+ isConsistent: keyframeIssues.length === 0 && streamOrderIssues.length === 0,
7280
7517
  keyframeIssues,
7281
- snapshotIssues
7518
+ snapshotIssues,
7519
+ streamOrderIssues
7282
7520
  };
7283
7521
  }
7284
7522
  const revisions = await this.operationStore.getRevisions(documentId, branch, signal);
@@ -7304,9 +7542,10 @@ var DocumentIntegrityService = class {
7304
7542
  }
7305
7543
  return {
7306
7544
  documentId,
7307
- isConsistent: keyframeIssues.length === 0 && snapshotIssues.length === 0,
7545
+ isConsistent: keyframeIssues.length === 0 && snapshotIssues.length === 0 && streamOrderIssues.length === 0,
7308
7546
  keyframeIssues,
7309
- snapshotIssues
7547
+ snapshotIssues,
7548
+ streamOrderIssues
7310
7549
  };
7311
7550
  }
7312
7551
  async rebuildKeyframes(documentId, branch = "main", signal) {
@@ -7328,12 +7567,26 @@ var DocumentIntegrityService = class {
7328
7567
  scopesInvalidated: scopes.length
7329
7568
  };
7330
7569
  }
7570
+ async findStreamOrderIssues(documentId, branch, signal) {
7571
+ const scopes = await this.discoverScopes(documentId, branch, signal);
7572
+ const issues = [];
7573
+ for (const scope of scopes) {
7574
+ throwIfAborted(signal);
7575
+ const pair = firstOutOfOrderPair((await this.operationStore.getSince(documentId, scope, branch, -1, void 0, void 0, signal)).results, { requireStrict: scope === "auth" });
7576
+ if (pair !== void 0) issues.push({
7577
+ scope,
7578
+ branch,
7579
+ ...pair
7580
+ });
7581
+ }
7582
+ return issues;
7583
+ }
7331
7584
  async discoverScopes(documentId, branch, signal) {
7332
7585
  const revisions = await this.operationStore.getRevisions(documentId, branch, signal);
7333
7586
  return Object.keys(revisions.revision);
7334
7587
  }
7335
7588
  };
7336
7589
  //#endregion
7337
- export { BaseReadModel, ChannelError, ChannelErrorSource, ChannelScheme, ConsistencyTracker, DEFAULT_DRIVE_CONTAINER_TYPES, DRIVE_AUTH_ERROR_MESSAGES, DefaultSubscriptionErrorHandler, DocumentChangeType, DocumentIntegrityService, DocumentModelRegistry, DocumentModelResolver, DriveClient, DriveCollectionId, DuplicateManifestError, DuplicateModuleError, DuplicateOperationError, EventBus, EventBusAggregateError, GqlRequestChannel, GqlRequestChannelFactory, GqlResponseChannel, GqlResponseChannelFactory, SimpleJobExecutor as InMemoryJobExecutor, SimpleJobExecutor, InMemoryJobTracker, InMemoryQueue, IntervalPollTimer, InvalidModuleError, JobAwaiter, JobExecutorEventTypes, JobStatus, KyselyDocumentIndexer, KyselyDocumentView, KyselyKeyframeStore, KyselyOperationStore, KyselySyncCursorStorage, KyselySyncRemoteStorage, KyselyWriteCache, Mailbox, ModuleNotFoundError, NullDocumentModelResolver, OptimisticLockError, PollBehavior, PollingChannelError, ProcessorManager, PropagationMode, QueueEventTypes, REACTOR_SCHEMA, Reactor, ReactorBuilder, ReactorClient, ReactorClientBuilder, ReactorEventTypes, ReactorSubscriptionManager, ReadModelCoordinator, RelationalDbProcessor, RelationshipChangeType, RevisionMismatchError, SimpleJobExecutorManager, SyncBuilder, SyncEventTypes, SyncOperation, SyncOperationAggregateError, SyncOperationStatus, SyncStatus, SyncStatusTracker, addRelationshipAction, batchOperationsByDocument, consolidateSyncOperations, createDocumentAction, createForwardingLogger, createMutableShutdownStatus, createRelationalDb, deleteDocumentAction, documentActions, driveIdFromUrl, envelopesToSyncOperations, errorToInfo, getMigrationStatus, instrumentPgPool, isDriveAuthError, makeConsistencyKey, parseDriveUrl, parsePagingOptions, removeRelationshipAction, runMigrations, sanitizeArg, trimMailboxFromAckOrdinal, updateRelationshipAction, upgradeDocumentAction, workerEntryPath };
7590
+ export { APPEND_CONDITION_FAILED_PREFIX, AppendConditionFailedError, BaseReadModel, ChannelError, ChannelErrorSource, ChannelScheme, ConsistencyTracker, DEFAULT_DRIVE_CONTAINER_TYPES, DRIVE_AUTH_ERROR_MESSAGES, DefaultSubscriptionErrorHandler, DocumentChangeType, DocumentIntegrityService, DocumentModelRegistry, DocumentModelResolver, DriveClient, DriveCollectionId, DuplicateManifestError, DuplicateModuleError, DuplicateOperationError, EventBus, EventBusAggregateError, GqlRequestChannel, GqlRequestChannelFactory, GqlResponseChannel, GqlResponseChannelFactory, SimpleJobExecutor as InMemoryJobExecutor, SimpleJobExecutor, InMemoryJobTracker, InMemoryQueue, IntervalPollTimer, InvalidModuleError, JobAwaiter, JobExecutorEventTypes, JobStatus, KyselyDocumentIndexer, KyselyDocumentView, KyselyKeyframeStore, KyselyOperationStore, KyselySyncCursorStorage, KyselySyncRemoteStorage, KyselyWriteCache, Mailbox, ModuleNotFoundError, NullDocumentModelResolver, OptimisticLockError, PollBehavior, PollingChannelError, ProcessorManager, PropagationMode, QueueEventTypes, REACTOR_SCHEMA, Reactor, ReactorBuilder, ReactorClient, ReactorClientBuilder, ReactorEventTypes, ReactorSubscriptionManager, ReadModelCoordinator, RelationalDbProcessor, RelationshipChangeType, RetryAccounting, RevisionMismatchError, SimpleJobExecutorManager, SyncBuilder, SyncEventTypes, SyncOperation, SyncOperationAggregateError, SyncOperationStatus, SyncStatus, SyncStatusTracker, addRelationshipAction, authDecisionModel, batchOperationsByDocument, buildDecisionModel, classifyJobFailure, consolidateSyncOperations, createDocumentAction, createForwardingLogger, createMutableShutdownStatus, createRelationalDb, decideAtHead, deleteDocumentAction, documentActions, documentDecisionModel, driveIdFromUrl, envelopesToSyncOperations, errorToInfo, firstOutOfOrderPair, getMigrationStatus, instrumentPgPool, isDriveAuthError, makeConsistencyKey, parseDriveUrl, parsePagingOptions, quarantinesDocument, removeRelationshipAction, runMigrations, sanitizeArg, selectDecisionModel, supportsLiveReadModelRegistration, syncOperationErrorType, trimMailboxFromAckOrdinal, updateRelationshipAction, upgradeDocumentAction, workerEntryPath };
7338
7591
 
7339
7592
  //# sourceMappingURL=index.js.map