@prometheus-ags/prometheus-entity-management 1.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.
@@ -0,0 +1,2608 @@
1
+ import * as zustand from 'zustand';
2
+ import { StoreApi } from 'zustand';
3
+ import * as react_jsx_runtime from 'react/jsx-runtime';
4
+ import React$1, { ReactNode } from 'react';
5
+ import { ColumnDef as ColumnDef$1 } from '@tanstack/react-table';
6
+ import { PersistStorage } from 'zustand/middleware';
7
+
8
+ /** Logical entity kind (e.g. `"Post"`). Used to partition the normalized graph. */
9
+ type EntityType = string;
10
+ /** Primary key for an entity within its `type`. Lists and relations reference this, not row copies. */
11
+ type EntityId = string;
12
+ /** Stable string key for a list query (often `JSON.stringify`-shaped). Lists store IDs only under this key. */
13
+ type QueryKey = string;
14
+ /**
15
+ * Fetch/cache metadata for a single entity instance (`type:id`).
16
+ * Separates transport concerns from canonical `entities` data so hooks can show spinners and stale-while-revalidate without mutating server-shaped fields.
17
+ */
18
+ interface EntityState {
19
+ isFetching: boolean;
20
+ lastFetched: number | null;
21
+ error: string | null;
22
+ stale: boolean;
23
+ }
24
+ /**
25
+ * Ordered **IDs** for a list query plus pagination metadata — never embedded entity payloads.
26
+ * Cross-view reactivity depends on this: when `entities[type][id]` updates, every list containing that id re-resolves rows from the graph.
27
+ */
28
+ interface ListState {
29
+ ids: EntityId[];
30
+ total: number | null;
31
+ nextCursor: string | null;
32
+ prevCursor: string | null;
33
+ hasNextPage: boolean;
34
+ hasPrevPage: boolean;
35
+ isFetching: boolean;
36
+ isFetchingMore: boolean;
37
+ error: string | null;
38
+ lastFetched: number | null;
39
+ stale: boolean;
40
+ currentPage: number | null;
41
+ pageSize: number | null;
42
+ }
43
+ /**
44
+ * Canonical Zustand store: **entities** (server truth), **patches** (UI-only overlay), **lists** (id order + list meta), and **entityStates** (per-entity fetch state).
45
+ * Prefer hooks for React reads; use `useGraphStore.getState()` inside stores/adapters/engine code where React is not available.
46
+ */
47
+ interface GraphState {
48
+ /** Normalized server-confirmed records. Mutate only via upsert/replace/remove — not from components. */
49
+ entities: Record<EntityType, Record<EntityId, Record<string, unknown>>>;
50
+ /** Local-only fields merged at read time (`readEntity` / hooks). Never send patches to the server. */
51
+ patches: Record<EntityType, Record<EntityId, Record<string, unknown>>>;
52
+ /** Per-entity fetch lifecycle keyed as `${type}:${id}`. */
53
+ entityStates: Record<string, EntityState>;
54
+ /** List slots keyed by serialized query keys. Values hold id arrays and pagination — not entity clones. */
55
+ lists: Record<QueryKey, ListState>;
56
+ /**
57
+ * Shallow-merge `data` into the canonical entity. Use when the API returns partial updates or normalized fragments.
58
+ * @param type - Entity kind
59
+ * @param id - Entity id
60
+ * @param data - Fields to merge into existing canonical data
61
+ */
62
+ upsertEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => void;
63
+ /**
64
+ * Batch upsert for list endpoints — avoids N separate writes when hydrating many rows at once.
65
+ * @param type - Entity kind
66
+ * @param entries - Pairs of id + partial/full payloads to merge
67
+ */
68
+ upsertEntities: (type: EntityType, entries: Array<{
69
+ id: EntityId;
70
+ data: Record<string, unknown>;
71
+ }>) => void;
72
+ /**
73
+ * Replace the canonical entity entirely (no merge). Use when the server returns a full snapshot and partial merge would leave stale keys behind.
74
+ */
75
+ replaceEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => void;
76
+ /** Remove canonical entity, its patches, and its entityState. Does not remove the id from lists — use `removeIdFromAllLists` if needed. */
77
+ removeEntity: (type: EntityType, id: EntityId) => void;
78
+ /**
79
+ * Merge UI-only fields into `patches` so every subscriber sees selection, expansion, optimistic toggles, etc., without forking canonical data.
80
+ */
81
+ patchEntity: (type: EntityType, id: EntityId, patch: Record<string, unknown>) => void;
82
+ /** Remove specific patch keys; other patch keys remain. */
83
+ unpatchEntity: (type: EntityType, id: EntityId, keys: string[]) => void;
84
+ /** Drop all patches for an entity (e.g. after successful mutation when server data is authoritative). */
85
+ clearPatch: (type: EntityType, id: EntityId) => void;
86
+ /** Reflect in-flight GET for a single entity (deduped fetches in the engine still flip this once per logical request). */
87
+ setEntityFetching: (type: EntityType, id: EntityId, fetching: boolean) => void;
88
+ /** Persist terminal fetch failure message and clear fetching — hooks surface `error` while leaving prior data if any. */
89
+ setEntityError: (type: EntityType, id: EntityId, error: string | null) => void;
90
+ /** Mark entity successfully loaded; clears error, fetching, stale, and updates `lastFetched`. */
91
+ setEntityFetched: (type: EntityType, id: EntityId) => void;
92
+ /** Drive background revalidation: when true, hooks refetch while still showing cached `readEntity` data. */
93
+ setEntityStale: (type: EntityType, id: EntityId, stale: boolean) => void;
94
+ /**
95
+ * Replace list ids and merge pagination meta after a primary list fetch (not load-more).
96
+ * Resets fetching flags and clears list error on success path (engine calls this after normalize).
97
+ */
98
+ setListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
99
+ /** Append unique ids (e.g. infinite scroll) while merging meta; dedupes id order. */
100
+ appendListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
101
+ /** Prepend ids (e.g. “new item at top”) with optional meta merge. */
102
+ prependListResult: (key: QueryKey, ids: EntityId[], meta?: Partial<ListState>) => void;
103
+ /** After deletes, keep every list consistent by removing an id everywhere it appears and decrementing `total` when tracked. */
104
+ removeIdFromAllLists: (type: EntityType, id: EntityId) => void;
105
+ /**
106
+ * Insert or move an id in one list. If the id already exists, it is removed first then re-inserted at `position`.
107
+ * @param position - `"start"`, `"end"`, or numeric index
108
+ */
109
+ insertIdInList: (key: QueryKey, id: EntityId, position: "start" | "end" | number) => void;
110
+ /** Initial or full list fetch in progress (not page-2+). */
111
+ setListFetching: (key: QueryKey, fetching: boolean) => void;
112
+ /** Pagination / “load more” in progress for the same list key. */
113
+ setListFetchingMore: (key: QueryKey, fetchingMore: boolean) => void;
114
+ /** Record list fetch failure; clears both fetching flags. */
115
+ setListError: (key: QueryKey, error: string | null) => void;
116
+ /** Mark a list query stale so hooks can refetch without discarding current ids (stale-while-revalidate). */
117
+ setListStale: (key: QueryKey, stale: boolean) => void;
118
+ /**
119
+ * Mark one or all entities of a type stale for subscriber-driven refetch. Does not delete data.
120
+ * @param id - Omit to stale every entity of `type`
121
+ */
122
+ invalidateEntity: (type: EntityType, id?: EntityId) => void;
123
+ /**
124
+ * Mark lists stale by key prefix or custom predicate so the next access triggers background refresh without dropping ids immediately.
125
+ */
126
+ invalidateLists: (matcher: string | ((key: QueryKey) => boolean)) => void;
127
+ /** Convenience: stale all entities of a type and lists whose keys start with `type`. */
128
+ invalidateType: (type: EntityType) => void;
129
+ /**
130
+ * Single read path: `{ ...entities[type][id], ...patches[type][id] }` or null if no canonical row exists.
131
+ * @returns Merged view suitable for rendering; not a deep clone
132
+ */
133
+ readEntity: <T = Record<string, unknown>>(type: EntityType, id: EntityId) => T | null;
134
+ }
135
+ /**
136
+ * Global entity graph store (Zustand + Immer). **Components should not subscribe directly** — use hooks so layering stays `Component → hook → store`.
137
+ * `getState()` is intended for non-React code paths (engine, adapters, mutations) that must write or read the graph synchronously.
138
+ */
139
+ declare const useGraphStore: zustand.UseBoundStore<Omit<Omit<zustand.StoreApi<GraphState>, "subscribe"> & {
140
+ subscribe: {
141
+ (listener: (selectedState: GraphState, previousSelectedState: GraphState) => void): () => void;
142
+ <U>(selector: (state: GraphState) => U, listener: (selectedState: U, previousSelectedState: U) => void, options?: {
143
+ equalityFn?: ((a: U, b: U) => boolean) | undefined;
144
+ fireImmediately?: boolean;
145
+ } | undefined): () => void;
146
+ };
147
+ }, "setState"> & {
148
+ setState(nextStateOrUpdater: GraphState | Partial<GraphState> | ((state: {
149
+ entities: {
150
+ [x: string]: {
151
+ [x: string]: {
152
+ [x: string]: unknown;
153
+ };
154
+ };
155
+ };
156
+ patches: {
157
+ [x: string]: {
158
+ [x: string]: {
159
+ [x: string]: unknown;
160
+ };
161
+ };
162
+ };
163
+ entityStates: {
164
+ [x: string]: {
165
+ isFetching: boolean;
166
+ lastFetched: number | null;
167
+ error: string | null;
168
+ stale: boolean;
169
+ };
170
+ };
171
+ lists: {
172
+ [x: string]: {
173
+ ids: string[];
174
+ total: number | null;
175
+ nextCursor: string | null;
176
+ prevCursor: string | null;
177
+ hasNextPage: boolean;
178
+ hasPrevPage: boolean;
179
+ isFetching: boolean;
180
+ isFetchingMore: boolean;
181
+ error: string | null;
182
+ lastFetched: number | null;
183
+ stale: boolean;
184
+ currentPage: number | null;
185
+ pageSize: number | null;
186
+ };
187
+ };
188
+ upsertEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => void;
189
+ upsertEntities: (type: EntityType, entries: Array<{
190
+ id: EntityId;
191
+ data: Record<string, unknown>;
192
+ }>) => void;
193
+ replaceEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => void;
194
+ removeEntity: (type: EntityType, id: EntityId) => void;
195
+ patchEntity: (type: EntityType, id: EntityId, patch: Record<string, unknown>) => void;
196
+ unpatchEntity: (type: EntityType, id: EntityId, keys: string[]) => void;
197
+ clearPatch: (type: EntityType, id: EntityId) => void;
198
+ setEntityFetching: (type: EntityType, id: EntityId, fetching: boolean) => void;
199
+ setEntityError: (type: EntityType, id: EntityId, error: string | null) => void;
200
+ setEntityFetched: (type: EntityType, id: EntityId) => void;
201
+ setEntityStale: (type: EntityType, id: EntityId, stale: boolean) => void;
202
+ setListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
203
+ appendListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
204
+ prependListResult: (key: QueryKey, ids: EntityId[], meta?: Partial<ListState>) => void;
205
+ removeIdFromAllLists: (type: EntityType, id: EntityId) => void;
206
+ insertIdInList: (key: QueryKey, id: EntityId, position: "start" | "end" | number) => void;
207
+ setListFetching: (key: QueryKey, fetching: boolean) => void;
208
+ setListFetchingMore: (key: QueryKey, fetchingMore: boolean) => void;
209
+ setListError: (key: QueryKey, error: string | null) => void;
210
+ setListStale: (key: QueryKey, stale: boolean) => void;
211
+ invalidateEntity: (type: EntityType, id?: EntityId) => void;
212
+ invalidateLists: (matcher: string | ((key: QueryKey) => boolean)) => void;
213
+ invalidateType: (type: EntityType) => void;
214
+ readEntity: <T = Record<string, unknown>>(type: EntityType, id: EntityId) => T | null;
215
+ }) => void), shouldReplace?: false): void;
216
+ setState(nextStateOrUpdater: GraphState | ((state: {
217
+ entities: {
218
+ [x: string]: {
219
+ [x: string]: {
220
+ [x: string]: unknown;
221
+ };
222
+ };
223
+ };
224
+ patches: {
225
+ [x: string]: {
226
+ [x: string]: {
227
+ [x: string]: unknown;
228
+ };
229
+ };
230
+ };
231
+ entityStates: {
232
+ [x: string]: {
233
+ isFetching: boolean;
234
+ lastFetched: number | null;
235
+ error: string | null;
236
+ stale: boolean;
237
+ };
238
+ };
239
+ lists: {
240
+ [x: string]: {
241
+ ids: string[];
242
+ total: number | null;
243
+ nextCursor: string | null;
244
+ prevCursor: string | null;
245
+ hasNextPage: boolean;
246
+ hasPrevPage: boolean;
247
+ isFetching: boolean;
248
+ isFetchingMore: boolean;
249
+ error: string | null;
250
+ lastFetched: number | null;
251
+ stale: boolean;
252
+ currentPage: number | null;
253
+ pageSize: number | null;
254
+ };
255
+ };
256
+ upsertEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => void;
257
+ upsertEntities: (type: EntityType, entries: Array<{
258
+ id: EntityId;
259
+ data: Record<string, unknown>;
260
+ }>) => void;
261
+ replaceEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => void;
262
+ removeEntity: (type: EntityType, id: EntityId) => void;
263
+ patchEntity: (type: EntityType, id: EntityId, patch: Record<string, unknown>) => void;
264
+ unpatchEntity: (type: EntityType, id: EntityId, keys: string[]) => void;
265
+ clearPatch: (type: EntityType, id: EntityId) => void;
266
+ setEntityFetching: (type: EntityType, id: EntityId, fetching: boolean) => void;
267
+ setEntityError: (type: EntityType, id: EntityId, error: string | null) => void;
268
+ setEntityFetched: (type: EntityType, id: EntityId) => void;
269
+ setEntityStale: (type: EntityType, id: EntityId, stale: boolean) => void;
270
+ setListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
271
+ appendListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
272
+ prependListResult: (key: QueryKey, ids: EntityId[], meta?: Partial<ListState>) => void;
273
+ removeIdFromAllLists: (type: EntityType, id: EntityId) => void;
274
+ insertIdInList: (key: QueryKey, id: EntityId, position: "start" | "end" | number) => void;
275
+ setListFetching: (key: QueryKey, fetching: boolean) => void;
276
+ setListFetchingMore: (key: QueryKey, fetchingMore: boolean) => void;
277
+ setListError: (key: QueryKey, error: string | null) => void;
278
+ setListStale: (key: QueryKey, stale: boolean) => void;
279
+ invalidateEntity: (type: EntityType, id?: EntityId) => void;
280
+ invalidateLists: (matcher: string | ((key: QueryKey) => boolean)) => void;
281
+ invalidateType: (type: EntityType) => void;
282
+ readEntity: <T = Record<string, unknown>>(type: EntityType, id: EntityId) => T | null;
283
+ }) => void), shouldReplace: true): void;
284
+ }>;
285
+
286
+ /**
287
+ * Process-wide defaults for stale times, retries, and background revalidation.
288
+ * Keeps hook signatures small: `useEntity` / `useEntityList` merge these with per-query overrides.
289
+ */
290
+ interface EngineOptions {
291
+ defaultStaleTime?: number;
292
+ /** Max age (`Date.now() - lastFetched`) for evicting entities with zero subscribers during GC. */
293
+ defaultGcTime?: number;
294
+ /** Interval between GC passes when the collector is active (default 60s). */
295
+ gcInterval?: number;
296
+ maxRetries?: number;
297
+ retryBaseDelay?: number;
298
+ revalidateOnFocus?: boolean;
299
+ revalidateOnReconnect?: boolean;
300
+ }
301
+ /**
302
+ * Declarative **instruction** for loading one entity: wire transport (`fetch`), normalization, and graph writes.
303
+ * Hooks pass this to `fetchEntity`; the graph remains source of truth after success.
304
+ */
305
+ interface EntityQueryOptions<TRaw, TEntity extends Record<string, unknown>> {
306
+ type: EntityType;
307
+ id: EntityId | null | undefined;
308
+ fetch: (id: EntityId) => Promise<TRaw>;
309
+ normalize: (raw: TRaw) => TEntity;
310
+ idField?: string;
311
+ sideEffects?: (raw: TRaw, store: typeof useGraphStore) => void;
312
+ staleTime?: number;
313
+ enabled?: boolean;
314
+ onSuccess?: (entity: TEntity) => void;
315
+ onError?: (error: Error) => void;
316
+ }
317
+ /** Cursor/page knobs passed through to list `fetch` implementations (REST, GraphQL, etc.). */
318
+ interface ListFetchParams {
319
+ cursor?: string;
320
+ page?: number;
321
+ pageSize?: number;
322
+ params?: Record<string, unknown>;
323
+ }
324
+ /**
325
+ * Normalized list page shape from any backend. Items are mapped through `normalize` into `{ id, data }` upserts — the list stores ids only.
326
+ */
327
+ interface ListResponse<T> {
328
+ items: T[];
329
+ total?: number;
330
+ nextCursor?: string;
331
+ prevCursor?: string;
332
+ hasNextPage?: boolean;
333
+ hasPrevPage?: boolean;
334
+ page?: number;
335
+ pageSize?: number;
336
+ }
337
+ /**
338
+ * Declarative **instruction** for a collection query: stable `queryKey`, fetcher, and per-row normalization into the graph.
339
+ * `mode` controls whether a fetch replaces ids or appends (infinite scroll) when used with load-more.
340
+ */
341
+ interface ListQueryOptions<TRaw, TEntity extends Record<string, unknown>> {
342
+ type: EntityType;
343
+ queryKey: unknown[];
344
+ fetch: (params: ListFetchParams) => Promise<ListResponse<TRaw>>;
345
+ normalize: (raw: TRaw) => {
346
+ id: EntityId;
347
+ data: TEntity;
348
+ };
349
+ sideEffects?: (items: TRaw[], store: typeof useGraphStore) => void;
350
+ mode?: "replace" | "append";
351
+ staleTime?: number;
352
+ enabled?: boolean;
353
+ onSuccess?: (result: ListResponse<TRaw>) => void;
354
+ onError?: (error: Error) => void;
355
+ }
356
+ /**
357
+ * Deterministic string key for list queries so object order in nested keys does not create duplicate cache entries.
358
+ * @param key - Hook-provided query key array (serializable values)
359
+ */
360
+ declare function serializeKey(key: unknown[]): string;
361
+ /**
362
+ * Collapse concurrent identical requests into one Promise (prevents stampedes when many components mount the same entity/list).
363
+ * @param key - Logical dedupe key (e.g. `type:id` or serialized list key)
364
+ * @param fn - Async work that performs the fetch + graph writes
365
+ */
366
+ declare function dedupe<T>(key: string, fn: () => Promise<T>): Promise<T>;
367
+ /**
368
+ * Stops the periodic garbage-collection timer started by `startGarbageCollector` / `configureEngine`.
369
+ */
370
+ declare function stopGarbageCollector(): void;
371
+ /**
372
+ * Starts periodic garbage collection using current `getEngineOptions().gcInterval`.
373
+ * Stops any previous interval first. No-ops during SSR (`window` is undefined) or without `setInterval`.
374
+ * @returns Disposer that stops this collector (equivalent to `stopGarbageCollector`).
375
+ */
376
+ declare function startGarbageCollector(): () => void;
377
+ /** Override global engine behavior (typically once at app bootstrap). Restarts GC with merged options. */
378
+ declare function configureEngine(opts: EngineOptions): void;
379
+ /**
380
+ * Run a single-entity fetch with dedupe, retries, normalization, and graph updates.
381
+ * Call from hooks/adapters — not from presentational components.
382
+ */
383
+ declare function fetchEntity<TRaw, TEntity extends Record<string, unknown>>(opts: EntityQueryOptions<TRaw, TEntity>, engineOpts: Required<EngineOptions>): Promise<void>;
384
+ /**
385
+ * Fetch a list page: upserts all rows, writes ids to the list key, supports append mode for pagination.
386
+ * @param isLoadMore - When true, uses a separate dedupe key and `appendListResult` / `setListFetchingMore`.
387
+ */
388
+ declare function fetchList<TRaw, TEntity extends Record<string, unknown>>(opts: ListQueryOptions<TRaw, TEntity>, params: ListFetchParams, engineOpts: Required<EngineOptions>, isLoadMore?: boolean): Promise<void>;
389
+
390
+ /**
391
+ * Debug-time snapshot of entity graph health: counts, list queries, patches, staleness,
392
+ * in-flight fetches, and engine subscriber ref-counts.
393
+ *
394
+ * Mount inside a DevTools panel or debug route; subscriber totals update via
395
+ * `useSyncExternalStore` when hooks register/unregister interest, and graph fields
396
+ * update through the Zustand store.
397
+ */
398
+ declare function useGraphDevTools(): {
399
+ subscriberCount: number;
400
+ entityCounts: Record<string, number>;
401
+ totalEntities: number;
402
+ listCount: number;
403
+ patchedEntities: {
404
+ type: string;
405
+ id: string;
406
+ }[];
407
+ staleEntities: {
408
+ type: string;
409
+ id: string;
410
+ }[];
411
+ fetchingEntities: {
412
+ type: string;
413
+ id: string;
414
+ }[];
415
+ lists: {
416
+ key: string;
417
+ idCount: number;
418
+ isFetching: boolean;
419
+ isStale: boolean;
420
+ }[];
421
+ };
422
+
423
+ /**
424
+ * View-model for one entity row: merged canonical + patch data plus fetch lifecycle flags.
425
+ * `isLoading` is true only when there is no data yet; `isFetching` includes background refreshes.
426
+ */
427
+ interface UseEntityResult<T> {
428
+ data: T | null;
429
+ isLoading: boolean;
430
+ isFetching: boolean;
431
+ error: string | null;
432
+ isStale: boolean;
433
+ refetch: () => void;
434
+ }
435
+ /**
436
+ * Subscribe to a single normalized entity: populates the graph via `fetch`/`normalize`, dedupes in-flight work, and revalidates when stale.
437
+ * Solves “query-owned silos” by keeping **one** canonical record every list/detail reads through.
438
+ *
439
+ * @param opts - Entity query instruction (`EntityQueryOptions`): `type`, `id`, `fetch`, `normalize`, optional `staleTime` / `enabled`
440
+ * @returns Merged entity (`entities` + `patches`), loading/error/stale flags, and `refetch`
441
+ *
442
+ * @example
443
+ * ```tsx
444
+ * const { data, isLoading, refetch } = useEntity({
445
+ * type: "Project",
446
+ * id: projectId,
447
+ * fetch: (id) => api.getProject(id),
448
+ * normalize: (raw) => ({ ...raw, id: String(raw.id) }),
449
+ * });
450
+ * ```
451
+ */
452
+ declare function useEntity<TRaw, TEntity extends Record<string, unknown>>(opts: EntityQueryOptions<TRaw, TEntity>): UseEntityResult<TEntity>;
453
+ /**
454
+ * Resolved rows for a list query: **`items` joins `ids` to the graph** at render time so shared entities update everywhere.
455
+ */
456
+ interface UseEntityListResult<TEntity> {
457
+ items: TEntity[];
458
+ ids: EntityId[];
459
+ isLoading: boolean;
460
+ isFetching: boolean;
461
+ isFetchingMore: boolean;
462
+ error: string | null;
463
+ hasNextPage: boolean;
464
+ hasPrevPage: boolean;
465
+ total: number | null;
466
+ currentPage: number | null;
467
+ fetchNextPage: () => void;
468
+ refetch: () => void;
469
+ }
470
+ /**
471
+ * Subscribe to a collection: stores **ordered ids** under a serialized `queryKey`, upserts each row into `entities`, and supports pagination.
472
+ * Use when you need a table or feed backed by the shared graph (not an isolated query cache).
473
+ *
474
+ * @param opts - List query instruction (`ListQueryOptions`)
475
+ * @returns Hydrated `items`, raw `ids`, pagination helpers, and fetch flags
476
+ *
477
+ * @example
478
+ * ```tsx
479
+ * const { items, fetchNextPage, hasNextPage } = useEntityList({
480
+ * type: "Task",
481
+ * queryKey: ["tasks", { projectId }],
482
+ * fetch: (p) => api.listTasks({ ...p, projectId }),
483
+ * normalize: (raw) => ({ id: raw.id, data: raw }),
484
+ * mode: "append",
485
+ * });
486
+ * ```
487
+ */
488
+ declare function useEntityList<TRaw, TEntity extends Record<string, unknown>>(opts: ListQueryOptions<TRaw, TEntity>): UseEntityListResult<TEntity>;
489
+ /**
490
+ * Options for graph-aware mutations: API call + optional normalization into `entities`, optimistic patches, and targeted invalidation.
491
+ * Prefer `invalidateLists` prefixes / `invalidateEntities` over ad-hoc store calls so list UIs stay coherent.
492
+ */
493
+ interface MutationOptions<TInput, TRaw, TEntity extends Record<string, unknown>> {
494
+ type: EntityType;
495
+ mutate: (input: TInput) => Promise<TRaw>;
496
+ normalize?: (raw: TRaw, input: TInput) => {
497
+ id: EntityId;
498
+ data: TEntity;
499
+ };
500
+ optimistic?: (input: TInput) => {
501
+ id: EntityId;
502
+ patch: Partial<TEntity>;
503
+ } | null;
504
+ invalidateLists?: string[];
505
+ invalidateEntities?: Array<{
506
+ type: EntityType;
507
+ id: EntityId;
508
+ }>;
509
+ onSuccess?: (result: TRaw, input: TInput) => void;
510
+ onError?: (error: Error, input: TInput) => void;
511
+ }
512
+ /** Imperative mutation handle plus explicit async `state` for UI pending/error/success. */
513
+ interface UseMutationResult<TInput, TRaw> {
514
+ mutate: (input: TInput) => Promise<TRaw | null>;
515
+ trigger: (input: TInput) => void;
516
+ reset: () => void;
517
+ state: {
518
+ isPending: boolean;
519
+ isSuccess: boolean;
520
+ isError: boolean;
521
+ error: string | null;
522
+ };
523
+ }
524
+ /**
525
+ * Perform writes through your API while keeping the entity graph authoritative: optional optimistic `patchEntity`, commit via `upsertEntity`, rollback on failure.
526
+ * Use when `useEntity`/`useEntityList` describe reads and you need a consistent mutation story without a second client cache.
527
+ *
528
+ * @example
529
+ * ```tsx
530
+ * const { mutate, state } = useEntityMutation({
531
+ * type: "Task",
532
+ * mutate: (input) => api.updateTask(input.id, input.patch),
533
+ * normalize: (raw) => ({ id: raw.id, data: raw }),
534
+ * });
535
+ * ```
536
+ */
537
+ declare function useEntityMutation<TInput, TRaw, TEntity extends Record<string, unknown>>(opts: MutationOptions<TInput, TRaw, TEntity>): UseMutationResult<TInput, TRaw>;
538
+ /**
539
+ * Read/write **UI-only** fields for one entity (`patches` layer) so selection, hover, or transient state is visible in every view that reads that id.
540
+ * Does not replace `useEntityCRUD`’s edit buffer for form drafts — patches are for shared, non-persisted overlays.
541
+ *
542
+ * @param type - Entity kind
543
+ * @param id - Entity id (no-ops when null/undefined)
544
+ * @returns Current patch slice and helpers `augment` / `unaugment` / `clear`
545
+ */
546
+ declare function useEntityAugment<TEntity extends Record<string, unknown>>(type: EntityType, id: EntityId | null | undefined): {
547
+ patch: Partial<TEntity> | null;
548
+ augment: (fields: Partial<TEntity>) => void;
549
+ unaugment: (keys: (keyof TEntity)[]) => void;
550
+ clear: () => void;
551
+ };
552
+ /**
553
+ * Suspense-compatible version of `useEntity`. Throws a promise while the entity
554
+ * is loading, allowing React Suspense boundaries to show fallback UI.
555
+ * Once data is available, returns the entity data directly (never null).
556
+ *
557
+ * @param opts - Same as `useEntity` (`EntityQueryOptions`)
558
+ * @returns `data` plus `isFetching`, `isStale`, and `refetch`
559
+ * @throws Promise while loading (caught by the nearest Suspense boundary)
560
+ * @throws Error if the fetch fails with no data, if `id` is missing when required, or if the entity never resolves
561
+ */
562
+ declare function useSuspenseEntity<TRaw, TEntity extends Record<string, unknown>>(opts: EntityQueryOptions<TRaw, TEntity>): {
563
+ data: TEntity;
564
+ isFetching: boolean;
565
+ isStale: boolean;
566
+ refetch: () => void;
567
+ };
568
+ /**
569
+ * Suspense-compatible version of `useEntityList`. Throws a promise during
570
+ * initial load, allowing Suspense boundaries to handle loading state.
571
+ *
572
+ * @param opts - Same as `useEntityList` (`ListQueryOptions`)
573
+ * @returns Same shape as `useEntityList` except `isLoading` is omitted (always false when not suspended)
574
+ * @throws Promise while initially loading (caught by the nearest Suspense boundary)
575
+ * @throws Error if the fetch fails while the list is still empty
576
+ */
577
+ declare function useSuspenseEntityList<TRaw, TEntity extends Record<string, unknown>>(opts: ListQueryOptions<TRaw, TEntity>): Omit<UseEntityListResult<TEntity>, "isLoading">;
578
+
579
+ /**
580
+ * Compiles a {@link FilterSpec} into a Prisma `where` object (plain JSON-serializable shape).
581
+ *
582
+ * Operator mapping matches common Prisma filter APIs: `eq` → `equals`, string ops use `mode: "insensitive"`,
583
+ * `nin` → `notIn`, `arrayContains` → `has`, `isNull` uses `null` / `{ not: null }` per `value`, and `isNotNull` → `{ not: null }`.
584
+ * Unsupported ops (`between`, `arrayOverlaps`, `matches`, `custom`) are omitted from the result.
585
+ *
586
+ * Top-level clause arrays are combined with `AND`. {@link FilterGroup} uses `AND` / `OR` to match `group.logic`.
587
+ */
588
+ declare function toPrismaWhere(filter: FilterSpec): Record<string, unknown>;
589
+ /**
590
+ * Compiles a {@link SortSpec} into Prisma `orderBy` form: `[{ fieldName: "asc" | "desc" }, …]`.
591
+ * `nulls` and `comparator` on {@link SortClause} are ignored (local-only); extend callers if your Prisma version supports null ordering.
592
+ */
593
+ declare function toPrismaOrderBy(sort: SortSpec): Record<string, string>[];
594
+
595
+ /**
596
+ * Transport-agnostic comparison operators. Same spec can compile to REST, SQL, GraphQL, or local JS (`evaluator`).
597
+ * `custom` opts out of automatic serialization — use for predicates only the client can evaluate.
598
+ */
599
+ type FilterOperator = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "nin" | "contains" | "startsWith" | "endsWith" | "isNull" | "isNotNull" | "between" | "arrayContains" | "arrayOverlaps" | "matches" | "custom";
600
+ /** Atomic filter: field path, operator, optional value, and optional JS predicate for `custom`. */
601
+ interface FilterClause {
602
+ field: string;
603
+ op: FilterOperator;
604
+ value?: unknown;
605
+ predicate?: (fieldValue: unknown, entity: Record<string, unknown>) => boolean;
606
+ }
607
+ type FilterLogic = "and" | "or";
608
+ /** Nested boolean group so you can express `(A AND B) OR C` without losing structure when compiling to backends. */
609
+ interface FilterGroup {
610
+ logic: FilterLogic;
611
+ clauses: Array<FilterClause | FilterGroup>;
612
+ }
613
+ /** Top-level filter: flat AND list of clauses, or a recursive `FilterGroup`. */
614
+ type FilterSpec = FilterGroup | FilterClause[];
615
+ type SortDirection = "asc" | "desc";
616
+ /** Single sort key with optional null ordering and custom comparator for local sort parity with remote semantics. */
617
+ interface SortClause {
618
+ field: string;
619
+ direction: SortDirection;
620
+ nulls?: "first" | "last";
621
+ comparator?: (a: unknown, b: unknown) => number;
622
+ }
623
+ /** Ordered multi-key sort (stable application in `compareEntities`). */
624
+ type SortSpec = SortClause[];
625
+ /**
626
+ * Everything `useEntityView` needs to describe a virtualized collection: filters, sorts, and simple multi-field search.
627
+ * One descriptor can drive local evaluation, remote query compilation, or hybrid mode.
628
+ */
629
+ interface ViewDescriptor {
630
+ filter?: FilterSpec;
631
+ sort?: SortSpec;
632
+ search?: {
633
+ query: string;
634
+ fields: string[];
635
+ minChars?: number;
636
+ };
637
+ }
638
+ /**
639
+ * How complete local graph data is relative to the view: **local** (all in memory), **remote** (server must filter/sort), **hybrid** (show local fast + remote reconcile).
640
+ */
641
+ type CompletenessMode = "local" | "remote" | "hybrid";
642
+ /**
643
+ * Compile a view to flat REST query params (`sort`, `q`, and `field[op]=value` keys). Skips `custom` clauses — those cannot be expressed as strings.
644
+ */
645
+ declare function toRestParams(view: ViewDescriptor): Record<string, string>;
646
+ /**
647
+ * Compile a view to parameterized SQL fragments for server-side filtering/sorting. Unknown ops become `TRUE` — validate or restrict ops at the edge.
648
+ */
649
+ declare function toSQLClauses(view: ViewDescriptor): {
650
+ where: string;
651
+ orderBy: string;
652
+ params: unknown[];
653
+ };
654
+ /**
655
+ * Produce a GraphQL-variable-shaped object from a view (Hasura/Postgraphile-style `_op` maps). Intended as a starting point — wire to your actual schema.
656
+ */
657
+ declare function toGraphQLVariables(view: ViewDescriptor): {
658
+ where?: Record<string, unknown>;
659
+ orderBy?: Array<Record<string, unknown>>;
660
+ search?: string;
661
+ };
662
+
663
+ /**
664
+ * Normalize nested `FilterGroup` trees to a flat clause list for compilers that only understand atomic predicates.
665
+ */
666
+ declare function flattenClauses(filter: FilterSpec): FilterClause[];
667
+ /** True if any clause requires client-side `predicate` logic — forces local/hybrid evaluation paths that cannot be pushed to generic REST/SQL. */
668
+ declare function hasCustomPredicates(filter: FilterSpec): boolean;
669
+
670
+ /**
671
+ * Precompiled transport payloads for one view snapshot — pass to REST, GraphQL, or SQL backends without re-deriving from `ViewDescriptor`.
672
+ */
673
+ interface ViewFetchParams {
674
+ rest: Record<string, string>;
675
+ graphql: ReturnType<typeof toGraphQLVariables>;
676
+ sql: ReturnType<typeof toSQLClauses>;
677
+ view: ViewDescriptor;
678
+ }
679
+ /**
680
+ * Configure a **live view** over a base list: filter/sort/search in JS when data is complete, or compile the same spec to remote params when not.
681
+ * `baseQueryKey` identifies the underlying id list in the graph; the hook may create additional keys for remote result sets.
682
+ */
683
+ interface UseEntityViewOptions<TEntity extends Record<string, unknown>> {
684
+ type: EntityType;
685
+ baseQueryKey: unknown[];
686
+ view: ViewDescriptor;
687
+ mode?: CompletenessMode;
688
+ remoteFetch?: (params: ViewFetchParams) => Promise<ListResponse<TEntity>>;
689
+ normalize?: (raw: TEntity) => {
690
+ id: EntityId;
691
+ data: Record<string, unknown>;
692
+ };
693
+ remoteDebounce?: number;
694
+ staleTime?: number;
695
+ enabled?: boolean;
696
+ /** SSR-seeded ids written once into `lists[baseKey]` to avoid empty-state flash before hydration fetch. */
697
+ initialIds?: EntityId[];
698
+ /** SSR-seeded total for completeness heuristics when ids are preloaded. */
699
+ initialTotal?: number;
700
+ }
701
+ /**
702
+ * Rich list UI state: projected `items`/`viewIds`, completeness mode, remote vs local fetching flags, and imperative view updaters.
703
+ * `isShowingLocalPending` signals hybrid mode where stale local rows are visible while a remote round-trip runs.
704
+ */
705
+ interface UseEntityViewResult<TEntity> {
706
+ items: TEntity[];
707
+ viewIds: EntityId[];
708
+ viewTotal: number | null;
709
+ isLoading: boolean;
710
+ isFetching: boolean;
711
+ isRemoteFetching: boolean;
712
+ isShowingLocalPending: boolean;
713
+ error: string | null;
714
+ hasNextPage: boolean;
715
+ fetchNextPage: () => void;
716
+ isLocallyComplete: boolean;
717
+ completenessMode: CompletenessMode;
718
+ setView: (v: Partial<ViewDescriptor>) => void;
719
+ setFilter: (f: FilterSpec | null) => void;
720
+ setSort: (s: SortSpec | null) => void;
721
+ setSearch: (q: string) => void;
722
+ clearView: () => void;
723
+ refetch: () => void;
724
+ isFetchingMore: boolean;
725
+ }
726
+ /**
727
+ * Higher-level list hook: combines **graph-backed id lists**, declarative `ViewDescriptor`, local `applyView`, optional remote fetch, and realtime sorted insertion.
728
+ * Solves “filters tied to one query cache” by deriving the visible id order from the shared graph whenever possible.
729
+ *
730
+ * @param opts - Base type/key, initial view, optional `remoteFetch` + `normalize`, SSR seeds, forced `mode`
731
+ * @returns Projected entities, view metadata, completeness, and setters for interactive toolbars
732
+ *
733
+ * @example
734
+ * ```tsx
735
+ * const view = useEntityView({
736
+ * type: "Task",
737
+ * baseQueryKey: ["tasks", projectId],
738
+ * view: { filter: [{ field: "status", op: "eq", value: "open" }], sort: [{ field: "dueAt", direction: "asc" }] },
739
+ * remoteFetch: (p) => api.tasksQuery(p.rest),
740
+ * normalize: (raw) => ({ id: raw.id, data: raw }),
741
+ * });
742
+ * ```
743
+ */
744
+ declare function useEntityView<TEntity extends Record<string, unknown>>(opts: UseEntityViewOptions<TEntity>): UseEntityViewResult<TEntity>;
745
+
746
+ /**
747
+ * Evaluate `FilterSpec` against one in-memory entity — mirrors remote semantics as closely as plain JS allows.
748
+ * Use for **local** and **hybrid** `useEntityView` paths so UI filtering matches what users expect from the declarative spec.
749
+ */
750
+ declare function matchesFilter(entity: Record<string, unknown>, filter: FilterSpec): boolean;
751
+ /**
752
+ * Case-insensitive substring match across configured string fields; empty query is a no-op pass.
753
+ * Keeps quick search consistent between client-only and debounced remote `q` params.
754
+ */
755
+ declare function matchesSearch(entity: Record<string, unknown>, query: string, fields: string[]): boolean;
756
+ /**
757
+ * Multi-key comparator implementing `SortSpec` (null ordering, optional custom comparators, locale-aware string fallback).
758
+ * Shared by local sorting and binary insertion for realtime updates.
759
+ */
760
+ declare function compareEntities(a: Record<string, unknown>, b: Record<string, unknown>, sort: SortSpec): number;
761
+ /**
762
+ * Pure list projection: map ids → entities, drop missing, filter/sort/search, return **ids** in display order.
763
+ * Bridges stored id lists with on-the-fly view descriptors without duplicating entity payloads.
764
+ */
765
+ declare function applyView(ids: string[], getEntity: (id: string) => Record<string, unknown> | null, filter?: FilterSpec | null, sort?: SortSpec | null, search?: {
766
+ query: string;
767
+ fields: string[];
768
+ } | null): string[];
769
+ /**
770
+ * Heuristic for whether the graph likely holds **all** rows for a list key (enables local-only filtering/sorting in `useEntityView`).
771
+ * `total` and `hasNextPage` come from list metadata written by fetchers.
772
+ */
773
+ declare function checkCompleteness(loadedCount: number, total: number | null, hasNextPage: boolean): {
774
+ isComplete: boolean;
775
+ reason: string;
776
+ };
777
+
778
+ /** UI mode for a single CRUD surface: drives which panels/forms are active without scattering boolean flags. */
779
+ type CRUDMode = "list" | "detail" | "edit" | "create";
780
+ /**
781
+ * Wire one entity type into list+detail+forms: remote list via `useEntityView`, optional detail fetch, and create/update/delete callbacks.
782
+ * Mutations call `cascadeInvalidation` on success so related lists/entities refresh per registered schemas.
783
+ */
784
+ interface CRUDOptions<TEntity extends Record<string, unknown>> {
785
+ type: EntityType;
786
+ listQueryKey: unknown[];
787
+ listFetch: (params: ViewFetchParams) => Promise<ListResponse<TEntity>>;
788
+ normalize: (raw: TEntity) => {
789
+ id: EntityId;
790
+ data: TEntity;
791
+ };
792
+ detailFetch?: (id: EntityId) => Promise<TEntity>;
793
+ onCreate?: (data: Partial<TEntity>) => Promise<TEntity>;
794
+ onUpdate?: (id: EntityId, patch: Partial<TEntity>) => Promise<TEntity>;
795
+ onDelete?: (id: EntityId) => Promise<void>;
796
+ createDefaults?: Partial<TEntity>;
797
+ initialView?: ViewDescriptor;
798
+ onCreateSuccess?: (entity: TEntity) => void;
799
+ onUpdateSuccess?: (entity: TEntity) => void;
800
+ onDeleteSuccess?: (id: EntityId) => void;
801
+ onError?: (op: "create" | "update" | "delete", error: Error) => void;
802
+ selectAfterCreate?: boolean;
803
+ clearSelectionAfterDelete?: boolean;
804
+ }
805
+ /** Tracks which fields diverge from loaded detail — edit buffer stays in React state so other views keep showing canonical graph data until save. */
806
+ interface DirtyFields<TEntity> {
807
+ changed: Set<keyof TEntity>;
808
+ isDirty: boolean;
809
+ }
810
+ /**
811
+ * Everything a CRUD screen needs: composed `list` view, selection, detail subscription, relation joins, edit/create buffers, and mutating actions.
812
+ * `applyOptimistic` is the escape hatch to mirror the buffer into `patches` for instant sliders/toggles without committing `save` yet.
813
+ */
814
+ interface CRUDState<TEntity extends Record<string, unknown>> {
815
+ mode: CRUDMode;
816
+ setMode: (mode: CRUDMode) => void;
817
+ list: UseEntityViewResult<TEntity>;
818
+ selectedId: EntityId | null;
819
+ select: (id: EntityId | null) => void;
820
+ openDetail: (id: EntityId) => void;
821
+ detail: TEntity | null;
822
+ detailIsLoading: boolean;
823
+ detailError: string | null;
824
+ relations: Record<string, unknown>;
825
+ editBuffer: Partial<TEntity>;
826
+ setField: <K extends keyof TEntity>(field: K, value: TEntity[K]) => void;
827
+ setFields: (fields: Partial<TEntity>) => void;
828
+ resetBuffer: () => void;
829
+ dirty: DirtyFields<TEntity>;
830
+ startEdit: (id?: EntityId) => void;
831
+ cancelEdit: () => void;
832
+ save: () => Promise<TEntity | null>;
833
+ isSaving: boolean;
834
+ saveError: string | null;
835
+ applyOptimistic: () => void;
836
+ createBuffer: Partial<TEntity>;
837
+ setCreateField: <K extends keyof TEntity>(field: K, value: TEntity[K]) => void;
838
+ setCreateFields: (fields: Partial<TEntity>) => void;
839
+ resetCreateBuffer: () => void;
840
+ startCreate: () => void;
841
+ cancelCreate: () => void;
842
+ create: () => Promise<TEntity | null>;
843
+ isCreating: boolean;
844
+ createError: string | null;
845
+ deleteEntity: (id?: EntityId) => Promise<void>;
846
+ isDeleting: boolean;
847
+ deleteError: string | null;
848
+ isEditing: boolean;
849
+ }
850
+ /**
851
+ * Batteries-included CRUD orchestration over the entity graph: list filtering/sorting, detail fetch, isolated edit buffer, optimistic create row, and transactional save/delete with rollback.
852
+ * Prefer this over ad-hoc `useEntity` wiring when building admin-style tables + side panels + forms for one resource.
853
+ *
854
+ * @param opts - `CRUDOptions` for type, list key/fetch, normalization, lifecycle callbacks
855
+ * @returns `CRUDState` with list/detail/edit/create controls
856
+ */
857
+ declare function useEntityCRUD<TEntity extends Record<string, unknown>>(opts: CRUDOptions<TEntity>): CRUDState<TEntity>;
858
+
859
+ /**
860
+ * FK edge: this entity points at one parent row. Used to invalidate parent aggregates and optional list keys when the FK changes.
861
+ */
862
+ interface BelongsToRelation {
863
+ cardinality: "belongsTo";
864
+ foreignKey: string;
865
+ targetType: EntityType;
866
+ invalidateTargetLists?: string[];
867
+ }
868
+ /**
869
+ * Inverse collection: children carry `foreignKey` pointing here; `listKeyPrefix` builds the child list query key for a given parent id.
870
+ */
871
+ interface HasManyRelation {
872
+ cardinality: "hasMany";
873
+ targetType: EntityType;
874
+ foreignKey: string;
875
+ listKeyPrefix: (parentId: EntityId) => unknown[];
876
+ }
877
+ /**
878
+ * Join-style relation stored as an id array on this entity; invalidates partner lists derived via `listKeyPrefix` for each touched id.
879
+ */
880
+ interface ManyToManyRelation {
881
+ cardinality: "manyToMany";
882
+ targetType: EntityType;
883
+ localArrayField?: string;
884
+ listKeyPrefix: (thisId: EntityId) => unknown[];
885
+ }
886
+ type RelationDescriptor = BelongsToRelation | HasManyRelation | ManyToManyRelation;
887
+ /**
888
+ * Declarative relation metadata for one `EntityType`: optional named relations and list key prefixes to invalidate on any mutation.
889
+ */
890
+ interface EntitySchema {
891
+ type: EntityType;
892
+ relations?: Record<string, RelationDescriptor>;
893
+ globalListKeys?: string[];
894
+ }
895
+ /**
896
+ * Snapshot diff passed to cascade rules after CRUD: compare `previous` vs `next` to find FK moves, array membership changes, etc.
897
+ */
898
+ interface CascadeContext {
899
+ type: EntityType;
900
+ id: EntityId;
901
+ previous: Record<string, unknown> | null;
902
+ next: Record<string, unknown> | null;
903
+ op: "create" | "update" | "delete";
904
+ }
905
+ /** Register or replace schema for `schema.type` (typically at app init). */
906
+ declare function registerSchema(schema: EntitySchema): void;
907
+ /** Lookup schema for cascade/join reads; returns null if unregistered. */
908
+ declare function getSchema(type: EntityType): EntitySchema | null;
909
+ /**
910
+ * After a successful mutation, mark related entities/lists stale so hooks refetch without manually hunting query keys.
911
+ * Traverses registered schemas (including reverse `hasMany`) so denormalized UIs stay eventually consistent with the graph.
912
+ */
913
+ declare function cascadeInvalidation(ctx: CascadeContext): void;
914
+ /**
915
+ * Resolve relation **placeholders** for detail panels: joins graph reads for belongs-to targets, has-many id lists, or many-to-many id arrays.
916
+ * Returns plain objects suitable for rendering; does not mutate the graph.
917
+ */
918
+ declare function readRelations(type: EntityType, entity: Record<string, unknown>): Record<string, unknown>;
919
+
920
+ /**
921
+ * adapters/types.ts
922
+ *
923
+ * Common contract every data-source adapter implements.
924
+ * The entity graph doesn't care whether data comes from REST, GraphQL,
925
+ * WebSocket, Supabase, Convex, PGlite shape sync — they all speak this
926
+ * interface and write into the same graph.
927
+ */
928
+
929
+ type ChangeOperation = "insert" | "update" | "delete" | "upsert";
930
+ interface EntityChange<T = Record<string, unknown>> {
931
+ op: ChangeOperation;
932
+ type: EntityType;
933
+ id: EntityId;
934
+ data?: T;
935
+ patch?: Partial<T>;
936
+ }
937
+ interface ChangeSet<T = Record<string, unknown>> {
938
+ changes: EntityChange<T>[];
939
+ affectedListKeys?: string[];
940
+ timestamp?: string;
941
+ }
942
+ type UnsubscribeFn$1 = () => void;
943
+ interface SubscriptionConfig {
944
+ label?: string;
945
+ replayOnConnect?: boolean;
946
+ }
947
+ interface RealtimeAdapter {
948
+ readonly name: string;
949
+ subscribe(config: SubscriptionConfig, handler: (changeset: ChangeSet) => void): UnsubscribeFn$1;
950
+ onStatusChange?: (cb: (status: AdapterStatus) => void) => UnsubscribeFn$1;
951
+ }
952
+ type AdapterStatus = "connecting" | "connected" | "disconnected" | "error";
953
+ interface SyncQueryResult<T> {
954
+ rows: T[];
955
+ total?: number;
956
+ }
957
+ interface SyncAdapter extends RealtimeAdapter {
958
+ query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<SyncQueryResult<T>>;
959
+ execute(sql: string, params?: unknown[]): Promise<void>;
960
+ isSynced(): boolean;
961
+ onSyncComplete(cb: () => void): UnsubscribeFn$1;
962
+ }
963
+ interface ChannelConfig {
964
+ type: EntityType;
965
+ filter?: Record<string, unknown>;
966
+ id?: EntityId;
967
+ operations?: ChangeOperation[];
968
+ }
969
+
970
+ interface ManagerOptions {
971
+ flushInterval?: number;
972
+ onStatusChange?: (adapter: string, status: AdapterStatus) => void;
973
+ onChangeReceived?: (adapter: string, change: EntityChange) => void;
974
+ }
975
+ declare class RealtimeManager {
976
+ private adapters;
977
+ private pendingChanges;
978
+ private pendingListKeys;
979
+ private flushTimer;
980
+ private opts;
981
+ constructor(opts?: ManagerOptions);
982
+ register(adapter: RealtimeAdapter, channels: ChannelConfig[], normalize?: (raw: unknown) => EntityChange | null): UnsubscribeFn$1;
983
+ unregister(name: string): void;
984
+ unregisterAll(): void;
985
+ private handleChangeset;
986
+ private scheduleFlush;
987
+ flush(): void;
988
+ forceFlush(): void;
989
+ }
990
+ declare function getRealtimeManager(opts?: ManagerOptions): RealtimeManager;
991
+ declare function resetRealtimeManager(): void;
992
+
993
+ /**
994
+ * adapters/realtime-adapters.ts
995
+ *
996
+ * WebSocket, Supabase Realtime, Convex, and GraphQL subscription adapters.
997
+ * All implement RealtimeAdapter and route through RealtimeManager → entity graph.
998
+ */
999
+
1000
+ interface WebSocketAdapterOptions {
1001
+ url: string | (() => string);
1002
+ parseMessage?: (data: unknown) => EntityChange[] | null;
1003
+ protocols?: string | string[];
1004
+ reconnectBaseDelay?: number;
1005
+ maxReconnectAttempts?: number;
1006
+ pingInterval?: number;
1007
+ pingMessage?: string;
1008
+ }
1009
+ declare function createWebSocketAdapter(opts: WebSocketAdapterOptions): RealtimeAdapter;
1010
+ interface SupabaseClient$1 {
1011
+ channel(name: string): SupabaseChannel;
1012
+ }
1013
+ interface SupabaseChannel {
1014
+ on(event: "postgres_changes", config: {
1015
+ event: "*" | "INSERT" | "UPDATE" | "DELETE";
1016
+ schema: string;
1017
+ table: string;
1018
+ filter?: string;
1019
+ }, handler: (payload: SupabasePayload) => void): SupabaseChannel;
1020
+ subscribe(cb?: (status: string) => void): SupabaseChannel;
1021
+ unsubscribe(): Promise<void>;
1022
+ }
1023
+ interface SupabasePayload {
1024
+ eventType: "INSERT" | "UPDATE" | "DELETE";
1025
+ new: Record<string, unknown>;
1026
+ old: Record<string, unknown>;
1027
+ table: string;
1028
+ }
1029
+ interface SupabaseAdapterOptions$1 {
1030
+ tableTypeMap?: Record<string, string>;
1031
+ extractId?: (record: Record<string, unknown>) => string;
1032
+ schema?: string;
1033
+ }
1034
+ declare function createSupabaseRealtimeAdapter(client: SupabaseClient$1, opts?: SupabaseAdapterOptions$1): RealtimeAdapter & {
1035
+ subscribeChannel: (config: ChannelConfig & {
1036
+ _handler?: (cs: ChangeSet) => void;
1037
+ }) => UnsubscribeFn$1;
1038
+ };
1039
+ interface ConvexClient {
1040
+ onUpdate<T>(query: unknown, args: Record<string, unknown>, handler: (result: T) => void): UnsubscribeFn$1;
1041
+ }
1042
+ interface ConvexChannelConfig<T extends Record<string, unknown>> {
1043
+ type: string;
1044
+ query: unknown;
1045
+ args?: Record<string, unknown>;
1046
+ extractId?: (record: T) => string;
1047
+ normalize?: (record: T) => Record<string, unknown>;
1048
+ }
1049
+ declare function createConvexAdapter<T extends Record<string, unknown>>(opts: {
1050
+ client: ConvexClient;
1051
+ channels: ConvexChannelConfig<T>[];
1052
+ }): RealtimeAdapter;
1053
+ interface GQLWsClient {
1054
+ subscribe<T>(payload: {
1055
+ query: string;
1056
+ variables?: Record<string, unknown>;
1057
+ }, sink: {
1058
+ next: (value: {
1059
+ data: T;
1060
+ }) => void;
1061
+ error: (err: unknown) => void;
1062
+ complete: () => void;
1063
+ }): UnsubscribeFn$1;
1064
+ }
1065
+ interface GQLSubscriptionConfig<T extends Record<string, unknown>> {
1066
+ type: string;
1067
+ document: string;
1068
+ variables?: Record<string, unknown>;
1069
+ getPayload: (data: T) => GQLPayload | GQLPayload[] | null;
1070
+ }
1071
+ interface GQLPayload {
1072
+ type?: string;
1073
+ node?: Record<string, unknown>;
1074
+ id?: string;
1075
+ }
1076
+ declare function createGraphQLSubscriptionAdapter<T extends Record<string, unknown>>(opts: {
1077
+ client: GQLWsClient;
1078
+ subscriptions: GQLSubscriptionConfig<T>[];
1079
+ extractId?: (node: Record<string, unknown>, type: string) => string;
1080
+ normalize?: (node: Record<string, unknown>, type: string) => Record<string, unknown>;
1081
+ }): RealtimeAdapter;
1082
+
1083
+ /**
1084
+ * Options for {@link createPrismaEntityConfig}: one REST-backed resource aligned with Prisma-style `where` / `orderBy` / `include` payloads.
1085
+ */
1086
+ interface PrismaEntityConfigOptions<TEntity extends Record<string, unknown>> {
1087
+ /** Graph entity type key (e.g. `"Task"`). */
1088
+ type: string;
1089
+ /** Base REST URL for the collection (list) and detail as `${endpoint}/:id`. */
1090
+ endpoint: string;
1091
+ /** Primary key field on normalized entities (default `"id"`). */
1092
+ idField?: string;
1093
+ /**
1094
+ * Declarative relations (Prisma-flavored names) used to build {@link EntitySchema} and {@link toPrismaInclude}.
1095
+ * `type` is the **related** model; `foreignKey` is the FK or scalar list field name as in your API.
1096
+ */
1097
+ relations?: Record<string, {
1098
+ type: string;
1099
+ foreignKey: string;
1100
+ relation: "belongsTo" | "hasMany" | "manyToMany";
1101
+ }>;
1102
+ }
1103
+ /**
1104
+ * Converts registered {@link RelationDescriptor} entries into a Prisma `include` map (`true` for each relation name).
1105
+ */
1106
+ declare function toPrismaInclude(relations: Record<string, RelationDescriptor>): Record<string, boolean | Record<string, unknown>>;
1107
+ /**
1108
+ * Maps Prisma-style relation declarations from {@link PrismaEntityConfigOptions} into a single {@link EntitySchema}
1109
+ * for {@link registerSchema} / cascade invalidation. `hasMany` uses `listKeyPrefix: (id) => [targetType, { [foreignKey]: id }]`.
1110
+ * `manyToMany` uses `localArrayField: foreignKey` and a stable `listKeyPrefix` of `[targetType, relationName, id]`.
1111
+ */
1112
+ declare function prismaRelationsToSchema(type: string, relations: PrismaEntityConfigOptions<Record<string, unknown>>["relations"]): EntitySchema;
1113
+ /**
1114
+ * Factory for REST-backed entity/list/CRUD options that serialize filters and sorts with {@link toPrismaWhere} / {@link toPrismaOrderBy}.
1115
+ *
1116
+ * - {@link PrismaEntityConfigOptions.endpoint `endpoint`} — GET list; GET `${endpoint}/:id` for detail.
1117
+ * - List/CRUD fetchers send `where` and `orderBy` as JSON query strings unless you override via {@link ListFetchParams.params}.
1118
+ */
1119
+ declare function createPrismaEntityConfig<TEntity extends Record<string, unknown>>(config: PrismaEntityConfigOptions<TEntity>): {
1120
+ /**
1121
+ * Builds {@link EntityQueryOptions} for {@link useEntity} (GET `${endpoint}/:id`).
1122
+ */
1123
+ entity: (id: EntityId) => EntityQueryOptions<TEntity, TEntity>;
1124
+ /**
1125
+ * Builds {@link ListQueryOptions} for {@link useEntityList}. Encode `filter` / `sort` in the returned `queryKey` so
1126
+ * refetches track view changes; each fetch sends Prisma-shaped `where` / `orderBy` query params.
1127
+ */
1128
+ list: (params?: {
1129
+ page?: number;
1130
+ pageSize?: number;
1131
+ filter?: FilterSpec;
1132
+ sort?: SortSpec;
1133
+ }) => ListQueryOptions<TEntity, TEntity>;
1134
+ /**
1135
+ * Builds partial {@link CRUDOptions} for {@link useEntityCRUD}: wires list fetch (Prisma query params from `ViewDescriptor`)
1136
+ * and detail fetch. Supply `onCreate` / `onUpdate` / `onDelete` at the call site.
1137
+ */
1138
+ crud: (opts?: {
1139
+ initialView?: ViewDescriptor;
1140
+ }) => CRUDOptions<TEntity>;
1141
+ /** Schemas to pass to {@link registerSchema} (one entry for this `type`). */
1142
+ schemas: () => EntitySchema[];
1143
+ };
1144
+
1145
+ interface PGlite {
1146
+ query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<{
1147
+ rows: T[];
1148
+ }>;
1149
+ exec(sql: string): Promise<void>;
1150
+ listen(channel: string, handler: (payload: string) => void): Promise<() => void>;
1151
+ }
1152
+ interface ShapeMessage<T = Record<string, unknown>> {
1153
+ headers: {
1154
+ operation: "insert" | "update" | "delete";
1155
+ };
1156
+ offset: string;
1157
+ value: T;
1158
+ key: string;
1159
+ }
1160
+ interface ShapeStream<T = Record<string, unknown>> {
1161
+ subscribe(onMsg: (msgs: ShapeMessage<T>[]) => void, onErr?: (e: Error) => void): () => void;
1162
+ isUpToDate: boolean;
1163
+ lastOffset: string;
1164
+ }
1165
+ interface ElectricTableConfig<T extends Record<string, unknown>> {
1166
+ type: EntityType;
1167
+ table: string;
1168
+ where?: string;
1169
+ idColumn?: string;
1170
+ normalize?: (row: T) => Record<string, unknown>;
1171
+ shapeStream: ShapeStream<T>;
1172
+ }
1173
+ interface ElectricAdapterOptions {
1174
+ pglite: PGlite;
1175
+ tables: ElectricTableConfig<Record<string, unknown>>[];
1176
+ onSynced?: () => void;
1177
+ }
1178
+ declare function createElectricAdapter(opts: ElectricAdapterOptions): SyncAdapter;
1179
+ interface UseLocalFirstResult {
1180
+ isSynced: boolean;
1181
+ query: <T = Record<string, unknown>>(sql: string, params?: unknown[]) => Promise<T[]>;
1182
+ execute: (sql: string, params?: unknown[]) => Promise<void>;
1183
+ }
1184
+ declare function useLocalFirst(adapter: SyncAdapter): UseLocalFirstResult;
1185
+ declare function usePGliteQuery<T extends Record<string, unknown>>(opts: {
1186
+ adapter: SyncAdapter;
1187
+ type: EntityType;
1188
+ sql: string;
1189
+ params?: unknown[];
1190
+ idColumn?: string;
1191
+ normalize?: (row: T) => Record<string, unknown>;
1192
+ deps?: unknown[];
1193
+ }): {
1194
+ isLoading: boolean;
1195
+ error: string | null;
1196
+ };
1197
+
1198
+ interface GQLClientConfig {
1199
+ url: string;
1200
+ headers?: () => Record<string, string>;
1201
+ onError?: (errors: GQLError[]) => void;
1202
+ }
1203
+ interface GQLError {
1204
+ message: string;
1205
+ locations?: Array<{
1206
+ line: number;
1207
+ column: number;
1208
+ }>;
1209
+ path?: string[];
1210
+ extensions?: Record<string, unknown>;
1211
+ }
1212
+ interface GQLResponse<T> {
1213
+ data: T | null;
1214
+ errors?: GQLError[];
1215
+ }
1216
+ interface EntityDescriptor<TNode, TEntity extends Record<string, unknown>> {
1217
+ type: EntityType;
1218
+ path: string;
1219
+ extractId?: (node: TNode) => EntityId;
1220
+ normalize: (node: TNode) => TEntity;
1221
+ relations?: EntityDescriptor<unknown, Record<string, unknown>>[];
1222
+ }
1223
+ declare function executeGQL<T>(cfg: GQLClientConfig, document: string, variables?: Record<string, unknown>): Promise<GQLResponse<T>>;
1224
+ declare function normalizeGQLResponse<T>(data: T, descriptors: EntityDescriptor<unknown, Record<string, unknown>>[]): Array<{
1225
+ type: EntityType;
1226
+ id: EntityId;
1227
+ }>;
1228
+ declare class GQLClient {
1229
+ private cfg;
1230
+ constructor(cfg: GQLClientConfig);
1231
+ query<TData, TEntity extends Record<string, unknown>>(opts: {
1232
+ document: string;
1233
+ variables?: Record<string, unknown>;
1234
+ descriptors: EntityDescriptor<unknown, TEntity>[];
1235
+ cacheKey?: string;
1236
+ }): Promise<GQLResponse<TData>>;
1237
+ mutate<TData, TEntity extends Record<string, unknown>>(opts: {
1238
+ document: string;
1239
+ variables?: Record<string, unknown>;
1240
+ descriptors?: EntityDescriptor<unknown, TEntity>[];
1241
+ optimistic?: () => void;
1242
+ }): Promise<GQLResponse<TData>>;
1243
+ subscribe<TData>(opts: {
1244
+ document: string;
1245
+ variables?: Record<string, unknown>;
1246
+ descriptors: EntityDescriptor<unknown, Record<string, unknown>>[];
1247
+ wsClient: {
1248
+ subscribe: (p: unknown, s: unknown) => () => void;
1249
+ };
1250
+ onData?: (data: TData) => void;
1251
+ onError?: (e: unknown) => void;
1252
+ }): () => void;
1253
+ }
1254
+ declare function createGQLClient(cfg: GQLClientConfig): GQLClient;
1255
+
1256
+ interface GQLEntityOptions<TData, TEntity extends Record<string, unknown>> {
1257
+ client: GQLClient;
1258
+ document: string;
1259
+ variables?: Record<string, unknown>;
1260
+ type: EntityType;
1261
+ id: EntityId | null | undefined;
1262
+ descriptor: EntityDescriptor<unknown, TEntity>;
1263
+ sideDescriptors?: EntityDescriptor<unknown, Record<string, unknown>>[];
1264
+ staleTime?: number;
1265
+ enabled?: boolean;
1266
+ onSuccess?: (data: TData) => void;
1267
+ onError?: (err: Error) => void;
1268
+ }
1269
+ declare function useGQLEntity<TData, TEntity extends Record<string, unknown>>(opts: GQLEntityOptions<TData, TEntity>): {
1270
+ data: TEntity | null;
1271
+ isLoading: boolean;
1272
+ isFetching: boolean;
1273
+ error: string | null;
1274
+ isStale: boolean;
1275
+ refetch: () => void;
1276
+ };
1277
+ interface GQLListOptions<TData, TEntity extends Record<string, unknown>> {
1278
+ client: GQLClient;
1279
+ document: string;
1280
+ variables?: Record<string, unknown>;
1281
+ type: EntityType;
1282
+ queryKey: unknown[];
1283
+ descriptor: EntityDescriptor<unknown, TEntity>;
1284
+ getItems: (data: TData) => unknown[];
1285
+ getPagination?: (data: TData) => {
1286
+ total?: number;
1287
+ nextCursor?: string;
1288
+ hasNextPage?: boolean;
1289
+ page?: number;
1290
+ pageSize?: number;
1291
+ };
1292
+ sideDescriptors?: EntityDescriptor<unknown, Record<string, unknown>>[];
1293
+ mode?: "replace" | "append";
1294
+ staleTime?: number;
1295
+ enabled?: boolean;
1296
+ }
1297
+ declare function useGQLList<TData, TEntity extends Record<string, unknown>>(opts: GQLListOptions<TData, TEntity>): {
1298
+ items: TEntity[];
1299
+ ids: string[];
1300
+ isLoading: boolean;
1301
+ isFetching: boolean;
1302
+ isFetchingMore: boolean;
1303
+ error: string | null;
1304
+ hasNextPage: boolean;
1305
+ total: number | null;
1306
+ currentPage: number | null;
1307
+ fetchNextPage: () => void;
1308
+ refetch: () => void;
1309
+ };
1310
+ declare function useGQLMutation<TData, TEntity extends Record<string, unknown>>(opts: {
1311
+ client: GQLClient;
1312
+ document: string;
1313
+ type: string;
1314
+ descriptors?: EntityDescriptor<unknown, TEntity>[];
1315
+ optimistic?: (variables: Record<string, unknown>) => void;
1316
+ invalidateLists?: string[];
1317
+ onSuccess?: (data: TData) => void;
1318
+ onError?: (err: Error) => void;
1319
+ }): {
1320
+ mutate: (variables: Record<string, unknown>) => Promise<GQLResponse<TData> | null>;
1321
+ trigger: (v: Record<string, unknown>) => void;
1322
+ state: {
1323
+ isPending: boolean;
1324
+ isSuccess: boolean;
1325
+ isError: boolean;
1326
+ error: string | null;
1327
+ };
1328
+ };
1329
+ declare function useGQLSubscription<TData>(opts: {
1330
+ client: GQLClient;
1331
+ wsClient: {
1332
+ subscribe: (payload: unknown, sink: unknown) => () => void;
1333
+ };
1334
+ document: string;
1335
+ variables?: Record<string, unknown>;
1336
+ descriptors: EntityDescriptor<unknown, Record<string, unknown>>[];
1337
+ onData?: (data: TData) => void;
1338
+ onError?: (err: unknown) => void;
1339
+ enabled?: boolean;
1340
+ }): {
1341
+ connected: boolean;
1342
+ error: string | null;
1343
+ };
1344
+
1345
+ declare function InlineCellEditor$1({ initialValue, onCommit, onCancel, className }: {
1346
+ initialValue: string;
1347
+ onCommit: (v: string) => void;
1348
+ onCancel: () => void;
1349
+ className?: string;
1350
+ }): react_jsx_runtime.JSX.Element;
1351
+ interface EntityTableProps<T extends Record<string, unknown>> {
1352
+ viewResult: UseEntityViewResult<T>;
1353
+ columns: ColumnDef$1<T>[];
1354
+ getRowId?: (row: T) => string;
1355
+ selectedId?: string | null;
1356
+ onRowClick?: (row: T) => void;
1357
+ onCellEdit?: (row: T, field: string, value: unknown) => void;
1358
+ onBulkAction?: (rows: T[]) => React$1.ReactNode;
1359
+ paginationMode?: "none" | "loadMore" | "pages";
1360
+ pageSize?: number;
1361
+ searchPlaceholder?: string;
1362
+ searchFields?: string[];
1363
+ toolbarChildren?: React$1.ReactNode;
1364
+ showToolbar?: boolean;
1365
+ emptyState?: React$1.ReactNode;
1366
+ className?: string;
1367
+ }
1368
+ declare function EntityTable<T extends Record<string, unknown>>({ viewResult, columns, getRowId, selectedId, onRowClick, onCellEdit, onBulkAction, paginationMode, pageSize, searchPlaceholder, searchFields, toolbarChildren, showToolbar, emptyState, className }: EntityTableProps<T>): react_jsx_runtime.JSX.Element;
1369
+
1370
+ type FieldType = "text" | "textarea" | "number" | "email" | "url" | "date" | "boolean" | "enum" | "custom";
1371
+ interface FieldDescriptor<TEntity> {
1372
+ field: keyof TEntity & string;
1373
+ label: string;
1374
+ type: FieldType;
1375
+ required?: boolean;
1376
+ placeholder?: string;
1377
+ options?: Array<{
1378
+ value: string;
1379
+ label: string;
1380
+ }>;
1381
+ hint?: string;
1382
+ render?: (value: unknown, entity: TEntity) => React$1.ReactNode;
1383
+ editControl?: (value: unknown, onChange: (v: unknown) => void, entity: Partial<TEntity>) => React$1.ReactNode;
1384
+ hideOnCreate?: boolean;
1385
+ hideOnEdit?: boolean;
1386
+ readonlyOnEdit?: boolean;
1387
+ }
1388
+ declare function Sheet({ open, onClose, title, subtitle, children, footer, width }: {
1389
+ open: boolean;
1390
+ onClose: () => void;
1391
+ title: string;
1392
+ subtitle?: string;
1393
+ children: React$1.ReactNode;
1394
+ footer?: React$1.ReactNode;
1395
+ width?: string;
1396
+ }): react_jsx_runtime.JSX.Element;
1397
+ declare function EntityDetailSheet<TEntity extends Record<string, unknown>>({ crud, fields, title, description, children, showEditButton, showDeleteButton, deleteConfirmMessage }: {
1398
+ crud: CRUDState<TEntity>;
1399
+ fields: FieldDescriptor<TEntity>[];
1400
+ title?: string | ((e: TEntity) => string);
1401
+ description?: string | ((e: TEntity) => string);
1402
+ children?: (entity: TEntity, crud: CRUDState<TEntity>) => React$1.ReactNode;
1403
+ showEditButton?: boolean;
1404
+ showDeleteButton?: boolean;
1405
+ deleteConfirmMessage?: string;
1406
+ }): react_jsx_runtime.JSX.Element;
1407
+ declare function EntityFormSheet<TEntity extends Record<string, unknown>>({ crud, fields, createTitle, editTitle }: {
1408
+ crud: CRUDState<TEntity>;
1409
+ fields: FieldDescriptor<TEntity>[];
1410
+ createTitle?: string;
1411
+ editTitle?: string | ((e: TEntity) => string);
1412
+ }): react_jsx_runtime.JSX.Element;
1413
+
1414
+ type ColumnFilterType = "text" | "number" | "date" | "dateRange" | "boolean" | "enum" | "relation" | "none";
1415
+ interface EntityColumnMeta<TEntity> {
1416
+ field: keyof TEntity;
1417
+ filterType: ColumnFilterType;
1418
+ enumOptions?: Array<{
1419
+ label: string;
1420
+ value: string;
1421
+ color?: string;
1422
+ }>;
1423
+ relationEntityType?: string;
1424
+ editable?: boolean;
1425
+ hideable?: boolean;
1426
+ }
1427
+ declare module "@tanstack/react-table" {
1428
+ interface ColumnMeta<TData, TValue> {
1429
+ entityMeta?: EntityColumnMeta<TData>;
1430
+ }
1431
+ }
1432
+ declare function SortHeader({ column, label }: {
1433
+ column: {
1434
+ getIsSorted: () => false | "asc" | "desc";
1435
+ toggleSorting: (desc?: boolean) => void;
1436
+ };
1437
+ label: string;
1438
+ }): react_jsx_runtime.JSX.Element;
1439
+ declare function selectionColumn$1<T>(): ColumnDef$1<T>;
1440
+ declare function textColumn$1<T>(opts: {
1441
+ field: keyof T & string;
1442
+ header: string;
1443
+ size?: number;
1444
+ editable?: boolean;
1445
+ filterType?: ColumnFilterType;
1446
+ cell?: (v: string, row: T) => ReactNode;
1447
+ }): ColumnDef$1<T>;
1448
+ declare function numberColumn$1<T>(opts: {
1449
+ field: keyof T & string;
1450
+ header: string;
1451
+ size?: number;
1452
+ format?: (v: number) => string;
1453
+ editable?: boolean;
1454
+ }): ColumnDef$1<T>;
1455
+ declare function dateColumn$1<T>(opts: {
1456
+ field: keyof T & string;
1457
+ header: string;
1458
+ size?: number;
1459
+ format?: Intl.DateTimeFormatOptions;
1460
+ }): ColumnDef$1<T>;
1461
+ declare function booleanColumn$1<T>(opts: {
1462
+ field: keyof T & string;
1463
+ header: string;
1464
+ size?: number;
1465
+ trueLabel?: string;
1466
+ falseLabel?: string;
1467
+ }): ColumnDef$1<T>;
1468
+ declare function enumColumn$1<T>(opts: {
1469
+ field: keyof T & string;
1470
+ header: string;
1471
+ options: Array<{
1472
+ value: string;
1473
+ label: string;
1474
+ className?: string;
1475
+ }>;
1476
+ size?: number;
1477
+ editable?: boolean;
1478
+ }): ColumnDef$1<T>;
1479
+ interface ActionItem<T> {
1480
+ label: string;
1481
+ icon?: React.ComponentType<{
1482
+ className?: string;
1483
+ }>;
1484
+ onClick: (row: T) => void;
1485
+ destructive?: boolean;
1486
+ separator?: boolean;
1487
+ hidden?: (row: T) => boolean;
1488
+ disabled?: (row: T) => boolean;
1489
+ }
1490
+ declare function actionsColumn$1<T>(actions: ActionItem<T>[]): ColumnDef$1<T>;
1491
+
1492
+ /**
1493
+ * table/types.ts
1494
+ *
1495
+ * Pure table engine type definitions — zero external dependencies.
1496
+ * Structurally compatible with TanStack Table v8 for easy migration,
1497
+ * but fully self-contained within the prometheus-entity-management library.
1498
+ */
1499
+
1500
+ type ViewMode = "table" | "gallery" | "list";
1501
+ type Updater<T> = T | ((prev: T) => T);
1502
+ type AccessorFn<TData, TValue = unknown> = (row: TData, index: number) => TValue;
1503
+ type HeaderContext<TData> = {
1504
+ table: TableInstance<TData>;
1505
+ header: Header<TData>;
1506
+ column: Column<TData>;
1507
+ };
1508
+ type CellContext<TData> = {
1509
+ table: TableInstance<TData>;
1510
+ row: Row<TData>;
1511
+ cell: Cell<TData>;
1512
+ column: Column<TData>;
1513
+ getValue: <T = unknown>() => T;
1514
+ renderValue: <T = unknown>() => T | null;
1515
+ };
1516
+ type HeaderRenderer<TData> = string | ((context: HeaderContext<TData>) => React$1.ReactNode);
1517
+ type CellRenderer<TData> = string | ((context: CellContext<TData>) => React$1.ReactNode);
1518
+ type FilterFn<TData> = (row: Row<TData>, columnId: string, filterValue: unknown) => boolean;
1519
+ type SortingFn<TData> = (rowA: Row<TData>, rowB: Row<TData>, columnId: string) => number;
1520
+ type AggregationFn<TData> = (columnId: string, leafRows: Row<TData>[], childRows: Row<TData>[]) => unknown;
1521
+ interface ColumnMeta<TData = unknown> {
1522
+ entityMeta?: {
1523
+ field: keyof TData;
1524
+ filterType: "text" | "number" | "date" | "dateRange" | "boolean" | "enum" | "relation" | "none";
1525
+ enumOptions?: Array<{
1526
+ label: string;
1527
+ value: string;
1528
+ color?: string;
1529
+ }>;
1530
+ relationEntityType?: string;
1531
+ editable?: boolean;
1532
+ hideable?: boolean;
1533
+ };
1534
+ [key: string]: unknown;
1535
+ }
1536
+ interface ColumnDef<TData, TValue = unknown> {
1537
+ id?: string;
1538
+ accessorKey?: keyof TData & string;
1539
+ accessorFn?: AccessorFn<TData, TValue>;
1540
+ header?: HeaderRenderer<TData>;
1541
+ cell?: CellRenderer<TData>;
1542
+ footer?: HeaderRenderer<TData>;
1543
+ size?: number;
1544
+ minSize?: number;
1545
+ maxSize?: number;
1546
+ enableSorting?: boolean;
1547
+ enableFiltering?: boolean;
1548
+ enableHiding?: boolean;
1549
+ enablePinning?: boolean;
1550
+ enableGrouping?: boolean;
1551
+ enableResizing?: boolean;
1552
+ filterFn?: FilterFn<TData> | "auto";
1553
+ sortingFn?: SortingFn<TData> | "auto";
1554
+ aggregationFn?: AggregationFn<TData> | "auto" | "sum" | "min" | "max" | "count" | "mean" | "median" | "unique";
1555
+ sortDescFirst?: boolean;
1556
+ sortUndefined?: "first" | "last" | false;
1557
+ invertSorting?: boolean;
1558
+ meta?: ColumnMeta<TData>;
1559
+ columns?: ColumnDef<TData, unknown>[];
1560
+ }
1561
+ interface SortingColumn {
1562
+ id: string;
1563
+ desc: boolean;
1564
+ }
1565
+ type SortingState = SortingColumn[];
1566
+ interface ColumnFilter {
1567
+ id: string;
1568
+ value: unknown;
1569
+ }
1570
+ type ColumnFiltersState = ColumnFilter[];
1571
+ type RowSelectionState = Record<string, boolean>;
1572
+ type ColumnVisibilityState = Record<string, boolean>;
1573
+ type ColumnOrderState = string[];
1574
+ interface ColumnPinningState {
1575
+ left?: string[];
1576
+ right?: string[];
1577
+ }
1578
+ type ColumnSizingState = Record<string, number>;
1579
+ type ColumnSizingInfoState = {
1580
+ startOffset: number | null;
1581
+ startSize: number | null;
1582
+ deltaOffset: number | null;
1583
+ deltaPercentage: number | null;
1584
+ isResizingColumn: string | false;
1585
+ columnSizingStart: [string, number][];
1586
+ };
1587
+ type ExpandedState = Record<string, boolean> | true;
1588
+ type GroupingState = string[];
1589
+ interface PaginationState {
1590
+ pageIndex: number;
1591
+ pageSize: number;
1592
+ }
1593
+ interface TableState {
1594
+ sorting: SortingState;
1595
+ columnFilters: ColumnFiltersState;
1596
+ globalFilter: unknown;
1597
+ rowSelection: RowSelectionState;
1598
+ columnVisibility: ColumnVisibilityState;
1599
+ columnOrder: ColumnOrderState;
1600
+ columnPinning: ColumnPinningState;
1601
+ columnSizing: ColumnSizingState;
1602
+ columnSizingInfo: ColumnSizingInfoState;
1603
+ expanded: ExpandedState;
1604
+ grouping: GroupingState;
1605
+ pagination: PaginationState;
1606
+ }
1607
+ interface Row<TData> {
1608
+ id: string;
1609
+ index: number;
1610
+ original: TData;
1611
+ depth: number;
1612
+ parentId?: string;
1613
+ subRows: Row<TData>[];
1614
+ getValue: <T = unknown>(columnId: string) => T;
1615
+ renderValue: <T = unknown>(columnId: string) => T | null;
1616
+ getIsSelected: () => boolean;
1617
+ getCanSelect: () => boolean;
1618
+ getIsAllSubRowsSelected: () => boolean;
1619
+ getIsSomeSelected: () => boolean;
1620
+ toggleSelected: (value?: boolean) => void;
1621
+ getToggleSelectedHandler: () => (e: unknown) => void;
1622
+ getIsExpanded: () => boolean;
1623
+ getCanExpand: () => boolean;
1624
+ toggleExpanded: (value?: boolean) => void;
1625
+ getToggleExpandedHandler: () => () => void;
1626
+ getIsGrouped: () => boolean;
1627
+ groupingColumnId?: string;
1628
+ groupingValue?: unknown;
1629
+ getVisibleCells: () => Cell<TData>[];
1630
+ getAllCells: () => Cell<TData>[];
1631
+ getIsPinned: () => "top" | "bottom" | false;
1632
+ pin: (position: "top" | "bottom" | false) => void;
1633
+ }
1634
+ interface Cell<TData> {
1635
+ id: string;
1636
+ row: Row<TData>;
1637
+ column: Column<TData>;
1638
+ getValue: <T = unknown>() => T;
1639
+ renderValue: <T = unknown>() => T | null;
1640
+ getIsGrouped: () => boolean;
1641
+ getIsPlaceholder: () => boolean;
1642
+ getIsAggregated: () => boolean;
1643
+ getContext: () => CellContext<TData>;
1644
+ }
1645
+ interface Header<TData> {
1646
+ id: string;
1647
+ index: number;
1648
+ depth: number;
1649
+ column: Column<TData>;
1650
+ isPlaceholder: boolean;
1651
+ placeholderId?: string;
1652
+ subHeaders: Header<TData>[];
1653
+ colSpan: number;
1654
+ rowSpan: number;
1655
+ getSize: () => number;
1656
+ getStart: () => number;
1657
+ getContext: () => HeaderContext<TData>;
1658
+ getResizeHandler: () => (event: unknown) => void;
1659
+ getLeafHeaders: () => Header<TData>[];
1660
+ }
1661
+ interface HeaderGroup<TData> {
1662
+ id: string;
1663
+ depth: number;
1664
+ headers: Header<TData>[];
1665
+ }
1666
+ interface Column<TData> {
1667
+ id: string;
1668
+ depth: number;
1669
+ columnDef: ColumnDef<TData>;
1670
+ columns: Column<TData>[];
1671
+ parent?: Column<TData>;
1672
+ getFlatColumns: () => Column<TData>[];
1673
+ getLeafColumns: () => Column<TData>[];
1674
+ getIsSorted: () => false | "asc" | "desc";
1675
+ getNextSortingOrder: () => "asc" | "desc" | false;
1676
+ getCanSort: () => boolean;
1677
+ toggleSorting: (desc?: boolean, isMulti?: boolean) => void;
1678
+ clearSorting: () => void;
1679
+ getSortIndex: () => number;
1680
+ getAutoSortingFn: () => SortingFn<TData>;
1681
+ getAutoSortDir: () => "asc" | "desc";
1682
+ getIsFiltered: () => boolean;
1683
+ getFilterValue: () => unknown;
1684
+ setFilterValue: (value: unknown) => void;
1685
+ getCanFilter: () => boolean;
1686
+ getAutoFilterFn: () => FilterFn<TData> | undefined;
1687
+ getIsVisible: () => boolean;
1688
+ toggleVisibility: (value?: boolean) => void;
1689
+ getCanHide: () => boolean;
1690
+ getIsPinned: () => "left" | "right" | false;
1691
+ pin: (position: "left" | "right" | false) => void;
1692
+ getCanPin: () => boolean;
1693
+ getIsGrouped: () => boolean;
1694
+ toggleGrouping: () => void;
1695
+ getCanGroup: () => boolean;
1696
+ getGroupedIndex: () => number;
1697
+ getSize: () => number;
1698
+ getStart: (position?: "left" | "center" | "right") => number;
1699
+ getCanResize: () => boolean;
1700
+ resetSize: () => void;
1701
+ getIndex: (position?: "left" | "center" | "right") => number;
1702
+ }
1703
+ interface RowModel<TData> {
1704
+ rows: Row<TData>[];
1705
+ flatRows: Row<TData>[];
1706
+ rowsById: Record<string, Row<TData>>;
1707
+ }
1708
+ interface TableOptions<TData> {
1709
+ data: TData[];
1710
+ columns: ColumnDef<TData>[];
1711
+ getRowId?: (row: TData, index: number, parent?: Row<TData>) => string;
1712
+ defaultColumn?: Partial<ColumnDef<TData>>;
1713
+ /** Seed the initial value for any internal state slice without making it controlled. */
1714
+ initialState?: Partial<TableState>;
1715
+ /** Fully controlled state — every key provided here locks that slice and must be updated externally. */
1716
+ state?: Partial<TableState>;
1717
+ onStateChange?: (updater: Updater<TableState>) => void;
1718
+ manualSorting?: boolean;
1719
+ enableSorting?: boolean;
1720
+ enableMultiSort?: boolean;
1721
+ enableSortingRemoval?: boolean;
1722
+ enableMultiRemove?: boolean;
1723
+ maxMultiSortColCount?: number;
1724
+ sortDescFirst?: boolean;
1725
+ onSortingChange?: (updater: Updater<SortingState>) => void;
1726
+ manualFiltering?: boolean;
1727
+ enableFiltering?: boolean;
1728
+ enableColumnFilters?: boolean;
1729
+ enableGlobalFilter?: boolean;
1730
+ globalFilterFn?: FilterFn<TData>;
1731
+ onColumnFiltersChange?: (updater: Updater<ColumnFiltersState>) => void;
1732
+ onGlobalFilterChange?: (updater: Updater<unknown>) => void;
1733
+ manualPagination?: boolean;
1734
+ pageCount?: number;
1735
+ autoResetPageIndex?: boolean;
1736
+ onPaginationChange?: (updater: Updater<PaginationState>) => void;
1737
+ enableRowSelection?: boolean | ((row: Row<TData>) => boolean);
1738
+ enableMultiRowSelection?: boolean | ((row: Row<TData>) => boolean);
1739
+ enableSubRowSelection?: boolean | ((row: Row<TData>) => boolean);
1740
+ onRowSelectionChange?: (updater: Updater<RowSelectionState>) => void;
1741
+ enableHiding?: boolean;
1742
+ onColumnVisibilityChange?: (updater: Updater<ColumnVisibilityState>) => void;
1743
+ onColumnOrderChange?: (updater: Updater<ColumnOrderState>) => void;
1744
+ enablePinning?: boolean;
1745
+ onColumnPinningChange?: (updater: Updater<ColumnPinningState>) => void;
1746
+ enableColumnResizing?: boolean;
1747
+ columnResizeMode?: "onChange" | "onEnd";
1748
+ columnResizeDirection?: "ltr" | "rtl";
1749
+ onColumnSizingChange?: (updater: Updater<ColumnSizingState>) => void;
1750
+ onColumnSizingInfoChange?: (updater: Updater<ColumnSizingInfoState>) => void;
1751
+ manualGrouping?: boolean;
1752
+ enableGrouping?: boolean;
1753
+ onGroupingChange?: (updater: Updater<GroupingState>) => void;
1754
+ manualExpanding?: boolean;
1755
+ enableExpanding?: boolean;
1756
+ getSubRows?: (row: TData, index: number) => TData[] | undefined;
1757
+ getIsRowExpanded?: (row: Row<TData>) => boolean;
1758
+ onExpandedChange?: (updater: Updater<ExpandedState>) => void;
1759
+ paginateExpandedRows?: boolean;
1760
+ enableRowPinning?: boolean | ((row: Row<TData>) => boolean);
1761
+ keepPinnedRows?: boolean;
1762
+ onRowPinningChange?: (updater: Updater<Record<string, "top" | "bottom">>) => void;
1763
+ }
1764
+ interface TableInstance<TData> {
1765
+ options: TableOptions<TData>;
1766
+ getState: () => TableState;
1767
+ setState: (updater: Updater<TableState>) => void;
1768
+ reset: () => void;
1769
+ getAllColumns: () => Column<TData>[];
1770
+ getAllFlatColumns: () => Column<TData>[];
1771
+ getAllLeafColumns: () => Column<TData>[];
1772
+ getColumn: (id: string) => Column<TData> | undefined;
1773
+ getHeaderGroups: () => HeaderGroup<TData>[];
1774
+ getLeftHeaderGroups: () => HeaderGroup<TData>[];
1775
+ getCenterHeaderGroups: () => HeaderGroup<TData>[];
1776
+ getRightHeaderGroups: () => HeaderGroup<TData>[];
1777
+ getFooterGroups: () => HeaderGroup<TData>[];
1778
+ getCoreRowModel: () => RowModel<TData>;
1779
+ getRowModel: () => RowModel<TData>;
1780
+ getPreFilteredRowModel: () => RowModel<TData>;
1781
+ getFilteredRowModel: () => RowModel<TData>;
1782
+ getPreSortedRowModel: () => RowModel<TData>;
1783
+ getSortedRowModel: () => RowModel<TData>;
1784
+ getGroupedRowModel: () => RowModel<TData>;
1785
+ getExpandedRowModel: () => RowModel<TData>;
1786
+ getPrePaginationRowModel: () => RowModel<TData>;
1787
+ getPaginationRowModel: () => RowModel<TData>;
1788
+ getSelectedRowModel: () => RowModel<TData>;
1789
+ getRow: (id: string) => Row<TData>;
1790
+ setSorting: (updater: Updater<SortingState>) => void;
1791
+ resetSorting: (defaultState?: boolean) => void;
1792
+ setColumnFilters: (updater: Updater<ColumnFiltersState>) => void;
1793
+ resetColumnFilters: (defaultState?: boolean) => void;
1794
+ setGlobalFilter: (value: unknown) => void;
1795
+ resetGlobalFilter: (defaultState?: boolean) => void;
1796
+ setPageIndex: (updater: Updater<number>) => void;
1797
+ resetPageIndex: (defaultState?: boolean) => void;
1798
+ setPageSize: (updater: Updater<number>) => void;
1799
+ resetPageSize: (defaultState?: boolean) => void;
1800
+ getPageCount: () => number;
1801
+ getCanPreviousPage: () => boolean;
1802
+ getCanNextPage: () => boolean;
1803
+ previousPage: () => void;
1804
+ nextPage: () => void;
1805
+ firstPage: () => void;
1806
+ lastPage: () => void;
1807
+ setRowSelection: (updater: Updater<RowSelectionState>) => void;
1808
+ resetRowSelection: (defaultState?: boolean) => void;
1809
+ toggleAllRowsSelected: (value?: boolean) => void;
1810
+ toggleAllPageRowsSelected: (value?: boolean) => void;
1811
+ getIsAllRowsSelected: () => boolean;
1812
+ getIsAllPageRowsSelected: () => boolean;
1813
+ getIsSomeRowsSelected: () => boolean;
1814
+ getIsSomePageRowsSelected: () => boolean;
1815
+ getToggleAllRowsSelectedHandler: () => (e: unknown) => void;
1816
+ getToggleAllPageRowsSelectedHandler: () => (e: unknown) => void;
1817
+ setColumnVisibility: (updater: Updater<ColumnVisibilityState>) => void;
1818
+ resetColumnVisibility: (defaultState?: boolean) => void;
1819
+ toggleAllColumnsVisible: (value?: boolean) => void;
1820
+ getIsAllColumnsVisible: () => boolean;
1821
+ getIsSomeColumnsVisible: () => boolean;
1822
+ getToggleAllColumnsVisibilityHandler: () => (e: unknown) => void;
1823
+ getVisibleFlatColumns: () => Column<TData>[];
1824
+ getVisibleLeafColumns: () => Column<TData>[];
1825
+ setColumnOrder: (updater: Updater<ColumnOrderState>) => void;
1826
+ resetColumnOrder: (defaultState?: boolean) => void;
1827
+ setColumnPinning: (updater: Updater<ColumnPinningState>) => void;
1828
+ resetColumnPinning: (defaultState?: boolean) => void;
1829
+ getLeftFlatColumns: () => Column<TData>[];
1830
+ getRightFlatColumns: () => Column<TData>[];
1831
+ getCenterFlatColumns: () => Column<TData>[];
1832
+ getLeftLeafColumns: () => Column<TData>[];
1833
+ getRightLeafColumns: () => Column<TData>[];
1834
+ getCenterLeafColumns: () => Column<TData>[];
1835
+ setColumnSizing: (updater: Updater<ColumnSizingState>) => void;
1836
+ setColumnSizingInfo: (updater: Updater<ColumnSizingInfoState>) => void;
1837
+ resetColumnSizing: (defaultState?: boolean) => void;
1838
+ setGrouping: (updater: Updater<GroupingState>) => void;
1839
+ resetGrouping: (defaultState?: boolean) => void;
1840
+ setExpanded: (updater: Updater<ExpandedState>) => void;
1841
+ resetExpanded: (defaultState?: boolean) => void;
1842
+ toggleAllRowsExpanded: (expanded?: boolean) => void;
1843
+ getIsAllRowsExpanded: () => boolean;
1844
+ getIsSomeRowsExpanded: () => boolean;
1845
+ getCanSomeRowsExpand: () => boolean;
1846
+ getExpandedDepth: () => number;
1847
+ }
1848
+ interface ActionDef<TData> {
1849
+ id: string;
1850
+ label: string;
1851
+ icon?: React$1.ComponentType<{
1852
+ className?: string;
1853
+ }>;
1854
+ onClick: (item: TData) => void;
1855
+ destructive?: boolean;
1856
+ hidden?: (item: TData) => boolean;
1857
+ disabled?: (item: TData) => boolean;
1858
+ confirm?: string | ((item: TData) => string);
1859
+ variant?: "primary" | "default" | "ghost" | "destructive";
1860
+ }
1861
+ interface ItemDescriptorBadge<TData> {
1862
+ field: keyof TData & string;
1863
+ options?: Array<{
1864
+ value: string;
1865
+ label: string;
1866
+ className?: string;
1867
+ }>;
1868
+ }
1869
+ interface ItemDescriptorMeta<TData> {
1870
+ field: keyof TData & string;
1871
+ label: string;
1872
+ format?: (value: unknown) => string;
1873
+ }
1874
+ interface ItemDescriptor<TData> {
1875
+ title: keyof TData & string;
1876
+ subtitle?: keyof TData & string;
1877
+ image?: keyof TData & string;
1878
+ icon?: (keyof TData & string) | React$1.ComponentType<{
1879
+ className?: string;
1880
+ }>;
1881
+ avatar?: keyof TData & string;
1882
+ badges?: ItemDescriptorBadge<TData>[];
1883
+ metadata?: ItemDescriptorMeta<TData>[];
1884
+ description?: keyof TData & string;
1885
+ }
1886
+ interface ItemRenderContext<TData> {
1887
+ isSelected: boolean;
1888
+ isEditing: boolean;
1889
+ isMultiSelectMode: boolean;
1890
+ onToggleSelect: () => void;
1891
+ onEdit: () => void;
1892
+ onSave: (changes: Partial<TData>) => void;
1893
+ onCancel: () => void;
1894
+ actions: ActionDef<TData>[];
1895
+ row: Row<TData>;
1896
+ }
1897
+ interface EmptyStateConfig {
1898
+ icon?: React$1.ComponentType<{
1899
+ className?: string;
1900
+ }>;
1901
+ title?: string;
1902
+ description?: string;
1903
+ action?: {
1904
+ label: string;
1905
+ onClick: () => void;
1906
+ };
1907
+ filteredTitle?: string;
1908
+ filteredDescription?: string;
1909
+ filteredAction?: {
1910
+ label: string;
1911
+ onClick: () => void;
1912
+ };
1913
+ }
1914
+ interface BatchActionDef {
1915
+ id: string;
1916
+ label: string;
1917
+ icon?: React$1.ComponentType<{
1918
+ className?: string;
1919
+ }>;
1920
+ destructive?: boolean;
1921
+ }
1922
+ interface GalleryColumns {
1923
+ sm?: number;
1924
+ md?: number;
1925
+ lg?: number;
1926
+ xl?: number;
1927
+ }
1928
+
1929
+ declare function useTable<TData extends Record<string, unknown>>(options: TableOptions<TData>): TableInstance<TData>;
1930
+
1931
+ /**
1932
+ * table/row-models.ts
1933
+ *
1934
+ * Pure row model pipeline functions. Each stage transforms data
1935
+ * into a RowModel — no side effects, no external dependencies.
1936
+ */
1937
+
1938
+ declare function createRow<TData>(original: TData, index: number, columns: Column<TData>[], table: TableInstance<TData>, depth?: number, parentId?: string, subRows?: Row<TData>[]): Row<TData>;
1939
+ declare function getCoreRowModel<TData>(data: TData[], columns: Column<TData>[], table: TableInstance<TData>): RowModel<TData>;
1940
+ declare function getFilteredRowModel<TData>(rowModel: RowModel<TData>, columnFilters: ColumnFiltersState, globalFilter: unknown, columns: Column<TData>[], globalFilterFn?: FilterFn<TData>): RowModel<TData>;
1941
+ declare function getSortedRowModel<TData>(rowModel: RowModel<TData>, sorting: SortingState, columns: Column<TData>[]): RowModel<TData>;
1942
+ declare function getGroupedRowModel<TData>(rowModel: RowModel<TData>, grouping: GroupingState, columns: Column<TData>[], table: TableInstance<TData>): RowModel<TData>;
1943
+ declare function getExpandedRowModel<TData>(rowModel: RowModel<TData>, expanded: ExpandedState): RowModel<TData>;
1944
+ declare function getPaginatedRowModel<TData>(rowModel: RowModel<TData>, pagination: PaginationState): RowModel<TData>;
1945
+ declare function getSelectedRowModel<TData>(rowModel: RowModel<TData>, selection: Record<string, boolean>): RowModel<TData>;
1946
+
1947
+ /**
1948
+ * table/faceting.ts
1949
+ *
1950
+ * Column faceting utilities — compute unique values, counts,
1951
+ * and min/max for filter UI controls.
1952
+ */
1953
+
1954
+ /**
1955
+ * Compute unique values and their counts for a given column.
1956
+ * Useful for rendering filter dropdown options with hit counts.
1957
+ */
1958
+ declare function getFacetedUniqueValues<TData>(rowModel: RowModel<TData>, columnId: string): Map<unknown, number>;
1959
+ /**
1960
+ * Compute min and max numeric values for a given column.
1961
+ * Returns [min, max] or undefined if no numeric values exist.
1962
+ */
1963
+ declare function getFacetedMinMaxValues<TData>(rowModel: RowModel<TData>, columnId: string): [number, number] | undefined;
1964
+ /**
1965
+ * Get a filtered row model scoped to a single column's facet.
1966
+ * Returns a row model that excludes the given column's own filter,
1967
+ * so the facet counts reflect what would be available if that
1968
+ * specific filter were removed.
1969
+ */
1970
+ declare function getFacetedRowModel<TData>(preFilteredRowModel: RowModel<TData>, columnId: string, allFilteredRowModel: RowModel<TData>): RowModel<TData>;
1971
+
1972
+ /**
1973
+ * table/selection-store.ts
1974
+ *
1975
+ * Zustand store for multi-select state, shared across all view modes.
1976
+ * Each EntityListView instance creates its own store via createSelectionStore()
1977
+ * so multiple lists on the same page don't interfere.
1978
+ */
1979
+
1980
+ interface SelectionStoreState {
1981
+ selectedIds: Set<string>;
1982
+ isMultiSelectMode: boolean;
1983
+ toggle: (id: string) => void;
1984
+ select: (id: string) => void;
1985
+ deselect: (id: string) => void;
1986
+ selectAll: (ids: string[]) => void;
1987
+ deselectAll: () => void;
1988
+ setMultiSelectMode: (enabled: boolean) => void;
1989
+ toggleMultiSelectMode: () => void;
1990
+ isSelected: (id: string) => boolean;
1991
+ selectedCount: () => number;
1992
+ getSelectedIds: () => string[];
1993
+ }
1994
+ declare function createSelectionStore(): StoreApi<SelectionStoreState>;
1995
+ declare function useSelectionStore<T>(store: StoreApi<SelectionStoreState>, selector: (state: SelectionStoreState) => T): T;
1996
+ declare const SelectionContext: React$1.Context<StoreApi<SelectionStoreState> | null>;
1997
+ declare function useSelectionContext(): StoreApi<SelectionStoreState>;
1998
+
1999
+ interface FilterPreset {
2000
+ id: string;
2001
+ name: string;
2002
+ description?: string;
2003
+ filter: FilterSpec;
2004
+ sort?: SortSpec;
2005
+ search?: {
2006
+ query: string;
2007
+ fields: string[];
2008
+ };
2009
+ isDefault?: boolean;
2010
+ createdAt: string;
2011
+ updatedAt: string;
2012
+ }
2013
+ interface ColumnPresetEntry {
2014
+ id: string;
2015
+ visible: boolean;
2016
+ width?: number;
2017
+ minWidth?: number;
2018
+ order: number;
2019
+ pinned?: "left" | "right" | false;
2020
+ formatOptions?: Record<string, unknown>;
2021
+ }
2022
+ interface ColumnPreset {
2023
+ id: string;
2024
+ name: string;
2025
+ description?: string;
2026
+ columns: ColumnPresetEntry[];
2027
+ isDefault?: boolean;
2028
+ createdAt: string;
2029
+ updatedAt: string;
2030
+ }
2031
+ type PresetChangeOperation = "created" | "updated" | "deleted";
2032
+ interface PresetChangeEvent {
2033
+ tableId: string;
2034
+ presetType: "filter" | "column";
2035
+ presetId: string;
2036
+ operation: PresetChangeOperation;
2037
+ preset?: FilterPreset | ColumnPreset;
2038
+ source: "local" | "remote";
2039
+ timestamp: number;
2040
+ }
2041
+ interface ActivePresets {
2042
+ filterId?: string;
2043
+ columnId?: string;
2044
+ viewMode?: ViewMode;
2045
+ }
2046
+ type UnsubscribeFn = () => void;
2047
+
2048
+ /**
2049
+ * table/presets/storage.ts
2050
+ *
2051
+ * Pluggable storage adapter interface for preset persistence.
2052
+ * Adapters implement CRUD + optional realtime subscription.
2053
+ */
2054
+
2055
+ interface TableStorageAdapter {
2056
+ loadFilterPresets(tableId: string): Promise<FilterPreset[]>;
2057
+ saveFilterPreset(tableId: string, preset: FilterPreset): Promise<void>;
2058
+ deleteFilterPreset(tableId: string, presetId: string): Promise<void>;
2059
+ loadColumnPresets(tableId: string): Promise<ColumnPreset[]>;
2060
+ saveColumnPreset(tableId: string, preset: ColumnPreset): Promise<void>;
2061
+ deleteColumnPreset(tableId: string, presetId: string): Promise<void>;
2062
+ loadActivePresets(tableId: string): Promise<ActivePresets>;
2063
+ saveActivePresets(tableId: string, active: ActivePresets): Promise<void>;
2064
+ subscribe?(tableId: string, callback: (event: PresetChangeEvent) => void): UnsubscribeFn;
2065
+ }
2066
+
2067
+ /**
2068
+ * table/presets/preset-store.ts
2069
+ *
2070
+ * Zustand store for reactive preset state across all tables.
2071
+ * Holds loaded presets, active selections, pending remote changes,
2072
+ * and the configurable auto-apply vs notify behavior.
2073
+ */
2074
+
2075
+ interface TablePresetSlice {
2076
+ filters: FilterPreset[];
2077
+ columns: ColumnPreset[];
2078
+ activeFilterId: string | null;
2079
+ activeColumnId: string | null;
2080
+ activeViewMode: ViewMode;
2081
+ }
2082
+ interface PresetStoreState {
2083
+ presets: Record<string, TablePresetSlice>;
2084
+ pendingChanges: PresetChangeEvent[];
2085
+ realtimeMode: "auto-apply" | "notify";
2086
+ getTablePresets: (tableId: string) => TablePresetSlice;
2087
+ loadPresets: (tableId: string, adapter: TableStorageAdapter) => Promise<void>;
2088
+ applyFilterPreset: (tableId: string, presetId: string | null) => void;
2089
+ applyColumnPreset: (tableId: string, presetId: string | null) => void;
2090
+ setViewMode: (tableId: string, mode: ViewMode) => void;
2091
+ saveFilterPreset: (tableId: string, preset: FilterPreset, adapter: TableStorageAdapter) => Promise<void>;
2092
+ saveColumnPreset: (tableId: string, preset: ColumnPreset, adapter: TableStorageAdapter) => Promise<void>;
2093
+ deleteFilterPreset: (tableId: string, presetId: string, adapter: TableStorageAdapter) => Promise<void>;
2094
+ deleteColumnPreset: (tableId: string, presetId: string, adapter: TableStorageAdapter) => Promise<void>;
2095
+ handleRemoteChange: (event: PresetChangeEvent, adapter: TableStorageAdapter) => void;
2096
+ acknowledgePendingChange: (index: number) => void;
2097
+ dismissPendingChanges: (tableId: string) => void;
2098
+ }
2099
+ declare function createPresetStore(realtimeMode?: "auto-apply" | "notify"): StoreApi<PresetStoreState>;
2100
+
2101
+ /**
2102
+ * table/presets/memory-adapter.ts
2103
+ *
2104
+ * In-memory storage adapter — zero-config default.
2105
+ * Data lives only for the session lifetime.
2106
+ */
2107
+
2108
+ declare class MemoryAdapter implements TableStorageAdapter {
2109
+ private filters;
2110
+ private columns;
2111
+ private active;
2112
+ loadFilterPresets(tableId: string): Promise<FilterPreset[]>;
2113
+ saveFilterPreset(tableId: string, preset: FilterPreset): Promise<void>;
2114
+ deleteFilterPreset(tableId: string, presetId: string): Promise<void>;
2115
+ loadColumnPresets(tableId: string): Promise<ColumnPreset[]>;
2116
+ saveColumnPreset(tableId: string, preset: ColumnPreset): Promise<void>;
2117
+ deleteColumnPreset(tableId: string, presetId: string): Promise<void>;
2118
+ loadActivePresets(tableId: string): Promise<ActivePresets>;
2119
+ saveActivePresets(tableId: string, active: ActivePresets): Promise<void>;
2120
+ }
2121
+
2122
+ interface ZustandPresetState {
2123
+ tables: Record<string, {
2124
+ filters: FilterPreset[];
2125
+ columns: ColumnPreset[];
2126
+ active: ActivePresets;
2127
+ }>;
2128
+ }
2129
+ interface ZustandAdapterOptions {
2130
+ storageKey?: string;
2131
+ storage?: PersistStorage<ZustandPresetState>;
2132
+ }
2133
+ declare class ZustandPersistAdapter implements TableStorageAdapter {
2134
+ private store;
2135
+ private listeners;
2136
+ constructor(options?: ZustandAdapterOptions);
2137
+ private getTable;
2138
+ private setTable;
2139
+ private emit;
2140
+ loadFilterPresets(tableId: string): Promise<FilterPreset[]>;
2141
+ saveFilterPreset(tableId: string, preset: FilterPreset): Promise<void>;
2142
+ deleteFilterPreset(tableId: string, presetId: string): Promise<void>;
2143
+ loadColumnPresets(tableId: string): Promise<ColumnPreset[]>;
2144
+ saveColumnPreset(tableId: string, preset: ColumnPreset): Promise<void>;
2145
+ deleteColumnPreset(tableId: string, presetId: string): Promise<void>;
2146
+ loadActivePresets(tableId: string): Promise<ActivePresets>;
2147
+ saveActivePresets(tableId: string, active: ActivePresets): Promise<void>;
2148
+ subscribe(tableId: string, callback: (event: PresetChangeEvent) => void): UnsubscribeFn;
2149
+ }
2150
+
2151
+ /**
2152
+ * table/presets/rest-adapter.ts
2153
+ *
2154
+ * REST API storage adapter with optional polling or SSE for realtime updates.
2155
+ * Requires a REST endpoint that stores presets per table ID.
2156
+ */
2157
+
2158
+ interface RestAdapterOptions {
2159
+ baseUrl: string;
2160
+ headers?: Record<string, string> | (() => Record<string, string>);
2161
+ pollInterval?: number;
2162
+ sseEndpoint?: string;
2163
+ }
2164
+ declare class RestApiAdapter implements TableStorageAdapter {
2165
+ private baseUrl;
2166
+ private headers;
2167
+ private pollInterval?;
2168
+ private sseEndpoint?;
2169
+ constructor(options: RestAdapterOptions);
2170
+ private getHeaders;
2171
+ loadFilterPresets(tableId: string): Promise<FilterPreset[]>;
2172
+ saveFilterPreset(tableId: string, preset: FilterPreset): Promise<void>;
2173
+ deleteFilterPreset(tableId: string, presetId: string): Promise<void>;
2174
+ loadColumnPresets(tableId: string): Promise<ColumnPreset[]>;
2175
+ saveColumnPreset(tableId: string, preset: ColumnPreset): Promise<void>;
2176
+ deleteColumnPreset(tableId: string, presetId: string): Promise<void>;
2177
+ loadActivePresets(tableId: string): Promise<ActivePresets>;
2178
+ saveActivePresets(tableId: string, active: ActivePresets): Promise<void>;
2179
+ subscribe(tableId: string, callback: (event: PresetChangeEvent) => void): UnsubscribeFn;
2180
+ private subscribeSSE;
2181
+ private subscribePoll;
2182
+ }
2183
+
2184
+ /**
2185
+ * table/presets/supabase-adapter.ts
2186
+ *
2187
+ * Supabase Realtime adapter for preset persistence.
2188
+ * Subscribes to a `table_presets` table for live changes.
2189
+ *
2190
+ * Uses type-only imports; the Supabase client is a peer dependency.
2191
+ */
2192
+
2193
+ interface SupabaseClient {
2194
+ from: (table: string) => {
2195
+ select: (columns?: string) => {
2196
+ eq: (col: string, val: string) => {
2197
+ eq: (col: string, val: string) => Promise<{
2198
+ data: unknown[] | null;
2199
+ error: unknown;
2200
+ }>;
2201
+ single: () => Promise<{
2202
+ data: unknown | null;
2203
+ error: unknown;
2204
+ }>;
2205
+ } & Promise<{
2206
+ data: unknown[] | null;
2207
+ error: unknown;
2208
+ }>;
2209
+ };
2210
+ upsert: (data: unknown) => Promise<{
2211
+ error: unknown;
2212
+ }>;
2213
+ delete: () => {
2214
+ eq: (col: string, val: string) => {
2215
+ eq: (col: string, val: string) => Promise<{
2216
+ error: unknown;
2217
+ }>;
2218
+ };
2219
+ };
2220
+ };
2221
+ channel: (name: string) => {
2222
+ on: (event: string, config: Record<string, unknown>, callback: (payload: Record<string, unknown>) => void) => {
2223
+ subscribe: () => {
2224
+ unsubscribe: () => void;
2225
+ };
2226
+ };
2227
+ };
2228
+ }
2229
+ interface SupabaseAdapterOptions {
2230
+ supabaseClient: SupabaseClient;
2231
+ tableName?: string;
2232
+ userId?: string;
2233
+ }
2234
+ declare class SupabaseRealtimeAdapter implements TableStorageAdapter {
2235
+ private client;
2236
+ private tableName;
2237
+ private userId;
2238
+ constructor(options: SupabaseAdapterOptions);
2239
+ loadFilterPresets(tableId: string): Promise<FilterPreset[]>;
2240
+ saveFilterPreset(tableId: string, preset: FilterPreset): Promise<void>;
2241
+ deleteFilterPreset(tableId: string, presetId: string): Promise<void>;
2242
+ loadColumnPresets(tableId: string): Promise<ColumnPreset[]>;
2243
+ saveColumnPreset(tableId: string, preset: ColumnPreset): Promise<void>;
2244
+ deleteColumnPreset(tableId: string, presetId: string): Promise<void>;
2245
+ loadActivePresets(tableId: string): Promise<ActivePresets>;
2246
+ saveActivePresets(tableId: string, active: ActivePresets): Promise<void>;
2247
+ subscribe(tableId: string, callback: (event: PresetChangeEvent) => void): UnsubscribeFn;
2248
+ }
2249
+
2250
+ /**
2251
+ * table/presets/electricsql-adapter.ts
2252
+ *
2253
+ * ElectricSQL / PGlite adapter for local-first preset persistence.
2254
+ * Uses type-only imports; PGlite is a peer/optional dependency.
2255
+ */
2256
+
2257
+ interface PGliteInstance {
2258
+ query: <T = Record<string, unknown>>(sql: string, params?: unknown[]) => Promise<{
2259
+ rows: T[];
2260
+ }>;
2261
+ exec: (sql: string) => Promise<void>;
2262
+ listen?: (channel: string, callback: (payload: string) => void) => Promise<() => void>;
2263
+ }
2264
+ interface ElectricSQLAdapterOptions {
2265
+ db: PGliteInstance;
2266
+ tableName?: string;
2267
+ }
2268
+ declare class ElectricSQLAdapter implements TableStorageAdapter {
2269
+ private db;
2270
+ private tableName;
2271
+ private initialized;
2272
+ constructor(options: ElectricSQLAdapterOptions);
2273
+ private ensureTable;
2274
+ private makeId;
2275
+ loadFilterPresets(tableId: string): Promise<FilterPreset[]>;
2276
+ saveFilterPreset(tableId: string, preset: FilterPreset): Promise<void>;
2277
+ deleteFilterPreset(tableId: string, presetId: string): Promise<void>;
2278
+ loadColumnPresets(tableId: string): Promise<ColumnPreset[]>;
2279
+ saveColumnPreset(tableId: string, preset: ColumnPreset): Promise<void>;
2280
+ deleteColumnPreset(tableId: string, presetId: string): Promise<void>;
2281
+ loadActivePresets(tableId: string): Promise<ActivePresets>;
2282
+ saveActivePresets(tableId: string, active: ActivePresets): Promise<void>;
2283
+ subscribe(tableId: string, callback: (event: PresetChangeEvent) => void): UnsubscribeFn;
2284
+ }
2285
+
2286
+ interface UseTablePresetsOptions {
2287
+ adapter?: TableStorageAdapter;
2288
+ realtimeMode?: "auto-apply" | "notify";
2289
+ enabled?: boolean;
2290
+ }
2291
+ interface UseTablePresetsResult {
2292
+ filterPresets: FilterPreset[];
2293
+ columnPresets: ColumnPreset[];
2294
+ activeFilterPreset: FilterPreset | null;
2295
+ activeColumnPreset: ColumnPreset | null;
2296
+ activeViewMode: ViewMode;
2297
+ pendingChanges: PresetChangeEvent[];
2298
+ applyFilterPreset: (id: string | null) => void;
2299
+ applyColumnPreset: (id: string | null) => void;
2300
+ setViewMode: (mode: ViewMode) => void;
2301
+ saveFilterPreset: (preset: Omit<FilterPreset, "id" | "createdAt" | "updatedAt">) => Promise<void>;
2302
+ updateFilterPreset: (id: string, patch: Partial<FilterPreset>) => Promise<void>;
2303
+ saveColumnPreset: (preset: Omit<ColumnPreset, "id" | "createdAt" | "updatedAt">) => Promise<void>;
2304
+ updateColumnPreset: (id: string, patch: Partial<ColumnPreset>) => Promise<void>;
2305
+ deleteFilterPreset: (id: string) => Promise<void>;
2306
+ deleteColumnPreset: (id: string) => Promise<void>;
2307
+ acknowledgePendingChange: (index: number) => void;
2308
+ dismissPendingChanges: () => void;
2309
+ isLoading: boolean;
2310
+ isSubscribed: boolean;
2311
+ }
2312
+ declare function useTablePresets(tableId: string, options?: UseTablePresetsOptions): UseTablePresetsResult;
2313
+
2314
+ interface TableStorageProviderProps {
2315
+ adapter: TableStorageAdapter;
2316
+ realtimeMode?: "auto-apply" | "notify";
2317
+ children: React$1.ReactNode;
2318
+ }
2319
+ declare function TableStorageProvider({ adapter, realtimeMode, children, }: TableStorageProviderProps): react_jsx_runtime.JSX.Element;
2320
+ declare function useTableStorageAdapter(): TableStorageAdapter;
2321
+ declare function useTableRealtimeMode(): "auto-apply" | "notify";
2322
+
2323
+ interface EntityListViewProps<TData extends Record<string, unknown>> {
2324
+ data?: TData[];
2325
+ viewResult?: {
2326
+ items: TData[];
2327
+ isFetching?: boolean;
2328
+ total?: number;
2329
+ };
2330
+ columns: ColumnDef<TData>[];
2331
+ itemDescriptor?: ItemDescriptor<TData>;
2332
+ renderCard?: (item: TData, context: ItemRenderContext<TData>) => React$1.ReactNode;
2333
+ renderItem?: (item: TData, context: ItemRenderContext<TData>) => React$1.ReactNode;
2334
+ defaultViewMode?: ViewMode;
2335
+ enabledViewModes?: ViewMode[];
2336
+ actions?: ActionDef<TData>[];
2337
+ onAction?: (action: string, item: TData) => void;
2338
+ enableMultiSelect?: boolean;
2339
+ onBatchAction?: (action: string, selectedItems: TData[]) => void;
2340
+ batchActions?: BatchActionDef[];
2341
+ enableInlineEdit?: boolean;
2342
+ onInlineEdit?: (item: TData, field: string, value: unknown) => void | Promise<void>;
2343
+ onInlineSave?: (item: TData, changes: Partial<TData>) => void | Promise<void>;
2344
+ emptyState?: React$1.ReactNode | EmptyStateConfig;
2345
+ tableId?: string;
2346
+ enablePresets?: boolean;
2347
+ getRowId?: (row: TData) => string;
2348
+ paginationMode?: "none" | "loadMore" | "pages";
2349
+ pageSize?: number;
2350
+ galleryColumns?: GalleryColumns;
2351
+ enableColumnResizing?: boolean;
2352
+ enableColumnPinning?: boolean;
2353
+ enableGrouping?: boolean;
2354
+ enableSearch?: boolean;
2355
+ onRefresh?: () => void;
2356
+ className?: string;
2357
+ }
2358
+ declare function EntityListView<TData extends Record<string, unknown>>(props: EntityListViewProps<TData>): react_jsx_runtime.JSX.Element;
2359
+
2360
+ interface DataTableProps<TData extends Record<string, unknown>> {
2361
+ table: TableInstance<TData>;
2362
+ actions?: ActionDef<TData>[];
2363
+ enableInlineEdit?: boolean;
2364
+ onInlineSave?: (item: TData, field: string, value: unknown) => void | Promise<void>;
2365
+ selectionStore?: StoreApi<SelectionStoreState>;
2366
+ enableMultiSelect?: boolean;
2367
+ getRowId?: (row: TData) => string;
2368
+ className?: string;
2369
+ }
2370
+ declare function DataTable<TData extends Record<string, unknown>>({ table, actions, enableInlineEdit, onInlineSave, selectionStore, enableMultiSelect, getRowId, className, }: DataTableProps<TData>): react_jsx_runtime.JSX.Element;
2371
+
2372
+ interface GalleryViewProps<TData extends Record<string, unknown>> {
2373
+ rows: Row<TData>[];
2374
+ columns: ColumnDef<TData>[];
2375
+ itemDescriptor?: ItemDescriptor<TData>;
2376
+ renderCard?: (item: TData, context: ItemRenderContext<TData>) => ReactNode;
2377
+ actions?: ActionDef<TData>[];
2378
+ enableInlineEdit?: boolean;
2379
+ onInlineSave?: (item: TData, changes: Partial<TData>) => void | Promise<void>;
2380
+ selectionStore?: StoreApi<SelectionStoreState>;
2381
+ enableMultiSelect?: boolean;
2382
+ getRowId?: (row: TData) => string;
2383
+ galleryColumns?: GalleryColumns;
2384
+ className?: string;
2385
+ }
2386
+ declare function GalleryView<TData extends Record<string, unknown>>({ rows, columns, itemDescriptor, renderCard, actions, enableInlineEdit, onInlineSave, selectionStore, enableMultiSelect, getRowId, galleryColumns, className, }: GalleryViewProps<TData>): react_jsx_runtime.JSX.Element;
2387
+
2388
+ interface ListViewProps<TData extends Record<string, unknown>> {
2389
+ rows: Row<TData>[];
2390
+ columns: ColumnDef<TData>[];
2391
+ itemDescriptor?: ItemDescriptor<TData>;
2392
+ renderItem?: (item: TData, context: ItemRenderContext<TData>) => ReactNode;
2393
+ actions?: ActionDef<TData>[];
2394
+ enableInlineEdit?: boolean;
2395
+ onInlineSave?: (item: TData, changes: Partial<TData>) => void | Promise<void>;
2396
+ selectionStore?: StoreApi<SelectionStoreState>;
2397
+ enableMultiSelect?: boolean;
2398
+ getRowId?: (row: TData) => string;
2399
+ className?: string;
2400
+ }
2401
+ declare function ListView<TData extends Record<string, unknown>>({ rows, columns, itemDescriptor, renderItem, actions, enableInlineEdit, onInlineSave, selectionStore, enableMultiSelect, getRowId, className, }: ListViewProps<TData>): react_jsx_runtime.JSX.Element;
2402
+
2403
+ interface ViewModeSwitcherProps {
2404
+ mode: ViewMode;
2405
+ onModeChange: (mode: ViewMode) => void;
2406
+ enabledModes?: ViewMode[];
2407
+ className?: string;
2408
+ }
2409
+ declare function ViewModeSwitcher({ mode, onModeChange, enabledModes, className, }: ViewModeSwitcherProps): react_jsx_runtime.JSX.Element | null;
2410
+
2411
+ interface DataTableToolbarProps<TData> {
2412
+ table: TableInstance<TData>;
2413
+ viewMode: ViewMode;
2414
+ onViewModeChange: (mode: ViewMode) => void;
2415
+ enabledViewModes?: ViewMode[];
2416
+ enableSearch?: boolean;
2417
+ onRefresh?: () => void;
2418
+ showColumnVisibility?: boolean;
2419
+ className?: string;
2420
+ children?: ReactNode;
2421
+ }
2422
+ declare function DataTableToolbar<TData>({ table, viewMode, onViewModeChange, enabledViewModes, enableSearch, onRefresh, showColumnVisibility, className, children, }: DataTableToolbarProps<TData>): react_jsx_runtime.JSX.Element;
2423
+
2424
+ interface DataTablePaginationProps<TData> {
2425
+ table: TableInstance<TData>;
2426
+ mode?: "pages" | "loadMore" | "none";
2427
+ pageSizeOptions?: number[];
2428
+ onLoadMore?: () => void;
2429
+ totalCount?: number;
2430
+ className?: string;
2431
+ }
2432
+ declare function DataTablePagination<TData>({ table, mode, pageSizeOptions, onLoadMore, totalCount, className, }: DataTablePaginationProps<TData>): react_jsx_runtime.JSX.Element | null;
2433
+
2434
+ interface DataTableColumnHeaderProps<TData> {
2435
+ column: Column<TData>;
2436
+ title: string;
2437
+ className?: string;
2438
+ }
2439
+ declare function DataTableColumnHeader<TData>({ column, title, className, }: DataTableColumnHeaderProps<TData>): react_jsx_runtime.JSX.Element;
2440
+
2441
+ interface DataTableFilterProps<TData> {
2442
+ column: Column<TData>;
2443
+ className?: string;
2444
+ }
2445
+ declare function DataTableFilter<TData>({ column, className, }: DataTableFilterProps<TData>): react_jsx_runtime.JSX.Element;
2446
+
2447
+ declare function viewAction<T>(opts: {
2448
+ onClick: (item: T) => void;
2449
+ label?: string;
2450
+ }): ActionDef<T>;
2451
+ declare function editAction<T>(opts: {
2452
+ onClick: (item: T) => void;
2453
+ label?: string;
2454
+ }): ActionDef<T>;
2455
+ declare function deleteAction<T>(opts: {
2456
+ onClick: (item: T) => void;
2457
+ label?: string;
2458
+ confirm?: string;
2459
+ }): ActionDef<T>;
2460
+ interface ActionDropdownProps<T> {
2461
+ item: T;
2462
+ actions: ActionDef<T>[];
2463
+ className?: string;
2464
+ }
2465
+ declare function ActionDropdown<T>({ item, actions, className, }: ActionDropdownProps<T>): react_jsx_runtime.JSX.Element | null;
2466
+ interface ActionButtonRowProps<T> {
2467
+ item: T;
2468
+ actions: ActionDef<T>[];
2469
+ maxVisible?: number;
2470
+ className?: string;
2471
+ }
2472
+ declare function ActionButtonRow<T>({ item, actions, maxVisible, className, }: ActionButtonRowProps<T>): react_jsx_runtime.JSX.Element;
2473
+
2474
+ interface InlineCellEditorProps<TData> {
2475
+ value: unknown;
2476
+ columnDef: ColumnDef<TData>;
2477
+ onSave: (value: unknown) => void;
2478
+ onCancel: () => void;
2479
+ className?: string;
2480
+ /** Associates the control with a `<label htmlFor>` in parent forms. */
2481
+ inputId?: string;
2482
+ /** Accessible name for the control (required when used without a visible label). */
2483
+ ariaLabel?: string;
2484
+ }
2485
+ declare function InlineCellEditor<TData>({ value: initialValue, columnDef, onSave, onCancel, className, inputId, ariaLabel, }: InlineCellEditorProps<TData>): react_jsx_runtime.JSX.Element;
2486
+ interface InlineItemEditorProps<TData extends Record<string, unknown>> {
2487
+ item: TData;
2488
+ columns: ColumnDef<TData>[];
2489
+ itemDescriptor?: ItemDescriptor<TData>;
2490
+ onSave: (changes: Partial<TData>) => void;
2491
+ onCancel: () => void;
2492
+ className?: string;
2493
+ }
2494
+ declare function InlineItemEditor<TData extends Record<string, unknown>>({ item, columns, itemDescriptor: _itemDescriptor, onSave, onCancel, className, }: InlineItemEditorProps<TData>): react_jsx_runtime.JSX.Element;
2495
+
2496
+ interface MultiSelectBarProps {
2497
+ store: StoreApi<SelectionStoreState>;
2498
+ batchActions?: BatchActionDef[];
2499
+ onBatchAction?: (actionId: string, selectedIds: string[]) => void;
2500
+ totalCount?: number;
2501
+ className?: string;
2502
+ }
2503
+ declare function MultiSelectBar({ store, batchActions, onBatchAction, totalCount, className, }: MultiSelectBarProps): react_jsx_runtime.JSX.Element | null;
2504
+
2505
+ interface EmptyStateProps {
2506
+ config?: EmptyStateConfig | React$1.ReactNode;
2507
+ isFiltered?: boolean;
2508
+ className?: string;
2509
+ }
2510
+ declare function EmptyState({ config, isFiltered, className }: EmptyStateProps): react_jsx_runtime.JSX.Element;
2511
+
2512
+ interface FilterPresetDialogProps<TData> {
2513
+ open: boolean;
2514
+ onOpenChange: (open: boolean) => void;
2515
+ columns: ColumnDef<TData>[];
2516
+ preset?: FilterPreset | null;
2517
+ onSave: (preset: Omit<FilterPreset, "id" | "createdAt" | "updatedAt">) => void;
2518
+ }
2519
+ declare function FilterPresetDialog<TData>({ open, onOpenChange, columns, preset, onSave, }: FilterPresetDialogProps<TData>): react_jsx_runtime.JSX.Element | null;
2520
+
2521
+ interface ColumnPresetDialogProps<TData> {
2522
+ open: boolean;
2523
+ onOpenChange: (open: boolean) => void;
2524
+ columns: ColumnDef<TData>[];
2525
+ preset?: ColumnPreset | null;
2526
+ onSave: (preset: Omit<ColumnPreset, "id" | "createdAt" | "updatedAt">) => void;
2527
+ }
2528
+ declare function ColumnPresetDialog<TData>({ open, onOpenChange, columns, preset, onSave, }: ColumnPresetDialogProps<TData>): react_jsx_runtime.JSX.Element | null;
2529
+
2530
+ interface PresetPickerProps {
2531
+ filterPresets: FilterPreset[];
2532
+ columnPresets: ColumnPreset[];
2533
+ activeFilterId: string | null;
2534
+ activeColumnId: string | null;
2535
+ onApplyFilter: (id: string | null) => void;
2536
+ onApplyColumn: (id: string | null) => void;
2537
+ onEditFilter: (preset: FilterPreset) => void;
2538
+ onEditColumn: (preset: ColumnPreset) => void;
2539
+ onDeleteFilter: (id: string) => void;
2540
+ onDeleteColumn: (id: string) => void;
2541
+ onNewFilter: () => void;
2542
+ onNewColumn: () => void;
2543
+ pendingChangesCount?: number;
2544
+ className?: string;
2545
+ }
2546
+ declare function PresetPicker({ filterPresets, columnPresets, activeFilterId, activeColumnId, onApplyFilter, onApplyColumn, onEditFilter, onEditColumn, onDeleteFilter, onDeleteColumn, onNewFilter, onNewColumn, pendingChangesCount, className, }: PresetPickerProps): react_jsx_runtime.JSX.Element;
2547
+
2548
+ /**
2549
+ * ui/pure-columns.tsx
2550
+ *
2551
+ * Column builder functions that return the pure ColumnDef type
2552
+ * from src/table/types.ts — parallel to existing TanStack-based columns.tsx.
2553
+ */
2554
+
2555
+ interface BaseColumnOptions<TData> {
2556
+ field: keyof TData & string;
2557
+ header: string;
2558
+ size?: number;
2559
+ minSize?: number;
2560
+ maxSize?: number;
2561
+ enableSorting?: boolean;
2562
+ enableFiltering?: boolean;
2563
+ enableHiding?: boolean;
2564
+ enableResizing?: boolean;
2565
+ enablePinning?: boolean;
2566
+ editable?: boolean;
2567
+ cell?: (context: CellContext<TData>) => React$1.ReactNode;
2568
+ }
2569
+ interface EnumOption {
2570
+ label: string;
2571
+ value: string;
2572
+ /** Hex color — legacy bordered-badge approach (outline only). */
2573
+ color?: string;
2574
+ /** Full Tailwind class string — renders a solid flat badge with no border. */
2575
+ badgeClassName?: string;
2576
+ }
2577
+ declare function selectionColumn<TData>(): ColumnDef<TData>;
2578
+ declare function textColumn<TData>(options: BaseColumnOptions<TData>): ColumnDef<TData>;
2579
+ declare function numberColumn<TData>(options: BaseColumnOptions<TData>): ColumnDef<TData>;
2580
+ declare function dateColumn<TData>(options: BaseColumnOptions<TData> & {
2581
+ format?: (date: Date) => string;
2582
+ }): ColumnDef<TData>;
2583
+ declare function booleanColumn<TData>(options: BaseColumnOptions<TData> & {
2584
+ trueLabel?: string;
2585
+ falseLabel?: string;
2586
+ }): ColumnDef<TData>;
2587
+ declare function enumColumn<TData>(options: BaseColumnOptions<TData> & {
2588
+ options: EnumOption[];
2589
+ }): ColumnDef<TData>;
2590
+ declare function actionsColumn<TData>(): ColumnDef<TData>;
2591
+
2592
+ /**
2593
+ * ui/table-primitives.tsx
2594
+ *
2595
+ * shadcn/ui-style table primitives — thin wrappers around HTML table
2596
+ * elements with Tailwind semantic classes.
2597
+ */
2598
+
2599
+ declare const Table: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLTableElement> & React$1.RefAttributes<HTMLTableElement>>;
2600
+ declare const TableHeader: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLTableSectionElement> & React$1.RefAttributes<HTMLTableSectionElement>>;
2601
+ declare const TableBody: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLTableSectionElement> & React$1.RefAttributes<HTMLTableSectionElement>>;
2602
+ declare const TableFooter: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLTableSectionElement> & React$1.RefAttributes<HTMLTableSectionElement>>;
2603
+ declare const TableRow: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLTableRowElement> & React$1.RefAttributes<HTMLTableRowElement>>;
2604
+ declare const TableHead: React$1.ForwardRefExoticComponent<React$1.ThHTMLAttributes<HTMLTableCellElement> & React$1.RefAttributes<HTMLTableCellElement>>;
2605
+ declare const TableCell: React$1.ForwardRefExoticComponent<React$1.TdHTMLAttributes<HTMLTableCellElement> & React$1.RefAttributes<HTMLTableCellElement>>;
2606
+ declare const TableCaption: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLTableCaptionElement> & React$1.RefAttributes<HTMLTableCaptionElement>>;
2607
+
2608
+ export { type AccessorFn, ActionButtonRow, type ActionDef, ActionDropdown, type ActionItem, type ActivePresets, type AdapterStatus, type AggregationFn, type BatchActionDef, type BelongsToRelation, type CRUDMode, type CRUDOptions, type CRUDState, type CascadeContext, type Cell, type CellContext, type CellRenderer, type ChangeOperation, type ChangeSet, type ChannelConfig, type Column, type ColumnFilterType, type ColumnFiltersState, type ColumnOrderState, type ColumnPinningState, type ColumnPreset, ColumnPresetDialog, type ColumnPresetEntry, type ColumnSizingState, type ColumnVisibilityState, type CompletenessMode, DataTable, DataTableColumnHeader, DataTableFilter, DataTablePagination, DataTableToolbar, type DirtyFields, type ElectricAdapterOptions, ElectricSQLAdapter as ElectricSQLPresetAdapter, type ElectricSQLAdapterOptions as ElectricSQLPresetAdapterOptions, type ElectricTableConfig, EmptyState, type EmptyStateConfig, type EngineOptions, type EntityChange, type EntityColumnMeta, type EntityDescriptor, EntityDetailSheet, EntityFormSheet, type EntityId, EntityListView, type EntityListViewProps, type EntityQueryOptions, type EntitySchema, type EntityState, EntityTable, type EntityType, type ExpandedState, type FieldDescriptor, type FieldType, type FilterClause, type FilterFn, type FilterGroup, type FilterOperator, type FilterPreset, FilterPresetDialog, type FilterSpec, GQLClient, type GQLClientConfig, type GQLEntityOptions, type GQLError, type GQLListOptions, type GQLResponse, type GalleryColumns, GalleryView, type GraphState, type GroupingState, type HasManyRelation, type Header, type HeaderContext, type HeaderGroup, type HeaderRenderer, InlineCellEditor$1 as InlineCellEditor, InlineItemEditor, type ItemDescriptor, type ItemDescriptorBadge, type ItemDescriptorMeta, type ItemRenderContext, type ListFetchParams, type ListQueryOptions, type ListResponse, type ListState, ListView, type ManagerOptions, type ManyToManyRelation, MemoryAdapter, MultiSelectBar, type PaginationState, type PresetChangeEvent, type PresetChangeOperation, PresetPicker, type PresetStoreState, type UnsubscribeFn as PresetUnsubscribeFn, type PrismaEntityConfigOptions, type ColumnDef as PureColumnDef, type ColumnMeta as PureColumnMeta, InlineCellEditor as PureInlineCellEditor, type RealtimeAdapter, RealtimeManager, type RelationDescriptor, type RestAdapterOptions, RestApiAdapter, type Row, type RowModel, type RowSelectionState, SelectionContext, type SelectionStoreState, Sheet, type SortClause, type SortDirection, SortHeader, type SortSpec, type SortingFn, type SortingState, type SubscriptionConfig, type SupabaseAdapterOptions$1 as SupabaseAdapterOptions, SupabaseRealtimeAdapter as SupabasePresetAdapter, type SupabaseAdapterOptions as SupabasePresetAdapterOptions, type SyncAdapter, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, type TableInstance, type TableOptions, type TablePresetSlice, TableRow, type TableState, type TableStorageAdapter, TableStorageProvider, type TableStorageProviderProps, type UnsubscribeFn$1 as UnsubscribeFn, type Updater, type UseEntityViewOptions, type UseEntityViewResult, type UseLocalFirstResult, type UseTablePresetsOptions, type UseTablePresetsResult, type ViewDescriptor, type ViewFetchParams, type ViewMode, ViewModeSwitcher, type WebSocketAdapterOptions, type ZustandAdapterOptions, ZustandPersistAdapter, actionsColumn$1 as actionsColumn, applyView, booleanColumn$1 as booleanColumn, cascadeInvalidation, checkCompleteness, compareEntities, configureEngine, createConvexAdapter, createElectricAdapter, createGQLClient, createGraphQLSubscriptionAdapter, createPresetStore, createPrismaEntityConfig, createRow, createSelectionStore, createSupabaseRealtimeAdapter, createWebSocketAdapter, dateColumn$1 as dateColumn, dedupe, deleteAction, editAction, enumColumn$1 as enumColumn, executeGQL, fetchEntity, fetchList, flattenClauses, getCoreRowModel, getExpandedRowModel, getFacetedMinMaxValues, getFacetedRowModel, getFacetedUniqueValues, getFilteredRowModel, getGroupedRowModel, getPaginatedRowModel, getRealtimeManager, getSchema, getSelectedRowModel, getSortedRowModel, hasCustomPredicates, matchesFilter, matchesSearch, normalizeGQLResponse, numberColumn$1 as numberColumn, prismaRelationsToSchema, actionsColumn as pureActionsColumn, booleanColumn as pureBooleanColumn, dateColumn as pureDateColumn, enumColumn as pureEnumColumn, numberColumn as pureNumberColumn, selectionColumn as pureSelectionColumn, textColumn as pureTextColumn, readRelations, registerSchema, resetRealtimeManager, selectionColumn$1 as selectionColumn, serializeKey, startGarbageCollector, stopGarbageCollector, textColumn$1 as textColumn, toGraphQLVariables, toPrismaInclude, toPrismaOrderBy, toPrismaWhere, toRestParams, toSQLClauses, useEntity, useEntityAugment, useEntityCRUD, useEntityList, useEntityMutation, useEntityView, useGQLEntity, useGQLList, useGQLMutation, useGQLSubscription, useGraphDevTools, useGraphStore, useLocalFirst, usePGliteQuery, useSelectionContext, useSelectionStore, useSuspenseEntity, useSuspenseEntityList, useTable, useTablePresets, useTableRealtimeMode, useTableStorageAdapter, viewAction };