@arrai-innovations/reactive-helpers 3.3.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arrai-innovations/reactive-helpers",
3
- "version": "3.3.0",
3
+ "version": "4.1.0",
4
4
  "description": "VueJS 3 utility composition functions to help manipulate objects and lists.",
5
5
  "main": "index.js",
6
6
  "directories": {
@@ -54,6 +54,7 @@
54
54
  "lodash": "^4.17.21",
55
55
  "pinst": "^3.0.0",
56
56
  "vue": "^3.2.33",
57
+ "vue-deepunref": "^1.0.1",
57
58
  "vue-router": "^4.0.15"
58
59
  }
59
60
  }
@@ -0,0 +1,76 @@
1
+ import { nextTick } from "vue";
2
+ import { deepUnref } from "vue-deepunref";
3
+
4
+ describe("use/listCalculated", () => {
5
+ let useListInstance, useListCalculated;
6
+ beforeEach(async () => {
7
+ const listInstanceModule = await import("../../../use/listInstance");
8
+ useListInstance = listInstanceModule.useListInstance;
9
+ const listCalculatedModule = await import("../../../use/listCalculated");
10
+ useListCalculated = listCalculatedModule.useListCalculated;
11
+ });
12
+ it("should return a list of calculated items", async () => {
13
+ const mainListInstance = useListInstance({});
14
+ const calculatedListInstance = useListInstance({});
15
+ mainListInstance.addListObject({
16
+ id: "1",
17
+ name: "main",
18
+ calculated_items: ["2", "3"],
19
+ calculated_id: "4",
20
+ });
21
+ calculatedListInstance.addListObject({
22
+ id: "2",
23
+ name: "calculated1",
24
+ });
25
+ calculatedListInstance.addListObject({
26
+ id: "3",
27
+ name: "calculated2",
28
+ });
29
+ calculatedListInstance.addListObject({
30
+ id: "4",
31
+ name: "calculated3",
32
+ });
33
+ const listCalculated = useListCalculated({
34
+ parentState: mainListInstance.state,
35
+ calculatedObjectsRules: {
36
+ calculatedItems: (obj) => obj.calculated_items.map((x) => calculatedListInstance.state.objects[x]),
37
+ calculatedItem: (obj) => calculatedListInstance.state.objects[obj.calculated_id],
38
+ },
39
+ calculatedObjectsPropertyName: "myCalculatedObjects",
40
+ });
41
+ await nextTick();
42
+ // listCalculated.state.objects is doing proxy shenanigans
43
+ // in uses handler.has
44
+ expect("myCalculatedObjects" in listCalculated.state).toBe(true);
45
+ expect(!!listCalculated.state.myCalculatedObjects?.[1]).toBe(true);
46
+ expect("calculatedItems" in listCalculated.state.myCalculatedObjects[1]).toBe(true);
47
+ expect("calculatedItem" in listCalculated.state.myCalculatedObjects[1]).toBe(true);
48
+ // expect uses enumeration, which uses handler.ownKeys and handler.getOwnPropertyDescriptor
49
+ expect(deepUnref(listCalculated.state.objects)).toEqual({
50
+ 1: {
51
+ id: "1",
52
+ name: "main",
53
+ calculated_id: "4",
54
+ calculated_items: ["2", "3"],
55
+ },
56
+ });
57
+ expect(deepUnref(listCalculated.state.myCalculatedObjects)).toEqual({
58
+ 1: {
59
+ calculatedItems: [
60
+ {
61
+ id: "2",
62
+ name: "calculated1",
63
+ },
64
+ {
65
+ id: "3",
66
+ name: "calculated2",
67
+ },
68
+ ],
69
+ calculatedItem: {
70
+ id: "4",
71
+ name: "calculated3",
72
+ },
73
+ },
74
+ });
75
+ });
76
+ });
@@ -1,7 +1,7 @@
1
1
  import { nextTick, reactive, ref } from "vue";
2
- import { doAwaitNot } from "../../../utils/watches";
2
+ import { deepUnref } from "vue-deepunref";
3
3
  import { useListFilters, useListSort } from "../../../use";
4
- import { unrefAndToRawDeep } from "../../../utils";
4
+ import { doAwaitNot } from "../../../utils/watches";
5
5
 
6
6
  describe("use/listFilter", () => {
7
7
  let useListInstance, useListFilter, setDefaultSearchOptions;
@@ -318,10 +318,10 @@ describe("use/listFilter", () => {
318
318
 
319
319
  expect(listFilters.A.state.excludedValues).toEqual({ id: 1, name: "three" });
320
320
  expect(listFilters.B.state.allowedValues).toEqual({ id: 2, name: "four" });
321
- expect(unrefAndToRawDeep(listFilters.A.parentState)).toEqual(unrefAndToRawDeep(listFilterA.parentState));
322
- expect(unrefAndToRawDeep(listFilters.B.parentState)).toEqual(unrefAndToRawDeep(listFilterB.parentState));
323
- expect(unrefAndToRawDeep(listFilters.A.state)).toEqual(unrefAndToRawDeep(listFilterA.state));
324
- expect(unrefAndToRawDeep(listFilters.B.state)).toEqual(unrefAndToRawDeep(listFilterB.state));
321
+ expect(deepUnref(listFilters.A.parentState)).toEqual(deepUnref(listFilterA.parentState));
322
+ expect(deepUnref(listFilters.B.parentState)).toEqual(deepUnref(listFilterB.parentState));
323
+ expect(deepUnref(listFilters.A.state)).toEqual(deepUnref(listFilterA.state));
324
+ expect(deepUnref(listFilters.B.state)).toEqual(deepUnref(listFilterB.state));
325
325
  });
326
326
  });
327
327
  });
@@ -1,5 +1,5 @@
1
1
  import { nextTick } from "vue";
2
- import { unrefAndToRawDeep } from "../../../utils/unrefAndToRawDeep";
2
+ import { deepUnref } from "vue-deepunref";
3
3
 
