@arrai-innovations/reactive-helpers 18.1.0 → 20.0.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 (55) hide show
  1. package/README.md +51 -54
  2. package/config/commonCrud.js +75 -0
  3. package/config/listCrud.js +69 -70
  4. package/config/objectCrud.js +61 -110
  5. package/index.js +3 -1
  6. package/package.json +5 -4
  7. package/types/config/commonCrud.d.ts +19 -0
  8. package/types/config/commonCrud.d.ts.map +1 -0
  9. package/types/config/listCrud.d.ts +59 -62
  10. package/types/config/listCrud.d.ts.map +1 -1
  11. package/types/config/objectCrud.d.ts +51 -55
  12. package/types/config/objectCrud.d.ts.map +1 -1
  13. package/types/index.d.ts +3 -1
  14. package/types/use/cancellableIntent.d.ts +43 -7
  15. package/types/use/cancellableIntent.d.ts.map +1 -1
  16. package/types/use/list.d.ts +8 -12
  17. package/types/use/list.d.ts.map +1 -1
  18. package/types/use/listCalculated.d.ts +2 -3
  19. package/types/use/listCalculated.d.ts.map +1 -1
  20. package/types/use/listInstance.d.ts +22 -35
  21. package/types/use/listInstance.d.ts.map +1 -1
  22. package/types/use/listKeys.d.ts.map +1 -1
  23. package/types/use/listRelated.d.ts +2 -5
  24. package/types/use/listRelated.d.ts.map +1 -1
  25. package/types/use/listSort.d.ts +2 -3
  26. package/types/use/listSort.d.ts.map +1 -1
  27. package/types/use/listSubscription.d.ts +3 -6
  28. package/types/use/listSubscription.d.ts.map +1 -1
  29. package/types/use/object.d.ts +4 -4
  30. package/types/use/object.d.ts.map +1 -1
  31. package/types/use/objectCalculated.d.ts +2 -2
  32. package/types/use/objectInstance.d.ts +22 -22
  33. package/types/use/objectInstance.d.ts.map +1 -1
  34. package/types/use/objectRelated.d.ts +2 -2
  35. package/types/use/objectSubscription.d.ts +7 -7
  36. package/types/use/objectSubscription.d.ts.map +1 -1
  37. package/types/utils/refIfReactive.d.ts +2 -0
  38. package/types/utils/refIfReactive.d.ts.map +1 -0
  39. package/types/utils/unwrapNested.d.ts +2 -0
  40. package/types/utils/unwrapNested.d.ts.map +1 -0
  41. package/use/cancellableIntent.js +24 -16
  42. package/use/list.js +16 -20
  43. package/use/listCalculated.js +2 -3
  44. package/use/listInstance.js +42 -49
  45. package/use/listKeys.js +1 -2
  46. package/use/listRelated.js +2 -5
  47. package/use/listSort.js +2 -3
  48. package/use/listSubscription.js +14 -23
  49. package/use/object.js +6 -6
  50. package/use/objectCalculated.js +2 -2
  51. package/use/objectInstance.js +34 -34
  52. package/use/objectRelated.js +2 -2
  53. package/use/objectSubscription.js +18 -18
  54. package/utils/refIfReactive.js +18 -0
  55. package/utils/unwrapNested.js +25 -0
package/README.md CHANGED
@@ -70,11 +70,11 @@ The container for your list of objects, providing loading or error status.
70
70
  import { setListCrud } from "@arrai-innovations/reactive-helpers";
71
71
 
