openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
125 lines (124 loc) • 5.88 kB
JavaScript
import { l as isSupportedOpenClawNodeVersion, r as SUPPORTED_NODE_VERSION_RANGE } from "./node-version-Dq_scYWR.mjs";
//#region node-sqlite.mjs
function probeSqlite(DatabaseSync) {
const result = {
available: false,
version: null,
text: false,
blob: false,
json: false
};
let database;
try {
database = new DatabaseSync(":memory:");
result.available = true;
const version = database.prepare("SELECT sqlite_version() AS version").get()?.version;
result.version = typeof version === "string" ? version : null;
const text = "a\0b\0";
const bytes = Buffer.from(text, "utf8");
const json = JSON.stringify({ value: text });
database.exec("CREATE TABLE probe (text_value TEXT, blob_value BLOB, json_value TEXT)");
database.prepare("INSERT INTO probe VALUES (?, ?, ?)").run(text, bytes, json);
const row = database.prepare("SELECT text_value, blob_value, json_value FROM probe").get();
result.text = typeof row?.text_value === "string" && row.text_value.length === 4 && Buffer.from(row.text_value, "utf8").equals(bytes);
result.blob = row?.blob_value instanceof Uint8Array && Buffer.from(row.blob_value).equals(bytes);
result.json = row?.json_value === json && JSON.parse(row.json_value).value === text;
} catch (error) {
result.error = error instanceof Error ? error.message : String(error);
} finally {
database?.close();
}
return result;
}
const capabilityCacheKey = Symbol.for("openclaw.sqliteCapabilities");
function unavailableSqliteCapabilities(error) {
return {
available: false,
version: null,
text: false,
blob: false,
json: false,
error: error instanceof Error ? error.message : String(error)
};
}
function isSqliteCapabilities(value) {
return value !== null && typeof value === "object" && typeof value.available === "boolean" && (value.version === null || typeof value.version === "string") && typeof value.text === "boolean" && typeof value.blob === "boolean" && typeof value.json === "boolean" && (value.error === void 0 || typeof value.error === "string");
}
async function probeCurrentSqlite() {
let worker;
try {
const DatabaseSync = process.getBuiltinModule?.("node:sqlite")?.DatabaseSync;
if (typeof DatabaseSync !== "function") return unavailableSqliteCapabilities(/* @__PURE__ */ new Error("node:sqlite is unavailable"));
if (!process.versions.bun && process.permission?.has("worker") === false) return probeSqlite(DatabaseSync);
const env = Object.fromEntries(Object.entries(process.env).filter(([name]) => !/^(NODE_OPTIONS|BUN_OPTIONS)$/i.test(name)));
env.NODE_NO_WARNINGS = "1";
const execArgv = !process.versions.bun && process.allowedNodeEnvironmentFlags?.has("--experimental-sqlite") ? ["--experimental-sqlite"] : [];
const { Worker } = await import("node:worker_threads");
worker = new Worker(`const { parentPort } = require("node:worker_threads");
parentPort.postMessage(${SQLITE_CAPABILITY_PROBE});
parentPort.close();`, {
eval: true,
env,
execArgv
});
} catch (error) {
return unavailableSqliteCapabilities(error);
}
return new Promise((resolve) => {
let result;
let failure;
let retirement;
const fail = (error) => {
failure ??= { error };
retirement ??= Promise.resolve().then(() => worker.terminate()).catch((error) => {
failure ??= { error };
});
};
const onMessage = (value) => {
if (result !== void 0 || !isSqliteCapabilities(value)) {
fail(/* @__PURE__ */ new Error("SQLite capability worker returned an invalid result"));
return;
}
result = value;
};
const onExit = (code) => {
worker.off("message", onMessage);
worker.off("error", fail);
worker.off("messageerror", fail);
if (failure) resolve(unavailableSqliteCapabilities(failure.error));
else if (code !== 0 || result === void 0) resolve(unavailableSqliteCapabilities(/* @__PURE__ */ new Error(`SQLite capability worker exited without a complete result (code ${code})`)));
else resolve(result);
};
worker.on("message", onMessage);
worker.on("error", fail);
worker.on("messageerror", fail);
worker.once("exit", onExit);
});
}
function detectCurrentSqliteCapabilities() {
globalThis[capabilityCacheKey] ??= Promise.resolve().then(probeCurrentSqlite);
return Promise.resolve(globalThis[capabilityCacheKey]);
}
function isSqliteWalResetSafeVersion(value) {
const match = /^(\d+)\.(\d+)\.(\d+)$/u.exec(value.trim());
if (!match) return false;
const [major, minor, patch] = match.slice(1).map(Number);
if (![
major,
minor,
patch
].every(Number.isSafeInteger)) return false;
return major > 3 || major === 3 && (minor > 51 || minor === 51 && patch >= 3 || minor === 50 && patch >= 7 || minor === 44 && patch >= 6);
}
function nodeRuntimeFailure(version, probe) {
const label = `Node ${version ?? "unknown"}`;
if (!probe.available) return `${label}: node:sqlite is unavailable; use 24.22.1+/26.12.1+ or a build with the fix.`;
if (!probe.text && !probe.error) return `${label}: node:sqlite truncates TEXT at embedded NUL (nodejs/node#61954); use 24.22.1+/26.12.1+ or a build with the fix`;
if (probe.error || !probe.text || !probe.blob || !probe.json) return `${label}: node:sqlite NUL round-trip capability check failed; use 24.22.1+/26.12.1+ or a build with the fix.`;
if (!probe.version || !isSqliteWalResetSafeVersion(probe.version)) return `${label}: SQLite ${probe.version ?? "unknown"} is not WAL-reset-safe; use SQLite 3.51.3+, 3.50.7+, or 3.44.6+ on its patched release line.`;
if (!isSupportedOpenClawNodeVersion(version)) return `${label}: openclaw requires Node ${SUPPORTED_NODE_VERSION_RANGE.replace(" || ", ", or ")}.`;
return null;
}
const SQLITE_CAPABILITY_PROBE = `(${probeSqlite.toString()})(require("node:sqlite").DatabaseSync)`;
//#endregion
export { nodeRuntimeFailure as i, detectCurrentSqliteCapabilities as n, isSqliteWalResetSafeVersion as r, SQLITE_CAPABILITY_PROBE as t };