@milaboratories/uikit 2.15.31 → 2.15.33

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.
@@ -1,6 +1,6 @@
1
1
   WARN  Issue while reading "/home/runner/_work/platforma/platforma/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
2
2
 
3
- > @milaboratories/uikit@2.15.31 build /home/runner/_work/platforma/platforma/lib/ui/uikit
3
+ > @milaboratories/uikit@2.15.33 build /home/runner/_work/platforma/platforma/lib/ui/uikit
4
4
  > ts-builder build --target browser-lib --build-config ./build.browser-lib.config.js
5
5
 
6
6
  Building browser-lib project...
@@ -18,7 +18,7 @@ Using custom config: ./build.browser-lib.config.js
18
18
  rendering chunks...
19
19
 
20
20
  [unplugin:dts] Start generate declaration files...
21
- [unplugin:dts] Declaration files built in 9010ms.
21
+ [unplugin:dts] Declaration files built in 5706ms.
22
22
 
23
23
  computing gzip size...
24
24
  dist/components/PlClipboard/PlClipboard.vue?vue&type=style&index=0&lang.css 0.04 kB │ gzip: 0.06 kB
@@ -500,7 +500,6 @@ dist/assets/icons/icon-assets-min/24_edit.js
500
500
  dist/assets/icons/icon-assets-min/24_line-error.js 0.58 kB │ gzip: 0.34 kB │ map: 0.67 kB
501
501
  dist/assets/icons/icon-assets-min/16_copy.js 0.59 kB │ gzip: 0.37 kB │ map: 0.68 kB
502
502
  dist/assets/icons/icon-assets-min/24_reverse.js 0.59 kB │ gzip: 0.36 kB │ map: 0.66 kB
503
- dist/composition/computedCached.js 0.59 kB │ gzip: 0.36 kB │ map: 2.72 kB
504
503
  dist/assets/icons/icon-assets-min/24_files-export.js 0.59 kB │ gzip: 0.38 kB │ map: 0.68 kB
505
504
  dist/assets/icons/icon-assets-min/16_edit.js 0.59 kB │ gzip: 0.38 kB │ map: 0.67 kB
506
505
  dist/assets/icons/icon-assets-min/24_files-import.js 0.60 kB │ gzip: 0.38 kB │ map: 0.68 kB
@@ -528,6 +527,15 @@ dist/composition/useQuery.js
528
527
  dist/assets/icons/icon-assets-min/16_settings.js 0.64 kB │ gzip: 0.37 kB │ map: 0.71 kB
529
528
  dist/assets/icons/icon-assets-min/24_table-upload.js 0.64 kB │ gzip: 0.38 kB │ map: 0.73 kB
530
529
  dist/assets/icons/icon-assets-min/16_data-dimentions.js 0.64 kB │ gzip: 0.42 kB │ map: 0.74 kB
530
+ dist/composition/computedCached.js 0.64 kB │ gzip: 0.39 kB │ map: 3.88 kB
531
+ [PLUGIN_TIMINGS] Warning: Your build spent significant time in plugins. Here is a breakdown:
532
+ - vite:asset (33%)
533
+ - sourcemaps (16%)
534
+ - vite:css-post (11%)
535
+ - unplugin-dts (10%)
536
+ - vite:vue (8%)
537
+ See https://rolldown.rs/options/checks#plugintimings for more details.
538
+ 
531
539
  dist/assets/icons/icon-assets-min/16_clipboard.js 0.64 kB │ gzip: 0.38 kB │ map: 0.72 kB
532
540
  dist/components/PlFileDialog/utils.js 0.64 kB │ gzip: 0.43 kB │ map: 2.18 kB
533
541
  dist/assets/icons/icon-assets-min/24_frame-type-none.js 0.65 kB │ gzip: 0.30 kB │ map: 0.72 kB
@@ -638,14 +646,6 @@ dist/helpers/downloadContent.js
638
646
  dist/assets/icons/icon-assets-min/24_boxplot-binned.js 1.00 kB │ gzip: 0.47 kB │ map: 1.09 kB
