@heddleagent/execution-host-client 6.3.0 → 6.4.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.
- package/README.md +75 -20
- package/dist/coordinator/contracts.d.ts +130 -0
- package/dist/coordinator/contracts.d.ts.map +1 -0
- package/dist/coordinator/contracts.js +112 -0
- package/dist/coordinator/contracts.js.map +1 -0
- package/dist/coordinator/hosted-heartbeat-coordinator-client.d.ts +20 -0
- package/dist/coordinator/hosted-heartbeat-coordinator-client.d.ts.map +1 -0
- package/dist/coordinator/hosted-heartbeat-coordinator-client.js +77 -0
- package/dist/coordinator/hosted-heartbeat-coordinator-client.js.map +1 -0
- package/dist/coordinator/hosted-heartbeat-delegation-client.d.ts +21 -0
- package/dist/coordinator/hosted-heartbeat-delegation-client.d.ts.map +1 -0
- package/dist/coordinator/hosted-heartbeat-delegation-client.js +117 -0
- package/dist/coordinator/hosted-heartbeat-delegation-client.js.map +1 -0
- package/dist/coordinator/hosted-heartbeat-delegation-service.d.ts +15 -0
- package/dist/coordinator/hosted-heartbeat-delegation-service.d.ts.map +1 -0
- package/dist/coordinator/hosted-heartbeat-delegation-service.js +81 -0
- package/dist/coordinator/hosted-heartbeat-delegation-service.js.map +1 -0
- package/dist/coordinator/hosted-heartbeat-task-reconciler.d.ts +11 -0
- package/dist/coordinator/hosted-heartbeat-task-reconciler.d.ts.map +1 -0
- package/dist/coordinator/hosted-heartbeat-task-reconciler.js +38 -0
- package/dist/coordinator/hosted-heartbeat-task-reconciler.js.map +1 -0
- package/dist/coordinator/index.d.ts +9 -0
- package/dist/coordinator/index.d.ts.map +1 -0
- package/dist/coordinator/index.js +7 -0
- package/dist/coordinator/index.js.map +1 -0
- package/dist/coordinator/node/delegation-http-service.d.ts +11 -0
- package/dist/coordinator/node/delegation-http-service.d.ts.map +1 -0
- package/dist/coordinator/node/delegation-http-service.js +131 -0
- package/dist/coordinator/node/delegation-http-service.js.map +1 -0
- package/dist/coordinator/node/environment.d.ts +3 -0
- package/dist/coordinator/node/environment.d.ts.map +1 -0
- package/dist/coordinator/node/environment.js +13 -0
- package/dist/coordinator/node/environment.js.map +1 -0
- package/dist/coordinator/node/index.d.ts +4 -0
- package/dist/coordinator/node/index.d.ts.map +1 -0
- package/dist/coordinator/node/index.js +3 -0
- package/dist/coordinator/node/index.js.map +1 -0
- package/dist/coordinator/node/types.d.ts +20 -0
- package/dist/coordinator/node/types.d.ts.map +1 -0
- package/dist/coordinator/node/types.js +2 -0
- package/dist/coordinator/node/types.js.map +1 -0
- package/dist/coordinator/runtime-session.d.ts +13 -0
- package/dist/coordinator/runtime-session.d.ts.map +1 -0
- package/dist/coordinator/runtime-session.js +23 -0
- package/dist/coordinator/runtime-session.js.map +1 -0
- package/dist/coordinator/service-token.d.ts +3 -0
- package/dist/coordinator/service-token.d.ts.map +1 -0
- package/dist/coordinator/service-token.js +7 -0
- package/dist/coordinator/service-token.js.map +1 -0
- package/dist/coordinator/types.d.ts +63 -0
- package/dist/coordinator/types.d.ts.map +1 -0
- package/dist/coordinator/types.js +2 -0
- package/dist/coordinator/types.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/node/http-service.d.ts.map +1 -1
- package/dist/node/http-service.js +5 -108
- package/dist/node/http-service.js.map +1 -1
- package/dist/node/http-utils.d.ts +15 -0
- package/dist/node/http-utils.d.ts.map +1 -0
- package/dist/node/http-utils.js +109 -0
- package/dist/node/http-utils.js.map +1 -0
- package/package.json +9 -1
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
declare const ProductExecutionScopeSchema: z.ZodObject<{
|
|
3
|
+
tenantId: z.ZodString;
|
|
4
|
+
subjectId: z.ZodString;
|
|
5
|
+
productSessionId: z.ZodString;
|
|
6
|
+
}, z.core.$strict>;
|
|
7
|
+
/** Stable Runtime session identity derived only from product-owned scope. */
|
|
8
|
+
export declare function createHostedRuntimeSessionId(rawInput: {
|
|
9
|
+
namespace: string;
|
|
10
|
+
scope: z.infer<typeof ProductExecutionScopeSchema>;
|
|
11
|
+
}): string;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=runtime-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-session.d.ts","sourceRoot":"","sources":["../../src/coordinator/runtime-session.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,QAAA,MAAM,2BAA2B;;;;kBAE/B,CAAC;AAOH,6EAA6E;AAC7E,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;CACpD,GAAG,MAAM,CAYT"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { ExecutionScopeSchema, OpaqueIdSchema, RuntimeSessionIdSchema, } from '../contracts/index.js';
|
|
4
|
+
const ProductExecutionScopeSchema = ExecutionScopeSchema.omit({
|
|
5
|
+
adopterId: true,
|
|
6
|
+
});
|
|
7
|
+
const HostedRuntimeSessionInputSchema = z.object({
|
|
8
|
+
namespace: OpaqueIdSchema,
|
|
9
|
+
scope: ProductExecutionScopeSchema,
|
|
10
|
+
}).strict();
|
|
11
|
+
/** Stable Runtime session identity derived only from product-owned scope. */
|
|
12
|
+
export function createHostedRuntimeSessionId(rawInput) {
|
|
13
|
+
const input = HostedRuntimeSessionInputSchema.parse(rawInput);
|
|
14
|
+
const digest = createHash('sha256')
|
|
15
|
+
.update(JSON.stringify([
|
|
16
|
+
input.scope.tenantId,
|
|
17
|
+
input.scope.subjectId,
|
|
18
|
+
input.scope.productSessionId,
|
|
19
|
+
]))
|
|
20
|
+
.digest('hex');
|
|
21
|
+
return RuntimeSessionIdSchema.parse(`${input.namespace}-runtime-session-${digest}`);
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=runtime-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-session.js","sourceRoot":"","sources":["../../src/coordinator/runtime-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,sBAAsB,GACvB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,2BAA2B,GAAG,oBAAoB,CAAC,IAAI,CAAC;IAC5D,SAAS,EAAE,IAAI;CAChB,CAAC,CAAC;AAEH,MAAM,+BAA+B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/C,SAAS,EAAE,cAAc;IACzB,KAAK,EAAE,2BAA2B;CACnC,CAAC,CAAC,MAAM,EAAE,CAAC;AAEZ,6EAA6E;AAC7E,MAAM,UAAU,4BAA4B,CAAC,QAG5C;IACC,MAAM,KAAK,GAAG,+BAA+B,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC;SAChC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC;QACrB,KAAK,CAAC,KAAK,CAAC,QAAQ;QACpB,KAAK,CAAC,KAAK,CAAC,SAAS;QACrB,KAAK,CAAC,KAAK,CAAC,gBAAgB;KAC7B,CAAC,CAAC;SACF,MAAM,CAAC,KAAK,CAAC,CAAC;IACjB,OAAO,sBAAsB,CAAC,KAAK,CACjC,GAAG,KAAK,CAAC,SAAS,oBAAoB,MAAM,EAAE,CAC/C,CAAC;AACJ,CAAC","sourcesContent":["import { createHash } from 'node:crypto';\nimport { z } from 'zod';\nimport {\n ExecutionScopeSchema,\n OpaqueIdSchema,\n RuntimeSessionIdSchema,\n} from '../contracts/index.js';\n\nconst ProductExecutionScopeSchema = ExecutionScopeSchema.omit({\n adopterId: true,\n});\n\nconst HostedRuntimeSessionInputSchema = z.object({\n namespace: OpaqueIdSchema,\n scope: ProductExecutionScopeSchema,\n}).strict();\n\n/** Stable Runtime session identity derived only from product-owned scope. */\nexport function createHostedRuntimeSessionId(rawInput: {\n namespace: string;\n scope: z.infer<typeof ProductExecutionScopeSchema>;\n}): string {\n const input = HostedRuntimeSessionInputSchema.parse(rawInput);\n const digest = createHash('sha256')\n .update(JSON.stringify([\n input.scope.tenantId,\n input.scope.subjectId,\n input.scope.productSessionId,\n ]))\n .digest('hex');\n return RuntimeSessionIdSchema.parse(\n `${input.namespace}-runtime-session-${digest}`,\n );\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-token.d.ts","sourceRoot":"","sources":["../../src/coordinator/service-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,iCAAiC,aAIjC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service-token.js","sourceRoot":"","sources":["../../src/coordinator/service-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC;KAC/C,MAAM,EAAE;KACR,IAAI,EAAE;KACN,GAAG,CAAC,EAAE,CAAC;KACP,GAAG,CAAC,KAAK,CAAC,CAAC","sourcesContent":["import { z } from 'zod';\n\nexport const HostedHeartbeatServiceTokenSchema = z\n .string()\n .trim()\n .min(32)\n .max(4_096);\n"]}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { ExecutionAuthorityIssuer } from '../authority/index.js';
|
|
2
|
+
import type { HostedModelCredentialProvider } from '../conversation/index.js';
|
|
3
|
+
import type { HostedHeartbeatAgentExecutionTransportInput } from '../heartbeat/index.js';
|
|
4
|
+
import type { HeartbeatExecutionHost } from '../http-sse/index.js';
|
|
5
|
+
import type { HostedHeartbeatCoordinatorTaskInput, HostedHeartbeatCoordinatorTaskSummary, HostedHeartbeatDelegation, HostedHeartbeatDelegationAuthorization, HostedHeartbeatDelegationRequest, HostedHeartbeatDesiredTask } from './contracts.js';
|
|
6
|
+
export type HostedHeartbeatCoordinatorClientConfig = {
|
|
7
|
+
baseUrl: URL;
|
|
8
|
+
apiToken: string;
|
|
9
|
+
fetch?: typeof globalThis.fetch;
|
|
10
|
+
};
|
|
11
|
+
export interface HostedHeartbeatCoordinatorTaskApi {
|
|
12
|
+
listTasks(signal?: AbortSignal): Promise<HostedHeartbeatCoordinatorTaskSummary[]>;
|
|
13
|
+
upsertTask(taskId: string, task: HostedHeartbeatCoordinatorTaskInput, signal?: AbortSignal): Promise<void>;
|
|
14
|
+
deleteTask(taskId: string, signal?: AbortSignal): Promise<void>;
|
|
15
|
+
pause(signal?: AbortSignal): Promise<void>;
|
|
16
|
+
resume(signal?: AbortSignal): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export type HostedHeartbeatTaskReconciliationInput = {
|
|
19
|
+
desiredTasks: readonly HostedHeartbeatDesiredTask[];
|
|
20
|
+
resume: boolean;
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
};
|
|
23
|
+
export type HostedHeartbeatTaskReconciliation = {
|
|
24
|
+
deleted: number;
|
|
25
|
+
upserted: number;
|
|
26
|
+
resumed: boolean;
|
|
27
|
+
};
|
|
28
|
+
export type HostedHeartbeatTaskReconcilerConfig = {
|
|
29
|
+
coordinator: HostedHeartbeatCoordinatorTaskApi;
|
|
30
|
+
};
|
|
31
|
+
export type HostedHeartbeatDelegationAuthorizationInput = {
|
|
32
|
+
taskId: string;
|
|
33
|
+
executionId: string;
|
|
34
|
+
signal: AbortSignal;
|
|
35
|
+
};
|
|
36
|
+
export interface HostedHeartbeatDelegationAuthorizer {
|
|
37
|
+
authorize(input: HostedHeartbeatDelegationAuthorizationInput): HostedHeartbeatDelegationAuthorization | undefined | Promise<HostedHeartbeatDelegationAuthorization | undefined>;
|
|
38
|
+
}
|
|
39
|
+
export type HostedHeartbeatDelegationServiceConfig = {
|
|
40
|
+
authority: ExecutionAuthorityIssuer;
|
|
41
|
+
authorizer: HostedHeartbeatDelegationAuthorizer;
|
|
42
|
+
runtimeSessionNamespace: string;
|
|
43
|
+
maxExecutionMs: number;
|
|
44
|
+
now?: () => Date;
|
|
45
|
+
};
|
|
46
|
+
export interface HostedHeartbeatDelegationIssuer {
|
|
47
|
+
issue(input: HostedHeartbeatDelegationRequest, signal?: AbortSignal): Promise<HostedHeartbeatDelegation>;
|
|
48
|
+
}
|
|
49
|
+
export type HostedHeartbeatDelegationClientConfig = {
|
|
50
|
+
baseUrl: URL;
|
|
51
|
+
apiToken: string;
|
|
52
|
+
fetch?: typeof globalThis.fetch;
|
|
53
|
+
path?: string;
|
|
54
|
+
};
|
|
55
|
+
export type HostedHeartbeatDelegatedExecutionTransportConfig = {
|
|
56
|
+
delegations: HostedHeartbeatDelegationIssuer;
|
|
57
|
+
executionHost: HeartbeatExecutionHost;
|
|
58
|
+
modelCredentials: HostedModelCredentialProvider;
|
|
59
|
+
};
|
|
60
|
+
export interface HostedHeartbeatDelegatedExecutionTransportPort {
|
|
61
|
+
execute(input: HostedHeartbeatAgentExecutionTransportInput): Promise<unknown>;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/coordinator/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/coordinator/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n ExecutionAuthorityIssuer,\n} from '../authority/index.js';\nimport type {\n HostedModelCredentialProvider,\n} from '../conversation/index.js';\nimport type {\n HostedHeartbeatAgentExecutionTransportInput,\n} from '../heartbeat/index.js';\nimport type { HeartbeatExecutionHost } from '../http-sse/index.js';\nimport type {\n HostedHeartbeatCoordinatorTaskInput,\n HostedHeartbeatCoordinatorTaskSummary,\n HostedHeartbeatDelegation,\n HostedHeartbeatDelegationAuthorization,\n HostedHeartbeatDelegationRequest,\n HostedHeartbeatDesiredTask,\n} from './contracts.js';\n\nexport type HostedHeartbeatCoordinatorClientConfig = {\n baseUrl: URL;\n apiToken: string;\n fetch?: typeof globalThis.fetch;\n};\n\nexport interface HostedHeartbeatCoordinatorTaskApi {\n listTasks(signal?: AbortSignal): Promise<\n HostedHeartbeatCoordinatorTaskSummary[]\n >;\n upsertTask(\n taskId: string,\n task: HostedHeartbeatCoordinatorTaskInput,\n signal?: AbortSignal,\n ): Promise<void>;\n deleteTask(taskId: string, signal?: AbortSignal): Promise<void>;\n pause(signal?: AbortSignal): Promise<void>;\n resume(signal?: AbortSignal): Promise<void>;\n}\n\nexport type HostedHeartbeatTaskReconciliationInput = {\n desiredTasks: readonly HostedHeartbeatDesiredTask[];\n resume: boolean;\n signal?: AbortSignal;\n};\n\nexport type HostedHeartbeatTaskReconciliation = {\n deleted: number;\n upserted: number;\n resumed: boolean;\n};\n\nexport type HostedHeartbeatTaskReconcilerConfig = {\n coordinator: HostedHeartbeatCoordinatorTaskApi;\n};\n\nexport type HostedHeartbeatDelegationAuthorizationInput = {\n taskId: string;\n executionId: string;\n signal: AbortSignal;\n};\n\nexport interface HostedHeartbeatDelegationAuthorizer {\n authorize(\n input: HostedHeartbeatDelegationAuthorizationInput,\n ): HostedHeartbeatDelegationAuthorization\n | undefined\n | Promise<HostedHeartbeatDelegationAuthorization | undefined>;\n}\n\nexport type HostedHeartbeatDelegationServiceConfig = {\n authority: ExecutionAuthorityIssuer;\n authorizer: HostedHeartbeatDelegationAuthorizer;\n runtimeSessionNamespace: string;\n maxExecutionMs: number;\n now?: () => Date;\n};\n\nexport interface HostedHeartbeatDelegationIssuer {\n issue(\n input: HostedHeartbeatDelegationRequest,\n signal?: AbortSignal,\n ): Promise<HostedHeartbeatDelegation>;\n}\n\nexport type HostedHeartbeatDelegationClientConfig = {\n baseUrl: URL;\n apiToken: string;\n fetch?: typeof globalThis.fetch;\n path?: string;\n};\n\nexport type HostedHeartbeatDelegatedExecutionTransportConfig = {\n delegations: HostedHeartbeatDelegationIssuer;\n executionHost: HeartbeatExecutionHost;\n modelCredentials: HostedModelCredentialProvider;\n};\n\nexport interface HostedHeartbeatDelegatedExecutionTransportPort {\n execute(\n input: HostedHeartbeatAgentExecutionTransportInput,\n ): Promise<unknown>;\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export * from './contracts/index.js';
|
|
|
2
2
|
export * from './authority/index.js';
|
|
3
3
|
export * from './conversation/index.js';
|
|
4
4
|
export * from './heartbeat/index.js';
|
|
5
|
+
export * from './coordinator/index.js';
|
|
5
6
|
export * from './mcp/index.js';
|
|
6
7
|
export * from './http-sse/index.js';
|
|
7
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ export * from './contracts/index.js';
|
|
|
2
2
|
export * from './authority/index.js';
|
|
3
3
|
export * from './conversation/index.js';
|
|
4
4
|
export * from './heartbeat/index.js';
|
|
5
|
+
export * from './coordinator/index.js';
|
|
5
6
|
export * from './mcp/index.js';
|
|
6
7
|
export * from './http-sse/index.js';
|
|
7
8
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC","sourcesContent":["export * from './contracts/index.js';\nexport * from './authority/index.js';\nexport * from './conversation/index.js';\nexport * from './heartbeat/index.js';\nexport * from './mcp/index.js';\nexport * from './http-sse/index.js';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAC;AACrC,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC","sourcesContent":["export * from './contracts/index.js';\nexport * from './authority/index.js';\nexport * from './conversation/index.js';\nexport * from './heartbeat/index.js';\nexport * from './coordinator/index.js';\nexport * from './mcp/index.js';\nexport * from './http-sse/index.js';\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-service.d.ts","sourceRoot":"","sources":["../../src/node/http-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACf,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"http-service.d.ts","sourceRoot":"","sources":["../../src/node/http-service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EACf,MAAM,WAAW,CAAC;AAcnB,OAAO,KAAK,EAEV,+BAA+B,EAE/B,qCAAqC,EAEtC,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,yBAAyB,2BAA2B,CAAC;AAClE,eAAO,MAAM,uCAAuC,yCACZ,CAAC;AAgBzC;;;;;;;GAOG;AACH,qBAAa,+BAA+B,CAAC,SAAS,CACtD,YAAW,+BAA+B;;gBAwB5B,MAAM,EAAE,qCAAqC,CAAC,SAAS,CAAC;IAkBpE,MAAM,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO;IAanE,UAAU,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,cAAc,GAAG,IAAI;IAwBpE,sBAAsB,CACpB,OAAO,EAAE,eAAe,EACxB,QAAQ,EAAE,cAAc,GACvB,IAAI;IA0CP,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAmJvB"}
|
|
@@ -2,24 +2,15 @@ import { once } from 'node:events';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { ExecutionHostStreamEventSchema } from '../contracts/index.js';
|
|
4
4
|
import { ExecutionHostInvocationCancelledError } from '../http-sse/index.js';
|
|
5
|
+
import { NodeHttpPathSchema, NodeHttpRequestError, errorType, readJsonBody, readPathname, takeAuthorization, writeJson, writeJsonError, } from './http-utils.js';
|
|
5
6
|
export const DEFAULT_ADOPTER_JWKS_PATH = '/.well-known/jwks.json';
|
|
6
7
|
export const DEFAULT_ADOPTER_CONVERSATION_TURNS_PATH = '/hosted-execution/conversation-turns';
|
|
7
8
|
const DEFAULT_MAX_BODY_BYTES = 64 * 1_024;
|
|
8
9
|
const DEFAULT_MAX_PROMPT_CHARACTERS = 20_000;
|
|
9
|
-
const REDACTED_HEADER_VALUE = '[REDACTED]';
|
|
10
|
-
const MAX_AUTHORIZATION_CHARACTERS = 8_192;
|
|
11
|
-
const PathSchema = z.string().min(1).max(256).regex(/^\/(?:[^?#\s]*)$/, 'must be an absolute path without query, fragment, or whitespace');
|
|
12
10
|
const PublicErrorSchema = z.object({
|
|
13
11
|
statusCode: z.number().int().min(400).max(599),
|
|
14
12
|
message: z.string().min(1).max(1_600),
|
|
15
13
|
}).strict();
|
|
16
|
-
class AdopterHttpRequestError extends Error {
|
|
17
|
-
statusCode;
|
|
18
|
-
constructor(statusCode) {
|
|
19
|
-
super('Invalid adopter HTTP request.');
|
|
20
|
-
this.statusCode = statusCode;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
14
|
/**
|
|
24
15
|
* Standard Node HTTP edge for adopter-side JWKS and hosted conversations.
|
|
25
16
|
*
|
|
@@ -151,7 +142,7 @@ export class NodeExecutionAdopterHttpService {
|
|
|
151
142
|
}
|
|
152
143
|
catch (error) {
|
|
153
144
|
request.resume();
|
|
154
|
-
writeJsonError(response, error instanceof
|
|
145
|
+
writeJsonError(response, error instanceof NodeHttpRequestError ? error.statusCode : 400, 'Invalid conversation request.');
|
|
155
146
|
return;
|
|
156
147
|
}
|
|
157
148
|
let principal;
|
|
@@ -187,7 +178,7 @@ export class NodeExecutionAdopterHttpService {
|
|
|
187
178
|
}).strict().parse(body).prompt;
|
|
188
179
|
}
|
|
189
180
|
catch (error) {
|
|
190
|
-
const statusCode = error instanceof
|
|
181
|
+
const statusCode = error instanceof NodeHttpRequestError
|
|
191
182
|
? error.statusCode
|
|
192
183
|
: 400;
|
|
193
184
|
writeJsonError(response, statusCode, 'Invalid conversation request.');
|
|
@@ -263,85 +254,14 @@ export class NodeExecutionAdopterHttpService {
|
|
|
263
254
|
}
|
|
264
255
|
function parsePaths(paths) {
|
|
265
256
|
const parsed = {
|
|
266
|
-
jwks:
|
|
267
|
-
conversationTurns:
|
|
257
|
+
jwks: NodeHttpPathSchema.parse(paths?.jwks ?? DEFAULT_ADOPTER_JWKS_PATH),
|
|
258
|
+
conversationTurns: NodeHttpPathSchema.parse(paths?.conversationTurns ?? DEFAULT_ADOPTER_CONVERSATION_TURNS_PATH),
|
|
268
259
|
};
|
|
269
260
|
if (parsed.jwks === parsed.conversationTurns) {
|
|
270
261
|
throw new Error('Adopter HTTP paths must be distinct.');
|
|
271
262
|
}
|
|
272
263
|
return Object.freeze(parsed);
|
|
273
264
|
}
|
|
274
|
-
function readPathname(rawUrl) {
|
|
275
|
-
try {
|
|
276
|
-
return new URL(rawUrl ?? '/', 'http://localhost').pathname;
|
|
277
|
-
}
|
|
278
|
-
catch {
|
|
279
|
-
return undefined;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
function takeAuthorization(request) {
|
|
283
|
-
const occurrences = request.rawHeaders.reduce((count, headerName, index) => count
|
|
284
|
-
+ (index % 2 === 0 && headerName.toLowerCase() === 'authorization' ? 1 : 0), 0);
|
|
285
|
-
const raw = request.headers.authorization;
|
|
286
|
-
request.headers.authorization = raw === undefined
|
|
287
|
-
? undefined
|
|
288
|
-
: REDACTED_HEADER_VALUE;
|
|
289
|
-
for (let index = 0; index < request.rawHeaders.length; index += 2) {
|
|
290
|
-
if (request.rawHeaders[index]?.toLowerCase() === 'authorization') {
|
|
291
|
-
request.rawHeaders[index + 1] = REDACTED_HEADER_VALUE;
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
if (occurrences > 1 || Array.isArray(raw)) {
|
|
295
|
-
throw new AdopterHttpRequestError(400);
|
|
296
|
-
}
|
|
297
|
-
if (raw && raw.length > MAX_AUTHORIZATION_CHARACTERS) {
|
|
298
|
-
throw new AdopterHttpRequestError(400);
|
|
299
|
-
}
|
|
300
|
-
return raw;
|
|
301
|
-
}
|
|
302
|
-
async function readJsonBody(request, maxBodyBytes, signal) {
|
|
303
|
-
const contentType = request.headers['content-type']
|
|
304
|
-
?.split(';', 1)[0]
|
|
305
|
-
?.trim()
|
|
306
|
-
.toLowerCase();
|
|
307
|
-
if (contentType !== 'application/json') {
|
|
308
|
-
request.resume();
|
|
309
|
-
throw new AdopterHttpRequestError(415);
|
|
310
|
-
}
|
|
311
|
-
const contentLength = request.headers['content-length'];
|
|
312
|
-
if (contentLength !== undefined) {
|
|
313
|
-
const declaredLength = Number(contentLength);
|
|
314
|
-
if (!Number.isSafeInteger(declaredLength) || declaredLength < 0) {
|
|
315
|
-
request.resume();
|
|
316
|
-
throw new AdopterHttpRequestError(400);
|
|
317
|
-
}
|
|
318
|
-
if (declaredLength > maxBodyBytes) {
|
|
319
|
-
request.resume();
|
|
320
|
-
throw new AdopterHttpRequestError(413);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
const chunks = [];
|
|
324
|
-
let receivedBytes = 0;
|
|
325
|
-
for await (const chunk of request) {
|
|
326
|
-
signal.throwIfAborted();
|
|
327
|
-
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
328
|
-
receivedBytes += buffer.byteLength;
|
|
329
|
-
if (receivedBytes > maxBodyBytes) {
|
|
330
|
-
request.resume();
|
|
331
|
-
throw new AdopterHttpRequestError(413);
|
|
332
|
-
}
|
|
333
|
-
chunks.push(buffer);
|
|
334
|
-
}
|
|
335
|
-
if (!request.complete || receivedBytes === 0) {
|
|
336
|
-
throw new AdopterHttpRequestError(400);
|
|
337
|
-
}
|
|
338
|
-
try {
|
|
339
|
-
return JSON.parse(Buffer.concat(chunks, receivedBytes).toString('utf8'));
|
|
340
|
-
}
|
|
341
|
-
catch {
|
|
342
|
-
throw new AdopterHttpRequestError(400);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
265
|
function writeEventStreamHeaders(response) {
|
|
346
266
|
response.writeHead(200, {
|
|
347
267
|
'Cache-Control': 'no-store',
|
|
@@ -363,27 +283,4 @@ async function writeSseEvent(response, event, signal) {
|
|
|
363
283
|
await once(response, 'drain', { signal });
|
|
364
284
|
}
|
|
365
285
|
}
|
|
366
|
-
function writeJsonError(response, statusCode, message, headers = {}) {
|
|
367
|
-
writeJson(response, statusCode, { error: { message } }, {
|
|
368
|
-
'Cache-Control': 'no-store',
|
|
369
|
-
...headers,
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
function writeJson(response, statusCode, value, headers = {}) {
|
|
373
|
-
if (response.headersSent || response.destroyed) {
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
const body = JSON.stringify(value);
|
|
377
|
-
response.writeHead(statusCode, {
|
|
378
|
-
'Cache-Control': 'no-store',
|
|
379
|
-
'Content-Length': Buffer.byteLength(body),
|
|
380
|
-
'Content-Type': 'application/json; charset=utf-8',
|
|
381
|
-
'X-Content-Type-Options': 'nosniff',
|
|
382
|
-
...headers,
|
|
383
|
-
});
|
|
384
|
-
response.end(body);
|
|
385
|
-
}
|
|
386
|
-
function errorType(error) {
|
|
387
|
-
return error instanceof Error ? error.name : 'unknown';
|
|
388
|
-
}
|
|
389
286
|
//# sourceMappingURL=http-service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-service.js","sourceRoot":"","sources":["../../src/node/http-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { once } from 'node:events';\nimport type {\n IncomingMessage,\n ServerResponse,\n} from 'node:http';\nimport { z } from 'zod';\nimport { ExecutionHostStreamEventSchema } from '../contracts/index.js';\nimport { ExecutionHostInvocationCancelledError } from '../http-sse/index.js';\nimport type {\n NodeExecutionAdopterFailure,\n NodeExecutionAdopterHttpHandler,\n NodeExecutionAdopterHttpPaths,\n NodeExecutionAdopterHttpServiceConfig,\n NodeExecutionAdopterPublicError,\n} from './types.js';\n\nexport const DEFAULT_ADOPTER_JWKS_PATH = '/.well-known/jwks.json';\nexport const DEFAULT_ADOPTER_CONVERSATION_TURNS_PATH =\n '/hosted-execution/conversation-turns';\n\nconst DEFAULT_MAX_BODY_BYTES = 64 * 1_024;\nconst DEFAULT_MAX_PROMPT_CHARACTERS = 20_000;\nconst REDACTED_HEADER_VALUE = '[REDACTED]';\nconst MAX_AUTHORIZATION_CHARACTERS = 8_192;\nconst PathSchema = z.string().min(1).max(256).regex(\n /^\\/(?:[^?#\\s]*)$/,\n 'must be an absolute path without query, fragment, or whitespace',\n);\nconst PublicErrorSchema = z.object({\n statusCode: z.number().int().min(400).max(599),\n message: z.string().min(1).max(1_600),\n}).strict();\n\ntype ActiveConversation = {\n abortController: AbortController;\n request: IncomingMessage;\n response: ServerResponse;\n pending: Promise<void>;\n};\n\nclass AdopterHttpRequestError extends Error {\n constructor(readonly statusCode: 400 | 413 | 415) {\n super('Invalid adopter HTTP request.');\n }\n}\n\n/**\n * Standard Node HTTP edge for adopter-side JWKS and hosted conversations.\n *\n * The service owns bounded parsing, credential-header scrubbing, SSE framing,\n * disconnect cancellation, safe failures, and graceful shutdown. Product\n * authentication, authorization, identity selection, and persistence stay in\n * adopter callbacks.\n */\nexport class NodeExecutionAdopterHttpService<Principal>\nimplements NodeExecutionAdopterHttpHandler {\n readonly #authority: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'authority'\n ];\n readonly #authenticator: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'authenticator'\n ];\n readonly #conversations: NodeExecutionAdopterHttpServiceConfig<\n Principal\n >['conversations'];\n readonly #projectError: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'projectError'\n ];\n readonly #reportFailure: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'reportFailure'\n ];\n readonly #paths: Readonly<NodeExecutionAdopterHttpPaths>;\n readonly #maxBodyBytes: number;\n readonly #maxPromptCharacters: number;\n readonly #jwksMaxAgeSeconds: number;\n readonly #activeConversations = new Set<ActiveConversation>();\n #closed = false;\n #closePromise: Promise<void> | undefined;\n\n constructor(config: NodeExecutionAdopterHttpServiceConfig<Principal>) {\n this.#authority = config.authority;\n this.#authenticator = config.authenticator;\n this.#conversations = config.conversations;\n this.#projectError = config.projectError;\n this.#reportFailure = config.reportFailure;\n this.#paths = parsePaths(config.paths);\n this.#maxBodyBytes = z.number().int().min(1).max(1_048_576).parse(\n config.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,\n );\n this.#maxPromptCharacters = z.number().int().min(1).max(200_000).parse(\n config.maxPromptCharacters ?? DEFAULT_MAX_PROMPT_CHARACTERS,\n );\n this.#jwksMaxAgeSeconds = z.number().int().min(0).max(86_400).parse(\n config.jwksMaxAgeSeconds ?? 60,\n );\n }\n\n handle(request: IncomingMessage, response: ServerResponse): boolean {\n const pathname = readPathname(request.url);\n if (pathname === this.#paths.jwks) {\n this.handleJwks(request, response);\n return true;\n }\n if (pathname === this.#paths.conversationTurns) {\n this.handleConversationTurn(request, response);\n return true;\n }\n return false;\n }\n\n handleJwks(request: IncomingMessage, response: ServerResponse): void {\n request.resume();\n if (this.#closed) {\n writeJsonError(response, 503, 'Hosted execution is unavailable.');\n return;\n }\n if (request.method !== 'GET') {\n writeJsonError(response, 405, 'Method not allowed.');\n return;\n }\n try {\n writeJson(response, 200, this.#authority.publicJwks(), {\n 'Cache-Control': `public, max-age=${this.#jwksMaxAgeSeconds}`,\n });\n } catch (error) {\n this.#report({\n phase: 'jwks',\n errorType: errorType(error),\n streamAccepted: false,\n });\n writeJsonError(response, 500, 'Verification keys are unavailable.');\n }\n }\n\n handleConversationTurn(\n request: IncomingMessage,\n response: ServerResponse,\n ): void {\n if (this.#closed) {\n request.resume();\n writeJsonError(response, 503, 'Hosted execution is unavailable.');\n return;\n }\n\n const abortController = new AbortController();\n const abort = () => abortController.abort(\n new Error('The owning adopter HTTP request closed.'),\n );\n request.once('aborted', abort);\n response.once('close', abort);\n const active: ActiveConversation = {\n abortController,\n request,\n response,\n pending: Promise.resolve(),\n };\n active.pending = this.#serveConversation(\n request,\n response,\n abortController.signal,\n ).catch((error) => {\n this.#report({\n phase: 'conversation',\n errorType: errorType(error),\n streamAccepted: response.headersSent,\n });\n if (!response.headersSent) {\n writeJsonError(response, 500, 'Hosted execution request failed.');\n } else if (!response.destroyed && !response.writableEnded) {\n response.end();\n }\n }).finally(() => {\n request.removeListener('aborted', abort);\n response.removeListener('close', abort);\n this.#activeConversations.delete(active);\n });\n this.#activeConversations.add(active);\n }\n\n close(): Promise<void> {\n if (!this.#closePromise) {\n this.#closed = true;\n this.#closePromise = this.#closeActiveConversations();\n }\n return this.#closePromise;\n }\n\n async #closeActiveConversations(): Promise<void> {\n const active = [...this.#activeConversations];\n active.forEach(({ abortController, request, response }) => {\n abortController.abort(new Error('The adopter HTTP service is stopping.'));\n request.destroy();\n response.destroy();\n });\n await Promise.allSettled(active.map(({ pending }) => pending));\n }\n\n async #serveConversation(\n request: IncomingMessage,\n response: ServerResponse,\n signal: AbortSignal,\n ): Promise<void> {\n let authorization: string | undefined;\n try {\n authorization = takeAuthorization(request);\n if (request.method !== 'POST') {\n request.resume();\n writeJsonError(response, 405, 'Method not allowed.');\n return;\n }\n } catch (error) {\n request.resume();\n writeJsonError(\n response,\n error instanceof AdopterHttpRequestError ? error.statusCode : 400,\n 'Invalid conversation request.',\n );\n return;\n }\n\n let principal: Principal | undefined;\n try {\n principal = await this.#authenticator.authenticate({\n authorization,\n remoteAddress: request.socket.remoteAddress,\n signal,\n });\n } catch (error) {\n this.#report({\n phase: 'authentication',\n errorType: errorType(error),\n streamAccepted: false,\n });\n request.resume();\n writeJsonError(response, 500, 'Authentication could not be completed.');\n return;\n }\n if (!principal) {\n request.resume();\n writeJsonError(response, 401, 'Authentication is required.', {\n 'WWW-Authenticate': 'Bearer',\n });\n return;\n }\n\n let prompt: string;\n try {\n const body = await readJsonBody(request, this.#maxBodyBytes, signal);\n prompt = z.object({\n prompt: z.string().trim().min(1).max(this.#maxPromptCharacters),\n }).strict().parse(body).prompt;\n } catch (error) {\n const statusCode = error instanceof AdopterHttpRequestError\n ? error.statusCode\n : 400;\n writeJsonError(response, statusCode, 'Invalid conversation request.');\n return;\n }\n\n let streamed = false;\n try {\n for await (const rawEvent of this.#conversations.streamTurn({\n principal,\n prompt,\n signal,\n })) {\n const event = ExecutionHostStreamEventSchema.parse(rawEvent);\n if (!streamed) {\n writeEventStreamHeaders(response);\n streamed = true;\n }\n await writeSseEvent(response, event, signal);\n }\n if (!streamed) {\n writeJsonError(response, 502, 'Execution Host returned no events.');\n return;\n }\n response.end();\n } catch (error) {\n const publicError = !streamed ? this.#project(error) : undefined;\n if (publicError) {\n writeJsonError(response, publicError.statusCode, publicError.message);\n return;\n }\n if (\n error instanceof ExecutionHostInvocationCancelledError\n || signal.aborted\n ) {\n if (!response.headersSent && !response.destroyed) {\n writeJsonError(response, 499, 'Hosted conversation was cancelled.');\n }\n return;\n }\n this.#report({\n phase: 'conversation',\n errorType: errorType(error),\n streamAccepted: streamed,\n });\n if (!response.headersSent) {\n writeJsonError(response, 502, 'Execution Host conversation failed.');\n } else if (!response.destroyed && !response.writableEnded) {\n // A post-acceptance failure deliberately ends without a terminal event.\n response.end();\n }\n }\n }\n\n #project(error: unknown): NodeExecutionAdopterPublicError | undefined {\n if (!this.#projectError) {\n return undefined;\n }\n try {\n const projected = this.#projectError(error);\n return projected ? PublicErrorSchema.parse(projected) : undefined;\n } catch {\n return undefined;\n }\n }\n\n #report(failure: NodeExecutionAdopterFailure): void {\n try {\n this.#reportFailure?.(Object.freeze({ ...failure }));\n } catch {\n // Observability must not change request settlement.\n }\n }\n}\n\nfunction parsePaths(\n paths: Partial<NodeExecutionAdopterHttpPaths> | undefined,\n): Readonly<NodeExecutionAdopterHttpPaths> {\n const parsed = {\n jwks: PathSchema.parse(paths?.jwks ?? DEFAULT_ADOPTER_JWKS_PATH),\n conversationTurns: PathSchema.parse(\n paths?.conversationTurns ?? DEFAULT_ADOPTER_CONVERSATION_TURNS_PATH,\n ),\n };\n if (parsed.jwks === parsed.conversationTurns) {\n throw new Error('Adopter HTTP paths must be distinct.');\n }\n return Object.freeze(parsed);\n}\n\nfunction readPathname(rawUrl: string | undefined): string | undefined {\n try {\n return new URL(rawUrl ?? '/', 'http://localhost').pathname;\n } catch {\n return undefined;\n }\n}\n\nfunction takeAuthorization(request: IncomingMessage): string | undefined {\n const occurrences = request.rawHeaders.reduce(\n (count, headerName, index) => count\n + (index % 2 === 0 && headerName.toLowerCase() === 'authorization' ? 1 : 0),\n 0,\n );\n const raw = request.headers.authorization;\n request.headers.authorization = raw === undefined\n ? undefined\n : REDACTED_HEADER_VALUE;\n for (let index = 0; index < request.rawHeaders.length; index += 2) {\n if (request.rawHeaders[index]?.toLowerCase() === 'authorization') {\n request.rawHeaders[index + 1] = REDACTED_HEADER_VALUE;\n }\n }\n if (occurrences > 1 || Array.isArray(raw)) {\n throw new AdopterHttpRequestError(400);\n }\n if (raw && raw.length > MAX_AUTHORIZATION_CHARACTERS) {\n throw new AdopterHttpRequestError(400);\n }\n return raw;\n}\n\nasync function readJsonBody(\n request: IncomingMessage,\n maxBodyBytes: number,\n signal: AbortSignal,\n): Promise<unknown> {\n const contentType = request.headers['content-type']\n ?.split(';', 1)[0]\n ?.trim()\n .toLowerCase();\n if (contentType !== 'application/json') {\n request.resume();\n throw new AdopterHttpRequestError(415);\n }\n const contentLength = request.headers['content-length'];\n if (contentLength !== undefined) {\n const declaredLength = Number(contentLength);\n if (!Number.isSafeInteger(declaredLength) || declaredLength < 0) {\n request.resume();\n throw new AdopterHttpRequestError(400);\n }\n if (declaredLength > maxBodyBytes) {\n request.resume();\n throw new AdopterHttpRequestError(413);\n }\n }\n\n const chunks: Buffer[] = [];\n let receivedBytes = 0;\n for await (const chunk of request) {\n signal.throwIfAborted();\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n receivedBytes += buffer.byteLength;\n if (receivedBytes > maxBodyBytes) {\n request.resume();\n throw new AdopterHttpRequestError(413);\n }\n chunks.push(buffer);\n }\n if (!request.complete || receivedBytes === 0) {\n throw new AdopterHttpRequestError(400);\n }\n try {\n return JSON.parse(\n Buffer.concat(chunks, receivedBytes).toString('utf8'),\n ) as unknown;\n } catch {\n throw new AdopterHttpRequestError(400);\n }\n}\n\nfunction writeEventStreamHeaders(response: ServerResponse): void {\n response.writeHead(200, {\n 'Cache-Control': 'no-store',\n 'Content-Type': 'text/event-stream; charset=utf-8',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n 'X-Content-Type-Options': 'nosniff',\n });\n}\n\nasync function writeSseEvent(\n response: ServerResponse,\n event: z.infer<typeof ExecutionHostStreamEventSchema>,\n signal: AbortSignal,\n): Promise<void> {\n const frame = [\n `event: ${event.kind}`,\n `id: ${event.sequence}`,\n `data: ${JSON.stringify(event)}`,\n '',\n '',\n ].join('\\n');\n if (!response.write(frame)) {\n await once(response, 'drain', { signal });\n }\n}\n\nfunction writeJsonError(\n response: ServerResponse,\n statusCode: number,\n message: string,\n headers: Record<string, string> = {},\n): void {\n writeJson(response, statusCode, { error: { message } }, {\n 'Cache-Control': 'no-store',\n ...headers,\n });\n}\n\nfunction writeJson(\n response: ServerResponse,\n statusCode: number,\n value: unknown,\n headers: Record<string, string> = {},\n): void {\n if (response.headersSent || response.destroyed) {\n return;\n }\n const body = JSON.stringify(value);\n response.writeHead(statusCode, {\n 'Cache-Control': 'no-store',\n 'Content-Length': Buffer.byteLength(body),\n 'Content-Type': 'application/json; charset=utf-8',\n 'X-Content-Type-Options': 'nosniff',\n ...headers,\n });\n response.end(body);\n}\n\nfunction errorType(error: unknown): string {\n return error instanceof Error ? error.name : 'unknown';\n}\n"]}
|
|
1
|
+
{"version":3,"file":"http-service.js","sourceRoot":"","sources":["../../src/node/http-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { once } from 'node:events';\nimport type {\n IncomingMessage,\n ServerResponse,\n} from 'node:http';\nimport { z } from 'zod';\nimport { ExecutionHostStreamEventSchema } from '../contracts/index.js';\nimport { ExecutionHostInvocationCancelledError } from '../http-sse/index.js';\nimport {\n NodeHttpPathSchema,\n NodeHttpRequestError,\n errorType,\n readJsonBody,\n readPathname,\n takeAuthorization,\n writeJson,\n writeJsonError,\n} from './http-utils.js';\nimport type {\n NodeExecutionAdopterFailure,\n NodeExecutionAdopterHttpHandler,\n NodeExecutionAdopterHttpPaths,\n NodeExecutionAdopterHttpServiceConfig,\n NodeExecutionAdopterPublicError,\n} from './types.js';\n\nexport const DEFAULT_ADOPTER_JWKS_PATH = '/.well-known/jwks.json';\nexport const DEFAULT_ADOPTER_CONVERSATION_TURNS_PATH =\n '/hosted-execution/conversation-turns';\n\nconst DEFAULT_MAX_BODY_BYTES = 64 * 1_024;\nconst DEFAULT_MAX_PROMPT_CHARACTERS = 20_000;\nconst PublicErrorSchema = z.object({\n statusCode: z.number().int().min(400).max(599),\n message: z.string().min(1).max(1_600),\n}).strict();\n\ntype ActiveConversation = {\n abortController: AbortController;\n request: IncomingMessage;\n response: ServerResponse;\n pending: Promise<void>;\n};\n\n/**\n * Standard Node HTTP edge for adopter-side JWKS and hosted conversations.\n *\n * The service owns bounded parsing, credential-header scrubbing, SSE framing,\n * disconnect cancellation, safe failures, and graceful shutdown. Product\n * authentication, authorization, identity selection, and persistence stay in\n * adopter callbacks.\n */\nexport class NodeExecutionAdopterHttpService<Principal>\nimplements NodeExecutionAdopterHttpHandler {\n readonly #authority: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'authority'\n ];\n readonly #authenticator: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'authenticator'\n ];\n readonly #conversations: NodeExecutionAdopterHttpServiceConfig<\n Principal\n >['conversations'];\n readonly #projectError: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'projectError'\n ];\n readonly #reportFailure: NodeExecutionAdopterHttpServiceConfig<Principal>[\n 'reportFailure'\n ];\n readonly #paths: Readonly<NodeExecutionAdopterHttpPaths>;\n readonly #maxBodyBytes: number;\n readonly #maxPromptCharacters: number;\n readonly #jwksMaxAgeSeconds: number;\n readonly #activeConversations = new Set<ActiveConversation>();\n #closed = false;\n #closePromise: Promise<void> | undefined;\n\n constructor(config: NodeExecutionAdopterHttpServiceConfig<Principal>) {\n this.#authority = config.authority;\n this.#authenticator = config.authenticator;\n this.#conversations = config.conversations;\n this.#projectError = config.projectError;\n this.#reportFailure = config.reportFailure;\n this.#paths = parsePaths(config.paths);\n this.#maxBodyBytes = z.number().int().min(1).max(1_048_576).parse(\n config.maxBodyBytes ?? DEFAULT_MAX_BODY_BYTES,\n );\n this.#maxPromptCharacters = z.number().int().min(1).max(200_000).parse(\n config.maxPromptCharacters ?? DEFAULT_MAX_PROMPT_CHARACTERS,\n );\n this.#jwksMaxAgeSeconds = z.number().int().min(0).max(86_400).parse(\n config.jwksMaxAgeSeconds ?? 60,\n );\n }\n\n handle(request: IncomingMessage, response: ServerResponse): boolean {\n const pathname = readPathname(request.url);\n if (pathname === this.#paths.jwks) {\n this.handleJwks(request, response);\n return true;\n }\n if (pathname === this.#paths.conversationTurns) {\n this.handleConversationTurn(request, response);\n return true;\n }\n return false;\n }\n\n handleJwks(request: IncomingMessage, response: ServerResponse): void {\n request.resume();\n if (this.#closed) {\n writeJsonError(response, 503, 'Hosted execution is unavailable.');\n return;\n }\n if (request.method !== 'GET') {\n writeJsonError(response, 405, 'Method not allowed.');\n return;\n }\n try {\n writeJson(response, 200, this.#authority.publicJwks(), {\n 'Cache-Control': `public, max-age=${this.#jwksMaxAgeSeconds}`,\n });\n } catch (error) {\n this.#report({\n phase: 'jwks',\n errorType: errorType(error),\n streamAccepted: false,\n });\n writeJsonError(response, 500, 'Verification keys are unavailable.');\n }\n }\n\n handleConversationTurn(\n request: IncomingMessage,\n response: ServerResponse,\n ): void {\n if (this.#closed) {\n request.resume();\n writeJsonError(response, 503, 'Hosted execution is unavailable.');\n return;\n }\n\n const abortController = new AbortController();\n const abort = () => abortController.abort(\n new Error('The owning adopter HTTP request closed.'),\n );\n request.once('aborted', abort);\n response.once('close', abort);\n const active: ActiveConversation = {\n abortController,\n request,\n response,\n pending: Promise.resolve(),\n };\n active.pending = this.#serveConversation(\n request,\n response,\n abortController.signal,\n ).catch((error) => {\n this.#report({\n phase: 'conversation',\n errorType: errorType(error),\n streamAccepted: response.headersSent,\n });\n if (!response.headersSent) {\n writeJsonError(response, 500, 'Hosted execution request failed.');\n } else if (!response.destroyed && !response.writableEnded) {\n response.end();\n }\n }).finally(() => {\n request.removeListener('aborted', abort);\n response.removeListener('close', abort);\n this.#activeConversations.delete(active);\n });\n this.#activeConversations.add(active);\n }\n\n close(): Promise<void> {\n if (!this.#closePromise) {\n this.#closed = true;\n this.#closePromise = this.#closeActiveConversations();\n }\n return this.#closePromise;\n }\n\n async #closeActiveConversations(): Promise<void> {\n const active = [...this.#activeConversations];\n active.forEach(({ abortController, request, response }) => {\n abortController.abort(new Error('The adopter HTTP service is stopping.'));\n request.destroy();\n response.destroy();\n });\n await Promise.allSettled(active.map(({ pending }) => pending));\n }\n\n async #serveConversation(\n request: IncomingMessage,\n response: ServerResponse,\n signal: AbortSignal,\n ): Promise<void> {\n let authorization: string | undefined;\n try {\n authorization = takeAuthorization(request);\n if (request.method !== 'POST') {\n request.resume();\n writeJsonError(response, 405, 'Method not allowed.');\n return;\n }\n } catch (error) {\n request.resume();\n writeJsonError(\n response,\n error instanceof NodeHttpRequestError ? error.statusCode : 400,\n 'Invalid conversation request.',\n );\n return;\n }\n\n let principal: Principal | undefined;\n try {\n principal = await this.#authenticator.authenticate({\n authorization,\n remoteAddress: request.socket.remoteAddress,\n signal,\n });\n } catch (error) {\n this.#report({\n phase: 'authentication',\n errorType: errorType(error),\n streamAccepted: false,\n });\n request.resume();\n writeJsonError(response, 500, 'Authentication could not be completed.');\n return;\n }\n if (!principal) {\n request.resume();\n writeJsonError(response, 401, 'Authentication is required.', {\n 'WWW-Authenticate': 'Bearer',\n });\n return;\n }\n\n let prompt: string;\n try {\n const body = await readJsonBody(request, this.#maxBodyBytes, signal);\n prompt = z.object({\n prompt: z.string().trim().min(1).max(this.#maxPromptCharacters),\n }).strict().parse(body).prompt;\n } catch (error) {\n const statusCode = error instanceof NodeHttpRequestError\n ? error.statusCode\n : 400;\n writeJsonError(response, statusCode, 'Invalid conversation request.');\n return;\n }\n\n let streamed = false;\n try {\n for await (const rawEvent of this.#conversations.streamTurn({\n principal,\n prompt,\n signal,\n })) {\n const event = ExecutionHostStreamEventSchema.parse(rawEvent);\n if (!streamed) {\n writeEventStreamHeaders(response);\n streamed = true;\n }\n await writeSseEvent(response, event, signal);\n }\n if (!streamed) {\n writeJsonError(response, 502, 'Execution Host returned no events.');\n return;\n }\n response.end();\n } catch (error) {\n const publicError = !streamed ? this.#project(error) : undefined;\n if (publicError) {\n writeJsonError(response, publicError.statusCode, publicError.message);\n return;\n }\n if (\n error instanceof ExecutionHostInvocationCancelledError\n || signal.aborted\n ) {\n if (!response.headersSent && !response.destroyed) {\n writeJsonError(response, 499, 'Hosted conversation was cancelled.');\n }\n return;\n }\n this.#report({\n phase: 'conversation',\n errorType: errorType(error),\n streamAccepted: streamed,\n });\n if (!response.headersSent) {\n writeJsonError(response, 502, 'Execution Host conversation failed.');\n } else if (!response.destroyed && !response.writableEnded) {\n // A post-acceptance failure deliberately ends without a terminal event.\n response.end();\n }\n }\n }\n\n #project(error: unknown): NodeExecutionAdopterPublicError | undefined {\n if (!this.#projectError) {\n return undefined;\n }\n try {\n const projected = this.#projectError(error);\n return projected ? PublicErrorSchema.parse(projected) : undefined;\n } catch {\n return undefined;\n }\n }\n\n #report(failure: NodeExecutionAdopterFailure): void {\n try {\n this.#reportFailure?.(Object.freeze({ ...failure }));\n } catch {\n // Observability must not change request settlement.\n }\n }\n}\n\nfunction parsePaths(\n paths: Partial<NodeExecutionAdopterHttpPaths> | undefined,\n): Readonly<NodeExecutionAdopterHttpPaths> {\n const parsed = {\n jwks: NodeHttpPathSchema.parse(paths?.jwks ?? DEFAULT_ADOPTER_JWKS_PATH),\n conversationTurns: NodeHttpPathSchema.parse(\n paths?.conversationTurns ?? DEFAULT_ADOPTER_CONVERSATION_TURNS_PATH,\n ),\n };\n if (parsed.jwks === parsed.conversationTurns) {\n throw new Error('Adopter HTTP paths must be distinct.');\n }\n return Object.freeze(parsed);\n}\n\nfunction writeEventStreamHeaders(response: ServerResponse): void {\n response.writeHead(200, {\n 'Cache-Control': 'no-store',\n 'Content-Type': 'text/event-stream; charset=utf-8',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n 'X-Content-Type-Options': 'nosniff',\n });\n}\n\nasync function writeSseEvent(\n response: ServerResponse,\n event: z.infer<typeof ExecutionHostStreamEventSchema>,\n signal: AbortSignal,\n): Promise<void> {\n const frame = [\n `event: ${event.kind}`,\n `id: ${event.sequence}`,\n `data: ${JSON.stringify(event)}`,\n '',\n '',\n ].join('\\n');\n if (!response.write(frame)) {\n await once(response, 'drain', { signal });\n }\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export declare const NodeHttpPathSchema: z.ZodString;
|
|
4
|
+
export declare class NodeHttpRequestError extends Error {
|
|
5
|
+
readonly statusCode: 400 | 413 | 415;
|
|
6
|
+
readonly name = "NodeHttpRequestError";
|
|
7
|
+
constructor(statusCode: 400 | 413 | 415);
|
|
8
|
+
}
|
|
9
|
+
export declare function readPathname(rawUrl: string | undefined): string | undefined;
|
|
10
|
+
export declare function takeAuthorization(request: IncomingMessage): string | undefined;
|
|
11
|
+
export declare function readJsonBody(request: IncomingMessage, maxBodyBytes: number, signal: AbortSignal): Promise<unknown>;
|
|
12
|
+
export declare function writeJsonError(response: ServerResponse, statusCode: number, message: string, headers?: Record<string, string>): void;
|
|
13
|
+
export declare function writeJson(response: ServerResponse, statusCode: number, value: unknown, headers?: Record<string, string>): void;
|
|
14
|
+
export declare function errorType(error: unknown): string;
|
|
15
|
+
//# sourceMappingURL=http-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-utils.d.ts","sourceRoot":"","sources":["../../src/node/http-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB,eAAO,MAAM,kBAAkB,aAG9B,CAAC;AAEF,qBAAa,oBAAqB,SAAQ,KAAK;IAGjC,QAAQ,CAAC,UAAU,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG;IAFhD,QAAQ,CAAC,IAAI,0BAA0B;gBAElB,UAAU,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG;CAGjD;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAM3E;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,eAAe,GACvB,MAAM,GAAG,SAAS,CAwBpB;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,eAAe,EACxB,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,OAAO,CAAC,CA4ClB;AAED,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,cAAc,EACxB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACnC,IAAI,CAKN;AAED,wBAAgB,SAAS,CACvB,QAAQ,EAAE,cAAc,EACxB,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,OAAO,EACd,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACnC,IAAI,CAaN;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEhD"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const REDACTED_HEADER_VALUE = '[REDACTED]';
|
|
3
|
+
const MAX_AUTHORIZATION_CHARACTERS = 8_192;
|
|
4
|
+
export const NodeHttpPathSchema = z.string().min(1).max(256).regex(/^\/(?:[^?#\s]*)$/, 'must be an absolute path without query, fragment, or whitespace');
|
|
5
|
+
export class NodeHttpRequestError extends Error {
|
|
6
|
+
statusCode;
|
|
7
|
+
name = 'NodeHttpRequestError';
|
|
8
|
+
constructor(statusCode) {
|
|
9
|
+
super('Invalid Node HTTP request.');
|
|
10
|
+
this.statusCode = statusCode;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function readPathname(rawUrl) {
|
|
14
|
+
try {
|
|
15
|
+
return new URL(rawUrl ?? '/', 'http://localhost').pathname;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function takeAuthorization(request) {
|
|
22
|
+
const occurrences = request.rawHeaders.reduce((count, headerName, index) => count
|
|
23
|
+
+ (index % 2 === 0 && headerName.toLowerCase() === 'authorization'
|
|
24
|
+
? 1
|
|
25
|
+
: 0), 0);
|
|
26
|
+
const raw = request.headers.authorization;
|
|
27
|
+
request.headers.authorization = raw === undefined
|
|
28
|
+
? undefined
|
|
29
|
+
: REDACTED_HEADER_VALUE;
|
|
30
|
+
for (let index = 0; index < request.rawHeaders.length; index += 2) {
|
|
31
|
+
if (request.rawHeaders[index]?.toLowerCase() === 'authorization') {
|
|
32
|
+
request.rawHeaders[index + 1] = REDACTED_HEADER_VALUE;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
if (occurrences > 1 || Array.isArray(raw)) {
|
|
36
|
+
throw new NodeHttpRequestError(400);
|
|
37
|
+
}
|
|
38
|
+
if (raw && raw.length > MAX_AUTHORIZATION_CHARACTERS) {
|
|
39
|
+
throw new NodeHttpRequestError(400);
|
|
40
|
+
}
|
|
41
|
+
return raw;
|
|
42
|
+
}
|
|
43
|
+
export async function readJsonBody(request, maxBodyBytes, signal) {
|
|
44
|
+
const contentType = request.headers['content-type']
|
|
45
|
+
?.split(';', 1)[0]
|
|
46
|
+
?.trim()
|
|
47
|
+
.toLowerCase();
|
|
48
|
+
if (contentType !== 'application/json') {
|
|
49
|
+
request.resume();
|
|
50
|
+
throw new NodeHttpRequestError(415);
|
|
51
|
+
}
|
|
52
|
+
const contentLength = request.headers['content-length'];
|
|
53
|
+
if (contentLength !== undefined) {
|
|
54
|
+
const declaredLength = Number(contentLength);
|
|
55
|
+
if (!Number.isSafeInteger(declaredLength) || declaredLength < 0) {
|
|
56
|
+
request.resume();
|
|
57
|
+
throw new NodeHttpRequestError(400);
|
|
58
|
+
}
|
|
59
|
+
if (declaredLength > maxBodyBytes) {
|
|
60
|
+
request.resume();
|
|
61
|
+
throw new NodeHttpRequestError(413);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const chunks = [];
|
|
65
|
+
let receivedBytes = 0;
|
|
66
|
+
for await (const chunk of request) {
|
|
67
|
+
signal.throwIfAborted();
|
|
68
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
69
|
+
receivedBytes += buffer.byteLength;
|
|
70
|
+
if (receivedBytes > maxBodyBytes) {
|
|
71
|
+
request.resume();
|
|
72
|
+
throw new NodeHttpRequestError(413);
|
|
73
|
+
}
|
|
74
|
+
chunks.push(buffer);
|
|
75
|
+
}
|
|
76
|
+
if (!request.complete || receivedBytes === 0) {
|
|
77
|
+
throw new NodeHttpRequestError(400);
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
return JSON.parse(Buffer.concat(chunks, receivedBytes).toString('utf8'));
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
throw new NodeHttpRequestError(400);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export function writeJsonError(response, statusCode, message, headers = {}) {
|
|
87
|
+
writeJson(response, statusCode, { error: { message } }, {
|
|
88
|
+
'Cache-Control': 'no-store',
|
|
89
|
+
...headers,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
export function writeJson(response, statusCode, value, headers = {}) {
|
|
93
|
+
if (response.headersSent || response.destroyed) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const body = JSON.stringify(value);
|
|
97
|
+
response.writeHead(statusCode, {
|
|
98
|
+
'Cache-Control': 'no-store',
|
|
99
|
+
'Content-Length': Buffer.byteLength(body),
|
|
100
|
+
'Content-Type': 'application/json; charset=utf-8',
|
|
101
|
+
'X-Content-Type-Options': 'nosniff',
|
|
102
|
+
...headers,
|
|
103
|
+
});
|
|
104
|
+
response.end(body);
|
|
105
|
+
}
|
|
106
|
+
export function errorType(error) {
|
|
107
|
+
return error instanceof Error ? error.name : 'unknown';
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=http-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-utils.js","sourceRoot":"","sources":["../../src/node/http-utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,qBAAqB,GAAG,YAAY,CAAC;AAC3C,MAAM,4BAA4B,GAAG,KAAK,CAAC;AAE3C,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,CAChE,kBAAkB,EAClB,iEAAiE,CAClE,CAAC;AAEF,MAAM,OAAO,oBAAqB,SAAQ,KAAK;IAGxB;IAFZ,IAAI,GAAG,sBAAsB,CAAC;IAEvC,YAAqB,UAA2B;QAC9C,KAAK,CAAC,4BAA4B,CAAC,CAAC;QADjB,eAAU,GAAV,UAAU,CAAiB;IAEhD,CAAC;CACF;AAED,MAAM,UAAU,YAAY,CAAC,MAA0B;IACrD,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,EAAE,kBAAkB,CAAC,CAAC,QAAQ,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,OAAwB;IAExB,MAAM,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,CAC3C,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK;UAC/B,CAAC,KAAK,GAAG,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,WAAW,EAAE,KAAK,eAAe;YAChE,CAAC,CAAC,CAAC;YACH,CAAC,CAAC,CAAC,CAAC,EACR,CAAC,CACF,CAAC;IACF,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;IAC1C,OAAO,CAAC,OAAO,CAAC,aAAa,GAAG,GAAG,KAAK,SAAS;QAC/C,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,qBAAqB,CAAC;IAC1B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAClE,IAAI,OAAO,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,WAAW,EAAE,KAAK,eAAe,EAAE,CAAC;YACjE,OAAO,CAAC,UAAU,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,qBAAqB,CAAC;QACxD,CAAC;IACH,CAAC;IACD,IAAI,WAAW,GAAG,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,4BAA4B,EAAE,CAAC;QACrD,MAAM,IAAI,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAwB,EACxB,YAAoB,EACpB,MAAmB;IAEnB,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC;QACjD,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAClB,EAAE,IAAI,EAAE;SACP,WAAW,EAAE,CAAC;IACjB,IAAI,WAAW,KAAK,kBAAkB,EAAE,CAAC;QACvC,OAAO,CAAC,MAAM,EAAE,CAAC;QACjB,MAAM,IAAI,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IACD,MAAM,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACxD,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,cAAc,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,cAAc,CAAC,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;YAChE,OAAO,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,IAAI,oBAAoB,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC;QACD,IAAI,cAAc,GAAG,YAAY,EAAE,CAAC;YAClC,OAAO,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,IAAI,oBAAoB,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAClC,MAAM,CAAC,cAAc,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnE,aAAa,IAAI,MAAM,CAAC,UAAU,CAAC;QACnC,IAAI,aAAa,GAAG,YAAY,EAAE,CAAC;YACjC,OAAO,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,IAAI,oBAAoB,CAAC,GAAG,CAAC,CAAC;QACtC,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IACD,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CACf,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAC3C,CAAC;IACf,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,QAAwB,EACxB,UAAkB,EAClB,OAAe,EACf,UAAkC,EAAE;IAEpC,SAAS,CAAC,QAAQ,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;QACtD,eAAe,EAAE,UAAU;QAC3B,GAAG,OAAO;KACX,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,QAAwB,EACxB,UAAkB,EAClB,KAAc,EACd,UAAkC,EAAE;IAEpC,IAAI,QAAQ,CAAC,WAAW,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;QAC/C,OAAO;IACT,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,UAAU,EAAE;QAC7B,eAAe,EAAE,UAAU;QAC3B,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;QACzC,cAAc,EAAE,iCAAiC;QACjD,wBAAwB,EAAE,SAAS;QACnC,GAAG,OAAO;KACX,CAAC,CAAC;IACH,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,OAAO,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC;AACzD,CAAC","sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http';\nimport { z } from 'zod';\n\nconst REDACTED_HEADER_VALUE = '[REDACTED]';\nconst MAX_AUTHORIZATION_CHARACTERS = 8_192;\n\nexport const NodeHttpPathSchema = z.string().min(1).max(256).regex(\n /^\\/(?:[^?#\\s]*)$/,\n 'must be an absolute path without query, fragment, or whitespace',\n);\n\nexport class NodeHttpRequestError extends Error {\n readonly name = 'NodeHttpRequestError';\n\n constructor(readonly statusCode: 400 | 413 | 415) {\n super('Invalid Node HTTP request.');\n }\n}\n\nexport function readPathname(rawUrl: string | undefined): string | undefined {\n try {\n return new URL(rawUrl ?? '/', 'http://localhost').pathname;\n } catch {\n return undefined;\n }\n}\n\nexport function takeAuthorization(\n request: IncomingMessage,\n): string | undefined {\n const occurrences = request.rawHeaders.reduce(\n (count, headerName, index) => count\n + (index % 2 === 0 && headerName.toLowerCase() === 'authorization'\n ? 1\n : 0),\n 0,\n );\n const raw = request.headers.authorization;\n request.headers.authorization = raw === undefined\n ? undefined\n : REDACTED_HEADER_VALUE;\n for (let index = 0; index < request.rawHeaders.length; index += 2) {\n if (request.rawHeaders[index]?.toLowerCase() === 'authorization') {\n request.rawHeaders[index + 1] = REDACTED_HEADER_VALUE;\n }\n }\n if (occurrences > 1 || Array.isArray(raw)) {\n throw new NodeHttpRequestError(400);\n }\n if (raw && raw.length > MAX_AUTHORIZATION_CHARACTERS) {\n throw new NodeHttpRequestError(400);\n }\n return raw;\n}\n\nexport async function readJsonBody(\n request: IncomingMessage,\n maxBodyBytes: number,\n signal: AbortSignal,\n): Promise<unknown> {\n const contentType = request.headers['content-type']\n ?.split(';', 1)[0]\n ?.trim()\n .toLowerCase();\n if (contentType !== 'application/json') {\n request.resume();\n throw new NodeHttpRequestError(415);\n }\n const contentLength = request.headers['content-length'];\n if (contentLength !== undefined) {\n const declaredLength = Number(contentLength);\n if (!Number.isSafeInteger(declaredLength) || declaredLength < 0) {\n request.resume();\n throw new NodeHttpRequestError(400);\n }\n if (declaredLength > maxBodyBytes) {\n request.resume();\n throw new NodeHttpRequestError(413);\n }\n }\n\n const chunks: Buffer[] = [];\n let receivedBytes = 0;\n for await (const chunk of request) {\n signal.throwIfAborted();\n const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);\n receivedBytes += buffer.byteLength;\n if (receivedBytes > maxBodyBytes) {\n request.resume();\n throw new NodeHttpRequestError(413);\n }\n chunks.push(buffer);\n }\n if (!request.complete || receivedBytes === 0) {\n throw new NodeHttpRequestError(400);\n }\n try {\n return JSON.parse(\n Buffer.concat(chunks, receivedBytes).toString('utf8'),\n ) as unknown;\n } catch {\n throw new NodeHttpRequestError(400);\n }\n}\n\nexport function writeJsonError(\n response: ServerResponse,\n statusCode: number,\n message: string,\n headers: Record<string, string> = {},\n): void {\n writeJson(response, statusCode, { error: { message } }, {\n 'Cache-Control': 'no-store',\n ...headers,\n });\n}\n\nexport function writeJson(\n response: ServerResponse,\n statusCode: number,\n value: unknown,\n headers: Record<string, string> = {},\n): void {\n if (response.headersSent || response.destroyed) {\n return;\n }\n const body = JSON.stringify(value);\n response.writeHead(statusCode, {\n 'Cache-Control': 'no-store',\n 'Content-Length': Buffer.byteLength(body),\n 'Content-Type': 'application/json; charset=utf-8',\n 'X-Content-Type-Options': 'nosniff',\n ...headers,\n });\n response.end(body);\n}\n\nexport function errorType(error: unknown): string {\n return error instanceof Error ? error.name : 'unknown';\n}\n"]}
|