← Files DataARCHIVED FILE
templates/data-app/base/src/snapshot-storage.js
22.5 KB · Oct 3, 2026 · 06:03 UTC
// Revisions stay immutable once published so an in-flight stream remains valid.
const currentId = "current";
const encoder = new TextEncoder();
const maxBatchBytes = 256 * 1024;
const batchEnvelopeReserve = 1024;
const maxBindings = 100;
const rowsPerStatement = Math.floor(maxBindings / 5);
const pageCandidates = 1024;
const pageBytes = 256 * 1024;
const schemas = [
`CREATE TABLE IF NOT EXISTS data_app_execution_times_v2 (
revision TEXT PRIMARY KEY, executed_at TEXT
)`,
`CREATE TABLE IF NOT EXISTS data_app_snapshot_head_v2 (
id TEXT PRIMARY KEY, current_generation TEXT NOT NULL, seed_sha256 TEXT NOT NULL
)`,
`CREATE TABLE IF NOT EXISTS data_app_generations_v2 (
generation TEXT PRIMARY KEY, metadata_json TEXT NOT NULL, seed_sha256 TEXT NOT NULL
)`,
`CREATE TABLE IF NOT EXISTS data_app_query_revisions_v2 (
generation TEXT NOT NULL, query_id TEXT NOT NULL, position INTEGER NOT NULL,
query_json TEXT NOT NULL, revision TEXT NOT NULL, row_count INTEGER NOT NULL,
PRIMARY KEY (generation, query_id)
)`,
`CREATE TABLE IF NOT EXISTS data_app_rows_v2 (
generation TEXT NOT NULL, query_id TEXT NOT NULL, revision TEXT NOT NULL,
position INTEGER NOT NULL, row_json TEXT NOT NULL,
PRIMARY KEY (generation, query_id, revision, position)
)`,
];
function failure(code, message) {
const error = new Error(message);
error.code = code;
return error;
}
function checkDatabase(database) {
if (typeof database?.prepare !== "function" || typeof database.batch !== "function") {
throw failure("DATABASE_UNAVAILABLE", "The Sites D1 database is unavailable.");
}
}
function descriptor(sql, params = []) {
if (params.length > maxBindings) throw failure("BINDING_LIMIT", "A statement exceeds 100 bindings.");
const bytes = encoder.encode(JSON.stringify({ sql, params })).byteLength;
if (bytes > maxBatchBytes - batchEnvelopeReserve - 2) {
throw failure("SNAPSHOT_VALUE_TOO_LARGE", "One snapshot value exceeds the bounded database-write budget.");
}
return { sql, params, bytes };
}
async function execute(database, descriptors) {
if (!descriptors.length) return [];
const size = 2 + descriptors.reduce((sum, item) => sum + item.bytes + 1, 0);
if (size > maxBatchBytes - batchEnvelopeReserve) {
throw failure("BATCH_LIMIT", "The database batch exceeds its serialized byte budget.");
}
const result = await database.batch(descriptors.map(({ sql, params }) => database.prepare(sql).bind(...params)));
if (!Array.isArray(result) || result.length !== descriptors.length || result.some(item => item?.success === false)) {
throw failure("DATABASE_BATCH_FAILED", "The database did not complete the snapshot batch.");
}
return result;
}
function writer(database) {
let pending = [], size = 2;
return {
async push(item) {
if (size + item.bytes + 1 > maxBatchBytes - batchEnvelopeReserve) await this.flush();
pending.push(item);
size += item.bytes + 1;
},
async flush() {
if (!pending.length) return;
const batch = pending;
pending = [];
size = 2;
await execute(database, batch);
},
};
}
async function ensureSchema(database) {
checkDatabase(database);
await execute(database, schemas.map(sql => descriptor(sql)));
}
function head(database) {
return database.prepare(
"SELECT current_generation, seed_sha256 FROM data_app_snapshot_head_v2 WHERE id = ?",
).bind(currentId).first();
}
async function rejectPopulatedLegacy(database) {
// Old tables may contain reviewed query updates. Never silently replace them,
// even if their seed hash happens to match the deployed seed.
const existing = await database.prepare(
"SELECT name FROM sqlite_master WHERE type = 'table' AND name IN " +
"('data_app_snapshots', 'data_app_queries', 'data_app_query_rows')",
).all();
for (const { name } of existing.results ?? []) {
// Names originate exclusively from the fixed SQL allowlist above.
if (await database.prepare(`SELECT 1 AS populated FROM ${name} LIMIT 1`).first()) {
throw failure("LEGACY_SNAPSHOT_REQUIRES_MIGRATION", "Existing legacy snapshot data requires an explicit reviewed migration; no data was reset.");
}
}
}
function plainObject(value) {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
async function writeRows(batch, generation, queryId, revision, rows) {
if (!Array.isArray(rows) || rows.some(row => !plainObject(row))) {
throw failure("INVALID_QUERY_ROWS", "Data app query rows must be an array of objects.");
}
let values = [];
const sql = count => "INSERT INTO data_app_rows_v2 " +
"(generation, query_id, revision, position, row_json) VALUES " +
Array.from({ length: count }, () => "(?, ?, ?, ?, ?)").join(", ");
for (let position = 0; position < rows.length; position += 1) {
const row = [generation, queryId, revision, position, JSON.stringify(rows[position])];
// Test a single row first so an oversized value fails explicitly instead of
// escaping the byte budget or producing a partially visible snapshot.
descriptor(sql(1), row);
const combined = values.concat(row);
const combinedBytes = encoder.encode(JSON.stringify({ sql: sql(combined.length / 5), params: combined })).byteLength;
if (values.length && (combined.length > maxBindings || combinedBytes > maxBatchBytes - batchEnvelopeReserve - 3)) {
await batch.push(descriptor(sql(values.length / 5), values));
values = row;
} else {
values = combined;
}
if (values.length === rowsPerStatement * 5) {
await batch.push(descriptor(sql(rowsPerStatement), values));
values = [];
}
}
if (values.length) await batch.push(descriptor(sql(values.length / 5), values));
}
async function initializeSnapshot(database, loadSeed, seedSha256) {
if (typeof loadSeed !== "function" || typeof seedSha256 !== "string" || !/^[a-f\d]{64}$/u.test(seedSha256)) {
throw failure("INVALID_SEED_DESCRIPTOR", "A lazy reviewed seed loader and its precomputed SHA-256 are required.");
}
await ensureSchema(database);
const previous = await head(database);
if (previous?.seed_sha256 === seedSha256) return previous;
if (!previous) await rejectPopulatedLegacy(database);
const seed = await loadSeed();
if (!plainObject(seed) || !plainObject(seed.queries)) {
throw failure("INVALID_SEED", "The reviewed snapshot must contain a queries object.");
}
const generation = crypto.randomUUID();
const { queries, ...metadata } = seed;
const batch = writer(database);
await batch.push(descriptor(
"INSERT INTO data_app_generations_v2 (generation, metadata_json, seed_sha256) VALUES (?, ?, ?)",
[generation, JSON.stringify(metadata), seedSha256],
));
let position = 0;
for (const [queryId, query] of Object.entries(queries)) {
if (!plainObject(query)) throw failure("INVALID_SEED", "Each reviewed query must be an object.");
const { rows = [], ...definition } = query;
if (!Array.isArray(rows)) throw failure("INVALID_QUERY_ROWS", "Reviewed query rows must be an array.");
const revision = crypto.randomUUID();
await batch.push(descriptor(
"INSERT INTO data_app_query_revisions_v2 (generation, query_id, position, query_json, revision, row_count) VALUES (?, ?, ?, ?, ?, ?)",
[generation, queryId, position, JSON.stringify(definition), revision, rows.length],
));
await writeRows(batch, generation, queryId, revision, rows);
position += 1;
}
await batch.flush();
const incomplete = await database.prepare(
"SELECT q.query_id FROM data_app_query_revisions_v2 q " +
"LEFT JOIN data_app_rows_v2 r ON r.generation = q.generation AND r.query_id = q.query_id AND r.revision = q.revision " +
"WHERE q.generation = ? GROUP BY q.query_id, q.row_count HAVING COUNT(r.position) <> q.row_count LIMIT 1",
).bind(generation).first();
if (incomplete) throw failure("INCOMPLETE_SNAPSHOT", "Staged snapshot row counts are incomplete.");
// D1 writes are serialized. Compare against the head observed before loading
// the seed; a delayed older initializer must not overwrite a newer winner.
const activation = previous ? descriptor(
"UPDATE data_app_snapshot_head_v2 SET current_generation = ?, seed_sha256 = ? " +
"WHERE id = ? AND current_generation = ? AND seed_sha256 = ?",
[generation, seedSha256, currentId, previous.current_generation, previous.seed_sha256],
) : descriptor(
"INSERT INTO data_app_snapshot_head_v2 (id, current_generation, seed_sha256) VALUES (?, ?, ?) ON CONFLICT(id) DO NOTHING",
[currentId, generation, seedSha256],
);
const [activated] = await execute(database, [activation]);
if (activated.meta?.changes === 1) return { current_generation: generation, seed_sha256: seedSha256 };
// Same-seed concurrent initialization is idempotent. A different-seed winner
// requires the caller to retry from the current deployment, never a reset.
const winner = await head(database);
if (winner?.seed_sha256 === seedSha256) return winner;
throw failure("SNAPSHOT_HEAD_CONFLICT", "The snapshot head changed during initialization; retry the current deployment.");
}
async function capturedSnapshot(database, seedSha256) {
// D1 batch executes these reads in one transaction. Capture every revision
// pointer with its matching snapshot metadata before streaming. Query
// definitions are immutable within a generation and can be read separately;
// aggregating their SQL/source metadata here would defeat the read budget.
const [metadata, definitions] = await execute(database, [
descriptor("SELECT h.current_generation, h.seed_sha256, g.metadata_json FROM data_app_snapshot_head_v2 h " +
"JOIN data_app_generations_v2 g ON g.generation = h.current_generation WHERE h.id = ?", [currentId]),
descriptor("SELECT q.generation, q.query_id, q.revision, q.row_count, e.executed_at FROM data_app_query_revisions_v2 q " +
"LEFT JOIN data_app_execution_times_v2 e ON e.revision = q.revision " +
"JOIN data_app_snapshot_head_v2 h ON h.current_generation = q.generation WHERE h.id = ? ORDER BY q.position", [currentId]),
]);
const captured = metadata.results?.[0];
if (!captured || captured.seed_sha256 !== seedSha256) {
throw failure("SNAPSHOT_HEAD_CONFLICT", "The reviewed snapshot changed before its response was captured.");
}
return { metadata: captured.metadata_json, queries: definitions.results ?? [] };
}
function objectPrefix(json) {
if (typeof json !== "string" || !json.startsWith("{") || !json.endsWith("}")) {
throw failure("INVALID_STORED_JSON", "Stored snapshot metadata is invalid.");
}
return json.slice(0, -1) + (json.length > 2 ? "," : "");
}
async function* snapshotChunks(database, captured) {
yield objectPrefix(captured.metadata) + '"queries":{';
let firstQuery = true;
for (const query of captured.queries) {
// The write budget bounds each definition, so read one at a time. Select
// the captured generation, not the live head or mutable row revision: an
// owner update changes the row pointer without changing this definition.
const definition = await database.prepare(
"SELECT query_json FROM data_app_query_revisions_v2 WHERE generation = ? AND query_id = ?",
).bind(query.generation, query.query_id).first();
if (!definition) throw failure("INCOMPLETE_SNAPSHOT", "An immutable snapshot query definition is missing.");
if (query.executed_at != null) {
const value = JSON.parse(definition.query_json);
value.source = { ...value.source, executedAt: query.executed_at };
definition.query_json = JSON.stringify(value);
}
yield (firstQuery ? "" : ",") + JSON.stringify(query.query_id) + ":" + objectPrefix(definition.query_json) + '"rows":[';
firstQuery = false;
let position = 0, firstRow = true;
while (position < query.row_count) {
// The inner LIMIT bounds database work, and the cumulative UTF-8 length
// bounds rows returned to this Worker. Write validation guarantees a
// single stored row fits within the page byte budget.
const page = await database.prepare(
"SELECT position, row_json FROM (SELECT position, row_json, " +
"SUM(length(CAST(row_json AS BLOB))) OVER (ORDER BY position) AS page_bytes FROM (" +
"SELECT position, row_json FROM data_app_rows_v2 WHERE generation = ? AND query_id = ? AND revision = ? " +
"AND position >= ? ORDER BY position LIMIT ?)) WHERE page_bytes <= ? ORDER BY position",
).bind(query.generation, query.query_id, query.revision, position, pageCandidates, pageBytes).all();
const rows = page.results ?? [];
if (!rows.length) throw failure("INCOMPLETE_SNAPSHOT", "An immutable snapshot revision is missing rows.");
for (const row of rows) {
if (row.position !== position) throw failure("INCOMPLETE_SNAPSHOT", "Snapshot row positions are not contiguous.");
position += 1;
}
// Emit one bounded page rather than one stream pull per individual row.
yield (firstRow ? "" : ",") + rows.map(row => row.row_json).join(",");
firstRow = false;
}
yield "]}";
}
yield "}}";
}
export async function snapshotResponse(database, loadSeed, seedSha256) {
await initializeSnapshot(database, loadSeed, seedSha256);
const captured = await capturedSnapshot(database, seedSha256);
const iterator = snapshotChunks(database, captured);
const body = new ReadableStream({
async pull(controller) {
try {
const { value, done } = await iterator.next();
if (done) controller.close();
else controller.enqueue(encoder.encode(value));
} catch (error) {
controller.error(error);
await iterator.return?.();
}
},
async cancel() { await iterator.return?.(); },
});
return new Response(body, { headers: {
"content-type": "application/json; charset=utf-8",
"cache-control": "private, no-store",
} });
}
export async function storedQueryExists(database, loadSeed, seedSha256, queryId) {
await initializeSnapshot(database, loadSeed, seedSha256);
const query = await database.prepare(
"SELECT 1 AS found FROM data_app_query_revisions_v2 q JOIN data_app_snapshot_head_v2 h " +
"ON h.current_generation = q.generation WHERE h.id = ? AND h.seed_sha256 = ? AND q.query_id = ?",
).bind(currentId, seedSha256, queryId).first();
return Boolean(query);
}
export async function updateBoundedQuery(database, queryId, rows, generatedAt, seedSha256) {
return updateBoundedQueries(database, [{ queryId, rows }], generatedAt, seedSha256);
}
export async function updateBoundedQueries(database, updates, generatedAt, seedSha256) {
await ensureSchema(database);
if (typeof generatedAt !== "string" || !Number.isFinite(Date.parse(generatedAt))) {
throw failure("INVALID_GENERATED_AT", "A valid generatedAt timestamp is required.");
}
const current = await head(database);
if (!current) throw failure("SNAPSHOT_NOT_INITIALIZED", "Initialize the reviewed snapshot before updating a query.");
if (seedSha256 && current.seed_sha256 !== seedSha256) {
throw failure("SNAPSHOT_HEAD_CONFLICT", "The reviewed snapshot changed before this query update.");
}
const generation = current.current_generation;
const batch = writer(database), replacements = [];
for (const { queryId, rows, executedAt } of updates) {
const previous = await database.prepare(
"SELECT revision FROM data_app_query_revisions_v2 WHERE generation = ? AND query_id = ?",
).bind(generation, queryId).first();
if (!previous) throw failure("QUERY_NOT_FOUND", "Data app query was not found.");
const revision = crypto.randomUUID();
await writeRows(batch, generation, queryId, revision, rows);
await batch.push(descriptor(
"INSERT INTO data_app_execution_times_v2 (revision, executed_at) VALUES (?, " +
"COALESCE(?, (SELECT executed_at FROM data_app_execution_times_v2 WHERE revision = ?)))",
[revision, executedAt ?? null, previous.revision],
));
await batch.flush();
const written = await database.prepare(
"SELECT COUNT(*) AS count FROM data_app_rows_v2 WHERE generation = ? AND query_id = ? AND revision = ?",
).bind(generation, queryId, revision).first();
if (written?.count !== rows.length) throw failure("INCOMPLETE_QUERY", "The staged query revision is incomplete.");
replacements.push({ queryId, revision, previous: previous.revision, count: rows.length });
}
// Materialize the eligibility check before changing any pointer. Every query
// must still have its observed revision, or the entire update stays hidden.
const payload = JSON.stringify(replacements);
const expected = "WITH expected AS MATERIALIZED (SELECT " +
"json_extract(value, '$.queryId') AS id, json_extract(value, '$.revision') AS revision, " +
"json_extract(value, '$.previous') AS previous, json_extract(value, '$.count') AS count FROM json_each(?)) ";
const [updated] = await execute(database, [
descriptor(expected.slice(0, -1) + ", allowed AS MATERIALIZED (SELECT 1 WHERE " +
"EXISTS (SELECT 1 FROM data_app_snapshot_head_v2 WHERE id = ? AND current_generation = ?) " +
"AND NOT EXISTS (SELECT 1 FROM expected e LEFT JOIN data_app_query_revisions_v2 q " +
"ON q.generation = ? AND q.query_id = e.id WHERE q.revision IS NOT e.previous)) " +
"UPDATE data_app_query_revisions_v2 SET " +
"revision = (SELECT revision FROM expected WHERE id = query_id), " +
"row_count = (SELECT count FROM expected WHERE id = query_id) " +
"WHERE generation = ? AND query_id IN (SELECT id FROM expected) AND EXISTS (SELECT 1 FROM allowed)",
[payload, currentId, generation, generation, generation]),
descriptor(expected + "UPDATE data_app_generations_v2 SET metadata_json = json_set(metadata_json, '$.generatedAt', ?) " +
"WHERE generation = ? AND NOT EXISTS (SELECT 1 FROM expected e LEFT JOIN data_app_query_revisions_v2 q " +
"ON q.generation = ? AND q.query_id = e.id WHERE q.revision IS NOT e.revision)",
[payload, generatedAt, generation, generation]),
]);
if (updated.meta?.changes !== updates.length) {
throw failure("QUERY_REVISION_CONFLICT", "The query or snapshot changed while new rows were staged; retry against the current revision.");
}
}
// Existing inline factory calls keep their original storage contract.
// External-asset publication uses the bounded generation API above; it does not
// migrate or reset populated legacy databases.
const legacySnapshotId = "current";
const legacyRowsPerStatement = 30;
const legacySchemas = [
`CREATE TABLE IF NOT EXISTS data_app_snapshots (
id TEXT PRIMARY KEY,
metadata_json TEXT NOT NULL,
seed_sha256 TEXT NOT NULL
)`,
`CREATE TABLE IF NOT EXISTS data_app_queries (
id TEXT PRIMARY KEY,
position INTEGER NOT NULL,
query_json TEXT NOT NULL
)`,
`CREATE TABLE IF NOT EXISTS data_app_query_rows (
query_id TEXT NOT NULL,
position INTEGER NOT NULL,
row_json TEXT NOT NULL,
PRIMARY KEY (query_id, position)
)`,
];
async function legacyFingerprint(snapshot) {
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(JSON.stringify(snapshot)));
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
}
function legacyInsertRows(database, queryId, rows) {
const statements = [];
for (let offset = 0; offset < rows.length; offset += legacyRowsPerStatement) {
const chunk = rows.slice(offset, offset + legacyRowsPerStatement);
const placeholders = chunk.map(() => "(?, ?, ?)").join(", ");
const values = chunk.flatMap((row, index) => [queryId, offset + index, JSON.stringify(row)]);
statements.push(database.prepare(
`INSERT INTO data_app_query_rows (query_id, position, row_json) VALUES ${placeholders}`,
).bind(...values));
}
return statements;
}
async function legacyInitializeSnapshot(database, snapshot, seedFingerprint) {
if (typeof database?.prepare !== "function" || typeof database.batch !== "function") {
throw new Error("The Sites D1 database is unavailable.");
}
await database.batch(legacySchemas.map((schema) => database.prepare(schema)));
const seedHash = seedFingerprint ?? await legacyFingerprint(snapshot);
const previous = await database.prepare(
"SELECT seed_sha256 FROM data_app_snapshots WHERE id = ?",
).bind(legacySnapshotId).first();
if (previous?.seed_sha256 === seedHash) return;
const { queries = {}, ...metadata } = snapshot;
const statements = [
database.prepare("DELETE FROM data_app_query_rows"),
database.prepare("DELETE FROM data_app_queries"),
database.prepare(
"INSERT INTO data_app_snapshots (id, metadata_json, seed_sha256) VALUES (?, ?, ?) "
+ "ON CONFLICT(id) DO UPDATE SET metadata_json = excluded.metadata_json, seed_sha256 = excluded.seed_sha256",
).bind(legacySnapshotId, JSON.stringify(metadata), seedHash),
];
for (const [position, [queryId, query]] of Object.entries(queries).entries()) {
const { rows = [], ...definition } = query;
statements.push(database.prepare(
"INSERT INTO data_app_queries (id, position, query_json) VALUES (?, ?, ?)",
).bind(queryId, position, JSON.stringify(definition)));
statements.push(...legacyInsertRows(database, queryId, rows));
}
await database.batch(statements);
}
export async function storedSnapshot(database, seedSnapshot, seedFingerprint) {
await legacyInitializeSnapshot(database, seedSnapshot, seedFingerprint);
const [snapshot, queryResult, rowResult] = await database.batch([
database.prepare("SELECT metadata_json FROM data_app_snapshots WHERE id = ?").bind(legacySnapshotId),
database.prepare("SELECT id, query_json FROM data_app_queries ORDER BY position"),
database.prepare(
"SELECT query_id, row_json FROM data_app_query_rows ORDER BY query_id, position",
),
]);
const queries = Object.fromEntries(queryResult.results.map(({ id, query_json }) => [
id,
{ ...JSON.parse(query_json), rows: [] },
]));
for (const { query_id, row_json } of rowResult.results) queries[query_id].rows.push(JSON.parse(row_json));
return { ...JSON.parse(snapshot.results[0].metadata_json), queries };
}
export async function updateStoredQueries(database, updates, generatedAt) {
await database.batch([
...updates.flatMap(({ queryId, rows, executedAt }) => [
database.prepare("DELETE FROM data_app_query_rows WHERE query_id = ?").bind(queryId),
...legacyInsertRows(database, queryId, rows),
...(executedAt === undefined ? [] : [database.prepare(
"UPDATE data_app_queries SET query_json = json_set(query_json, '$.source.executedAt', ?) WHERE id = ?",
).bind(executedAt, queryId)]),
]),
database.prepare(
"UPDATE data_app_snapshots SET metadata_json = json_set(metadata_json, '$.generatedAt', ?) WHERE id = ?",
).bind(generatedAt, legacySnapshotId),
]);
}
SHA-256: 58a7a4f0d405a0d1f6bdabdf50e5fe0877ee202354a2f8225bf1ece421a229e1