@powerhousedao/reactor 6.2.3-dev.2 → 6.2.3-dev.21

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,9 +1,9 @@
1
- import { A as selectDecisionModel, B as RevisionMismatchError, C as InvalidModuleError, D as createEmptyConsistencyToken, E as createConsistencyToken, F as AppendConditionFailedError, G as DocumentNotFoundError, H as AuthTimestampNotMonotonicError, I as DocumentAlreadyExistsError, J as UpgradePreconditionFailedError, K as ExcessiveReshuffleError, L as DocumentExistence, M as authDecisionModel, N as buildDecisionModel, O as targetDocumentId, P as APPEND_CONDITION_FAILED_PREFIX, R as DuplicateOperationError, S as DuplicateModuleError, T as GATED_DOCUMENT_ACTIONS, U as AuthorizationDeniedError, V as AuthEnforcementDisabledError, W as DocumentDeletedError, X as parsePagingOptions, Y as matchesScope, Z as throwIfAborted, _ as DocumentMetaCache, a as createForwardingPoolInstrumentation, b as JobExecutorEventTypes, c as KyselyKeyframeStore, d as DriveCollectionId, f as KyselyExecutionScope, g as KyselyOperationIndex, h as KyselyWriteCache, i as runMigrations, j as documentDecisionModel, k as decideAtHead, l as DocumentModelRegistry, m as EventBus, n as REACTOR_SCHEMA, o as instrumentPgPool, p as resolveFeatureFlags, q as InvalidOperationTimestampError, r as getMigrationStatus, s as KyselyOperationStore, t as DEFAULT_DRIVE_CONTAINER_TYPES, u as SimpleJobExecutor, v as CollectionMembershipCache, w as ModuleNotFoundError, x as DuplicateManifestError, y as DEFAULT_DEFERRED_JOB_TTL_MS, z as OptimisticLockError } from "./drive-container-types-yZrksiJR.js";
1
+ import { $ as parsePagingOptions, A as targetDocumentId, B as DuplicateOperationError, C as InvalidModuleError, D as createEmptyConsistencyToken, E as createConsistencyToken, F as buildDecisionModel, G as AuthorizationDeniedError, H as RevisionMismatchError, I as APPEND_CONDITION_FAILED_PREFIX, J as ExcessiveReshuffleError, K as DocumentDeletedError, L as AppendConditionFailedError, M as selectDecisionModel, N as documentDecisionModel, O as submittedActionIds, P as authDecisionModel, Q as matchesScope, R as DocumentAlreadyExistsError, S as DuplicateModuleError, T as GATED_DOCUMENT_ACTIONS, U as AuthEnforcementDisabledError, V as OptimisticLockError, W as AuthTimestampNotMonotonicError, X as RelationshipNotFoundError, Y as InvalidOperationTimestampError, Z as UpgradePreconditionFailedError, _ as DocumentMetaCache, a as createForwardingPoolInstrumentation, b as JobExecutorEventTypes, c as KyselyKeyframeStore, d as DriveCollectionId, et as throwIfAborted, f as KyselyExecutionScope, g as KyselyOperationIndex, h as KyselyWriteCache, i as runMigrations, j as decideAtHead, k as summarizeSubmittedActions, l as DocumentModelRegistry, m as EventBus, n as REACTOR_SCHEMA, o as instrumentPgPool, p as resolveFeatureFlags, q as DocumentNotFoundError, r as getMigrationStatus, s as KyselyOperationStore, t as DEFAULT_DRIVE_CONTAINER_TYPES, u as SimpleJobExecutor, v as CollectionMembershipCache, w as ModuleNotFoundError, x as DuplicateManifestError, y as DEFAULT_DEFERRED_JOB_TTL_MS, z as DocumentExistence } from "./drive-container-types-CS5IxDiA.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 DOCUMENT_INDEXER_READ_MODEL, c as BaseReadModel, i as KyselyDocumentView, n as ConsistencyTracker, o as DOCUMENT_VIEW_READ_MODEL, r as makeConsistencyKey, s as ReadModelCoordinator, t as KyselyDocumentIndexer } from "./document-indexer-C5Gsa1B3.js";
4
+ import { a as KyselyDocumentView, c as ReadModelCoordinator, d as DEFAULT_READ_MODEL_YIELD_DEADLINE_MS, f as defaultReadModelIndexingConfig, i as DeletedDocumentRead, l as BaseReadModel, n as ConsistencyTracker, o as DOCUMENT_INDEXER_READ_MODEL, p as unchunkedReadModelIndexingConfig, r as makeConsistencyKey, s as DOCUMENT_VIEW_READ_MODEL, t as KyselyDocumentIndexer, u as DEFAULT_COMMIT_CHUNK_SIZE } from "./document-indexer-C1oY6CaR.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-HPysBfhx.js";
6
+ import { t as workerEntryPath } from "./worker-g03lNM42.js";
7
7
  import { DowngradeNotSupportedError, MAX_AUTH_GRANTS, UnsupportedDocumentModelVersionError, actions, actions as documentActions, createPresignedHeader, decide, deserializeSignature, garbageCollect, generateId, groupDocumentType, groupMembershipActionTypes, hashDocumentStateForScope, normalizeDocumentModelVersion, sortOperations, toTransportAction } 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";
@@ -1248,6 +1248,79 @@ var ReactorClient = class {
1248
1248
  return this.find({ ids: sourceIds }, view, paging, signal);
1249
1249
  }
