@arrai-innovations/reactive-helpers 22.0.0 → 22.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 (46) hide show
  1. package/README.md +3 -3
  2. package/config/commonCrud.js +3 -4
  3. package/config/listCrud.js +13 -11
  4. package/config/objectCrud.js +36 -24
  5. package/package.json +11 -1
  6. package/types/config/listCrud.d.ts +6 -8
  7. package/types/config/objectCrud.d.ts +33 -22
  8. package/types/tests/benchmarks/fixtures.d.ts +60 -0
  9. package/types/tests/benchmarks/listLayers.bench.d.ts +1 -0
  10. package/types/tests/benchmarks/listPush.bench.d.ts +1 -0
  11. package/types/tests/benchmarks/listStream.bench.d.ts +1 -0
  12. package/types/tests/unit/use/lifecycleCleanup.spec.d.ts +1 -0
  13. package/types/tests/unit/use/listPerformance.spec.d.ts +1 -0
  14. package/types/tests/unit/utils/cancellablePromise.spec.d.ts +1 -0
  15. package/types/use/cancellableIntent.d.ts +9 -2
  16. package/types/use/combineClasses.d.ts +1 -1
  17. package/types/use/listCalculated.d.ts +21 -20
  18. package/types/use/listInstance.d.ts +6 -1
  19. package/types/use/listRelated.d.ts +15 -15
  20. package/types/use/listSort.d.ts +6 -1
  21. package/types/use/listSubscription.d.ts +6 -2
  22. package/types/use/objectCalculated.d.ts +18 -14
  23. package/types/use/objectInstance.d.ts +4 -2
  24. package/types/use/objectRelated.d.ts +11 -9
  25. package/types/use/objectSubscription.d.ts +6 -5
  26. package/types/utils/cancellableFetch.d.ts +2 -3
  27. package/types/utils/cancellablePromise.d.ts +39 -5
  28. package/types/utils/getFakePk.d.ts +2 -2
  29. package/use/cancellableIntent.js +9 -2
  30. package/use/combineClasses.js +1 -1
  31. package/use/listCalculated.js +22 -17
  32. package/use/listFilter.js +4 -3
  33. package/use/listInstance.js +76 -16
  34. package/use/listRelated.js +9 -9
  35. package/use/listSearch.js +1 -1
  36. package/use/listSort.js +68 -23
  37. package/use/listSubscription.js +10 -1
  38. package/use/object.js +7 -8
  39. package/use/objectCalculated.js +13 -11
  40. package/use/objectInstance.js +9 -3
  41. package/use/objectRelated.js +6 -5
  42. package/use/objectSubscription.js +7 -5
  43. package/use/proxyLoadingError.js +3 -0
  44. package/utils/cancellableFetch.js +3 -3
  45. package/utils/cancellablePromise.js +21 -4
  46. package/utils/getFakePk.js +2 -2
