@powerhousedao/reactor 6.2.3-dev.31 → 6.2.3-dev.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{build-worker-executor-Bvoba-AV.js → build-worker-executor-BonPsKdx.js} +2 -2
- package/dist/{build-worker-executor-Bvoba-AV.js.map → build-worker-executor-BonPsKdx.js.map} +1 -1
- package/dist/{document-indexer-BVTGbEB0.js → document-indexer-I6i_YzsR.js} +759 -130
- package/dist/document-indexer-I6i_YzsR.js.map +1 -0
- package/dist/entry.js +2 -2
- package/dist/{forwarding-logger-CnuVDVze.js → forwarding-logger-BkwlvD9W.js} +484 -110
- package/dist/forwarding-logger-BkwlvD9W.js.map +1 -0
- package/dist/index.d.ts +2318 -1790
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1122 -140
- package/dist/index.js.map +1 -1
- package/dist/projection-entry.js +54 -6
- package/dist/projection-entry.js.map +1 -1
- package/dist/{projection-shard-manager-BpcqDtnQ.js → projection-shard-manager-CJIdjsd8.js} +23 -3
- package/dist/projection-shard-manager-CJIdjsd8.js.map +1 -0
- package/dist/{worker-handle-C2rLUHwv.js → worker-handle-DjbEQkq-.js} +2 -2
- package/dist/{worker-handle-C2rLUHwv.js.map → worker-handle-DjbEQkq-.js.map} +1 -1
- package/package.json +5 -4
- package/dist/document-indexer-BVTGbEB0.js.map +0 -1
- package/dist/forwarding-logger-CnuVDVze.js.map +0 -1
- package/dist/projection-shard-manager-BpcqDtnQ.js.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
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";
|
|
1
|
+
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, PEER_CAPABILITIES, 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, localSupports, mentionedGroupIds, normalizeDocumentModelVersion, operationOutcome, referencedGroupIds, signaturePolicyOf, sortOperations, targetDocumentId, v2TupleProblem, withProtocolVersions } from "@powerhousedao/shared/document-model";
|
|
2
2
|
import { v4 } from "uuid";
|
|
3
|
-
import { Migrator, sql } from "kysely";
|
|
3
|
+
import { Migrator, TableNode, sql } from "kysely";
|
|
4
4
|
//#region \0rolldown/runtime.js
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __exportAll = (all, no_symbols) => {
|
|
@@ -79,7 +79,8 @@ const ReactorEventTypes = {
|
|
|
79
79
|
READMODEL_BATCH_COMPLETED: 10006,
|
|
80
80
|
READMODEL_INDEXED: 10007,
|
|
81
81
|
MODEL_LOADED: 10008,
|
|
82
|
-
SIGNATURE_REFUSED: 10009
|
|
82
|
+
SIGNATURE_REFUSED: 10009,
|
|
83
|
+
CATCHUP_SWEPT: 10010
|
|
83
84
|
};
|
|
84
85
|
//#endregion
|
|
85
86
|
//#region src/shared/errors.ts
|
|
@@ -173,6 +174,23 @@ var InvalidOperationTimestampError = class InvalidOperationTimestampError extend
|
|
|
173
174
|
return Error.isError(error) && error.name === "InvalidOperationTimestampError";
|
|
174
175
|
}
|
|
175
176
|
};
|
|
177
|
+
/** A document requires a protocol version this reactor does not run. Terminal. */
|
|
178
|
+
var UnsupportedProtocolVersionError = class UnsupportedProtocolVersionError extends Error {
|
|
179
|
+
documentId;
|
|
180
|
+
protocol;
|
|
181
|
+
version;
|
|
182
|
+
constructor(documentId, protocol, version) {
|
|
183
|
+
super(`Document ${documentId} requires ${protocol} ${version}, which this reactor does not support`);
|
|
184
|
+
this.name = "UnsupportedProtocolVersionError";
|
|
185
|
+
this.documentId = documentId;
|
|
186
|
+
this.protocol = protocol;
|
|
187
|
+
this.version = version;
|
|
188
|
+
Error.captureStackTrace(this, UnsupportedProtocolVersionError);
|
|
189
|
+
}
|
|
190
|
+
static isError(error) {
|
|
191
|
+
return Error.isError(error) && error.name === "UnsupportedProtocolVersionError";
|
|
192
|
+
}
|
|
193
|
+
};
|
|
176
194
|
/**
|
|
177
195
|
* A load would move more operations than the bound allows, indicating a real
|
|
178
196
|
* divergence between local and incoming history. Counts only first-time moves,
|
|
@@ -307,6 +325,22 @@ var RelationshipNotFoundError = class RelationshipNotFoundError extends Error {
|
|
|
307
325
|
return Error.isError(error) && error.name === "RelationshipNotFoundError";
|
|
308
326
|
}
|
|
309
327
|
};
|
|
328
|
+
/** The store holds documents at protocol versions this reactor does not run. */
|
|
329
|
+
var UnsupportedStoredProtocolError = class UnsupportedStoredProtocolError extends Error {
|
|
330
|
+
versions;
|
|
331
|
+
documents;
|
|
332
|
+
constructor(versions, documents) {
|
|
333
|
+
const named = versions.map(({ protocol, version }) => `${protocol} ${version}`).join(", ");
|
|
334
|
+
super(`${documents} stored document(s) require ${named}, which this reactor does not run. Start a build that runs them, or accept them read-only with withUnsupportedStoredDocuments("read-only")`);
|
|
335
|
+
this.name = "UnsupportedStoredProtocolError";
|
|
336
|
+
this.versions = versions;
|
|
337
|
+
this.documents = documents;
|
|
338
|
+
Error.captureStackTrace(this, UnsupportedStoredProtocolError);
|
|
339
|
+
}
|
|
340
|
+
static isError(error) {
|
|
341
|
+
return Error.isError(error) && error.name === "UnsupportedStoredProtocolError";
|
|
342
|
+
}
|
|
343
|
+
};
|
|
310
344
|
//#endregion
|
|
311
345
|
//#region src/storage/interfaces.ts
|
|
312
346
|
/**
|
|
@@ -393,6 +427,52 @@ let DocumentExistence = /* @__PURE__ */ function(DocumentExistence) {
|
|
|
393
427
|
return DocumentExistence;
|
|
394
428
|
}({});
|
|
395
429
|
//#endregion
|
|
430
|
+
//#region src/cache/operation-index-types.ts
|
|
431
|
+
const DRIVE_COLLECTION_PREFIX = "drive.";
|
|
432
|
+
/**
|
|
433
|
+
* Identifies the collection a remote synchronizes. Collections are drive-level
|
|
434
|
+
* abstractions (document-drive and reactor-drive), so a collection id is the
|
|
435
|
+
* drive document id plus the branch it scopes to rather than an opaque string.
|
|
436
|
+
*
|
|
437
|
+
* The canonical string form (`drive.${branch}.${driveId}`) is produced only by
|
|
438
|
+
* `key` and parsed only by `fromKey`; that string is the wire and storage
|
|
439
|
+
* representation and is byte-for-byte identical to the legacy
|
|
440
|
+
* `driveCollectionId(branch, driveId)` output, so existing `document_collections`
|
|
441
|
+
* rows and persisted remotes remain valid without migration.
|
|
442
|
+
*/
|
|
443
|
+
var DriveCollectionId = class DriveCollectionId {
|
|
444
|
+
driveId;
|
|
445
|
+
branch;
|
|
446
|
+
constructor(driveId, branch) {
|
|
447
|
+
this.driveId = driveId;
|
|
448
|
+
this.branch = branch;
|
|
449
|
+
}
|
|
450
|
+
static forDrive(driveId, branch = "main") {
|
|
451
|
+
return new DriveCollectionId(driveId, branch);
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* The single deserializer for the wire/storage form. `branch` may contain
|
|
455
|
+
* dots, while `driveId` is a dot-free document id, so the drive id is the
|
|
456
|
+
* final dot-delimited segment.
|
|
457
|
+
*/
|
|
458
|
+
static fromKey(key) {
|
|
459
|
+
if (!key.startsWith(DRIVE_COLLECTION_PREFIX)) throw new Error(`Unsupported collection id: ${key}`);
|
|
460
|
+
const rest = key.slice(6);
|
|
461
|
+
const lastDot = rest.lastIndexOf(".");
|
|
462
|
+
if (lastDot === -1 || lastDot === rest.length - 1) throw new Error(`Malformed drive collection id: ${key}`);
|
|
463
|
+
return new DriveCollectionId(rest.slice(lastDot + 1), rest.slice(0, lastDot));
|
|
464
|
+
}
|
|
465
|
+
get key() {
|
|
466
|
+
return `${DRIVE_COLLECTION_PREFIX}${this.branch}.${this.driveId}`;
|
|
467
|
+
}
|
|
468
|
+
toString() {
|
|
469
|
+
return this.key;
|
|
470
|
+
}
|
|
471
|
+
equals(other) {
|
|
472
|
+
return this.driveId === other.driveId && this.branch === other.branch;
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
//#endregion
|
|
396
476
|
//#region src/decision/build-decision-model.ts
|
|
397
477
|
/**
|
|
398
478
|
* Reads each projection's stream through the supplied reader, recording the
|
|
@@ -1283,6 +1363,212 @@ var DocumentMetaCache = class DocumentMetaCache {
|
|
|
1283
1363
|
};
|
|
1284
1364
|
}
|
|
1285
1365
|
};
|
|
1366
|
+
//#endregion
|
|
1367
|
+
//#region src/catch-up/settled-watermark.ts
|
|
1368
|
+
const MAX_PENDING_PROBES = 64;
|
|
1369
|
+
/** pg_sequence_last_value needs 10; pg_current_* replaced txid_* in 13. */
|
|
1370
|
+
function snapshotFunctionsFor(serverVersionNum) {
|
|
1371
|
+
if (serverVersionNum >= 13e4) return {
|
|
1372
|
+
snapshot: "pg_current_snapshot",
|
|
1373
|
+
currentXid: "pg_current_xact_id",
|
|
1374
|
+
xidIfAssigned: "pg_current_xact_id_if_assigned"
|
|
1375
|
+
};
|
|
1376
|
+
if (serverVersionNum >= 1e5) return {
|
|
1377
|
+
snapshot: "txid_current_snapshot",
|
|
1378
|
+
currentXid: "txid_current",
|
|
1379
|
+
xidIfAssigned: "txid_current_if_assigned"
|
|
1380
|
+
};
|
|
1381
|
+
throw new Error(`PostgreSQL server_version_num ${serverVersionNum} is not supported: read-side catch-up needs 10 or later`);
|
|
1382
|
+
}
|
|
1383
|
+
/** Parses the `xmin:xmax:xip,...` text form of a snapshot. */
|
|
1384
|
+
function parseSnapshot(text) {
|
|
1385
|
+
const parts = text.split(":");
|
|
1386
|
+
if (parts.length !== 3) throw new Error(`Malformed snapshot: ${text}`);
|
|
1387
|
+
const [xmin, xmax, xip] = parts;
|
|
1388
|
+
return {
|
|
1389
|
+
xmin: BigInt(xmin),
|
|
1390
|
+
xmax: BigInt(xmax),
|
|
1391
|
+
xip: xip === "" ? [] : xip.split(",").map((xid) => BigInt(xid))
|
|
1392
|
+
};
|
|
1393
|
+
}
|
|
1394
|
+
async function readSnapshotFunctions(db) {
|
|
1395
|
+
const result = await sql`select current_setting('server_version_num') as version`.execute(db);
|
|
1396
|
+
return snapshotFunctionsFor(Number(result.rows[0].version));
|
|
1397
|
+
}
|
|
1398
|
+
/** A probe settles once an xmin passes the xid it took after its head. */
|
|
1399
|
+
var ProbeSettler = class {
|
|
1400
|
+
settled = 0;
|
|
1401
|
+
lastHead = 0;
|
|
1402
|
+
covered = 0;
|
|
1403
|
+
lastXip = [];
|
|
1404
|
+
pending = [];
|
|
1405
|
+
get settledThrough() {
|
|
1406
|
+
return this.settled;
|
|
1407
|
+
}
|
|
1408
|
+
get head() {
|
|
1409
|
+
return this.lastHead;
|
|
1410
|
+
}
|
|
1411
|
+
/** The highest head a settled or pending probe covers. */
|
|
1412
|
+
get probedHead() {
|
|
1413
|
+
return this.covered;
|
|
1414
|
+
}
|
|
1415
|
+
observe(head, xid, snapshot, nowUtcMs) {
|
|
1416
|
+
this.lastHead = Math.max(this.lastHead, head);
|
|
1417
|
+
this.lastXip = snapshot.xip;
|
|
1418
|
+
if (xid !== null && head > this.covered) {
|
|
1419
|
+
this.covered = head;
|
|
1420
|
+
this.pending.push({
|
|
1421
|
+
head,
|
|
1422
|
+
xid,
|
|
1423
|
+
takenAtUtcMs: nowUtcMs
|
|
1424
|
+
});
|
|
1425
|
+
if (this.pending.length > MAX_PENDING_PROBES) this.pending.shift();
|
|
1426
|
+
}
|
|
1427
|
+
const stillPending = [];
|
|
1428
|
+
for (const probe of this.pending) if (snapshot.xmin > probe.xid) this.settled = Math.max(this.settled, probe.head);
|
|
1429
|
+
else stillPending.push(probe);
|
|
1430
|
+
this.pending = stillPending.filter((probe) => probe.head > this.settled);
|
|
1431
|
+
return this.settled;
|
|
1432
|
+
}
|
|
1433
|
+
/** Xids an unsettled probe still waits on. */
|
|
1434
|
+
waitingOn() {
|
|
1435
|
+
if (this.pending.length === 0) return [];
|
|
1436
|
+
const xid = this.pending[0].xid;
|
|
1437
|
+
return this.lastXip.filter((open) => open < xid).map((open) => open.toString());
|
|
1438
|
+
}
|
|
1439
|
+
stalledSinceUtcMs() {
|
|
1440
|
+
return this.pending[0]?.takenAtUtcMs;
|
|
1441
|
+
}
|
|
1442
|
+
};
|
|
1443
|
+
function operationIndexTableName(db) {
|
|
1444
|
+
const from = db.selectFrom("operation_index_operations").selectAll().toOperationNode().from?.froms[0];
|
|
1445
|
+
const schema = from !== void 0 && TableNode.is(from) ? from.table.schema?.name : void 0;
|
|
1446
|
+
const quote = (identifier) => `"${identifier.replace(/"/g, "\"\"")}"`;
|
|
1447
|
+
return schema === void 0 ? quote("operation_index_operations") : `${quote(schema)}.${quote("operation_index_operations")}`;
|
|
1448
|
+
}
|
|
1449
|
+
/** Reads the head, takes and commits an xid, then reads a snapshot, in order. */
|
|
1450
|
+
function createKyselyWatermarkProbe(db) {
|
|
1451
|
+
let resolved = null;
|
|
1452
|
+
const resolve = async () => {
|
|
1453
|
+
const fns = await readSnapshotFunctions(db);
|
|
1454
|
+
const table = operationIndexTableName(db);
|
|
1455
|
+
const sequence = (await sql`select pg_get_serial_sequence(${table}, 'ordinal') as sequence`.execute(db)).rows[0]?.sequence;
|
|
1456
|
+
if (!sequence) throw new Error(`No ordinal sequence found for ${table}`);
|
|
1457
|
+
return {
|
|
1458
|
+
fns,
|
|
1459
|
+
sequence
|
|
1460
|
+
};
|
|
1461
|
+
};
|
|
1462
|
+
return async (probedHead) => {
|
|
1463
|
+
resolved ??= resolve().catch((error) => {
|
|
1464
|
+
resolved = null;
|
|
1465
|
+
throw error;
|
|
1466
|
+
});
|
|
1467
|
+
const { fns, sequence } = await resolved;
|
|
1468
|
+
const headRow = (await sql`select coalesce(pg_sequence_last_value(${sequence}::regclass), 0) as head, ${sql.raw(fns.xidIfAssigned)}() is null as outside_write`.execute(db)).rows[0];
|
|
1469
|
+
const head = Number(headRow.head);
|
|
1470
|
+
let xid = null;
|
|
1471
|
+
if (headRow.outside_write && head > probedHead) {
|
|
1472
|
+
const xidResult = await sql`select ${sql.raw(fns.currentXid)}()::text as xid`.execute(db);
|
|
1473
|
+
xid = BigInt(xidResult.rows[0].xid);
|
|
1474
|
+
}
|
|
1475
|
+
const row = (await sql`select ${sql.raw(fns.snapshot)}()::text as snapshot, ${sql.raw(fns.xidIfAssigned)}() is null as outside_write`.execute(db)).rows[0];
|
|
1476
|
+
return {
|
|
1477
|
+
head,
|
|
1478
|
+
xid,
|
|
1479
|
+
snapshot: parseSnapshot(row.snapshot),
|
|
1480
|
+
outsideWrite: headRow.outside_write && row.outside_write
|
|
1481
|
+
};
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
/** Knows when an ordinal gap is final by asking which transactions are open. */
|
|
1485
|
+
var SettledWatermark = class {
|
|
1486
|
+
probe;
|
|
1487
|
+
logger;
|
|
1488
|
+
now;
|
|
1489
|
+
settler = new ProbeSettler();
|
|
1490
|
+
listeners = /* @__PURE__ */ new Set();
|
|
1491
|
+
current;
|
|
1492
|
+
next;
|
|
1493
|
+
warnedInsideWrite = false;
|
|
1494
|
+
constructor(probe, logger, now = Date.now) {
|
|
1495
|
+
this.probe = probe;
|
|
1496
|
+
this.logger = logger;
|
|
1497
|
+
this.now = now;
|
|
1498
|
+
}
|
|
1499
|
+
get settledThrough() {
|
|
1500
|
+
return this.settler.settledThrough;
|
|
1501
|
+
}
|
|
1502
|
+
refresh(signal) {
|
|
1503
|
+
signal?.throwIfAborted();
|
|
1504
|
+
if (this.next !== void 0) return this.next;
|
|
1505
|
+
if (this.current === void 0) return this.start();
|
|
1506
|
+
const next = this.current.catch(() => this.settledThrough).then(() => {
|
|
1507
|
+
this.next = void 0;
|
|
1508
|
+
return this.start();
|
|
1509
|
+
});
|
|
1510
|
+
this.next = next;
|
|
1511
|
+
return next;
|
|
1512
|
+
}
|
|
1513
|
+
onAdvance(listener) {
|
|
1514
|
+
this.listeners.add(listener);
|
|
1515
|
+
return () => {
|
|
1516
|
+
this.listeners.delete(listener);
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
status() {
|
|
1520
|
+
const stalledSinceUtcMs = this.settler.head > this.settledThrough ? this.settler.stalledSinceUtcMs() : void 0;
|
|
1521
|
+
return {
|
|
1522
|
+
head: this.settler.head,
|
|
1523
|
+
settledThrough: this.settledThrough,
|
|
1524
|
+
waitingOn: this.settler.waitingOn(),
|
|
1525
|
+
...stalledSinceUtcMs !== void 0 ? { stalledSinceUtcMs } : {}
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
start() {
|
|
1529
|
+
const run = this.probeOnce().finally(() => {
|
|
1530
|
+
if (this.current === run) this.current = void 0;
|
|
1531
|
+
});
|
|
1532
|
+
this.current = run;
|
|
1533
|
+
return run;
|
|
1534
|
+
}
|
|
1535
|
+
async probeOnce() {
|
|
1536
|
+
const reading = await this.probe(this.settler.probedHead);
|
|
1537
|
+
if (!reading.outsideWrite) {
|
|
1538
|
+
if (!this.warnedInsideWrite) {
|
|
1539
|
+
this.warnedInsideWrite = true;
|
|
1540
|
+
this.logger.warn("settled watermark probe ran inside another session's write transaction; discarded");
|
|
1541
|
+
}
|
|
1542
|
+
return this.settledThrough;
|
|
1543
|
+
}
|
|
1544
|
+
const before = this.settledThrough;
|
|
1545
|
+
const after = this.settler.observe(reading.head, reading.xid, reading.snapshot, this.now());
|
|
1546
|
+
if (after > before) for (const listener of this.listeners) try {
|
|
1547
|
+
listener(after);
|
|
1548
|
+
} catch (error) {
|
|
1549
|
+
this.logger.error("settled watermark listener failed: @error", error);
|
|
1550
|
+
}
|
|
1551
|
+
return after;
|
|
1552
|
+
}
|
|
1553
|
+
};
|
|
1554
|
+
/** Sessions holding these xids open; empty without pg_stat_activity. */
|
|
1555
|
+
async function describeWaitingSessions(db, xids) {
|
|
1556
|
+
if (xids.length === 0) return [];
|
|
1557
|
+
const epochless = xids.map((xid) => (BigInt(xid) % 2n ** 32n).toString());
|
|
1558
|
+
try {
|
|
1559
|
+
return (await sql`select pid, application_name, state, xact_start, backend_xid::text as xid
|
|
1560
|
+
from pg_stat_activity
|
|
1561
|
+
where backend_xid::text in (${sql.join(epochless)})`.execute(db)).rows.map((row) => ({
|
|
1562
|
+
pid: Number(row.pid),
|
|
1563
|
+
applicationName: row.application_name ?? "",
|
|
1564
|
+
state: row.state ?? "",
|
|
1565
|
+
xactStart: row.xact_start === null ? null : new Date(row.xact_start).toISOString(),
|
|
1566
|
+
xid: row.xid
|
|
1567
|
+
}));
|
|
1568
|
+
} catch {
|
|
1569
|
+
return [];
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1286
1572
|
/** Bind slots per statement: 16-bit on the wire, read signed in-process. */
|
|
1287
1573
|
const MAX_BIND_PARAMETERS = 32767;
|
|
1288
1574
|
/** Splits rows into runs that each bind at most MAX_BIND_PARAMETERS. */
|
|
@@ -1295,6 +1581,8 @@ function chunkRows(rows) {
|
|
|
1295
1581
|
for (let i = 0; i < rows.length; i += perChunk) chunks.push(rows.slice(i, i + perChunk));
|
|
1296
1582
|
return chunks;
|
|
1297
1583
|
}
|
|
1584
|
+
/** Per database: the function that assigns the transaction's xid. */
|
|
1585
|
+
const xidFunctions = /* @__PURE__ */ new WeakMap();
|
|
1298
1586
|
var KyselyOperationIndexTxn = class {
|
|
1299
1587
|
collections = [];
|
|
1300
1588
|
collectionMemberships = [];
|
|
@@ -1421,6 +1709,7 @@ var KyselyOperationIndex = class KyselyOperationIndex {
|
|
|
1421
1709
|
}
|
|
1422
1710
|
let operationOrdinals = [];
|
|
1423
1711
|
if (operations.length > 0) {
|
|
1712
|
+
await this.assignXid(trx);
|
|
1424
1713
|
const operationRows = operations.map((op) => ({
|
|
1425
1714
|
opId: op.id || "",
|
|
1426
1715
|
documentId: op.documentId,
|
|
@@ -1471,6 +1760,27 @@ var KyselyOperationIndex = class KyselyOperationIndex {
|
|
|
1471
1760
|
}
|
|
1472
1761
|
return operationOrdinals;
|
|
1473
1762
|
}
|
|
1763
|
+
async getOrdinalsInRange(after, through, limit, signal) {
|
|
1764
|
+
signal?.throwIfAborted();
|
|
1765
|
+
if (through <= after || limit <= 0) return [];
|
|
1766
|
+
return (await this.queryExecutor.selectFrom("operation_index_operations").select("ordinal").where("ordinal", ">", after).where("ordinal", "<=", through).orderBy("ordinal", "asc").limit(limit).execute()).map((row) => Number(row.ordinal));
|
|
1767
|
+
}
|
|
1768
|
+
async getByOrdinals(ordinals, signal) {
|
|
1769
|
+
signal?.throwIfAborted();
|
|
1770
|
+
if (ordinals.length === 0) return [];
|
|
1771
|
+
const results = [];
|
|
1772
|
+
for (let i = 0; i < ordinals.length; i += MAX_BIND_PARAMETERS) {
|
|
1773
|
+
const chunk = ordinals.slice(i, i + MAX_BIND_PARAMETERS);
|
|
1774
|
+
const rows = await this.queryExecutor.selectFrom("operation_index_operations").selectAll().where("ordinal", "in", chunk).orderBy("ordinal", "asc").execute();
|
|
1775
|
+
for (const row of rows) results.push(this.rowToOperationWithContext(row));
|
|
1776
|
+
}
|
|
1777
|
+
results.sort((a, b) => a.context.ordinal - b.context.ordinal);
|
|
1778
|
+
return results;
|
|
1779
|
+
}
|
|
1780
|
+
async getStreamAfter(stream, after, signal) {
|
|
1781
|
+
signal?.throwIfAborted();
|
|
1782
|
+
return (await this.queryExecutor.selectFrom("operation_index_operations").selectAll().where("documentId", "=", stream.documentId).where("branch", "=", stream.branch).where("scope", "=", stream.scope).where("ordinal", ">", after).orderBy("ordinal", "asc").execute()).map((row) => this.rowToOperationWithContext(row));
|
|
1783
|
+
}
|
|
1474
1784
|
async getGroupReferencers(groupId, signal) {
|
|
1475
1785
|
if (signal?.aborted) throw new Error("Operation aborted");
|
|
1476
1786
|
return (await this.queryExecutor.selectFrom("group_references").select("documentId").where("groupId", "=", groupId).orderBy("documentId").execute()).map((row) => row.documentId);
|
|
@@ -1494,6 +1804,7 @@ var KyselyOperationIndex = class KyselyOperationIndex {
|
|
|
1494
1804
|
if (view?.branch) qb = qb.where("oi.branch", "=", view.branch);
|
|
1495
1805
|
if (view?.scopes && view.scopes.length > 0) qb = qb.where("oi.scope", "in", view.scopes);
|
|
1496
1806
|
if (view?.excludeSourceRemote) qb = qb.where("oi.sourceRemote", "!=", view.excludeSourceRemote);
|
|
1807
|
+
if (view?.throughOrdinal !== void 0) qb = qb.where("oi.ordinal", "<=", view.throughOrdinal).where("dc.joinedOrdinal", "<=", BigInt(view.throughOrdinal));
|
|
1497
1808
|
return qb;
|
|
1498
1809
|
};
|
|
1499
1810
|
const rows = await buildBranch("joiner").unionAll(buildBranch("newOps")).orderBy("ordinal", "asc").limit(limit + 1).execute();
|
|
@@ -1582,6 +1893,17 @@ var KyselyOperationIndex = class KyselyOperationIndex {
|
|
|
1582
1893
|
}, signal) : void 0
|
|
1583
1894
|
};
|
|
1584
1895
|
}
|
|
1896
|
+
/** Takes the xid before the first ordinal, as the settled watermark needs. */
|
|
1897
|
+
async assignXid(trx) {
|
|
1898
|
+
let fn = xidFunctions.get(this.db);
|
|
1899
|
+
if (fn === void 0) {
|
|
1900
|
+
fn = readSnapshotFunctions(trx).then((fns) => fns.currentXid);
|
|
1901
|
+
xidFunctions.set(this.db, fn);
|
|
1902
|
+
fn.catch(() => xidFunctions.delete(this.db));
|
|
1903
|
+
}
|
|
1904
|
+
const name = await fn;
|
|
1905
|
+
await sql`select ${sql.raw(name)}()`.execute(trx);
|
|
1906
|
+
}
|
|
1585
1907
|
rowToOperationWithContext(row) {
|
|
1586
1908
|
return {
|
|
1587
1909
|
operation: {
|
|
@@ -2004,9 +2326,9 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
2004
2326
|
* the last replayed operation are applied at the end. Creation-time 0->N seed
|
|
2005
2327
|
* upgrades carry the initial state, so they still apply immediately.
|
|
2006
2328
|
*/
|
|
2007
|
-
async coldMissRebuild(documentId, scope, branch, targetRevision, signal) {
|
|
2329
|
+
async coldMissRebuild(documentId, scope, branch, targetRevision, signal, fromKeyframe = true) {
|
|
2008
2330
|
const effectiveTargetRevision = targetRevision || Number.MAX_SAFE_INTEGER;
|
|
2009
|
-
const keyframe = await this.findNearestKeyframe(documentId, scope, branch, effectiveTargetRevision, signal);
|
|
2331
|
+
const keyframe = fromKeyframe ? await this.findNearestKeyframe(documentId, scope, branch, effectiveTargetRevision, signal) : void 0;
|
|
2010
2332
|
const documentScopeBound = scope === "document" ? targetRevision : void 0;
|
|
2011
2333
|
let document;
|
|
2012
2334
|
let startRevision;
|
|
@@ -2143,6 +2465,7 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
2143
2465
|
let cursor = void 0;
|
|
2144
2466
|
const pageSize = 100;
|
|
2145
2467
|
let hasMorePages;
|
|
2468
|
+
let needsFullReplay = false;
|
|
2146
2469
|
do {
|
|
2147
2470
|
if (signal?.aborted) throw new Error("Operation aborted");
|
|
2148
2471
|
const paging = {
|
|
@@ -2153,6 +2476,10 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
2153
2476
|
const result = await this.operationStore.getSince(documentId, scope, branch, startRevision, void 0, paging, signal);
|
|
2154
2477
|
for (const operation of result.results) {
|
|
2155
2478
|
if (targetRevision !== void 0 && operation.index > targetRevision) break;
|
|
2479
|
+
if (keyframe && operation.skip > 0) {
|
|
2480
|
+
needsFullReplay = true;
|
|
2481
|
+
break;
|
|
2482
|
+
}
|
|
2156
2483
|
const moduleVersion = this.resolveModuleVersionForOp(operation.index, operation.timestampUtcMs, scope, validatedUpgrades, finalVersion);
|
|
2157
2484
|
document = this.applyPendingUpgrades(document, pendingUpgrades, moduleVersion ?? Number.MAX_SAFE_INTEGER);
|
|
2158
2485
|
if (isDenied(operation)) document = appendWithoutApplying(document, operation, scope);
|
|
@@ -2167,12 +2494,13 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
2167
2494
|
}
|
|
2168
2495
|
}
|
|
2169
2496
|
const reachedTarget = targetRevision !== void 0 && result.results.some((op) => op.index >= targetRevision);
|
|
2170
|
-
hasMorePages = Boolean(result.nextCursor) && !reachedTarget;
|
|
2497
|
+
hasMorePages = Boolean(result.nextCursor) && !reachedTarget && !needsFullReplay;
|
|
2171
2498
|
if (hasMorePages) cursor = result.nextCursor;
|
|
2172
2499
|
} catch (err) {
|
|
2173
2500
|
throw new Error(`Failed to rebuild document ${documentId}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
2174
2501
|
}
|
|
2175
2502
|
} while (hasMorePages);
|
|
2503
|
+
if (needsFullReplay) return this.coldMissRebuild(documentId, scope, branch, targetRevision, signal, false);
|
|
2176
2504
|
document = this.applyTailPendingUpgrades(document, pendingUpgrades, scope, targetRevision);
|
|
2177
2505
|
document = await this.stampRevisions(document, documentId, scope, branch, targetRevision, signal);
|
|
2178
2506
|
if (pendingUpgrades.length > 0) {
|
|
@@ -2279,11 +2607,16 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
2279
2607
|
if ((await this.operationStore.getSince(documentId, "document", branch, docScopeNextIndex - 1, void 0, void 0, signal)).results.length > 0) return this.coldMissRebuild(documentId, scope, branch, targetRevision, signal);
|
|
2280
2608
|
const module = this.registry.getModule(documentType, extractModuleVersion(baseDocument));
|
|
2281
2609
|
let document = copyDocument(baseDocument);
|
|
2610
|
+
let needsFullReplay = false;
|
|
2282
2611
|
try {
|
|
2283
2612
|
const pagedResults = await this.operationStore.getSince(documentId, scope, branch, baseRevision, void 0, void 0, signal);
|
|
2284
2613
|
for (const operation of pagedResults.results) {
|
|
2285
2614
|
if (signal?.aborted) throw new Error("Operation aborted");
|
|
2286
2615
|
if (targetRevision !== void 0 && operation.index > targetRevision) break;
|
|
2616
|
+
if (operation.skip > 0) {
|
|
2617
|
+
needsFullReplay = true;
|
|
2618
|
+
break;
|
|
2619
|
+
}
|
|
2287
2620
|
if (isDenied(operation)) document = appendWithoutApplying(document, operation, scope);
|
|
2288
2621
|
else {
|
|
2289
2622
|
const protocolVersion = baseReducerVersion(document.header);
|
|
@@ -2299,6 +2632,7 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
2299
2632
|
} catch (err) {
|
|
2300
2633
|
throw new Error(`Failed to rebuild document ${documentId}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
2301
2634
|
}
|
|
2635
|
+
if (needsFullReplay) return this.coldMissRebuild(documentId, scope, branch, targetRevision, signal, false);
|
|
2302
2636
|
const revisions = await this.operationStore.getRevisions(documentId, branch, signal);
|
|
2303
2637
|
document.header.revision = revisions.revision;
|
|
2304
2638
|
if (targetRevision !== void 0) document.header.revision = {
|
|
@@ -2794,52 +3128,6 @@ async function evaluateByPosition(model, target, subject, stores, signal) {
|
|
|
2794
3128
|
return operations.map((operation) => reasons.get(operation.id));
|
|
2795
3129
|
}
|
|
2796
3130
|
//#endregion
|
|
2797
|
-
//#region src/cache/operation-index-types.ts
|
|
2798
|
-
const DRIVE_COLLECTION_PREFIX = "drive.";
|
|
2799
|
-
/**
|
|
2800
|
-
* Identifies the collection a remote synchronizes. Collections are drive-level
|
|
2801
|
-
* abstractions (document-drive and reactor-drive), so a collection id is the
|
|
2802
|
-
* drive document id plus the branch it scopes to rather than an opaque string.
|
|
2803
|
-
*
|
|
2804
|
-
* The canonical string form (`drive.${branch}.${driveId}`) is produced only by
|
|
2805
|
-
* `key` and parsed only by `fromKey`; that string is the wire and storage
|
|
2806
|
-
* representation and is byte-for-byte identical to the legacy
|
|
2807
|
-
* `driveCollectionId(branch, driveId)` output, so existing `document_collections`
|
|
2808
|
-
* rows and persisted remotes remain valid without migration.
|
|
2809
|
-
*/
|
|
2810
|
-
var DriveCollectionId = class DriveCollectionId {
|
|
2811
|
-
driveId;
|
|
2812
|
-
branch;
|
|
2813
|
-
constructor(driveId, branch) {
|
|
2814
|
-
this.driveId = driveId;
|
|
2815
|
-
this.branch = branch;
|
|
2816
|
-
}
|
|
2817
|
-
static forDrive(driveId, branch = "main") {
|
|
2818
|
-
return new DriveCollectionId(driveId, branch);
|
|
2819
|
-
}
|
|
2820
|
-
/**
|
|
2821
|
-
* The single deserializer for the wire/storage form. `branch` may contain
|
|
2822
|
-
* dots, while `driveId` is a dot-free document id, so the drive id is the
|
|
2823
|
-
* final dot-delimited segment.
|
|
2824
|
-
*/
|
|
2825
|
-
static fromKey(key) {
|
|
2826
|
-
if (!key.startsWith(DRIVE_COLLECTION_PREFIX)) throw new Error(`Unsupported collection id: ${key}`);
|
|
2827
|
-
const rest = key.slice(6);
|
|
2828
|
-
const lastDot = rest.lastIndexOf(".");
|
|
2829
|
-
if (lastDot === -1 || lastDot === rest.length - 1) throw new Error(`Malformed drive collection id: ${key}`);
|
|
2830
|
-
return new DriveCollectionId(rest.slice(lastDot + 1), rest.slice(0, lastDot));
|
|
2831
|
-
}
|
|
2832
|
-
get key() {
|
|
2833
|
-
return `${DRIVE_COLLECTION_PREFIX}${this.branch}.${this.driveId}`;
|
|
2834
|
-
}
|
|
2835
|
-
toString() {
|
|
2836
|
-
return this.key;
|
|
2837
|
-
}
|
|
2838
|
-
equals(other) {
|
|
2839
|
-
return this.driveId === other.driveId && this.branch === other.branch;
|
|
2840
|
-
}
|
|
2841
|
-
};
|
|
2842
|
-
//#endregion
|
|
2843
3131
|
//#region src/executor/document-action-handler.ts
|
|
2844
3132
|
var DocumentActionHandler = class {
|
|
2845
3133
|
registry;
|
|
@@ -2847,12 +3135,29 @@ var DocumentActionHandler = class {
|
|
|
2847
3135
|
driveContainerTypes;
|
|
2848
3136
|
featureFlags;
|
|
2849
3137
|
decisionModel;
|
|
2850
|
-
|
|
3138
|
+
protocolSupport;
|
|
3139
|
+
loggedUnregisteredProtocols = /* @__PURE__ */ new Set();
|
|
3140
|
+
constructor(registry, logger, driveContainerTypes, featureFlags, decisionModel, protocolSupport) {
|
|
2851
3141
|
this.registry = registry;
|
|
2852
3142
|
this.logger = logger;
|
|
2853
3143
|
this.driveContainerTypes = driveContainerTypes;
|
|
2854
3144
|
this.featureFlags = featureFlags;
|
|
2855
3145
|
this.decisionModel = decisionModel;
|
|
3146
|
+
this.protocolSupport = protocolSupport;
|
|
3147
|
+
}
|
|
3148
|
+
/** Keys this reactor does not register are admitted: it cannot judge them. */
|
|
3149
|
+
unsupportedProtocol(documentId, protocolVersions) {
|
|
3150
|
+
for (const [protocol, version] of Object.entries(protocolVersions ?? {})) {
|
|
3151
|
+
const supported = this.protocolSupport[protocol];
|
|
3152
|
+
if (supported === void 0) {
|
|
3153
|
+
if (!this.loggedUnregisteredProtocols.has(protocol)) {
|
|
3154
|
+
this.loggedUnregisteredProtocols.add(protocol);
|
|
3155
|
+
this.logger.info("Admitting document @documentId with unregistered protocol @protocol @version", documentId, protocol, version);
|
|
3156
|
+
}
|
|
3157
|
+
continue;
|
|
3158
|
+
}
|
|
3159
|
+
if (!supported.includes(version)) return new UnsupportedProtocolVersionError(documentId, protocol, version);
|
|
3160
|
+
}
|
|
2856
3161
|
}
|
|
2857
3162
|
/** Whether the write arrives with its evaluation already decided. */
|
|
2858
3163
|
alreadyEvaluated(executing) {
|
|
@@ -2968,6 +3273,9 @@ var DocumentActionHandler = class {
|
|
|
2968
3273
|
error: /* @__PURE__ */ new Error(`CREATE_DOCUMENT must be in "document" scope, got "${job.scope}"`),
|
|
2969
3274
|
duration: Date.now() - startTime
|
|
2970
3275
|
};
|
|
3276
|
+
const input = action.input;
|
|
3277
|
+
const unsupported = this.unsupportedProtocol(input.documentId, input.protocolVersions);
|
|
3278
|
+
if (unsupported) return buildErrorResult(job, unsupported, startTime);
|
|
2971
3279
|
const document = createDocumentFromAction(action);
|
|
2972
3280
|
let operation = createOperation(action, 0, skip, {
|
|
2973
3281
|
documentId: document.header.id,
|
|
@@ -4006,6 +4314,7 @@ var SimpleJobExecutor = class {
|
|
|
4006
4314
|
this.collectionMembershipCache = collectionMembershipCache;
|
|
4007
4315
|
this.driveContainerTypes = driveContainerTypes;
|
|
4008
4316
|
this.signer = signer ?? new PassthroughSigner();
|
|
4317
|
+
this.featureFlags = resolveFeatureFlags(config.featureFlags);
|
|
4009
4318
|
this.config = {
|
|
4010
4319
|
featureFlags: config.featureFlags ?? {},
|
|
4011
4320
|
maxSkipThreshold: config.maxSkipThreshold ?? MAX_SKIP_THRESHOLD,
|
|
@@ -4016,9 +4325,9 @@ var SimpleJobExecutor = class {
|
|
|
4016
4325
|
retryMaxDelayMs: config.retryMaxDelayMs ?? 5e3,
|
|
4017
4326
|
yieldDeadlineMs: config.yieldDeadlineMs ?? 50,
|
|
4018
4327
|
batchApplies: config.batchApplies ?? true,
|
|
4019
|
-
signatureVerification: config.signatureVerification ?? "enforce"
|
|
4328
|
+
signatureVerification: config.signatureVerification ?? "enforce",
|
|
4329
|
+
protocolSupport: config.protocolSupport ?? localSupports(PEER_CAPABILITIES, this.featureFlags).protocols
|
|
4020
4330
|
};
|
|
4021
|
-
this.featureFlags = resolveFeatureFlags(config.featureFlags);
|
|
4022
4331
|
this.decisionModel = selectDecisionModel(this.featureFlags, registry);
|
|
4023
4332
|
this.signatureAdmission = new SignatureAdmission(this.config.signatureVerification, logger, eventBus, this.featureFlags.documentDecisions ? "write-cache" : "meta", {
|
|
4024
4333
|
signer: this.signer,
|
|
@@ -4026,7 +4335,7 @@ var SimpleJobExecutor = class {
|
|
|
4026
4335
|
policy: trustPolicy,
|
|
4027
4336
|
timeoutMs: Math.min(DEFAULT_TRUST_TIMEOUT_MS, this.config.jobTimeoutMs / 2)
|
|
4028
4337
|
});
|
|
4029
|
-
this.documentActionHandler = new DocumentActionHandler(registry, logger, driveContainerTypes, this.featureFlags, this.decisionModel);
|
|
4338
|
+
this.documentActionHandler = new DocumentActionHandler(registry, logger, driveContainerTypes, this.featureFlags, this.decisionModel, this.config.protocolSupport);
|
|
4030
4339
|
this.executionScope = executionScope ?? new DefaultExecutionScope(operationStore, operationIndex, writeCache, documentMetaCache, collectionMembershipCache);
|
|
4031
4340
|
}
|
|
4032
4341
|
/**
|
|
@@ -4092,6 +4401,8 @@ var SimpleJobExecutor = class {
|
|
|
4092
4401
|
async executeInScope(params) {
|
|
4093
4402
|
const { job, startTime, stores, signal, touchedStreams, postCommitInvalidations, postCommitMembershipInvalidations } = params;
|
|
4094
4403
|
let pendingEvent;
|
|
4404
|
+
const unsupported = await this.unsupportedStoredProtocol(job, stores, signal);
|
|
4405
|
+
if (unsupported) return { result: buildErrorResult(job, unsupported, startTime) };
|
|
4095
4406
|
const indexTxn = stores.operationIndex.start();
|
|
4096
4407
|
if (job.kind === "load") {
|
|
4097
4408
|
const loadResult = await this.executeLoadJob({
|
|
@@ -4210,6 +4521,16 @@ var SimpleJobExecutor = class {
|
|
|
4210
4521
|
for (let i = 0; i < actionResult.operationsWithContext.length; i++) actionResult.operationsWithContext[i].context.ordinal = ordinals[i];
|
|
4211
4522
|
return this.committedOutcome(job, committed, actionResult.operationsWithContext, actionResult.generatedOperations, stores, startTime);
|
|
4212
4523
|
}
|
|
4524
|
+
/** A stored document at a version this reactor does not run is read-only here. */
|
|
4525
|
+
async unsupportedStoredProtocol(job, stores, signal) {
|
|
4526
|
+
let versions;
|
|
4527
|
+
try {
|
|
4528
|
+
versions = this.featureFlags.documentDecisions && job.kind !== "load" ? (await stores.writeCache.getState(job.documentId, "document", job.branch, void 0, signal)).header.protocolVersions : (await stores.documentMetaCache.getDocumentMeta(job.documentId, job.branch, signal)).protocolVersions;
|
|
4529
|
+
} catch {
|
|
4530
|
+
return;
|
|
4531
|
+
}
|
|
4532
|
+
return this.documentActionHandler.unsupportedProtocol(job.documentId, versions);
|
|
4533
|
+
}
|
|
4213
4534
|
/**
|
|
4214
4535
|
* A successful mutation's result and the write-ready event it owes, carrying
|
|
4215
4536
|
* what earlier attempts committed ahead of what this one wrote.
|
|
@@ -5841,8 +6162,8 @@ function createForwardingPoolInstrumentation(name) {
|
|
|
5841
6162
|
}
|
|
5842
6163
|
//#endregion
|
|
5843
6164
|
//#region src/storage/migrations/001_create_operation_table.ts
|
|
5844
|
-
var _001_create_operation_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
5845
|
-
async function up$
|
|
6165
|
+
var _001_create_operation_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$22 });
|
|
6166
|
+
async function up$22(db) {
|
|
5846
6167
|
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", [
|
|
5847
6168
|
"documentId",
|
|
5848
6169
|
"scope",
|
|
@@ -5867,8 +6188,8 @@ async function up$19(db) {
|
|
|
5867
6188
|
}
|
|
5868
6189
|
//#endregion
|
|
5869
6190
|
//#region src/storage/migrations/002_create_keyframe_table.ts
|
|
5870
|
-
var _002_create_keyframe_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
5871
|
-
async function up$
|
|
6191
|
+
var _002_create_keyframe_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$21 });
|
|
6192
|
+
async function up$21(db) {
|
|
5872
6193
|
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", [
|
|
5873
6194
|
"documentId",
|
|
5874
6195
|
"scope",
|
|
@@ -5884,14 +6205,14 @@ async function up$18(db) {
|
|
|
5884
6205
|
}
|
|
5885
6206
|
//#endregion
|
|
5886
6207
|
//#region src/storage/migrations/003_create_document_table.ts
|
|
5887
|
-
var _003_create_document_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
5888
|
-
async function up$
|
|
6208
|
+
var _003_create_document_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$20 });
|
|
6209
|
+
async function up$20(db) {
|
|
5889
6210
|
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();
|
|
5890
6211
|
}
|
|
5891
6212
|
//#endregion
|
|
5892
6213
|
//#region src/storage/migrations/004_create_document_relationship_table.ts
|
|
5893
|
-
var _004_create_document_relationship_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
5894
|
-
async function up$
|
|
6214
|
+
var _004_create_document_relationship_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$19 });
|
|
6215
|
+
async function up$19(db) {
|
|
5895
6216
|
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", [
|
|
5896
6217
|
"sourceId",
|
|
5897
6218
|
"targetId",
|
|
@@ -5903,14 +6224,14 @@ async function up$16(db) {
|
|
|
5903
6224
|
}
|
|
5904
6225
|
//#endregion
|
|
5905
6226
|
//#region src/storage/migrations/005_create_indexer_state_table.ts
|
|
5906
|
-
var _005_create_indexer_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
5907
|
-
async function up$
|
|
6227
|
+
var _005_create_indexer_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$18 });
|
|
6228
|
+
async function up$18(db) {
|
|
5908
6229
|
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();
|
|
5909
6230
|
}
|
|
5910
6231
|
//#endregion
|
|
5911
6232
|
//#region src/storage/migrations/006_create_document_snapshot_table.ts
|
|
5912
|
-
var _006_create_document_snapshot_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
5913
|
-
async function up$
|
|
6233
|
+
var _006_create_document_snapshot_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$17 });
|
|
6234
|
+
async function up$17(db) {
|
|
5914
6235
|
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", [
|
|
5915
6236
|
"documentId",
|
|
5916
6237
|
"scope",
|
|
@@ -5931,8 +6252,8 @@ async function up$14(db) {
|
|
|
5931
6252
|
}
|
|
5932
6253
|
//#endregion
|
|
5933
6254
|
//#region src/storage/migrations/007_create_slug_mapping_table.ts
|
|
5934
|
-
var _007_create_slug_mapping_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
5935
|
-
async function up$
|
|
6255
|
+
var _007_create_slug_mapping_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$16 });
|
|
6256
|
+
async function up$16(db) {
|
|
5936
6257
|
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", [
|
|
5937
6258
|
"documentId",
|
|
5938
6259
|
"scope",
|
|
@@ -5942,14 +6263,14 @@ async function up$13(db) {
|
|
|
5942
6263
|
}
|
|
5943
6264
|
//#endregion
|
|
5944
6265
|
//#region src/storage/migrations/008_create_view_state_table.ts
|
|
5945
|
-
var _008_create_view_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
5946
|
-
async function up$
|
|
6266
|
+
var _008_create_view_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$15 });
|
|
6267
|
+
async function up$15(db) {
|
|
5947
6268
|
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();
|
|
5948
6269
|
}
|
|
5949
6270
|
//#endregion
|
|
5950
6271
|
//#region src/storage/migrations/009_create_operation_index_tables.ts
|
|
5951
|
-
var _009_create_operation_index_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
5952
|
-
async function up$
|
|
6272
|
+
var _009_create_operation_index_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$14 });
|
|
6273
|
+
async function up$14(db) {
|
|
5953
6274
|
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();
|
|
5954
6275
|
await db.schema.createIndex("idx_document_collections_collectionId").on("document_collections").column("collectionId").execute();
|
|
5955
6276
|
await db.schema.createIndex("idx_doc_collections_collection_range").on("document_collections").columns(["collectionId", "joinedOrdinal"]).execute();
|
|
@@ -5963,8 +6284,8 @@ async function up$11(db) {
|
|
|
5963
6284
|
}
|
|
5964
6285
|
//#endregion
|
|
5965
6286
|
//#region src/storage/migrations/010_create_sync_tables.ts
|
|
5966
|
-
var _010_create_sync_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
5967
|
-
async function up$
|
|
6287
|
+
var _010_create_sync_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$13 });
|
|
6288
|
+
async function up$13(db) {
|
|
5968
6289
|
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();
|
|
5969
6290
|
await db.schema.createIndex("idx_sync_remotes_collection").on("sync_remotes").column("collection_id").execute();
|
|
5970
6291
|
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();
|
|
@@ -5972,8 +6293,8 @@ async function up$10(db) {
|
|
|
5972
6293
|
}
|
|
5973
6294
|
//#endregion
|
|
5974
6295
|
//#region src/storage/migrations/011_add_cursor_type_column.ts
|
|
5975
|
-
var _011_add_cursor_type_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
5976
|
-
async function up$
|
|
6296
|
+
var _011_add_cursor_type_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$12 });
|
|
6297
|
+
async function up$12(db) {
|
|
5977
6298
|
await db.deleteFrom("sync_cursors").where("remote_name", "like", "outbox::%").execute();
|
|
5978
6299
|
await db.deleteFrom("sync_remotes").where("name", "like", "outbox::%").execute();
|
|
5979
6300
|
await db.schema.dropTable("sync_cursors").execute();
|
|
@@ -5982,64 +6303,64 @@ async function up$9(db) {
|
|
|
5982
6303
|
}
|
|
5983
6304
|
//#endregion
|
|
5984
6305
|
//#region src/storage/migrations/012_add_source_remote_column.ts
|
|
5985
|
-
var _012_add_source_remote_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
5986
|
-
async function up$
|
|
6306
|
+
var _012_add_source_remote_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$11 });
|
|
6307
|
+
async function up$11(db) {
|
|
5987
6308
|
await db.schema.alterTable("operation_index_operations").addColumn("sourceRemote", "text", (col) => col.notNull().defaultTo("")).execute();
|
|
5988
6309
|
}
|
|
5989
6310
|
//#endregion
|
|
5990
6311
|
//#region src/storage/migrations/013_create_sync_dead_letters_table.ts
|
|
5991
|
-
var _013_create_sync_dead_letters_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
5992
|
-
async function up$
|
|
6312
|
+
var _013_create_sync_dead_letters_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$10 });
|
|
6313
|
+
async function up$10(db) {
|
|
5993
6314
|
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();
|
|
5994
6315
|
await db.schema.createIndex("idx_sync_dead_letters_remote").on("sync_dead_letters").column("remote_name").execute();
|
|
5995
6316
|
}
|
|
5996
6317
|
//#endregion
|
|
5997
6318
|
//#region src/storage/migrations/014_create_processor_cursor_table.ts
|
|
5998
|
-
var _014_create_processor_cursor_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
5999
|
-
async function up$
|
|
6319
|
+
var _014_create_processor_cursor_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$9 });
|
|
6320
|
+
async function up$9(db) {
|
|
6000
6321
|
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();
|
|
6001
6322
|
}
|
|
6002
6323
|
//#endregion
|
|
6003
6324
|
//#region src/storage/migrations/015_add_operation_denied_reason.ts
|
|
6004
6325
|
var _015_add_operation_denied_reason_exports = /* @__PURE__ */ __exportAll({
|
|
6005
|
-
down: () => down$
|
|
6006
|
-
up: () => up$
|
|
6326
|
+
down: () => down$8,
|
|
6327
|
+
up: () => up$8
|
|
6007
6328
|
});
|
|
6008
6329
|
/**
|
|
6009
6330
|
* Records why authorization refused an operation. Separate from `error` so a
|
|
6010
6331
|
* denial is distinguishable from a reducer failure without matching on a
|
|
6011
6332
|
* message. Null for every operation written before decisions were enforced.
|
|
6012
6333
|
*/
|
|
6013
|
-
async function up$
|
|
6334
|
+
async function up$8(db) {
|
|
6014
6335
|
await db.schema.alterTable("Operation").addColumn("deniedReason", "text").execute();
|
|
6015
6336
|
await db.schema.alterTable("operation_index_operations").addColumn("deniedReason", "text").execute();
|
|
6016
6337
|
}
|
|
6017
|
-
async function down$
|
|
6338
|
+
async function down$8(db) {
|
|
6018
6339
|
await db.schema.alterTable("operation_index_operations").dropColumn("deniedReason").execute();
|
|
6019
6340
|
await db.schema.alterTable("Operation").dropColumn("deniedReason").execute();
|
|
6020
6341
|
}
|
|
6021
6342
|
//#endregion
|
|
6022
6343
|
//#region src/storage/migrations/016_add_dead_letter_error_type.ts
|
|
6023
6344
|
var _016_add_dead_letter_error_type_exports = /* @__PURE__ */ __exportAll({
|
|
6024
|
-
down: () => down$
|
|
6025
|
-
up: () => up$
|
|
6345
|
+
down: () => down$7,
|
|
6346
|
+
up: () => up$7
|
|
6026
6347
|
});
|
|
6027
6348
|
/**
|
|
6028
6349
|
* The classification a dead letter falls into, stored because it decides whether
|
|
6029
6350
|
* the document stays quarantined and the in-memory error is gone after a restart.
|
|
6030
6351
|
* Defaulted rather than nullable, so a pre-existing row rehydrates.
|
|
6031
6352
|
*/
|
|
6032
|
-
async function up$
|
|
6353
|
+
async function up$7(db) {
|
|
6033
6354
|
await db.schema.alterTable("sync_dead_letters").addColumn("error_type", "text", (col) => col.notNull().defaultTo("UNCLASSIFIED")).execute();
|
|
6034
6355
|
}
|
|
6035
|
-
async function down$
|
|
6356
|
+
async function down$7(db) {
|
|
6036
6357
|
await db.schema.alterTable("sync_dead_letters").dropColumn("error_type").execute();
|
|
6037
6358
|
}
|
|
6038
6359
|
//#endregion
|
|
6039
6360
|
//#region src/storage/migrations/017_create_group_references.ts
|
|
6040
6361
|
var _017_create_group_references_exports = /* @__PURE__ */ __exportAll({
|
|
6041
|
-
down: () => down$
|
|
6042
|
-
up: () => up$
|
|
6362
|
+
down: () => down$6,
|
|
6363
|
+
up: () => up$6
|
|
6043
6364
|
});
|
|
6044
6365
|
/**
|
|
6045
6366
|
* One row per (document, group) reference ever discovered from an auth
|
|
@@ -6050,18 +6371,18 @@ var _017_create_group_references_exports = /* @__PURE__ */ __exportAll({
|
|
|
6050
6371
|
* requires (sync), and by groupId for the documents a group change affects
|
|
6051
6372
|
* (re-evaluation).
|
|
6052
6373
|
*/
|
|
6053
|
-
async function up$
|
|
6374
|
+
async function up$6(db) {
|
|
6054
6375
|
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();
|
|
6055
6376
|
await db.schema.createIndex("idx_group_references_groupId").on("group_references").column("groupId").execute();
|
|
6056
6377
|
}
|
|
6057
|
-
async function down$
|
|
6378
|
+
async function down$6(db) {
|
|
6058
6379
|
await db.schema.dropTable("group_references").execute();
|
|
6059
6380
|
}
|
|
6060
6381
|
//#endregion
|
|
6061
6382
|
//#region src/storage/migrations/018_add_sync_remote_bound_address.ts
|
|
6062
6383
|
var _018_add_sync_remote_bound_address_exports = /* @__PURE__ */ __exportAll({
|
|
6063
|
-
down: () => down$
|
|
6064
|
-
up: () => up$
|
|
6384
|
+
down: () => down$5,
|
|
6385
|
+
up: () => up$5
|
|
6065
6386
|
});
|
|
6066
6387
|
/**
|
|
6067
6388
|
* The address a sync channel is bound to, so a channel created by one subject
|
|
@@ -6072,17 +6393,17 @@ var _018_add_sync_remote_bound_address_exports = /* @__PURE__ */ __exportAll({
|
|
|
6072
6393
|
* until its first authenticated poll adopts it. A default would claim them all
|
|
6073
6394
|
* for one address.
|
|
6074
6395
|
*/
|
|
6075
|
-
async function up$
|
|
6396
|
+
async function up$5(db) {
|
|
6076
6397
|
await db.schema.alterTable("sync_remotes").addColumn("bound_address", "text").execute();
|
|
6077
6398
|
}
|
|
6078
|
-
async function down$
|
|
6399
|
+
async function down$5(db) {
|
|
6079
6400
|
await db.schema.alterTable("sync_remotes").dropColumn("bound_address").execute();
|
|
6080
6401
|
}
|
|
6081
6402
|
//#endregion
|
|
6082
6403
|
//#region src/storage/migrations/019_require_action_id.ts
|
|
6083
6404
|
var _019_require_action_id_exports = /* @__PURE__ */ __exportAll({
|
|
6084
|
-
down: () => down$
|
|
6085
|
-
up: () => up$
|
|
6405
|
+
down: () => down$4,
|
|
6406
|
+
up: () => up$4
|
|
6086
6407
|
});
|
|
6087
6408
|
/**
|
|
6088
6409
|
* Makes an operation whose action carries no id physically unstorable.
|
|
@@ -6109,7 +6430,7 @@ var _019_require_action_id_exports = /* @__PURE__ */ __exportAll({
|
|
|
6109
6430
|
* The empty string is rejected alongside null. It derives the same colliding
|
|
6110
6431
|
* operation id as an absent id, so admitting it would leave the hole open.
|
|
6111
6432
|
*/
|
|
6112
|
-
async function up$
|
|
6433
|
+
async function up$4(db) {
|
|
6113
6434
|
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();
|
|
6114
6435
|
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();
|
|
6115
6436
|
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();
|
|
@@ -6121,22 +6442,72 @@ async function up$1(db) {
|
|
|
6121
6442
|
* their operations are now known by, and reverting them would reintroduce the
|
|
6122
6443
|
* collision the migration removed.
|
|
6123
6444
|
*/
|
|
6124
|
-
async function down$
|
|
6445
|
+
async function down$4(db) {
|
|
6125
6446
|
await db.schema.alterTable("operation_index_operations").dropConstraint("action_must_have_id").execute();
|
|
6126
6447
|
await db.schema.alterTable("Operation").dropConstraint("action_must_have_id").execute();
|
|
6127
6448
|
}
|
|
6128
6449
|
//#endregion
|
|
6129
6450
|
//#region src/storage/migrations/020_add_snapshot_operation_ordinal.ts
|
|
6130
6451
|
var _020_add_snapshot_operation_ordinal_exports = /* @__PURE__ */ __exportAll({
|
|
6452
|
+
down: () => down$3,
|
|
6453
|
+
up: () => up$3
|
|
6454
|
+
});
|
|
6455
|
+
/** Orders header row writes across scopes; `lastOperationIndex` is per scope. */
|
|
6456
|
+
async function up$3(db) {
|
|
6457
|
+
await db.schema.alterTable("DocumentSnapshot").addColumn("lastOperationOrdinal", "integer", (col) => col.notNull().defaultTo(0)).execute();
|
|
6458
|
+
}
|
|
6459
|
+
async function down$3(db) {
|
|
6460
|
+
await db.schema.alterTable("DocumentSnapshot").dropColumn("lastOperationOrdinal").execute();
|
|
6461
|
+
}
|
|
6462
|
+
//#endregion
|
|
6463
|
+
//#region src/storage/migrations/021_add_sync_remote_peer.ts
|
|
6464
|
+
var _021_add_sync_remote_peer_exports = /* @__PURE__ */ __exportAll({
|
|
6465
|
+
down: () => down$2,
|
|
6466
|
+
up: () => up$2
|
|
6467
|
+
});
|
|
6468
|
+
/**
|
|
6469
|
+
* The peer's manifest as canonical JSON, and when it was heard. Both null: not
|
|
6470
|
+
* yet heard. A time with no manifest: a silent peer, which gets the baselines.
|
|
6471
|
+
*/
|
|
6472
|
+
async function up$2(db) {
|
|
6473
|
+
await db.schema.alterTable("sync_remotes").addColumn("peer_manifest", "text").addColumn("peer_manifest_at_utc_ms", "bigint").execute();
|
|
6474
|
+
}
|
|
6475
|
+
async function down$2(db) {
|
|
6476
|
+
await db.schema.alterTable("sync_remotes").dropColumn("peer_manifest").dropColumn("peer_manifest_at_utc_ms").execute();
|
|
6477
|
+
}
|
|
6478
|
+
//#endregion
|
|
6479
|
+
//#region src/storage/migrations/022_create_sync_holds.ts
|
|
6480
|
+
var _022_create_sync_holds_exports = /* @__PURE__ */ __exportAll({
|
|
6481
|
+
down: () => down$1,
|
|
6482
|
+
up: () => up$1
|
|
6483
|
+
});
|
|
6484
|
+
/** Documents held back from a remote whose peer cannot run their versions. */
|
|
6485
|
+
async function up$1(db) {
|
|
6486
|
+
await db.schema.createTable("sync_holds").addColumn("remote_name", "text", (col) => col.notNull().references("sync_remotes.name").onDelete("cascade")).addColumn("document_id", "text", (col) => col.notNull()).addColumn("branch", "text", (col) => col.notNull()).addColumn("protocol", "text", (col) => col.notNull()).addColumn("version", "integer", (col) => col.notNull()).addColumn("held_at_utc_ms", "bigint", (col) => col.notNull()).addPrimaryKeyConstraint("sync_holds_pkey", [
|
|
6487
|
+
"remote_name",
|
|
6488
|
+
"document_id",
|
|
6489
|
+
"branch"
|
|
6490
|
+
]).execute();
|
|
6491
|
+
}
|
|
6492
|
+
async function down$1(db) {
|
|
6493
|
+
await db.schema.dropTable("sync_holds").execute();
|
|
6494
|
+
}
|
|
6495
|
+
//#endregion
|
|
6496
|
+
//#region src/storage/migrations/023_index_created_protocol_versions.ts
|
|
6497
|
+
var _023_index_created_protocol_versions_exports = /* @__PURE__ */ __exportAll({
|
|
6131
6498
|
down: () => down,
|
|
6132
6499
|
up: () => up
|
|
6133
6500
|
});
|
|
6134
|
-
/**
|
|
6501
|
+
/**
|
|
6502
|
+
* One entry per document created with protocolVersions, keyed by a hash so an
|
|
6503
|
+
* oversized header cannot make its creation unstorable. Startup reads the
|
|
6504
|
+
* distinct values without visiting every document.
|
|
6505
|
+
*/
|
|
6135
6506
|
async function up(db) {
|
|
6136
|
-
await db.schema.
|
|
6507
|
+
await db.schema.createIndex("idx_operation_created_protocol_versions").on("Operation").expression(sql`md5((action->'input'->'protocolVersions')::text)`).where(sql`scope = 'document' and "index" = 0 and (action->'input'->'protocolVersions') is not null`).execute();
|
|
6137
6508
|
}
|
|
6138
6509
|
async function down(db) {
|
|
6139
|
-
await db.schema.
|
|
6510
|
+
await db.schema.dropIndex("idx_operation_created_protocol_versions").execute();
|
|
6140
6511
|
}
|
|
6141
6512
|
//#endregion
|
|
6142
6513
|
//#region src/storage/migrations/migrator.ts
|
|
@@ -6161,7 +6532,10 @@ const migrations = {
|
|
|
6161
6532
|
"017_create_group_references": _017_create_group_references_exports,
|
|
6162
6533
|
"018_add_sync_remote_bound_address": _018_add_sync_remote_bound_address_exports,
|
|
6163
6534
|
"019_require_action_id": _019_require_action_id_exports,
|
|
6164
|
-
"020_add_snapshot_operation_ordinal": _020_add_snapshot_operation_ordinal_exports
|
|
6535
|
+
"020_add_snapshot_operation_ordinal": _020_add_snapshot_operation_ordinal_exports,
|
|
6536
|
+
"021_add_sync_remote_peer": _021_add_sync_remote_peer_exports,
|
|
6537
|
+
"022_create_sync_holds": _022_create_sync_holds_exports,
|
|
6538
|
+
"023_index_created_protocol_versions": _023_index_created_protocol_versions_exports
|
|
6165
6539
|
};
|
|
6166
6540
|
var ProgrammaticMigrationProvider = class {
|
|
6167
6541
|
getMigrations() {
|
|
@@ -6361,6 +6735,6 @@ function createForwardingLogger(post, level = "info", tags = []) {
|
|
|
6361
6735
|
};
|
|
6362
6736
|
}
|
|
6363
6737
|
//#endregion
|
|
6364
|
-
export {
|
|
6738
|
+
export { OptimisticLockError as $, DEFAULT_DEFERRED_JOB_TTL_MS as A, summarizeSubmittedActions as B, SettledWatermark as C, snapshotFunctionsFor as D, parseSnapshot as E, ModuleNotFoundError as F, authDecisionModel as G, decideAtHead as H, GATED_DOCUMENT_ACTIONS as I, APPEND_CONDITION_FAILED_PREFIX as J, buildDecisionModel as K, createConsistencyToken as L, DuplicateManifestError as M, DuplicateModuleError as N, DocumentMetaCache as O, InvalidModuleError as P, DuplicateOperationError as Q, createEmptyConsistencyToken as R, ProbeSettler as S, describeWaitingSessions as T, selectDecisionModel as U, targetDocumentId as V, documentDecisionModel as W, DocumentAlreadyExistsError as X, AppendConditionFailedError as Y, DocumentExistence as Z, KyselyExecutionScope as _, throwIfAborted as _t, DEFAULT_DRIVE_CONTAINER_TYPES as a, DocumentNotFoundError as at, KyselyWriteCache as b, runMigrations as c, InvalidSignatureError as ct, KyselyOperationStore as d, UnsupportedStoredProtocolError as dt, RevisionMismatchError as et, KyselyKeyframeStore as f, UpgradePreconditionFailedError as ft, PassthroughSigner as g, parsePagingOptions as gt, verifyActionSignature as h, matchesScope as ht, fromErrorInfo as i, DocumentDeletedError as it, JobExecutorEventTypes as j, CollectionMembershipCache as k, createForwardingPoolInstrumentation as l, RelationshipNotFoundError as lt, SimpleJobExecutor as m, ReactorEventTypes as mt, errorToInfo as n, AuthTimestampNotMonotonicError as nt, REACTOR_SCHEMA as o, ExcessiveReshuffleError as ot, DocumentModelRegistry as p, EventBusAggregateError as pt, DriveCollectionId as q, sanitizeArg as r, AuthorizationDeniedError as rt, getMigrationStatus as s, InvalidOperationTimestampError as st, createForwardingLogger as t, AuthEnforcementDisabledError as tt, instrumentPgPool as u, UnsupportedProtocolVersionError as ut, resolveFeatureFlags as v, yieldToMain as vt, createKyselyWatermarkProbe as w, KyselyOperationIndex as x, EventBus as y, __exportAll as yt, submittedActionIds as z };
|
|
6365
6739
|
|
|
6366
|
-
//# sourceMappingURL=forwarding-logger-
|
|
6740
|
+
//# sourceMappingURL=forwarding-logger-BkwlvD9W.js.map
|