openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
272 lines (271 loc) • 12.3 kB
JavaScript
import { a as resolveGlobalSingleton } from "./global-singleton-0m-JziJW.mjs";
import { r as getAsyncWorkSignal } from "./async-work-scope-DDiH0P7W.mjs";
import { _ as getSqlitePinnedReadSnapshot, h as runSqliteReadOperationSync, i as getAdmittedSqliteSchemaFacts, t as admitSqliteSchema, v as runSqlitePinnedReadSnapshotSync } from "./sqlite-schema-facts-BVf0E_0z.mjs";
import { r as assertExistingDatabaseIdentity } from "./sqlite-worker-identity-BRNRpRyc.mjs";
import { t as isPromiseLike } from "./promise-like-D7-l5Fsp.mjs";
import { i as SqliteCoordinatorError, s as throwSqliteLifecycleErrors } from "./sqlite-lifecycle-errors-DuOygjcn.mjs";
import { t as createSubsystemLogger } from "./subsystem-4NzR43uz.mjs";
import { t as assertTransactionUsable } from "./sqlite-transaction-DBRLvCDM.mjs";
import { n as existingPathOrUndefined } from "./openclaw-state-db.paths-CmKkMYdo.mjs";
import { l as getOpenClawDatabaseMaintenanceResourceScope, u as getOpenClawDatabaseMaintenanceScope } from "./openclaw-state-db-async-lifecycle-DwFy2ZTk.mjs";
import { C as retainOpenClawStateDatabaseForIndependentRead, b as requireOpenClawStateDatabaseIdentity, p as openClawStateDatabaseCache, r as captureOpenClawStateDatabaseReadAdmission, v as registerOpenClawStateDatabaseAsyncResource } from "./openclaw-state-db-cache-CmYLFRSt.mjs";
import { a as isExistingOpenClawStateSchema } from "./openclaw-state-db-schema-policy-B65l9xTe.mjs";
import { o as runWithSqliteWorkerStateContext } from "./sqlite-worker-state-context-G3Xbcj1W.mjs";
import { n as openOpenClawStateReadConnection, t as assertStateReadSchema } from "./openclaw-state-db-read-connection-B7Zi_byb.mjs";
import { a as withCurrentOpenClawStateReadScope, m as canReadWarmNativeSourceIndependently, t as executeExistingOpenClawStateRead } from "./openclaw-state-db-readonly-ywwvuxdQ.mjs";
import { t as createSqliteForeignObservation } from "./sqlite-foreign-observation-PXuD_HBR.mjs";
//#region src/state/openclaw-state-db-current-reader.ts
const log = createSubsystemLogger("state/db");
const currentReaders = resolveGlobalSingleton(Symbol.for("openclaw.stateCurrentReaders"), () => /* @__PURE__ */ new Map());
/** One private live connection per physical store, independent of caller cursors. */
async function prepareOpenClawStateCurrentReader(context) {
const pathname = context.admission.databasePath;
const { key, canonicalPath, birthtime } = context.admission.identity;
const signal = getAsyncWorkSignal();
const assertSource = () => {
signal?.throwIfAborted();
getAsyncWorkSignal()?.throwIfAborted();
context.maintenanceScope?.assertReadAdmission();
context.admission.assertCurrent();
openClawStateDatabaseCache.assertOpenClawStateDatabaseOpenAllowed(pathname, "cached-read");
};
assertSource();
if (!key.startsWith("file:")) {
if (existingPathOrUndefined(pathname)) throw new Error("Current shared-state reader source was created after capture");
return;
}
assertExistingDatabaseIdentity(pathname, key, birthtime);
const physicalKey = `${key}:${birthtime ?? ""}`;
const previous = currentReaders.get(physicalKey);
if (previous?.retiring) previous.close();
if (!currentReaders.has(physicalKey)) {
const admitted = await executeExistingOpenClawStateRead({
path: pathname,
env: context.environment
}, { type: "admit" }, {
context,
current: true,
signal
});
assertSource();
assertExistingDatabaseIdentity(pathname, key, birthtime);
if (!admitted?.ok || admitted.type !== "admit") throw new Error("Current shared-state reader admission did not settle");
}
let reader = currentReaders.get(physicalKey);
if (!reader) {
const connection = openOpenClawStateReadConnection(pathname, pathname, key);
let unregister = () => {};
const opened = {
connection,
observation: createSqliteForeignObservation(connection.database.db, () => {
if (opened.retiring) throw new Error("Current shared-state reader is closed");
assertExistingDatabaseIdentity(canonicalPath, key, birthtime);
}),
users: 0,
retiring: false,
closed: false,
close() {
if (opened.closed) return;
opened.retiring = true;
opened.observation.invalidate();
if (!connection.close()) throw new Error("Current shared-state reader cleanup is incomplete");
opened.closed = true;
if (currentReaders.get(physicalKey) === opened) currentReaders.delete(physicalKey);
unregister();
}
};
try {
unregister = registerOpenClawStateDatabaseAsyncResource({ async close(identity) {
if (!identity || identity.key === key || identity.canonicalPath === canonicalPath) opened.close();
} });
} catch (error) {
try {
opened.close();
} catch (cleanupError) {
throwSqliteLifecycleErrors([error, cleanupError], "Current shared-state reader registration and cleanup failed");
}
throw error;
}
currentReaders.set(physicalKey, opened);
reader = opened;
}
const retained = reader;
retained.users += 1;
let active = true;
const assertCurrent = () => {
if (!active || retained.retiring || !retained.connection.database.db.isOpen) throw new Error("Current shared-state reader is closed");
assertSource();
const integrity = context.stateIntegrity;
if (integrity && Atomics.load(new BigInt64Array(integrity.revision), 0) !== integrity.epoch || context.existingSchemaPath !== void 0 && (!integrity || Atomics.load(new BigInt64Array(integrity.proof), 0) === -1n)) throw new Error("Shared-state reader requires current worker integrity proof");
try {
assertExistingDatabaseIdentity(pathname, key, birthtime);
} catch (error) {
retained.observation.invalidate();
throw error;
}
if (retained.connection.database.db.isTransaction || getSqlitePinnedReadSnapshot(retained.connection.database.db)) throw new Error("Current shared-state reader cannot retain a transaction or snapshot");
};
const release = () => {
if (active) {
active = false;
retained.users -= 1;
}
if (retained.users === 0 && !retained.closed) retained.close();
};
const resource = { async close() {
release();
} };
try {
context.maintenanceScope?.own(resource, "shared-resources", () => resource.close());
assertCurrent();
return {
createCertification() {
assertCurrent();
return retained.observation.createCertification(assertCurrent);
},
read(operation) {
assertCurrent();
const read = () => runWithSqliteWorkerStateContext(context, () => runOpenClawStateCurrentReadConnection(retained.connection, operation, void 0, "require-proof"));
const result = context.runInCapturedSchemaScope ? context.runInCapturedSchemaScope(read) : read();
assertCurrent();
return result;
},
dispose() {
try {
release();
} catch (error) {
log.warn("Current shared-state reader cleanup failed; retaining cleanup custody", { error });
}
}
};
} catch (error) {
try {
release();
} catch (cleanupError) {
throwSqliteLifecycleErrors([error, cleanupError], "Current shared-state reader admission and cleanup failed");
}
throw error;
}
}
/** Mutating maintenance can retain an independent current reader under its exact source lifetime. */
function createOpenClawStateCurrentWarmReader(operation, options, openStateSchemaReadAdmission) {
let bound;
return () => {
return withCurrentOpenClawStateReadScope(options, (pathname) => {
const scope = getOpenClawDatabaseMaintenanceScope();
if (!scope?.ownsSchemaMaintenance || bound && bound.scope !== scope) return { available: false };
if (bound && bound.path !== pathname) throw new Error("Current shared-state reader cannot change its database before cleanup");
if (!bound) {
const native = openClawStateDatabaseCache.getCachedOpenClawStateDatabase(pathname, { readOnly: true });
if (!native?.db.isOpen) return { available: false };
const sourceScope = getOpenClawDatabaseMaintenanceResourceScope(native.db);
if (sourceScope && sourceScope !== scope) return { available: false };
const identity = requireOpenClawStateDatabaseIdentity(native);
if (!canReadWarmNativeSourceIndependently(native, pathname, identity.key)) return { available: false };
const admission = captureOpenClawStateDatabaseReadAdmission(pathname);
const retained = retainOpenClawStateDatabaseForIndependentRead(pathname, "cached-read");
if (!retained) return { available: false };
let connection;
try {
connection = openOpenClawStateReadConnection(pathname, pathname, identity.key);
} catch (error) {
try {
retained.release();
} catch (cleanupError) {
throwSqliteLifecycleErrors([error, cleanupError], "Current shared-state reader open and cleanup failed.");
}
throw error;
}
let active = true;
let unregister = () => {};
const assertCurrent = () => {
if (!active) throw new Error("Current shared-state reader is closed");
scope?.assertReadAdmission();
admission.assertCurrent();
retained.assertCurrent();
assertExistingDatabaseIdentity(pathname, identity.key);
openClawStateDatabaseCache.assertOpenClawStateDatabaseFreshOpenAllowedAtPath(pathname, options.env ?? process.env);
};
const reader = {
path: pathname,
scope,
read() {
assertCurrent();
const result = runOpenClawStateCurrentReadConnection(connection, operation, openStateSchemaReadAdmission);
assertCurrent();
retained.observe();
return result;
}
};
const resource = { async close(closingIdentity) {
if (!active || closingIdentity && closingIdentity.key !== identity.key && closingIdentity.canonicalPath !== identity.canonicalPath) return;
connection.close();
retained.release();
active = false;
if (bound === reader) bound = void 0;
unregister();
} };
unregister = registerOpenClawStateDatabaseAsyncResource(resource);
scope?.own(resource, "shared-resources", () => resource.close());
bound = reader;
}
return {
available: true,
value: bound.read()
};
});
};
}
const currentReaderSchemaAdmissions = /* @__PURE__ */ new WeakMap();
/** An independent current reader keeps composite policy rows in one bounded snapshot. */
function runOpenClawStateCurrentReadConnection(connection, operation, openStateSchemaReadAdmission, integrityPolicy) {
const { db, path: pathname } = connection.database;
let closeAdmission;
const errors = [];
let result;
try {
const previous = currentReaderSchemaAdmissions.get(db);
const facts = previous && !previous.legacyAdmission ? runSqliteReadOperationSync(db, () => getAdmittedSqliteSchemaFacts(db)) : void 0;
if (!previous || previous.admission !== openStateSchemaReadAdmission || previous.legacyAdmission || previous.facts !== facts) closeAdmission = openStateSchemaReadAdmission?.(db);
const existingSchema = isExistingOpenClawStateSchema(pathname, db);
const admit = () => {
const current = getAdmittedSqliteSchemaFacts(db);
const accepted = currentReaderSchemaAdmissions.get(db);
if (!current || accepted?.facts !== current || accepted.existingSchema !== existingSchema || accepted.admission !== openStateSchemaReadAdmission) {
assertStateReadSchema(db, pathname, integrityPolicy);
admitSqliteSchema(db);
const admitted = getAdmittedSqliteSchemaFacts(db);
if (!admitted) throw new Error("Current shared-state reader could not retain schema admission");
currentReaderSchemaAdmissions.set(db, {
facts: admitted,
existingSchema,
admission: openStateSchemaReadAdmission,
legacyAdmission: closeAdmission !== void 0
});
}
};
runSqliteReadOperationSync(db, admit);
result = runSqlitePinnedReadSnapshotSync(db, () => {
const value = operation(connection.database);
if (isPromiseLike(value)) throw new SqliteCoordinatorError("SQLite current-authority read must remain synchronous");
return value;
});
runSqliteReadOperationSync(db, admit);
} catch (error) {
errors.push(error);
}
try {
closeAdmission?.();
} catch (error) {
errors.push(error);
}
try {
assertTransactionUsable(db);
} catch (error) {
errors.push(error);
}
throwSqliteLifecycleErrors(errors, "Current shared-state read and schema cleanup failed.");
return result;
}
//#endregion
export { prepareOpenClawStateCurrentReader as n, createOpenClawStateCurrentWarmReader as t };