@kensio/yulin 1.20.14 → 1.20.15
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/config/oxlint/cffjs2.oxlintrc.json → cffjs2.oxlintrc.json} +1 -1
- package/dist/config/oxlint/cffjs2.oxlint.config.js +4 -3
- package/dist/service/cloudformation/resource/resolve/property/sim-cfn-resource-property-resolver.d.ts +11 -4
- package/dist/service/cloudformation/resource/resolve/property/sim-cfn-resource-property-resolver.js +21 -8
- package/dist/service/cloudformation/resource/sim-cfn-resource.type.d.ts +9 -0
- package/dist/service/cloudformation/template/dynamic/make-sim-cfn-dynamic-references.d.ts +2 -0
- package/dist/service/cloudformation/template/dynamic/make-sim-cfn-dynamic-references.js +2 -2
- package/dist/service/cloudformation/template/dynamic/sim-cfn-dynamic-reference-resolvers.d.ts +10 -0
- package/dist/service/cloudformation/template/dynamic/sim-cfn-dynamic-reference-resolvers.js +4 -3
- package/dist/service/cloudformation/template/dynamic/sim-cfn-dynamic-reference-scan.d.ts +11 -0
- package/dist/service/cloudformation/template/dynamic/sim-cfn-dynamic-reference-scan.js +21 -0
- package/dist/service/cloudformation/template/dynamic/sim-cfn-dynamic-references.d.ts +24 -2
- package/dist/service/cloudformation/template/dynamic/sim-cfn-dynamic-references.js +50 -11
- package/dist/service/cloudformation/template/dynamic/sim-cfn-prefetched-dynamic-references.d.ts +42 -0
- package/dist/service/cloudformation/template/dynamic/sim-cfn-prefetched-dynamic-references.js +80 -0
- package/dist/service/eventbridge/delivery/sim-event-bridge-delivery-failures.d.ts +16 -0
- package/dist/service/eventbridge/delivery/sim-event-bridge-delivery-failures.js +19 -0
- package/dist/service/iam/command/policy/create-policy/create-policy-input-resolver.js +4 -1
- package/dist/service/iam/command/policy/put-role-policy/put-role-policy.handler.js +5 -1
- package/dist/service/iam/command/policy/put-user-policy/put-user-policy.handler.js +5 -1
- package/dist/service/iam/policy/parse/sim-iam-document-parser.d.ts +4 -1
- package/dist/service/iam/policy/parse/sim-iam-document-parser.js +13 -15
- package/dist/service/iam/policy/sim-iam-statement-label.d.ts +28 -0
- package/dist/service/iam/policy/sim-iam-statement-label.js +17 -0
- package/dist/service/iam/policy/sim-iam-statement-strings.d.ts +11 -0
- package/dist/service/iam/policy/sim-iam-statement-strings.js +27 -0
- package/dist/service/iam/validate/sim-iam-policy-document-validator.d.ts +9 -2
- package/dist/service/iam/validate/sim-iam-policy-document-validator.js +37 -19
- package/dist/service/lambda/function/code/sim-lambda-vm-zip-function.factory.d.ts +7 -0
- package/dist/service/lambda/function/code/sim-lambda-vm-zip-function.factory.js +1 -0
- package/dist/service/lambda/function/code/vm/sdk/sim-lambda-provided-sdk-module.d.ts +19 -0
- package/dist/service/lambda/function/code/vm/sdk/sim-lambda-provided-sdk-module.js +78 -0
- package/dist/service/lambda/function/code/vm/sdk/sim-lambda-sdk-packages-not-installed.error.d.ts +15 -0
- package/dist/service/lambda/function/code/vm/sdk/sim-lambda-sdk-packages-not-installed.error.js +27 -0
- package/dist/service/lambda/function/code/vm/sdk/sim-lambda-vm-sdk-module-provider.d.ts +9 -0
- package/dist/service/lambda/function/code/vm/sdk/sim-sdk-lambda-vm-module-provider.d.ts +10 -0
- package/dist/service/lambda/function/code/vm/sdk/sim-sdk-lambda-vm-module-provider.js +28 -34
- package/dist/service/lambda/function/code/vm/sim-lambda-host-modules.d.ts +15 -0
- package/dist/service/lambda/function/code/vm/sim-lambda-host-modules.js +31 -0
- package/dist/service/lambda/function/code/vm/sim-lambda-vm-module-resolver.d.ts +7 -0
- package/dist/service/lambda/function/code/vm/sim-lambda-vm-module-resolver.js +15 -0
- package/dist/service/lambda/function/code/vm/sim-lambda-vm-modules.js +5 -4
- package/dist/service/s3/command/put-bucket-policy/put-bucket-policy.handler.js +4 -1
- package/dist/service/scheduler/delivery/sim-scheduler-delivery-failures.d.ts +16 -0
- package/dist/service/scheduler/delivery/sim-scheduler-delivery-failures.js +19 -0
- package/dist/service/scheduler/index.d.ts +1 -1
- package/dist/service/scheduler/index.js +1 -1
- package/dist/service/secretsmanager/cfn/dynamic/sim-cfn-secrets-manager-dynamic-reference-resolver.d.ts +13 -15
- package/dist/service/secretsmanager/cfn/dynamic/sim-cfn-secrets-manager-dynamic-reference-resolver.js +22 -43
- package/dist/service/secretsmanager/cfn/dynamic/sim-cfn-secrets-manager-reference-stand-in.d.ts +9 -0
- package/dist/service/secretsmanager/cfn/dynamic/sim-cfn-secrets-manager-reference-stand-in.js +14 -0
- package/dist/service/secretsmanager/cfn/dynamic/sim-cfn-secrets-manager-reference-value.d.ts +12 -0
- package/dist/service/secretsmanager/cfn/dynamic/sim-cfn-secrets-manager-reference-value.js +29 -0
- package/dist/service/ssm/cfn/dynamic/sim-cfn-ssm-dynamic-reference-resolver.d.ts +22 -6
- package/dist/service/ssm/cfn/dynamic/sim-cfn-ssm-dynamic-reference-resolver.js +39 -24
- package/dist/service/ssm/cfn/dynamic/sim-cfn-ssm-secure-dynamic-reference-resolver.d.ts +10 -5
- package/dist/service/ssm/cfn/dynamic/sim-cfn-ssm-secure-dynamic-reference-resolver.js +10 -8
- package/dist/service/ssm/sim-ssm.d.ts +10 -2
- package/dist/service/ssm/sim-ssm.js +12 -4
- package/package.json +4 -3
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { simIamPolicyDocumentStatements } from "../sim-iam-pol-document-statements.js";
|
|
2
|
+
import { simIamStatementLabel } from "../sim-iam-statement-label.js";
|
|
3
|
+
import { simIamStatementStrings } from "../sim-iam-statement-strings.js";
|
|
2
4
|
/**
|
|
3
5
|
* Parse raw IAM policy document shapes into a form convenient for evaluation.
|
|
4
6
|
*
|
|
5
7
|
* IAM policy JSON commonly allows either a single value or an array. The decision
|
|
6
8
|
* engine should not need to care which source shape was used.
|
|
9
|
+
*
|
|
10
|
+
* A document stored without validation can hold a value of the wrong type in
|
|
11
|
+
* one of those fields. Parsing it reports a malformed policy naming the
|
|
12
|
+
* statement.
|
|
7
13
|
*/
|
|
8
14
|
export class SimIamPolicyDocumentParser {
|
|
9
15
|
/**
|
|
@@ -11,30 +17,22 @@ export class SimIamPolicyDocumentParser {
|
|
|
11
17
|
*/
|
|
12
18
|
parse(policy) {
|
|
13
19
|
return {
|
|
14
|
-
statements: simIamPolicyDocumentStatements(policy).map((statement) => this.parseStatement(statement)),
|
|
20
|
+
statements: simIamPolicyDocumentStatements(policy).map((statement, index) => this.parseStatement(statement, index)),
|
|
15
21
|
};
|
|
16
22
|
}
|
|
17
|
-
parseStatement(statement) {
|
|
23
|
+
parseStatement(statement, index) {
|
|
24
|
+
const label = simIamStatementLabel(index);
|
|
18
25
|
return {
|
|
19
26
|
source: statement,
|
|
20
27
|
sid: statement.Sid,
|
|
21
28
|
effect: statement.Effect,
|
|
22
|
-
actions:
|
|
23
|
-
notActions:
|
|
24
|
-
resources:
|
|
25
|
-
notResources:
|
|
29
|
+
actions: simIamStatementStrings(statement.Action, label, "Action"),
|
|
30
|
+
notActions: simIamStatementStrings(statement.NotAction, label, "NotAction"),
|
|
31
|
+
resources: simIamStatementStrings(statement.Resource, label, "Resource"),
|
|
32
|
+
notResources: simIamStatementStrings(statement.NotResource, label, "NotResource"),
|
|
26
33
|
principal: statement.Principal,
|
|
27
34
|
notPrincipal: statement.NotPrincipal,
|
|
28
35
|
condition: statement.Condition,
|
|
29
36
|
};
|
|
30
37
|
}
|
|
31
|
-
stringList(value) {
|
|
32
|
-
if (value === undefined) {
|
|
33
|
-
return undefined;
|
|
34
|
-
}
|
|
35
|
-
if (typeof value === "string") {
|
|
36
|
-
return [value];
|
|
37
|
-
}
|
|
38
|
-
return [...value];
|
|
39
|
-
}
|
|
40
38
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a policy document is attached to, for naming it when one of its
|
|
3
|
+
* statements is rejected.
|
|
4
|
+
*/
|
|
5
|
+
export interface SimIamPolicyDocumentSubject {
|
|
6
|
+
/**
|
|
7
|
+
* The kind of thing the document is attached to, such as `Role` or `Bucket`.
|
|
8
|
+
*/
|
|
9
|
+
readonly attachedTo: string;
|
|
10
|
+
/**
|
|
11
|
+
* The name of the thing the document is attached to.
|
|
12
|
+
*/
|
|
13
|
+
readonly name: string;
|
|
14
|
+
/**
|
|
15
|
+
* The policy's own name, for a document stored under one.
|
|
16
|
+
*/
|
|
17
|
+
readonly policyName?: string | undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Name one statement of a policy document, for a message about what is wrong
|
|
21
|
+
* with it.
|
|
22
|
+
*
|
|
23
|
+
* A malformed statement usually arrives from a CloudFormation template. The
|
|
24
|
+
* reader chasing one back needs the document it came from as well as the fault
|
|
25
|
+
* in it. Statements carry no names of their own. The position in the document
|
|
26
|
+
* stands in for one.
|
|
27
|
+
*/
|
|
28
|
+
export declare function simIamStatementLabel(index: number, subject?: SimIamPolicyDocumentSubject): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Name one statement of a policy document, for a message about what is wrong
|
|
3
|
+
* with it.
|
|
4
|
+
*
|
|
5
|
+
* A malformed statement usually arrives from a CloudFormation template. The
|
|
6
|
+
* reader chasing one back needs the document it came from as well as the fault
|
|
7
|
+
* in it. Statements carry no names of their own. The position in the document
|
|
8
|
+
* stands in for one.
|
|
9
|
+
*/
|
|
10
|
+
export function simIamStatementLabel(index, subject) {
|
|
11
|
+
const statement = `statement ${index + 1}`;
|
|
12
|
+
if (subject === undefined) {
|
|
13
|
+
return `IAM policy ${statement}`;
|
|
14
|
+
}
|
|
15
|
+
const policy = subject.policyName === undefined ? "" : ` policy "${subject.policyName}"`;
|
|
16
|
+
return `${subject.attachedTo} "${subject.name}"${policy} ${statement}`;
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a statement field holding either one string or a list of strings.
|
|
3
|
+
*
|
|
4
|
+
* Action, NotAction, Resource and NotResource each take either shape, and
|
|
5
|
+
* anything else is a malformed document. An unresolved CloudFormation intrinsic
|
|
6
|
+
* is the usual way one arrives. The IAM policy parsers store `PolicyDocument`
|
|
7
|
+
* whole, and a `Ref` or `Fn::GetAtt` nested inside a statement is kept as
|
|
8
|
+
* written. Refusing it here keeps it out of evaluation, where the same value
|
|
9
|
+
* surfaces as a `TypeError` naming no policy.
|
|
10
|
+
*/
|
|
11
|
+
export declare function simIamStatementStrings(value: unknown, label: string, field: string): readonly string[] | undefined;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SimIamMalformedPolicyDocument } from "../error/sim-iam.error.js";
|
|
2
|
+
/**
|
|
3
|
+
* Read a statement field holding either one string or a list of strings.
|
|
4
|
+
*
|
|
5
|
+
* Action, NotAction, Resource and NotResource each take either shape, and
|
|
6
|
+
* anything else is a malformed document. An unresolved CloudFormation intrinsic
|
|
7
|
+
* is the usual way one arrives. The IAM policy parsers store `PolicyDocument`
|
|
8
|
+
* whole, and a `Ref` or `Fn::GetAtt` nested inside a statement is kept as
|
|
9
|
+
* written. Refusing it here keeps it out of evaluation, where the same value
|
|
10
|
+
* surfaces as a `TypeError` naming no policy.
|
|
11
|
+
*/
|
|
12
|
+
export function simIamStatementStrings(value, label, field) {
|
|
13
|
+
if (value === undefined) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
if (typeof value === "string") {
|
|
17
|
+
return [value];
|
|
18
|
+
}
|
|
19
|
+
if (isStringArray(value)) {
|
|
20
|
+
return [...value];
|
|
21
|
+
}
|
|
22
|
+
throw new SimIamMalformedPolicyDocument(`${label}: ${field} must be a string or an array of strings, but holds ${JSON.stringify(value)}`);
|
|
23
|
+
}
|
|
24
|
+
function isStringArray(value) {
|
|
25
|
+
return (Array.isArray(value) &&
|
|
26
|
+
value.every((entry) => typeof entry === "string"));
|
|
27
|
+
}
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import { type JSONString } from "../../../util/type-guard/json.js";
|
|
2
2
|
import type { SimIamPolicyDocument } from "../policy/sim-iam-policy.js";
|
|
3
|
+
import { type SimIamPolicyDocumentSubject } from "../policy/sim-iam-statement-label.js";
|
|
3
4
|
/**
|
|
4
5
|
* Performs basic IAM policy document validation shared by IAM policy commands.
|
|
6
|
+
*
|
|
7
|
+
* A caller can say what the document is attached to, and a rejection names it.
|
|
8
|
+
* Malformed documents usually arrive from a CloudFormation template, and the
|
|
9
|
+
* failure has to carry enough for a reader to find the template that wrote
|
|
10
|
+
* it.
|
|
5
11
|
*/
|
|
6
12
|
export declare class SimIamPolicyDocumentValidator {
|
|
7
13
|
/**
|
|
8
14
|
* Validate a required JSON IAM policy document string.
|
|
9
15
|
*/
|
|
10
|
-
validateRequired(policyDocument: string | undefined): asserts policyDocument is JSONString<SimIamPolicyDocument>;
|
|
16
|
+
validateRequired(policyDocument: string | undefined, subject?: SimIamPolicyDocumentSubject): asserts policyDocument is JSONString<SimIamPolicyDocument>;
|
|
11
17
|
/**
|
|
12
18
|
* Validate an optional JSON IAM policy document string when present.
|
|
13
19
|
*/
|
|
14
|
-
validateOptional(policyDocument: string | undefined): asserts policyDocument is JSONString<SimIamPolicyDocument> | undefined;
|
|
20
|
+
validateOptional(policyDocument: string | undefined, subject?: SimIamPolicyDocumentSubject): asserts policyDocument is JSONString<SimIamPolicyDocument> | undefined;
|
|
15
21
|
private validate;
|
|
16
22
|
private validateParsed;
|
|
23
|
+
private validateStatement;
|
|
17
24
|
}
|
|
@@ -1,45 +1,63 @@
|
|
|
1
1
|
import { jsonParse } from "../../../util/type-guard/json.js";
|
|
2
2
|
import { SimIamMalformedPolicyDocument } from "../error/sim-iam.error.js";
|
|
3
3
|
import { simIamPolicyDocumentStatements } from "../policy/sim-iam-pol-document-statements.js";
|
|
4
|
+
import { simIamStatementLabel, } from "../policy/sim-iam-statement-label.js";
|
|
5
|
+
import { simIamStatementStrings } from "../policy/sim-iam-statement-strings.js";
|
|
4
6
|
/**
|
|
5
7
|
* Performs basic IAM policy document validation shared by IAM policy commands.
|
|
8
|
+
*
|
|
9
|
+
* A caller can say what the document is attached to, and a rejection names it.
|
|
10
|
+
* Malformed documents usually arrive from a CloudFormation template, and the
|
|
11
|
+
* failure has to carry enough for a reader to find the template that wrote
|
|
12
|
+
* it.
|
|
6
13
|
*/
|
|
7
14
|
export class SimIamPolicyDocumentValidator {
|
|
8
15
|
/**
|
|
9
16
|
* Validate a required JSON IAM policy document string.
|
|
10
17
|
*/
|
|
11
|
-
validateRequired(policyDocument) {
|
|
18
|
+
validateRequired(policyDocument, subject) {
|
|
12
19
|
if (policyDocument === undefined || policyDocument.length === 0) {
|
|
13
20
|
throw new Error("PolicyDocument is required");
|
|
14
21
|
}
|
|
15
|
-
this.validate(policyDocument);
|
|
22
|
+
this.validate(policyDocument, subject);
|
|
16
23
|
}
|
|
17
24
|
/**
|
|
18
25
|
* Validate an optional JSON IAM policy document string when present.
|
|
19
26
|
*/
|
|
20
|
-
validateOptional(policyDocument) {
|
|
27
|
+
validateOptional(policyDocument, subject) {
|
|
21
28
|
if (policyDocument === undefined || policyDocument.length === 0) {
|
|
22
29
|
return;
|
|
23
30
|
}
|
|
24
|
-
this.validate(policyDocument);
|
|
31
|
+
this.validate(policyDocument, subject);
|
|
25
32
|
}
|
|
26
|
-
validate(policyDocument) {
|
|
33
|
+
validate(policyDocument, subject) {
|
|
27
34
|
const parsedPolicyDocument = jsonParse(policyDocument);
|
|
28
|
-
this.validateParsed(parsedPolicyDocument);
|
|
35
|
+
this.validateParsed(parsedPolicyDocument, subject);
|
|
29
36
|
}
|
|
30
|
-
validateParsed(policyDocument) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
throw new SimIamMalformedPolicyDocument('IAM policy statement Effect must be either "Allow" or "Deny"');
|
|
35
|
-
}
|
|
36
|
-
if (statement.Action === undefined && statement.NotAction === undefined) {
|
|
37
|
-
throw new SimIamMalformedPolicyDocument("IAM policy statement must define either Action or NotAction");
|
|
38
|
-
}
|
|
39
|
-
if (statement.Resource === undefined &&
|
|
40
|
-
statement.NotResource === undefined) {
|
|
41
|
-
throw new SimIamMalformedPolicyDocument("IAM policy statement must define either Resource or NotResource");
|
|
42
|
-
}
|
|
37
|
+
validateParsed(policyDocument, subject) {
|
|
38
|
+
const statements = simIamPolicyDocumentStatements(policyDocument);
|
|
39
|
+
for (const [index, statement] of statements.entries()) {
|
|
40
|
+
this.validateStatement(statement, simIamStatementLabel(index, subject));
|
|
43
41
|
}
|
|
44
42
|
}
|
|
43
|
+
validateStatement(statement, label) {
|
|
44
|
+
// oxlint-disable-next-line typescript/no-unnecessary-condition
|
|
45
|
+
if (statement.Effect !== "Allow" && statement.Effect !== "Deny") {
|
|
46
|
+
throw new SimIamMalformedPolicyDocument(`${label}: Effect must be either "Allow" or "Deny"`);
|
|
47
|
+
}
|
|
48
|
+
if (statement.Action === undefined && statement.NotAction === undefined) {
|
|
49
|
+
throw new SimIamMalformedPolicyDocument(`${label}: must define either Action or NotAction`);
|
|
50
|
+
}
|
|
51
|
+
if (statement.Resource === undefined &&
|
|
52
|
+
statement.NotResource === undefined) {
|
|
53
|
+
throw new SimIamMalformedPolicyDocument(`${label}: must define either Resource or NotResource`);
|
|
54
|
+
}
|
|
55
|
+
// Read for the types alone. A field of the wrong type throws while the
|
|
56
|
+
// document is still beside the call that wrote it. Left to the first
|
|
57
|
+
// authorization, the same fault names no policy at all.
|
|
58
|
+
simIamStatementStrings(statement.Action, label, "Action");
|
|
59
|
+
simIamStatementStrings(statement.NotAction, label, "NotAction");
|
|
60
|
+
simIamStatementStrings(statement.Resource, label, "Resource");
|
|
61
|
+
simIamStatementStrings(statement.NotResource, label, "NotResource");
|
|
62
|
+
}
|
|
45
63
|
}
|
|
@@ -2,6 +2,7 @@ import { MappedFactory } from "@kensio/part-factory";
|
|
|
2
2
|
import type { SimAwsAccountRegionScope } from "../../../aws/sim-aws-account-region-scope.js";
|
|
3
3
|
import { SimLambdaFunction } from "../sim-lambda-function.js";
|
|
4
4
|
import { type LambdaCodeZipFiles } from "./make-lambda-code-zip.js";
|
|
5
|
+
import type { SimLambdaVmSdkModuleProvider } from "./vm/sdk/sim-lambda-vm-sdk-module-provider.js";
|
|
5
6
|
/**
|
|
6
7
|
* What a test asks for when it wants a function backed by zip code running in
|
|
7
8
|
* a vm sandbox.
|
|
@@ -16,6 +17,12 @@ export interface SimLambdaVmZipFunctionInput {
|
|
|
16
17
|
readonly code: string | LambdaCodeZipFiles;
|
|
17
18
|
readonly handlerName: string;
|
|
18
19
|
readonly memorySizeMb: number;
|
|
20
|
+
/**
|
|
21
|
+
* What provides the modules the archive does not bundle, as the real
|
|
22
|
+
* runtime provides the AWS SDK. Left out, the code has none, and an SDK
|
|
23
|
+
* require is refused for want of one.
|
|
24
|
+
*/
|
|
25
|
+
readonly sdkModuleProvider?: SimLambdaVmSdkModuleProvider | undefined;
|
|
19
26
|
}
|
|
20
27
|
/**
|
|
21
28
|
* Builds a simulated Lambda function whose code is a real zip archive run in a
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { SimZipArchive } from "../../../../../../util/zip/zip-archive.js";
|
|
2
|
+
import type { SimLambdaVmSdkModuleProvider } from "./sim-lambda-vm-sdk-module-provider.js";
|
|
3
|
+
/**
|
|
4
|
+
* What the archive holds and who provides for what it does not. Between them
|
|
5
|
+
* that is enough to tell an absent package from one already accounted for.
|
|
6
|
+
*/
|
|
7
|
+
interface SimLambdaMissingSdkPackagesContext {
|
|
8
|
+
readonly archive: SimZipArchive;
|
|
9
|
+
readonly sdkModuleProvider: SimLambdaVmSdkModuleProvider;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The module the runtime provides for a specifier the archive does not
|
|
13
|
+
* bundle, or undefined for a specifier the provider does not serve.
|
|
14
|
+
*
|
|
15
|
+
* A package the project has not installed is refused here naming every other
|
|
16
|
+
* AWS SDK package the function code imports and the project is missing too.
|
|
17
|
+
*/
|
|
18
|
+
export declare function provideSdkModule(specifier: string, context: SimLambdaMissingSdkPackagesContext): unknown;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { assertDefined } from "../../../../../../util/type-guard/defined.js";
|
|
2
|
+
import { SimLambdaVmModuleResolver } from "../sim-lambda-vm-module-resolver.js";
|
|
3
|
+
import { SimLambdaSdkPackagesNotInstalledError } from "./sim-lambda-sdk-packages-not-installed.error.js";
|
|
4
|
+
/**
|
|
5
|
+
* The archive entries worth reading specifiers out of.
|
|
6
|
+
*/
|
|
7
|
+
const javaScriptFilePattern = /\.[cm]?js$/;
|
|
8
|
+
/**
|
|
9
|
+
* A quoted `@aws-sdk/*` specifier, however a file asks for it. A `require`
|
|
10
|
+
* call, a dynamic `import`, and a static import's `from` clause all match.
|
|
11
|
+
*/
|
|
12
|
+
const awsSdkImportPattern = /(?:\b(?:require|import)\s*\(|\b(?:from|import)\b)\s*["'](@aws-sdk\/[^"']+)["']/g;
|
|
13
|
+
/**
|
|
14
|
+
* The module the runtime provides for a specifier the archive does not
|
|
15
|
+
* bundle, or undefined for a specifier the provider does not serve.
|
|
16
|
+
*
|
|
17
|
+
* A package the project has not installed is refused here naming every other
|
|
18
|
+
* AWS SDK package the function code imports and the project is missing too.
|
|
19
|
+
*/
|
|
20
|
+
export function provideSdkModule(specifier, context) {
|
|
21
|
+
try {
|
|
22
|
+
return context.sdkModuleProvider.provideModule(specifier);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
throw everyMissingSdkPackage(error, context);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The refusal to raise for a package the project has not installed, naming
|
|
30
|
+
* every other AWS SDK package the function code imports and the project is
|
|
31
|
+
* missing too. Any other error stands as it is.
|
|
32
|
+
*
|
|
33
|
+
* The runtime provides a package when the code requires it, so on its own a
|
|
34
|
+
* refusal only ever names the one the code reached. A function importing two
|
|
35
|
+
* absent packages would then cost two runs and two installs, and the archive
|
|
36
|
+
* is the only place to read what a third-party function imports. So the
|
|
37
|
+
* archive is read here, once a package has already turned out to be missing,
|
|
38
|
+
* and one install covers the set. An archive that runs is never read.
|
|
39
|
+
*/
|
|
40
|
+
function everyMissingSdkPackage(error, context) {
|
|
41
|
+
if (!(error instanceof SimLambdaSdkPackagesNotInstalledError)) {
|
|
42
|
+
return error;
|
|
43
|
+
}
|
|
44
|
+
const resolver = new SimLambdaVmModuleResolver(context.archive);
|
|
45
|
+
const candidates = archiveAwsSdkImports(context.archive).filter((specifier) => !error.specifiers.includes(specifier) &&
|
|
46
|
+
!resolver.bundlesPackage(specifier));
|
|
47
|
+
const missing = context.sdkModuleProvider.unresolvedModules?.(candidates) ?? [];
|
|
48
|
+
if (missing.length === 0) {
|
|
49
|
+
return error;
|
|
50
|
+
}
|
|
51
|
+
return new SimLambdaSdkPackagesNotInstalledError([...error.specifiers, ...missing], { cause: error.cause });
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The AWS SDK packages a function code archive's own files import, in the
|
|
55
|
+
* order they appear.
|
|
56
|
+
*
|
|
57
|
+
* Bundled dependencies are left out. What a package under `node_modules/`
|
|
58
|
+
* imports is that package's own business, resolved from the bundle beside it,
|
|
59
|
+
* and reading a vendored tree to name one more install is a poor trade.
|
|
60
|
+
*/
|
|
61
|
+
function archiveAwsSdkImports(archive) {
|
|
62
|
+
const specifiers = new Set();
|
|
63
|
+
for (const filePath of archive.filePaths()) {
|
|
64
|
+
if (!isFunctionCodeFile(filePath)) {
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const source = archive.file(filePath).toString();
|
|
68
|
+
for (const match of source.matchAll(awsSdkImportPattern)) {
|
|
69
|
+
const specifier = match[1];
|
|
70
|
+
assertDefined(specifier, "Matched import has no specifier");
|
|
71
|
+
specifiers.add(specifier);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return specifiers.values().toArray();
|
|
75
|
+
}
|
|
76
|
+
function isFunctionCodeFile(filePath) {
|
|
77
|
+
return (javaScriptFilePattern.test(filePath) && !filePath.includes("node_modules/"));
|
|
78
|
+
}
|
package/dist/service/lambda/function/code/vm/sdk/sim-lambda-sdk-packages-not-installed.error.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS SDK packages the sim Lambda runtime was asked to provide and the
|
|
3
|
+
* consuming project has not installed.
|
|
4
|
+
*
|
|
5
|
+
* The real Lambda runtime provides these packages from the execution
|
|
6
|
+
* environment rather than from the deployment package, so the simulated
|
|
7
|
+
* runtime provides them from the host project. The fix is the same for every
|
|
8
|
+
* one of them, which is why they are named together: install them, or bundle
|
|
9
|
+
* them into the function code archive.
|
|
10
|
+
*/
|
|
11
|
+
export declare class SimLambdaSdkPackagesNotInstalledError extends Error {
|
|
12
|
+
readonly specifiers: readonly string[];
|
|
13
|
+
readonly name = "SimLambdaSdkPackagesNotInstalledError";
|
|
14
|
+
constructor(specifiers: readonly string[], options?: ErrorOptions);
|
|
15
|
+
}
|
package/dist/service/lambda/function/code/vm/sdk/sim-lambda-sdk-packages-not-installed.error.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS SDK packages the sim Lambda runtime was asked to provide and the
|
|
3
|
+
* consuming project has not installed.
|
|
4
|
+
*
|
|
5
|
+
* The real Lambda runtime provides these packages from the execution
|
|
6
|
+
* environment rather than from the deployment package, so the simulated
|
|
7
|
+
* runtime provides them from the host project. The fix is the same for every
|
|
8
|
+
* one of them, which is why they are named together: install them, or bundle
|
|
9
|
+
* them into the function code archive.
|
|
10
|
+
*/
|
|
11
|
+
export class SimLambdaSdkPackagesNotInstalledError extends Error {
|
|
12
|
+
specifiers;
|
|
13
|
+
name = "SimLambdaSdkPackagesNotInstalledError";
|
|
14
|
+
constructor(specifiers, options) {
|
|
15
|
+
super(notInstalledMessage(specifiers), options);
|
|
16
|
+
this.specifiers = specifiers;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function notInstalledMessage(specifiers) {
|
|
20
|
+
const [subject, object] = specifiers.length > 1
|
|
21
|
+
? ["the packages are", "them"]
|
|
22
|
+
: ["the package is", "it"];
|
|
23
|
+
return (`Cannot provide ${specifiers.join(", ")} to sim Lambda function code: ` +
|
|
24
|
+
`${subject} not installed. Install ${object} in your project, as the ` +
|
|
25
|
+
`real Lambda runtime provides ${object}, or bundle ${object} into the ` +
|
|
26
|
+
"function code archive.");
|
|
27
|
+
}
|
|
@@ -16,6 +16,15 @@ export declare const awsSdkPackagePrefix = "@aws-sdk/";
|
|
|
16
16
|
*/
|
|
17
17
|
export interface SimLambdaVmSdkModuleProvider {
|
|
18
18
|
provideModule(specifier: string): unknown;
|
|
19
|
+
/**
|
|
20
|
+
* Which of the given specifiers this provider serves and cannot resolve.
|
|
21
|
+
*
|
|
22
|
+
* The module system asks once a package has turned out to be missing. The
|
|
23
|
+
* refusal then names every package the project has to install, where on
|
|
24
|
+
* its own it would name only the one the function code reached first. A
|
|
25
|
+
* provider that does not answer is asked for one specifier at a time.
|
|
26
|
+
*/
|
|
27
|
+
unresolvedModules?(specifiers: readonly string[]): readonly string[];
|
|
19
28
|
}
|
|
20
29
|
/**
|
|
21
30
|
* Module provider used when no simulated AWS environment is wired up, such
|
|
@@ -46,6 +46,16 @@ export declare class SimSdkLambdaVmModuleProvider implements SimLambdaVmSdkModul
|
|
|
46
46
|
* undefined for other specifiers.
|
|
47
47
|
*/
|
|
48
48
|
provideModule(specifier: string): unknown;
|
|
49
|
+
/**
|
|
50
|
+
* Which of the given specifiers this provider serves and the host cannot
|
|
51
|
+
* resolve.
|
|
52
|
+
*
|
|
53
|
+
* Resolution only. A package that resolves and then throws while it
|
|
54
|
+
* initializes is installed, and that error belongs to the function code
|
|
55
|
+
* requiring it rather than to a report of what is missing.
|
|
56
|
+
*/
|
|
57
|
+
unresolvedModules(specifiers: readonly string[]): readonly string[];
|
|
58
|
+
private resolves;
|
|
49
59
|
private hostModuleExports;
|
|
50
60
|
}
|
|
51
61
|
export {};
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import path from "node:path";
|
|
3
1
|
import { SimSdkModuleClientInterceptor } from "../../../../../../sdk/module/sim-sdk-module-client-interceptor.js";
|
|
4
2
|
import { SimSdkCommandDispatcher } from "../../../../../../sdk/sim-sdk-command-dispatcher.js";
|
|
5
3
|
import { SimLambdaAwsApiOutbound } from "../../../outbound/sim-lambda-aws-api-outbound.js";
|
|
6
4
|
import { isSimLambdaHttpModuleSpecifier, makeSimLambdaHttpModule, simLambdaHttpModuleScheme, } from "../../../outbound/sim-lambda-http-module.js";
|
|
5
|
+
import { isModuleNotFoundError, requireHostModule, } from "../sim-lambda-host-modules.js";
|
|
6
|
+
import { SimLambdaSdkPackagesNotInstalledError } from "./sim-lambda-sdk-packages-not-installed.error.js";
|
|
7
7
|
import { awsSdkPackagePrefix, } from "./sim-lambda-vm-sdk-module-provider.js";
|
|
8
|
-
const hostRequire = createRequire(import.meta.url);
|
|
9
8
|
/**
|
|
10
9
|
* Provides host-installed AWS SDK v3 packages to sim Lambda vm function
|
|
11
10
|
* code, with client classes intercepted into the owning simulated AWS
|
|
@@ -61,6 +60,29 @@ export class SimSdkLambdaVmModuleProvider {
|
|
|
61
60
|
this.providedModules.set(specifier, module);
|
|
62
61
|
return module;
|
|
63
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Which of the given specifiers this provider serves and the host cannot
|
|
65
|
+
* resolve.
|
|
66
|
+
*
|
|
67
|
+
* Resolution only. A package that resolves and then throws while it
|
|
68
|
+
* initializes is installed, and that error belongs to the function code
|
|
69
|
+
* requiring it rather than to a report of what is missing.
|
|
70
|
+
*/
|
|
71
|
+
unresolvedModules(specifiers) {
|
|
72
|
+
return specifiers.filter((specifier) => specifier.startsWith(awsSdkPackagePrefix) && !this.resolves(specifier));
|
|
73
|
+
}
|
|
74
|
+
resolves(specifier) {
|
|
75
|
+
if (this.providedModules.has(specifier)) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
this.requireModule(specifier);
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
return !isModuleNotFoundError(error);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
64
86
|
hostModuleExports(specifier) {
|
|
65
87
|
let moduleExports;
|
|
66
88
|
try {
|
|
@@ -72,10 +94,9 @@ export class SimSdkLambdaVmModuleProvider {
|
|
|
72
94
|
if (!isModuleNotFoundError(error)) {
|
|
73
95
|
throw error;
|
|
74
96
|
}
|
|
75
|
-
throw new
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
"code archive.", { cause: error });
|
|
97
|
+
throw new SimLambdaSdkPackagesNotInstalledError([specifier], {
|
|
98
|
+
cause: error,
|
|
99
|
+
});
|
|
79
100
|
}
|
|
80
101
|
if (typeof moduleExports !== "object" || moduleExports === null) {
|
|
81
102
|
throw new Error(`Cannot provide ${specifier} to sim Lambda function code: the host ` +
|
|
@@ -84,30 +105,3 @@ export class SimSdkLambdaVmModuleProvider {
|
|
|
84
105
|
return moduleExports;
|
|
85
106
|
}
|
|
86
107
|
}
|
|
87
|
-
/**
|
|
88
|
-
* Require an AWS SDK package from the host.
|
|
89
|
-
*
|
|
90
|
-
* The packages are installed by the consuming project, as they are provided
|
|
91
|
-
* by the real Lambda runtime rather than by this package. Requiring from
|
|
92
|
-
* this module's context covers hoisted installs; the working-directory
|
|
93
|
-
* fallback covers stricter package layouts.
|
|
94
|
-
*/
|
|
95
|
-
function requireHostModule(specifier) {
|
|
96
|
-
try {
|
|
97
|
-
return hostRequire(specifier);
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
/* v8 ignore next 4 -- only reachable in consumer package layouts where
|
|
101
|
-
this package's own require context cannot see the SDK packages. */
|
|
102
|
-
return createRequire(path.join(process.cwd(), "package.json"))(specifier);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* Whether an error is a Node.js module resolution failure, as opposed to an
|
|
107
|
-
* error thrown while a resolved module initializes.
|
|
108
|
-
*/
|
|
109
|
-
function isModuleNotFoundError(error) {
|
|
110
|
-
return (error instanceof Error &&
|
|
111
|
-
"code" in error &&
|
|
112
|
-
(error.code === "MODULE_NOT_FOUND" || error.code === "ERR_MODULE_NOT_FOUND"));
|
|
113
|
-
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Require from the host a module the real Lambda runtime provides without it
|
|
3
|
+
* being bundled: a Node.js built-in, or an AWS SDK package.
|
|
4
|
+
*
|
|
5
|
+
* The SDK packages are installed by the consuming project, as the real
|
|
6
|
+
* runtime provides them rather than this package. Requiring from this
|
|
7
|
+
* module's context covers hoisted installs; the working-directory fallback
|
|
8
|
+
* covers stricter package layouts.
|
|
9
|
+
*/
|
|
10
|
+
export declare function requireHostModule(specifier: string): unknown;
|
|
11
|
+
/**
|
|
12
|
+
* Whether an error is a Node.js module resolution failure, as opposed to an
|
|
13
|
+
* error thrown while a resolved module initializes.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isModuleNotFoundError(error: unknown): boolean;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const hostRequire = createRequire(import.meta.url);
|
|
4
|
+
/**
|
|
5
|
+
* Require from the host a module the real Lambda runtime provides without it
|
|
6
|
+
* being bundled: a Node.js built-in, or an AWS SDK package.
|
|
7
|
+
*
|
|
8
|
+
* The SDK packages are installed by the consuming project, as the real
|
|
9
|
+
* runtime provides them rather than this package. Requiring from this
|
|
10
|
+
* module's context covers hoisted installs; the working-directory fallback
|
|
11
|
+
* covers stricter package layouts.
|
|
12
|
+
*/
|
|
13
|
+
export function requireHostModule(specifier) {
|
|
14
|
+
try {
|
|
15
|
+
return hostRequire(specifier);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
/* v8 ignore next 4 -- only reachable in consumer package layouts where
|
|
19
|
+
this package's own require context cannot see the SDK packages. */
|
|
20
|
+
return createRequire(path.join(process.cwd(), "package.json"))(specifier);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Whether an error is a Node.js module resolution failure, as opposed to an
|
|
25
|
+
* error thrown while a resolved module initializes.
|
|
26
|
+
*/
|
|
27
|
+
export function isModuleNotFoundError(error) {
|
|
28
|
+
return (error instanceof Error &&
|
|
29
|
+
"code" in error &&
|
|
30
|
+
(error.code === "MODULE_NOT_FOUND" || error.code === "ERR_MODULE_NOT_FOUND"));
|
|
31
|
+
}
|
|
@@ -14,6 +14,13 @@ export declare class SimLambdaVmModuleResolver {
|
|
|
14
14
|
* Resolve a require specifier to an archive file path.
|
|
15
15
|
*/
|
|
16
16
|
resolveFilePath(specifier: string, fromDirectory: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Whether the archive bundles a bare specifier itself.
|
|
19
|
+
*
|
|
20
|
+
* The archive takes precedence over anything the runtime provides, so a
|
|
21
|
+
* specifier it resolves is never asked of the module provider.
|
|
22
|
+
*/
|
|
23
|
+
bundlesPackage(specifier: string): boolean;
|
|
17
24
|
private resolveCandidates;
|
|
18
25
|
/**
|
|
19
26
|
* Resolve a bare specifier against node_modules bundled in the archive.
|
|
@@ -22,6 +22,21 @@ export class SimLambdaVmModuleResolver {
|
|
|
22
22
|
}
|
|
23
23
|
return this.resolvePackage(specifier);
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Whether the archive bundles a bare specifier itself.
|
|
27
|
+
*
|
|
28
|
+
* The archive takes precedence over anything the runtime provides, so a
|
|
29
|
+
* specifier it resolves is never asked of the module provider.
|
|
30
|
+
*/
|
|
31
|
+
bundlesPackage(specifier) {
|
|
32
|
+
try {
|
|
33
|
+
this.resolvePackage(specifier);
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
25
40
|
resolveCandidates(basePath, specifier) {
|
|
26
41
|
const candidates = [
|
|
27
42
|
basePath,
|