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

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 (26) hide show
  1. package/dist/{build-worker-executor-DUjyF4NM.js → build-worker-executor-DxmDJ4QQ.js} +24 -8
  2. package/dist/build-worker-executor-DxmDJ4QQ.js.map +1 -0
  3. package/dist/{document-indexer-CQzTEE1o.js → document-indexer-Dn9RB0sg.js} +3 -3
  4. package/dist/{document-indexer-CQzTEE1o.js.map → document-indexer-Dn9RB0sg.js.map} +1 -1
  5. package/dist/{drive-container-types-CS5IxDiA.js → drive-container-types-Be5-EY9W.js} +843 -144
  6. package/dist/drive-container-types-Be5-EY9W.js.map +1 -0
  7. package/dist/entry.js +10 -5
  8. package/dist/entry.js.map +1 -1
  9. package/dist/index.d.ts +1951 -1805
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +203 -99
  12. package/dist/index.js.map +1 -1
  13. package/dist/projection-entry.js +4 -4
  14. package/dist/{projection-shard-manager-CUo4bF4O.js → projection-shard-manager-mQhfVipO.js} +2 -2
  15. package/dist/{projection-shard-manager-CUo4bF4O.js.map → projection-shard-manager-mQhfVipO.js.map} +1 -1
  16. package/dist/{types-DMKLa0Ok.js → types-DxDCmtId.js} +3 -2
  17. package/dist/types-DxDCmtId.js.map +1 -0
  18. package/dist/{worker-g03lNM42.js → worker-BDVzCtuf.js} +2 -2
  19. package/dist/{worker-g03lNM42.js.map → worker-BDVzCtuf.js.map} +1 -1
  20. package/dist/{worker-handle-CrERzl8s.js → worker-handle-B3p-nuqG.js} +14 -9
  21. package/dist/worker-handle-B3p-nuqG.js.map +1 -0
  22. package/package.json +6 -7
  23. package/dist/build-worker-executor-DUjyF4NM.js.map +0 -1
  24. package/dist/drive-container-types-CS5IxDiA.js.map +0 -1
  25. package/dist/types-DMKLa0Ok.js.map +0 -1
  26. package/dist/worker-handle-CrERzl8s.js.map +0 -1
@@ -1,5 +1,5 @@
1
- import { n as ReactorEventTypes, t as EventBusAggregateError } from "./types-DMKLa0Ok.js";
2
- import { AUTH_ACTION_TYPES, AUTH_DENIED_BY_GRANT_REASON, AUTH_NO_GRANT_REASON, AUTH_VERSION_UNSUPPORTED_REASON, DOCUMENT_DELETED_REASON, DowngradeNotSupportedError as DowngradeNotSupportedError$1, appendWithoutApplying, applyAuthAction, applyDeleteDocumentAction, applyDeleteDocumentAction as applyDeleteDocumentAction$1, applyUpgradeDocumentAction, applyUpgradeDocumentAction as applyUpgradeDocumentAction$1, baseReducerVersion, createPresignedHeader, decide, defaultBaseState, deriveOperationId, evaluate, garbageCollect, groupDocumentType, groupMembershipActionTypes, hashDocumentStateForScope, isDenied, isUndoRedo, mentionedGroupIds, normalizeDocumentModelVersion, operationOutcome, referencedGroupIds, sortOperations } from "@powerhousedao/shared/document-model";
1
+ import { n as ReactorEventTypes, t as EventBusAggregateError } from "./types-DxDCmtId.js";
2
+ import { AUTH_ACTION_TYPES, AUTH_DENIED_BY_GRANT_REASON, AUTH_NO_GRANT_REASON, AUTH_VERSION_UNSUPPORTED_REASON, DOCUMENT_DELETED_REASON, DOCUMENT_SCOPE_ACTION_TYPES, DowngradeNotSupportedError as DowngradeNotSupportedError$1, actionSignerIdentity, actionSigningTarget, appendWithoutApplying, applyAuthAction, applyDeleteDocumentAction, applyDeleteDocumentAction as applyDeleteDocumentAction$1, applyUpgradeDocumentAction, applyUpgradeDocumentAction as applyUpgradeDocumentAction$1, base58Decode, baseReducerVersion, buildOperationSignatureMessage, buildOperationSignatureParams, canonicalJson, createPresignedHeader, decide, defaultBaseState, deriveDocumentId, deriveOperationId, evaluate, garbageCollect, groupDocumentType, groupMembershipActionTypes, hashActionV2, hashBrowser, hashDocumentStateForScope, isDenied, isDerivedDocumentId, isUndoRedo, isV2ActionHash, mentionedGroupIds, normalizeDocumentModelVersion, operationOutcome, referencedGroupIds, signaturePolicyOf, sortOperations, targetDocumentId, v2TupleProblem, withProtocolVersions } from "@powerhousedao/shared/document-model";
3
3
  import { v4 } from "uuid";
4
4
  import { Migrator, sql } from "kysely";
5
5
  //#region \0rolldown/runtime.js
@@ -167,19 +167,22 @@ var ExcessiveReshuffleError = class ExcessiveReshuffleError extends Error {
167
167
  return Error.isError(error) && error.name === "ExcessiveReshuffleError";
168
168
  }
169
169
  };
170
- /**
171
- * Error thrown when an operation has an invalid signature.
172
- */
170
+ /** The message carries the code: only name and message cross the queue. */
173
171
  var InvalidSignatureError = class InvalidSignatureError extends Error {
174
172
  documentId;
173
+ code;
175
174
  reason;
176
- constructor(documentId, reason) {
177
- super(`Invalid signature in document ${documentId}: ${reason}`);
175
+ constructor(documentId, code, reason) {
176
+ super(`Invalid signature in document ${documentId} [${code}]: ${reason}`);
178
177
  this.name = "InvalidSignatureError";
179
178
  this.documentId = documentId;
179
+ this.code = code;
180
180
  this.reason = reason;
181
181
  Error.captureStackTrace(this, InvalidSignatureError);
182
182
  }
183
+ static isError(error) {
184
+ return Error.isError(error) && error.name === "InvalidSignatureError";
185
+ }
183
186
  };
184
187
  /**
185
188
  * An UPGRADE_DOCUMENT action's preconditions (fromVersion and the per-scope
@@ -714,34 +717,13 @@ function selectDecisionModel(flags, registry) {
714
717
  //#endregion
715
718
  //#region src/executor/util.ts
716
719
  /** Actions the reactor reduces itself, onto the document scope. */
717
- const DOCUMENT_SCOPE_ACTIONS = new Set([
718
- "CREATE_DOCUMENT",
719
- "DELETE_DOCUMENT",
720
- "UPGRADE_DOCUMENT",
721
- "ADD_RELATIONSHIP",
722
- "REMOVE_RELATIONSHIP",
723
- "UPDATE_RELATIONSHIP"
724
- ]);
720
+ const DOCUMENT_SCOPE_ACTIONS = DOCUMENT_SCOPE_ACTION_TYPES;
725
721
  /**
726
722
  * `CREATE_DOCUMENT` is exempt by necessity: it runs before the document exists,
727
723
  * so building a decision model would throw and defer the job forever.
728
724
  */
729
725
  const GATED_DOCUMENT_ACTIONS = new Set([...DOCUMENT_SCOPE_ACTIONS].filter((type) => type !== "CREATE_DOCUMENT"));
730
726
  /**
731
- * The document a document-scope action writes to, which is not always the job's
732
- * own document: delete and upgrade name it in `input.documentId`, and the
733
- * relationship actions in `input.sourceId`. `execute` only checks that a batch
734
- * shares one scope, so a caller can submit an action whose target is a document
735
- * other than the one the job is keyed by. The policy gate has to follow the
736
- * action rather than the job, or it decides against a policy the caller may
737
- * control instead of the one guarding the write.
738
- */
739
- function targetDocumentId(action, fallback) {
740
- const input = action.input;
741
- if (action.type === "ADD_RELATIONSHIP" || action.type === "REMOVE_RELATIONSHIP" || action.type === "UPDATE_RELATIONSHIP") return typeof input?.sourceId === "string" && input.sourceId.length > 0 ? input.sourceId : fallback;
742
- return typeof input?.documentId === "string" && input.documentId.length > 0 ? input.documentId : fallback;
743
- }
744
- /**
745
727
  * Creates a PHDocument from a CREATE_DOCUMENT action input.
746
728
  * Reconstructs the document header and initializes the base state.
747
729
  *
@@ -1264,6 +1246,7 @@ var DocumentMetaCache = class DocumentMetaCache {
1264
1246
  return {
1265
1247
  state: document.state.document,
1266
1248
  documentType,
1249
+ protocolVersions: createAction.input.protocolVersions,
1267
1250
  documentScopeRevision: documentScopeRevision + 1
1268
1251
  };
1269
1252
  }
@@ -1459,6 +1442,12 @@ var KyselyOperationIndex = class KyselyOperationIndex {
1459
1442
  if (signal?.aborted) throw new Error("Operation aborted");
1460
1443
  return (await this.queryExecutor.selectFrom("group_references").select("documentId").where("groupId", "=", groupId).orderBy("documentId").execute()).map((row) => row.documentId);
1461
1444
  }
1445
+ async getOrdinalsByOpIds(documentId, scope, branch, opIds, signal) {
1446
+ signal?.throwIfAborted();
1447
+ if (opIds.length === 0) return /* @__PURE__ */ new Map();
1448
+ const rows = await this.queryExecutor.selectFrom("operation_index_operations").select("opId").select((eb) => eb.fn.max("ordinal").as("ordinal")).where("documentId", "=", documentId).where("branch", "=", branch).where("scope", "=", scope).where("opId", "in", opIds).groupBy("opId").execute();
1449
+ return new Map(rows.map((row) => [row.opId, Number(row.ordinal)]));
1450
+ }
1462
1451
  async find(collectionId, cursor, view, paging, signal) {
1463
1452
  if (signal?.aborted) throw new Error("Operation aborted");
1464
1453
  const outerCursor = cursor ?? -1;
@@ -2099,10 +2088,10 @@ var KyselyWriteCache = class KyselyWriteCache {
2099
2088
  for (const pending of pendingUpgrades) pending.subsequentDeletes.push(operation.action);
2100
2089
  } else {
2101
2090
  const protocolVersion = baseReducerVersion(document.header);
2102
- document = docModule.reducer(document, operation.action, void 0, {
2091
+ document = withProtocolVersions(docModule.reducer(document, operation.action, void 0, {
2103
2092
  skip: operation.skip,
2104
2093
  protocolVersion
2105
- });
2094
+ }), document.header.protocolVersions);
2106
2095
  }
2107
2096
  }
