octane 0.2.11 → 0.2.13

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 (264) hide show
  1. package/README.md +5 -0
  2. package/dist/behavior-root.d.ts +13 -3
  3. package/dist/behavior-root.js +28 -5
  4. package/dist/cjs/behavior-root.cjs +32 -6
  5. package/dist/cjs/class-names.cjs +49 -0
  6. package/dist/cjs/constants.cjs +9 -0
  7. package/dist/cjs/context-epoch.cjs +38 -0
  8. package/dist/cjs/css.cjs +6 -27
  9. package/dist/cjs/data-encoding.cjs +160 -0
  10. package/dist/cjs/dom-binding-handoff.cjs +93 -0
  11. package/dist/cjs/dom-binding-protocol.cjs +115 -0
  12. package/dist/cjs/dom-binding-styles.cjs +292 -0
  13. package/dist/cjs/dom-bindings.cjs +700 -0
  14. package/dist/cjs/dom-focused-move.cjs +66 -0
  15. package/dist/cjs/dom-stage.cjs +896 -0
  16. package/dist/cjs/dom-tables.cjs +4 -94
  17. package/dist/cjs/error-codes.client.generated.cjs +33 -0
  18. package/dist/cjs/error-codes.server.generated.cjs +24 -0
  19. package/dist/cjs/hydration/control-capture.cjs +244 -0
  20. package/dist/cjs/hydration/event-capture.cjs +125 -43
  21. package/dist/cjs/hydration/interaction-config.cjs +50 -2
  22. package/dist/cjs/hydration-markers.cjs +37 -0
  23. package/dist/cjs/independent-hydration-protocol.cjs +106 -0
  24. package/dist/cjs/index.cjs +9 -0
  25. package/dist/cjs/runtime.cjs +6397 -1683
  26. package/dist/cjs/runtime.server.cjs +1465 -213
  27. package/dist/cjs/server/early-signals.cjs +45 -0
  28. package/dist/cjs/server/index.cjs +47 -0
  29. package/dist/cjs/server/rpc-batch.cjs +149 -0
  30. package/dist/cjs/server/rpc-stream.cjs +146 -0
  31. package/dist/cjs/server/rpc.cjs +34 -15
  32. package/dist/cjs/server/signal-query-observation.cjs +158 -0
  33. package/dist/cjs/server/streamed-region.cjs +52 -0
  34. package/dist/cjs/server/streamed-signals.cjs +512 -0
  35. package/dist/cjs/server-call.cjs +103 -0
  36. package/dist/cjs/server-rpc-batch-client.cjs +141 -0
  37. package/dist/cjs/server-rpc-client.cjs +94 -27
  38. package/dist/cjs/server-rpc-protocol.cjs +141 -0
  39. package/dist/cjs/server-rpc-stream-client.cjs +118 -0
  40. package/dist/cjs/signals/actions.cjs +474 -0
  41. package/dist/cjs/signals/client.cjs +4 -1
  42. package/dist/cjs/signals/computations.cjs +460 -0
  43. package/dist/cjs/signals/control-binding.cjs +368 -0
  44. package/dist/cjs/signals/derived-facade.cjs +50 -0
  45. package/dist/cjs/signals/document-owner.cjs +71 -0
  46. package/dist/cjs/signals/early-values.cjs +155 -0
  47. package/dist/cjs/signals/encoding.cjs +6 -95
  48. package/dist/cjs/signals/engine.cjs +276 -12
  49. package/dist/cjs/signals/errors.cjs +18 -0
  50. package/dist/cjs/signals/facade.cjs +477 -0
  51. package/dist/cjs/signals/graph.cjs +166 -20
  52. package/dist/cjs/signals/handle-protocol.cjs +36 -0
  53. package/dist/cjs/signals/index.cjs +81 -2
  54. package/dist/cjs/signals/native-read-client.cjs +10 -0
  55. package/dist/cjs/signals/owner-context.cjs +112 -0
  56. package/dist/cjs/signals/query-attempt-observer.cjs +68 -0
  57. package/dist/cjs/signals/query-facade.cjs +72 -0
  58. package/dist/cjs/signals/read-protocol.cjs +54 -0
  59. package/dist/cjs/signals/requests.cjs +287 -14
  60. package/dist/cjs/signals/scalar-computations.cjs +76 -0
  61. package/dist/cjs/signals/server.cjs +4 -1
  62. package/dist/cjs/signals/transition-candidate.cjs +567 -0
  63. package/dist/cjs/signals/transition-state.cjs +106 -0
  64. package/dist/cjs/signals/types.cjs +50 -0
  65. package/dist/cjs/stream-protocol.cjs +3 -0
  66. package/dist/cjs/streamed-signals-protocol.cjs +159 -0
  67. package/dist/cjs/style-values.cjs +122 -0
  68. package/dist/cjs/trusted-html.cjs +30 -0
  69. package/dist/cjs/version.cjs +1 -1
  70. package/dist/class-names.d.ts +5 -0
  71. package/dist/class-names.js +25 -0
  72. package/dist/compiler/bundler.js +51 -22
  73. package/dist/compiler/compile.js +2534 -581
  74. package/dist/compiler/dom-binding-program.js +935 -0
  75. package/dist/compiler/dom-binding-request.js +57 -0
  76. package/dist/compiler/dom-bindings.js +2245 -0
  77. package/dist/compiler/hook-deps.js +759 -10
  78. package/dist/compiler/hydrate-boundaries.js +358 -15
  79. package/dist/compiler/index.d.ts +39 -2
  80. package/dist/compiler/index.js +1 -0
  81. package/dist/compiler/native-attribute-reads.js +127 -0
  82. package/dist/compiler/native-change-diagnostics.js +32 -38
  83. package/dist/compiler/native-read-diagnostics.js +69 -8
  84. package/dist/compiler/native-read-types.js +80 -34
  85. package/dist/compiler/parser.node.js +1 -1
  86. package/dist/compiler/renderer-boundaries.js +40 -0
  87. package/dist/compiler/server-context.js +118 -0
  88. package/dist/compiler/signal-attempt-reads.js +479 -0
  89. package/dist/compiler/signal-declarations.js +557 -0
  90. package/dist/compiler/signal-start-reads.js +413 -0
  91. package/dist/compiler/slot-hooks.d.ts +3 -1
  92. package/dist/compiler/slot-hooks.js +168 -34
  93. package/dist/compiler/strong-auto-memo.js +90 -0
  94. package/dist/compiler/strong-html.js +143 -0
  95. package/dist/compiler/strong-mode.js +764 -66
  96. package/dist/compiler/strong-template-policy.js +307 -0
  97. package/dist/compiler/template-ir.js +200 -0
  98. package/dist/compiler/typescript.d.ts +2 -0
  99. package/dist/compiler/typescript.js +2 -0
  100. package/dist/compiler/vite.d.ts +3 -1
  101. package/dist/compiler/vite.js +122 -4
  102. package/dist/compiler/volar.LICENSES.txt +1 -1
  103. package/dist/compiler/volar.d.ts +6 -1
  104. package/dist/compiler/volar.js +750 -1433
  105. package/dist/constants.d.ts +1 -0
  106. package/dist/constants.js +10 -0
  107. package/dist/context-epoch.d.ts +2 -0
  108. package/dist/context-epoch.js +13 -0
  109. package/dist/css.d.ts +2 -5
  110. package/dist/css.js +2 -23
  111. package/dist/data-encoding.d.ts +9 -0
  112. package/dist/data-encoding.js +134 -0
  113. package/dist/dom-binding-classes.d.ts +15 -0
  114. package/dist/dom-binding-classes.js +129 -0
  115. package/dist/dom-binding-controls.d.ts +2 -0
  116. package/dist/dom-binding-controls.js +6 -0
  117. package/dist/dom-binding-handoff.d.ts +55 -0
  118. package/dist/dom-binding-handoff.js +62 -0
  119. package/dist/dom-binding-program.d.ts +300 -0
  120. package/dist/dom-binding-program.js +1625 -0
  121. package/dist/dom-binding-projections.d.ts +15 -0
  122. package/dist/dom-binding-projections.js +187 -0
  123. package/dist/dom-binding-protocol.d.ts +13 -0
  124. package/dist/dom-binding-protocol.js +88 -0
  125. package/dist/dom-binding-signals.d.ts +21 -0
  126. package/dist/dom-binding-signals.js +109 -0
  127. package/dist/dom-binding-styles.d.ts +17 -0
  128. package/dist/dom-binding-styles.js +272 -0
  129. package/dist/dom-bindings.d.ts +89 -0
  130. package/dist/dom-bindings.js +670 -0
  131. package/dist/dom-focused-move.d.ts +6 -0
  132. package/dist/dom-focused-move.js +42 -0
  133. package/dist/dom-stage.d.ts +52 -0
  134. package/dist/dom-stage.js +872 -0
  135. package/dist/dom-tables.d.ts +1 -3
  136. package/dist/dom-tables.js +1 -91
  137. package/dist/error-codes.client.generated.d.ts +9 -0
  138. package/dist/error-codes.client.generated.js +33 -0
  139. package/dist/error-codes.server.generated.d.ts +6 -0
  140. package/dist/error-codes.server.generated.js +24 -0
  141. package/dist/hydration/control-capture.d.ts +62 -0
  142. package/dist/hydration/control-capture.js +223 -0
  143. package/dist/hydration/document-lifecycle.d.ts +23 -0
  144. package/dist/hydration/document-lifecycle.js +80 -0
  145. package/dist/hydration/event-capture.d.ts +20 -1
  146. package/dist/hydration/event-capture.js +128 -42
  147. package/dist/hydration/independent-island.d.ts +45 -0
  148. package/dist/hydration/independent-island.js +233 -0
  149. package/dist/hydration/index.d.ts +8 -1
  150. package/dist/hydration/index.js +55 -1
  151. package/dist/hydration/interaction-config.d.ts +5 -0
  152. package/dist/hydration/interaction-config.js +44 -1
  153. package/dist/hydration/stream-delivery.d.ts +32 -0
  154. package/dist/hydration/stream-delivery.js +333 -0
  155. package/dist/hydration/stream-receiver.d.ts +26 -0
  156. package/dist/hydration/stream-receiver.js +204 -0
  157. package/dist/hydration/stream-result-receiver.d.ts +70 -0
  158. package/dist/hydration/stream-result-receiver.js +246 -0
  159. package/dist/hydration/streamed-signals.d.ts +44 -0
  160. package/dist/hydration/streamed-signals.js +186 -0
  161. package/dist/hydration/types.d.ts +6 -0
  162. package/dist/hydration-markers.d.ts +7 -0
  163. package/dist/hydration-markers.js +10 -0
  164. package/dist/independent-hydration-protocol.d.ts +31 -0
  165. package/dist/independent-hydration-protocol.js +80 -0
  166. package/dist/index.d.ts +3 -0
  167. package/dist/index.js +6 -0
  168. package/dist/internal/signal-read.d.ts +1 -0
  169. package/dist/internal/signal-read.js +4 -0
  170. package/dist/jsx-runtime-strong.d.ts +33 -0
  171. package/dist/jsx-runtime.d.ts +58 -2
  172. package/dist/public-types.d.ts +15 -1
  173. package/dist/runtime.d.ts +297 -23
  174. package/dist/runtime.js +6405 -1679
  175. package/dist/runtime.server.d.ts +110 -27
  176. package/dist/runtime.server.js +1458 -213
  177. package/dist/server/early-signals.d.ts +14 -0
  178. package/dist/server/early-signals.js +34 -0
  179. package/dist/server/index.d.ts +11 -2
  180. package/dist/server/index.js +50 -1
  181. package/dist/server/rpc-batch.d.ts +17 -0
  182. package/dist/server/rpc-batch.js +129 -0
  183. package/dist/server/rpc-stream.d.ts +9 -0
  184. package/dist/server/rpc-stream.js +125 -0
  185. package/dist/server/rpc.d.ts +5 -1
  186. package/dist/server/rpc.js +37 -13
  187. package/dist/server/signal-query-observation.d.ts +3 -0
  188. package/dist/server/signal-query-observation.js +134 -0
  189. package/dist/server/streamed-region.d.ts +15 -0
  190. package/dist/server/streamed-region.js +30 -0
  191. package/dist/server/streamed-signals.d.ts +45 -0
  192. package/dist/server/streamed-signals.js +488 -0
  193. package/dist/server-call.d.ts +41 -0
  194. package/dist/server-call.js +75 -0
  195. package/dist/server-rpc-batch-client.d.ts +7 -0
  196. package/dist/server-rpc-batch-client.js +119 -0
  197. package/dist/server-rpc-client.d.ts +3 -1
  198. package/dist/server-rpc-client.js +99 -27
  199. package/dist/server-rpc-protocol.d.ts +47 -0
  200. package/dist/server-rpc-protocol.js +109 -0
  201. package/dist/server-rpc-stream-client.d.ts +5 -0
  202. package/dist/server-rpc-stream-client.js +98 -0
  203. package/dist/signals/actions.d.ts +12 -0
  204. package/dist/signals/actions.js +466 -0
  205. package/dist/signals/client.d.ts +2 -0
  206. package/dist/signals/client.js +1 -0
  207. package/dist/signals/computations.d.ts +37 -0
  208. package/dist/signals/computations.js +455 -0
  209. package/dist/signals/control-binding.d.ts +31 -0
  210. package/dist/signals/control-binding.js +353 -0
  211. package/dist/signals/derived-facade.d.ts +3 -0
  212. package/dist/signals/derived-facade.js +25 -0
  213. package/dist/signals/document-owner.d.ts +10 -0
  214. package/dist/signals/document-owner.js +43 -0
  215. package/dist/signals/early-values.d.ts +24 -0
  216. package/dist/signals/early-values.js +124 -0
  217. package/dist/signals/encoding.d.ts +1 -3
  218. package/dist/signals/encoding.js +3 -93
  219. package/dist/signals/engine.d.ts +53 -4
  220. package/dist/signals/engine.js +275 -16
  221. package/dist/signals/errors.d.ts +7 -0
  222. package/dist/signals/errors.js +16 -0
  223. package/dist/signals/facade.d.ts +74 -0
  224. package/dist/signals/facade.js +452 -0
  225. package/dist/signals/graph.d.ts +52 -6
  226. package/dist/signals/graph.js +174 -19
  227. package/dist/signals/handle-protocol.d.ts +4 -0
  228. package/dist/signals/handle-protocol.js +11 -0
  229. package/dist/signals/index.d.ts +12 -3
  230. package/dist/signals/index.js +71 -2
  231. package/dist/signals/native-read-client.d.ts +3 -0
  232. package/dist/signals/native-read-client.js +10 -0
  233. package/dist/signals/owner-context.d.ts +22 -0
  234. package/dist/signals/owner-context.js +77 -0
  235. package/dist/signals/query-attempt-observer.d.ts +49 -0
  236. package/dist/signals/query-attempt-observer.js +41 -0
  237. package/dist/signals/query-facade.d.ts +3 -0
  238. package/dist/signals/query-facade.js +49 -0
  239. package/dist/signals/read-protocol.d.ts +37 -2
  240. package/dist/signals/read-protocol.js +44 -0
  241. package/dist/signals/requests.d.ts +38 -5
  242. package/dist/signals/requests.js +298 -15
  243. package/dist/signals/scalar-computations.d.ts +2 -0
  244. package/dist/signals/scalar-computations.js +60 -0
  245. package/dist/signals/server.d.ts +2 -0
  246. package/dist/signals/server.js +1 -0
  247. package/dist/signals/transition-candidate.d.ts +71 -0
  248. package/dist/signals/transition-candidate.js +557 -0
  249. package/dist/signals/transition-state.d.ts +51 -0
  250. package/dist/signals/transition-state.js +70 -0
  251. package/dist/signals/types.d.ts +95 -5
  252. package/dist/signals/types.js +20 -0
  253. package/dist/stream-protocol.d.ts +0 -5
  254. package/dist/stream-protocol.js +3 -0
  255. package/dist/streamed-signals-protocol.d.ts +60 -0
  256. package/dist/streamed-signals-protocol.js +130 -0
  257. package/dist/style-values.d.ts +3 -0
  258. package/dist/style-values.js +96 -0
  259. package/dist/trusted-html.d.ts +17 -0
  260. package/dist/trusted-html.js +6 -0
  261. package/dist/universal-core.d.ts +6 -0
  262. package/dist/universal-core.js +214 -202
  263. package/dist/version.js +1 -1
  264. package/package.json +48 -5
