@monlite/core 0.10.0 → 1.1.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.
package/dist/index.d.cts CHANGED
@@ -357,12 +357,28 @@ interface GroupByArgs<T = Doc> {
357
357
  }
358
358
  type GroupByResult = Record<string, any>;
359
359
  type DriverName = "auto" | "better-sqlite3" | "node:sqlite";
360
+ /** Encryption-at-rest configuration (requires `better-sqlite3-multiple-ciphers`). */
361
+ interface EncryptionOptions {
362
+ /** The passphrase used to encrypt/decrypt the database file. */
363
+ key: string;
364
+ /**
365
+ * Cipher scheme to use (e.g. `"sqlcipher"`, `"chacha20"`, `"aes256cbc"`).
366
+ * Defaults to the library default (ChaCha20-Poly1305).
367
+ */
368
+ cipher?: string;
369
+ }
360
370
  interface MonliteOptions {
361
371
  /**
362
372
  * Which SQLite backend to use. `"auto"` (default) prefers `better-sqlite3`
363
373
  * when installed, otherwise the built-in `node:sqlite` (Node >= 22.5).
364
374
  */
365
375
  driver?: DriverName;
376
+ /**
377
+ * Encrypt the database at rest. Requires the `better-sqlite3-multiple-ciphers`
378
+ * package (a drop-in for `better-sqlite3`); not supported on `node:sqlite`.
379
+ * Use `db.rekey(newKey)` to rotate the key.
380
+ */
381
+ encryption?: EncryptionOptions;
366
382
  /** Opt-in plugins (e.g. `@monlite/fts`). */
367
383
  plugins?: MonlitePlugin[];
368
384
  /**
@@ -414,6 +430,8 @@ interface Driver {
414
430
  /** Run `fn` inside a transaction; rolls back and rethrows if it throws. */
415
431
  transaction<T>(fn: () => T): T;
416
432
  close(): void;
433
+ /** Rotate the encryption key (encrypted backends only). */
434
+ rekey?(key: string, cipher?: string): void;
417
435
  /** The underlying native handle (better-sqlite3 Database / node:sqlite DatabaseSync). */
418
436
  readonly raw: any;
419
437
  }
@@ -613,6 +631,7 @@ declare class Monlite {
613
631
  readonly $sync?: SyncStore;
614
632
  private readonly collections;
615
633
  private readonly plugins;
634
+ private readonly encrypted;
616
635
  private closed;
617
636
  constructor(filename: string, options?: MonliteOptions);
618
637
  /** @internal Notify plugins that documents changed (post-commit). */
@@ -655,6 +674,11 @@ declare class Monlite {
655
674
  * `VACUUM INTO`). The destination file must not already exist.
656
675
  */
657
676
  backup(path: string): Promise<void>;
677
+ /**
678
+ * Rotate the encryption key. Only valid for a database opened with the
679
+ * `encryption` option; throws otherwise. Pass `cipher` to also change scheme.
680
+ */
681
+ rekey(key: string, cipher?: string): void;
658
682
  /** Close the underlying SQLite connection. */
659
683
  $disconnect(): Promise<void>;
660
684
  private assertOpen;
@@ -677,6 +701,12 @@ declare class MonliteQueryError extends MonliteError {
677
701
  cause?: unknown;
678
702
  });
679
703
  }
704
+ /** Thrown when an encrypted database can't be opened (wrong key, not encrypted). */
705
+ declare class MonliteEncryptionError extends MonliteError {
706
+ constructor(message: string, options?: {
707
+ cause?: unknown;
708
+ });
709
+ }
680
710
  /** A database constraint was violated (base class for the specific kinds). */
