@kensio/yulin 1.21.28 → 1.21.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/sdk/wire/sim-sdk-wire-bedrock-action.d.ts +18 -0
  2. package/dist/sdk/wire/sim-sdk-wire-bedrock-action.js +71 -0
  3. package/dist/sdk/wire/sim-sdk-wire-bedrock-input.d.ts +11 -0
  4. package/dist/sdk/wire/sim-sdk-wire-bedrock-input.js +45 -0
  5. package/dist/sdk/wire/sim-sdk-wire-bedrock.d.ts +10 -0
  6. package/dist/sdk/wire/sim-sdk-wire-bedrock.js +47 -0
  7. package/dist/sdk/wire/sim-sdk-wire-call.d.ts +30 -0
  8. package/dist/sdk/wire/sim-sdk-wire-call.js +32 -0
  9. package/dist/sdk/wire/sim-sdk-wire-dispatcher.js +7 -9
  10. package/dist/sdk/wire/sim-sdk-wire-event-stream.d.ts +16 -0
  11. package/dist/sdk/wire/sim-sdk-wire-event-stream.js +79 -0
  12. package/dist/sdk/wire/sim-sdk-wire-unbridged.js +2 -2
  13. package/dist/service/bedrock/command/authorize/sim-bedrock-authorizer.js +7 -3
  14. package/dist/service/lambda/function/code/vm/sdk/sim-sdk-lambda-vm-module-provider.d.ts +2 -2
  15. package/dist/service/lambda/function/code/vm/sdk/sim-sdk-lambda-vm-module-provider.js +5 -9
  16. package/dist/service/lambda/function/code/vm/sim-lambda-vm-context.d.ts +1 -1
  17. package/dist/service/lambda/function/code/vm/sim-lambda-vm-context.js +11 -22
  18. package/dist/service/lambda/function/code/vm/sim-lambda-vm-host-globals.d.ts +4 -0
  19. package/dist/service/lambda/function/code/vm/sim-lambda-vm-host-globals.js +87 -0
  20. package/dist/service/lambda/function/invoke/sim-lambda-outbound-clients.d.ts +3 -0
  21. package/dist/service/lambda/function/invoke/sim-lambda-outbound-clients.js +10 -0
  22. package/dist/service/lambda/function/outbound/sim-lambda-http2-exchanges.d.ts +32 -0
  23. package/dist/service/lambda/function/outbound/sim-lambda-http2-exchanges.js +39 -0
  24. package/dist/service/lambda/function/outbound/sim-lambda-http2-messages.d.ts +33 -0
  25. package/dist/service/lambda/function/outbound/sim-lambda-http2-messages.js +87 -0
  26. package/dist/service/lambda/function/outbound/sim-lambda-http2-module.d.ts +27 -0
  27. package/dist/service/lambda/function/outbound/sim-lambda-http2-module.js +53 -0
  28. package/dist/service/lambda/function/outbound/sim-lambda-http2-session.d.ts +31 -0
  29. package/dist/service/lambda/function/outbound/sim-lambda-http2-session.js +57 -0
  30. package/dist/service/lambda/function/outbound/sim-lambda-transport-module.d.ts +11 -0
  31. package/dist/service/lambda/function/outbound/sim-lambda-transport-module.js +24 -0
  32. package/docs/services/lambda/README.md +18 -8
  33. package/package.json +1 -1