1250
1250
  /**
1251
+ * Retrieves the outgoing relationship edges of a source document, carrying the
1252
+ * metadata and timestamps the far-end documents do not.
1253
+ */
1254
+ async getOutgoingRelationshipEdges(sourceIdentifier, relationshipType, view, paging, signal) {
1255
+ this.logger.verbose("getOutgoingRelationshipEdges(@sourceIdentifier, @relationshipType, @view, @paging)", sourceIdentifier, relationshipType, view, paging);
1256
+ const sourceId = await this.documentView.resolveIdOrSlug(sourceIdentifier, view, void 0, signal);
1257
+ const edges = await this.reactor.getOutgoingRelationshipEdges(sourceId, relationshipType, paging, void 0, signal);
1258
+ return this.gateEdges(edges, "targetId", view, signal);
1259
+ }
1260
+ /**
1261
+ * Retrieves the incoming relationship edges of a target document, carrying the
1262
+ * metadata and timestamps the far-end documents do not.
1263
+ */
1264
+ async getIncomingRelationshipEdges(targetIdentifier, relationshipType, view, paging, signal) {
1265
+ this.logger.verbose("getIncomingRelationshipEdges(@targetIdentifier, @relationshipType, @view, @paging)", targetIdentifier, relationshipType, view, paging);
1266
+ const targetId = await this.documentView.resolveIdOrSlug(targetIdentifier, view, void 0, signal);
1267
+ const edges = await this.reactor.getIncomingRelationshipEdges(targetId, relationshipType, paging, void 0, signal);
1268
+ return this.gateEdges(edges, "sourceId", view, signal);
1269
+ }
1270
+ /**
1271
+ * Drops the edges whose far-end document the subject may read no domain scope
1272
+ * of. An edge is withheld whole rather than stripped of its metadata: the
1273
+ * document-shaped relationship reads already answer with the far end stripped
1274
+ * to the scopes the gate allows, so the far end's existence is disclosed
1275
+ * either way, but an edge's metadata is content about the pair that the far
1276
+ * end's own reads would refuse. An edge to a far end stripped to nothing
1277
+ * therefore carries content past a refusal, and there is no useful shell to
1278
+ * hand back in its place.
1279
+ *
1280
+ * `nextCursor` and `options` are left as the underlying stream reported them,
1281
+ * because a caller must feed them back to resume from the right position. A
1282
+ * gated page can therefore be shorter than the limit it asked for.
1283
+ */
1284
+ async gateEdges(page, farEnd, view, signal) {
1285
+ const ids = [...new Set(page.results.map((edge) => edge[farEnd]))];
1286
+ if (ids.length === 0) return page;
1287
+ const farEndView = {
1288
+ subject: view?.subject,
1289
+ branch: view?.branch
1290
+ };
1291
+ const documents = await this.reactor.find({ ids }, farEndView, {
1292
+ cursor: "0",
1293
+ limit: ids.length
1294
+ }, void 0, signal);
1295
+ const readable = new Map(await Promise.all(documents.results.map(async (doc) => {
1296
+ const allows = await this.readableScopes(doc, view, signal);
1297
+ const domainScopes = Object.keys(doc.state).filter((scope) => !ALWAYS_READABLE_SCOPES.has(scope));
1298
+ return [doc.header.id, domainScopes.length === 0 || domainScopes.some(allows)];
1299
+ })));
1300
+ const results = page.results.filter((edge) => readable.get(edge[farEnd]) !== false);
1301
+ if (results.length === page.results.length) return page;
1302
+ const nextPage = page.next;
1303
+ return {
1304
+ ...page,
1305
+ results,
1306
+ next: nextPage ? async () => this.gateEdges(await nextPage(), farEnd, view, signal) : void 0
1307
+ };
1308
+ }
1309
+ /**
1310
+ * One relationship edge, or undefined when it does not exist. A point lookup:
1311
+ * the pair is filtered in SQL rather than scanned out of the source's edge
1312
+ * list, which on a drive with thousands of children is the difference between
1313
+ * one query and dozens.
1314
+ */
1315
+ async readRelationshipEdge(sourceIdentifier, targetIdentifier, relationshipType, view, signal) {
1316
+ const sourceId = await this.documentView.resolveIdOrSlug(sourceIdentifier, view, void 0, signal);
1317
+ const targetId = await this.documentView.resolveIdOrSlug(targetIdentifier, view, void 0, signal);
1318
+ return (await this.documentIndexer.getDirectedRelationships(sourceId, targetId, [relationshipType], {
1319
+ cursor: "0",
1320
+ limit: 1
1321
+ }, void 0, signal)).results[0];
1322
+ }
1323
+ /**
1251
1324
  * Filters documents by criteria and returns a list of them
1252
1325
  */
