@kensio/yulin 1.21.7 → 1.21.9
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/README.md +22 -2
- package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-properties.js +2 -0
- package/dist/service/lambda/cfn/event-source-mapping/sim-cfn-lambda-event-source-mapping-property-rules.js +1 -1
- package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-input.d.ts +2 -0
- package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-input.js +4 -0
- package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping-refusals.js +0 -5
- package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping.handler.d.ts +2 -0
- package/dist/service/lambda/command/event-source-mapping/create-event-source-mapping.handler.js +2 -0
- package/dist/service/lambda/command/event-source-mapping/sim-lambda-event-source-mapping-commands.d.ts +2 -0
- package/dist/service/lambda/destination/sim-lambda-destination-targets.d.ts +2 -1
- package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-shard-poller.d.ts +0 -4
- package/dist/service/lambda/event-source/poll/kinesis/sim-lambda-kinesis-shard-poller.js +10 -15
- package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-event-source-poller.d.ts +1 -6
- package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-event-source-poller.js +9 -27
- package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-polling.d.ts +19 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-dynamodb-stream-polling.js +27 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-event-source-function.d.ts +1 -1
- package/dist/service/lambda/event-source/poll/sim-lambda-event-source-function.js +3 -1
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-age.d.ts +13 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-age.js +27 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-info.d.ts +15 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-info.js +26 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-outcome.d.ts +1 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-outcome.js +7 -1
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-processing.d.ts +17 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-batch-processing.js +12 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-cursor.d.ts +15 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-cursor.js +28 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-expiry.d.ts +19 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-expiry.js +23 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-context.d.ts +3 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-context.js +19 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-destination.d.ts +15 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-destination.js +38 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-record.d.ts +41 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-failure-record.js +17 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress-state.d.ts +21 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress-state.js +39 -0
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress.d.ts +7 -37
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-progress.js +22 -62
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-retry.d.ts +4 -39
- package/dist/service/lambda/event-source/poll/sim-lambda-stream-retry.js +12 -64
- package/dist/service/lambda/event-source/sim-lambda-event-source-mapping.d.ts +10 -3
- package/dist/service/lambda/event-source/sim-lambda-event-source-mapping.js +12 -3
- package/dist/service/lambda/event-source/sim-lambda-stream-destination-config.d.ts +7 -0
- package/dist/service/lambda/event-source/sim-lambda-stream-destination-config.js +38 -0
- package/dist/service/lambda/event-source/stream/kinesis/sim-kinesis-event-source-streams.js +1 -0
- package/dist/service/lambda/event-source/stream/sim-dynamodb-event-source-stream-reader.js +1 -0
- package/dist/service/lambda/event-source/stream/sim-dynamodb-event-source-stream-shard.d.ts +1 -0
- package/dist/service/lambda/event-source/stream/sim-dynamodb-event-source-stream-shard.js +7 -0
- package/dist/service/lambda/event-source/stream/sim-lambda-event-source-streams.d.ts +1 -0
- package/dist/service/lambda/index.d.ts +1 -0
- package/dist/service/lambda/sim-lambda-commands.js +1 -0
- package/dist/service/ses/command/authorize/sim-ses-authorizer.d.ts +9 -0
- package/dist/service/ses/command/authorize/sim-ses-authorizer.js +18 -2
- package/dist/service/ses/command/send/sim-ses-send-email.js +2 -1
- package/docs/README.md +7 -0
- package/docs/ai-skill/README.md +6 -1
- package/docs/sdk/README.md +15 -0
- package/docs/services/acm/README.md +24 -40
- package/docs/services/apigateway/README.md +52 -71
- package/docs/services/apigatewayv2/README.md +55 -74
- package/docs/services/athena/README.md +17 -26
- package/docs/services/backup/README.md +29 -39
- package/docs/services/bedrock/README.md +38 -52
- package/docs/services/cloudformation/README.md +43 -55
- package/docs/services/cloudfront/README.md +69 -95
- package/docs/services/cloudwatch/README.md +40 -54
- package/docs/services/cognito/README.md +30 -45
- package/docs/services/dynamodb/README.md +34 -51
- package/docs/services/ecr/README.md +36 -77
- package/docs/services/ecs/README.md +26 -46
- package/docs/services/elbv2/README.md +19 -29
- package/docs/services/eventbridge/README.md +14 -18
- package/docs/services/firehose/README.md +24 -32
- package/docs/services/glue/README.md +41 -78
- package/docs/services/iam/README.md +13 -15
- package/docs/services/kinesis/README.md +53 -93
- package/docs/services/kms/README.md +22 -32
- package/docs/services/lambda/README.md +112 -85
- package/docs/services/logs/README.md +41 -50
- package/docs/services/organizations/README.md +50 -85
- package/docs/services/personalize/README.md +28 -44
- package/docs/services/rekognition/README.md +26 -38
- package/docs/services/route53/README.md +17 -17
- package/docs/services/s3/README.md +47 -51
- package/docs/services/scheduler/README.md +41 -52
- package/docs/services/secretsmanager/README.md +27 -42
- package/docs/services/ses/README.md +24 -34
- package/docs/services/sns/README.md +18 -26
- package/docs/services/sqs/README.md +14 -14
- package/docs/services/ssm/README.md +13 -17
- package/docs/services/stepfunctions/README.md +18 -20
- package/docs/services/sts/README.md +32 -45
- package/docs/services/wafv2/README.md +12 -17
- package/docs/testing/README.md +228 -0
- package/docs/time/README.md +10 -0
- package/llms.txt +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { SimLambdaStreamFailureRecord, StreamFailureRecordProperties } from "./sim-lambda-stream-failure-record.js";
|
|
2
|
+
/** Build invocation metadata for a stream failure notification. */
|
|
3
|
+
export declare function streamFailureContext(properties: StreamFailureRecordProperties): Pick<SimLambdaStreamFailureRecord, "requestContext" | "responseContext">;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
/** Build invocation metadata for a stream failure notification. */
|
|
3
|
+
export function streamFailureContext(properties) {
|
|
4
|
+
return {
|
|
5
|
+
requestContext: {
|
|
6
|
+
requestId: randomUUID(),
|
|
7
|
+
functionArn: properties.mapping.functionArn,
|
|
8
|
+
condition: properties.discard.condition,
|
|
9
|
+
approximateInvokeCount: properties.invokeCount,
|
|
10
|
+
},
|
|
11
|
+
...(properties.invokeCount > 0 && {
|
|
12
|
+
responseContext: {
|
|
13
|
+
statusCode: 200,
|
|
14
|
+
executedVersion: properties.simFunction.version,
|
|
15
|
+
...(properties.functionError && { functionError: "Unhandled" }),
|
|
16
|
+
},
|
|
17
|
+
}),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { SimLambdaStreamBatchOutcome } from "./sim-lambda-stream-batch-outcome.js";
|
|
2
|
+
import type { SimLambdaFunction } from "../../function/sim-lambda-function.js";
|
|
3
|
+
import type { SimLambdaEventSourceMapping } from "../sim-lambda-event-source-mapping.js";
|
|
4
|
+
import type { SimLambdaEventSourceStreamProgressBatch } from "../stream/sim-lambda-event-source-streams.js";
|
|
5
|
+
import { type SimLambdaStreamDiscard } from "./sim-lambda-stream-failure-record.js";
|
|
6
|
+
/** Count invocations and send discarded batch metadata through the destination service. */
|
|
7
|
+
export declare class SimLambdaStreamFailureDestination {
|
|
8
|
+
private readonly mapping;
|
|
9
|
+
private invokeCount;
|
|
10
|
+
private functionError;
|
|
11
|
+
constructor(mapping: SimLambdaEventSourceMapping);
|
|
12
|
+
invoked(outcome: SimLambdaStreamBatchOutcome): void;
|
|
13
|
+
reset(): void;
|
|
14
|
+
deliver(discard: SimLambdaStreamDiscard | undefined, batch: SimLambdaEventSourceStreamProgressBatch, simFunction: SimLambdaFunction): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { SimLambdaDestinationArn } from "../../destination/sim-lambda-destination-arn.js";
|
|
2
|
+
import { simLambdaStreamFailureRecord, } from "./sim-lambda-stream-failure-record.js";
|
|
3
|
+
/** Count invocations and send discarded batch metadata through the destination service. */
|
|
4
|
+
export class SimLambdaStreamFailureDestination {
|
|
5
|
+
mapping;
|
|
6
|
+
invokeCount = 0;
|
|
7
|
+
functionError = false;
|
|
8
|
+
constructor(mapping) {
|
|
9
|
+
this.mapping = mapping;
|
|
10
|
+
}
|
|
11
|
+
invoked(outcome) {
|
|
12
|
+
this.invokeCount += 1;
|
|
13
|
+
this.functionError = outcome.functionError;
|
|
14
|
+
}
|
|
15
|
+
reset() {
|
|
16
|
+
this.invokeCount = 0;
|
|
17
|
+
}
|
|
18
|
+
async deliver(discard, batch, simFunction) {
|
|
19
|
+
const mapping = this.mapping;
|
|
20
|
+
const arn = mapping.failureDestinationArn;
|
|
21
|
+
if (discard === undefined || arn === undefined || arn === "")
|
|
22
|
+
return;
|
|
23
|
+
const record = simLambdaStreamFailureRecord({
|
|
24
|
+
mapping,
|
|
25
|
+
simFunction,
|
|
26
|
+
discard,
|
|
27
|
+
invokeCount: this.invokeCount,
|
|
28
|
+
shardId: batch.shardId,
|
|
29
|
+
functionError: this.functionError,
|
|
30
|
+
});
|
|
31
|
+
await mapping.destinations.deliver({
|
|
32
|
+
destinationArn: SimLambdaDestinationArn.of(arn),
|
|
33
|
+
sourceFunctionArn: mapping.functionArn,
|
|
34
|
+
sourceFunctionRoleArn: simFunction.roleArn,
|
|
35
|
+
record,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type SimLambdaStreamBatchInfo } from "./sim-lambda-stream-batch-info.js";
|
|
2
|
+
import type { SimLambdaFunction } from "../../function/sim-lambda-function.js";
|
|
3
|
+
import type { SimLambdaEventSourceMapping } from "../sim-lambda-event-source-mapping.js";
|
|
4
|
+
import type { SimLambdaStreamRecordTime } from "./sim-lambda-stream-record-times.js";
|
|
5
|
+
export type SimLambdaStreamFailureCondition = "RetryAttemptsExhausted" | "RecordAgeExceeded";
|
|
6
|
+
export interface SimLambdaStreamDiscard {
|
|
7
|
+
readonly records: readonly SimLambdaStreamRecordTime[];
|
|
8
|
+
readonly condition: SimLambdaStreamFailureCondition;
|
|
9
|
+
readonly at: Date;
|
|
10
|
+
}
|
|
11
|
+
export interface SimLambdaStreamFailureRecord {
|
|
12
|
+
readonly version: string;
|
|
13
|
+
readonly timestamp: string;
|
|
14
|
+
readonly requestContext: {
|
|
15
|
+
readonly requestId: string;
|
|
16
|
+
readonly functionArn: string;
|
|
17
|
+
readonly condition: SimLambdaStreamFailureCondition;
|
|
18
|
+
readonly approximateInvokeCount: number;
|
|
19
|
+
};
|
|
20
|
+
readonly responseContext?: {
|
|
21
|
+
readonly statusCode: number;
|
|
22
|
+
readonly executedVersion: string;
|
|
23
|
+
readonly functionError?: string;
|
|
24
|
+
};
|
|
25
|
+
readonly DDBStreamBatchInfo?: SimLambdaStreamBatchInfo;
|
|
26
|
+
readonly KinesisBatchInfo?: SimLambdaStreamBatchInfo;
|
|
27
|
+
}
|
|
28
|
+
export interface StreamFailureRecordProperties {
|
|
29
|
+
readonly mapping: SimLambdaEventSourceMapping;
|
|
30
|
+
readonly simFunction: SimLambdaFunction;
|
|
31
|
+
readonly shardId: string | undefined;
|
|
32
|
+
readonly discard: SimLambdaStreamDiscard;
|
|
33
|
+
readonly invokeCount: number;
|
|
34
|
+
readonly functionError: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* SQS and SNS receive metadata identifying the discarded records.
|
|
38
|
+
* @see https://docs.aws.amazon.com/lambda/latest/dg/services-dynamodb-errors.html
|
|
39
|
+
* @see https://docs.aws.amazon.com/lambda/latest/dg/kinesis-on-failure-destination.html
|
|
40
|
+
*/
|
|
41
|
+
export declare function simLambdaStreamFailureRecord(properties: StreamFailureRecordProperties): SimLambdaStreamFailureRecord;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { streamFailureContext } from "./sim-lambda-stream-failure-context.js";
|
|
2
|
+
import { simLambdaStreamBatchInfo, streamFailureBatchInfo, } from "./sim-lambda-stream-batch-info.js";
|
|
3
|
+
/**
|
|
4
|
+
* SQS and SNS receive metadata identifying the discarded records.
|
|
5
|
+
* @see https://docs.aws.amazon.com/lambda/latest/dg/services-dynamodb-errors.html
|
|
6
|
+
* @see https://docs.aws.amazon.com/lambda/latest/dg/kinesis-on-failure-destination.html
|
|
7
|
+
*/
|
|
8
|
+
export function simLambdaStreamFailureRecord(properties) {
|
|
9
|
+
const { mapping, discard, shardId } = properties;
|
|
10
|
+
const info = simLambdaStreamBatchInfo(discard.records, mapping.eventSourceArn, shardId);
|
|
11
|
+
return {
|
|
12
|
+
version: "1.0",
|
|
13
|
+
timestamp: discard.at.toISOString(),
|
|
14
|
+
...streamFailureContext(properties),
|
|
15
|
+
...streamFailureBatchInfo(mapping.eventSourceArn, info),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SimLambdaStreamExpiry } from "./sim-lambda-stream-expiry.js";
|
|
2
|
+
import { SimLambdaStreamFailureDestination } from "./sim-lambda-stream-failure-destination.js";
|
|
3
|
+
import { SimLambdaStreamCursor } from "./sim-lambda-stream-cursor.js";
|
|
4
|
+
import { SimLambdaStreamRetry } from "./sim-lambda-stream-retry.js";
|
|
5
|
+
import type { BackgroundScheduler, BackgroundTask } from "../../../../util/background/background.js";
|
|
6
|
+
import type { SimLambdaEventSourceMapping } from "../sim-lambda-event-source-mapping.js";
|
|
7
|
+
import type { SimLambdaEventSourceStreamProgressBatch } from "../stream/sim-lambda-event-source-streams.js";
|
|
8
|
+
export interface SimLambdaStreamProgressProperties {
|
|
9
|
+
readonly mapping: SimLambdaEventSourceMapping;
|
|
10
|
+
readonly background: BackgroundScheduler;
|
|
11
|
+
readonly poll: BackgroundTask;
|
|
12
|
+
}
|
|
13
|
+
/** Own the per-shard retry state and its expiry and destination collaborators. */
|
|
14
|
+
export declare class SimLambdaStreamProgressState {
|
|
15
|
+
readonly expiry: SimLambdaStreamExpiry;
|
|
16
|
+
readonly failures: SimLambdaStreamFailureDestination;
|
|
17
|
+
readonly cursor: SimLambdaStreamCursor;
|
|
18
|
+
readonly retry: SimLambdaStreamRetry;
|
|
19
|
+
constructor(properties: SimLambdaStreamProgressProperties);
|
|
20
|
+
handled(batch: SimLambdaEventSourceStreamProgressBatch): void;
|
|
21
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { SimLambdaStreamExpiry } from "./sim-lambda-stream-expiry.js";
|
|
2
|
+
import { SimLambdaStreamBatchAge } from "./sim-lambda-stream-batch-age.js";
|
|
3
|
+
import { SimLambdaStreamFailureDestination } from "./sim-lambda-stream-failure-destination.js";
|
|
4
|
+
import { SimLambdaStreamCursor } from "./sim-lambda-stream-cursor.js";
|
|
5
|
+
import { SimLambdaStreamRetry } from "./sim-lambda-stream-retry.js";
|
|
6
|
+
import { PollSchedule } from "../../../../util/background/poll-schedule.js";
|
|
7
|
+
import { assertDefined } from "../../../../util/type-guard/defined.js";
|
|
8
|
+
/** Own the per-shard retry state and its expiry and destination collaborators. */
|
|
9
|
+
export class SimLambdaStreamProgressState {
|
|
10
|
+
expiry;
|
|
11
|
+
failures;
|
|
12
|
+
cursor;
|
|
13
|
+
retry;
|
|
14
|
+
constructor(properties) {
|
|
15
|
+
const { mapping, background } = properties;
|
|
16
|
+
this.failures = new SimLambdaStreamFailureDestination(mapping);
|
|
17
|
+
// A stream mapping is refused at creation unless it names a starting
|
|
18
|
+
// position, so one that has got this far without one is the simulator
|
|
19
|
+
// having gone wrong rather than a request having been wrong.
|
|
20
|
+
const { start, streamRetryLimits } = mapping;
|
|
21
|
+
assertDefined(start, "stream mapping start position");
|
|
22
|
+
assertDefined(streamRetryLimits, "stream mapping retry limits");
|
|
23
|
+
this.cursor = new SimLambdaStreamCursor(start, new PollSchedule(properties));
|
|
24
|
+
this.retry = new SimLambdaStreamRetry(streamRetryLimits, background);
|
|
25
|
+
this.expiry = new SimLambdaStreamExpiry({
|
|
26
|
+
age: new SimLambdaStreamBatchAge(streamRetryLimits, background),
|
|
27
|
+
cursor: this.cursor,
|
|
28
|
+
failures: this.failures,
|
|
29
|
+
handled: (batch) => {
|
|
30
|
+
this.handled(batch);
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
handled(batch) {
|
|
35
|
+
this.failures.reset();
|
|
36
|
+
this.retry.reset();
|
|
37
|
+
this.cursor.handled(batch);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -1,27 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
1
|
+
import { type SimLambdaStreamProgressProperties } from "./sim-lambda-stream-progress-state.js";
|
|
2
|
+
import type { SimLambdaStreamRecordTime } from "./sim-lambda-stream-record-times.js";
|
|
3
|
+
import type { SimLambdaFunction } from "../../function/sim-lambda-function.js";
|
|
3
4
|
import type { SimLambdaEventSourceStreamPosition, SimLambdaEventSourceStreamProgressBatch } from "../stream/sim-lambda-event-source-streams.js";
|
|
4
5
|
import type { SimLambdaStreamBatchOutcome } from "./sim-lambda-stream-batch-outcome.js";
|
|
5
|
-
|
|
6
|
-
readonly mapping: SimLambdaEventSourceMapping;
|
|
7
|
-
readonly background: BackgroundScheduler;
|
|
8
|
-
readonly poll: BackgroundTask;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* How far along its stream one mapping has got, and what it does next.
|
|
12
|
-
*
|
|
13
|
-
* The two belong together because they are the same decision. A batch the
|
|
14
|
-
* function took moves the checkpoint and the mapping reads on; a batch it threw
|
|
15
|
-
* on leaves the checkpoint where it is and the mapping tries the same records
|
|
16
|
-
* again after a wait; a batch it reported failing partway through moves the
|
|
17
|
-
* checkpoint back to the record it named. Nothing behind a failing batch is
|
|
18
|
-
* read until it is through, which is what it means for a stream mapping to
|
|
19
|
-
* block its shard.
|
|
20
|
-
*/
|
|
6
|
+
/** Apply a stream batch outcome to its checkpoint and retry schedule. */
|
|
21
7
|
export declare class SimLambdaStreamProgress {
|
|
22
|
-
private readonly
|
|
23
|
-
private readonly retry;
|
|
24
|
-
private readonly schedule;
|
|
8
|
+
private readonly state;
|
|
25
9
|
constructor(properties: SimLambdaStreamProgressProperties);
|
|
26
10
|
/**
|
|
27
11
|
* Poll as soon as the simulation gets to it.
|
|
@@ -39,10 +23,11 @@ export declare class SimLambdaStreamProgress {
|
|
|
39
23
|
* than working out a new one each time.
|
|
40
24
|
*/
|
|
41
25
|
caughtUp(batch: SimLambdaEventSourceStreamProgressBatch): void;
|
|
26
|
+
before(records: readonly SimLambdaStreamRecordTime[], batch: SimLambdaEventSourceStreamProgressBatch, simFunction: SimLambdaFunction): Promise<number>;
|
|
42
27
|
/**
|
|
43
28
|
* Take what became of a batch: move past it, or wait and try it again.
|
|
44
29
|
*/
|
|
45
|
-
after(outcome: SimLambdaStreamBatchOutcome, batch: SimLambdaEventSourceStreamProgressBatch): void
|
|
30
|
+
after(outcome: SimLambdaStreamBatchOutcome, batch: SimLambdaEventSourceStreamProgressBatch, simFunction: SimLambdaFunction): Promise<void>;
|
|
46
31
|
/**
|
|
47
32
|
* Move past a batch that is finished with, and read on.
|
|
48
33
|
*
|
|
@@ -50,19 +35,4 @@ export declare class SimLambdaStreamProgress {
|
|
|
50
35
|
* given up on.
|
|
51
36
|
*/
|
|
52
37
|
handled(batch: SimLambdaEventSourceStreamProgressBatch): void;
|
|
53
|
-
/**
|
|
54
|
-
* Wait and hand a failed batch over again, or give up on it.
|
|
55
|
-
*
|
|
56
|
-
* Giving up discards the records and carries on with the stream, which is
|
|
57
|
-
* what AWS does when a stream mapping's error handling runs out. Parking the
|
|
58
|
-
* shard instead would be a simulator invention.
|
|
59
|
-
*
|
|
60
|
-
* A batch runs out either by having had its retries or by every record left
|
|
61
|
-
* in it being older than the mapping will carry. Records that aged out of the
|
|
62
|
-
* front of a batch are left behind, and the ones behind them go over again,
|
|
63
|
-
* so a mapping given a record age keeps working through its shard rather than
|
|
64
|
-
* stalling on the oldest record on it.
|
|
65
|
-
*/
|
|
66
|
-
failed(outcome: SimLambdaStreamBatchOutcome, batch: SimLambdaEventSourceStreamProgressBatch): void;
|
|
67
38
|
}
|
|
68
|
-
export {};
|
|
@@ -1,47 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { SimLambdaStreamCheckpoint } from "./sim-lambda-stream-checkpoint.js";
|
|
4
|
-
import { SimLambdaStreamRetry } from "./sim-lambda-stream-retry.js";
|
|
5
|
-
/**
|
|
6
|
-
* How far along its stream one mapping has got, and what it does next.
|
|
7
|
-
*
|
|
8
|
-
* The two belong together because they are the same decision. A batch the
|
|
9
|
-
* function took moves the checkpoint and the mapping reads on; a batch it threw
|
|
10
|
-
* on leaves the checkpoint where it is and the mapping tries the same records
|
|
11
|
-
* again after a wait; a batch it reported failing partway through moves the
|
|
12
|
-
* checkpoint back to the record it named. Nothing behind a failing batch is
|
|
13
|
-
* read until it is through, which is what it means for a stream mapping to
|
|
14
|
-
* block its shard.
|
|
15
|
-
*/
|
|
1
|
+
import { SimLambdaStreamProgressState, } from "./sim-lambda-stream-progress-state.js";
|
|
2
|
+
/** Apply a stream batch outcome to its checkpoint and retry schedule. */
|
|
16
3
|
export class SimLambdaStreamProgress {
|
|
17
|
-
|
|
18
|
-
retry;
|
|
19
|
-
schedule;
|
|
4
|
+
state;
|
|
20
5
|
constructor(properties) {
|
|
21
|
-
|
|
22
|
-
// position, so one that has got this far without one is the simulator
|
|
23
|
-
// having gone wrong rather than a request having been wrong.
|
|
24
|
-
const { start, streamRetryLimits } = properties.mapping;
|
|
25
|
-
assertDefined(start, "stream mapping start position");
|
|
26
|
-
assertDefined(streamRetryLimits, "stream mapping retry limits");
|
|
27
|
-
this.checkpoint = new SimLambdaStreamCheckpoint(start);
|
|
28
|
-
this.retry = new SimLambdaStreamRetry({
|
|
29
|
-
limits: streamRetryLimits,
|
|
30
|
-
clock: properties.background,
|
|
31
|
-
});
|
|
32
|
-
this.schedule = new PollSchedule(properties);
|
|
6
|
+
this.state = new SimLambdaStreamProgressState(properties);
|
|
33
7
|
}
|
|
34
8
|
/**
|
|
35
9
|
* Poll as soon as the simulation gets to it.
|
|
36
10
|
*/
|
|
37
11
|
pollNow() {
|
|
38
|
-
this.
|
|
12
|
+
this.state.cursor.pollNow();
|
|
39
13
|
}
|
|
40
14
|
/**
|
|
41
15
|
* Where the next read starts.
|
|
42
16
|
*/
|
|
43
17
|
get position() {
|
|
44
|
-
return this.
|
|
18
|
+
return this.state.cursor.position;
|
|
45
19
|
}
|
|
46
20
|
/**
|
|
47
21
|
* Take a read that came back with nothing.
|
|
@@ -51,17 +25,29 @@ export class SimLambdaStreamProgress {
|
|
|
51
25
|
* than working out a new one each time.
|
|
52
26
|
*/
|
|
53
27
|
caughtUp(batch) {
|
|
54
|
-
this.
|
|
28
|
+
this.state.cursor.advanceTo(batch.next);
|
|
29
|
+
}
|
|
30
|
+
async before(records, batch, simFunction) {
|
|
31
|
+
return await this.state.expiry.before(records, batch, simFunction);
|
|
55
32
|
}
|
|
56
33
|
/**
|
|
57
34
|
* Take what became of a batch: move past it, or wait and try it again.
|
|
58
35
|
*/
|
|
59
|
-
after(outcome, batch) {
|
|
36
|
+
async after(outcome, batch, simFunction) {
|
|
37
|
+
this.state.failures.invoked(outcome);
|
|
60
38
|
if (outcome.isHandled) {
|
|
61
39
|
this.handled(batch);
|
|
62
40
|
return;
|
|
63
41
|
}
|
|
64
|
-
this.
|
|
42
|
+
const again = this.state.retry.after(outcome, this.position);
|
|
43
|
+
const notification = this.state.failures.deliver(this.state.retry.discarded, batch, simFunction);
|
|
44
|
+
if (again === undefined) {
|
|
45
|
+
this.handled(batch);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
this.state.cursor.resume(again);
|
|
49
|
+
}
|
|
50
|
+
await notification;
|
|
65
51
|
}
|
|
66
52
|
/**
|
|
67
53
|
* Move past a batch that is finished with, and read on.
|
|
@@ -70,32 +56,6 @@ export class SimLambdaStreamProgress {
|
|
|
70
56
|
* given up on.
|
|
71
57
|
*/
|
|
72
58
|
handled(batch) {
|
|
73
|
-
this.
|
|
74
|
-
this.checkpoint.advanceTo(batch.next);
|
|
75
|
-
if (!batch.drained) {
|
|
76
|
-
this.schedule.now();
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Wait and hand a failed batch over again, or give up on it.
|
|
81
|
-
*
|
|
82
|
-
* Giving up discards the records and carries on with the stream, which is
|
|
83
|
-
* what AWS does when a stream mapping's error handling runs out. Parking the
|
|
84
|
-
* shard instead would be a simulator invention.
|
|
85
|
-
*
|
|
86
|
-
* A batch runs out either by having had its retries or by every record left
|
|
87
|
-
* in it being older than the mapping will carry. Records that aged out of the
|
|
88
|
-
* front of a batch are left behind, and the ones behind them go over again,
|
|
89
|
-
* so a mapping given a record age keeps working through its shard rather than
|
|
90
|
-
* stalling on the oldest record on it.
|
|
91
|
-
*/
|
|
92
|
-
failed(outcome, batch) {
|
|
93
|
-
const again = this.retry.after(outcome, this.checkpoint.position);
|
|
94
|
-
if (again === undefined) {
|
|
95
|
-
this.handled(batch);
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
this.checkpoint.advanceTo(again.position);
|
|
99
|
-
this.schedule.afterSeconds(again.afterSeconds);
|
|
59
|
+
this.state.handled(batch);
|
|
100
60
|
}
|
|
101
61
|
}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
+
import type { SimLambdaStreamDiscard } from "./sim-lambda-stream-failure-record.js";
|
|
1
2
|
import type { SimClock } from "../../../../util/clock/sim-clock.js";
|
|
2
3
|
import type { SimLambdaStreamRetryLimits } from "../sim-lambda-stream-retry-limits.js";
|
|
3
4
|
import type { SimLambdaEventSourceStreamPosition } from "../stream/sim-lambda-event-source-streams.js";
|
|
4
5
|
import type { SimLambdaStreamBatchOutcome } from "./sim-lambda-stream-batch-outcome.js";
|
|
5
|
-
interface SimLambdaStreamRetryProperties {
|
|
6
|
-
readonly limits: SimLambdaStreamRetryLimits;
|
|
7
|
-
readonly clock: SimClock;
|
|
8
|
-
}
|
|
9
6
|
/**
|
|
10
7
|
* One more delivery of a failed batch: where it starts, and how long the
|
|
11
8
|
* mapping waits first.
|
|
@@ -14,26 +11,12 @@ export interface SimLambdaStreamRetryAttempt {
|
|
|
14
11
|
readonly position: SimLambdaEventSourceStreamPosition;
|
|
15
12
|
readonly afterSeconds: number;
|
|
16
13
|
}
|
|
17
|
-
/**
|
|
18
|
-
* Whether a failed batch is handed over again, and from which record.
|
|
19
|
-
*
|
|
20
|
-
* Both of Lambda's failed-batch limits end here because they end the same
|
|
21
|
-
* thing. A batch stops being delivered when it has had its retries, and a
|
|
22
|
-
* record stops being delivered when it is older than the mapping will carry;
|
|
23
|
-
* whichever comes first, what is left of the batch is discarded and the mapping
|
|
24
|
-
* reads on. Records age out of the front of a batch, because a batch is in
|
|
25
|
-
* stream order, so the answer is one record to start from rather than a list of
|
|
26
|
-
* survivors.
|
|
27
|
-
*
|
|
28
|
-
* Ages are read at the instant the next delivery falls due rather than now,
|
|
29
|
-
* because the wait before it is what ages the records out. A record judged now
|
|
30
|
-
* would be handed to the function and only then found to be too old.
|
|
31
|
-
*/
|
|
14
|
+
/** Tracks the retry quota and checkpoint of one failed stream batch. */
|
|
32
15
|
export declare class SimLambdaStreamRetry {
|
|
33
|
-
|
|
16
|
+
discarded: SimLambdaStreamDiscard | undefined;
|
|
34
17
|
private readonly clock;
|
|
35
18
|
private readonly backoff;
|
|
36
|
-
constructor(
|
|
19
|
+
constructor(limits: SimLambdaStreamRetryLimits, clock: SimClock);
|
|
37
20
|
/**
|
|
38
21
|
* The next delivery of a failed batch, or nothing when the batch is finished
|
|
39
22
|
* with.
|
|
@@ -43,22 +26,4 @@ export declare class SimLambdaStreamRetry {
|
|
|
43
26
|
* Start counting again, which a batch that is finished with does.
|
|
44
27
|
*/
|
|
45
28
|
reset(): void;
|
|
46
|
-
/**
|
|
47
|
-
* Where the next delivery starts, or nothing when every record left in the
|
|
48
|
-
* batch has aged out.
|
|
49
|
-
*/
|
|
50
|
-
private retryPosition;
|
|
51
|
-
/**
|
|
52
|
-
* The first record from `from` on that is still young enough to deliver.
|
|
53
|
-
*/
|
|
54
|
-
private firstLiveIndex;
|
|
55
|
-
/**
|
|
56
|
-
* When the next delivery falls due, which is what a record's age is judged
|
|
57
|
-
* against.
|
|
58
|
-
*
|
|
59
|
-
* Judging it now would hand a record over and only then notice that it was
|
|
60
|
-
* too old to be worth handing over, since the wait is what makes it too old.
|
|
61
|
-
*/
|
|
62
|
-
private deliveryAt;
|
|
63
29
|
}
|
|
64
|
-
export {};
|
|
@@ -1,44 +1,29 @@
|
|
|
1
1
|
import { SimLambdaStreamRetryBackoff } from "./sim-lambda-stream-retry-backoff.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Whether a failed batch is handed over again, and from which record.
|
|
5
|
-
*
|
|
6
|
-
* Both of Lambda's failed-batch limits end here because they end the same
|
|
7
|
-
* thing. A batch stops being delivered when it has had its retries, and a
|
|
8
|
-
* record stops being delivered when it is older than the mapping will carry;
|
|
9
|
-
* whichever comes first, what is left of the batch is discarded and the mapping
|
|
10
|
-
* reads on. Records age out of the front of a batch, because a batch is in
|
|
11
|
-
* stream order, so the answer is one record to start from rather than a list of
|
|
12
|
-
* survivors.
|
|
13
|
-
*
|
|
14
|
-
* Ages are read at the instant the next delivery falls due rather than now,
|
|
15
|
-
* because the wait before it is what ages the records out. A record judged now
|
|
16
|
-
* would be handed to the function and only then found to be too old.
|
|
17
|
-
*/
|
|
2
|
+
/** Tracks the retry quota and checkpoint of one failed stream batch. */
|
|
18
3
|
export class SimLambdaStreamRetry {
|
|
19
|
-
|
|
4
|
+
discarded;
|
|
20
5
|
clock;
|
|
21
6
|
backoff;
|
|
22
|
-
constructor(
|
|
23
|
-
this.
|
|
24
|
-
this.
|
|
25
|
-
this.backoff = new SimLambdaStreamRetryBackoff(properties.limits.attemptLimit);
|
|
7
|
+
constructor(limits, clock) {
|
|
8
|
+
this.clock = clock;
|
|
9
|
+
this.backoff = new SimLambdaStreamRetryBackoff(limits.attemptLimit);
|
|
26
10
|
}
|
|
27
11
|
/**
|
|
28
12
|
* The next delivery of a failed batch, or nothing when the batch is finished
|
|
29
13
|
* with.
|
|
30
14
|
*/
|
|
31
15
|
after(outcome, readFrom) {
|
|
16
|
+
this.discarded = undefined;
|
|
32
17
|
if (this.backoff.isExhausted) {
|
|
18
|
+
this.discarded = {
|
|
19
|
+
records: outcome.records.slice(outcome.retryIndex),
|
|
20
|
+
condition: "RetryAttemptsExhausted",
|
|
21
|
+
at: this.clock.now(),
|
|
22
|
+
};
|
|
33
23
|
return undefined;
|
|
34
24
|
}
|
|
35
25
|
const afterSeconds = this.backoff.nextSeconds();
|
|
36
|
-
|
|
37
|
-
const position = this.retryPosition(outcome, readFrom, deliveryAt);
|
|
38
|
-
if (position === undefined) {
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
return { position, afterSeconds };
|
|
26
|
+
return { position: outcome.retryPosition(readFrom), afterSeconds };
|
|
42
27
|
}
|
|
43
28
|
/**
|
|
44
29
|
* Start counting again, which a batch that is finished with does.
|
|
@@ -46,41 +31,4 @@ export class SimLambdaStreamRetry {
|
|
|
46
31
|
reset() {
|
|
47
32
|
this.backoff.reset();
|
|
48
33
|
}
|
|
49
|
-
/**
|
|
50
|
-
* Where the next delivery starts, or nothing when every record left in the
|
|
51
|
-
* batch has aged out.
|
|
52
|
-
*/
|
|
53
|
-
retryPosition(outcome, readFrom, deliveryAt) {
|
|
54
|
-
const { records, retryIndex } = outcome;
|
|
55
|
-
const live = this.firstLiveIndex(records, retryIndex, deliveryAt);
|
|
56
|
-
if (live === undefined) {
|
|
57
|
-
return undefined;
|
|
58
|
-
}
|
|
59
|
-
const sequenceNumber = records.at(live)?.sequenceNumber;
|
|
60
|
-
// Nothing aged out of the front, so the batch goes back exactly where the
|
|
61
|
-
// report left it. A record that aged out but cannot be named leaves the
|
|
62
|
-
// batch where it was too, since skipping past a record the mapping cannot
|
|
63
|
-
// name would skip the records after it as well.
|
|
64
|
-
if (live === retryIndex || sequenceNumber === undefined) {
|
|
65
|
-
return outcome.retryPosition(readFrom);
|
|
66
|
-
}
|
|
67
|
-
return { kind: "sequence", sequenceNumber };
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* The first record from `from` on that is still young enough to deliver.
|
|
71
|
-
*/
|
|
72
|
-
firstLiveIndex(records, from, deliveryAt) {
|
|
73
|
-
const index = records.findIndex((record, position) => position >= from && !this.limits.hasAgedOut(record.at, deliveryAt));
|
|
74
|
-
return index === -1 ? undefined : index;
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* When the next delivery falls due, which is what a record's age is judged
|
|
78
|
-
* against.
|
|
79
|
-
*
|
|
80
|
-
* Judging it now would hand a record over and only then notice that it was
|
|
81
|
-
* too old to be worth handing over, since the wait is what makes it too old.
|
|
82
|
-
*/
|
|
83
|
-
deliveryAt(afterSeconds) {
|
|
84
|
-
return new Date(this.clock.now().getTime() + afterSeconds * millisecondsPerSecond);
|
|
85
|
-
}
|
|
86
34
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { SimLambdaStreamDestinationConfiguration } from "./sim-lambda-stream-destination-config.js";
|
|
2
|
+
import type { SimLambdaDestinationTargets } from "../destination/sim-lambda-destination-targets.js";
|
|
1
3
|
import type { SimAwsAccountRegionScope } from "../../aws/sim-aws-account-region-scope.js";
|
|
2
4
|
import type { SimLambdaFunctionArn } from "../function/sim-lambda-function-configuration.js";
|
|
3
5
|
import type { SimLambdaStreamRetryLimits, SimLambdaStreamRetryLimitsConfiguration } from "./sim-lambda-stream-retry-limits.js";
|
|
@@ -31,6 +33,8 @@ interface SimLambdaEventSourceMappingProperties {
|
|
|
31
33
|
readonly enabled?: boolean | undefined;
|
|
32
34
|
readonly functionResponseTypes?: readonly SimLambdaFunctionResponseType[] | undefined;
|
|
33
35
|
readonly streamRetryLimits?: SimLambdaStreamRetryLimits | undefined;
|
|
36
|
+
readonly destinationConfig?: SimLambdaStreamDestinationConfiguration | undefined;
|
|
37
|
+
readonly destinations?: SimLambdaDestinationTargets | undefined;
|
|
34
38
|
readonly createdAt: Date;
|
|
35
39
|
}
|
|
36
40
|
/**
|
|
@@ -39,6 +43,7 @@ interface SimLambdaEventSourceMappingProperties {
|
|
|
39
43
|
* DeleteEventSourceMapping responses all report it.
|
|
40
44
|
*/
|
|
41
45
|
export interface SimLambdaEventSourceMappingConfiguration extends Partial<SimLambdaStreamRetryLimitsConfiguration> {
|
|
46
|
+
readonly DestinationConfig?: SimLambdaStreamDestinationConfiguration | undefined;
|
|
42
47
|
readonly UUID: string;
|
|
43
48
|
readonly EventSourceMappingArn: SimLambdaEventSourceMappingArn;
|
|
44
49
|
readonly EventSourceArn: string;
|
|
@@ -64,6 +69,7 @@ export interface SimLambdaEventSourceMappingConfiguration extends Partial<SimLam
|
|
|
64
69
|
*/
|
|
65
70
|
export declare class SimLambdaEventSourceMapping {
|
|
66
71
|
#private;
|
|
72
|
+
readonly destinations: SimLambdaDestinationTargets;
|
|
67
73
|
readonly uuid: string;
|
|
68
74
|
readonly eventSourceArn: string;
|
|
69
75
|
readonly functionName: string;
|
|
@@ -91,13 +97,14 @@ export declare class SimLambdaEventSourceMapping {
|
|
|
91
97
|
* the queue's own problem once the batch comes back.
|
|
92
98
|
*/
|
|
93
99
|
readonly streamRetryLimits: SimLambdaStreamRetryLimits | undefined;
|
|
100
|
+
private readonly destinationConfig;
|
|
94
101
|
private readonly accountRegionScope;
|
|
95
102
|
private readonly enabled;
|
|
96
103
|
private readonly lastModified;
|
|
97
104
|
constructor(properties: SimLambdaEventSourceMappingProperties);
|
|
98
|
-
/**
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
/** The configured failure queue or topic ARN. */
|
|
106
|
+
get failureDestinationArn(): string | undefined;
|
|
107
|
+
/** The ARN naming this mapping. */
|
|
101
108
|
get arn(): SimLambdaEventSourceMappingArn;
|
|
102
109
|
/**
|
|
103
110
|
* The state this mapping is in.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SimLambdaNoDestinationTargets } from "../destination/sim-lambda-destination-targets.js";
|
|
1
2
|
import { randomUUID } from "node:crypto";
|
|
2
3
|
/**
|
|
3
4
|
* One simulated Lambda event source mapping, from an event source to a
|
|
@@ -10,6 +11,7 @@ import { randomUUID } from "node:crypto";
|
|
|
10
11
|
* it.
|
|
11
12
|
*/
|
|
12
13
|
export class SimLambdaEventSourceMapping {
|
|
14
|
+
destinations;
|
|
13
15
|
uuid = randomUUID();
|
|
14
16
|
eventSourceArn;
|
|
15
17
|
functionName;
|
|
@@ -37,11 +39,15 @@ export class SimLambdaEventSourceMapping {
|
|
|
37
39
|
* the queue's own problem once the batch comes back.
|
|
38
40
|
*/
|
|
39
41
|
streamRetryLimits;
|
|
42
|
+
destinationConfig;
|
|
40
43
|
accountRegionScope;
|
|
41
44
|
enabled;
|
|
42
45
|
lastModified;
|
|
43
46
|
#state = "Creating";
|
|
44
47
|
constructor(properties) {
|
|
48
|
+
this.destinationConfig = properties.destinationConfig;
|
|
49
|
+
this.destinations =
|
|
50
|
+
properties.destinations ?? new SimLambdaNoDestinationTargets();
|
|
45
51
|
this.accountRegionScope = properties.accountRegionScope;
|
|
46
52
|
this.eventSourceArn = properties.eventSourceArn;
|
|
47
53
|
this.functionName = properties.functionName;
|
|
@@ -56,9 +62,11 @@ export class SimLambdaEventSourceMapping {
|
|
|
56
62
|
this.enabled = properties.enabled ?? true;
|
|
57
63
|
this.lastModified = properties.createdAt;
|
|
58
64
|
}
|
|
59
|
-
/**
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
/** The configured failure queue or topic ARN. */
|
|
66
|
+
get failureDestinationArn() {
|
|
67
|
+
return this.destinationConfig?.OnFailure?.Destination;
|
|
68
|
+
}
|
|
69
|
+
/** The ARN naming this mapping. */
|
|
62
70
|
get arn() {
|
|
63
71
|
const { regionName, accountId } = this.accountRegionScope;
|
|
64
72
|
return `arn:aws:lambda:${regionName}:${accountId}:event-source-mapping:${this.uuid}`;
|
|
@@ -96,6 +104,7 @@ export class SimLambdaEventSourceMapping {
|
|
|
96
104
|
*/
|
|
97
105
|
configuration() {
|
|
98
106
|
return {
|
|
107
|
+
DestinationConfig: structuredClone(this.destinationConfig),
|
|
99
108
|
UUID: this.uuid,
|
|
100
109
|
EventSourceMappingArn: this.arn,
|
|
101
110
|
EventSourceArn: this.eventSourceArn,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface SimLambdaStreamDestinationConfiguration {
|
|
2
|
+
readonly OnFailure?: {
|
|
3
|
+
readonly Destination?: string | undefined;
|
|
4
|
+
} | undefined;
|
|
5
|
+
}
|
|
6
|
+
/** Validate the same destination shape for SDK and CloudFormation requests. */
|
|
7
|
+
export declare function simLambdaStreamDestinationConfig(value: unknown, sourceKind?: string): SimLambdaStreamDestinationConfiguration | undefined;
|