@kensio/yulin 1.21.31 → 1.21.33

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 (78) hide show
  1. package/dist/serve/message/sim-message-log-console.js +3 -3
  2. package/dist/service/apigateway/api/deployment/sim-rest-api-deployment.js +3 -3
  3. package/dist/service/apigateway/api/method/sim-rest-api-method.js +12 -12
  4. package/dist/service/apigateway/api/resource/sim-rest-api-resource.js +11 -9
  5. package/dist/service/apigateway/api/sim-rest-api-view.js +3 -3
  6. package/dist/service/apigateway/api/stage/sim-rest-api-stage.js +8 -10
  7. package/dist/service/apigatewayv2/api/integration/sim-http-api-integration.js +3 -3
  8. package/dist/service/apigatewayv2/api/route/sim-http-api-route-view.js +6 -6
  9. package/dist/service/apigatewayv2/api/sim-http-api-view.js +3 -3
  10. package/dist/service/apigatewayv2/api/stage/settings/sim-http-api-stage-route-settings.js +8 -7
  11. package/dist/service/apigatewayv2/api/stage/sim-http-api-stage.js +9 -9
  12. package/dist/service/athena/engine/sim-athena-partitioned-objects.js +7 -7
  13. package/dist/service/athena/sim-athena-engine.fixture.js +3 -0
  14. package/dist/service/athena/table/sim-athena-partition-filters.js +6 -5
  15. package/dist/service/backup/command/sim-backup-authorizer.js +5 -4
  16. package/dist/service/bedrock/response/sim-bedrock-declared-content.js +5 -10
  17. package/dist/service/cloudformation/stack/update/sim-cfn-stack-resource-changes.js +19 -10
  18. package/dist/service/cloudfront/cfn/response-headers-policy/sim-cfn-cf-rh-policy-security-values.js +2 -6
  19. package/dist/service/cognito/user-pool/trigger/sim-cognito-pre-sign-up-response.js +4 -7
  20. package/dist/service/dynamodb/command/transact/sim-dynamodb-transact-write.js +34 -25
  21. package/dist/service/dynamodb/secondary-index/sim-dynamodb-index-limits.js +5 -4
  22. package/dist/service/dynamodb/stream/sim-dynamodb-stream-shard-iterator.js +1 -3
  23. package/dist/service/dynamodb/time-to-live/sim-dynamodb-time-to-live.js +4 -3
  24. package/dist/service/glue/expression/sim-glue-partition-comparisons.js +1 -1
  25. package/dist/service/iam/authorize/context/sim-iam-auth-z-allow-requirement.js +6 -6
  26. package/dist/service/iam/authorize/match/condition/null/sim-iam-null.js +4 -1
  27. package/dist/service/kinesis/stream/sim-kinesis-shard-iterator.js +1 -3
  28. package/dist/service/lambda/command/create-function/create-function.handler.js +1 -0
  29. package/dist/service/lambda/destination/sim-lambda-destination-record.js +7 -7
  30. package/dist/service/lambda/function/environment/sim-lambda-environment-conflict.d.ts +22 -0
  31. package/dist/service/lambda/function/environment/sim-lambda-environment-conflict.js +33 -0
  32. package/dist/service/lambda/function/environment/sim-lambda-environment-conflicts.d.ts +53 -11
  33. package/dist/service/lambda/function/environment/sim-lambda-environment-conflicts.js +78 -24
  34. package/dist/service/lambda/function/policy/sim-lambda-permission.js +15 -14
  35. package/dist/service/lambda/function/sim-lambda-function.js +1 -0
  36. package/dist/service/lambda/function/sim-lambda-function.type.d.ts +8 -0
  37. package/dist/service/lambda/metric/sim-lambda-function-metrics.js +11 -9
  38. package/dist/service/lambda/sim-lambda-commands.js +3 -1
  39. package/dist/service/logs/metric/sim-logs-metric-filter.js +1 -8
  40. package/dist/service/route53/dns/answer/sim-route53-dns-zone-finder.js +5 -4
  41. package/dist/service/route53/resolve/sim-r53-zone-record-finder.js +8 -6
  42. package/dist/service/s3/bucket/notification/sim-s3-notification-filter.js +4 -7
  43. package/dist/service/s3/bucket/sim-s3-bucket.d.ts +8 -0
  44. package/dist/service/s3/bucket/sim-s3-bucket.js +11 -0
  45. package/dist/service/s3/bucket/sim-s3-key-writes.d.ts +16 -0
  46. package/dist/service/s3/bucket/sim-s3-key-writes.js +43 -0
  47. package/dist/service/s3/bucket/versioning/sim-s3-bucket-versioning.js +1 -3
  48. package/dist/service/s3/command/complete-multipart-upload/complete-multipart-upload.command.d.ts +2 -1
  49. package/dist/service/s3/command/complete-multipart-upload/complete-multipart-upload.handler.js +5 -1
  50. package/dist/service/s3/command/copy-object/copy-object-precondition.d.ts +23 -0
  51. package/dist/service/s3/command/copy-object/copy-object-precondition.js +34 -0
  52. package/dist/service/s3/command/copy-object/copy-object.command.d.ts +3 -1
  53. package/dist/service/s3/command/copy-object/copy-object.handler.d.ts +4 -0
  54. package/dist/service/s3/command/copy-object/copy-object.handler.js +11 -1
  55. package/dist/service/s3/command/object/sim-s3-write-precondition.d.ts +34 -0
  56. package/dist/service/s3/command/object/sim-s3-write-precondition.js +47 -0
  57. package/dist/service/s3/command/put-object/put-object.command.d.ts +2 -1
  58. package/dist/service/s3/command/put-object/put-object.handler.js +5 -1
  59. package/dist/service/s3/error/sim-s3.error.d.ts +10 -0
  60. package/dist/service/s3/error/sim-s3.error.js +12 -0
  61. package/dist/service/s3/object/s3-object-listing.js +4 -3
  62. package/dist/service/s3/serve/api/sim-s3-api-input.d.ts +4 -0
  63. package/dist/service/s3/serve/api/sim-s3-api-input.js +10 -0
  64. package/dist/service/s3/serve/api/sim-s3-api-multipart-input.js +2 -0
  65. package/dist/service/s3/serve/api/sim-s3-api-object-input.d.ts +6 -0
  66. package/dist/service/s3/serve/api/sim-s3-api-object-input.js +34 -2
  67. package/dist/service/s3/storage/filesystem/s3-filesystem-object-keys.js +9 -8
  68. package/dist/service/secretsmanager/cfn/secret/sim-cfn-secrets-manager-secret-template.test-support.js +15 -23
  69. package/dist/service/sqs/message/sim-sqs-message.js +3 -3
  70. package/dist/service/stepfunctions/machine/sim-state-machine.js +4 -3
  71. package/dist/util/process/sim-process-environment-reads.d.ts +52 -0
  72. package/dist/util/process/sim-process-environment-reads.js +94 -0
  73. package/dist/util/process/sim-process-environment.d.ts +32 -9
  74. package/dist/util/process/sim-process-environment.js +43 -17
  75. package/dist/util/schedule/cron/sim-cron-field-bounds.js +1 -1
  76. package/docs/services/lambda/README.md +6 -0
  77. package/docs/services/s3/README.md +84 -3
  78. package/package.json +3 -3