@@ -0,0 +1,18 @@
1
+ import type { SimSdkWireRequest, SimSdkWireResponse } from "./sim-sdk-wire.types.js";
2
+ /**
3
+ * The REST-JSON content type, for bodies and for errors.
4
+ */
5
+ export declare const simSdkWireBedrockJsonContentType = "application/json";
6
+ /**
7
+ * How one Bedrock Runtime operation reads its input off a request and writes
8
+ * its output back.
9
+ */
10
+ export interface SimSdkWireBedrockAction {
11
+ readonly commandName: string;
12
+ input(modelId: string, request: SimSdkWireRequest): unknown;
13
+ outputResponse(output: unknown): Promise<SimSdkWireResponse>;
14
+ }
15
+ /**
16
+ * The invocation operations, keyed by the last segment of their path.
17
+ */
18
+ export declare const simSdkWireBedrockActions: ReadonlyMap<string, SimSdkWireBedrockAction>;
@@ -0,0 +1,71 @@
1
+ import { simSdkWireConverseInput, simSdkWireInvokeInput, } from "./sim-sdk-wire-bedrock-input.js";
2
+ import { simSdkWireEventStreamBody, simSdkWireEventStreamContentType, } from "./sim-sdk-wire-event-stream.js";
3
+ import { simSdkWireOutputResponse } from "./sim-sdk-wire-response.js";
4
+ /**
5
+ * The REST-JSON content type, for bodies and for errors.
6
+ */
7
+ export const simSdkWireBedrockJsonContentType = "application/json";
8
+ /**
9
+ * The invocation operations, keyed by the last segment of their path.
10
+ */
11
+ export const simSdkWireBedrockActions = new Map([
12
+ [
13
+ "converse",
14
+ {
15
+ commandName: "ConverseCommand",
16
+ input: simSdkWireConverseInput,
17
+ outputResponse: (output) => Promise.resolve(simSdkWireOutputResponse(output, simSdkWireBedrockJsonContentType)),
18
+ },
19
+ ],
20
+ [
21
+ "converse-stream",
22
+ {
23
+ commandName: "ConverseStreamCommand",
24
+ input: simSdkWireConverseInput,
25
+ outputResponse: async (output) => await eventStreamResponse(output.stream, {}),
26
+ },
27
+ ],
28
+ [
29
+ "invoke",
30
+ {
31
+ commandName: "InvokeModelCommand",
32
+ input: (modelId, request) => simSdkWireInvokeInput(modelId, request, "accept"),
33
+ outputResponse: invokeModelResponse,
34
+ },
35
+ ],
36
+ [
37
+ "invoke-with-response-stream",
38
+ {
39
+ commandName: "InvokeModelWithResponseStreamCommand",
40
+ input: (modelId, request) => simSdkWireInvokeInput(modelId, request, "x-amzn-bedrock-accept"),
41
+ outputResponse: async (output) => {
42
+ const { body, contentType } = output;
43
+ return await eventStreamResponse(body, {
44
+ "x-amzn-bedrock-content-type": contentType,
45
+ });
46
+ },
47
+ },
48
+ ],
49
+ ]);
50
+ /**
51
+ * `InvokeModel` answers with the model's own body as it is, labelled with its
52
+ * content type.
53
+ */
54
+ function invokeModelResponse(output) {
55
+ const { body, contentType } = output;
56
+ return Promise.resolve({
57
+ statusCode: 200,
58
+ headers: { "content-type": contentType },
59
+ body,
60
+ });
61
+ }
62
+ /**
63
+ * The streaming operations answer with an event stream body.
64
+ */
65
+ async function eventStreamResponse(events, headers) {
66
+ return {
67
+ statusCode: 200,
68
+ headers: { "content-type": simSdkWireEventStreamContentType, ...headers },
69
+ body: await simSdkWireEventStreamBody(events),
70
+ };
71
+ }
@@ -0,0 +1,11 @@
1
+ import type { SimSdkWireRequest } from "./sim-sdk-wire.types.js";
2
+ /**
3
+ * `Converse` and `ConverseStream` send their input as the JSON body, less the
4
+ * model id the path carries.
5
+ */
6
+ export declare function simSdkWireConverseInput(modelId: string, request: SimSdkWireRequest): unknown;
7
+ /**
8
+ * The model invocations send the request body as it is, with every other
9
+ * member in a header.
10
+ */
11
+ export declare function simSdkWireInvokeInput(modelId: string, request: SimSdkWireRequest, acceptHeaderName: string): unknown;
@@ -0,0 +1,45 @@
1
+ import { readSimSdkWireInput } from "./sim-sdk-wire-json.js";
2
+ /**
3
+ * The Command input members `InvokeModel` and
4
+ * `InvokeModelWithResponseStream` carry in headers, with the header each one
5
+ * travels in. The two differ only in where they carry `accept`.
6
+ *
7
+ * Every one is read, including the ones the simulation refuses, so that a
8
+ * request naming a guardrail is refused here as it is through an intercepted
9
+ * client.
10
+ */
11
+ const invokeHeaderMembers = [
12
+ ["contentType", "content-type"],
13
+ ["trace", "x-amzn-bedrock-trace"],
14
+ ["guardrailIdentifier", "x-amzn-bedrock-guardrailidentifier"],
15
+ ["guardrailVersion", "x-amzn-bedrock-guardrailversion"],
16
+ ["performanceConfigLatency", "x-amzn-bedrock-performanceconfig-latency"],
17
+ ["serviceTier", "x-amzn-bedrock-service-tier"],
18
+ ["requestMetadata", "x-amzn-bedrock-request-metadata"],
19
+ ];
20
+ /**
21
+ * `Converse` and `ConverseStream` send their input as the JSON body, less the
22
+ * model id the path carries.
23
+ */
24
+ export function simSdkWireConverseInput(modelId, request) {
25
+ return { ...readSimSdkWireInput(request.body), modelId };
26
+ }
27
+ /**
28
+ * The model invocations send the request body as it is, with every other
29
+ * member in a header.
30
+ */
31
+ export function simSdkWireInvokeInput(modelId, request, acceptHeaderName) {
32
+ const members = [
33
+ ...invokeHeaderMembers,
34
+ ["accept", acceptHeaderName],
35
+ ].flatMap(([member, headerName]) => {
36
+ // oxlint-disable-next-line security/detect-object-injection -- this module's own fixed header names.
37
+ const value = request.headers[headerName];
38
+ return value === undefined ? [] : [[member, value]];
39
+ });
40
+ return {
41
+ ...Object.fromEntries(members),
42
+ modelId,
43
+ ...(request.body.byteLength > 0 && { body: request.body }),
44
+ };
45
+ }
@@ -0,0 +1,10 @@
1
+ import type { SimSdkWireCall } from "./sim-sdk-wire-call.js";
2
+ import type { SimSdkWireRequest } from "./sim-sdk-wire.types.js";
3
+ /**
4
+ * Read a Bedrock Runtime model invocation, which names its operation and its
5
+ * model in the path rather than in a header.
6
+ *
7
+ * A request counts as one when it was signed for Bedrock, or, unsigned, sent
8
+ * to a Bedrock Runtime endpoint, and its path is a model invocation.
9
+ */
10
+ export declare function readSimSdkWireBedrockCall(request: SimSdkWireRequest): SimSdkWireCall | undefined;
@@ -0,0 +1,47 @@
1
+ import { simSdkWireBedrockActions, simSdkWireBedrockJsonContentType, } from "./sim-sdk-wire-bedrock-action.js";
2
+ import { readSimSdkWireCredentialScope } from "./sim-sdk-wire-operation.js";
3
+ import { simSdkWireErrorResponse } from "./sim-sdk-wire-response.js";
4
+ /**
5
+ * The SDK `config.serviceId` of a `BedrockRuntimeClient`.
6
+ */
7
+ const bedrockRuntimeServiceId = "Bedrock Runtime";
8
+ /**
9
+ * The SigV4 signing name Bedrock Runtime requests are signed with.
10
+ */
11
+ const bedrockSigningName = "bedrock";
12
+ /**
13
+ * The path of a model invocation: `/model/{modelId}/{action}`, where the
14
+ * model id is URL-encoded and may be a whole ARN.
15
+ */
16
+ const invocationPathPattern = /^\/model\/(?<modelId>[^/]+)\/(?<action>[a-z-]+)$/;
17
+ /**
18
+ * Read a Bedrock Runtime model invocation, which names its operation and its
19
+ * model in the path rather than in a header.
20
+ *
21
+ * A request counts as one when it was signed for Bedrock, or, unsigned, sent
22
+ * to a Bedrock Runtime endpoint, and its path is a model invocation.
23
+ */
24
+ export function readSimSdkWireBedrockCall(request) {
25
+ const signingName = readSimSdkWireCredentialScope(request)?.signingName;
26
+ const isBedrock = signingName === undefined
27
+ ? request.hostname.startsWith("bedrock-runtime.")
28
+ : signingName === bedrockSigningName;
29
+ if (!isBedrock || request.method.toUpperCase() !== "POST") {
30
+ return undefined;
31
+ }
32
+ const groups = invocationPathPattern.exec(request.path.split("?", 1)[0] ?? "")?.groups;
33
+ const action = simSdkWireBedrockActions.get(groups?.["action"] ?? "");
34
+ if (groups === undefined || action === undefined) {
35
+ return undefined;
36
+ }
37
+ // A malformed escape fails decoding, which is the service's answer to the
38
+ // request, so the model id is decoded when the input is read.
39
+ const encodedModelId = groups["modelId"] ?? "";
40
+ return {
41
+ serviceId: bedrockRuntimeServiceId,
42
+ commandName: action.commandName,
43
+ input: () => action.input(decodeURIComponent(encodedModelId), request),
44
+ outputResponse: async (output) => await action.outputResponse(output),
45
+ errorResponse: (error) => simSdkWireErrorResponse(error, simSdkWireBedrockJsonContentType),
46
+ };
47
+ }
@@ -0,0 +1,30 @@
1
+ import type { SimSdkWireRequest, SimSdkWireResponse } from "./sim-sdk-wire.types.js";
2
+ /**
3
+ * One wire request read back into the Command it was serialized from, with
4
+ * the protocol it came in to encode the answer in.
5
+ */
6
+ export interface SimSdkWireCall {
7
+ /** The SDK `config.serviceId` of the service the request is addressed to. */
8
+ readonly serviceId: string;
9
+ /** The SDK Command class name the request would have been sent as. */
10
+ readonly commandName: string;
11
+ /**
12
+ * The Command input. Reading it can fail on a malformed body, and that
13
+ * failure is the service's answer to the request, so it is read when the
14
+ * operation runs.
15
+ */
16
+ input(): unknown;
17
+ /** The response for an output the simulated operation answered with. */
18
+ outputResponse(output: unknown): Promise<SimSdkWireResponse>;
19
+ /** The response for an error the simulated operation refused with. */
20
+ errorResponse(error: unknown): SimSdkWireResponse;
21
+ }
22
+ /**
23
+ * Read a wire request as the Command it was serialized from.
24
+ *
25
+ * Returns undefined for a request no reader here understands. The AWS JSON
26
+ * protocol services all name their operation in one header. Bedrock Runtime
27
+ * speaks REST-JSON, with the operation and model in the path, and has a
28
+ * reader of its own.
29
+ */
30
+ export declare function readSimSdkWireCall(request: SimSdkWireRequest): SimSdkWireCall | undefined;
@@ -0,0 +1,32 @@
1
+ import { readSimSdkWireBedrockCall } from "./sim-sdk-wire-bedrock.js";
2
+ import { readSimSdkWireInput } from "./sim-sdk-wire-json.js";
3
+ import { readSimSdkWireOperation } from "./sim-sdk-wire-operation.js";
4
+ import { simSdkWireErrorResponse, simSdkWireOutputResponse, } from "./sim-sdk-wire-response.js";
5
+ /**
6
+ * Read a wire request as the Command it was serialized from.
7
+ *
8
+ * Returns undefined for a request no reader here understands. The AWS JSON
9
+ * protocol services all name their operation in one header. Bedrock Runtime
10
+ * speaks REST-JSON, with the operation and model in the path, and has a
11
+ * reader of its own.
12
+ */
13
+ export function readSimSdkWireCall(request) {
14
+ return readSimSdkWireJsonCall(request) ?? readSimSdkWireBedrockCall(request);
15
+ }
16
+ /**
17
+ * Read an AWS JSON protocol request, whose body is the Command input and
18
+ * whose response echoes the protocol version it was sent with.
19
+ */
20
+ function readSimSdkWireJsonCall(request) {
21
+ const operation = readSimSdkWireOperation(request);
22
+ if (operation === undefined) {
23
+ return undefined;
24
+ }
25
+ const contentType = request.headers["content-type"];
26
+ return {
27
+ ...operation,
28
+ input: () => readSimSdkWireInput(request.body),
29
+ outputResponse: (output) => Promise.resolve(simSdkWireOutputResponse(output, contentType)),
30
+ errorResponse: (error) => simSdkWireErrorResponse(error, contentType),
31
+ };
32
+ }
@@ -1,8 +1,7 @@
1
1
  import { SimSdkCommandDispatcher } from "../sim-sdk-command-dispatcher.js";
