@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
@@ -13,6 +13,10 @@ export default defineConfig({
13
13
  files: ["**/*.cff.js"],
14
14
  extends: [tseslint.configs.disableTypeChecked],
15
15
  languageOptions: {
16
+ // The typescript-eslint rules below read scope information only its own
17
+ // parser produces. Under ESLint's default parser, `no-unused-vars` takes
18
+ // every reference for a type-only one and reports each variable.
19
+ parser: tseslint.parser,
16
20
  parserOptions: {
17
21
  projectService: false,
18
22
  },
@@ -0,0 +1,18 @@
1
+ import type { SimSdkWireRequest, SimSdkWireResponse } from "./sim-sdk-wire.types.js";
2
+ /**
3
+ * The REST-JSON content type, for bodies and for errors.
4
+ */
5
+ export declare const simSdkWireBedrockJsonContentType = "application/json";
6
+ /**
7
+ * How one Bedrock Runtime operation reads its input off a request and writes
8
+ * its output back.
9
+ */
10
+ export interface SimSdkWireBedrockAction {
11
+ readonly commandName: string;
12
+ input(modelId: string, request: SimSdkWireRequest): unknown;
13
+ outputResponse(output: unknown): Promise<SimSdkWireResponse>;
14
+ }
15
+ /**
16
+ * The invocation operations, keyed by the last segment of their path.
17
+ */
18
+ export declare const simSdkWireBedrockActions: ReadonlyMap<string, SimSdkWireBedrockAction>;
@@ -0,0 +1,71 @@
1
+ import { simSdkWireConverseInput, simSdkWireInvokeInput, } from "./sim-sdk-wire-bedrock-input.js";
2
+ import { simSdkWireEventStreamBody, simSdkWireEventStreamContentType, } from "./sim-sdk-wire-event-stream.js";
3
+ import { simSdkWireOutputResponse } from "./sim-sdk-wire-response.js";
4
+ /**
5
+ * The REST-JSON content type, for bodies and for errors.
6
+ */
7
+ export const simSdkWireBedrockJsonContentType = "application/json";
8
+ /**
9
+ * The invocation operations, keyed by the last segment of their path.
10
+ */
11
+ export const simSdkWireBedrockActions = new Map([
12
+ [
13
+ "converse",
14
+ {
15
+ commandName: "ConverseCommand",
16
+ input: simSdkWireConverseInput,
17
+ outputResponse: (output) => Promise.resolve(simSdkWireOutputResponse(output, simSdkWireBedrockJsonContentType)),
18
+ },
19
+ ],
20
+ [
21
+ "converse-stream",
22
+ {
23
+ commandName: "ConverseStreamCommand",
24
+ input: simSdkWireConverseInput,
25
+ outputResponse: async (output) => await eventStreamResponse(output.stream, {}),
26
+ },
27
+ ],
28
+ [
29
+ "invoke",
30
+ {
31
+ commandName: "InvokeModelCommand",
32
+ input: (modelId, request) => simSdkWireInvokeInput(modelId, request, "accept"),
33
+ outputResponse: invokeModelResponse,
34
+ },
35
+ ],
36
+ [
37
+ "invoke-with-response-stream",
38
+ {
39
+ commandName: "InvokeModelWithResponseStreamCommand",
40
+ input: (modelId, request) => simSdkWireInvokeInput(modelId, request, "x-amzn-bedrock-accept"),
41
+ outputResponse: async (output) => {
42
+ const { body, contentType } = output;
43
+ return await eventStreamResponse(body, {
44
+ "x-amzn-bedrock-content-type": contentType,
45
+ });
46
+ },
47
+ },
48
+ ],
49
+ ]);
50
+ /**
51
+ * `InvokeModel` answers with the model's own body as it is, labelled with its
52
+ * content type.
53
+ */
54
+ function invokeModelResponse(output) {
55
+ const { body, contentType } = output;
56
+ return Promise.resolve({
57
+ statusCode: 200,
58
+ headers: { "content-type": contentType },
59
+ body,
60
+ });
61
+ }
62
+ /**
63
+ * The streaming operations answer with an event stream body.
64
+ */
65
+ async function eventStreamResponse(events, headers) {
66
+ return {
67
+ statusCode: 200,
68
+ headers: { "content-type": simSdkWireEventStreamContentType, ...headers },
69
+ body: await simSdkWireEventStreamBody(events),
70
+ };
71
+ }
@@ -0,0 +1,11 @@
1
+ import type { SimSdkWireRequest } from "./sim-sdk-wire.types.js";
2
+ /**
3
+ * `Converse` and `ConverseStream` send their input as the JSON body, less the
4
+ * model id the path carries.
5
+ */
6
+ export declare function simSdkWireConverseInput(modelId: string, request: SimSdkWireRequest): unknown;
7
+ /**
8
+ * The model invocations send the request body as it is, with every other
9
+ * member in a header.
10
+ */
11
+ export declare function simSdkWireInvokeInput(modelId: string, request: SimSdkWireRequest, acceptHeaderName: string): unknown;
@@ -0,0 +1,45 @@
1
+ import { readSimSdkWireInput } from "./sim-sdk-wire-json.js";
2
+ /**
3
+ * The Command input members `InvokeModel` and
4
+ * `InvokeModelWithResponseStream` carry in headers, with the header each one
5
+ * travels in. The two differ only in where they carry `accept`.
6
+ *
7
+ * Every one is read, including the ones the simulation refuses, so that a
8
+ * request naming a guardrail is refused here as it is through an intercepted
9
+ * client.
10
+ */
11
+ const invokeHeaderMembers = [
12
+ ["contentType", "content-type"],
13
+ ["trace", "x-amzn-bedrock-trace"],
14
+ ["guardrailIdentifier", "x-amzn-bedrock-guardrailidentifier"],
15
+ ["guardrailVersion", "x-amzn-bedrock-guardrailversion"],
16
+ ["performanceConfigLatency", "x-amzn-bedrock-performanceconfig-latency"],
17
+ ["serviceTier", "x-amzn-bedrock-service-tier"],
18
+ ["requestMetadata", "x-amzn-bedrock-request-metadata"],
19
+ ];
20
+ /**
21
+ * `Converse` and `ConverseStream` send their input as the JSON body, less the
22
+ * model id the path carries.
23
+ */
24
+ export function simSdkWireConverseInput(modelId, request) {
25
+ return { ...readSimSdkWireInput(request.body), modelId };
26
+ }
27
+ /**
28
+ * The model invocations send the request body as it is, with every other
29
+ * member in a header.
30
+ */
31
+ export function simSdkWireInvokeInput(modelId, request, acceptHeaderName) {
32
+ const members = [
33
+ ...invokeHeaderMembers,
34
+ ["accept", acceptHeaderName],
35
+ ].flatMap(([member, headerName]) => {
36
+ // oxlint-disable-next-line security/detect-object-injection -- this module's own fixed header names.
37
+ const value = request.headers[headerName];
38
+ return value === undefined ? [] : [[member, value]];
39
+ });
40
+ return {
41
+ ...Object.fromEntries(members),
42
+ modelId,
43
+ ...(request.body.byteLength > 0 && { body: request.body }),
44
+ };
45
+ }
@@ -0,0 +1,10 @@
1
+ import type { SimSdkWireCall } from "./sim-sdk-wire-call.js";
2
+ import type { SimSdkWireRequest } from "./sim-sdk-wire.types.js";
3
+ /**
4
+ * Read a Bedrock Runtime model invocation, which names its operation and its
5
+ * model in the path rather than in a header.
6
+ *
7
+ * A request counts as one when it was signed for Bedrock, or, unsigned, sent
8
+ * to a Bedrock Runtime endpoint, and its path is a model invocation.
9
+ */
10
+ export declare function readSimSdkWireBedrockCall(request: SimSdkWireRequest): SimSdkWireCall | undefined;
@@ -0,0 +1,47 @@
1
+ import { simSdkWireBedrockActions, simSdkWireBedrockJsonContentType, } from "./sim-sdk-wire-bedrock-action.js";
2
+ import { readSimSdkWireCredentialScope } from "./sim-sdk-wire-operation.js";
3
+ import { simSdkWireErrorResponse } from "./sim-sdk-wire-response.js";
4
+ /**
5
+ * The SDK `config.serviceId` of a `BedrockRuntimeClient`.
6
+ */
7
+ const bedrockRuntimeServiceId = "Bedrock Runtime";
8
+ /**
9
+ * The SigV4 signing name Bedrock Runtime requests are signed with.
10
+ */
11
+ const bedrockSigningName = "bedrock";
12
+ /**
13
+ * The path of a model invocation: `/model/{modelId}/{action}`, where the
14
+ * model id is URL-encoded and may be a whole ARN.
15
+ */
16
+ const invocationPathPattern = /^\/model\/(?<modelId>[^/]+)\/(?<action>[a-z-]+)$/;
17
+ /**
18
+ * Read a Bedrock Runtime model invocation, which names its operation and its
19
+ * model in the path rather than in a header.
20
+ *
21
+ * A request counts as one when it was signed for Bedrock, or, unsigned, sent
22
+ * to a Bedrock Runtime endpoint, and its path is a model invocation.
23
+ */
24
+ export function readSimSdkWireBedrockCall(request) {
25
+ const signingName = readSimSdkWireCredentialScope(request)?.signingName;
26
+ const isBedrock = signingName === undefined
27
+ ? request.hostname.startsWith("bedrock-runtime.")
28
+ : signingName === bedrockSigningName;
29
+ if (!isBedrock || request.method.toUpperCase() !== "POST") {
30
+ return undefined;
31
+ }
32
+ const groups = invocationPathPattern.exec(request.path.split("?", 1)[0] ?? "")?.groups;
33
+ const action = simSdkWireBedrockActions.get(groups?.["action"] ?? "");
34
+ if (groups === undefined || action === undefined) {
35
+ return undefined;
36
+ }
37
+ // A malformed escape fails decoding, which is the service's answer to the
38
+ // request, so the model id is decoded when the input is read.
39
+ const encodedModelId = groups["modelId"] ?? "";
40
+ return {
41
+ serviceId: bedrockRuntimeServiceId,
42
+ commandName: action.commandName,
43
+ input: () => action.input(decodeURIComponent(encodedModelId), request),
44
+ outputResponse: async (output) => await action.outputResponse(output),
45
+ errorResponse: (error) => simSdkWireErrorResponse(error, simSdkWireBedrockJsonContentType),
46
+ };
47
+ }
@@ -0,0 +1,30 @@
1
+ import type { SimSdkWireRequest, SimSdkWireResponse } from "./sim-sdk-wire.types.js";
2
+ /**
3
+ * One wire request read back into the Command it was serialized from, with
4
+ * the protocol it came in to encode the answer in.
5
+ */
6
+ export interface SimSdkWireCall {
7
+ /** The SDK `config.serviceId` of the service the request is addressed to. */
8
+ readonly serviceId: string;
9
+ /** The SDK Command class name the request would have been sent as. */
10
+ readonly commandName: string;
11
+ /**
12
+ * The Command input. Reading it can fail on a malformed body, and that
13
+ * failure is the service's answer to the request, so it is read when the
14
+ * operation runs.
15
+ */
16
+ input(): unknown;
17
+ /** The response for an output the simulated operation answered with. */
18
+ outputResponse(output: unknown): Promise<SimSdkWireResponse>;
19
+ /** The response for an error the simulated operation refused with. */
20
+ errorResponse(error: unknown): SimSdkWireResponse;
21
+ }
22
+ /**
23
+ * Read a wire request as the Command it was serialized from.
24
+ *
25
+ * Returns undefined for a request no reader here understands. The AWS JSON
26
+ * protocol services all name their operation in one header. Bedrock Runtime
27
+ * speaks REST-JSON, with the operation and model in the path, and has a
28
+ * reader of its own.
29
+ */
30
+ export declare function readSimSdkWireCall(request: SimSdkWireRequest): SimSdkWireCall | undefined;
@@ -0,0 +1,32 @@
1
+ import { readSimSdkWireBedrockCall } from "./sim-sdk-wire-bedrock.js";
2
+ import { readSimSdkWireInput } from "./sim-sdk-wire-json.js";
3
+ import { readSimSdkWireOperation } from "./sim-sdk-wire-operation.js";
4
+ import { simSdkWireErrorResponse, simSdkWireOutputResponse, } from "./sim-sdk-wire-response.js";
5
+ /**
6
+ * Read a wire request as the Command it was serialized from.
7
+ *
8
+ * Returns undefined for a request no reader here understands. The AWS JSON
9
+ * protocol services all name their operation in one header. Bedrock Runtime
10
+ * speaks REST-JSON, with the operation and model in the path, and has a
11
+ * reader of its own.
12
+ */
13
+ export function readSimSdkWireCall(request) {
14
+ return readSimSdkWireJsonCall(request) ?? readSimSdkWireBedrockCall(request);
15
+ }
16
+ /**
17
+ * Read an AWS JSON protocol request, whose body is the Command input and
18
+ * whose response echoes the protocol version it was sent with.
19
+ */
20
+ function readSimSdkWireJsonCall(request) {
21
+ const operation = readSimSdkWireOperation(request);
22
+ if (operation === undefined) {
23
+ return undefined;
24
+ }
25
+ const contentType = request.headers["content-type"];
26
+ return {
27
+ ...operation,
28
+ input: () => readSimSdkWireInput(request.body),
29
+ outputResponse: (output) => Promise.resolve(simSdkWireOutputResponse(output, contentType)),
30
+ errorResponse: (error) => simSdkWireErrorResponse(error, contentType),
31
+ };
32
+ }
@@ -1,8 +1,7 @@
1
1
  import { SimSdkCommandDispatcher } from "../sim-sdk-command-dispatcher.js";
2
2
  import { makeSimSdkWireClient, makeSimSdkWireCommand, } from "./sim-sdk-wire-command.js";
3
- import { readSimSdkWireInput } from "./sim-sdk-wire-json.js";
4
- import { readSimSdkWireCredentialScope, readSimSdkWireOperation, } from "./sim-sdk-wire-operation.js";
5
- import { simSdkWireErrorResponse, simSdkWireOutputResponse, } from "./sim-sdk-wire-response.js";
3
+ import { readSimSdkWireCall } from "./sim-sdk-wire-call.js";
4
+ import { readSimSdkWireCredentialScope } from "./sim-sdk-wire-operation.js";
6
5
  import { simSdkUnbridgedWireRequest } from "./sim-sdk-wire-unbridged.js";
7
6
  /**
8
7
  * Dispatches AWS API requests to simulated AWS service operations.
@@ -44,20 +43,19 @@ export class SimSdkWireDispatcher {
44
43
  * relies on.
45
44
  */
46
45
  async dispatch(request, caller) {
47
- const operation = readSimSdkWireOperation(request);
48
- if (operation === undefined) {
46
+ const call = readSimSdkWireCall(request);
47
+ if (call === undefined) {
49
48
  throw simSdkUnbridgedWireRequest(request);
50
49
  }
51
50
  // The operation runs in the Region it was signed for or, unsigned, the
52
51
  // Region of the code that sent it.
53
52
  const scope = readSimSdkWireCredentialScope(request);
54
- const contentType = request.headers["content-type"];
55
53
  try {
56
- const output = await this.dispatcher.dispatch(makeSimSdkWireCommand(operation.commandName, readSimSdkWireInput(request.body)), makeSimSdkWireClient(operation.serviceId, scope?.regionName ?? this.regionName), undefined, caller);
57
- return simSdkWireOutputResponse(output, contentType);
54
+ const output = await this.dispatcher.dispatch(makeSimSdkWireCommand(call.commandName, call.input()), makeSimSdkWireClient(call.serviceId, scope?.regionName ?? this.regionName), undefined, caller);
55
+ return await call.outputResponse(output);
58
56
  }
59
57
  catch (error) {
60
- return simSdkWireErrorResponse(error, contentType);
58
+ return call.errorResponse(error);
61
59
  }
62
60
  }
63
61
  }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * The media type of an AWS event stream response body.
3
+ */
4
+ export declare const simSdkWireEventStreamContentType = "application/vnd.amazon.eventstream";
5
+ /**
6
+ * Encode an event stream as the `application/vnd.amazon.eventstream` body an
7
+ * SDK reads it from.
8
+ *
9
+ * Each event is a union with one member set, as an SDK hands it to calling
10
+ * code (`{ chunk: { bytes } }` or `{ contentBlockDelta: { ... } }`). The
11
+ * member name is the message's event type, and its value is the JSON payload.
12
+ *
13
+ * The whole stream is read before the body is built. A simulated stream has
14
+ * every event available at once, so sending them together loses nothing.
15
+ */
16
+ export declare function simSdkWireEventStreamBody(events: AsyncIterable<object>): Promise<Uint8Array>;
@@ -0,0 +1,79 @@
1
+ import { crc32 } from "../../util/zip/crc32.js";
2
+ import { simSdkWireJsonBody } from "./sim-sdk-wire-json.js";
3
+ /**
4
+ * The media type of an AWS event stream response body.
5
+ */
6
+ export const simSdkWireEventStreamContentType = "application/vnd.amazon.eventstream";
7
+ /**
8
+ * The header value type code for a string, the only type written here.
9
+ */
10
+ const stringHeaderType = 7;
11
+ /**
12
+ * The bytes of the prelude: the total length, the headers length and the
13
+ * prelude checksum, each four bytes.
14
+ */
15
+ const preludeLength = 12;
16
+ /**
17
+ * The bytes of the checksum closing every message.
18
+ */
19
+ const messageChecksumLength = 4;
20
+ /**
21
+ * Encode an event stream as the `application/vnd.amazon.eventstream` body an
22
+ * SDK reads it from.
23
+ *
24
+ * Each event is a union with one member set, as an SDK hands it to calling
25
+ * code (`{ chunk: { bytes } }` or `{ contentBlockDelta: { ... } }`). The
26
+ * member name is the message's event type, and its value is the JSON payload.
27
+ *
28
+ * The whole stream is read before the body is built. A simulated stream has
29
+ * every event available at once, so sending them together loses nothing.
30
+ */
31
+ export async function simSdkWireEventStreamBody(events) {
32
+ const messages = [];
33
+ for await (const event of events) {
34
+ const members = Object.entries(event).filter(([, value]) => value !== undefined);
35
+ for (const [eventType, value] of members) {
36
+ messages.push(eventMessage(eventType, simSdkWireJsonBody(value)));
37
+ }
38
+ }
39
+ return Buffer.concat(messages);
40
+ }
41
+ /**
42
+ * Encode one event message.
43
+ *
44
+ * The layout is the prelude, the headers, the payload and a CRC-32 of
45
+ * everything before it. The prelude ends with a CRC-32 of its first eight
46
+ * bytes.
47
+ */
48
+ function eventMessage(eventType, payload) {
49
+ const headers = Buffer.concat([
50
+ stringHeader(":event-type", eventType),
51
+ stringHeader(":content-type", "application/json"),
52
+ stringHeader(":message-type", "event"),
53
+ ]);
54
+ const totalLength = preludeLength + headers.length + payload.length + messageChecksumLength;
55
+ const checksumOffset = totalLength - messageChecksumLength;
56
+ const message = Buffer.alloc(totalLength);
57
+ message.writeUInt32BE(totalLength, 0);
58
+ message.writeUInt32BE(headers.length, 4);
59
+ message.writeUInt32BE(crc32(message.subarray(0, 8)), 8);
60
+ headers.copy(message, preludeLength);
61
+ message.set(payload, preludeLength + headers.length);
62
+ message.writeUInt32BE(crc32(message.subarray(0, checksumOffset)), checksumOffset);
63
+ return message;
64
+ }
65
+ /**
66
+ * Encode one string-valued header: the name length and name, the type code,
67
+ * and the value length and value.
68
+ */
69
+ function stringHeader(name, value) {
70
+ const nameBytes = Buffer.from(name);
71
+ const valueBytes = Buffer.from(value);
72
+ const header = Buffer.alloc(4 + nameBytes.length + valueBytes.length);
73
+ header.writeUInt8(nameBytes.length, 0);
74
+ nameBytes.copy(header, 1);
75
+ header.writeUInt8(stringHeaderType, 1 + nameBytes.length);
76
+ header.writeUInt16BE(valueBytes.length, 2 + nameBytes.length);
77
+ valueBytes.copy(header, 4 + nameBytes.length);
78
+ return header;
79
+ }
@@ -16,8 +16,8 @@ import { readSimSdkWireCredentialScope } from "./sim-sdk-wire-operation.js";
16
16
  export function simSdkUnbridgedWireRequest(request) {
17
17
  const service = readSimSdkWireCredentialScope(request)?.signingName ?? request.hostname;
18
18
  return new SimSdkUnbridgedWireRequestError(`Cannot answer a request to ${service} from the simulation: a ` +
19
- "serialized AWS API request can only be routed for a service using " +
20
- "the AWS JSON protocol. Where this is an AWS SDK bundled into " +
19
+ "serialized AWS API request can only be routed for S3, Bedrock " +
20
+ "Runtime and the services using the AWS JSON protocol. Where this is an AWS SDK bundled into " +
21
21
  "function code, leaving the SDK out of the deployment package puts it " +
22
22
  "back on the module interception path, which every simulated service " +
23
23
  "is reachable through.");
@@ -24,9 +24,13 @@ export class SimBedrockAuthorizer {
24
24
  caller: options.caller,
25
25
  });
26
26
  if (decision.isDenied) {
27
- throw new SimBedrockAccessDeniedException(`User: ${this.callerIdentifier.format(decision.caller.principal)} is ` +
28
- `not authorized to perform: ${action} on resource: ${resource} ` +
29
- `because no identity-based policy allows the ${action} action`);
27
+ // A service control policy denial names the policy, as other services'
28
+ // denials do.
29
+ const reason = decision.denialReason ??
30
+ `because no identity-based policy allows the ${action} action`;
31
+ const caller = this.callerIdentifier.format(decision.caller.principal);
32
+ throw new SimBedrockAccessDeniedException(`User: ${caller} is not authorized to perform: ${action} on ` +
33
+ `resource: ${resource} ${reason}`);
30
34
  }
31
35
  return decision.caller;
32
36
  }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Whether a declared Parameter `Type` holds a list.
3
+ *
4
+ * `CommaDelimitedList` and every `List<...>` type do, whatever the member
5
+ * type. A `Ref` to one resolves to a list of strings, `List<Number>` included.
6
+ */
7
+ export declare function isSimCfnParameterListType(type: string | undefined): boolean;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Whether a declared Parameter `Type` holds a list.
3
+ *
4
+ * `CommaDelimitedList` and every `List<...>` type do, whatever the member
5
+ * type. A `Ref` to one resolves to a list of strings, `List<Number>` included.
6
+ */
7
+ export function isSimCfnParameterListType(type) {
8
+ return type === "CommaDelimitedList" || (type?.startsWith("List<") ?? false);
9
+ }
@@ -13,6 +13,9 @@ interface SimCfnParameterValuesProperties {
13
13
  * the command wins over a template `Default`, and a Parameter declared as a
14
14
  * Parameter Store value type reads whichever of the two it ended up with as the
15
15
  * name to read.
16
+ *
17
+ * A `CommaDelimitedList` or `List<...>` Parameter is given its value as one
18
+ * comma-separated string, and holds it as the list of members.
16
19
  */
17
20
  export declare class SimCfnParameterValues {
18
21
  private readonly values;
@@ -29,5 +32,12 @@ export declare class SimCfnParameterValues {
29
32
  * `Default`.
30
33
  */
31
34
  private recordDefaults;
35
+ /**
36
+ * Split the value of each list-typed Parameter on its commas.
37
+ *
38
+ * Each member is trimmed of the spaces around it, as CloudFormation trims
39
+ * them. A value with no commas is a list of one.
40
+ */
41
+ private splitLists;
32
42
  }
33
43
  export {};
@@ -1,3 +1,4 @@
1
+ import { isSimCfnParameterListType } from "./sim-cfn-parameter-list-type.js";
1
2
  import { SimCfnParameterStoreValues } from "./store/sim-cfn-parameter-store-values.js";
2
3
  /**
3
4
  * The runtime value of every Parameter a Stack was created with.
@@ -6,6 +7,9 @@ import { SimCfnParameterStoreValues } from "./store/sim-cfn-parameter-store-valu
6
7
  * the command wins over a template `Default`, and a Parameter declared as a
7
8
  * Parameter Store value type reads whichever of the two it ended up with as the
8
9
  * name to read.
10
+ *
11
+ * A `CommaDelimitedList` or `List<...>` Parameter is given its value as one
12
+ * comma-separated string, and holds it as the list of members.
9
13
  */
10
14
  export class SimCfnParameterValues {
11
15
  values;
@@ -15,6 +19,7 @@ export class SimCfnParameterValues {
15
19
  this.storeValues = new SimCfnParameterStoreValues({ store });
16
20
  this.values = new Map(Object.entries(supplied));
17
21
  this.recordDefaults(definitions);
22
+ this.splitLists(definitions);
18
23
  this.storeValues.applyTo(this.values, definitions);
19
24
  }
20
25
  /** Every Parameter that resolved to a stand-in value. */
@@ -39,4 +44,19 @@ export class SimCfnParameterValues {
39
44
  }
40
45
  }
41
46
  }
47
+ /**
48
+ * Split the value of each list-typed Parameter on its commas.
49
+ *
50
+ * Each member is trimmed of the spaces around it, as CloudFormation trims
51
+ * them. A value with no commas is a list of one.
52
+ */
53
+ splitLists(definitions) {
54
+ for (const [parameterName, definition] of definitions) {
55
+ const value = this.values.get(parameterName);
56
+ if (typeof value === "string" &&
57
+ isSimCfnParameterListType(definition.Type)) {
58
+ this.values.set(parameterName, value.split(",").map((member) => member.trim()));
59
+ }
60
+ }
61
+ }
42
62
  }
@@ -5,9 +5,10 @@ import type { SimCfnParameterStoreReader } from "./store/sim-cfn-parameter-store
5
5
  * defaults and Parameter Store reads have been applied.
6
6
  *
7
7
  * A Parameter is given its value as a string, because AWS CloudFormation
8
- * command inputs provide ParameterValue as one. A Parameter declared as
9
- * `AWS::SSM::Parameter::Value<List<String>>` is given a parameter name and
10
- * resolves to the stored value split into a list, so a resolved value can be
8
+ * command inputs provide ParameterValue as one. A `CommaDelimitedList` or
9
+ * `List<...>` Parameter resolves to that string split into a list. So does an
10
+ * `AWS::SSM::Parameter::Value<List<String>>` Parameter, which is given a
11
+ * parameter name and splits the value stored under it. A resolved value can be
11
12
  * either.
12
13
  */
13
14
  export type SimCloudFormationParameterValue = string | string[];
@@ -61,8 +62,9 @@ export type SimCfnParametersContext = Pick<SimCfnParametersProperties, "definiti
61
62
  *
62
63
  * Most fields are recorded for structural typing/documentation. Runtime
63
64
  * behavior applies string Default values when a command does not provide an
64
- * explicit Parameter value, and reads an `AWS::SSM::Parameter::Value<...>`
65
- * Type from simulated Parameter Store.
65
+ * explicit Parameter value, splits the value of a `CommaDelimitedList` or
66
+ * `List<...>` Type, and reads an `AWS::SSM::Parameter::Value<...>` Type from
67
+ * simulated Parameter Store.
66
68
  */
67
69
  export interface SimCfnParameterDefinition {
68
70
  readonly Type?: string | undefined;
@@ -1,3 +1,4 @@
1
+ import { isSimCfnParameterListType } from "../sim-cfn-parameter-list-type.js";
1
2
  /** The Parameter type that holds a Parameter Store name. */
2
3
  const parameterStoreValuePrefix = "AWS::SSM::Parameter::Value<";
3
4
  /**
@@ -14,6 +15,6 @@ export function simCfnParameterStoreValueType(declaredType) {
14
15
  }
15
16
  const innerType = declaredType.slice(parameterStoreValuePrefix.length, -1);
16
17
  return {
17
- list: innerType === "CommaDelimitedList" || innerType.startsWith("List<"),
18
+ list: isSimCfnParameterListType(innerType),
18
19
  };
19
20
  }
@@ -9,19 +9,25 @@ import type { SimCfnResolveContext } from "../../../resolve/sim-cfn-resolve-cont
9
9
  * {
10
10
  * "Fn::Join": ["-", ["a", "b", { "Ref": "Name" }]]
11
11
  * }
12
+ *
13
+ * The values may also come from one function that returns the whole list:
14
+ *
15
+ * {
16
+ * "Fn::Join": [",", { "Fn::GetAtt": ["Zone", "NameServers"] }]
17
+ * }
12
18
  */
13
19
  export declare class SimCfnFnJoin extends SimCfnNode {
14
20
  private readonly delimiter;
15
21
  private readonly values;
16
- constructor(delimiter: string, values: readonly SimCfnNode[]);
22
+ constructor(delimiter: string, values: SimCfnNode);
17
23
  /**
18
- * Resolve each value and join them with the delimiter.
24
+ * Resolve the values and join them with the delimiter.
19
25
  *
20
- * Joining happens only when every value has resolved to a string. If any
21
- * value is still an unresolved expression (for example a Resource `Ref`
22
- * during the up-front pass before Resources exist), this node re-emits
23
- * itself in template form. A later resolution pass can finish it once the
24
- * referenced Resources are available.
26
+ * Joining happens only when the values have resolved to a list of strings.
27
+ * If the list, or any value in it, is still an unresolved expression (for
28
+ * example a Resource `Ref` during the up-front pass before Resources exist),
29
+ * this node re-emits itself in template form. A later resolution pass can
30
+ * finish it once the referenced Resources are available.
25
31
  *
26
32
  * A dynamic reference can be assembled out of the joined values, as CDK
27
33
  * writes one whenever the secret it reads sits in the same Stack: the
@@ -32,16 +38,15 @@ export declare class SimCfnFnJoin extends SimCfnNode {
32
38
  */
33
39
  resolve(context: SimCfnResolveContext): SimCfnTemplateValue;
34
40
  /**
35
- * Collect referenced names from every joined value.
41
+ * Collect referenced names from the values.
36
42
  */
37
43
  referencedNames(): string[];
38
- private resolveValue;
39
44
  /**
40
- * Whether a resolved value is still an unresolved intrinsic expression.
45
+ * Refuse a value that can never become a string.
41
46
  *
42
- * Only object-shaped values (a preserved `Ref` or nested function object)
43
- * are treated as deferred. Other non-string primitives such as numbers are
44
- * genuine type errors.
47
+ * A string joins now, and an unresolved expression may join on a later
48
+ * pass. Anything else (a number, a nested list, a plain object) is a type
49
+ * error, as CloudFormation joins a list of strings only.
45
50
  */
46
- private isDeferredExpression;
51
+ private assertJoinable;
47
52
  }