latticesql 1.13.10 → 1.15.0
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 +84 -24
- package/dist/cli.js +2234 -1381
- package/dist/index.cjs +329 -14
- package/dist/index.d.cts +176 -2
- package/dist/index.d.ts +176 -2
- package/dist/index.js +324 -15
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -804,6 +804,16 @@ await db.seed({
|
|
|
804
804
|
});
|
|
805
805
|
```
|
|
806
806
|
|
|
807
|
+
A junction link whose target row doesn't resolve is **never silently dropped**. `SeedResult.unresolvedLinks` lists every such link (source record, field, target name, junction). Pass `onUnresolvedLink: 'throw'` to abort with a `SeedReconciliationError` instead — for pipelines that must never leave a record citing a relationship that has no link in the graph:
|
|
808
|
+
|
|
809
|
+
```typescript
|
|
810
|
+
const result = await db.seed({ ...config, onUnresolvedLink: 'collect' });
|
|
811
|
+
if (result.unresolvedLinks.length) {
|
|
812
|
+
// create the missing targets, then re-seed
|
|
813
|
+
console.warn('unresolved links:', result.unresolvedLinks);
|
|
814
|
+
}
|
|
815
|
+
```
|
|
816
|
+
|
|
807
817
|
### `buildReport()` (v0.14+)
|
|
808
818
|
|
|
809
819
|
```typescript
|
|
@@ -2091,12 +2101,14 @@ The convergence means you don't need to duplicate entity-context definitions in
|
|
|
2091
2101
|
|
|
2092
2102
|
**Dashboard renders every entity (v1.13.3+).** Previously the dashboard cards filtered through a hardcoded entity list (`meetings`, `people`, `messages`, `projects`, `repositories`, `files`). Installs whose YAML declared different names saw a blank dashboard. Now every first-class entity gets a card; the hardcoded list survives as an ordering preference only.
|
|
2093
2103
|
|
|
2104
|
+
**Approximate row counts on Postgres (v1.14.1+).** The dashboard / entity-list view reads row counts from `pg_class.reltuples` (the planner statistic maintained by `ANALYZE` / autovacuum) so that a single query covers every table. Older versions issued one `COUNT(*)` per table in parallel, which exhausted small connection pools (e.g. a 95-table database against Supabase's 15-slot session pooler). The trade is that list-view counts are approximate and include soft-deleted rows for tables that have a `deleted_at` column; per-table drill-in still shows exact filtered counts. SQLite-backed installs are unaffected and continue to show exact, soft-delete-aware counts (no pool to exhaust).
|
|
2105
|
+
|
|
2094
2106
|
**Views**
|
|
2095
2107
|
|
|
2096
2108
|
- **Dashboard** (`#/`) — one card per first-class entity with live row counts.
|
|
2097
2109
|
- **Table view** (`#/objects/<entity>`) — intrinsic columns, `belongsTo` chips, and a column per junction this entity participates in.
|
|
2098
2110
|
- **Detail view** (`#/objects/<entity>/<id>`) — read mode by default; `Edit` flips cells into inputs (`Save` PATCHes, `Cancel` reverts).
|
|
2099
|
-
- **Data Model** (
|
|
2111
|
+
- **Data Model** (inside **Database Settings**, v1.14+) — entity-level graph including the native `files`/`secrets` objects, with a per-entity editor. On a team cloud each table you own carries a **Share with team / Unshare** toggle. (Pre-1.14 this was a separate `#/settings/data-model` nav item; that hash still resolves for back-compat.)
|
|
2100
2112
|
|
|
2101
2113
|
**Internal tables added on first open**
|
|
2102
2114
|
|
|
@@ -2112,18 +2124,22 @@ These tables are prefixed with `_lattice_gui_` and are hidden from `/api/entitie
|
|
|
2112
2124
|
|
|
2113
2125
|
**HTTP surface** (all routes scoped to `http://127.0.0.1:<port>/api`):
|
|
2114
2126
|
|
|
2115
|
-
| Route
|
|
2116
|
-
|
|
|
2117
|
-
| `/project`
|
|
2118
|
-
| `/entities`
|
|
2119
|
-
| `/graph`
|
|
2120
|
-
| `/tables/:table/rows`
|
|
2121
|
-
| `/tables/:table/rows`
|
|
2122
|
-
| `/tables/:table/rows/:id`
|
|
2123
|
-
| `/tables/:table/rows/:id`
|
|
2124
|
-
| `/tables/:table/rows/:id`
|
|
2125
|
-
| `/tables/:junction/link`
|
|
2126
|
-
| `/tables/:junction/unlink`
|
|
2127
|
+
| Route | Method | Lattice call |
|
|
2128
|
+
| ------------------------------ | ------ | --------------------------------------------------------------- |
|
|
2129
|
+
| `/project` | GET | (config + manifest summary) |
|
|
2130
|
+
| `/entities` | GET | tables + `db.count` per table |
|
|
2131
|
+
| `/graph` | GET | (schema graph for Data Model) |
|
|
2132
|
+
| `/tables/:table/rows` | GET | `db.query(table, …)` |
|
|
2133
|
+
| `/tables/:table/rows` | POST | `db.insert(table, body)` |
|
|
2134
|
+
| `/tables/:table/rows/:id` | GET | `db.get(table, id)` |
|
|
2135
|
+
| `/tables/:table/rows/:id` | PATCH | `db.update(table, id, body)` |
|
|
2136
|
+
| `/tables/:table/rows/:id` | DELETE | `db.delete(table, id)` |
|
|
2137
|
+
| `/tables/:junction/link` | POST | `db.link(junction, body)` |
|
|
2138
|
+
| `/tables/:junction/unlink` | POST | `db.unlink(junction, body)` |
|
|
2139
|
+
| `/schema/entities` | POST | create a new entity/table |
|
|
2140
|
+
| `/schema/entities/:name/share` | POST | share/unshare a table you own with the team (cloud, owner-only) |
|
|
2141
|
+
|
|
2142
|
+
On a team cloud, `/entities` and `/graph` (and the queryable `/tables/*` allowlist) are filtered to the tables you own plus tables shared to the team — so the API surface matches exactly what the GUI shows; a table you can't see is not reachable. `/entities` rows carry `shared` / `ownedByMe` flags in that mode.
|
|
2127
2143
|
|
|
2128
2144
|
The server only binds to `127.0.0.1` and has no authentication. See [SECURITY.md](./SECURITY.md) for the threat model — do not expose this port to a non-loopback interface.
|
|
2129
2145
|
|
|
@@ -2150,6 +2166,18 @@ registerNativeEntities(db);
|
|
|
2150
2166
|
await db.init();
|
|
2151
2167
|
```
|
|
2152
2168
|
|
|
2169
|
+
`isNativeEntity(name)` / `NATIVE_ENTITY_NAMES` are the single source of truth for "is this table a framework-shipped native object?" — adding a key to `NATIVE_ENTITY_DEFS` flows everywhere automatically (table creation, GUI surfacing, recognition).
|
|
2170
|
+
|
|
2171
|
+
**Adopting an existing `files`/`secrets` table (v1.14+).** If a database already has its own `files` or `secrets` table — possibly with a different/legacy column shape — `adoptNativeEntities(db)` (run after `init()`) labels that physical table as THE native object instead of duplicating it: it merges the native column superset non-destructively (`CREATE TABLE IF NOT EXISTS` + `ADD COLUMN IF NOT EXISTS`, never dropping data) and records the binding in an internal `__lattice_native_entities` registry. Legacy plaintext `secrets.value` rows stay readable (decrypt passes non-`enc:` values through) and new writes encrypt. `listNativeBindings(db)` reads the bindings. The GUI runs this automatically on every open and exposes the bindings at `GET /api/native-entities`.
|
|
2172
|
+
|
|
2173
|
+
```ts
|
|
2174
|
+
import { adoptNativeEntities, listNativeBindings } from 'latticesql';
|
|
2175
|
+
|
|
2176
|
+
await db.init();
|
|
2177
|
+
await adoptNativeEntities(db); // merge + label existing files/secrets as native
|
|
2178
|
+
const bindings = await listNativeBindings(db); // [{ entity, tableName, origin }]
|
|
2179
|
+
```
|
|
2180
|
+
|
|
2153
2181
|
**Machine-local user config at `~/.lattice/` (v1.12+).** A small set of files outside any Lattice DB so a user's identity, encrypted master key, saved cloud-DB credentials, and per-team bearer tokens survive switching projects:
|
|
2154
2182
|
|
|
2155
2183
|
| File | Purpose |
|
|
@@ -2220,9 +2248,19 @@ lattice teams join \
|
|
|
2220
2248
|
|
|
2221
2249
|
The cloud rejects redemption if the caller's claimed email doesn't match the invitation's `invitee_email` (case-insensitive). Sharing an invite token in a public channel is therefore safe — only the addressee can redeem it.
|
|
2222
2250
|
|
|
2223
|
-
**Other subcommands** (`lattice teams help` for the full list): `list`, `members`, `leave`, `destroy`, `share`, `unshare`, `shared`, `sync`, `link`, `unlink`, `pull`, `push`, `status`.
|
|
2251
|
+
**Other subcommands** (`lattice teams help` for the full list): `list`, `members`, `leave`, `destroy`, `share`, `unshare`, `shared`, `sync`, `link`, `unlink`, `pull`, `push`, `status`, `dlq`.
|
|
2252
|
+
|
|
2253
|
+
**Dead-letter queue (v1.15+).** A pulled change envelope that fails to apply (e.g. it arrived before the row/table it depends on), and any non-owner-overwrite divergence notice, lands in `__lattice_team_dlq`. Inspect and recover it instead of losing it behind the pull cursor:
|
|
2254
|
+
|
|
2255
|
+
```bash
|
|
2256
|
+
lattice teams dlq list --team <name> # show entries (op, target, error)
|
|
2257
|
+
lattice teams dlq retry --team <name> [--id <id>] # replay; a late dependency now applies cleanly
|
|
2258
|
+
lattice teams dlq purge --team <name> [--id <id>] # discard without applying
|
|
2259
|
+
```
|
|
2224
2260
|
|
|
2225
|
-
**
|
|
2261
|
+
**Per-table ownership + opt-in sharing (v1.14+).** Team members share one physical Postgres, so visibility is enforced at the app layer via a `__lattice_object_owners` table: each table records its creator, and a user sees only the tables they own plus tables explicitly shared to the team. The native `files`/`secrets` objects are owned by the database creator and private by default. Sharing is an explicit, owner-only action (not a side effect of creating a table). The filter gates API access, not just the display.
|
|
2262
|
+
|
|
2263
|
+
**Same flows from the GUI (v1.14+).** The local `lattice gui` drives the entire teams lifecycle from **Database Settings**: rename (owner-only), invite by email (owner-only), the inline Members list (the owner is always shown as `creator`; your own row offers Leave/Destroy; non-owners can't kick), share/unshare from the Data Model, and sync status. Member admin is resolved from `GET /api/dbconfig` against the active cloud DB, so it works even when the team cloud itself is the active database. Identity (display name + email) comes from `~/.lattice/identity.json` and is locked in the Join modal. Leaving a team removes the local config + credential and switches you to another database.
|
|
2226
2264
|
|
|
2227
2265
|
**Joining via the GUI is one click (v1.13.7+).** When you click "Join via invite" and the redeem succeeds, the team's cloud URL is automatically saved as a switchable database credential and a sibling YAML config is written to your project directory. The new entry shows up in the database dropdown as `<team-name>.config`. Clicking it opens the SPA with the team's shared tables already populated — no YAML editing, no `db.define()` calls.
|
|
2228
2266
|
|
|
@@ -2593,14 +2631,40 @@ interface AutoUpdateResult {
|
|
|
2593
2631
|
|
|
2594
2632
|
## Telemetry
|
|
2595
2633
|
|
|
2596
|
-
`latticesql`
|
|
2634
|
+
`latticesql` includes [Scarf](https://scarf.sh) install analytics so we can understand how the package is used in the wild — what versions are running, on what platforms, at roughly what scale. This signal is what lets us prioritize fixes, deprecations, and new features against real usage instead of guesswork.
|
|
2635
|
+
|
|
2636
|
+
**What is sent — once, at `npm install` time, by the `@scarf/scarf` postinstall hook:**
|
|
2597
2637
|
|
|
2598
|
-
|
|
2638
|
+
- Package name + version (e.g. `latticesql@1.13.6`)
|
|
2639
|
+
- Node.js version, OS, CPU architecture
|
|
2640
|
+
- A coarse, non-identifying hash derived from the install host (Scarf's default — used for deduplication, not identification)
|
|
2641
|
+
- The public IP of the install request (visible to any HTTPS endpoint; not stored long-term by Scarf)
|
|
2599
2642
|
|
|
2600
|
-
|
|
2601
|
-
|
|
2643
|
+
**What is NOT sent:**
|
|
2644
|
+
|
|
2645
|
+
- No data from your application code, schemas, rows, or query strings
|
|
2646
|
+
- No environment variables, file paths, hostnames, or usernames
|
|
2647
|
+
- No runtime telemetry — `latticesql` makes zero outbound telemetry calls after install. The only network requests it makes at runtime are the explicit `checkForUpdate()` / `autoUpdate()` calls to `registry.npmjs.org`, which you opt into by calling them.
|
|
2648
|
+
|
|
2649
|
+
**How to opt out** — any one of these suppresses the install ping:
|
|
2650
|
+
|
|
2651
|
+
```bash
|
|
2652
|
+
# Per-install (recommended for CI):
|
|
2653
|
+
SCARF_ANALYTICS=false npm install latticesql
|
|
2602
2654
|
|
|
2603
|
-
|
|
2655
|
+
# Or, project-wide (add to .npmrc):
|
|
2656
|
+
scarf-analytics=false
|
|
2657
|
+
|
|
2658
|
+
# Or, the cross-tool standard:
|
|
2659
|
+
DO_NOT_TRACK=1 npm install latticesql
|
|
2660
|
+
|
|
2661
|
+
# Or, disable all postinstall scripts entirely:
|
|
2662
|
+
npm install latticesql --ignore-scripts
|
|
2663
|
+
```
|
|
2664
|
+
|
|
2665
|
+
Opting out has no effect on functionality — the package works identically. The Scarf postinstall is a fire-and-forget HTTPS ping with a short timeout; even when enabled it cannot fail your install.
|
|
2666
|
+
|
|
2667
|
+
See Scarf's own [privacy documentation](https://docs.scarf.sh) for the upstream policy.
|
|
2604
2668
|
|
|
2605
2669
|
---
|
|
2606
2670
|
|
|
@@ -2619,7 +2683,3 @@ See [CHANGELOG.md](./CHANGELOG.md) for the full history.
|
|
|
2619
2683
|
## License
|
|
2620
2684
|
|
|
2621
2685
|
[Apache 2.0](./LICENSE) — includes explicit patent grant (Section 3).
|
|
2622
|
-
|
|
2623
|
-
<!-- Scarf README pixel — see § Telemetry above for what it does and how to block it. -->
|
|
2624
|
-
|
|
2625
|
-

|