on-zero 0.11.2 → 0.11.3

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.
@@ -26,7 +26,7 @@ __export(useStreamingField_exports, {
26
26
  createUseStreamingFields: () => createUseStreamingFields
27
27
  });
28
28
  module.exports = __toCommonJS(useStreamingField_exports);
29
- var import_realtime = require("orez-sync-executor/realtime");
29
+ var import_realtime = require("orez-lite/realtime");
30
30
  var import_react = require("react");
31
31
  function createUseStreamingField(getStore) {
32
32
  return function useStreamingField(handle, base) {
@@ -36,9 +36,13 @@ function createUseStreamingField(getStore) {
36
36
  if (!store || !handle) return () => {};
37
37
  return store.subscribe(handle, onChange);
38
38
  }, [store, id]);
39
+ const last = (0, import_react.useRef)(void 0);
39
40
  const getSnapshot = (0, import_react.useCallback)(() => {
40
- if (!store || !handle) return durableOnly(base);
41
- return store.read(handle, base);
41
+ const next = !store || !handle ? durableState(base) : store.read(handle, base);
42
+ const cached = last.current;
43
+ if (cached && sameState(cached, next)) return cached;
44
+ last.current = next;
45
+ return next;
42
46
  }, [store, id, base]);
43
47
  return (0, import_react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
44
48
  };
@@ -58,15 +62,18 @@ function createUseStreamingFields(getStore) {
58
62
  value: {}
59
63
  }), [store, topicsKey]);
