event-store-adapter-js 2.2.167-snapshot.1 → 3.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 (108) hide show
  1. package/README.ja.md +41 -15
  2. package/README.md +42 -16
  3. package/dist/aggregate-id-value.d.ts +7 -0
  4. package/dist/aggregate-id-value.js +9 -0
  5. package/dist/aggregate-id.d.ts +7 -0
  6. package/dist/aggregate.d.ts +10 -0
  7. package/dist/dynamodb-event-store-input.d.ts +22 -0
  8. package/dist/dynamodb-event-store-input.js +2 -0
  9. package/dist/event-serializer.d.ts +7 -0
  10. package/dist/event-serializer.js +2 -0
  11. package/dist/event-store.d.ts +13 -11
  12. package/dist/event-store.js +15 -14
  13. package/dist/event.d.ts +10 -0
  14. package/dist/event.js +2 -0
  15. package/dist/index.d.ts +4 -0
  16. package/dist/index.js +4 -0
  17. package/dist/internal/default-serializer.d.ts +7 -11
  18. package/dist/internal/default-serializer.js +6 -14
  19. package/dist/internal/default-shard-selector.d.ts +7 -0
  20. package/dist/internal/default-shard-selector.js +31 -0
  21. package/dist/internal/dynamodb-aggregate-key.d.ts +14 -0
  22. package/dist/internal/dynamodb-aggregate-key.js +23 -0
  23. package/dist/internal/dynamodb-delete-ttl-millis.d.ts +2 -0
  24. package/dist/internal/dynamodb-delete-ttl-millis.js +18 -0
  25. package/dist/internal/dynamodb-event-store.d.ts +36 -0
  26. package/dist/internal/dynamodb-event-store.js +360 -0
  27. package/dist/internal/dynamodb-snapshot-retention-executor.d.ts +27 -0
  28. package/dist/internal/dynamodb-snapshot-retention-executor.js +297 -0
  29. package/dist/internal/event-store-assertions.d.ts +5 -0
  30. package/dist/internal/event-store-assertions.js +21 -0
  31. package/dist/internal/json-converter.d.ts +2 -0
  32. package/dist/internal/json-converter.js +21 -0
  33. package/dist/internal/memory-event-store.d.ts +17 -0
  34. package/dist/internal/memory-event-store.js +126 -0
  35. package/dist/internal/spanner-aggregate-key.d.ts +13 -0
  36. package/dist/internal/spanner-aggregate-key.js +19 -0
  37. package/dist/internal/spanner-event-store.d.ts +46 -0
  38. package/dist/internal/spanner-event-store.js +440 -0
  39. package/dist/logger.d.ts +8 -0
  40. package/dist/logger.js +2 -0
  41. package/dist/memory-event-store-input.d.ts +6 -0
  42. package/dist/memory-event-store-input.js +2 -0
  43. package/dist/optimistic-lock-error.d.ts +4 -0
  44. package/dist/optimistic-lock-error.js +10 -0
  45. package/dist/shard-count.d.ts +7 -0
  46. package/dist/shard-count.js +9 -0
  47. package/dist/shard-id.d.ts +7 -0
  48. package/dist/shard-id.js +9 -0
  49. package/dist/shard-selector.d.ts +7 -0
  50. package/dist/shard-selector.js +2 -0
  51. package/dist/snapshot-serializer.d.ts +7 -0
  52. package/dist/snapshot-serializer.js +2 -0
  53. package/dist/spanner-event-store-input.d.ts +18 -0
  54. package/dist/spanner-event-store-input.js +2 -0
  55. package/dist/types.d.ts +10 -44
  56. package/dist/types.js +7 -12
  57. package/docs/DATABASE_SCHEMA.ja.md +3 -1
  58. package/docs/DATABASE_SCHEMA.md +3 -1
  59. package/docs/GCP_EVENT_INTEGRATION.ja.md +154 -0
  60. package/docs/MIGRATION_GUIDE_3.0.ja.md +65 -0
  61. package/docs/MIGRATION_GUIDE_3.0.md +65 -0
  62. package/docs/SPANNER_DATABASE_SCHEMA.ja.md +49 -0
  63. package/docs/SPANNER_DATABASE_SCHEMA.md +51 -0
  64. package/package.json +30 -16
  65. package/.github/CODEONWERS +0 -1
  66. package/.github/create-release-note-body.py +0 -40
  67. package/.github/create-release-note-header.py +0 -28
  68. package/.github/next-semver.py +0 -20
  69. package/.github/semver-level.py +0 -31
  70. package/.github/workflows/bump-version.yml +0 -97
  71. package/.github/workflows/ci.yml +0 -35
  72. package/.github/workflows/release.yml +0 -23
  73. package/.github/workflows/snapshot.yml +0 -38
  74. package/.mise.toml +0 -2
  75. package/.vscode/settings.json +0 -11
  76. package/biome.json +0 -27
  77. package/dist/event-store-options.d.ts +0 -11
  78. package/dist/event-store-with-options.d.ts +0 -6
  79. package/dist/internal/default-key-resolver.d.ts +0 -7
  80. package/dist/internal/default-key-resolver.js +0 -29
  81. package/dist/internal/event-store-for-dynamodb.d.ts +0 -42
  82. package/dist/internal/event-store-for-dynamodb.js +0 -455
  83. package/dist/internal/event-store-for-memory.d.ts +0 -12
  84. package/dist/internal/event-store-for-memory.js +0 -89
  85. package/jest.config.ts +0 -5
  86. package/renovate.json +0 -19
  87. package/scripts/run-claude.sh +0 -3
  88. package/scripts/run-codex.sh +0 -7
  89. package/src/event-store-options.ts +0 -31
  90. package/src/event-store-with-options.ts +0 -12
  91. package/src/event-store.ts +0 -91
  92. package/src/index.ts +0 -2
  93. package/src/internal/default-key-resolver.ts +0 -31
  94. package/src/internal/default-serializer.ts +0 -54
  95. package/src/internal/event-store-for-dynamodb.test.ts +0 -151
  96. package/src/internal/event-store-for-dynamodb.ts +0 -671
  97. package/src/internal/event-store-for-memory.test.ts +0 -90
  98. package/src/internal/event-store-for-memory.ts +0 -99
  99. package/src/internal/test/dynamodb-utils.ts +0 -154
  100. package/src/internal/test/user-account-event.ts +0 -64
  101. package/src/internal/test/user-account-id.ts +0 -17
  102. package/src/internal/test/user-account-repository.test.ts +0 -121
  103. package/src/internal/test/user-account-repository.ts +0 -36
  104. package/src/internal/test/user-account.ts +0 -109
  105. package/src/types.ts +0 -81
  106. package/tsconfig.json +0 -21
  107. /package/dist/{event-store-options.js → aggregate-id.js} +0 -0
  108. /package/dist/{event-store-with-options.js → aggregate.js} +0 -0
