latticesql 3.4.4 → 3.4.5
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 +122 -33
- package/dist/index.cjs +122 -32
- package/dist/index.d.cts +38 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +121 -32
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -632,14 +632,6 @@ function resolveDbPath(raw, configDir2) {
|
|
|
632
632
|
}
|
|
633
633
|
return resolve(configDir2, raw);
|
|
634
634
|
}
|
|
635
|
-
function warnDeprecatedRef(entity, field, target) {
|
|
636
|
-
const key = `${entity}.${field}`;
|
|
637
|
-
if (warnedDeprecatedRefs.has(key)) return;
|
|
638
|
-
warnedDeprecatedRefs.add(key);
|
|
639
|
-
console.warn(
|
|
640
|
-
`Lattice: one-to-many \`ref:\` on "${entity}.${field}" \u2192 "${target}" is deprecated in favor of many-to-many junction tables and will be removed in 2.0.`
|
|
641
|
-
);
|
|
642
|
-
}
|
|
643
635
|
function entityToTableDef(entityName, entity) {
|
|
644
636
|
const rawFields = entity.fields;
|
|
645
637
|
if (!rawFields || typeof rawFields !== "object" || Array.isArray(rawFields)) {
|
|
@@ -666,7 +658,6 @@ function entityToTableDef(entityName, entity) {
|
|
|
666
658
|
table: field.ref,
|
|
667
659
|
foreignKey: fieldName
|
|
668
660
|
};
|
|
669
|
-
warnDeprecatedRef(entityName, fieldName, field.ref);
|
|
670
661
|
}
|
|
671
662
|
}
|
|
672
663
|
const primaryKey = entity.primaryKey ?? pkFromField;
|
|
@@ -823,12 +814,10 @@ function parseEntityContexts(entityContexts) {
|
|
|
823
814
|
}
|
|
824
815
|
return result;
|
|
825
816
|
}
|
|
826
|
-
var warnedDeprecatedRefs;
|
|
827
817
|
var init_parser = __esm({
|
|
828
818
|
"src/config/parser.ts"() {
|
|
829
819
|
"use strict";
|
|
830
820
|
init_user_config();
|
|
831
|
-
warnedDeprecatedRefs = /* @__PURE__ */ new Set();
|
|
832
821
|
}
|
|
833
822
|
});
|
|
834
823
|
|
|
@@ -54094,6 +54083,8 @@ var css = `
|
|
|
54094
54083
|
.app-version:empty { display: none; }
|
|
54095
54084
|
.app-update { flex: 0 0 auto; color: var(--accent, #4a9); font-size: 12px; white-space: nowrap; }
|
|
54096
54085
|
.app-update[hidden] { display: none; }
|
|
54086
|
+
#app-update-link { flex: 0 0 auto; margin-left: 8px; color: var(--accent, #4a9); font-size: 12px; cursor: pointer; white-space: nowrap; }
|
|
54087
|
+
#app-update-link[hidden] { display: none; }
|
|
54097
54088
|
/* Unseen-change count next to a sidebar entity. */
|
|
54098
54089
|
.nav-badge {
|
|
54099
54090
|
display: inline-block; min-width: 16px; text-align: center;
|
|
@@ -55572,6 +55563,12 @@ var appJs = `
|
|
|
55572
55563
|
// drag handle once the app has booted.
|
|
55573
55564
|
var savedRail = parseInt(window.localStorage.getItem(RAIL_KEY) || '', 10);
|
|
55574
55565
|
if (!isNaN(savedRail)) applyRailWidth(savedRail);
|
|
55566
|
+
// The version chip + manual-upgrade link live in the static shell (present
|
|
55567
|
+
// from first paint, in both the normal and virgin-state boots), so wire the
|
|
55568
|
+
// click handler and run the first availability check here \u2014 independent of
|
|
55569
|
+
// the async workspace bootstrap. checkServerVersion() refreshes it later.
|
|
55570
|
+
wireUpdateLink();
|
|
55571
|
+
checkUpdateAvailable();
|
|
55575
55572
|
// Failsafe: never leave the overlay up forever if a fetch hangs without
|
|
55576
55573
|
// rejecting, or a future early-return (e.g. the virgin-state screen)
|
|
55577
55574
|
// bypasses the .then() tail. Idempotent, so a later real hide is a no-op.
|
|
@@ -56056,6 +56053,26 @@ var appJs = `
|
|
|
56056
56053
|
showUpdatePill(label || 'Updated \u2014 reloading\u2026');
|
|
56057
56054
|
setTimeout(function () { location.reload(); }, 600);
|
|
56058
56055
|
}
|
|
56056
|
+
// Manual upgrade fallback: show an "Update available \u2014 Upgrade" link next to
|
|
56057
|
+
// the version chip only when the server reports a newer, installable version.
|
|
56058
|
+
// The auto-updater installs in the background on its own cadence; this lets
|
|
56059
|
+
// the user force it now. Best-effort; the link stays hidden on any failure.
|
|
56060
|
+
function checkUpdateAvailable() {
|
|
56061
|
+
var el = document.getElementById('app-update-link');
|
|
56062
|
+
if (!el) return;
|
|
56063
|
+
fetch('/api/update/status')
|
|
56064
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
56065
|
+
.then(function (s) {
|
|
56066
|
+
if (s && s.latest && s.current && s.latest !== s.current && s.installable) {
|
|
56067
|
+
el.textContent = 'Update available \u2014 Upgrade';
|
|
56068
|
+
el.title = 'Install v' + s.latest + ' and restart';
|
|
56069
|
+
el.hidden = false;
|
|
56070
|
+
} else {
|
|
56071
|
+
el.hidden = true;
|
|
56072
|
+
}
|
|
56073
|
+
})
|
|
56074
|
+
.catch(function () { /* best-effort \u2014 keep the link hidden */ });
|
|
56075
|
+
}
|
|
56059
56076
|
// On every (re)connect, ask the server its version. A change vs BOOT_VERSION
|
|
56060
56077
|
// means a relaunch onto new code \u2192 reload. Best-effort; never throws.
|
|
56061
56078
|
function checkServerVersion() {
|
|
@@ -56069,6 +56086,31 @@ var appJs = `
|
|
|
56069
56086
|
else hideUpdatePill();
|
|
56070
56087
|
})
|
|
56071
56088
|
.catch(function () { /* offline / mid-restart \u2014 the next reconnect retries */ });
|
|
56089
|
+
// Refresh the manual-upgrade link alongside the reconnect version check.
|
|
56090
|
+
checkUpdateAvailable();
|
|
56091
|
+
}
|
|
56092
|
+
// Wire the manual-upgrade link's click: kick off the install (the server
|
|
56093
|
+
// installs the latest and restarts onto it) and surface the progress. On
|
|
56094
|
+
// success we do nothing else \u2014 the update-applied event + the reconnect
|
|
56095
|
+
// version check land the page on the new version (no manual reload). A
|
|
56096
|
+
// false ok means the install can't run (unsupervised) \u2014 toast why.
|
|
56097
|
+
function wireUpdateLink() {
|
|
56098
|
+
var el = document.getElementById('app-update-link');
|
|
56099
|
+
if (!el) return;
|
|
56100
|
+
el.addEventListener('click', function (e) {
|
|
56101
|
+
e.preventDefault();
|
|
56102
|
+
el.hidden = true;
|
|
56103
|
+
showUpdatePill('Updating\u2026');
|
|
56104
|
+
fetch('/api/update/apply', { method: 'POST' })
|
|
56105
|
+
.then(function (r) { return r.json(); })
|
|
56106
|
+
.then(function (d) {
|
|
56107
|
+
if (d && d.ok === false) {
|
|
56108
|
+
hideUpdatePill();
|
|
56109
|
+
showToast(d.error || 'Update unavailable', {});
|
|
56110
|
+
}
|
|
56111
|
+
})
|
|
56112
|
+
.catch(function () { /* server may already be restarting */ });
|
|
56113
|
+
});
|
|
56072
56114
|
}
|
|
56073
56115
|
function dispatchStreamMessage(type, data) {
|
|
56074
56116
|
if (type === 'realtime-state') {
|
|
@@ -62886,6 +62928,7 @@ var guiAppHtml = `<!doctype html>
|
|
|
62886
62928
|
<span class="offline-pill" id="offline-pill" title="Edits queued offline \u2014 will sync when the cloud reconnects" hidden></span>
|
|
62887
62929
|
<span class="app-update" id="app-update" title="A new version is being applied" hidden></span>
|
|
62888
62930
|
<span class="app-version" id="app-version" title="Lattice version"><!--LATTICE_VERSION--></span>
|
|
62931
|
+
<a id="app-update-link" href="#" hidden>Update available \u2014 Upgrade</a>
|
|
62889
62932
|
<button id="settings-gear" title="Settings" aria-label="Open settings">
|
|
62890
62933
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
62891
62934
|
<circle cx="12" cy="12" r="3"/>
|
|
@@ -68096,6 +68139,28 @@ async function startGuiServer(options) {
|
|
|
68096
68139
|
setActive(next, created.id);
|
|
68097
68140
|
return created.id;
|
|
68098
68141
|
};
|
|
68142
|
+
const cleanupWorkspaceFiles = (root6, ws) => {
|
|
68143
|
+
if (!ws.configPath && ws.kind === "local") {
|
|
68144
|
+
rmSync(workspaceDir(root6, ws.dir), { recursive: true, force: true });
|
|
68145
|
+
} else if (ws.kind === "cloud") {
|
|
68146
|
+
if (ws.configPath && existsSync25(ws.configPath)) {
|
|
68147
|
+
rmSync(ws.configPath, { force: true });
|
|
68148
|
+
}
|
|
68149
|
+
const labelMatch = /^\$\{LATTICE_DB:([A-Za-z0-9._-]+)\}$/.exec(ws.db.trim());
|
|
68150
|
+
const label = labelMatch?.[1];
|
|
68151
|
+
if (label) {
|
|
68152
|
+
const stillUsed = listWorkspaces(root6).some(
|
|
68153
|
+
(w2) => w2.db.includes("${LATTICE_DB:" + label + "}")
|
|
68154
|
+
);
|
|
68155
|
+
if (!stillUsed) {
|
|
68156
|
+
try {
|
|
68157
|
+
deleteDbCredential(label);
|
|
68158
|
+
} catch {
|
|
68159
|
+
}
|
|
68160
|
+
}
|
|
68161
|
+
}
|
|
68162
|
+
}
|
|
68163
|
+
};
|
|
68099
68164
|
const handleVirginRoute = async (req, res, pathname, method) => {
|
|
68100
68165
|
if (method === "GET" && pathname === "/") {
|
|
68101
68166
|
sendText(
|
|
@@ -68147,6 +68212,35 @@ async function startGuiServer(options) {
|
|
|
68147
68212
|
}
|
|
68148
68213
|
return true;
|
|
68149
68214
|
}
|
|
68215
|
+
if (method === "POST" && pathname === "/api/workspaces/delete") {
|
|
68216
|
+
if (!latticeRoot) {
|
|
68217
|
+
sendJson(res, { error: "No .lattice root \u2014 workspaces unavailable" }, 400);
|
|
68218
|
+
return true;
|
|
68219
|
+
}
|
|
68220
|
+
const body = await readJson(req);
|
|
68221
|
+
if (typeof body.id !== "string") {
|
|
68222
|
+
sendJson(res, { error: "id must be a string" }, 400);
|
|
68223
|
+
return true;
|
|
68224
|
+
}
|
|
68225
|
+
const ws = getWorkspace(latticeRoot, body.id);
|
|
68226
|
+
if (!ws) {
|
|
68227
|
+
sendJson(res, { error: `No workspace with id ${body.id}` }, 400);
|
|
68228
|
+
return true;
|
|
68229
|
+
}
|
|
68230
|
+
removeWorkspace(latticeRoot, ws.id);
|
|
68231
|
+
try {
|
|
68232
|
+
cleanupWorkspaceFiles(latticeRoot, ws);
|
|
68233
|
+
} catch (e6) {
|
|
68234
|
+
sendJson(
|
|
68235
|
+
res,
|
|
68236
|
+
{ error: `Workspace unregistered but file cleanup failed: ${e6.message}` },
|
|
68237
|
+
500
|
|
68238
|
+
);
|
|
68239
|
+
return true;
|
|
68240
|
+
}
|
|
68241
|
+
sendJson(res, { ok: true, switchedTo: null });
|
|
68242
|
+
return true;
|
|
68243
|
+
}
|
|
68150
68244
|
if (method === "POST" && pathname === "/api/cloud/redeem-invite") {
|
|
68151
68245
|
await redeemInvite(createCloudWorkspace, req, res);
|
|
68152
68246
|
return true;
|
|
@@ -68181,6 +68275,18 @@ async function startGuiServer(options) {
|
|
|
68181
68275
|
);
|
|
68182
68276
|
return;
|
|
68183
68277
|
}
|
|
68278
|
+
if (method === "POST" && pathname === "/api/update/apply") {
|
|
68279
|
+
if (updateService) {
|
|
68280
|
+
void updateService.checkNow(true);
|
|
68281
|
+
sendJson(res, { ok: true, status: updateService.status() });
|
|
68282
|
+
} else {
|
|
68283
|
+
sendJson(res, {
|
|
68284
|
+
ok: false,
|
|
68285
|
+
error: "Automatic update is not available for this install. Reinstall from https://latticesql.com to get the latest version."
|
|
68286
|
+
});
|
|
68287
|
+
}
|
|
68288
|
+
return;
|
|
68289
|
+
}
|
|
68184
68290
|
if (!activeRef) {
|
|
68185
68291
|
if (await handleVirginRoute(req, res, pathname, method)) return;
|
|
68186
68292
|
sendJson(res, { error: "No active workspace" }, 409);
|
|
@@ -69274,26 +69380,7 @@ async function startGuiServer(options) {
|
|
|
69274
69380
|
}
|
|
69275
69381
|
removeWorkspace(latticeRoot, ws.id);
|
|
69276
69382
|
try {
|
|
69277
|
-
|
|
69278
|
-
rmSync(workspaceDir(latticeRoot, ws.dir), { recursive: true, force: true });
|
|
69279
|
-
} else if (ws.kind === "cloud") {
|
|
69280
|
-
if (ws.configPath && existsSync25(ws.configPath)) {
|
|
69281
|
-
rmSync(ws.configPath, { force: true });
|
|
69282
|
-
}
|
|
69283
|
-
const labelMatch = /^\$\{LATTICE_DB:([A-Za-z0-9._-]+)\}$/.exec(ws.db.trim());
|
|
69284
|
-
const label = labelMatch?.[1];
|
|
69285
|
-
if (label) {
|
|
69286
|
-
const stillUsed = listWorkspaces(latticeRoot).some(
|
|
69287
|
-
(w2) => w2.db.includes("${LATTICE_DB:" + label + "}")
|
|
69288
|
-
);
|
|
69289
|
-
if (!stillUsed) {
|
|
69290
|
-
try {
|
|
69291
|
-
deleteDbCredential(label);
|
|
69292
|
-
} catch {
|
|
69293
|
-
}
|
|
69294
|
-
}
|
|
69295
|
-
}
|
|
69296
|
-
}
|
|
69383
|
+
cleanupWorkspaceFiles(latticeRoot, ws);
|
|
69297
69384
|
} catch (e6) {
|
|
69298
69385
|
sendJson(
|
|
69299
69386
|
res,
|
|
@@ -69759,7 +69846,9 @@ ${e6.stack ?? ""}`
|
|
|
69759
69846
|
}
|
|
69760
69847
|
}
|
|
69761
69848
|
};
|
|
69762
|
-
if (options.
|
|
69849
|
+
if (options.updateServiceFactory) {
|
|
69850
|
+
updateService = options.updateServiceFactory(broadcast);
|
|
69851
|
+
} else if (options.selfUpdate && guiVersion) {
|
|
69763
69852
|
updateService = createUpdateService({ currentVersion: guiVersion, emit: broadcast });
|
|
69764
69853
|
}
|
|
69765
69854
|
const handleEventStream = (ws) => {
|
|
@@ -70132,7 +70221,7 @@ function printHelp() {
|
|
|
70132
70221
|
);
|
|
70133
70222
|
}
|
|
70134
70223
|
function getVersion() {
|
|
70135
|
-
if (true) return "3.4.
|
|
70224
|
+
if (true) return "3.4.5";
|
|
70136
70225
|
try {
|
|
70137
70226
|
const pkgPath = new URL("../package.json", import.meta.url).pathname;
|
|
70138
70227
|
const pkg = JSON.parse(readFileSync20(pkgPath, "utf-8"));
|
package/dist/index.cjs
CHANGED
|
@@ -4527,14 +4527,6 @@ function resolveDbPath(raw, configDir2) {
|
|
|
4527
4527
|
}
|
|
4528
4528
|
return (0, import_node_path11.resolve)(configDir2, raw);
|
|
4529
4529
|
}
|
|
4530
|
-
function warnDeprecatedRef(entity, field, target) {
|
|
4531
|
-
const key = `${entity}.${field}`;
|
|
4532
|
-
if (warnedDeprecatedRefs.has(key)) return;
|
|
4533
|
-
warnedDeprecatedRefs.add(key);
|
|
4534
|
-
console.warn(
|
|
4535
|
-
`Lattice: one-to-many \`ref:\` on "${entity}.${field}" \u2192 "${target}" is deprecated in favor of many-to-many junction tables and will be removed in 2.0.`
|
|
4536
|
-
);
|
|
4537
|
-
}
|
|
4538
4530
|
function entityToTableDef(entityName, entity) {
|
|
4539
4531
|
const rawFields = entity.fields;
|
|
4540
4532
|
if (!rawFields || typeof rawFields !== "object" || Array.isArray(rawFields)) {
|
|
@@ -4561,7 +4553,6 @@ function entityToTableDef(entityName, entity) {
|
|
|
4561
4553
|
table: field.ref,
|
|
4562
4554
|
foreignKey: fieldName
|
|
4563
4555
|
};
|
|
4564
|
-
warnDeprecatedRef(entityName, fieldName, field.ref);
|
|
4565
4556
|
}
|
|
4566
4557
|
}
|
|
4567
4558
|
const primaryKey = entity.primaryKey ?? pkFromField;
|
|
@@ -4718,7 +4709,7 @@ function parseEntityContexts(entityContexts) {
|
|
|
4718
4709
|
}
|
|
4719
4710
|
return result;
|
|
4720
4711
|
}
|
|
4721
|
-
var import_node_fs10, import_node_path11, import_yaml3
|
|
4712
|
+
var import_node_fs10, import_node_path11, import_yaml3;
|
|
4722
4713
|
var init_parser = __esm({
|
|
4723
4714
|
"src/config/parser.ts"() {
|
|
4724
4715
|
"use strict";
|
|
@@ -4726,7 +4717,6 @@ var init_parser = __esm({
|
|
|
4726
4717
|
import_node_path11 = require("path");
|
|
4727
4718
|
import_yaml3 = require("yaml");
|
|
4728
4719
|
init_user_config();
|
|
4729
|
-
warnedDeprecatedRefs = /* @__PURE__ */ new Set();
|
|
4730
4720
|
}
|
|
4731
4721
|
});
|
|
4732
4722
|
|
|
@@ -55971,6 +55961,8 @@ var css = `
|
|
|
55971
55961
|
.app-version:empty { display: none; }
|
|
55972
55962
|
.app-update { flex: 0 0 auto; color: var(--accent, #4a9); font-size: 12px; white-space: nowrap; }
|
|
55973
55963
|
.app-update[hidden] { display: none; }
|
|
55964
|
+
#app-update-link { flex: 0 0 auto; margin-left: 8px; color: var(--accent, #4a9); font-size: 12px; cursor: pointer; white-space: nowrap; }
|
|
55965
|
+
#app-update-link[hidden] { display: none; }
|
|
55974
55966
|
/* Unseen-change count next to a sidebar entity. */
|
|
55975
55967
|
.nav-badge {
|
|
55976
55968
|
display: inline-block; min-width: 16px; text-align: center;
|
|
@@ -57449,6 +57441,12 @@ var appJs = `
|
|
|
57449
57441
|
// drag handle once the app has booted.
|
|
57450
57442
|
var savedRail = parseInt(window.localStorage.getItem(RAIL_KEY) || '', 10);
|
|
57451
57443
|
if (!isNaN(savedRail)) applyRailWidth(savedRail);
|
|
57444
|
+
// The version chip + manual-upgrade link live in the static shell (present
|
|
57445
|
+
// from first paint, in both the normal and virgin-state boots), so wire the
|
|
57446
|
+
// click handler and run the first availability check here \u2014 independent of
|
|
57447
|
+
// the async workspace bootstrap. checkServerVersion() refreshes it later.
|
|
57448
|
+
wireUpdateLink();
|
|
57449
|
+
checkUpdateAvailable();
|
|
57452
57450
|
// Failsafe: never leave the overlay up forever if a fetch hangs without
|
|
57453
57451
|
// rejecting, or a future early-return (e.g. the virgin-state screen)
|
|
57454
57452
|
// bypasses the .then() tail. Idempotent, so a later real hide is a no-op.
|
|
@@ -57933,6 +57931,26 @@ var appJs = `
|
|
|
57933
57931
|
showUpdatePill(label || 'Updated \u2014 reloading\u2026');
|
|
57934
57932
|
setTimeout(function () { location.reload(); }, 600);
|
|
57935
57933
|
}
|
|
57934
|
+
// Manual upgrade fallback: show an "Update available \u2014 Upgrade" link next to
|
|
57935
|
+
// the version chip only when the server reports a newer, installable version.
|
|
57936
|
+
// The auto-updater installs in the background on its own cadence; this lets
|
|
57937
|
+
// the user force it now. Best-effort; the link stays hidden on any failure.
|
|
57938
|
+
function checkUpdateAvailable() {
|
|
57939
|
+
var el = document.getElementById('app-update-link');
|
|
57940
|
+
if (!el) return;
|
|
57941
|
+
fetch('/api/update/status')
|
|
57942
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
57943
|
+
.then(function (s) {
|
|
57944
|
+
if (s && s.latest && s.current && s.latest !== s.current && s.installable) {
|
|
57945
|
+
el.textContent = 'Update available \u2014 Upgrade';
|
|
57946
|
+
el.title = 'Install v' + s.latest + ' and restart';
|
|
57947
|
+
el.hidden = false;
|
|
57948
|
+
} else {
|
|
57949
|
+
el.hidden = true;
|
|
57950
|
+
}
|
|
57951
|
+
})
|
|
57952
|
+
.catch(function () { /* best-effort \u2014 keep the link hidden */ });
|
|
57953
|
+
}
|
|
57936
57954
|
// On every (re)connect, ask the server its version. A change vs BOOT_VERSION
|
|
57937
57955
|
// means a relaunch onto new code \u2192 reload. Best-effort; never throws.
|
|
57938
57956
|
function checkServerVersion() {
|
|
@@ -57946,6 +57964,31 @@ var appJs = `
|
|
|
57946
57964
|
else hideUpdatePill();
|
|
57947
57965
|
})
|
|
57948
57966
|
.catch(function () { /* offline / mid-restart \u2014 the next reconnect retries */ });
|
|
57967
|
+
// Refresh the manual-upgrade link alongside the reconnect version check.
|
|
57968
|
+
checkUpdateAvailable();
|
|
57969
|
+
}
|
|
57970
|
+
// Wire the manual-upgrade link's click: kick off the install (the server
|
|
57971
|
+
// installs the latest and restarts onto it) and surface the progress. On
|
|
57972
|
+
// success we do nothing else \u2014 the update-applied event + the reconnect
|
|
57973
|
+
// version check land the page on the new version (no manual reload). A
|
|
57974
|
+
// false ok means the install can't run (unsupervised) \u2014 toast why.
|
|
57975
|
+
function wireUpdateLink() {
|
|
57976
|
+
var el = document.getElementById('app-update-link');
|
|
57977
|
+
if (!el) return;
|
|
57978
|
+
el.addEventListener('click', function (e) {
|
|
57979
|
+
e.preventDefault();
|
|
57980
|
+
el.hidden = true;
|
|
57981
|
+
showUpdatePill('Updating\u2026');
|
|
57982
|
+
fetch('/api/update/apply', { method: 'POST' })
|
|
57983
|
+
.then(function (r) { return r.json(); })
|
|
57984
|
+
.then(function (d) {
|
|
57985
|
+
if (d && d.ok === false) {
|
|
57986
|
+
hideUpdatePill();
|
|
57987
|
+
showToast(d.error || 'Update unavailable', {});
|
|
57988
|
+
}
|
|
57989
|
+
})
|
|
57990
|
+
.catch(function () { /* server may already be restarting */ });
|
|
57991
|
+
});
|
|
57949
57992
|
}
|
|
57950
57993
|
function dispatchStreamMessage(type, data) {
|
|
57951
57994
|
if (type === 'realtime-state') {
|
|
@@ -64763,6 +64806,7 @@ var guiAppHtml = `<!doctype html>
|
|
|
64763
64806
|
<span class="offline-pill" id="offline-pill" title="Edits queued offline \u2014 will sync when the cloud reconnects" hidden></span>
|
|
64764
64807
|
<span class="app-update" id="app-update" title="A new version is being applied" hidden></span>
|
|
64765
64808
|
<span class="app-version" id="app-version" title="Lattice version"><!--LATTICE_VERSION--></span>
|
|
64809
|
+
<a id="app-update-link" href="#" hidden>Update available \u2014 Upgrade</a>
|
|
64766
64810
|
<button id="settings-gear" title="Settings" aria-label="Open settings">
|
|
64767
64811
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
64768
64812
|
<circle cx="12" cy="12" r="3"/>
|
|
@@ -69305,6 +69349,28 @@ async function startGuiServer(options) {
|
|
|
69305
69349
|
setActive(next, created.id);
|
|
69306
69350
|
return created.id;
|
|
69307
69351
|
};
|
|
69352
|
+
const cleanupWorkspaceFiles = (root6, ws) => {
|
|
69353
|
+
if (!ws.configPath && ws.kind === "local") {
|
|
69354
|
+
(0, import_node_fs35.rmSync)(workspaceDir(root6, ws.dir), { recursive: true, force: true });
|
|
69355
|
+
} else if (ws.kind === "cloud") {
|
|
69356
|
+
if (ws.configPath && (0, import_node_fs35.existsSync)(ws.configPath)) {
|
|
69357
|
+
(0, import_node_fs35.rmSync)(ws.configPath, { force: true });
|
|
69358
|
+
}
|
|
69359
|
+
const labelMatch = /^\$\{LATTICE_DB:([A-Za-z0-9._-]+)\}$/.exec(ws.db.trim());
|
|
69360
|
+
const label = labelMatch?.[1];
|
|
69361
|
+
if (label) {
|
|
69362
|
+
const stillUsed = listWorkspaces(root6).some(
|
|
69363
|
+
(w2) => w2.db.includes("${LATTICE_DB:" + label + "}")
|
|
69364
|
+
);
|
|
69365
|
+
if (!stillUsed) {
|
|
69366
|
+
try {
|
|
69367
|
+
deleteDbCredential(label);
|
|
69368
|
+
} catch {
|
|
69369
|
+
}
|
|
69370
|
+
}
|
|
69371
|
+
}
|
|
69372
|
+
}
|
|
69373
|
+
};
|
|
69308
69374
|
const handleVirginRoute = async (req, res, pathname, method) => {
|
|
69309
69375
|
if (method === "GET" && pathname === "/") {
|
|
69310
69376
|
sendText(
|
|
@@ -69356,6 +69422,35 @@ async function startGuiServer(options) {
|
|
|
69356
69422
|
}
|
|
69357
69423
|
return true;
|
|
69358
69424
|
}
|
|
69425
|
+
if (method === "POST" && pathname === "/api/workspaces/delete") {
|
|
69426
|
+
if (!latticeRoot) {
|
|
69427
|
+
sendJson(res, { error: "No .lattice root \u2014 workspaces unavailable" }, 400);
|
|
69428
|
+
return true;
|
|
69429
|
+
}
|
|
69430
|
+
const body = await readJson(req);
|
|
69431
|
+
if (typeof body.id !== "string") {
|
|
69432
|
+
sendJson(res, { error: "id must be a string" }, 400);
|
|
69433
|
+
return true;
|
|
69434
|
+
}
|
|
69435
|
+
const ws = getWorkspace(latticeRoot, body.id);
|
|
69436
|
+
if (!ws) {
|
|
69437
|
+
sendJson(res, { error: `No workspace with id ${body.id}` }, 400);
|
|
69438
|
+
return true;
|
|
69439
|
+
}
|
|
69440
|
+
removeWorkspace(latticeRoot, ws.id);
|
|
69441
|
+
try {
|
|
69442
|
+
cleanupWorkspaceFiles(latticeRoot, ws);
|
|
69443
|
+
} catch (e6) {
|
|
69444
|
+
sendJson(
|
|
69445
|
+
res,
|
|
69446
|
+
{ error: `Workspace unregistered but file cleanup failed: ${e6.message}` },
|
|
69447
|
+
500
|
|
69448
|
+
);
|
|
69449
|
+
return true;
|
|
69450
|
+
}
|
|
69451
|
+
sendJson(res, { ok: true, switchedTo: null });
|
|
69452
|
+
return true;
|
|
69453
|
+
}
|
|
69359
69454
|
if (method === "POST" && pathname === "/api/cloud/redeem-invite") {
|
|
69360
69455
|
await redeemInvite(createCloudWorkspace, req, res);
|
|
69361
69456
|
return true;
|
|
@@ -69390,6 +69485,18 @@ async function startGuiServer(options) {
|
|
|
69390
69485
|
);
|
|
69391
69486
|
return;
|
|
69392
69487
|
}
|
|
69488
|
+
if (method === "POST" && pathname === "/api/update/apply") {
|
|
69489
|
+
if (updateService) {
|
|
69490
|
+
void updateService.checkNow(true);
|
|
69491
|
+
sendJson(res, { ok: true, status: updateService.status() });
|
|
69492
|
+
} else {
|
|
69493
|
+
sendJson(res, {
|
|
69494
|
+
ok: false,
|
|
69495
|
+
error: "Automatic update is not available for this install. Reinstall from https://latticesql.com to get the latest version."
|
|
69496
|
+
});
|
|
69497
|
+
}
|
|
69498
|
+
return;
|
|
69499
|
+
}
|
|
69393
69500
|
if (!activeRef) {
|
|
69394
69501
|
if (await handleVirginRoute(req, res, pathname, method)) return;
|
|
69395
69502
|
sendJson(res, { error: "No active workspace" }, 409);
|
|
@@ -70483,26 +70590,7 @@ async function startGuiServer(options) {
|
|
|
70483
70590
|
}
|
|
70484
70591
|
removeWorkspace(latticeRoot, ws.id);
|
|
70485
70592
|
try {
|
|
70486
|
-
|
|
70487
|
-
(0, import_node_fs35.rmSync)(workspaceDir(latticeRoot, ws.dir), { recursive: true, force: true });
|
|
70488
|
-
} else if (ws.kind === "cloud") {
|
|
70489
|
-
if (ws.configPath && (0, import_node_fs35.existsSync)(ws.configPath)) {
|
|
70490
|
-
(0, import_node_fs35.rmSync)(ws.configPath, { force: true });
|
|
70491
|
-
}
|
|
70492
|
-
const labelMatch = /^\$\{LATTICE_DB:([A-Za-z0-9._-]+)\}$/.exec(ws.db.trim());
|
|
70493
|
-
const label = labelMatch?.[1];
|
|
70494
|
-
if (label) {
|
|
70495
|
-
const stillUsed = listWorkspaces(latticeRoot).some(
|
|
70496
|
-
(w2) => w2.db.includes("${LATTICE_DB:" + label + "}")
|
|
70497
|
-
);
|
|
70498
|
-
if (!stillUsed) {
|
|
70499
|
-
try {
|
|
70500
|
-
deleteDbCredential(label);
|
|
70501
|
-
} catch {
|
|
70502
|
-
}
|
|
70503
|
-
}
|
|
70504
|
-
}
|
|
70505
|
-
}
|
|
70593
|
+
cleanupWorkspaceFiles(latticeRoot, ws);
|
|
70506
70594
|
} catch (e6) {
|
|
70507
70595
|
sendJson(
|
|
70508
70596
|
res,
|
|
@@ -70968,7 +71056,9 @@ ${e6.stack ?? ""}`
|
|
|
70968
71056
|
}
|
|
70969
71057
|
}
|
|
70970
71058
|
};
|
|
70971
|
-
if (options.
|
|
71059
|
+
if (options.updateServiceFactory) {
|
|
71060
|
+
updateService = options.updateServiceFactory(broadcast);
|
|
71061
|
+
} else if (options.selfUpdate && guiVersion) {
|
|
70972
71062
|
updateService = createUpdateService({ currentVersion: guiVersion, emit: broadcast });
|
|
70973
71063
|
}
|
|
70974
71064
|
const handleEventStream = (ws) => {
|
package/dist/index.d.cts
CHANGED
|
@@ -4923,6 +4923,37 @@ interface PdfOptions {
|
|
|
4923
4923
|
*/
|
|
4924
4924
|
declare function describePdf(auth: ClaudeAuth, path: string, opts?: PdfOptions): Promise<string>;
|
|
4925
4925
|
|
|
4926
|
+
/** How the running copy of the package was installed. */
|
|
4927
|
+
type InstallKind = 'global' | 'local' | 'npx' | 'linked-dev' | 'unknown';
|
|
4928
|
+
interface InstallContext {
|
|
4929
|
+
kind: InstallKind;
|
|
4930
|
+
/** True only when an `npm install` may safely upgrade this copy in place. */
|
|
4931
|
+
installable: boolean;
|
|
4932
|
+
/** Directory to run the install from (the consumer project root for `local`). */
|
|
4933
|
+
cwd: string;
|
|
4934
|
+
/** Resolved package root of the running copy, if found. */
|
|
4935
|
+
packageRoot: string | null;
|
|
4936
|
+
/** Human-readable explanation (logged / surfaced in `/api/update/status`). */
|
|
4937
|
+
reason: string;
|
|
4938
|
+
}
|
|
4939
|
+
|
|
4940
|
+
interface UpdateStatus {
|
|
4941
|
+
current: string;
|
|
4942
|
+
latest: string | null;
|
|
4943
|
+
kind: InstallContext['kind'];
|
|
4944
|
+
installable: boolean;
|
|
4945
|
+
checking: boolean;
|
|
4946
|
+
installing: boolean;
|
|
4947
|
+
lastError: string | null;
|
|
4948
|
+
}
|
|
4949
|
+
interface UpdateService {
|
|
4950
|
+
start(): void;
|
|
4951
|
+
stop(): void;
|
|
4952
|
+
status(): UpdateStatus;
|
|
4953
|
+
/** Run a check now (and install if applicable). Returns the resulting status. */
|
|
4954
|
+
checkNow(force?: boolean): Promise<UpdateStatus>;
|
|
4955
|
+
}
|
|
4956
|
+
|
|
4926
4957
|
interface StartGuiServerOptions {
|
|
4927
4958
|
/**
|
|
4928
4959
|
* Active workspace config to open. NULL/empty ⇒ boot into the zero-workspace
|
|
@@ -4979,6 +5010,13 @@ interface StartGuiServerOptions {
|
|
|
4979
5010
|
* `GET /api/version` + `GET /api/update/status` are served regardless.
|
|
4980
5011
|
*/
|
|
4981
5012
|
selfUpdate?: boolean;
|
|
5013
|
+
/**
|
|
5014
|
+
* Test seam: supply the update service instead of building one from the real
|
|
5015
|
+
* npm-backed install context. Lets tests exercise the update routes against a
|
|
5016
|
+
* deterministic fake (no real registry check, no real npm install). When set,
|
|
5017
|
+
* it overrides `selfUpdate`'s default factory.
|
|
5018
|
+
*/
|
|
5019
|
+
updateServiceFactory?: (emit: (type: string, data: unknown) => void) => UpdateService;
|
|
4982
5020
|
}
|
|
4983
5021
|
interface GuiServerHandle {
|
|
4984
5022
|
server: Server;
|
package/dist/index.d.ts
CHANGED
|
@@ -4923,6 +4923,37 @@ interface PdfOptions {
|
|
|
4923
4923
|
*/
|
|
4924
4924
|
declare function describePdf(auth: ClaudeAuth, path: string, opts?: PdfOptions): Promise<string>;
|
|
4925
4925
|
|
|
4926
|
+
/** How the running copy of the package was installed. */
|
|
4927
|
+
type InstallKind = 'global' | 'local' | 'npx' | 'linked-dev' | 'unknown';
|
|
4928
|
+
interface InstallContext {
|
|
4929
|
+
kind: InstallKind;
|
|
4930
|
+
/** True only when an `npm install` may safely upgrade this copy in place. */
|
|
4931
|
+
installable: boolean;
|
|
4932
|
+
/** Directory to run the install from (the consumer project root for `local`). */
|
|
4933
|
+
cwd: string;
|
|
4934
|
+
/** Resolved package root of the running copy, if found. */
|
|
4935
|
+
packageRoot: string | null;
|
|
4936
|
+
/** Human-readable explanation (logged / surfaced in `/api/update/status`). */
|
|
4937
|
+
reason: string;
|
|
4938
|
+
}
|
|
4939
|
+
|
|
4940
|
+
interface UpdateStatus {
|
|
4941
|
+
current: string;
|
|
4942
|
+
latest: string | null;
|
|
4943
|
+
kind: InstallContext['kind'];
|
|
4944
|
+
installable: boolean;
|
|
4945
|
+
checking: boolean;
|
|
4946
|
+
installing: boolean;
|
|
4947
|
+
lastError: string | null;
|
|
4948
|
+
}
|
|
4949
|
+
interface UpdateService {
|
|
4950
|
+
start(): void;
|
|
4951
|
+
stop(): void;
|
|
4952
|
+
status(): UpdateStatus;
|
|
4953
|
+
/** Run a check now (and install if applicable). Returns the resulting status. */
|
|
4954
|
+
checkNow(force?: boolean): Promise<UpdateStatus>;
|
|
4955
|
+
}
|
|
4956
|
+
|
|
4926
4957
|
interface StartGuiServerOptions {
|
|
4927
4958
|
/**
|
|
4928
4959
|
* Active workspace config to open. NULL/empty ⇒ boot into the zero-workspace
|
|
@@ -4979,6 +5010,13 @@ interface StartGuiServerOptions {
|
|
|
4979
5010
|
* `GET /api/version` + `GET /api/update/status` are served regardless.
|
|
4980
5011
|
*/
|
|
4981
5012
|
selfUpdate?: boolean;
|
|
5013
|
+
/**
|
|
5014
|
+
* Test seam: supply the update service instead of building one from the real
|
|
5015
|
+
* npm-backed install context. Lets tests exercise the update routes against a
|
|
5016
|
+
* deterministic fake (no real registry check, no real npm install). When set,
|
|
5017
|
+
* it overrides `selfUpdate`'s default factory.
|
|
5018
|
+
*/
|
|
5019
|
+
updateServiceFactory?: (emit: (type: string, data: unknown) => void) => UpdateService;
|
|
4982
5020
|
}
|
|
4983
5021
|
interface GuiServerHandle {
|
|
4984
5022
|
server: Server;
|
package/dist/index.js
CHANGED
|
@@ -4531,14 +4531,6 @@ function resolveDbPath(raw, configDir2) {
|
|
|
4531
4531
|
}
|
|
4532
4532
|
return resolve2(configDir2, raw);
|
|
4533
4533
|
}
|
|
4534
|
-
function warnDeprecatedRef(entity, field, target) {
|
|
4535
|
-
const key = `${entity}.${field}`;
|
|
4536
|
-
if (warnedDeprecatedRefs.has(key)) return;
|
|
4537
|
-
warnedDeprecatedRefs.add(key);
|
|
4538
|
-
console.warn(
|
|
4539
|
-
`Lattice: one-to-many \`ref:\` on "${entity}.${field}" \u2192 "${target}" is deprecated in favor of many-to-many junction tables and will be removed in 2.0.`
|
|
4540
|
-
);
|
|
4541
|
-
}
|
|
4542
4534
|
function entityToTableDef(entityName, entity) {
|
|
4543
4535
|
const rawFields = entity.fields;
|
|
4544
4536
|
if (!rawFields || typeof rawFields !== "object" || Array.isArray(rawFields)) {
|
|
@@ -4565,7 +4557,6 @@ function entityToTableDef(entityName, entity) {
|
|
|
4565
4557
|
table: field.ref,
|
|
4566
4558
|
foreignKey: fieldName
|
|
4567
4559
|
};
|
|
4568
|
-
warnDeprecatedRef(entityName, fieldName, field.ref);
|
|
4569
4560
|
}
|
|
4570
4561
|
}
|
|
4571
4562
|
const primaryKey = entity.primaryKey ?? pkFromField;
|
|
@@ -4722,12 +4713,10 @@ function parseEntityContexts(entityContexts) {
|
|
|
4722
4713
|
}
|
|
4723
4714
|
return result;
|
|
4724
4715
|
}
|
|
4725
|
-
var warnedDeprecatedRefs;
|
|
4726
4716
|
var init_parser = __esm({
|
|
4727
4717
|
"src/config/parser.ts"() {
|
|
4728
4718
|
"use strict";
|
|
4729
4719
|
init_user_config();
|
|
4730
|
-
warnedDeprecatedRefs = /* @__PURE__ */ new Set();
|
|
4731
4720
|
}
|
|
4732
4721
|
});
|
|
4733
4722
|
|
|
@@ -55797,6 +55786,8 @@ var css = `
|
|
|
55797
55786
|
.app-version:empty { display: none; }
|
|
55798
55787
|
.app-update { flex: 0 0 auto; color: var(--accent, #4a9); font-size: 12px; white-space: nowrap; }
|
|
55799
55788
|
.app-update[hidden] { display: none; }
|
|
55789
|
+
#app-update-link { flex: 0 0 auto; margin-left: 8px; color: var(--accent, #4a9); font-size: 12px; cursor: pointer; white-space: nowrap; }
|
|
55790
|
+
#app-update-link[hidden] { display: none; }
|
|
55800
55791
|
/* Unseen-change count next to a sidebar entity. */
|
|
55801
55792
|
.nav-badge {
|
|
55802
55793
|
display: inline-block; min-width: 16px; text-align: center;
|
|
@@ -57275,6 +57266,12 @@ var appJs = `
|
|
|
57275
57266
|
// drag handle once the app has booted.
|
|
57276
57267
|
var savedRail = parseInt(window.localStorage.getItem(RAIL_KEY) || '', 10);
|
|
57277
57268
|
if (!isNaN(savedRail)) applyRailWidth(savedRail);
|
|
57269
|
+
// The version chip + manual-upgrade link live in the static shell (present
|
|
57270
|
+
// from first paint, in both the normal and virgin-state boots), so wire the
|
|
57271
|
+
// click handler and run the first availability check here \u2014 independent of
|
|
57272
|
+
// the async workspace bootstrap. checkServerVersion() refreshes it later.
|
|
57273
|
+
wireUpdateLink();
|
|
57274
|
+
checkUpdateAvailable();
|
|
57278
57275
|
// Failsafe: never leave the overlay up forever if a fetch hangs without
|
|
57279
57276
|
// rejecting, or a future early-return (e.g. the virgin-state screen)
|
|
57280
57277
|
// bypasses the .then() tail. Idempotent, so a later real hide is a no-op.
|
|
@@ -57759,6 +57756,26 @@ var appJs = `
|
|
|
57759
57756
|
showUpdatePill(label || 'Updated \u2014 reloading\u2026');
|
|
57760
57757
|
setTimeout(function () { location.reload(); }, 600);
|
|
57761
57758
|
}
|
|
57759
|
+
// Manual upgrade fallback: show an "Update available \u2014 Upgrade" link next to
|
|
57760
|
+
// the version chip only when the server reports a newer, installable version.
|
|
57761
|
+
// The auto-updater installs in the background on its own cadence; this lets
|
|
57762
|
+
// the user force it now. Best-effort; the link stays hidden on any failure.
|
|
57763
|
+
function checkUpdateAvailable() {
|
|
57764
|
+
var el = document.getElementById('app-update-link');
|
|
57765
|
+
if (!el) return;
|
|
57766
|
+
fetch('/api/update/status')
|
|
57767
|
+
.then(function (r) { return r.ok ? r.json() : null; })
|
|
57768
|
+
.then(function (s) {
|
|
57769
|
+
if (s && s.latest && s.current && s.latest !== s.current && s.installable) {
|
|
57770
|
+
el.textContent = 'Update available \u2014 Upgrade';
|
|
57771
|
+
el.title = 'Install v' + s.latest + ' and restart';
|
|
57772
|
+
el.hidden = false;
|
|
57773
|
+
} else {
|
|
57774
|
+
el.hidden = true;
|
|
57775
|
+
}
|
|
57776
|
+
})
|
|
57777
|
+
.catch(function () { /* best-effort \u2014 keep the link hidden */ });
|
|
57778
|
+
}
|
|
57762
57779
|
// On every (re)connect, ask the server its version. A change vs BOOT_VERSION
|
|
57763
57780
|
// means a relaunch onto new code \u2192 reload. Best-effort; never throws.
|
|
57764
57781
|
function checkServerVersion() {
|
|
@@ -57772,6 +57789,31 @@ var appJs = `
|
|
|
57772
57789
|
else hideUpdatePill();
|
|
57773
57790
|
})
|
|
57774
57791
|
.catch(function () { /* offline / mid-restart \u2014 the next reconnect retries */ });
|
|
57792
|
+
// Refresh the manual-upgrade link alongside the reconnect version check.
|
|
57793
|
+
checkUpdateAvailable();
|
|
57794
|
+
}
|
|
57795
|
+
// Wire the manual-upgrade link's click: kick off the install (the server
|
|
57796
|
+
// installs the latest and restarts onto it) and surface the progress. On
|
|
57797
|
+
// success we do nothing else \u2014 the update-applied event + the reconnect
|
|
57798
|
+
// version check land the page on the new version (no manual reload). A
|
|
57799
|
+
// false ok means the install can't run (unsupervised) \u2014 toast why.
|
|
57800
|
+
function wireUpdateLink() {
|
|
57801
|
+
var el = document.getElementById('app-update-link');
|
|
57802
|
+
if (!el) return;
|
|
57803
|
+
el.addEventListener('click', function (e) {
|
|
57804
|
+
e.preventDefault();
|
|
57805
|
+
el.hidden = true;
|
|
57806
|
+
showUpdatePill('Updating\u2026');
|
|
57807
|
+
fetch('/api/update/apply', { method: 'POST' })
|
|
57808
|
+
.then(function (r) { return r.json(); })
|
|
57809
|
+
.then(function (d) {
|
|
57810
|
+
if (d && d.ok === false) {
|
|
57811
|
+
hideUpdatePill();
|
|
57812
|
+
showToast(d.error || 'Update unavailable', {});
|
|
57813
|
+
}
|
|
57814
|
+
})
|
|
57815
|
+
.catch(function () { /* server may already be restarting */ });
|
|
57816
|
+
});
|
|
57775
57817
|
}
|
|
57776
57818
|
function dispatchStreamMessage(type, data) {
|
|
57777
57819
|
if (type === 'realtime-state') {
|
|
@@ -64589,6 +64631,7 @@ var guiAppHtml = `<!doctype html>
|
|
|
64589
64631
|
<span class="offline-pill" id="offline-pill" title="Edits queued offline \u2014 will sync when the cloud reconnects" hidden></span>
|
|
64590
64632
|
<span class="app-update" id="app-update" title="A new version is being applied" hidden></span>
|
|
64591
64633
|
<span class="app-version" id="app-version" title="Lattice version"><!--LATTICE_VERSION--></span>
|
|
64634
|
+
<a id="app-update-link" href="#" hidden>Update available \u2014 Upgrade</a>
|
|
64592
64635
|
<button id="settings-gear" title="Settings" aria-label="Open settings">
|
|
64593
64636
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
64594
64637
|
<circle cx="12" cy="12" r="3"/>
|
|
@@ -69130,6 +69173,28 @@ async function startGuiServer(options) {
|
|
|
69130
69173
|
setActive(next, created.id);
|
|
69131
69174
|
return created.id;
|
|
69132
69175
|
};
|
|
69176
|
+
const cleanupWorkspaceFiles = (root6, ws) => {
|
|
69177
|
+
if (!ws.configPath && ws.kind === "local") {
|
|
69178
|
+
rmSync(workspaceDir(root6, ws.dir), { recursive: true, force: true });
|
|
69179
|
+
} else if (ws.kind === "cloud") {
|
|
69180
|
+
if (ws.configPath && existsSync24(ws.configPath)) {
|
|
69181
|
+
rmSync(ws.configPath, { force: true });
|
|
69182
|
+
}
|
|
69183
|
+
const labelMatch = /^\$\{LATTICE_DB:([A-Za-z0-9._-]+)\}$/.exec(ws.db.trim());
|
|
69184
|
+
const label = labelMatch?.[1];
|
|
69185
|
+
if (label) {
|
|
69186
|
+
const stillUsed = listWorkspaces(root6).some(
|
|
69187
|
+
(w2) => w2.db.includes("${LATTICE_DB:" + label + "}")
|
|
69188
|
+
);
|
|
69189
|
+
if (!stillUsed) {
|
|
69190
|
+
try {
|
|
69191
|
+
deleteDbCredential(label);
|
|
69192
|
+
} catch {
|
|
69193
|
+
}
|
|
69194
|
+
}
|
|
69195
|
+
}
|
|
69196
|
+
}
|
|
69197
|
+
};
|
|
69133
69198
|
const handleVirginRoute = async (req, res, pathname, method) => {
|
|
69134
69199
|
if (method === "GET" && pathname === "/") {
|
|
69135
69200
|
sendText(
|
|
@@ -69181,6 +69246,35 @@ async function startGuiServer(options) {
|
|
|
69181
69246
|
}
|
|
69182
69247
|
return true;
|
|
69183
69248
|
}
|
|
69249
|
+
if (method === "POST" && pathname === "/api/workspaces/delete") {
|
|
69250
|
+
if (!latticeRoot) {
|
|
69251
|
+
sendJson(res, { error: "No .lattice root \u2014 workspaces unavailable" }, 400);
|
|
69252
|
+
return true;
|
|
69253
|
+
}
|
|
69254
|
+
const body = await readJson(req);
|
|
69255
|
+
if (typeof body.id !== "string") {
|
|
69256
|
+
sendJson(res, { error: "id must be a string" }, 400);
|
|
69257
|
+
return true;
|
|
69258
|
+
}
|
|
69259
|
+
const ws = getWorkspace(latticeRoot, body.id);
|
|
69260
|
+
if (!ws) {
|
|
69261
|
+
sendJson(res, { error: `No workspace with id ${body.id}` }, 400);
|
|
69262
|
+
return true;
|
|
69263
|
+
}
|
|
69264
|
+
removeWorkspace(latticeRoot, ws.id);
|
|
69265
|
+
try {
|
|
69266
|
+
cleanupWorkspaceFiles(latticeRoot, ws);
|
|
69267
|
+
} catch (e6) {
|
|
69268
|
+
sendJson(
|
|
69269
|
+
res,
|
|
69270
|
+
{ error: `Workspace unregistered but file cleanup failed: ${e6.message}` },
|
|
69271
|
+
500
|
|
69272
|
+
);
|
|
69273
|
+
return true;
|
|
69274
|
+
}
|
|
69275
|
+
sendJson(res, { ok: true, switchedTo: null });
|
|
69276
|
+
return true;
|
|
69277
|
+
}
|
|
69184
69278
|
if (method === "POST" && pathname === "/api/cloud/redeem-invite") {
|
|
69185
69279
|
await redeemInvite(createCloudWorkspace, req, res);
|
|
69186
69280
|
return true;
|
|
@@ -69215,6 +69309,18 @@ async function startGuiServer(options) {
|
|
|
69215
69309
|
);
|
|
69216
69310
|
return;
|
|
69217
69311
|
}
|
|
69312
|
+
if (method === "POST" && pathname === "/api/update/apply") {
|
|
69313
|
+
if (updateService) {
|
|
69314
|
+
void updateService.checkNow(true);
|
|
69315
|
+
sendJson(res, { ok: true, status: updateService.status() });
|
|
69316
|
+
} else {
|
|
69317
|
+
sendJson(res, {
|
|
69318
|
+
ok: false,
|
|
69319
|
+
error: "Automatic update is not available for this install. Reinstall from https://latticesql.com to get the latest version."
|
|
69320
|
+
});
|
|
69321
|
+
}
|
|
69322
|
+
return;
|
|
69323
|
+
}
|
|
69218
69324
|
if (!activeRef) {
|
|
69219
69325
|
if (await handleVirginRoute(req, res, pathname, method)) return;
|
|
69220
69326
|
sendJson(res, { error: "No active workspace" }, 409);
|
|
@@ -70308,26 +70414,7 @@ async function startGuiServer(options) {
|
|
|
70308
70414
|
}
|
|
70309
70415
|
removeWorkspace(latticeRoot, ws.id);
|
|
70310
70416
|
try {
|
|
70311
|
-
|
|
70312
|
-
rmSync(workspaceDir(latticeRoot, ws.dir), { recursive: true, force: true });
|
|
70313
|
-
} else if (ws.kind === "cloud") {
|
|
70314
|
-
if (ws.configPath && existsSync24(ws.configPath)) {
|
|
70315
|
-
rmSync(ws.configPath, { force: true });
|
|
70316
|
-
}
|
|
70317
|
-
const labelMatch = /^\$\{LATTICE_DB:([A-Za-z0-9._-]+)\}$/.exec(ws.db.trim());
|
|
70318
|
-
const label = labelMatch?.[1];
|
|
70319
|
-
if (label) {
|
|
70320
|
-
const stillUsed = listWorkspaces(latticeRoot).some(
|
|
70321
|
-
(w2) => w2.db.includes("${LATTICE_DB:" + label + "}")
|
|
70322
|
-
);
|
|
70323
|
-
if (!stillUsed) {
|
|
70324
|
-
try {
|
|
70325
|
-
deleteDbCredential(label);
|
|
70326
|
-
} catch {
|
|
70327
|
-
}
|
|
70328
|
-
}
|
|
70329
|
-
}
|
|
70330
|
-
}
|
|
70417
|
+
cleanupWorkspaceFiles(latticeRoot, ws);
|
|
70331
70418
|
} catch (e6) {
|
|
70332
70419
|
sendJson(
|
|
70333
70420
|
res,
|
|
@@ -70793,7 +70880,9 @@ ${e6.stack ?? ""}`
|
|
|
70793
70880
|
}
|
|
70794
70881
|
}
|
|
70795
70882
|
};
|
|
70796
|
-
if (options.
|
|
70883
|
+
if (options.updateServiceFactory) {
|
|
70884
|
+
updateService = options.updateServiceFactory(broadcast);
|
|
70885
|
+
} else if (options.selfUpdate && guiVersion) {
|
|
70797
70886
|
updateService = createUpdateService({ currentVersion: guiVersion, emit: broadcast });
|
|
70798
70887
|
}
|
|
70799
70888
|
const handleEventStream = (ws) => {
|
package/package.json
CHANGED