Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions packages/storage/src/__tests__/package-import.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import assert from 'node:assert/strict';
import { spawnSync } from 'node:child_process';
import { test } from 'node:test';

/**
* Regression guard for #1257. Restored after #2710 removed it; the guard's
* absence let three static `node:sqlite` value imports back into the
* `@maka/storage` barrel, so `maka --help` printed Node's SQLite
* ExperimentalWarning again.
*/
function runModule(source: string): { status: number | null; stderr: string } {
const result = spawnSync(process.execPath, ['--input-type=module', '--eval', source], {
encoding: 'utf8',
});
return { status: result.status, stderr: result.stderr };
}

test('top-level package import does not initialize node:sqlite', () => {
const packageEntry = new URL('../index.js', import.meta.url).href;
const result = runModule(`await import(${JSON.stringify(packageEntry)})`);

assert.equal(result.status, 0, result.stderr);
assert.equal(result.stderr, '');
});

test('constructing a SQLite store stays silent on stderr', () => {
const loader = new URL('../sqlite-module.js', import.meta.url).href;
const result = runModule(
`const { loadDatabaseSync } = await import(${JSON.stringify(loader)});` +
`new (loadDatabaseSync())(':memory:').close();`,
);

assert.equal(result.status, 0, result.stderr);
assert.equal(result.stderr, '');
});

test('no compiled module statically imports node:sqlite', async () => {
const { readdir, readFile } = await import('node:fs/promises');
// Tests run against dist/, where `import type` has already been erased — so
// any surviving `node:sqlite` specifier is a real value load.
const distRoot = new URL('../', import.meta.url);
const offenders: string[] = [];
for (const entry of await readdir(distRoot)) {
if (!entry.endsWith('.js') || entry === 'sqlite-module.js') continue;
const text = await readFile(new URL(entry, distRoot), 'utf8');
if (text.includes("'node:sqlite'") || text.includes('"node:sqlite"')) {
offenders.push(entry);
}
}
assert.deepEqual(offenders, []);
});
3 changes: 2 additions & 1 deletion packages/storage/src/codex-session-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
ExternalSessionQuery,
ExternalSessionSummary,
} from '@maka/core/external-session';
import { loadSqliteModule } from './sqlite-module.js';