2
2
  import { makeSimSdkWireClient, makeSimSdkWireCommand, } from "./sim-sdk-wire-command.js";
3
- import { readSimSdkWireInput } from "./sim-sdk-wire-json.js";
4
- import { readSimSdkWireCredentialScope, readSimSdkWireOperation, } from "./sim-sdk-wire-operation.js";
5
- import { simSdkWireErrorResponse, simSdkWireOutputResponse, } from "./sim-sdk-wire-response.js";
3
+ import { readSimSdkWireCall } from "./sim-sdk-wire-call.js";
4
+ import { readSimSdkWireCredentialScope } from "./sim-sdk-wire-operation.js";
6
5
  import { simSdkUnbridgedWireRequest } from "./sim-sdk-wire-unbridged.js";
7
6
  /**
8
7
  * Dispatches AWS API requests to simulated AWS service operations.
@@ -44,20 +43,19 @@ export class SimSdkWireDispatcher {
44
43
  * relies on.
45
44
  */
46
45
  async dispatch(request, caller) {
47
- const operation = readSimSdkWireOperation(request);
48
- if (operation === undefined) {
46
+ const call = readSimSdkWireCall(request);
47
+ if (call === undefined) {
49
48
  throw simSdkUnbridgedWireRequest(request);
50
49
  }
51
50
  // The operation runs in the Region it was signed for or, unsigned, the
52
51
  // Region of the code that sent it.
53
52
  const scope = readSimSdkWireCredentialScope(request);
54
- const contentType = request.headers["content-type"];
55
53
  try {
56
- const output = await this.dispatcher.dispatch(makeSimSdkWireCommand(operation.commandName, readSimSdkWireInput(request.body)), makeSimSdkWireClient(operation.serviceId, scope?.regionName ?? this.regionName), undefined, caller);
57
- return simSdkWireOutputResponse(output, contentType);
54
+ const output = await this.dispatcher.dispatch(makeSimSdkWireCommand(call.commandName, call.input()), makeSimSdkWireClient(call.serviceId, scope?.regionName ?? this.regionName), undefined, caller);
55
+ return await call.outputResponse(output);
58
56
  }
59
57
  catch (error) {
60
- return simSdkWireErrorResponse(error, contentType);
58
+ return call.errorResponse(error);
61
59
  }
62
60
  }
63
61
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The media type of an AWS event stream response body.
3
+ */
4
+ export declare const simSdkWireEventStreamContentType = "application/vnd.amazon.eventstream";
5
+ /**
6
+ * Encode an event stream as the `application/vnd.amazon.eventstream` body an
7
+ * SDK reads it from.
8
+ *
9
+ * Each event is a union with one member set, as an SDK hands it to calling
10
+ * code (`{ chunk: { bytes } }` or `{ contentBlockDelta: { ... } }`). The
11
+ * member name is the message's event type, and its value is the JSON payload.
12
+ *
13
+ * The whole stream is read before the body is built. A simulated stream has
14
+ * every event available at once, so sending them together loses nothing.
15
+ */
16
+ export declare function simSdkWireEventStreamBody(events: AsyncIterable<object>): Promise<Uint8Array>;
@@ -0,0 +1,79 @@
1
+ import { crc32 } from "../../util/zip/crc32.js";
2
+ import { simSdkWireJsonBody } from "./sim-sdk-wire-json.js";
3
+ /**
4
+ * The media type of an AWS event stream response body.
5
+ */
6
+ export const simSdkWireEventStreamContentType = "application/vnd.amazon.eventstream";
7
+ /**
8
+ * The header value type code for a string, the only type written here.
9
+ */
10
+ const stringHeaderType = 7;
11
+ /**
12
+ * The bytes of the prelude: the total length, the headers length and the
13
+ * prelude checksum, each four bytes.
14
+ */
15
+ const preludeLength = 12;
16
+ /**
17
+ * The bytes of the checksum closing every message.
18
+ */
19
+ const messageChecksumLength = 4;
20
+ /**
21
+ * Encode an event stream as the `application/vnd.amazon.eventstream` body an
22
+ * SDK reads it from.
23
+ *
24
+ * Each event is a union with one member set, as an SDK hands it to calling
25
+ * code (`{ chunk: { bytes } }` or `{ contentBlockDelta: { ... } }`). The
26
+ * member name is the message's event type, and its value is the JSON payload.
27
+ *
28
+ * The whole stream is read before the body is built. A simulated stream has
29
+ * every event available at once, so sending them together loses nothing.
30
+ */
31
+ export async function simSdkWireEventStreamBody(events) {
32
+ const messages = [];
33
+ for await (const event of events) {
34
+ const members = Object.entries(event).filter(([, value]) => value !== undefined);
35
+ for (const [eventType, value] of members) {
36
+ messages.push(eventMessage(eventType, simSdkWireJsonBody(value)));
37
+ }
38
+ }
39
+ return Buffer.concat(messages);
40
+ }
41
+ /**
42
+ * Encode one event message.
43
+ *
44
+ * The layout is the prelude, the headers, the payload and a CRC-32 of
45
+ * everything before it. The prelude ends with a CRC-32 of its first eight
46
+ * bytes.
47
+ */
48
+ function eventMessage(eventType, payload) {
49
+ const headers = Buffer.concat([
50
+ stringHeader(":event-type", eventType),
51
+ stringHeader(":content-type", "application/json"),
52
+ stringHeader(":message-type", "event"),
53
+ ]);
54
+ const totalLength = preludeLength + headers.length + payload.length + messageChecksumLength;
55
+ const checksumOffset = totalLength - messageChecksumLength;
56
+ const message = Buffer.alloc(totalLength);
57
+ message.writeUInt32BE(totalLength, 0);
58
+ message.writeUInt32BE(headers.length, 4);
59
+ message.writeUInt32BE(crc32(message.subarray(0, 8)), 8);
60
+ headers.copy(message, preludeLength);
61
+ message.set(payload, preludeLength + headers.length);
62
+ message.writeUInt32BE(crc32(message.subarray(0, checksumOffset)), checksumOffset);
63
+ return message;
64
+ }
65
+ /**
66
+ * Encode one string-valued header: the name length and name, the type code,
67
+ * and the value length and value.
68
+ */
69
+ function stringHeader(name, value) {
70
+ const nameBytes = Buffer.from(name);
71
+ const valueBytes = Buffer.from(value);
72
+ const header = Buffer.alloc(4 + nameBytes.length + valueBytes.length);
73
+ header.writeUInt8(nameBytes.length, 0);
74
+ nameBytes.copy(header, 1);
75
+ header.writeUInt8(stringHeaderType, 1 + nameBytes.length);
76
+ header.writeUInt16BE(valueBytes.length, 2 + nameBytes.length);
77
+ valueBytes.copy(header, 4 + nameBytes.length);
78
+ return header;
79
+ }
@@ -16,8 +16,8 @@ import { readSimSdkWireCredentialScope } from "./sim-sdk-wire-operation.js";
16
16
  export function simSdkUnbridgedWireRequest(request) {
17
17
  const service = readSimSdkWireCredentialScope(request)?.signingName ?? request.hostname;
18
18
  return new SimSdkUnbridgedWireRequestError(`Cannot answer a request to ${service} from the simulation: a ` +
19
- "serialized AWS API request can only be routed for a service using " +
20
- "the AWS JSON protocol. Where this is an AWS SDK bundled into " +
19
+ "serialized AWS API request can only be routed for S3, Bedrock " +
20
+ "Runtime and the services using the AWS JSON protocol. Where this is an AWS SDK bundled into " +
21
21
  "function code, leaving the SDK out of the deployment package puts it " +
22
22
  "back on the module interception path, which every simulated service " +
23
23
  "is reachable through.");
@@ -24,9 +24,13 @@ export class SimBedrockAuthorizer {
24
24
  caller: options.caller,
25
25
  });
26
26
  if (decision.isDenied) {
27
- throw new SimBedrockAccessDeniedException(`User: ${this.callerIdentifier.format(decision.caller.principal)} is ` +
28
- `not authorized to perform: ${action} on resource: ${resource} ` +
29
- `because no identity-based policy allows the ${action} action`);
27
+ // A service control policy denial names the policy, as other services'
28
+ // denials do.
29
+ const reason = decision.denialReason ??
30
+ `because no identity-based policy allows the ${action} action`;
31
+ const caller = this.callerIdentifier.format(decision.caller.principal);
32
+ throw new SimBedrockAccessDeniedException(`User: ${caller} is not authorized to perform: ${action} on ` +
33
+ `resource: ${resource} ${reason}`);
30
34
  }
31
35
  return decision.caller;
32
36
  }