@@ -16,15 +16,17 @@ import { computed, effectScope, nextTick, onScopeDispose, reactive, ref, toRef,
16
16
  /**
17
17
  * @typedef {{
18
18
  * [ruleKey: string]: (object: any, relatedObject: any) => any
19
- * }} ObjectCalculatedRules - The object calculated state keys.
19
+ * }} ObjectCalculatedRules - Rules for calculating values from the managed object, keyed by rule name. Each rule is used
20
+ * as a computed body and receives exactly two arguments: the managed object and its related objects (the latter only
21
+ * populated when a useObjectRelated sits upstream).
20
22
  */
21
23
 
22
24
  /**
23
25
  * @typedef {object} ObjectCalculatedRawState - The raw state for object calculated.
24
26
  * @property {ObjectCalculatedRules} calculatedObjectRules - The calculated object rules.
25
27
  * @property {{
26
- * [ruleKey: string]: import('vue').ComputedRef<any>
27
- * }} calculatedObject - The calculated object.
28
+ * [ruleKey: string]: any
29
+ * }} calculatedObject - The calculated values, by rule name. Each entry is backed by a computed, but it is read through a reactive proxy that unwraps it, so reads yield the calculated value and never carry a `.value`.
28
30
  * @property {boolean} calculatedObjectWatchRunning - Whether the calculated object watch is running.
29
31
  * @property {boolean} parentStateObjectWatchRunning - Whether the parent state object watch is running.
30
32
  * @property {boolean} calculatedRunning - Whether the calculated is running.
@@ -125,7 +127,7 @@ export function useObjectCalculateds(objectCalculatedArgs) {
125
127
  * ```vue
126
128
  * <script setup>
127
129
  * import { useObjectCalculated, useObjectSubscription } from "@arrai-innovations/reactive-helpers";
128
- * import { ref, reactive } from "vue";
130
+ * import { reactive } from "vue";
129
131
  *
130
132
  * const objectSubscriptionProps = reactive({
131
133
  * // whatever object subscription props you need to work with your crud implementation
@@ -134,21 +136,21 @@ export function useObjectCalculateds(objectCalculatedArgs) {
134
136
  * pk: '1',
135
137
  * pkKey: 'id',
136
138
  * intendToRetrieve: true,
137
- * };
138
- * const objectSubscription = useObjectSubscription(objectSubscriptionProps);
139
+ * });
140
+ * const objectSubscription = useObjectSubscription({ props: objectSubscriptionProps });
139
141
  * const objectCalculatedProps = reactive({
140
142
  * parentState: objectSubscription.state,
141
143
  * calculatedObjectRules: {
142
- * someRule: (object, relatedObject, calculatedObjects) => {
143
- * // some complex calculation, relatedObjects would be assuming there was a listRelated between the two
144
- * // calculatedObjects would be the other calculated objects in the list
145
- * // including yourself, so try not to create circular dependencies
144
+ * someRule: (object, relatedObject) => {
145
+ * // some complex calculation. relatedObject holds the managed object's related objects, and is
146
+ * // only populated when a useObjectRelated sits between the object and this composable.
146
147
  * // this is used as a computed body.
147
148
  * return object.someProperty + object.someOtherProperty;
148
149
  * },
149
- * ...
150
+ * // ...further rules
150
151
  * },
151
152
  * });
153
+ * const objectCalculated = useObjectCalculated(objectCalculatedProps);
152
154
  * </script>
153
155
  * <template>
154
156
  * <div>
@@ -2,7 +2,7 @@ import { defaultObjectCrud, getObjectCrud } from "../config/objectCrud.js";
2
2
  import { assignReactiveObject } from "../utils/assignReactiveObject.js";
3
3
  import { useLoadingError } from "./loadingError.js";
4
4
  import { reactive, readonly, ref } from "vue";
5
- import { CancellablePromise, wrapMaybeCancellable } from "../utils/cancellablePromise.js";
5
+ import { wrapMaybeCancellable } from "../utils/cancellablePromise.js";
6
6
  import { pkRefIfReactive, refIfReactive } from "../utils/refIfReactive.js";
7
7
 
8
8
  /**
@@ -56,7 +56,8 @@ import { pkRefIfReactive, refIfReactive } from "../utils/refIfReactive.js";
56
56
  * @property {import('vue').Ref<string|undefined>} pkKey - The pk key of the object.
57
57
  * @property {import('vue').Ref<{[key:string]: any}>} params - The arguments to be passed to the retrieve function.
58
58
  * @property {import('vue').Reactive<CrudObject>} object - The object.
59
- * @property {boolean} deleted - Whether the object is deleted.
59
+ * @property {boolean} deleted - Whether the object was deleted by the delete action or a subscription delete
60
+ * event. Cleared when a later create, retrieve, update, or patch repopulates the object, and by `clear()`.
60
61
  */
61
62
 
62
63
  /**
@@ -280,6 +281,7 @@ export function useObjectInstance({ props, handlers = {} }) {
280
281
  createPromise
281
282
  .then((/** @type {ExistingCrudObject} */ object) => {
282
283
  assignReactiveObject(state.object, object);
284
+ state.deleted = false;
283
285
  return true;
284
286
  })
