@arrai-innovations/reactive-helpers 9.0.3 → 10.0.1
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/.idea/inspectionProfiles/Project_Default.xml +1 -0
- package/README.md +405 -65
- package/config/index.js +2 -0
- package/config/listCrud.js +37 -0
- package/config/objectCrud.js +45 -0
- package/docs.md +186 -24
- package/index.js +1 -0
- package/package.json +2 -2
- package/tests/unit/config/index.spec.js +18 -0
- package/tests/unit/config/listCrud.spec.js +107 -0
- package/tests/unit/config/objectCrud.spec.js +155 -0
- package/tests/unit/use/cancellableIntent.spec.js +1 -1
- package/tests/unit/use/listInstance.spec.js +5 -4
- package/tests/unit/use/listSort.spec.js +1 -1
- package/tests/unit/use/listSubscription.spec.js +3 -5
- package/tests/unit/utils/flattenPaths.spec.js +8 -0
- package/tests/unit/utils/keyDiff.spec.js +44 -1
- package/use/list.js +49 -15
- package/use/listCalculated.js +3 -0
- package/use/listFilter.js +40 -0
- package/use/listInstance.js +79 -27
- package/use/listRelated.js +41 -9
- package/use/listSort.js +71 -41
- package/use/listSubscription.js +57 -15
- package/use/object.js +26 -5
- package/use/objectCalculated.js +26 -39
- package/use/objectInstance.js +19 -35
- package/use/objectRelated.js +24 -34
- package/use/objectSubscription.js +25 -28
- package/utils/flattenPaths.js +11 -3
- package/utils/keyDiff.js +11 -4
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { addOrUpdateReactiveObject } from "../utils/assignReactiveObject.js";
|
|
2
|
+
import cloneDeep from "lodash-es/cloneDeep.js";
|
|
3
|
+
import isFunction from "lodash-es/isFunction.js";
|
|
4
|
+
import { isReactive, toRef } from "vue";
|
|
5
|
+
|
|
6
|
+
const defaultCrud = {
|
|
7
|
+
args: {},
|
|
8
|
+
list: undefined,
|
|
9
|
+
subscribe: undefined,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const setListCrud = ({ list, subscribe, args = {}, ...rest }) => {
|
|
13
|
+
defaultCrud.list = list;
|
|
14
|
+
defaultCrud.subscribe = subscribe;
|
|
15
|
+
// defensive cloning
|
|
16
|
+
Object.assign(defaultCrud.args, cloneDeep(args));
|
|
17
|
+
if (Object.keys(rest).length) {
|
|
18
|
+
throw new Error(`Unknown key(s) passed to setListCrud: ${Object.keys(rest).join(", ")}`);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export const getListCrud = (reactiveCrud, { props, functions } = {}) => {
|
|
23
|
+
// don't mutate the default crud
|
|
24
|
+
Object.assign(reactiveCrud, cloneDeep(defaultCrud));
|
|
25
|
+
if (props?.crudArgs) {
|
|
26
|
+
addOrUpdateReactiveObject(reactiveCrud.args, props.crudArgs);
|
|
27
|
+
}
|
|
28
|
+
if (functions) {
|
|
29
|
+
for (const [key, value] of Object.entries(functions)) {
|
|
30
|
+
if (isFunction(value) && key in reactiveCrud) {
|
|
31
|
+
reactiveCrud[key] = isReactive(functions) ? toRef(functions, key) : value;
|
|
32
|
+
} else {
|
|
33
|
+
throw Error(`Invalid function "${key}" for getListCrud: invalid key or not a function.`);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { addOrUpdateReactiveObject } from "../utils/assignReactiveObject.js";
|
|
2
|
+
import cloneDeep from "lodash-es/cloneDeep.js";
|
|
3
|
+
import isFunction from "lodash-es/isFunction.js";
|
|
4
|
+
import { isReactive, toRef } from "vue";
|
|
5
|
+
|
|
6
|
+
const defaultCrud = {
|
|
7
|
+
args: {},
|
|
8
|
+
retrieve: undefined,
|
|
9
|
+
create: undefined,
|
|
10
|
+
update: undefined,
|
|
11
|
+
patch: undefined,
|
|
12
|
+
delete: undefined,
|
|
13
|
+
subscribe: undefined,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const setObjectCrud = ({ retrieve, create, update, patch, delete: deleteFn, subscribe, args = {}, ...rest }) => {
|
|
17
|
+
defaultCrud.retrieve = retrieve;
|
|
18
|
+
defaultCrud.create = create;
|
|
19
|
+
defaultCrud.update = update;
|
|
20
|
+
defaultCrud.patch = patch;
|
|
21
|
+
defaultCrud.delete = deleteFn;
|
|
22
|
+
defaultCrud.subscribe = subscribe;
|
|
23
|
+
// defensive cloning
|
|
24
|
+
Object.assign(defaultCrud.args, cloneDeep(args));
|
|
25
|
+
if (Object.keys(rest).length) {
|
|
26
|
+
throw new Error(`Unknown key(s) passed to setObjectCrud: ${Object.keys(rest).join(", ")}`);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const getObjectCrud = (reactiveCrud, { props, functions } = {}) => {
|
|
31
|
+
// don't mutate the default crud
|
|
32
|
+
Object.assign(reactiveCrud, cloneDeep(defaultCrud));
|
|
33
|
+
if (props?.crudArgs) {
|
|
34
|
+
addOrUpdateReactiveObject(reactiveCrud.args, props.crudArgs);
|
|
35
|
+
}
|
|
36
|
+
if (functions) {
|
|
37
|
+
for (const [key, value] of Object.entries(functions)) {
|
|
38
|
+
if (isFunction(value) && key in reactiveCrud) {
|
|
39
|
+
reactiveCrud[key] = isReactive(functions) ? toRef(functions, key) : value;
|
|
40
|
+
} else {
|
|
41
|
+
throw Error(`Invalid function "${key}" for getObjectCrud: invalid key or not a function.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
package/docs.md
CHANGED
|
@@ -23,23 +23,31 @@
|
|
|
23
23
|
|
|
24
24
|
## Functions
|
|
25
25
|
|
|
26
|
-
| Name
|
|
27
|
-
|
|
|
28
|
-
| [useCombineClasses(classes)]
|
|
26
|
+
| Name | Description |
|
|
27
|
+
| ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
28
|
+
| [useCombineClasses(classes)] |
|
|
29
|
+
| [useListInstance(options)] | `useListInstance` is a Vue composition function that manages a list of objects. It has the ability to retrieve the list from an implementation, or subscribe to updates from an implementation. It tracks the objects in the list, and their added order. |
|
|
30
|
+
| [useListSubscription(options)] | `useListSubscription` creates a reactive object that manages a list of objects, as returned by `useListInstance`, causing the list to be re-fetched as needed and listening for updates to the list. |
|
|
29
31
|
|
|
30
32
|
## Typedefs
|
|
31
33
|
|
|
32
|
-
| Name
|
|
33
|
-
|
|
|
34
|
-
| [CSSValue]
|
|
35
|
-
| [CSSObject]
|
|
36
|
-
| [CSSClasses]
|
|
37
|
-
| [CSSStringOrObject]
|
|
38
|
-
| [
|
|
39
|
-
| [
|
|
40
|
-
| [
|
|
41
|
-
| [
|
|
42
|
-
| [
|
|
34
|
+
| Name | Description |
|
|
35
|
+
| ------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
36
|
+
| [CSSValue] | A string representing a CSS class or a space-separated list of CSS classes. |
|
|
37
|
+
| [CSSObject] | A CSS object where keys are CSS classes and values are booleans indicating whether to apply the class. |
|
|
38
|
+
| [CSSClasses] | A mixed array containing multiple ways of specifying CSS classes. |
|
|
39
|
+
| [CSSStringOrObject] | The amalgamated classes as returned by `objectifyClasses` & `combineClasses`. |
|
|
40
|
+
| [ListInstanceOptions] | The configuration options used to create a list instance. |
|
|
41
|
+
| [ListInstanceState] | A reactive object that manages a list of objects, as returned by `useListInstance`. |
|
|
42
|
+
| [ListInstance] |
|
|
43
|
+
| [ListSubscriptionOptions] | The configuration options used to create a list subscription. |
|
|
44
|
+
| [ListSubscriptionState] | A reactive object that manages a list of objects, as returned by `useListInstance`. |
|
|
45
|
+
| [ListSubscription] |
|
|
46
|
+
| [CSSValue] | A string representing a CSS class or a space-separated list of CSS classes. |
|
|
47
|
+
| [CSSObject] | A CSS object where keys are CSS classes and values are booleans indicating whether to apply the class. |
|
|
48
|
+
| [CSSClasses] | A mixed array containing multiple ways of specifying CSS classes. |
|
|
49
|
+
| [CSSClassesWithRefs] | A mixed array containing multiple ways of specifying CSS classes. |
|
|
50
|
+
| [CSSStringOrObject] | A CSS object or a space-separated list of CSS classes. |
|
|
43
51
|
|
|
44
52
|
## utils/assignReactiveObject
|
|
45
53
|
|
|
@@ -213,7 +221,7 @@ Logs debug messages based on the specified categories and logging rules.
|
|
|
213
221
|
|
|
214
222
|
Get all paths from an array or object.
|
|
215
223
|
|
|
216
|
-
### utils/flattenPaths~flattenPaths(arrayOrObject,
|
|
224
|
+
### utils/flattenPaths~flattenPaths(arrayOrObject, options)
|
|
217
225
|
|
|
218
226
|
Turn an array or object into an array of path strings. Recurses for any found arrays or objects.
|
|
219
227
|
|
|
@@ -222,10 +230,13 @@ Array indexes are wrapped in square brackets and object keys are prefixed with a
|
|
|
222
230
|
**Kind**: inner method of [`utils/flattenPaths`]
|
|
223
231
|
**Returns**: `Array.<string>` - paths
|
|
224
232
|
|
|
225
|
-
| Param
|
|
226
|
-
|
|
|
227
|
-
| arrayOrObject
|
|
228
|
-
|
|
|
233
|
+
| Param | Type | Description |
|
|
234
|
+
| ------------------- | ------------------- | -------------------------------------------------- |
|
|
235
|
+
| arrayOrObject | `Array` \| `object` | array or object to flatten |
|
|
236
|
+
| options | `object` | options |
|
|
237
|
+
| options.currentPath | `string` | current path, for recursion or as a starting point |
|
|
238
|
+
| options.depth | `number` | current depth, for recursion |
|
|
239
|
+
| options.limit | `number` | limit the depth of recursion |
|
|
229
240
|
|
|
230
241
|
## utils/keyDiff
|
|
231
242
|
|
|
@@ -260,11 +271,15 @@ what keys are removed, and what keys are added. Keys are sourced deeply in the o
|
|
|
260
271
|
**Kind**: inner method of [`utils/keyDiff`]
|
|
261
272
|
**Returns**: `KeyDiffResult` - - the differences
|
|
262
273
|
|
|
263
|
-
| Param
|
|
264
|
-
|
|
|
265
|
-
| newObj
|
|
266
|
-
| oldObj
|
|
267
|
-
| \[options\]
|
|
274
|
+
| Param | Type | Description |
|
|
275
|
+
| ----------------------- | ---------------- | -------------------------------------- |
|
|
276
|
+
| newObj | `object` | the new version of the object |
|
|
277
|
+
| oldObj | `object` | the old version of the object |
|
|
278
|
+
| \[options\] | `KeyDiffOptions` | which differences are returned |
|
|
279
|
+
| \[options.sameKeys\] | `boolean` | if true, return keys that are the same |
|
|
280
|
+
| \[options.removedKeys\] | `boolean` | if true, return keys that are removed |
|
|
281
|
+
| \[options.addedKeys\] | `boolean` | if true, return keys that are added |
|
|
282
|
+
| \[options.limit\] | `number` | limit the depth of recursion |
|
|
268
283
|
|
|
269
284
|
### utils/keyDiff~KeyDiffOptions
|
|
270
285
|
|
|
@@ -427,6 +442,31 @@ Remove empty objects and undefined values from arrays in a mixed object array tr
|
|
|
427
442
|
| ------- | -------------- | -------------------------------------------------------------------------------------------- |
|
|
428
443
|
| classes | [`CSSClasses`] | A mixed array containing multiple ways of specifying CSS classes. Non-ref values are cloned. |
|
|
429
444
|
|
|
445
|
+
## useListInstance(options)
|
|
446
|
+
|
|
447
|
+
`useListInstance` is a Vue composition function that manages a list of objects.
|
|
448
|
+
It has the ability to retrieve the list from an implementation, or subscribe to updates from an implementation.
|
|
449
|
+
It tracks the objects in the list, and their added order.
|
|
450
|
+
|
|
451
|
+
**Kind**: global function
|
|
452
|
+
**Returns**: [`ListInstance`] - - the list instance
|
|
453
|
+
|
|
454
|
+
| Param | Type | Description |
|
|
455
|
+
| ------- | ----------------------- | -------------------------------------------- |
|
|
456
|
+
| options | [`ListInstanceOptions`] | the options used to create the list instance |
|
|
457
|
+
|
|
458
|
+
## useListSubscription(options)
|
|
459
|
+
|
|
460
|
+
`useListSubscription` creates a reactive object that manages a list of objects, as returned by `useListInstance`,
|
|
461
|
+
causing the list to be re-fetched as needed and listening for updates to the list.
|
|
462
|
+
|
|
463
|
+
**Kind**: global function
|
|
464
|
+
**Returns**: ListSubscription
|
|
465
|
+
|
|
466
|
+
| Param | Type |
|
|
467
|
+
| ------- | --------------------------- |
|
|
468
|
+
| options | [`ListSubscriptionOptions`] |
|
|
469
|
+
|
|
430
470
|
## CSSValue
|
|
431
471
|
|
|
432
472
|
A string representing a CSS class or a space-separated list of CSS classes.
|
|
@@ -451,6 +491,115 @@ The amalgamated classes as returned by `objectifyClasses` & `combineClasses`.
|
|
|
451
491
|
|
|
452
492
|
**Kind**: global typedef
|
|
453
493
|
|
|
494
|
+
## ListInstanceOptions
|
|
495
|
+
|
|
496
|
+
The configuration options used to create a list instance.
|
|
497
|
+
|
|
498
|
+
**Kind**: global typedef
|
|
499
|
+
**Properties**
|
|
500
|
+
|
|
501
|
+
| Name | Type | Description |
|
|
502
|
+
| ------------------- | ---------- | ---------------------------------------------------------------------------------------- |
|
|
503
|
+
| props | `object` | props passed to the component |
|
|
504
|
+
| props.retrieveArgs | `object` | the arguments passed to the server |
|
|
505
|
+
| props.listArgs | `object` | the arguments passed to the server |
|
|
506
|
+
| props.crudArgs | `object` | implementation specific arguments |
|
|
507
|
+
| functions | `object` | optional. default implementation are used as set by `setListCrud`. |
|
|
508
|
+
| functions.list | `function` | provide the implementation for the list function |
|
|
509
|
+
| functions.subscribe | `function` | provide the implementation for the subscribe function |
|
|
510
|
+
| keepOldPages | `boolean` | if true, pages will not be cleared when defaultPageCallback is called. default is false. |
|
|
511
|
+
|
|
512
|
+
## ListInstanceState
|
|
513
|
+
|
|
514
|
+
A reactive object that manages a list of objects, as returned by `useListInstance`.
|
|
515
|
+
|
|
516
|
+
**Kind**: global typedef
|
|
517
|
+
**Properties**
|
|
518
|
+
|
|
519
|
+
| Name | Type | Description |
|
|
520
|
+
| -------------- | ---------- | ------------------------------------------ |
|
|
521
|
+
| crud | `object` | the crud functions |
|
|
522
|
+
| crud.args | `object` | the arguments passed to the crud functions |
|
|
523
|
+
| crud.list | `function` | the list function |
|
|
524
|
+
| crud.subscribe | `function` | the subscribe function |
|
|
525
|
+
| retrieveArgs | `object` | the arguments passed to the server |
|
|
526
|
+
| listArgs | `object` | the arguments passed to the server |
|
|
527
|
+
| objects | `Map` | the objects in the list |
|
|
528
|
+
| loading | `boolean` | true if the list is loading |
|
|
529
|
+
| errored | `boolean` | true if the list has errored |
|
|
530
|
+
| error | `object` | the error object |
|
|
531
|
+
| objectsInOrder | `Array` | the objects in the list in order |
|
|
532
|
+
| order | `Array` | the order of the objects in the list |
|
|
533
|
+
| totalRecords | `number` | the total number of records |
|
|
534
|
+
| totalPages | `number` | the total number of pages |
|
|
535
|
+
| perPage | `number` | the number of records per page |
|
|
536
|
+
|
|
537
|
+
## ListInstance
|
|
538
|
+
|
|
539
|
+
**Kind**: global typedef
|
|
540
|
+
**Properties**
|
|
541
|
+
|
|
542
|
+
| Name | Type | Description |
|
|
543
|
+
| ------------------- | --------------------- | -------------------------------------------- |
|
|
544
|
+
| list | `function` | subscribe to updates from the implementation |
|
|
545
|
+
| addListObject | `function` | add an object to the list |
|
|
546
|
+
| updateListObject | `function` | update an object in the list |
|
|
547
|
+
| deleteListObject | `function` | delete an object from the list |
|
|
548
|
+
| clearList | `function` | clear the list |
|
|
549
|
+
| clearError | `function` | clear the error |
|
|
550
|
+
| getFakeId | `function` | get a fake id |
|
|
551
|
+
| defaultPageCallback | `function` | the default page callback |
|
|
552
|
+
| pageCallback | `function` | the page callback |
|
|
553
|
+
| state | [`ListInstanceState`] | the list instance state |
|
|
554
|
+
| effectScope | `object` | a Vue effect scope |
|
|
555
|
+
|
|
556
|
+
## ListSubscriptionOptions
|
|
557
|
+
|
|
558
|
+
The configuration options used to create a list subscription.
|
|
559
|
+
|
|
560
|
+
**Kind**: global typedef
|
|
561
|
+
**Properties**
|
|
562
|
+
|
|
563
|
+
| Name | Type | Description |
|
|
564
|
+
| ------------ | ---------------- | ---------------------------------------------------------------------------------------- |
|
|
565
|
+
| props | `object` | passed on to a created list instance if one is not provided |
|
|
566
|
+
| functions | `object` | passed on to a created list instance if one is not provided |
|
|
567
|
+
| listInstance | [`ListInstance`] | a list instance to use instead of creating one |
|
|
568
|
+
| keepOldPages | `boolean` | if true, pages will not be cleared when defaultPageCallback is called. default is false. |
|
|
569
|
+
|
|
570
|
+
## ListSubscriptionState
|
|
571
|
+
|
|
572
|
+
A reactive object that manages a list of objects, as returned by `useListInstance`.
|
|
573
|
+
|
|
574
|
+
**Kind**: global typedef
|
|
575
|
+
**Extends**: [`ListInstanceState`]
|
|
576
|
+
**Properties**
|
|
577
|
+
|
|
578
|
+
| Name | Type | Description |
|
|
579
|
+
| ------------------- | --------- | ----------------------------------------------------------------- |
|
|
580
|
+
| subscriptionLoading | `boolean` | true if the subscription is loading |
|
|
581
|
+
| subscriptionErrored | `boolean` | true if the subscription errored |
|
|
582
|
+
| subscriptionError | `Error` | the error that caused the subscription to error |
|
|
583
|
+
| intendToList | `boolean` | true if the list should be fetched, or refetched when args change |
|
|
584
|
+
| intendToSubscribe | `boolean` | true if the list should subscribe for updates |
|
|
585
|
+
| subscribed | `boolean` | true if the subscription is active |
|
|
586
|
+
|
|
587
|
+
## ListSubscription
|
|
588
|
+
|
|
589
|
+
**Kind**: global typedef
|
|
590
|
+
**Properties**
|
|
591
|
+
|
|
592
|
+
| Name | Type | Description |
|
|
593
|
+
| ------------------ | ------------------------- | -------------------------------------------------------------------------------------- |
|
|
594
|
+
| state | [`ListSubscriptionState`] | the reactive state of the list subscription |
|
|
595
|
+
| listInstance | [`ListInstance`] | the list instance used by the subscription |
|
|
596
|
+
| listIntent | `object` | the useCancelleableIntent object managing if the list should be (re)fetched |
|
|
597
|
+
| subscriptionIntent | `object` | the useCancelleableIntent object managing if the subscription should be (un)subscribed |
|
|
598
|
+
| subscribe | `function` | subscribe to the list |
|
|
599
|
+
| unsubscribe | `function` | unsubscribe from the list |
|
|
600
|
+
| clearError | `function` | clear the subscription error |
|
|
601
|
+
| effectScope | `object` | a Vue effect scope |
|
|
602
|
+
|
|
454
603
|
## CSSValue
|
|
455
604
|
|
|
456
605
|
A string representing a CSS class or a space-separated list of CSS classes.
|
|
@@ -500,6 +649,12 @@ A CSS object or a space-separated list of CSS classes.
|
|
|
500
649
|
[cssobject]: #cssobject
|
|
501
650
|
[cssclasses]: #cssclasses
|
|
502
651
|
[cssstringorobject]: #cssstringorobject
|
|
652
|
+
[listinstanceoptions]: #listinstanceoptions
|
|
653
|
+
[listinstancestate]: #listinstancestate
|
|
654
|
+
[listinstance]: #listinstance
|
|
655
|
+
[listsubscriptionoptions]: #listsubscriptionoptions
|
|
656
|
+
[listsubscriptionstate]: #listsubscriptionstate
|
|
657
|
+
[listsubscription]: #listsubscription
|
|
503
658
|
[cssclasseswithrefs]: #cssclasseswithrefs
|
|
504
659
|
[~validtargetorsource]: #utilsassignreactiveobjectvalidtargetorsource
|
|
505
660
|
[`utils/assignreactiveobject`]: #utilsassignreactiveobject
|
|
@@ -515,7 +670,14 @@ A CSS object or a space-separated list of CSS classes.
|
|
|
515
670
|
[`cssclasses`]: #cssclasses
|
|
516
671
|
[`cssstringorobject`]: #cssstringorobject
|
|
517
672
|
[`cssclasseswithrefs`]: #cssclasseswithrefs
|
|
673
|
+
[`listinstance`]: #listinstance
|
|
674
|
+
[`listinstanceoptions`]: #listinstanceoptions
|
|
675
|
+
[`listsubscriptionoptions`]: #listsubscriptionoptions
|
|
676
|
+
[`listinstancestate`]: #listinstancestate
|
|
677
|
+
[`listsubscriptionstate`]: #listsubscriptionstate
|
|
518
678
|
[usecombineclasses(classes)]: #usecombineclassesclasses
|
|
679
|
+
[uselistinstance(options)]: #uselistinstanceoptions
|
|
680
|
+
[uselistsubscription(options)]: #uselistsubscriptionoptions
|
|
519
681
|
[~addreactiveobject(target, source, \[exclude\], \[addedkeys\])]: #utilsassignreactiveobjectaddreactiveobjecttarget-source-exclude-addedkeys
|
|
520
682
|
[~updatereactiveobject(target, source, \[exclude\], \[samekeys\])]: #utilsassignreactiveobjectupdatereactiveobjecttarget-source-exclude-samekeys
|
|
521
683
|
[~addorupdatereactiveobject(target, source, \[exclude\], \[addedkeys\], \[samekeys\])]: #utilsassignreactiveobjectaddorupdatereactiveobjecttarget-source-exclude-addedkeys-samekeys
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arrai-innovations/reactive-helpers",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.1",
|
|
4
4
|
"description": "VueJS 3 utility composition functions to help manipulate objects and lists.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"jsdoc-to-markdown": "^8.0.0",
|
|
46
46
|
"lint-staged": "^13.2.2",
|
|
47
47
|
"prettier": "2.6.2",
|
|
48
|
-
"vitest": "^0.
|
|
48
|
+
"vitest": "^0.34.1"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"browser-util-inspect": "^0.2.0",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as config from "../../../config/index.js";
|
|
2
|
+
import fs from "fs/promises";
|
|
3
|
+
|
|
4
|
+
describe("config/index.js", function () {
|
|
5
|
+
it("should export everything exported by individual files", async function () {
|
|
6
|
+
const files = await fs.readdir("./config");
|
|
7
|
+
const modules = files.filter((file) => file.endsWith(".js") && file !== "index.js");
|
|
8
|
+
const exportedKeys = [];
|
|
9
|
+
for (const module of modules) {
|
|
10
|
+
const moduleExports = await import(`../../../config/${module}`);
|
|
11
|
+
exportedKeys.push(...Object.keys(moduleExports).filter((key) => key !== "default"));
|
|
12
|
+
}
|
|
13
|
+
const useKeys = Object.keys(config);
|
|
14
|
+
useKeys.sort();
|
|
15
|
+
exportedKeys.sort();
|
|
16
|
+
expect(useKeys).toEqual(exportedKeys);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import cloneDeep from "lodash-es/cloneDeep.js";
|
|
2
|
+
import { reactive } from "vue";
|
|
3
|
+
|
|
4
|
+
describe("config/listCrud.js", () => {
|
|
5
|
+
describe("get & set", () => {
|
|
6
|
+
let setListCrud, getListCrud;
|
|
7
|
+
beforeAll(async () => {
|
|
8
|
+
const listCrudModule = await import("../../../config/listCrud.js");
|
|
9
|
+
setListCrud = listCrudModule.setListCrud;
|
|
10
|
+
getListCrud = listCrudModule.getListCrud;
|
|
11
|
+
});
|
|
12
|
+
it("should not die getting crud before setting", () => {
|
|
13
|
+
const reactiveCrud = reactive({});
|
|
14
|
+
expect(() => getListCrud(reactiveCrud)).not.toThrow();
|
|
15
|
+
});
|
|
16
|
+
it("should set and get the default crud, avoiding mutation", () => {
|
|
17
|
+
const crud = {
|
|
18
|
+
list: () => 1,
|
|
19
|
+
subscribe: () => 2,
|
|
20
|
+
args: {
|
|
21
|
+
test: "test",
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
expect(() => setListCrud(crud)).not.toThrow();
|
|
25
|
+
|
|
26
|
+
const retrievedCrud = reactive({});
|
|
27
|
+
expect(() => getListCrud(retrievedCrud)).not.toThrow();
|
|
28
|
+
expect(new Set(Object.keys(retrievedCrud))).toEqual(new Set(["list", "subscribe", "args"]));
|
|
29
|
+
expect(retrievedCrud.args).not.toBe(crud.args);
|
|
30
|
+
expect(retrievedCrud.args).toEqual(crud.args);
|
|
31
|
+
expect(retrievedCrud.list).toBe(crud.list);
|
|
32
|
+
expect(retrievedCrud.subscribe).toBe(crud.subscribe);
|
|
33
|
+
|
|
34
|
+
const originalCrud = cloneDeep(crud);
|
|
35
|
+
crud.args.test = "test2";
|
|
36
|
+
crud.list = () => 3;
|
|
37
|
+
crud.subscribe = () => 4;
|
|
38
|
+
expect(retrievedCrud.args.test).toBe(originalCrud.args.test);
|
|
39
|
+
expect(retrievedCrud.list).toBe(originalCrud.list);
|
|
40
|
+
expect(retrievedCrud.subscribe).toBe(originalCrud.subscribe);
|
|
41
|
+
});
|
|
42
|
+
it("should die if an unknown function is passed", () => {
|
|
43
|
+
expect(() =>
|
|
44
|
+
setListCrud({ list: () => 1, subscribe: () => 2, args: { test: "test" }, unknown: () => 3 })
|
|
45
|
+
).toThrow("Unknown key(s) passed to setListCrud: unknown");
|
|
46
|
+
});
|
|
47
|
+
it("should customize via getListCrud", () => {
|
|
48
|
+
const defaultCrud = {
|
|
49
|
+
list: () => 1,
|
|
50
|
+
subscribe: () => 2,
|
|
51
|
+
args: {
|
|
52
|
+
test: "test",
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
const customCrudArgs = {
|
|
56
|
+
props: reactive({
|
|
57
|
+
crudArgs: {
|
|
58
|
+
test: "test2",
|
|
59
|
+
},
|
|
60
|
+
}),
|
|
61
|
+
functions: {
|
|
62
|
+
list: () => 3,
|
|
63
|
+
subscribe: () => 4,
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
const expectedCustomCrud = {
|
|
67
|
+
list: customCrudArgs.functions.list,
|
|
68
|
+
subscribe: customCrudArgs.functions.subscribe,
|
|
69
|
+
args: customCrudArgs.props.crudArgs,
|
|
70
|
+
};
|
|
71
|
+
expect(() => setListCrud(defaultCrud)).not.toThrow();
|
|
72
|
+
const defaultRetrievedCrud = reactive({});
|
|
73
|
+
getListCrud(defaultRetrievedCrud);
|
|
74
|
+
expect(defaultRetrievedCrud).toEqual(defaultCrud);
|
|
75
|
+
expect(defaultRetrievedCrud).not.toEqual(expectedCustomCrud);
|
|
76
|
+
const customRetrievedCrud = reactive({});
|
|
77
|
+
getListCrud(customRetrievedCrud, customCrudArgs);
|
|
78
|
+
expect(customRetrievedCrud).toEqual(expectedCustomCrud);
|
|
79
|
+
expect(customRetrievedCrud).not.toEqual(defaultCrud);
|
|
80
|
+
expect(defaultRetrievedCrud).toEqual(defaultCrud);
|
|
81
|
+
expect(defaultRetrievedCrud).not.toEqual(expectedCustomCrud);
|
|
82
|
+
});
|
|
83
|
+
it("should throw if passed functions object that has values that are not functions", () => {
|
|
84
|
+
const retrievedCrud = reactive({});
|
|
85
|
+
expect(() =>
|
|
86
|
+
getListCrud(retrievedCrud, {
|
|
87
|
+
functions: {
|
|
88
|
+
list: () => 1,
|
|
89
|
+
subscribe: true,
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
).toThrow('Invalid function "subscribe" for getListCrud: invalid key or not a function.');
|
|
93
|
+
});
|
|
94
|
+
it("should throw if passed unexpected functions", () => {
|
|
95
|
+
const retrievedCrud = reactive({});
|
|
96
|
+
expect(() =>
|
|
97
|
+
getListCrud(retrievedCrud, {
|
|
98
|
+
functions: {
|
|
99
|
+
list: () => 1,
|
|
100
|
+
subscribe: () => 2,
|
|
101
|
+
unknown: () => 3,
|
|
102
|
+
},
|
|
103
|
+
})
|
|
104
|
+
).toThrow('Invalid function "unknown" for getListCrud: invalid key or not a function.');
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import cloneDeep from "lodash-es/cloneDeep.js";
|
|
2
|
+
import { reactive } from "vue";
|
|
3
|
+
|
|
4
|
+
describe("config/objectCrud.js", () => {
|
|
5
|
+
describe("get & set", () => {
|
|
6
|
+
let setObjectCrud, getObjectCrud;
|
|
7
|
+
beforeAll(async () => {
|
|
8
|
+
const objectCrudModule = await import("../../../config/objectCrud.js");
|
|
9
|
+
setObjectCrud = objectCrudModule.setObjectCrud;
|
|
10
|
+
getObjectCrud = objectCrudModule.getObjectCrud;
|
|
11
|
+
});
|
|
12
|
+
it("should not die getting crud before setting", () => {
|
|
13
|
+
const reactiveCrud = reactive({});
|
|
14
|
+
expect(() => getObjectCrud(reactiveCrud)).not.toThrow();
|
|
15
|
+
});
|
|
16
|
+
it("should set and get the default crud, avoiding mutation", () => {
|
|
17
|
+
const crud = {
|
|
18
|
+
retrieve: () => 1,
|
|
19
|
+
create: () => 2,
|
|
20
|
+
update: () => 3,
|
|
21
|
+
patch: () => 4,
|
|
22
|
+
delete: () => 5,
|
|
23
|
+
subscribe: () => 6,
|
|
24
|
+
args: {
|
|
25
|
+
test: "test",
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
expect(() => setObjectCrud(crud)).not.toThrow();
|
|
29
|
+
|
|
30
|
+
const retrievedCrud = reactive({});
|
|
31
|
+
expect(() => getObjectCrud(retrievedCrud)).not.toThrow();
|
|
32
|
+
expect(new Set(Object.keys(retrievedCrud))).toEqual(
|
|
33
|
+
new Set(["retrieve", "create", "update", "patch", "delete", "subscribe", "args"])
|
|
34
|
+
);
|
|
35
|
+
expect(retrievedCrud.args).not.toBe(crud.args);
|
|
36
|
+
expect(retrievedCrud.args).toEqual(crud.args);
|
|
37
|
+
expect(retrievedCrud.retrieve).toBe(crud.retrieve);
|
|
38
|
+
expect(retrievedCrud.create).toBe(crud.create);
|
|
39
|
+
expect(retrievedCrud.update).toBe(crud.update);
|
|
40
|
+
expect(retrievedCrud.patch).toBe(crud.patch);
|
|
41
|
+
expect(retrievedCrud.delete).toBe(crud.delete);
|
|
42
|
+
expect(retrievedCrud.subscribe).toBe(crud.subscribe);
|
|
43
|
+
|
|
44
|
+
const originalCrud = cloneDeep(crud);
|
|
45
|
+
crud.args.test = "test2";
|
|
46
|
+
crud.retrieve = () => 7;
|
|
47
|
+
crud.create = () => 8;
|
|
48
|
+
crud.update = () => 9;
|
|
49
|
+
crud.patch = () => 10;
|
|
50
|
+
crud.delete = () => 11;
|
|
51
|
+
crud.subscribe = () => 12;
|
|
52
|
+
expect(retrievedCrud.args.test).toBe(originalCrud.args.test);
|
|
53
|
+
expect(retrievedCrud.retrieve).toBe(originalCrud.retrieve);
|
|
54
|
+
expect(retrievedCrud.create).toBe(originalCrud.create);
|
|
55
|
+
expect(retrievedCrud.update).toBe(originalCrud.update);
|
|
56
|
+
expect(retrievedCrud.patch).toBe(originalCrud.patch);
|
|
57
|
+
expect(retrievedCrud.delete).toBe(originalCrud.delete);
|
|
58
|
+
expect(retrievedCrud.subscribe).toBe(originalCrud.subscribe);
|
|
59
|
+
});
|
|
60
|
+
it("should customize via getObjectCrud", () => {
|
|
61
|
+
const defaultCrud = {
|
|
62
|
+
retrieve: () => 1,
|
|
63
|
+
create: () => 2,
|
|
64
|
+
update: () => 3,
|
|
65
|
+
patch: () => 4,
|
|
66
|
+
delete: () => 5,
|
|
67
|
+
subscribe: () => 6,
|
|
68
|
+
args: {
|
|
69
|
+
test: "test",
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
const customCrudArgs = {
|
|
73
|
+
props: reactive({
|
|
74
|
+
crudArgs: {
|
|
75
|
+
test: "test2",
|
|
76
|
+
},
|
|
77
|
+
}),
|
|
78
|
+
functions: {
|
|
79
|
+
retrieve: () => 7,
|
|
80
|
+
create: () => 8,
|
|
81
|
+
update: () => 9,
|
|
82
|
+
patch: () => 10,
|
|
83
|
+
delete: () => 11,
|
|
84
|
+
subscribe: () => 12,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
const expectedCustomCrud = {
|
|
88
|
+
retrieve: customCrudArgs.functions.retrieve,
|
|
89
|
+
create: customCrudArgs.functions.create,
|
|
90
|
+
update: customCrudArgs.functions.update,
|
|
91
|
+
patch: customCrudArgs.functions.patch,
|
|
92
|
+
delete: customCrudArgs.functions.delete,
|
|
93
|
+
subscribe: customCrudArgs.functions.subscribe,
|
|
94
|
+
args: customCrudArgs.props.crudArgs,
|
|
95
|
+
};
|
|
96
|
+
expect(() => setObjectCrud(defaultCrud)).not.toThrow();
|
|
97
|
+
const defaultRetrievedCrud = reactive({});
|
|
98
|
+
getObjectCrud(defaultRetrievedCrud);
|
|
99
|
+
expect(defaultRetrievedCrud).toEqual(defaultCrud);
|
|
100
|
+
expect(defaultRetrievedCrud).not.toEqual(expectedCustomCrud);
|
|
101
|
+
const customRetrievedCrud = reactive({});
|
|
102
|
+
getObjectCrud(customRetrievedCrud, customCrudArgs);
|
|
103
|
+
expect(customRetrievedCrud).toEqual(expectedCustomCrud);
|
|
104
|
+
expect(customRetrievedCrud).not.toEqual(defaultCrud);
|
|
105
|
+
expect(defaultRetrievedCrud).toEqual(defaultCrud);
|
|
106
|
+
expect(defaultRetrievedCrud).not.toEqual(expectedCustomCrud);
|
|
107
|
+
});
|
|
108
|
+
it("should die if an unknown function is passed", () => {
|
|
109
|
+
expect(() =>
|
|
110
|
+
setObjectCrud({
|
|
111
|
+
retrieve: () => 1,
|
|
112
|
+
create: () => 2,
|
|
113
|
+
update: () => 3,
|
|
114
|
+
patch: () => 4,
|
|
115
|
+
delete: () => 5,
|
|
116
|
+
subscribe: () => 6,
|
|
117
|
+
args: { test: "test" },
|
|
118
|
+
unknown: () => 7,
|
|
119
|
+
})
|
|
120
|
+
).toThrow("Unknown key(s) passed to setObjectCrud: unknown");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should throw if passed functions object that has values that are not functions", () => {
|
|
124
|
+
const retrievedCrud = reactive({});
|
|
125
|
+
expect(() =>
|
|
126
|
+
getObjectCrud(retrievedCrud, {
|
|
127
|
+
functions: {
|
|
128
|
+
retrieve: () => 1,
|
|
129
|
+
create: () => 2,
|
|
130
|
+
update: 3,
|
|
131
|
+
patch: () => 4,
|
|
132
|
+
delete: () => 5,
|
|
133
|
+
subscribe: () => 6,
|
|
134
|
+
},
|
|
135
|
+
})
|
|
136
|
+
).toThrow('Invalid function "update" for getObjectCrud: invalid key or not a function.');
|
|
137
|
+
});
|
|
138
|
+
it("should throw if passed unexpected functions", () => {
|
|
139
|
+
const retrievedCrud = reactive({});
|
|
140
|
+
expect(() =>
|
|
141
|
+
getObjectCrud(retrievedCrud, {
|
|
142
|
+
functions: {
|
|
143
|
+
retrieve: () => 1,
|
|
144
|
+
create: () => 2,
|
|
145
|
+
update: () => 3,
|
|
146
|
+
patch: () => 4,
|
|
147
|
+
delete: () => 5,
|
|
148
|
+
subscribe: () => 6,
|
|
149
|
+
unknown: () => 7,
|
|
150
|
+
},
|
|
151
|
+
})
|
|
152
|
+
).toThrow('Invalid function "unknown" for getObjectCrud: invalid key or not a function.');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
});
|