@kensio/yulin 1.21.27 → 1.21.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/config/eslint/cffjs2.eslint.config.js +4 -0
  2. package/dist/sdk/wire/sim-sdk-wire-bedrock-action.d.ts +18 -0
  3. package/dist/sdk/wire/sim-sdk-wire-bedrock-action.js +71 -0
  4. package/dist/sdk/wire/sim-sdk-wire-bedrock-input.d.ts +11 -0
  5. package/dist/sdk/wire/sim-sdk-wire-bedrock-input.js +45 -0
  6. package/dist/sdk/wire/sim-sdk-wire-bedrock.d.ts +10 -0
  7. package/dist/sdk/wire/sim-sdk-wire-bedrock.js +47 -0
  8. package/dist/sdk/wire/sim-sdk-wire-call.d.ts +30 -0
  9. package/dist/sdk/wire/sim-sdk-wire-call.js +32 -0
  10. package/dist/sdk/wire/sim-sdk-wire-dispatcher.js +7 -9
  11. package/dist/sdk/wire/sim-sdk-wire-event-stream.d.ts +16 -0
  12. package/dist/sdk/wire/sim-sdk-wire-event-stream.js +79 -0
  13. package/dist/sdk/wire/sim-sdk-wire-unbridged.js +2 -2
  14. package/dist/service/bedrock/command/authorize/sim-bedrock-authorizer.js +7 -3
  15. package/dist/service/cloudformation/parameters/sim-cfn-parameter-list-type.d.ts +7 -0
  16. package/dist/service/cloudformation/parameters/sim-cfn-parameter-list-type.js +9 -0
  17. package/dist/service/cloudformation/parameters/sim-cfn-parameter-values.d.ts +10 -0
  18. package/dist/service/cloudformation/parameters/sim-cfn-parameter-values.js +20 -0
  19. package/dist/service/cloudformation/parameters/sim-cfn-parameters.type.d.ts +7 -5
  20. package/dist/service/cloudformation/parameters/store/sim-cfn-parameter-store-value-type.js +2 -1
  21. package/dist/service/cloudformation/template/node/fn/join/sim-cfn-fn-join.d.ts +19 -14
  22. package/dist/service/cloudformation/template/node/fn/join/sim-cfn-fn-join.js +47 -28
  23. package/dist/service/cloudformation/template/parse/fn/join/sim-cfn-fn-join-parser.d.ts +7 -4
  24. package/dist/service/cloudformation/template/parse/fn/join/sim-cfn-fn-join-parser.js +11 -7
  25. package/dist/service/lambda/function/code/vm/sdk/sim-sdk-lambda-vm-module-provider.d.ts +2 -2
  26. package/dist/service/lambda/function/code/vm/sdk/sim-sdk-lambda-vm-module-provider.js +5 -9
  27. package/dist/service/lambda/function/code/vm/sim-lambda-vm-context.d.ts +1 -1
  28. package/dist/service/lambda/function/code/vm/sim-lambda-vm-context.js +11 -22
  29. package/dist/service/lambda/function/code/vm/sim-lambda-vm-host-globals.d.ts +4 -0
  30. package/dist/service/lambda/function/code/vm/sim-lambda-vm-host-globals.js +87 -0
  31. package/dist/service/lambda/function/invoke/sim-lambda-outbound-clients.d.ts +3 -0
  32. package/dist/service/lambda/function/invoke/sim-lambda-outbound-clients.js +10 -0
  33. package/dist/service/lambda/function/outbound/sim-lambda-http2-exchanges.d.ts +32 -0
  34. package/dist/service/lambda/function/outbound/sim-lambda-http2-exchanges.js +39 -0
  35. package/dist/service/lambda/function/outbound/sim-lambda-http2-messages.d.ts +33 -0
  36. package/dist/service/lambda/function/outbound/sim-lambda-http2-messages.js +87 -0
  37. package/dist/service/lambda/function/outbound/sim-lambda-http2-module.d.ts +27 -0
  38. package/dist/service/lambda/function/outbound/sim-lambda-http2-module.js +53 -0
  39. package/dist/service/lambda/function/outbound/sim-lambda-http2-session.d.ts +31 -0
  40. package/dist/service/lambda/function/outbound/sim-lambda-http2-session.js +57 -0
  41. package/dist/service/lambda/function/outbound/sim-lambda-transport-module.d.ts +11 -0
  42. package/dist/service/lambda/function/outbound/sim-lambda-transport-module.js +24 -0
  43. package/docs/serve/README.md +1 -1
  44. package/docs/services/cloudformation/README.md +28 -5
  45. package/docs/services/dynamodb/README.md +2 -2
  46. package/docs/services/lambda/README.md +18 -8
  47. package/docs/services/s3/README.md +1 -1
  48. package/docs/services/sqs/README.md +1 -1
  49. package/package.json +2 -2
