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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # signalium
2
2
 
3
+ ## 4.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - c4449de: Release the React render-path changes as a major version. They change behavior that existing apps can observe:
8
+
9
+ - `component()` props are treated as immutable, the same contract as `React.memo`. When every prop is identical (by `Object.is`) to the previous render, the previous props hash is reused, and React element props are hashed by identity. A plain object, array, `Map`, `Set` or `Date` mutated in place and passed again no longer re-renders a `component()` child; pass a new value instead. Freshly created element children (`children={<X />}`) re-render the child on every parent render.
10
+ - `component()` mounts in one render instead of two.
11
+ - Watches taken during render are leases. A render React discards (an interrupted transition, a mount that suspends, a StrictMode double render) releases its relays after `renderLeaseTtl` (default 10 s) instead of keeping them active for the rest of the session. `useReactive` and `useReactiveShallow` readers suspended by React 19's `use()` for longer than the TTL lose their lease, so their relays may deactivate and restart when React retries the render.
12
+
13
+ ### Minor Changes
14
+
15
+ - d96ce16: `component()` no longer walks React element trees when hashing props. A React element is a plain object, so the structural props hash recursed through `props` and every descendant element — for a component taking `children`, that walk dominated each render, and reached into whatever the child elements' own props pointed at. Element-valued props are now hashed by identity: nothing reads through an element, and `hashValue` already hashes by identity any object whose prototype it doesn't recognise — elements only escaped that because they happen to be plain objects.
16
+
17
+ Every other prop keeps its structural hash, so a new-but-equivalent object or array still reuses the previous render, and a value whose class has a `registerCustomHash` function (an `Array` subclass included) is still hashed by it. Applies to async `component()` too, which hashed props the same way.
18
+
19
+ **Behaviour change:** a freshly-created children tree that is structurally identical to the previous one no longer reuses the memoized render, so such a component re-renders where it used to be skipped. Nothing can go stale as a result — React skips a subtree whose element is referentially identical, so a re-render was never what made an update visible. If a component relied on being skipped for a fresh-but-equal `children` (or other element) prop, hoist the element or memoize it so the same element is passed again.
20
+
21
+ - c4449de: `component()` no longer hashes its props when every prop is identical (by `Object.is`) to the previous render's — the common case of a parent re-rendering without changing anything — and reuses the previous hash instead. A shallow identity check costs a fraction of the structural hash (6–30x less for typical props in a micro-benchmark).
22
+
23
+ **Behaviour change:** props are now treated as immutable, the same contract as `React.memo` and the React Compiler: a plain object, array, `Map`, `Set` or `Date` prop mutated in place and passed again under the same identity no longer re-renders the component (the structural hash used to notice such a mutation). Pass a new object, or read changing data from a signal inside the component. Applies to async `component()` too.
24
+
25
+ - 1b46c55: Export `snapshotArray`, `snapshotPlainObject` and the `SnapshotFn` type from `signalium/utils`.
26
+ - c4449de: New opt-in `'state'` delivery mode for React readers. By default `component()`, `useReactive` and `useReactiveShallow` reach React through `useSyncExternalStore`, which always renders at SyncLane and throws away any in-progress transition render; with live data ticking underneath, a transition only finishes at React's ~5 s expiry. In `'state'` mode a reader reads its signal directly during render, subscribes on commit (claiming its render lease), and delivers changes with a default-priority `setState`, coalesced per reader and flushed for all readers in one `runBatch` on a microtask (`scheduleReactDelivery`). Default-priority updates wait for an in-progress transition instead of restarting it. A change between render and subscribe, or during a delivered render, is caught by a version check on commit, which first re-validates the signal so a change it missed while unwatched (an expired render lease) is not lost. The subscription lives as long as a `useSyncExternalStore` one would: a Suspense hide (react-freeze) keeps it and its relays, `<Activity mode="hidden">` and unmount release it. A hidden reader is not rendered, unless its own render is what suspended, so it can still recover when a change no longer needs the pending data; when it is revealed (including the first reveal of content prerendered inside a hidden `<Activity>`) it re-validates and, if anything changed since it rendered, renders before that commit paints. A first mount delivers a change between its render and its commit at default priority instead.
27
+
28
+ Enable globally with `setConfig({ reactDelivery: 'state' })` (the default stays `'sync'`), or per reader with `component(fn, { delivery })`, `useReactive(fn, { delivery })` and `useReactiveShallow(fn, { delivery })`. The Babel preset memoizes the thunk of a `useReactive` / `useReactiveShallow` / `useReactiveDeep` call that passes options too (it used to skip any call with a second argument, so such a call created a new signal on every render); the options argument is left as written. The mode is fixed when a reader mounts. Trade-off: updates are consistent per delivery batch, but a reader that mounts while a delivery is pending can show a newer value than its already-mounted siblings for one commit; readers whose values must stay correlated keep `'sync'`.
29
+
30
+ - 98ef30c: React render-time watches are now leases. `component()`, `useReactive` and `useReactiveShallow` watch their signal while rendering so relays read during the render activate, but React can discard a render (an interrupted transition, a mount that suspends, a StrictMode double render) without ever subscribing or running an effect, and the watch — with every relay under it — stayed active for the rest of the session. The render-time watch is now a provisional lease: the store subscription React makes on commit claims it (keeping the relays warm, with no restart), and a lease nobody claims is released after `renderLeaseTtl` (default 10 s, configurable via `setConfig`; a lease lives between one and two TTLs). A render that reads a still-leased signal again extends the lease. A `component()` render that suspends keeps its lease until the thrown thenable settles; one suspended by React 19's `use()` (whose thenable React keeps to itself) keeps it until React retries or commits it, or for 30 TTLs if React abandons it. `useReactive` / `useReactiveShallow` cannot see a `use()` that suspends the component after they return, so their leases follow the TTL. Committed components behave exactly as before.
31
+
32
+ `PauseSignalsProvider` pausing is counted per reader. A signal stays watched while any of its subscribers is unpaused, so a `useReactiveShallow` signal shared with readers outside a paused provider keeps updating for them, and an unpaused reader that suspends on such a signal still starts its relays. Readers register on commit, before they subscribe, so un-pausing no longer re-watches a signal from a discarded render; a provider also tracks render leases taken under it, so pausing reaches a mount that suspended before committing.
33
+
34
+ New: `retain(fn, { ttl })` keeps `fn`'s relays/queries watched for `ttl` ms (or until the returned release function is called) — an app-level lease for prefetching or keeping a hidden surface warm. The Babel preset transforms async callbacks passed to it. `ttl: Infinity` retains until released, and a `ttl` above 2^31 - 1 ms (the longest timer delay) is clamped to it. `setConfig({ renderLeaseTtl })` clamps values above that limit and ignores non-positive ones, warning in development. `signalium/debug` exports `getRenderLeaseCount()` and `releaseRenderLeases()`, which both include leases pinned for a suspended render.
35
+
36
+ ## 3.0.3
37
+
38
+ ### Patch Changes
39
+
40
+ - bad3258: `RelayHooks.deactivate` now receives a `DeactivateOptions` object with an `isPausing` flag, distinguishing a temporary pause (e.g. `PauseSignalsProvider`) from a genuine cleanup. Relays can use it to skip destructive work like garbage collection while pausing. `DeactivateOptions` is exported.
41
+
3
42
  ## 3.0.2
