@prometheus-ags/entity-graph-core 3.0.0-alpha.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,2913 @@
1
+ import * as zustand from 'zustand';
2
+ import React from 'react';
3
+
4
+ /**
5
+ * Typed errors used throughout 2.0 to drive retry policy and consumer
6
+ * UI decisions.
7
+ *
8
+ * Why two classes (not a discriminator field):
9
+ * - `instanceof` checks are zero-cost at runtime and survive bundler
10
+ * minification of property names.
11
+ * - Consumers can write `if (error instanceof TerminalError) renderInline()`
12
+ * without importing a type-narrowing helper.
13
+ * - The engine's retry policy is one `instanceof TransientError` check.
14
+ *
15
+ * 4xx → `TerminalError` (no retry — the request is structurally wrong:
16
+ * unauthorized, not-found, schema mismatch, validation failure).
17
+ *
18
+ * 5xx + network failures → `TransientError` (engine retries with
19
+ * exponential backoff up to `maxRetries`).
20
+ *
21
+ * Custom transports can throw either class. The default REST helper
22
+ * (`makeRestTransport`) maps HTTP status codes to the appropriate
23
+ * class automatically.
24
+ *
25
+ * Both classes preserve the original error via the standard
26
+ * `cause` option so stack traces survive the boundary.
27
+ */
28
+ interface EntityErrorOptions {
29
+ /** HTTP status if the underlying transport was HTTP. */
30
+ status?: number;
31
+ /** Original thrown value (`Error`, `Response`, anything). */
32
+ cause?: unknown;
33
+ }
34
+ /**
35
+ * Permanent failure — do not retry. The request will not succeed by
36
+ * trying again with the same inputs.
37
+ *
38
+ * Typical sources: HTTP 4xx, missing tables, validation errors,
39
+ * permission denied, schema mismatches.
40
+ */
41
+ declare class TerminalError extends Error {
42
+ readonly kind: "terminal";
43
+ readonly status?: number;
44
+ constructor(message: string, opts?: EntityErrorOptions);
45
+ }
46
+ /**
47
+ * Temporary failure — safe to retry. The engine will retry with
48
+ * exponential backoff up to the configured `maxRetries`.
49
+ *
50
+ * Typical sources: HTTP 5xx, network timeouts, fetch rejections,
51
+ * dropped sockets.
52
+ */
53
+ declare class TransientError extends Error {
54
+ readonly kind: "transient";
55
+ readonly status?: number;
56
+ constructor(message: string, opts?: EntityErrorOptions);
57
+ }
58
+ /**
59
+ * Union of every typed error the library raises. Use on consumer-side
60
+ * `error` props when both classes are possible.
61
+ */
62
+ type EntityError = TerminalError | TransientError;
63
+ /**
64
+ * Convert an unknown thrown value into a typed entity error.
65
+ *
66
+ * - Already-typed errors pass through.
67
+ * - HTTP-status-bearing errors become Terminal (4xx) or Transient (5xx).
68
+ * - `AbortError` / `DOMException` "AbortError" → Terminal (caller
69
+ * cancelled; not worth retrying).
70
+ * - Everything else → Transient (assume retryable; better than
71
+ * accidentally permanently failing on a one-off network blip).
72
+ */
73
+ declare function toEntityError(err: unknown): EntityError;
74
+
75
+ /** Logical entity kind (e.g. `"Post"`). Used to partition the normalized graph. */
76
+ type EntityType = string;
77
+ /** Primary key for an entity within its `type`. Lists and relations reference this, not row copies. */
78
+ type EntityId = string;
79
+ /** Stable string key for a list query (often `JSON.stringify`-shaped). Lists store IDs only under this key. */
80
+ type QueryKey = string;
81
+ /** Provenance of the latest known entity state. */
82
+ type SyncOrigin = "server" | "client" | "optimistic";
83
+ /** Optional sync-facing metadata kept outside canonical entity payloads. */
84
+ interface EntitySyncMetadata {
85
+ synced: boolean;
86
+ origin: SyncOrigin;
87
+ updatedAt: number | null;
88
+ }
89
+ /** Snapshot shape returned by sync-aware reads and graph-native query helpers. */
90
+ type EntitySnapshot<T extends object> = T & {
91
+ $synced: boolean;
92
+ $origin: SyncOrigin;
93
+ $updatedAt: number | null;
94
+ };
95
+ /**
96
+ * Fetch/cache metadata for a single entity instance (`type:id`).
97
+ * Separates transport concerns from canonical `entities` data so hooks can show spinners and stale-while-revalidate without mutating server-shaped fields.
98
+ */
99
+ interface EntityState {
100
+ isFetching: boolean;
101
+ lastFetched: number | null;
102
+ error: string | null;
103
+ stale: boolean;
104
+ }
105
+ /**
106
+ * Ordered **IDs** for a list query plus pagination metadata — never embedded entity payloads.
107
+ * Cross-view reactivity depends on this: when `entities[type][id]` updates, every list containing that id re-resolves rows from the graph.
108
+ */
109
+ interface ListState {
110
+ ids: EntityId[];
111
+ total: number | null;
112
+ nextCursor: string | null;
113
+ prevCursor: string | null;
114
+ hasNextPage: boolean;
115
+ hasPrevPage: boolean;
116
+ isFetching: boolean;
117
+ isFetchingMore: boolean;
118
+ error: string | null;
119
+ /**
120
+ * Typed error instance for 2.0 hooks (`useEntities`, `useEntityQuery`).
121
+ * Carries `TerminalError` / `TransientError` so consumers can
122
+ * `instanceof`-check the failure mode without parsing the string in
123
+ * `error`. Both fields are written together by `setListError`; the
124
+ * string `error` field remains for back-compat with 1.x consumers.
125
+ */
126
+ lastError: EntityError | null;
127
+ lastFetched: number | null;
128
+ stale: boolean;
129
+ currentPage: number | null;
130
+ pageSize: number | null;
131
+ }
132
+ /**
133
+ * Stable fallbacks for missing `lists[key]` / `entityStates[key]` slots.
134
+ * Inline `?? { ... }` defaults allocate new objects every `getSnapshot` call and trigger React 19
135
+ * “getSnapshot should be cached” / maximum update depth errors with `useSyncExternalStore`.
136
+ */
137
+ declare const EMPTY_IDS: EntityId[];
138
+ declare const EMPTY_ENTITY_STATE: EntityState;
139
+ declare const EMPTY_SYNC_METADATA: EntitySyncMetadata;
140
+ declare const EMPTY_LIST_STATE: ListState;
141
+ /**
142
+ * Canonical Zustand store: **entities** (server truth), **patches** (UI-only overlay), **lists** (id order + list meta), and **entityStates** (per-entity fetch state).
143
+ * Prefer hooks for React reads; use `useGraphStore.getState()` inside stores/adapters/engine code where React is not available.
144
+ */
145
+ interface GraphState {
146
+ /** Normalized server-confirmed records. Mutate only via upsert/replace/remove — not from components. */
147
+ entities: Record<EntityType, Record<EntityId, Record<string, unknown>>>;
148
+ /** Local-only fields merged at read time (`readEntity` / hooks). Never send patches to the server. */
149
+ patches: Record<EntityType, Record<EntityId, Record<string, unknown>>>;
150
+ /** Per-entity fetch lifecycle keyed as `${type}:${id}`. */
151
+ entityStates: Record<string, EntityState>;
152
+ /** Optional sync/provenance state layered beside the canonical entity payload. */
153
+ syncMetadata: Record<string, EntitySyncMetadata>;
154
+ /** List slots keyed by serialized query keys. Values hold id arrays and pagination — not entity clones. */
155
+ lists: Record<QueryKey, ListState>;
156
+ /**
157
+ * Shallow-merge `data` into the canonical entity. Use when the API returns partial updates or normalized fragments.
158
+ * @param type - Entity kind
159
+ * @param id - Entity id
160
+ * @param data - Fields to merge into existing canonical data
161
+ */
162
+ upsertEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => void;
163
+ /**
164
+ * Batch upsert for list endpoints — avoids N separate writes when hydrating many rows at once.
165
+ * @param type - Entity kind
166
+ * @param entries - Pairs of id + partial/full payloads to merge
167
+ */
168
+ upsertEntities: (type: EntityType, entries: Array<{
169
+ id: EntityId;
170
+ data: Record<string, unknown>;
171
+ }>) => void;
172
+ /**
173
+ * Replace the canonical entity entirely (no merge). Use when the server returns a full snapshot and partial merge would leave stale keys behind.
174
+ */
175
+ replaceEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => void;
176
+ /** Remove canonical entity, its patches, and its entityState. Does not remove the id from lists — use `removeIdFromAllLists` if needed. */
177
+ removeEntity: (type: EntityType, id: EntityId) => void;
178
+ /**
179
+ * Merge UI-only fields into `patches` so every subscriber sees selection, expansion, optimistic toggles, etc., without forking canonical data.
180
+ */
181
+ patchEntity: (type: EntityType, id: EntityId, patch: Record<string, unknown>) => void;
182
+ /** Remove specific patch keys; other patch keys remain. */
183
+ unpatchEntity: (type: EntityType, id: EntityId, keys: string[]) => void;
184
+ /** Drop all patches for an entity (e.g. after successful mutation when server data is authoritative). */
185
+ clearPatch: (type: EntityType, id: EntityId) => void;
186
+ /** Reflect in-flight GET for a single entity (deduped fetches in the engine still flip this once per logical request). */
187
+ setEntityFetching: (type: EntityType, id: EntityId, fetching: boolean) => void;
188
+ /** Persist terminal fetch failure message and clear fetching — hooks surface `error` while leaving prior data if any. */
189
+ setEntityError: (type: EntityType, id: EntityId, error: string | null) => void;
190
+ /** Mark entity successfully loaded; clears error, fetching, stale, and updates `lastFetched`. */
191
+ setEntityFetched: (type: EntityType, id: EntityId) => void;
192
+ /** Drive background revalidation: when true, hooks refetch while still showing cached `readEntity` data. */
193
+ setEntityStale: (type: EntityType, id: EntityId, stale: boolean) => void;
194
+ /** Merge sync metadata for one entity without polluting canonical server fields. */
195
+ setEntitySyncMetadata: (type: EntityType, id: EntityId, metadata: Partial<EntitySyncMetadata>) => void;
196
+ /** Clear sync metadata for one entity. */
197
+ clearEntitySyncMetadata: (type: EntityType, id: EntityId) => void;
198
+ /**
199
+ * Replace list ids and merge pagination meta after a primary list fetch (not load-more).
200
+ * Resets fetching flags and clears list error on success path (engine calls this after normalize).
201
+ */
202
+ setListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
203
+ /** Append unique ids (e.g. infinite scroll) while merging meta; dedupes id order. */
204
+ appendListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
205
+ /** Prepend ids (e.g. “new item at top”) with optional meta merge. */
206
+ prependListResult: (key: QueryKey, ids: EntityId[], meta?: Partial<ListState>) => void;
207
+ /** After deletes, keep every list consistent by removing an id everywhere it appears and decrementing `total` when tracked. */
208
+ removeIdFromAllLists: (type: EntityType, id: EntityId) => void;
209
+ /**
210
+ * Insert or move an id in one list. If the id already exists, it is removed first then re-inserted at `position`.
211
+ * @param position - `"start"`, `"end"`, or numeric index
212
+ */
213
+ insertIdInList: (key: QueryKey, id: EntityId, position: "start" | "end" | number) => void;
214
+ /** Initial or full list fetch in progress (not page-2+). */
215
+ setListFetching: (key: QueryKey, fetching: boolean) => void;
216
+ /** Pagination / “load more” in progress for the same list key. */
217
+ setListFetchingMore: (key: QueryKey, fetchingMore: boolean) => void;
218
+ /**
219
+ * Record list fetch failure; clears both fetching flags.
220
+ * The optional `typed` argument is the typed `EntityError` instance
221
+ * (used by 2.0 hooks); when omitted only the string `error` field is
222
+ * populated (back-compat with 1.x callers).
223
+ */
224
+ setListError: (key: QueryKey, error: string | null, typed?: EntityError | null) => void;
225
+ /** Mark a list query stale so hooks can refetch without discarding current ids (stale-while-revalidate). */
226
+ setListStale: (key: QueryKey, stale: boolean) => void;
227
+ /**
228
+ * Mark one or all entities of a type stale for subscriber-driven refetch. Does not delete data.
229
+ * @param id - Omit to stale every entity of `type`
230
+ */
231
+ invalidateEntity: (type: EntityType, id?: EntityId) => void;
232
+ /**
233
+ * Mark lists stale by key prefix or custom predicate so the next access triggers background refresh without dropping ids immediately.
234
+ */
235
+ invalidateLists: (matcher: string | ((key: QueryKey) => boolean)) => void;
236
+ /** Convenience: stale all entities of a type and lists whose keys start with `type`. */
237
+ invalidateType: (type: EntityType) => void;
238
+ /**
239
+ * Single read path: `{ ...entities[type][id], ...patches[type][id] }` or null if no canonical row exists.
240
+ * @returns Merged view suitable for rendering; not a deep clone
241
+ */
242
+ readEntity: <T = Record<string, unknown>>(type: EntityType, id: EntityId) => T | null;
243
+ /**
244
+ * Sync-aware read path: canonical entity + patches + virtual sync metadata (`$synced`, `$origin`, `$updatedAt`).
245
+ */
246
+ readEntitySnapshot: <T = Record<string, unknown>>(type: EntityType, id: EntityId) => EntitySnapshot<T & Record<string, unknown>> | null;
247
+ }
248
+ /**
249
+ * Global entity graph store (Zustand + Immer). **Components should not subscribe directly** — use hooks so layering stays `Component → hook → store`.
250
+ * `getState()` is intended for non-React code paths (engine, adapters, mutations) that must write or read the graph synchronously.
251
+ */
252
+ declare const useGraphStore: zustand.UseBoundStore<Omit<Omit<zustand.StoreApi<GraphState>, "subscribe"> & {
253
+ subscribe: {
254
+ (listener: (selectedState: GraphState, previousSelectedState: GraphState) => void): () => void;
255
+ <U>(selector: (state: GraphState) => U, listener: (selectedState: U, previousSelectedState: U) => void, options?: {
256
+ equalityFn?: ((a: U, b: U) => boolean) | undefined;
257
+ fireImmediately?: boolean;
258
+ } | undefined): () => void;
259
+ };
260
+ }, "setState"> & {
261
+ setState(nextStateOrUpdater: GraphState | Partial<GraphState> | ((state: {
262
+ entities: {
263
+ [x: string]: {
264
+ [x: string]: {
265
+ [x: string]: unknown;
266
+ };
267
+ };
268
+ };
269
+ patches: {
270
+ [x: string]: {
271
+ [x: string]: {
272
+ [x: string]: unknown;
273
+ };
274
+ };
275
+ };
276
+ entityStates: {
277
+ [x: string]: {
278
+ isFetching: boolean;
279
+ lastFetched: number | null;
280
+ error: string | null;
281
+ stale: boolean;
282
+ };
283
+ };
284
+ syncMetadata: {
285
+ [x: string]: {
286
+ synced: boolean;
287
+ origin: SyncOrigin;
288
+ updatedAt: number | null;
289
+ };
290
+ };
291
+ lists: {
292
+ [x: string]: {
293
+ ids: string[];
294
+ total: number | null;
295
+ nextCursor: string | null;
296
+ prevCursor: string | null;
297
+ hasNextPage: boolean;
298
+ hasPrevPage: boolean;
299
+ isFetching: boolean;
300
+ isFetchingMore: boolean;
301
+ error: string | null;
302
+ lastError: {
303
+ kind: "terminal";
304
+ status?: number | undefined;
305
+ name: string;
306
+ message: string;
307
+ stack?: string | undefined;
308
+ cause?: unknown;
309
+ } | {
310
+ kind: "transient";
311
+ status?: number | undefined;
312
+ name: string;
313
+ message: string;
314
+ stack?: string | undefined;
315
+ cause?: unknown;
316
+ } | null;
317
+ lastFetched: number | null;
318
+ stale: boolean;
319
+ currentPage: number | null;
320
+ pageSize: number | null;
321
+ };
322
+ };
323
+ upsertEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => void;
324
+ upsertEntities: (type: EntityType, entries: Array<{
325
+ id: EntityId;
326
+ data: Record<string, unknown>;
327
+ }>) => void;
328
+ replaceEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => void;
329
+ removeEntity: (type: EntityType, id: EntityId) => void;
330
+ patchEntity: (type: EntityType, id: EntityId, patch: Record<string, unknown>) => void;
331
+ unpatchEntity: (type: EntityType, id: EntityId, keys: string[]) => void;
332
+ clearPatch: (type: EntityType, id: EntityId) => void;
333
+ setEntityFetching: (type: EntityType, id: EntityId, fetching: boolean) => void;
334
+ setEntityError: (type: EntityType, id: EntityId, error: string | null) => void;
335
+ setEntityFetched: (type: EntityType, id: EntityId) => void;
336
+ setEntityStale: (type: EntityType, id: EntityId, stale: boolean) => void;
337
+ setEntitySyncMetadata: (type: EntityType, id: EntityId, metadata: Partial<EntitySyncMetadata>) => void;
338
+ clearEntitySyncMetadata: (type: EntityType, id: EntityId) => void;
339
+ setListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
340
+ appendListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
341
+ prependListResult: (key: QueryKey, ids: EntityId[], meta?: Partial<ListState>) => void;
342
+ removeIdFromAllLists: (type: EntityType, id: EntityId) => void;
343
+ insertIdInList: (key: QueryKey, id: EntityId, position: "start" | "end" | number) => void;
344
+ setListFetching: (key: QueryKey, fetching: boolean) => void;
345
+ setListFetchingMore: (key: QueryKey, fetchingMore: boolean) => void;
346
+ setListError: (key: QueryKey, error: string | null, typed?: EntityError | null) => void;
347
+ setListStale: (key: QueryKey, stale: boolean) => void;
348
+ invalidateEntity: (type: EntityType, id?: EntityId) => void;
349
+ invalidateLists: (matcher: string | ((key: QueryKey) => boolean)) => void;
350
+ invalidateType: (type: EntityType) => void;
351
+ readEntity: <T = Record<string, unknown>>(type: EntityType, id: EntityId) => T | null;
352
+ readEntitySnapshot: <T = Record<string, unknown>>(type: EntityType, id: EntityId) => EntitySnapshot<T & Record<string, unknown>> | null;
353
+ }) => void), shouldReplace?: false): void;
354
+ setState(nextStateOrUpdater: GraphState | ((state: {
355
+ entities: {
356
+ [x: string]: {
357
+ [x: string]: {
358
+ [x: string]: unknown;
359
+ };
360
+ };
361
+ };
362
+ patches: {
363
+ [x: string]: {
364
+ [x: string]: {
365
+ [x: string]: unknown;
366
+ };
367
+ };
368
+ };
369
+ entityStates: {
370
+ [x: string]: {
371
+ isFetching: boolean;
372
+ lastFetched: number | null;
373
+ error: string | null;
374
+ stale: boolean;
375
+ };
376
+ };
377
+ syncMetadata: {
378
+ [x: string]: {
379
+ synced: boolean;
380
+ origin: SyncOrigin;
381
+ updatedAt: number | null;
382
+ };
383
+ };
384
+ lists: {
385
+ [x: string]: {
386
+ ids: string[];
387
+ total: number | null;
388
+ nextCursor: string | null;
389
+ prevCursor: string | null;
390
+ hasNextPage: boolean;
391
+ hasPrevPage: boolean;
392
+ isFetching: boolean;
393
+ isFetchingMore: boolean;
394
+ error: string | null;
395
+ lastError: {
396
+ kind: "terminal";
397
+ status?: number | undefined;
398
+ name: string;
399
+ message: string;
400
+ stack?: string | undefined;
401
+ cause?: unknown;
402
+ } | {
403
+ kind: "transient";
404
+ status?: number | undefined;
405
+ name: string;
406
+ message: string;
407
+ stack?: string | undefined;
408
+ cause?: unknown;
409
+ } | null;
410
+ lastFetched: number | null;
411
+ stale: boolean;
412
+ currentPage: number | null;
413
+ pageSize: number | null;
414
+ };
415
+ };
416
+ upsertEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => void;
417
+ upsertEntities: (type: EntityType, entries: Array<{
418
+ id: EntityId;
419
+ data: Record<string, unknown>;
420
+ }>) => void;
421
+ replaceEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => void;
422
+ removeEntity: (type: EntityType, id: EntityId) => void;
423
+ patchEntity: (type: EntityType, id: EntityId, patch: Record<string, unknown>) => void;
424
+ unpatchEntity: (type: EntityType, id: EntityId, keys: string[]) => void;
425
+ clearPatch: (type: EntityType, id: EntityId) => void;
426
+ setEntityFetching: (type: EntityType, id: EntityId, fetching: boolean) => void;
427
+ setEntityError: (type: EntityType, id: EntityId, error: string | null) => void;
428
+ setEntityFetched: (type: EntityType, id: EntityId) => void;
429
+ setEntityStale: (type: EntityType, id: EntityId, stale: boolean) => void;
430
+ setEntitySyncMetadata: (type: EntityType, id: EntityId, metadata: Partial<EntitySyncMetadata>) => void;
431
+ clearEntitySyncMetadata: (type: EntityType, id: EntityId) => void;
432
+ setListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
433
+ appendListResult: (key: QueryKey, ids: EntityId[], meta: Partial<Omit<ListState, "ids" | "isFetching" | "isFetchingMore" | "error" | "stale">>) => void;
434
+ prependListResult: (key: QueryKey, ids: EntityId[], meta?: Partial<ListState>) => void;
435
+ removeIdFromAllLists: (type: EntityType, id: EntityId) => void;
436
+ insertIdInList: (key: QueryKey, id: EntityId, position: "start" | "end" | number) => void;
437
+ setListFetching: (key: QueryKey, fetching: boolean) => void;
438
+ setListFetchingMore: (key: QueryKey, fetchingMore: boolean) => void;
439
+ setListError: (key: QueryKey, error: string | null, typed?: EntityError | null) => void;
440
+ setListStale: (key: QueryKey, stale: boolean) => void;
441
+ invalidateEntity: (type: EntityType, id?: EntityId) => void;
442
+ invalidateLists: (matcher: string | ((key: QueryKey) => boolean)) => void;
443
+ invalidateType: (type: EntityType) => void;
444
+ readEntity: <T = Record<string, unknown>>(type: EntityType, id: EntityId) => T | null;
445
+ readEntitySnapshot: <T = Record<string, unknown>>(type: EntityType, id: EntityId) => EntitySnapshot<T & Record<string, unknown>> | null;
446
+ }) => void), shouldReplace: true): void;
447
+ }>;
448
+
449
+ /**
450
+ * Compiles a {@link FilterSpec} into a Prisma `where` object (plain JSON-serializable shape).
451
+ *
452
+ * Operator mapping matches common Prisma filter APIs: `eq` → `equals`, string ops use `mode: "insensitive"`,
453
+ * `nin` → `notIn`, `arrayContains` → `has`, `isNull` uses `null` / `{ not: null }` per `value`, and `isNotNull` → `{ not: null }`.
454
+ * Unsupported ops (`between`, `arrayOverlaps`, `matches`, `custom`) are omitted from the result.
455
+ *
456
+ * Top-level clause arrays are combined with `AND`. {@link FilterGroup} uses `AND` / `OR` to match `group.logic`.
457
+ */
458
+ declare function toPrismaWhere(filter: FilterSpec): Record<string, unknown>;
459
+ /**
460
+ * Compiles a {@link SortSpec} into Prisma `orderBy` form: `[{ fieldName: "asc" | "desc" }, …]`.
461
+ * `nulls` and `comparator` on {@link SortClause} are ignored (local-only); extend callers if your Prisma version supports null ordering.
462
+ */
463
+ declare function toPrismaOrderBy(sort: SortSpec): Record<string, string>[];
464
+
465
+ /**
466
+ * Transport-agnostic comparison operators. Same spec can compile to REST, SQL, GraphQL, or local JS (`evaluator`).
467
+ * `custom` opts out of automatic serialization — use for predicates only the client can evaluate.
468
+ */
469
+ type FilterOperator = "eq" | "neq" | "gt" | "gte" | "lt" | "lte" | "in" | "nin" | "contains" | "startsWith" | "endsWith" | "isNull" | "isNotNull" | "between" | "arrayContains" | "arrayOverlaps" | "matches" | "custom";
470
+ /** Atomic filter: field path, operator, optional value, and optional JS predicate for `custom`. */
471
+ interface FilterClause {
472
+ field: string;
473
+ op: FilterOperator;
474
+ value?: unknown;
475
+ predicate?: (fieldValue: unknown, entity: Record<string, unknown>) => boolean;
476
+ }
477
+ type FilterLogic = "and" | "or";
478
+ /** Nested boolean group so you can express `(A AND B) OR C` without losing structure when compiling to backends. */
479
+ interface FilterGroup {
480
+ logic: FilterLogic;
481
+ clauses: Array<FilterClause | FilterGroup>;
482
+ }
483
+ /** Top-level filter: flat AND list of clauses, or a recursive `FilterGroup`. */
484
+ type FilterSpec = FilterGroup | FilterClause[];
485
+ type SortDirection = "asc" | "desc";
486
+ /** Single sort key with optional null ordering and custom comparator for local sort parity with remote semantics. */
487
+ interface SortClause {
488
+ field: string;
489
+ direction: SortDirection;
490
+ nulls?: "first" | "last";
491
+ comparator?: (a: unknown, b: unknown) => number;
492
+ }
493
+ /** Ordered multi-key sort (stable application in `compareEntities`). */
494
+ type SortSpec = SortClause[];
495
+ /**
496
+ * Everything `useEntityView` needs to describe a virtualized collection: filters, sorts, and simple multi-field search.
497
+ * One descriptor can drive local evaluation, remote query compilation, or hybrid mode.
498
+ */
499
+ interface ViewDescriptor {
500
+ filter?: FilterSpec;
501
+ sort?: SortSpec;
502
+ search?: {
503
+ query: string;
504
+ fields: string[];
505
+ minChars?: number;
506
+ };
507
+ }
508
+ /**
509
+ * 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).
510
+ */
511
+ type CompletenessMode = "local" | "remote" | "hybrid";
512
+ /**
513
+ * Compile a view to flat REST query params (`sort`, `q`, and `field[op]=value` keys). Skips `custom` clauses — those cannot be expressed as strings.
514
+ */
515
+ declare function toRestParams(view: ViewDescriptor): Record<string, string>;
516
+ /**
517
+ * Compile a view to parameterized SQL fragments for server-side filtering/sorting. Unknown ops become `TRUE` — validate or restrict ops at the edge.
518
+ */
519
+ declare function toSQLClauses(view: ViewDescriptor): {
520
+ where: string;
521
+ orderBy: string;
522
+ params: unknown[];
523
+ };
524
+ /**
525
+ * Produce a GraphQL-variable-shaped object from a view (Hasura/Postgraphile-style `_op` maps). Intended as a starting point — wire to your actual schema.
526
+ */
527
+ declare function toGraphQLVariables(view: ViewDescriptor): {
528
+ where?: Record<string, unknown>;
529
+ orderBy?: Array<Record<string, unknown>>;
530
+ search?: string;
531
+ };
532
+
533
+ /**
534
+ * Normalize nested `FilterGroup` trees to a flat clause list for compilers that only understand atomic predicates.
535
+ */
536
+ declare function flattenClauses(filter: FilterSpec): FilterClause[];
537
+ /** True if any clause requires client-side `predicate` logic — forces local/hybrid evaluation paths that cannot be pushed to generic REST/SQL. */
538
+ declare function hasCustomPredicates(filter: FilterSpec): boolean;
539
+
540
+ /**
541
+ * 2.0 transport contract.
542
+ *
543
+ * Why this exists: in 1.x each call site of `useEntityList` /
544
+ * `useEntityView` passed its own `fetch` closure + `normalize`
545
+ * callback. That leaked the transport contract to every consumer and
546
+ * was the structural cause of the Quick Stats trap — each widget could
547
+ * (and did) reinvent the same retry-loop bug.
548
+ *
549
+ * In 2.0 the transport is registered ONCE per entity type at app boot
550
+ * via `registerEntityTransport(type, transport)`. Hooks
551
+ * (`useEntities`, `useEntityQuery`) look up the registered transport
552
+ * by entity type — consumers never pass a fetcher.
553
+ *
554
+ * Transports are pluggable: a built-in `makeRestTransport` covers
555
+ * PostgREST / Supabase; future helpers can cover GraphQL, PGlite-direct,
556
+ * mock-in-memory, etc. The contract is intentionally tiny.
557
+ */
558
+
559
+ /**
560
+ * Transport-agnostic list query passed by `useEntities` / `useEntityQuery`
561
+ * down to the registered transport.
562
+ *
563
+ * The transport is responsible for compiling this into the
564
+ * transport-specific wire format (HTTP query string, GraphQL variables,
565
+ * SQL WHERE, in-memory predicate, etc.).
566
+ */
567
+ interface ListQuery {
568
+ /** Optional filter; transport-agnostic, see `FilterSpec`. */
569
+ filter?: FilterSpec | null;
570
+ /** Optional sort spec; transport-agnostic. */
571
+ sort?: SortSpec | null;
572
+ /** Free-text search string; transport decides which fields to match. */
573
+ search?: string;
574
+ /** Page size hint. Transport may cap or ignore. */
575
+ limit?: number;
576
+ /** Opaque cursor for pagination (cursor or numeric offset). */
577
+ cursor?: string | number | null;
578
+ /** Aborts the request when fired. Transports MUST honour this. */
579
+ signal?: AbortSignal;
580
+ }
581
+ /**
582
+ * Page of results returned by `EntityTransport.list`.
583
+ *
584
+ * `total` is `null` when the backend doesn't report it cheaply
585
+ * (consumers should treat as "unknown" rather than "zero").
586
+ *
587
+ * `nextCursor` is `null`/`undefined` when there are no more pages.
588
+ */
589
+ interface ListResult<T> {
590
+ rows: T[];
591
+ total: number | null;
592
+ nextCursor?: string | number | null;
593
+ }
594
+ /**
595
+ * Operation flavour for a realtime change event.
596
+ */
597
+ type ChangeOp = "insert" | "update" | "delete";
598
+ /**
599
+ * Realtime change event emitted by a transport's `subscribe`.
600
+ *
601
+ * For `delete` only `id` is meaningful; `row` MAY be omitted.
602
+ */
603
+ interface ChangeEvent<T> {
604
+ op: ChangeOp;
605
+ id: string;
606
+ row?: T;
607
+ }
608
+ /**
609
+ * The full per-entity transport contract.
610
+ *
611
+ * One transport per entity type, registered at app boot. The hooks
612
+ * (`useEntities`, `useEntityQuery`) look it up via `getEntityTransport(type)`.
613
+ *
614
+ * Required: `identify`, `authoritative`, `list`.
615
+ * Optional: `get` (for single-row fetches), `subscribe` (for realtime),
616
+ * `staleTime` (hint for SWR).
617
+ */
618
+ interface EntityTransport<T extends object> {
619
+ /**
620
+ * Derive the stable string id of a row. Used by the engine to write
621
+ * into the entity graph under the right id key.
622
+ */
623
+ identify: (row: T) => string;
624
+ /**
625
+ * `true` when the local copy is considered authoritative (e.g. PGlite
626
+ * + Electric Tier-A sync) — consumers can render from cache without
627
+ * a remote round-trip on every mount.
628
+ *
629
+ * `false` when the remote is authoritative (Tier-B REST hybrid) — the
630
+ * cache is a hint, not the truth.
631
+ */
632
+ authoritative: boolean;
633
+ /**
634
+ * Stale-time hint in ms. The engine SWR logic refetches when
635
+ * `Date.now() - lastFetched > staleTime`. Omit to inherit the engine
636
+ * default (30s).
637
+ */
638
+ staleTime?: number;
639
+ /** Fetch a list. The signal in `q.signal` MUST be honoured. */
640
+ list: (q: ListQuery) => Promise<ListResult<T>>;
641
+ /** Fetch a single row by id. Optional. */
642
+ get?: (id: string, signal?: AbortSignal) => Promise<T | null>;
643
+ /**
644
+ * Subscribe to realtime change events for this entity type. Optional.
645
+ *
646
+ * Called by `useEntities` / `useEntityQuery` exactly once per
647
+ * subscriber-bearing key. The returned function MUST tear down all
648
+ * underlying resources (sockets, channels, intervals).
649
+ */
650
+ subscribe?: (onChange: (ev: ChangeEvent<T>) => void) => () => void;
651
+ }
652
+
653
+ /**
654
+ * Process-global registry of `EntityTransport` instances by entity type.
655
+ *
656
+ * Why global state: every consumer of `useEntities("Client")` must
657
+ * resolve the SAME transport. Threading a registry through React
658
+ * context would require every test, every isolated story, every
659
+ * non-React caller (engine internals) to plumb the context — a tax
660
+ * that buys nothing. The 1.x library already used module-global state
661
+ * for the `useGraphStore` Zustand store; this follows the same shape.
662
+ *
663
+ * The registry is mutable: re-registering a type silently replaces the
664
+ * prior transport (tests rely on this; production code should only
665
+ * register once at app boot).
666
+ */
667
+
668
+ /**
669
+ * Register the transport for an entity type. Called once per type at
670
+ * app boot, typically from `src/shared/db/entity-transports.ts` in the
671
+ * consuming application.
672
+ *
673
+ * Re-registering REPLACES the prior transport — useful for tests, a
674
+ * footgun for production code. If you find yourself re-registering at
675
+ * runtime, you almost certainly want a different design.
676
+ */
677
+ declare function registerEntityTransport<T extends object>(type: EntityType, transport: EntityTransport<T>): void;
678
+ /**
679
+ * Look up the transport for an entity type. Throws if none is
680
+ * registered — there is no "default" transport, by design.
681
+ *
682
+ * The cast to `EntityTransport<T>` is the API ergonomic
683
+ * compromise that lets call sites stay generic; the registry stores
684
+ * `EntityTransport<object>` because TypeScript can't otherwise unify
685
+ * heterogeneous rows in one map.
686
+ */
687
+ declare function getEntityTransport<T extends object>(type: EntityType): EntityTransport<T>;
688
+ /**
689
+ * Test-only: clear ALL registered transports. Production code should
690
+ * never need this; tests use it to reset between specs.
691
+ */
692
+ declare function __resetEntityTransports(): void;
693
+ /**
694
+ * Introspection: how many transports are currently registered.
695
+ * Useful for asserting in tests that boot wiring ran exactly once.
696
+ */
697
+ declare function getRegisteredEntityTypes(): EntityType[];
698
+
699
+ /**
700
+ * `makeRestTransport` — built-in helper for the most common case: a
701
+ * Supabase / PostgREST backend reached through a `SupabaseClient`-like
702
+ * object.
703
+ *
704
+ * Why a helper (not a class): consumers stay declarative —
705
+ * registerEntityTransport("Client",
706
+ * makeRestTransport<Client>({ supabase, table: "client" }));
707
+ * — and the library never imports `@supabase/supabase-js` directly
708
+ * (peer-dep-free).
709
+ *
710
+ * The transport:
711
+ * - Compiles `ListQuery` (filter / sort / search / cursor / limit)
712
+ * into a PostgREST query via the supabase-js builder.
713
+ * - Maps HTTP status to `TerminalError` (4xx) or `TransientError`
714
+ * (5xx / network).
715
+ * - Honours `AbortSignal` via supabase-js `.abortSignal(signal)`.
716
+ * - Returns `total` from PostgREST `count: 'exact'` when requested.
717
+ */
718
+
719
+ /**
720
+ * Minimal slice of the `@supabase/supabase-js` `SupabaseClient` we use.
721
+ * Declared structurally so the library does not need to import the
722
+ * package as a dependency (peer-dep-free).
723
+ */
724
+ interface SupabaseLike {
725
+ from: (table: string) => SupabaseQueryBuilderLike;
726
+ }
727
+ /**
728
+ * Subset of the supabase-js builder we exercise. Each call returns the
729
+ * same builder for chaining; `then`-able at the end resolves to
730
+ * `{ data, error, count, status }`.
731
+ */
732
+ interface SupabaseQueryBuilderLike {
733
+ select: (columns: string, opts?: {
734
+ count?: "exact" | "planned" | "estimated";
735
+ head?: boolean;
736
+ }) => SupabaseQueryBuilderLike;
737
+ eq: (col: string, val: unknown) => SupabaseQueryBuilderLike;
738
+ neq: (col: string, val: unknown) => SupabaseQueryBuilderLike;
739
+ gt: (col: string, val: unknown) => SupabaseQueryBuilderLike;
740
+ gte: (col: string, val: unknown) => SupabaseQueryBuilderLike;
741
+ lt: (col: string, val: unknown) => SupabaseQueryBuilderLike;
742
+ lte: (col: string, val: unknown) => SupabaseQueryBuilderLike;
743
+ in: (col: string, vals: readonly unknown[]) => SupabaseQueryBuilderLike;
744
+ ilike: (col: string, pattern: string) => SupabaseQueryBuilderLike;
745
+ is: (col: string, val: null | boolean) => SupabaseQueryBuilderLike;
746
+ or: (filter: string) => SupabaseQueryBuilderLike;
747
+ order: (col: string, opts?: {
748
+ ascending?: boolean;
749
+ nullsFirst?: boolean;
750
+ }) => SupabaseQueryBuilderLike;
751
+ range: (from: number, to: number) => SupabaseQueryBuilderLike;
752
+ limit: (n: number) => SupabaseQueryBuilderLike;
753
+ abortSignal?: (signal: AbortSignal) => SupabaseQueryBuilderLike;
754
+ then: <TResult1 = unknown, TResult2 = never>(onfulfilled?: ((value: {
755
+ data: unknown;
756
+ error: unknown;
757
+ count?: number | null;
758
+ status?: number;
759
+ }) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null) => Promise<TResult1 | TResult2>;
760
+ }
761
+ /** Options for `makeRestTransport`. */
762
+ interface MakeRestTransportOptions<T extends object> {
763
+ /** A `@supabase/supabase-js` client (or any structurally-compatible thing). */
764
+ supabase: SupabaseLike;
765
+ /** PostgREST table name. */
766
+ table: string;
767
+ /** Columns to select (default `"*"`). */
768
+ select?: string;
769
+ /** Stable id derivation. Defaults to `(row) => String(row.id)`. */
770
+ identify?: (row: T) => string;
771
+ /** Whether the local cache is treated as authoritative. */
772
+ authoritative?: boolean;
773
+ /** Stale-time hint in ms. */
774
+ staleTime?: number;
775
+ /** Free-text search column list (compiled to `or=col.ilike.*query*,…`). */
776
+ searchColumns?: string[];
777
+ /** Default page size when `ListQuery.limit` is absent. */
778
+ defaultLimit?: number;
779
+ }
780
+ /**
781
+ * Build an `EntityTransport<T>` from a Supabase client + a table name.
782
+ *
783
+ * The returned transport:
784
+ * - Maps `ListQuery.filter` clauses to PostgREST operators (`eq`, `gt`,
785
+ * `in`, `ilike`, `is null`, etc.).
786
+ * - Compiles `ListQuery.search` into an `or=` clause across
787
+ * `searchColumns`.
788
+ * - Uses `count: 'exact'` on first page to surface `total`.
789
+ * - Throws `TerminalError` on 4xx (no retry); `TransientError` on 5xx
790
+ * or network failure.
791
+ * - Honours `signal` via `.abortSignal(signal)` (supabase-js >= 2.39).
792
+ */
793
+ declare function makeRestTransport<T extends object>(opts: MakeRestTransportOptions<T>): EntityTransport<T>;
794
+
795
+ declare function getValueAtPath<T = unknown>(source: unknown, path: string): T | undefined;
796
+ declare function setValueAtPath<T extends object>(source: T, path: string, value: unknown): T;
797
+ declare function collectDirtyPaths(current: unknown, original: unknown, prefix?: string, acc?: Set<string>): Set<string>;
798
+
799
+ /**
800
+ * merge/types.ts — Pluggable conflict-resolution seam for the entity graph.
801
+ *
802
+ * Why a port (not a hard-wired engine):
803
+ * - The graph receives concurrent writes from multiple sources — realtime
804
+ * adapters (Flint, Supabase, Surreal), AG-UI agents, and optimistic local
805
+ * mutations. The default resolution is last-write-wins (LWW), matching the
806
+ * historical shallow-merge in `upsertEntity`.
807
+ * - Collaborative / agentic multi-writer scenarios need stronger guarantees
808
+ * (CRDT). But the *engine* (Loro vs Automerge vs Yjs) is a downstream
809
+ * decision — and the Flint fabric's own CRDT choice is still open. Encoding
810
+ * the decision as a swappable `MergeStrategy` keeps that open decision from
811
+ * blocking this layer.
812
+ *
813
+ * One strategy is registered per entity type (or a global default). The graph
814
+ * consults it at write time; absent a registration it falls back to LWW, so
815
+ * existing behavior is byte-for-byte preserved.
816
+ */
817
+
818
+ /** Context handed to a {@link MergeStrategy} for one write. */
819
+ interface MergeContext {
820
+ /** Entity kind being written. */
821
+ type: EntityType;
822
+ /** Entity id being written. */
823
+ id: EntityId;
824
+ /** Provenance of the incoming write (server / client / optimistic). */
825
+ origin: SyncOrigin;
826
+ /** Wall-clock ms of the incoming write, when the caller knows it. */
827
+ updatedAt: number | null;
828
+ }
829
+ /**
830
+ * Resolves the next canonical entity value from the previous value and an
831
+ * incoming partial/full write.
832
+ *
833
+ * Contract:
834
+ * - MUST be pure (no side effects, no graph access) — the graph applies the
835
+ * returned value inside its Immer producer.
836
+ * - MUST return a NEW object; never mutate `prev` or `next` (immutability rule).
837
+ * - `prev` is `undefined` on first write for an id.
838
+ *
839
+ * @param prev - Current canonical value, or undefined if none exists yet.
840
+ * @param next - Incoming write (partial for upsert, full for replace).
841
+ * @param ctx - Type/id/origin/timestamp metadata for this write.
842
+ * @returns The merged canonical value to store.
843
+ */
844
+ type MergeStrategy = (prev: Record<string, unknown> | undefined, next: Record<string, unknown>, ctx: MergeContext) => Record<string, unknown>;
845
+
846
+ /**
847
+ * merge/lww.ts — Last-write-wins default strategy.
848
+ *
849
+ * Semantics match the historical `upsertEntity` behavior exactly: shallow-merge
850
+ * the incoming write over the previous value. This is the fallback when no
851
+ * other strategy is registered, so registering nothing is a no-op change.
852
+ */
853
+
854
+ /**
855
+ * Shallow-merge `next` over `prev` (incoming keys win). Returns a new object.
856
+ * On first write (`prev` undefined) returns a shallow copy of `next`.
857
+ */
858
+ declare const lwwStrategy: MergeStrategy;
859
+
860
+ /**
861
+ * merge/registry.ts — One MergeStrategy per entity type (+ optional global default).
862
+ *
863
+ * Lookup order at write time:
864
+ * 1. strategy registered for the exact `type`
865
+ * 2. global default strategy (if one was set)
866
+ * 3. built-in LWW (`lwwStrategy`)
867
+ *
868
+ * Step 3 guarantees that with zero registrations the graph behaves exactly as
869
+ * it did before this seam existed.
870
+ */
871
+
872
+ /**
873
+ * Register a {@link MergeStrategy} for one entity type. Overwrites any prior
874
+ * registration for that type.
875
+ */
876
+ declare function registerMergeStrategy(type: string, strategy: MergeStrategy): void;
877
+ /**
878
+ * Set the fallback strategy used for any type without its own registration.
879
+ * Pass `null` to clear it (reverting the fallback to built-in LWW).
880
+ */
881
+ declare function setDefaultMergeStrategy(strategy: MergeStrategy | null): void;
882
+ /**
883
+ * Resolve the effective strategy for a type: per-type → global default → LWW.
884
+ * Always returns a usable strategy (never null).
885
+ */
886
+ declare function getMergeStrategy(type: string): MergeStrategy;
887
+ /** True if `type` (or a global default) has a non-LWW strategy registered. */
888
+ declare function hasMergeStrategy(type: string): boolean;
889
+
890
+ /**
891
+ * merge/loro.ts — Loro-backed CRDT MergeStrategy (reference implementation).
892
+ *
893
+ * Maps each entity (`type:id`) onto a Loro document whose root is an LWW-Map —
894
+ * a near-1:1 fit for the graph's `type:id:field` model. Concurrent field writes
895
+ * resolve via Loro's CRDT semantics rather than blind last-write-wins, so two
896
+ * writers updating *different* fields never clobber each other, and writers
897
+ * updating the *same* field resolve deterministically across replicas.
898
+ *
899
+ * `loro-crdt` is an OPTIONAL peer dependency. It is imported lazily so the core
900
+ * bundle stays `zustand + immer` only — nothing loads Loro unless an app
901
+ * actually registers `createLoroMergeStrategy()`.
902
+ *
903
+ * NOTE: `createLoroMergeStrategy` is async because it dynamically imports the
904
+ * engine. Await it once at boot, then register the resolved synchronous
905
+ * strategy. The returned strategy itself is pure+sync (the MergeStrategy
906
+ * contract), keeping per-write resolution off the async path.
907
+ */
908
+
909
+ /**
910
+ * Build a Loro-backed {@link MergeStrategy}. Resolves `loro-crdt` lazily; throws
911
+ * a clear error if the optional peer is not installed.
912
+ *
913
+ * @example
914
+ * ```ts
915
+ * const loro = await createLoroMergeStrategy();
916
+ * registerMergeStrategy("Document", loro);
917
+ * ```
918
+ */
919
+ declare function createLoroMergeStrategy(): Promise<MergeStrategy>;
920
+
921
+ type GraphStore = ReturnType<typeof useGraphStore.getState>;
922
+ type GraphIncludeMap = Record<string, GraphIncludeRelation>;
923
+ type GraphIncludeRelation = {
924
+ type: EntityType;
925
+ via: {
926
+ kind: "field";
927
+ field: string;
928
+ };
929
+ include?: GraphIncludeMap;
930
+ } | {
931
+ type: EntityType;
932
+ via: {
933
+ kind: "array";
934
+ field: string;
935
+ };
936
+ include?: GraphIncludeMap;
937
+ } | {
938
+ type: EntityType;
939
+ via: {
940
+ kind: "list";
941
+ key: QueryKey | ((entity: Record<string, unknown>) => QueryKey | null | undefined);
942
+ };
943
+ include?: GraphIncludeMap;
944
+ } | {
945
+ type: EntityType;
946
+ via: {
947
+ kind: "resolver";
948
+ resolve: (entity: Record<string, unknown>, store: GraphStore) => EntityId | EntityId[] | null | undefined;
949
+ };
950
+ include?: GraphIncludeMap;
951
+ };
952
+ interface GraphQueryOptions<TEntity extends object> {
953
+ type: EntityType;
954
+ id?: EntityId;
955
+ ids?: EntityId[];
956
+ listKey?: QueryKey;
957
+ where?: (entity: EntitySnapshot<TEntity>) => boolean;
958
+ sort?: (a: EntitySnapshot<TEntity>, b: EntitySnapshot<TEntity>) => number;
959
+ include?: GraphIncludeMap;
960
+ select?: ((entity: Record<string, unknown>) => unknown) | string[];
961
+ }
962
+ type ProjectedRow = Record<string, unknown>;
963
+ declare function queryOnce<TEntity extends object>(opts: GraphQueryOptions<TEntity> & {
964
+ id: EntityId;
965
+ }): ProjectedRow | null;
966
+ declare function queryOnce<TEntity extends object>(opts: GraphQueryOptions<TEntity>): ProjectedRow[];
967
+ declare const selectGraph: typeof queryOnce;
968
+
969
+ interface GraphDataSnapshot {
970
+ entities: ReturnType<typeof useGraphStore.getState>["entities"];
971
+ patches: ReturnType<typeof useGraphStore.getState>["patches"];
972
+ entityStates: ReturnType<typeof useGraphStore.getState>["entityStates"];
973
+ syncMetadata: ReturnType<typeof useGraphStore.getState>["syncMetadata"];
974
+ lists: ReturnType<typeof useGraphStore.getState>["lists"];
975
+ }
976
+ interface GraphTransaction {
977
+ upsertEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => GraphTransaction;
978
+ replaceEntity: (type: EntityType, id: EntityId, data: Record<string, unknown>) => GraphTransaction;
979
+ removeEntity: (type: EntityType, id: EntityId) => GraphTransaction;
980
+ patchEntity: (type: EntityType, id: EntityId, patch: Record<string, unknown>) => GraphTransaction;
981
+ clearPatch: (type: EntityType, id: EntityId) => GraphTransaction;
982
+ insertIdInList: (key: QueryKey, id: EntityId, position: "start" | "end" | number) => GraphTransaction;
983
+ removeIdFromAllLists: (type: EntityType, id: EntityId) => GraphTransaction;
984
+ setEntitySyncMetadata: (type: EntityType, id: EntityId, metadata: Partial<EntitySyncMetadata>) => GraphTransaction;
985
+ markEntityPending: (type: EntityType, id: EntityId, origin?: SyncOrigin) => GraphTransaction;
986
+ markEntitySynced: (type: EntityType, id: EntityId, origin?: SyncOrigin) => GraphTransaction;
987
+ commit: () => void;
988
+ rollback: () => void;
989
+ snapshot: () => GraphDataSnapshot;
990
+ }
991
+ interface GraphActionOptions<TInput, TResult> {
992
+ key?: string;
993
+ optimistic?: (tx: GraphTransaction, input: TInput) => void;
994
+ run: (tx: GraphTransaction, input: TInput) => Promise<TResult> | TResult;
995
+ onSuccess?: (result: TResult, input: TInput, tx: GraphTransaction) => void;
996
+ onError?: (error: Error, input: TInput) => void;
997
+ }
998
+ interface GraphActionRecord$1 {
999
+ id: string;
1000
+ key: string;
1001
+ input: unknown;
1002
+ enqueuedAt: string;
1003
+ }
1004
+ type GraphActionEvent = {
1005
+ type: "enqueued";
1006
+ record: GraphActionRecord$1;
1007
+ } | {
1008
+ type: "settled";
1009
+ record: GraphActionRecord$1;
1010
+ };
1011
+ declare function createGraphTransaction(): GraphTransaction;
1012
+ declare function createGraphAction<TInput, TResult>(opts: GraphActionOptions<TInput, TResult>): (input: TInput) => Promise<TResult>;
1013
+
1014
+ interface GraphEffectEvent<T> {
1015
+ key: string;
1016
+ value: T;
1017
+ previousValue: T;
1018
+ }
1019
+ interface GraphEffectOptions<T> {
1020
+ query: () => T[] | T | null;
1021
+ getKey?: (value: T, index: number) => string;
1022
+ skipInitial?: boolean;
1023
+ isEqual?: (previousValue: T, nextValue: T) => boolean;
1024
+ onEnter?: (event: {
1025
+ key: string;
1026
+ value: T;
1027
+ }) => void;
1028
+ onUpdate?: (event: GraphEffectEvent<T>) => void;
1029
+ onExit?: (event: {
1030
+ key: string;
1031
+ previousValue: T;
1032
+ }) => void;
1033
+ }
1034
+ interface GraphEffectHandle {
1035
+ dispose: () => void;
1036
+ }
1037
+ declare function createGraphEffect<T>(opts: GraphEffectOptions<T>): GraphEffectHandle;
1038
+
1039
+ /**
1040
+ * schema.ts (core) — framework-agnostic JSON-schema registry + snapshot/markdown
1041
+ * helpers. The React field renderers/editors (MarkdownFieldRenderer,
1042
+ * MarkdownFieldEditor, useSchemaEntityFields, buildEntityFieldsFromSchema which
1043
+ * depends on the UI FieldDescriptor type) live in the react binding's schema.tsx,
1044
+ * built on these primitives.
1045
+ */
1046
+ interface JsonSchemaObject {
1047
+ $id?: string;
1048
+ title?: string;
1049
+ description?: string;
1050
+ type?: string | string[];
1051
+ format?: string;
1052
+ enum?: readonly unknown[];
1053
+ default?: unknown;
1054
+ properties?: Record<string, JsonSchemaObject>;
1055
+ items?: JsonSchemaObject;
1056
+ required?: string[];
1057
+ additionalProperties?: boolean | JsonSchemaObject;
1058
+ ["x-a2ui-component"]?: string;
1059
+ ["x-display-order"]?: number;
1060
+ ["x-field-type"]?: string;
1061
+ ["x-hidden"]?: boolean;
1062
+ }
1063
+ interface EntityJsonSchemaConfig {
1064
+ entityType: string;
1065
+ schemaId?: string;
1066
+ field?: string;
1067
+ version?: string;
1068
+ source?: "static" | "runtime" | "ai";
1069
+ schema: JsonSchemaObject;
1070
+ }
1071
+ interface GetEntityJsonSchemaOptions {
1072
+ entityType: string;
1073
+ schemaId?: string;
1074
+ field?: string;
1075
+ }
1076
+ interface GraphSnapshotWithSchemasOptions {
1077
+ scope: string;
1078
+ data: unknown;
1079
+ schemas: Array<EntityJsonSchemaConfig | null | undefined>;
1080
+ pretty?: boolean;
1081
+ }
1082
+ declare function registerEntityJsonSchema(config: EntityJsonSchemaConfig): void;
1083
+ declare function registerRuntimeSchema(config: EntityJsonSchemaConfig): void;
1084
+ declare function getEntityJsonSchema(opts: GetEntityJsonSchemaOptions): EntityJsonSchemaConfig | null;
1085
+ declare function exportGraphSnapshotWithSchemas(opts: GraphSnapshotWithSchemasOptions): string;
1086
+ declare function escapeHtml(input: string): string;
1087
+ declare function renderMarkdownToHtml(value: string): string;
1088
+ /** Infer a coarse field-type string from a JSON-schema node (UI-agnostic). */
1089
+ declare function inferSchemaFieldType(schema: JsonSchemaObject): string;
1090
+ declare function registryKey(entityType: string, field?: string, schemaId?: string): string;
1091
+ declare function humanizeFieldName(value: string): string;
1092
+
1093
+ interface GraphSnapshotExportOptions {
1094
+ scope: string;
1095
+ data: unknown;
1096
+ pretty?: boolean;
1097
+ }
1098
+ interface GraphToolContext {
1099
+ store: ReturnType<typeof useGraphStore.getState>;
1100
+ queryOnce: typeof queryOnce;
1101
+ exportGraphSnapshot: typeof exportGraphSnapshot;
1102
+ }
1103
+ interface SchemaGraphToolContext extends GraphToolContext {
1104
+ getEntityJsonSchema: typeof getEntityJsonSchema;
1105
+ exportGraphSnapshotWithSchemas: typeof exportGraphSnapshotWithSchemas;
1106
+ }
1107
+ declare function exportGraphSnapshot(opts: GraphSnapshotExportOptions): string;
1108
+ declare function createGraphTool<TInput, TResult>(handler: (input: TInput, ctx: GraphToolContext) => Promise<TResult> | TResult): (input: TInput) => TResult | Promise<TResult>;
1109
+ declare function createSchemaGraphTool<TInput, TResult>(handler: (input: TInput, ctx: SchemaGraphToolContext) => Promise<TResult> | TResult): (input: TInput) => TResult | Promise<TResult>;
1110
+
1111
+ interface GraphPersistenceAdapter {
1112
+ get: (key: string) => Promise<string | null> | string | null;
1113
+ set: (key: string, value: string) => Promise<void> | void;
1114
+ remove?: (key: string) => Promise<void> | void;
1115
+ }
1116
+ interface GraphActionRecord {
1117
+ id: string;
1118
+ key: string;
1119
+ input: unknown;
1120
+ enqueuedAt: string;
1121
+ }
1122
+ interface GraphSyncStatus {
1123
+ phase: "idle" | "hydrating" | "syncing" | "ready" | "offline" | "error";
1124
+ isOnline: boolean;
1125
+ isSynced: boolean;
1126
+ pendingActions: number;
1127
+ lastHydratedAt: string | null;
1128
+ lastPersistedAt: string | null;
1129
+ storageKey: string | null;
1130
+ error: string | null;
1131
+ }
1132
+ interface GraphSnapshotPayload {
1133
+ version: 1;
1134
+ snapshot: {
1135
+ entities: ReturnType<typeof useGraphStore.getState>["entities"];
1136
+ patches: ReturnType<typeof useGraphStore.getState>["patches"];
1137
+ entityStates: ReturnType<typeof useGraphStore.getState>["entityStates"];
1138
+ syncMetadata: ReturnType<typeof useGraphStore.getState>["syncMetadata"];
1139
+ lists: ReturnType<typeof useGraphStore.getState>["lists"];
1140
+ };
1141
+ pendingActions: GraphActionRecord[];
1142
+ }
1143
+ interface PersistGraphToStorageOptions {
1144
+ storage: GraphPersistenceAdapter;
1145
+ key: string;
1146
+ pendingActions?: GraphActionRecord[];
1147
+ }
1148
+ interface HydrateGraphFromStorageOptions {
1149
+ storage: GraphPersistenceAdapter;
1150
+ key: string;
1151
+ }
1152
+ /**
1153
+ * Retry-with-backoff policy for replaying pending offline actions.
1154
+ *
1155
+ * The replay loop tracks per-action attempt counts. After `maxAttempts`
1156
+ * failed attempts the action is considered "poisoned" — it is removed from
1157
+ * the in-memory pending queue (so it won't block other actions) and the
1158
+ * optional `poisonHandler` is invoked. Consumers typically log the action,
1159
+ * surface a UI prompt, or persist it to a dead-letter store.
1160
+ *
1161
+ * Defaults: 5 attempts, starting at 500ms, doubling up to 30s, with
1162
+ * "equal" jitter (random in `[delay/2, delay]`).
1163
+ */
1164
+ interface ReplayRetryPolicy {
1165
+ maxAttempts?: number;
1166
+ initialDelayMs?: number;
1167
+ maxDelayMs?: number;
1168
+ backoffFactor?: number;
1169
+ jitter?: "full" | "equal" | "none";
1170
+ poisonHandler?: (action: GraphActionRecord, error: unknown) => void | Promise<void>;
1171
+ }
1172
+ interface StartLocalFirstGraphOptions {
1173
+ storage: GraphPersistenceAdapter;
1174
+ key?: string;
1175
+ replayPendingActions?: boolean;
1176
+ onlineSource?: {
1177
+ getIsOnline: () => boolean;
1178
+ subscribe: (listener: (online: boolean) => void) => () => void;
1179
+ };
1180
+ persistDebounceMs?: number;
1181
+ /** Retry-with-backoff policy for offline action replay. */
1182
+ retryPolicy?: ReplayRetryPolicy;
1183
+ }
1184
+ interface LocalFirstGraphRuntime {
1185
+ ready: Promise<void>;
1186
+ dispose: () => void;
1187
+ persistNow: () => Promise<void>;
1188
+ hydrate: () => Promise<Awaited<ReturnType<typeof hydrateGraphFromStorage>>>;
1189
+ getStatus: () => GraphSyncStatus;
1190
+ }
1191
+ declare function useGraphSyncStatus(): GraphSyncStatus;
1192
+ declare function persistGraphToStorage(opts: PersistGraphToStorageOptions): Promise<{
1193
+ ok: true;
1194
+ key: string;
1195
+ bytes: number;
1196
+ persistedAt: string;
1197
+ }>;
1198
+ declare function hydrateGraphFromStorage(opts: HydrateGraphFromStorageOptions): Promise<{
1199
+ ok: false;
1200
+ key: string;
1201
+ hydratedAt: null;
1202
+ entityCounts: {};
1203
+ error: string;
1204
+ pendingActions?: undefined;
1205
+ } | {
1206
+ ok: true;
1207
+ key: string;
1208
+ hydratedAt: string;
1209
+ entityCounts: {
1210
+ [k: string]: number;
1211
+ };
1212
+ pendingActions: GraphActionRecord[];
1213
+ error?: undefined;
1214
+ }>;
1215
+ declare function startLocalFirstGraph(opts: StartLocalFirstGraphOptions): LocalFirstGraphRuntime;
1216
+ interface ResolvedRetryPolicy {
1217
+ maxAttempts: number;
1218
+ initialDelayMs: number;
1219
+ maxDelayMs: number;
1220
+ backoffFactor: number;
1221
+ jitter: "full" | "equal" | "none";
1222
+ poisonHandler?: (action: GraphActionRecord, error: unknown) => void | Promise<void>;
1223
+ }
1224
+ /**
1225
+ * Attempt to replay an action up to `maxAttempts` times. On exhaustion the
1226
+ * action goes to "poison" — the optional handler is invoked and the function
1227
+ * resolves. Exposed for unit testing.
1228
+ */
1229
+ declare function replayActionWithRetry(action: GraphActionRecord, policy: ResolvedRetryPolicy): Promise<{
1230
+ ok: true;
1231
+ } | {
1232
+ ok: false;
1233
+ poisoned: true;
1234
+ error: unknown;
1235
+ }>;
1236
+
1237
+ /**
1238
+ * Process-wide defaults for stale times, retries, and background revalidation.
1239
+ * Keeps hook signatures small: `useEntity` / `useEntityList` merge these with per-query overrides.
1240
+ */
1241
+ interface EngineOptions {
1242
+ defaultStaleTime?: number;
1243
+ /** Max age (`Date.now() - lastFetched`) for evicting entities with zero subscribers during GC. */
1244
+ defaultGcTime?: number;
1245
+ /** Interval between GC passes when the collector is active (default 60s). */
1246
+ gcInterval?: number;
1247
+ maxRetries?: number;
1248
+ retryBaseDelay?: number;
1249
+ revalidateOnFocus?: boolean;
1250
+ revalidateOnReconnect?: boolean;
1251
+ }
1252
+ /**
1253
+ * Declarative **instruction** for loading one entity: wire transport (`fetch`), normalization, and graph writes.
1254
+ * Hooks pass this to `fetchEntity`; the graph remains source of truth after success.
1255
+ */
1256
+ interface EntityQueryOptions<TRaw, TEntity extends object> {
1257
+ type: EntityType;
1258
+ id: EntityId | null | undefined;
1259
+ fetch: (id: EntityId) => Promise<TRaw>;
1260
+ normalize: (raw: TRaw) => TEntity;
1261
+ idField?: string;
1262
+ sideEffects?: (raw: TRaw, store: typeof useGraphStore) => void;
1263
+ staleTime?: number;
1264
+ enabled?: boolean;
1265
+ onSuccess?: (entity: TEntity) => void;
1266
+ onError?: (error: Error) => void;
1267
+ }
1268
+ /** Cursor/page knobs passed through to list `fetch` implementations (REST, GraphQL, etc.). */
1269
+ interface ListFetchParams {
1270
+ cursor?: string;
1271
+ page?: number;
1272
+ pageSize?: number;
1273
+ params?: Record<string, unknown>;
1274
+ }
1275
+ /**
1276
+ * Normalized list page shape from any backend. Items are mapped through `normalize` into `{ id, data }` upserts — the list stores ids only.
1277
+ */
1278
+ interface ListResponse<T> {
1279
+ items: T[];
1280
+ total?: number | null;
1281
+ nextCursor?: string | null;
1282
+ prevCursor?: string;
1283
+ hasNextPage?: boolean;
1284
+ hasPrevPage?: boolean;
1285
+ page?: number;
1286
+ pageSize?: number;
1287
+ }
1288
+ /**
1289
+ * Declarative **instruction** for a collection query: stable `queryKey`, fetcher, and per-row normalization into the graph.
1290
+ * `mode` controls whether a fetch replaces ids or appends (infinite scroll) when used with load-more.
1291
+ */
1292
+ interface ListQueryOptions<TRaw, TEntity extends object> {
1293
+ type: EntityType;
1294
+ queryKey: unknown[];
1295
+ /**
1296
+ * Inline fetcher (deprecated 1.x REST form). Optional in 2.0: pure graph
1297
+ * subscriptions for Tier-A PGlite/Electric entities pass no fetcher — the
1298
+ * graph is hydrated out-of-band and the hook only joins ids → entities.
1299
+ * When omitted, `fetchList` is a no-op and the hook is read-only.
1300
+ */
1301
+ fetch?: (params: ListFetchParams) => Promise<ListResponse<TRaw>>;
1302
+ /** Row normalizer. Optional alongside `fetch` (graph-only callers omit both). */
1303
+ normalize?: (raw: TRaw) => {
1304
+ id: EntityId;
1305
+ data: TEntity;
1306
+ };
1307
+ sideEffects?: (items: TRaw[], store: typeof useGraphStore) => void;
1308
+ mode?: "replace" | "append";
1309
+ staleTime?: number;
1310
+ enabled?: boolean;
1311
+ onSuccess?: (result: ListResponse<TRaw>) => void;
1312
+ onError?: (error: Error) => void;
1313
+ }
1314
+ /**
1315
+ * Deterministic string key for list queries so object order in nested keys does not create duplicate cache entries.
1316
+ * @param key - Hook-provided query key array (serializable values)
1317
+ */
1318
+ declare function serializeKey(key: unknown[]): string;
1319
+ /**
1320
+ * Collapse concurrent identical requests into one Promise (prevents stampedes when many components mount the same entity/list).
1321
+ * @param key - Logical dedupe key (e.g. `type:id` or serialized list key)
1322
+ * @param fn - Async work that performs the fetch + graph writes
1323
+ */
1324
+ declare function dedupe<T>(key: string, fn: () => Promise<T>): Promise<T>;
1325
+ /**
1326
+ * Ref-count graph interest for a subscriber key (`${type}:${id}`). Background revalidation skips keys with zero subscribers.
1327
+ * @returns Opaque token required for `unregisterSubscriber`
1328
+ */
1329
+ declare function registerSubscriber(key: string): symbol;
1330
+ /** Pair with `registerSubscriber` on unmount so idle entities stop refetching. */
1331
+ declare function unregisterSubscriber(key: string, token: symbol): void;
1332
+ /**
1333
+ * Subscribe to ref-count changes for `registerSubscriber` / `unregisterSubscriber`.
1334
+ * Used by `useGraphDevTools` so subscriber totals update without graph mutations.
1335
+ */
1336
+ declare function subscribeSubscriberStats(onChange: () => void): () => boolean;
1337
+ /** Total active entity subscriber tokens across all keys (sum of ref-counts). */
1338
+ declare function getActiveSubscriberCount(): number;
1339
+ /**
1340
+ * Discriminated union of devtools events.
1341
+ *
1342
+ * - `upsert`, `patch`, `clearPatch` are emitted from `graph-actions.ts` ops.
1343
+ * - `unpatch` and `list` are forward-compatible kinds reserved for future
1344
+ * instrumentation (W5+ event bus / W6+ panel). Consumers MUST handle them
1345
+ * in exhaustive `switch`/`if` chains today, even though no op-site emits
1346
+ * them in this change.
1347
+ */
1348
+ type DevtoolsEvent = {
1349
+ kind: "upsert";
1350
+ type: EntityType;
1351
+ id: EntityId;
1352
+ data: Record<string, unknown>;
1353
+ at: string;
1354
+ } | {
1355
+ kind: "patch";
1356
+ type: EntityType;
1357
+ id: EntityId;
1358
+ patch: Record<string, unknown>;
1359
+ at: string;
1360
+ } | {
1361
+ kind: "unpatch";
1362
+ type: EntityType;
1363
+ id: EntityId;
1364
+ keys: string[];
1365
+ at: string;
1366
+ } | {
1367
+ kind: "clearPatch";
1368
+ type: EntityType;
1369
+ id: EntityId;
1370
+ at: string;
1371
+ } | {
1372
+ kind: "list";
1373
+ key: string;
1374
+ idCount: number;
1375
+ at: string;
1376
+ };
1377
+ type DevtoolsListener = (event: DevtoolsEvent) => void;
1378
+ /**
1379
+ * Subscribe to the devtools event stream.
1380
+ *
1381
+ * - Returns an `UnsubscribeFn` that removes this listener.
1382
+ * - Idempotent: subscribing the same function reference twice tracks it once
1383
+ * (Set semantics).
1384
+ * - Throwing listeners do NOT block delivery to other listeners; the error is
1385
+ * logged via `console.warn` and dispatch continues.
1386
+ * - Re-entrancy safe: listeners that subscribe/unsubscribe during dispatch
1387
+ * affect future events only, not the in-flight one.
1388
+ */
1389
+ declare function subscribeDevtoolsEvent(cb: DevtoolsListener): () => void;
1390
+ /**
1391
+ * Stops the periodic garbage-collection timer started by `startGarbageCollector` / `configureEngine`.
1392
+ */
1393
+ declare function stopGarbageCollector(): void;
1394
+ /**
1395
+ * Starts periodic garbage collection using current `getEngineOptions().gcInterval`.
1396
+ * Stops any previous interval first. No-ops during SSR (`window` is undefined) or without `setInterval`.
1397
+ * @returns Disposer that stops this collector (equivalent to `stopGarbageCollector`).
1398
+ */
1399
+ declare function startGarbageCollector(): () => void;
1400
+ /** Override global engine behavior (typically once at app bootstrap). Restarts GC with merged options. */
1401
+ declare function configureEngine(opts: EngineOptions): void;
1402
+ /** Read merged engine options; hooks use this for default `staleTime` and retry behavior. */
1403
+ declare function getEngineOptions(): Required<EngineOptions>;
1404
+ /**
1405
+ * Run a single-entity fetch with dedupe, retries, normalization, and graph updates.
1406
+ * Call from hooks/adapters — not from presentational components.
1407
+ */
1408
+ declare function fetchEntity<TRaw, TEntity extends object>(opts: EntityQueryOptions<TRaw, TEntity>, engineOpts: Required<EngineOptions>): Promise<void>;
1409
+ /**
1410
+ * Fetch a list page: upserts all rows, writes ids to the list key, supports append mode for pagination.
1411
+ * @param isLoadMore - When true, uses a separate dedupe key and `appendListResult` / `setListFetchingMore`.
1412
+ */
1413
+ declare function fetchList<TRaw, TEntity extends object>(opts: ListQueryOptions<TRaw, TEntity>, params: ListFetchParams, engineOpts: Required<EngineOptions>, isLoadMore?: boolean): Promise<void>;
1414
+ /**
1415
+ * Opt-in window listeners that mark **subscribed** entities stale on focus/reconnect so hooks can SWR-refetch.
1416
+ * Idempotent; safe for SSR (no-ops without `window`). Pair with `configureEngine` flags.
1417
+ */
1418
+ declare function attachGlobalListeners(): void;
1419
+
1420
+ /**
1421
+ * devtools-event-bus — W5 (seim-em-explorer-event-bus-registry)
1422
+ *
1423
+ * Ring-buffered, fan-out event bus on top of the W3 `subscribeDevtoolsEvent` tap.
1424
+ * Adds: replay-on-subscribe, microtask-level burst coalescing, multi-store registry.
1425
+ */
1426
+
1427
+ interface DevtoolsEventBusOptions {
1428
+ /** Maximum events retained in the ring buffer. Default: 500. */
1429
+ bufferSize?: number;
1430
+ /**
1431
+ * Events 1..N (N = threshold) are dispatched individually as they arrive.
1432
+ * Events N+1.. accumulate until the end of the microtask, then are coalesced
1433
+ * into one synthetic `kind:"list"` summary event.
1434
+ * Default: 10. Set to 0 to disable coalescing entirely.
1435
+ */
1436
+ coalesceBurstThreshold?: number;
1437
+ }
1438
+ interface DevtoolsEventBus {
1439
+ /** Subscribe to live events. Replays current buffer synchronously before returning. */
1440
+ subscribe(cb: (event: DevtoolsEvent) => void): () => void;
1441
+ /** Snapshot of buffered events in chronological order (oldest first). */
1442
+ getBuffer(): readonly DevtoolsEvent[];
1443
+ /** Force-dispatch any pending coalesced burst synchronously. */
1444
+ flush(): void;
1445
+ /** Release engine-tap, clear all subscribers, cascade inactive to registry. */
1446
+ destroy(): void;
1447
+ /**
1448
+ * Inject a synthetic `DevtoolsEvent` directly into the bus, bypassing the
1449
+ * engine tap. Used by the Chrome MV3 extension panel to feed events arriving
1450
+ * over the port-messaging bridge into the local bus instance.
1451
+ */
1452
+ inject(event: DevtoolsEvent): void;
1453
+ }
1454
+ /** Matches the signature of `subscribeDevtoolsEvent` — used for external sources. */
1455
+ type DevtoolsSourceFn = (cb: (event: DevtoolsEvent) => void) => () => void;
1456
+ interface RegisteredStore {
1457
+ name: string;
1458
+ active: boolean;
1459
+ }
1460
+ /** @internal Test-only. NOT re-exported from index.ts. */
1461
+ declare function __resetStoreRegistry(): void;
1462
+ declare function createDevtoolsEventBus(opts?: DevtoolsEventBusOptions): DevtoolsEventBus;
1463
+ /**
1464
+ * Wire an additional event source into an existing `DevtoolsEventBus`.
1465
+ * Events from `source` flow through the bus's ring buffer, coalescing, and fan-out.
1466
+ * Throws if `name` is already registered and active.
1467
+ */
1468
+ declare function registerStore(bus: DevtoolsEventBus, source: DevtoolsSourceFn, name: string): () => void;
1469
+ /** Returns a snapshot of all registered stores (active + inactive) in registration order. */
1470
+ declare function getRegisteredStores(): ReadonlyArray<RegisteredStore>;
1471
+
1472
+ /**
1473
+ * devtools-time-travel.ts — G4 parity: true time-travel for the entity graph.
1474
+ *
1475
+ * The Timeline tab (C5) inspected a read-only history log. This adds the
1476
+ * defining Redux-DevTools capability the gap analysis called out: **rewind the
1477
+ * LIVE graph to a prior recorded state and replay forward.**
1478
+ *
1479
+ * Model:
1480
+ * - `recordGraphSnapshot()` captures the canonical graph data (entities, patches,
1481
+ * entityStates, syncMetadata, lists) into a bounded ring (default 50).
1482
+ * - `restoreGraphSnapshot(index)` writes a captured snapshot back into the live
1483
+ * Zustand store — the graph (and every subscriber/view) reverts to that state.
1484
+ * - `getTimeTravelState()` exposes the ring + current cursor for the devtools UI.
1485
+ *
1486
+ * Snapshots are deep-cloned on capture and on restore so restoring never aliases
1487
+ * ring contents into the live store (a later mutation must not corrupt history).
1488
+ *
1489
+ * This is debug-time machinery: it is intended to run behind the EntityExplorer,
1490
+ * not in production data paths.
1491
+ */
1492
+
1493
+ type GraphSlice = Pick<ReturnType<typeof useGraphStore.getState>, "entities" | "patches" | "entityStates" | "syncMetadata" | "lists">;
1494
+ interface TimeTravelSnapshot {
1495
+ /** Monotonic id (capture order). */
1496
+ seq: number;
1497
+ /** Capture timestamp (ms). */
1498
+ at: number;
1499
+ /** Optional label (e.g. the action that produced this state). */
1500
+ label?: string;
1501
+ data: GraphSlice;
1502
+ }
1503
+ interface TimeTravelState {
1504
+ snapshots: ReadonlyArray<Omit<TimeTravelSnapshot, "data">>;
1505
+ /** Index currently restored, or null if live (at head). */
1506
+ cursor: number | null;
1507
+ capacity: number;
1508
+ }
1509
+ /** Configure ring capacity (number of retained snapshots). */
1510
+ declare function configureTimeTravel(opts: {
1511
+ capacity?: number;
1512
+ }): void;
1513
+ /** Capture the current live graph state into the ring. Returns the snapshot seq. */
1514
+ declare function recordGraphSnapshot(label?: string): number;
1515
+ /**
1516
+ * Restore the live graph to the snapshot at ring `index` (0-based into the
1517
+ * current ring). The Zustand store is overwritten with a deep clone of the
1518
+ * captured slice, so all subscribers re-render at that historical state.
1519
+ * Returns true if restored.
1520
+ */
1521
+ declare function restoreGraphSnapshot(index: number): boolean;
1522
+ /** Restore by capture seq (stable across ring eviction within capacity). */
1523
+ declare function restoreGraphSnapshotBySeq(seq: number): boolean;
1524
+ /** Step the cursor by `delta` (negative = back in time) and restore. */
1525
+ declare function stepTimeTravel(delta: number): boolean;
1526
+ /** Snapshot metadata + cursor for the devtools UI (no heavy data payloads). */
1527
+ declare function getTimeTravelState(): TimeTravelState;
1528
+ /** Subscribe to ring/cursor changes (for useSyncExternalStore in the UI). */
1529
+ declare function subscribeTimeTravel(cb: () => void): () => void;
1530
+
1531
+ /**
1532
+ * Evaluate `FilterSpec` against one in-memory entity — mirrors remote semantics as closely as plain JS allows.
1533
+ * Use for **local** and **hybrid** `useEntityView` paths so UI filtering matches what users expect from the declarative spec.
1534
+ */
1535
+ declare function matchesFilter(entity: Record<string, unknown>, filter: FilterSpec): boolean;
1536
+ /**
1537
+ * Case-insensitive substring match across configured string fields; empty query is a no-op pass.
1538
+ * Keeps quick search consistent between client-only and debounced remote `q` params.
1539
+ */
1540
+ declare function matchesSearch(entity: Record<string, unknown>, query: string, fields: string[]): boolean;
1541
+ /**
1542
+ * Multi-key comparator implementing `SortSpec` (null ordering, optional custom comparators, locale-aware string fallback).
1543
+ * Shared by local sorting and binary insertion for realtime updates.
1544
+ */
1545
+ declare function compareEntities(a: Record<string, unknown>, b: Record<string, unknown>, sort: SortSpec): number;
1546
+ /**
1547
+ * Binary-search insertion index so realtime inserts land in sorted order without resorting the full id list (O(log n) per insert).
1548
+ * Skips missing `getEntity` rows by advancing low — callers should ensure entities exist or tolerate append-like behavior.
1549
+ */
1550
+ declare function findInsertionIndex(entity: Record<string, unknown>, sortedIds: string[], getEntity: (id: string) => Record<string, unknown> | null, sort: SortSpec): number;
1551
+ /**
1552
+ * Pure list projection: map ids → entities, drop missing, filter/sort/search, return **ids** in display order.
1553
+ * Bridges stored id lists with on-the-fly view descriptors without duplicating entity payloads.
1554
+ */
1555
+ declare function applyView(ids: string[], getEntity: (id: string) => Record<string, unknown> | null, filter?: FilterSpec | null, sort?: SortSpec | null, search?: {
1556
+ query: string;
1557
+ fields: string[];
1558
+ } | null): string[];
1559
+ /**
1560
+ * Heuristic for whether the graph likely holds **all** rows for a list key (enables local-only filtering/sorting in `useEntityView`).
1561
+ * `total` and `hasNextPage` come from list metadata written by fetchers.
1562
+ */
1563
+ declare function checkCompleteness(loadedCount: number, total: number | null, hasNextPage: boolean): {
1564
+ isComplete: boolean;
1565
+ reason: string;
1566
+ };
1567
+
1568
+ /**
1569
+ * view/incremental.ts — G3 parity: incremental view maintenance.
1570
+ *
1571
+ * The baseline `applyView` re-filters + re-sorts the entire id list on every
1572
+ * change — O(n) per update. For large lists this is the gap vs TanStack DB's
1573
+ * differential-dataflow live queries.
1574
+ *
1575
+ * This maintains a derived, sorted result and updates it for a SINGLE entity
1576
+ * change in O(log n): binary-search the entity's current position, decide
1577
+ * membership via the filter, and insert / remove / reposition. No full
1578
+ * re-derivation. Result is byte-identical to `applyView` for the same inputs
1579
+ * (correctness parity is asserted in tests).
1580
+ *
1581
+ * Design choice: a hand-rolled dirty-tracking index rather than adopting d2ts
1582
+ * (0.1.x, pre-production) — same acceptance bar (sub-linear single-row update),
1583
+ * far lower dependency risk. The differential engine can replace the internals
1584
+ * later behind this same class without an API change.
1585
+ */
1586
+
1587
+ interface IncrementalViewOptions {
1588
+ filter?: FilterSpec | null;
1589
+ sort?: SortSpec | null;
1590
+ search?: {
1591
+ query: string;
1592
+ fields: string[];
1593
+ } | null;
1594
+ /** Resolve an entity by id from the graph (or null if absent). */
1595
+ getEntity: (id: string) => Record<string, unknown> | null;
1596
+ }
1597
+ /**
1598
+ * A live, sorted view over a set of entity ids that updates incrementally.
1599
+ *
1600
+ * - `seed(ids)` builds the initial result (one O(n log n) pass).
1601
+ * - `applyChange(id)` updates the result for one changed/added/removed entity
1602
+ * in O(log n) comparisons (+ array splice).
1603
+ * - `result()` returns the current ordered id list (stable reference until a
1604
+ * change mutates it).
1605
+ */
1606
+ declare class IncrementalView {
1607
+ private readonly opts;
1608
+ private ids;
1609
+ private readonly members;
1610
+ constructor(opts: IncrementalViewOptions);
1611
+ /** Whether an entity qualifies for this view (filter + search). */
1612
+ private qualifies;
1613
+ /** Comparator over ids using the configured sort (stable, id tiebreak). */
1614
+ private cmp;
1615
+ /** Binary-search the insertion index for `id` among current `ids`. */
1616
+ private insertionIndex;
1617
+ /** Build the initial sorted result from a full id set. */
1618
+ seed(ids: string[]): void;
1619
+ /**
1620
+ * Update the view for ONE entity that changed/was added/removed.
1621
+ * Returns true if the result order changed.
1622
+ *
1623
+ * O(log n) to locate; the array splice is O(n) memory-move but performs no
1624
+ * filter/sort over the whole set — the differentiator from full re-derivation.
1625
+ */
1626
+ applyChange(id: string): boolean;
1627
+ /** Current ordered id list. */
1628
+ result(): string[];
1629
+ /** Current member count. */
1630
+ size(): number;
1631
+ }
1632
+
1633
+ /**
1634
+ * FK edge: this entity points at one parent row. Used to invalidate parent aggregates and optional list keys when the FK changes.
1635
+ */
1636
+ interface BelongsToRelation {
1637
+ cardinality: "belongsTo";
1638
+ foreignKey: string;
1639
+ targetType: EntityType;
1640
+ invalidateTargetLists?: string[];
1641
+ }
1642
+ /**
1643
+ * Inverse collection: children carry `foreignKey` pointing here; `listKeyPrefix` builds the child list query key for a given parent id.
1644
+ */
1645
+ interface HasManyRelation {
1646
+ cardinality: "hasMany";
1647
+ targetType: EntityType;
1648
+ foreignKey: string;
1649
+ listKeyPrefix: (parentId: EntityId) => unknown[];
1650
+ }
1651
+ /**
1652
+ * Join-style relation stored as an id array on this entity; invalidates partner lists derived via `listKeyPrefix` for each touched id.
1653
+ */
1654
+ interface ManyToManyRelation {
1655
+ cardinality: "manyToMany";
1656
+ targetType: EntityType;
1657
+ localArrayField?: string;
1658
+ listKeyPrefix: (thisId: EntityId) => unknown[];
1659
+ }
1660
+ type RelationDescriptor = BelongsToRelation | HasManyRelation | ManyToManyRelation;
1661
+ /**
1662
+ * Declarative relation metadata for one `EntityType`: optional named relations and list key prefixes to invalidate on any mutation.
1663
+ */
1664
+ interface EntitySchema {
1665
+ type: EntityType;
1666
+ relations?: Record<string, RelationDescriptor>;
1667
+ globalListKeys?: string[];
1668
+ }
1669
+ /**
1670
+ * Snapshot diff passed to cascade rules after CRUD: compare `previous` vs `next` to find FK moves, array membership changes, etc.
1671
+ */
1672
+ interface CascadeContext {
1673
+ type: EntityType;
1674
+ id: EntityId;
1675
+ previous: Record<string, unknown> | null;
1676
+ next: Record<string, unknown> | null;
1677
+ op: "create" | "update" | "delete";
1678
+ }
1679
+ /** Register or replace schema for `schema.type` (typically at app init). */
1680
+ declare function registerSchema(schema: EntitySchema): void;
1681
+ /** Lookup schema for cascade/join reads; returns null if unregistered. */
1682
+ declare function getSchema(type: EntityType): EntitySchema | null;
1683
+ /**
1684
+ * After a successful mutation, mark related entities/lists stale so hooks refetch without manually hunting query keys.
1685
+ * Traverses registered schemas (including reverse `hasMany`) so denormalized UIs stay eventually consistent with the graph.
1686
+ */
1687
+ declare function cascadeInvalidation(ctx: CascadeContext): void;
1688
+ /**
1689
+ * Resolve relation **placeholders** for detail panels: joins graph reads for belongs-to targets, has-many id lists, or many-to-many id arrays.
1690
+ * Returns plain objects suitable for rendering; does not mutate the graph.
1691
+ */
1692
+ declare function readRelations(type: EntityType, entity: Record<string, unknown>): Record<string, unknown>;
1693
+
1694
+ /**
1695
+ * agent/json-patch.ts — Minimal RFC-6902 JSON Patch applier.
1696
+ *
1697
+ * AG-UI `STATE_DELTA` events carry RFC-6902 operations. Rather than take a hard
1698
+ * dependency on `fast-json-patch` (last released 2022) or require consumers to
1699
+ * have `@ag-ui/client`'s applier, this implements the standard's six operations
1700
+ * directly. It is small, dependency-free, and fully unit-tested.
1701
+ *
1702
+ * Immutability: every op returns a NEW document; the input is never mutated.
1703
+ *
1704
+ * Supported ops: add, replace, remove, move, copy, test.
1705
+ * Pointers follow RFC-6901 (`/a/b/0`), including the `-` array-append token and
1706
+ * `~1`/`~0` escapes for `/` and `~`.
1707
+ */
1708
+ interface JsonPatchOp {
1709
+ op: "add" | "remove" | "replace" | "move" | "copy" | "test";
1710
+ path: string;
1711
+ /** For add/replace/test. */
1712
+ value?: unknown;
1713
+ /** For move/copy. */
1714
+ from?: string;
1715
+ }
1716
+ /**
1717
+ * Apply a sequence of RFC-6902 operations to `doc`, returning a new document.
1718
+ * A failing `test` op throws (per spec); structural ops are applied in order.
1719
+ */
1720
+ declare function applyJsonPatch<T = unknown>(doc: T, ops: readonly JsonPatchOp[]): T;
1721
+
1722
+ /**
1723
+ * agent/ag-ui-bridge.ts — Ingest AG-UI agent state into the entity graph.
1724
+ *
1725
+ * The headline agentic capability (change C2): an agent emits AG-UI state, it
1726
+ * lands in the entity graph, and every view reading those entities updates.
1727
+ *
1728
+ * AG-UI (https://docs.ag-ui.com) streams two state events:
1729
+ * - STATE_SNAPSHOT — the complete agent state object
1730
+ * - STATE_DELTA — a partial update as an RFC-6902 JSON Patch
1731
+ *
1732
+ * Our `patchEntity`/`upsertEntity` already align with this model: STATE_DELTA's
1733
+ * JSON Patch maps onto the graph, and writes route through the C1 MergeStrategy
1734
+ * so agent edits resolve against concurrent realtime/local writes.
1735
+ *
1736
+ * Mapping: the agent's state tree is projected onto entities via an
1737
+ * {@link AgUiStateMapping}. Each entry says "the object at this JSON-Pointer
1738
+ * prefix is an entity of `type`, keyed by `id` (a field of that object or a
1739
+ * constant)". This keeps the bridge transport-agnostic — it does not depend on
1740
+ * `@ag-ui/client` and takes plain event objects, so `@ag-ui/*` stays an OPTIONAL
1741
+ * peer (use it for typed events on the consumer side if desired).
1742
+ */
1743
+
1744
+ /** AG-UI STATE_SNAPSHOT event (minimal shape — no @ag-ui dependency). */
1745
+ interface AgUiStateSnapshotEvent {
1746
+ type?: "STATE_SNAPSHOT" | string;
1747
+ snapshot: Record<string, unknown>;
1748
+ }
1749
+ /** AG-UI STATE_DELTA event (minimal shape — RFC-6902 patch). */
1750
+ interface AgUiStateDeltaEvent {
1751
+ type?: "STATE_DELTA" | string;
1752
+ delta: JsonPatchOp[];
1753
+ }
1754
+ /** Maps one slice of agent state onto an entity type. */
1755
+ interface AgUiStateMapping {
1756
+ /** Entity type the mapped object(s) belong to. */
1757
+ entityType: string;
1758
+ /**
1759
+ * JSON-Pointer prefix into the agent state where this entity (or collection)
1760
+ * lives. `"/order"` → a single entity; `"/orders"` → a collection keyed by id.
1761
+ */
1762
+ pointer: string;
1763
+ /** Whether `pointer` addresses one object or a collection (array/record). */
1764
+ kind: "single" | "collection";
1765
+ /**
1766
+ * Field on each object that holds its entity id. For `single` you may instead
1767
+ * pass a constant id via `constantId`.
1768
+ */
1769
+ idField?: string;
1770
+ /** Constant id for `single` mappings whose object has no id field. */
1771
+ constantId?: string;
1772
+ /**
1773
+ * How to write: `upsert` (merge via MergeStrategy — default) or `replace`
1774
+ * (full overwrite). Snapshots typically replace; deltas typically upsert.
1775
+ */
1776
+ write?: "upsert" | "replace";
1777
+ }
1778
+ interface ApplyAgUiOptions {
1779
+ mappings: AgUiStateMapping[];
1780
+ }
1781
+ /**
1782
+ * Apply an AG-UI STATE_SNAPSHOT to the graph. Each mapped slice is written as an
1783
+ * entity. Snapshots default to `replace` semantics when the mapping does not
1784
+ * specify otherwise is left to the mapping's `write` field (default upsert).
1785
+ *
1786
+ * @example
1787
+ * ```ts
1788
+ * applyAgUiSnapshot(event, { mappings: [
1789
+ * { entityType: "Order", pointer: "/order", kind: "single", write: "replace" },
1790
+ * { entityType: "LineItem", pointer: "/order/items", kind: "collection" },
1791
+ * ]});
1792
+ * ```
1793
+ */
1794
+ declare function applyAgUiSnapshot(event: AgUiStateSnapshotEvent, options: ApplyAgUiOptions): void;
1795
+ /**
1796
+ * Apply an AG-UI STATE_DELTA (RFC-6902 JSON Patch) to the graph.
1797
+ *
1798
+ * The delta is applied against a reconstructed agent-state view assembled from
1799
+ * the current graph entities (per the mappings), then the resulting slices are
1800
+ * re-projected back into the graph. This lets path-based deltas target nested
1801
+ * entity fields without the caller tracking the full agent state themselves.
1802
+ */
1803
+ declare function applyAgUiDelta(event: AgUiStateDeltaEvent, options: ApplyAgUiOptions,
1804
+ /** Current agent-state view; if omitted, reconstructed from the graph mappings. */
1805
+ currentState?: Record<string, unknown>): void;
1806
+
1807
+ /**
1808
+ * adapters/types.ts
1809
+ *
1810
+ * Common contract every data-source adapter implements.
1811
+ * The entity graph doesn't care whether data comes from REST, GraphQL,
1812
+ * WebSocket, Supabase, Convex, PGlite shape sync — they all speak this
1813
+ * interface and write into the same graph.
1814
+ */
1815
+
1816
+ type ChangeOperation = "insert" | "update" | "delete" | "upsert";
1817
+ interface EntityChange<T = Record<string, unknown>> {
1818
+ op: ChangeOperation;
1819
+ type: EntityType;
1820
+ id: EntityId;
1821
+ data?: T;
1822
+ patch?: Partial<T>;
1823
+ }
1824
+ interface ChangeSet<T = Record<string, unknown>> {
1825
+ changes: EntityChange<T>[];
1826
+ affectedListKeys?: string[];
1827
+ timestamp?: string;
1828
+ }
1829
+ type UnsubscribeFn = () => void;
1830
+ interface SubscriptionConfig {
1831
+ label?: string;
1832
+ replayOnConnect?: boolean;
1833
+ }
1834
+ interface RealtimeAdapter {
1835
+ readonly name: string;
1836
+ subscribe(config: SubscriptionConfig, handler: (changeset: ChangeSet) => void): UnsubscribeFn;
1837
+ onStatusChange?: (cb: (status: AdapterStatus) => void) => UnsubscribeFn;
1838
+ }
1839
+ type AdapterStatus = "connecting" | "connected" | "disconnected" | "error";
1840
+ interface SyncQueryResult<T> {
1841
+ rows: T[];
1842
+ total?: number;
1843
+ }
1844
+ interface SyncAdapter extends RealtimeAdapter {
1845
+ query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<SyncQueryResult<T>>;
1846
+ execute(sql: string, params?: unknown[]): Promise<void>;
1847
+ isSynced(): boolean;
1848
+ onSyncComplete(cb: () => void): UnsubscribeFn;
1849
+ }
1850
+ interface ChannelConfig {
1851
+ type: EntityType;
1852
+ filter?: Record<string, unknown>;
1853
+ id?: EntityId;
1854
+ operations?: ChangeOperation[];
1855
+ }
1856
+
1857
+ interface ManagerOptions {
1858
+ flushInterval?: number;
1859
+ onStatusChange?: (adapter: string, status: AdapterStatus) => void;
1860
+ onChangeReceived?: (adapter: string, change: EntityChange) => void;
1861
+ }
1862
+ declare class RealtimeManager {
1863
+ private adapters;
1864
+ private pendingChanges;
1865
+ private pendingListKeys;
1866
+ private flushTimer;
1867
+ private opts;
1868
+ constructor(opts?: ManagerOptions);
1869
+ register(adapter: RealtimeAdapter, channels: ChannelConfig[], normalize?: (raw: unknown) => EntityChange | null): UnsubscribeFn;
1870
+ unregister(name: string): void;
1871
+ unregisterAll(): void;
1872
+ private handleChangeset;
1873
+ private scheduleFlush;
1874
+ flush(): void;
1875
+ forceFlush(): void;
1876
+ }
1877
+ declare function getRealtimeManager(opts?: ManagerOptions): RealtimeManager;
1878
+ declare function resetRealtimeManager(): void;
1879
+
1880
+ /**
1881
+ * adapters/realtime-adapters.ts
1882
+ *
1883
+ * WebSocket, Supabase Realtime, Convex, and GraphQL subscription adapters.
1884
+ * All implement RealtimeAdapter and route through RealtimeManager → entity graph.
1885
+ */
1886
+
1887
+ interface WebSocketAdapterOptions {
1888
+ url: string | (() => string);
1889
+ parseMessage?: (data: unknown) => EntityChange[] | null;
1890
+ protocols?: string | string[];
1891
+ reconnectBaseDelay?: number;
1892
+ maxReconnectAttempts?: number;
1893
+ pingInterval?: number;
1894
+ pingMessage?: string;
1895
+ }
1896
+ declare function createWebSocketAdapter(opts: WebSocketAdapterOptions): RealtimeAdapter;
1897
+ interface SupabaseClient {
1898
+ channel(name: string): SupabaseChannel;
1899
+ }
1900
+ interface SupabaseChannel {
1901
+ on(event: "postgres_changes", config: {
1902
+ event: "*" | "INSERT" | "UPDATE" | "DELETE";
1903
+ schema: string;
1904
+ table: string;
1905
+ filter?: string;
1906
+ }, handler: (payload: SupabasePayload) => void): SupabaseChannel;
1907
+ subscribe(cb?: (status: string) => void): SupabaseChannel;
1908
+ unsubscribe(): Promise<void>;
1909
+ }
1910
+ interface SupabasePayload {
1911
+ eventType: "INSERT" | "UPDATE" | "DELETE";
1912
+ new: Record<string, unknown>;
1913
+ old: Record<string, unknown>;
1914
+ table: string;
1915
+ }
1916
+ interface SupabaseAdapterOptions {
1917
+ tableTypeMap?: Record<string, string>;
1918
+ extractId?: (record: Record<string, unknown>) => string;
1919
+ schema?: string;
1920
+ }
1921
+ declare function createSupabaseRealtimeAdapter(client: SupabaseClient, opts?: SupabaseAdapterOptions): RealtimeAdapter & {
1922
+ subscribeChannel: (config: ChannelConfig & {
1923
+ _handler?: (cs: ChangeSet) => void;
1924
+ }) => UnsubscribeFn;
1925
+ };
1926
+ interface ConvexClient {
1927
+ onUpdate<T>(query: unknown, args: Record<string, unknown>, handler: (result: T) => void): UnsubscribeFn;
1928
+ }
1929
+ interface ConvexChannelConfig<T extends object> {
1930
+ type: string;
1931
+ query: unknown;
1932
+ args?: Record<string, unknown>;
1933
+ extractId?: (record: T) => string;
1934
+ normalize?: (record: T) => Record<string, unknown>;
1935
+ }
1936
+ declare function createConvexAdapter<T extends object>(opts: {
1937
+ client: ConvexClient;
1938
+ channels: ConvexChannelConfig<T>[];
1939
+ }): RealtimeAdapter;
1940
+ interface GQLWsClient {
1941
+ subscribe<T>(payload: {
1942
+ query: string;
1943
+ variables?: Record<string, unknown>;
1944
+ }, sink: {
1945
+ next: (value: {
1946
+ data: T;
1947
+ }) => void;
1948
+ error: (err: unknown) => void;
1949
+ complete: () => void;
1950
+ }): UnsubscribeFn;
1951
+ }
1952
+ interface GQLSubscriptionConfig<T extends object> {
1953
+ type: string;
1954
+ document: string;
1955
+ variables?: Record<string, unknown>;
1956
+ getPayload: (data: T) => GQLPayload | GQLPayload[] | null;
1957
+ }
1958
+ interface GQLPayload {
1959
+ type?: string;
1960
+ node?: Record<string, unknown>;
1961
+ id?: string;
1962
+ }
1963
+ declare function createGraphQLSubscriptionAdapter<T extends object>(opts: {
1964
+ client: GQLWsClient;
1965
+ subscriptions: GQLSubscriptionConfig<T>[];
1966
+ extractId?: (node: Record<string, unknown>, type: string) => string;
1967
+ normalize?: (node: Record<string, unknown>, type: string) => Record<string, unknown>;
1968
+ }): RealtimeAdapter;
1969
+
1970
+ /**
1971
+ * adapters/surreal-live.ts
1972
+ *
1973
+ * SurrealDB LIVE SELECT realtime adapter.
1974
+ *
1975
+ * SurrealDB live stream → per-channel state machine → RealtimeAdapter contract
1976
+ * one ChannelConfig = one LIVE SELECT = one independent reconnect/replay state
1977
+ *
1978
+ * Implements the RealtimeAdapter contract (NOT SyncAdapter) per the corrected
1979
+ * spec at openspec/specs/entity-surreal-live-adapter/spec.md. The consumer
1980
+ * owns the SurrealDB client; the adapter only orchestrates live queries.
1981
+ */
1982
+
1983
+ interface SurrealLike {
1984
+ query<T = unknown>(sql: string, vars?: Record<string, unknown>): Promise<T[]>;
1985
+ live<T = unknown>(table: string, callback: (action: SurrealLiveAction<T>) => void, diff?: boolean): Promise<string>;
1986
+ kill(uuid: string): Promise<void>;
1987
+ }
1988
+ interface SurrealLiveAction<T = unknown> {
1989
+ action: "CREATE" | "UPDATE" | "DELETE" | "CLOSE" | string;
1990
+ result: T;
1991
+ }
1992
+ interface SurrealCheckpointStore {
1993
+ get(channelKey: string): Promise<string | undefined>;
1994
+ set(channelKey: string, value: string): Promise<void>;
1995
+ }
1996
+ interface SurrealTableConfig {
1997
+ type: EntityType;
1998
+ /** SurrealDB table name; defaults to `type`. */
1999
+ table?: string;
2000
+ /** Field in the row that carries the entity id; defaults to "id". */
2001
+ idField?: string;
2002
+ }
2003
+ interface SurrealLiveAdapterOptions {
2004
+ name?: string;
2005
+ surreal: SurrealLike;
2006
+ tables: SurrealTableConfig[];
2007
+ initialQueryStrategy?: "select-then-live" | "live-only";
2008
+ initialDelayMs?: number;
2009
+ maxDelayMs?: number;
2010
+ connectedSettleMs?: number;
2011
+ checkpointStore?: SurrealCheckpointStore;
2012
+ checkpointField?: string;
2013
+ listKeyResolver?: (change: EntityChange) => string[] | undefined;
2014
+ permanentErrorPatterns?: RegExp[];
2015
+ }
2016
+ declare function createSurrealLiveAdapter(opts: SurrealLiveAdapterOptions): RealtimeAdapter;
2017
+
2018
+ /**
2019
+ * adapters/flint.ts — Flint Realtime Fabric adapter.
2020
+ *
2021
+ * Flint frf-entity-management `watchEntities()` AsyncIterable
2022
+ * → our ChangeSet stream → RealtimeManager → entity graph
2023
+ *
2024
+ * GAP-1 is a BRIDGE, not a from-scratch build: the Flint repo already ships
2025
+ * `@prometheusags/frf-entity-management` whose `RealtimeAdapter.watchEntities()`
2026
+ * yields a plain `EntityEvent` (it JSON-decodes the spine envelope — no proto
2027
+ * types leak). We consume that entity-shaped facade behind a minimal-surface
2028
+ * type, so:
2029
+ * - The unfrozen `proto-v1` risk is contained BELOW this seam.
2030
+ * - `@prometheusags/frf-sdk` stays an OPTIONAL peer; this module imports
2031
+ * nothing from it. The consumer constructs the Flint client and passes it in.
2032
+ *
2033
+ * `EntityEvent.offset` (bigint) is forwarded to an optional checkpoint store so
2034
+ * reconnects resume from the last seen offset (mirrors the surreal-live adapter).
2035
+ */
2036
+
2037
+ /** A decoded entity change event received from the Flint spine. */
2038
+ interface FlintEntityEvent {
2039
+ entityType: string;
2040
+ entityId: string;
2041
+ tenantId: string;
2042
+ channelId: string;
2043
+ data: Record<string, unknown>;
2044
+ offset: bigint;
2045
+ correlationId?: string;
2046
+ }
2047
+ /** Filter criteria for a Flint entity subscription. */
2048
+ interface FlintEntityQuery {
2049
+ channelId: string;
2050
+ consumerId: string;
2051
+ entityType?: string;
2052
+ fromOffset?: bigint;
2053
+ }
2054
+ /** An entity mutation to publish on the Flint spine. */
2055
+ interface FlintEntityRecord {
2056
+ entityType: string;
2057
+ entityId: string;
2058
+ tenantId: string;
2059
+ channelId: string;
2060
+ data: Record<string, unknown>;
2061
+ correlationId?: string;
2062
+ }
2063
+ /** Minimal surface of the Flint `RealtimeAdapter` we consume. */
2064
+ interface FlintClientLike {
2065
+ watchEntities(query: FlintEntityQuery): AsyncIterable<FlintEntityEvent>;
2066
+ mutateEntity(record: FlintEntityRecord): Promise<void>;
2067
+ }
2068
+ /** Optional resume-offset persistence (per channel+consumer). */
2069
+ interface FlintCheckpointStore {
2070
+ get(key: string): Promise<bigint | undefined> | bigint | undefined;
2071
+ set(key: string, offset: bigint): Promise<void> | void;
2072
+ }
2073
+ interface CreateFlintAdapterOptions {
2074
+ name?: string;
2075
+ /** The Flint entity-management client (frf-entity-management RealtimeAdapter). */
2076
+ client: FlintClientLike;
2077
+ /** Channel to subscribe/publish on. */
2078
+ channelId: string;
2079
+ /** Consumer id for this subscription. */
2080
+ consumerId: string;
2081
+ /** Restrict to one entity type (optional). */
2082
+ entityType?: string;
2083
+ /** Resume-offset store; reconnects start `fromOffset = lastSeen + 1`. */
2084
+ checkpoints?: FlintCheckpointStore;
2085
+ /** Map a Flint event to a change operation. Defaults to "upsert". */
2086
+ resolveOp?: (event: FlintEntityEvent) => ChangeOperation;
2087
+ }
2088
+ /**
2089
+ * Build a {@link RealtimeAdapter} backed by the Flint Realtime Fabric.
2090
+ *
2091
+ * The consumer owns the Flint client (constructed from `@prometheusags/frf-sdk`
2092
+ * / `frf-entity-management`); this adapter only orchestrates the stream.
2093
+ *
2094
+ * @example
2095
+ * ```ts
2096
+ * import { RealtimeAdapter as FrfAdapter } from "@prometheusags/frf-entity-management";
2097
+ * import { getRealtimeManager, createFlintAdapter } from "@prometheus-ags/prometheus-entity-management";
2098
+ *
2099
+ * const flint = new FrfAdapter(spineClient);
2100
+ * const adapter = createFlintAdapter({ client: flint, channelId: "tenant-1", consumerId: "web-1" });
2101
+ * getRealtimeManager().register(adapter);
2102
+ * ```
2103
+ */
2104
+ declare function createFlintAdapter(options: CreateFlintAdapterOptions): RealtimeAdapter;
2105
+ /**
2106
+ * Publish an entity mutation to the Flint spine via the client.
2107
+ * Thin pass-through kept here so consumers import one Flint surface from us.
2108
+ */
2109
+ declare function publishFlintMutation(client: FlintClientLike, record: FlintEntityRecord): Promise<void>;
2110
+
2111
+ /**
2112
+ * adapters/electricsql.ts (core)
2113
+ *
2114
+ * ElectricSQL + PGlite local-first adapter — the React-free base.
2115
+ *
2116
+ * Server Postgres → ElectricSQL shape sync (SSE) → PGlite (local WASM Postgres)
2117
+ * ↓ NOTIFY
2118
+ * Entity Graph (Zustand)
2119
+ *
2120
+ * React hooks (`useLocalFirst`, `usePGliteQuery`) live in the binding package
2121
+ * (`@prometheus-ags/entity-graph-react`), built on this adapter.
2122
+ */
2123
+
2124
+ interface PGlite$1 {
2125
+ query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<{
2126
+ rows: T[];
2127
+ }>;
2128
+ exec(sql: string): Promise<unknown>;
2129
+ listen(channel: string, handler: (payload: string) => void): Promise<() => void>;
2130
+ }
2131
+ interface ShapeMessage$1<T = Record<string, unknown>> {
2132
+ headers: {
2133
+ operation: "insert" | "update" | "delete";
2134
+ };
2135
+ offset: string;
2136
+ value: T;
2137
+ key: string;
2138
+ }
2139
+ interface ShapeStream$1<T = Record<string, unknown>> {
2140
+ subscribe(onMsg: (msgs: ShapeMessage$1<T>[]) => void, onErr?: (e: Error) => void): () => void;
2141
+ isUpToDate: boolean;
2142
+ lastOffset: string;
2143
+ }
2144
+ interface ElectricTableConfig<T extends object> {
2145
+ type: EntityType;
2146
+ table: string;
2147
+ where?: string;
2148
+ idColumn?: string;
2149
+ normalize?: (row: T) => Record<string, unknown>;
2150
+ shapeStream: ShapeStream$1<T>;
2151
+ }
2152
+ interface ElectricAdapterOptions {
2153
+ pglite: PGlite$1;
2154
+ tables: ElectricTableConfig<Record<string, unknown>>[];
2155
+ onSynced?: () => void;
2156
+ }
2157
+
2158
+ declare function createElectricAdapter(opts: ElectricAdapterOptions): SyncAdapter;
2159
+
2160
+ /**
2161
+ * adapters/electricsql-tenant.ts
2162
+ *
2163
+ * Tenant-scoped Electric adapter — the safety primitive that enforces
2164
+ * **RULE 5 — Shape predicates ⊆ RLS** (see hotseaters constraints).
2165
+ *
2166
+ * PGlite has no row-level security. The only way to guarantee a client
2167
+ * never sees cross-tenant data through Electric is to refuse to attach
2168
+ * any shape that lacks a tenant predicate. This wrapper does exactly that:
2169
+ *
2170
+ * - Validates the tenant claim (currently `{ companyId }`) is a UUID.
2171
+ * - Refuses to attach a shape whose `tenantColumn` is `undefined`. We
2172
+ * accept explicit `null` for the company root itself (filtered by `id`).
2173
+ * - Builds the `where` clause from `tenantColumn` and the validated
2174
+ * companyId so individual shape factories cannot drift from RLS.
2175
+ * - Delegates to the existing `createElectricAdapter` for the actual
2176
+ * sync wiring.
2177
+ *
2178
+ * This also fulfils Change 13 item 11 (auth-claim-aware shape registration):
2179
+ * the `tenantClaim` is the typed seam where authn meets shape registration.
2180
+ *
2181
+ * Self-hosted Supabase only. Local stack and `https://electricsql.prometheusags.ai`
2182
+ * are the only acceptable Electric endpoints — this module does not
2183
+ * hard-code either; it only governs the predicate.
2184
+ */
2185
+
2186
+ interface PGlite {
2187
+ query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<{
2188
+ rows: T[];
2189
+ }>;
2190
+ exec(sql: string): Promise<unknown>;
2191
+ listen(channel: string, handler: (payload: string) => void): Promise<() => void>;
2192
+ }
2193
+ interface ShapeMessage<T = Record<string, unknown>> {
2194
+ headers: {
2195
+ operation: "insert" | "update" | "delete";
2196
+ };
2197
+ offset: string;
2198
+ value: T;
2199
+ key: string;
2200
+ }
2201
+ interface ShapeStream<T = Record<string, unknown>> {
2202
+ subscribe(onMsg: (msgs: ShapeMessage<T>[]) => void, onErr?: (e: Error) => void): () => void;
2203
+ isUpToDate: boolean;
2204
+ lastOffset: string;
2205
+ }
2206
+ /** Auth claim required to register tenant-scoped shapes. */
2207
+ interface TenantClaim {
2208
+ companyId: string;
2209
+ }
2210
+ interface TenantScopedTableConfig<T extends object = Record<string, unknown>> {
2211
+ type: EntityType;
2212
+ table: string;
2213
+ /**
2214
+ * Column used for tenant filtering.
2215
+ *
2216
+ * - A string (`"company_id"`) yields `WHERE company_id = '<companyId>'`.
2217
+ * - Explicit `null` means the table IS the tenant root and is filtered by
2218
+ * `id = '<companyId>'`. Only the `company` table qualifies.
2219
+ * - `undefined` is **rejected** — this is the safety gate.
2220
+ */
2221
+ tenantColumn: string | null;
2222
+ primaryKey?: string[];
2223
+ /**
2224
+ * Factory invoked with the constructed `where` clause and the tenant claim.
2225
+ * Consumers wire their preferred Electric `ShapeStream` builder here. The
2226
+ * factory must use the supplied `where` verbatim (no widening).
2227
+ */
2228
+ shapeStreamFactory: (params: {
2229
+ table: string;
2230
+ where: string;
2231
+ tenantClaim: TenantClaim;
2232
+ }) => ShapeStream<T>;
2233
+ normalize?: (row: T) => Record<string, unknown>;
2234
+ }
2235
+ interface TenantScopedAdapterOptions {
2236
+ pglite: PGlite;
2237
+ tenantClaim: TenantClaim;
2238
+ tables: TenantScopedTableConfig[];
2239
+ onSynced?: () => void;
2240
+ }
2241
+ /**
2242
+ * Build the `where` clause for a single shape. Exposed for tests.
2243
+ *
2244
+ * - `tenantColumn === null` → `id = '<companyId>'` (company-root case)
2245
+ * - `tenantColumn === string` → `<tenantColumn> = '<companyId>'`
2246
+ *
2247
+ * Throws if `tenantColumn` is `undefined` or `companyId` is not a UUID.
2248
+ */
2249
+ declare function buildTenantWhere(tenantColumn: string | null | undefined, companyId: string, tableLabel: string): string;
2250
+ /**
2251
+ * Build a tenant-scoped {@link SyncAdapter}.
2252
+ *
2253
+ * For each table in `tables`:
2254
+ * 1. Validates `tenantColumn` is defined (string or explicit null).
2255
+ * 2. Validates `companyId` is a UUID.
2256
+ * 3. Builds the predicate and calls `shapeStreamFactory(...)` to get the
2257
+ * actual `ShapeStream`.
2258
+ * 4. Hands the resulting list off to `createElectricAdapter` for wiring.
2259
+ *
2260
+ * @throws if any table lacks `tenantColumn`, or the company id is not a UUID.
2261
+ */
2262
+ declare function createTenantScopedElectricAdapter(opts: TenantScopedAdapterOptions): SyncAdapter;
2263
+
2264
+ /**
2265
+ * adapters/pglite-persistence.ts
2266
+ *
2267
+ * PGlite-backed implementation of {@link GraphPersistenceAdapter}.
2268
+ *
2269
+ * Stores the local-first runtime's graph snapshot inside a PGlite table
2270
+ * (`_graph_snapshot` by default) instead of `localStorage`/`IndexedDB`.
2271
+ *
2272
+ * Why this exists:
2273
+ * - The app already has a PGlite handle for ElectricSQL sync.
2274
+ * - Putting the graph snapshot alongside synced data means one storage
2275
+ * surface to back up, clear, and reason about.
2276
+ * - Works in Tauri WebView + Node (PGlite is a WASM Postgres) without
2277
+ * pulling in browser-only storage.
2278
+ *
2279
+ * Contract:
2280
+ * - `get(key)` -> returns the stored string or null
2281
+ * - `set(key, v)` -> upserts (insert-or-replace), stamping `updated_at`
2282
+ * - `remove(key)` -> deletes the row (no-op if absent)
2283
+ *
2284
+ * The function lazily ensures the storage table exists on first call.
2285
+ * That `CREATE TABLE IF NOT EXISTS` is idempotent so it's safe to retry.
2286
+ *
2287
+ * Self-hosted Supabase only. HotSeatersMVP is the bible. This module does
2288
+ * not import `@electric-sql/pglite` — consumers pass a PGlite-shaped handle.
2289
+ */
2290
+
2291
+ interface PGlitePersistenceClient {
2292
+ query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<{
2293
+ rows: T[];
2294
+ }>;
2295
+ exec(sql: string): Promise<unknown>;
2296
+ }
2297
+ interface CreatePGlitePersistenceAdapterOptions {
2298
+ /** Name of the snapshot table. Defaults to `_graph_snapshot`. */
2299
+ tableName?: string;
2300
+ }
2301
+ /**
2302
+ * Returns a {@link GraphPersistenceAdapter} that reads/writes the graph
2303
+ * snapshot through a PGlite-shaped client.
2304
+ *
2305
+ * @example
2306
+ * ```ts
2307
+ * import { PGlite } from "@electric-sql/pglite";
2308
+ * import { startLocalFirstGraph, createPGlitePersistenceAdapter } from "@prometheus-ags/prometheus-entity-management";
2309
+ *
2310
+ * const pglite = await PGlite.create("idb://hotseaters");
2311
+ * const storage = await createPGlitePersistenceAdapter(pglite);
2312
+ * const runtime = startLocalFirstGraph({ storage, key: "hotseaters:graph" });
2313
+ * ```
2314
+ */
2315
+ declare function createPGlitePersistenceAdapter(pglite: PGlitePersistenceClient, options?: CreatePGlitePersistenceAdapterOptions): Promise<GraphPersistenceAdapter>;
2316
+
2317
+ /**
2318
+ * adapters/tauri-sql-persistence.ts
2319
+ *
2320
+ * SQLite-backed implementation of {@link GraphPersistenceAdapter} for Tauri
2321
+ * desktop, via the official `@tauri-apps/plugin-sql` `Database` handle.
2322
+ *
2323
+ * Why this exists:
2324
+ * - On Tauri desktop the natural on-device store is SQLite, not PGlite-wasm.
2325
+ * - This pairs with `createPGlitePersistenceAdapter` (browser/wasm) so the
2326
+ * same local-first runtime persists across web and desktop unchanged.
2327
+ *
2328
+ * The consumer owns the Tauri `Database` handle (from `Database.load(...)`).
2329
+ * This module does NOT import `@tauri-apps/plugin-sql` — it takes a
2330
+ * minimal-surface client, mirroring the PGlite/Electric adapter pattern, so it
2331
+ * adds no hard dependency. `@tauri-apps/plugin-sql` is an optional peer.
2332
+ *
2333
+ * Contract (same as every GraphPersistenceAdapter):
2334
+ * - `get(key)` -> stored string or null
2335
+ * - `set(key, v)` -> upsert (INSERT ... ON CONFLICT)
2336
+ * - `remove(key)` -> delete (no-op if absent)
2337
+ *
2338
+ * Tauri SQL uses SQLite `?`-positional bind params (not `$1`).
2339
+ */
2340
+
2341
+ /**
2342
+ * Minimal surface of a `@tauri-apps/plugin-sql` `Database` handle.
2343
+ * Avoids a hard dep; matches the loaded-Database shape (`execute` + `select`).
2344
+ */
2345
+ interface TauriSqlClient {
2346
+ execute(query: string, bindValues?: unknown[]): Promise<unknown>;
2347
+ select<T = Record<string, unknown>>(query: string, bindValues?: unknown[]): Promise<T[]>;
2348
+ }
2349
+ interface CreateTauriSqlPersistenceAdapterOptions {
2350
+ /** Name of the snapshot table. Defaults to `_graph_snapshot`. */
2351
+ tableName?: string;
2352
+ }
2353
+ /**
2354
+ * Returns a {@link GraphPersistenceAdapter} backed by a Tauri SQLite `Database`.
2355
+ *
2356
+ * @example
2357
+ * ```ts
2358
+ * import Database from "@tauri-apps/plugin-sql";
2359
+ * import { startLocalFirstGraph, createTauriSqlPersistenceAdapter } from "@prometheus-ags/prometheus-entity-management";
2360
+ *
2361
+ * const db = await Database.load("sqlite:app.db");
2362
+ * const storage = await createTauriSqlPersistenceAdapter(db);
2363
+ * const runtime = startLocalFirstGraph({ storage, key: "app:graph" });
2364
+ * ```
2365
+ */
2366
+ declare function createTauriSqlPersistenceAdapter(db: TauriSqlClient, options?: CreateTauriSqlPersistenceAdapterOptions): Promise<GraphPersistenceAdapter>;
2367
+
2368
+ /**
2369
+ * schema-from-sql.ts
2370
+ *
2371
+ * Generates a JSON Schema (and registers it via `registerEntityJsonSchema`)
2372
+ * directly from a Postgres `CREATE TABLE` statement. Lets a consumer keep
2373
+ * `latest-data/supabase/migrations/*.sql` as the single source of truth for
2374
+ * column shapes — no hand-maintained TypeScript schema duplicates.
2375
+ *
2376
+ * Deliberately regex-based: a real SQL parser is overkill for the slice of
2377
+ * DDL we care about, and would add a non-trivial runtime dep.
2378
+ *
2379
+ * Type mapping
2380
+ * ------------
2381
+ * UUID, TEXT, VARCHAR(*) -> { type: "string" }
2382
+ * INTEGER, INT, BIGINT, SMALLINT -> { type: "integer" }
2383
+ * NUMERIC(*), DECIMAL(*) -> { type: "number" }
2384
+ * BOOLEAN, BOOL -> { type: "boolean" }
2385
+ * TIMESTAMPTZ, TIMESTAMP -> { type: "string", format: "date-time" }
2386
+ * DATE -> { type: "string", format: "date" }
2387
+ * JSONB, JSON -> { type: "object" }
2388
+ * TEXT[] -> { type: "array", items: { type: "string" } }
2389
+ * anything else -> { type: "string" } + "x-warning"
2390
+ *
2391
+ * `required` is populated from `NOT NULL` columns that lack a `DEFAULT`.
2392
+ * Callers can pass `overrides` to deep-merge a partial config (typically to
2393
+ * narrow a JSONB column to an array, or to add `enum`/`format` hints).
2394
+ */
2395
+
2396
+ interface RegisterEntityFromSqlOptions {
2397
+ entityType: string;
2398
+ /** A single `CREATE TABLE` statement (extra DDL around it is ignored). */
2399
+ createTableSql: string;
2400
+ /** Optional deep-merge overrides (overrides win). */
2401
+ overrides?: Partial<EntityJsonSchemaConfig>;
2402
+ }
2403
+ interface ParsedColumn {
2404
+ name: string;
2405
+ sqlType: string;
2406
+ notNull: boolean;
2407
+ hasDefault: boolean;
2408
+ }
2409
+ interface ParsedTable {
2410
+ tableName: string;
2411
+ columns: ParsedColumn[];
2412
+ }
2413
+ /**
2414
+ * Parse a `CREATE TABLE` block. Exposed for tests and for callers who need
2415
+ * the intermediate representation.
2416
+ */
2417
+ declare function parseCreateTable(sql: string): ParsedTable;
2418
+ /**
2419
+ * Map a normalized (uppercased, whitespace-stripped) SQL type to a JSON
2420
+ * schema fragment.
2421
+ *
2422
+ * Returns `{ schema, warning }` so the caller can surface "unmapped type"
2423
+ * notes via `x-warning` on the property.
2424
+ */
2425
+ declare function sqlTypeToJsonSchema(sqlType: string): {
2426
+ schema: JsonSchemaObject;
2427
+ warning?: string;
2428
+ };
2429
+ /**
2430
+ * Build (and register) an `EntityJsonSchemaConfig` from a CREATE TABLE
2431
+ * statement. Returns the registered config so callers can introspect it.
2432
+ */
2433
+ declare function registerEntityFromSql(opts: RegisterEntityFromSqlOptions): EntityJsonSchemaConfig;
2434
+
2435
+ /**
2436
+ * table/types.ts
2437
+ *
2438
+ * Pure table engine type definitions — zero external dependencies.
2439
+ * Structurally compatible with TanStack Table v8 for easy migration,
2440
+ * but fully self-contained within the prometheus-entity-management library.
2441
+ */
2442
+
2443
+ type ViewMode = "table" | "gallery" | "list";
2444
+ type Updater<T> = T | ((prev: T) => T);
2445
+ type AccessorFn<TData, TValue = unknown> = (row: TData, index: number) => TValue;
2446
+ type HeaderContext<TData> = {
2447
+ table: TableInstance<TData>;
2448
+ header: Header<TData>;
2449
+ column: Column<TData>;
2450
+ };
2451
+ type CellContext<TData> = {
2452
+ table: TableInstance<TData>;
2453
+ row: Row<TData>;
2454
+ cell: Cell<TData>;
2455
+ column: Column<TData>;
2456
+ getValue: <T = unknown>() => T;
2457
+ renderValue: <T = unknown>() => T | null;
2458
+ };
2459
+ type HeaderRenderer<TData> = string | ((context: HeaderContext<TData>) => React.ReactNode);
2460
+ type CellRenderer<TData> = string | ((context: CellContext<TData>) => React.ReactNode);
2461
+ type FilterFn<TData> = (row: Row<TData>, columnId: string, filterValue: unknown) => boolean;
2462
+ type SortingFn<TData> = (rowA: Row<TData>, rowB: Row<TData>, columnId: string) => number;
2463
+ type AggregationFn<TData> = (columnId: string, leafRows: Row<TData>[], childRows: Row<TData>[]) => unknown;
2464
+ interface ColumnMeta<TData = unknown> {
2465
+ entityMeta?: {
2466
+ field: keyof TData;
2467
+ filterType: "text" | "number" | "date" | "dateRange" | "boolean" | "enum" | "relation" | "none";
2468
+ enumOptions?: Array<{
2469
+ label: string;
2470
+ value: string;
2471
+ color?: string;
2472
+ }>;
2473
+ relationEntityType?: string;
2474
+ editable?: boolean;
2475
+ hideable?: boolean;
2476
+ };
2477
+ [key: string]: unknown;
2478
+ }
2479
+ interface ColumnDef<TData, TValue = unknown> {
2480
+ id?: string;
2481
+ accessorKey?: keyof TData & string;
2482
+ accessorFn?: AccessorFn<TData, TValue>;
2483
+ header?: HeaderRenderer<TData>;
2484
+ cell?: CellRenderer<TData>;
2485
+ footer?: HeaderRenderer<TData>;
2486
+ size?: number;
2487
+ minSize?: number;
2488
+ maxSize?: number;
2489
+ enableSorting?: boolean;
2490
+ enableFiltering?: boolean;
2491
+ enableHiding?: boolean;
2492
+ enablePinning?: boolean;
2493
+ enableGrouping?: boolean;
2494
+ enableResizing?: boolean;
2495
+ filterFn?: FilterFn<TData> | "auto";
2496
+ sortingFn?: SortingFn<TData> | "auto";
2497
+ aggregationFn?: AggregationFn<TData> | "auto" | "sum" | "min" | "max" | "count" | "mean" | "median" | "unique";
2498
+ sortDescFirst?: boolean;
2499
+ sortUndefined?: "first" | "last" | false;
2500
+ invertSorting?: boolean;
2501
+ meta?: ColumnMeta<TData>;
2502
+ columns?: ColumnDef<TData, unknown>[];
2503
+ }
2504
+ interface SortingColumn {
2505
+ id: string;
2506
+ desc: boolean;
2507
+ }
2508
+ type SortingState = SortingColumn[];
2509
+ interface ColumnFilter {
2510
+ id: string;
2511
+ value: unknown;
2512
+ }
2513
+ type ColumnFiltersState = ColumnFilter[];
2514
+ type RowSelectionState = Record<string, boolean>;
2515
+ type ColumnVisibilityState = Record<string, boolean>;
2516
+ type ColumnOrderState = string[];
2517
+ interface ColumnPinningState {
2518
+ left?: string[];
2519
+ right?: string[];
2520
+ }
2521
+ type ColumnSizingState = Record<string, number>;
2522
+ type ColumnSizingInfoState = {
2523
+ startOffset: number | null;
2524
+ startSize: number | null;
2525
+ deltaOffset: number | null;
2526
+ deltaPercentage: number | null;
2527
+ isResizingColumn: string | false;
2528
+ columnSizingStart: [string, number][];
2529
+ };
2530
+ type ExpandedState = Record<string, boolean> | true;
2531
+ type GroupingState = string[];
2532
+ interface PaginationState {
2533
+ pageIndex: number;
2534
+ pageSize: number;
2535
+ }
2536
+ interface TableState {
2537
+ sorting: SortingState;
2538
+ columnFilters: ColumnFiltersState;
2539
+ globalFilter: unknown;
2540
+ rowSelection: RowSelectionState;
2541
+ columnVisibility: ColumnVisibilityState;
2542
+ columnOrder: ColumnOrderState;
2543
+ columnPinning: ColumnPinningState;
2544
+ columnSizing: ColumnSizingState;
2545
+ columnSizingInfo: ColumnSizingInfoState;
2546
+ expanded: ExpandedState;
2547
+ grouping: GroupingState;
2548
+ pagination: PaginationState;
2549
+ }
2550
+ interface Row<TData> {
2551
+ id: string;
2552
+ index: number;
2553
+ original: TData;
2554
+ depth: number;
2555
+ parentId?: string;
2556
+ subRows: Row<TData>[];
2557
+ getValue: <T = unknown>(columnId: string) => T;
2558
+ renderValue: <T = unknown>(columnId: string) => T | null;
2559
+ getIsSelected: () => boolean;
2560
+ getCanSelect: () => boolean;
2561
+ getIsAllSubRowsSelected: () => boolean;
2562
+ getIsSomeSelected: () => boolean;
2563
+ toggleSelected: (value?: boolean) => void;
2564
+ getToggleSelectedHandler: () => (e: unknown) => void;
2565
+ getIsExpanded: () => boolean;
2566
+ getCanExpand: () => boolean;
2567
+ toggleExpanded: (value?: boolean) => void;
2568
+ getToggleExpandedHandler: () => () => void;
2569
+ getIsGrouped: () => boolean;
2570
+ groupingColumnId?: string;
2571
+ groupingValue?: unknown;
2572
+ getVisibleCells: () => Cell<TData>[];
2573
+ getAllCells: () => Cell<TData>[];
2574
+ getIsPinned: () => "top" | "bottom" | false;
2575
+ pin: (position: "top" | "bottom" | false) => void;
2576
+ }
2577
+ interface Cell<TData> {
2578
+ id: string;
2579
+ row: Row<TData>;
2580
+ column: Column<TData>;
2581
+ getValue: <T = unknown>() => T;
2582
+ renderValue: <T = unknown>() => T | null;
2583
+ getIsGrouped: () => boolean;
2584
+ getIsPlaceholder: () => boolean;
2585
+ getIsAggregated: () => boolean;
2586
+ getContext: () => CellContext<TData>;
2587
+ }
2588
+ interface Header<TData> {
2589
+ id: string;
2590
+ index: number;
2591
+ depth: number;
2592
+ column: Column<TData>;
2593
+ isPlaceholder: boolean;
2594
+ placeholderId?: string;
2595
+ subHeaders: Header<TData>[];
2596
+ colSpan: number;
2597
+ rowSpan: number;
2598
+ getSize: () => number;
2599
+ getStart: () => number;
2600
+ getContext: () => HeaderContext<TData>;
2601
+ getResizeHandler: () => (event: unknown) => void;
2602
+ getLeafHeaders: () => Header<TData>[];
2603
+ }
2604
+ interface HeaderGroup<TData> {
2605
+ id: string;
2606
+ depth: number;
2607
+ headers: Header<TData>[];
2608
+ }
2609
+ interface Column<TData> {
2610
+ id: string;
2611
+ depth: number;
2612
+ columnDef: ColumnDef<TData>;
2613
+ columns: Column<TData>[];
2614
+ parent?: Column<TData>;
2615
+ getFlatColumns: () => Column<TData>[];
2616
+ getLeafColumns: () => Column<TData>[];
2617
+ getIsSorted: () => false | "asc" | "desc";
2618
+ getNextSortingOrder: () => "asc" | "desc" | false;
2619
+ getCanSort: () => boolean;
2620
+ toggleSorting: (desc?: boolean, isMulti?: boolean) => void;
2621
+ clearSorting: () => void;
2622
+ getSortIndex: () => number;
2623
+ getAutoSortingFn: () => SortingFn<TData>;
2624
+ getAutoSortDir: () => "asc" | "desc";
2625
+ getIsFiltered: () => boolean;
2626
+ getFilterValue: () => unknown;
2627
+ setFilterValue: (value: unknown) => void;
2628
+ getCanFilter: () => boolean;
2629
+ getAutoFilterFn: () => FilterFn<TData> | undefined;
2630
+ getIsVisible: () => boolean;
2631
+ toggleVisibility: (value?: boolean) => void;
2632
+ getCanHide: () => boolean;
2633
+ getIsPinned: () => "left" | "right" | false;
2634
+ pin: (position: "left" | "right" | false) => void;
2635
+ getCanPin: () => boolean;
2636
+ getIsGrouped: () => boolean;
2637
+ toggleGrouping: () => void;
2638
+ getCanGroup: () => boolean;
2639
+ getGroupedIndex: () => number;
2640
+ getSize: () => number;
2641
+ getStart: (position?: "left" | "center" | "right") => number;
2642
+ getCanResize: () => boolean;
2643
+ resetSize: () => void;
2644
+ getIndex: (position?: "left" | "center" | "right") => number;
2645
+ }
2646
+ interface RowModel<TData> {
2647
+ rows: Row<TData>[];
2648
+ flatRows: Row<TData>[];
2649
+ rowsById: Record<string, Row<TData>>;
2650
+ }
2651
+ interface TableOptions<TData> {
2652
+ data: TData[];
2653
+ columns: ColumnDef<TData>[];
2654
+ getRowId?: (row: TData, index: number, parent?: Row<TData>) => string;
2655
+ defaultColumn?: Partial<ColumnDef<TData>>;
2656
+ /** Seed the initial value for any internal state slice without making it controlled. */
2657
+ initialState?: Partial<TableState>;
2658
+ /** Fully controlled state — every key provided here locks that slice and must be updated externally. */
2659
+ state?: Partial<TableState>;
2660
+ onStateChange?: (updater: Updater<TableState>) => void;
2661
+ manualSorting?: boolean;
2662
+ enableSorting?: boolean;
2663
+ enableMultiSort?: boolean;
2664
+ enableSortingRemoval?: boolean;
2665
+ enableMultiRemove?: boolean;
2666
+ maxMultiSortColCount?: number;
2667
+ sortDescFirst?: boolean;
2668
+ onSortingChange?: (updater: Updater<SortingState>) => void;
2669
+ manualFiltering?: boolean;
2670
+ enableFiltering?: boolean;
2671
+ enableColumnFilters?: boolean;
2672
+ enableGlobalFilter?: boolean;
2673
+ globalFilterFn?: FilterFn<TData>;
2674
+ onColumnFiltersChange?: (updater: Updater<ColumnFiltersState>) => void;
2675
+ onGlobalFilterChange?: (updater: Updater<unknown>) => void;
2676
+ manualPagination?: boolean;
2677
+ pageCount?: number;
2678
+ autoResetPageIndex?: boolean;
2679
+ onPaginationChange?: (updater: Updater<PaginationState>) => void;
2680
+ enableRowSelection?: boolean | ((row: Row<TData>) => boolean);
2681
+ enableMultiRowSelection?: boolean | ((row: Row<TData>) => boolean);
2682
+ enableSubRowSelection?: boolean | ((row: Row<TData>) => boolean);
2683
+ onRowSelectionChange?: (updater: Updater<RowSelectionState>) => void;
2684
+ enableHiding?: boolean;
2685
+ onColumnVisibilityChange?: (updater: Updater<ColumnVisibilityState>) => void;
2686
+ onColumnOrderChange?: (updater: Updater<ColumnOrderState>) => void;
2687
+ enablePinning?: boolean;
2688
+ onColumnPinningChange?: (updater: Updater<ColumnPinningState>) => void;
2689
+ enableColumnResizing?: boolean;
2690
+ columnResizeMode?: "onChange" | "onEnd";
2691
+ columnResizeDirection?: "ltr" | "rtl";
2692
+ onColumnSizingChange?: (updater: Updater<ColumnSizingState>) => void;
2693
+ onColumnSizingInfoChange?: (updater: Updater<ColumnSizingInfoState>) => void;
2694
+ manualGrouping?: boolean;
2695
+ enableGrouping?: boolean;
2696
+ onGroupingChange?: (updater: Updater<GroupingState>) => void;
2697
+ manualExpanding?: boolean;
2698
+ enableExpanding?: boolean;
2699
+ getSubRows?: (row: TData, index: number) => TData[] | undefined;
2700
+ getIsRowExpanded?: (row: Row<TData>) => boolean;
2701
+ onExpandedChange?: (updater: Updater<ExpandedState>) => void;
2702
+ paginateExpandedRows?: boolean;
2703
+ enableRowPinning?: boolean | ((row: Row<TData>) => boolean);
2704
+ keepPinnedRows?: boolean;
2705
+ onRowPinningChange?: (updater: Updater<Record<string, "top" | "bottom">>) => void;
2706
+ }
2707
+ interface TableInstance<TData> {
2708
+ options: TableOptions<TData>;
2709
+ getState: () => TableState;
2710
+ setState: (updater: Updater<TableState>) => void;
2711
+ reset: () => void;
2712
+ getAllColumns: () => Column<TData>[];
2713
+ getAllFlatColumns: () => Column<TData>[];
2714
+ getAllLeafColumns: () => Column<TData>[];
2715
+ getColumn: (id: string) => Column<TData> | undefined;
2716
+ getHeaderGroups: () => HeaderGroup<TData>[];
2717
+ getLeftHeaderGroups: () => HeaderGroup<TData>[];
2718
+ getCenterHeaderGroups: () => HeaderGroup<TData>[];
2719
+ getRightHeaderGroups: () => HeaderGroup<TData>[];
2720
+ getFooterGroups: () => HeaderGroup<TData>[];
2721
+ getCoreRowModel: () => RowModel<TData>;
2722
+ getRowModel: () => RowModel<TData>;
2723
+ getPreFilteredRowModel: () => RowModel<TData>;
2724
+ getFilteredRowModel: () => RowModel<TData>;
2725
+ getPreSortedRowModel: () => RowModel<TData>;
2726
+ getSortedRowModel: () => RowModel<TData>;
2727
+ getGroupedRowModel: () => RowModel<TData>;
2728
+ getExpandedRowModel: () => RowModel<TData>;
2729
+ getPrePaginationRowModel: () => RowModel<TData>;
2730
+ getPaginationRowModel: () => RowModel<TData>;
2731
+ getSelectedRowModel: () => RowModel<TData>;
2732
+ getRow: (id: string) => Row<TData>;
2733
+ setSorting: (updater: Updater<SortingState>) => void;
2734
+ resetSorting: (defaultState?: boolean) => void;
2735
+ setColumnFilters: (updater: Updater<ColumnFiltersState>) => void;
2736
+ resetColumnFilters: (defaultState?: boolean) => void;
2737
+ setGlobalFilter: (value: unknown) => void;
2738
+ resetGlobalFilter: (defaultState?: boolean) => void;
2739
+ setPageIndex: (updater: Updater<number>) => void;
2740
+ resetPageIndex: (defaultState?: boolean) => void;
2741
+ setPageSize: (updater: Updater<number>) => void;
2742
+ resetPageSize: (defaultState?: boolean) => void;
2743
+ getPageCount: () => number;
2744
+ getCanPreviousPage: () => boolean;
2745
+ getCanNextPage: () => boolean;
2746
+ previousPage: () => void;
2747
+ nextPage: () => void;
2748
+ firstPage: () => void;
2749
+ lastPage: () => void;
2750
+ setRowSelection: (updater: Updater<RowSelectionState>) => void;
2751
+ resetRowSelection: (defaultState?: boolean) => void;
2752
+ toggleAllRowsSelected: (value?: boolean) => void;
2753
+ toggleAllPageRowsSelected: (value?: boolean) => void;
2754
+ getIsAllRowsSelected: () => boolean;
2755
+ getIsAllPageRowsSelected: () => boolean;
2756
+ getIsSomeRowsSelected: () => boolean;
2757
+ getIsSomePageRowsSelected: () => boolean;
2758
+ getToggleAllRowsSelectedHandler: () => (e: unknown) => void;
2759
+ getToggleAllPageRowsSelectedHandler: () => (e: unknown) => void;
2760
+ setColumnVisibility: (updater: Updater<ColumnVisibilityState>) => void;
2761
+ resetColumnVisibility: (defaultState?: boolean) => void;
2762
+ toggleAllColumnsVisible: (value?: boolean) => void;
2763
+ getIsAllColumnsVisible: () => boolean;
2764
+ getIsSomeColumnsVisible: () => boolean;
2765
+ getToggleAllColumnsVisibilityHandler: () => (e: unknown) => void;
2766
+ getVisibleFlatColumns: () => Column<TData>[];
2767
+ getVisibleLeafColumns: () => Column<TData>[];
2768
+ setColumnOrder: (updater: Updater<ColumnOrderState>) => void;
2769
+ resetColumnOrder: (defaultState?: boolean) => void;
2770
+ setColumnPinning: (updater: Updater<ColumnPinningState>) => void;
2771
+ resetColumnPinning: (defaultState?: boolean) => void;
2772
+ getLeftFlatColumns: () => Column<TData>[];
2773
+ getRightFlatColumns: () => Column<TData>[];
2774
+ getCenterFlatColumns: () => Column<TData>[];
2775
+ getLeftLeafColumns: () => Column<TData>[];
2776
+ getRightLeafColumns: () => Column<TData>[];
2777
+ getCenterLeafColumns: () => Column<TData>[];
2778
+ setColumnSizing: (updater: Updater<ColumnSizingState>) => void;
2779
+ setColumnSizingInfo: (updater: Updater<ColumnSizingInfoState>) => void;
2780
+ resetColumnSizing: (defaultState?: boolean) => void;
2781
+ setGrouping: (updater: Updater<GroupingState>) => void;
2782
+ resetGrouping: (defaultState?: boolean) => void;
2783
+ setExpanded: (updater: Updater<ExpandedState>) => void;
2784
+ resetExpanded: (defaultState?: boolean) => void;
2785
+ toggleAllRowsExpanded: (expanded?: boolean) => void;
2786
+ getIsAllRowsExpanded: () => boolean;
2787
+ getIsSomeRowsExpanded: () => boolean;
2788
+ getCanSomeRowsExpand: () => boolean;
2789
+ getExpandedDepth: () => number;
2790
+ }
2791
+ interface ActionDef<TData> {
2792
+ id: string;
2793
+ label: string;
2794
+ icon?: React.ComponentType<{
2795
+ className?: string;
2796
+ }>;
2797
+ onClick: (item: TData) => void;
2798
+ destructive?: boolean;
2799
+ hidden?: (item: TData) => boolean;
2800
+ disabled?: (item: TData) => boolean;
2801
+ confirm?: string | ((item: TData) => string);
2802
+ variant?: "primary" | "default" | "ghost" | "destructive";
2803
+ }
2804
+ interface ItemDescriptorBadge<TData> {
2805
+ field: keyof TData & string;
2806
+ options?: Array<{
2807
+ value: string;
2808
+ label: string;
2809
+ className?: string;
2810
+ }>;
2811
+ }
2812
+ interface ItemDescriptorMeta<TData> {
2813
+ field: keyof TData & string;
2814
+ label: string;
2815
+ format?: (value: unknown) => string;
2816
+ }
2817
+ interface ItemDescriptor<TData> {
2818
+ title: keyof TData & string;
2819
+ subtitle?: keyof TData & string;
2820
+ image?: keyof TData & string;
2821
+ icon?: (keyof TData & string) | React.ComponentType<{
2822
+ className?: string;
2823
+ }>;
2824
+ avatar?: keyof TData & string;
2825
+ badges?: ItemDescriptorBadge<TData>[];
2826
+ metadata?: ItemDescriptorMeta<TData>[];
2827
+ description?: keyof TData & string;
2828
+ }
2829
+ interface ItemRenderContext<TData> {
2830
+ isSelected: boolean;
2831
+ isEditing: boolean;
2832
+ isMultiSelectMode: boolean;
2833
+ onToggleSelect: () => void;
2834
+ onEdit: () => void;
2835
+ onSave: (changes: Partial<TData>) => void;
2836
+ onCancel: () => void;
2837
+ actions: ActionDef<TData>[];
2838
+ row: Row<TData>;
2839
+ }
2840
+ interface EmptyStateConfig {
2841
+ icon?: React.ComponentType<{
2842
+ className?: string;
2843
+ }>;
2844
+ title?: string;
2845
+ description?: string;
2846
+ action?: {
2847
+ label: string;
2848
+ onClick: () => void;
2849
+ };
2850
+ filteredTitle?: string;
2851
+ filteredDescription?: string;
2852
+ filteredAction?: {
2853
+ label: string;
2854
+ onClick: () => void;
2855
+ };
2856
+ }
2857
+ interface BatchActionDef {
2858
+ id: string;
2859
+ label: string;
2860
+ icon?: React.ComponentType<{
2861
+ className?: string;
2862
+ }>;
2863
+ destructive?: boolean;
2864
+ }
2865
+ interface GalleryColumns {
2866
+ sm?: number;
2867
+ md?: number;
2868
+ lg?: number;
2869
+ xl?: number;
2870
+ }
2871
+
2872
+ /**
2873
+ * table/row-models.ts
2874
+ *
2875
+ * Pure row model pipeline functions. Each stage transforms data
2876
+ * into a RowModel — no side effects, no external dependencies.
2877
+ */
2878
+
2879
+ declare function createRow<TData>(original: TData, index: number, columns: Column<TData>[], table: TableInstance<TData>, depth?: number, parentId?: string, subRows?: Row<TData>[]): Row<TData>;
2880
+ declare function getCoreRowModel<TData>(data: TData[], columns: Column<TData>[], table: TableInstance<TData>): RowModel<TData>;
2881
+ declare function getFilteredRowModel<TData>(rowModel: RowModel<TData>, columnFilters: ColumnFiltersState, globalFilter: unknown, columns: Column<TData>[], globalFilterFn?: FilterFn<TData>): RowModel<TData>;
2882
+ declare function getSortedRowModel<TData>(rowModel: RowModel<TData>, sorting: SortingState, columns: Column<TData>[]): RowModel<TData>;
2883
+ declare function getGroupedRowModel<TData>(rowModel: RowModel<TData>, grouping: GroupingState, columns: Column<TData>[], table: TableInstance<TData>): RowModel<TData>;
2884
+ declare function getExpandedRowModel<TData>(rowModel: RowModel<TData>, expanded: ExpandedState): RowModel<TData>;
2885
+ declare function getPaginatedRowModel<TData>(rowModel: RowModel<TData>, pagination: PaginationState): RowModel<TData>;
2886
+ declare function getSelectedRowModel<TData>(rowModel: RowModel<TData>, selection: Record<string, boolean>): RowModel<TData>;
2887
+
2888
+ /**
2889
+ * table/faceting.ts
2890
+ *
2891
+ * Column faceting utilities — compute unique values, counts,
2892
+ * and min/max for filter UI controls.
2893
+ */
2894
+
2895
+ /**
2896
+ * Compute unique values and their counts for a given column.
2897
+ * Useful for rendering filter dropdown options with hit counts.
2898
+ */
2899
+ declare function getFacetedUniqueValues<TData>(rowModel: RowModel<TData>, columnId: string): Map<unknown, number>;
2900
+ /**
2901
+ * Compute min and max numeric values for a given column.
2902
+ * Returns [min, max] or undefined if no numeric values exist.
2903
+ */
2904
+ declare function getFacetedMinMaxValues<TData>(rowModel: RowModel<TData>, columnId: string): [number, number] | undefined;
2905
+ /**
2906
+ * Get a filtered row model scoped to a single column's facet.
2907
+ * Returns a row model that excludes the given column's own filter,
2908
+ * so the facet counts reflect what would be available if that
2909
+ * specific filter were removed.
2910
+ */
2911
+ declare function getFacetedRowModel<TData>(preFilteredRowModel: RowModel<TData>, columnId: string, allFilteredRowModel: RowModel<TData>): RowModel<TData>;
2912
+
2913
+ export { type AccessorFn, type ActionDef, type AdapterStatus, type AgUiStateDeltaEvent, type AgUiStateMapping, type AgUiStateSnapshotEvent, type AggregationFn, type ApplyAgUiOptions, type BatchActionDef, type BelongsToRelation, type CascadeContext, type Cell, type CellContext, type CellRenderer, type ChangeEvent, type ChangeOp, type ChangeOperation, type ChangeSet, type ChannelConfig, type Column, type ColumnDef, type ColumnFiltersState, type ColumnMeta, type ColumnOrderState, type ColumnPinningState, type ColumnSizingInfoState, type ColumnSizingState, type ColumnVisibilityState, type CompletenessMode, type CreateFlintAdapterOptions, type CreatePGlitePersistenceAdapterOptions, type CreateTauriSqlPersistenceAdapterOptions, type DevtoolsEvent, type DevtoolsEventBus, type DevtoolsEventBusOptions, type DevtoolsSourceFn, EMPTY_ENTITY_STATE, EMPTY_IDS, EMPTY_LIST_STATE, EMPTY_SYNC_METADATA, type ElectricAdapterOptions, type ElectricTableConfig, type EmptyStateConfig, type EngineOptions, type EntityChange, type EntityError, type EntityErrorOptions, type EntityId, type EntityJsonSchemaConfig, type EntityQueryOptions, type EntitySchema, type EntitySnapshot, type EntityState, type EntitySyncMetadata, type EntityTransport, type EntityType, type ExpandedState, type FilterClause, type FilterFn, type FilterGroup, type FilterOperator, type FilterSpec, type FlintCheckpointStore, type FlintClientLike, type FlintEntityEvent, type FlintEntityQuery, type FlintEntityRecord, type GalleryColumns, type GetEntityJsonSchemaOptions, 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, IncrementalView, type IncrementalViewOptions, type ItemDescriptor, type ItemDescriptorBadge, type ItemDescriptorMeta, type ItemRenderContext, type JsonPatchOp, type JsonSchemaObject, type ListFetchParams, type ListQuery, type ListQueryOptions, type ListResponse, type ListResult, type ListState, type LocalFirstGraphRuntime, type MakeRestTransportOptions, type ManagerOptions, type ManyToManyRelation, type MergeContext, type MergeStrategy, type PGlitePersistenceClient, type PaginationState, type ColumnDef as PureColumnDef, type ColumnMeta as PureColumnMeta, type QueryKey, type RealtimeAdapter, RealtimeManager, type RegisterEntityFromSqlOptions, type RegisteredStore, type RelationDescriptor, type ReplayRetryPolicy, type Row, type RowModel, type RowSelectionState, type SchemaGraphToolContext, type SortClause, type SortDirection, type SortSpec, type SortingFn, type SortingState, type StartLocalFirstGraphOptions, type SubscriptionConfig, type SupabaseAdapterOptions, type SupabaseLike, type SupabaseQueryBuilderLike, type SurrealCheckpointStore, type SurrealLike, type SurrealLiveAction, type SurrealLiveAdapterOptions, type SurrealTableConfig, type SyncAdapter, type SyncOrigin, type TableInstance, type TableOptions, type TableState, type TauriSqlClient, type TenantClaim, type TenantScopedAdapterOptions, type TenantScopedTableConfig, TerminalError, type TimeTravelSnapshot, type TimeTravelState, TransientError, type UnsubscribeFn, type Updater, type ViewDescriptor, type ViewMode, type WebSocketAdapterOptions, __resetEntityTransports, __resetStoreRegistry, applyAgUiDelta, applyAgUiSnapshot, applyJsonPatch, applyView, attachGlobalListeners, buildTenantWhere, cascadeInvalidation, checkCompleteness, collectDirtyPaths, compareEntities, configureEngine, configureTimeTravel, createConvexAdapter, createDevtoolsEventBus, createElectricAdapter, createFlintAdapter, createGraphAction, createGraphEffect, createGraphQLSubscriptionAdapter, createGraphTool, createGraphTransaction, createLoroMergeStrategy, createPGlitePersistenceAdapter, createRow, createSchemaGraphTool, createSupabaseRealtimeAdapter, createSurrealLiveAdapter, createTauriSqlPersistenceAdapter, createTenantScopedElectricAdapter, createWebSocketAdapter, dedupe, escapeHtml, exportGraphSnapshot, exportGraphSnapshotWithSchemas, fetchEntity, fetchList, findInsertionIndex, flattenClauses, getActiveSubscriberCount, getCoreRowModel, getRealtimeManager as getElectricRealtimeManager, getEngineOptions, getEntityJsonSchema, getEntityTransport, getExpandedRowModel, getFacetedMinMaxValues, getFacetedRowModel, getFacetedUniqueValues, getFilteredRowModel, getGroupedRowModel, getMergeStrategy, getPaginatedRowModel, getRealtimeManager, getRegisteredEntityTypes, getRegisteredStores, getSchema, getSelectedRowModel, getSortedRowModel, getTimeTravelState, getValueAtPath, hasCustomPredicates, hasMergeStrategy, humanizeFieldName, hydrateGraphFromStorage, inferSchemaFieldType, lwwStrategy, makeRestTransport, matchesFilter, matchesSearch, parseCreateTable, persistGraphToStorage, publishFlintMutation, queryOnce, readRelations, recordGraphSnapshot, registerEntityFromSql, registerEntityJsonSchema, registerEntityTransport, registerMergeStrategy, registerRuntimeSchema, registerSchema, registerStore, registerSubscriber, registryKey, renderMarkdownToHtml, replayActionWithRetry, resetRealtimeManager, restoreGraphSnapshot, restoreGraphSnapshotBySeq, selectGraph, serializeKey, setDefaultMergeStrategy, setValueAtPath, sqlTypeToJsonSchema, startGarbageCollector, startLocalFirstGraph, stepTimeTravel, stopGarbageCollector, subscribeDevtoolsEvent, subscribeSubscriberStats, subscribeTimeTravel, toEntityError, toGraphQLVariables, toPrismaOrderBy, toPrismaWhere, toRestParams, toSQLClauses, unregisterSubscriber, useGraphStore, useGraphSyncStatus };