latticesql 3.3.2 → 3.3.4

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 CHANGED
@@ -1061,13 +1061,24 @@ function moduleContext() {
1061
1061
  return _moduleContext;
1062
1062
  }
1063
1063
  async function registerPostgresPolyfills(run) {
1064
+ let permissionDenied = false;
1064
1065
  for (const { warn, sql } of POSTGRES_POLYFILLS) {
1065
1066
  try {
1066
1067
  await run(sql);
1067
1068
  } catch (err) {
1068
- console.warn(`[PostgresAdapter] ${warn}`, err instanceof Error ? err.message : err);
1069
+ const msg = err instanceof Error ? err.message : String(err);
1070
+ if (/permission denied/i.test(msg)) {
1071
+ permissionDenied = true;
1072
+ } else {
1073
+ console.warn(`[PostgresAdapter] ${warn}`, msg);
1074
+ }
1069
1075
  }
1070
1076
  }
1077
+ if (permissionDenied) {
1078
+ console.debug(
1079
+ "[PostgresAdapter] SQLite-compat polyfills are owner-managed on this cloud; skipping member-side (re)creation (expected)."
1080
+ );
1081
+ }
1071
1082
  }
1072
1083
  function translateDialect(sql) {
1073
1084
  if (/INSERT\s+OR\s+REPLACE\s+INTO/i.test(sql)) {
@@ -5103,7 +5114,23 @@ var init_lattice = __esm({
5103
5114
  }
5104
5115
  /** Async tail of init(). See {@link init} for the sync-validation phase. */
5105
5116
  async _initAsync(options) {
5106
- if (options.introspectOnly) {
5117
+ let introspectOnly = options.introspectOnly === true;
5118
+ if (!introspectOnly && this.getDialect() === "postgres") {
5119
+ try {
5120
+ const [marker, role] = await Promise.all([
5121
+ getAsyncOrSync(this._adapter, `SELECT to_regclass('__lattice_owners') AS reg`),
5122
+ getAsyncOrSync(
5123
+ this._adapter,
5124
+ `SELECT rolcreaterole FROM pg_roles WHERE rolname = current_user`
5125
+ )
5126
+ ]);
5127
+ const provisioned = !!marker && marker.reg != null;
5128
+ const canCreateRoles = !!role && role.rolcreaterole === true;
5129
+ introspectOnly = provisioned && !canCreateRoles;
5130
+ } catch {
5131
+ }
5132
+ }
5133
+ if (introspectOnly) {
5107
5134
  for (const tableName of this._schema.getTables().keys()) {
5108
5135
  try {
5109
5136
  const cols = await introspectColumnsAsyncOrSync(this._adapter, tableName);
@@ -57051,7 +57078,7 @@ var appJs = `
57051
57078
  '<div class="view-header">' +
57052
57079
  '<span class="entity-icon">\u2699</span>' +
57053
57080
  '<h1>' + escapeHtml(tableName) + '</h1>' +
57054
- '<span class="count">' + entry.rowCount + ' row' + (entry.rowCount === 1 ? '' : 's') +
57081
+ '<span class="count">' + (entry.rowCount == null ? 'no access' : (entry.rowCount + ' row' + (entry.rowCount === 1 ? '' : 's'))) +
57055
57082
  ' \xB7 read-only</span>' +
57056
57083
  '</div>' +
57057
57084
  '<div class="muted" style="margin-bottom:12px;font-size:13px;">' +
@@ -61766,6 +61793,52 @@ async function reconcileCloudMemberAccess(db) {
61766
61793
  );
61767
61794
  }
61768
61795
  }
61796
+ const memberSystemGrants = [
61797
+ ["_lattice_gui_meta", "SELECT, INSERT, UPDATE"],
61798
+ ["_lattice_gui_column_meta", "SELECT, INSERT, UPDATE"],
61799
+ ["_lattice_gui_audit", "SELECT, INSERT"],
61800
+ ["__lattice_user_identity", "SELECT, INSERT, UPDATE"]
61801
+ ];
61802
+ for (const [tbl, privs] of memberSystemGrants) {
61803
+ await runAsyncOrSync(
61804
+ db.adapter,
61805
+ `DO $LATTICE$ BEGIN
61806
+ IF to_regclass('${tbl}') IS NOT NULL THEN
61807
+ EXECUTE 'GRANT ${privs} ON "${tbl}" TO ${MEMBER_GROUP}';
61808
+ END IF;
61809
+ END $LATTICE$`
61810
+ );
61811
+ }
61812
+ try {
61813
+ await runAsyncOrSync(
61814
+ db.adapter,
61815
+ `GRANT EXECUTE ON FUNCTION json_extract(text, text), strftime(text, text) TO ${MEMBER_GROUP}`
61816
+ );
61817
+ } catch (err) {
61818
+ console.warn(
61819
+ "[reconcileCloudMemberAccess] could not grant EXECUTE on polyfills (will retry next open):",
61820
+ err instanceof Error ? err.message : String(err)
61821
+ );
61822
+ }
61823
+ for (const table of registered) {
61824
+ if (table.startsWith("__lattice_") || table.startsWith("_lattice_")) continue;
61825
+ const cols = db.getRegisteredColumns(table);
61826
+ if (cols && !("deleted_at" in cols)) {
61827
+ const q3 = `"${table.replace(/"/g, '""')}"`;
61828
+ await runAsyncOrSync(
61829
+ db.adapter,
61830
+ `ALTER TABLE ${q3} ADD COLUMN IF NOT EXISTS "deleted_at" TEXT`
61831
+ );
61832
+ }
61833
+ }
61834
+ await runAsyncOrSync(
61835
+ db.adapter,
61836
+ `DO $LATTICE$ BEGIN
61837
+ IF to_regclass('__lattice_changelog') IS NOT NULL THEN
61838
+ EXECUTE 'GRANT SELECT, INSERT ON "__lattice_changelog" TO ${MEMBER_GROUP}';
61839
+ END IF;
61840
+ END $LATTICE$`
61841
+ );
61769
61842
  }
61770
61843
  async function secureNewCloudTable(db, table, pk) {
61771
61844
  if (db.getDialect() !== "postgres") return;
@@ -61791,14 +61864,6 @@ async function secureCloud(db) {
61791
61864
  await secureNewCloudTable(db, table, db.getPrimaryKey(table));
61792
61865
  }
61793
61866
  await reconcileCloudMemberAccess(db);
61794
- await runAsyncOrSync(
61795
- db.adapter,
61796
- `DO $LATTICE$ BEGIN
61797
- IF to_regclass('__lattice_user_identity') IS NOT NULL THEN
61798
- EXECUTE 'GRANT SELECT, INSERT, UPDATE ON "__lattice_user_identity" TO ${MEMBER_GROUP}';
61799
- END IF;
61800
- END $LATTICE$`
61801
- );
61802
61867
  }
61803
61868
 
61804
61869
  // src/gui/meta-gen.ts
@@ -65307,6 +65372,7 @@ async function openConfig(configPath, outputDir, autoRender = false, realtimeWat
65307
65372
  ]);
65308
65373
  const views = viewsRaw;
65309
65374
  const knownTables = /* @__PURE__ */ new Set([...declared, ...discovered.map((t8) => t8.name)]);
65375
+ const memberEntityDefs = [];
65310
65376
  for (const t8 of discovered) {
65311
65377
  if (declared.has(t8.name)) continue;
65312
65378
  if (t8.columns.length === 0) continue;
@@ -65314,17 +65380,25 @@ async function openConfig(configPath, outputDir, autoRender = false, realtimeWat
65314
65380
  discoveredJunctions.add(t8.name);
65315
65381
  continue;
65316
65382
  }
65317
- db.define(t8.name, {
65383
+ const def = {
65318
65384
  columns: Object.fromEntries(t8.columns.map((c6) => [c6, "TEXT"])),
65319
65385
  ...t8.pk.length > 0 ? { primaryKey: t8.pk.length === 1 ? t8.pk[0] : t8.pk } : {},
65320
65386
  render: () => "",
65321
65387
  outputFile: `${t8.name}/.lattice/${t8.name}.md`
65322
- });
65388
+ };
65389
+ db.define(t8.name, def);
65390
+ memberEntityDefs.push({ name: t8.name, definition: def });
65323
65391
  }
65324
65392
  for (const { name } of views) {
65325
65393
  const base = name.slice(0, -2);
65326
65394
  if (knownTables.has(base)) maskedReadViews.set(base, name);
65327
65395
  }
65396
+ if (autoRender && memberEntityDefs.length > 0) {
65397
+ const existingContexts = db.entityContexts();
65398
+ for (const { table, definition } of deriveCanonicalContexts(memberEntityDefs)) {
65399
+ if (!existingContexts.has(table)) db.defineEntityContext(table, definition);
65400
+ }
65401
+ }
65328
65402
  }
65329
65403
  }