4
43
 
5
44
  ### Patch Changes
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const debug = require("./debug-C46WDxhL.js");
2
+ const debug = require("./debug-CbAX7VeM.js");
3
3
  function isGeneratorFunction(fn) {
4
4
  return typeof fn === "function" && fn.constructor?.name === "GeneratorFunction";
5
5
  }
@@ -30,4 +30,4 @@ exports.createServerAsyncComponentWrapper = createServerAsyncComponentWrapper;
30
30
  exports.createServerSyncComponentWrapper = createServerSyncComponentWrapper;
31
31
  exports.isAsyncFunctionWithoutTransform = isAsyncFunctionWithoutTransform;
32
32
  exports.isGeneratorFunction = isGeneratorFunction;
33
- //# sourceMappingURL=component-shared-Cug2Y5YZ.js.map
33
+ //# sourceMappingURL=component-shared-BjwFh5Jq.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"component-shared-Cug2Y5YZ.js","sources":["../../../.tsc-out/react/component-shared.js"],"sourcesContent":["import { getCurrentConsumer, setCurrentConsumer } from '../internals/consumer.js';\nimport { getCurrentScope } from '../internals/contexts.js';\nimport { createReactiveSignal } from '../internals/reactive.js';\nimport { generatorResultToPromiseWithConsumer } from '../internals/generators.js';\nexport function isGeneratorFunction(fn) {\n return (typeof fn === 'function' && fn.constructor?.name === 'GeneratorFunction');\n}\nexport function isAsyncFunctionWithoutTransform(fn) {\n return typeof fn === 'function' && fn.constructor?.name === 'AsyncFunction';\n}\n/**\n * Wrap a Babel-transformed async generator in a real `async function` that drives it\n * with {@link generatorResultToPromiseWithConsumer}. Used on the server (RSC + SSR)\n * where hooks-based Suspense replay is not appropriate.\n */\nexport function createServerAsyncComponentWrapper(fn) {\n return async function (props) {\n const scope = getCurrentScope();\n const owned = createReactiveSignal({ compute: () => null, equals: () => true, isRelay: false, tracer: undefined }, [], undefined, scope);\n return generatorResultToPromiseWithConsumer(fn(props), owned);\n };\n}\n/**\n * Wrap a sync render function with consumer/scope tracking. Used by the server\n * `component()` for non-generator definitions.\n */\nexport function createServerSyncComponentWrapper(fn) {\n return function (props) {\n const scope = getCurrentScope();\n const owned = createReactiveSignal({ compute: () => null, equals: () => true, isRelay: false, tracer: undefined }, [], undefined, scope);\n const prevConsumer = getCurrentConsumer();\n try {\n setCurrentConsumer(owned);\n return fn(props);\n }\n finally {\n setCurrentConsumer(prevConsumer);\n }\n };\n}\n"],"names":["getCurrentScope","createReactiveSignal","generatorResultToPromiseWithConsumer","getCurrentConsumer","setCurrentConsumer"],"mappings":";;AAIO,SAAS,oBAAoB,IAAI;AACpC,SAAQ,OAAO,OAAO,cAAc,GAAG,aAAa,SAAS;AACjE;AACO,SAAS,gCAAgC,IAAI;AAChD,SAAO,OAAO,OAAO,cAAc,GAAG,aAAa,SAAS;AAChE;AAMO,SAAS,kCAAkC,IAAI;AAClD,SAAO,eAAgB,OAAO;AAC1B,UAAM,QAAQA,MAAAA,gBAAe;AAC7B,UAAM,QAAQC,MAAAA,qBAAqB,EAAE,SAAS,MAAM,MAAM,QAAQ,MAAM,MAAM,SAAS,OAAO,QAAQ,OAAS,GAAI,CAAA,GAAI,QAAW,KAAK;AACvI,WAAOC,2CAAqC,GAAG,KAAK,GAAG,KAAK;AAAA,EAChE;AACJ;AAKO,SAAS,iCAAiC,IAAI;AACjD,SAAO,SAAU,OAAO;AACpB,UAAM,QAAQF,MAAAA,gBAAe;AAC7B,UAAM,QAAQC,MAAAA,qBAAqB,EAAE,SAAS,MAAM,MAAM,QAAQ,MAAM,MAAM,SAAS,OAAO,QAAQ,OAAS,GAAI,CAAA,GAAI,QAAW,KAAK;AACvI,UAAM,eAAeE,MAAAA,mBAAkB;AACvC,QAAI;AACAC,YAAAA,mBAAmB,KAAK;AACxB,aAAO,GAAG,KAAK;AAAA,IACnB,UACR;AACYA,YAAAA,mBAAmB,YAAY;AAAA,IACnC;AAAA,EACJ;AACJ;;;;;"}
1
+ {"version":3,"file":"component-shared-BjwFh5Jq.js","sources":["../../../.tsc-out/react/component-shared.js"],"sourcesContent":["import { getCurrentConsumer, setCurrentConsumer } from '../internals/consumer.js';\nimport { getCurrentScope } from '../internals/contexts.js';\nimport { createReactiveSignal } from '../internals/reactive.js';\nimport { generatorResultToPromiseWithConsumer } from '../internals/generators.js';\nexport function isGeneratorFunction(fn) {\n return (typeof fn === 'function' && fn.constructor?.name === 'GeneratorFunction');\n}\nexport function isAsyncFunctionWithoutTransform(fn) {\n return typeof fn === 'function' && fn.constructor?.name === 'AsyncFunction';\n}\n/**\n * Wrap a Babel-transformed async generator in a real `async function` that drives it\n * with {@link generatorResultToPromiseWithConsumer}. Used on the server (RSC + SSR)\n * where hooks-based Suspense replay is not appropriate.\n */\nexport function createServerAsyncComponentWrapper(fn) {\n return async function (props) {\n const scope = getCurrentScope();\n const owned = createReactiveSignal({ compute: () => null, equals: () => true, isRelay: false, tracer: undefined }, [], undefined, scope);\n return generatorResultToPromiseWithConsumer(fn(props), owned);\n };\n}\n/**\n * Wrap a sync render function with consumer/scope tracking. Used by the server\n * `component()` for non-generator definitions.\n */\nexport function createServerSyncComponentWrapper(fn) {\n return function (props) {\n const scope = getCurrentScope();\n const owned = createReactiveSignal({ compute: () => null, equals: () => true, isRelay: false, tracer: undefined }, [], undefined, scope);\n const prevConsumer = getCurrentConsumer();\n try {\n setCurrentConsumer(owned);\n return fn(props);\n }\n finally {\n setCurrentConsumer(prevConsumer);\n }\n };\n}\n"],"names":["getCurrentScope","createReactiveSignal","generatorResultToPromiseWithConsumer","getCurrentConsumer","setCurrentConsumer"],"mappings":";;AAIO,SAAS,oBAAoB,IAAI;AACpC,SAAQ,OAAO,OAAO,cAAc,GAAG,aAAa,SAAS;AACjE;AACO,SAAS,gCAAgC,IAAI;AAChD,SAAO,OAAO,OAAO,cAAc,GAAG,aAAa,SAAS;AAChE;AAMO,SAAS,kCAAkC,IAAI;AAClD,SAAO,eAAgB,OAAO;AAC1B,UAAM,QAAQA,MAAAA,gBAAe;AAC7B,UAAM,QAAQC,MAAAA,qBAAqB,EAAE,SAAS,MAAM,MAAM,QAAQ,MAAM,MAAM,SAAS,OAAO,QAAQ,OAAS,GAAI,CAAA,GAAI,QAAW,KAAK;AACvI,WAAOC,2CAAqC,GAAG,KAAK,GAAG,KAAK;AAAA,EAChE;AACJ;AAKO,SAAS,iCAAiC,IAAI;AACjD,SAAO,SAAU,OAAO;AACpB,UAAM,QAAQF,MAAAA,gBAAe;AAC7B,UAAM,QAAQC,MAAAA,qBAAqB,EAAE,SAAS,MAAM,MAAM,QAAQ,MAAM,MAAM,SAAS,OAAO,QAAQ,OAAS,GAAI,CAAA,GAAI,QAAW,KAAK;AACvI,UAAM,eAAeE,MAAAA,mBAAkB;AACvC,QAAI;AACAC,YAAAA,mBAAmB,KAAK;AACxB,aAAO,GAAG,KAAK;AAAA,IACnB,UACR;AACYA,YAAAA,mBAAmB,YAAY;AAAA,IACnC;AAAA,EACJ;AACJ;;;;;"}
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ let _scheduleFlush = (flushWatchers) => {
3
+ setTimeout(() => {
4
+ flushWatchers();
5
+ }, 0);
6
+ };
7
+ let _runBatch = (fn) => fn();
8
+ const DEFAULT_RENDER_LEASE_TTL = 1e4;
9
+ let _renderLeaseTtl = DEFAULT_RENDER_LEASE_TTL;
10
+ const MAX_TIMEOUT = 2 ** 31 - 1;
11
+ function validateRenderLeaseTtl(ttl) {
12
+ if (typeof ttl !== "number" || Number.isNaN(ttl) || ttl <= 0) {
13
+ {
14
+ console.warn(`signalium: setConfig({ renderLeaseTtl: ${String(ttl)} }) ignored; it must be a positive number of milliseconds.`);
15
+ }
16
+ return _renderLeaseTtl;
17
+ }
18
+ if (ttl > MAX_TIMEOUT) {
19
+ {
20
+ console.warn(`signalium: setConfig({ renderLeaseTtl: ${String(ttl)} }) clamped to ${MAX_TIMEOUT} ms, the longest timer delay.`);
21
+ }
22
+ return MAX_TIMEOUT;
23
+ }
24
+ return ttl;
25
+ }
26
+ let _reactDelivery = "sync";
27
+ const resolvedPromise = Promise.resolve();
28
+ let _scheduleReactDelivery = (fn) => {
29
+ resolvedPromise.then(fn);
30
+ };
31
+ function setConfig(cfg) {
32
+ _scheduleFlush = cfg.scheduleFlush ?? _scheduleFlush;
33
+ _runBatch = cfg.runBatch ?? _runBatch;
34
+ if (cfg.renderLeaseTtl !== void 0) {
35
+ _renderLeaseTtl = validateRenderLeaseTtl(cfg.renderLeaseTtl);
36
+ }
37
+ _reactDelivery = cfg.reactDelivery ?? _reactDelivery;
38
+ _scheduleReactDelivery = cfg.scheduleReactDelivery ?? _scheduleReactDelivery;
39
+ }
40
+ const scheduleFlush = (fn) => {
41
+ _scheduleFlush(fn);
42
+ };
43
+ const runBatch = (fn) => {
44
+ _runBatch(fn);
45
+ };
46
+ const getRenderLeaseTtl = () => _renderLeaseTtl;
47
+ const getReactDelivery = () => _reactDelivery;
48
+ const scheduleReactDelivery = (fn) => {
49
+ _scheduleReactDelivery(fn);
50
+ };
51
+ exports.MAX_TIMEOUT = MAX_TIMEOUT;
52
+ exports.getReactDelivery = getReactDelivery;
53
+ exports.getRenderLeaseTtl = getRenderLeaseTtl;
54
+ exports.runBatch = runBatch;
55
+ exports.scheduleFlush = scheduleFlush;
56
+ exports.scheduleReactDelivery = scheduleReactDelivery;
57
+ exports.setConfig = setConfig;
58
+ //# sourceMappingURL=config-D6wyrhrW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-D6wyrhrW.js","sources":["../../../.tsc-out/internals/config.js"],"sourcesContent":["let _scheduleFlush = flushWatchers => {\n setTimeout(() => {\n flushWatchers();\n }, 0);\n};\nlet _runBatch = fn => fn();\n/** Default render lease TTL (ms). It must outlive a transition render, which React caps at 5 s. */\nexport const DEFAULT_RENDER_LEASE_TTL = 10_000;\nlet _renderLeaseTtl = DEFAULT_RENDER_LEASE_TTL;\n/** The longest `setTimeout` delay (2^31 - 1 ms); longer ones fire immediately. */\nexport const MAX_TIMEOUT = 2 ** 31 - 1;\nfunction validateRenderLeaseTtl(ttl) {\n if (typeof ttl !== 'number' || Number.isNaN(ttl) || ttl <= 0) {\n if (IS_DEV) {\n console.warn(`signalium: setConfig({ renderLeaseTtl: ${String(ttl)} }) ignored; it must be a positive number of milliseconds.`);\n }\n return _renderLeaseTtl;\n }\n if (ttl > MAX_TIMEOUT) {\n if (IS_DEV) {\n console.warn(`signalium: setConfig({ renderLeaseTtl: ${String(ttl)} }) clamped to ${MAX_TIMEOUT} ms, the longest timer delay.`);\n }\n return MAX_TIMEOUT;\n }\n return ttl;\n}\nlet _reactDelivery = 'sync';\nconst resolvedPromise = Promise.resolve();\nlet _scheduleReactDelivery = fn => {\n resolvedPromise.then(fn);\n};\nexport function setConfig(cfg) {\n _scheduleFlush = cfg.scheduleFlush ?? _scheduleFlush;\n _runBatch = cfg.runBatch ?? _runBatch;\n if (cfg.renderLeaseTtl !== undefined) {\n _renderLeaseTtl = validateRenderLeaseTtl(cfg.renderLeaseTtl);\n }\n _reactDelivery = cfg.reactDelivery ?? _reactDelivery;\n _scheduleReactDelivery = cfg.scheduleReactDelivery ?? _scheduleReactDelivery;\n}\nexport const scheduleFlush = (fn) => {\n _scheduleFlush(fn);\n};\nexport const runBatch = (fn) => {\n _runBatch(fn);\n};\nexport const getRenderLeaseTtl = () => _renderLeaseTtl;\nexport const getReactDelivery = () => _reactDelivery;\nexport const scheduleReactDelivery = (fn) => {\n _scheduleReactDelivery(fn);\n};\n"],"names":[],"mappings":";AAAA,IAAI,iBAAiB,CAAA,kBAAiB;AAClC,aAAW,MAAM;AACb,kBAAA;AAAA,EACJ,GAAG,CAAC;AACR;AACA,IAAI,YAAY,QAAM,GAAA;AAEf,MAAM,2BAA2B;AACxC,IAAI,kBAAkB;AAEf,MAAM,cAAc,KAAK,KAAK;AACrC,SAAS,uBAAuB,KAAK;AACjC,MAAI,OAAO,QAAQ,YAAY,OAAO,MAAM,GAAG,KAAK,OAAO,GAAG;AAC9C;AACR,cAAQ,KAAK,0CAA0C,OAAO,GAAG,CAAC,4DAA4D;AAAA,IAClI;AACA,WAAO;AAAA,EACX;AACA,MAAI,MAAM,aAAa;AACP;AACR,cAAQ,KAAK,0CAA0C,OAAO,GAAG,CAAC,kBAAkB,WAAW,+BAA+B;AAAA,IAClI;AACA,WAAO;AAAA,EACX;AACA,SAAO;AACX;AACA,IAAI,iBAAiB;AACrB,MAAM,kBAAkB,QAAQ,QAAA;AAChC,IAAI,yBAAyB,CAAA,OAAM;AAC/B,kBAAgB,KAAK,EAAE;AAC3B;AACO,SAAS,UAAU,KAAK;AAC3B,mBAAiB,IAAI,iBAAiB;AACtC,cAAY,IAAI,YAAY;AAC5B,MAAI,IAAI,mBAAmB,QAAW;AAClC,sBAAkB,uBAAuB,IAAI,cAAc;AAAA,EAC/D;AACA,mBAAiB,IAAI,iBAAiB;AACtC,2BAAyB,IAAI,yBAAyB;AAC1D;AACO,MAAM,gBAAgB,CAAC,OAAO;AACjC,iBAAe,EAAE;AACrB;AACO,MAAM,WAAW,CAAC,OAAO;AAC5B,YAAU,EAAE;AAChB;AACO,MAAM,oBAAoB,MAAM;AAChC,MAAM,mBAAmB,MAAM;AAC/B,MAAM,wBAAwB,CAAC,OAAO;AACzC,yBAAuB,EAAE;AAC7B;;;;;;;;"}
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const config = require("./config-B0MtLBgx.js");
3
+ const config = require("./config-D6wyrhrW.js");
4
4
  exports.setConfig = config.setConfig;
