@prometheus-ags/prometheus-entity-management 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -11,6 +11,20 @@ type EntityType = string;
11
11
  type EntityId = string;
12
12
  /** Stable string key for a list query (often `JSON.stringify`-shaped). Lists store IDs only under this key. */
13
13
  type QueryKey = string;
14
+ /** Provenance of the latest known entity state. */
15
+ type SyncOrigin = "server" | "client" | "optimistic";
16
+ /** Optional sync-facing metadata kept outside canonical entity payloads. */
17
+ interface EntitySyncMetadata {
18
+ synced: boolean;
19
+ origin: SyncOrigin;
20
+ updatedAt: number | null;
21
+ }
22
+ /** Snapshot shape returned by sync-aware reads and graph-native query helpers. */
23
+ type EntitySnapshot<T extends Record<string, unknown>> = T & {
24
+ $synced: boolean;
25
+ $origin: SyncOrigin;
26
+ $updatedAt: number | null;
27
+ };
14
28
  /**
15
29
  * Fetch/cache metadata for a single entity instance (`type:id`).
16
30
  * Separates transport concerns from canonical `entities` data so hooks can show spinners and stale-while-revalidate without mutating server-shaped fields.
@@ -51,6 +65,8 @@ interface GraphState {
51
65
  patches: Record<EntityType, Record<EntityId, Record<string, unknown>>>;
52
66
  /** Per-entity fetch lifecycle keyed as `${type}:${id}`. */
53
67
  entityStates: Record<string, EntityState>;
68
+ /** Optional sync/provenance state layered beside the canonical entity payload. */
69
+ syncMetadata: Record<string, EntitySyncMetadata>;
54
70
  /** List slots keyed by serialized query keys. Values hold id arrays and pagination — not entity clones. */
55
71
  lists: Record<QueryKey, ListState>;
56
72
  /**
@@ -91,6 +107,10 @@ interface GraphState {
91
107
  setEntityFetched: (type: EntityType, id: EntityId) => void;
92
108
  /** Drive background revalidation: when true, hooks refetch while still showing cached `readEntity` data. */
93
109
  setEntityStale: (type: EntityType, id: EntityId, stale: boolean) => void;
110
+ /** Merge sync metadata for one entity without polluting canonical server fields. */
111
+ setEntitySyncMetadata: (type: EntityType, id: EntityId, metadata: Partial<EntitySyncMetadata>) => void;
112
+ /** Clear sync metadata for one entity. */
113
+ clearEntitySyncMetadata: (type: EntityType, id: EntityId) => void;
94
114
  /**
95
115
  * Replace list ids and merge pagination meta after a primary list fetch (not load-more).
96
116
  * Resets fetching flags and clears list error on success path (engine calls this after normalize).
@@ -131,6 +151,10 @@ interface GraphState {
131
151
  * @returns Merged view suitable for rendering; not a deep clone
132
152
  */
133
153
  readEntity: <T = Record<string, unknown>>(type: EntityType, id: EntityId) => T | null;
154
+ /**
155
+ * Sync-aware read path: canonical entity + patches + virtual sync metadata (`$synced`, `$origin`, `$updatedAt`).
156
+ */
157
+ readEntitySnapshot: <T = Record<string, unknown>>(type: EntityType, id: EntityId) => EntitySnapshot<T & Record<string, unknown>> | null;
134
158
  }