681
711
  declare class MonliteConstraintError extends MonliteError {
682
712
  readonly collection?: string;
@@ -717,4 +747,4 @@ declare function objectId(): string;
717
747
  /** True when a value looks like a monlite/ObjectId id (24 hex chars). */
718
748
  declare function isObjectId(value: unknown): value is string;
719
749
 
720
- export { type AggregateArgs, type AggregateResult, type ApplyResult, Collection, type CollectionMode, type CollectionOptions, type CollectionSchema, type ColumnDef, type ColumnInfo, type ColumnType, type ConflictResolver, type ConflictRow, type CountArgs, type CreateArgs, type CreateManyArgs, Monlite as Db, type DeleteArgs, type Doc, type Driver, type DriverName, type ExplainResult, type FieldFilter, type FieldSelection, type FilterInput, type FindFirstArgs, type FindManyArgs, type GroupByArgs, type GroupByResult, type HavingComparison, type HavingInput, type LiveEvent, type LocalChange, Monlite, MonliteConstraintError, MonliteError, MonliteForeignKeyError, MonliteNotNullError, type MonliteOptions, type MonlitePlugin, MonliteQueryError, MonliteUniqueConstraintError, type OrderBy, type PluginChange, type PreparedStatement, type RemoteChange, type Select, type SortOrder, type SyncOp, type SyncStateRow, SyncStore, type SystemFields, type UpdateArgs, type UpdateData, type UpdateOperators, type UpsertArgs, type Version, type WatchHandle, type WhereInput as WhereClause, type WhereInput, type WithId, compareVersions, createDb, isObjectId, makeVersion, normalizeDriverError, objectId, versionTs };
750
+ export { type AggregateArgs, type AggregateResult, type ApplyResult, Collection, type CollectionMode, type CollectionOptions, type CollectionSchema, type ColumnDef, type ColumnInfo, type ColumnType, type ConflictResolver, type ConflictRow, type CountArgs, type CreateArgs, type CreateManyArgs, Monlite as Db, type DeleteArgs, type Doc, type Driver, type DriverName, type EncryptionOptions, type ExplainResult, type FieldFilter, type FieldSelection, type FilterInput, type FindFirstArgs, type FindManyArgs, type GroupByArgs, type GroupByResult, type HavingComparison, type HavingInput, type LiveEvent, type LocalChange, Monlite, MonliteConstraintError, MonliteEncryptionError, MonliteError, MonliteForeignKeyError, MonliteNotNullError, type MonliteOptions, type MonlitePlugin, MonliteQueryError, MonliteUniqueConstraintError, type OrderBy, type PluginChange, type PreparedStatement, type RemoteChange, type Select, type SortOrder, type SyncOp, type SyncStateRow, SyncStore, type SystemFields, type UpdateArgs, type UpdateData, type UpdateOperators, type UpsertArgs, type Version, type WatchHandle, type WhereInput as WhereClause, type WhereInput, type WithId, compareVersions, createDb, isObjectId, makeVersion, normalizeDriverError, objectId, versionTs };
package/dist/index.d.ts CHANGED
@@ -357,12 +357,28 @@ interface GroupByArgs<T = Doc> {
357
357
  }
358
358
  type GroupByResult = Record<string, any>;
359
359
  type DriverName = "auto" | "better-sqlite3" | "node:sqlite";
360
+ /** Encryption-at-rest configuration (requires `better-sqlite3-multiple-ciphers`). */
361
+ interface EncryptionOptions {
362
+ /** The passphrase used to encrypt/decrypt the database file. */
363
+ key: string;
364
+ /**
365
+ * Cipher scheme to use (e.g. `"sqlcipher"`, `"chacha20"`, `"aes256cbc"`).
366
+ * Defaults to the library default (ChaCha20-Poly1305).
367
+ */
368
+ cipher?: string;
369
+ }
360
370
  interface MonliteOptions {
361
371
  /**
362
372
  * Which SQLite backend to use. `"auto"` (default) prefers `better-sqlite3`
363
373
  * when installed, otherwise the built-in `node:sqlite` (Node >= 22.5).
364
374
  */
365
375
  driver?: DriverName;
376
+ /**
377
+ * Encrypt the database at rest. Requires the `better-sqlite3-multiple-ciphers`
378
+ * package (a drop-in for `better-sqlite3`); not supported on `node:sqlite`.
379
+ * Use `db.rekey(newKey)` to rotate the key.
380
+ */
381
+ encryption?: EncryptionOptions;
366
382
  /** Opt-in plugins (e.g. `@monlite/fts`). */
367
383
  plugins?: MonlitePlugin[];
368
384
  /**
@@ -414,6 +430,8 @@ interface Driver {
414
430
  /** Run `fn` inside a transaction; rolls back and rethrows if it throws. */
415
431
  transaction<T>(fn: () => T): T;
416
432
  close(): void;
433
+ /** Rotate the encryption key (encrypted backends only). */
434
+ rekey?(key: string, cipher?: string): void;
417
435
  /** The underlying native handle (better-sqlite3 Database / node:sqlite DatabaseSync). */
418
436
  readonly raw: any;
419
437
  }
@@ -613,6 +631,7 @@ declare class Monlite {
613
631
  readonly $sync?: SyncStore;
614
632
  private readonly collections;
615
633
  private readonly plugins;
634
+ private readonly encrypted;
616
635
  private closed;
617
636
  constructor(filename: string, options?: MonliteOptions);
618
637
  /** @internal Notify plugins that documents changed (post-commit). */
@@ -655,6 +674,11 @@ declare class Monlite {
655
674
  * `VACUUM INTO`). The destination file must not already exist.
656
675
  */
657
676
  backup(path: string): Promise<void>;
677
+ /**
678
+ * Rotate the encryption key. Only valid for a database opened with the
679
+ * `encryption` option; throws otherwise. Pass `cipher` to also change scheme.
680
+ */
681
+ rekey(key: string, cipher?: string): void;
658
682
  /** Close the underlying SQLite connection. */
659
683
  $disconnect(): Promise<void>;
660
684
  private assertOpen;
@@ -677,6 +701,12 @@ declare class MonliteQueryError extends MonliteError {
677
701
  cause?: unknown;
678
702
  });
679
703
  }