@@ -1,4 +1,5 @@
1
1
  import { SimCfnNode } from "../../sim-cfn-node.js";
2
+ import { isSimCfnUnresolvedExpression } from "../../../value/sim-cfn-unresolved-expression.js";
2
3
  /**
3
4
  * Simulated CloudFormation `Fn::Join` intrinsic function.
4
5
  *
@@ -7,6 +8,12 @@ import { SimCfnNode } from "../../sim-cfn-node.js";
7
8
  * {
8
9
  * "Fn::Join": ["-", ["a", "b", { "Ref": "Name" }]]
9
10
  * }
11
+ *
12
+ * The values may also come from one function that returns the whole list:
13
+ *
14
+ * {
15
+ * "Fn::Join": [",", { "Fn::GetAtt": ["Zone", "NameServers"] }]
16
+ * }
10
17
  */
11
18
  export class SimCfnFnJoin extends SimCfnNode {
12
19
  delimiter;
@@ -17,13 +24,13 @@ export class SimCfnFnJoin extends SimCfnNode {
17
24
  this.values = values;
18
25
  }
19
26
  /**
20
- * Resolve each value and join them with the delimiter.
27
+ * Resolve the values and join them with the delimiter.
21
28
  *
22
- * Joining happens only when every value has resolved to a string. If any
23
- * value is still an unresolved expression (for example a Resource `Ref`
24
- * during the up-front pass before Resources exist), this node re-emits
25
- * itself in template form. A later resolution pass can finish it once the
26
- * referenced Resources are available.
29
+ * Joining happens only when the values have resolved to a list of strings.
30
+ * If the list, or any value in it, is still an unresolved expression (for
31
+ * example a Resource `Ref` during the up-front pass before Resources exist),
32
+ * this node re-emits itself in template form. A later resolution pass can
33
+ * finish it once the referenced Resources are available.
27
34
  *
28
35
  * A dynamic reference can be assembled out of the joined values, as CDK
29
36
  * writes one whenever the secret it reads sits in the same Stack: the
@@ -33,37 +40,49 @@ export class SimCfnFnJoin extends SimCfnNode {
33
40
  * out of its variables.
34
41
  */
35
42
  resolve(context) {
36
- const resolved = this.values.map((value) => this.resolveValue(value, context));
37
- if (resolved.every((value) => typeof value === "string")) {
38
- const joined = resolved.join(this.delimiter);
43
+ const values = this.values.resolve(context);
44
+ if (isSimCfnUnresolvedExpression(values)) {
45
+ return { "Fn::Join": [this.delimiter, values] };
46
+ }
47
+ if (!Array.isArray(values)) {
48
+ throw new TypeError(`Sim CloudFormation Fn::Join values must resolve to a list, got ${describe(values)}`);
49
+ }
50
+ for (const value of values) {
51
+ this.assertJoinable(value);
52
+ }
53
+ if (values.every((value) => typeof value === "string")) {
54
+ const joined = values.join(this.delimiter);
39
55
  return context.dynamicReferences?.substitute(joined) ?? joined;
40
56
  }
41
- return { "Fn::Join": [this.delimiter, resolved] };
57
+ return { "Fn::Join": [this.delimiter, values] };
42
58
  }
43
59
  /**
44
- * Collect referenced names from every joined value.
60
+ * Collect referenced names from the values.
45
61
  */
46
62
  referencedNames() {
47
- return this.values.flatMap((value) => value.referencedNames());
48
- }
49
- resolveValue(value, context) {
50
- const resolved = value.resolve(context);
51
- if (typeof resolved === "string") {
52
- return resolved;
53
- }
54
- if (this.isDeferredExpression(resolved)) {
55
- return resolved;
56
- }
57
- throw new TypeError(`Sim CloudFormation Fn::Join values must each resolve to a string, got ${typeof resolved}`);
63
+ return this.values.referencedNames();
58
64
  }
59
65
  /**
60
- * Whether a resolved value is still an unresolved intrinsic expression.
66
+ * Refuse a value that can never become a string.
61
67
  *
62
- * Only object-shaped values (a preserved `Ref` or nested function object)
63
- * are treated as deferred. Other non-string primitives such as numbers are
64
- * genuine type errors.
68
+ * A string joins now, and an unresolved expression may join on a later
69
+ * pass. Anything else (a number, a nested list, a plain object) is a type
70
+ * error, as CloudFormation joins a list of strings only.
65
71
  */
66
- isDeferredExpression(value) {
67
- return typeof value === "object" && value !== null;
72
+ assertJoinable(value) {
73
+ if (typeof value === "string" || isSimCfnUnresolvedExpression(value)) {
74
+ return;
75
+ }
76
+ throw new TypeError(`Sim CloudFormation Fn::Join values must each resolve to a string, got ${describe(value)}`);
77
+ }
78
+ }
79
+ /**
80
+ * Name a resolved value's type for an error message, telling a list and a
81
+ * null apart from an object.
82
+ */
83
+ function describe(value) {
84
+ if (Array.isArray(value)) {
85
+ return "list";
68
86
  }
87
+ return value === null ? "null" : typeof value;
69
88
  }
@@ -10,10 +10,13 @@ import type { SimCfnValueParser } from "../../value/sim-cfn-value-parser.type.js
10
10
  * "Fn::Join": ["-", ["a", { "Ref": "Name" }]]
11
11
  * }
12
12
  *
13
- * The parser validates only the Fn::Join envelope: a string delimiter and an
14
- * array of values. Each joined value is parsed through the recursive value
15
- * parser, because join entries may themselves be literals, Refs, other
16
- * intrinsic functions, arrays, or plain objects.
13
+ * The values argument goes through the recursive value parser. It may be a
14
+ * literal list, whose entries may themselves be literals, Refs or other
15
+ * intrinsic functions, or a function that returns a whole list (such as
16
+ * `Fn::GetAtt` on a hosted zone's `NameServers`, or `Fn::Split`). Whether it
17
+ * resolves to a list of strings is a resolution-time question, so it belongs
18
+ * to the node. A string, number or boolean in its place can never become a
19
+ * list, and is refused here.
17
20
  */
18
21
  export declare class SimCfnFnJoinParser {
19
22
  private readonly valueParser;
@@ -8,10 +8,13 @@ import { SimCfnFnJoin as SimCfnFunctionJoin } from "../../../node/fn/join/sim-cf
8
8
  * "Fn::Join": ["-", ["a", { "Ref": "Name" }]]
9
9
  * }
10
10
  *
11
- * The parser validates only the Fn::Join envelope: a string delimiter and an
12
- * array of values. Each joined value is parsed through the recursive value
13
- * parser, because join entries may themselves be literals, Refs, other
14
- * intrinsic functions, arrays, or plain objects.
11
+ * The values argument goes through the recursive value parser. It may be a
12
+ * literal list, whose entries may themselves be literals, Refs or other
13
+ * intrinsic functions, or a function that returns a whole list (such as
14
+ * `Fn::GetAtt` on a hosted zone's `NameServers`, or `Fn::Split`). Whether it
15
+ * resolves to a list of strings is a resolution-time question, so it belongs
16
+ * to the node. A string, number or boolean in its place can never become a
17
+ * list, and is refused here.
15
18
  */
16
19
  export class SimCfnFnJoinParser {
17
20
  valueParser;
@@ -29,9 +32,10 @@ export class SimCfnFnJoinParser {
29
32
  if (typeof delimiter !== "string") {
30
33
  throw new TypeError("Sim CloudFormation Fn::Join delimiter must be a string");
31
34
  }
32
- if (!Array.isArray(values)) {
33
- throw new TypeError("Sim CloudFormation Fn::Join values must be an array");
35
+ if (typeof values !== "object" || values === null) {
36
+ throw new TypeError("Sim CloudFormation Fn::Join values must be a list or a function " +
37
+ "that returns one");
34
38
  }
35
- return new SimCfnFunctionJoin(delimiter, values.map((item) => this.valueParser.parse(item)));
39
+ return new SimCfnFunctionJoin(delimiter, this.valueParser.parse(values));
36
40
  }
37
41
  }
@@ -42,8 +42,8 @@ export declare class SimSdkLambdaVmModuleProvider implements SimLambdaVmSdkModul
42
42
  private readonly providedModules;
43
43
  constructor(properties: SimSdkLambdaVmModuleProviderProperties);
44
44
  /**
45
- * Provide an intercepted AWS SDK package or HTTP transport module, or
46
- * undefined for other specifiers.
45
+ * Provide an intercepted AWS SDK package or HTTP or HTTP/2 transport
46
+ * module, or undefined for other specifiers.
47
47
  */
48
48
  provideModule(specifier: string): unknown;
49
49
  /**
@@ -1,7 +1,7 @@
1
1
  import { SimSdkModuleClientInterceptor } from "../../../../../../sdk/module/sim-sdk-module-client-interceptor.js";
2
2
  import { SimSdkCommandDispatcher } from "../../../../../../sdk/sim-sdk-command-dispatcher.js";
3
3
  import { SimLambdaAwsApiOutbound } from "../../../outbound/sim-lambda-aws-api-outbound.js";
4
- import { isSimLambdaHttpModuleSpecifier, makeSimLambdaHttpModule, simLambdaHttpModuleScheme, } from "../../../outbound/sim-lambda-http-module.js";
4
+ import { isSimLambdaTransportModuleSpecifier, makeSimLambdaTransportModule, } from "../../../outbound/sim-lambda-transport-module.js";
5
5
  import { isModuleNotFoundError, requireHostModule, } from "../sim-lambda-host-modules.js";
6
6
  import { SimLambdaSdkPackagesNotInstalledError } from "./sim-lambda-sdk-packages-not-installed.error.js";
7
7
  import { awsSdkPackagePrefix, } from "./sim-lambda-vm-sdk-module-provider.js";
@@ -37,11 +37,11 @@ export class SimSdkLambdaVmModuleProvider {
37
37
  this.requireModule = properties.requireModule ?? requireHostModule;
38
38
  }
39
39
  /**
40
- * Provide an intercepted AWS SDK package or HTTP transport module, or
41
- * undefined for other specifiers.
40
+ * Provide an intercepted AWS SDK package or HTTP or HTTP/2 transport
41
+ * module, or undefined for other specifiers.
42
42
  */
43
43
  provideModule(specifier) {
44
- const isTransport = isSimLambdaHttpModuleSpecifier(specifier);
44
+ const isTransport = isSimLambdaTransportModuleSpecifier(specifier);
45
45
  if (!isTransport && !specifier.startsWith(awsSdkPackagePrefix)) {
46
46
  return undefined;
47
47
  }
@@ -51,11 +51,7 @@ export class SimSdkLambdaVmModuleProvider {
51
51
  }
52
52
  const hostExports = this.hostModuleExports(specifier);
53
53
  const module = isTransport
54
- ? makeSimLambdaHttpModule({
55
- hostModule: hostExports,
56
- outbound: this.outboundHttp,
57
- scheme: simLambdaHttpModuleScheme(specifier),
58
- })
54
+ ? makeSimLambdaTransportModule(specifier, hostExports, this.outboundHttp)
59
55
  : this.interceptor.interceptModule(hostExports);
60
56
  this.providedModules.set(specifier, module);
61
57
  return module;
@@ -29,7 +29,7 @@ export interface SimLambdaVmContextProperties {
29
29
  /**
30
30
  * Create the sandbox vm context that sim Lambda function code runs in.
31
31
  *
32
- * The context provides the common globals a Node.js Lambda runtime offers,
32
+ * The context provides the globals a Node.js Lambda runtime offers,
33
33
  * including an AWS-like process.env holding the standard runtime variables
34
34
  * and any variables declared for the function, and the writable standard
35
35
  * streams function code writes its output to. Everything else must be part
@@ -6,11 +6,12 @@ import { simLambdaNoOutputSink, } from "../../logging/sim-lambda-output-sink.js"
6
6
  import { makeSimLambdaOutboundFetch } from "../../outbound/sim-lambda-outbound-fetch.js";
7
7
  import { simLambdaClearInterval, simLambdaClearTimeout, } from "../../invoke/timer/sim-lambda-clear-timer.js";
8
8
  import { simLambdaSetInterval, simLambdaSetTimeout, } from "../../invoke/timer/sim-lambda-process-timers.js";
9
+ import { simLambdaVmHostGlobals } from "./sim-lambda-vm-host-globals.js";
9
10
  import { SimLambdaVmOutputStream } from "./sim-lambda-vm-output-stream.js";
10
11
  /**
11
12
  * Create the sandbox vm context that sim Lambda function code runs in.
12
13
  *
13
- * The context provides the common globals a Node.js Lambda runtime offers,
14
+ * The context provides the globals a Node.js Lambda runtime offers,
14
15
  * including an AWS-like process.env holding the standard runtime variables
15
16
  * and any variables declared for the function, and the writable standard
16
17
  * streams function code writes its output to. Everything else must be part
@@ -33,12 +34,12 @@ export function makeSimLambdaVmContext(properties) {
33
34
  // What function code writes reaches the function's log group through here.
34
35
  stdout.recordTo(sink);
35
36
  stderr.recordTo(sink);
36
- return vm.createContext({
37
+ const context = vm.createContext({
38
+ ...simLambdaVmHostGlobals(),
37
39
  // The sandbox's console is built over those streams, as the real
38
40
  // runtime's is, so everything function code prints goes to one place
39
41
  // whether it printed through the console or wrote to the stream itself.
40
42
  console: new Console({ stdout, stderr }),
41
- Buffer,
42
43
  process: {
43
44
  env: environment.variables(),
44
45
  stdout,
@@ -55,25 +56,13 @@ export function makeSimLambdaVmContext(properties) {
55
56
  clearTimeout: simLambdaClearTimeout,
56
57
  setInterval: simLambdaSetInterval,
57
58
  clearInterval: simLambdaClearInterval,
58
- setImmediate,
59
- queueMicrotask,
60
- TextEncoder,
61
- TextDecoder,
62
- URL,
63
- URLSearchParams,
64
- structuredClone,
65
- crypto,
66
- // The Fetch API the Node.js runtime provides, with the client itself
67
- // routed into the simulation. The rest are the host's own, so the objects
68
- // a handler builds and the ones the simulation answers with are the same
69
- // kind of thing.
59
+ // The Fetch API client, routed into the simulation. The types it works
60
+ // with are the host's own.
70
61
  fetch: makeSimLambdaOutboundFetch(outboundHttp),
71
- Headers,
72
- Request,
73
- Response,
74
- FormData,
75
- Blob,
76
- AbortController,
77
- AbortSignal,
78
62
  });
63
+ // Node.js names its global object `global` as well as `globalThis`, and
64
+ // bundlers emit references to it for older packages. It has to be the
65
+ // sandbox's own global, which only exists once the context does.
66
+ vm.runInContext("globalThis.global = globalThis;", context);
67
+ return context;
79
68
  }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * The host's own value for each global the sandbox passes through.
3
+ */
4
+ export declare function simLambdaVmHostGlobals(): Record<string, unknown>;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * The globals the Node.js runtime provides that the sandbox passes through
3
+ * from the host as they are.
4
+ *
5
+ * Anything here is the host's own object, so what function code builds with
6
+ * one and what the simulation answers with are the same kind of thing. The
7
+ * globals the sandbox replaces (the console, `process`, `Date`, the timers
8
+ * and `fetch`) are built in `makeSimLambdaVmContext` instead.
9
+ */
10
+ const simLambdaVmHostGlobalNames = [
11
+ "Buffer",
12
+ "setImmediate",
13
+ // The host's, since the sandbox's `setImmediate` is the host's too.
14
+ "clearImmediate",
15
+ "queueMicrotask",
16
+ "structuredClone",
17
+ "atob",
18
+ "btoa",
19
+ "TextEncoder",
20
+ "TextDecoder",
21
+ "URL",
22
+ "URLSearchParams",
23
+ "URLPattern",
24
+ "DOMException",
25
+ // The Fetch API types. The client itself is the sandbox's own.
26
+ "Headers",
27
+ "Request",
28
+ "Response",
29
+ "FormData",
30
+ "Blob",
31
+ "File",
32
+ "AbortController",
33
+ "AbortSignal",
34
+ // WHATWG streams, which bundled SDK clients build their event streams on.
35
+ "ReadableStream",
36
+ "ReadableStreamDefaultReader",
37
+ "ReadableStreamBYOBReader",
38
+ "ReadableStreamBYOBRequest",
39
+ "ReadableByteStreamController",
40
+ "ReadableStreamDefaultController",
41
+ "WritableStream",
42
+ "WritableStreamDefaultController",
43
+ "WritableStreamDefaultWriter",
44
+ "TransformStream",
45
+ "TransformStreamDefaultController",
46
+ "ByteLengthQueuingStrategy",
47
+ "CountQueuingStrategy",
48
+ "TextEncoderStream",
49
+ "TextDecoderStream",
50
+ "CompressionStream",
51
+ "DecompressionStream",
52
+ // Events and messaging.
53
+ "Event",
54
+ "EventTarget",
55
+ "CustomEvent",
56
+ "MessageEvent",
57
+ "CloseEvent",
58
+ "MessageChannel",
59
+ "MessagePort",
60
+ "BroadcastChannel",
61
+ "WebSocket",
62
+ // Web Crypto.
63
+ "crypto",
64
+ "Crypto",
65
+ "CryptoKey",
66
+ "SubtleCrypto",
67
+ // Performance reads host time, while `Date` reads the simulated clock.
68
+ "performance",
69
+ "Performance",
70
+ "PerformanceEntry",
71
+ "PerformanceMark",
72
+ "PerformanceMeasure",
73
+ "PerformanceObserver",
74
+ "PerformanceObserverEntryList",
75
+ "PerformanceResourceTiming",
76
+ "navigator",
77
+ "Navigator",
78
+ ];
79
+ /**
80
+ * The host's own value for each global the sandbox passes through.
81
+ */
82
+ export function simLambdaVmHostGlobals() {
83
+ return Object.fromEntries(simLambdaVmHostGlobalNames.map((name) => [
84
+ name,
85
+ Reflect.get(globalThis, name),
86
+ ]));
87
+ }
@@ -12,5 +12,8 @@ import type { SimLambdaOutboundHttp } from "../outbound/sim-lambda-outbound-http
12
12
  * modules export as it was. The built-in modules' named ES module exports are
13
13
  * a snapshot of those properties, so they are re-synchronised afterwards for
14
14
  * the handler that imported `request` by name rather than the module.
15
+ *
16
+ * `node:http2` has `connect` replaced the same way. Bedrock Runtime's SDK
17
+ * client sends over HTTP/2.
15
18
  */
16
19
  export declare function installSimLambdaOutboundClients(outbound: SimLambdaOutboundHttp): void;
@@ -1,7 +1,9 @@
1
1
  import http from "node:http";
2
+ import http2 from "node:http2";
2
3
  import https from "node:https";
3
4
  import { syncBuiltinESMExports } from "node:module";
4
5
  import { makeSimLambdaHttpModule } from "../outbound/sim-lambda-http-module.js";
6
+ import { makeSimLambdaHttp2Module } from "../outbound/sim-lambda-http2-module.js";
5
7
  import { makeSimLambdaOutboundFetch } from "../outbound/sim-lambda-outbound-fetch.js";
6
8
  /**
7
9
  * The transport modules an in-process handler makes requests through, with the
@@ -24,6 +26,9 @@ const transportModules = [
24
26
  * modules export as it was. The built-in modules' named ES module exports are
25
27
  * a snapshot of those properties, so they are re-synchronised afterwards for
26
28
  * the handler that imported `request` by name rather than the module.
29
+ *
30
+ * `node:http2` has `connect` replaced the same way. Bedrock Runtime's SDK
31
+ * client sends over HTTP/2.
27
32
  */
28
33
  export function installSimLambdaOutboundClients(outbound) {
29
34
  const hostFetch = globalThis.fetch;
@@ -38,5 +43,10 @@ export function installSimLambdaOutboundClients(outbound) {
38
43
  patched["request"] = routed["request"];
39
44
  patched["get"] = routed["get"];
40
45
  }
46
+ const http2Module = http2;
47
+ http2Module["connect"] = makeSimLambdaHttp2Module({
48
+ hostModule: http2Module,
49
+ outbound,
50
+ })["connect"];
41
51
  syncBuiltinESMExports();
42
52
  }
@@ -0,0 +1,32 @@
1
+ import { AsyncResource } from "node:async_hooks";
2
+ import type http2 from "node:http2";
3
+ /**
4
+ * A request sent on a simulated session, waiting for its answer.
5
+ */
6
+ export interface SimLambdaHttp2Exchange {
7
+ readonly stream: http2.ClientHttp2Stream;
8
+ /**
9
+ * The asynchronous context the request was sent from. A pooled session
10
+ * outlives the invocation that opened it, and each request on it belongs to
11
+ * whichever invocation sent it.
12
+ */
13
+ readonly scope: AsyncResource;
14
+ }
15
+ /**
16
+ * The requests in flight on one simulated session, each joined to the
17
+ * asynchronous context it was sent from.
18
+ */
19
+ export declare class SimLambdaHttp2Exchanges {
20
+ private readonly exchanges;
21
+ /**
22
+ * Tag each request the session sends with the context it was sent from.
23
+ */
24
+ track(session: http2.ClientHttp2Session): void;
25
+ /**
26
+ * The exchange a request arriving at the session's server belongs to.
27
+ *
28
+ * Every request on the session's client is tagged by `track`, and nothing
29
+ * else can reach its server.
30
+ */
31
+ exchangeFor(request: http2.Http2ServerRequest): SimLambdaHttp2Exchange;
32
+ }
@@ -0,0 +1,39 @@
1
+ import { AsyncResource } from "node:async_hooks";
2
+ import { randomUUID } from "node:crypto";
3
+ import { assertDefined } from "../../../../util/type-guard/defined.js";
4
+ import { simLambdaHttp2ExchangeHeaderName, simLambdaHttp2ExchangeId, } from "./sim-lambda-http2-messages.js";
5
+ /**
6
+ * The requests in flight on one simulated session, each joined to the
7
+ * asynchronous context it was sent from.
8
+ */
9
+ export class SimLambdaHttp2Exchanges {
10
+ exchanges = new Map();
11
+ /**
12
+ * Tag each request the session sends with the context it was sent from.
13
+ */
14
+ track(session) {
15
+ const hostRequest = session.request.bind(session);
16
+ const request = (headers, options) => {
17
+ const exchangeId = randomUUID();
18
+ const stream = hostRequest({ ...headers, [simLambdaHttp2ExchangeHeaderName]: exchangeId }, options);
19
+ this.exchanges.set(exchangeId, {
20
+ stream,
21
+ scope: new AsyncResource("SimLambdaHttp2Request"),
22
+ });
23
+ stream.once("close", () => this.exchanges.delete(exchangeId));
24
+ return stream;
25
+ };
26
+ Object.assign(session, { request });
27
+ }
28
+ /**
29
+ * The exchange a request arriving at the session's server belongs to.
30
+ *
31
+ * Every request on the session's client is tagged by `track`, and nothing
32
+ * else can reach its server.
33
+ */
34
+ exchangeFor(request) {
35
+ const exchange = this.exchanges.get(simLambdaHttp2ExchangeId(request));
36
+ assertDefined(exchange, "sim Lambda HTTP/2 exchange for a request");
37
+ return exchange;
38
+ }
39
+ }
@@ -0,0 +1,33 @@
1
+ import type http2 from "node:http2";
2
+ import type { SimLambdaOutboundHttp } from "./sim-lambda-outbound-http.js";
3
+ /**
4
+ * The header a request sent on a simulated session carries to say which
5
+ * request it is, so the answer runs in the asynchronous context the request
6
+ * was sent from. The simulation never sees it.
7
+ */
8
+ export declare const simLambdaHttp2ExchangeHeaderName = "x-yulin-sim-lambda-exchange";
9
+ /**
10
+ * The headers of a simulated answer that HTTP/2 can carry.
11
+ */
12
+ export declare function simLambdaHttp2ResponseHeaders(headers: Headers): Record<string, string>;
13
+ /**
14
+ * The request a stream carried, as the request the simulation is asked to
15
+ * answer, at the authority the session was connected to.
16
+ *
17
+ * A pooled session outlives the invocation that opened it. A request sent on
18
+ * it once no invocation is running has nothing to answer it, and is refused
19
+ * saying so.
20
+ */
21
+ export declare function simLambdaHttp2Request(authority: URL, outbound: SimLambdaOutboundHttp, request: http2.Http2ServerRequest): Promise<Request>;
22
+ /**
23
+ * The exchange tag a request on a simulated session carries.
24
+ */
25
+ export declare function simLambdaHttp2ExchangeId(request: http2.Http2ServerRequest): string;
26
+ /**
27
+ * The options and listener a `connect` call passed after the authority. The
28
+ * listener comes back as a list of none or one, to pass on as it came.
29
+ */
30
+ export declare function simLambdaHttp2ConnectArguments(callArguments: readonly unknown[]): {
31
+ readonly options: object | undefined;
32
+ readonly listener: unknown[];
33
+ };
@@ -0,0 +1,87 @@
1
+ /**
2
+ * The header a request sent on a simulated session carries to say which
3
+ * request it is, so the answer runs in the asynchronous context the request
4
+ * was sent from. The simulation never sees it.
5
+ */
6
+ export const simLambdaHttp2ExchangeHeaderName = "x-yulin-sim-lambda-exchange";
7
+ /**
8
+ * The response headers HTTP/2 forbids, which a simulated answer may still
9
+ * carry from an HTTP/1.1 shape.
10
+ */
11
+ const connectionHeaderNames = new Set([
12
+ "connection",
13
+ "keep-alive",
14
+ "proxy-connection",
15
+ "transfer-encoding",
16
+ "upgrade",
17
+ ]);
18
+ /**
19
+ * The headers of a request, less the HTTP/2 pseudo-headers and the exchange
20
+ * tag.
21
+ */
22
+ function requestHeaders(headers) {
23
+ return Object.fromEntries(Object.entries(headers).flatMap(([name, value]) => value === undefined ||
24
+ name === simLambdaHttp2ExchangeHeaderName ||
25
+ name.startsWith(":")
26
+ ? []
27
+ : [[name, Array.isArray(value) ? value.join(", ") : value]]));
28
+ }
29
+ /**
30
+ * The headers of a simulated answer that HTTP/2 can carry.
31
+ */
32
+ export function simLambdaHttp2ResponseHeaders(headers) {
33
+ return Object.fromEntries([...headers].filter(([name]) => !connectionHeaderNames.has(name)));
34
+ }
35
+ /**
36
+ * The request a stream carried, as the request the simulation is asked to
37
+ * answer, at the authority the session was connected to.
38
+ *
39
+ * A pooled session outlives the invocation that opened it. A request sent on
40
+ * it once no invocation is running has nothing to answer it, and is refused
41
+ * saying so.
42
+ */
43
+ export async function simLambdaHttp2Request(authority, outbound, request) {
44
+ const { hostname, host, protocol } = authority;
45
+ if (!outbound.serves(hostname)) {
46
+ throw sessionEndedError(hostname);
47
+ }
48
+ const chunks = [];
49
+ for await (const chunk of request) {
50
+ chunks.push(chunk);
51
+ }
52
+ const body = Buffer.concat(chunks);
53
+ return new Request(`${protocol}//${host}${request.url}`, {
54
+ method: request.method,
55
+ headers: requestHeaders(request.headers),
56
+ ...(body.length > 0 && { body }),
57
+ });
58
+ }
59
+ /**
60
+ * The exchange tag a request on a simulated session carries.
61
+ */
62
+ export function simLambdaHttp2ExchangeId(request) {
63
+ // oxlint-disable-next-line security/detect-object-injection -- this module's own fixed header name.
64
+ return String(request.headers[simLambdaHttp2ExchangeHeaderName]);
65
+ }
66
+ /**
67
+ * The options and listener a `connect` call passed after the authority. The
68
+ * listener comes back as a list of none or one, to pass on as it came.
69
+ */
70
+ export function simLambdaHttp2ConnectArguments(callArguments) {
71
+ const [, second, third] = callArguments;
72
+ const listener = typeof second === "function" ? second : third;
73
+ return {
74
+ options: typeof second === "object" ? (second ?? undefined) : undefined,
75
+ listener: listener === undefined ? [] : [listener],
76
+ };
77
+ }
78
+ /**
79
+ * Refuse a request on a session a sim Lambda invocation opened, sent once no
80
+ * invocation is running to answer it.
81
+ */
82
+ function sessionEndedError(hostname) {
83
+ return new Error(`Cannot send a request to ${hostname} on an HTTP/2 session a sim ` +
84
+ "Lambda invocation opened, once no invocation is running. Create " +
85
+ "the SDK client inside the handler, or send this call from a " +
86
+ "handler invocation.");
87
+ }
@@ -0,0 +1,27 @@
1
+ import type { SimLambdaOutboundHttp } from "./sim-lambda-outbound-http.js";
2
+ /**
3
+ * A `node:http2` module, as far as this needs to know.
4
+ */
5
+ type NodeHttp2Module = Record<string, unknown>;
6
+ /**
7
+ * Whether a module specifier names the HTTP/2 module.
8
+ */
9
+ export declare function isSimLambdaHttp2ModuleSpecifier(specifier: string): boolean;
10
+ interface SimLambdaHttp2ModuleProperties {
11
+ readonly hostModule: NodeHttp2Module;
12
+ readonly outbound: SimLambdaOutboundHttp;
13
+ }
14
+ /**
15
+ * Build the `node:http2` module sim Lambda function code connects through.
16
+ *
17
+ * Everything the real module exports is kept, and only `connect` is
18
+ * replaced. A session to a hostname the simulation serves is a real HTTP/2
19
+ * session, connected over an in-memory socket pair to a server in this
20
+ * process that hands each request to the simulation. Every other session is
21
+ * the host module's, connected exactly as it was asked for.
22
+ *
23
+ * Bedrock Runtime's SDK client sends over HTTP/2, so this is where its calls
24
+ * from function code reach simulated Bedrock.
25
+ */
26
+ export declare function makeSimLambdaHttp2Module(properties: SimLambdaHttp2ModuleProperties): NodeHttp2Module;
27
+ export {};