@prometheus-ags/entity-graph-solid 3.2.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @prometheus-ags/entity-graph-solid
2
2
 
3
+ ## 4.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [d1588d8]
8
+ - Updated dependencies
9
+ - @prometheus-ags/entity-graph-core@4.0.0
10
+
3
11
  ## 3.2.0
4
12
 
5
13
  ### Minor Changes
package/package.json CHANGED
@@ -1,23 +1,17 @@
1
1
  {
2
2
  "name": "@prometheus-ags/entity-graph-solid",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
4
4
  "description": "SolidJS bindings for @prometheus-ags/entity-graph-core — fine-grained reactive entity and list primitives over the normalized entity graph.",
5
5
  "author": "Travis James <travis@prometheus.dev>",
6
6
  "license": "MIT",
7
7
  "type": "module",
8
- "main": "./dist/index.cjs",
8
+ "main": "./dist/index.mjs",
9
9
  "module": "./dist/index.mjs",
10
10
  "types": "./dist/index.d.ts",
11
11
  "exports": {
12
12
  ".": {
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
- }
13
+ "types": "./dist/index.d.ts",
14
+ "default": "./dist/index.mjs"
21
15
  }
22
16
  },
23
17
  "sideEffects": false,
@@ -31,7 +25,7 @@
31
25
  },
32
26
  "peerDependencies": {
33
27
  "solid-js": ">=1.8.0",
34
- "@prometheus-ags/entity-graph-core": "^3.2.0"
28
+ "@prometheus-ags/entity-graph-core": "^4.0.0"
35
29
  },