704
+ /** Thrown when an encrypted database can't be opened (wrong key, not encrypted). */
705
+ declare class MonliteEncryptionError extends MonliteError {
706
+ constructor(message: string, options?: {
707
+ cause?: unknown;
708
+ });
709
+ }
680
710
  /** A database constraint was violated (base class for the specific kinds). */
681
711
  declare class MonliteConstraintError extends MonliteError {
682
712
  readonly collection?: string;
@@ -717,4 +747,4 @@ declare function objectId(): string;
717
747
  /** True when a value looks like a monlite/ObjectId id (24 hex chars). */
718
748
  declare function isObjectId(value: unknown): value is string;
719
749
 
720
- export { type AggregateArgs, type AggregateResult, type ApplyResult, Collection, type CollectionMode, type CollectionOptions, type CollectionSchema, type ColumnDef, type ColumnInfo, type ColumnType, type ConflictResolver, type ConflictRow, type CountArgs, type CreateArgs, type CreateManyArgs, Monlite as Db, type DeleteArgs, type Doc, type Driver, type DriverName, type ExplainResult, type FieldFilter, type FieldSelection, type FilterInput, type FindFirstArgs, type FindManyArgs, type GroupByArgs, type GroupByResult, type HavingComparison, type HavingInput, type LiveEvent, type LocalChange, Monlite, MonliteConstraintError, MonliteError, MonliteForeignKeyError, MonliteNotNullError, type MonliteOptions, type MonlitePlugin, MonliteQueryError, MonliteUniqueConstraintError, type OrderBy, type PluginChange, type PreparedStatement, type RemoteChange, type Select, type SortOrder, type SyncOp, type SyncStateRow, SyncStore, type SystemFields, type UpdateArgs, type UpdateData, type UpdateOperators, type UpsertArgs, type Version, type WatchHandle, type WhereInput as WhereClause, type WhereInput, type WithId, compareVersions, createDb, isObjectId, makeVersion, normalizeDriverError, objectId, versionTs };
750
+ export { type AggregateArgs, type AggregateResult, type ApplyResult, Collection, type CollectionMode, type CollectionOptions, type CollectionSchema, type ColumnDef, type ColumnInfo, type ColumnType, type ConflictResolver, type ConflictRow, type CountArgs, type CreateArgs, type CreateManyArgs, Monlite as Db, type DeleteArgs, type Doc, type Driver, type DriverName, type EncryptionOptions, type ExplainResult, type FieldFilter, type FieldSelection, type FilterInput, type FindFirstArgs, type FindManyArgs, type GroupByArgs, type GroupByResult, type HavingComparison, type HavingInput, type LiveEvent, type LocalChange, Monlite, MonliteConstraintError, MonliteEncryptionError, MonliteError, MonliteForeignKeyError, MonliteNotNullError, type MonliteOptions, type MonlitePlugin, MonliteQueryError, MonliteUniqueConstraintError, type OrderBy, type PluginChange, type PreparedStatement, type RemoteChange, type Select, type SortOrder, type SyncOp, type SyncStateRow, SyncStore, type SystemFields, type UpdateArgs, type UpdateData, type UpdateOperators, type UpsertArgs, type Version, type WatchHandle, type WhereInput as WhereClause, type WhereInput, type WithId, compareVersions, createDb, isObjectId, makeVersion, normalizeDriverError, objectId, versionTs };
package/dist/index.js CHANGED
@@ -116,6 +116,12 @@ var MonliteQueryError = class extends MonliteError {
116
116
  this.name = "MonliteQueryError";
117
117
  }
118
118
  };