2108
2097
  }
@@ -2151,12 +2140,12 @@ var KyselyWriteCache = class KyselyWriteCache {
2151
2140
  if (isDenied(operation)) document = appendWithoutApplying(document, operation, scope);
2152
2141
  else {
2153
2142
  const protocolVersion = baseReducerVersion(document.header);
2154
- document = getModuleCached(moduleVersion).reducer(document, operation.action, void 0, {
2143
+ document = withProtocolVersions(getModuleCached(moduleVersion).reducer(document, operation.action, void 0, {
2155
2144
  skip: operation.skip,
2156
2145
  protocolVersion,
2157
2146
  replayOptions: { operation },
2158
2147
  skipIndexValidation: true
2159
- });
2148
+ }), document.header.protocolVersions);
2160
2149
  }
2161
2150
  }
2162
2151
  const reachedTarget = targetRevision !== void 0 && result.results.some((op) => op.index >= targetRevision);
@@ -2280,12 +2269,12 @@ var KyselyWriteCache = class KyselyWriteCache {
2280
2269
  if (isDenied(operation)) document = appendWithoutApplying(document, operation, scope);
2281
2270
  else {
2282
2271
  const protocolVersion = baseReducerVersion(document.header);
2283
- document = module.reducer(document, operation.action, void 0, {
2272
+ document = withProtocolVersions(module.reducer(document, operation.action, void 0, {
2284
2273
  skip: operation.skip,
2285
2274
  protocolVersion,
2286
2275
  replayOptions: { operation },
2287
2276
  skipIndexValidation: true
2288
- });
2277
+ }), document.header.protocolVersions);
2289
2278
  }
2290
2279
  if (targetRevision !== void 0 && operation.index === targetRevision) break;
2291
2280
  }
