signalium 3.0.2 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/cjs/development/{component-shared-Cug2Y5YZ.js → component-shared-BjwFh5Jq.js} +2 -2
  3. package/dist/cjs/development/{component-shared-Cug2Y5YZ.js.map → component-shared-BjwFh5Jq.js.map} +1 -1
  4. package/dist/cjs/development/config-D6wyrhrW.js +58 -0
  5. package/dist/cjs/development/config-D6wyrhrW.js.map +1 -0
  6. package/dist/cjs/development/config.js +1 -1
  7. package/dist/cjs/development/{debug-C46WDxhL.js → debug-CbAX7VeM.js} +260 -43
  8. package/dist/cjs/development/debug-CbAX7VeM.js.map +1 -0
  9. package/dist/cjs/development/debug.js +3 -1
  10. package/dist/cjs/development/debug.js.map +1 -1
  11. package/dist/cjs/development/index.js +3 -2
  12. package/dist/cjs/development/index.js.map +1 -1
  13. package/dist/cjs/development/react/index.js +399 -52
  14. package/dist/cjs/development/react/index.js.map +1 -1
  15. package/dist/cjs/development/react/index.server.js +2 -2
  16. package/dist/cjs/development/react/index.server.js.map +1 -1
  17. package/dist/cjs/development/react/server.js +1 -1
  18. package/dist/cjs/development/{snapshot-CbZ6IH3B.js → snapshot-BTMVSd8Y.js} +4 -2
  19. package/dist/cjs/development/snapshot-BTMVSd8Y.js.map +1 -0
  20. package/dist/cjs/development/transform/index.js +5 -2
  21. package/dist/cjs/development/transform/index.js.map +1 -1
  22. package/dist/cjs/development/utils.js +34 -2
  23. package/dist/cjs/development/utils.js.map +1 -1
  24. package/dist/cjs/production/{component-shared-D0J85PUi.js → component-shared-CcxyLg4p.js} +2 -2
  25. package/dist/cjs/production/{component-shared-D0J85PUi.js.map → component-shared-CcxyLg4p.js.map} +1 -1
  26. package/dist/cjs/production/config-BHvmcsLQ.js +52 -0
  27. package/dist/cjs/production/config-BHvmcsLQ.js.map +1 -0
  28. package/dist/cjs/production/config.js +1 -1
  29. package/dist/cjs/production/{contexts-CM3QLfV8.js → contexts-waczQzHZ.js} +260 -43
  30. package/dist/cjs/production/contexts-waczQzHZ.js.map +1 -0
  31. package/dist/cjs/production/debug.js +3 -1
  32. package/dist/cjs/production/debug.js.map +1 -1
  33. package/dist/cjs/production/index.js +3 -2
  34. package/dist/cjs/production/index.js.map +1 -1
  35. package/dist/cjs/production/react/index.js +399 -52
  36. package/dist/cjs/production/react/index.js.map +1 -1
  37. package/dist/cjs/production/react/index.server.js +2 -2
  38. package/dist/cjs/production/react/index.server.js.map +1 -1
  39. package/dist/cjs/production/react/server.js +1 -1
  40. package/dist/cjs/production/{snapshot-DWwH7_9y.js → snapshot-7264bWj1.js} +4 -2
  41. package/dist/cjs/production/snapshot-7264bWj1.js.map +1 -0
  42. package/dist/cjs/production/transform/index.js +5 -2
  43. package/dist/cjs/production/transform/index.js.map +1 -1
  44. package/dist/cjs/production/utils.js +34 -2
  45. package/dist/cjs/production/utils.js.map +1 -1
  46. package/dist/esm/config.d.ts +1 -1
  47. package/dist/esm/config.d.ts.map +1 -1
  48. package/dist/esm/debug.d.ts +1 -0
  49. package/dist/esm/debug.d.ts.map +1 -1
  50. package/dist/esm/development/{component-shared-CLSmdnGU.js → component-shared-B3Q6mIzY.js} +2 -2
  51. package/dist/esm/development/{component-shared-CLSmdnGU.js.map → component-shared-B3Q6mIzY.js.map} +1 -1
  52. package/dist/esm/development/config-CQLKpPRj.js +59 -0
  53. package/dist/esm/development/config-CQLKpPRj.js.map +1 -0
  54. package/dist/esm/development/config.js +2 -2
  55. package/dist/esm/development/{debug-BTLbgTPK.js → debug-C3HD8WcT.js} +288 -71
  56. package/dist/esm/development/debug-C3HD8WcT.js.map +1 -0
  57. package/dist/esm/development/debug.js +12 -10
  58. package/dist/esm/development/index.js +18 -17
  59. package/dist/esm/development/react/index.js +400 -53
  60. package/dist/esm/development/react/index.js.map +1 -1
  61. package/dist/esm/development/react/index.server.js +2 -2
  62. package/dist/esm/development/react/index.server.js.map +1 -1
  63. package/dist/esm/development/react/server.js +1 -1
  64. package/dist/esm/development/{snapshot-CkndI4Hh.js → snapshot-CCKlBy-y.js} +7 -5
  65. package/dist/esm/development/snapshot-CCKlBy-y.js.map +1 -0
  66. package/dist/esm/development/transform/index.js +5 -2
  67. package/dist/esm/development/transform/index.js.map +1 -1
  68. package/dist/esm/development/utils.js +38 -6
  69. package/dist/esm/development/utils.js.map +1 -1
  70. package/dist/esm/index.d.ts +2 -2
  71. package/dist/esm/index.d.ts.map +1 -1
  72. package/dist/esm/internals/async.d.ts.map +1 -1
  73. package/dist/esm/internals/config.d.ts +40 -0
  74. package/dist/esm/internals/config.d.ts.map +1 -1
  75. package/dist/esm/internals/lease.d.ts +30 -0
  76. package/dist/esm/internals/lease.d.ts.map +1 -0
  77. package/dist/esm/internals/reactive.d.ts +18 -1
  78. package/dist/esm/internals/reactive.d.ts.map +1 -1
  79. package/dist/esm/internals/scheduling.d.ts +2 -1
  80. package/dist/esm/internals/scheduling.d.ts.map +1 -1
  81. package/dist/esm/internals/utils/hash.d.ts +7 -0
  82. package/dist/esm/internals/utils/hash.d.ts.map +1 -1
  83. package/dist/esm/internals/utils/snapshot.d.ts +2 -0
  84. package/dist/esm/internals/utils/snapshot.d.ts.map +1 -1
  85. package/dist/esm/internals/watch.d.ts +3 -2
  86. package/dist/esm/internals/watch.d.ts.map +1 -1
  87. package/dist/esm/production/{component-shared-Dva0S5e3.js → component-shared-CycOXiis.js} +2 -2
  88. package/dist/esm/production/{component-shared-Dva0S5e3.js.map → component-shared-CycOXiis.js.map} +1 -1
  89. package/dist/esm/production/config-BhtwQYAN.js +53 -0
  90. package/dist/esm/production/config-BhtwQYAN.js.map +1 -0
  91. package/dist/esm/production/config.js +2 -2
  92. package/dist/esm/production/{contexts-FxCndlvf.js → contexts-ry5lvNSx.js} +291 -74
  93. package/dist/esm/production/contexts-ry5lvNSx.js.map +1 -0
  94. package/dist/esm/production/debug.js +3 -1
  95. package/dist/esm/production/index.js +19 -18
  96. package/dist/esm/production/react/index.js +400 -53
  97. package/dist/esm/production/react/index.js.map +1 -1
  98. package/dist/esm/production/react/index.server.js +2 -2
  99. package/dist/esm/production/react/index.server.js.map +1 -1
  100. package/dist/esm/production/react/server.js +1 -1
  101. package/dist/esm/production/{snapshot-CdCaJJ0L.js → snapshot-1d_HVP3L.js} +7 -5
  102. package/dist/esm/production/snapshot-1d_HVP3L.js.map +1 -0
  103. package/dist/esm/production/transform/index.js +5 -2
  104. package/dist/esm/production/transform/index.js.map +1 -1
  105. package/dist/esm/production/utils.js +39 -7
  106. package/dist/esm/production/utils.js.map +1 -1
  107. package/dist/esm/react/async-component.d.ts +10 -2
  108. package/dist/esm/react/async-component.d.ts.map +1 -1
  109. package/dist/esm/react/component-server.d.ts +6 -2
  110. package/dist/esm/react/component-server.d.ts.map +1 -1
  111. package/dist/esm/react/component.d.ts +5 -2
  112. package/dist/esm/react/component.d.ts.map +1 -1
  113. package/dist/esm/react/delivery.d.ts +107 -0
  114. package/dist/esm/react/delivery.d.ts.map +1 -0
  115. package/dist/esm/react/index.d.ts +3 -2
  116. package/dist/esm/react/index.d.ts.map +1 -1
  117. package/dist/esm/react/pause-signals-context.d.ts +16 -2
  118. package/dist/esm/react/pause-signals-context.d.ts.map +1 -1
  119. package/dist/esm/react/props-hash.d.ts +20 -0
  120. package/dist/esm/react/props-hash.d.ts.map +1 -0
  121. package/dist/esm/react/use-reactive.d.ts +6 -3
  122. package/dist/esm/react/use-reactive.d.ts.map +1 -1
  123. package/dist/esm/transform/async.d.ts.map +1 -1
  124. package/dist/esm/transform/callback.d.ts.map +1 -1
  125. package/dist/esm/transform/promise.d.ts.map +1 -1
  126. package/dist/esm/transform/use-reactive.d.ts.map +1 -1
  127. package/dist/esm/transform/utils.d.ts +1 -1
  128. package/dist/esm/transform/utils.d.ts.map +1 -1
  129. package/dist/esm/types.d.ts +20 -1
  130. package/dist/esm/types.d.ts.map +1 -1
  131. package/dist/esm/utils.d.ts +17 -1
  132. package/dist/esm/utils.d.ts.map +1 -1
  133. package/package.json +1 -1
  134. package/dist/cjs/development/config-B0MtLBgx.js +0 -21
  135. package/dist/cjs/development/config-B0MtLBgx.js.map +0 -1
  136. package/dist/cjs/development/debug-C46WDxhL.js.map +0 -1
  137. package/dist/cjs/development/snapshot-CbZ6IH3B.js.map +0 -1
  138. package/dist/cjs/production/config-B0MtLBgx.js +0 -21
  139. package/dist/cjs/production/config-B0MtLBgx.js.map +0 -1
  140. package/dist/cjs/production/contexts-CM3QLfV8.js.map +0 -1
  141. package/dist/cjs/production/snapshot-DWwH7_9y.js.map +0 -1
  142. package/dist/esm/development/config-CPQL7hX-.js +0 -22
  143. package/dist/esm/development/config-CPQL7hX-.js.map +0 -1
  144. package/dist/esm/development/debug-BTLbgTPK.js.map +0 -1
  145. package/dist/esm/development/snapshot-CkndI4Hh.js.map +0 -1
  146. package/dist/esm/production/config-CPQL7hX-.js +0 -22
  147. package/dist/esm/production/config-CPQL7hX-.js.map +0 -1
  148. package/dist/esm/production/contexts-FxCndlvf.js.map +0 -1
  149. package/dist/esm/production/snapshot-CdCaJJ0L.js.map +0 -1
