@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
|
@@ -1,15 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @typedef {
|
|
3
|
-
* @typedef {import(
|
|
4
|
-
* @typedef {
|
|
2
|
+
* @typedef {import('./loading.js').LoadingProperties | import("vue").Reactive<import('./loading.js').LoadingProperties>} WatchableLoading
|
|
3
|
+
* @typedef {import('vue').MaybeRef<WatchableLoading>} MaybeRefWatchableLoading
|
|
4
|
+
* @typedef {import('./loading.js').LoadingProperties} ReadonlyLoadingStatus
|
|
5
5
|
*/
|
|
6
6
|
/**
|
|
7
7
|
* A composable function for aggregating loading state across multiple sources.
|
|
8
8
|
*
|
|
9
|
-
* @param {
|
|
9
|
+
* @param {import('vue').MaybeRef<MaybeRefWatchableLoading[]>} loadings - The loading states to monitor.
|
|
10
10
|
* @returns {ReadonlyLoadingStatus} An object containing the aggregated loading field.
|
|
11
11
|
*/
|
|
12
|
-
export function useProxyLoading(loadings:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
export function useProxyLoading(loadings: import("vue").MaybeRef<MaybeRefWatchableLoading[]>): ReadonlyLoadingStatus;
|
|
13
|
+
/**
|
|
14
|
+
* Adapt an object with reactive loading state into a WatchableLoading shape.
|
|
15
|
+
* Accepts either an object with a `state` property or an object that already exposes `loading`.
|
|
16
|
+
*
|
|
17
|
+
* @param {import('vue').MaybeRef<{ state: WatchableLoading } | WatchableLoading>} source - The source object to adapt.
|
|
18
|
+
* @returns {WatchableLoading} - The adapted WatchableLoading object.
|
|
19
|
+
*/
|
|
20
|
+
export function asWatchableLoading(source: import("vue").MaybeRef<{
|
|
21
|
+
state: WatchableLoading;
|
|
22
|
+
} | WatchableLoading>): WatchableLoading;
|
|
23
|
+
export type WatchableLoading = import("./loading.js").LoadingProperties | import("vue").Reactive<import("./loading.js").LoadingProperties>;
|
|
24
|
+
export type MaybeRefWatchableLoading = import("vue").MaybeRef<WatchableLoading>;
|
|
25
|
+
export type ReadonlyLoadingStatus = import("./loading.js").LoadingProperties;
|
|
@@ -1,13 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {import('./proxyLoading.js').WatchableLoading & import('./proxyError.js').WatchableError} WatchableLoadingError
|
|
3
|
-
* @typedef {import('
|
|
3
|
+
* @typedef {import('vue').MaybeRef<WatchableLoadingError>} MaybeRefWatchableLoadingError
|
|
4
|
+
* @typedef {import('./loading.js').LoadingProperties & import('./error.js').ReadonlyErrorStatus} ProxyLoadingError
|
|
4
5
|
*/
|
|
5
6
|
/**
|
|
6
|
-
* A composable function combining aggregated loading and error state.
|
|
7
|
+
* A composable function combining aggregated loading and error state. Use `asWatchableLoadingError` to convert <List|Object><Instance|Subscription> to WatchableLoadingError.
|
|
7
8
|
*
|
|
8
|
-
* @param {
|
|
9
|
+
* @param {import('vue').MaybeRef<MaybeRefWatchableLoadingError[]>} loadingErrors - The loading and error states to monitor.
|
|
9
10
|
* @returns {ProxyLoadingError} - An object containing aggregated reactive fields and actions for both loading and error state.
|
|
10
11
|
*/
|
|
11
|
-
export function useProxyLoadingError(loadingErrors:
|
|
12
|
+
export function useProxyLoadingError(loadingErrors: import("vue").MaybeRef<MaybeRefWatchableLoadingError[]>): ProxyLoadingError;
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {{ state: import('vue').Reactive<import('./error.js').ErrorProperties> } & import('./error.js').ErrorReadOnlyFunctions} SeparateStateLoadingError
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Adapt an object that exposes loading/error state and clearError into a WatchableLoadingError shape.
|
|
18
|
+
*
|
|
19
|
+
* @param {import('vue').MaybeRef<SeparateStateLoadingError | WatchableLoadingError>} source - The source object to adapt.
|
|
20
|
+
* @returns {WatchableLoadingError} - The adapted WatchableLoadingError object.
|
|
21
|
+
*/
|
|
22
|
+
export function asWatchableLoadingError(source: import("vue").MaybeRef<SeparateStateLoadingError | WatchableLoadingError>): WatchableLoadingError;
|
|
12
23
|
export type WatchableLoadingError = import("./proxyLoading.js").WatchableLoading & import("./proxyError.js").WatchableError;
|
|
13
|
-
export type
|
|
24
|
+
export type MaybeRefWatchableLoadingError = import("vue").MaybeRef<WatchableLoadingError>;
|
|
25
|
+
export type ProxyLoadingError = import("./loading.js").LoadingProperties & import("./error.js").ReadonlyErrorStatus;
|
|
26
|
+
export type SeparateStateLoadingError = {
|
|
27
|
+
state: import("vue").Reactive<import("./error.js").ErrorProperties>;
|
|
28
|
+
} & import("./error.js").ErrorReadOnlyFunctions;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @template {object} T
|
|
3
|
+
* @param {T | import('vue').Reactive<T>} v - The value to check.
|
|
4
|
+
* @returns {v is import('vue').Reactive<T>} - True if the value is reactive, false otherwise.
|
|
5
|
+
*/
|
|
6
|
+
export function isReactiveTyped<T extends unknown>(v: T | import("vue").Reactive<T>): v is import("vue").Reactive<T>;
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export function refIfReactive<T, K extends keyof T>(source: (T & object) | undefined | null, property: K, defaultValue?: T[K]): import("vue").ComputedRef<T[K]> | import("vue").Ref<T[K]>;
|
|
1
|
+
export function refIfReactive<T, K extends keyof T>(source: (T & object) | undefined | null, property: K, defaultValue?: T[K] | undefined): import("vue").ComputedRef<T[K] | undefined> | import("vue").Ref<T[K] | undefined>;
|
|
2
|
+
export function pkRefIfReactive(source: object | undefined | null, property?: string, defaultValue?: import("../config/commonCrud.js").Pk | null): import("vue").ComputedRef<import("../config/commonCrud.js").Pk | undefined>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @template {object} T
|
|
3
|
+
* @overload
|
|
4
|
+
* @param {import('vue').Reactive<T>} source - The source reactive object.
|
|
5
|
+
* @returns {import('vue').ToRefs<T>} - The refs of the reactive object.
|
|
6
|
+
*/
|
|
7
|
+
export function toRefsIfReactive<T extends unknown>(source: import("vue").Reactive<T>): import("vue").ToRefs<T>;
|
|
8
|
+
/**
|
|
9
|
+
* @template {object} T
|
|
10
|
+
* @overload
|
|
11
|
+
* @param {T} source - The source object.
|
|
12
|
+
* @returns {T} - The original object.
|
|
13
|
+
*/
|
|
14
|
+
export function toRefsIfReactive<T extends unknown>(source: T): T;
|
package/use/cancellableIntent.js
CHANGED
|
@@ -34,7 +34,7 @@ export class CancellableIntentError extends Error {
|
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
* @typedef {object}
|
|
37
|
+
* @typedef {object} CancellableIntentMyState - The raw state of the cancellable intent.
|
|
38
38
|
* @property {import('vue').ComputedRef<boolean>|undefined} active - Whether there are active intents.
|
|
39
39
|
* @property {import('vue').ComputedRef<boolean>|undefined} resolving - Whether there are resolving intents.
|
|
40
40
|
* @property {boolean} clearActiveOnResolved - Whether to clear the active state when the promise resolves.
|
|
@@ -44,10 +44,11 @@ export class CancellableIntentError extends Error {
|
|
|
44
44
|
*/
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
* @typedef {import(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
* @typedef {CancellableIntentMyState & import('./error.js').ErrorProperties} CancellableIntentRawState - The raw state of the cancellable intent.
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @typedef {import("vue").Reactive<CancellableIntentRawState>} CancellableIntentState - The state of the cancellable intent.
|
|
51
52
|
*/
|
|
52
53
|
|
|
53
54
|
/**
|
|
@@ -63,7 +64,7 @@ export class CancellableIntentError extends Error {
|
|
|
63
64
|
*/
|
|
64
65
|
|
|
65
66
|
/**
|
|
66
|
-
* @typedef {(runTracking: CommonRunTracking) => import('../utils/cancellablePromise.js').MaybeCancellablePromise<
|
|
67
|
+
* @typedef {(runTracking: CommonRunTracking) => import('../utils/cancellablePromise.js').MaybeCancellablePromise<unknown>} AwaitableWithCancel - A function that returns a promise that can be cancelled. The return value of the promise is not used.
|
|
67
68
|
*/
|
|
68
69
|
|
|
69
70
|
/**
|
|
@@ -95,7 +96,7 @@ export class CancellableIntentError extends Error {
|
|
|
95
96
|
*/
|
|
96
97
|
|
|
97
98
|
/**
|
|
98
|
-
* @typedef {MyCancellableIntent &
|
|
99
|
+
* @typedef {MyCancellableIntent & import('./error.js').ErrorReadOnlyFunctions} CancellableIntent - The instance of the cancellable intent.
|
|
99
100
|
*/
|
|
100
101
|
|
|
101
102
|
/**
|
package/use/error.js
CHANGED
|
@@ -9,15 +9,34 @@ import { readonly, ref } from "vue";
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* @typedef {object} ErrorStatus
|
|
12
|
+
* @typedef {object} ErrorProperties
|
|
15
13
|
* @property {ErrorReadonlyRef} error - The error that occurred.
|
|
16
14
|
* @property {ErroredReadonlyRef} errored - Whether an error has occurred.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @typedef {object} ErrorFunctions
|
|
17
19
|
* @property {(error: Error) => void} setError - Set the error state.
|
|
18
20
|
* @property {ClearErrorFn} clearError - Clear the error state.
|
|
19
21
|
*/
|
|
20
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Proxies can still clear errors but cannot set them directly.
|
|
25
|
+
*
|
|
26
|
+
* @typedef {object} ErrorReadOnlyFunctions
|
|
27
|
+
* @property {ClearErrorFn} clearError - Clear the error state.
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @typedef {ErrorProperties & ErrorReadOnlyFunctions} ReadonlyErrorStatus
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The error state API.
|
|
36
|
+
*
|
|
37
|
+
* @typedef {ErrorProperties & ErrorFunctions} ErrorStatus
|
|
38
|
+
*/
|
|
39
|
+
|
|
21
40
|
/**
|
|
22
41
|
* A composable function for managing error state.
|
|
23
42
|
*
|
package/use/listCalculated.js
CHANGED
|
@@ -35,7 +35,7 @@ import { computed, effectScope, nextTick, reactive, ref, toRef, toRefs, unref, w
|
|
|
35
35
|
* The raw state for a list calculated.
|
|
36
36
|
*
|
|
37
37
|
* @typedef {object} ListCalculatedRawState - The raw state for a list calculated property.
|
|
38
|
-
* @property {{[pk:
|
|
38
|
+
* @property {{[pk: import('../config/commonCrud.js').Pk]: {[rule: string]: import('vue').ComputedRef<any>}}} calculatedObjects - The calculated objects.
|
|
39
39
|
* @property {ListCalculatedRules} calculatedObjectsRules - The rules for the calculated objects.
|
|
40
40
|
* @property {boolean} calculatedObjectsParentStateObjectsWatchRunning - Whether the parent state objects watch is running.
|
|
41
41
|
* @property {boolean} calculatedObjectsWatchRunning - Whether the calculated objects watch is running.
|
package/use/listInstance.js
CHANGED
|
@@ -59,7 +59,7 @@ export class ListInstanceError extends Error {
|
|
|
59
59
|
/**
|
|
60
60
|
* The objects by pk.
|
|
61
61
|
*
|
|
62
|
-
* @typedef {{[pk:
|
|
62
|
+
* @typedef {{[pk: import('../config/commonCrud.js').Pk]: import('../use/objectInstance.js').ExistingCrudObject}} ObjectsByPk
|
|
63
63
|
*/
|
|
64
64
|
|
|
65
65
|
/**
|
|
@@ -71,7 +71,7 @@ export class ListInstanceError extends Error {
|
|
|
71
71
|
/**
|
|
72
72
|
* The order of the objects in the list.
|
|
73
73
|
*
|
|
74
|
-
* @typedef {import('vue').ComputedRef<
|
|
74
|
+
* @typedef {import('vue').ComputedRef<import('../config/commonCrud.js').Pk[]>} ListOrder
|
|
75
75
|
*/
|
|
76
76
|
|
|
77
77
|
/**
|
|
@@ -84,7 +84,7 @@ export class ListInstanceError extends Error {
|
|
|
84
84
|
*/
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
-
* @typedef {Map<
|
|
87
|
+
* @typedef {Map<import('../config/commonCrud.js').Pk, import('vue').Reactive<import('../use/objectInstance.js').ExistingCrudObject>>} ObjectsMap
|
|
88
88
|
*/
|
|
89
89
|
|
|
90
90
|
/**
|
|
@@ -156,13 +156,13 @@ export class ListInstanceError extends Error {
|
|
|
156
156
|
* @property {PushObjectsFn} pushObjects - Customizable callback for handling new objects per page.
|
|
157
157
|
* @property {(object: import('../use/objectInstance.js').ExistingCrudObject) => void} addListObject - Adds an object to the list.
|
|
158
158
|
* @property {(object: import('../use/objectInstance.js').ExistingCrudObject) => void} updateListObject - Updates an object in the list.
|
|
159
|
-
* @property {(objectId:
|
|
159
|
+
* @property {(objectId: import('../config/commonCrud.js').PkInput) => void} deleteListObject - Deletes an object from the list by pk.
|
|
160
160
|
* @property {(options?: ClearListOptions) => void} clearList - Clears the list objects and optionally keeps pagination, totals,
|
|
161
161
|
* or error state.
|
|
162
|
-
* @property {() =>
|
|
163
|
-
* @property {() => import('../utils/cancellablePromise.js').MaybeCancellablePromise<boolean|never>} list - Initiates a fetch to retrieve objects according to the CRUD configuration, returning a promise to a boolean indicating success.
|
|
164
|
-
* @property {(args
|
|
165
|
-
* @property {() => Promise<object|string|
|
|
162
|
+
* @property {() => import('../config/commonCrud.js').Pk} getFakePk - Generates a unique fake pk for use within the list.
|
|
163
|
+
* @property {(args?: import('../config/listCrud.js').AdditionalListArgs) => import('../utils/cancellablePromise.js').MaybeCancellablePromise<boolean|never>} list - Initiates a fetch to retrieve objects according to the CRUD configuration, returning a promise to a boolean indicating success.
|
|
164
|
+
* @property {(args?: {pks?: import('../config/commonCrud.js').Pk[]} & import('../config/listCrud.js').AdditionalListArgs) => Promise<boolean>} bulkDelete - Deletes objects from the list by pk, returning a promise to a boolean indicating success.
|
|
165
|
+
* @property {(args: {action: string, pks?: import('../config/commonCrud.js').Pk[]} & import('../config/listCrud.js').AdditionalListArgs) => Promise<object|string|boolean|null>} executeAction - Initiates an action on all objects in the list, returning the response, or null if the action failed.
|
|
166
166
|
* @property {(info: PaginateInfo) => void} setPaginateInfo - The method to update pagination information.
|
|
167
167
|
* @property {(total: ColumnTotals) => void} setColumnTotals - The method to update column totals.
|
|
168
168
|
*/
|
|
@@ -262,7 +262,7 @@ export function useListInstance({ props, handlers = {} }) {
|
|
|
262
262
|
|
|
263
263
|
const [_objectsProxy, _objectsMapProxy] = es.run(() => {
|
|
264
264
|
// ### do not use this directly, because we proxy `set` to make sure that values are reactive ###
|
|
265
|
-
/** @type {import('vue').Reactive<Map<
|
|
265
|
+
/** @type {import('vue').Reactive<Map<import('../config/commonCrud.js').Pk, import('../use/objectInstance.js').ExistingCrudObject>>} */
|
|
266
266
|
const _objectsMap = shallowReactive(new Map()); // maps are ordered, if you don't clear lists, you need to insert pages in order.
|
|
267
267
|
|
|
268
268
|
// ### this is a proxy to make the map behave like an object for reactivity ###
|
|
@@ -417,8 +417,7 @@ export function useListInstance({ props, handlers = {} }) {
|
|
|
417
417
|
setColumnTotals: (total) => {
|
|
418
418
|
assignReactiveObject(state.columnTotals, total || {});
|
|
419
419
|
},
|
|
420
|
-
list: (args) => {
|
|
421
|
-
const { runId, isCurrentRun } = args || {};
|
|
420
|
+
list: (args = {}) => {
|
|
422
421
|
// this function cannot be async, or the resulting promise will lose its .cancel() method
|
|
423
422
|
if (promises.list) {
|
|
424
423
|
// if a retrieve is already in progress, return the existing promise
|
|
@@ -433,6 +432,7 @@ export function useListInstance({ props, handlers = {} }) {
|
|
|
433
432
|
let listPromise = null;
|
|
434
433
|
try {
|
|
435
434
|
const listCrudArgs = {
|
|
435
|
+
...args,
|
|
436
436
|
target: state.crud.args,
|
|
437
437
|
pkKey: state.pkKey,
|
|
438
438
|
params: state.params,
|
|
@@ -442,10 +442,6 @@ export function useListInstance({ props, handlers = {} }) {
|
|
|
442
442
|
setPaginateInfo: self.setPaginateInfo,
|
|
443
443
|
setColumnTotals: self.setColumnTotals,
|
|
444
444
|
};
|
|
445
|
-
if (runId) {
|
|
446
|
-
listCrudArgs.runId = runId;
|
|
447
|
-
listCrudArgs.isCurrentRun = isCurrentRun;
|
|
448
|
-
}
|
|
449
445
|
listPromise = state.crud.list(listCrudArgs);
|
|
450
446
|
} catch (e) {
|
|
451
447
|
loadingError.setError(e);
|
|
@@ -475,7 +471,7 @@ export function useListInstance({ props, handlers = {} }) {
|
|
|
475
471
|
);
|
|
476
472
|
return promises.list;
|
|
477
473
|
},
|
|
478
|
-
bulkDelete: ({ pks } = {}) => {
|
|
474
|
+
bulkDelete: ({ pks, ...additionalArgs } = {}) => {
|
|
479
475
|
if (state.loading) {
|
|
480
476
|
return Promise.reject(new ListInstanceError("already loading.", "already-loading"));
|
|
481
477
|
}
|
|
@@ -486,6 +482,7 @@ export function useListInstance({ props, handlers = {} }) {
|
|
|
486
482
|
loadingError.clearError();
|
|
487
483
|
return state.crud
|
|
488
484
|
.bulkDelete({
|
|
485
|
+
...additionalArgs,
|
|
489
486
|
target: state.crud.args,
|
|
490
487
|
pks,
|
|
491
488
|
pkKey: state.pkKey,
|
|
@@ -503,7 +500,7 @@ export function useListInstance({ props, handlers = {} }) {
|
|
|
503
500
|
loadingError.clearLoading();
|
|
504
501
|
});
|
|
505
502
|
},
|
|
506
|
-
executeAction: ({ pks, action }) => {
|
|
503
|
+
executeAction: ({ pks, action, ...additionalArgs }) => {
|
|
507
504
|
if (state.loading) {
|
|
508
505
|
return Promise.reject(new ListInstanceError("already loading.", "already-loading"));
|
|
509
506
|
}
|
|
@@ -514,8 +511,9 @@ export function useListInstance({ props, handlers = {} }) {
|
|
|
514
511
|
loadingError.clearError();
|
|
515
512
|
return state.crud
|
|
516
513
|
.executeAction({
|
|
517
|
-
|
|
514
|
+
...additionalArgs,
|
|
518
515
|
target: state.crud.args,
|
|
516
|
+
action,
|
|
519
517
|
pks,
|
|
520
518
|
pkKey: state.pkKey,
|
|
521
519
|
})
|
|
@@ -532,36 +530,39 @@ export function useListInstance({ props, handlers = {} }) {
|
|
|
532
530
|
});
|
|
533
531
|
},
|
|
534
532
|
addListObject: (object) => {
|
|
533
|
+
const pk = String(object[state.pkKey]);
|
|
535
534
|
if (!object[state.pkKey]) {
|
|
536
535
|
throw new ListInstanceError(
|
|
537
536
|
`addListObject: object missing pk(${state.pkKey}).\n${inspect(object)}`,
|
|
538
537
|
"missing-pk"
|
|
539
538
|
);
|
|
540
539
|
}
|
|
541
|
-
if (
|
|
540
|
+
if (pk in state.objects) {
|
|
542
541
|
throw new ListInstanceError(
|
|
543
|
-
`addListObject: list already has object for pk(${state.pkKey}): ${inspect(
|
|
542
|
+
`addListObject: list already has object for pk(${state.pkKey}): ${inspect(pk)}`,
|
|
544
543
|
"duplicate-pk"
|
|
545
544
|
);
|
|
546
545
|
}
|
|
547
|
-
state.objects[
|
|
546
|
+
state.objects[pk] = object;
|
|
548
547
|
},
|
|
549
548
|
updateListObject: (object) => {
|
|
549
|
+
const pk = String(object[state.pkKey]);
|
|
550
550
|
if (!object[state.pkKey]) {
|
|
551
551
|
throw new ListInstanceError(
|
|
552
552
|
`updateListObject: object missing pk(${state.pkKey}).\n${inspect(object)}`,
|
|
553
553
|
"missing-pk"
|
|
554
554
|
);
|
|
555
555
|
}
|
|
556
|
-
if (!(
|
|
556
|
+
if (!(pk in state.objects)) {
|
|
557
557
|
throw new ListInstanceError(
|
|
558
|
-
`updateListObject: list missing object for update by pk(${state.pkKey}): ${inspect(
|
|
558
|
+
`updateListObject: list missing object for update by pk(${state.pkKey}): ${inspect(pk)}`,
|
|
559
559
|
"missing-object"
|
|
560
560
|
);
|
|
561
561
|
}
|
|
562
|
-
assignReactiveObject(state.objects[
|
|
562
|
+
assignReactiveObject(state.objects[pk], object);
|
|
563
563
|
},
|
|
564
|
-
deleteListObject: (
|
|
564
|
+
deleteListObject: (pkInput) => {
|
|
565
|
+
const pk = String(pkInput);
|
|
565
566
|
if (!(pk in state.objects)) {
|
|
566
567
|
throw new ListInstanceError(
|
|
567
568
|
`deleteListObject: list missing object for removal by pk(${state.pkKey}): ${inspect(pk)}`,
|
|
@@ -588,7 +589,8 @@ export function useListInstance({ props, handlers = {} }) {
|
|
|
588
589
|
getFakePk: () => getFakePk(state.objects, state.pkKey),
|
|
589
590
|
pushObjects: (newObjects) => {
|
|
590
591
|
newObjects.forEach((newObject) => {
|
|
591
|
-
|
|
592
|
+
const pk = String(newObject[state.pkKey]);
|
|
593
|
+
if (pk in state.objects) {
|
|
592
594
|
self.updateListObject.call(this, newObject);
|
|
593
595
|
} else {
|
|
594
596
|
self.addListObject.call(this, newObject);
|
package/use/listRelated.js
CHANGED
|
@@ -41,18 +41,18 @@ import { computed, effectScope, nextTick, onScopeDispose, reactive, ref, toRef,
|
|
|
41
41
|
*
|
|
42
42
|
* @typedef {object} ListRelatedRawState
|
|
43
43
|
* @property {{
|
|
44
|
-
* [pk:
|
|
44
|
+
* [pk: import('../config/commonCrud.js').Pk]: {
|
|
45
45
|
* [rule: string]: import('vue').ComputedRef<any>,
|
|
46
46
|
* },
|
|
47
47
|
* }} relatedObjects - Stores computed references to related objects, allowing for dynamic access based on object pk and specific rules.
|
|
48
48
|
* @property {ListRelatedRules} relatedObjectsRules - Defines the rules for establishing relationships, such as foreign key links and sorting orders.
|
|
49
49
|
* @property {{
|
|
50
|
-
* [pk:
|
|
50
|
+
* [pk: import('../config/commonCrud.js').Pk]: {
|
|
51
51
|
* [rule: string]: import('vue').ComputedRef<[object, string]>,
|
|
52
52
|
* },
|
|
53
53
|
* }} objAndKeyForPkAndRule - Maps each object pk and rule to a tuple consisting of the related object and its respective key, facilitating direct data manipulation.
|
|
54
54
|
* @property {{
|
|
55
|
-
* [pk:
|
|
55
|
+
* [pk: import('../config/commonCrud.js').Pk]: {
|
|
56
56
|
* [rule: string]: import('vue').ComputedRef<any>,
|
|
57
57
|
* },
|
|
58
58
|
* }} fkForPkAndRule - Maintains computed references to the foreign keys for each object pk and rule, crucial for navigating complex data relationships.
|
package/use/listSubscription.js
CHANGED
|
@@ -5,8 +5,8 @@ import inspect from "browser-util-inspect";
|
|
|
5
5
|
import cloneDeep from "lodash-es/cloneDeep.js";
|
|
6
6
|
import isEmpty from "lodash-es/isEmpty.js";
|
|
7
7
|
import isObject from "lodash-es/isObject.js";
|
|
8
|
-
import { reactive, toRef, toRefs } from "vue";
|
|
9
|
-
import { useProxyLoadingError } from "./proxyLoadingError.js";
|
|
8
|
+
import { reactive, readonly, ref, toRef, toRefs } from "vue";
|
|
9
|
+
import { asWatchableLoadingError, useProxyLoadingError } from "./proxyLoadingError.js";
|
|
10
10
|
import { refIfReactive } from "../utils/refIfReactive.js";
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -89,12 +89,16 @@ export class ListSubscriptionError extends Error {
|
|
|
89
89
|
* @typedef {ListSubscriptionFunctions & ListSubscriptionProperties} ListSubscription
|
|
90
90
|
*/
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
* @typedef {object} ListSubscriptionOwnOptions
|
|
94
|
+
* @property {import("./listInstance.js").ListInstance} [listInstance] - A list instance to use instead of creating one.
|
|
95
|
+
*/
|
|
96
|
+
|
|
92
97
|
/**
|
|
93
98
|
* Defines the settings required to establish a list subscription, detailing how list instances should handle updates
|
|
94
99
|
* and subscriptions based on the given properties.
|
|
95
100
|
*
|
|
96
|
-
* @typedef {
|
|
97
|
-
* @property {import("./listInstance.js").ListInstance} listInstance - A list instance to use instead of creating one.
|
|
101
|
+
* @typedef {import("./listInstance.js").ListInstanceOptions & ListSubscriptionOwnOptions} ListSubscriptionOptions
|
|
98
102
|
*/
|
|
99
103
|
|
|
100
104
|
/**
|
|
@@ -182,7 +186,7 @@ export function useListSubscription({ listInstance, props, handlers }) {
|
|
|
182
186
|
const parentState = listInstance.state;
|
|
183
187
|
|
|
184
188
|
const loadingError = useLoadingError();
|
|
185
|
-
const proxyLoadingError = useProxyLoadingError([loadingError,
|
|
189
|
+
const proxyLoadingError = useProxyLoadingError([loadingError, asWatchableLoadingError(listInstance)]);
|
|
186
190
|
// stand alone in order to avoid circular dependency between state & intents.
|
|
187
191
|
const intendToList = refIfReactive(props, "intendToList", false);
|
|
188
192
|
const intendToSubscribe = refIfReactive(props, "intendToSubscribe", false);
|
|
@@ -208,95 +212,109 @@ export function useListSubscription({ listInstance, props, handlers }) {
|
|
|
208
212
|
const subscribeIntent = useCancellableIntent({
|
|
209
213
|
awaitableWithCancel: ({ runId, isCurrentRun }) => {
|
|
210
214
|
// this function cannot be async, or the resulting promise will lose its .cancel() method
|
|
215
|
+
const isCancelled = ref(false);
|
|
211
216
|
loadingError.setLoading();
|
|
212
217
|
let subscribePromise, catchPromise;
|
|
213
218
|
try {
|
|
214
|
-
subscribePromise =
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
219
|
+
subscribePromise = /** @type {import('../utils/cancellablePromise.js').CancellablePromise<void>} */ (
|
|
220
|
+
parentState.crud.subscribe({
|
|
221
|
+
runId,
|
|
222
|
+
isCurrentRun,
|
|
223
|
+
target: cloneDeep(parentState.crud.args),
|
|
224
|
+
pkKey: parentState.pkKey,
|
|
225
|
+
params: cloneDeep(parentState.params),
|
|
226
|
+
applyObjectEvent: (
|
|
227
|
+
/** @type {import('./objectInstance.js').ExistingCrudObject} */ data,
|
|
228
|
+
/** @type {"create"|"update"|"delete"} */ action
|
|
229
|
+
) => {
|
|
230
|
+
if (!data || (isObject(data) && isEmpty(data))) {
|
|
231
|
+
throw new ListSubscriptionError(
|
|
232
|
+
`got update with no data (${inspect(data)}), action: ${action}`,
|
|
233
|
+
"empty-data"
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
switch (action) {
|
|
237
|
+
case "delete":
|
|
238
|
+
try {
|
|
239
|
+
const pkValue = isObject(data) ? data[parentState.pkKey] : data;
|
|
240
|
+
listInstance.deleteListObject(pkValue);
|
|
241
|
+
} catch (err) {
|
|
242
|
+
if (err.name === "ListInstanceError" && err.code === "missing-object") {
|
|
243
|
+
console.warn(
|
|
244
|
+
`deleteFromSubscription: delete for pk(${parentState.pkKey}) not in objects (${inspect(data)}).`
|
|
245
|
+
);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
throw err;
|
|
249
|
+
}
|
|
250
|
+
break;
|
|
251
|
+
case "create":
|
|
252
|
+
if (!data[parentState.pkKey]) {
|
|
253
|
+
throw new ListSubscriptionError(
|
|
254
|
+
`addFromSubscription: data missing pk(${parentState.pkKey}).\n${inspect(data)}`,
|
|
255
|
+
"missing-pk"
|
|
238
256
|
);
|
|
239
|
-
return;
|
|
240
257
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
if (
|
|
255
|
-
|
|
256
|
-
`
|
|
258
|
+
try {
|
|
259
|
+
listInstance.addListObject(data);
|
|
260
|
+
} catch (err) {
|
|
261
|
+
if (err.name === "ListInstanceError" && err.code === "duplicate-pk") {
|
|
262
|
+
console.warn(
|
|
263
|
+
`addFromSubscription: add for pk(${parentState.pkKey}) already in objects (${data[parentState.pkKey]}).`
|
|
264
|
+
);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
throw err;
|
|
268
|
+
}
|
|
269
|
+
break;
|
|
270
|
+
case "update":
|
|
271
|
+
if (!data[parentState.pkKey]) {
|
|
272
|
+
throw new ListSubscriptionError(
|
|
273
|
+
`updateFromSubscription: data missing pk(${parentState.pkKey}).\n${inspect(data)}`,
|
|
274
|
+
"missing-pk"
|
|
257
275
|
);
|
|
258
|
-
return;
|
|
259
276
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
277
|
+
try {
|
|
278
|
+
listInstance.updateListObject(data);
|
|
279
|
+
} catch (err) {
|
|
280
|
+
if (err.name === "ListInstanceError" && err.code === "missing-object") {
|
|
281
|
+
console.warn(
|
|
282
|
+
`updateFromSubscription: update for pk(${parentState.pkKey}) not in objects (${data[parentState.pkKey]}).`
|
|
283
|
+
);
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
throw err;
|
|
287
|
+
}
|
|
288
|
+
break;
|
|
289
|
+
default:
|
|
265
290
|
throw new ListSubscriptionError(
|
|
266
|
-
`
|
|
267
|
-
"
|
|
291
|
+
`got update for unknown action: ${action}\n${inspect(data)}`,
|
|
292
|
+
"unknown-action"
|
|
268
293
|
);
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
} catch (err) {
|
|
273
|
-
if (err.name === "ListInstanceError" && err.code === "missing-object") {
|
|
274
|
-
console.warn(
|
|
275
|
-
`updateFromSubscription: update for pk(${listInstance.state.pkKey}) not in objects (${data[listInstance.state.pkKey]}).`
|
|
276
|
-
);
|
|
277
|
-
return;
|
|
278
|
-
}
|
|
279
|
-
throw err;
|
|
280
|
-
}
|
|
281
|
-
break;
|
|
282
|
-
default:
|
|
283
|
-
throw new ListSubscriptionError(
|
|
284
|
-
`got update for unknown action: ${action}\n${inspect(data)}`,
|
|
285
|
-
"unknown-action"
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
},
|
|
289
|
-
});
|
|
290
|
-
// catching makes a new promise, we need to make sure the cancel method lives on.
|
|
291
|
-
catchPromise = subscribePromise
|
|
292
|
-
.catch((/** @type {Error} */ err) => {
|
|
293
|
-
console.error(err);
|
|
294
|
-
loadingError.setError(err);
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
isCancelled: readonly(isCancelled),
|
|
295
297
|
})
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
298
|
+
);
|
|
299
|
+
// catching makes a new promise, we need to make sure the cancel method lives on.
|
|
300
|
+
catchPromise = /** @type {import('../utils/cancellablePromise.js').CancellablePromise<void>} */ (
|
|
301
|
+
subscribePromise
|
|
302
|
+
.catch((/** @type {Error} */ err) => {
|
|
303
|
+
console.error(err);
|
|
304
|
+
loadingError.setError(err);
|
|
305
|
+
})
|
|
306
|
+
.finally(() => {
|
|
307
|
+
loadingError.clearLoading();
|
|
308
|
+
})
|
|
309
|
+
);
|
|
310
|
+
if (subscribePromise.cancel) {
|
|
311
|
+
const originalCancel = subscribePromise.cancel.bind(subscribePromise);
|
|
312
|
+
const cancelWithFlag = async (/** @type {any} */ reason) => {
|
|
313
|
+
isCancelled.value = true;
|
|
314
|
+
return originalCancel(reason);
|
|
315
|
+
};
|
|
316
|
+
catchPromise.cancel = cancelWithFlag;
|
|
317
|
+
}
|
|
300
318
|
return catchPromise;
|
|
301
319
|
} catch (err) {
|
|
302
320
|
console.error(err);
|