@@ -0,0 +1,557 @@
1
+ import { ScopeDisposedError, SignalFrameError } from "./errors.js";
2
+ import {
3
+ NativeAdoptionMiss,
4
+ NATIVE_TRANSITION_CONSUMER,
5
+ SIGNAL_DEPENDENT_NODE
6
+ } from "./read-protocol.js";
7
+ import {
8
+ activeCandidate,
9
+ addCandidateWriter,
10
+ candidateGraph as bridge,
11
+ candidateWriters,
12
+ CandidateUnsupportedError,
13
+ removeCandidateWriter,
14
+ swapActiveSignalCandidate,
15
+ swapCandidateInvalidation,
16
+ withoutSignalCandidate
17
+ } from "./transition-state.js";
18
+ function isThenable(value) {
19
+ return value !== null && (typeof value === "object" || typeof value === "function") && typeof value.then === "function";
20
+ }
21
+ function clearPreparationWake(watch) {
22
+ watch.notify = void 0;
23
+ watch.validate = void 0;
24
+ watch.token = void 0;
25
+ }
26
+ function queuePreparationWake(watch, invalid = false) {
27
+ if (!watch.notify) return;
28
+ watch.invalid ||= invalid;
29
+ if (watch.queued) return;
30
+ watch.queued = true;
31
+ queueMicrotask(() => {
32
+ const notify = watch.notify;
33
+ if (!notify) return;
34
+ const token = watch.token;
35
+ const kind = watch.invalid || !watch.validate?.() ? "invalid" : "retry";
36
+ clearPreparationWake(watch);
37
+ notify({ kind, token });
38
+ });
39
+ }
40
+ function settlePreparationWake(watch) {
41
+ queuePreparationWake(watch);
42
+ }
43
+ function createNativeReadCandidateSource(candidate) {
44
+ let target = candidate;
45
+ let accepted = false;
46
+ let acceptedVersion = NaN;
47
+ let canonicalVersion = NaN;
48
+ const subscriptions = /* @__PURE__ */ new Set();
49
+ const source = {
50
+ getVersion: () => target === void 0 ? NaN : !accepted ? target.getVersion() : target.getVersion() === canonicalVersion ? acceptedVersion : NaN,
51
+ subscribe(notify) {
52
+ if (target === void 0) throw new TypeError("The native read candidate has retired.");
53
+ const subscription = { notify, dispose: target.subscribe(notify) };
54
+ subscriptions.add(subscription);
55
+ return () => {
56
+ if (subscriptions.delete(subscription)) subscription.dispose();
57
+ };
58
+ },
59
+ serialize(version) {
60
+ return source.getVersion() === version ? target?.serialize?.(accepted ? canonicalVersion : version) : void 0;
61
+ }
62
+ };
63
+ if (candidate.inspect) source.inspect = () => target.inspect();
64
+ return {
65
+ source,
66
+ accept(canonical, observedVersion) {
67
+ if (accepted || target === void 0 || target.getVersion() !== observedVersion) return false;
68
+ const version = canonical.getVersion();
69
+ const acquired = /* @__PURE__ */ new Map();
70
+ try {
71
+ for (const subscription of subscriptions)
72
+ acquired.set(subscription, canonical.subscribe(subscription.notify));
73
+ } catch (error) {
74
+ for (const dispose of acquired.values()) dispose();
75
+ throw error;
76
+ }
77
+ acceptedVersion = observedVersion;
78
+ canonicalVersion = version;
79
+ target = canonical;
80
+ accepted = true;
81
+ if (!canonical.inspect) delete source.inspect;
82
+ for (const [subscription, dispose] of acquired) {
83
+ subscription.dispose();
84
+ subscription.dispose = dispose;
85
+ }
86
+ return true;
87
+ },
88
+ discard() {
89
+ if (accepted || target === void 0) return;
90
+ target = void 0;
91
+ delete source.inspect;
92
+ for (const subscription of subscriptions) subscription.dispose();
93
+ subscriptions.clear();
94
+ }
95
+ };
96
+ }
97
+ class SignalCandidateFrame {
98
+ entries;
99
+ originals;
100
+ generation = 0;
101
+ active = true;
102
+ preparationToken;
103
+ preparationWatch;
104
+ demand;
105
+ run(callback) {
106
+ if (!this.active) throw new TypeError("The signal candidate has retired.");
107
+ if (activeCandidate && activeCandidate !== this) {
108
+ throw new CandidateUnsupportedError("Nested signal candidates are not supported.");
109
+ }
110
+ const previous = swapActiveSignalCandidate(this);
111
+ try {
112
+ return callback();
113
+ } finally {
114
+ swapActiveSignalCandidate(previous);
115
+ }
116
+ }
117
+ resolve(node) {
118
+ if (!this.active) throw new TypeError("The signal candidate has retired.");
119
+ const original = this.originals?.get(node);
120
+ if (original) {
121
+ this.demand?.add(original);
122
+ return node;
123
+ }
124
+ this.demand?.add(node);
125
+ const existing = this.entries?.get(node);
126
+ if (existing) return existing.target;
127
+ if (bridge.historical() || node.owner.readBarrier || !node.owner.forkCandidate) {
128
+ throw new CandidateUnsupportedError("Only live candidate-capable owners are supported.");
129
+ }
130
+ bridge.assertAlive(node.owner);
131
+ this.invalidatePreparation();
132
+ const target = new bridge.ScopedNode(node.owner, node.key, node.kind);
133
+ target.state = node.state;
134
+ target.last = node.last;
135
+ target.lastState = node.lastState;
136
+ target.hasLast = node.hasLast;
137
+ const entry = {
138
+ node,
139
+ target,
140
+ revision: node.revision,
141
+ epoch: node.owner.epoch,
142
+ stop: bridge.attachObserver(
143
+ node,
144
+ () => {
145
+ if (node.owner.retired) this.discard();
146
+ else this.invalidatePreparation();
147
+ },
148
+ true
149
+ )
150
+ };
151
+ (this.entries ??= /* @__PURE__ */ new Map()).set(node, entry);
152
+ (this.originals ??= /* @__PURE__ */ new Map()).set(target, node);
153
+ try {
154
+ entry.producer = node.owner.forkCandidate(node, target, this);
155
+ const compute = target.compute;
156
+ if (compute) target.compute = () => this.run(() => compute(target));
157
+ } catch (error) {
158
+ this.discard();
159
+ throw error;
160
+ }
161
+ return target;
162
+ }
163
+ write(node, value) {
164
+ if (!this.validate()) throw new TypeError("Rebase the signal candidate before writing.");
165
+ const target = this.resolve(node);
166
+ const current = bridge.strictValue(bridge.refreshNode(target), node.key);
167
+ let previous = current;
168
+ const writers = candidateWriters?.get(node);
169
+ if (typeof value === "function")
170
+ for (const writer of writers ?? []) {
171
+ if (writer === this) continue;
172
+ const pending = writer.entries.get(node);
173
+ previous = bridge.strictValue(bridge.refreshNode(pending.target), node.key);
174
+ for (const operation of pending.urgentOperations ?? [])
175
+ previous = bridge.untrack(() => bridge.pure(() => operation(previous)));
176
+ }
177
+ const next = typeof value === "function" ? bridge.untrack(() => bridge.pure(() => value(previous))) : value;
178
+ let releases;
179
+ for (const writer of writers ?? []) {
180
+ if (writer === this) continue;
181
+ const release = writer.replaceWrite(node);
182
+ if (release) (releases ??= []).push(release);
183
+ }
184
+ try {
185
+ if (Object.is(current, next)) return;
186
+ const entry = this.entries.get(node);
187
+ if (!entry.written) {
188
+ addCandidateWriter(node, this);
189
+ entry.written = true;
190
+ }
191
+ this.generation++;
192
+ this.invalidatePreparation();
193
+ bridge.signalBatch(() => bridge.publishNode(target, bridge.readyState(next)));
194
+ } finally {
195
+ if (releases)
196
+ this.committed(() => {
197
+ for (const release of releases) release();
198
+ });
199
+ }
200
+ }
201
+ /** Only observed candidates allocate forwarding sources; ordinary reads keep their identity. */
202
+ nativeSource(target, read, source) {
203
+ const entry = this.entries.get(this.originals.get(target));
204
+ const sources = entry.sources ??= {};
205
+ return (sources[read] ??= createNativeReadCandidateSource(source)).source;
206
+ }
207
+ validate() {
208
+ if (!this.active) return false;
209
+ for (const entry of this.entries?.values() ?? []) {
210
+ if (entry.withdrawn || entry.urgentOperations !== void 0 || entry.node.owner.retired || entry.epoch !== entry.node.owner.epoch || entry.node.owner.readBarrier || entry.node.kind !== "derived" && entry.revision !== entry.node.revision)
211
+ return false;
212
+ }
213
+ return true;
214
+ }
215
+ hasWrites() {
216
+ for (const entry of this.entries?.values() ?? []) if (entry.written) return true;
217
+ return false;
218
+ }
219
+ /** Follow existing graph edges, never the document or a global consumer registry. */
220
+ consumers() {
221
+ const consumers = /* @__PURE__ */ new Set();
222
+ const visited = /* @__PURE__ */ new Set();
223
+ for (const entry of this.entries?.values() ?? []) if (entry.written) visited.add(entry.node);
224
+ for (const node of visited) {
225
+ for (let link = node.subs; link; link = link.nextSub) {
226
+ const subscriber = link.sub;
227
+ if (subscriber instanceof bridge.ScopedNode) visited.add(subscriber);
228
+ else {
229
+ const notify = subscriber.notify;
230
+ const dependent = notify?.[SIGNAL_DEPENDENT_NODE];
231
+ if (dependent) visited.add(dependent);
232
+ const consumer = notify?.[NATIVE_TRANSITION_CONSUMER];
233
+ if (consumer?.active()) consumers.add(consumer);
234
+ }
235
+ }
236
+ }
237
+ return [...consumers];
238
+ }
239
+ /** Canonical memo witnesses cannot justify skipping a private forward read. */
240
+ observes(node) {
241
+ return this.originals?.has(node) ?? false;
242
+ }
243
+ canonical(node) {
244
+ return this.originals?.get(node) ?? node;
245
+ }
246
+ /** Record intent without evaluating the forward graph in an urgent setter. */
247
+ recordUrgentWrite(node, value) {
248
+ if (typeof value !== "function") return this.replaceWrite(node);
249
+ const entry = this.entries?.get(node);
250
+ if (!this.active || !entry?.written) return;
251
+ this.generation++;
252
+ this.invalidatePreparation();
253
+ (entry.urgentOperations ??= []).push(value);
254
+ }
255
+ replaceWrite(node) {
256
+ const entry = this.entries?.get(node);
257
+ if (!this.active || !entry?.written) return;
258
+ this.generation++;
259
+ this.invalidatePreparation();
260
+ entry.withdrawn = true;
261
+ entry.urgentOperations = void 0;
262
+ this.forgetWrite(entry);
263
+ if (this.hasWrites()) return;
264
+ this.active = false;
265
+ return () => this.release();
266
+ }
267
+ forgetWrite(entry) {
268
+ if (!entry.written) return;
269
+ entry.written = false;
270
+ removeCandidateWriter(entry.node, this);
271
+ }
272
+ /** Urgent writes win their own cells without restarting unaffected producer leases. */
273
+ rebase() {
274
+ if (!this.active) throw new TypeError("The signal candidate has retired.");
275
+ const hadWrites = this.hasWrites();
276
+ this.generation++;
277
+ this.invalidatePreparation();
278
+ bridge.signalBatch(() => {
279
+ for (const entry of this.entries?.values() ?? []) {
280
+ bridge.assertAlive(entry.node.owner);
281
+ if (entry.epoch !== entry.node.owner.epoch || entry.node.owner.readBarrier) {
282
+ throw new TypeError("The candidate owner changed lifetime.");
283
+ }
284
+ const operations = entry.urgentOperations;
285
+ if (!entry.withdrawn && !operations && entry.revision === entry.node.revision) continue;
286
+ entry.revision = entry.node.revision;
287
+ if (entry.node.kind === "signal") {
288
+ let state = entry.node.state;
289
+ if (operations) {
290
+ let value = bridge.strictValue(bridge.refreshNode(entry.target), entry.node.key);
291
+ for (const operation of operations)
292
+ value = this.run(() => bridge.untrack(() => bridge.pure(() => operation(value))));
293
+ if (!Object.is(value, bridge.strictValue(state, entry.node.key)))
294
+ state = bridge.readyState(value);
295
+ else this.forgetWrite(entry);
296
+ } else this.forgetWrite(entry);
297
+ entry.withdrawn = false;
298
+ entry.urgentOperations = void 0;
299
+ bridge.publishNode(entry.target, state);
300
+ }
301
+ }
302
+ });
303
+ if (hadWrites && !this.hasWrites()) this.discard();
304
+ }
305
+ prepare(reads = []) {
306
+ if (this.preparationWatch) clearPreparationWake(this.preparationWatch);
307
+ this.preparationWatch = void 0;
308
+ this.preparationToken = void 0;
309
+ const token = {};
310
+ if (!this.validate()) return { status: "invalid", token, reason: "stale" };
311
+ const generation = this.generation;
312
+ let waiting;
313
+ let failed = false;
314
+ let failure;
315
+ let invalid;
316
+ const observeFailure = (error, handled = false) => {
317
+ if (error instanceof CandidateUnsupportedError) invalid = "unsupported";
318
+ else if (error instanceof ScopeDisposedError || error instanceof SignalFrameError || error instanceof NativeAdoptionMiss)
319
+ invalid ??= "stale";
320
+ else if (!handled) {
321
+ try {
322
+ if (isThenable(error)) {
323
+ (waiting ??= /* @__PURE__ */ new Set()).add(error);
324
+ return;
325
+ }
326
+ } catch (inspectionError) {
327
+ error = inspectionError;
328
+ }
329
+ if (!failed) failure = error;
330
+ failed = true;
331
+ }
332
+ };
333
+ const demand = reads.length ? /* @__PURE__ */ new Set() : void 0;
334
+ this.demand = demand;
335
+ for (const read of reads) {
336
+ try {
337
+ this.run(() => bridge.pure(read));
338
+ } catch (error) {
339
+ observeFailure(error);
340
+ }
341
+ }
342
+ this.demand = void 0;
343
+ if (demand) this.pruneDemand(demand);
344
+ const prepared = [];
345
+ for (const entry of this.entries?.values() ?? []) {
346
+ let state;
347
+ let producer;
348
+ try {
349
+ state = this.run(() => bridge.refreshNode(entry.target));
350
+ const outcome = entry.producer?.prepare();
351
+ if (outcome?.status === "pending") (waiting ??= /* @__PURE__ */ new Set()).add(outcome.waiting);
352
+ else if (outcome?.status === "error") observeFailure(outcome.error);
353
+ else if (outcome?.status === "invalid") invalid ??= "unsupported";
354
+ else if (outcome?.status === "ready") producer = outcome.receipt;
355
+ } catch (error) {
356
+ observeFailure(error);
357
+ continue;
358
+ }
359
+ if (state.snapshot.status === "pending" && state.waiting)
360
+ (waiting ??= /* @__PURE__ */ new Set()).add(state.waiting);
361
+ else if (state.snapshot.status === "error")
362
+ observeFailure(state.snapshot.error, reads.length > 0 || producer !== void 0);
363
+ else if (state.snapshot.status === "idle" && !producer) invalid ??= "unsupported";
364
+ prepared.push({
365
+ entry,
366
+ state,
367
+ last: entry.target.last,
368
+ lastState: entry.target.lastState,
369
+ hasLast: entry.target.hasLast,
370
+ revision: entry.target.revision,
371
+ producer
372
+ });
373
+ }
374
+ if (invalid) return { status: "invalid", token, reason: invalid };
375
+ if (!this.validate() || generation !== this.generation)
376
+ return { status: "invalid", token, reason: "stale" };
377
+ this.preparationToken = token;
378
+ if (failed) return { status: "error", token, error: failure };
379
+ if (waiting?.size) return { status: "pending", token, wakeables: [...waiting] };
380
+ return {
381
+ status: "ready",
382
+ token,
383
+ receipt: {
384
+ publish: (accepted) => {
385
+ if (!this.validate() || token !== this.preparationToken || generation !== this.generation || prepared.some(({ entry, state, last, lastState, hasLast, revision, producer }) => {
386
+ if (entry.target.revision !== revision || !Object.is(entry.target.last, last) || entry.target.lastState !== lastState || entry.target.hasLast !== hasLast)
387
+ return true;
388
+ if (state.owners) {
389
+ for (const owner of state.owners) if (owner.retired) return true;
390
+ }
391
+ if (lastState?.owners && lastState.owners !== state.owners) {
392
+ for (const owner of lastState.owners) if (owner.retired) return true;
393
+ }
394
+ return producer !== void 0 && !producer.validate();
395
+ }))
396
+ return false;
397
+ const releases = [];
398
+ const changed = [];
399
+ this.committed(
400
+ () => bridge.signalBatch(() => {
401
+ for (const { entry } of prepared) entry.node.flags |= bridge.flags.Dirty;
402
+ for (const { entry, state, last, lastState, hasLast, producer } of prepared) {
403
+ const { node, target } = entry;
404
+ if (producer) releases.push(producer.publish());
405
+ while (node.deps) bridge.graph.unlink(node.deps, node);
406
+ for (let link = target.deps; link; link = link.nextDep) {
407
+ const original = this.originals.get(link.dep);
408
+ if (!original) throw new TypeError("Candidate dependency escaped its frame.");
409
+ bridge.link(original, node);
410
+ }
411
+ const currentChanged = node.state?.snapshot !== state.snapshot || !bridge.sameState(node.state, state);
412
+ const retainedChanged = node.hasLast !== hasLast || !Object.is(node.last, last) || (lastState ? !bridge.sameState(node.lastState, lastState) : node.lastState !== void 0);
413
+ if (currentChanged) bridge.commitState(node, state);
414
+ if (node.lastState?.owners !== lastState?.owners)
415
+ bridge.releaseRetainedOwners(node);
416
+ node.last = last;
417
+ node.lastState = lastState;
418
+ node.hasLast = hasLast;
419
+ if (state.snapshot.status !== "ready") bridge.retainOwners(node);
420
+ if (!currentChanged && retainedChanged) node.revision++;
421
+ if (currentChanged || retainedChanged) changed.push(node);
422
+ }
423
+ this.active = false;
424
+ const previous = swapCandidateInvalidation(true);
425
+ try {
426
+ for (const node of changed) {
427
+ if (node.subs) {
428
+ bridge.graph.propagate(node.subs, false);
429
+ bridge.graph.shallowPropagate(node.subs);
430
+ }
431
+ }
432
+ } finally {
433
+ swapCandidateInvalidation(previous);
434
+ }
435
+ for (const { entry } of prepared) {
436
+ entry.node.flags = bridge.flags.Mutable | bridge.flags.Watching;
437
+ }
438
+ try {
439
+ for (const { producer } of prepared) producer?.accept?.();
440
+ for (const { entry, revision } of prepared) {
441
+ if (!entry.sources) continue;
442
+ for (const read of ["value", "latest", "snapshot"]) {
443
+ const source = entry.sources[read];
444
+ if (!source) continue;
445
+ const field = read === "value" ? "nativeSource" : read === "latest" ? "nativeLatestSource" : "nativeSnapshotSource";
446
+ source.accept(
447
+ entry.node[field] ??= bridge.createNativeSource(entry.node, read),
448
+ revision
449
+ );
450
+ }
451
+ }
452
+ accepted?.();
453
+ } finally {
454
+ this.release();
455
+ for (const release of releases) release();
456
+ }
457
+ })
458
+ );
459
+ return true;
460
+ }
461
+ }
462
+ };
463
+ }
464
+ watchPreparation(outcome, notify) {
465
+ if (this.preparationWatch) clearPreparationWake(this.preparationWatch);
466
+ this.preparationWatch = void 0;
467
+ if (outcome.status !== "pending") return () => {
468
+ };
469
+ const token = outcome.token;
470
+ const watch = {
471
+ notify,
472
+ token,
473
+ queued: false,
474
+ invalid: false,
475
+ validate: () => token === this.preparationToken && this.validate()
476
+ };
477
+ this.preparationWatch = watch;
478
+ if (!watch.validate()) queuePreparationWake(watch, true);
479
+ else
480
+ for (const waiting of outcome.wakeables) {
481
+ const settled = settlePreparationWake.bind(null, watch);
482
+ Promise.resolve(waiting).then(settled, settled);
483
+ }
484
+ return () => clearPreparationWake(watch);
485
+ }
486
+ invalidatePreparation() {
487
+ this.preparationToken = void 0;
488
+ const watch = this.preparationWatch;
489
+ this.preparationWatch = void 0;
490
+ if (watch) {
491
+ watch.validate = void 0;
492
+ queuePreparationWake(watch, true);
493
+ }
494
+ }
495
+ pruneDemand(demand) {
496
+ for (const entry of this.entries?.values() ?? []) if (entry.written) demand.add(entry.node);
497
+ for (const node of demand) {
498
+ const entry = this.entries?.get(node);
499
+ for (let link = entry?.target.deps; link; link = link.nextDep) {
500
+ const original = this.originals?.get(link.dep);
501
+ if (original) demand.add(original);
502
+ }
503
+ for (const dependency of entry?.producer?.dependencies?.() ?? []) {
504
+ demand.add(this.canonical(dependency));
505
+ }
506
+ }
507
+ let removed;
508
+ for (const entry of this.entries?.values() ?? []) {
509
+ if (demand.has(entry.node)) continue;
510
+ this.entries.delete(entry.node);
511
+ this.originals.delete(entry.target);
512
+ this.releaseEntry(entry);
513
+ (removed ??= []).push(entry);
514
+ }
515
+ if (removed)
516
+ this.committed(() => {
517
+ for (const entry of removed) entry.producer?.dispose();
518
+ });
519
+ }
520
+ discard() {
521
+ if (!this.active) return;
522
+ this.active = false;
523
+ this.generation++;
524
+ this.invalidatePreparation();
525
+ this.release();
526
+ }
527
+ release() {
528
+ if (this.preparationWatch) clearPreparationWake(this.preparationWatch);
529
+ this.preparationWatch = void 0;
530
+ this.preparationToken = void 0;
531
+ const entries = this.entries;
532
+ this.entries = void 0;
533
+ this.originals = void 0;
534
+ this.demand = void 0;
535
+ for (const entry of entries?.values() ?? []) this.releaseEntry(entry);
536
+ this.committed(() => {
537
+ for (const entry of entries?.values() ?? []) entry.producer?.dispose();
538
+ });
539
+ }
540
+ releaseEntry(entry) {
541
+ if (entry.sources) for (const source of Object.values(entry.sources)) source.discard();
542
+ this.forgetWrite(entry);
543
+ entry.urgentOperations = void 0;
544
+ entry.stop();
545
+ bridge.removeQueued(entry.target);
546
+ while (entry.target.deps) bridge.graph.unlink(entry.target.deps, entry.target);
547
+ while (entry.target.subs) bridge.graph.unlink(entry.target.subs);
548
+ bridge.releaseRetention(entry.target);
549
+ entry.target.compute = void 0;
550
+ }
551
+ committed(callback) {
552
+ return withoutSignalCandidate(callback);
553
+ }
554
+ }
555
+ export {
556
+ SignalCandidateFrame
557
+ };
@@ -0,0 +1,51 @@
1
+ import type { createReactiveSystem } from 'alien-signals/system';
2
+ import type { GraphOwner, ScopedNode, NodeState, SignalReadMode } from './graph.js';
3
+ import type { SignalCandidateFrame } from './transition-candidate.js';
4
+ import { type NativeReadSource } from './read-protocol.js';
5
+ /** Shared state must not import the candidate implementation: eager signal chunks
6
+ * can otherwise absorb the full class used only by a later renderer chunk. */
7
+ interface CandidateGraph {
8
+ ScopedNode: typeof import('./graph.js').ScopedNode;
9
+ graph: ReturnType<typeof createReactiveSystem>;
10
+ flags: {
11
+ Dirty: number;
12
+ Mutable: number;
13
+ Watching: number;
14
+ };
15
+ historical(): boolean;
16
+ link(from: ScopedNode, to: ScopedNode): void;
17
+ removeQueued(node: ScopedNode): void;
18
+ assertAlive(owner: GraphOwner): void;
19
+ strictValue<T>(state: NodeState<T>, key?: string): T;
20
+ refreshNode<T>(node: ScopedNode<T>): NodeState<T>;
21
+ pure<T>(read: () => T): T;
22
+ untrack<T>(read: () => T): T;
23
+ signalBatch<T>(read: () => T): T;
24
+ publishNode<T>(node: ScopedNode<T>, state: NodeState<T>): void;
25
+ readyState<T>(value: T): NodeState<T>;
26
+ commitState(node: ScopedNode, state: NodeState): void;
27
+ sameState(a: NodeState | undefined, b: NodeState): boolean;
28
+ releaseRetainedOwners(node: ScopedNode): void;
29
+ retainOwners(node: ScopedNode): void;
30
+ releaseRetention(node: ScopedNode): void;
31
+ createNativeSource(node: ScopedNode, mode: SignalReadMode): NativeReadSource;
32
+ attachObserver(node: ScopedNode, notify: () => void, native: boolean): () => void;
33
+ }
34
+ /** The graph registers primitives, never a candidate factory. */
35
+ export declare let candidateGraph: CandidateGraph;
36
+ export declare function registerCandidateGraph(graph: CandidateGraph): void;
37
+ export declare function withoutSignalCandidate<T>(callback: () => T): T;
38
+ /** Internal capability refusal, distinct from an authored TypeError. */
39
+ export declare class CandidateUnsupportedError extends TypeError {
40
+ }
41
+ export declare let activeCandidate: SignalCandidateFrame | undefined;
42
+ /** Synchronous action scope; never keep a candidate active across an await. */
43
+ export declare function swapActiveSignalCandidate(frame: SignalCandidateFrame | undefined): SignalCandidateFrame | undefined;
44
+ export declare let deferCandidateInvalidation: boolean;
45
+ export declare function swapCandidateInvalidation(defer: boolean): boolean;
46
+ export declare let candidateWriteCount: number;
47
+ export declare let candidateWriters: WeakMap<ScopedNode, Set<SignalCandidateFrame>> | undefined;
48
+ export declare function addCandidateWriter(node: ScopedNode, frame: SignalCandidateFrame): void;
49
+ export declare function removeCandidateWriter(node: ScopedNode, frame: SignalCandidateFrame): void;
50
+ export declare function recordCandidateUrgentWrite(node: ScopedNode, value: unknown): (() => void)[] | undefined;
51
+ export {};
@@ -0,0 +1,70 @@
1
+ import { setNativeCandidateResolver } from "./read-protocol.js";
2
+ let candidateGraph;
3
+ function registerCandidateGraph(graph) {
4
+ candidateGraph = graph;
5
+ }
6
+ function withoutSignalCandidate(callback) {
7
+ const previous = activeCandidate;
8
+ const resolver = setNativeCandidateResolver(null);
9
+ activeCandidate = void 0;
10
+ try {
11
+ return callback();
12
+ } finally {
13
+ activeCandidate = previous;
14
+ setNativeCandidateResolver(resolver);
15
+ }
16
+ }
17
+ class CandidateUnsupportedError extends TypeError {
18
+ }
19
+ let activeCandidate;
20
+ function swapActiveSignalCandidate(frame) {
21
+ const previous = activeCandidate;
22
+ activeCandidate = frame;
23
+ return previous;
24
+ }
25
+ let deferCandidateInvalidation = false;
26
+ function swapCandidateInvalidation(defer) {
27
+ const previous = deferCandidateInvalidation;
28
+ deferCandidateInvalidation = defer;
29
+ return previous;
30
+ }
31
+ let candidateWriteCount = 0;
32
+ let candidateWriters;
33
+ function addCandidateWriter(node, frame) {
34
+ candidateWriteCount++;
35
+ candidateWriters ??= /* @__PURE__ */ new WeakMap();
36
+ let writers = candidateWriters.get(node);
37
+ if (!writers) candidateWriters.set(node, writers = /* @__PURE__ */ new Set());
38
+ writers.add(frame);
39
+ }
40
+ function removeCandidateWriter(node, frame) {
41
+ const writers = candidateWriters.get(node);
42
+ writers.delete(frame);
43
+ if (writers.size === 0) candidateWriters.delete(node);
44
+ if (--candidateWriteCount === 0) candidateWriters = void 0;
45
+ }
46
+ function recordCandidateUrgentWrite(node, value) {
47
+ let releases;
48
+ const writers = candidateWriters?.get(node);
49
+ if (writers)
50
+ for (const frame of writers) {
51
+ const release = frame.recordUrgentWrite(node, value);
52
+ if (release) (releases ??= []).push(release);
53
+ }
54
+ return releases;
55
+ }
56
+ export {
57
+ CandidateUnsupportedError,
58
+ activeCandidate,
59
+ addCandidateWriter,
60
+ candidateGraph,
61
+ candidateWriteCount,
62
+ candidateWriters,
63
+ deferCandidateInvalidation,
64
+ recordCandidateUrgentWrite,
65
+ registerCandidateGraph,
66
+ removeCandidateWriter,
67
+ swapActiveSignalCandidate,
68
+ swapCandidateInvalidation,
69
+ withoutSignalCandidate
70
+ };