119
+ var MonliteEncryptionError = class extends MonliteError {
120
+ constructor(message, options) {
121
+ super(message, options);
122
+ this.name = "MonliteEncryptionError";
123
+ }
124
+ };
119
125
  var MonliteConstraintError = class extends MonliteError {
120
126
  collection;
121
127
  constructor(message, options) {
@@ -1314,6 +1320,7 @@ var AutoIndexer = class {
1314
1320
 
1315
1321
  // src/driver/better-sqlite3.ts
1316
1322
  var STMT_CACHE_MAX = 256;
1323
+ var quote = (s) => s.replace(/'/g, "''");
1317
1324
  var BetterSqlite3Driver = class {
1318
1325
  name = "better-sqlite3";
1319
1326
  raw;
@@ -1324,6 +1331,9 @@ var BetterSqlite3Driver = class {
1324
1331
  this.raw = new BetterSqlite3(filename, {
1325
1332
  readonly: options.readonly ?? false
1326
1333
  });
1334
+ if (options.encryption) {
1335
+ this.applyKey(options.encryption.key, options.encryption.cipher);
1336
+ }
1327
1337
  this.raw.pragma("foreign_keys = ON");
1328
1338
  this.raw.pragma(`busy_timeout = ${options.busyTimeout ?? 5e3}`);
1329
1339
  if (!options.readonly && (options.wal ?? true)) {
@@ -1352,6 +1362,28 @@ var BetterSqlite3Driver = class {
1352
1362
  transaction(fn) {
1353
1363
  return this.raw.transaction(fn)();
1354
1364
  }
1365
+ /** Apply the encryption key and verify it by reading the schema. */
1366
+ applyKey(key, cipher) {
1367
+ if (cipher) this.raw.pragma(`cipher='${quote(cipher)}'`);
1368
+ this.raw.pragma(`key='${quote(key)}'`);
1369
+ try {
1370
+ this.raw.exec("SELECT count(*) FROM sqlite_master");
1371
+ } catch (err) {
1372
+ this.raw.close();
1373
+ throw new MonliteEncryptionError(
1374
+ "Failed to open the encrypted database: the key is incorrect, or the file is not encrypted.",
1375
+ { cause: err }
1376
+ );
1377
+ }
1378
+ }
1379
+ rekey(key, cipher) {
1380
+ if (cipher) this.raw.pragma(`cipher='${quote(cipher)}'`);
1381
+ const mode = String(this.raw.pragma("journal_mode", { simple: true }));
1382
+ const wasWal = mode.toLowerCase() === "wal";
1383
+ if (wasWal) this.raw.pragma("journal_mode = DELETE");
1384
+ this.raw.pragma(`rekey='${quote(key)}'`);
1385
+ if (wasWal) this.raw.pragma("journal_mode = WAL");
1386
+ }
1355
1387
  close() {
1356
1388
  this.cache.clear();
1357
1389
  this.raw.close();
@@ -1367,6 +1399,11 @@ var NodeSqliteDriver = class {
1367
1399
  cache = /* @__PURE__ */ new Map();
1368
1400
  depth = 0;
1369
1401
  constructor(nodeSqlite, filename, options) {
1402
+ if (options.encryption) {
1403
+ throw new MonliteError(
1404
+ "Encryption is not supported on the node:sqlite backend. Use better-sqlite3 with the better-sqlite3-multiple-ciphers package."
1405
+ );
1406
+ }
1370
1407
  this.verbose = options.verbose;
1371
1408
  const { DatabaseSync } = nodeSqlite;
1372
1409
  this.raw = new DatabaseSync(filename, {
@@ -1442,6 +1479,14 @@ function loadBetterSqlite3() {
1442
1479
  return null;
1443
1480
  }
1444
1481
  }
1482
+ function loadCipherSqlite3() {
1483
+ try {
1484
+ const mod = req("better-sqlite3-multiple-ciphers");
1485
+ return mod?.default ?? mod;
1486
+ } catch {
1487
+ return null;
1488
+ }
1489
+ }
1445
1490
  function loadNodeSqlite() {
1446
1491
  try {
1447
1492
  return req("node:sqlite");
@@ -1451,6 +1496,20 @@ function loadNodeSqlite() {
1451
1496
  }
1452
1497
  function createDriver(filename, options = {}) {
1453
1498
  const choice = options.driver ?? "auto";
1499
+ if (options.encryption) {
1500
+ if (choice === "node:sqlite") {
1501
+ throw new MonliteError(
1502
+ `Encryption is not supported on the node:sqlite backend. Use better-sqlite3 with the better-sqlite3-multiple-ciphers package.`
1503
+ );
1504
+ }
1505
+ const cipher = loadCipherSqlite3();
1506
+ if (!cipher) {
1507
+ throw new MonliteError(
1508
+ `Encryption requires the "better-sqlite3-multiple-ciphers" package (a drop-in for better-sqlite3). Run \`npm install better-sqlite3-multiple-ciphers\`.`
1509
+ );
1510
+ }
1511
+ return new BetterSqlite3Driver(cipher, filename, options);
1512
+ }
1454
1513
  if (choice === "better-sqlite3") {
1455
1514
  const mod = loadBetterSqlite3();
1456
1515
  if (!mod) {
@@ -1890,6 +1949,7 @@ var Monlite = class {
1890
1949
  $sync;
1891
1950
  collections = /* @__PURE__ */ new Map();
1892
1951
  plugins;
1952
+ encrypted;
1893
1953
  closed = false;
1894
1954
  constructor(filename, options = {}) {
1895
1955
  this.driver = createDriver(filename, {
@@ -1898,8 +1958,10 @@ var Monlite = class {
1898
1958
  wal: options.wal,
1899
1959
  busyTimeout: options.busyTimeout,
1900
1960
  allowExtensions: options.allowExtensions,
1961
+ encryption: options.encryption,
1901
1962
  verbose: options.verbose
1902
1963
  });
1964
+ this.encrypted = options.encryption !== void 0;
1903
1965
  this.autoIndexer = new AutoIndexer(
1904
1966
  this.driver,
1905
1967
  options.autoIndex ?? true,
@@ -2047,6 +2109,19 @@ var Monlite = class {
2047
2109
  this.driver.exec(`VACUUM INTO '${path.replace(/'/g, "''")}'`);
2048
2110
  return Promise.resolve();
2049
2111
  }
2112
+ /**
2113
+ * Rotate the encryption key. Only valid for a database opened with the
2114
+ * `encryption` option; throws otherwise. Pass `cipher` to also change scheme.
2115
+ */
2116
+ rekey(key, cipher) {
2117
+ this.assertOpen();
2118
+ if (!this.encrypted || !this.driver.rekey) {
2119
+ throw new MonliteError(
2120
+ "rekey() requires a database opened with the `encryption` option."
2121
+ );
2122
+ }
2123
+ this.driver.rekey(key, cipher);
2124
+ }
2050
2125
  /** Close the underlying SQLite connection. */
2051
2126
  $disconnect() {
2052
2127
  if (!this.closed) {
@@ -2063,6 +2138,6 @@ function createDb(filename, options) {
2063
2138
  return new Monlite(filename, options);
2064
2139
  }
2065
2140
 
2066
- export { Collection, Monlite, MonliteConstraintError, MonliteError, MonliteForeignKeyError, MonliteNotNullError, MonliteQueryError, MonliteUniqueConstraintError, SyncStore, compareVersions, createDb, isObjectId, makeVersion, normalizeDriverError, objectId, versionTs };
2141
+ export { Collection, Monlite, MonliteConstraintError, MonliteEncryptionError, MonliteError, MonliteForeignKeyError, MonliteNotNullError, MonliteQueryError, MonliteUniqueConstraintError, SyncStore, compareVersions, createDb, isObjectId, makeVersion, normalizeDriverError, objectId, versionTs };
2067
2142
  //# sourceMappingURL=index.js.map
2068
2143
  //# sourceMappingURL=index.js.map