@spoosh/angular 0.1.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/LICENSE +21 -0
- package/README.md +229 -0
- package/dist/index.d.mts +358 -0
- package/dist/index.d.ts +358 -0
- package/dist/index.js +692 -0
- package/dist/index.mjs +697 -0
- package/package.json +50 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,692 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
createAngularSpoosh: () => createAngularSpoosh
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(src_exports);
|
|
26
|
+
|
|
27
|
+
// src/injectRead/index.ts
|
|
28
|
+
var import_core = require("@angular/core");
|
|
29
|
+
var import_core2 = require("@spoosh/core");
|
|
30
|
+
function createInjectRead(options) {
|
|
31
|
+
const { api, stateManager, eventEmitter, pluginExecutor } = options;
|
|
32
|
+
return function injectRead(readFn, readOptions) {
|
|
33
|
+
const destroyRef = (0, import_core.inject)(import_core.DestroyRef);
|
|
34
|
+
const {
|
|
35
|
+
enabled: enabledOption = true,
|
|
36
|
+
tags = void 0,
|
|
37
|
+
additionalTags = void 0,
|
|
38
|
+
...pluginOpts
|
|
39
|
+
} = readOptions ?? {};
|
|
40
|
+
const getEnabled = () => typeof enabledOption === "function" ? enabledOption() : enabledOption;
|
|
41
|
+
const dataSignal = (0, import_core.signal)(void 0);
|
|
42
|
+
const errorSignal = (0, import_core.signal)(void 0);
|
|
43
|
+
const loadingSignal = (0, import_core.signal)(true);
|
|
44
|
+
const fetchingSignal = (0, import_core.signal)(false);
|
|
45
|
+
const inputSignal = (0, import_core.signal)({});
|
|
46
|
+
const metaSignal = (0, import_core.signal)({});
|
|
47
|
+
let currentController = null;
|
|
48
|
+
let currentQueryKey = null;
|
|
49
|
+
let currentSubscription = null;
|
|
50
|
+
let prevContext = null;
|
|
51
|
+
let initialized = false;
|
|
52
|
+
let isMounted = false;
|
|
53
|
+
const captureSelector = () => {
|
|
54
|
+
const selectorResult = {
|
|
55
|
+
call: null,
|
|
56
|
+
selector: null
|
|
57
|
+
};
|
|
58
|
+
const selectorProxy = (0, import_core2.createSelectorProxy)(
|
|
59
|
+
(result2) => {
|
|
60
|
+
selectorResult.call = result2.call;
|
|
61
|
+
selectorResult.selector = result2.selector;
|
|
62
|
+
}
|
|
63
|
+
);
|
|
64
|
+
readFn(selectorProxy);
|
|
65
|
+
return selectorResult;
|
|
66
|
+
};
|
|
67
|
+
const executeWithTracking = async (controller, force = false) => {
|
|
68
|
+
const hasData = dataSignal() !== void 0;
|
|
69
|
+
loadingSignal.set(!hasData);
|
|
70
|
+
fetchingSignal.set(true);
|
|
71
|
+
try {
|
|
72
|
+
const response = await controller.execute(void 0, { force });
|
|
73
|
+
if (response.error) {
|
|
74
|
+
errorSignal.set(response.error);
|
|
75
|
+
} else {
|
|
76
|
+
errorSignal.set(void 0);
|
|
77
|
+
}
|
|
78
|
+
if (response.data !== void 0) {
|
|
79
|
+
dataSignal.set(response.data);
|
|
80
|
+
}
|
|
81
|
+
return response;
|
|
82
|
+
} catch (err) {
|
|
83
|
+
errorSignal.set(err);
|
|
84
|
+
throw err;
|
|
85
|
+
} finally {
|
|
86
|
+
loadingSignal.set(false);
|
|
87
|
+
fetchingSignal.set(false);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
let wasEnabled = false;
|
|
91
|
+
(0, import_core.effect)(
|
|
92
|
+
() => {
|
|
93
|
+
const isEnabled = getEnabled();
|
|
94
|
+
const selectorResult = captureSelector();
|
|
95
|
+
const capturedCall = selectorResult.call;
|
|
96
|
+
if (!capturedCall) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
"injectRead requires calling an HTTP method ($get). Example: injectRead((api) => api.posts.$get())"
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
const requestOptions = capturedCall.options;
|
|
102
|
+
const resolvedPath = (0, import_core2.resolvePath)(
|
|
103
|
+
capturedCall.path,
|
|
104
|
+
requestOptions?.params
|
|
105
|
+
);
|
|
106
|
+
const resolvedTags = (0, import_core2.resolveTags)(
|
|
107
|
+
{ tags, additionalTags },
|
|
108
|
+
resolvedPath
|
|
109
|
+
);
|
|
110
|
+
const queryKey = stateManager.createQueryKey({
|
|
111
|
+
path: capturedCall.path,
|
|
112
|
+
method: capturedCall.method,
|
|
113
|
+
options: capturedCall.options
|
|
114
|
+
});
|
|
115
|
+
const opts = capturedCall.options;
|
|
116
|
+
const inputInner = {};
|
|
117
|
+
if (opts?.query !== void 0) {
|
|
118
|
+
inputInner.query = opts.query;
|
|
119
|
+
}
|
|
120
|
+
if (opts?.body !== void 0) {
|
|
121
|
+
inputInner.body = opts.body;
|
|
122
|
+
}
|
|
123
|
+
if (opts?.formData !== void 0) {
|
|
124
|
+
inputInner.formData = opts.formData;
|
|
125
|
+
}
|
|
126
|
+
if (opts?.params !== void 0) {
|
|
127
|
+
inputInner.params = opts.params;
|
|
128
|
+
}
|
|
129
|
+
inputSignal.set(inputInner);
|
|
130
|
+
const queryKeyChanged = queryKey !== currentQueryKey;
|
|
131
|
+
const enabledChanged = isEnabled !== wasEnabled;
|
|
132
|
+
wasEnabled = isEnabled;
|
|
133
|
+
if (queryKeyChanged) {
|
|
134
|
+
if (currentController && initialized) {
|
|
135
|
+
prevContext = currentController.getContext();
|
|
136
|
+
}
|
|
137
|
+
if (currentSubscription) {
|
|
138
|
+
currentSubscription();
|
|
139
|
+
}
|
|
140
|
+
const controller = (0, import_core2.createOperationController)({
|
|
141
|
+
operationType: "read",
|
|
142
|
+
path: capturedCall.path,
|
|
143
|
+
method: capturedCall.method,
|
|
144
|
+
tags: resolvedTags,
|
|
145
|
+
requestOptions: capturedCall.options,
|
|
146
|
+
stateManager,
|
|
147
|
+
eventEmitter,
|
|
148
|
+
pluginExecutor,
|
|
149
|
+
hookId: `angular-${Math.random().toString(36).slice(2)}`,
|
|
150
|
+
fetchFn: async (fetchOpts) => {
|
|
151
|
+
let current = api;
|
|
152
|
+
for (const segment of resolvedPath) {
|
|
153
|
+
current = current[segment];
|
|
154
|
+
}
|
|
155
|
+
const method = current[capturedCall.method];
|
|
156
|
+
return method(fetchOpts);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
controller.setPluginOptions(pluginOpts);
|
|
160
|
+
currentSubscription = controller.subscribe(() => {
|
|
161
|
+
const state = controller.getState();
|
|
162
|
+
dataSignal.set(state.data);
|
|
163
|
+
errorSignal.set(state.error);
|
|
164
|
+
const entry = stateManager.getCache(queryKey);
|
|
165
|
+
const newMeta = entry?.pluginResult ? Object.fromEntries(entry.pluginResult) : {};
|
|
166
|
+
metaSignal.set(newMeta);
|
|
167
|
+
});
|
|
168
|
+
currentController = controller;
|
|
169
|
+
currentQueryKey = queryKey;
|
|
170
|
+
if (!initialized) {
|
|
171
|
+
initialized = true;
|
|
172
|
+
} else if (prevContext) {
|
|
173
|
+
controller.update(prevContext);
|
|
174
|
+
prevContext = null;
|
|
175
|
+
}
|
|
176
|
+
if (isEnabled) {
|
|
177
|
+
controller.mount();
|
|
178
|
+
isMounted = true;
|
|
179
|
+
(0, import_core.untracked)(() => {
|
|
180
|
+
executeWithTracking(controller, false);
|
|
181
|
+
});
|
|
182
|
+
} else {
|
|
183
|
+
loadingSignal.set(false);
|
|
184
|
+
}
|
|
185
|
+
} else if (enabledChanged && currentController) {
|
|
186
|
+
if (isEnabled && !isMounted) {
|
|
187
|
+
currentController.mount();
|
|
188
|
+
isMounted = true;
|
|
189
|
+
(0, import_core.untracked)(() => {
|
|
190
|
+
executeWithTracking(currentController, false);
|
|
191
|
+
});
|
|
192
|
+
} else if (!isEnabled && isMounted) {
|
|
193
|
+
currentController.unmount();
|
|
194
|
+
isMounted = false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (!isEnabled) {
|
|
198
|
+
loadingSignal.set(false);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const unsubRefetch = eventEmitter.on(
|
|
202
|
+
"refetch",
|
|
203
|
+
(event) => {
|
|
204
|
+
if (event.queryKey === currentQueryKey && currentController) {
|
|
205
|
+
(0, import_core.untracked)(() => {
|
|
206
|
+
executeWithTracking(currentController, true);
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
const unsubInvalidate = eventEmitter.on(
|
|
212
|
+
"invalidate",
|
|
213
|
+
(invalidatedTags) => {
|
|
214
|
+
const hasMatch = invalidatedTags.some(
|
|
215
|
+
(tag) => resolvedTags.includes(tag)
|
|
216
|
+
);
|
|
217
|
+
if (hasMatch && currentController) {
|
|
218
|
+
(0, import_core.untracked)(() => {
|
|
219
|
+
executeWithTracking(currentController, true);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
);
|
|
224
|
+
return () => {
|
|
225
|
+
unsubRefetch();
|
|
226
|
+
unsubInvalidate();
|
|
227
|
+
};
|
|
228
|
+
},
|
|
229
|
+
{ allowSignalWrites: true }
|
|
230
|
+
);
|
|
231
|
+
destroyRef.onDestroy(() => {
|
|
232
|
+
if (currentSubscription) {
|
|
233
|
+
currentSubscription();
|
|
234
|
+
}
|
|
235
|
+
if (currentController && isMounted) {
|
|
236
|
+
currentController.unmount();
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
const abort = () => {
|
|
240
|
+
currentController?.abort();
|
|
241
|
+
};
|
|
242
|
+
const refetch = () => {
|
|
243
|
+
if (currentController) {
|
|
244
|
+
return executeWithTracking(currentController, true);
|
|
245
|
+
}
|
|
246
|
+
return Promise.resolve({ data: void 0, error: void 0 });
|
|
247
|
+
};
|
|
248
|
+
const result = {
|
|
249
|
+
meta: metaSignal,
|
|
250
|
+
get input() {
|
|
251
|
+
return inputSignal();
|
|
252
|
+
},
|
|
253
|
+
data: dataSignal,
|
|
254
|
+
error: errorSignal,
|
|
255
|
+
loading: loadingSignal,
|
|
256
|
+
fetching: fetchingSignal,
|
|
257
|
+
abort,
|
|
258
|
+
refetch
|
|
259
|
+
};
|
|
260
|
+
return result;
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/injectWrite/index.ts
|
|
265
|
+
var import_core3 = require("@angular/core");
|
|
266
|
+
var import_core4 = require("@spoosh/core");
|
|
267
|
+
function createInjectWrite(options) {
|
|
268
|
+
const { api, stateManager, pluginExecutor, eventEmitter } = options;
|
|
269
|
+
return function injectWrite(writeFn) {
|
|
270
|
+
const destroyRef = (0, import_core3.inject)(import_core3.DestroyRef);
|
|
271
|
+
const selectorResult = {
|
|
272
|
+
call: null,
|
|
273
|
+
selector: null
|
|
274
|
+
};
|
|
275
|
+
const selectorProxy = (0, import_core4.createSelectorProxy)(
|
|
276
|
+
(result2) => {
|
|
277
|
+
selectorResult.call = result2.call;
|
|
278
|
+
selectorResult.selector = result2.selector;
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
writeFn(selectorProxy);
|
|
282
|
+
const selectedEndpoint = selectorResult.selector;
|
|
283
|
+
if (!selectedEndpoint) {
|
|
284
|
+
throw new Error(
|
|
285
|
+
"injectWrite requires selecting an HTTP method ($post, $put, $patch, $delete). Example: injectWrite((api) => api.posts.$post)"
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
const queryKey = stateManager.createQueryKey({
|
|
289
|
+
path: selectedEndpoint.path,
|
|
290
|
+
method: selectedEndpoint.method,
|
|
291
|
+
options: void 0
|
|
292
|
+
});
|
|
293
|
+
const controller = (0, import_core4.createOperationController)({
|
|
294
|
+
operationType: "write",
|
|
295
|
+
path: selectedEndpoint.path,
|
|
296
|
+
method: selectedEndpoint.method,
|
|
297
|
+
tags: [],
|
|
298
|
+
stateManager,
|
|
299
|
+
eventEmitter,
|
|
300
|
+
pluginExecutor,
|
|
301
|
+
hookId: `angular-${Math.random().toString(36).slice(2)}`,
|
|
302
|
+
fetchFn: async (fetchOpts) => {
|
|
303
|
+
const params = fetchOpts?.params;
|
|
304
|
+
const resolvedPath = (0, import_core4.resolvePath)(selectedEndpoint.path, params);
|
|
305
|
+
let current = api;
|
|
306
|
+
for (const segment of resolvedPath) {
|
|
307
|
+
current = current[segment];
|
|
308
|
+
}
|
|
309
|
+
const method = current[selectedEndpoint.method];
|
|
310
|
+
return method(fetchOpts);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
const dataSignal = (0, import_core3.signal)(void 0);
|
|
314
|
+
const errorSignal = (0, import_core3.signal)(void 0);
|
|
315
|
+
const loadingSignal = (0, import_core3.signal)(false);
|
|
316
|
+
const lastTriggerOptionsSignal = (0, import_core3.signal)(void 0);
|
|
317
|
+
const metaSignal = (0, import_core3.signal)({});
|
|
318
|
+
const subscription = controller.subscribe(() => {
|
|
319
|
+
const state = controller.getState();
|
|
320
|
+
dataSignal.set(state.data);
|
|
321
|
+
errorSignal.set(state.error);
|
|
322
|
+
const entry = stateManager.getCache(queryKey);
|
|
323
|
+
const newMeta = entry?.pluginResult ? Object.fromEntries(entry.pluginResult) : {};
|
|
324
|
+
metaSignal.set(newMeta);
|
|
325
|
+
});
|
|
326
|
+
destroyRef.onDestroy(() => {
|
|
327
|
+
subscription();
|
|
328
|
+
});
|
|
329
|
+
const reset = () => {
|
|
330
|
+
stateManager.deleteCache(queryKey);
|
|
331
|
+
errorSignal.set(void 0);
|
|
332
|
+
loadingSignal.set(false);
|
|
333
|
+
};
|
|
334
|
+
const abort = () => {
|
|
335
|
+
controller.abort();
|
|
336
|
+
};
|
|
337
|
+
const trigger = async (triggerOptions) => {
|
|
338
|
+
lastTriggerOptionsSignal.set(triggerOptions);
|
|
339
|
+
loadingSignal.set(true);
|
|
340
|
+
const params = triggerOptions?.params;
|
|
341
|
+
const resolvedPath = (0, import_core4.resolvePath)(selectedEndpoint.path, params);
|
|
342
|
+
const tags = (0, import_core4.resolveTags)(triggerOptions, resolvedPath);
|
|
343
|
+
controller.setPluginOptions({ ...triggerOptions, tags });
|
|
344
|
+
try {
|
|
345
|
+
const response = await controller.execute(triggerOptions, {
|
|
346
|
+
force: true
|
|
347
|
+
});
|
|
348
|
+
if (response.error) {
|
|
349
|
+
errorSignal.set(response.error);
|
|
350
|
+
} else {
|
|
351
|
+
errorSignal.set(void 0);
|
|
352
|
+
}
|
|
353
|
+
return response;
|
|
354
|
+
} catch (err) {
|
|
355
|
+
errorSignal.set(err);
|
|
356
|
+
throw err;
|
|
357
|
+
} finally {
|
|
358
|
+
loadingSignal.set(false);
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
const inputSignal = (0, import_core3.signal)({});
|
|
362
|
+
(0, import_core3.effect)(
|
|
363
|
+
() => {
|
|
364
|
+
const opts = lastTriggerOptionsSignal();
|
|
365
|
+
const inputInner = {};
|
|
366
|
+
if (opts?.query !== void 0) {
|
|
367
|
+
inputInner.query = opts.query;
|
|
368
|
+
}
|
|
369
|
+
if (opts?.body !== void 0) {
|
|
370
|
+
inputInner.body = opts.body;
|
|
371
|
+
}
|
|
372
|
+
if (opts?.formData !== void 0) {
|
|
373
|
+
inputInner.formData = opts.formData;
|
|
374
|
+
}
|
|
375
|
+
if (opts?.params !== void 0) {
|
|
376
|
+
inputInner.params = opts.params;
|
|
377
|
+
}
|
|
378
|
+
inputSignal.set(inputInner);
|
|
379
|
+
},
|
|
380
|
+
{ allowSignalWrites: true }
|
|
381
|
+
);
|
|
382
|
+
const result = {
|
|
383
|
+
trigger,
|
|
384
|
+
meta: metaSignal,
|
|
385
|
+
input: inputSignal,
|
|
386
|
+
data: dataSignal,
|
|
387
|
+
error: errorSignal,
|
|
388
|
+
loading: loadingSignal,
|
|
389
|
+
reset,
|
|
390
|
+
abort
|
|
391
|
+
};
|
|
392
|
+
return result;
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/injectInfiniteRead/index.ts
|
|
397
|
+
var import_core5 = require("@angular/core");
|
|
398
|
+
var import_core6 = require("@spoosh/core");
|
|
399
|
+
function createInjectInfiniteRead(options) {
|
|
400
|
+
const { api, stateManager, eventEmitter, pluginExecutor } = options;
|
|
401
|
+
return function injectInfiniteRead(readFn, readOptions) {
|
|
402
|
+
const destroyRef = (0, import_core5.inject)(import_core5.DestroyRef);
|
|
403
|
+
const {
|
|
404
|
+
enabled: enabledOption = true,
|
|
405
|
+
tags,
|
|
406
|
+
additionalTags,
|
|
407
|
+
canFetchNext,
|
|
408
|
+
nextPageRequest,
|
|
409
|
+
merger,
|
|
410
|
+
canFetchPrev,
|
|
411
|
+
prevPageRequest,
|
|
412
|
+
...pluginOpts
|
|
413
|
+
} = readOptions;
|
|
414
|
+
const getEnabled = () => typeof enabledOption === "function" ? enabledOption() : enabledOption;
|
|
415
|
+
const callbackRefs = {
|
|
416
|
+
canFetchNext,
|
|
417
|
+
canFetchPrev,
|
|
418
|
+
nextPageRequest,
|
|
419
|
+
prevPageRequest,
|
|
420
|
+
merger
|
|
421
|
+
};
|
|
422
|
+
const selectorResult = {
|
|
423
|
+
call: null,
|
|
424
|
+
selector: null
|
|
425
|
+
};
|
|
426
|
+
const selectorProxy = (0, import_core6.createSelectorProxy)(
|
|
427
|
+
(result2) => {
|
|
428
|
+
selectorResult.call = result2.call;
|
|
429
|
+
selectorResult.selector = result2.selector;
|
|
430
|
+
}
|
|
431
|
+
);
|
|
432
|
+
readFn(selectorProxy);
|
|
433
|
+
const capturedCall = selectorResult.call;
|
|
434
|
+
if (!capturedCall) {
|
|
435
|
+
throw new Error(
|
|
436
|
+
"injectInfiniteRead requires calling an HTTP method ($get). Example: injectInfiniteRead((api) => api.posts.$get())"
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
const requestOptions = capturedCall.options;
|
|
440
|
+
const initialRequest = {
|
|
441
|
+
query: requestOptions?.query,
|
|
442
|
+
params: requestOptions?.params,
|
|
443
|
+
body: requestOptions?.body
|
|
444
|
+
};
|
|
445
|
+
const baseOptionsForKey = {
|
|
446
|
+
...capturedCall.options,
|
|
447
|
+
query: void 0,
|
|
448
|
+
params: void 0,
|
|
449
|
+
body: void 0
|
|
450
|
+
};
|
|
451
|
+
const resolvedPath = (0, import_core6.resolvePath)(capturedCall.path, requestOptions?.params);
|
|
452
|
+
const resolvedTags = (0, import_core6.resolveTags)({ tags, additionalTags }, resolvedPath);
|
|
453
|
+
const controller = (0, import_core6.createInfiniteReadController)({
|
|
454
|
+
path: capturedCall.path,
|
|
455
|
+
method: capturedCall.method,
|
|
456
|
+
tags: resolvedTags,
|
|
457
|
+
initialRequest,
|
|
458
|
+
baseOptionsForKey,
|
|
459
|
+
canFetchNext: (ctx) => callbackRefs.canFetchNext(ctx),
|
|
460
|
+
canFetchPrev: canFetchPrev ? (ctx) => callbackRefs.canFetchPrev?.(ctx) ?? false : void 0,
|
|
461
|
+
nextPageRequest: (ctx) => callbackRefs.nextPageRequest(ctx),
|
|
462
|
+
prevPageRequest: prevPageRequest ? (ctx) => callbackRefs.prevPageRequest?.(ctx) ?? {} : void 0,
|
|
463
|
+
merger: (responses) => callbackRefs.merger(responses),
|
|
464
|
+
stateManager,
|
|
465
|
+
eventEmitter,
|
|
466
|
+
pluginExecutor,
|
|
467
|
+
hookId: `angular-${Math.random().toString(36).slice(2)}`,
|
|
468
|
+
fetchFn: async (opts, abortSignal) => {
|
|
469
|
+
const fetchPath = (0, import_core6.resolvePath)(capturedCall.path, opts.params);
|
|
470
|
+
let current = api;
|
|
471
|
+
for (const segment of fetchPath) {
|
|
472
|
+
current = current[segment];
|
|
473
|
+
}
|
|
474
|
+
const method = current[capturedCall.method];
|
|
475
|
+
const fetchOptions = {
|
|
476
|
+
...capturedCall.options,
|
|
477
|
+
query: opts.query,
|
|
478
|
+
params: opts.params,
|
|
479
|
+
body: opts.body,
|
|
480
|
+
signal: abortSignal
|
|
481
|
+
};
|
|
482
|
+
return method(fetchOptions);
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
controller.setPluginOptions(pluginOpts);
|
|
486
|
+
const dataSignal = (0, import_core5.signal)(void 0);
|
|
487
|
+
const allResponsesSignal = (0, import_core5.signal)(void 0);
|
|
488
|
+
const errorSignal = (0, import_core5.signal)(void 0);
|
|
489
|
+
const loadingSignal = (0, import_core5.signal)(false);
|
|
490
|
+
const canFetchNextSignal = (0, import_core5.signal)(false);
|
|
491
|
+
const canFetchPrevSignal = (0, import_core5.signal)(false);
|
|
492
|
+
const metaSignal = (0, import_core5.signal)({});
|
|
493
|
+
const queryKey = stateManager.createQueryKey({
|
|
494
|
+
path: capturedCall.path,
|
|
495
|
+
method: capturedCall.method,
|
|
496
|
+
options: baseOptionsForKey
|
|
497
|
+
});
|
|
498
|
+
const subscription = controller.subscribe(() => {
|
|
499
|
+
const state = controller.getState();
|
|
500
|
+
dataSignal.set(state.data);
|
|
501
|
+
allResponsesSignal.set(state.allResponses);
|
|
502
|
+
errorSignal.set(state.error);
|
|
503
|
+
canFetchNextSignal.set(state.canFetchNext);
|
|
504
|
+
canFetchPrevSignal.set(state.canFetchPrev);
|
|
505
|
+
const entry = stateManager.getCache(queryKey);
|
|
506
|
+
const newMeta = entry?.pluginResult ? Object.fromEntries(entry.pluginResult) : {};
|
|
507
|
+
metaSignal.set(newMeta);
|
|
508
|
+
});
|
|
509
|
+
const fetchingNextSignal = (0, import_core5.signal)(false);
|
|
510
|
+
const fetchingPrevSignal = (0, import_core5.signal)(false);
|
|
511
|
+
let prevContext = null;
|
|
512
|
+
let hasDoneInitialFetch = false;
|
|
513
|
+
let isMounted = false;
|
|
514
|
+
const updateSignalsFromState = () => {
|
|
515
|
+
const state = controller.getState();
|
|
516
|
+
dataSignal.set(state.data);
|
|
517
|
+
allResponsesSignal.set(state.allResponses);
|
|
518
|
+
errorSignal.set(state.error);
|
|
519
|
+
canFetchNextSignal.set(state.canFetchNext);
|
|
520
|
+
canFetchPrevSignal.set(state.canFetchPrev);
|
|
521
|
+
};
|
|
522
|
+
const triggerFetch = () => {
|
|
523
|
+
const currentState = controller.getState();
|
|
524
|
+
const isFetching = (0, import_core5.untracked)(
|
|
525
|
+
() => fetchingNextSignal() || fetchingPrevSignal()
|
|
526
|
+
);
|
|
527
|
+
if (currentState.data === void 0 && !isFetching) {
|
|
528
|
+
loadingSignal.set(true);
|
|
529
|
+
fetchingNextSignal.set(true);
|
|
530
|
+
controller.fetchNext().finally(() => {
|
|
531
|
+
updateSignalsFromState();
|
|
532
|
+
loadingSignal.set(false);
|
|
533
|
+
fetchingNextSignal.set(false);
|
|
534
|
+
});
|
|
535
|
+
} else if (currentState.data !== void 0) {
|
|
536
|
+
updateSignalsFromState();
|
|
537
|
+
}
|
|
538
|
+
};
|
|
539
|
+
const unsubInvalidate = eventEmitter.on(
|
|
540
|
+
"invalidate",
|
|
541
|
+
(invalidatedTags) => {
|
|
542
|
+
if (!getEnabled()) return;
|
|
543
|
+
const hasMatch = invalidatedTags.some(
|
|
544
|
+
(tag) => resolvedTags.includes(tag)
|
|
545
|
+
);
|
|
546
|
+
if (hasMatch) {
|
|
547
|
+
loadingSignal.set(true);
|
|
548
|
+
controller.refetch().finally(() => {
|
|
549
|
+
updateSignalsFromState();
|
|
550
|
+
loadingSignal.set(false);
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
);
|
|
555
|
+
(0, import_core5.effect)(
|
|
556
|
+
() => {
|
|
557
|
+
const isEnabled = getEnabled();
|
|
558
|
+
if (!isEnabled) {
|
|
559
|
+
if (isMounted) {
|
|
560
|
+
controller.unmount();
|
|
561
|
+
isMounted = false;
|
|
562
|
+
}
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
if (!isMounted) {
|
|
566
|
+
controller.mount();
|
|
567
|
+
isMounted = true;
|
|
568
|
+
}
|
|
569
|
+
if (!hasDoneInitialFetch) {
|
|
570
|
+
hasDoneInitialFetch = true;
|
|
571
|
+
(0, import_core5.untracked)(() => {
|
|
572
|
+
triggerFetch();
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
if (prevContext) {
|
|
576
|
+
(0, import_core5.untracked)(() => {
|
|
577
|
+
controller.update(prevContext);
|
|
578
|
+
prevContext = null;
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
},
|
|
582
|
+
{ allowSignalWrites: true }
|
|
583
|
+
);
|
|
584
|
+
destroyRef.onDestroy(() => {
|
|
585
|
+
unsubInvalidate();
|
|
586
|
+
});
|
|
587
|
+
destroyRef.onDestroy(() => {
|
|
588
|
+
subscription();
|
|
589
|
+
if (isMounted) {
|
|
590
|
+
controller.unmount();
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
const fetchNext = async () => {
|
|
594
|
+
if (!getEnabled()) return;
|
|
595
|
+
fetchingNextSignal.set(true);
|
|
596
|
+
try {
|
|
597
|
+
await controller.fetchNext();
|
|
598
|
+
updateSignalsFromState();
|
|
599
|
+
} finally {
|
|
600
|
+
fetchingNextSignal.set(false);
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
const fetchPrev = async () => {
|
|
604
|
+
if (!getEnabled()) return;
|
|
605
|
+
fetchingPrevSignal.set(true);
|
|
606
|
+
try {
|
|
607
|
+
await controller.fetchPrev();
|
|
608
|
+
updateSignalsFromState();
|
|
609
|
+
} finally {
|
|
610
|
+
fetchingPrevSignal.set(false);
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
const refetch = async () => {
|
|
614
|
+
if (!getEnabled()) return;
|
|
615
|
+
loadingSignal.set(true);
|
|
616
|
+
try {
|
|
617
|
+
await controller.refetch();
|
|
618
|
+
updateSignalsFromState();
|
|
619
|
+
} finally {
|
|
620
|
+
loadingSignal.set(false);
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
const abort = () => {
|
|
624
|
+
controller.abort();
|
|
625
|
+
};
|
|
626
|
+
const fetchingSignal = (0, import_core5.computed)(
|
|
627
|
+
() => fetchingNextSignal() || fetchingPrevSignal()
|
|
628
|
+
);
|
|
629
|
+
const result = {
|
|
630
|
+
meta: metaSignal,
|
|
631
|
+
data: dataSignal,
|
|
632
|
+
allResponses: allResponsesSignal,
|
|
633
|
+
error: errorSignal,
|
|
634
|
+
loading: loadingSignal,
|
|
635
|
+
fetching: fetchingSignal,
|
|
636
|
+
fetchingNext: fetchingNextSignal,
|
|
637
|
+
fetchingPrev: fetchingPrevSignal,
|
|
638
|
+
canFetchNext: canFetchNextSignal,
|
|
639
|
+
canFetchPrev: canFetchPrevSignal,
|
|
640
|
+
fetchNext,
|
|
641
|
+
fetchPrev,
|
|
642
|
+
refetch,
|
|
643
|
+
abort
|
|
644
|
+
};
|
|
645
|
+
return result;
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// src/createAngularSpoosh/index.ts
|
|
650
|
+
function createAngularSpoosh(instance) {
|
|
651
|
+
const { api, stateManager, eventEmitter, pluginExecutor } = instance;
|
|
652
|
+
const injectRead = createInjectRead({
|
|
653
|
+
api,
|
|
654
|
+
stateManager,
|
|
655
|
+
eventEmitter,
|
|
656
|
+
pluginExecutor
|
|
657
|
+
});
|
|
658
|
+
const injectWrite = createInjectWrite({
|
|
659
|
+
api,
|
|
660
|
+
stateManager,
|
|
661
|
+
eventEmitter,
|
|
662
|
+
pluginExecutor
|
|
663
|
+
});
|
|
664
|
+
const injectInfiniteRead = createInjectInfiniteRead({
|
|
665
|
+
api,
|
|
666
|
+
stateManager,
|
|
667
|
+
eventEmitter,
|
|
668
|
+
pluginExecutor
|
|
669
|
+
});
|
|
670
|
+
const instanceApiContext = {
|
|
671
|
+
api,
|
|
672
|
+
stateManager,
|
|
673
|
+
eventEmitter,
|
|
674
|
+
pluginExecutor
|
|
675
|
+
};
|
|
676
|
+
const plugins = pluginExecutor.getPlugins();
|
|
677
|
+
const instanceApis = plugins.reduce(
|
|
678
|
+
(acc, plugin) => {
|
|
679
|
+
if (plugin.instanceApi) {
|
|
680
|
+
return { ...acc, ...plugin.instanceApi(instanceApiContext) };
|
|
681
|
+
}
|
|
682
|
+
return acc;
|
|
683
|
+
},
|
|
684
|
+
{}
|
|
685
|
+
);
|
|
686
|
+
return {
|
|
687
|
+
injectRead,
|
|
688
|
+
injectWrite,
|
|
689
|
+
injectInfiniteRead,
|
|
690
|
+
...instanceApis
|
|
691
|
+
};
|
|
692
|
+
}
|