@wrongstack/persistence 0.309.1 → 0.310.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,6 +40,7 @@ export declare class PersistenceFsError extends Error {
40
40
  */
41
41
  export declare function createPersistencePrimitives(options?: PersistencePrimitiveOptions): PersistencePrimitives;
42
42
  export declare const atomicWrite: (targetPath: string, content: string | Uint8Array, opts?: AtomicWriteOptions) => Promise<void>;
43
+ export declare const atomicReplaceWithWriter: <T>(targetPath: string, write: (handle: fs.FileHandle) => Promise<T>, opts?: AtomicWriteOptions) => Promise<T>;
43
44
  export declare const ensureDir: (dir: string) => Promise<void>;
44
45
  export declare const withFileLock: <T>(targetPath: string, fn: () => Promise<T>, opts?: FileLockOptions) => Promise<T>;
45
46
  //# sourceMappingURL=atomic-write.d.ts.map
package/dist/index.d.ts CHANGED
@@ -2,4 +2,5 @@ export * from './atomic-write.js';
2
2
  export * from './file-permissions.js';
3
3
  export * from './project-endpoint.js';
4
4
  export * from './socket-path.js';
5
+ export * from './sqlite-runtime.js';
5
6
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -80,7 +80,7 @@ function createPersistencePrimitives(options = {}) {
80
80
  throw error;
81
81
  }
82
82
  }
