@arrai-innovations/reactive-helpers 21.0.1 → 21.1.2
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/README.md +11 -0
- package/config/commonCrud.js +12 -0
- package/config/listCrud.js +21 -6
- package/config/objectCrud.js +53 -11
- package/index.js +3 -0
- package/package.json +7 -5
- package/types/config/commonCrud.d.ts +8 -0
- package/types/config/listCrud.d.ts +14 -6
- package/types/config/objectCrud.d.ts +48 -10
- package/types/index.d.ts +3 -0
- package/types/tests/unit/utils/isReactiveTyped.spec.d.ts +1 -0
- package/types/tests/unit/utils/refIfReactive.spec.d.ts +1 -0
- package/types/tests/unit/utils/toRefsIfReactive.spec.d.ts +1 -0
- package/types/use/cancellableIntent.d.ts +16 -12
- package/types/use/error.d.ts +35 -7
- package/types/use/listCalculated.d.ts +2 -2
- package/types/use/listInstance.d.ts +22 -19
- package/types/use/listRelated.d.ts +6 -6
- package/types/use/listSubscription.d.ts +12 -3
- package/types/use/loading.d.ts +16 -7
- package/types/use/loadingError.d.ts +6 -2
- package/types/use/objectInstance.d.ts +38 -20
- package/types/use/objectSubscription.d.ts +22 -5
- package/types/use/proxyError.d.ts +21 -18
- package/types/use/proxyLoading.d.ts +18 -8
- package/types/use/proxyLoadingError.d.ts +20 -5
- package/types/utils/isReactiveTyped.d.ts +6 -0
- package/types/utils/refIfReactive.d.ts +2 -1
- package/types/utils/toRefsIfReactive.d.ts +14 -0
- package/use/cancellableIntent.js +8 -7
- package/use/error.js +22 -3
- package/use/listCalculated.js +1 -1
- package/use/listInstance.js +28 -26
- package/use/listRelated.js +3 -3
- package/use/listSubscription.js +102 -84
- package/use/loading.js +11 -3
- package/use/loadingError.js +3 -1
- package/use/objectInstance.js +77 -23
- package/use/objectSubscription.js +25 -26
- package/use/proxyError.js +28 -15
- package/use/proxyLoading.js +21 -8
- package/use/proxyLoadingError.js +33 -7
- package/utils/cancellableFetch.js +21 -1
- package/utils/isReactiveTyped.js +10 -0
- package/utils/refIfReactive.js +22 -2
- package/utils/toRefsIfReactive.js +28 -0
package/use/loading.js
CHANGED
|
@@ -6,14 +6,22 @@ import { readonly, ref } from "vue";
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* @typedef {object} LoadingStatus
|
|
9
|
+
* @typedef {object} LoadingProperties
|
|
12
10
|
* @property {LoadingReadonlyRef} loading - Whether the component is loading.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {object} LoadingFunctions
|
|
13
15
|
* @property {() => void} setLoading - Set the loading state to true.
|
|
14
16
|
* @property {() => void} clearLoading - Set the loading state to false.
|
|
15
17
|
*/
|
|
16
18
|
|
|
19
|
+
/**
|
|
20
|
+
* The loading state API.
|
|
21
|
+
*
|
|
22
|
+
* @typedef {LoadingProperties & LoadingFunctions} LoadingStatus
|
|
23
|
+
*/
|
|
24
|
+
|
|
17
25
|
/**
|
|
18
26
|
* A composable function for managing loading state.
|
|
19
27
|
*
|
package/use/loadingError.js
CHANGED
|
@@ -2,7 +2,9 @@ import { useLoading } from "./loading.js";
|
|
|
2
2
|
import { useError } from "./error.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* @typedef {import('./loading.js').
|
|
5
|
+
* @typedef {import('./loading.js').LoadingProperties & import('./error.js').ErrorProperties} LoadingErrorProperties
|
|
6
|
+
* @typedef {import('./loading.js').LoadingFunctions & import('./error.js').ErrorFunctions} LoadingErrorFunctions
|
|
7
|
+
* @typedef {LoadingErrorProperties & LoadingErrorFunctions} LoadingErrorStatus
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
10
|
/**
|
package/use/objectInstance.js
CHANGED
|
@@ -3,10 +3,10 @@ import { assignReactiveObject } from "../utils/assignReactiveObject.js";
|
|
|
3
3
|
import { useLoadingError } from "./loadingError.js";
|
|
4
4
|
import { reactive, readonly, ref } from "vue";
|
|
5
5
|
import { CancellablePromise, wrapMaybeCancellable } from "../utils/cancellablePromise.js";
|
|
6
|
-
import { refIfReactive } from "../utils/refIfReactive.js";
|
|
6
|
+
import { pkRefIfReactive, refIfReactive } from "../utils/refIfReactive.js";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* A composition function to manage create, retrieve, update, delete, and
|
|
9
|
+
* A composition function to manage create, retrieve, update, delete, patch, and executeAction operations.
|
|
10
10
|
*
|
|
11
11
|
* @module use/objectInstance.js
|
|
12
12
|
*/
|
|
@@ -40,7 +40,7 @@ import { refIfReactive } from "../utils/refIfReactive.js";
|
|
|
40
40
|
* Reactive arguments to be passed to the object instance.
|
|
41
41
|
*
|
|
42
42
|
* @typedef {object} ObjectInstanceRawProps
|
|
43
|
-
* @property {
|
|
43
|
+
* @property {import('../config/commonCrud.js').PkInput} [pk] - The pk of the object, optional to support creating new objects.
|
|
44
44
|
* @property {string} pkKey - The pk key of the object.
|
|
45
45
|
* @property {object} params - The arguments to be passed to the retrieve function.
|
|
46
46
|
* @property {import('../config/objectCrud.js').ObjectTarget} target - The arguments to be passed to the crud handlers.
|
|
@@ -55,6 +55,7 @@ import { refIfReactive } from "../utils/refIfReactive.js";
|
|
|
55
55
|
* @property {import('../config/objectCrud.js').CrudPatchFn} patch - The patch function.
|
|
56
56
|
* @property {import('../config/objectCrud.js').CrudDeleteFn} delete - The delete function.
|
|
57
57
|
* @property {import('../config/objectCrud.js').CrudObjectSubscribeFn} subscribe - The subscribe function.
|
|
58
|
+
* @property {import('../config/objectCrud.js').CrudObjectExecuteActionFn} executeAction - The executeAction function.
|
|
58
59
|
*/
|
|
59
60
|
|
|
60
61
|
/**
|
|
@@ -62,7 +63,7 @@ import { refIfReactive } from "../utils/refIfReactive.js";
|
|
|
62
63
|
*
|
|
63
64
|
* @typedef {object} ObjectInstanceRawMyState
|
|
64
65
|
* @property {import('vue').Reactive<ObjectInstanceRawStateCrud>} crud - The crud handlers.
|
|
65
|
-
* @property {import('vue').Ref<
|
|
66
|
+
* @property {import('vue').Ref<import('../config/commonCrud.js').Pk|undefined>} pk - The pk of the object.
|
|
66
67
|
* @property {import('vue').Ref<string|undefined>} pkKey - The pk key of the object.
|
|
67
68
|
* @property {import('vue').Ref<{[key:string]: any}>} params - The arguments to be passed to the retrieve function.
|
|
68
69
|
* @property {import('vue').Reactive<CrudObject>} object - The object.
|
|
@@ -72,7 +73,7 @@ import { refIfReactive } from "../utils/refIfReactive.js";
|
|
|
72
73
|
/**
|
|
73
74
|
* The raw state of the object instance.
|
|
74
75
|
*
|
|
75
|
-
* @typedef {ObjectInstanceRawMyState &
|
|
76
|
+
* @typedef {ObjectInstanceRawMyState & import('./loadingError.js').LoadingErrorProperties} ObjectInstanceRawState
|
|
76
77
|
*/
|
|
77
78
|
|
|
78
79
|
/**
|
|
@@ -86,15 +87,20 @@ import { refIfReactive } from "../utils/refIfReactive.js";
|
|
|
86
87
|
/** @typedef {{ object: ExistingCrudObject }} ObjectInstanceUpdateArgs */
|
|
87
88
|
/** @typedef {{ partialObject: ExistingCrudObject }} ObjectInstancePatchArgs */
|
|
88
89
|
|
|
90
|
+
/**
|
|
91
|
+
* @typedef {{[key:string]: any}} AdditionalArgs
|
|
92
|
+
*/
|
|
93
|
+
|
|
89
94
|
/**
|
|
90
95
|
* The functions available on the object instance.
|
|
91
96
|
*
|
|
92
97
|
* @typedef {object} ObjectInstanceMyFunctions
|
|
93
|
-
* @property {(args: ObjectInstanceCreateArgs) => import('../utils/cancellablePromise.js').MaybeCancellablePromise<boolean|never>} create - Called to turn the current object into a new object on the server.
|
|
94
|
-
* @property {(args?: import('./cancellableIntent.js').CommonRunTracking) => import('../utils/cancellablePromise.js').MaybeCancellablePromise<boolean|never>} retrieve - Called to retrieve the current object by pk from the server.
|
|
95
|
-
* @property {(args: ObjectInstanceUpdateArgs) => import('../utils/cancellablePromise.js').MaybeCancellablePromise<boolean|never>} update - Called to update the current object on the server.
|
|
96
|
-
* @property {() => import('../utils/cancellablePromise.js').MaybeCancellablePromise<boolean|never>} delete - Called to delete the current object on the server.
|
|
97
|
-
* @property {(args: ObjectInstancePatchArgs) => import('../utils/cancellablePromise.js').MaybeCancellablePromise<boolean|never>} patch - Called to patch the current object on the server.
|
|
98
|
+
* @property {(args: ObjectInstanceCreateArgs & AdditionalArgs) => import('../utils/cancellablePromise.js').MaybeCancellablePromise<boolean|never>} create - Called to turn the current object into a new object on the server.
|
|
99
|
+
* @property {(args?: Partial<import('./cancellableIntent.js').CommonRunTracking> & AdditionalArgs) => import('../utils/cancellablePromise.js').MaybeCancellablePromise<boolean|never>} retrieve - Called to retrieve the current object by pk from the server.
|
|
100
|
+
* @property {(args: ObjectInstanceUpdateArgs & AdditionalArgs) => import('../utils/cancellablePromise.js').MaybeCancellablePromise<boolean|never>} update - Called to update the current object on the server.
|
|
101
|
+
* @property {(args?: AdditionalArgs) => import('../utils/cancellablePromise.js').MaybeCancellablePromise<boolean|never>} delete - Called to delete the current object on the server.
|
|
102
|
+
* @property {(args: ObjectInstancePatchArgs & AdditionalArgs) => import('../utils/cancellablePromise.js').MaybeCancellablePromise<boolean|never>} patch - Called to patch the current object on the server.
|
|
103
|
+
* @property {(args: {action: string} & AdditionalArgs) => import('../utils/cancellablePromise.js').MaybeCancellablePromise<boolean|never>} executeAction - Called to execute certain action on the current object.
|
|
98
104
|
* @property {() => void} clear - Called to clear the object state.
|
|
99
105
|
*/
|
|
100
106
|
|
|
@@ -102,7 +108,7 @@ import { refIfReactive } from "../utils/refIfReactive.js";
|
|
|
102
108
|
* The functions available on the object instance, including the ability to clear LoadingError errors.
|
|
103
109
|
*
|
|
104
110
|
* @typedef {(
|
|
105
|
-
*
|
|
111
|
+
* import('./error.js').ErrorReadOnlyFunctions &
|
|
106
112
|
* ObjectInstanceMyFunctions
|
|
107
113
|
* )} ObjectInstanceFunctions
|
|
108
114
|
*/
|
|
@@ -150,7 +156,16 @@ export const objectInstanceStateKeys = [
|
|
|
150
156
|
"deleted",
|
|
151
157
|
];
|
|
152
158
|
|
|
153
|
-
export const objectInstanceFunctions = [
|
|
159
|
+
export const objectInstanceFunctions = [
|
|
160
|
+
"create",
|
|
161
|
+
"retrieve",
|
|
162
|
+
"update",
|
|
163
|
+
"delete",
|
|
164
|
+
"patch",
|
|
165
|
+
"executeAction",
|
|
166
|
+
"clearError",
|
|
167
|
+
"clear",
|
|
168
|
+
];
|
|
154
169
|
|
|
155
170
|
/**
|
|
156
171
|
* Initializes multiple useObjectInstance instances, returning an object of them based on the keys of the instanceArgs.
|
|
@@ -217,7 +232,7 @@ export function useObjectInstances(instanceArgs) {
|
|
|
217
232
|
* ```
|
|
218
233
|
*
|
|
219
234
|
* @param {ObjectInstanceOptions} options - The options to be passed to useObjectInstance.
|
|
220
|
-
* @returns {ObjectInstance} - An object used to manage create, retrieve, update, delete, and
|
|
235
|
+
* @returns {ObjectInstance} - An object used to manage create, retrieve, update, delete, patch, and executeAction operations.
|
|
221
236
|
*/
|
|
222
237
|
export function useObjectInstance({ props, handlers = {} }) {
|
|
223
238
|
// missing pk is fine, to support creating new objects
|
|
@@ -240,9 +255,10 @@ export function useObjectInstance({ props, handlers = {} }) {
|
|
|
240
255
|
delete: defaultObjectCrud.delete,
|
|
241
256
|
patch: defaultObjectCrud.patch,
|
|
242
257
|
subscribe: defaultObjectCrud.subscribe,
|
|
258
|
+
executeAction: defaultObjectCrud.executeAction,
|
|
243
259
|
},
|
|
244
260
|
object: {},
|
|
245
|
-
pk:
|
|
261
|
+
pk: pkRefIfReactive(props, "pk", null),
|
|
246
262
|
pkKey: refIfReactive(props, "pkKey"),
|
|
247
263
|
params: refIfReactive(props, "params", {}),
|
|
248
264
|
loading: loadingError.loading,
|
|
@@ -263,7 +279,7 @@ export function useObjectInstance({ props, handlers = {} }) {
|
|
|
263
279
|
/** @type {ObjectInstance} */
|
|
264
280
|
const instance = {
|
|
265
281
|
state,
|
|
266
|
-
create: ({ object }) => {
|
|
282
|
+
create: ({ object, ...additionalArgs }) => {
|
|
267
283
|
// this function cannot be async, or the resulting promise will lose its .cancel() method
|
|
268
284
|
if (state.loading) {
|
|
269
285
|
// we throw because we want devs to see this error in the console
|
|
@@ -274,6 +290,7 @@ export function useObjectInstance({ props, handlers = {} }) {
|
|
|
274
290
|
loadingError.clearError();
|
|
275
291
|
const isCancelled = ref(false);
|
|
276
292
|
const createPromise = state.crud.create({
|
|
293
|
+
...additionalArgs,
|
|
277
294
|
target: state.crud.args,
|
|
278
295
|
object,
|
|
279
296
|
params: state.params,
|
|
@@ -303,8 +320,7 @@ export function useObjectInstance({ props, handlers = {} }) {
|
|
|
303
320
|
: undefined
|
|
304
321
|
);
|
|
305
322
|
},
|
|
306
|
-
retrieve: (args) => {
|
|
307
|
-
const { runId, isCurrentRun } = args || {};
|
|
323
|
+
retrieve: (args = {}) => {
|
|
308
324
|
// this function cannot be async, or the resulting promise will lose its .cancel() method
|
|
309
325
|
if (promises.retrieve) {
|
|
310
326
|
// if a retrieve is already in progress, return the existing promise
|
|
@@ -322,8 +338,7 @@ export function useObjectInstance({ props, handlers = {} }) {
|
|
|
322
338
|
let retrievePromise = null;
|
|
323
339
|
try {
|
|
324
340
|
retrievePromise = state.crud.retrieve({
|
|
325
|
-
|
|
326
|
-
isCurrentRun,
|
|
341
|
+
...args,
|
|
327
342
|
target: state.crud.args,
|
|
328
343
|
pk: state.pk,
|
|
329
344
|
params: state.params,
|
|
@@ -361,7 +376,7 @@ export function useObjectInstance({ props, handlers = {} }) {
|
|
|
361
376
|
|
|
362
377
|
return promises.retrieve;
|
|
363
378
|
},
|
|
364
|
-
update: ({ object }) => {
|
|
379
|
+
update: ({ object, ...additionalArgs }) => {
|
|
365
380
|
// this function cannot be async, or the resulting promise will lose its .cancel() method
|
|
366
381
|
if (state.loading) {
|
|
367
382
|
// we throw because we want devs to see this error in the console
|
|
@@ -372,6 +387,7 @@ export function useObjectInstance({ props, handlers = {} }) {
|
|
|
372
387
|
loadingError.clearError();
|
|
373
388
|
const isCancelled = ref(false);
|
|
374
389
|
const updatePromise = state.crud.update({
|
|
390
|
+
...additionalArgs,
|
|
375
391
|
target: state.crud.args,
|
|
376
392
|
object,
|
|
377
393
|
params: state.params,
|
|
@@ -400,7 +416,7 @@ export function useObjectInstance({ props, handlers = {} }) {
|
|
|
400
416
|
: undefined
|
|
401
417
|
);
|
|
402
418
|
},
|
|
403
|
-
delete: () => {
|
|
419
|
+
delete: (args = {}) => {
|
|
404
420
|
// this function cannot be async, or the resulting promise will lose its .cancel() method
|
|
405
421
|
if (state.loading) {
|
|
406
422
|
// we throw because we want devs to see this error in the console
|
|
@@ -410,6 +426,7 @@ export function useObjectInstance({ props, handlers = {} }) {
|
|
|
410
426
|
loadingError.setLoading();
|
|
411
427
|
loadingError.clearError();
|
|
412
428
|
const deletePromise = state.crud.delete({
|
|
429
|
+
...args,
|
|
413
430
|
target: state.crud.args,
|
|
414
431
|
pk: state.pk,
|
|
415
432
|
pkKey: state.pkKey,
|
|
@@ -436,7 +453,7 @@ export function useObjectInstance({ props, handlers = {} }) {
|
|
|
436
453
|
: undefined
|
|
437
454
|
);
|
|
438
455
|
},
|
|
439
|
-
patch: ({ partialObject }) => {
|
|
456
|
+
patch: ({ partialObject, ...additionalArgs }) => {
|
|
440
457
|
// this function cannot be async, or the resulting promise will lose its .cancel() method
|
|
441
458
|
if (state.loading) {
|
|
442
459
|
// we throw because we want devs to see this error in the console
|
|
@@ -447,10 +464,11 @@ export function useObjectInstance({ props, handlers = {} }) {
|
|
|
447
464
|
loadingError.clearError();
|
|
448
465
|
const isCancelled = ref(false);
|
|
449
466
|
const patchPromise = state.crud.patch({
|
|
467
|
+
...additionalArgs,
|
|
450
468
|
target: state.crud.args,
|
|
469
|
+
partialObject,
|
|
451
470
|
pk: state.pk,
|
|
452
471
|
pkKey: state.pkKey,
|
|
453
|
-
partialObject,
|
|
454
472
|
params: state.params,
|
|
455
473
|
isCancelled: readonly(isCancelled),
|
|
456
474
|
});
|
|
@@ -476,6 +494,42 @@ export function useObjectInstance({ props, handlers = {} }) {
|
|
|
476
494
|
: undefined
|
|
477
495
|
);
|
|
478
496
|
},
|
|
497
|
+
executeAction: ({ action, ...additionalArgs }) => {
|
|
498
|
+
if (state.loading) {
|
|
499
|
+
throw new ObjectError("already loading.", "already-loading");
|
|
500
|
+
}
|
|
501
|
+
loadingError.setLoading();
|
|
502
|
+
loadingError.clearError();
|
|
503
|
+
const isCancelled = ref(false);
|
|
504
|
+
const executeActionPromise = state.crud.executeAction({
|
|
505
|
+
...additionalArgs,
|
|
506
|
+
target: state.crud.args,
|
|
507
|
+
action,
|
|
508
|
+
pk: state.pk,
|
|
509
|
+
pkKey: state.pkKey,
|
|
510
|
+
isCancelled: readonly(isCancelled),
|
|
511
|
+
});
|
|
512
|
+
return wrapMaybeCancellable(
|
|
513
|
+
executeActionPromise
|
|
514
|
+
.then(() => {
|
|
515
|
+
return true;
|
|
516
|
+
})
|
|
517
|
+
.catch((/** @type {Error} */ error) => {
|
|
518
|
+
loadingError.setError(error);
|
|
519
|
+
return false;
|
|
520
|
+
})
|
|
521
|
+
.finally(() => {
|
|
522
|
+
loadingError.clearLoading();
|
|
523
|
+
}),
|
|
524
|
+
executeActionPromise.cancel
|
|
525
|
+
? async (/** @type {any} */ reason) => {
|
|
526
|
+
isCancelled.value = true;
|
|
527
|
+
await executeActionPromise.cancel?.(reason);
|
|
528
|
+
loadingError.clearLoading();
|
|
529
|
+
}
|
|
530
|
+
: undefined
|
|
531
|
+
);
|
|
532
|
+
},
|
|
479
533
|
clearError: loadingError.clearError,
|
|
480
534
|
clear: () => {
|
|
481
535
|
loadingError.clearError();
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { assignReactiveObject } from "../utils/assignReactiveObject.js";
|
|
2
2
|
import { useCancellableIntent } from "./cancellableIntent.js";
|
|
3
3
|
import { useObjectInstance } from "./objectInstance.js";
|
|
4
|
-
import { computed, reactive,
|
|
4
|
+
import { computed, reactive, readonly, ref, toRefs } from "vue";
|
|
5
5
|
import { refIfReactive } from "../utils/refIfReactive.js";
|
|
6
|
-
import { useProxyError } from "./proxyError.js";
|
|
6
|
+
import { asWatchableError, useProxyError } from "./proxyError.js";
|
|
7
7
|
import { loadingCombine } from "../utils/loadingCombine.js";
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -91,14 +91,18 @@ export const objectSubscriptionFunctions = ["clearError"];
|
|
|
91
91
|
*/
|
|
92
92
|
|
|
93
93
|
/**
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* @typedef {object & import('./objectInstance.js').ObjectInstanceOptions} ObjectSubscriptionOptions
|
|
94
|
+
* @typedef {object} ObjectSubscriptionOwnOptions
|
|
97
95
|
* @property {import('./objectInstance.js').ObjectInstance} [objectInstance] - An object instance to use instead of creating a new one.
|
|
98
96
|
* @property {import('vue').UnwrapNestedRefs<(
|
|
99
97
|
* ObjectSubscriptionRawProps & import('./objectInstance.js').ObjectInstanceRawProps
|
|
100
98
|
* )>} props - The reactive args to be passed to useObjectInstance.
|
|
101
|
-
* @property {import('
|
|
99
|
+
* @property {import('../config/objectCrud.js').ObjectCrudHandlers} [handlers] - The handlers to be passed to useObjectInstance.
|
|
100
|
+
*/
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Options for initializing an object subscription, including reactive props and non-reactive handlers.
|
|
104
|
+
*
|
|
105
|
+
* @typedef {ObjectSubscriptionOwnOptions & import('./objectInstance.js').ObjectInstanceOptions} ObjectSubscriptionOptions
|
|
102
106
|
*/
|
|
103
107
|
|
|
104
108
|
/**
|
|
@@ -200,14 +204,8 @@ export function useObjectSubscription({ objectInstance, props, handlers }) {
|
|
|
200
204
|
const intendToRetrieve = refIfReactive(props, "intendToRetrieve", false);
|
|
201
205
|
const intendToSubscribe = refIfReactive(props, "intendToSubscribe", false);
|
|
202
206
|
const parentState = objectInstance.state;
|
|
203
|
-
/** @type {import('./proxyError.js').
|
|
204
|
-
const errorStates = ref([
|
|
205
|
-
{
|
|
206
|
-
error: toRef(parentState, "error"),
|
|
207
|
-
errored: toRef(parentState, "errored"),
|
|
208
|
-
clearError: objectInstance.clearError,
|
|
209
|
-
},
|
|
210
|
-
]);
|
|
207
|
+
/** @type {import('vue').Ref<import('./proxyError.js').WatchableError[]>} */
|
|
208
|
+
const errorStates = ref([asWatchableError(objectInstance)]);
|
|
211
209
|
const proxyError = useProxyError(errorStates);
|
|
212
210
|
const {
|
|
213
211
|
crud: parentCrud,
|
|
@@ -249,7 +247,7 @@ export function useObjectSubscription({ objectInstance, props, handlers }) {
|
|
|
249
247
|
}
|
|
250
248
|
};
|
|
251
249
|
const parentState = objectInstance.state;
|
|
252
|
-
|
|
250
|
+
const subscribePromise = parentState.crud.subscribe({
|
|
253
251
|
runId,
|
|
254
252
|
isCurrentRun,
|
|
255
253
|
target: parentState.crud.args,
|
|
@@ -257,8 +255,17 @@ export function useObjectSubscription({ objectInstance, props, handlers }) {
|
|
|
257
255
|
pkKey: parentState.pkKey,
|
|
258
256
|
params: state.params,
|
|
259
257
|
callback: subscribeCallback,
|
|
260
|
-
isCancelled,
|
|
258
|
+
isCancelled: readonly(isCancelled),
|
|
261
259
|
});
|
|
260
|
+
if (subscribePromise?.cancel) {
|
|
261
|
+
const originalCancel = subscribePromise.cancel.bind(subscribePromise);
|
|
262
|
+
const cancelWithFlag = async (/** @type {any} */ reason) => {
|
|
263
|
+
isCancelled.value = true;
|
|
264
|
+
return originalCancel(reason);
|
|
265
|
+
};
|
|
266
|
+
subscribePromise.cancel = cancelWithFlag;
|
|
267
|
+
}
|
|
268
|
+
return subscribePromise;
|
|
262
269
|
},
|
|
263
270
|
watchArguments: {
|
|
264
271
|
intendToSubscribe,
|
|
@@ -272,16 +279,8 @@ export function useObjectSubscription({ objectInstance, props, handlers }) {
|
|
|
272
279
|
// subscriptions persist until cancelled
|
|
273
280
|
clearActiveOnResolved: false,
|
|
274
281
|
});
|
|
275
|
-
errorStates.value.push(
|
|
276
|
-
|
|
277
|
-
errored: toRef(retrieveIntent.state, "errored"),
|
|
278
|
-
clearError: retrieveIntent.clearError,
|
|
279
|
-
});
|
|
280
|
-
errorStates.value.push({
|
|
281
|
-
error: toRef(subscribeIntent.state, "error"),
|
|
282
|
-
errored: toRef(subscribeIntent.state, "errored"),
|
|
283
|
-
clearError: subscribeIntent.clearError,
|
|
284
|
-
});
|
|
282
|
+
errorStates.value.push(asWatchableError(retrieveIntent));
|
|
283
|
+
errorStates.value.push(asWatchableError(subscribeIntent));
|
|
285
284
|
/** @type {ObjectSubscriptionState} */
|
|
286
285
|
const state = reactive({
|
|
287
286
|
crud: parentCrud,
|
package/use/proxyError.js
CHANGED
|
@@ -1,27 +1,17 @@
|
|
|
1
1
|
import { computed, readonly, unref } from "vue";
|
|
2
2
|
import identity from "lodash-es/identity.js";
|
|
3
|
+
import { toRefsIfReactive } from "../utils/toRefsIfReactive.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
|
-
* @typedef {
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @typedef {import("vue").Ref<ReadonlyErrorStatus>} RefErrorStatus
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* @typedef {ReadonlyErrorStatus | RefErrorStatus} WatchableError
|
|
12
|
-
*/
|
|
13
|
-
/**
|
|
14
|
-
* @typedef {import('vue').Ref<WatchableError[]>} WatchableErrorRef
|
|
15
|
-
*/
|
|
16
|
-
/**
|
|
17
|
-
* @typedef {WatchableErrorRef|WatchableError[]} WatchableErrors
|
|
6
|
+
* @typedef {import('./error.js').ReadonlyErrorStatus | import("vue").Reactive<import('./error.js').ReadonlyErrorStatus>} WatchableError
|
|
7
|
+
* @typedef {import('vue').MaybeRef<WatchableError>} MaybeRefWatchableError
|
|
18
8
|
*/
|
|
19
9
|
|
|
20
10
|
/**
|
|
21
11
|
* A composable function for aggregating error state across multiple sources.
|
|
22
12
|
*
|
|
23
|
-
* @param {
|
|
24
|
-
* @returns {ReadonlyErrorStatus} An object containing aggregated reactive fields and actions for error state.
|
|
13
|
+
* @param {import('vue').MaybeRef<MaybeRefWatchableError[]>} errors - The error states to monitor.
|
|
14
|
+
* @returns {import('./error.js').ReadonlyErrorStatus} An object containing aggregated reactive fields and actions for error state.
|
|
25
15
|
*/
|
|
26
16
|
export function useProxyError(errors) {
|
|
27
17
|
const error = computed(
|
|
@@ -46,3 +36,26 @@ export function useProxyError(errors) {
|
|
|
46
36
|
},
|
|
47
37
|
};
|
|
48
38
|
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @typedef {{ state: import('vue').Reactive<import('./error.js').ErrorProperties> } & import('./error.js').ErrorReadOnlyFunctions} SeparateStateError
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Adapt an object with reactive error state into a WatchableError shape.
|
|
46
|
+
* Accepts either an object with a `state` property or an object that already exposes error/errored/clearError.
|
|
47
|
+
*
|
|
48
|
+
* @param {import('vue').MaybeRef<
|
|
49
|
+
* SeparateStateError |
|
|
50
|
+
* WatchableError
|
|
51
|
+
* >} source - The source object to adapt.
|
|
52
|
+
* @returns {WatchableError} - The adapted WatchableError object.
|
|
53
|
+
*/
|
|
54
|
+
export function asWatchableError(source) {
|
|
55
|
+
const unwrapped = unref(source);
|
|
56
|
+
if ("state" in unwrapped) {
|
|
57
|
+
const stateRefs = toRefsIfReactive(unwrapped.state);
|
|
58
|
+
return { ...stateRefs, clearError: unwrapped.clearError };
|
|
59
|
+
}
|
|
60
|
+
return toRefsIfReactive(unwrapped);
|
|
61
|
+
}
|
package/use/proxyLoading.js
CHANGED
|
@@ -1,22 +1,35 @@
|
|
|
1
|
-
import { computed,
|
|
1
|
+
import { computed, unref } from "vue";
|
|
2
2
|
import { loadingCombine } from "../utils/loadingCombine.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* @typedef {
|
|
6
|
-
* @typedef {import(
|
|
7
|
-
* @typedef {
|
|
5
|
+
* @typedef {import('./loading.js').LoadingProperties | import("vue").Reactive<import('./loading.js').LoadingProperties>} WatchableLoading
|
|
6
|
+
* @typedef {import('vue').MaybeRef<WatchableLoading>} MaybeRefWatchableLoading
|
|
7
|
+
* @typedef {import('./loading.js').LoadingProperties} ReadonlyLoadingStatus
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* A composable function for aggregating loading state across multiple sources.
|
|
12
12
|
*
|
|
13
|
-
* @param {
|
|
13
|
+
* @param {import('vue').MaybeRef<MaybeRefWatchableLoading[]>} loadings - The loading states to monitor.
|
|
14
14
|
* @returns {ReadonlyLoadingStatus} An object containing the aggregated loading field.
|
|
15
15
|
*/
|
|
16
16
|
export function useProxyLoading(loadings) {
|
|
17
|
-
const loading = computed(() => loadingCombine(...unref(loadings).map((l) => unref(unref(l).loading))));
|
|
18
|
-
|
|
19
17
|
return {
|
|
20
|
-
loading:
|
|
18
|
+
loading: computed(() => loadingCombine(...unref(loadings).map((l) => unref(unref(l).loading)))),
|
|
21
19
|
};
|
|
22
20
|
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Adapt an object with reactive loading state into a WatchableLoading shape.
|
|
24
|
+
* Accepts either an object with a `state` property or an object that already exposes `loading`.
|
|
25
|
+
*
|
|
26
|
+
* @param {import('vue').MaybeRef<{ state: WatchableLoading } | WatchableLoading>} source - The source object to adapt.
|
|
27
|
+
* @returns {WatchableLoading} - The adapted WatchableLoading object.
|
|
28
|
+
*/
|
|
29
|
+
export function asWatchableLoading(source) {
|
|
30
|
+
const unwrappedSource = unref(source);
|
|
31
|
+
return (
|
|
32
|
+
/** @type {WatchableLoading} */
|
|
33
|
+
"state" in unwrappedSource ? unwrappedSource.state : unwrappedSource
|
|
34
|
+
);
|
|
35
|
+
}
|
package/use/proxyLoadingError.js
CHANGED
|
@@ -1,20 +1,46 @@
|
|
|
1
|
-
import { useProxyLoading } from "./proxyLoading.js";
|
|
2
|
-
import { useProxyError } from "./proxyError.js";
|
|
1
|
+
import { asWatchableLoading, useProxyLoading } from "./proxyLoading.js";
|
|
2
|
+
import { asWatchableError, useProxyError } from "./proxyError.js";
|
|
3
|
+
import { toRefsIfReactive } from "../utils/toRefsIfReactive.js";
|
|
4
|
+
import { unref } from "vue";
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* @typedef {import('./proxyLoading.js').WatchableLoading & import('./proxyError.js').WatchableError} WatchableLoadingError
|
|
6
|
-
* @typedef {import('
|
|
8
|
+
* @typedef {import('vue').MaybeRef<WatchableLoadingError>} MaybeRefWatchableLoadingError
|
|
9
|
+
* @typedef {import('./loading.js').LoadingProperties & import('./error.js').ReadonlyErrorStatus} ProxyLoadingError
|
|
7
10
|
*/
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
|
-
* A composable function combining aggregated loading and error state.
|
|
13
|
+
* A composable function combining aggregated loading and error state. Use `asWatchableLoadingError` to convert <List|Object><Instance|Subscription> to WatchableLoadingError.
|
|
11
14
|
*
|
|
12
|
-
* @param {
|
|
15
|
+
* @param {import('vue').MaybeRef<MaybeRefWatchableLoadingError[]>} loadingErrors - The loading and error states to monitor.
|
|
13
16
|
* @returns {ProxyLoadingError} - An object containing aggregated reactive fields and actions for both loading and error state.
|
|
14
17
|
*/
|
|
15
18
|
export function useProxyLoadingError(loadingErrors) {
|
|
19
|
+
const unwrappedLoadingErrors = unref(loadingErrors);
|
|
16
20
|
return {
|
|
17
|
-
...useProxyLoading(
|
|
18
|
-
...useProxyError(
|
|
21
|
+
...useProxyLoading(unwrappedLoadingErrors),
|
|
22
|
+
...useProxyError(unwrappedLoadingErrors),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {{ state: import('vue').Reactive<import('./error.js').ErrorProperties> } & import('./error.js').ErrorReadOnlyFunctions} SeparateStateLoadingError
|
|
28
|
+
*/
|
|
29
|
+
/**
|
|
30
|
+
* Adapt an object that exposes loading/error state and clearError into a WatchableLoadingError shape.
|
|
31
|
+
*
|
|
32
|
+
* @param {import('vue').MaybeRef<SeparateStateLoadingError | WatchableLoadingError>} source - The source object to adapt.
|
|
33
|
+
* @returns {WatchableLoadingError} - The adapted WatchableLoadingError object.
|
|
34
|
+
*/
|
|
35
|
+
export function asWatchableLoadingError(source) {
|
|
36
|
+
const unwrappedSource = unref(source);
|
|
37
|
+
const isSeparateState = "state" in unwrappedSource;
|
|
38
|
+
const normalizedRefs = /** @type {WatchableLoadingError} */ (
|
|
39
|
+
isSeparateState ? unwrappedSource.state : unwrappedSource
|
|
40
|
+
);
|
|
41
|
+
const unwrappedRefs = toRefsIfReactive(normalizedRefs);
|
|
42
|
+
return {
|
|
43
|
+
...asWatchableLoading(unwrappedRefs),
|
|
44
|
+
...asWatchableError(unwrappedRefs),
|
|
19
45
|
};
|
|
20
46
|
}
|
|
@@ -11,9 +11,29 @@ import { CancellablePromise } from "./cancellablePromise.js";
|
|
|
11
11
|
*/
|
|
12
12
|
export function cancellableFetch(input, init, transform) {
|
|
13
13
|
const controller = new AbortController();
|
|
14
|
+
const externalSignal = init?.signal;
|
|
14
15
|
const signal = controller.signal;
|
|
16
|
+
let cleanupExternalSignal = () => {};
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
if (externalSignal) {
|
|
19
|
+
if (externalSignal.aborted) {
|
|
20
|
+
controller.abort(externalSignal.reason);
|
|
21
|
+
} else {
|
|
22
|
+
const handleAbort = () => {
|
|
23
|
+
controller.abort(externalSignal.reason);
|
|
24
|
+
};
|
|
25
|
+
externalSignal.addEventListener("abort", handleAbort, { once: true });
|
|
26
|
+
cleanupExternalSignal = () => {
|
|
27
|
+
externalSignal.removeEventListener("abort", handleAbort);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const basePromise = fetch(input, { ...init, signal })
|
|
33
|
+
.then(transform)
|
|
34
|
+
.finally(() => {
|
|
35
|
+
cleanupExternalSignal();
|
|
36
|
+
});
|
|
17
37
|
|
|
18
38
|
return CancellablePromise(basePromise, async (/** @type {any} */ reason) => {
|
|
19
39
|
controller.abort(reason);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { isReactive } from "vue";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @template {object} T
|
|
5
|
+
* @param {T | import('vue').Reactive<T>} v - The value to check.
|
|
6
|
+
* @returns {v is import('vue').Reactive<T>} - True if the value is reactive, false otherwise.
|
|
7
|
+
*/
|
|
8
|
+
export function isReactiveTyped(v) {
|
|
9
|
+
return !!v && isReactive(v);
|
|
10
|
+
}
|
package/utils/refIfReactive.js
CHANGED
|
@@ -7,8 +7,8 @@ import { computed, isReactive, toRef, unref } from "vue";
|
|
|
7
7
|
* @template {keyof T} K
|
|
8
8
|
* @param {T & object | undefined | null} source - The source object.
|
|
9
9
|
* @param {K} property - The property to access.
|
|
10
|
-
* @param {T[K]} [defaultValue] - The default value to use if source or property is missing.
|
|
11
|
-
* @returns {import('vue').ComputedRef<T[K]> | import('vue').Ref<T[K]>} The ref to the property if the source is reactive
|
|
10
|
+
* @param {T[K] | undefined} [defaultValue] - The default value to use if source or property is missing.
|
|
11
|
+
* @returns {import('vue').ComputedRef<T[K] | undefined> | import('vue').Ref<T[K] | undefined>} The ref to the property if the source is reactive; otherwise a computed that can be undefined when missing.
|
|
12
12
|
*/
|
|
13
13
|
export const refIfReactive = (source, property, defaultValue) => {
|
|
14
14
|
if (source && isReactive(source)) {
|
|
@@ -16,3 +16,23 @@ export const refIfReactive = (source, property, defaultValue) => {
|
|
|
16
16
|
}
|
|
17
17
|
return computed(() => unref(source?.[property]) ?? defaultValue);
|
|
18
18
|
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns a ref to a pk property, coercing string|number input to string output.
|
|
22
|
+
* Returns undefined if the source pk is null/undefined.
|
|
23
|
+
*
|
|
24
|
+
* @param {object | undefined | null} source - The source object containing the pk.
|
|
25
|
+
* @param {string} [property="pk"] - The property name to access.
|
|
26
|
+
* @param {import('../config/commonCrud.js').Pk | null} [defaultValue=null] - The default value if missing.
|
|
27
|
+
* @returns {import('vue').ComputedRef<import('../config/commonCrud.js').Pk | undefined>} A computed ref that coerces to string.
|
|
28
|
+
*/
|
|
29
|
+
export const pkRefIfReactive = (source, property = "pk", defaultValue = null) => {
|
|
30
|
+
const rawRef = refIfReactive(source, property, defaultValue);
|
|
31
|
+
return computed(() => {
|
|
32
|
+
const value = unref(rawRef);
|
|
33
|
+
if (value === null || value === undefined) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
return String(value);
|
|
37
|
+
});
|
|
38
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { toRefs } from "vue";
|
|
2
|
+
import { isReactiveTyped } from "./isReactiveTyped.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @template {object} T
|
|
6
|
+
* @overload
|
|
7
|
+
* @param {import('vue').Reactive<T>} source - The source reactive object.
|
|
8
|
+
* @returns {import('vue').ToRefs<T>} - The refs of the reactive object.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* @template {object} T
|
|
12
|
+
* @overload
|
|
13
|
+
* @param {T} source - The source object.
|
|
14
|
+
* @returns {T} - The original object.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Converts a reactive object to refs, or returns the original object if not reactive.
|
|
18
|
+
*
|
|
19
|
+
* @template {object} T
|
|
20
|
+
* @param {T | import('vue').Reactive<T>} source - The source object.
|
|
21
|
+
* @returns {T | import('vue').ToRefs<T>} - The refs of the reactive object or the original object.
|
|
22
|
+
*/
|
|
23
|
+
export function toRefsIfReactive(source) {
|
|
24
|
+
if (isReactiveTyped(source)) {
|
|
25
|
+
return toRefs(/** @type {import('vue').Reactive<object>} */ (source));
|
|
26
|
+
}
|
|
27
|
+
return source;
|
|
28
|
+
}
|