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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
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, referencedGroupIds, sortOperations } from "@powerhousedao/shared/document-model";
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";
3
3
  import { v4 } from "uuid";
4
4
  import { Migrator, sql } from "kysely";
5
5
  //#region \0rolldown/runtime.js
@@ -250,6 +250,30 @@ var AuthEnforcementDisabledError = class AuthEnforcementDisabledError extends Er
250
250
  return Error.isError(error) && error.name === "AuthEnforcementDisabledError";
251
251
  }
252
252
  };
253
+ /**
254
+ * Error thrown when a relationship edge an operation names does not exist.
255
+ *
256
+ * Detection is by `name`, not `instanceof`: the SharedWorker RPC boundary
257
+ * rebuilds a thrown error from `{ name, message, stack, cause }` alone
258
+ * (`reactor-browser/src/rpc/error-info.ts`), so the class identity is lost in
259
+ * transit.
260
+ */
261
+ var RelationshipNotFoundError = class RelationshipNotFoundError extends Error {
262
+ sourceId;
263
+ targetId;
264
+ relationshipType;
265
+ constructor(sourceId, targetId, relationshipType) {
266
+ super(`No ${relationshipType} relationship from ${sourceId} to ${targetId}`);
267
+ this.name = "RelationshipNotFoundError";
268
+ this.sourceId = sourceId;
269
+ this.targetId = targetId;
270
+ this.relationshipType = relationshipType;
271
+ Error.captureStackTrace(this, RelationshipNotFoundError);
272
+ }
273
+ static isError(error) {
274
+ return Error.isError(error) && error.name === "RelationshipNotFoundError";
275
+ }
276
+ };
253
277
  //#endregion
254
278
  //#region src/storage/interfaces.ts
255
279
  /**
@@ -896,6 +920,40 @@ var TouchedStreams = class {
896
920
  return this.streams.values();
897
921
  }
898
922
  };
923
+ /**
924
+ * The ids of the actions the caller handed to a job. Load and reevaluation
925
+ * jobs write operations nobody submitted, so they report none.
926
+ */
927
+ function submittedActionIds(job) {
928
+ return job.kind === "mutation" ? job.actions.map((action) => action.id) : [];
929
+ }
930
+ /**
931
+ * Reports what became of each submitted action.
932
+ *
933
+ * A job's operations can include ones it only moved to a new index, so only
934
+ * those carrying a submitted action are reported. Returns undefined when the
935
+ * job submitted nothing, which keeps `JobInfo.result` null for the jobs that
936
+ * have no caller to answer to.
937
+ */
938
+ function summarizeSubmittedActions(operations, submitted) {
939
+ if (!submitted || submitted.length === 0) return;
940
+ const ids = new Set(submitted);
941
+ const actions = [];
942
+ for (const { operation, context } of operations) {
943
+ if (!ids.has(operation.action.id)) continue;
944
+ actions.push({
945
+ actionId: operation.action.id,
946
+ scope: context.scope,
947
+ index: operation.index,
948
+ ...operationOutcome(operation)
949
+ });
950
+ }
951
+ if (actions.length === 0) return;
952
+ return {
953
+ actions,
954
+ allApplied: actions.every((action) => action.kind === "applied")
955
+ };
956
+ }
899
957
  //#endregion
900
958
  //#region src/registry/errors.ts
901
959
  /**
@@ -1210,6 +1268,18 @@ var DocumentMetaCache = class DocumentMetaCache {
1210
1268
  };
1211
1269
  }
1212
1270
  };
1271
+ /** Bind slots per statement: 16-bit on the wire, read signed in-process. */
1272
+ const MAX_BIND_PARAMETERS = 32767;
1273
+ /** Splits rows into runs that each bind at most MAX_BIND_PARAMETERS. */
1274
+ function chunkRows(rows) {
1275
+ if (rows.length === 0) return [];
1276
+ const columnsPerRow = Math.max(1, Object.keys(rows[0]).length);
1277
+ const perChunk = Math.max(1, Math.floor(MAX_BIND_PARAMETERS / columnsPerRow));
1278
+ if (rows.length <= perChunk) return [rows];
1279
+ const chunks = [];
1280
+ for (let i = 0; i < rows.length; i += perChunk) chunks.push(rows.slice(i, i + perChunk));
1281
+ return chunks;
1282
+ }
1213
1283
  var KyselyOperationIndexTxn = class {
1214
1284
  collections = [];
1215
1285
  collectionMemberships = [];
@@ -1331,7 +1401,7 @@ var KyselyOperationIndex = class KyselyOperationIndex {
1331
1401
  leftOrdinal: null
1332
1402
  }));
1333
1403
  for (const collectionId of collections) kyselyTxn.recordMembershipInvalidation(collectionId);
1334
- await trx.insertInto("document_collections").values(collectionRows).onConflict((oc) => oc.doNothing()).execute();
1404
+ for (const chunk of chunkRows(collectionRows)) await trx.insertInto("document_collections").values(chunk).onConflict((oc) => oc.doNothing()).execute();
1335
1405
  }
