signalium 3.0.3 → 4.0.1

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 (143) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/cjs/development/{component-shared-DzjqSRY-.js → component-shared-CU7qN_Ks.js} +2 -2
  3. package/dist/cjs/development/{component-shared-DzjqSRY-.js.map → component-shared-CU7qN_Ks.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-CDy1L3Id.js → debug-BS6RdX4D.js} +252 -32
  8. package/dist/cjs/development/debug-BS6RdX4D.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-D8Ydfj9E.js → snapshot-BLa9mv8h.js} +4 -2
  19. package/dist/cjs/development/snapshot-BLa9mv8h.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-BuDl2V-v.js → component-shared-BrbkxFeg.js} +2 -2
  25. package/dist/cjs/production/{component-shared-BuDl2V-v.js.map → component-shared-BrbkxFeg.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-CMvA-Ae9.js → contexts-Bkwv59kH.js} +252 -32
  30. package/dist/cjs/production/contexts-Bkwv59kH.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-I8W4cb8t.js → snapshot-gv-aZDmB.js} +4 -2
  41. package/dist/cjs/production/snapshot-gv-aZDmB.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-D_i2IMKF.js → component-shared-C6HefNX-.js} +2 -2
  51. package/dist/esm/development/{component-shared-D_i2IMKF.js.map → component-shared-C6HefNX-.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-DCgp_XPm.js → debug-D7mtRqCt.js} +280 -60
  56. package/dist/esm/development/debug-D7mtRqCt.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-D19Mgcgz.js → snapshot-CMsut-I_.js} +7 -5
  65. package/dist/esm/development/snapshot-CMsut-I_.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 +1 -1
  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/utils/hash.d.ts +7 -0
  80. package/dist/esm/internals/utils/hash.d.ts.map +1 -1
  81. package/dist/esm/internals/utils/snapshot.d.ts +2 -0
  82. package/dist/esm/internals/utils/snapshot.d.ts.map +1 -1
  83. package/dist/esm/production/{component-shared-XZnx53QZ.js → component-shared-DzYWFipC.js} +2 -2
  84. package/dist/esm/production/{component-shared-XZnx53QZ.js.map → component-shared-DzYWFipC.js.map} +1 -1
  85. package/dist/esm/production/config-BhtwQYAN.js +53 -0
  86. package/dist/esm/production/config-BhtwQYAN.js.map +1 -0
  87. package/dist/esm/production/config.js +2 -2
  88. package/dist/esm/production/{contexts-Cpo7QZDe.js → contexts-D2PPNujL.js} +283 -63
  89. package/dist/esm/production/contexts-D2PPNujL.js.map +1 -0
  90. package/dist/esm/production/debug.js +3 -1
  91. package/dist/esm/production/index.js +19 -18
  92. package/dist/esm/production/react/index.js +400 -53
  93. package/dist/esm/production/react/index.js.map +1 -1
  94. package/dist/esm/production/react/index.server.js +2 -2
  95. package/dist/esm/production/react/index.server.js.map +1 -1
  96. package/dist/esm/production/react/server.js +1 -1
  97. package/dist/esm/production/{snapshot-CZTu3qTo.js → snapshot-Bx73RooF.js} +7 -5
  98. package/dist/esm/production/snapshot-Bx73RooF.js.map +1 -0
  99. package/dist/esm/production/transform/index.js +5 -2
  100. package/dist/esm/production/transform/index.js.map +1 -1
  101. package/dist/esm/production/utils.js +39 -7
  102. package/dist/esm/production/utils.js.map +1 -1
  103. package/dist/esm/react/async-component.d.ts +10 -2
  104. package/dist/esm/react/async-component.d.ts.map +1 -1
  105. package/dist/esm/react/component-server.d.ts +6 -2
  106. package/dist/esm/react/component-server.d.ts.map +1 -1
  107. package/dist/esm/react/component.d.ts +5 -2
  108. package/dist/esm/react/component.d.ts.map +1 -1
  109. package/dist/esm/react/delivery.d.ts +107 -0
  110. package/dist/esm/react/delivery.d.ts.map +1 -0
  111. package/dist/esm/react/index.d.ts +3 -2
  112. package/dist/esm/react/index.d.ts.map +1 -1
  113. package/dist/esm/react/pause-signals-context.d.ts +16 -2
  114. package/dist/esm/react/pause-signals-context.d.ts.map +1 -1
  115. package/dist/esm/react/props-hash.d.ts +20 -0
  116. package/dist/esm/react/props-hash.d.ts.map +1 -0
  117. package/dist/esm/react/use-reactive.d.ts +6 -3
  118. package/dist/esm/react/use-reactive.d.ts.map +1 -1
  119. package/dist/esm/transform/async.d.ts.map +1 -1
  120. package/dist/esm/transform/callback.d.ts.map +1 -1
  121. package/dist/esm/transform/promise.d.ts.map +1 -1
  122. package/dist/esm/transform/use-reactive.d.ts.map +1 -1
  123. package/dist/esm/transform/utils.d.ts +1 -1
  124. package/dist/esm/transform/utils.d.ts.map +1 -1
  125. package/dist/esm/utils.d.ts +17 -1
  126. package/dist/esm/utils.d.ts.map +1 -1
  127. package/package.json +1 -1
  128. package/dist/cjs/development/config-B0MtLBgx.js +0 -21
  129. package/dist/cjs/development/config-B0MtLBgx.js.map +0 -1
  130. package/dist/cjs/development/debug-CDy1L3Id.js.map +0 -1
  131. package/dist/cjs/development/snapshot-D8Ydfj9E.js.map +0 -1
  132. package/dist/cjs/production/config-B0MtLBgx.js +0 -21
  133. package/dist/cjs/production/config-B0MtLBgx.js.map +0 -1
  134. package/dist/cjs/production/contexts-CMvA-Ae9.js.map +0 -1
  135. package/dist/cjs/production/snapshot-I8W4cb8t.js.map +0 -1
  136. package/dist/esm/development/config-CPQL7hX-.js +0 -22
  137. package/dist/esm/development/config-CPQL7hX-.js.map +0 -1
  138. package/dist/esm/development/debug-DCgp_XPm.js.map +0 -1
  139. package/dist/esm/development/snapshot-D19Mgcgz.js.map +0 -1
  140. package/dist/esm/production/config-CPQL7hX-.js +0 -22
  141. package/dist/esm/production/config-CPQL7hX-.js.map +0 -1
  142. package/dist/esm/production/contexts-Cpo7QZDe.js.map +0 -1
  143. package/dist/esm/production/snapshot-CZTu3qTo.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 debug = require("../debug-CDy1L3Id.js");