639
647
  dist/assets/icons/icon-assets-min/24_bar-error.js 1.00 kB │ gzip: 0.43 kB │ map: 1.14 kB
640
648
  dist/assets/icons/icon-assets-min/24_dendrogram-Y.js 1.00 kB │ gzip: 0.41 kB │ map: 1.14 kB
641
- [PLUGIN_TIMINGS] Warning: Your build spent significant time in plugins. Here is a breakdown:
642
- - vite:asset (37%)
643
- - vite:css-post (13%)
644
- - unplugin-dts (11%)
645
- - vite:vue (9%)
646
- - vite:css (8%)
647
- See https://rolldown.rs/options/checks#plugintimings for more details.
648
- 
649
649
  dist/assets/icons/icon-assets-min/24_view-show.js 1.01 kB │ gzip: 0.59 kB │ map: 1.09 kB
650
650
  dist/components/DataTable/assets/TableIcon.js 1.01 kB │ gzip: 0.57 kB │ map: 1.02 kB
651
651
  dist/assets/icons/icon-assets-min/24_theme-dark.js 1.02 kB │ gzip: 0.52 kB │ map: 1.09 kB
@@ -790,5 +790,5 @@ dist/composition/filters/metadata.js
790
790
  dist/index.js 12.53 kB │ gzip: 2.96 kB │ map: 21.32 kB
791
791
  dist/components/PlSvg/PlSvg.vue_vue_type_script_setup_true_lang.js 39.41 kB │ gzip: 3.40 kB │ map: 8.66 kB
792
792
 
793
- ✓ built in 11.65s
793
+ ✓ built in 7.62s
794
794
  Build completed successfully
@@ -1,6 +1,6 @@
1
1
   WARN  Issue while reading "/home/runner/_work/platforma/platforma/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
2
2
 
3
- > @milaboratories/uikit@2.15.31 formatter:check /home/runner/_work/platforma/platforma/lib/ui/uikit
3
+ > @milaboratories/uikit@2.15.33 formatter:check /home/runner/_work/platforma/platforma/lib/ui/uikit
4
4
  > ts-builder formatter --check
5
5
 
6
6
  Checking formatting...
@@ -8,5 +8,5 @@ Checking formatting...
8
8
  Checking formatting...
9
9
 
10
10
  All matched files use the correct format.
11
- Finished in 2461ms on 383 files using 16 threads.
11
+ Finished in 2517ms on 383 files using 16 threads.
12
12
  Format check completed successfully
@@ -1,10 +1,10 @@
1
1
   WARN  Issue while reading "/home/runner/_work/platforma/platforma/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
2
2
 
3
- > @milaboratories/uikit@2.15.31 linter:check /home/runner/_work/platforma/platforma/lib/ui/uikit
3
+ > @milaboratories/uikit@2.15.33 linter:check /home/runner/_work/platforma/platforma/lib/ui/uikit
4
4
  > ts-builder linter --check
5
5
 
6
6
  Linting project...
7
7
  ↳ oxlint --config /home/runner/_work/platforma/platforma/lib/ui/uikit/.oxlintrc.json --deny-warnings
8
8
  Found 0 warnings and 0 errors.
9
- Finished in 23ms on 310 files with 108 rules using 16 threads.
9
+ Finished in 30ms on 310 files with 108 rules using 16 threads.
10
10
  Linting completed successfully
@@ -1,6 +1,6 @@
1
1
   WARN  Issue while reading "/home/runner/_work/platforma/platforma/.npmrc". Failed to replace env in config: ${NPMJS_TOKEN}
2
2
 
3
- > @milaboratories/uikit@2.15.31 types:check /home/runner/_work/platforma/platforma/lib/ui/uikit
3
+ > @milaboratories/uikit@2.15.33 types:check /home/runner/_work/platforma/platforma/lib/ui/uikit
4
4
  > ts-builder type-check --target browser-lib