@@ -2925,6 +2914,7 @@ var DocumentActionHandler = class {
2925
2914
  stores.documentMetaCache.putDocumentMeta(job.documentId, job.branch, {
2926
2915
  state: standing.state.document,
2927
2916
  documentType: standing.header.documentType,
2917
+ protocolVersions: standing.header.protocolVersions,
2928
2918
  documentScopeRevision: operation.index + 1
2929
2919
  });
2930
2920
  return buildSuccessResult(job, operation, job.documentId, standing.header.documentType, JSON.stringify({
@@ -2948,8 +2938,8 @@ var DocumentActionHandler = class {
2948
2938
  branch: job.branch
2949
2939
  });
2950
2940
  const resultingStateObj = {
2951
- header: document.header,
2952
- ...document.state
2941
+ ...document.state,
2942
+ header: document.header
2953
2943
  };
2954
2944
  const resultingState = JSON.stringify(resultingStateObj);
2955
2945
  const writeResult = await this.writeOperationToStore({
@@ -2983,6 +2973,7 @@ var DocumentActionHandler = class {
2983
2973
  stores.documentMetaCache.putDocumentMeta(document.header.id, job.branch, {
2984
2974
  state: document.state.document,
2985
2975
  documentType: document.header.documentType,
2976
+ protocolVersions: document.header.protocolVersions,
2986
2977
  documentScopeRevision: 1
2987
2978
  });
2988
2979
  return buildSuccessResult(job, operation, document.header.id, document.header.documentType, resultingState, startTime);
@@ -3038,6 +3029,7 @@ var DocumentActionHandler = class {
3038
3029
  stores.documentMetaCache.putDocumentMeta(documentId, job.branch, {
3039
3030
  state: document.state.document,
3040
3031
  documentType: document.header.documentType,
3032
+ protocolVersions: document.header.protocolVersions,
3041
3033
  documentScopeRevision: operation.index + 1
3042
3034
  });
3043
3035
  return buildSuccessResult(job, operation, documentId, document.header.documentType, resultingState, startTime);
@@ -3118,8 +3110,8 @@ var DocumentActionHandler = class {
3118
3110
  branch: job.branch
3119
3111
  });
3120
3112
  const resultingStateObj = {
3121
- header: document.header,
3122
- ...document.state
3113
+ ...document.state,
3114
+ header: document.header
3123
3115
  };
3124
3116
  if (fromVersion > 0) resultingStateObj.__migrated = true;
3125
3117
  const resultingState = JSON.stringify(resultingStateObj);
@@ -3154,6 +3146,7 @@ var DocumentActionHandler = class {
3154
3146
  stores.documentMetaCache.putDocumentMeta(documentId, job.branch, {
3155
3147
  state: document.state.document,
3156
3148
  documentType: document.header.documentType,
3149
+ protocolVersions: document.header.protocolVersions,
3157
3150
  documentScopeRevision: operation.index + 1
3158
3151
  });
3159
3152
  return buildSuccessResult(job, operation, documentId, document.header.documentType, resultingState, startTime);
@@ -3242,6 +3235,7 @@ var DocumentActionHandler = class {
3242
3235
  stores.documentMetaCache.putDocumentMeta(input.sourceId, job.branch, {
3243
3236
  state: sourceDoc.state.document,
3244
3237
  documentType: sourceDoc.header.documentType,
3238
+ protocolVersions: sourceDoc.header.protocolVersions,
3245
3239
  documentScopeRevision: operation.index + 1
3246
3240
  });
3247
3241
  return buildSuccessResult(job, operation, input.sourceId, sourceDoc.header.documentType, resultingState, startTime);
@@ -3270,55 +3264,624 @@ var DocumentActionHandler = class {
3270
3264
  }
3271
3265
  };
3272
3266
  //#endregion
3273
- //#region src/executor/signature-verifier.ts
3274
- var SignatureVerifier = class {
3275
- constructor(verifier) {
3276
- this.verifier = verifier;
3277
- }
3278
- async verifyActions(documentId, branch, actions) {
3279
- if (!this.verifier) return;
3280
- for (const action of actions) {
3281
- const signer = action.context?.signer;
3282
- if (!signer) continue;
3283
- if (signer.signatures.length === 0) throw new InvalidSignatureError(documentId, `Action ${action.id} has signer but no signatures`);
3284
- const publicKey = signer.app.key;
3285
- let isValid;
3286
- try {
3287
- const tempOperation = {
3288
- id: deriveOperationId(documentId, action.scope, branch, action.id),
3289
- index: 0,
3290
- timestampUtcMs: action.timestampUtcMs || (/* @__PURE__ */ new Date()).toISOString(),
3291
- hash: "",
3292
- skip: 0,
3293
- action
3294
- };
3295
- isValid = await this.verifier(tempOperation, publicKey);
3296
- } catch (error) {
3297
- const errorMessage = error instanceof Error ? error.message : String(error);
3298
- throw new InvalidSignatureError(documentId, `Action ${action.id} verification failed: ${errorMessage}`);
3267
+ //#region src/signer/passthrough-signer.ts
3268
+ /**
3269
+ * A no-op signer that returns empty values for all methods.
3270
+ * Used when signing is not required.
3271
+ */
3272
+ var PassthroughSigner = class {
3273
+ publicKey = {};
3274
+ sign() {
3275
+ return Promise.resolve(new Uint8Array(0));
3276
+ }
3277
+ verify() {
3278
+ return Promise.resolve();
3279
+ }
3280
+ signAction() {
3281
+ return Promise.resolve([
3282
+ "",
3283
+ "",
3284
+ "",
3285
+ "",
3286
+ ""
3287
+ ]);
3288
+ }
3289
+ };
3290
+ //#endregion
3291
+ //#region src/signer/trust-policy.ts
3292
+ /**
3293
+ * The policy admission asks: the reactor's own key signing as its own user is
3294
+ * accepted, then the host's policy decides, else the default does. `own` is
3295
+ * read on every ask, since its user changes on login.
3296
+ */
3297
+ function admissionTrustPolicy(own, authEnforcement, policy) {
3298
+ return { authorizeSigner(signer, key, documentId) {
3299
+ const identity = actionSignerIdentity(own);
3300
+ if (identity.app.key !== "" && key === identity.app.key) {
3301
+ if (sameUser(actionSignerIdentity(signer).user, identity.user)) return Promise.resolve(true);
3302
+ }
3303
+ if (policy) return policy.authorizeSigner(signer, key, documentId);
3304
+ return Promise.resolve(!authEnforcement);
3305
+ } };
3306
+ }
3307
+ function sameUser(a, b) {
3308
+ return a.address.toLowerCase() === b.address.toLowerCase() && a.networkId === b.networkId && a.chainId === b.chainId;
3309
+ }
3310
+ //#endregion
3311
+ //#region src/signer/did-key.ts
3312
+ const DID_KEY_PREFIX = "did:key:z";
3313
+ const P256_MULTICODEC = [128, 36];
3314
+ const MAX_CACHED_KEYS = 1024;
3315
+ const P = BigInt("0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff");
3316
+ const A = BigInt("0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc");
3317
+ const B = BigInt("0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b");
3318
+ /** A did:key that is not a compressed P-256 public key. */
3319
+ var MalformedDidKeyError = class extends Error {
3320
+ constructor(did, detail) {
3321
+ super(`Malformed did:key ${did}: ${detail}`);
3322
+ this.name = "MalformedDidKeyError";
3323
+ }
3324
+ };
3325
+ const keyCache = /* @__PURE__ */ new Map();
3326
+ /** Cached per process: decompressing the point costs a BigInt modPow. */
3327
+ async function importDidKey(did) {
3328
+ const cached = keyCache.get(did);
3329
+ if (cached) {
3330
+ keyCache.delete(did);
3331
+ keyCache.set(did, cached);
3332
+ return cached;
3333
+ }
3334
+ const raw = decompressP256(compressedPointFromDid(did), did);
3335
+ const key = await crypto.subtle.importKey("raw", raw.buffer, {
3336
+ name: "ECDSA",
3337
+ namedCurve: "P-256"
3338
+ }, false, ["verify"]);
3339
+ keyCache.set(did, key);
3340
+ if (keyCache.size > MAX_CACHED_KEYS) {
3341
+ const oldest = keyCache.keys().next().value;
3342
+ if (oldest !== void 0) keyCache.delete(oldest);
3343
+ }
3344
+ return key;
3345
+ }
3346
+ function compressedPointFromDid(did) {
3347
+ if (!did.startsWith(DID_KEY_PREFIX)) throw new MalformedDidKeyError(did, "not a base58btc did:key");
3348
+ const decoded = base58Decode(did.slice(9));
3349
+ if (!decoded || decoded.length !== 35) throw new MalformedDidKeyError(did, "not a compressed P-256 key");
3350
+ if (decoded[0] !== P256_MULTICODEC[0] || decoded[1] !== P256_MULTICODEC[1]) throw new MalformedDidKeyError(did, "not a P-256 multicodec");
3351
+ return decoded.subarray(2);
3352
+ }
3353
+ function decompressP256(compressed, did) {
3354
+ const prefix = compressed[0];
3355
+ if (prefix !== 2 && prefix !== 3) throw new MalformedDidKeyError(did, `compression prefix ${prefix}`);
3356
+ let x = BigInt(0);
3357
+ for (let i = 1; i < compressed.length; i++) x = x << BigInt(8) | BigInt(compressed[i]);
3358
+ if (x >= P) throw new MalformedDidKeyError(did, "x outside the field");
3359
+ const ySquared = (modPow(x, BigInt(3), P) + A * x + B) % P;
3360
+ let y = modPow(ySquared, (P + BigInt(1)) / BigInt(4), P);
3361
+ if (y * y % P !== ySquared) throw new MalformedDidKeyError(did, "x is not on the curve");
3362
+ if (y % BigInt(2) === BigInt(0) !== (prefix === 2)) y = P - y;
3363
+ const uncompressed = new Uint8Array(65);
3364
+ uncompressed[0] = 4;
3365
+ uncompressed.set(bigIntToBytes(x), 1);
3366
+ uncompressed.set(bigIntToBytes(y), 33);
3367
+ return uncompressed;
3368
+ }
3369
+ function modPow(base, exp, mod) {
3370
+ let result = BigInt(1);
3371
+ let b = base % mod;
3372
+ let e = exp;
3373
+ while (e > BigInt(0)) {
3374
+ if (e & BigInt(1)) result = result * b % mod;
3375
+ e >>= BigInt(1);
3376
+ b = b * b % mod;
3377
+ }
3378
+ return result;
3379
+ }
3380
+ function bigIntToBytes(value) {
3381
+ const bytes = new Uint8Array(32);
3382
+ let v = value;
3383
+ for (let i = 31; i >= 0; i--) {
3384
+ bytes[i] = Number(v & BigInt(255));
3385
+ v >>= BigInt(8);
3386
+ }
3387
+ return bytes;
3388
+ }
3389
+ //#endregion
3390
+ //#region src/signer/verify-action-signature.ts
3391
+ const RENOWN_HASH_LENGTH = 44;
3392
+ const SHARED_HASH_LENGTH = 28;
3393
+ const HEX_SIGNATURE = /^(0x)?([0-9a-fA-F]{2})+$/;
3394
+ /**
3395
+ * Integrity only: identity binding and the live-id check sit with the caller.
3396
+ * `operation` is the incoming operation at load admission.
3397
+ */
3398
+ async function verifyActionSignature(action, target, path, operation) {
3399
+ const policy = target.policy ?? "legacy";
3400
+ const refusal = policyRefusal(action, policy);
3401
+ if (refusal) return refusal;
3402
+ const shape = signerShape(action.context?.signer);
3403
+ if (shape.kind === "unsigned") return policy === "v2-required" ? refuse("unsigned", "UNSIGNED_REQUIRED", `action ${action.id} is unsigned but ${target.documentId} requires v2 signatures`) : {
3404
+ ok: true,
3405
+ scheme: "unsigned"
3406
+ };
3407
+ if (shape.kind === "malformed") return refuse("legacy-unknown", "MALFORMED_TUPLE", `action ${action.id} ${shape.reason}`);
3408
+ const { signer, tuple } = shape;
3409
+ const scheme = schemeOf(tuple[2]);
3410
+ if (tuple[1] !== signer.app.key) return refuse(scheme, "KEY_MISMATCH", `action ${action.id} tuple key does not match signer.app.key`);
3411
+ if (scheme !== "v2" && policy === "v2-required") return refuse(scheme, "SCHEME_BELOW_POLICY", `action ${action.id} carries a legacy tuple but ${target.documentId} requires v2 signatures`);
3412
+ if (scheme === "legacy-unknown" && path === "mutation") return refuse(scheme, "MALFORMED_TUPLE", `action ${action.id} carries a hash of unknown length ${tuple[2].length}`);
3413
+ if (scheme === "v2") {
3414
+ const refusal = await checkV2(action, tuple, target, operation);
3415
+ if (refusal) return refusal;
3416
+ } else if (path === "mutation") {
3417
+ if (await legacyHash(scheme, action, target) !== tuple[2]) return refuse(scheme, "HASH_MISMATCH", `action ${action.id} does not match the hash its signature covers`);
3418
+ }
3419
+ return verifyEcdsa(tuple, scheme, action.id);
3420
+ }
3421
+ /** Refusals a document's policy makes before any tuple is read. */
3422
+ function policyRefusal(action, policy) {
3423
+ if (action.type !== "CREATE_DOCUMENT") return;
3424
+ const scheme = schemeOfAction(action);
3425
+ const input = action.input;
3426
+ const documentId = input?.documentId ?? "";
3427
+ if (policy === "legacy") return isDerivedDocumentId(documentId) ? refuse(scheme, "ID_MISMATCH", `action ${action.id}: a legacy document cannot take the content-addressed id ${documentId}`) : void 0;
3428
+ const expected = derivedCreateId(input);
3429
+ if (expected === documentId) return;
3430
+ return refuse(scheme, "ID_MISMATCH", expected === void 0 ? `action ${action.id} creates v2-required ${documentId} without the header params its id is derived from` : `action ${action.id} creates v2-required ${documentId}, but its header params derive ${expected}`);
3431
+ }
3432
+ function derivedCreateId(input) {
3433
+ if (!input?.signing || !input.protocolVersions) return;
3434
+ try {
3435
+ return deriveDocumentId({
3436
+ documentType: input.model,
3437
+ createdAtUtcIso: input.signing.createdAtUtcIso,
3438
+ nonce: input.signing.nonce,
3439
+ protocolVersions: input.protocolVersions
3440
+ });
3441
+ } catch {
3442
+ return;
3443
+ }
3444
+ }
3445
+ function schemeOfAction(action) {
3446
+ const shape = signerShape(action.context?.signer);
3447
+ if (shape.kind === "unsigned") return "unsigned";
3448
+ return shape.kind === "signed" ? schemeOf(shape.tuple[2]) : "legacy-unknown";
3449
+ }
3450
+ /** Peers send arbitrary JSON, so nothing past `context` is assumed. */
3451
+ function signerShape(value) {
3452
+ if (value === void 0 || value === null) return { kind: "unsigned" };
3453
+ if (!isRecord(value)) return {
3454
+ kind: "malformed",
3455
+ reason: "has a signer that is not an object"
3456
+ };
3457
+ const { app, user, signatures } = value;
3458
+ if (app !== void 0 && app !== null && !isRecord(app)) return {
3459
+ kind: "malformed",
3460
+ reason: "has a signer.app that is not an object"
3461
+ };
3462
+ const key = isRecord(app) ? app.key : void 0;
3463
+ if (key !== void 0 && key !== null && typeof key !== "string") return {
3464
+ kind: "malformed",
3465
+ reason: "has a signer.app.key that is not a string"
3466
+ };
3467
+ if (!key) return { kind: "unsigned" };
3468
+ if (!isRecord(user) || typeof user.address !== "string") return {
3469
+ kind: "malformed",
3470
+ reason: "has a signer without a user address"
3471
+ };
3472
+ if (!Array.isArray(signatures)) return {
3473
+ kind: "malformed",
3474
+ reason: "has a signer whose signatures are not a list"
3475
+ };
3476
+ const tuple = signatures.at(-1);
3477
+ if (!isTuple(tuple)) return {
3478
+ kind: "malformed",
3479
+ reason: signatures.length === 0 ? "has a signer but no signatures" : "carries a malformed signature tuple"
3480
+ };
3481
+ return {
3482
+ kind: "signed",
3483
+ signer: value,
3484
+ tuple
3485
+ };
3486
+ }
3487
+ function isRecord(value) {
3488
+ return typeof value === "object" && value !== null && !Array.isArray(value);
3489
+ }
3490
+ async function checkV2(action, tuple, target, operation) {
3491
+ const problem = v2TupleProblem(tuple);
3492
+ if (problem) return refuse("v2", "MALFORMED_TUPLE", `action ${action.id}: ${problem}`);
3493
+ if (operation && !sameInstant(operation.timestampUtcMs, action.timestampUtcMs)) return refuse("v2", "TIMESTAMP_MISMATCH", `action ${action.id} is stamped ${action.timestampUtcMs} but its operation ${operation.timestampUtcMs}`);
3494
+ let expected;
3495
+ try {
3496
+ expected = await hashActionV2(action, target, action.context.signer);
3497
+ } catch (error) {
3498
+ return refuse("v2", "HASH_MISMATCH", `action ${action.id} has no v2 hash: ${error instanceof Error ? error.message : String(error)}`);
3499
+ }
3500
+ if (expected !== tuple[2]) return refuse("v2", "HASH_MISMATCH", `action ${action.id} does not match the hash its signature covers`);
3501
+ }
3502
+ function sameInstant(a, b) {
3503
+ const left = Date.parse(a);
3504
+ return !Number.isNaN(left) && left === Date.parse(b);
3505
+ }
3506
+ function schemeOf(hash) {
3507
+ if (isV2ActionHash(hash)) return "v2";
3508
+ if (hash.length === RENOWN_HASH_LENGTH) return "legacy-renown";
3509
+ if (hash.length === SHARED_HASH_LENGTH) return "legacy-shared";
3510
+ return "legacy-unknown";
3511
+ }
3512
+ async function legacyHash(scheme, action, target) {
3513
+ if (scheme === "legacy-renown") {
3514
+ const payload = [
3515
+ action.scope,
3516
+ action.type,
3517
+ JSON.stringify(action.input)
3518
+ ].join("");
3519
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(payload));
3520
+ return bytesToBase64(new Uint8Array(digest));
3521
+ }
3522
+ const signer = action.context.signer;
3523
+ return buildOperationSignatureParams({
3524
+ documentId: target.documentId,
3525
+ signer,
3526
+ action,
3527
+ previousStateHash: ""
3528
+ })[2];
3529
+ }
3530
+ async function verifyEcdsa(tuple, scheme, actionId) {
3531
+ const [timestamp, key, hash, prevStateHash, signatureHex] = tuple;
3532
+ if (!HEX_SIGNATURE.test(signatureHex)) return refuse(scheme, "MALFORMED_TUPLE", `action ${actionId} signature is not hex`);
3533
+ let cryptoKey;
3534
+ try {
3535
+ cryptoKey = await importDidKey(key);
3536
+ } catch (error) {
3537
+ return refuse(scheme, "MALFORMED_TUPLE", `action ${actionId}: ${error instanceof Error ? error.message : String(error)}`);
3538
+ }
3539
+ const message = buildOperationSignatureMessage([
3540
+ timestamp,
3541
+ key,
3542
+ hash,
3543
+ prevStateHash
3544
+ ]);
3545
+ let valid;
3546
+ try {
3547
+ valid = await crypto.subtle.verify({
3548
+ name: "ECDSA",
3549
+ hash: "SHA-256"
3550
+ }, cryptoKey, hexToBytes(signatureHex).buffer, message.buffer);
3551
+ } catch {
3552
+ valid = false;
3553
+ }
3554
+ if (!valid) return refuse(scheme, "BAD_SIGNATURE", `action ${actionId} signature does not verify under its key`);
3555
+ return {
3556
+ ok: true,
3557
+ scheme
3558
+ };
3559
+ }
3560
+ function isTuple(value) {
3561
+ return Array.isArray(value) && value.length === 5 && value.every((element) => typeof element === "string");
3562
+ }
3563
+ function refuse(scheme, code, reason) {
3564
+ return {
3565
+ ok: false,
3566
+ scheme,
3567
+ code,
3568
+ reason
3569
+ };
3570
+ }
3571
+ function hexToBytes(hex) {
3572
+ const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
3573
+ const bytes = new Uint8Array(clean.length / 2);
3574
+ for (let i = 0; i < bytes.length; i++) bytes[i] = parseInt(clean.substring(i * 2, i * 2 + 2), 16);
3575
+ return bytes;
3576
+ }
3577
+ function bytesToBase64(bytes) {
3578
+ let binary = "";
3579
+ for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
3580
+ return btoa(binary);
3581
+ }
3582
+ //#endregion
3583
+ //#region src/executor/synthesized-signing.ts
3584
+ /** Submitted types whose stored action the reducer writes in their place. */
3585
+ const SYNTHESIZING_TYPES = new Set(["UNDO", "REDO"]);
3586
+ /**
3587
+ * The id of the action synthesized from `submittedId`. Derived, so the stored
3588
+ * operation answers for the submitted id at the live-id check.
3589
+ */
3590
+ function synthesizedActionId(submittedId) {
3591
+ return hashBrowser(`synthesized:${submittedId}`, "sha1", "hex").slice(0, 32);
3592
+ }
3593
+ /**
3594
+ * Whether `operation` stores an action the reducer made from `submitted`
3595
+ * rather than `submitted` itself or an operation already in `history`.
3596
+ */
3597
+ function isSynthesized(submitted, operation, history) {
3598
+ if (!SYNTHESIZING_TYPES.has(submitted.type)) return false;
3599
+ const id = operation.action.id;
3600
+ if (!id) return true;
3601
+ if (id === submitted.id) return false;
3602
+ return !history.some((existing) => existing.action.id === id);
3603
+ }
3604
+ /**
3605
+ * Signs the synthesized action of `operation` in place as `signer`, under the
3606
+ * id {@link synthesizedActionId} derives from the submitted one. A REDO's
3607
+ * rebuilt action has no timestamp, so it takes the submitted one; the
3608
+ * operation timestamp is set to the action's, which a peer requires of a v2
3609
+ * tuple.
3610
+ */
3611
+ async function signSynthesized(operation, submitted, target, signer, signal) {
3612
+ const synthesized = operation.action;
3613
+ const id = synthesizedActionId(submitted.id);
3614
+ const timestampUtcMs = operation.timestampUtcMs || synthesized.timestampUtcMs || submitted.timestampUtcMs;
3615
+ const action = {
3616
+ id,
3617
+ timestampUtcMs,
3618
+ type: synthesized.type,
3619
+ input: synthesized.input,
3620
+ scope: synthesized.scope
3621
+ };
3622
+ const signature = await signer.signAction(action, {
3623
+ documentId: target.documentId,
3624
+ branch: target.branch
3625
+ }, signal);
3626
+ operation.action = {
3627
+ ...action,
3628
+ context: { signer: {
3629
+ ...actionSignerIdentity(signer),
3630
+ signatures: [signature]
3631
+ } }
3632
+ };
3633
+ operation.id = deriveOperationId(target.documentId, target.scope, target.branch, id);
3634
+ operation.timestampUtcMs = timestampUtcMs;
3635
+ }
3636
+ //#endregion
3637
+ //#region src/executor/signature-admission.ts
3638
+ const DEFAULT_TRUST_TIMEOUT_MS = 1e4;
3639
+ /** Runs once per write, when this reactor first stores it; never on re-appends. */
3640
+ var SignatureAdmission = class {
3641
+ trustPolicy;
3642
+ trustTimeoutMs;
3643
+ constructor(mode, logger, eventBus, policySource = "meta", trust = {
3644
+ authEnforcement: false,
3645
+ timeoutMs: DEFAULT_TRUST_TIMEOUT_MS
3646
+ }) {
3647
+ this.mode = mode;
3648
+ this.logger = logger;
3649
+ this.eventBus = eventBus;
3650
+ this.policySource = policySource;
3651
+ this.trustPolicy = admissionTrustPolicy(trust.signer ?? new PassthroughSigner(), trust.authEnforcement, trust.policy);
3652
+ this.trustTimeoutMs = trust.timeoutMs;
3653
+ }
3654
+ /** The first refusal of a mutation's submitted actions, when enforcing. */
3655
+ async admitMutation(job, stores, signal) {
3656
+ const { operationStore } = stores;
3657
+ const candidates = job.actions.map((action) => candidate(action, mutationStream(action, job)));
3658
+ const live = await this.liveOperationIds(candidates, operationStore, signal);
3659
+ const committed = await this.storedAsSubmitted(candidates, live, isRetry(job), operationStore, signal);
3660
+ await resolvePolicies(candidates, job.actions, (documentId, branch) => storedPolicy(stores, this.policySource, documentId, branch, signal));
3661
+ const submitted = /* @__PURE__ */ new Set();
3662
+ for (const entry of candidates) {
3663
+ if (committed.has(entry)) continue;
3664
+ const verdict = await this.verdict(entry, live, submitted, "mutation", signal);
3665
+ submitted.add(entry.opId);
3666
+ if (verdict.ok) continue;
3667
+ const refusal = this.record(job, entry, verdict, "mutation");
3668
+ if (refusal) return {
3669
+ kind: "refused",
3670
+ error: refusal
3671
+ };
3672
+ }
3673
+ return {
3674
+ kind: "admitted",
3675
+ committed: [...committed.values()]
3676
+ };
3677
+ }
3678
+ /** The incoming operations a load drops, empty unless enforcing. */
3679
+ async admitLoad(job, operations, stores, signal) {
3680
+ const { operationStore } = stores;
3681
+ const stream = {
3682
+ documentId: job.documentId,
3683
+ scope: job.scope,
3684
+ branch: job.branch
3685
+ };
3686
+ const candidates = operations.map((operation) => ({
3687
+ ...candidate(operation.action, stream),
3688
+ operation
3689
+ }));
3690
+ const live = await this.liveOperationIds(candidates, operationStore, signal);
3691
+ await resolvePolicies(candidates, job.operations.map((operation) => operation.action), (documentId, branch) => storedPolicy(stores, this.policySource, documentId, branch, signal));
3692
+ const dropped = /* @__PURE__ */ new Set();
3693
+ for (let i = 0; i < candidates.length; i++) {
3694
+ const verdict = await this.verdict(candidates[i], live, void 0, "load", signal);
3695
+ if (verdict.ok) continue;
3696
+ if (this.record(job, candidates[i], verdict, "load")) dropped.add(operations[i]);
3697
+ }
3698
+ return dropped;
3699
+ }
3700
+ async verdict(entry, live, submitted, path, signal) {
3701
+ const verdict = await verifyActionSignature(entry.action, {
3702
+ documentId: entry.stream.documentId,
3703
+ branch: entry.stream.branch,
3704
+ policy: entry.policy
3705
+ }, path, entry.operation);
3706
+ if (!verdict.ok) return verdict;
3707
+ if (isLive(entry, live) || submitted?.has(entry.opId)) return {
3708
+ ok: false,
3709
+ scheme: verdict.scheme,
3710
+ code: "DUPLICATE_ACTION",
3711
+ reason: `action ${entry.action.id} is already in the stream`
3712
+ };
3713
+ if (verdict.scheme === "unsigned") {
3714
+ const address = entry.action.context?.signer?.user?.address;
3715
+ return address ? {
3716
+ ok: false,
3717
+ scheme: "unsigned",
3718
+ code: "UNSIGNED_IDENTITY",
3719
+ reason: `action ${entry.action.id} is unsigned but claims to act as ${address}`
3720
+ } : verdict;
3721
+ }
3722
+ const signer = entry.action.context.signer;
3723
+ if (!await this.authorize(signer, entry.stream.documentId, signal)) return {
3724
+ ok: false,
3725
+ scheme: verdict.scheme,
3726
+ code: "SIGNER_UNAUTHORIZED",
3727
+ reason: `action ${entry.action.id}: key ${signer.app.key} may not sign as ${signer.user.address}`
3728
+ };
3729
+ return verdict;
3730
+ }
3731
+ /** Throws when the policy does, times out or the job is aborted. */
3732
+ async authorize(signer, documentId, signal) {
3733
+ signal?.throwIfAborted();
3734
+ let timer;
3735
+ let onAbort;
3736
+ const stop = new Promise((_, reject) => {
3737
+ timer = setTimeout(() => {
3738
+ reject(/* @__PURE__ */ new Error(`signature trust policy did not answer for ${signer.app.key} within ${this.trustTimeoutMs}ms`));
3739
+ }, this.trustTimeoutMs);
3740
+ if (signal) {
3741
+ onAbort = () => reject(signal.reason instanceof Error ? signal.reason : new Error(String(signal.reason)));
3742
+ signal.addEventListener("abort", onAbort, { once: true });
3299
3743
  }
3300
- if (!isValid) throw new InvalidSignatureError(documentId, `Action ${action.id} signature verification returned false`);
3744
+ });
3745
+ try {
3746
+ return await Promise.race([this.trustPolicy.authorizeSigner(signer, signer.app.key, documentId), stop]);
3747
+ } finally {
3748
+ clearTimeout(timer);
3749
+ if (onAbort) signal?.removeEventListener("abort", onAbort);
3301
3750
  }
3302
3751
  }
3303
- async verifyOperations(documentId, operations) {
3304
- if (!this.verifier) return;
3305
- for (let i = 0; i < operations.length; i++) {
3306
- const operation = operations[i];
3307
- const signer = operation.action.context?.signer;
3308
- if (!signer) continue;
3309
- if (signer.signatures.length === 0) throw new InvalidSignatureError(documentId, `Operation ${operation.id} at index ${operation.index} has signer but no signatures`);
3310
- const publicKey = signer.app.key;
3311
- let isValid;
3312
- try {
3313
- isValid = await this.verifier(operation, publicKey);
3314
- } catch (error) {
3315
- const errorMessage = error instanceof Error ? error.message : String(error);
3316
- throw new InvalidSignatureError(documentId, `Operation ${operation.id} at index ${operation.index} verification failed: ${errorMessage}`);
3752
+ /**
3753
+ * The live candidates stored exactly as submitted. A synthesized operation
3754
+ * holds another action, so it cannot be compared; only a retry, whose first
3755
+ * attempt this job was, takes it as committed.
3756
+ */
3757
+ async storedAsSubmitted(candidates, live, retry, operationStore, signal) {
3758
+ const committed = /* @__PURE__ */ new Map();
3759
+ const toCompare = [];
3760
+ for (const entry of candidates) if (entry.synthesizedOpId && live.has(entry.synthesizedOpId)) {
3761
+ if (retry) committed.set(entry, committedWrite(entry, entry.synthesizedOpId));
3762
+ } else if (live.has(entry.opId)) toCompare.push(entry);
3763
+ for (const [stream, entries] of byStream(toCompare)) {
3764
+ const stored = await operationStore.getOperationsByIds(stream.documentId, stream.scope, stream.branch, entries.map((entry) => entry.opId), signal);
3765
+ const byId = new Map(stored.map((operation) => [operation.id, operation]));
3766
+ for (const entry of entries) {
3767
+ const operation = byId.get(entry.opId);
3768
+ if (operation && sameContent(operation.action, entry.action)) committed.set(entry, committedWrite(entry, entry.opId));
3317
3769
  }
3318
- if (!isValid) throw new InvalidSignatureError(documentId, `Operation ${operation.id} at index ${operation.index} signature verification returned false`);
3319
3770
  }
3771
+ return committed;
3772
+ }
3773
+ async liveOperationIds(candidates, operationStore, signal) {
3774
+ const live = /* @__PURE__ */ new Set();
3775
+ for (const [stream, entries] of byStream(candidates)) {
3776
+ const found = await operationStore.findOperationIds(stream.documentId, stream.scope, stream.branch, entries.flatMap((entry) => entry.synthesizedOpId ? [entry.opId, entry.synthesizedOpId] : [entry.opId]), signal);
3777
+ for (const opId of found) live.add(opId);
3778
+ }
3779
+ return live;
3780
+ }
3781
+ /** Logs and counts a refusal; returns the error only when enforcing. */
3782
+ record(job, entry, refusal, path) {
3783
+ const enforced = this.mode === "enforce";
3784
+ const event = {
3785
+ jobId: job.id,
3786
+ documentId: entry.stream.documentId,
3787
+ scope: entry.stream.scope,
3788
+ branch: entry.stream.branch,
3789
+ actionId: entry.action.id,
3790
+ code: refusal.code,
3791
+ scheme: refusal.scheme,
3792
+ path,
3793
+ enforced,
3794
+ reason: refusal.reason
3795
+ };
3796
+ this.logger.warn(enforced ? "Signature refused: @Refusal" : "Signature refusal logged, write admitted: @Refusal", event);
3797
+ this.eventBus.emit(ReactorEventTypes.SIGNATURE_REFUSED, event).catch((error) => {
3798
+ this.logger.error("Failed to emit SIGNATURE_REFUSED: @Error", error);
3799
+ });
3800
+ return enforced ? new InvalidSignatureError(entry.stream.documentId, refusal.code, refusal.reason) : void 0;
3320
3801
  }
3321
3802
  };
3803
+ /**
3804
+ * A CREATE_DOCUMENT is verified under its own input. Anything else takes the
3805
+ * stored document's policy, or, for a document this job creates, that CREATE's.
3806
+ */
3807
+ async function resolvePolicies(candidates, jobActions, stored) {
3808
+ const resolved = /* @__PURE__ */ new Map();
3809
+ for (const entry of candidates) {
3810
+ if (entry.action.type === "CREATE_DOCUMENT") {
3811
+ entry.policy = createPolicy(entry.action);
3812
+ continue;
3813
+ }
3814
+ const { documentId, branch } = entry.stream;
3815
+ const key = `${documentId}\u0000${branch}`;
3816
+ let policy = resolved.get(key);
3817
+ if (policy === void 0) {
3818
+ policy = await stored(documentId, branch) ?? createdPolicy(jobActions, documentId) ?? "legacy";
3819
+ resolved.set(key, policy);
3820
+ }
3821
+ entry.policy = policy;
3822
+ }
3823
+ }
3824
+ async function storedPolicy(stores, source, documentId, branch, signal) {
3825
+ try {
3826
+ if (source === "write-cache") return signaturePolicyOf((await stores.writeCache.getState(documentId, "document", branch, void 0, signal)).header);
3827
+ return signaturePolicyOf((await stores.documentMetaCache.getDocumentMeta(documentId, branch, signal)).protocolVersions);
3828
+ } catch (error) {
3829
+ if (DocumentNotFoundError.isError(error)) return;
3830
+ throw error;
3831
+ }
3832
+ }
3833
+ function createdPolicy(actions, documentId) {
3834
+ const create = actions.find((action) => action.type === "CREATE_DOCUMENT" && action.input?.documentId === documentId);
3835
+ return create ? createPolicy(create) : void 0;
3836
+ }
3837
+ function createPolicy(action) {
3838
+ return signaturePolicyOf(action.input);
3839
+ }
3840
+ function candidate(action, stream) {
3841
+ const opId = (actionId) => deriveOperationId(stream.documentId, stream.scope, stream.branch, actionId);
3842
+ return {
3843
+ action,
3844
+ stream,
3845
+ opId: opId(action.id),
3846
+ synthesizedOpId: SYNTHESIZING_TYPES.has(action.type) ? opId(synthesizedActionId(action.id)) : void 0
3847
+ };
3848
+ }
3849
+ function isLive(entry, live) {
3850
+ return live.has(entry.opId) || entry.synthesizedOpId !== void 0 && live.has(entry.synthesizedOpId);
3851
+ }
3852
+ function mutationStream(action, job) {
3853
+ return {
3854
+ ...actionSigningTarget(action, job.documentId, job.branch),
3855
+ scope: job.scope
3856
+ };
3857
+ }
3858
+ function byStream(candidates) {
3859
+ const groups = /* @__PURE__ */ new Map();
3860
+ for (const entry of candidates) {
3861
+ const key = `${entry.stream.documentId}\u0000${entry.stream.scope}\u0000${entry.stream.branch}`;
3862
+ const group = groups.get(key) ?? [entry.stream, []];
3863
+ group[1].push(entry);
3864
+ groups.set(key, group);
3865
+ }
3866
+ return [...groups.values()];
3867
+ }
3868
+ function isRetry(job) {
3869
+ return job.errorHistory.length > 0 || (job.retryCount ?? 0) > 0;
3870
+ }
3871
+ function committedWrite(entry, opId) {
3872
+ return {
3873
+ ...entry.stream,
3874
+ actionId: entry.action.id,
3875
+ opId
3876
+ };
3877
+ }
3878
+ function sameContent(stored, submitted) {
3879
+ try {
3880
+ return canonicalJson(stored) === canonicalJson(submitted);
3881
+ } catch {
3882
+ return false;
3883
+ }
3884
+ }
3322
3885
  //#endregion
3323
3886
  //#region src/executor/simple-job-executor.ts
3324
3887
  const MAX_SKIP_THRESHOLD = 1e3;
@@ -3346,10 +3909,15 @@ var SimpleJobExecutor = class {
3346
3909
  config;
3347
3910
  featureFlags;
3348
3911
  decisionModel;
3349
- signatureVerifierModule;
3912
+ signatureAdmission;
3350
3913
  documentActionHandler;
3351
3914
  executionScope;
3352
- constructor(logger, registry, operationStore, eventBus, writeCache, operationIndex, documentMetaCache, collectionMembershipCache, driveContainerTypes, config, signatureVerifier, executionScope) {
3915
+ signer;
3916
+ /**
3917
+ * `signer` signs the operations the reducer synthesizes; unsigned if omitted.
3918
+ * `trustPolicy` decides which keys may sign as which users at admission.
3919
+ */
3920
+ constructor(logger, registry, operationStore, eventBus, writeCache, operationIndex, documentMetaCache, collectionMembershipCache, driveContainerTypes, config, executionScope, signer, trustPolicy) {
3353
3921
  this.logger = logger;
3354
3922
  this.registry = registry;
3355
3923
  this.operationStore = operationStore;
@@ -3359,6 +3927,7 @@ var SimpleJobExecutor = class {
3359
3927
  this.documentMetaCache = documentMetaCache;
3360
3928
  this.collectionMembershipCache = collectionMembershipCache;
3361
3929
  this.driveContainerTypes = driveContainerTypes;
3930
+ this.signer = signer ?? new PassthroughSigner();
3362
3931
  this.config = {
3363
3932
  featureFlags: config.featureFlags ?? {},
3364
3933
  maxSkipThreshold: config.maxSkipThreshold ?? MAX_SKIP_THRESHOLD,
@@ -3368,11 +3937,17 @@ var SimpleJobExecutor = class {
3368
3937
  retryBaseDelayMs: config.retryBaseDelayMs ?? 100,
3369
3938
  retryMaxDelayMs: config.retryMaxDelayMs ?? 5e3,
3370
3939
  yieldDeadlineMs: config.yieldDeadlineMs ?? 50,
3371
- batchApplies: config.batchApplies ?? true
3940
+ batchApplies: config.batchApplies ?? true,
3941
+ signatureVerification: config.signatureVerification ?? "enforce"
3372
3942
  };
3373
3943
  this.featureFlags = resolveFeatureFlags(config.featureFlags);
3374
3944
  this.decisionModel = selectDecisionModel(this.featureFlags, registry);
3375
- this.signatureVerifierModule = new SignatureVerifier(signatureVerifier);
3945
+ this.signatureAdmission = new SignatureAdmission(this.config.signatureVerification, logger, eventBus, this.featureFlags.documentDecisions ? "write-cache" : "meta", {
3946
+ signer: this.signer,
3947
+ authEnforcement: this.featureFlags.authEnforcement,
3948
+ policy: trustPolicy,
3949
+ timeoutMs: Math.min(DEFAULT_TRUST_TIMEOUT_MS, this.config.jobTimeoutMs / 2)
3950
+ });
3376
3951
  this.documentActionHandler = new DocumentActionHandler(registry, logger, driveContainerTypes, this.featureFlags, this.decisionModel);
3377
3952
  this.executionScope = executionScope ?? new DefaultExecutionScope(operationStore, operationIndex, writeCache, documentMetaCache, collectionMembershipCache);
3378
3953
  }
@@ -3502,10 +4077,29 @@ var SimpleJobExecutor = class {
3502
4077
  pendingEvent
3503
4078
  };
3504
4079
  }
3505
- const positioned = await this.positionByTimestamp(job, stores, signal);
4080
+ let admission;
4081
+ try {
4082
+ admission = await this.signatureAdmission.admitMutation(job, stores, signal);
4083
+ } catch (error) {
4084
+ return { result: buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime) };
4085
+ }
4086
+ if (admission.kind === "refused") return { result: buildErrorResult(job, admission.error, startTime) };
4087
+ let committed;
4088
+ try {
4089
+ committed = await this.reloadCommitted(admission.committed, stores, signal);
4090
+ } catch (error) {
4091
+ return { result: buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime) };
4092
+ }
4093
+ const committedIds = new Set(admission.committed.map((write) => write.actionId));
4094
+ const fresh = committedIds.size === 0 ? job : {
4095
+ ...job,
4096
+ actions: job.actions.filter((action) => !committedIds.has(action.id))
4097
+ };
4098
+ if (fresh.actions.length === 0) return this.committedOutcome(job, committed, [], [], stores, startTime);
4099
+ const positioned = await this.positionByTimestamp(fresh, stores, signal);
3506
4100
  if (positioned.error) return { result: buildErrorResult(job, positioned.error, startTime) };
3507
4101
  const executing = {
3508
- job,
4102
+ job: fresh,
3509
4103
  startTime,
3510
4104
  indexTxn,
3511
4105
  stores,
@@ -3535,12 +4129,21 @@ var SimpleJobExecutor = class {
3535
4129
  } };
3536
4130
  const ordinals = await stores.operationIndex.commit(indexTxn, signal);
3537
4131
  postCommitMembershipInvalidations.push(...indexTxn.getMembershipInvalidations());
3538
- if (actionResult.operationsWithContext.length > 0) {
3539
- for (let i = 0; i < actionResult.operationsWithContext.length; i++) actionResult.operationsWithContext[i].context.ordinal = ordinals[i];
3540
- const collectionMemberships = await this.getCollectionMembershipsForOperations(actionResult.operationsWithContext, stores);
4132
+ for (let i = 0; i < actionResult.operationsWithContext.length; i++) actionResult.operationsWithContext[i].context.ordinal = ordinals[i];
4133
+ return this.committedOutcome(job, committed, actionResult.operationsWithContext, actionResult.generatedOperations, stores, startTime);
4134
+ }
4135
+ /**
4136
+ * A successful mutation's result and the write-ready event it owes, carrying
4137
+ * what earlier attempts committed ahead of what this one wrote.
4138
+ */
4139
+ async committedOutcome(job, reloaded, written, generated, stores, startTime) {
4140
+ const operationsWithContext = [...reloaded, ...written];
4141
+ let pendingEvent;
4142
+ if (operationsWithContext.length > 0) {
4143
+ const collectionMemberships = await this.getCollectionMembershipsForOperations(operationsWithContext, stores);
3541
4144
  pendingEvent = {
3542
4145
  jobId: job.id,
3543
- operations: actionResult.operationsWithContext,
4146
+ operations: operationsWithContext,
3544
4147
  jobMeta: job.meta,
3545
4148
  submittedActionIds: submittedActionIds(job),
3546
4149
  collectionMemberships
@@ -3550,14 +4153,62 @@ var SimpleJobExecutor = class {
3550
4153
  result: {
3551
4154
  job,
3552
4155
  success: true,
3553
- operations: actionResult.generatedOperations,
3554
- operationsWithContext: actionResult.operationsWithContext,
4156
+ operations: [...reloaded.map((entry) => entry.operation), ...generated],
4157
+ operationsWithContext,
3555
4158
  duration: Date.now() - startTime
3556
4159
  },
3557
4160
  pendingEvent
3558
4161
  };
3559
4162
  }
3560
4163
  /**
4164
+ * The stored operations of writes an earlier attempt committed, as a fresh
4165
+ * write would report them. State is rebuilt from the write cache, and left
4166
+ * out when it cannot be.
4167
+ */
4168
+ async reloadCommitted(writes, stores, signal) {
4169
+ const streams = /* @__PURE__ */ new Map();
4170
+ for (const write of writes) {
4171
+ const key = `${write.documentId}\u0000${write.scope}\u0000${write.branch}`;
4172
+ streams.set(key, [...streams.get(key) ?? [], write]);
4173
+ }
4174
+ const reloaded = [];
4175
+ for (const group of streams.values()) {
4176
+ const { documentId, scope, branch } = group[0];
4177
+ const opIds = [...new Set(group.map((write) => write.opId))];
4178
+ const operations = await stores.operationStore.getOperationsByIds(documentId, scope, branch, opIds, signal);
4179
+ const ordinals = await stores.operationIndex.getOrdinalsByOpIds(documentId, scope, branch, opIds, signal);
4180
+ const { documentType } = await stores.documentMetaCache.getDocumentMeta(documentId, branch, signal);
4181
+ for (const operation of operations.sort((a, b) => a.index - b.index)) reloaded.push({
4182
+ operation,
4183
+ context: {
4184
+ documentId,
4185
+ scope,
4186
+ branch,
4187
+ documentType,
4188
+ resultingState: await this.rebuiltState(stores, {
4189
+ documentId,
4190
+ scope,
4191
+ branch
4192
+ }, operation, signal),
4193
+ ordinal: ordinals.get(operation.id) ?? 0
4194
+ }
4195
+ });
4196
+ }
4197
+ return reloaded;
4198
+ }
4199
+ async rebuiltState(stores, stream, operation, signal) {
4200
+ try {
4201
+ const document = await stores.writeCache.getState(stream.documentId, stream.scope, stream.branch, operation.index, signal);
4202
+ return JSON.stringify({
4203
+ ...document.state,
4204
+ header: document.header
4205
+ });
4206
+ } catch (error) {
4207
+ this.logger.warn("No state for committed operation @OperationId in @Stream: @Error", operation.id, stream, error);
4208
+ return;
4209
+ }
4210
+ }
4211
+ /**
3561
4212
  * Drops the cached state of every stream a job wrote, after its transaction
3562
4213
  * did not commit.
3563
4214
  *
@@ -3586,16 +4237,6 @@ var SimpleJobExecutor = class {
3586
4237
  const actions = writes.map((write) => write.action);
3587
4238
  const generatedOperations = [];
3588
4239
  const operationsWithContext = [];
3589
- try {
3590
- await this.signatureVerifierModule.verifyActions(job.documentId, job.branch, actions);
3591
- } catch (error) {
3592
- return {
3593
- success: false,
3594
- generatedOperations,
3595
- operationsWithContext,
3596
- error: error instanceof Error ? error : new Error(String(error))
3597
- };
3598
- }
3599
4240
  for (const action of actions) if (action.timestampUtcMs && !isValidISOTimestamp(action.timestampUtcMs)) return {
3600
4241
  success: false,
3601
4242
  generatedOperations,
@@ -3693,7 +4334,7 @@ var SimpleJobExecutor = class {
3693
4334
  if (docMeta.state.isDeleted) return buildErrorResult(job, new DocumentDeletedError(job.documentId, docMeta.state.deletedAtUtcIso), startTime);
3694
4335
  documentVersion = docMeta.state.version;
3695
4336
  }
3696
- if (isUndoRedo(action) || action.type === "PRUNE" || skip > 0) stores.writeCache.invalidate(job.documentId, job.scope, job.branch);
4337
+ if (isUndoRedo(action) || skip > 0) stores.writeCache.invalidate(job.documentId, job.scope, job.branch);
3697
4338
  let document;
3698
4339
  if (baseDocument !== void 0) document = baseDocument;
3699
4340
  else try {
@@ -3753,7 +4394,7 @@ var SimpleJobExecutor = class {
3753
4394
  branch: job.branch,
3754
4395
  protocolVersion
3755
4396
  };
3756
- updatedDocument = module.reducer(document, action, void 0, reducerOptions);
4397
+ updatedDocument = withProtocolVersions(module.reducer(document, action, void 0, reducerOptions), document.header.protocolVersions);
3757
4398
  } catch (error) {
3758
4399
  const contextMessage = `Failed to apply action to document:\n Action type: ${action.type}\n Document ID: ${job.documentId}\n Document type: ${document.header.documentType}\n Scope: ${job.scope}\n Original error: ${error instanceof Error ? error.message : String(error)}`;
3759
4400
  const enhancedError = new Error(contextMessage);
@@ -3765,6 +4406,15 @@ var SimpleJobExecutor = class {
3765
4406
  if (operations.length === 0) return buildErrorResult(job, /* @__PURE__ */ new Error("No operation generated from action"), startTime);
3766
4407
  const newOperation = operations[operations.length - 1];
3767
4408
  if (!isUndoRedo(action)) newOperation.skip = skip;
4409
+ if (deniedReason === void 0 && sourceOperation === void 0 && !executing.replayingAcceptedHistory && isSynthesized(action, newOperation, document.operations[scope] ?? [])) try {
4410
+ await signSynthesized(newOperation, action, {
4411
+ documentId: job.documentId,
4412
+ scope,
4413
+ branch: job.branch
4414
+ }, this.signer, signal);
4415
+ } catch (error) {
4416
+ return buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime);
4417
+ }
3768
4418
  const resultingState = JSON.stringify({
3769
4419
  ...updatedDocument.state,
3770
4420
  header: updatedDocument.header
@@ -3865,7 +4515,7 @@ var SimpleJobExecutor = class {
3865
4515
  * apply and its own reasons for it.
3866
4516
  * - A positional or replayed run carries skips and re-appended operations,
3867
4517
  * whose indices are not a simple ascending run from the head.
3868
- * - UNDO, REDO, PRUNE and NOOP-with-skip each invalidate the write cache to
4518
+ * - UNDO, REDO and NOOP-with-skip each invalidate the write cache to
3869
4519
  * force a full-history rebuild, so they cannot be reduced against state
3870
4520
  * threaded from the write before them.
3871
4521
  * - The auth scope decides later writes against the policy earlier ones
@@ -3883,7 +4533,7 @@ var SimpleJobExecutor = class {
3883
4533
  if (scope === "auth" || scope === "document") return false;
3884
4534
  return writes.every((write) => {
3885
4535
  const type = write.action.type;
3886
- return write.skip === 0 && write.deniedReason === void 0 && write.sourceOperation === void 0 && !DOCUMENT_SCOPE_ACTIONS.has(type) && !isUndoRedo(write.action) && type !== "PRUNE" && type !== "NOOP";
4536
+ return write.skip === 0 && write.deniedReason === void 0 && write.sourceOperation === void 0 && !DOCUMENT_SCOPE_ACTIONS.has(type) && !isUndoRedo(write.action) && type !== "NOOP";
3887
4537
  });
3888
4538
  }
3889
4539
  /**
@@ -4270,36 +4920,32 @@ var SimpleJobExecutor = class {
4270
4920
  error: new InvalidOperationTimestampError(job.documentId, scope, operation.timestampUtcMs, `operation (index: ${operation.index})`),
4271
4921
  duration: Date.now() - startTime
4272
4922
  };
4273
- let minIncomingIndex = Number.POSITIVE_INFINITY;
4274
- let minIncomingTimestamp = job.operations[0]?.timestampUtcMs || "";
4275
- for (const operation of job.operations) {
4276
- minIncomingIndex = Math.min(minIncomingIndex, operation.index);
4277
- const ts = operation.timestampUtcMs || "";
4278
- if (Date.parse(ts) < Date.parse(minIncomingTimestamp)) minIncomingTimestamp = ts;
4279
- }
4280
- let conflictingOps;
4281
- try {
4282
- conflictingOps = (await stores.operationStore.getConflicting(job.documentId, scope, job.branch, minIncomingTimestamp, void 0, signal)).results;
4283
- } catch {
4284
- conflictingOps = [];
4285
- }
4286
- let allOpsFromMinConflictingIndex = conflictingOps;
4287
- if (conflictingOps.length > 0) {
4288
- const minConflictingIndex = Math.min(...conflictingOps.map((op) => op.index));
4923
+ const dropped = /* @__PURE__ */ new Set();
4924
+ const admitted = /* @__PURE__ */ new Set();
4925
+ let selection = await this.selectLoadWrites(job, job.operations, stores, signal);
4926
+ for (;;) {
4927
+ const unadmitted = selection.incomingOpsToApply.filter((operation) => !admitted.has(operation));
4928
+ if (unadmitted.length === 0) break;
4929
+ let refused;
4289
4930
  try {
4290
- allOpsFromMinConflictingIndex = (await stores.operationStore.getSince(job.documentId, scope, job.branch, minConflictingIndex - 1, void 0, void 0, signal)).results;
4291
- } catch {
4292
- allOpsFromMinConflictingIndex = conflictingOps;
4931
+ refused = await this.signatureAdmission.admitLoad(job, unadmitted, stores, signal);
4932
+ } catch (error) {
4933
+ return buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime);
4293
4934
  }
4935
+ for (const operation of unadmitted) if (refused.has(operation)) dropped.add(operation);
4936
+ else admitted.add(operation);
4937
+ if (refused.size === 0) break;
4938
+ const remaining = job.operations.filter((operation) => !dropped.has(operation));
4939
+ if (remaining.length === 0) return {
4940
+ job,
4941
+ success: true,
4942
+ operations: [],
4943
+ operationsWithContext: [],
4944
+ duration: Date.now() - startTime
4945
+ };
4946
+ selection = await this.selectLoadWrites(job, remaining, stores, signal);
4294
4947
  }
4295
- const incomingActionIds = new Set(job.operations.map((op) => op.action.id));
4296
- const nonSupersededOps = conflictingOps.filter((op) => {
4297
- if (op.index < minIncomingIndex && !incomingActionIds.has(op.action.id)) return false;
4298
- for (const laterOp of allOpsFromMinConflictingIndex) if (laterOp.index > op.index && laterOp.skip > 0) {
4299
- if (laterOp.index - laterOp.skip <= op.index) return false;
4300
- }
4301
- return true;
4302
- });
4948
+ const { nonSupersededOps, allOpsFromMinConflictingIndex, incomingOpsToApply } = selection;
4303
4949
  const existingOpsToReshuffle = monotonicAuthStream ? [] : nonSupersededOps.filter((operation) => !isGenesisOperation(operation));
4304
4950
  const actionIdCounts = /* @__PURE__ */ new Map();
4305
4951
  for (const operation of allOpsFromMinConflictingIndex) actionIdCounts.set(operation.action.id, (actionIdCounts.get(operation.action.id) ?? 0) + 1);
@@ -4320,14 +4966,6 @@ var SimpleJobExecutor = class {
4320
4966
  const logicalSkip = latestRevision - minLogicalIndex;
4321
4967
  if (logicalSkip > skipCount) skipCount = logicalSkip;
4322
4968
  }
4323
- const existingActionIds = new Set(nonSupersededOps.map((op) => op.action.id));
4324
- const seenIncomingActionIds = /* @__PURE__ */ new Set();
4325
- const incomingOpsToApply = job.operations.filter((op) => {
4326
- if (existingActionIds.has(op.action.id)) return false;
4327
- if (seenIncomingActionIds.has(op.action.id)) return false;
4328
- seenIncomingActionIds.add(op.action.id);
4329
- return true;
4330
- });
4331
4969
  if (incomingOpsToApply.length === 0) return {
4332
4970
  job,
4333
4971
  success: true,
@@ -4407,6 +5045,53 @@ var SimpleJobExecutor = class {
4407
5045
  duration: Date.now() - startTime
4408
5046
  };
4409
5047
  }
5048
+ /** The conflicting window a load opens, and which of its operations are new. */
5049
+ async selectLoadWrites(job, operations, stores, signal) {
5050
+ const scope = job.scope;
5051
+ let minIncomingIndex = Number.POSITIVE_INFINITY;
5052
+ let minIncomingTimestamp = operations[0]?.timestampUtcMs || "";
5053
+ for (const operation of operations) {
5054
+ minIncomingIndex = Math.min(minIncomingIndex, operation.index);
5055
+ const ts = operation.timestampUtcMs || "";
5056
+ if (Date.parse(ts) < Date.parse(minIncomingTimestamp)) minIncomingTimestamp = ts;
5057
+ }
5058
+ let conflictingOps;
5059
+ try {
5060
+ conflictingOps = (await stores.operationStore.getConflicting(job.documentId, scope, job.branch, minIncomingTimestamp, void 0, signal)).results;
5061
+ } catch {
5062
+ conflictingOps = [];
5063
+ }
5064
+ let allOpsFromMinConflictingIndex = conflictingOps;
5065
+ if (conflictingOps.length > 0) {
5066
+ const minConflictingIndex = Math.min(...conflictingOps.map((op) => op.index));
5067
+ try {
5068
+ allOpsFromMinConflictingIndex = (await stores.operationStore.getSince(job.documentId, scope, job.branch, minConflictingIndex - 1, void 0, void 0, signal)).results;
5069
+ } catch {
5070
+ allOpsFromMinConflictingIndex = conflictingOps;
5071
+ }
5072
+ }
5073
+ const incomingActionIds = new Set(operations.map((op) => op.action.id));
5074
+ const nonSupersededOps = conflictingOps.filter((op) => {
5075
+ if (op.index < minIncomingIndex && !incomingActionIds.has(op.action.id)) return false;
5076
+ for (const laterOp of allOpsFromMinConflictingIndex) if (laterOp.index > op.index && laterOp.skip > 0) {
5077
+ if (laterOp.index - laterOp.skip <= op.index) return false;
5078
+ }
5079
+ return true;
5080
+ });
5081
+ const existingActionIds = new Set(nonSupersededOps.map((op) => op.action.id));
5082
+ const seenIncomingActionIds = /* @__PURE__ */ new Set();
5083
+ const incomingOpsToApply = operations.filter((op) => {
5084
+ if (existingActionIds.has(op.action.id)) return false;
5085
+ if (seenIncomingActionIds.has(op.action.id)) return false;
5086
+ seenIncomingActionIds.add(op.action.id);
5087
+ return true;
5088
+ });
5089
+ return {
5090
+ nonSupersededOps,
5091
+ allOpsFromMinConflictingIndex,
5092
+ incomingOpsToApply
5093
+ };
5094
+ }
4410
5095
  accumulateResultOrReturnError(result, generatedOperations, operationsWithContext) {
4411
5096
  if (!result.success) return result;
4412
5097
  if (result.operations && result.operations.length > 0) generatedOperations.push(...result.operations);
@@ -4939,6 +5624,20 @@ var KyselyOperationStore = class KyselyOperationStore {
4939
5624
  const latest = await this.queryExecutor.selectFrom("Operation").select((eb) => eb.fn.max("timestampUtcMs").as("latestTimestamp")).where("documentId", "=", documentId).where("scope", "=", scope).where("branch", "=", branch).executeTakeFirst();
4940
5625
  return latest?.latestTimestamp ? new Date(latest.latestTimestamp).toISOString() : void 0;
4941
5626
  }
5627
+ async findOperationIds(documentId, scope, branch, opIds, signal) {
5628
+ throwIfAborted(signal);
5629
+ if (opIds.length === 0) return /* @__PURE__ */ new Set();
5630
+ const rows = await this.queryExecutor.selectFrom("Operation").select("opId").distinct().where("opId", "in", opIds).where("documentId", "=", documentId).where("scope", "=", scope).where("branch", "=", branch).execute();
5631
+ return new Set(rows.map((row) => row.opId));
5632
+ }
5633
+ async getOperationsByIds(documentId, scope, branch, opIds, signal) {
5634
+ throwIfAborted(signal);
5635
+ if (opIds.length === 0) return [];
5636
+ const rows = await this.queryExecutor.selectFrom("Operation").selectAll().where("opId", "in", opIds).where("documentId", "=", documentId).where("scope", "=", scope).where("branch", "=", branch).orderBy("index", "asc").execute();
5637
+ const latest = /* @__PURE__ */ new Map();
5638
+ for (const row of rows) latest.set(row.opId, this.rowToOperation(row));
5639
+ return [...latest.values()];
5640
+ }
4942
5641
  rowToOperation(row) {
4943
5642
  return {
4944
5643
  index: row.index,
@@ -5412,6 +6111,6 @@ async function getMigrationStatus(db, schema = REACTOR_SCHEMA) {
5412
6111
  //#region src/core/drive-container-types.ts
5413
6112
  const DEFAULT_DRIVE_CONTAINER_TYPES = new Set(["powerhouse/document-drive", "powerhouse/reactor-drive"]);
5414
6113
  //#endregion
5415
- export { parsePagingOptions as $, targetDocumentId as A, DuplicateOperationError as B, InvalidModuleError as C, createEmptyConsistencyToken as D, createConsistencyToken as E, buildDecisionModel as F, AuthorizationDeniedError as G, RevisionMismatchError as H, APPEND_CONDITION_FAILED_PREFIX as I, ExcessiveReshuffleError as J, DocumentDeletedError as K, AppendConditionFailedError as L, selectDecisionModel as M, documentDecisionModel as N, submittedActionIds as O, authDecisionModel as P, matchesScope as Q, DocumentAlreadyExistsError as R, DuplicateModuleError as S, GATED_DOCUMENT_ACTIONS as T, AuthEnforcementDisabledError as U, OptimisticLockError as V, AuthTimestampNotMonotonicError as W, RelationshipNotFoundError as X, InvalidOperationTimestampError as Y, UpgradePreconditionFailedError as Z, DocumentMetaCache as _, createForwardingPoolInstrumentation as a, JobExecutorEventTypes as b, KyselyKeyframeStore as c, DriveCollectionId as d, throwIfAborted as et, KyselyExecutionScope as f, KyselyOperationIndex as g, KyselyWriteCache as h, runMigrations as i, decideAtHead as j, summarizeSubmittedActions as k, DocumentModelRegistry as l, EventBus as m, REACTOR_SCHEMA as n, __exportAll as nt, instrumentPgPool as o, resolveFeatureFlags as p, DocumentNotFoundError as q, getMigrationStatus as r, KyselyOperationStore as s, DEFAULT_DRIVE_CONTAINER_TYPES as t, yieldToMain as tt, SimpleJobExecutor as u, CollectionMembershipCache as v, ModuleNotFoundError as w, DuplicateManifestError as x, DEFAULT_DEFERRED_JOB_TTL_MS as y, DocumentExistence as z };
6114
+ export { RelationshipNotFoundError as $, submittedActionIds as A, DocumentAlreadyExistsError as B, DuplicateManifestError as C, GATED_DOCUMENT_ACTIONS as D, ModuleNotFoundError as E, documentDecisionModel as F, AuthEnforcementDisabledError as G, DuplicateOperationError as H, authDecisionModel as I, DocumentDeletedError as J, AuthTimestampNotMonotonicError as K, buildDecisionModel as L, targetDocumentId as M, decideAtHead as N, createConsistencyToken as O, selectDecisionModel as P, InvalidSignatureError as Q, APPEND_CONDITION_FAILED_PREFIX as R, JobExecutorEventTypes as S, InvalidModuleError as T, OptimisticLockError as U, DocumentExistence as V, RevisionMismatchError as W, ExcessiveReshuffleError as X, DocumentNotFoundError as Y, InvalidOperationTimestampError as Z, KyselyWriteCache as _, createForwardingPoolInstrumentation as a, __exportAll as at, CollectionMembershipCache as b, KyselyKeyframeStore as c, verifyActionSignature as d, UpgradePreconditionFailedError as et, PassthroughSigner as f, EventBus as g, resolveFeatureFlags as h, runMigrations as i, yieldToMain as it, summarizeSubmittedActions as j, createEmptyConsistencyToken as k, DocumentModelRegistry as l, KyselyExecutionScope as m, REACTOR_SCHEMA as n, parsePagingOptions as nt, instrumentPgPool as o, DriveCollectionId as p, AuthorizationDeniedError as q, getMigrationStatus as r, throwIfAborted as rt, KyselyOperationStore as s, DEFAULT_DRIVE_CONTAINER_TYPES as t, matchesScope as tt, SimpleJobExecutor as u, KyselyOperationIndex as v, DuplicateModuleError as w, DEFAULT_DEFERRED_JOB_TTL_MS as x, DocumentMetaCache as y, AppendConditionFailedError as z };
5416
6115
 
5417
- //# sourceMappingURL=drive-container-types-CS5IxDiA.js.map
6116
+ //# sourceMappingURL=drive-container-types-Be5-EY9W.js.map