5
5
  //# sourceMappingURL=config.js.map
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const config = require("./config-B0MtLBgx.js");
2
+ const config = require("./config-D6wyrhrW.js");
3
3
  let UNKNOWN_SIGNAL_ID = 0;
4
4
  const UNKNOWN_SIGNAL_NAMES = /* @__PURE__ */ new Map();
5
5
  function getUnknownSignalFnName(fn) {
@@ -428,12 +428,12 @@ function watchSignal(signal2) {
428
428
  }
429
429
  activateSignal(signal2);
430
430
  }
431
- function unwatchSignal(signal2) {
431
+ function unwatchSignal(signal2, options = {}) {
432
432
  const { watchCount } = signal2;
433
433
  const newWatchCount = Math.max(watchCount - 1, 0);
434
434
  signal2.watchCount = newWatchCount;
435
435
  if (newWatchCount === 0) {
436
- scheduleDeactivate(signal2);
436
+ scheduleDeactivate(signal2, options);
437
437
  }
438
438
  }
439
439
  function activateSignal(signal2) {
@@ -443,22 +443,22 @@ function activateSignal(signal2) {
443
443
  checkSignal(signal2);
444
444
  }
445
445
  }
446
- function deactivateSignal(signal2) {
446
+ function deactivateSignal(signal2, options = {}) {
447
447
  signal2._isActive = false;
448
448
  for (const dep of signal2.deps.keys()) {
449
449
  const newWatchCount = Math.max(dep.watchCount - 1, 0);
450
450
  dep.watchCount = newWatchCount;
451
451
  if (newWatchCount === 0) {
452
- deactivateSignal(dep);
452
+ deactivateSignal(dep, options);
453
453
  }
454
454
  }
455
455
  if (isRelay(signal2)) {
456
- signal2._value?.();
456
+ signal2._value?.(options);
457
457
  }
458
458
  }
459
459
  let PENDING_PULLS = /* @__PURE__ */ new Set();
460
460
  let PENDING_ASYNC_PULLS = [];
461
- let PENDING_DEACTIVE = /* @__PURE__ */ new Set();
461
+ let PENDING_DEACTIVE = /* @__PURE__ */ new Map();
462
462
  let PENDING_LISTENERS = [];
463
463
  let PENDING_TRACERS = [];
464
464
  const microtask = () => Promise.resolve();
@@ -482,8 +482,9 @@ const scheduleAsyncPull = (signal2) => {
482
482
  PENDING_ASYNC_PULLS.push(signal2);
483
483
  scheduleFlush();
484
484
  };
485
- const scheduleDeactivate = (signal2) => {
486
- PENDING_DEACTIVE.add(signal2);
485
+ const scheduleDeactivate = (signal2, options = {}) => {
486
+ const existing = PENDING_DEACTIVE.get(signal2);
487
+ PENDING_DEACTIVE.set(signal2, existing === void 0 ? options : { isPausing: Boolean(existing.isPausing && options.isPausing) });
487
488
  scheduleFlush();
488
489
  };
489
490
  const cancelDeactivate = (signal2) => {
@@ -518,8 +519,8 @@ const flushWatchers = async () => {
518
519
  }
519
520
  currentFlush = null;
520
521
  config.runBatch(() => {
521
- for (const signal2 of PENDING_DEACTIVE) {
522
- deactivateSignal(signal2);
522
+ for (const [signal2, options] of PENDING_DEACTIVE) {
523
+ deactivateSignal(signal2, options);
523
524
  }
524
525
  PENDING_DEACTIVE.clear();
525
526
  for (const signal2 of PENDING_LISTENERS) {
@@ -1299,11 +1300,11 @@ function createRelay(activate, scope, opts) {
1299
1300
  const p = new ReactivePromiseImpl();
1300
1301
  let active = false;
1301
1302
  let currentSub;
1302
- const unsubscribe = () => {
1303
+ const unsubscribe = (options) => {
1303
1304
  if (typeof currentSub === "function") {
1304
1305
  currentSub();
1305
1306
  } else if (currentSub !== void 0) {
1306
- currentSub.deactivate?.();
1307
+ currentSub.deactivate?.(options);
1307
1308
  }
1308
1309
  const signal2 = p["_signal"];
1309
1310
  signal2.subs = /* @__PURE__ */ new Map();
@@ -1603,32 +1604,43 @@ function hashNumber(num, seed = 0) {
1603
1604
  h ^= h >>> 16;
1604
1605
  return h >>> 0;
1605
1606
  }
1606
- function hashArray(arr, seen) {
1607
- let h = ARRAY;
1607
+ function mixHash(h, k) {
1608
1608
  const c1 = 3432918353;
1609
1609
  const c2 = 461845907;
1610
- for (const item of arr) {
1611
- let k = hashValue(item, seen);
1612
- k = imul(k, c1);
1613
- k = k << 15 | k >>> 17;
1614
- k = imul(k, c2);
1615
- h ^= k;
1616
- h = h << 13 | h >>> 19;
1617
- h = imul(h, 5) + 3864292196;
1618
- }
1619
- h ^= arr.length;
1610
+ const n = 3864292196;
1611
+ k = imul(k, c1);
1612
+ k = k << 15 | k >>> 17;
1613
+ k = imul(k, c2);
1614
+ h ^= k;
1615
+ h = h << 13 | h >>> 19;
1616
+ return imul(h, 5) + n | 0;
1617
+ }
1618
+ function finalizeHash(h, length) {
1619
+ const fmix1 = 2246822507;
1620
+ const fmix2 = 3266489909;
1621
+ h ^= length;
1620
1622
  h ^= h >>> 16;
1621
- h = imul(h, 2246822507);
1623
+ h = imul(h, fmix1);
1622
1624
  h ^= h >>> 13;
1623
- h = imul(h, 3266489909);
1625
+ h = imul(h, fmix2);
1624
1626
  h ^= h >>> 16;
1625
1627
  return h >>> 0;
1626
1628
  }
1629
+ function hashArray(arr, seen) {
1630
+ let h = ARRAY_SEED;
1631
+ for (const item of arr) {
1632
+ h = mixHash(h, hashValue(item, seen));
1633
+ }
1634
+ return finalizeHash(h, arr.length);
1635
+ }
1627
1636
  function hashObject(obj, seen) {
1637
+ return hashObjectKeys(obj, Object.keys(obj), seen);
1638
+ }
1639
+ function hashObjectKeys(obj, keys, seen) {
1640
+ const keyMultiplier = 2654435769;
1628
1641
  let sum = OBJECT;
1629
- const keys = Object.keys(obj);
1630
1642
  for (const key of keys) {
1631
- sum += imul(hashValue(key, seen), 2654435769) ^ hashValue(obj[key], seen);
1643
+ sum += imul(hashValue(key, seen), keyMultiplier) ^ hashValue(obj[key], seen);
1632
1644
  }
1633
1645
  return sum >>> 0;
1634
1646
  }
@@ -1640,9 +1652,10 @@ function hashSet(set, seen) {
1640
1652
  return sum >>> 0;
1641
1653
  }
1642
1654
  function hashMap(map, seen) {
1655
+ const keyMultiplier = 2654435769;
1643
1656
  let sum = MAP;
1644
1657
  for (const [key, value] of map) {
1645
- sum += imul(hashValue(key, seen), 2654435769) ^ hashValue(value, seen);
1658
+ sum += imul(hashValue(key, seen), keyMultiplier) ^ hashValue(value, seen);
1646
1659
  }
1647
1660
  return sum >>> 0;
1648
1661
  }
@@ -1681,7 +1694,7 @@ const FALSE = hashStr(
1681
1694
  3
1682
1695
  /* HashType.FALSE */
1683
1696
  );
1684
- const ARRAY = hashStr(
1697
+ const ARRAY_SEED = hashStr(
1685
1698
  "array",
1686
1699
  7
1687
1700
  /* HashType.ARRAY */
@@ -1845,6 +1858,95 @@ function stringifyValue(value) {
1845
1858
  return getObjectId(value);
1846
1859
  }
1847
1860
  }
1861
+ let young = /* @__PURE__ */ new Set();
1862
+ let old = /* @__PURE__ */ new Set();
1863
+ let timer;
1864
+ const pinned = /* @__PURE__ */ new Map();
1865
+ const SUSPENDED_LEASE_HOLD_TTLS = 30;
1866
+ function armTimer() {
1867
+ timer = setTimeout(sweepLeases, config.getRenderLeaseTtl());
1868
+ timer.unref?.();
1869
+ }
1870
+ function sweepLeases() {
1871
+ timer = void 0;
1872
+ const expired = old;
1873
+ for (const signal2 of expired) {
1874
+ signal2._releaseLease();
1875
+ }
1876
+ expired.clear();
1877
+ old = young;
1878
+ young = expired;
1879
+ if (old.size > 0) {
1880
+ armTimer();
1881
+ }
1882
+ }
1883
+ function addLease(signal2) {
1884
+ if (!pinned.delete(signal2)) {
1885
+ old.delete(signal2);
1886
+ }
1887
+ young.add(signal2);
1888
+ if (timer === void 0) {
1889
+ armTimer();
1890
+ }
1891
+ }
1892
+ function removeLease(signal2) {
1893
+ if (!young.delete(signal2) && !old.delete(signal2)) {
1894
+ pinned.delete(signal2);
1895
+ }
1896
+ }
1897
+ function pinLease(signal2) {
1898
+ if (!signal2._isLeased) {
1899
+ return void 0;
1900
+ }
1901
+ young.delete(signal2);
1902
+ old.delete(signal2);
1903
+ const token = {};
1904
+ pinned.set(signal2, token);
1905
+ return token;
1906
+ }
1907
+ function unpinLease(signal2, token) {
1908
+ if (pinned.get(signal2) === token && signal2._isLeased) {
1909
+ addLease(signal2);
1910
+ }
1911
+ }
1912
+ function holdLeaseUntilSettled(signal2, thenable) {
1913
+ const token = pinLease(signal2);
1914
+ if (token === void 0) {
1915
+ return;
1916
+ }
1917
+ const renew = () => unpinLease(signal2, token);
1918
+ thenable.then(renew, renew);
1919
+ }
1920
+ function holdSuspendedLease(signal2) {
1921
+ const token = pinLease(signal2);
1922
+ if (token === void 0) {
1923
+ return;
1924
+ }
1925
+ const hold = setTimeout(() => unpinLease(signal2, token), Math.min(config.getRenderLeaseTtl() * SUSPENDED_LEASE_HOLD_TTLS, config.MAX_TIMEOUT));
1926
+ hold.unref?.();
1927
+ }
1928
+ function releaseAbandonedAttempt(signal2) {
1929
+ const token = pinned.get(signal2);
1930
+ if (token !== void 0) {
1931
+ unpinLease(signal2, token);
1932
+ }
1933
+ }
1934
+ function releaseRenderLeases() {
1935
+ if (timer !== void 0) {
1936
+ clearTimeout(timer);
1937
+ timer = void 0;
1938
+ }
1939
+ const leases = [...old, ...young, ...pinned.keys()];
1940
+ old = /* @__PURE__ */ new Set();
1941
+ young = /* @__PURE__ */ new Set();
1942
+ pinned.clear();
1943
+ for (const signal2 of leases) {
1944
+ signal2._releaseLease();
1945
+ }
1946
+ }
1947
+ function getRenderLeaseCount() {
1948
+ return young.size + old.size + pinned.size;
1949
+ }
1848
1950
  let ID = 0;
1849
1951
  function createReactiveDefinition(id, desc, compute, equals, isRelay2, paramKey, tracer) {
1850
1952
  const def = {
@@ -1917,6 +2019,9 @@ class ReactiveSignal {
1917
2019
  this.flags &= -33;
1918
2020
  }
1919
2021
  }
2022
+ get _isLeased() {
2023
+ return (this.flags & 128) !== 0;
2024
+ }
1920
2025
  get _isLazy() {
1921
2026
  return (this.flags & 64) !== 0;
1922
2027
  }
@@ -1931,6 +2036,7 @@ class ReactiveSignal {
1931
2036
  return this._listeners ?? (this._listeners = {
1932
2037
  updatedAt: 0,
1933
2038
  current: /* @__PURE__ */ new Map(),
2039
+ pausedReaders: 0,
1934
2040
  cachedBoundAdd: this.addListener.bind(this)
1935
2041
  });
1936
2042
  }
@@ -1938,7 +2044,8 @@ class ReactiveSignal {
1938
2044
  return getSignal(this);
1939
2045
  }
1940
2046
  addListener(listener, opts) {
1941
- const { current } = this.listeners;
2047
+ const meta = this.listeners;
2048
+ const { current } = meta;
1942
2049
  if (!current.has(listener)) {
1943
2050
  let effective = listener;
1944
2051
  if (opts?.skipInitial) {
@@ -1951,23 +2058,39 @@ class ReactiveSignal {
1951
2058
  listener();
1952
2059
  };
1953
2060
  }
1954
- if (!this._isListener) {
1955
- watchSignal(this);
1956
- this.flags |= 16;
2061
+ const flags = this.flags;
2062
+ current.set(listener, effective);
2063
+ const unpaused = meta.pausedReaders < current.size;
2064
+ if ((flags & 16) === 0) {
2065
+ this.flags = flags | 16;
2066
+ } else if ((flags & 128) !== 0 && (unpaused || (flags & 256) === 0)) {
2067
+ this.flags = flags & -129;
2068
+ removeLease(this);
2069
+ }
2070
+ if (unpaused) {
2071
+ this._takeListenerWatch();
2072
+ } else {
2073
+ this._pauseListenerWatch();
1957
2074
  }
1958
2075
  if (this.watchCount > 0) {
1959
2076
  schedulePull(this);
1960
2077
  }
1961
- current.set(listener, effective);
1962
2078
  }
1963
2079
  return () => {
1964
2080
  if (current.has(listener)) {
1965
2081
  current.delete(listener);
1966
2082
  if (current.size === 0) {
2083
+ const flags = this.flags;
2084
+ if ((flags & 128) !== 0)
2085
+ return;
1967
2086
  cancelPull(this);
1968
- unwatchSignal(this);
1969
- this.flags &= -17;
1970
- this.listeners.updatedAt = 0;
2087
+ this.flags = flags & -273;
2088
+ if ((flags & 256) !== 0) {
2089
+ unwatchSignal(this);
2090
+ }
2091
+ meta.updatedAt = 0;
2092
+ } else if (meta.pausedReaders >= current.size) {
2093
+ this._pauseListenerWatch();
1971
2094
  }
1972
2095
  }
1973
2096
  };
@@ -1975,15 +2098,99 @@ class ReactiveSignal {
1975
2098
  // This method is used in React hooks specifically. It returns a bound add method
1976
2099
  // that is cached to avoid creating a new one on each call, and it eagerly sets
1977
2100
  // the listener as watched so that relays that are accessed will be activated.
2101
+ //
2102
+ // The eager watch is a lease (see `lease.ts`): the commit's subscription claims it, and a
2103
+ // discarded render's lease expires. Each render of a still-leased signal extends it.
1978
2104
  addListenerLazy(watch = true) {
1979
- if (!this._isListener) {
2105
+ const flags = this.flags;
2106
+ if ((flags & 16) === 0) {
1980
2107
  if (watch) {
1981
2108
  watchSignal(this);
2109
+ this.flags |= 16 | 128 | 256;
2110
+ } else {
2111
+ this.flags |= 16 | 128;
2112
+ }
2113
+ addLease(this);
2114
+ } else if ((flags & 128) !== 0 || watch && (flags & 256) === 0) {
2115
+ if (watch && (flags & 256) === 0) {
2116
+ watchSignal(this);
2117
+ this.flags |= 256;
1982
2118
  }
1983
- this.flags |= 16;
2119
+ this.flags |= 128;
2120
+ addLease(this);
1984
2121
  }
1985
2122
  return this.listeners.cachedBoundAdd;
1986
2123
  }
2124
+ /** Drops the listener watch; listeners stay subscribed. */
2125
+ _pauseWatch() {
2126
+ const flags = this.flags;
2127
+ if ((flags & 256) !== 0) {
2128
+ this.flags = flags & -257;
2129
+ unwatchSignal(this, { isPausing: true });
2130
+ }
2131
+ }
2132
+ /** Retakes the listener watch, pulling changes missed while unwatched. */
2133
+ _resumeWatch() {
2134
+ if ((this.flags & 16) !== 0 && this._takeListenerWatch()) {
2135
+ schedulePull(this);
2136
+ }
2137
+ }
2138
+ _takeListenerWatch() {
2139
+ if ((this.flags & 256) !== 0) {
2140
+ return false;
2141
+ }
2142
+ watchSignal(this);
2143
+ this.flags |= 256;
2144
+ return true;
2145
+ }
2146
+ /** `deferred` reconciles in a microtask, for a reader that unregisters before it unsubscribes. */
2147
+ _addPausedReaders(delta, deferred = false) {
2148
+ this.listeners.pausedReaders += delta;
2149
+ if (deferred) {
2150
+ queueMicrotask(() => this._reconcilePausedReaders());
2151
+ } else {
2152
+ this._reconcilePausedReaders();
2153
+ }
2154
+ }
2155
+ _reconcilePausedReaders() {
2156
+ const { current, pausedReaders } = this.listeners;
2157
+ if (current.size === 0)
2158
+ return;
2159
+ if (pausedReaders < current.size) {
2160
+ this._resumeWatch();
2161
+ } else {
2162
+ this._pauseListenerWatch();
2163
+ }
2164
+ }
2165
+ /** Pauses for the listeners' sake; an unclaimed render lease's watch belongs to its render. */
2166
+ _pauseListenerWatch() {
2167
+ if ((this.flags & 128) === 0) {
2168
+ this._pauseWatch();
2169
+ }
2170
+ }
2171
+ /** Releases an unclaimed render lease; a no-op once claimed. */
2172
+ _releaseLease() {
2173
+ const flags = this.flags;
2174
+ if ((flags & 128) === 0) {
2175
+ return;
2176
+ }
2177
+ const meta = this._listeners;
2178
+ if (meta !== null && meta.current.size > 0) {
2179
+ this.flags = flags & -129;
2180
+ if (meta.pausedReaders >= meta.current.size) {
2181
+ this._pauseWatch();
2182
+ }
2183
+ return;
2184
+ }
2185
+ this.flags = flags & -401;
2186
+ if (meta !== null) {
2187
+ meta.updatedAt = 0;
2188
+ }
2189
+ cancelPull(this);
2190
+ if ((flags & 256) !== 0) {
2191
+ unwatchSignal(this);
2192
+ }
2193
+ }
1987
2194
  }
1988
2195
  const runListeners = (signal2) => {
1989
2196
  const { listeners } = signal2;
@@ -2123,11 +2330,13 @@ const getScopeOwner = (obj) => {
2123
2330
  }
2124
2331
  return scope;
2125
2332
  };
2333
+ exports.ARRAY_SEED = ARRAY_SEED;
2126
2334
  exports.ReactivePromise = ReactivePromise;
2127
2335
  exports.SignalScope = SignalScope;
2128
2336
  exports.Tracer = Tracer;
2129
2337
  exports.VisualizerNode = VisualizerNode;
2130
2338
  exports.callback = callback;
2339
+ exports.checkSignal = checkSignal;
2131
2340
  exports.clearGlobalContexts = clearGlobalContexts;
2132
2341
  exports.context = context;
2133
2342
  exports.createReactiveDefinition = createReactiveDefinition;
@@ -2136,24 +2345,32 @@ exports.createRelay = createRelay;
2136
2345
  exports.createTask = createTask;
2137
2346
  exports.createTracer = createTracer;
2138
2347
  exports.createTracerFromId = createTracerFromId;
2348
+ exports.finalizeHash = finalizeHash;
2139
2349
  exports.generatorResultToPromiseWithConsumer = generatorResultToPromiseWithConsumer;
2140
2350
  exports.getContext = getContext;
2141
2351
  exports.getCurrentConsumer = getCurrentConsumer;
2142
2352
  exports.getCurrentScope = getCurrentScope;
2143
2353
  exports.getGlobalScope = getGlobalScope;
2354
+ exports.getObjectHash = getObjectHash;
2355
+ exports.getRenderLeaseCount = getRenderLeaseCount;
2144
2356
  exports.getScopeOwner = getScopeOwner;
2145
2357
  exports.getSignal = getSignal;
2146
2358
  exports.getTracerProxy = getTracerProxy;
2359
+ exports.hashObjectKeys = hashObjectKeys;
2147
2360
  exports.hashValue = hashValue;
2361
+ exports.holdLeaseUntilSettled = holdLeaseUntilSettled;
2362
+ exports.holdSuspendedLease = holdSuspendedLease;
2148
2363
  exports.isPromise = isPromise;
2149
2364
  exports.isReactivePromise = isReactivePromise;
2150
2365
  exports.isSignal = isSignal;
2151
2366
  exports.isThennable = isThennable;
2367
+ exports.mixHash = mixHash;
2152
2368
  exports.notifier = notifier;
2153
2369
  exports.registerCustomHash = registerCustomHash;
2370
+ exports.releaseAbandonedAttempt = releaseAbandonedAttempt;
2371
+ exports.releaseRenderLeases = releaseRenderLeases;
2154
2372
  exports.removeTracer = removeTracer;
2155
2373
  exports.runSignal = runSignal;
2156
- exports.schedulePull = schedulePull;
2157
2374
  exports.scheduleTracer = scheduleTracer;
2158
2375
  exports.setCurrentConsumer = setCurrentConsumer;
2159
2376
  exports.setGlobalContexts = setGlobalContexts;
@@ -2166,4 +2383,4 @@ exports.signal = signal;
2166
2383
  exports.unwatchSignal = unwatchSignal;
2167
2384
  exports.watchSignal = watchSignal;
2168
2385
  exports.withContexts = withContexts;
2169
- //# sourceMappingURL=debug-C46WDxhL.js.map
2386
+ //# sourceMappingURL=debug-CbAX7VeM.js.map