@midsummer-ai/sdk 0.0.0-stage → 0.1.0-next.1723

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 (79) hide show
  1. package/LICENSE +177 -0
  2. package/README.md +4 -2
  3. package/agents/contract.d.mts +9 -0
  4. package/agents/contract.mjs +221 -0
  5. package/agents/index.d.mts +128 -0
  6. package/agents/index.mjs +203 -0
  7. package/agents/stand-in.d.mts +35 -0
  8. package/agents/stand-in.mjs +149 -0
  9. package/ai/contract.d.mts +10 -0
  10. package/ai/contract.mjs +77 -0
  11. package/ai/index.d.mts +94 -0
  12. package/ai/index.mjs +193 -0
  13. package/ai/stand-in.d.mts +7 -0
  14. package/ai/stand-in.mjs +41 -0
  15. package/build-SQ44jwSa.d.mts +41 -0
  16. package/client.d.mts +45 -0
  17. package/client.mjs +106 -0
  18. package/connection-BHjDaLYA.mjs +2009 -0
  19. package/contract.d.mts +10 -0
  20. package/contract.mjs +469 -0
  21. package/database/build.d.mts +2 -0
  22. package/database/build.mjs +579 -0
  23. package/database/client.d.mts +60 -0
  24. package/database/client.mjs +133 -0
  25. package/database/contract.d.mts +11 -0
  26. package/database/contract.mjs +164 -0
  27. package/database/errors.d.mts +23 -0
  28. package/database/errors.mjs +13 -0
  29. package/database/server.d.mts +3 -0
  30. package/database/server.mjs +320 -0
  31. package/database/stand-in.d.mts +99 -0
  32. package/database/stand-in.mjs +430 -0
  33. package/email/client.d.mts +34 -0
  34. package/email/client.mjs +131 -0
  35. package/email/contract.d.mts +11 -0
  36. package/email/contract.mjs +99 -0
  37. package/email/index.d.mts +74 -0
  38. package/email/index.mjs +59 -0
  39. package/email/smtp.d.mts +22 -0
  40. package/email/smtp.mjs +296 -0
  41. package/email/stand-in.d.mts +13 -0
  42. package/email/stand-in.mjs +51 -0
  43. package/index-DdoK0y7D.d.mts +192 -0
  44. package/index-sgdyoA2P.d.mts +212 -0
  45. package/index.d.mts +2 -0
  46. package/index.mjs +137 -0
  47. package/names-DfCBKupJ.mjs +115 -0
  48. package/package.json +152 -4
  49. package/predict-Drfm_8pO.mjs +179 -0
  50. package/queues/contract.d.mts +13 -0
  51. package/queues/contract.mjs +278 -0
  52. package/queues/index.d.mts +184 -0
  53. package/queues/index.mjs +126 -0
  54. package/queues/stand-in.d.mts +7 -0
  55. package/queues/stand-in.mjs +110 -0
  56. package/queues/wire.d.mts +9 -0
  57. package/queues/wire.mjs +199 -0
  58. package/s3.d.mts +43 -0
  59. package/s3.mjs +1003 -0
  60. package/schema-BF-Y1EVu.mjs +142 -0
  61. package/schema-DS8x1Dn8.mjs +115 -0
  62. package/schema-f7E9uzUn.d.mts +74 -0
  63. package/server-C7O7CICU.d.mts +182 -0
  64. package/socket.d.mts +67 -0
  65. package/socket.mjs +263 -0
  66. package/stand-in.d.mts +7 -0
  67. package/stand-in.mjs +185 -0
  68. package/store-BA8K-bAR.mjs +237 -0
  69. package/tabs-DEeMee7M.mjs +457 -0
  70. package/vector/contract.d.mts +10 -0
  71. package/vector/contract.mjs +635 -0
  72. package/vector/index.d.mts +2 -0
  73. package/vector/index.mjs +108 -0
  74. package/vector/stand-in.d.mts +11 -0
  75. package/vector/stand-in.mjs +169 -0
  76. package/vector/wire.d.mts +25 -0
  77. package/vector/wire.mjs +175 -0
  78. package/wire.d.mts +54 -0
  79. package/wire.mjs +731 -0
