@wataruoguchi/emmett-event-store-kysely 2.0.2 → 2.2.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 CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  A Kysely-based event store implementation for [Emmett](https://github.com/event-driven-io/emmett), providing event sourcing capabilities with PostgreSQL.
4
4
 
5
+ ## 📚 Documentation
6
+
7
+ **👉 [View Complete Documentation →](https://wataruoguchi.github.io/emmett-libs/emmett-event-store-kysely)**
8
+
5
9
  ## Features
6
10
 
7
11
  - **Event Store** - Full event sourcing with Kysely and PostgreSQL
@@ -20,13 +24,12 @@ npm install @wataruoguchi/emmett-event-store-kysely @event-driven-io/emmett kyse
20
24
 
21
25
  ### 1. Database Setup
22
26
 
23
- Set up the required PostgreSQL tables using [our migration example](https://github.com/wataruoguchi/emmett-event-store-kysely/blob/main/package/database/migrations/1758758113676_event_sourcing_migration_example.ts):
27
+ Set up the required PostgreSQL tables using [our migration example](https://github.com/wataruoguchi/emmett-libs/blob/main/packages/emmett-event-store-kysely/database/migrations/1758758113676_event_sourcing_migration_example.ts):
24
28
 
25
29
  ```typescript
26
30
  import { Kysely } from "kysely";
27
31
 
28
32
  // Required tables: messages, streams, subscriptions
29
- // See https://github.com/wataruoguchi/emmett-event-store-kysely/blob/main/package/docs/database-setup.md for details
30
33
  ```
31
34
 
32
35
  A read model table expects to have the following columns:
@@ -110,24 +113,10 @@ await runner.projectEvents("subscription-id", "cart-123", {
110
113
  });
111
114
  ```
112
115
 
113
- See [Snapshot Projections documentation](https://github.com/wataruoguchi/emmett-event-store-kysely/blob/main/package/docs/snapshot-projections.md) for details.
114
-
115
- ## Documentation
116
-
117
- 📚 **[Complete Documentation](https://github.com/wataruoguchi/emmett-event-store-kysely/blob/main/package/docs/README.md)**
118
-
119
- ### Core Guides
120
-
121
- - [Database Setup](https://github.com/wataruoguchi/emmett-event-store-kysely/blob/main/package/docs/database-setup.md) - PostgreSQL schema and requirements
122
- - [Event Store](https://github.com/wataruoguchi/emmett-event-store-kysely/blob/main/package/docs/event-store.md) - Core event store API
123
- - [Snapshot Projections](https://github.com/wataruoguchi/emmett-event-store-kysely/blob/main/package/docs/snapshot-projections.md) - Build read models (recommended) ⭐
124
- - [Event Consumer](https://github.com/wataruoguchi/emmett-event-store-kysely/blob/main/package/docs/consumer.md) - Continuous background processing
125
- - [Projection Runner](https://github.com/wataruoguchi/emmett-event-store-kysely/blob/main/package/docs/projection-runner.md) - On-demand processing for tests
126
-
127
- ### Examples
116
+ ## Examples
128
117
 
129
- - [Working Example](https://github.com/wataruoguchi/emmett-event-store-kysely/tree/main/example/) - Complete application with carts and generators
130
- - [Migration Example](https://github.com/wataruoguchi/emmett-event-store-kysely/blob/main/package/database/migrations/1758758113676_event_sourcing_migration_example.ts) - Database setup
118
+ - [Working Example](https://github.com/wataruoguchi/emmett-libs/tree/main/example/) - Complete application with carts and generators
119
+ - [Migration Example](https://github.com/wataruoguchi/emmett-libs/blob/main/packages/emmett-event-store-kysely/database/migrations/1758758113676_event_sourcing_migration_example.ts) - Database setup
131
120
 
132
121
  ## License
133
122
 
@@ -135,4 +124,4 @@ MIT
135
124
 
136
125
  ## Contributing
137
126
 
138
- Contributions are welcome! Please see our [GitHub repository](https://github.com/wataruoguchi/emmett-event-store-kysely) for issues and PRs.
127
+ Contributions are welcome! Please see our [GitHub repository](https://github.com/wataruoguchi/emmett-libs) for issues and PRs.
@@ -1 +1 @@
1
- {"version":3,"file":"kysely-event-store.d.ts","sourceRoot":"","sources":["../../src/event-store/kysely-event-store.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,qBAAqB,EAC1B,KAAK,sCAAsC,EAC3C,KAAK,KAAK,EACV,KAAK,UAAU,EAEf,KAAK,wBAAwB,EAE7B,KAAK,mCAAmC,EACxC,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,eAAe,EACrB,MAAM,aAAa,CAAC;AAErB,KAAK,uBAAuB,GAAG,mCAAmC,CAAC;AACnE,KAAK,6BAA6B,GAAG,qBAAqB,GAAG,eAAe,CAAC;AAI7E,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,WAAW,gBACf,SAAQ,UAAU,CAAC,uBAAuB,CAAC,EACzC,wBAAwB,CAAC,gBAAgB,CAAC;IAE5C,UAAU,CAAC,SAAS,SAAS,KAAK,EAChC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,GAAG,2BAA2B,GAChE,OAAO,CAAC,gBAAgB,CAAC,SAAS,EAAE,uBAAuB,CAAC,CAAC,CAAC;IACjE,cAAc,CAAC,SAAS,SAAS,KAAK,EACpC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,SAAS,EAAE,EACnB,OAAO,CAAC,EAAE,6BAA6B,GACtC,OAAO,CAAC,sCAAsC,CAAC,CAAC;IACnD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,MAAM,EAAE;QACN,GAAG,IAAI,MAAM,CAAC;QACd,KAAK,IAAI,IAAI,CAAC;QACd,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;KAC1B,CAAC;CACH;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,kCAAkC;IAClC,iBAAiB,CAAC,EAAE;QAClB,iDAAiD;QACjD,EAAE,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;KACtB,CAAC;IACF,gCAAgC;IAChC,MAAM,CAAC,EAAE;QACP,8BAA8B;QAC9B,aAAa,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAAC;KAC3C,CAAC;IACF,iCAAiC;IACjC,KAAK,CAAC,EAAE;QACN,qCAAqC;QACrC,oBAAoB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;KACnD,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,uBAIlC,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,MAAM,YAAY,KAAG,gBAiKxD,CAAC"}
1
+ {"version":3,"file":"kysely-event-store.d.ts","sourceRoot":"","sources":["../../src/event-store/kysely-event-store.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,qBAAqB,EAC1B,KAAK,sCAAsC,EAC3C,KAAK,KAAK,EACV,KAAK,UAAU,EAEf,KAAK,wBAAwB,EAE7B,KAAK,mCAAmC,EACxC,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACtB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAGL,KAAK,YAAY,EACjB,KAAK,eAAe,EACrB,MAAM,aAAa,CAAC;AAErB,KAAK,uBAAuB,GAAG,mCAAmC,CAAC;AACnE,KAAK,6BAA6B,GAAG,qBAAqB,GAAG,eAAe,CAAC;AAI7E,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,WAAW,gBACf,SAAQ,UAAU,CAAC,uBAAuB,CAAC,EACzC,wBAAwB,CAAC,gBAAgB,CAAC;IAE5C,UAAU,CAAC,SAAS,SAAS,KAAK,EAChC,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,GAAG,2BAA2B,GAChE,OAAO,CAAC,gBAAgB,CAAC,SAAS,EAAE,uBAAuB,CAAC,CAAC,CAAC;IACjE,cAAc,CAAC,SAAS,SAAS,KAAK,EACpC,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,SAAS,EAAE,EACnB,OAAO,CAAC,EAAE,6BAA6B,GACtC,OAAO,CAAC,sCAAsC,CAAC,CAAC;IACnD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,MAAM,EAAE;QACN,GAAG,IAAI,MAAM,CAAC;QACd,KAAK,IAAI,IAAI,CAAC;QACd,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;KAC1B,CAAC;CACH;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,kCAAkC;IAClC,iBAAiB,CAAC,EAAE;QAClB,iDAAiD;QACjD,EAAE,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;KACtB,CAAC;IACF,gCAAgC;IAChC,MAAM,CAAC,EAAE;QACP,8BAA8B;QAC9B,aAAa,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAAC;KAC3C,CAAC;IACF,iCAAiC;IACjC,KAAK,CAAC,EAAE;QACN,qCAAqC;QACrC,oBAAoB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;KACnD,CAAC;CACH,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,uBAIlC,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,MAAM,YAAY,KAAG,gBAkLxD,CAAC"}
@@ -7,7 +7,7 @@ export const defaultKyselyOptions = {
7
7
  },
8
8
  };
9
9
  export const getKyselyEventStore = (deps) => {
10
- const { db, logger } = deps;
10
+ const { db, logger, inTransaction = false } = deps;
11
11
  const eventStore = {
12
12
  /**
13
13
  * @description We do not use schema management in this package.
@@ -18,17 +18,25 @@ export const getKyselyEventStore = (deps) => {
18
18
  migrate: async () => Promise.resolve(),
19
19
  },
20
20
  /**
21
- * @description We do not use session management in this package.
21
+ * Provide a session-bound event store using a Kysely transaction.
22
+ * All operations within the callback will share the same DB transaction.
22
23
  */
23
24
  async withSession(callback) {
24
- return await callback({
25
- eventStore,
26
- close: () => Promise.resolve(),
25
+ return await db.transaction().execute(async (trx) => {
26
+ const sessionEventStore = getKyselyEventStore({
27
+ db: trx,
28
+ logger,
29
+ inTransaction: true,
30
+ });
31
+ return await callback({
32
+ eventStore: sessionEventStore,
33
+ close: () => Promise.resolve(),
34
+ });
27
35
  });
28
36
  },
29
37
  async aggregateStream(streamName, options) {
30
38
  const { evolve, initialState, read } = options;
31
- logger.info({ streamName, options }, "aggregateStream");
39
+ logger.debug?.({ streamName, options }, "aggregateStream");
32
40
  const expectedStreamVersion = read?.expectedStreamVersion;
33
41
  const result = await eventStore.readStream(streamName, read);
34
42
  assertExpectedVersionMatchesCurrent(result.currentStreamVersion, expectedStreamVersion, PostgreSQLEventStoreDefaultStreamVersion);
@@ -41,7 +49,7 @@ export const getKyselyEventStore = (deps) => {
41
49
  },
42
50
  async readStream(streamName, options) {
43
51
  const partition = getPartition(options);
44
- logger.info({ streamName, options, partition }, "readStream");
52
+ logger.debug?.({ streamName, options, partition }, "readStream");
45
53
  const { currentStreamVersion, streamExists } = await fetchStreamInfo(db, streamName, partition);
46
54
  const range = parseRangeOptions(options);
47
55
  const rows = await buildEventsQuery({ db, logger }, streamName, partition, range).execute();
@@ -56,25 +64,29 @@ export const getKyselyEventStore = (deps) => {
56
64
  const streamType = getStreamType(options);
57
65
  const partition = getPartition(options);
58
66
  const expected = options?.expectedStreamVersion;
59
- logger.info({ streamName, events, options, partition }, "appendToStream");
67
+ logger.debug?.({ streamName, events, options, partition }, "appendToStream");
60
68
  ensureEventsNotEmpty(events, expected);
61
- const result = await db
62
- .transaction()
63
- .execute(async (trx) => {
64
- const { currentStreamVersion, streamExists } = await fetchStreamInfo(trx, streamName, partition);
69
+ // It may be called within a transaction via withSession.
70
+ const executeOn = async (executor) => {
71
+ const { currentStreamVersion, streamExists } = await fetchStreamInfo(executor, streamName, partition);
65
72
  assertExpectedVersion(expected, currentStreamVersion, streamExists);
66
73
  const basePos = currentStreamVersion;
67
74
  const nextStreamPosition = computeNextStreamPosition(basePos, events.length);
68
- await upsertStreamRow(trx, streamName, partition, streamType, basePos, nextStreamPosition, expected, streamExists);
75
+ await upsertStreamRow(executor, streamName, partition, streamType, basePos, nextStreamPosition, expected, streamExists);
69
76
  const messagesToInsert = buildMessagesToInsert(events, basePos, streamName, partition);
70
- const lastEventGlobalPosition = await insertMessagesAndGetLastGlobalPosition(trx, messagesToInsert);
77
+ const lastEventGlobalPosition = await insertMessagesAndGetLastGlobalPosition(executor, messagesToInsert);
71
78
  return {
72
79
  nextExpectedStreamVersion: nextStreamPosition,
73
80
  lastEventGlobalPosition,
74
81
  createdNewStream: !streamExists,
75
82
  };
76
- });
77
- return result;
83
+ };
84
+ if (inTransaction) {
85
+ return executeOn(db);
86
+ }
87
+ return db
88
+ .transaction()
89
+ .execute(async (trx) => executeOn(trx));
78
90
  },
79
91
  close: async () => {
80
92
  // Kysely doesn't require explicit closing for most cases
@@ -155,7 +167,6 @@ function buildMessagesToInsert(events, basePos, streamId, partition) {
155
167
  const rawMeta = "metadata" in e ? e.metadata : undefined;
156
168
  const eventMeta = rawMeta && typeof rawMeta === "object" ? rawMeta : {};
157
169
  const messageMetadata = {
158
- messageId,
159
170
  ...eventMeta,
160
171
  };
161
172
  return {
package/dist/index.cjs CHANGED
@@ -184,7 +184,7 @@ function createProjectionRegistry(...registries) {
184
184
 
185
185
  // src/event-store/kysely-event-store.ts
186
186
  var getKyselyEventStore = (deps) => {
187
- const { db, logger } = deps;
187
+ const { db, logger, inTransaction = false } = deps;
188
188
  const eventStore = {
189
189
  /**
190
190
  * @description We do not use schema management in this package.
@@ -195,17 +195,25 @@ var getKyselyEventStore = (deps) => {
195
195
  migrate: async () => Promise.resolve()
196
196
  },
197
197
  /**
198
- * @description We do not use session management in this package.
198
+ * Provide a session-bound event store using a Kysely transaction.
199
+ * All operations within the callback will share the same DB transaction.
199
200
  */
200
201
  async withSession(callback) {
201
- return await callback({
202
- eventStore,
203
- close: () => Promise.resolve()
202
+ return await db.transaction().execute(async (trx) => {
203
+ const sessionEventStore = getKyselyEventStore({
204
+ db: trx,
205
+ logger,
206
+ inTransaction: true
207
+ });
208
+ return await callback({
209
+ eventStore: sessionEventStore,
210
+ close: () => Promise.resolve()
211
+ });
204
212
  });
205
213
  },
206
214
  async aggregateStream(streamName, options) {
207
215
  const { evolve, initialState, read } = options;
208
- logger.info({ streamName, options }, "aggregateStream");
216
+ logger.debug?.({ streamName, options }, "aggregateStream");
209
217
  const expectedStreamVersion = read?.expectedStreamVersion;
210
218
  const result = await eventStore.readStream(streamName, read);
211
219
  (0, import_emmett.assertExpectedVersionMatchesCurrent)(
@@ -225,7 +233,7 @@ var getKyselyEventStore = (deps) => {
225
233
  },
226
234
  async readStream(streamName, options) {
227
235
  const partition = getPartition(options);
228
- logger.info({ streamName, options, partition }, "readStream");
236
+ logger.debug?.({ streamName, options, partition }, "readStream");
229
237
  const { currentStreamVersion, streamExists } = await fetchStreamInfo(
230
238
  db,
231
239
  streamName,
@@ -251,11 +259,14 @@ var getKyselyEventStore = (deps) => {
251
259
  const streamType = getStreamType(options);
252
260
  const partition = getPartition(options);
253
261
  const expected = options?.expectedStreamVersion;
254
- logger.info({ streamName, events, options, partition }, "appendToStream");
262
+ logger.debug?.(
263
+ { streamName, events, options, partition },
264
+ "appendToStream"
265
+ );
255
266
  ensureEventsNotEmpty(events, expected);
256
- const result = await db.transaction().execute(async (trx) => {
267
+ const executeOn = async (executor) => {
257
268
  const { currentStreamVersion, streamExists } = await fetchStreamInfo(
258
- trx,
269
+ executor,
259
270
  streamName,
260
271
  partition
261
272
  );
@@ -266,7 +277,7 @@ var getKyselyEventStore = (deps) => {
266
277
  events.length
267
278
  );
268
279
  await upsertStreamRow(
269
- trx,
280
+ executor,
270
281
  streamName,
271
282
  partition,
272
283
  streamType,
@@ -281,14 +292,20 @@ var getKyselyEventStore = (deps) => {
281
292
  streamName,
282
293
  partition
283
294
  );
284
- const lastEventGlobalPosition = await insertMessagesAndGetLastGlobalPosition(trx, messagesToInsert);
295
+ const lastEventGlobalPosition = await insertMessagesAndGetLastGlobalPosition(
296
+ executor,
297
+ messagesToInsert
298
+ );
285
299
  return {
286
300
  nextExpectedStreamVersion: nextStreamPosition,
287
301
  lastEventGlobalPosition,
288
302
  createdNewStream: !streamExists
289
303
  };
290
- });
291
- return result;
304
+ };
305
+ if (inTransaction) {
306
+ return executeOn(db);
307
+ }
308
+ return db.transaction().execute(async (trx) => executeOn(trx));
292
309
  },
293
310
  close: async () => {
294
311
  await Promise.resolve();
@@ -351,7 +368,6 @@ function buildMessagesToInsert(events, basePos, streamId, partition) {
351
368
  const rawMeta = "metadata" in e ? e.metadata : void 0;
352
369
  const eventMeta = rawMeta && typeof rawMeta === "object" ? rawMeta : {};
353
370
  const messageMetadata = {
354
- messageId,
355
371
  ...eventMeta
356
372
  };
357
373
  return {
@@ -430,7 +446,11 @@ async function fetchStreamInfo(executor, streamId, partition) {
430
446
  }
431
447
 
432
448
  // src/projections/runner.ts
433
- function createProjectionRunner({ db, readStream, registry }) {
449
+ function createProjectionRunner({
450
+ db,
451
+ readStream,
452
+ registry
453
+ }) {
434
454
  async function getOrCreateCheckpoint(subscriptionId, partition) {
435
455
  const existing = await db.selectFrom("subscriptions").select([
436
456
  "subscription_id as subscriptionId",
@@ -1,16 +1,21 @@
1
+ import type { Kysely } from "kysely";
1
2
  import type { KyselyEventStore } from "../event-store/kysely-event-store.js";
2
- import type { DatabaseExecutor, ProjectionRegistry } from "../types.js";
3
+ import type { ProjectionRegistry } from "../types.js";
3
4
  export type SubscriptionCheckpoint = {
4
5
  subscriptionId: string;
5
6
  partition: string;
6
7
  lastProcessedPosition: bigint;
7
8
  };
8
- export type ProjectionRunnerDeps<T extends DatabaseExecutor = DatabaseExecutor> = {
9
- db: T;
9
+ /**
10
+ * Flexible database type for projection runner.
11
+ * Uses `Kysely<any> | any` to work around Kysely's private field variance issue.
12
+ */
13
+ export type ProjectionRunnerDeps = {
14
+ db: Kysely<any> | any;
10
15
  readStream: KyselyEventStore["readStream"];
11
- registry: ProjectionRegistry<T>;
16
+ registry: ProjectionRegistry;
12
17
  };
13
- export declare function createProjectionRunner<T extends DatabaseExecutor = DatabaseExecutor>({ db, readStream, registry }: ProjectionRunnerDeps<T>): {
18
+ export declare function createProjectionRunner({ db, readStream, registry, }: ProjectionRunnerDeps): {
14
19
  projectEvents: (subscriptionId: string, streamId: string, opts?: {
15
20
  partition?: string;
16
21
  batchSize?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/projections/runner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,KAAK,EACV,gBAAgB,EAEhB,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAErB,MAAM,MAAM,sBAAsB,GAAG;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,oBAAoB,CAC9B,CAAC,SAAS,gBAAgB,GAAG,gBAAgB,IAC3C;IACF,EAAE,EAAE,CAAC,CAAC;IACN,UAAU,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAC;IAC3C,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC;CACjC,CAAC;AAEF,wBAAgB,sBAAsB,CACpC,CAAC,SAAS,gBAAgB,GAAG,gBAAgB,EAC7C,EAAE,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,oBAAoB,CAAC,CAAC,CAAC;oCA4EnC,MAAM,YACZ,MAAM,SACT;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;;;;EAiDpD"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/projections/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAqB,MAAM,QAAQ,CAAC;AAExD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,KAAK,EAAmB,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEvE,MAAM,MAAM,sBAAsB,GAAG;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,MAAM,CAAC;CAC/B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;IACtB,UAAU,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAC;IAC3C,QAAQ,EAAE,kBAAkB,CAAC;CAC9B,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,EACrC,EAAE,EACF,UAAU,EACV,QAAQ,GACT,EAAE,oBAAoB;oCA4EH,MAAM,YACZ,MAAM,SACT;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE;;;;EAiDpD"}
@@ -1,4 +1,4 @@
1
- export function createProjectionRunner({ db, readStream, registry }) {
1
+ export function createProjectionRunner({ db, readStream, registry, }) {
2
2
  async function getOrCreateCheckpoint(subscriptionId, partition) {
3
3
  const existing = await db
4
4
  .selectFrom("subscriptions")
@@ -116,5 +116,5 @@ export declare function createSnapshotProjectionRegistry<TState, TTable extends
116
116
  } = {
117
117
  type: string;
118
118
  data: unknown;
119
- }>(eventTypes: E["type"][], config: SnapshotProjectionConfig<TState, TTable, E>): ProjectionRegistry<DatabaseExecutor>;
119
+ }>(eventTypes: E["type"][], config: SnapshotProjectionConfig<TState, TTable, E>): ProjectionRegistry;
120
120
  //# sourceMappingURL=snapshot-projection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot-projection.d.ts","sourceRoot":"","sources":["../../src/projections/snapshot-projection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAEhB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,CAClC,MAAM,EACN,MAAM,SAAS,MAAM,EACrB,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,IACzE;IACF;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,WAAW,EAAE,MAAM,EAAE,CAAC;IAEtB;;OAEG;IACH,WAAW,EAAE,CACX,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,SAAS,EAAE,MAAM,KACd,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE5B;;;OAGG;IACH,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC;IAE7D;;OAEG;IACH,YAAY,EAAE,MAAM,MAAM,CAAC;IAE3B;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3D,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EACN,MAAM,SAAS,MAAM,EACrB,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,EAE3E,MAAM,EAAE,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,GAClD,iBAAiB,CAAC,gBAAgB,EAAE,CAAC,CAAC,CA2FxC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gCAAgC,CAC9C,MAAM,EACN,MAAM,SAAS,MAAM,EACrB,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,EAE3E,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EACvB,MAAM,EAAE,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,GAClD,kBAAkB,CAAC,gBAAgB,CAAC,CAStC"}
1
+ {"version":3,"file":"snapshot-projection.d.ts","sourceRoot":"","sources":["../../src/projections/snapshot-projection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAEhB,eAAe,EACf,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,MAAM,MAAM,wBAAwB,CAClC,MAAM,EACN,MAAM,SAAS,MAAM,EACrB,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,IACzE;IACF;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,WAAW,EAAE,MAAM,EAAE,CAAC;IAEtB;;OAEG;IACH,WAAW,EAAE,CACX,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,SAAS,EAAE,MAAM,KACd,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE5B;;;OAGG;IACH,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC;IAE7D;;OAEG;IACH,YAAY,EAAE,MAAM,MAAM,CAAC;IAE3B;;;;;;;;;;;;OAYG;IACH,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3D,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EACN,MAAM,SAAS,MAAM,EACrB,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,EAE3E,MAAM,EAAE,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,GAClD,iBAAiB,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAkGxC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gCAAgC,CAC9C,MAAM,EACN,MAAM,SAAS,MAAM,EACrB,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,EAE3E,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EACvB,MAAM,EAAE,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,GAClD,kBAAkB,CAYpB"}
@@ -34,10 +34,14 @@ export function createSnapshotProjection(config) {
34
34
  return async ({ db, partition }, event) => {
35
35
  const keys = extractKeys(event, partition);
36
36
  // Check if event is newer than what we've already processed
37
- // Note: Casting to any is necessary because Kysely cannot infer types for dynamic table names
37
+ // Note: Casting to `any` is necessary because Kysely cannot infer types for dynamic table names.
38
+ // The table name is provided at runtime, so TypeScript cannot verify the table structure at compile time.
39
+ // This is a known limitation when working with dynamic table names in Kysely.
40
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
41
  const existing = await db
39
42
  .selectFrom(tableName)
40
43
  .select(["last_stream_position", "snapshot"])
44
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
45
  .where((eb) => {
42
46
  const conditions = Object.entries(keys).map(([key, value]) => eb(key, "=", value));
43
47
  return eb.and(conditions);
@@ -86,6 +90,9 @@ export function createSnapshotProjection(config) {
86
90
  }
87
91
  }
88
92
  await insertQuery
93
+ // Note: `any` is used here because the conflict builder needs to work with any table schema.
94
+ // The actual schema is validated at runtime through Kysely's query builder.
95
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
89
96
  .onConflict((oc) => {
90
97
  const conflictBuilder = oc.columns(primaryKeys);
91
98
  return conflictBuilder.doUpdateSet(updateSet);
@@ -119,6 +126,9 @@ export function createSnapshotProjectionRegistry(eventTypes, config) {
119
126
  const handler = createSnapshotProjection(config);
120
127
  const registry = {};
121
128
  for (const eventType of eventTypes) {
129
+ // Type cast is safe here because ProjectionHandler is contravariant in its event type parameter.
130
+ // A handler for a specific event type E can safely handle any event that matches E's structure.
131
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
122
132
  registry[eventType] = [handler];
123
133
  }
124
134
  return registry;
package/dist/types.d.ts CHANGED
@@ -1,14 +1,22 @@
1
1
  import type { Kysely } from "kysely";
2
2
  export type DatabaseExecutor<T = any> = Kysely<T>;
3
+ /**
4
+ * Flexible database type that accepts any Kysely instance.
5
+ * The `Kysely<any> | any` union type is intentional to work around Kysely's private field variance
6
+ * issue in TypeScript, allowing DatabaseExecutor from different modules to be passed.
7
+ */
8
+ export type FlexibleDatabase = Kysely<any> | any;
3
9
  export type Logger = {
4
10
  info: (obj: unknown, msg?: string) => void;
5
11
  error: (obj: unknown, msg?: string) => void;
6
12
  warn?: (obj: unknown, msg?: string) => void;
7
13
  debug?: (obj: unknown, msg?: string) => void;
8
14
  };
9
- export type Dependencies<T = any> = {
10
- db: DatabaseExecutor<T>;
15
+ export type Dependencies = {
16
+ db: FlexibleDatabase;
11
17
  logger: Logger;
18
+ /** If true, the provided db is already a transaction executor. */
19
+ inTransaction?: boolean;
12
20
  };
13
21
  export type ExtendedOptions = {
14
22
  partition?: string;
@@ -50,17 +58,27 @@ export type ProjectionEvent<E extends {
50
58
  }> = E & {
51
59
  metadata: ProjectionEventMetadata;
52
60
  };
53
- export type ProjectionContext<T = DatabaseExecutor<any>> = {
61
+ export type ProjectionContext<T = FlexibleDatabase> = {
54
62
  db: T;
55
63
  partition: string;
56
64
  };
57
- export type ProjectionHandler<T = DatabaseExecutor<any>, E extends {
65
+ export type ProjectionHandler<T = FlexibleDatabase, E extends {
58
66
  type: string;
59
67
  data: unknown;
60
68
  } = {
61
69
  type: string;
62
70
  data: unknown;
63
71
  }> = (ctx: ProjectionContext<T>, event: ProjectionEvent<E>) => void | Promise<void>;
64
- export type ProjectionRegistry<T = DatabaseExecutor<any>> = Record<string, ProjectionHandler<T, any>[]>;
65
- export declare function createProjectionRegistry<T = DatabaseExecutor<any>>(...registries: ProjectionRegistry<T>[]): ProjectionRegistry<T>;
72
+ /**
73
+ * ProjectionRegistry maps event types to their handlers.
74
+ * The `any` in `ProjectionHandler<T, any>[]` is intentional - it allows handlers
75
+ * for different event types to be registered together, with type safety enforced
76
+ * at the handler level through the ProjectionHandler generic parameter.
77
+ * Uses FlexibleDatabase by default to work around Kysely's private field variance issue.
78
+ */
79
+ export type ProjectionRegistry<T = FlexibleDatabase> = Record<string, ProjectionHandler<T, {
80
+ type: string;
81
+ data: unknown;
82
+ }>[]>;
83
+ export declare function createProjectionRegistry<T = FlexibleDatabase>(...registries: ProjectionRegistry<T>[]): ProjectionRegistry<T>;
66
84
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGrC,MAAM,MAAM,gBAAgB,CAAC,CAAC,GAAG,GAAG,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;AAElD,MAAM,MAAM,MAAM,GAAG;IACnB,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,GAAG,IAAI;IAClC,EAAE,EAAE,gBAAgB,CAAC,CAAC,CAAC,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,wCAAwC,KAAK,CAAC;AAC3D,eAAO,MAAM,iBAAiB,EAAG,mBAA4B,CAAC;AAG9D,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,IAAI,CAAC,GAAG;IAC3E,QAAQ,EAAE,uBAAuB,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,IAAI;IACzD,EAAE,EAAE,CAAC,CAAC;IACN,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAC3B,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,EACzB,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,IACzE,CACF,GAAG,EAAE,iBAAiB,CAAC,CAAC,CAAC,EACzB,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,KACtB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B,MAAM,MAAM,kBAAkB,CAAC,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,IAAI,MAAM,CAChE,MAAM,EACN,iBAAiB,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAC5B,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,CAAC,GAAG,gBAAgB,CAAC,GAAG,CAAC,EAChE,GAAG,UAAU,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAAE,GACrC,kBAAkB,CAAC,CAAC,CAAC,CAYvB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGrC,MAAM,MAAM,gBAAgB,CAAC,CAAC,GAAG,GAAG,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC;AAElD;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;AAEjD,MAAM,MAAM,MAAM,GAAG;IACnB,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,KAAK,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,IAAI,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5C,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,gBAAgB,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,wCAAwC,KAAK,CAAC;AAC3D,eAAO,MAAM,iBAAiB,EAAG,mBAA4B,CAAC;AAG9D,MAAM,MAAM,uBAAuB,GAAG;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,IAAI,CAAC,GAAG;IAC3E,QAAQ,EAAE,uBAAuB,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,CAAC,GAAG,gBAAgB,IAAI;IACpD,EAAE,EAAE,CAAC,CAAC;IACN,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAC3B,CAAC,GAAG,gBAAgB,EACpB,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,IACzE,CACF,GAAG,EAAE,iBAAiB,CAAC,CAAC,CAAC,EACzB,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,KACtB,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAE1B;;;;;;GAMG;AACH,MAAM,MAAM,kBAAkB,CAAC,CAAC,GAAG,gBAAgB,IAAI,MAAM,CAC3D,MAAM,EACN,iBAAiB,CAAC,CAAC,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,EAAE,CACxD,CAAC;AAEF,wBAAgB,wBAAwB,CAAC,CAAC,GAAG,gBAAgB,EAC3D,GAAG,UAAU,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAAE,GACrC,kBAAkB,CAAC,CAAC,CAAC,CAYvB"}
package/package.json CHANGED
@@ -3,16 +3,16 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "2.0.2",
6
+ "version": "2.2.0",
7
7
  "description": "Emmett Event Store with Kysely",
8
8
  "author": "Wataru Oguchi",
9
9
  "license": "MIT",
10
10
  "repository": {
11
11
  "type": "git",
12
- "url": "https://github.com/wataruoguchi/emmett-event-store-kysely.git"
12
+ "url": "https://github.com/wataruoguchi/emmett-libs.git"
13
13
  },
14
- "homepage": "https://github.com/wataruoguchi/emmett-event-store-kysely",
15
- "bugs": "https://github.com/wataruoguchi/emmett-event-store-kysely/issues",
14
+ "homepage": "https://github.com/wataruoguchi/emmett-libs",
15
+ "bugs": "https://github.com/wataruoguchi/emmett-libs/issues",
16
16
  "type": "module",
17
17
  "main": "dist/index.js",
18
18
  "module": "dist/index.js",
@@ -30,17 +30,13 @@
30
30
  "scripts": {
31
31
  "build": "rm -rf dist && tsc -p tsconfig.build.json && tsup src/index.ts src/projections/index.ts",
32
32
  "type-check": "tsc --noEmit",
33
+ "tc": "npm run type-check",
33
34
  "test": "npm run type-check && vitest run",
34
- "release": "semantic-release",
35
- "release:dry-run": "semantic-release --dry-run"
35
+ "test:coverage": "vitest run --coverage"
36
36
  },
37
37
  "devDependencies": {
38
- "@semantic-release/commit-analyzer": "^13.0.1",
39
- "@semantic-release/github": "^11.0.6",
40
- "@semantic-release/npm": "^12.0.2",
41
- "@semantic-release/release-notes-generator": "^14.1.0",
38
+ "@types/node": "^24.9.2",
42
39
  "@vitest/coverage-v8": "^3.2.4",
43
- "semantic-release": "^24.2.9",
44
40
  "tsup": "^8.5.0",
45
41
  "typescript": "^5.8.3",
46
42
  "vitest": "^3.2.4"