@prometheus-ags/entity-graph-solid 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.
- package/README.md +192 -0
- package/dist/index.d.cts +304 -0
- package/dist/index.d.ts +304 -0
- package/dist/index.js +314 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +298 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# @prometheus-ags/entity-graph-solid
|
|
2
|
+
|
|
3
|
+
SolidJS bindings for `@prometheus-ags/entity-graph-core` — fine-grained reactive entity and list primitives backed by the normalized entity graph.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Component (SolidJS)
|
|
9
|
+
└─ createEntity / createEntityList (this package, Layer 2)
|
|
10
|
+
└─ core engine fetchEntity / fetchList (entity-graph-core, Layer 1)
|
|
11
|
+
└─ useGraphStore (Zustand, Layer 0)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Components access data only through `createEntity` / `createEntityList`. The store (Zustand) is the single source of truth; SolidJS signals mirror it for fine-grained reactivity.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pnpm add @prometheus-ags/entity-graph-solid @prometheus-ags/entity-graph-core solid-js
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### 1. Configure the engine once at app boot
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// src/index.tsx
|
|
28
|
+
import { setupGraphProvider } from "@prometheus-ags/entity-graph-solid";
|
|
29
|
+
import { registerEntityTransport, makeRestTransport } from "@prometheus-ags/entity-graph-core";
|
|
30
|
+
|
|
31
|
+
// Register transports at startup — one per entity type.
|
|
32
|
+
registerEntityTransport("Invoice", makeRestTransport({ baseUrl: "/api" }));
|
|
33
|
+
|
|
34
|
+
// Wire focus/reconnect listeners and GC.
|
|
35
|
+
const cleanup = setupGraphProvider({ staleTime: 60_000 });
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. Fetch a single entity
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { createEntity } from "@prometheus-ags/entity-graph-solid";
|
|
42
|
+
import { createSignal, Show } from "solid-js";
|
|
43
|
+
|
|
44
|
+
interface Invoice {
|
|
45
|
+
id: string;
|
|
46
|
+
amount: number;
|
|
47
|
+
status: "draft" | "paid";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function InvoiceDetail(props: { id: string }) {
|
|
51
|
+
const invoice = createEntity<{ id: string; amount: number; status: string }, Invoice>({
|
|
52
|
+
type: "Invoice",
|
|
53
|
+
id: () => props.id,
|
|
54
|
+
fetch: (id) => fetch(`/api/invoices/${id}`).then((r) => r.json()),
|
|
55
|
+
normalize: (raw) => ({ id: raw.id, amount: raw.amount, status: raw.status as Invoice["status"] }),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Show when={!invoice.isLoading()} fallback={<p>Loading…</p>}>
|
|
60
|
+
<Show when={invoice.data()} fallback={<p>Not found</p>}>
|
|
61
|
+
{(inv) => (
|
|
62
|
+
<div>
|
|
63
|
+
<h1>Invoice #{inv().id}</h1>
|
|
64
|
+
<p>Amount: ${inv().amount}</p>
|
|
65
|
+
<p>Status: {inv().status}</p>
|
|
66
|
+
</div>
|
|
67
|
+
)}
|
|
68
|
+
</Show>
|
|
69
|
+
</Show>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 3. Fetch a reactive list
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
import { createEntityList } from "@prometheus-ags/entity-graph-solid";
|
|
78
|
+
import { createSignal, For, Show } from "solid-js";
|
|
79
|
+
|
|
80
|
+
function InvoiceList() {
|
|
81
|
+
const [status, setStatus] = createSignal<string>("all");
|
|
82
|
+
|
|
83
|
+
const invoices = createEntityList<{ id: string; amount: number }, Invoice>({
|
|
84
|
+
type: "Invoice",
|
|
85
|
+
queryKey: () => ["invoices", { status: status() }],
|
|
86
|
+
fetch: ({ cursor, params }) =>
|
|
87
|
+
fetch(`/api/invoices?cursor=${cursor ?? ""}&status=${params?.status ?? "all"}`)
|
|
88
|
+
.then((r) => r.json()),
|
|
89
|
+
normalize: (raw) => ({
|
|
90
|
+
id: raw.id,
|
|
91
|
+
data: { id: raw.id, amount: raw.amount, status: "draft" },
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<div>
|
|
97
|
+
<button onClick={() => setStatus("paid")}>Show paid</button>
|
|
98
|
+
<Show when={invoices.isLoading()}>
|
|
99
|
+
<p>Loading…</p>
|
|
100
|
+
</Show>
|
|
101
|
+
<For each={invoices.items()}>
|
|
102
|
+
{(inv) => <div>{inv.id} — ${inv.amount}</div>}
|
|
103
|
+
</For>
|
|
104
|
+
<Show when={invoices.hasNextPage()}>
|
|
105
|
+
<button
|
|
106
|
+
onClick={() => invoices.fetchNextPage()}
|
|
107
|
+
disabled={invoices.isLoadingMore()}
|
|
108
|
+
>
|
|
109
|
+
Load more
|
|
110
|
+
</button>
|
|
111
|
+
</Show>
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### 4. Ad-hoc graph slice
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
import { createGraphStore } from "@prometheus-ags/entity-graph-solid";
|
|
121
|
+
|
|
122
|
+
// Fine-grained accessor for any graph slice.
|
|
123
|
+
const invoiceCount = createGraphStore(
|
|
124
|
+
(s) => Object.keys(s.entities["Invoice"] ?? {}).length,
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
console.log(invoiceCount()); // reactive SolidJS signal
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## API Reference
|
|
131
|
+
|
|
132
|
+
### `createEntity(opts)`
|
|
133
|
+
|
|
134
|
+
| Option | Type | Description |
|
|
135
|
+
|--------|------|-------------|
|
|
136
|
+
| `type` | `string` | Entity kind (graph partition key). |
|
|
137
|
+
| `id` | `() => string \| null` | Reactive id accessor. |
|
|
138
|
+
| `fetch` | `(id) => Promise<TRaw>` | Transport fetch function. |
|
|
139
|
+
| `normalize` | `(raw) => TEntity` | Maps raw response to canonical shape. |
|
|
140
|
+
| `idField` | `string` | Id field name in normalized entity (default `"id"`). |
|
|
141
|
+
| `staleTime` | `number` | Cache freshness in ms. |
|
|
142
|
+
| `enabled` | `() => boolean` | Gate fetching. |
|
|
143
|
+
| `onSuccess` | `(entity) => void` | Called after successful graph write. |
|
|
144
|
+
| `onError` | `(error) => void` | Called after all retries fail. |
|
|
145
|
+
|
|
146
|
+
**Returns** `{ data, isLoading, isRefetching, error, typedError, refetch, entityState }`
|
|
147
|
+
|
|
148
|
+
### `createEntityList(opts)`
|
|
149
|
+
|
|
150
|
+
| Option | Type | Description |
|
|
151
|
+
|--------|------|-------------|
|
|
152
|
+
| `type` | `string` | Entity kind. |
|
|
153
|
+
| `queryKey` | `() => unknown[]` | Reactive list cache key. |
|
|
154
|
+
| `fetch` | `(params) => Promise<ListResult>` | Transport fetcher. |
|
|
155
|
+
| `normalize` | `(raw) => { id, data }` | Per-row normalizer. |
|
|
156
|
+
| `mode` | `"replace" \| "append"` | Replace list or append (infinite scroll). |
|
|
157
|
+
| `staleTime` | `number` | Cache freshness in ms. |
|
|
158
|
+
| `enabled` | `() => boolean` | Gate fetching. |
|
|
159
|
+
|
|
160
|
+
**Returns** `{ items, isLoading, isLoadingMore, error, typedError, total, hasNextPage, hasPrevPage, fetchNextPage, refetch, listState }`
|
|
161
|
+
|
|
162
|
+
### `createGraphStore(selector, equalityFn?)`
|
|
163
|
+
|
|
164
|
+
Returns a fine-grained SolidJS accessor that updates when the selected graph slice changes.
|
|
165
|
+
|
|
166
|
+
### `setupGraphProvider(opts?)`
|
|
167
|
+
|
|
168
|
+
Configures the engine and attaches browser listeners. Call once at app startup. Returns a cleanup function.
|
|
169
|
+
|
|
170
|
+
## Error Handling
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
import { TerminalError, TransientError } from "@prometheus-ags/entity-graph-solid";
|
|
174
|
+
|
|
175
|
+
const invoice = createEntity({ ... });
|
|
176
|
+
|
|
177
|
+
// String error (always available):
|
|
178
|
+
<Show when={invoice.error()}><p>Error: {invoice.error()}</p></Show>
|
|
179
|
+
|
|
180
|
+
// Typed error (list variant):
|
|
181
|
+
const list = createEntityList({ ... });
|
|
182
|
+
const err = list.typedError();
|
|
183
|
+
if (err instanceof TerminalError) {
|
|
184
|
+
// 4xx — do not offer a retry button
|
|
185
|
+
} else if (err instanceof TransientError) {
|
|
186
|
+
// 5xx / network — show retry button
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## License
|
|
191
|
+
|
|
192
|
+
MIT
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { EntityType, EntityId, EntityError, ListState, EntityState, GraphState, EngineOptions } from '@prometheus-ags/entity-graph-core';
|
|
2
|
+
export { EntityError, EntityId, EntityState, EntityType, FilterSpec, ListState, SortSpec, TerminalError, TransientError, ViewDescriptor, useGraphStore } from '@prometheus-ags/entity-graph-core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Shared option types for SolidJS entity-graph bindings.
|
|
6
|
+
*
|
|
7
|
+
* These mirror the engine option shapes from @prometheus-ags/entity-graph-core
|
|
8
|
+
* but are scoped to accessor patterns (no React-specific concepts).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Options for `createEntity`. The `source` accessor is called reactively;
|
|
13
|
+
* changing either `type` or `id` inside it re-triggers the fetch.
|
|
14
|
+
*
|
|
15
|
+
* @template TRaw - Shape returned by the transport fetch function.
|
|
16
|
+
* @template TEntity - Normalized entity shape written to the graph.
|
|
17
|
+
*/
|
|
18
|
+
interface CreateEntityOptions<TRaw, TEntity extends object> {
|
|
19
|
+
/** Entity kind (e.g. `"Invoice"`). Drives the graph partition. */
|
|
20
|
+
type: EntityType;
|
|
21
|
+
/** Reactive accessor returning the entity id — return `null` / `undefined` to disable. */
|
|
22
|
+
id: () => EntityId | null | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Transport fetch function. Receives the resolved `id` and returns raw data.
|
|
25
|
+
* Called by the underlying `createResource`; deduplicated by the core engine.
|
|
26
|
+
*/
|
|
27
|
+
fetch: (id: EntityId) => Promise<TRaw>;
|
|
28
|
+
/** Maps raw API response to the canonical shape upserted into the graph. */
|
|
29
|
+
normalize: (raw: TRaw) => TEntity;
|
|
30
|
+
/**
|
|
31
|
+
* Name of the id field inside the normalized entity (default `"id"`).
|
|
32
|
+
* Used when the server-returned id may differ from the request id.
|
|
33
|
+
*/
|
|
34
|
+
idField?: string;
|
|
35
|
+
/**
|
|
36
|
+
* Milliseconds before a cached entity is considered stale and a background
|
|
37
|
+
* refetch is triggered (inherits from `configureEngine` when omitted).
|
|
38
|
+
*/
|
|
39
|
+
staleTime?: number;
|
|
40
|
+
/** Set to `false` to skip fetching regardless of id presence (default `true`). */
|
|
41
|
+
enabled?: () => boolean;
|
|
42
|
+
/** Called after a successful fetch and graph write. */
|
|
43
|
+
onSuccess?: (entity: TEntity) => void;
|
|
44
|
+
/** Called after all retries are exhausted. */
|
|
45
|
+
onError?: (error: Error) => void;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Options for `createEntityList`. The `queryKey` accessor is called reactively;
|
|
49
|
+
* changing the key triggers a new list fetch.
|
|
50
|
+
*
|
|
51
|
+
* @template TRaw - Shape of each raw row from the transport.
|
|
52
|
+
* @template TEntity - Normalized entity shape written to the graph.
|
|
53
|
+
*/
|
|
54
|
+
interface CreateEntityListOptions<TRaw, TEntity extends object> {
|
|
55
|
+
/** Entity kind for all items in this list. */
|
|
56
|
+
type: EntityType;
|
|
57
|
+
/**
|
|
58
|
+
* Reactive accessor returning the list query key array.
|
|
59
|
+
* Changing any element triggers a re-fetch. Use stable primitive values.
|
|
60
|
+
*/
|
|
61
|
+
queryKey: () => unknown[];
|
|
62
|
+
/**
|
|
63
|
+
* Transport fetcher for the full list. Optional for graph-only (e.g. local-first
|
|
64
|
+
* PGlite) scenarios where the graph is hydrated out-of-band.
|
|
65
|
+
*/
|
|
66
|
+
fetch?: (params: {
|
|
67
|
+
cursor?: string;
|
|
68
|
+
page?: number;
|
|
69
|
+
pageSize?: number;
|
|
70
|
+
params?: Record<string, unknown>;
|
|
71
|
+
}) => Promise<{
|
|
72
|
+
items: TRaw[];
|
|
73
|
+
total?: number | null;
|
|
74
|
+
nextCursor?: string | null;
|
|
75
|
+
prevCursor?: string;
|
|
76
|
+
hasNextPage?: boolean;
|
|
77
|
+
hasPrevPage?: boolean;
|
|
78
|
+
page?: number;
|
|
79
|
+
pageSize?: number;
|
|
80
|
+
}>;
|
|
81
|
+
/** Row normalizer: maps a raw row to `{ id, data }` for graph upsert. */
|
|
82
|
+
normalize?: (raw: TRaw) => {
|
|
83
|
+
id: EntityId;
|
|
84
|
+
data: TEntity;
|
|
85
|
+
};
|
|
86
|
+
/** Replace or append ids on re-fetch (default `"replace"`). */
|
|
87
|
+
mode?: "replace" | "append";
|
|
88
|
+
/** Milliseconds before the list is re-fetched in the background. */
|
|
89
|
+
staleTime?: number;
|
|
90
|
+
/** Reactive accessor; set to `false` to pause fetching (default `true`). */
|
|
91
|
+
enabled?: () => boolean;
|
|
92
|
+
/** Called after a successful list fetch. */
|
|
93
|
+
onSuccess?: (result: {
|
|
94
|
+
items: TRaw[];
|
|
95
|
+
total?: number | null;
|
|
96
|
+
}) => void;
|
|
97
|
+
/** Called after all retries are exhausted. */
|
|
98
|
+
onError?: (error: Error) => void;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Return value of `createEntity`.
|
|
102
|
+
* All fields are SolidJS signals (fine-grained, no re-render of unrelated parts).
|
|
103
|
+
*/
|
|
104
|
+
interface CreateEntityReturn<TEntity extends object> {
|
|
105
|
+
/** The merged entity (canonical + patch overlay) or `null` when not yet loaded. */
|
|
106
|
+
data: () => TEntity | null;
|
|
107
|
+
/** `true` while the initial or background fetch is in-flight. */
|
|
108
|
+
isLoading: () => boolean;
|
|
109
|
+
/** `true` only during background SWR refetch (data already present). */
|
|
110
|
+
isRefetching: () => boolean;
|
|
111
|
+
/** Per-entity fetch error string from the graph, or `null`. */
|
|
112
|
+
error: () => string | null;
|
|
113
|
+
/**
|
|
114
|
+
* Typed `EntityError` instance (`TerminalError` | `TransientError`) so
|
|
115
|
+
* consumers can `instanceof`-check the failure mode without string parsing.
|
|
116
|
+
*/
|
|
117
|
+
typedError: () => EntityError | null;
|
|
118
|
+
/** Imperatively trigger a refetch, bypassing staleness checks. */
|
|
119
|
+
refetch: () => Promise<void>;
|
|
120
|
+
/** Raw `EntityState` slice for advanced use (isFetching, stale, lastFetched). */
|
|
121
|
+
entityState: () => EntityState;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Return value of `createEntityList`.
|
|
125
|
+
* Items are fine-grained: each row is individually reactive via SolidJS signals.
|
|
126
|
+
*/
|
|
127
|
+
interface CreateEntityListReturn<TEntity extends object> {
|
|
128
|
+
/** Resolved entity objects in list order (ids joined to graph at read time). */
|
|
129
|
+
items: () => TEntity[];
|
|
130
|
+
/** `true` while the initial list fetch is in-flight. */
|
|
131
|
+
isLoading: () => boolean;
|
|
132
|
+
/** `true` while a "load more" / pagination fetch is in-flight. */
|
|
133
|
+
isLoadingMore: () => boolean;
|
|
134
|
+
/** List fetch error string, or `null`. */
|
|
135
|
+
error: () => string | null;
|
|
136
|
+
/** Typed error instance for `instanceof` checks. */
|
|
137
|
+
typedError: () => EntityError | null;
|
|
138
|
+
/** Total entity count from the server (may be `null` before first fetch). */
|
|
139
|
+
total: () => number | null;
|
|
140
|
+
/** `true` when a next page is available. */
|
|
141
|
+
hasNextPage: () => boolean;
|
|
142
|
+
/** `true` when a previous page is available. */
|
|
143
|
+
hasPrevPage: () => boolean;
|
|
144
|
+
/** Fetch the next page (appends when `mode` is `"append"`). */
|
|
145
|
+
fetchNextPage: () => Promise<void>;
|
|
146
|
+
/** Refetch the list from scratch (replaces ids). */
|
|
147
|
+
refetch: () => Promise<void>;
|
|
148
|
+
/** Raw `ListState` slice for advanced pagination/meta access. */
|
|
149
|
+
listState: () => ListState;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* createEntity — SolidJS primitive that bridges a single entity to the
|
|
154
|
+
* normalized entity graph via `createResource`.
|
|
155
|
+
*
|
|
156
|
+
* Architecture:
|
|
157
|
+
* createEntity (this file, Layer 2)
|
|
158
|
+
* └─ core engine fetchEntity (Layer 1 / transport)
|
|
159
|
+
* └─ useGraphStore (Layer 0 / Zustand graph)
|
|
160
|
+
*
|
|
161
|
+
* SolidJS `createResource` owns the async lifecycle; the graph is the
|
|
162
|
+
* single source of truth for the entity payload. Once fetchEntity writes to
|
|
163
|
+
* the graph, a Zustand subscription (via `createStore`) propagates the merged
|
|
164
|
+
* entity back to SolidJS as a fine-grained reactive signal.
|
|
165
|
+
*/
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Creates a fine-grained SolidJS reactive primitive for a single entity.
|
|
169
|
+
*
|
|
170
|
+
* - Uses `createResource` as the async driver so Suspense and ErrorBoundary
|
|
171
|
+
* boundaries work naturally.
|
|
172
|
+
* - Subscribes to the Zustand graph with `useGraphStore.subscribe` and mirrors
|
|
173
|
+
* the merged entity into a `createStore`-backed signal for fine-grained
|
|
174
|
+
* SolidJS reactivity (only the changed field triggers updates).
|
|
175
|
+
* - Registers / unregisters a subscriber token with the core engine so idle
|
|
176
|
+
* entities stop background revalidation when no components are observing them.
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```tsx
|
|
180
|
+
* const invoice = createEntity({
|
|
181
|
+
* type: "Invoice",
|
|
182
|
+
* id: () => props.invoiceId,
|
|
183
|
+
* fetch: (id) => api.get(`/invoices/${id}`),
|
|
184
|
+
* normalize: (raw) => ({ ...raw, id: raw.invoice_id }),
|
|
185
|
+
* });
|
|
186
|
+
*
|
|
187
|
+
* return (
|
|
188
|
+
* <Show when={!invoice.isLoading()} fallback={<Spinner />}>
|
|
189
|
+
* <p>{invoice.data()?.amount}</p>
|
|
190
|
+
* </Show>
|
|
191
|
+
* );
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
declare function createEntity<TRaw, TEntity extends object>(opts: CreateEntityOptions<TRaw, TEntity>): CreateEntityReturn<TEntity>;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* createEntityList — SolidJS primitive for reactive normalized entity lists.
|
|
198
|
+
*
|
|
199
|
+
* Architecture:
|
|
200
|
+
* createEntityList (this file, Layer 2)
|
|
201
|
+
* └─ core engine fetchList (Layer 1 / transport)
|
|
202
|
+
* └─ useGraphStore (Layer 0 / Zustand graph)
|
|
203
|
+
*
|
|
204
|
+
* A Zustand `subscribe` bridges the graph's `lists[key]` slot + entity rows
|
|
205
|
+
* into a SolidJS `createStore`, giving SolidJS fine-grained per-row reactivity.
|
|
206
|
+
* Changing `queryKey()` re-serializes to a new list slot, triggering a fresh
|
|
207
|
+
* fetch while keeping existing data visible (stale-while-revalidate).
|
|
208
|
+
*/
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Creates a fine-grained SolidJS reactive primitive for an entity list.
|
|
212
|
+
*
|
|
213
|
+
* - Subscribes to `useGraphStore` to mirror list state (ids + pagination)
|
|
214
|
+
* and resolves the entity rows by joining ids → graph entities on each read.
|
|
215
|
+
* - Changing `queryKey()` triggers a new fetch and subscribes to the new list
|
|
216
|
+
* slot; the previous subscription is cleaned up automatically.
|
|
217
|
+
* - Supports pagination (`fetchNextPage`) and imperative refetch.
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```tsx
|
|
221
|
+
* const invoices = createEntityList({
|
|
222
|
+
* type: "Invoice",
|
|
223
|
+
* queryKey: () => ["invoices", { status: filter() }],
|
|
224
|
+
* fetch: (params) => api.getInvoices(params),
|
|
225
|
+
* normalize: (raw) => ({ id: raw.invoice_id, data: { ...raw } }),
|
|
226
|
+
* });
|
|
227
|
+
*
|
|
228
|
+
* return (
|
|
229
|
+
* <For each={invoices.items()}>
|
|
230
|
+
* {(invoice) => <InvoiceRow invoice={invoice} />}
|
|
231
|
+
* </For>
|
|
232
|
+
* );
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
235
|
+
declare function createEntityList<TRaw, TEntity extends object>(opts: CreateEntityListOptions<TRaw, TEntity>): CreateEntityListReturn<TEntity>;
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* createGraphStore — SolidJS fine-grained reactive bridge to the entire
|
|
239
|
+
* Zustand entity graph.
|
|
240
|
+
*
|
|
241
|
+
* Consumers that need ad-hoc access to the graph outside `createEntity` /
|
|
242
|
+
* `createEntityList` can use this to read any slice of `GraphState` as a
|
|
243
|
+
* SolidJS signal. The subscription is scoped to only the selected slice,
|
|
244
|
+
* so unrelated graph mutations do not trigger re-computations.
|
|
245
|
+
*
|
|
246
|
+
* @example
|
|
247
|
+
* ```ts
|
|
248
|
+
* // Read total entity count for a devtools panel.
|
|
249
|
+
* const entityCount = createGraphStore(
|
|
250
|
+
* (s) => Object.keys(s.entities).reduce((n, t) => n + Object.keys(s.entities[t]).length, 0),
|
|
251
|
+
* );
|
|
252
|
+
* console.log(entityCount()); // reactive SolidJS accessor
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Subscribe to a selector over the entity graph.
|
|
258
|
+
* Returns a SolidJS accessor that updates whenever the selected slice changes.
|
|
259
|
+
*
|
|
260
|
+
* @param selector - Pure function that maps the full `GraphState` to a derived value.
|
|
261
|
+
* @param equalityFn - Optional equality check (default: strict reference equality `===`).
|
|
262
|
+
*/
|
|
263
|
+
declare function createGraphStore<T>(selector: (state: GraphState) => T, equalityFn?: (a: T, b: T) => boolean): () => T;
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* GraphProvider utilities for SolidJS.
|
|
267
|
+
*
|
|
268
|
+
* Provides boot-time setup helpers for attaching global focus/reconnect
|
|
269
|
+
* listeners and configuring the engine from within a SolidJS application root.
|
|
270
|
+
*
|
|
271
|
+
* Usage in the application root (e.g. `src/index.tsx`):
|
|
272
|
+
*
|
|
273
|
+
* ```ts
|
|
274
|
+
* import { setupGraphProvider } from "@prometheus-ags/entity-graph-solid";
|
|
275
|
+
* import { configureEngine, attachGlobalListeners } from "@prometheus-ags/entity-graph-core";
|
|
276
|
+
*
|
|
277
|
+
* setupGraphProvider({
|
|
278
|
+
* staleTime: 60_000,
|
|
279
|
+
* revalidateOnFocus: true,
|
|
280
|
+
* });
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
|
|
284
|
+
interface GraphProviderOptions extends EngineOptions {
|
|
285
|
+
/**
|
|
286
|
+
* Whether to attach window focus/online listeners for stale-while-revalidate.
|
|
287
|
+
* Defaults to `true` in browser environments.
|
|
288
|
+
*/
|
|
289
|
+
attachListeners?: boolean;
|
|
290
|
+
/**
|
|
291
|
+
* Whether to start the periodic garbage collector.
|
|
292
|
+
* Defaults to `true`.
|
|
293
|
+
*/
|
|
294
|
+
startGc?: boolean;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Configures the entity-graph engine and wires browser-level listeners.
|
|
298
|
+
* Call this once at application startup before mounting the root component.
|
|
299
|
+
*
|
|
300
|
+
* Returns a cleanup function that stops the GC interval (useful for tests).
|
|
301
|
+
*/
|
|
302
|
+
declare function setupGraphProvider(opts?: GraphProviderOptions): () => void;
|
|
303
|
+
|
|
304
|
+
export { type CreateEntityListOptions, type CreateEntityListReturn, type CreateEntityOptions, type CreateEntityReturn, type GraphProviderOptions, createEntity, createEntityList, createGraphStore, setupGraphProvider };
|