5
5
 
6
6
  ↳ vue-tsc-ts6.cjs --noEmit --project ./tsconfig.json --customConditions default
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @milaboratories/uikit
2
2
 
3
+ ## 2.15.33
4
+
5
+ ### Patch Changes
6
+
7
+ - 43e07a3: Fix an infinite grid-remount loop in `PlAgDataTableV2` that recreated the whole
8
+ AG Grid around twenty times a second — strobing the column headers and filling
9
+ the console with AG Grid licence banners — on opening a page whose table had a
10
+ saved grid state, most often right after a block rebuild.
11
+
12
+ `computedCached`'s `writeThrough` made a set visible to readers immediately, but
13
+ the cache was still overwritten whenever the source re-emitted. A set is
14
+ debounced, so between the write and its arrival in the project state the source
15
+ still holds the value the write moved away from; a running block has that state
16
+ pushed back repeatedly, and each push reverted the cache. The stored grid state
17
+ therefore flipped between what the grid had and what the project still held, and
18
+ every flip made the reload watch destroy and recreate the grid — which, because
19
+ the flips never stopped on their own, never converged.
20
+
21
+ A write-through value now stays authoritative until the source reports it back.
22
+ Since `get` maps set values to themselves, that is exactly when the round trip
23
+ has completed; a change from elsewhere arriving mid-flight is dropped in favour
24
+ of the local write that is about to land.
25
+
26
+ ## 2.15.32
27
+
28
+ ### Patch Changes
29
+
30
+ - @platforma-sdk/model@1.84.1
31
+
3
32
  ## 2.15.31
4
33
 
5
34
  ### Patch Changes
@@ -6,6 +6,17 @@ import { ComputedGetter, ComputedSetter, ComputedRef, WritableComputedRef } from
6
6
  * With `writeThrough`, a set updates the cache synchronously, so a read reflects
7
7
  * the write immediately even when `set` defers its work (e.g. debounced); the
8
8
  * getter must then map set values to themselves (`get(x)` deep-equals `x`).
9
+ *
10
+ * A deferred set also leaves a window in which the source still holds the value
11
+ * the write moved away from, and anything that re-emits the source during that
12
+ * window — a running block having its project state pushed back, say — used to
13
+ * revert the cache to it. The next write moved the cache forward again: a
14
+ * ping-pong that never settled, and that whoever watches the value (the data
15
+ * table rebuilds its whole grid) pays for on every swing. So a write-through
16
+ * value stays authoritative until the source reports it back; since `get` maps
17
+ * set values to themselves, that is exactly when the round trip has completed.
18
+ * A change from elsewhere that arrives mid-flight is therefore dropped in
19
+ * favour of the local write, which is about to land anyway.
9
20
  */