package/ai/index.mjs ADDED
@@ -0,0 +1,193 @@
1
+ //#region src/ai/index.ts
2
+ var AiError = class extends Error {
3
+ code;
4
+ constructor(code, why) {
5
+ super(why);
6
+ this.code = code;
7
+ this.name = "AiError";
8
+ }
9
+ };
10
+ /** Checks a question before it leaves the caller. */
11
+ function checked(input) {
12
+ if (Boolean(input.prompt) === Boolean(input.messages)) throw new AiError("invalid", "Give a prompt or messages.");
13
+ if (input.messages && !input.messages.length) throw new AiError("invalid", "Give at least one message.");
14
+ if (input.maxTokens !== void 0 && !(Number.isInteger(input.maxTokens) && input.maxTokens >= 1 && input.maxTokens <= 32e3)) throw new AiError("invalid", "maxTokens is a whole number from 1 to 32,000.");
15
+ if (input.temperature !== void 0 && !(input.temperature >= 0 && input.temperature <= 2)) throw new AiError("invalid", "temperature is from 0 to 2.");
16
+ return input;
17
+ }
18
+ function ai(engine) {
19
+ return {
20
+ async complete(input) {
21
+ let text = "";
22
+ for await (const event of engine.ask(checked(input))) if ("text" in event) text += event.text;
23
+ else return {
24
+ ...event.done,
25
+ text
26
+ };
27
+ throw new AiError("unavailable", "The answer ended early. Try again.");
28
+ },
29
+ stream(input) {
30
+ let settle, fail;
31
+ const done = new Promise((resolve, reject) => {
32
+ settle = resolve;
33
+ fail = reject;
34
+ });
35
+ done.catch(() => void 0);
36
+ let asked;
37
+ try {
38
+ asked = checked(input);
39
+ } catch (error) {
40
+ fail(error);
41
+ return {
42
+ done,
43
+ [Symbol.asyncIterator]: () => ({ next: () => Promise.reject(error) })
44
+ };
45
+ }
46
+ async function* texts() {
47
+ try {
48
+ for await (const event of engine.ask(asked)) if ("text" in event) yield event.text;
49
+ else return settle(event.done);
50
+ throw new AiError("unavailable", "The answer ended early. Try again.");
51
+ } catch (error) {
52
+ fail(error);
53
+ throw error;
54
+ } finally {
55
+ fail(new AiError("unavailable", "The answer was not read to its end."));
56
+ }
57
+ }
58
+ return {
59
+ done,
60
+ [Symbol.asyncIterator]: texts
61
+ };
62
+ }
63
+ };
64
+ }
65
+ /** One line of the wire, checked whole: a piece of text, what the call spent, or why it stopped. Anything else is a broken answer. */
66
+ function parsed(line) {
67
+ const broken = () => new AiError("unavailable", "The answer could not be read. Try again.");
68
+ let event;
69
+ try {
70
+ event = JSON.parse(line);
71
+ } catch {
72
+ throw broken();
73
+ }
74
+ const object = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
75
+ const count = (value) => typeof value === "number" && Number.isInteger(value) && value >= 0;
76
+ if (!object(event)) throw broken();
77
+ if (typeof event.text === "string") return { text: event.text };
78
+ const done = event.done;
79
+ if (object(done)) {
80
+ if (typeof done.model !== "string" || !count(done.inputTokens) || !count(done.outputTokens) || typeof done.credits !== "number" || !(done.credits >= 0) || typeof done.catalogue !== "string") throw broken();
81
+ return { done: {
82
+ model: done.model,
83
+ inputTokens: done.inputTokens,
84
+ outputTokens: done.outputTokens,
85
+ credits: done.credits,
86
+ catalogue: done.catalogue
87
+ } };
88
+ }
89
+ const error = event.error;
90
+ if (object(error) && (error.code === void 0 || typeof error.code === "string") && (error.message === void 0 || typeof error.message === "string")) return { error: {
91
+ ...typeof error.code === "string" ? { code: error.code } : {},
92
+ ...typeof error.message === "string" ? { message: error.message } : {}
93
+ } };
94
+ throw broken();
95
+ }
96
+ const CODES = {
97
+ 400: "invalid",
98
+ 401: "forbidden",
99
+ 403: "forbidden",
100
+ 409: "conflict",
101
+ 429: "quota"
102
+ };
103
+ const CODE_NAMES = [
104
+ "invalid",
105
+ "forbidden",
106
+ "quota",
107
+ "conflict",
108
+ "unavailable"
109
+ ];
110
+ /**
111
+ * The platform's AI Gateway at `origin` (the API's), for the AI key `key`: each call is one POST to
112
+ * `<origin>/v1/ai/ask`, which replies with newline-delimited JSON events as the Model writes.
113
+ */
114
+ function aiClient(origin, options) {
115
+ const url = `${origin.replace(/\/+$/, "")}/v1/ai/ask`;
116
+ const send = options.fetch ?? ((input, init) => fetch(input, init));
117
+ return { async *ask(input) {
118
+ const leave = new AbortController();
119
+ let response;
120
+ try {
121
+ response = await send(url, {
122
+ method: "POST",
123
+ signal: leave.signal,
124
+ headers: { "Content-Type": "application/json" },
125
+ body: JSON.stringify({
126
+ ...input,
127
+ key: options.key,
128
+ stream: true
129
+ })
130
+ });
131
+ } catch {
132
+ throw new AiError("unavailable", "The AI Gateway could not be reached. Try again.");
133
+ }
134
+ if (!response.ok || !response.body) {
135
+ const why = await response.json().then((body) => typeof body.error === "string" ? body.error : void 0, () => void 0);
136
+ throw new AiError(CODES[response.status] ?? "unavailable", why ?? `The AI Gateway answered ${response.status}.`);
137
+ }
138
+ try {
139
+ yield* eventsOf(response.body);
140
+ } finally {
141
+ leave.abort();
142
+ }
143
+ } };
144
+ }
145
+ /** The events of one call's newline-delimited JSON, as the AI Gateway streams them; cancelled when the reader leaves. */
146
+ async function* eventsOf(body) {
147
+ const decoder = new TextDecoder();
148
+ let pending = "";
149
+ const reader = body.getReader();
150
+ try {
151
+ while (true) {
152
+ let read;
153
+ try {
154
+ read = await reader.read();
155
+ } catch {
156
+ throw new AiError("unavailable", "The answer was cut off. Try again.");
157
+ }
158
+ pending += read.done ? decoder.decode() : decoder.decode(read.value, { stream: true });
159
+ let end;
160
+ while ((end = pending.indexOf("\n")) >= 0) {
161
+ const line = pending.slice(0, end).trim();
162
+ pending = pending.slice(end + 1);
163
+ if (!line) continue;
164
+ const event = parsed(line);
165
+ if ("error" in event) throw new AiError(event.error.code && CODE_NAMES.includes(event.error.code) ? event.error.code : "unavailable", event.error.message ?? "The model could not answer. Try again.");
166
+ yield event;
167
+ if ("done" in event) return;
168
+ }
169
+ if (read.done) throw new AiError("unavailable", "The answer ended early. Try again.");
170
+ }
171
+ } finally {
172
+ await reader.cancel().catch(() => void 0);
173
+ }
174
+ }
175
+ /**
176
+ * What Midsummer hands a Deployment as `context.ai`: the Team's AI Gateway, with no key. Each call
177
+ * streams the events `aiClient` reads.
178
+ *
179
+ * const answer = await ai(boundAi(context.ai)).complete({ prompt: "Hello" })
180
+ */
181
+ function boundAi(handle) {
182
+ return { async *ask(input) {
183
+ let body;
184
+ try {
185
+ body = await handle.ask(input);
186
+ } catch (error) {
187
+ throw new AiError("unavailable", error instanceof Error ? error.message : "The AI Gateway could not be reached. Try again.");
188
+ }
189
+ yield* eventsOf(body);
190
+ } };
191
+ }
192
+ //#endregion
193
+ export { AiError, ai, aiClient, boundAi };
@@ -0,0 +1,7 @@
1
+ import { AiEngine } from "./index.mjs";
2
+ //#region src/ai/stand-in.d.ts
3
+ declare function aiStandIn(options?: {
4
+ readonly models?: ReadonlyArray<string>;
5
+ }): AiEngine;
6
+ //#endregion
7
+ export { aiStandIn };
@@ -0,0 +1,41 @@
1
+ import { AiError } from "./index.mjs";
2
+ //#region src/ai/stand-in.ts
3
+ /**
4
+ * AI's Stand-in: Model calls replied to on this computer, for `midsummer dev` and tests, with no
5
+ * Model behind them. It passes the contract (`@midsummer-ai/sdk/ai/contract`); its replies are only
6
+ * an echo of the question, so code can be run end to end without spending credits.
7
+ *
8
+ * const models = ai(aiStandIn())
9
+ * await models.complete({ prompt: "Hi" }) // { text: "You asked: Hi", model: "stand-in", credits: 0, … }
10
+ *
11
+ * It offers the Models it is given (`stand-in` unless told), the first the default. Tokens are
12
+ * counted at about four characters a token, and cost nothing.
13
+ */
14
+ function aiStandIn(options = {}) {
15
+ const models = options.models?.length ? options.models : ["stand-in"];
16
+ const tokens = (text) => Math.ceil(text.length / 4);
17
+ return { async *ask(input) {
18
+ const model = input.model ?? models[0];
19
+ if (!models.includes(model)) throw new AiError("invalid", `There is no model ${model}.`);
20
+ const messages = [...input.system ? [{
21
+ role: "system",
22
+ content: input.system
23
+ }] : [], ...input.messages ?? [{
24
+ role: "user",
25
+ content: input.prompt ?? ""
26
+ }]];
27
+ const last = messages.filter((message) => message.role !== "system").at(-1);
28
+ if (last?.role !== "user" || !last.content.trim()) throw new AiError("invalid", "End with a user message that asks something.");
29
+ const answer = `You asked: ${last.content}`.slice(0, (input.maxTokens ?? 1024) * 4);
30
+ for (const piece of answer.match(/\S+\s*/g) ?? []) yield { text: piece };
31
+ yield { done: {
32
+ model,
33
+ inputTokens: tokens(messages.map((message) => message.content).join("")),
34
+ outputTokens: tokens(answer),
35
+ credits: 0,
36
+ catalogue: "stand-in"
37
+ } };
38
+ } };
39
+ }
40
+ //#endregion
41
+ export { aiStandIn };
@@ -0,0 +1,41 @@
1
+ import { r as Shape, t as IndexDeclaration } from "./schema-f7E9uzUn.mjs";
2
+ //#region src/database/build.d.ts
3
+ /** A file the Database would refuse; the message gives the file, and the line when there is one. */
4
+ declare class Refused extends Error {
5
+ constructor(message: string);
6
+ }
7
+ interface BuiltFunction {
8
+ readonly name: string;
9
+ readonly kind: "query" | "mutation" | "action" | "internalQuery" | "internalMutation" | "internalAction";
10
+ /** The built file it is in, and the export that holds it. */
11
+ readonly file: string;
12
+ readonly export: string;
13
+ /** Its `args` fields and `returns` shape as written, for its types: null where they are not literals. */
14
+ readonly args: Readonly<Record<string, Shape | null>> | null;
15
+ readonly returns: Shape | null;
16
+ }
17
+ interface Built {
18
+ /** The folder's modules as JavaScript, by `<name>.mjs`, each import of the folder naming its built file. */
19
+ readonly files: Readonly<Record<string, string>>;
20
+ /** Which author file each built one came from. */
21
+ readonly from: Readonly<Record<string, string>>;
22
+ readonly functions: ReadonlyArray<BuiltFunction>;
23
+ /** The schema's tables by field, and their indexes; null without `schema.ts`. */
24
+ readonly tables: Readonly<Record<string, Readonly<Record<string, Shape>>>> | null;
25
+ readonly indexes: Readonly<Record<string, Readonly<Record<string, IndexDeclaration>>>> | null;
26
+ /** The files of `db/_generated/`. */
27
+ readonly generated: {
28
+ readonly "api.mjs": string;
29
+ readonly "api.d.ts": string;
30
+ readonly "dataModel.d.ts": string;
31
+ readonly "server.d.ts": string;
32
+ };
33
+ }
34
+ /** Whether a file directly in `db/` is a source: types (`.d.ts`) and dotfiles are none. */
35
+ declare const isSource: (name: string) => boolean;
36
+ /** Builds a `db/` folder's sources: `{ <file>: text }`, its top level. Throws `Refused`. */
37
+ declare function buildDatabase(sources: Readonly<Record<string, string>>, strip: (ts: string) => string): Built;
38
+ /** A shape as a TypeScript type. */
39
+ declare function typeOf(shape: Shape | null | undefined): string;
40
+ //#endregion
41
+ export { isSource as a, buildDatabase as i, BuiltFunction as n, typeOf as o, Refused as r, Built as t };
package/client.d.mts ADDED
@@ -0,0 +1,45 @@
1
+ import { m as StorageEngine } from "./index-sgdyoA2P.mjs";
2
+ import { S3Credential } from "./s3.mjs";
3
+ import { Transport } from "./wire.mjs";
4
+ import { socket } from "./socket.mjs";
5
+ //#region src/client.d.ts
6
+ declare const SOCKET_PATH = "/storage/socket";
7
+ declare const FRAMES_PATH = "/storage/frames";
8
+ type WebSocketLike = NonNullable<Parameters<typeof socket>[1]["WebSocket"]>;
9
+ type Fetch = (url: string, init: RequestInit) => Promise<Response>;
10
+ /**
11
+ * Storage's engine at `origin` (the API's, `https://api.<site>`) for the Storage Key `key`: one socket,
12
+ * batched HTTP where it cannot open. `close` ends the connection; the next call opens another.
13
+ */
14
+ declare function client(origin: string, options: {
15
+ readonly key: string;
16
+ readonly secret: string;
17
+ readonly fetch?: Fetch;
18
+ readonly WebSocket?: WebSocketLike;
19
+ readonly clock?: () => number;
20
+ }): StorageEngine & {
21
+ close(): void;
22
+ };
23
+ /**
24
+ * A transport over HTTP signed with a Storage Key: each frame is one POST to `url`, replied to by
25
+ * `serveSigned`, and sent once more when the request or its answer's body fails and the frame is
26
+ * `repeatable`. A refused key is `forbidden`; a server that cannot be reached, or replies with anything but
27
+ * a frame, is `unavailable`.
28
+ */
29
+ declare function signedHttp(url: string, options: {
30
+ readonly key: string;
31
+ readonly secret: string;
32
+ readonly fetch?: Fetch;
33
+ readonly clock?: () => number;
34
+ }): Transport;
35
+ /**
36
+ * Serves `signedHttp` transports: a POST of one frame, signed by a key `credentials` knows, gets one
37
+ * answer frame from `engine` as that key reaches it (`keyed`). Anything else is 405 or 401.
38
+ */
39
+ declare function serveSigned(options: {
40
+ readonly engine: StorageEngine;
41
+ readonly credentials: (accessKeyId: string, named?: ReadonlyArray<string>) => S3Credential | null | Promise<S3Credential | null>;
42
+ readonly clock?: () => number;
43
+ }): (request: Request) => Promise<Response>;
44
+ //#endregion
45
+ export { FRAMES_PATH, SOCKET_PATH, client, serveSigned, signedHttp };
package/client.mjs ADDED
@@ -0,0 +1,106 @@
1
+ import { h as StorageError } from "./names-DfCBKupJ.mjs";
2
+ import { bucketsOf, remote, repeatable, serve } from "./wire.mjs";
3
+ import { keyed, sameHex, sign, socket } from "./socket.mjs";
4
+ //#region src/client.ts
5
+ /**
6
+ * Storage from code holding a Storage Key, as an app reaches it: calls are written as usual and travel
7
+ * over one WebSocket, so a client pays for one request when it connects, not one a call. Where a
8
+ * connection cannot open, they travel as batched HTTP, signed with the same key.
9
+ *
10
+ * const files = storage(client("https://api.<site>", { key, secret }))
11
+ * await files.bucket("avatars").put("ada.png", bytes)
12
+ *
13
+ * The calls made in the same turn travel as one batch frame either way. A call whose connection drops
14
+ * before it gets a reply (a server going away on deploy) is sent again on a new connection when the wire
15
+ * says repeating it is safe (`repeatable` in `@midsummer-ai/sdk/wire`), and is otherwise `unavailable`.
16
+ *
17
+ * Signed HTTP is one POST of a frame to `<origin>/storage/frames`, with
18
+ * `authorization: Midsummer-Key key=<id>, time=<ms>, signature=<hex>`: the HMAC-SHA256, under the key's
19
+ * secret, of the time and the frame's SHA-256 (`<time>\n<hex>`). The secret never travels, and a server
20
+ * (`serveSigned`) refuses a signature more than five minutes from its own clock.
21
+ */
22
+ const SOCKET_PATH = "/storage/socket";
23
+ const FRAMES_PATH = "/storage/frames";
24
+ const SKEW_MS = 3e5;
25
+ /**
26
+ * Storage's engine at `origin` (the API's, `https://api.<site>`) for the Storage Key `key`: one socket,
27
+ * batched HTTP where it cannot open. `close` ends the connection; the next call opens another.
28
+ */
29
+ function client(origin, options) {
30
+ const base = origin.replace(/\/+$/, "");
31
+ const http = signedHttp(`${base}${FRAMES_PATH}`, options);
32
+ const transport = socket(`${base.replace(/^http/, "ws")}${SOCKET_PATH}`, {
33
+ ...options,
34
+ connections: 1,
35
+ fallback: http
36
+ });
37
+ return {
38
+ ...remote(transport, { batch: true }),
39
+ close: () => transport.close()
40
+ };
41
+ }
42
+ async function sha256(bytes) {
43
+ return [...new Uint8Array(await crypto.subtle.digest("SHA-256", bytes))].map((b) => b.toString(16).padStart(2, "0")).join("");
44
+ }
45
+ /**
46
+ * A transport over HTTP signed with a Storage Key: each frame is one POST to `url`, replied to by
47
+ * `serveSigned`, and sent once more when the request or its answer's body fails and the frame is
48
+ * `repeatable`. A refused key is `forbidden`; a server that cannot be reached, or replies with anything but
49
+ * a frame, is `unavailable`.
50
+ */
51
+ function signedHttp(url, options) {
52
+ const send = options.fetch ?? ((to, init) => fetch(to, init));
53
+ const clock = options.clock ?? Date.now;
54
+ return { async send(frame) {
55
+ const time = clock();
56
+ const signature = await sign(options.secret, `${time}\n${await sha256(frame)}`);
57
+ for (let sends = 1;; sends += 1) {
58
+ const lost = () => {
59
+ if (sends === 2 || !repeatable(frame)) throw new StorageError("unavailable", "Storage could not be reached");
60
+ };
61
+ let response;
62
+ try {
63
+ response = await send(url, {
64
+ method: "POST",
65
+ headers: {
66
+ authorization: `Midsummer-Key key=${options.key}, time=${time}, signature=${signature}`,
67
+ "content-type": "application/octet-stream"
68
+ },
69
+ body: frame
70
+ });
71
+ } catch {
72
+ lost();
73
+ continue;
74
+ }
75
+ if (response.status === 401 || response.status === 403) throw new StorageError("forbidden", "Storage refused the key");
76
+ if (response.status !== 200) throw new StorageError("unavailable", `Storage answered ${response.status}`);
77
+ try {
78
+ return new Uint8Array(await response.arrayBuffer());
79
+ } catch {
80
+ lost();
81
+ }
82
+ }
83
+ } };
84
+ }
85
+ /**
86
+ * Serves `signedHttp` transports: a POST of one frame, signed by a key `credentials` knows, gets one
87
+ * answer frame from `engine` as that key reaches it (`keyed`). Anything else is 405 or 401.
88
+ */
89
+ function serveSigned(options) {
90
+ const clock = options.clock ?? Date.now;
91
+ return async (request) => {
92
+ if (request.method !== "POST") return new Response(null, { status: 405 });
93
+ const said = /^Midsummer-Key key=([^,\s]+), time=(\d+), signature=([0-9a-f]{64})$/.exec(request.headers.get("authorization") ?? "");
94
+ const frame = new Uint8Array(await request.arrayBuffer());
95
+ if (!said || Math.abs(clock() - Number(said[2])) > SKEW_MS) return new Response(null, { status: 401 });
96
+ const credential = await options.credentials(said[1], bucketsOf(frame));
97
+ if (!credential || !sameHex(await sign(credential.secret, `${said[2]}\n${await sha256(frame)}`), said[3])) return new Response(null, { status: 401 });
98
+ const answer = await serve(keyed(options.engine, credential))(frame);
99
+ return new Response(answer, {
100
+ status: 200,
101
+ headers: { "content-type": "application/octet-stream" }
102
+ });
103
+ };
104
+ }
105
+ //#endregion
106
+ export { FRAMES_PATH, SOCKET_PATH, client, serveSigned, signedHttp };