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/cli.js
CHANGED
|
@@ -1704,7 +1704,7 @@ async function registerPostgresPolyfills(run) {
|
|
|
1704
1704
|
await run(sql);
|
|
1705
1705
|
} catch (err) {
|
|
1706
1706
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1707
|
-
if (/permission denied/i.test(msg)) {
|
|
1707
|
+
if (/permission denied/i.test(msg) || /must be owner of/i.test(msg)) {
|
|
1708
1708
|
permissionDenied = true;
|
|
1709
1709
|
} else {
|
|
1710
1710
|
console.warn(`[PostgresAdapter] ${warn}`, msg);
|
|
@@ -2222,40 +2222,111 @@ var init_postgres = __esm({
|
|
|
2222
2222
|
SELECT doc::jsonb #>> string_to_array(regexp_replace(path, '^\\$\\.?', ''), '.')
|
|
2223
2223
|
$fn$;
|
|
2224
2224
|
END IF;
|
|
2225
|
+
END $do$;`
|
|
2226
|
+
},
|
|
2227
|
+
{
|
|
2228
|
+
warn: "could not register strftime format helper:",
|
|
2229
|
+
// Shared format translator (SQLite strftime tokens → to_char patterns), in UTC.
|
|
2230
|
+
// Factored out so the 2-arg and 3-arg strftime overloads share one definition.
|
|
2231
|
+
// Each polyfill below is CREATE OR REPLACE (so an existing cloud's prior copy is
|
|
2232
|
+
// UPGRADED when the owner opens), wrapped in a DO block whose EXCEPTION swallows a
|
|
2233
|
+
// scoped member's insufficient-privilege failure IN A SUBTRANSACTION — so a member
|
|
2234
|
+
// that runs registration inside a transaction is NOT aborted. ("must be owner of
|
|
2235
|
+
// function", raised when a non-owner CREATE OR REPLACEs another role's function, is
|
|
2236
|
+
// SQLSTATE 42501 / `insufficient_privilege`, same class as "permission denied".) The
|
|
2237
|
+
// owner already owns + replaced the function; the member simply uses the owner's
|
|
2238
|
+
// copy — and `ownPolyfillsByGroup` re-owns the whole set to the member group so any
|
|
2239
|
+
// member can replace them on a later upgrade.
|
|
2240
|
+
sql: `DO $do$ BEGIN
|
|
2241
|
+
CREATE OR REPLACE FUNCTION __lattice_strftime_fmt(ts timestamptz, format text)
|
|
2242
|
+
RETURNS text
|
|
2243
|
+
LANGUAGE sql
|
|
2244
|
+
IMMUTABLE
|
|
2245
|
+
AS $fn$
|
|
2246
|
+
SELECT to_char(
|
|
2247
|
+
ts AT TIME ZONE 'UTC',
|
|
2248
|
+
replace(replace(replace(replace(replace(replace(replace(replace(
|
|
2249
|
+
format,
|
|
2250
|
+
'%Y', 'YYYY'),
|
|
2251
|
+
'%m', 'MM'),
|
|
2252
|
+
'%d', 'DD'),
|
|
2253
|
+
'%H', 'HH24'),
|
|
2254
|
+
'%M', 'MI'),
|
|
2255
|
+
'%S', 'SS'),
|
|
2256
|
+
'%f', 'MS'),
|
|
2257
|
+
'T', '"T"')
|
|
2258
|
+
);
|
|
2259
|
+
$fn$;
|
|
2260
|
+
EXCEPTION WHEN insufficient_privilege THEN NULL;
|
|
2225
2261
|
END $do$;`
|
|
2226
2262
|
},
|
|
2227
2263
|
{
|
|
2228
2264
|
warn: "could not register strftime polyfill:",
|
|
2265
|
+
// The pre-4.3.3 strftime cast the modifier straight to timestamptz and threw
|
|
2266
|
+
// `invalid input syntax for type timestamp with time zone: ""` on a legacy ''
|
|
2267
|
+
// value (3.x stored nullable TEXT timestamps as ''), bricking the whole workspace
|
|
2268
|
+
// open. Now an empty, whitespace, or unparseable time string returns NULL (SQLite's
|
|
2269
|
+
// strftime semantics) instead of aborting the query.
|
|
2229
2270
|
sql: `DO $do$ BEGIN
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2271
|
+
CREATE OR REPLACE FUNCTION strftime(format text, modifier text)
|
|
2272
|
+
RETURNS text
|
|
2273
|
+
LANGUAGE plpgsql
|
|
2274
|
+
IMMUTABLE
|
|
2275
|
+
AS $fn$
|
|
2276
|
+
DECLARE ts timestamptz;
|
|
2277
|
+
BEGIN
|
|
2278
|
+
IF modifier = 'now' THEN
|
|
2279
|
+
ts := now();
|
|
2280
|
+
ELSIF NULLIF(btrim(modifier), '') IS NULL THEN
|
|
2281
|
+
RETURN NULL;
|
|
2282
|
+
ELSE
|
|
2283
|
+
BEGIN
|
|
2241
2284
|
ts := modifier::timestamptz;
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2285
|
+
EXCEPTION WHEN others THEN
|
|
2286
|
+
RETURN NULL;
|
|
2287
|
+
END;
|
|
2288
|
+
END IF;
|
|
2289
|
+
RETURN __lattice_strftime_fmt(ts, format);
|
|
2290
|
+
END;
|
|
2291
|
+
$fn$;
|
|
2292
|
+
EXCEPTION WHEN insufficient_privilege THEN NULL;
|
|
2293
|
+
END $do$;`
|
|
2294
|
+
},
|
|
2295
|
+
{
|
|
2296
|
+
warn: "could not register 3-arg strftime polyfill:",
|
|
2297
|
+
// SQLite's 3-arg strftime(format, timestring, modifier) — used by the changelog
|
|
2298
|
+
// retention prune (e.g. strftime('%Y-...','now','-30 days')). Postgres had no
|
|
2299
|
+
// 3-arg overload, so that prune threw `function strftime(...) does not exist` on
|
|
2300
|
+
// every PG cloud with retention. Resolve the base time, apply the modifier as an
|
|
2301
|
+
// interval, then reuse the shared formatter. Empty/invalid → NULL, never throws.
|
|
2302
|
+
sql: `DO $do$ BEGIN
|
|
2303
|
+
CREATE OR REPLACE FUNCTION strftime(format text, timestring text, modifier text)
|
|
2304
|
+
RETURNS text
|
|
2305
|
+
LANGUAGE plpgsql
|
|
2306
|
+
IMMUTABLE
|
|
2307
|
+
AS $fn$
|
|
2308
|
+
DECLARE ts timestamptz;
|
|
2309
|
+
BEGIN
|
|
2310
|
+
IF timestring = 'now' THEN
|
|
2311
|
+
ts := now();
|
|
2312
|
+
ELSIF NULLIF(btrim(timestring), '') IS NULL THEN
|
|
2313
|
+
RETURN NULL;
|
|
2314
|
+
ELSE
|
|
2315
|
+
BEGIN
|
|
2316
|
+
ts := timestring::timestamptz;
|
|
2317
|
+
EXCEPTION WHEN others THEN
|
|
2318
|
+
RETURN NULL;
|
|
2319
|
+
END;
|
|
2320
|
+
END IF;
|
|
2321
|
+
BEGIN
|
|
2322
|
+
ts := ts + modifier::interval;
|
|
2323
|
+
EXCEPTION WHEN others THEN
|
|
2324
|
+
RETURN NULL;
|
|
2256
2325
|
END;
|
|
2257
|
-
|
|
2258
|
-
|
|
2326
|
+
RETURN __lattice_strftime_fmt(ts, format);
|
|
2327
|
+
END;
|
|
2328
|
+
$fn$;
|
|
2329
|
+
EXCEPTION WHEN insufficient_privilege THEN NULL;
|
|
2259
2330
|
END $do$;`
|
|
2260
2331
|
}
|
|
2261
2332
|
];
|
|
@@ -9180,7 +9251,12 @@ CREATE TRIGGER "${trg}" AFTER INSERT OR UPDATE OR DELETE ON ${q3}
|
|
|
9180
9251
|
async function ownPolyfillsByGroup(db) {
|
|
9181
9252
|
if (!isPg(db)) return;
|
|
9182
9253
|
const group3 = await memberGroupFor(db);
|
|
9183
|
-
for (const sig of [
|
|
9254
|
+
for (const sig of [
|
|
9255
|
+
"json_extract(text, text)",
|
|
9256
|
+
"strftime(text, text)",
|
|
9257
|
+
"strftime(text, text, text)",
|
|
9258
|
+
"__lattice_strftime_fmt(timestamptz, text)"
|
|
9259
|
+
]) {
|
|
9184
9260
|
try {
|
|
9185
9261
|
const reg = await getAsyncOrSync(db.adapter, `SELECT to_regprocedure($1) AS reg`, [sig]);
|
|
9186
9262
|
if (reg?.reg == null) continue;
|
|
@@ -29084,12 +29160,12 @@ var getNodeModulesParentDirs;
|
|
|
29084
29160
|
var init_getNodeModulesParentDirs = __esm({
|
|
29085
29161
|
"node_modules/@aws-sdk/core/dist-es/submodules/client/util-user-agent-node/getNodeModulesParentDirs.js"() {
|
|
29086
29162
|
"use strict";
|
|
29087
|
-
getNodeModulesParentDirs = (
|
|
29163
|
+
getNodeModulesParentDirs = (dirname20) => {
|
|
29088
29164
|
const cwd = process.cwd();
|
|
29089
|
-
if (!
|
|
29165
|
+
if (!dirname20) {
|
|
29090
29166
|
return [cwd];
|
|
29091
29167
|
}
|
|
29092
|
-
const normalizedPath = normalize(
|
|
29168
|
+
const normalizedPath = normalize(dirname20);
|
|
29093
29169
|
const parts = normalizedPath.split(sep6);
|
|
29094
29170
|
const nodeModulesIndex = parts.indexOf("node_modules");
|
|
29095
29171
|
const parentDir = nodeModulesIndex !== -1 ? parts.slice(0, nodeModulesIndex).join(sep6) : normalizedPath;
|
|
@@ -29167,8 +29243,8 @@ var init_getTypeScriptUserAgentPair = __esm({
|
|
|
29167
29243
|
tscVersion = null;
|
|
29168
29244
|
return void 0;
|
|
29169
29245
|
}
|
|
29170
|
-
const
|
|
29171
|
-
const nodeModulesParentDirs = getNodeModulesParentDirs(
|
|
29246
|
+
const dirname20 = typeof __dirname !== "undefined" ? __dirname : void 0;
|
|
29247
|
+
const nodeModulesParentDirs = getNodeModulesParentDirs(dirname20);
|
|
29172
29248
|
let versionFromApp;
|
|
29173
29249
|
for (const nodeModulesParentDir of nodeModulesParentDirs) {
|
|
29174
29250
|
try {
|
|
@@ -57954,8 +58030,8 @@ var init_dist_es22 = __esm({
|
|
|
57954
58030
|
});
|
|
57955
58031
|
|
|
57956
58032
|
// src/cli.ts
|
|
57957
|
-
import { resolve as resolve17, dirname as
|
|
57958
|
-
import { readFileSync as readFileSync26, existsSync as
|
|
58033
|
+
import { resolve as resolve17, dirname as dirname19 } from "path";
|
|
58034
|
+
import { readFileSync as readFileSync26, existsSync as existsSync34 } from "fs";
|
|
57959
58035
|
import { execSync } from "child_process";
|
|
57960
58036
|
import { parse as parse3 } from "yaml";
|
|
57961
58037
|
|
|
@@ -58222,9 +58298,9 @@ function installLatest(ctx, version, opts = {}) {
|
|
|
58222
58298
|
init_http();
|
|
58223
58299
|
import { createServer } from "http";
|
|
58224
58300
|
import { spawn as spawn2 } from "child_process";
|
|
58225
|
-
import { existsSync as
|
|
58301
|
+
import { existsSync as existsSync33 } from "fs";
|
|
58226
58302
|
import { WebSocketServer, WebSocket } from "ws";
|
|
58227
|
-
import { dirname as
|
|
58303
|
+
import { dirname as dirname18, resolve as resolve16 } from "path";
|
|
58228
58304
|
|
|
58229
58305
|
// src/gui/active-db.ts
|
|
58230
58306
|
init_adapter();
|
|
@@ -76758,8 +76834,16 @@ async function dispatchIngestRoute(req, res, ctx) {
|
|
|
76758
76834
|
|
|
76759
76835
|
// src/gui/sources-routes.ts
|
|
76760
76836
|
init_user_config();
|
|
76761
|
-
import {
|
|
76762
|
-
|
|
76837
|
+
import {
|
|
76838
|
+
statSync as statSync9,
|
|
76839
|
+
readdirSync as readdirSync8,
|
|
76840
|
+
readFileSync as readFileSync23,
|
|
76841
|
+
writeFileSync as writeFileSync10,
|
|
76842
|
+
existsSync as existsSync27,
|
|
76843
|
+
mkdirSync as mkdirSync12,
|
|
76844
|
+
rmSync
|
|
76845
|
+
} from "fs";
|
|
76846
|
+
import { resolve as resolve12, join as join29, basename as basename12, sep as sep8, dirname as dirname16 } from "path";
|
|
76763
76847
|
import { execFile } from "child_process";
|
|
76764
76848
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
76765
76849
|
init_http();
|
|
@@ -76767,20 +76851,49 @@ var MAX_LIST_ENTRIES = 2e3;
|
|
|
76767
76851
|
var MAX_INGEST_FILES = 500;
|
|
76768
76852
|
var MAX_INGEST_DEPTH = 8;
|
|
76769
76853
|
var SKIP_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".DS_Store", "__pycache__", ".venv", "venv"]);
|
|
76770
|
-
function rootsFile() {
|
|
76771
|
-
return join29(
|
|
76854
|
+
function rootsFile(configPath) {
|
|
76855
|
+
return join29(dirname16(configPath), "sources.json");
|
|
76856
|
+
}
|
|
76857
|
+
function migrateGlobalRootsIfNeeded(configPath) {
|
|
76858
|
+
const wsFile = rootsFile(configPath);
|
|
76859
|
+
if (existsSync27(wsFile)) return;
|
|
76860
|
+
const globalFile = join29(configDir(), "sources.json");
|
|
76861
|
+
if (globalFile === wsFile || !existsSync27(globalFile)) return;
|
|
76862
|
+
let roots;
|
|
76863
|
+
try {
|
|
76864
|
+
const parsed = JSON.parse(readFileSync23(globalFile, "utf8"));
|
|
76865
|
+
if (!Array.isArray(parsed.roots) || parsed.roots.length === 0) return;
|
|
76866
|
+
roots = parsed.roots;
|
|
76867
|
+
} catch {
|
|
76868
|
+
return;
|
|
76869
|
+
}
|
|
76870
|
+
try {
|
|
76871
|
+
mkdirSync12(dirname16(wsFile), { recursive: true });
|
|
76872
|
+
writeFileSync10(wsFile, JSON.stringify({ roots }, null, 2), "utf8");
|
|
76873
|
+
} catch {
|
|
76874
|
+
return;
|
|
76875
|
+
}
|
|
76876
|
+
try {
|
|
76877
|
+
rmSync(globalFile, { force: true });
|
|
76878
|
+
} catch {
|
|
76879
|
+
try {
|
|
76880
|
+
writeFileSync10(globalFile, JSON.stringify({ roots: [] }, null, 2), "utf8");
|
|
76881
|
+
} catch {
|
|
76882
|
+
}
|
|
76883
|
+
}
|
|
76772
76884
|
}
|
|
76773
|
-
function readRoots() {
|
|
76885
|
+
function readRoots(configPath) {
|
|
76886
|
+
migrateGlobalRootsIfNeeded(configPath);
|
|
76774
76887
|
try {
|
|
76775
|
-
const raw = readFileSync23(rootsFile(), "utf8");
|
|
76888
|
+
const raw = readFileSync23(rootsFile(configPath), "utf8");
|
|
76776
76889
|
const parsed = JSON.parse(raw);
|
|
76777
76890
|
return Array.isArray(parsed.roots) ? parsed.roots : [];
|
|
76778
76891
|
} catch {
|
|
76779
76892
|
return [];
|
|
76780
76893
|
}
|
|
76781
76894
|
}
|
|
76782
|
-
function writeRoots(roots) {
|
|
76783
|
-
writeFileSync10(rootsFile(), JSON.stringify({ roots }, null, 2), "utf8");
|
|
76895
|
+
function writeRoots(configPath, roots) {
|
|
76896
|
+
writeFileSync10(rootsFile(configPath), JSON.stringify({ roots }, null, 2), "utf8");
|
|
76784
76897
|
}
|
|
76785
76898
|
function safeResolveInside2(target, roots) {
|
|
76786
76899
|
const abs = resolve12(target);
|
|
@@ -76870,14 +76983,14 @@ async function ingestFolder(abs, ingestFile) {
|
|
|
76870
76983
|
return { ingested, skipped };
|
|
76871
76984
|
}
|
|
76872
76985
|
async function dispatchSourcesRoute(req, res, deps) {
|
|
76873
|
-
const { pathname, method, ingestFile } = deps;
|
|
76986
|
+
const { pathname, method, ingestFile, configPath } = deps;
|
|
76874
76987
|
if (!pathname.startsWith("/api/sources/")) return false;
|
|
76875
76988
|
if (!localFileOpenEnabled()) {
|
|
76876
76989
|
sendJson(res, { enabled: false });
|
|
76877
76990
|
return true;
|
|
76878
76991
|
}
|
|
76879
76992
|
if (pathname === "/api/sources/roots" && method === "GET") {
|
|
76880
|
-
sendJson(res, { enabled: true, roots: readRoots() });
|
|
76993
|
+
sendJson(res, { enabled: true, roots: readRoots(configPath) });
|
|
76881
76994
|
return true;
|
|
76882
76995
|
}
|
|
76883
76996
|
if (pathname === "/api/sources/roots" && method === "POST") {
|
|
@@ -76903,12 +77016,12 @@ async function dispatchSourcesRoute(req, res, deps) {
|
|
|
76903
77016
|
sendJson(res, { error: `path not found: ${abs}` }, 400);
|
|
76904
77017
|
return true;
|
|
76905
77018
|
}
|
|
76906
|
-
const roots = readRoots();
|
|
77019
|
+
const roots = readRoots(configPath);
|
|
76907
77020
|
let root6 = roots.find((r6) => resolve12(r6.path) === abs);
|
|
76908
77021
|
if (!root6) {
|
|
76909
77022
|
root6 = { id: randomUUID4(), path: abs, kind, name: basename12(abs) || abs };
|
|
76910
77023
|
roots.push(root6);
|
|
76911
|
-
writeRoots(roots);
|
|
77024
|
+
writeRoots(configPath, roots);
|
|
76912
77025
|
}
|
|
76913
77026
|
let result;
|
|
76914
77027
|
if (kind === "folder") result = await ingestFolder(abs, ingestFile);
|
|
@@ -76919,8 +77032,8 @@ async function dispatchSourcesRoute(req, res, deps) {
|
|
|
76919
77032
|
const delMatch = /^\/api\/sources\/roots\/([^/]+)$/.exec(pathname);
|
|
76920
77033
|
if (delMatch && method === "DELETE") {
|
|
76921
77034
|
const id = decodeURIComponent(delMatch[1] ?? "");
|
|
76922
|
-
const roots = readRoots().filter((r6) => r6.id !== id);
|
|
76923
|
-
writeRoots(roots);
|
|
77035
|
+
const roots = readRoots(configPath).filter((r6) => r6.id !== id);
|
|
77036
|
+
writeRoots(configPath, roots);
|
|
76924
77037
|
sendJson(res, { ok: true });
|
|
76925
77038
|
return true;
|
|
76926
77039
|
}
|
|
@@ -76938,7 +77051,7 @@ async function dispatchSourcesRoute(req, res, deps) {
|
|
|
76938
77051
|
sendJson(res, { error: "path is required" }, 400);
|
|
76939
77052
|
return true;
|
|
76940
77053
|
}
|
|
76941
|
-
const abs = safeResolveInside2(target, readRoots());
|
|
77054
|
+
const abs = safeResolveInside2(target, readRoots(configPath));
|
|
76942
77055
|
if (!abs) {
|
|
76943
77056
|
sendJson(res, { error: "path is outside any registered source root" }, 403);
|
|
76944
77057
|
return true;
|
|
@@ -76953,7 +77066,7 @@ async function dispatchSourcesRoute(req, res, deps) {
|
|
|
76953
77066
|
if (pathname === "/api/sources/ingest-folder" && method === "POST") {
|
|
76954
77067
|
const body = await readJson(req).catch(() => ({}));
|
|
76955
77068
|
const target = typeof body.path === "string" ? body.path : "";
|
|
76956
|
-
const abs = safeResolveInside2(target, readRoots());
|
|
77069
|
+
const abs = safeResolveInside2(target, readRoots(configPath));
|
|
76957
77070
|
if (!abs) {
|
|
76958
77071
|
sendJson(res, { error: "path is outside any registered source root" }, 403);
|
|
76959
77072
|
return true;
|
|
@@ -76967,7 +77080,7 @@ async function dispatchSourcesRoute(req, res, deps) {
|
|
|
76967
77080
|
// src/gui/import-routes.ts
|
|
76968
77081
|
init_adapter();
|
|
76969
77082
|
init_http();
|
|
76970
|
-
import { existsSync as
|
|
77083
|
+
import { existsSync as existsSync28, readFileSync as readFileSync24, statSync as statSync10 } from "fs";
|
|
76971
77084
|
import { isAbsolute as isAbsolute4, join as join30 } from "path";
|
|
76972
77085
|
init_native_entities();
|
|
76973
77086
|
function badRequest(message) {
|
|
@@ -77001,7 +77114,7 @@ async function readImportSourceFromFile(db, fileId, latticeRoot) {
|
|
|
77001
77114
|
);
|
|
77002
77115
|
if (!row) throw badRequest("Unknown import file: " + fileId);
|
|
77003
77116
|
const path3 = localPathOf2(row, latticeRoot);
|
|
77004
|
-
if (!path3 || !
|
|
77117
|
+
if (!path3 || !existsSync28(path3)) {
|
|
77005
77118
|
throw badRequest("The import file\u2019s bytes are not available locally.");
|
|
77006
77119
|
}
|
|
77007
77120
|
const sizeBytes = statSync10(path3).size;
|
|
@@ -79142,7 +79255,7 @@ init_dispatch();
|
|
|
79142
79255
|
init_column_descriptions();
|
|
79143
79256
|
init_mutations();
|
|
79144
79257
|
init_native_entities();
|
|
79145
|
-
import { createReadStream as createReadStream3, existsSync as
|
|
79258
|
+
import { createReadStream as createReadStream3, existsSync as existsSync29, realpathSync as realpathSync2, statSync as statSync11 } from "fs";
|
|
79146
79259
|
import { extname as extname3, join as join31, normalize as normalize2, sep as sep9 } from "path";
|
|
79147
79260
|
|
|
79148
79261
|
// src/gui/count-many.ts
|
|
@@ -79350,7 +79463,7 @@ async function handleReadRoutes(req, res, ctx, deps) {
|
|
|
79350
79463
|
const base = deps.guiAssetsDir.replace(/[/\\]+$/, "");
|
|
79351
79464
|
const target = normalize2(join31(base, rel));
|
|
79352
79465
|
const within = target === base || target.startsWith(base + sep9);
|
|
79353
|
-
if (!within || !
|
|
79466
|
+
if (!within || !existsSync29(target) || !statSync11(target).isFile()) {
|
|
79354
79467
|
sendJson(res, { error: "asset not found" }, 404);
|
|
79355
79468
|
return true;
|
|
79356
79469
|
}
|
|
@@ -80558,16 +80671,16 @@ async function handleHistoryRoutes(req, res, ctx, deps) {
|
|
|
80558
80671
|
// src/gui/workspaces-routes.ts
|
|
80559
80672
|
init_http();
|
|
80560
80673
|
import { resolve as resolve13 } from "path";
|
|
80561
|
-
import { existsSync as
|
|
80674
|
+
import { existsSync as existsSync30, rmSync as rmSync2 } from "fs";
|
|
80562
80675
|
init_workspace();
|
|
80563
80676
|
init_lattice_root();
|
|
80564
80677
|
init_user_config();
|
|
80565
80678
|
function cleanupWorkspaceFiles(root6, ws) {
|
|
80566
80679
|
if (!ws.configPath && ws.kind === "local") {
|
|
80567
|
-
|
|
80680
|
+
rmSync2(workspaceDir(root6, ws.dir), { recursive: true, force: true });
|
|
80568
80681
|
} else if (ws.kind === "cloud") {
|
|
80569
|
-
if (ws.configPath &&
|
|
80570
|
-
|
|
80682
|
+
if (ws.configPath && existsSync30(ws.configPath)) {
|
|
80683
|
+
rmSync2(ws.configPath, { force: true });
|
|
80571
80684
|
}
|
|
80572
80685
|
const labelMatch = /^\$\{LATTICE_DB:([A-Za-z0-9._-]+)\}$/.exec(ws.db.trim());
|
|
80573
80686
|
const label = labelMatch?.[1];
|
|
@@ -80770,15 +80883,15 @@ async function handleWorkspacesRoutes(req, res, ctx, deps) {
|
|
|
80770
80883
|
// src/gui/databases-routes.ts
|
|
80771
80884
|
init_http();
|
|
80772
80885
|
import { basename as basename14, resolve as resolve15 } from "path";
|
|
80773
|
-
import { existsSync as
|
|
80886
|
+
import { existsSync as existsSync32 } from "fs";
|
|
80774
80887
|
init_parser();
|
|
80775
80888
|
|
|
80776
80889
|
// src/gui/config-paths.ts
|
|
80777
80890
|
init_parser();
|
|
80778
|
-
import { basename as basename13, dirname as
|
|
80891
|
+
import { basename as basename13, dirname as dirname17, join as join32, resolve as resolve14 } from "path";
|
|
80779
80892
|
import {
|
|
80780
|
-
existsSync as
|
|
80781
|
-
mkdirSync as
|
|
80893
|
+
existsSync as existsSync31,
|
|
80894
|
+
mkdirSync as mkdirSync13,
|
|
80782
80895
|
readFileSync as readFileSync25,
|
|
80783
80896
|
readdirSync as readdirSync9,
|
|
80784
80897
|
unlinkSync as unlinkSync5,
|
|
@@ -80786,10 +80899,10 @@ import {
|
|
|
80786
80899
|
} from "fs";
|
|
80787
80900
|
import { parseDocument as parseDocument7 } from "yaml";
|
|
80788
80901
|
function resolveOutputDirForConfig(configPath) {
|
|
80789
|
-
const base =
|
|
80902
|
+
const base = dirname17(resolve14(configPath));
|
|
80790
80903
|
for (const dir of ["context", ".", "generated"]) {
|
|
80791
80904
|
const abs = resolve14(base, dir);
|
|
80792
|
-
if (
|
|
80905
|
+
if (existsSync31(join32(abs, ".lattice", "manifest.json"))) return abs;
|
|
80793
80906
|
}
|
|
80794
80907
|
return resolve14(base, "context");
|
|
80795
80908
|
}
|
|
@@ -80798,7 +80911,7 @@ function friendlyConfigName(parsedName, configPath) {
|
|
|
80798
80911
|
return basename13(configPath).replace(/\.(ya?ml)$/, "");
|
|
80799
80912
|
}
|
|
80800
80913
|
function listConfigs(activeConfigPath) {
|
|
80801
|
-
const dir =
|
|
80914
|
+
const dir = dirname17(activeConfigPath);
|
|
80802
80915
|
const entries = [];
|
|
80803
80916
|
for (const fname of readdirSync9(dir)) {
|
|
80804
80917
|
if (!fname.endsWith(".yml") && !fname.endsWith(".yaml")) continue;
|
|
@@ -80827,17 +80940,17 @@ function listConfigs(activeConfigPath) {
|
|
|
80827
80940
|
return entries.sort((a6, b6) => a6.label.localeCompare(b6.label));
|
|
80828
80941
|
}
|
|
80829
80942
|
function createBlankConfig(activeConfigPath, dbName) {
|
|
80830
|
-
const dir =
|
|
80943
|
+
const dir = dirname17(activeConfigPath);
|
|
80831
80944
|
const slug = dbName.toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
80832
80945
|
if (!slug) throw new Error("Workspace name must contain at least one alphanumeric character");
|
|
80833
80946
|
const configPath = join32(dir, `${slug}.config.yml`);
|
|
80834
|
-
if (
|
|
80947
|
+
if (existsSync31(configPath)) throw new Error(`Config already exists: ${slug}.config.yml`);
|
|
80835
80948
|
const yaml = `db: ./data/${slug}.db
|
|
80836
80949
|
|
|
80837
80950
|
entities: {}
|
|
80838
80951
|
`;
|
|
80839
80952
|
writeFileSync11(configPath, yaml, "utf8");
|
|
80840
|
-
|
|
80953
|
+
mkdirSync13(join32(dir, "data"), { recursive: true });
|
|
80841
80954
|
return configPath;
|
|
80842
80955
|
}
|
|
80843
80956
|
function sqliteFileForConfig(configPath) {
|
|
@@ -80846,18 +80959,18 @@ function sqliteFileForConfig(configPath) {
|
|
|
80846
80959
|
if (!raw) return null;
|
|
80847
80960
|
if (isPostgresUrl(raw) || raw.startsWith("${LATTICE_DB:")) return null;
|
|
80848
80961
|
if (raw === ":memory:" || raw.startsWith("file:")) return null;
|
|
80849
|
-
return resolve14(
|
|
80962
|
+
return resolve14(dirname17(configPath), raw);
|
|
80850
80963
|
}
|
|
80851
80964
|
function deleteDatabaseFiles(targetConfigPath) {
|
|
80852
80965
|
const sqliteFile = sqliteFileForConfig(targetConfigPath);
|
|
80853
80966
|
unlinkSync5(targetConfigPath);
|
|
80854
80967
|
let deletedDbFile = null;
|
|
80855
|
-
if (sqliteFile &&
|
|
80968
|
+
if (sqliteFile && existsSync31(sqliteFile)) {
|
|
80856
80969
|
unlinkSync5(sqliteFile);
|
|
80857
80970
|
deletedDbFile = sqliteFile;
|
|
80858
80971
|
for (const suffix of ["-wal", "-shm", "-journal"]) {
|
|
80859
80972
|
const sidecar = sqliteFile + suffix;
|
|
80860
|
-
if (
|
|
80973
|
+
if (existsSync31(sidecar)) unlinkSync5(sidecar);
|
|
80861
80974
|
}
|
|
80862
80975
|
}
|
|
80863
80976
|
return { deletedConfig: basename13(targetConfigPath), deletedDbFile };
|
|
@@ -80891,7 +81004,7 @@ async function handleDatabasesRoutes(req, res, ctx, deps) {
|
|
|
80891
81004
|
return true;
|
|
80892
81005
|
}
|
|
80893
81006
|
const newPath = resolve15(body.path);
|
|
80894
|
-
if (!
|
|
81007
|
+
if (!existsSync32(newPath)) {
|
|
80895
81008
|
sendJson(res, { error: `Config not found: ${newPath}` }, 400);
|
|
80896
81009
|
return true;
|
|
80897
81010
|
}
|
|
@@ -80997,8 +81110,8 @@ async function handleDatabasesRoutes(req, res, ctx, deps) {
|
|
|
80997
81110
|
// src/gui/server.ts
|
|
80998
81111
|
init_user_config();
|
|
80999
81112
|
function resolveDefaultGuiAssetsDir() {
|
|
81000
|
-
const bundleSibling = process.argv[1] ? resolve16(
|
|
81001
|
-
if (bundleSibling &&
|
|
81113
|
+
const bundleSibling = process.argv[1] ? resolve16(dirname18(process.argv[1]), "gui-assets") : null;
|
|
81114
|
+
if (bundleSibling && existsSync33(bundleSibling)) return bundleSibling;
|
|
81002
81115
|
return resolve16(process.cwd(), "dist", "gui-assets");
|
|
81003
81116
|
}
|
|
81004
81117
|
function sendText(res, body, status = 200, contentType = "text/plain; charset=utf-8") {
|
|
@@ -81055,7 +81168,7 @@ async function startGuiServer(options) {
|
|
|
81055
81168
|
const sessionId = crypto.randomUUID();
|
|
81056
81169
|
let updateService = null;
|
|
81057
81170
|
let activeRef = bootConfigPath && bootOutputDir ? await openConfig(bootConfigPath, bootOutputDir, autoRender, options.realtimeWatchdogMs) : null;
|
|
81058
|
-
const latticeRoot = (bootConfigPath ? findLatticeRoot(
|
|
81171
|
+
const latticeRoot = (bootConfigPath ? findLatticeRoot(dirname18(bootConfigPath)) : null) ?? (options.latticeRoot ? resolve16(options.latticeRoot) : null);
|
|
81059
81172
|
let currentWorkspaceId = null;
|
|
81060
81173
|
if (latticeRoot && bootConfigPath) {
|
|
81061
81174
|
const launched = listWorkspaces(latticeRoot).find(
|
|
@@ -81409,7 +81522,7 @@ async function startGuiServer(options) {
|
|
|
81409
81522
|
createJunction: (otherTable) => createFileJunction(active, otherTable, sessionId),
|
|
81410
81523
|
createEntity: (entity, columns) => createUserEntity(active, entity, columns, sessionId),
|
|
81411
81524
|
aggressiveness: getAggressiveness(),
|
|
81412
|
-
latticeRoot:
|
|
81525
|
+
latticeRoot: dirname18(active.configPath),
|
|
81413
81526
|
configPath: active.configPath,
|
|
81414
81527
|
outputDir: active.outputDir,
|
|
81415
81528
|
sessionId,
|
|
@@ -81434,7 +81547,7 @@ async function startGuiServer(options) {
|
|
|
81434
81547
|
createJunction: (otherTable) => createFileJunction(active, otherTable, sessionId),
|
|
81435
81548
|
createEntity: (entity, columns) => createUserEntity(active, entity, columns, sessionId),
|
|
81436
81549
|
aggressiveness: getAggressiveness(),
|
|
81437
|
-
latticeRoot:
|
|
81550
|
+
latticeRoot: dirname18(active.configPath),
|
|
81438
81551
|
configPath: active.configPath,
|
|
81439
81552
|
outputDir: active.outputDir,
|
|
81440
81553
|
sessionId,
|
|
@@ -81445,6 +81558,7 @@ async function startGuiServer(options) {
|
|
|
81445
81558
|
return await dispatchSourcesRoute(req2, res2, {
|
|
81446
81559
|
db: active.db,
|
|
81447
81560
|
ingestFile: (p3) => ingestLocalFile(ingestCtx, mctx, p3, false),
|
|
81561
|
+
configPath: active.configPath,
|
|
81448
81562
|
pathname,
|
|
81449
81563
|
method
|
|
81450
81564
|
});
|
|
@@ -81460,7 +81574,7 @@ async function startGuiServer(options) {
|
|
|
81460
81574
|
return await dispatchImportRoute(req2, res2, {
|
|
81461
81575
|
db: active.db,
|
|
81462
81576
|
configPath: active.configPath,
|
|
81463
|
-
latticeRoot:
|
|
81577
|
+
latticeRoot: dirname18(active.configPath),
|
|
81464
81578
|
validTables: active.validTables,
|
|
81465
81579
|
softDeletable: active.softDeletable
|
|
81466
81580
|
});
|
|
@@ -81489,7 +81603,7 @@ async function startGuiServer(options) {
|
|
|
81489
81603
|
if (!pathname.startsWith("/api/files/")) return false;
|
|
81490
81604
|
return await dispatchFilesRoute(req2, res2, {
|
|
81491
81605
|
db: active.db,
|
|
81492
|
-
latticeRoot:
|
|
81606
|
+
latticeRoot: dirname18(active.configPath),
|
|
81493
81607
|
configPath: active.configPath,
|
|
81494
81608
|
pathname,
|
|
81495
81609
|
method
|
|
@@ -81982,7 +82096,7 @@ function printHelp() {
|
|
|
81982
82096
|
);
|
|
81983
82097
|
}
|
|
81984
82098
|
function getVersion() {
|
|
81985
|
-
if (true) return "4.3.
|
|
82099
|
+
if (true) return "4.3.3";
|
|
81986
82100
|
try {
|
|
81987
82101
|
const pkgPath = new URL("../package.json", import.meta.url).pathname;
|
|
81988
82102
|
const pkg = JSON.parse(readFileSync26(pkgPath, "utf-8"));
|
|
@@ -81996,7 +82110,7 @@ function printVersion() {
|
|
|
81996
82110
|
}
|
|
81997
82111
|
function getGuiAssetsDir() {
|
|
81998
82112
|
const fromBundle = new URL("./gui-assets", import.meta.url).pathname;
|
|
81999
|
-
if (
|
|
82113
|
+
if (existsSync34(fromBundle)) return fromBundle;
|
|
82000
82114
|
return new URL("../dist/gui-assets", import.meta.url).pathname;
|
|
82001
82115
|
}
|
|
82002
82116
|
async function runUpdate() {
|
|
@@ -82040,7 +82154,7 @@ function runGenerate(args) {
|
|
|
82040
82154
|
console.error('Error: config must have an "entities" key');
|
|
82041
82155
|
process.exit(1);
|
|
82042
82156
|
}
|
|
82043
|
-
const configDir2 =
|
|
82157
|
+
const configDir2 = dirname19(configPath);
|
|
82044
82158
|
const outDir = resolve17(args.out);
|
|
82045
82159
|
try {
|
|
82046
82160
|
const result = generateAll({ config, configDir: configDir2, outDir, scaffold: args.scaffold });
|