83
- async function atomicReplaceWithWriter(targetPath, write, opts = {}) {
83
+ async function atomicReplaceWithWriter2(targetPath, write, opts = {}) {
84
84
  await fs.mkdir(path.dirname(targetPath), { recursive: true });
85
85
  const tmp = tempPathFor(targetPath);
86
86
  try {
@@ -135,7 +135,9 @@ function createPersistencePrimitives(options = {}) {
135
135
  await retryAfterBackoff();
136
136
  continue;
137
137
  }
138
- if (code !== "EEXIST" && code !== "EPERM") throw error;
138
+ if (code !== "EEXIST" && code !== "EPERM" && code !== "EACCES" && code !== "EBUSY") {
139
+ throw error;
140
+ }
139
141
  try {
140
142
  const stat2 = await fs.stat(lockPath);
141
143
  if (Date.now() - stat2.mtimeMs > staleMs) {
@@ -171,7 +173,7 @@ function createPersistencePrimitives(options = {}) {
171
173
  await fs.unlink(lockPath).catch(() => void 0);
172
174
  }
173
175
  }
174
- return { atomicWrite: atomicWrite2, atomicReplaceWithWriter, ensureDir: ensureDir2, withFileLock: withFileLock2 };
176
+ return { atomicWrite: atomicWrite2, atomicReplaceWithWriter: atomicReplaceWithWriter2, ensureDir: ensureDir2, withFileLock: withFileLock2 };
175
177
  }
176
178
  async function waitForLockRelease(lockPath, remainingMs) {
177
179
  const parentDir = path.dirname(lockPath);
@@ -251,6 +253,7 @@ async function renameWithRetry(from, to) {
251
253
  }
252
254
  var defaultPrimitives = createPersistencePrimitives();
253
255
  var atomicWrite = defaultPrimitives.atomicWrite;
256
+ var atomicReplaceWithWriter = defaultPrimitives.atomicReplaceWithWriter;
254
257
  var ensureDir = defaultPrimitives.ensureDir;
255
258
  var withFileLock = defaultPrimitives.withFileLock;
256
259
 
@@ -432,17 +435,67 @@ async function bindProjectEndpoint(options) {
432
435
  )
433
436
  };
434
437
  }
438
+
439
+ // src/sqlite-runtime.ts
440
+ import { createRequire } from "node:module";
441
+ function databaseSyncFromNode(module) {
442
+ if (typeof module !== "object" || module === null || !("DatabaseSync" in module)) return null;
443
+ return typeof module.DatabaseSync === "function" ? module.DatabaseSync : null;
444
+ }
445
+ function databaseSyncFromBun(module) {
446
+ if (typeof module !== "object" || module === null || !("Database" in module)) return null;
447
+ if (typeof module.Database !== "function") return null;
448
+ const BunDatabase = module.Database;
449
+ function BunDatabaseSync(filename, options) {
450
+ const bunOptions = options?.readOnly ? { readonly: true, create: false, readwrite: false } : void 0;
451
+ return new BunDatabase(filename, bunOptions);
452
+ }
453
+ return BunDatabaseSync;
454
+ }
455
+ function loadRuntimeDatabaseSync(loadModule = createRequire(import.meta.url)) {
456
+ let nodeError;
457
+ try {
458
+ const Database = databaseSyncFromNode(loadModule("node:sqlite"));
459
+ if (Database) return Database;
460
+ nodeError = new Error("node:sqlite did not export DatabaseSync");
461
+ } catch (error) {
462
+ nodeError = error;
463
+ }
464
+ let bunError;
465
+ try {
466
+ const Database = databaseSyncFromBun(loadModule("bun:sqlite"));
467
+ if (Database) return Database;
468
+ bunError = new Error("bun:sqlite did not export Database");
469
+ } catch (error) {
470
+ bunError = error;
471
+ }
472
+ const describe = (error) => error instanceof Error ? error.message : String(error);
473
+ throw new Error(
474
+ `No supported synchronous SQLite runtime is available (node:sqlite: ${describe(nodeError)}; bun:sqlite: ${describe(bunError)}).`
475
+ );
476
+ }
477
+ function isRuntimeSqliteAvailable(loadModule) {
478
+ try {
479
+ loadRuntimeDatabaseSync(loadModule);
480
+ return true;
481
+ } catch {
482
+ return false;
483
+ }
484
+ }
435
485
  export {
436
486
  PersistenceFsError,
437
487
  SECRET_DIR_MODE,
438
488
  SECRET_FILE_MODE,
439
489
  assertUnixSocketPathWithinLimit,
490
+ atomicReplaceWithWriter,
440
491
  atomicWrite,
441
492
  bindProjectEndpoint,
442
493
  checkUnixSocketPath,
443
494
  createPersistencePrimitives,
444
495
  ensureDir,
445
496
  isProjectEndpointLive,
497
+ isRuntimeSqliteAvailable,
498
+ loadRuntimeDatabaseSync,
446
499
  restrictDirPermissions,
447
500
  restrictFilePermissions,
448
501
  unixSocketPathLimit,
@@ -0,0 +1,13 @@
1
+ import type { DatabaseSync } from 'node:sqlite';
2
+ type DatabaseSyncConstructor = typeof DatabaseSync;
3
+ type ModuleLoader = (specifier: string) => unknown;
4
+ /**
5
+ * Resolve the synchronous SQLite constructor for the active JavaScript runtime.
6
+ * Node uses `node:sqlite`; Bun uses a small constructor adapter over `bun:sqlite`.
7
+ * The SQL surface WrongStack relies on (`exec`, `prepare`, `get`, `all`, `run`,
8
+ * and `close`) is shared by both implementations.
9
+ */
10
+ export declare function loadRuntimeDatabaseSync(loadModule?: ModuleLoader): DatabaseSyncConstructor;
11
+ export declare function isRuntimeSqliteAvailable(loadModule?: ModuleLoader): boolean;
12
+ export {};
13
+ //# sourceMappingURL=sqlite-runtime.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/persistence",
3
- "version": "0.309.1",
3
+ "version": "0.310.0",
4
4
  "license": "MIT",
5
5
  "description": "Dependency-free filesystem persistence primitives shared across WrongStack packages.",
6
6
  "repository": {
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "scripts": {
37
37
  "build": "node ../../scripts/build-package.mjs",
38
- "typecheck": "tsc --noEmit",
38
+ "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.test.json",
39
39
  "test": "vitest run --config vitest.config.ts",
40
40
  "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\""
41
41
  }