package/README.ja.md CHANGED
@@ -13,7 +13,7 @@
13
13
  # 導入方法
14
14
 
15
15
  ```shell
16
- $ npm install event-store-adapter-js
16
+ npm install event-store-adapter-js
17
17
  ```
18
18
 
19
19
  # 使い方
@@ -58,22 +58,32 @@ class UserAccountRepository {
58
58
  以下はリポジトリの使用例です。
59
59
 
60
60
  ```typescript
61
- const eventStore = EventStoreFactory.ofDynamoDB<
61
+ const eventStore = EventStore.ofDynamoDB<
62
62
  UserAccountId,
63
63
  UserAccount,
64
64
  UserAccountEvent
65
- >(
66
- dynamodbClient,
67
- JOURNAL_TABLE_NAME,
68
- SNAPSHOT_TABLE_NAME,
69
- JOURNAL_AID_INDEX_NAME,
70
- SNAPSHOTS_AID_INDEX_NAME,
71
- 32,
72
- convertJSONtoUserAccountEvent,
73
- convertJSONToUserAccount,
74
- );
65
+ >({
66
+ client: dynamodbClient,
67
+ journalTableName: JOURNAL_TABLE_NAME,
68
+ snapshotTableName: SNAPSHOT_TABLE_NAME,
69
+ journalAidIndexName: JOURNAL_AID_INDEX_NAME,
70
+ snapshotAidIndexName: SNAPSHOTS_AID_INDEX_NAME,
71
+ snapshotActiveTtlIndexName: SNAPSHOTS_ACTIVE_TTL_INDEX_NAME,
72
+ shardCount: 32,
73
+ eventConverter: convertJSONtoUserAccountEvent,
74
+ snapshotConverter: convertJSONToUserAccount,
75
+ });
75
76
  // if you want to use in-memory event store, use the following code.
76
- // const eventStore = EventStoreFactory.ofMemory<UserAccountId, UserAccount, UserAccountEvent>();
77
+ // const eventStore = EventStore.ofMemory<UserAccountId, UserAccount, UserAccountEvent>({});
78
+ // Cloud Spannerを使う場合は、呼び出し側で管理するDatabaseを渡します。
79
+ // const eventStore = EventStore.ofSpanner<UserAccountId, UserAccount, UserAccountEvent>({
80
+ // database: spannerDatabase,
81
+ // journalTableName: "journal",
82
+ // snapshotTableName: "snapshot",
83
+ // shardCount: 32,
84
+ // eventConverter: convertJSONtoUserAccountEvent,
85
+ // snapshotConverter: convertJSONToUserAccount,
86
+ // });
77
87
 
78
88
  const userAccountRepository = new UserAccountRepository(eventStore);
79
89
 
@@ -98,13 +108,30 @@ expect(userAccount3.sequenceNumber).toEqual(2);
98
108
  expect(userAccount3.version).toEqual(2);
99
109
  ```
100
110
 
111
+ ## 開発
112
+
113
+ このリポジトリは pnpm workspace を使います。ライブラリパッケージは
114
+ `packages/library` に配置し、今後の example や test package の追加先として
115
+ `packages/examples` と `packages/tests` を予約しています。
116
+
117
+ ```shell
118
+ pnpm install
119
+ pnpm run lint
120
+ pnpm run build
121
+ pnpm run test
122
+ pnpm run coverage
123
+ ```
124
+
101
125
  ## テーブル仕様
102
126
 
103
127
  [docs/DATABASE_SCHEMA.ja.md](docs/DATABASE_SCHEMA.ja.md)を参照してください。
104
128
 
129
+ Cloud Spannerについては[docs/SPANNER_DATABASE_SCHEMA.ja.md](docs/SPANNER_DATABASE_SCHEMA.ja.md)を参照してください。
130
+
105
131
  ## ライセンス
106
132
 
107
- MITライセンスです。詳細は[LICENSE](LICENSE)を参照してください。
133
+ MIT と Apache-2.0 のデュアルライセンスです。詳細は
134
+ [LICENSE-MIT](LICENSE-MIT) と [LICENSE-APACHE](LICENSE-APACHE) を参照してください。
108
135
 
109
136
  ## 他の言語のための実装
110
137
 
@@ -116,4 +143,3 @@ MITライセンスです。詳細は[LICENSE](LICENSE)を参照してくださ
116
143
  - [for JavaScript/TypeScript](https://github.com/j5ik2o/event-store-adapter-js)
117
144
  - [for .NET](https://github.com/j5ik2o/event-store-adapter-dotnet)
118
145
  - [for PHP](https://github.com/j5ik2o/event-store-adapter-php)
119
-
package/README.md CHANGED
@@ -14,7 +14,7 @@ This library is designed to turn DynamoDB into an Event Store for CQRS/Event Sou
14
14
  # Installation
15
15
 
16
16
  ```shell
17
- $ npm install event-store-adapter-js
17
+ npm install event-store-adapter-js
18
18
  ```
19
19
 
20
20
  # Usage
@@ -61,22 +61,32 @@ class UserAccountRepository {
61
61
  The following is an example of the repository usage.
62
62
 
63
63
  ```typescript
64
- const eventStore = EventStoreFactory.ofDynamoDB<
64
+ const eventStore = EventStore.ofDynamoDB<
65
65
  UserAccountId,
66
66
  UserAccount,
67
67
  UserAccountEvent
68
- >(
69
- dynamodbClient,
70
- JOURNAL_TABLE_NAME,
71
- SNAPSHOT_TABLE_NAME,
72
- JOURNAL_AID_INDEX_NAME,
73
- SNAPSHOTS_AID_INDEX_NAME,
74
- 32,
75
- convertJSONtoUserAccountEvent,
76
- convertJSONToUserAccount,
77
- );
68
+ >({
69
+ client: dynamodbClient,
70
+ journalTableName: JOURNAL_TABLE_NAME,
71
+ snapshotTableName: SNAPSHOT_TABLE_NAME,
72
+ journalAidIndexName: JOURNAL_AID_INDEX_NAME,
73
+ snapshotAidIndexName: SNAPSHOTS_AID_INDEX_NAME,
74
+ snapshotActiveTtlIndexName: SNAPSHOTS_ACTIVE_TTL_INDEX_NAME,
75
+ shardCount: 32,
76
+ eventConverter: convertJSONtoUserAccountEvent,
77
+ snapshotConverter: convertJSONToUserAccount,
78
+ });
78
79
  // if you want to use in-memory event store, use the following code.
79
- // const eventStore = EventStoreFactory.ofMemory<UserAccountId, UserAccount, UserAccountEvent>();
80
+ // const eventStore = EventStore.ofMemory<UserAccountId, UserAccount, UserAccountEvent>({});
81
+ // if you want to use Cloud Spanner, pass a caller-managed Database.
82
+ // const eventStore = EventStore.ofSpanner<UserAccountId, UserAccount, UserAccountEvent>({
83
+ // database: spannerDatabase,
84
+ // journalTableName: "journal",
85
+ // snapshotTableName: "snapshot",
86
+ // shardCount: 32,
87
+ // eventConverter: convertJSONtoUserAccountEvent,
88
+ // snapshotConverter: convertJSONToUserAccount,
89
+ // });
80
90
 
81
91
  const userAccountRepository = new UserAccountRepository(eventStore);
82
92
 
@@ -101,19 +111,35 @@ expect(userAccount3.sequenceNumber).toEqual(2);
101
111
  expect(userAccount3.version).toEqual(2);
102
112
  ```
103
113
 
114
+ ## Development
115
+
116
+ This repository uses pnpm workspaces. The library package is located at
117
+ `packages/library`, with `packages/examples` and `packages/tests` reserved for
118
+ future example and test packages.
119
+
120
+ ```shell
121
+ pnpm install
122
+ pnpm run lint
123
+ pnpm run build
124
+ pnpm run test
125
+ pnpm run coverage
126
+ ```
127
+
104
128
  ## Table Specifications
105
129
 
106
130
  See [docs/DATABASE_SCHEMA.md](docs/DATABASE_SCHEMA.md).
107
131
 
132
+ For Cloud Spanner, see [docs/SPANNER_DATABASE_SCHEMA.md](docs/SPANNER_DATABASE_SCHEMA.md).
133
+
108
134
  ## CQRS/Event Sourcing Example
109
135
 
110
136
  See [j5ik2o/cqrs-es-example-js](https://github.com/j5ik2o/cqrs-es-example-js).
111
137
 
112
- ## License.
138
+ ## License
113
139
 
114
- MIT License. See [LICENSE](LICENSE) for details.
140
+ Dual-licensed under MIT and Apache-2.0.
141
+ See [LICENSE-MIT](LICENSE-MIT) and [LICENSE-APACHE](LICENSE-APACHE).
115
142
 
116
143
  ## Links
117
144
 
118
145
  - [Common Documents](https://github.com/j5ik2o/event-store-adapter)
119
-
@@ -0,0 +1,7 @@
1
+ declare const aggregateIdValueBrand: unique symbol;
2
+ type AggregateIdValue = string & {
3
+ readonly [aggregateIdValueBrand]: "AggregateIdValue";
4
+ };
5
+ declare function createAggregateIdValue(value: string): AggregateIdValue;
6
+ export type { AggregateIdValue };
7
+ export { createAggregateIdValue };
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAggregateIdValue = createAggregateIdValue;
4
+ function createAggregateIdValue(value) {
5
+ if (value.length === 0) {
6
+ throw new Error("aggregateId value must be non-empty");
7
+ }
8
+ return value;
9
+ }
@@ -0,0 +1,7 @@
1
+ import type { AggregateIdValue } from "./aggregate-id-value";
2
+ interface AggregateId {
3
+ typeName: string;
4
+ value: AggregateIdValue;
5
+ asString: () => string;
6
+ }
7
+ export type { AggregateId };
@@ -0,0 +1,10 @@
1
+ import type { AggregateId } from "./aggregate-id";
2
+ interface Aggregate<This extends Aggregate<This, AID>, AID extends AggregateId> {
3
+ typeName: string;
4
+ id: AID;
5
+ sequenceNumber: number;
6
+ version: number;
7
+ withVersion(version: number): This;
8
+ updateVersion(version: (value: number) => number): This;
9
+ }
10
+ export type { Aggregate };
@@ -0,0 +1,22 @@
1
+ import type { DynamoDBClient } from "@aws-sdk/client-dynamodb";
2
+ import type { Aggregate, AggregateId, Event, EventSerializer, Logger, ShardSelector, SnapshotSerializer } from "./types";
3
+ interface DynamoDBEventStoreInput<AID extends AggregateId, A extends Aggregate<A, AID>, E extends Event<AID>> {
4
+ client: DynamoDBClient;
5
+ journalTableName: string;
6
+ snapshotTableName: string;
7
+ journalAidIndexName: string;
8
+ snapshotAidIndexName: string;
9
+ snapshotActiveTtlIndexName: string;
10
+ shardCount: number;
11
+ /** Converts the deserialized event JSON payload from unknown into an event. */
12
+ eventConverter: (json: unknown) => E;
13
+ /** Converts the deserialized snapshot JSON payload from unknown into an aggregate. */
14
+ snapshotConverter: (json: unknown) => A;
15
+ keepSnapshotCount?: number;
16
+ deleteTtlMillis?: number;
17
+ shardSelector?: ShardSelector<AID>;
18
+ eventSerializer?: EventSerializer<AID, E>;
19
+ snapshotSerializer?: SnapshotSerializer<AID, A>;
20
+ logger?: Logger;
21
+ }
22
+ export type { DynamoDBEventStoreInput };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,7 @@
1
+ import type { AggregateId } from "./aggregate-id";
2
+ import type { Event } from "./event";
3
+ interface EventSerializer<AID extends AggregateId, E extends Event<AID>> {
4
+ serialize(event: E): Uint8Array;
5
+ deserialize(bytes: Uint8Array, converter: (json: unknown) => E): E;
6
+ }
7
+ export type { EventSerializer };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,15 +1,17 @@
1
- import type { DynamoDBClient } from "@aws-sdk/client-dynamodb";
2
- import type moment from "moment";
3
- import type { EventStoreWithOptions } from "./event-store-with-options";
4
- import type { Aggregate, AggregateId, Event, EventSerializer, KeyResolver, Logger, SnapshotSerializer } from "./types";
5
- interface EventStore<AID extends AggregateId, A extends Aggregate<A, AID>, E extends Event<AID>> {
6
- persistEvent(event: E, version: number): Promise<void>;
1
+ import type { DynamoDBEventStoreInput } from "./dynamodb-event-store-input";
2
+ import type { MemoryEventStoreInput } from "./memory-event-store-input";
3
+ import type { SpannerEventStoreInput } from "./spanner-event-store-input";
4
+ import type { Aggregate, AggregateId, Event } from "./types";
5
+ export interface EventStore<AID extends AggregateId, A extends Aggregate<A, AID>, E extends Event<AID>> {
6
+ persistEvent(event: E, expectedVersion: number): Promise<void>;
7
7
  persistEventAndSnapshot(event: E, aggregate: A): Promise<void>;
8
8
  getEventsByIdSinceSequenceNumber(id: AID, sequenceNumber: number): Promise<E[]>;
9
9
  getLatestSnapshotById(id: AID): Promise<A | undefined>;
10
10
  }
11
- declare class EventStoreFactory {
12
- static ofDynamoDB<AID extends AggregateId, A extends Aggregate<A, AID>, E extends Event<AID>>(dynamodbClient: DynamoDBClient, journalTableName: string, snapshotTableName: string, journalAidIndexName: string, snapshotAidIndexName: string, shardCount: number, eventConverter: (json: string) => E, snapshotConverter: (json: string) => A, keepSnapshotCount?: number | undefined, deleteTtl?: moment.Duration | undefined, keyResolver?: KeyResolver<AID>, eventSerializer?: EventSerializer<AID, E>, snapshotSerializer?: SnapshotSerializer<AID, A>, logger?: Logger | undefined): EventStoreWithOptions<AID, A, E>;
13
- static ofMemory<AID extends AggregateId, A extends Aggregate<A, AID>, E extends Event<AID>>(events?: Map<AID, E[]>, snapshots?: Map<AID, A>): EventStore<AID, A, E>;
14
- }
15
- export { type EventStore, EventStoreFactory };
11
+ type EventStoreConstructors = Readonly<{
12
+ ofDynamoDB<AID extends AggregateId, A extends Aggregate<A, AID>, E extends Event<AID>>(input: DynamoDBEventStoreInput<AID, A, E>): EventStore<AID, A, E>;
13
+ ofMemory<AID extends AggregateId, A extends Aggregate<A, AID>, E extends Event<AID>>(input?: MemoryEventStoreInput<AID, A, E>): EventStore<AID, A, E>;
14
+ ofSpanner<AID extends AggregateId, A extends Aggregate<A, AID>, E extends Event<AID>>(input: SpannerEventStoreInput<AID, A, E>): EventStore<AID, A, E>;
15
+ }>;
16
+ export declare const EventStore: EventStoreConstructors;
17
+ export {};
@@ -1,16 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EventStoreFactory = void 0;
4
- const default_key_resolver_1 = require("./internal/default-key-resolver");
5
- const default_serializer_1 = require("./internal/default-serializer");
6
- const event_store_for_dynamodb_1 = require("./internal/event-store-for-dynamodb");
7
- const event_store_for_memory_1 = require("./internal/event-store-for-memory");
8
- class EventStoreFactory {
9
- static ofDynamoDB(dynamodbClient, journalTableName, snapshotTableName, journalAidIndexName, snapshotAidIndexName, shardCount, eventConverter, snapshotConverter, keepSnapshotCount = undefined, deleteTtl = undefined, keyResolver = new default_key_resolver_1.DefaultKeyResolver(), eventSerializer = new default_serializer_1.JsonEventSerializer(), snapshotSerializer = new default_serializer_1.JsonSnapshotSerializer(), logger = undefined) {
10
- return new event_store_for_dynamodb_1.EventStoreForDynamoDB(dynamodbClient, journalTableName, snapshotTableName, journalAidIndexName, snapshotAidIndexName, shardCount, eventConverter, snapshotConverter, keepSnapshotCount, deleteTtl, keyResolver, eventSerializer, snapshotSerializer, logger);
11
- }
12
- static ofMemory(events = new Map(), snapshots = new Map()) {
13
- return new event_store_for_memory_1.EventStoreForMemory(events, snapshots);
14
- }
15
- }
16
- exports.EventStoreFactory = EventStoreFactory;
3
+ exports.EventStore = void 0;
4
+ const dynamodb_event_store_1 = require("./internal/dynamodb-event-store");
5
+ const memory_event_store_1 = require("./internal/memory-event-store");
6
+ const spanner_event_store_1 = require("./internal/spanner-event-store");
7
+ exports.EventStore = Object.freeze({
8
+ ofDynamoDB(input) {
9
+ return new dynamodb_event_store_1.DynamoDBEventStore(input);
10
+ },
11
+ ofMemory(input) {
12
+ return new memory_event_store_1.MemoryEventStore(input !== null && input !== void 0 ? input : {});
13
+ },
14
+ ofSpanner(input) {
15
+ return new spanner_event_store_1.SpannerEventStore(input);
16
+ },
17
+ });
@@ -0,0 +1,10 @@
1
+ import type { AggregateId } from "./aggregate-id";
2
+ interface Event<AID extends AggregateId> {
3
+ typeName: string;
4
+ id: string;
5
+ aggregateId: AID;
6
+ sequenceNumber: number;
7
+ occurredAt: Date;
8
+ isCreated: boolean;
9
+ }
10
+ export type { Event };
package/dist/event.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/index.d.ts CHANGED
@@ -1,2 +1,6 @@
1
+ export * from "./dynamodb-event-store-input";
1
2
  export * from "./event-store";
3
+ export * from "./memory-event-store-input";
4
+ export * from "./shard-id";
5
+ export * from "./spanner-event-store-input";
2
6
  export * from "./types";
package/dist/index.js CHANGED
@@ -14,5 +14,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./dynamodb-event-store-input"), exports);
17
18
  __exportStar(require("./event-store"), exports);
19
+ __exportStar(require("./memory-event-store-input"), exports);
20
+ __exportStar(require("./shard-id"), exports);
21
+ __exportStar(require("./spanner-event-store-input"), exports);
18
22
  __exportStar(require("./types"), exports);
@@ -1,14 +1,10 @@
1
- import type { Aggregate, AggregateId, Event, EventSerializer, SnapshotSerializer } from "../types";
2
- declare class JsonEventSerializer<AID extends AggregateId, E extends Event<AID>> implements EventSerializer<AID, E> {
3
- private encoder;
4
- private decoder;
5
- deserialize(bytes: Uint8Array, converter: (json: any) => E): E;
6
- serialize(event: E): Uint8Array;
1
+ import type { Aggregate, AggregateId, Event } from "../types";
2
+ declare class JsonEventSerializer {
3
+ deserialize<E>(bytes: Uint8Array, converter: (json: unknown) => E): E;
4
+ serialize<AID extends AggregateId, E extends Event<AID>>(event: E): Uint8Array;
7
5
  }
8
- declare class JsonSnapshotSerializer<AID extends AggregateId, A extends Aggregate<A, AID>> implements SnapshotSerializer<AID, A> {
9
- private encoder;
10
- private decoder;
11
- deserialize(bytes: Uint8Array, converter: (json: any) => A): A;
12
- serialize(aggregate: A): Uint8Array;
6
+ declare class JsonSnapshotSerializer {
7
+ deserialize<A>(bytes: Uint8Array, converter: (json: unknown) => A): A;
8
+ serialize<AID extends AggregateId, A extends Aggregate<A, AID>>(aggregate: A): Uint8Array;
13
9
  }
14
10
  export { JsonEventSerializer, JsonSnapshotSerializer };
@@ -1,14 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.JsonSnapshotSerializer = exports.JsonEventSerializer = void 0;
4
+ const encoder = new TextEncoder();
5
+ const decoder = new TextDecoder();
4
6
  class JsonEventSerializer {
5
- constructor() {
6
- this.encoder = new TextEncoder();
7
- this.decoder = new TextDecoder();
8
- }
9
- // biome-ignore lint/suspicious/noExplicitAny: JSON deserialization requires dynamic typing
10
7
  deserialize(bytes, converter) {
11
- const jsonString = this.decoder.decode(bytes);
8
+ const jsonString = decoder.decode(bytes);
12
9
  const json = JSON.parse(jsonString);
13
10
  return converter(json);
14
11
  }
@@ -17,18 +14,13 @@ class JsonEventSerializer {
17
14
  type: event.typeName,
18
15
  data: event,
19
16
  });
20
- return this.encoder.encode(jsonString);
17
+ return encoder.encode(jsonString);
21
18
  }
22
19
  }
23
20
  exports.JsonEventSerializer = JsonEventSerializer;
24
21
  class JsonSnapshotSerializer {
25
- constructor() {
26
- this.encoder = new TextEncoder();
27
- this.decoder = new TextDecoder();
28
- }
29
- // biome-ignore lint/suspicious/noExplicitAny: JSON deserialization requires dynamic typing
30
22
  deserialize(bytes, converter) {
31
- const jsonString = this.decoder.decode(bytes);
23
+ const jsonString = decoder.decode(bytes);
32
24
  const obj = JSON.parse(jsonString);
33
25
  return converter(obj);
34
26
  }
@@ -37,7 +29,7 @@ class JsonSnapshotSerializer {
37
29
  type: aggregate.typeName,
38
30
  data: aggregate,
39
31
  });
40
- return this.encoder.encode(jsonString);
32
+ return encoder.encode(jsonString);
41
33
  }
42
34
  }
43
35
  exports.JsonSnapshotSerializer = JsonSnapshotSerializer;
@@ -0,0 +1,7 @@
1
+ import { type ShardId } from "../shard-id";
2
+ import type { AggregateId, ShardCount, ShardSelector } from "../types";
3
+ declare class DefaultShardSelector<AID extends AggregateId> implements ShardSelector<AID> {
4
+ selectShardId(aggregateId: AID, shardCount: ShardCount): ShardId;
5
+ private hashString;
6
+ }
7
+ export { DefaultShardSelector };
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DefaultShardSelector = void 0;
4
+ const shard_id_1 = require("../shard-id");
5
+ class DefaultShardSelector {
6
+ selectShardId(aggregateId, shardCount) {
7
+ if (aggregateId === undefined || aggregateId === null) {
8
+ throw new Error(`aggregateId is undefined or null: ${aggregateId}`);
9
+ }
10
+ if (!Number.isSafeInteger(shardCount) || shardCount <= 0) {
11
+ throw new Error(`shardCount must be a positive safe integer, got ${shardCount}`);
12
+ }
13
+ const hash = this.hashString(aggregateId.asString());
14
+ return (0, shard_id_1.createShardId)(hash % shardCount);
15
+ }
16
+ hashString(str) {
17
+ // AggregateId.asString() is user code, so validate runtime values despite the TypeScript type.
18
+ if (typeof str !== "string") {
19
+ throw new Error(`str is not a string: ${str}`);
20
+ }
21
+ let hash = 0;
22
+ for (let i = 0; i < str.length; i++) {
23
+ const char = str.charCodeAt(i);
24
+ hash = (hash << 5) - hash + char;
25
+ // Keep the legacy DefaultKeyResolver result, using an explicit 32-bit mask instead of the opaque hash & hash idiom.
26
+ hash = hash & 0xffffffff;
27
+ }
28
+ return hash >>> 0;
29
+ }
30
+ }
31
+ exports.DefaultShardSelector = DefaultShardSelector;
@@ -0,0 +1,14 @@
1
+ import type { ShardCount } from "../shard-count";
2
+ import type { ShardId } from "../shard-id";
3
+ import type { ShardSelector } from "../shard-selector";
4
+ import type { AggregateId } from "../types";
5
+ declare class DynamoDBAggregateKey<AID extends AggregateId> {
6
+ readonly shardId: ShardId;
7
+ readonly aggregateId: AID;
8
+ readonly sequenceNumber: number;
9
+ private constructor();
10
+ static create<AID extends AggregateId>(aggregateId: AID, sequenceNumber: number, shardSelector: ShardSelector<AID>, shardCount: ShardCount): DynamoDBAggregateKey<AID>;
11
+ get partitionKeyValue(): string;
12
+ get sortKeyValue(): string;
13
+ }
14
+ export { DynamoDBAggregateKey };
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DynamoDBAggregateKey = void 0;
4
+ class DynamoDBAggregateKey {
5
+ constructor(shardId, aggregateId, sequenceNumber) {
6
+ this.shardId = shardId;
7
+ this.aggregateId = aggregateId;
8
+ this.sequenceNumber = sequenceNumber;
9
+ }
10
+ static create(aggregateId, sequenceNumber, shardSelector, shardCount) {
11
+ if (!Number.isSafeInteger(sequenceNumber) || sequenceNumber < 0) {
12
+ throw new Error(`sequenceNumber must be a non-negative safe integer, got ${sequenceNumber}`);
13
+ }
14
+ return new DynamoDBAggregateKey(shardSelector.selectShardId(aggregateId, shardCount), aggregateId, sequenceNumber);
15
+ }
16
+ get partitionKeyValue() {
17
+ return `${this.aggregateId.typeName}-${this.shardId}`;
18
+ }
19
+ get sortKeyValue() {
20
+ return `${this.aggregateId.typeName}-${this.aggregateId.value}-${this.sequenceNumber}`;
21
+ }
22
+ }
23
+ exports.DynamoDBAggregateKey = DynamoDBAggregateKey;
@@ -0,0 +1,2 @@
1
+ declare function normalizeDynamoDBDeleteTtlMillis(deleteTtlMillis: number | undefined): number | undefined;
2
+ export { normalizeDynamoDBDeleteTtlMillis };
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeDynamoDBDeleteTtlMillis = normalizeDynamoDBDeleteTtlMillis;
4
+ function normalizeDynamoDBDeleteTtlMillis(deleteTtlMillis) {
5
+ if (deleteTtlMillis === undefined) {
6
+ return undefined;
7
+ }
8
+ if (!Number.isFinite(deleteTtlMillis)) {
9
+ throw new Error(`deleteTtlMillis must be finite, got ${formatDeleteTtlMillis(deleteTtlMillis)}`);
10
+ }
11
+ if (deleteTtlMillis < 0 || Object.is(deleteTtlMillis, -0)) {
12
+ throw new Error(`deleteTtlMillis must be non-negative, got ${formatDeleteTtlMillis(deleteTtlMillis)}`);
13
+ }
14
+ return Math.floor(deleteTtlMillis);
15
+ }
16
+ function formatDeleteTtlMillis(deleteTtlMillis) {
17
+ return Object.is(deleteTtlMillis, -0) ? "-0" : String(deleteTtlMillis);
18
+ }
@@ -0,0 +1,36 @@
1
+ import type { DynamoDBEventStoreInput } from "../dynamodb-event-store-input";
2
+ import type { EventStore } from "../event-store";
3
+ import { type Aggregate, type AggregateId, type Event } from "../types";
4
+ declare class DynamoDBEventStore<AID extends AggregateId, A extends Aggregate<A, AID>, E extends Event<AID>> implements EventStore<AID, A, E> {
5
+ private readonly dynamodbClient;
6
+ private readonly journalTableName;
7
+ private readonly snapshotTableName;
8
+ private readonly journalAidIndexName;
9
+ private readonly snapshotAidIndexName;
10
+ private readonly snapshotActiveTtlIndexName;
11
+ private readonly shardCount;
12
+ private readonly eventConverter;
13
+ private readonly snapshotConverter;
14
+ private readonly keepSnapshotCount;
15
+ private readonly deleteTtlMillis;
16
+ private readonly shardSelector;
17
+ private readonly eventSerializer;
18
+ private readonly snapshotSerializer;
19
+ private readonly logger;
20
+ constructor(input: DynamoDBEventStoreInput<AID, A, E>);
21
+ getEventsByIdSinceSequenceNumber(id: AID, sequenceNumber: number): Promise<E[]>;
22
+ getLatestSnapshotById(id: AID): Promise<A | undefined>;
23
+ persistEvent(event: E, expectedVersion: number): Promise<void>;
24
+ persistEventAndSnapshot(event: E, aggregate: A): Promise<void>;
25
+ private createEventAndSnapshot;
26
+ private updateEventAndSnapshotOpt;
27
+ private putJournal;
28
+ private putSnapshot;
29
+ private putRedundantSnapshot;
30
+ private updateSnapshot;
31
+ private purgeExcessSnapshots;
32
+ private assertConverter;
33
+ private parseShardCount;
34
+ private normalizeDeleteTtlMillis;
35
+ }
36
+ export { DynamoDBEventStore };