@yourrealm/sdk 0.0.0-alpha.1378.ce03c8dc
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/LICENSE-EXCEPTION +26 -0
- package/README.md +34 -0
- package/dist/chunk-3R6KEFOI.js +22 -0
- package/dist/chunk-67BDAMYX.js +376 -0
- package/dist/chunk-AITGS27R.js +139 -0
- package/dist/chunk-CMAEHQ4F.js +6 -0
- package/dist/chunk-MLKGABMK.js +9 -0
- package/dist/chunk-O3UIK34B.js +1430 -0
- package/dist/chunk-VHTUSZUX.js +176 -0
- package/dist/cli.js +9006 -0
- package/dist/docker.js +54 -0
- package/dist/index.js +1053 -0
- package/dist/ingress.js +436 -0
- package/dist/jsx-runtime.js +2 -0
- package/dist/preact.js +22 -0
- package/dist/service-url.js +11 -0
- package/dist/services.js +213 -0
- package/dist/test.js +5069 -0
- package/dist/ui.js +254 -0
- package/dist/worker-protocol.js +89 -0
- package/package.json +112 -0
- package/src/app-icon.ts +74 -0
- package/src/apply.ts +52 -0
- package/src/bundle-ui.ts +211 -0
- package/src/bundle.ts +546 -0
- package/src/cli/build-dev-landing.ts +84 -0
- package/src/cli/build-install-panel.ts +90 -0
- package/src/cli/build-runtime.ts +133 -0
- package/src/cli/build.ts +63 -0
- package/src/cli/dev.ts +107 -0
- package/src/cli/down.ts +60 -0
- package/src/cli/main.ts +56 -0
- package/src/cli/test.ts +61 -0
- package/src/cli/validate.ts +37 -0
- package/src/compile.ts +100 -0
- package/src/create-app.ts +982 -0
- package/src/deep-merge.ts +33 -0
- package/src/dev/fixture.ts +48 -0
- package/src/dev/install-handler.ts +383 -0
- package/src/dev/server.ts +968 -0
- package/src/dev/stack.ts +108 -0
- package/src/dev/state.ts +159 -0
- package/src/docker.ts +96 -0
- package/src/errors.ts +61 -0
- package/src/factory.ts +481 -0
- package/src/garage.ts +56 -0
- package/src/generator/hash.ts +6 -0
- package/src/generator/resolve-refs.ts +207 -0
- package/src/grants-builder.ts +100 -0
- package/src/grants.ts +260 -0
- package/src/home-api.ts +172 -0
- package/src/home-cookie.ts +10 -0
- package/src/home-registry.ts +120 -0
- package/src/home.ts +187 -0
- package/src/host-registry.ts +143 -0
- package/src/http-header.ts +20 -0
- package/src/identifiers.ts +49 -0
- package/src/index.ts +66 -0
- package/src/info.ts +64 -0
- package/src/ini.ts +50 -0
- package/src/json.ts +14 -0
- package/src/jsx-runtime/index.ts +5 -0
- package/src/keys.ts +29 -0
- package/src/mysql.ts +56 -0
- package/src/pack-build.ts +108 -0
- package/src/pack.ts +115 -0
- package/src/peer-registry.ts +121 -0
- package/src/peer-world.ts +117 -0
- package/src/permission-meta.ts +116 -0
- package/src/permissions.ts +108 -0
- package/src/pkg-root.ts +34 -0
- package/src/postgres.ts +79 -0
- package/src/preact/index.ts +27 -0
- package/src/redis.ts +43 -0
- package/src/ref.ts +271 -0
- package/src/remote-registry.ts +33 -0
- package/src/required-host-kinds.ts +110 -0
- package/src/resolve-context.ts +111 -0
- package/src/resolve.ts +471 -0
- package/src/runtime/docker/compose/compose-schema.ts +118 -0
- package/src/runtime/docker/compose/convert-to-docker-compose.ts +278 -0
- package/src/runtime/docker/compose/default-hardening.ts +21 -0
- package/src/runtime/docker/compose/effects.ts +107 -0
- package/src/runtime/docker/compose/index.ts +17 -0
- package/src/runtime/docker/compose/labels.ts +39 -0
- package/src/runtime/docker/compose/types.ts +78 -0
- package/src/runtime/docker/home-proxy-server/index.ts +177 -0
- package/src/runtime/docker/home-proxy-server/protocol.ts +53 -0
- package/src/runtime/docker/ingress/build-config.ts +819 -0
- package/src/runtime/docker/ingress/index.ts +16 -0
- package/src/runtime/docker/ingress/types.ts +182 -0
- package/src/runtime/docker/log-frames.ts +121 -0
- package/src/runtime/docker/logs-server/index.ts +366 -0
- package/src/runtime/docker/spawn-server/context-key.ts +82 -0
- package/src/runtime/docker/spawn-server/index.ts +492 -0
- package/src/runtime/docker/spawn-server/protocol.ts +70 -0
- package/src/runtime/docker/spawn-server/spec.ts +122 -0
- package/src/runtime/docker/stats-server/disks.ts +161 -0
- package/src/runtime/docker/stats-server/index.ts +322 -0
- package/src/runtime/home-client/index.ts +71 -0
- package/src/runtime/log-line.ts +24 -0
- package/src/runtime/logs-client/index.ts +175 -0
- package/src/runtime/spawn-client/index.ts +93 -0
- package/src/runtime/spawn-client/kaniko.ts +79 -0
- package/src/runtime/stats-client/index.ts +65 -0
- package/src/runtime/stats-sample.ts +95 -0
- package/src/safe-json.ts +37 -0
- package/src/sandbox.ts +1209 -0
- package/src/schema/build-apps.ts +38 -0
- package/src/schema/home.ts +217 -0
- package/src/schema/host.ts +792 -0
- package/src/schema/inputs.ts +26 -0
- package/src/schema/nodes.ts +75 -0
- package/src/schema/peer.ts +400 -0
- package/src/schema/public-domain.ts +116 -0
- package/src/schema/remote.ts +74 -0
- package/src/security.ts +594 -0
- package/src/serialize.ts +21 -0
- package/src/service-definition.ts +284 -0
- package/src/service-url.ts +64 -0
- package/src/service.ts +39 -0
- package/src/services/index.ts +5 -0
- package/src/shared.ts +65 -0
- package/src/speculative-silo.ts +96 -0
- package/src/stable-secret.ts +14 -0
- package/src/surfaces.ts +264 -0
- package/src/tailwind-bin.ts +56 -0
- package/src/test/boot.ts +192 -0
- package/src/test/compile.ts +501 -0
- package/src/test/describe.ts +117 -0
- package/src/test/index.ts +11 -0
- package/src/test/ingress-fixture.ts +37 -0
- package/src/test/types.ts +318 -0
- package/src/trusted-headers.ts +79 -0
- package/src/types.ts +126 -0
- package/src/ui/components/aside.tsx +96 -0
- package/src/ui/components/badge.tsx +42 -0
- package/src/ui/components/button.tsx +59 -0
- package/src/ui/components/card.tsx +106 -0
- package/src/ui/components/checkbox.tsx +37 -0
- package/src/ui/components/code-block.tsx +29 -0
- package/src/ui/components/combobox.tsx +119 -0
- package/src/ui/components/config-field.tsx +284 -0
- package/src/ui/components/dialog.tsx +137 -0
- package/src/ui/components/domain-editor.tsx +317 -0
- package/src/ui/components/domain-list.ts +205 -0
- package/src/ui/components/dropdown-menu.tsx +73 -0
- package/src/ui/components/empty-state.tsx +51 -0
- package/src/ui/components/highlight.ts +59 -0
- package/src/ui/components/icons.tsx +101 -0
- package/src/ui/components/image-posture.tsx +85 -0
- package/src/ui/components/input.tsx +28 -0
- package/src/ui/components/label.tsx +16 -0
- package/src/ui/components/line-chart.tsx +113 -0
- package/src/ui/components/page.tsx +39 -0
- package/src/ui/components/permission-row.tsx +262 -0
- package/src/ui/components/permission-section.tsx +51 -0
- package/src/ui/components/pie-chart.tsx +245 -0
- package/src/ui/components/schema-block.tsx +60 -0
- package/src/ui/components/separator.tsx +42 -0
- package/src/ui/components/sheet.tsx +130 -0
- package/src/ui/components/sidebar.tsx +650 -0
- package/src/ui/components/silo-picker.tsx +510 -0
- package/src/ui/components/skeleton.tsx +13 -0
- package/src/ui/components/sonner.tsx +33 -0
- package/src/ui/components/switch.tsx +58 -0
- package/src/ui/components/table-view.ts +219 -0
- package/src/ui/components/table.tsx +506 -0
- package/src/ui/components/tabs.tsx +59 -0
- package/src/ui/components/timeago.tsx +84 -0
- package/src/ui/components/tooltip.tsx +56 -0
- package/src/ui/components.json +21 -0
- package/src/ui/dev/dev-shell.tsx +145 -0
- package/src/ui/home/home-grid.tsx +177 -0
- package/src/ui/hooks/use-mobile.ts +19 -0
- package/src/ui/index.ts +189 -0
- package/src/ui/install/adapter.ts +193 -0
- package/src/ui/install/deploy-targets-picker.tsx +185 -0
- package/src/ui/install/install-app-panel.tsx +912 -0
- package/src/ui/install/install-error.tsx +62 -0
- package/src/ui/install/perms-inactive.ts +50 -0
- package/src/ui/install/required-fields.ts +37 -0
- package/src/ui/install/tabs-form.tsx +557 -0
- package/src/ui/tokens.css +279 -0
- package/src/ui/utils.ts +78 -0
- package/src/ui-error.ts +139 -0
- package/src/ui-runtime/dev-landing-entry.tsx +258 -0
- package/src/ui-runtime/index.ts +83 -0
- package/src/ui-runtime/install-entry.tsx +119 -0
- package/src/ui-runtime/mount.ts +367 -0
- package/src/ui-runtime/runtime.css +56 -0
- package/src/ui-surface.ts +451 -0
- package/src/valkey.ts +43 -0
- package/src/worker/extract-tar.ts +125 -0
- package/src/worker/identity.ts +183 -0
- package/src/worker/index.ts +16 -0
- package/src/worker/public-ip.ts +92 -0
- package/src/worker/queue.ts +54 -0
- package/src/worker/reconcile.ts +127 -0
- package/src/worker/start-worker.ts +227 -0
- package/src/worker/ui-proxy.ts +335 -0
- package/src/worker/uplink.ts +523 -0
- package/src/worker/validate.ts +34 -0
- package/src/worker/verify-relay.ts +102 -0
- package/src/worker-protocol/app-dirs.ts +94 -0
- package/src/worker-protocol/home-proxy-paths.ts +76 -0
- package/src/worker-protocol/index.ts +55 -0
- package/src/worker-protocol/ingress.ts +166 -0
- package/src/worker-protocol/logs-socket-path.ts +50 -0
- package/src/worker-protocol/networks.ts +39 -0
- package/src/worker-protocol/node-version.ts +70 -0
- package/src/worker-protocol/silo-paths.ts +114 -0
- package/src/worker-protocol/spawn-paths.ts +165 -0
- package/src/worker-protocol/stats-socket-path.ts +40 -0
- package/src/worker-protocol/update-files.ts +77 -0
- package/src/worker-protocol/uplink/constants.ts +86 -0
- package/src/worker-protocol/uplink/errors.ts +61 -0
- package/src/worker-protocol/uplink/frame.ts +114 -0
- package/src/worker-protocol/uplink/identity.ts +40 -0
- package/src/worker-protocol/uplink/index.ts +63 -0
- package/src/worker-protocol/uplink/methods.ts +462 -0
- package/src/worker-protocol/uplink/mux.ts +646 -0
- package/src/worker-protocol/uplink/protocol.ts +11 -0
- package/src/worker-protocol/uplink/rpc.ts +309 -0
- package/src/worker-protocol/uplink/session.ts +540 -0
- package/src/worker-protocol/uplink/test-support.ts +188 -0
- package/src/worker-protocol/worker-mount.ts +27 -0
- package/src/worker-protocol/worker-state.ts +190 -0
- package/src/xml.ts +49 -0
- package/src/yaml.ts +15 -0
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
// Request/response over one mux stream. The OPEN header is UTF-8 JSON
|
|
2
|
+
// `{method, headers}`; the request body is that stream's DATA until END.
|
|
3
|
+
// The response is `[u16 headLen][head JSON][body…]` then END — the length
|
|
4
|
+
// prefix is what makes the head robust to fragment boundaries.
|
|
5
|
+
//
|
|
6
|
+
// The method names themselves are not defined here: the step that rehosts a
|
|
7
|
+
// call owns its schema.
|
|
8
|
+
|
|
9
|
+
import { MAX_PAYLOAD_BYTES, MAX_RPC_HEAD_BYTES } from "./constants.ts";
|
|
10
|
+
import { UplinkProtocolError } from "./errors.ts";
|
|
11
|
+
import { concatBytes, decodeUtf8, encodeUtf8 } from "./frame.ts";
|
|
12
|
+
import type { Mux, Stream } from "./mux.ts";
|
|
13
|
+
|
|
14
|
+
export interface RpcRequest {
|
|
15
|
+
method: string;
|
|
16
|
+
headers: Record<string, string>;
|
|
17
|
+
body: ReadableStream<Uint8Array>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface RpcResponse {
|
|
21
|
+
status: number;
|
|
22
|
+
headers: Record<string, string>;
|
|
23
|
+
body: ReadableStream<Uint8Array>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type RpcHandler = (req: RpcRequest) => RpcResponse | Promise<RpcResponse>;
|
|
27
|
+
|
|
28
|
+
export class Rpc {
|
|
29
|
+
readonly #mux: Mux;
|
|
30
|
+
readonly #handlers: Record<string, RpcHandler>;
|
|
31
|
+
|
|
32
|
+
constructor(mux: Mux, handlers: Record<string, RpcHandler>) {
|
|
33
|
+
this.#mux = mux;
|
|
34
|
+
this.#handlers = handlers;
|
|
35
|
+
mux.onOpen((stream, header) => {
|
|
36
|
+
void this.#serve(stream, header);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Resolves as soon as the response head is parsed; the body streams after. */
|
|
41
|
+
async call(
|
|
42
|
+
method: string,
|
|
43
|
+
headers: Record<string, string>,
|
|
44
|
+
body?: Uint8Array | ReadableStream<Uint8Array>,
|
|
45
|
+
opts?: { signal?: AbortSignal },
|
|
46
|
+
): Promise<RpcResponse> {
|
|
47
|
+
const header = encodeUtf8(JSON.stringify({ method, headers }));
|
|
48
|
+
if (header.length > MAX_PAYLOAD_BYTES) {
|
|
49
|
+
throw new RangeError(`OPEN header ${header.length} exceeds ${MAX_PAYLOAD_BYTES} bytes`);
|
|
50
|
+
}
|
|
51
|
+
const signal = opts?.signal;
|
|
52
|
+
signal?.throwIfAborted();
|
|
53
|
+
const stream = await this.#mux.open(header);
|
|
54
|
+
let aborted = false;
|
|
55
|
+
let abortReason: unknown;
|
|
56
|
+
const onAbort = () => {
|
|
57
|
+
aborted = true;
|
|
58
|
+
abortReason = signal?.reason;
|
|
59
|
+
stream.reset(reasonText(abortReason));
|
|
60
|
+
};
|
|
61
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
62
|
+
try {
|
|
63
|
+
// Not awaited: a handler may answer before it has drained the request
|
|
64
|
+
// body, and the two directions are independent. A write failure shows
|
|
65
|
+
// up on the read side.
|
|
66
|
+
void sendBody(stream, body).catch(() => {});
|
|
67
|
+
const { head, body: rest } = await readHead(stream.readable);
|
|
68
|
+
return { status: head.status, headers: head.headers, body: rest };
|
|
69
|
+
} catch (e) {
|
|
70
|
+
if (aborted) throw abortReason;
|
|
71
|
+
throw e;
|
|
72
|
+
} finally {
|
|
73
|
+
signal?.removeEventListener("abort", onAbort);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async #serve(stream: Stream, header: Uint8Array): Promise<void> {
|
|
78
|
+
let open: { method: string; headers: Record<string, string> };
|
|
79
|
+
try {
|
|
80
|
+
open = parseOpenHeader(header);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
stream.reset(asError(e).message);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const handler = this.#handlers[open.method];
|
|
86
|
+
let res: RpcResponse;
|
|
87
|
+
if (handler === undefined) res = jsonResponse(404, { error: "unknown method" });
|
|
88
|
+
else {
|
|
89
|
+
try {
|
|
90
|
+
res = await handler({ method: open.method, headers: open.headers, body: stream.readable });
|
|
91
|
+
} catch (e) {
|
|
92
|
+
res = textResponse(500, asError(e).message);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
await writeResponse(stream, res);
|
|
97
|
+
// A handler that ignored the body (404, or a throw) would otherwise
|
|
98
|
+
// leave the caller stalled on a window that never reopens.
|
|
99
|
+
if (!stream.readable.locked) await drain(stream.readable);
|
|
100
|
+
} catch {
|
|
101
|
+
stream.reset("response failed");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** JSON body helper so a method handler stays one line. */
|
|
107
|
+
export function jsonResponse(
|
|
108
|
+
status: number,
|
|
109
|
+
value: unknown,
|
|
110
|
+
headers?: Record<string, string>,
|
|
111
|
+
): RpcResponse {
|
|
112
|
+
return {
|
|
113
|
+
status,
|
|
114
|
+
headers: { "content-type": "application/json", ...headers },
|
|
115
|
+
body: bytesBody(encodeUtf8(JSON.stringify(value))),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Drains a request or response body and parses it as JSON. */
|
|
120
|
+
export async function readJson<T>(body: ReadableStream<Uint8Array>): Promise<T> {
|
|
121
|
+
return JSON.parse(decodeUtf8(await readAll(body))) as T;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function textResponse(status: number, text: string): RpcResponse {
|
|
125
|
+
return {
|
|
126
|
+
status,
|
|
127
|
+
headers: { "content-type": "text/plain; charset=utf-8" },
|
|
128
|
+
body: bytesBody(encodeUtf8(text)),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function bytesBody(bytes: Uint8Array): ReadableStream<Uint8Array> {
|
|
133
|
+
return new ReadableStream<Uint8Array>(
|
|
134
|
+
{
|
|
135
|
+
pull: (controller) => {
|
|
136
|
+
if (bytes.length > 0) controller.enqueue(bytes);
|
|
137
|
+
controller.close();
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
{ highWaterMark: 0 },
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async function sendBody(
|
|
145
|
+
stream: Stream,
|
|
146
|
+
body: Uint8Array | ReadableStream<Uint8Array> | undefined,
|
|
147
|
+
): Promise<void> {
|
|
148
|
+
if (body === undefined) {
|
|
149
|
+
await stream.writable.close();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (body instanceof Uint8Array) {
|
|
153
|
+
const writer = stream.writable.getWriter();
|
|
154
|
+
await writer.write(body);
|
|
155
|
+
await writer.close();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
await body.pipeTo(stream.writable);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async function writeResponse(stream: Stream, res: RpcResponse): Promise<void> {
|
|
162
|
+
const head = encodeUtf8(JSON.stringify({ status: res.status, headers: res.headers }));
|
|
163
|
+
if (head.length > MAX_RPC_HEAD_BYTES) {
|
|
164
|
+
throw new RangeError(`response head ${head.length} exceeds ${MAX_RPC_HEAD_BYTES} bytes`);
|
|
165
|
+
}
|
|
166
|
+
const prefix = new Uint8Array(2);
|
|
167
|
+
new DataView(prefix.buffer).setUint16(0, head.length);
|
|
168
|
+
const writer = stream.writable.getWriter();
|
|
169
|
+
await writer.write(concatBytes(prefix, head));
|
|
170
|
+
writer.releaseLock();
|
|
171
|
+
await res.body.pipeTo(stream.writable);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
interface ResponseHead {
|
|
175
|
+
status: number;
|
|
176
|
+
headers: Record<string, string>;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function readHead(
|
|
180
|
+
readable: ReadableStream<Uint8Array>,
|
|
181
|
+
): Promise<{ head: ResponseHead; body: ReadableStream<Uint8Array> }> {
|
|
182
|
+
const reader = readable.getReader();
|
|
183
|
+
let buf: Uint8Array = new Uint8Array(0);
|
|
184
|
+
for (;;) {
|
|
185
|
+
if (buf.length >= 2) {
|
|
186
|
+
const headLen = new DataView(buf.buffer, buf.byteOffset, buf.byteLength).getUint16(0);
|
|
187
|
+
if (buf.length >= 2 + headLen) {
|
|
188
|
+
const head = parseResponseHead(buf.subarray(2, 2 + headLen));
|
|
189
|
+
return { head, body: continuation(reader, buf.slice(2 + headLen)) };
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const { value, done } = await reader.read();
|
|
193
|
+
if (done) throw new UplinkProtocolError("response ended before its head");
|
|
194
|
+
buf = concatBytes(buf, value);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// The rest of the stream, after the bytes already pulled with the head.
|
|
199
|
+
function continuation(
|
|
200
|
+
reader: ReadableStreamDefaultReader<Uint8Array>,
|
|
201
|
+
rest: Uint8Array,
|
|
202
|
+
): ReadableStream<Uint8Array> {
|
|
203
|
+
let leftover: Uint8Array | undefined = rest.length > 0 ? rest : undefined;
|
|
204
|
+
return new ReadableStream<Uint8Array>(
|
|
205
|
+
{
|
|
206
|
+
pull: async (controller) => {
|
|
207
|
+
if (leftover !== undefined) {
|
|
208
|
+
const chunk = leftover;
|
|
209
|
+
leftover = undefined;
|
|
210
|
+
controller.enqueue(chunk);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
const { value, done } = await reader.read();
|
|
214
|
+
if (done) controller.close();
|
|
215
|
+
else controller.enqueue(value);
|
|
216
|
+
},
|
|
217
|
+
cancel: async (reason) => {
|
|
218
|
+
await reader.cancel(reason);
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
{ highWaterMark: 0 },
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function parseOpenHeader(header: Uint8Array): { method: string; headers: Record<string, string> } {
|
|
226
|
+
const value = parseJson(header, "OPEN header");
|
|
227
|
+
const method = (value as { method?: unknown }).method;
|
|
228
|
+
if (typeof method !== "string" || method === "") {
|
|
229
|
+
throw new UplinkProtocolError("OPEN header has no method");
|
|
230
|
+
}
|
|
231
|
+
return { method, headers: readHeaders(value, "OPEN header") };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function parseResponseHead(head: Uint8Array): ResponseHead {
|
|
235
|
+
const value = parseJson(head, "response head");
|
|
236
|
+
const status = (value as { status?: unknown }).status;
|
|
237
|
+
if (typeof status !== "number" || !Number.isInteger(status)) {
|
|
238
|
+
throw new UplinkProtocolError("response head has no status");
|
|
239
|
+
}
|
|
240
|
+
return { status, headers: readHeaders(value, "response head") };
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function parseJson(bytes: Uint8Array, what: string): Record<string, unknown> {
|
|
244
|
+
let value: unknown;
|
|
245
|
+
try {
|
|
246
|
+
value = JSON.parse(decodeUtf8(bytes));
|
|
247
|
+
} catch {
|
|
248
|
+
throw new UplinkProtocolError(`${what} is not JSON`);
|
|
249
|
+
}
|
|
250
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
251
|
+
throw new UplinkProtocolError(`${what} is not an object`);
|
|
252
|
+
}
|
|
253
|
+
return value as Record<string, unknown>;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function readHeaders(value: Record<string, unknown>, what: string): Record<string, string> {
|
|
257
|
+
const headers = value.headers;
|
|
258
|
+
if (headers === undefined) return {};
|
|
259
|
+
if (typeof headers !== "object" || headers === null || Array.isArray(headers)) {
|
|
260
|
+
throw new UplinkProtocolError(`${what} has non-object headers`);
|
|
261
|
+
}
|
|
262
|
+
const out: Record<string, string> = {};
|
|
263
|
+
for (const [key, entry] of Object.entries(headers)) {
|
|
264
|
+
if (typeof entry !== "string") {
|
|
265
|
+
throw new UplinkProtocolError(`${what} header ${key} is not a string`);
|
|
266
|
+
}
|
|
267
|
+
out[key] = entry;
|
|
268
|
+
}
|
|
269
|
+
return out;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async function readAll(body: ReadableStream<Uint8Array>): Promise<Uint8Array> {
|
|
273
|
+
const reader = body.getReader();
|
|
274
|
+
const chunks: Uint8Array[] = [];
|
|
275
|
+
let size = 0;
|
|
276
|
+
for (;;) {
|
|
277
|
+
const { value, done } = await reader.read();
|
|
278
|
+
if (done) break;
|
|
279
|
+
chunks.push(value);
|
|
280
|
+
size += value.length;
|
|
281
|
+
}
|
|
282
|
+
const out = new Uint8Array(size);
|
|
283
|
+
let offset = 0;
|
|
284
|
+
for (const chunk of chunks) {
|
|
285
|
+
out.set(chunk, offset);
|
|
286
|
+
offset += chunk.length;
|
|
287
|
+
}
|
|
288
|
+
return out;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function drain(body: ReadableStream<Uint8Array>): Promise<void> {
|
|
292
|
+
const reader = body.getReader();
|
|
293
|
+
try {
|
|
294
|
+
while (!(await reader.read()).done) {
|
|
295
|
+
/* discard */
|
|
296
|
+
}
|
|
297
|
+
} finally {
|
|
298
|
+
reader.releaseLock();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function asError(value: unknown): Error {
|
|
303
|
+
return value instanceof Error ? value : new Error(String(value));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function reasonText(reason: unknown): string {
|
|
307
|
+
if (reason === undefined || reason === null) return "aborted";
|
|
308
|
+
return reason instanceof Error ? reason.message : String(reason);
|
|
309
|
+
}
|