1336
1406
  let operationOrdinals = [];
1337
1407
  if (operations.length > 0) {
@@ -1349,7 +1419,10 @@ var KyselyOperationIndex = class KyselyOperationIndex {
1349
1419
  deniedReason: op.deniedReason ?? null,
1350
1420
  sourceRemote: op.sourceRemote
1351
1421
  }));
1352
- operationOrdinals = (await trx.insertInto("operation_index_operations").values(operationRows).returning("ordinal").execute()).map((row) => row.ordinal);
1422
+ for (const chunk of chunkRows(operationRows)) {
1423
+ const insertedOps = await trx.insertInto("operation_index_operations").values(chunk).returning("ordinal").execute();
1424
+ operationOrdinals = operationOrdinals.concat(insertedOps.map((row) => row.ordinal));
1425
+ }
1353
1426
  }
1354
1427
  if (memberships.length > 0) for (const m of memberships) {
1355
1428
  const ordinal = operationOrdinals[m.operationIndex];
@@ -3469,6 +3542,7 @@ var SimpleJobExecutor = class {
3469
3542
  jobId: job.id,
3470
3543
  operations: actionResult.operationsWithContext,
3471
3544
  jobMeta: job.meta,
3545
+ submittedActionIds: submittedActionIds(job),
3472
3546
  collectionMemberships
3473
3547
  };
3474
3548
  }
@@ -3932,13 +4006,22 @@ var SimpleJobExecutor = class {
3932
4006
  }
3933
4007
  if (!backdated) return plain();
3934
4008
  const conflicting = (await stores.operationStore.getConflicting(job.documentId, job.scope, job.branch, earliest, void 0, signal)).results.filter((operation) => !isGenesisOperation(operation));
3935
- if (conflicting.length === 0) {
4009
+ const nothingToMove = async () => {
3936
4010
  if (!this.featureFlags.authEnforcement) return plain();
3937
4011
  return this.evaluatePositioned(job, stores, this.appendedOperations(job, revisions.revision[job.scope] ?? 0), signal);
3938
- }
4012
+ };
4013
+ if (conflicting.length === 0) return nothingToMove();
3939
4014
  const nextIndex = revisions.revision[job.scope] ?? 0;
3940
4015
  let firstConflicting = conflicting[0].index;
3941
4016
  for (const operation of conflicting) if (operation.index < firstConflicting) firstConflicting = operation.index;
4017
+ const stored = (await stores.operationStore.getSince(job.documentId, job.scope, job.branch, firstConflicting - 1, void 0, void 0, signal)).results;
4018
+ const conflictingIds = new Set(conflicting.map((operation) => operation.id));
4019
+ const effective = garbageCollect(sortOperations(stored)).filter((operation) => !isGenesisOperation(operation));
4020
+ const firstMoving = effective.findIndex((operation) => conflictingIds.has(operation.id));
4021
+ const moving = firstMoving === -1 ? [] : effective.slice(firstMoving);
4022
+ if (moving.length === 0) return nothingToMove();
4023
+ let firstRetracted = moving[0].index;
4024
+ for (const operation of moving) firstRetracted = Math.min(firstRetracted, operation.index - operation.skip);
3942
4025
  const incoming = job.actions.map((action, i) => ({
3943
4026
  id: action.id,
3944
4027
  index: nextIndex + i,
@@ -3949,8 +4032,8 @@ var SimpleJobExecutor = class {
3949
4032
  }));
3950
4033
  const merged = reshuffleByTimestamp({
3951
4034
  index: nextIndex,
3952
- skip: retractionSkip(nextIndex, firstConflicting)
3953
- }, conflicting, incoming);
4035
+ skip: retractionSkip(nextIndex, firstRetracted)
4036
+ }, moving, incoming);
3954
4037
  stores.writeCache.invalidate(job.documentId, job.scope, job.branch);