60
64
  const getSnapshot = (0, import_react.useCallback)(() => {
65
+ const previous = cache.value;
61
66
  const next = {};
62
67
  let changed = false;
63
68
  for (const request of requests) {
64
- const state = store ? store.read(request.handle, request.base) : durableOnly(request.base);
69
+ const raw = store ? store.read(request.handle, request.base) : durableState(request.base);
70
+ const cached = previous[request.key];
71
+ const state = cached && sameState(cached, raw) ? cached : raw;
65
72
  next[request.key] = state;
66
- if (cache.value[request.key] !== state) changed = true;
73
+ if (state !== cached) changed = true;
67
74
  }
68
- if (!changed && Object.keys(cache.value).length === Object.keys(next).length) {
69
- return cache.value;
75
+ if (!changed && Object.keys(previous).length === Object.keys(next).length) {
76
+ return previous;
70
77
  }
71
78
  cache.value = next;
72
79
  return next;
@@ -74,29 +81,13 @@ function createUseStreamingFields(getStore) {
74
81
  return (0, import_react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
75
82
  };
76
83
  }
77
- const DURABLE_CACHE = /* @__PURE__ */new WeakMap();
78
- let lastPrimitiveBase;
79
- let lastPrimitiveState;
80
- function durableOnly(base) {
81
- if (base !== null && typeof base === "object") {
82
- const cached = DURABLE_CACHE.get(base);
83
- if (cached) return cached;
84
- const state = {
85
- value: base,
86
- phase: "durable",
87
- streamID: null
88
- };
89
- DURABLE_CACHE.set(base, state);
90
- return state;
91
- }
92
- if (lastPrimitiveState && Object.is(lastPrimitiveBase, base)) {
93
- return lastPrimitiveState;
94
- }
95
- lastPrimitiveBase = base;
96
- lastPrimitiveState = {
84
+ function sameState(a, b) {
85
+ return Object.is(a.value, b.value) && a.phase === b.phase && a.streamID === b.streamID;
86
+ }
87
+ function durableState(base) {
88
+ return {
97
89
  value: base,
98
90
  phase: "durable",
99
91
  streamID: null
100
92
  };
101
- return lastPrimitiveState;
102
93
  }
@@ -0,0 +1,101 @@
1
+ var import_zero = require("@rocicorp/zero");
2
+ var import_realtime = require("orez-lite/realtime");
3
+ var import_react = require("react");
4
+ var import_client = require("react-dom/client");
5
+ var import_vitest = require("vitest");
6
+ var import_useStreamingField = require("./useStreamingField.cjs");
7
+ var import_jsx_runtime = require("react/jsx-runtime");
8
+ globalThis.IS_REACT_ACT_ENVIRONMENT = true;
9
+ const sootTask = (0, import_zero.table)("sootTask").columns({
10
+ id: (0, import_zero.string)(),
11
+ title: (0, import_zero.string)(),
12
+ description: (0, import_zero.string)()
13
+ }).primaryKey("id");
14
+ const schema = (0, import_zero.createSchema)({
15
+ tables: [sootTask]
16
+ });
17
+ const streaming = (0, import_realtime.defineStreamingFields)(schema, {
18
+ sootTask: {
19
+ description: {
20
+ maxBytes: 1e5,
21
+ maxUpdatesPerSecond: 30,
22
+ maxBytesPerSecond: 2e5
23
+ }
24
+ }
25
+ });
26
+ let container;
27
+ let root;
28
+ let consoleError;
29
+ (0, import_vitest.beforeEach)(() => {
30
+ container = document.createElement("div");
31
+ document.body.appendChild(container);
32
+ root = (0, import_client.createRoot)(container);
33
+ consoleError = import_vitest.vi.spyOn(console, "error").mockImplementation(() => {});
34
+ });
35
+ (0, import_vitest.afterEach)(() => {
36
+ (0, import_react.act)(() => root.unmount());
37
+ container.remove();
38
+ consoleError.mockRestore();
39
+ });
40
+ const uncachedSnapshotWarnings = () => consoleError.mock.calls.filter(call => String(call[0]).includes("getSnapshot should be cached"));
41
+ (0, import_vitest.test)("single-field hook is snapshot-stable while its topic is unsubscribed", () => {
42
+ const realtime = (0, import_realtime.createLocalRealtime)({
43
+ manifest: streaming.manifest
44
+ });
45
+ const useStreamingField = (0, import_useStreamingField.createUseStreamingField)(() => realtime.store);
46
+ function Card({
47
+ id,
48
+ base
49
+ }) {
50
+ const state = useStreamingField(streaming.sootTask.description({
51
+ id
52
+ }), base);
53
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)("div", {
54
+ children: state.value
55
+ });
56
+ }
57
+ (0, import_react.act)(() => {
58
+ root.render(/* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, {
59
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(Card, {
60
+ id: "t1",
61
+ base: ""
62
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(Card, {
63
+ id: "t2",
64
+ base: "a durable description"
65
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(Card, {
66
+ id: "t3",
67
+ base: ""
68
+ })]
69
+ }));
70
+ });
71
+ (0, import_vitest.expect)(uncachedSnapshotWarnings()).toEqual([]);
72
+ });
73
+ (0, import_vitest.test)("multi-field hook is snapshot-stable while topics are unsubscribed", () => {
74
+ const realtime = (0, import_realtime.createLocalRealtime)({
75
+ manifest: streaming.manifest
76
+ });
77
+ const useStreamingFields = (0, import_useStreamingField.createUseStreamingFields)(() => realtime.store);
78
+ const requests = [{
79
+ key: "t1",
80
+ handle: streaming.sootTask.description({
81
+ id: "t1"
82
+ }),
83
+ base: ""
84
+ }, {
85
+ key: "t2",
86
+ handle: streaming.sootTask.description({
87
+ id: "t2"
88
+ }),
89
+ base: "x"
90
+ }];
91
+ function List() {
92
+ const states = useStreamingFields(requests);
93
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)("div", {
94
+ children: Object.keys(states).length
95
+ });
96
+ }
97
+ (0, import_react.act)(() => {
98
+ root.render(/* @__PURE__ */(0, import_jsx_runtime.jsx)(List, {}));
99
+ });
100
+ (0, import_vitest.expect)(uncachedSnapshotWarnings()).toEqual([]);
101
+ });
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+
3
+ var import_jsx_runtime = require("react/jsx-runtime");
4
+ var import_zero = require("@rocicorp/zero");
5
+ var import_realtime = require("orez-lite/realtime");
6
+ var import_react = require("react");
7
+ var import_client = require("react-dom/client");
8
+ var import_vitest = require("vitest");
9
+ var import_useStreamingField = require("./useStreamingField.native.js");
10
+ globalThis.IS_REACT_ACT_ENVIRONMENT = true;
11
+ var sootTask = (0, import_zero.table)("sootTask").columns({
12
+ id: (0, import_zero.string)(),
13
+ title: (0, import_zero.string)(),
14
+ description: (0, import_zero.string)()
15
+ }).primaryKey("id");
16
+ var schema = (0, import_zero.createSchema)({
17
+ tables: [sootTask]
18
+ });
19
+ var streaming = (0, import_realtime.defineStreamingFields)(schema, {
20
+ sootTask: {
21
+ description: {
22
+ maxBytes: 1e5,
23
+ maxUpdatesPerSecond: 30,
24
+ maxBytesPerSecond: 2e5
25
+ }
26
+ }
27
+ });
28
+ var container;
29
+ var root;
30
+ var consoleError;
31
+ (0, import_vitest.beforeEach)(function () {
32
+ container = document.createElement("div");
33
+ document.body.appendChild(container);
34
+ root = (0, import_client.createRoot)(container);
35
+ consoleError = import_vitest.vi.spyOn(console, "error").mockImplementation(function () {});
36
+ });
37
+ (0, import_vitest.afterEach)(function () {
38
+ (0, import_react.act)(function () {
39
+ return root.unmount();
40
+ });
41
+ container.remove();
42
+ consoleError.mockRestore();
43
+ });
44
+ var uncachedSnapshotWarnings = function uncachedSnapshotWarnings2() {
45
+ return consoleError.mock.calls.filter(function (call) {
46
+ return String(call[0]).includes("getSnapshot should be cached");
47
+ });
48
+ };
49
+ (0, import_vitest.test)("single-field hook is snapshot-stable while its topic is unsubscribed", function () {
50
+ var realtime = (0, import_realtime.createLocalRealtime)({
51
+ manifest: streaming.manifest
52
+ });
53
+ var useStreamingField = (0, import_useStreamingField.createUseStreamingField)(function () {
54
+ return realtime.store;
55
+ });
56
+ function Card(param) {
57
+ var {
58
+ id,
59
+ base
60
+ } = param;
61
+ var state = useStreamingField(streaming.sootTask.description({
62
+ id
63
+ }), base);
64
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)("div", {
65
+ children: state.value
66
+ });
67
+ }
68
+ (0, import_react.act)(function () {
69
+ root.render(/* @__PURE__ */(0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, {
70
+ children: [/* @__PURE__ */(0, import_jsx_runtime.jsx)(Card, {
71
+ id: "t1",
72
+ base: ""
73
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(Card, {
74
+ id: "t2",
75
+ base: "a durable description"
76
+ }), /* @__PURE__ */(0, import_jsx_runtime.jsx)(Card, {
77
+ id: "t3",
78
+ base: ""
79
+ })]
80
+ }));
81
+ });
82
+ (0, import_vitest.expect)(uncachedSnapshotWarnings()).toEqual([]);
83
+ });
84
+ (0, import_vitest.test)("multi-field hook is snapshot-stable while topics are unsubscribed", function () {
85
+ var realtime = (0, import_realtime.createLocalRealtime)({
86
+ manifest: streaming.manifest
87
+ });
88
+ var useStreamingFields = (0, import_useStreamingField.createUseStreamingFields)(function () {
89
+ return realtime.store;
90
+ });
91
+ var requests = [{
92
+ key: "t1",
93
+ handle: streaming.sootTask.description({
94
+ id: "t1"
95
+ }),
96
+ base: ""
97
+ }, {
98
+ key: "t2",
99
+ handle: streaming.sootTask.description({
100
+ id: "t2"
101
+ }),
102
+ base: "x"
103
+ }];
104
+ function List() {
105
+ var states = useStreamingFields(requests);
106
+ return /* @__PURE__ */(0, import_jsx_runtime.jsx)("div", {
107
+ children: Object.keys(states).length
108
+ });
109
+ }
110
+ (0, import_react.act)(function () {
111
+ root.render(/* @__PURE__ */(0, import_jsx_runtime.jsx)(List, {}));
112
+ });
113
+ (0, import_vitest.expect)(uncachedSnapshotWarnings()).toEqual([]);
114
+ });
115
+ //# sourceMappingURL=useStreamingField.loop.test.native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["import_jsx_runtime","require","import_zero","import_realtime","import_react","import_client","import_vitest","import_useStreamingField","globalThis","IS_REACT_ACT_ENVIRONMENT","sootTask","table","columns","id","string","title","description","primaryKey","schema","createSchema","tables","streaming","defineStreamingFields","maxBytes","maxUpdatesPerSecond","maxBytesPerSecond","container","root","consoleError","beforeEach","document","createElement","body","appendChild","createRoot","vi","spyOn","console","mockImplementation","afterEach","act","unmount","remove","mockRestore","uncachedSnapshotWarnings","uncachedSnapshotWarnings2","mock","calls","filter","call","String","includes","test","realtime","createLocalRealtime","manifest","useStreamingField","createUseStreamingField","store","Card","param","base","state","jsx","children","value","render","jsxs","Fragment"],"sources":["../../src/useStreamingField.loop.test.tsx"],"sourcesContent":[null],"mappings":"AAeA;;AACA,IAAAA,kBAA2D,GAAAC,OAAA;AAC3D,IAAAC,WAAA,GAAAD,OAAoB;AACpB,IAAAE,eAAA,GAAsCF,OAAA;AACtC,IAAAG,YAAA,GAAAH,OAAwD;AAExD,IAAAI,aAAA,GAAAJ,OAAA,mBAAkE;AAmDvD,IAAAK,aAAA,GAAAL,OAAA;AA7CX,IAAAM,wBAAW,GAAAN,OAA2B;AAEtCO,UAAM,CAAAC,wBAAW,OAAM;AAGvB,IAAAC,QAAM,OAASR,WAAA,CAAAS,KAAA,YAAe,EAAAC,OAAS;EAEvCC,EAAA,EAAM,IAAAX,WAAA,CAAYY,MAAA;EAChBC,KAAA,MAAUb,WAAA,CAAAY,MAAA;EAAAE,WACR,IAAa,EAAAd,WAAA,CAAAY,MAAA;AAAA,GAAAG,UACX,KAAU;AAAA,IAAAC,MACV,OAAAhB,WAAqB,CAAAiB,YAAA;EAAAC,MACrB,GACFV,QAAA;AAEJ,CAAC;AAED,IAAIW,SAAA,OAAAlB,eAAA,CAAAmB,qBAAA,EAAAJ,MAAA;EACJR,QAAI;IACAM,WAAA;MAEJO,QAAA;MACEC,mBAAqB;MACrBC,iBAAc;IACd;EACA;AAAmE,EAAC;AACtE,IAACC,SAAA;AAAA,IAEDC,IAAA;AACE,IAAAC,YAAA;AACA,IAAAtB,aAAU,CAAAuB,UAAO;EACjBH,SAAA,GAAAI,QAAa,CAAAC,aAAY;EAC1BD,QAAA,CAAAE,IAAA,CAAAC,WAAA,CAAAP,SAAA;EAEDC,IAAM,OAAAtB,aAAA,CAAA6B,UAA2B,EAC/BR,SAAA;EAA+BE,YAC7B,GAAAtB,aAAgB,CAAA6B,EAAA,CAAAC,KAAS,CAAAC,OAAA,WAAAC,kBAA8B,cACzD;AAAA;AAGA,IAAAhC,aAAM,CAAAiC,SAAW;EACjB,IAAAnC,YAAM,CAAAoC,GAAA,cAAoB;IAE1B,OAASb,IAAA,CAAKc,OAAM;EAClB;EACAf,SAAO,CAAAgB,MAAA;EACTd,YAAA,CAAAe,WAAA;AAEA;AAIE,IAAAC,wBAAK,YAAAC,0BAAA;EAAA,OACHjB,YAAA,CAAAkB,IAAA,CAAAC,KAAA,CAAAC,MAAA,WAAAC,IAAA;IACE,OAAAC,MAAA,CAAAD,IAAA,KAAAE,QAAA,+BAAS;EAAa;AACqB;AACrB,IAAA7C,aACxB,CAAA8C,IAAA;EAAA,IACFC,QAAA,OAAAlD,eAAA,CAAAmD,mBAAA;IACDC,QAAA,EAAAlC,SAAA,CAAAkC;EAED;EACD,IAAAC,iBAAA,OAAAjD,wBAAA,CAAAkD,uBAAA;IAED,OAAAJ,QAAA,CAAAK,KAAK;EACH;EACA,SAAMC,KAAAC,KAAA;IAEN,IAAM;MAAA/C,EAAA;MAAAgD;IAAW,IAAAD,KAAA;IACf,IAAEE,KAAK,GAAAN,iBAAc,CAAAnC,SAAU,CAASX,QAAA,CAAAM,WAAkB;MACxDH;IACJ,IAAAgD,IAAA;IAEA,OAAS,eAAO,IAAA7D,kBAAA,CAAA+D,GAAA;MACdC,QAAM,EAAAF,KAAS,CAAAG;IACf;EACF;EAEA,IAAA7D,YAAA,CAAAoC,GAAA,EAAI,YAAM;IACRb,IAAA,CAAKuC,MAAA,CAAO,mBAAAlE,kBAAA,CAAAmE,IAAA,EAACnE,kBAAO,CAAAoE,QAAA;MACrBJ,QAAA,GAED,mBAAOhE,kBAAA,CAAA+D,GAAA,EAAAJ,IAA4B;QACpC9C,EAAA","ignoreList":[]}
@@ -28,13 +28,8 @@ __export(useStreamingField_exports, {
28
28
  createUseStreamingFields: () => createUseStreamingFields
29
29
  });
30
30
  module.exports = __toCommonJS(useStreamingField_exports);
31
- var import_realtime = require("orez-sync-executor/realtime");
31
+ var import_realtime = require("orez-lite/realtime");
32
32
  var import_react = require("react");
33
- function _type_of(obj) {
34
- "@swc/helpers - typeof";
35
-
36
- return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
37
- }
38
33
  function createUseStreamingField(getStore) {
39
34
  return function useStreamingField(handle, base) {
40
35
  var store = getStore();
@@ -43,9 +38,13 @@ function createUseStreamingField(getStore) {
43
38
  if (!store || !handle) return function () {};
44
39
  return store.subscribe(handle, onChange);
45
40
  }, [store, id]);
41
+ var last = (0, import_react.useRef)(void 0);
46
42
  var getSnapshot = (0, import_react.useCallback)(function () {
47
- if (!store || !handle) return durableOnly(base);
48
- return store.read(handle, base);
43
+ var next = !store || !handle ? durableState(base) : store.read(handle, base);
44
+ var cached = last.current;
45
+ if (cached && sameState(cached, next)) return cached;
46
+ last.current = next;
47
+ return next;
49
48
  }, [store, id, base]);
50
49
  return (0, import_react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
51
50
  };
@@ -94,6 +93,7 @@ function createUseStreamingFields(getStore) {
94
93
  };
95
94
  }, [store, topicsKey]);
96
95
  var getSnapshot = (0, import_react.useCallback)(function () {
96
+ var previous = cache.value;
97
97
  var next = {};
98
98
  var changed = false;
99
99
  var _iteratorNormalCompletion = true,
@@ -102,9 +102,11 @@ function createUseStreamingFields(getStore) {
102
102
  try {
103
103
  for (var _iterator = requests[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
104
104
  var request = _step.value;
105
- var state = store ? store.read(request.handle, request.base) : durableOnly(request.base);
105
+ var raw = store ? store.read(request.handle, request.base) : durableState(request.base);
106
+ var cached = previous[request.key];
107
+ var state = cached && sameState(cached, raw) ? cached : raw;
106
108
  next[request.key] = state;
107
- if (cache.value[request.key] !== state) changed = true;
109
+ if (state !== cached) changed = true;
108
110
  }
109
111
  } catch (err) {
110
112
  _didIteratorError = true;
@@ -120,8 +122,8 @@ function createUseStreamingFields(getStore) {
120
122
  }
121
123
  }
122
124
  }
123
- if (!changed && Object.keys(cache.value).length === Object.keys(next).length) {
124
- return cache.value;
125
+ if (!changed && Object.keys(previous).length === Object.keys(next).length) {
126
+ return previous;
125
127
  }
126
128
  cache.value = next;
127
129
  return next;
@@ -129,30 +131,14 @@ function createUseStreamingFields(getStore) {
129
131
  return (0, import_react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
130
132
  };
131
133
  }
132
- var DURABLE_CACHE = /* @__PURE__ */new WeakMap();
133
- var lastPrimitiveBase;
134
- var lastPrimitiveState;
135
- function durableOnly(base) {
136
- if (base !== null && (typeof base === "undefined" ? "undefined" : _type_of(base)) === "object") {
137
- var cached = DURABLE_CACHE.get(base);
138
- if (cached) return cached;
139
- var state = {
140
- value: base,
141
- phase: "durable",
142
- streamID: null
143
- };
144
- DURABLE_CACHE.set(base, state);
145
- return state;
146
- }
147
- if (lastPrimitiveState && Object.is(lastPrimitiveBase, base)) {
148
- return lastPrimitiveState;
149
- }
150
- lastPrimitiveBase = base;
151
- lastPrimitiveState = {
134
+ function sameState(a, b) {
135
+ return Object.is(a.value, b.value) && a.phase === b.phase && a.streamID === b.streamID;
136
+ }
137
+ function durableState(base) {
138
+ return {
152
139
  value: base,
153
140
  phase: "durable",
154
141
  streamID: null
155
142
  };
156
- return lastPrimitiveState;
157
143
  }
158
144
  //# sourceMappingURL=useStreamingField.native.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["__toCommonJS","mod","__copyProps","__defProp","value","useStreamingField_exports","__export","createUseStreamingField","createUseStreamingFields","module","exports","import_realtime","require","import_react","_type_of","obj","Symbol","constructor","getStore","useStreamingField","handle","base","store","id","canonicalTopic","spec","primaryKey","topic","subscribe","useCallback","onChange","getSnapshot","durableOnly","read","useSyncExternalStore","useStreamingFields","requests","topicsKey","useMemo","map","request","key","join","releases","_iteratorNormalCompletion","_didIteratorError","_iteratorError","_iterator","iterator","_step","next","done","release","err","return","cache"],"sources":["../../src/useStreamingField.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["__toCommonJS","mod","__copyProps","__defProp","value","useStreamingField_exports","__export","createUseStreamingField","createUseStreamingFields","module","exports","import_realtime","require","import_react","getStore","useStreamingField","handle","base","store","id","canonicalTopic","spec","primaryKey","topic","subscribe","useCallback","onChange","last","useRef","getSnapshot","next","durableState","read","cached","current","sameState","useSyncExternalStore","useStreamingFields","requests","topicsKey","useMemo","map","request","key","join","releases","_iteratorNormalCompletion","_didIteratorError","_iteratorError","_iterator","Symbol","iterator","_step","done","release","err","return","cache"],"sources":["../../src/useStreamingField.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,102 @@
1
+ import { createSchema, string, table } from "@rocicorp/zero";
2
+ import { createLocalRealtime, defineStreamingFields } from "orez-lite/realtime";
3
+ import { act } from "react";
4
+ import { createRoot } from "react-dom/client";
5
+ import { afterEach, beforeEach, expect, test, vi } from "vitest";
6
+ import { createUseStreamingField, createUseStreamingFields } from "./useStreamingField.mjs";
7
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
+ globalThis.IS_REACT_ACT_ENVIRONMENT = true;
9
+ const sootTask = table("sootTask").columns({
10
+ id: string(),
11
+ title: string(),
12
+ description: string()
13
+ }).primaryKey("id");
14
+ const schema = createSchema({
15
+ tables: [sootTask]
16
+ });
17
+ const streaming = defineStreamingFields(schema, {
18
+ sootTask: {
19
+ description: {
20
+ maxBytes: 1e5,
21
+ maxUpdatesPerSecond: 30,
22
+ maxBytesPerSecond: 2e5
23
+ }
24
+ }
25
+ });
26
+ let container;
27
+ let root;
28
+ let consoleError;
29
+ beforeEach(() => {
30
+ container = document.createElement("div");
31
+ document.body.appendChild(container);
32
+ root = createRoot(container);
33
+ consoleError = vi.spyOn(console, "error").mockImplementation(() => {});
34
+ });
35
+ afterEach(() => {
36
+ act(() => root.unmount());
37
+ container.remove();
38
+ consoleError.mockRestore();
39
+ });
40
+ const uncachedSnapshotWarnings = () => consoleError.mock.calls.filter(call => String(call[0]).includes("getSnapshot should be cached"));
41
+ test("single-field hook is snapshot-stable while its topic is unsubscribed", () => {
42
+ const realtime = createLocalRealtime({
43
+ manifest: streaming.manifest
44
+ });
45
+ const useStreamingField = createUseStreamingField(() => realtime.store);
46
+ function Card({
47
+ id,
48
+ base
49
+ }) {
50
+ const state = useStreamingField(streaming.sootTask.description({
51
+ id
52
+ }), base);
53
+ return /* @__PURE__ */jsx("div", {
54
+ children: state.value
55
+ });
56
+ }
57
+ act(() => {
58
+ root.render(/* @__PURE__ */jsxs(Fragment, {
59
+ children: [/* @__PURE__ */jsx(Card, {
60
+ id: "t1",
61
+ base: ""
62
+ }), /* @__PURE__ */jsx(Card, {
63
+ id: "t2",
64
+ base: "a durable description"
65
+ }), /* @__PURE__ */jsx(Card, {
66
+ id: "t3",
67
+ base: ""
68
+ })]
69
+ }));
70
+ });
71
+ expect(uncachedSnapshotWarnings()).toEqual([]);
72
+ });
73
+ test("multi-field hook is snapshot-stable while topics are unsubscribed", () => {
74
+ const realtime = createLocalRealtime({
75
+ manifest: streaming.manifest
76
+ });
77
+ const useStreamingFields = createUseStreamingFields(() => realtime.store);
78
+ const requests = [{
79
+ key: "t1",
80
+ handle: streaming.sootTask.description({
81
+ id: "t1"
82
+ }),
83
+ base: ""
84
+ }, {
85
+ key: "t2",
86
+ handle: streaming.sootTask.description({
87
+ id: "t2"
88
+ }),
89
+ base: "x"
90
+ }];
91
+ function List() {
92
+ const states = useStreamingFields(requests);
93
+ return /* @__PURE__ */jsx("div", {
94
+ children: Object.keys(states).length
95
+ });
96
+ }
97
+ act(() => {
98
+ root.render(/* @__PURE__ */jsx(List, {}));
99
+ });
100
+ expect(uncachedSnapshotWarnings()).toEqual([]);
101
+ });
102
+ //# sourceMappingURL=useStreamingField.loop.test.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["createSchema","string","table","createLocalRealtime","defineStreamingFields","act","createRoot","afterEach","beforeEach","expect","test","vi","createUseStreamingField","createUseStreamingFields","Fragment","jsx","jsxs","globalThis","IS_REACT_ACT_ENVIRONMENT","sootTask","columns","id","title","description","primaryKey","schema","tables","streaming","maxBytes","maxUpdatesPerSecond","maxBytesPerSecond","container","root","consoleError","document","createElement","body","appendChild","spyOn","console","mockImplementation","unmount","remove","mockRestore","uncachedSnapshotWarnings","mock","calls","filter","call","String","includes","realtime","manifest","useStreamingField","store","Card","base","state","children","value","render","toEqual","useStreamingFields","requests","key","handle","List","states","Object","keys","length"],"sources":["../../src/useStreamingField.loop.test.tsx"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,113 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { createSchema, string, table } from "@rocicorp/zero";
3
+ import { createLocalRealtime, defineStreamingFields } from "orez-lite/realtime";
4
+ import { act } from "react";
5
+ import { createRoot } from "react-dom/client";
6
+ import { afterEach, beforeEach, expect, test, vi } from "vitest";
7
+ import { createUseStreamingField, createUseStreamingFields } from "./useStreamingField.native.js";
8
+ globalThis.IS_REACT_ACT_ENVIRONMENT = true;
9
+ var sootTask = table("sootTask").columns({
10
+ id: string(),
11
+ title: string(),
12
+ description: string()
13
+ }).primaryKey("id");
14
+ var schema = createSchema({
15
+ tables: [sootTask]
16
+ });
17
+ var streaming = defineStreamingFields(schema, {
18
+ sootTask: {
19
+ description: {
20
+ maxBytes: 1e5,
21
+ maxUpdatesPerSecond: 30,
22
+ maxBytesPerSecond: 2e5
23
+ }
24
+ }
25
+ });
26
+ var container;
27
+ var root;
28
+ var consoleError;
29
+ beforeEach(function () {
30
+ container = document.createElement("div");
31
+ document.body.appendChild(container);
32
+ root = createRoot(container);
33
+ consoleError = vi.spyOn(console, "error").mockImplementation(function () {});
34
+ });
35
+ afterEach(function () {
36
+ act(function () {
37
+ return root.unmount();
38
+ });
39
+ container.remove();
40
+ consoleError.mockRestore();
41
+ });
42
+ var uncachedSnapshotWarnings = function uncachedSnapshotWarnings2() {
43
+ return consoleError.mock.calls.filter(function (call) {
44
+ return String(call[0]).includes("getSnapshot should be cached");
45
+ });
46
+ };
47
+ test("single-field hook is snapshot-stable while its topic is unsubscribed", function () {
48
+ var realtime = createLocalRealtime({
49
+ manifest: streaming.manifest
50
+ });
51
+ var useStreamingField = createUseStreamingField(function () {
52
+ return realtime.store;
53
+ });
54
+ function Card(param) {
55
+ var {
56
+ id,
57
+ base
58
+ } = param;
59
+ var state = useStreamingField(streaming.sootTask.description({
60
+ id
61
+ }), base);
62
+ return /* @__PURE__ */_jsx("div", {
63
+ children: state.value
64
+ });
65
+ }
66
+ act(function () {
67
+ root.render(/* @__PURE__ */_jsxs(_Fragment, {
68
+ children: [/* @__PURE__ */_jsx(Card, {
69
+ id: "t1",
70
+ base: ""
71
+ }), /* @__PURE__ */_jsx(Card, {
72
+ id: "t2",
73
+ base: "a durable description"
74
+ }), /* @__PURE__ */_jsx(Card, {
75
+ id: "t3",
76
+ base: ""
77
+ })]
78
+ }));
79
+ });
80
+ expect(uncachedSnapshotWarnings()).toEqual([]);
81
+ });
82
+ test("multi-field hook is snapshot-stable while topics are unsubscribed", function () {
83
+ var realtime = createLocalRealtime({
84
+ manifest: streaming.manifest
85
+ });
86
+ var useStreamingFields = createUseStreamingFields(function () {
87
+ return realtime.store;
88
+ });
89
+ var requests = [{
90
+ key: "t1",
91
+ handle: streaming.sootTask.description({
92
+ id: "t1"
93
+ }),
94
+ base: ""
95
+ }, {
96
+ key: "t2",
97
+ handle: streaming.sootTask.description({
98
+ id: "t2"
99
+ }),
100
+ base: "x"
101
+ }];
102
+ function List() {
103
+ var states = useStreamingFields(requests);
104
+ return /* @__PURE__ */_jsx("div", {
105
+ children: Object.keys(states).length
106
+ });
107
+ }
108
+ act(function () {
109
+ root.render(/* @__PURE__ */_jsx(List, {}));
110
+ });
111
+ expect(uncachedSnapshotWarnings()).toEqual([]);
112
+ });
113
+ //# sourceMappingURL=useStreamingField.loop.test.native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["jsx","_jsx","jsxs","_jsxs","Fragment","_Fragment","createSchema","string","table","createLocalRealtime","defineStreamingFields","act","createRoot","afterEach","beforeEach","expect","test","vi","createUseStreamingField","createUseStreamingFields","globalThis","IS_REACT_ACT_ENVIRONMENT","sootTask","columns","id","title","description","primaryKey","schema","tables","streaming","maxBytes","maxUpdatesPerSecond","maxBytesPerSecond","container","root","consoleError","document","createElement","body","appendChild","spyOn","console","mockImplementation","unmount","remove","mockRestore","uncachedSnapshotWarnings","uncachedSnapshotWarnings2","mock","calls","filter","call","String","includes","realtime","manifest","useStreamingField","store","Card","param","base","state","children","value","render"],"sources":["../../src/useStreamingField.loop.test.tsx"],"sourcesContent":[null],"mappings":"AAeA,SAASA,GAAA,IAAAC,IAAA,EAAAC,IAAc,IAAAC,KAAQ,EAAAC,QAAa,IAAAC,SAAA;AAC5C,SAASC,YAAA,EAAAC,MAAA,EAAqBC,KAAA,wBAA6B;AAC3D,SAASC,mBAAW,EAAAC,qBAAA;AACpB,SAASC,GAAA,eAA6B;AACtC,SAASC,UAAA,QAAW,kBAAoB;AAExC,SAASC,SAAA,EAAAC,UAAA,EAAAC,MAAyB,EAAAC,IAAA,EAAAC,EAAA;AAmDvB,SAQLC,uBAAA,EAAAC,wBARK;AA7CXC,UAAA,CAAWC,wBAAA,GAA2B;AAEtC,IAAAC,QAAM,GAAAd,KAAW,WAAM,EAAUe,OAC9B;EAEHC,EAAA,EAAMjB,MAAA;EAENkB,KAAM,EAAAlB,MAAA;EACJmB,WAAU,EAAAnB,MAAA;AAAA,GAAAoB,UACR,KAAa;AAAA,IAAAC,MACX,GAAAtB,YAAU;EAAAuB,MACV,GAAqBP,QACrB;AACF,EACF;AACF,IAACQ,SAAA,GAAApB,qBAAA,CAAAkB,MAAA;EAEDN,QAAI;IACAI,WAAA;MACAK,QAAA;MAEJC,mBAAiB;MACfC,iBAAqB;IACrB;EACA;AACA;AAAmE,IAAEC,SAAA;AACvE,IAACC,IAAA;AAED,IAAAC,YAAgB;AACdtB,UAAI,aAAW;EACfoB,SAAA,GAAUG,QAAO,CAAAC,aAAA;EACjBD,QAAA,CAAAE,IAAa,CAAAC,WAAY,CAAAN,SAAA;EAC1BC,IAAA,GAAAvB,UAAA,CAAAsB,SAAA;EAEDE,YAAM,GAAAnB,EAAA,CAAAwB,KAAA,CAAAC,OAA2B,SAC/B,EAAAC,kBAAkB,CAAM,aAAO,CAAC;AAEhC;AAEF9B,SAAK;EACHF,GAAA,aAAM;IACN,OAAMwB,IAAA,CAAAS,OAAA;EAEN;EACEV,SAAM,CAAAW,MAAQ;EACdT,YAAO,CAAAU,WAAA;AAAkB,EAC3B;AAEA,IAAAC,wBAAU,YAAAC,0BAAA;EAIR,OAAKZ,YAAA,CAAAa,IAAA,CAAAC,KAAA,CAAAC,MAAA,WAAAC,IAAA;IAAA,OACHC,MAAA,CAAAD,IAAA,KAAAE,QAAA,+BACE;EAAA;AAAsB;AACqBtC,IAAA,uEACrB;EAAA,IAAAuC,QACxB,GAAA9C,mBAAA;IACF+C,QAAA,EAAA1B,SAAA,CAAA0B;EACF,CAAC;EAED,IAAAC,iBAAO,GAAAvC,uBAAsC;IAC9C,OAAAqC,QAAA,CAAAG,KAAA;EAED;EACE,SAAMC,KAAAC,KAAW;IACjB,IAAM;MAAApC,EAAA;MAAAqC;IAAA,IAAAD,KAAA;IAEN,IAAME,KAAA,GAAAL,iBAAW,CAAA3B,SAAA,CAAAR,QAAA,CAAAI,WAAA;MACbF;IACF,EAAE,EAAAqC,IAAK;IACT,sBAAA5D,IAAA;MAEA8D,QAAS,EAAAD,KAAO,CAAAE;IACd;EACA;EACFrD,GAAA;IAEAwB,IAAI,CAAA8B,MAAM,gBAAA9D,KAAA,CAAAE,SAAA;MACR0D,QAAK,EAAO,CACb,eAAA9D,IAAA,CAAA0D,IAAA;QAEMnC,EAAA;QACRqC,IAAA","ignoreList":[]}
@@ -1,5 +1,5 @@
1
- import { canonicalTopic } from "orez-sync-executor/realtime";
2
- import { useCallback, useMemo, useSyncExternalStore } from "react";
1
+ import { canonicalTopic } from "orez-lite/realtime";
2
+ import { useCallback, useMemo, useRef, useSyncExternalStore } from "react";
3
3
  function createUseStreamingField(getStore) {
4
4
  return function useStreamingField(handle, base) {
5
5
  const store = getStore();
@@ -8,9 +8,13 @@ function createUseStreamingField(getStore) {
8
8
  if (!store || !handle) return () => {};
9
9
  return store.subscribe(handle, onChange);
10
10
  }, [store, id]);
11
+ const last = useRef(void 0);
11
12
  const getSnapshot = useCallback(() => {
12
- if (!store || !handle) return durableOnly(base);
13
- return store.read(handle, base);
13
+ const next = !store || !handle ? durableState(base) : store.read(handle, base);
14
+ const cached = last.current;
15
+ if (cached && sameState(cached, next)) return cached;
16
+ last.current = next;
17
+ return next;
14
18
  }, [store, id, base]);
15
19
  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
16
20
  };
@@ -30,15 +34,18 @@ function createUseStreamingFields(getStore) {
30
34
  value: {}
31
35
  }), [store, topicsKey]);
32
36
  const getSnapshot = useCallback(() => {
37
+ const previous = cache.value;
33
38
  const next = {};
34
39
  let changed = false;
35
40
  for (const request of requests) {
36
- const state = store ? store.read(request.handle, request.base) : durableOnly(request.base);
41
+ const raw = store ? store.read(request.handle, request.base) : durableState(request.base);
42
+ const cached = previous[request.key];
43
+ const state = cached && sameState(cached, raw) ? cached : raw;
37
44
  next[request.key] = state;
38
- if (cache.value[request.key] !== state) changed = true;
45
+ if (state !== cached) changed = true;
39
46
  }
40
- if (!changed && Object.keys(cache.value).length === Object.keys(next).length) {
41
- return cache.value;
47
+ if (!changed && Object.keys(previous).length === Object.keys(next).length) {
48
+ return previous;
42
49
  }
43
50
  cache.value = next;
44
51
  return next;
@@ -46,31 +53,15 @@ function createUseStreamingFields(getStore) {
46
53
  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
47
54
  };
48
55
  }
49
- const DURABLE_CACHE = /* @__PURE__ */new WeakMap();
50
- let lastPrimitiveBase;
51
- let lastPrimitiveState;
52
- function durableOnly(base) {
53
- if (base !== null && typeof base === "object") {
54
- const cached = DURABLE_CACHE.get(base);
55
- if (cached) return cached;
56
- const state = {
57
- value: base,
58
- phase: "durable",
59
- streamID: null
60
- };
61
- DURABLE_CACHE.set(base, state);
62
- return state;
63
- }
64
- if (lastPrimitiveState && Object.is(lastPrimitiveBase, base)) {
65
- return lastPrimitiveState;
66
- }
67
- lastPrimitiveBase = base;
68
- lastPrimitiveState = {
56
+ function sameState(a, b) {
57
+ return Object.is(a.value, b.value) && a.phase === b.phase && a.streamID === b.streamID;
58
+ }
59
+ function durableState(base) {
60
+ return {
69
61
  value: base,
70
62
  phase: "durable",
71
63
  streamID: null
72
64
  };
73
- return lastPrimitiveState;
74
65
  }
75
66
  export { createUseStreamingField, createUseStreamingFields };
76
67
  //# sourceMappingURL=useStreamingField.mjs.map
@@ -1 +1 @@
1
- {"version":3,"names":["canonicalTopic","useCallback","useMemo","useSyncExternalStore","createUseStreamingField","getStore","useStreamingField","handle","base","store","id","spec","primaryKey","topic","subscribe","onChange","getSnapshot","durableOnly","read","createUseStreamingFields","useStreamingFields","requests","topicsKey","map","request","key","join","releases","release","cache","value","next","changed","state","Object","keys","length","DURABLE_CACHE","WeakMap","lastPrimitiveBase","lastPrimitiveState","cached","get","phase","streamID","set","is"],"sources":["../../src/useStreamingField.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["canonicalTopic","useCallback","useMemo","useRef","useSyncExternalStore","createUseStreamingField","getStore","useStreamingField","handle","base","store","id","spec","primaryKey","topic","subscribe","onChange","last","getSnapshot","next","durableState","read","cached","current","sameState","createUseStreamingFields","useStreamingFields","requests","topicsKey","map","request","key","join","releases","release","cache","value","previous","changed","raw","state","Object","keys","length","a","b","is","phase","streamID"],"sources":["../../src/useStreamingField.ts"],"sourcesContent":[null],"mappings":"AAmBA,SAASA,cAAA,QAAsB;AAC/B,SAASC,WAAA,EAAaC,OAAA,EAASC,MAAA,EAAQC,oBAAA,QAA4B;AAqC5D,SAASC,wBACdC,QAAA,EACmB;EACnB,OAAO,SAASC,kBACdC,MAAA,EACAC,IAAA,EAC4B;IAC5B,MAAMC,KAAA,GAAQJ,QAAA,CAAS;IAGvB,MAAMK,EAAA,GAAKH,MAAA,GAASR,cAAA,CAAeQ,MAAA,CAAOI,IAAA,CAAKC,UAAA,EAAYL,MAAA,CAAOM,KAAK,IAAI;IAE3E,MAAMC,SAAA,GAAYd,WAAA,CACfe,QAAA,IAAyB;MACxB,IAAI,CAACN,KAAA,IAAS,CAACF,MAAA,EAAQ,OAAO,MAAM,CAAC;MACrC,OAAOE,KAAA,CAAMK,SAAA,CAAUP,MAAA,EAAQQ,QAAQ;IACzC,GACA,CAACN,KAAA,EAAOC,EAAE,CACZ;IAYA,MAAMM,IAAA,GAAOd,MAAA,CAA+C,MAAS;IACrE,MAAMe,WAAA,GAAcjB,WAAA,CAAY,MAAM;MACpC,MAAMkB,IAAA,GAAO,CAACT,KAAA,IAAS,CAACF,MAAA,GAASY,YAAA,CAAaX,IAAI,IAAIC,KAAA,CAAMW,IAAA,CAAKb,MAAA,EAAQC,IAAI;MAC7E,MAAMa,MAAA,GAASL,IAAA,CAAKM,OAAA;MACpB,IAAID,MAAA,IAAUE,SAAA,CAAUF,MAAA,EAAQH,IAAI,GAAG,OAAOG,MAAA;MAC9CL,IAAA,CAAKM,OAAA,GAAUJ,IAAA;MACf,OAAOA,IAAA;IACT,GAAG,CAACT,KAAA,EAAOC,EAAA,EAAIF,IAAI,CAAC;IAEpB,OAAOL,oBAAA,CAAqBW,SAAA,EAAWG,WAAA,EAAaA,WAAW;EACjE;AACF;AASO,SAASO,yBACdnB,QAAA,EACoB;EACpB,OAAO,SAASoB,mBACdC,QAAA,EACsD;IACtD,MAAMjB,KAAA,GAAQJ,QAAA,CAAS;IAIvB,MAAMsB,SAAA,GAAY1B,OAAA,CAChB,MACEyB,QAAA,CACGE,GAAA,CACEC,OAAA,IACC,GAAGA,OAAA,CAAQC,GAAG,IAAI/B,cAAA,CAAe8B,OAAA,CAAQtB,MAAA,CAAOI,IAAA,CAAKC,UAAA,EAAYiB,OAAA,CAAQtB,MAAA,CAAOM,KAAK,CAAC,EAC1F,EACCkB,IAAA,CAAK,IAAI,GACd,CAACL,QAAQ,CACX;IAEA,MAAMZ,SAAA,GAAYd,WAAA,CACfe,QAAA,IAAyB;MACxB,IAAI,CAACN,KAAA,EAAO,OAAO,MAAM,CAAC;MAC1B,MAAMuB,QAAA,GAAWN,QAAA,CAASE,GAAA,CAAKC,OAAA,IAC7BpB,KAAA,CAAMK,SAAA,CAAUe,OAAA,CAAQtB,MAAA,EAAQQ,QAAQ,CAC1C;MACA,OAAO,MAAM;QACX,WAAWkB,OAAA,IAAWD,QAAA,EAAUC,OAAA,CAAQ;MAC1C;IACF,GACA,CAACxB,KAAA,EAAOkB,SAAS,CACnB;IAKA,MAAMO,KAAA,GAAQjC,OAAA,CACZ,OAAO;MAAEkC,KAAA,EAAO,CAAC;IAAgD,IACjE,CAAC1B,KAAA,EAAOkB,SAAS,CACnB;IAEA,MAAMV,WAAA,GAAcjB,WAAA,CAAY,MAAM;MACpC,MAAMoC,QAAA,GAAWF,KAAA,CAAMC,KAAA;MACvB,MAAMjB,IAAA,GAAmD,CAAC;MAC1D,IAAImB,OAAA,GAAU;MACd,WAAWR,OAAA,IAAWH,QAAA,EAAU;QAC9B,MAAMY,GAAA,GAAM7B,KAAA,GACRA,KAAA,CAAMW,IAAA,CAAKS,OAAA,CAAQtB,MAAA,EAAQsB,OAAA,CAAQrB,IAAI,IACvCW,YAAA,CAAaU,OAAA,CAAQrB,IAAI;QAK7B,MAAMa,MAAA,GAASe,QAAA,CAASP,OAAA,CAAQC,GAAG;QACnC,MAAMS,KAAA,GAAQlB,MAAA,IAAUE,SAAA,CAAUF,MAAA,EAAQiB,GAAG,IAAIjB,MAAA,GAASiB,GAAA;QAC1DpB,IAAA,CAAKW,OAAA,CAAQC,GAAG,IAAIS,KAAA;QACpB,IAAIA,KAAA,KAAUlB,MAAA,EAAQgB,OAAA,GAAU;MAClC;MACA,IAAI,CAACA,OAAA,IAAWG,MAAA,CAAOC,IAAA,CAAKL,QAAQ,EAAEM,MAAA,KAAWF,MAAA,CAAOC,IAAA,CAAKvB,IAAI,EAAEwB,MAAA,EAAQ;QACzE,OAAON,QAAA;MACT;MACAF,KAAA,CAAMC,KAAA,GAAQjB,IAAA;MACd,OAAOA,IAAA;IACT,GAAG,CAACT,KAAA,EAAOkB,SAAA,EAAWO,KAAA,EAAOR,QAAQ,CAAC;IAEtC,OAAOvB,oBAAA,CAAqBW,SAAA,EAAWG,WAAA,EAAaA,WAAW;EACjE;AACF;AAEA,SAASM,UACPoB,CAAA,EACAC,CAAA,EACS;EAIT,OAAOJ,MAAA,CAAOK,EAAA,CAAGF,CAAA,CAAER,KAAA,EAAOS,CAAA,CAAET,KAAK,KAAKQ,CAAA,CAAEG,KAAA,KAAUF,CAAA,CAAEE,KAAA,IAASH,CAAA,CAAEI,QAAA,KAAaH,CAAA,CAAEG,QAAA;AAChF;AAEA,SAAS5B,aAAoBX,IAAA,EAAyC;EACpE,OAAO;IAAE2B,KAAA,EAAO3B,IAAA;IAAMsC,KAAA,EAAO;IAAWC,QAAA,EAAU;EAAK;AACzD","ignoreList":[]}
@@ -1,10 +1,5 @@
1
- import { canonicalTopic } from "orez-sync-executor/realtime";
2
- import { useCallback, useMemo, useSyncExternalStore } from "react";
3
- function _type_of(obj) {
4
- "@swc/helpers - typeof";
5
-
6
- return obj && typeof Symbol !== "undefined" && obj.constructor === Symbol ? "symbol" : typeof obj;
7
- }
1
+ import { canonicalTopic } from "orez-lite/realtime";
2
+ import { useCallback, useMemo, useRef, useSyncExternalStore } from "react";
8
3
  function createUseStreamingField(getStore) {
9
4
  return function useStreamingField(handle, base) {
10
5
  var store = getStore();
@@ -13,9 +8,13 @@ function createUseStreamingField(getStore) {
13
8
  if (!store || !handle) return function () {};
14
9
  return store.subscribe(handle, onChange);
15
10
  }, [store, id]);
11
+ var last = useRef(void 0);
16
12
  var getSnapshot = useCallback(function () {
17
- if (!store || !handle) return durableOnly(base);
18
- return store.read(handle, base);
13
+ var next = !store || !handle ? durableState(base) : store.read(handle, base);
14
+ var cached = last.current;
15
+ if (cached && sameState(cached, next)) return cached;
16
+ last.current = next;
17
+ return next;
19
18
  }, [store, id, base]);
20
19
  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
21
20
  };
@@ -64,6 +63,7 @@ function createUseStreamingFields(getStore) {
64
63
  };
65
64
  }, [store, topicsKey]);
66
65
  var getSnapshot = useCallback(function () {
66
+ var previous = cache.value;
67
67
  var next = {};
68
68
  var changed = false;
69
69
  var _iteratorNormalCompletion = true,
@@ -72,9 +72,11 @@ function createUseStreamingFields(getStore) {
72
72
  try {
73
73
  for (var _iterator = requests[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
74
74
  var request = _step.value;
75
- var state = store ? store.read(request.handle, request.base) : durableOnly(request.base);
75
+ var raw = store ? store.read(request.handle, request.base) : durableState(request.base);
76
+ var cached = previous[request.key];
77
+ var state = cached && sameState(cached, raw) ? cached : raw;
76
78
  next[request.key] = state;
77
- if (cache.value[request.key] !== state) changed = true;
79
+ if (state !== cached) changed = true;
78
80
  }
79
81
  } catch (err) {
80
82
  _didIteratorError = true;
@@ -90,8 +92,8 @@ function createUseStreamingFields(getStore) {
90
92
  }
91
93
  }
92
94
  }
93
- if (!changed && Object.keys(cache.value).length === Object.keys(next).length) {
94
- return cache.value;
95
+ if (!changed && Object.keys(previous).length === Object.keys(next).length) {
96
+ return previous;
95
97
  }
96
98
  cache.value = next;
97
99
  return next;
@@ -99,31 +101,15 @@ function createUseStreamingFields(getStore) {
99
101
  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
100
102
  };
101
103
  }
102
- var DURABLE_CACHE = /* @__PURE__ */new WeakMap();
103
- var lastPrimitiveBase;
104
- var lastPrimitiveState;
105
- function durableOnly(base) {
106
- if (base !== null && (typeof base === "undefined" ? "undefined" : _type_of(base)) === "object") {
107
- var cached = DURABLE_CACHE.get(base);
108
- if (cached) return cached;
109
- var state = {
110
- value: base,
111
- phase: "durable",
112
- streamID: null
113
- };
114
- DURABLE_CACHE.set(base, state);
115
- return state;
116
- }
117
- if (lastPrimitiveState && Object.is(lastPrimitiveBase, base)) {
118
- return lastPrimitiveState;
119
- }
120
- lastPrimitiveBase = base;
121
- lastPrimitiveState = {
104
+ function sameState(a, b) {
105
+ return Object.is(a.value, b.value) && a.phase === b.phase && a.streamID === b.streamID;
106
+ }
107
+ function durableState(base) {
108
+ return {
122
109
  value: base,
123
110
  phase: "durable",
124
111
  streamID: null
125
112
  };
126
- return lastPrimitiveState;
127
113
  }
128
114
  export { createUseStreamingField, createUseStreamingFields };
129
115
  //# sourceMappingURL=useStreamingField.native.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["canonicalTopic","useCallback","useMemo","useSyncExternalStore","_type_of","obj","Symbol","constructor","createUseStreamingField","getStore","useStreamingField","handle","base","store","id","spec","primaryKey","topic","subscribe","onChange","getSnapshot","durableOnly","read","createUseStreamingFields","useStreamingFields","requests","topicsKey","map","request","key","join","releases","_iteratorNormalCompletion","_didIteratorError","_iteratorError","_iterator","iterator","_step","next","done","release","value","err","return","cache","changed"],"sources":["../../src/useStreamingField.ts"],"sourcesContent":[null],"mappings":"AAmBA,SAASA,cAAA,QAAsB;AAC/B,SAASC,WAAA,EAAaC,OAAA,EAASC,oBAAA,QAA4B;AAqCpD,SAASC,SAAAC,GAAA;EAGd,uBAAgB;;EAId,OAAAA,GAAM,WAAQC,MAAS,oBAAAD,GAAA,CAAAE,WAAA,KAAAD,MAAA,qBAAAD,GAAA;AAGvB;AAEA,SAAAG,uBAAkBA,CAAAC,QAAA;EAAA,OACf,SAAAC,iBAAyBA,CAAAC,MAAA,EAAAC,IAAA;IACxB,IAAAC,KAAK,GAAAJ,QAAU;IAAqB,IAACK,EAAA,GAAAH,MAAA,GAAAX,cAAA,CAAAW,MAAA,CAAAI,IAAA,CAAAC,UAAA,EAAAL,MAAA,CAAAM,KAAA;IACrC,IAAAC,SAAO,GAAAjB,WAAgB,WAAQkB,QAAQ;MACzC,KAAAN,KAAA,KAAAF,MAAA,sBACA,CAAC;MACH,OAAAE,KAAA,CAAAK,SAAA,CAAAP,MAAA,EAAAQ,QAAA;IAMA,IACEN,KAAK,EACLC,EAAA,CACF;IAEA,IAAAM,WAAO,GAAAnB,WAAqB,aAAW;MACzC,KAAAY,KAAA,KAAAF,MAAA,SAAAU,WAAA,CAAAT,IAAA;MACF,OAAAC,KAAA,CAAAS,IAAA,CAAAX,MAAA,EAAAC,IAAA;IASO,IAGLC,KAAO,EAGLC,EAAA,EAIAF,IAAM,CAAY,CAChB;IAEK,OACET,oBACI,CAAQe,SAAO,EAAAE,WAAe,EAAAA,WAAQ;EAA6C;AAEhF;AACL,SACXG,yBAAAd,QAAA;EAEA,gBAAMe,kBAAYA,CAAAC,QAAA;IAAA,IACfZ,KAAA,GAAAJ,QAAyB;IACxB,IAAAiB,SAAK,GAAOxB,OAAO,aAAM;MAAA,OAACuB,QAAA,CAAAE,GAAA,WAAAC,OAAA;QAC1B,OAAM,GAAAA,OAAW,CAAAC,GAAA,IAAA7B,cAAS,CAAA4B,OAAA,CAAAjB,MAAA,CAAAI,IAAA,CAAAC,UAAA,EAAAY,OAAA,CAAAjB,MAAA,CAAAM,KAAA;MAAA,GAAAa,IAAK;IACW,IAE1CL,QAAA,CACE;IAAwC,IAC1CP,SAAA,GAAAjB,WAAA,WAAAkB,QAAA;MACF,KAAAN,KAAA,sBACA,CAAC;MACH,IAAAkB,QAAA,GAAAN,QAAA,CAAAE,GAAA,WAAAC,OAAA;QAKA,OAAMf,KAAQ,CAAAK,SAAA,CAAAU,OAAA,CAAAjB,MAAA,EAAAQ,QAAA;MACZ;MACA,OAAC,YAAgB;QACnB,IAAAa,yBAAA;UAAAC,iBAAA;UAAAC,cAAA;QAEA,IAAM;UACJ,KAAM,IAAmDC,SAAC,GAAAJ,QAAA,CAAAzB,MAAA,CAAA8B,QAAA,KAAAC,KAAA,IAAAL,yBAAA,IAAAK,KAAA,GAAAF,SAAA,CAAAG,IAAA,IAAAC,IAAA,GAAAP,yBAAA;YACtD,IAAAQ,OAAU,GAAAH,KAAA,CAAAI,KAAA;YACdD,OAAW;UACT;QAGA,SAAKE,GAAA,EAAQ;UACbT,iBAAgB,OAAQ;UAC1BC,cAAA,GAAAQ,GAAA;QACA,UAAK;UACH;YACF,KAAAV,yBAAA,IAAAG,SAAA,CAAAQ,MAAA;cACMR,SAAQ,CAAAQ,MAAA;YACd;UACE,UAAO;YAEJ,IAAAV,iBAAqB;cAC9B,MAAAC,cAAA;YACF;UAEM;QACF;MACA;IAGJ,IACMrB,KAAA,EACFa,SAAM,CACN;IACA,IAAAkB,KAAM,GAAA1C,OAAU,aAAa;MAC7B;QACAuC,KAAO;MACT;IACA,GAAI,CACF5B,KAAO,EACTa,SAAA,CACA;IACA,IAAAN,WAAA,GAAAnB,WAA8B,aAAa;MAC3C,IAAOqC,IAAA;MACT,IAAAO,OAAA","ignoreList":[]}
1
+ {"version":3,"names":["canonicalTopic","useCallback","useMemo","useRef","useSyncExternalStore","createUseStreamingField","getStore","useStreamingField","handle","base","store","id","spec","primaryKey","topic","subscribe","onChange","last","getSnapshot","next","durableState","read","cached","current","sameState","createUseStreamingFields","useStreamingFields","requests","topicsKey","map","request","key","join","releases","_iteratorNormalCompletion","_didIteratorError","_iteratorError","_iterator","Symbol","iterator","_step","done","release","value","err","return","cache"],"sources":["../../src/useStreamingField.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "on-zero",
3
- "version": "0.11.2",
3
+ "version": "0.11.3",
4
4
  "description": "A typed layer over @rocicorp/zero with queries, mutations, and permissions",
5
5
  "sideEffects": false,
6
6
  "files": [
@@ -105,8 +105,7 @@
105
105
  "chokidar": "^4.0.3",
106
106
  "citty": "^0.1.6",
107
107
  "dequal": "^2.0.3",
108
- "orez-lite": "0.11.2",
109
- "orez-sync-executor": "0.11.2",
108
+ "orez-lite": "0.11.3",
110
109
  "valibot": "^1.1.0"
111
110
  },
112
111
  "peerDependencies": {
@@ -0,0 +1,111 @@
1
+ // @vitest-environment jsdom
2
+ //
3
+ // useSyncExternalStore's getSnapshot contract: the same reference must come
4
+ // back until something observable changes. React DEV enforces it by calling
5
+ // getSnapshot twice per render and warning "The result of getSnapshot should
6
+ // be cached to avoid an infinite loop"; in production the same instability is
7
+ // a forceStoreRerender loop that throws "Maximum update depth exceeded".
8
+ //
9
+ // The store only stabilizes SUBSCRIBED topics. An unsubscribed read — the
10
+ // render before the subscription effect lands, or every render after an error
11
+ // boundary keeps that effect from ever landing — built a fresh state object
12
+ // per call, and a factory workspace rendering a task card with an empty
13
+ // description looped React until the whole pane tree unmounted. The hooks now
14
+ // carry their own per-hook stabilization, which this file locks in.
15
+
16
+ import { createSchema, string, table } from '@rocicorp/zero'
17
+ import { createLocalRealtime, defineStreamingFields } from 'orez-lite/realtime'
18
+ import { act } from 'react'
19
+ import { createRoot, type Root } from 'react-dom/client'
20
+ import { afterEach, beforeEach, expect, test, vi } from 'vitest'
21
+
22
+ import { createUseStreamingField, createUseStreamingFields } from './useStreamingField'
23
+
24
+ declare global {
25
+ // eslint-disable-next-line no-var
26
+ var IS_REACT_ACT_ENVIRONMENT: boolean | undefined
27
+ }
28
+ globalThis.IS_REACT_ACT_ENVIRONMENT = true
29
+
30
+ const sootTask = table('sootTask')
31
+ .columns({ id: string(), title: string(), description: string() })
32
+ .primaryKey('id')
33
+ const schema = createSchema({ tables: [sootTask] })
34
+
35
+ const streaming = defineStreamingFields(schema, {
36
+ sootTask: {
37
+ description: {
38
+ maxBytes: 100_000,
39
+ maxUpdatesPerSecond: 30,
40
+ maxBytesPerSecond: 200_000,
41
+ },
42
+ },
43
+ })
44
+
45
+ let container: HTMLDivElement
46
+ let root: Root
47
+ let consoleError: ReturnType<typeof vi.spyOn>
48
+
49
+ beforeEach(() => {
50
+ container = document.createElement('div')
51
+ document.body.appendChild(container)
52
+ root = createRoot(container)
53
+ consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
54
+ })
55
+
56
+ afterEach(() => {
57
+ act(() => root.unmount())
58
+ container.remove()
59
+ consoleError.mockRestore()
60
+ })
61
+
62
+ const uncachedSnapshotWarnings = () =>
63
+ consoleError.mock.calls.filter((call) =>
64
+ String(call[0]).includes('getSnapshot should be cached')
65
+ )
66
+
67
+ test('single-field hook is snapshot-stable while its topic is unsubscribed', () => {
68
+ const realtime = createLocalRealtime({ manifest: streaming.manifest })
69
+ const useStreamingField = createUseStreamingField(() => realtime.store)
70
+
71
+ function Card({ id, base }: { id: string; base: string }) {
72
+ const state = useStreamingField(streaming.sootTask.description({ id }), base)
73
+ return <div>{state.value}</div>
74
+ }
75
+
76
+ act(() => {
77
+ // several consumers with DIFFERENT bases interleave in one render pass, so
78
+ // a shared module-level cache cannot satisfy the contract — only per-hook
79
+ // stabilization can
80
+ root.render(
81
+ <>
82
+ <Card id="t1" base="" />
83
+ <Card id="t2" base="a durable description" />
84
+ <Card id="t3" base="" />
85
+ </>
86
+ )
87
+ })
88
+
89
+ expect(uncachedSnapshotWarnings()).toEqual([])
90
+ })
91
+
92
+ test('multi-field hook is snapshot-stable while topics are unsubscribed', () => {
93
+ const realtime = createLocalRealtime({ manifest: streaming.manifest })
94
+ const useStreamingFields = createUseStreamingFields(() => realtime.store)
95
+
96
+ const requests = [
97
+ { key: 't1', handle: streaming.sootTask.description({ id: 't1' }), base: '' },
98
+ { key: 't2', handle: streaming.sootTask.description({ id: 't2' }), base: 'x' },
99
+ ]
100
+
101
+ function List() {
102
+ const states = useStreamingFields(requests)
103
+ return <div>{Object.keys(states).length}</div>
104
+ }
105
+
106
+ act(() => {
107
+ root.render(<List />)
108
+ })
109
+
110
+ expect(uncachedSnapshotWarnings()).toEqual([])
111
+ })
@@ -17,14 +17,14 @@
17
17
  // is the property an app hand-rolls otherwise, usually as a global emitter plus
18
18
  // a deep-equal projection to undo the over-broadcasting.
19
19
 
20
- import { canonicalTopic } from 'orez-sync-executor/realtime'
21
- import { useCallback, useMemo, useSyncExternalStore } from 'react'
20
+ import { canonicalTopic } from 'orez-lite/realtime'
21
+ import { useCallback, useMemo, useRef, useSyncExternalStore } from 'react'
22
22
 
23
23
  import type {
24
24
  RealtimeStore,
25
25
  StreamingFieldHandle,
26
26
  StreamingFieldState,
27
- } from 'orez-sync-executor/realtime'
27
+ } from 'orez-lite/realtime'
28
28
 
29
29
  // `streaming.message.content({ id })` returns a StreamingFieldHandle: the row's
30
30
  // topic plus the manifest spec for that column, so a hook needs no lookup and
@@ -75,13 +75,23 @@ export function createUseStreamingField(
75
75
  [store, id]
76
76
  )
77
77
 
78
- // `store.read` returns a reference-stable state: useSyncExternalStore
79
- // re-invokes getSnapshot on every render and loops forever if the result is
80
- // a fresh object each time. `base` participates because a new durable value
81
- // from Zero is what ends the committing phase.
78
+ // getSnapshot must hand back the SAME reference until something observable
79
+ // changes: useSyncExternalStore re-invokes it on every render and rerenders
80
+ // forever (throwing "Maximum update depth exceeded") if each call returns a
81
+ // fresh object. The store only stabilizes SUBSCRIBED topics — an
82
+ // unsubscribed read (before the subscription effect lands, after an error
83
+ // boundary unmounts the tree so it never lands, or with a null handle) has
84
+ // no entry to cache on. Each hook therefore carries its own last-state
85
+ // slot, which also survives interleaved renders of many hooks in a way a
86
+ // module-level cache cannot. `base` participates because a new durable
87
+ // value from Zero is what ends the committing phase.
88
+ const last = useRef<StreamingFieldState<Value> | undefined>(undefined)
82
89
  const getSnapshot = useCallback(() => {
83
- if (!store || !handle) return durableOnly(base)
84
- return store.read(handle, base)
90
+ const next = !store || !handle ? durableState(base) : store.read(handle, base)
91
+ const cached = last.current
92
+ if (cached && sameState(cached, next)) return cached
93
+ last.current = next
94
+ return next
85
95
  }, [store, id, base])
86
96
 
87
97
  return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
@@ -138,17 +148,24 @@ export function createUseStreamingFields(
138
148
  )
139
149
 
140
150
  const getSnapshot = useCallback(() => {
151
+ const previous = cache.value
141
152
  const next: Record<string, StreamingFieldState<Value>> = {}
142
153
  let changed = false
143
154
  for (const request of requests) {
144
- const state = store
155
+ const raw = store
145
156
  ? store.read(request.handle, request.base)
146
- : durableOnly(request.base)
157
+ : durableState(request.base)
158
+ // stabilize per key here, not just per store entry: an unsubscribed
159
+ // topic's read is a fresh object each call, and one unstable member
160
+ // would otherwise force a fresh map from every getSnapshot — the same
161
+ // rerender loop the single-field hook guards against.
162
+ const cached = previous[request.key]
163
+ const state = cached && sameState(cached, raw) ? cached : raw
147
164
  next[request.key] = state
148
- if (cache.value[request.key] !== state) changed = true
165
+ if (state !== cached) changed = true
149
166
  }
150
- if (!changed && Object.keys(cache.value).length === Object.keys(next).length) {
151
- return cache.value
167
+ if (!changed && Object.keys(previous).length === Object.keys(next).length) {
168
+ return previous
152
169
  }
153
170
  cache.value = next
154
171
  return next
@@ -158,23 +175,16 @@ export function createUseStreamingFields(
158
175
  }
159
176
  }
160
177
 
161
- const DURABLE_CACHE = new WeakMap<object, StreamingFieldState<unknown>>()
162
- let lastPrimitiveBase: unknown
163
- let lastPrimitiveState: StreamingFieldState<unknown> | undefined
164
-
165
- // Unsubscribed reads still have to be reference-stable for the same reason.
166
- function durableOnly<Value>(base: Value): StreamingFieldState<Value> {
167
- if (base !== null && typeof base === 'object') {
168
- const cached = DURABLE_CACHE.get(base)
169
- if (cached) return cached as StreamingFieldState<Value>
170
- const state = { value: base, phase: 'durable', streamID: null } as const
171
- DURABLE_CACHE.set(base, state as StreamingFieldState<unknown>)
172
- return state as StreamingFieldState<Value>
173
- }
174
- if (lastPrimitiveState && Object.is(lastPrimitiveBase, base)) {
175
- return lastPrimitiveState as StreamingFieldState<Value>
176
- }
177
- lastPrimitiveBase = base
178
- lastPrimitiveState = { value: base, phase: 'durable', streamID: null }
179
- return lastPrimitiveState as StreamingFieldState<Value>
178
+ function sameState(
179
+ a: StreamingFieldState<unknown>,
180
+ b: StreamingFieldState<unknown>
181
+ ): boolean {
182
+ // exact: `value` is either the caller's own base reference or the store
183
+ // generation's accumulated value, both of which only change identity when
184
+ // they actually change
185
+ return Object.is(a.value, b.value) && a.phase === b.phase && a.streamID === b.streamID
186
+ }
187
+
188
+ function durableState<Value>(base: Value): StreamingFieldState<Value> {
189
+ return { value: base, phase: 'durable', streamID: null }
180
190
  }
@@ -1,4 +1,4 @@
1
- import type { RealtimeStore, StreamingFieldHandle, StreamingFieldState } from 'orez-sync-executor/realtime';
1
+ import type { RealtimeStore, StreamingFieldHandle, StreamingFieldState } from 'orez-lite/realtime';
2
2
  export type { StreamingFieldHandle };
3
3
  export type UseStreamingField = <Value>(handle: StreamingFieldHandle | null | undefined, base: Value) => StreamingFieldState<Value>;
4
4
  export type StreamingFieldRequest<Value = unknown> = {
@@ -1 +1 @@
1
- {"version":3,"file":"useStreamingField.d.ts","sourceRoot":"","sources":["../src/useStreamingField.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,6BAA6B,CAAA;AAKpC,YAAY,EAAE,oBAAoB,EAAE,CAAA;AAKpC,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EACpC,MAAM,EAAE,oBAAoB,GAAG,IAAI,GAAG,SAAS,EAC/C,IAAI,EAAE,KAAK,KACR,mBAAmB,CAAC,KAAK,CAAC,CAAA;AAK/B,MAAM,MAAM,qBAAqB,CAAC,KAAK,GAAG,OAAO,IAAI;IACnD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAA;IACrC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EACrC,QAAQ,EAAE,SAAS,qBAAqB,CAAC,KAAK,CAAC,EAAE,KAC9C,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAKzD,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,aAAa,GAAG,SAAS,GACxC,iBAAiB,CA6BnB;AASD,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,aAAa,GAAG,SAAS,GACxC,kBAAkB,CA2DpB"}
1
+ {"version":3,"file":"useStreamingField.d.ts","sourceRoot":"","sources":["../src/useStreamingField.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,oBAAoB,CAAA;AAK3B,YAAY,EAAE,oBAAoB,EAAE,CAAA;AAKpC,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EACpC,MAAM,EAAE,oBAAoB,GAAG,IAAI,GAAG,SAAS,EAC/C,IAAI,EAAE,KAAK,KACR,mBAAmB,CAAC,KAAK,CAAC,CAAA;AAK/B,MAAM,MAAM,qBAAqB,CAAC,KAAK,GAAG,OAAO,IAAI;IACnD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,MAAM,EAAE,oBAAoB,CAAA;IACrC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EACrC,QAAQ,EAAE,SAAS,qBAAqB,CAAC,KAAK,CAAC,EAAE,KAC9C,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;AAKzD,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,aAAa,GAAG,SAAS,GACxC,iBAAiB,CAuCnB;AASD,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,MAAM,aAAa,GAAG,SAAS,GACxC,kBAAkB,CAkEpB"}
@@ -0,0 +1,5 @@
1
+ declare global {
2
+ var IS_REACT_ACT_ENVIRONMENT: boolean | undefined;
3
+ }
4
+ export {};
5
+ //# sourceMappingURL=useStreamingField.loop.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStreamingField.loop.test.d.ts","sourceRoot":"","sources":["../src/useStreamingField.loop.test.tsx"],"names":[],"mappings":"AAuBA,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,wBAAwB,EAAE,OAAO,GAAG,SAAS,CAAA;CAClD"}