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
@@ -1,10 +1,15 @@
1
1
  import { decodeSignalValue, encodeSignalValue } from "./encoding.js";
2
+ import { createResourceCellWith } from "./engine.js";
3
+ import { SignalStreamError } from "./errors.js";
2
4
  import {
3
5
  ScopedNode,
6
+ CandidateUnsupportedError,
4
7
  assertAlive,
5
8
  assertWritable,
6
9
  errorState,
10
+ idleState,
7
11
  invalidateNode,
12
+ isThenable,
8
13
  pendingState,
9
14
  publishNode,
10
15
  pure,
@@ -12,8 +17,18 @@ import {
12
17
  refreshNode,
13
18
  releaseRetention,
14
19
  signalBatch,
15
- untrack
20
+ untrack,
21
+ untrackCommitted
16
22
  } from "./graph.js";
23
+ import {
24
+ QUERY_REQUEST,
25
+ skip
26
+ } from "./types.js";
27
+ import {
28
+ captureCurrentServerSignalQueryAttemptObserver,
29
+ hasServerSignalQueryAttemptObserver,
30
+ serverSignalQueryAttemptObserver
31
+ } from "./query-attempt-observer.js";
17
32
  class Request {
18
33
  constructor(definition, argument) {
19
34
  this.definition = definition;
@@ -24,6 +39,9 @@ class Request {
24
39
  Object.freeze(this);
25
40
  }
26
41
  definition;
42
+ get [QUERY_REQUEST]() {
43
+ return void 0;
44
+ }
27
45
  queryKey;
28
46
  identity;
29
47
  argument;
@@ -42,16 +60,24 @@ function query(key, load, options) {
42
60
  });
43
61
  return Object.freeze(describe);
44
62
  }
45
- function makeAttempt(entry) {
63
+ function createResource(owner, key, describe) {
64
+ return createResourceCellWith(owner, key, describe, initializeResource, true);
65
+ }
66
+ function makeAttempt(entry, generation, streamed = false) {
46
67
  let resolve;
47
68
  const settled = new Promise((done) => {
48
69
  resolve = done;
49
70
  });
50
71
  return {
51
72
  entry,
52
- controller: new AbortController(),
73
+ controller: streamed ? void 0 : new AbortController(),
53
74
  iterator: void 0,
54
75
  settled,
76
+ generation,
77
+ streamed,
78
+ result: void 0,
79
+ observations: void 0,
80
+ sequence: 0,
55
81
  resolve,
56
82
  hasYielded: false
57
83
  };
@@ -75,13 +101,33 @@ class RequestEntry {
75
101
  get active() {
76
102
  return this.attempt !== void 0;
77
103
  }
104
+ get attemptGeneration() {
105
+ return this.generation;
106
+ }
107
+ observe(nodeKey) {
108
+ const attempt = this.attempt;
109
+ if (!attempt || attempt.streamed) return;
110
+ const context = serverSignalQueryAttemptObserver(this.owner.scopeKey);
111
+ if (context === void 0) return;
112
+ const observations = attempt.observations ??= context.createObservations();
113
+ observations.observe(context, {
114
+ nodeKey,
115
+ selectionKey: this.request.identity,
116
+ attempt: attempt.generation,
117
+ kind: this.request.definition.kind,
118
+ result: attempt.result,
119
+ isCurrent: () => currentEntry(attempt) === this
120
+ });
121
+ return attempt;
122
+ }
78
123
  start(pending) {
124
+ if (this.owner.readBarrier !== void 0) return;
79
125
  const generation = ++this.generation;
80
126
  this.stopAttempt();
81
- if (generation !== this.generation || this.owner.retired || this.owner.requests.get(this.request.identity) !== this || !this.consumers.size)
127
+ if (generation !== this.generation || this.owner.readBarrier !== void 0 || this.owner.retired || this.owner.requests.get(this.request.identity) !== this || !this.consumers.size)
82
128
  return;
83
129
  const previous = this.state.snapshot;
84
- const attempt = this.attempt = makeAttempt(this);
130
+ const attempt = this.attempt = makeAttempt(this, generation);
85
131
  const connection = this.request.definition.kind === "stream" ? "connecting" : "none";
86
132
  this.state = !pending && previous.status === "ready" ? readyState(previous.value, {
87
133
  refreshing: true,
@@ -95,7 +141,8 @@ class RequestEntry {
95
141
  result = untrack(
96
142
  () => signalBatch(
97
143
  () => this.request.definition.load(this.request.argument, {
98
- signal: attempt.controller.signal
144
+ signal: attempt.controller.signal,
145
+ ...previous.status === "ready" ? { previous: previous.value } : {}
99
146
  })
100
147
  )
101
148
  );
@@ -104,7 +151,73 @@ class RequestEntry {
104
151
  return;
105
152
  }
106
153
  if (this.request.definition.kind === "stream") observeStream(attempt, result);
107
- else observePromise(attempt, result);
154
+ else {
155
+ if (currentEntry(attempt)) attempt.result = result;
156
+ observePromise(attempt, result);
157
+ }
158
+ }
159
+ startStreamed(identity) {
160
+ if (this.owner.readBarrier !== void 0 || identity.selectionKey !== this.request.identity || identity.attempt < this.generation || this.attempt === void 0 && identity.attempt === this.generation && this.state.snapshot.status !== "pending" || this.owner.retired || !this.consumers.size) {
161
+ return false;
162
+ }
163
+ if (this.attempt?.streamed && this.attempt.generation === identity.attempt) {
164
+ return true;
165
+ }
166
+ this.generation = identity.attempt;
167
+ this.stopAttempt();
168
+ if (this.generation !== identity.attempt || this.owner.readBarrier !== void 0 || this.owner.retired || this.owner.requests.get(this.request.identity) !== this || !this.consumers.size) {
169
+ return false;
170
+ }
171
+ this.attempt = makeAttempt(this, identity.attempt, true);
172
+ return true;
173
+ }
174
+ acceptStreamed(frame) {
175
+ const attempt = this.attempt;
176
+ if (!attempt?.streamed || attempt.generation !== frame.identity.attempt || frame.sequence !== attempt.sequence || currentEntry(attempt) !== this) {
177
+ return false;
178
+ }
179
+ attempt.sequence++;
180
+ signalBatch(() => {
181
+ if (frame.kind === "open") return;
182
+ if (frame.kind === "value") {
183
+ attempt.hasYielded = true;
184
+ this.state = readyState(decodeSignalValue(frame.value), {
185
+ requestKey: this.request.identity,
186
+ connection: this.request.definition.kind === "stream" ? "open" : "none",
187
+ complete: false
188
+ });
189
+ this.deliver();
190
+ return;
191
+ }
192
+ if (frame.kind === "error") {
193
+ this.state = errorState(
194
+ new SignalStreamError(frame.code),
195
+ this.request.definition.kind === "stream" ? "closed" : "none",
196
+ this.request.identity
197
+ );
198
+ completeAttempt(attempt);
199
+ this.deliver();
200
+ return;
201
+ }
202
+ if (!attempt.hasYielded || this.state.snapshot.status !== "ready") {
203
+ this.state = errorState(
204
+ new Error("The streamed signal completed without yielding a value."),
205
+ this.request.definition.kind === "stream" ? "closed" : "none",
206
+ this.request.identity
207
+ );
208
+ completeAttempt(attempt);
209
+ this.deliver();
210
+ return;
211
+ }
212
+ this.state = readyState(this.state.snapshot.value, {
213
+ requestKey: this.request.identity,
214
+ connection: this.request.definition.kind === "stream" ? "closed" : "none",
215
+ complete: true
216
+ });
217
+ completeAttempt(attempt);
218
+ this.deliver();
219
+ });
220
+ return true;
108
221
  }
109
222
  deliver() {
110
223
  for (const consumer of this.consumers) consumer.deliver();
@@ -118,8 +231,10 @@ class RequestEntry {
118
231
  const iterator = attempt.iterator;
119
232
  attempt.controller = void 0;
120
233
  attempt.iterator = void 0;
234
+ attempt.result = void 0;
235
+ attempt.observations?.retire();
121
236
  attempt.resolve();
122
- if (controller) untrack(() => signalBatch(() => controller.abort()));
237
+ if (controller) untrackCommitted(() => signalBatch(() => controller.abort()));
123
238
  if (iterator) closeIterator(iterator);
124
239
  }
125
240
  remove(consumer) {
@@ -136,11 +251,13 @@ function currentEntry(attempt) {
136
251
  return entry && !entry.owner.retired && entry.attempt === attempt ? entry : void 0;
137
252
  }
138
253
  function completeAttempt(attempt) {
254
+ attempt.observations?.complete();
139
255
  const entry = attempt.entry;
140
256
  if (entry?.attempt === attempt) entry.attempt = void 0;
141
257
  attempt.entry = void 0;
142
258
  attempt.controller = void 0;
143
259
  attempt.iterator = void 0;
260
+ attempt.result = void 0;
144
261
  attempt.resolve();
145
262
  }
146
263
  function observePromise(attempt, result) {
@@ -160,6 +277,7 @@ function observePromise(attempt, result) {
160
277
  function failAttempt(attempt, error) {
161
278
  const entry = currentEntry(attempt);
162
279
  if (!entry) return;
280
+ attempt.observations?.fail(error);
163
281
  signalBatch(() => {
164
282
  const iterator = attempt.iterator;
165
283
  entry.state = errorState(
@@ -176,7 +294,7 @@ function ignoreRetiredCloseFailure() {
176
294
  }
177
295
  function closeIterator(iterator) {
178
296
  try {
179
- const result = untrack(() => signalBatch(() => iterator.return?.()));
297
+ const result = untrackCommitted(() => signalBatch(() => iterator.return?.()));
180
298
  Promise.resolve(result).catch(ignoreRetiredCloseFailure);
181
299
  } catch {
182
300
  }
@@ -272,7 +390,9 @@ function receiveStreamStep(attempt, result) {
272
390
  });
273
391
  accepted.deliver();
274
392
  });
275
- nextStreamStep(attempt);
393
+ const observed = attempt.observations?.publish(value);
394
+ if (observed === void 0) nextStreamStep(attempt);
395
+ else observed.then(() => nextStreamStep(attempt));
276
396
  }
277
397
  class ResourceBinding {
278
398
  constructor(owner, node, describe, seed, retained = seed) {
@@ -286,7 +406,11 @@ class ResourceBinding {
286
406
  kind: identity.kind,
287
407
  argument: decodeSignalValue(identity.argument)
288
408
  } : void 0;
289
- node.compute = () => this.compute();
409
+ node.compute = () => {
410
+ const pending = this.pendingObserver;
411
+ this.pendingObserver = void 0;
412
+ return pending === void 0 || hasServerSignalQueryAttemptObserver(this.owner.scopeKey) ? this.compute() : pending(() => this.compute());
413
+ };
290
414
  node.retry = (options) => this.retry(options);
291
415
  }
292
416
  owner;
@@ -297,18 +421,92 @@ class ResourceBinding {
297
421
  retainedRequest;
298
422
  seeded;
299
423
  describedAttempt;
424
+ observedAttempt;
425
+ pendingObserver;
300
426
  pendingPromise;
301
427
  resolvePending;
428
+ streamedSelection;
429
+ selectionAuthority = {};
302
430
  request() {
303
431
  const request = pure(() => this.describe());
432
+ if (request === skip) return request;
304
433
  if (!(request instanceof Request))
305
- throw new TypeError("asyncSignal$ must describe a query request.");
434
+ throw new TypeError("A resource must describe a query request.");
306
435
  return request;
307
436
  }
437
+ forkCandidate(target) {
438
+ if (this.streamedSelection || this.owner.streamedSelections?.has(this.node.key)) {
439
+ throw new CandidateUnsupportedError(
440
+ "Streamed candidates are not supported by this prototype."
441
+ );
442
+ }
443
+ const fork = new ResourceBinding(this.owner, target, this.describe);
444
+ fork.candidate = true;
445
+ fork.retainedRequest = this.retainedRequest;
446
+ return {
447
+ dispose: () => fork.dispose(),
448
+ prepare: () => {
449
+ if (this.streamedSelection || this.owner.streamedSelections?.has(this.node.key))
450
+ return { status: "invalid" };
451
+ const entry = fork.selected;
452
+ const attempt = entry?.attempt;
453
+ if (entry) {
454
+ if (attempt?.streamed) return { status: "invalid" };
455
+ const snapshot = entry.state.snapshot;
456
+ if (attempt && (entry.request.definition.kind !== "stream" || snapshot.status !== "ready"))
457
+ return { status: "pending", waiting: target.state?.waiting ?? attempt.settled };
458
+ if (!(snapshot.status === "ready" && (snapshot.complete || entry.request.definition.kind === "stream") || snapshot.status === "error"))
459
+ return { status: "invalid" };
460
+ } else if (target.state?.snapshot.status !== "idle") {
461
+ const state2 = target.state;
462
+ if (state2?.snapshot.status === "error")
463
+ return { status: "error", error: state2.snapshot.error };
464
+ if (state2?.waiting) return { status: "pending", waiting: state2.waiting };
465
+ return { status: "invalid" };
466
+ }
467
+ const state = entry?.state;
468
+ const authority = this.selectionAuthority;
469
+ const forkAuthority = fork.selectionAuthority;
470
+ return {
471
+ status: "ready",
472
+ receipt: {
473
+ validate: () => !this.streamedSelection && !this.owner.streamedSelections?.has(this.node.key) && this.selectionAuthority === authority && fork.selectionAuthority === forkAuthority && fork.selected === entry && (entry ? entry.state === state && entry.attempt === attempt && entry.consumers.has(fork) : target.state?.snapshot.status === "idle"),
474
+ publish: () => {
475
+ const previous = this.selected;
476
+ const resolve = this.resolvePending;
477
+ entry?.consumers.add(this);
478
+ entry?.consumers.delete(fork);
479
+ this.selected = entry;
480
+ this.selectedIdentity = fork.selectedIdentity;
481
+ this.retainedRequest = fork.retainedRequest;
482
+ this.describedAttempt = fork.describedAttempt;
483
+ this.observedAttempt = fork.observedAttempt;
484
+ this.selectionAuthority = fork.selectionAuthority;
485
+ this.pendingObserver = void 0;
486
+ this.pendingPromise = void 0;
487
+ this.resolvePending = void 0;
488
+ this.seeded = void 0;
489
+ fork.selected = void 0;
490
+ return () => {
491
+ resolve?.();
492
+ if (previous !== entry && previous !== this.selected) previous?.remove(this);
493
+ };
494
+ }
495
+ }
496
+ };
497
+ }
498
+ };
499
+ }
308
500
  compute() {
309
501
  let request;
310
502
  try {
311
- request = this.request();
503
+ const described = this.request();
504
+ if (described === skip) {
505
+ this.detach();
506
+ this.seeded = void 0;
507
+ return idleState();
508
+ }
509
+ request = described;
312
510
  const previousDefinition = this.owner.queryDefinitions.get(request.queryKey);
313
511
  if (previousDefinition && (previousDefinition.load !== request.definition.load || previousDefinition.kind !== request.definition.kind)) {
314
512
  throw new TypeError(
@@ -317,12 +515,18 @@ class ResourceBinding {
317
515
  }
318
516
  this.owner.queryDefinitions.set(request.queryKey, request.definition);
319
517
  } catch (error) {
518
+ if (isThenable(error)) {
519
+ this.pendingObserver = captureCurrentServerSignalQueryAttemptObserver(this.owner.scopeKey);
520
+ }
320
521
  this.detach();
321
522
  throw error;
322
523
  }
323
524
  if (this.selected?.request.identity !== request.identity) {
324
525
  this.detach();
325
526
  assertAlive(this.owner);
527
+ if (this.candidate && this.describe === void 0) {
528
+ throw new TypeError("The signal candidate has retired.");
529
+ }
326
530
  this.selectedIdentity = {
327
531
  queryKey: request.queryKey,
328
532
  kind: request.definition.kind,
@@ -344,8 +548,19 @@ class ResourceBinding {
344
548
  this.selected = entry;
345
549
  entry.consumers.add(this);
346
550
  this.owner.trace("select", this.node);
347
- if (start) entry.start(entry.state.snapshot.status !== "ready");
551
+ const streamed = this.candidate ? void 0 : this.owner.streamedSelections?.get(this.node.key);
552
+ if (streamed && streamed.selectionKey !== request.identity) {
553
+ this.owner.discardCompletedStreamedSelection?.(streamed);
554
+ }
555
+ if (streamed?.selectionKey === request.identity && entry.startStreamed(streamed)) {
556
+ this.streamedSelection = streamed;
557
+ this.owner.streamedSelectionReady?.(this);
558
+ entry.deliver();
559
+ } else if (start) {
560
+ entry.start(entry.state.snapshot.status !== "ready");
561
+ }
348
562
  }
563
+ this.observeSelectedAttempt();
349
564
  assertAlive(this.owner);
350
565
  return this.state();
351
566
  }
@@ -383,17 +598,65 @@ class ResourceBinding {
383
598
  }
384
599
  detach() {
385
600
  const entry = this.selected;
601
+ if (!this.candidate && this.streamedSelection === this.owner.streamedSelections?.get(this.node.key)) {
602
+ this.owner.streamedSelections?.delete(this.node.key);
603
+ }
386
604
  this.selected = void 0;
387
605
  this.selectedIdentity = void 0;
388
606
  this.describedAttempt = void 0;
607
+ this.observedAttempt = void 0;
389
608
  this.resolvePending?.();
390
609
  this.resolvePending = void 0;
391
610
  this.pendingPromise = void 0;
611
+ this.streamedSelection = void 0;
612
+ this.selectionAuthority = {};
392
613
  entry?.remove(this);
393
614
  }
615
+ observeSelectedAttempt() {
616
+ const attempt = this.selected?.attempt;
617
+ if (!attempt || attempt === this.observedAttempt) return;
618
+ if (this.selected.observe(this.node.key) === attempt) this.observedAttempt = attempt;
619
+ }
620
+ get authority() {
621
+ return this.selected ? this.selectionAuthority : void 0;
622
+ }
623
+ isStreamedSelectionReady(identity) {
624
+ return this.streamedSelection === identity && this.selected?.request.identity === identity.selectionKey;
625
+ }
626
+ bindStreamedSelection(identity) {
627
+ assertAlive(this.owner);
628
+ const entry = this.selected;
629
+ if (identity.nodeKey !== this.node.key) return false;
630
+ if (!entry || entry.request.identity !== identity.selectionKey) {
631
+ this.streamedSelection = void 0;
632
+ return true;
633
+ }
634
+ if (!entry.startStreamed(identity)) return false;
635
+ this.streamedSelection = identity;
636
+ entry.deliver();
637
+ return true;
638
+ }
639
+ acceptStreamed(frame) {
640
+ const identity = this.streamedSelection;
641
+ const entry = this.selected;
642
+ if (!identity || !entry || identity.nodeKey !== frame.identity.nodeKey || identity.selectionKey !== frame.identity.selectionKey || identity.selectionGeneration !== frame.identity.selectionGeneration || identity.attempt !== frame.identity.attempt || frame.kind === "open" && frame.resource !== entry.request.definition.kind) {
643
+ return false;
644
+ }
645
+ return entry.acceptStreamed(frame);
646
+ }
647
+ failStreamed(identity, error) {
648
+ const streamed = this.streamedSelection;
649
+ const entry = this.selected;
650
+ if (!streamed || !entry || streamed.nodeKey !== identity.nodeKey || streamed.selectionKey !== identity.selectionKey || streamed.selectionGeneration !== identity.selectionGeneration || streamed.attempt !== identity.attempt || entry.attempt?.generation !== identity.attempt || !entry.attempt.streamed) {
651
+ return false;
652
+ }
653
+ failAttempt(entry.attempt, error);
654
+ return true;
655
+ }
394
656
  retry(options) {
395
657
  assertAlive(this.owner);
396
658
  assertWritable();
659
+ if (!this.selected && this.node.state?.snapshot.status === "idle") return;
397
660
  signalBatch(() => {
398
661
  const previous = this.selected;
399
662
  if (!previous) invalidateNode(this.node);
@@ -403,7 +666,23 @@ class ResourceBinding {
403
666
  }
404
667
  this.owner.trace("retry", this.node);
405
668
  if (this.selected === previous) this.selected.start(options?.pending === true);
669
+ this.observeSelectedAttempt();
670
+ });
671
+ }
672
+ adopt(requestKey, value) {
673
+ assertAlive(this.owner);
674
+ assertWritable();
675
+ const entry = this.selected;
676
+ if (!entry || entry.request.identity !== requestKey) return false;
677
+ const streaming = entry.request.definition.kind === "stream";
678
+ if (!streaming) entry.stopAttempt();
679
+ entry.state = readyState(value, {
680
+ requestKey: entry.request.identity,
681
+ connection: streaming ? entry.state.snapshot.connection : "none",
682
+ complete: streaming ? entry.state.snapshot.complete : true
406
683
  });
684
+ entry.deliver();
685
+ return true;
407
686
  }
408
687
  seedRequest(retained = false) {
409
688
  const request = retained ? this.retainedRequest : this.selectedIdentity;
@@ -415,13 +694,16 @@ class ResourceBinding {
415
694
  };
416
695
  }
417
696
  acceptsSeed(seed) {
418
- return matchesSeed(this.request(), seed);
697
+ const request = this.request();
698
+ return request !== skip && matchesSeed(request, seed);
419
699
  }
420
700
  dispose() {
421
701
  this.detach();
702
+ this.pendingObserver = void 0;
422
703
  this.describe = void 0;
423
704
  this.seeded = void 0;
424
705
  this.retainedRequest = void 0;
706
+ this.streamedSelection = void 0;
425
707
  }
426
708
  }
427
709
  function matchesSeed(request, seed) {
@@ -433,6 +715,7 @@ function initializeResource(owner, node, describe, seed, retained = seed) {
433
715
  export {
434
716
  RequestEntry,
435
717
  ResourceBinding,
718
+ createResource,
436
719
  initializeResource,
437
720
  query
438
721
  };
@@ -0,0 +1,2 @@
1
+ import type { DerivedCompute, DerivedSignal, Scope } from './types.js';
2
+ export declare function createDeclaredScalarCell<T>(owner: Scope, key: string, compute: DerivedCompute<T>): DerivedSignal<T>;
@@ -0,0 +1,60 @@
1
+ import { createDerivedCellWith } from "./engine.js";
2
+ import {
3
+ CandidateUnsupportedError,
4
+ derivedValueState,
5
+ errorState,
6
+ invalidateNode,
7
+ isThenable,
8
+ pendingState,
9
+ refreshNode
10
+ } from "./graph.js";
11
+ class ScalarBinding {
12
+ constructor(owner, node, compute) {
13
+ this.owner = owner;
14
+ this.node = node;
15
+ this.compute = compute;
16
+ node.compute = (target) => {
17
+ if (owner.readBarrier !== void 0) {
18
+ this.frozen = true;
19
+ return target.state?.snapshot.status === "ready" ? target.state : pendingState(owner.readBarrier);
20
+ }
21
+ let result;
22
+ try {
23
+ result = this.compute();
24
+ } catch (error) {
25
+ if (isThenable(error)) throw error;
26
+ return errorState(error);
27
+ }
28
+ return derivedValueState(target, result);
29
+ };
30
+ }
31
+ owner;
32
+ node;
33
+ frozen = false;
34
+ compute;
35
+ forkCandidate(target) {
36
+ if (this.frozen || this.owner.readBarrier || !this.compute) {
37
+ throw new CandidateUnsupportedError("Frozen scalar candidates are not supported.");
38
+ }
39
+ target.compute = this.node.compute;
40
+ }
41
+ suspend() {
42
+ return false;
43
+ }
44
+ resume() {
45
+ if (!this.frozen || this.owner.retired || this.owner.readBarrier !== void 0 || !this.compute)
46
+ return;
47
+ this.frozen = false;
48
+ invalidateNode(this.node);
49
+ refreshNode(this.node);
50
+ }
51
+ dispose() {
52
+ this.compute = void 0;
53
+ }
54
+ }
55
+ function createDeclaredScalarCell(owner, key, compute) {
56
+ return createDerivedCellWith(owner, key, compute, void 0, ScalarBinding);
57
+ }
58
+ export {
59
+ createDeclaredScalarCell
60
+ };
@@ -1,3 +1,5 @@
1
+ /** Server twin selected by the native-read compiler for octane/signals/client. */
2
+ export * from './index.js';
1
3
  import type { WritableSignal } from './types.js';
2
4
  /** Local server state is recreated on a new pass and never enters shared seeds. */
3
5
  export declare function useSignal$<T>(initial: T | (() => T), slot?: symbol): WritableSignal<T>;
@@ -1,3 +1,4 @@
1
+ export * from "./index.js";
1
2
  import { nativeLocalHook } from "../runtime.server.js";
2
3
  import { createLocalScope } from "./engine.js";
3
4
  function disposeLocalSignal(cell) {
@@ -0,0 +1,71 @@
1
+ import type { ScopedNode, SignalReadMode } from './graph.js';
2
+ import { type NativeReadSource, type NativeTransitionConsumer } from './read-protocol.js';
3
+ export type CandidatePreparation = {
4
+ status: 'ready';
5
+ token: object;
6
+ receipt: {
7
+ publish(accepted?: () => void): boolean;
8
+ };
9
+ } | {
10
+ status: 'pending';
11
+ token: object;
12
+ wakeables: readonly PromiseLike<unknown>[];
13
+ } | {
14
+ status: 'error';
15
+ token: object;
16
+ error: unknown;
17
+ } | {
18
+ status: 'invalid';
19
+ token: object;
20
+ reason: 'stale' | 'unsupported';
21
+ };
22
+ interface PreparationWake {
23
+ notify?: (event: {
24
+ kind: 'retry' | 'invalid';
25
+ token: object;
26
+ }) => void;
27
+ validate?: () => boolean;
28
+ token?: object;
29
+ queued: boolean;
30
+ invalid: boolean;
31
+ }
32
+ /**
33
+ * Private transition graph. The renderer supplies admission through existing
34
+ * presentation transactions; ordinary signal consumers do not retain this class.
35
+ */
36
+ export declare class SignalCandidateFrame {
37
+ private entries;
38
+ private originals;
39
+ private generation;
40
+ private active;
41
+ private preparationToken;
42
+ private preparationWatch;
43
+ private demand;
44
+ run<T>(callback: () => T): T;
45
+ resolve<T>(node: ScopedNode<T>): ScopedNode<T>;
46
+ write<T>(node: ScopedNode<T>, value: T | ((previous: T) => T)): void;
47
+ /** Only observed candidates allocate forwarding sources; ordinary reads keep their identity. */
48
+ nativeSource(target: ScopedNode, read: SignalReadMode, source: NativeReadSource): NativeReadSource;
49
+ validate(): boolean;
50
+ hasWrites(): boolean;
51
+ /** Follow existing graph edges, never the document or a global consumer registry. */
52
+ consumers(): readonly NativeTransitionConsumer[];
53
+ /** Canonical memo witnesses cannot justify skipping a private forward read. */
54
+ observes(node: ScopedNode): boolean;
55
+ canonical<T>(node: ScopedNode<T>): ScopedNode<T>;
56
+ /** Record intent without evaluating the forward graph in an urgent setter. */
57
+ recordUrgentWrite(node: ScopedNode, value: unknown): (() => void) | undefined;
58
+ private replaceWrite;
59
+ private forgetWrite;
60
+ /** Urgent writes win their own cells without restarting unaffected producer leases. */
61
+ rebase(): void;
62
+ prepare(reads?: readonly (() => unknown)[]): CandidatePreparation;
63
+ watchPreparation(outcome: CandidatePreparation, notify: NonNullable<PreparationWake['notify']>): () => void;
64
+ private invalidatePreparation;
65
+ private pruneDemand;
66
+ discard(): void;
67
+ private release;
68
+ private releaseEntry;
69
+ private committed;
70
+ }
71
+ export {};