65330
65404
  } catch {
@@ -66815,9 +66889,18 @@ async function startGuiServer(options) {
66815
66889
  }
66816
66890
  const tables = [];
66817
66891
  for (const r6 of rows) {
66818
- const cols = await active.db.introspectColumns(r6.name);
66819
- const rowCount = await active.db.count(r6.name);
66820
- tables.push({ name: r6.name, columns: cols, rowCount });
66892
+ try {
66893
+ const cols = await active.db.introspectColumns(r6.name);
66894
+ const rowCount = await active.db.count(r6.name);
66895
+ tables.push({ name: r6.name, columns: cols, rowCount });
66896
+ } catch (err) {
66897
+ const msg = err instanceof Error ? err.message : String(err);
66898
+ if (/permission denied|does not exist/i.test(msg)) {
66899
+ tables.push({ name: r6.name, columns: [], rowCount: null });
66900
+ } else {
66901
+ throw err;
66902
+ }
66903
+ }
66821
66904
  }
66822
66905
  sendJson(res, { tables });
66823
66906
  return;
@@ -67783,7 +67866,7 @@ function printHelp() {
67783
67866
  );
67784
67867
  }
67785
67868
  function getVersion() {
67786
- if (true) return "3.3.2";
67869
+ if (true) return "3.3.4";
67787
67870
  try {
67788
67871
  const pkgPath = new URL("../package.json", import.meta.url).pathname;
67789
67872
  const pkg = JSON.parse(readFileSync18(pkgPath, "utf-8"));
package/dist/index.cjs CHANGED
@@ -437,13 +437,24 @@ function moduleContext() {
437
437
  return _moduleContext;
438
438
  }
439
439
  async function registerPostgresPolyfills(run) {
440
+ let permissionDenied = false;
440
441
  for (const { warn, sql } of POSTGRES_POLYFILLS) {
441
442
  try {
442
443
  await run(sql);
443
444
  } catch (err) {
444
- console.warn(`[PostgresAdapter] ${warn}`, err instanceof Error ? err.message : err);
445
+ const msg = err instanceof Error ? err.message : String(err);
446
+ if (/permission denied/i.test(msg)) {
447
+ permissionDenied = true;
448
+ } else {
449
+ console.warn(`[PostgresAdapter] ${warn}`, msg);
450
+ }
445
451
  }
446
452
  }
453
+ if (permissionDenied) {
454
+ console.debug(
455
+ "[PostgresAdapter] SQLite-compat polyfills are owner-managed on this cloud; skipping member-side (re)creation (expected)."
456
+ );
457
+ }
447
458
  }
448
459
  function translateDialect(sql) {
449
460
  if (/INSERT\s+OR\s+REPLACE\s+INTO/i.test(sql)) {
@@ -5266,7 +5277,23 @@ var init_lattice = __esm({
5266
5277
  }
5267
5278
  /** Async tail of init(). See {@link init} for the sync-validation phase. */
5268
5279
  async _initAsync(options) {
5269
- if (options.introspectOnly) {
5280
+ let introspectOnly = options.introspectOnly === true;
5281
+ if (!introspectOnly && this.getDialect() === "postgres") {
5282
+ try {
5283
+ const [marker, role] = await Promise.all([
5284
+ getAsyncOrSync(this._adapter, `SELECT to_regclass('__lattice_owners') AS reg`),
5285
+ getAsyncOrSync(
5286
+ this._adapter,
5287
+ `SELECT rolcreaterole FROM pg_roles WHERE rolname = current_user`
5288
+ )
5289
+ ]);
5290
+ const provisioned = !!marker && marker.reg != null;
5291
+ const canCreateRoles = !!role && role.rolcreaterole === true;
5292
+ introspectOnly = provisioned && !canCreateRoles;
5293
+ } catch {
5294
+ }
5295
+ }
5296
+ if (introspectOnly) {
5270
5297
  for (const tableName of this._schema.getTables().keys()) {
5271
5298
  try {
5272
5299
  const cols = await introspectColumnsAsyncOrSync(this._adapter, tableName);
@@ -54016,6 +54043,52 @@ async function reconcileCloudMemberAccess(db) {
54016
54043
  );
54017
54044
  }
54018
54045
  }
54046
+ const memberSystemGrants = [
54047
+ ["_lattice_gui_meta", "SELECT, INSERT, UPDATE"],
54048
+ ["_lattice_gui_column_meta", "SELECT, INSERT, UPDATE"],
54049
+ ["_lattice_gui_audit", "SELECT, INSERT"],
54050
+ ["__lattice_user_identity", "SELECT, INSERT, UPDATE"]
54051
+ ];
54052
+ for (const [tbl, privs] of memberSystemGrants) {
54053
+ await runAsyncOrSync(
54054
+ db.adapter,
54055
+ `DO $LATTICE$ BEGIN
54056
+ IF to_regclass('${tbl}') IS NOT NULL THEN
54057
+ EXECUTE 'GRANT ${privs} ON "${tbl}" TO ${MEMBER_GROUP}';
54058
+ END IF;
54059
+ END $LATTICE$`
54060
+ );
54061
+ }
54062
+ try {
54063
+ await runAsyncOrSync(
54064
+ db.adapter,
54065
+ `GRANT EXECUTE ON FUNCTION json_extract(text, text), strftime(text, text) TO ${MEMBER_GROUP}`
54066
+ );
54067
+ } catch (err) {
54068
+ console.warn(
54069
+ "[reconcileCloudMemberAccess] could not grant EXECUTE on polyfills (will retry next open):",
54070
+ err instanceof Error ? err.message : String(err)
54071
+ );
54072
+ }
54073
+ for (const table of registered) {
54074
+ if (table.startsWith("__lattice_") || table.startsWith("_lattice_")) continue;
54075
+ const cols = db.getRegisteredColumns(table);
54076
+ if (cols && !("deleted_at" in cols)) {
54077
+ const q3 = `"${table.replace(/"/g, '""')}"`;
54078
+ await runAsyncOrSync(
54079
+ db.adapter,
54080
+ `ALTER TABLE ${q3} ADD COLUMN IF NOT EXISTS "deleted_at" TEXT`
54081
+ );
54082
+ }
54083
+ }
54084
+ await runAsyncOrSync(
54085
+ db.adapter,
54086
+ `DO $LATTICE$ BEGIN
54087
+ IF to_regclass('__lattice_changelog') IS NOT NULL THEN
54088
+ EXECUTE 'GRANT SELECT, INSERT ON "__lattice_changelog" TO ${MEMBER_GROUP}';
54089
+ END IF;
54090
+ END $LATTICE$`
54091
+ );
54019
54092
  }
54020
54093
  async function secureNewCloudTable(db, table, pk) {
54021
54094
  if (db.getDialect() !== "postgres") return;
@@ -54041,14 +54114,6 @@ async function secureCloud(db) {
54041
54114
  await secureNewCloudTable(db, table, db.getPrimaryKey(table));
54042
54115
  }
54043
54116
  await reconcileCloudMemberAccess(db);
54044
- await runAsyncOrSync(
54045
- db.adapter,
54046
- `DO $LATTICE$ BEGIN
54047
- IF to_regclass('__lattice_user_identity') IS NOT NULL THEN
54048
- EXECUTE 'GRANT SELECT, INSERT, UPDATE ON "__lattice_user_identity" TO ${MEMBER_GROUP}';
54049
- END IF;
54050
- END $LATTICE$`
54051
- );
54052
54117
  }
54053
54118
 
54054
54119
  // src/index.ts
@@ -58921,7 +58986,7 @@ var appJs = `
58921
58986
  '<div class="view-header">' +
58922
58987
  '<span class="entity-icon">\u2699</span>' +
58923
58988
  '<h1>' + escapeHtml(tableName) + '</h1>' +
58924
- '<span class="count">' + entry.rowCount + ' row' + (entry.rowCount === 1 ? '' : 's') +
58989
+ '<span class="count">' + (entry.rowCount == null ? 'no access' : (entry.rowCount + ' row' + (entry.rowCount === 1 ? '' : 's'))) +
58925
58990
  ' \xB7 read-only</span>' +
58926
58991
  '</div>' +
58927
58992
  '<div class="muted" style="margin-bottom:12px;font-size:13px;">' +
@@ -66479,6 +66544,7 @@ async function openConfig(configPath, outputDir, autoRender = false, realtimeWat
66479
66544
  ]);
66480
66545
  const views = viewsRaw;
66481
66546
  const knownTables = /* @__PURE__ */ new Set([...declared, ...discovered.map((t8) => t8.name)]);
66547
+ const memberEntityDefs = [];
66482
66548
  for (const t8 of discovered) {
66483
66549
  if (declared.has(t8.name)) continue;
66484
66550
  if (t8.columns.length === 0) continue;
@@ -66486,17 +66552,25 @@ async function openConfig(configPath, outputDir, autoRender = false, realtimeWat
66486
66552
  discoveredJunctions.add(t8.name);
66487
66553
  continue;
66488
66554
  }
66489
- db.define(t8.name, {
66555
+ const def = {
66490
66556
  columns: Object.fromEntries(t8.columns.map((c6) => [c6, "TEXT"])),
66491
66557
  ...t8.pk.length > 0 ? { primaryKey: t8.pk.length === 1 ? t8.pk[0] : t8.pk } : {},
66492
66558
  render: () => "",
66493
66559
  outputFile: `${t8.name}/.lattice/${t8.name}.md`
66494
- });
66560
+ };
66561
+ db.define(t8.name, def);
66562
+ memberEntityDefs.push({ name: t8.name, definition: def });
66495
66563
  }
66496
66564
  for (const { name } of views) {
66497
66565
  const base = name.slice(0, -2);
66498
66566
  if (knownTables.has(base)) maskedReadViews.set(base, name);
66499
66567
  }
66568
+ if (autoRender && memberEntityDefs.length > 0) {
66569
+ const existingContexts = db.entityContexts();
66570
+ for (const { table, definition } of deriveCanonicalContexts(memberEntityDefs)) {
66571
+ if (!existingContexts.has(table)) db.defineEntityContext(table, definition);
66572
+ }
66573
+ }
66500
66574
  }
66501
66575
  }
66502
66576
  } catch {
@@ -67987,9 +68061,18 @@ async function startGuiServer(options) {
67987
68061
  }
67988
68062
  const tables = [];
67989
68063
  for (const r6 of rows) {
67990
- const cols = await active.db.introspectColumns(r6.name);
67991
- const rowCount = await active.db.count(r6.name);
67992
- tables.push({ name: r6.name, columns: cols, rowCount });
68064
+ try {
68065
+ const cols = await active.db.introspectColumns(r6.name);
68066
+ const rowCount = await active.db.count(r6.name);
68067
+ tables.push({ name: r6.name, columns: cols, rowCount });
68068
+ } catch (err) {
68069
+ const msg = err instanceof Error ? err.message : String(err);
68070
+ if (/permission denied|does not exist/i.test(msg)) {
68071
+ tables.push({ name: r6.name, columns: [], rowCount: null });
68072
+ } else {
68073
+ throw err;
68074
+ }
68075
+ }
67993
68076
  }
67994
68077
  sendJson(res, { tables });
67995
68078
  return;
package/dist/index.js CHANGED
@@ -430,13 +430,24 @@ function moduleContext() {
430
430
  return _moduleContext;
431
431
  }
432
432
  async function registerPostgresPolyfills(run) {
433
+ let permissionDenied = false;
433
434
  for (const { warn, sql } of POSTGRES_POLYFILLS) {
434
435
  try {
435
436
  await run(sql);
436
437
  } catch (err) {
437
- console.warn(`[PostgresAdapter] ${warn}`, err instanceof Error ? err.message : err);
438
+ const msg = err instanceof Error ? err.message : String(err);
439
+ if (/permission denied/i.test(msg)) {
440
+ permissionDenied = true;
441
+ } else {
442
+ console.warn(`[PostgresAdapter] ${warn}`, msg);
443
+ }
438
444
  }
439
445
  }
446
+ if (permissionDenied) {
447
+ console.debug(
448
+ "[PostgresAdapter] SQLite-compat polyfills are owner-managed on this cloud; skipping member-side (re)creation (expected)."
449
+ );
450
+ }
440
451
  }
441
452
  function translateDialect(sql) {
442
453
  if (/INSERT\s+OR\s+REPLACE\s+INTO/i.test(sql)) {
@@ -5262,7 +5273,23 @@ var init_lattice = __esm({
5262
5273
  }
5263
5274
  /** Async tail of init(). See {@link init} for the sync-validation phase. */
5264
5275
  async _initAsync(options) {
5265
- if (options.introspectOnly) {
5276
+ let introspectOnly = options.introspectOnly === true;
5277
+ if (!introspectOnly && this.getDialect() === "postgres") {
5278
+ try {
5279
+ const [marker, role] = await Promise.all([
5280
+ getAsyncOrSync(this._adapter, `SELECT to_regclass('__lattice_owners') AS reg`),
5281
+ getAsyncOrSync(
5282
+ this._adapter,
5283
+ `SELECT rolcreaterole FROM pg_roles WHERE rolname = current_user`
5284
+ )
5285
+ ]);
5286
+ const provisioned = !!marker && marker.reg != null;
5287
+ const canCreateRoles = !!role && role.rolcreaterole === true;
5288
+ introspectOnly = provisioned && !canCreateRoles;
5289
+ } catch {
5290
+ }
5291
+ }
5292
+ if (introspectOnly) {
5266
5293
  for (const tableName of this._schema.getTables().keys()) {
5267
5294
  try {
5268
5295
  const cols = await introspectColumnsAsyncOrSync(this._adapter, tableName);
@@ -53829,6 +53856,52 @@ async function reconcileCloudMemberAccess(db) {
53829
53856
  );
53830
53857
  }
53831
53858
  }
53859
+ const memberSystemGrants = [
53860
+ ["_lattice_gui_meta", "SELECT, INSERT, UPDATE"],
53861
+ ["_lattice_gui_column_meta", "SELECT, INSERT, UPDATE"],
53862
+ ["_lattice_gui_audit", "SELECT, INSERT"],
53863
+ ["__lattice_user_identity", "SELECT, INSERT, UPDATE"]
53864
+ ];
53865
+ for (const [tbl, privs] of memberSystemGrants) {
53866
+ await runAsyncOrSync(
53867
+ db.adapter,
53868
+ `DO $LATTICE$ BEGIN
53869
+ IF to_regclass('${tbl}') IS NOT NULL THEN
53870
+ EXECUTE 'GRANT ${privs} ON "${tbl}" TO ${MEMBER_GROUP}';
53871
+ END IF;
53872
+ END $LATTICE$`
53873
+ );
53874
+ }
53875
+ try {
53876
+ await runAsyncOrSync(
53877
+ db.adapter,
53878
+ `GRANT EXECUTE ON FUNCTION json_extract(text, text), strftime(text, text) TO ${MEMBER_GROUP}`
53879
+ );
53880
+ } catch (err) {
53881
+ console.warn(
53882
+ "[reconcileCloudMemberAccess] could not grant EXECUTE on polyfills (will retry next open):",
53883
+ err instanceof Error ? err.message : String(err)
53884
+ );
53885
+ }
53886
+ for (const table of registered) {
53887
+ if (table.startsWith("__lattice_") || table.startsWith("_lattice_")) continue;
53888
+ const cols = db.getRegisteredColumns(table);
53889
+ if (cols && !("deleted_at" in cols)) {
53890
+ const q3 = `"${table.replace(/"/g, '""')}"`;
53891
+ await runAsyncOrSync(
53892
+ db.adapter,
53893
+ `ALTER TABLE ${q3} ADD COLUMN IF NOT EXISTS "deleted_at" TEXT`
53894
+ );
53895
+ }
53896
+ }
53897
+ await runAsyncOrSync(
53898
+ db.adapter,
53899
+ `DO $LATTICE$ BEGIN
53900
+ IF to_regclass('__lattice_changelog') IS NOT NULL THEN
53901
+ EXECUTE 'GRANT SELECT, INSERT ON "__lattice_changelog" TO ${MEMBER_GROUP}';
53902
+ END IF;
53903
+ END $LATTICE$`
53904
+ );
53832
53905
  }
53833
53906
  async function secureNewCloudTable(db, table, pk) {
53834
53907
  if (db.getDialect() !== "postgres") return;
@@ -53854,14 +53927,6 @@ async function secureCloud(db) {
53854
53927
  await secureNewCloudTable(db, table, db.getPrimaryKey(table));
53855
53928
  }
53856
53929
  await reconcileCloudMemberAccess(db);
53857
- await runAsyncOrSync(
53858
- db.adapter,
53859
- `DO $LATTICE$ BEGIN
53860
- IF to_regclass('__lattice_user_identity') IS NOT NULL THEN
53861
- EXECUTE 'GRANT SELECT, INSERT, UPDATE ON "__lattice_user_identity" TO ${MEMBER_GROUP}';
53862
- END IF;
53863
- END $LATTICE$`
53864
- );
53865
53930
  }
53866
53931
 
53867
53932
  // src/index.ts
@@ -58741,7 +58806,7 @@ var appJs = `
58741
58806
  '<div class="view-header">' +
58742
58807
  '<span class="entity-icon">\u2699</span>' +
58743
58808
  '<h1>' + escapeHtml(tableName) + '</h1>' +
58744
- '<span class="count">' + entry.rowCount + ' row' + (entry.rowCount === 1 ? '' : 's') +
58809
+ '<span class="count">' + (entry.rowCount == null ? 'no access' : (entry.rowCount + ' row' + (entry.rowCount === 1 ? '' : 's'))) +
58745
58810
  ' \xB7 read-only</span>' +
58746
58811
  '</div>' +
58747
58812
  '<div class="muted" style="margin-bottom:12px;font-size:13px;">' +
@@ -66298,6 +66363,7 @@ async function openConfig(configPath, outputDir, autoRender = false, realtimeWat
66298
66363
  ]);
66299
66364
  const views = viewsRaw;
66300
66365
  const knownTables = /* @__PURE__ */ new Set([...declared, ...discovered.map((t8) => t8.name)]);
66366
+ const memberEntityDefs = [];
66301
66367
  for (const t8 of discovered) {
66302
66368
  if (declared.has(t8.name)) continue;
66303
66369
  if (t8.columns.length === 0) continue;
@@ -66305,17 +66371,25 @@ async function openConfig(configPath, outputDir, autoRender = false, realtimeWat
66305
66371
  discoveredJunctions.add(t8.name);
66306
66372
  continue;
66307
66373
  }
66308
- db.define(t8.name, {
66374
+ const def = {
66309
66375
  columns: Object.fromEntries(t8.columns.map((c6) => [c6, "TEXT"])),
66310
66376
  ...t8.pk.length > 0 ? { primaryKey: t8.pk.length === 1 ? t8.pk[0] : t8.pk } : {},
66311
66377
  render: () => "",
66312
66378
  outputFile: `${t8.name}/.lattice/${t8.name}.md`
66313
- });
66379
+ };
66380
+ db.define(t8.name, def);
66381
+ memberEntityDefs.push({ name: t8.name, definition: def });
66314
66382
  }
66315
66383
  for (const { name } of views) {
66316
66384
  const base = name.slice(0, -2);
66317
66385
  if (knownTables.has(base)) maskedReadViews.set(base, name);
66318
66386
  }
66387
+ if (autoRender && memberEntityDefs.length > 0) {
66388
+ const existingContexts = db.entityContexts();
66389
+ for (const { table, definition } of deriveCanonicalContexts(memberEntityDefs)) {
66390
+ if (!existingContexts.has(table)) db.defineEntityContext(table, definition);
66391
+ }
66392
+ }
66319
66393
  }
66320
66394
  }
66321
66395
  } catch {
@@ -67806,9 +67880,18 @@ async function startGuiServer(options) {
67806
67880
  }
67807
67881
  const tables = [];
67808
67882
  for (const r6 of rows) {
67809
- const cols = await active.db.introspectColumns(r6.name);
67810
- const rowCount = await active.db.count(r6.name);
67811
- tables.push({ name: r6.name, columns: cols, rowCount });
67883
+ try {
67884
+ const cols = await active.db.introspectColumns(r6.name);
67885
+ const rowCount = await active.db.count(r6.name);
67886
+ tables.push({ name: r6.name, columns: cols, rowCount });
67887
+ } catch (err) {
67888
+ const msg = err instanceof Error ? err.message : String(err);
67889
+ if (/permission denied|does not exist/i.test(msg)) {
67890
+ tables.push({ name: r6.name, columns: [], rowCount: null });
67891
+ } else {
67892
+ throw err;
67893
+ }
67894
+ }
67812
67895
  }
67813
67896
  sendJson(res, { tables });
67814
67897
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "latticesql",
3
- "version": "3.3.2",
3
+ "version": "3.3.4",
4
4
  "description": "Persistent structured memory for AI agent systems — pluggable SQLite or Postgres backend, LLM context bridge",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",