@@ -42,8 +42,8 @@ export declare class SimSdkLambdaVmModuleProvider implements SimLambdaVmSdkModul
42
42
  private readonly providedModules;
43
43
  constructor(properties: SimSdkLambdaVmModuleProviderProperties);
44
44
  /**
45
- * Provide an intercepted AWS SDK package or HTTP transport module, or
46
- * undefined for other specifiers.
45
+ * Provide an intercepted AWS SDK package or HTTP or HTTP/2 transport
46
+ * module, or undefined for other specifiers.
47
47
  */
48
48
  provideModule(specifier: string): unknown;
49
49
  /**
@@ -1,7 +1,7 @@
1
1
  import { SimSdkModuleClientInterceptor } from "../../../../../../sdk/module/sim-sdk-module-client-interceptor.js";
2
2
  import { SimSdkCommandDispatcher } from "../../../../../../sdk/sim-sdk-command-dispatcher.js";
3
3
  import { SimLambdaAwsApiOutbound } from "../../../outbound/sim-lambda-aws-api-outbound.js";
4
- import { isSimLambdaHttpModuleSpecifier, makeSimLambdaHttpModule, simLambdaHttpModuleScheme, } from "../../../outbound/sim-lambda-http-module.js";
4
+ import { isSimLambdaTransportModuleSpecifier, makeSimLambdaTransportModule, } from "../../../outbound/sim-lambda-transport-module.js";
5
5
  import { isModuleNotFoundError, requireHostModule, } from "../sim-lambda-host-modules.js";
6
6
  import { SimLambdaSdkPackagesNotInstalledError } from "./sim-lambda-sdk-packages-not-installed.error.js";
7
7
  import { awsSdkPackagePrefix, } from "./sim-lambda-vm-sdk-module-provider.js";
@@ -37,11 +37,11 @@ export class SimSdkLambdaVmModuleProvider {
37
37
  this.requireModule = properties.requireModule ?? requireHostModule;
38
38
  }
39
39
  /**
40
- * Provide an intercepted AWS SDK package or HTTP transport module, or
41
- * undefined for other specifiers.
40
+ * Provide an intercepted AWS SDK package or HTTP or HTTP/2 transport
41
+ * module, or undefined for other specifiers.
42
42
  */