135
159
  /**
136
160
  * Global entity graph store (Zustand + Immer). **Components should not subscribe directly** — use hooks so layering stays `Component → hook → store`.
@@ -168,6 +192,13 @@ declare const useGraphStore: zustand.UseBoundStore<Omit<Omit<zustand.StoreApi<Gr
168
192
  stale: boolean;
169
193
  };
170
194
  };
195
+ syncMetadata: {
196
+ [x: string]: {
197
+ synced: boolean;
198
+ origin: SyncOrigin;
199
+ updatedAt: number | null;
200
+ };
201
+ };
171
202
  lists: {
172
203
  [x: string]: {
173
204
  ids: string[];
@@ -199,6 +230,8 @@ declare const useGraphStore: zustand.UseBoundStore<Omit<Omit<zustand.StoreApi<Gr
199
230
  setEntityError: (type: EntityType, id: EntityId, error: string | null) => void;
200
231
  setEntityFetched: (type: EntityType, id: EntityId) => void;
201
232
  setEntityStale: (type: EntityType, id: EntityId, stale: boolean) => void;
233
+ setEntitySyncMetadata: (type: EntityType, id: EntityId, metadata: Partial<EntitySyncMetadata>) => void;
234
+ clearEntitySyncMetadata: (type: EntityType, id: EntityId) => void;
202
235
  setListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
203
236
  appendListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
204
237
  prependListResult: (key: QueryKey, ids: EntityId[], meta?: Partial<ListState>) => void;
@@ -212,6 +245,7 @@ declare const useGraphStore: zustand.UseBoundStore<Omit<Omit<zustand.StoreApi<Gr
212
245
  invalidateLists: (matcher: string | ((key: QueryKey) => boolean)) => void;
213
246
  invalidateType: (type: EntityType) => void;
214
247
  readEntity: <T = Record<string, unknown>>(type: EntityType, id: EntityId) => T | null;
248
+ readEntitySnapshot: <T = Record<string, unknown>>(type: EntityType, id: EntityId) => EntitySnapshot<T & Record<string, unknown>> | null;
215
249
  }) => void), shouldReplace?: false): void;
216
250
  setState(nextStateOrUpdater: GraphState | ((state: {
217
251
  entities: {
@@ -236,6 +270,13 @@ declare const useGraphStore: zustand.UseBoundStore<Omit<Omit<zustand.StoreApi<Gr
236
270
  stale: boolean;
237
271
  };
238
272
  };
273
+ syncMetadata: {
274
+ [x: string]: {
275
+ synced: boolean;
276
+ origin: SyncOrigin;
277
+ updatedAt: number | null;
278
+ };
279
+ };
239
280
  lists: {
240
281
  [x: string]: {
241
282
  ids: string[];
@@ -267,6 +308,8 @@ declare const useGraphStore: zustand.UseBoundStore<Omit<Omit<zustand.StoreApi<Gr
267
308
  setEntityError: (type: EntityType, id: EntityId, error: string | null) => void;
268
309
  setEntityFetched: (type: EntityType, id: EntityId) => void;
269
310
  setEntityStale: (type: EntityType, id: EntityId, stale: boolean) => void;
311
+ setEntitySyncMetadata: (type: EntityType, id: EntityId, metadata: Partial<EntitySyncMetadata>) => void;
312
+ clearEntitySyncMetadata: (type: EntityType, id: EntityId) => void;
270
313
  setListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
271
314
  appendListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
272
315
  prependListResult: (key: QueryKey, ids: EntityId[], meta?: Partial<ListState>) => void;
@@ -280,9 +323,219 @@ declare const useGraphStore: zustand.UseBoundStore<Omit<Omit<zustand.StoreApi<Gr
280
323
  invalidateLists: (matcher: string | ((key: QueryKey) => boolean)) => void;
281
324
  invalidateType: (type: EntityType) => void;
282
325
  readEntity: <T = Record<string, unknown>>(type: EntityType, id: EntityId) => T | null;
326
+ readEntitySnapshot: <T = Record<string, unknown>>(type: EntityType, id: EntityId) => EntitySnapshot<T & Record<string, unknown>> | null;
283
327
  }) => void), shouldReplace: true): void;
284
328
  }>;
285
329
 
330
+ type GraphStore = ReturnType<typeof useGraphStore.getState>;
331
+ type GraphIncludeMap = Record<string, GraphIncludeRelation>;
332
+ type GraphIncludeRelation = {
333
+ type: EntityType;
334
+ via: {
335
+ kind: "field";
336
+ field: string;
337
+ };
338
+ include?: GraphIncludeMap;
339
+ } | {
340
+ type: EntityType;
341
+ via: {
342
+ kind: "array";
343
+ field: string;
344
+ };
345
+ include?: GraphIncludeMap;
346
+ } | {
347
+ type: EntityType;
348
+ via: {
349
+ kind: "list";
350
+ key: QueryKey | ((entity: Record<string, unknown>) => QueryKey | null | undefined);
351
+ };
352
+ include?: GraphIncludeMap;
353
+ } | {
354
+ type: EntityType;
355
+ via: {
356
+ kind: "resolver";
357
+ resolve: (entity: Record<string, unknown>, store: GraphStore) => EntityId | EntityId[] | null | undefined;
358
+ };
359
+ include?: GraphIncludeMap;
360
+ };
361
+ interface GraphQueryOptions<TEntity extends Record<string, unknown>> {
362
+ type: EntityType;
363
+ id?: EntityId;
364
+ ids?: EntityId[];
365
+ listKey?: QueryKey;
366
+ where?: (entity: EntitySnapshot<TEntity>) => boolean;
367
+ sort?: (a: EntitySnapshot<TEntity>, b: EntitySnapshot<TEntity>) => number;
368
+ include?: GraphIncludeMap;
369
+ select?: ((entity: Record<string, unknown>) => unknown) | string[];
370
+ }
371
+ type ProjectedRow = Record<string, unknown>;
372
+ declare function queryOnce<TEntity extends Record<string, unknown>>(opts: GraphQueryOptions<TEntity> & {
373
+ id: EntityId;
374
+ }): ProjectedRow | null;
375
+ declare function queryOnce<TEntity extends Record<string, unknown>>(opts: GraphQueryOptions<TEntity>): ProjectedRow[];
376
+ declare const selectGraph: typeof queryOnce;
377
+
378
+ interface GraphDataSnapshot {
379
+ entities: ReturnType<typeof useGraphStore.getState>["entities"];
380
+ patches: ReturnType<typeof useGraphStore.getState>["patches"];
381
+ entityStates: ReturnType<typeof useGraphStore.getState>["entityStates"];
382
+ syncMetadata: ReturnType<typeof useGraphStore.getState>["syncMetadata"];
383
+ lists: ReturnType<typeof useGraphStore.getState>["lists"];
384
+ }
385
+ interface GraphTransaction {
386
+ upsertEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => GraphTransaction;
387
+ replaceEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => GraphTransaction;
388
+ removeEntity: (type: EntityType, id: EntityId) => GraphTransaction;
389
+ patchEntity: (type: EntityType, id: EntityId, patch: Record<string, unknown>) => GraphTransaction;
390
+ clearPatch: (type: EntityType, id: EntityId) => GraphTransaction;
391
+ insertIdInList: (key: QueryKey, id: EntityId, position: "start" | "end" | number) => GraphTransaction;
392
+ removeIdFromAllLists: (type: EntityType, id: EntityId) => GraphTransaction;
393
+ setEntitySyncMetadata: (type: EntityType, id: EntityId, metadata: Partial<EntitySyncMetadata>) => GraphTransaction;
394
+ markEntityPending: (type: EntityType, id: EntityId, origin?: SyncOrigin) => GraphTransaction;
395
+ markEntitySynced: (type: EntityType, id: EntityId, origin?: SyncOrigin) => GraphTransaction;
396
+ commit: () => void;
397
+ rollback: () => void;
398
+ snapshot: () => GraphDataSnapshot;
399
+ }
400
+ interface GraphActionOptions<TInput, TResult> {
401
+ key?: string;
402
+ optimistic?: (tx: GraphTransaction, input: TInput) => void;
403
+ run: (tx: GraphTransaction, input: TInput) => Promise<TResult> | TResult;
404
+ onSuccess?: (result: TResult, input: TInput, tx: GraphTransaction) => void;
405
+ onError?: (error: Error, input: TInput) => void;
406
+ }
407
+ interface GraphActionRecord$1 {
408
+ id: string;
409
+ key: string;
410
+ input: unknown;
411
+ enqueuedAt: string;
412
+ }
413
+ type GraphActionEvent = {
414
+ type: "enqueued";
415
+ record: GraphActionRecord$1;
416
+ } | {
417
+ type: "settled";
418
+ record: GraphActionRecord$1;
419
+ };
420
+ declare function createGraphTransaction(): GraphTransaction;
421
+ declare function createGraphAction<TInput, TResult>(opts: GraphActionOptions<TInput, TResult>): (input: TInput) => Promise<TResult>;
422
+
423
+ interface GraphEffectEvent<T> {
424
+ key: string;
425
+ value: T;
426
+ previousValue: T;
427
+ }
428
+ interface GraphEffectOptions<T> {
429
+ query: () => T[] | T | null;
430
+ getKey?: (value: T, index: number) => string;
431
+ skipInitial?: boolean;
432
+ isEqual?: (previousValue: T, nextValue: T) => boolean;
433
+ onEnter?: (event: {
434
+ key: string;
435
+ value: T;
436
+ }) => void;
437
+ onUpdate?: (event: GraphEffectEvent<T>) => void;
438
+ onExit?: (event: {
439
+ key: string;
440
+ previousValue: T;
441
+ }) => void;
442
+ }
443
+ interface GraphEffectHandle {
444
+ dispose: () => void;
445
+ }
446
+ declare function createGraphEffect<T>(opts: GraphEffectOptions<T>): GraphEffectHandle;
447
+
448
+ /**
449
+ * Compiles a {@link FilterSpec} into a Prisma `where` object (plain JSON-serializable shape).
450
+ *
451
+ * Operator mapping matches common Prisma filter APIs: `eq` → `equals`, string ops use `mode: "insensitive"`,
452
+ * `nin` → `notIn`, `arrayContains` → `has`, `isNull` uses `null` / `{ not: null }` per `value`, and `isNotNull` → `{ not: null }`.
453
+ * Unsupported ops (`between`, `arrayOverlaps`, `matches`, `custom`) are omitted from the result.
454
+ *
455
+ * Top-level clause arrays are combined with `AND`. {@link FilterGroup} uses `AND` / `OR` to match `group.logic`.
456
+ */
457
+ declare function toPrismaWhere(filter: FilterSpec): Record<string, unknown>;
458
+ /**
459
+ * Compiles a {@link SortSpec} into Prisma `orderBy` form: `[{ fieldName: "asc" | "desc" }, …]`.
460
+ * `nulls` and `comparator` on {@link SortClause} are ignored (local-only); extend callers if your Prisma version supports null ordering.
461
+ */
462
+ declare function toPrismaOrderBy(sort: SortSpec): Record<string, string>[];
463
+
464
+ /**
465
+ * Transport-agnostic comparison operators. Same spec can compile to REST, SQL, GraphQL, or local JS (`evaluator`).
466
+ * `custom` opts out of automatic serialization — use for predicates only the client can evaluate.
467
+ */
468
+ type FilterOperator = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "nin" | "contains" | "startsWith" | "endsWith" | "isNull" | "isNotNull" | "between" | "arrayContains" | "arrayOverlaps" | "matches" | "custom";
469
+ /** Atomic filter: field path, operator, optional value, and optional JS predicate for `custom`. */
470
+ interface FilterClause {
471
+ field: string;
472
+ op: FilterOperator;
473
+ value?: unknown;
474
+ predicate?: (fieldValue: unknown, entity: Record<string, unknown>) => boolean;
475
+ }
476
+ type FilterLogic = "and" | "or";
477
+ /** Nested boolean group so you can express `(A AND B) OR C` without losing structure when compiling to backends. */
478
+ interface FilterGroup {
479
+ logic: FilterLogic;
480
+ clauses: Array<FilterClause | FilterGroup>;
481
+ }
482
+ /** Top-level filter: flat AND list of clauses, or a recursive `FilterGroup`. */
483
+ type FilterSpec = FilterGroup | FilterClause[];
484
+ type SortDirection = "asc" | "desc";
485
+ /** Single sort key with optional null ordering and custom comparator for local sort parity with remote semantics. */
486
+ interface SortClause {
487
+ field: string;
488
+ direction: SortDirection;
489
+ nulls?: "first" | "last";
490
+ comparator?: (a: unknown, b: unknown) => number;
491
+ }
492
+ /** Ordered multi-key sort (stable application in `compareEntities`). */
493
+ type SortSpec = SortClause[];
494
+ /**
495
+ * Everything `useEntityView` needs to describe a virtualized collection: filters, sorts, and simple multi-field search.
496
+ * One descriptor can drive local evaluation, remote query compilation, or hybrid mode.
497
+ */
498
+ interface ViewDescriptor {
499
+ filter?: FilterSpec;
500
+ sort?: SortSpec;
501
+ search?: {
502
+ query: string;
503
+ fields: string[];
504
+ minChars?: number;
505
+ };
506
+ }
507
+ /**
508
+ * 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).
509
+ */
510
+ type CompletenessMode = "local" | "remote" | "hybrid";
511
+ /**
512
+ * Compile a view to flat REST query params (`sort`, `q`, and `field[op]=value` keys). Skips `custom` clauses — those cannot be expressed as strings.
513
+ */
514
+ declare function toRestParams(view: ViewDescriptor): Record<string, string>;
515
+ /**
516
+ * Compile a view to parameterized SQL fragments for server-side filtering/sorting. Unknown ops become `TRUE` — validate or restrict ops at the edge.
517
+ */
518
+ declare function toSQLClauses(view: ViewDescriptor): {
519
+ where: string;
520
+ orderBy: string;
521
+ params: unknown[];
522
+ };
523
+ /**
524
+ * Produce a GraphQL-variable-shaped object from a view (Hasura/Postgraphile-style `_op` maps). Intended as a starting point — wire to your actual schema.
525
+ */
526
+ declare function toGraphQLVariables(view: ViewDescriptor): {
527
+ where?: Record<string, unknown>;
528
+ orderBy?: Array<Record<string, unknown>>;
529
+ search?: string;
530
+ };
531
+
532
+ /**
533
+ * Normalize nested `FilterGroup` trees to a flat clause list for compilers that only understand atomic predicates.
534
+ */
535
+ declare function flattenClauses(filter: FilterSpec): FilterClause[];
536
+ /** True if any clause requires client-side `predicate` logic — forces local/hybrid evaluation paths that cannot be pushed to generic REST/SQL. */
537
+ declare function hasCustomPredicates(filter: FilterSpec): boolean;
538
+
286
539
  /**
287
540
  * Process-wide defaults for stale times, retries, and background revalidation.
288
541
  * Keeps hook signatures small: `useEntity` / `useEntityList` merge these with per-query overrides.
@@ -388,360 +641,562 @@ declare function fetchEntity<TRaw, TEntity extends Record<string, unknown>>(opts
388
641
  declare function fetchList<TRaw, TEntity extends Record<string, unknown>>(opts: ListQueryOptions<TRaw, TEntity>, params: ListFetchParams, engineOpts: Required<EngineOptions>, isLoadMore?: boolean): Promise<void>;
389
642
 
390
643
  /**
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.
644
+ * Precompiled transport payloads for one view snapshot pass to REST, GraphQL, or SQL backends without re-deriving from `ViewDescriptor`.
426
645
  */
