latticesql 4.3.1 → 4.3.3

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.
@@ -835,7 +835,12 @@ CREATE TRIGGER "${trg}" AFTER INSERT OR UPDATE OR DELETE ON ${q3}
835
835
  async function ownPolyfillsByGroup(db) {
836
836
  if (!isPg(db)) return;
837
837
  const group3 = await memberGroupFor(db);
838
- for (const sig of ["json_extract(text, text)", "strftime(text, text)"]) {
838
+ for (const sig of [
839
+ "json_extract(text, text)",
840
+ "strftime(text, text)",
841
+ "strftime(text, text, text)",
842
+ "__lattice_strftime_fmt(timestamptz, text)"
843
+ ]) {
839
844
  try {
840
845
  const reg = await getAsyncOrSync(db.adapter, `SELECT to_regprocedure($1) AS reg`, [sig]);
841
846
  if (reg?.reg == null) continue;
@@ -1781,7 +1786,7 @@ async function registerPostgresPolyfills(run) {
1781
1786
  await run(sql);
1782
1787
  } catch (err) {
1783
1788
  const msg = err instanceof Error ? err.message : String(err);
1784
- if (/permission denied/i.test(msg)) {
1789
+ if (/permission denied/i.test(msg) || /must be owner of/i.test(msg)) {
1785
1790
  permissionDenied = true;
1786
1791
  } else {
1787
1792
  console.warn(`[PostgresAdapter] ${warn}`, msg);
@@ -2299,40 +2304,111 @@ var init_postgres = __esm({
2299
2304
  SELECT doc::jsonb #>> string_to_array(regexp_replace(path, '^\\$\\.?', ''), '.')
2300
2305
  $fn$;
2301
2306
  END IF;
2307
+ END $do$;`
2308
+ },
2309
+ {
2310
+ warn: "could not register strftime format helper:",
2311
+ // Shared format translator (SQLite strftime tokens → to_char patterns), in UTC.
2312
+ // Factored out so the 2-arg and 3-arg strftime overloads share one definition.
2313
+ // Each polyfill below is CREATE OR REPLACE (so an existing cloud's prior copy is
2314
+ // UPGRADED when the owner opens), wrapped in a DO block whose EXCEPTION swallows a
2315
+ // scoped member's insufficient-privilege failure IN A SUBTRANSACTION — so a member
2316
+ // that runs registration inside a transaction is NOT aborted. ("must be owner of
2317
+ // function", raised when a non-owner CREATE OR REPLACEs another role's function, is
2318
+ // SQLSTATE 42501 / `insufficient_privilege`, same class as "permission denied".) The
2319
+ // owner already owns + replaced the function; the member simply uses the owner's
2320
+ // copy — and `ownPolyfillsByGroup` re-owns the whole set to the member group so any
2321
+ // member can replace them on a later upgrade.
2322
+ sql: `DO $do$ BEGIN
2323
+ CREATE OR REPLACE FUNCTION __lattice_strftime_fmt(ts timestamptz, format text)
2324
+ RETURNS text
2325
+ LANGUAGE sql
2326
+ IMMUTABLE
2327
+ AS $fn$
2328
+ SELECT to_char(
2329
+ ts AT TIME ZONE 'UTC',
2330
+ replace(replace(replace(replace(replace(replace(replace(replace(
2331
+ format,
2332
+ '%Y', 'YYYY'),
2333
+ '%m', 'MM'),
2334
+ '%d', 'DD'),
2335
+ '%H', 'HH24'),
2336
+ '%M', 'MI'),
2337
+ '%S', 'SS'),
2338
+ '%f', 'MS'),
2339
+ 'T', '"T"')
2340
+ );
2341
+ $fn$;
2342
+ EXCEPTION WHEN insufficient_privilege THEN NULL;
2302
2343
  END $do$;`
2303
2344
  },
2304
2345
  {
2305
2346
  warn: "could not register strftime polyfill:",
2347
+ // The pre-4.3.3 strftime cast the modifier straight to timestamptz and threw
2348
+ // `invalid input syntax for type timestamp with time zone: ""` on a legacy ''
2349
+ // value (3.x stored nullable TEXT timestamps as ''), bricking the whole workspace
2350
+ // open. Now an empty, whitespace, or unparseable time string returns NULL (SQLite's
2351
+ // strftime semantics) instead of aborting the query.
2306
2352
  sql: `DO $do$ BEGIN
2307
- IF to_regprocedure('strftime(text, text)') IS NULL THEN
2308
- CREATE FUNCTION strftime(format text, modifier text)
2309
- RETURNS text
2310
- LANGUAGE plpgsql
2311
- IMMUTABLE
2312
- AS $fn$
2313
- DECLARE ts timestamptz;
2314
- BEGIN
2315
- IF modifier = 'now' THEN
2316
- ts := now();
2317
- ELSE
2353
+ CREATE OR REPLACE FUNCTION strftime(format text, modifier text)
2354
+ RETURNS text
2355
+ LANGUAGE plpgsql
2356
+ IMMUTABLE
2357
+ AS $fn$
2358
+ DECLARE ts timestamptz;
2359
+ BEGIN
2360
+ IF modifier = 'now' THEN
2361
+ ts := now();
2362
+ ELSIF NULLIF(btrim(modifier), '') IS NULL THEN
2363
+ RETURN NULL;
2364
+ ELSE
2365
+ BEGIN
2318
2366
  ts := modifier::timestamptz;
2319
- END IF;
2320
- RETURN to_char(
2321
- ts AT TIME ZONE 'UTC',
2322
- replace(replace(replace(replace(replace(replace(replace(replace(
2323
- format,
2324
- '%Y', 'YYYY'),
2325
- '%m', 'MM'),
2326
- '%d', 'DD'),
2327
- '%H', 'HH24'),
2328
- '%M', 'MI'),
2329
- '%S', 'SS'),
2330
- '%f', 'MS'),
2331
- 'T', '"T"')
2332
- );
2367
+ EXCEPTION WHEN others THEN
2368
+ RETURN NULL;
2369
+ END;
2370
+ END IF;
2371
+ RETURN __lattice_strftime_fmt(ts, format);
2372
+ END;
2373
+ $fn$;
2374
+ EXCEPTION WHEN insufficient_privilege THEN NULL;
2375
+ END $do$;`
2376
+ },
2377
+ {
2378
+ warn: "could not register 3-arg strftime polyfill:",
2379
+ // SQLite's 3-arg strftime(format, timestring, modifier) — used by the changelog
2380
+ // retention prune (e.g. strftime('%Y-...','now','-30 days')). Postgres had no
2381
+ // 3-arg overload, so that prune threw `function strftime(...) does not exist` on
2382
+ // every PG cloud with retention. Resolve the base time, apply the modifier as an
2383
+ // interval, then reuse the shared formatter. Empty/invalid → NULL, never throws.
2384
+ sql: `DO $do$ BEGIN
2385
+ CREATE OR REPLACE FUNCTION strftime(format text, timestring text, modifier text)
2386
+ RETURNS text
2387
+ LANGUAGE plpgsql
2388
+ IMMUTABLE
2389
+ AS $fn$
2390
+ DECLARE ts timestamptz;
2391
+ BEGIN
2392
+ IF timestring = 'now' THEN
2393
+ ts := now();
2394
+ ELSIF NULLIF(btrim(timestring), '') IS NULL THEN
2395
+ RETURN NULL;
2396
+ ELSE
2397
+ BEGIN
2398
+ ts := timestring::timestamptz;
2399
+ EXCEPTION WHEN others THEN
2400
+ RETURN NULL;
2401
+ END;
2402
+ END IF;
2403
+ BEGIN
2404
+ ts := ts + modifier::interval;
2405
+ EXCEPTION WHEN others THEN
2406
+ RETURN NULL;
2333
2407
  END;
2334
- $fn$;
2335
- END IF;
2408
+ RETURN __lattice_strftime_fmt(ts, format);
2409
+ END;
2410
+ $fn$;
2411
+ EXCEPTION WHEN insufficient_privilege THEN NULL;
2336
2412
  END $do$;`
2337
2413
  }
2338
2414
  ];
@@ -29043,12 +29119,12 @@ var getNodeModulesParentDirs;
29043
29119
  var init_getNodeModulesParentDirs = __esm({
29044
29120
  "node_modules/@aws-sdk/core/dist-es/submodules/client/util-user-agent-node/getNodeModulesParentDirs.js"() {
29045
29121
  "use strict";
29046
- getNodeModulesParentDirs = (dirname17) => {
29122
+ getNodeModulesParentDirs = (dirname18) => {
29047
29123
  const cwd = process.cwd();
29048
- if (!dirname17) {
29124
+ if (!dirname18) {
29049
29125
  return [cwd];
29050
29126
  }
29051
- const normalizedPath = normalize(dirname17);
29127
+ const normalizedPath = normalize(dirname18);
29052
29128
  const parts = normalizedPath.split(sep6);
29053
29129
  const nodeModulesIndex = parts.indexOf("node_modules");
29054
29130
  const parentDir = nodeModulesIndex !== -1 ? parts.slice(0, nodeModulesIndex).join(sep6) : normalizedPath;
@@ -29126,8 +29202,8 @@ var init_getTypeScriptUserAgentPair = __esm({
29126
29202
  tscVersion = null;
29127
29203
  return void 0;
29128
29204
  }
29129
- const dirname17 = typeof __dirname !== "undefined" ? __dirname : void 0;
29130
- const nodeModulesParentDirs = getNodeModulesParentDirs(dirname17);
29205
+ const dirname18 = typeof __dirname !== "undefined" ? __dirname : void 0;
29206
+ const nodeModulesParentDirs = getNodeModulesParentDirs(dirname18);
29131
29207
  let versionFromApp;
29132
29208
  for (const nodeModulesParentDir of nodeModulesParentDirs) {
29133
29209
  try {
@@ -57916,9 +57992,9 @@ var init_dist_es22 = __esm({
57916
57992
  init_http();
57917
57993
  import { createServer } from "http";
57918
57994
  import { spawn as spawn2 } from "child_process";
57919
- import { existsSync as existsSync31 } from "fs";
57995
+ import { existsSync as existsSync32 } from "fs";
57920
57996
  import { WebSocketServer, WebSocket } from "ws";
57921
- import { dirname as dirname16, resolve as resolve15 } from "path";
57997
+ import { dirname as dirname17, resolve as resolve15 } from "path";
57922
57998
 
57923
57999
  // src/gui/active-db.ts
57924
58000
  init_adapter();
@@ -76618,8 +76694,16 @@ async function dispatchIngestRoute(req, res, ctx) {
76618
76694
 
76619
76695
  // src/gui/sources-routes.ts
76620
76696
  init_user_config();
76621
- import { statSync as statSync9, readdirSync as readdirSync8, readFileSync as readFileSync23, writeFileSync as writeFileSync9 } from "fs";
76622
- import { resolve as resolve11, join as join28, basename as basename12, sep as sep8 } from "path";
76697
+ import {
76698
+ statSync as statSync9,
76699
+ readdirSync as readdirSync8,
76700
+ readFileSync as readFileSync23,
76701
+ writeFileSync as writeFileSync9,
76702
+ existsSync as existsSync26,
76703
+ mkdirSync as mkdirSync11,
76704
+ rmSync
76705
+ } from "fs";
76706
+ import { resolve as resolve11, join as join28, basename as basename12, sep as sep8, dirname as dirname15 } from "path";
76623
76707
  import { execFile } from "child_process";
76624
76708
  import { randomUUID as randomUUID4 } from "crypto";
76625
76709
  init_http();
@@ -76627,20 +76711,49 @@ var MAX_LIST_ENTRIES = 2e3;
76627
76711
  var MAX_INGEST_FILES = 500;
76628
76712
  var MAX_INGEST_DEPTH = 8;
76629
76713
  var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".DS_Store", "__pycache__", ".venv", "venv"]);
76630
- function rootsFile() {
76631
- return join28(configDir(), "sources.json");
76714
+ function rootsFile(configPath) {
76715
+ return join28(dirname15(configPath), "sources.json");
76716
+ }
76717
+ function migrateGlobalRootsIfNeeded(configPath) {
76718
+ const wsFile = rootsFile(configPath);
76719
+ if (existsSync26(wsFile)) return;
76720
+ const globalFile = join28(configDir(), "sources.json");
76721
+ if (globalFile === wsFile || !existsSync26(globalFile)) return;
76722
+ let roots;
76723
+ try {
76724
+ const parsed = JSON.parse(readFileSync23(globalFile, "utf8"));
76725
+ if (!Array.isArray(parsed.roots) || parsed.roots.length === 0) return;
76726
+ roots = parsed.roots;
76727
+ } catch {
76728
+ return;
76729
+ }
76730
+ try {
76731
+ mkdirSync11(dirname15(wsFile), { recursive: true });
76732
+ writeFileSync9(wsFile, JSON.stringify({ roots }, null, 2), "utf8");
76733
+ } catch {
76734
+ return;
76735
+ }
76736
+ try {
76737
+ rmSync(globalFile, { force: true });
76738
+ } catch {
76739
+ try {
76740
+ writeFileSync9(globalFile, JSON.stringify({ roots: [] }, null, 2), "utf8");
76741
+ } catch {
76742
+ }
76743
+ }
76632
76744
  }
76633
- function readRoots() {
76745
+ function readRoots(configPath) {
76746
+ migrateGlobalRootsIfNeeded(configPath);
76634
76747
  try {
76635
- const raw = readFileSync23(rootsFile(), "utf8");
76748
+ const raw = readFileSync23(rootsFile(configPath), "utf8");
76636
76749
  const parsed = JSON.parse(raw);
76637
76750
  return Array.isArray(parsed.roots) ? parsed.roots : [];
76638
76751
  } catch {
76639
76752
  return [];
76640
76753
  }
76641
76754
  }
76642
- function writeRoots(roots) {
76643
- writeFileSync9(rootsFile(), JSON.stringify({ roots }, null, 2), "utf8");
76755
+ function writeRoots(configPath, roots) {
76756
+ writeFileSync9(rootsFile(configPath), JSON.stringify({ roots }, null, 2), "utf8");
76644
76757
  }
76645
76758
  function safeResolveInside2(target, roots) {
76646
76759
  const abs = resolve11(target);
@@ -76730,14 +76843,14 @@ async function ingestFolder(abs, ingestFile) {
76730
76843
  return { ingested, skipped };
76731
76844
  }
76732
76845
  async function dispatchSourcesRoute(req, res, deps) {
76733
- const { pathname, method, ingestFile } = deps;
76846
+ const { pathname, method, ingestFile, configPath } = deps;
76734
76847
  if (!pathname.startsWith("/api/sources/")) return false;
76735
76848
  if (!localFileOpenEnabled()) {
76736
76849
  sendJson(res, { enabled: false });
76737
76850
  return true;
76738
76851
  }
76739
76852
  if (pathname === "/api/sources/roots" && method === "GET") {
76740
- sendJson(res, { enabled: true, roots: readRoots() });
76853
+ sendJson(res, { enabled: true, roots: readRoots(configPath) });
76741
76854
  return true;
76742
76855
  }
76743
76856
  if (pathname === "/api/sources/roots" && method === "POST") {
@@ -76763,12 +76876,12 @@ async function dispatchSourcesRoute(req, res, deps) {
76763
76876
  sendJson(res, { error: `path not found: ${abs}` }, 400);
76764
76877
  return true;
76765
76878
  }
76766
- const roots = readRoots();
76879
+ const roots = readRoots(configPath);
76767
76880
  let root6 = roots.find((r6) => resolve11(r6.path) === abs);
76768
76881
  if (!root6) {
76769
76882
  root6 = { id: randomUUID4(), path: abs, kind, name: basename12(abs) || abs };
76770
76883
  roots.push(root6);
76771
- writeRoots(roots);
76884
+ writeRoots(configPath, roots);
76772
76885
  }
76773
76886
  let result;
76774
76887
  if (kind === "folder") result = await ingestFolder(abs, ingestFile);
@@ -76779,8 +76892,8 @@ async function dispatchSourcesRoute(req, res, deps) {
76779
76892
  const delMatch = /^\/api\/sources\/roots\/([^/]+)$/.exec(pathname);
76780
76893
  if (delMatch && method === "DELETE") {
76781
76894
  const id = decodeURIComponent(delMatch[1] ?? "");
76782
- const roots = readRoots().filter((r6) => r6.id !== id);
76783
- writeRoots(roots);
76895
+ const roots = readRoots(configPath).filter((r6) => r6.id !== id);
76896
+ writeRoots(configPath, roots);
76784
76897
  sendJson(res, { ok: true });
76785
76898
  return true;
76786
76899
  }
@@ -76798,7 +76911,7 @@ async function dispatchSourcesRoute(req, res, deps) {
76798
76911
  sendJson(res, { error: "path is required" }, 400);
76799
76912
  return true;
76800
76913
  }
76801
- const abs = safeResolveInside2(target, readRoots());
76914
+ const abs = safeResolveInside2(target, readRoots(configPath));
76802
76915
  if (!abs) {
76803
76916
  sendJson(res, { error: "path is outside any registered source root" }, 403);
76804
76917
  return true;
@@ -76813,7 +76926,7 @@ async function dispatchSourcesRoute(req, res, deps) {
76813
76926
  if (pathname === "/api/sources/ingest-folder" && method === "POST") {
76814
76927
  const body = await readJson(req).catch(() => ({}));
76815
76928
  const target = typeof body.path === "string" ? body.path : "";
76816
- const abs = safeResolveInside2(target, readRoots());
76929
+ const abs = safeResolveInside2(target, readRoots(configPath));
76817
76930
  if (!abs) {
76818
76931
  sendJson(res, { error: "path is outside any registered source root" }, 403);
76819
76932
  return true;
@@ -76827,7 +76940,7 @@ async function dispatchSourcesRoute(req, res, deps) {
76827
76940
  // src/gui/import-routes.ts
76828
76941
  init_adapter();
76829
76942
  init_http();
76830
- import { existsSync as existsSync26, readFileSync as readFileSync24, statSync as statSync10 } from "fs";
76943
+ import { existsSync as existsSync27, readFileSync as readFileSync24, statSync as statSync10 } from "fs";
76831
76944
  import { isAbsolute as isAbsolute4, join as join29 } from "path";
76832
76945
  init_native_entities();
76833
76946
  function badRequest(message) {
@@ -76861,7 +76974,7 @@ async function readImportSourceFromFile(db, fileId, latticeRoot) {
76861
76974
  );
76862
76975
  if (!row) throw badRequest("Unknown import file: " + fileId);
76863
76976
  const path3 = localPathOf2(row, latticeRoot);
76864
- if (!path3 || !existsSync26(path3)) {
76977
+ if (!path3 || !existsSync27(path3)) {
76865
76978
  throw badRequest("The import file\u2019s bytes are not available locally.");
76866
76979
  }
76867
76980
  const sizeBytes = statSync10(path3).size;
@@ -79002,7 +79115,7 @@ init_dispatch();
79002
79115
  init_column_descriptions();
79003
79116
  init_mutations();
79004
79117
  init_native_entities();
79005
- import { createReadStream as createReadStream3, existsSync as existsSync27, realpathSync as realpathSync2, statSync as statSync11 } from "fs";
79118
+ import { createReadStream as createReadStream3, existsSync as existsSync28, realpathSync as realpathSync2, statSync as statSync11 } from "fs";
79006
79119
  import { extname as extname3, join as join30, normalize as normalize2, sep as sep9 } from "path";
79007
79120
 
79008
79121
  // src/gui/count-many.ts
@@ -79210,7 +79323,7 @@ async function handleReadRoutes(req, res, ctx, deps) {
79210
79323
  const base = deps.guiAssetsDir.replace(/[/\\]+$/, "");
79211
79324
  const target = normalize2(join30(base, rel));
79212
79325
  const within = target === base || target.startsWith(base + sep9);
79213
- if (!within || !existsSync27(target) || !statSync11(target).isFile()) {
79326
+ if (!within || !existsSync28(target) || !statSync11(target).isFile()) {
79214
79327
  sendJson(res, { error: "asset not found" }, 404);
79215
79328
  return true;
79216
79329
  }
@@ -80418,16 +80531,16 @@ async function handleHistoryRoutes(req, res, ctx, deps) {
80418
80531
  // src/gui/workspaces-routes.ts
80419
80532
  init_http();
80420
80533
  import { resolve as resolve12 } from "path";
80421
- import { existsSync as existsSync28, rmSync } from "fs";
80534
+ import { existsSync as existsSync29, rmSync as rmSync2 } from "fs";
80422
80535
  init_workspace();
80423
80536
  init_lattice_root();
80424
80537
  init_user_config();
80425
80538
  function cleanupWorkspaceFiles(root6, ws) {
80426
80539
  if (!ws.configPath && ws.kind === "local") {
80427
- rmSync(workspaceDir(root6, ws.dir), { recursive: true, force: true });
80540
+ rmSync2(workspaceDir(root6, ws.dir), { recursive: true, force: true });
80428
80541
  } else if (ws.kind === "cloud") {
80429
- if (ws.configPath && existsSync28(ws.configPath)) {
80430
- rmSync(ws.configPath, { force: true });
80542
+ if (ws.configPath && existsSync29(ws.configPath)) {
80543
+ rmSync2(ws.configPath, { force: true });
80431
80544
  }
80432
80545
  const labelMatch = /^\$\{LATTICE_DB:([A-Za-z0-9._-]+)\}$/.exec(ws.db.trim());
80433
80546
  const label = labelMatch?.[1];
@@ -80630,15 +80743,15 @@ async function handleWorkspacesRoutes(req, res, ctx, deps) {
80630
80743
  // src/gui/databases-routes.ts
80631
80744
  init_http();
80632
80745
  import { basename as basename14, resolve as resolve14 } from "path";
80633
- import { existsSync as existsSync30 } from "fs";
80746
+ import { existsSync as existsSync31 } from "fs";
80634
80747
  init_parser();
80635
80748
 
80636
80749
  // src/gui/config-paths.ts
80637
80750
  init_parser();
80638
- import { basename as basename13, dirname as dirname15, join as join31, resolve as resolve13 } from "path";
80751
+ import { basename as basename13, dirname as dirname16, join as join31, resolve as resolve13 } from "path";
80639
80752
  import {
80640
- existsSync as existsSync29,
80641
- mkdirSync as mkdirSync11,
80753
+ existsSync as existsSync30,
80754
+ mkdirSync as mkdirSync12,
80642
80755
  readFileSync as readFileSync25,
80643
80756
  readdirSync as readdirSync9,
80644
80757
  unlinkSync as unlinkSync5,
@@ -80646,10 +80759,10 @@ import {
80646
80759
  } from "fs";
80647
80760
  import { parseDocument as parseDocument7 } from "yaml";
80648
80761
  function resolveOutputDirForConfig(configPath) {
80649
- const base = dirname15(resolve13(configPath));
80762
+ const base = dirname16(resolve13(configPath));
80650
80763
  for (const dir of ["context", ".", "generated"]) {
80651
80764
  const abs = resolve13(base, dir);
80652
- if (existsSync29(join31(abs, ".lattice", "manifest.json"))) return abs;
80765
+ if (existsSync30(join31(abs, ".lattice", "manifest.json"))) return abs;
80653
80766
  }
80654
80767
  return resolve13(base, "context");
80655
80768
  }
@@ -80658,7 +80771,7 @@ function friendlyConfigName(parsedName, configPath) {
80658
80771
  return basename13(configPath).replace(/\.(ya?ml)$/, "");
80659
80772
  }
80660
80773
  function listConfigs(activeConfigPath) {
80661
- const dir = dirname15(activeConfigPath);
80774
+ const dir = dirname16(activeConfigPath);
80662
80775
  const entries = [];
80663
80776
  for (const fname of readdirSync9(dir)) {
80664
80777
  if (!fname.endsWith(".yml") && !fname.endsWith(".yaml")) continue;
@@ -80687,17 +80800,17 @@ function listConfigs(activeConfigPath) {
80687
80800
  return entries.sort((a6, b6) => a6.label.localeCompare(b6.label));
80688
80801
  }
80689
80802
  function createBlankConfig(activeConfigPath, dbName) {
80690
- const dir = dirname15(activeConfigPath);
80803
+ const dir = dirname16(activeConfigPath);
80691
80804
  const slug = dbName.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
80692
80805
  if (!slug) throw new Error("Workspace name must contain at least one alphanumeric character");
80693
80806
  const configPath = join31(dir, `${slug}.config.yml`);
80694
- if (existsSync29(configPath)) throw new Error(`Config already exists: ${slug}.config.yml`);
80807
+ if (existsSync30(configPath)) throw new Error(`Config already exists: ${slug}.config.yml`);
80695
80808
  const yaml = `db: ./data/${slug}.db
80696
80809
 
80697
80810
  entities: {}
80698
80811
  `;
80699
80812
  writeFileSync10(configPath, yaml, "utf8");
80700
- mkdirSync11(join31(dir, "data"), { recursive: true });
80813
+ mkdirSync12(join31(dir, "data"), { recursive: true });
80701
80814
  return configPath;
80702
80815
  }
80703
80816
  function sqliteFileForConfig(configPath) {
@@ -80706,18 +80819,18 @@ function sqliteFileForConfig(configPath) {
80706
80819
  if (!raw) return null;
80707
80820
  if (isPostgresUrl(raw) || raw.startsWith("${LATTICE_DB:")) return null;
80708
80821
  if (raw === ":memory:" || raw.startsWith("file:")) return null;
80709
- return resolve13(dirname15(configPath), raw);
80822
+ return resolve13(dirname16(configPath), raw);
80710
80823
  }
80711
80824
  function deleteDatabaseFiles(targetConfigPath) {
80712
80825
  const sqliteFile = sqliteFileForConfig(targetConfigPath);
80713
80826
  unlinkSync5(targetConfigPath);
80714
80827
  let deletedDbFile = null;
80715
- if (sqliteFile && existsSync29(sqliteFile)) {
80828
+ if (sqliteFile && existsSync30(sqliteFile)) {
80716
80829
  unlinkSync5(sqliteFile);
80717
80830
  deletedDbFile = sqliteFile;
80718
80831
  for (const suffix of ["-wal", "-shm", "-journal"]) {
80719
80832
  const sidecar = sqliteFile + suffix;
80720
- if (existsSync29(sidecar)) unlinkSync5(sidecar);
80833
+ if (existsSync30(sidecar)) unlinkSync5(sidecar);
80721
80834
  }
80722
80835
  }
80723
80836
  return { deletedConfig: basename13(targetConfigPath), deletedDbFile };
@@ -80751,7 +80864,7 @@ async function handleDatabasesRoutes(req, res, ctx, deps) {
80751
80864
  return true;
80752
80865
  }
80753
80866
  const newPath = resolve14(body.path);
80754
- if (!existsSync30(newPath)) {
80867
+ if (!existsSync31(newPath)) {
80755
80868
  sendJson(res, { error: `Config not found: ${newPath}` }, 400);
80756
80869
  return true;
80757
80870
  }
@@ -80857,8 +80970,8 @@ async function handleDatabasesRoutes(req, res, ctx, deps) {
80857
80970
  // src/gui/server.ts
80858
80971
  init_user_config();
80859
80972
  function resolveDefaultGuiAssetsDir() {
80860
- const bundleSibling = process.argv[1] ? resolve15(dirname16(process.argv[1]), "gui-assets") : null;
80861
- if (bundleSibling && existsSync31(bundleSibling)) return bundleSibling;
80973
+ const bundleSibling = process.argv[1] ? resolve15(dirname17(process.argv[1]), "gui-assets") : null;
80974
+ if (bundleSibling && existsSync32(bundleSibling)) return bundleSibling;
80862
80975
  return resolve15(process.cwd(), "dist", "gui-assets");
80863
80976
  }
80864
80977
  function sendText(res, body, status = 200, contentType = "text/plain; charset=utf-8") {
@@ -80915,7 +81028,7 @@ async function startGuiServer(options) {
80915
81028
  const sessionId = crypto.randomUUID();
80916
81029
  let updateService = null;
80917
81030
  let activeRef = bootConfigPath && bootOutputDir ? await openConfig(bootConfigPath, bootOutputDir, autoRender, options.realtimeWatchdogMs) : null;
80918
- const latticeRoot = (bootConfigPath ? findLatticeRoot(dirname16(bootConfigPath)) : null) ?? (options.latticeRoot ? resolve15(options.latticeRoot) : null);
81031
+ const latticeRoot = (bootConfigPath ? findLatticeRoot(dirname17(bootConfigPath)) : null) ?? (options.latticeRoot ? resolve15(options.latticeRoot) : null);
80919
81032
  let currentWorkspaceId = null;
80920
81033
  if (latticeRoot && bootConfigPath) {
80921
81034
  const launched = listWorkspaces(latticeRoot).find(
@@ -81269,7 +81382,7 @@ async function startGuiServer(options) {
81269
81382
  createJunction: (otherTable) => createFileJunction(active, otherTable, sessionId),
81270
81383
  createEntity: (entity, columns) => createUserEntity(active, entity, columns, sessionId),
81271
81384
  aggressiveness: getAggressiveness(),
81272
- latticeRoot: dirname16(active.configPath),
81385
+ latticeRoot: dirname17(active.configPath),
81273
81386
  configPath: active.configPath,
81274
81387
  outputDir: active.outputDir,
81275
81388
  sessionId,
@@ -81294,7 +81407,7 @@ async function startGuiServer(options) {
81294
81407
  createJunction: (otherTable) => createFileJunction(active, otherTable, sessionId),
81295
81408
  createEntity: (entity, columns) => createUserEntity(active, entity, columns, sessionId),
81296
81409
  aggressiveness: getAggressiveness(),
81297
- latticeRoot: dirname16(active.configPath),
81410
+ latticeRoot: dirname17(active.configPath),
81298
81411
  configPath: active.configPath,
81299
81412
  outputDir: active.outputDir,
81300
81413
  sessionId,
@@ -81305,6 +81418,7 @@ async function startGuiServer(options) {
81305
81418
  return await dispatchSourcesRoute(req2, res2, {
81306
81419
  db: active.db,
81307
81420
  ingestFile: (p3) => ingestLocalFile(ingestCtx, mctx, p3, false),
81421
+ configPath: active.configPath,
81308
81422
  pathname,
81309
81423
  method
81310
81424
  });
@@ -81320,7 +81434,7 @@ async function startGuiServer(options) {
81320
81434
  return await dispatchImportRoute(req2, res2, {
81321
81435
  db: active.db,
81322
81436
  configPath: active.configPath,
81323
- latticeRoot: dirname16(active.configPath),
81437
+ latticeRoot: dirname17(active.configPath),
81324
81438
  validTables: active.validTables,
81325
81439
  softDeletable: active.softDeletable
81326
81440
  });
@@ -81349,7 +81463,7 @@ async function startGuiServer(options) {
81349
81463
  if (!pathname.startsWith("/api/files/")) return false;
81350
81464
  return await dispatchFilesRoute(req2, res2, {
81351
81465
  db: active.db,
81352
- latticeRoot: dirname16(active.configPath),
81466
+ latticeRoot: dirname17(active.configPath),
81353
81467
  configPath: active.configPath,
81354
81468
  pathname,
81355
81469
  method
@@ -81560,7 +81674,7 @@ ${e6.stack ?? ""}`
81560
81674
  }
81561
81675
 
81562
81676
  // src/desktop-entry.ts
81563
- var VERSION2 = true ? "4.3.1" : "unknown";
81677
+ var VERSION2 = true ? "4.3.3" : "unknown";
81564
81678
  export {
81565
81679
  VERSION2 as VERSION,
81566
81680
  ensureRootForGui,