@@ -0,0 +1,23 @@
1
+ import type { SimS3Object } from "../../object/s3-object.js";
2
+ /**
3
+ * The conditions a copy can place on its source Object, as the
4
+ * `x-amz-copy-source-if-*` headers.
5
+ */
6
+ export interface SimS3CopySourcePrecondition {
7
+ readonly CopySourceIfMatch?: string | undefined;
8
+ readonly CopySourceIfNoneMatch?: string | undefined;
9
+ readonly CopySourceIfModifiedSince?: Date | undefined;
10
+ readonly CopySourceIfUnmodifiedSince?: Date | undefined;
11
+ }
12
+ /**
13
+ * Refuse a copy whose source Object fails one of its conditions.
14
+ *
15
+ * Real S3 follows RFC 7232 here. An ETag condition takes precedence over the
16
+ * date condition paired with it, so a source that matches `if-match` is copied
17
+ * however recently it changed, and a source that fails `if-none-match` is
18
+ * refused however long ago it changed. HTTP dates carry whole seconds, and the
19
+ * Object's last-modified time is compared at that precision.
20
+ *
21
+ * https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html
22
+ */
23
+ export declare function simS3RequireCopySourcePrecondition(source: SimS3Object, condition: SimS3CopySourcePrecondition): void;
@@ -0,0 +1,34 @@
1
+ import { SimS3PreconditionFailed } from "../../error/sim-s3.error.js";
2
+ import { simS3ETagMatches, simS3PreconditionMessage, } from "../object/sim-s3-write-precondition.js";
3
+ /**
4
+ * Refuse a copy whose source Object fails one of its conditions.
5
+ *
6
+ * Real S3 follows RFC 7232 here. An ETag condition takes precedence over the
7
+ * date condition paired with it, so a source that matches `if-match` is copied
8
+ * however recently it changed, and a source that fails `if-none-match` is
9
+ * refused however long ago it changed. HTTP dates carry whole seconds, and the
10
+ * Object's last-modified time is compared at that precision.
11
+ *
12
+ * https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html
13
+ */
14
+ export function simS3RequireCopySourcePrecondition(source, condition) {
15
+ if (!sourceConditionsHold(source, condition)) {
16
+ throw new SimS3PreconditionFailed(simS3PreconditionMessage);
17
+ }
18
+ }
19
+ function sourceConditionsHold(source, condition) {
20
+ const modified = wholeSeconds(source.lastModified);
21
+ const { CopySourceIfMatch: ifMatch, CopySourceIfNoneMatch: ifNoneMatch, CopySourceIfModifiedSince: ifModifiedSince, CopySourceIfUnmodifiedSince: ifUnmodifiedSince, } = condition;
22
+ const matchHolds = ifMatch === undefined
23
+ ? ifUnmodifiedSince === undefined ||
24
+ modified <= wholeSeconds(ifUnmodifiedSince)
25
+ : simS3ETagMatches(ifMatch, source.etag);
26
+ const noneMatchHolds = ifNoneMatch === undefined
27
+ ? ifModifiedSince === undefined ||
28
+ modified > wholeSeconds(ifModifiedSince)
29
+ : !simS3ETagMatches(ifNoneMatch, source.etag);
30
+ return matchHolds && noneMatchHolds;
31
+ }
32
+ function wholeSeconds(date) {
33
+ return Math.floor(date.getTime() / 1000);
34
+ }
@@ -2,6 +2,8 @@ import type { SimResponseMetadata } from "../../../aws/metadata/response-metadat
2
2
  import type { SimS3ObjectWriteMetadata } from "../../object/s3-write-metadata.js";
