@powerhousedao/reactor 6.2.3-dev.23 → 6.2.3-dev.25

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.
Files changed (40) hide show
  1. package/dist/{build-worker-executor-DUjyF4NM.js → build-worker-executor-DPGjh9Uc.js} +24 -8
  2. package/dist/build-worker-executor-DPGjh9Uc.js.map +1 -0
  3. package/dist/{document-indexer-CQzTEE1o.js → document-indexer-zbdzqZ3K.js} +18 -9
  4. package/dist/{document-indexer-CQzTEE1o.js.map → document-indexer-zbdzqZ3K.js.map} +1 -1
  5. package/dist/entry.d.ts +2 -1
  6. package/dist/entry.js +10 -6
  7. package/dist/entry.js.map +1 -1
  8. package/dist/{errors-D3S6Eysd.js → errors-BM_ULVWd.js} +9 -1
  9. package/dist/{errors-D3S6Eysd.js.map → errors-BM_ULVWd.js.map} +1 -1
  10. package/dist/{drive-container-types-CS5IxDiA.js → forwarding-logger-CSsCJ38f.js} +1081 -174
  11. package/dist/forwarding-logger-CSsCJ38f.js.map +1 -0
  12. package/dist/index.d.ts +2248 -1978
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +438 -258
  15. package/dist/index.js.map +1 -1
  16. package/dist/projection-entry.d.ts +2 -1
  17. package/dist/projection-entry.js +7 -13
  18. package/dist/projection-entry.js.map +1 -1
  19. package/dist/{projection-shard-manager-CUo4bF4O.js → projection-shard-manager-B-j8VYzA.js} +2 -3
  20. package/dist/{projection-shard-manager-CUo4bF4O.js.map → projection-shard-manager-B-j8VYzA.js.map} +1 -1
  21. package/dist/{projection-worker-wI4PwcV2.js → projection-worker-D8uNFdwX.js} +1 -1
  22. package/dist/{projection-worker-wI4PwcV2.js.map → projection-worker-D8uNFdwX.js.map} +1 -1
  23. package/dist/{transport-CuogVKN_.js → transport-B3G50KVd.js} +1 -1
  24. package/dist/{transport-CuogVKN_.js.map → transport-B3G50KVd.js.map} +1 -1
  25. package/dist/{transport-ByGviWdZ.js → transport-CZDHzfvy.js} +1 -1
  26. package/dist/{transport-ByGviWdZ.js.map → transport-CZDHzfvy.js.map} +1 -1
  27. package/dist/{worker-handle-CrERzl8s.js → worker-handle-CwWNTpUg.js} +22 -16
  28. package/dist/worker-handle-CwWNTpUg.js.map +1 -0
  29. package/package.json +7 -8
  30. package/dist/build-worker-executor-DUjyF4NM.js.map +0 -1
  31. package/dist/drive-container-types-CS5IxDiA.js.map +0 -1
  32. package/dist/error-info-Cpu4OY3o.js +0 -62
  33. package/dist/error-info-Cpu4OY3o.js.map +0 -1
  34. package/dist/forwarding-logger-BBkMSxuJ.js +0 -85
  35. package/dist/forwarding-logger-BBkMSxuJ.js.map +0 -1
  36. package/dist/types-DMKLa0Ok.js +0 -33
  37. package/dist/types-DMKLa0Ok.js.map +0 -1
  38. package/dist/worker-g03lNM42.js +0 -22
  39. package/dist/worker-g03lNM42.js.map +0 -1
  40. package/dist/worker-handle-CrERzl8s.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,10 +1,7 @@
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
- import { n as ReactorEventTypes, t as EventBusAggregateError } from "./types-DMKLa0Ok.js";
3
- import { i as WorkerInitFailedError, r as WorkerExitedError, t as WorkerAbortTimeoutError } from "./errors-D3S6Eysd.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-CQzTEE1o.js";
5
- import { n as errorToInfo, r as sanitizeArg, t as createForwardingLogger } from "./forwarding-logger-BBkMSxuJ.js";
6
- import { t as workerEntryPath } from "./worker-g03lNM42.js";
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";
1
+ import { $ as DocumentNotFoundError, A as ModuleNotFoundError, B as authDecisionModel, C as DocumentMetaCache, D as DuplicateManifestError, E as JobExecutorEventTypes, F as summarizeSubmittedActions, G as DocumentExistence, H as APPEND_CONDITION_FAILED_PREFIX, I as targetDocumentId, J as RevisionMismatchError, K as DuplicateOperationError, L as decideAtHead, M as createConsistencyToken, N as createEmptyConsistencyToken, O as DuplicateModuleError, P as submittedActionIds, Q as DocumentDeletedError, R as selectDecisionModel, S as KyselyOperationIndex, T as DEFAULT_DEFERRED_JOB_TTL_MS, U as AppendConditionFailedError, V as buildDecisionModel, W as DocumentAlreadyExistsError, X as AuthTimestampNotMonotonicError, Y as AuthEnforcementDisabledError, Z as AuthorizationDeniedError, _ as DriveCollectionId, a as DEFAULT_DRIVE_CONTAINER_TYPES, at as EventBusAggregateError, b as EventBus, c as runMigrations, ct as parsePagingOptions, d as KyselyOperationStore, dt as __exportAll, et as ExcessiveReshuffleError, f as KyselyKeyframeStore, g as PassthroughSigner, h as verifyActionSignature, it as UpgradePreconditionFailedError, j as GATED_DOCUMENT_ACTIONS, k as InvalidModuleError, l as createForwardingPoolInstrumentation, lt as throwIfAborted, m as SimpleJobExecutor, n as errorToInfo, nt as InvalidSignatureError, o as REACTOR_SCHEMA, ot as ReactorEventTypes, p as DocumentModelRegistry, q as OptimisticLockError, r as sanitizeArg, rt as RelationshipNotFoundError, s as getMigrationStatus, st as matchesScope, t as createForwardingLogger, tt as InvalidOperationTimestampError, u as instrumentPgPool, v as KyselyExecutionScope, w as CollectionMembershipCache, x as KyselyWriteCache, y as resolveFeatureFlags, z as documentDecisionModel } from "./forwarding-logger-CSsCJ38f.js";
2
+ import { i as WorkerInitFailedError, r as WorkerExitedError, t as WorkerAbortTimeoutError } from "./errors-BM_ULVWd.js";
3
+ 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-zbdzqZ3K.js";
4
+ import { DEFAULT_SIGNATURE_POLICY, DowngradeNotSupportedError, MAX_AUTH_GRANTS, UnsupportedDocumentModelVersionError, actionSignerIdentity, actionSigningTarget, actions, actions as documentActions, createCopyHeader, decide, deserializeSignature, garbageCollect, generateId, groupDocumentType, groupMembershipActionTypes, hashDocumentStateForScope, normalizeDocumentModelVersion, requestedSignaturePolicy, sortOperations, toTransportAction, withSignaturePolicy } from "@powerhousedao/shared/document-model";
8
5
  import { addFile, addFolder, copyNode, deleteNode, driveCreateDocument, generateNodesCopy, getDescendants, handleTargetNameCollisions, isFileNode, isFolderNode, moveNode, updateNode } from "@powerhousedao/shared/document-drive";
9
6
  import { v4 } from "uuid";
10
7
  import { ConsoleLogger, childLogger } from "document-model";
@@ -237,37 +234,31 @@ function getSharedActionScope(actions) {
237
234
  return baseScope;
238
235
  }
239
236
  /**
240
- * Signs an action with the provided signer.
241
- * If the action already has valid signatures, it is returned unchanged.
237
+ * Signs an action for `target`, the log it is written to. An action already
238
+ * signed under a key is returned unchanged; one whose signer or last tuple has
239
+ * no key is unsigned, and is signed like any other.
242
240
  */