3955
4038
  if (!this.featureFlags.authEnforcement) return {
3956
4039
  writes: merged.map((operation) => ({
@@ -4779,6 +4862,14 @@ var KyselyOperationStore = class KyselyOperationStore {
4779
4862
  }
4780
4863
  return false;
4781
4864
  }
4865
+ /**
4866
+ * The paging cursor here encodes the index to resume from (one past the
4867
+ * last row returned), not the last row's own index. This keeps "0" an
4868
+ * unambiguous start-of-stream sentinel even when a page ends at index 0
4869
+ * (e.g. `limit: 1` on a fresh stream), which would otherwise make
4870
+ * `nextCursor` equal the start cursor and loop a caller that walks pages
4871
+ * forever.
4872
+ */
4782
4873
  async getSince(documentId, scope, branch, revision, filter, paging, signal) {
4783
4874
  throwIfAborted(signal);
4784
4875
  let query = this.queryExecutor.selectFrom("Operation").selectAll().where("documentId", "=", documentId).where("scope", "=", scope).where("branch", "=", branch).where("index", ">", revision).orderBy("index", "asc");
@@ -4793,10 +4884,10 @@ var KyselyOperationStore = class KyselyOperationStore {
4793
4884
  }
4794
4885
  if (paging) {
4795
4886
  const cursorValue = Number.parseInt(paging.cursor, 10);
4796
- if (cursorValue > 0) query = query.where("index", ">", cursorValue);
4887
+ if (Number.isFinite(cursorValue) && cursorValue > 0) query = query.where("index", ">=", cursorValue);
4797
4888
  if (paging.limit) query = query.limit(paging.limit + 1);
4798
4889
  }
4799
- return paginateRows(await query.execute(), paging, (row) => row.index, (row) => this.rowToOperation(row), (cursor, limit) => this.getSince(documentId, scope, branch, revision, filter, {
4890
+ return paginateRows(await query.execute(), paging, (row) => row.index + 1, (row) => this.rowToOperation(row), (cursor, limit) => this.getSince(documentId, scope, branch, revision, filter, {
4800
4891
  cursor,
4801
4892
  limit
4802
4893
  }, signal));
@@ -4814,26 +4905,28 @@ var KyselyOperationStore = class KyselyOperationStore {
4814
4905
  limit
4815
4906
  }, signal));
4816
4907
  }
4908
+ /**
4909
+ * The paging cursor here encodes the index to resume from (one past the
4910
+ * last row returned), not the last row's own index, for the same reason as
4911
+ * `getSince`: a page ending at index 0 must not produce a cursor that is
4912
+ * indistinguishable from the start-of-stream sentinel.
4913
+ */
4817
4914
  async getConflicting(documentId, scope, branch, minTimestamp, paging, signal) {
4818
4915
  throwIfAborted(signal);
4819
4916
  let query = this.queryExecutor.selectFrom("Operation").selectAll().where("documentId", "=", documentId).where("scope", "=", scope).where("branch", "=", branch).where("timestampUtcMs", ">=", new Date(minTimestamp)).orderBy("index", "asc");
4820
4917
  if (paging) {
4821
4918
  const cursorValue = Number.parseInt(paging.cursor, 10);
4822
- if (cursorValue > 0) query = query.where("index", ">", cursorValue);
4919
+ if (Number.isFinite(cursorValue) && cursorValue > 0) query = query.where("index", ">=", cursorValue);
4823
4920
  if (paging.limit) query = query.limit(paging.limit + 1);
4824
4921
  }
4825
- return paginateRows(await query.execute(), paging, (row) => row.index, (row) => this.rowToOperation(row), (cursor, limit) => this.getConflicting(documentId, scope, branch, minTimestamp, {
4922
+ return paginateRows(await query.execute(), paging, (row) => row.index + 1, (row) => this.rowToOperation(row), (cursor, limit) => this.getConflicting(documentId, scope, branch, minTimestamp, {
4826
4923
  cursor,
4827
4924
  limit
4828
4925
  }, signal));
4829
4926
  }
4830
4927
  async getRevisions(documentId, branch, signal) {
4831
4928
  throwIfAborted(signal);
4832
- const scopeRevisions = await this.queryExecutor.selectFrom("Operation as o1").select([
4833
- "o1.scope",
4834
- "o1.index",
4835
- "o1.timestampUtcMs"
4836
- ]).where("o1.documentId", "=", documentId).where("o1.branch", "=", branch).where((eb) => eb("o1.index", "=", eb.selectFrom("Operation as o2").select((eb2) => eb2.fn.max("o2.index").as("maxIndex")).where("o2.documentId", "=", eb.ref("o1.documentId")).where("o2.branch", "=", eb.ref("o1.branch")).where("o2.scope", "=", eb.ref("o1.scope")))).execute();
4929
+ const scopeRevisions = await this.queryExecutor.selectFrom("Operation").select("scope").select((eb) => eb.fn.max("index").as("index")).where("documentId", "=", documentId).where("branch", "=", branch).groupBy("scope").execute();
4837
4930
  const latest = await this.queryExecutor.selectFrom("Operation").select((eb) => eb.fn.max("timestampUtcMs").as("latestTimestamp")).where("documentId", "=", documentId).where("branch", "=", branch).executeTakeFirst();
4838
4931
  const revision = {};
4839
4932
  for (const row of scopeRevisions) revision[row.scope] = row.index + 1;
@@ -4939,8 +5032,8 @@ function createForwardingPoolInstrumentation(name) {
4939
5032
  }
4940
5033
  //#endregion
4941
5034
  //#region src/storage/migrations/001_create_operation_table.ts
4942
- var _001_create_operation_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$18 });
4943
- async function up$18(db) {
5035
+ var _001_create_operation_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$19 });
5036
+ async function up$19(db) {
4944
5037
  await db.schema.createTable("Operation").addColumn("id", "serial", (col) => col.primaryKey()).addColumn("jobId", "text", (col) => col.notNull()).addColumn("opId", "text", (col) => col.notNull()).addColumn("prevOpId", "text", (col) => col.notNull()).addColumn("writeTimestampUtcMs", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("documentId", "text", (col) => col.notNull()).addColumn("documentType", "text", (col) => col.notNull()).addColumn("scope", "text", (col) => col.notNull()).addColumn("branch", "text", (col) => col.notNull()).addColumn("timestampUtcMs", "timestamptz", (col) => col.notNull()).addColumn("index", "integer", (col) => col.notNull()).addColumn("action", "jsonb", (col) => col.notNull()).addColumn("skip", "integer", (col) => col.notNull()).addColumn("error", "text").addColumn("hash", "text", (col) => col.notNull()).addUniqueConstraint("unique_revision", [
4945
5038
  "documentId",
4946
5039
  "scope",
@@ -4965,8 +5058,8 @@ async function up$18(db) {
4965
5058
  }
4966
5059
  //#endregion
4967
5060
  //#region src/storage/migrations/002_create_keyframe_table.ts
4968
- var _002_create_keyframe_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$17 });
4969
- async function up$17(db) {
5061
+ var _002_create_keyframe_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$18 });
5062
+ async function up$18(db) {
4970
5063
  await db.schema.createTable("Keyframe").addColumn("id", "serial", (col) => col.primaryKey()).addColumn("documentId", "text", (col) => col.notNull()).addColumn("documentType", "text", (col) => col.notNull()).addColumn("scope", "text", (col) => col.notNull()).addColumn("branch", "text", (col) => col.notNull()).addColumn("revision", "integer", (col) => col.notNull()).addColumn("document", "jsonb", (col) => col.notNull()).addColumn("createdAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addUniqueConstraint("unique_keyframe", [
4971
5064
  "documentId",
4972
5065
  "scope",
@@ -4982,14 +5075,14 @@ async function up$17(db) {
4982
5075
  }
4983
5076
  //#endregion
4984
5077
  //#region src/storage/migrations/003_create_document_table.ts
4985
- var _003_create_document_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$16 });
4986
- async function up$16(db) {
5078
+ var _003_create_document_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$17 });
5079
+ async function up$17(db) {
4987
5080
  await db.schema.createTable("Document").addColumn("id", "text", (col) => col.primaryKey()).addColumn("createdAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("updatedAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
4988
5081
  }
4989
5082
  //#endregion
4990
5083
  //#region src/storage/migrations/004_create_document_relationship_table.ts
4991
- var _004_create_document_relationship_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$15 });
4992
- async function up$15(db) {
5084
+ var _004_create_document_relationship_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$16 });
5085
+ async function up$16(db) {
4993
5086
  await db.schema.createTable("DocumentRelationship").addColumn("id", "text", (col) => col.primaryKey()).addColumn("sourceId", "text", (col) => col.notNull().references("Document.id").onDelete("cascade")).addColumn("targetId", "text", (col) => col.notNull().references("Document.id").onDelete("cascade")).addColumn("relationshipType", "text", (col) => col.notNull()).addColumn("metadata", "jsonb").addColumn("createdAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("updatedAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addUniqueConstraint("unique_source_target_type", [
4994
5087
  "sourceId",
4995
5088
  "targetId",
@@ -5001,14 +5094,14 @@ async function up$15(db) {
5001
5094
  }
5002
5095
  //#endregion
5003
5096
  //#region src/storage/migrations/005_create_indexer_state_table.ts
5004
- var _005_create_indexer_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$14 });
5005
- async function up$14(db) {
5097
+ var _005_create_indexer_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$15 });
5098
+ async function up$15(db) {
5006
5099
  await db.schema.createTable("IndexerState").addColumn("id", "integer", (col) => col.primaryKey().generatedAlwaysAsIdentity()).addColumn("lastOperationId", "integer", (col) => col.notNull()).addColumn("lastOperationTimestamp", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
5007
5100
  }
5008
5101
  //#endregion
5009
5102
  //#region src/storage/migrations/006_create_document_snapshot_table.ts
5010
- var _006_create_document_snapshot_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$13 });
5011
- async function up$13(db) {
5103
+ var _006_create_document_snapshot_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$14 });
5104
+ async function up$14(db) {
5012
5105
  await db.schema.createTable("DocumentSnapshot").addColumn("id", "text", (col) => col.primaryKey()).addColumn("documentId", "text", (col) => col.notNull()).addColumn("slug", "text").addColumn("name", "text").addColumn("scope", "text", (col) => col.notNull()).addColumn("branch", "text", (col) => col.notNull()).addColumn("content", "jsonb", (col) => col.notNull()).addColumn("documentType", "text", (col) => col.notNull()).addColumn("lastOperationIndex", "integer", (col) => col.notNull()).addColumn("lastOperationHash", "text", (col) => col.notNull()).addColumn("lastUpdatedAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("snapshotVersion", "integer", (col) => col.notNull().defaultTo(1)).addColumn("identifiers", "jsonb").addColumn("metadata", "jsonb").addColumn("isDeleted", "boolean", (col) => col.notNull().defaultTo(false)).addColumn("deletedAt", "timestamptz").addUniqueConstraint("unique_doc_scope_branch", [
5013
5106
  "documentId",
5014
5107
  "scope",
@@ -5029,8 +5122,8 @@ async function up$13(db) {
5029
5122
  }
5030
5123
  //#endregion
5031
5124
  //#region src/storage/migrations/007_create_slug_mapping_table.ts
5032
- var _007_create_slug_mapping_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$12 });
5033
- async function up$12(db) {
5125
+ var _007_create_slug_mapping_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$13 });
5126
+ async function up$13(db) {
5034
5127
  await db.schema.createTable("SlugMapping").addColumn("slug", "text", (col) => col.primaryKey()).addColumn("documentId", "text", (col) => col.notNull()).addColumn("scope", "text", (col) => col.notNull()).addColumn("branch", "text", (col) => col.notNull()).addColumn("createdAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("updatedAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addUniqueConstraint("unique_docid_scope_branch", [
5035
5128
  "documentId",
5036
5129
  "scope",
@@ -5040,14 +5133,14 @@ async function up$12(db) {
5040
5133
  }
5041
5134
  //#endregion
5042
5135
  //#region src/storage/migrations/008_create_view_state_table.ts
5043
- var _008_create_view_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$11 });
5044
- async function up$11(db) {
5136
+ var _008_create_view_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$12 });
5137
+ async function up$12(db) {
5045
5138
  await db.schema.createTable("ViewState").addColumn("readModelId", "text", (col) => col.primaryKey()).addColumn("lastOrdinal", "integer", (col) => col.notNull().defaultTo(0)).addColumn("lastOperationTimestamp", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
5046
5139
  }
5047
5140
  //#endregion
5048
5141
  //#region src/storage/migrations/009_create_operation_index_tables.ts
5049
- var _009_create_operation_index_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$10 });
5050
- async function up$10(db) {
5142
+ var _009_create_operation_index_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$11 });
5143
+ async function up$11(db) {
5051
5144
  await db.schema.createTable("document_collections").addColumn("documentId", "text", (col) => col.notNull()).addColumn("collectionId", "text", (col) => col.notNull()).addColumn("joinedOrdinal", "bigint", (col) => col.notNull().defaultTo(0)).addColumn("leftOrdinal", "bigint").addPrimaryKeyConstraint("document_collections_pkey", ["documentId", "collectionId"]).execute();
5052
5145
  await db.schema.createIndex("idx_document_collections_collectionId").on("document_collections").column("collectionId").execute();
5053
5146
  await db.schema.createIndex("idx_doc_collections_collection_range").on("document_collections").columns(["collectionId", "joinedOrdinal"]).execute();
@@ -5061,8 +5154,8 @@ async function up$10(db) {
5061
5154
  }
5062
5155
  //#endregion
5063
5156
  //#region src/storage/migrations/010_create_sync_tables.ts
5064
- var _010_create_sync_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$9 });
5065
- async function up$9(db) {
5157
+ var _010_create_sync_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$10 });
5158
+ async function up$10(db) {
5066
5159
  await db.schema.createTable("sync_remotes").addColumn("name", "text", (col) => col.primaryKey()).addColumn("collection_id", "text", (col) => col.notNull()).addColumn("channel_type", "text", (col) => col.notNull()).addColumn("channel_id", "text", (col) => col.notNull().defaultTo("")).addColumn("remote_name", "text", (col) => col.notNull().defaultTo("")).addColumn("channel_parameters", "jsonb", (col) => col.notNull().defaultTo(sql`'{}'::jsonb`)).addColumn("filter_document_ids", "jsonb").addColumn("filter_scopes", "jsonb").addColumn("filter_branch", "text", (col) => col.notNull().defaultTo("main")).addColumn("push_state", "text", (col) => col.notNull().defaultTo("idle")).addColumn("push_last_success_utc_ms", "text").addColumn("push_last_failure_utc_ms", "text").addColumn("push_failure_count", "integer", (col) => col.notNull().defaultTo(0)).addColumn("pull_state", "text", (col) => col.notNull().defaultTo("idle")).addColumn("pull_last_success_utc_ms", "text").addColumn("pull_last_failure_utc_ms", "text").addColumn("pull_failure_count", "integer", (col) => col.notNull().defaultTo(0)).addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("updated_at", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
5067
5160
  await db.schema.createIndex("idx_sync_remotes_collection").on("sync_remotes").column("collection_id").execute();
5068
5161
  await db.schema.createTable("sync_cursors").addColumn("remote_name", "text", (col) => col.primaryKey().references("sync_remotes.name").onDelete("cascade")).addColumn("cursor_ordinal", "bigint", (col) => col.notNull().defaultTo(0)).addColumn("last_synced_at_utc_ms", "text").addColumn("updated_at", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
@@ -5070,8 +5163,8 @@ async function up$9(db) {
5070
5163
  }
5071
5164
  //#endregion
5072
5165
  //#region src/storage/migrations/011_add_cursor_type_column.ts
5073
- var _011_add_cursor_type_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$8 });
5074
- async function up$8(db) {
5166
+ var _011_add_cursor_type_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$9 });
5167
+ async function up$9(db) {
5075
5168
  await db.deleteFrom("sync_cursors").where("remote_name", "like", "outbox::%").execute();
5076
5169
  await db.deleteFrom("sync_remotes").where("name", "like", "outbox::%").execute();
5077
5170
  await db.schema.dropTable("sync_cursors").execute();
@@ -5080,64 +5173,64 @@ async function up$8(db) {
5080
5173
  }
5081
5174
  //#endregion
5082
5175
  //#region src/storage/migrations/012_add_source_remote_column.ts
5083
- var _012_add_source_remote_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$7 });
5084
- async function up$7(db) {
5176
+ var _012_add_source_remote_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$8 });
5177
+ async function up$8(db) {
5085
5178
  await db.schema.alterTable("operation_index_operations").addColumn("sourceRemote", "text", (col) => col.notNull().defaultTo("")).execute();
5086
5179
  }
