UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

189 lines (188 loc) • 9.63 kB
import { t as clearNodeSqliteKyselyCacheForDatabase } from "./kysely-sync-cache-state-DbAMR_DD.mjs"; import { t as assertNoActiveSqliteReaders } from "./sqlite-reader-lifecycle-CF8Y2ajA.mjs"; import { i as getAdmittedSqliteSchemaFacts, t as admitSqliteSchema, u as readSqliteCacheDataVersion } from "./sqlite-schema-facts-BVf0E_0z.mjs"; import { i as setSqliteBusyTimeout } from "./sqlite-busy-timeout-D3v1uB5d.mjs"; import "./openclaw-state-db-contract-BYv0nUQr.mjs"; import { t as assertTransactionUsable } from "./sqlite-transaction-DBRLvCDM.mjs"; import { a as readSqliteUserVersion } from "./sqlite-user-version-DLMl1A7W.mjs"; import { r as resolveDatabasePath } from "./openclaw-state-db.paths-CmKkMYdo.mjs"; import { n as assertSupportedStateSchemaVersion } from "./openclaw-state-db-schema-version-DZI_59qS.mjs"; import { S as assertOpenClawStateDatabaseOwner } from "./openclaw-state-db-fast-path-Kg79WElU.mjs"; import { l as getCanonicalSqliteTableNames, n as assertSqliteSchemaContains, u as readSqliteSchemaCookie } from "./sqlite-schema-contract-BXfI47YR.mjs"; import { n as assertSqliteIntegrity, t as SqliteRepairableForeignKeyError } from "./sqlite-integrity-D3yHPOet.mjs"; import { p as openClawStateDatabaseCache } from "./openclaw-state-db-cache-CmYLFRSt.mjs"; import { a as isExistingOpenClawStateSchema, n as assertOpenClawStateSchemaRepairAllowed } from "./openclaw-state-db-schema-policy-B65l9xTe.mjs"; import { i as openTrackedStateDatabase, r as closeTrackedStateDatabase } from "./openclaw-state-db-integrity-admission-D2ZPZec-.mjs"; import { n as assertOpenClawStateWriteAllowed, u as withStateDatabaseSchemaMaintenance } from "./openclaw-state-ownership-C6zxQPsi.mjs"; import { n as assertExistingOpenClawStateRuntimeSchema, t as assertExistingOpenClawStateRuntimeMetadata } from "./openclaw-state-db-existing-schema-CdYR46xe.mjs"; import { n as runManagedStateTransaction } from "./openclaw-state-db-transaction-BuHteyJk.mjs"; import { t as recoverOrphanTaskDeliveryRows } from "./openclaw-state-db-task-delivery-recovery-CT-XjjJk.mjs"; import fs from "node:fs"; //#region src/state/openclaw-state-db-existing-write.ts function assertExistingOpenClawStateSchemaMetadata(db, pathname, version) { const metadata = assertOpenClawStateDatabaseOwner(db, { pathname }); if (version < 1 || metadata?.schema_version !== version) throw new Error("Existing-state schema metadata is inconsistent."); } /** Validate only the stable storage subset used by an existing-schema owner. * This read neither repairs nor grants write authority; callers retain their * actual handle, generation, lease and publication checks. */ function assertExistingOpenClawStateSchema(db, pathname, schemaSql, compatibility) { const version = assertSupportedStateSchemaVersion(db, pathname); assertExistingOpenClawStateSchemaMetadata(db, pathname, version); assertSqliteIntegrity(db, pathname); assertSqliteSchemaContains(db, pathname, schemaSql, compatibility); return version; } /** A synchronous write to an already-compatible, caller-owned schema subset. * No database bootstrap, schema repair, journal-mode setup, cached publication or WAL timer. * First-use owners may install their declared additive tables; existing objects * must already match. This never opens or migrates the full runtime schema. * The admitted native connection owns transaction serialization. */ function runExistingOpenClawStateWriteTransaction(operation, options, contract) { const prepared = prepareExistingOpenClawStateWriter(options, contract); const write = () => { const writer = createExistingOpenClawStateWriter(prepared, contract); try { return writer.run(operation, options); } finally { writer.close(); } }; return contract.recoverTaskDeliveryOrphans ? withStateDatabaseSchemaMaintenance({ databasePath: prepared.pathname }, write) : write(); } /** Retain the admitted subset connection without bootstrapping the runtime schema. */ function openExistingOpenClawStateWriter(options, contract) { return createExistingOpenClawStateWriter(prepareExistingOpenClawStateWriter(options, contract), contract); } function prepareExistingOpenClawStateWriter(options, contract) { if (options.database || options.readOnly) throw new Error("Existing-state writes require their own tracked writable connection."); const env = options.env ?? process.env; const pathname = resolveDatabasePath({ path: options.path, env }); isExistingOpenClawStateSchema(pathname); if (contract.recoverTaskDeliveryOrphans) assertOpenClawStateSchemaRepairAllowed(pathname); const original = fs.lstatSync(pathname); if (!original.isFile()) throw new Error("Existing-state write requires a regular database file."); return { env, pathname, original }; } function createExistingOpenClawStateWriter({ env, pathname, original }, contract) { const assertSameFile = () => { const current = fs.lstatSync(pathname); if (!current.isFile() || current.dev !== original.dev || current.ino !== original.ino) throw new Error("Existing-state database generation changed."); }; assertSameFile(); openClawStateDatabaseCache.assertOpenClawStateDatabaseFreshOpenAllowedAtPath(pathname, env); const db = openTrackedStateDatabase(pathname, { existingOnly: true, ...contract.recoverTaskDeliveryOrphans ? { enableForeignKeyConstraints: false } : {} }); let closed = false; let admitted; return { run(operation, currentOptions) { if (closed || !db.isOpen) throw new Error("Existing-state writer is closed."); if (currentOptions.database || currentOptions.readOnly) throw new Error("Existing-state writes require their own tracked writable connection."); const currentEnv = currentOptions.env ?? process.env; if (resolveDatabasePath({ path: currentOptions.path, env: currentEnv }) !== pathname) throw new Error("Existing-state writer cannot change its database path."); assertSameFile(); const existingSchema = isExistingOpenClawStateSchema(pathname); if (admitted && existingSchema !== admitted.existingSchema) throw new Error("Existing-state writer schema admission changed."); if (admitted) { readSqliteCacheDataVersion(db); getAdmittedSqliteSchemaFacts(db); } const busyTimeoutMs = currentOptions.busyTimeoutMs ?? contract.busyTimeoutMs ?? 5e3; setSqliteBusyTimeout(db, busyTimeoutMs); let pendingAdmission; const result = runManagedStateTransaction(db, () => { assertSameFile(); assertOpenClawStateWriteAllowed({ database: db, databasePath: pathname, env: currentEnv }); let needsAdmission = !admitted; if (admitted) { readSqliteCacheDataVersion(db); const facts = getAdmittedSqliteSchemaFacts(db); if (!facts) throw new Error("Existing-state writer schema facts are unavailable."); needsAdmission = facts.userVersion !== admitted.version || facts.schemaVersion !== admitted.cookie; } if (needsAdmission && existingSchema) assertExistingOpenClawStateRuntimeSchema(db, pathname); const validate = () => assertExistingOpenClawStateSchema(db, pathname, !admitted && contract.initializeAdditiveSchema ? "" : contract.schemaSql, contract.schemaCompatibility); let version; let recoveryChanges = []; if (admitted && !needsAdmission) { if (existingSchema) version = assertExistingOpenClawStateRuntimeMetadata(db, pathname); else { version = assertSupportedStateSchemaVersion(db, pathname); assertExistingOpenClawStateSchemaMetadata(db, pathname, version); } } else try { version = validate(); } catch (error) { if (!contract.recoverTaskDeliveryOrphans || !(error instanceof SqliteRepairableForeignKeyError)) throw error; recoveryChanges = recoverOrphanTaskDeliveryRows(db, pathname); version = validate(); } if (!admitted && contract.initializeAdditiveSchema) { assertSqliteSchemaContains(db, pathname, contract.schemaSql, { ...contract.schemaCompatibility, allowedMissingTables: getCanonicalSqliteTableNames(contract.schemaSql) }); db.exec(contract.schemaSql); assertSqliteSchemaContains(db, pathname, contract.schemaSql, contract.schemaCompatibility); } const schemaVersion = readSqliteSchemaCookie(db); if (typeof schemaVersion !== "number") throw new Error("Existing-state schema version is unavailable."); if (needsAdmission) { admitSqliteSchema(db); pendingAdmission = { version, cookie: schemaVersion, existingSchema }; } const value = operation({ db, path: pathname, recoveryChanges }); assertSameFile(); if (readSqliteUserVersion(db) !== version || readSqliteSchemaCookie(db) !== schemaVersion) throw new Error("Existing-state transaction cannot migrate schema."); if (contract.recoverTaskDeliveryOrphans) assertSqliteIntegrity(db, pathname); return value; }, { beginLockFailureReporting: contract.beginLockFailureReporting, busyTimeoutMs, databaseLabel: pathname, operationLabel: contract.operationLabel }); if (pendingAdmission) admitted = pendingAdmission; return result; }, assertSettled() { assertSameFile(); assertTransactionUsable(db); if (db.isOpen && db.isTransaction) throw new Error("Existing-state writer retained an unsettled transaction"); if (db.isOpen) assertNoActiveSqliteReaders(db, "Existing-state writer"); }, close() { closed = true; clearNodeSqliteKyselyCacheForDatabase(db); closeTrackedStateDatabase(db); } }; } //#endregion export { runExistingOpenClawStateWriteTransaction as n, openExistingOpenClawStateWriter as t };