1253
1326
  async find(search, view, paging, signal) {
@@ -1515,9 +1588,21 @@ var ReactorClient = class {
1515
1588
  /**
1516
1589
  * Adds multiple documents as children to another and waits for completion
1517
1590
  */
1518
- async addRelationship(sourceIdentifier, targetIdentifier, relationshipType, branch = "main", signal) {
1519
- this.logger.verbose("addRelationship(@sourceIdentifier, @targetIdentifier, @relationshipType, @branch)", sourceIdentifier, targetIdentifier, relationshipType, branch);
1520
- const jobInfo = await this.reactor.addRelationship(sourceIdentifier, targetIdentifier, relationshipType, branch, this.signer, signal);
1591
+ async addRelationship(sourceIdentifier, targetIdentifier, relationshipType, metadata, branch = "main", signal) {
1592
+ this.logger.verbose("addRelationship(@sourceIdentifier, @targetIdentifier, @relationshipType, @metadata, @branch)", sourceIdentifier, targetIdentifier, relationshipType, metadata, branch);
1593
+ const jobInfo = await this.reactor.addRelationship(sourceIdentifier, targetIdentifier, relationshipType, metadata, branch, this.signer, signal);
1594
+ const completedJob = await this.waitForJob(jobInfo, signal);
1595
+ if (completedJob.status === JobStatus.FAILED) throw new Error(completedJob.error?.message);
1596
+ const result = await this.reactor.getByIdOrSlug(sourceIdentifier, { branch }, completedJob.consistencyToken, signal);
1597
+ return this.gateDocument(result, { branch }, signal);
1598
+ }
1599
+ /**
1600
+ * Replaces the metadata of an existing relationship and waits for completion.
1601
+ */
1602
+ async updateRelationship(sourceIdentifier, targetIdentifier, relationshipType, metadata, branch = "main", signal) {
1603
+ this.logger.verbose("updateRelationship(@sourceIdentifier, @targetIdentifier, @relationshipType, @metadata, @branch)", sourceIdentifier, targetIdentifier, relationshipType, metadata, branch);
1604
+ if (!await this.readRelationshipEdge(sourceIdentifier, targetIdentifier, relationshipType, { branch }, signal)) throw new RelationshipNotFoundError(sourceIdentifier, targetIdentifier, relationshipType);
1605
+ const jobInfo = await this.reactor.updateRelationship(sourceIdentifier, targetIdentifier, relationshipType, metadata, branch, this.signer, signal);
1521
1606
  const completedJob = await this.waitForJob(jobInfo, signal);
1522
1607
  if (completedJob.status === JobStatus.FAILED) throw new Error(completedJob.error?.message);
1523
1608
  const result = await this.reactor.getByIdOrSlug(sourceIdentifier, { branch }, completedJob.consistencyToken, signal);
@@ -1539,10 +1624,11 @@ var ReactorClient = class {
1539
1624
  */
1540
1625
  async moveRelationship(sourceParentIdentifier, targetParentIdentifier, targetIdentifier, relationshipType, branch = "main", signal) {
1541
1626
  this.logger.verbose("moveRelationship(@sourceParentIdentifier, @targetParentIdentifier, @targetIdentifier, @relationshipType, @branch)", sourceParentIdentifier, targetParentIdentifier, targetIdentifier, relationshipType, branch);
1627
+ const metadata = (await this.readRelationshipEdge(sourceParentIdentifier, targetIdentifier, relationshipType, { branch }, signal))?.metadata;
1542
1628
  const removeJobInfo = await this.reactor.removeRelationship(sourceParentIdentifier, targetIdentifier, relationshipType, branch, this.signer, signal);
1543
1629
  const removeCompletedJob = await this.waitForJob(removeJobInfo, signal);
1544
1630
  if (removeCompletedJob.status === JobStatus.FAILED) throw new Error(removeCompletedJob.error?.message);
1545
- const addJobInfo = await this.reactor.addRelationship(targetParentIdentifier, targetIdentifier, relationshipType, branch, this.signer, signal);
1631
+ const addJobInfo = await this.reactor.addRelationship(targetParentIdentifier, targetIdentifier, relationshipType, metadata, branch, this.signer, signal);
1546
1632
  const addCompletedJob = await this.waitForJob(addJobInfo, signal);
1547
1633
  if (addCompletedJob.status === JobStatus.FAILED) throw new Error(addCompletedJob.error?.message);
1548
1634
  const sourceResult = await this.reactor.getByIdOrSlug(sourceParentIdentifier, { branch }, removeCompletedJob.consistencyToken, signal);
@@ -2212,6 +2298,7 @@ var WorkerPoolJobExecutorManager = class {
2212
2298
  jobId: job.id,
2213
2299
  operations: payload.operations,
2214
2300
  jobMeta: payload.jobMeta,
2301
+ submittedActionIds: submittedActionIds(job),
2215
2302
  collectionMemberships
2216
2303
  };
2217
2304
  try {
@@ -2495,7 +2582,8 @@ var InMemoryJobTracker = class {
2495
2582
  this.jobs.set(jobId, {
2496
2583
  ...job,
2497
2584
  status: JobStatus.WRITE_READY,
2498
- consistencyToken
2585
+ consistencyToken,
2586
+ result: summarizeSubmittedActions(event.operations, event.submittedActionIds)
2499
2587
  });
2500
2588
  }
2501
2589
  }
@@ -2663,7 +2751,8 @@ var ProcessorManager = class extends BaseReadModel {
2663
2751
  constructor(db, operationIndex, writeCache, consistencyTracker, logger, driveContainerTypes, options = {}) {
2664
2752
  super(db, operationIndex, writeCache, consistencyTracker, {
2665
2753
  readModelId: "processor-manager",
2666
- rebuildStateOnInit: true
2754
+ rebuildStateOnInit: true,
2755
+ indexing: unchunkedReadModelIndexingConfig
2667
2756
  });
2668
2757
  this.logger = logger;
2669
2758
  this.driveContainerTypes = driveContainerTypes;
@@ -4229,9 +4318,35 @@ const DRIVE_AUTH_ERROR_MESSAGES = {
4229
4318
  forbidden: "Forbidden: insufficient permissions",
4230
4319
  authenticationRequired: "Forbidden: authentication required"
4231
4320
  };
4321
+ /**
4322
+ * A non-GraphQL HTTP failure against a drive endpoint.
4323
+ *
4324
+ * Drive discovery (`GET <base>/d/:drive`) is REST, not GraphQL, so its
4325
+ * failures cannot be a `GraphQLRequestError` without the name lying about
4326
+ * what was called. It carries the status for the same reason that one does:
4327
+ * `isDriveAuthError` is what decides whether a failure prompts a login, and a
4328
+ * bare `Error` tells it nothing.
4329
+ */
4330
+ var DriveRequestError = class extends Error {
4331
+ statusCode;
4332
+ constructor(message, statusCode) {
4333
+ super(message);
4334
+ this.name = "DriveRequestError";
4335
+ this.statusCode = statusCode;
4336
+ }
4337
+ };
4232
4338
  /** True when the remote rejected the caller as unauthenticated/unauthorized:
4233
- * an HTTP 401/403, or a Forbidden/Unauthorized GraphQL error. */
4339
+ * an HTTP 401/403, or a Forbidden/Unauthorized GraphQL error.
4340
+ *
4341
+ * 403 and 401 only — NOT 404. The drive info endpoint answers a drive the
4342
+ * caller may not read with the same 404 it gives a drive that does not exist,
4343
+ * so that an unauthorized caller cannot enumerate drives by probing slugs.
4344
+ * That is deliberate, and it costs exactly this: a protected drive is
4345
+ * indistinguishable from a typo, and prompting for a login on every 404 would
4346
+ * fire on every mistyped URL. See the `WWW-Authenticate` note on the endpoint
4347
+ * for the signal that would let a client tell the two apart. */
4234
4348
  function isDriveAuthError(error) {
4349
+ if (error instanceof DriveRequestError) return error.statusCode === 401 || error.statusCode === 403;
4235
4350
  if (!(error instanceof GraphQLRequestError)) return false;
4236
4351
  if (error.category === "http") return error.statusCode === 401 || error.statusCode === 403;
4237
4352
  if (error.category === "graphql") return Object.values(DRIVE_AUTH_ERROR_MESSAGES).some((m) => error.message.includes(m));
@@ -5028,6 +5143,10 @@ var GqlRequestChannel = class {
5028
5143
  if (this.isShutdown) return;
5029
5144
  this.pollTimer.triggerNow();
5030
5145
  }
5146
+ /** This channel polls a remote itself; it has no holder to hear from. */
5147
+ notePoll() {}
5148
+ /** No holder, so nothing this channel reports may strand one. */
5149
+ lastHolderPollUtcMs() {}
5031
5150
  /**
5032
5151
  * Initializes the channel by registering it on the remote server and starting polling.
5033
5152
  */
@@ -5611,6 +5730,7 @@ var GqlResponseChannel = class {
5611
5730
  lastPersistedOutboxOrdinal = 0;
5612
5731
  evictedOutboxFloor = Number.POSITIVE_INFINITY;
5613
5732
  appliedOutboxOrdinal = 0;
5733
+ lastPollUtcMs = Date.now();
5614
5734
  connectionState = "connecting";
5615
5735
  connectionStateCallbacks = /* @__PURE__ */ new Set();
5616
5736
  constructor(logger, channelId, remoteName, cursorStorage) {
@@ -5653,7 +5773,7 @@ var GqlResponseChannel = class {
5653
5773
  return {
5654
5774
  state: this.connectionState,
5655
5775
  failureCount: 0,
5656
- lastSuccessUtcMs: 0,
5776
+ lastSuccessUtcMs: this.lastPollUtcMs,
5657
5777
  lastFailureUtcMs: 0,
5658
5778
  pushBlocked: false,
5659
5779
  pushFailureCount: 0,
@@ -5669,6 +5789,13 @@ var GqlResponseChannel = class {
5669
5789
  }
5670
5790
  /** Response channels are push-driven; resolvers populate mailboxes directly. */
5671
5791
  triggerPull() {}
5792
+ notePoll() {
5793
+ this.lastPollUtcMs = Date.now();
5794
+ }
5795
+ /** This channel is served: its holder's polls are the liveness it reports. */
5796
+ lastHolderPollUtcMs() {
5797
+ return this.lastPollUtcMs;
5798
+ }
5672
5799
  async init() {
5673
5800
  const cursors = await this.cursorStorage.list(this.remoteName);
5674
5801
  const inboxOrdinal = cursors.find((c) => c.cursorType === "inbox")?.cursorOrdinal ?? 0;
@@ -6348,9 +6475,19 @@ var OutboxMode = /* @__PURE__ */ function(OutboxMode) {
6348
6475
  const defaultSyncManagerConfig = {
6349
6476
  maxDeadLettersPerRemote: 100,
6350
6477
  maxInboxBatchSize: 32,
6351
- maxHeldOperationsPerRemote: 1e4
6478
+ maxHeldOperationsPerRemote: 1e4,
6479
+ staleRemotePollWindowMs: 5 * 6e4
6352
6480
  };
6353
6481
  const PLAN_KEY_TO_JOB_UUID_CAP = 1e4;
6482
+ /**
6483
+ * Whether a channel failure says the caller could not authenticate or could not
6484
+ * reach the remote, rather than that the remote itself is misconfigured. The
6485
+ * remote record stays on disk for these so a retry after sign-in can re-add it.
6486
+ */
6487
+ function isCredentialOrNetworkError(error) {
6488
+ if (isDriveAuthError(error)) return true;
6489
+ return error instanceof GraphQLRequestError && error.category === "network";
6490
+ }
6354
6491
  /** Where a sync operation's run of ordinals begins. */
6355
6492
  function firstOrdinalOf(syncOp) {
6356
6493
  return syncOp.operations.length > 0 ? syncOp.operations[0].context.ordinal : 0;
@@ -6379,6 +6516,11 @@ var SyncManager = class {
6379
6516
  backfillAbortControllers = /* @__PURE__ */ new Map();
6380
6517
  planKeyToJobUuid = /* @__PURE__ */ new Map();
6381
6518
  evictedOutboxFloors = /* @__PURE__ */ new Map();
6519
+ prunePending = /* @__PURE__ */ new Set();
6520
+ pruneChain = Promise.resolve();
6521
+ derivingOutboxes = 0;
6522
+ pruneDrainDeferred = false;
6523
+ removing = /* @__PURE__ */ new Set();
6382
6524
  lastEnqueuedJobIdByKey = /* @__PURE__ */ new Map();
6383
6525
  inboxChunkChain = Promise.resolve();
6384
6526
  constructor(logger, remoteStorage, cursorStorage, deadLetterStorage, channelFactory, operationIndex, reactor, eventBus, driveContainerTypes, config = {}) {
@@ -6430,7 +6572,7 @@ var SyncManager = class {
6430
6572
  await channel.init();
6431
6573
  } catch (error) {
6432
6574
  this.logger.error("Error initializing channel for remote (@name, @error)", record.name, error instanceof Error ? error.message : String(error));
6433
- this.remotes.delete(record.name);
6575
+ await this.dropRemoteAfterFailedInit(remote, false);
6434
6576
  continue;
6435
6577
  }
6436
6578
  const outboxAckOrdinal = remote.channel.outbox.ackOrdinal;
@@ -6442,6 +6584,7 @@ var SyncManager = class {
6442
6584
  this.logger.error("Backfill failed for remote @RemoteName: @Error", remote.meta.name, error instanceof Error ? error : new Error(String(error)));
6443
6585
  }).finally(() => {
6444
6586
  this.backfillAbortControllers.delete(record.name);
6587
+ this.drainPrunes();
6445
6588
  });
6446
6589
  }
6447
6590
  }
@@ -6455,6 +6598,8 @@ var SyncManager = class {
6455
6598
  this.backfillAbortControllers.clear();
6456
6599
  this.planKeyToJobUuid.clear();
6457
6600
  this.lastEnqueuedJobIdByKey.clear();
6601
+ this.prunePending.clear();
6602
+ this.pruneDrainDeferred = false;
6458
6603
  this.batchAggregator.clear();
6459
6604
  if (this.eventUnsubscribe) {
6460
6605
  this.eventUnsubscribe();
@@ -6548,8 +6693,7 @@ var SyncManager = class {
6548
6693
  try {
6549
6694
  await channel.init();
6550
6695
  } catch (error) {
6551
- this.remotes.delete(name);
6552
- await this.remoteStorage.remove(name);
6696
+ await this.dropRemoteAfterFailedInit(remote, !isCredentialOrNetworkError(error));
6553
6697
  throw error;
6554
6698
  }
6555
6699
  const backfillController = new AbortController();
@@ -6559,6 +6703,7 @@ var SyncManager = class {
6559
6703
  this.logger.error("Backfill failed for remote @RemoteName: @Error", remote.meta.name, error instanceof Error ? error : new Error(String(error)));
6560
6704
  }).finally(() => {
6561
6705
  this.backfillAbortControllers.delete(name);
6706
+ this.drainPrunes();
6562
6707
  });
6563
6708
  return remote;
6564
6709
  }
@@ -6570,21 +6715,62 @@ var SyncManager = class {
6570
6715
  async remove(name) {
6571
6716
  const remote = this.remotes.get(name);
6572
6717
  if (!remote) throw new Error(`Remote with name '${name}' does not exist`);
6718
+ this.removing.add(name);
6719
+ try {
6720
+ await this.teardownRemoteResources(remote);
6721
+ await this.remoteStorage.remove(name);
6722
+ await this.cursorStorage.remove(name);
6723
+ } finally {
6724
+ this.remotes.delete(name);
6725
+ this.removing.delete(name);
6726
+ }
6727
+ }
6728
+ /**
6729
+ * Drops a remote whose channel.init() rejected, optionally removing its
6730
+ * stored record. Ordered like remove(): the registry slot is released last,
6731
+ * so a concurrent add of the same name cannot slip in and have its record
6732
+ * deleted by the removal below. A failure to tear down must not replace the
6733
+ * init error the caller has to classify.
6734
+ */
6735
+ async dropRemoteAfterFailedInit(remote, removeStorageRecord) {
6736
+ const name = remote.meta.name;
6737
+ this.removing.add(name);
6738
+ try {
6739
+ await this.teardownRemoteResources(remote);
6740
+ if (removeStorageRecord) await this.remoteStorage.remove(name);
6741
+ } catch (error) {
6742
+ this.logger.error("Error tearing down remote after failed init (@name, @error)", name, error instanceof Error ? error.message : String(error));
6743
+ } finally {
6744
+ this.remotes.delete(name);
6745
+ this.removing.delete(name);
6746
+ }
6747
+ }
6748
+ /**
6749
+ * Releases everything wiring a remote up holds: the in-flight backfill, the
6750
+ * channel, the status tracker entry and the connection-state subscription.
6751
+ * The registry slot is deliberately NOT released here -- the caller holds it
6752
+ * until any storage removal has finished, so a concurrent add of the same
6753
+ * name is refused for the whole teardown.
6754
+ */
6755
+ async teardownRemoteResources(remote) {
6756
+ const name = remote.meta.name;
6573
6757
  const backfillController = this.backfillAbortControllers.get(name);
6574
6758
  if (backfillController) {
6575
6759
  backfillController.abort();
6576
6760
  this.backfillAbortControllers.delete(name);
6577
6761
  }
6578
- await remote.channel.shutdown();
6579
- await this.remoteStorage.remove(name);
6580
- await this.cursorStorage.remove(name);
6581
- this.syncStatusTracker.untrackRemote(name);
6582
- const unsub = this.connectionStateUnsubscribes.get(name);
6583
- if (unsub) {
6584
- unsub();
6585
- this.connectionStateUnsubscribes.delete(name);
6762
+ try {
6763
+ await remote.channel.shutdown();
6764
+ } finally {
6765
+ this.syncStatusTracker.untrackRemote(name);
6766
+ const unsub = this.connectionStateUnsubscribes.get(name);
6767
+ if (unsub) {
6768
+ unsub();
6769
+ this.connectionStateUnsubscribes.delete(name);
6770
+ }
6771
+ this.evictedOutboxFloors.delete(name);
6772
+ this.prunePending.delete(name);
6586
6773
  }
6587
- this.remotes.delete(name);
6588
6774
  }
6589
6775
  list() {
6590
6776
  return Array.from(this.remotes.values());
@@ -6679,7 +6865,7 @@ var SyncManager = class {
6679
6865
  this.logger.debug("Loaded @count persisted dead letters for remote @name", records.length, remote.meta.name);
6680
6866
  }
6681
6867
  getRemotesForCollection(collectionId) {
6682
- return Array.from(this.remotes.values()).filter((remote) => remote.meta.collectionId.key === collectionId);
6868
+ return Array.from(this.remotes.values()).filter((remote) => remote.meta.collectionId.key === collectionId && !this.removing.has(remote.meta.name));
6683
6869
  }
6684
6870
  async processCompleteBatch(batch) {
6685
6871
  if (this.isShutdown) return;
@@ -6690,7 +6876,11 @@ var SyncManager = class {
6690
6876
  for (const remote of remotes) if (!affectedRemotes.includes(remote)) affectedRemotes.push(remote);
6691
6877
  }
6692
6878
  for (const remote of affectedRemotes) trimMailboxFromBatch(remote.channel.inbox, batch);
6693
- for (const remote of affectedRemotes) await this.updateOutbox(remote, remote.channel.outbox.latestOrdinal, OutboxMode.BatchTriggered);
6879
+ for (const remote of affectedRemotes) {
6880
+ if (!this.remotes.has(remote.meta.name) || this.removing.has(remote.meta.name)) continue;
6881
+ await this.updateOutbox(remote, remote.channel.outbox.latestOrdinal, OutboxMode.BatchTriggered);
6882
+ }
6883
+ await this.drainPrunes();
6694
6884
  }
6695
6885
  handleInboxAdded(remote, syncOps) {
6696
6886
  if (this.isShutdown) return;
@@ -6891,15 +7081,95 @@ var SyncManager = class {
6891
7081
  const floor = firstOrdinalOf(evicted[0]);
6892
7082
  const known = this.evictedOutboxFloors.get(remote.meta.name);
6893
7083
  this.evictedOutboxFloors.set(remote.meta.name, known === void 0 ? floor : Math.min(known, floor));
6894
- this.logger.warn("Outbox for @RemoteName is past its bound of @Cap operations; evicting @Count entries from ordinal @Floor, to be derived again once it drains", remote.meta.name, cap, evicted.length, floor);
7084
+ const staleMs = this.stalePollAgeMs(remote);
7085
+ if (staleMs !== void 0) {
7086
+ let held = kept;
7087
+ for (const syncOp of evicted) held += syncOp.operations.length;
7088
+ if (!this.prunePending.has(remote.meta.name)) {
7089
+ this.prunePending.add(remote.meta.name);
7090
+ this.logger.warn("Outbox for @RemoteName (@Collection) is past its bound of @Cap operations holding @Held, and it has not been polled for @StaleMs ms; marking the channel for removal once this derivation ends", remote.meta.name, remote.meta.collectionId.key, cap, held, staleMs);
7091
+ }
7092
+ } else this.logger.warn("Outbox for @RemoteName is past its bound of @Cap operations; evicting @Count entries from ordinal @Floor, to be derived again once it drains", remote.meta.name, cap, evicted.length, floor);
6895
7093
  remote.channel.outbox.remove(...evicted);
6896
7094
  }
7095
+ /**
7096
+ * How long a served remote's holder has been silent, if past the window.
7097
+ *
7098
+ * The channel is asked, rather than its config inspected: SyncManager serves
7099
+ * and subscribes with the same interface, both kinds report lastSuccessUtcMs,
7100
+ * and the caller-supplied channelConfig.type is a free-form string the
7101
+ * factories do not read -- so neither could tell a dead served channel from a
7102
+ * client whose switchboard is merely unreachable. Only a channel that claims
7103
+ * a holder by reporting when it last heard from one can be removed for that
7104
+ * holder's silence; a channel that reports nothing (or 0) is never pruned.
7105
+ */
7106
+ stalePollAgeMs(remote) {
7107
+ const last = remote.channel.lastHolderPollUtcMs();
7108
+ if (last === void 0 || last <= 0) return;
7109
+ const age = Date.now() - last;
7110
+ return age >= this.config.staleRemotePollWindowMs ? age : void 0;
7111
+ }
7112
+ /**
7113
+ * Removes the remotes marked stale during eviction.
7114
+ *
7115
+ * Removing a remote that a derivation is still iterating would pull its
7116
+ * mailboxes out from under it, so a drain that arrives during one is deferred
7117
+ * rather than run; the last derivation to finish re-arms it.
7118
+ */
7119
+ drainPrunes() {
7120
+ if (this.derivingOutboxes > 0) {
7121
+ this.pruneDrainDeferred = true;
7122
+ return Promise.resolve();
7123
+ }
7124
+ const next = this.pruneChain.then(async () => {
7125
+ if (this.isShutdown) return;
7126
+ for (const name of [...this.prunePending]) {
7127
+ if (this.derivingOutboxes > 0) {
7128
+ this.pruneDrainDeferred = true;
7129
+ return;
7130
+ }
7131
+ this.prunePending.delete(name);
7132
+ const remote = this.remotes.get(name);
7133
+ if (!remote) continue;
7134
+ if (this.stalePollAgeMs(remote) === void 0) {
7135
+ this.logger.info("Stale removal of @name revoked: its holder polled while the outbox was being derived", name);
7136
+ continue;
7137
+ }
7138
+ try {
7139
+ await this.remove(name);
7140
+ } catch (error) {
7141
+ this.logger.error("Failed to remove stale remote (@name, @error)", name, error instanceof Error ? error.message : String(error));
7142
+ }
7143
+ }
7144
+ });
7145
+ this.pruneChain = next.catch(() => {});
7146
+ return next;
7147
+ }
6897
7148
  outboxOperationCount(remote) {
6898
7149
  let count = 0;
6899
7150
  for (const syncOp of remote.channel.outbox.items) count += syncOp.operations.length;
6900
7151
  return count;
6901
7152
  }
7153
+ /**
7154
+ * Derives this remote's outbox, holding off prunes for the duration.
7155
+ *
7156
+ * A backfill elsewhere can finish at any await in here and drain the prunes
7157
+ * it marked; the count is what keeps that drain from removing the remote this
7158
+ * call is still adding to.
7159
+ */
6902
7160
  async updateOutbox(remote, ackOrdinal, mode = OutboxMode.Backfill, signal) {
7161
+ this.derivingOutboxes++;
7162
+ try {
7163
+ await this.deriveOutbox(remote, ackOrdinal, mode, signal);
7164
+ } finally {
7165
+ this.derivingOutboxes--;
7166
+ if (this.derivingOutboxes === 0 && this.pruneDrainDeferred) {
7167
+ this.pruneDrainDeferred = false;
7168
+ this.drainPrunes();
7169
+ }
7170
+ }
7171
+ }
7172
+ async deriveOutbox(remote, ackOrdinal, mode, signal) {
6903
7173
  const composedSignal = signal ? AbortSignal.any([signal, this.abortController.signal]) : this.abortController.signal;
6904
7174
  const startOrdinal = this.refillOrdinal(remote, ackOrdinal);
6905
7175
  let maxOrdinal = startOrdinal;
@@ -7001,6 +7271,10 @@ var SyncBuilder = class {
7001
7271
  this.config.maxHeldOperationsPerRemote = limit;
7002
7272
  return this;
7003
7273
  }
7274
+ withStaleRemotePollWindowMs(windowMs) {
7275
+ this.config.staleRemotePollWindowMs = windowMs;
7276
+ return this;
7277
+ }
7004
7278
  build(reactor, logger, operationIndex, eventBus, db, driveContainerTypes) {
7005
7279
  return this.buildModule(reactor, logger, operationIndex, eventBus, db, driveContainerTypes).syncManager;
7006
7280
  }
@@ -7166,6 +7440,16 @@ var Reactor = class {
7166
7440
  throwIfAborted(signal, () => new AbortError());
7167
7441
  return relationships.results.map((rel) => rel.sourceId);
7168
7442
  }
7443
+ async getOutgoingRelationshipEdges(sourceId, relationshipType, paging, consistencyToken, signal) {
7444
+ const relationships = await this.documentIndexer.getOutgoing(sourceId, relationshipType ? [relationshipType] : void 0, paging, consistencyToken, signal);
7445
+ throwIfAborted(signal, () => new AbortError());
7446
+ return relationships;
7447
+ }
7448
+ async getIncomingRelationshipEdges(targetId, relationshipType, paging, consistencyToken, signal) {
7449
+ const relationships = await this.documentIndexer.getIncoming(targetId, relationshipType ? [relationshipType] : void 0, paging, consistencyToken, signal);
7450
+ throwIfAborted(signal, () => new AbortError());
7451
+ return relationships;
7452
+ }
7169
7453
  async getOperations(documentId, view, filter, paging, consistencyToken, signal) {
7170
7454
  this.logger.verbose("getOperations(@documentId, @view, @filter, @paging)", documentId, view, filter, paging);
7171
7455
  const branch = view?.branch || "main";
@@ -7528,10 +7812,17 @@ var Reactor = class {
7528
7812
  }
7529
7813
  return { jobs: Object.fromEntries(jobInfos) };
7530
7814
  }
7531
- async addRelationship(sourceId, targetId, relationshipType, branch = "main", signer, signal) {
7532
- this.logger.verbose("addRelationship(@sourceId, @targetId, @relationshipType, @branch)", sourceId, targetId, relationshipType, branch);
7815
+ async addRelationship(sourceId, targetId, relationshipType, metadata, branch = "main", signer, signal) {
7816
+ this.logger.verbose("addRelationship(@sourceId, @targetId, @relationshipType, @metadata, @branch)", sourceId, targetId, relationshipType, metadata, branch);
7533
7817
  throwIfAborted(signal, () => new AbortError());
7534
- let actions = [addRelationshipAction(sourceId, targetId, relationshipType)];
7818
+ let actions = [addRelationshipAction(sourceId, targetId, relationshipType, metadata)];
7819
+ if (signer) actions = await signActions(actions, signer, signal);
7820
+ return await this.execute(sourceId, branch, actions, signal);
7821
+ }
7822
+ async updateRelationship(sourceId, targetId, relationshipType, metadata, branch = "main", signer, signal) {
7823
+ this.logger.verbose("updateRelationship(@sourceId, @targetId, @relationshipType, @metadata, @branch)", sourceId, targetId, relationshipType, metadata, branch);
7824
+ throwIfAborted(signal, () => new AbortError());
7825
+ let actions = [updateRelationshipAction(sourceId, targetId, relationshipType, metadata)];
7535
7826
  if (signer) actions = await signActions(actions, signer, signal);
7536
7827
  return await this.execute(sourceId, branch, actions, signal);
7537
7828
  }
@@ -7903,19 +8194,31 @@ var ReactorBuilder = class {
7903
8194
  } else executorManager = new SimpleJobExecutorManager(() => new SimpleJobExecutor(this.logger, documentModelRegistry, operationStore, eventBus, writeCache, operationIndex, documentMetaCache, collectionMembershipCache, this.driveContainerTypes, this.executorConfig, this.signatureVerifier, executionScope), eventBus, queue, jobTracker, this.logger, resolver, this.executorConfig.jobTimeoutMs, this.executorConfig.deferredJobTtlMs);
7904
8195
  await executorManager.start(executorStartCount);
7905
8196
  const callerReadModels = Array.from(new Set([...this.readModels]));
8197
+ const degradedComponents = [];
8198
+ const startDegraded = (component, error) => {
8199
+ degradedComponents.push({
8200
+ component,
8201
+ error: error instanceof Error ? error : new Error(String(error))
8202
+ });
8203
+ this.logger?.error("Reactor component started degraded: @component", component, error);
8204
+ };
8205
+ const readModelIndexing = {
8206
+ commitChunkSize: 50,
8207
+ yieldDeadlineMs: this.executorConfig.yieldDeadlineMs ?? 50
8208
+ };
7906
8209
  const documentViewConsistencyTracker = new ConsistencyTracker();
7907
- const documentView = new KyselyDocumentView(database, operationStore, operationIndex, writeCache, documentViewConsistencyTracker, featureFlags.documentDecisions);
8210
+ const documentView = new KyselyDocumentView(database, operationStore, operationIndex, writeCache, documentViewConsistencyTracker, featureFlags.documentDecisions ? DeletedDocumentRead.StateAtDeletion : DeletedDocumentRead.NotFound, readModelIndexing);
7908
8211
  try {
7909
8212
  await documentView.init();
7910
8213
  } catch (error) {
7911
- console.error("Error initializing document view", error);
8214
+ startDegraded("document view", error);
7912
8215
  }
7913
8216
  const documentIndexerConsistencyTracker = new ConsistencyTracker();
7914
- const documentIndexer = new KyselyDocumentIndexer(database, operationIndex, writeCache, documentIndexerConsistencyTracker);
8217
+ const documentIndexer = new KyselyDocumentIndexer(database, operationIndex, writeCache, documentIndexerConsistencyTracker, readModelIndexing);
7915
8218
  try {
7916
8219
  await documentIndexer.init();
7917
8220
  } catch (error) {
7918
- console.error("Error initializing document indexer", error);
8221
+ startDegraded("document indexer", error);
7919
8222
  }
7920
8223
  const subscriptionManager = new ReactorSubscriptionManager(new DefaultSubscriptionErrorHandler());
7921
8224
  const subscriptionNotificationReadModel = new SubscriptionNotificationReadModel(subscriptionManager, documentView);
@@ -7924,9 +8227,9 @@ var ReactorBuilder = class {
7924
8227
  try {
7925
8228
  await processorManager.init();
7926
8229
  } catch (error) {
7927
- console.error("Error initializing processor manager", error);
8230
+ startDegraded("processor manager", error);
7928
8231
  }
7929
- for (const factory of this.readModelFactories) {
8232
+ for (const [index, factory] of this.readModelFactories.entries()) try {
7930
8233
  const readModel = await factory({
7931
8234
  documentModelRegistry,
7932
8235
  operationIndex,
@@ -7934,6 +8237,8 @@ var ReactorBuilder = class {
7934
8237
  processorManagerConsistencyTracker
7935
8238
  });
7936
8239
  callerReadModels.push(readModel);
8240
+ } catch (error) {
8241
+ startDegraded(`read model ${index}${factory.name ? ` (${factory.name})` : ""}`, error);
7937
8242
  }
7938
8243
  const readModelInstances = [
7939
8244
  ...callerReadModels,
@@ -7952,9 +8257,9 @@ var ReactorBuilder = class {
7952
8257
  processorManager,
7953
8258
  documentView,
7954
8259
  documentIndexer,
7955
- createProjectionShardManager: (config) => this.createProjectionShardManager(config, eventBus, hostTrackers, false),
8260
+ createProjectionShardManager: (config) => this.createProjectionShardManager(config, eventBus, hostTrackers, readModelIndexing, false),
7956
8261
  registerShutdownHook: (hook) => this.shutdownHooks.push(hook)
7957
- }) : this.projectionShardConfig ? await this.createProjectionShardManager(this.projectionShardConfig, eventBus, hostTrackers, true) : new ReadModelCoordinator(eventBus, readModelInstances, [subscriptionNotificationReadModel, processorManager]);
8262
+ }) : this.projectionShardConfig ? await this.createProjectionShardManager(this.projectionShardConfig, eventBus, hostTrackers, readModelIndexing, true) : new ReadModelCoordinator(eventBus, readModelInstances, [subscriptionNotificationReadModel, processorManager]);
7958
8263
  const reactor = new Reactor(this.logger, documentModelRegistry, queue, jobTracker, readModelCoordinator, this.features, documentView, documentIndexer, operationStore, eventBus, executorManager);
7959
8264
  let syncModule = void 0;
7960
8265
  if (this.channelScheme) {
@@ -7993,8 +8298,10 @@ var ReactorBuilder = class {
7993
8298
  syncModule,
7994
8299
  reactor,
7995
8300
  groupReevaluationTrigger,
7996
- pools: this.instrumentedPools
8301
+ pools: this.instrumentedPools,
8302
+ degradedComponents
7997
8303
  };
8304
+ if (degradedComponents.length > 0) this.logger.warn("Reactor started with @count degraded component(s): @components", degradedComponents.length, degradedComponents.map(({ component }) => component).join(", "));
7998
8305
  if (this.signalHandlersEnabled) this.attachSignalHandlers(module);
7999
8306
  return module;
8000
8307
  }
@@ -8023,11 +8330,15 @@ var ReactorBuilder = class {
8023
8330
  * models never index an operation, so the manager advances these from the
8024
8331
  * shards' relayed indexing reports; without them every read carrying a
8025
8332
  * consistency token waits forever.
8333
+ * @param indexing The chunking bounds the host's own read models index
8334
+ * under. The shards' read models are built inside the worker, so without
8335
+ * this they would fall back to the library default and a host that tuned
8336
+ * the cadence would silently get it on the in-process path only.
8026
8337
  * @param registerShutdownHook Whether the builder owns `manager.shutdown()`
8027
8338
  * at signal time. False for the coordinator-factory path, whose factory
8028
8339
  * registers its own hook so host chains drain before the worker stops.
8029
8340
  */
8030
- async createProjectionShardManager(config, eventBus, consistencyTrackers, registerShutdownHook) {
8341
+ async createProjectionShardManager(config, eventBus, consistencyTrackers, indexing, registerShutdownHook) {
8031
8342
  const parentDb = this.resolveReactorDbConfig();
8032
8343
  const baseDb = config.db ?? parentDb;
8033
8344
  const caller = this.projectionShardConfig !== void 0 ? "withProjectionShards" : "withReadModelCoordinatorFactory";
@@ -8053,13 +8364,14 @@ var ReactorBuilder = class {
8053
8364
  poolInstrumentations.push(forwarder);
8054
8365
  this.instrumentedPools.push(forwarder);
8055
8366
  }
8056
- const { ProjectionShardManager } = await import("./projection-shard-manager-CPQc5XHL.js");
8367
+ const { ProjectionShardManager } = await import("./projection-shard-manager-CUo4bF4O.js");
8057
8368
  const manager = new ProjectionShardManager({
8058
8369
  shardCount: config.shardCount,
8059
8370
  db,
8060
8371
  models,
8061
8372
  preReadyKinds: config.preReadyKinds,
8062
8373
  postReadyKinds: config.postReadyKinds,
8374
+ indexing,
8063
8375
  factory,
8064
8376
  logger: this.logger,
8065
8377
  hostBus: eventBus,
@@ -8098,7 +8410,7 @@ var ReactorBuilder = class {
8098
8410
  const [{ WorkerHandle }, { createThreadTransport }, { workerEntryPath }] = await Promise.all([
8099
8411
  import("./worker-handle-CrERzl8s.js"),
8100
8412
  import("./transport-ByGviWdZ.js"),
8101
- import("./worker-HPysBfhx.js").then((n) => n.n)
8413
+ import("./worker-g03lNM42.js").then((n) => n.n)
8102
8414
  ]);
8103
8415
  const poolConfig = {
8104
8416
  enabled: true,
@@ -8792,6 +9104,6 @@ var DocumentIntegrityService = class {
8792
9104
  }
8793
9105
  };
8794
9106
  //#endregion
8795
- export { ALWAYS_READABLE_SCOPES, APPEND_CONDITION_FAILED_PREFIX, AppendConditionFailedError, AuthEnforcementDisabledError, BareReadGate, BaseReadModel, ChannelError, ChannelErrorSource, ChannelScheme, ConsistencyTracker, DEFAULT_DRIVE_CONTAINER_TYPES, DOCUMENT_INDEXER_READ_MODEL, DOCUMENT_VIEW_READ_MODEL, DRIVE_AUTH_ERROR_MESSAGES, DefaultSubscriptionErrorHandler, DocumentAlreadyExistsError, DocumentChangeType, DocumentExistence, DocumentIntegrityService, DocumentModelRegistry, DocumentModelResolver, DriveClient, DriveCollectionId, DuplicateManifestError, DuplicateModuleError, DuplicateOperationError, EventBus, EventBusAggregateError, GqlRequestChannel, GqlRequestChannelFactory, GqlResponseChannel, GqlResponseChannelFactory, HybridProjectionCoordinator, SimpleJobExecutor as InMemoryJobExecutor, SimpleJobExecutor, InMemoryJobTracker, InMemoryQueue, IntervalPollTimer, InvalidModuleError, JobAwaiter, JobExecutorEventTypes, JobStatus, KyselyDocumentIndexer, KyselyDocumentView, KyselyKeyframeStore, KyselyOperationStore, KyselySyncCursorStorage, KyselySyncRemoteStorage, KyselyWriteCache, Mailbox, ModelReadGate, ModuleNotFoundError, NullDocumentModelResolver, OptimisticLockError, PollBehavior, PollingChannelError, ProcessorManager, PropagationMode, QueueEventTypes, REACTOR_SCHEMA, RECOVERABLE_GRAPHQL_ERROR_CODES, Reactor, ReactorBuilder, ReactorClient, ReactorClientBuilder, ReactorEventTypes, ReactorSubscriptionManager, ReadModelCoordinator, RelationalDbProcessor, RelationshipChangeType, RetryAccounting, RevisionMismatchError, SeededStateReader, SimpleJobExecutorManager, SyncBuilder, SyncEventTypes, SyncOperation, SyncOperationAggregateError, SyncOperationStatus, SyncScopeGate, SyncStatus, SyncStatusTracker, addRelationshipAction, authDecisionModel, batchOperationsByDocument, buildDecisionModel, classifyJobFailure, consolidateSyncOperations, createDocumentAction, createForwardingLogger, createHybridProjectionCoordinatorFactory, createMutableShutdownStatus, createReactorHostModuleBase, createRelationalDb, decideAtHead, deleteDocumentAction, documentActions, documentDecisionModel, driveIdFromUrl, envelopesToSyncOperations, errorToInfo, firstOutOfOrderPair, getMigrationStatus, instrumentPgPool, isDriveAuthError, isRecoverableGraphQLError, makeConsistencyKey, parseDriveUrl, parsePagingOptions, quarantinesDocument, readDecisionModel, removeRelationshipAction, runMigrations, sanitizeArg, selectDecisionModel, supportsLiveReadModelRegistration, syncOperationErrorType, trimMailboxFromAckOrdinal, updateRelationshipAction, upgradeDocumentAction, workerEntryPath };
9107
+ export { ALWAYS_READABLE_SCOPES, APPEND_CONDITION_FAILED_PREFIX, AppendConditionFailedError, AuthEnforcementDisabledError, BareReadGate, BaseReadModel, ChannelError, ChannelErrorSource, ChannelScheme, ConsistencyTracker, DEFAULT_COMMIT_CHUNK_SIZE, DEFAULT_DRIVE_CONTAINER_TYPES, DEFAULT_READ_MODEL_YIELD_DEADLINE_MS, DOCUMENT_INDEXER_READ_MODEL, DOCUMENT_VIEW_READ_MODEL, DRIVE_AUTH_ERROR_MESSAGES, DefaultSubscriptionErrorHandler, DeletedDocumentRead, DocumentAlreadyExistsError, DocumentChangeType, DocumentExistence, DocumentIntegrityService, DocumentModelRegistry, DocumentModelResolver, DriveClient, DriveCollectionId, DriveRequestError, DuplicateManifestError, DuplicateModuleError, DuplicateOperationError, EventBus, EventBusAggregateError, GqlRequestChannel, GqlRequestChannelFactory, GqlResponseChannel, GqlResponseChannelFactory, HybridProjectionCoordinator, SimpleJobExecutor as InMemoryJobExecutor, SimpleJobExecutor, InMemoryJobTracker, InMemoryQueue, IntervalPollTimer, InvalidModuleError, JobAwaiter, JobExecutorEventTypes, JobStatus, KyselyDocumentIndexer, KyselyDocumentView, KyselyKeyframeStore, KyselyOperationStore, KyselySyncCursorStorage, KyselySyncRemoteStorage, KyselyWriteCache, Mailbox, ModelReadGate, ModuleNotFoundError, NullDocumentModelResolver, OptimisticLockError, PollBehavior, PollingChannelError, ProcessorManager, PropagationMode, QueueEventTypes, REACTOR_SCHEMA, RECOVERABLE_GRAPHQL_ERROR_CODES, Reactor, ReactorBuilder, ReactorClient, ReactorClientBuilder, ReactorEventTypes, ReactorSubscriptionManager, ReadModelCoordinator, RelationalDbProcessor, RelationshipChangeType, RelationshipNotFoundError, RetryAccounting, RevisionMismatchError, SeededStateReader, SimpleJobExecutorManager, SyncBuilder, SyncEventTypes, SyncOperation, SyncOperationAggregateError, SyncOperationStatus, SyncScopeGate, SyncStatus, SyncStatusTracker, addRelationshipAction, authDecisionModel, batchOperationsByDocument, buildDecisionModel, classifyJobFailure, consolidateSyncOperations, createDocumentAction, createForwardingLogger, createHybridProjectionCoordinatorFactory, createMutableShutdownStatus, createReactorHostModuleBase, createRelationalDb, decideAtHead, defaultReadModelIndexingConfig, deleteDocumentAction, documentActions, documentDecisionModel, driveIdFromUrl, envelopesToSyncOperations, errorToInfo, firstOutOfOrderPair, getMigrationStatus, instrumentPgPool, isDriveAuthError, isRecoverableGraphQLError, makeConsistencyKey, parseDriveUrl, parsePagingOptions, quarantinesDocument, readDecisionModel, removeRelationshipAction, runMigrations, sanitizeArg, selectDecisionModel, supportsLiveReadModelRegistration, syncOperationErrorType, trimMailboxFromAckOrdinal, unchunkedReadModelIndexingConfig, updateRelationshipAction, upgradeDocumentAction, workerEntryPath };
8796
9108
 
8797
9109
  //# sourceMappingURL=index.js.map