@griffin-app/griffin-executor 0.1.0
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 +152 -0
- package/dist/adapters/axios.d.ts +5 -0
- package/dist/adapters/axios.d.ts.map +1 -0
- package/dist/adapters/axios.js +36 -0
- package/dist/adapters/axios.js.map +1 -0
- package/dist/adapters/index.d.ts +3 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +3 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/stub.d.ts +22 -0
- package/dist/adapters/stub.d.ts.map +1 -0
- package/dist/adapters/stub.js +36 -0
- package/dist/adapters/stub.js.map +1 -0
- package/dist/events/adapters/in-memory.d.ts +52 -0
- package/dist/events/adapters/in-memory.d.ts.map +1 -0
- package/dist/events/adapters/in-memory.js +70 -0
- package/dist/events/adapters/in-memory.js.map +1 -0
- package/dist/events/adapters/in-memory.test.d.ts +2 -0
- package/dist/events/adapters/in-memory.test.d.ts.map +1 -0
- package/dist/events/adapters/in-memory.test.js +109 -0
- package/dist/events/adapters/in-memory.test.js.map +1 -0
- package/dist/events/adapters/index.d.ts +9 -0
- package/dist/events/adapters/index.d.ts.map +1 -0
- package/dist/events/adapters/index.js +9 -0
- package/dist/events/adapters/index.js.map +1 -0
- package/dist/events/adapters/kinesis.d.ts +91 -0
- package/dist/events/adapters/kinesis.d.ts.map +1 -0
- package/dist/events/adapters/kinesis.js +136 -0
- package/dist/events/adapters/kinesis.js.map +1 -0
- package/dist/events/adapters/kinesis.test.d.ts +2 -0
- package/dist/events/adapters/kinesis.test.d.ts.map +1 -0
- package/dist/events/adapters/kinesis.test.js +249 -0
- package/dist/events/adapters/kinesis.test.js.map +1 -0
- package/dist/events/emitter.d.ts +68 -0
- package/dist/events/emitter.d.ts.map +1 -0
- package/dist/events/emitter.js +83 -0
- package/dist/events/emitter.js.map +1 -0
- package/dist/events/emitter.test.d.ts +2 -0
- package/dist/events/emitter.test.d.ts.map +1 -0
- package/dist/events/emitter.test.js +262 -0
- package/dist/events/emitter.test.js.map +1 -0
- package/dist/events/index.d.ts +4 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +4 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/types.d.ts +112 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/events/types.js +9 -0
- package/dist/events/types.js.map +1 -0
- package/dist/executor.d.ts +4 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +799 -0
- package/dist/executor.js.map +1 -0
- package/dist/executor.test.d.ts +2 -0
- package/dist/executor.test.d.ts.map +1 -0
- package/dist/executor.test.js +1584 -0
- package/dist/executor.test.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/secrets/factory.d.ts +121 -0
- package/dist/secrets/factory.d.ts.map +1 -0
- package/dist/secrets/factory.js +137 -0
- package/dist/secrets/factory.js.map +1 -0
- package/dist/secrets/index.d.ts +14 -0
- package/dist/secrets/index.d.ts.map +1 -0
- package/dist/secrets/index.js +18 -0
- package/dist/secrets/index.js.map +1 -0
- package/dist/secrets/providers/aws.d.ts +63 -0
- package/dist/secrets/providers/aws.d.ts.map +1 -0
- package/dist/secrets/providers/aws.js +110 -0
- package/dist/secrets/providers/aws.js.map +1 -0
- package/dist/secrets/providers/env.d.ts +36 -0
- package/dist/secrets/providers/env.d.ts.map +1 -0
- package/dist/secrets/providers/env.js +37 -0
- package/dist/secrets/providers/env.js.map +1 -0
- package/dist/secrets/providers/index.d.ts +7 -0
- package/dist/secrets/providers/index.d.ts.map +1 -0
- package/dist/secrets/providers/index.js +7 -0
- package/dist/secrets/providers/index.js.map +1 -0
- package/dist/secrets/providers/vault.d.ts +75 -0
- package/dist/secrets/providers/vault.d.ts.map +1 -0
- package/dist/secrets/providers/vault.js +143 -0
- package/dist/secrets/providers/vault.js.map +1 -0
- package/dist/secrets/registry.d.ts +39 -0
- package/dist/secrets/registry.d.ts.map +1 -0
- package/dist/secrets/registry.js +134 -0
- package/dist/secrets/registry.js.map +1 -0
- package/dist/secrets/resolver.d.ts +45 -0
- package/dist/secrets/resolver.d.ts.map +1 -0
- package/dist/secrets/resolver.js +188 -0
- package/dist/secrets/resolver.js.map +1 -0
- package/dist/secrets/secrets.test.d.ts +2 -0
- package/dist/secrets/secrets.test.d.ts.map +1 -0
- package/dist/secrets/secrets.test.js +317 -0
- package/dist/secrets/secrets.test.js.map +1 -0
- package/dist/secrets/types.d.ts +70 -0
- package/dist/secrets/types.d.ts.map +1 -0
- package/dist/secrets/types.js +42 -0
- package/dist/secrets/types.js.map +1 -0
- package/dist/shared.d.ts +8 -0
- package/dist/shared.d.ts.map +1 -0
- package/dist/shared.js +30 -0
- package/dist/shared.js.map +1 -0
- package/dist/test-monitor-types.d.ts +43 -0
- package/dist/test-monitor-types.d.ts.map +1 -0
- package/dist/test-monitor-types.js +2 -0
- package/dist/test-monitor-types.js.map +1 -0
- package/dist/test-plan-types.d.ts +43 -0
- package/dist/test-plan-types.d.ts.map +1 -0
- package/dist/test-plan-types.js +2 -0
- package/dist/test-plan-types.js.map +1 -0
- package/dist/types.d.ts +93 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/dates.d.ts +11 -0
- package/dist/utils/dates.d.ts.map +1 -0
- package/dist/utils/dates.js +13 -0
- package/dist/utils/dates.js.map +1 -0
- package/package.json +39 -0
- package/src/adapters/axios.ts +39 -0
- package/src/adapters/index.ts +2 -0
- package/src/adapters/stub.ts +47 -0
- package/src/events/adapters/README.md +144 -0
- package/src/events/adapters/in-memory.test.ts +146 -0
- package/src/events/adapters/in-memory.ts +93 -0
- package/src/events/adapters/index.ts +9 -0
- package/src/events/adapters/kinesis.test.ts +323 -0
- package/src/events/adapters/kinesis.ts +211 -0
- package/src/events/emitter.test.ts +327 -0
- package/src/events/emitter.ts +133 -0
- package/src/events/index.ts +3 -0
- package/src/events/types.ts +136 -0
- package/src/executor.test.ts +1732 -0
- package/src/executor.ts +1075 -0
- package/src/index.ts +81 -0
- package/src/secrets/factory.ts +248 -0
- package/src/secrets/index.ts +48 -0
- package/src/secrets/providers/aws.ts +178 -0
- package/src/secrets/providers/env.ts +66 -0
- package/src/secrets/providers/index.ts +15 -0
- package/src/secrets/providers/vault.ts +257 -0
- package/src/secrets/resolver.ts +269 -0
- package/src/secrets/secrets.test.ts +402 -0
- package/src/secrets/types.ts +106 -0
- package/src/shared.ts +46 -0
- package/src/test-monitor-types.ts +49 -0
- package/src/types.ts +114 -0
- package/src/utils/dates.ts +13 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +14 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS Kinesis event bus adapter.
|
|
3
|
+
*
|
|
4
|
+
* Publishes execution events to an AWS Kinesis stream.
|
|
5
|
+
* Supports batching, retries, and automatic partitioning by executionId.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Automatic batching (max 500 records per PutRecords call)
|
|
9
|
+
* - Retry logic for failed records with exponential backoff
|
|
10
|
+
* - Partition key strategies for optimal shard distribution
|
|
11
|
+
* - EventType included in data for Lambda filtering
|
|
12
|
+
*/
|
|
13
|
+
import type { DurableEventBusAdapter } from "../emitter.js";
|
|
14
|
+
import type { ExecutionEvent } from "../types.js";
|
|
15
|
+
import { KinesisClient } from "@aws-sdk/client-kinesis";
|
|
16
|
+
export interface KinesisAdapterOptions {
|
|
17
|
+
/**
|
|
18
|
+
* AWS Kinesis client instance.
|
|
19
|
+
* Should be pre-configured with region and credentials.
|
|
20
|
+
*
|
|
21
|
+
* Example:
|
|
22
|
+
* import { KinesisClient } from "@aws-sdk/client-kinesis";
|
|
23
|
+
* const client = new KinesisClient({ region: "us-east-1" });
|
|
24
|
+
*/
|
|
25
|
+
client: KinesisClient;
|
|
26
|
+
/**
|
|
27
|
+
* Name of the Kinesis stream to publish to.
|
|
28
|
+
*/
|
|
29
|
+
streamName: string;
|
|
30
|
+
/**
|
|
31
|
+
* Maximum number of retries for failed records.
|
|
32
|
+
* Default: 3
|
|
33
|
+
*/
|
|
34
|
+
maxRetries?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Milliseconds to wait between retries.
|
|
37
|
+
* Default: 1000
|
|
38
|
+
*/
|
|
39
|
+
retryDelayMs?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Partition key strategy.
|
|
42
|
+
* - "executionId": Use executionId as partition key (maintains ordering within execution)
|
|
43
|
+
* - "organizationId": Use organizationId as partition key (better shard distribution for multi-tenant)
|
|
44
|
+
* - "composite": Use "orgId:executionId" for balanced distribution with execution ordering
|
|
45
|
+
* Default: "executionId"
|
|
46
|
+
*/
|
|
47
|
+
partitionKeyStrategy?: "executionId" | "organizationId" | "composite";
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* AWS Kinesis adapter for durable event publishing.
|
|
51
|
+
*
|
|
52
|
+
* Batches events and publishes them to a Kinesis stream.
|
|
53
|
+
* Implements retry logic for failed records and respects Kinesis batch limits.
|
|
54
|
+
*/
|
|
55
|
+
export declare class KinesisAdapter implements DurableEventBusAdapter {
|
|
56
|
+
private readonly client;
|
|
57
|
+
private readonly streamName;
|
|
58
|
+
private readonly maxRetries;
|
|
59
|
+
private readonly retryDelayMs;
|
|
60
|
+
private readonly partitionKeyStrategy;
|
|
61
|
+
private static readonly KINESIS_MAX_BATCH;
|
|
62
|
+
constructor(options: KinesisAdapterOptions);
|
|
63
|
+
publish(events: ExecutionEvent[]): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Publish a single batch with retry logic for failed records.
|
|
66
|
+
*/
|
|
67
|
+
private publishBatchWithRetry;
|
|
68
|
+
/**
|
|
69
|
+
* Convert ExecutionEvent to Kinesis record format.
|
|
70
|
+
*/
|
|
71
|
+
private eventToRecord;
|
|
72
|
+
/**
|
|
73
|
+
* Get partition key based on configured strategy.
|
|
74
|
+
*/
|
|
75
|
+
private getPartitionKey;
|
|
76
|
+
/**
|
|
77
|
+
* Serialize event to JSON and encode as UTF-8 bytes.
|
|
78
|
+
* Events are structured to enable efficient Lambda filtering by including
|
|
79
|
+
* type as a top-level field in the JSON payload.
|
|
80
|
+
*/
|
|
81
|
+
private serializeEvent;
|
|
82
|
+
/**
|
|
83
|
+
* Split array into chunks of specified size.
|
|
84
|
+
*/
|
|
85
|
+
private chunkArray;
|
|
86
|
+
/**
|
|
87
|
+
* Sleep for specified milliseconds.
|
|
88
|
+
*/
|
|
89
|
+
private sleep;
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=kinesis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kinesis.d.ts","sourceRoot":"","sources":["../../../src/events/adapters/kinesis.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAEL,aAAa,EAGd,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,qBAAqB;IACpC;;;;;;;OAOG;IACH,MAAM,EAAE,aAAa,CAAC;IAEtB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,aAAa,GAAG,gBAAgB,GAAG,WAAW,CAAC;CACvE;AAED;;;;;GAKG;AACH,qBAAa,cAAe,YAAW,sBAAsB;IAC3D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgB;IACvC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAGrB;IAChB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAO;gBAEpC,OAAO,EAAE,qBAAqB;IAQpC,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBtD;;OAEG;YACW,qBAAqB;IAuDnC;;OAEG;IACH,OAAO,CAAC,aAAa;IAOrB;;OAEG;IACH,OAAO,CAAC,eAAe;IAWvB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAKtB;;OAEG;IACH,OAAO,CAAC,UAAU;IAQlB;;OAEG;IACH,OAAO,CAAC,KAAK;CAGd"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS Kinesis event bus adapter.
|
|
3
|
+
*
|
|
4
|
+
* Publishes execution events to an AWS Kinesis stream.
|
|
5
|
+
* Supports batching, retries, and automatic partitioning by executionId.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Automatic batching (max 500 records per PutRecords call)
|
|
9
|
+
* - Retry logic for failed records with exponential backoff
|
|
10
|
+
* - Partition key strategies for optimal shard distribution
|
|
11
|
+
* - EventType included in data for Lambda filtering
|
|
12
|
+
*/
|
|
13
|
+
import { PutRecordsCommand, } from "@aws-sdk/client-kinesis";
|
|
14
|
+
/**
|
|
15
|
+
* AWS Kinesis adapter for durable event publishing.
|
|
16
|
+
*
|
|
17
|
+
* Batches events and publishes them to a Kinesis stream.
|
|
18
|
+
* Implements retry logic for failed records and respects Kinesis batch limits.
|
|
19
|
+
*/
|
|
20
|
+
export class KinesisAdapter {
|
|
21
|
+
client;
|
|
22
|
+
streamName;
|
|
23
|
+
maxRetries;
|
|
24
|
+
retryDelayMs;
|
|
25
|
+
partitionKeyStrategy;
|
|
26
|
+
static KINESIS_MAX_BATCH = 500;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.client = options.client;
|
|
29
|
+
this.streamName = options.streamName;
|
|
30
|
+
this.maxRetries = options.maxRetries ?? 3;
|
|
31
|
+
this.retryDelayMs = options.retryDelayMs ?? 1000;
|
|
32
|
+
this.partitionKeyStrategy = options.partitionKeyStrategy ?? "executionId";
|
|
33
|
+
}
|
|
34
|
+
async publish(events) {
|
|
35
|
+
if (events.length === 0) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
// Convert events to Kinesis records
|
|
39
|
+
const records = events.map((event) => this.eventToRecord(event));
|
|
40
|
+
// Process in batches of 500 (Kinesis limit)
|
|
41
|
+
const batches = this.chunkArray(records, KinesisAdapter.KINESIS_MAX_BATCH);
|
|
42
|
+
for (const batch of batches) {
|
|
43
|
+
await this.publishBatchWithRetry(batch);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Publish a single batch with retry logic for failed records.
|
|
48
|
+
*/
|
|
49
|
+
async publishBatchWithRetry(records, attempt = 0) {
|
|
50
|
+
try {
|
|
51
|
+
const command = new PutRecordsCommand({
|
|
52
|
+
StreamName: this.streamName,
|
|
53
|
+
Records: records,
|
|
54
|
+
});
|
|
55
|
+
const response = await this.client.send(command);
|
|
56
|
+
// Check for failed records
|
|
57
|
+
if (response.FailedRecordCount && response.FailedRecordCount > 0) {
|
|
58
|
+
const failedRecords = records.filter((_, idx) => {
|
|
59
|
+
const result = response.Records?.[idx];
|
|
60
|
+
return result?.ErrorCode !== undefined;
|
|
61
|
+
});
|
|
62
|
+
if (attempt < this.maxRetries) {
|
|
63
|
+
// Exponential backoff
|
|
64
|
+
const delay = this.retryDelayMs * Math.pow(2, attempt);
|
|
65
|
+
console.warn(`Retrying ${failedRecords.length} failed records (attempt ${attempt + 1}/${this.maxRetries}) after ${delay}ms`);
|
|
66
|
+
await this.sleep(delay);
|
|
67
|
+
await this.publishBatchWithRetry(failedRecords, attempt + 1);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
console.error(`Failed to publish ${failedRecords.length} records after ${this.maxRetries} attempts`);
|
|
71
|
+
// Events are lost after max retries - caller should implement dead letter queue if needed
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (attempt < this.maxRetries) {
|
|
77
|
+
const delay = this.retryDelayMs * Math.pow(2, attempt);
|
|
78
|
+
console.warn(`Kinesis publish error, retrying (attempt ${attempt + 1}/${this.maxRetries}) after ${delay}ms:`, error);
|
|
79
|
+
await this.sleep(delay);
|
|
80
|
+
await this.publishBatchWithRetry(records, attempt + 1);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
console.error(`Failed to publish batch after ${this.maxRetries} attempts:`, error);
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Convert ExecutionEvent to Kinesis record format.
|
|
90
|
+
*/
|
|
91
|
+
eventToRecord(event) {
|
|
92
|
+
return {
|
|
93
|
+
Data: this.serializeEvent(event),
|
|
94
|
+
PartitionKey: this.getPartitionKey(event),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Get partition key based on configured strategy.
|
|
99
|
+
*/
|
|
100
|
+
getPartitionKey(event) {
|
|
101
|
+
switch (this.partitionKeyStrategy) {
|
|
102
|
+
case "executionId":
|
|
103
|
+
return event.execution_id;
|
|
104
|
+
case "organizationId":
|
|
105
|
+
return event.organization_id;
|
|
106
|
+
case "composite":
|
|
107
|
+
return `${event.organization_id}:${event.execution_id}`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Serialize event to JSON and encode as UTF-8 bytes.
|
|
112
|
+
* Events are structured to enable efficient Lambda filtering by including
|
|
113
|
+
* type as a top-level field in the JSON payload.
|
|
114
|
+
*/
|
|
115
|
+
serializeEvent(event) {
|
|
116
|
+
const json = JSON.stringify(event);
|
|
117
|
+
return new TextEncoder().encode(json);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Split array into chunks of specified size.
|
|
121
|
+
*/
|
|
122
|
+
chunkArray(array, chunkSize) {
|
|
123
|
+
const chunks = [];
|
|
124
|
+
for (let i = 0; i < array.length; i += chunkSize) {
|
|
125
|
+
chunks.push(array.slice(i, i + chunkSize));
|
|
126
|
+
}
|
|
127
|
+
return chunks;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Sleep for specified milliseconds.
|
|
131
|
+
*/
|
|
132
|
+
sleep(ms) {
|
|
133
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=kinesis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kinesis.js","sourceRoot":"","sources":["../../../src/events/adapters/kinesis.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kinesis.test.d.ts","sourceRoot":"","sources":["../../../src/events/adapters/kinesis.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { KinesisAdapter } from "./kinesis.js";
|
|
3
|
+
describe("KinesisAdapter", () => {
|
|
4
|
+
const createMockEvent = (type, executionId, organizationId, seq) => ({
|
|
5
|
+
type,
|
|
6
|
+
event_id: `event-${seq}`,
|
|
7
|
+
seq,
|
|
8
|
+
timestamp: Date.now(),
|
|
9
|
+
monitor_id: "monitor-1",
|
|
10
|
+
execution_id: executionId,
|
|
11
|
+
organization_id: organizationId,
|
|
12
|
+
monitor_name: "Test Monitor",
|
|
13
|
+
monitor_version: "1.0.0",
|
|
14
|
+
node_count: 1,
|
|
15
|
+
edge_count: 1,
|
|
16
|
+
});
|
|
17
|
+
describe("publish", () => {
|
|
18
|
+
it("should publish events successfully", async () => {
|
|
19
|
+
const mockSend = vi.fn().mockResolvedValue({
|
|
20
|
+
FailedRecordCount: 0,
|
|
21
|
+
Records: [{ SequenceNumber: "seq-1", ShardId: "shard-1" }],
|
|
22
|
+
});
|
|
23
|
+
const mockClient = {
|
|
24
|
+
send: mockSend,
|
|
25
|
+
};
|
|
26
|
+
const adapter = new KinesisAdapter({
|
|
27
|
+
client: mockClient,
|
|
28
|
+
streamName: "test-stream",
|
|
29
|
+
});
|
|
30
|
+
const events = [createMockEvent("MONITOR_START", "exec-1", "org-1", 0)];
|
|
31
|
+
await adapter.publish(events);
|
|
32
|
+
expect(mockSend).toHaveBeenCalledOnce();
|
|
33
|
+
const command = mockSend.mock.calls[0][0];
|
|
34
|
+
expect(command.input.StreamName).toBe("test-stream");
|
|
35
|
+
expect(command.input.Records).toHaveLength(1);
|
|
36
|
+
});
|
|
37
|
+
it("should handle empty event array", async () => {
|
|
38
|
+
const mockSend = vi.fn();
|
|
39
|
+
const mockClient = {
|
|
40
|
+
send: mockSend,
|
|
41
|
+
};
|
|
42
|
+
const adapter = new KinesisAdapter({
|
|
43
|
+
client: mockClient,
|
|
44
|
+
streamName: "test-stream",
|
|
45
|
+
});
|
|
46
|
+
await adapter.publish([]);
|
|
47
|
+
expect(mockSend).not.toHaveBeenCalled();
|
|
48
|
+
});
|
|
49
|
+
it("should batch events into chunks of 500", async () => {
|
|
50
|
+
const mockSend = vi.fn().mockResolvedValue({
|
|
51
|
+
FailedRecordCount: 0,
|
|
52
|
+
Records: [],
|
|
53
|
+
});
|
|
54
|
+
const mockClient = {
|
|
55
|
+
send: mockSend,
|
|
56
|
+
};
|
|
57
|
+
const adapter = new KinesisAdapter({
|
|
58
|
+
client: mockClient,
|
|
59
|
+
streamName: "test-stream",
|
|
60
|
+
});
|
|
61
|
+
// Create 1250 events (should result in 3 batches: 500, 500, 250)
|
|
62
|
+
const events = Array.from({ length: 1250 }, (_, i) => createMockEvent("MONITOR_START", "exec-1", "org-1", i));
|
|
63
|
+
await adapter.publish(events);
|
|
64
|
+
expect(mockSend).toHaveBeenCalledTimes(3);
|
|
65
|
+
expect(mockSend.mock.calls[0][0].input.Records).toHaveLength(500);
|
|
66
|
+
expect(mockSend.mock.calls[1][0].input.Records).toHaveLength(500);
|
|
67
|
+
expect(mockSend.mock.calls[2][0].input.Records).toHaveLength(250);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe("partition key strategies", () => {
|
|
71
|
+
it("should use executionId as partition key by default", async () => {
|
|
72
|
+
const mockSend = vi.fn().mockResolvedValue({
|
|
73
|
+
FailedRecordCount: 0,
|
|
74
|
+
Records: [],
|
|
75
|
+
});
|
|
76
|
+
const mockClient = {
|
|
77
|
+
send: mockSend,
|
|
78
|
+
};
|
|
79
|
+
const adapter = new KinesisAdapter({
|
|
80
|
+
client: mockClient,
|
|
81
|
+
streamName: "test-stream",
|
|
82
|
+
});
|
|
83
|
+
const events = [
|
|
84
|
+
createMockEvent("MONITOR_START", "exec-123", "org-456", 0),
|
|
85
|
+
];
|
|
86
|
+
await adapter.publish(events);
|
|
87
|
+
const records = mockSend.mock.calls[0][0].input.Records;
|
|
88
|
+
expect(records[0].PartitionKey).toBe("exec-123");
|
|
89
|
+
});
|
|
90
|
+
it("should use organizationId as partition key when configured", async () => {
|
|
91
|
+
const mockSend = vi.fn().mockResolvedValue({
|
|
92
|
+
FailedRecordCount: 0,
|
|
93
|
+
Records: [],
|
|
94
|
+
});
|
|
95
|
+
const mockClient = {
|
|
96
|
+
send: mockSend,
|
|
97
|
+
};
|
|
98
|
+
const adapter = new KinesisAdapter({
|
|
99
|
+
client: mockClient,
|
|
100
|
+
streamName: "test-stream",
|
|
101
|
+
partitionKeyStrategy: "organizationId",
|
|
102
|
+
});
|
|
103
|
+
const events = [
|
|
104
|
+
createMockEvent("MONITOR_START", "exec-123", "org-456", 0),
|
|
105
|
+
];
|
|
106
|
+
await adapter.publish(events);
|
|
107
|
+
const records = mockSend.mock.calls[0][0].input.Records;
|
|
108
|
+
expect(records[0].PartitionKey).toBe("org-456");
|
|
109
|
+
});
|
|
110
|
+
it("should use composite partition key when configured", async () => {
|
|
111
|
+
const mockSend = vi.fn().mockResolvedValue({
|
|
112
|
+
FailedRecordCount: 0,
|
|
113
|
+
Records: [],
|
|
114
|
+
});
|
|
115
|
+
const mockClient = {
|
|
116
|
+
send: mockSend,
|
|
117
|
+
};
|
|
118
|
+
const adapter = new KinesisAdapter({
|
|
119
|
+
client: mockClient,
|
|
120
|
+
streamName: "test-stream",
|
|
121
|
+
partitionKeyStrategy: "composite",
|
|
122
|
+
});
|
|
123
|
+
const events = [
|
|
124
|
+
createMockEvent("MONITOR_START", "exec-123", "org-456", 0),
|
|
125
|
+
];
|
|
126
|
+
await adapter.publish(events);
|
|
127
|
+
const records = mockSend.mock.calls[0][0].input.Records;
|
|
128
|
+
expect(records[0].PartitionKey).toBe("org-456:exec-123");
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
describe("retry logic", () => {
|
|
132
|
+
it("should retry failed records with exponential backoff", async () => {
|
|
133
|
+
let callCount = 0;
|
|
134
|
+
const mockSend = vi.fn().mockImplementation(() => {
|
|
135
|
+
callCount++;
|
|
136
|
+
if (callCount === 1) {
|
|
137
|
+
// First call: one record fails
|
|
138
|
+
return Promise.resolve({
|
|
139
|
+
FailedRecordCount: 1,
|
|
140
|
+
Records: [{ ErrorCode: "ProvisionedThroughputExceededException" }],
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// Second call: success
|
|
144
|
+
return Promise.resolve({
|
|
145
|
+
FailedRecordCount: 0,
|
|
146
|
+
Records: [{ SequenceNumber: "seq-1", ShardId: "shard-1" }],
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
const mockClient = {
|
|
150
|
+
send: mockSend,
|
|
151
|
+
};
|
|
152
|
+
const adapter = new KinesisAdapter({
|
|
153
|
+
client: mockClient,
|
|
154
|
+
streamName: "test-stream",
|
|
155
|
+
maxRetries: 3,
|
|
156
|
+
retryDelayMs: 10, // Short delay for testing
|
|
157
|
+
});
|
|
158
|
+
const events = [createMockEvent("MONITOR_START", "exec-1", "org-1", 0)];
|
|
159
|
+
await adapter.publish(events);
|
|
160
|
+
expect(mockSend).toHaveBeenCalledTimes(2); // Initial + 1 retry
|
|
161
|
+
});
|
|
162
|
+
it("should stop retrying after max attempts", async () => {
|
|
163
|
+
const mockSend = vi.fn().mockResolvedValue({
|
|
164
|
+
FailedRecordCount: 1,
|
|
165
|
+
Records: [{ ErrorCode: "ProvisionedThroughputExceededException" }],
|
|
166
|
+
});
|
|
167
|
+
const mockClient = {
|
|
168
|
+
send: mockSend,
|
|
169
|
+
};
|
|
170
|
+
const adapter = new KinesisAdapter({
|
|
171
|
+
client: mockClient,
|
|
172
|
+
streamName: "test-stream",
|
|
173
|
+
maxRetries: 2,
|
|
174
|
+
retryDelayMs: 10,
|
|
175
|
+
});
|
|
176
|
+
const events = [createMockEvent("MONITOR_START", "exec-1", "org-1", 0)];
|
|
177
|
+
// Should not throw, but log errors
|
|
178
|
+
await adapter.publish(events);
|
|
179
|
+
expect(mockSend).toHaveBeenCalledTimes(3); // Initial + 2 retries
|
|
180
|
+
});
|
|
181
|
+
it("should retry on client errors", async () => {
|
|
182
|
+
let callCount = 0;
|
|
183
|
+
const mockSend = vi.fn().mockImplementation(() => {
|
|
184
|
+
callCount++;
|
|
185
|
+
if (callCount === 1) {
|
|
186
|
+
return Promise.reject(new Error("Network error"));
|
|
187
|
+
}
|
|
188
|
+
return Promise.resolve({
|
|
189
|
+
FailedRecordCount: 0,
|
|
190
|
+
Records: [{ SequenceNumber: "seq-1", ShardId: "shard-1" }],
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
const mockClient = {
|
|
194
|
+
send: mockSend,
|
|
195
|
+
};
|
|
196
|
+
const adapter = new KinesisAdapter({
|
|
197
|
+
client: mockClient,
|
|
198
|
+
streamName: "test-stream",
|
|
199
|
+
maxRetries: 3,
|
|
200
|
+
retryDelayMs: 10,
|
|
201
|
+
});
|
|
202
|
+
const events = [createMockEvent("MONITOR_START", "exec-1", "org-1", 0)];
|
|
203
|
+
await adapter.publish(events);
|
|
204
|
+
expect(mockSend).toHaveBeenCalledTimes(2); // Initial + 1 retry
|
|
205
|
+
});
|
|
206
|
+
it("should throw after max retries on client errors", async () => {
|
|
207
|
+
const mockSend = vi.fn().mockRejectedValue(new Error("Network error"));
|
|
208
|
+
const mockClient = {
|
|
209
|
+
send: mockSend,
|
|
210
|
+
};
|
|
211
|
+
const adapter = new KinesisAdapter({
|
|
212
|
+
client: mockClient,
|
|
213
|
+
streamName: "test-stream",
|
|
214
|
+
maxRetries: 2,
|
|
215
|
+
retryDelayMs: 10,
|
|
216
|
+
});
|
|
217
|
+
const events = [createMockEvent("MONITOR_START", "exec-1", "org-1", 0)];
|
|
218
|
+
await expect(adapter.publish(events)).rejects.toThrow("Network error");
|
|
219
|
+
expect(mockSend).toHaveBeenCalledTimes(3); // Initial + 2 retries
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
describe("event serialization", () => {
|
|
223
|
+
it("should serialize events as JSON", async () => {
|
|
224
|
+
const mockSend = vi.fn().mockResolvedValue({
|
|
225
|
+
FailedRecordCount: 0,
|
|
226
|
+
Records: [],
|
|
227
|
+
});
|
|
228
|
+
const mockClient = {
|
|
229
|
+
send: mockSend,
|
|
230
|
+
};
|
|
231
|
+
const adapter = new KinesisAdapter({
|
|
232
|
+
client: mockClient,
|
|
233
|
+
streamName: "test-stream",
|
|
234
|
+
});
|
|
235
|
+
const event = createMockEvent("MONITOR_START", "exec-1", "org-1", 0);
|
|
236
|
+
await adapter.publish([event]);
|
|
237
|
+
const records = mockSend.mock.calls[0][0].input.Records;
|
|
238
|
+
const data = records[0].Data;
|
|
239
|
+
// Decode the Uint8Array back to JSON
|
|
240
|
+
const decoder = new TextDecoder();
|
|
241
|
+
const json = decoder.decode(data);
|
|
242
|
+
const parsed = JSON.parse(json);
|
|
243
|
+
expect(parsed.type).toBe("MONITOR_START");
|
|
244
|
+
expect(parsed.execution_id).toBe("exec-1");
|
|
245
|
+
expect(parsed.organization_id).toBe("org-1");
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
//# sourceMappingURL=kinesis.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kinesis.test.js","sourceRoot":"","sources":["../../../src/events/adapters/kinesis.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ExecutionEvent } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Pluggable event emitter interface for execution observability.
|
|
4
|
+
*
|
|
5
|
+
* Implementations can be synchronous (local) or asynchronous (durable bus).
|
|
6
|
+
* The executor treats emission as best-effort and non-blocking.
|
|
7
|
+
*/
|
|
8
|
+
export interface ExecutionEventEmitter {
|
|
9
|
+
/**
|
|
10
|
+
* Emit a single event. May be synchronous or asynchronous depending on implementation.
|
|
11
|
+
* Errors during emission should not propagate to the executor.
|
|
12
|
+
*/
|
|
13
|
+
emit(event: ExecutionEvent): void | Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Optional: flush any buffered events (for batching implementations).
|
|
16
|
+
* Called at the end of execution to ensure all events are delivered.
|
|
17
|
+
*/
|
|
18
|
+
flush?(): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Optional: cleanup resources (timers, connections, etc.).
|
|
21
|
+
*/
|
|
22
|
+
destroy?(): void;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* In-memory event emitter for local/development use.
|
|
26
|
+
* Events are delivered synchronously to all subscribers.
|
|
27
|
+
*/
|
|
28
|
+
export declare class LocalEventEmitter implements ExecutionEventEmitter {
|
|
29
|
+
private listeners;
|
|
30
|
+
/**
|
|
31
|
+
* Subscribe to all execution events.
|
|
32
|
+
* @returns Unsubscribe function
|
|
33
|
+
*/
|
|
34
|
+
subscribe(listener: (event: ExecutionEvent) => void): () => void;
|
|
35
|
+
emit(event: ExecutionEvent): void;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Adapter interface for durable event buses (SQS, Kafka, Redis Streams, etc.).
|
|
39
|
+
* Implementations provided by callers based on their infrastructure.
|
|
40
|
+
*/
|
|
41
|
+
export interface DurableEventBusAdapter {
|
|
42
|
+
/**
|
|
43
|
+
* Publish a batch of events to the durable bus.
|
|
44
|
+
* Should handle serialization, retries, and error handling internally.
|
|
45
|
+
*/
|
|
46
|
+
publish(events: ExecutionEvent[]): Promise<void>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Event emitter that batches events for efficient delivery to durable event buses.
|
|
50
|
+
* Flushes on interval and when batch size is reached.
|
|
51
|
+
*/
|
|
52
|
+
export declare class DurableEventEmitter implements ExecutionEventEmitter {
|
|
53
|
+
private adapter;
|
|
54
|
+
private options;
|
|
55
|
+
private buffer;
|
|
56
|
+
private flushInterval;
|
|
57
|
+
private isDestroyed;
|
|
58
|
+
constructor(adapter: DurableEventBusAdapter, options?: {
|
|
59
|
+
/** Number of events to batch before auto-flushing (default: 50) */
|
|
60
|
+
batchSize?: number;
|
|
61
|
+
/** Milliseconds between auto-flushes (default: 100) */
|
|
62
|
+
flushIntervalMs?: number;
|
|
63
|
+
});
|
|
64
|
+
emit(event: ExecutionEvent): void;
|
|
65
|
+
flush(): Promise<void>;
|
|
66
|
+
destroy(): void;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=emitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../src/events/emitter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,IAAI,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAElD;;;OAGG;IACH,KAAK,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAExB;;OAEG;IACH,OAAO,CAAC,IAAI,IAAI,CAAC;CAClB;AAED;;;GAGG;AACH,qBAAa,iBAAkB,YAAW,qBAAqB;IAC7D,OAAO,CAAC,SAAS,CAA8C;IAE/D;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,GAAG,MAAM,IAAI;IAOhE,IAAI,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;CAUlC;AAED;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD;AAED;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,qBAAqB;IAM7D,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,OAAO;IANjB,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,WAAW,CAAS;gBAGlB,OAAO,EAAE,sBAAsB,EAC/B,OAAO,GAAE;QACf,mEAAmE;QACnE,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,uDAAuD;QACvD,eAAe,CAAC,EAAE,MAAM,CAAC;KACrB;IAUR,IAAI,CAAC,KAAK,EAAE,cAAc,GAAG,IAAI;IAgB3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAc5B,OAAO,IAAI,IAAI;CAOhB"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory event emitter for local/development use.
|
|
3
|
+
* Events are delivered synchronously to all subscribers.
|
|
4
|
+
*/
|
|
5
|
+
export class LocalEventEmitter {
|
|
6
|
+
listeners = [];
|
|
7
|
+
/**
|
|
8
|
+
* Subscribe to all execution events.
|
|
9
|
+
* @returns Unsubscribe function
|
|
10
|
+
*/
|
|
11
|
+
subscribe(listener) {
|
|
12
|
+
this.listeners.push(listener);
|
|
13
|
+
return () => {
|
|
14
|
+
this.listeners = this.listeners.filter((l) => l !== listener);
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
emit(event) {
|
|
18
|
+
for (const listener of this.listeners) {
|
|
19
|
+
try {
|
|
20
|
+
listener(event);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
// Don't let listener errors break execution
|
|
24
|
+
console.error("Error in event listener:", error);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Event emitter that batches events for efficient delivery to durable event buses.
|
|
31
|
+
* Flushes on interval and when batch size is reached.
|
|
32
|
+
*/
|
|
33
|
+
export class DurableEventEmitter {
|
|
34
|
+
adapter;
|
|
35
|
+
options;
|
|
36
|
+
buffer = [];
|
|
37
|
+
flushInterval = null;
|
|
38
|
+
isDestroyed = false;
|
|
39
|
+
constructor(adapter, options = {}) {
|
|
40
|
+
this.adapter = adapter;
|
|
41
|
+
this.options = options;
|
|
42
|
+
const intervalMs = options.flushIntervalMs ?? 100;
|
|
43
|
+
this.flushInterval = setInterval(() => {
|
|
44
|
+
this.flush().catch((error) => {
|
|
45
|
+
console.error("Error flushing events:", error);
|
|
46
|
+
});
|
|
47
|
+
}, intervalMs);
|
|
48
|
+
}
|
|
49
|
+
emit(event) {
|
|
50
|
+
if (this.isDestroyed) {
|
|
51
|
+
console.warn("Cannot emit event: emitter is destroyed");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
this.buffer.push(event);
|
|
55
|
+
// Auto-flush when batch size is reached
|
|
56
|
+
if (this.buffer.length >= (this.options.batchSize ?? 50)) {
|
|
57
|
+
this.flush().catch((error) => {
|
|
58
|
+
console.error("Error auto-flushing events:", error);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async flush() {
|
|
63
|
+
if (this.buffer.length === 0)
|
|
64
|
+
return;
|
|
65
|
+
const events = this.buffer;
|
|
66
|
+
this.buffer = [];
|
|
67
|
+
try {
|
|
68
|
+
await this.adapter.publish(events);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.error("Failed to publish events:", error);
|
|
72
|
+
// Events are lost on failure - caller adapter should implement retries if needed
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
destroy() {
|
|
76
|
+
this.isDestroyed = true;
|
|
77
|
+
if (this.flushInterval) {
|
|
78
|
+
clearInterval(this.flushInterval);
|
|
79
|
+
this.flushInterval = null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=emitter.js.map
|