@othree.io/chisel-sdk 3.1.0 → 5.0.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.
Files changed (84) hide show
  1. package/README.md +207 -1
  2. package/lib/cjs/aws/index.d.ts.map +1 -0
  3. package/lib/cjs/aws/index.js.map +1 -0
  4. package/lib/cjs/aws/read.d.ts +44 -0
  5. package/lib/cjs/aws/read.d.ts.map +1 -0
  6. package/lib/{aws → cjs/aws}/read.js +65 -48
  7. package/lib/cjs/aws/read.js.map +1 -0
  8. package/lib/cjs/aws/write.d.ts +23 -0
  9. package/lib/cjs/aws/write.d.ts.map +1 -0
  10. package/lib/{aws → cjs/aws}/write.js +56 -34
  11. package/lib/cjs/aws/write.js.map +1 -0
  12. package/lib/cjs/iac/index.d.ts +28 -0
  13. package/lib/cjs/iac/index.d.ts.map +1 -0
  14. package/lib/{iac → cjs/iac}/index.js +23 -8
  15. package/lib/cjs/iac/index.js.map +1 -0
  16. package/lib/cjs/iac/read.d.ts +50 -0
  17. package/lib/cjs/iac/read.d.ts.map +1 -0
  18. package/lib/{iac → cjs/iac}/read.js +60 -33
  19. package/lib/cjs/iac/read.js.map +1 -0
  20. package/lib/cjs/iac/shared.d.ts +14 -0
  21. package/lib/cjs/iac/shared.d.ts.map +1 -0
  22. package/lib/{iac → cjs/iac}/shared.js +12 -8
  23. package/lib/cjs/iac/shared.js.map +1 -0
  24. package/lib/cjs/iac/types.d.ts +28 -0
  25. package/lib/cjs/iac/types.d.ts.map +1 -0
  26. package/lib/cjs/iac/types.js +26 -0
  27. package/lib/cjs/iac/types.js.map +1 -0
  28. package/lib/cjs/iac/write.d.ts +32 -0
  29. package/lib/cjs/iac/write.d.ts.map +1 -0
  30. package/lib/{iac → cjs/iac}/write.js +40 -17
  31. package/lib/cjs/iac/write.js.map +1 -0
  32. package/lib/cjs/index.d.ts.map +1 -0
  33. package/lib/cjs/index.js.map +1 -0
  34. package/lib/esm/aws/index.js +3 -0
  35. package/lib/esm/aws/index.js.map +1 -0
  36. package/lib/esm/aws/read.js +119 -0
  37. package/lib/esm/aws/read.js.map +1 -0
  38. package/lib/esm/aws/write.js +99 -0
  39. package/lib/esm/aws/write.js.map +1 -0
  40. package/lib/esm/iac/index.js +66 -0
  41. package/lib/esm/iac/index.js.map +1 -0
  42. package/lib/esm/iac/read.js +168 -0
  43. package/lib/esm/iac/read.js.map +1 -0
  44. package/lib/esm/iac/shared.js +32 -0
  45. package/lib/esm/iac/shared.js.map +1 -0
  46. package/lib/esm/iac/types.js +23 -0
  47. package/lib/esm/iac/types.js.map +1 -0
  48. package/lib/esm/iac/write.js +92 -0
  49. package/lib/esm/iac/write.js.map +1 -0
  50. package/lib/esm/index.js +3 -0
  51. package/lib/esm/index.js.map +1 -0
  52. package/package.json +50 -21
  53. package/.gitlab-ci.yml +0 -29
  54. package/lib/aws/index.d.ts.map +0 -1
  55. package/lib/aws/index.js.map +0 -1
  56. package/lib/aws/read.d.ts +0 -35
  57. package/lib/aws/read.d.ts.map +0 -1
  58. package/lib/aws/read.js.map +0 -1
  59. package/lib/aws/write.d.ts +0 -24
  60. package/lib/aws/write.d.ts.map +0 -1
  61. package/lib/aws/write.js.map +0 -1
  62. package/lib/iac/index.d.ts +0 -24
  63. package/lib/iac/index.d.ts.map +0 -1
  64. package/lib/iac/index.js.map +0 -1
  65. package/lib/iac/read.d.ts +0 -47
  66. package/lib/iac/read.d.ts.map +0 -1
  67. package/lib/iac/read.js.map +0 -1
  68. package/lib/iac/shared.d.ts +0 -12
  69. package/lib/iac/shared.d.ts.map +0 -1
  70. package/lib/iac/shared.js.map +0 -1
  71. package/lib/iac/types.d.ts +0 -13
  72. package/lib/iac/types.d.ts.map +0 -1
  73. package/lib/iac/types.js +0 -14
  74. package/lib/iac/types.js.map +0 -1
  75. package/lib/iac/write.d.ts +0 -29
  76. package/lib/iac/write.d.ts.map +0 -1
  77. package/lib/iac/write.js.map +0 -1
  78. package/lib/index.d.ts.map +0 -1
  79. package/lib/index.js.map +0 -1
  80. package/release.config.js +0 -4
  81. /package/lib/{aws → cjs/aws}/index.d.ts +0 -0
  82. /package/lib/{aws → cjs/aws}/index.js +0 -0
  83. /package/lib/{index.d.ts → cjs/index.d.ts} +0 -0
  84. /package/lib/{index.js → cjs/index.js} +0 -0
