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.
- package/dist/cli.js +200 -86
- package/dist/desktop-entry.js +196 -82
- package/dist/index.cjs +154 -48
- package/dist/index.js +200 -86
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -909,7 +909,7 @@ async function registerPostgresPolyfills(run) {
|
|
|
909
909
|
await run(sql);
|
|
910
910
|
} catch (err) {
|
|
911
911
|
const msg = err instanceof Error ? err.message : String(err);
|
|
912
|
-
if (/permission denied/i.test(msg)) {
|
|
912
|
+
if (/permission denied/i.test(msg) || /must be owner of/i.test(msg)) {
|
|
913
913
|
permissionDenied = true;
|
|
914
914
|
} else {
|
|
915
915
|
console.warn(`[PostgresAdapter] ${warn}`, msg);
|
|
@@ -1427,40 +1427,111 @@ var init_postgres = __esm({
|
|
|
1427
1427
|
SELECT doc::jsonb #>> string_to_array(regexp_replace(path, '^\\$\\.?', ''), '.')
|
|
1428
1428
|
$fn$;
|
|
1429
1429
|
END IF;
|
|
1430
|
+
END $do$;`
|
|
1431
|
+
},
|
|
1432
|
+
{
|
|
1433
|
+
warn: "could not register strftime format helper:",
|
|
1434
|
+
// Shared format translator (SQLite strftime tokens → to_char patterns), in UTC.
|
|
1435
|
+
// Factored out so the 2-arg and 3-arg strftime overloads share one definition.
|
|
1436
|
+
// Each polyfill below is CREATE OR REPLACE (so an existing cloud's prior copy is
|
|
1437
|
+
// UPGRADED when the owner opens), wrapped in a DO block whose EXCEPTION swallows a
|
|
1438
|
+
// scoped member's insufficient-privilege failure IN A SUBTRANSACTION — so a member
|
|
1439
|
+
// that runs registration inside a transaction is NOT aborted. ("must be owner of
|
|
1440
|
+
// function", raised when a non-owner CREATE OR REPLACEs another role's function, is
|
|
1441
|
+
// SQLSTATE 42501 / `insufficient_privilege`, same class as "permission denied".) The
|
|
1442
|
+
// owner already owns + replaced the function; the member simply uses the owner's
|
|
1443
|
+
// copy — and `ownPolyfillsByGroup` re-owns the whole set to the member group so any
|
|
1444
|
+
// member can replace them on a later upgrade.
|
|
1445
|
+
sql: `DO $do$ BEGIN
|
|
1446
|
+
CREATE OR REPLACE FUNCTION __lattice_strftime_fmt(ts timestamptz, format text)
|
|
1447
|
+
RETURNS text
|
|
1448
|
+
LANGUAGE sql
|
|
1449
|
+
IMMUTABLE
|
|
1450
|
+
AS $fn$
|
|
1451
|
+
SELECT to_char(
|
|
1452
|
+
ts AT TIME ZONE 'UTC',
|
|
1453
|
+
replace(replace(replace(replace(replace(replace(replace(replace(
|
|
1454
|
+
format,
|
|
1455
|
+
'%Y', 'YYYY'),
|
|
1456
|
+
'%m', 'MM'),
|
|
1457
|
+
'%d', 'DD'),
|
|
1458
|
+
'%H', 'HH24'),
|
|
1459
|
+
'%M', 'MI'),
|
|
1460
|
+
'%S', 'SS'),
|
|
1461
|
+
'%f', 'MS'),
|
|
1462
|
+
'T', '"T"')
|
|
1463
|
+
);
|
|
1464
|
+
$fn$;
|
|
1465
|
+
EXCEPTION WHEN insufficient_privilege THEN NULL;
|
|
1430
1466
|
END $do$;`
|
|
1431
1467
|
},
|
|
1432
1468
|
{
|
|
1433
1469
|
warn: "could not register strftime polyfill:",
|
|
1470
|
+
// The pre-4.3.3 strftime cast the modifier straight to timestamptz and threw
|
|
1471
|
+
// `invalid input syntax for type timestamp with time zone: ""` on a legacy ''
|
|
1472
|
+
// value (3.x stored nullable TEXT timestamps as ''), bricking the whole workspace
|
|
1473
|
+
// open. Now an empty, whitespace, or unparseable time string returns NULL (SQLite's
|
|
1474
|
+
// strftime semantics) instead of aborting the query.
|
|
1434
1475
|
sql: `DO $do$ BEGIN
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1476
|
+
CREATE OR REPLACE FUNCTION strftime(format text, modifier text)
|
|
1477
|
+
RETURNS text
|
|
1478
|
+
LANGUAGE plpgsql
|
|
1479
|
+
IMMUTABLE
|
|
1480
|
+
AS $fn$
|
|
1481
|
+
DECLARE ts timestamptz;
|
|
1482
|
+
BEGIN
|
|
1483
|
+
IF modifier = 'now' THEN
|
|
1484
|
+
ts := now();
|
|
1485
|
+
ELSIF NULLIF(btrim(modifier), '') IS NULL THEN
|
|
1486
|
+
RETURN NULL;
|
|
1487
|
+
ELSE
|
|
1488
|
+
BEGIN
|
|
1446
1489
|
ts := modifier::timestamptz;
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1490
|
+
EXCEPTION WHEN others THEN
|
|
1491
|
+
RETURN NULL;
|
|
1492
|
+
END;
|
|
1493
|
+
END IF;
|
|
1494
|
+
RETURN __lattice_strftime_fmt(ts, format);
|
|
1495
|
+
END;
|
|
1496
|
+
$fn$;
|
|
1497
|
+
EXCEPTION WHEN insufficient_privilege THEN NULL;
|
|
1498
|
+
END $do$;`
|
|
1499
|
+
},
|
|
1500
|
+
{
|
|
1501
|
+
warn: "could not register 3-arg strftime polyfill:",
|
|
1502
|
+
// SQLite's 3-arg strftime(format, timestring, modifier) — used by the changelog
|
|
1503
|
+
// retention prune (e.g. strftime('%Y-...','now','-30 days')). Postgres had no
|
|
1504
|
+
// 3-arg overload, so that prune threw `function strftime(...) does not exist` on
|
|
1505
|
+
// every PG cloud with retention. Resolve the base time, apply the modifier as an
|
|
1506
|
+
// interval, then reuse the shared formatter. Empty/invalid → NULL, never throws.
|
|
1507
|
+
sql: `DO $do$ BEGIN
|
|
1508
|
+
CREATE OR REPLACE FUNCTION strftime(format text, timestring text, modifier text)
|
|
1509
|
+
RETURNS text
|
|
1510
|
+
LANGUAGE plpgsql
|
|
1511
|
+
IMMUTABLE
|
|
1512
|
+
AS $fn$
|
|
1513
|
+
DECLARE ts timestamptz;
|
|
1514
|
+
BEGIN
|
|
1515
|
+
IF timestring = 'now' THEN
|
|
1516
|
+
ts := now();
|
|
1517
|
+
ELSIF NULLIF(btrim(timestring), '') IS NULL THEN
|
|
1518
|
+
RETURN NULL;
|
|
1519
|
+
ELSE
|
|
1520
|
+
BEGIN
|
|
1521
|
+
ts := timestring::timestamptz;
|
|
1522
|
+
EXCEPTION WHEN others THEN
|
|
1523
|
+
RETURN NULL;
|
|
1524
|
+
END;
|
|
1525
|
+
END IF;
|
|
1526
|
+
BEGIN
|
|
1527
|
+
ts := ts + modifier::interval;
|
|
1528
|
+
EXCEPTION WHEN others THEN
|
|
1529
|
+
RETURN NULL;
|
|
1461
1530
|
END;
|
|
1462
|
-
|
|
1463
|
-
|
|
1531
|
+
RETURN __lattice_strftime_fmt(ts, format);
|
|
1532
|
+
END;
|
|
1533
|
+
$fn$;
|
|
1534
|
+
EXCEPTION WHEN insufficient_privilege THEN NULL;
|
|
1464
1535
|
END $do$;`
|
|
1465
1536
|
}
|
|
1466
1537
|
];
|
|
@@ -9397,7 +9468,12 @@ CREATE TRIGGER "${trg}" AFTER INSERT OR UPDATE OR DELETE ON ${q3}
|
|
|
9397
9468
|
async function ownPolyfillsByGroup(db) {
|
|
9398
9469
|
if (!isPg(db)) return;
|
|
9399
9470
|
const group3 = await memberGroupFor(db);
|
|
9400
|
-
for (const sig of [
|
|
9471
|
+
for (const sig of [
|
|
9472
|
+
"json_extract(text, text)",
|
|
9473
|
+
"strftime(text, text)",
|
|
9474
|
+
"strftime(text, text, text)",
|
|
9475
|
+
"__lattice_strftime_fmt(timestamptz, text)"
|
|
9476
|
+
]) {
|
|
9401
9477
|
try {
|
|
9402
9478
|
const reg = await getAsyncOrSync(db.adapter, `SELECT to_regprocedure($1) AS reg`, [sig]);
|
|
9403
9479
|
if (reg?.reg == null) continue;
|
|
@@ -23495,12 +23571,12 @@ var getNodeModulesParentDirs;
|
|
|
23495
23571
|
var init_getNodeModulesParentDirs = __esm({
|
|
23496
23572
|
"node_modules/@aws-sdk/core/dist-es/submodules/client/util-user-agent-node/getNodeModulesParentDirs.js"() {
|
|
23497
23573
|
"use strict";
|
|
23498
|
-
getNodeModulesParentDirs = (
|
|
23574
|
+
getNodeModulesParentDirs = (dirname19) => {
|
|
23499
23575
|
const cwd = process.cwd();
|
|
23500
|
-
if (!
|
|
23576
|
+
if (!dirname19) {
|
|
23501
23577
|
return [cwd];
|
|
23502
23578
|
}
|
|
23503
|
-
const normalizedPath = normalize(
|
|
23579
|
+
const normalizedPath = normalize(dirname19);
|
|
23504
23580
|
const parts = normalizedPath.split(sep3);
|
|
23505
23581
|
const nodeModulesIndex = parts.indexOf("node_modules");
|
|
23506
23582
|
const parentDir = nodeModulesIndex !== -1 ? parts.slice(0, nodeModulesIndex).join(sep3) : normalizedPath;
|
|
@@ -23578,8 +23654,8 @@ var init_getTypeScriptUserAgentPair = __esm({
|
|
|
23578
23654
|
tscVersion = null;
|
|
23579
23655
|
return void 0;
|
|
23580
23656
|
}
|
|
23581
|
-
const
|
|
23582
|
-
const nodeModulesParentDirs = getNodeModulesParentDirs(
|
|
23657
|
+
const dirname19 = typeof __dirname !== "undefined" ? __dirname : void 0;
|
|
23658
|
+
const nodeModulesParentDirs = getNodeModulesParentDirs(dirname19);
|
|
23583
23659
|
let versionFromApp;
|
|
23584
23660
|
for (const nodeModulesParentDir of nodeModulesParentDirs) {
|
|
23585
23661
|
try {
|
|
@@ -62153,9 +62229,9 @@ init_summarize();
|
|
|
62153
62229
|
init_http2();
|
|
62154
62230
|
import { createServer } from "http";
|
|
62155
62231
|
import { spawn as spawn2 } from "child_process";
|
|
62156
|
-
import { existsSync as
|
|
62232
|
+
import { existsSync as existsSync32 } from "fs";
|
|
62157
62233
|
import { WebSocketServer, WebSocket } from "ws";
|
|
62158
|
-
import { dirname as
|
|
62234
|
+
import { dirname as dirname17, resolve as resolve16 } from "path";
|
|
62159
62235
|
|
|
62160
62236
|
// src/gui/active-db.ts
|
|
62161
62237
|
init_adapter();
|
|
@@ -79982,8 +80058,16 @@ async function dispatchIngestRoute(req, res, ctx) {
|
|
|
79982
80058
|
|
|
79983
80059
|
// src/gui/sources-routes.ts
|
|
79984
80060
|
init_user_config();
|
|
79985
|
-
import {
|
|
79986
|
-
|
|
80061
|
+
import {
|
|
80062
|
+
statSync as statSync10,
|
|
80063
|
+
readdirSync as readdirSync8,
|
|
80064
|
+
readFileSync as readFileSync24,
|
|
80065
|
+
writeFileSync as writeFileSync9,
|
|
80066
|
+
existsSync as existsSync26,
|
|
80067
|
+
mkdirSync as mkdirSync11,
|
|
80068
|
+
rmSync
|
|
80069
|
+
} from "fs";
|
|
80070
|
+
import { resolve as resolve12, join as join30, basename as basename13, sep as sep8, dirname as dirname15 } from "path";
|
|
79987
80071
|
import { execFile } from "child_process";
|
|
79988
80072
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
79989
80073
|
init_http2();
|
|
@@ -79991,20 +80075,49 @@ var MAX_LIST_ENTRIES = 2e3;
|
|
|
79991
80075
|
var MAX_INGEST_FILES = 500;
|
|
79992
80076
|
var MAX_INGEST_DEPTH = 8;
|
|
79993
80077
|
var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".DS_Store", "__pycache__", ".venv", "venv"]);
|
|
79994
|
-
function rootsFile() {
|
|
79995
|
-
return join30(
|
|
80078
|
+
function rootsFile(configPath) {
|
|
80079
|
+
return join30(dirname15(configPath), "sources.json");
|
|
80080
|
+
}
|
|
80081
|
+
function migrateGlobalRootsIfNeeded(configPath) {
|
|
80082
|
+
const wsFile = rootsFile(configPath);
|
|
80083
|
+
if (existsSync26(wsFile)) return;
|
|
80084
|
+
const globalFile = join30(configDir(), "sources.json");
|
|
80085
|
+
if (globalFile === wsFile || !existsSync26(globalFile)) return;
|
|
80086
|
+
let roots;
|
|
80087
|
+
try {
|
|
80088
|
+
const parsed = JSON.parse(readFileSync24(globalFile, "utf8"));
|
|
80089
|
+
if (!Array.isArray(parsed.roots) || parsed.roots.length === 0) return;
|
|
80090
|
+
roots = parsed.roots;
|
|
80091
|
+
} catch {
|
|
80092
|
+
return;
|
|
80093
|
+
}
|
|
80094
|
+
try {
|
|
80095
|
+
mkdirSync11(dirname15(wsFile), { recursive: true });
|
|
80096
|
+
writeFileSync9(wsFile, JSON.stringify({ roots }, null, 2), "utf8");
|
|
80097
|
+
} catch {
|
|
80098
|
+
return;
|
|
80099
|
+
}
|
|
80100
|
+
try {
|
|
80101
|
+
rmSync(globalFile, { force: true });
|
|
80102
|
+
} catch {
|
|
80103
|
+
try {
|
|
80104
|
+
writeFileSync9(globalFile, JSON.stringify({ roots: [] }, null, 2), "utf8");
|
|
80105
|
+
} catch {
|
|
80106
|
+
}
|
|
80107
|
+
}
|
|
79996
80108
|
}
|
|
79997
|
-
function readRoots() {
|
|
80109
|
+
function readRoots(configPath) {
|
|
80110
|
+
migrateGlobalRootsIfNeeded(configPath);
|
|
79998
80111
|
try {
|
|
79999
|
-
const raw = readFileSync24(rootsFile(), "utf8");
|
|
80112
|
+
const raw = readFileSync24(rootsFile(configPath), "utf8");
|
|
80000
80113
|
const parsed = JSON.parse(raw);
|
|
80001
80114
|
return Array.isArray(parsed.roots) ? parsed.roots : [];
|
|
80002
80115
|
} catch {
|
|
80003
80116
|
return [];
|
|
80004
80117
|
}
|
|
80005
80118
|
}
|
|
80006
|
-
function writeRoots(roots) {
|
|
80007
|
-
writeFileSync9(rootsFile(), JSON.stringify({ roots }, null, 2), "utf8");
|
|
80119
|
+
function writeRoots(configPath, roots) {
|
|
80120
|
+
writeFileSync9(rootsFile(configPath), JSON.stringify({ roots }, null, 2), "utf8");
|
|
80008
80121
|
}
|
|
80009
80122
|
function safeResolveInside2(target, roots) {
|
|
80010
80123
|
const abs = resolve12(target);
|
|
@@ -80094,14 +80207,14 @@ async function ingestFolder(abs, ingestFile) {
|
|
|
80094
80207
|
return { ingested, skipped };
|
|
80095
80208
|
}
|
|
80096
80209
|
async function dispatchSourcesRoute(req, res, deps) {
|
|
80097
|
-
const { pathname, method, ingestFile } = deps;
|
|
80210
|
+
const { pathname, method, ingestFile, configPath } = deps;
|
|
80098
80211
|
if (!pathname.startsWith("/api/sources/")) return false;
|
|
80099
80212
|
if (!localFileOpenEnabled()) {
|
|
80100
80213
|
sendJson(res, { enabled: false });
|
|
80101
80214
|
return true;
|
|
80102
80215
|
}
|
|
80103
80216
|
if (pathname === "/api/sources/roots" && method === "GET") {
|
|
80104
|
-
sendJson(res, { enabled: true, roots: readRoots() });
|
|
80217
|
+
sendJson(res, { enabled: true, roots: readRoots(configPath) });
|
|
80105
80218
|
return true;
|
|
80106
80219
|
}
|
|
80107
80220
|
if (pathname === "/api/sources/roots" && method === "POST") {
|
|
@@ -80127,12 +80240,12 @@ async function dispatchSourcesRoute(req, res, deps) {
|
|
|
80127
80240
|
sendJson(res, { error: `path not found: ${abs}` }, 400);
|
|
80128
80241
|
return true;
|
|
80129
80242
|
}
|
|
80130
|
-
const roots = readRoots();
|
|
80243
|
+
const roots = readRoots(configPath);
|
|
80131
80244
|
let root6 = roots.find((r6) => resolve12(r6.path) === abs);
|
|
80132
80245
|
if (!root6) {
|
|
80133
80246
|
root6 = { id: randomUUID4(), path: abs, kind, name: basename13(abs) || abs };
|
|
80134
80247
|
roots.push(root6);
|
|
80135
|
-
writeRoots(roots);
|
|
80248
|
+
writeRoots(configPath, roots);
|
|
80136
80249
|
}
|
|
80137
80250
|
let result;
|
|
80138
80251
|
if (kind === "folder") result = await ingestFolder(abs, ingestFile);
|
|
@@ -80143,8 +80256,8 @@ async function dispatchSourcesRoute(req, res, deps) {
|
|
|
80143
80256
|
const delMatch = /^\/api\/sources\/roots\/([^/]+)$/.exec(pathname);
|
|
80144
80257
|
if (delMatch && method === "DELETE") {
|
|
80145
80258
|
const id = decodeURIComponent(delMatch[1] ?? "");
|
|
80146
|
-
const roots = readRoots().filter((r6) => r6.id !== id);
|
|
80147
|
-
writeRoots(roots);
|
|
80259
|
+
const roots = readRoots(configPath).filter((r6) => r6.id !== id);
|
|
80260
|
+
writeRoots(configPath, roots);
|
|
80148
80261
|
sendJson(res, { ok: true });
|
|
80149
80262
|
return true;
|
|
80150
80263
|
}
|
|
@@ -80162,7 +80275,7 @@ async function dispatchSourcesRoute(req, res, deps) {
|
|
|
80162
80275
|
sendJson(res, { error: "path is required" }, 400);
|
|
80163
80276
|
return true;
|
|
80164
80277
|
}
|
|
80165
|
-
const abs = safeResolveInside2(target, readRoots());
|
|
80278
|
+
const abs = safeResolveInside2(target, readRoots(configPath));
|
|
80166
80279
|
if (!abs) {
|
|
80167
80280
|
sendJson(res, { error: "path is outside any registered source root" }, 403);
|
|
80168
80281
|
return true;
|
|
@@ -80177,7 +80290,7 @@ async function dispatchSourcesRoute(req, res, deps) {
|
|
|
80177
80290
|
if (pathname === "/api/sources/ingest-folder" && method === "POST") {
|
|
80178
80291
|
const body = await readJson(req).catch(() => ({}));
|
|
80179
80292
|
const target = typeof body.path === "string" ? body.path : "";
|
|
80180
|
-
const abs = safeResolveInside2(target, readRoots());
|
|
80293
|
+
const abs = safeResolveInside2(target, readRoots(configPath));
|
|
80181
80294
|
if (!abs) {
|
|
80182
80295
|
sendJson(res, { error: "path is outside any registered source root" }, 403);
|
|
80183
80296
|
return true;
|
|
@@ -80191,7 +80304,7 @@ async function dispatchSourcesRoute(req, res, deps) {
|
|
|
80191
80304
|
// src/gui/import-routes.ts
|
|
80192
80305
|
init_adapter();
|
|
80193
80306
|
init_http2();
|
|
80194
|
-
import { existsSync as
|
|
80307
|
+
import { existsSync as existsSync27, readFileSync as readFileSync25, statSync as statSync11 } from "fs";
|
|
80195
80308
|
import { isAbsolute as isAbsolute4, join as join31 } from "path";
|
|
80196
80309
|
init_native_entities();
|
|
80197
80310
|
function badRequest(message) {
|
|
@@ -80225,7 +80338,7 @@ async function readImportSourceFromFile(db, fileId, latticeRoot) {
|
|
|
80225
80338
|
);
|
|
80226
80339
|
if (!row) throw badRequest("Unknown import file: " + fileId);
|
|
80227
80340
|
const path3 = localPathOf2(row, latticeRoot);
|
|
80228
|
-
if (!path3 || !
|
|
80341
|
+
if (!path3 || !existsSync27(path3)) {
|
|
80229
80342
|
throw badRequest("The import file\u2019s bytes are not available locally.");
|
|
80230
80343
|
}
|
|
80231
80344
|
const sizeBytes = statSync11(path3).size;
|
|
@@ -80520,7 +80633,7 @@ init_dispatch();
|
|
|
80520
80633
|
init_column_descriptions();
|
|
80521
80634
|
init_mutations();
|
|
80522
80635
|
init_native_entities();
|
|
80523
|
-
import { createReadStream as createReadStream3, existsSync as
|
|
80636
|
+
import { createReadStream as createReadStream3, existsSync as existsSync28, realpathSync as realpathSync2, statSync as statSync12 } from "fs";
|
|
80524
80637
|
import { extname as extname3, join as join32, normalize as normalize2, sep as sep9 } from "path";
|
|
80525
80638
|
|
|
80526
80639
|
// src/gui/count-many.ts
|
|
@@ -80728,7 +80841,7 @@ async function handleReadRoutes(req, res, ctx, deps) {
|
|
|
80728
80841
|
const base = deps.guiAssetsDir.replace(/[/\\]+$/, "");
|
|
80729
80842
|
const target = normalize2(join32(base, rel));
|
|
80730
80843
|
const within = target === base || target.startsWith(base + sep9);
|
|
80731
|
-
if (!within || !
|
|
80844
|
+
if (!within || !existsSync28(target) || !statSync12(target).isFile()) {
|
|
80732
80845
|
sendJson(res, { error: "asset not found" }, 404);
|
|
80733
80846
|
return true;
|
|
80734
80847
|
}
|
|
@@ -81936,16 +82049,16 @@ async function handleHistoryRoutes(req, res, ctx, deps) {
|
|
|
81936
82049
|
// src/gui/workspaces-routes.ts
|
|
81937
82050
|
init_http2();
|
|
81938
82051
|
import { resolve as resolve13 } from "path";
|
|
81939
|
-
import { existsSync as
|
|
82052
|
+
import { existsSync as existsSync29, rmSync as rmSync2 } from "fs";
|
|
81940
82053
|
init_workspace();
|
|
81941
82054
|
init_lattice_root();
|
|
81942
82055
|
init_user_config();
|
|
81943
82056
|
function cleanupWorkspaceFiles(root6, ws) {
|
|
81944
82057
|
if (!ws.configPath && ws.kind === "local") {
|
|
81945
|
-
|
|
82058
|
+
rmSync2(workspaceDir(root6, ws.dir), { recursive: true, force: true });
|
|
81946
82059
|
} else if (ws.kind === "cloud") {
|
|
81947
|
-
if (ws.configPath &&
|
|
81948
|
-
|
|
82060
|
+
if (ws.configPath && existsSync29(ws.configPath)) {
|
|
82061
|
+
rmSync2(ws.configPath, { force: true });
|
|
81949
82062
|
}
|
|
81950
82063
|
const labelMatch = /^\$\{LATTICE_DB:([A-Za-z0-9._-]+)\}$/.exec(ws.db.trim());
|
|
81951
82064
|
const label = labelMatch?.[1];
|
|
@@ -82148,15 +82261,15 @@ async function handleWorkspacesRoutes(req, res, ctx, deps) {
|
|
|
82148
82261
|
// src/gui/databases-routes.ts
|
|
82149
82262
|
init_http2();
|
|
82150
82263
|
import { basename as basename15, resolve as resolve15 } from "path";
|
|
82151
|
-
import { existsSync as
|
|
82264
|
+
import { existsSync as existsSync31 } from "fs";
|
|
82152
82265
|
init_parser();
|
|
82153
82266
|
|
|
82154
82267
|
// src/gui/config-paths.ts
|
|
82155
82268
|
init_parser();
|
|
82156
|
-
import { basename as basename14, dirname as
|
|
82269
|
+
import { basename as basename14, dirname as dirname16, join as join33, resolve as resolve14 } from "path";
|
|
82157
82270
|
import {
|
|
82158
|
-
existsSync as
|
|
82159
|
-
mkdirSync as
|
|
82271
|
+
existsSync as existsSync30,
|
|
82272
|
+
mkdirSync as mkdirSync12,
|
|
82160
82273
|
readFileSync as readFileSync26,
|
|
82161
82274
|
readdirSync as readdirSync9,
|
|
82162
82275
|
unlinkSync as unlinkSync5,
|
|
@@ -82164,10 +82277,10 @@ import {
|
|
|
82164
82277
|
} from "fs";
|
|
82165
82278
|
import { parseDocument as parseDocument7 } from "yaml";
|
|
82166
82279
|
function resolveOutputDirForConfig(configPath) {
|
|
82167
|
-
const base =
|
|
82280
|
+
const base = dirname16(resolve14(configPath));
|
|
82168
82281
|
for (const dir of ["context", ".", "generated"]) {
|
|
82169
82282
|
const abs = resolve14(base, dir);
|
|
82170
|
-
if (
|
|
82283
|
+
if (existsSync30(join33(abs, ".lattice", "manifest.json"))) return abs;
|
|
82171
82284
|
}
|
|
82172
82285
|
return resolve14(base, "context");
|
|
82173
82286
|
}
|
|
@@ -82176,7 +82289,7 @@ function friendlyConfigName(parsedName, configPath) {
|
|
|
82176
82289
|
return basename14(configPath).replace(/\.(ya?ml)$/, "");
|
|
82177
82290
|
}
|
|
82178
82291
|
function listConfigs(activeConfigPath) {
|
|
82179
|
-
const dir =
|
|
82292
|
+
const dir = dirname16(activeConfigPath);
|
|
82180
82293
|
const entries = [];
|
|
82181
82294
|
for (const fname of readdirSync9(dir)) {
|
|
82182
82295
|
if (!fname.endsWith(".yml") && !fname.endsWith(".yaml")) continue;
|
|
@@ -82205,17 +82318,17 @@ function listConfigs(activeConfigPath) {
|
|
|
82205
82318
|
return entries.sort((a6, b6) => a6.label.localeCompare(b6.label));
|
|
82206
82319
|
}
|
|
82207
82320
|
function createBlankConfig(activeConfigPath, dbName) {
|
|
82208
|
-
const dir =
|
|
82321
|
+
const dir = dirname16(activeConfigPath);
|
|
82209
82322
|
const slug = dbName.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
82210
82323
|
if (!slug) throw new Error("Workspace name must contain at least one alphanumeric character");
|
|
82211
82324
|
const configPath = join33(dir, `${slug}.config.yml`);
|
|
82212
|
-
if (
|
|
82325
|
+
if (existsSync30(configPath)) throw new Error(`Config already exists: ${slug}.config.yml`);
|
|
82213
82326
|
const yaml = `db: ./data/${slug}.db
|
|
82214
82327
|
|
|
82215
82328
|
entities: {}
|
|
82216
82329
|
`;
|
|
82217
82330
|
writeFileSync10(configPath, yaml, "utf8");
|
|
82218
|
-
|
|
82331
|
+
mkdirSync12(join33(dir, "data"), { recursive: true });
|
|
82219
82332
|
return configPath;
|
|
82220
82333
|
}
|
|
82221
82334
|
function sqliteFileForConfig(configPath) {
|
|
@@ -82224,18 +82337,18 @@ function sqliteFileForConfig(configPath) {
|
|
|
82224
82337
|
if (!raw) return null;
|
|
82225
82338
|
if (isPostgresUrl(raw) || raw.startsWith("${LATTICE_DB:")) return null;
|
|
82226
82339
|
if (raw === ":memory:" || raw.startsWith("file:")) return null;
|
|
82227
|
-
return resolve14(
|
|
82340
|
+
return resolve14(dirname16(configPath), raw);
|
|
82228
82341
|
}
|
|
82229
82342
|
function deleteDatabaseFiles(targetConfigPath) {
|
|
82230
82343
|
const sqliteFile = sqliteFileForConfig(targetConfigPath);
|
|
82231
82344
|
unlinkSync5(targetConfigPath);
|
|
82232
82345
|
let deletedDbFile = null;
|
|
82233
|
-
if (sqliteFile &&
|
|
82346
|
+
if (sqliteFile && existsSync30(sqliteFile)) {
|
|
82234
82347
|
unlinkSync5(sqliteFile);
|
|
82235
82348
|
deletedDbFile = sqliteFile;
|
|
82236
82349
|
for (const suffix of ["-wal", "-shm", "-journal"]) {
|
|
82237
82350
|
const sidecar = sqliteFile + suffix;
|
|
82238
|
-
if (
|
|
82351
|
+
if (existsSync30(sidecar)) unlinkSync5(sidecar);
|
|
82239
82352
|
}
|
|
82240
82353
|
}
|
|
82241
82354
|
return { deletedConfig: basename14(targetConfigPath), deletedDbFile };
|
|
@@ -82269,7 +82382,7 @@ async function handleDatabasesRoutes(req, res, ctx, deps) {
|
|
|
82269
82382
|
return true;
|
|
82270
82383
|
}
|
|
82271
82384
|
const newPath = resolve15(body.path);
|
|
82272
|
-
if (!
|
|
82385
|
+
if (!existsSync31(newPath)) {
|
|
82273
82386
|
sendJson(res, { error: `Config not found: ${newPath}` }, 400);
|
|
82274
82387
|
return true;
|
|
82275
82388
|
}
|
|
@@ -82375,8 +82488,8 @@ async function handleDatabasesRoutes(req, res, ctx, deps) {
|
|
|
82375
82488
|
// src/gui/server.ts
|
|
82376
82489
|
init_user_config();
|
|
82377
82490
|
function resolveDefaultGuiAssetsDir() {
|
|
82378
|
-
const bundleSibling = process.argv[1] ? resolve16(
|
|
82379
|
-
if (bundleSibling &&
|
|
82491
|
+
const bundleSibling = process.argv[1] ? resolve16(dirname17(process.argv[1]), "gui-assets") : null;
|
|
82492
|
+
if (bundleSibling && existsSync32(bundleSibling)) return bundleSibling;
|
|
82380
82493
|
return resolve16(process.cwd(), "dist", "gui-assets");
|
|
82381
82494
|
}
|
|
82382
82495
|
function sendText(res, body, status = 200, contentType = "text/plain; charset=utf-8") {
|
|
@@ -82433,7 +82546,7 @@ async function startGuiServer(options) {
|
|
|
82433
82546
|
const sessionId = crypto.randomUUID();
|
|
82434
82547
|
let updateService = null;
|
|
82435
82548
|
let activeRef = bootConfigPath && bootOutputDir ? await openConfig(bootConfigPath, bootOutputDir, autoRender, options.realtimeWatchdogMs) : null;
|
|
82436
|
-
const latticeRoot = (bootConfigPath ? findLatticeRoot(
|
|
82549
|
+
const latticeRoot = (bootConfigPath ? findLatticeRoot(dirname17(bootConfigPath)) : null) ?? (options.latticeRoot ? resolve16(options.latticeRoot) : null);
|
|
82437
82550
|
let currentWorkspaceId = null;
|
|
82438
82551
|
if (latticeRoot && bootConfigPath) {
|
|
82439
82552
|
const launched = listWorkspaces(latticeRoot).find(
|
|
@@ -82787,7 +82900,7 @@ async function startGuiServer(options) {
|
|
|
82787
82900
|
createJunction: (otherTable) => createFileJunction(active, otherTable, sessionId),
|
|
82788
82901
|
createEntity: (entity, columns) => createUserEntity(active, entity, columns, sessionId),
|
|
82789
82902
|
aggressiveness: getAggressiveness(),
|
|
82790
|
-
latticeRoot:
|
|
82903
|
+
latticeRoot: dirname17(active.configPath),
|
|
82791
82904
|
configPath: active.configPath,
|
|
82792
82905
|
outputDir: active.outputDir,
|
|
82793
82906
|
sessionId,
|
|
@@ -82812,7 +82925,7 @@ async function startGuiServer(options) {
|
|
|
82812
82925
|
createJunction: (otherTable) => createFileJunction(active, otherTable, sessionId),
|
|
82813
82926
|
createEntity: (entity, columns) => createUserEntity(active, entity, columns, sessionId),
|
|
82814
82927
|
aggressiveness: getAggressiveness(),
|
|
82815
|
-
latticeRoot:
|
|
82928
|
+
latticeRoot: dirname17(active.configPath),
|
|
82816
82929
|
configPath: active.configPath,
|
|
82817
82930
|
outputDir: active.outputDir,
|
|
82818
82931
|
sessionId,
|
|
@@ -82823,6 +82936,7 @@ async function startGuiServer(options) {
|
|
|
82823
82936
|
return await dispatchSourcesRoute(req2, res2, {
|
|
82824
82937
|
db: active.db,
|
|
82825
82938
|
ingestFile: (p3) => ingestLocalFile(ingestCtx, mctx, p3, false),
|
|
82939
|
+
configPath: active.configPath,
|
|
82826
82940
|
pathname,
|
|
82827
82941
|
method
|
|
82828
82942
|
});
|
|
@@ -82838,7 +82952,7 @@ async function startGuiServer(options) {
|
|
|
82838
82952
|
return await dispatchImportRoute(req2, res2, {
|
|
82839
82953
|
db: active.db,
|
|
82840
82954
|
configPath: active.configPath,
|
|
82841
|
-
latticeRoot:
|
|
82955
|
+
latticeRoot: dirname17(active.configPath),
|
|
82842
82956
|
validTables: active.validTables,
|
|
82843
82957
|
softDeletable: active.softDeletable
|
|
82844
82958
|
});
|
|
@@ -82867,7 +82981,7 @@ async function startGuiServer(options) {
|
|
|
82867
82981
|
if (!pathname.startsWith("/api/files/")) return false;
|
|
82868
82982
|
return await dispatchFilesRoute(req2, res2, {
|
|
82869
82983
|
db: active.db,
|
|
82870
|
-
latticeRoot:
|
|
82984
|
+
latticeRoot: dirname17(active.configPath),
|
|
82871
82985
|
configPath: active.configPath,
|
|
82872
82986
|
pathname,
|
|
82873
82987
|
method
|
|
@@ -83078,8 +83192,8 @@ ${e6.stack ?? ""}`
|
|
|
83078
83192
|
}
|
|
83079
83193
|
|
|
83080
83194
|
// src/cloud/file-source-key-store.ts
|
|
83081
|
-
import { readFileSync as readFileSync27, writeFileSync as writeFileSync11, existsSync as
|
|
83082
|
-
import { dirname as
|
|
83195
|
+
import { readFileSync as readFileSync27, writeFileSync as writeFileSync11, existsSync as existsSync33, mkdirSync as mkdirSync13, renameSync as renameSync5, chmodSync as chmodSync3 } from "fs";
|
|
83196
|
+
import { dirname as dirname18, resolve as resolve17 } from "path";
|
|
83083
83197
|
import { createCipheriv as createCipheriv3, createDecipheriv as createDecipheriv3, randomBytes as randomBytes9, scryptSync as scryptSync3 } from "crypto";
|
|
83084
83198
|
var ENC_HEADER = "LATTICE-KMS-v1\n";
|
|
83085
83199
|
var SCRYPT_N = 1 << 15;
|
|
@@ -83130,9 +83244,9 @@ var FileSourceKeyStore = class {
|
|
|
83130
83244
|
// ── internals ────────────────────────────────────────────────────────
|
|
83131
83245
|
load() {
|
|
83132
83246
|
const out = /* @__PURE__ */ new Map();
|
|
83133
|
-
if (!
|
|
83134
|
-
const dir =
|
|
83135
|
-
if (!
|
|
83247
|
+
if (!existsSync33(this.path)) {
|
|
83248
|
+
const dir = dirname18(this.path);
|
|
83249
|
+
if (!existsSync33(dir)) mkdirSync13(dir, { recursive: true, mode: 448 });
|
|
83136
83250
|
return out;
|
|
83137
83251
|
}
|
|
83138
83252
|
const raw = readFileSync27(this.path);
|
package/package.json
CHANGED