@spoosh/angular 0.10.2 → 0.11.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/dist/index.d.ts +195 -448
- package/dist/index.js +106 -98
- package/package.json +6 -6
- package/dist/index.d.mts +0 -628
- package/dist/index.mjs +0 -950
package/dist/index.mjs
DELETED
|
@@ -1,950 +0,0 @@
|
|
|
1
|
-
// src/injectRead/index.ts
|
|
2
|
-
import {
|
|
3
|
-
signal,
|
|
4
|
-
effect,
|
|
5
|
-
DestroyRef,
|
|
6
|
-
inject,
|
|
7
|
-
untracked
|
|
8
|
-
} from "@angular/core";
|
|
9
|
-
import {
|
|
10
|
-
createOperationController,
|
|
11
|
-
createSelectorProxy,
|
|
12
|
-
resolvePath,
|
|
13
|
-
resolveTags
|
|
14
|
-
} from "@spoosh/core";
|
|
15
|
-
function createInjectRead(options) {
|
|
16
|
-
const { api, stateManager, eventEmitter, pluginExecutor } = options;
|
|
17
|
-
return function injectRead(readFn, readOptions) {
|
|
18
|
-
const destroyRef = inject(DestroyRef);
|
|
19
|
-
const {
|
|
20
|
-
enabled: enabledOption = true,
|
|
21
|
-
tags,
|
|
22
|
-
...pluginOpts
|
|
23
|
-
} = readOptions ?? {};
|
|
24
|
-
const getEnabled = () => typeof enabledOption === "function" ? enabledOption() : enabledOption;
|
|
25
|
-
const dataSignal = signal(void 0);
|
|
26
|
-
const errorSignal = signal(void 0);
|
|
27
|
-
const loadingSignal = signal(true);
|
|
28
|
-
const fetchingSignal = signal(false);
|
|
29
|
-
const inputSignal = signal({});
|
|
30
|
-
const metaSignal = signal({});
|
|
31
|
-
let currentController = null;
|
|
32
|
-
let currentQueryKey = null;
|
|
33
|
-
let baseQueryKey = null;
|
|
34
|
-
let currentSubscription = null;
|
|
35
|
-
let currentResolvedTags = [];
|
|
36
|
-
let prevContext = null;
|
|
37
|
-
let isMounted = false;
|
|
38
|
-
const instanceId = `angular-${Math.random().toString(36).slice(2)}`;
|
|
39
|
-
const captureSelector = () => {
|
|
40
|
-
const selectorResult = {
|
|
41
|
-
call: null,
|
|
42
|
-
selector: null
|
|
43
|
-
};
|
|
44
|
-
const selectorProxy = createSelectorProxy(
|
|
45
|
-
(result2) => {
|
|
46
|
-
selectorResult.call = result2.call;
|
|
47
|
-
selectorResult.selector = result2.selector;
|
|
48
|
-
}
|
|
49
|
-
);
|
|
50
|
-
readFn(selectorProxy);
|
|
51
|
-
return selectorResult;
|
|
52
|
-
};
|
|
53
|
-
const createController = (capturedCall, resolvedTags, queryKey) => {
|
|
54
|
-
if (currentSubscription) {
|
|
55
|
-
currentSubscription();
|
|
56
|
-
}
|
|
57
|
-
const controller = createOperationController({
|
|
58
|
-
operationType: "read",
|
|
59
|
-
path: capturedCall.path,
|
|
60
|
-
method: capturedCall.method,
|
|
61
|
-
tags: resolvedTags,
|
|
62
|
-
requestOptions: capturedCall.options,
|
|
63
|
-
stateManager,
|
|
64
|
-
eventEmitter,
|
|
65
|
-
pluginExecutor,
|
|
66
|
-
instanceId,
|
|
67
|
-
fetchFn: async (fetchOpts) => {
|
|
68
|
-
const pathMethods = api(capturedCall.path);
|
|
69
|
-
const method = pathMethods[capturedCall.method];
|
|
70
|
-
return method(fetchOpts);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
controller.setPluginOptions(pluginOpts);
|
|
74
|
-
currentSubscription = controller.subscribe(() => {
|
|
75
|
-
const state = controller.getState();
|
|
76
|
-
dataSignal.set(state.data);
|
|
77
|
-
errorSignal.set(state.error);
|
|
78
|
-
const entry = stateManager.getCache(queryKey);
|
|
79
|
-
const newMeta = entry?.meta ? Object.fromEntries(entry.meta) : {};
|
|
80
|
-
metaSignal.set(newMeta);
|
|
81
|
-
});
|
|
82
|
-
currentController = controller;
|
|
83
|
-
currentQueryKey = queryKey;
|
|
84
|
-
currentResolvedTags = resolvedTags;
|
|
85
|
-
return controller;
|
|
86
|
-
};
|
|
87
|
-
const executeWithTracking = async (controller, force = false, overrideOptions) => {
|
|
88
|
-
const hasData = dataSignal() !== void 0;
|
|
89
|
-
loadingSignal.set(!hasData);
|
|
90
|
-
fetchingSignal.set(true);
|
|
91
|
-
errorSignal.set(void 0);
|
|
92
|
-
try {
|
|
93
|
-
const execOptions = overrideOptions ? {
|
|
94
|
-
...currentController?.getContext().request,
|
|
95
|
-
...overrideOptions
|
|
96
|
-
} : void 0;
|
|
97
|
-
const response = await controller.execute(execOptions, { force });
|
|
98
|
-
if (response.error) {
|
|
99
|
-
errorSignal.set(response.error);
|
|
100
|
-
} else {
|
|
101
|
-
errorSignal.set(void 0);
|
|
102
|
-
}
|
|
103
|
-
if (response.data !== void 0) {
|
|
104
|
-
dataSignal.set(response.data);
|
|
105
|
-
}
|
|
106
|
-
return response;
|
|
107
|
-
} catch (err) {
|
|
108
|
-
errorSignal.set(err);
|
|
109
|
-
return { error: err };
|
|
110
|
-
} finally {
|
|
111
|
-
loadingSignal.set(false);
|
|
112
|
-
fetchingSignal.set(false);
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
const initialSelectorResult = captureSelector();
|
|
116
|
-
const initialCapturedCall = initialSelectorResult.call;
|
|
117
|
-
if (!initialCapturedCall) {
|
|
118
|
-
throw new Error(
|
|
119
|
-
'injectRead requires calling an HTTP method (GET). Example: injectRead((api) => api("posts").GET())'
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
const initialRequestOptions = initialCapturedCall.options;
|
|
123
|
-
const initialPathSegments = initialCapturedCall.path.split("/").filter(Boolean);
|
|
124
|
-
const initialResolvedPath = resolvePath(
|
|
125
|
-
initialPathSegments,
|
|
126
|
-
initialRequestOptions?.params
|
|
127
|
-
);
|
|
128
|
-
const initialResolvedTags = resolveTags(
|
|
129
|
-
tags !== void 0 ? { tags } : void 0,
|
|
130
|
-
initialResolvedPath
|
|
131
|
-
);
|
|
132
|
-
const initialQueryKey = stateManager.createQueryKey({
|
|
133
|
-
path: initialCapturedCall.path,
|
|
134
|
-
method: initialCapturedCall.method,
|
|
135
|
-
options: initialCapturedCall.options
|
|
136
|
-
});
|
|
137
|
-
createController(initialCapturedCall, initialResolvedTags, initialQueryKey);
|
|
138
|
-
baseQueryKey = initialQueryKey;
|
|
139
|
-
loadingSignal.set(false);
|
|
140
|
-
let wasEnabled = false;
|
|
141
|
-
effect(
|
|
142
|
-
() => {
|
|
143
|
-
const isEnabled = getEnabled();
|
|
144
|
-
const selectorResult = captureSelector();
|
|
145
|
-
const capturedCall = selectorResult.call;
|
|
146
|
-
if (!capturedCall) {
|
|
147
|
-
throw new Error(
|
|
148
|
-
'injectRead requires calling an HTTP method (GET). Example: injectRead((api) => api("posts").GET())'
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
const requestOptions = capturedCall.options;
|
|
152
|
-
const pathSegments = capturedCall.path.split("/").filter(Boolean);
|
|
153
|
-
const resolvedPath = resolvePath(pathSegments, requestOptions?.params);
|
|
154
|
-
const resolvedTags = resolveTags(
|
|
155
|
-
tags !== void 0 ? { tags } : void 0,
|
|
156
|
-
resolvedPath
|
|
157
|
-
);
|
|
158
|
-
const queryKey = stateManager.createQueryKey({
|
|
159
|
-
path: capturedCall.path,
|
|
160
|
-
method: capturedCall.method,
|
|
161
|
-
options: capturedCall.options
|
|
162
|
-
});
|
|
163
|
-
const opts = capturedCall.options;
|
|
164
|
-
const inputInner = {};
|
|
165
|
-
if (opts?.query !== void 0) {
|
|
166
|
-
inputInner.query = opts.query;
|
|
167
|
-
}
|
|
168
|
-
if (opts?.body !== void 0) {
|
|
169
|
-
inputInner.body = opts.body;
|
|
170
|
-
}
|
|
171
|
-
if (opts?.params !== void 0) {
|
|
172
|
-
inputInner.params = opts.params;
|
|
173
|
-
}
|
|
174
|
-
inputSignal.set(inputInner);
|
|
175
|
-
const baseQueryKeyChanged = queryKey !== baseQueryKey;
|
|
176
|
-
const enabledChanged = isEnabled !== wasEnabled;
|
|
177
|
-
wasEnabled = isEnabled;
|
|
178
|
-
if (baseQueryKeyChanged) {
|
|
179
|
-
baseQueryKey = queryKey;
|
|
180
|
-
if (currentController) {
|
|
181
|
-
prevContext = currentController.getContext();
|
|
182
|
-
if (isMounted) {
|
|
183
|
-
currentController.unmount();
|
|
184
|
-
isMounted = false;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
const controller = createController(
|
|
188
|
-
capturedCall,
|
|
189
|
-
resolvedTags,
|
|
190
|
-
queryKey
|
|
191
|
-
);
|
|
192
|
-
if (prevContext) {
|
|
193
|
-
controller.update(prevContext);
|
|
194
|
-
prevContext = null;
|
|
195
|
-
}
|
|
196
|
-
if (isEnabled) {
|
|
197
|
-
controller.mount();
|
|
198
|
-
isMounted = true;
|
|
199
|
-
untracked(() => {
|
|
200
|
-
executeWithTracking(controller, false);
|
|
201
|
-
});
|
|
202
|
-
} else {
|
|
203
|
-
loadingSignal.set(false);
|
|
204
|
-
}
|
|
205
|
-
} else if (enabledChanged && currentController) {
|
|
206
|
-
if (isEnabled && !isMounted) {
|
|
207
|
-
currentController.mount();
|
|
208
|
-
isMounted = true;
|
|
209
|
-
untracked(() => {
|
|
210
|
-
executeWithTracking(currentController, false);
|
|
211
|
-
});
|
|
212
|
-
} else if (!isEnabled && isMounted) {
|
|
213
|
-
currentController.unmount();
|
|
214
|
-
isMounted = false;
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
if (!isEnabled) {
|
|
218
|
-
loadingSignal.set(false);
|
|
219
|
-
return;
|
|
220
|
-
}
|
|
221
|
-
const unsubRefetch = eventEmitter.on(
|
|
222
|
-
"refetch",
|
|
223
|
-
(event) => {
|
|
224
|
-
if (event.queryKey === currentQueryKey && currentController) {
|
|
225
|
-
untracked(() => {
|
|
226
|
-
executeWithTracking(currentController, true);
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
);
|
|
231
|
-
const unsubInvalidate = eventEmitter.on(
|
|
232
|
-
"invalidate",
|
|
233
|
-
(invalidatedTags) => {
|
|
234
|
-
const hasMatch = invalidatedTags.some(
|
|
235
|
-
(tag) => currentResolvedTags.includes(tag)
|
|
236
|
-
);
|
|
237
|
-
if (hasMatch && currentController) {
|
|
238
|
-
untracked(() => {
|
|
239
|
-
executeWithTracking(currentController, true);
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
);
|
|
244
|
-
const unsubRefetchAll = eventEmitter.on("refetchAll", () => {
|
|
245
|
-
if (currentController) {
|
|
246
|
-
untracked(() => {
|
|
247
|
-
executeWithTracking(currentController, true);
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
return () => {
|
|
252
|
-
unsubRefetch();
|
|
253
|
-
unsubInvalidate();
|
|
254
|
-
unsubRefetchAll();
|
|
255
|
-
};
|
|
256
|
-
},
|
|
257
|
-
{ allowSignalWrites: true }
|
|
258
|
-
);
|
|
259
|
-
destroyRef.onDestroy(() => {
|
|
260
|
-
if (currentSubscription) {
|
|
261
|
-
currentSubscription();
|
|
262
|
-
}
|
|
263
|
-
if (currentController && isMounted) {
|
|
264
|
-
currentController.unmount();
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
const abort = () => {
|
|
268
|
-
currentController?.abort();
|
|
269
|
-
};
|
|
270
|
-
const trigger = async (triggerOptions) => {
|
|
271
|
-
const { force = true, ...overrideOptions } = triggerOptions ?? {};
|
|
272
|
-
const hasOverrides = Object.keys(overrideOptions).length > 0;
|
|
273
|
-
if (!hasOverrides) {
|
|
274
|
-
if (!currentController) {
|
|
275
|
-
return Promise.resolve({ data: void 0, error: void 0 });
|
|
276
|
-
}
|
|
277
|
-
return executeWithTracking(currentController, force, void 0);
|
|
278
|
-
}
|
|
279
|
-
const selectorResult = captureSelector();
|
|
280
|
-
const capturedCall = selectorResult.call;
|
|
281
|
-
if (!capturedCall) {
|
|
282
|
-
return Promise.resolve({ data: void 0, error: void 0 });
|
|
283
|
-
}
|
|
284
|
-
const mergedOptions = {
|
|
285
|
-
...capturedCall.options ?? {},
|
|
286
|
-
...overrideOptions
|
|
287
|
-
};
|
|
288
|
-
const pathSegments = capturedCall.path.split("/").filter(Boolean);
|
|
289
|
-
const newQueryKey = stateManager.createQueryKey({
|
|
290
|
-
path: capturedCall.path,
|
|
291
|
-
method: capturedCall.method,
|
|
292
|
-
options: mergedOptions
|
|
293
|
-
});
|
|
294
|
-
if (newQueryKey === currentQueryKey && currentController) {
|
|
295
|
-
return executeWithTracking(currentController, force, overrideOptions);
|
|
296
|
-
}
|
|
297
|
-
const params = mergedOptions?.params;
|
|
298
|
-
const newResolvedPath = resolvePath(pathSegments, params);
|
|
299
|
-
const newResolvedTags = resolveTags(
|
|
300
|
-
tags !== void 0 ? { tags } : void 0,
|
|
301
|
-
newResolvedPath
|
|
302
|
-
);
|
|
303
|
-
const newController = createController(
|
|
304
|
-
{ ...capturedCall, options: mergedOptions },
|
|
305
|
-
newResolvedTags,
|
|
306
|
-
newQueryKey
|
|
307
|
-
);
|
|
308
|
-
newController.mount();
|
|
309
|
-
isMounted = true;
|
|
310
|
-
return executeWithTracking(newController, force, void 0);
|
|
311
|
-
};
|
|
312
|
-
const result = {
|
|
313
|
-
meta: metaSignal,
|
|
314
|
-
get input() {
|
|
315
|
-
return inputSignal();
|
|
316
|
-
},
|
|
317
|
-
data: dataSignal,
|
|
318
|
-
error: errorSignal,
|
|
319
|
-
loading: loadingSignal,
|
|
320
|
-
fetching: fetchingSignal,
|
|
321
|
-
abort,
|
|
322
|
-
trigger
|
|
323
|
-
};
|
|
324
|
-
return result;
|
|
325
|
-
};
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// src/injectWrite/index.ts
|
|
329
|
-
import { signal as signal2, effect as effect2, DestroyRef as DestroyRef2, inject as inject2 } from "@angular/core";
|
|
330
|
-
import {
|
|
331
|
-
createOperationController as createOperationController2,
|
|
332
|
-
createSelectorProxy as createSelectorProxy2,
|
|
333
|
-
resolvePath as resolvePath2,
|
|
334
|
-
resolveTags as resolveTags2
|
|
335
|
-
} from "@spoosh/core";
|
|
336
|
-
function createInjectWrite(options) {
|
|
337
|
-
const { api, stateManager, pluginExecutor, eventEmitter } = options;
|
|
338
|
-
function injectWrite(writeFn, writeOptions) {
|
|
339
|
-
const destroyRef = inject2(DestroyRef2);
|
|
340
|
-
const captureSelector = () => {
|
|
341
|
-
const selectorResult = {
|
|
342
|
-
call: null,
|
|
343
|
-
selector: null
|
|
344
|
-
};
|
|
345
|
-
const selectorProxy = createSelectorProxy2(
|
|
346
|
-
(result2) => {
|
|
347
|
-
selectorResult.call = result2.call;
|
|
348
|
-
selectorResult.selector = result2.selector;
|
|
349
|
-
}
|
|
350
|
-
);
|
|
351
|
-
writeFn(selectorProxy);
|
|
352
|
-
if (!selectorResult.call) {
|
|
353
|
-
throw new Error(
|
|
354
|
-
'injectWrite requires calling an HTTP method (POST, PUT, PATCH, DELETE). Example: injectWrite((api) => api("posts").POST())'
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
|
-
return selectorResult.call;
|
|
358
|
-
};
|
|
359
|
-
const instanceId = `angular-${Math.random().toString(36).slice(2)}`;
|
|
360
|
-
let currentQueryKey = null;
|
|
361
|
-
let currentController = null;
|
|
362
|
-
let currentSubscription = null;
|
|
363
|
-
const dataSignal = signal2(void 0);
|
|
364
|
-
const errorSignal = signal2(void 0);
|
|
365
|
-
const loadingSignal = signal2(false);
|
|
366
|
-
const lastTriggerOptionsSignal = signal2(void 0);
|
|
367
|
-
const metaSignal = signal2({});
|
|
368
|
-
destroyRef.onDestroy(() => {
|
|
369
|
-
if (currentSubscription) {
|
|
370
|
-
currentSubscription();
|
|
371
|
-
}
|
|
372
|
-
});
|
|
373
|
-
const abort = () => {
|
|
374
|
-
currentController?.abort();
|
|
375
|
-
};
|
|
376
|
-
const trigger = async (triggerOptions) => {
|
|
377
|
-
const selectedEndpoint = captureSelector();
|
|
378
|
-
const params = triggerOptions?.params;
|
|
379
|
-
const pathSegments = selectedEndpoint.path.split("/").filter(Boolean);
|
|
380
|
-
const resolvedPath = resolvePath2(pathSegments, params);
|
|
381
|
-
const tags = resolveTags2(triggerOptions, resolvedPath);
|
|
382
|
-
const queryKey = stateManager.createQueryKey({
|
|
383
|
-
path: selectedEndpoint.path,
|
|
384
|
-
method: selectedEndpoint.method,
|
|
385
|
-
options: triggerOptions
|
|
386
|
-
});
|
|
387
|
-
const needsNewController = !currentController || currentQueryKey !== queryKey;
|
|
388
|
-
if (needsNewController) {
|
|
389
|
-
if (currentSubscription) {
|
|
390
|
-
currentSubscription();
|
|
391
|
-
}
|
|
392
|
-
const controller = createOperationController2({
|
|
393
|
-
operationType: "write",
|
|
394
|
-
path: selectedEndpoint.path,
|
|
395
|
-
method: selectedEndpoint.method,
|
|
396
|
-
tags,
|
|
397
|
-
stateManager,
|
|
398
|
-
eventEmitter,
|
|
399
|
-
pluginExecutor,
|
|
400
|
-
instanceId,
|
|
401
|
-
fetchFn: async (fetchOpts) => {
|
|
402
|
-
const pathMethods = api(selectedEndpoint.path);
|
|
403
|
-
const method = pathMethods[selectedEndpoint.method];
|
|
404
|
-
return method(fetchOpts);
|
|
405
|
-
}
|
|
406
|
-
});
|
|
407
|
-
currentSubscription = controller.subscribe(() => {
|
|
408
|
-
const state = controller.getState();
|
|
409
|
-
dataSignal.set(state.data);
|
|
410
|
-
errorSignal.set(state.error);
|
|
411
|
-
const entry = stateManager.getCache(queryKey);
|
|
412
|
-
const newMeta = entry?.meta ? Object.fromEntries(entry.meta) : {};
|
|
413
|
-
metaSignal.set(newMeta);
|
|
414
|
-
});
|
|
415
|
-
currentController = controller;
|
|
416
|
-
currentQueryKey = queryKey;
|
|
417
|
-
}
|
|
418
|
-
lastTriggerOptionsSignal.set(triggerOptions);
|
|
419
|
-
loadingSignal.set(true);
|
|
420
|
-
errorSignal.set(void 0);
|
|
421
|
-
const mergedOptions = { ...writeOptions, ...triggerOptions, tags };
|
|
422
|
-
currentController.setPluginOptions(mergedOptions);
|
|
423
|
-
try {
|
|
424
|
-
const response = await currentController.execute(triggerOptions, {
|
|
425
|
-
force: true
|
|
426
|
-
});
|
|
427
|
-
if (response.error) {
|
|
428
|
-
errorSignal.set(response.error);
|
|
429
|
-
} else {
|
|
430
|
-
errorSignal.set(void 0);
|
|
431
|
-
}
|
|
432
|
-
return response;
|
|
433
|
-
} catch (err) {
|
|
434
|
-
errorSignal.set(err);
|
|
435
|
-
return { error: err };
|
|
436
|
-
} finally {
|
|
437
|
-
loadingSignal.set(false);
|
|
438
|
-
}
|
|
439
|
-
};
|
|
440
|
-
const inputSignal = signal2({});
|
|
441
|
-
effect2(
|
|
442
|
-
() => {
|
|
443
|
-
const opts = lastTriggerOptionsSignal();
|
|
444
|
-
const inputInner = {};
|
|
445
|
-
if (opts?.query !== void 0) {
|
|
446
|
-
inputInner.query = opts.query;
|
|
447
|
-
}
|
|
448
|
-
if (opts?.body !== void 0) {
|
|
449
|
-
inputInner.body = opts.body;
|
|
450
|
-
}
|
|
451
|
-
if (opts?.params !== void 0) {
|
|
452
|
-
inputInner.params = opts.params;
|
|
453
|
-
}
|
|
454
|
-
inputSignal.set(inputInner);
|
|
455
|
-
},
|
|
456
|
-
{ allowSignalWrites: true }
|
|
457
|
-
);
|
|
458
|
-
const result = {
|
|
459
|
-
trigger,
|
|
460
|
-
meta: metaSignal,
|
|
461
|
-
input: inputSignal,
|
|
462
|
-
data: dataSignal,
|
|
463
|
-
error: errorSignal,
|
|
464
|
-
loading: loadingSignal,
|
|
465
|
-
abort
|
|
466
|
-
};
|
|
467
|
-
return result;
|
|
468
|
-
}
|
|
469
|
-
return injectWrite;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// src/injectPages/index.ts
|
|
473
|
-
import {
|
|
474
|
-
signal as signal3,
|
|
475
|
-
computed,
|
|
476
|
-
effect as effect3,
|
|
477
|
-
DestroyRef as DestroyRef3,
|
|
478
|
-
inject as inject3,
|
|
479
|
-
untracked as untracked2
|
|
480
|
-
} from "@angular/core";
|
|
481
|
-
import {
|
|
482
|
-
createInfiniteReadController,
|
|
483
|
-
createSelectorProxy as createSelectorProxy3,
|
|
484
|
-
resolvePath as resolvePath3,
|
|
485
|
-
resolveTags as resolveTags3
|
|
486
|
-
} from "@spoosh/core";
|
|
487
|
-
function createInjectPages(options) {
|
|
488
|
-
const { api, stateManager, eventEmitter, pluginExecutor } = options;
|
|
489
|
-
return function injectPages(readFn, readOptions) {
|
|
490
|
-
const destroyRef = inject3(DestroyRef3);
|
|
491
|
-
const {
|
|
492
|
-
enabled: enabledOption = true,
|
|
493
|
-
tags,
|
|
494
|
-
canFetchNext,
|
|
495
|
-
nextPageRequest,
|
|
496
|
-
merger,
|
|
497
|
-
canFetchPrev,
|
|
498
|
-
prevPageRequest,
|
|
499
|
-
...pluginOpts
|
|
500
|
-
} = readOptions;
|
|
501
|
-
const getEnabled = () => typeof enabledOption === "function" ? enabledOption() : enabledOption;
|
|
502
|
-
const callbackRefs = {
|
|
503
|
-
canFetchNext,
|
|
504
|
-
canFetchPrev,
|
|
505
|
-
nextPageRequest,
|
|
506
|
-
prevPageRequest,
|
|
507
|
-
merger
|
|
508
|
-
};
|
|
509
|
-
const captureSelector = () => {
|
|
510
|
-
const selectorResult = {
|
|
511
|
-
call: null,
|
|
512
|
-
selector: null
|
|
513
|
-
};
|
|
514
|
-
const selectorProxy = createSelectorProxy3(
|
|
515
|
-
(result2) => {
|
|
516
|
-
selectorResult.call = result2.call;
|
|
517
|
-
selectorResult.selector = result2.selector;
|
|
518
|
-
}
|
|
519
|
-
);
|
|
520
|
-
readFn(selectorProxy);
|
|
521
|
-
if (!selectorResult.call) {
|
|
522
|
-
throw new Error(
|
|
523
|
-
'injectPages requires calling an HTTP method (GET). Example: injectPages((api) => api("posts").GET())'
|
|
524
|
-
);
|
|
525
|
-
}
|
|
526
|
-
return selectorResult.call;
|
|
527
|
-
};
|
|
528
|
-
const instanceId = `angular-${Math.random().toString(36).slice(2)}`;
|
|
529
|
-
const dataSignal = signal3(void 0);
|
|
530
|
-
const pagesSignal = signal3([]);
|
|
531
|
-
const errorSignal = signal3(void 0);
|
|
532
|
-
const loadingSignal = signal3(false);
|
|
533
|
-
const canFetchNextSignal = signal3(false);
|
|
534
|
-
const canFetchPrevSignal = signal3(false);
|
|
535
|
-
const fetchingNextSignal = signal3(false);
|
|
536
|
-
const fetchingPrevSignal = signal3(false);
|
|
537
|
-
let currentController = null;
|
|
538
|
-
let currentQueryKey = null;
|
|
539
|
-
let currentSubscription = null;
|
|
540
|
-
let currentResolvedTags = [];
|
|
541
|
-
let prevContext = null;
|
|
542
|
-
let isMounted = false;
|
|
543
|
-
let unsubInvalidate = null;
|
|
544
|
-
let unsubRefetchAll = null;
|
|
545
|
-
const updateSignalsFromState = () => {
|
|
546
|
-
if (!currentController) return;
|
|
547
|
-
const state = currentController.getState();
|
|
548
|
-
dataSignal.set(state.data);
|
|
549
|
-
pagesSignal.set(
|
|
550
|
-
state.pages
|
|
551
|
-
);
|
|
552
|
-
errorSignal.set(state.error);
|
|
553
|
-
canFetchNextSignal.set(state.canFetchNext);
|
|
554
|
-
canFetchPrevSignal.set(state.canFetchPrev);
|
|
555
|
-
};
|
|
556
|
-
const triggerFetch = () => {
|
|
557
|
-
if (!currentController) return;
|
|
558
|
-
const currentState = currentController.getState();
|
|
559
|
-
const isFetching = untracked2(
|
|
560
|
-
() => fetchingNextSignal() || fetchingPrevSignal()
|
|
561
|
-
);
|
|
562
|
-
if (currentState.data === void 0 && !isFetching) {
|
|
563
|
-
loadingSignal.set(true);
|
|
564
|
-
fetchingNextSignal.set(true);
|
|
565
|
-
errorSignal.set(void 0);
|
|
566
|
-
currentController.fetchNext().finally(() => {
|
|
567
|
-
updateSignalsFromState();
|
|
568
|
-
loadingSignal.set(false);
|
|
569
|
-
fetchingNextSignal.set(false);
|
|
570
|
-
});
|
|
571
|
-
} else if (currentState.data !== void 0) {
|
|
572
|
-
updateSignalsFromState();
|
|
573
|
-
}
|
|
574
|
-
};
|
|
575
|
-
const createController = (capturedCall, resolvedTags, queryKey) => {
|
|
576
|
-
if (currentSubscription) {
|
|
577
|
-
currentSubscription();
|
|
578
|
-
}
|
|
579
|
-
if (unsubInvalidate) {
|
|
580
|
-
unsubInvalidate();
|
|
581
|
-
}
|
|
582
|
-
if (unsubRefetchAll) {
|
|
583
|
-
unsubRefetchAll();
|
|
584
|
-
}
|
|
585
|
-
const requestOptions = capturedCall.options;
|
|
586
|
-
const initialRequest = {
|
|
587
|
-
query: requestOptions?.query,
|
|
588
|
-
params: requestOptions?.params,
|
|
589
|
-
body: requestOptions?.body
|
|
590
|
-
};
|
|
591
|
-
const controller = createInfiniteReadController({
|
|
592
|
-
path: capturedCall.path,
|
|
593
|
-
method: capturedCall.method,
|
|
594
|
-
tags: resolvedTags,
|
|
595
|
-
initialRequest,
|
|
596
|
-
canFetchNext: canFetchNext ? (ctx) => callbackRefs.canFetchNext?.(ctx) ?? false : void 0,
|
|
597
|
-
canFetchPrev: canFetchPrev ? (ctx) => callbackRefs.canFetchPrev?.(ctx) ?? false : void 0,
|
|
598
|
-
nextPageRequest: nextPageRequest ? (ctx) => callbackRefs.nextPageRequest?.(ctx) ?? {} : void 0,
|
|
599
|
-
prevPageRequest: prevPageRequest ? (ctx) => callbackRefs.prevPageRequest?.(ctx) ?? {} : void 0,
|
|
600
|
-
merger: (pages) => callbackRefs.merger(pages),
|
|
601
|
-
stateManager,
|
|
602
|
-
eventEmitter,
|
|
603
|
-
pluginExecutor,
|
|
604
|
-
instanceId,
|
|
605
|
-
fetchFn: async (opts, abortSignal) => {
|
|
606
|
-
const pathMethods = api(capturedCall.path);
|
|
607
|
-
const method = pathMethods[capturedCall.method];
|
|
608
|
-
const fetchOptions = {
|
|
609
|
-
...capturedCall.options,
|
|
610
|
-
...opts,
|
|
611
|
-
signal: abortSignal
|
|
612
|
-
};
|
|
613
|
-
return method(fetchOptions);
|
|
614
|
-
}
|
|
615
|
-
});
|
|
616
|
-
controller.setPluginOptions(pluginOpts);
|
|
617
|
-
currentSubscription = controller.subscribe(() => {
|
|
618
|
-
const state = controller.getState();
|
|
619
|
-
dataSignal.set(state.data);
|
|
620
|
-
pagesSignal.set(
|
|
621
|
-
state.pages
|
|
622
|
-
);
|
|
623
|
-
errorSignal.set(state.error);
|
|
624
|
-
canFetchNextSignal.set(state.canFetchNext);
|
|
625
|
-
canFetchPrevSignal.set(state.canFetchPrev);
|
|
626
|
-
});
|
|
627
|
-
currentController = controller;
|
|
628
|
-
currentQueryKey = queryKey;
|
|
629
|
-
currentResolvedTags = resolvedTags;
|
|
630
|
-
unsubInvalidate = eventEmitter.on(
|
|
631
|
-
"invalidate",
|
|
632
|
-
(invalidatedTags) => {
|
|
633
|
-
if (!getEnabled() || !currentController) return;
|
|
634
|
-
const hasMatch = invalidatedTags.some(
|
|
635
|
-
(tag) => currentResolvedTags.includes(tag)
|
|
636
|
-
);
|
|
637
|
-
if (hasMatch) {
|
|
638
|
-
loadingSignal.set(true);
|
|
639
|
-
currentController.trigger().finally(() => {
|
|
640
|
-
updateSignalsFromState();
|
|
641
|
-
loadingSignal.set(false);
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
);
|
|
646
|
-
unsubRefetchAll = eventEmitter.on("refetchAll", () => {
|
|
647
|
-
if (!getEnabled() || !currentController) return;
|
|
648
|
-
loadingSignal.set(true);
|
|
649
|
-
currentController.trigger().finally(() => {
|
|
650
|
-
updateSignalsFromState();
|
|
651
|
-
loadingSignal.set(false);
|
|
652
|
-
});
|
|
653
|
-
});
|
|
654
|
-
return controller;
|
|
655
|
-
};
|
|
656
|
-
const initialCapturedCall = captureSelector();
|
|
657
|
-
const initialRequestOptions = initialCapturedCall.options;
|
|
658
|
-
const initialPathSegments = initialCapturedCall.path.split("/").filter(Boolean);
|
|
659
|
-
const initialResolvedPath = resolvePath3(
|
|
660
|
-
initialPathSegments,
|
|
661
|
-
initialRequestOptions?.params
|
|
662
|
-
);
|
|
663
|
-
const initialResolvedTags = resolveTags3(
|
|
664
|
-
tags !== void 0 ? { tags } : void 0,
|
|
665
|
-
initialResolvedPath
|
|
666
|
-
);
|
|
667
|
-
const initialQueryKey = stateManager.createQueryKey({
|
|
668
|
-
path: initialCapturedCall.path,
|
|
669
|
-
method: initialCapturedCall.method,
|
|
670
|
-
options: initialCapturedCall.options
|
|
671
|
-
});
|
|
672
|
-
createController(initialCapturedCall, initialResolvedTags, initialQueryKey);
|
|
673
|
-
effect3(
|
|
674
|
-
() => {
|
|
675
|
-
const isEnabled = getEnabled();
|
|
676
|
-
const capturedCall = captureSelector();
|
|
677
|
-
const requestOptions = capturedCall.options;
|
|
678
|
-
const pathSegments = capturedCall.path.split("/").filter(Boolean);
|
|
679
|
-
const resolvedPath = resolvePath3(pathSegments, requestOptions?.params);
|
|
680
|
-
const resolvedTags = resolveTags3(
|
|
681
|
-
tags !== void 0 ? { tags } : void 0,
|
|
682
|
-
resolvedPath
|
|
683
|
-
);
|
|
684
|
-
const queryKey = stateManager.createQueryKey({
|
|
685
|
-
path: capturedCall.path,
|
|
686
|
-
method: capturedCall.method,
|
|
687
|
-
options: capturedCall.options
|
|
688
|
-
});
|
|
689
|
-
const queryKeyChanged = queryKey !== currentQueryKey;
|
|
690
|
-
if (!isEnabled) {
|
|
691
|
-
if (isMounted && currentController) {
|
|
692
|
-
currentController.unmount();
|
|
693
|
-
isMounted = false;
|
|
694
|
-
}
|
|
695
|
-
loadingSignal.set(false);
|
|
696
|
-
return;
|
|
697
|
-
}
|
|
698
|
-
if (queryKeyChanged) {
|
|
699
|
-
if (currentController && isMounted) {
|
|
700
|
-
prevContext = currentController.getContext();
|
|
701
|
-
currentController.unmount();
|
|
702
|
-
isMounted = false;
|
|
703
|
-
}
|
|
704
|
-
const controller = createController(
|
|
705
|
-
capturedCall,
|
|
706
|
-
resolvedTags,
|
|
707
|
-
queryKey
|
|
708
|
-
);
|
|
709
|
-
if (prevContext) {
|
|
710
|
-
controller.update(prevContext);
|
|
711
|
-
prevContext = null;
|
|
712
|
-
}
|
|
713
|
-
controller.mount();
|
|
714
|
-
isMounted = true;
|
|
715
|
-
untracked2(() => {
|
|
716
|
-
loadingSignal.set(true);
|
|
717
|
-
fetchingNextSignal.set(true);
|
|
718
|
-
errorSignal.set(void 0);
|
|
719
|
-
controller.trigger({ force: false }).finally(() => {
|
|
720
|
-
updateSignalsFromState();
|
|
721
|
-
loadingSignal.set(false);
|
|
722
|
-
fetchingNextSignal.set(false);
|
|
723
|
-
});
|
|
724
|
-
});
|
|
725
|
-
} else if (!isMounted && currentController) {
|
|
726
|
-
currentController.mount();
|
|
727
|
-
isMounted = true;
|
|
728
|
-
untracked2(() => {
|
|
729
|
-
triggerFetch();
|
|
730
|
-
});
|
|
731
|
-
}
|
|
732
|
-
},
|
|
733
|
-
{ allowSignalWrites: true }
|
|
734
|
-
);
|
|
735
|
-
destroyRef.onDestroy(() => {
|
|
736
|
-
if (unsubInvalidate) {
|
|
737
|
-
unsubInvalidate();
|
|
738
|
-
}
|
|
739
|
-
if (unsubRefetchAll) {
|
|
740
|
-
unsubRefetchAll();
|
|
741
|
-
}
|
|
742
|
-
if (currentSubscription) {
|
|
743
|
-
currentSubscription();
|
|
744
|
-
}
|
|
745
|
-
if (currentController && isMounted) {
|
|
746
|
-
currentController.unmount();
|
|
747
|
-
}
|
|
748
|
-
});
|
|
749
|
-
const fetchNext = async () => {
|
|
750
|
-
if (!currentController) return;
|
|
751
|
-
if (!isMounted) {
|
|
752
|
-
currentController.mount();
|
|
753
|
-
isMounted = true;
|
|
754
|
-
}
|
|
755
|
-
fetchingNextSignal.set(true);
|
|
756
|
-
try {
|
|
757
|
-
await currentController.fetchNext();
|
|
758
|
-
updateSignalsFromState();
|
|
759
|
-
} finally {
|
|
760
|
-
fetchingNextSignal.set(false);
|
|
761
|
-
}
|
|
762
|
-
};
|
|
763
|
-
const fetchPrev = async () => {
|
|
764
|
-
if (!currentController) return;
|
|
765
|
-
if (!isMounted) {
|
|
766
|
-
currentController.mount();
|
|
767
|
-
isMounted = true;
|
|
768
|
-
}
|
|
769
|
-
fetchingPrevSignal.set(true);
|
|
770
|
-
try {
|
|
771
|
-
await currentController.fetchPrev();
|
|
772
|
-
updateSignalsFromState();
|
|
773
|
-
} finally {
|
|
774
|
-
fetchingPrevSignal.set(false);
|
|
775
|
-
}
|
|
776
|
-
};
|
|
777
|
-
const trigger = async (options2) => {
|
|
778
|
-
if (!currentController) return;
|
|
779
|
-
if (!isMounted) {
|
|
780
|
-
currentController.mount();
|
|
781
|
-
isMounted = true;
|
|
782
|
-
}
|
|
783
|
-
loadingSignal.set(true);
|
|
784
|
-
try {
|
|
785
|
-
await currentController.trigger(options2);
|
|
786
|
-
updateSignalsFromState();
|
|
787
|
-
} finally {
|
|
788
|
-
loadingSignal.set(false);
|
|
789
|
-
}
|
|
790
|
-
};
|
|
791
|
-
const abort = () => {
|
|
792
|
-
currentController?.abort();
|
|
793
|
-
};
|
|
794
|
-
const fetchingSignal = computed(
|
|
795
|
-
() => fetchingNextSignal() || fetchingPrevSignal()
|
|
796
|
-
);
|
|
797
|
-
const result = {
|
|
798
|
-
data: dataSignal,
|
|
799
|
-
pages: pagesSignal,
|
|
800
|
-
error: errorSignal,
|
|
801
|
-
loading: loadingSignal,
|
|
802
|
-
fetching: fetchingSignal,
|
|
803
|
-
fetchingNext: fetchingNextSignal,
|
|
804
|
-
fetchingPrev: fetchingPrevSignal,
|
|
805
|
-
canFetchNext: canFetchNextSignal,
|
|
806
|
-
canFetchPrev: canFetchPrevSignal,
|
|
807
|
-
fetchNext,
|
|
808
|
-
fetchPrev,
|
|
809
|
-
trigger,
|
|
810
|
-
abort
|
|
811
|
-
};
|
|
812
|
-
return result;
|
|
813
|
-
};
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
// src/injectQueue/index.ts
|
|
817
|
-
import { signal as signal4, DestroyRef as DestroyRef4, inject as inject4 } from "@angular/core";
|
|
818
|
-
import {
|
|
819
|
-
createSelectorProxy as createSelectorProxy4,
|
|
820
|
-
createQueueController
|
|
821
|
-
} from "@spoosh/core";
|
|
822
|
-
function createInjectQueue(options) {
|
|
823
|
-
const { api, stateManager, pluginExecutor, eventEmitter } = options;
|
|
824
|
-
function injectQueue(queueFn, queueOptions) {
|
|
825
|
-
const destroyRef = inject4(DestroyRef4);
|
|
826
|
-
const captureSelector = () => {
|
|
827
|
-
const selectorResult = {
|
|
828
|
-
call: null,
|
|
829
|
-
selector: null
|
|
830
|
-
};
|
|
831
|
-
const selectorProxy = createSelectorProxy4(
|
|
832
|
-
(result) => {
|
|
833
|
-
selectorResult.call = result.call;
|
|
834
|
-
selectorResult.selector = result.selector;
|
|
835
|
-
}
|
|
836
|
-
);
|
|
837
|
-
queueFn(selectorProxy);
|
|
838
|
-
const captured = selectorResult.call ?? selectorResult.selector;
|
|
839
|
-
if (!captured) {
|
|
840
|
-
throw new Error(
|
|
841
|
-
'injectQueue requires selecting an HTTP method. Example: injectQueue((api) => api("uploads").POST())'
|
|
842
|
-
);
|
|
843
|
-
}
|
|
844
|
-
return captured;
|
|
845
|
-
};
|
|
846
|
-
const selectedEndpoint = captureSelector();
|
|
847
|
-
const { concurrency, ...hookOptions } = queueOptions ?? {};
|
|
848
|
-
const config = {
|
|
849
|
-
path: selectedEndpoint.path,
|
|
850
|
-
method: selectedEndpoint.method,
|
|
851
|
-
concurrency,
|
|
852
|
-
operationType: "queue",
|
|
853
|
-
hookOptions
|
|
854
|
-
};
|
|
855
|
-
const controller = createQueueController(config, {
|
|
856
|
-
api,
|
|
857
|
-
stateManager,
|
|
858
|
-
eventEmitter,
|
|
859
|
-
pluginExecutor
|
|
860
|
-
});
|
|
861
|
-
const tasksSignal = signal4(controller.getQueue());
|
|
862
|
-
const statsSignal = signal4(controller.getStats());
|
|
863
|
-
const unsubscribe = controller.subscribe(() => {
|
|
864
|
-
tasksSignal.set(controller.getQueue());
|
|
865
|
-
statsSignal.set(controller.getStats());
|
|
866
|
-
});
|
|
867
|
-
destroyRef.onDestroy(() => {
|
|
868
|
-
unsubscribe();
|
|
869
|
-
controller.clear();
|
|
870
|
-
});
|
|
871
|
-
const trigger = (input) => {
|
|
872
|
-
return controller.trigger(input ?? {});
|
|
873
|
-
};
|
|
874
|
-
return {
|
|
875
|
-
trigger,
|
|
876
|
-
tasks: tasksSignal.asReadonly(),
|
|
877
|
-
stats: statsSignal.asReadonly(),
|
|
878
|
-
abort: controller.abort,
|
|
879
|
-
retry: controller.retry,
|
|
880
|
-
remove: controller.remove,
|
|
881
|
-
removeSettled: controller.removeSettled,
|
|
882
|
-
clear: controller.clear,
|
|
883
|
-
setConcurrency: controller.setConcurrency
|
|
884
|
-
};
|
|
885
|
-
}
|
|
886
|
-
return injectQueue;
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
// src/create/index.ts
|
|
890
|
-
function create(instance) {
|
|
891
|
-
const { api, stateManager, eventEmitter, pluginExecutor } = instance;
|
|
892
|
-
const injectRead = createInjectRead({
|
|
893
|
-
api,
|
|
894
|
-
stateManager,
|
|
895
|
-
eventEmitter,
|
|
896
|
-
pluginExecutor
|
|
897
|
-
});
|
|
898
|
-
const injectWrite = createInjectWrite({
|
|
899
|
-
api,
|
|
900
|
-
stateManager,
|
|
901
|
-
eventEmitter,
|
|
902
|
-
pluginExecutor
|
|
903
|
-
});
|
|
904
|
-
const injectPages = createInjectPages({
|
|
905
|
-
api,
|
|
906
|
-
stateManager,
|
|
907
|
-
eventEmitter,
|
|
908
|
-
pluginExecutor
|
|
909
|
-
});
|
|
910
|
-
const injectQueue = createInjectQueue({
|
|
911
|
-
api,
|
|
912
|
-
stateManager,
|
|
913
|
-
eventEmitter,
|
|
914
|
-
pluginExecutor
|
|
915
|
-
});
|
|
916
|
-
const plugins = pluginExecutor.getPlugins();
|
|
917
|
-
const setupContext = {
|
|
918
|
-
stateManager,
|
|
919
|
-
eventEmitter,
|
|
920
|
-
pluginExecutor
|
|
921
|
-
};
|
|
922
|
-
for (const plugin of plugins) {
|
|
923
|
-
plugin.setup?.(setupContext);
|
|
924
|
-
}
|
|
925
|
-
const instanceApiContext = {
|
|
926
|
-
api,
|
|
927
|
-
stateManager,
|
|
928
|
-
eventEmitter,
|
|
929
|
-
pluginExecutor
|
|
930
|
-
};
|
|
931
|
-
const instanceApis = plugins.reduce(
|
|
932
|
-
(acc, plugin) => {
|
|
933
|
-
if (plugin.instanceApi) {
|
|
934
|
-
return { ...acc, ...plugin.instanceApi(instanceApiContext) };
|
|
935
|
-
}
|
|
936
|
-
return acc;
|
|
937
|
-
},
|
|
938
|
-
{}
|
|
939
|
-
);
|
|
940
|
-
return {
|
|
941
|
-
injectRead,
|
|
942
|
-
injectWrite,
|
|
943
|
-
injectPages,
|
|
944
|
-
injectQueue,
|
|
945
|
-
...instanceApis
|
|
946
|
-
};
|
|
947
|
-
}
|
|
948
|
-
export {
|
|
949
|
-
create
|
|
950
|
-
};
|