package/README.md CHANGED
@@ -1 +1,207 @@
1
- # chisel-sdk
1
+ # @othree.io/chisel-sdk
2
+
3
+ SDK for building event-sourced microservices with [Chisel](https://www.npmjs.com/package/@othree.io/chisel) on AWS. Provides pre-wired write-side actors, read-side projections, and CDK infrastructure stacks out of the box.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @othree.io/chisel-sdk
9
+ ```
10
+
11
+ ## Exports
12
+
13
+ ```typescript
14
+ import { aws, iac } from '@othree.io/chisel-sdk'
15
+ ```
16
+
17
+ | Namespace | Purpose |
18
+ |-----------|---------|
19
+ | `aws` | Runtime Lambda handlers for write-side (actor) and read-side (projections) |
20
+ | `iac` | CDK stack factories for the full CQRS infrastructure |
21
+
22
+ ## Write Side (`aws.createActor`)
23
+
24
+ Creates a fully wired Chisel actor with DynamoDB event persistence, sharded SNS publishing, and X-Ray tracing.
25
+
26
+ ```typescript
27
+ import { aws } from '@othree.io/chisel-sdk'
28
+
29
+ const actor = aws.createActor<MyAggregate, MyEvent>({
30
+ boundedContext: 'Orders',
31
+ now: () => Date.now(),
32
+ getInitialState: async (contextId) => Optional(emptyOrder),
33
+ handleCommand: myCommandHandler,
34
+ reduceEvent: myReducer,
35
+ })
36
+
37
+ // Lambda exports
38
+ export const handleCommand = actor.handleCommand
39
+ export const getState = actor.getState
40
+ ```
41
+
42
+ **Environment variables** (read automatically):
43
+
44
+ | Variable | Description |
45
+ |----------|-------------|
46
+ | `EVENTS_TABLE` | DynamoDB events table name |
47
+ | `EVENTS_TOPICS` | Comma-separated SNS topic ARNs |
48
+ | `LOG_LEVEL` | Logging level (default: `info`) |
49
+
50
+ **Returns** a `LambdaActor<AggregateRoot>` with:
51
+ - `handleCommand` - processes a `LambdaCommand` and returns an `InvocationResult<CommandResult<AggregateRoot>>`
52
+ - `getState` - loads and returns the current aggregate state as `InvocationResult<AggregateRoot>` by `contextId`
53
+
54
+ ## Read Side (`aws.getProjectionServices`)
55
+
56
+ Creates a fully wired projection processor with DynamoDB upsert/delete, SQS batch processing, and DLQ routing.
57
+
58
+ ```typescript
59
+ import { aws } from '@othree.io/chisel-sdk'
60
+
61
+ const projection = aws.getProjectionServices<MyAggregate, MyEvent>({
62
+ boundedContext: 'Orders',
63
+ now: () => Date.now(),
64
+ })
65
+
66
+ // Lambda exports
67
+ export const updateProjection = projection.sqs.updateProjection
68
+ export const getById = projection.services.getById
69
+ ```
70
+
71
+ **Environment variables** (read automatically):
72
+
73
+ | Variable | Description |
74
+ |----------|-------------|
75
+ | `PROJECTION_TABLE` | DynamoDB projection table name |
76
+ | `PROJECTION_DLQ` | Application DLQ URL |
77
+ | `PROJECTION_PK` | Projection table partition key name |
78
+ | `DELETE_ON_EVENTS` | Comma-separated event types that trigger deletion |
79
+ | `LOG_LEVEL` | Logging level (default: `info`) |
80
+
81
+ **Returns:**
82
+ - `sqs.updateProjection` - SQS batch handler that upserts or deletes projections based on event type
83
+ - `services.getById` - queries the projection table by ID
84
+
85
+ ### Standalone Functions
86
+
87
+ These are also exported for direct use or custom wiring:
88
+
89
+ | Function | Description |
90
+ |----------|-------------|
91
+ | `aws.getProcessInput` | Parses an SQS record containing an SNS-wrapped Chisel event |
92
+ | `aws.processEvent` | Routes to delete or upsert based on `deleteOnEvents` configuration |
93
+ | `aws.getProjectionById` | Queries the projection table and throws `NotFoundError` if empty |
94
+ | `aws.getProjectionServiceHandler` | Wraps a projection query function as a Lambda service handler |
95
+ | `aws.getConfiguration` | Reads projection configuration from environment variables |
96
+
97
+ ## Infrastructure (`iac.createChiselStacks`)
98
+
99
+ Creates all CDK stacks for a Chisel microservice in a single call.
100
+
101
+ ```typescript
102
+ import { App } from 'aws-cdk-lib'
103
+ import { iac } from '@othree.io/chisel-sdk'
104
+
105
+ const app = new App()
106
+
107
+ const stacks = iac.createChiselStacks(app, {
108
+ boundedContext: 'Orders',
109
+ version: 'v1',
110
+ projectMetadata: {
111
+ project: 'my-project',
112
+ environment: 'prod',
113
+ owner: 'team-a',
114
+ },
115
+ context: {
116
+ project: { env: 'prod', naming: { prefix: 'myapp' } },
117
+ lambda: { functions: [] },
118
+ chisel: { numberOfShards: 3 },
119
+ },
120
+ writeSide: {
121
+ codePath: 'dist/write',
122
+ },
123
+ readSide: {
124
+ codePath: 'dist/read',
125
+ deleteOnEvents: ['OrderDeleted'],
126
+ },
127
+ })
128
+ ```
129
+
130
+ The `context` is validated against `ChiselServiceContextConstraints` at runtime using Zod.
131
+
132
+ ### Generated Stacks
133
+
134
+ | Stack | Description | Output |
135
+ |-------|-------------|--------|
136
+ | **EventBus** | FIFO SNS topics (one per shard) | `stacks.topics.stack`, `stacks.topics.topics` |
137
+ | **WritePersistence** | DynamoDB events table | `stacks.writeSide.persistence.eventsTable` |
138
+ | **WriteServices** | Command handler and get-state Lambda functions | `stacks.writeSide.services.handleCommandArn`, `stacks.writeSide.services.getStateArn` |
139
+ | **TopicSubscribers** | SQS queues subscribed to SNS topics with DLQ and redrive | `stacks.readSide.subscribers.eventQueues`, `stacks.readSide.subscribers.dlq` |
140
+ | **ProjectionPersistence** | DynamoDB projection table with optional GSIs | `stacks.readSide.persistence.projectionTable` |
141
+ | **ReadServices** | Update-projection and get-by-id Lambda functions | `stacks.readSide.servicesStack.stack` |
142
+
143
+ The read side is optional. Omit `readSide` from the input to create a write-only service.
144
+
145
+ ### Context Configuration
146
+
147
+ The `ChiselServiceContext` controls stack behavior:
148
+
149
+ ```typescript
150
+ type ChiselServiceContext = Readonly<{
151
+ project: {
152
+ env: string // Environment name (used as resource suffix)
153
+ naming?: { prefix?: string } // Optional resource name prefix
154
+ }
155
+ lambda: {
156
+ functions: Array<{
157
+ name: string
158
+ reservedConcurrentExecutions?: number
159
+ }>
160
+ }
161
+ chisel: {
162
+ numberOfShards: number // Number of FIFO SNS topics
163
+ }
164
+ }>
165
+ ```
166
+
167
+ ### Read Side Options
168
+
169
+ ```typescript
170
+ type ReadSideInput = Readonly<{
171
+ codePath: string // Lambda code path
172
+ tablePk?: string // Projection table PK name (default: 'id')
173
+ globalSecondaryIndexes?: Array<GSIInput> // Optional GSIs
174
+ deleteOnEvents: Array<string> // Event types that trigger row deletion
175
+ }>
176
+ ```
177
+
178
+ ## Peer Dependencies
179
+
180
+ ```json
181
+ {
182
+ "@othree.io/auditor": "^5.0.0",
183
+ "@othree.io/awsome": "^5.0.0",
184
+ "@othree.io/cdk": "^6.0.0",
185
+ "@othree.io/chisel": "^7.0.0",
186
+ "@othree.io/chisel-aws": "^7.0.0",
187
+ "@othree.io/excuses": "^2.0.0",
188
+ "@othree.io/journal": "^3.0.0",
189
+ "@othree.io/stethoscope": "^5.0.0",
190
+ "aws-cdk-lib": "^2.239.0",
191
+ "aws-lambda": "^1.0.7",
192
+ "aws-xray-sdk-core": "^3.12.0",
193
+ "uuid": "^13.0.0"
194
+ }
195
+ ```
196
+
197
+ ## Development
198
+
199
+ ```bash
200
+ npm install
201
+ npm run build # Dual CJS/ESM output
202
+ npm test # Vitest with coverage
203
+ ```
204
+
205
+ ## License
206
+
207
+ ISC
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/aws/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAA;AACtB,cAAc,SAAS,CAAA"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/aws/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,yCAAsB;AACtB,0CAAuB","sourcesContent":["export * from './read'\nexport * from './write'"]}
@@ -0,0 +1,44 @@
1
+ import { dynamo } from '@othree.io/awsome';
2
+ import { Optional } from '@othree.io/optional';
3
+ import { ChiselEvent, TriggeredEvent } from '@othree.io/chisel';
4
+ import { SQSRecord } from 'aws-lambda';
5
+ export type ProjectionConfiguration = Readonly<{
6
+ projectionTable: string;
7
+ projectionDLQ: string;
8
+ projectionPk: string;
9
+ deleteOnEvents: Array<string>;
10
+ }>;
11
+ type CreateProjectionInput = Readonly<{
12
+ boundedContext: string;
13
+ now: () => number;
14
+ getConfiguration?: () => ProjectionConfiguration;
15
+ }>;
16
+ type EventState<AggregateRoot, EventType extends ChiselEvent> = Readonly<{
17
+ state: AggregateRoot;
18
+ event: TriggeredEvent<EventType>;
19
+ }>;
20
+ export declare const getProcessInput: <AggregateRoot, EventType extends ChiselEvent>(input: SQSRecord) => Promise<Optional<EventState<AggregateRoot, EventType>>>;
21
+ export declare const processEvent: <AggregateRoot, EventType extends ChiselEvent>(configuration: ProjectionConfiguration) => (deleteByFn: (keys: dynamo.Keys) => Promise<Optional<boolean>>) => (upsertFn: (entity: AggregateRoot) => Promise<Optional<AggregateRoot>>) => (eventState: EventState<AggregateRoot, EventType>) => Promise<Optional<AggregateRoot>>;
22
+ export declare const getProjectionById: (boundedContext: string, configuration: ProjectionConfiguration) => <AggregateRoot>(getByFn: (keys: dynamo.Keys) => Promise<Optional<Array<AggregateRoot>>>) => (input: GetByIdQuery) => Promise<Optional<AggregateRoot>>;
23
+ export type ServiceQuery<T> = Readonly<{
24
+ query: T;
25
+ }>;
26
+ export type GetByIdQuery = Readonly<{
27
+ id: string;
28
+ }>;
29
+ export declare const getProjectionServiceHandler: <QueryType, ProjectionType>(getProjection: (query: QueryType) => Promise<Optional<ProjectionType>>) => (event: ServiceQuery<QueryType>) => Promise<ProjectionType>;
30
+ export declare const getConfiguration: () => ProjectionConfiguration;
31
+ export declare const getProjectionServices: <AggregateRoot, EventType extends ChiselEvent>(input: CreateProjectionInput) => {
32
+ sqs: {
33
+ updateProjection: (event: import("aws-lambda").SQSEvent) => Promise<import("aws-lambda").SQSBatchResponse>;
34
+ };
35
+ services: {
36
+ getById: (event: Readonly<{
37
+ query: Readonly<{
38
+ id: string;
39
+ }>;
40
+ }>) => Promise<Optional<AggregateRoot>>;
41
+ };
42
+ };
43
+ export {};
44
+ //# sourceMappingURL=read.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../src/aws/read.ts"],"names":[],"mappings":"AAIA,OAAO,EAAC,MAAM,EAAC,MAAM,mBAAmB,CAAA;AACxC,OAAO,EAAC,QAAQ,EAAW,MAAM,qBAAqB,CAAA;AACtD,OAAO,EAAC,WAAW,EAAE,cAAc,EAAC,MAAM,mBAAmB,CAAA;AAG7D,OAAO,EAAC,SAAS,EAAC,MAAM,YAAY,CAAA;AAGpC,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAAC;IAC3C,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CAChC,CAAC,CAAA;AAEF,KAAK,qBAAqB,GAAG,QAAQ,CAAC;IAClC,cAAc,EAAE,MAAM,CAAA;IACtB,GAAG,EAAE,MAAM,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,MAAM,uBAAuB,CAAA;CACnD,CAAC,CAAA;AASF,KAAK,UAAU,CAAC,aAAa,EAAE,SAAS,SAAS,WAAW,IAAI,QAAQ,CAAC;IACrE,KAAK,EAAE,aAAa,CAAC;IACrB,KAAK,EAAE,cAAc,CAAC,SAAS,CAAC,CAAA;CACnC,CAAC,CAAA;AAEF,eAAO,MAAM,eAAe,GAAU,aAAa,EAAE,SAAS,SAAS,WAAW,EAAE,OAAO,SAAS,KAAG,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC,CAW5J,CAAA;AAED,eAAO,MAAM,YAAY,GAAI,aAAa,EAAE,SAAS,SAAS,WAAW,EAAE,eAAe,uBAAuB,MAC5G,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,MACzD,UAAU,CAAC,MAAM,EAAE,aAAa,KAAK,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,MAC/D,YAAY,UAAU,CAAC,aAAa,EAAE,SAAS,CAAC,qCAQtD,CAAA;AAET,eAAO,MAAM,iBAAiB,GAAI,gBAAgB,MAAM,EAAE,eAAe,uBAAuB,MAC3F,aAAa,EAAE,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,KAAK,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,MAC5E,OAAO,YAAY,KAAG,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAU3D,CAAA;AAET,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,QAAQ,CAAC;IACnC,KAAK,EAAE,CAAC,CAAA;CACX,CAAC,CAAA;AAEF,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;IAChC,EAAE,EAAE,MAAM,CAAA;CACb,CAAC,CAAA;AAEF,eAAO,MAAM,2BAA2B,GAAI,SAAS,EAAE,cAAc,EAAE,eAAe,CAAC,KAAK,EAAE,SAAS,KAAK,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC,MAClI,OAAO,YAAY,CAAC,SAAS,CAAC,KAAG,OAAO,CAAC,cAAc,CAE7D,CAAA;AAGL,eAAO,MAAM,gBAAgB,QAAO,uBASnC,CAAA;AAED,eAAO,MAAM,qBAAqB,GAAI,aAAa,EAAE,SAAS,SAAS,WAAW,EAAE,OAAO,qBAAqB;;;;;;;oBApBxG,MAAM;;;;CA6Fb,CAAA"}
@@ -1,32 +1,23 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.getProjectionServices = exports.getConfiguration = exports.getProjectionById = exports.processEvent = exports.getProcessInput = void 0;
3
+ exports.getProjectionServices = exports.getConfiguration = exports.getProjectionServiceHandler = exports.getProjectionById = exports.processEvent = exports.getProcessInput = void 0;
13
4
  const stethoscope_1 = require("@othree.io/stethoscope");
14
5
  const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
6
+ const client_sqs_1 = require("@aws-sdk/client-sqs");
15
7
  const journal_1 = require("@othree.io/journal");
16
- const dynamo_1 = require("@othree.io/awsome/lib/dynamo");
17
- const projection_utils_1 = require("@othree.io/projection-utils");
8
+ const awsome_1 = require("@othree.io/awsome");
18
9
  const optional_1 = require("@othree.io/optional");
19
10
  const zod_1 = require("zod");
20
11
  const auditor_1 = require("@othree.io/auditor");
21
12
  const excuses_1 = require("@othree.io/excuses");
22
13
  const ProjectionConstraints = zod_1.z.object({
23
- projectionTable: auditor_1.zod.NonEmptyString,
24
- projectionDLQ: auditor_1.zod.NonEmptyString,
25
- projectionPk: auditor_1.zod.NonEmptyString,
26
- deleteOnEvents: zod_1.z.array(auditor_1.zod.NonEmptyString)
14
+ projectionTable: auditor_1.NonEmptyString,
15
+ projectionDLQ: auditor_1.NonEmptyString,
16
+ projectionPk: auditor_1.NonEmptyString,
17
+ deleteOnEvents: zod_1.z.array(auditor_1.NonEmptyString)
27
18
  });
28
- const getProcessInput = (input) => __awaiter(void 0, void 0, void 0, function* () {
29
- return (0, optional_1.TryAsync)(() => __awaiter(void 0, void 0, void 0, function* () {
19
+ const getProcessInput = async (input) => {
20
+ return (0, optional_1.TryAsync)(async () => {
30
21
  const { body } = input;
31
22
  const sqsEvent = JSON.parse(body);
32
23
  const eventMessage = JSON.parse(sqsEvent.Message);
@@ -34,20 +25,20 @@ const getProcessInput = (input) => __awaiter(void 0, void 0, void 0, function* (
34
25
  state: eventMessage.state,
35
26
  event: eventMessage.event
36
27
  };
37
- }));
38
- });
28
+ });
29
+ };
39
30
  exports.getProcessInput = getProcessInput;
40
- const processEvent = (configuration) => (deleteBy) => (upsert) => (eventState) => __awaiter(void 0, void 0, void 0, function* () {
31
+ const processEvent = (configuration) => (deleteByFn) => (upsertFn) => async (eventState) => {
41
32
  if (configuration.deleteOnEvents.includes(eventState.event.type)) {
42
- return deleteBy({
33
+ return deleteByFn({
43
34
  [configuration.projectionPk]: eventState.event.contextId
44
35
  }).then(maybeSuccess => maybeSuccess.map(_ => eventState.state));
45
36
  }
46
- return upsert(eventState.state);
47
- });
37
+ return upsertFn(eventState.state);
38
+ };
48
39
  exports.processEvent = processEvent;
49
- const getProjectionById = (boundedContext, configuration) => (getBy) => (input) => __awaiter(void 0, void 0, void 0, function* () {
50
- return (yield getBy({
40
+ const getProjectionById = (boundedContext, configuration) => (getByFn) => async (input) => {
41
+ return (await getByFn({
51
42
  [configuration.projectionPk]: input.id
52
43
  })).map(offers => {
53
44
  if (offers.length > 0) {
@@ -55,10 +46,15 @@ const getProjectionById = (boundedContext, configuration) => (getBy) => (input)
55
46
  }
56
47
  throw new excuses_1.NotFoundError(input.id, boundedContext);
57
48
  });
58
- });
49
+ };
59
50
  exports.getProjectionById = getProjectionById;
51
+ const getProjectionServiceHandler = (getProjection) => async (event) => {
52
+ return getProjection(event.query).then(_ => _.get());
53
+ };
54
+ exports.getProjectionServiceHandler = getProjectionServiceHandler;
55
+ /* v8 ignore start: wiring — covered by integration tests */
60
56
  const getConfiguration = () => {
61
- const envVars = auditor_1.zod.zodValidate(ProjectionConstraints)({
57
+ const envVars = (0, auditor_1.zodValidate)({ constraints: ProjectionConstraints })({
62
58
  projectionTable: (0, optional_1.Optional)(process.env.PROJECTION_TABLE).map(String).orElse(''),
63
59
  projectionDLQ: (0, optional_1.Optional)(process.env.PROJECTION_DLQ).map(String).orElse(''),
64
60
  projectionPk: (0, optional_1.Optional)(process.env.PROJECTION_PK).map(String).orElse(''),
@@ -69,35 +65,55 @@ const getConfiguration = () => {
69
65
  exports.getConfiguration = getConfiguration;
70
66
  const getProjectionServices = (input) => {
71
67
  const logLevel = (0, optional_1.Optional)(process.env.LOG_LEVEL)
72
- .orMap(() => {
73
- console.warn(`LOG_LEVEL env var not defined. Defaulting to 'info'`);
74
- return 'info';
75
- })
76
- .get();
68
+ .orElse('info');
77
69
  const configuration = (0, optional_1.Optional)(input.getConfiguration)
78
70
  .map(_ => _())
79
71
  .orElse((0, exports.getConfiguration)());
80
- const logger = journal_1.winston.createWinstonLogger(input.boundedContext, logLevel);
81
- const now = () => new Date().getTime();
82
- const logDebug = (0, journal_1.logIOAsync)(now)(logger.debug)(logger.error);
83
- const logInfo = (0, journal_1.logIOAsync)(now)(logger.info)(logger.error);
72
+ const logger = (0, journal_1.createDefaultWinstonLogger)(console)(input.boundedContext, logLevel);
73
+ const logDebug = (0, journal_1.logIOAsync)({
74
+ now: input.now,
75
+ log: logger.debug.bind(logger),
76
+ logError: logger.error.bind(logger),
77
+ });
78
+ const logInfo = (0, journal_1.logIOAsync)({
79
+ now: input.now,
80
+ log: logger.info.bind(logger),
81
+ logError: logger.error.bind(logger),
82
+ });
84
83
  const dynamoDbClient = new client_dynamodb_1.DynamoDBClient({});
85
- const upsertProjection = logDebug((0, dynamo_1.upsert)(dynamoDbClient, {
84
+ const sqsClient = new client_sqs_1.SQSClient({});
85
+ const dynamoConfig = {
86
86
  TableName: configuration.projectionTable
87
+ };
88
+ const upsertProjection = logDebug(awsome_1.dynamo.upsert({
89
+ dynamoDb: dynamoDbClient,
90
+ configuration: dynamoConfig,
87
91
  }))(`upsert${input.boundedContext}`);
88
- const deleteProjectionBy = logDebug((0, dynamo_1.deleteBy)(dynamoDbClient, {
89
- TableName: configuration.projectionTable
92
+ const deleteProjectionBy = logDebug(awsome_1.dynamo.deleteBy({
93
+ dynamoDb: dynamoDbClient,
94
+ configuration: dynamoConfig,
90
95
  }))(`delete${input.boundedContext}`);
91
- const getProjectionBy = logDebug((0, dynamo_1.getBy)((0, dynamo_1.query)(dynamoDbClient, {
92
- TableName: configuration.projectionTable
93
- })))(`get${input.boundedContext}`);
96
+ const queryProjection = awsome_1.dynamo.query({
97
+ dynamoDb: dynamoDbClient,
98
+ configuration: dynamoConfig,
99
+ });
100
+ const getProjectionBy = logDebug(awsome_1.dynamo.getBy({
101
+ query: queryProjection,
102
+ }))(`get${input.boundedContext}`);
94
103
  const getById = logDebug((0, exports.getProjectionById)(input.boundedContext, configuration)(getProjectionBy))(`get${input.boundedContext}ById`);
95
104
  const processEventFn = logDebug((0, exports.processEvent)(configuration)(deleteProjectionBy)(upsertProjection))(`process${input.boundedContext}Event`);
96
- const processSqsFn = (0, stethoscope_1.processSqs)(logInfo)({
97
- dlqUrl: configuration.projectionTable,
98
- exponentialTimeouts: [5, 10, 30]
99
- })((_) => false)(processEventFn)(exports.getProcessInput);
100
- const getByIdHandlerFn = logInfo((0, projection_utils_1.getProjectionServiceHandler)(getById))(`get${input.boundedContext}ByIdHandler`);
105
+ const processSqsFn = (0, stethoscope_1.processSqs)({
106
+ log: logInfo,
107
+ sqsClient,
108
+ configuration: {
109
+ dlqUrl: configuration.projectionDLQ,
110
+ exponentialTimeouts: [5, 10, 30],
111
+ },
112
+ didItFail: (_) => false,
113
+ runProcess: processEventFn,
114
+ getProcessInput: (exports.getProcessInput),
115
+ });
116
+ const getByIdHandlerFn = logInfo((0, exports.getProjectionServiceHandler)(getById))(`get${input.boundedContext}ByIdHandler`);
101
117
  return {
102
118
  sqs: {
103
119
  updateProjection: processSqsFn
@@ -108,4 +124,5 @@ const getProjectionServices = (input) => {
108
124
  };
109
125
  };
110
126
  exports.getProjectionServices = getProjectionServices;
127
+ /* v8 ignore stop */
111
128
  //# sourceMappingURL=read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../src/aws/read.ts"],"names":[],"mappings":"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","sourcesContent":["import {processSqs} from '@othree.io/stethoscope'\nimport {DynamoDBClient} from '@aws-sdk/client-dynamodb'\nimport {SQSClient} from '@aws-sdk/client-sqs'\nimport {logIOAsync, createDefaultWinstonLogger} from '@othree.io/journal'\nimport {dynamo} from '@othree.io/awsome'\nimport {Optional, TryAsync} from '@othree.io/optional'\nimport {ChiselEvent, TriggeredEvent} from '@othree.io/chisel'\nimport {z} from 'zod'\nimport {zodValidate, NonEmptyString} from '@othree.io/auditor'\nimport {SQSRecord} from 'aws-lambda'\nimport {NotFoundError} from '@othree.io/excuses'\n\nexport type ProjectionConfiguration = Readonly<{\n projectionTable: string\n projectionDLQ: string\n projectionPk: string\n deleteOnEvents: Array<string>\n}>\n\ntype CreateProjectionInput = Readonly<{\n boundedContext: string\n now: () => number\n getConfiguration?: () => ProjectionConfiguration\n}>\n\nconst ProjectionConstraints: z.ZodType<ProjectionConfiguration> = z.object({\n projectionTable: NonEmptyString,\n projectionDLQ: NonEmptyString,\n projectionPk: NonEmptyString,\n deleteOnEvents: z.array(NonEmptyString)\n})\n\ntype EventState<AggregateRoot, EventType extends ChiselEvent> = Readonly<{\n state: AggregateRoot,\n event: TriggeredEvent<EventType>\n}>\n\nexport const getProcessInput = async <AggregateRoot, EventType extends ChiselEvent>(input: SQSRecord): Promise<Optional<EventState<AggregateRoot, EventType>>> => {\n return TryAsync(async () => {\n const {body} = input\n const sqsEvent = JSON.parse(body)\n const eventMessage = JSON.parse(sqsEvent.Message)\n\n return {\n state: eventMessage.state as AggregateRoot,\n event: eventMessage.event as TriggeredEvent<EventType>\n }\n })\n}\n\nexport const processEvent = <AggregateRoot, EventType extends ChiselEvent>(configuration: ProjectionConfiguration) =>\n (deleteByFn: (keys: dynamo.Keys) => Promise<Optional<boolean>>) =>\n (upsertFn: (entity: AggregateRoot) => Promise<Optional<AggregateRoot>>) =>\n async (eventState: EventState<AggregateRoot, EventType>) => {\n if (configuration.deleteOnEvents.includes(eventState.event.type)) {\n return deleteByFn({\n [configuration.projectionPk]: eventState.event.contextId\n }).then(maybeSuccess => maybeSuccess.map(_ => eventState.state))\n }\n\n return upsertFn(eventState.state)\n }\n\nexport const getProjectionById = (boundedContext: string, configuration: ProjectionConfiguration) =>\n <AggregateRoot>(getByFn: (keys: dynamo.Keys) => Promise<Optional<Array<AggregateRoot>>>) =>\n async (input: GetByIdQuery): Promise<Optional<AggregateRoot>> => {\n return (await getByFn({\n [configuration.projectionPk]: input.id\n })).map(offers => {\n if (offers.length > 0) {\n return offers[0]\n }\n\n throw new NotFoundError(input.id, boundedContext)\n })\n }\n\nexport type ServiceQuery<T> = Readonly<{\n query: T\n}>\n\nexport type GetByIdQuery = Readonly<{\n id: string\n}>\n\nexport const getProjectionServiceHandler = <QueryType, ProjectionType>(getProjection: (query: QueryType) => Promise<Optional<ProjectionType>>) =>\n async (event: ServiceQuery<QueryType>): Promise<ProjectionType> => {\n return getProjection(event.query).then(_ => _.get() as ProjectionType)\n }\n\n/* v8 ignore start: wiring — covered by integration tests */\nexport const getConfiguration = (): ProjectionConfiguration => {\n const envVars = zodValidate({ constraints: ProjectionConstraints })({\n projectionTable: Optional(process.env.PROJECTION_TABLE).map(String).orElse(''),\n projectionDLQ: Optional(process.env.PROJECTION_DLQ).map(String).orElse(''),\n projectionPk: Optional(process.env.PROJECTION_PK).map(String).orElse(''),\n deleteOnEvents: Optional(process.env.DELETE_ON_EVENTS).map(_ => _.split(',')).orElse([])\n }).get()\n\n return envVars\n}\n\nexport const getProjectionServices = <AggregateRoot, EventType extends ChiselEvent>(input: CreateProjectionInput) => {\n const logLevel = Optional(process.env.LOG_LEVEL)\n .orElse('info')\n\n const configuration = Optional(input.getConfiguration)\n .map(_ => _())\n .orElse(getConfiguration())\n\n const logger = createDefaultWinstonLogger(console)(input.boundedContext, logLevel)\n\n const logDebug = logIOAsync({\n now: input.now,\n log: logger.debug.bind(logger),\n logError: logger.error.bind(logger),\n })\n const logInfo = logIOAsync({\n now: input.now,\n log: logger.info.bind(logger),\n logError: logger.error.bind(logger),\n })\n\n const dynamoDbClient = new DynamoDBClient({})\n const sqsClient = new SQSClient({})\n\n const dynamoConfig: dynamo.DynamoConfiguration = {\n TableName: configuration.projectionTable\n }\n\n const upsertProjection: (entity: AggregateRoot) => Promise<Optional<AggregateRoot>> = logDebug(dynamo.upsert<AggregateRoot>({\n dynamoDb: dynamoDbClient,\n configuration: dynamoConfig,\n }))(`upsert${input.boundedContext}`)\n\n const deleteProjectionBy: (keys: dynamo.Keys) => Promise<Optional<boolean>> = logDebug(dynamo.deleteBy<AggregateRoot>({\n dynamoDb: dynamoDbClient,\n configuration: dynamoConfig,\n }))(`delete${input.boundedContext}`)\n\n const queryProjection = dynamo.query<AggregateRoot>({\n dynamoDb: dynamoDbClient,\n configuration: dynamoConfig,\n })\n\n const getProjectionBy: (keys: dynamo.Keys) => Promise<Optional<Array<AggregateRoot>>> = logDebug(dynamo.getBy<AggregateRoot>({\n query: queryProjection,\n }))(`get${input.boundedContext}`)\n\n const getById: (input: GetByIdQuery) => Promise<Optional<AggregateRoot>> = logDebug(getProjectionById(input.boundedContext, configuration)(getProjectionBy))(`get${input.boundedContext}ById`)\n\n const processEventFn: (eventState: EventState<AggregateRoot, EventType>) => Promise<Optional<AggregateRoot>> = logDebug(processEvent<AggregateRoot, EventType>(configuration)(deleteProjectionBy)(upsertProjection))(`process${input.boundedContext}Event`)\n\n const processSqsFn = processSqs<EventState<AggregateRoot, EventType>, AggregateRoot>({\n log: logInfo,\n sqsClient,\n configuration: {\n dlqUrl: configuration.projectionDLQ,\n exponentialTimeouts: [5, 10, 30],\n },\n didItFail: (_: Error) => false,\n runProcess: processEventFn,\n getProcessInput: getProcessInput<AggregateRoot, EventType>,\n })\n\n const getByIdHandlerFn = logInfo(getProjectionServiceHandler<GetByIdQuery, AggregateRoot>(getById))(`get${input.boundedContext}ByIdHandler`)\n\n return {\n sqs: {\n updateProjection: processSqsFn\n },\n services: {\n getById: getByIdHandlerFn\n }\n }\n}\n/* v8 ignore stop */\n"]}
@@ -0,0 +1,23 @@
1
+ import { handlers } from '@othree.io/chisel-aws';
2
+ import { actor, ChiselEvent, CommandResult, eventSource, EventSourceConfiguration } from '@othree.io/chisel';
3
+ import { lambda } from '@othree.io/awsome';
4
+ import { Optional } from '@othree.io/optional';
5
+ import { ChiselDynamoConfiguration, ChiselShardedSnsConfiguration } from '@othree.io/chisel-aws';
6
+ type GetStateQuery = Readonly<{
7
+ contextId: string;
8
+ }>;
9
+ export type LambdaActor<AggregateRoot> = Readonly<{
10
+ handleCommand: (event: handlers.LambdaCommand) => Promise<lambda.InvocationResult<CommandResult<AggregateRoot>>>;
11
+ getState: (event: lambda.Query<GetStateQuery>) => Promise<lambda.InvocationResult<AggregateRoot>>;
12
+ }>;
13
+ type CreateLambdaActorInput<AggregateRoot, Event extends ChiselEvent> = Readonly<{
14
+ boundedContext: string;
15
+ now: () => number;
16
+ getInitialState: (contextId: Optional<string>) => Promise<Optional<AggregateRoot>>;
17
+ handleCommand: actor.HandleCommand<AggregateRoot>;
18
+ reduceEvent: eventSource.Reduce<AggregateRoot, Event>;
19
+ getConfiguration?: () => ChiselDynamoConfiguration & ChiselShardedSnsConfiguration<AggregateRoot> & EventSourceConfiguration<AggregateRoot>;
20
+ }>;
21
+ export declare const createActor: <AggregateRoot, Event extends ChiselEvent>(input: CreateLambdaActorInput<AggregateRoot, Event>) => LambdaActor<AggregateRoot>;
22
+ export {};
23
+ //# sourceMappingURL=write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../src/aws/write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAC,MAAM,uBAAuB,CAAA;AAC9C,OAAO,EAAC,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,wBAAwB,EAAC,MAAM,mBAAmB,CAAA;AAC1G,OAAO,EAAC,MAAM,EAAC,MAAM,mBAAmB,CAAA;AAExC,OAAO,EAAC,QAAQ,EAAC,MAAM,qBAAqB,CAAA;AAE5C,OAAO,EAGH,yBAAyB,EACzB,6BAA6B,EAChC,MAAM,uBAAuB,CAAA;AAQ9B,KAAK,aAAa,GAAG,QAAQ,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAA;CACpB,CAAC,CAAA;AAEF,MAAM,MAAM,WAAW,CAAC,aAAa,IAAI,QAAQ,CAAC;IAC9C,aAAa,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAAA;IAChH,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC,CAAA;CACpG,CAAC,CAAA;AAkCF,KAAK,sBAAsB,CAAC,aAAa,EAAE,KAAK,SAAS,WAAW,IAAI,QAAQ,CAAC;IAC7E,cAAc,EAAE,MAAM,CAAA;IACtB,GAAG,EAAE,MAAM,MAAM,CAAA;IACjB,eAAe,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,CAAA;IAClF,aAAa,EAAE,KAAK,CAAC,aAAa,CAAC,aAAa,CAAC,CAAA;IACjD,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,aAAa,EAAE,KAAK,CAAC,CAAA;IACrD,gBAAgB,CAAC,EAAE,MAAM,yBAAyB,GAAG,6BAA6B,CAAC,aAAa,CAAC,GAAG,wBAAwB,CAAC,aAAa,CAAC,CAAA;CAC9I,CAAC,CAAA;AAQF,eAAO,MAAM,WAAW,GAAI,aAAa,EAAE,KAAK,SAAS,WAAW,EAAE,OAAO,sBAAsB,CAAC,aAAa,EAAE,KAAK,CAAC,KAAG,WAAW,CAAC,aAAa,CAqEpJ,CAAA"}
@@ -32,34 +32,27 @@ var __importStar = (this && this.__importStar) || (function () {
32
32
  return result;
33
33
  };
34
34
  })();
35
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
- return new (P || (P = Promise))(function (resolve, reject) {
38
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
39
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
40
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
41
- step((generator = generator.apply(thisArg, _arguments || [])).next());
42
- });
43
- };
44
35
  Object.defineProperty(exports, "__esModule", { value: true });
45
36
  exports.createActor = void 0;
37
+ const chisel_aws_1 = require("@othree.io/chisel-aws");
46
38
  const chisel_1 = require("@othree.io/chisel");
39
+ const awsome_1 = require("@othree.io/awsome");
47
40
  const journal_1 = require("@othree.io/journal");
48
41
  const optional_1 = require("@othree.io/optional");
49
- const chisel_aws_1 = require("@othree.io/chisel-aws");
42
+ const cerillo_1 = require("@othree.io/cerillo");
43
+ const chisel_aws_2 = require("@othree.io/chisel-aws");
50
44
  const auditor_1 = require("@othree.io/auditor");
51
45
  const zod_1 = require("zod");
52
46
  const uuid_1 = require("uuid");
53
47
  const AWSXRay = __importStar(require("aws-xray-sdk-core"));
54
48
  const client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
55
49
  const client_sns_1 = require("@aws-sdk/client-sns");
56
- const actor_1 = require("@othree.io/chisel-aws/lib/actor");
57
50
  const ChiselActorConstraints = zod_1.z.object({
58
- eventsTable: auditor_1.zod.NonEmptyString,
59
- eventsTopics: zod_1.z.array(auditor_1.zod.NonEmptyString),
51
+ eventsTable: auditor_1.NonEmptyString,
52
+ eventsTopics: zod_1.z.array(auditor_1.NonEmptyString),
60
53
  });
61
54
  const getConfiguration = (input) => {
62
- const envVars = auditor_1.zod.zodValidate(ChiselActorConstraints)({
55
+ const envVars = (0, auditor_1.zodValidate)({ constraints: ChiselActorConstraints })({
63
56
  eventsTable: (0, optional_1.Optional)(process.env.EVENTS_TABLE).map(String).orElse(''),
64
57
  eventsTopics: (0, optional_1.Optional)(process.env.EVENTS_TOPICS).map(_ => String(_).split(',')).orElse([]),
65
58
  }).get();
@@ -70,7 +63,7 @@ const getConfiguration = (input) => {
70
63
  TableName: envVars.eventsTable,
71
64
  TopicsArn: envVars.eventsTopics,
72
65
  SnapshotEventType: '$SNAPSHOT$',
73
- GetMessageGroupId: (event, state) => event.contextId,
66
+ GetMessageGroupId: (event, _state) => event.contextId,
74
67
  IncludeState: true,
75
68
  SnapshotSerializer: {
76
69
  serialize: (state) => JSON.stringify(state),
@@ -78,36 +71,65 @@ const getConfiguration = (input) => {
78
71
  }
79
72
  };
80
73
  };
81
- const getStateHandler = (loadState) => (event) => __awaiter(void 0, void 0, void 0, function* () {
74
+ const getStateHandler = (loadState) => async (event) => {
82
75
  return loadState((0, optional_1.Optional)(event.query.contextId))
83
76
  .then(_ => _.map(state => state.state).get());
84
- });
77
+ };
85
78
  const createActor = (input) => {
86
79
  const logLevel = (0, optional_1.Optional)(process.env.LOG_LEVEL)
87
- .orMap(() => {
88
- console.warn(`LOG_LEVEL env var not defined. Defaulting to 'info'`);
89
- return 'info';
90
- })
91
- .get();
92
- const logger = journal_1.winston.createWinstonLogger(input.boundedContext, logLevel);
93
- const now = () => new Date().getTime();
94
- const logDebug = (0, journal_1.logIOAsync)(now)(logger.debug)(logger.error);
80
+ .orElse('info');
81
+ const logger = (0, journal_1.createDefaultWinstonLogger)(console)(input.boundedContext, logLevel);
82
+ const logDebug = (0, journal_1.logIOAsync)({
83
+ now: input.now,
84
+ log: logger.debug.bind(logger),
85
+ logError: logger.error.bind(logger),
86
+ });
95
87
  const configuration = (0, optional_1.Optional)(input.getConfiguration)
96
88
  .map(_ => _())
97
89
  .orElse(getConfiguration(input));
98
90
  const dynamoDbClient = AWSXRay.captureAWSv3Client(new client_dynamodb_1.DynamoDBClient({}));
99
91
  const snsClient = AWSXRay.captureAWSv3Client(new client_sns_1.SNSClient({}));
100
- const getEvents = logDebug(chisel_aws_1.awsEventSource.getEvents(dynamoDbClient, configuration))('getEvents');
101
- const persistEvent = logDebug(chisel_aws_1.awsEventSource.persist(dynamoDbClient, configuration))('persistEvent');
102
- const publishEvent = logDebug(chisel_aws_1.awsActor.publishShardedEvent(actor_1.hashCode)(snsClient, configuration))('publishEvent');
103
- const loadState = logDebug(chisel_1.eventSource.loadState(configuration)(getEvents)(input.getInitialState)(input.reduceEvent))('loadState');
104
- const calculateNewState = logDebug(chisel_1.eventSource.calculateNewState(configuration)(uuid_1.v7)(now)(persistEvent)(input.reduceEvent))('calculateNewState');
92
+ const getEvents = logDebug(chisel_aws_2.awsEventSource.getEvents({
93
+ dynamoDb: dynamoDbClient,
94
+ configuration,
95
+ }))('getEvents');
96
+ const persistEvent = logDebug(chisel_aws_2.awsEventSource.persist({
97
+ dynamoDb: dynamoDbClient,
98
+ configuration,
99
+ }))('persistEvent');
100
+ const publishEvent = logDebug(chisel_aws_2.awsActor.publishShardedEvent({
101
+ hashCode: chisel_aws_2.awsActor.hashCode,
102
+ snsClient,
103
+ configuration,
104
+ }))('publishEvent');
105
+ const loadState = logDebug(chisel_1.eventSource.loadState({
106
+ configuration,
107
+ getEvents: getEvents,
108
+ getInitialState: input.getInitialState,
109
+ reduce: input.reduceEvent,
110
+ match: cerillo_1.match,
111
+ }))('loadState');
112
+ const calculateNewState = logDebug(chisel_1.eventSource.calculateNewState({
113
+ configuration,
114
+ newId: uuid_1.v7,
115
+ now: input.now,
116
+ persistEvent: persistEvent,
117
+ reduce: input.reduceEvent,
118
+ }))('calculateNewState');
105
119
  const getStateHandlerFn = logDebug(getStateHandler(loadState))('getStateHandler');
106
- const actorHandler = logDebug(chisel_1.actor.handle(loadState)(input.handleCommand)(calculateNewState)(publishEvent))('chiselHandleCommand');
107
- const handleCommand = logDebug(chisel_aws_1.handlers.handleLambdaCommand(actorHandler))('handleLambdaCommand');
120
+ const actorHandler = logDebug(chisel_1.actor.handle({
121
+ loadState,
122
+ handleCommand: input.handleCommand,
123
+ calculateNewState,
124
+ publishEvent,
125
+ match: cerillo_1.match,
126
+ }))('chiselHandleCommand');
127
+ const handleCommand = logDebug(chisel_aws_1.handlers.handleLambdaCommand({
128
+ handle: actorHandler,
129
+ }))('handleLambdaCommand');
108
130
  return Object.freeze({
109
- handleCommand: handleCommand,
110
- getState: getStateHandlerFn,
131
+ handleCommand: awsome_1.lambda.unwrap(handleCommand),
132
+ getState: awsome_1.lambda.unwrap(getStateHandlerFn),
111
133
  });
112
134
  };
113
135
  exports.createActor = createActor;