72
72
  setListCrud({
73
- list: async function listCrudAdaptor({ crudArgs, retrieveArgs, listArgs, pageCallback }) {
73
+ list: async function listCrudAdaptor({ target, params, pageCallback }) {
74
74
  // todo: your implemenation here.
75
- const listOfObjects = await talkToServer(crudArgs, retrieveArgs, listArgs);
75
+ const listOfObjects = await talkToServer(target, params);
76
76
  pageCallback(listOfObjects);
77
- const nextListOfObjects = await talkToServerAgain(crudArgs, retrieveArgs, listArgs);
77
+ const nextListOfObjects = await talkToServerAgain(target, params);
78
78
  pageCallback(nextListOfObjects);
79
79
  },
80
80
  });
@@ -86,15 +86,12 @@ import { useListInstance } from "@arrai-innovations/reactive-helpers";
86
86
  import { reactive } from "vue";
87
87
 
88
88
  const listProps = reactive({
89
- // crudArgs are implementation specific to your crud functions.
90
- crudArgs: {
89
+ // target are implementation specific to your crud handlers.
90
+ target: {
91
91
  stream: "contacts",
92
92
  },
93
93
  pkKey: "id",
94
- retrieveArgs: {
95
- fields: ["id", "has_name", "lexical_name", "organization", "phone"],
96
- },
97
- listArgs,
94
+ params,
98
95
  });
99
96
  const contacts = useListInstance({
100
97
  props: listProps,
@@ -110,12 +107,12 @@ console.log(contacts.error);
110
107
  console.log(contacts.objects);
111
108
  // { contacts keyed by 'id' }
112
109
  // change list or retrieve args directly
113
- contacts.state.retrieveArgs.fields.push("message_count");
110
+ contacts.state.params.fields.push("message_count");
114
111
  await contacts.list();
115
112
  console.log(contacts.objects);
116
113
  // { contacts keyed by 'id' with message_count }
117
114
  // change list or retrieve args indirectly
118
- listProps.listArgs.has_organization = false;
115
+ listProps.params.has_organization = false;
119
116
  await contacts.list();
120
117
  console.log(contacts.objects);
121
118
  // { contacts keyed by 'id' with organizationless contacts }
@@ -130,7 +127,7 @@ Adds functionality to a list instance to receive updates from the server.
130
127
  import { setListCrud } from "@arrai-innovations/reactive-helpers";
131
128
  setListCrud({
132
129
  ..., // in addition to the list crud adaptor above
133
- subscribe: function subscribeCrudAdaptor({ crudArgs, retrieveArgs, listArgs, eventCallback }) {
130
+ subscribe: function subscribeCrudAdaptor({ target, params, eventCallback }) {
134
131
  // todo: your implemenation here.
135
132
  const subscription = talkToServer(function (data, action) {
136
133
  eventCallback(data, action);
@@ -150,19 +147,17 @@ import { useListInstance, useListSubscription } from "@arrai-innovations/reactiv
150
147
  import { reactive } from "vue";
151
148
 
152
149
  const listProps = reactive({
153
- // crudArgs are implementation specific to your crud functions.
154
- crudArgs: {
150
+ // target are implementation specific to your crud handlers.
151
+ target: {
155
152
  stream: "contacts",
156
153
  includeCreateEvents: true,
157
154
  subscribeAction: "subscribe_contacts",
158
155
  unsubscribeAction: "unsubscribe_contacts",
159
156
  },
160
157
  pkKey: "id",
161
- retrieveArgs: {
162
- fields: ["id", "has_name", "lexical_name", "organization", "phone"],
163
- },
164
- listArgs: {
158
+ params: {
165
159
  has_organization: true,
160
+ fields: ["id", "has_name", "lexical_name", "organization", "phone"],
166
161
  },
167
162
  });
168
163
  const contacts = useListInstance({
@@ -179,9 +174,9 @@ contactsSubscription.subscribe();
179
174
  // stop getting updates.
180
175
  contactsSubscription.unsubscribe();
181
176
  // re-retreive the list of existing contacts including another field.
182
- contacts.retrieveArgs.fields.push("message_count");
177
+ contacts.params.fields.push("message_count");
183
178
  // re-retreive the list of all existing contacts.
184
- delete contacts.listArgs.has_organization;
179
+ delete contacts.params.has_organization;
185
180
  ```
186
181
 
187
182
  ```js
@@ -190,17 +185,15 @@ import { useListSubscription } from "@arrai-innovations/reactive-helpers";
190
185
  import { reactive } from "vue";
191
186
 
192
187
  const listProps = reactive({
193
- // crudArgs are implementation specific to your crud functions.
194
- crudArgs: {
188
+ // target are implementation specific to your crud handlers.
189
+ target: {
195
190
  stream: "contacts",
196
191
  includeCreateEvents: true,
197
192
  subscribeAction: "subscribe_contacts",
198
193
  unsubscribeAction: "unsubscribe_contacts",
199
194
  },
200
- retrieveArgs: {
195
+ params: {
201
196
  fields: ["id", "has_name", "lexical_name", "organization", "phone"],
202
- },
203
- listArgs: {
204
197
  has_organization: true,
205
198
  },
206
199
  });
@@ -221,7 +214,7 @@ import { reactive, toRef } from "vue";
221
214
 
222
215
  const organizationsProps = reactive({
223
216
  pkKey: "id",
224
- retrieveArgs: {
217
+ params: {
225
218
  fields: ["id", "name"],
226
219
  },
227
220
  });
@@ -230,7 +223,7 @@ const organizations = useListInstance({
230
223
  });
231
224
  const contactsProps = reactive({
232
225
  pkKey: "id",
233
- retrieveArgs: {
226
+ params: {
234
227
  fields: ["id", "lexical_name", "organization"],
235
228
  },
236
229
  });
@@ -306,7 +299,7 @@ import { nextTick, reactive } from "vue";
306
299
 
307
300
  const contactsProps = reactive({
308
301
  pkKey: "id",
309
- retrieveArgs: {
302
+ params: {
310
303
  fields: ["id", "has_name", "has_billing", "lexical_name", "organization"],
311
304
  },
312
305
  });
@@ -361,7 +354,7 @@ import { reactive, ref } from "vue";
361
354
 
362
355
  const contactsProps = reactive({
363
356
  pkKey: "id",
364
- retrieveArgs: {
357
+ params: {
365
358
  fields: ["id", "has_name", "has_billing", "lexical_name", "organization"],
366
359
  },
367
360
  });
@@ -406,7 +399,7 @@ import { reactive } from "vue";
406
399
 
407
400
  const contactsProps = reactive({
408
401
  pkKey: "id",
409
- retrieveArgs: {
402
+ params: {
410
403
  fields: ["id", "has_name", "has_billing", "lexical_name", "organization"],
411
404
  },
412
405
  });
@@ -451,7 +444,7 @@ import { reactive } from "vue";
451
444
 
452
445
  const contactsProps = reactive({
453
446
  pkKey: "id",
454
- retrieveArgs: {
447
+ params: {
455
448
  fields: ["id", "has_name", "lexical_name", "organization"],
456
449
  },
457
450
  });
@@ -488,19 +481,17 @@ import { useList } from "@arrai-innovations/reactive-helpers";
488
481
  import { reactive, toRef } from "vue";
489
482
 
490
483
  const managedListProps = reactive({
491
- // crudArgs are implementation specific to your crud functions.
492
- crudArgs: {
484
+ // target are implementation specific to your crud handlers.
485
+ target: {
493
486
  stream: "contacts",
494
487
  includeCreateEvents: true,
495
488
  subscribeAction: "subscribe_contacts",
496
489
  unsubscribeAction: "unsubscribe_contacts",
497
490
  },
498
491
  pkKey: "id",
499
- retrieveArgs: {
500
- fields: ["id", "has_name", "lexical_name", "organization", "phone"],
501
- },
502
- listArgs: {
492
+ params: {
503
493
  has_organization: true,
494
+ fields: ["id", "has_name", "lexical_name", "organization", "phone"],
504
495
  },
505
496
  relatedObjectsRules: {
506
497
  organization: {
@@ -524,7 +515,7 @@ const managedListProps = reactive({
524
515
  });
525
516
  const managedList = useList({
526
517
  props: managedListProps,
527
- functions: {
518
+ handlers: {
528
519
  list: customListFunction,
529
520
  subscribe: customSubscribeFunction,
530
521
  },
@@ -553,31 +544,31 @@ console.log(managedList.managed);
553
544
  import { setObjectCrud } from "@arrai-innovations/reactive-helpers";
554
545
 
555
546
  setObjectCrud({
556
- create: async function createCrudAdaptor({ crudArgs, retrieveArgs, object }) {
547
+ create: async function createCrudAdaptor({ target, params, object }) {
557
548
  // todo: your implemenation here.
558
549
  const newObject = await talkToServer(object);
559
550
  return newObject;
560
551
  },
561
- retrieve: async function retrieveCrudAdaptor({ crudArgs, retrieveArgs, id }) {
552
+ retrieve: async function retrieveCrudAdaptor({ target, params, id }) {
562
553
  // todo: your implemenation here.
563
554
  const retrievedObject = await talkToServer(id);
564
555
  return retrievedObject;
565
556
  },
566
- update: async function updateCrudAdaptor({ crudArgs, retrieveArgs, object }) {
557
+ update: async function updateCrudAdaptor({ target, params, object }) {
567
558
  // todo: your implemenation here.
568
559
  const updatedObject = await talkToServer(object);
569
560
  return updatedObject;
570
561
  },
571
- delete: async function deleteCrudAdaptor({ crudArgs, id }) {
562
+ delete: async function deleteCrudAdaptor({ target, id }) {
572
563
  // todo: your implemenation here.
573
564
  await talkToServer(id);
574
565
  },
575
- patch: async function patchCrudAdaptor({ crudArgs, retrieveArgs, id, partialObject }) {
566
+ patch: async function patchCrudAdaptor({ target, params, id, partialObject }) {
576
567
  // todo: your implemenation here.
577
568
  const patchedObject = await talkToServer(object);
578
569
  return patchedObject; // still return the full object.
579
570
  },
580
- subscribe: function subscribeCrudAdaptor({ crudArgs, retrieveArgs, listArgs, eventCallback }) {
571
+ subscribe: function subscribeCrudAdaptor({ target, params, params, eventCallback }) {
581
572
  // todo: your implemenation here.
582
573
  const subscription = talkToServer(function (data, action) {
583
574
  eventCallback(data, action);
@@ -602,10 +593,10 @@ import {
602
593
 
603
594
  const contactObject = useObjectInstance({
604
595
  props: {
605
- crudArgs: {
596
+ target: {
606
597
  stream: "contacts",
607
598
  },
608
- retrieveArgs: {
599
+ params: {
609
600
  fields: ["id", "has_name", "lexical_name", "organization", "phone"],
610
601
  },
611
602
  id: contactId,
@@ -618,10 +609,10 @@ const contactSubscription = useObjectSubscription({
618
609
  // or
619
610
  const contactSubscription = useObjectSubscription({
620
611
  props: {
621
- crudArgs: {
612
+ target: {
622
613
  stream: "contacts",
623
614
  },
624
- retrieveArgs: {
615
+ params: {
625
616
  fields: ["id", "has_name", "lexical_name", "organization", "phone"],
626
617
  },
627
618
  id: contactId,
@@ -630,10 +621,10 @@ const contactSubscription = useObjectSubscription({
630
621
  console.log(contactSubscription.state.object);
631
622
  const organizations = useList({
632
623
  props: {
633
- crudArgs: {
624
+ target: {
634
625
  stream: "organizations",
635
626
  },
636
- retrieveArgs: {
627
+ params: {
637
628
  fields: ["id", "name"],
638
629
  },
639
630
  },
@@ -667,10 +658,10 @@ import { useObject } from "@arrai-innovations/reactive-helpers";
667
658
  import { reactive } from "vue";
668
659
 
669
660
  const managedObjectProps = reactive({
670
- crudArgs: {
661
+ target: {
671
662
  stream: "contacts",
672
663
  },
673
- retrieveArgs: {
664
+ params: {
674
665
  fields: ["id", "has_name", "lexical_name", "organization", "phone"],
675
666
  },
676
667
  pk: contactId,
@@ -690,7 +681,7 @@ const managedObjectProps = reactive({
690
681
  });
691
682
  const managedObject = useObject({
692
683
  props: managedObjectProps,
693
- functions: {
684
+ handlers: {
694
685
  retrieve: customRetrieveFunction,
695
686
  subscribe: customSubscribeFunction,
696
687
  },
@@ -948,11 +939,17 @@ await awaitNot3.promise;
948
939
  ```bash
949
940
  $ npm ci
950
941
  ```
951
- 3. Run tests via jest:
942
+ 3. Run tests via vitest:
952
943
  ```bash
953
944
  $ npm test
954
945
  ```
955
946
  4. Run tests with coverage output:
947
+
956
948
  ```bash
957
949
  $ npm run coverage
958
950
  ```
951
+
952
+ 5. Generate types and typedocs:
953
+ ```bash
954
+ $ npm run docs
955
+ ```
@@ -0,0 +1,75 @@
1
+ import { CancellablePromise } from "../utils/cancellablePromise.js";
2
+ import cloneDeep from "lodash-es/cloneDeep.js";
3
+ import { addOrUpdateReactiveObject } from "../utils/assignReactiveObject.js";
4
+ import isFunction from "lodash-es/isFunction.js";
5
+ import { refIfReactive } from "../utils/refIfReactive.js";
6
+
7
+ /**
8
+ * @param {string} name - The name of the method.
9
+ * @returns {(...args: any[]) => import('../utils/cancellablePromise.js').MaybeCancellablePromise<any>} - A function that returns a rejected promise with an error message.
10
+ */
11
+ export const missingMethod = (name) => () =>
12
+ CancellablePromise.reject(new Error(`Crud method "${name}" is not implemented.`));
13
+
14
+ // HACK: eslint, tsc, webstorm all can't agree on how to do this right
15
+ // noinspection JSValidateTypes,JSUnusedLocalSymbols
16
+ /**
17
+ * @param {string} name - The name of the method.
18
+ * @returns {(
19
+ * (..._args: any[]) => import('../utils/cancellablePromise.js').CancellablePromise<void>
20
+ * )} - A function that returns a rejected promise with an error message.
21
+ */
22
+ export const requiredCancelMissingMethod =
23
+ (name) =>
24
+ /* eslint-disable no-unused-vars */
25
+ // @ts-ignore - refuses to accept returned CancellablePromise<void> = imported CancellablePromise<void>
26
+ (..._args) =>
27
+ CancellablePromise(Promise.reject(new Error(`Crud method "${name}" is not implemented.`)), () => {});
28
+ /* eslint-enable no-unused-vars */
29
+
30
+ /**
31
+ * Creates a default CRUD object with the given keys.
32
+ *
33
+ * @param {string[]} keys - The CRUD function keys.
34
+ * @param {Set<string>} cancellableKeys - Which ones need required cancellation.
35
+ * @returns {object} - The default CRUD object.
36
+ */
37
+ export const createDefaultCrud = (keys, cancellableKeys = new Set()) => {
38
+ const result = { args: {} };
39
+ for (const key of keys) {
40
+ result[key] = cancellableKeys.has(key) ? requiredCancelMissingMethod(key) : missingMethod(key);
41
+ }
42
+ return result;
43
+ };
44
+
45
+ /**
46
+ * Assigns the default CRUD handlers to the target object.
47
+ *
48
+ * @param {object} target - The reactive object to assign to.
49
+ * @param {object} defaultCrud - The default CRUD definition (usually created by `createDefaultCrud`).
50
+ * @param {object} [options] - The options object.
51
+ * @param {object} [options.props] - The props object.
52
+ * @param {object} [options.handlers] - The functions to assign.
53
+ * @param {Set<string>} [options.validKeys] - The valid keys for the handlers.
54
+ */
55
+ export function assignCrud(
56
+ target,
57
+ defaultCrud,
58
+ { props, handlers, validKeys = new Set(Object.keys(defaultCrud).filter((k) => k !== "args")) } = {}
59
+ ) {
60
+ Object.assign(target, cloneDeep(defaultCrud));
61
+ if (props?.target) {
62
+ addOrUpdateReactiveObject(target.args, props.target);
63
+ }
64
+ if (handlers) {
65
+ for (const [key, fn] of Object.entries(handlers)) {
66
+ if (!validKeys.has(key)) {
67
+ throw new Error(`Invalid function key "${key}" passed to assignCrud`);
68
+ }
69
+ if (!isFunction(fn)) {
70
+ throw new Error(`Function "${key}" is not actually a function`);
71
+ }
72
+ target[key] = refIfReactive(handlers, key, fn);
73
+ }
74
+ }
75
+ }
@@ -1,22 +1,13 @@
1
- import { addOrUpdateReactiveObject } from "../utils/assignReactiveObject.js";
1
+ import { assignCrud, createDefaultCrud } from "./commonCrud.js";
2
2
  import cloneDeep from "lodash-es/cloneDeep.js";
3
- import isFunction from "lodash-es/isFunction.js";
4
- import { isReactive, toRef } from "vue";
3
+ import { readonly } from "vue";
5
4
 
6
5
  /**
7
- * Configuration for the default list crud functions.
6
+ * Configuration for the default list crud handlers.
8
7
  *
9
8
  * @module config/listCrud.js
10
9
  */
11
10
 
12
- const defaultCrud = {
13
- args: {},
14
- list: undefined,
15
- bulkDelete: undefined,
16
- subscribe: undefined,
17
- executeAction: undefined,
18
- };
19
-
20
11
  /**
21
12
  * @typedef {object} PaginateInfo
22
13
  * @property {number} totalRecords - The total records.
@@ -32,21 +23,22 @@ const defaultCrud = {
32
23
  */
33
24
 
34
25
  /**
35
- * @typedef {object} ListFnArgs
36
- * @property {object} crudArgs - The arguments to be passed to the crud functions.
26
+ * @typedef {object} ListArgs
27
+ * @property {object} target - The arguments to be passed to the crud handlers.
37
28
  * @property {string} pkKey - The key name of the primary key.
38
- * @property {object} retrieveArgs - The arguments to be passed to the retrieve function.
39
- * @property {object} listArgs - The arguments to be passed for list crud functions.
29
+ * @property {object} params - The arguments to be passed for list crud handlers.
40
30
  * @property {PageCallback} pageCallback - The method to call with new page(s) of data received.
41
31
  * @property {Readonly<import('vue').Ref<boolean>>} isCancelled - A ref to a boolean indicating whether the request has
42
32
  * been cancelled.
43
33
  */
44
34
 
45
35
  /**
46
- * @typedef {object} DeleteFnArgs
47
- * @property {object} crudArgs - The arguments to be passed to the crud functions.
36
+ * @typedef {object} BulkDeleteArgs
37
+ * @property {object} target - The arguments to be passed to the crud handlers.
48
38
  * @property {string[]} pks - The ids of the objects to be deleted.
49
39
  * @property {string} pkKey - The key name of the primary key.
40
+ * @property {Readonly<import('vue').Ref<boolean>>} isCancelled - A ref to a boolean indicating whether the request has
41
+ * been cancelled.
50
42
  */
51
43
 
52
44
  /**
@@ -57,95 +49,102 @@ const defaultCrud = {
57
49
  */
58
50
 
59
51
  /**
60
- * @typedef {object} SubscribeFnArgs
61
- * @property {object} crudArgs - The arguments to be passed to the crud functions.
52
+ * @typedef {object} ListSubscribeArgs
53
+ * @property {object} target - The arguments to be passed to the crud handlers.
62
54
  * @property {string} pkKey - The key name of the primary key.
63
- * @property {object} retrieveArgs - The arguments to be passed to the retrieve function.
64
- * @property {object} listArgs - The arguments to be passed for list crud functions.
55
+ * @property {object} params - The arguments to be passed for list crud handlers.
65
56
  * @property {SubscriptionEventCallback} subscriptionEventCallback - The method to call when new data is received.
57
+ * @property {Readonly<import('vue').Ref<boolean>>} isCancelled - A ref to a boolean indicating whether the request has
58
+ * been cancelled.
66
59
  */
67
60
 
68
61
  /**
69
- * @typedef {object} ExecuteActionFnArgs
70
- * @property {object} crudArgs - The arguments to be passed to the crud functions.
62
+ * @typedef {object} ExecuteActionArgs
63
+ * @property {object} target - The arguments to be passed to the crud handlers.
71
64
  * @property {string[]} pks - The ids of the objects to be acted upon.
72
65
  * @property {string} pkKey - The key name of the primary key.
73
66
  * @property {string} action - The action to execute.
67
+ * @property {Readonly<import('vue').Ref<boolean>>} isCancelled - A ref to a boolean indicating whether the request has
68
+ * been cancelled.
74
69
  */
75
70
 
76
71
  /**
77
- * @typedef {(ListFnArgs)=>Promise<boolean> & { cancel: () => Promise<void>|void }} ListFn - The list function to get a list of items, returning a boolean indicating success.
72
+ * @callback CrudListFn
73
+ * @param {ListArgs} args - The arguments to be passed to the crud handlers.
74
+ * @returns {import('../utils/cancellablePromise.js').MaybeCancellablePromise<void>} - A promise that resolves to a boolean indicating success.
78
75
  */
79
76
 
80
77
  /**
81
- * @typedef {(DeleteFnArgs)=>Promise<boolean> & { cancel: () => Promise<void>|void }} BulkDeleteFn - The delete function to bulk delete a list of items.
78
+ * @callback CrudBulkDeleteFn
79
+ * @param {BulkDeleteArgs} args - The arguments to be passed to the crud handlers.
80
+ * @returns {import('../utils/cancellablePromise.js').MaybeCancellablePromise<void>} - A promise that resolves to a boolean indicating success.
82
81
  */
83
82
 
84
83
  /**
85
- * @typedef {(SubscribeFnArgs)=>Promise<boolean> & { cancel: () => Promise<void>|void }} SubscribeFn - The subscribe function to set up a subscription to a list of items.
84
+ * @callback CrudListSubscribeFn
85
+ * @param {ListSubscribeArgs} args - The arguments to be passed to the crud handlers.
86
+ * @returns {import('../utils/cancellablePromise.js').CancellablePromise<void>} - A promise that resolves to a boolean indicating success.
86
87
  */
87
88
 
88
89
  /**
89
- * @typedef {(ExecuteActionFnArgs)=>Promise<import('./objectCrud.js').CrudResponse|false> & { cancel: () => Promise<void>|void }} ExecuteActionFn - The function to execute a certain action on a list of items, returning the response data or false.
90
+ * @callback CrudExecuteActionFn
91
+ * @param {ExecuteActionArgs} args - The arguments to be passed to the crud handlers.
92
+ * @returns {import('../utils/cancellablePromise.js').MaybeCancellablePromise<object|string|null>} - A promise that resolves the result of the action, returned to the executor.
90
93
  */
91
94
 
92
95
  /**
93
- * @typedef {object} ListCrudFunctions
94
- * @property {ListFn} [list] - The list function to get a list of items.
95
- * @property {BulkDeleteFn} [bulkDelete] - The delete function to bulk delete a list of items.
96
- * @property {ExecuteActionFn} [executeAction] - The function to execute a certain action on a list of items.
97
- * @property {SubscribeFn} [subscribe] - The subscribe function to get a subscription to a list of items.
96
+ * @typedef {object} ListCrudHandlers
97
+ * @property {CrudListFn} [list] - The list function to get a list of items.
98
+ * @property {CrudBulkDeleteFn} [bulkDelete] - The delete function to bulk delete a list of items.
99
+ * @property {CrudExecuteActionFn} [executeAction] - The function to execute a certain action on a list of items.
100
+ * @property {CrudListSubscribeFn} [subscribe] - The subscribe function to get a subscription to a list of items.
98
101
  */
99
102
 
100
103
  /**
101
- * @typedef {object} ListCrudArgs
102
- * @property {object} [args={}] - The default arguments for the crud functions.
104
+ * @typedef {object} ListTarget
105
+ * @property {object} args - The default arguments for the crud handlers.
103
106
  */
104
107
 
105
108
  /**
106
- * Set the list and subscribe functions for the default crud.
109
+ * @typedef {object} ListTargetOption
110
+ * @property {object} [target={}] - The default arguments for the crud handlers.
111
+ */
112
+
113
+ const _defaultCrud = createDefaultCrud(["list", "bulkDelete", "executeAction", "subscribe"], new Set(["subscribe"]));
114
+
115
+ /**
116
+ * The default list crud handlers.
117
+ *
118
+ * @type {Readonly<ListCrudHandlers>}
119
+ */
120
+ export const defaultListCrud = readonly(_defaultCrud);
121
+
122
+ /**
123
+ * Set the list and subscribe handlers for the default crud.
107
124
  *
108
- * @param {ListCrudFunctions & Partial<ListCrudArgs>} options - The options for the default crud.
125
+ * @param {ListCrudHandlers & Partial<ListTarget>} options - The options for the default crud.
109
126
  * @throws {Error} - If unknown keys are passed.
110
127
  * @returns {void}
111
128
  */
112
- export const setListCrud = ({ list, bulkDelete, subscribe, executeAction, args = {}, ...rest }) => {
113
- defaultCrud.list = list;
114
- defaultCrud.subscribe = subscribe;
115
- defaultCrud.bulkDelete = bulkDelete;
116
- defaultCrud.executeAction = executeAction;
117
- Object.assign(defaultCrud.args, cloneDeep(args));
118
- if (Object.keys(rest).length) {
119
- throw new Error(`Unknown key(s) passed to setListCrud: ${Object.keys(rest).join(", ")}`);
129
+ export const setListCrud = ({ args = {}, ...rest }) => {
130
+ Object.assign(_defaultCrud.args, cloneDeep(args));
131
+ for (const [key, value] of Object.entries(rest)) {
132
+ if (!(key in _defaultCrud)) {
133
+ throw new Error(`Unknown key "${key}" passed to setListCrud`);
134
+ }
135
+ _defaultCrud[key] = value ?? _defaultCrud[key];
120
136
  }
121
137
  };
122
138
 
123
139
  /**
124
- * Get the previously set list and subscribe functions for the default crud.
140
+ * Get the previously set list and subscribe handlers for the default crud.
125
141
  *
126
- * @param {import("vue").UnwrapNestedRefs<ListCrudFunctions & ListCrudArgs>} reactiveCrud - The reactive crud object, which will be mutated.
142
+ * @param {import("vue").UnwrapNestedRefs<ListCrudHandlers & ListTarget>} target - The reactive crud object, which will be mutated.
127
143
  * @param {object} options - The options for the default crud.
128
- * @param {import("vue").UnwrapNestedRefs<{
129
- * crudArgs: object|undefined,
130
- * }>} [options.props] - The props to set for the crud.
131
- * @param {ListCrudFunctions & ListCrudArgs} [options.functions] - The functions to set for the crud.
132
- */
133
- export const getListCrud = (reactiveCrud, { props, functions } = {}) => {
134
- // don't mutate the default crud
135
- Object.assign(reactiveCrud, cloneDeep(defaultCrud));
136
- if (props?.crudArgs) {
137
- addOrUpdateReactiveObject(reactiveCrud.args, props.crudArgs);
138
- }
139
- if (functions) {
140
- for (const [key, value] of Object.entries(functions)) {
141
- if (isFunction(value) && key in reactiveCrud) {
142
- reactiveCrud[key] = isReactive(functions)
143
- ? // @ts-ignore - this is a valid key...
144
- toRef(functions, key)
145
- : value;
146
- } else {
147
- throw Error(`Invalid function "${key}" for getListCrud: invalid key or not a function.`);
148
- }
149
- }
150
- }
144
+ * @param {import("vue").UnwrapNestedRefs<ListTargetOption>} [options.props] - The props to set for the crud.
145
+ * @param {ListCrudHandlers} [options.handlers] - The functions to set for the crud.
146
+ * @throws {Error} - If an invalid function is passed, or if the function is not a function.
147
+ */
148
+ export const getListCrud = (target, options) => {
149
+ assignCrud(target, _defaultCrud, options);
151
150
  };