@tinycloud/replica 0.1.0-beta.3 → 0.1.1-beta.0
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/index.cjs +8 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/replica.worker.js +582 -360
- package/dist/replica.worker.js.map +1 -1
- package/dist/sqlite.cjs +64 -4
- package/dist/sqlite.cjs.map +1 -1
- package/dist/sqlite.d.cts +17 -2
- package/dist/sqlite.d.ts +17 -2
- package/dist/sqlite.js +64 -4
- package/dist/sqlite.js.map +1 -1
- package/package.json +2 -2
package/dist/sqlite.js
CHANGED
|
@@ -118,8 +118,20 @@ function wrap(native) {
|
|
|
118
118
|
get: (sql, ...params) => prepared(sql).get(...params) ?? void 0,
|
|
119
119
|
all: (sql, ...params) => prepared(sql).all(...params),
|
|
120
120
|
close: () => {
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
let failure;
|
|
122
|
+
try {
|
|
123
|
+
for (const statement of statements.values()) {
|
|
124
|
+
try {
|
|
125
|
+
statement.finalize?.();
|
|
126
|
+
} catch (error) {
|
|
127
|
+
failure ??= error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
} finally {
|
|
131
|
+
statements.clear();
|
|
132
|
+
native.close();
|
|
133
|
+
}
|
|
134
|
+
if (failure !== void 0) throw failure;
|
|
123
135
|
}
|
|
124
136
|
};
|
|
125
137
|
}
|
|
@@ -133,7 +145,7 @@ async function loadSqlite() {
|
|
|
133
145
|
if (runtime.Bun !== void 0) {
|
|
134
146
|
const module2 = await load(["bun", "sqlite"].join(":"));
|
|
135
147
|
const Database = module2.Database;
|
|
136
|
-
return (path) => wrap(new Database(path, { create: true }));
|
|
148
|
+
return (path, options) => wrap(new Database(path, { create: !options?.readonly, ...options?.readonly ? { readonly: true } : {} }));
|
|
137
149
|
}
|
|
138
150
|
const version = runtime.process?.versions?.node ?? "0.0.0";
|
|
139
151
|
if (!nodeSqliteSupported(version)) {
|
|
@@ -158,7 +170,7 @@ async function loadSqlite() {
|
|
|
158
170
|
process2.emitWarning = emitWarning;
|
|
159
171
|
}
|
|
160
172
|
const DatabaseSync = module.DatabaseSync;
|
|
161
|
-
return (path) => wrap(new DatabaseSync(path));
|
|
173
|
+
return (path, options) => wrap(options?.readonly ? new DatabaseSync(path, { readOnly: true }) : new DatabaseSync(path));
|
|
162
174
|
}
|
|
163
175
|
|
|
164
176
|
// src/sqlite/store.ts
|
|
@@ -278,6 +290,54 @@ var SqliteReplicaStore = class _SqliteReplicaStore {
|
|
|
278
290
|
this.#faults = options[FAULTS] ?? {};
|
|
279
291
|
this.#guard = options.guard ?? ((section) => section());
|
|
280
292
|
}
|
|
293
|
+
/**
|
|
294
|
+
* Read-only status snapshot; does not create a replica, migrate, recover
|
|
295
|
+
* revocation cleanup, or enter the mutation guard. An existing database is
|
|
296
|
+
* opened normally in read-only WAL mode so committed WAL state is visible.
|
|
297
|
+
*/
|
|
298
|
+
static async inspect(dir, options = {}) {
|
|
299
|
+
const checkAbort = () => {
|
|
300
|
+
if (options.signal?.aborted) throw options.signal.reason ?? new ReplicaError(ReplicaErrorCode.CLOSED, "Replica status inspection was cancelled.");
|
|
301
|
+
};
|
|
302
|
+
checkAbort();
|
|
303
|
+
const dbPath = join(dir, "replica.db");
|
|
304
|
+
let ino;
|
|
305
|
+
try {
|
|
306
|
+
ino = (await stat(dbPath)).ino;
|
|
307
|
+
} catch (error) {
|
|
308
|
+
if (errnoOf(error) === "ENOENT") return null;
|
|
309
|
+
throw storageError(error, "Inspecting the replica");
|
|
310
|
+
}
|
|
311
|
+
checkAbort();
|
|
312
|
+
const opener = await loadSqlite();
|
|
313
|
+
checkAbort();
|
|
314
|
+
let db;
|
|
315
|
+
try {
|
|
316
|
+
db = opener(dbPath, { readonly: true });
|
|
317
|
+
db.exec("PRAGMA busy_timeout = 0");
|
|
318
|
+
checkAbort();
|
|
319
|
+
const store = new _SqliteReplicaStore(dir, db, ino, { create: false, ...options.now ? { now: options.now } : {} });
|
|
320
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
321
|
+
const before = db.get("PRAGMA data_version").data_version;
|
|
322
|
+
const state = await store.open();
|
|
323
|
+
checkAbort();
|
|
324
|
+
if (state === null) return null;
|
|
325
|
+
const status = await store.status();
|
|
326
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
327
|
+
checkAbort();
|
|
328
|
+
const after = db.get("PRAGMA data_version").data_version;
|
|
329
|
+
if (before === after) return { state, status };
|
|
330
|
+
}
|
|
331
|
+
throw new ReplicaError(ReplicaErrorCode.BUSY, "The replica changed during status inspection.");
|
|
332
|
+
} catch (error) {
|
|
333
|
+
if (/SQLITE_BUSY|database is locked/i.test(error instanceof Error ? error.message : String(error))) {
|
|
334
|
+
throw new ReplicaError(ReplicaErrorCode.BUSY, "The replica is busy during status inspection.", void 0, { cause: error });
|
|
335
|
+
}
|
|
336
|
+
throw storageError(error, "Inspecting the replica");
|
|
337
|
+
} finally {
|
|
338
|
+
db?.close();
|
|
339
|
+
}
|
|
340
|
+
}
|
|
281
341
|
/**
|
|
282
342
|
* Open the replica store in `dir`. With `create: false` a missing replica is
|
|
283
343
|
* REPLICA_NOT_FOUND and nothing is written to disk.
|
package/dist/sqlite.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sqlite/store.ts","../src/errors.ts","../src/scope.ts","../src/sqlite/driver.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { constants as fsConstants } from \"node:fs\";\nimport { chmod, link, mkdir, open, readFile, readdir, rm, rmdir, stat, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { ReplicaError, ReplicaErrorCode, isReplicaError } from \"../errors.js\";\nimport { effectiveAuthority } from \"../scope.js\";\nimport type {\n Coverage,\n GrantRecord,\n LeaseToken,\n ListOpts,\n LocalEntry,\n LocalReadPolicy,\n ReplicaConfig,\n ReplicaLastError,\n ReplicaState,\n ReplicaStatus,\n ReplicaStore,\n VerifiedPage,\n} from \"../types.js\";\nimport { loadSqlite, type SqliteDatabase, type SqliteOpener, type SqlValue } from \"./driver.js\";\n\nconst SCHEMA_VERSION = 1;\nexport const DURABILITY = \"sqlite-wal-synchronous-full+fsynced-blobs\";\n\nconst SCHEMA = `\nCREATE TABLE IF NOT EXISTS replica (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n name TEXT NOT NULL,\n replica_id TEXT NOT NULL,\n host TEXT NOT NULL,\n space TEXT NOT NULL,\n prefix TEXT NOT NULL,\n device_did TEXT NOT NULL,\n allow_secrets INTEGER NOT NULL CHECK (allow_secrets IN (0, 1)),\n local_read_policy TEXT NOT NULL CHECK (local_read_policy IN ('whileGrantValid', 'retainAfterExpiry')),\n retention_grant_cid TEXT,\n grant_cid TEXT,\n grant_bytes BLOB,\n grant_audience TEXT,\n grant_issuer TEXT,\n grant_nbf INTEGER,\n grant_exp INTEGER,\n pending_grant_cid TEXT,\n pending_grant_bytes BLOB,\n pending_grant_audience TEXT,\n pending_grant_issuer TEXT,\n pending_grant_nbf INTEGER,\n pending_grant_exp INTEGER,\n node_did TEXT,\n attested INTEGER NOT NULL DEFAULT 0 CHECK (attested IN (0, 1)),\n not_before TEXT,\n expires_at TEXT,\n retain_until TEXT,\n revoked_detail TEXT,\n purge_pending INTEGER NOT NULL DEFAULT 0 CHECK (purge_pending IN (0, 1)),\n retention_revoked TEXT,\n pending_grant_error TEXT,\n cursor TEXT,\n coverage TEXT NOT NULL CHECK (coverage IN ('empty', 'bootstrapping', 'complete')),\n generation INTEGER NOT NULL DEFAULT 0,\n created_at TEXT NOT NULL,\n last_sync_at TEXT,\n last_complete_at TEXT,\n last_error TEXT,\n last_reset TEXT,\n lease_token INTEGER NOT NULL DEFAULT 0,\n lease_holder TEXT,\n lease_expires_at INTEGER\n) STRICT;\nCREATE TABLE IF NOT EXISTS entry (\n key TEXT PRIMARY KEY,\n deleted INTEGER NOT NULL CHECK (deleted IN (0, 1)),\n etag TEXT,\n hash TEXT,\n metadata TEXT NOT NULL,\n content INTEGER NOT NULL CHECK (content IN (0, 1)),\n size INTEGER,\n CHECK (\n (deleted = 1 AND etag IS NULL AND hash IS NULL AND content = 0 AND size IS NULL) OR\n (deleted = 0 AND etag IS NOT NULL AND hash IS NOT NULL)\n )\n) STRICT, WITHOUT ROWID;\nCREATE INDEX IF NOT EXISTS entry_hash ON entry (hash) WHERE hash IS NOT NULL;\n`;\n\ntype ReplicaRow = {\n name: string;\n replica_id: string;\n host: string;\n space: string;\n prefix: string;\n device_did: string;\n allow_secrets: number;\n local_read_policy: LocalReadPolicy;\n retention_grant_cid: string | null;\n grant_cid: string | null;\n grant_bytes: Uint8Array | null;\n grant_audience: string | null;\n grant_issuer: string | null;\n grant_nbf: number | null;\n grant_exp: number | null;\n pending_grant_cid: string | null;\n pending_grant_bytes: Uint8Array | null;\n pending_grant_audience: string | null;\n pending_grant_issuer: string | null;\n pending_grant_nbf: number | null;\n pending_grant_exp: number | null;\n node_did: string | null;\n attested: number;\n not_before: string | null;\n expires_at: string | null;\n retain_until: string | null;\n revoked_detail: string | null;\n purge_pending: number;\n retention_revoked: string | null;\n pending_grant_error: string | null;\n cursor: string | null;\n coverage: Coverage;\n generation: number;\n created_at: string;\n last_sync_at: string | null;\n last_complete_at: string | null;\n last_error: string | null;\n last_reset: string | null;\n lease_token: number;\n lease_holder: string | null;\n lease_expires_at: number | null;\n};\n\ntype EntryRow = {\n key: string;\n deleted: number;\n etag: string | null;\n hash: string | null;\n metadata: string;\n content: number;\n};\n\nfunction grantOf(\n cid: string | null,\n bytes: Uint8Array | null,\n audience: string | null,\n issuer: string | null,\n nbf: number | null,\n exp: number | null,\n): GrantRecord | null {\n if (cid === null || bytes === null || audience === null || issuer === null) return null;\n return { cid, bytes: new Uint8Array(bytes), audience, issuer, notBefore: nbf, expiresAt: exp };\n}\n\nfunction entryOf(row: EntryRow): LocalEntry {\n if (row.deleted === 1) return { key: row.key, deleted: true };\n return {\n key: row.key,\n deleted: false,\n etag: row.etag!,\n hash: row.hash!,\n metadata: JSON.parse(row.metadata) as Record<string, string>,\n content: row.content === 1,\n };\n}\n\nfunction storageError(error: unknown, action: string): ReplicaError {\n if (isReplicaError(error)) return error;\n const code = errnoOf(error);\n const message = error instanceof Error ? error.message : String(error);\n if (code === \"ENOSPC\" || code === \"EDQUOT\" || /SQLITE_FULL|database or disk is full/i.test(`${String(code)} ${message}`)) {\n return new ReplicaError(ReplicaErrorCode.STORAGE_FULL, `${action}: the local disk is full.`, undefined, { cause: error });\n }\n return new ReplicaError(ReplicaErrorCode.STORAGE_ERROR, `${action}: ${message}`, undefined, { cause: error });\n}\n\nconst isHash = (name: string) => /^[0-9a-f]{64}$/.test(name);\n\nfunction errnoOf(error: unknown): unknown {\n return typeof error === \"object\" && error !== null && \"code\" in error ? error.code : undefined;\n}\n\n/** A directory's entries; only a directory that does not exist is empty. */\nasync function entriesOf(path: string): Promise<string[]> {\n try {\n return await readdir(path);\n } catch (error) {\n if (errnoOf(error) === \"ENOENT\") return [];\n throw error;\n }\n}\n\n/** Crash- and race-test seam: called at the durability boundaries of `applyPage`. */\nexport type StoreFaults = {\n /** Blobs are durable; no row references them yet. */\n afterBlobs?(): void | Promise<void>;\n /** Rows and cursor are written inside the open transaction, not committed. */\n beforeCommit?(): void;\n /** The page is committed. */\n afterCommit?(): void;\n /** A revocation purge is committed; its blobs are not unlinked yet. */\n afterPurgeMark?(): void;\n};\n\n/**\n * Runs a mutation section (every database write and every filesystem\n * change). The CLI passes one that holds the profile lock briefly and\n * refuses when the profile was deleted, so a command never writes into or\n * recreates a deleted profile.\n */\nexport type MutationGuard = <T>(section: () => Promise<T>) => Promise<T>;\n\nexport type SqliteReplicaStoreOptions = {\n /** Create the replica directory and database if missing (else REPLICA_NOT_FOUND). */\n create: boolean;\n sqlite?: SqliteOpener;\n /** Clock for leases and status (ms since epoch). */\n now?: () => number;\n guard?: MutationGuard;\n};\n\n/** Crash-test seam key. Only tests import it (from this module, not the package entry). */\nexport const FAULTS = Symbol(\"tinycloud.replica.faults\");\ntype InternalOptions = SqliteReplicaStoreOptions & { [FAULTS]?: StoreFaults };\n\n/**\n * The CLI replica store: SQLite (WAL, synchronous=FULL) for entries, cursor\n * and authority, committed in one transaction per page; content-addressed\n * blob files written with fsync + link before any row references them.\n */\nexport class SqliteReplicaStore implements ReplicaStore {\n readonly dir: string;\n readonly #db: SqliteDatabase;\n readonly #holder = `${process.pid}-${randomBytes(6).toString(\"hex\")}`;\n readonly #now: () => number;\n readonly #faults: StoreFaults;\n readonly #guard: MutationGuard;\n /** Identity of the database file this store opened: a deleted-and-recreated replica is a different file. */\n readonly #ino: number;\n\n private constructor(dir: string, db: SqliteDatabase, ino: number, options: SqliteReplicaStoreOptions) {\n this.dir = dir;\n this.#db = db;\n this.#ino = ino;\n this.#now = options.now ?? Date.now;\n this.#faults = (options as InternalOptions)[FAULTS] ?? {};\n this.#guard = options.guard ?? ((section) => section());\n }\n\n /**\n * Open the replica store in `dir`. With `create: false` a missing replica is\n * REPLICA_NOT_FOUND and nothing is written to disk.\n */\n static async open(dir: string, options: SqliteReplicaStoreOptions): Promise<SqliteReplicaStore> {\n const dbPath = join(dir, \"replica.db\");\n if (!options.create) {\n const exists = await stat(dbPath).then(\n () => true,\n () => false,\n );\n if (!exists) throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, `No replica at ${dir}.`);\n }\n const opener = options.sqlite ?? (await loadSqlite());\n let store: SqliteReplicaStore;\n try {\n if (options.create) {\n await mkdir(join(dir, \"blobs\", \".tmp\"), { recursive: true, mode: 0o700 });\n await Promise.all([chmod(dir, 0o700), chmod(join(dir, \"blobs\"), 0o700), chmod(join(dir, \"blobs\", \".tmp\"), 0o700)]);\n // SQLite gives -wal and -shm the database file's mode.\n const handle = await open(dbPath, fsConstants.O_CREAT | fsConstants.O_RDWR, 0o600);\n await handle.close();\n await chmod(dbPath, 0o600);\n }\n const ino = (await stat(dbPath)).ino;\n const db = opener(dbPath);\n db.exec(\"PRAGMA busy_timeout = 5000\");\n db.exec(\"PRAGMA journal_mode = WAL\");\n db.exec(\"PRAGMA synchronous = FULL\");\n db.exec(\"PRAGMA secure_delete = ON\");\n db.exec(\"PRAGMA foreign_keys = ON\");\n const version = db.get<{ user_version: number }>(\"PRAGMA user_version\")?.user_version ?? 0;\n if (version > SCHEMA_VERSION) {\n db.close();\n throw new ReplicaError(\n ReplicaErrorCode.STORAGE_ERROR,\n `The replica at ${dir} was written by a newer tc (schema ${version}); upgrade tc to read it.`,\n );\n }\n if (version < SCHEMA_VERSION) {\n db.exec(\"BEGIN IMMEDIATE\");\n db.exec(SCHEMA);\n db.exec(`PRAGMA user_version = ${SCHEMA_VERSION}`);\n db.exec(\"COMMIT\");\n }\n store = new SqliteReplicaStore(dir, db, ino, options);\n } catch (error) {\n throw storageError(error, `Opening the replica at ${dir}`);\n }\n // A revocation purge interrupted by a crash (or by a writer that put\n // content back after it) finishes on the next open.\n const row = store.#row();\n if (row?.revoked_detail != null && (row.purge_pending === 1 || (await store.#holdsContentFiles().catch(() => true)))) {\n await store.#finishRevocationPurge().catch(() => undefined);\n }\n return store;\n }\n\n #row(): ReplicaRow | undefined {\n return this.#db.get<ReplicaRow>(\"SELECT * FROM replica WHERE id = 1\");\n }\n\n /** Refuse to touch the filesystem for a replica that was deleted (or replaced) under us. */\n async #assertPresent(): Promise<void> {\n const ino = await stat(join(this.dir, \"replica.db\")).then(\n (stats) => stats.ino,\n () => undefined,\n );\n if (ino !== this.#ino) {\n throw new ReplicaError(\n ReplicaErrorCode.NOT_FOUND,\n `The replica at ${this.dir} was deleted while this command ran (its profile was deleted or the replica purged); nothing more was written.`,\n );\n }\n }\n\n /** A short mutation section: guarded (profile lock) and checked against the database this store opened. */\n #mutate<T>(section: () => Promise<T>): Promise<T> {\n return this.#guard(async () => {\n await this.#assertPresent();\n return section();\n });\n }\n\n /** Run `body` in a write transaction (BEGIN IMMEDIATE). Callers outside a mutation section use `#guardedWrite`. */\n #write<T>(action: string, body: () => T): T {\n try {\n this.#db.exec(\"BEGIN IMMEDIATE\");\n } catch (error) {\n throw storageError(error, action);\n }\n try {\n const result = body();\n this.#db.exec(\"COMMIT\");\n return result;\n } catch (error) {\n try {\n this.#db.exec(\"ROLLBACK\");\n } catch {\n // The transaction is already gone.\n }\n throw storageError(error, action);\n }\n }\n\n /** One write transaction as its own mutation section. */\n #guardedWrite<T>(action: string, body: () => T): Promise<T> {\n return this.#mutate(async () => this.#write(action, body));\n }\n\n /**\n * The caller still holds a live lease (its token, not expired), and the\n * replica is not revoked (unless `allowRevoked`). Inside a write\n * transaction this fences the write; before one it refuses early.\n */\n #checkLease(t: LeaseToken, options: { allowRevoked?: boolean } = {}): void {\n const row = this.#db.get<{ lease_token: number; lease_holder: string | null; lease_expires_at: number | null; revoked_detail: string | null }>(\n \"SELECT lease_token, lease_holder, lease_expires_at, revoked_detail FROM replica WHERE id = 1\",\n );\n if (row?.revoked_detail != null && options.allowRevoked !== true) {\n throw new ReplicaError(ReplicaErrorCode.GRANT_REVOKED, `The replica's grant was revoked: ${row.revoked_detail}`);\n }\n if (row === undefined || row.lease_token !== t.token || row.lease_holder !== t.holder) {\n throw new ReplicaError(ReplicaErrorCode.BUSY, \"Another process took over this replica's sync lease.\");\n }\n if (row.lease_expires_at === null || row.lease_expires_at <= this.#now()) {\n throw new ReplicaError(ReplicaErrorCode.BUSY, \"This process's sync lease expired; nothing was written.\");\n }\n }\n\n async open(): Promise<ReplicaState | null> {\n let row: ReplicaRow | undefined;\n try {\n row = this.#row();\n } catch (error) {\n throw storageError(error, \"Reading the replica\");\n }\n if (row === undefined) return null;\n return {\n config: {\n name: row.name,\n replicaId: row.replica_id,\n host: row.host,\n space: row.space,\n prefix: row.prefix,\n deviceDid: row.device_did,\n allowSecrets: row.allow_secrets === 1,\n localReadPolicy: row.local_read_policy,\n retentionGrantCid: row.retention_grant_cid,\n },\n grant: grantOf(row.grant_cid, row.grant_bytes, row.grant_audience, row.grant_issuer, row.grant_nbf, row.grant_exp),\n pendingGrant: grantOf(\n row.pending_grant_cid,\n row.pending_grant_bytes,\n row.pending_grant_audience,\n row.pending_grant_issuer,\n row.pending_grant_nbf,\n row.pending_grant_exp,\n ),\n nodeDid: row.node_did,\n authority: row.attested === 1 ? { notBefore: row.not_before, expiresAt: row.expires_at, retainUntil: row.retain_until } : null,\n revoked: row.revoked_detail,\n retentionRevoked: row.retention_revoked,\n pendingGrantError: row.pending_grant_error === null ? null : (JSON.parse(row.pending_grant_error) as ReplicaLastError),\n cursor: row.cursor,\n coverage: row.coverage,\n generation: row.generation,\n createdAt: row.created_at,\n lastSyncAt: row.last_sync_at,\n lastCompleteAt: row.last_complete_at,\n lastError: row.last_error === null ? null : (JSON.parse(row.last_error) as ReplicaLastError),\n lastReset: row.last_reset === null ? null : (JSON.parse(row.last_reset) as { at: string; reason: string }),\n };\n }\n\n async init(c: ReplicaConfig): Promise<void> {\n await this.#guardedWrite(\"Creating the replica\", () => {\n if (this.#row() !== undefined) {\n throw new ReplicaError(ReplicaErrorCode.CONFIG_MISMATCH, `Replica ${c.name} already exists.`);\n }\n this.#db.run(\n `INSERT INTO replica (id, name, replica_id, host, space, prefix, device_did, allow_secrets, local_read_policy,\n retention_grant_cid, coverage, created_at)\n VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'empty', ?)`,\n c.name,\n c.replicaId,\n c.host,\n c.space,\n c.prefix,\n c.deviceDid,\n c.allowSecrets ? 1 : 0,\n c.localReadPolicy,\n c.retentionGrantCid,\n new Date(this.#now()).toISOString(),\n );\n });\n }\n\n /** Update the retention opt-in; it takes effect at the next sync. */\n async setRetentionGrant(cid: string | null): Promise<void> {\n await this.#guardedWrite(\"Updating the retention grant\", () => {\n // Changing the CID invalidates the attested retainUntil: it was earned\n // under the previous retain grant, so only a fresh sync under the new\n // CID may re-establish it.\n this.#db.run(\n \"UPDATE replica SET retention_grant_cid = ?, local_read_policy = ?, retain_until = NULL WHERE id = 1 AND retention_grant_cid IS NOT ?\",\n cid,\n cid === null ? \"whileGrantValid\" : \"retainAfterExpiry\",\n cid,\n );\n });\n }\n\n async installGrant(g: GrantRecord): Promise<void> {\n await this.#guardedWrite(\"Installing the grant\", () => {\n const row = this.#row();\n if (row === undefined) throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, \"The replica has not been created.\");\n if (row.grant_cid === g.cid || row.pending_grant_cid === g.cid) return;\n this.#db.run(\n `UPDATE replica SET pending_grant_cid = ?, pending_grant_bytes = ?, pending_grant_audience = ?,\n pending_grant_issuer = ?, pending_grant_nbf = ?, pending_grant_exp = ?, pending_grant_error = NULL WHERE id = 1`,\n g.cid,\n g.bytes,\n g.audience,\n g.issuer,\n g.notBefore,\n g.expiresAt,\n );\n });\n }\n\n async discardPendingGrant(cid: string, detail: string): Promise<void> {\n await this.#guardedWrite(\"Discarding the revoked pending grant\", () => {\n this.#db.run(\n `UPDATE replica SET pending_grant_cid = NULL, pending_grant_bytes = NULL, pending_grant_audience = NULL,\n pending_grant_issuer = NULL, pending_grant_nbf = NULL, pending_grant_exp = NULL, pending_grant_error = NULL,\n last_error = ? WHERE id = 1 AND pending_grant_cid = ?`,\n JSON.stringify({\n at: new Date(this.#now()).toISOString(),\n code: ReplicaErrorCode.GRANT_REVOKED,\n message: `The pending grant ${cid} was revoked and discarded: ${detail}`,\n }),\n cid,\n );\n });\n }\n\n async recordPendingGrantError(cid: string, e: ReplicaLastError): Promise<void> {\n await this.#guardedWrite(\"Recording the pending grant's refusal\", () => {\n this.#db.run(\"UPDATE replica SET pending_grant_error = ? WHERE id = 1 AND pending_grant_cid = ?\", JSON.stringify(e), cid);\n });\n }\n\n async markRetentionRevoked(cid: string, detail: string): Promise<void> {\n await this.#guardedWrite(\"Recording the retention grant's revocation\", () => {\n this.#db.run(\n `UPDATE replica SET retention_grant_cid = NULL, local_read_policy = 'whileGrantValid', retain_until = NULL,\n retention_revoked = ? WHERE id = 1 AND retention_grant_cid = ?`,\n detail,\n cid,\n );\n this.#db.run(\n \"UPDATE replica SET last_error = ? WHERE id = 1\",\n JSON.stringify({ at: new Date(this.#now()).toISOString(), code: ReplicaErrorCode.RETENTION_GRANT_REFUSED, message: detail }),\n );\n });\n }\n\n async acquireSyncLease(ttlMs: number): Promise<LeaseToken | null> {\n return this.#guardedWrite(\"Taking the sync lease\", () => {\n const row = this.#row();\n if (row === undefined) throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, \"The replica has not been created.\");\n const now = this.#now();\n if (row.lease_holder !== null && row.lease_expires_at !== null && row.lease_expires_at > now) return null;\n const token = row.lease_token + 1;\n this.#db.run(\n \"UPDATE replica SET lease_token = ?, lease_holder = ?, lease_expires_at = ? WHERE id = 1\",\n token,\n this.#holder,\n now + ttlMs,\n );\n return { token, holder: this.#holder };\n });\n }\n\n async renewLease(t: LeaseToken, ttlMs: number): Promise<void> {\n await this.#guardedWrite(\"Renewing the sync lease\", () => {\n this.#checkLease(t);\n this.#db.run(\"UPDATE replica SET lease_expires_at = ? WHERE id = 1\", this.#now() + ttlMs);\n });\n }\n\n async releaseLease(t: LeaseToken): Promise<void> {\n await this.#guardedWrite(\"Releasing the sync lease\", () => {\n this.#db.run(\n \"UPDATE replica SET lease_holder = NULL, lease_expires_at = NULL WHERE id = 1 AND lease_token = ? AND lease_holder = ?\",\n t.token,\n t.holder,\n );\n });\n }\n\n async hasContent(hashes: string[]): Promise<Set<string>> {\n const held = new Set<string>();\n for (let start = 0; start < hashes.length; start += 500) {\n const chunk = hashes.slice(start, start + 500);\n const rows = this.#db.all<{ hash: string }>(\n `SELECT DISTINCT hash FROM entry WHERE content = 1 AND hash IN (${chunk.map(() => \"?\").join(\",\")})`,\n ...chunk,\n );\n for (const row of rows) held.add(row.hash);\n }\n return held;\n }\n\n #blobPath(hash: string): string {\n return join(this.dir, \"blobs\", hash.slice(0, 2), hash);\n }\n\n /**\n * Make every blob durable before any row references it: write to a fresh\n * temp file (O_EXCL, 0600), fsync, link to its content address (an existing\n * blob wins), unlink the temp, then fsync each touched directory once.\n * Never creates the replica's own directories: a deleted replica fails.\n */\n async #writeBlobs(blobs: Map<string, Uint8Array>): Promise<void> {\n const dirs = new Set<string>();\n for (const [hash, bytes] of blobs) {\n const final = this.#blobPath(hash);\n const exists = await stat(final).then(\n () => true,\n () => false,\n );\n if (exists) continue;\n const shard = join(this.dir, \"blobs\", hash.slice(0, 2));\n await mkdir(shard, { mode: 0o700 }).catch((error: unknown) => {\n if (errnoOf(error) !== \"EEXIST\") throw error;\n });\n const temp = join(this.dir, \"blobs\", \".tmp\", `${hash}.${randomBytes(6).toString(\"hex\")}`);\n const handle = await open(temp, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY, 0o600);\n try {\n await handle.writeFile(bytes);\n await handle.sync();\n } finally {\n await handle.close();\n }\n try {\n await link(temp, final);\n } catch (error) {\n if (errnoOf(error) !== \"EEXIST\") throw error;\n } finally {\n await unlink(temp);\n }\n dirs.add(shard);\n dirs.add(join(this.dir, \"blobs\"));\n }\n for (const dir of dirs) {\n const handle = await open(dir, fsConstants.O_RDONLY);\n try {\n await handle.sync();\n } finally {\n await handle.close();\n }\n }\n }\n\n async applyPage(t: LeaseToken, p: VerifiedPage): Promise<void> {\n await this.#mutate(async () => {\n // Fence before any content reaches the disk: a writer whose lease was\n // taken over, expired or revoked writes nothing.\n this.#checkLease(t);\n // Resolve every reused blob's size before any row changes: an earlier\n // row of this page may drop the last entry that references it.\n const reused = new Map<string, number>();\n for (const change of p.changes) {\n if (change.deleted || !change.content || p.blobs.has(change.hash) || reused.has(change.hash)) continue;\n const size = this.#db.get<{ size: number | null }>(\n \"SELECT size FROM entry WHERE hash = ? AND content = 1 AND size IS NOT NULL LIMIT 1\",\n change.hash,\n )?.size;\n if (size === undefined || size === null) {\n throw new ReplicaError(ReplicaErrorCode.STORAGE_ERROR, `No stored content backs ${JSON.stringify(change.key)}.`);\n }\n reused.set(change.hash, size);\n }\n try {\n await this.#writeBlobs(p.blobs);\n } catch (error) {\n throw storageError(error, \"Writing replica content\");\n }\n await this.#faults.afterBlobs?.();\n try {\n this.#write(\"Committing a sync page\", () => {\n this.#checkLease(t);\n // Authority is judged now, at the commit, not when the page arrived.\n const now = this.#now();\n if (p.window.notBefore !== null && now < Date.parse(p.window.notBefore)) {\n throw new ReplicaError(ReplicaErrorCode.GRANT_NOT_YET_VALID, `The grant is not valid before ${p.window.notBefore}; nothing was committed.`);\n }\n if (p.window.expiresAt !== null && now >= Date.parse(p.window.expiresAt)) {\n throw new ReplicaError(ReplicaErrorCode.GRANT_EXPIRED, `The grant expired at ${p.window.expiresAt}; nothing was committed.`);\n }\n for (const change of p.changes) {\n if (change.deleted) {\n this.#db.run(\n `INSERT INTO entry (key, deleted, etag, hash, metadata, content, size) VALUES (?, 1, NULL, NULL, '{}', 0, NULL)\n ON CONFLICT (key) DO UPDATE SET deleted = 1, etag = NULL, hash = NULL, metadata = '{}', content = 0, size = NULL`,\n change.key,\n );\n continue;\n }\n const size = change.content ? (p.blobs.get(change.hash)?.length ?? reused.get(change.hash)!) : null;\n this.#db.run(\n `INSERT INTO entry (key, deleted, etag, hash, metadata, content, size) VALUES (?, 0, ?, ?, ?, ?, ?)\n ON CONFLICT (key) DO UPDATE SET deleted = 0, etag = excluded.etag, hash = excluded.hash,\n metadata = excluded.metadata, content = excluded.content, size = excluded.size`,\n change.key,\n change.etag,\n change.hash,\n JSON.stringify(change.metadata),\n change.content ? 1 : 0,\n size,\n );\n }\n const sets: string[] = [\"cursor = ?\", \"coverage = ?\"];\n const params: SqlValue[] = [p.cursor, p.coverage];\n if (p.authority !== null) {\n sets.push(\"node_did = ?\", \"attested = 1\", \"not_before = ?\", \"expires_at = ?\", \"last_sync_at = ?\");\n params.push(p.source.nodeDid, p.authority.notBefore, p.authority.expiresAt, p.at);\n // The attested retainUntil belongs to the retention grant the\n // request presented: if a setRetentionGrant raced the page, the\n // stored CID no longer matches and retain_until stays untouched\n // (null after the CID change) instead of reviving the previous\n // grant's window under a new CID.\n if (this.#row()!.retention_grant_cid === p.retentionGrantCid) {\n sets.push(\"retain_until = ?\");\n params.push(p.authority.retainUntil);\n // A node-attested retention under a new retain grant supersedes a learned revocation of the old one.\n if (p.authority.retainUntil !== null) sets.push(\"retention_revoked = NULL\");\n }\n }\n if (p.complete) {\n sets.push(\"last_complete_at = ?\", \"last_error = NULL\");\n params.push(p.at);\n }\n if (p.promoteGrant !== null) {\n const g = p.promoteGrant;\n sets.push(\"grant_cid = ?\", \"grant_bytes = ?\", \"grant_audience = ?\", \"grant_issuer = ?\", \"grant_nbf = ?\", \"grant_exp = ?\");\n params.push(g.cid, g.bytes, g.audience, g.issuer, g.notBefore, g.expiresAt);\n // Clear the pending slot only if it still holds the grant just validated.\n if (this.#row()!.pending_grant_cid === g.cid) {\n sets.push(\n \"pending_grant_cid = NULL\",\n \"pending_grant_bytes = NULL\",\n \"pending_grant_audience = NULL\",\n \"pending_grant_issuer = NULL\",\n \"pending_grant_nbf = NULL\",\n \"pending_grant_exp = NULL\",\n \"pending_grant_error = NULL\",\n );\n }\n }\n this.#db.run(`UPDATE replica SET ${sets.join(\", \")} WHERE id = 1`, ...params);\n this.#faults.beforeCommit?.();\n });\n } catch (error) {\n // Revoked while this call wrote its blobs: the purge may already have\n // run, so remove them again (a failure leaves purge_pending for open).\n if (isReplicaError(error, ReplicaErrorCode.GRANT_REVOKED)) await this.#purgeContent().catch(() => undefined);\n throw error;\n }\n this.#faults.afterCommit?.();\n });\n }\n\n /** Unlink every blob not in `keep`, and every temp file. Only a missing directory counts as empty. */\n async #unlinkBlobs(keep: Set<string>): Promise<number> {\n let removed = 0;\n const root = join(this.dir, \"blobs\");\n for (const shard of await entriesOf(root)) {\n const shardPath = join(root, shard);\n if (shard === \".tmp\") {\n for (const name of await entriesOf(shardPath)) {\n await rm(join(shardPath, name), { force: true });\n removed += 1;\n }\n continue;\n }\n let kept = 0;\n for (const name of await entriesOf(shardPath)) {\n if (isHash(name) && keep.has(name)) {\n kept += 1;\n continue;\n }\n await rm(join(shardPath, name), { force: true });\n removed += 1;\n }\n if (kept === 0) await rmdir(shardPath).catch(() => undefined);\n }\n return removed;\n }\n\n /** Whether any content file (a blob or a temp file) is on disk. */\n async #holdsContentFiles(): Promise<boolean> {\n const root = join(this.dir, \"blobs\");\n for (const shard of await entriesOf(root)) {\n if ((await entriesOf(join(root, shard))).length > 0) return true;\n }\n return false;\n }\n\n async collectGarbage(t: LeaseToken): Promise<number> {\n return this.#mutate(async () => {\n // The write lock is held across the unlinks, and the lease token is\n // checked under it: a paused former holder can never unlink a blob the\n // current holder just committed a reference to.\n try {\n this.#db.exec(\"BEGIN IMMEDIATE\");\n } catch (error) {\n throw storageError(error, \"Collecting unused content\");\n }\n try {\n this.#checkLease(t);\n const keep = new Set(\n this.#db.all<{ hash: string }>(\"SELECT DISTINCT hash FROM entry WHERE content = 1\").map((row) => row.hash),\n );\n const removed = await this.#unlinkBlobs(keep);\n this.#db.exec(\"COMMIT\");\n return removed;\n } catch (error) {\n try {\n this.#db.exec(\"ROLLBACK\");\n } catch {\n // already rolled back\n }\n throw storageError(error, \"Collecting unused content\");\n }\n });\n }\n\n async reset(t: LeaseToken, reason: string, options: { keepSource?: boolean } = {}): Promise<void> {\n await this.#mutate(async () => {\n this.#write(\"Resetting the replica\", () => {\n this.#checkLease(t);\n this.#db.run(\"DELETE FROM entry\");\n this.#db.run(\n `UPDATE replica SET cursor = NULL, ${options.keepSource ? \"\" : \"node_did = NULL, \"}coverage = 'empty', last_sync_at = NULL,\n last_complete_at = NULL, last_error = NULL, last_reset = ?, generation = generation + 1 WHERE id = 1`,\n JSON.stringify({ at: new Date(this.#now()).toISOString(), reason }),\n );\n });\n });\n await this.collectGarbage(t);\n if (!this.#checkpoint()) {\n throw new ReplicaError(\n ReplicaErrorCode.BUSY,\n \"The replica was reset, but another process is reading it, so its old pages are still in the write-ahead log. Run the reset again when that process ends.\",\n );\n }\n }\n\n /**\n * Remove this replica's directory for good (`tc replica reset --purge`).\n * Fenced like a commit: `t` must still be a live lease on the database this\n * store opened, checked under the write lock, so a paused process can\n * never remove a replica another process took over or recreated. Allowed\n * on a revoked replica. The store is unusable afterwards; close it.\n */\n async destroy(t: LeaseToken): Promise<void> {\n await this.#mutate(async () => {\n try {\n this.#db.exec(\"BEGIN IMMEDIATE\");\n } catch (error) {\n throw storageError(error, \"Removing the replica\");\n }\n try {\n this.#checkLease(t, { allowRevoked: true });\n // Holding the write lock: no one can take the lease until the directory is gone.\n await this.#assertPresent();\n await rm(this.dir, { recursive: true, force: true });\n } catch (error) {\n throw storageError(error, \"Removing the replica\");\n } finally {\n try {\n this.#db.exec(\"ROLLBACK\");\n } catch {\n // Nothing was written.\n }\n }\n });\n }\n\n /**\n * Truncate the WAL so purged pages do not linger in it. TRUNCATE waits on\n * readers through busy_timeout; false when one still held it.\n */\n #checkpoint(): boolean {\n try {\n const result = this.#db.get<{ busy: number }>(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n return result === undefined || Number(result.busy) === 0;\n } catch (error) {\n throw storageError(error, \"Checkpointing the replica\");\n }\n }\n\n /**\n * Record the revocation (durable before this returns: reads are blocked\n * from here on, also after a restart), then purge. A purge that fails\n * keeps purge_pending set: status reports it and every open retries it.\n */\n async markRevoked(detail: string): Promise<void> {\n await this.#guardedWrite(\"Recording the revocation\", () => {\n this.#db.run(\"DELETE FROM entry\");\n // Bumping the lease token fences every outstanding writer.\n this.#db.run(\n `UPDATE replica SET revoked_detail = ?, cursor = NULL, coverage = 'empty', purge_pending = 1,\n lease_token = lease_token + 1, lease_holder = NULL, lease_expires_at = NULL, generation = generation + 1,\n last_error = ? WHERE id = 1`,\n detail,\n JSON.stringify({ at: new Date(this.#now()).toISOString(), code: ReplicaErrorCode.GRANT_REVOKED, message: detail }),\n );\n });\n this.#faults.afterPurgeMark?.();\n await this.#finishRevocationPurge().catch(() => undefined);\n }\n\n #finishRevocationPurge(): Promise<void> {\n return this.#mutate(() => this.#purgeContent());\n }\n\n /**\n * Inside a mutation section of a revoked replica: unlink every content\n * file and truncate the WAL. purge_pending is set first and cleared only\n * when both are done.\n */\n async #purgeContent(): Promise<void> {\n this.#write(\"Starting the revocation purge\", () => {\n this.#db.run(\"UPDATE replica SET purge_pending = 1 WHERE id = 1 AND revoked_detail IS NOT NULL\");\n });\n try {\n await this.#unlinkBlobs(new Set());\n } catch (error) {\n throw storageError(error, \"Purging revoked content\");\n }\n if (!this.#checkpoint()) {\n throw new ReplicaError(ReplicaErrorCode.BUSY, \"A reader holds the replica's write-ahead log; the purge finishes on the next open.\");\n }\n this.#write(\"Finishing the revocation purge\", () => {\n this.#db.run(\"UPDATE replica SET purge_pending = 0 WHERE id = 1 AND revoked_detail IS NOT NULL\");\n });\n }\n\n async recordError(e: ReplicaLastError): Promise<void> {\n await this.#guardedWrite(\"Recording the sync error\", () => {\n this.#db.run(\"UPDATE replica SET last_error = ? WHERE id = 1\", JSON.stringify(e));\n });\n }\n\n async get(key: string): Promise<LocalEntry | undefined> {\n try {\n const row = this.#db.get<EntryRow>(\"SELECT key, deleted, etag, hash, metadata, content FROM entry WHERE key = ?\", key);\n return row === undefined ? undefined : entryOf(row);\n } catch (error) {\n throw storageError(error, \"Reading the replica\");\n }\n }\n\n async list(o: ListOpts): Promise<LocalEntry[]> {\n const where = [\"deleted = 0\"];\n const params: SqlValue[] = [];\n if (o.prefix !== undefined && o.prefix !== \"\") {\n where.push(\"key >= ?\", \"substr(key, 1, length(?)) = ?\");\n params.push(o.prefix, o.prefix, o.prefix);\n }\n if (o.after !== undefined) {\n where.push(\"key > ?\");\n params.push(o.after);\n }\n params.push(o.limit ?? -1);\n try {\n return this.#db\n .all<EntryRow>(\n `SELECT key, deleted, etag, hash, metadata, content FROM entry WHERE ${where.join(\" AND \")} ORDER BY key LIMIT ?`,\n ...params,\n )\n .map(entryOf);\n } catch (error) {\n throw storageError(error, \"Listing the replica\");\n }\n }\n\n async pendingRepairs(limit: number): Promise<Array<Extract<LocalEntry, { deleted: false }>>> {\n return this.#db\n .all<EntryRow>(\n \"SELECT key, deleted, etag, hash, metadata, content FROM entry WHERE deleted = 0 AND content = 0 ORDER BY key LIMIT ?\",\n limit,\n )\n .map(entryOf) as Array<Extract<LocalEntry, { deleted: false }>>;\n }\n\n async readContent(hash: string): Promise<Uint8Array | undefined> {\n if (!isHash(hash)) return undefined;\n try {\n return new Uint8Array(await readFile(this.#blobPath(hash)));\n } catch (error) {\n if (errnoOf(error) === \"ENOENT\") return undefined;\n throw storageError(error, \"Reading replica content\");\n }\n }\n\n async status(now = this.#now()): Promise<ReplicaStatus> {\n const state = await this.open();\n if (state === null) throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, \"The replica has not been created.\");\n const row = this.#row()!;\n const counts = this.#db.get<{ keys: number; missing: number; tombstones: number; bytes: number }>(\n `SELECT\n COALESCE(SUM(deleted = 0), 0) AS keys,\n COALESCE(SUM(deleted = 0 AND content = 0), 0) AS missing,\n COALESCE(SUM(deleted = 1), 0) AS tombstones,\n COALESCE(SUM(CASE WHEN deleted = 0 AND content = 1 THEN size ELSE 0 END), 0) AS bytes\n FROM entry`,\n )!;\n const { retentionRevoked: _retentionRevoked, ...authority } = effectiveAuthority({\n window: state.authority,\n grant: state.grant,\n revoked: state.revoked,\n retentionRevoked: state.retentionRevoked,\n policy: state.config.localReadPolicy,\n now,\n });\n return {\n name: state.config.name,\n replicaId: state.config.replicaId,\n source: { host: state.config.host, nodeDid: state.nodeDid, space: state.config.space, prefix: state.config.prefix },\n device: {\n did: state.config.deviceDid,\n delegationCid: state.grant?.cid ?? null,\n pendingDelegationCid: state.pendingGrant?.cid ?? null,\n pendingDelegationError: state.pendingGrantError,\n },\n authority: {\n ...authority,\n localReadPolicy: state.config.localReadPolicy,\n retentionGrantCid: state.config.retentionGrantCid,\n revokedDetail: state.revoked,\n retentionRevokedDetail: state.retentionRevoked,\n },\n consistency: \"observed\",\n coverage: state.coverage,\n lastSyncAt: state.lastSyncAt,\n lastCompleteAt: state.lastCompleteAt,\n counts: { keys: Number(counts.keys), contentMissing: Number(counts.missing), tombstones: Number(counts.tombstones) },\n bytes: Number(counts.bytes),\n durability: DURABILITY,\n syncing: row.lease_holder !== null && row.lease_expires_at !== null && row.lease_expires_at > now,\n purgePending: row.purge_pending === 1,\n lastError: state.lastError,\n lastReset: state.lastReset,\n };\n }\n\n async close(): Promise<void> {\n this.#db.close();\n }\n}\n","/**\n * Typed replica errors. Every failure a caller can act on has its own code;\n * hosts (CLI, worker) map codes to their own exit codes or UI states.\n */\nexport const ReplicaErrorCode = {\n /** Another process holds the sync lease. */\n BUSY: \"REPLICA_BUSY\",\n /** No replica with this name exists. */\n NOT_FOUND: \"REPLICA_NOT_FOUND\",\n /** The replica exists but its stored configuration differs from the request. */\n CONFIG_MISMATCH: \"REPLICA_CONFIG_MISMATCH\",\n /** The runtime has no supported durable store (e.g. Node < 22.13). */\n RUNTIME_UNSUPPORTED: \"RUNTIME_UNSUPPORTED\",\n /** A required open() argument is missing or malformed (e.g. `principal`). */\n INVALID_ARGUMENT: \"REPLICA_INVALID_ARGUMENT\",\n /** The local store failed (I/O, corruption, schema). */\n STORAGE_ERROR: \"STORAGE_ERROR\",\n /** The local disk is full. */\n STORAGE_FULL: \"STORAGE_FULL\",\n /** The key is outside the replica's prefix. */\n NOT_COVERED: \"NOT_COVERED\",\n /** Syncing the secrets space or the vault namespace needs an explicit opt-in. */\n SECRETS_OPT_IN_REQUIRED: \"SECRETS_OPT_IN_REQUIRED\",\n /** No grant is installed, or none covers the requested scope. */\n GRANT_MISSING: \"GRANT_MISSING\",\n /** The grant bytes are not a verifiable UCAN, or its signature is wrong. */\n GRANT_INVALID: \"GRANT_INVALID\",\n /** SIWE/CACAO and other non-UCAN grants cannot be used for a replica. */\n GRANT_FORMAT_UNSUPPORTED: \"GRANT_FORMAT_UNSUPPORTED\",\n /** The grant was issued to a different device. */\n GRANT_AUDIENCE_MISMATCH: \"GRANT_AUDIENCE_MISMATCH\",\n /** The grant lacks `tinycloud.kv/sync` or `tinycloud.kv/get` on the prefix. */\n GRANT_NOT_COVERING: \"GRANT_NOT_COVERING\",\n GRANT_NOT_YET_VALID: \"GRANT_NOT_YET_VALID\",\n GRANT_EXPIRED: \"GRANT_EXPIRED\",\n GRANT_REVOKED: \"GRANT_REVOKED\",\n /** The node refused the invocation for a reason other than revocation. */\n GRANT_UNAUTHORIZED: \"GRANT_UNAUTHORIZED\",\n /** The node refused the retention grant. */\n RETENTION_GRANT_REFUSED: \"RETENTION_GRANT_REFUSED\",\n /** The node could not be reached. */\n NETWORK_ERROR: \"NETWORK_ERROR\",\n /** The node answered with an unexpected status. */\n NODE_ERROR: \"NODE_ERROR\",\n /** The node's answer violates the wire contract. */\n PROTOCOL_ERROR: \"PROTOCOL_ERROR\",\n /** The node asked for a fresh bootstrap (410). Handled inside sync. */\n RESET_REQUIRED: \"RESET_REQUIRED\",\n /** The feed now comes from a different node than the one pinned. */\n SOURCE_CHANGED: \"SOURCE_CHANGED\",\n /** The feed delivered a key outside the replica's prefix. */\n SCOPE_VIOLATION: \"SCOPE_VIOLATION\",\n /** Fetched bytes do not hash to the ETag the node attested for them. */\n CONTENT_MISMATCH: \"CONTENT_MISMATCH\",\n /** A stored blob no longer hashes to its name. */\n INTEGRITY_ERROR: \"INTEGRITY_ERROR\",\n /** The replica handle was closed; in-flight calls settle with this code. */\n CLOSED: \"REPLICA_CLOSED\",\n} as const;\n\nexport type ReplicaErrorCode = (typeof ReplicaErrorCode)[keyof typeof ReplicaErrorCode];\n\n/**\n * The package ships separate entry bundles (`.`, `./sqlite`), each with its own\n * copy of this class, so identity is a registered symbol, not `instanceof`.\n */\nconst REPLICA_ERROR = Symbol.for(\"tinycloud.replica.error\");\n\nexport class ReplicaError extends Error {\n readonly [REPLICA_ERROR] = true;\n readonly code: ReplicaErrorCode;\n readonly detail?: Record<string, unknown>;\n\n constructor(code: ReplicaErrorCode, message: string, detail?: Record<string, unknown>, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"ReplicaError\";\n this.code = code;\n if (detail !== undefined) this.detail = detail;\n }\n}\n\nexport function isReplicaError(error: unknown, code?: ReplicaErrorCode): error is ReplicaError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as { [REPLICA_ERROR]?: unknown })[REPLICA_ERROR] === true &&\n (code === undefined || (error as ReplicaError).code === code)\n );\n}\n","import { ReplicaError, ReplicaErrorCode } from \"./errors.js\";\nimport type { AuthorityState, AuthorityWindow, GrantRecord, LocalReadPolicy } from \"./types.js\";\n\n/**\n * The node's `kv_prefix_covers`: whole path segments, byte-exact. `notes/`\n * covers everything under `notes/` but not `notes`; `notes` covers `notes`\n * and `notes/…` but not `notes-secret/x`.\n */\nexport function kvPrefixCovers(prefix: string, key: string): boolean {\n if (prefix === \"\") return true;\n if (prefix.endsWith(\"/\")) return key.startsWith(prefix);\n return key === prefix || key.startsWith(`${prefix}/`);\n}\n\n/**\n * Syncing the owner's secrets space, or any prefix overlapping the vault\n * namespace (including bare `vault`), copies encrypted secret material to\n * disk and needs an explicit opt-in (TC-733).\n */\nexport function requiresSecretsOptIn(space: string, prefix: string): boolean {\n if (space === \"secrets\" || space.endsWith(\":secrets\")) return true;\n const firstSegment = prefix.split(\"/\", 1)[0];\n return firstSegment === \"vault\";\n}\n\n/** `\"blake3-<hex>\"` (quoted or not) → the 64-hex hash, or undefined. */\nexport function hashFromEtag(etag: string): string | undefined {\n const match = /^\"?blake3-([0-9a-f]{64})\"?$/.exec(etag);\n return match?.[1];\n}\n\nconst RFC3339 = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$/;\n\n/** An RFC 3339 instant that parses: the only form an authority bound may take. */\nexport function isInstant(value: string): boolean {\n return RFC3339.test(value) && Number.isFinite(Date.parse(value));\n}\n\nexport type EffectiveAuthority = {\n state: AuthorityState;\n notBefore: string | null;\n expiresAt: string | null;\n retainUntil: string | null;\n /** The retention grant was revoked: after expiry, reads are revoked rather than expired. */\n retentionRevoked: boolean;\n};\n\nfunction minIso(a: string | null, bSeconds: number | null): string | null {\n if (bSeconds === null) return a;\n const b = new Date(bSeconds * 1000).toISOString();\n if (a === null) return b;\n return Date.parse(a) <= Date.parse(b) ? a : b;\n}\n\nfunction maxIso(a: string | null, bSeconds: number | null): string | null {\n if (bSeconds === null) return a;\n const b = new Date(bSeconds * 1000).toISOString();\n if (a === null) return b;\n return Date.parse(a) >= Date.parse(b) ? a : b;\n}\n\n/**\n * The window local reads are enforced against: the node-attested window from\n * the last successful sync, bounded by the installed leaf grant (an upper\n * bound only — a multi-parent ancestor can end the chain earlier, which only\n * the node can see).\n */\nexport function effectiveAuthority(input: {\n window: AuthorityWindow | null;\n grant: GrantRecord | null;\n revoked: string | null;\n retentionRevoked?: string | null;\n policy: LocalReadPolicy;\n now: number;\n}): EffectiveAuthority {\n const notBefore = maxIso(input.window?.notBefore ?? null, input.grant?.notBefore ?? null);\n const expiresAt = minIso(input.window?.expiresAt ?? null, input.grant?.expiresAt ?? null);\n const retainUntil = input.policy === \"retainAfterExpiry\" ? input.window?.retainUntil ?? null : null;\n let state: AuthorityState = \"valid\";\n if (input.revoked !== null) state = \"revoked\";\n else if (notBefore !== null && input.now < Date.parse(notBefore)) state = \"not-yet-valid\";\n else if (expiresAt !== null && input.now >= Date.parse(expiresAt)) state = \"expired\";\n return { state, notBefore, expiresAt, retainUntil, retentionRevoked: (input.retentionRevoked ?? null) !== null };\n}\n\n/**\n * Throws unless local reads are allowed now. Returns the reported authority\n * state: `valid`, or `expired` while a node-attested retention holds.\n */\nexport function assertReadable(authority: EffectiveAuthority, now: number): AuthorityState {\n switch (authority.state) {\n case \"valid\":\n return \"valid\";\n case \"revoked\":\n throw new ReplicaError(ReplicaErrorCode.GRANT_REVOKED, \"The replica's grant was revoked; local reads are blocked.\");\n case \"not-yet-valid\":\n throw new ReplicaError(\n ReplicaErrorCode.GRANT_NOT_YET_VALID,\n `The replica's grant is not valid before ${authority.notBefore}.`,\n );\n case \"expired\":\n if (authority.retentionRevoked) {\n throw new ReplicaError(\n ReplicaErrorCode.GRANT_REVOKED,\n `The replica's grant expired at ${authority.expiresAt} and its retention grant was revoked; local reads are blocked.`,\n );\n }\n if (authority.retainUntil !== null && now < Date.parse(authority.retainUntil)) return \"expired\";\n throw new ReplicaError(\n ReplicaErrorCode.GRANT_EXPIRED,\n authority.retainUntil === null\n ? `The replica's grant expired at ${authority.expiresAt}; local reads are blocked.`\n : `The replica's retention ended at ${authority.retainUntil}; local reads are blocked.`,\n );\n }\n}\n","import { ReplicaError, ReplicaErrorCode } from \"../errors.js\";\n\nexport type SqlValue = string | number | null | Uint8Array;\n\n/** The synchronous SQLite surface shared by `node:sqlite` and `bun:sqlite`. */\nexport interface SqliteDatabase {\n exec(sql: string): void;\n run(sql: string, ...params: SqlValue[]): void;\n get<T>(sql: string, ...params: SqlValue[]): T | undefined;\n all<T>(sql: string, ...params: SqlValue[]): T[];\n close(): void;\n}\n\ntype Statement = {\n run(...params: SqlValue[]): unknown;\n get(...params: SqlValue[]): unknown;\n all(...params: SqlValue[]): unknown[];\n};\ntype NativeDatabase = { exec(sql: string): void; prepare(sql: string): Statement; close(): void };\n\nexport type SqliteOpener = (path: string) => SqliteDatabase;\n\nfunction wrap(native: NativeDatabase): SqliteDatabase {\n const statements = new Map<string, Statement>();\n const prepared = (sql: string): Statement => {\n let statement = statements.get(sql);\n if (statement === undefined) {\n statement = native.prepare(sql);\n statements.set(sql, statement);\n }\n return statement;\n };\n return {\n exec: (sql) => native.exec(sql),\n run: (sql, ...params) => void prepared(sql).run(...params),\n // bun:sqlite answers a missing row with null, node:sqlite with undefined.\n get: <T>(sql: string, ...params: SqlValue[]) => (prepared(sql).get(...params) ?? undefined) as T | undefined,\n all: <T>(sql: string, ...params: SqlValue[]) => prepared(sql).all(...params) as T[],\n close: () => {\n statements.clear();\n native.close();\n },\n };\n}\n\n/** Node ≥ 22.13 ships `node:sqlite` without a flag. */\nexport function nodeSqliteSupported(version: string): boolean {\n const [major = 0, minor = 0] = version.split(\".\").map(Number);\n return major > 22 || (major === 22 && minor >= 13);\n}\n\n/**\n * Load the runtime's built-in SQLite: `bun:sqlite` under Bun, `node:sqlite`\n * on Node ≥ 22.13. No native addon. Specifiers are computed so bundlers keep\n * both modules external, and only SQLite's ExperimentalWarning is silenced.\n */\nexport async function loadSqlite(): Promise<SqliteOpener> {\n const runtime = globalThis as { Bun?: unknown; process?: NodeJS.Process };\n const load = (specifier: string): Promise<Record<string, unknown>> => import(specifier);\n if (runtime.Bun !== undefined) {\n const module = await load([\"bun\", \"sqlite\"].join(\":\"));\n const Database = module.Database as new (path: string, options: { create: boolean }) => NativeDatabase;\n return (path) => wrap(new Database(path, { create: true }));\n }\n const version = runtime.process?.versions?.node ?? \"0.0.0\";\n if (!nodeSqliteSupported(version)) {\n throw new ReplicaError(\n ReplicaErrorCode.RUNTIME_UNSUPPORTED,\n `tc replica needs Node.js 22.13 or newer (built-in SQLite); this is Node.js ${version}.`,\n { node: version },\n );\n }\n const process = runtime.process!;\n const emitWarning = process.emitWarning;\n process.emitWarning = function (this: NodeJS.Process, warning: string | Error, ...rest: unknown[]) {\n const message = typeof warning === \"string\" ? warning : warning.message;\n const type = typeof rest[0] === \"string\" ? rest[0] : (rest[0] as { type?: string } | undefined)?.type ?? (warning as Error).name;\n if (type === \"ExperimentalWarning\" && /SQLite/i.test(message)) return;\n return (emitWarning as (...args: unknown[]) => void).call(this, warning, ...rest);\n } as NodeJS.Process[\"emitWarning\"];\n let module: Record<string, unknown>;\n try {\n module = await load([\"node\", \"sqlite\"].join(\":\"));\n } finally {\n process.emitWarning = emitWarning;\n }\n const DatabaseSync = module.DatabaseSync as new (path: string) => NativeDatabase;\n return (path) => wrap(new DatabaseSync(path));\n}\n"],"mappings":"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","names":["module","process"]}
|
|
1
|
+
{"version":3,"sources":["../src/sqlite/store.ts","../src/errors.ts","../src/scope.ts","../src/sqlite/driver.ts"],"sourcesContent":["import { randomBytes } from \"node:crypto\";\nimport { constants as fsConstants } from \"node:fs\";\nimport { chmod, link, mkdir, open, readFile, readdir, rm, rmdir, stat, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { ReplicaError, ReplicaErrorCode, isReplicaError } from \"../errors.js\";\nimport { effectiveAuthority } from \"../scope.js\";\nimport type {\n Coverage,\n GrantRecord,\n LeaseToken,\n ListOpts,\n LocalEntry,\n LocalReadPolicy,\n ReplicaConfig,\n ReplicaLastError,\n ReplicaState,\n ReplicaStatus,\n ReplicaStore,\n VerifiedPage,\n} from \"../types.js\";\nimport { loadSqlite, type SqliteDatabase, type SqliteOpener, type SqlValue } from \"./driver.js\";\n\nconst SCHEMA_VERSION = 1;\nexport const DURABILITY = \"sqlite-wal-synchronous-full+fsynced-blobs\";\n\nconst SCHEMA = `\nCREATE TABLE IF NOT EXISTS replica (\n id INTEGER PRIMARY KEY CHECK (id = 1),\n name TEXT NOT NULL,\n replica_id TEXT NOT NULL,\n host TEXT NOT NULL,\n space TEXT NOT NULL,\n prefix TEXT NOT NULL,\n device_did TEXT NOT NULL,\n allow_secrets INTEGER NOT NULL CHECK (allow_secrets IN (0, 1)),\n local_read_policy TEXT NOT NULL CHECK (local_read_policy IN ('whileGrantValid', 'retainAfterExpiry')),\n retention_grant_cid TEXT,\n grant_cid TEXT,\n grant_bytes BLOB,\n grant_audience TEXT,\n grant_issuer TEXT,\n grant_nbf INTEGER,\n grant_exp INTEGER,\n pending_grant_cid TEXT,\n pending_grant_bytes BLOB,\n pending_grant_audience TEXT,\n pending_grant_issuer TEXT,\n pending_grant_nbf INTEGER,\n pending_grant_exp INTEGER,\n node_did TEXT,\n attested INTEGER NOT NULL DEFAULT 0 CHECK (attested IN (0, 1)),\n not_before TEXT,\n expires_at TEXT,\n retain_until TEXT,\n revoked_detail TEXT,\n purge_pending INTEGER NOT NULL DEFAULT 0 CHECK (purge_pending IN (0, 1)),\n retention_revoked TEXT,\n pending_grant_error TEXT,\n cursor TEXT,\n coverage TEXT NOT NULL CHECK (coverage IN ('empty', 'bootstrapping', 'complete')),\n generation INTEGER NOT NULL DEFAULT 0,\n created_at TEXT NOT NULL,\n last_sync_at TEXT,\n last_complete_at TEXT,\n last_error TEXT,\n last_reset TEXT,\n lease_token INTEGER NOT NULL DEFAULT 0,\n lease_holder TEXT,\n lease_expires_at INTEGER\n) STRICT;\nCREATE TABLE IF NOT EXISTS entry (\n key TEXT PRIMARY KEY,\n deleted INTEGER NOT NULL CHECK (deleted IN (0, 1)),\n etag TEXT,\n hash TEXT,\n metadata TEXT NOT NULL,\n content INTEGER NOT NULL CHECK (content IN (0, 1)),\n size INTEGER,\n CHECK (\n (deleted = 1 AND etag IS NULL AND hash IS NULL AND content = 0 AND size IS NULL) OR\n (deleted = 0 AND etag IS NOT NULL AND hash IS NOT NULL)\n )\n) STRICT, WITHOUT ROWID;\nCREATE INDEX IF NOT EXISTS entry_hash ON entry (hash) WHERE hash IS NOT NULL;\n`;\n\ntype ReplicaRow = {\n name: string;\n replica_id: string;\n host: string;\n space: string;\n prefix: string;\n device_did: string;\n allow_secrets: number;\n local_read_policy: LocalReadPolicy;\n retention_grant_cid: string | null;\n grant_cid: string | null;\n grant_bytes: Uint8Array | null;\n grant_audience: string | null;\n grant_issuer: string | null;\n grant_nbf: number | null;\n grant_exp: number | null;\n pending_grant_cid: string | null;\n pending_grant_bytes: Uint8Array | null;\n pending_grant_audience: string | null;\n pending_grant_issuer: string | null;\n pending_grant_nbf: number | null;\n pending_grant_exp: number | null;\n node_did: string | null;\n attested: number;\n not_before: string | null;\n expires_at: string | null;\n retain_until: string | null;\n revoked_detail: string | null;\n purge_pending: number;\n retention_revoked: string | null;\n pending_grant_error: string | null;\n cursor: string | null;\n coverage: Coverage;\n generation: number;\n created_at: string;\n last_sync_at: string | null;\n last_complete_at: string | null;\n last_error: string | null;\n last_reset: string | null;\n lease_token: number;\n lease_holder: string | null;\n lease_expires_at: number | null;\n};\n\ntype EntryRow = {\n key: string;\n deleted: number;\n etag: string | null;\n hash: string | null;\n metadata: string;\n content: number;\n};\n\nfunction grantOf(\n cid: string | null,\n bytes: Uint8Array | null,\n audience: string | null,\n issuer: string | null,\n nbf: number | null,\n exp: number | null,\n): GrantRecord | null {\n if (cid === null || bytes === null || audience === null || issuer === null) return null;\n return { cid, bytes: new Uint8Array(bytes), audience, issuer, notBefore: nbf, expiresAt: exp };\n}\n\nfunction entryOf(row: EntryRow): LocalEntry {\n if (row.deleted === 1) return { key: row.key, deleted: true };\n return {\n key: row.key,\n deleted: false,\n etag: row.etag!,\n hash: row.hash!,\n metadata: JSON.parse(row.metadata) as Record<string, string>,\n content: row.content === 1,\n };\n}\n\nfunction storageError(error: unknown, action: string): ReplicaError {\n if (isReplicaError(error)) return error;\n const code = errnoOf(error);\n const message = error instanceof Error ? error.message : String(error);\n if (code === \"ENOSPC\" || code === \"EDQUOT\" || /SQLITE_FULL|database or disk is full/i.test(`${String(code)} ${message}`)) {\n return new ReplicaError(ReplicaErrorCode.STORAGE_FULL, `${action}: the local disk is full.`, undefined, { cause: error });\n }\n return new ReplicaError(ReplicaErrorCode.STORAGE_ERROR, `${action}: ${message}`, undefined, { cause: error });\n}\n\nconst isHash = (name: string) => /^[0-9a-f]{64}$/.test(name);\n\nfunction errnoOf(error: unknown): unknown {\n return typeof error === \"object\" && error !== null && \"code\" in error ? error.code : undefined;\n}\n\n\n/** A directory's entries; only a directory that does not exist is empty. */\nasync function entriesOf(path: string): Promise<string[]> {\n try {\n return await readdir(path);\n } catch (error) {\n if (errnoOf(error) === \"ENOENT\") return [];\n throw error;\n }\n}\n\n/** Crash- and race-test seam: called at the durability boundaries of `applyPage`. */\nexport type StoreFaults = {\n /** Blobs are durable; no row references them yet. */\n afterBlobs?(): void | Promise<void>;\n /** Rows and cursor are written inside the open transaction, not committed. */\n beforeCommit?(): void;\n /** The page is committed. */\n afterCommit?(): void;\n /** A revocation purge is committed; its blobs are not unlinked yet. */\n afterPurgeMark?(): void;\n};\n\n/**\n * Runs a mutation section (every database write and every filesystem\n * change). The CLI passes one that holds the profile lock briefly and\n * refuses when the profile was deleted, so a command never writes into or\n * recreates a deleted profile.\n */\nexport type MutationGuard = <T>(section: () => Promise<T>) => Promise<T>;\n\nexport type SqliteReplicaStoreOptions = {\n /** Create the replica directory and database if missing (else REPLICA_NOT_FOUND). */\n create: boolean;\n sqlite?: SqliteOpener;\n /** Clock for leases and status (ms since epoch). */\n now?: () => number;\n guard?: MutationGuard;\n};\n\n/** Crash-test seam key. Only tests import it (from this module, not the package entry). */\nexport const FAULTS = Symbol(\"tinycloud.replica.faults\");\ntype InternalOptions = SqliteReplicaStoreOptions & { [FAULTS]?: StoreFaults };\n\n/**\n * The CLI replica store: SQLite (WAL, synchronous=FULL) for entries, cursor\n * and authority, committed in one transaction per page; content-addressed\n * blob files written with fsync + link before any row references them.\n */\nexport class SqliteReplicaStore implements ReplicaStore {\n readonly dir: string;\n readonly #db: SqliteDatabase;\n readonly #holder = `${process.pid}-${randomBytes(6).toString(\"hex\")}`;\n readonly #now: () => number;\n readonly #faults: StoreFaults;\n readonly #guard: MutationGuard;\n /** Identity of the database file this store opened: a deleted-and-recreated replica is a different file. */\n readonly #ino: number;\n\n private constructor(dir: string, db: SqliteDatabase, ino: number, options: SqliteReplicaStoreOptions) {\n this.dir = dir;\n this.#db = db;\n this.#ino = ino;\n this.#now = options.now ?? Date.now;\n this.#faults = (options as InternalOptions)[FAULTS] ?? {};\n this.#guard = options.guard ?? ((section) => section());\n }\n\n /**\n * Read-only status snapshot; does not create a replica, migrate, recover\n * revocation cleanup, or enter the mutation guard. An existing database is\n * opened normally in read-only WAL mode so committed WAL state is visible.\n */\n\n static async inspect(\n dir: string,\n options: { now?: () => number; signal?: AbortSignal } = {},\n ): Promise<{ state: ReplicaState; status: ReplicaStatus } | null> {\n const checkAbort = () => {\n if (options.signal?.aborted) throw options.signal.reason ?? new ReplicaError(ReplicaErrorCode.CLOSED, \"Replica status inspection was cancelled.\");\n };\n checkAbort();\n const dbPath = join(dir, \"replica.db\");\n let ino: number;\n try {\n ino = (await stat(dbPath)).ino;\n } catch (error) {\n if (errnoOf(error) === \"ENOENT\") return null;\n throw storageError(error, \"Inspecting the replica\");\n }\n checkAbort();\n const opener = await loadSqlite();\n checkAbort();\n let db: SqliteDatabase | undefined;\n try {\n db = opener(dbPath, { readonly: true });\n db.exec(\"PRAGMA busy_timeout = 0\");\n checkAbort();\n const store = new SqliteReplicaStore(dir, db, ino, { create: false, ...(options.now ? { now: options.now } : {}) });\n for (let attempt = 0; attempt < 2; attempt++) {\n const before = db.get<{ data_version: number }>(\"PRAGMA data_version\")!.data_version;\n const state = await store.open();\n checkAbort();\n if (state === null) return null;\n const status = await store.status();\n await new Promise<void>((resolve) => setImmediate(resolve));\n checkAbort();\n const after = db.get<{ data_version: number }>(\"PRAGMA data_version\")!.data_version;\n if (before === after) return { state, status };\n }\n throw new ReplicaError(ReplicaErrorCode.BUSY, \"The replica changed during status inspection.\");\n } catch (error) {\n if (/SQLITE_BUSY|database is locked/i.test(error instanceof Error ? error.message : String(error))) {\n throw new ReplicaError(ReplicaErrorCode.BUSY, \"The replica is busy during status inspection.\", undefined, { cause: error });\n }\n throw storageError(error, \"Inspecting the replica\");\n } finally {\n db?.close();\n }\n }\n\n /**\n * Open the replica store in `dir`. With `create: false` a missing replica is\n * REPLICA_NOT_FOUND and nothing is written to disk.\n */\n static async open(dir: string, options: SqliteReplicaStoreOptions): Promise<SqliteReplicaStore> {\n const dbPath = join(dir, \"replica.db\");\n if (!options.create) {\n const exists = await stat(dbPath).then(\n () => true,\n () => false,\n );\n if (!exists) throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, `No replica at ${dir}.`);\n }\n const opener = options.sqlite ?? (await loadSqlite());\n let store: SqliteReplicaStore;\n try {\n if (options.create) {\n await mkdir(join(dir, \"blobs\", \".tmp\"), { recursive: true, mode: 0o700 });\n await Promise.all([chmod(dir, 0o700), chmod(join(dir, \"blobs\"), 0o700), chmod(join(dir, \"blobs\", \".tmp\"), 0o700)]);\n // SQLite gives -wal and -shm the database file's mode.\n const handle = await open(dbPath, fsConstants.O_CREAT | fsConstants.O_RDWR, 0o600);\n await handle.close();\n await chmod(dbPath, 0o600);\n }\n const ino = (await stat(dbPath)).ino;\n const db = opener(dbPath);\n db.exec(\"PRAGMA busy_timeout = 5000\");\n db.exec(\"PRAGMA journal_mode = WAL\");\n db.exec(\"PRAGMA synchronous = FULL\");\n db.exec(\"PRAGMA secure_delete = ON\");\n db.exec(\"PRAGMA foreign_keys = ON\");\n const version = db.get<{ user_version: number }>(\"PRAGMA user_version\")?.user_version ?? 0;\n if (version > SCHEMA_VERSION) {\n db.close();\n throw new ReplicaError(\n ReplicaErrorCode.STORAGE_ERROR,\n `The replica at ${dir} was written by a newer tc (schema ${version}); upgrade tc to read it.`,\n );\n }\n if (version < SCHEMA_VERSION) {\n db.exec(\"BEGIN IMMEDIATE\");\n db.exec(SCHEMA);\n db.exec(`PRAGMA user_version = ${SCHEMA_VERSION}`);\n db.exec(\"COMMIT\");\n }\n store = new SqliteReplicaStore(dir, db, ino, options);\n } catch (error) {\n throw storageError(error, `Opening the replica at ${dir}`);\n }\n // A revocation purge interrupted by a crash (or by a writer that put\n // content back after it) finishes on the next open.\n const row = store.#row();\n if (row?.revoked_detail != null && (row.purge_pending === 1 || (await store.#holdsContentFiles().catch(() => true)))) {\n await store.#finishRevocationPurge().catch(() => undefined);\n }\n return store;\n }\n\n #row(): ReplicaRow | undefined {\n return this.#db.get<ReplicaRow>(\"SELECT * FROM replica WHERE id = 1\");\n }\n\n /** Refuse to touch the filesystem for a replica that was deleted (or replaced) under us. */\n async #assertPresent(): Promise<void> {\n const ino = await stat(join(this.dir, \"replica.db\")).then(\n (stats) => stats.ino,\n () => undefined,\n );\n if (ino !== this.#ino) {\n throw new ReplicaError(\n ReplicaErrorCode.NOT_FOUND,\n `The replica at ${this.dir} was deleted while this command ran (its profile was deleted or the replica purged); nothing more was written.`,\n );\n }\n }\n\n /** A short mutation section: guarded (profile lock) and checked against the database this store opened. */\n #mutate<T>(section: () => Promise<T>): Promise<T> {\n return this.#guard(async () => {\n await this.#assertPresent();\n return section();\n });\n }\n\n /** Run `body` in a write transaction (BEGIN IMMEDIATE). Callers outside a mutation section use `#guardedWrite`. */\n #write<T>(action: string, body: () => T): T {\n try {\n this.#db.exec(\"BEGIN IMMEDIATE\");\n } catch (error) {\n throw storageError(error, action);\n }\n try {\n const result = body();\n this.#db.exec(\"COMMIT\");\n return result;\n } catch (error) {\n try {\n this.#db.exec(\"ROLLBACK\");\n } catch {\n // The transaction is already gone.\n }\n throw storageError(error, action);\n }\n }\n\n /** One write transaction as its own mutation section. */\n #guardedWrite<T>(action: string, body: () => T): Promise<T> {\n return this.#mutate(async () => this.#write(action, body));\n }\n\n /**\n * The caller still holds a live lease (its token, not expired), and the\n * replica is not revoked (unless `allowRevoked`). Inside a write\n * transaction this fences the write; before one it refuses early.\n */\n #checkLease(t: LeaseToken, options: { allowRevoked?: boolean } = {}): void {\n const row = this.#db.get<{ lease_token: number; lease_holder: string | null; lease_expires_at: number | null; revoked_detail: string | null }>(\n \"SELECT lease_token, lease_holder, lease_expires_at, revoked_detail FROM replica WHERE id = 1\",\n );\n if (row?.revoked_detail != null && options.allowRevoked !== true) {\n throw new ReplicaError(ReplicaErrorCode.GRANT_REVOKED, `The replica's grant was revoked: ${row.revoked_detail}`);\n }\n if (row === undefined || row.lease_token !== t.token || row.lease_holder !== t.holder) {\n throw new ReplicaError(ReplicaErrorCode.BUSY, \"Another process took over this replica's sync lease.\");\n }\n if (row.lease_expires_at === null || row.lease_expires_at <= this.#now()) {\n throw new ReplicaError(ReplicaErrorCode.BUSY, \"This process's sync lease expired; nothing was written.\");\n }\n }\n\n async open(): Promise<ReplicaState | null> {\n let row: ReplicaRow | undefined;\n try {\n row = this.#row();\n } catch (error) {\n throw storageError(error, \"Reading the replica\");\n }\n if (row === undefined) return null;\n return {\n config: {\n name: row.name,\n replicaId: row.replica_id,\n host: row.host,\n space: row.space,\n prefix: row.prefix,\n deviceDid: row.device_did,\n allowSecrets: row.allow_secrets === 1,\n localReadPolicy: row.local_read_policy,\n retentionGrantCid: row.retention_grant_cid,\n },\n grant: grantOf(row.grant_cid, row.grant_bytes, row.grant_audience, row.grant_issuer, row.grant_nbf, row.grant_exp),\n pendingGrant: grantOf(\n row.pending_grant_cid,\n row.pending_grant_bytes,\n row.pending_grant_audience,\n row.pending_grant_issuer,\n row.pending_grant_nbf,\n row.pending_grant_exp,\n ),\n nodeDid: row.node_did,\n authority: row.attested === 1 ? { notBefore: row.not_before, expiresAt: row.expires_at, retainUntil: row.retain_until } : null,\n revoked: row.revoked_detail,\n retentionRevoked: row.retention_revoked,\n pendingGrantError: row.pending_grant_error === null ? null : (JSON.parse(row.pending_grant_error) as ReplicaLastError),\n cursor: row.cursor,\n coverage: row.coverage,\n generation: row.generation,\n createdAt: row.created_at,\n lastSyncAt: row.last_sync_at,\n lastCompleteAt: row.last_complete_at,\n lastError: row.last_error === null ? null : (JSON.parse(row.last_error) as ReplicaLastError),\n lastReset: row.last_reset === null ? null : (JSON.parse(row.last_reset) as { at: string; reason: string }),\n };\n }\n\n async init(c: ReplicaConfig): Promise<void> {\n await this.#guardedWrite(\"Creating the replica\", () => {\n if (this.#row() !== undefined) {\n throw new ReplicaError(ReplicaErrorCode.CONFIG_MISMATCH, `Replica ${c.name} already exists.`);\n }\n this.#db.run(\n `INSERT INTO replica (id, name, replica_id, host, space, prefix, device_did, allow_secrets, local_read_policy,\n retention_grant_cid, coverage, created_at)\n VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'empty', ?)`,\n c.name,\n c.replicaId,\n c.host,\n c.space,\n c.prefix,\n c.deviceDid,\n c.allowSecrets ? 1 : 0,\n c.localReadPolicy,\n c.retentionGrantCid,\n new Date(this.#now()).toISOString(),\n );\n });\n }\n\n /** Update the retention opt-in; it takes effect at the next sync. */\n async setRetentionGrant(cid: string | null): Promise<void> {\n await this.#guardedWrite(\"Updating the retention grant\", () => {\n // Changing the CID invalidates the attested retainUntil: it was earned\n // under the previous retain grant, so only a fresh sync under the new\n // CID may re-establish it.\n this.#db.run(\n \"UPDATE replica SET retention_grant_cid = ?, local_read_policy = ?, retain_until = NULL WHERE id = 1 AND retention_grant_cid IS NOT ?\",\n cid,\n cid === null ? \"whileGrantValid\" : \"retainAfterExpiry\",\n cid,\n );\n });\n }\n\n async installGrant(g: GrantRecord): Promise<void> {\n await this.#guardedWrite(\"Installing the grant\", () => {\n const row = this.#row();\n if (row === undefined) throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, \"The replica has not been created.\");\n if (row.grant_cid === g.cid || row.pending_grant_cid === g.cid) return;\n this.#db.run(\n `UPDATE replica SET pending_grant_cid = ?, pending_grant_bytes = ?, pending_grant_audience = ?,\n pending_grant_issuer = ?, pending_grant_nbf = ?, pending_grant_exp = ?, pending_grant_error = NULL WHERE id = 1`,\n g.cid,\n g.bytes,\n g.audience,\n g.issuer,\n g.notBefore,\n g.expiresAt,\n );\n });\n }\n\n async discardPendingGrant(cid: string, detail: string): Promise<void> {\n await this.#guardedWrite(\"Discarding the revoked pending grant\", () => {\n this.#db.run(\n `UPDATE replica SET pending_grant_cid = NULL, pending_grant_bytes = NULL, pending_grant_audience = NULL,\n pending_grant_issuer = NULL, pending_grant_nbf = NULL, pending_grant_exp = NULL, pending_grant_error = NULL,\n last_error = ? WHERE id = 1 AND pending_grant_cid = ?`,\n JSON.stringify({\n at: new Date(this.#now()).toISOString(),\n code: ReplicaErrorCode.GRANT_REVOKED,\n message: `The pending grant ${cid} was revoked and discarded: ${detail}`,\n }),\n cid,\n );\n });\n }\n\n async recordPendingGrantError(cid: string, e: ReplicaLastError): Promise<void> {\n await this.#guardedWrite(\"Recording the pending grant's refusal\", () => {\n this.#db.run(\"UPDATE replica SET pending_grant_error = ? WHERE id = 1 AND pending_grant_cid = ?\", JSON.stringify(e), cid);\n });\n }\n\n async markRetentionRevoked(cid: string, detail: string): Promise<void> {\n await this.#guardedWrite(\"Recording the retention grant's revocation\", () => {\n this.#db.run(\n `UPDATE replica SET retention_grant_cid = NULL, local_read_policy = 'whileGrantValid', retain_until = NULL,\n retention_revoked = ? WHERE id = 1 AND retention_grant_cid = ?`,\n detail,\n cid,\n );\n this.#db.run(\n \"UPDATE replica SET last_error = ? WHERE id = 1\",\n JSON.stringify({ at: new Date(this.#now()).toISOString(), code: ReplicaErrorCode.RETENTION_GRANT_REFUSED, message: detail }),\n );\n });\n }\n\n async acquireSyncLease(ttlMs: number): Promise<LeaseToken | null> {\n return this.#guardedWrite(\"Taking the sync lease\", () => {\n const row = this.#row();\n if (row === undefined) throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, \"The replica has not been created.\");\n const now = this.#now();\n if (row.lease_holder !== null && row.lease_expires_at !== null && row.lease_expires_at > now) return null;\n const token = row.lease_token + 1;\n this.#db.run(\n \"UPDATE replica SET lease_token = ?, lease_holder = ?, lease_expires_at = ? WHERE id = 1\",\n token,\n this.#holder,\n now + ttlMs,\n );\n return { token, holder: this.#holder };\n });\n }\n\n async renewLease(t: LeaseToken, ttlMs: number): Promise<void> {\n await this.#guardedWrite(\"Renewing the sync lease\", () => {\n this.#checkLease(t);\n this.#db.run(\"UPDATE replica SET lease_expires_at = ? WHERE id = 1\", this.#now() + ttlMs);\n });\n }\n\n async releaseLease(t: LeaseToken): Promise<void> {\n await this.#guardedWrite(\"Releasing the sync lease\", () => {\n this.#db.run(\n \"UPDATE replica SET lease_holder = NULL, lease_expires_at = NULL WHERE id = 1 AND lease_token = ? AND lease_holder = ?\",\n t.token,\n t.holder,\n );\n });\n }\n\n async hasContent(hashes: string[]): Promise<Set<string>> {\n const held = new Set<string>();\n for (let start = 0; start < hashes.length; start += 500) {\n const chunk = hashes.slice(start, start + 500);\n const rows = this.#db.all<{ hash: string }>(\n `SELECT DISTINCT hash FROM entry WHERE content = 1 AND hash IN (${chunk.map(() => \"?\").join(\",\")})`,\n ...chunk,\n );\n for (const row of rows) held.add(row.hash);\n }\n return held;\n }\n\n #blobPath(hash: string): string {\n return join(this.dir, \"blobs\", hash.slice(0, 2), hash);\n }\n\n /**\n * Make every blob durable before any row references it: write to a fresh\n * temp file (O_EXCL, 0600), fsync, link to its content address (an existing\n * blob wins), unlink the temp, then fsync each touched directory once.\n * Never creates the replica's own directories: a deleted replica fails.\n */\n async #writeBlobs(blobs: Map<string, Uint8Array>): Promise<void> {\n const dirs = new Set<string>();\n for (const [hash, bytes] of blobs) {\n const final = this.#blobPath(hash);\n const exists = await stat(final).then(\n () => true,\n () => false,\n );\n if (exists) continue;\n const shard = join(this.dir, \"blobs\", hash.slice(0, 2));\n await mkdir(shard, { mode: 0o700 }).catch((error: unknown) => {\n if (errnoOf(error) !== \"EEXIST\") throw error;\n });\n const temp = join(this.dir, \"blobs\", \".tmp\", `${hash}.${randomBytes(6).toString(\"hex\")}`);\n const handle = await open(temp, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY, 0o600);\n try {\n await handle.writeFile(bytes);\n await handle.sync();\n } finally {\n await handle.close();\n }\n try {\n await link(temp, final);\n } catch (error) {\n if (errnoOf(error) !== \"EEXIST\") throw error;\n } finally {\n await unlink(temp);\n }\n dirs.add(shard);\n dirs.add(join(this.dir, \"blobs\"));\n }\n for (const dir of dirs) {\n const handle = await open(dir, fsConstants.O_RDONLY);\n try {\n await handle.sync();\n } finally {\n await handle.close();\n }\n }\n }\n\n async applyPage(t: LeaseToken, p: VerifiedPage): Promise<void> {\n await this.#mutate(async () => {\n // Fence before any content reaches the disk: a writer whose lease was\n // taken over, expired or revoked writes nothing.\n this.#checkLease(t);\n // Resolve every reused blob's size before any row changes: an earlier\n // row of this page may drop the last entry that references it.\n const reused = new Map<string, number>();\n for (const change of p.changes) {\n if (change.deleted || !change.content || p.blobs.has(change.hash) || reused.has(change.hash)) continue;\n const size = this.#db.get<{ size: number | null }>(\n \"SELECT size FROM entry WHERE hash = ? AND content = 1 AND size IS NOT NULL LIMIT 1\",\n change.hash,\n )?.size;\n if (size === undefined || size === null) {\n throw new ReplicaError(ReplicaErrorCode.STORAGE_ERROR, `No stored content backs ${JSON.stringify(change.key)}.`);\n }\n reused.set(change.hash, size);\n }\n try {\n await this.#writeBlobs(p.blobs);\n } catch (error) {\n throw storageError(error, \"Writing replica content\");\n }\n await this.#faults.afterBlobs?.();\n try {\n this.#write(\"Committing a sync page\", () => {\n this.#checkLease(t);\n // Authority is judged now, at the commit, not when the page arrived.\n const now = this.#now();\n if (p.window.notBefore !== null && now < Date.parse(p.window.notBefore)) {\n throw new ReplicaError(ReplicaErrorCode.GRANT_NOT_YET_VALID, `The grant is not valid before ${p.window.notBefore}; nothing was committed.`);\n }\n if (p.window.expiresAt !== null && now >= Date.parse(p.window.expiresAt)) {\n throw new ReplicaError(ReplicaErrorCode.GRANT_EXPIRED, `The grant expired at ${p.window.expiresAt}; nothing was committed.`);\n }\n for (const change of p.changes) {\n if (change.deleted) {\n this.#db.run(\n `INSERT INTO entry (key, deleted, etag, hash, metadata, content, size) VALUES (?, 1, NULL, NULL, '{}', 0, NULL)\n ON CONFLICT (key) DO UPDATE SET deleted = 1, etag = NULL, hash = NULL, metadata = '{}', content = 0, size = NULL`,\n change.key,\n );\n continue;\n }\n const size = change.content ? (p.blobs.get(change.hash)?.length ?? reused.get(change.hash)!) : null;\n this.#db.run(\n `INSERT INTO entry (key, deleted, etag, hash, metadata, content, size) VALUES (?, 0, ?, ?, ?, ?, ?)\n ON CONFLICT (key) DO UPDATE SET deleted = 0, etag = excluded.etag, hash = excluded.hash,\n metadata = excluded.metadata, content = excluded.content, size = excluded.size`,\n change.key,\n change.etag,\n change.hash,\n JSON.stringify(change.metadata),\n change.content ? 1 : 0,\n size,\n );\n }\n const sets: string[] = [\"cursor = ?\", \"coverage = ?\"];\n const params: SqlValue[] = [p.cursor, p.coverage];\n if (p.authority !== null) {\n sets.push(\"node_did = ?\", \"attested = 1\", \"not_before = ?\", \"expires_at = ?\", \"last_sync_at = ?\");\n params.push(p.source.nodeDid, p.authority.notBefore, p.authority.expiresAt, p.at);\n // The attested retainUntil belongs to the retention grant the\n // request presented: if a setRetentionGrant raced the page, the\n // stored CID no longer matches and retain_until stays untouched\n // (null after the CID change) instead of reviving the previous\n // grant's window under a new CID.\n if (this.#row()!.retention_grant_cid === p.retentionGrantCid) {\n sets.push(\"retain_until = ?\");\n params.push(p.authority.retainUntil);\n // A node-attested retention under a new retain grant supersedes a learned revocation of the old one.\n if (p.authority.retainUntil !== null) sets.push(\"retention_revoked = NULL\");\n }\n }\n if (p.complete) {\n sets.push(\"last_complete_at = ?\", \"last_error = NULL\");\n params.push(p.at);\n }\n if (p.promoteGrant !== null) {\n const g = p.promoteGrant;\n sets.push(\"grant_cid = ?\", \"grant_bytes = ?\", \"grant_audience = ?\", \"grant_issuer = ?\", \"grant_nbf = ?\", \"grant_exp = ?\");\n params.push(g.cid, g.bytes, g.audience, g.issuer, g.notBefore, g.expiresAt);\n // Clear the pending slot only if it still holds the grant just validated.\n if (this.#row()!.pending_grant_cid === g.cid) {\n sets.push(\n \"pending_grant_cid = NULL\",\n \"pending_grant_bytes = NULL\",\n \"pending_grant_audience = NULL\",\n \"pending_grant_issuer = NULL\",\n \"pending_grant_nbf = NULL\",\n \"pending_grant_exp = NULL\",\n \"pending_grant_error = NULL\",\n );\n }\n }\n this.#db.run(`UPDATE replica SET ${sets.join(\", \")} WHERE id = 1`, ...params);\n this.#faults.beforeCommit?.();\n });\n } catch (error) {\n // Revoked while this call wrote its blobs: the purge may already have\n // run, so remove them again (a failure leaves purge_pending for open).\n if (isReplicaError(error, ReplicaErrorCode.GRANT_REVOKED)) await this.#purgeContent().catch(() => undefined);\n throw error;\n }\n this.#faults.afterCommit?.();\n });\n }\n\n /** Unlink every blob not in `keep`, and every temp file. Only a missing directory counts as empty. */\n async #unlinkBlobs(keep: Set<string>): Promise<number> {\n let removed = 0;\n const root = join(this.dir, \"blobs\");\n for (const shard of await entriesOf(root)) {\n const shardPath = join(root, shard);\n if (shard === \".tmp\") {\n for (const name of await entriesOf(shardPath)) {\n await rm(join(shardPath, name), { force: true });\n removed += 1;\n }\n continue;\n }\n let kept = 0;\n for (const name of await entriesOf(shardPath)) {\n if (isHash(name) && keep.has(name)) {\n kept += 1;\n continue;\n }\n await rm(join(shardPath, name), { force: true });\n removed += 1;\n }\n if (kept === 0) await rmdir(shardPath).catch(() => undefined);\n }\n return removed;\n }\n\n /** Whether any content file (a blob or a temp file) is on disk. */\n async #holdsContentFiles(): Promise<boolean> {\n const root = join(this.dir, \"blobs\");\n for (const shard of await entriesOf(root)) {\n if ((await entriesOf(join(root, shard))).length > 0) return true;\n }\n return false;\n }\n\n async collectGarbage(t: LeaseToken): Promise<number> {\n return this.#mutate(async () => {\n // The write lock is held across the unlinks, and the lease token is\n // checked under it: a paused former holder can never unlink a blob the\n // current holder just committed a reference to.\n try {\n this.#db.exec(\"BEGIN IMMEDIATE\");\n } catch (error) {\n throw storageError(error, \"Collecting unused content\");\n }\n try {\n this.#checkLease(t);\n const keep = new Set(\n this.#db.all<{ hash: string }>(\"SELECT DISTINCT hash FROM entry WHERE content = 1\").map((row) => row.hash),\n );\n const removed = await this.#unlinkBlobs(keep);\n this.#db.exec(\"COMMIT\");\n return removed;\n } catch (error) {\n try {\n this.#db.exec(\"ROLLBACK\");\n } catch {\n // already rolled back\n }\n throw storageError(error, \"Collecting unused content\");\n }\n });\n }\n\n async reset(t: LeaseToken, reason: string, options: { keepSource?: boolean } = {}): Promise<void> {\n await this.#mutate(async () => {\n this.#write(\"Resetting the replica\", () => {\n this.#checkLease(t);\n this.#db.run(\"DELETE FROM entry\");\n this.#db.run(\n `UPDATE replica SET cursor = NULL, ${options.keepSource ? \"\" : \"node_did = NULL, \"}coverage = 'empty', last_sync_at = NULL,\n last_complete_at = NULL, last_error = NULL, last_reset = ?, generation = generation + 1 WHERE id = 1`,\n JSON.stringify({ at: new Date(this.#now()).toISOString(), reason }),\n );\n });\n });\n await this.collectGarbage(t);\n if (!this.#checkpoint()) {\n throw new ReplicaError(\n ReplicaErrorCode.BUSY,\n \"The replica was reset, but another process is reading it, so its old pages are still in the write-ahead log. Run the reset again when that process ends.\",\n );\n }\n }\n\n /**\n * Remove this replica's directory for good (`tc replica reset --purge`).\n * Fenced like a commit: `t` must still be a live lease on the database this\n * store opened, checked under the write lock, so a paused process can\n * never remove a replica another process took over or recreated. Allowed\n * on a revoked replica. The store is unusable afterwards; close it.\n */\n async destroy(t: LeaseToken): Promise<void> {\n await this.#mutate(async () => {\n try {\n this.#db.exec(\"BEGIN IMMEDIATE\");\n } catch (error) {\n throw storageError(error, \"Removing the replica\");\n }\n try {\n this.#checkLease(t, { allowRevoked: true });\n // Holding the write lock: no one can take the lease until the directory is gone.\n await this.#assertPresent();\n await rm(this.dir, { recursive: true, force: true });\n } catch (error) {\n throw storageError(error, \"Removing the replica\");\n } finally {\n try {\n this.#db.exec(\"ROLLBACK\");\n } catch {\n // Nothing was written.\n }\n }\n });\n }\n\n /**\n * Truncate the WAL so purged pages do not linger in it. TRUNCATE waits on\n * readers through busy_timeout; false when one still held it.\n */\n #checkpoint(): boolean {\n try {\n const result = this.#db.get<{ busy: number }>(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n return result === undefined || Number(result.busy) === 0;\n } catch (error) {\n throw storageError(error, \"Checkpointing the replica\");\n }\n }\n\n /**\n * Record the revocation (durable before this returns: reads are blocked\n * from here on, also after a restart), then purge. A purge that fails\n * keeps purge_pending set: status reports it and every open retries it.\n */\n async markRevoked(detail: string): Promise<void> {\n await this.#guardedWrite(\"Recording the revocation\", () => {\n this.#db.run(\"DELETE FROM entry\");\n // Bumping the lease token fences every outstanding writer.\n this.#db.run(\n `UPDATE replica SET revoked_detail = ?, cursor = NULL, coverage = 'empty', purge_pending = 1,\n lease_token = lease_token + 1, lease_holder = NULL, lease_expires_at = NULL, generation = generation + 1,\n last_error = ? WHERE id = 1`,\n detail,\n JSON.stringify({ at: new Date(this.#now()).toISOString(), code: ReplicaErrorCode.GRANT_REVOKED, message: detail }),\n );\n });\n this.#faults.afterPurgeMark?.();\n await this.#finishRevocationPurge().catch(() => undefined);\n }\n\n #finishRevocationPurge(): Promise<void> {\n return this.#mutate(() => this.#purgeContent());\n }\n\n /**\n * Inside a mutation section of a revoked replica: unlink every content\n * file and truncate the WAL. purge_pending is set first and cleared only\n * when both are done.\n */\n async #purgeContent(): Promise<void> {\n this.#write(\"Starting the revocation purge\", () => {\n this.#db.run(\"UPDATE replica SET purge_pending = 1 WHERE id = 1 AND revoked_detail IS NOT NULL\");\n });\n try {\n await this.#unlinkBlobs(new Set());\n } catch (error) {\n throw storageError(error, \"Purging revoked content\");\n }\n if (!this.#checkpoint()) {\n throw new ReplicaError(ReplicaErrorCode.BUSY, \"A reader holds the replica's write-ahead log; the purge finishes on the next open.\");\n }\n this.#write(\"Finishing the revocation purge\", () => {\n this.#db.run(\"UPDATE replica SET purge_pending = 0 WHERE id = 1 AND revoked_detail IS NOT NULL\");\n });\n }\n\n async recordError(e: ReplicaLastError): Promise<void> {\n await this.#guardedWrite(\"Recording the sync error\", () => {\n this.#db.run(\"UPDATE replica SET last_error = ? WHERE id = 1\", JSON.stringify(e));\n });\n }\n\n async get(key: string): Promise<LocalEntry | undefined> {\n try {\n const row = this.#db.get<EntryRow>(\"SELECT key, deleted, etag, hash, metadata, content FROM entry WHERE key = ?\", key);\n return row === undefined ? undefined : entryOf(row);\n } catch (error) {\n throw storageError(error, \"Reading the replica\");\n }\n }\n\n async list(o: ListOpts): Promise<LocalEntry[]> {\n const where = [\"deleted = 0\"];\n const params: SqlValue[] = [];\n if (o.prefix !== undefined && o.prefix !== \"\") {\n where.push(\"key >= ?\", \"substr(key, 1, length(?)) = ?\");\n params.push(o.prefix, o.prefix, o.prefix);\n }\n if (o.after !== undefined) {\n where.push(\"key > ?\");\n params.push(o.after);\n }\n params.push(o.limit ?? -1);\n try {\n return this.#db\n .all<EntryRow>(\n `SELECT key, deleted, etag, hash, metadata, content FROM entry WHERE ${where.join(\" AND \")} ORDER BY key LIMIT ?`,\n ...params,\n )\n .map(entryOf);\n } catch (error) {\n throw storageError(error, \"Listing the replica\");\n }\n }\n\n async pendingRepairs(limit: number): Promise<Array<Extract<LocalEntry, { deleted: false }>>> {\n return this.#db\n .all<EntryRow>(\n \"SELECT key, deleted, etag, hash, metadata, content FROM entry WHERE deleted = 0 AND content = 0 ORDER BY key LIMIT ?\",\n limit,\n )\n .map(entryOf) as Array<Extract<LocalEntry, { deleted: false }>>;\n }\n\n async readContent(hash: string): Promise<Uint8Array | undefined> {\n if (!isHash(hash)) return undefined;\n try {\n return new Uint8Array(await readFile(this.#blobPath(hash)));\n } catch (error) {\n if (errnoOf(error) === \"ENOENT\") return undefined;\n throw storageError(error, \"Reading replica content\");\n }\n }\n\n async status(now = this.#now()): Promise<ReplicaStatus> {\n const state = await this.open();\n if (state === null) throw new ReplicaError(ReplicaErrorCode.NOT_FOUND, \"The replica has not been created.\");\n const row = this.#row()!;\n const counts = this.#db.get<{ keys: number; missing: number; tombstones: number; bytes: number }>(\n `SELECT\n COALESCE(SUM(deleted = 0), 0) AS keys,\n COALESCE(SUM(deleted = 0 AND content = 0), 0) AS missing,\n COALESCE(SUM(deleted = 1), 0) AS tombstones,\n COALESCE(SUM(CASE WHEN deleted = 0 AND content = 1 THEN size ELSE 0 END), 0) AS bytes\n FROM entry`,\n )!;\n const { retentionRevoked: _retentionRevoked, ...authority } = effectiveAuthority({\n window: state.authority,\n grant: state.grant,\n revoked: state.revoked,\n retentionRevoked: state.retentionRevoked,\n policy: state.config.localReadPolicy,\n now,\n });\n return {\n name: state.config.name,\n replicaId: state.config.replicaId,\n source: { host: state.config.host, nodeDid: state.nodeDid, space: state.config.space, prefix: state.config.prefix },\n device: {\n did: state.config.deviceDid,\n delegationCid: state.grant?.cid ?? null,\n pendingDelegationCid: state.pendingGrant?.cid ?? null,\n pendingDelegationError: state.pendingGrantError,\n },\n authority: {\n ...authority,\n localReadPolicy: state.config.localReadPolicy,\n retentionGrantCid: state.config.retentionGrantCid,\n revokedDetail: state.revoked,\n retentionRevokedDetail: state.retentionRevoked,\n },\n consistency: \"observed\",\n coverage: state.coverage,\n lastSyncAt: state.lastSyncAt,\n lastCompleteAt: state.lastCompleteAt,\n counts: { keys: Number(counts.keys), contentMissing: Number(counts.missing), tombstones: Number(counts.tombstones) },\n bytes: Number(counts.bytes),\n durability: DURABILITY,\n syncing: row.lease_holder !== null && row.lease_expires_at !== null && row.lease_expires_at > now,\n purgePending: row.purge_pending === 1,\n lastError: state.lastError,\n lastReset: state.lastReset,\n };\n }\n\n async close(): Promise<void> {\n this.#db.close();\n }\n}\n","/**\n * Typed replica errors. Every failure a caller can act on has its own code;\n * hosts (CLI, worker) map codes to their own exit codes or UI states.\n */\nexport const ReplicaErrorCode = {\n /** Another process holds the sync lease. */\n BUSY: \"REPLICA_BUSY\",\n /** No replica with this name exists. */\n NOT_FOUND: \"REPLICA_NOT_FOUND\",\n /** The replica exists but its stored configuration differs from the request. */\n CONFIG_MISMATCH: \"REPLICA_CONFIG_MISMATCH\",\n /** The runtime has no supported durable store (e.g. Node < 22.13). */\n RUNTIME_UNSUPPORTED: \"RUNTIME_UNSUPPORTED\",\n /** A required open() argument is missing or malformed (e.g. `principal`). */\n INVALID_ARGUMENT: \"REPLICA_INVALID_ARGUMENT\",\n /** The local store failed (I/O, corruption, schema). */\n STORAGE_ERROR: \"STORAGE_ERROR\",\n /** The local disk is full. */\n STORAGE_FULL: \"STORAGE_FULL\",\n /** The key is outside the replica's prefix. */\n NOT_COVERED: \"NOT_COVERED\",\n /** Syncing the secrets space or the vault namespace needs an explicit opt-in. */\n SECRETS_OPT_IN_REQUIRED: \"SECRETS_OPT_IN_REQUIRED\",\n /** No grant is installed, or none covers the requested scope. */\n GRANT_MISSING: \"GRANT_MISSING\",\n /** The grant bytes are not a verifiable UCAN, or its signature is wrong. */\n GRANT_INVALID: \"GRANT_INVALID\",\n /** SIWE/CACAO and other non-UCAN grants cannot be used for a replica. */\n GRANT_FORMAT_UNSUPPORTED: \"GRANT_FORMAT_UNSUPPORTED\",\n /** The grant was issued to a different device. */\n GRANT_AUDIENCE_MISMATCH: \"GRANT_AUDIENCE_MISMATCH\",\n /** The grant lacks `tinycloud.kv/sync` or `tinycloud.kv/get` on the prefix. */\n GRANT_NOT_COVERING: \"GRANT_NOT_COVERING\",\n GRANT_NOT_YET_VALID: \"GRANT_NOT_YET_VALID\",\n GRANT_EXPIRED: \"GRANT_EXPIRED\",\n GRANT_REVOKED: \"GRANT_REVOKED\",\n /** The node refused the invocation for a reason other than revocation. */\n GRANT_UNAUTHORIZED: \"GRANT_UNAUTHORIZED\",\n /** The node refused the retention grant. */\n RETENTION_GRANT_REFUSED: \"RETENTION_GRANT_REFUSED\",\n /** The node could not be reached. */\n NETWORK_ERROR: \"NETWORK_ERROR\",\n /** The node answered with an unexpected status. */\n NODE_ERROR: \"NODE_ERROR\",\n /** The node's answer violates the wire contract. */\n PROTOCOL_ERROR: \"PROTOCOL_ERROR\",\n /** The node asked for a fresh bootstrap (410). Handled inside sync. */\n RESET_REQUIRED: \"RESET_REQUIRED\",\n /** The feed now comes from a different node than the one pinned. */\n SOURCE_CHANGED: \"SOURCE_CHANGED\",\n /** The feed delivered a key outside the replica's prefix. */\n SCOPE_VIOLATION: \"SCOPE_VIOLATION\",\n /** Fetched bytes do not hash to the ETag the node attested for them. */\n CONTENT_MISMATCH: \"CONTENT_MISMATCH\",\n /** A stored blob no longer hashes to its name. */\n INTEGRITY_ERROR: \"INTEGRITY_ERROR\",\n /** The replica handle was closed; in-flight calls settle with this code. */\n CLOSED: \"REPLICA_CLOSED\",\n} as const;\n\nexport type ReplicaErrorCode = (typeof ReplicaErrorCode)[keyof typeof ReplicaErrorCode];\n\n/**\n * The package ships separate entry bundles (`.`, `./sqlite`), each with its own\n * copy of this class, so identity is a registered symbol, not `instanceof`.\n */\nconst REPLICA_ERROR = Symbol.for(\"tinycloud.replica.error\");\n\nexport class ReplicaError extends Error {\n readonly [REPLICA_ERROR] = true;\n readonly code: ReplicaErrorCode;\n readonly detail?: Record<string, unknown>;\n\n constructor(code: ReplicaErrorCode, message: string, detail?: Record<string, unknown>, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"ReplicaError\";\n this.code = code;\n if (detail !== undefined) this.detail = detail;\n }\n}\n\nexport function isReplicaError(error: unknown, code?: ReplicaErrorCode): error is ReplicaError {\n return (\n typeof error === \"object\" &&\n error !== null &&\n (error as { [REPLICA_ERROR]?: unknown })[REPLICA_ERROR] === true &&\n (code === undefined || (error as ReplicaError).code === code)\n );\n}\n","import { ReplicaError, ReplicaErrorCode } from \"./errors.js\";\nimport type { AuthorityState, AuthorityWindow, GrantRecord, LocalReadPolicy } from \"./types.js\";\n\n/**\n * Published replica selector semantics: whole path segments, byte-exact.\n * Bare `notes` selects `notes` and descendants but not `notesX`; `notes/`\n * selects descendants only.\n */\nexport function kvPrefixCovers(prefix: string, key: string): boolean {\n if (prefix === \"\") return true;\n if (prefix.endsWith(\"/\")) return key.startsWith(prefix);\n return key === prefix || key.startsWith(`${prefix}/`);\n}\n\n/** Capability containment: a bare grant is exact; `/` grants descendants. */\nexport function authorityPathCovers(grantPath: string, requestedPath: string): boolean {\n if (grantPath === \"\" || grantPath === \"/\") return true;\n if (grantPath.endsWith(\"/\")) return requestedPath.startsWith(grantPath);\n return requestedPath === grantPath;\n}\n/** Whether a selector contains both the LIST key and its descendants. */\nexport function selectorCoversListRange(selector: string, path: string): boolean {\n const descendants = path.endsWith(\"/\") ? path : `${path}/`;\n return kvPrefixCovers(selector, path) && kvPrefixCovers(selector, descendants);\n}\n\n/**\n * Syncing the owner's secrets space, or any prefix overlapping the vault\n * namespace (including bare `vault`), copies encrypted secret material to\n * disk and needs an explicit opt-in (TC-733).\n */\nexport function requiresSecretsOptIn(space: string, prefix: string): boolean {\n if (space === \"secrets\" || space.endsWith(\":secrets\")) return true;\n const firstSegment = prefix.split(\"/\", 1)[0];\n return firstSegment === \"vault\";\n}\n\n/** `\"blake3-<hex>\"` (quoted or not) → the 64-hex hash, or undefined. */\nexport function hashFromEtag(etag: string): string | undefined {\n const match = /^\"?blake3-([0-9a-f]{64})\"?$/.exec(etag);\n return match?.[1];\n}\n\nconst RFC3339 = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})$/;\n\n/** An RFC 3339 instant that parses: the only form an authority bound may take. */\nexport function isInstant(value: string): boolean {\n return RFC3339.test(value) && Number.isFinite(Date.parse(value));\n}\n\nexport type EffectiveAuthority = {\n state: AuthorityState;\n notBefore: string | null;\n expiresAt: string | null;\n retainUntil: string | null;\n /** The retention grant was revoked: after expiry, reads are revoked rather than expired. */\n retentionRevoked: boolean;\n};\n\nfunction minIso(a: string | null, bSeconds: number | null): string | null {\n if (bSeconds === null) return a;\n const b = new Date(bSeconds * 1000).toISOString();\n if (a === null) return b;\n return Date.parse(a) <= Date.parse(b) ? a : b;\n}\n\nfunction maxIso(a: string | null, bSeconds: number | null): string | null {\n if (bSeconds === null) return a;\n const b = new Date(bSeconds * 1000).toISOString();\n if (a === null) return b;\n return Date.parse(a) >= Date.parse(b) ? a : b;\n}\n\n/**\n * The window local reads are enforced against: the node-attested window from\n * the last successful sync, bounded by the installed leaf grant (an upper\n * bound only — a multi-parent ancestor can end the chain earlier, which only\n * the node can see).\n */\nexport function effectiveAuthority(input: {\n window: AuthorityWindow | null;\n grant: GrantRecord | null;\n revoked: string | null;\n retentionRevoked?: string | null;\n policy: LocalReadPolicy;\n now: number;\n}): EffectiveAuthority {\n const notBefore = maxIso(input.window?.notBefore ?? null, input.grant?.notBefore ?? null);\n const expiresAt = minIso(input.window?.expiresAt ?? null, input.grant?.expiresAt ?? null);\n const retainUntil = input.policy === \"retainAfterExpiry\" ? input.window?.retainUntil ?? null : null;\n let state: AuthorityState = \"valid\";\n if (input.revoked !== null) state = \"revoked\";\n else if (notBefore !== null && input.now < Date.parse(notBefore)) state = \"not-yet-valid\";\n else if (expiresAt !== null && input.now >= Date.parse(expiresAt)) state = \"expired\";\n return { state, notBefore, expiresAt, retainUntil, retentionRevoked: (input.retentionRevoked ?? null) !== null };\n}\n\n/**\n * Throws unless local reads are allowed now. Returns the reported authority\n * state: `valid`, or `expired` while a node-attested retention holds.\n */\nexport function assertReadable(authority: EffectiveAuthority, now: number): AuthorityState {\n switch (authority.state) {\n case \"valid\":\n return \"valid\";\n case \"revoked\":\n throw new ReplicaError(ReplicaErrorCode.GRANT_REVOKED, \"The replica's grant was revoked; local reads are blocked.\");\n case \"not-yet-valid\":\n throw new ReplicaError(\n ReplicaErrorCode.GRANT_NOT_YET_VALID,\n `The replica's grant is not valid before ${authority.notBefore}.`,\n );\n case \"expired\":\n if (authority.retentionRevoked) {\n throw new ReplicaError(\n ReplicaErrorCode.GRANT_REVOKED,\n `The replica's grant expired at ${authority.expiresAt} and its retention grant was revoked; local reads are blocked.`,\n );\n }\n if (authority.retainUntil !== null && now < Date.parse(authority.retainUntil)) return \"expired\";\n throw new ReplicaError(\n ReplicaErrorCode.GRANT_EXPIRED,\n authority.retainUntil === null\n ? `The replica's grant expired at ${authority.expiresAt}; local reads are blocked.`\n : `The replica's retention ended at ${authority.retainUntil}; local reads are blocked.`,\n );\n }\n}\n","import { ReplicaError, ReplicaErrorCode } from \"../errors.js\";\n\nexport type SqlValue = string | number | null | Uint8Array;\n\n/** The synchronous SQLite surface shared by `node:sqlite` and `bun:sqlite`. */\nexport interface SqliteDatabase {\n exec(sql: string): void;\n run(sql: string, ...params: SqlValue[]): void;\n get<T>(sql: string, ...params: SqlValue[]): T | undefined;\n all<T>(sql: string, ...params: SqlValue[]): T[];\n\n close(): void;\n}\n\ntype Statement = {\n run(...params: SqlValue[]): unknown;\n get(...params: SqlValue[]): unknown;\n all(...params: SqlValue[]): unknown[];\n finalize?(): void;\n};\ntype NativeDatabase = { exec(sql: string): void; prepare(sql: string): Statement; close(): void };\n\nexport type SqliteOpenOptions = { readonly?: boolean };\nexport type SqliteOpener = (path: string, options?: SqliteOpenOptions) => SqliteDatabase;\n\nfunction wrap(native: NativeDatabase): SqliteDatabase {\n const statements = new Map<string, Statement>();\n const prepared = (sql: string): Statement => {\n let statement = statements.get(sql);\n if (statement === undefined) {\n statement = native.prepare(sql);\n statements.set(sql, statement);\n }\n return statement;\n };\n return {\n exec: (sql) => native.exec(sql),\n run: (sql, ...params) => void prepared(sql).run(...params),\n // bun:sqlite answers a missing row with null, node:sqlite with undefined.\n get: <T>(sql: string, ...params: SqlValue[]) => (prepared(sql).get(...params) ?? undefined) as T | undefined,\n all: <T>(sql: string, ...params: SqlValue[]) => prepared(sql).all(...params) as T[],\n close: () => {\n let failure: unknown;\n try {\n for (const statement of statements.values()) {\n try {\n statement.finalize?.();\n } catch (error) {\n failure ??= error;\n }\n }\n } finally {\n statements.clear();\n native.close();\n }\n if (failure !== undefined) throw failure;\n },\n };\n}\n\n\n/** Node ≥ 22.13 ships `node:sqlite` without a flag. */\nexport function nodeSqliteSupported(version: string): boolean {\n const [major = 0, minor = 0] = version.split(\".\").map(Number);\n return major > 22 || (major === 22 && minor >= 13);\n}\n\n/**\n * Load the runtime's built-in SQLite: `bun:sqlite` under Bun, `node:sqlite`\n * on Node ≥ 22.13. No native addon. Specifiers are computed so bundlers keep\n * both modules external, and only SQLite's ExperimentalWarning is silenced.\n */\nexport async function loadSqlite(): Promise<SqliteOpener> {\n const runtime = globalThis as { Bun?: unknown; process?: NodeJS.Process };\n const load = (specifier: string): Promise<Record<string, unknown>> => import(specifier);\n if (runtime.Bun !== undefined) {\n const module = await load([\"bun\", \"sqlite\"].join(\":\"));\n const Database = module.Database as new (path: string, options: { create: boolean; readonly?: boolean } | number) => NativeDatabase;\n return (path, options) =>\n wrap(new Database(path, { create: !options?.readonly, ...(options?.readonly ? { readonly: true } : {}) }));\n }\n const version = runtime.process?.versions?.node ?? \"0.0.0\";\n if (!nodeSqliteSupported(version)) {\n throw new ReplicaError(\n ReplicaErrorCode.RUNTIME_UNSUPPORTED,\n `tc replica needs Node.js 22.13 or newer (built-in SQLite); this is Node.js ${version}.`,\n { node: version },\n );\n }\n const process = runtime.process!;\n const emitWarning = process.emitWarning;\n process.emitWarning = function (this: NodeJS.Process, warning: string | Error, ...rest: unknown[]) {\n const message = typeof warning === \"string\" ? warning : warning.message;\n const type = typeof rest[0] === \"string\" ? rest[0] : (rest[0] as { type?: string } | undefined)?.type ?? (warning as Error).name;\n if (type === \"ExperimentalWarning\" && /SQLite/i.test(message)) return;\n return (emitWarning as (...args: unknown[]) => void).call(this, warning, ...rest);\n } as NodeJS.Process[\"emitWarning\"];\n let module: Record<string, unknown>;\n try {\n module = await load([\"node\", \"sqlite\"].join(\":\"));\n } finally {\n process.emitWarning = emitWarning;\n }\n const DatabaseSync = module.DatabaseSync as new (path: string, options?: { readOnly?: boolean }) => NativeDatabase;\n return (path, options) =>\n wrap(options?.readonly ? new DatabaseSync(path, { readOnly: true }) : new DatabaseSync(path));\n}\n"],"mappings":"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","names":["module","process"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tinycloud/replica",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1-beta.0",
|
|
4
4
|
"description": "Durable, read-only local replicas of a TinyCloud KV prefix over the tinycloud.kv/sync change feed.",
|
|
5
5
|
"author": "TinyCloud, Inc.",
|
|
6
6
|
"license": "EGPL",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@noble/hashes": "^1.8.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@tinycloud/sdk-services": "3.
|
|
49
|
+
"@tinycloud/sdk-services": "3.2.0-beta.3",
|
|
50
50
|
"@tinycloud/web-sdk-wasm": "1.7.6",
|
|
51
51
|
"@types/node": "^20",
|
|
52
52
|
"fake-indexeddb": "^6.2.5",
|