3
3
  import type { SimS3ObjectWriteStorage } from "../../object/s3-write-storage.js";
4
4
  import type { SimS3ObjectWriteTagging } from "../../object/s3-write-tagging.js";
5
+ import type { SimS3WritePrecondition } from "../object/sim-s3-write-precondition.js";
6
+ import type { SimS3CopySourcePrecondition } from "./copy-object-precondition.js";
5
7
  /**
6
8
  * Minimal structural sim S3 CopyObject command.
7
9
  */
@@ -15,7 +17,7 @@ export interface SimCopyObjectCommand {
15
17
  * apply to the destination Object under `MetadataDirective: REPLACE`. Under
16
18
  * the default `COPY` they are ignored, as real S3 ignores them.
17
19
  */
18
- export interface SimCopyObjectCommandInput extends SimS3ObjectWriteMetadata, SimS3ObjectWriteStorage, SimS3ObjectWriteTagging {
20
+ export interface SimCopyObjectCommandInput extends SimS3ObjectWriteMetadata, SimS3ObjectWriteStorage, SimS3ObjectWriteTagging, SimS3WritePrecondition, SimS3CopySourcePrecondition {
19
21
  readonly Bucket?: string | undefined;
20
22
  readonly Key?: string | undefined;
21
23
  /** The source Object, as `sourceBucket/sourceKey`, URL-encoded. */
@@ -36,6 +36,10 @@ export declare class CopyObjectCommandHandler implements CommandHandler<SimCopyO
36
36
  * decision. That keeps a malformed request and a missing Bucket answering
37
37
  * what real S3 answers. The source Object is read after authorization, which
38
38
  * leaves a refused caller knowing nothing about which keys exist.
39
+ *
40
+ * The conditions on the source are checked once it has been read, and the
41
+ * conditions on the destination after that. Either one failing refuses the
42
+ * copy before anything is written.
39
43
  */
40
44
  handle(command: SimCopyObjectCommand, options?: SimS3RequestOptions): Promise<SimCopyObjectCommandOutput>;
41
45
  }
@@ -4,8 +4,10 @@ import { SimIamAllowAllAuth, } from "../../../iam/authorize/sim-iam-inter-servic
4
4
  import { simS3QuotedETag } from "../../object/s3-object-etag.js";
5
5
  import { GetObjectAuthorizer } from "../get-object/get-object-authorizer.js";
6
6
  import { PutObjectAuthorizer } from "../put-object/put-object-authorizer.js";
7
+ import { simS3RequireWritePrecondition } from "../object/sim-s3-write-precondition.js";
7
8
  import { requireSimS3Bucket } from "../require-sim-s3-bucket.js";
8
9
  import { CopyObjectBuilder } from "./copy-object-builder.js";
10
+ import { simS3RequireCopySourcePrecondition } from "./copy-object-precondition.js";
9
11
  import { simS3CopySource, simS3CopySourceObject, simS3RefuseUnchangedSelfCopy, } from "./copy-object-source.js";
10
12
  /**
11
13
  * Simulated S3 CopyObjectCommand handler.
@@ -40,6 +42,10 @@ export class CopyObjectCommandHandler {
40
42
  * decision. That keeps a malformed request and a missing Bucket answering
41
43
  * what real S3 answers. The source Object is read after authorization, which
42
44
  * leaves a refused caller knowing nothing about which keys exist.
45
+ *
46
+ * The conditions on the source are checked once it has been read, and the
47
+ * conditions on the destination after that. Either one failing refuses the
48
+ * copy before anything is written.
43
49
  */
44
50
  async handle(command, options) {
45
51
  assertDefined(command.input.Bucket, "CopyObjectCommand.input.Bucket");
@@ -56,8 +62,12 @@ export class CopyObjectCommandHandler {
56
62
  this.sourceAuthorizer.authorize(source, from.key, options);
57
63
  const caller = this.destinationAuthorizer.authorize(destination, key, options);
58
64
  const stored = await simS3CopySourceObject(source, from.key);
65
+ simS3RequireCopySourcePrecondition(stored, command.input);
59
66
  const object = this.objectBuilder.build(command.input, key, stored, destination.getEncryption().algorithm);
60
- const version = await destination.putObject(object);
67
+ const version = await destination.writeKey(key, async () => {
68
+ await simS3RequireWritePrecondition(destination, key, command.input);
69
+ return await destination.putObject(object);
70
+ });
61
71
  this.notifications.objectCreated({
62
72
  bucket: destination,
63
73
  object,
@@ -0,0 +1,34 @@
1
+ import type { SimS3Bucket } from "../../bucket/sim-s3-bucket.js";
2
+ /**
3
+ * The conditions a write can place on the Object already under its key.
4
+ *
5
+ * PutObject, CopyObject and CompleteMultipartUpload all take these, as the
6
+ * `If-None-Match` and `If-Match` headers.
7
+ *
8
+ * https://docs.aws.amazon.com/AmazonS3/latest/userguide/conditional-writes.html
9
+ */
10
+ export interface SimS3WritePrecondition {
11
+ readonly IfMatch?: string | undefined;
12
+ readonly IfNoneMatch?: string | undefined;
13
+ }
14
+ /**
15
+ * The message real S3 gives with `PreconditionFailed`.
16
+ */
17
+ export declare const simS3PreconditionMessage = "At least one of the pre-conditions you specified did not hold";
18
+ /**
19
+ * Refuse a write whose condition does not hold against the key's current
20
+ * Object.
21
+ *
22
+ * `If-None-Match: *` fails when the key holds an Object. `If-Match` fails when
23
+ * the Object's ETag differs, and answers `NoSuchKey` when the key holds
24
+ * nothing. A delete marker counts as nothing, because a read of the key finds
25
+ * nothing behind it. Real S3 accepts only `*` for `If-None-Match` on a write.
26
+ */
27
+ export declare function simS3RequireWritePrecondition(bucket: SimS3Bucket, key: string, condition: SimS3WritePrecondition): Promise<void>;
28
+ /**
29
+ * Whether an entity tag a client sent names an Object's ETag.
30
+ *
31
+ * `*` names any Object. A client sends back the ETag it was given, quotes and
32
+ * all, so both forms are accepted.
33
+ */
34
+ export declare function simS3ETagMatches(sent: string, etag: string): boolean;
@@ -0,0 +1,47 @@
1
+ import { SimS3NoSuchKey, SimS3NotImplemented, SimS3PreconditionFailed, } from "../../error/sim-s3.error.js";
2
+ import { simS3UnquotedETag } from "../../object/s3-object-etag.js";
3
+ /**
4
+ * The message real S3 gives with `PreconditionFailed`.
5
+ */
6
+ export const simS3PreconditionMessage = "At least one of the pre-conditions you specified did not hold";
7
+ /**
8
+ * Refuse a write whose condition does not hold against the key's current
9
+ * Object.
10
+ *
11
+ * `If-None-Match: *` fails when the key holds an Object. `If-Match` fails when
12
+ * the Object's ETag differs, and answers `NoSuchKey` when the key holds
13
+ * nothing. A delete marker counts as nothing, because a read of the key finds
14
+ * nothing behind it. Real S3 accepts only `*` for `If-None-Match` on a write.
15
+ */
16
+ export async function simS3RequireWritePrecondition(bucket, key, condition) {
17
+ const { IfMatch, IfNoneMatch } = condition;
18
+ if (IfMatch === undefined && IfNoneMatch === undefined) {
19
+ return;
20
+ }
21
+ if (IfNoneMatch !== undefined && IfNoneMatch !== "*") {
22
+ throw new SimS3NotImplemented(`A header you provided implies functionality that is not implemented. ` +
23
+ `If-None-Match on a write takes only *.`);
24
+ }
25
+ const current = await bucket.getObject(key);
26
+ if (IfNoneMatch !== undefined && current !== undefined) {
27
+ throw new SimS3PreconditionFailed(simS3PreconditionMessage);
28
+ }
29
+ if (IfMatch === undefined) {
30
+ return;
31
+ }
32
+ if (current === undefined) {
33
+ throw new SimS3NoSuchKey(`No S3 Object named ${key}`);
34
+ }
35
+ if (!simS3ETagMatches(IfMatch, current.etag)) {
36
+ throw new SimS3PreconditionFailed(simS3PreconditionMessage);
37
+ }
38
+ }
39
+ /**
40
+ * Whether an entity tag a client sent names an Object's ETag.
41
+ *
42
+ * `*` names any Object. A client sends back the ETag it was given, quotes and
43
+ * all, so both forms are accepted.
44
+ */
45
+ export function simS3ETagMatches(sent, etag) {
46
+ return sent.trim() === "*" || simS3UnquotedETag(sent).trim() === etag;
47
+ }
@@ -3,6 +3,7 @@ import type { SimS3WriteBody } from "../../object/s3-write-body.js";
3
3
  import type { SimS3ObjectWriteMetadata } from "../../object/s3-write-metadata.js";
4
4
  import type { SimS3ObjectWriteStorage } from "../../object/s3-write-storage.js";
5
5
  import type { SimS3ObjectWriteTagging } from "../../object/s3-write-tagging.js";
6
+ import type { SimS3WritePrecondition } from "../object/sim-s3-write-precondition.js";
6
7
  /**
7
8
  * Minimal structural sim S3 PutObject command.
8
9
  */
@@ -16,7 +17,7 @@ export interface SimPutObjectCommand {
16
17
  * Object carries, which is why they are declared once in
17
18
  * `SimS3ObjectWriteMetadata` and shared with `CreateMultipartUpload`.
18
19
  */
19
- export interface SimPutObjectCommandInput extends SimS3ObjectWriteMetadata, SimS3ObjectWriteStorage, SimS3ObjectWriteTagging {
20
+ export interface SimPutObjectCommandInput extends SimS3ObjectWriteMetadata, SimS3ObjectWriteStorage, SimS3ObjectWriteTagging, SimS3WritePrecondition {
20
21
  readonly Bucket?: string | undefined;
21
22
  readonly Key?: string | undefined;
22
23
  readonly Body?: SimPutObjectBody;
@@ -5,6 +5,7 @@ import { requireSimS3Bucket } from "../require-sim-s3-bucket.js";
5
5
  import { PutObjectAuthorizer } from "./put-object-authorizer.js";
6
6
  import { PutObjectBuilder } from "./put-object-builder.js";
7
7
  import { simS3QuotedETag } from "../../object/s3-object-etag.js";
8
+ import { simS3RequireWritePrecondition } from "../object/sim-s3-write-precondition.js";
8
9
  /**
9
10
  * Simulated S3 PutObjectCommand handler.
10
11
  *
@@ -45,7 +46,10 @@ export class PutObjectCommandHandler {
45
46
  await this.background.sequence();
46
47
  const caller = this.authorizer.authorize(bucket, Key, options);
47
48
  const object = this.objectBuilder.build(command, bucket.getEncryption().algorithm);
48
- const version = await bucket.putObject(object);
49
+ const version = await bucket.writeKey(Key, async () => {
50
+ await simS3RequireWritePrecondition(bucket, Key, command.input);
51
+ return await bucket.putObject(object);
52
+ });
49
53
  const versionId = version?.versionId;
50
54
  // Every write path funnels through here, so this one call covers the SDK,
51
55
  // an intercepted SDK client and the REST endpoint alike.
@@ -199,3 +199,13 @@ export declare class SimS3InvalidTag extends SimS3Error {
199
199
  readonly name = "InvalidTag";
200
200
  constructor(message: string);
201
201
  }
202
+ /**
203
+ * Simulated S3 PreconditionFailed error.
204
+ *
205
+ * Real S3 answers this when a conditional request's condition does not hold.
206
+ * A write refused this way leaves the Object under its key as it was.
207
+ */
208
+ export declare class SimS3PreconditionFailed extends SimS3Error {
209
+ readonly name = "PreconditionFailed";
210
+ constructor(message: string);
211
+ }
@@ -234,3 +234,15 @@ export class SimS3InvalidTag extends SimS3Error {
234
234
  super(message, { httpStatusCode: 400 });
235
235
  }
236
236
  }
237
+ /**
238
+ * Simulated S3 PreconditionFailed error.
239
+ *
240
+ * Real S3 answers this when a conditional request's condition does not hold.
241
+ * A write refused this way leaves the Object under its key as it was.
242
+ */
243
+ export class SimS3PreconditionFailed extends SimS3Error {
244
+ name = "PreconditionFailed";
245
+ constructor(message) {
246
+ super(message, { httpStatusCode: 412 });
247
+ }
248
+ }
@@ -87,10 +87,11 @@ function listingEntries(request) {
87
87
  entries.push({ name: object.key, object });
88
88
  continue;
89
89
  }
90
- if (commonPrefix !== rolledUp) {
91
- rolledUp = commonPrefix;
92
- entries.push({ name: commonPrefix });
90
+ if (commonPrefix === rolledUp) {
91
+ continue;
93
92
  }
93
+ rolledUp = commonPrefix;
94
+ entries.push({ name: commonPrefix });
94
95
  }
95
96
  return entries;
96
97
  }
@@ -31,6 +31,10 @@ export declare function listObjectsInput(request: SimS3ApiRequest): object;
31
31
  * an absent member and an empty one differently.
32
32
  */
33
33
  export declare function optional(name: string, value: string | null): Record<string, string>;
34
+ /**
35
+ * The date form of the same, dropping a value that is not a date.
36
+ */
37
+ export declare function optionalDate(name: string, value: string | null): Record<string, Date>;
34
38
  /**
35
39
  * The numeric form of the same, dropping a value that is not a number.
36
40
  */
@@ -61,6 +61,16 @@ export function listObjectsInput(request) {
61
61
  export function optional(name, value) {
62
62
  return value === null ? {} : { [name]: value };
63
63
  }
64
+ /**
65
+ * The date form of the same, dropping a value that is not a date.
66
+ */
67
+ export function optionalDate(name, value) {
68
+ if (value === null) {
69
+ return {};
70
+ }
71
+ const parsed = new Date(value);
72
+ return Number.isNaN(parsed.getTime()) ? {} : { [name]: parsed };
73
+ }
64
74
  /**
65
75
  * The numeric form of the same, dropping a value that is not a number.
66
76
  */
@@ -1,4 +1,5 @@
1
1
  import { optional, optionalNumber } from "./sim-s3-api-input.js";
2
+ import { writePreconditionInput } from "./sim-s3-api-object-input.js";
2
3
  import { readSimS3CompletedUpload } from "./sim-s3-api-xml-read.js";
3
4
  /**
4
5
  * Reading the multipart upload operations' inputs out of a request.
@@ -33,6 +34,7 @@ export function uploadPartInput(request) {
33
34
  export function completeMultipartUploadInput(request) {
34
35
  return {
35
36
  ...uploadInput(request),
37
+ ...writePreconditionInput(request),
36
38
  MultipartUpload: readSimS3CompletedUpload(request.body),
37
39
  };
38
40
  }
@@ -35,6 +35,12 @@ export declare function getObjectInput(request: SimS3ApiRequest): object;
35
35
  * them.
36
36
  */
37
37
  export declare function putObjectInput(request: SimS3ApiRequest): object;
38
+ /**
39
+ * The conditions a write places on the Object already under its key, which
40
+ * PutObject, CopyObject and CompleteMultipartUpload all read from the same two
41
+ * headers.
42
+ */
43
+ export declare function writePreconditionInput(request: SimS3ApiRequest): object;
38
44
  /**
39
45
  * The input of a copy, which names its source in a header and carries no bytes
40
46
  * of its own.
@@ -1,5 +1,5 @@
1
1
  import { simS3WriteMetadataHeaders } from "../../object/s3-write-metadata.js";
2
- import { optional, utf8 } from "./sim-s3-api-input.js";
2
+ import { optional, optionalDate, utf8 } from "./sim-s3-api-input.js";
3
3
  import { readSimS3Tagging } from "./sim-s3-api-tagging-read.js";
4
4
  /**
5
5
  * Reading the Object operations' inputs out of a request.
@@ -54,7 +54,22 @@ export function getObjectInput(request) {
54
54
  * them.
55
55
  */
56
56
  export function putObjectInput(request) {
57
- return { ...createMultipartUploadInput(request), Body: request.body };
57
+ return {
58
+ ...createMultipartUploadInput(request),
59
+ ...writePreconditionInput(request),
60
+ Body: request.body,
61
+ };
62
+ }
63
+ /**
64
+ * The conditions a write places on the Object already under its key, which
65
+ * PutObject, CopyObject and CompleteMultipartUpload all read from the same two
66
+ * headers.
67
+ */
68
+ export function writePreconditionInput(request) {
69
+ return {
70
+ ...optional("IfMatch", request.headers.get("if-match")),
71
+ ...optional("IfNoneMatch", request.headers.get("if-none-match")),
72
+ };
58
73
  }
59
74
  /**
60
75
  * The input of a copy, which names its source in a header and carries no bytes
@@ -67,11 +82,28 @@ export function putObjectInput(request) {
67
82
  export function copyObjectInput(request) {
68
83
  return {
69
84
  ...createMultipartUploadInput(request),
85
+ ...writePreconditionInput(request),
86
+ ...copySourcePreconditionInput(request),
70
87
  ...optional("CopySource", request.headers.get(simS3CopySourceHeader)),
71
88
  ...optional("MetadataDirective", request.headers.get("x-amz-metadata-directive")),
72
89
  ...optional("TaggingDirective", request.headers.get("x-amz-tagging-directive")),
73
90
  };
74
91
  }
92
+ /**
93
+ * The conditions a copy places on its source Object.
94
+ *
95
+ * The two date headers are HTTP dates, which the SDK hands the operation as
96
+ * `Date` values.
97
+ */
98
+ function copySourcePreconditionInput(request) {
99
+ const header = (name) => request.headers.get(`x-amz-copy-source-if-${name}`);
100
+ return {
101
+ ...optional("CopySourceIfMatch", header("match")),
102
+ ...optional("CopySourceIfNoneMatch", header("none-match")),
103
+ ...optionalDate("CopySourceIfModifiedSince", header("modified-since")),
104
+ ...optionalDate("CopySourceIfUnmodifiedSince", header("unmodified-since")),
105
+ };
106
+ }
75
107
  /**
76
108
  * The input of a request that describes an Object without carrying its bytes,
77
109
  * which is what starting a multipart upload is.
@@ -60,14 +60,15 @@ export class FilesystemS3ObjectKeys {
60
60
  nestedKeyPromises.push(this.listInDirectory(entryPath));
61
61
  continue;
62
62
  }
63
- if (entry.isFile()) {
64
- const key = path
65
- .relative(this.directoryPath, entryPath)
66
- .split(path.sep)
67
- .join("/");
68
- if (this.safety.isAllowedObjectKeyExtension(key)) {
69
- keys.push(key);
70
- }
63
+ if (!entry.isFile()) {
64
+ continue;
65
+ }
66
+ const key = path
67
+ .relative(this.directoryPath, entryPath)
68
+ .split(path.sep)
69
+ .join("/");
70
+ if (this.safety.isAllowedObjectKeyExtension(key)) {
71
+ keys.push(key);
71
72
  }
72
73
  }
73
74
  const nestedKeys = await Promise.all(nestedKeyPromises);
@@ -18,29 +18,21 @@ export function secretTemplate(properties = {}) {
18
18
  }
19
19
  function secretProperties(properties) {
20
20
  const { name, description, kmsKeyId, tags, secretString } = properties;
21
- const declared = {};
22
- if (name !== undefined) {
23
- declared["Name"] = name;
24
- }
25
- if (description !== undefined) {
26
- declared["Description"] = description;
27
- }
28
- if (kmsKeyId !== undefined) {
29
- declared["KmsKeyId"] = kmsKeyId;
30
- }
31
- if (tags !== undefined) {
32
- declared["Tags"] = [...tags];
33
- }
34
- if (secretString === undefined) {
35
- declared["GenerateSecretString"] = {
36
- SecretStringTemplate: "{}",
37
- GenerateStringKey: "current",
38
- PasswordLength: properties.passwordLength ?? 64,
39
- };
40
- }
41
- else {
42
- declared["SecretString"] = secretString;
43
- }
21
+ const declared = {
22
+ ...(name !== undefined && { Name: name }),
23
+ ...(description !== undefined && { Description: description }),
24
+ ...(kmsKeyId !== undefined && { KmsKeyId: kmsKeyId }),
25
+ ...(tags !== undefined && { Tags: [...tags] }),
26
+ ...(secretString === undefined
27
+ ? {
28
+ GenerateSecretString: {
29
+ SecretStringTemplate: "{}",
30
+ GenerateStringKey: "current",
31
+ PasswordLength: properties.passwordLength ?? 64,
32
+ },
33
+ }
34
+ : { SecretString: secretString }),
35
+ };
44
36
  return declared;
45
37
  }
46
38
  function readerResource() {
@@ -126,10 +126,10 @@ export class SimSqsMessage {
126
126
  "ApproximateFirstReceiveTimestamp",
127
127
  String(this.visibility.firstReceived.getTime()),
128
128
  ],
129
+ ...(this.deadLetterSourceArn === undefined
130
+ ? []
131
+ : [["DeadLetterQueueSourceArn", this.deadLetterSourceArn]]),
129
132
  ]);
130
- if (this.deadLetterSourceArn !== undefined) {
131
- values.set("DeadLetterQueueSourceArn", this.deadLetterSourceArn);
132
- }
133
133
  return values;
134
134
  }
135
135
  }
@@ -56,9 +56,10 @@ export class SimStateMachine {
56
56
  */
57
57
  update(changes) {
58
58
  this.#roleArn = changes.roleArn ?? this.#roleArn;
59
- if (changes.definition !== undefined && changes.parsed !== undefined) {
60
- this.#definition = changes.definition;
61
- this.#parsed = changes.parsed;
59
+ if (changes.definition === undefined || changes.parsed === undefined) {
60
+ return;
62
61
  }
62
+ this.#definition = changes.definition;
63
+ this.#parsed = changes.parsed;
63
64
  }
64
65
  }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Records the names code reads from the host process.env.
3
+ *
4
+ * SimLambdaEnvironmentConflicts warns that a module-scope read sees the host
5
+ * value of a variable instead of the function's own. Whether that matters
6
+ * depends on a read having happened at all, which configuration cannot tell.
7
+ * This keeps the evidence: the host environment is handed out behind a Proxy
8
+ * that notes each name read through it.
9
+ *
10
+ * Enumerating the whole environment is not counted as reading every name in
11
+ * it. Node.js copies process.env key by key when it spawns a child process,
12
+ * and so does any tool that snapshots it, and none of that is handler code
13
+ * capturing a value it later depends on. An enumeration is recognised by its
14
+ * shape: the property reads that follow a listing of the keys, in the order
15
+ * the listing gave them. Its reads are recorded as they happen and taken off
16
+ * the record again once every key has been read, so a listing that is not
17
+ * followed by a complete copy hides nothing. A module copying the environment
18
+ * with a spread at module scope is missed as a result.
19
+ */
20
+ export declare class SimProcessEnvironmentReads {
21
+ private readonly names;
22
+ /**
23
+ * The environment objects each watching Proxy stands in front of, so that
24
+ * assigning one back to process.env does not stack a Proxy on a Proxy.
25
+ */
26
+ private readonly targets;
27
+ /**
28
+ * The keys of an enumeration in progress, and how far its reads have got.
29
+ */
30
+ private enumeration;
31
+ /**
32
+ * Whether code has read the name through a watched environment.
33
+ */
34
+ wasRead(name: string): boolean;
35
+ /**
36
+ * A view of the environment that records the names read through it.
37
+ *
38
+ * Writes and deletes go straight to the environment, so a value assigned
39
+ * through the view is coerced to a string as process.env would coerce it.
40
+ */
41
+ watch(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
42
+ /**
43
+ * The environment behind a watching view, or the environment itself when it
44
+ * is not one.
45
+ */
46
+ unwrap(environment: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
47
+ /**
48
+ * Note a read, and take an enumeration's reads off the record once it has
49
+ * read every key.
50
+ */
51
+ private recordRead;
52
+ }