4
4
  describe("use/listRelated", () => {
5
5
  let useListInstance, useListRelated;
@@ -47,31 +47,34 @@ describe("use/listRelated", () => {
47
47
  await nextTick();
48
48
  // listRelated.state.objects is doing proxy shenanigans
49
49
  // in uses handler.has
50
- expect("myRelatedObjects" in listRelated.state.objects[1]).toBe(true);
51
- expect("relatedItems" in listRelated.state.objects[1].myRelatedObjects).toBe(true);
52
- expect("relatedItem" in listRelated.state.objects[1].myRelatedObjects).toBe(true);
50
+ expect("myRelatedObjects" in listRelated.state).toBe(true);
51
+ expect(!!listRelated.state.myRelatedObjects?.[1]).toBe(true);
52
+ expect("relatedItems" in listRelated.state.myRelatedObjects[1]).toBe(true);
53
+ expect("relatedItem" in listRelated.state.myRelatedObjects[1]).toBe(true);
53
54
  // expect uses enumeration, which uses handler.ownKeys and handler.getOwnPropertyDescriptor
54
- expect(unrefAndToRawDeep(listRelated.state.objects)).toEqual({
55
+ expect(deepUnref(listRelated.state.objects)).toEqual({
55
56
  1: {
56
57
  id: "1",
57
58
  name: "main",
58
59
  related_id: "4",
59
60
  related_items: ["2", "3"],
60
- myRelatedObjects: {
61
- relatedItems: [
62
- {
63
- id: "2",
64
- name: "related1",
65
- },
66
- {
67
- id: "3",
68
- name: "related2",
69
- },
70
- ],
71
- relatedItem: {
72
- id: "4",
73
- name: "related3",
61
+ },
62
+ });
63
+ expect(deepUnref(listRelated.state.myRelatedObjects)).toEqual({
64
+ 1: {
65
+ relatedItems: [
66
+ {
67
+ id: "2",
68
+ name: "related1",
74
69
  },
70
+ {
71
+ id: "3",
72
+ name: "related2",
73
+ },
74
+ ],
75
+ relatedItem: {
76
+ id: "4",
77
+ name: "related3",
75
78
  },
76
79
  },
77
80
  });
@@ -1,5 +1,6 @@
1
1
  import { nextTick } from "vue";
2
- import { doAwaitTimeout, unrefAndToRawDeep } from "../../../utils";
2
+ import { deepUnref } from "vue-deepunref";
3
+ import { doAwaitTimeout } from "../../../utils";
3
4
 
4
5
  describe("use/useListSort", () => {
5
6
  let listInstance,
@@ -178,10 +179,10 @@ describe("use/useListSort", () => {
178
179
  },
179
180
  listInstances
180
181
  );
181
- expect(unrefAndToRawDeep(listSorts.A.parentState)).toEqual(unrefAndToRawDeep(listInstanceA.state));
182
- expect(unrefAndToRawDeep(listSorts.B.parentState)).toEqual(unrefAndToRawDeep(listInstanceB.state));
183
- expect(unrefAndToRawDeep(listSorts.A.state)).toEqual(unrefAndToRawDeep(listSortA.state));
184
- expect(unrefAndToRawDeep(listSorts.B.state)).toEqual(unrefAndToRawDeep(listSortB.state));
182
+ expect(deepUnref(listSorts.A.parentState)).toEqual(deepUnref(listInstanceA.state));
183
+ expect(deepUnref(listSorts.B.parentState)).toEqual(deepUnref(listInstanceB.state));
184
+ expect(deepUnref(listSorts.A.state)).toEqual(deepUnref(listSortA.state));
185
+ expect(deepUnref(listSorts.B.state)).toEqual(deepUnref(listSortB.state));
185
186
  });
186
187
  describe("useListSort/sortThrottleWait", () => {
187
188
  it("respects throttle time prior to triggering", async () => {
@@ -1,7 +1,7 @@
1
- import { nextTick, reactive } from "vue";
2
- import { inspect } from "util";
3
- import { doAwaitTimeout } from "../../../utils";
4
1
  import flushPromises from "flush-promises";
2
+ import { inspect } from "util";
3
+ import { nextTick, reactive } from "vue";
4
+ import { doAwaitNot, doAwaitTimeout } from "../../../utils";
5
5
  import { CancellableResolvable } from "../crudPromise";
6
6
  import { poll } from "../poll";
7
7
 
@@ -512,8 +512,12 @@ describe("use/listSubscription.spec.js", function () {
512
512
  listArgs.user = 2;
513
513
  retrieveArgs.fields = ["name"];
514
514
  await nextTick();
515
- await flushPromises();
516
- await doAwaitTimeout(1500);
515
+ await crudSubscribeResolvable[1].resolve();
516
+ await crudListResolvable[1].resolve();
517
+ await doAwaitNot({
518
+ obj: listSubscription.listIntent.state,
519
+ prop: "resolving",
520
+ });
517
521
  expect(crudSubscribeResolvable[0].promise.cancel).toHaveBeenCalledWith();
518
522
  expect(crudSubscribeResolvable[0].promise.cancel).toHaveBeenCalledTimes(1);
519
523
  expect(crudSubscribe).toHaveBeenCalledWith({
@@ -1,9 +1,10 @@
1
- import { effectScope, isReactive, isRef, onScopeDispose, reactive, unref, watch } from "vue";
2
- import { cloneDeep, isEqual } from "lodash";
1
+ import { identity, isEqual } from "lodash";
2
+ import { effectScope, nextTick, onScopeDispose, reactive, readonly, watch } from "vue";
3
+ import { deepUnref } from "vue-deepunref";
3
4
 
4
5
  /*
5
6
  * Calls your awaitable function with the arguments you pass in, when the watch arguments change and are all truthy.
6
- * Watch arguments can be an array or an object.
7
+ * Watch arguments should be a reactive object.
7
8
  * If the promise is not resolved before the watch arguments change again, the previous promise is cancelled.
8
9
  */
9
10
  export function useCancellableIntent({ awaitableWithCancel, watchArguments = {}, clearActiveOnResolved = true }) {
@@ -15,11 +16,12 @@ export function useCancellableIntent({ awaitableWithCancel, watchArguments = {},
15
16
  }
16
17
  const state = reactive({
17
18
  active: undefined,
19
+ resolving: undefined,
18
20
  errored: false,
19
21
  error: null,
20
22
  clearActiveOnResolved,
21
23
  });
22
- let previousWatchArguments = null,
24
+ let previousWatchValues = null,
23
25
  cancelFunction = null;
24
26
 
25
27
  const es = effectScope();
@@ -32,6 +34,7 @@ export function useCancellableIntent({ awaitableWithCancel, watchArguments = {},
32
34
  async function cancel() {
33
35
  if (cancelFunction) {
34
36
  state.active = false;
37
+ state.resolving = false;
35
38
  const cancelPromise = cancelFunction().catch(console.error);
36
39
  cancelFunction = null;
37
40
  return cancelPromise;
@@ -39,54 +42,58 @@ export function useCancellableIntent({ awaitableWithCancel, watchArguments = {},
39
42
  return false;
40
43
  }
41
44
 
42
- es.run(() => {
43
- watch(
44
- isReactive(watchArguments) || isRef(watchArguments) ? watchArguments : Object.values(watchArguments),
45
- () => {
46
- let newArguments = cloneDeep(watchArguments.map((arg) => unref(arg)));
47
- if (isEqual(unref(watchArguments), previousWatchArguments)) {
48
- return;
49
- }
50
- previousWatchArguments = newArguments;
51
- cancel().catch(console.error);
52
- if (Object.values(previousWatchArguments).every((v) => unref(v))) {
53
- state.errored = false;
54
- state.error = null;
55
- let awaitablePromise = awaitableWithCancel();
56
- state.active = true;
57
- if (awaitablePromise.cancel) {
58
- cancelFunction = async () => {
59
- state.active = false;
60
- cancelFunction = null;
61
- return awaitablePromise.cancel();
62
- };
63
- }
64
- awaitablePromise
65
- .then(() => {
66
- if (state.clearActiveOnResolved) {
67
- cancelFunction = null;
68
- state.active = false;
69
- }
70
- })
71
- .catch(async (err) => {
72
- await cancel();
73
- console.error(err);
74
- state.errored = true;
75
- state.error = err;
76
- throw err;
77
- });
78
- }
79
- },
80
- {
81
- deep: true,
82
- immediate: true,
45
+ const watchFn = () => {
46
+ let newWatchValues = deepUnref(Object.values(watchArguments));
47
+ if (isEqual(newWatchValues, previousWatchValues)) {
48
+ return;
49
+ }
50
+ previousWatchValues = newWatchValues;
51
+ cancel().catch(console.error);
52
+ if (Object.values(previousWatchValues).every(identity)) {
53
+ state.errored = false;
54
+ state.error = null;
55
+ let awaitablePromise = awaitableWithCancel();
56
+ state.active = true;
57
+ state.resolving = true;
58
+ if (awaitablePromise.cancel) {
59
+ cancelFunction = async () => {
60
+ state.active = false;
61
+ state.resolving = false;
62
+ cancelFunction = null;
63
+ return awaitablePromise.cancel();
64
+ };
83
65
  }
84
- );
66
+ awaitablePromise
67
+ .then(() => {
68
+ state.resolving = false;
69
+ if (state.clearActiveOnResolved) {
70
+ cancelFunction = null;
71
+ state.active = false;
72
+ }
73
+ })
74
+ .catch(async (err) => {
75
+ await cancel();
76
+ console.error(err);
77
+ state.errored = true;
78
+ state.error = err;
79
+ throw err;
80
+ });
81
+ }
82
+ };
83
+
84
+ es.run(() => {
85
+ watch(() => Object.values(watchArguments), watchFn, {
86
+ // this can't be immediate because subscribe wants to look at our state, which won't exist yet.
87
+ deep: true,
88
+ });
89
+
90
+ nextTick().then(watchFn);
85
91
 
86
92
  onScopeDispose(cancel);
87
93
  });
88
94
  return {
89
95
  state,
96
+ watchArguments: readonly(watchArguments),
90
97
  stop,
91
98
  cancel,
92
99
  };
package/use/list.js CHANGED
@@ -1,11 +1,12 @@
1
- import { effectScope, shallowReactive, shallowReadonly, toRef, watch } from "vue";
1
+ import { effectScope, reactive, shallowReactive, shallowReadonly, toRef, watch } from "vue";
2
2
  import { useListCalculated } from "./listCalculated";
3
3
  import { useListInstance } from "./listInstance";
4
4
  import { useListRelated } from "./listRelated";
5
5
  import { useListSubscription } from "./listSubscription";
6
+ import { usePagedListInstance } from "./paginatedListInstance";
6
7
 
7
8
  // the big brother of useObject, managing a chain of useList* instances.
8
- export const useList = ({ props, functions }) => {
9
+ export const useList = ({ props, functions, paged = false }) => {
9
10
  const managed = shallowReactive({
10
11
  listInstance: null,
11
12
  listSubscription: null,
@@ -14,7 +15,7 @@ export const useList = ({ props, functions }) => {
14
15
  });
15
16
  const es = effectScope();
16
17
 
17
- managed.listInstance = useListInstance({
18
+ managed.listInstance = (paged ? usePagedListInstance : useListInstance)({
18
19
  crudArgs: toRef(props, "crudArgs"),
19
20
  functions,
20
21
  retrieveArgs: toRef(props, "retrieveArgs"),
@@ -60,7 +61,7 @@ export const useList = ({ props, functions }) => {
60
61
  });
61
62
  };
62
63
 
63
- let exposedState;
64
+ const exposedState = reactive({});
64
65
 
65
66
  es.run(() => {
66
67
  watch(
@@ -73,31 +74,40 @@ export const useList = ({ props, functions }) => {
73
74
  intentPropsWatch,
74
75
  { immediate: true }
75
76
  );
76
- const getState = () =>
77
- managed.listCalculated?.state ||
78
- managed.listRelated?.state ||
79
- managed.listSubscription?.state ||
80
- managed.listInstance?.state;
81
- // used as proxy to have the properties and not be settable, so we only have to override get
82
- const proxyBase = shallowReadonly({
83
- loading: null,
84
- error: null,
85
- errored: null,
86
- objects: null,
87
- order: null,
88
- objectsInOrder: null,
89
- running: null,
90
- });
91
- exposedState = new Proxy(proxyBase, {
92
- get: (target, prop) => {
93
- return Reflect.get(getState(), prop);
77
+ const propertiesToRelay = [
78
+ "loading",
79
+ "error",
80
+ "errored",
81
+ "objects",
82
+ "order",
83
+ "objectsInOrder",
84
+ "running",
85
+ "relatedObjects",
86
+ "calculatedObjects",
87
+ ];
88
+ watch(
89
+ () =>
90
+ managed.listCalculated?.state ||
91
+ managed.listRelated?.state ||
92
+ managed.listSubscription?.state ||
93
+ managed.listInstance?.state,
94
+ (newState, oldState) => {
95
+ if (newState !== oldState && newState) {
96
+ propertiesToRelay.forEach((x) => {
97
+ exposedState[x] = toRef(newState, x);
98
+ });
99
+ }
94
100
  },
95
- });
101
+ {
102
+ immediate: true,
103
+ }
104
+ );
96
105
  });
97
106
 
98
107
  return {
108
+ // we manage the keys on both of these, so hands off the root.
99
109
  managed: shallowReadonly(managed),
100
- state: exposedState,
110
+ state: shallowReadonly(exposedState),
101
111
  effectScope: es,
102
112
  };
103
113
  };
@@ -19,11 +19,11 @@ export function useListCalculated({
19
19
  parentState,
20
20
  calculatedObjectsRules,
21
21
  calculatedObjectsPropertyName = "calculatedObjects", // NOT REACTIVE
22
+ passThroughPropertyNames = ["relatedObjects"], // NOT REACTIVE
22
23
  }) {
23
24
  const state = reactive({
24
25
  calculatedObjectsRules,
25
26
  calculatedObjectsObjects: {},
26
- objects: {},
27
27
  parentStateObjectsWatchRunning: false,
28
28
  calculatedObjectsWatchRunning: false,
29
29
  });
@@ -39,7 +39,6 @@ export function useListCalculated({
39
39
  );
40
40
  for (const removedKey of removedKeys) {
41
41
  delete state.calculatedObjectsObjects[removedKey];
42
- delete state.objects[removedKey];
43
42
  if (calculatedObjectsEffectScopes[removedKey]) {
44
43
  calculatedObjectsEffectScopes[removedKey].stop();
45
44
  delete calculatedObjectsEffectScopes[removedKey];
@@ -47,63 +46,46 @@ export function useListCalculated({
47
46
  }
48
47
  for (const addedKey of addedKeys) {
49
48
  state.calculatedObjectsObjects[addedKey] = {};
50
- state.objects[addedKey] = new Proxy(parentState.objects[addedKey], {
51
- get(target, prop, receiver) {
52
- if (prop === copn) {
53
- return state.calculatedObjectsObjects[addedKey];
54
- }
55
- return Reflect.get(target, prop, receiver);
56
- },
57
- ownKeys(target) {
58
- return Reflect.ownKeys(target).concat(copn);
59
- },
60
- });
61
49
  }
62
50
  state.parentStateObjectsWatchRunning = false;
63
51
  }
64
52
 
65
53
  function calculatedObjectsWatch() {
66
- if (state.calculatedObjectsRules === false) {
67
- return;
68
- }
69
- const calculatedObjectsRulesIsEmpty = isEmpty(state.calculatedObjectsRules);
54
+ const calculatedObjectsRulesIsEmpty = !state.calculatedObjectsRules || isEmpty(state.calculatedObjectsRules);
70
55
  for (const objectKey of Object.keys(state.calculatedObjectsObjects)) {
71
- const calculatedObjectsEffectScope = effectScope();
72
- calculatedObjectsEffectScope.run(() => {
73
- const calculatedObjectsObject = state.calculatedObjectsObjects[objectKey];
74
- const originalObject = parentState.objects[objectKey];
75
- if (!calculatedObjectsObject[copn]) {
76
- calculatedObjectsObject[copn] = {};
77
- }
78
- let removedRuleKeys, addedRuleKeys;
79
- if (!calculatedObjectsRulesIsEmpty) {
80
- ({ removedKeys: removedRuleKeys, addedKeys: addedRuleKeys } = keyDiff(
81
- Object.keys(state.calculatedObjectsRules),
82
- Object.keys(calculatedObjectsObject[copn])
83
- ));
84
- } else {
85
- if (isEmpty(calculatedObjectsObject[copn])) {
86
- return;
87
- }
88
- removedRuleKeys = Object.keys(calculatedObjectsObject[copn]);
89
- addedRuleKeys = [];
90
- }
91
- for (const removedRuleKey of removedRuleKeys) {
92
- delete calculatedObjectsObject[copn][removedRuleKey];
56
+ if (!calculatedObjectsEffectScopes[objectKey]) {
57
+ calculatedObjectsEffectScopes[objectKey] = effectScope();
58
+ }
59
+ const originalObject = parentState.objects[objectKey];
60
+ if (!state.calculatedObjectsObjects[objectKey]) {
61
+ state.calculatedObjectsObjects[objectKey] = {};
62
+ }
63
+ const calculatedObjectsObject = state.calculatedObjectsObjects[objectKey];
64
+ let removedRuleKeys, addedRuleKeys;
65
+ if (!calculatedObjectsRulesIsEmpty) {
66
+ ({ removedKeys: removedRuleKeys, addedKeys: addedRuleKeys } = keyDiff(
67
+ Object.keys(state.calculatedObjectsRules),
68
+ Object.keys(calculatedObjectsObject)
69
+ ));
70
+ } else {
71
+ if (isEmpty(calculatedObjectsObject)) {
72
+ return;
93
73
  }
74
+ removedRuleKeys = Object.keys(calculatedObjectsObject);
75
+ addedRuleKeys = [];
76
+ }
77
+ for (const removedRuleKey of removedRuleKeys) {
78
+ delete calculatedObjectsObject[removedRuleKey];
79
+ }
80
+ calculatedObjectsEffectScopes[objectKey].run(() => {
94
81
  for (const addedRuleKey of addedRuleKeys) {
95
- calculatedObjectsObject[copn][addedRuleKey] = computed(() => {
82
+ calculatedObjectsObject[addedRuleKey] = computed(() => {
96
83
  return state.calculatedObjectsRules?.[addedRuleKey]?.(originalObject);
97
84
  });
98
85
  }
99
86
  });
100
- if (calculatedObjectsEffectScopes[objectKey]) {
101
- calculatedObjectsEffectScopes[objectKey].stop();
102
- }
103
- calculatedObjectsEffectScopes[objectKey] = calculatedObjectsEffectScope;
104
87
  }
105
88
  state.calculatedObjectsWatchRunning = false;
106
- parentStateObjectsWatch();
107
89
  }
108
90
 
109
91
  let watchesRunning = null;
@@ -118,7 +100,12 @@ export function useListCalculated({
118
100
  state.retrieveArgs = toRef(parentState, "retrieveArgs");
119
101
  state.listArgs = toRef(parentState, "listArgs");
120
102
  state.order = toRef(parentState, "order");
121
- state.objectsInOrder = computed(() => state.order.map((id) => state.objects[id]));
103
+ state.objects = toRef(parentState, "objects");
104
+ state.objectsInOrder = toRef(parentState, "objectsInOrder");
105
+ state[copn] = toRef(state, "calculatedObjectsObjects");
106
+ for (let key in passThroughPropertyNames) {
107
+ state[key] = toRef(parentState, key);
108
+ }
122
109
 
123
110
  watch(() => Object.keys(parentState.objects), parentStateObjectsWatch, { immediate: true });
124
111
  watch(
@@ -134,7 +121,11 @@ export function useListCalculated({
134
121
  );
135
122
 
136
123
  watchesRunning = useWatchesRunning({
137
- triggerRefs: [computed(() => (!isEmpty(state.calculatedObjectsRules) ? parentState.loading : false))],
124
+ triggerRefs: [
125
+ computed(() =>
126
+ state.calculatedObjectsRules && !isEmpty(state.calculatedObjectsRules) ? parentState.loading : false
127
+ ),
128
+ ],
138
129
  watchSentinelRefs: [
139
130
  toRef(state, "parentStateObjectsWatchRunning"),
140
131
  toRef(state, "calculatedObjectsWatchRunning"),
@@ -16,11 +16,11 @@ export function useListRelated({
16
16
  parentState,
17
17
  relatedObjectsRules,
18
18
  relatedObjectsPropertyName = "relatedObjects", // NOT REACTIVE
19
+ passThroughPropertyNames = ["calculatedObjects"], // NOT REACTIVE
19
20
  }) {
20
21
  const state = reactive({
21
22
  relatedObjectsRules: relatedObjectsRules,
22
23
  relatedObjectsObjects: {},
23
- objects: {},
24
24
  parentStateObjectsWatchRunning: false,
25
25
  relatedObjectsWatchRunning: false,
26
26
  });
@@ -36,7 +36,6 @@ export function useListRelated({
36
36
  );
37
37
  for (const removedKey of removedKeys) {
38
38
  delete state.relatedObjectsObjects[removedKey];
39
- delete state.objects[removedKey];
40
39
  if (relatedObjectsEffectScopes[removedKey]) {
41
40
  relatedObjectsEffectScopes[removedKey].stop();
42
41
  delete relatedObjectsEffectScopes[removedKey];
@@ -44,69 +43,35 @@ export function useListRelated({
44
43
  }
45
44
  for (const addedKey of addedKeys) {
46
45
  state.relatedObjectsObjects[addedKey] = {};
47
- state.objects[addedKey] = new Proxy(parentState.objects[addedKey], {
48
- get(target, prop, receiver) {
49
- if (prop === ropn) {
50
- return state.relatedObjectsObjects[addedKey];
51
- }
52
- return Reflect.get(target, prop, receiver);
53
- },
54
- ownKeys(target) {
55
- return Reflect.ownKeys(target).concat(ropn);
56
- },
57
- has(target, prop) {
58
- if (prop === ropn) {
59
- return true;
60
- }
61
- return Reflect.has(target, prop);
62
- },
63
- getOwnPropertyDescriptor(target, p) {
64
- if (p === ropn) {
65
- return {
66
- configurable: true,
67
- enumerable: true,
68
- value: state.relatedObjectsObjects[addedKey],
69
- writable: true,
70
- };
71
- }
72
- return Reflect.getOwnPropertyDescriptor(target, p);
73
- },
74
- defineProperty() {
75
- return false;
76
- },
77
- });
78
46
  }
47
+ state.parentStateObjectsWatchRunning = false;
79
48
  }
80
49
 
81
50
  function relatedObjectsWatch() {
82
- if (state.relatedObjectsRules === false) {
83
- return;
84
- }
85
- const relatedObjectsRulesIsEmpty = isEmpty(state.relatedObjectsRules);
51
+ const relatedObjectsRulesIsEmpty = !state.relatedObjectsRules || isEmpty(state.relatedObjectsRules);
86
52
  for (const objectKey of Object.keys(state.relatedObjectsObjects)) {
87
- if (relatedObjectsEffectScopes[objectKey]) {
88
- relatedObjectsEffectScopes[objectKey].stop();
89
- }
90
- const relatedObjectsEffectScope = effectScope();
91
- relatedObjectsEffectScope.run(() => {
92
- const relatedObjectsObject = state.relatedObjectsObjects[objectKey];
93
- const originalObject = parentState.objects[objectKey];
94
- let removedRuleKeys, addedRuleKeys;
95
- if (!relatedObjectsRulesIsEmpty) {
96
- ({ removedKeys: removedRuleKeys, addedKeys: addedRuleKeys } = keyDiff(
97
- Object.keys(state.relatedObjectsRules),
98
- Object.keys(relatedObjectsObject)
99
- ));
100
- } else {
101
- if (isEmpty(relatedObjectsObject)) {
102
- return;
103
- }
104
- removedRuleKeys = Object.keys(relatedObjectsObject);
105
- addedRuleKeys = [];
106
- }
107
- for (const removedRuleKey of removedRuleKeys) {
108
- delete relatedObjectsObject[removedRuleKey];
53
+ const relatedObjectsObject = state.relatedObjectsObjects[objectKey];
54
+ const originalObject = parentState.objects[objectKey];
55
+ let removedRuleKeys, addedRuleKeys;
56
+ if (!relatedObjectsRulesIsEmpty) {
57
+ ({ removedKeys: removedRuleKeys, addedKeys: addedRuleKeys } = keyDiff(
58
+ Object.keys(state.relatedObjectsRules),
59
+ Object.keys(relatedObjectsObject)
60
+ ));
61
+ } else {
62
+ if (isEmpty(relatedObjectsObject)) {
63
+ return;
109
64
  }
65
+ removedRuleKeys = Object.keys(relatedObjectsObject);
66
+ addedRuleKeys = [];
67
+ }
68
+ for (const removedRuleKey of removedRuleKeys) {
69
+ delete relatedObjectsObject[removedRuleKey];
70
+ }
71
+ if (!relatedObjectsEffectScopes[objectKey]) {
72
+ relatedObjectsEffectScopes[objectKey] = effectScope();
73
+ }
74
+ relatedObjectsEffectScopes[objectKey].run(() => {
110
75
  for (const addedRuleKey of addedRuleKeys) {
111
76
  relatedObjectsObject[addedRuleKey] = computed(() => {
112
77
  // deal with computed objects being passed.
@@ -126,9 +91,8 @@ export function useListRelated({
126
91
  });
127
92
  }
128
93
  });
129
- relatedObjectsEffectScopes[objectKey] = relatedObjectsEffectScope;
130
94
  }
131
- parentStateObjectsWatch();
95
+ state.relatedObjectsWatchRunning = false;
132
96
  }
133
97
 
134
98
  let watchesRunning = null;
@@ -143,7 +107,12 @@ export function useListRelated({
143
107
  state.retrieveArgs = toRef(parentState, "retrieveArgs");
144
108
  state.listArgs = toRef(parentState, "listArgs");
145
109
  state.order = toRef(parentState, "order");
146
- state.objectsInOrder = computed(() => state.order.map((id) => state.objects[id]));
110
+ state.objects = toRef(parentState, "objects");
111
+ state.objectsInOrder = toRef(parentState, "objectsInOrder");
112
+ state[ropn] = toRef(state, "relatedObjectsObjects");
113
+ for (let key in passThroughPropertyNames) {
114
+ state[key] = toRef(parentState, key);
115
+ }
147
116
 
148
117
  watch(() => Object.keys(parentState.objects), parentStateObjectsWatch, { immediate: true });
149
118
  watch(
@@ -156,7 +125,11 @@ export function useListRelated({
156
125
  );
157
126
 
158
127
  watchesRunning = useWatchesRunning({
159
- triggerRefs: [computed(() => (!isEmpty(state.relatedObjectsRules) ? parentState.loading : false))],
128
+ triggerRefs: [
129
+ computed(() =>
130
+ state.relatedObjectsRules && !isEmpty(state.relatedObjectsRules) ? parentState.loading : false
131
+ ),
132
+ ],
160
133
  watchSentinelRefs: [
161
134
  toRef(state, "parentStateObjectsWatchRunning"),
162
135
  toRef(state, "relatedObjectsWatchRunning"),
@@ -167,11 +167,11 @@ export function useListSubscription({ listInstance, crudArgs, listArgs, retrieve
167
167
  catchPromise.cancel = subscribePromise.cancel;
168
168
  return catchPromise;
169
169
  },
170
- watchArguments: [
171
- toRef(state, "intendToSubscribe"),
172
- toRef(listInstance.state, "listArgs"),
173
- toRef(state, "retrieveArgs"),
174
- ],
170
+ watchArguments: reactive({
171
+ intendToSubscribe: toRef(state, "intendToSubscribe"),
172
+ listArgs: toRef(listInstance.state, "listArgs"),
173
+ retrieveArgs: toRef(listInstance.state, "retrieveArgs"),
174
+ }),
175
175
  clearActiveOnResolved: false,
176
176
  });
177
177
 
@@ -182,12 +182,11 @@ export function useListSubscription({ listInstance, crudArgs, listArgs, retrieve
182
182
  listInstance.clearList();
183
183
  return listInstance.list();
184
184
  },
185
- watchArguments: [
186
- toRef(state, "intendToList"),
187
- toRef(listInstance.state, "listArgs"),
188
- toRef(state, "retrieveArgs"),
189
- ],
190
- nameOnLog: "listIntent",
185
+ watchArguments: reactive({
186
+ intendToList: toRef(state, "intendToList"),
187
+ listArgs: toRef(listInstance.state, "listArgs"),
188
+ retrieveArgs: toRef(listInstance.state, "retrieveArgs"),
189
+ }),
191
190
  });
192
191
  });
193
192
 
package/use/object.js CHANGED
@@ -1,4 +1,4 @@
1
- import { effectScope, shallowReactive, shallowReadonly, toRef, watch } from "vue";
1
+ import { effectScope, reactive, shallowReactive, shallowReadonly, toRef, watch } from "vue";
2
2
  import { useObjectCalculated } from "./objectCalculated";
3
3
  import { useObjectInstance } from "./objectInstance";
4
4
  import { useObjectRelated } from "./objectRelated";
@@ -60,7 +60,7 @@ export const useObject = ({ props, functions }) => {
60
60
  });
61
61
  };
62
62
 
63
- let exposedState;
63
+ const exposedState = reactive({});
64
64
 
65
65
  es.run(() => {
66
66
  watch(
@@ -72,30 +72,39 @@ export const useObject = ({ props, functions }) => {
72
72
  intentPropsWatch,
73
73
  { immediate: true }
74
74
  );
75
- const getState = () =>
76
- managed.objectCalculated?.state ||
77
- managed.objectRelated?.state ||
78
- managed.objectSubscription?.state ||
79
- managed.objectInstance.state;
80
- // used as proxy to have the properties and not be settable, so we only have to override get
81
- const proxyBase = shallowReadonly({
82
- loading: null,
83
- error: null,
84
- errored: null,
85
- object: null,
86
- running: null,
87
- });
88
- exposedState = new Proxy(proxyBase, {
89
- // get values from the current state
90
- get(target, prop) {
91
- return Reflect.get(getState(), prop);
75
+
76
+ const propertiesToRelay = [
77
+ "loading",
78
+ "error",
79
+ "errored",
80
+ "object",
81
+ "running",
82
+ "relatedObject",
83
+ "calculatedObject",
84
+ ];
85
+ watch(
86
+ () =>
87
+ managed.objectCalculated?.state ||
88
+ managed.objectRelated?.state ||
89
+ managed.objectSubscription?.state ||
90
+ managed.objectInstance.state,
91
+ (newState, oldState) => {
92
+ if (newState !== oldState && newState) {
93
+ propertiesToRelay.forEach((x) => {
94
+ exposedState[x] = toRef(newState, x);
95
+ });
96
+ }
92
97
  },
93
- });
98
+ {
99
+ immediate: true,
100
+ }
101
+ );
94
102
  });
95
103
 
96
104
  return {
105
+ // we manage the keys on both of these, so hands off the root.
97
106
  managed: shallowReadonly(managed),
98
- state: exposedState,
107
+ state: shallowReadonly(exposedState),
99
108
  effectScope: es,
100
109
  };
101
110
  };
@@ -17,11 +17,11 @@ export function useObjectCalculated({
17
17
  parentState,
18
18
  calculatedObjectRules,
19
19
  calculatedObjectPropertyName = "calculatedObject", // NOT REACTIVE
20
+ passThroughPropertyNames = ["relatedObject"], // NOT REACTIVE
20
21
  }) {
21
22
  const state = reactive({
22
23
  calculatedObjectRules,
23
24
  calculatedObjectObjects: {},
24
- object: {},
25
25
  parentStateObjectWatchRunning: false,
26
26
  calculatedObjectWatchRunning: false,
27
27
  });
@@ -36,41 +36,15 @@ export function useObjectCalculated({
36
36
  const es = effectScope();
37
37
 
38
38
  es.run(() => {
39
- state.object = new Proxy(parentState.object, {
40
- get(target, key, receiver) {
41
- if (key === copn) {
42
- return state.calculatedObjectObjects;
43
- }
44
- return Reflect.get(target, key, receiver);
45
- },
46
- ownKeys(target) {
47
- return Reflect.ownKeys(target).concat(copn);
48
- },
49
- has(target, key) {
50
- if (key === copn) {
51
- return true;
52
- }
53
- return Reflect.has(target, key);
54
- },
55
- getOwnPropertyDescriptor(target, key) {
56
- if (key === copn) {
57
- return {
58
- configurable: true,
59
- enumerable: true,
60
- value: state.calculatedObjectObjects,
61
- writable: true,
62
- };
63
- }
64
- return Reflect.getOwnPropertyDescriptor(target, key);
65
- },
66
- defineProperty() {
67
- return false;
68
- },
69
- });
70
39
  state.loading = toRef(parentState, "loading");
71
40
  state.error = toRef(parentState, "error");
72
41
  state.errored = toRef(parentState, "errored");
73
42
  state.deleted = toRef(parentState, "deleted");
43
+ state.object = toRef(parentState, "object");
44
+ state[copn] = toRef(state, "calculatedObjectObjects");
45
+ for (let key in passThroughPropertyNames) {
46
+ state[key] = toRef(parentState, key);
47
+ }
74
48
 
75
49
  watch([() => state.calculatedObjectRules && Object.keys(state.calculatedObjectRules)], () => {
76
50
  let addedKeys = [],
@@ -17,11 +17,11 @@ export function useObjectRelated({
17
17
  parentState,
18
18
  relatedObjectRules,
19
19
  relatedObjectPropertyName = "relatedObject", // NOT REACTIVE
20
+ passThroughPropertyNames = ["calculatedObject"], // NOT REACTIVE
20
21
  }) {
21
22
  const state = reactive({
22
23
  relatedObjectRules,
23
24
  relatedObjectObjects: {},
24
- object: {},
25
25
  parentStateObjectWatchRunning: false,
26
26
  relatedObjectWatchRunning: false,
27
27
  });
@@ -35,41 +35,15 @@ export function useObjectRelated({
35
35
  const es = effectScope();
36
36
 
37
37
  es.run(() => {
38
- state.object = new Proxy(parentState.object, {
39
- get(target, key, receiver) {
40
- if (key === ropn) {
41
- return state.relatedObjectObjects;
42
- }
43
- return Reflect.get(target, key, receiver);
44
- },
45
- ownKeys(target) {
46
- return Reflect.ownKeys(target).concat(ropn);
47
- },
48
- has(target, key) {
49
- if (key === ropn) {
50
- return true;
51
- }
52
- return Reflect.has(target, key);
53
- },
54
- getOwnPropertyDescriptor(target, key) {
55
- if (key === ropn) {
56
- return {
57
- configurable: true,
58
- enumerable: true,
59
- value: state.relatedObjectObjects,
60
- writable: true,
61
- };
62
- }
63
- return Reflect.getOwnPropertyDescriptor(target, key);
64
- },
65
- defineProperty() {
66
- return false;
67
- },
68
- });
69
38
  state.loading = toRef(parentState, "loading");
70
39
  state.error = toRef(parentState, "error");
71
40
  state.errored = toRef(parentState, "errored");
72
41
  state.deleted = toRef(parentState, "deleted");
42
+ state.object = toRef(parentState, "object");
43
+ state[ropn] = toRef(state, "relatedObjectObjects");
44
+ for (let key in passThroughPropertyNames) {
45
+ state[key] = toRef(parentState, key);
46
+ }
73
47
 
74
48
  watch([() => state.relatedObjectRules && Object.keys(state.relatedObjectRules)], () => {
75
49
  let addedRuleKeys = [],
@@ -155,22 +155,21 @@ export function useObjectSubscription({ objectInstance, crudArgs, id, retrieveAr
155
155
 
156
156
  subscribeIntent = useCancellableIntent({
157
157
  awaitableWithCancel: subscribe,
158
- watchArguments: [
159
- toRef(state, "intendToSubscribe"),
160
- toRef(objectInstance.state, "id"),
161
- toRef(state, "retrieveArgs"),
162
- ],
158
+ watchArguments: reactive({
159
+ intendToSubscribe: toRef(state, "intendToSubscribe"),
160
+ listArgs: toRef(objectInstance.state, "id"),
161
+ retrieveArgs: toRef(objectInstance.state, "retrieveArgs"),
162
+ }),
163
163
  clearActiveOnResolved: false,
164
164
  });
165
165
 
166
166
  retrieveIntent = useCancellableIntent({
167
167
  awaitableWithCancel: objectInstance.retrieve,
168
- watchArguments: [
169
- toRef(state, "intendToRetrieve"),
170
- toRef(objectInstance.state, "id"),
171
- toRef(state, "retrieveArgs"),
172
- ],
173
- nameForLog: "retrieveIntent",
168
+ watchArguments: reactive({
169
+ intendToSubscribe: toRef(state, "intendToRetrieve"),
170
+ listArgs: toRef(objectInstance.state, "id"),
171
+ retrieveArgs: toRef(objectInstance.state, "retrieveArgs"),
172
+ }),
174
173
  });
175
174
  });
176
175
 
package/utils/index.js CHANGED
@@ -4,5 +4,4 @@ export * from "./getFakeId";
4
4
  export * from "./keyDiff";
5
5
  export * from "./loadingCombine";
6
6
  export * from "./set";
7
- export * from "./unrefAndToRawDeep";
8
7
  export * from "./watches";
package/utils/watches.js CHANGED
@@ -42,6 +42,8 @@ export class AwaitTimeout {
42
42
  });
43
43
  this.timeout = timeout;
44
44
  this.timeoutId = null;
45
+ // prebuild the exception for a more useful stack.
46
+ this.cancelledError = new AwaitTimeoutError("Cancelled", "timeout_cancelled");
45
47
  }
46
48
 
47
49
  start() {
@@ -65,7 +67,7 @@ export class AwaitTimeout {
65
67
  if (this.timeoutId) {
66
68
  clearTimeout(this.timeoutId);
67
69
  delete this.timeoutId;
68
- this.reject(new AwaitTimeoutError("Cancelled", "timeout_cancelled"));
70
+ this.reject(this.cancelledError);
69
71
  }
70
72
  if (this.resolve) {
71
73
  delete this.resolve;
@@ -94,13 +96,15 @@ export class AwaitNot {
94
96
  this.prop = prop;
95
97
  this.trueISW = new ImmediateStopWatch();
96
98
  this.falseISW = new ImmediateStopWatch();
99
+ // prebuild the exception for a more useful stack.
100
+ this.timeoutError = new AwaitNotError("Timeout", "timeout");
97
101
  }
98
102
 
99
103
  start() {
100
104
  this.timeout.promise
101
105
  .then(() => {
102
106
  this.stop();
103
- this.reject(new AwaitNotError("Timeout", "timeout"));
107
+ this.reject(this.timeoutError);
104
108
  })
105
109
  .catch((err) => {
106
110
  this.stop();
@@ -1,170 +0,0 @@
1
- import { reactive, ref, toRef, unref } from "vue";
2
- import { circular, unrefAndToRawDeep } from "../../../utils";
3
-
4
- describe("unrefAndToRawDeep", () => {
5
- it("should unref refs", () => {
6
- const obj = {
7
- a: ref(1),
8
- b: ref(true),
9
- c: ref("foo"),
10
- d: ref(null),
11
- };
12
- expect(unrefAndToRawDeep(obj)).toEqual({
13
- a: 1,
14
- b: true,
15
- c: "foo",
16
- d: null,
17
- });
18
- });
19
- it("should toRaw reactive", () => {
20
- const obj = {
21
- state: reactive({
22
- a: 1,
23
- b: true,
24
- c: "foo",
25
- d: null,
26
- }),
27
- parentState: reactive({
28
- a: 1,
29
- b: true,
30
- c: "foo",
31
- d: null,
32
- }),
33
- };
34
- expect(unrefAndToRawDeep(obj)).toEqual({
35
- state: {
36
- a: 1,
37
- b: true,
38
- c: "foo",
39
- d: null,
40
- },
41
- parentState: {
42
- a: 1,
43
- b: true,
44
- c: "foo",
45
- d: null,
46
- },
47
- });
48
- });
49
- it("should handle arrays", () => {
50
- const arr = [ref(1), ref(true), ref("foo"), ref(null)];
51
- const arr2 = [
52
- reactive({
53
- a: 1,
54
- b: true,
55
- c: "foo",
56
- d: null,
57
- }),
58
- reactive({
59
- a: 2,
60
- b: false,
61
- c: "bar",
62
- d: NaN,
63
- }),
64
- ];
65
- expect(unrefAndToRawDeep(arr)).toEqual([1, true, "foo", null]);
66
- expect(unrefAndToRawDeep(arr2)).toEqual([
67
- {
68
- a: 1,
69
- b: true,
70
- c: "foo",
71
- d: null,
72
- },
73
- {
74
- a: 2,
75
-
76
- b: false,
77
- c: "bar",
78
- d: NaN,
79
- },
80
- ]);
81
- });
82
- it("should toRaw reactive with circular ref", () => {
83
- const state = reactive({
84
- objects: {
85
- 1: {
86
- id: 1,
87
- name: "one",
88
- parent: 2,
89
- children: [],
90
- refProperty: ref(null),
91
- },
92
- 2: {
93
- id: 2,
94
- name: "two",
95
- parent: null,
96
- children: [1],
97
- refProperty: ref(null),
98
- },
99
- 3: {
100
- id: 3,
101
- name: "three",
102
- parent: null,
103
- children: [],
104
- },
105
- },
106
- });
107
- state.objects["1"].relatedObjects = reactive({
108
- parent: toRef(state.objects, "2"),
109
- });
110
- state.objects["2"].relatedObjects = reactive({
111
- children: [toRef(state.objects, "1")],
112
- });
113
- state.objects["1"].refProperty = toRef(state.objects["3"], "name");
114
- state.objects["2"].refProperty = toRef(state.objects["3"], "name");
115
- expect(state.objects["1"]).toBe(unref(state.objects["2"].relatedObjects.children[0]));
116
- expect(state.objects["2"]).toBe(state.objects["1"].relatedObjects.parent);
117
- expect(state.objects["1"].refProperty).toBe(state.objects["3"].name);
118
- expect(state.objects["2"].refProperty).toBe(state.objects["3"].name);
119
- expect(unrefAndToRawDeep(state)).toEqual({
120
- objects: {
121
- 1: {
122
- id: 1,
123
- name: "one",
124
- parent: 2,
125
- children: [],
126
- refProperty: "three",
127
- relatedObjects: {
128
- parent: {
129
- id: 2,
130
- name: "two",
131
- parent: null,
132
- children: [1],
133
- refProperty: "three",
134
- relatedObjects: {
135
- children: [circular],
136
- },
137
- },
138
- },
139
- },
140
- 2: {
141
- id: 2,
142
- name: "two",
143
- parent: null,
144
- children: [1],
145
- refProperty: "three",
146
- relatedObjects: {
147
- children: [
148
- {
149
- id: 1,
150
- name: "one",
151
- parent: 2,
152
- children: [],
153
- refProperty: "three",
154
- relatedObjects: {
155
- parent: circular,
156
- },
157
- },
158
- ],
159
- },
160
- },
161
- 3: {
162
- id: 3,
163
- name: "three",
164
- parent: null,
165
- children: [],
166
- },
167
- },
168
- });
169
- });
170
- });
@@ -1,49 +0,0 @@
1
- import { isProxy, isRef, toRaw, unref } from "vue";
2
- import { isArray, isUndefined } from "lodash";
3
- import { isObjectLike } from "lodash/lang";
4
-
5
- export const circular = Symbol("circular");
6
-
7
- export function unrefAndToRawDeep(obj) {
8
- if (isUndefined(obj)) {
9
- return obj;
10
- }
11
- const seen = new Set();
12
- const returnValue = isArray(obj) ? [] : {};
13
- const queue = [];
14
- for (const [key, value] of Object.entries(obj)) {
15
- queue.push([returnValue, key, value]);
16
- }
17
- while (queue.length) {
18
- const [writePosition, key, value] = queue.shift();
19
- if (seen.has(value)) {
20
- // if (seen.some((item) => item === value)) {
21
- writePosition[key] = circular;
22
- continue;
23
- }
24
- let raw = value;
25
- if (isRef(raw)) {
26
- const refValue = unref(raw);
27
- // primitive values are not added to the seen set
28
- if (isObjectLike(refValue)) {
29
- seen.add(raw);
30
- }
31
- raw = unref(raw);
32
- }
33
- if (isProxy(raw)) {
34
- // this doesn't seem to do anything for the current test case.
35
- // seen.add(raw);
36
- raw = toRaw(raw);
37
- }
38
- if (!isObjectLike(raw)) {
39
- // primitive values don't need to add to the queue
40
- writePosition[key] = raw;
41
- continue;
42
- }
43
- writePosition[key] = isArray(raw) ? [] : {};
44
- for (const [nextKey, nextValue] of Object.entries(raw)) {
45
- queue.push([writePosition[key], nextKey, nextValue]);
46
- }
47
- }
48
- return returnValue;
49
- }