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.
- package/README.md +5 -0
- package/dist/behavior-root.d.ts +13 -3
- package/dist/behavior-root.js +28 -5
- package/dist/cjs/behavior-root.cjs +32 -6
- package/dist/cjs/class-names.cjs +49 -0
- package/dist/cjs/constants.cjs +9 -0
- package/dist/cjs/context-epoch.cjs +38 -0
- package/dist/cjs/css.cjs +6 -27
- package/dist/cjs/data-encoding.cjs +160 -0
- package/dist/cjs/dom-binding-handoff.cjs +93 -0
- package/dist/cjs/dom-binding-protocol.cjs +115 -0
- package/dist/cjs/dom-binding-styles.cjs +292 -0
- package/dist/cjs/dom-bindings.cjs +700 -0
- package/dist/cjs/dom-focused-move.cjs +66 -0
- package/dist/cjs/dom-stage.cjs +896 -0
- package/dist/cjs/dom-tables.cjs +4 -94
- package/dist/cjs/error-codes.client.generated.cjs +33 -0
- package/dist/cjs/error-codes.server.generated.cjs +24 -0
- package/dist/cjs/hydration/control-capture.cjs +244 -0
- package/dist/cjs/hydration/event-capture.cjs +125 -43
- package/dist/cjs/hydration/interaction-config.cjs +50 -2
- package/dist/cjs/hydration-markers.cjs +37 -0
- package/dist/cjs/independent-hydration-protocol.cjs +106 -0
- package/dist/cjs/index.cjs +9 -0
- package/dist/cjs/runtime.cjs +6397 -1683
- package/dist/cjs/runtime.server.cjs +1465 -213
- package/dist/cjs/server/early-signals.cjs +45 -0
- package/dist/cjs/server/index.cjs +47 -0
- package/dist/cjs/server/rpc-batch.cjs +149 -0
- package/dist/cjs/server/rpc-stream.cjs +146 -0
- package/dist/cjs/server/rpc.cjs +34 -15
- package/dist/cjs/server/signal-query-observation.cjs +158 -0
- package/dist/cjs/server/streamed-region.cjs +52 -0
- package/dist/cjs/server/streamed-signals.cjs +512 -0
- package/dist/cjs/server-call.cjs +103 -0
- package/dist/cjs/server-rpc-batch-client.cjs +141 -0
- package/dist/cjs/server-rpc-client.cjs +94 -27
- package/dist/cjs/server-rpc-protocol.cjs +141 -0
- package/dist/cjs/server-rpc-stream-client.cjs +118 -0
- package/dist/cjs/signals/actions.cjs +474 -0
- package/dist/cjs/signals/client.cjs +4 -1
- package/dist/cjs/signals/computations.cjs +460 -0
- package/dist/cjs/signals/control-binding.cjs +368 -0
- package/dist/cjs/signals/derived-facade.cjs +50 -0
- package/dist/cjs/signals/document-owner.cjs +71 -0
- package/dist/cjs/signals/early-values.cjs +155 -0
- package/dist/cjs/signals/encoding.cjs +6 -95
- package/dist/cjs/signals/engine.cjs +276 -12
- package/dist/cjs/signals/errors.cjs +18 -0
- package/dist/cjs/signals/facade.cjs +477 -0
- package/dist/cjs/signals/graph.cjs +166 -20
- package/dist/cjs/signals/handle-protocol.cjs +36 -0
- package/dist/cjs/signals/index.cjs +81 -2
- package/dist/cjs/signals/native-read-client.cjs +10 -0
- package/dist/cjs/signals/owner-context.cjs +112 -0
- package/dist/cjs/signals/query-attempt-observer.cjs +68 -0
- package/dist/cjs/signals/query-facade.cjs +72 -0
- package/dist/cjs/signals/read-protocol.cjs +54 -0
- package/dist/cjs/signals/requests.cjs +287 -14
- package/dist/cjs/signals/scalar-computations.cjs +76 -0
- package/dist/cjs/signals/server.cjs +4 -1
- package/dist/cjs/signals/transition-candidate.cjs +567 -0
- package/dist/cjs/signals/transition-state.cjs +106 -0
- package/dist/cjs/signals/types.cjs +50 -0
- package/dist/cjs/stream-protocol.cjs +3 -0
- package/dist/cjs/streamed-signals-protocol.cjs +159 -0
- package/dist/cjs/style-values.cjs +122 -0
- package/dist/cjs/trusted-html.cjs +30 -0
- package/dist/cjs/version.cjs +1 -1
- package/dist/class-names.d.ts +5 -0
- package/dist/class-names.js +25 -0
- package/dist/compiler/bundler.js +51 -22
- package/dist/compiler/compile.js +2534 -581
- package/dist/compiler/dom-binding-program.js +935 -0
- package/dist/compiler/dom-binding-request.js +57 -0
- package/dist/compiler/dom-bindings.js +2245 -0
- package/dist/compiler/hook-deps.js +759 -10
- package/dist/compiler/hydrate-boundaries.js +358 -15
- package/dist/compiler/index.d.ts +39 -2
- package/dist/compiler/index.js +1 -0
- package/dist/compiler/native-attribute-reads.js +127 -0
- package/dist/compiler/native-change-diagnostics.js +32 -38
- package/dist/compiler/native-read-diagnostics.js +69 -8
- package/dist/compiler/native-read-types.js +80 -34
- package/dist/compiler/parser.node.js +1 -1
- package/dist/compiler/renderer-boundaries.js +40 -0
- package/dist/compiler/server-context.js +118 -0
- package/dist/compiler/signal-attempt-reads.js +479 -0
- package/dist/compiler/signal-declarations.js +557 -0
- package/dist/compiler/signal-start-reads.js +413 -0
- package/dist/compiler/slot-hooks.d.ts +3 -1
- package/dist/compiler/slot-hooks.js +168 -34
- package/dist/compiler/strong-auto-memo.js +90 -0
- package/dist/compiler/strong-html.js +143 -0
- package/dist/compiler/strong-mode.js +764 -66
- package/dist/compiler/strong-template-policy.js +307 -0
- package/dist/compiler/template-ir.js +200 -0
- package/dist/compiler/typescript.d.ts +2 -0
- package/dist/compiler/typescript.js +2 -0
- package/dist/compiler/vite.d.ts +3 -1
- package/dist/compiler/vite.js +122 -4
- package/dist/compiler/volar.LICENSES.txt +1 -1
- package/dist/compiler/volar.d.ts +6 -1
- package/dist/compiler/volar.js +750 -1433
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +10 -0
- package/dist/context-epoch.d.ts +2 -0
- package/dist/context-epoch.js +13 -0
- package/dist/css.d.ts +2 -5
- package/dist/css.js +2 -23
- package/dist/data-encoding.d.ts +9 -0
- package/dist/data-encoding.js +134 -0
- package/dist/dom-binding-classes.d.ts +15 -0
- package/dist/dom-binding-classes.js +129 -0
- package/dist/dom-binding-controls.d.ts +2 -0
- package/dist/dom-binding-controls.js +6 -0
- package/dist/dom-binding-handoff.d.ts +55 -0
- package/dist/dom-binding-handoff.js +62 -0
- package/dist/dom-binding-program.d.ts +300 -0
- package/dist/dom-binding-program.js +1625 -0
- package/dist/dom-binding-projections.d.ts +15 -0
- package/dist/dom-binding-projections.js +187 -0
- package/dist/dom-binding-protocol.d.ts +13 -0
- package/dist/dom-binding-protocol.js +88 -0
- package/dist/dom-binding-signals.d.ts +21 -0
- package/dist/dom-binding-signals.js +109 -0
- package/dist/dom-binding-styles.d.ts +17 -0
- package/dist/dom-binding-styles.js +272 -0
- package/dist/dom-bindings.d.ts +89 -0
- package/dist/dom-bindings.js +670 -0
- package/dist/dom-focused-move.d.ts +6 -0
- package/dist/dom-focused-move.js +42 -0
- package/dist/dom-stage.d.ts +52 -0
- package/dist/dom-stage.js +872 -0
- package/dist/dom-tables.d.ts +1 -3
- package/dist/dom-tables.js +1 -91
- package/dist/error-codes.client.generated.d.ts +9 -0
- package/dist/error-codes.client.generated.js +33 -0
- package/dist/error-codes.server.generated.d.ts +6 -0
- package/dist/error-codes.server.generated.js +24 -0
- package/dist/hydration/control-capture.d.ts +62 -0
- package/dist/hydration/control-capture.js +223 -0
- package/dist/hydration/document-lifecycle.d.ts +23 -0
- package/dist/hydration/document-lifecycle.js +80 -0
- package/dist/hydration/event-capture.d.ts +20 -1
- package/dist/hydration/event-capture.js +128 -42
- package/dist/hydration/independent-island.d.ts +45 -0
- package/dist/hydration/independent-island.js +233 -0
- package/dist/hydration/index.d.ts +8 -1
- package/dist/hydration/index.js +55 -1
- package/dist/hydration/interaction-config.d.ts +5 -0
- package/dist/hydration/interaction-config.js +44 -1
- package/dist/hydration/stream-delivery.d.ts +32 -0
- package/dist/hydration/stream-delivery.js +333 -0
- package/dist/hydration/stream-receiver.d.ts +26 -0
- package/dist/hydration/stream-receiver.js +204 -0
- package/dist/hydration/stream-result-receiver.d.ts +70 -0
- package/dist/hydration/stream-result-receiver.js +246 -0
- package/dist/hydration/streamed-signals.d.ts +44 -0
- package/dist/hydration/streamed-signals.js +186 -0
- package/dist/hydration/types.d.ts +6 -0
- package/dist/hydration-markers.d.ts +7 -0
- package/dist/hydration-markers.js +10 -0
- package/dist/independent-hydration-protocol.d.ts +31 -0
- package/dist/independent-hydration-protocol.js +80 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +6 -0
- package/dist/internal/signal-read.d.ts +1 -0
- package/dist/internal/signal-read.js +4 -0
- package/dist/jsx-runtime-strong.d.ts +33 -0
- package/dist/jsx-runtime.d.ts +58 -2
- package/dist/public-types.d.ts +15 -1
- package/dist/runtime.d.ts +297 -23
- package/dist/runtime.js +6405 -1679
- package/dist/runtime.server.d.ts +110 -27
- package/dist/runtime.server.js +1458 -213
- package/dist/server/early-signals.d.ts +14 -0
- package/dist/server/early-signals.js +34 -0
- package/dist/server/index.d.ts +11 -2
- package/dist/server/index.js +50 -1
- package/dist/server/rpc-batch.d.ts +17 -0
- package/dist/server/rpc-batch.js +129 -0
- package/dist/server/rpc-stream.d.ts +9 -0
- package/dist/server/rpc-stream.js +125 -0
- package/dist/server/rpc.d.ts +5 -1
- package/dist/server/rpc.js +37 -13
- package/dist/server/signal-query-observation.d.ts +3 -0
- package/dist/server/signal-query-observation.js +134 -0
- package/dist/server/streamed-region.d.ts +15 -0
- package/dist/server/streamed-region.js +30 -0
- package/dist/server/streamed-signals.d.ts +45 -0
- package/dist/server/streamed-signals.js +488 -0
- package/dist/server-call.d.ts +41 -0
- package/dist/server-call.js +75 -0
- package/dist/server-rpc-batch-client.d.ts +7 -0
- package/dist/server-rpc-batch-client.js +119 -0
- package/dist/server-rpc-client.d.ts +3 -1
- package/dist/server-rpc-client.js +99 -27
- package/dist/server-rpc-protocol.d.ts +47 -0
- package/dist/server-rpc-protocol.js +109 -0
- package/dist/server-rpc-stream-client.d.ts +5 -0
- package/dist/server-rpc-stream-client.js +98 -0
- package/dist/signals/actions.d.ts +12 -0
- package/dist/signals/actions.js +466 -0
- package/dist/signals/client.d.ts +2 -0
- package/dist/signals/client.js +1 -0
- package/dist/signals/computations.d.ts +37 -0
- package/dist/signals/computations.js +455 -0
- package/dist/signals/control-binding.d.ts +31 -0
- package/dist/signals/control-binding.js +353 -0
- package/dist/signals/derived-facade.d.ts +3 -0
- package/dist/signals/derived-facade.js +25 -0
- package/dist/signals/document-owner.d.ts +10 -0
- package/dist/signals/document-owner.js +43 -0
- package/dist/signals/early-values.d.ts +24 -0
- package/dist/signals/early-values.js +124 -0
- package/dist/signals/encoding.d.ts +1 -3
- package/dist/signals/encoding.js +3 -93
- package/dist/signals/engine.d.ts +53 -4
- package/dist/signals/engine.js +275 -16
- package/dist/signals/errors.d.ts +7 -0
- package/dist/signals/errors.js +16 -0
- package/dist/signals/facade.d.ts +74 -0
- package/dist/signals/facade.js +452 -0
- package/dist/signals/graph.d.ts +52 -6
- package/dist/signals/graph.js +174 -19
- package/dist/signals/handle-protocol.d.ts +4 -0
- package/dist/signals/handle-protocol.js +11 -0
- package/dist/signals/index.d.ts +12 -3
- package/dist/signals/index.js +71 -2
- package/dist/signals/native-read-client.d.ts +3 -0
- package/dist/signals/native-read-client.js +10 -0
- package/dist/signals/owner-context.d.ts +22 -0
- package/dist/signals/owner-context.js +77 -0
- package/dist/signals/query-attempt-observer.d.ts +49 -0
- package/dist/signals/query-attempt-observer.js +41 -0
- package/dist/signals/query-facade.d.ts +3 -0
- package/dist/signals/query-facade.js +49 -0
- package/dist/signals/read-protocol.d.ts +37 -2
- package/dist/signals/read-protocol.js +44 -0
- package/dist/signals/requests.d.ts +38 -5
- package/dist/signals/requests.js +298 -15
- package/dist/signals/scalar-computations.d.ts +2 -0
- package/dist/signals/scalar-computations.js +60 -0
- package/dist/signals/server.d.ts +2 -0
- package/dist/signals/server.js +1 -0
- package/dist/signals/transition-candidate.d.ts +71 -0
- package/dist/signals/transition-candidate.js +557 -0
- package/dist/signals/transition-state.d.ts +51 -0
- package/dist/signals/transition-state.js +70 -0
- package/dist/signals/types.d.ts +95 -5
- package/dist/signals/types.js +20 -0
- package/dist/stream-protocol.d.ts +0 -5
- package/dist/stream-protocol.js +3 -0
- package/dist/streamed-signals-protocol.d.ts +60 -0
- package/dist/streamed-signals-protocol.js +130 -0
- package/dist/style-values.d.ts +3 -0
- package/dist/style-values.js +96 -0
- package/dist/trusted-html.d.ts +17 -0
- package/dist/trusted-html.js +6 -0
- package/dist/universal-core.d.ts +6 -0
- package/dist/universal-core.js +214 -202
- package/dist/version.js +1 -1
- 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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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;
|