5087
5180
  //#endregion
5088
5181
  //#region src/storage/migrations/013_create_sync_dead_letters_table.ts
5089
- var _013_create_sync_dead_letters_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$6 });
5090
- async function up$6(db) {
5182
+ var _013_create_sync_dead_letters_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$7 });
5183
+ async function up$7(db) {
5091
5184
  await db.schema.createTable("sync_dead_letters").addColumn("ordinal", "serial", (col) => col.primaryKey()).addColumn("id", "text", (col) => col.unique().notNull()).addColumn("job_id", "text", (col) => col.notNull()).addColumn("job_dependencies", "jsonb", (col) => col.notNull().defaultTo(sql`'[]'::jsonb`)).addColumn("remote_name", "text", (col) => col.notNull().references("sync_remotes.name").onDelete("cascade")).addColumn("document_id", "text", (col) => col.notNull()).addColumn("scopes", "jsonb", (col) => col.notNull().defaultTo(sql`'[]'::jsonb`)).addColumn("branch", "text", (col) => col.notNull()).addColumn("operations", "jsonb", (col) => col.notNull().defaultTo(sql`'[]'::jsonb`)).addColumn("error_source", "text", (col) => col.notNull()).addColumn("error_message", "text", (col) => col.notNull()).addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
5092
5185
  await db.schema.createIndex("idx_sync_dead_letters_remote").on("sync_dead_letters").column("remote_name").execute();
5093
5186
  }
