@opendatalabs/personal-server-ts-server 1.34.2 → 1.35.1
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/api-auth.d.ts +1 -0
- package/dist/api-auth.d.ts.map +1 -1
- package/dist/api-auth.js.map +1 -1
- package/dist/api.d.ts +3 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +3 -0
- package/dist/api.js.map +1 -1
- package/dist/app.d.ts +14 -2
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +64 -8
- package/dist/app.js.map +1 -1
- package/dist/bootstrap.d.ts +19 -0
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +87 -4
- package/dist/bootstrap.js.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +7 -0
- package/dist/client.js.map +1 -1
- package/dist/enclave-main.d.ts.map +1 -1
- package/dist/enclave-main.js +0 -2
- package/dist/enclave-main.js.map +1 -1
- package/dist/mcp/durable-state.d.ts +2 -1
- package/dist/mcp/durable-state.d.ts.map +1 -1
- package/dist/mcp/durable-state.js +35 -1
- package/dist/mcp/durable-state.js.map +1 -1
- package/dist/pdpp/bootstrap.d.ts.map +1 -1
- package/dist/pdpp/bootstrap.js +1 -0
- package/dist/pdpp/bootstrap.js.map +1 -1
- package/dist/pdpp/records-bootstrap.d.ts +47 -1
- package/dist/pdpp/records-bootstrap.d.ts.map +1 -1
- package/dist/pdpp/records-bootstrap.js +78 -2
- package/dist/pdpp/records-bootstrap.js.map +1 -1
- package/dist/pdpp/replica-authorization.d.ts +35 -0
- package/dist/pdpp/replica-authorization.d.ts.map +1 -0
- package/dist/pdpp/replica-authorization.js +234 -0
- package/dist/pdpp/replica-authorization.js.map +1 -0
- package/dist/pending-blob-deletions.d.ts +1 -0
- package/dist/pending-blob-deletions.d.ts.map +1 -1
- package/dist/pending-blob-deletions.js +8 -4
- package/dist/pending-blob-deletions.js.map +1 -1
- package/dist/routes/data.d.ts.map +1 -1
- package/dist/routes/data.js +5 -0
- package/dist/routes/data.js.map +1 -1
- package/dist/routes/mcp.d.ts +18 -2
- package/dist/routes/mcp.d.ts.map +1 -1
- package/dist/routes/mcp.js +111 -11
- package/dist/routes/mcp.js.map +1 -1
- package/dist/routes/oura.d.ts +6 -0
- package/dist/routes/oura.d.ts.map +1 -0
- package/dist/routes/oura.js +272 -0
- package/dist/routes/oura.js.map +1 -0
- package/dist/routes/pdpp-auth-query-keys.d.ts +7 -0
- package/dist/routes/pdpp-auth-query-keys.d.ts.map +1 -0
- package/dist/routes/pdpp-auth-query-keys.js +25 -0
- package/dist/routes/pdpp-auth-query-keys.js.map +1 -0
- package/dist/routes/pdpp-auth.d.ts +3 -1
- package/dist/routes/pdpp-auth.d.ts.map +1 -1
- package/dist/routes/pdpp-auth.js +126 -45
- package/dist/routes/pdpp-auth.js.map +1 -1
- package/dist/routes/pdpp-blobs.d.ts +13 -1
- package/dist/routes/pdpp-blobs.d.ts.map +1 -1
- package/dist/routes/pdpp-blobs.js +100 -65
- package/dist/routes/pdpp-blobs.js.map +1 -1
- package/dist/routes/pdpp-connections.d.ts.map +1 -1
- package/dist/routes/pdpp-connections.js +24 -1
- package/dist/routes/pdpp-connections.js.map +1 -1
- package/dist/routes/pdpp-instance-bindings.d.ts +3 -0
- package/dist/routes/pdpp-instance-bindings.d.ts.map +1 -1
- package/dist/routes/pdpp-instance-bindings.js +63 -0
- package/dist/routes/pdpp-instance-bindings.js.map +1 -1
- package/dist/routes/pdpp-record-query.d.ts +19 -0
- package/dist/routes/pdpp-record-query.d.ts.map +1 -0
- package/dist/routes/pdpp-record-query.js +51 -0
- package/dist/routes/pdpp-record-query.js.map +1 -0
- package/dist/routes/pdpp-records.d.ts +12 -1
- package/dist/routes/pdpp-records.d.ts.map +1 -1
- package/dist/routes/pdpp-records.js +120 -233
- package/dist/routes/pdpp-records.js.map +1 -1
- package/dist/routes/pdpp-replica-quarantine-fixture.d.ts +65 -0
- package/dist/routes/pdpp-replica-quarantine-fixture.d.ts.map +1 -0
- package/dist/routes/pdpp-replica-quarantine-fixture.js +389 -0
- package/dist/routes/pdpp-replica-quarantine-fixture.js.map +1 -0
- package/dist/storage/claude-method-transition.d.ts +12 -0
- package/dist/storage/claude-method-transition.d.ts.map +1 -0
- package/dist/storage/claude-method-transition.js +139 -0
- package/dist/storage/claude-method-transition.js.map +1 -0
- package/dist/storage/import-operations.d.ts +16 -0
- package/dist/storage/import-operations.d.ts.map +1 -1
- package/dist/storage/import-operations.js +54 -1
- package/dist/storage/import-operations.js.map +1 -1
- package/dist/storage/index-schema.d.ts +1 -1
- package/dist/storage/index-schema.d.ts.map +1 -1
- package/dist/storage/index-schema.js +22 -5
- package/dist/storage/index-schema.js.map +1 -1
- package/dist/storage/legacy-scope-authority.d.ts.map +1 -1
- package/dist/storage/legacy-scope-authority.js +83 -11
- package/dist/storage/legacy-scope-authority.js.map +1 -1
- package/dist/storage/node-data-storage.d.ts.map +1 -1
- package/dist/storage/node-data-storage.js +58 -2
- package/dist/storage/node-data-storage.js.map +1 -1
- package/dist/storage/pdpp-binary-chunks.d.ts +120 -0
- package/dist/storage/pdpp-binary-chunks.d.ts.map +1 -0
- package/dist/storage/pdpp-binary-chunks.js +480 -0
- package/dist/storage/pdpp-binary-chunks.js.map +1 -0
- package/dist/storage/pdpp-records-sqlite-store.d.ts +96 -15
- package/dist/storage/pdpp-records-sqlite-store.d.ts.map +1 -1
- package/dist/storage/pdpp-records-sqlite-store.js +616 -211
- package/dist/storage/pdpp-records-sqlite-store.js.map +1 -1
- package/dist/storage/pdpp-replica-canonical-port.d.ts +202 -0
- package/dist/storage/pdpp-replica-canonical-port.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-canonical-port.js +451 -0
- package/dist/storage/pdpp-replica-canonical-port.js.map +1 -0
- package/dist/storage/pdpp-replica-coordinator-state.d.ts +16 -0
- package/dist/storage/pdpp-replica-coordinator-state.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-coordinator-state.js +25 -0
- package/dist/storage/pdpp-replica-coordinator-state.js.map +1 -0
- package/dist/storage/pdpp-replica-copies.d.ts +81 -0
- package/dist/storage/pdpp-replica-copies.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-copies.js +59 -0
- package/dist/storage/pdpp-replica-copies.js.map +1 -0
- package/dist/storage/pdpp-replica-reconciler.d.ts +96 -0
- package/dist/storage/pdpp-replica-reconciler.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-reconciler.js +710 -0
- package/dist/storage/pdpp-replica-reconciler.js.map +1 -0
- package/dist/storage/pdpp-replica-sql.d.ts +13 -0
- package/dist/storage/pdpp-replica-sql.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-sql.js +43 -0
- package/dist/storage/pdpp-replica-sql.js.map +1 -0
- package/dist/storage/pdpp-replica-staging.d.ts +36 -0
- package/dist/storage/pdpp-replica-staging.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-staging.js +202 -0
- package/dist/storage/pdpp-replica-staging.js.map +1 -0
- package/dist/storage/pdpp-replica-visibility-view.d.ts +64 -0
- package/dist/storage/pdpp-replica-visibility-view.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-visibility-view.js +1397 -0
- package/dist/storage/pdpp-replica-visibility-view.js.map +1 -0
- package/dist/storage/pdpp-replica-writer-identity.d.ts +64 -0
- package/dist/storage/pdpp-replica-writer-identity.d.ts.map +1 -0
- package/dist/storage/pdpp-replica-writer-identity.js +81 -0
- package/dist/storage/pdpp-replica-writer-identity.js.map +1 -0
- package/dist/storage/pdpp-replication-door.d.ts +60 -0
- package/dist/storage/pdpp-replication-door.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-door.js +203 -0
- package/dist/storage/pdpp-replication-door.js.map +1 -0
- package/dist/storage/pdpp-replication-host.d.ts +67 -0
- package/dist/storage/pdpp-replication-host.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-host.js +159 -0
- package/dist/storage/pdpp-replication-host.js.map +1 -0
- package/dist/storage/pdpp-replication-memory.fixture.d.ts +2 -0
- package/dist/storage/pdpp-replication-memory.fixture.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-memory.fixture.js +267 -0
- package/dist/storage/pdpp-replication-memory.fixture.js.map +1 -0
- package/dist/storage/pdpp-replication-preparation.d.ts +3 -0
- package/dist/storage/pdpp-replication-preparation.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-preparation.js +3 -0
- package/dist/storage/pdpp-replication-preparation.js.map +1 -0
- package/dist/storage/pdpp-replication-promotion-memory.fixture.d.ts +2 -0
- package/dist/storage/pdpp-replication-promotion-memory.fixture.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-promotion-memory.fixture.js +65 -0
- package/dist/storage/pdpp-replication-promotion-memory.fixture.js.map +1 -0
- package/dist/storage/pdpp-replication-receiver.d.ts +230 -0
- package/dist/storage/pdpp-replication-receiver.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-receiver.js +950 -0
- package/dist/storage/pdpp-replication-receiver.js.map +1 -0
- package/dist/storage/pdpp-replication-sqlite.d.ts +95 -0
- package/dist/storage/pdpp-replication-sqlite.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-sqlite.js +615 -0
- package/dist/storage/pdpp-replication-sqlite.js.map +1 -0
- package/dist/storage/pdpp-replication-transport.d.ts +39 -0
- package/dist/storage/pdpp-replication-transport.d.ts.map +1 -0
- package/dist/storage/pdpp-replication-transport.js +196 -0
- package/dist/storage/pdpp-replication-transport.js.map +1 -0
- package/dist/storage/schema-versions.d.ts +5 -0
- package/dist/storage/schema-versions.d.ts.map +1 -0
- package/dist/storage/schema-versions.js +26 -0
- package/dist/storage/schema-versions.js.map +1 -0
- package/dist/storage/strava-method-transition.d.ts +18 -0
- package/dist/storage/strava-method-transition.d.ts.map +1 -0
- package/dist/storage/strava-method-transition.js +209 -0
- package/dist/storage/strava-method-transition.js.map +1 -0
- package/dist/ui/ps-lite-debug.js +154947 -124896
- package/package.json +3 -3
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { CursorExpiredError, InvalidCursorError, } from "@opendatalabs/personal-server-ts-core/storage/pdpp-records";
|
|
3
|
+
import { BINARY_CHUNKS_MIGRATION, binaryLength, clearMalformedManifest, readBinary, verifyBinary, writeCanonicalBinary, } from "./pdpp-binary-chunks.js";
|
|
4
|
+
import { REPLICA_COPY_MIGRATION } from "./pdpp-replica-copies.js";
|
|
5
|
+
import { RECEIVER_MIGRATION, REPLICA_FACT_REMOVAL_MIGRATION, REPLICA_INTEGRITY_MIGRATION, } from "./pdpp-replication-receiver.js";
|
|
6
|
+
import { createReplicationJournal, REPLICATION_MIGRATION, REPLICA_BINDING_HISTORY_MIGRATION, REPLICA_BLOB_CLAIM_INDEX_MIGRATION, } from "./pdpp-replication-sqlite.js";
|
|
7
|
+
import { currentReplicaWriterIdentity, REPLICA_WRITER_IDENTITY_MIGRATION, } from "./pdpp-replica-writer-identity.js";
|
|
8
|
+
import { createSqliteRecordStore, PdppBindingError, } from "./pdpp-records-sqlite-store.js";
|
|
9
|
+
import { admitReplication, captureWrites } from "./pdpp-replication-door.js";
|
|
10
|
+
/**
|
|
11
|
+
* The replica schema. It is versioned by `pdpp_replica_schema_version`, apart
|
|
12
|
+
* from the canonical `MIGRATIONS` chain, and only `ensureReplicaSchema` applies
|
|
13
|
+
* it. A database that never enabled replication has none of these tables, and
|
|
14
|
+
* a canonical build that knows nothing of them still opens one that did.
|
|
15
|
+
*/
|
|
16
|
+
export const REPLICA_MIGRATIONS = [
|
|
17
|
+
// r1: installation writer and the log of its own writes. The owner's local view
|
|
18
|
+
// of a binding-conflicted connection projects the log
|
|
19
|
+
// (pdpp-replica-visibility-view.ts).
|
|
20
|
+
`
|
|
21
|
+
CREATE TABLE IF NOT EXISTS pdpp_installation_writer (
|
|
22
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
23
|
+
writer_id TEXT NOT NULL UNIQUE
|
|
24
|
+
);
|
|
25
|
+
CREATE TABLE IF NOT EXISTS pdpp_replica_own_writes (
|
|
26
|
+
sequence INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
27
|
+
operation_id TEXT NOT NULL UNIQUE,
|
|
28
|
+
writer_id TEXT NOT NULL,
|
|
29
|
+
source_id TEXT,
|
|
30
|
+
connection_id TEXT NOT NULL,
|
|
31
|
+
method TEXT,
|
|
32
|
+
generation INTEGER CHECK (generation > 0),
|
|
33
|
+
stream TEXT NOT NULL,
|
|
34
|
+
record_key TEXT NOT NULL,
|
|
35
|
+
operation TEXT NOT NULL CHECK (operation IN ('upsert', 'delete')),
|
|
36
|
+
value_json TEXT NOT NULL,
|
|
37
|
+
blob_ids_json TEXT NOT NULL
|
|
38
|
+
);
|
|
39
|
+
-- REPLACE's implicit delete need not fire DELETE triggers. Guard both identities
|
|
40
|
+
-- before insertion as well, independent of recursive_triggers configuration.
|
|
41
|
+
CREATE TRIGGER IF NOT EXISTS pdpp_replica_own_writes_no_replace BEFORE INSERT ON pdpp_replica_own_writes
|
|
42
|
+
WHEN EXISTS (SELECT 1 FROM pdpp_replica_own_writes
|
|
43
|
+
WHERE sequence = NEW.sequence OR operation_id = NEW.operation_id)
|
|
44
|
+
BEGIN SELECT RAISE(ABORT, 'local write log is append-only'); END;
|
|
45
|
+
CREATE TRIGGER IF NOT EXISTS pdpp_replica_own_writes_no_update BEFORE UPDATE ON pdpp_replica_own_writes
|
|
46
|
+
BEGIN SELECT RAISE(ABORT, 'local write log is append-only'); END;
|
|
47
|
+
CREATE TRIGGER IF NOT EXISTS pdpp_replica_own_writes_no_delete BEFORE DELETE ON pdpp_replica_own_writes
|
|
48
|
+
BEGIN SELECT RAISE(ABORT, 'local write log is append-only'); END;
|
|
49
|
+
`,
|
|
50
|
+
REPLICATION_MIGRATION,
|
|
51
|
+
RECEIVER_MIGRATION,
|
|
52
|
+
// Earlier builds also created the persistent trigger
|
|
53
|
+
// `pdpp_canonical_chunks_delete` on `pdpp_blobs` here. It would fire on a
|
|
54
|
+
// plain open's blob deletes, so `ensureReplicaSchema` drops it and the
|
|
55
|
+
// port recreates it as a TEMP trigger. See `CANONICAL_CHUNKS_TRIGGER`.
|
|
56
|
+
BINARY_CHUNKS_MIGRATION,
|
|
57
|
+
// Integrity fences and binding-conflict facts.
|
|
58
|
+
REPLICA_INTEGRITY_MIGRATION,
|
|
59
|
+
// Batch copies, finalized facts, dispositions, receipts.
|
|
60
|
+
REPLICA_COPY_MIGRATION,
|
|
61
|
+
// Removing a quarantine fact raises the integrity epoch.
|
|
62
|
+
REPLICA_FACT_REMOVAL_MIGRATION,
|
|
63
|
+
// Indexed blob-claim existence lookup for captureBlob.
|
|
64
|
+
REPLICA_BLOB_CLAIM_INDEX_MIGRATION,
|
|
65
|
+
// Writer ids derived from the registered server key (host contract §3.2).
|
|
66
|
+
REPLICA_WRITER_IDENTITY_MIGRATION,
|
|
67
|
+
// Binding generations and declaration history (host contract §4.5).
|
|
68
|
+
REPLICA_BINDING_HISTORY_MIGRATION,
|
|
69
|
+
// Capture pins that reference canonical chunks instead of copying them.
|
|
70
|
+
// See `pinReferenceKeepsBytes`.
|
|
71
|
+
`CREATE TABLE IF NOT EXISTS pdpp_replica_blob_pins (blob_id TEXT PRIMARY KEY);`,
|
|
72
|
+
];
|
|
73
|
+
/**
|
|
74
|
+
* Apply the replica schema. Call it only when replication is enabled, after
|
|
75
|
+
* the canonical store migrated the database. Idempotent.
|
|
76
|
+
*/
|
|
77
|
+
export function ensureReplicaSchema(db) {
|
|
78
|
+
db.transaction(() => {
|
|
79
|
+
db.exec(`
|
|
80
|
+
CREATE TABLE IF NOT EXISTS pdpp_replica_schema_version (
|
|
81
|
+
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
82
|
+
version INTEGER NOT NULL
|
|
83
|
+
);
|
|
84
|
+
INSERT OR IGNORE INTO pdpp_replica_schema_version (id, version) VALUES (1, 0);
|
|
85
|
+
`);
|
|
86
|
+
const { version } = db
|
|
87
|
+
.prepare("SELECT version FROM pdpp_replica_schema_version WHERE id = 1")
|
|
88
|
+
.get();
|
|
89
|
+
if (version > REPLICA_MIGRATIONS.length)
|
|
90
|
+
throw new Error(`PDPP replica schema version ${version} is newer than this build supports (${REPLICA_MIGRATIONS.length}). Refusing to open.`);
|
|
91
|
+
for (let v = version; v < REPLICA_MIGRATIONS.length; v++) {
|
|
92
|
+
db.exec(REPLICA_MIGRATIONS[v]);
|
|
93
|
+
if (v === 0)
|
|
94
|
+
db.prepare("INSERT OR IGNORE INTO pdpp_installation_writer (id, writer_id) VALUES (1, ?)").run(randomUUID());
|
|
95
|
+
}
|
|
96
|
+
db.prepare("UPDATE pdpp_replica_schema_version SET version = ? WHERE id = 1").run(REPLICA_MIGRATIONS.length);
|
|
97
|
+
// A database a canary build opened has the chunk trigger as a persistent
|
|
98
|
+
// object. Replica triggers on canonical tables must exist only on a
|
|
99
|
+
// connection that enabled replication, so remove it.
|
|
100
|
+
db.exec("DROP TRIGGER IF EXISTS pdpp_canonical_chunks_delete");
|
|
101
|
+
// A plain open can delete a blob without clearing its replica chunks.
|
|
102
|
+
// Clear what such deletes left, on every enabled open; a pin keeps them.
|
|
103
|
+
const orphans = `SELECT blob_id FROM pdpp_binary_manifests WHERE namespace='canonical' AND blob_id NOT IN (SELECT blob_id FROM pdpp_blobs)`;
|
|
104
|
+
const keep = pinReferenceKeepsBytes("@id").map((sql) => db.prepare(sql));
|
|
105
|
+
for (const { blob_id } of db.prepare(orphans).all())
|
|
106
|
+
for (const statement of keep)
|
|
107
|
+
statement.run({ id: blob_id });
|
|
108
|
+
db.exec(`
|
|
109
|
+
DELETE FROM pdpp_binary_chunks WHERE namespace='canonical' AND blob_id NOT IN (SELECT blob_id FROM pdpp_blobs);
|
|
110
|
+
DELETE FROM pdpp_binary_manifests WHERE namespace='canonical' AND blob_id NOT IN (SELECT blob_id FROM pdpp_blobs);
|
|
111
|
+
`);
|
|
112
|
+
}).immediate();
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* A capture pin of a large blob references its canonical chunks; it holds no
|
|
116
|
+
* copy (`captureBlob`). Before canonical storage drops such a blob, its
|
|
117
|
+
* chunks change namespace to `pinned`, so export can still read them. No byte
|
|
118
|
+
* is copied, and the pin row goes: the pinned copy now holds the bytes.
|
|
119
|
+
*/
|
|
120
|
+
function pinReferenceKeepsBytes(id) {
|
|
121
|
+
const pinOnly = `EXISTS (SELECT 1 FROM pdpp_replica_blob_pins WHERE blob_id=${id}) AND NOT EXISTS (SELECT 1 FROM pdpp_binary_manifests WHERE namespace='pinned' AND blob_id=${id})`;
|
|
122
|
+
return [
|
|
123
|
+
`UPDATE pdpp_binary_chunks SET namespace='pinned' WHERE namespace='canonical' AND blob_id=${id} AND ${pinOnly};`,
|
|
124
|
+
`INSERT INTO pdpp_binary_manifests SELECT 'pinned',blob_id,size_bytes FROM pdpp_binary_manifests WHERE namespace='canonical' AND blob_id=${id} AND ${pinOnly};`,
|
|
125
|
+
`DELETE FROM pdpp_replica_blob_pins WHERE blob_id=${id} AND EXISTS (SELECT 1 FROM pdpp_binary_manifests WHERE namespace='pinned' AND blob_id=${id});`,
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Deleting a canonical blob deletes its replica chunks. TEMP, so it lives on
|
|
130
|
+
* the connection that enabled replication and never fires for a plain open's
|
|
131
|
+
* writes; a plain open leaves the chunks, and the next enabled open clears
|
|
132
|
+
* them (see `ensureReplicaSchema`).
|
|
133
|
+
*/
|
|
134
|
+
const CANONICAL_CHUNKS_TRIGGER = `CREATE TEMP TRIGGER IF NOT EXISTS pdpp_canonical_chunks_delete AFTER DELETE ON pdpp_blobs BEGIN ${pinReferenceKeepsBytes("OLD.blob_id").join(" ")} DELETE FROM pdpp_binary_chunks WHERE namespace='canonical' AND blob_id=OLD.blob_id; DELETE FROM pdpp_binary_manifests WHERE namespace='canonical' AND blob_id=OLD.blob_id; END`;
|
|
135
|
+
function assertReplicaSchema(db) {
|
|
136
|
+
const row = db
|
|
137
|
+
.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'pdpp_replica_schema_version'")
|
|
138
|
+
.get()
|
|
139
|
+
? db
|
|
140
|
+
.prepare("SELECT version FROM pdpp_replica_schema_version WHERE id = 1")
|
|
141
|
+
.get()
|
|
142
|
+
: undefined;
|
|
143
|
+
if (row?.version !== REPLICA_MIGRATIONS.length)
|
|
144
|
+
throw new Error("Readonly PDPP replica store requires the current schema");
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Shared read predicate for canonical, replica-preview, and known-local
|
|
148
|
+
* projections. A key fact hides rows of exactly its generation. Without
|
|
149
|
+
* `generationSql` that is the binding's current generation (canonical rows).
|
|
150
|
+
* With it, it is the row's own generation, for overlay and local rows that can
|
|
151
|
+
* belong to a generation a pending reset introduces. Both halves use
|
|
152
|
+
* equality, so an overlay row and the canonical row of one generation always
|
|
153
|
+
* agree.
|
|
154
|
+
*/
|
|
155
|
+
export function unquarantinedRecordSql(alias, generationSql) {
|
|
156
|
+
const connection = `NOT EXISTS (SELECT 1 FROM pdpp_replica_quarantined_connections q WHERE q.instance = ${alias}.instance)`;
|
|
157
|
+
const key = (scope) => `NOT EXISTS (SELECT 1 FROM pdpp_replica_quarantined_keys q ${scope} q.instance=${alias}.instance AND q.stream=${alias}.stream AND q.record_key=${alias}.record_key`;
|
|
158
|
+
return generationSql === undefined
|
|
159
|
+
? `${connection} AND ${key("JOIN pdpp_instance_binding b ON b.instance=q.instance AND b.generation=q.generation WHERE")})`
|
|
160
|
+
: `${connection} AND ${key("WHERE")} AND q.generation = (${generationSql}))`;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Build the replica port of a canonical database. Applies the replica schema
|
|
164
|
+
* first (readonly handles must already have it).
|
|
165
|
+
*/
|
|
166
|
+
export function createReplicaCanonicalPort(db) {
|
|
167
|
+
if (db.readonly)
|
|
168
|
+
assertReplicaSchema(db);
|
|
169
|
+
else {
|
|
170
|
+
// Refuse behind a closed door before any replica write.
|
|
171
|
+
admitReplication(db);
|
|
172
|
+
ensureReplicaSchema(db);
|
|
173
|
+
db.exec(CANONICAL_CHUNKS_TRIGGER);
|
|
174
|
+
}
|
|
175
|
+
const writerId = db
|
|
176
|
+
.prepare("SELECT writer_id FROM pdpp_installation_writer WHERE id = 1")
|
|
177
|
+
.get().writer_id;
|
|
178
|
+
// Captured operations name the derived operation writer. Before the first
|
|
179
|
+
// enable they name the installation writer and stay local: no receiver
|
|
180
|
+
// accepts them, and enabling refuses a journal that has them.
|
|
181
|
+
let journalObserver;
|
|
182
|
+
const journal = createReplicationJournal(db, () => currentReplicaWriterIdentity(db)?.operationWriter ?? writerId, () => journalObserver?.());
|
|
183
|
+
const insertLocalWrite = db.prepare(`
|
|
184
|
+
INSERT INTO pdpp_replica_own_writes
|
|
185
|
+
(operation_id, writer_id, source_id, connection_id, method, generation,
|
|
186
|
+
stream, record_key, operation, value_json, blob_ids_json)
|
|
187
|
+
VALUES (@operation_id, @writer_id, @source_id, @connection_id, @method, @generation,
|
|
188
|
+
@stream, @record_key, @operation, @value_json, @blob_ids_json)
|
|
189
|
+
`);
|
|
190
|
+
let visibilityObserver;
|
|
191
|
+
let writeBoundary;
|
|
192
|
+
let readBoundary;
|
|
193
|
+
let localWriteObserver;
|
|
194
|
+
// Keys written since the last settle. A TEMP table, so a rolled-back write
|
|
195
|
+
// (at any transaction or savepoint depth) takes its queued key with it and is
|
|
196
|
+
// never reconciled. It lives on this connection only and is not part of the
|
|
197
|
+
// database file.
|
|
198
|
+
db.exec("CREATE TEMP TABLE IF NOT EXISTS pdpp_replica_unsettled_writes (instance TEXT NOT NULL, stream TEXT NOT NULL, record_key TEXT NOT NULL, PRIMARY KEY (instance, stream, record_key))");
|
|
199
|
+
const queueLocalWrite = db.prepare("INSERT OR IGNORE INTO pdpp_replica_unsettled_writes VALUES (?, ?, ?)");
|
|
200
|
+
const takeLocalWrites = db.prepare("DELETE FROM pdpp_replica_unsettled_writes RETURNING instance, stream, record_key");
|
|
201
|
+
function replicaIntegrityEpoch(connectionKey) {
|
|
202
|
+
return db
|
|
203
|
+
.prepare(`SELECT coalesce((SELECT fences FROM pdpp_replica_integrity_fences WHERE instance = ?), 0)
|
|
204
|
+
+ coalesce((SELECT reset_clock FROM pdpp_instance_binding WHERE instance = ?), 0) AS epoch`)
|
|
205
|
+
.get(connectionKey, connectionKey).epoch;
|
|
206
|
+
}
|
|
207
|
+
function replicaVisibilityEpoch(instanceIds) {
|
|
208
|
+
if (instanceIds) {
|
|
209
|
+
const ids = [...new Set(instanceIds)].sort();
|
|
210
|
+
const state = ids.map((id) => [id, replicaIntegrityEpoch(id)]);
|
|
211
|
+
return createHash("sha256").update(JSON.stringify(state)).digest("hex");
|
|
212
|
+
}
|
|
213
|
+
const exists = db
|
|
214
|
+
.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='pdpp_replica_visibility_epoch'")
|
|
215
|
+
.get();
|
|
216
|
+
if (!exists)
|
|
217
|
+
return "uninitialized";
|
|
218
|
+
const row = db
|
|
219
|
+
.prepare("SELECT epoch FROM pdpp_replica_visibility_epoch WHERE id=1")
|
|
220
|
+
.get();
|
|
221
|
+
return row ? String(row.epoch) : "uninitialized";
|
|
222
|
+
}
|
|
223
|
+
function fenceReplicaConnection(connectionKey) {
|
|
224
|
+
if (!db.inTransaction)
|
|
225
|
+
throw new Error("an integrity fence requires the hiding transaction");
|
|
226
|
+
db.prepare("INSERT INTO pdpp_replica_integrity_fences VALUES (?, 1) ON CONFLICT(instance) DO UPDATE SET fences = fences + 1").run(connectionKey);
|
|
227
|
+
}
|
|
228
|
+
function cursorBinding(instanceIds, fields) {
|
|
229
|
+
const ids = [...new Set(instanceIds)].sort();
|
|
230
|
+
const integrity = Object.fromEntries(ids.map((id) => [id, replicaIntegrityEpoch(id)]));
|
|
231
|
+
return {
|
|
232
|
+
query: createHash("sha256")
|
|
233
|
+
.update(JSON.stringify([ids, fields ? [...fields].sort() : null]))
|
|
234
|
+
.digest("hex"),
|
|
235
|
+
integrity,
|
|
236
|
+
visibilityEpoch: replicaVisibilityEpoch(ids),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
const port = {
|
|
240
|
+
unquarantinedRecordSql,
|
|
241
|
+
replicaVisibilityEpoch,
|
|
242
|
+
assertKeyWritable(instance, stream, recordKey) {
|
|
243
|
+
if (db
|
|
244
|
+
.prepare("SELECT 1 FROM pdpp_replica_quarantined_keys q JOIN pdpp_instance_binding b ON q.instance = b.instance AND q.generation = b.generation WHERE q.instance = ? AND q.stream = ? AND q.record_key = ?")
|
|
245
|
+
.get(instance, stream, recordKey))
|
|
246
|
+
throw new PdppBindingError("replica_integrity_quarantine");
|
|
247
|
+
},
|
|
248
|
+
assertConnectionWritable(instance) {
|
|
249
|
+
if (db
|
|
250
|
+
.prepare("SELECT 1 FROM pdpp_replica_quarantined_connections WHERE instance = ?")
|
|
251
|
+
.get(instance))
|
|
252
|
+
throw new PdppBindingError("replica_integrity_quarantine");
|
|
253
|
+
},
|
|
254
|
+
assertMethodTransitionAllowed() {
|
|
255
|
+
if (db.prepare("SELECT 1 FROM pdpp_replica_settings").get())
|
|
256
|
+
throw new PdppBindingError("replica_method_transition_unsupported");
|
|
257
|
+
},
|
|
258
|
+
assertStreamWritable(instance, stream, generation) {
|
|
259
|
+
if (db
|
|
260
|
+
.prepare("SELECT 1 FROM pdpp_replica_quarantined_keys WHERE instance = ? AND stream = ? AND generation = ? LIMIT 1")
|
|
261
|
+
.get(instance, stream, generation))
|
|
262
|
+
throw new PdppBindingError("replica_integrity_quarantine");
|
|
263
|
+
},
|
|
264
|
+
recordWritten(instance, stream, recordKey, semantics, primaryKey, decl) {
|
|
265
|
+
// Do not use getInstanceBinding's synthetic defaults for a missing legacy row.
|
|
266
|
+
const binding = db
|
|
267
|
+
.prepare("SELECT source_id, method, generation FROM pdpp_instance_binding WHERE instance = ?")
|
|
268
|
+
.get(instance);
|
|
269
|
+
const row = db
|
|
270
|
+
.prepare("SELECT * FROM pdpp_records WHERE instance = ? AND stream = ? AND record_key = ?")
|
|
271
|
+
.get(instance, stream, recordKey);
|
|
272
|
+
const value = row.deleted
|
|
273
|
+
? {
|
|
274
|
+
instance,
|
|
275
|
+
stream,
|
|
276
|
+
recordKey,
|
|
277
|
+
version: row.version,
|
|
278
|
+
deletedAt: row.deleted_at ?? row.emitted_at,
|
|
279
|
+
emittedAt: row.emitted_at,
|
|
280
|
+
deleted: true,
|
|
281
|
+
}
|
|
282
|
+
: {
|
|
283
|
+
instance,
|
|
284
|
+
stream,
|
|
285
|
+
recordKey,
|
|
286
|
+
data: row.data ? JSON.parse(row.data) : {},
|
|
287
|
+
version: row.version,
|
|
288
|
+
emittedAt: row.emitted_at,
|
|
289
|
+
deleted: false,
|
|
290
|
+
};
|
|
291
|
+
const blobId = row.deleted ? null : row.blob_id;
|
|
292
|
+
insertLocalWrite.run({
|
|
293
|
+
operation_id: randomUUID(),
|
|
294
|
+
writer_id: writerId,
|
|
295
|
+
source_id: binding?.source_id ?? null,
|
|
296
|
+
connection_id: instance,
|
|
297
|
+
method: binding?.method ?? null,
|
|
298
|
+
generation: binding?.generation ?? null,
|
|
299
|
+
stream,
|
|
300
|
+
record_key: recordKey,
|
|
301
|
+
operation: row.deleted ? "delete" : "upsert",
|
|
302
|
+
value_json: JSON.stringify(value),
|
|
303
|
+
blob_ids_json: JSON.stringify(blobId ? [blobId] : []),
|
|
304
|
+
});
|
|
305
|
+
journal.captureRecord(instance, stream, recordKey, semantics, primaryKey, decl);
|
|
306
|
+
queueLocalWrite.run(instance, stream, recordKey);
|
|
307
|
+
visibilityObserver?.();
|
|
308
|
+
},
|
|
309
|
+
settleLocalWrites() {
|
|
310
|
+
const writes = takeLocalWrites.all();
|
|
311
|
+
for (const { instance, stream, record_key } of writes)
|
|
312
|
+
localWriteObserver?.(instance, stream, record_key);
|
|
313
|
+
},
|
|
314
|
+
captureInvalidation: journal.captureInvalidation,
|
|
315
|
+
captureCompletion: journal.captureCompletion,
|
|
316
|
+
captureLifecycle: (instance, kind) => journal.captureLifecycle(instance, kind),
|
|
317
|
+
captureBlob: journal.captureBlob,
|
|
318
|
+
notifyVisibility: () => visibilityObserver?.(),
|
|
319
|
+
exclusive: (write) => {
|
|
320
|
+
const captured = () => captureWrites(db, write);
|
|
321
|
+
return writeBoundary
|
|
322
|
+
? writeBoundary(captured)
|
|
323
|
+
: db.transaction(captured).immediate();
|
|
324
|
+
},
|
|
325
|
+
snapshot: (read) => readBoundary ? readBoundary(read) : db.transaction(read).deferred(),
|
|
326
|
+
cursorBinding,
|
|
327
|
+
assertCursorIntegrity(payload, instanceIds, fields) {
|
|
328
|
+
const current = cursorBinding(instanceIds, fields);
|
|
329
|
+
if (payload.query !== undefined && payload.query !== current.query)
|
|
330
|
+
throw new InvalidCursorError();
|
|
331
|
+
if (payload.query === undefined ||
|
|
332
|
+
JSON.stringify(payload.integrity) !==
|
|
333
|
+
JSON.stringify(current.integrity) ||
|
|
334
|
+
payload.visibilityEpoch !== current.visibilityEpoch)
|
|
335
|
+
throw new CursorExpiredError();
|
|
336
|
+
},
|
|
337
|
+
blobBytes: {
|
|
338
|
+
has: (blobId) => db
|
|
339
|
+
.prepare("SELECT 1 FROM pdpp_blob_bytes WHERE blob_id = ?")
|
|
340
|
+
.get(blobId) !== undefined ||
|
|
341
|
+
binaryLength(db, "canonical", blobId) !== undefined,
|
|
342
|
+
// A forged or incomplete manifest cannot force a whole-value allocation.
|
|
343
|
+
readVerified: (meta) => {
|
|
344
|
+
const hasChunkCopy = db
|
|
345
|
+
.prepare("SELECT 1 FROM pdpp_binary_manifests WHERE namespace = 'canonical' AND blob_id = ?")
|
|
346
|
+
.get(meta.blobId) !== undefined;
|
|
347
|
+
if (verifyBinary(db, "canonical", meta.blobId, `sha256:${meta.sha256}`, meta.sizeBytes)) {
|
|
348
|
+
const chunked = readBinary(db, "canonical", meta.blobId);
|
|
349
|
+
if (chunked)
|
|
350
|
+
return chunked;
|
|
351
|
+
}
|
|
352
|
+
if (hasChunkCopy)
|
|
353
|
+
return undefined;
|
|
354
|
+
const row = db
|
|
355
|
+
.prepare("SELECT bytes FROM pdpp_blob_bytes WHERE blob_id = ?")
|
|
356
|
+
.get(meta.blobId);
|
|
357
|
+
if (!row || row.bytes.length !== meta.sizeBytes)
|
|
358
|
+
return undefined;
|
|
359
|
+
const bytes = new Uint8Array(row.bytes);
|
|
360
|
+
return createHash("sha256").update(bytes).digest("hex") === meta.sha256
|
|
361
|
+
? bytes
|
|
362
|
+
: undefined;
|
|
363
|
+
},
|
|
364
|
+
// A malformed manifest vetoes every reader; a blob write repairs it.
|
|
365
|
+
clearMalformed: (blobId) => clearMalformedManifest(db, "canonical", blobId),
|
|
366
|
+
write: (blobId, bytes) => writeCanonicalBinary(db, blobId, bytes),
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
return {
|
|
370
|
+
port,
|
|
371
|
+
writerId,
|
|
372
|
+
exporter: journal.exporter,
|
|
373
|
+
setReplicaVisibilityObserver(observer) {
|
|
374
|
+
visibilityObserver = observer;
|
|
375
|
+
},
|
|
376
|
+
/** The visibility view's `exclusive` and `snapshot`: where canonical transactions begin. */
|
|
377
|
+
setReplicaWriteBoundary(write, read) {
|
|
378
|
+
writeBoundary = write;
|
|
379
|
+
readBoundary = read;
|
|
380
|
+
},
|
|
381
|
+
/**
|
|
382
|
+
* Called for each operation appended to the local journal (a record,
|
|
383
|
+
* blob, completion or lifecycle write; never an import), inside the
|
|
384
|
+
* write's transaction. The observer must defer its effect past the
|
|
385
|
+
* transaction: a rolled-back write may still have called it.
|
|
386
|
+
*/
|
|
387
|
+
setReplicaJournalObserver(observer) {
|
|
388
|
+
journalObserver = observer;
|
|
389
|
+
},
|
|
390
|
+
/** Called for each locally written key when the store settles, inside the write's transaction. */
|
|
391
|
+
setReplicaLocalWriteObserver(observer) {
|
|
392
|
+
localWriteObserver = observer;
|
|
393
|
+
},
|
|
394
|
+
replicaIntegrityEpoch,
|
|
395
|
+
replicaVisibilityEpoch,
|
|
396
|
+
fenceReplicaConnection,
|
|
397
|
+
replicaBindingConflict(connectionId) {
|
|
398
|
+
return !!db
|
|
399
|
+
.prepare("SELECT 1 FROM pdpp_replica_binding_conflicts WHERE instance = ?")
|
|
400
|
+
.get(connectionId);
|
|
401
|
+
},
|
|
402
|
+
/**
|
|
403
|
+
* Record that a connection's evidence disagrees with its binding. The
|
|
404
|
+
* fact is append-only and puts the connection in the conflicted state
|
|
405
|
+
* (the visibility view hides it from apps; the owner sees local rows),
|
|
406
|
+
* and it raises the integrity epoch in the same transaction. Idempotent:
|
|
407
|
+
* returns true only when this call recorded it.
|
|
408
|
+
*/
|
|
409
|
+
markReplicaBindingConflict(input) {
|
|
410
|
+
return db.transaction(() => {
|
|
411
|
+
const recorded = db
|
|
412
|
+
.prepare("INSERT OR IGNORE INTO pdpp_replica_binding_conflicts VALUES (?, ?, ?)")
|
|
413
|
+
.run(input.connectionId, input.reason, JSON.stringify(input.evidence)).changes > 0;
|
|
414
|
+
if (!recorded)
|
|
415
|
+
return false;
|
|
416
|
+
fenceReplicaConnection(input.connectionId);
|
|
417
|
+
return true;
|
|
418
|
+
})();
|
|
419
|
+
},
|
|
420
|
+
cursorBinding,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* The canonical store with replication enabled: replica schema applied,
|
|
425
|
+
* captures, quarantine guards and integrity-bound cursors on. Only the
|
|
426
|
+
* replication composition and replica tests construct it.
|
|
427
|
+
*/
|
|
428
|
+
export function createReplicaRecordStore(db, options = {}) {
|
|
429
|
+
let replica;
|
|
430
|
+
const store = createSqliteRecordStore(db, {
|
|
431
|
+
...options,
|
|
432
|
+
replica: () => (replica = createReplicaCanonicalPort(db)).port,
|
|
433
|
+
});
|
|
434
|
+
const r = replica;
|
|
435
|
+
return Object.assign(store, {
|
|
436
|
+
unquarantinedRecordSql,
|
|
437
|
+
replication: r.exporter,
|
|
438
|
+
getInstallationWriterId: () => r.writerId,
|
|
439
|
+
setReplicaVisibilityObserver: r.setReplicaVisibilityObserver,
|
|
440
|
+
setReplicaWriteBoundary: r.setReplicaWriteBoundary,
|
|
441
|
+
setReplicaLocalWriteObserver: r.setReplicaLocalWriteObserver,
|
|
442
|
+
setReplicaJournalObserver: r.setReplicaJournalObserver,
|
|
443
|
+
replicaIntegrityEpoch: r.replicaIntegrityEpoch,
|
|
444
|
+
replicaVisibilityEpoch: r.replicaVisibilityEpoch,
|
|
445
|
+
fenceReplicaConnection: r.fenceReplicaConnection,
|
|
446
|
+
replicaBindingConflict: r.replicaBindingConflict,
|
|
447
|
+
markReplicaBindingConflict: r.markReplicaBindingConflict,
|
|
448
|
+
cursorBinding: r.cursorBinding,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
//# sourceMappingURL=pdpp-replica-canonical-port.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdpp-replica-canonical-port.js","sourceRoot":"","sources":["../../src/storage/pdpp-replica-canonical-port.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Database } from "better-sqlite3";
|
|
2
|
+
import type { ReplicaCoordinatorStatePort } from "@opendatalabs/personal-server-ts-core/replication/coordinator";
|
|
3
|
+
export { REPLICA_COORDINATOR_SCHEMA } from "@opendatalabs/personal-server-ts-core/replication/coordinator-state";
|
|
4
|
+
/**
|
|
5
|
+
* This adapter does NOT acquire or replace Node's exclusive writer lock.
|
|
6
|
+
* It only fences the existing token inside the receiver's write transaction.
|
|
7
|
+
* Registration intent/recovery tables and their state machine are untouched.
|
|
8
|
+
* The rotation rules are core's, shared with PS-Lite's adapter.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createSqliteReplicaCoordinatorState(input: {
|
|
11
|
+
db: Database;
|
|
12
|
+
owner: () => string | undefined;
|
|
13
|
+
/** Use receiver.atomically: all attempt projection work shares this boundary. */
|
|
14
|
+
atomically: <T>(run: () => T) => T;
|
|
15
|
+
}): ReplicaCoordinatorStatePort;
|
|
16
|
+
//# sourceMappingURL=pdpp-replica-coordinator-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdpp-replica-coordinator-state.d.ts","sourceRoot":"","sources":["../../src/storage/pdpp-replica-coordinator-state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,+DAA+D,CAAC;AAQjH,OAAO,EAAE,0BAA0B,EAAE,MAAM,qEAAqE,CAAC;AAEjH;;;;;GAKG;AACH,wBAAgB,mCAAmC,CAAC,KAAK,EAAE;IACzD,EAAE,EAAE,QAAQ,CAAC;IACb,KAAK,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAChC,iFAAiF;IACjF,UAAU,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;CACpC,GAAG,2BAA2B,CAmB9B"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { commitReplicaCoordinatorState, loadReplicaCoordinatorState, } from "@opendatalabs/personal-server-ts-core/replication/coordinator-state";
|
|
2
|
+
import { runEffectSync } from "@opendatalabs/personal-server-ts-core/replication/effects";
|
|
3
|
+
import { nodeReplicaContext } from "./pdpp-replica-sql.js";
|
|
4
|
+
export { REPLICA_COORDINATOR_SCHEMA } from "@opendatalabs/personal-server-ts-core/replication/coordinator-state";
|
|
5
|
+
/**
|
|
6
|
+
* This adapter does NOT acquire or replace Node's exclusive writer lock.
|
|
7
|
+
* It only fences the existing token inside the receiver's write transaction.
|
|
8
|
+
* Registration intent/recovery tables and their state machine are untouched.
|
|
9
|
+
* The rotation rules are core's, shared with PS-Lite's adapter.
|
|
10
|
+
*/
|
|
11
|
+
export function createSqliteReplicaCoordinatorState(input) {
|
|
12
|
+
const { db } = input;
|
|
13
|
+
const context = nodeReplicaContext(db);
|
|
14
|
+
return {
|
|
15
|
+
load: () => runEffectSync(loadReplicaCoordinatorState(input.owner), context),
|
|
16
|
+
commit(expected, next, checkpoint) {
|
|
17
|
+
return input.atomically(() => {
|
|
18
|
+
if (!db.inTransaction)
|
|
19
|
+
throw new Error("coordinator requires a write transaction");
|
|
20
|
+
return runEffectSync(commitReplicaCoordinatorState(input.owner, expected, next, () => checkpoint()), context);
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=pdpp-replica-coordinator-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdpp-replica-coordinator-state.js","sourceRoot":"","sources":["../../src/storage/pdpp-replica-coordinator-state.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,6BAA6B,EAC7B,2BAA2B,GAC5B,MAAM,qEAAqE,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,2DAA2D,CAAC;AAC1F,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,EAAE,0BAA0B,EAAE,MAAM,qEAAqE,CAAC;AAEjH;;;;;GAKG;AACH,MAAM,UAAU,mCAAmC,CAAC,KAKnD;IACC,MAAM,EAAE,EAAE,EAAE,GAAG,KAAK,CAAC;IACrB,MAAM,OAAO,GAAG,kBAAkB,CAAC,EAAE,CAAC,CAAC;IACvC,OAAO;QACL,IAAI,EAAE,GAAG,EAAE,CACT,aAAa,CAAC,2BAA2B,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAClE,MAAM,CAAC,QAAQ,EAAE,IAAI,EAAE,UAAU;YAC/B,OAAO,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE;gBAC3B,IAAI,CAAC,EAAE,CAAC,aAAa;oBACnB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;gBAC9D,OAAO,aAAa,CAClB,6BAA6B,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,CAC9D,UAAU,EAAE,CACb,EACD,OAAO,CACR,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { Database } from "better-sqlite3";
|
|
2
|
+
import type { ReplicaEnvelope } from "@opendatalabs/personal-server-ts-core/replication/envelope";
|
|
3
|
+
import type { CopyDecision, FinalizedFact } from "@opendatalabs/personal-server-ts-core/replication/copy-promotion";
|
|
4
|
+
import { type StagedCopy } from "@opendatalabs/personal-server-ts-core/replication/copies";
|
|
5
|
+
/**
|
|
6
|
+
* Batch-copy retention, finalized-fact history and batch disposition
|
|
7
|
+
* (contract v3.1, replica-copy-promotion-contract-1021-v3.md) on Node. The
|
|
8
|
+
* transitions are core's (`core/replication/copies.ts`), shared with PS-Lite;
|
|
9
|
+
* this binds them to one better-sqlite3 connection. Every transition is one
|
|
10
|
+
* SQLite transaction and idempotent under replay.
|
|
11
|
+
*/
|
|
12
|
+
export { REPLICA_COPY_MIGRATION, ReplicaCopyRefused, type CopyFragment, type CopyPromotion, type StagedCopy, } from "@opendatalabs/personal-server-ts-core/replication/copies";
|
|
13
|
+
export { REPLICA_REFETCH_BUDGET } from "@opendatalabs/personal-server-ts-core/replication/copy-promotion";
|
|
14
|
+
export declare function createReplicaCopies(deps: {
|
|
15
|
+
db: Database;
|
|
16
|
+
owner: string;
|
|
17
|
+
/** Import an envelope into canonical state (the receiver's import). */
|
|
18
|
+
importEnvelope(envelope: ReplicaEnvelope, blobs: StagedCopy["blobs"], id: string): void;
|
|
19
|
+
/**
|
|
20
|
+
* Withdraw a connection from every reader: connection quarantine, canonical
|
|
21
|
+
* withdrawal and an integrity epoch raise, in the caller's transaction.
|
|
22
|
+
*/
|
|
23
|
+
quarantineConnection(instance: string): void;
|
|
24
|
+
/**
|
|
25
|
+
* The single pending preview of a batch, keyed by id: at most one copy is
|
|
26
|
+
* ever `pending` (contract B), so the read side keeps its id key.
|
|
27
|
+
*/
|
|
28
|
+
preview: {
|
|
29
|
+
show(id: string, envelope: ReplicaEnvelope, blobsJson: string): void;
|
|
30
|
+
hide(id: string, state: "confirmed" | "quarantined"): void;
|
|
31
|
+
};
|
|
32
|
+
/** Drop pending custody bytes no retained copy still needs. */
|
|
33
|
+
releaseBytes(id: string, blobIds: readonly string[]): void;
|
|
34
|
+
now: () => number;
|
|
35
|
+
}): {
|
|
36
|
+
REPLICA_COPY_MIGRATION: string;
|
|
37
|
+
decide: (id: string, refetch: {
|
|
38
|
+
attempts: number;
|
|
39
|
+
budget: number;
|
|
40
|
+
}) => CopyDecision;
|
|
41
|
+
disposition: (id: string) => import("@opendatalabs/personal-server-ts-core/replication/copy-promotion").CopyDisposition;
|
|
42
|
+
copies: (id: string) => {
|
|
43
|
+
copyKey: import("@opendatalabs/personal-server-ts-core/replication/copy-promotion").Hex;
|
|
44
|
+
state: "confirmed" | "pending" | "quarantined";
|
|
45
|
+
reason: "finalized_conflict" | "copy_conflict" | null;
|
|
46
|
+
envelopeDigest: string;
|
|
47
|
+
}[];
|
|
48
|
+
/**
|
|
49
|
+
* Retain one valid copy (contract §3 "Stage copy", amendment C), then
|
|
50
|
+
* decide and apply the quarantine set. Returns the copy key and decision.
|
|
51
|
+
* An input failing a validity check is recorded as a reject only.
|
|
52
|
+
*/
|
|
53
|
+
stage: (copy: StagedCopy, refetch: {
|
|
54
|
+
attempts: number;
|
|
55
|
+
budget: number;
|
|
56
|
+
}) => {
|
|
57
|
+
copyKey: import("@opendatalabs/personal-server-ts-core/replication/copy-promotion").Hex | undefined;
|
|
58
|
+
decision: CopyDecision;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Append a verified finalized fact for one registered slot (F2). A fact
|
|
62
|
+
* from another owner is refused (a diagnostic, never a fact). Returns
|
|
63
|
+
* true when it is new.
|
|
64
|
+
*/
|
|
65
|
+
recordFact: (input: {
|
|
66
|
+
id: string;
|
|
67
|
+
scope: string;
|
|
68
|
+
fragmentIndex: number;
|
|
69
|
+
fact: FinalizedFact;
|
|
70
|
+
}) => boolean;
|
|
71
|
+
/** Apply a non-promoting decision's durable effects (coordinator step). */
|
|
72
|
+
apply(id: string, decision: CopyDecision): void;
|
|
73
|
+
terminate(id: string, reason: string): void;
|
|
74
|
+
/** Promote one copy (contract §3, amendments E and F). */
|
|
75
|
+
promote: (input: {
|
|
76
|
+
id: string;
|
|
77
|
+
copyKey?: string;
|
|
78
|
+
}) => import("@opendatalabs/personal-server-ts-core/replication/copies").CopyPromotion;
|
|
79
|
+
};
|
|
80
|
+
export type ReplicaCopies = ReturnType<typeof createReplicaCopies>;
|
|
81
|
+
//# sourceMappingURL=pdpp-replica-copies.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pdpp-replica-copies.d.ts","sourceRoot":"","sources":["../../src/storage/pdpp-replica-copies.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4DAA4D,CAAC;AAClG,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACd,MAAM,kEAAkE,CAAC;AAM1E,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,0DAA0D,CAAC;AAGlE;;;;;;GAMG;AACH,OAAO,EACL,sBAAsB,EACtB,kBAAkB,EAClB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,0DAA0D,CAAC;AAClE,OAAO,EAAE,sBAAsB,EAAE,MAAM,kEAAkE,CAAC;AAE1G,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,EAAE,EAAE,QAAQ,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,cAAc,CACZ,QAAQ,EAAE,eAAe,EACzB,KAAK,EAAE,UAAU,CAAC,OAAO,CAAC,EAC1B,EAAE,EAAE,MAAM,GACT,IAAI,CAAC;IACR;;;OAGG;IACH,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7C;;;OAGG;IACH,OAAO,EAAE;QACP,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACrE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,aAAa,GAAG,IAAI,CAAC;KAC5D,CAAC;IACF,+DAA+D;IAC/D,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IAC3D,GAAG,EAAE,MAAM,MAAM,CAAC;CACnB;;iBA0BgB,MAAM,WAAW;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;sBAEhD,MAAM;iBACX,MAAM;;;;;;IACnB;;;;OAIG;kBACW,UAAU,WAAW;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE;;;;IAEvE;;;;OAIG;wBACiB;QAClB,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,IAAI,EAAE,aAAa,CAAC;KACrB;IACD,2EAA2E;cACjE,MAAM,YAAY,YAAY;kBAG1B,MAAM,UAAU,MAAM;IAGpC,0DAA0D;qBACzC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE;EAGpD;AACD,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC"}
|