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,119 @@
1
+ import { readServerResult } from "./server-rpc-stream-client.js";
2
+ import {
3
+ ServerCallUncertainError,
4
+ serverResultLimits,
5
+ validateServerCallBatch
6
+ } from "./server-rpc-protocol.js";
7
+ const SERVER_BATCH_CONTENT_TYPE = "application/x-octane-rpc-batch+ndjson";
8
+ let collecting;
9
+ function batchServerCalls(options, callback) {
10
+ validateServerCallBatch(options);
11
+ const previous = collecting;
12
+ collecting = { options: { ...options }, members: [] };
13
+ try {
14
+ return callback();
15
+ } finally {
16
+ collecting = previous;
17
+ }
18
+ }
19
+ function enqueueServerCall(hash, body, options, limits) {
20
+ const batch = collecting;
21
+ if (batch === void 0 || limits !== void 0) return void 0;
22
+ if (batch.members.length === 32) {
23
+ collecting = { options: batch.options, members: [] };
24
+ return enqueueServerCall(hash, body, options);
25
+ }
26
+ if (batch.members.length === 0)
27
+ queueMicrotask(() => {
28
+ void dispatch(batch);
29
+ });
30
+ return new Promise((resolve, reject) => {
31
+ const abort = () => {
32
+ if (member.settled) return;
33
+ member.settled = true;
34
+ member.dispose();
35
+ reject(new ServerCallUncertainError(options.signal?.reason));
36
+ };
37
+ const member = {
38
+ hash,
39
+ body,
40
+ signal: options.signal,
41
+ resolve,
42
+ reject,
43
+ settled: false,
44
+ dispose: () => options.signal?.removeEventListener("abort", abort)
45
+ };
46
+ options.signal?.addEventListener("abort", abort, { once: true });
47
+ batch.members.push(member);
48
+ if (options.signal?.aborted) abort();
49
+ });
50
+ }
51
+ async function dispatch(batch) {
52
+ const members = batch.members.filter((member) => !member.settled);
53
+ if (members.length === 0) return;
54
+ const limits = serverResultLimits(batch.options);
55
+ const transport = new AbortController();
56
+ let definitiveRejection = false;
57
+ const timer = setTimeout(
58
+ () => transport.abort(new DOMException("Server batch timed out", "TimeoutError")),
59
+ limits.timeoutMs
60
+ );
61
+ try {
62
+ const response = await fetch(
63
+ new URL("/_$_ripple_rpc_$_/" + members[0].hash, globalThis.location.href).href,
64
+ {
65
+ method: "POST",
66
+ headers: { "Content-Type": "application/json", Accept: SERVER_BATCH_CONTENT_TYPE },
67
+ body: JSON.stringify([1, members.map((member, id) => [id, member.hash, member.body])]),
68
+ signal: transport.signal
69
+ }
70
+ );
71
+ if (!response.ok || response.headers.get("content-type")?.split(";", 1)[0] !== SERVER_BATCH_CONTENT_TYPE) {
72
+ definitiveRejection = !response.ok && response.headers.get("Octane-RPC-Outcome") === "rejected";
73
+ try {
74
+ void response.body?.cancel().catch(() => {
75
+ });
76
+ } catch {
77
+ }
78
+ throw new Error("Server-call batching was rejected or is unavailable");
79
+ }
80
+ const result = await readServerResult(response, { ...limits, signal: transport.signal });
81
+ if (result === null || typeof result !== "object" || !(Symbol.asyncIterator in result)) {
82
+ throw new Error("Invalid server batch result");
83
+ }
84
+ const seen = /* @__PURE__ */ new Set();
85
+ for await (const frame of result) {
86
+ if (!Array.isArray(frame) || frame.length !== 3 || !Number.isSafeInteger(frame[0]) || frame[0] < 0 || frame[0] >= members.length || seen.has(frame[0]) || !["value", "rejected", "uncertain"].includes(frame[1]) || frame[1] !== "value" && typeof frame[2] !== "string") {
87
+ throw new Error("Invalid or duplicate server batch member");
88
+ }
89
+ seen.add(frame[0]);
90
+ const member = members[frame[0]];
91
+ if (member.settled) continue;
92
+ member.settled = true;
93
+ member.dispose();
94
+ if (frame[1] === "value") member.resolve(frame[2]);
95
+ else {
96
+ const error = new Error(frame[2]);
97
+ member.reject(frame[1] === "rejected" ? error : new ServerCallUncertainError(error));
98
+ }
99
+ }
100
+ if (seen.size !== members.length) throw new Error("Server batch ended with missing members");
101
+ } catch (error) {
102
+ for (const member of members) {
103
+ if (member.settled) continue;
104
+ member.settled = true;
105
+ member.dispose();
106
+ member.reject(
107
+ definitiveRejection || error instanceof ServerCallUncertainError ? error : new ServerCallUncertainError(error)
108
+ );
109
+ }
110
+ } finally {
111
+ clearTimeout(timer);
112
+ transport.abort();
113
+ }
114
+ }
115
+ export {
116
+ SERVER_BATCH_CONTENT_TYPE,
117
+ batchServerCalls,
118
+ enqueueServerCall
119
+ };
@@ -1,5 +1,7 @@
1
+ import type { ServerCallOptions } from './server-call.js';
2
+ import { type ServerResultLimits } from './server-rpc-protocol.js';
1
3
  /**
2
4
  * Compiler target for browser calls to a `module server` export.
3
5
  * @internal
4
6
  */
