@arrai-innovations/reactive-helpers 18.0.2 → 18.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/config/listCrud.js +1 -1
- package/config/objectCrud.js +129 -47
- package/index.js +3 -0
- package/package.json +2 -2
- package/types/config/listCrud.d.ts +1 -1
- package/types/config/objectCrud.d.ts +99 -33
- package/types/config/objectCrud.d.ts.map +1 -1
- package/types/index.d.ts +3 -0
- package/types/use/cancellableIntent.d.ts +2 -13
- package/types/use/cancellableIntent.d.ts.map +1 -1
- package/types/use/listInstance.d.ts +2 -2
- package/types/use/listInstance.d.ts.map +1 -1
- package/types/use/loadingError.d.ts +30 -6
- package/types/use/loadingError.d.ts.map +1 -1
- package/types/use/objectInstance.d.ts +83 -31
- package/types/use/objectInstance.d.ts.map +1 -1
- package/types/use/objectSubscription.d.ts +33 -29
- package/types/use/objectSubscription.d.ts.map +1 -1
- package/types/use/proxyLoadingError.d.ts +8 -23
- package/types/use/proxyLoadingError.d.ts.map +1 -1
- package/types/utils/cancellableFetch.d.ts +12 -0
- package/types/utils/cancellableFetch.d.ts.map +1 -0
- package/types/utils/cancellablePromise.d.ts +40 -0
- package/types/utils/cancellablePromise.d.ts.map +1 -0
- package/types/utils/deepUnref.d.ts +26 -0
- package/types/utils/deepUnref.d.ts.map +1 -0
- package/use/cancellableIntent.js +8 -13
- package/use/listInstance.js +39 -40
- package/use/listSearch.js +1 -1
- package/use/loadingError.js +30 -6
- package/use/objectInstance.js +225 -125
- package/use/objectSubscription.js +81 -82
- package/use/proxyLoadingError.js +14 -27
- package/use/search.js +1 -1
- package/utils/cancellableFetch.js +22 -0
- package/utils/cancellablePromise.js +70 -0
- package/utils/classes.js +5 -5
- package/utils/deepUnref.js +28 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deepUnref.d.ts","sourceRoot":"","sources":["../../utils/deepUnref.js"],"names":[],"mappings":"AAIA;;;;;;;;;;;;GAYG;AAIH;;;;;;GAMG;AACH,4BAAoC;;;;8BAnBvB,GAAC,SAAS,OAAO,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,GAC1C,UAAU,CAAC,CAAC,CAAC,GACb,GAAC,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,GACtB,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GACpB,GAAC,SAAS,MAAM,GACd,GAAG,CAAC,IAAI,MAAM,GAAC,GAAG,UAAU,CAAC,GAAC,CAAC,CAAC,CAAC,CAAC,GAAE,GACpC,GAAC"}
|
package/use/cancellableIntent.js
CHANGED
|
@@ -2,19 +2,13 @@ import identity from "lodash-es/identity.js";
|
|
|
2
2
|
import isEmpty from "lodash-es/isEmpty.js";
|
|
3
3
|
import isEqual from "lodash-es/isEqual.js";
|
|
4
4
|
import { computed, effectScope, nextTick, onScopeDispose, reactive, readonly, watch } from "vue";
|
|
5
|
-
import { deepUnref } from "
|
|
5
|
+
import { deepUnref } from "../utils/deepUnref.js";
|
|
6
6
|
import { tryOnActivated, tryOnDeactivated } from "../utils/keepAliveTry.js";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @module use/cancellableIntent.js - A composable function for handling cancellable intents.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
* A Promise that can be cancelled.
|
|
14
|
-
*
|
|
15
|
-
* @typedef {Promise<any> & { cancel: () => Promise<void>|void }} CancellablePromise
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
12
|
/**
|
|
19
13
|
* @typedef {import("vue").UnwrapNestedRefs<object>} CancellableIntentState - The state of the cancellable intent.
|
|
20
14
|
* @property {number} activeCount - The number of active intents.
|
|
@@ -28,7 +22,7 @@ import { tryOnActivated, tryOnDeactivated } from "../utils/keepAliveTry.js";
|
|
|
28
22
|
|
|
29
23
|
/**
|
|
30
24
|
* @typedef {object} CancellableIntentOptions - The options for the cancellable intent.
|
|
31
|
-
* @property {() =>
|
|
25
|
+
* @property {() => import('../utils/cancellablePromise.js').MaybeCancellablePromise<void>} awaitableWithCancel - The function that returns a promise that can be cancelled.
|
|
32
26
|
* @property {import("vue").UnwrapNestedRefs<object>} [watchArguments={}] - The reactive object to watch for changes.
|
|
33
27
|
* @property {import("vue").UnwrapNestedRefs<object>} [guardArguments={}] - The reactive object to watch for truthiness before running the intent.
|
|
34
28
|
* @property {boolean} [clearActiveOnResolved=true] - Whether to clear the active state when the promise resolves.
|
|
@@ -112,9 +106,9 @@ export function useCancellableIntent({
|
|
|
112
106
|
es.stop();
|
|
113
107
|
}
|
|
114
108
|
|
|
115
|
-
async function cancel() {
|
|
109
|
+
async function cancel(reason) {
|
|
116
110
|
if (cancelFunction) {
|
|
117
|
-
return cancelFunction()
|
|
111
|
+
return cancelFunction(reason)
|
|
118
112
|
.catch(console.error)
|
|
119
113
|
.finally(() => {
|
|
120
114
|
if (!state.clearActiveOnResolved) {
|
|
@@ -145,7 +139,7 @@ export function useCancellableIntent({
|
|
|
145
139
|
// we don't want to await this, because we want to be able to cancel it
|
|
146
140
|
awaitablePromise
|
|
147
141
|
.catch(async (err) => {
|
|
148
|
-
await cancel();
|
|
142
|
+
await cancel("Error in awaitableWithCancel");
|
|
149
143
|
console.error(err);
|
|
150
144
|
state.errored = true;
|
|
151
145
|
state.error = err;
|
|
@@ -174,7 +168,7 @@ export function useCancellableIntent({
|
|
|
174
168
|
if (isEqual(newWatchValues, previousWatchValues)) {
|
|
175
169
|
return;
|
|
176
170
|
}
|
|
177
|
-
await cancel(); // this can take time so guards and watches can change!
|
|
171
|
+
await cancel("Intent watch cancelled"); // this can take time so guards and watches can change!
|
|
178
172
|
newWatchValues = deepUnref(Object.values(watchArguments));
|
|
179
173
|
if (isEqual(newWatchValues, previousWatchValues)) {
|
|
180
174
|
return;
|
|
@@ -212,7 +206,8 @@ export function useCancellableIntent({
|
|
|
212
206
|
|
|
213
207
|
const cleanup = () => {
|
|
214
208
|
// cancel the intent when the component is deactivated
|
|
215
|
-
|
|
209
|
+
// noinspection JSIgnoredPromiseFromCall
|
|
210
|
+
cancel("Component deactivated");
|
|
216
211
|
if (state.clearActiveOnResolved) {
|
|
217
212
|
// otherwise it clears when resolved
|
|
218
213
|
cancelFunction = null;
|
package/use/listInstance.js
CHANGED
|
@@ -4,6 +4,7 @@ import { getFakePk } from "../utils/getFakePk.js";
|
|
|
4
4
|
import { useLoadingError } from "./loadingError.js";
|
|
5
5
|
import inspect from "browser-util-inspect";
|
|
6
6
|
import { computed, effectScope, nextTick, reactive, readonly, ref, toRef, unref, watch } from "vue";
|
|
7
|
+
import { CancellablePromise, wrapMaybeCancellable } from "../utils/cancellablePromise.js";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* A composable function for managing a list of objects.
|
|
@@ -117,7 +118,7 @@ export class ListInstanceError extends Error {
|
|
|
117
118
|
* @property {(objectId: string) => void} deleteListObject - Deletes an object from the list by pk.
|
|
118
119
|
* @property {() => void} clearList - Clears all objects and errors from the list.
|
|
119
120
|
* @property {() => string} getFakePk - Generates a unique fake pk for use within the list.
|
|
120
|
-
* @property {() =>
|
|
121
|
+
* @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.
|
|
121
122
|
* @property {() => Promise<boolean>} bulkDelete - Initiates a bulk delete operation on all objects in the list, returning a promise to a boolean indicating success.
|
|
122
123
|
* @property {() => Promise<object|string|false>} executeAction - Initiates an action on all objects in the list, returning the response, or false if the action failed.
|
|
123
124
|
*/
|
|
@@ -286,7 +287,7 @@ export function useListInstance({ props, functions = {}, keepOldPages }) {
|
|
|
286
287
|
|
|
287
288
|
getListCrud(state.crud, { props, functions });
|
|
288
289
|
|
|
289
|
-
const defaultPageCallback = (newObjects) => {
|
|
290
|
+
const defaultPageCallback = (/** @type {ListObject[]} */ newObjects) => {
|
|
290
291
|
// with keepOldPages, you are responsible for clearing the list as needed
|
|
291
292
|
if (!keepOldPages) {
|
|
292
293
|
// display one page at a time, clear the list
|
|
@@ -301,7 +302,7 @@ export function useListInstance({ props, functions = {}, keepOldPages }) {
|
|
|
301
302
|
});
|
|
302
303
|
};
|
|
303
304
|
|
|
304
|
-
/** @type {{[key: string]: import('
|
|
305
|
+
/** @type {{[key: string]: import('../utils/cancellablePromise.js').MaybeCancellablePromise<boolean>|null}} */
|
|
305
306
|
const promises = {
|
|
306
307
|
list: null,
|
|
307
308
|
};
|
|
@@ -323,7 +324,7 @@ export function useListInstance({ props, functions = {}, keepOldPages }) {
|
|
|
323
324
|
loadingError.clearError();
|
|
324
325
|
return Promise.resolve(true);
|
|
325
326
|
})
|
|
326
|
-
.catch((error) => {
|
|
327
|
+
.catch((/** @type {Error} */ error) => {
|
|
327
328
|
loadingError.setError(error);
|
|
328
329
|
return Promise.resolve(false);
|
|
329
330
|
})
|
|
@@ -344,11 +345,11 @@ export function useListInstance({ props, functions = {}, keepOldPages }) {
|
|
|
344
345
|
pks: Object.keys(state.objects).map(Number),
|
|
345
346
|
pkKey: state.pkKey,
|
|
346
347
|
})
|
|
347
|
-
.then((responseData) => {
|
|
348
|
+
.then((/** @type {any} */ responseData) => {
|
|
348
349
|
loadingError.clearError();
|
|
349
350
|
return Promise.resolve(responseData);
|
|
350
351
|
})
|
|
351
|
-
.catch((error) => {
|
|
352
|
+
.catch((/** @type {Error} */ error) => {
|
|
352
353
|
loadingError.setError(error);
|
|
353
354
|
return Promise.resolve(false);
|
|
354
355
|
})
|
|
@@ -357,6 +358,12 @@ export function useListInstance({ props, functions = {}, keepOldPages }) {
|
|
|
357
358
|
});
|
|
358
359
|
}
|
|
359
360
|
|
|
361
|
+
/**
|
|
362
|
+
* Fetches a list of objects from the server, using the configured CRUD operations and arguments.
|
|
363
|
+
*
|
|
364
|
+
* @throws {ListInstanceError} If the list is already loading.
|
|
365
|
+
* @returns {import('../utils/cancellablePromise.js').MaybeCancellablePromise<boolean|never>} A promise that resolves to true if the list was successfully retrieved, or false if an error occurred.
|
|
366
|
+
*/
|
|
360
367
|
function list() {
|
|
361
368
|
// this function cannot be async, or the resulting promise will lose its .cancel() method
|
|
362
369
|
if (promises.list) {
|
|
@@ -364,13 +371,8 @@ export function useListInstance({ props, functions = {}, keepOldPages }) {
|
|
|
364
371
|
return promises.list;
|
|
365
372
|
}
|
|
366
373
|
if (state.loading) {
|
|
367
|
-
return
|
|
374
|
+
return CancellablePromise.reject(new ListInstanceError("already loading.", "already-loading"));
|
|
368
375
|
}
|
|
369
|
-
let returnPromiseResolve;
|
|
370
|
-
// @ts-ignore - we are setting this in the promise
|
|
371
|
-
promises.list = /** @type {import('./cancellableIntent.js').CancellablePromise} */ new Promise(
|
|
372
|
-
(resolve) => (returnPromiseResolve = resolve)
|
|
373
|
-
);
|
|
374
376
|
loadingError.clearError();
|
|
375
377
|
loadingError.setLoading();
|
|
376
378
|
const isCancelled = ref(false);
|
|
@@ -382,35 +384,31 @@ export function useListInstance({ props, functions = {}, keepOldPages }) {
|
|
|
382
384
|
pageCallback: returnedObject.pageCallback,
|
|
383
385
|
isCancelled: readonly(isCancelled),
|
|
384
386
|
});
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
loadingError.clearLoading();
|
|
407
|
-
returnPromiseResolve(resolveState);
|
|
408
|
-
promises.list = null;
|
|
409
|
-
});
|
|
387
|
+
promises.list = wrapMaybeCancellable(
|
|
388
|
+
listPromise
|
|
389
|
+
.then(() => {
|
|
390
|
+
return true;
|
|
391
|
+
})
|
|
392
|
+
.catch((/** @type {Error} */ error) => {
|
|
393
|
+
loadingError.setError(error);
|
|
394
|
+
return false;
|
|
395
|
+
})
|
|
396
|
+
.finally(() => {
|
|
397
|
+
loadingError.clearLoading();
|
|
398
|
+
promises.list = null;
|
|
399
|
+
}),
|
|
400
|
+
listPromise.cancel
|
|
401
|
+
? async (/** @type {any} */ reason) => {
|
|
402
|
+
isCancelled.value = true;
|
|
403
|
+
await listPromise.cancel?.(reason);
|
|
404
|
+
loadingError.clearLoading();
|
|
405
|
+
}
|
|
406
|
+
: undefined
|
|
407
|
+
);
|
|
410
408
|
return promises.list;
|
|
411
409
|
}
|
|
412
410
|
|
|
413
|
-
function addListObject(object) {
|
|
411
|
+
function addListObject(/** @type {ListObject} */ object) {
|
|
414
412
|
if (!object[state.pkKey]) {
|
|
415
413
|
throw new ListInstanceError(
|
|
416
414
|
`addListObject: object missing pk(${state.pkKey}).\n${inspect(object)}`,
|
|
@@ -429,7 +427,7 @@ export function useListInstance({ props, functions = {}, keepOldPages }) {
|
|
|
429
427
|
}
|
|
430
428
|
}
|
|
431
429
|
|
|
432
|
-
function updateListObject(object) {
|
|
430
|
+
function updateListObject(/** @type {ListObject} */ object) {
|
|
433
431
|
if (!object[state.pkKey]) {
|
|
434
432
|
throw new ListInstanceError(
|
|
435
433
|
`updateListObject: object missing pk(${state.pkKey}).\n${inspect(object)}`,
|
|
@@ -448,7 +446,7 @@ export function useListInstance({ props, functions = {}, keepOldPages }) {
|
|
|
448
446
|
}
|
|
449
447
|
}
|
|
450
448
|
|
|
451
|
-
function deleteListObject(pk) {
|
|
449
|
+
function deleteListObject(/** @type {string} */ pk) {
|
|
452
450
|
if (!(pk in state.objects)) {
|
|
453
451
|
throw new ListInstanceError(
|
|
454
452
|
`deleteListObject: list missing object for removal by pk(${state.pkKey}): ${inspect(pk)}`,
|
|
@@ -479,6 +477,7 @@ export function useListInstance({ props, functions = {}, keepOldPages }) {
|
|
|
479
477
|
toRef(state, "objects"),
|
|
480
478
|
() => {
|
|
481
479
|
objectsInOrderRefs.value = Object.values(state.objects).map((object) => ref(object));
|
|
480
|
+
// noinspection JSIgnoredPromiseFromCall
|
|
482
481
|
nextTick(() => {
|
|
483
482
|
if (state.running) {
|
|
484
483
|
state.running = false;
|
package/use/listSearch.js
CHANGED
|
@@ -15,7 +15,7 @@ import { useSearch } from "./search.js";
|
|
|
15
15
|
import get from "lodash-es/get.js";
|
|
16
16
|
import isEqual from "lodash-es/isEqual.js";
|
|
17
17
|
import { computed, effectScope, onScopeDispose, reactive, readonly, ref, toRef, unref, watch } from "vue";
|
|
18
|
-
import { deepUnref } from "
|
|
18
|
+
import { deepUnref } from "../utils/deepUnref.js";
|
|
19
19
|
import { assignReactiveObject } from "../utils/assignReactiveObject.js";
|
|
20
20
|
import { loadingCombine } from "../utils/loadingCombine.js";
|
|
21
21
|
|
package/use/loadingError.js
CHANGED
|
@@ -8,13 +8,37 @@ import { readonly, ref } from "vue";
|
|
|
8
8
|
* @typedef {() => void} ClearErrorFn - Clear the error state.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {import("vue").Ref<boolean|undefined>} LoadingRef
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {import("vue").Ref<Error|null>} ErrorRef
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @typedef {import("vue").Ref<boolean>} ErroredRef
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {Readonly<LoadingRef>} LoadingReadonlyRef
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @typedef {Readonly<ErrorRef>} ErrorReadonlyRef
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {Readonly<ErroredRef>} ErroredReadonlyRef
|
|
33
|
+
*/
|
|
34
|
+
|
|
11
35
|
/**
|
|
12
36
|
* The common API for loading and error states.
|
|
13
37
|
*
|
|
14
38
|
* @typedef {object} LoadingErrorStatus
|
|
15
|
-
* @property {
|
|
16
|
-
* @property {
|
|
17
|
-
* @property {
|
|
39
|
+
* @property {LoadingReadonlyRef} loading - Whether the component is loading.
|
|
40
|
+
* @property {ErrorReadonlyRef} error - The error that occurred.
|
|
41
|
+
* @property {ErroredReadonlyRef} errored - Whether an error has occurred.
|
|
18
42
|
* @property {ClearErrorFn} clearError - Clear the error state.
|
|
19
43
|
*/
|
|
20
44
|
|
|
@@ -39,11 +63,11 @@ import { readonly, ref } from "vue";
|
|
|
39
63
|
* @returns {LoadingError} - An object containing reactive fields and actions for loading and error states.
|
|
40
64
|
*/
|
|
41
65
|
export function useLoadingError() {
|
|
42
|
-
/** @type {
|
|
66
|
+
/** @type {LoadingRef} */
|
|
43
67
|
const loading = ref(undefined);
|
|
44
|
-
/** @type {
|
|
68
|
+
/** @type {ErrorRef} */
|
|
45
69
|
const error = ref(null);
|
|
46
|
-
/** @type {
|
|
70
|
+
/** @type {ErroredRef} */
|
|
47
71
|
const errored = ref(false);
|
|
48
72
|
|
|
49
73
|
return {
|