latticesql 1.13.0 → 1.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -6
- package/dist/cli.js +117 -40
- package/dist/index.cjs +10 -0
- package/dist/index.d.cts +10 -2
- package/dist/index.d.ts +10 -2
- package/dist/index.js +10 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2066,12 +2066,21 @@ npx lattice gui --config ./lattice.config.yml --output ./context --port 4317
|
|
|
2066
2066
|
|
|
2067
2067
|
**Options**
|
|
2068
2068
|
|
|
2069
|
-
| Flag | Default | Description
|
|
2070
|
-
| --------------------- | ---------------------- |
|
|
2071
|
-
| `--config, -c <path>` | `./lattice.config.yml` | Path to the config file
|
|
2072
|
-
| `--output <dir>` |
|
|
2073
|
-
| `--port <number>` | `4317` | Localhost port; auto-increments when the port is busy
|
|
2074
|
-
| `--no-open` | off | Print the URL without opening a browser
|
|
2069
|
+
| Flag | Default | Description |
|
|
2070
|
+
| --------------------- | ---------------------- | -------------------------------------------------------- |
|
|
2071
|
+
| `--config, -c <path>` | `./lattice.config.yml` | Path to the config file |
|
|
2072
|
+
| `--output <dir>` | (auto-detected) | Output directory containing rendered context — see below |
|
|
2073
|
+
| `--port <number>` | `4317` | Localhost port; auto-increments when the port is busy |
|
|
2074
|
+
| `--no-open` | off | Print the URL without opening a browser |
|
|
2075
|
+
|
|
2076
|
+
**Output-directory auto-detection (v1.13.1+).** When `--output` is not passed explicitly, the GUI probes `./context`, `.`, and `./generated` in order and uses the first directory containing a `.lattice/manifest.json` (announced via a one-line `auto-detected rendered context at "<dir>"` log on stdout). Projects whose `lattice render` writes into the project root no longer need to pass `--output .` every time. An explicit `--output` is always honoured.
|
|
2077
|
+
|
|
2078
|
+
**Entity-context discovery (v1.13.1+).** The Database panel's row-context viewer reads entity contexts from two layered sources so it works regardless of how you register them:
|
|
2079
|
+
|
|
2080
|
+
1. **Live Lattice schema** — anything declared in `lattice.config.yml` or added programmatically via `db.defineEntityContext()` against the active Lattice. Exposed via the new public `Lattice.entityContexts()` accessor.
|
|
2081
|
+
2. **Render manifest fallback** — when a table has no schema-registered entity context but the on-disk `.lattice/manifest.json` names it (typical for projects that register entity contexts in a JS / TS module like `lattice.schema.mjs` that the GUI process never imports), the GUI derives the row → slug mapping heuristically from `row.slug` / `row.id` / `row.name` and surfaces the rendered files anyway.
|
|
2082
|
+
|
|
2083
|
+
The convergence means you don't need to duplicate entity-context definitions in YAML for the GUI to find rendered files.
|
|
2075
2084
|
|
|
2076
2085
|
**Views**
|
|
2077
2086
|
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
7
7
|
});
|
|
8
8
|
|
|
9
9
|
// src/cli.ts
|
|
10
|
-
import { resolve as
|
|
10
|
+
import { resolve as resolve9, dirname as dirname7 } from "path";
|
|
11
11
|
import { readFileSync as readFileSync12 } from "fs";
|
|
12
12
|
import { execSync } from "child_process";
|
|
13
13
|
import { parse as parse2 } from "yaml";
|
|
@@ -3369,6 +3369,16 @@ var Lattice = class {
|
|
|
3369
3369
|
this._schema.defineEntityContext(table, def);
|
|
3370
3370
|
return this;
|
|
3371
3371
|
}
|
|
3372
|
+
/**
|
|
3373
|
+
* All entity contexts currently registered on this Lattice — both those
|
|
3374
|
+
* declared in `lattice.config.yml` and those added programmatically via
|
|
3375
|
+
* `defineEntityContext()`.
|
|
3376
|
+
*
|
|
3377
|
+
* Returns a defensive copy so callers can't mutate the schema.
|
|
3378
|
+
*/
|
|
3379
|
+
entityContexts() {
|
|
3380
|
+
return new Map(this._schema.getEntityContexts());
|
|
3381
|
+
}
|
|
3372
3382
|
/**
|
|
3373
3383
|
* Register a write hook that fires after insert/update/delete operations.
|
|
3374
3384
|
* Hooks run synchronously after the DB write and audit emit.
|
|
@@ -5358,8 +5368,13 @@ var guiAppHtml = `<!doctype html>
|
|
|
5358
5368
|
}
|
|
5359
5369
|
|
|
5360
5370
|
/* \u2500\u2500 Layout \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
5371
|
+
/* minmax(0, 1fr) on the content track lets a wide child (a table with
|
|
5372
|
+
chip-heavy cells) shrink instead of forcing the page wider than the
|
|
5373
|
+
viewport. Without the explicit 0 lower bound, the implicit auto
|
|
5374
|
+
minimum keeps the track at content-width and the whole page scrolls
|
|
5375
|
+
horizontally. */
|
|
5361
5376
|
.layout {
|
|
5362
|
-
display: grid; grid-template-columns: 220px 1fr;
|
|
5377
|
+
display: grid; grid-template-columns: 220px minmax(0, 1fr);
|
|
5363
5378
|
height: calc(100vh - 56px);
|
|
5364
5379
|
}
|
|
5365
5380
|
nav.sidebar {
|
|
@@ -5431,6 +5446,19 @@ var guiAppHtml = `<!doctype html>
|
|
|
5431
5446
|
tbody tr { cursor: pointer; }
|
|
5432
5447
|
tbody tr:hover td { background: var(--row-hover); }
|
|
5433
5448
|
td.muted { color: var(--text-muted); }
|
|
5449
|
+
/* Row cells truncate at 3 lines so a row with many chips or a long text
|
|
5450
|
+
blob stays one consistent visual height instead of wrapping into a
|
|
5451
|
+
paragraph. The wrapping <div class="cell-clip"> is necessary because
|
|
5452
|
+
-webkit-line-clamp doesn't apply to <td> directly in all engines. */
|
|
5453
|
+
td .cell-clip {
|
|
5454
|
+
display: -webkit-box;
|
|
5455
|
+
-webkit-line-clamp: 3;
|
|
5456
|
+
-webkit-box-orient: vertical;
|
|
5457
|
+
overflow: hidden;
|
|
5458
|
+
line-height: 1.45;
|
|
5459
|
+
max-height: calc(1.45em * 3);
|
|
5460
|
+
word-break: break-word;
|
|
5461
|
+
}
|
|
5434
5462
|
.chip {
|
|
5435
5463
|
display: inline-block; padding: 2px 8px; margin: 1px 3px 1px 0;
|
|
5436
5464
|
background: var(--accent-soft); color: var(--accent);
|
|
@@ -6390,11 +6418,11 @@ var guiAppHtml = `<!doctype html>
|
|
|
6390
6418
|
if (isSecretColumn(tableName, c) && r[c] != null && r[c] !== '') {
|
|
6391
6419
|
return '<td class="muted">' + SECRET_MASK + '</td>';
|
|
6392
6420
|
}
|
|
6393
|
-
return '<td>' + escapeHtml(truncate(r[c], 120)) + '</td>';
|
|
6421
|
+
return '<td><div class="cell-clip">' + escapeHtml(truncate(r[c], 120)) + '</div></td>';
|
|
6394
6422
|
});
|
|
6395
6423
|
belongsTo.forEach(function (b) {
|
|
6396
6424
|
var ref = (loadedTables[b.rel.table] || []).find(function (x) { return x.id === r[b.rel.foreignKey]; });
|
|
6397
|
-
tds.push('<td>' + chipLink(b.rel.table, ref) + '</td>');
|
|
6425
|
+
tds.push('<td><div class="cell-clip">' + chipLink(b.rel.table, ref) + '</div></td>');
|
|
6398
6426
|
});
|
|
6399
6427
|
junctions.forEach(function (j) {
|
|
6400
6428
|
var matches = (loadedTables[j.junction] || []).filter(function (jr) { return jr[j.localFk] === r.id; });
|
|
@@ -6403,7 +6431,7 @@ var guiAppHtml = `<!doctype html>
|
|
|
6403
6431
|
var ref = (loadedTables[j.remoteRel.table] || []).find(function (x) { return x.id === jr[remoteFkCol]; });
|
|
6404
6432
|
return ref ? chipLink(j.remoteRel.table, ref) : '';
|
|
6405
6433
|
}).join('');
|
|
6406
|
-
tds.push('<td>' + (chips || '<span class="muted">\u2014</span>') + '</td>');
|
|
6434
|
+
tds.push('<td><div class="cell-clip">' + (chips || '<span class="muted">\u2014</span>') + '</div></td>');
|
|
6407
6435
|
});
|
|
6408
6436
|
if (viewMode === 'trash') {
|
|
6409
6437
|
tds.push('<td class="row-actions">' +
|
|
@@ -8703,7 +8731,7 @@ function sendJson(res, body, status = 200) {
|
|
|
8703
8731
|
res.end(JSON.stringify(body));
|
|
8704
8732
|
}
|
|
8705
8733
|
function readJson(req) {
|
|
8706
|
-
return new Promise((
|
|
8734
|
+
return new Promise((resolve10, reject) => {
|
|
8707
8735
|
let raw = "";
|
|
8708
8736
|
req.setEncoding("utf8");
|
|
8709
8737
|
req.on("data", (chunk) => {
|
|
@@ -8712,7 +8740,7 @@ function readJson(req) {
|
|
|
8712
8740
|
});
|
|
8713
8741
|
req.on("end", () => {
|
|
8714
8742
|
try {
|
|
8715
|
-
|
|
8743
|
+
resolve10(raw ? JSON.parse(raw) : {});
|
|
8716
8744
|
} catch (e) {
|
|
8717
8745
|
reject(new Error(`Invalid JSON body: ${e.message}`));
|
|
8718
8746
|
}
|
|
@@ -10403,7 +10431,7 @@ function sendJson2(res, body, status = 200) {
|
|
|
10403
10431
|
res.end(JSON.stringify(body));
|
|
10404
10432
|
}
|
|
10405
10433
|
function readJson2(req) {
|
|
10406
|
-
return new Promise((
|
|
10434
|
+
return new Promise((resolve10, reject) => {
|
|
10407
10435
|
let raw = "";
|
|
10408
10436
|
req.setEncoding("utf8");
|
|
10409
10437
|
req.on("data", (chunk) => {
|
|
@@ -10412,7 +10440,7 @@ function readJson2(req) {
|
|
|
10412
10440
|
});
|
|
10413
10441
|
req.on("end", () => {
|
|
10414
10442
|
try {
|
|
10415
|
-
|
|
10443
|
+
resolve10(raw ? JSON.parse(raw) : {});
|
|
10416
10444
|
} catch (e) {
|
|
10417
10445
|
reject(new Error(`Invalid JSON body: ${e.message}`));
|
|
10418
10446
|
}
|
|
@@ -10676,7 +10704,7 @@ function sendJson3(res, body, status = 200) {
|
|
|
10676
10704
|
res.end(JSON.stringify(body));
|
|
10677
10705
|
}
|
|
10678
10706
|
function readJson3(req) {
|
|
10679
|
-
return new Promise((
|
|
10707
|
+
return new Promise((resolve10, reject) => {
|
|
10680
10708
|
let raw = "";
|
|
10681
10709
|
req.setEncoding("utf8");
|
|
10682
10710
|
req.on("data", (chunk) => {
|
|
@@ -10685,7 +10713,7 @@ function readJson3(req) {
|
|
|
10685
10713
|
});
|
|
10686
10714
|
req.on("end", () => {
|
|
10687
10715
|
try {
|
|
10688
|
-
|
|
10716
|
+
resolve10(raw ? JSON.parse(raw) : {});
|
|
10689
10717
|
} catch (e) {
|
|
10690
10718
|
reject(new Error(`Invalid JSON body: ${e.message}`));
|
|
10691
10719
|
}
|
|
@@ -11520,15 +11548,43 @@ async function applyForward(db, entry) {
|
|
|
11520
11548
|
}
|
|
11521
11549
|
void before;
|
|
11522
11550
|
}
|
|
11523
|
-
function
|
|
11524
|
-
const
|
|
11525
|
-
const
|
|
11551
|
+
function deriveSlugFromManifest(row, knownSlugs) {
|
|
11552
|
+
const candidateFields = ["slug", "id", "name"];
|
|
11553
|
+
for (const field of candidateFields) {
|
|
11554
|
+
const value = row[field];
|
|
11555
|
+
if (typeof value === "string" && knownSlugs.has(value)) return value;
|
|
11556
|
+
}
|
|
11557
|
+
return null;
|
|
11558
|
+
}
|
|
11559
|
+
function buildRowContextLocator(table, row, schemaDef, manifest) {
|
|
11560
|
+
if (schemaDef) {
|
|
11561
|
+
return {
|
|
11562
|
+
directoryRoot: schemaDef.directoryRoot ?? "",
|
|
11563
|
+
slug: schemaDef.slug(row),
|
|
11564
|
+
fileNames: Object.keys(schemaDef.files)
|
|
11565
|
+
};
|
|
11566
|
+
}
|
|
11567
|
+
const manifestEntry = manifest?.entityContexts[table];
|
|
11568
|
+
if (!manifestEntry) return null;
|
|
11569
|
+
const knownSlugs = new Set(Object.keys(manifestEntry.entities));
|
|
11570
|
+
const derivedSlug = deriveSlugFromManifest(row, knownSlugs);
|
|
11571
|
+
if (!derivedSlug) return null;
|
|
11572
|
+
const entityFiles = manifestEntry.entities[derivedSlug];
|
|
11573
|
+
const fileNames = entityFiles ? entityFileNames(entityFiles) : manifestEntry.declaredFiles;
|
|
11574
|
+
return {
|
|
11575
|
+
directoryRoot: manifestEntry.directoryRoot,
|
|
11576
|
+
slug: derivedSlug,
|
|
11577
|
+
fileNames
|
|
11578
|
+
};
|
|
11579
|
+
}
|
|
11580
|
+
function readRowContext(outputDir, locator, secretCols) {
|
|
11581
|
+
const { slug, directoryRoot, fileNames } = locator;
|
|
11526
11582
|
const entityDir = resolve6(outputDir, directoryRoot, slug);
|
|
11527
11583
|
const resolvedBase = resolve6(outputDir);
|
|
11528
11584
|
if (entityDir !== resolvedBase && !entityDir.startsWith(resolvedBase + sep3)) {
|
|
11529
11585
|
throw new Error(`Path traversal detected: slug "${slug}" escapes output directory`);
|
|
11530
11586
|
}
|
|
11531
|
-
return
|
|
11587
|
+
return fileNames.map((filename) => {
|
|
11532
11588
|
const absPath = join13(entityDir, filename);
|
|
11533
11589
|
const relPath = join13(directoryRoot, slug, filename);
|
|
11534
11590
|
if (!existsSync14(absPath)) return { name: filename, path: relPath, content: "" };
|
|
@@ -11598,10 +11654,8 @@ async function openConfig(configPath, outputDir) {
|
|
|
11598
11654
|
const junctionTables = new Set(
|
|
11599
11655
|
getGuiEntities(configPath, outputDir).tables.filter(isJunctionTable).map((t) => t.name)
|
|
11600
11656
|
);
|
|
11601
|
-
const entityContextByTable =
|
|
11602
|
-
|
|
11603
|
-
entityContextByTable.set(table, definition);
|
|
11604
|
-
}
|
|
11657
|
+
const entityContextByTable = db.entityContexts();
|
|
11658
|
+
const manifest = readManifest(outputDir);
|
|
11605
11659
|
const softDeletable = new Set(
|
|
11606
11660
|
parsed.tables.filter(({ definition }) => "deleted_at" in definition.columns).map(({ name }) => name)
|
|
11607
11661
|
);
|
|
@@ -11615,6 +11669,7 @@ async function openConfig(configPath, outputDir) {
|
|
|
11615
11669
|
validTables,
|
|
11616
11670
|
junctionTables,
|
|
11617
11671
|
entityContextByTable,
|
|
11672
|
+
manifest,
|
|
11618
11673
|
softDeletable
|
|
11619
11674
|
};
|
|
11620
11675
|
}
|
|
@@ -12116,16 +12171,17 @@ async function startGuiServer(options) {
|
|
|
12116
12171
|
sendJson5(res, { error: `Unknown table: ${ctxTable}` }, 400);
|
|
12117
12172
|
return;
|
|
12118
12173
|
}
|
|
12119
|
-
const def = active.entityContextByTable.get(ctxTable);
|
|
12120
|
-
if (!def) {
|
|
12121
|
-
sendJson5(res, { files: [] });
|
|
12122
|
-
return;
|
|
12123
|
-
}
|
|
12124
12174
|
const row = await active.db.get(ctxTable, ctxId);
|
|
12125
12175
|
if (row === null) {
|
|
12126
12176
|
sendJson5(res, { error: "Row not found" }, 404);
|
|
12127
12177
|
return;
|
|
12128
12178
|
}
|
|
12179
|
+
const def = active.entityContextByTable.get(ctxTable);
|
|
12180
|
+
const locator = buildRowContextLocator(ctxTable, row, def, active.manifest);
|
|
12181
|
+
if (!locator) {
|
|
12182
|
+
sendJson5(res, { files: [] });
|
|
12183
|
+
return;
|
|
12184
|
+
}
|
|
12129
12185
|
const colMetaRows = await active.db.query("_lattice_gui_column_meta", {
|
|
12130
12186
|
filters: [
|
|
12131
12187
|
{ col: "table_name", op: "eq", val: ctxTable },
|
|
@@ -12133,7 +12189,7 @@ async function startGuiServer(options) {
|
|
|
12133
12189
|
]
|
|
12134
12190
|
});
|
|
12135
12191
|
const secretCols = new Set(colMetaRows.map((r) => r.column_name));
|
|
12136
|
-
sendJson5(res, { files: readRowContext(active.outputDir,
|
|
12192
|
+
sendJson5(res, { files: readRowContext(active.outputDir, locator, secretCols) });
|
|
12137
12193
|
return;
|
|
12138
12194
|
}
|
|
12139
12195
|
const rowsMatch = ROWS_PATH.exec(pathname);
|
|
@@ -12288,8 +12344,20 @@ async function startGuiServer(options) {
|
|
|
12288
12344
|
};
|
|
12289
12345
|
}
|
|
12290
12346
|
|
|
12347
|
+
// src/gui/discover-output-dir.ts
|
|
12348
|
+
import { existsSync as existsSync15 } from "fs";
|
|
12349
|
+
import { join as join14, resolve as resolve7 } from "path";
|
|
12350
|
+
function discoverOutputDir(explicitOutput, explicit) {
|
|
12351
|
+
if (explicit) return explicitOutput;
|
|
12352
|
+
const candidates = ["./context", ".", "./generated"];
|
|
12353
|
+
for (const dir of candidates) {
|
|
12354
|
+
if (existsSync15(join14(resolve7(dir), ".lattice", "manifest.json"))) return dir;
|
|
12355
|
+
}
|
|
12356
|
+
return explicitOutput;
|
|
12357
|
+
}
|
|
12358
|
+
|
|
12291
12359
|
// src/teams/cli-commands.ts
|
|
12292
|
-
import { resolve as
|
|
12360
|
+
import { resolve as resolve8 } from "path";
|
|
12293
12361
|
var TEAMS_USAGE = [
|
|
12294
12362
|
"lattice teams <subcommand> [options]",
|
|
12295
12363
|
"",
|
|
@@ -12405,7 +12473,7 @@ function requireArg(args, key, label) {
|
|
|
12405
12473
|
return v.trim();
|
|
12406
12474
|
}
|
|
12407
12475
|
async function openLocal(configPath) {
|
|
12408
|
-
const db = new Lattice({ config:
|
|
12476
|
+
const db = new Lattice({ config: resolve8(configPath) });
|
|
12409
12477
|
await db.init();
|
|
12410
12478
|
return db;
|
|
12411
12479
|
}
|
|
@@ -12729,6 +12797,7 @@ function parseArgs(argv) {
|
|
|
12729
12797
|
let config = "./lattice.config.yml";
|
|
12730
12798
|
let out = "./generated";
|
|
12731
12799
|
let output = "./context";
|
|
12800
|
+
let outputExplicit = false;
|
|
12732
12801
|
let scaffold = false;
|
|
12733
12802
|
let help = false;
|
|
12734
12803
|
let version = false;
|
|
@@ -12779,6 +12848,7 @@ function parseArgs(argv) {
|
|
|
12779
12848
|
} else if ((arg === "--output" || arg === "--output-dir") && i + 1 < argv.length) {
|
|
12780
12849
|
i++;
|
|
12781
12850
|
output = argv[i] ?? output;
|
|
12851
|
+
outputExplicit = true;
|
|
12782
12852
|
} else if (arg === "--scaffold") {
|
|
12783
12853
|
scaffold = true;
|
|
12784
12854
|
} else if (arg === "--dry-run") {
|
|
@@ -12854,6 +12924,7 @@ function parseArgs(argv) {
|
|
|
12854
12924
|
config,
|
|
12855
12925
|
out,
|
|
12856
12926
|
output,
|
|
12927
|
+
outputExplicit,
|
|
12857
12928
|
scaffold,
|
|
12858
12929
|
help,
|
|
12859
12930
|
version,
|
|
@@ -12979,7 +13050,7 @@ async function runUpdate() {
|
|
|
12979
13050
|
}
|
|
12980
13051
|
}
|
|
12981
13052
|
function runGenerate(args) {
|
|
12982
|
-
const configPath =
|
|
13053
|
+
const configPath = resolve9(args.config);
|
|
12983
13054
|
let raw;
|
|
12984
13055
|
try {
|
|
12985
13056
|
raw = readFileSync12(configPath, "utf-8");
|
|
@@ -12999,7 +13070,7 @@ function runGenerate(args) {
|
|
|
12999
13070
|
process.exit(1);
|
|
13000
13071
|
}
|
|
13001
13072
|
const configDir2 = dirname7(configPath);
|
|
13002
|
-
const outDir =
|
|
13073
|
+
const outDir = resolve9(args.out);
|
|
13003
13074
|
try {
|
|
13004
13075
|
const result = generateAll({ config, configDir: configDir2, outDir, scaffold: args.scaffold });
|
|
13005
13076
|
console.log(`Generated ${String(result.filesWritten.length)} file(s):`);
|
|
@@ -13012,15 +13083,15 @@ function runGenerate(args) {
|
|
|
13012
13083
|
}
|
|
13013
13084
|
}
|
|
13014
13085
|
async function runRender(args) {
|
|
13015
|
-
const outputDir =
|
|
13086
|
+
const outputDir = resolve9(args.output);
|
|
13016
13087
|
let parsed;
|
|
13017
13088
|
try {
|
|
13018
|
-
parsed = parseConfigFile(
|
|
13089
|
+
parsed = parseConfigFile(resolve9(args.config));
|
|
13019
13090
|
} catch (e) {
|
|
13020
13091
|
console.error(`Error: ${e.message}`);
|
|
13021
13092
|
process.exit(1);
|
|
13022
13093
|
}
|
|
13023
|
-
const db = new Lattice({ config:
|
|
13094
|
+
const db = new Lattice({ config: resolve9(args.config) });
|
|
13024
13095
|
try {
|
|
13025
13096
|
await db.init();
|
|
13026
13097
|
const start = Date.now();
|
|
@@ -13039,8 +13110,8 @@ async function runRender(args) {
|
|
|
13039
13110
|
void parsed;
|
|
13040
13111
|
}
|
|
13041
13112
|
async function runReconcile(args, isDryRun) {
|
|
13042
|
-
const outputDir =
|
|
13043
|
-
const db = new Lattice({ config:
|
|
13113
|
+
const outputDir = resolve9(args.output);
|
|
13114
|
+
const db = new Lattice({ config: resolve9(args.config) });
|
|
13044
13115
|
try {
|
|
13045
13116
|
await db.init();
|
|
13046
13117
|
const start = Date.now();
|
|
@@ -13099,8 +13170,8 @@ function formatTimestamp() {
|
|
|
13099
13170
|
return `${hh}:${mm}:${ss}`;
|
|
13100
13171
|
}
|
|
13101
13172
|
async function runWatch(args) {
|
|
13102
|
-
const outputDir =
|
|
13103
|
-
const db = new Lattice({ config:
|
|
13173
|
+
const outputDir = resolve9(args.output);
|
|
13174
|
+
const db = new Lattice({ config: resolve9(args.config) });
|
|
13104
13175
|
try {
|
|
13105
13176
|
await db.init();
|
|
13106
13177
|
} catch (e) {
|
|
@@ -13141,9 +13212,15 @@ async function runWatch(args) {
|
|
|
13141
13212
|
}
|
|
13142
13213
|
async function runGui(args) {
|
|
13143
13214
|
try {
|
|
13215
|
+
const resolvedOutput = discoverOutputDir(args.output, args.outputExplicit);
|
|
13216
|
+
if (!args.outputExplicit && resolvedOutput !== args.output) {
|
|
13217
|
+
console.log(
|
|
13218
|
+
`Lattice GUI: auto-detected rendered context at "${resolvedOutput}" (use --output to override).`
|
|
13219
|
+
);
|
|
13220
|
+
}
|
|
13144
13221
|
const handle = await startGuiServer({
|
|
13145
|
-
configPath:
|
|
13146
|
-
outputDir:
|
|
13222
|
+
configPath: resolve9(args.config),
|
|
13223
|
+
outputDir: resolve9(resolvedOutput),
|
|
13147
13224
|
port: args.port,
|
|
13148
13225
|
openBrowser: !args.noOpen
|
|
13149
13226
|
});
|
|
@@ -13162,8 +13239,8 @@ async function runGui(args) {
|
|
|
13162
13239
|
async function runServe(args) {
|
|
13163
13240
|
try {
|
|
13164
13241
|
const handle = await startGuiServer({
|
|
13165
|
-
configPath:
|
|
13166
|
-
outputDir:
|
|
13242
|
+
configPath: resolve9(args.config),
|
|
13243
|
+
outputDir: resolve9(args.output),
|
|
13167
13244
|
host: args.host,
|
|
13168
13245
|
port: args.port,
|
|
13169
13246
|
openBrowser: false,
|
package/dist/index.cjs
CHANGED
|
@@ -3434,6 +3434,16 @@ var Lattice = class {
|
|
|
3434
3434
|
this._schema.defineEntityContext(table, def);
|
|
3435
3435
|
return this;
|
|
3436
3436
|
}
|
|
3437
|
+
/**
|
|
3438
|
+
* All entity contexts currently registered on this Lattice — both those
|
|
3439
|
+
* declared in `lattice.config.yml` and those added programmatically via
|
|
3440
|
+
* `defineEntityContext()`.
|
|
3441
|
+
*
|
|
3442
|
+
* Returns a defensive copy so callers can't mutate the schema.
|
|
3443
|
+
*/
|
|
3444
|
+
entityContexts() {
|
|
3445
|
+
return new Map(this._schema.getEntityContexts());
|
|
3446
|
+
}
|
|
3437
3447
|
/**
|
|
3438
3448
|
* Register a write hook that fires after insert/update/delete operations.
|
|
3439
3449
|
* Hooks run synchronously after the DB write and audit emit.
|
package/dist/index.d.cts
CHANGED
|
@@ -1608,6 +1608,14 @@ declare class Lattice {
|
|
|
1608
1608
|
private _registerTable;
|
|
1609
1609
|
defineMulti(name: string, def: MultiTableDefinition): this;
|
|
1610
1610
|
defineEntityContext(table: string, def: EntityContextDefinition): this;
|
|
1611
|
+
/**
|
|
1612
|
+
* All entity contexts currently registered on this Lattice — both those
|
|
1613
|
+
* declared in `lattice.config.yml` and those added programmatically via
|
|
1614
|
+
* `defineEntityContext()`.
|
|
1615
|
+
*
|
|
1616
|
+
* Returns a defensive copy so callers can't mutate the schema.
|
|
1617
|
+
*/
|
|
1618
|
+
entityContexts(): Map<string, EntityContextDefinition>;
|
|
1611
1619
|
/**
|
|
1612
1620
|
* Register a write hook that fires after insert/update/delete operations.
|
|
1613
1621
|
* Hooks run synchronously after the DB write and audit emit.
|
|
@@ -2731,8 +2739,8 @@ declare function attachBlob(srcPath: string, latticeRoot: string): Promise<BlobM
|
|
|
2731
2739
|
* keys/<label>.token per-joined-team bearer tokens (added later).
|
|
2732
2740
|
* db-credentials.enc encrypted Postgres URLs (added later).
|
|
2733
2741
|
*
|
|
2734
|
-
*
|
|
2735
|
-
*
|
|
2742
|
+
* Security: do NOT log filesystem paths or user identity values from
|
|
2743
|
+
* this module. Errors must be thrown without echoing sensitive arguments.
|
|
2736
2744
|
*/
|
|
2737
2745
|
/** Root directory for machine-local lattice config. Override via env. */
|
|
2738
2746
|
declare function configDir(): string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1608,6 +1608,14 @@ declare class Lattice {
|
|
|
1608
1608
|
private _registerTable;
|
|
1609
1609
|
defineMulti(name: string, def: MultiTableDefinition): this;
|
|
1610
1610
|
defineEntityContext(table: string, def: EntityContextDefinition): this;
|
|
1611
|
+
/**
|
|
1612
|
+
* All entity contexts currently registered on this Lattice — both those
|
|
1613
|
+
* declared in `lattice.config.yml` and those added programmatically via
|
|
1614
|
+
* `defineEntityContext()`.
|
|
1615
|
+
*
|
|
1616
|
+
* Returns a defensive copy so callers can't mutate the schema.
|
|
1617
|
+
*/
|
|
1618
|
+
entityContexts(): Map<string, EntityContextDefinition>;
|
|
1611
1619
|
/**
|
|
1612
1620
|
* Register a write hook that fires after insert/update/delete operations.
|
|
1613
1621
|
* Hooks run synchronously after the DB write and audit emit.
|
|
@@ -2731,8 +2739,8 @@ declare function attachBlob(srcPath: string, latticeRoot: string): Promise<BlobM
|
|
|
2731
2739
|
* keys/<label>.token per-joined-team bearer tokens (added later).
|
|
2732
2740
|
* db-credentials.enc encrypted Postgres URLs (added later).
|
|
2733
2741
|
*
|
|
2734
|
-
*
|
|
2735
|
-
*
|
|
2742
|
+
* Security: do NOT log filesystem paths or user identity values from
|
|
2743
|
+
* this module. Errors must be thrown without echoing sensitive arguments.
|
|
2736
2744
|
*/
|
|
2737
2745
|
/** Root directory for machine-local lattice config. Override via env. */
|
|
2738
2746
|
declare function configDir(): string;
|
package/dist/index.js
CHANGED
|
@@ -3362,6 +3362,16 @@ var Lattice = class {
|
|
|
3362
3362
|
this._schema.defineEntityContext(table, def);
|
|
3363
3363
|
return this;
|
|
3364
3364
|
}
|
|
3365
|
+
/**
|
|
3366
|
+
* All entity contexts currently registered on this Lattice — both those
|
|
3367
|
+
* declared in `lattice.config.yml` and those added programmatically via
|
|
3368
|
+
* `defineEntityContext()`.
|
|
3369
|
+
*
|
|
3370
|
+
* Returns a defensive copy so callers can't mutate the schema.
|
|
3371
|
+
*/
|
|
3372
|
+
entityContexts() {
|
|
3373
|
+
return new Map(this._schema.getEntityContexts());
|
|
3374
|
+
}
|
|
3365
3375
|
/**
|
|
3366
3376
|
* Register a write hook that fires after insert/update/delete operations.
|
|
3367
3377
|
* Hooks run synchronously after the DB write and audit emit.
|
package/package.json
CHANGED