10
21
  export declare function computedCached<T>(options: {
11
22
  get: ComputedGetter<T>;
@@ -1 +1 @@
1
- {"version":3,"file":"computedCached.d.ts","sourceRoot":"","sources":["../../src/composition/computedCached.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACzB,MAAM,KAAK,CAAC;AAEb;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE;IACzC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IACvB,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC;AAC3B,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"computedCached.d.ts","sourceRoot":"","sources":["../../src/composition/computedCached.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACzB,MAAM,KAAK,CAAC;AAEb;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE;IACzC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IACvB,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC;AAC3B,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC"}
@@ -4,13 +4,17 @@ import { deepClone as r, isJsonEqual as i } from "@milaboratories/helpers";
4
4
  function a(a) {
5
5
  let o, s, c = !1;
6
6
  typeof a == "function" ? o = a : (o = a.get, s = a.set, c = a.writeThrough ?? !1);
7
- let l = t(o());
7
+ let l = t(o()), u = !1;
8
8
  return n(o, (e) => {
9
- i(e, l.value) || (l.value = r(e));
9
+ if (i(e, l.value)) {
10
+ u = !1;
11
+ return;
12
+ }
13
+ u || (l.value = r(e));
10
14
  }, { deep: !0 }), s ? e({
11
15
  get: () => l.value,
12
16
  set: c ? (e) => {
13
- i(e, l.value) || (l.value = r(e)), s(e);
17
+ i(e, l.value) || (l.value = r(e), u = !0), s(e);
14
18
  } : s
15
19
  }) : l;
16
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"computedCached.js","names":[],"sources":["../../src/composition/computedCached.ts"],"sourcesContent":["import { deepClone, isJsonEqual } from \"@milaboratories/helpers\";\nimport {\n computed,\n ref,\n watch,\n type ComputedGetter,\n type ComputedSetter,\n type ComputedRef,\n type WritableComputedRef,\n} from \"vue\";\n\n/**\n * Alternative to `computed`, but triggering only on actual data changes.\n * Always `deep` as the plain `computed` is.\n *\n * With `writeThrough`, a set updates the cache synchronously, so a read reflects\n * the write immediately even when `set` defers its work (e.g. debounced); the\n * getter must then map set values to themselves (`get(x)` deep-equals `x`).\n */\nexport function computedCached<T>(options: {\n get: ComputedGetter<T>;\n set: ComputedSetter<T>;\n writeThrough?: boolean;\n}): WritableComputedRef<T>;\nexport function computedCached<T>(getter: ComputedGetter<T>): ComputedRef<T>;\nexport function computedCached<T>(\n arg:\n | ComputedGetter<T>\n | {\n get: ComputedGetter<T>;\n set: ComputedSetter<T>;\n writeThrough?: boolean;\n },\n) {\n let getter: ComputedGetter<T>;\n let setter: ComputedSetter<T> | undefined = undefined;\n let writeThrough = false;\n if (typeof arg === \"function\") {\n getter = arg;\n } else {\n getter = arg.get;\n setter = arg.set;\n writeThrough = arg.writeThrough ?? false;\n }\n\n const cachedValue = ref<T>(getter());\n watch(\n getter,\n (newValue) => {\n if (!isJsonEqual(newValue, cachedValue.value)) {\n // `deepClone` is needed because in case some fields are patched the deep would be triggered,\n // but objects would be equal as the saved value was also patched\n cachedValue.value = deepClone(newValue);\n }\n },\n { deep: true },\n );\n\n if (setter) {\n return computed({\n get: () => cachedValue.value,\n set: writeThrough\n ? (newValue) => {\n // Reflect the value in the cache now; `set` itself may defer its work.\n if (!isJsonEqual(newValue, cachedValue.value)) cachedValue.value = deepClone(newValue);\n setter(newValue);\n }\n : setter,\n });\n } else {\n return cachedValue;\n }\n}\n"],"mappings":";;;AAyBA,SAAgB,EACd,GAOA;CACA,IAAI,GACA,GACA,IAAe;AACnB,CAAI,OAAO,KAAQ,aACjB,IAAS,KAET,IAAS,EAAI,KACb,IAAS,EAAI,KACb,IAAe,EAAI,gBAAgB;CAGrC,IAAM,IAAc,EAAO,GAAQ,CAAC;AAyBlC,QAxBF,EACE,IACC,MAAa;AACZ,EAAK,EAAY,GAAU,EAAY,MAAM,KAG3C,EAAY,QAAQ,EAAU,EAAS;IAG3C,EAAE,MAAM,IAAM,CACf,EAEG,IACK,EAAS;EACd,WAAW,EAAY;EACvB,KAAK,KACA,MAAa;AAGZ,GADK,EAAY,GAAU,EAAY,MAAM,KAAE,EAAY,QAAQ,EAAU,EAAS,GACtF,EAAO,EAAS;MAElB;EACL,CAAC,GAEK"}
1
+ {"version":3,"file":"computedCached.js","names":[],"sources":["../../src/composition/computedCached.ts"],"sourcesContent":["import { deepClone, isJsonEqual } from \"@milaboratories/helpers\";\nimport {\n computed,\n ref,\n watch,\n type ComputedGetter,\n type ComputedSetter,\n type ComputedRef,\n type WritableComputedRef,\n} from \"vue\";\n\n/**\n * Alternative to `computed`, but triggering only on actual data changes.\n * Always `deep` as the plain `computed` is.\n *\n * With `writeThrough`, a set updates the cache synchronously, so a read reflects\n * the write immediately even when `set` defers its work (e.g. debounced); the\n * getter must then map set values to themselves (`get(x)` deep-equals `x`).\n *\n * A deferred set also leaves a window in which the source still holds the value\n * the write moved away from, and anything that re-emits the source during that\n * window — a running block having its project state pushed back, say — used to\n * revert the cache to it. The next write moved the cache forward again: a\n * ping-pong that never settled, and that whoever watches the value (the data\n * table rebuilds its whole grid) pays for on every swing. So a write-through\n * value stays authoritative until the source reports it back; since `get` maps\n * set values to themselves, that is exactly when the round trip has completed.\n * A change from elsewhere that arrives mid-flight is therefore dropped in\n * favour of the local write, which is about to land anyway.\n */\nexport function computedCached<T>(options: {\n get: ComputedGetter<T>;\n set: ComputedSetter<T>;\n writeThrough?: boolean;\n}): WritableComputedRef<T>;\nexport function computedCached<T>(getter: ComputedGetter<T>): ComputedRef<T>;\nexport function computedCached<T>(\n arg:\n | ComputedGetter<T>\n | {\n get: ComputedGetter<T>;\n set: ComputedSetter<T>;\n writeThrough?: boolean;\n },\n) {\n let getter: ComputedGetter<T>;\n let setter: ComputedSetter<T> | undefined = undefined;\n let writeThrough = false;\n if (typeof arg === \"function\") {\n getter = arg;\n } else {\n getter = arg.get;\n setter = arg.set;\n writeThrough = arg.writeThrough ?? false;\n }\n\n const cachedValue = ref<T>(getter());\n /** A write-through value the source has not reported back yet. */\n let writePending = false;\n watch(\n getter,\n (newValue) => {\n if (isJsonEqual(newValue, cachedValue.value)) {\n // The source has caught up with the write (or never diverged from it).\n writePending = false;\n return;\n }\n if (writePending) return;\n // `deepClone` is needed because in case some fields are patched the deep would be triggered,\n // but objects would be equal as the saved value was also patched\n cachedValue.value = deepClone(newValue);\n },\n { deep: true },\n );\n\n if (setter) {\n return computed({\n get: () => cachedValue.value,\n set: writeThrough\n ? (newValue) => {\n // Reflect the value in the cache now; `set` itself may defer its work.\n if (!isJsonEqual(newValue, cachedValue.value)) {\n cachedValue.value = deepClone(newValue);\n writePending = true;\n }\n setter(newValue);\n }\n : setter,\n });\n } else {\n return cachedValue;\n }\n}\n"],"mappings":";;;AAoCA,SAAgB,EACd,GAOA;CACA,IAAI,GACA,GACA,IAAe;AACnB,CAAI,OAAO,KAAQ,aACjB,IAAS,KAET,IAAS,EAAI,KACb,IAAS,EAAI,KACb,IAAe,EAAI,gBAAgB;CAGrC,IAAM,IAAc,EAAO,GAAQ,CAAC,EAEhC,IAAe;AAgCjB,QA/BF,EACE,IACC,MAAa;AACZ,MAAI,EAAY,GAAU,EAAY,MAAM,EAAE;AAE5C,OAAe;AACf;;AAEE,QAGJ,EAAY,QAAQ,EAAU,EAAS;IAEzC,EAAE,MAAM,IAAM,CACf,EAEG,IACK,EAAS;EACd,WAAW,EAAY;EACvB,KAAK,KACA,MAAa;AAMZ,GAJK,EAAY,GAAU,EAAY,MAAM,KAC3C,EAAY,QAAQ,EAAU,EAAS,EACvC,IAAe,KAEjB,EAAO,EAAS;MAElB;EACL,CAAC,GAEK"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/uikit",
3
- "version": "2.15.31",
3
+ "version": "2.15.33",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
@@ -32,7 +32,7 @@
32
32
  "sortablejs": "^1.15.6",
33
33
  "vue": "3.5.24",
34
34
  "@milaboratories/helpers": "1.14.5",
35
- "@platforma-sdk/model": "1.84.0"
35
+ "@platforma-sdk/model": "1.84.1"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@vitest/coverage-istanbul": "^4.1.3",
@@ -40,8 +40,8 @@
40
40
  "svgo": "^3.3.2",
41
41
  "typescript": "7.0.2",
42
42
  "vitest": "^4.1.3",
43
- "@milaboratories/ts-builder": "1.7.2",
44
43
  "@milaboratories/build-configs": "2.0.2",
44
+ "@milaboratories/ts-builder": "1.7.2",
45
45
  "@milaboratories/ts-configs": "1.4.0"
46
46
  },
47
47
  "scripts": {
@@ -16,6 +16,17 @@ import {
16
16
  * With `writeThrough`, a set updates the cache synchronously, so a read reflects
17
17
  * the write immediately even when `set` defers its work (e.g. debounced); the
18
18
  * getter must then map set values to themselves (`get(x)` deep-equals `x`).
19
+ *
20
+ * A deferred set also leaves a window in which the source still holds the value
21
+ * the write moved away from, and anything that re-emits the source during that
22
+ * window — a running block having its project state pushed back, say — used to
23
+ * revert the cache to it. The next write moved the cache forward again: a
24
+ * ping-pong that never settled, and that whoever watches the value (the data
25
+ * table rebuilds its whole grid) pays for on every swing. So a write-through
26
+ * value stays authoritative until the source reports it back; since `get` maps
27
+ * set values to themselves, that is exactly when the round trip has completed.
28
+ * A change from elsewhere that arrives mid-flight is therefore dropped in
29
+ * favour of the local write, which is about to land anyway.
19
30
  */
20
31
  export function computedCached<T>(options: {
21
32
  get: ComputedGetter<T>;
@@ -44,14 +55,20 @@ export function computedCached<T>(
44
55
  }
45
56
 
46
57
  const cachedValue = ref<T>(getter());
58
+ /** A write-through value the source has not reported back yet. */
59
+ let writePending = false;
47
60
  watch(
48
61
  getter,
49
62
  (newValue) => {
50
- if (!isJsonEqual(newValue, cachedValue.value)) {
51
- // `deepClone` is needed because in case some fields are patched the deep would be triggered,
52
- // but objects would be equal as the saved value was also patched
53
- cachedValue.value = deepClone(newValue);
63
+ if (isJsonEqual(newValue, cachedValue.value)) {
64
+ // The source has caught up with the write (or never diverged from it).
65
+ writePending = false;
66
+ return;
54
67
  }
68
+ if (writePending) return;
69
+ // `deepClone` is needed because in case some fields are patched the deep would be triggered,
70
+ // but objects would be equal as the saved value was also patched
71
+ cachedValue.value = deepClone(newValue);
55
72
  },
56
73
  { deep: true },
57
74
  );
@@ -62,7 +79,10 @@ export function computedCached<T>(
62
79
  set: writeThrough
63
80
  ? (newValue) => {
64
81
  // Reflect the value in the cache now; `set` itself may defer its work.
65
- if (!isJsonEqual(newValue, cachedValue.value)) cachedValue.value = deepClone(newValue);
82
+ if (!isJsonEqual(newValue, cachedValue.value)) {
83
+ cachedValue.value = deepClone(newValue);
84
+ writePending = true;
85
+ }
66
86
  setter(newValue);
67
87
  }
68
88
  : setter,