43
43
  provideModule(specifier) {
44
- const isTransport = isSimLambdaHttpModuleSpecifier(specifier);
44
+ const isTransport = isSimLambdaTransportModuleSpecifier(specifier);
45
45
  if (!isTransport && !specifier.startsWith(awsSdkPackagePrefix)) {
46
46
  return undefined;
47
47
  }
@@ -51,11 +51,7 @@ export class SimSdkLambdaVmModuleProvider {
51
51
  }
52
52
  const hostExports = this.hostModuleExports(specifier);
53
53
  const module = isTransport
54
- ? makeSimLambdaHttpModule({
55
- hostModule: hostExports,
56
- outbound: this.outboundHttp,
57
- scheme: simLambdaHttpModuleScheme(specifier),
58
- })
54
+ ? makeSimLambdaTransportModule(specifier, hostExports, this.outboundHttp)
59
55
  : this.interceptor.interceptModule(hostExports);
60
56
  this.providedModules.set(specifier, module);
61
57
  return module;
@@ -29,7 +29,7 @@ export interface SimLambdaVmContextProperties {
29
29
  /**
30
30
  * Create the sandbox vm context that sim Lambda function code runs in.
31
31
  *
32
- * The context provides the common globals a Node.js Lambda runtime offers,
32
+ * The context provides the globals a Node.js Lambda runtime offers,
33
33
  * including an AWS-like process.env holding the standard runtime variables
34
34
  * and any variables declared for the function, and the writable standard
35
35
  * streams function code writes its output to. Everything else must be part
@@ -6,11 +6,12 @@ import { simLambdaNoOutputSink, } from "../../logging/sim-lambda-output-sink.js"
6
6
  import { makeSimLambdaOutboundFetch } from "../../outbound/sim-lambda-outbound-fetch.js";
7
7
  import { simLambdaClearInterval, simLambdaClearTimeout, } from "../../invoke/timer/sim-lambda-clear-timer.js";
8
8
  import { simLambdaSetInterval, simLambdaSetTimeout, } from "../../invoke/timer/sim-lambda-process-timers.js";
9
+ import { simLambdaVmHostGlobals } from "./sim-lambda-vm-host-globals.js";
9
10
  import { SimLambdaVmOutputStream } from "./sim-lambda-vm-output-stream.js";
10
11
  /**
11
12
  * Create the sandbox vm context that sim Lambda function code runs in.
12
13
  *
13
- * The context provides the common globals a Node.js Lambda runtime offers,
14
+ * The context provides the globals a Node.js Lambda runtime offers,
14
15
  * including an AWS-like process.env holding the standard runtime variables
15
16
  * and any variables declared for the function, and the writable standard
16
17
  * streams function code writes its output to. Everything else must be part
@@ -33,12 +34,12 @@ export function makeSimLambdaVmContext(properties) {
33
34
  // What function code writes reaches the function's log group through here.
34
35
  stdout.recordTo(sink);
35
36
  stderr.recordTo(sink);
36
- return vm.createContext({
37
+ const context = vm.createContext({
38
+ ...simLambdaVmHostGlobals(),
37
39
  // The sandbox's console is built over those streams, as the real
38
40
  // runtime's is, so everything function code prints goes to one place
39
41
  // whether it printed through the console or wrote to the stream itself.
40
42
  console: new Console({ stdout, stderr }),
41
- Buffer,
42
43
  process: {
43
44
  env: environment.variables(),
44
45
  stdout,
@@ -55,25 +56,13 @@ export function makeSimLambdaVmContext(properties) {
55
56
  clearTimeout: simLambdaClearTimeout,
56
57
  setInterval: simLambdaSetInterval,
57
58
  clearInterval: simLambdaClearInterval,
58
- setImmediate,
59
- queueMicrotask,
60
- TextEncoder,
61
- TextDecoder,
62
- URL,
63
- URLSearchParams,
64
- structuredClone,
65
- crypto,
66
- // The Fetch API the Node.js runtime provides, with the client itself
67
- // routed into the simulation. The rest are the host's own, so the objects
68
- // a handler builds and the ones the simulation answers with are the same
69
- // kind of thing.
59
+ // The Fetch API client, routed into the simulation. The types it works
60
+ // with are the host's own.
70
61
  fetch: makeSimLambdaOutboundFetch(outboundHttp),
71
- Headers,
72
- Request,
73
- Response,
74
- FormData,
75
- Blob,
76
- AbortController,
77
- AbortSignal,
78
62
  });
63
+ // Node.js names its global object `global` as well as `globalThis`, and
64
+ // bundlers emit references to it for older packages. It has to be the
65
+ // sandbox's own global, which only exists once the context does.
66
+ vm.runInContext("globalThis.global = globalThis;", context);
67
+ return context;
79
68
  }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * The host's own value for each global the sandbox passes through.
3
+ */
4
+ export declare function simLambdaVmHostGlobals(): Record<string, unknown>;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * The globals the Node.js runtime provides that the sandbox passes through
3
+ * from the host as they are.
4
+ *
5
+ * Anything here is the host's own object, so what function code builds with
6
+ * one and what the simulation answers with are the same kind of thing. The
7
+ * globals the sandbox replaces (the console, `process`, `Date`, the timers
8
+ * and `fetch`) are built in `makeSimLambdaVmContext` instead.
9
+ */
10
+ const simLambdaVmHostGlobalNames = [
11
+ "Buffer",
12
+ "setImmediate",
13
+ // The host's, since the sandbox's `setImmediate` is the host's too.
14
+ "clearImmediate",
15
+ "queueMicrotask",
16
+ "structuredClone",
17
+ "atob",
18
+ "btoa",
19
+ "TextEncoder",
20
+ "TextDecoder",
21
+ "URL",
22
+ "URLSearchParams",
23
+ "URLPattern",
24
+ "DOMException",
25
+ // The Fetch API types. The client itself is the sandbox's own.
26
+ "Headers",
27
+ "Request",
28
+ "Response",
29
+ "FormData",
30
+ "Blob",
31
+ "File",
32
+ "AbortController",
33
+ "AbortSignal",
34
+ // WHATWG streams, which bundled SDK clients build their event streams on.
35
+ "ReadableStream",
36
+ "ReadableStreamDefaultReader",
37
+ "ReadableStreamBYOBReader",
38
+ "ReadableStreamBYOBRequest",
39
+ "ReadableByteStreamController",
40
+ "ReadableStreamDefaultController",
41
+ "WritableStream",
42
+ "WritableStreamDefaultController",
43
+ "WritableStreamDefaultWriter",
44
+ "TransformStream",
45
+ "TransformStreamDefaultController",
46
+ "ByteLengthQueuingStrategy",
47
+ "CountQueuingStrategy",
48
+ "TextEncoderStream",
49
+ "TextDecoderStream",
50
+ "CompressionStream",
51
+ "DecompressionStream",
52
+ // Events and messaging.
53
+ "Event",
54
+ "EventTarget",
55
+ "CustomEvent",
56
+ "MessageEvent",
57
+ "CloseEvent",
58
+ "MessageChannel",
59
+ "MessagePort",
60
+ "BroadcastChannel",
61
+ "WebSocket",
62
+ // Web Crypto.
63
+ "crypto",
64
+ "Crypto",
65
+ "CryptoKey",
66
+ "SubtleCrypto",
67
+ // Performance reads host time, while `Date` reads the simulated clock.
68
+ "performance",
69
+ "Performance",
70
+ "PerformanceEntry",
71
+ "PerformanceMark",
72
+ "PerformanceMeasure",
73
+ "PerformanceObserver",
74
+ "PerformanceObserverEntryList",
75
+ "PerformanceResourceTiming",
76
+ "navigator",
77
+ "Navigator",
78
+ ];
79
+ /**
80
+ * The host's own value for each global the sandbox passes through.
81
+ */
82
+ export function simLambdaVmHostGlobals() {
83
+ return Object.fromEntries(simLambdaVmHostGlobalNames.map((name) => [
84
+ name,
85
+ Reflect.get(globalThis, name),
86
+ ]));
87
+ }
@@ -12,5 +12,8 @@ import type { SimLambdaOutboundHttp } from "../outbound/sim-lambda-outbound-http
12
12
  * modules export as it was. The built-in modules' named ES module exports are
13
13
  * a snapshot of those properties, so they are re-synchronised afterwards for
14
14
  * the handler that imported `request` by name rather than the module.
15
+ *
16
+ * `node:http2` has `connect` replaced the same way. Bedrock Runtime's SDK
17
+ * client sends over HTTP/2.
15
18
  */
16
19
  export declare function installSimLambdaOutboundClients(outbound: SimLambdaOutboundHttp): void;
@@ -1,7 +1,9 @@
1
1
  import http from "node:http";
2
+ import http2 from "node:http2";
2
3
  import https from "node:https";
3
4
  import { syncBuiltinESMExports } from "node:module";
4
5
  import { makeSimLambdaHttpModule } from "../outbound/sim-lambda-http-module.js";
6
+ import { makeSimLambdaHttp2Module } from "../outbound/sim-lambda-http2-module.js";
5
7
  import { makeSimLambdaOutboundFetch } from "../outbound/sim-lambda-outbound-fetch.js";
6
8
  /**
7
9
  * The transport modules an in-process handler makes requests through, with the
@@ -24,6 +26,9 @@ const transportModules = [
24
26
  * modules export as it was. The built-in modules' named ES module exports are
25
27
  * a snapshot of those properties, so they are re-synchronised afterwards for
26
28
  * the handler that imported `request` by name rather than the module.
29
+ *
30
+ * `node:http2` has `connect` replaced the same way. Bedrock Runtime's SDK
31
+ * client sends over HTTP/2.
27
32
  */
28
33
  export function installSimLambdaOutboundClients(outbound) {
29
34
  const hostFetch = globalThis.fetch;
@@ -38,5 +43,10 @@ export function installSimLambdaOutboundClients(outbound) {
38
43
  patched["request"] = routed["request"];
39
44
  patched["get"] = routed["get"];
40
45
  }
46
+ const http2Module = http2;
47
+ http2Module["connect"] = makeSimLambdaHttp2Module({
48
+ hostModule: http2Module,
49
+ outbound,
50
+ })["connect"];
41
51
  syncBuiltinESMExports();
42
52
  }
@@ -0,0 +1,32 @@
1
+ import { AsyncResource } from "node:async_hooks";
2
+ import type http2 from "node:http2";
3
+ /**
4
+ * A request sent on a simulated session, waiting for its answer.
5
+ */
6
+ export interface SimLambdaHttp2Exchange {
7
+ readonly stream: http2.ClientHttp2Stream;
8
+ /**
9
+ * The asynchronous context the request was sent from. A pooled session
10
+ * outlives the invocation that opened it, and each request on it belongs to
11
+ * whichever invocation sent it.
12
+ */
13
+ readonly scope: AsyncResource;
14
+ }
15
+ /**
16
+ * The requests in flight on one simulated session, each joined to the
17
+ * asynchronous context it was sent from.
18
+ */
19
+ export declare class SimLambdaHttp2Exchanges {
20
+ private readonly exchanges;
21
+ /**
22
+ * Tag each request the session sends with the context it was sent from.
23
+ */
24
+ track(session: http2.ClientHttp2Session): void;
25
+ /**
26
+ * The exchange a request arriving at the session's server belongs to.
27
+ *
28
+ * Every request on the session's client is tagged by `track`, and nothing
29
+ * else can reach its server.
30
+ */
31
+ exchangeFor(request: http2.Http2ServerRequest): SimLambdaHttp2Exchange;
32
+ }
@@ -0,0 +1,39 @@
1
+ import { AsyncResource } from "node:async_hooks";
2
+ import { randomUUID } from "node:crypto";
3
+ import { assertDefined } from "../../../../util/type-guard/defined.js";
4
+ import { simLambdaHttp2ExchangeHeaderName, simLambdaHttp2ExchangeId, } from "./sim-lambda-http2-messages.js";
5
+ /**
6
+ * The requests in flight on one simulated session, each joined to the
7
+ * asynchronous context it was sent from.
8
+ */
9
+ export class SimLambdaHttp2Exchanges {
10
+ exchanges = new Map();
11
+ /**
12
+ * Tag each request the session sends with the context it was sent from.
13
+ */
14
+ track(session) {
15
+ const hostRequest = session.request.bind(session);
16
+ const request = (headers, options) => {
17
+ const exchangeId = randomUUID();
18
+ const stream = hostRequest({ ...headers, [simLambdaHttp2ExchangeHeaderName]: exchangeId }, options);
19
+ this.exchanges.set(exchangeId, {
20
+ stream,
21
+ scope: new AsyncResource("SimLambdaHttp2Request"),
22
+ });
23
+ stream.once("close", () => this.exchanges.delete(exchangeId));
24
+ return stream;
25
+ };
26
+ Object.assign(session, { request });
27
+ }
28
+ /**
29
+ * The exchange a request arriving at the session's server belongs to.
30
+ *
31
+ * Every request on the session's client is tagged by `track`, and nothing
32
+ * else can reach its server.
33
+ */
34
+ exchangeFor(request) {
35
+ const exchange = this.exchanges.get(simLambdaHttp2ExchangeId(request));
36
+ assertDefined(exchange, "sim Lambda HTTP/2 exchange for a request");
37
+ return exchange;
38
+ }
39
+ }
@@ -0,0 +1,33 @@
1
+ import type http2 from "node:http2";
2
+ import type { SimLambdaOutboundHttp } from "./sim-lambda-outbound-http.js";
3
+ /**
4
+ * The header a request sent on a simulated session carries to say which
5
+ * request it is, so the answer runs in the asynchronous context the request
6
+ * was sent from. The simulation never sees it.
7
+ */
8
+ export declare const simLambdaHttp2ExchangeHeaderName = "x-yulin-sim-lambda-exchange";
9
+ /**
10
+ * The headers of a simulated answer that HTTP/2 can carry.
11
+ */
12
+ export declare function simLambdaHttp2ResponseHeaders(headers: Headers): Record<string, string>;
13
+ /**
14
+ * The request a stream carried, as the request the simulation is asked to
15
+ * answer, at the authority the session was connected to.
16
+ *
17
+ * A pooled session outlives the invocation that opened it. A request sent on
18
+ * it once no invocation is running has nothing to answer it, and is refused
19
+ * saying so.
20
+ */
21
+ export declare function simLambdaHttp2Request(authority: URL, outbound: SimLambdaOutboundHttp, request: http2.Http2ServerRequest): Promise<Request>;
22
+ /**
23
+ * The exchange tag a request on a simulated session carries.
24
+ */
25
+ export declare function simLambdaHttp2ExchangeId(request: http2.Http2ServerRequest): string;
26
+ /**
27
+ * The options and listener a `connect` call passed after the authority. The
28
+ * listener comes back as a list of none or one, to pass on as it came.
29
+ */
30
+ export declare function simLambdaHttp2ConnectArguments(callArguments: readonly unknown[]): {
31
+ readonly options: object | undefined;
32
+ readonly listener: unknown[];
33
+ };
@@ -0,0 +1,87 @@
1
+ /**
2
+ * The header a request sent on a simulated session carries to say which
3
+ * request it is, so the answer runs in the asynchronous context the request
4
+ * was sent from. The simulation never sees it.
5
+ */
6
+ export const simLambdaHttp2ExchangeHeaderName = "x-yulin-sim-lambda-exchange";
7
+ /**
8
+ * The response headers HTTP/2 forbids, which a simulated answer may still
9
+ * carry from an HTTP/1.1 shape.
10
+ */
11
+ const connectionHeaderNames = new Set([
12
+ "connection",
13
+ "keep-alive",
14
+ "proxy-connection",
15
+ "transfer-encoding",
16
+ "upgrade",
17
+ ]);
18
+ /**
19
+ * The headers of a request, less the HTTP/2 pseudo-headers and the exchange
20
+ * tag.
21
+ */
22
+ function requestHeaders(headers) {
23
+ return Object.fromEntries(Object.entries(headers).flatMap(([name, value]) => value === undefined ||
24
+ name === simLambdaHttp2ExchangeHeaderName ||
25
+ name.startsWith(":")
26
+ ? []
27
+ : [[name, Array.isArray(value) ? value.join(", ") : value]]));
28
+ }
29
+ /**
30
+ * The headers of a simulated answer that HTTP/2 can carry.
31
+ */
32
+ export function simLambdaHttp2ResponseHeaders(headers) {
33
+ return Object.fromEntries([...headers].filter(([name]) => !connectionHeaderNames.has(name)));
34
+ }
35
+ /**
36
+ * The request a stream carried, as the request the simulation is asked to
37
+ * answer, at the authority the session was connected to.
38
+ *
39
+ * A pooled session outlives the invocation that opened it. A request sent on
40
+ * it once no invocation is running has nothing to answer it, and is refused
41
+ * saying so.
42
+ */
43
+ export async function simLambdaHttp2Request(authority, outbound, request) {
44
+ const { hostname, host, protocol } = authority;
45
+ if (!outbound.serves(hostname)) {
46
+ throw sessionEndedError(hostname);
47
+ }
48
+ const chunks = [];
49
+ for await (const chunk of request) {
50
+ chunks.push(chunk);
51
+ }
52
+ const body = Buffer.concat(chunks);
53
+ return new Request(`${protocol}//${host}${request.url}`, {
54
+ method: request.method,
55
+ headers: requestHeaders(request.headers),
56
+ ...(body.length > 0 && { body }),
57
+ });
58
+ }
59
+ /**
60
+ * The exchange tag a request on a simulated session carries.
61
+ */
62
+ export function simLambdaHttp2ExchangeId(request) {
63
+ // oxlint-disable-next-line security/detect-object-injection -- this module's own fixed header name.
64
+ return String(request.headers[simLambdaHttp2ExchangeHeaderName]);
65
+ }
66
+ /**
67
+ * The options and listener a `connect` call passed after the authority. The
68
+ * listener comes back as a list of none or one, to pass on as it came.
69
+ */
70
+ export function simLambdaHttp2ConnectArguments(callArguments) {
71
+ const [, second, third] = callArguments;
72
+ const listener = typeof second === "function" ? second : third;
73
+ return {
74
+ options: typeof second === "object" ? (second ?? undefined) : undefined,
75
+ listener: listener === undefined ? [] : [listener],
76
+ };
77
+ }
78
+ /**
79
+ * Refuse a request on a session a sim Lambda invocation opened, sent once no
80
+ * invocation is running to answer it.
81
+ */
82
+ function sessionEndedError(hostname) {
83
+ return new Error(`Cannot send a request to ${hostname} on an HTTP/2 session a sim ` +
84
+ "Lambda invocation opened, once no invocation is running. Create " +
85
+ "the SDK client inside the handler, or send this call from a " +
86
+ "handler invocation.");
87
+ }
@@ -0,0 +1,27 @@
1
+ import type { SimLambdaOutboundHttp } from "./sim-lambda-outbound-http.js";
2
+ /**
3
+ * A `node:http2` module, as far as this needs to know.
4
+ */
5
+ type NodeHttp2Module = Record<string, unknown>;
6
+ /**
7
+ * Whether a module specifier names the HTTP/2 module.
8
+ */
9
+ export declare function isSimLambdaHttp2ModuleSpecifier(specifier: string): boolean;
10
+ interface SimLambdaHttp2ModuleProperties {
11
+ readonly hostModule: NodeHttp2Module;
12
+ readonly outbound: SimLambdaOutboundHttp;
13
+ }
14
+ /**
15
+ * Build the `node:http2` module sim Lambda function code connects through.
16
+ *
17
+ * Everything the real module exports is kept, and only `connect` is
18
+ * replaced. A session to a hostname the simulation serves is a real HTTP/2
19
+ * session, connected over an in-memory socket pair to a server in this
20
+ * process that hands each request to the simulation. Every other session is
21
+ * the host module's, connected exactly as it was asked for.
22
+ *
23
+ * Bedrock Runtime's SDK client sends over HTTP/2, so this is where its calls
24
+ * from function code reach simulated Bedrock.
25
+ */
26
+ export declare function makeSimLambdaHttp2Module(properties: SimLambdaHttp2ModuleProperties): NodeHttp2Module;
27
+ export {};
@@ -0,0 +1,53 @@
1
+ import { SimLambdaHttp2Session, } from "./sim-lambda-http2-session.js";
2
+ /**
3
+ * The specifiers a bundler and a hand-written require may ask for the
4
+ * HTTP/2 module by.
5
+ */
6
+ const http2ModuleSpecifiers = new Set([
7
+ "http2",
8
+ "node:http2",
9
+ ]);
10
+ /**
11
+ * Whether a module specifier names the HTTP/2 module.
12
+ */
13
+ export function isSimLambdaHttp2ModuleSpecifier(specifier) {
14
+ return http2ModuleSpecifiers.has(specifier);
15
+ }
16
+ /**
17
+ * Build the `node:http2` module sim Lambda function code connects through.
18
+ *
19
+ * Everything the real module exports is kept, and only `connect` is
20
+ * replaced. A session to a hostname the simulation serves is a real HTTP/2
21
+ * session, connected over an in-memory socket pair to a server in this
22
+ * process that hands each request to the simulation. Every other session is
23
+ * the host module's, connected exactly as it was asked for.
24
+ *
25
+ * Bedrock Runtime's SDK client sends over HTTP/2, so this is where its calls
26
+ * from function code reach simulated Bedrock.
27
+ */
28
+ export function makeSimLambdaHttp2Module(properties) {
29
+ const { hostModule, outbound } = properties;
30
+ const hostConnect = hostModule["connect"];
31
+ return {
32
+ ...hostModule,
33
+ connect: (...callArguments) => {
34
+ const authority = authorityUrl(callArguments[0]);
35
+ if (authority === undefined || !outbound.serves(authority.hostname)) {
36
+ return hostConnect(...callArguments);
37
+ }
38
+ return new SimLambdaHttp2Session(authority, outbound).connect(hostConnect, callArguments);
39
+ },
40
+ };
41
+ }
42
+ /**
43
+ * The URL `connect` was asked for, as a string or a URL, or undefined for
44
+ * anything that is not one.
45
+ */
46
+ function authorityUrl(authority) {
47
+ if (authority instanceof URL) {
48
+ return authority;
49
+ }
50
+ return typeof authority === "string" && URL.canParse(authority)
51
+ ? new URL(authority)
52
+ : undefined;
53
+ }
@@ -0,0 +1,31 @@
1
+ import http2 from "node:http2";
2
+ import type { SimLambdaOutboundHttp } from "./sim-lambda-outbound-http.js";
3
+ /**
4
+ * The host module's `connect`.
5
+ */
6
+ export type SimLambdaHttp2Connect = (...callArguments: unknown[]) => http2.ClientHttp2Session;
7
+ /**
8
+ * One HTTP/2 session the simulation answers.
9
+ */
10
+ export declare class SimLambdaHttp2Session {
11
+ private readonly authority;
12
+ private readonly outbound;
13
+ private readonly exchanges;
14
+ constructor(authority: URL, outbound: SimLambdaOutboundHttp);
15
+ /**
16
+ * Connect the host module's client to an in-process server.
17
+ *
18
+ * The host client does all the HTTP/2 work, so streams, flow control,
19
+ * timeouts and resets behave as they do over a network socket. Only the
20
+ * socket it runs over is supplied.
21
+ */
22
+ connect(hostConnect: SimLambdaHttp2Connect, callArguments: readonly unknown[]): http2.ClientHttp2Session;
23
+ private answer;
24
+ /**
25
+ * Answer one request from the simulation.
26
+ *
27
+ * A request the simulation cannot answer fails the client's stream with the
28
+ * error that explains it, which is what the SDK rejects the call with.
29
+ */
30
+ private respond;
31
+ }
@@ -0,0 +1,57 @@
1
+ import http2 from "node:http2";
2
+ import { duplexPair } from "node:stream";
3
+ import { simLambdaHttp2ConnectArguments, simLambdaHttp2Request, simLambdaHttp2ResponseHeaders, } from "./sim-lambda-http2-messages.js";
4
+ import { SimLambdaHttp2Exchanges } from "./sim-lambda-http2-exchanges.js";
5
+ /**
6
+ * One HTTP/2 session the simulation answers.
7
+ */
8
+ export class SimLambdaHttp2Session {
9
+ authority;
10
+ outbound;
11
+ exchanges = new SimLambdaHttp2Exchanges();
12
+ constructor(authority, outbound) {
13
+ this.authority = authority;
14
+ this.outbound = outbound;
15
+ }
16
+ /**
17
+ * Connect the host module's client to an in-process server.
18
+ *
19
+ * The host client does all the HTTP/2 work, so streams, flow control,
20
+ * timeouts and resets behave as they do over a network socket. Only the
21
+ * socket it runs over is supplied.
22
+ */
23
+ connect(hostConnect, callArguments) {
24
+ const { options, listener } = simLambdaHttp2ConnectArguments(callArguments);
25
+ const server = http2.createServer((request, response) => {
26
+ this.answer(request, response);
27
+ });
28
+ const [clientSocket, serverSocket] = duplexPair();
29
+ server.emit("connection", serverSocket);
30
+ const session = hostConnect(this.authority, { ...options, createConnection: () => clientSocket }, ...listener);
31
+ this.exchanges.track(session);
32
+ return session;
33
+ }
34
+ answer(request, response) {
35
+ const exchange = this.exchanges.exchangeFor(request);
36
+ exchange.scope.runInAsyncScope(() => {
37
+ void this.respond(request, response, exchange.stream);
38
+ });
39
+ }
40
+ /**
41
+ * Answer one request from the simulation.
42
+ *
43
+ * A request the simulation cannot answer fails the client's stream with the
44
+ * error that explains it, which is what the SDK rejects the call with.
45
+ */
46
+ async respond(request, response, clientStream) {
47
+ try {
48
+ const answered = await this.outbound.fetch(await simLambdaHttp2Request(this.authority, this.outbound, request));
49
+ const body = Buffer.from(await answered.arrayBuffer());
50
+ response.writeHead(answered.status, simLambdaHttp2ResponseHeaders(answered.headers));
51
+ response.end(body);
52
+ }
53
+ catch (error) {
54
+ clientStream.destroy(error);
55
+ }
56
+ }
57
+ }
@@ -0,0 +1,11 @@
1
+ import type { SimLambdaOutboundHttp } from "./sim-lambda-outbound-http.js";
2
+ /**
3
+ * Whether a module specifier names one of the transport modules function code
4
+ * makes requests through: `node:http`, `node:https` or `node:http2`.
5
+ */
6
+ export declare function isSimLambdaTransportModuleSpecifier(specifier: string): boolean;
7
+ /**
8
+ * The transport module a specifier names, with its requests to the hostnames
9
+ * the simulation serves answered by the simulation.
10
+ */
11
+ export declare function makeSimLambdaTransportModule(specifier: string, hostModule: Record<string, unknown>, outbound: SimLambdaOutboundHttp): Record<string, unknown>;
@@ -0,0 +1,24 @@
1
+ import { isSimLambdaHttpModuleSpecifier, makeSimLambdaHttpModule, simLambdaHttpModuleScheme, } from "./sim-lambda-http-module.js";
2
+ import { isSimLambdaHttp2ModuleSpecifier, makeSimLambdaHttp2Module, } from "./sim-lambda-http2-module.js";
3
+ /**
4
+ * Whether a module specifier names one of the transport modules function code
5
+ * makes requests through: `node:http`, `node:https` or `node:http2`.
6
+ */
7
+ export function isSimLambdaTransportModuleSpecifier(specifier) {
8
+ return (isSimLambdaHttpModuleSpecifier(specifier) ||
9
+ isSimLambdaHttp2ModuleSpecifier(specifier));
10
+ }
11
+ /**
12
+ * The transport module a specifier names, with its requests to the hostnames
13
+ * the simulation serves answered by the simulation.
14
+ */
15
+ export function makeSimLambdaTransportModule(specifier, hostModule, outbound) {
16
+ if (isSimLambdaHttp2ModuleSpecifier(specifier)) {
17
+ return makeSimLambdaHttp2Module({ hostModule, outbound });
18
+ }
19
+ return makeSimLambdaHttpModule({
20
+ hostModule,
21
+ outbound,
22
+ scheme: simLambdaHttpModuleScheme(specifier),
23
+ });
24
+ }
@@ -152,6 +152,9 @@ The vm runtime models the real Node.js runtime closely:
152
152
  (`AWS_REGION`, `AWS_LAMBDA_FUNCTION_NAME`, `AWS_LAMBDA_FUNCTION_MEMORY_SIZE`, ...).
153
153
  - The sandbox provides writable `process.stdout` and `process.stderr`, with its `console` built
154
154
  over them, as the real runtime does. See [What a handler prints](#what-a-handler-prints).
155
+ - The sandbox provides the other globals of the Node.js runtime, including `global`, the WHATWG
156
+ streams, `EventTarget`, `atob`, `performance` and `navigator`. `Date` and the timers follow the
157
+ simulated clock. `performance.now()` reads host time.
155
158
  - Import and handler problems surface as invocation errors, with the real runtime error types
156
159
  (`Runtime.ImportModuleError`, `Runtime.HandlerNotFound`, `Runtime.UserCodeSyntaxError`,
157
160
  `Runtime.MalformedHandlerName`). Creation succeeds either way.
@@ -728,8 +731,8 @@ Some deployment packages inline the SDK rather than relying on the runtime to pr
728
731
  `NodejsFunction` does that when given `bundling: { externalModules: [] }`. There is then no
729
732
  `@aws-sdk/*` module left for the runtime to provide, and nothing to intercept.
730
733
 
731
- Those functions reach the simulation anyway. The runtime provides their HTTP transport, `node:http`
732
- and `node:https`, and a request addressed to an AWS API endpoint is answered from the same
734
+ Those functions reach the simulation anyway. The runtime provides their HTTP transport, `node:http`,
735
+ `node:https` and `node:http2`, and a request addressed to an AWS API endpoint is answered from the same
733
736
  simulated services, as the same execution role, instead of going to the network. The execution
734
737
  role credentials are in the function's environment, as real Lambda puts them there. The SDK resolves
735
738
  credentials inside the sandbox, where it would otherwise fail with `CredentialsProviderError`.
@@ -746,6 +749,13 @@ sends. A function can read and write Objects with an `S3Client` its archive inli
746
749
  Role authorizes each request, and a read it holds no `s3:GetObject` for comes back to the SDK as
747
750
  `AccessDenied`.
748
751
 
752
+ Bedrock Runtime states its operation and its model in the path, and its client sends over HTTP/2.
753
+ `Converse`, `ConverseStream`, `InvokeModel` and `InvokeModelWithResponseStream` from a bundled
754
+ `BedrockRuntimeClient` return what `simAws.bedrock().responses()` declares, and the SDK reads the
755
+ two streaming operations as it reads a real event stream. A model id given as an inference profile
756
+ ARN works too. The execution Role authorizes each call, and a denial reaches the handler as
757
+ `AccessDeniedException`.
758
+
749
759
  A bundled call to any other simulated service, such as SNS, SES or Lambda itself, fails with an
750
760
  error naming the service. Leaving the SDK out of that archive puts it back
751
761
  on the module interception path above, which every simulated service is reachable through.
@@ -761,8 +771,8 @@ hostname, along with everything else a handler asks for over HTTP.
761
771
  ## The HTTP requests function code makes
762
772
 
763
773
  A request from a handler to a hostname the simulation serves is answered by the simulation. It
764
- never leaves the process. `fetch`, `node:http` and `node:https` all reach it, so it makes no
765
- difference which client the handler was written with.
774
+ never leaves the process. `fetch`, `node:http`, `node:https` and `node:http2` all reach it, so it
775
+ makes no difference which client the handler was written with.
766
776
 
767
777
  Two kinds of hostname are served. Everything simulated Route53 resolves is answered over the same
768
778
  in-process HTTP entry point a browser on localhost reaches. That covers a
@@ -4358,9 +4368,9 @@ Sim Lambda currently supports:
4358
4368
  - Per-function environment variables with `Environment.Variables`
4359
4369
  - Runtime-provided `@aws-sdk/*` packages inside function code, routed into the owning simulated AWS
4360
4370
  environment
4361
- - `fetch`, `node:http` and `node:https` requests from function code, answered by the simulation for
4362
- every hostname it serves, including a Cognito user pool domain's OAuth endpoints and the JWKS a
4363
- token verifier fetches from the regional Cognito endpoint
4371
+ - `fetch`, `node:http`, `node:https` and `node:http2` requests from function code, answered by the
4372
+ simulation for every hostname it serves, including a Cognito user pool domain's OAuth endpoints
4373
+ and the JWKS a token verifier fetches from the regional Cognito endpoint
4364
4374
  - Execution roles, evaluated against simulated IAM
4365
4375
  - Published versions and aliases, with `PublishVersion`, `ListVersionsByFunction`, `CreateAlias`,
4366
4376
  `UpdateAlias`, `GetAlias`, `ListAliases` and `DeleteAlias`, and a `Qualifier` on `Invoke`,
@@ -4470,7 +4480,7 @@ Current documented limitations:
4470
4480
  - Environment variables declared with `Environment.Variables` reach a real in-process handler
4471
4481
  function only while it runs, so a variable read at module scope sees the host process value
4472
4482
  instead. See [Environment variables](#environment-variables).
4473
- - Outbound HTTP is routed for `fetch`, `node:http` and `node:https` alone. A handler using another
4483
+ - Outbound HTTP is routed for `fetch`, `node:http`, `node:https` and `node:http2` alone. A handler using another
4474
4484
  client, or a request made from a handler module while it is being imported, reaches the network.
4475
4485
  A routed response comes back as the simulation answered it, with redirects left for the caller to
4476
4486
  follow. See [The HTTP requests function code makes](#the-http-requests-function-code-makes).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kensio/yulin",
3
- "version": "1.21.28",
3
+ "version": "1.21.29",
4
4
  "description": "AWS system behaviour simulation for isolated unit testing",
5
5
  "repository": "https://github.com/KensioSoftware/yulin",
6
6
  "homepage": "https://yulinsim.dev/",