@prometheus-ags/prometheus-entity-management 1.2.3 → 2.0.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/CHANGELOG.md +263 -0
- package/README.md +85 -6
- package/dist/index.d.mts +1004 -163
- package/dist/index.d.ts +1004 -163
- package/dist/index.js +2589 -1583
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2573 -1585
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,269 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
+
## [2.0.0] — 2026-05-25 — BREAKING
|
|
9
|
+
|
|
10
|
+
### Overview
|
|
11
|
+
|
|
12
|
+
2.0 eliminates the **"transport leak"** — the 1.x pattern where every
|
|
13
|
+
`useEntityView` / `useEntityList` call accepted its own `remoteFetch`,
|
|
14
|
+
`normalize`, `queryKey`, `enabled`, and error-handling strategy. Each
|
|
15
|
+
call site reinvented the same retry-loop bug in subtly different ways.
|
|
16
|
+
|
|
17
|
+
The new model: register ONE transport per entity type at app boot.
|
|
18
|
+
Every hook thereafter looks it up by name. Error handling, retry policy,
|
|
19
|
+
AbortController threading, and SWR staleness are enforced once, in the
|
|
20
|
+
library, not at every call site.
|
|
21
|
+
|
|
22
|
+
### BREAKING CHANGES
|
|
23
|
+
|
|
24
|
+
#### Removed (runtime-warning shims remain — they log a migration message and continue working)
|
|
25
|
+
|
|
26
|
+
- `useEntityList(opts)` — inline `fetch`/`normalize` closure form
|
|
27
|
+
- `useEntityView(opts)` — inline `remoteFetch`/`normalize` closure form
|
|
28
|
+
|
|
29
|
+
Both names still export. Calling them logs:
|
|
30
|
+
```
|
|
31
|
+
[entity-management] useEntityView("Foo") is deprecated in 2.0.
|
|
32
|
+
Register a transport: registerEntityTransport("Foo", makeRestTransport(...))
|
|
33
|
+
Then replace this call with: useEntityQuery<T>("Foo", { view })
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
TypeScript types remain unchanged — existing consumers compile without
|
|
37
|
+
modification. Runtime behavior is identical. The warning is the only
|
|
38
|
+
observable change for existing call sites.
|
|
39
|
+
|
|
40
|
+
#### Migration guide
|
|
41
|
+
|
|
42
|
+
**Step 1 — Register transports at app boot (once per entity type):**
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { registerEntityTransport, makeRestTransport } from "@prometheus-ags/prometheus-entity-management";
|
|
46
|
+
import { supabase } from "@/shared/db/supabase";
|
|
47
|
+
|
|
48
|
+
registerEntityTransport("Invoice", makeRestTransport({
|
|
49
|
+
supabase,
|
|
50
|
+
table: "invoice",
|
|
51
|
+
authoritative: false,
|
|
52
|
+
}));
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Step 2a — Simple lists: replace `useEntityList` with `useEntities`:**
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
// BEFORE
|
|
59
|
+
const { items, isLoading, error } = useEntityList({
|
|
60
|
+
type: "Invoice",
|
|
61
|
+
queryKey: ["Invoice", companyId],
|
|
62
|
+
fetch: () => supabase.from("invoice").select("*"),
|
|
63
|
+
normalize: (raw) => ({ id: String(raw.id), data: raw }),
|
|
64
|
+
enabled: !!companyId,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// AFTER
|
|
68
|
+
const { items, isLoading, error } = useEntities<Invoice>("Invoice", {
|
|
69
|
+
filter: { field: "company_id", op: "eq", value: companyId },
|
|
70
|
+
enabled: !!companyId,
|
|
71
|
+
});
|
|
72
|
+
// error is now TerminalError | TransientError | null (instanceof-checkable)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Step 2b — Rich views with toolbars: replace `useEntityView` with `useEntityQuery`:**
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
// BEFORE
|
|
79
|
+
const { items, setFilter, setSort } = useEntityView({
|
|
80
|
+
type: "Client",
|
|
81
|
+
baseQueryKey: ["clients", workspaceId],
|
|
82
|
+
view: { filter, sort },
|
|
83
|
+
remoteFetch: (params) => api.clients(params.rest),
|
|
84
|
+
normalize: (raw) => ({ id: raw.id, data: raw }),
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// AFTER
|
|
88
|
+
const { items, setFilter, setSort } = useEntityQuery<Client>("Client", { view: { filter, sort } });
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Added
|
|
92
|
+
|
|
93
|
+
- **`TerminalError`** — 4xx / permanent failures. `instanceof`-checkable.
|
|
94
|
+
`kind: "terminal"`, optional `status: number`.
|
|
95
|
+
Engine does NOT retry on TerminalError.
|
|
96
|
+
|
|
97
|
+
- **`TransientError`** — 5xx / network failures. `instanceof`-checkable.
|
|
98
|
+
`kind: "transient"`, optional `status: number`.
|
|
99
|
+
Engine retries with exponential backoff (up to `maxRetries`, default 3).
|
|
100
|
+
|
|
101
|
+
- **`toEntityError(err)`** — Converts unknown thrown values to
|
|
102
|
+
`TerminalError | TransientError`:
|
|
103
|
+
- 4xx status → TerminalError
|
|
104
|
+
- AbortError → TerminalError
|
|
105
|
+
- 5xx / network → TransientError
|
|
106
|
+
- Plain Error → TransientError
|
|
107
|
+
|
|
108
|
+
- **`EntityTransport<T>` interface** — One implementation per entity type:
|
|
109
|
+
```ts
|
|
110
|
+
interface EntityTransport<T extends object> {
|
|
111
|
+
identify: (row: T) => string;
|
|
112
|
+
authoritative: boolean;
|
|
113
|
+
staleTime?: number;
|
|
114
|
+
list: (q: ListQuery) => Promise<ListResult<T>>;
|
|
115
|
+
get?: (id: string, signal?: AbortSignal) => Promise<T | null>;
|
|
116
|
+
subscribe?: (onChange: (ev: ChangeEvent<T>) => void) => () => void;
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
- **`registerEntityTransport(type, transport)`** — Register at app boot.
|
|
121
|
+
Re-registering replaces (useful in tests).
|
|
122
|
+
|
|
123
|
+
- **`getEntityTransport<T>(type)`** — Look up registered transport.
|
|
124
|
+
Throws `TerminalError` if not found.
|
|
125
|
+
|
|
126
|
+
- **`makeRestTransport(opts)`** — PostgREST/Supabase transport builder.
|
|
127
|
+
Maps `ListQuery` → query params, parses `Content-Range` for total,
|
|
128
|
+
maps 4xx → TerminalError, 5xx/network → TransientError, threads signal.
|
|
129
|
+
|
|
130
|
+
- **`useEntities<T>(type, opts)`** — Thin replacement for `useEntityList`.
|
|
131
|
+
5-field return: `{ items, isLoading, isError, error, refetch }`.
|
|
132
|
+
- `error` is typed `TerminalError | TransientError | null`.
|
|
133
|
+
- `isLoading` is `lastFetched === null && isFetching` — never stuck at true.
|
|
134
|
+
- AbortController per fetch; aborts on unmount/key-change/refetch.
|
|
135
|
+
- 4xx → TerminalError, no retry.
|
|
136
|
+
- 5xx → TransientError, retry with exponential backoff.
|
|
137
|
+
|
|
138
|
+
- **`useEntityQuery<T>(type, opts)`** — Rich replacement for `useEntityView`.
|
|
139
|
+
Full toolbar API: `setFilter`, `setSort`, `setSearch`, `fetchNextPage`,
|
|
140
|
+
`setView`, `clearView`, `refetch`. Transport looked up from registry
|
|
141
|
+
(no inline closure). `error` is typed.
|
|
142
|
+
|
|
143
|
+
### Fixed (preserved from 1.3.2)
|
|
144
|
+
|
|
145
|
+
- `setListError` stamps `lastFetched` — terminal failures no longer cause
|
|
146
|
+
infinite retry loops.
|
|
147
|
+
- `useEntityView` writes errors to the base key — closes the Quick Stats
|
|
148
|
+
staleness trap.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## [1.3.2] — 2026-05-25
|
|
153
|
+
|
|
154
|
+
### Fixed
|
|
155
|
+
|
|
156
|
+
- **`setListError` now stamps `lastFetched` and clears `stale`.**
|
|
157
|
+
Previously, a failed list fetch only set `error` + cleared
|
|
158
|
+
`isFetching` — leaving `lastFetched: null`. Every consumer hook's
|
|
159
|
+
SWR staleness check
|
|
160
|
+
(`Date.now() - (lastFetched ?? 0) > staleTime`) then returned
|
|
161
|
+
`true` on the very next render, refiring the fetcher in an
|
|
162
|
+
infinite loop. A 404 on a missing table (e.g. against a schema
|
|
163
|
+
that hasn't been migrated yet) became a perpetual retry storm.
|
|
164
|
+
After this fix, a terminal failure is treated as a completed
|
|
165
|
+
attempt: consumers see a stable `error` and `isFetching: false`,
|
|
166
|
+
and the fetcher runs once. Manual `refetch()` is still available
|
|
167
|
+
for explicit retries.
|
|
168
|
+
- **`useEntityView` writes errors to the BASE key**, not just to
|
|
169
|
+
the remote-result key. The base key is the one
|
|
170
|
+
`isLoading` / `isStale` read from — without this, the staleness
|
|
171
|
+
check kept refiring even after the catch. Combined with the
|
|
172
|
+
`setListError` fix, this closes the terminal-error trap for
|
|
173
|
+
`useEntityView` consumers (Quick Stats, Active Trial Performance,
|
|
174
|
+
Revenue Trend, Recent Activity, etc.).
|
|
175
|
+
- **`useEntityView`'s `isLoading` no longer defaults to `true` when
|
|
176
|
+
there is no list state.** The previous `listState?.isFetching ??
|
|
177
|
+
true` was the actual symptom of the trap: when no list state
|
|
178
|
+
existed (because the failed fetch never seeded the base key), the
|
|
179
|
+
`?? true` kept `isLoading` at `true` forever. Changed to `?? false`
|
|
180
|
+
to match `useEntityList`'s symmetric behaviour (it reads
|
|
181
|
+
`EMPTY_LIST_STATE` which has `isFetching: false` by default).
|
|
182
|
+
|
|
183
|
+
### Added
|
|
184
|
+
|
|
185
|
+
- **`isError: boolean`** added to both `UseEntityViewResult` and
|
|
186
|
+
`UseEntityListResult`. Convenience for `error !== null`,
|
|
187
|
+
matching TanStack Query's hook ergonomics. Purely additive on
|
|
188
|
+
the return — no breaking API change.
|
|
189
|
+
|
|
190
|
+
### Notes
|
|
191
|
+
|
|
192
|
+
- This release does NOT add an `onError` callback option to either
|
|
193
|
+
hook. The decision is deliberate: TanStack Query deprecated
|
|
194
|
+
per-query `onError` callbacks in v5 because they fire per
|
|
195
|
+
observer (calling the same hook from N components produces N
|
|
196
|
+
notifications on a single failure). Consumers should read
|
|
197
|
+
`error` / `isError` from the hook return and decide their own
|
|
198
|
+
display strategy. See
|
|
199
|
+
https://tkdodo.eu/blog/react-query-error-handling for the
|
|
200
|
+
research that drove this decision.
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## [1.3.1] — 2026-05-25
|
|
205
|
+
|
|
206
|
+
### Fixed
|
|
207
|
+
|
|
208
|
+
- **`useEntityList` no longer triggers React 19's "The result of
|
|
209
|
+
getSnapshot should be cached to avoid an infinite loop" warning.**
|
|
210
|
+
The hook's return shape was a fresh object literal on every render,
|
|
211
|
+
which `useSyncExternalStore` (via Zustand's `useStore`) interpreted
|
|
212
|
+
as a changed snapshot. Wrapping the return in `useMemo` keyed on
|
|
213
|
+
`[items, listState, fetchNextPage, doFetch]` stabilises the
|
|
214
|
+
identity. `items` was already identity-stable via the
|
|
215
|
+
`useShallow(itemsSelector)` call on the `useStore` read; this fix
|
|
216
|
+
closes the gap for the outer shape consumers depend on (e.g. hook
|
|
217
|
+
composition chains like `useTeam` → `useQuickStats` → widget).
|
|
218
|
+
See [pmndrs/zustand discussion #1936](https://github.com/pmndrs/zustand/discussions/1936)
|
|
219
|
+
and [React's `useSyncExternalStore` docs](https://react.dev/reference/react/useSyncExternalStore)
|
|
220
|
+
for the contract being honoured.
|
|
221
|
+
- Downstream effect: consumers stuck at first commit because of the
|
|
222
|
+
warning-loop guard now hydrate normally, so Tier-A and hybrid list
|
|
223
|
+
views render data on first paint instead of showing perpetual
|
|
224
|
+
loading skeletons.
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
## [1.3.0] — 2026-05-23
|
|
229
|
+
|
|
230
|
+
Upstream features driven by the `hotseaters-pglite-port` phase — every
|
|
231
|
+
consumer of the library benefits from these primitives, but the focal use
|
|
232
|
+
case is a tenant-scoped, PGlite-backed local-first React app talking to a
|
|
233
|
+
self-hosted Supabase + ElectricSQL stack.
|
|
234
|
+
|
|
235
|
+
### Added
|
|
236
|
+
|
|
237
|
+
- **`createPGlitePersistenceAdapter(pglite, options?)`** in
|
|
238
|
+
`src/adapters/pglite-persistence.ts` — a `GraphPersistenceAdapter` that
|
|
239
|
+
stores the local-first runtime's graph snapshot in a PGlite table
|
|
240
|
+
(`_graph_snapshot` by default), instead of `localStorage`/`IndexedDB`.
|
|
241
|
+
- **`createTenantScopedElectricAdapter(opts)`** in
|
|
242
|
+
`src/adapters/electricsql-tenant.ts` — Electric adapter wrapper that
|
|
243
|
+
refuses to attach a shape unless it declares a `tenantColumn` (string or
|
|
244
|
+
explicit `null` for the tenant root). Builds the `WHERE` clause from a
|
|
245
|
+
validated `{ companyId }` claim so shape predicates can never widen past
|
|
246
|
+
RLS by accident. Implements RULE 5 (shape predicates ⊆ RLS) and the
|
|
247
|
+
auth-claim-aware shape registration helper (Change 13 item 11).
|
|
248
|
+
- **`registerEntityFromSql({ entityType, createTableSql, overrides })`** in
|
|
249
|
+
`src/schema-from-sql.ts` — generates and registers a JSON Schema directly
|
|
250
|
+
from a Postgres `CREATE TABLE` block, removing the need to hand-maintain
|
|
251
|
+
TypeScript schema duplicates.
|
|
252
|
+
- **`useEntityListAsTable(opts)`** in `src/table/use-entity-list-as-table.ts`
|
|
253
|
+
— wraps `useEntityList` and returns a referentially-stable `data` array
|
|
254
|
+
suitable for TanStack Table's `data` prop. Does not pull
|
|
255
|
+
`@tanstack/react-table` as a dep.
|
|
256
|
+
- **Retry-with-backoff replay** for pending offline actions in
|
|
257
|
+
`startLocalFirstGraph(...)` via a new `retryPolicy` option
|
|
258
|
+
(`{ maxAttempts, initialDelayMs, maxDelayMs, backoffFactor, jitter, poisonHandler }`).
|
|
259
|
+
Exhausted actions go to a poison handler instead of looping forever.
|
|
260
|
+
|
|
261
|
+
### Notes
|
|
262
|
+
|
|
263
|
+
- No new runtime dependencies. PGlite and ElectricSQL are still consumed
|
|
264
|
+
through minimal structural types, exactly like the existing
|
|
265
|
+
`adapters/electricsql.ts`.
|
|
266
|
+
- Backward compatible: every existing export remains. Consumers can adopt
|
|
267
|
+
the new APIs incrementally.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
8
271
|
## [1.2.0] — 2026-04-05
|
|
9
272
|
|
|
10
273
|
PWA/local-first and schema-driven entity release focused on dynamic JSON-column UI, markdown-aware rendering, and IPC-safe graph persistence.
|
package/README.md
CHANGED
|
@@ -99,6 +99,45 @@ Data flows **up** into the graph; UI reads **down** through hooks (see [Architec
|
|
|
99
99
|
|
|
100
100
|
---
|
|
101
101
|
|
|
102
|
+
## v1.3 additions
|
|
103
|
+
|
|
104
|
+
Eight focused additions across three patch releases for tenant-scoped,
|
|
105
|
+
PGlite-backed local-first apps. All backward compatible; no new runtime
|
|
106
|
+
dependencies.
|
|
107
|
+
|
|
108
|
+
### v1.3.0 — new APIs
|
|
109
|
+
|
|
110
|
+
| API | File | Purpose |
|
|
111
|
+
|-----|------|---------|
|
|
112
|
+
| `createPGlitePersistenceAdapter(pglite, options?)` | `src/adapters/pglite-persistence.ts` | `GraphPersistenceAdapter` that stores the snapshot in a PGlite table (`_graph_snapshot` by default) |
|
|
113
|
+
| `createTenantScopedElectricAdapter(opts)` | `src/adapters/electricsql-tenant.ts` | Refuses to attach Electric shapes that lack a `tenantColumn`; builds the `WHERE` from a validated `{ companyId }` claim so shape predicates can never widen past RLS |
|
|
114
|
+
| `registerEntityFromSql({ entityType, createTableSql, overrides })` | `src/schema-from-sql.ts` | Generates and registers a JSON Schema directly from a Postgres `CREATE TABLE` block — no hand-maintained TypeScript schema duplicates |
|
|
115
|
+
| `useEntityListAsTable(opts)` | `src/table/use-entity-list-as-table.ts` | Wraps `useEntityList` for TanStack Table — returns a referentially-stable `data` array and `rowCount`; no TanStack Table dep required |
|
|
116
|
+
| `startLocalFirstGraph({ ..., retryPolicy })` | `src/local-first-runtime.ts` | Retry-with-backoff for pending offline action replay; exhausted actions go to an opt-in `poisonHandler` instead of looping forever |
|
|
117
|
+
|
|
118
|
+
### v1.3.1 — stability fix
|
|
119
|
+
|
|
120
|
+
- **`useEntityList`** return shape is now `useMemo`-stabilised. React 19's
|
|
121
|
+
`useSyncExternalStore` was detecting a fresh object on every render and
|
|
122
|
+
emitting an infinite-loop warning. Identity-stable `items` + stable
|
|
123
|
+
pagination state means no more perpetual loading skeletons from hook
|
|
124
|
+
composition chains.
|
|
125
|
+
|
|
126
|
+
### v1.3.2 — error handling
|
|
127
|
+
|
|
128
|
+
- **`isError: boolean`** added to both `UseEntityListResult` and
|
|
129
|
+
`UseEntityViewResult` as a convenience alias for `error !== null`,
|
|
130
|
+
matching TanStack Query's hook ergonomics.
|
|
131
|
+
- **`setListError`** now stamps `lastFetched` and clears `stale`, closing
|
|
132
|
+
a terminal-error retry loop where a 404 on a missing table triggered an
|
|
133
|
+
infinite refetch storm.
|
|
134
|
+
- **`useEntityView`** writes errors to the base key (the one `isLoading`
|
|
135
|
+
reads from) and defaults `isLoading` to `false` when no list state
|
|
136
|
+
exists, so a failed first fetch no longer leaves consumers stuck in a
|
|
137
|
+
perpetual loading state.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
102
141
|
## New in v1.2
|
|
103
142
|
|
|
104
143
|
The graph runtime now exposes a focused set of non-hook helpers for loaders, workflows, and orchestration:
|
|
@@ -183,6 +222,7 @@ Compare against peers only when measurement methodology matches (minified vs unm
|
|
|
183
222
|
| Export | Description |
|
|
184
223
|
|--------|-------------|
|
|
185
224
|
| `registerEntityJsonSchema` / `registerRuntimeSchema` | Register static or runtime-generated JSON Schemas for an entity type or JSON column. |
|
|
225
|
+
| `registerEntityFromSql` | Generate and register a JSON Schema from a Postgres `CREATE TABLE` block — eliminates hand-maintained TypeScript schema duplicates. |
|
|
186
226
|
| `getEntityJsonSchema` | Resolve the active schema by entity type, schema id, or field. |
|
|
187
227
|
| `buildEntityFieldsFromSchema` | Generate entity field descriptors from JSON Schema for dynamic forms and detail views. |
|
|
188
228
|
| `useSchemaEntityFields` | Hook that resolves a registered schema and returns generated field descriptors. |
|
|
@@ -193,19 +233,23 @@ Compare against peers only when measurement methodology matches (minified vs unm
|
|
|
193
233
|
|
|
194
234
|
| Export | Description |
|
|
195
235
|
|--------|-------------|
|
|
196
|
-
| `startLocalFirstGraph` | Starts a higher-level local-first runtime for graph hydration, persistence, action replay, and sync status. |
|
|
236
|
+
| `startLocalFirstGraph` | Starts a higher-level local-first runtime for graph hydration, persistence, action replay, and sync status. Accepts optional `retryPolicy` for offline action replay. |
|
|
197
237
|
| `hydrateGraphFromStorage` | Restore graph state from a storage adapter using a JSON-serializable snapshot payload. |
|
|
198
238
|
| `persistGraphToStorage` | Persist graph state and pending action metadata through a storage adapter. |
|
|
199
239
|
| `useGraphSyncStatus` | Hook exposing online/offline/hydrating/syncing/ready state for PWAs and IPC-safe hosts. |
|
|
240
|
+
| `replayActionWithRetry` | Replay a single pending action with configurable exponential-backoff retry. |
|
|
241
|
+
| `createPGlitePersistenceAdapter` | PGlite-backed `GraphPersistenceAdapter`; stores the snapshot in a PGlite table alongside synced data. |
|
|
200
242
|
|
|
201
243
|
### Hooks (REST-oriented)
|
|
202
244
|
|
|
203
245
|
| Export | Description |
|
|
204
246
|
|--------|-------------|
|
|
205
|
-
| `useEntity` | Subscribe to one entity; fetch/normalize into graph; SWR + subscriber-aware refetch. |
|
|
206
|
-
| `useEntityList` | Subscribe to a list query key; stores IDs; merges row data from graph. |
|
|
247
|
+
| `useEntity` | Subscribe to one entity; fetch/normalize into graph; SWR + subscriber-aware refetch. Returns `{ data, isLoading, isError, error, refetch }`. |
|
|
248
|
+
| `useEntityList` | Subscribe to a list query key; stores IDs; merges row data from graph. Returns `{ items, isLoading, isError, error, isFetching, fetchNextPage, refetch }`. |
|
|
249
|
+
| `useEntityView` | Filter/sort/search with local/remote/hybrid completeness modes. Returns `{ items, isLoading, isError, error, setFilter, setSort, setSearch }`. |
|
|
207
250
|
| `useEntityMutation` | Mutate with optional optimistic updates and list invalidation hooks. |
|
|
208
251
|
| `useEntityAugment` | Patch UI-only fields merged at read time across all subscribers. |
|
|
252
|
+
| `useEntityListAsTable` | Wraps `useEntityList` with a referentially-stable `data` array + `rowCount` for TanStack Table. |
|
|
209
253
|
| `useSuspenseEntity` | Suspense variant of `useEntity` (non-null `id` required). |
|
|
210
254
|
| `useSuspenseEntityList` | Suspense variant of `useEntityList`. |
|
|
211
255
|
|
|
@@ -213,7 +257,6 @@ Compare against peers only when measurement methodology matches (minified vs unm
|
|
|
213
257
|
|
|
214
258
|
| Export | Description |
|
|
215
259
|
|--------|-------------|
|
|
216
|
-
| `useEntityView` | Filter/sort/search with `local` / `remote` / `hybrid` completeness modes. |
|
|
217
260
|
| `FilterSpec`, `SortSpec` | Transport-agnostic filter and sort AST types. |
|
|
218
261
|
| `toRestParams` | Compile view → REST query params. |
|
|
219
262
|
| `toSQLClauses` | Compile view → SQL-style WHERE / ORDER BY fragments. |
|
|
@@ -259,11 +302,12 @@ Compare against peers only when measurement methodology matches (minified vs unm
|
|
|
259
302
|
| `prismaRelationsToSchema` | Convert Prisma-style relation map → `EntitySchema` for `registerSchema`. |
|
|
260
303
|
| `toPrismaInclude` | Build an `include` map from relation descriptors. |
|
|
261
304
|
|
|
262
|
-
### Local-first
|
|
305
|
+
### Local-first adapters
|
|
263
306
|
|
|
264
307
|
| Export | Description |
|
|
265
308
|
|--------|-------------|
|
|
266
|
-
| `createElectricAdapter` | ElectricSQL / PGlite changes → graph. |
|
|
309
|
+
| `createElectricAdapter` | ElectricSQL / PGlite shape changes → graph. |
|
|
310
|
+
| `createTenantScopedElectricAdapter` | Safety wrapper: refuses to attach a shape unless it declares a `tenantColumn`; builds the `WHERE` clause from a validated `{ companyId }` claim. |
|
|
267
311
|
| `useLocalFirst` | Hook for local-first workflows with the adapter. |
|
|
268
312
|
| `usePGliteQuery` | Run queries against PGlite in sync with the graph story. |
|
|
269
313
|
|
|
@@ -337,6 +381,41 @@ const { items, isLoading } = useEntityList<Post, Post>({
|
|
|
337
381
|
|
|
338
382
|
**Difference:** the list stores **IDs**; row objects are always read through the normalized `Post` map, so updates propagate everywhere.
|
|
339
383
|
|
|
384
|
+
### TanStack Table: `useQuery` data prop → `useEntityListAsTable`
|
|
385
|
+
|
|
386
|
+
If you wire `useEntityList` directly into TanStack Table's `data` prop, the table treats a
|
|
387
|
+
new array reference as new data on every render. Use `useEntityListAsTable` instead—it
|
|
388
|
+
returns a referentially-stable `data` array that only changes when the underlying items
|
|
389
|
+
actually change.
|
|
390
|
+
|
|
391
|
+
**Before**
|
|
392
|
+
|
|
393
|
+
```tsx
|
|
394
|
+
const { data = [] } = useQuery<Post[]>({
|
|
395
|
+
queryKey: ["posts"],
|
|
396
|
+
queryFn: () => api.posts.list(),
|
|
397
|
+
});
|
|
398
|
+
const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
**After**
|
|
402
|
+
|
|
403
|
+
```tsx
|
|
404
|
+
import { useEntityListAsTable } from "@prometheus-ags/prometheus-entity-management";
|
|
405
|
+
|
|
406
|
+
const { data, rowCount, isLoading, isError, error } = useEntityListAsTable<Post, Post>({
|
|
407
|
+
type: "Post",
|
|
408
|
+
fetch: (p) => api.posts.list(p),
|
|
409
|
+
normalize: (row) => ({ id: row.id, data: row }),
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
const table = useReactTable({ data, rowCount, columns, getCoreRowModel: getCoreRowModel() });
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
**Difference:** `data` identity is stable across renders where items haven't changed, so
|
|
416
|
+
TanStack Table's memoization works correctly and row state (selection, expansion) is
|
|
417
|
+
preserved between refetches.
|
|
418
|
+
|
|
340
419
|
### Mutations: `useMutation` → `useEntityMutation`
|
|
341
420
|
|
|
342
421
|
**Before**
|