@prometheus-ags/entity-graph-solid 3.0.0-alpha.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -0
- package/LICENSE +21 -0
- package/README.md +7 -1
- package/dist/{index.js → index.cjs} +19 -15
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.mjs +15 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +36 -15
- package/dist/index.js.map +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# @prometheus-ags/entity-graph-solid
|
|
2
|
+
|
|
3
|
+
## 3.0.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 30fc348: Require applications to supply one compatible entity-graph core instance across every stable framework binding, and verify the installed singleton through packed consumers.
|
|
8
|
+
- Updated dependencies [30fc348]
|
|
9
|
+
- Updated dependencies [7b64d76]
|
|
10
|
+
- @prometheus-ags/entity-graph-core@3.0.0
|
|
11
|
+
|
|
12
|
+
## 3.0.0-rc.1
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- Require applications to supply one compatible entity-graph core instance across every stable framework binding, and verify the installed singleton through packed consumers.
|
|
17
|
+
- @prometheus-ags/entity-graph-core@3.0.0-rc.1
|
|
18
|
+
|
|
19
|
+
## 3.0.0
|
|
20
|
+
|
|
21
|
+
### Major Changes
|
|
22
|
+
|
|
23
|
+
- v3.0.0 — Universal Platform Evolution
|
|
24
|
+
|
|
25
|
+
Evolves the React-only entity graph into a cross-platform, multi-framework,
|
|
26
|
+
AI-native, local-first ecosystem.
|
|
27
|
+
- **Monorepo split (breaking, but consumer-safe):** the framework-agnostic core
|
|
28
|
+
is extracted into `@prometheus-ags/entity-graph-core` (zero React). The
|
|
29
|
+
published `@prometheus-ags/prometheus-entity-management` package is the React
|
|
30
|
+
binding over the vanilla core. Existing React hook names remain available,
|
|
31
|
+
while core-only imperative consumers migrate from `useGraphStore.getState()`
|
|
32
|
+
to `graphStore.getState()`; the deprecated core alias remains through the
|
|
33
|
+
next-major removal window.
|
|
34
|
+
- **New framework bindings:** `entity-graph-svelte` (Svelte 5 runes),
|
|
35
|
+
`entity-graph-solid`, `entity-graph-web-components` (Lit 3), `entity-graph-alpine`,
|
|
36
|
+
`entity-graph-htmx` (Node SSE fragment server).
|
|
37
|
+
- **Peer sync:** `entity-graph-sync` with a pluggable SyncProvider — Yjs (default)
|
|
38
|
+
and Loro (reusing the 2.2.0 merge seam).
|
|
39
|
+
- **AI-native:** `entity-graph-a2a` plus `a2ui-react` with official A2UI
|
|
40
|
+
v0.9.1 rendering and default-deny graph actions; legacy
|
|
41
|
+
EntityChat/Copilot/Stream/Diff/Approval APIs live under `./ag-ui`.
|
|
42
|
+
- **Codegen contract:** `entity-graph-sdl` (schema → validated IR).
|
|
43
|
+
- **Native (published outside npm):** `entity-graph-cli` + `entity-graph-mcp`
|
|
44
|
+
(Rust, via crates.io), `entity-graph-tauri` (Tauri v2 plugin), and
|
|
45
|
+
`entity_graph_flutter` (Riverpod 3, via pub.dev).
|
|
46
|
+
|
|
47
|
+
All integrations ship as optional peer dependencies; the core bundle stays
|
|
48
|
+
`zustand + immer`.
|
|
49
|
+
|
|
50
|
+
### Patch Changes
|
|
51
|
+
|
|
52
|
+
- Updated dependencies
|
|
53
|
+
- @prometheus-ags/entity-graph-core@3.0.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Travis James / Prometheus AGS / KnowMe LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ SolidJS bindings for `@prometheus-ags/entity-graph-core` — fine-grained reacti
|
|
|
8
8
|
Component (SolidJS)
|
|
9
9
|
└─ createEntity / createEntityList (this package, Layer 2)
|
|
10
10
|
└─ core engine fetchEntity / fetchList (entity-graph-core, Layer 1)
|
|
11
|
-
└─
|
|
11
|
+
└─ graphStore (vanilla Zustand, Layer 0)
|
|
12
12
|
```
|
|
13
13
|
|
|
14
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.
|
|
@@ -19,6 +19,8 @@ Components access data only through `createEntity` / `createEntityList`. The sto
|
|
|
19
19
|
pnpm add @prometheus-ags/entity-graph-solid @prometheus-ags/entity-graph-core solid-js
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
`@prometheus-ags/entity-graph-core` is a required peer: install it explicitly so the application owns the one compatible graph instance. The Solid binding must not install a private core copy. See the [binding singleton contract](../../release/binding-singleton-contract.md).
|
|
23
|
+
|
|
22
24
|
## Quick Start
|
|
23
25
|
|
|
24
26
|
### 1. Configure the engine once at app boot
|
|
@@ -163,6 +165,10 @@ console.log(invoiceCount()); // reactive SolidJS signal
|
|
|
163
165
|
|
|
164
166
|
Returns a fine-grained SolidJS accessor that updates when the selected graph slice changes.
|
|
165
167
|
|
|
168
|
+
### `graphStore` and deprecated `useGraphStore`
|
|
169
|
+
|
|
170
|
+
`graphStore` is the vanilla StoreApi re-exported from the application-owned core and is intended for framework internals, adapters, and imperative integration code. The same-named Solid `createGraphStore(selector)` above returns a reactive accessor; it does not create an independent normalized graph. `useGraphStore` remains a deprecated core StoreApi alias for alpha migration and is not a Solid primitive.
|
|
171
|
+
|
|
166
172
|
### `setupGraphProvider(opts?)`
|
|
167
173
|
|
|
168
174
|
Configures the engine and attaches browser listeners. Call once at app startup. Returns a cleanup function.
|
|
@@ -30,7 +30,7 @@ function createEntity(opts) {
|
|
|
30
30
|
if (!id || !enabled) return null;
|
|
31
31
|
const engineOpts = entityGraphCore.getEngineOptions();
|
|
32
32
|
const effectiveStaleTime = staleTime ?? engineOpts.defaultStaleTime;
|
|
33
|
-
const graphState = entityGraphCore.
|
|
33
|
+
const graphState = entityGraphCore.graphStore.getState();
|
|
34
34
|
const cachedEntityState = graphState.entityStates[`${type}:${id}`];
|
|
35
35
|
const cachedEntity = graphState.readEntity(type, id);
|
|
36
36
|
const age = Date.now() - (cachedEntityState?.lastFetched ?? 0);
|
|
@@ -51,7 +51,7 @@ function createEntity(opts) {
|
|
|
51
51
|
},
|
|
52
52
|
{ ...entityGraphCore.getEngineOptions(), defaultStaleTime: effectiveStaleTime }
|
|
53
53
|
);
|
|
54
|
-
return entityGraphCore.
|
|
54
|
+
return entityGraphCore.graphStore.getState().readEntity(type, id);
|
|
55
55
|
});
|
|
56
56
|
solidJs.createEffect(() => {
|
|
57
57
|
const id = idAccessor();
|
|
@@ -59,14 +59,14 @@ function createEntity(opts) {
|
|
|
59
59
|
const subKey = `${type}:${id}`;
|
|
60
60
|
const token = entityGraphCore.registerSubscriber(subKey);
|
|
61
61
|
const syncFromGraph = () => {
|
|
62
|
-
const s = entityGraphCore.
|
|
62
|
+
const s = entityGraphCore.graphStore.getState();
|
|
63
63
|
const entity = s.readEntity(type, id);
|
|
64
64
|
const entityState = s.entityStates[subKey] ?? entityGraphCore.EMPTY_ENTITY_STATE;
|
|
65
65
|
setSnapshot("data", entity);
|
|
66
66
|
setSnapshot("entityState", (prev) => ({ ...prev, ...entityState }));
|
|
67
67
|
};
|
|
68
68
|
syncFromGraph();
|
|
69
|
-
const unsub = entityGraphCore.
|
|
69
|
+
const unsub = entityGraphCore.graphStore.subscribe(
|
|
70
70
|
(s) => ({
|
|
71
71
|
entity: s.readEntity(type, id),
|
|
72
72
|
entityState: s.entityStates[subKey] ?? entityGraphCore.EMPTY_ENTITY_STATE
|
|
@@ -86,7 +86,7 @@ function createEntity(opts) {
|
|
|
86
86
|
const refetch = async () => {
|
|
87
87
|
const id = idAccessor();
|
|
88
88
|
if (!id) return;
|
|
89
|
-
entityGraphCore.
|
|
89
|
+
entityGraphCore.graphStore.getState().setEntityStale(type, id, true);
|
|
90
90
|
await entityGraphCore.fetchEntity(
|
|
91
91
|
{ type, id, fetch, normalize, idField, onSuccess, onError },
|
|
92
92
|
entityGraphCore.getEngineOptions()
|
|
@@ -147,13 +147,13 @@ function createEntityList(opts) {
|
|
|
147
147
|
listState: { ...entityGraphCore.EMPTY_LIST_STATE }
|
|
148
148
|
});
|
|
149
149
|
function resolveItems(key) {
|
|
150
|
-
const s = entityGraphCore.
|
|
150
|
+
const s = entityGraphCore.graphStore.getState();
|
|
151
151
|
const list = s.lists[key];
|
|
152
152
|
if (!list) return [];
|
|
153
153
|
return list.ids.map((id) => s.readEntity(type, id)).filter((e) => e !== null);
|
|
154
154
|
}
|
|
155
155
|
async function doInitialFetch(key) {
|
|
156
|
-
const s = entityGraphCore.
|
|
156
|
+
const s = entityGraphCore.graphStore.getState();
|
|
157
157
|
const listState = s.lists[key];
|
|
158
158
|
const effectiveStaleTime = staleTime ?? entityGraphCore.getEngineOptions().defaultStaleTime;
|
|
159
159
|
const age = Date.now() - (listState?.lastFetched ?? 0);
|
|
@@ -178,14 +178,14 @@ function createEntityList(opts) {
|
|
|
178
178
|
const key = entityGraphCore.serializeKey(rawKey);
|
|
179
179
|
const enabled = enabledAccessor ? enabledAccessor() : true;
|
|
180
180
|
const syncFromGraph = () => {
|
|
181
|
-
const s = entityGraphCore.
|
|
181
|
+
const s = entityGraphCore.graphStore.getState();
|
|
182
182
|
const listState = s.lists[key] ?? entityGraphCore.EMPTY_LIST_STATE;
|
|
183
183
|
const items = resolveItems(key);
|
|
184
184
|
setSlice("listState", (prev) => ({ ...prev, ...listState }));
|
|
185
185
|
setSlice("items", items);
|
|
186
186
|
};
|
|
187
187
|
syncFromGraph();
|
|
188
|
-
const unsub = entityGraphCore.
|
|
188
|
+
const unsub = entityGraphCore.graphStore.subscribe(
|
|
189
189
|
(s) => {
|
|
190
190
|
const listState = s.lists[key] ?? entityGraphCore.EMPTY_LIST_STATE;
|
|
191
191
|
const entityBucket = s.entities[type];
|
|
@@ -207,7 +207,7 @@ function createEntityList(opts) {
|
|
|
207
207
|
});
|
|
208
208
|
async function fetchNextPage() {
|
|
209
209
|
const key = entityGraphCore.serializeKey(queryKeyAccessor());
|
|
210
|
-
const s = entityGraphCore.
|
|
210
|
+
const s = entityGraphCore.graphStore.getState();
|
|
211
211
|
const listState = s.lists[key] ?? entityGraphCore.EMPTY_LIST_STATE;
|
|
212
212
|
if (!listState.hasNextPage || listState.isFetchingMore) return;
|
|
213
213
|
await entityGraphCore.fetchList(
|
|
@@ -228,7 +228,7 @@ function createEntityList(opts) {
|
|
|
228
228
|
}
|
|
229
229
|
async function refetch() {
|
|
230
230
|
const key = entityGraphCore.serializeKey(queryKeyAccessor());
|
|
231
|
-
entityGraphCore.
|
|
231
|
+
entityGraphCore.graphStore.getState().setListStale(key, true);
|
|
232
232
|
await entityGraphCore.fetchList(
|
|
233
233
|
{
|
|
234
234
|
type,
|
|
@@ -266,9 +266,9 @@ function createEntityList(opts) {
|
|
|
266
266
|
}
|
|
267
267
|
function createGraphStore(selector, equalityFn) {
|
|
268
268
|
const eq = equalityFn ?? ((a, b) => a === b);
|
|
269
|
-
const initial = selector(entityGraphCore.
|
|
269
|
+
const initial = selector(entityGraphCore.graphStore.getState());
|
|
270
270
|
const [value, setValue] = solidJs.createSignal(initial);
|
|
271
|
-
const unsub = entityGraphCore.
|
|
271
|
+
const unsub = entityGraphCore.graphStore.subscribe(
|
|
272
272
|
selector,
|
|
273
273
|
(next, prev) => {
|
|
274
274
|
if (!eq(next, prev)) {
|
|
@@ -302,6 +302,10 @@ Object.defineProperty(exports, "TransientError", {
|
|
|
302
302
|
enumerable: true,
|
|
303
303
|
get: function () { return entityGraphCore.TransientError; }
|
|
304
304
|
});
|
|
305
|
+
Object.defineProperty(exports, "graphStore", {
|
|
306
|
+
enumerable: true,
|
|
307
|
+
get: function () { return entityGraphCore.graphStore; }
|
|
308
|
+
});
|
|
305
309
|
Object.defineProperty(exports, "useGraphStore", {
|
|
306
310
|
enumerable: true,
|
|
307
311
|
get: function () { return entityGraphCore.useGraphStore; }
|
|
@@ -310,5 +314,5 @@ exports.createEntity = createEntity;
|
|
|
310
314
|
exports.createEntityList = createEntityList;
|
|
311
315
|
exports.createGraphStore = createGraphStore;
|
|
312
316
|
exports.setupGraphProvider = setupGraphProvider;
|
|
313
|
-
//# sourceMappingURL=index.
|
|
314
|
-
//# sourceMappingURL=index.
|
|
317
|
+
//# sourceMappingURL=index.cjs.map
|
|
318
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/create-entity.ts","../src/create-entity-list.ts","../src/create-graph-store.ts","../src/graph-provider.ts"],"names":["createStore","EMPTY_ENTITY_STATE","createResource","getEngineOptions","graphStore","fetchEntity","createEffect","registerSubscriber","onCleanup","unregisterSubscriber","createSignal","EMPTY_LIST_STATE","fetchList","serializeKey","configureEngine","attachGlobalListeners","startGarbageCollector"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * createEntity — SolidJS primitive that bridges a single entity to the\n * normalized entity graph via `createResource`.\n *\n * Architecture:\n * createEntity (this file, Layer 2)\n * └─ core engine fetchEntity (Layer 1 / transport)\n * └─ graphStore (Layer 0 / Zustand graph)\n *\n * SolidJS `createResource` owns the async lifecycle; the graph is the\n * single source of truth for the entity payload. Once fetchEntity writes to\n * the graph, a Zustand subscription (via `createStore`) propagates the merged\n * entity back to SolidJS as a fine-grained reactive signal.\n */\n\nimport {\n createResource,\n createSignal,\n createEffect,\n onCleanup,\n} from \"solid-js\";\nimport { createStore } from \"solid-js/store\";\n\nimport {\n graphStore,\n fetchEntity,\n getEngineOptions,\n registerSubscriber,\n unregisterSubscriber,\n EMPTY_ENTITY_STATE,\n} from \"@prometheus-ags/entity-graph-core\";\nimport type { EntityState, EntityError } from \"@prometheus-ags/entity-graph-core\";\nimport type { CreateEntityOptions, CreateEntityReturn } from \"./types\";\n\n// ── Internal snapshot shape kept in the Solid store ─────────────────────────\n\ninterface EntitySnapshot<TEntity extends object> {\n data: TEntity | null;\n entityState: EntityState;\n}\n\n/**\n * Creates a fine-grained SolidJS reactive primitive for a single entity.\n *\n * - Uses `createResource` as the async driver so Suspense and ErrorBoundary\n * boundaries work naturally.\n * - Subscribes to the Zustand graph with `graphStore.subscribe` and mirrors\n * the merged entity into a `createStore`-backed signal for fine-grained\n * SolidJS reactivity (only the changed field triggers updates).\n * - Registers / unregisters a subscriber token with the core engine so idle\n * entities stop background revalidation when no components are observing them.\n *\n * @example\n * ```tsx\n * const invoice = createEntity({\n * type: \"Invoice\",\n * id: () => props.invoiceId,\n * fetch: (id) => api.get(`/invoices/${id}`),\n * normalize: (raw) => ({ ...raw, id: raw.invoice_id }),\n * });\n *\n * return (\n * <Show when={!invoice.isLoading()} fallback={<Spinner />}>\n * <p>{invoice.data()?.amount}</p>\n * </Show>\n * );\n * ```\n */\nexport function createEntity<TRaw, TEntity extends object>(\n opts: CreateEntityOptions<TRaw, TEntity>,\n): CreateEntityReturn<TEntity> {\n const {\n type,\n id: idAccessor,\n fetch,\n normalize,\n idField = \"id\",\n staleTime,\n enabled: enabledAccessor,\n onSuccess,\n onError,\n } = opts;\n\n // ── Reactive source for createResource ─────────────────────────────────────\n // Returns [id, enabled] so resource re-fires when either changes.\n const source = (): [string | null | undefined, boolean] => {\n const id = idAccessor();\n const enabled = enabledAccessor ? enabledAccessor() : true;\n return [id ?? null, enabled];\n };\n\n // ── Solid store: mirrors graph state for fine-grained reactivity ────────────\n const [snapshot, setSnapshot] = createStore<EntitySnapshot<TEntity>>({\n data: null,\n entityState: { ...EMPTY_ENTITY_STATE },\n });\n\n // ── createResource: drives Suspense / ErrorBoundary + triggers fetches ──────\n const [resource] = createResource(source, async ([id, enabled]) => {\n if (!id || !enabled) return null;\n\n const engineOpts = getEngineOptions();\n const effectiveStaleTime = staleTime ?? engineOpts.defaultStaleTime;\n\n // Check if cached data is fresh enough to skip a network round-trip.\n const graphState = graphStore.getState();\n const cachedEntityState = graphState.entityStates[`${type}:${id}`];\n const cachedEntity = graphState.readEntity<TEntity>(type, id);\n const age = Date.now() - (cachedEntityState?.lastFetched ?? 0);\n\n if (cachedEntity && age < effectiveStaleTime && !cachedEntityState?.stale) {\n // Fresh cache hit: update snapshot and skip fetch.\n setSnapshot(\"data\", cachedEntity);\n setSnapshot(\"entityState\", (s) => ({ ...s, ...cachedEntityState }));\n return cachedEntity;\n }\n\n // Delegate to core engine — handles dedup, retries, and graph writes.\n await fetchEntity(\n {\n type,\n id,\n fetch,\n normalize,\n idField,\n onSuccess,\n onError,\n },\n { ...getEngineOptions(), defaultStaleTime: effectiveStaleTime },\n );\n\n // Return the graph value after the fetch completes.\n return graphStore.getState().readEntity<TEntity>(type, id);\n });\n\n // ── Subscribe to Zustand graph for live fine-grained updates ───────────────\n createEffect(() => {\n const id = idAccessor();\n if (!id) return;\n\n const subKey = `${type}:${id}`;\n const token = registerSubscriber(subKey);\n\n // Sync initial graph state into the Solid store.\n const syncFromGraph = () => {\n const s = graphStore.getState();\n const entity = s.readEntity<TEntity>(type, id);\n const entityState = s.entityStates[subKey] ?? EMPTY_ENTITY_STATE;\n setSnapshot(\"data\", entity);\n setSnapshot(\"entityState\", (prev) => ({ ...prev, ...entityState }));\n };\n\n syncFromGraph();\n\n // Subscribe to graph changes that affect this entity.\n const unsub = graphStore.subscribe(\n (s) => ({\n entity: s.readEntity<TEntity>(type, id),\n entityState: s.entityStates[subKey] ?? EMPTY_ENTITY_STATE,\n }),\n (next) => {\n setSnapshot(\"data\", next.entity);\n setSnapshot(\"entityState\", (prev) => ({ ...prev, ...next.entityState }));\n },\n { equalityFn: shallowEqual },\n );\n\n onCleanup(() => {\n unsub();\n unregisterSubscriber(subKey, token);\n });\n });\n\n // ── Imperative refetch ──────────────────────────────────────────────────────\n const [_refetchTrigger, setRefetchTrigger] = createSignal(0);\n\n const refetch = async () => {\n const id = idAccessor();\n if (!id) return;\n graphStore.getState().setEntityStale(type, id, true);\n await fetchEntity(\n { type, id, fetch, normalize, idField, onSuccess, onError },\n getEngineOptions(),\n );\n };\n\n // ── Derived signals ──────────────────────────────────────────────────────────\n const isLoading = () => {\n const res = resource();\n const es = snapshot.entityState;\n return resource.loading || (es.isFetching && snapshot.data === null);\n };\n\n const isRefetching = () => {\n const es = snapshot.entityState;\n return es.isFetching && snapshot.data !== null;\n };\n\n const error = () => snapshot.entityState.error;\n\n // Typed error: pull from the graph's entityStates — the engine writes\n // TerminalError / TransientError instances into listState.lastError for lists,\n // and for single entities the error string is sufficient. We re-expose it as\n // EntityError | null for API consistency (callers may cast if needed).\n const typedError = (): EntityError | null => {\n const errStr = snapshot.entityState.error;\n if (!errStr) return null;\n // Surface the string-backed error as a plain Error (full typed errors are on\n // lists; single-entity fetch only has string form in the graph).\n return null;\n };\n\n return {\n data: () => snapshot.data,\n isLoading,\n isRefetching,\n error,\n typedError,\n refetch,\n entityState: () => snapshot.entityState,\n };\n}\n\n// ── Shallow equality helper for Zustand selector ─────────────────────────────\n\nfunction shallowEqual<T extends Record<string, unknown>>(a: T, b: T): boolean {\n if (a === b) return true;\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n for (const k of keysA) {\n if (a[k] !== b[k]) return false;\n }\n return true;\n}\n","/**\n * createEntityList — SolidJS primitive for reactive normalized entity lists.\n *\n * Architecture:\n * createEntityList (this file, Layer 2)\n * └─ core engine fetchList (Layer 1 / transport)\n * └─ graphStore (Layer 0 / Zustand graph)\n *\n * A Zustand `subscribe` bridges the graph's `lists[key]` slot + entity rows\n * into a SolidJS `createStore`, giving SolidJS fine-grained per-row reactivity.\n * Changing `queryKey()` re-serializes to a new list slot, triggering a fresh\n * fetch while keeping existing data visible (stale-while-revalidate).\n */\n\nimport { createEffect, onCleanup } from \"solid-js\";\nimport { createStore } from \"solid-js/store\";\n\nimport {\n graphStore,\n fetchList,\n serializeKey,\n getEngineOptions,\n EMPTY_LIST_STATE,\n} from \"@prometheus-ags/entity-graph-core\";\nimport type { EntityId, ListState } from \"@prometheus-ags/entity-graph-core\";\nimport type { CreateEntityListOptions, CreateEntityListReturn } from \"./types\";\n\n// ── Internal store shape ──────────────────────────────────────────────────────\n\ninterface ListSlice<TEntity extends object> {\n items: TEntity[];\n listState: ListState;\n}\n\n// ── Equality helper for list subscription ────────────────────────────────────\n\nfunction listSliceEqual(\n a: { listState: ListState; entityBucket: Record<string, unknown> | undefined },\n b: { listState: ListState; entityBucket: Record<string, unknown> | undefined },\n): boolean {\n return a.listState === b.listState && a.entityBucket === b.entityBucket;\n}\n\n/**\n * Creates a fine-grained SolidJS reactive primitive for an entity list.\n *\n * - Subscribes to `graphStore` to mirror list state (ids + pagination)\n * and resolves the entity rows by joining ids → graph entities on each read.\n * - Changing `queryKey()` triggers a new fetch and subscribes to the new list\n * slot; the previous subscription is cleaned up automatically.\n * - Supports pagination (`fetchNextPage`) and imperative refetch.\n *\n * @example\n * ```tsx\n * const invoices = createEntityList({\n * type: \"Invoice\",\n * queryKey: () => [\"invoices\", { status: filter() }],\n * fetch: (params) => api.getInvoices(params),\n * normalize: (raw) => ({ id: raw.invoice_id, data: { ...raw } }),\n * });\n *\n * return (\n * <For each={invoices.items()}>\n * {(invoice) => <InvoiceRow invoice={invoice} />}\n * </For>\n * );\n * ```\n */\nexport function createEntityList<TRaw, TEntity extends object>(\n opts: CreateEntityListOptions<TRaw, TEntity>,\n): CreateEntityListReturn<TEntity> {\n const {\n type,\n queryKey: queryKeyAccessor,\n fetch,\n normalize,\n mode = \"replace\",\n staleTime,\n enabled: enabledAccessor,\n onSuccess,\n onError,\n } = opts;\n\n // ── Fine-grained Solid store ─────────────────────────────────────────────────\n const [slice, setSlice] = createStore<ListSlice<TEntity>>({\n items: [],\n listState: { ...EMPTY_LIST_STATE },\n });\n\n // ── Helper: resolve items from current graph state for a given key ───────────\n function resolveItems(key: string): TEntity[] {\n const s = graphStore.getState();\n const list = s.lists[key];\n if (!list) return [];\n return list.ids\n .map((id: EntityId) => s.readEntity<TEntity>(type, id))\n .filter((e): e is TEntity => e !== null);\n }\n\n // ── Initial / stale-while-revalidate fetch ────────────────────────────────────\n // Declared before createEffect so it is always defined when the effect runs\n // (the effect mock executes the callback synchronously in tests).\n async function doInitialFetch(key: string): Promise<void> {\n const s = graphStore.getState();\n const listState = s.lists[key];\n const effectiveStaleTime = staleTime ?? getEngineOptions().defaultStaleTime;\n const age = Date.now() - (listState?.lastFetched ?? 0);\n const isFresh = listState && !listState.stale && age < effectiveStaleTime;\n if (isFresh) return;\n\n await fetchList(\n {\n type,\n queryKey: JSON.parse(key) as unknown[],\n fetch,\n normalize,\n mode,\n onSuccess: onSuccess as ((r: { items: TRaw[]; total?: number | null }) => void) | undefined,\n onError,\n },\n {},\n getEngineOptions(),\n );\n }\n\n // ── Reactive effect: subscribe to graph whenever queryKey changes ─────────────\n createEffect(() => {\n const rawKey = queryKeyAccessor();\n const key = serializeKey(rawKey);\n const enabled = enabledAccessor ? enabledAccessor() : true;\n\n // Sync snapshot immediately with whatever is already in the graph.\n const syncFromGraph = () => {\n const s = graphStore.getState();\n const listState = s.lists[key] ?? EMPTY_LIST_STATE;\n const items = resolveItems(key);\n setSlice(\"listState\", (prev) => ({ ...prev, ...listState }));\n setSlice(\"items\", items);\n };\n\n syncFromGraph();\n\n // Subscribe to any graph change that could affect this list.\n // Solid's fine-grained update system means only changed fields propagate.\n const unsub = graphStore.subscribe(\n (s) => {\n const listState = s.lists[key] ?? EMPTY_LIST_STATE;\n const entityBucket = s.entities[type];\n return { listState, entityBucket };\n },\n ({ listState }) => {\n const items = resolveItems(key);\n setSlice(\"listState\", (prev) => ({ ...prev, ...listState }));\n setSlice(\"items\", items);\n },\n { equalityFn: listSliceEqual },\n );\n\n // Kick off the fetch when enabled and data may be stale.\n if (enabled && (fetch || normalize)) {\n void doInitialFetch(key);\n }\n\n onCleanup(() => {\n unsub();\n });\n });\n\n // ── fetchNextPage ─────────────────────────────────────────────────────────────\n async function fetchNextPage(): Promise<void> {\n const key = serializeKey(queryKeyAccessor());\n const s = graphStore.getState();\n const listState = s.lists[key] ?? EMPTY_LIST_STATE;\n if (!listState.hasNextPage || listState.isFetchingMore) return;\n\n await fetchList(\n {\n type,\n queryKey: JSON.parse(key) as unknown[],\n fetch,\n normalize,\n mode: \"append\",\n onSuccess: onSuccess as ((r: { items: TRaw[]; total?: number | null }) => void) | undefined,\n onError,\n },\n { cursor: listState.nextCursor ?? undefined },\n getEngineOptions(),\n true, // isLoadMore\n );\n }\n\n // ── Imperative refetch ────────────────────────────────────────────────────────\n async function refetch(): Promise<void> {\n const key = serializeKey(queryKeyAccessor());\n graphStore.getState().setListStale(key, true);\n await fetchList(\n {\n type,\n queryKey: JSON.parse(key) as unknown[],\n fetch,\n normalize,\n mode: \"replace\",\n onSuccess: onSuccess as ((r: { items: TRaw[]; total?: number | null }) => void) | undefined,\n onError,\n },\n {},\n getEngineOptions(),\n );\n }\n\n // ── Derived signals ───────────────────────────────────────────────────────────\n const isLoading = () => slice.listState.isFetching && slice.items.length === 0;\n const isLoadingMore = () => slice.listState.isFetchingMore;\n const error = () => slice.listState.error;\n const typedError = () => slice.listState.lastError;\n const total = () => slice.listState.total;\n const hasNextPage = () => slice.listState.hasNextPage;\n const hasPrevPage = () => slice.listState.hasPrevPage;\n\n return {\n items: () => slice.items,\n isLoading,\n isLoadingMore,\n error,\n typedError,\n total,\n hasNextPage,\n hasPrevPage,\n fetchNextPage,\n refetch,\n listState: () => slice.listState,\n };\n}\n","/**\n * createGraphStore — SolidJS fine-grained reactive bridge to the entire\n * Zustand entity graph.\n *\n * Consumers that need ad-hoc access to the graph outside `createEntity` /\n * `createEntityList` can use this to read any slice of `GraphState` as a\n * SolidJS signal. The subscription is scoped to only the selected slice,\n * so unrelated graph mutations do not trigger re-computations.\n *\n * @example\n * ```ts\n * // Read total entity count for a devtools panel.\n * const entityCount = createGraphStore(\n * (s) => Object.keys(s.entities).reduce((n, t) => n + Object.keys(s.entities[t]).length, 0),\n * );\n * console.log(entityCount()); // reactive SolidJS accessor\n * ```\n */\n\nimport { createSignal, onCleanup } from \"solid-js\";\nimport { graphStore } from \"@prometheus-ags/entity-graph-core\";\nimport type { GraphState } from \"@prometheus-ags/entity-graph-core\";\n\n/**\n * Subscribe to a selector over the entity graph.\n * Returns a SolidJS accessor that updates whenever the selected slice changes.\n *\n * @param selector - Pure function that maps the full `GraphState` to a derived value.\n * @param equalityFn - Optional equality check (default: strict reference equality `===`).\n */\nexport function createGraphStore<T>(\n selector: (state: GraphState) => T,\n equalityFn?: (a: T, b: T) => boolean,\n): () => T {\n const eq = equalityFn ?? ((a, b) => a === b);\n const initial = selector(graphStore.getState());\n const [value, setValue] = createSignal<T>(initial);\n\n const unsub = graphStore.subscribe(\n selector,\n (next, prev) => {\n if (!eq(next, prev)) {\n setValue(() => next);\n }\n },\n );\n\n onCleanup(() => unsub());\n\n return value;\n}\n","/**\n * GraphProvider utilities for SolidJS.\n *\n * Provides boot-time setup helpers for attaching global focus/reconnect\n * listeners and configuring the engine from within a SolidJS application root.\n *\n * Usage in the application root (e.g. `src/index.tsx`):\n *\n * ```ts\n * import { setupGraphProvider } from \"@prometheus-ags/entity-graph-solid\";\n * import { configureEngine, attachGlobalListeners } from \"@prometheus-ags/entity-graph-core\";\n *\n * setupGraphProvider({\n * staleTime: 60_000,\n * revalidateOnFocus: true,\n * });\n * ```\n */\n\nimport {\n configureEngine,\n attachGlobalListeners,\n startGarbageCollector,\n} from \"@prometheus-ags/entity-graph-core\";\nimport type { EngineOptions } from \"@prometheus-ags/entity-graph-core\";\n\nexport interface GraphProviderOptions extends EngineOptions {\n /**\n * Whether to attach window focus/online listeners for stale-while-revalidate.\n * Defaults to `true` in browser environments.\n */\n attachListeners?: boolean;\n /**\n * Whether to start the periodic garbage collector.\n * Defaults to `true`.\n */\n startGc?: boolean;\n}\n\n/**\n * Configures the entity-graph engine and wires browser-level listeners.\n * Call this once at application startup before mounting the root component.\n *\n * Returns a cleanup function that stops the GC interval (useful for tests).\n */\nexport function setupGraphProvider(opts: GraphProviderOptions = {}): () => void {\n const { attachListeners = true, startGc = true, ...engineOpts } = opts;\n\n if (Object.keys(engineOpts).length > 0) {\n configureEngine(engineOpts);\n }\n\n if (attachListeners && typeof window !== \"undefined\") {\n attachGlobalListeners();\n }\n\n let stopGc: (() => void) | undefined;\n if (startGc && typeof window !== \"undefined\") {\n stopGc = startGarbageCollector();\n }\n\n return () => stopGc?.();\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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';
|
|
2
|
+
export { EntityError, EntityId, EntityState, EntityType, FilterSpec, ListState, SortSpec, TerminalError, TransientError, ViewDescriptor, graphStore, useGraphStore } from '@prometheus-ags/entity-graph-core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Shared option types for SolidJS entity-graph bindings.
|
|
@@ -156,7 +156,7 @@ interface CreateEntityListReturn<TEntity extends object> {
|
|
|
156
156
|
* Architecture:
|
|
157
157
|
* createEntity (this file, Layer 2)
|
|
158
158
|
* └─ core engine fetchEntity (Layer 1 / transport)
|
|
159
|
-
* └─
|
|
159
|
+
* └─ graphStore (Layer 0 / Zustand graph)
|
|
160
160
|
*
|
|
161
161
|
* SolidJS `createResource` owns the async lifecycle; the graph is the
|
|
162
162
|
* single source of truth for the entity payload. Once fetchEntity writes to
|
|
@@ -169,7 +169,7 @@ interface CreateEntityListReturn<TEntity extends object> {
|
|
|
169
169
|
*
|
|
170
170
|
* - Uses `createResource` as the async driver so Suspense and ErrorBoundary
|
|
171
171
|
* boundaries work naturally.
|
|
172
|
-
* - Subscribes to the Zustand graph with `
|
|
172
|
+
* - Subscribes to the Zustand graph with `graphStore.subscribe` and mirrors
|
|
173
173
|
* the merged entity into a `createStore`-backed signal for fine-grained
|
|
174
174
|
* SolidJS reactivity (only the changed field triggers updates).
|
|
175
175
|
* - Registers / unregisters a subscriber token with the core engine so idle
|
|
@@ -199,7 +199,7 @@ declare function createEntity<TRaw, TEntity extends object>(opts: CreateEntityOp
|
|
|
199
199
|
* Architecture:
|
|
200
200
|
* createEntityList (this file, Layer 2)
|
|
201
201
|
* └─ core engine fetchList (Layer 1 / transport)
|
|
202
|
-
* └─
|
|
202
|
+
* └─ graphStore (Layer 0 / Zustand graph)
|
|
203
203
|
*
|
|
204
204
|
* A Zustand `subscribe` bridges the graph's `lists[key]` slot + entity rows
|
|
205
205
|
* into a SolidJS `createStore`, giving SolidJS fine-grained per-row reactivity.
|
|
@@ -210,7 +210,7 @@ declare function createEntity<TRaw, TEntity extends object>(opts: CreateEntityOp
|
|
|
210
210
|
/**
|
|
211
211
|
* Creates a fine-grained SolidJS reactive primitive for an entity list.
|
|
212
212
|
*
|
|
213
|
-
* - Subscribes to `
|
|
213
|
+
* - Subscribes to `graphStore` to mirror list state (ids + pagination)
|
|
214
214
|
* and resolves the entity rows by joining ids → graph entities on each read.
|
|
215
215
|
* - Changing `queryKey()` triggers a new fetch and subscribes to the new list
|
|
216
216
|
* slot; the previous subscription is cleaned up automatically.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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';
|
|
2
|
+
export { EntityError, EntityId, EntityState, EntityType, FilterSpec, ListState, SortSpec, TerminalError, TransientError, ViewDescriptor, graphStore, useGraphStore } from '@prometheus-ags/entity-graph-core';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Shared option types for SolidJS entity-graph bindings.
|
|
@@ -156,7 +156,7 @@ interface CreateEntityListReturn<TEntity extends object> {
|
|
|
156
156
|
* Architecture:
|
|
157
157
|
* createEntity (this file, Layer 2)
|
|
158
158
|
* └─ core engine fetchEntity (Layer 1 / transport)
|
|
159
|
-
* └─
|
|
159
|
+
* └─ graphStore (Layer 0 / Zustand graph)
|
|
160
160
|
*
|
|
161
161
|
* SolidJS `createResource` owns the async lifecycle; the graph is the
|
|
162
162
|
* single source of truth for the entity payload. Once fetchEntity writes to
|
|
@@ -169,7 +169,7 @@ interface CreateEntityListReturn<TEntity extends object> {
|
|
|
169
169
|
*
|
|
170
170
|
* - Uses `createResource` as the async driver so Suspense and ErrorBoundary
|
|
171
171
|
* boundaries work naturally.
|
|
172
|
-
* - Subscribes to the Zustand graph with `
|
|
172
|
+
* - Subscribes to the Zustand graph with `graphStore.subscribe` and mirrors
|
|
173
173
|
* the merged entity into a `createStore`-backed signal for fine-grained
|
|
174
174
|
* SolidJS reactivity (only the changed field triggers updates).
|
|
175
175
|
* - Registers / unregisters a subscriber token with the core engine so idle
|
|
@@ -199,7 +199,7 @@ declare function createEntity<TRaw, TEntity extends object>(opts: CreateEntityOp
|
|
|
199
199
|
* Architecture:
|
|
200
200
|
* createEntityList (this file, Layer 2)
|
|
201
201
|
* └─ core engine fetchList (Layer 1 / transport)
|
|
202
|
-
* └─
|
|
202
|
+
* └─ graphStore (Layer 0 / Zustand graph)
|
|
203
203
|
*
|
|
204
204
|
* A Zustand `subscribe` bridges the graph's `lists[key]` slot + entity rows
|
|
205
205
|
* into a SolidJS `createStore`, giving SolidJS fine-grained per-row reactivity.
|
|
@@ -210,7 +210,7 @@ declare function createEntity<TRaw, TEntity extends object>(opts: CreateEntityOp
|
|
|
210
210
|
/**
|
|
211
211
|
* Creates a fine-grained SolidJS reactive primitive for an entity list.
|
|
212
212
|
*
|
|
213
|
-
* - Subscribes to `
|
|
213
|
+
* - Subscribes to `graphStore` to mirror list state (ids + pagination)
|
|
214
214
|
* and resolves the entity rows by joining ids → graph entities on each read.
|
|
215
215
|
* - Changing `queryKey()` triggers a new fetch and subscribes to the new list
|
|
216
216
|
* slot; the previous subscription is cleaned up automatically.
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createResource, createEffect, onCleanup, createSignal } from 'solid-js';
|
|
2
2
|
import { createStore } from 'solid-js/store';
|
|
3
|
-
import { EMPTY_ENTITY_STATE, getEngineOptions,
|
|
4
|
-
export { TerminalError, TransientError, useGraphStore } from '@prometheus-ags/entity-graph-core';
|
|
3
|
+
import { EMPTY_ENTITY_STATE, getEngineOptions, graphStore, fetchEntity, registerSubscriber, unregisterSubscriber, EMPTY_LIST_STATE, serializeKey, configureEngine, attachGlobalListeners, startGarbageCollector, fetchList } from '@prometheus-ags/entity-graph-core';
|
|
4
|
+
export { TerminalError, TransientError, graphStore, useGraphStore } from '@prometheus-ags/entity-graph-core';
|
|
5
5
|
|
|
6
6
|
// src/create-entity.ts
|
|
7
7
|
function createEntity(opts) {
|
|
@@ -29,7 +29,7 @@ function createEntity(opts) {
|
|
|
29
29
|
if (!id || !enabled) return null;
|
|
30
30
|
const engineOpts = getEngineOptions();
|
|
31
31
|
const effectiveStaleTime = staleTime ?? engineOpts.defaultStaleTime;
|
|
32
|
-
const graphState =
|
|
32
|
+
const graphState = graphStore.getState();
|
|
33
33
|
const cachedEntityState = graphState.entityStates[`${type}:${id}`];
|
|
34
34
|
const cachedEntity = graphState.readEntity(type, id);
|
|
35
35
|
const age = Date.now() - (cachedEntityState?.lastFetched ?? 0);
|
|
@@ -50,7 +50,7 @@ function createEntity(opts) {
|
|
|
50
50
|
},
|
|
51
51
|
{ ...getEngineOptions(), defaultStaleTime: effectiveStaleTime }
|
|
52
52
|
);
|
|
53
|
-
return
|
|
53
|
+
return graphStore.getState().readEntity(type, id);
|
|
54
54
|
});
|
|
55
55
|
createEffect(() => {
|
|
56
56
|
const id = idAccessor();
|
|
@@ -58,14 +58,14 @@ function createEntity(opts) {
|
|
|
58
58
|
const subKey = `${type}:${id}`;
|
|
59
59
|
const token = registerSubscriber(subKey);
|
|
60
60
|
const syncFromGraph = () => {
|
|
61
|
-
const s =
|
|
61
|
+
const s = graphStore.getState();
|
|
62
62
|
const entity = s.readEntity(type, id);
|
|
63
63
|
const entityState = s.entityStates[subKey] ?? EMPTY_ENTITY_STATE;
|
|
64
64
|
setSnapshot("data", entity);
|
|
65
65
|
setSnapshot("entityState", (prev) => ({ ...prev, ...entityState }));
|
|
66
66
|
};
|
|
67
67
|
syncFromGraph();
|
|
68
|
-
const unsub =
|
|
68
|
+
const unsub = graphStore.subscribe(
|
|
69
69
|
(s) => ({
|
|
70
70
|
entity: s.readEntity(type, id),
|
|
71
71
|
entityState: s.entityStates[subKey] ?? EMPTY_ENTITY_STATE
|
|
@@ -85,7 +85,7 @@ function createEntity(opts) {
|
|
|
85
85
|
const refetch = async () => {
|
|
86
86
|
const id = idAccessor();
|
|
87
87
|
if (!id) return;
|
|
88
|
-
|
|
88
|
+
graphStore.getState().setEntityStale(type, id, true);
|
|
89
89
|
await fetchEntity(
|
|
90
90
|
{ type, id, fetch, normalize, idField, onSuccess, onError },
|
|
91
91
|
getEngineOptions()
|
|
@@ -146,13 +146,13 @@ function createEntityList(opts) {
|
|
|
146
146
|
listState: { ...EMPTY_LIST_STATE }
|
|
147
147
|
});
|
|
148
148
|
function resolveItems(key) {
|
|
149
|
-
const s =
|
|
149
|
+
const s = graphStore.getState();
|
|
150
150
|
const list = s.lists[key];
|
|
151
151
|
if (!list) return [];
|
|
152
152
|
return list.ids.map((id) => s.readEntity(type, id)).filter((e) => e !== null);
|
|
153
153
|
}
|
|
154
154
|
async function doInitialFetch(key) {
|
|
155
|
-
const s =
|
|
155
|
+
const s = graphStore.getState();
|
|
156
156
|
const listState = s.lists[key];
|
|
157
157
|
const effectiveStaleTime = staleTime ?? getEngineOptions().defaultStaleTime;
|
|
158
158
|
const age = Date.now() - (listState?.lastFetched ?? 0);
|
|
@@ -177,14 +177,14 @@ function createEntityList(opts) {
|
|
|
177
177
|
const key = serializeKey(rawKey);
|
|
178
178
|
const enabled = enabledAccessor ? enabledAccessor() : true;
|
|
179
179
|
const syncFromGraph = () => {
|
|
180
|
-
const s =
|
|
180
|
+
const s = graphStore.getState();
|
|
181
181
|
const listState = s.lists[key] ?? EMPTY_LIST_STATE;
|
|
182
182
|
const items = resolveItems(key);
|
|
183
183
|
setSlice("listState", (prev) => ({ ...prev, ...listState }));
|
|
184
184
|
setSlice("items", items);
|
|
185
185
|
};
|
|
186
186
|
syncFromGraph();
|
|
187
|
-
const unsub =
|
|
187
|
+
const unsub = graphStore.subscribe(
|
|
188
188
|
(s) => {
|
|
189
189
|
const listState = s.lists[key] ?? EMPTY_LIST_STATE;
|
|
190
190
|
const entityBucket = s.entities[type];
|
|
@@ -206,7 +206,7 @@ function createEntityList(opts) {
|
|
|
206
206
|
});
|
|
207
207
|
async function fetchNextPage() {
|
|
208
208
|
const key = serializeKey(queryKeyAccessor());
|
|
209
|
-
const s =
|
|
209
|
+
const s = graphStore.getState();
|
|
210
210
|
const listState = s.lists[key] ?? EMPTY_LIST_STATE;
|
|
211
211
|
if (!listState.hasNextPage || listState.isFetchingMore) return;
|
|
212
212
|
await fetchList(
|
|
@@ -227,7 +227,7 @@ function createEntityList(opts) {
|
|
|
227
227
|
}
|
|
228
228
|
async function refetch() {
|
|
229
229
|
const key = serializeKey(queryKeyAccessor());
|
|
230
|
-
|
|
230
|
+
graphStore.getState().setListStale(key, true);
|
|
231
231
|
await fetchList(
|
|
232
232
|
{
|
|
233
233
|
type,
|
|
@@ -265,9 +265,9 @@ function createEntityList(opts) {
|
|
|
265
265
|
}
|
|
266
266
|
function createGraphStore(selector, equalityFn) {
|
|
267
267
|
const eq = equalityFn ?? ((a, b) => a === b);
|
|
268
|
-
const initial = selector(
|
|
268
|
+
const initial = selector(graphStore.getState());
|
|
269
269
|
const [value, setValue] = createSignal(initial);
|
|
270
|
-
const unsub =
|
|
270
|
+
const unsub = graphStore.subscribe(
|
|
271
271
|
selector,
|
|
272
272
|
(next, prev) => {
|
|
273
273
|
if (!eq(next, prev)) {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/create-entity.ts","../src/create-entity-list.ts","../src/create-graph-store.ts","../src/graph-provider.ts"],"names":["createStore","useGraphStore","getEngineOptions","createEffect","onCleanup","createSignal"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * createEntity — SolidJS primitive that bridges a single entity to the\n * normalized entity graph via `createResource`.\n *\n * Architecture:\n * createEntity (this file, Layer 2)\n * └─ core engine fetchEntity (Layer 1 / transport)\n * └─ useGraphStore (Layer 0 / Zustand graph)\n *\n * SolidJS `createResource` owns the async lifecycle; the graph is the\n * single source of truth for the entity payload. Once fetchEntity writes to\n * the graph, a Zustand subscription (via `createStore`) propagates the merged\n * entity back to SolidJS as a fine-grained reactive signal.\n */\n\nimport {\n createResource,\n createSignal,\n createEffect,\n onCleanup,\n} from \"solid-js\";\nimport { createStore } from \"solid-js/store\";\n\nimport {\n useGraphStore,\n fetchEntity,\n getEngineOptions,\n registerSubscriber,\n unregisterSubscriber,\n EMPTY_ENTITY_STATE,\n} from \"@prometheus-ags/entity-graph-core\";\nimport type { EntityState, EntityError } from \"@prometheus-ags/entity-graph-core\";\nimport type { CreateEntityOptions, CreateEntityReturn } from \"./types\";\n\n// ── Internal snapshot shape kept in the Solid store ─────────────────────────\n\ninterface EntitySnapshot<TEntity extends object> {\n data: TEntity | null;\n entityState: EntityState;\n}\n\n/**\n * Creates a fine-grained SolidJS reactive primitive for a single entity.\n *\n * - Uses `createResource` as the async driver so Suspense and ErrorBoundary\n * boundaries work naturally.\n * - Subscribes to the Zustand graph with `useGraphStore.subscribe` and mirrors\n * the merged entity into a `createStore`-backed signal for fine-grained\n * SolidJS reactivity (only the changed field triggers updates).\n * - Registers / unregisters a subscriber token with the core engine so idle\n * entities stop background revalidation when no components are observing them.\n *\n * @example\n * ```tsx\n * const invoice = createEntity({\n * type: \"Invoice\",\n * id: () => props.invoiceId,\n * fetch: (id) => api.get(`/invoices/${id}`),\n * normalize: (raw) => ({ ...raw, id: raw.invoice_id }),\n * });\n *\n * return (\n * <Show when={!invoice.isLoading()} fallback={<Spinner />}>\n * <p>{invoice.data()?.amount}</p>\n * </Show>\n * );\n * ```\n */\nexport function createEntity<TRaw, TEntity extends object>(\n opts: CreateEntityOptions<TRaw, TEntity>,\n): CreateEntityReturn<TEntity> {\n const {\n type,\n id: idAccessor,\n fetch,\n normalize,\n idField = \"id\",\n staleTime,\n enabled: enabledAccessor,\n onSuccess,\n onError,\n } = opts;\n\n // ── Reactive source for createResource ─────────────────────────────────────\n // Returns [id, enabled] so resource re-fires when either changes.\n const source = (): [string | null | undefined, boolean] => {\n const id = idAccessor();\n const enabled = enabledAccessor ? enabledAccessor() : true;\n return [id ?? null, enabled];\n };\n\n // ── Solid store: mirrors graph state for fine-grained reactivity ────────────\n const [snapshot, setSnapshot] = createStore<EntitySnapshot<TEntity>>({\n data: null,\n entityState: { ...EMPTY_ENTITY_STATE },\n });\n\n // ── createResource: drives Suspense / ErrorBoundary + triggers fetches ──────\n const [resource] = createResource(source, async ([id, enabled]) => {\n if (!id || !enabled) return null;\n\n const engineOpts = getEngineOptions();\n const effectiveStaleTime = staleTime ?? engineOpts.defaultStaleTime;\n\n // Check if cached data is fresh enough to skip a network round-trip.\n const graphState = useGraphStore.getState();\n const cachedEntityState = graphState.entityStates[`${type}:${id}`];\n const cachedEntity = graphState.readEntity<TEntity>(type, id);\n const age = Date.now() - (cachedEntityState?.lastFetched ?? 0);\n\n if (cachedEntity && age < effectiveStaleTime && !cachedEntityState?.stale) {\n // Fresh cache hit: update snapshot and skip fetch.\n setSnapshot(\"data\", cachedEntity);\n setSnapshot(\"entityState\", (s) => ({ ...s, ...cachedEntityState }));\n return cachedEntity;\n }\n\n // Delegate to core engine — handles dedup, retries, and graph writes.\n await fetchEntity(\n {\n type,\n id,\n fetch,\n normalize,\n idField,\n onSuccess,\n onError,\n },\n { ...getEngineOptions(), defaultStaleTime: effectiveStaleTime },\n );\n\n // Return the graph value after the fetch completes.\n return useGraphStore.getState().readEntity<TEntity>(type, id);\n });\n\n // ── Subscribe to Zustand graph for live fine-grained updates ───────────────\n createEffect(() => {\n const id = idAccessor();\n if (!id) return;\n\n const subKey = `${type}:${id}`;\n const token = registerSubscriber(subKey);\n\n // Sync initial graph state into the Solid store.\n const syncFromGraph = () => {\n const s = useGraphStore.getState();\n const entity = s.readEntity<TEntity>(type, id);\n const entityState = s.entityStates[subKey] ?? EMPTY_ENTITY_STATE;\n setSnapshot(\"data\", entity);\n setSnapshot(\"entityState\", (prev) => ({ ...prev, ...entityState }));\n };\n\n syncFromGraph();\n\n // Subscribe to graph changes that affect this entity.\n const unsub = useGraphStore.subscribe(\n (s) => ({\n entity: s.readEntity<TEntity>(type, id),\n entityState: s.entityStates[subKey] ?? EMPTY_ENTITY_STATE,\n }),\n (next) => {\n setSnapshot(\"data\", next.entity);\n setSnapshot(\"entityState\", (prev) => ({ ...prev, ...next.entityState }));\n },\n { equalityFn: shallowEqual },\n );\n\n onCleanup(() => {\n unsub();\n unregisterSubscriber(subKey, token);\n });\n });\n\n // ── Imperative refetch ──────────────────────────────────────────────────────\n const [_refetchTrigger, setRefetchTrigger] = createSignal(0);\n\n const refetch = async () => {\n const id = idAccessor();\n if (!id) return;\n useGraphStore.getState().setEntityStale(type, id, true);\n await fetchEntity(\n { type, id, fetch, normalize, idField, onSuccess, onError },\n getEngineOptions(),\n );\n };\n\n // ── Derived signals ──────────────────────────────────────────────────────────\n const isLoading = () => {\n const res = resource();\n const es = snapshot.entityState;\n return resource.loading || (es.isFetching && snapshot.data === null);\n };\n\n const isRefetching = () => {\n const es = snapshot.entityState;\n return es.isFetching && snapshot.data !== null;\n };\n\n const error = () => snapshot.entityState.error;\n\n // Typed error: pull from the graph's entityStates — the engine writes\n // TerminalError / TransientError instances into listState.lastError for lists,\n // and for single entities the error string is sufficient. We re-expose it as\n // EntityError | null for API consistency (callers may cast if needed).\n const typedError = (): EntityError | null => {\n const errStr = snapshot.entityState.error;\n if (!errStr) return null;\n // Surface the string-backed error as a plain Error (full typed errors are on\n // lists; single-entity fetch only has string form in the graph).\n return null;\n };\n\n return {\n data: () => snapshot.data,\n isLoading,\n isRefetching,\n error,\n typedError,\n refetch,\n entityState: () => snapshot.entityState,\n };\n}\n\n// ── Shallow equality helper for Zustand selector ─────────────────────────────\n\nfunction shallowEqual<T extends Record<string, unknown>>(a: T, b: T): boolean {\n if (a === b) return true;\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n for (const k of keysA) {\n if (a[k] !== b[k]) return false;\n }\n return true;\n}\n","/**\n * createEntityList — SolidJS primitive for reactive normalized entity lists.\n *\n * Architecture:\n * createEntityList (this file, Layer 2)\n * └─ core engine fetchList (Layer 1 / transport)\n * └─ useGraphStore (Layer 0 / Zustand graph)\n *\n * A Zustand `subscribe` bridges the graph's `lists[key]` slot + entity rows\n * into a SolidJS `createStore`, giving SolidJS fine-grained per-row reactivity.\n * Changing `queryKey()` re-serializes to a new list slot, triggering a fresh\n * fetch while keeping existing data visible (stale-while-revalidate).\n */\n\nimport { createEffect, onCleanup } from \"solid-js\";\nimport { createStore } from \"solid-js/store\";\n\nimport {\n useGraphStore,\n fetchList,\n serializeKey,\n getEngineOptions,\n EMPTY_LIST_STATE,\n} from \"@prometheus-ags/entity-graph-core\";\nimport type { EntityId, ListState } from \"@prometheus-ags/entity-graph-core\";\nimport type { CreateEntityListOptions, CreateEntityListReturn } from \"./types\";\n\n// ── Internal store shape ──────────────────────────────────────────────────────\n\ninterface ListSlice<TEntity extends object> {\n items: TEntity[];\n listState: ListState;\n}\n\n// ── Equality helper for list subscription ────────────────────────────────────\n\nfunction listSliceEqual(\n a: { listState: ListState; entityBucket: Record<string, unknown> | undefined },\n b: { listState: ListState; entityBucket: Record<string, unknown> | undefined },\n): boolean {\n return a.listState === b.listState && a.entityBucket === b.entityBucket;\n}\n\n/**\n * Creates a fine-grained SolidJS reactive primitive for an entity list.\n *\n * - Subscribes to `useGraphStore` to mirror list state (ids + pagination)\n * and resolves the entity rows by joining ids → graph entities on each read.\n * - Changing `queryKey()` triggers a new fetch and subscribes to the new list\n * slot; the previous subscription is cleaned up automatically.\n * - Supports pagination (`fetchNextPage`) and imperative refetch.\n *\n * @example\n * ```tsx\n * const invoices = createEntityList({\n * type: \"Invoice\",\n * queryKey: () => [\"invoices\", { status: filter() }],\n * fetch: (params) => api.getInvoices(params),\n * normalize: (raw) => ({ id: raw.invoice_id, data: { ...raw } }),\n * });\n *\n * return (\n * <For each={invoices.items()}>\n * {(invoice) => <InvoiceRow invoice={invoice} />}\n * </For>\n * );\n * ```\n */\nexport function createEntityList<TRaw, TEntity extends object>(\n opts: CreateEntityListOptions<TRaw, TEntity>,\n): CreateEntityListReturn<TEntity> {\n const {\n type,\n queryKey: queryKeyAccessor,\n fetch,\n normalize,\n mode = \"replace\",\n staleTime,\n enabled: enabledAccessor,\n onSuccess,\n onError,\n } = opts;\n\n // ── Fine-grained Solid store ─────────────────────────────────────────────────\n const [slice, setSlice] = createStore<ListSlice<TEntity>>({\n items: [],\n listState: { ...EMPTY_LIST_STATE },\n });\n\n // ── Helper: resolve items from current graph state for a given key ───────────\n function resolveItems(key: string): TEntity[] {\n const s = useGraphStore.getState();\n const list = s.lists[key];\n if (!list) return [];\n return list.ids\n .map((id: EntityId) => s.readEntity<TEntity>(type, id))\n .filter((e): e is TEntity => e !== null);\n }\n\n // ── Initial / stale-while-revalidate fetch ────────────────────────────────────\n // Declared before createEffect so it is always defined when the effect runs\n // (the effect mock executes the callback synchronously in tests).\n async function doInitialFetch(key: string): Promise<void> {\n const s = useGraphStore.getState();\n const listState = s.lists[key];\n const effectiveStaleTime = staleTime ?? getEngineOptions().defaultStaleTime;\n const age = Date.now() - (listState?.lastFetched ?? 0);\n const isFresh = listState && !listState.stale && age < effectiveStaleTime;\n if (isFresh) return;\n\n await fetchList(\n {\n type,\n queryKey: JSON.parse(key) as unknown[],\n fetch,\n normalize,\n mode,\n onSuccess: onSuccess as ((r: { items: TRaw[]; total?: number | null }) => void) | undefined,\n onError,\n },\n {},\n getEngineOptions(),\n );\n }\n\n // ── Reactive effect: subscribe to graph whenever queryKey changes ─────────────\n createEffect(() => {\n const rawKey = queryKeyAccessor();\n const key = serializeKey(rawKey);\n const enabled = enabledAccessor ? enabledAccessor() : true;\n\n // Sync snapshot immediately with whatever is already in the graph.\n const syncFromGraph = () => {\n const s = useGraphStore.getState();\n const listState = s.lists[key] ?? EMPTY_LIST_STATE;\n const items = resolveItems(key);\n setSlice(\"listState\", (prev) => ({ ...prev, ...listState }));\n setSlice(\"items\", items);\n };\n\n syncFromGraph();\n\n // Subscribe to any graph change that could affect this list.\n // Solid's fine-grained update system means only changed fields propagate.\n const unsub = useGraphStore.subscribe(\n (s) => {\n const listState = s.lists[key] ?? EMPTY_LIST_STATE;\n const entityBucket = s.entities[type];\n return { listState, entityBucket };\n },\n ({ listState }) => {\n const items = resolveItems(key);\n setSlice(\"listState\", (prev) => ({ ...prev, ...listState }));\n setSlice(\"items\", items);\n },\n { equalityFn: listSliceEqual },\n );\n\n // Kick off the fetch when enabled and data may be stale.\n if (enabled && (fetch || normalize)) {\n void doInitialFetch(key);\n }\n\n onCleanup(() => {\n unsub();\n });\n });\n\n // ── fetchNextPage ─────────────────────────────────────────────────────────────\n async function fetchNextPage(): Promise<void> {\n const key = serializeKey(queryKeyAccessor());\n const s = useGraphStore.getState();\n const listState = s.lists[key] ?? EMPTY_LIST_STATE;\n if (!listState.hasNextPage || listState.isFetchingMore) return;\n\n await fetchList(\n {\n type,\n queryKey: JSON.parse(key) as unknown[],\n fetch,\n normalize,\n mode: \"append\",\n onSuccess: onSuccess as ((r: { items: TRaw[]; total?: number | null }) => void) | undefined,\n onError,\n },\n { cursor: listState.nextCursor ?? undefined },\n getEngineOptions(),\n true, // isLoadMore\n );\n }\n\n // ── Imperative refetch ────────────────────────────────────────────────────────\n async function refetch(): Promise<void> {\n const key = serializeKey(queryKeyAccessor());\n useGraphStore.getState().setListStale(key, true);\n await fetchList(\n {\n type,\n queryKey: JSON.parse(key) as unknown[],\n fetch,\n normalize,\n mode: \"replace\",\n onSuccess: onSuccess as ((r: { items: TRaw[]; total?: number | null }) => void) | undefined,\n onError,\n },\n {},\n getEngineOptions(),\n );\n }\n\n // ── Derived signals ───────────────────────────────────────────────────────────\n const isLoading = () => slice.listState.isFetching && slice.items.length === 0;\n const isLoadingMore = () => slice.listState.isFetchingMore;\n const error = () => slice.listState.error;\n const typedError = () => slice.listState.lastError;\n const total = () => slice.listState.total;\n const hasNextPage = () => slice.listState.hasNextPage;\n const hasPrevPage = () => slice.listState.hasPrevPage;\n\n return {\n items: () => slice.items,\n isLoading,\n isLoadingMore,\n error,\n typedError,\n total,\n hasNextPage,\n hasPrevPage,\n fetchNextPage,\n refetch,\n listState: () => slice.listState,\n };\n}\n","/**\n * createGraphStore — SolidJS fine-grained reactive bridge to the entire\n * Zustand entity graph.\n *\n * Consumers that need ad-hoc access to the graph outside `createEntity` /\n * `createEntityList` can use this to read any slice of `GraphState` as a\n * SolidJS signal. The subscription is scoped to only the selected slice,\n * so unrelated graph mutations do not trigger re-computations.\n *\n * @example\n * ```ts\n * // Read total entity count for a devtools panel.\n * const entityCount = createGraphStore(\n * (s) => Object.keys(s.entities).reduce((n, t) => n + Object.keys(s.entities[t]).length, 0),\n * );\n * console.log(entityCount()); // reactive SolidJS accessor\n * ```\n */\n\nimport { createSignal, onCleanup } from \"solid-js\";\nimport { useGraphStore } from \"@prometheus-ags/entity-graph-core\";\nimport type { GraphState } from \"@prometheus-ags/entity-graph-core\";\n\n/**\n * Subscribe to a selector over the entity graph.\n * Returns a SolidJS accessor that updates whenever the selected slice changes.\n *\n * @param selector - Pure function that maps the full `GraphState` to a derived value.\n * @param equalityFn - Optional equality check (default: strict reference equality `===`).\n */\nexport function createGraphStore<T>(\n selector: (state: GraphState) => T,\n equalityFn?: (a: T, b: T) => boolean,\n): () => T {\n const eq = equalityFn ?? ((a, b) => a === b);\n const initial = selector(useGraphStore.getState());\n const [value, setValue] = createSignal<T>(initial);\n\n const unsub = useGraphStore.subscribe(\n selector,\n (next, prev) => {\n if (!eq(next, prev)) {\n setValue(() => next);\n }\n },\n );\n\n onCleanup(() => unsub());\n\n return value;\n}\n","/**\n * GraphProvider utilities for SolidJS.\n *\n * Provides boot-time setup helpers for attaching global focus/reconnect\n * listeners and configuring the engine from within a SolidJS application root.\n *\n * Usage in the application root (e.g. `src/index.tsx`):\n *\n * ```ts\n * import { setupGraphProvider } from \"@prometheus-ags/entity-graph-solid\";\n * import { configureEngine, attachGlobalListeners } from \"@prometheus-ags/entity-graph-core\";\n *\n * setupGraphProvider({\n * staleTime: 60_000,\n * revalidateOnFocus: true,\n * });\n * ```\n */\n\nimport {\n configureEngine,\n attachGlobalListeners,\n startGarbageCollector,\n} from \"@prometheus-ags/entity-graph-core\";\nimport type { EngineOptions } from \"@prometheus-ags/entity-graph-core\";\n\nexport interface GraphProviderOptions extends EngineOptions {\n /**\n * Whether to attach window focus/online listeners for stale-while-revalidate.\n * Defaults to `true` in browser environments.\n */\n attachListeners?: boolean;\n /**\n * Whether to start the periodic garbage collector.\n * Defaults to `true`.\n */\n startGc?: boolean;\n}\n\n/**\n * Configures the entity-graph engine and wires browser-level listeners.\n * Call this once at application startup before mounting the root component.\n *\n * Returns a cleanup function that stops the GC interval (useful for tests).\n */\nexport function setupGraphProvider(opts: GraphProviderOptions = {}): () => void {\n const { attachListeners = true, startGc = true, ...engineOpts } = opts;\n\n if (Object.keys(engineOpts).length > 0) {\n configureEngine(engineOpts);\n }\n\n if (attachListeners && typeof window !== \"undefined\") {\n attachGlobalListeners();\n }\n\n let stopGc: (() => void) | undefined;\n if (startGc && typeof window !== \"undefined\") {\n stopGc = startGarbageCollector();\n }\n\n return () => stopGc?.();\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/create-entity.ts","../src/create-entity-list.ts","../src/create-graph-store.ts","../src/graph-provider.ts"],"names":["createStore","graphStore","getEngineOptions","createEffect","onCleanup","createSignal"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * createEntity — SolidJS primitive that bridges a single entity to the\n * normalized entity graph via `createResource`.\n *\n * Architecture:\n * createEntity (this file, Layer 2)\n * └─ core engine fetchEntity (Layer 1 / transport)\n * └─ graphStore (Layer 0 / Zustand graph)\n *\n * SolidJS `createResource` owns the async lifecycle; the graph is the\n * single source of truth for the entity payload. Once fetchEntity writes to\n * the graph, a Zustand subscription (via `createStore`) propagates the merged\n * entity back to SolidJS as a fine-grained reactive signal.\n */\n\nimport {\n createResource,\n createSignal,\n createEffect,\n onCleanup,\n} from \"solid-js\";\nimport { createStore } from \"solid-js/store\";\n\nimport {\n graphStore,\n fetchEntity,\n getEngineOptions,\n registerSubscriber,\n unregisterSubscriber,\n EMPTY_ENTITY_STATE,\n} from \"@prometheus-ags/entity-graph-core\";\nimport type { EntityState, EntityError } from \"@prometheus-ags/entity-graph-core\";\nimport type { CreateEntityOptions, CreateEntityReturn } from \"./types\";\n\n// ── Internal snapshot shape kept in the Solid store ─────────────────────────\n\ninterface EntitySnapshot<TEntity extends object> {\n data: TEntity | null;\n entityState: EntityState;\n}\n\n/**\n * Creates a fine-grained SolidJS reactive primitive for a single entity.\n *\n * - Uses `createResource` as the async driver so Suspense and ErrorBoundary\n * boundaries work naturally.\n * - Subscribes to the Zustand graph with `graphStore.subscribe` and mirrors\n * the merged entity into a `createStore`-backed signal for fine-grained\n * SolidJS reactivity (only the changed field triggers updates).\n * - Registers / unregisters a subscriber token with the core engine so idle\n * entities stop background revalidation when no components are observing them.\n *\n * @example\n * ```tsx\n * const invoice = createEntity({\n * type: \"Invoice\",\n * id: () => props.invoiceId,\n * fetch: (id) => api.get(`/invoices/${id}`),\n * normalize: (raw) => ({ ...raw, id: raw.invoice_id }),\n * });\n *\n * return (\n * <Show when={!invoice.isLoading()} fallback={<Spinner />}>\n * <p>{invoice.data()?.amount}</p>\n * </Show>\n * );\n * ```\n */\nexport function createEntity<TRaw, TEntity extends object>(\n opts: CreateEntityOptions<TRaw, TEntity>,\n): CreateEntityReturn<TEntity> {\n const {\n type,\n id: idAccessor,\n fetch,\n normalize,\n idField = \"id\",\n staleTime,\n enabled: enabledAccessor,\n onSuccess,\n onError,\n } = opts;\n\n // ── Reactive source for createResource ─────────────────────────────────────\n // Returns [id, enabled] so resource re-fires when either changes.\n const source = (): [string | null | undefined, boolean] => {\n const id = idAccessor();\n const enabled = enabledAccessor ? enabledAccessor() : true;\n return [id ?? null, enabled];\n };\n\n // ── Solid store: mirrors graph state for fine-grained reactivity ────────────\n const [snapshot, setSnapshot] = createStore<EntitySnapshot<TEntity>>({\n data: null,\n entityState: { ...EMPTY_ENTITY_STATE },\n });\n\n // ── createResource: drives Suspense / ErrorBoundary + triggers fetches ──────\n const [resource] = createResource(source, async ([id, enabled]) => {\n if (!id || !enabled) return null;\n\n const engineOpts = getEngineOptions();\n const effectiveStaleTime = staleTime ?? engineOpts.defaultStaleTime;\n\n // Check if cached data is fresh enough to skip a network round-trip.\n const graphState = graphStore.getState();\n const cachedEntityState = graphState.entityStates[`${type}:${id}`];\n const cachedEntity = graphState.readEntity<TEntity>(type, id);\n const age = Date.now() - (cachedEntityState?.lastFetched ?? 0);\n\n if (cachedEntity && age < effectiveStaleTime && !cachedEntityState?.stale) {\n // Fresh cache hit: update snapshot and skip fetch.\n setSnapshot(\"data\", cachedEntity);\n setSnapshot(\"entityState\", (s) => ({ ...s, ...cachedEntityState }));\n return cachedEntity;\n }\n\n // Delegate to core engine — handles dedup, retries, and graph writes.\n await fetchEntity(\n {\n type,\n id,\n fetch,\n normalize,\n idField,\n onSuccess,\n onError,\n },\n { ...getEngineOptions(), defaultStaleTime: effectiveStaleTime },\n );\n\n // Return the graph value after the fetch completes.\n return graphStore.getState().readEntity<TEntity>(type, id);\n });\n\n // ── Subscribe to Zustand graph for live fine-grained updates ───────────────\n createEffect(() => {\n const id = idAccessor();\n if (!id) return;\n\n const subKey = `${type}:${id}`;\n const token = registerSubscriber(subKey);\n\n // Sync initial graph state into the Solid store.\n const syncFromGraph = () => {\n const s = graphStore.getState();\n const entity = s.readEntity<TEntity>(type, id);\n const entityState = s.entityStates[subKey] ?? EMPTY_ENTITY_STATE;\n setSnapshot(\"data\", entity);\n setSnapshot(\"entityState\", (prev) => ({ ...prev, ...entityState }));\n };\n\n syncFromGraph();\n\n // Subscribe to graph changes that affect this entity.\n const unsub = graphStore.subscribe(\n (s) => ({\n entity: s.readEntity<TEntity>(type, id),\n entityState: s.entityStates[subKey] ?? EMPTY_ENTITY_STATE,\n }),\n (next) => {\n setSnapshot(\"data\", next.entity);\n setSnapshot(\"entityState\", (prev) => ({ ...prev, ...next.entityState }));\n },\n { equalityFn: shallowEqual },\n );\n\n onCleanup(() => {\n unsub();\n unregisterSubscriber(subKey, token);\n });\n });\n\n // ── Imperative refetch ──────────────────────────────────────────────────────\n const [_refetchTrigger, setRefetchTrigger] = createSignal(0);\n\n const refetch = async () => {\n const id = idAccessor();\n if (!id) return;\n graphStore.getState().setEntityStale(type, id, true);\n await fetchEntity(\n { type, id, fetch, normalize, idField, onSuccess, onError },\n getEngineOptions(),\n );\n };\n\n // ── Derived signals ──────────────────────────────────────────────────────────\n const isLoading = () => {\n const res = resource();\n const es = snapshot.entityState;\n return resource.loading || (es.isFetching && snapshot.data === null);\n };\n\n const isRefetching = () => {\n const es = snapshot.entityState;\n return es.isFetching && snapshot.data !== null;\n };\n\n const error = () => snapshot.entityState.error;\n\n // Typed error: pull from the graph's entityStates — the engine writes\n // TerminalError / TransientError instances into listState.lastError for lists,\n // and for single entities the error string is sufficient. We re-expose it as\n // EntityError | null for API consistency (callers may cast if needed).\n const typedError = (): EntityError | null => {\n const errStr = snapshot.entityState.error;\n if (!errStr) return null;\n // Surface the string-backed error as a plain Error (full typed errors are on\n // lists; single-entity fetch only has string form in the graph).\n return null;\n };\n\n return {\n data: () => snapshot.data,\n isLoading,\n isRefetching,\n error,\n typedError,\n refetch,\n entityState: () => snapshot.entityState,\n };\n}\n\n// ── Shallow equality helper for Zustand selector ─────────────────────────────\n\nfunction shallowEqual<T extends Record<string, unknown>>(a: T, b: T): boolean {\n if (a === b) return true;\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n for (const k of keysA) {\n if (a[k] !== b[k]) return false;\n }\n return true;\n}\n","/**\n * createEntityList — SolidJS primitive for reactive normalized entity lists.\n *\n * Architecture:\n * createEntityList (this file, Layer 2)\n * └─ core engine fetchList (Layer 1 / transport)\n * └─ graphStore (Layer 0 / Zustand graph)\n *\n * A Zustand `subscribe` bridges the graph's `lists[key]` slot + entity rows\n * into a SolidJS `createStore`, giving SolidJS fine-grained per-row reactivity.\n * Changing `queryKey()` re-serializes to a new list slot, triggering a fresh\n * fetch while keeping existing data visible (stale-while-revalidate).\n */\n\nimport { createEffect, onCleanup } from \"solid-js\";\nimport { createStore } from \"solid-js/store\";\n\nimport {\n graphStore,\n fetchList,\n serializeKey,\n getEngineOptions,\n EMPTY_LIST_STATE,\n} from \"@prometheus-ags/entity-graph-core\";\nimport type { EntityId, ListState } from \"@prometheus-ags/entity-graph-core\";\nimport type { CreateEntityListOptions, CreateEntityListReturn } from \"./types\";\n\n// ── Internal store shape ──────────────────────────────────────────────────────\n\ninterface ListSlice<TEntity extends object> {\n items: TEntity[];\n listState: ListState;\n}\n\n// ── Equality helper for list subscription ────────────────────────────────────\n\nfunction listSliceEqual(\n a: { listState: ListState; entityBucket: Record<string, unknown> | undefined },\n b: { listState: ListState; entityBucket: Record<string, unknown> | undefined },\n): boolean {\n return a.listState === b.listState && a.entityBucket === b.entityBucket;\n}\n\n/**\n * Creates a fine-grained SolidJS reactive primitive for an entity list.\n *\n * - Subscribes to `graphStore` to mirror list state (ids + pagination)\n * and resolves the entity rows by joining ids → graph entities on each read.\n * - Changing `queryKey()` triggers a new fetch and subscribes to the new list\n * slot; the previous subscription is cleaned up automatically.\n * - Supports pagination (`fetchNextPage`) and imperative refetch.\n *\n * @example\n * ```tsx\n * const invoices = createEntityList({\n * type: \"Invoice\",\n * queryKey: () => [\"invoices\", { status: filter() }],\n * fetch: (params) => api.getInvoices(params),\n * normalize: (raw) => ({ id: raw.invoice_id, data: { ...raw } }),\n * });\n *\n * return (\n * <For each={invoices.items()}>\n * {(invoice) => <InvoiceRow invoice={invoice} />}\n * </For>\n * );\n * ```\n */\nexport function createEntityList<TRaw, TEntity extends object>(\n opts: CreateEntityListOptions<TRaw, TEntity>,\n): CreateEntityListReturn<TEntity> {\n const {\n type,\n queryKey: queryKeyAccessor,\n fetch,\n normalize,\n mode = \"replace\",\n staleTime,\n enabled: enabledAccessor,\n onSuccess,\n onError,\n } = opts;\n\n // ── Fine-grained Solid store ─────────────────────────────────────────────────\n const [slice, setSlice] = createStore<ListSlice<TEntity>>({\n items: [],\n listState: { ...EMPTY_LIST_STATE },\n });\n\n // ── Helper: resolve items from current graph state for a given key ───────────\n function resolveItems(key: string): TEntity[] {\n const s = graphStore.getState();\n const list = s.lists[key];\n if (!list) return [];\n return list.ids\n .map((id: EntityId) => s.readEntity<TEntity>(type, id))\n .filter((e): e is TEntity => e !== null);\n }\n\n // ── Initial / stale-while-revalidate fetch ────────────────────────────────────\n // Declared before createEffect so it is always defined when the effect runs\n // (the effect mock executes the callback synchronously in tests).\n async function doInitialFetch(key: string): Promise<void> {\n const s = graphStore.getState();\n const listState = s.lists[key];\n const effectiveStaleTime = staleTime ?? getEngineOptions().defaultStaleTime;\n const age = Date.now() - (listState?.lastFetched ?? 0);\n const isFresh = listState && !listState.stale && age < effectiveStaleTime;\n if (isFresh) return;\n\n await fetchList(\n {\n type,\n queryKey: JSON.parse(key) as unknown[],\n fetch,\n normalize,\n mode,\n onSuccess: onSuccess as ((r: { items: TRaw[]; total?: number | null }) => void) | undefined,\n onError,\n },\n {},\n getEngineOptions(),\n );\n }\n\n // ── Reactive effect: subscribe to graph whenever queryKey changes ─────────────\n createEffect(() => {\n const rawKey = queryKeyAccessor();\n const key = serializeKey(rawKey);\n const enabled = enabledAccessor ? enabledAccessor() : true;\n\n // Sync snapshot immediately with whatever is already in the graph.\n const syncFromGraph = () => {\n const s = graphStore.getState();\n const listState = s.lists[key] ?? EMPTY_LIST_STATE;\n const items = resolveItems(key);\n setSlice(\"listState\", (prev) => ({ ...prev, ...listState }));\n setSlice(\"items\", items);\n };\n\n syncFromGraph();\n\n // Subscribe to any graph change that could affect this list.\n // Solid's fine-grained update system means only changed fields propagate.\n const unsub = graphStore.subscribe(\n (s) => {\n const listState = s.lists[key] ?? EMPTY_LIST_STATE;\n const entityBucket = s.entities[type];\n return { listState, entityBucket };\n },\n ({ listState }) => {\n const items = resolveItems(key);\n setSlice(\"listState\", (prev) => ({ ...prev, ...listState }));\n setSlice(\"items\", items);\n },\n { equalityFn: listSliceEqual },\n );\n\n // Kick off the fetch when enabled and data may be stale.\n if (enabled && (fetch || normalize)) {\n void doInitialFetch(key);\n }\n\n onCleanup(() => {\n unsub();\n });\n });\n\n // ── fetchNextPage ─────────────────────────────────────────────────────────────\n async function fetchNextPage(): Promise<void> {\n const key = serializeKey(queryKeyAccessor());\n const s = graphStore.getState();\n const listState = s.lists[key] ?? EMPTY_LIST_STATE;\n if (!listState.hasNextPage || listState.isFetchingMore) return;\n\n await fetchList(\n {\n type,\n queryKey: JSON.parse(key) as unknown[],\n fetch,\n normalize,\n mode: \"append\",\n onSuccess: onSuccess as ((r: { items: TRaw[]; total?: number | null }) => void) | undefined,\n onError,\n },\n { cursor: listState.nextCursor ?? undefined },\n getEngineOptions(),\n true, // isLoadMore\n );\n }\n\n // ── Imperative refetch ────────────────────────────────────────────────────────\n async function refetch(): Promise<void> {\n const key = serializeKey(queryKeyAccessor());\n graphStore.getState().setListStale(key, true);\n await fetchList(\n {\n type,\n queryKey: JSON.parse(key) as unknown[],\n fetch,\n normalize,\n mode: \"replace\",\n onSuccess: onSuccess as ((r: { items: TRaw[]; total?: number | null }) => void) | undefined,\n onError,\n },\n {},\n getEngineOptions(),\n );\n }\n\n // ── Derived signals ───────────────────────────────────────────────────────────\n const isLoading = () => slice.listState.isFetching && slice.items.length === 0;\n const isLoadingMore = () => slice.listState.isFetchingMore;\n const error = () => slice.listState.error;\n const typedError = () => slice.listState.lastError;\n const total = () => slice.listState.total;\n const hasNextPage = () => slice.listState.hasNextPage;\n const hasPrevPage = () => slice.listState.hasPrevPage;\n\n return {\n items: () => slice.items,\n isLoading,\n isLoadingMore,\n error,\n typedError,\n total,\n hasNextPage,\n hasPrevPage,\n fetchNextPage,\n refetch,\n listState: () => slice.listState,\n };\n}\n","/**\n * createGraphStore — SolidJS fine-grained reactive bridge to the entire\n * Zustand entity graph.\n *\n * Consumers that need ad-hoc access to the graph outside `createEntity` /\n * `createEntityList` can use this to read any slice of `GraphState` as a\n * SolidJS signal. The subscription is scoped to only the selected slice,\n * so unrelated graph mutations do not trigger re-computations.\n *\n * @example\n * ```ts\n * // Read total entity count for a devtools panel.\n * const entityCount = createGraphStore(\n * (s) => Object.keys(s.entities).reduce((n, t) => n + Object.keys(s.entities[t]).length, 0),\n * );\n * console.log(entityCount()); // reactive SolidJS accessor\n * ```\n */\n\nimport { createSignal, onCleanup } from \"solid-js\";\nimport { graphStore } from \"@prometheus-ags/entity-graph-core\";\nimport type { GraphState } from \"@prometheus-ags/entity-graph-core\";\n\n/**\n * Subscribe to a selector over the entity graph.\n * Returns a SolidJS accessor that updates whenever the selected slice changes.\n *\n * @param selector - Pure function that maps the full `GraphState` to a derived value.\n * @param equalityFn - Optional equality check (default: strict reference equality `===`).\n */\nexport function createGraphStore<T>(\n selector: (state: GraphState) => T,\n equalityFn?: (a: T, b: T) => boolean,\n): () => T {\n const eq = equalityFn ?? ((a, b) => a === b);\n const initial = selector(graphStore.getState());\n const [value, setValue] = createSignal<T>(initial);\n\n const unsub = graphStore.subscribe(\n selector,\n (next, prev) => {\n if (!eq(next, prev)) {\n setValue(() => next);\n }\n },\n );\n\n onCleanup(() => unsub());\n\n return value;\n}\n","/**\n * GraphProvider utilities for SolidJS.\n *\n * Provides boot-time setup helpers for attaching global focus/reconnect\n * listeners and configuring the engine from within a SolidJS application root.\n *\n * Usage in the application root (e.g. `src/index.tsx`):\n *\n * ```ts\n * import { setupGraphProvider } from \"@prometheus-ags/entity-graph-solid\";\n * import { configureEngine, attachGlobalListeners } from \"@prometheus-ags/entity-graph-core\";\n *\n * setupGraphProvider({\n * staleTime: 60_000,\n * revalidateOnFocus: true,\n * });\n * ```\n */\n\nimport {\n configureEngine,\n attachGlobalListeners,\n startGarbageCollector,\n} from \"@prometheus-ags/entity-graph-core\";\nimport type { EngineOptions } from \"@prometheus-ags/entity-graph-core\";\n\nexport interface GraphProviderOptions extends EngineOptions {\n /**\n * Whether to attach window focus/online listeners for stale-while-revalidate.\n * Defaults to `true` in browser environments.\n */\n attachListeners?: boolean;\n /**\n * Whether to start the periodic garbage collector.\n * Defaults to `true`.\n */\n startGc?: boolean;\n}\n\n/**\n * Configures the entity-graph engine and wires browser-level listeners.\n * Call this once at application startup before mounting the root component.\n *\n * Returns a cleanup function that stops the GC interval (useful for tests).\n */\nexport function setupGraphProvider(opts: GraphProviderOptions = {}): () => void {\n const { attachListeners = true, startGc = true, ...engineOpts } = opts;\n\n if (Object.keys(engineOpts).length > 0) {\n configureEngine(engineOpts);\n }\n\n if (attachListeners && typeof window !== \"undefined\") {\n attachGlobalListeners();\n }\n\n let stopGc: (() => void) | undefined;\n if (startGc && typeof window !== \"undefined\") {\n stopGc = startGarbageCollector();\n }\n\n return () => stopGc?.();\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,31 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prometheus-ags/entity-graph-solid",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "SolidJS bindings for @prometheus-ags/entity-graph-core — fine-grained reactive entity and list primitives over the normalized entity graph.",
|
|
5
|
+
"author": "Travis James <travis@prometheus.dev>",
|
|
5
6
|
"license": "MIT",
|
|
6
7
|
"type": "module",
|
|
7
|
-
"main": "./dist/index.
|
|
8
|
+
"main": "./dist/index.cjs",
|
|
8
9
|
"module": "./dist/index.mjs",
|
|
9
10
|
"types": "./dist/index.d.ts",
|
|
10
11
|
"exports": {
|
|
11
12
|
".": {
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
"import": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.mjs"
|
|
16
|
+
},
|
|
17
|
+
"require": {
|
|
18
|
+
"types": "./dist/index.d.cts",
|
|
19
|
+
"default": "./dist/index.cjs"
|
|
20
|
+
}
|
|
15
21
|
}
|
|
16
22
|
},
|
|
17
23
|
"sideEffects": false,
|
|
18
24
|
"files": [
|
|
19
|
-
"dist"
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md",
|
|
27
|
+
"CHANGELOG.md"
|
|
20
28
|
],
|
|
21
29
|
"publishConfig": {
|
|
22
30
|
"access": "public"
|
|
23
31
|
},
|
|
24
|
-
"dependencies": {
|
|
25
|
-
"@prometheus-ags/entity-graph-core": "3.0.0-alpha.0"
|
|
26
|
-
},
|
|
27
32
|
"peerDependencies": {
|
|
28
|
-
"solid-js": ">=1.8.0"
|
|
33
|
+
"solid-js": ">=1.8.0",
|
|
34
|
+
"@prometheus-ags/entity-graph-core": "^3.0.1"
|
|
29
35
|
},
|
|
30
36
|
"peerDependenciesMeta": {
|
|
31
37
|
"solid-js": {
|
|
@@ -33,10 +39,11 @@
|
|
|
33
39
|
}
|
|
34
40
|
},
|
|
35
41
|
"devDependencies": {
|
|
36
|
-
"solid-js": "^1.9.
|
|
37
|
-
"tsup": "
|
|
38
|
-
"typescript": "
|
|
39
|
-
"vitest": "
|
|
42
|
+
"solid-js": "^1.9.14",
|
|
43
|
+
"tsup": "8.5.1",
|
|
44
|
+
"typescript": "6.0.2",
|
|
45
|
+
"vitest": "4.1.10",
|
|
46
|
+
"@prometheus-ags/entity-graph-core": "3.0.1"
|
|
40
47
|
},
|
|
41
48
|
"keywords": [
|
|
42
49
|
"solid",
|
|
@@ -47,9 +54,23 @@
|
|
|
47
54
|
"reactive",
|
|
48
55
|
"fine-grained-reactivity"
|
|
49
56
|
],
|
|
57
|
+
"repository": {
|
|
58
|
+
"type": "git",
|
|
59
|
+
"url": "git+https://github.com/prometheus-ags/prometheus-entity-management.git",
|
|
60
|
+
"directory": "packages/entity-graph-solid"
|
|
61
|
+
},
|
|
62
|
+
"homepage": "https://github.com/prometheus-ags/prometheus-entity-management/tree/main/packages/entity-graph-solid#readme",
|
|
63
|
+
"bugs": {
|
|
64
|
+
"url": "https://github.com/prometheus-ags/prometheus-entity-management/issues"
|
|
65
|
+
},
|
|
66
|
+
"engines": {
|
|
67
|
+
"node": "^22.14.0 || ^24.0.0 || >=26.0.0"
|
|
68
|
+
},
|
|
50
69
|
"scripts": {
|
|
51
70
|
"build": "tsup",
|
|
52
71
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
53
|
-
"test": "vitest run"
|
|
72
|
+
"test": "vitest run",
|
|
73
|
+
"refresh:exports": "pnpm run build && node ../../scripts/refresh-exports-ledger.mjs --pkg solid",
|
|
74
|
+
"verify:skills": "node ../../scripts/verify-skills-exports.mjs --pkg solid"
|
|
54
75
|
}
|
|
55
76
|
}
|
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/create-entity.ts","../src/create-entity-list.ts","../src/create-graph-store.ts","../src/graph-provider.ts"],"names":["createStore","EMPTY_ENTITY_STATE","createResource","getEngineOptions","useGraphStore","fetchEntity","createEffect","registerSubscriber","onCleanup","unregisterSubscriber","createSignal","EMPTY_LIST_STATE","fetchList","serializeKey","configureEngine","attachGlobalListeners","startGarbageCollector"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * createEntity — SolidJS primitive that bridges a single entity to the\n * normalized entity graph via `createResource`.\n *\n * Architecture:\n * createEntity (this file, Layer 2)\n * └─ core engine fetchEntity (Layer 1 / transport)\n * └─ useGraphStore (Layer 0 / Zustand graph)\n *\n * SolidJS `createResource` owns the async lifecycle; the graph is the\n * single source of truth for the entity payload. Once fetchEntity writes to\n * the graph, a Zustand subscription (via `createStore`) propagates the merged\n * entity back to SolidJS as a fine-grained reactive signal.\n */\n\nimport {\n createResource,\n createSignal,\n createEffect,\n onCleanup,\n} from \"solid-js\";\nimport { createStore } from \"solid-js/store\";\n\nimport {\n useGraphStore,\n fetchEntity,\n getEngineOptions,\n registerSubscriber,\n unregisterSubscriber,\n EMPTY_ENTITY_STATE,\n} from \"@prometheus-ags/entity-graph-core\";\nimport type { EntityState, EntityError } from \"@prometheus-ags/entity-graph-core\";\nimport type { CreateEntityOptions, CreateEntityReturn } from \"./types\";\n\n// ── Internal snapshot shape kept in the Solid store ─────────────────────────\n\ninterface EntitySnapshot<TEntity extends object> {\n data: TEntity | null;\n entityState: EntityState;\n}\n\n/**\n * Creates a fine-grained SolidJS reactive primitive for a single entity.\n *\n * - Uses `createResource` as the async driver so Suspense and ErrorBoundary\n * boundaries work naturally.\n * - Subscribes to the Zustand graph with `useGraphStore.subscribe` and mirrors\n * the merged entity into a `createStore`-backed signal for fine-grained\n * SolidJS reactivity (only the changed field triggers updates).\n * - Registers / unregisters a subscriber token with the core engine so idle\n * entities stop background revalidation when no components are observing them.\n *\n * @example\n * ```tsx\n * const invoice = createEntity({\n * type: \"Invoice\",\n * id: () => props.invoiceId,\n * fetch: (id) => api.get(`/invoices/${id}`),\n * normalize: (raw) => ({ ...raw, id: raw.invoice_id }),\n * });\n *\n * return (\n * <Show when={!invoice.isLoading()} fallback={<Spinner />}>\n * <p>{invoice.data()?.amount}</p>\n * </Show>\n * );\n * ```\n */\nexport function createEntity<TRaw, TEntity extends object>(\n opts: CreateEntityOptions<TRaw, TEntity>,\n): CreateEntityReturn<TEntity> {\n const {\n type,\n id: idAccessor,\n fetch,\n normalize,\n idField = \"id\",\n staleTime,\n enabled: enabledAccessor,\n onSuccess,\n onError,\n } = opts;\n\n // ── Reactive source for createResource ─────────────────────────────────────\n // Returns [id, enabled] so resource re-fires when either changes.\n const source = (): [string | null | undefined, boolean] => {\n const id = idAccessor();\n const enabled = enabledAccessor ? enabledAccessor() : true;\n return [id ?? null, enabled];\n };\n\n // ── Solid store: mirrors graph state for fine-grained reactivity ────────────\n const [snapshot, setSnapshot] = createStore<EntitySnapshot<TEntity>>({\n data: null,\n entityState: { ...EMPTY_ENTITY_STATE },\n });\n\n // ── createResource: drives Suspense / ErrorBoundary + triggers fetches ──────\n const [resource] = createResource(source, async ([id, enabled]) => {\n if (!id || !enabled) return null;\n\n const engineOpts = getEngineOptions();\n const effectiveStaleTime = staleTime ?? engineOpts.defaultStaleTime;\n\n // Check if cached data is fresh enough to skip a network round-trip.\n const graphState = useGraphStore.getState();\n const cachedEntityState = graphState.entityStates[`${type}:${id}`];\n const cachedEntity = graphState.readEntity<TEntity>(type, id);\n const age = Date.now() - (cachedEntityState?.lastFetched ?? 0);\n\n if (cachedEntity && age < effectiveStaleTime && !cachedEntityState?.stale) {\n // Fresh cache hit: update snapshot and skip fetch.\n setSnapshot(\"data\", cachedEntity);\n setSnapshot(\"entityState\", (s) => ({ ...s, ...cachedEntityState }));\n return cachedEntity;\n }\n\n // Delegate to core engine — handles dedup, retries, and graph writes.\n await fetchEntity(\n {\n type,\n id,\n fetch,\n normalize,\n idField,\n onSuccess,\n onError,\n },\n { ...getEngineOptions(), defaultStaleTime: effectiveStaleTime },\n );\n\n // Return the graph value after the fetch completes.\n return useGraphStore.getState().readEntity<TEntity>(type, id);\n });\n\n // ── Subscribe to Zustand graph for live fine-grained updates ───────────────\n createEffect(() => {\n const id = idAccessor();\n if (!id) return;\n\n const subKey = `${type}:${id}`;\n const token = registerSubscriber(subKey);\n\n // Sync initial graph state into the Solid store.\n const syncFromGraph = () => {\n const s = useGraphStore.getState();\n const entity = s.readEntity<TEntity>(type, id);\n const entityState = s.entityStates[subKey] ?? EMPTY_ENTITY_STATE;\n setSnapshot(\"data\", entity);\n setSnapshot(\"entityState\", (prev) => ({ ...prev, ...entityState }));\n };\n\n syncFromGraph();\n\n // Subscribe to graph changes that affect this entity.\n const unsub = useGraphStore.subscribe(\n (s) => ({\n entity: s.readEntity<TEntity>(type, id),\n entityState: s.entityStates[subKey] ?? EMPTY_ENTITY_STATE,\n }),\n (next) => {\n setSnapshot(\"data\", next.entity);\n setSnapshot(\"entityState\", (prev) => ({ ...prev, ...next.entityState }));\n },\n { equalityFn: shallowEqual },\n );\n\n onCleanup(() => {\n unsub();\n unregisterSubscriber(subKey, token);\n });\n });\n\n // ── Imperative refetch ──────────────────────────────────────────────────────\n const [_refetchTrigger, setRefetchTrigger] = createSignal(0);\n\n const refetch = async () => {\n const id = idAccessor();\n if (!id) return;\n useGraphStore.getState().setEntityStale(type, id, true);\n await fetchEntity(\n { type, id, fetch, normalize, idField, onSuccess, onError },\n getEngineOptions(),\n );\n };\n\n // ── Derived signals ──────────────────────────────────────────────────────────\n const isLoading = () => {\n const res = resource();\n const es = snapshot.entityState;\n return resource.loading || (es.isFetching && snapshot.data === null);\n };\n\n const isRefetching = () => {\n const es = snapshot.entityState;\n return es.isFetching && snapshot.data !== null;\n };\n\n const error = () => snapshot.entityState.error;\n\n // Typed error: pull from the graph's entityStates — the engine writes\n // TerminalError / TransientError instances into listState.lastError for lists,\n // and for single entities the error string is sufficient. We re-expose it as\n // EntityError | null for API consistency (callers may cast if needed).\n const typedError = (): EntityError | null => {\n const errStr = snapshot.entityState.error;\n if (!errStr) return null;\n // Surface the string-backed error as a plain Error (full typed errors are on\n // lists; single-entity fetch only has string form in the graph).\n return null;\n };\n\n return {\n data: () => snapshot.data,\n isLoading,\n isRefetching,\n error,\n typedError,\n refetch,\n entityState: () => snapshot.entityState,\n };\n}\n\n// ── Shallow equality helper for Zustand selector ─────────────────────────────\n\nfunction shallowEqual<T extends Record<string, unknown>>(a: T, b: T): boolean {\n if (a === b) return true;\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) return false;\n for (const k of keysA) {\n if (a[k] !== b[k]) return false;\n }\n return true;\n}\n","/**\n * createEntityList — SolidJS primitive for reactive normalized entity lists.\n *\n * Architecture:\n * createEntityList (this file, Layer 2)\n * └─ core engine fetchList (Layer 1 / transport)\n * └─ useGraphStore (Layer 0 / Zustand graph)\n *\n * A Zustand `subscribe` bridges the graph's `lists[key]` slot + entity rows\n * into a SolidJS `createStore`, giving SolidJS fine-grained per-row reactivity.\n * Changing `queryKey()` re-serializes to a new list slot, triggering a fresh\n * fetch while keeping existing data visible (stale-while-revalidate).\n */\n\nimport { createEffect, onCleanup } from \"solid-js\";\nimport { createStore } from \"solid-js/store\";\n\nimport {\n useGraphStore,\n fetchList,\n serializeKey,\n getEngineOptions,\n EMPTY_LIST_STATE,\n} from \"@prometheus-ags/entity-graph-core\";\nimport type { EntityId, ListState } from \"@prometheus-ags/entity-graph-core\";\nimport type { CreateEntityListOptions, CreateEntityListReturn } from \"./types\";\n\n// ── Internal store shape ──────────────────────────────────────────────────────\n\ninterface ListSlice<TEntity extends object> {\n items: TEntity[];\n listState: ListState;\n}\n\n// ── Equality helper for list subscription ────────────────────────────────────\n\nfunction listSliceEqual(\n a: { listState: ListState; entityBucket: Record<string, unknown> | undefined },\n b: { listState: ListState; entityBucket: Record<string, unknown> | undefined },\n): boolean {\n return a.listState === b.listState && a.entityBucket === b.entityBucket;\n}\n\n/**\n * Creates a fine-grained SolidJS reactive primitive for an entity list.\n *\n * - Subscribes to `useGraphStore` to mirror list state (ids + pagination)\n * and resolves the entity rows by joining ids → graph entities on each read.\n * - Changing `queryKey()` triggers a new fetch and subscribes to the new list\n * slot; the previous subscription is cleaned up automatically.\n * - Supports pagination (`fetchNextPage`) and imperative refetch.\n *\n * @example\n * ```tsx\n * const invoices = createEntityList({\n * type: \"Invoice\",\n * queryKey: () => [\"invoices\", { status: filter() }],\n * fetch: (params) => api.getInvoices(params),\n * normalize: (raw) => ({ id: raw.invoice_id, data: { ...raw } }),\n * });\n *\n * return (\n * <For each={invoices.items()}>\n * {(invoice) => <InvoiceRow invoice={invoice} />}\n * </For>\n * );\n * ```\n */\nexport function createEntityList<TRaw, TEntity extends object>(\n opts: CreateEntityListOptions<TRaw, TEntity>,\n): CreateEntityListReturn<TEntity> {\n const {\n type,\n queryKey: queryKeyAccessor,\n fetch,\n normalize,\n mode = \"replace\",\n staleTime,\n enabled: enabledAccessor,\n onSuccess,\n onError,\n } = opts;\n\n // ── Fine-grained Solid store ─────────────────────────────────────────────────\n const [slice, setSlice] = createStore<ListSlice<TEntity>>({\n items: [],\n listState: { ...EMPTY_LIST_STATE },\n });\n\n // ── Helper: resolve items from current graph state for a given key ───────────\n function resolveItems(key: string): TEntity[] {\n const s = useGraphStore.getState();\n const list = s.lists[key];\n if (!list) return [];\n return list.ids\n .map((id: EntityId) => s.readEntity<TEntity>(type, id))\n .filter((e): e is TEntity => e !== null);\n }\n\n // ── Initial / stale-while-revalidate fetch ────────────────────────────────────\n // Declared before createEffect so it is always defined when the effect runs\n // (the effect mock executes the callback synchronously in tests).\n async function doInitialFetch(key: string): Promise<void> {\n const s = useGraphStore.getState();\n const listState = s.lists[key];\n const effectiveStaleTime = staleTime ?? getEngineOptions().defaultStaleTime;\n const age = Date.now() - (listState?.lastFetched ?? 0);\n const isFresh = listState && !listState.stale && age < effectiveStaleTime;\n if (isFresh) return;\n\n await fetchList(\n {\n type,\n queryKey: JSON.parse(key) as unknown[],\n fetch,\n normalize,\n mode,\n onSuccess: onSuccess as ((r: { items: TRaw[]; total?: number | null }) => void) | undefined,\n onError,\n },\n {},\n getEngineOptions(),\n );\n }\n\n // ── Reactive effect: subscribe to graph whenever queryKey changes ─────────────\n createEffect(() => {\n const rawKey = queryKeyAccessor();\n const key = serializeKey(rawKey);\n const enabled = enabledAccessor ? enabledAccessor() : true;\n\n // Sync snapshot immediately with whatever is already in the graph.\n const syncFromGraph = () => {\n const s = useGraphStore.getState();\n const listState = s.lists[key] ?? EMPTY_LIST_STATE;\n const items = resolveItems(key);\n setSlice(\"listState\", (prev) => ({ ...prev, ...listState }));\n setSlice(\"items\", items);\n };\n\n syncFromGraph();\n\n // Subscribe to any graph change that could affect this list.\n // Solid's fine-grained update system means only changed fields propagate.\n const unsub = useGraphStore.subscribe(\n (s) => {\n const listState = s.lists[key] ?? EMPTY_LIST_STATE;\n const entityBucket = s.entities[type];\n return { listState, entityBucket };\n },\n ({ listState }) => {\n const items = resolveItems(key);\n setSlice(\"listState\", (prev) => ({ ...prev, ...listState }));\n setSlice(\"items\", items);\n },\n { equalityFn: listSliceEqual },\n );\n\n // Kick off the fetch when enabled and data may be stale.\n if (enabled && (fetch || normalize)) {\n void doInitialFetch(key);\n }\n\n onCleanup(() => {\n unsub();\n });\n });\n\n // ── fetchNextPage ─────────────────────────────────────────────────────────────\n async function fetchNextPage(): Promise<void> {\n const key = serializeKey(queryKeyAccessor());\n const s = useGraphStore.getState();\n const listState = s.lists[key] ?? EMPTY_LIST_STATE;\n if (!listState.hasNextPage || listState.isFetchingMore) return;\n\n await fetchList(\n {\n type,\n queryKey: JSON.parse(key) as unknown[],\n fetch,\n normalize,\n mode: \"append\",\n onSuccess: onSuccess as ((r: { items: TRaw[]; total?: number | null }) => void) | undefined,\n onError,\n },\n { cursor: listState.nextCursor ?? undefined },\n getEngineOptions(),\n true, // isLoadMore\n );\n }\n\n // ── Imperative refetch ────────────────────────────────────────────────────────\n async function refetch(): Promise<void> {\n const key = serializeKey(queryKeyAccessor());\n useGraphStore.getState().setListStale(key, true);\n await fetchList(\n {\n type,\n queryKey: JSON.parse(key) as unknown[],\n fetch,\n normalize,\n mode: \"replace\",\n onSuccess: onSuccess as ((r: { items: TRaw[]; total?: number | null }) => void) | undefined,\n onError,\n },\n {},\n getEngineOptions(),\n );\n }\n\n // ── Derived signals ───────────────────────────────────────────────────────────\n const isLoading = () => slice.listState.isFetching && slice.items.length === 0;\n const isLoadingMore = () => slice.listState.isFetchingMore;\n const error = () => slice.listState.error;\n const typedError = () => slice.listState.lastError;\n const total = () => slice.listState.total;\n const hasNextPage = () => slice.listState.hasNextPage;\n const hasPrevPage = () => slice.listState.hasPrevPage;\n\n return {\n items: () => slice.items,\n isLoading,\n isLoadingMore,\n error,\n typedError,\n total,\n hasNextPage,\n hasPrevPage,\n fetchNextPage,\n refetch,\n listState: () => slice.listState,\n };\n}\n","/**\n * createGraphStore — SolidJS fine-grained reactive bridge to the entire\n * Zustand entity graph.\n *\n * Consumers that need ad-hoc access to the graph outside `createEntity` /\n * `createEntityList` can use this to read any slice of `GraphState` as a\n * SolidJS signal. The subscription is scoped to only the selected slice,\n * so unrelated graph mutations do not trigger re-computations.\n *\n * @example\n * ```ts\n * // Read total entity count for a devtools panel.\n * const entityCount = createGraphStore(\n * (s) => Object.keys(s.entities).reduce((n, t) => n + Object.keys(s.entities[t]).length, 0),\n * );\n * console.log(entityCount()); // reactive SolidJS accessor\n * ```\n */\n\nimport { createSignal, onCleanup } from \"solid-js\";\nimport { useGraphStore } from \"@prometheus-ags/entity-graph-core\";\nimport type { GraphState } from \"@prometheus-ags/entity-graph-core\";\n\n/**\n * Subscribe to a selector over the entity graph.\n * Returns a SolidJS accessor that updates whenever the selected slice changes.\n *\n * @param selector - Pure function that maps the full `GraphState` to a derived value.\n * @param equalityFn - Optional equality check (default: strict reference equality `===`).\n */\nexport function createGraphStore<T>(\n selector: (state: GraphState) => T,\n equalityFn?: (a: T, b: T) => boolean,\n): () => T {\n const eq = equalityFn ?? ((a, b) => a === b);\n const initial = selector(useGraphStore.getState());\n const [value, setValue] = createSignal<T>(initial);\n\n const unsub = useGraphStore.subscribe(\n selector,\n (next, prev) => {\n if (!eq(next, prev)) {\n setValue(() => next);\n }\n },\n );\n\n onCleanup(() => unsub());\n\n return value;\n}\n","/**\n * GraphProvider utilities for SolidJS.\n *\n * Provides boot-time setup helpers for attaching global focus/reconnect\n * listeners and configuring the engine from within a SolidJS application root.\n *\n * Usage in the application root (e.g. `src/index.tsx`):\n *\n * ```ts\n * import { setupGraphProvider } from \"@prometheus-ags/entity-graph-solid\";\n * import { configureEngine, attachGlobalListeners } from \"@prometheus-ags/entity-graph-core\";\n *\n * setupGraphProvider({\n * staleTime: 60_000,\n * revalidateOnFocus: true,\n * });\n * ```\n */\n\nimport {\n configureEngine,\n attachGlobalListeners,\n startGarbageCollector,\n} from \"@prometheus-ags/entity-graph-core\";\nimport type { EngineOptions } from \"@prometheus-ags/entity-graph-core\";\n\nexport interface GraphProviderOptions extends EngineOptions {\n /**\n * Whether to attach window focus/online listeners for stale-while-revalidate.\n * Defaults to `true` in browser environments.\n */\n attachListeners?: boolean;\n /**\n * Whether to start the periodic garbage collector.\n * Defaults to `true`.\n */\n startGc?: boolean;\n}\n\n/**\n * Configures the entity-graph engine and wires browser-level listeners.\n * Call this once at application startup before mounting the root component.\n *\n * Returns a cleanup function that stops the GC interval (useful for tests).\n */\nexport function setupGraphProvider(opts: GraphProviderOptions = {}): () => void {\n const { attachListeners = true, startGc = true, ...engineOpts } = opts;\n\n if (Object.keys(engineOpts).length > 0) {\n configureEngine(engineOpts);\n }\n\n if (attachListeners && typeof window !== \"undefined\") {\n attachGlobalListeners();\n }\n\n let stopGc: (() => void) | undefined;\n if (startGc && typeof window !== \"undefined\") {\n stopGc = startGarbageCollector();\n }\n\n return () => stopGc?.();\n}\n"]}
|