243
- const signAction = async (action, signer, signal) => {
244
- const existingSignatures = action.context?.signer?.signatures;
245
- if (existingSignatures && existingSignatures.length > 0) return action;
246
- const signature = await signer.signAction(action, signal);
241
+ const signAction = async (action, signer, target, signal) => {
242
+ const existing = action.context?.signer;
243
+ if (existing?.app?.key && existing.signatures.at(-1)?.[1]) return action;
244
+ const signature = await signer.signAction(action, target, signal);
247
245
  return {
248
246
  ...action,
249
247
  context: {
250
248
  ...action.context,
251
249
  signer: {
252
- user: {
253
- address: signer.user?.address || "",
254
- networkId: signer.user?.networkId || "",
255
- chainId: signer.user?.chainId || 0
256
- },
257
- app: {
258
- name: signer.app?.name || "",
259
- key: signer.app?.key || ""
260
- },
250
+ ...actionSignerIdentity(signer),
261
251
  signatures: [signature]
262
252
  }
263
253
  }
264
254
  };
265
255
  };
266
256
  /**
267
- * Signs multiple actions with the provided signer
257
+ * Signs the actions of a job on `job.documentId`, each for the log it is
258
+ * written to: a relationship action lands in its source document.
268
259
  */
269
- const signActions = async (actions, signer, signal) => {
270
- return Promise.all(actions.map((action) => signAction(action, signer, signal)));
260
+ const signActions = async (actions, signer, job, signal) => {
261
+ return Promise.all(actions.map((action) => signAction(action, signer, actionSigningTarget(action, job.documentId, job.branch), signal)));
271
262
  };
272
263
  function buildSingleJobMeta(jobId, callerMeta) {
273
264
  return {
@@ -321,6 +312,10 @@ let JobStatus = /* @__PURE__ */ function(JobStatus) {
321
312
  * during construction, so the partial-`this` hazard does not apply.
322
313
  */
323
314
  var DriveClient = class {
315
+ client;
316
+ logger;
317
+ reactor;
318
+ signer;
324
319
  constructor(client, logger, reactor, signer) {
325
320
  this.client = client;
326
321
  this.logger = logger;
@@ -329,11 +324,11 @@ var DriveClient = class {
329
324
  }
330
325
  async create(input, signal) {
331
326
  this.logger.verbose("drives.create(@input)", input);
332
- const driveDoc = driveCreateDocument({ global: {
327
+ const driveDoc = withSignaturePolicy(driveCreateDocument({ global: {
333
328
  name: input.global.name || "",
334
329
  icon: input.global.icon ?? null,
335
330
  nodes: []
336
- } });
331
+ } }), requestedSignaturePolicy(input, await this.client.getCreateSignaturePolicy()), { protocolVersions: input.protocolVersions });
337
332
  if (input.preferredEditor) driveDoc.header.meta = {
338
333
  ...driveDoc.header.meta,
339
334
  preferredEditor: input.preferredEditor
@@ -343,24 +338,25 @@ var DriveClient = class {
343
338
  async addFile(driveIdentifier, document, parentFolder, signal) {
344
339
  this.logger.verbose("drives.addFile(@driveIdentifier, @document, @parentFolder)", driveIdentifier, document.header.id, parentFolder);
345
340
  const documentId = document.header.id;
341
+ const createInput = {
342
+ model: document.header.documentType,
343
+ version: 0,
344
+ documentId: document.header.id,
345
+ signing: {
346
+ signature: document.header.id,
347
+ publicKey: document.header.sig.publicKey,
348
+ nonce: document.header.sig.nonce,
349
+ createdAtUtcIso: document.header.createdAtUtcIso,
350
+ documentType: document.header.documentType
351
+ },
352
+ slug: document.header.slug,
353
+ name: document.header.name,
354
+ branch: document.header.branch,
355
+ meta: document.header.meta,
356
+ protocolVersions: document.header.protocolVersions ?? { "base-reducer": 2 }
357
+ };
346
358
  const documentActions = await signActions([
347
- createDocumentAction({
348
- model: document.header.documentType,
349
- version: 0,
350
- documentId: document.header.id,
351
- signing: {
352
- signature: document.header.id,
353
- publicKey: document.header.sig.publicKey,
354
- nonce: document.header.sig.nonce,
355
- createdAtUtcIso: document.header.createdAtUtcIso,
356
- documentType: document.header.documentType
357
- },
358
- slug: document.header.slug,
359
- name: document.header.name,
360
- branch: document.header.branch,
361
- meta: document.header.meta,
362
- protocolVersions: document.header.protocolVersions ?? { "base-reducer": 2 }
363
- }),
359
+ createDocumentAction(createInput),
364
360
  upgradeDocumentAction({
365
361
  documentId: document.header.id,
366
362
  model: document.header.documentType,
@@ -369,13 +365,19 @@ var DriveClient = class {
369
365
  initialState: document.state
370
366
  }),
371
367
  addRelationshipAction(driveIdentifier, documentId, "child")
372
- ], this.signer, signal);
368
+ ], this.signer, {
369
+ documentId,
370
+ branch: "main"
371
+ }, signal);
373
372
  const driveActions = await signActions([addFile({
374
373
  id: documentId,
375
374
  name: document.header.name || documentId,
376
375
  documentType: document.header.documentType,
377
376
  parentFolder
378
- })], this.signer, signal);
377
+ })], this.signer, {
378
+ documentId: driveIdentifier,
379
+ branch: "main"
380
+ }, signal);
379
381
  await this.runJobs([{
380
382
  key: "document",
381
383
  documentId,
@@ -465,18 +467,20 @@ var DriveClient = class {
465
467
  });
466
468
  resolvedNamesByTargetId.set(entry.targetId, resolved);
467
469
  }
470
+ const policy = await this.client.getCreateSignaturePolicy();
468
471
  for (const entry of copyPlan) {
469
472
  const node = drive.state.global.nodes.find((n) => n.id === entry.srcId);
470
473
  if (!node || !isFileNode(node)) continue;
471
474
  const srcDoc = await this.client.get(entry.srcId, void 0, signal);
472
475
  const duplicated = {
473
476
  ...srcDoc,
474
- header: createPresignedHeader(entry.targetId, srcDoc.header.documentType),
477
+ header: createCopyHeader(srcDoc.header, entry.targetId, policy),
475
478
  initialState: srcDoc.state,
476
479
  operations: {}
477
480
  };
478
481
  const resolvedName = resolvedNamesByTargetId.get(entry.targetId);
479
482
  if (resolvedName) duplicated.header.name = resolvedName;
483
+ entry.targetId = duplicated.header.id;
480
484
  await this.addFile(driveIdentifier, duplicated, entry.targetParentFolder ?? void 0, signal);
481
485
  }
482
486
  return this.client.execute(driveIdentifier, "main", copyPlan.map((entry) => copyNode(entry)), signal);
@@ -512,7 +516,7 @@ var DriveClient = class {
512
516
  async runJobs(jobs, signal) {
513
517
  const batchResult = await this.reactor.executeBatch({ jobs }, signal);
514
518
  const completedJobs = await Promise.all(Object.values(batchResult.jobs).map((job) => this.client.waitForJob(job, signal)));
515
- for (const job of completedJobs) if (job.status === JobStatus.FAILED) throw this.toError(job);
519
+ for (const job of completedJobs) if (job.status === "FAILED") throw this.toError(job);
516
520
  return completedJobs;
517
521
  }
518
522
  toError(job) {
@@ -529,8 +533,14 @@ var DriveClient = class {
529
533
  }
530
534
  }
531
535
  async removeFileNode(driveId, fileId, signal) {
532
- const relationshipActions = await signActions([removeRelationshipAction(driveId, fileId, "child")], this.signer, signal);
533
- const driveActions = await signActions([deleteNode({ id: fileId })], this.signer, signal);
536
+ const relationshipActions = await signActions([removeRelationshipAction(driveId, fileId, "child")], this.signer, {
537
+ documentId: driveId,
538
+ branch: "main"
539
+ }, signal);
540
+ const driveActions = await signActions([deleteNode({ id: fileId })], this.signer, {
541
+ documentId: driveId,
542
+ branch: "main"
543
+ }, signal);
534
544
  const batchResult = await this.reactor.executeBatch({ jobs: [{
535
545
  key: "relationship",
536
546
  documentId: driveId,
@@ -547,10 +557,10 @@ var DriveClient = class {
547
557
  dependsOn: ["relationship"]
548
558
  }] }, signal);
549
559
  const completedJobs = await Promise.all(Object.values(batchResult.jobs).map((job) => this.client.waitForJob(job, signal)));
550
- for (const job of completedJobs) if (job.status === JobStatus.FAILED) throw new Error(job.error?.message);
560
+ for (const job of completedJobs) if (job.status === "FAILED") throw new Error(job.error?.message);
551
561
  const deleteJob = await this.reactor.deleteDocument(fileId, this.signer, signal);
552
562
  const deleteCompleted = await this.client.waitForJob(deleteJob, signal);
553
- if (deleteCompleted.status === JobStatus.FAILED) throw new Error(deleteCompleted.error?.message);
563
+ if (deleteCompleted.status === "FAILED") throw new Error(deleteCompleted.error?.message);
554
564
  }
555
565
  };
556
566
  //#endregion
@@ -561,13 +571,15 @@ var DriveClient = class {
561
571
  * Only READ_READY and FAILED are truly terminal.
562
572
  */
563
573
  function isTerminalStatus(status) {
564
- return status === JobStatus.READ_READY || status === JobStatus.FAILED;
574
+ return status === "READ_READY" || status === "FAILED";
565
575
  }
566
576
  /**
567
577
  * Event-driven implementation of IJobAwaiter.
568
578
  * Subscribes to operation events to detect job completion without polling.
569
579
  */
570
580
  var JobAwaiter = class {
581
+ eventBus;
582
+ getJobStatus;
571
583
  pendingJobs = /* @__PURE__ */ new Map();
572
584
  unsubscribers = [];
573
585
  constructor(eventBus, getJobStatus) {
@@ -709,7 +721,7 @@ let DocumentChangeType = /* @__PURE__ */ function(DocumentChangeType) {
709
721
  * would diverge permanently. The document scope carries the metadata the same
710
722
  * argument covers. Grants gate domain-scope reads only.
711
723
  */
712
- const ALWAYS_READABLE_SCOPES = new Set(["auth", "document"]);
724
+ const ALWAYS_READABLE_SCOPES = /* @__PURE__ */ new Set(["auth", "document"]);
713
725
  /** What a gate does with an uninitialized policy when told nothing. */
714
726
  const OPEN_BY_DEFAULT = { withholdUninitialized: false };
715
727
  /**
@@ -783,6 +795,9 @@ var BareReadGate = class {
783
795
  * list names is fetched.
784
796
  */
785
797
  var SeededStateReader = class {
798
+ documentView;
799
+ seed;
800
+ branch;
786
801
  constructor(documentView, seed, branch) {
787
802
  this.documentView = documentView;
788
803
  this.seed = seed;
@@ -812,7 +827,7 @@ var SeededStateReader = class {
812
827
  if (DocumentNotFoundError.isError(error)) return;
813
828
  let exists;
814
829
  try {
815
- exists = await this.documentView.exists([documentId], DocumentExistence.LiveOnly, void 0, signal);
830
+ exists = await this.documentView.exists([documentId], "LiveOnly", void 0, signal);
816
831
  } catch {
817
832
  throw error;
818
833
  }
@@ -833,6 +848,12 @@ var SeededStateReader = class {
833
848
  * from state no commit ever invalidates.
834
849
  */
835
850
  var ModelReadGate = class {
851
+ model;
852
+ documentView;
853
+ servesGroups;
854
+ operationIndex;
855
+ logger;
856
+ options;
836
857
  constructor(model, documentView, servesGroups, operationIndex, logger, options = OPEN_BY_DEFAULT) {
837
858
  this.model = model;
838
859
  this.documentView = documentView;
@@ -972,7 +993,7 @@ function authSubjectFromSigner(signer) {
972
993
  function withAuthScope(view) {
973
994
  if (view?.scopes && view.scopes.length > 0) return {
974
995
  ...view,
975
- scopes: [...new Set([...view.scopes, "auth"])]
996
+ scopes: [.../* @__PURE__ */ new Set([...view.scopes, "auth"])]
976
997
  };
977
998
  return view;
978
999
  }
@@ -1049,8 +1070,9 @@ var ReactorClient = class {
1049
1070
  documentView;
1050
1071
  readGate;
1051
1072
  actionEvaluation;
1073
+ createSignaturePolicy;
1052
1074
  drives;
1053
- constructor(logger, reactor, signer, subscriptionManager, jobAwaiter, documentIndexer, documentView, readGate = new BareReadGate(), actionEvaluation) {
1075
+ constructor(logger, reactor, signer, subscriptionManager, jobAwaiter, documentIndexer, documentView, readGate = new BareReadGate(), actionEvaluation, createSignaturePolicy = DEFAULT_SIGNATURE_POLICY) {
1054
1076
  this.logger = logger;
1055
1077
  this.reactor = reactor;
1056
1078
  this.signer = signer;
@@ -1060,6 +1082,7 @@ var ReactorClient = class {
1060
1082
  this.documentView = documentView;
1061
1083
  this.readGate = readGate;
1062
1084
  this.actionEvaluation = actionEvaluation;
1085
+ this.createSignaturePolicy = createSignaturePolicy;
1063
1086
  this.drives = new DriveClient(this, logger, reactor, signer);
1064
1087
  this.logger.verbose("ReactorClient initialized");
1065
1088
  }
@@ -1150,7 +1173,7 @@ var ReactorClient = class {
1150
1173
  /** True when the id is taken; a soft-deleted document's id is still taken. */
1151
1174
  async isDocumentIdTaken(documentId, signal) {
1152
1175
  this.logger.verbose("isDocumentIdTaken(@documentId)", documentId);
1153
- return (await this.documentView.exists([documentId], DocumentExistence.IncludingDeleted, void 0, signal))[0];
1176
+ return (await this.documentView.exists([documentId], "IncludingDeleted", void 0, signal))[0];
1154
1177
  }
1155
1178
  /**
1156
1179
  * Retrieves operations for a document
@@ -1403,13 +1426,17 @@ var ReactorClient = class {
1403
1426
  scopeStates: document.state ?? {}
1404
1427
  };
1405
1428
  }
1429
+ getCreateSignaturePolicy() {
1430
+ return Promise.resolve(this.createSignaturePolicy);
1431
+ }
1406
1432
  /**
1407
1433
  * Creates a document and waits for completion
1408
1434
  */
1409
1435
  async create(document, parentIdentifier, signal) {
1410
1436
  this.logger.verbose("create(@id, @parentIdentifier)", document.header.id, parentIdentifier);
1411
1437
  const documentId = document.header.id;
1412
- const createActions = await signActions([createDocumentAction({
1438
+ const branch = document.header.branch || "main";
1439
+ const createInput = {
1413
1440
  model: document.header.documentType,
1414
1441
  version: 0,
1415
1442
  documentId,
@@ -1425,23 +1452,30 @@ var ReactorClient = class {
1425
1452
  branch: document.header.branch,
1426
1453
  meta: document.header.meta,
1427
1454
  protocolVersions: document.header.protocolVersions ?? { "base-reducer": 2 }
1428
- }), upgradeDocumentAction({
1455
+ };
1456
+ const createActions = await signActions([createDocumentAction(createInput), upgradeDocumentAction({
1429
1457
  documentId,
1430
1458
  model: document.header.documentType,
1431
1459
  fromVersion: 0,
1432
1460
  toVersion: normalizeDocumentModelVersion(document.state.document?.version),
1433
1461
  initialState: document.state
1434
- })], this.signer, signal);
1462
+ })], this.signer, {
1463
+ documentId,
1464
+ branch
1465
+ }, signal);
1435
1466
  const jobs = [{
1436
1467
  key: "create",
1437
1468
  documentId,
1438
1469
  scope: getSharedActionScope(createActions),
1439
- branch: "main",
1470
+ branch,
1440
1471
  actions: createActions,
1441
1472
  dependsOn: []
1442
1473
  }];
1443
1474
  if (parentIdentifier) {
1444
- const parentActions = await signActions([addRelationshipAction(parentIdentifier, documentId, "child")], this.signer, signal);
1475
+ const parentActions = await signActions([addRelationshipAction(parentIdentifier, documentId, "child")], this.signer, {
1476
+ documentId: parentIdentifier,
1477
+ branch: "main"
1478
+ }, signal);
1445
1479
  jobs.push({
1446
1480
  key: "parent",
1447
1481
  documentId: parentIdentifier,
@@ -1453,7 +1487,7 @@ var ReactorClient = class {
1453
1487
  }
1454
1488
  const batchResult = await this.reactor.executeBatch({ jobs }, signal);
1455
1489
  const completedJobs = await Promise.all(Object.values(batchResult.jobs).map((job) => this.waitForJob(job, signal)));
1456
- for (const job of completedJobs) if (job.status === JobStatus.FAILED) throw new Error(job.error?.message);
1490
+ for (const job of completedJobs) if (job.status === "FAILED") throw new Error(job.error?.message);
1457
1491
  const created = await this.reactor.get(documentId);
1458
1492
  return this.gateDocument(created, void 0, signal);
1459
1493
  }
@@ -1475,7 +1509,7 @@ var ReactorClient = class {
1475
1509
  }, void 0);
1476
1510
  if (!module) throw new Error(`Document model not found for type: ${documentModelType}`);
1477
1511
  }
1478
- const document = module.utils.createDocument();
1512
+ const document = withSignaturePolicy(module.utils.createDocument(), requestedSignaturePolicy(options, this.createSignaturePolicy), { protocolVersions: options?.protocolVersions });
1479
1513
  document.state.document.version = normalizeDocumentModelVersion(module.version);
1480
1514
  return this.create(document, options?.parentIdentifier, signal);
1481
1515
  }
@@ -1501,19 +1535,26 @@ var ReactorClient = class {
1501
1535
  const branch = document.header.branch || "main";
1502
1536
  const fromVersion = normalizeDocumentModelVersion(document.state.document?.version);
1503
1537
  let targetVersion = toVersion;
1504
- if (targetVersion === void 0) targetVersion = normalizeDocumentModelVersion((await this.getDocumentModelModule(documentType)).version);
1538
+ if (targetVersion === void 0) {
1539
+ const module = await this.getDocumentModelModule(documentType);
1540
+ targetVersion = normalizeDocumentModelVersion(module.version);
1541
+ }
1505
1542
  if (targetVersion === fromVersion) return this.gateDocument(document, { branch }, signal);
1506
1543
  if (targetVersion < fromVersion) throw new DowngradeNotSupportedError(documentType, fromVersion, targetVersion);
1507
- const signedActions = await signActions([upgradeDocumentAction({
1544
+ const action = upgradeDocumentAction({
1508
1545
  documentId,
1509
1546
  model: documentType,
1510
1547
  fromVersion,
1511
1548
  toVersion: targetVersion,
1512
1549
  revision: { ...document.header.revision }
1513
- })], this.signer, signal);
1550
+ });
1551
+ const signedActions = await signActions([action], this.signer, {
1552
+ documentId,
1553
+ branch
1554
+ }, signal);
1514
1555
  const jobInfo = await this.reactor.execute(documentId, branch, signedActions, signal);
1515
1556
  const completedJob = await this.waitForJob(jobInfo, signal);
1516
- if (completedJob.status !== JobStatus.FAILED) {
1557
+ if (completedJob.status !== "FAILED") {
1517
1558
  const upgraded = await this.reactor.getByIdOrSlug(documentId, { branch }, completedJob.consistencyToken, signal);
1518
1559
  return this.gateDocument(upgraded, { branch }, signal);
1519
1560
  }
@@ -1540,10 +1581,14 @@ var ReactorClient = class {
1540
1581
  */
1541
1582
  async execute(documentIdentifier, branch, actions, signal, subject) {
1542
1583
  this.logger.verbose("execute(@documentIdentifier, @branch, @count actions)", documentIdentifier, branch, actions.length);
1543
- const signedActions = await signActions(actions, this.signer, signal);
1544
- const jobInfo = await this.reactor.execute(documentIdentifier, branch, signedActions, signal);
1584
+ const documentId = await this.resolveWriteTarget(documentIdentifier, branch, actions, signal);
1585
+ const signedActions = await signActions(actions, this.signer, {
1586
+ documentId,
1587
+ branch
1588
+ }, signal);
1589
+ const jobInfo = await this.reactor.execute(documentId, branch, signedActions, signal);
1545
1590
  const completedJob = await this.waitForJob(jobInfo, signal);
1546
- if (completedJob.status === JobStatus.FAILED) throw new Error(completedJob.error?.message);
1591
+ if (completedJob.status === "FAILED") throw new Error(completedJob.error?.message);
1547
1592
  const view = {
1548
1593
  branch,
1549
1594
  subject
@@ -1556,18 +1601,41 @@ var ReactorClient = class {
1556
1601
  */
1557
1602
  async executeAsync(documentIdentifier, branch, actions, signal) {
1558
1603
  this.logger.verbose("executeAsync(@documentIdentifier, @branch, @count actions)", documentIdentifier, branch, actions.length);
1559
- const signedActions = await signActions(actions, this.signer, signal);
1560
- return this.reactor.execute(documentIdentifier, branch, signedActions, signal);
1604
+ const documentId = await this.resolveWriteTarget(documentIdentifier, branch, actions, signal);
1605
+ const signedActions = await signActions(actions, this.signer, {
1606
+ documentId,
1607
+ branch
1608
+ }, signal);
1609
+ return this.reactor.execute(documentId, branch, signedActions, signal);
1610
+ }
1611
+ /**
1612
+ * The id a write on `identifier` is stored under, which its signatures bind.
1613
+ * A create names its own id, and an id no slug maps to is taken as given, so
1614
+ * a document still in flight resolves to itself.
1615
+ */
1616
+ async resolveWriteTarget(identifier, branch, actions, signal) {
1617
+ if (actions.some((action) => action.type === "CREATE_DOCUMENT")) return identifier;
1618
+ const view = { branch };
1619
+ const bySlug = await this.documentView.resolveSlug(identifier, view, void 0, signal);
1620
+ if (bySlug === void 0 || bySlug === identifier) return identifier;
1621
+ return this.documentView.resolveIdOrSlug(identifier, view, void 0, signal);
1561
1622
  }
1562
1623
  async executeBatch(request, signal) {
1563
1624
  this.logger.verbose("executeBatch(@count jobs)", request.jobs.length);
1564
- const signedJobs = await Promise.all(request.jobs.map(async (job) => ({
1565
- ...job,
1566
- actions: await signActions(job.actions, this.signer, signal)
1567
- })));
1625
+ const signedJobs = await Promise.all(request.jobs.map(async (job) => {
1626
+ const documentId = await this.resolveWriteTarget(job.documentId, job.branch, job.actions, signal);
1627
+ return {
1628
+ ...job,
1629
+ documentId,
1630
+ actions: await signActions(job.actions, this.signer, {
1631
+ documentId,
1632
+ branch: job.branch
1633
+ }, signal)
1634
+ };
1635
+ }));
1568
1636
  const batchResult = await this.reactor.executeBatch({ jobs: signedJobs }, signal);
1569
1637
  const completedJobs = await Promise.all(Object.values(batchResult.jobs).map((job) => this.waitForJob(job, signal)));
1570
- for (const job of completedJobs) if (job.status === JobStatus.FAILED) throw new Error(job.error?.message);
1638
+ for (const job of completedJobs) if (job.status === "FAILED") throw new Error(job.error?.message);
1571
1639
  return batchResult;
1572
1640
  }
1573
1641
  /**
@@ -1592,7 +1660,7 @@ var ReactorClient = class {
1592
1660
  this.logger.verbose("addRelationship(@sourceIdentifier, @targetIdentifier, @relationshipType, @metadata, @branch)", sourceIdentifier, targetIdentifier, relationshipType, metadata, branch);
1593
1661
  const jobInfo = await this.reactor.addRelationship(sourceIdentifier, targetIdentifier, relationshipType, metadata, branch, this.signer, signal);
1594
1662
  const completedJob = await this.waitForJob(jobInfo, signal);
1595
- if (completedJob.status === JobStatus.FAILED) throw new Error(completedJob.error?.message);
1663
+ if (completedJob.status === "FAILED") throw new Error(completedJob.error?.message);
1596
1664
  const result = await this.reactor.getByIdOrSlug(sourceIdentifier, { branch }, completedJob.consistencyToken, signal);
1597
1665
  return this.gateDocument(result, { branch }, signal);
1598
1666
  }
@@ -1604,7 +1672,7 @@ var ReactorClient = class {
1604
1672
  if (!await this.readRelationshipEdge(sourceIdentifier, targetIdentifier, relationshipType, { branch }, signal)) throw new RelationshipNotFoundError(sourceIdentifier, targetIdentifier, relationshipType);
1605
1673
  const jobInfo = await this.reactor.updateRelationship(sourceIdentifier, targetIdentifier, relationshipType, metadata, branch, this.signer, signal);
1606
1674
  const completedJob = await this.waitForJob(jobInfo, signal);
1607
- if (completedJob.status === JobStatus.FAILED) throw new Error(completedJob.error?.message);
1675
+ if (completedJob.status === "FAILED") throw new Error(completedJob.error?.message);
1608
1676
  const result = await this.reactor.getByIdOrSlug(sourceIdentifier, { branch }, completedJob.consistencyToken, signal);
1609
1677
  return this.gateDocument(result, { branch }, signal);
1610
1678
  }
@@ -1615,7 +1683,7 @@ var ReactorClient = class {
1615
1683
  this.logger.verbose("removeRelationship(@sourceIdentifier, @targetIdentifier, @relationshipType, @branch)", sourceIdentifier, targetIdentifier, relationshipType, branch);
1616
1684
  const jobInfo = await this.reactor.removeRelationship(sourceIdentifier, targetIdentifier, relationshipType, branch, this.signer, signal);
1617
1685
  const completedJob = await this.waitForJob(jobInfo, signal);
1618
- if (completedJob.status === JobStatus.FAILED) throw new Error(completedJob.error?.message);
1686
+ if (completedJob.status === "FAILED") throw new Error(completedJob.error?.message);
1619
1687
  const result = await this.reactor.getByIdOrSlug(sourceIdentifier, { branch }, completedJob.consistencyToken, signal);
1620
1688
  return this.gateDocument(result, { branch }, signal);
1621
1689
  }
@@ -1627,10 +1695,10 @@ var ReactorClient = class {
1627
1695
  const metadata = (await this.readRelationshipEdge(sourceParentIdentifier, targetIdentifier, relationshipType, { branch }, signal))?.metadata;
1628
1696
  const removeJobInfo = await this.reactor.removeRelationship(sourceParentIdentifier, targetIdentifier, relationshipType, branch, this.signer, signal);
1629
1697
  const removeCompletedJob = await this.waitForJob(removeJobInfo, signal);
1630
- if (removeCompletedJob.status === JobStatus.FAILED) throw new Error(removeCompletedJob.error?.message);
1698
+ if (removeCompletedJob.status === "FAILED") throw new Error(removeCompletedJob.error?.message);
1631
1699
  const addJobInfo = await this.reactor.addRelationship(targetParentIdentifier, targetIdentifier, relationshipType, metadata, branch, this.signer, signal);
1632
1700
  const addCompletedJob = await this.waitForJob(addJobInfo, signal);
1633
- if (addCompletedJob.status === JobStatus.FAILED) throw new Error(addCompletedJob.error?.message);
1701
+ if (addCompletedJob.status === "FAILED") throw new Error(addCompletedJob.error?.message);
1634
1702
  const sourceResult = await this.reactor.getByIdOrSlug(sourceParentIdentifier, { branch }, removeCompletedJob.consistencyToken, signal);
1635
1703
  const targetResult = await this.reactor.getByIdOrSlug(targetParentIdentifier, { branch }, addCompletedJob.consistencyToken, signal);
1636
1704
  return {
@@ -1644,7 +1712,7 @@ var ReactorClient = class {
1644
1712
  const completedJobs = await Promise.all(Object.entries(result.jobs).map(async ([key, jobInfo]) => {
1645
1713
  return [key, await this.waitForJob(jobInfo, signal)];
1646
1714
  }));
1647
- for (const [, completedJob] of completedJobs) if (completedJob.status === JobStatus.FAILED) throw new Error(completedJob.error?.message);
1715
+ for (const [, completedJob] of completedJobs) if (completedJob.status === "FAILED") throw new Error(completedJob.error?.message);
1648
1716
  return { jobs: Object.fromEntries(completedJobs) };
1649
1717
  }
1650
1718
  /**
@@ -1653,8 +1721,8 @@ var ReactorClient = class {
1653
1721
  async deleteDocument(identifier, propagate, signal) {
1654
1722
  this.logger.verbose("deleteDocument(@identifier, @propagate)", identifier, propagate);
1655
1723
  const jobs = [];
1656
- if (propagate === PropagationMode.Cascade) {
1657
- const toDelete = new Set([identifier]);
1724
+ if (propagate === "cascade") {
1725
+ const toDelete = /* @__PURE__ */ new Set([identifier]);
1658
1726
  let changed = true;
1659
1727
  while (changed) {
1660
1728
  if (signal?.aborted) throw new Error("Operation aborted");
@@ -1678,7 +1746,7 @@ var ReactorClient = class {
1678
1746
  const jobInfo = await this.reactor.deleteDocument(identifier, this.signer, signal);
1679
1747
  jobs.push(jobInfo);
1680
1748
  const completedJobs = await Promise.all(jobs.map((job) => this.waitForJob(job, signal)));
1681
- for (const completedJob of completedJobs) if (completedJob.status === JobStatus.FAILED) throw new Error(completedJob.error?.message);
1749
+ for (const completedJob of completedJobs) if (completedJob.status === "FAILED") throw new Error(completedJob.error?.message);
1682
1750
  }
1683
1751
  /**
1684
1752
  * Deletes documents and waits for completion
@@ -1738,27 +1806,27 @@ var ReactorClient = class {
1738
1806
  deliver((async () => {
1739
1807
  const documents = await Promise.all(result.results.map((id) => this.reactor.get(id, withAuthScope(view), void 0, void 0)));
1740
1808
  return {
1741
- type: DocumentChangeType.Created,
1809
+ type: "created",
1742
1810
  documents: await Promise.all(documents.map(readable))
1743
1811
  };
1744
1812
  })());
1745
1813
  }, search);
1746
1814
  const unsubscribeDeleted = this.subscriptionManager.onDocumentDeleted((documentIds) => {
1747
1815
  deliver({
1748
- type: DocumentChangeType.Deleted,
1816
+ type: "deleted",
1749
1817
  documents: [],
1750
1818
  context: { childId: documentIds[0] }
1751
1819
  });
1752
1820
  }, search);
1753
1821
  const unsubscribeUpdated = this.subscriptionManager.onDocumentStateUpdated((result) => {
1754
1822
  deliver((async () => ({
1755
- type: DocumentChangeType.Updated,
1823
+ type: "updated",
1756
1824
  documents: await Promise.all(result.results.map(readable))
1757
1825
  }))());
1758
1826
  }, search, view);
1759
1827
  const unsubscribeRelationship = this.subscriptionManager.onRelationshipChanged((parentId, childId, changeType) => {
1760
1828
  deliver({
1761
- type: changeType === RelationshipChangeType.Added ? DocumentChangeType.ChildAdded : DocumentChangeType.ChildRemoved,
1829
+ type: changeType === "added" ? "child_added" : "child_removed",
1762
1830
  documents: [],
1763
1831
  context: {
1764
1832
  parentId,
@@ -1787,18 +1855,6 @@ var ReactorClient = class {
1787
1855
  //#endregion
1788
1856
  //#region src/queue/types.ts
1789
1857
  /**
1790
- * State of a job in the queue
1791
- */
1792
- let JobQueueState = /* @__PURE__ */ function(JobQueueState) {
1793
- JobQueueState[JobQueueState["UNKNOWN"] = -1] = "UNKNOWN";
1794
- JobQueueState[JobQueueState["PREPROCESSING"] = 0] = "PREPROCESSING";
1795
- JobQueueState[JobQueueState["PENDING"] = 1] = "PENDING";
1796
- JobQueueState[JobQueueState["READY"] = 2] = "READY";
1797
- JobQueueState[JobQueueState["RUNNING"] = 3] = "RUNNING";
1798
- JobQueueState[JobQueueState["RESOLVED"] = 4] = "RESOLVED";
1799
- return JobQueueState;
1800
- }({});
1801
- /**
1802
1858
  * How a retry is accounted against the job's retry limit.
1803
1859
  * - `CountAgainstLimit` (default): a fault; the job eventually exhausts its
1804
1860
  * retries and fails terminally.
@@ -1831,6 +1887,11 @@ function toErrorInfo(error) {
1831
1887
  };
1832
1888
  }
1833
1889
  var JobResultHandler = class {
1890
+ queue;
1891
+ jobTracker;
1892
+ eventBus;
1893
+ resolver;
1894
+ logger;
1834
1895
  constructor(queue, jobTracker, eventBus, resolver, logger) {
1835
1896
  this.queue = queue;
1836
1897
  this.jobTracker = jobTracker;
@@ -1861,7 +1922,7 @@ var JobResultHandler = class {
1861
1922
  if (result.error && AppendConditionFailedError.isError(result.error) && this.countConflicts(handle.job) < MAX_EXEMPT_CONFLICT_RETRIES) {
1862
1923
  const errorInfo = toErrorInfo(result.error);
1863
1924
  try {
1864
- await this.queue.retryJob(handle.job.id, errorInfo, RetryAccounting.ExemptFromLimit);
1925
+ await this.queue.retryJob(handle.job.id, errorInfo, "exempt-from-limit");
1865
1926
  return;
1866
1927
  } catch {}
1867
1928
  }
@@ -1870,7 +1931,7 @@ var JobResultHandler = class {
1870
1931
  callbacks.deferJob(result.error.documentId, handle.job);
1871
1932
  return;
1872
1933
  }
1873
- if (result.error && (DocumentDeletedError.isError(result.error) || DocumentAlreadyExistsError.isError(result.error) || DocumentNotFoundError.isError(result.error) || AuthorizationDeniedError.isError(result.error) || AuthTimestampNotMonotonicError.isError(result.error) || InvalidOperationTimestampError.isError(result.error) || ExcessiveReshuffleError.isError(result.error) || UpgradePreconditionFailedError.isError(result.error))) {
1934
+ if (result.error && (DocumentDeletedError.isError(result.error) || DocumentAlreadyExistsError.isError(result.error) || DocumentNotFoundError.isError(result.error) || AuthorizationDeniedError.isError(result.error) || AuthTimestampNotMonotonicError.isError(result.error) || InvalidOperationTimestampError.isError(result.error) || ExcessiveReshuffleError.isError(result.error) || InvalidSignatureError.isError(result.error) || UpgradePreconditionFailedError.isError(result.error))) {
1874
1935
  const errorInfo = toErrorInfo(result.error);
1875
1936
  this.jobTracker.markFailed(handle.job.id, errorInfo, handle.job);
1876
1937
  this.eventBus.emit(ReactorEventTypes.JOB_FAILED, {
@@ -1959,6 +2020,12 @@ var JobResultHandler = class {
1959
2020
  * is stranded at PENDING for as long as the deferral lasts.
1960
2021
  */
1961
2022
  var DeferredJobs = class {
2023
+ queue;
2024
+ jobTracker;
2025
+ eventBus;
2026
+ logger;
2027
+ ttlMs;
2028
+ onReleased;
1962
2029
  #byDocumentId = /* @__PURE__ */ new Map();
1963
2030
  #timers = /* @__PURE__ */ new Map();
1964
2031
  constructor(queue, jobTracker, eventBus, logger, ttlMs = DEFAULT_DEFERRED_JOB_TTL_MS, onReleased = () => Promise.resolve()) {
@@ -2120,6 +2187,13 @@ function bucketFor(documentId, numWorkers) {
2120
2187
  * (Powerhouse board wiki id: d400d711-f07e-4389-a226-4e9fdd4fa8ba)
2121
2188
  */
2122
2189
  var WorkerPoolJobExecutorManager = class {
2190
+ workerFactory;
2191
+ eventBus;
2192
+ queue;
2193
+ jobTracker;
2194
+ logger;
2195
+ resolver;
2196
+ operationIndex;
2123
2197
  workers = [];
2124
2198
  isRunning = false;
2125
2199
  activeJobs = 0;
@@ -2258,6 +2332,7 @@ var WorkerPoolJobExecutorManager = class {
2258
2332
  await this.tryDispatchFor(worker);
2259
2333
  return;
2260
2334
  }
2335
+ for (const refusal of outcome.signatureRefusals ?? []) this.eventBus.emit(ReactorEventTypes.SIGNATURE_REFUSED, refusal).catch(() => {});
2261
2336
  if (outcome.result.success) {
2262
2337
  this.totalJobsProcessed++;
2263
2338
  const completedEvent = {
@@ -2383,6 +2458,12 @@ function fillMissingMemberships(documentIds, found) {
2383
2458
  * Listens for job available events and dispatches jobs to executors.
2384
2459
  */
2385
2460
  var SimpleJobExecutorManager = class {
2461
+ executorFactory;
2462
+ eventBus;
2463
+ queue;
2464
+ jobTracker;
2465
+ logger;
2466
+ resolver;
2386
2467
  executors = [];
2387
2468
  isRunning = false;
2388
2469
  activeJobs = 0;
@@ -2557,6 +2638,7 @@ var SimpleJobExecutorManager = class {
2557
2638
  * Subscribes to operation events to update job states.
2558
2639
  */
2559
2640
  var InMemoryJobTracker = class {
2641
+ eventBus;
2560
2642
  jobs = /* @__PURE__ */ new Map();
2561
2643
  unsubscribers = [];
2562
2644
  constructor(eventBus) {
@@ -2577,11 +2659,11 @@ var InMemoryJobTracker = class {
2577
2659
  handleWriteReady(event) {
2578
2660
  const jobId = event.jobId;
2579
2661
  const job = this.jobs.get(jobId);
2580
- if (job && job.status === JobStatus.RUNNING) {
2662
+ if (job && job.status === "RUNNING") {
2581
2663
  const consistencyToken = createConsistencyToken(event.operations);
2582
2664
  this.jobs.set(jobId, {
2583
2665
  ...job,
2584
- status: JobStatus.WRITE_READY,
2666
+ status: "WRITE_READY",
2585
2667
  consistencyToken,
2586
2668
  result: summarizeSubmittedActions(event.operations, event.submittedActionIds)
2587
2669
  });
@@ -2590,9 +2672,9 @@ var InMemoryJobTracker = class {
2590
2672
  handleReadReady(event) {
2591
2673
  const jobId = event.jobId;
2592
2674
  const job = this.jobs.get(jobId);
2593
- if (job && job.status === JobStatus.WRITE_READY) this.jobs.set(jobId, {
2675
+ if (job && job.status === "WRITE_READY") this.jobs.set(jobId, {
2594
2676
  ...job,
2595
- status: JobStatus.READ_READY
2677
+ status: "READ_READY"
2596
2678
  });
2597
2679
  }
2598
2680
  handleJobFailed(event) {
@@ -2615,7 +2697,7 @@ var InMemoryJobTracker = class {
2615
2697
  this.jobs.set(jobId, {
2616
2698
  id: jobId,
2617
2699
  documentId: "",
2618
- status: JobStatus.RUNNING,
2700
+ status: "RUNNING",
2619
2701
  createdAtUtcIso: (/* @__PURE__ */ new Date()).toISOString(),
2620
2702
  consistencyToken: createEmptyConsistencyToken(),
2621
2703
  meta: {
@@ -2627,7 +2709,7 @@ var InMemoryJobTracker = class {
2627
2709
  }
2628
2710
  this.jobs.set(jobId, {
2629
2711
  ...job,
2630
- status: JobStatus.RUNNING
2712
+ status: "RUNNING"
2631
2713
  });
2632
2714
  }
2633
2715
  markFailed(jobId, error, job) {
@@ -2636,7 +2718,7 @@ var InMemoryJobTracker = class {
2636
2718
  this.jobs.set(jobId, {
2637
2719
  id: jobId,
2638
2720
  documentId: job?.documentId ?? "",
2639
- status: JobStatus.FAILED,
2721
+ status: "FAILED",
2640
2722
  createdAtUtcIso: (/* @__PURE__ */ new Date()).toISOString(),
2641
2723
  completedAtUtcIso: (/* @__PURE__ */ new Date()).toISOString(),
2642
2724
  error,
@@ -2651,7 +2733,7 @@ var InMemoryJobTracker = class {
2651
2733
  }
2652
2734
  this.jobs.set(jobId, {
2653
2735
  ...existing,
2654
- status: JobStatus.FAILED,
2736
+ status: "FAILED",
2655
2737
  completedAtUtcIso: (/* @__PURE__ */ new Date()).toISOString(),
2656
2738
  error,
2657
2739
  job,
@@ -2745,6 +2827,7 @@ function highestOf(ops) {
2745
2827
  return highest;
2746
2828
  }
2747
2829
  var ProcessorQueue = class {
2830
+ options;
2748
2831
  tasks = [];
2749
2832
  running = false;
2750
2833
  closed = false;
@@ -3327,11 +3410,11 @@ var JobExecutionHandle = class {
3327
3410
  onDefer;
3328
3411
  getStateName(state) {
3329
3412
  switch (state) {
3330
- case JobQueueState.PREPROCESSING: return "PREPROCESSING";
3331
- case JobQueueState.PENDING: return "PENDING";
3332
- case JobQueueState.READY: return "READY";
3333
- case JobQueueState.RUNNING: return "RUNNING";
3334
- case JobQueueState.RESOLVED: return "RESOLVED";
3413
+ case 0: return "PREPROCESSING";
3414
+ case 1: return "PENDING";
3415
+ case 2: return "READY";
3416
+ case 3: return "RUNNING";
3417
+ case 4: return "RESOLVED";
3335
3418
  default: return `UNKNOWN`;
3336
3419
  }
3337
3420
  }
@@ -3350,23 +3433,23 @@ var JobExecutionHandle = class {
3350
3433
  return this._state;
3351
3434
  }
3352
3435
  start() {
3353
- if (this._state !== JobQueueState.READY) throw new Error(`Cannot start job in state ${this.getStateName(this._state)}`);
3354
- this._state = JobQueueState.RUNNING;
3436
+ if (this._state !== 2) throw new Error(`Cannot start job in state ${this.getStateName(this._state)}`);
3437
+ this._state = 3;
3355
3438
  this.onStart?.();
3356
3439
  }
3357
3440
  complete() {
3358
- if (this._state !== JobQueueState.RUNNING) throw new Error(`Cannot complete job in state ${this.getStateName(this._state)}`);
3359
- this._state = JobQueueState.RESOLVED;
3441
+ if (this._state !== 3) throw new Error(`Cannot complete job in state ${this.getStateName(this._state)}`);
3442
+ this._state = 4;
3360
3443
  this.onComplete?.();
3361
3444
  }
3362
3445
  fail(error) {
3363
- if (this._state !== JobQueueState.RUNNING) throw new Error(`Cannot fail job in state ${this.getStateName(this._state)}`);
3364
- this._state = JobQueueState.RESOLVED;
3446
+ if (this._state !== 3) throw new Error(`Cannot fail job in state ${this.getStateName(this._state)}`);
3447
+ this._state = 4;
3365
3448
  this.onFail?.(error);
3366
3449
  }
3367
3450
  defer() {
3368
- if (this._state !== JobQueueState.RUNNING) throw new Error(`Cannot defer job in state ${this.getStateName(this._state)}`);
3369
- this._state = JobQueueState.RESOLVED;
3451
+ if (this._state !== 3) throw new Error(`Cannot defer job in state ${this.getStateName(this._state)}`);
3452
+ this._state = 4;
3370
3453
  this.onDefer?.();
3371
3454
  }
3372
3455
  };
@@ -3379,6 +3462,8 @@ var JobExecutionHandle = class {
3379
3462
  * Implements dependency management through queue hints.
3380
3463
  */
3381
3464
  var InMemoryQueue = class {
3465
+ eventBus;
3466
+ resolver;
3382
3467
  queues = /* @__PURE__ */ new Map();
3383
3468
  jobIdToQueueKey = /* @__PURE__ */ new Map();
3384
3469
  docIdToJobId = /* @__PURE__ */ new Map();
@@ -3513,7 +3598,7 @@ var InMemoryQueue = class {
3513
3598
  this.jobIdToQueueKey.delete(job.id);
3514
3599
  this.markJobExecuting(job);
3515
3600
  if (queue.length === 0) this.queues.delete(queueKey);
3516
- const handle = new JobExecutionHandle(job, JobQueueState.READY, {
3601
+ const handle = new JobExecutionHandle(job, 2, {
3517
3602
  onStart: () => {},
3518
3603
  onComplete: () => {
3519
3604
  this.completeJob(job.id);
@@ -3539,7 +3624,7 @@ var InMemoryQueue = class {
3539
3624
  this.jobIdToQueueKey.delete(job.id);
3540
3625
  this.markJobExecuting(job);
3541
3626
  if (queue.length === 0) this.queues.delete(queueKey);
3542
- const handle = new JobExecutionHandle(job, JobQueueState.READY, {
3627
+ const handle = new JobExecutionHandle(job, 2, {
3543
3628
  onStart: () => {},
3544
3629
  onComplete: () => {
3545
3630
  this.completeJob(job.id);
@@ -3573,7 +3658,7 @@ var InMemoryQueue = class {
3573
3658
  this.jobIdToQueueKey.delete(job.id);
3574
3659
  this.markJobExecuting(job);
3575
3660
  if (queue.length === 0) this.queues.delete(queueKey);
3576
- const handle = new JobExecutionHandle(job, JobQueueState.READY, {
3661
+ const handle = new JobExecutionHandle(job, 2, {
3577
3662
  onStart: () => {},
3578
3663
  onComplete: () => {
3579
3664
  this.completeJob(job.id);
@@ -3675,7 +3760,7 @@ var InMemoryQueue = class {
3675
3760
  if (documentId) this.markJobComplete(jobId, documentId);
3676
3761
  this.jobIndex.delete(jobId);
3677
3762
  }
3678
- async retryJob(jobId, error, accounting = RetryAccounting.CountAgainstLimit) {
3763
+ async retryJob(jobId, error, accounting = "count-against-limit") {
3679
3764
  const job = this.jobIndex.get(jobId);
3680
3765
  if (!job) return;
3681
3766
  job.lastError = error;
@@ -3687,7 +3772,7 @@ var InMemoryQueue = class {
3687
3772
  const retryCount = job.retryCount || 0;
3688
3773
  const updatedJob = {
3689
3774
  ...job,
3690
- retryCount: accounting === RetryAccounting.CountAgainstLimit ? retryCount + 1 : retryCount,
3775
+ retryCount: accounting === "count-against-limit" ? retryCount + 1 : retryCount,
3691
3776
  lastError: error
3692
3777
  };
3693
3778
  await this.enqueue(updatedJob);
@@ -3888,6 +3973,8 @@ function describeSource(source) {
3888
3973
  * and failure-caching state.
3889
3974
  */
3890
3975
  var DocumentModelResolver = class {
3976
+ registry;
3977
+ loader;
3891
3978
  loadingModels = /* @__PURE__ */ new Map();
3892
3979
  failedModelTypes = /* @__PURE__ */ new Set();
3893
3980
  broadcastHook = null;
@@ -3958,6 +4045,7 @@ var DocumentModelResolver = class {
3958
4045
  * Since there is no loader, missing models cannot be recovered.
3959
4046
  */
3960
4047
  var NullDocumentModelResolver = class {
4048
+ registry;
3961
4049
  constructor(registry) {
3962
4050
  this.registry = registry;
3963
4051
  }
@@ -4181,6 +4269,8 @@ var ReactorSubscriptionManager = class {
4181
4269
  * is emitted, so that reactor.get() returns fresh data when callbacks fire.
4182
4270
  */
4183
4271
  var SubscriptionNotificationReadModel = class {
4272
+ subscriptionManager;
4273
+ documentView;
4184
4274
  name = "subscription-notification";
4185
4275
  constructor(subscriptionManager, documentView) {
4186
4276
  this.subscriptionManager = subscriptionManager;
@@ -4203,10 +4293,10 @@ var SubscriptionNotificationReadModel = class {
4203
4293
  deleted.push(deletedId);
4204
4294
  } else if (actionType === "ADD_RELATIONSHIP") {
4205
4295
  const input = operation.action.input;
4206
- this.subscriptionManager.notifyRelationshipChanged(input.sourceId, input.targetId, RelationshipChangeType.Added, input.childType);
4296
+ this.subscriptionManager.notifyRelationshipChanged(input.sourceId, input.targetId, "added", input.childType);
4207
4297
  } else if (actionType === "REMOVE_RELATIONSHIP") {
4208
4298
  const input = operation.action.input;
4209
- this.subscriptionManager.notifyRelationshipChanged(input.sourceId, input.targetId, RelationshipChangeType.Removed, input.childType);
4299
+ this.subscriptionManager.notifyRelationshipChanged(input.sourceId, input.targetId, "removed", input.childType);
4210
4300
  } else if (!created.includes(context.documentId)) updatedIds.add(context.documentId);
4211
4301
  }
4212
4302
  if (created.length > 0) this.subscriptionManager.notifyDocumentsCreated(created, documentTypes, parentIds);
@@ -4231,6 +4321,10 @@ var SubscriptionNotificationReadModel = class {
4231
4321
  * keeps the common case (a membership write later than all history) free.
4232
4322
  */
4233
4323
  var GroupReevaluationTrigger = class {
4324
+ logger;
4325
+ eventBus;
4326
+ queue;
4327
+ operationIndex;
4234
4328
  unsubscribe;
4235
4329
  constructor(logger, eventBus, queue, operationIndex) {
4236
4330
  this.logger = logger;
@@ -4382,7 +4476,7 @@ var Mailbox = class {
4382
4476
  this.itemsMap.set(item.id, item);
4383
4477
  for (const op of item.operations) this._latestOrdinal = Math.max(this._latestOrdinal, op.context.ordinal);
4384
4478
  item.on((syncOp, _, next) => {
4385
- if (next === SyncOperationStatus.Applied) for (const op of syncOp.operations) this._ack = Math.max(this._ack, op.context.ordinal);
4479
+ if (next === 2) for (const op of syncOp.operations) this._ack = Math.max(this._ack, op.context.ordinal);
4386
4480
  });
4387
4481
  }
4388
4482
  if (this.paused) {
@@ -4496,7 +4590,7 @@ var BufferedMailbox = class {
4496
4590
  this.itemsMap.set(item.id, item);
4497
4591
  for (const op of item.operations) this._latestOrdinal = Math.max(this._latestOrdinal, op.context.ordinal);
4498
4592
  item.on((syncOp, _, next) => {
4499
- if (next === SyncOperationStatus.Applied) for (const op of syncOp.operations) this._ack = Math.max(this._ack, op.context.ordinal);
4593
+ if (next === 2) for (const op of syncOp.operations) this._ack = Math.max(this._ack, op.context.ordinal);
4500
4594
  });
4501
4595
  }
4502
4596
  this.addedBuffer.push(...items);
@@ -4610,7 +4704,14 @@ var GraphQLRequestError = class extends Error {
4610
4704
  * difference between a channel that recovers and one that is dead for the
4611
4705
  * process lifetime.
4612
4706
  */
4613
- const RECOVERABLE_GRAPHQL_ERROR_CODES = { malformedStoredOperation: "MALFORMED_STORED_OPERATION" };
4707
+ const RECOVERABLE_GRAPHQL_ERROR_CODES = {
4708
+ /**
4709
+ * A stored operation cannot be represented in the schema - an action with no
4710
+ * id, say. The document holding it needs repairing, but the channel serves
4711
+ * every other document, and a peer that stopped polling would stop receiving
4712
+ * those too.
4713
+ */
4714
+ malformedStoredOperation: "MALFORMED_STORED_OPERATION" };
4614
4715
  const RECOVERABLE_CODES = new Set(Object.values(RECOVERABLE_GRAPHQL_ERROR_CODES));
4615
4716
  /**
4616
4717
  * True when every error the response carried named a recoverable code.
@@ -4719,23 +4820,23 @@ var SyncOperation = class {
4719
4820
  this.scopes = scopes;
4720
4821
  this.branch = branch;
4721
4822
  this.operations = operations;
4722
- this.status = SyncOperationStatus.Unknown;
4823
+ this.status = -1;
4723
4824
  }
4724
4825
  on(callback) {
4725
4826
  this.callbacks.push(callback);
4726
4827
  }
4727
4828
  started() {
4728
- this.transition(SyncOperationStatus.TransportPending);
4829
+ this.transition(0);
4729
4830
  }
4730
4831
  transported() {
4731
- this.transition(SyncOperationStatus.ExecutionPending);
4832
+ this.transition(1);
4732
4833
  }
4733
4834
  executed() {
4734
- this.transition(SyncOperationStatus.Applied);
4835
+ this.transition(2);
4735
4836
  }
4736
4837
  failed(error) {
4737
4838
  this.error = error;
4738
- this.transition(SyncOperationStatus.Error);
4839
+ this.transition(3);
4739
4840
  }
4740
4841
  transition(next) {
4741
4842
  const prev = this.status;
@@ -4945,9 +5046,11 @@ function consolidateSyncOperations(syncOps) {
4945
5046
  }
4946
5047
  const first = group.ops[0];
4947
5048
  const merged = new SyncOperation(first.id, first.jobId, remappedDeps, first.remoteName, first.documentId, first.scopes, first.branch, allOperations);
4948
- if (first.status > SyncOperationStatus.TransportPending) if (first.status >= SyncOperationStatus.Error) merged.executed();
4949
- else if (first.status >= SyncOperationStatus.Applied) merged.transported();
4950
- else merged.started();
5049
+ if (first.status > 0) {
5050
+ if (first.status >= 3) merged.executed();
5051
+ else if (first.status >= 2) merged.transported();
5052
+ else merged.started();
5053
+ }
4951
5054
  result.push(merged);
4952
5055
  }
4953
5056
  return result;
@@ -5078,7 +5181,7 @@ function syncOperationErrorType(error) {
5078
5181
  return error?.errorType ?? classifyJobFailure(error?.error?.name ?? "Error");
5079
5182
  }
5080
5183
  /** Dead-letter types that must not stop the document syncing. */
5081
- const NON_QUARANTINING_ERROR_TYPES = new Set(["AUTH_TIMESTAMP_NOT_MONOTONIC"]);
5184
+ const NON_QUARANTINING_ERROR_TYPES = /* @__PURE__ */ new Set(["AUTH_TIMESTAMP_NOT_MONOTONIC"]);
5082
5185
  /**
5083
5186
  * A held auth operation must not quarantine: reconciling the two policies needs
5084
5187
  * the traffic a quarantine would stop.
@@ -5304,7 +5407,7 @@ function envelopesToSyncOperations(envelope, remoteName) {
5304
5407
  }
5305
5408
  const getLatestAppliedOrdinal = (syncOps) => {
5306
5409
  let maxOrdinal = 0;
5307
- for (const syncOp of syncOps) if (syncOp.status === SyncOperationStatus.Applied) for (const op of syncOp.operations) maxOrdinal = Math.max(maxOrdinal, op.context.ordinal);
5410
+ for (const syncOp of syncOps) if (syncOp.status === 2) for (const op of syncOp.operations) maxOrdinal = Math.max(maxOrdinal, op.context.ordinal);
5308
5411
  return maxOrdinal;
5309
5412
  };
5310
5413
  //#endregion
@@ -5319,6 +5422,7 @@ const DECISION_FIELDS = ["deniedReason", "errorType"];
5319
5422
  * GraphQL-based synchronization channel for network communication between reactors.
5320
5423
  */
5321
5424
  var GqlRequestChannel = class {
5425
+ logger;
5322
5426
  inbox;
5323
5427
  outbox;
5324
5428
  deadLetter;
@@ -5526,7 +5630,7 @@ var GqlRequestChannel = class {
5526
5630
  const syncOps = [];
5527
5631
  for (const dl of deadLetters) {
5528
5632
  const syncOp = new SyncOperation(crypto.randomUUID(), dl.jobId, [], this.remoteName, dl.documentId, dl.scopes, dl.branch, []);
5529
- syncOp.failed(new ChannelError(ChannelErrorSource.Outbox, new Error(dl.error), dl.errorType ?? void 0));
5633
+ syncOp.failed(new ChannelError("outbox", new Error(dl.error), dl.errorType ?? void 0));
5530
5634
  syncOps.push(syncOp);
5531
5635
  }
5532
5636
  this.deadLetter.add(...syncOps);
@@ -5546,7 +5650,7 @@ var GqlRequestChannel = class {
5546
5650
  const classification = this.classifyError(err);
5547
5651
  this.failureCount++;
5548
5652
  this.lastFailureUtcMs = Date.now();
5549
- const channelError = new ChannelError(ChannelErrorSource.Inbox, err);
5653
+ const channelError = new ChannelError("inbox", err);
5550
5654
  this.logger.error("GqlChannel poll error (@FailureCount, @Classification): @Error", this.failureCount, classification, channelError);
5551
5655
  if (classification === "unrecoverable") {
5552
5656
  this.pollTimer.stop();
@@ -5790,7 +5894,7 @@ var GqlRequestChannel = class {
5790
5894
  this.transitionConnectionState("reconnecting");
5791
5895
  this.schedulePushRetry();
5792
5896
  } else {
5793
- const channelError = new ChannelError(ChannelErrorSource.Outbox, err);
5897
+ const channelError = new ChannelError("outbox", err);
5794
5898
  for (const syncOp of syncOps) syncOp.failed(channelError);
5795
5899
  this.deadLetter.add(...syncOps);
5796
5900
  this.outbox.remove(...syncOps);
@@ -6012,7 +6116,7 @@ var GqlRequestChannelFactory = class {
6012
6116
  ...retryMaxDelayMs !== void 0 && { retryMaxDelayMs },
6013
6117
  ...maxQueueDepth !== void 0 && { maxQueueDepth },
6014
6118
  ...backpressureCheckIntervalMs !== void 0 && { backpressureCheckIntervalMs },
6015
- startPaused: options?.pollBehavior === PollBehavior.Manual
6119
+ startPaused: options?.pollBehavior === "manual"
6016
6120
  });
6017
6121
  return new GqlRequestChannel(this.logger, remoteId, remoteName, cursorStorage, gqlConfig, operationIndex, pollTimer);
6018
6122
  }
@@ -6029,6 +6133,7 @@ function firstOrdinalOf$1(syncOp) {
6029
6133
  * In general, the resolvers are responsible for updating mailboxes.
6030
6134
  */
6031
6135
  var GqlResponseChannel = class {
6136
+ logger;
6032
6137
  inbox;
6033
6138
  outbox;
6034
6139
  deadLetter;
@@ -6139,7 +6244,7 @@ var GqlResponseChannel = class {
6139
6244
  */
6140
6245
  rememberUnserved(syncOps) {
6141
6246
  for (const syncOp of syncOps) {
6142
- if (syncOp.status === SyncOperationStatus.Applied) continue;
6247
+ if (syncOp.status === 2) continue;
6143
6248
  const first = firstOrdinalOf$1(syncOp);
6144
6249
  if (first > 0 && first < this.evictedOutboxFloor) this.evictedOutboxFloor = first;
6145
6250
  }
@@ -6184,7 +6289,7 @@ var GqlResponseChannel = class {
6184
6289
  unservedFloor() {
6185
6290
  let floor = this.evictedOutboxFloor;
6186
6291
  for (const syncOp of this.outbox.items) {
6187
- if (syncOp.status === SyncOperationStatus.Applied) continue;
6292
+ if (syncOp.status === 2) continue;
6188
6293
  const first = firstOrdinalOf$1(syncOp);
6189
6294
  if (first > 0 && first < floor) floor = first;
6190
6295
  }
@@ -6224,6 +6329,7 @@ function remoteCursorToRow(cursor) {
6224
6329
  };
6225
6330
  }
6226
6331
  var KyselySyncCursorStorage = class {
6332
+ db;
6227
6333
  constructor(db) {
6228
6334
  this.db = db;
6229
6335
  }
@@ -6299,6 +6405,7 @@ function deadLetterRecordToRow(record) {
6299
6405
  * PGlite/Kysely-backed implementation of {@link ISyncDeadLetterStorage}.
6300
6406
  */
6301
6407
  var KyselySyncDeadLetterStorage = class {
6408
+ db;
6302
6409
  constructor(db) {
6303
6410
  this.db = db;
6304
6411
  }
@@ -6420,6 +6527,7 @@ function remoteRecordToRow(remote) {
6420
6527
  };
6421
6528
  }
6422
6529
  var KyselySyncRemoteStorage = class {
6530
+ db;
6423
6531
  constructor(db) {
6424
6532
  this.db = db;
6425
6533
  }
@@ -6570,6 +6678,7 @@ var BatchAggregator = class {
6570
6678
  * to provide a fast path for jobs that have already synced.
6571
6679
  */
6572
6680
  var SyncAwaiter = class {
6681
+ eventBus;
6573
6682
  completedResults = /* @__PURE__ */ new Map();
6574
6683
  pendingWaiters = /* @__PURE__ */ new Map();
6575
6684
  unsubscribers = [];
@@ -6769,24 +6878,19 @@ var SyncStatusTracker = class {
6769
6878
  }
6770
6879
  };
6771
6880
  function deriveStatus(inboxCount, outboxCount, errorCount) {
6772
- if (errorCount > 0) return SyncStatus.Error;
6773
- if (inboxCount > 0 && outboxCount > 0) return SyncStatus.OutgoingAndIncoming;
6774
- if (inboxCount > 0) return SyncStatus.Incoming;
6775
- if (outboxCount > 0) return SyncStatus.Outgoing;
6776
- return SyncStatus.Synced;
6881
+ if (errorCount > 0) return "ERROR";
6882
+ if (inboxCount > 0 && outboxCount > 0) return "OUTGOING_AND_INCOMING";
6883
+ if (inboxCount > 0) return "INCOMING";
6884
+ if (outboxCount > 0) return "OUTGOING";
6885
+ return "SYNCED";
6777
6886
  }
6778
6887
  //#endregion
6779
6888
  //#region src/sync/sync-manager.ts
6780
- var OutboxMode = /* @__PURE__ */ function(OutboxMode) {
6781
- OutboxMode["Backfill"] = "backfill";
6782
- OutboxMode["BatchTriggered"] = "batch-triggered";
6783
- return OutboxMode;
6784
- }(OutboxMode || {});
6785
6889
  const defaultSyncManagerConfig = {
6786
6890
  maxDeadLettersPerRemote: 100,
6787
6891
  maxInboxBatchSize: 32,
6788
6892
  maxHeldOperationsPerRemote: 1e4,
6789
- staleRemotePollWindowMs: 5 * 6e4
6893
+ staleRemotePollWindowMs: 3e5
6790
6894
  };
6791
6895
  const PLAN_KEY_TO_JOB_UUID_CAP = 1e4;
6792
6896
  /**
@@ -6889,7 +6993,7 @@ var SyncManager = class {
6889
6993
  if (outboxAckOrdinal > 0) {
6890
6994
  const backfillController = new AbortController();
6891
6995
  this.backfillAbortControllers.set(record.name, backfillController);
6892
- this.updateOutbox(remote, outboxAckOrdinal, OutboxMode.Backfill, backfillController.signal).catch((error) => {
6996
+ this.updateOutbox(remote, outboxAckOrdinal, "backfill", backfillController.signal).catch((error) => {
6893
6997
  if (backfillController.signal.aborted) return;
6894
6998
  this.logger.error("Backfill failed for remote @RemoteName: @Error", remote.meta.name, error instanceof Error ? error : new Error(String(error)));
6895
6999
  }).finally(() => {
@@ -7008,7 +7112,7 @@ var SyncManager = class {
7008
7112
  }
7009
7113
  const backfillController = new AbortController();
7010
7114
  this.backfillAbortControllers.set(name, backfillController);
7011
- this.updateOutbox(remote, 0, OutboxMode.Backfill, backfillController.signal).catch((error) => {
7115
+ this.updateOutbox(remote, 0, "backfill", backfillController.signal).catch((error) => {
7012
7116
  if (backfillController.signal.aborted) return;
7013
7117
  this.logger.error("Backfill failed for remote @RemoteName: @Error", remote.meta.name, error instanceof Error ? error : new Error(String(error)));
7014
7118
  }).finally(() => {
@@ -7128,7 +7232,7 @@ var SyncManager = class {
7128
7232
  scopes: syncOp.scopes,
7129
7233
  branch: syncOp.branch,
7130
7234
  operations: syncOp.operations,
7131
- errorSource: syncOp.error?.source ?? ChannelErrorSource.None,
7235
+ errorSource: syncOp.error?.source ?? "none",
7132
7236
  errorMessage: syncOp.error?.error.message ?? "unknown",
7133
7237
  errorType
7134
7238
  };
@@ -7188,7 +7292,7 @@ var SyncManager = class {
7188
7292
  for (const remote of affectedRemotes) trimMailboxFromBatch(remote.channel.inbox, batch);
7189
7293
  for (const remote of affectedRemotes) {
7190
7294
  if (!this.remotes.has(remote.meta.name) || this.removing.has(remote.meta.name)) continue;
7191
- await this.updateOutbox(remote, remote.channel.outbox.latestOrdinal, OutboxMode.BatchTriggered);
7295
+ await this.updateOutbox(remote, remote.channel.outbox.latestOrdinal, "batch-triggered");
7192
7296
  }
7193
7297
  await this.drainPrunes();
7194
7298
  }
@@ -7230,7 +7334,7 @@ var SyncManager = class {
7230
7334
  if (this.isShutdown) return;
7231
7335
  const err = error instanceof Error ? error : new Error(String(error));
7232
7336
  this.logger.error("Failed to load operations from inbox (@remote, @documentId, @error)", remote.meta.name, syncOp.documentId, err.message);
7233
- const channelError = new ChannelError(ChannelErrorSource.Inbox, err);
7337
+ const channelError = new ChannelError("inbox", err);
7234
7338
  syncOp.failed(channelError);
7235
7339
  remote.channel.deadLetter.add(syncOp);
7236
7340
  remote.channel.inbox.remove(syncOp);
@@ -7243,14 +7347,14 @@ var SyncManager = class {
7243
7347
  if (this.isShutdown) return;
7244
7348
  const err = error instanceof Error ? error : new Error(String(error));
7245
7349
  this.logger.error("Failed to wait for job completion (@remote, @documentId, @jobId, @error)", remote.meta.name, syncOp.documentId, jobInfo.id, err.message);
7246
- const channelError = new ChannelError(ChannelErrorSource.Inbox, err);
7350
+ const channelError = new ChannelError("inbox", err);
7247
7351
  syncOp.failed(channelError);
7248
7352
  remote.channel.deadLetter.add(syncOp);
7249
7353
  remote.channel.inbox.remove(syncOp);
7250
7354
  return;
7251
7355
  }
7252
7356
  if (this.isShutdown) return;
7253
- if (completedJobInfo.status === JobStatus.FAILED) {
7357
+ if (completedJobInfo.status === "FAILED") {
7254
7358
  const errorMessage = completedJobInfo.error?.message || "Unknown error";
7255
7359
  this.logger.error("Failed to apply operations from inbox (@remote, @documentId, @jobId, @error)", remote.meta.name, syncOp.documentId, completedJobInfo.id, errorMessage);
7256
7360
  syncOp.failed(this.inboxFailure(completedJobInfo.error));
@@ -7293,7 +7397,7 @@ var SyncManager = class {
7293
7397
  if (this.isShutdown) return;
7294
7398
  for (const { remote, syncOp } of items) {
7295
7399
  const err = error instanceof Error ? error : new Error(String(error));
7296
- syncOp.failed(new ChannelError(ChannelErrorSource.Inbox, err));
7400
+ syncOp.failed(new ChannelError("inbox", err));
7297
7401
  remote.channel.deadLetter.add(syncOp);
7298
7402
  remote.channel.inbox.remove(syncOp);
7299
7403
  }
@@ -7310,7 +7414,7 @@ var SyncManager = class {
7310
7414
  for (const { remote, syncOp } of items) {
7311
7415
  if (!(syncOp.jobId in result.jobs)) {
7312
7416
  this.logger.error("Job key missing from batch load result (@remote, @documentId, @jobId)", remote.meta.name, syncOp.documentId, syncOp.jobId);
7313
- const error = new ChannelError(ChannelErrorSource.Inbox, /* @__PURE__ */ new Error(`Job key '${syncOp.jobId}' missing from batch load result`));
7417
+ const error = new ChannelError("inbox", /* @__PURE__ */ new Error(`Job key '${syncOp.jobId}' missing from batch load result`));
7314
7418
  syncOp.failed(error);
7315
7419
  remote.channel.deadLetter.add(syncOp);
7316
7420
  remote.channel.inbox.remove(syncOp);
@@ -7323,13 +7427,13 @@ var SyncManager = class {
7323
7427
  } catch (error) {
7324
7428
  if (this.isShutdown) continue;
7325
7429
  const err = error instanceof Error ? error : new Error(String(error));
7326
- syncOp.failed(new ChannelError(ChannelErrorSource.Inbox, err));
7430
+ syncOp.failed(new ChannelError("inbox", err));
7327
7431
  remote.channel.deadLetter.add(syncOp);
7328
7432
  remote.channel.inbox.remove(syncOp);
7329
7433
  continue;
7330
7434
  }
7331
7435
  if (this.isShutdown) return;
7332
- if (completedJobInfo.status === JobStatus.FAILED) {
7436
+ if (completedJobInfo.status === "FAILED") {
7333
7437
  syncOp.failed(this.inboxFailure(completedJobInfo.error));
7334
7438
  remote.channel.deadLetter.add(syncOp);
7335
7439
  } else syncOp.executed();
@@ -7348,7 +7452,7 @@ var SyncManager = class {
7348
7452
  */
7349
7453
  inboxFailure(error) {
7350
7454
  const message = error?.message || "Unknown error";
7351
- return new ChannelError(ChannelErrorSource.Inbox, /* @__PURE__ */ new Error(`Failed to apply operations: ${message}`), classifyJobFailure(error?.name ?? "Error"));
7455
+ return new ChannelError("inbox", /* @__PURE__ */ new Error(`Failed to apply operations: ${message}`), classifyJobFailure(error?.name ?? "Error"));
7352
7456
  }
7353
7457
  /**
7354
7458
  * Where the next outbox derivation starts: at the evicted floor when there is
@@ -7375,7 +7479,7 @@ var SyncManager = class {
7375
7479
  */
7376
7480
  evictPastOutboxBound(remote) {
7377
7481
  const cap = this.config.maxHeldOperationsPerRemote;
7378
- const queued = remote.channel.outbox.items.filter((syncOp) => syncOp.status === SyncOperationStatus.Unknown).sort((a, b) => firstOrdinalOf(a) - firstOrdinalOf(b));
7482
+ const queued = remote.channel.outbox.items.filter((syncOp) => syncOp.status === -1).sort((a, b) => firstOrdinalOf(a) - firstOrdinalOf(b));
7379
7483
  let kept = 0;
7380
7484
  let cut = queued.length;
7381
7485
  for (let i = 0; i < queued.length; i++) {
@@ -7467,7 +7571,7 @@ var SyncManager = class {
7467
7571
  * it marked; the count is what keeps that drain from removing the remote this
7468
7572
  * call is still adding to.
7469
7573
  */
7470
- async updateOutbox(remote, ackOrdinal, mode = OutboxMode.Backfill, signal) {
7574
+ async updateOutbox(remote, ackOrdinal, mode = "backfill", signal) {
7471
7575
  this.derivingOutboxes++;
7472
7576
  try {
7473
7577
  await this.deriveOutbox(remote, ackOrdinal, mode, signal);
@@ -7495,11 +7599,11 @@ var SyncManager = class {
7495
7599
  const prevJobId = lastJobByDoc.get(batch.documentId);
7496
7600
  const deps = [];
7497
7601
  if (prevJobId) deps.push(prevJobId);
7498
- if (mode === OutboxMode.BatchTriggered && prevChainJobId && prevChainJobId !== prevJobId) deps.push(prevChainJobId);
7602
+ if (mode === "batch-triggered" && prevChainJobId && prevChainJobId !== prevJobId) deps.push(prevChainJobId);
7499
7603
  const syncOp = new SyncOperation(crypto.randomUUID(), jobId, deps, remote.meta.name, batch.documentId, [batch.scope], batch.branch, batch.operations);
7500
7604
  syncOps.push(syncOp);
7501
7605
  lastJobByDoc.set(batch.documentId, jobId);
7502
- if (mode === OutboxMode.BatchTriggered) prevChainJobId = jobId;
7606
+ if (mode === "batch-triggered") prevChainJobId = jobId;
7503
7607
  }
7504
7608
  remote.channel.outbox.add(...syncOps);
7505
7609
  this.evictPastOutboxBound(remote);
@@ -7807,7 +7911,7 @@ var Reactor = class {
7807
7911
  this.logger.verbose("create(@id, @type, @slug)", document.header.id, document.header.documentType, document.header.slug);
7808
7912
  const createdAtUtcIso = (/* @__PURE__ */ new Date()).toISOString();
7809
7913
  throwIfAborted(signal, () => new AbortError());
7810
- let actions = [createDocumentAction({
7914
+ const createAction = createDocumentAction({
7811
7915
  model: document.header.documentType,
7812
7916
  version: 0,
7813
7917
  documentId: document.header.id,
@@ -7823,14 +7927,20 @@ var Reactor = class {
7823
7927
  branch: document.header.branch,
7824
7928
  meta: document.header.meta,
7825
7929
  protocolVersions: document.header.protocolVersions ?? { "base-reducer": 2 }
7826
- }), upgradeDocumentAction({
7930
+ });
7931
+ const upgradeAction = upgradeDocumentAction({
7827
7932
  documentId: document.header.id,
7828
7933
  model: document.header.documentType,
7829
7934
  fromVersion: 0,
7830
7935
  toVersion: normalizeDocumentModelVersion(document.state.document?.version),
7831
7936
  initialState: document.state
7832
- })];
7833
- if (signer) actions = await signActions(actions, signer, signal);
7937
+ });
7938
+ const branch = document.header.branch || "main";
7939
+ let actions = [createAction, upgradeAction];
7940
+ if (signer) actions = await signActions(actions, signer, {
7941
+ documentId: document.header.id,
7942
+ branch
7943
+ }, signal);
7834
7944
  const jobId = v4();
7835
7945
  const jobMeta = buildSingleJobMeta(jobId, meta);
7836
7946
  const job = {
@@ -7838,7 +7948,7 @@ var Reactor = class {
7838
7948
  kind: "mutation",
7839
7949
  documentId: document.header.id,
7840
7950
  scope: "document",
7841
- branch: "main",
7951
+ branch,
7842
7952
  actions,
7843
7953
  operations: [],
7844
7954
  createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -7850,7 +7960,7 @@ var Reactor = class {
7850
7960
  const jobInfo = {
7851
7961
  id: jobId,
7852
7962
  documentId: job.documentId,
7853
- status: JobStatus.PENDING,
7963
+ status: "PENDING",
7854
7964
  createdAtUtcIso,
7855
7965
  consistencyToken: {
7856
7966
  version: 1,
@@ -7869,7 +7979,10 @@ var Reactor = class {
7869
7979
  const createdAtUtcIso = (/* @__PURE__ */ new Date()).toISOString();
7870
7980
  throwIfAborted(signal, () => new AbortError());
7871
7981
  let action = deleteDocumentAction(id);
7872
- if (signer) action = await signAction(action, signer, signal);
7982
+ if (signer) action = await signAction(action, signer, {
7983
+ documentId: id,
7984
+ branch: "main"
7985
+ }, signal);
7873
7986
  const jobId = v4();
7874
7987
  const jobMeta = buildSingleJobMeta(jobId, meta);
7875
7988
  const job = {
@@ -7889,7 +8002,7 @@ var Reactor = class {
7889
8002
  const jobInfo = {
7890
8003
  id: jobId,
7891
8004
  documentId: job.documentId,
7892
- status: JobStatus.PENDING,
8005
+ status: "PENDING",
7893
8006
  createdAtUtcIso,
7894
8007
  consistencyToken: {
7895
8008
  version: 1,
@@ -7927,7 +8040,7 @@ var Reactor = class {
7927
8040
  const jobInfo = {
7928
8041
  id: jobId,
7929
8042
  documentId: job.documentId,
7930
- status: JobStatus.PENDING,
8043
+ status: "PENDING",
7931
8044
  createdAtUtcIso,
7932
8045
  consistencyToken: {
7933
8046
  version: 1,
@@ -7967,7 +8080,7 @@ var Reactor = class {
7967
8080
  const jobInfo = {
7968
8081
  id: jobId,
7969
8082
  documentId: job.documentId,
7970
- status: JobStatus.PENDING,
8083
+ status: "PENDING",
7971
8084
  createdAtUtcIso,
7972
8085
  consistencyToken: {
7973
8086
  version: 1,
@@ -8002,7 +8115,7 @@ var Reactor = class {
8002
8115
  const jobInfo = {
8003
8116
  id: planKeyToJobId.get(jobPlan.key),
8004
8117
  documentId: jobPlan.documentId,
8005
- status: JobStatus.PENDING,
8118
+ status: "PENDING",
8006
8119
  createdAtUtcIso,
8007
8120
  consistencyToken: {
8008
8121
  version: 1,
@@ -8072,7 +8185,7 @@ var Reactor = class {
8072
8185
  const jobInfo = {
8073
8186
  id: planKeyToJobId.get(jobPlan.key),
8074
8187
  documentId: jobPlan.documentId,
8075
- status: JobStatus.PENDING,
8188
+ status: "PENDING",
8076
8189
  createdAtUtcIso,
8077
8190
  consistencyToken: {
8078
8191
  version: 1,
@@ -8126,21 +8239,30 @@ var Reactor = class {
8126
8239
  this.logger.verbose("addRelationship(@sourceId, @targetId, @relationshipType, @metadata, @branch)", sourceId, targetId, relationshipType, metadata, branch);
8127
8240
  throwIfAborted(signal, () => new AbortError());
8128
8241
  let actions = [addRelationshipAction(sourceId, targetId, relationshipType, metadata)];
8129
- if (signer) actions = await signActions(actions, signer, signal);
8242
+ if (signer) actions = await signActions(actions, signer, {
8243
+ documentId: sourceId,
8244
+ branch
8245
+ }, signal);
8130
8246
  return await this.execute(sourceId, branch, actions, signal);
8131
8247
  }
8132
8248
  async updateRelationship(sourceId, targetId, relationshipType, metadata, branch = "main", signer, signal) {
8133
8249
  this.logger.verbose("updateRelationship(@sourceId, @targetId, @relationshipType, @metadata, @branch)", sourceId, targetId, relationshipType, metadata, branch);
8134
8250
  throwIfAborted(signal, () => new AbortError());
8135
8251
  let actions = [updateRelationshipAction(sourceId, targetId, relationshipType, metadata)];
8136
- if (signer) actions = await signActions(actions, signer, signal);
8252
+ if (signer) actions = await signActions(actions, signer, {
8253
+ documentId: sourceId,
8254
+ branch
8255
+ }, signal);
8137
8256
  return await this.execute(sourceId, branch, actions, signal);
8138
8257
  }
8139
8258
  async removeRelationship(sourceId, targetId, relationshipType, branch = "main", signer, signal) {
8140
8259
  this.logger.verbose("removeRelationship(@sourceId, @targetId, @relationshipType, @branch)", sourceId, targetId, relationshipType, branch);
8141
8260
  throwIfAborted(signal, () => new AbortError());
8142
8261
  let actions = [removeRelationshipAction(sourceId, targetId, relationshipType)];
8143
- if (signer) actions = await signActions(actions, signer, signal);
8262
+ if (signer) actions = await signActions(actions, signer, {
8263
+ documentId: sourceId,
8264
+ branch
8265
+ }, signal);
8144
8266
  return await this.execute(sourceId, branch, actions, signal);
8145
8267
  }
8146
8268
  getJobStatus(jobId, signal) {
@@ -8152,7 +8274,7 @@ var Reactor = class {
8152
8274
  return Promise.resolve({
8153
8275
  id: jobId,
8154
8276
  documentId: "",
8155
- status: JobStatus.FAILED,
8277
+ status: "FAILED",
8156
8278
  createdAtUtcIso: now,
8157
8279
  completedAtUtcIso: now,
8158
8280
  error: toErrorInfo$1("Job not found"),
@@ -8244,8 +8366,11 @@ var ReactorBuilder = class {
8244
8366
  eventBus;
8245
8367
  readModelCoordinator;
8246
8368
  readModelCoordinatorFactory;
8247
- signatureVerifier;
8248
8369
  kyselyInstance;
8370
+ signer;
8371
+ workerSigner;
8372
+ trustPolicy;
8373
+ workerTrustPolicy;
8249
8374
  signalHandlersEnabled = false;
8250
8375
  queueInstance;
8251
8376
  channelScheme;
@@ -8344,10 +8469,34 @@ var ReactorBuilder = class {
8344
8469
  this.eventBus = eventBus;
8345
8470
  return this;
8346
8471
  }
8347
- withSignatureVerifier(verifier) {
8348
- this.signatureVerifier = verifier;
8472
+ /**
8473
+ * Signs the operations the executor synthesizes: the NOOP an UNDO becomes and
8474
+ * the action a REDO rebuilds. Without one they are stored unsigned. Pooled
8475
+ * workers import `workerSigner` to build the same signer.
8476
+ */
8477
+ withSigner(signer, workerSigner) {
8478
+ this.signer = signer;
8479
+ this.workerSigner = workerSigner;
8349
8480
  return this;
8350
8481
  }
8482
+ /** Whether {@link withSigner} was called. */
8483
+ hasSigner() {
8484
+ return this.signer !== void 0;
8485
+ }
8486
+ /**
8487
+ * Decides at admission whether a key may sign as the user it claims; see
8488
+ * {@link SignatureTrustPolicy} for the contract and the default. Pooled
8489
+ * workers import `workerTrustPolicy` to build the same policy.
8490
+ */
8491
+ withTrustPolicy(trustPolicy, workerTrustPolicy) {
8492
+ this.trustPolicy = trustPolicy;
8493
+ this.workerTrustPolicy = workerTrustPolicy;
8494
+ return this;
8495
+ }
8496
+ /** Whether {@link withTrustPolicy} was called. */
8497
+ hasTrustPolicy() {
8498
+ return this.trustPolicy !== void 0;
8499
+ }
8351
8500
  withKysely(kysely) {
8352
8501
  this.kyselyInstance = kysely;
8353
8502
  return this;
@@ -8401,8 +8550,7 @@ var ReactorBuilder = class {
8401
8550
  * this enables the pool — there is no `enabled` flag. Provide `db`
8402
8551
  * (each worker opens its own Postgres pool; the parent database is built
8403
8552
  * from it too unless {@link withKysely} is set) or a custom `factory`
8404
- * transport. `verifier` is imported by the default transport's workers;
8405
- * omitted = no executor-side signature verification.
8553
+ * transport.
8406
8554
  */
8407
8555
  withWorkerPool(options) {
8408
8556
  this.workerPool = options;
@@ -8477,11 +8625,12 @@ var ReactorBuilder = class {
8477
8625
  if (resolver instanceof DocumentModelResolver) resolver.setModelLoadedHook((documentType) => eventBus.emit(ReactorEventTypes.MODEL_LOADED, { documentType }));
8478
8626
  const queue = this.queueInstance ?? new InMemoryQueue(eventBus, resolver);
8479
8627
  const jobTracker = new InMemoryJobTracker(eventBus);
8480
- const writeCache = new KyselyWriteCache(keyframeStore, operationStore, documentModelRegistry, {
8628
+ const cacheConfig = {
8481
8629
  maxDocuments: this.writeCacheConfig?.maxDocuments ?? 100,
8482
8630
  ringBufferSize: this.writeCacheConfig?.ringBufferSize ?? 10,
8483
8631
  keyframeInterval: this.writeCacheConfig?.keyframeInterval ?? 10
8484
- });
8632
+ };
8633
+ const writeCache = new KyselyWriteCache(keyframeStore, operationStore, documentModelRegistry, cacheConfig);
8485
8634
  await writeCache.startup();
8486
8635
  const operationIndex = new KyselyOperationIndex(database);
8487
8636
  const documentMetaCache = new DocumentMetaCache(operationStore, { maxDocuments: 1e3 });
@@ -8490,20 +8639,24 @@ var ReactorBuilder = class {
8490
8639
  const executionScope = new KyselyExecutionScope(database, operationStore, operationIndex, keyframeStore, writeCache, documentMetaCache, collectionMembershipCache);
8491
8640
  let executorManager = this.executorManager;
8492
8641
  let executorStartCount = this.executorConfig.maxConcurrency ?? 1;
8493
- if (!executorManager) if (this.workerPool) {
8494
- const pool = this.workerPool;
8495
- let factory = pool.factory;
8496
- if (!factory) {
8497
- if (pool.db === void 0) throw new Error("unreachable: worker pool configured without db or factory");
8498
- factory = await this.createDefaultWorkerFactory(pool.numWorkers, pool.db, pool.verifier);
8499
- }
8500
- const poolManager = new WorkerPoolJobExecutorManager(factory, eventBus, queue, jobTracker, this.logger, resolver, operationIndex, this.executorConfig.jobTimeoutMs, this.executorConfig.deferredJobTtlMs);
8501
- executorManager = poolManager;
8502
- executorStartCount = pool.numWorkers;
8503
- if (resolver instanceof DocumentModelResolver) resolver.setBroadcastHook((entry) => poolManager.loadModel(entry));
8504
- } 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);
8642
+ if (!executorManager) {
8643
+ if (this.workerPool) {
8644
+ const pool = this.workerPool;
8645
+ let factory = pool.factory;
8646
+ if (!factory) {
8647
+ if (pool.db === void 0) throw new Error("unreachable: worker pool configured without db or factory");
8648
+ if (this.signer && !this.workerSigner) this.logger.warn("Worker pool has no signer spec; pooled workers store synthesized operations unsigned");
8649
+ if (this.trustPolicy && !this.workerTrustPolicy) this.logger.warn("Worker pool has no trust policy spec; pooled workers apply the default trust policy");
8650
+ factory = await this.createDefaultWorkerFactory(pool.numWorkers, pool.db);
8651
+ }
8652
+ const poolManager = new WorkerPoolJobExecutorManager(factory, eventBus, queue, jobTracker, this.logger, resolver, operationIndex, this.executorConfig.jobTimeoutMs, this.executorConfig.deferredJobTtlMs);
8653
+ executorManager = poolManager;
8654
+ executorStartCount = pool.numWorkers;
8655
+ if (resolver instanceof DocumentModelResolver) resolver.setBroadcastHook((entry) => poolManager.loadModel(entry));
8656
+ } else executorManager = new SimpleJobExecutorManager(() => new SimpleJobExecutor(this.logger, documentModelRegistry, operationStore, eventBus, writeCache, operationIndex, documentMetaCache, collectionMembershipCache, this.driveContainerTypes, this.executorConfig, executionScope, this.signer, this.trustPolicy), eventBus, queue, jobTracker, this.logger, resolver, this.executorConfig.jobTimeoutMs, this.executorConfig.deferredJobTtlMs);
8657
+ }
8505
8658
  await executorManager.start(executorStartCount);
8506
- const callerReadModels = Array.from(new Set([...this.readModels]));
8659
+ const callerReadModels = Array.from(/* @__PURE__ */ new Set([...this.readModels]));
8507
8660
  const degradedComponents = [];
8508
8661
  const startDegraded = (component, error) => {
8509
8662
  degradedComponents.push({
@@ -8517,7 +8670,7 @@ var ReactorBuilder = class {
8517
8670
  yieldDeadlineMs: this.executorConfig.yieldDeadlineMs ?? 50
8518
8671
  };
8519
8672
  const documentViewConsistencyTracker = new ConsistencyTracker();
8520
- const documentView = new KyselyDocumentView(database, operationStore, operationIndex, writeCache, documentViewConsistencyTracker, featureFlags.documentDecisions ? DeletedDocumentRead.StateAtDeletion : DeletedDocumentRead.NotFound, readModelIndexing);
8673
+ const documentView = new KyselyDocumentView(database, operationStore, operationIndex, writeCache, documentViewConsistencyTracker, featureFlags.documentDecisions ? "StateAtDeletion" : "NotFound", readModelIndexing);
8521
8674
  try {
8522
8675
  await documentView.init();
8523
8676
  } catch (error) {
@@ -8573,7 +8726,7 @@ var ReactorBuilder = class {
8573
8726
  const reactor = new Reactor(this.logger, documentModelRegistry, queue, jobTracker, readModelCoordinator, this.features, documentView, documentIndexer, operationStore, eventBus, executorManager);
8574
8727
  let syncModule = void 0;
8575
8728
  if (this.channelScheme) {
8576
- const factory = this.channelScheme === ChannelScheme.CONNECT ? new GqlRequestChannelFactory(this.logger, this.jwtHandler, queue) : new GqlResponseChannelFactory(this.logger);
8729
+ const factory = this.channelScheme === "connect" ? new GqlRequestChannelFactory(this.logger, this.jwtHandler, queue) : new GqlResponseChannelFactory(this.logger);
8577
8730
  syncModule = new SyncBuilder().withChannelFactory(factory).buildModule(reactor, this.logger, operationIndex, eventBus, database, this.driveContainerTypes);
8578
8731
  await syncModule.syncManager.startup();
8579
8732
  } else if (this.syncBuilder) {
@@ -8674,7 +8827,7 @@ var ReactorBuilder = class {
8674
8827
  poolInstrumentations.push(forwarder);
8675
8828
  this.instrumentedPools.push(forwarder);
8676
8829
  }
8677
- const { ProjectionShardManager } = await import("./projection-shard-manager-CUo4bF4O.js");
8830
+ const { ProjectionShardManager } = await import("./projection-shard-manager-B-j8VYzA.js");
8678
8831
  const manager = new ProjectionShardManager({
8679
8832
  shardCount: config.shardCount,
8680
8833
  db,
@@ -8708,7 +8861,7 @@ var ReactorBuilder = class {
8708
8861
  return manager;
8709
8862
  }
8710
8863
  async createDefaultProjectionWorkerFactory() {
8711
- const [{ createProjectionThreadTransport }, { projectionWorkerEntryPath }] = await Promise.all([import("./transport-CuogVKN_.js"), import("./projection-worker-wI4PwcV2.js")]);
8864
+ const [{ createProjectionThreadTransport }, { projectionWorkerEntryPath }] = await Promise.all([import("./transport-B3G50KVd.js"), import("./projection-worker-D8uNFdwX.js")]);
8712
8865
  return () => createProjectionThreadTransport(projectionWorkerEntryPath);
8713
8866
  }
8714
8867
  /**
@@ -8716,11 +8869,11 @@ var ReactorBuilder = class {
8716
8869
  * custom `factory`. Each worker spawns a real `node:worker_threads`
8717
8870
  * Worker pointing at the compiled `worker/entry.js`.
8718
8871
  */
8719
- async createDefaultWorkerFactory(numWorkers, db, signatureVerifier) {
8872
+ async createDefaultWorkerFactory(numWorkers, db) {
8720
8873
  const [{ WorkerHandle }, { createThreadTransport }, { workerEntryPath }] = await Promise.all([
8721
- import("./worker-handle-CrERzl8s.js"),
8722
- import("./transport-ByGviWdZ.js"),
8723
- import("./worker-g03lNM42.js").then((n) => n.n)
8874
+ import("./worker-handle-CwWNTpUg.js"),
8875
+ import("./transport-CZDHzfvy.js"),
8876
+ Promise.resolve().then(() => worker_exports)
8724
8877
  ]);
8725
8878
  const poolConfig = {
8726
8879
  enabled: true,
@@ -8740,9 +8893,10 @@ var ReactorBuilder = class {
8740
8893
  initPayload: {
8741
8894
  poolConfig,
8742
8895
  db,
8743
- signatureVerifier,
8744
8896
  models,
8745
- executorConfig: this.executorConfig
8897
+ executorConfig: this.executorConfig,
8898
+ signer: this.workerSigner,
8899
+ trustPolicy: this.workerTrustPolicy
8746
8900
  },
8747
8901
  logger,
8748
8902
  poolInstrumentation
@@ -8985,30 +9139,6 @@ function createHybridProjectionCoordinatorFactory(options = {}) {
8985
9139
  };
8986
9140
  }
8987
9141
  //#endregion
8988
- //#region src/signer/passthrough-signer.ts
8989
- /**
8990
- * A no-op signer that returns empty values for all methods.
8991
- * Used when signing is not required.
8992
- */
8993
- var PassthroughSigner = class {
8994
- publicKey = {};
8995
- sign() {
8996
- return Promise.resolve(new Uint8Array(0));
8997
- }
8998
- verify() {
8999
- return Promise.resolve();
9000
- }
9001
- signAction() {
9002
- return Promise.resolve([
9003
- "",
9004
- "",
9005
- "",
9006
- "",
9007
- ""
9008
- ]);
9009
- }
9010
- };
9011
- //#endregion
9012
9142
  //#region src/core/reactor-client-builder.ts
9013
9143
  /**
9014
9144
  * Builder class for constructing ReactorClient instances with proper configuration
@@ -9021,7 +9151,10 @@ var ReactorClientBuilder = class {
9021
9151
  documentIndexer;
9022
9152
  documentView;
9023
9153
  signer;
9024
- signatureVerifier;
9154
+ workerSigner;
9155
+ trustPolicy;
9156
+ workerTrustPolicy;
9157
+ createSignaturePolicy = DEFAULT_SIGNATURE_POLICY;
9025
9158
  subscriptionManager;
9026
9159
  jobAwaiter;
9027
9160
  documentModelLoader;
@@ -9055,17 +9188,28 @@ var ReactorClientBuilder = class {
9055
9188
  return this;
9056
9189
  }
9057
9190
  /**
9058
- * Sets the signer configuration for signing and verifying actions.
9059
- *
9060
- * @param config - Either an ISigner for signing only, or a SignerConfig for both signing and verification
9191
+ * Signs submitted actions, and the operations a reactor built from
9192
+ * `withReactorBuilder` synthesizes unless that builder has its own signer.
9193
+ * A `trustPolicy` reaches that builder unless it has its own.
9061
9194
  */
9062
9195
  withSigner(config) {
9063
9196
  if ("signer" in config) {
9064
9197
  this.signer = config.signer;
9065
- this.signatureVerifier = config.verifier;
9198
+ this.workerSigner = config.workerSigner;
9199
+ this.trustPolicy = config.trustPolicy;
9200
+ this.workerTrustPolicy = config.workerTrustPolicy;
9066
9201
  } else this.signer = config;
9067
9202
  return this;
9068
9203
  }
9204
+ /**
9205
+ * What the client creates new documents as when a call does not say:
9206
+ * `v2-required` (the default) or `legacy`, for a fleet with peers that
9207
+ * predate v2-required documents. Existing documents keep their policy.
9208
+ */
9209
+ withCreateSignaturePolicy(policy) {
9210
+ this.createSignaturePolicy = policy;
9211
+ return this;
9212
+ }
9069
9213
  withSubscriptionManager(subscriptionManager) {
9070
9214
  this.subscriptionManager = subscriptionManager;
9071
9215
  return this;
@@ -9130,8 +9274,9 @@ var ReactorClientBuilder = class {
9130
9274
  let documentView;
9131
9275
  let reactorModule;
9132
9276
  if (this.reactorBuilder) {
9133
- if (this.signatureVerifier) this.reactorBuilder.withSignatureVerifier(this.signatureVerifier);
9134
9277
  if (this.documentModelLoader) this.reactorBuilder.withDocumentModelLoader(this.documentModelLoader);
9278
+ if (this.signer && !this.reactorBuilder.hasSigner()) this.reactorBuilder.withSigner(this.signer, this.workerSigner);
9279
+ if (this.trustPolicy && !this.reactorBuilder.hasTrustPolicy()) this.reactorBuilder.withTrustPolicy(this.trustPolicy, this.workerTrustPolicy);
9135
9280
  reactorModule = await this.reactorBuilder.buildModule();
9136
9281
  reactor = reactorModule.reactor;
9137
9282
  eventBus = reactorModule.eventBus;
@@ -9149,7 +9294,7 @@ var ReactorClientBuilder = class {
9149
9294
  const jobAwaiter = this.jobAwaiter ?? new JobAwaiter(eventBus, (jobId, signal) => reactor.getJobStatus(jobId, signal));
9150
9295
  const decisionModel = reactorModule ? readDecisionModel(reactorModule.featureFlags, reactorModule.documentModelRegistry) : void 0;
9151
9296
  return {
9152
- client: new ReactorClient(this.logger, reactor, signer, subscriptionManager, jobAwaiter, documentIndexer, documentView, this.readGate ?? this.resolveReadGate(reactorModule, documentView, decisionModel), this.resolveActionEvaluation(reactorModule, decisionModel)),
9297
+ client: new ReactorClient(this.logger, reactor, signer, subscriptionManager, jobAwaiter, documentIndexer, documentView, this.readGate ?? this.resolveReadGate(reactorModule, documentView, decisionModel), this.resolveActionEvaluation(reactorModule, decisionModel), this.createSignaturePolicy),
9153
9298
  reactor,
9154
9299
  eventBus,
9155
9300
  documentIndexer,
@@ -9187,6 +9332,38 @@ function driveIdFromUrl(url) {
9187
9332
  return url.split("/").pop() ?? "";
9188
9333
  }
9189
9334
  //#endregion
9335
+ //#region src/signer/types.ts
9336
+ const SIGNATURE_REFUSAL_CODES = [
9337
+ "UNSIGNED_REQUIRED",
9338
+ "UNSIGNED_IDENTITY",
9339
+ "KEY_MISMATCH",
9340
+ "MALFORMED_TUPLE",
9341
+ "TIMESTAMP_MISMATCH",
9342
+ "HASH_MISMATCH",
9343
+ "BAD_SIGNATURE",
9344
+ "SCHEME_BELOW_POLICY",
9345
+ "ID_MISMATCH",
9346
+ "DUPLICATE_ACTION",
9347
+ "SIGNER_UNAUTHORIZED"
9348
+ ];
9349
+ //#endregion
9350
+ //#region src/executor/worker/index.ts
9351
+ var worker_exports = /* @__PURE__ */ __exportAll({
9352
+ createForwardingLogger: () => createForwardingLogger,
9353
+ errorToInfo: () => errorToInfo,
9354
+ sanitizeArg: () => sanitizeArg,
9355
+ workerEntryPath: () => workerEntryPath
9356
+ });
9357
+ function resolveWorkerEntryPath() {
9358
+ const url = new URL("./entry.js", import.meta.url);
9359
+ if (url.protocol !== "file:") return url.href;
9360
+ let pathname = decodeURIComponent(url.pathname);
9361
+ if (/^\/[A-Za-z]:[/\\]/.test(pathname)) pathname = pathname.slice(1);
9362
+ return pathname;
9363
+ }
9364
+ /** Absolute path to the worker entry script for use with `new Worker(workerEntryPath)`. */
9365
+ const workerEntryPath = resolveWorkerEntryPath();
9366
+ //#endregion
9190
9367
  //#region src/decision/sync-scope-gate.ts
9191
9368
  /** What a subject may read of a document that is not there to be read. */
9192
9369
  const META_ONLY = (scope) => ALWAYS_READABLE_SCOPES.has(scope);
@@ -9198,6 +9375,9 @@ const META_ONLY = (scope) => ALWAYS_READABLE_SCOPES.has(scope);
9198
9375
  * path's own job, and it is the only thing this adds to the gate it wraps.
9199
9376
  */
9200
9377
  var SyncScopeGate = class {
9378
+ gate;
9379
+ documentView;
9380
+ logger;
9201
9381
  constructor(gate, documentView, logger) {
9202
9382
  this.gate = gate;
9203
9383
  this.documentView = documentView;
@@ -9414,6 +9594,6 @@ var DocumentIntegrityService = class {
9414
9594
  }
9415
9595
  };
9416
9596
  //#endregion
9417
- 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 };
9597
+ 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, InvalidSignatureError, 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, SIGNATURE_REFUSAL_CODES, 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, verifyActionSignature, workerEntryPath };
9418
9598
 
9419
9599
  //# sourceMappingURL=index.js.map