@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.
- package/dist/sdk/wire/sim-sdk-wire-bedrock-action.d.ts +18 -0
- package/dist/sdk/wire/sim-sdk-wire-bedrock-action.js +71 -0
- package/dist/sdk/wire/sim-sdk-wire-bedrock-input.d.ts +11 -0
- package/dist/sdk/wire/sim-sdk-wire-bedrock-input.js +45 -0
- package/dist/sdk/wire/sim-sdk-wire-bedrock.d.ts +10 -0
- package/dist/sdk/wire/sim-sdk-wire-bedrock.js +47 -0
- package/dist/sdk/wire/sim-sdk-wire-call.d.ts +30 -0
- package/dist/sdk/wire/sim-sdk-wire-call.js +32 -0
- package/dist/sdk/wire/sim-sdk-wire-dispatcher.js +7 -9
- package/dist/sdk/wire/sim-sdk-wire-event-stream.d.ts +16 -0
- package/dist/sdk/wire/sim-sdk-wire-event-stream.js +79 -0
- package/dist/sdk/wire/sim-sdk-wire-unbridged.js +2 -2
- package/dist/service/bedrock/command/authorize/sim-bedrock-authorizer.js +7 -3
- package/dist/service/lambda/function/code/vm/sdk/sim-sdk-lambda-vm-module-provider.d.ts +2 -2
- package/dist/service/lambda/function/code/vm/sdk/sim-sdk-lambda-vm-module-provider.js +5 -9
- package/dist/service/lambda/function/code/vm/sim-lambda-vm-context.d.ts +1 -1
- package/dist/service/lambda/function/code/vm/sim-lambda-vm-context.js +11 -22
- package/dist/service/lambda/function/code/vm/sim-lambda-vm-host-globals.d.ts +4 -0
- package/dist/service/lambda/function/code/vm/sim-lambda-vm-host-globals.js +87 -0
- package/dist/service/lambda/function/invoke/sim-lambda-outbound-clients.d.ts +3 -0
- package/dist/service/lambda/function/invoke/sim-lambda-outbound-clients.js +10 -0
- package/dist/service/lambda/function/outbound/sim-lambda-http2-exchanges.d.ts +32 -0
- package/dist/service/lambda/function/outbound/sim-lambda-http2-exchanges.js +39 -0
- package/dist/service/lambda/function/outbound/sim-lambda-http2-messages.d.ts +33 -0
- package/dist/service/lambda/function/outbound/sim-lambda-http2-messages.js +87 -0
- package/dist/service/lambda/function/outbound/sim-lambda-http2-module.d.ts +27 -0
- package/dist/service/lambda/function/outbound/sim-lambda-http2-module.js +53 -0
- package/dist/service/lambda/function/outbound/sim-lambda-http2-session.d.ts +31 -0
- package/dist/service/lambda/function/outbound/sim-lambda-http2-session.js +57 -0
- package/dist/service/lambda/function/outbound/sim-lambda-transport-module.d.ts +11 -0
- package/dist/service/lambda/function/outbound/sim-lambda-transport-module.js +24 -0
- package/docs/services/lambda/README.md +18 -8
- 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 {
|
|
4
|
-
import { readSimSdkWireCredentialScope
|
|
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
|
|
48
|
-
if (
|
|
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(
|
|
57
|
-
return
|
|
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
|
|
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
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
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 {
|
|
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
|
|
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 =
|
|
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
|
-
?
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
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,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:
|
|
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:
|
|
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:
|
|
4362
|
-
every hostname it serves, including a Cognito user pool domain's OAuth endpoints
|
|
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:
|
|
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