36
30
  "peerDependenciesMeta": {
37
31
  "solid-js": {
@@ -43,7 +37,7 @@
43
37
  "tsup": "8.5.1",
44
38
  "typescript": "6.0.2",
45
39
  "vitest": "4.1.10",
46
- "@prometheus-ags/entity-graph-core": "3.2.0"
40
+ "@prometheus-ags/entity-graph-core": "4.0.0"
47
41
  },
48
42
  "keywords": [
49
43
  "solid",
package/dist/index.cjs DELETED
@@ -1,318 +0,0 @@
1
- 'use strict';
2
-
3
- var solidJs = require('solid-js');
4
- var store = require('solid-js/store');
5
- var entityGraphCore = require('@prometheus-ags/entity-graph-core');
6
-
7
- // src/create-entity.ts
8
- function createEntity(opts) {
9
- const {
10
- type,
11
- id: idAccessor,
12
- fetch,
13
- normalize,
14
- idField = "id",
15
- staleTime,
16
- enabled: enabledAccessor,
17
- onSuccess,
18
- onError
19
- } = opts;
20
- const source = () => {
21
- const id = idAccessor();
22
- const enabled = enabledAccessor ? enabledAccessor() : true;
23
- return [id ?? null, enabled];
24
- };
25
- const [snapshot, setSnapshot] = store.createStore({
26
- data: null,
27
- entityState: { ...entityGraphCore.EMPTY_ENTITY_STATE }
28
- });
29
- const [resource] = solidJs.createResource(source, async ([id, enabled]) => {
30
- if (!id || !enabled) return null;
31
- const engineOpts = entityGraphCore.getEngineOptions();
32
- const effectiveStaleTime = staleTime ?? engineOpts.defaultStaleTime;
33
- const graphState = entityGraphCore.graphStore.getState();
34
- const cachedEntityState = graphState.entityStates[`${type}:${id}`];
35
- const cachedEntity = graphState.readEntity(type, id);
36
- const age = Date.now() - (cachedEntityState?.lastFetched ?? 0);
37
- if (cachedEntity && age < effectiveStaleTime && !cachedEntityState?.stale) {
38
- setSnapshot("data", cachedEntity);
39
- setSnapshot("entityState", (s) => ({ ...s, ...cachedEntityState }));
40
- return cachedEntity;
41
- }
42
- await entityGraphCore.fetchEntity(
43
- {
44
- type,
45
- id,
46
- fetch,
47
- normalize,
48
- idField,
49
- onSuccess,
50
- onError
51
- },
52
- { ...entityGraphCore.getEngineOptions(), defaultStaleTime: effectiveStaleTime }
53
- );
54
- return entityGraphCore.graphStore.getState().readEntity(type, id);
55
- });
56
- solidJs.createEffect(() => {
57
- const id = idAccessor();
58
- if (!id) return;
59
- const subKey = `${type}:${id}`;
60
- const token = entityGraphCore.registerSubscriber(subKey);
61
- const syncFromGraph = () => {
62
- const s = entityGraphCore.graphStore.getState();
63
- const entity = s.readEntity(type, id);
64
- const entityState = s.entityStates[subKey] ?? entityGraphCore.EMPTY_ENTITY_STATE;
65
- setSnapshot("data", entity);
66
- setSnapshot("entityState", (prev) => ({ ...prev, ...entityState }));
67
- };
68
- syncFromGraph();
69
- const unsub = entityGraphCore.graphStore.subscribe(
70
- (s) => ({
71
- entity: s.readEntity(type, id),
72
- entityState: s.entityStates[subKey] ?? entityGraphCore.EMPTY_ENTITY_STATE
73
- }),
74
- (next) => {
75
- setSnapshot("data", next.entity);
76
- setSnapshot("entityState", (prev) => ({ ...prev, ...next.entityState }));
77
- },
78
- { equalityFn: shallowEqual }
79
- );
80
- solidJs.onCleanup(() => {
81
- unsub();
82
- entityGraphCore.unregisterSubscriber(subKey, token);
83
- });
84
- });
85
- const [_refetchTrigger, setRefetchTrigger] = solidJs.createSignal(0);
86
- const refetch = async () => {
87
- const id = idAccessor();
88
- if (!id) return;
89
- entityGraphCore.graphStore.getState().setEntityStale(type, id, true);
90
- await entityGraphCore.fetchEntity(
91
- { type, id, fetch, normalize, idField, onSuccess, onError },
92
- entityGraphCore.getEngineOptions()
93
- );
94
- };
95
- const isLoading = () => {
96
- resource();
97
- const es = snapshot.entityState;
98
- return resource.loading || es.isFetching && snapshot.data === null;
99
- };
100
- const isRefetching = () => {
101
- const es = snapshot.entityState;
102
- return es.isFetching && snapshot.data !== null;
103
- };
104
- const error = () => snapshot.entityState.error;
105
- const typedError = () => {
106
- const errStr = snapshot.entityState.error;
107
- if (!errStr) return null;
108
- return null;
109
- };
110
- return {
111
- data: () => snapshot.data,
112
- isLoading,
113
- isRefetching,
114
- error,
115
- typedError,
116
- refetch,
117
- entityState: () => snapshot.entityState
118
- };
119
- }
120
- function shallowEqual(a, b) {
121
- if (a === b) return true;
122
- const keysA = Object.keys(a);
123
- const keysB = Object.keys(b);
124
- if (keysA.length !== keysB.length) return false;
125
- for (const k of keysA) {
126
- if (a[k] !== b[k]) return false;
127
- }
128
- return true;
129
- }
130
- function listSliceEqual(a, b) {
131
- return a.listState === b.listState && a.entityBucket === b.entityBucket;
132
- }
133
- function createEntityList(opts) {
134
- const {
135
- type,
136
- queryKey: queryKeyAccessor,
137
- fetch,
138
- normalize,
139
- mode = "replace",
140
- staleTime,
141
- enabled: enabledAccessor,
142
- onSuccess,
143
- onError
144
- } = opts;
145
- const [slice, setSlice] = store.createStore({
146
- items: [],
147
- listState: { ...entityGraphCore.EMPTY_LIST_STATE }
148
- });
149
- function resolveItems(key) {
150
- const s = entityGraphCore.graphStore.getState();
151
- const list = s.lists[key];
152
- if (!list) return [];
153
- return list.ids.map((id) => s.readEntity(type, id)).filter((e) => e !== null);
154
- }
155
- async function doInitialFetch(key) {
156
- const s = entityGraphCore.graphStore.getState();
157
- const listState = s.lists[key];
158
- const effectiveStaleTime = staleTime ?? entityGraphCore.getEngineOptions().defaultStaleTime;
159
- const age = Date.now() - (listState?.lastFetched ?? 0);
160
- const isFresh = listState && !listState.stale && age < effectiveStaleTime;
161
- if (isFresh) return;
162
- await entityGraphCore.fetchList(
163
- {
164
- type,
165
- queryKey: JSON.parse(key),
166
- fetch,
167
- normalize,
168
- mode,
169
- onSuccess,
170
- onError
171
- },
172
- {},
173
- entityGraphCore.getEngineOptions()
174
- );
175
- }
176
- solidJs.createEffect(() => {
177
- const rawKey = queryKeyAccessor();
178
- const key = entityGraphCore.serializeKey(rawKey);
179
- const enabled = enabledAccessor ? enabledAccessor() : true;
180
- const syncFromGraph = () => {
181
- const s = entityGraphCore.graphStore.getState();
182
- const listState = s.lists[key] ?? entityGraphCore.EMPTY_LIST_STATE;
183
- const items = resolveItems(key);
184
- setSlice("listState", (prev) => ({ ...prev, ...listState }));
185
- setSlice("items", items);
186
- };
187
- syncFromGraph();
188
- const unsub = entityGraphCore.graphStore.subscribe(
189
- (s) => {
190
- const listState = s.lists[key] ?? entityGraphCore.EMPTY_LIST_STATE;
191
- const entityBucket = s.entities[type];
192
- return { listState, entityBucket };
193
- },
194
- ({ listState }) => {
195
- const items = resolveItems(key);
196
- setSlice("listState", (prev) => ({ ...prev, ...listState }));
197
- setSlice("items", items);
198
- },
199
- { equalityFn: listSliceEqual }
200
- );
201
- if (enabled && (fetch || normalize)) {
202
- void doInitialFetch(key);
203
- }
204
- solidJs.onCleanup(() => {
205
- unsub();
206
- });
207
- });
208
- async function fetchNextPage() {
209
- const key = entityGraphCore.serializeKey(queryKeyAccessor());
210
- const s = entityGraphCore.graphStore.getState();
211
- const listState = s.lists[key] ?? entityGraphCore.EMPTY_LIST_STATE;
212
- if (!listState.hasNextPage || listState.isFetchingMore) return;
213
- await entityGraphCore.fetchList(
214
- {
215
- type,
216
- queryKey: JSON.parse(key),
217
- fetch,
218
- normalize,
219
- mode: "append",
220
- onSuccess,
221
- onError
222
- },
223
- { cursor: listState.nextCursor ?? void 0 },
224
- entityGraphCore.getEngineOptions(),
225
- true
226
- // isLoadMore
227
- );
228
- }
229
- async function refetch() {
230
- const key = entityGraphCore.serializeKey(queryKeyAccessor());
231
- entityGraphCore.graphStore.getState().setListStale(key, true);
232
- await entityGraphCore.fetchList(
233
- {
234
- type,
235
- queryKey: JSON.parse(key),
236
- fetch,
237
- normalize,
238
- mode: "replace",
239
- onSuccess,
240
- onError
241
- },
242
- {},
243
- entityGraphCore.getEngineOptions()
244
- );
245
- }
246
- const isLoading = () => slice.listState.isFetching && slice.items.length === 0;
247
- const isLoadingMore = () => slice.listState.isFetchingMore;
248
- const error = () => slice.listState.error;
249
- const typedError = () => slice.listState.lastError;
250
- const total = () => slice.listState.total;
251
- const hasNextPage = () => slice.listState.hasNextPage;
252
- const hasPrevPage = () => slice.listState.hasPrevPage;
253
- return {
254
- items: () => slice.items,
255
- isLoading,
256
- isLoadingMore,
257
- error,
258
- typedError,
259
- total,
260
- hasNextPage,
261
- hasPrevPage,
262
- fetchNextPage,
263
- refetch,
264
- listState: () => slice.listState
265
- };
266
- }
267
- function createGraphStore(selector, equalityFn) {
268
- const eq = equalityFn ?? ((a, b) => a === b);
269
- const initial = selector(entityGraphCore.graphStore.getState());
270
- const [value, setValue] = solidJs.createSignal(initial);
271
- const unsub = entityGraphCore.graphStore.subscribe(
272
- selector,
273
- (next, prev) => {
274
- if (!eq(next, prev)) {
275
- setValue(() => next);
276
- }
277
- }
278
- );
279
- solidJs.onCleanup(() => unsub());
280
- return value;
281
- }
282
- function setupGraphProvider(opts = {}) {
283
- const { attachListeners = true, startGc = true, ...engineOpts } = opts;
284
- if (Object.keys(engineOpts).length > 0) {
285
- entityGraphCore.configureEngine(engineOpts);
286
- }
287
- if (attachListeners && typeof window !== "undefined") {
288
- entityGraphCore.attachGlobalListeners();
289
- }
290
- let stopGc;
291
- if (startGc && typeof window !== "undefined") {
292
- stopGc = entityGraphCore.startGarbageCollector();
293
- }
294
- return () => stopGc?.();
295
- }
296
-
297
- Object.defineProperty(exports, "TerminalError", {
298
- enumerable: true,
299
- get: function () { return entityGraphCore.TerminalError; }
300
- });
301
- Object.defineProperty(exports, "TransientError", {
302
- enumerable: true,
303
- get: function () { return entityGraphCore.TransientError; }
304
- });
305
- Object.defineProperty(exports, "graphStore", {
306
- enumerable: true,
307
- get: function () { return entityGraphCore.graphStore; }
308
- });
309
- Object.defineProperty(exports, "useGraphStore", {
310
- enumerable: true,
311
- get: function () { return entityGraphCore.useGraphStore; }
312
- });
313
- exports.createEntity = createEntity;
314
- exports.createEntityList = createEntityList;
315
- exports.createGraphStore = createGraphStore;
316
- exports.setupGraphProvider = setupGraphProvider;
317
- //# sourceMappingURL=index.cjs.map
318
- //# sourceMappingURL=index.cjs.map
@@ -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","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 DELETED
@@ -1,304 +0,0 @@
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, graphStore, useGraphStore } from '@prometheus-ags/entity-graph-core';
3
-
4
- /**
5
- * Shared option types for SolidJS entity-graph bindings.
6
- *
7
- * These mirror the engine option shapes from @prometheus-ags/entity-graph-core
8
- * but are scoped to accessor patterns (no React-specific concepts).
9
- */
10
-
11
- /**
12
- * Options for `createEntity`. The `source` accessor is called reactively;
13
- * changing either `type` or `id` inside it re-triggers the fetch.
14
- *
15
- * @template TRaw - Shape returned by the transport fetch function.
16
- * @template TEntity - Normalized entity shape written to the graph.
17
- */
18
- interface CreateEntityOptions<TRaw, TEntity extends object> {
19
- /** Entity kind (e.g. `"Invoice"`). Drives the graph partition. */
20
- type: EntityType;
21
- /** Reactive accessor returning the entity id — return `null` / `undefined` to disable. */
22
- id: () => EntityId | null | undefined;
23
- /**
24
- * Transport fetch function. Receives the resolved `id` and returns raw data.
25
- * Called by the underlying `createResource`; deduplicated by the core engine.
26
- */
27
- fetch: (id: EntityId) => Promise<TRaw>;
28
- /** Maps raw API response to the canonical shape upserted into the graph. */
29
- normalize: (raw: TRaw) => TEntity;
30
- /**
31
- * Name of the id field inside the normalized entity (default `"id"`).
32
- * Used when the server-returned id may differ from the request id.
33
- */
34
- idField?: string;
35
- /**
36
- * Milliseconds before a cached entity is considered stale and a background
37
- * refetch is triggered (inherits from `configureEngine` when omitted).
38
- */
39
- staleTime?: number;
40
- /** Set to `false` to skip fetching regardless of id presence (default `true`). */
41
- enabled?: () => boolean;
42
- /** Called after a successful fetch and graph write. */
43
- onSuccess?: (entity: TEntity) => void;
44
- /** Called after all retries are exhausted. */
45
- onError?: (error: Error) => void;
46
- }
47
- /**
48
- * Options for `createEntityList`. The `queryKey` accessor is called reactively;
49
- * changing the key triggers a new list fetch.
50
- *
51
- * @template TRaw - Shape of each raw row from the transport.
52
- * @template TEntity - Normalized entity shape written to the graph.
53
- */
54
- interface CreateEntityListOptions<TRaw, TEntity extends object> {
55
- /** Entity kind for all items in this list. */
56
- type: EntityType;
57
- /**
58
- * Reactive accessor returning the list query key array.
59
- * Changing any element triggers a re-fetch. Use stable primitive values.
60
- */
61
- queryKey: () => unknown[];
62
- /**
63
- * Transport fetcher for the full list. Optional for graph-only (e.g. local-first
64
- * PGlite) scenarios where the graph is hydrated out-of-band.
65
- */
66
- fetch?: (params: {
67
- cursor?: string;
68
- page?: number;
69
- pageSize?: number;
70
- params?: Record<string, unknown>;
71
- }) => Promise<{
72
- items: TRaw[];
73
- total?: number | null;
74
- nextCursor?: string | null;
75
- prevCursor?: string;
76
- hasNextPage?: boolean;
77
- hasPrevPage?: boolean;
78
- page?: number;
79
- pageSize?: number;
80
- }>;
81
- /** Row normalizer: maps a raw row to `{ id, data }` for graph upsert. */
82
- normalize?: (raw: TRaw) => {
83
- id: EntityId;
84
- data: TEntity;
85
- };
86
- /** Replace or append ids on re-fetch (default `"replace"`). */
87
- mode?: "replace" | "append";
88
- /** Milliseconds before the list is re-fetched in the background. */
89
- staleTime?: number;
90
- /** Reactive accessor; set to `false` to pause fetching (default `true`). */
91
- enabled?: () => boolean;
92
- /** Called after a successful list fetch. */
93
- onSuccess?: (result: {
94
- items: TRaw[];
95
- total?: number | null;
96
- }) => void;
97
- /** Called after all retries are exhausted. */
98
- onError?: (error: Error) => void;
99
- }
100
- /**
101
- * Return value of `createEntity`.
102
- * All fields are SolidJS signals (fine-grained, no re-render of unrelated parts).
103
- */
104
- interface CreateEntityReturn<TEntity extends object> {
105
- /** The merged entity (canonical + patch overlay) or `null` when not yet loaded. */
106
- data: () => TEntity | null;
107
- /** `true` while the initial or background fetch is in-flight. */
108
- isLoading: () => boolean;
109
- /** `true` only during background SWR refetch (data already present). */
110
- isRefetching: () => boolean;
111
- /** Per-entity fetch error string from the graph, or `null`. */
112
- error: () => string | null;
113
- /**
114
- * Typed `EntityError` instance (`TerminalError` | `TransientError`) so
115
- * consumers can `instanceof`-check the failure mode without string parsing.
116
- */
117
- typedError: () => EntityError | null;
118
- /** Imperatively trigger a refetch, bypassing staleness checks. */
119
- refetch: () => Promise<void>;
120
- /** Raw `EntityState` slice for advanced use (isFetching, stale, lastFetched). */
121
- entityState: () => EntityState;
122
- }
123
- /**
124
- * Return value of `createEntityList`.
125
- * Items are fine-grained: each row is individually reactive via SolidJS signals.
126
- */
127
- interface CreateEntityListReturn<TEntity extends object> {
128
- /** Resolved entity objects in list order (ids joined to graph at read time). */
129
- items: () => TEntity[];
130
- /** `true` while the initial list fetch is in-flight. */
131
- isLoading: () => boolean;
132
- /** `true` while a "load more" / pagination fetch is in-flight. */
133
- isLoadingMore: () => boolean;
134
- /** List fetch error string, or `null`. */
135
- error: () => string | null;
136
- /** Typed error instance for `instanceof` checks. */
137
- typedError: () => EntityError | null;
138
- /** Total entity count from the server (may be `null` before first fetch). */
139
- total: () => number | null;
140
- /** `true` when a next page is available. */
141
- hasNextPage: () => boolean;
142
- /** `true` when a previous page is available. */
143
- hasPrevPage: () => boolean;
144
- /** Fetch the next page (appends when `mode` is `"append"`). */
145
- fetchNextPage: () => Promise<void>;
146
- /** Refetch the list from scratch (replaces ids). */
147
- refetch: () => Promise<void>;
148
- /** Raw `ListState` slice for advanced pagination/meta access. */
149
- listState: () => ListState;
150
- }
151
-
152
- /**
153
- * createEntity — SolidJS primitive that bridges a single entity to the
154
- * normalized entity graph via `createResource`.
155
- *
156
- * Architecture:
157
- * createEntity (this file, Layer 2)
158
- * └─ core engine fetchEntity (Layer 1 / transport)
159
- * └─ graphStore (Layer 0 / Zustand graph)
160
- *
161
- * SolidJS `createResource` owns the async lifecycle; the graph is the
162
- * single source of truth for the entity payload. Once fetchEntity writes to
163
- * the graph, a Zustand subscription (via `createStore`) propagates the merged
164
- * entity back to SolidJS as a fine-grained reactive signal.
165
- */
166
-
167
- /**
168
- * Creates a fine-grained SolidJS reactive primitive for a single entity.
169
- *
170
- * - Uses `createResource` as the async driver so Suspense and ErrorBoundary
171
- * boundaries work naturally.
172
- * - Subscribes to the Zustand graph with `graphStore.subscribe` and mirrors
173
- * the merged entity into a `createStore`-backed signal for fine-grained
174
- * SolidJS reactivity (only the changed field triggers updates).
175
- * - Registers / unregisters a subscriber token with the core engine so idle
176
- * entities stop background revalidation when no components are observing them.
177
- *
178
- * @example
179
- * ```tsx
180
- * const invoice = createEntity({
181
- * type: "Invoice",
182
- * id: () => props.invoiceId,
183
- * fetch: (id) => api.get(`/invoices/${id}`),
184
- * normalize: (raw) => ({ ...raw, id: raw.invoice_id }),
185
- * });
186
- *
187
- * return (
188
- * <Show when={!invoice.isLoading()} fallback={<Spinner />}>
189
- * <p>{invoice.data()?.amount}</p>
190
- * </Show>
191
- * );
192
- * ```
193
- */
194
- declare function createEntity<TRaw, TEntity extends object>(opts: CreateEntityOptions<TRaw, TEntity>): CreateEntityReturn<TEntity>;
195
-
196
- /**
197
- * createEntityList — SolidJS primitive for reactive normalized entity lists.
198
- *
199
- * Architecture:
200
- * createEntityList (this file, Layer 2)
201
- * └─ core engine fetchList (Layer 1 / transport)
202
- * └─ graphStore (Layer 0 / Zustand graph)
203
- *
204
- * A Zustand `subscribe` bridges the graph's `lists[key]` slot + entity rows
205
- * into a SolidJS `createStore`, giving SolidJS fine-grained per-row reactivity.
206
- * Changing `queryKey()` re-serializes to a new list slot, triggering a fresh
207
- * fetch while keeping existing data visible (stale-while-revalidate).
208
- */
209
-
210
- /**
211
- * Creates a fine-grained SolidJS reactive primitive for an entity list.
212
- *
213
- * - Subscribes to `graphStore` to mirror list state (ids + pagination)
214
- * and resolves the entity rows by joining ids → graph entities on each read.
215
- * - Changing `queryKey()` triggers a new fetch and subscribes to the new list
216
- * slot; the previous subscription is cleaned up automatically.
217
- * - Supports pagination (`fetchNextPage`) and imperative refetch.
218
- *
219
- * @example
220
- * ```tsx
221
- * const invoices = createEntityList({
222
- * type: "Invoice",
223
- * queryKey: () => ["invoices", { status: filter() }],
224
- * fetch: (params) => api.getInvoices(params),
225
- * normalize: (raw) => ({ id: raw.invoice_id, data: { ...raw } }),
226
- * });
227
- *
228
- * return (
229
- * <For each={invoices.items()}>
230
- * {(invoice) => <InvoiceRow invoice={invoice} />}
231
- * </For>
232
- * );
233
- * ```
234
- */
235
- declare function createEntityList<TRaw, TEntity extends object>(opts: CreateEntityListOptions<TRaw, TEntity>): CreateEntityListReturn<TEntity>;
236
-
237
- /**
238
- * createGraphStore — SolidJS fine-grained reactive bridge to the entire
239
- * Zustand entity graph.
240
- *
241
- * Consumers that need ad-hoc access to the graph outside `createEntity` /
242
- * `createEntityList` can use this to read any slice of `GraphState` as a
243
- * SolidJS signal. The subscription is scoped to only the selected slice,
244
- * so unrelated graph mutations do not trigger re-computations.
245
- *
246
- * @example
247
- * ```ts
248
- * // Read total entity count for a devtools panel.
249
- * const entityCount = createGraphStore(
250
- * (s) => Object.keys(s.entities).reduce((n, t) => n + Object.keys(s.entities[t]).length, 0),
251
- * );
252
- * console.log(entityCount()); // reactive SolidJS accessor
253
- * ```
254
- */
255
-
256
- /**
257
- * Subscribe to a selector over the entity graph.
258
- * Returns a SolidJS accessor that updates whenever the selected slice changes.
259
- *
260
- * @param selector - Pure function that maps the full `GraphState` to a derived value.
261
- * @param equalityFn - Optional equality check (default: strict reference equality `===`).
262
- */
263
- declare function createGraphStore<T>(selector: (state: GraphState) => T, equalityFn?: (a: T, b: T) => boolean): () => T;
264
-
265
- /**
266
- * GraphProvider utilities for SolidJS.
267
- *
268
- * Provides boot-time setup helpers for attaching global focus/reconnect
269
- * listeners and configuring the engine from within a SolidJS application root.
270
- *
271
- * Usage in the application root (e.g. `src/index.tsx`):
272
- *
273
- * ```ts
274
- * import { setupGraphProvider } from "@prometheus-ags/entity-graph-solid";
275
- * import { configureEngine, attachGlobalListeners } from "@prometheus-ags/entity-graph-core";
276
- *
277
- * setupGraphProvider({
278
- * staleTime: 60_000,
279
- * revalidateOnFocus: true,
280
- * });
281
- * ```
282
- */
283
-
284
- interface GraphProviderOptions extends EngineOptions {
285
- /**
286
- * Whether to attach window focus/online listeners for stale-while-revalidate.
287
- * Defaults to `true` in browser environments.
288
- */
289
- attachListeners?: boolean;
290
- /**
291
- * Whether to start the periodic garbage collector.
292
- * Defaults to `true`.
293
- */
294
- startGc?: boolean;
295
- }
296
- /**
297
- * Configures the entity-graph engine and wires browser-level listeners.
298
- * Call this once at application startup before mounting the root component.
299
- *
300
- * Returns a cleanup function that stops the GC interval (useful for tests).
301
- */
302
- declare function setupGraphProvider(opts?: GraphProviderOptions): () => void;
303
-
304
- export { type CreateEntityListOptions, type CreateEntityListReturn, type CreateEntityOptions, type CreateEntityReturn, type GraphProviderOptions, createEntity, createEntityList, createGraphStore, setupGraphProvider };