export const CODEX_SESSION_ADAPTER_ID = 'codex';
export const CODEX_ROLLOUT_MAX_BYTES = 64 * 1024 * 1024;
Expand Down Expand Up @@ -521,7 +522,7 @@ async function readCodexThreadRows(
exactId?: string,
): Promise<CodexThreadRow[] | undefined> {
try {
const sqlite = await import('node:sqlite');
const sqlite = loadSqliteModule();
const db = new sqlite.DatabaseSync(dbPath, { readOnly: true });
try {
const columns = new Set(
Expand Down
3 changes: 2 additions & 1 deletion packages/storage/src/foreign-session-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ import {
type ForeignSessionSource,
type ForeignSessionSummary,
} from '@maka/core/foreign-session';
import { loadSqliteModule } from './sqlite-module.js';

export interface ForeignSessionScanOptions {
/** Only sessions whose recorded cwd equals this path (after realpath-free
Expand Down Expand Up @@ -536,7 +537,7 @@ async function readCodexThreadRows(
cwdFilter?: string,
): Promise<CodexThreadRow[] | undefined> {
try {
const sqlite = await import('node:sqlite');
const sqlite = loadSqliteModule();
const db = new sqlite.DatabaseSync(dbPath, { readOnly: true });
try {
const columns = new Set(
Expand Down
5 changes: 2 additions & 3 deletions packages/storage/src/managed-dependency-environment.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { execFile } from 'node:child_process';
import { createHash, randomUUID } from 'node:crypto';
import { createReadStream } from 'node:fs';
import { createRequire } from 'node:module';
import {
chmod,
cp,
Expand All @@ -19,6 +18,7 @@ import {
} from 'node:fs/promises';
import { dirname, isAbsolute, join, normalize, posix, relative, resolve } from 'node:path';
import type { DatabaseSync } from 'node:sqlite';
import { loadDatabaseSync } from './sqlite-module.js';
import { tryLock, unlock } from 'fs-native-extensions';

const MANAGED_DEPENDENCY_IDENTITY_DOMAIN = 'maka.managed_dependency_environment.v1\0';
Expand Down Expand Up @@ -59,7 +59,6 @@ const RECEIPT_KEYS = [
'contentBytes',
'contentEntries',
] as const;
const require = createRequire(import.meta.url);

export type ManagedDependencyPackageManager = 'npm' | 'pnpm' | 'yarn';

Expand Down Expand Up @@ -202,7 +201,7 @@ interface DependencyAuthorityOwnerLock {
}

function openDependencyReceiptAuthority(path: string): DependencyReceiptAuthority {
const Database = (require('node:sqlite') as typeof import('node:sqlite')).DatabaseSync;
const Database = loadDatabaseSync();
const database: DatabaseSync = new Database(path);
database.exec('PRAGMA synchronous = FULL');
const version = Number(
Expand Down
7 changes: 4 additions & 3 deletions packages/storage/src/operational-state-backup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
writeFile,
} from 'node:fs/promises';
import { dirname, relative, resolve } from 'node:path';
import { DatabaseSync } from 'node:sqlite';
import type { DatabaseSync } from 'node:sqlite';
import { loadDatabaseSync } from './sqlite-module.js';
import { decodeArtifactRecordJsons } from './artifact-metadata-codec.js';
import { withArtifactWriterLock } from './artifact-writer-lock.js';
import { decodeStoredMessage } from './execution-record-codec.js';
Expand Down Expand Up @@ -326,7 +327,7 @@ function validateSqlite(path: string, files: readonly OperationalBackupFile[]):
if (!metadata.isFile() || metadata.isSymbolicLink()) {
throw new Error('runtime.sqlite is not a regular file');
}
const database = new DatabaseSync(path, { readOnly: true });
const database = new (loadDatabaseSync())(path, { readOnly: true });
try {
database.exec('PRAGMA query_only = ON; PRAGMA foreign_keys = ON; BEGIN');
const integrity = database.prepare('PRAGMA integrity_check').all() as Array<{
Expand Down Expand Up @@ -491,7 +492,7 @@ function validateSqlite(path: string, files: readonly OperationalBackupFile[]):
}

function normalizeStandaloneSqliteSnapshot(path: string): void {
const database = new DatabaseSync(path);
const database = new (loadDatabaseSync())(path);
try {
const row = database.prepare('PRAGMA journal_mode = DELETE').get() as
| { journal_mode?: unknown }
Expand Down
26 changes: 1 addition & 25 deletions packages/storage/src/operational-state-store.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { existsSync, mkdirSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { createRequire } from 'node:module';
import type { DatabaseSync } from 'node:sqlite';
import { loadDatabaseSync, loadSqliteModule } from './sqlite-module.js';
import {
configureSqliteRuntimeDatabase,
configureSqliteRuntimeLockWait,
Expand Down Expand Up @@ -118,7 +118,6 @@ const RELEASED_CUTOVER_STORE_VALIDATION_KEYS: ReadonlyMap<string, ReadonlySet<st
['workflow_task_ledger', new Set(['sessions', 'events'])],
]);

const require = createRequire(import.meta.url);
const owners = new Map<string, OperationalStateDatabaseOwner>();

export interface OperationalStateDatabaseOptions {
Expand Down Expand Up @@ -582,29 +581,6 @@ function registerSchema(db: DatabaseSync, scope: string, version: number, applie
`).run(scope, version, appliedAt);
}

function loadDatabaseSync(): typeof import('node:sqlite').DatabaseSync {
const emitWarning = process.emitWarning;
process.emitWarning = ((warning: string | Error, ...args: unknown[]) => {
const warningType = typeof args[0] === 'string' ? args[0] : undefined;
if (
warningType === 'ExperimentalWarning' &&
String(warning).startsWith('SQLite is an experimental feature')
) {
return;
}
Reflect.apply(emitWarning, process, [warning, ...args]);
}) as typeof process.emitWarning;
try {
return (require('node:sqlite') as typeof import('node:sqlite')).DatabaseSync;
} finally {
process.emitWarning = emitWarning;
}
}

function loadSqliteModule(): typeof import('node:sqlite') {
return require('node:sqlite') as typeof import('node:sqlite');
}

function rollback(db: DatabaseSync): void {
try {
db.exec('ROLLBACK');
Expand Down
7 changes: 4 additions & 3 deletions packages/storage/src/operational-target-schema.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DatabaseSync } from 'node:sqlite';
import type { DatabaseSync } from 'node:sqlite';
import { loadDatabaseSync } from './sqlite-module.js';
import { migrateSqliteArtifactDatabase } from './sqlite-artifact-schema.js';
import { migrateSqliteCoreExecutionDatabase } from './sqlite-core-execution-schema.js';
import { migrateSqliteRuntimeDatabase } from './sqlite-runtime-schema.js';
Expand Down Expand Up @@ -45,7 +46,7 @@ export function assertCurrentOperationalTargetSchema(database: DatabaseSync): vo
}

function buildOperationalTargetSchema(): ReadonlyMap<string, string> {
const database = new DatabaseSync(':memory:');
const database = new (loadDatabaseSync())(':memory:');
try {
migrateSqliteRuntimeDatabase(database);
migrateSqliteSessionMetadataDatabase(database);
Expand Down Expand Up @@ -126,7 +127,7 @@ const RELEASED_LEGACY_RETIREMENT_DDL: ReadonlyMap<string, string> = new Map([
let cachedLegacyRetirementSchema: ReadonlyMap<string, ReadonlyMap<string, string>> | undefined;

function buildLegacyRetirementSchema(): ReadonlyMap<string, ReadonlyMap<string, string>> {
const database = new DatabaseSync(':memory:');
const database = new (loadDatabaseSync())(':memory:');
try {
database.exec('PRAGMA foreign_keys = OFF');
for (const ddl of RELEASED_LEGACY_RETIREMENT_DDL.values()) database.exec(ddl);
Expand Down
7 changes: 4 additions & 3 deletions packages/storage/src/session-bundle-policy.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { copyFile, lstat, mkdir, readFile, readdir, realpath, rename, rm } from 'node:fs/promises';
import { randomUUID } from 'node:crypto';
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
import { DatabaseSync } from 'node:sqlite';
import type { DatabaseSync } from 'node:sqlite';
import { loadDatabaseSync } from './sqlite-module.js';
import type { ArtifactRecord } from '@maka/core/artifacts';
import { decodeArtifactRecordJsons } from './artifact-metadata-codec.js';
import { withArtifactWriterLock } from './artifact-writer-lock.js';
Expand Down Expand Up @@ -81,7 +82,7 @@ export async function planSessionBundleExport(

const databasePath = resolve(stateRoot, OPERATIONAL_STATE_DATABASE_NAME);
await assertRegularFile(databasePath, OPERATIONAL_STATE_DATABASE_NAME);
const database = new DatabaseSync(databasePath, { readOnly: true });
const database = new (loadDatabaseSync())(databasePath, { readOnly: true });
let artifacts: ArtifactRecord[];
try {
const session = database
Expand Down Expand Up @@ -182,7 +183,7 @@ async function exportFilteredDatabase(
} finally {
lease.close();
}
const database = new DatabaseSync(destinationPath);
const database = new (loadDatabaseSync())(destinationPath);
try {
database.exec('PRAGMA foreign_keys = OFF; BEGIN IMMEDIATE');
const tables = database
Expand Down
7 changes: 1 addition & 6 deletions packages/storage/src/sqlite-long-term-memory-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import {
lstatSync,
openSync,
} from 'node:fs';
import { createRequire } from 'node:module';
import type { DatabaseSync } from 'node:sqlite';
import { loadDatabaseSync } from './sqlite-module.js';
import {
MemoryItemStoreConflictError,
isMemoryItemKind,
Expand Down Expand Up @@ -60,7 +60,6 @@ const MAX_IDENTIFIER_CODE_POINTS = 512;
const MAX_KEY_CODE_POINTS = 256;
const MAX_OPERATION_RESULT_JSON_CODE_UNITS = 128 * 1_024;

const require = createRequire(import.meta.url);

export type SqliteMemoryItemStoreFailpoint =
| 'after_item_write'
Expand Down Expand Up @@ -1153,10 +1152,6 @@ export class SqliteMemoryItemStore implements MemoryItemStore {
}
}

function loadDatabaseSync(): typeof import('node:sqlite').DatabaseSync {
return (require('node:sqlite') as typeof import('node:sqlite')).DatabaseSync;
}

export function buildSqliteMemoryKeySearchQuery(input: {
readonly terms: readonly string[];
readonly match: 'exact' | 'prefix';
Expand Down
48 changes: 48 additions & 0 deletions packages/storage/src/sqlite-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* The single seam through which this package loads `node:sqlite`.
*
* Two rules, both load-bearing:
*
* 1. LAZY. `node:sqlite` is never a static value import. Node evaluates the
* builtin the moment it enters a module graph, so a static import makes
* every consumer of the `@maka/storage` barrel pay for SQLite — including
* `maka --help`, which never opens a database. That is the regression
* issue #1257 / PR #1258 fixed once already.
* 2. QUIET. Evaluating the builtin emits Node's
* `ExperimentalWarning: SQLite is an experimental feature` on stderr.
* Maka's storage layer is SQLite by design, so the warning carries no
* decision for the user; it only corrupts the stderr of every CLI
* invocation and of subprocess tests that assert clean output. Suppress
* exactly that one warning, for exactly the duration of the load, and
* leave every other warning — deprecations included — untouched.
*/
import { createRequire } from 'node:module';

const require = createRequire(import.meta.url);

let cached: typeof import('node:sqlite') | undefined;

export function loadSqliteModule(): typeof import('node:sqlite') {
if (cached) return cached;
const emitWarning = process.emitWarning;
process.emitWarning = ((warning: string | Error, ...args: unknown[]) => {
const warningType = typeof args[0] === 'string' ? args[0] : undefined;
if (
warningType === 'ExperimentalWarning' &&
String(warning).startsWith('SQLite is an experimental feature')
) {
return;
}
Reflect.apply(emitWarning, process, [warning, ...args]);
}) as typeof process.emitWarning;
try {
cached = require('node:sqlite') as typeof import('node:sqlite');
return cached;
} finally {
process.emitWarning = emitWarning;
}
}

export function loadDatabaseSync(): typeof import('node:sqlite').DatabaseSync {
return loadSqliteModule().DatabaseSync;
}
7 changes: 1 addition & 6 deletions packages/storage/src/sqlite-runtime-store.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createHash } from 'node:crypto';
import { mkdirSync } from 'node:fs';
import { createRequire } from 'node:module';
import { dirname } from 'node:path';
import type { DatabaseSync, SQLInputValue } from 'node:sqlite';
import { loadDatabaseSync } from './sqlite-module.js';
import { isDeepStrictEqual } from 'node:util';
import {
buildWorkspaceBaselineAuthorityEvents,
Expand Down Expand Up @@ -110,11 +110,6 @@ function requireRuntimeEventScanCount(value: unknown): number {
return value as number;
}

const require = createRequire(import.meta.url);

function loadDatabaseSync(): typeof import('node:sqlite').DatabaseSync {
return (require('node:sqlite') as typeof import('node:sqlite')).DatabaseSync;
}

function configureSqliteRuntimeReadOnlyDatabase(db: DatabaseSync): void {
db.exec('PRAGMA busy_timeout = 5000');
Expand Down
22 changes: 1 addition & 21 deletions packages/storage/src/sqlite-session-metadata-store.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { createRequire } from 'node:module';
import { createHash } from 'node:crypto';
import { dirname, resolve } from 'node:path';
import { tmpdir } from 'node:os';
import { existsSync, mkdirSync } from 'node:fs';
import { isDeepStrictEqual } from 'node:util';
import type { DatabaseSync } from 'node:sqlite';
import { loadSqliteModule } from './sqlite-module.js';
import {
AGENT_GRAPH_CLIENT_PROJECTION_SCHEMA_VERSION,
AgentGraphClientProjectionConflictError,
Expand Down Expand Up @@ -126,30 +126,10 @@ const SQLITE_TURN_CONTRIBUTION_MAX_SOURCE_MESSAGES = 1_024;
const SQLITE_TURN_CONTRIBUTION_MAX_SOURCE_BYTES = 4 * 1024 * 1024;
const SQLITE_TURN_LANDMARK_LEGACY_NEIGHBOR_MESSAGES = 32;

const require = createRequire(import.meta.url);
const AGENT_GRAPH_CONTROL_DELETE_TABLES = SQLITE_AGENT_GRAPH_CONTROL_TABLES.filter(
(table) => table !== 'agent_graph_epochs',
).reverse();

function loadSqliteModule(): typeof import('node:sqlite') {
const emitWarning = process.emitWarning;
process.emitWarning = ((warning: string | Error, ...args: unknown[]) => {
const warningType = typeof args[0] === 'string' ? args[0] : undefined;
if (
warningType === 'ExperimentalWarning' &&
String(warning).startsWith('SQLite is an experimental feature')
) {
return;
}
Reflect.apply(emitWarning, process, [warning, ...args]);
}) as typeof process.emitWarning;
try {
return require('node:sqlite') as typeof import('node:sqlite');
} finally {
process.emitWarning = emitWarning;
}
}

export type SqliteSessionMetadataStoreFailpoint =
| 'after_session_row_write'
| 'after_agent_graph_intent_claim_write'
Expand Down