6
- const componentShared = require("../component-shared-DzjqSRY-.js");
7
- const snapshot = require("../snapshot-D8Ydfj9E.js");
5
+ const debug = require("../debug-BS6RdX4D.js");
6
+ const config = require("../config-D6wyrhrW.js");
7
+ const componentShared = require("../component-shared-CU7qN_Ks.js");
8
+ const snapshot = require("../snapshot-BLa9mv8h.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 = [], 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
- debug.unwatchSignal(signal, { isPausing: true });
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
- debug.watchSignal(signal);
69
- debug.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 = debug.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 debug.getObjectHash(value);
153
+ }
154
+ seen.push(value);
155
+ const h = debug.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 = debug.ARRAY_SEED;
165
+ for (let i = 0; i < array.length; i++) {
166
+ h = debug.mixHash(h, hashPropValue(array[i], seen));
167
+ }
168
+ seen.pop();
169
+ return debug.finalizeHash(h, array.length);
170
+ }
171
+ }
172
+ return debug.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(debug.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
+ debug.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
+ debug.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" && debug.isThennable(error)) {
395
+ debug.holdLeaseUntilSettled(signal, error);
396
+ } else if (isReactSuspenseException(error)) {
397
+ debug.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
+ debug.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
  debug.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
- debug.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 = debug.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
  debug.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
- debug.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 = debug.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,30 +587,34 @@ 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
  if (debug.getCurrentConsumer()) {
262
605
  throw new Error("signalium: `useReactiveShallow` cannot be called inside a reactive function. Call your reactive function directly instead — it already participates in the signal graph.");
263
606
  }
264
607
  const [, def] = snapshot.getReactiveFnAndDefinition(fn);
265
608
  const scope = useScope() ?? debug.getGlobalScope();
266
609
  const signal = scope.get(def, []);
267
- return useSignalWithSuspension(signal);
610
+ return useSignalWithSuspension(signal, options);
268
611
  }
269
- function useReactive(fn) {
612
+ function useReactive(fn, options) {
270
613
  if (debug.getCurrentConsumer()) {
271
614
  throw new Error("signalium: `useReactive` cannot be called inside a reactive function. Call your reactive function directly instead — it already participates in the signal graph.");
272
615
  }
273
616
  const manager = usePauseSignalsManager();
274
- const watch = !manager?.paused;
617
+ const delivery = useDeliveryMode(options?.delivery);
275
618
  const scope = useScope() ?? debug.getGlobalScope();
276
619
  const innerSignalRef = React.useRef(void 0);
277
620
  const cloneSignalRef = React.useRef(void 0);
@@ -288,17 +631,21 @@ function useReactive(fn) {
288
631
  });
289
632
  }
290
633
  const cloneSignal = cloneSignalRef.current;
291
- manager?.register(cloneSignal);
292
- React.useEffect(() => {
293
- return () => manager?.unregister(cloneSignal);
294
- }, [manager, cloneSignal]);
295
- return React.useSyncExternalStore(cloneSignal.addListenerLazy(watch), () => cloneSignal.value, () => cloneSignal.value);
634
+ const subscribe = addRenderListener(cloneSignal, manager);
635
+ if (delivery === "state") {
636
+ const stateDelivery = useStateDeliveryState(cloneSignal);
637
+ const value = cloneSignal.value;
638
+ useStateDelivery(stateDelivery, cloneSignal, subscribe, cloneSignal.updatedCount, manager);
639
+ return value;
640
+ }
641
+ const getSnapshot = () => cloneSignal.value;
642
+ return usePausableStore(manager, cloneSignal, subscribe, getSnapshot);
296
643
  }
297
- function useReactiveDeep(fn) {
644
+ function useReactiveDeep(fn, options) {
298
645
  {
299
646
  warnUseReactiveDeepOnce();
300
647
  }
301
- return useReactive(fn);
648
+ return useReactive(fn, options);
302
649
  }
303
650
  let _useReactiveDeepWarned = false;
304
651
  function warnUseReactiveDeepOnce() {