@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.
Files changed (38) hide show
  1. package/config/listCrud.js +1 -1
  2. package/config/objectCrud.js +129 -47
  3. package/index.js +3 -0
  4. package/package.json +2 -2
  5. package/types/config/listCrud.d.ts +1 -1
  6. package/types/config/objectCrud.d.ts +99 -33
  7. package/types/config/objectCrud.d.ts.map +1 -1
  8. package/types/index.d.ts +3 -0
  9. package/types/use/cancellableIntent.d.ts +2 -13
  10. package/types/use/cancellableIntent.d.ts.map +1 -1
  11. package/types/use/listInstance.d.ts +2 -2
  12. package/types/use/listInstance.d.ts.map +1 -1
  13. package/types/use/loadingError.d.ts +30 -6
  14. package/types/use/loadingError.d.ts.map +1 -1
  15. package/types/use/objectInstance.d.ts +83 -31
  16. package/types/use/objectInstance.d.ts.map +1 -1
  17. package/types/use/objectSubscription.d.ts +33 -29
  18. package/types/use/objectSubscription.d.ts.map +1 -1
  19. package/types/use/proxyLoadingError.d.ts +8 -23
  20. package/types/use/proxyLoadingError.d.ts.map +1 -1
  21. package/types/utils/cancellableFetch.d.ts +12 -0
  22. package/types/utils/cancellableFetch.d.ts.map +1 -0
  23. package/types/utils/cancellablePromise.d.ts +40 -0
  24. package/types/utils/cancellablePromise.d.ts.map +1 -0
  25. package/types/utils/deepUnref.d.ts +26 -0
  26. package/types/utils/deepUnref.d.ts.map +1 -0
  27. package/use/cancellableIntent.js +8 -13
  28. package/use/listInstance.js +39 -40
  29. package/use/listSearch.js +1 -1
  30. package/use/loadingError.js +30 -6
  31. package/use/objectInstance.js +225 -125
  32. package/use/objectSubscription.js +81 -82
  33. package/use/proxyLoadingError.js +14 -27
  34. package/use/search.js +1 -1
  35. package/utils/cancellableFetch.js +22 -0
  36. package/utils/cancellablePromise.js +70 -0
  37. package/utils/classes.js +5 -5
  38. 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"}
@@ -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 "vue-deepunref";
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 {() => CancellablePromise} awaitableWithCancel - The function that returns a promise that can be cancelled.
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
- cancel();
209
+ // noinspection JSIgnoredPromiseFromCall
210
+ cancel("Component deactivated");
216
211
  if (state.clearActiveOnResolved) {
217
212
  // otherwise it clears when resolved
218
213
  cancelFunction = null;
@@ -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 {() => Promise<boolean>} list - Initiates a fetch to retrieve objects according to the CRUD configuration, returning a promise to a boolean indicating success.
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('./cancellableIntent.js').CancellablePromise|null}} */
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 Promise.reject(new ListInstanceError("already loading.", "already-loading"));
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
- let resolveState = false;
387
- if (listPromise.cancel) {
388
- promises.list.cancel = async () => {
389
- let promise = listPromise.cancel();
390
- isCancelled.value = true;
391
- if (promise) {
392
- await promise;
393
- }
394
- loadingError.clearLoading();
395
- };
396
- }
397
- // the indirection of promises here is to allow us to do additional work on listPromise's cancel
398
- listPromise
399
- .then(() => {
400
- resolveState = true;
401
- })
402
- .catch((error) => {
403
- loadingError.setError(error);
404
- })
405
- .finally(() => {
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 "vue-deepunref";
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
 
@@ -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 {Readonly<import("vue").Ref<boolean|undefined>>} loading - Whether the component is loading.
16
- * @property {Readonly<import("vue").Ref<Error|null>>} error - The error that occurred.
17
- * @property {Readonly<import("vue").Ref<boolean>>} errored - Whether an error has occurred.
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 {import("vue").Ref<boolean|undefined>} */
66
+ /** @type {LoadingRef} */
43
67
  const loading = ref(undefined);
44
- /** @type {import("vue").Ref<Error|null>} */
68
+ /** @type {ErrorRef} */
45
69
  const error = ref(null);
46
- /** @type {import("vue").Ref<boolean>} */
70
+ /** @type {ErroredRef} */
47
71
  const errored = ref(false);
48
72
 
49
73
  return {