5094
5187
  //#endregion
5095
5188
  //#region src/storage/migrations/014_create_processor_cursor_table.ts
5096
- var _014_create_processor_cursor_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$5 });
5097
- async function up$5(db) {
5189
+ var _014_create_processor_cursor_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$6 });
5190
+ async function up$6(db) {
5098
5191
  await db.schema.createTable("ProcessorCursor").addColumn("processorId", "text", (col) => col.primaryKey()).addColumn("factoryId", "text", (col) => col.notNull()).addColumn("driveId", "text", (col) => col.notNull()).addColumn("processorIndex", "integer", (col) => col.notNull()).addColumn("lastOrdinal", "integer", (col) => col.notNull().defaultTo(sql`0`)).addColumn("status", "text", (col) => col.notNull().defaultTo(sql`'active'`)).addColumn("lastError", "text").addColumn("lastErrorTimestamp", "timestamptz").addColumn("createdAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("updatedAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
5099
5192
  }
5100
5193
  //#endregion
5101
5194
  //#region src/storage/migrations/015_add_operation_denied_reason.ts
5102
5195
  var _015_add_operation_denied_reason_exports = /* @__PURE__ */ __exportAll({
5103
- down: () => down$4,
5104
- up: () => up$4
5196
+ down: () => down$5,
5197
+ up: () => up$5
5105
5198
  });
5106
5199
  /**
5107
5200
  * Records why authorization refused an operation. Separate from `error` so a
5108
5201
  * denial is distinguishable from a reducer failure without matching on a
5109
5202
  * message. Null for every operation written before decisions were enforced.
5110
5203
  */
5111
- async function up$4(db) {
5204
+ async function up$5(db) {
5112
5205
  await db.schema.alterTable("Operation").addColumn("deniedReason", "text").execute();
5113
5206
  await db.schema.alterTable("operation_index_operations").addColumn("deniedReason", "text").execute();
5114
5207
  }
5115
- async function down$4(db) {
5208
+ async function down$5(db) {
5116
5209
  await db.schema.alterTable("operation_index_operations").dropColumn("deniedReason").execute();
5117
5210
  await db.schema.alterTable("Operation").dropColumn("deniedReason").execute();
5118
5211
  }
5119
5212
  //#endregion
5120
5213
  //#region src/storage/migrations/016_add_dead_letter_error_type.ts
5121
5214
  var _016_add_dead_letter_error_type_exports = /* @__PURE__ */ __exportAll({
5122
- down: () => down$3,
5123
- up: () => up$3
5215
+ down: () => down$4,
5216
+ up: () => up$4
5124
5217
  });
5125
5218
  /**
5126
5219
  * The classification a dead letter falls into, stored because it decides whether
5127
5220
  * the document stays quarantined and the in-memory error is gone after a restart.
5128
5221
  * Defaulted rather than nullable, so a pre-existing row rehydrates.
5129
5222
  */
5130
- async function up$3(db) {
5223
+ async function up$4(db) {
5131
5224
  await db.schema.alterTable("sync_dead_letters").addColumn("error_type", "text", (col) => col.notNull().defaultTo("UNCLASSIFIED")).execute();
5132
5225
  }
5133
- async function down$3(db) {
5226
+ async function down$4(db) {
5134
5227
  await db.schema.alterTable("sync_dead_letters").dropColumn("error_type").execute();
5135
5228
  }
5136
5229
  //#endregion
5137
5230
  //#region src/storage/migrations/017_create_group_references.ts
5138
5231
  var _017_create_group_references_exports = /* @__PURE__ */ __exportAll({
5139
- down: () => down$2,
5140
- up: () => up$2
5232
+ down: () => down$3,
5233
+ up: () => up$3
5141
5234
  });
5142
5235
  /**
5143
5236
  * One row per (document, group) reference ever discovered from an auth
@@ -5148,18 +5241,18 @@ var _017_create_group_references_exports = /* @__PURE__ */ __exportAll({
5148
5241
  * requires (sync), and by groupId for the documents a group change affects
5149
5242
  * (re-evaluation).
5150
5243
  */
5151
- async function up$2(db) {
5244
+ async function up$3(db) {
5152
5245
  await db.schema.createTable("group_references").addColumn("documentId", "text", (col) => col.notNull()).addColumn("groupId", "text", (col) => col.notNull()).addPrimaryKeyConstraint("group_references_pkey", ["documentId", "groupId"]).execute();
5153
5246
  await db.schema.createIndex("idx_group_references_groupId").on("group_references").column("groupId").execute();
5154
5247
  }
5155
- async function down$2(db) {
5248
+ async function down$3(db) {
5156
5249
  await db.schema.dropTable("group_references").execute();
5157
5250
  }
5158
5251
  //#endregion
5159
5252
  //#region src/storage/migrations/018_add_sync_remote_bound_address.ts
5160
5253
  var _018_add_sync_remote_bound_address_exports = /* @__PURE__ */ __exportAll({
5161
- down: () => down$1,
5162
- up: () => up$1
5254
+ down: () => down$2,
5255
+ up: () => up$2
5163
5256
  });
5164
5257
  /**
5165
5258
  * The address a sync channel is bound to, so a channel created by one subject
@@ -5170,17 +5263,17 @@ var _018_add_sync_remote_bound_address_exports = /* @__PURE__ */ __exportAll({
5170
5263
  * until its first authenticated poll adopts it. A default would claim them all
5171
5264
  * for one address.
5172
5265
  */
5173
- async function up$1(db) {
5266
+ async function up$2(db) {
5174
5267
  await db.schema.alterTable("sync_remotes").addColumn("bound_address", "text").execute();
5175
5268
  }
5176
- async function down$1(db) {
5269
+ async function down$2(db) {
5177
5270
  await db.schema.alterTable("sync_remotes").dropColumn("bound_address").execute();
5178
5271
  }
5179
5272
  //#endregion
5180
5273
  //#region src/storage/migrations/019_require_action_id.ts
5181
5274
  var _019_require_action_id_exports = /* @__PURE__ */ __exportAll({
5182
- down: () => down,
5183
- up: () => up
5275
+ down: () => down$1,
5276
+ up: () => up$1
5184
5277
  });
5185
5278
  /**
5186
5279
  * Makes an operation whose action carries no id physically unstorable.
@@ -5207,7 +5300,7 @@ var _019_require_action_id_exports = /* @__PURE__ */ __exportAll({
5207
5300
  * The empty string is rejected alongside null. It derives the same colliding
5208
5301
  * operation id as an absent id, so admitting it would leave the hole open.
5209
5302
  */
5210
- async function up(db) {
5303
+ async function up$1(db) {
5211
5304
  await db.updateTable("Operation").set({ action: sql`jsonb_set(action, '{id}', to_jsonb(gen_random_uuid()::text))` }).where(sql`jsonb_typeof(action) = 'object' and coalesce(action->>'id', '') = ''`).execute();
5212
5305
  await db.updateTable("operation_index_operations as oio").from("Operation as op").set({ action: sql`jsonb_set(oio.action, '{id}', to_jsonb(op.action->>'id'))` }).whereRef("oio.opId", "=", "op.opId").whereRef("oio.index", "=", "op.index").whereRef("oio.skip", "=", "op.skip").where(sql`jsonb_typeof(oio.action) = 'object' and coalesce(oio.action->>'id', '') = ''`).where(sql`coalesce(op.action->>'id', '') <> ''`).execute();
5213
5306
  await db.updateTable("operation_index_operations").set({ action: sql`jsonb_set(action, '{id}', to_jsonb(gen_random_uuid()::text))` }).where(sql`jsonb_typeof(action) = 'object' and coalesce(action->>'id', '') = ''`).execute();
@@ -5219,11 +5312,24 @@ async function up(db) {
5219
5312
  * their operations are now known by, and reverting them would reintroduce the
5220
5313
  * collision the migration removed.
5221
5314
  */
5222
- async function down(db) {
5315
+ async function down$1(db) {
5223
5316
  await db.schema.alterTable("operation_index_operations").dropConstraint("action_must_have_id").execute();
5224
5317
  await db.schema.alterTable("Operation").dropConstraint("action_must_have_id").execute();
5225
5318
  }
5226
5319
  //#endregion
5320
+ //#region src/storage/migrations/020_add_snapshot_operation_ordinal.ts
5321
+ var _020_add_snapshot_operation_ordinal_exports = /* @__PURE__ */ __exportAll({
5322
+ down: () => down,
5323
+ up: () => up
5324
+ });
5325
+ /** Orders header row writes across scopes; `lastOperationIndex` is per scope. */
5326
+ async function up(db) {
5327
+ await db.schema.alterTable("DocumentSnapshot").addColumn("lastOperationOrdinal", "integer", (col) => col.notNull().defaultTo(0)).execute();
5328
+ }
5329
+ async function down(db) {
5330
+ await db.schema.alterTable("DocumentSnapshot").dropColumn("lastOperationOrdinal").execute();
5331
+ }
5332
+ //#endregion
5227
5333
  //#region src/storage/migrations/migrator.ts
5228
5334
  const REACTOR_SCHEMA = "reactor";
5229
5335
  const migrations = {
@@ -5245,7 +5351,8 @@ const migrations = {
5245
5351
  "016_add_dead_letter_error_type": _016_add_dead_letter_error_type_exports,
5246
5352
  "017_create_group_references": _017_create_group_references_exports,
5247
5353
  "018_add_sync_remote_bound_address": _018_add_sync_remote_bound_address_exports,
5248
- "019_require_action_id": _019_require_action_id_exports
5354
+ "019_require_action_id": _019_require_action_id_exports,
5355
+ "020_add_snapshot_operation_ordinal": _020_add_snapshot_operation_ordinal_exports
5249
5356
  };
5250
5357
  var ProgrammaticMigrationProvider = class {
5251
5358
  getMigrations() {
@@ -5305,6 +5412,6 @@ async function getMigrationStatus(db, schema = REACTOR_SCHEMA) {
5305
5412
  //#region src/core/drive-container-types.ts
5306
5413
  const DEFAULT_DRIVE_CONTAINER_TYPES = new Set(["powerhouse/document-drive", "powerhouse/reactor-drive"]);
5307
5414
  //#endregion
5308
- export { selectDecisionModel as A, RevisionMismatchError as B, InvalidModuleError as C, createEmptyConsistencyToken as D, createConsistencyToken as E, AppendConditionFailedError as F, DocumentNotFoundError as G, AuthTimestampNotMonotonicError as H, DocumentAlreadyExistsError as I, UpgradePreconditionFailedError as J, ExcessiveReshuffleError as K, DocumentExistence as L, authDecisionModel as M, buildDecisionModel as N, targetDocumentId as O, APPEND_CONDITION_FAILED_PREFIX as P, __exportAll as Q, DuplicateOperationError as R, DuplicateModuleError as S, GATED_DOCUMENT_ACTIONS as T, AuthorizationDeniedError as U, AuthEnforcementDisabledError as V, DocumentDeletedError as W, parsePagingOptions as X, matchesScope as Y, throwIfAborted as Z, DocumentMetaCache as _, createForwardingPoolInstrumentation as a, JobExecutorEventTypes as b, KyselyKeyframeStore as c, DriveCollectionId as d, KyselyExecutionScope as f, KyselyOperationIndex as g, KyselyWriteCache as h, runMigrations as i, documentDecisionModel as j, decideAtHead as k, DocumentModelRegistry as l, EventBus as m, REACTOR_SCHEMA as n, instrumentPgPool as o, resolveFeatureFlags as p, InvalidOperationTimestampError as q, getMigrationStatus as r, KyselyOperationStore as s, DEFAULT_DRIVE_CONTAINER_TYPES as t, SimpleJobExecutor as u, CollectionMembershipCache as v, ModuleNotFoundError as w, DuplicateManifestError as x, DEFAULT_DEFERRED_JOB_TTL_MS as y, OptimisticLockError as z };
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 };
5309
5416
 
5310
- //# sourceMappingURL=drive-container-types-yZrksiJR.js.map
5417
+ //# sourceMappingURL=drive-container-types-CS5IxDiA.js.map