latticesql 0.18.3 → 0.18.4

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/README.md CHANGED
@@ -2012,6 +2012,14 @@ Three complete, commented examples are in [docs/examples/](./docs/examples/):
2012
2012
 
2013
2013
  ---
2014
2014
 
2015
+ ## Staying up to date
2016
+
2017
+ **CLI users:** The `lattice` CLI checks for new versions automatically and prints a notice when an update is available. Run `lattice update` to upgrade in place. Alternatively, use `npx lattice` to always run the latest version without a global install.
2018
+
2019
+ **Library consumers:** By default, `npm install latticesql` adds a `^` semver range to your `package.json`, so patch and minor updates are picked up on your next `npm install`. For fully automated dependency updates, set up [Dependabot](https://docs.github.com/en/code-security/dependabot) or [Renovate](https://github.com/renovatebot/renovate) — they'll create PRs in your repo whenever a new version is published.
2020
+
2021
+ ---
2022
+
2015
2023
  ## Contributing
2016
2024
 
2017
2025
  See [CONTRIBUTING.md](./CONTRIBUTING.md) for dev setup, test commands, and contribution guidelines.
package/dist/cli.js CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { resolve as resolve4, dirname as dirname5 } from "path";
5
- import { readFileSync as readFileSync6 } from "fs";
5
+ import { readFileSync as readFileSync7 } from "fs";
6
+ import { execSync } from "child_process";
6
7
  import { parse as parse2 } from "yaml";
7
8
 
8
9
  // src/codegen/generate.ts
@@ -493,7 +494,7 @@ var SchemaManager = class {
493
494
  applySchema(adapter) {
494
495
  for (const [name, def] of this._tables) {
495
496
  const pkCols = this._tablePK.get(name) ?? ["id"];
496
- let constraints = def.tableConstraints ? [...def.tableConstraints] : [];
497
+ const constraints = def.tableConstraints ? [...def.tableConstraints] : [];
497
498
  if (pkCols.length > 1) {
498
499
  const alreadyHasPK = constraints.some((c) => c.toUpperCase().startsWith("PRIMARY KEY"));
499
500
  if (!alreadyHasPK) {
@@ -537,7 +538,7 @@ var SchemaManager = class {
537
538
  queryTable(adapter, name) {
538
539
  if (this._tables.has(name)) {
539
540
  const def = this._tables.get(name);
540
- if (def.columns && "deleted_at" in def.columns) {
541
+ if (def?.columns && "deleted_at" in def.columns) {
541
542
  return adapter.all(`SELECT * FROM "${name}" WHERE deleted_at IS NULL`);
542
543
  }
543
544
  return adapter.all(`SELECT * FROM "${name}"`);
@@ -721,7 +722,7 @@ function resolveEntitySource(source, entityRow, entityPk, adapter, protection) {
721
722
  case "self":
722
723
  return [entityRow];
723
724
  case "hasMany": {
724
- if (protection && protection.protectedTables.has(source.table)) {
725
+ if (protection?.protectedTables.has(source.table)) {
725
726
  if (source.table === protection.currentTable) return [entityRow];
726
727
  return [];
727
728
  }
@@ -733,7 +734,7 @@ function resolveEntitySource(source, entityRow, entityPk, adapter, protection) {
733
734
  return adapter.all(sql, params);
734
735
  }
735
736
  case "manyToMany": {
736
- if (protection && protection.protectedTables.has(source.remoteTable)) {
737
+ if (protection?.protectedTables.has(source.remoteTable)) {
737
738
  if (source.remoteTable === protection.currentTable) return [entityRow];
738
739
  return [];
739
740
  }
@@ -759,7 +760,7 @@ function resolveEntitySource(source, entityRow, entityPk, adapter, protection) {
759
760
  return adapter.all(sql, params);
760
761
  }
761
762
  case "belongsTo": {
762
- if (protection && protection.protectedTables.has(source.table)) {
763
+ if (protection?.protectedTables.has(source.table)) {
763
764
  if (source.table === protection.currentTable) return [entityRow];
764
765
  return [];
765
766
  }
@@ -1790,9 +1791,7 @@ var Lattice = class {
1790
1791
  `Entity context "${table}" has encrypted: true but no encryptionKey was provided in Lattice options`
1791
1792
  );
1792
1793
  }
1793
- if (!this._encryptionKey) {
1794
- this._encryptionKey = deriveKey(this._encryptionKeyRaw);
1795
- }
1794
+ this._encryptionKey ??= deriveKey(this._encryptionKeyRaw);
1796
1795
  const pragmaRows = this._adapter.all(`PRAGMA table_info("${table}")`);
1797
1796
  const allCols = pragmaRows.map((r) => r.name);
1798
1797
  const encCols = resolveEncryptedColumns(def.encrypted, allCols);
@@ -2621,6 +2620,49 @@ var Lattice = class {
2621
2620
  }
2622
2621
  };
2623
2622
 
2623
+ // src/update-check.ts
2624
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, mkdirSync as mkdirSync4, existsSync as existsSync8 } from "fs";
2625
+ import { join as join8 } from "path";
2626
+ import { homedir } from "os";
2627
+ var ONE_DAY_MS = 864e5;
2628
+ function isNewer(latest, current) {
2629
+ const a = latest.split(".").map(Number);
2630
+ const b = current.split(".").map(Number);
2631
+ for (let i = 0; i < Math.max(a.length, b.length); i++) {
2632
+ const av = a[i] ?? 0;
2633
+ const bv = b[i] ?? 0;
2634
+ if (av > bv) return true;
2635
+ if (av < bv) return false;
2636
+ }
2637
+ return false;
2638
+ }
2639
+ async function checkForUpdate(pkgName, currentVersion) {
2640
+ const cacheDir = join8(homedir(), `.${pkgName}`);
2641
+ const cachePath = join8(cacheDir, "update-check.json");
2642
+ try {
2643
+ if (existsSync8(cachePath)) {
2644
+ const cached = JSON.parse(readFileSync6(cachePath, "utf-8"));
2645
+ if (Date.now() - cached.checked < ONE_DAY_MS) {
2646
+ return isNewer(cached.latest, currentVersion) ? cached.latest : null;
2647
+ }
2648
+ }
2649
+ } catch {
2650
+ }
2651
+ const res = await fetch(`https://registry.npmjs.org/${pkgName}/latest`, {
2652
+ headers: { accept: "application/json" },
2653
+ signal: AbortSignal.timeout(5e3)
2654
+ });
2655
+ if (!res.ok) return null;
2656
+ const data = await res.json();
2657
+ const latest = data.version;
2658
+ try {
2659
+ if (!existsSync8(cacheDir)) mkdirSync4(cacheDir, { recursive: true });
2660
+ writeFileSync3(cachePath, JSON.stringify({ latest, checked: Date.now() }));
2661
+ } catch {
2662
+ }
2663
+ return isNewer(latest, currentVersion) ? latest : null;
2664
+ }
2665
+
2624
2666
  // src/cli.ts
2625
2667
  function parseArgs(argv) {
2626
2668
  let command;
@@ -2707,6 +2749,7 @@ function printHelp() {
2707
2749
  " reconcile Render + cleanup orphaned entity directories and files",
2708
2750
  " status Dry-run reconcile \u2014 show what would change without writing",
2709
2751
  " watch Poll for changes and re-render on each cycle",
2752
+ " update Upgrade latticesql to the latest version",
2710
2753
  "",
2711
2754
  "Options (generate):",
2712
2755
  " --config, -c <path> Path to config file (default: ./lattice.config.yml)",
@@ -2744,20 +2787,40 @@ function printHelp() {
2744
2787
  ].join("\n")
2745
2788
  );
2746
2789
  }
2747
- function printVersion() {
2790
+ function getVersion() {
2748
2791
  try {
2749
2792
  const pkgPath = new URL("../package.json", import.meta.url).pathname;
2750
- const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
2751
- console.log(pkg.version);
2793
+ const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
2794
+ return pkg.version;
2752
2795
  } catch {
2753
- console.log("unknown");
2796
+ return "unknown";
2797
+ }
2798
+ }
2799
+ function printVersion() {
2800
+ console.log(getVersion());
2801
+ }
2802
+ async function runUpdate() {
2803
+ const currentVersion = getVersion();
2804
+ console.log(`Current version: ${currentVersion}`);
2805
+ const latest = await checkForUpdate("latticesql", currentVersion);
2806
+ if (!latest) {
2807
+ console.log("Already up to date.");
2808
+ return;
2809
+ }
2810
+ console.log(`Updating to ${latest}...`);
2811
+ try {
2812
+ execSync("npm install -g latticesql@latest", { stdio: "inherit" });
2813
+ console.log(`Updated latticesql ${currentVersion} \u2192 ${latest}`);
2814
+ } catch {
2815
+ console.error("Update failed. Try running manually: npm install -g latticesql@latest");
2816
+ process.exit(1);
2754
2817
  }
2755
2818
  }
2756
2819
  function runGenerate(args) {
2757
2820
  const configPath = resolve4(args.config);
2758
2821
  let raw;
2759
2822
  try {
2760
- raw = readFileSync6(configPath, "utf-8");
2823
+ raw = readFileSync7(configPath, "utf-8");
2761
2824
  } catch {
2762
2825
  console.error(`Error: cannot read config file at "${configPath}"`);
2763
2826
  process.exit(1);
@@ -2924,6 +2987,19 @@ function main() {
2924
2987
  printHelp();
2925
2988
  process.exit(args.command === void 0 && !args.help ? 1 : 0);
2926
2989
  }
2990
+ const version = getVersion();
2991
+ if (version !== "unknown") {
2992
+ checkForUpdate("latticesql", version).then((latest) => {
2993
+ if (latest) {
2994
+ process.on("exit", () => {
2995
+ console.log(
2996
+ `
2997
+ Update available: ${version} \u2192 ${latest} \u2014 run "lattice update" to upgrade`
2998
+ );
2999
+ });
3000
+ }
3001
+ }).catch(() => void 0);
3002
+ }
2927
3003
  switch (args.command) {
2928
3004
  case "generate":
2929
3005
  runGenerate(args);
@@ -2940,6 +3016,9 @@ function main() {
2940
3016
  case "watch":
2941
3017
  void runWatch(args);
2942
3018
  break;
3019
+ case "update":
3020
+ void runUpdate();
3021
+ break;
2943
3022
  default:
2944
3023
  console.error(`Unknown command: ${args.command}`);
2945
3024
  printHelp();
package/dist/index.cjs CHANGED
@@ -246,7 +246,7 @@ var SchemaManager = class {
246
246
  applySchema(adapter) {
247
247
  for (const [name, def] of this._tables) {
248
248
  const pkCols = this._tablePK.get(name) ?? ["id"];
249
- let constraints = def.tableConstraints ? [...def.tableConstraints] : [];
249
+ const constraints = def.tableConstraints ? [...def.tableConstraints] : [];
250
250
  if (pkCols.length > 1) {
251
251
  const alreadyHasPK = constraints.some((c) => c.toUpperCase().startsWith("PRIMARY KEY"));
252
252
  if (!alreadyHasPK) {
@@ -290,7 +290,7 @@ var SchemaManager = class {
290
290
  queryTable(adapter, name) {
291
291
  if (this._tables.has(name)) {
292
292
  const def = this._tables.get(name);
293
- if (def.columns && "deleted_at" in def.columns) {
293
+ if (def?.columns && "deleted_at" in def.columns) {
294
294
  return adapter.all(`SELECT * FROM "${name}" WHERE deleted_at IS NULL`);
295
295
  }
296
296
  return adapter.all(`SELECT * FROM "${name}"`);
@@ -474,7 +474,7 @@ function resolveEntitySource(source, entityRow, entityPk, adapter, protection) {
474
474
  case "self":
475
475
  return [entityRow];
476
476
  case "hasMany": {
477
- if (protection && protection.protectedTables.has(source.table)) {
477
+ if (protection?.protectedTables.has(source.table)) {
478
478
  if (source.table === protection.currentTable) return [entityRow];
479
479
  return [];
480
480
  }
@@ -486,7 +486,7 @@ function resolveEntitySource(source, entityRow, entityPk, adapter, protection) {
486
486
  return adapter.all(sql, params);
487
487
  }
488
488
  case "manyToMany": {
489
- if (protection && protection.protectedTables.has(source.remoteTable)) {
489
+ if (protection?.protectedTables.has(source.remoteTable)) {
490
490
  if (source.remoteTable === protection.currentTable) return [entityRow];
491
491
  return [];
492
492
  }
@@ -512,7 +512,7 @@ function resolveEntitySource(source, entityRow, entityPk, adapter, protection) {
512
512
  return adapter.all(sql, params);
513
513
  }
514
514
  case "belongsTo": {
515
- if (protection && protection.protectedTables.has(source.table)) {
515
+ if (protection?.protectedTables.has(source.table)) {
516
516
  if (source.table === protection.currentTable) return [entityRow];
517
517
  return [];
518
518
  }
@@ -1834,9 +1834,7 @@ var Lattice = class {
1834
1834
  `Entity context "${table}" has encrypted: true but no encryptionKey was provided in Lattice options`
1835
1835
  );
1836
1836
  }
1837
- if (!this._encryptionKey) {
1838
- this._encryptionKey = deriveKey(this._encryptionKeyRaw);
1839
- }
1837
+ this._encryptionKey ??= deriveKey(this._encryptionKeyRaw);
1840
1838
  const pragmaRows = this._adapter.all(`PRAGMA table_info("${table}")`);
1841
1839
  const allCols = pragmaRows.map((r) => r.name);
1842
1840
  const encCols = resolveEncryptedColumns(def.encrypted, allCols);
@@ -2677,7 +2675,7 @@ function fixSchemaConflicts(db, checks) {
2677
2675
  }
2678
2676
  if (tableExists(db, "__lattice_migrations")) {
2679
2677
  const versionCol = db.prepare('PRAGMA table_info("__lattice_migrations")').all().find((c) => c.name === "version");
2680
- if (versionCol && versionCol.type.toUpperCase().includes("INTEGER")) {
2678
+ if (versionCol?.type.toUpperCase().includes("INTEGER")) {
2681
2679
  db.exec(
2682
2680
  'ALTER TABLE "__lattice_migrations" RENAME TO "__lattice_migrations_v1"'
2683
2681
  );
package/dist/index.d.cts CHANGED
@@ -1539,10 +1539,10 @@ declare function contentHash(content: string): string;
1539
1539
  * // Now safe to call lattice.init()
1540
1540
  * ```
1541
1541
  */
1542
- declare function fixSchemaConflicts(db: Database.Database, checks: Array<{
1542
+ declare function fixSchemaConflicts(db: Database.Database, checks: {
1543
1543
  table: string;
1544
1544
  requiredColumns: string[];
1545
- }>): void;
1545
+ }[]): void;
1546
1546
 
1547
1547
  /**
1548
1548
  * Derive a 256-bit AES key from a master password using scrypt.
package/dist/index.d.ts CHANGED
@@ -1539,10 +1539,10 @@ declare function contentHash(content: string): string;
1539
1539
  * // Now safe to call lattice.init()
1540
1540
  * ```
1541
1541
  */
1542
- declare function fixSchemaConflicts(db: Database.Database, checks: Array<{
1542
+ declare function fixSchemaConflicts(db: Database.Database, checks: {
1543
1543
  table: string;
1544
1544
  requiredColumns: string[];
1545
- }>): void;
1545
+ }[]): void;
1546
1546
 
1547
1547
  /**
1548
1548
  * Derive a 256-bit AES key from a master password using scrypt.
package/dist/index.js CHANGED
@@ -179,7 +179,7 @@ var SchemaManager = class {
179
179
  applySchema(adapter) {
180
180
  for (const [name, def] of this._tables) {
181
181
  const pkCols = this._tablePK.get(name) ?? ["id"];
182
- let constraints = def.tableConstraints ? [...def.tableConstraints] : [];
182
+ const constraints = def.tableConstraints ? [...def.tableConstraints] : [];
183
183
  if (pkCols.length > 1) {
184
184
  const alreadyHasPK = constraints.some((c) => c.toUpperCase().startsWith("PRIMARY KEY"));
185
185
  if (!alreadyHasPK) {
@@ -223,7 +223,7 @@ var SchemaManager = class {
223
223
  queryTable(adapter, name) {
224
224
  if (this._tables.has(name)) {
225
225
  const def = this._tables.get(name);
226
- if (def.columns && "deleted_at" in def.columns) {
226
+ if (def?.columns && "deleted_at" in def.columns) {
227
227
  return adapter.all(`SELECT * FROM "${name}" WHERE deleted_at IS NULL`);
228
228
  }
229
229
  return adapter.all(`SELECT * FROM "${name}"`);
@@ -407,7 +407,7 @@ function resolveEntitySource(source, entityRow, entityPk, adapter, protection) {
407
407
  case "self":
408
408
  return [entityRow];
409
409
  case "hasMany": {
410
- if (protection && protection.protectedTables.has(source.table)) {
410
+ if (protection?.protectedTables.has(source.table)) {
411
411
  if (source.table === protection.currentTable) return [entityRow];
412
412
  return [];
413
413
  }
@@ -419,7 +419,7 @@ function resolveEntitySource(source, entityRow, entityPk, adapter, protection) {
419
419
  return adapter.all(sql, params);
420
420
  }
421
421
  case "manyToMany": {
422
- if (protection && protection.protectedTables.has(source.remoteTable)) {
422
+ if (protection?.protectedTables.has(source.remoteTable)) {
423
423
  if (source.remoteTable === protection.currentTable) return [entityRow];
424
424
  return [];
425
425
  }
@@ -445,7 +445,7 @@ function resolveEntitySource(source, entityRow, entityPk, adapter, protection) {
445
445
  return adapter.all(sql, params);
446
446
  }
447
447
  case "belongsTo": {
448
- if (protection && protection.protectedTables.has(source.table)) {
448
+ if (protection?.protectedTables.has(source.table)) {
449
449
  if (source.table === protection.currentTable) return [entityRow];
450
450
  return [];
451
451
  }
@@ -1767,9 +1767,7 @@ var Lattice = class {
1767
1767
  `Entity context "${table}" has encrypted: true but no encryptionKey was provided in Lattice options`
1768
1768
  );
1769
1769
  }
1770
- if (!this._encryptionKey) {
1771
- this._encryptionKey = deriveKey(this._encryptionKeyRaw);
1772
- }
1770
+ this._encryptionKey ??= deriveKey(this._encryptionKeyRaw);
1773
1771
  const pragmaRows = this._adapter.all(`PRAGMA table_info("${table}")`);
1774
1772
  const allCols = pragmaRows.map((r) => r.name);
1775
1773
  const encCols = resolveEncryptedColumns(def.encrypted, allCols);
@@ -2610,7 +2608,7 @@ function fixSchemaConflicts(db, checks) {
2610
2608
  }
2611
2609
  if (tableExists(db, "__lattice_migrations")) {
2612
2610
  const versionCol = db.prepare('PRAGMA table_info("__lattice_migrations")').all().find((c) => c.name === "version");
2613
- if (versionCol && versionCol.type.toUpperCase().includes("INTEGER")) {
2611
+ if (versionCol?.type.toUpperCase().includes("INTEGER")) {
2614
2612
  db.exec(
2615
2613
  'ALTER TABLE "__lattice_migrations" RENAME TO "__lattice_migrations_v1"'
2616
2614
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "latticesql",
3
- "version": "0.18.3",
3
+ "version": "0.18.4",
4
4
  "description": "Persistent structured memory for AI agent systems — SQLite ↔ LLM context bridge",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",