285
287
  .catch((/** @type {Error} */ error) => {
@@ -329,13 +331,14 @@ export function useObjectInstance({ props, handlers = {} }) {
329
331
  } catch (error) {
330
332
  loadingError.setError(error);
331
333
  loadingError.clearLoading();
332
- return CancellablePromise.resolve(false);
334
+ return Promise.resolve(false);
333
335
  }
334
336
 
335
337
  promises.retrieve = wrapMaybeCancellable(
336
338
  retrievePromise
337
339
  .then((/** @type {ExistingCrudObject} */ object) => {
338
340
  assignReactiveObject(state.object, object);
341
+ state.deleted = false;
339
342
  return true;
340
343
  })
341
344
  .catch((/** @type {Error} */ error) => {
@@ -382,6 +385,7 @@ export function useObjectInstance({ props, handlers = {} }) {
382
385
  updatePromise
383
386
  .then((/** @type {ExistingCrudObject} */ object) => {
384
387
  assignReactiveObject(state.object, object);
388
+ state.deleted = false;
385
389
  return true;
386
390
  })
387
391
  .catch((/** @type {Error} */ error) => {
@@ -469,6 +473,7 @@ export function useObjectInstance({ props, handlers = {} }) {
469
473
  patchPromise
470
474
  .then((/** @type {ExistingCrudObject} */ object) => {
471
475
  assignReactiveObject(state.object, object);
476
+ state.deleted = false;
472
477
  return true;
473
478
  })
474
479
  .catch((/** @type {Error} */ error) => {
@@ -533,6 +538,7 @@ export function useObjectInstance({ props, handlers = {} }) {
533
538
  clear: () => {
534
539
  loadingError.clearError();
535
540
  assignReactiveObject(state.object, {});
541
+ state.deleted = false;
536
542
  },
537
543
  };
538
544
  return instance;
@@ -21,9 +21,10 @@ import { computed, effectScope, nextTick, reactive, ref, toRef, unref, watch } f
21
21
  // todo: pkKey is misnamed, it should be fkKey... this will be a major breaking change.
22
22
  /**
23
23
  * @typedef {object} ObjectRelatedRule - The rule for defining relationships for the managed object to other collections of objects.
24
- * @property {string} pkKey - The key in the managed object that corresponds to the key in the related object.
24
+ * @property {string} [pkKey] - The key in the managed object that corresponds to the key in the related object.
25
+ * Defaults to the rule's own key when omitted.
25
26
  * @property {import('./listInstance.js').ObjectsByPk} objects - The related objects, indexed by the key in the related object.
26
- * @property {string[]} order - The order of the related objects, if the related objects are an array.
27
+ * @property {string[]} [order] - The order of the related objects, if the related objects are an array.
27
28
  */
28
29
 
29
30
  /**
@@ -38,8 +39,8 @@ import { computed, effectScope, nextTick, reactive, ref, toRef, unref, watch } f
38
39
  * @typedef {object} ObjectRelatedRawState - The raw reactive state of the object related composable, holding its rules, computed relations, and running flags.
39
40
  * @property {ObjectRelatedRawRules} relatedObjectRules - The rules for defining relationships for the managed object to other collections of objects.
40
41
  * @property {{
41
- * [rule: string]: import('vue').ComputedRef<any>,
42
- * }} relatedObject - The related objects, indexed by the key in the related object.
42
+ * [rule: string]: any,
43
+ * }} relatedObject - The related objects, by rule name. Each entry is backed by a computed, but it is read through a reactive proxy that unwraps it, so reads yield the related object (or array of related objects) and never carry a `.value`.
43
44
  * @property {boolean} relatedObjectWatchRunning - Whether the related object watch is running.
44
45
  * @property {boolean} parentStateObjectWatchRunning - Whether the parent state object watch is running.
45
46
  * @property {boolean} relatedRunning - Whether the related objects are loading.
@@ -178,7 +179,7 @@ export function useObjectRelateds(objectRelatedArgs) {
178
179
  * order: ['3','1','2'],
179
180
  * },
180
181
  * secondOrder: {
181
- * pkKey: 'relatedObject.secondOrderId',
182
+ * pkKey: 'relatedItem.firstOrder.secondOrderId',
182
183
  * objects: someOtherObjectsSource.objects,
183
184
  * },
184
185
  * },
@@ -123,7 +123,7 @@ export function useObjectSubscriptions(subscriptionArgs) {
123
123
  * ```
124
124
  * <script setup>
125
125
  * import { useObjectSubscription } from "@arrai-innovations/reactive-helpers";
126
- * import { reactive, ref, toRef } from "vue";
126
+ * import { reactive, toRef } from "vue";
127
127
  *
128
128
  * const pkKey = "id";
129
129
  * const props = defineProps({
@@ -142,11 +142,12 @@ export function useObjectSubscriptions(subscriptionArgs) {
142
142
  * params: {
143
143
  * fields: ['foo', 'bar'],
144
144
  * },
145
- * intendToRetrieve: false,
146
- * intendToSubscribe: false,
145
+ * intendToRetrieve: true,
146
+ * intendToSubscribe: true,
147
+ * });
148
+ * const objectSubscription = useObjectSubscription({
149
+ * props: objectSubscriptionProps,
147
150
  * });
148
- * objectSubscriptionProps.intendToRetrieve = objectSubscriptionProps.intendToSubscribe = computed(()=> !!props.pk);
149
- * const objectSubscription = useObjectSubscription(objectSubscriptionProps);
150
151
  * </script>
151
152
  * <template>
152
153
  * <div v-if="objectSubscription.state.loading">Loading...</div>
@@ -230,6 +231,7 @@ export function useObjectSubscription({ objectInstance, props, handlers }) {
230
231
  assignReactiveObject(objectInstance.state.object, {});
231
232
  } else {
232
233
  assignReactiveObject(objectInstance.state.object, data);
234
+ state.deleted = false;
233
235
  }
234
236
  };
235
237
  const parentState = objectInstance.state;
@@ -44,5 +44,8 @@ export function asWatchableLoadingError(source) {
44
44
  return {
45
45
  ...asWatchableLoading(unwrappedRefs),
46
46
  ...asWatchableError(unwrappedRefs),
47
+ // Preserve clearError: for a separate-state source it lives on the source
48
+ // itself, not on state, so extracting state above would otherwise drop it.
49
+ clearError: unwrappedSource.clearError,
47
50
  };
48
51
  }
@@ -1,4 +1,4 @@
1
- import { CancellablePromise } from "./cancellablePromise.js";
1
+ import { makeCancellable } from "./cancellablePromise.js";
2
2
 
3
3
  /**
4
4
  * A wrapper around fetch that adds cancellation via AbortController and returns a CancellablePromise.
@@ -7,7 +7,7 @@ import { CancellablePromise } from "./cancellablePromise.js";
7
7
  * @param {RequestInfo} input - The URL or Request object to fetch.
8
8
  * @param {RequestInit} init - The options for the fetch request.
9
9
  * @param {(response: Response) => Promise<T>} transform - A function to transform the response.
10
- * @returns {CancellablePromise<T>} A cancellable promise that resolves to the transformed response.
10
+ * @returns {import("./cancellablePromise.js").CancellablePromise<T>} A cancellable promise that resolves to the transformed response.
11
11
  */
12
12
  export function cancellableFetch(input, init, transform) {
13
13
  const controller = new AbortController();
@@ -35,7 +35,7 @@ export function cancellableFetch(input, init, transform) {
35
35
  cleanupExternalSignal();
36
36
  });
37
37
 
38
- return CancellablePromise(basePromise, async (/** @type {any} */ reason) => {
38
+ return makeCancellable(basePromise, async (/** @type {any} */ reason) => {
39
39
  controller.abort(reason);
40
40
  await basePromise.catch(() => {});
41
41
  });
@@ -13,22 +13,37 @@
13
13
  */
14
14
 
15
15
  /**
16
- * Creates a cancellable promise, mostly for easy of type checking.
16
+ * Adds a cancel method to a promise.
17
17
  *
18
18
  * @template T
19
- * @param {Promise<T>} promise - The promise to be cancellable.
19
+ * @param {Promise<T>} promise - The promise to make cancellable.
20
20
  * @param {(reason?: any) => (Promise<void>|void)} cancel - The function to cancel the promise.
21
21
  * @returns {CancellablePromise<T>} The cancellable promise.
22
22
  */
23
- export const CancellablePromise = (promise, cancel) => {
23
+ export function makeCancellable(promise, cancel) {
24
24
  const cancellablePromise = /** @type {CancellablePromise<T>} */ (promise);
25
25
  cancellablePromise.cancel = cancel;
26
26
  return cancellablePromise;
27
- };
27
+ }
28
+
29
+ /**
30
+ * Adds a cancel method to a promise.
31
+ *
32
+ * @deprecated Use {@link makeCancellable} instead.
33
+ * @template T
34
+ * @param {Promise<T>} promise - The promise to make cancellable.
35
+ * @param {(reason?: any) => (Promise<void>|void)} cancel - The function to cancel the promise.
36
+ * @returns {CancellablePromise<T>} The cancellable promise.
37
+ */
38
+ export function CancellablePromise(promise, cancel) {
39
+ return makeCancellable(promise, cancel);
40
+ }
28
41
 
29
42
  /**
30
43
  * Creates a rejected 'cancellable' promise.
31
44
  *
45
+ * @deprecated Use `Promise.reject` directly; a plain rejected promise already
46
+ * satisfies {@link MaybeCancellablePromise}.
32
47
  * @param {any} reason - The reason for the rejection.
33
48
  * @returns {MaybeCancellablePromise<never>} A rejected 'cancellable' promise.
34
49
  */
@@ -39,6 +54,8 @@ CancellablePromise.reject = (reason) => {
39
54
  /**
40
55
  * Creates a resolved 'cancellable' promise.
41
56
  *
57
+ * @deprecated Use `Promise.resolve` directly; a plain resolved promise already
58
+ * satisfies {@link MaybeCancellablePromise}.
42
59
  * @template T
43
60
  * @param {T} value - The value to resolve the promise with.
44
61
  * @returns {MaybeCancellablePromise<T>} A resolved 'cancellable' promise.
@@ -3,8 +3,8 @@ import isMap from "lodash-es/isMap.js";
3
3
  import isSet from "lodash-es/isSet.js";
4
4
 
5
5
  /**
6
- * Get a fake pk that is not in the array, set, map, or object. The fake pk is negative number, so they can be
7
- * differentiated from real ids. They are returned as strings, as javascript object property keys are always strings.
6
+ * Get a random safe integer at or below zero and return it as a string. A candidate is redrawn when its numeric value
7
+ * exists in an array field, Set, or Map, or when its string property key exists in an object.
8
8
  *
9
9
  * @param {Array|Set|Map|object} arraySetMapOrObject - The array, set, map, or object to check for the fake pk.
10
10
  * An array is assumed to be an array of objects.