@arrai-innovations/reactive-helpers 11.0.4 → 11.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": "11.0.4",
3
+ "version": "11.1.0",
4
4
  "description": "VueJS 3 utility composition functions to help manipulate objects and lists.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  import { useListFilters, useListSort } from "../../../use/index.js";
2
2
  import { doAwaitNot } from "../../../utils/watches.js";
3
- import { ref, unref, reactive } from "vue";
3
+ import { reactive, ref, unref } from "vue";
4
4
  import { deepUnref } from "vue-deepunref";
5
5
 
6
6
  describe("use/listFilter", () => {
@@ -122,6 +122,10 @@ describe("use/listFilter", () => {
122
122
  const filter = useListFilter({
123
123
  parentState: listInstance.state,
124
124
  });
125
+ await doAwaitNot({
126
+ obj: filter.state,
127
+ prop: "running",
128
+ });
125
129
  expect(filter.state.objects).toEqual(listInstance.state.objects);
126
130
  });
127
131
  describe("useListFilter operates on parentState modified by useListSort", () => {
@@ -373,10 +377,6 @@ describe("use/listFilter", () => {
373
377
  parentState: filter1.state,
374
378
  allowedFilter: filter2AllowedFilter,
375
379
  });
376
- await doAwaitNot({
377
- obj: filter1.state,
378
- prop: "running",
379
- });
380
380
  await doAwaitNot({
381
381
  obj: filter2.state,
382
382
  prop: "running",
@@ -386,10 +386,6 @@ describe("use/listFilter", () => {
386
386
  expect(filter2.state.objects).toEqual({});
387
387
  expect(filter2.state.order).toEqual([]);
388
388
  list.addListObject({ id: 1, name: "one", has_things: true, has_stuff: true, has_other_stuff: true });
389
- await doAwaitNot({
390
- obj: filter1.state,
391
- prop: "running",
392
- });
393
389
  await doAwaitNot({
394
390
  obj: filter2.state,
395
391
  prop: "running",
@@ -403,10 +399,6 @@ describe("use/listFilter", () => {
403
399
  });
404
400
  expect(filter2.state.order).toEqual(["1"]);
405
401
  list.addListObject({ id: 2, name: "two", has_things: true, has_stuff: true, has_other_stuff: false });
406
- await doAwaitNot({
407
- obj: filter1.state,
408
- prop: "running",
409
- });
410
402
  await doAwaitNot({
411
403
  obj: filter2.state,
412
404
  prop: "running",
@@ -421,10 +413,6 @@ describe("use/listFilter", () => {
421
413
  });
422
414
  expect(filter2.state.order).toEqual(["1"]);
423
415
  list.addListObject({ id: 3, name: "three", has_things: true, has_stuff: false, has_other_stuff: true });
424
- await doAwaitNot({
425
- obj: filter1.state,
426
- prop: "running",
427
- });
428
416
  await doAwaitNot({
429
417
  obj: filter2.state,
430
418
  prop: "running",
@@ -439,10 +427,6 @@ describe("use/listFilter", () => {
439
427
  });
440
428
  expect(filter2.state.order).toEqual(["1"]);
441
429
  list.addListObject({ id: 4, name: "four", has_things: true, has_stuff: false, has_other_stuff: false });
442
- await doAwaitNot({
443
- obj: filter1.state,
444
- prop: "running",
445
- });
446
430
  await doAwaitNot({
447
431
  obj: filter2.state,
448
432
  prop: "running",
@@ -458,10 +442,6 @@ describe("use/listFilter", () => {
458
442
  expect(filter2.state.order).toEqual(["1"]);
459
443
  list.state.objects[1].has_stuff = false;
460
444
  list.state.objects[2].has_other_stuff = true;
461
- await doAwaitNot({
462
- obj: filter1.state,
463
- prop: "running",
464
- });
465
445
  await doAwaitNot({
466
446
  obj: filter2.state,
467
447
  prop: "running",
@@ -478,10 +458,6 @@ describe("use/listFilter", () => {
478
458
  list.addListObject({ id: 6, name: "six", has_things: true, has_stuff: true, has_other_stuff: false });
479
459
  list.addListObject({ id: 7, name: "seven", has_things: false, has_stuff: true, has_other_stuff: true });
480
460
  list.addListObject({ id: 8, name: "eight", has_things: true, has_stuff: true, has_other_stuff: true });
481
- await doAwaitNot({
482
- obj: filter1.state,
483
- prop: "running",
484
- });
485
461
  await doAwaitNot({
486
462
  obj: filter2.state,
487
463
  prop: "running",
@@ -381,7 +381,7 @@ describe("use/listInstance.spec.js", function () {
381
381
  listInstance.addListObject(listObject);
382
382
  expect(() => listInstance.addListObject({ listObject })).toThrowError(ListError);
383
383
  });
384
- it("succeeded", function () {
384
+ it("succeeded", async function () {
385
385
  const listInstance = useListInstance({ props: {} });
386
386
  const newId = listInstance.getFakeId();
387
387
  listObject.id = newId;
@@ -389,6 +389,18 @@ describe("use/listInstance.spec.js", function () {
389
389
  expect(listInstance.state.objects[newId]).toEqual(listObject);
390
390
  const reactiveProxy = listInstance.state.objects[newId];
391
391
  expect(isReactive(reactiveProxy)).toBe(true);
392
+ expect(reactiveProxy.id).toBe(newId);
393
+ expect(reactiveProxy.__str__).toBe("nvm");
394
+ expect(reactiveProxy.name).toBe("nvm");
395
+ expect(listInstance.state.objectsInOrder).toEqual([]);
396
+ // order updates immediately due to not being proxied through objectsInOrderRefs
397
+ expect(listInstance.state.order).toStrictEqual([newId.toString()]);
398
+ await doAwaitNot({
399
+ obj: listInstance.state,
400
+ prop: "running",
401
+ });
402
+ expect(listInstance.state.objectsInOrder).toEqual([reactiveProxy]);
403
+ expect(listInstance.state.order).toEqual([newId.toString()]);
392
404
  });
393
405
  });
394
406
  describe("updateListObject", function () {
@@ -401,7 +413,7 @@ describe("use/listInstance.spec.js", function () {
401
413
  });
402
414
  it("succeeds", async function () {
403
415
  const listInstance = useListInstance({
404
- props: { listArgs: { user: 1 }, retrieveArgs: { fields: fields } },
416
+ props: {},
405
417
  });
406
418
  let crudListResolve;
407
419
  const crudListPromise = new Promise((resolve) => {
@@ -418,10 +430,70 @@ describe("use/listInstance.spec.js", function () {
418
430
  passedPageCallback(crudListResolvedPage1);
419
431
  crudListResolve();
420
432
 
433
+ await doAwaitNot({
434
+ obj: listInstance.state,
435
+ prop: "running",
436
+ });
437
+
421
438
  let updateObject = listInstance.state.objects["1"];
422
439
  updateObject.name = "updated";
423
440
  listInstance.updateListObject(updateObject);
424
441
  expect(listInstance.state.objects["1"]).toEqual(updateObject);
442
+ expect(listInstance.state.objectsInOrder[0].name).toBe("updated");
443
+ await doAwaitNot({
444
+ obj: listInstance.state,
445
+ prop: "running",
446
+ });
447
+ expect(listInstance.state.objectsInOrder[0].name).toBe("updated");
448
+ });
449
+ });
450
+ describe("deleteListObject", function () {
451
+ it("errors", function () {
452
+ const listInstance = useListInstance({ props: {} });
453
+ expect(() => listInstance.deleteListObject(-50002000)).toThrowError(ListError);
454
+ });
455
+ it("succeeds", async function () {
456
+ const listInstance = useListInstance({
457
+ props: {},
458
+ });
459
+ let crudListResolve;
460
+ const crudListPromise = new Promise((resolve) => {
461
+ crudListResolve = resolve;
462
+ });
463
+ let passedPageCallback;
464
+ globalList.mockImplementation(({ pageCallback }) => {
465
+ passedPageCallback = pageCallback;
466
+ return crudListPromise;
467
+ });
468
+
469
+ listInstance.list();
470
+
471
+ passedPageCallback(crudListResolvedPage1);
472
+ crudListResolve();
473
+
474
+ await doAwaitNot({
475
+ obj: listInstance.state,
476
+ prop: "running",
477
+ });
478
+
479
+ listInstance.deleteListObject(1);
480
+ expect(listInstance.state.objects["1"]).toBeUndefined();
481
+ expect(listInstance.state.objectsInOrder).toStrictEqual([
482
+ undefined,
483
+ crudListResolvedObjects1["2"],
484
+ crudListResolvedObjects1["3"],
485
+ ]);
486
+ // order updates immediately due to not being proxied through objectsInOrderRefs
487
+ expect(listInstance.state.order).toStrictEqual(["2", "3"]);
488
+ await doAwaitNot({
489
+ obj: listInstance.state,
490
+ prop: "running",
491
+ });
492
+ expect(listInstance.state.objectsInOrder).toEqual([
493
+ crudListResolvedObjects1["2"],
494
+ crudListResolvedObjects1["3"],
495
+ ]);
496
+ expect(listInstance.state.order).toEqual(["2", "3"]);
425
497
  });
426
498
  });
427
499
  describe("getFakeId", function () {
@@ -1,4 +1,4 @@
1
- import { doAwaitTimeout, doAwaitNot } from "../../../utils/index.js";
1
+ import { doAwaitNot, doAwaitTimeout } from "../../../utils/index.js";
2
2
  import { reactive, ref } from "vue";
3
3
  import { deepUnref } from "vue-deepunref";
4
4
 
@@ -13,8 +13,7 @@ describe("use/useListSort", () => {
13
13
  useListFilter,
14
14
  useListSort,
15
15
  useListSorts,
16
- setListSortDefaultOptions,
17
- AwaitNot;
16
+ setListSortDefaultOptions;
18
17
  const contactsResolved = [
19
18
  {
20
19
  id: 15,
@@ -65,20 +64,17 @@ describe("use/useListSort", () => {
65
64
  setListSortDefaultOptions({
66
65
  sortThrottleWait: 0,
67
66
  });
68
- const importedUtils = await import("../../../utils");
69
- AwaitNot = importedUtils.AwaitNot;
70
67
  });
71
68
 
72
69
  afterEach(() => vi.resetAllMocks());
73
70
 
74
71
  const waitForListSort = async (listSort) => {
75
- const anr = new AwaitNot({
72
+ await doAwaitNot({
76
73
  obj: listSort.state,
77
74
  prop: "running",
78
75
  timeout: 2000,
76
+ couldAlreadyBeFalse: false,
79
77
  });
80
- anr.start();
81
- await anr.promise;
82
78
  };
83
79
 
84
80
  it("generates initial values from inputs", () => {
@@ -106,6 +102,10 @@ describe("use/useListSort", () => {
106
102
  for (const contact of contactsResolved) {
107
103
  listInstance.addListObject(contact);
108
104
  }
105
+ await doAwaitNot({
106
+ obj: listInstance.state,
107
+ prop: "running",
108
+ });
109
109
  const listSort = useListSort({ parentState: listInstance.state, orderByRules });
110
110
  // sorts immediately
111
111
  expect(listSort.state.order).toEqual(testOrder1);
@@ -1,12 +1,13 @@
1
- import { keyDiff, loadingCombine, difference } from "../utils/index.js";
1
+ import { difference, keyDiff, loadingCombine } from "../utils/index.js";
2
+ import { proxyRunning } from "../utils/proxyRunning.js";
2
3
  import {
3
- listRelatedStateKeys,
4
- listSubscriptionStateKeys,
5
- listInstanceStateKeys,
6
- listFilterStateKeys,
7
4
  listCalculatedStateKeys,
8
- listSortStateKeys,
5
+ listFilterStateKeys,
6
+ listInstanceStateKeys,
7
+ listRelatedStateKeys,
9
8
  listSearchStateKeys,
9
+ listSortStateKeys,
10
+ listSubscriptionStateKeys,
10
11
  } from "./listKeys.js";
11
12
  import { useWatchesRunning } from "./watchesRunning.js";
12
13
  import isEmpty from "lodash-es/isEmpty.js";
@@ -145,7 +146,9 @@ export function useListCalculated({ parentState, calculatedObjectsRules }) {
145
146
  });
146
147
 
147
148
  state.calculatedRunning = toRef(watchesRunning.state, "running");
148
- state.running = computed(() => loadingCombine(watchesRunning.state.running, parentState.running));
149
+ const parentRunning = ref(undefined);
150
+ proxyRunning(parentState, "running", parentRunning);
151
+ state.running = computed(() => loadingCombine(watchesRunning.state.running, parentRunning.value));
149
152
 
150
153
  onScopeDispose(() => {
151
154
  for (const objectKey of Object.keys(calculatedObjectsEffectScopes)) {
package/use/listFilter.js CHANGED
@@ -1,15 +1,16 @@
1
1
  import { assignReactiveObject, difference, loadingCombine } from "../utils/index.js";
2
2
  import { keyDiff } from "../utils/keyDiff.js";
3
+ import { proxyRunning } from "../utils/proxyRunning.js";
3
4
  import {
4
- listSortStateKeys,
5
- listFilterStateKeys,
6
- listRelatedStateKeys,
7
5
  listCalculatedStateKeys,
8
- listSubscriptionStateKeys,
6
+ listFilterStateKeys,
9
7
  listInstanceStateKeys,
8
+ listRelatedStateKeys,
10
9
  listSearchStateKeys,
10
+ listSortStateKeys,
11
+ listSubscriptionStateKeys,
11
12
  } from "./listKeys.js";
12
- import { effectScope, reactive, toRef, watch, unref, computed, nextTick } from "vue";
13
+ import { computed, effectScope, nextTick, reactive, ref, toRef, unref, watch } from "vue";
13
14
 
14
15
  const parentStateKeys = difference(
15
16
  new Set([
@@ -71,9 +72,6 @@ export function useListFilter({ parentState, allowedFilter, excludedFilter }) {
71
72
  previousExcludedFilter = null;
72
73
 
73
74
  const objectsWatch = () => {
74
- if (parentState.running) {
75
- return;
76
- }
77
75
  state.objectsWatchRunning = true;
78
76
  const allowedOrExcludedFilterChanged =
79
77
  allowedFilter !== previousAllowedFilter || excludedFilter !== previousExcludedFilter;
@@ -155,8 +153,10 @@ export function useListFilter({ parentState, allowedFilter, excludedFilter }) {
155
153
  state[key] = toRef(parentState, key);
156
154
  }
157
155
 
156
+ const parentRunning = ref(undefined);
157
+ proxyRunning(parentState, "running", parentRunning);
158
158
  state.running = computed(() => {
159
- return loadingCombine(parentState.running, state.objectsWatchRunning, state.resultsWatchRunning);
159
+ return loadingCombine(parentRunning.value, state.objectsWatchRunning, state.resultsWatchRunning);
160
160
  });
161
161
 
162
162
  watch(toRef(state, "inResults"), resultsWatch, { deep: true });
@@ -100,6 +100,9 @@ export function useListInstance({ props, functions = {}, keepOldPages = false })
100
100
  },
101
101
  set(target, prop, value) {
102
102
  target.set(prop, value); // map.set() returns the map, we don't need that
103
+ if (!state.running) {
104
+ state.running = true;
105
+ }
103
106
  return true;
104
107
  },
105
108
  ownKeys(target) {
@@ -135,6 +138,7 @@ export function useListInstance({ props, functions = {}, keepOldPages = false })
135
138
  listArgs: toRef(props, "listArgs"),
136
139
  objects: _objectsProxy,
137
140
  loading: undefined,
141
+ running: false,
138
142
  errored: false,
139
143
  error: null,
140
144
  order: [],
@@ -218,6 +222,9 @@ export function useListInstance({ props, functions = {}, keepOldPages = false })
218
222
  throw new ListError(`addListObject: list already has object for id: ${inspect(object.id)}`, "duplicate-id");
219
223
  }
220
224
  state.objects[object.id] = object;
225
+ if (!state.running) {
226
+ state.running = true;
227
+ }
221
228
  }
222
229
 
223
230
  function updateListObject(object) {
@@ -231,6 +238,9 @@ export function useListInstance({ props, functions = {}, keepOldPages = false })
231
238
  );
232
239
  }
233
240
  assignReactiveObject(state.objects[object.id], object);
241
+ if (!state.running) {
242
+ state.running = true;
243
+ }
234
244
  }
235
245
 
236
246
  function deleteListObject(objectId) {
@@ -241,6 +251,9 @@ export function useListInstance({ props, functions = {}, keepOldPages = false })
241
251
  );
242
252
  }
243
253
  delete state.objects[objectId];
254
+ if (!state.running) {
255
+ state.running = true;
256
+ }
244
257
  }
245
258
 
246
259
  function clearList() {
@@ -265,6 +278,9 @@ export function useListInstance({ props, functions = {}, keepOldPages = false })
265
278
  state.objectsInOrderRefs,
266
279
  Object.keys(state.objects).map((id) => toRef(state.objects, id))
267
280
  );
281
+ if (state.running) {
282
+ state.running = false;
283
+ }
268
284
  },
269
285
  {
270
286
  immediate: true,
package/use/listKeys.js CHANGED
@@ -4,6 +4,7 @@ export const listInstanceStateKeys = [
4
4
  "listArgs",
5
5
  "objects",
6
6
  "loading",
7
+ "running",
7
8
  "errored",
8
9
  "error",
9
10
  "objectsInOrder",
@@ -1,15 +1,16 @@
1
1
  import { keyDiff } from "../utils/keyDiff.js";
2
2
  import { loadingCombine } from "../utils/loadingCombine.js";
3
+ import { proxyRunning } from "../utils/proxyRunning.js";
3
4
  import { getObjectRelatedByKey } from "../utils/relatedCalculatedHelpers.js";
4
5
  import { difference } from "../utils/set.js";
5
6
  import {
6
- listSubscriptionStateKeys,
7
- listInstanceStateKeys,
8
- listRelatedStateKeys,
9
7
  listCalculatedStateKeys,
10
- listSortStateKeys,
11
8
  listFilterStateKeys,
9
+ listInstanceStateKeys,
10
+ listRelatedStateKeys,
12
11
  listSearchStateKeys,
12
+ listSortStateKeys,
13
+ listSubscriptionStateKeys,
13
14
  } from "./listKeys.js";
14
15
  import { useWatchesRunning } from "./watchesRunning.js";
15
16
  import get from "lodash-es/get.js";
@@ -17,7 +18,7 @@ import identity from "lodash-es/identity.js";
17
18
  import isArray from "lodash-es/isArray.js";
18
19
  import isEmpty from "lodash-es/isEmpty.js";
19
20
  import isUndefined from "lodash-es/isUndefined.js";
20
- import { computed, effectScope, onScopeDispose, reactive, toRef, unref, watch } from "vue";
21
+ import { computed, effectScope, onScopeDispose, reactive, ref, toRef, unref, watch } from "vue";
21
22
 
22
23
  const parentStateKeys = difference(
23
24
  new Set([
@@ -198,7 +199,9 @@ export function useListRelated({ parentState, relatedObjectsRules }) {
198
199
  });
199
200
 
200
201
  state.relatedRunning = toRef(watchesRunning.state, "running");
201
- state.running = computed(() => loadingCombine(watchesRunning.state.running, parentState.running));
202
+ const parentRunning = ref(undefined);
203
+ proxyRunning(parentState, "running", parentRunning);
204
+ state.running = computed(() => loadingCombine(watchesRunning.state.running, parentRunning.value));
202
205
 
203
206
  onScopeDispose(() => {
204
207
  for (const objectKey of Object.keys(relatedObjectsEffectScopes)) {
package/use/listSearch.js CHANGED
@@ -1,18 +1,19 @@
1
- import { difference, loadingCombine, assignReactiveObject, keyDiff } from "../utils/index.js";
1
+ import { assignReactiveObject, difference, keyDiff, loadingCombine } from "../utils/index.js";
2
+ import { proxyRunning } from "../utils/proxyRunning.js";
2
3
  import { getObjectRelatedCalculatedByKey } from "../utils/relatedCalculatedHelpers.js";
3
4
  import {
4
- listInstanceStateKeys,
5
- listSubscriptionStateKeys,
6
- listRelatedStateKeys,
7
5
  listCalculatedStateKeys,
8
- listSortStateKeys,
9
6
  listFilterStateKeys,
7
+ listInstanceStateKeys,
8
+ listRelatedStateKeys,
10
9
  listSearchStateKeys,
10
+ listSortStateKeys,
11
+ listSubscriptionStateKeys,
11
12
  } from "./listKeys.js";
12
13
  import { useSearch } from "./search.js";
13
14
  import get from "lodash-es/get.js";
14
15
  import isEqual from "lodash-es/isEqual.js";
15
- import { reactive, effectScope, toRef, computed, watch, unref, onScopeDispose } from "vue";
16
+ import { computed, effectScope, onScopeDispose, reactive, ref, toRef, unref, watch } from "vue";
16
17
  import { deepUnref } from "vue-deepunref";
17
18
 
18
19
  const parentStateKeys = difference(
@@ -304,8 +305,10 @@ export function useListSearch({ parentState, props, throttle = 500, showAllWhenE
304
305
  textSearchIndex.state.search = toRef(state, "textSearchValue");
305
306
  textSearchIndex.events.addEventListener("newIndex", indexWasCleared);
306
307
  state.searched = toRef(() => textSearchIndex.state.searched);
308
+ const parentRunning = ref(undefined);
309
+ proxyRunning(parentState, "running", parentRunning);
307
310
  state.running = computed(() => {
308
- return loadingCombine(parentState.running, state.newSearchComputeds, textSearchIndex.state.running);
311
+ return loadingCombine(parentRunning.value, state.newSearchComputeds, textSearchIndex.state.running);
309
312
  });
310
313
  state.objectsInOrder = computed(() => state.objectsInOrderRefs.map((ref) => unref(ref)));
311
314
 
package/use/listSort.js CHANGED
@@ -1,12 +1,13 @@
1
- import { assignReactiveObject, keyDiff, loadingCombine, difference } from "../utils/index.js";
1
+ import { assignReactiveObject, difference, keyDiff, loadingCombine } from "../utils/index.js";
2
+ import { proxyRunning } from "../utils/proxyRunning.js";
2
3
  import {
3
- listSortStateKeys,
4
- listFilterStateKeys,
5
- listRelatedStateKeys,
6
4
  listCalculatedStateKeys,
7
- listSubscriptionStateKeys,
5
+ listFilterStateKeys,
8
6
  listInstanceStateKeys,
7
+ listRelatedStateKeys,
9
8
  listSearchStateKeys,
9
+ listSortStateKeys,
10
+ listSubscriptionStateKeys,
10
11
  } from "./listKeys.js";
11
12
  import { useWatchesRunning } from "./watchesRunning.js";
12
13
  import get from "lodash-es/get.js";
@@ -17,7 +18,7 @@ import isNull from "lodash-es/isNull.js";
17
18
  import isUndefined from "lodash-es/isUndefined.js";
18
19
  import throttle from "lodash-es/throttle.js";
19
20
  import zip from "lodash-es/zip.js";
20
- import { effectScope, reactive, toRef, unref, watch, computed } from "vue";
21
+ import { computed, effectScope, reactive, ref, toRef, unref, watch } from "vue";
21
22
 
22
23
  const collator = new Intl.Collator(undefined, { numeric: true });
23
24
 
@@ -251,8 +252,10 @@ export function useListSort({ parentState, orderByRules, sortThrottleWait = defa
251
252
 
252
253
  state.objectsInOrder = computed(() => state.objectsInOrderRefs.map((e) => unref(e)));
253
254
  state.sortRunning = computed(() => loadingCombine(watchesRunning.state.running, state.outstandingEffects));
255
+ const parentRunning = ref(undefined);
256
+ proxyRunning(parentState, "running", parentRunning);
254
257
  state.running = computed(() =>
255
- loadingCombine(watchesRunning.state.running, state.outstandingEffects, parentState.running)
258
+ loadingCombine(watchesRunning.state.running, state.outstandingEffects, parentRunning.value)
256
259
  );
257
260
  });
258
261
 
@@ -1,3 +1,4 @@
1
+ import { proxyRunning } from "../utils/index.js";
1
2
  import { keyDiff } from "../utils/keyDiff.js";
2
3
  import { loadingCombine } from "../utils/loadingCombine.js";
3
4
  import { objectInstanceStateKeys } from "./objectInstance.js";
@@ -5,7 +6,7 @@ import { objectRelatedStateKeys } from "./objectRelated.js";
5
6
  import { objectSubscriptionStateKeys } from "./objectSubscription.js";
6
7
  import { useWatchesRunning } from "./watchesRunning.js";
7
8
  import isEmpty from "lodash-es/isEmpty.js";
8
- import { computed, effectScope, onScopeDispose, reactive, toRef, watch } from "vue";
9
+ import { computed, effectScope, onScopeDispose, reactive, ref, toRef, watch } from "vue";
9
10
 
10
11
  export const objectCalculatedStateKeys = [
11
12
  "calculatedObject",
@@ -99,7 +100,9 @@ export function useObjectCalculated({ parentState, calculatedObjectRules }) {
99
100
  });
100
101
 
101
102
  state.calculatedRunning = toRef(watchesRunning.state, "running");
102
- state.running = computed(() => loadingCombine(watchesRunning.state.running, parentState.running));
103
+ const parentRunning = ref(undefined);
104
+ proxyRunning(parentState, "running", parentRunning);
105
+ state.running = computed(() => loadingCombine(watchesRunning.state.running, parentRunning));
103
106
 
104
107
  onScopeDispose(() => {
105
108
  for (const key in calculatedObjectEffectScopes) {
@@ -1,4 +1,4 @@
1
- import { loadingCombine } from "../utils/index.js";
1
+ import { loadingCombine, proxyRunning } from "../utils/index.js";
2
2
  import { keyDiff } from "../utils/keyDiff.js";
3
3
  import { objectInstanceStateKeys } from "./objectInstance.js";
4
4
  import { objectSubscriptionStateKeys } from "./objectSubscription.js";
@@ -9,7 +9,7 @@ import isArray from "lodash-es/isArray.js";
9
9
  import isEmpty from "lodash-es/isEmpty.js";
10
10
  import isEqual from "lodash-es/isEqual.js";
11
11
  import isUndefined from "lodash-es/isUndefined.js";
12
- import { computed, effectScope, onScopeDispose, reactive, toRef, unref, watch } from "vue";
12
+ import { computed, effectScope, onScopeDispose, reactive, ref, toRef, unref, watch } from "vue";
13
13
 
14
14
  export const objectRelatedStateKeys = [
15
15
  "relatedObject",
@@ -129,7 +129,9 @@ export function useObjectRelated({ parentState, relatedObjectRules }) {
129
129
  });
130
130
 
131
131
  state.relatedRunning = toRef(watchesRunning.state, "running");
132
- state.running = computed(() => loadingCombine(watchesRunning.state.running, parentState.running));
132
+ const parentRunning = ref(undefined);
133
+ proxyRunning(parentState, "running", parentRunning);
134
+ state.running = computed(() => loadingCombine(watchesRunning.state.running, parentRunning));
133
135
 
134
136
  onScopeDispose(() => {
135
137
  for (const key in relatedObjectEffectScopes) {
@@ -309,7 +309,7 @@ function recursiveInner(target, source, exclude, addedKeys, sameKeys, path, fn)
309
309
  const keysForRecurse = [];
310
310
  const keysForReplace = [];
311
311
  for (const key of sameKeys) {
312
- if (!exclude.includes(key)) {
312
+ if (!exclude?.includes(key)) {
313
313
  if (isObject(source[key]) && isObject(target[key])) {
314
314
  keysForRecurse.push(key);
315
315
  } else if (target[key] !== source[key]) {
package/utils/index.js CHANGED
@@ -8,6 +8,7 @@ export * from "./getFakeId.js";
8
8
  export * from "./keyDiff.js";
9
9
  export * from "./lifecycleDebug.js";
10
10
  export * from "./loadingCombine.js";
11
+ export * from "./proxyRunning.js";
11
12
  export * from "./relatedCalculatedHelpers.js";
12
13
  export * from "./set.js";
13
14
  export * from "./transformWalk.js";
@@ -0,0 +1,26 @@
1
+ import { nextTick, toRef, watch } from "vue";
2
+
3
+ export function proxyRunning(parentState, parentStateProp, state, prop) {
4
+ // we want you to have been running false for two ticks before we stop you, but we want you to start running true immediately
5
+ // the point of this is to reduce running flicker where the stack has running states awaiting other running states
6
+ let falseTickCount = 0;
7
+ const checkStillFalse = () => {
8
+ if (falseTickCount > 1) {
9
+ state[prop] = false;
10
+ falseTickCount = 0;
11
+ } else if (parentState[parentStateProp] === false) {
12
+ falseTickCount++;
13
+ nextTick(checkStillFalse);
14
+ } else {
15
+ falseTickCount = 0;
16
+ }
17
+ };
18
+ watch(toRef(parentState, parentStateProp), (running) => {
19
+ if (running && state[prop] !== running) {
20
+ state[prop] = running;
21
+ } else if (!running && state[prop] !== running) {
22
+ nextTick(checkStillFalse);
23
+ }
24
+ });
25
+ state[prop] = parentState[parentStateProp];
26
+ }
package/utils/watches.js CHANGED
@@ -5,8 +5,11 @@ export class ImmediateStopWatch {
5
5
  constructor() {}
6
6
 
7
7
  // watchFuncArgs are for immediate call only.
8
- start(watchSources, watchFunc, watchFuncArgs = []) {
9
- this.stopWatch = watch(watchSources, watchFunc);
8
+ start(watchSources, watchFunc, watchFuncArgs = [], watchOptions = {}) {
9
+ if (watchOptions.immediate) {
10
+ throw new Error("ImmediateStopWatch is always immediate.");
11
+ }
12
+ this.stopWatch = watch(watchSources, watchFunc, watchOptions);
10
13
  watchFunc(...watchFuncArgs);
11
14
  }
12
15