openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
145 lines (144 loc) • 5.6 kB
JavaScript
import { t as pruneMapToMaxSize } from "./map-size-BlrH_CnE.mjs";
import { a as resolveGlobalSingleton } from "./global-singleton-0m-JziJW.mjs";
import { n as registerListener } from "./listeners-BogSNJ-R.mjs";
//#region src/infra/kysely-sync-cache-state.ts
const { kyselyByDatabase, queryErrorHandlerByDatabase } = resolveGlobalSingleton(Symbol.for("openclaw.sqliteKyselyCacheState"), () => ({
kyselyByDatabase: /* @__PURE__ */ new WeakMap(),
queryErrorHandlerByDatabase: /* @__PURE__ */ new WeakMap()
}));
const statementCacheSymbol = Symbol.for("openclaw.kyselySyncStatementCache");
const statementInvalidationSymbol = Symbol.for("openclaw.kyselySyncStatementInvalidation");
const statementCacheEnabledSymbol = Symbol.for("openclaw.kyselySyncStatementCacheEnabled");
const authorizerActiveSymbol = Symbol.for("openclaw.kyselySyncAuthorizerActive");
const disposeCallbacksSymbol = Symbol.for("openclaw.sqliteDisposeCallbacks");
const statementCacheCapacity = 64;
const statementCacheEntryBytes = 65536;
/** Retire dependent native resources before this connection closes or is replaced. */
function registerNodeSqliteDisposeCallback(db, callback) {
const owner = db;
installStatementInvalidation(owner);
return registerListener(owner[disposeCallbacksSymbol] ??= /* @__PURE__ */ new Set(), callback);
}
function disposeNodeSqliteDependents(owner, reason = "close") {
for (const callback of owner[disposeCallbacksSymbol] ?? []) callback(reason);
}
/** Register the lifecycle owner's handler for synchronous Kysely query failures. */
function registerNodeSqliteKyselyQueryErrorHandler(db, handler) {
queryErrorHandlerByDatabase.set(db, handler);
}
/** Drop cached Kysely state for a DatabaseSync. */
function clearNodeSqliteKyselyCacheForDatabase(db) {
delete db[statementCacheSymbol];
kyselyByDatabase.delete(db);
queryErrorHandlerByDatabase.delete(db);
}
function installStatementInvalidation(owner) {
if (owner[statementInvalidationSymbol]) return;
if (typeof owner.setAuthorizer === "function") {
const setAuthorizer = owner.setAuthorizer.bind(owner);
Object.defineProperty(owner, "setAuthorizer", {
configurable: true,
writable: true,
value(callback) {
setAuthorizer(callback);
this[authorizerActiveSymbol] = callback !== null;
delete this[statementCacheSymbol];
}
});
}
if (typeof owner.deserialize === "function") {
const deserialize = owner.deserialize.bind(owner);
Object.defineProperty(owner, "deserialize", {
configurable: true,
writable: true,
value(...args) {
disposeNodeSqliteDependents(this, "replace");
try {
deserialize(...args);
} finally {
delete this[statementCacheSymbol];
}
}
});
}
for (const method of ["close", Symbol.dispose]) if (typeof owner[method] === "function") {
const dispose = owner[method].bind(owner);
Object.defineProperty(owner, method, {
configurable: true,
writable: true,
value() {
disposeNodeSqliteDependents(this);
clearNodeSqliteKyselyCacheForDatabase(this);
return dispose();
}
});
}
Object.defineProperty(owner, statementInvalidationSymbol, {
configurable: true,
value: true
});
}
/**
* Enable bounded statement caching for a lifecycle-owned database that has not
* installed an authorizer before this call.
*/
function enableNodeSqliteKyselyStatementCache(db) {
const owner = db;
installStatementInvalidation(owner);
owner[statementCacheEnabledSymbol] = true;
}
function queryFitsStatementCache(sql, parameters) {
let bytes = Buffer.byteLength(sql);
if (bytes > statementCacheEntryBytes) return false;
for (const parameter of parameters) {
if (typeof parameter === "string") {
if (parameter.length > statementCacheEntryBytes - bytes) return false;
bytes += Buffer.byteLength(parameter);
} else if (ArrayBuffer.isView(parameter)) bytes += parameter.byteLength;
if (bytes > statementCacheEntryBytes) return false;
}
return true;
}
function executeWithCachedStatement(db, sql, parameters, execute) {
const owner = db;
if (!owner[statementCacheEnabledSymbol] || owner[authorizerActiveSymbol] || !queryFitsStatementCache(sql, parameters)) return execute(db.prepare(sql));
let cache = owner[statementCacheSymbol];
if (!cache) {
cache = {
statements: /* @__PURE__ */ new Map(),
candidates: /* @__PURE__ */ new Set(),
active: /* @__PURE__ */ new WeakSet()
};
Object.defineProperty(owner, statementCacheSymbol, {
configurable: true,
value: cache
});
}
const cached = cache.statements.get(sql);
let statement;
if (cached && !cache.active.has(cached)) {
cache.statements.delete(sql);
cache.statements.set(sql, cached);
statement = cached;
} else {
statement = db.prepare(sql);
if (!cached && cache.candidates.delete(sql)) {
cache.statements.set(sql, statement);
pruneMapToMaxSize(cache.statements, statementCacheCapacity);
} else if (!cached) {
cache.candidates.add(sql);
if (cache.candidates.size > statementCacheCapacity) {
const oldestCandidate = cache.candidates.values().next().value;
if (oldestCandidate !== void 0) cache.candidates.delete(oldestCandidate);
}
}
}
cache.active.add(statement);
try {
return execute(statement);
} finally {
cache.active.delete(statement);
}
}
//#endregion
export { installStatementInvalidation as a, registerNodeSqliteDisposeCallback as c, executeWithCachedStatement as i, registerNodeSqliteKyselyQueryErrorHandler as l, disposeNodeSqliteDependents as n, kyselyByDatabase as o, enableNodeSqliteKyselyStatementCache as r, queryErrorHandlerByDatabase as s, clearNodeSqliteKyselyCacheForDatabase as t };