@kensio/yulin 1.21.28 → 1.21.30
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/athena/engine/sim-athena-array-shims.js +1 -1
- package/dist/service/athena/engine/sim-athena-binary-text.d.ts +2 -2
- package/dist/service/athena/engine/sim-athena-binary-text.js +2 -2
- package/dist/service/athena/engine/sim-athena-cast-rewrite.d.ts +30 -0
- package/dist/service/athena/engine/sim-athena-cast-rewrite.js +107 -0
- package/dist/service/athena/engine/sim-athena-cast-shims.d.ts +12 -0
- package/dist/service/athena/engine/sim-athena-cast-shims.js +29 -0
- package/dist/service/athena/engine/sim-athena-cast-text.d.ts +13 -0
- package/dist/service/athena/engine/sim-athena-cast-text.js +66 -0
- package/dist/service/athena/engine/sim-athena-comparison-shims.js +1 -2
- package/dist/service/athena/engine/sim-athena-date-arithmetic-shims.d.ts +4 -5
- package/dist/service/athena/engine/sim-athena-date-arithmetic-shims.js +6 -14
- package/dist/service/athena/engine/sim-athena-date-units.d.ts +1 -1
- package/dist/service/athena/engine/sim-athena-date-units.js +1 -1
- package/dist/service/athena/engine/sim-athena-engine-answer.d.ts +10 -5
- package/dist/service/athena/engine/sim-athena-engine-answer.js +4 -4
- package/dist/service/athena/engine/sim-athena-engine-run.d.ts +4 -0
- package/dist/service/athena/engine/sim-athena-engine-run.js +10 -2
- package/dist/service/athena/engine/sim-athena-regexp-shims.js +2 -2
- package/dist/service/athena/engine/sim-athena-shim-refusal.d.ts +17 -0
- package/dist/service/athena/engine/sim-athena-shim-refusal.js +22 -0
- package/dist/service/athena/engine/sim-athena-shim-registry.d.ts +5 -1
- package/dist/service/athena/engine/sim-athena-shim-registry.js +14 -2
- package/dist/service/athena/engine/sim-athena-shims.js +2 -0
- package/dist/service/athena/engine/sim-athena-sql-translation.d.ts +1 -23
- package/dist/service/athena/engine/sim-athena-sql-translation.js +8 -23
- package/dist/service/athena/engine/sim-athena-translated-sql.d.ts +29 -0
- package/dist/service/athena/engine/sim-athena-translated-sql.js +12 -0
- package/dist/service/athena/engine/sim-athena-turn-down.d.ts +7 -0
- package/dist/service/athena/engine/sim-athena-turn-down.js +11 -0
- package/dist/service/athena/result/sim-athena-query-answer.d.ts +2 -1
- package/dist/service/athena/result/sim-athena-query-answer.js +2 -1
- package/dist/service/athena/sim-athena-snapshot-table.fixture.d.ts +10 -0
- package/dist/service/athena/sim-athena-snapshot-table.fixture.js +39 -0
- 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/athena/README.md +29 -8
- package/docs/services/lambda/README.md +18 -8
- package/package.json +1 -1
|
@@ -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
|
+
}
|