@finalai/agent-provider 0.1.0
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.
Potentially problematic release.
This version of @finalai/agent-provider might be problematic. Click here for more details.
- package/README.md +12 -0
- package/dist/modules/channel-context.d.mts +102 -0
- package/dist/modules/channel-context.mjs +52 -0
- package/dist/modules/channel-context.mjs.map +7 -0
- package/dist/modules/channel-socket.d.mts +65 -0
- package/dist/modules/channel-socket.mjs +188 -0
- package/dist/modules/channel-socket.mjs.map +7 -0
- package/dist/modules/channel.d.mts +382 -0
- package/dist/modules/channel.mjs +146 -0
- package/dist/modules/channel.mjs.map +7 -0
- package/dist/modules/conformance/agent.d.mts +20 -0
- package/dist/modules/conformance/agent.mjs +623 -0
- package/dist/modules/conformance/agent.mjs.map +7 -0
- package/dist/modules/conformance/fixtures.d.mts +41 -0
- package/dist/modules/conformance/fixtures.mjs +190 -0
- package/dist/modules/conformance/fixtures.mjs.map +7 -0
- package/dist/modules/conformance/harness-kit.d.mts +82 -0
- package/dist/modules/conformance/harness-kit.mjs +1303 -0
- package/dist/modules/conformance/harness-kit.mjs.map +7 -0
- package/dist/modules/conformance/image.d.mts +2 -0
- package/dist/modules/conformance/image.mjs +79 -0
- package/dist/modules/conformance/image.mjs.map +7 -0
- package/dist/modules/conformance/index.d.mts +8 -0
- package/dist/modules/conformance/index.mjs +12 -0
- package/dist/modules/conformance/index.mjs.map +7 -0
- package/dist/modules/conformance/local-harness.d.mts +25 -0
- package/dist/modules/conformance/local-harness.mjs +108 -0
- package/dist/modules/conformance/local-harness.mjs.map +7 -0
- package/dist/modules/conformance/protocol.d.mts +30 -0
- package/dist/modules/conformance/protocol.mjs +56 -0
- package/dist/modules/conformance/protocol.mjs.map +7 -0
- package/dist/modules/conformance/sandbox-kit.d.mts +19 -0
- package/dist/modules/conformance/sandbox-kit.mjs +320 -0
- package/dist/modules/conformance/sandbox-kit.mjs.map +7 -0
- package/dist/modules/conformance/scripted-model/chat-completions.d.mts +30 -0
- package/dist/modules/conformance/scripted-model/chat-completions.mjs +125 -0
- package/dist/modules/conformance/scripted-model/chat-completions.mjs.map +7 -0
- package/dist/modules/conformance/scripted-model/directives.d.mts +84 -0
- package/dist/modules/conformance/scripted-model/directives.mjs +304 -0
- package/dist/modules/conformance/scripted-model/directives.mjs.map +7 -0
- package/dist/modules/conformance/scripted-model/handler.d.mts +14 -0
- package/dist/modules/conformance/scripted-model/handler.mjs +133 -0
- package/dist/modules/conformance/scripted-model/handler.mjs.map +7 -0
- package/dist/modules/conformance/scripted-model/index.d.mts +6 -0
- package/dist/modules/conformance/scripted-model/index.mjs +7 -0
- package/dist/modules/conformance/scripted-model/index.mjs.map +7 -0
- package/dist/modules/conformance/scripted-model/messages.d.mts +39 -0
- package/dist/modules/conformance/scripted-model/messages.mjs +139 -0
- package/dist/modules/conformance/scripted-model/messages.mjs.map +7 -0
- package/dist/modules/conformance/scripted-model/responses.d.mts +62 -0
- package/dist/modules/conformance/scripted-model/responses.mjs +273 -0
- package/dist/modules/conformance/scripted-model/responses.mjs.map +7 -0
- package/dist/modules/conformance/scripted-model/sse.d.mts +15 -0
- package/dist/modules/conformance/scripted-model/sse.mjs +75 -0
- package/dist/modules/conformance/scripted-model/sse.mjs.map +7 -0
- package/dist/modules/conformance/v1-agent.d.mts +24 -0
- package/dist/modules/conformance/v1-agent.mjs +40 -0
- package/dist/modules/conformance/v1-agent.mjs.map +7 -0
- package/dist/modules/credential-routes.d.mts +24 -0
- package/dist/modules/credential-routes.mjs +25 -0
- package/dist/modules/credential-routes.mjs.map +7 -0
- package/dist/modules/credentials.d.mts +206 -0
- package/dist/modules/credentials.mjs +207 -0
- package/dist/modules/credentials.mjs.map +7 -0
- package/dist/modules/dynamic.d.mts +32 -0
- package/dist/modules/dynamic.mjs +77 -0
- package/dist/modules/dynamic.mjs.map +7 -0
- package/dist/modules/harness.d.mts +397 -0
- package/dist/modules/harness.mjs +213 -0
- package/dist/modules/harness.mjs.map +7 -0
- package/dist/modules/index.d.mts +15 -0
- package/dist/modules/index.mjs +40 -0
- package/dist/modules/index.mjs.map +7 -0
- package/dist/modules/json.d.mts +12 -0
- package/dist/modules/json.mjs +19 -0
- package/dist/modules/json.mjs.map +7 -0
- package/dist/modules/local-channel-socket.d.mts +23 -0
- package/dist/modules/local-channel-socket.mjs +141 -0
- package/dist/modules/local-channel-socket.mjs.map +7 -0
- package/dist/modules/manifest.d.mts +558 -0
- package/dist/modules/manifest.mjs +266 -0
- package/dist/modules/manifest.mjs.map +7 -0
- package/dist/modules/mcp-content.d.mts +21 -0
- package/dist/modules/mcp-content.mjs +86 -0
- package/dist/modules/mcp-content.mjs.map +7 -0
- package/dist/modules/provider-ref.d.mts +29 -0
- package/dist/modules/provider-ref.mjs +55 -0
- package/dist/modules/provider-ref.mjs.map +7 -0
- package/dist/modules/sandbox/bytes.d.mts +9 -0
- package/dist/modules/sandbox/bytes.mjs +154 -0
- package/dist/modules/sandbox/bytes.mjs.map +7 -0
- package/dist/modules/sandbox/client.d.mts +51 -0
- package/dist/modules/sandbox/client.mjs +500 -0
- package/dist/modules/sandbox/client.mjs.map +7 -0
- package/dist/modules/sandbox/confined-files.d.mts +22 -0
- package/dist/modules/sandbox/confined-files.mjs +142 -0
- package/dist/modules/sandbox/confined-files.mjs.map +7 -0
- package/dist/modules/sandbox/convenience.d.mts +10 -0
- package/dist/modules/sandbox/convenience.mjs +116 -0
- package/dist/modules/sandbox/convenience.mjs.map +7 -0
- package/dist/modules/sandbox/editor.d.mts +49 -0
- package/dist/modules/sandbox/editor.mjs +43 -0
- package/dist/modules/sandbox/editor.mjs.map +7 -0
- package/dist/modules/sandbox/frame-channel.d.mts +47 -0
- package/dist/modules/sandbox/frame-channel.mjs +153 -0
- package/dist/modules/sandbox/frame-channel.mjs.map +7 -0
- package/dist/modules/sandbox/index.d.mts +18 -0
- package/dist/modules/sandbox/index.mjs +70 -0
- package/dist/modules/sandbox/index.mjs.map +7 -0
- package/dist/modules/sandbox/mcp-channel.d.mts +9 -0
- package/dist/modules/sandbox/mcp-channel.mjs +77 -0
- package/dist/modules/sandbox/mcp-channel.mjs.map +7 -0
- package/dist/modules/sandbox/mcp-hosted.d.mts +46 -0
- package/dist/modules/sandbox/mcp-hosted.mjs +221 -0
- package/dist/modules/sandbox/mcp-hosted.mjs.map +7 -0
- package/dist/modules/sandbox/mcp-search.d.mts +51 -0
- package/dist/modules/sandbox/mcp-search.mjs +129 -0
- package/dist/modules/sandbox/mcp-search.mjs.map +7 -0
- package/dist/modules/sandbox/mcp-shell.d.mts +21 -0
- package/dist/modules/sandbox/mcp-shell.mjs +205 -0
- package/dist/modules/sandbox/mcp-shell.mjs.map +7 -0
- package/dist/modules/sandbox/mcp-testing.d.mts +11 -0
- package/dist/modules/sandbox/mcp-testing.mjs +33 -0
- package/dist/modules/sandbox/mcp-testing.mjs.map +7 -0
- package/dist/modules/sandbox/mcp.d.mts +83 -0
- package/dist/modules/sandbox/mcp.mjs +971 -0
- package/dist/modules/sandbox/mcp.mjs.map +7 -0
- package/dist/modules/sandbox/memory-sandbox.d.mts +32 -0
- package/dist/modules/sandbox/memory-sandbox.mjs +416 -0
- package/dist/modules/sandbox/memory-sandbox.mjs.map +7 -0
- package/dist/modules/sandbox/memory-shell.d.mts +29 -0
- package/dist/modules/sandbox/memory-shell.mjs +278 -0
- package/dist/modules/sandbox/memory-shell.mjs.map +7 -0
- package/dist/modules/sandbox/open-scope-node.d.mts +4 -0
- package/dist/modules/sandbox/open-scope-node.mjs +19 -0
- package/dist/modules/sandbox/open-scope-node.mjs.map +7 -0
- package/dist/modules/sandbox/open-scope.d.mts +16 -0
- package/dist/modules/sandbox/open-scope.mjs +66 -0
- package/dist/modules/sandbox/open-scope.mjs.map +7 -0
- package/dist/modules/sandbox/paths.d.mts +7 -0
- package/dist/modules/sandbox/paths.mjs +50 -0
- package/dist/modules/sandbox/paths.mjs.map +7 -0
- package/dist/modules/sandbox/rpc-peer.d.mts +35 -0
- package/dist/modules/sandbox/rpc-peer.mjs +224 -0
- package/dist/modules/sandbox/rpc-peer.mjs.map +7 -0
- package/dist/modules/sandbox/seeds.d.mts +26 -0
- package/dist/modules/sandbox/seeds.mjs +228 -0
- package/dist/modules/sandbox/seeds.mjs.map +7 -0
- package/dist/modules/sandbox/server.d.mts +66 -0
- package/dist/modules/sandbox/server.mjs +567 -0
- package/dist/modules/sandbox/server.mjs.map +7 -0
- package/dist/modules/sandbox/snapshots.d.mts +49 -0
- package/dist/modules/sandbox/snapshots.mjs +55 -0
- package/dist/modules/sandbox/snapshots.mjs.map +7 -0
- package/dist/modules/sandbox/stream-mux.d.mts +18 -0
- package/dist/modules/sandbox/stream-mux.mjs +285 -0
- package/dist/modules/sandbox/stream-mux.mjs.map +7 -0
- package/dist/modules/sandbox/testing.d.mts +25 -0
- package/dist/modules/sandbox/testing.mjs +64 -0
- package/dist/modules/sandbox/testing.mjs.map +7 -0
- package/dist/modules/sandbox/types.d.mts +352 -0
- package/dist/modules/sandbox/types.mjs +86 -0
- package/dist/modules/sandbox/types.mjs.map +7 -0
- package/dist/modules/sandbox/wire.d.mts +671 -0
- package/dist/modules/sandbox/wire.mjs +537 -0
- package/dist/modules/sandbox/wire.mjs.map +7 -0
- package/dist/modules/sandbox/workspace-root.d.mts +8 -0
- package/dist/modules/sandbox/workspace-root.mjs +79 -0
- package/dist/modules/sandbox/workspace-root.mjs.map +7 -0
- package/dist/modules/schemas.d.mts +121 -0
- package/dist/modules/schemas.mjs +146 -0
- package/dist/modules/schemas.mjs.map +7 -0
- package/dist/modules/semantic-events.d.mts +31 -0
- package/dist/modules/semantic-events.mjs +23 -0
- package/dist/modules/semantic-events.mjs.map +7 -0
- package/package.json +199 -0
package/README.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# @finalai/agent-provider
|
|
2
|
+
|
|
3
|
+
Contracts for custom harnesses, sandboxes, channels, and credential policies, plus
|
|
4
|
+
RPC/MCP projections and conformance tools for integration authors.
|
|
5
|
+
|
|
6
|
+
Implement the public provider interfaces and use `/conformance` to exercise them.
|
|
7
|
+
Use leaf entrypoints for sandbox wire clients, servers, MCP adapters, and local
|
|
8
|
+
channel sockets. These helpers adapt your integration to the provider contracts.
|
|
9
|
+
|
|
10
|
+
`/conformance` provides harness and sandbox checks, profiles, and a reference harness.
|
|
11
|
+
`/conformance/scripted-model` provides deterministic model handlers for exercising
|
|
12
|
+
your integration. The `final-agents conformance` CLI runs the harness checks.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';
|
|
2
|
+
import * as z from 'zod';
|
|
3
|
+
import { TurnPolicy, type ChannelPlatformIface } from "./channel.mjs";
|
|
4
|
+
import type { JsonObject, JsonValue } from "./json.mjs";
|
|
5
|
+
export declare enum ChannelPrincipalType {
|
|
6
|
+
User = "user",
|
|
7
|
+
Service = "service"
|
|
8
|
+
}
|
|
9
|
+
export interface ChannelAuthIface extends JsonObject {
|
|
10
|
+
principalId: string;
|
|
11
|
+
principalType: ChannelPrincipalType | `${ChannelPrincipalType}`;
|
|
12
|
+
authenticator: string;
|
|
13
|
+
attributes: JsonObject;
|
|
14
|
+
}
|
|
15
|
+
export declare const channelAuthSchema: z.ZodObject<{
|
|
16
|
+
principalId: z.ZodString;
|
|
17
|
+
principalType: z.ZodEnum<typeof ChannelPrincipalType>;
|
|
18
|
+
authenticator: z.ZodString;
|
|
19
|
+
attributes: z.ZodDefault<z.ZodType<JsonObject, unknown, z.core.$ZodTypeInternals<JsonObject, unknown>>>;
|
|
20
|
+
}, z.core.$strip>;
|
|
21
|
+
export declare const channelPrincipalMatches: (first: JsonObject | null, second: JsonObject | null) => boolean;
|
|
22
|
+
export interface ChannelSendInputIface {
|
|
23
|
+
content: ContentBlock[];
|
|
24
|
+
idempotencyKey: string;
|
|
25
|
+
auth: JsonObject | null;
|
|
26
|
+
context?: string[];
|
|
27
|
+
title?: string;
|
|
28
|
+
turnPolicy?: TurnPolicy | `${TurnPolicy}`;
|
|
29
|
+
}
|
|
30
|
+
export interface ChannelResponseInputIface {
|
|
31
|
+
pendingId: string;
|
|
32
|
+
outcome: JsonValue;
|
|
33
|
+
auth: JsonObject | null;
|
|
34
|
+
}
|
|
35
|
+
export interface ChannelSessionTargetIface {
|
|
36
|
+
address?: string;
|
|
37
|
+
sessionId?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface ChannelSessionSnapshotIface {
|
|
40
|
+
sessionId: string;
|
|
41
|
+
conversationId: string | null;
|
|
42
|
+
}
|
|
43
|
+
export interface ChannelCancelInputIface {
|
|
44
|
+
reason?: string;
|
|
45
|
+
throughMessageSeq?: number;
|
|
46
|
+
}
|
|
47
|
+
export interface ChannelSessionOperationsIface {
|
|
48
|
+
inspect(target: ChannelSessionTargetIface): Promise<ChannelSessionSnapshotIface | null>;
|
|
49
|
+
resolve(target: ChannelSessionTargetIface): Promise<string | null>;
|
|
50
|
+
send(target: ChannelSessionTargetIface, input: ChannelSendInputIface): Promise<{
|
|
51
|
+
sessionId: string;
|
|
52
|
+
turnId: string;
|
|
53
|
+
messageId: string;
|
|
54
|
+
}>;
|
|
55
|
+
respond(target: ChannelSessionTargetIface, input: ChannelResponseInputIface): Promise<{
|
|
56
|
+
resolved: boolean;
|
|
57
|
+
}>;
|
|
58
|
+
cancel(target: ChannelSessionTargetIface, input?: ChannelCancelInputIface): Promise<{
|
|
59
|
+
accepted: boolean;
|
|
60
|
+
}>;
|
|
61
|
+
close(target: ChannelSessionTargetIface): Promise<void>;
|
|
62
|
+
observe(target: ChannelSessionTargetIface, input: {
|
|
63
|
+
content: ContentBlock[];
|
|
64
|
+
idempotencyKey: string;
|
|
65
|
+
}): Promise<void>;
|
|
66
|
+
setConfigOption(target: ChannelSessionTargetIface, input: {
|
|
67
|
+
configId: string;
|
|
68
|
+
value: JsonValue;
|
|
69
|
+
}): Promise<void>;
|
|
70
|
+
park(target: ChannelSessionTargetIface): Promise<void>;
|
|
71
|
+
getState(target: ChannelSessionTargetIface): Promise<JsonObject>;
|
|
72
|
+
setState(target: ChannelSessionTargetIface, state: JsonObject): Promise<void>;
|
|
73
|
+
handoff(channel: string, target: JsonValue, input: ChannelSendInputIface): Promise<void>;
|
|
74
|
+
}
|
|
75
|
+
export interface ChannelSessionHandleIface {
|
|
76
|
+
inspect(): Promise<ChannelSessionSnapshotIface | null>;
|
|
77
|
+
resolveSession(): Promise<string | null>;
|
|
78
|
+
send(input: ChannelSendInputIface): ReturnType<ChannelSessionOperationsIface['send']>;
|
|
79
|
+
respond(input: ChannelResponseInputIface): ReturnType<ChannelSessionOperationsIface['respond']>;
|
|
80
|
+
cancel(input?: ChannelCancelInputIface): ReturnType<ChannelSessionOperationsIface['cancel']>;
|
|
81
|
+
close(): Promise<void>;
|
|
82
|
+
observe(input: {
|
|
83
|
+
content: ContentBlock[];
|
|
84
|
+
idempotencyKey: string;
|
|
85
|
+
}): Promise<void>;
|
|
86
|
+
setConfigOption(input: {
|
|
87
|
+
configId: string;
|
|
88
|
+
value: JsonValue;
|
|
89
|
+
}): Promise<void>;
|
|
90
|
+
park(): Promise<void>;
|
|
91
|
+
getState(): Promise<JsonObject>;
|
|
92
|
+
setState(state: JsonObject): Promise<void>;
|
|
93
|
+
}
|
|
94
|
+
export interface ChannelAddressContextIface {
|
|
95
|
+
from(address: string): ChannelSessionHandleIface;
|
|
96
|
+
resolveSession(address: string): Promise<string | null>;
|
|
97
|
+
attachSession(id: string): ChannelSessionHandleIface;
|
|
98
|
+
to(channel: string, target: JsonValue): {
|
|
99
|
+
send(input: ChannelSendInputIface): Promise<void>;
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export declare const channelAccessors: (platform: Pick<ChannelPlatformIface, "channelSessions">) => ChannelAddressContextIface;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { TurnPolicy } from "./channel.mjs";
|
|
3
|
+
import { jsonObjectSchema } from "./json.mjs";
|
|
4
|
+
var ChannelPrincipalType = /* @__PURE__ */ ((ChannelPrincipalType2) => {
|
|
5
|
+
ChannelPrincipalType2["User"] = "user";
|
|
6
|
+
ChannelPrincipalType2["Service"] = "service";
|
|
7
|
+
return ChannelPrincipalType2;
|
|
8
|
+
})(ChannelPrincipalType || {});
|
|
9
|
+
const channelAuthSchema = z.object({
|
|
10
|
+
principalId: z.string().min(1),
|
|
11
|
+
principalType: z.nativeEnum(ChannelPrincipalType),
|
|
12
|
+
authenticator: z.string().min(1),
|
|
13
|
+
attributes: jsonObjectSchema.default({})
|
|
14
|
+
});
|
|
15
|
+
const channelPrincipalMatches = (first, second) => {
|
|
16
|
+
if (first == null || second == null) return first == null && second == null;
|
|
17
|
+
const a = channelAuthSchema.parse(first), b = channelAuthSchema.parse(second);
|
|
18
|
+
return a.principalId === b.principalId && a.principalType === b.principalType && a.authenticator === b.authenticator;
|
|
19
|
+
};
|
|
20
|
+
const channelAccessors = (platform) => {
|
|
21
|
+
const operations = () => {
|
|
22
|
+
if (platform.channelSessions == null)
|
|
23
|
+
throw new Error("channel session operations are unavailable");
|
|
24
|
+
return platform.channelSessions;
|
|
25
|
+
};
|
|
26
|
+
const handle = (target) => ({
|
|
27
|
+
inspect: () => operations().inspect(target),
|
|
28
|
+
resolveSession: () => operations().resolve(target),
|
|
29
|
+
send: (input) => operations().send(target, input),
|
|
30
|
+
respond: (input) => operations().respond(target, input),
|
|
31
|
+
cancel: (input) => operations().cancel(target, input),
|
|
32
|
+
close: () => operations().close(target),
|
|
33
|
+
observe: (input) => operations().observe(target, input),
|
|
34
|
+
setConfigOption: (input) => operations().setConfigOption(target, input),
|
|
35
|
+
park: () => operations().park(target),
|
|
36
|
+
getState: () => operations().getState(target),
|
|
37
|
+
setState: (state) => operations().setState(target, state)
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
from: (address) => handle({ address }),
|
|
41
|
+
resolveSession: (address) => operations().resolve({ address }),
|
|
42
|
+
attachSession: (sessionId) => handle({ sessionId }),
|
|
43
|
+
to: (channel, target) => ({ send: (input) => operations().handoff(channel, target, input) })
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
export {
|
|
47
|
+
ChannelPrincipalType,
|
|
48
|
+
channelAccessors,
|
|
49
|
+
channelAuthSchema,
|
|
50
|
+
channelPrincipalMatches
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=channel-context.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/channel-context.ts"],
|
|
4
|
+
"sourcesContent": ["import type { ContentBlock } from '@agentclientprotocol/sdk/experimental/v2';\nimport * as z from 'zod';\nimport { TurnPolicy, type ChannelPlatformIface } from \"./channel.mjs\";\nimport type { JsonObject, JsonValue } from \"./json.mjs\";\nimport { jsonObjectSchema } from \"./json.mjs\";\n\nexport enum ChannelPrincipalType {\n User = 'user',\n Service = 'service',\n}\nexport interface ChannelAuthIface extends JsonObject {\n principalId: string;\n principalType: ChannelPrincipalType | `${ChannelPrincipalType}`;\n authenticator: string;\n attributes: JsonObject;\n}\nexport const channelAuthSchema = z.object({\n principalId: z.string().min(1),\n principalType: z.nativeEnum(ChannelPrincipalType),\n authenticator: z.string().min(1),\n attributes: jsonObjectSchema.default({}),\n});\nexport const channelPrincipalMatches = (\n first: JsonObject | null,\n second: JsonObject | null,\n): boolean => {\n if (first == null || second == null) return first == null && second == null;\n const a = channelAuthSchema.parse(first),\n b = channelAuthSchema.parse(second);\n return (\n a.principalId === b.principalId &&\n a.principalType === b.principalType &&\n a.authenticator === b.authenticator\n );\n};\n\nexport interface ChannelSendInputIface {\n content: ContentBlock[];\n idempotencyKey: string;\n auth: JsonObject | null;\n context?: string[];\n title?: string;\n turnPolicy?: TurnPolicy | `${TurnPolicy}`;\n}\n\nexport interface ChannelResponseInputIface {\n pendingId: string;\n outcome: JsonValue;\n auth: JsonObject | null;\n}\n\nexport interface ChannelSessionTargetIface {\n address?: string;\n sessionId?: string;\n}\n\nexport interface ChannelSessionSnapshotIface {\n sessionId: string;\n conversationId: string | null;\n}\n\nexport interface ChannelCancelInputIface {\n reason?: string;\n throughMessageSeq?: number;\n}\n\nexport interface ChannelSessionOperationsIface {\n inspect(target: ChannelSessionTargetIface): Promise<ChannelSessionSnapshotIface | null>;\n resolve(target: ChannelSessionTargetIface): Promise<string | null>;\n send(\n target: ChannelSessionTargetIface,\n input: ChannelSendInputIface,\n ): Promise<{ sessionId: string; turnId: string; messageId: string }>;\n respond(\n target: ChannelSessionTargetIface,\n input: ChannelResponseInputIface,\n ): Promise<{ resolved: boolean }>;\n cancel(\n target: ChannelSessionTargetIface,\n input?: ChannelCancelInputIface,\n ): Promise<{ accepted: boolean }>;\n close(target: ChannelSessionTargetIface): Promise<void>;\n observe(\n target: ChannelSessionTargetIface,\n input: { content: ContentBlock[]; idempotencyKey: string },\n ): Promise<void>;\n setConfigOption(\n target: ChannelSessionTargetIface,\n input: { configId: string; value: JsonValue },\n ): Promise<void>;\n park(target: ChannelSessionTargetIface): Promise<void>;\n getState(target: ChannelSessionTargetIface): Promise<JsonObject>;\n setState(target: ChannelSessionTargetIface, state: JsonObject): Promise<void>;\n handoff(channel: string, target: JsonValue, input: ChannelSendInputIface): Promise<void>;\n}\n\nexport interface ChannelSessionHandleIface {\n inspect(): Promise<ChannelSessionSnapshotIface | null>;\n resolveSession(): Promise<string | null>;\n send(input: ChannelSendInputIface): ReturnType<ChannelSessionOperationsIface['send']>;\n respond(input: ChannelResponseInputIface): ReturnType<ChannelSessionOperationsIface['respond']>;\n cancel(input?: ChannelCancelInputIface): ReturnType<ChannelSessionOperationsIface['cancel']>;\n close(): Promise<void>;\n observe(input: { content: ContentBlock[]; idempotencyKey: string }): Promise<void>;\n setConfigOption(input: { configId: string; value: JsonValue }): Promise<void>;\n park(): Promise<void>;\n getState(): Promise<JsonObject>;\n setState(state: JsonObject): Promise<void>;\n}\n\nexport interface ChannelAddressContextIface {\n from(address: string): ChannelSessionHandleIface;\n resolveSession(address: string): Promise<string | null>;\n attachSession(id: string): ChannelSessionHandleIface;\n to(channel: string, target: JsonValue): { send(input: ChannelSendInputIface): Promise<void> };\n}\n\n// Handles resolve at operation time. Only attachSession captures a session ID.\nexport const channelAccessors = (\n platform: Pick<ChannelPlatformIface, 'channelSessions'>,\n): ChannelAddressContextIface => {\n const operations = () => {\n if (platform.channelSessions == null)\n throw new Error('channel session operations are unavailable');\n return platform.channelSessions;\n };\n const handle = (target: ChannelSessionTargetIface): ChannelSessionHandleIface => ({\n inspect: () => operations().inspect(target),\n resolveSession: () => operations().resolve(target),\n send: (input) => operations().send(target, input),\n respond: (input) => operations().respond(target, input),\n cancel: (input) => operations().cancel(target, input),\n close: () => operations().close(target),\n observe: (input) => operations().observe(target, input),\n setConfigOption: (input) => operations().setConfigOption(target, input),\n park: () => operations().park(target),\n getState: () => operations().getState(target),\n setState: (state) => operations().setState(target, state),\n });\n return {\n from: (address) => handle({ address }),\n resolveSession: (address) => operations().resolve({ address }),\n attachSession: (sessionId) => handle({ sessionId }),\n to: (channel, target) => ({ send: (input) => operations().handoff(channel, target, input) }),\n };\n};\n"],
|
|
5
|
+
"mappings": "AACA,YAAY,OAAO;AACnB,SAAS,kBAA6C;AAEtD,SAAS,wBAAwB;AAE1B,IAAK,uBAAL,kBAAKA,0BAAL;AACL,EAAAA,sBAAA,UAAO;AACP,EAAAA,sBAAA,aAAU;AAFA,SAAAA;AAAA,GAAA;AAUL,MAAM,oBAAoB,EAAE,OAAO;AAAA,EACxC,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC7B,eAAe,EAAE,WAAW,oBAAoB;AAAA,EAChD,eAAe,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC/B,YAAY,iBAAiB,QAAQ,CAAC,CAAC;AACzC,CAAC;AACM,MAAM,0BAA0B,CACrC,OACA,WACY;AACZ,MAAI,SAAS,QAAQ,UAAU,KAAM,QAAO,SAAS,QAAQ,UAAU;AACvE,QAAM,IAAI,kBAAkB,MAAM,KAAK,GACrC,IAAI,kBAAkB,MAAM,MAAM;AACpC,SACE,EAAE,gBAAgB,EAAE,eACpB,EAAE,kBAAkB,EAAE,iBACtB,EAAE,kBAAkB,EAAE;AAE1B;AAoFO,MAAM,mBAAmB,CAC9B,aAC+B;AAC/B,QAAM,aAAa,MAAM;AACvB,QAAI,SAAS,mBAAmB;AAC9B,YAAM,IAAI,MAAM,4CAA4C;AAC9D,WAAO,SAAS;AAAA,EAClB;AACA,QAAM,SAAS,CAAC,YAAkE;AAAA,IAChF,SAAS,MAAM,WAAW,EAAE,QAAQ,MAAM;AAAA,IAC1C,gBAAgB,MAAM,WAAW,EAAE,QAAQ,MAAM;AAAA,IACjD,MAAM,CAAC,UAAU,WAAW,EAAE,KAAK,QAAQ,KAAK;AAAA,IAChD,SAAS,CAAC,UAAU,WAAW,EAAE,QAAQ,QAAQ,KAAK;AAAA,IACtD,QAAQ,CAAC,UAAU,WAAW,EAAE,OAAO,QAAQ,KAAK;AAAA,IACpD,OAAO,MAAM,WAAW,EAAE,MAAM,MAAM;AAAA,IACtC,SAAS,CAAC,UAAU,WAAW,EAAE,QAAQ,QAAQ,KAAK;AAAA,IACtD,iBAAiB,CAAC,UAAU,WAAW,EAAE,gBAAgB,QAAQ,KAAK;AAAA,IACtE,MAAM,MAAM,WAAW,EAAE,KAAK,MAAM;AAAA,IACpC,UAAU,MAAM,WAAW,EAAE,SAAS,MAAM;AAAA,IAC5C,UAAU,CAAC,UAAU,WAAW,EAAE,SAAS,QAAQ,KAAK;AAAA,EAC1D;AACA,SAAO;AAAA,IACL,MAAM,CAAC,YAAY,OAAO,EAAE,QAAQ,CAAC;AAAA,IACrC,gBAAgB,CAAC,YAAY,WAAW,EAAE,QAAQ,EAAE,QAAQ,CAAC;AAAA,IAC7D,eAAe,CAAC,cAAc,OAAO,EAAE,UAAU,CAAC;AAAA,IAClD,IAAI,CAAC,SAAS,YAAY,EAAE,MAAM,CAAC,UAAU,WAAW,EAAE,QAAQ,SAAS,QAAQ,KAAK,EAAE;AAAA,EAC5F;AACF;",
|
|
6
|
+
"names": ["ChannelPrincipalType"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import * as z from 'zod';
|
|
2
|
+
import type { WebSocketChannelRouteDefinitionIface } from "./channel.mjs";
|
|
3
|
+
import { type JsonValue } from "./json.mjs";
|
|
4
|
+
export declare const CHANNEL_SOCKET_MAX_MESSAGE_BYTES: number;
|
|
5
|
+
export declare const CHANNEL_SOCKET_MAX_LINE_BYTES: number;
|
|
6
|
+
export declare enum ChannelSocketCommandKind {
|
|
7
|
+
Message = "message",
|
|
8
|
+
Close = "close",
|
|
9
|
+
Context = "context"
|
|
10
|
+
}
|
|
11
|
+
export declare enum ChannelSocketActionKind {
|
|
12
|
+
Accept = "accept",
|
|
13
|
+
Message = "message",
|
|
14
|
+
Close = "close",
|
|
15
|
+
Reject = "reject"
|
|
16
|
+
}
|
|
17
|
+
export declare const validChannelSocketClose: (code: number) => boolean;
|
|
18
|
+
export declare const channelSocketCommandSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
19
|
+
data: z.ZodString;
|
|
20
|
+
binary: z.ZodBoolean;
|
|
21
|
+
kind: z.ZodLiteral<ChannelSocketCommandKind.Message>;
|
|
22
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
23
|
+
code: z.ZodOptional<z.ZodNumber>;
|
|
24
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
25
|
+
kind: z.ZodLiteral<ChannelSocketCommandKind.Close>;
|
|
26
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
27
|
+
kind: z.ZodLiteral<ChannelSocketCommandKind.Context>;
|
|
28
|
+
token: z.ZodString;
|
|
29
|
+
}, z.core.$strip>], "kind">;
|
|
30
|
+
export declare const channelSocketActionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
31
|
+
kind: z.ZodLiteral<ChannelSocketActionKind.Accept>;
|
|
32
|
+
protocol: z.ZodOptional<z.ZodString>;
|
|
33
|
+
metadata: z.ZodOptional<z.ZodType<JsonValue, unknown, z.core.$ZodTypeInternals<JsonValue, unknown>>>;
|
|
34
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
35
|
+
data: z.ZodString;
|
|
36
|
+
binary: z.ZodBoolean;
|
|
37
|
+
kind: z.ZodLiteral<ChannelSocketActionKind.Message>;
|
|
38
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
39
|
+
code: z.ZodOptional<z.ZodNumber>;
|
|
40
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
41
|
+
kind: z.ZodLiteral<ChannelSocketActionKind.Close>;
|
|
42
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
43
|
+
kind: z.ZodLiteral<ChannelSocketActionKind.Reject>;
|
|
44
|
+
status: z.ZodNumber;
|
|
45
|
+
headers: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
46
|
+
body: z.ZodString;
|
|
47
|
+
}, z.core.$strip>], "kind">;
|
|
48
|
+
export type ChannelSocketCommand = z.infer<typeof channelSocketCommandSchema>;
|
|
49
|
+
export type ChannelSocketAction = z.infer<typeof channelSocketActionSchema>;
|
|
50
|
+
export declare const channelSocketMessage: (data: string | Uint8Array) => {
|
|
51
|
+
data: string;
|
|
52
|
+
binary: boolean;
|
|
53
|
+
};
|
|
54
|
+
export declare const channelSocketData: (message: {
|
|
55
|
+
data: string;
|
|
56
|
+
binary: boolean;
|
|
57
|
+
}) => string | Uint8Array;
|
|
58
|
+
export declare const runChannelSocketHandler: (args: {
|
|
59
|
+
handler: WebSocketChannelRouteDefinitionIface["handler"];
|
|
60
|
+
context: Omit<Parameters<WebSocketChannelRouteDefinitionIface["handler"]>[0], "socket">;
|
|
61
|
+
messages: AsyncIterable<string | Uint8Array>;
|
|
62
|
+
signal: AbortSignal;
|
|
63
|
+
emit: (action: ChannelSocketAction) => void;
|
|
64
|
+
}) => Promise<void>;
|
|
65
|
+
export declare function readChannelSocketLines(body: ReadableStream<Uint8Array>, signal?: AbortSignal): AsyncGenerator<JsonValue>;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { jsonValueSchema } from "./json.mjs";
|
|
3
|
+
import { base64ToBytes, bytesToBase64 } from "./sandbox/bytes.mjs";
|
|
4
|
+
const CHANNEL_SOCKET_MAX_MESSAGE_BYTES = 1024 * 1024;
|
|
5
|
+
const CHANNEL_SOCKET_MAX_LINE_BYTES = 8 * 1024 * 1024;
|
|
6
|
+
var ChannelSocketCommandKind = /* @__PURE__ */ ((ChannelSocketCommandKind2) => {
|
|
7
|
+
ChannelSocketCommandKind2["Message"] = "message";
|
|
8
|
+
ChannelSocketCommandKind2["Close"] = "close";
|
|
9
|
+
ChannelSocketCommandKind2["Context"] = "context";
|
|
10
|
+
return ChannelSocketCommandKind2;
|
|
11
|
+
})(ChannelSocketCommandKind || {});
|
|
12
|
+
var ChannelSocketActionKind = /* @__PURE__ */ ((ChannelSocketActionKind2) => {
|
|
13
|
+
ChannelSocketActionKind2["Accept"] = "accept";
|
|
14
|
+
ChannelSocketActionKind2["Message"] = "message";
|
|
15
|
+
ChannelSocketActionKind2["Close"] = "close";
|
|
16
|
+
ChannelSocketActionKind2["Reject"] = "reject";
|
|
17
|
+
return ChannelSocketActionKind2;
|
|
18
|
+
})(ChannelSocketActionKind || {});
|
|
19
|
+
const messageFields = { data: z.string(), binary: z.boolean() };
|
|
20
|
+
const validChannelSocketClose = (code) => code === 1e3 || code === 1001 || code === 1002 || code === 1003 || code >= 1007 && code <= 1014 || code >= 3e3 && code <= 4999;
|
|
21
|
+
const closeFields = {
|
|
22
|
+
code: z.number().int().refine(validChannelSocketClose).optional(),
|
|
23
|
+
reason: z.string().refine((value) => new TextEncoder().encode(value).byteLength <= 123).optional()
|
|
24
|
+
};
|
|
25
|
+
const channelSocketCommandSchema = z.discriminatedUnion("kind", [
|
|
26
|
+
z.object({ kind: z.literal("message" /* Message */), ...messageFields }),
|
|
27
|
+
z.object({ kind: z.literal("close" /* Close */), ...closeFields }),
|
|
28
|
+
z.object({ kind: z.literal("context" /* Context */), token: z.string() })
|
|
29
|
+
]);
|
|
30
|
+
const channelSocketActionSchema = z.discriminatedUnion("kind", [
|
|
31
|
+
z.object({
|
|
32
|
+
kind: z.literal("accept" /* Accept */),
|
|
33
|
+
protocol: z.string().optional(),
|
|
34
|
+
metadata: jsonValueSchema.optional()
|
|
35
|
+
}),
|
|
36
|
+
z.object({ kind: z.literal("message" /* Message */), ...messageFields }),
|
|
37
|
+
z.object({ kind: z.literal("close" /* Close */), ...closeFields }),
|
|
38
|
+
z.object({
|
|
39
|
+
kind: z.literal("reject" /* Reject */),
|
|
40
|
+
status: z.number().int().min(200).max(599),
|
|
41
|
+
headers: z.record(z.string(), z.string()),
|
|
42
|
+
body: z.string()
|
|
43
|
+
})
|
|
44
|
+
]);
|
|
45
|
+
const channelSocketMessage = (data) => {
|
|
46
|
+
const size = data instanceof Uint8Array ? data.byteLength : new TextEncoder().encode(data).byteLength;
|
|
47
|
+
if (size > CHANNEL_SOCKET_MAX_MESSAGE_BYTES)
|
|
48
|
+
throw new Error("Channel socket message exceeds 1 MiB");
|
|
49
|
+
return data instanceof Uint8Array ? { data: bytesToBase64(data), binary: true } : { data, binary: false };
|
|
50
|
+
};
|
|
51
|
+
const channelSocketData = (message) => {
|
|
52
|
+
const data = message.binary ? base64ToBytes(message.data) : message.data;
|
|
53
|
+
channelSocketMessage(data);
|
|
54
|
+
return data;
|
|
55
|
+
};
|
|
56
|
+
const runChannelSocketHandler = async (args) => {
|
|
57
|
+
args.signal.throwIfAborted();
|
|
58
|
+
let accepted = false;
|
|
59
|
+
let closed = false;
|
|
60
|
+
const socket = {
|
|
61
|
+
signal: args.signal,
|
|
62
|
+
messages: args.messages,
|
|
63
|
+
accept: (options) => {
|
|
64
|
+
args.signal.throwIfAborted();
|
|
65
|
+
if (accepted || closed) throw new Error("Channel socket already accepted or closed");
|
|
66
|
+
const protocol = options?.protocol;
|
|
67
|
+
if (protocol != null && (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(protocol) || !(args.context.request.headers.get("sec-websocket-protocol") ?? "").split(",").map((part) => part.trim()).includes(protocol)))
|
|
68
|
+
throw new Error("Channel socket selected a protocol the caller did not offer");
|
|
69
|
+
accepted = true;
|
|
70
|
+
args.emit(
|
|
71
|
+
protocol == null ? { kind: "accept" /* Accept */ } : { kind: "accept" /* Accept */, protocol }
|
|
72
|
+
);
|
|
73
|
+
},
|
|
74
|
+
send: (data) => {
|
|
75
|
+
args.signal.throwIfAborted();
|
|
76
|
+
if (!accepted || closed) throw new Error("Channel socket is not open");
|
|
77
|
+
args.emit({ kind: "message" /* Message */, ...channelSocketMessage(data) });
|
|
78
|
+
},
|
|
79
|
+
close: (code = 1e3, reason = "") => {
|
|
80
|
+
if (closed || args.signal.aborted) return;
|
|
81
|
+
if (!accepted) throw new Error("Reject the handshake with an HTTP response before accepting");
|
|
82
|
+
if (!validChannelSocketClose(code) || new TextEncoder().encode(reason).byteLength > 123)
|
|
83
|
+
throw new Error("Invalid channel socket close");
|
|
84
|
+
closed = true;
|
|
85
|
+
args.emit({ kind: "close" /* Close */, code, reason });
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
try {
|
|
89
|
+
const response = await args.handler({ ...args.context, socket });
|
|
90
|
+
if (args.signal.aborted) return;
|
|
91
|
+
if (!accepted) {
|
|
92
|
+
const rejected = response ?? new Response("No channel socket accepted", { status: 400 });
|
|
93
|
+
args.emit({
|
|
94
|
+
kind: "reject" /* Reject */,
|
|
95
|
+
status: rejected.status,
|
|
96
|
+
headers: Object.fromEntries(rejected.headers),
|
|
97
|
+
body: bytesToBase64(await readRejectionBody(rejected))
|
|
98
|
+
});
|
|
99
|
+
} else socket.close();
|
|
100
|
+
} catch {
|
|
101
|
+
if (args.signal.aborted) return;
|
|
102
|
+
if (accepted) socket.close(1011, "Channel socket failed");
|
|
103
|
+
else
|
|
104
|
+
args.emit({
|
|
105
|
+
kind: "reject" /* Reject */,
|
|
106
|
+
status: 500,
|
|
107
|
+
headers: {},
|
|
108
|
+
body: bytesToBase64(new TextEncoder().encode("Channel socket failed"))
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
const readRejectionBody = async (response) => {
|
|
113
|
+
if (response.body == null) return new Uint8Array();
|
|
114
|
+
const reader = response.body.getReader();
|
|
115
|
+
const chunks = [];
|
|
116
|
+
let length = 0;
|
|
117
|
+
try {
|
|
118
|
+
for (; ; ) {
|
|
119
|
+
const item = await reader.read();
|
|
120
|
+
if (item.done) break;
|
|
121
|
+
length += item.value.byteLength;
|
|
122
|
+
if (length > CHANNEL_SOCKET_MAX_MESSAGE_BYTES)
|
|
123
|
+
throw new Error("Channel socket rejection exceeds 1 MiB");
|
|
124
|
+
chunks.push(item.value);
|
|
125
|
+
}
|
|
126
|
+
const body = new Uint8Array(length);
|
|
127
|
+
let offset = 0;
|
|
128
|
+
for (const chunk of chunks) {
|
|
129
|
+
body.set(chunk, offset);
|
|
130
|
+
offset += chunk.byteLength;
|
|
131
|
+
}
|
|
132
|
+
return body;
|
|
133
|
+
} finally {
|
|
134
|
+
await reader.cancel().catch(() => void 0);
|
|
135
|
+
reader.releaseLock();
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
async function* readChannelSocketLines(body, signal) {
|
|
139
|
+
const reader = body.getReader();
|
|
140
|
+
const cancel = () => {
|
|
141
|
+
void reader.cancel().catch(() => void 0);
|
|
142
|
+
};
|
|
143
|
+
signal?.addEventListener("abort", cancel, { once: true });
|
|
144
|
+
if (signal?.aborted === true) cancel();
|
|
145
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
146
|
+
const encoder = new TextEncoder();
|
|
147
|
+
let buffer = "";
|
|
148
|
+
try {
|
|
149
|
+
for (; ; ) {
|
|
150
|
+
const item = await reader.read();
|
|
151
|
+
if (item.done) break;
|
|
152
|
+
buffer += decoder.decode(item.value, { stream: true });
|
|
153
|
+
let newline = buffer.indexOf("\n");
|
|
154
|
+
while (newline >= 0) {
|
|
155
|
+
if (encoder.encode(buffer.slice(0, newline)).byteLength > CHANNEL_SOCKET_MAX_LINE_BYTES)
|
|
156
|
+
throw new Error("Channel socket line exceeds its limit");
|
|
157
|
+
const line = buffer.slice(0, newline);
|
|
158
|
+
buffer = buffer.slice(newline + 1);
|
|
159
|
+
if (line.length > 0) yield jsonValueSchema.parse(JSON.parse(line));
|
|
160
|
+
newline = buffer.indexOf("\n");
|
|
161
|
+
}
|
|
162
|
+
if (encoder.encode(buffer).byteLength > CHANNEL_SOCKET_MAX_LINE_BYTES)
|
|
163
|
+
throw new Error("Channel socket line exceeds its limit");
|
|
164
|
+
}
|
|
165
|
+
buffer += decoder.decode();
|
|
166
|
+
if (encoder.encode(buffer).byteLength > CHANNEL_SOCKET_MAX_LINE_BYTES)
|
|
167
|
+
throw new Error("Channel socket line exceeds its limit");
|
|
168
|
+
if (buffer.length > 0) yield jsonValueSchema.parse(JSON.parse(buffer));
|
|
169
|
+
} finally {
|
|
170
|
+
signal?.removeEventListener("abort", cancel);
|
|
171
|
+
await reader.cancel().catch(() => void 0);
|
|
172
|
+
reader.releaseLock();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export {
|
|
176
|
+
CHANNEL_SOCKET_MAX_LINE_BYTES,
|
|
177
|
+
CHANNEL_SOCKET_MAX_MESSAGE_BYTES,
|
|
178
|
+
ChannelSocketActionKind,
|
|
179
|
+
ChannelSocketCommandKind,
|
|
180
|
+
channelSocketActionSchema,
|
|
181
|
+
channelSocketCommandSchema,
|
|
182
|
+
channelSocketData,
|
|
183
|
+
channelSocketMessage,
|
|
184
|
+
readChannelSocketLines,
|
|
185
|
+
runChannelSocketHandler,
|
|
186
|
+
validChannelSocketClose
|
|
187
|
+
};
|
|
188
|
+
//# sourceMappingURL=channel-socket.mjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/channel-socket.ts"],
|
|
4
|
+
"sourcesContent": ["import * as z from 'zod';\nimport type { ChannelSocketIface, WebSocketChannelRouteDefinitionIface } from \"./channel.mjs\";\nimport { jsonValueSchema, type JsonValue } from \"./json.mjs\";\nimport { base64ToBytes, bytesToBase64 } from \"./sandbox/bytes.mjs\";\n\nexport const CHANNEL_SOCKET_MAX_MESSAGE_BYTES = 1024 * 1024;\nexport const CHANNEL_SOCKET_MAX_LINE_BYTES = 8 * 1024 * 1024;\n\nexport enum ChannelSocketCommandKind {\n Message = 'message',\n Close = 'close',\n Context = 'context',\n}\n\nexport enum ChannelSocketActionKind {\n Accept = 'accept',\n Message = 'message',\n Close = 'close',\n Reject = 'reject',\n}\n\nconst messageFields = { data: z.string(), binary: z.boolean() };\nexport const validChannelSocketClose = (code: number): boolean =>\n code === 1000 ||\n code === 1001 ||\n code === 1002 ||\n code === 1003 ||\n (code >= 1007 && code <= 1014) ||\n (code >= 3000 && code <= 4999);\n\nconst closeFields = {\n code: z.number().int().refine(validChannelSocketClose).optional(),\n reason: z\n .string()\n .refine((value) => new TextEncoder().encode(value).byteLength <= 123)\n .optional(),\n};\nexport const channelSocketCommandSchema = z.discriminatedUnion('kind', [\n z.object({ kind: z.literal(ChannelSocketCommandKind.Message), ...messageFields }),\n z.object({ kind: z.literal(ChannelSocketCommandKind.Close), ...closeFields }),\n z.object({ kind: z.literal(ChannelSocketCommandKind.Context), token: z.string() }),\n]);\nexport const channelSocketActionSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal(ChannelSocketActionKind.Accept),\n protocol: z.string().optional(),\n metadata: jsonValueSchema.optional(),\n }),\n z.object({ kind: z.literal(ChannelSocketActionKind.Message), ...messageFields }),\n z.object({ kind: z.literal(ChannelSocketActionKind.Close), ...closeFields }),\n z.object({\n kind: z.literal(ChannelSocketActionKind.Reject),\n status: z.number().int().min(200).max(599),\n headers: z.record(z.string(), z.string()),\n body: z.string(),\n }),\n]);\nexport type ChannelSocketCommand = z.infer<typeof channelSocketCommandSchema>;\nexport type ChannelSocketAction = z.infer<typeof channelSocketActionSchema>;\n\nexport const channelSocketMessage = (\n data: string | Uint8Array,\n): { data: string; binary: boolean } => {\n const size =\n data instanceof Uint8Array ? data.byteLength : new TextEncoder().encode(data).byteLength;\n if (size > CHANNEL_SOCKET_MAX_MESSAGE_BYTES)\n throw new Error('Channel socket message exceeds 1 MiB');\n return data instanceof Uint8Array\n ? { data: bytesToBase64(data), binary: true }\n : { data, binary: false };\n};\nexport const channelSocketData = (message: {\n data: string;\n binary: boolean;\n}): string | Uint8Array => {\n const data = message.binary ? base64ToBytes(message.data) : message.data;\n channelSocketMessage(data);\n return data;\n};\n\nexport const runChannelSocketHandler = async (args: {\n handler: WebSocketChannelRouteDefinitionIface['handler'];\n context: Omit<Parameters<WebSocketChannelRouteDefinitionIface['handler']>[0], 'socket'>;\n messages: AsyncIterable<string | Uint8Array>;\n signal: AbortSignal;\n emit: (action: ChannelSocketAction) => void;\n}): Promise<void> => {\n args.signal.throwIfAborted();\n let accepted = false;\n let closed = false;\n const socket: ChannelSocketIface = {\n signal: args.signal,\n messages: args.messages,\n accept: (options) => {\n args.signal.throwIfAborted();\n if (accepted || closed) throw new Error('Channel socket already accepted or closed');\n const protocol = options?.protocol;\n if (\n protocol != null &&\n (!/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(protocol) ||\n !(args.context.request.headers.get('sec-websocket-protocol') ?? '')\n .split(',')\n .map((part) => part.trim())\n .includes(protocol))\n )\n throw new Error('Channel socket selected a protocol the caller did not offer');\n accepted = true;\n args.emit(\n protocol == null\n ? { kind: ChannelSocketActionKind.Accept }\n : { kind: ChannelSocketActionKind.Accept, protocol },\n );\n },\n send: (data) => {\n args.signal.throwIfAborted();\n if (!accepted || closed) throw new Error('Channel socket is not open');\n args.emit({ kind: ChannelSocketActionKind.Message, ...channelSocketMessage(data) });\n },\n close: (code = 1000, reason = '') => {\n if (closed || args.signal.aborted) return;\n if (!accepted) throw new Error('Reject the handshake with an HTTP response before accepting');\n if (!validChannelSocketClose(code) || new TextEncoder().encode(reason).byteLength > 123)\n throw new Error('Invalid channel socket close');\n closed = true;\n args.emit({ kind: ChannelSocketActionKind.Close, code, reason });\n },\n };\n try {\n const response = await args.handler({ ...args.context, socket });\n if (args.signal.aborted) return;\n if (!accepted) {\n const rejected = response ?? new Response('No channel socket accepted', { status: 400 });\n args.emit({\n kind: ChannelSocketActionKind.Reject,\n status: rejected.status,\n headers: Object.fromEntries(rejected.headers),\n body: bytesToBase64(await readRejectionBody(rejected)),\n });\n } else socket.close();\n } catch {\n if (args.signal.aborted) return;\n if (accepted) socket.close(1011, 'Channel socket failed');\n else\n args.emit({\n kind: ChannelSocketActionKind.Reject,\n status: 500,\n headers: {},\n body: bytesToBase64(new TextEncoder().encode('Channel socket failed')),\n });\n }\n};\n\nconst readRejectionBody = async (response: Response): Promise<Uint8Array> => {\n if (response.body == null) return new Uint8Array();\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let length = 0;\n try {\n for (;;) {\n const item = await reader.read();\n if (item.done) break;\n length += item.value.byteLength;\n if (length > CHANNEL_SOCKET_MAX_MESSAGE_BYTES)\n throw new Error('Channel socket rejection exceeds 1 MiB');\n chunks.push(item.value);\n }\n const body = new Uint8Array(length);\n let offset = 0;\n for (const chunk of chunks) {\n body.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return body;\n } finally {\n await reader.cancel().catch(() => undefined);\n reader.releaseLock();\n }\n};\n\n// Byte streams can cross Worker RPC. Each bounded line keeps one socket message intact.\nexport async function* readChannelSocketLines(\n body: ReadableStream<Uint8Array>,\n signal?: AbortSignal,\n): AsyncGenerator<JsonValue> {\n const reader = body.getReader();\n const cancel = () => {\n void reader.cancel().catch(() => undefined);\n };\n signal?.addEventListener('abort', cancel, { once: true });\n if (signal?.aborted === true) cancel();\n const decoder = new TextDecoder('utf-8', { fatal: true });\n const encoder = new TextEncoder();\n let buffer = '';\n try {\n for (;;) {\n const item = await reader.read();\n if (item.done) break;\n buffer += decoder.decode(item.value, { stream: true });\n let newline = buffer.indexOf('\\n');\n while (newline >= 0) {\n if (encoder.encode(buffer.slice(0, newline)).byteLength > CHANNEL_SOCKET_MAX_LINE_BYTES)\n throw new Error('Channel socket line exceeds its limit');\n const line = buffer.slice(0, newline);\n buffer = buffer.slice(newline + 1);\n if (line.length > 0) yield jsonValueSchema.parse(JSON.parse(line));\n newline = buffer.indexOf('\\n');\n }\n if (encoder.encode(buffer).byteLength > CHANNEL_SOCKET_MAX_LINE_BYTES)\n throw new Error('Channel socket line exceeds its limit');\n }\n buffer += decoder.decode();\n if (encoder.encode(buffer).byteLength > CHANNEL_SOCKET_MAX_LINE_BYTES)\n throw new Error('Channel socket line exceeds its limit');\n if (buffer.length > 0) yield jsonValueSchema.parse(JSON.parse(buffer));\n } finally {\n signal?.removeEventListener('abort', cancel);\n await reader.cancel().catch(() => undefined);\n reader.releaseLock();\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["ChannelSocketCommandKind", "ChannelSocketActionKind"]
|
|
7
|
+
}
|