427
- interface UseEntityResult<T> {
428
- data: T | null;
429
- isLoading: boolean;
430
- isFetching: boolean;
431
- error: string | null;
432
- isStale: boolean;
433
- refetch: () => void;
646
+ interface ViewFetchParams {
647
+ rest: Record<string, string>;
648
+ graphql: ReturnType<typeof toGraphQLVariables>;
649
+ sql: ReturnType<typeof toSQLClauses>;
650
+ view: ViewDescriptor;
434
651
  }
435
652
  /**
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
- * ```
653
+ * 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.
654
+ * `baseQueryKey` identifies the underlying id list in the graph; the hook may create additional keys for remote result sets.
451
655
  */
452
- declare function useEntity<TRaw, TEntity extends Record<string, unknown>>(opts: EntityQueryOptions<TRaw, TEntity>): UseEntityResult<TEntity>;
656
+ interface UseEntityViewOptions<TEntity extends Record<string, unknown>> {
657
+ type: EntityType;
658
+ baseQueryKey: unknown[];
659
+ view: ViewDescriptor;
660
+ mode?: CompletenessMode;
661
+ remoteFetch?: (params: ViewFetchParams) => Promise<ListResponse<TEntity>>;
662
+ normalize?: (raw: TEntity) => {
663
+ id: EntityId;
664
+ data: Record<string, unknown>;
665
+ };
666
+ remoteDebounce?: number;
667
+ staleTime?: number;
668
+ enabled?: boolean;
669
+ /** SSR-seeded ids written once into `lists[baseKey]` to avoid empty-state flash before hydration fetch. */
670
+ initialIds?: EntityId[];
671
+ /** SSR-seeded total for completeness heuristics when ids are preloaded. */
672
+ initialTotal?: number;
673
+ }
453
674
  /**
454
- * Resolved rows for a list query: **`items` joins `ids` to the graph** at render time so shared entities update everywhere.
675
+ * Rich list UI state: projected `items`/`viewIds`, completeness mode, remote vs local fetching flags, and imperative view updaters.
676
+ * `isShowingLocalPending` signals hybrid mode where stale local rows are visible while a remote round-trip runs.
455
677
  */
456
- interface UseEntityListResult<TEntity> {
678
+ interface UseEntityViewResult<TEntity> {
457
679
  items: TEntity[];
458
- ids: EntityId[];
680
+ viewIds: EntityId[];
681
+ viewTotal: number | null;
459
682
  isLoading: boolean;
460
683
  isFetching: boolean;
461
- isFetchingMore: boolean;
684
+ isRemoteFetching: boolean;
685
+ isShowingLocalPending: boolean;
462
686
  error: string | null;
463
687
  hasNextPage: boolean;
464
- hasPrevPage: boolean;
465
- total: number | null;
466
- currentPage: number | null;
467
688
  fetchNextPage: () => void;
689
+ isLocallyComplete: boolean;
690
+ completenessMode: CompletenessMode;
691
+ setView: (v: Partial<ViewDescriptor>) => void;
692
+ setFilter: (f: FilterSpec | null) => void;
693
+ setSort: (s: SortSpec | null) => void;
694
+ setSearch: (q: string) => void;
695
+ clearView: () => void;
468
696
  refetch: () => void;
697
+ isFetchingMore: boolean;
469
698
  }
470
699
  /**
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).
700
+ * Higher-level list hook: combines **graph-backed id lists**, declarative `ViewDescriptor`, local `applyView`, optional remote fetch, and realtime sorted insertion.
701
+ * Solves “filters tied to one query cache” by deriving the visible id order from the shared graph whenever possible.
473
702
  *
474
- * @param opts - List query instruction (`ListQueryOptions`)
475
- * @returns Hydrated `items`, raw `ids`, pagination helpers, and fetch flags
703
+ * @param opts - Base type/key, initial view, optional `remoteFetch` + `normalize`, SSR seeds, forced `mode`
704
+ * @returns Projected entities, view metadata, completeness, and setters for interactive toolbars
476
705
  *
477
706
  * @example
478
707
  * ```tsx
479
- * const { items, fetchNextPage, hasNextPage } = useEntityList({
708
+ * const view = useEntityView({
480
709
  * type: "Task",
481
- * queryKey: ["tasks", { projectId }],
482
- * fetch: (p) => api.listTasks({ ...p, projectId }),
710
+ * baseQueryKey: ["tasks", projectId],
711
+ * view: { filter: [{ field: "status", op: "eq", value: "open" }], sort: [{ field: "dueAt", direction: "asc" }] },
712
+ * remoteFetch: (p) => api.tasksQuery(p.rest),
483
713
  * normalize: (raw) => ({ id: raw.id, data: raw }),
484
- * mode: "append",
485
714
  * });
486
715
  * ```
487
716
  */
488
- declare function useEntityList<TRaw, TEntity extends Record<string, unknown>>(opts: ListQueryOptions<TRaw, TEntity>): UseEntityListResult<TEntity>;
717
+ declare function useEntityView<TEntity extends Record<string, unknown>>(opts: UseEntityViewOptions<TEntity>): UseEntityViewResult<TEntity>;
718
+
719
+ /** UI mode for a single CRUD surface: drives which panels/forms are active without scattering boolean flags. */
720
+ type CRUDMode = "list" | "detail" | "edit" | "create";
489
721
  /**
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.
722
+ * Wire one entity type into list+detail+forms: remote list via `useEntityView`, optional detail fetch, and create/update/delete callbacks.
723
+ * Mutations call `cascadeInvalidation` on success so related lists/entities refresh per registered schemas.
492
724
  */
493
- interface MutationOptions<TInput, TRaw, TEntity extends Record<string, unknown>> {
725
+ interface CRUDOptions<TEntity extends Record<string, unknown>> {
494
726
  type: EntityType;
495
- mutate: (input: TInput) => Promise<TRaw>;
496
- normalize?: (raw: TRaw, input: TInput) => {
727
+ listQueryKey: unknown[];
728
+ listFetch: (params: ViewFetchParams) => Promise<ListResponse<TEntity>>;
729
+ normalize: (raw: TEntity) => {
497
730
  id: EntityId;
498
731
  data: TEntity;
499
732
  };
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;
733
+ detailFetch?: (id: EntityId) => Promise<TEntity>;
734
+ onCreate?: (data: Partial<TEntity>) => Promise<TEntity>;
735
+ onUpdate?: (id: EntityId, patch: Partial<TEntity>) => Promise<TEntity>;
736
+ onDelete?: (id: EntityId) => Promise<void>;
737
+ createDefaults?: Partial<TEntity>;
738
+ initialView?: ViewDescriptor;
739
+ onCreateSuccess?: (entity: TEntity) => void;
740
+ onUpdateSuccess?: (entity: TEntity) => void;
741
+ onDeleteSuccess?: (id: EntityId) => void;
742
+ onError?: (op: "create" | "update" | "delete", error: Error) => void;
743
+ selectAfterCreate?: boolean;
744
+ clearSelectionAfterDelete?: boolean;
511
745
  }
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
- };
746
+ /** Public field input for CRUD setters: supports classic `keyof T` calls and dotted nested paths for JSON-backed forms. */
747
+ type EntityFieldPath<TEntity extends Record<string, unknown>> = keyof TEntity | string;
748
+ /** Tracks which fields diverge from loaded detail — edit buffer stays in React state so other views keep showing canonical graph data until save. */
749
+ interface DirtyFields<TEntity extends Record<string, unknown>> {
750
+ changed: ReadonlySet<EntityFieldPath<TEntity>>;
751
+ isDirty: boolean;
523
752
  }
524
753
  /**
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
754
+ * Everything a CRUD screen needs: composed `list` view, selection, detail subscription, relation joins, edit/create buffers, and mutating actions.
755
+ * `applyOptimistic` is the escape hatch to mirror the buffer into `patches` for instant sliders/toggles without committing `save` yet.
576
756
  */
577
- declare function useSuspenseEntityList<TRaw, TEntity extends Record<string, unknown>>(opts: ListQueryOptions<TRaw, TEntity>): Omit<UseEntityListResult<TEntity>, "isLoading">;
578
-
757
+ interface CRUDState<TEntity extends Record<string, unknown>> {
758
+ mode: CRUDMode;
759
+ setMode: (mode: CRUDMode) => void;
760
+ list: UseEntityViewResult<TEntity>;
761
+ selectedId: EntityId | null;
762
+ select: (id: EntityId | null) => void;
763
+ openDetail: (id: EntityId) => void;
764
+ detail: TEntity | null;
765
+ detailIsLoading: boolean;
766
+ detailError: string | null;
767
+ relations: Record<string, unknown>;
768
+ editBuffer: Partial<TEntity>;
769
+ setField: (field: EntityFieldPath<TEntity>, value: unknown) => void;
770
+ setFields: (fields: Partial<TEntity>) => void;
771
+ resetBuffer: () => void;
772
+ dirty: DirtyFields<TEntity>;
773
+ startEdit: (id?: EntityId) => void;
774
+ cancelEdit: () => void;
775
+ save: () => Promise<TEntity | null>;
776
+ isSaving: boolean;
777
+ saveError: string | null;
778
+ applyOptimistic: () => void;
779
+ createBuffer: Partial<TEntity>;
780
+ setCreateField: (field: EntityFieldPath<TEntity>, value: unknown) => void;
781
+ setCreateFields: (fields: Partial<TEntity>) => void;
782
+ resetCreateBuffer: () => void;
783
+ startCreate: () => void;
784
+ cancelCreate: () => void;
785
+ create: () => Promise<TEntity | null>;
786
+ isCreating: boolean;
787
+ createError: string | null;
788
+ deleteEntity: (id?: EntityId) => Promise<void>;
789
+ isDeleting: boolean;
790
+ deleteError: string | null;
791
+ isEditing: boolean;
792
+ }
579
793
  /**
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.
794
+ * 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.
795
+ * Prefer this over ad-hoc `useEntity` wiring when building admin-style tables + side panels + forms for one resource.
585
796
  *
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.
797
+ * @param opts - `CRUDOptions` for type, list key/fetch, normalization, lifecycle callbacks
798
+ * @returns `CRUDState` with list/detail/edit/create controls
592
799
  */
593
- declare function toPrismaOrderBy(sort: SortSpec): Record<string, string>[];
800
+ declare function useEntityCRUD<TEntity extends Record<string, unknown>>(opts: CRUDOptions<TEntity>): CRUDState<TEntity>;
594
801
 
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 {
802
+ type FieldType = "text" | "textarea" | "number" | "email" | "url" | "date" | "boolean" | "enum" | "json" | "markdown" | "custom";
803
+ interface FieldDescriptor<TEntity> {
602
804
  field: string;
603
- op: FilterOperator;
604
- value?: unknown;
605
- predicate?: (fieldValue: unknown, entity: Record<string, unknown>) => boolean;
805
+ label: string;
806
+ type: FieldType;
807
+ required?: boolean;
808
+ placeholder?: string;
809
+ options?: Array<{
810
+ value: string;
811
+ label: string;
812
+ }>;
813
+ hint?: string;
814
+ render?: (value: unknown, entity: TEntity) => React$1.ReactNode;
815
+ editControl?: (value: unknown, onChange: (v: unknown) => void, entity: Partial<TEntity>) => React$1.ReactNode;
816
+ hideOnCreate?: boolean;
817
+ hideOnEdit?: boolean;
818
+ readonlyOnEdit?: boolean;
606
819
  }
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>;
820
+ declare function Sheet({ open, onClose, title, subtitle, children, footer, width }: {
821
+ open: boolean;
822
+ onClose: () => void;
823
+ title: string;
824
+ subtitle?: string;
825
+ children: React$1.ReactNode;
826
+ footer?: React$1.ReactNode;
827
+ width?: string;
828
+ }): react_jsx_runtime.JSX.Element;
829
+ declare function EntityDetailSheet<TEntity extends Record<string, unknown>>({ crud, fields, title, description, children, showEditButton, showDeleteButton, deleteConfirmMessage }: {
830
+ crud: CRUDState<TEntity>;
831
+ fields: FieldDescriptor<TEntity>[];
832
+ title?: string | ((e: TEntity) => string);
833
+ description?: string | ((e: TEntity) => string);
834
+ children?: (entity: TEntity, crud: CRUDState<TEntity>) => React$1.ReactNode;
835
+ showEditButton?: boolean;
836
+ showDeleteButton?: boolean;
837
+ deleteConfirmMessage?: string;
838
+ }): react_jsx_runtime.JSX.Element;
839
+ declare function EntityFormSheet<TEntity extends Record<string, unknown>>({ crud, fields, createTitle, editTitle }: {
840
+ crud: CRUDState<TEntity>;
841
+ fields: FieldDescriptor<TEntity>[];
842
+ createTitle?: string;
843
+ editTitle?: string | ((e: TEntity) => string);
844
+ }): react_jsx_runtime.JSX.Element;
845
+
846
+ interface JsonSchemaObject {
847
+ $id?: string;
848
+ title?: string;
849
+ description?: string;
850
+ type?: string | string[];
851
+ format?: string;
852
+ enum?: unknown[];
853
+ default?: unknown;
854
+ properties?: Record<string, JsonSchemaObject>;
855
+ items?: JsonSchemaObject;
856
+ required?: string[];
857
+ ["x-a2ui-component"]?: string;
858
+ ["x-display-order"]?: number;
859
+ ["x-field-type"]?: string;
860
+ ["x-hidden"]?: boolean;
861
+ }
862
+ interface EntityJsonSchemaConfig {
863
+ entityType: string;
864
+ schemaId?: string;
865
+ field?: string;
866
+ version?: string;
867
+ source?: "static" | "runtime" | "ai";
868
+ schema: JsonSchemaObject;
869
+ }
870
+ interface GetEntityJsonSchemaOptions {
871
+ entityType: string;
872
+ schemaId?: string;
873
+ field?: string;
874
+ }
875
+ interface SchemaFieldDescriptor<TEntity extends Record<string, unknown> = Record<string, unknown>> extends FieldDescriptor<TEntity> {
876
+ schemaPath: string;
877
+ schema: JsonSchemaObject;
878
+ componentHint?: string;
879
+ }
880
+ interface BuildEntityFieldsFromSchemaOptions {
881
+ schema: JsonSchemaObject;
882
+ rootField?: string;
883
+ }
884
+ interface GraphSnapshotWithSchemasOptions {
885
+ scope: string;
886
+ data: unknown;
887
+ schemas: Array<EntityJsonSchemaConfig | null | undefined>;
888
+ pretty?: boolean;
889
+ }
890
+ declare function registerEntityJsonSchema(config: EntityJsonSchemaConfig): void;
891
+ declare function registerRuntimeSchema(config: EntityJsonSchemaConfig): void;
892
+ declare function getEntityJsonSchema(opts: GetEntityJsonSchemaOptions): EntityJsonSchemaConfig | null;
893
+ declare function useSchemaEntityFields<TEntity extends Record<string, unknown> = Record<string, unknown>>(opts: GetEntityJsonSchemaOptions & {
894
+ schema?: JsonSchemaObject;
895
+ rootField?: string;
896
+ }): SchemaFieldDescriptor<TEntity>[];
897
+ declare function buildEntityFieldsFromSchema<TEntity extends Record<string, unknown> = Record<string, unknown>>(opts: BuildEntityFieldsFromSchemaOptions): SchemaFieldDescriptor<TEntity>[];
898
+ declare function exportGraphSnapshotWithSchemas(opts: GraphSnapshotWithSchemasOptions): string;
899
+ declare function renderMarkdownToHtml(value: string): string;
900
+ declare function MarkdownFieldRenderer({ value, className }: {
901
+ value: string;
902
+ className?: string;
903
+ }): react_jsx_runtime.JSX.Element;
904
+ declare function MarkdownFieldEditor({ value, onChange, placeholder, }: {
905
+ value: string;
906
+ onChange: (value: string) => void;
907
+ placeholder?: string;
908
+ }): react_jsx_runtime.JSX.Element;
909
+
910
+ interface GraphSnapshotExportOptions {
911
+ scope: string;
912
+ data: unknown;
913
+ pretty?: boolean;
612
914
  }
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;
915
+ interface GraphToolContext {
916
+ store: ReturnType<typeof useGraphStore.getState>;
917
+ queryOnce: typeof queryOnce;
918
+ exportGraphSnapshot: typeof exportGraphSnapshot;
622
919
  }
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;
920
+ interface SchemaGraphToolContext extends GraphToolContext {
921
+ getEntityJsonSchema: typeof getEntityJsonSchema;
922
+ exportGraphSnapshotWithSchemas: typeof exportGraphSnapshotWithSchemas;
923
+ }
924
+ declare function exportGraphSnapshot(opts: GraphSnapshotExportOptions): string;
925
+ declare function createGraphTool<TInput, TResult>(handler: (input: TInput, ctx: GraphToolContext) => Promise<TResult> | TResult): (input: TInput) => TResult | Promise<TResult>;
926
+ declare function createSchemaGraphTool<TInput, TResult>(handler: (input: TInput, ctx: SchemaGraphToolContext) => Promise<TResult> | TResult): (input: TInput) => TResult | Promise<TResult>;
927
+
928
+ interface GraphPersistenceAdapter {
929
+ get: (key: string) => Promise<string | null> | string | null;
930
+ set: (key: string, value: string) => Promise<void> | void;
931
+ remove?: (key: string) => Promise<void> | void;
932
+ }
933
+ interface GraphActionRecord {
934
+ id: string;
935
+ key: string;
936
+ input: unknown;
937
+ enqueuedAt: string;
938
+ }
939
+ interface GraphSyncStatus {
940
+ phase: "idle" | "hydrating" | "syncing" | "ready" | "offline" | "error";
941
+ isOnline: boolean;
942
+ isSynced: boolean;
943
+ pendingActions: number;
944
+ lastHydratedAt: string | null;
945
+ lastPersistedAt: string | null;
946
+ storageKey: string | null;
947
+ error: string | null;
948
+ }
949
+ interface GraphSnapshotPayload {
950
+ version: 1;
951
+ snapshot: {
952
+ entities: ReturnType<typeof useGraphStore.getState>["entities"];
953
+ patches: ReturnType<typeof useGraphStore.getState>["patches"];
954
+ entityStates: ReturnType<typeof useGraphStore.getState>["entityStates"];
955
+ syncMetadata: ReturnType<typeof useGraphStore.getState>["syncMetadata"];
956
+ lists: ReturnType<typeof useGraphStore.getState>["lists"];
636
957
  };
958
+ pendingActions: GraphActionRecord[];
637
959
  }
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
- };
960
+ interface PersistGraphToStorageOptions {
961
+ storage: GraphPersistenceAdapter;
962
+ key: string;
963
+ pendingActions?: GraphActionRecord[];
964
+ }
965
+ interface HydrateGraphFromStorageOptions {
966
+ storage: GraphPersistenceAdapter;
967
+ key: string;
968
+ }
969
+ interface StartLocalFirstGraphOptions {
970
+ storage: GraphPersistenceAdapter;
971
+ key?: string;
972
+ replayPendingActions?: boolean;
973
+ onlineSource?: {
974
+ getIsOnline: () => boolean;
975
+ subscribe: (listener: (online: boolean) => void) => () => void;
976
+ };
977
+ persistDebounceMs?: number;
978
+ }
979
+ interface LocalFirstGraphRuntime {
980
+ ready: Promise<void>;
981
+ dispose: () => void;
982
+ persistNow: () => Promise<void>;
983
+ hydrate: () => Promise<Awaited<ReturnType<typeof hydrateGraphFromStorage>>>;
984
+ getStatus: () => GraphSyncStatus;
985
+ }
986
+ declare function useGraphSyncStatus(): GraphSyncStatus;
987
+ declare function persistGraphToStorage(opts: PersistGraphToStorageOptions): Promise<{
988
+ ok: true;
989
+ key: string;
990
+ bytes: number;
991
+ persistedAt: string;
992
+ }>;
993
+ declare function hydrateGraphFromStorage(opts: HydrateGraphFromStorageOptions): Promise<{
994
+ ok: false;
995
+ key: string;
996
+ hydratedAt: null;
997
+ entityCounts: {};
998
+ error: string;
999
+ pendingActions?: undefined;
1000
+ } | {
1001
+ ok: true;
1002
+ key: string;
1003
+ hydratedAt: string;
1004
+ entityCounts: {
1005
+ [k: string]: number;
1006
+ };
1007
+ pendingActions: GraphActionRecord[];
1008
+ error?: undefined;
1009
+ }>;
1010
+ declare function startLocalFirstGraph(opts: StartLocalFirstGraphOptions): LocalFirstGraphRuntime;
662
1011
 
663
1012
  /**
664
- * Normalize nested `FilterGroup` trees to a flat clause list for compilers that only understand atomic predicates.
1013
+ * Debug-time snapshot of entity graph health: counts, list queries, patches, staleness,
1014
+ * in-flight fetches, and engine subscriber ref-counts.
1015
+ *
1016
+ * Mount inside a DevTools panel or debug route; subscriber totals update via
1017
+ * `useSyncExternalStore` when hooks register/unregister interest, and graph fields
1018
+ * update through the Zustand store.
665
1019
  */
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;
1020
+ declare function useGraphDevTools(): {
1021
+ subscriberCount: number;
1022
+ entityCounts: Record<string, number>;
1023
+ totalEntities: number;
1024
+ listCount: number;
1025
+ patchedEntities: {
1026
+ type: string;
1027
+ id: string;
1028
+ }[];
1029
+ staleEntities: {
1030
+ type: string;
1031
+ id: string;
1032
+ }[];
1033
+ fetchingEntities: {
1034
+ type: string;
1035
+ id: string;
1036
+ }[];
1037
+ lists: {
1038
+ key: string;
1039
+ idCount: number;
1040
+ isFetching: boolean;
1041
+ isStale: boolean;
1042
+ }[];
1043
+ };
669
1044
 
670
1045
  /**
671
- * Precompiled transport payloads for one view snapshot pass to REST, GraphQL, or SQL backends without re-deriving from `ViewDescriptor`.
1046
+ * View-model for one entity row: merged canonical + patch data plus fetch lifecycle flags.
1047
+ * `isLoading` is true only when there is no data yet; `isFetching` includes background refreshes.
672
1048
  */
673
- interface ViewFetchParams {
674
- rest: Record<string, string>;
675
- graphql: ReturnType<typeof toGraphQLVariables>;
676
- sql: ReturnType<typeof toSQLClauses>;
677
- view: ViewDescriptor;
1049
+ interface UseEntityResult<T> {
1050
+ data: T | null;
1051
+ isLoading: boolean;
1052
+ isFetching: boolean;
1053
+ error: string | null;
1054
+ isStale: boolean;
1055
+ refetch: () => void;
678
1056
  }
679
1057
  /**
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
- }
1058
+ * Subscribe to a single normalized entity: populates the graph via `fetch`/`normalize`, dedupes in-flight work, and revalidates when stale.
1059
+ * Solves “query-owned silos” by keeping **one** canonical record every list/detail reads through.
1060
+ *
1061
+ * @param opts - Entity query instruction (`EntityQueryOptions`): `type`, `id`, `fetch`, `normalize`, optional `staleTime` / `enabled`
1062
+ * @returns Merged entity (`entities` + `patches`), loading/error/stale flags, and `refetch`
1063
+ *
1064
+ * @example
1065
+ * ```tsx
1066
+ * const { data, isLoading, refetch } = useEntity({
1067
+ * type: "Project",
1068
+ * id: projectId,
1069
+ * fetch: (id) => api.getProject(id),
1070
+ * normalize: (raw) => ({ ...raw, id: String(raw.id) }),
1071
+ * });
1072
+ * ```
1073
+ */
1074
+ declare function useEntity<TRaw, TEntity extends Record<string, unknown>>(opts: EntityQueryOptions<TRaw, TEntity>): UseEntityResult<TEntity>;
701
1075
  /**
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.
1076
+ * Resolved rows for a list query: **`items` joins `ids` to the graph** at render time so shared entities update everywhere.
704
1077
  */
705
- interface UseEntityViewResult<TEntity> {
1078
+ interface UseEntityListResult<TEntity> {
706
1079
  items: TEntity[];
707
- viewIds: EntityId[];
708
- viewTotal: number | null;
1080
+ ids: EntityId[];
709
1081
  isLoading: boolean;
710
1082
  isFetching: boolean;
711
- isRemoteFetching: boolean;
712
- isShowingLocalPending: boolean;
1083
+ isFetchingMore: boolean;
713
1084
  error: string | null;
714
1085
  hasNextPage: boolean;
1086
+ hasPrevPage: boolean;
1087
+ total: number | null;
1088
+ currentPage: number | null;
715
1089
  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
1090
  refetch: () => void;
724
- isFetchingMore: boolean;
725
1091
  }
726
1092
  /**
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.
1093
+ * Subscribe to a collection: stores **ordered ids** under a serialized `queryKey`, upserts each row into `entities`, and supports pagination.
1094
+ * Use when you need a table or feed backed by the shared graph (not an isolated query cache).
729
1095
  *
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
1096
+ * @param opts - List query instruction (`ListQueryOptions`)
1097
+ * @returns Hydrated `items`, raw `ids`, pagination helpers, and fetch flags
732
1098
  *
733
1099
  * @example
734
1100
  * ```tsx
735
- * const view = useEntityView({
1101
+ * const { items, fetchNextPage, hasNextPage } = useEntityList({
736
1102
  * 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),
1103
+ * queryKey: ["tasks", { projectId }],
1104
+ * fetch: (p) => api.listTasks({ ...p, projectId }),
740
1105
  * normalize: (raw) => ({ id: raw.id, data: raw }),
1106
+ * mode: "append",
741
1107
  * });
742
1108
  * ```
743
1109
  */
744
- declare function useEntityView<TEntity extends Record<string, unknown>>(opts: UseEntityViewOptions<TEntity>): UseEntityViewResult<TEntity>;
1110
+ declare function useEntityList<TRaw, TEntity extends Record<string, unknown>>(opts: ListQueryOptions<TRaw, TEntity>): UseEntityListResult<TEntity>;
1111
+ /**
1112
+ * Options for graph-aware mutations: API call + optional normalization into `entities`, optimistic patches, and targeted invalidation.
1113
+ * Prefer `invalidateLists` prefixes / `invalidateEntities` over ad-hoc store calls so list UIs stay coherent.
1114
+ */
1115
+ interface MutationOptions<TInput, TRaw, TEntity extends Record<string, unknown>> {
1116
+ type: EntityType;
1117
+ mutate: (input: TInput) => Promise<TRaw>;
1118
+ normalize?: (raw: TRaw, input: TInput) => {
1119
+ id: EntityId;
1120
+ data: TEntity;
1121
+ };
1122
+ optimistic?: (input: TInput) => {
1123
+ id: EntityId;
1124
+ patch: Partial<TEntity>;
1125
+ } | null;
1126
+ invalidateLists?: string[];
1127
+ invalidateEntities?: Array<{
1128
+ type: EntityType;
1129
+ id: EntityId;
1130
+ }>;
1131
+ onSuccess?: (result: TRaw, input: TInput) => void;
1132
+ onError?: (error: Error, input: TInput) => void;
1133
+ }
1134
+ /** Imperative mutation handle plus explicit async `state` for UI pending/error/success. */
1135
+ interface UseMutationResult<TInput, TRaw> {
1136
+ mutate: (input: TInput) => Promise<TRaw | null>;
1137
+ trigger: (input: TInput) => void;
1138
+ reset: () => void;
1139
+ state: {
1140
+ isPending: boolean;
1141
+ isSuccess: boolean;
1142
+ isError: boolean;
1143
+ error: string | null;
1144
+ };
1145
+ }
1146
+ /**
1147
+ * Perform writes through your API while keeping the entity graph authoritative: optional optimistic `patchEntity`, commit via `upsertEntity`, rollback on failure.
1148
+ * Use when `useEntity`/`useEntityList` describe reads and you need a consistent mutation story without a second client cache.
1149
+ *
1150
+ * @example
1151
+ * ```tsx
1152
+ * const { mutate, state } = useEntityMutation({
1153
+ * type: "Task",
1154
+ * mutate: (input) => api.updateTask(input.id, input.patch),
1155
+ * normalize: (raw) => ({ id: raw.id, data: raw }),
1156
+ * });
1157
+ * ```
1158
+ */
1159
+ declare function useEntityMutation<TInput, TRaw, TEntity extends Record<string, unknown>>(opts: MutationOptions<TInput, TRaw, TEntity>): UseMutationResult<TInput, TRaw>;
1160
+ /**
1161
+ * 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.
1162
+ * Does not replace `useEntityCRUD`’s edit buffer for form drafts — patches are for shared, non-persisted overlays.
1163
+ *
1164
+ * @param type - Entity kind
1165
+ * @param id - Entity id (no-ops when null/undefined)
1166
+ * @returns Current patch slice and helpers `augment` / `unaugment` / `clear`
1167
+ */
1168
+ declare function useEntityAugment<TEntity extends Record<string, unknown>>(type: EntityType, id: EntityId | null | undefined): {
1169
+ patch: Partial<TEntity> | null;
1170
+ augment: (fields: Partial<TEntity>) => void;
1171
+ unaugment: (keys: (keyof TEntity)[]) => void;
1172
+ clear: () => void;
1173
+ };
1174
+ /**
1175
+ * Suspense-compatible version of `useEntity`. Throws a promise while the entity
1176
+ * is loading, allowing React Suspense boundaries to show fallback UI.
1177
+ * Once data is available, returns the entity data directly (never null).
1178
+ *
1179
+ * @param opts - Same as `useEntity` (`EntityQueryOptions`)
1180
+ * @returns `data` plus `isFetching`, `isStale`, and `refetch`
1181
+ * @throws Promise while loading (caught by the nearest Suspense boundary)
1182
+ * @throws Error if the fetch fails with no data, if `id` is missing when required, or if the entity never resolves
1183
+ */
1184
+ declare function useSuspenseEntity<TRaw, TEntity extends Record<string, unknown>>(opts: EntityQueryOptions<TRaw, TEntity>): {
1185
+ data: TEntity;
1186
+ isFetching: boolean;
1187
+ isStale: boolean;
1188
+ refetch: () => void;
1189
+ };
1190
+ /**
1191
+ * Suspense-compatible version of `useEntityList`. Throws a promise during
1192
+ * initial load, allowing Suspense boundaries to handle loading state.
1193
+ *
1194
+ * @param opts - Same as `useEntityList` (`ListQueryOptions`)
1195
+ * @returns Same shape as `useEntityList` except `isLoading` is omitted (always false when not suspended)
1196
+ * @throws Promise while initially loading (caught by the nearest Suspense boundary)
1197
+ * @throws Error if the fetch fails while the list is still empty
1198
+ */
1199
+ declare function useSuspenseEntityList<TRaw, TEntity extends Record<string, unknown>>(opts: ListQueryOptions<TRaw, TEntity>): Omit<UseEntityListResult<TEntity>, "isLoading">;
745
1200
 
746
1201
  /**
747
1202
  * Evaluate `FilterSpec` against one in-memory entity — mirrors remote semantics as closely as plain JS allows.
@@ -775,87 +1230,6 @@ declare function checkCompleteness(loadedCount: number, total: number | null, ha
775
1230
  reason: string;
776
1231
  };
777
1232
 
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
1233
  /**
860
1234
  * FK edge: this entity points at one parent row. Used to invalidate parent aggregates and optional list keys when the FK changes.
861
1235
  */
@@ -1367,50 +1741,6 @@ interface EntityTableProps<T extends Record<string, unknown>> {
1367
1741
  }
1368
1742
  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
1743
 
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
1744
  type ColumnFilterType = "text" | "number" | "date" | "dateRange" | "boolean" | "enum" | "relation" | "none";
1415
1745
  interface EntityColumnMeta<TEntity> {
1416
1746
  field: keyof TEntity;
@@ -2605,4 +2935,4 @@ declare const TableHead: React$1.ForwardRefExoticComponent<React$1.ThHTMLAttribu
2605
2935
  declare const TableCell: React$1.ForwardRefExoticComponent<React$1.TdHTMLAttributes<HTMLTableCellElement> & React$1.RefAttributes<HTMLTableCellElement>>;
2606
2936
  declare const TableCaption: React$1.ForwardRefExoticComponent<React$1.HTMLAttributes<HTMLTableCaptionElement> & React$1.RefAttributes<HTMLTableCaptionElement>>;
2607
2937
 
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 };
2938
+ export { type AccessorFn, ActionButtonRow, type ActionDef, ActionDropdown, type ActionItem, type ActivePresets, type AdapterStatus, type AggregationFn, type BatchActionDef, type BelongsToRelation, type BuildEntityFieldsFromSchemaOptions, 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, type EntityJsonSchemaConfig, EntityListView, type EntityListViewProps, type EntityQueryOptions, type EntitySchema, type EntitySnapshot, type EntityState, type EntitySyncMetadata, 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 GraphActionEvent, type GraphActionOptions, type GraphActionRecord$1 as GraphActionRecord, type GraphEffectEvent, type GraphEffectHandle, type GraphEffectOptions, type GraphIncludeMap, type GraphIncludeRelation, type GraphPersistenceAdapter, type GraphQueryOptions, type GraphSnapshotExportOptions, type GraphSnapshotPayload, type GraphSnapshotWithSchemasOptions, type GraphState, type GraphSyncStatus, type GraphToolContext, type GraphTransaction, 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 JsonSchemaObject, type ListFetchParams, type ListQueryOptions, type ListResponse, type ListState, ListView, type LocalFirstGraphRuntime, type ManagerOptions, type ManyToManyRelation, MarkdownFieldEditor, MarkdownFieldRenderer, MemoryAdapter, MultiSelectBar, type PaginationState, type GraphActionRecord as PersistedGraphActionRecord, 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 QueryKey, type RealtimeAdapter, RealtimeManager, type RelationDescriptor, type RestAdapterOptions, RestApiAdapter, type Row, type RowModel, type RowSelectionState, type SchemaFieldDescriptor, type SchemaGraphToolContext, SelectionContext, type SelectionStoreState, Sheet, type SortClause, type SortDirection, SortHeader, type SortSpec, type SortingFn, type SortingState, type StartLocalFirstGraphOptions, type SubscriptionConfig, type SupabaseAdapterOptions$1 as SupabaseAdapterOptions, SupabaseRealtimeAdapter as SupabasePresetAdapter, type SupabaseAdapterOptions as SupabasePresetAdapterOptions, type SyncAdapter, type SyncOrigin, 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, buildEntityFieldsFromSchema, cascadeInvalidation, checkCompleteness, compareEntities, configureEngine, createConvexAdapter, createElectricAdapter, createGQLClient, createGraphAction, createGraphEffect, createGraphQLSubscriptionAdapter, createGraphTool, createGraphTransaction, createPresetStore, createPrismaEntityConfig, createRow, createSchemaGraphTool, createSelectionStore, createSupabaseRealtimeAdapter, createWebSocketAdapter, dateColumn$1 as dateColumn, dedupe, deleteAction, editAction, enumColumn$1 as enumColumn, executeGQL, exportGraphSnapshot, exportGraphSnapshotWithSchemas, fetchEntity, fetchList, flattenClauses, getCoreRowModel, getEntityJsonSchema, getExpandedRowModel, getFacetedMinMaxValues, getFacetedRowModel, getFacetedUniqueValues, getFilteredRowModel, getGroupedRowModel, getPaginatedRowModel, getRealtimeManager, getSchema, getSelectedRowModel, getSortedRowModel, hasCustomPredicates, hydrateGraphFromStorage, matchesFilter, matchesSearch, normalizeGQLResponse, numberColumn$1 as numberColumn, persistGraphToStorage, prismaRelationsToSchema, actionsColumn as pureActionsColumn, booleanColumn as pureBooleanColumn, dateColumn as pureDateColumn, enumColumn as pureEnumColumn, numberColumn as pureNumberColumn, selectionColumn as pureSelectionColumn, textColumn as pureTextColumn, queryOnce, readRelations, registerEntityJsonSchema, registerRuntimeSchema, registerSchema, renderMarkdownToHtml, resetRealtimeManager, selectGraph, selectionColumn$1 as selectionColumn, serializeKey, startGarbageCollector, startLocalFirstGraph, stopGarbageCollector, textColumn$1 as textColumn, toGraphQLVariables, toPrismaInclude, toPrismaOrderBy, toPrismaWhere, toRestParams, toSQLClauses, useEntity, useEntityAugment, useEntityCRUD, useEntityList, useEntityMutation, useEntityView, useGQLEntity, useGQLList, useGQLMutation, useGQLSubscription, useGraphDevTools, useGraphStore, useGraphSyncStatus, useLocalFirst, usePGliteQuery, useSchemaEntityFields, useSelectionContext, useSelectionStore, useSuspenseEntity, useSuspenseEntityList, useTable, useTablePresets, useTableRealtimeMode, useTableStorageAdapter, viewAction };