@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 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
- └─ useGraphStore (Zustand, Layer 0)
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.useGraphStore.getState();
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.useGraphStore.getState().readEntity(type, id);
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.useGraphStore.getState();
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.useGraphStore.subscribe(
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.useGraphStore.getState().setEntityStale(type, id, true);
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.useGraphStore.getState();
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.useGraphStore.getState();
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.useGraphStore.getState();
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.useGraphStore.subscribe(
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.useGraphStore.getState();
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.useGraphStore.getState().setListStale(key, true);
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.useGraphStore.getState());
269
+ const initial = selector(entityGraphCore.graphStore.getState());
270
270
  const [value, setValue] = solidJs.createSignal(initial);
271
- const unsub = entityGraphCore.useGraphStore.subscribe(
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.js.map
314
- //# sourceMappingURL=index.js.map
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
- * └─ useGraphStore (Layer 0 / Zustand graph)
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 `useGraphStore.subscribe` and mirrors
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
- * └─ useGraphStore (Layer 0 / Zustand graph)
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 `useGraphStore` to mirror list state (ids + pagination)
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
- * └─ useGraphStore (Layer 0 / Zustand graph)
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 `useGraphStore.subscribe` and mirrors
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
- * └─ useGraphStore (Layer 0 / Zustand graph)
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 `useGraphStore` to mirror list state (ids + pagination)
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, useGraphStore, fetchEntity, registerSubscriber, unregisterSubscriber, EMPTY_LIST_STATE, serializeKey, configureEngine, attachGlobalListeners, startGarbageCollector, fetchList } from '@prometheus-ags/entity-graph-core';
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 = useGraphStore.getState();
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 useGraphStore.getState().readEntity(type, id);
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 = useGraphStore.getState();
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 = useGraphStore.subscribe(
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
- useGraphStore.getState().setEntityStale(type, id, true);
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 = useGraphStore.getState();
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 = useGraphStore.getState();
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 = useGraphStore.getState();
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 = useGraphStore.subscribe(
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 = useGraphStore.getState();
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
- useGraphStore.getState().setListStale(key, true);
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(useGraphStore.getState());
268
+ const initial = selector(graphStore.getState());
269
269
  const [value, setValue] = createSignal(initial);
270
- const unsub = useGraphStore.subscribe(
270
+ const unsub = graphStore.subscribe(
271
271
  selector,
272
272
  (next, prev) => {
273
273
  if (!eq(next, prev)) {
@@ -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.0-alpha.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.js",
8
+ "main": "./dist/index.cjs",
8
9
  "module": "./dist/index.mjs",
9
10
  "types": "./dist/index.d.ts",
10
11
  "exports": {
11
12
  ".": {
12
- "types": "./dist/index.d.ts",
13
- "import": "./dist/index.mjs",
14
- "require": "./dist/index.js"
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.7",
37
- "tsup": ">=8",
38
- "typescript": ">=6",
39
- "vitest": ">=4"
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"]}