5
- export declare function __serverRpc(hash: string, args: unknown[]): Promise<unknown>;
7
+ export declare function __serverRpc(hash: string, args: unknown[], options?: ServerCallOptions, stream?: boolean, limits?: ServerResultLimits): Promise<unknown>;
@@ -1,35 +1,107 @@
1
1
  import * as devalue from "devalue";
2
- async function __serverRpc(hash, args) {
3
- let response;
4
- try {
5
- response = await fetch("/_$_ripple_rpc_$_/" + hash, {
6
- method: "POST",
7
- headers: { "Content-Type": "application/json" },
8
- body: devalue.stringify(args)
9
- });
10
- } catch {
11
- throw new Error("An error occurred while trying to call the Octane server function.");
2
+ import {
3
+ SERVER_RESULT_CONTENT_TYPE,
4
+ ServerCallUncertainError,
5
+ encodeServerArguments,
6
+ serverResultLimits
7
+ } from "./server-rpc-protocol.js";
8
+ import { readServerResult } from "./server-rpc-stream-client.js";
9
+ import { enqueueServerCall } from "./server-rpc-batch-client.js";
10
+ async function __serverRpc(hash, args, options = {}, stream = false, limits) {
11
+ if (options === null || typeof options !== "object" || Object.keys(options).some((key) => key !== "signal") || options.signal !== void 0 && !(options.signal instanceof AbortSignal))
12
+ throw new TypeError("Server function options accept only a local AbortSignal");
13
+ options.signal?.throwIfAborted();
14
+ const resolvedLimits = stream ? serverResultLimits(limits) : void 0;
15
+ const payload = stream ? encodeServerArguments(args) : devalue.stringify(args);
16
+ if (stream) {
17
+ const batched = enqueueServerCall(hash, payload, options, limits);
18
+ if (batched !== void 0) return batched;
12
19
  }
13
- if (!response.ok) {
14
- let message = `Server function call failed with status ${response.status}`;
15
- const body2 = await response.text().catch(() => "");
16
- if (body2) {
17
- try {
18
- const parsed = JSON.parse(body2);
19
- message = typeof parsed?.error === "string" && parsed.error ? parsed.error : body2;
20
- } catch {
21
- message = body2;
20
+ const deadline = resolvedLimits === void 0 ? void 0 : Date.now() + resolvedLimits.timeoutMs;
21
+ const cancellation = resolvedLimits === void 0 ? void 0 : new AbortController();
22
+ const timer = resolvedLimits === void 0 ? void 0 : setTimeout(() => {
23
+ cancellation.abort(new DOMException("Server function timed out", "TimeoutError"));
24
+ }, resolvedLimits.timeoutMs);
25
+ const signal = cancellation === void 0 ? options.signal : options.signal === void 0 ? cancellation.signal : AbortSignal.any([options.signal, cancellation.signal]);
26
+ try {
27
+ let response;
28
+ try {
29
+ response = await fetch(new URL("/_$_ripple_rpc_$_/" + hash, globalThis.location.href).href, {
30
+ method: "POST",
31
+ headers: {
32
+ "Content-Type": "application/json",
33
+ ...stream ? { Accept: SERVER_RESULT_CONTENT_TYPE } : {}
34
+ },
35
+ body: payload,
36
+ signal
37
+ });
38
+ } catch (cause) {
39
+ throw new ServerCallUncertainError(
40
+ new Error("An error occurred while trying to call the Octane server function.", { cause })
41
+ );
42
+ }
43
+ if (!response.ok) {
44
+ let message = `Server function call failed with status ${response.status}`;
45
+ let body2 = "";
46
+ if (stream) {
47
+ try {
48
+ void response.body?.cancel().catch(() => {
49
+ });
50
+ } catch {
51
+ }
52
+ } else {
53
+ body2 = await response.text().catch(() => "");
22
54
  }
55
+ if (body2) {
56
+ try {
57
+ const parsed = JSON.parse(body2);
58
+ message = typeof parsed?.error === "string" && parsed.error ? parsed.error : body2;
59
+ } catch {
60
+ message = body2;
61
+ }
62
+ }
63
+ const error = Object.assign(new Error(message), { status: response.status });
64
+ throw response.headers.get("Octane-RPC-Outcome") === "rejected" ? error : new ServerCallUncertainError(error);
23
65
  }
24
- throw new Error(message);
25
- }
26
- const body = await response.text();
27
- if (body === "") {
28
- throw new Error(
29
- "The server function endpoint returned an empty response. Is the Octane server running?"
30
- );
66
+ if (stream) {
67
+ if (response.headers.get("content-type")?.split(";", 1)[0] !== SERVER_RESULT_CONTENT_TYPE) {
68
+ void response.body?.cancel().catch(() => {
69
+ });
70
+ throw new ServerCallUncertainError(
71
+ new Error("The server does not support the requested result protocol")
72
+ );
73
+ }
74
+ const remaining = deadline - Date.now();
75
+ if (remaining <= 0) {
76
+ void response.body?.cancel().catch(() => {
77
+ });
78
+ throw new ServerCallUncertainError(
79
+ new DOMException("Server function timed out", "TimeoutError")
80
+ );
81
+ }
82
+ return await readServerResult(response, { ...resolvedLimits, timeoutMs: remaining, signal });
83
+ }
84
+ let body;
85
+ try {
86
+ body = await response.text();
87
+ } catch (error) {
88
+ throw new ServerCallUncertainError(error);
89
+ }
90
+ if (body === "") {
91
+ throw new ServerCallUncertainError(
92
+ new Error(
93
+ "The server function endpoint returned an empty response. Is the Octane server running?"
94
+ )
95
+ );
96
+ }
97
+ try {
98
+ return devalue.parse(body).value;
99
+ } catch (error) {
100
+ throw new ServerCallUncertainError(error);
101
+ }
102
+ } finally {
103
+ clearTimeout(timer);
31
104
  }
32
- return devalue.parse(body).value;
33
105
  }
34
106
  export {
35
107
  __serverRpc
@@ -0,0 +1,47 @@
1
+ /** The streamed protocol is opt-in; legacy devalue RPC remains unchanged. */
2
+ export declare const SERVER_RESULT_CONTENT_TYPE = "application/x-octane-rpc+ndjson";
3
+ export declare function encodeServerArguments(args: unknown[]): string;
4
+ export declare function decodeServerArguments(body: string): unknown[];
5
+ /** No authoritative receipt arrived; retrying a mutation could execute it twice. */
6
+ export declare class ServerCallUncertainError extends Error {
7
+ readonly code = "OCTANE_RPC_UNCERTAIN";
8
+ constructor(cause: unknown);
9
+ }
10
+ export interface ServerResultLimits {
11
+ /** Maximum encoded bytes in one complete value or terminal frame. */
12
+ maxFrameBytes?: number;
13
+ /** Maximum bytes in this invocation, independently of the request-body limit. */
14
+ maxTotalBytes?: number;
15
+ /** Total invocation lifetime, including a stalled producer or consumer. */
16
+ timeoutMs?: number;
17
+ }
18
+ export interface ServerCallBatchOptions extends ServerResultLimits {
19
+ /** Explicitly asserts independent, finite reads; mutations/subscriptions stay outside. */
20
+ kind: 'independent-reads';
21
+ /** Local compatibility keys, not credentials sent to or trusted by the server. */
22
+ authority: string;
23
+ document: string;
24
+ }
25
+ export declare function validateServerCallBatch(options: ServerCallBatchOptions): void;
26
+ export interface ResolvedServerResultLimits {
27
+ maxFrameBytes: number;
28
+ maxTotalBytes: number;
29
+ timeoutMs: number;
30
+ }
31
+ export declare function serverResultLimits(limits?: ServerResultLimits): ResolvedServerResultLimits;
32
+ export type ServerResultFrame = {
33
+ kind: 'stream' | 'complete';
34
+ } | {
35
+ kind: 'value';
36
+ value: unknown;
37
+ } | {
38
+ kind: 'error';
39
+ error: string;
40
+ };
41
+ export declare function encodeServerResultFrame(sequence: number, frame: ServerResultFrame): Uint8Array;
42
+ export declare function decodeServerResultFrame(line: string, sequence: number): ServerResultFrame;
43
+ /**
44
+ * Read one line at a time without concatenating the entire response or repeatedly
45
+ * copying a growing partial frame. A transport chunk may contain several frames.
46
+ */
47
+ export declare function serverResultReader(reader: ReadableStreamDefaultReader<Uint8Array>, limits: ResolvedServerResultLimits): () => Promise<ServerResultFrame>;
@@ -0,0 +1,109 @@
1
+ import { decodeSignalValue, encodeSignalValue } from "./data-encoding.js";
2
+ const SERVER_RESULT_CONTENT_TYPE = "application/x-octane-rpc+ndjson";
3
+ function encodeServerArguments(args) {
4
+ return JSON.stringify([1, encodeSignalValue(args)]);
5
+ }
6
+ function decodeServerArguments(body) {
7
+ const envelope = JSON.parse(body);
8
+ if (!Array.isArray(envelope) || envelope.length !== 2 || envelope[0] !== 1) {
9
+ throw new Error("Invalid server argument protocol");
10
+ }
11
+ const args = decodeSignalValue(envelope[1]);
12
+ if (!Array.isArray(args)) throw new Error("Invalid server arguments");
13
+ return args;
14
+ }
15
+ class ServerCallUncertainError extends Error {
16
+ code = "OCTANE_RPC_UNCERTAIN";
17
+ constructor(cause) {
18
+ super(cause instanceof Error ? cause.message : "Server function outcome is uncertain", {
19
+ cause
20
+ });
21
+ this.name = "ServerCallUncertainError";
22
+ }
23
+ }
24
+ function validateServerCallBatch(options) {
25
+ if (options.kind !== "independent-reads" || !options.authority || !options.document) {
26
+ throw new TypeError(
27
+ "A server-call batch requires independent reads and authority/document keys"
28
+ );
29
+ }
30
+ serverResultLimits(options);
31
+ }
32
+ function serverResultLimits(limits = {}) {
33
+ const result = {
34
+ maxFrameBytes: limits.maxFrameBytes ?? 1024 * 1024,
35
+ maxTotalBytes: limits.maxTotalBytes ?? 16 * 1024 * 1024,
36
+ timeoutMs: limits.timeoutMs ?? 3e4
37
+ };
38
+ for (const value of Object.values(result)) {
39
+ if (!Number.isSafeInteger(value) || value <= 0) {
40
+ throw new RangeError("Server result limits must be positive safe integers");
41
+ }
42
+ }
43
+ if (result.maxFrameBytes > result.maxTotalBytes) {
44
+ throw new RangeError("A server result frame cannot exceed the total response budget");
45
+ }
46
+ return result;
47
+ }
48
+ function encodeServerResultFrame(sequence, frame) {
49
+ const payload = frame.kind === "value" ? encodeSignalValue(frame.value) : frame.kind === "error" ? frame.error : null;
50
+ return new TextEncoder().encode(JSON.stringify([1, sequence, frame.kind, payload]) + "\n");
51
+ }
52
+ function decodeServerResultFrame(line, sequence) {
53
+ const frame = JSON.parse(line);
54
+ if (!Array.isArray(frame) || frame.length !== 4 || frame[0] !== 1 || frame[1] !== sequence) {
55
+ throw new Error("Invalid or out-of-order server result frame");
56
+ }
57
+ const [, , kind, payload] = frame;
58
+ if (kind === "value") {
59
+ return { kind, value: decodeSignalValue(payload) };
60
+ }
61
+ if (kind === "error" && typeof payload === "string") return { kind, error: payload };
62
+ if ((kind === "stream" || kind === "complete") && payload === null) return { kind };
63
+ throw new Error("Invalid server result frame");
64
+ }
65
+ function serverResultReader(reader, limits) {
66
+ let chunk = new Uint8Array(0);
67
+ let offset = 0;
68
+ let sequence = 0;
69
+ let totalBytes = 0;
70
+ return async () => {
71
+ const parts = [];
72
+ let bytes = 0;
73
+ while (true) {
74
+ if (offset === chunk.length) {
75
+ const next = await reader.read();
76
+ if (next.done) throw new Error("Server result ended without a terminal frame");
77
+ chunk = next.value;
78
+ offset = 0;
79
+ }
80
+ const newline = chunk.indexOf(10, offset);
81
+ const end = newline === -1 ? chunk.length : newline + 1;
82
+ const part = chunk.subarray(offset, end);
83
+ bytes += part.length;
84
+ totalBytes += part.length;
85
+ if (bytes > limits.maxFrameBytes || totalBytes > limits.maxTotalBytes) {
86
+ throw new Error("Server result exceeded its response budget");
87
+ }
88
+ parts.push(part);
89
+ offset = end;
90
+ if (newline !== -1) {
91
+ const decoder = new TextDecoder("utf-8", { fatal: true });
92
+ const text = parts.map((value) => decoder.decode(value, { stream: true }));
93
+ text.push(decoder.decode());
94
+ return decodeServerResultFrame(text.join(""), sequence++);
95
+ }
96
+ }
97
+ };
98
+ }
99
+ export {
100
+ SERVER_RESULT_CONTENT_TYPE,
101
+ ServerCallUncertainError,
102
+ decodeServerArguments,
103
+ decodeServerResultFrame,
104
+ encodeServerArguments,
105
+ encodeServerResultFrame,
106
+ serverResultLimits,
107
+ serverResultReader,
108
+ validateServerCallBatch
109
+ };
@@ -0,0 +1,5 @@
1
+ import { type ServerResultLimits } from './server-rpc-protocol.js';
2
+ /** A single-consumer result; iterator.return() cancels only this invocation. */
3
+ export declare function readServerResult(response: Response, options?: ServerResultLimits & {
4
+ signal?: AbortSignal;
5
+ }): Promise<unknown>;
@@ -0,0 +1,98 @@
1
+ import {
2
+ serverResultLimits,
3
+ serverResultReader,
4
+ ServerCallUncertainError
5
+ } from "./server-rpc-protocol.js";
6
+ async function readServerResult(response, options = {}) {
7
+ if (response.body === null)
8
+ throw new ServerCallUncertainError(new Error("Server function returned an empty response"));
9
+ const limits = serverResultLimits(options);
10
+ const reader = response.body.getReader();
11
+ const read = serverResultReader(reader, limits);
12
+ let finished = false;
13
+ let failure;
14
+ const cleanup = () => {
15
+ clearTimeout(timer);
16
+ options.signal?.removeEventListener("abort", abort);
17
+ };
18
+ const close = async () => {
19
+ if (finished) return;
20
+ finished = true;
21
+ cleanup();
22
+ try {
23
+ void reader.cancel().catch(() => {
24
+ });
25
+ } catch {
26
+ }
27
+ reader.releaseLock();
28
+ };
29
+ const abort = () => {
30
+ failure = new ServerCallUncertainError(
31
+ options.signal?.reason ?? new DOMException("Server function canceled", "AbortError")
32
+ );
33
+ void close();
34
+ };
35
+ const timer = setTimeout(() => {
36
+ failure = new ServerCallUncertainError(
37
+ new DOMException("Server function timed out", "TimeoutError")
38
+ );
39
+ void close();
40
+ }, limits.timeoutMs);
41
+ options.signal?.addEventListener("abort", abort, { once: true });
42
+ if (options.signal?.aborted) abort();
43
+ const next = async () => {
44
+ if (failure !== void 0) throw failure;
45
+ try {
46
+ const frame = await read();
47
+ if (failure !== void 0) throw failure;
48
+ if (frame.kind === "error") throw new Error(frame.error);
49
+ return frame;
50
+ } catch (error) {
51
+ await close();
52
+ throw failure ?? new ServerCallUncertainError(error);
53
+ }
54
+ };
55
+ try {
56
+ const first = await next();
57
+ if (first.kind === "value") {
58
+ if ((await next()).kind !== "complete") throw new Error("Invalid one-shot server result");
59
+ await close();
60
+ return first.value;
61
+ }
62
+ if (first.kind !== "stream") throw new Error("Invalid server result opening frame");
63
+ let claimed = false;
64
+ return {
65
+ [Symbol.asyncIterator]() {
66
+ if (claimed) throw new Error("A server result stream supports one consumer");
67
+ claimed = true;
68
+ let pulls = Promise.resolve();
69
+ const pull = async () => {
70
+ if (failure !== void 0) throw failure;
71
+ if (finished) return { done: true, value: void 0 };
72
+ const frame = await next();
73
+ if (frame.kind === "value") return { done: false, value: frame.value };
74
+ await close();
75
+ if (frame.kind !== "complete") throw new Error("Invalid server result continuation");
76
+ return { done: true, value: void 0 };
77
+ };
78
+ return {
79
+ next() {
80
+ const result = pulls.then(pull, pull);
81
+ pulls = result;
82
+ return result;
83
+ },
84
+ async return() {
85
+ await close();
86
+ return { done: true, value: void 0 };
87
+ }
88
+ };
89
+ }
90
+ };
91
+ } catch (error) {
92
+ await close();
93
+ throw error instanceof ServerCallUncertainError ? error : new ServerCallUncertainError(error);
94
+ }
95
+ }
96
+ export {
97
+ readServerResult
98
+ };
@@ -0,0 +1,12 @@
1
+ import { type ActionOperation, type ActionUncertain, type OptimisticOptions, type OptimisticSignal, type SignalAction, type SignalHandle } from './types.js';
2
+ export declare class ActionUncertainError extends Error {
3
+ readonly operationId: string;
4
+ readonly code = "OCTANE_ACTION_UNCERTAIN";
5
+ constructor(operationId: string, options: {
6
+ cause: unknown;
7
+ });
8
+ }
9
+ export declare function optimistic$<T>(source$: SignalHandle<T>, options?: OptimisticOptions<T>): OptimisticSignal<T>;
10
+ export declare function action$<F extends (operation: ActionOperation, ...args: any[]) => any>(handler: F): SignalAction<F>;
11
+ export declare function action$<F extends (operation: ActionOperation, ...args: any[]) => any>(key: string, handler: F): SignalAction<F>;
12
+ export declare function isActionUncertain(value: unknown): value is ActionUncertain | ActionUncertainError;