@@ -2,9 +2,10 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const jsxRuntime = require("react/jsx-runtime");
4
4
  const React = require("react");
5
- const contexts = require("../contexts-CM3QLfV8.js");
6
- const componentShared = require("../component-shared-D0J85PUi.js");
7
- const snapshot = require("../snapshot-DWwH7_9y.js");
5
+ const contexts = require("../contexts-waczQzHZ.js");
6
+ const config = require("../config-BHvmcsLQ.js");
7
+ const componentShared = require("../component-shared-CcxyLg4p.js");
8
+ const snapshot = require("../snapshot-7264bWj1.js");
8
9
  function _interopNamespaceDefault(e) {
9
10
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
10
11
  if (e) {
@@ -43,7 +44,14 @@ function ContextProvider({ children, contexts: contexts$1 = [], inherit = true }
43
44
  return jsxRuntime.jsx(ScopeContext.Provider, { value: scope, children });
44
45
  }
45
46
  class PauseSignalsManager {
46
- signals = /* @__PURE__ */ new Set();
47
+ /**
48
+ * Readers per registered signal. Scope-cached signals can be shared across providers, so pausing
49
+ * counts readers on the signal instead of pausing it outright.
50
+ */
51
+ signals = /* @__PURE__ */ new Map();
52
+ /** Unclaimed render leases taken under this provider (e.g. a suspended mount). Pruned lazily. */
53
+ leases = /* @__PURE__ */ new Set();
54
+ pruneLeasesAt = 64;
47
55
  _paused;
48
56
  constructor(initialPaused) {
49
57
  this._paused = initialPaused;
@@ -51,22 +59,52 @@ class PauseSignalsManager {
51
59
  get paused() {
52
60
  return this._paused;
53
61
  }
62
+ trackLease(signal) {
63
+ const leases = this.leases;
64
+ leases.add(signal);
65
+ if (leases.size >= this.pruneLeasesAt) {
66
+ for (const leased of leases) {
67
+ if (!leased._isLeased)
68
+ leases.delete(leased);
69
+ }
70
+ this.pruneLeasesAt = Math.max(64, leases.size * 2);
71
+ }
72
+ }
73
+ /** Call before the reader subscribes, so a paused reader never takes the watch. */
54
74
  register(signal) {
55
- this.signals.add(signal);
75
+ this.signals.set(signal, (this.signals.get(signal) ?? 0) + 1);
76
+ this.leases.delete(signal);
77
+ if (this._paused) {
78
+ signal._addPausedReaders(1);
79
+ }
56
80
  }
57
81
  unregister(signal) {
58
- this.signals.delete(signal);
82
+ const count = this.signals.get(signal);
83
+ if (count === void 0)
84
+ return;
85
+ if (count > 1) {
86
+ this.signals.set(signal, count - 1);
87
+ } else {
88
+ this.signals.delete(signal);
89
+ }
90
+ if (this._paused) {
91
+ signal._addPausedReaders(-1, true);
92
+ }
59
93
  }
60
94
  setPaused(value) {
61
95
  if (value === this._paused)
62
96
  return;
63
97
  this._paused = value;
64
- for (const signal of this.signals) {
65
- if (value) {
66
- contexts.unwatchSignal(signal);
98
+ for (const [signal, count] of this.signals) {
99
+ signal._addPausedReaders(value ? count : -count);
100
+ }
101
+ for (const signal of this.leases) {
102
+ if (!signal._isLeased) {
103
+ this.leases.delete(signal);
104
+ } else if (value) {
105
+ signal._pauseWatch();
67
106
  } else {
68
- contexts.watchSignal(signal);
69
- contexts.schedulePull(signal);
107
+ signal._resumeWatch();
70
108
  }
71
109
  }
72
110
  }
@@ -86,6 +124,299 @@ function PauseSignalsProvider({ value, children }) {
86
124
  function usePauseSignalsManager() {
87
125
  return React.useContext(PauseSignalsManagerContext);
88
126
  }
127
+ function addRenderListener(signal, manager) {
128
+ const subscribe = signal.addListenerLazy(!manager?.paused);
129
+ if (manager !== null && signal._isLeased) {
130
+ manager.trackLease(signal);
131
+ }
132
+ return subscribe;
133
+ }
134
+ function usePausableStore(manager, signal, subscribe, getSnapshot) {
135
+ React.useEffect(() => {
136
+ if (manager === null)
137
+ return;
138
+ manager.register(signal);
139
+ return () => manager.unregister(signal);
140
+ }, [manager, signal]);
141
+ return React.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
142
+ }
143
+ const { imul } = Math;
144
+ const { getPrototypeOf, keys: objectKeys } = Object;
145
+ const EMPTY_PROPS_HASH = contexts.hashValue({});
146
+ function hashPropValue(value, seen) {
147
+ if (typeof value === "object" && value !== null) {
148
+ const proto = getPrototypeOf(value);
149
+ if (proto === Object.prototype) {
150
+ const keys = objectKeys(value);
151
+ if (keys.includes("$$typeof") && typeof value.$$typeof === "symbol") {
152
+ return contexts.getObjectHash(value);
153
+ }
154
+ seen.push(value);
155
+ const h = contexts.hashObjectKeys(value, keys, seen);
156
+ seen.pop();
157
+ return h;
158
+ }
159
+ if (proto === Array.prototype) {
160
+ if (seen.includes(value))
161
+ return 0;
162
+ seen.push(value);
163
+ const array = value;
164
+ let h = contexts.ARRAY_SEED;
165
+ for (let i = 0; i < array.length; i++) {
166
+ h = contexts.mixHash(h, hashPropValue(array[i], seen));
167
+ }
168
+ seen.pop();
169
+ return contexts.finalizeHash(h, array.length);
170
+ }
171
+ }
172
+ return contexts.hashValue(value, seen);
173
+ }
174
+ function hashProps(props) {
175
+ const keyMultiplier = 2654435769;
176
+ const seen = [];
177
+ let sum = EMPTY_PROPS_HASH;
178
+ for (const key of objectKeys(props)) {
179
+ sum += imul(contexts.hashValue(key), keyMultiplier) ^ hashPropValue(props[key], seen);
180
+ }
181
+ return sum >>> 0;
182
+ }
183
+ function shallowIdentical(prev, next) {
184
+ let count = 0;
185
+ for (const key in next) {
186
+ if (!Object.is(prev[key], next[key]) || !(key in prev))
187
+ return false;
188
+ count++;
189
+ }
190
+ for (const _key in prev) {
191
+ count--;
192
+ }
193
+ return count === 0;
194
+ }
195
+ class PropsHashCache {
196
+ props;
197
+ hash;
198
+ constructor(props, hash) {
199
+ this.props = props;
200
+ this.hash = hash;
201
+ }
202
+ }
203
+ function usePropsHash(props) {
204
+ const ref = React.useRef(null);
205
+ const cache = ref.current;
206
+ if (cache !== null) {
207
+ if (cache.props === props || shallowIdentical(cache.props, props)) {
208
+ return cache.hash;
209
+ }
210
+ const hash2 = hashProps(props);
211
+ cache.props = props;
212
+ cache.hash = hash2;
213
+ return hash2;
214
+ }
215
+ const hash = hashProps(props);
216
+ ref.current = new PropsHashCache(props, hash);
217
+ return hash;
218
+ }
219
+ function useDeliveryMode(override) {
220
+ const ref = React.useRef(void 0);
221
+ return ref.current ??= override ?? config.getReactDelivery();
222
+ }
223
+ const useCommitEffect = typeof window === "undefined" ? React.useEffect : React.useLayoutEffect;
224
+ const increment = (n) => n + 1;
225
+ class StateDelivery {
226
+ signal;
227
+ committed;
228
+ forceUpdate;
229
+ /** In the pending-delivery queue. */
230
+ queued = false;
231
+ /** A `setState` was delivered and its render has not committed yet. */
232
+ awaitingCommit = false;
233
+ /** Visible: between the subscription layout effect's setup and cleanup. Only visible readers get deliveries. */
234
+ mounted = false;
235
+ /**
236
+ * Set by every render, cleared by its commit. Still set when Suspense hides the reader, it means
237
+ * the reader's own render suspended, so it stays live: a change may no longer need what it
238
+ * suspended on.
239
+ */
240
+ rendering = false;
241
+ /** The subscription layout effect has run before: its next run is a reveal, not a first mount. */
242
+ connected = false;
243
+ /**
244
+ * A commit included the reader without connecting it: it was committed hidden (prerendered in a
245
+ * hidden `<Activity>`, or mounted into a Suspense-hidden tree), so its first connect is a reveal.
246
+ */
247
+ committedHidden = false;
248
+ /** The subscription currently held, the subscribe function it was made with, and its signal and pause manager. */
249
+ subscribedWith = null;
250
+ subscribedSignal = null;
251
+ pauseManager = null;
252
+ unsubscribe = null;
253
+ listener;
254
+ constructor(signal, version, forceUpdate) {
255
+ this.signal = signal;
256
+ this.committed = version;
257
+ this.forceUpdate = forceUpdate;
258
+ this.listener = () => this.check();
259
+ }
260
+ check() {
261
+ if (this.queued || this.awaitingCommit || !this.mounted)
262
+ return;
263
+ if (this.signal.updatedCount !== this.committed) {
264
+ enqueueDelivery(this);
265
+ }
266
+ }
267
+ /**
268
+ * Brings the signal up to date the way a read would, and reports whether it moved past the
269
+ * committed version. A signal nobody watched (the reader was hidden, or its render lease expired
270
+ * before the commit) is not recomputed when its dependencies change, so its `updatedCount` alone
271
+ * can be stale.
272
+ */
273
+ pull() {
274
+ try {
275
+ contexts.checkSignal(this.signal);
276
+ } catch {
277
+ return 2;
278
+ }
279
+ return this.signal.updatedCount !== this.committed ? 1 : 0;
280
+ }
281
+ subscribe(subscribe, manager) {
282
+ if (this.subscribedWith === subscribe && this.pauseManager === manager)
283
+ return;
284
+ this.release();
285
+ manager?.register(this.signal);
286
+ this.subscribedSignal = this.signal;
287
+ this.pauseManager = manager;
288
+ this.unsubscribe = subscribe(this.listener);
289
+ this.subscribedWith = subscribe;
290
+ }
291
+ release() {
292
+ if (this.unsubscribe === null)
293
+ return;
294
+ this.unsubscribe();
295
+ this.pauseManager?.unregister(this.subscribedSignal);
296
+ this.unsubscribe = null;
297
+ this.subscribedWith = null;
298
+ this.subscribedSignal = null;
299
+ this.pauseManager = null;
300
+ }
301
+ }
302
+ let PENDING_DELIVERIES = [];
303
+ let deliveryScheduled = false;
304
+ function enqueueDelivery(delivery) {
305
+ delivery.queued = true;
306
+ PENDING_DELIVERIES.push(delivery);
307
+ if (!deliveryScheduled) {
308
+ deliveryScheduled = true;
309
+ config.scheduleReactDelivery(flushDeliveries);
310
+ }
311
+ }
312
+ function flushDeliveries() {
313
+ deliveryScheduled = false;
314
+ const deliveries = PENDING_DELIVERIES;
315
+ PENDING_DELIVERIES = [];
316
+ config.runBatch(() => {
317
+ for (const delivery of deliveries) {
318
+ delivery.queued = false;
319
+ if (delivery.mounted && delivery.signal.updatedCount !== delivery.committed) {
320
+ delivery.awaitingCommit = true;
321
+ delivery.forceUpdate();
322
+ }
323
+ }
324
+ });
325
+ }
326
+ function useStateDeliveryState(signal) {
327
+ const [, forceUpdate] = React.useReducer(increment, 0);
328
+ const [delivery] = React.useState(() => new StateDelivery(signal, signal.updatedCount, forceUpdate));
329
+ delivery.rendering = true;
330
+ return delivery;
331
+ }
332
+ function useStateDelivery(delivery, signal, subscribe, version, manager) {
333
+ useCommitEffect(() => {
334
+ delivery.signal = signal;
335
+ delivery.committed = version;
336
+ delivery.awaitingCommit = false;
337
+ delivery.rendering = false;
338
+ delivery.check();
339
+ });
340
+ React.useInsertionEffect(() => {
341
+ if (delivery.connected)
342
+ return;
343
+ queueMicrotask(() => {
344
+ if (!delivery.connected)
345
+ delivery.committedHidden = true;
346
+ });
347
+ }, [delivery]);
348
+ useCommitEffect(() => {
349
+ const revealing = delivery.connected || delivery.committedHidden;
350
+ delivery.connected = true;
351
+ delivery.committedHidden = false;
352
+ delivery.mounted = true;
353
+ delivery.subscribe(subscribe, manager);
354
+ const pulled = delivery.pull();
355
+ if (pulled === 2 || pulled === 1 && revealing) {
356
+ delivery.awaitingCommit = true;
357
+ delivery.forceUpdate();
358
+ } else if (pulled === 1) {
359
+ delivery.check();
360
+ }
361
+ return () => {
362
+ if (delivery.rendering) {
363
+ delivery.awaitingCommit = false;
364
+ } else {
365
+ delivery.mounted = false;
366
+ }
367
+ };
368
+ }, [subscribe, manager]);
369
+ React.useEffect(() => () => delivery.release(), [delivery]);
370
+ }
371
+ const elementPropsByInnerProps = /* @__PURE__ */ new WeakMap();
372
+ const suspendedAttemptsByElement = /* @__PURE__ */ new WeakMap();
373
+ function createComponentElement(Inner, props) {
374
+ const element = React.createElement(Inner, props);
375
+ elementPropsByInnerProps.set(element.props, props);
376
+ return element;
377
+ }
378
+ function isReactSuspenseException(error) {
379
+ if (!(error instanceof Error))
380
+ return false;
381
+ const { message } = error;
382
+ return message.startsWith("Suspense Exception") || /^Minified React error #(460|542)\b/.test(message);
383
+ }
384
+ function readComponentSignal(signal, props) {
385
+ const element = elementPropsByInnerProps.get(props);
386
+ let attempts = element === void 0 ? void 0 : suspendedAttemptsByElement.get(element);
387
+ try {
388
+ contexts.runSignal(signal);
389
+ const value = signal.value;
390
+ attempts?.delete(signal);
391
+ return value;
392
+ } catch (error) {
393
+ let pinned = true;
394
+ if (error !== null && typeof error === "object" && contexts.isThennable(error)) {
395
+ contexts.holdLeaseUntilSettled(signal, error);
396
+ } else if (isReactSuspenseException(error)) {
397
+ contexts.holdSuspendedLease(signal);
398
+ } else {
399
+ pinned = false;
400
+ }
401
+ if (pinned && element !== void 0 && signal._isLeased) {
402
+ if (attempts === void 0) {
403
+ attempts = /* @__PURE__ */ new Set();
404
+ suspendedAttemptsByElement.set(element, attempts);
405
+ }
406
+ attempts.add(signal);
407
+ }
408
+ throw error;
409
+ } finally {
410
+ if (attempts !== void 0) {
411
+ for (const attempt of attempts) {
412
+ if (attempt !== signal) {
413
+ contexts.releaseAbandonedAttempt(attempt);
414
+ attempts.delete(attempt);
415
+ }
416
+ }
417
+ }
418
+ }
419
+ }
89
420
  const thenableOutcome = /* @__PURE__ */ new WeakMap();
90
421
  function adoptYieldedThenable(thenable) {
91
422
  const expanded = thenable;
@@ -152,10 +483,12 @@ function runSyncReplayAsyncComponent(fn, props, ownerSignal) {
152
483
  contexts.setCurrentConsumer(prevConsumer);
153
484
  }
154
485
  }
155
- function createAsyncComponentWrapper(fn) {
486
+ function createAsyncComponentWrapper(fn, options) {
487
+ const deliveryOverride = options?.delivery;
156
488
  const Inner = (props) => {
157
489
  const scope = useScope();
158
490
  const manager = usePauseSignalsManager();
491
+ const delivery = useDeliveryMode(deliveryOverride);
159
492
  const fnSignalRef = React.useRef(void 0);
160
493
  const propsRef = React.useRef(props);
161
494
  propsRef.current = props;
@@ -171,18 +504,20 @@ function createAsyncComponentWrapper(fn) {
171
504
  owned._isLazy = true;
172
505
  fnSignalRef.current = sig = owned;
173
506
  }
174
- const watch = !manager?.paused;
175
- manager?.register(sig);
176
- React.useEffect(() => {
177
- return () => manager?.unregister(sig);
178
- }, [manager, sig]);
179
- React.useSyncExternalStore(sig.addListenerLazy(watch), () => sig.updatedCount, () => sig.updatedCount);
180
- contexts.runSignal(sig);
181
- return sig.value;
507
+ const subscribe = addRenderListener(sig, manager);
508
+ const stateDelivery = delivery === "state" ? useStateDeliveryState(sig) : null;
509
+ const value = readComponentSignal(sig, props);
510
+ if (stateDelivery !== null) {
511
+ useStateDelivery(stateDelivery, sig, subscribe, sig.updatedCount, manager);
512
+ } else {
513
+ const getSnapshot = () => sig.updatedCount;
514
+ usePausableStore(manager, sig, subscribe, getSnapshot);
515
+ }
516
+ return value;
182
517
  };
183
518
  const Outer = (props) => {
184
- const hash = contexts.hashValue(props);
185
- return React.useMemo(() => React.createElement(Inner, props), [hash]);
519
+ const hash = usePropsHash(props);
520
+ return React.useMemo(() => createComponentElement(Inner, props), [hash]);
186
521
  };
187
522
  Object.defineProperty(Outer, SIGNALIUM_ASYNC_COMPONENT, { value: true, enumerable: false });
188
523
  return Outer;
@@ -198,7 +533,7 @@ function ensureSsrScope() {
198
533
  contexts.setRequestScopeGetter(() => getScope());
199
534
  }
200
535
  }
201
- function component(fn) {
536
+ function component(fn, options) {
202
537
  ensureSsrScope();
203
538
  if (componentShared.isAsyncFunctionWithoutTransform(fn)) {
204
539
  throw new Error("signalium: `component(async (props) => { await ... })` requires the Signalium Babel preset (async transform).");
@@ -207,12 +542,14 @@ function component(fn) {
207
542
  if (typeof window === "undefined") {
208
543
  return componentShared.createServerAsyncComponentWrapper(fn);
209
544
  }
210
- return createAsyncComponentWrapper(fn);
545
+ return createAsyncComponentWrapper(fn, options);
211
546
  }
212
547
  const syncFn = fn;
548
+ const deliveryOverride = options?.delivery;
213
549
  const Component = (props) => {
214
550
  const scope = useScope();
215
551
  const manager = usePauseSignalsManager();
552
+ const delivery = useDeliveryMode(deliveryOverride);
216
553
  const fnSignalRef = React.useRef(void 0);
217
554
  const propsRef = React.useRef(props);
218
555
  propsRef.current = props;
@@ -227,18 +564,20 @@ function component(fn) {
227
564
  created._isLazy = true;
228
565
  fnSignalRef.current = signal = created;
229
566
  }
230
- const watch = !manager?.paused;
231
- manager?.register(signal);
232
- React.useEffect(() => {
233
- return () => manager?.unregister(signal);
234
- }, [manager, signal]);
235
- React.useSyncExternalStore(signal.addListenerLazy(watch), () => signal.updatedCount, () => signal.updatedCount);
236
- contexts.runSignal(signal);
237
- return signal.value;
567
+ const subscribe = addRenderListener(signal, manager);
568
+ const stateDelivery = delivery === "state" ? useStateDeliveryState(signal) : null;
569
+ const value = readComponentSignal(signal, props);
570
+ if (stateDelivery !== null) {
571
+ useStateDelivery(stateDelivery, signal, subscribe, signal.updatedCount, manager);
572
+ } else {
573
+ const getSnapshot = () => signal.updatedCount;
574
+ usePausableStore(manager, signal, subscribe, getSnapshot);
575
+ }
576
+ return value;
238
577
  };
239
578
  return (props) => {
240
- const hash = contexts.hashValue(props);
241
- return React.useMemo(() => jsxRuntime.jsx(Component, { ...props }), [hash]);
579
+ const hash = usePropsHash(props);
580
+ return React.useMemo(() => createComponentElement(Component, props), [hash]);
242
581
  };
243
582
  }
244
583
  function useSignal(value, opts) {
@@ -248,24 +587,28 @@ function useSignal(value, opts) {
248
587
  }
249
588
  return ref.current;
250
589
  }
251
- function useSignalWithSuspension(signal) {
590
+ function useSignalWithSuspension(signal, options) {
252
591
  const manager = usePauseSignalsManager();
253
- const watch = !manager?.paused;
254
- manager?.register(signal);
255
- React.useEffect(() => {
256
- return () => manager?.unregister(signal);
257
- }, [manager, signal]);
258
- return React.useSyncExternalStore(signal.addListenerLazy(watch), () => signal.value, () => signal.value);
592
+ const delivery = useDeliveryMode(options?.delivery);
593
+ const subscribe = addRenderListener(signal, manager);
594
+ if (delivery === "state") {
595
+ const stateDelivery = useStateDeliveryState(signal);
596
+ const value = signal.value;
597
+ useStateDelivery(stateDelivery, signal, subscribe, signal.updatedCount, manager);
598
+ return value;
599
+ }
600
+ const getSnapshot = () => signal.value;
601
+ return usePausableStore(manager, signal, subscribe, getSnapshot);
259
602
  }
260
- function useReactiveShallow(fn) {
603
+ function useReactiveShallow(fn, options) {
261
604
  const [, def] = snapshot.getReactiveFnAndDefinition(fn);
262
605
  const scope = useScope() ?? contexts.getGlobalScope();
263
606
  const signal = scope.get(def, []);
264
- return useSignalWithSuspension(signal);
607
+ return useSignalWithSuspension(signal, options);
265
608
  }
266
- function useReactive(fn) {
609
+ function useReactive(fn, options) {
267
610
  const manager = usePauseSignalsManager();
268
- const watch = !manager?.paused;
611
+ const delivery = useDeliveryMode(options?.delivery);
269
612
  const scope = useScope() ?? contexts.getGlobalScope();
270
613
  const innerSignalRef = React.useRef(void 0);
271
614
  const cloneSignalRef = React.useRef(void 0);
@@ -282,14 +625,18 @@ function useReactive(fn) {
282
625
  });
283
626
  }
284
627
  const cloneSignal = cloneSignalRef.current;
285
- manager?.register(cloneSignal);
286
- React.useEffect(() => {
287
- return () => manager?.unregister(cloneSignal);
288
- }, [manager, cloneSignal]);
289
- return React.useSyncExternalStore(cloneSignal.addListenerLazy(watch), () => cloneSignal.value, () => cloneSignal.value);
628
+ const subscribe = addRenderListener(cloneSignal, manager);
629
+ if (delivery === "state") {
630
+ const stateDelivery = useStateDeliveryState(cloneSignal);
631
+ const value = cloneSignal.value;
632
+ useStateDelivery(stateDelivery, cloneSignal, subscribe, cloneSignal.updatedCount, manager);
633
+ return value;
634
+ }
635
+ const getSnapshot = () => cloneSignal.value;
636
+ return usePausableStore(manager, cloneSignal, subscribe, getSnapshot);
290
637
  }
291
- function useReactiveDeep(fn) {
292
- return useReactive(fn);
638
+ function useReactiveDeep(fn, options) {
639
+ return useReactive(fn, options);
293
640
  }
294
641
  exports.isAsyncFunctionWithoutTransform = componentShared.isAsyncFunctionWithoutTransform;
295
642
  exports.ContextProvider = ContextProvider;