@l-etabli/events 0.6.0 → 0.7.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 +96 -0
- package/dist/adapters/effect-kysely/EffectKyselyEventQueries.cjs +54 -0
- package/dist/adapters/effect-kysely/EffectKyselyEventQueries.cjs.map +1 -0
- package/dist/adapters/effect-kysely/EffectKyselyEventQueries.d.cts +10 -0
- package/dist/adapters/effect-kysely/EffectKyselyEventQueries.d.ts +10 -0
- package/dist/adapters/effect-kysely/EffectKyselyEventQueries.mjs +30 -0
- package/dist/adapters/effect-kysely/EffectKyselyEventQueries.mjs.map +1 -0
- package/dist/adapters/effect-kysely/EffectKyselyEventRepository.cjs +85 -0
- package/dist/adapters/effect-kysely/EffectKyselyEventRepository.cjs.map +1 -0
- package/dist/adapters/effect-kysely/EffectKyselyEventRepository.d.cts +9 -0
- package/dist/adapters/effect-kysely/EffectKyselyEventRepository.d.ts +9 -0
- package/dist/adapters/effect-kysely/EffectKyselyEventRepository.mjs +61 -0
- package/dist/adapters/effect-kysely/EffectKyselyEventRepository.mjs.map +1 -0
- package/dist/adapters/effect-kysely/index.cjs +32 -0
- package/dist/adapters/effect-kysely/index.cjs.map +1 -0
- package/dist/adapters/effect-kysely/index.d.cts +9 -0
- package/dist/adapters/effect-kysely/index.d.ts +9 -0
- package/dist/adapters/effect-kysely/index.mjs +7 -0
- package/dist/adapters/effect-kysely/index.mjs.map +1 -0
- package/dist/adapters/in-memory/InMemoryEventBus.cjs +3 -17
- package/dist/adapters/in-memory/InMemoryEventBus.cjs.map +1 -1
- package/dist/adapters/in-memory/InMemoryEventBus.mjs +2 -16
- package/dist/adapters/in-memory/InMemoryEventBus.mjs.map +1 -1
- package/dist/adapters/in-memory/InMemoryEventQueries.cjs +2 -20
- package/dist/adapters/in-memory/InMemoryEventQueries.cjs.map +1 -1
- package/dist/adapters/in-memory/InMemoryEventQueries.mjs +2 -20
- package/dist/adapters/in-memory/InMemoryEventQueries.mjs.map +1 -1
- package/dist/adapters/kysely/KyselyEventQueries.cjs +20 -24
- package/dist/adapters/kysely/KyselyEventQueries.cjs.map +1 -1
- package/dist/adapters/kysely/KyselyEventQueries.d.cts +1 -1
- package/dist/adapters/kysely/KyselyEventQueries.d.ts +1 -1
- package/dist/adapters/kysely/KyselyEventQueries.mjs +20 -24
- package/dist/adapters/kysely/KyselyEventQueries.mjs.map +1 -1
- package/dist/adapters/kysely/KyselyEventRepository.cjs +47 -45
- package/dist/adapters/kysely/KyselyEventRepository.cjs.map +1 -1
- package/dist/adapters/kysely/KyselyEventRepository.d.cts +1 -1
- package/dist/adapters/kysely/KyselyEventRepository.d.ts +1 -1
- package/dist/adapters/kysely/KyselyEventRepository.mjs +43 -41
- package/dist/adapters/kysely/KyselyEventRepository.mjs.map +1 -1
- package/dist/adapters/kysely/jsonb.cjs +30 -0
- package/dist/adapters/kysely/jsonb.cjs.map +1 -0
- package/dist/adapters/kysely/jsonb.d.cts +5 -0
- package/dist/adapters/kysely/jsonb.d.ts +5 -0
- package/dist/adapters/kysely/jsonb.mjs +6 -0
- package/dist/adapters/kysely/jsonb.mjs.map +1 -0
- package/dist/adapters/kysely/mapEventRow.cjs +35 -0
- package/dist/adapters/kysely/mapEventRow.cjs.map +1 -0
- package/dist/adapters/kysely/mapEventRow.d.cts +6 -0
- package/dist/adapters/kysely/mapEventRow.d.ts +6 -0
- package/dist/adapters/kysely/mapEventRow.mjs +11 -0
- package/dist/adapters/kysely/mapEventRow.mjs.map +1 -0
- package/dist/createEventCrawler.cjs +2 -8
- package/dist/createEventCrawler.cjs.map +1 -1
- package/dist/createEventCrawler.mjs +1 -7
- package/dist/createEventCrawler.mjs.map +1 -1
- package/dist/effect/EffectEventCrawler.cjs +111 -0
- package/dist/effect/EffectEventCrawler.cjs.map +1 -0
- package/dist/effect/EffectEventCrawler.d.cts +26 -0
- package/dist/effect/EffectEventCrawler.d.ts +26 -0
- package/dist/effect/EffectEventCrawler.mjs +87 -0
- package/dist/effect/EffectEventCrawler.mjs.map +1 -0
- package/dist/effect/EffectInMemoryEventBus.cjs +131 -0
- package/dist/effect/EffectInMemoryEventBus.cjs.map +1 -0
- package/dist/effect/EffectInMemoryEventBus.d.cts +31 -0
- package/dist/effect/EffectInMemoryEventBus.d.ts +31 -0
- package/dist/effect/EffectInMemoryEventBus.mjs +112 -0
- package/dist/effect/EffectInMemoryEventBus.mjs.map +1 -0
- package/dist/effect/EffectInMemoryEventQueries.cjs +35 -0
- package/dist/effect/EffectInMemoryEventQueries.cjs.map +1 -0
- package/dist/effect/EffectInMemoryEventQueries.d.cts +12 -0
- package/dist/effect/EffectInMemoryEventQueries.d.ts +12 -0
- package/dist/effect/EffectInMemoryEventQueries.mjs +11 -0
- package/dist/effect/EffectInMemoryEventQueries.mjs.map +1 -0
- package/dist/effect/EffectInMemoryEventRepository.cjs +73 -0
- package/dist/effect/EffectInMemoryEventRepository.cjs.map +1 -0
- package/dist/effect/EffectInMemoryEventRepository.d.cts +15 -0
- package/dist/effect/EffectInMemoryEventRepository.d.ts +15 -0
- package/dist/effect/EffectInMemoryEventRepository.mjs +48 -0
- package/dist/effect/EffectInMemoryEventRepository.mjs.map +1 -0
- package/dist/effect/EffectSubscriptions.cjs +61 -0
- package/dist/effect/EffectSubscriptions.cjs.map +1 -0
- package/dist/effect/EffectSubscriptions.d.cts +22 -0
- package/dist/effect/EffectSubscriptions.d.ts +22 -0
- package/dist/effect/EffectSubscriptions.mjs +36 -0
- package/dist/effect/EffectSubscriptions.mjs.map +1 -0
- package/dist/effect/index.cjs +47 -0
- package/dist/effect/index.cjs.map +1 -0
- package/dist/effect/index.d.cts +27 -0
- package/dist/effect/index.d.ts +27 -0
- package/dist/effect/index.mjs +20 -0
- package/dist/effect/index.mjs.map +1 -0
- package/dist/effect/ports/EffectEventBus.cjs +17 -0
- package/dist/effect/ports/EffectEventBus.cjs.map +1 -0
- package/dist/effect/ports/EffectEventBus.d.cts +13 -0
- package/dist/effect/ports/EffectEventBus.d.ts +13 -0
- package/dist/effect/ports/EffectEventBus.mjs +1 -0
- package/dist/effect/ports/EffectEventBus.mjs.map +1 -0
- package/dist/effect/ports/EffectEventQueries.cjs +17 -0
- package/dist/effect/ports/EffectEventQueries.cjs.map +1 -0
- package/dist/effect/ports/EffectEventQueries.d.cts +9 -0
- package/dist/effect/ports/EffectEventQueries.d.ts +9 -0
- package/dist/effect/ports/EffectEventQueries.mjs +1 -0
- package/dist/effect/ports/EffectEventQueries.mjs.map +1 -0
- package/dist/effect/ports/EffectEventRepository.cjs +17 -0
- package/dist/effect/ports/EffectEventRepository.cjs.map +1 -0
- package/dist/effect/ports/EffectEventRepository.d.cts +17 -0
- package/dist/effect/ports/EffectEventRepository.d.ts +17 -0
- package/dist/effect/ports/EffectEventRepository.mjs +1 -0
- package/dist/effect/ports/EffectEventRepository.mjs.map +1 -0
- package/dist/filterEvents.cjs +48 -0
- package/dist/filterEvents.cjs.map +1 -0
- package/dist/filterEvents.d.cts +6 -0
- package/dist/filterEvents.d.ts +6 -0
- package/dist/filterEvents.mjs +24 -0
- package/dist/filterEvents.mjs.map +1 -0
- package/dist/getSubscriptionIdsToPublish.cjs +40 -0
- package/dist/getSubscriptionIdsToPublish.cjs.map +1 -0
- package/dist/getSubscriptionIdsToPublish.d.cts +5 -0
- package/dist/getSubscriptionIdsToPublish.d.ts +5 -0
- package/dist/getSubscriptionIdsToPublish.mjs +16 -0
- package/dist/getSubscriptionIdsToPublish.mjs.map +1 -0
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/ports/EventQueries.cjs.map +1 -1
- package/dist/ports/EventQueries.d.cts +1 -1
- package/dist/ports/EventQueries.d.ts +1 -1
- package/dist/splitIntoChunks.cjs +35 -0
- package/dist/splitIntoChunks.cjs.map +1 -0
- package/dist/splitIntoChunks.d.cts +3 -0
- package/dist/splitIntoChunks.d.ts +3 -0
- package/dist/splitIntoChunks.mjs +11 -0
- package/dist/splitIntoChunks.mjs.map +1 -0
- package/package.json +18 -3
- package/src/adapters/effect-kysely/EffectKyselyEventQueries.ts +45 -0
- package/src/adapters/effect-kysely/EffectKyselyEventRepository.ts +90 -0
- package/src/adapters/effect-kysely/index.ts +3 -0
- package/src/adapters/in-memory/InMemoryEventBus.ts +2 -23
- package/src/adapters/in-memory/InMemoryEventQueries.ts +2 -32
- package/src/adapters/kysely/KyselyEventQueries.ts +27 -31
- package/src/adapters/kysely/KyselyEventRepository.ts +66 -64
- package/src/adapters/kysely/jsonb.ts +4 -0
- package/src/adapters/kysely/mapEventRow.ts +15 -0
- package/src/createEventCrawler.ts +1 -8
- package/src/effect/EffectEventCrawler.ts +124 -0
- package/src/effect/EffectInMemoryEventBus.ts +231 -0
- package/src/effect/EffectInMemoryEventQueries.ts +16 -0
- package/src/effect/EffectInMemoryEventRepository.ts +68 -0
- package/src/effect/EffectSubscriptions.ts +74 -0
- package/src/effect/index.ts +26 -0
- package/src/effect/ports/EffectEventBus.ts +17 -0
- package/src/effect/ports/EffectEventQueries.ts +9 -0
- package/src/effect/ports/EffectEventRepository.ts +27 -0
- package/src/filterEvents.ts +39 -0
- package/src/getSubscriptionIdsToPublish.ts +21 -0
- package/src/ports/EventQueries.ts +1 -1
- package/src/splitIntoChunks.ts +7 -0
package/README.md
CHANGED
|
@@ -253,6 +253,102 @@ export async function up(db: Kysely<unknown>): Promise<void> {
|
|
|
253
253
|
}
|
|
254
254
|
```
|
|
255
255
|
|
|
256
|
+
## Effect v4 support
|
|
257
|
+
|
|
258
|
+
Effect-native ports and adapters are available via dedicated subpath exports. Non-Effect users are not impacted — `effect` is an optional peer dependency.
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
bun add @l-etabli/events effect
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Setup
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
import { Effect } from "effect";
|
|
268
|
+
import type { InferEventsFromDefinitions } from "@l-etabli/events";
|
|
269
|
+
import {
|
|
270
|
+
createEffectInMemoryEventBus,
|
|
271
|
+
createEffectInMemoryEventRepositoryAndQueries,
|
|
272
|
+
createEffectEventCrawler,
|
|
273
|
+
} from "@l-etabli/events/effect";
|
|
274
|
+
|
|
275
|
+
const { eventQueries, withUow } =
|
|
276
|
+
createEffectInMemoryEventRepositoryAndQueries<AppEvents>();
|
|
277
|
+
|
|
278
|
+
const { eventBus, createNewEvent } = createEffectInMemoryEventBus(withUow, {
|
|
279
|
+
eventDefinitions,
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const crawler = createEffectEventCrawler({
|
|
283
|
+
withUow,
|
|
284
|
+
eventQueries,
|
|
285
|
+
eventBus,
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Subscribe
|
|
290
|
+
|
|
291
|
+
```ts
|
|
292
|
+
eventBus.subscribe({
|
|
293
|
+
topic: "ProjectCreated",
|
|
294
|
+
subscriptionId: "send-project-created-email",
|
|
295
|
+
callBack: (event) =>
|
|
296
|
+
Effect.promise(() => emailService.send(event.payload.project.id)),
|
|
297
|
+
});
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Emit
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
await Effect.runPromise(
|
|
304
|
+
withUow((uow) =>
|
|
305
|
+
uow.eventRepository.saveNewEventsBatch([
|
|
306
|
+
createNewEvent({
|
|
307
|
+
topic: "ProjectCreated",
|
|
308
|
+
payload: { project },
|
|
309
|
+
context: { projectId: project.id },
|
|
310
|
+
triggeredByActor: createUserActor(currentUserId),
|
|
311
|
+
}),
|
|
312
|
+
]),
|
|
313
|
+
),
|
|
314
|
+
);
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Process
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
// Traditional server
|
|
321
|
+
crawler.start();
|
|
322
|
+
|
|
323
|
+
// Serverless / on-demand
|
|
324
|
+
await Effect.runPromise(crawler.triggerProcessing());
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Kysely adapters
|
|
328
|
+
|
|
329
|
+
For PostgreSQL with Effect-native Kysely:
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
import {
|
|
333
|
+
createEffectKyselyEventRepository,
|
|
334
|
+
createEffectKyselyEventQueries,
|
|
335
|
+
} from "@l-etabli/events/effect-kysely";
|
|
336
|
+
|
|
337
|
+
const eventRepository = createEffectKyselyEventRepository<AppEvents, Db>(db);
|
|
338
|
+
const eventQueries = createEffectKyselyEventQueries<AppEvents, Db>(db);
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
These adapters wrap Kysely queries with `Effect.promise()` — they work with any standard Kysely instance.
|
|
342
|
+
|
|
343
|
+
### Subpath exports
|
|
344
|
+
|
|
345
|
+
| Import path | What | Requires |
|
|
346
|
+
|---|---|---|
|
|
347
|
+
| `@l-etabli/events` | Promise-based ports, in-memory adapters, crawler | — |
|
|
348
|
+
| `@l-etabli/events/kysely` | Promise-based Kysely adapters | `kysely` |
|
|
349
|
+
| `@l-etabli/events/effect` | Effect-native ports, in-memory adapters, crawler | `effect` |
|
|
350
|
+
| `@l-etabli/events/effect-kysely` | Effect-native Kysely adapters | `effect`, `kysely` |
|
|
351
|
+
|
|
256
352
|
## Examples
|
|
257
353
|
|
|
258
354
|
See [`examples/`](./examples/) for Kysely integration, cascading events and serverless usage.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var EffectKyselyEventQueries_exports = {};
|
|
20
|
+
__export(EffectKyselyEventQueries_exports, {
|
|
21
|
+
createEffectKyselyEventQueries: () => createEffectKyselyEventQueries
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(EffectKyselyEventQueries_exports);
|
|
24
|
+
var import_effect = require("effect");
|
|
25
|
+
var import_kysely = require("kysely");
|
|
26
|
+
var import_mapEventRow = require("../kysely/mapEventRow.ts");
|
|
27
|
+
const createEffectKyselyEventQueries = (db) => {
|
|
28
|
+
const eventsDb = db;
|
|
29
|
+
return {
|
|
30
|
+
getEvents: ({ filters, limit }) => import_effect.Effect.gen(function* () {
|
|
31
|
+
let query = eventsDb.selectFrom("events").selectAll().where("status", "in", filters.statuses).limit(limit);
|
|
32
|
+
if (filters.context) {
|
|
33
|
+
for (const [key, value] of Object.entries(filters.context)) {
|
|
34
|
+
query = query.where(import_kysely.sql`context->>${key} = ${value}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (filters.occurredAt?.from) {
|
|
38
|
+
query = query.where("occurredAt", ">=", filters.occurredAt.from);
|
|
39
|
+
}
|
|
40
|
+
if (filters.occurredAt?.to) {
|
|
41
|
+
query = query.where("occurredAt", "<=", filters.occurredAt.to);
|
|
42
|
+
}
|
|
43
|
+
const rows = yield* import_effect.Effect.promise(() => query.execute());
|
|
44
|
+
return rows.map(
|
|
45
|
+
(row) => (0, import_mapEventRow.mapEventRow)(row)
|
|
46
|
+
);
|
|
47
|
+
})
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
51
|
+
0 && (module.exports = {
|
|
52
|
+
createEffectKyselyEventQueries
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=EffectKyselyEventQueries.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/effect-kysely/EffectKyselyEventQueries.ts"],"sourcesContent":["import { Effect } from \"effect\";\nimport type { Kysely, SqlBool } from \"kysely\";\nimport { sql } from \"kysely\";\nimport type { EventQueries } from \"../../effect/ports/EffectEventQueries.ts\";\nimport type { DefaultContext, GenericEvent } from \"../../types.ts\";\nimport { mapEventRow } from \"../kysely/mapEventRow.ts\";\nimport type { EventsTable } from \"../kysely/types.ts\";\n\nexport const createEffectKyselyEventQueries = <\n Event extends GenericEvent<string, unknown, DefaultContext>,\n DB extends EventsTable = EventsTable,\n>(\n db: Kysely<DB>,\n): EventQueries<Event> => {\n const eventsDb = db as unknown as Kysely<EventsTable>;\n return {\n getEvents: ({ filters, limit }) =>\n Effect.gen(function* () {\n let query = eventsDb\n .selectFrom(\"events\")\n .selectAll()\n .where(\"status\", \"in\", filters.statuses)\n .limit(limit);\n\n if (filters.context) {\n for (const [key, value] of Object.entries(filters.context)) {\n query = query.where(sql<SqlBool>`context->>${key} = ${value}`);\n }\n }\n\n if (filters.occurredAt?.from) {\n query = query.where(\"occurredAt\", \">=\", filters.occurredAt.from);\n }\n\n if (filters.occurredAt?.to) {\n query = query.where(\"occurredAt\", \"<=\", filters.occurredAt.to);\n }\n\n const rows = yield* Effect.promise(() => query.execute());\n return rows.map((row: EventsTable[\"events\"]) =>\n mapEventRow<Event>(row),\n );\n }),\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAuB;AAEvB,oBAAoB;AAGpB,yBAA4B;AAGrB,MAAM,iCAAiC,CAI5C,OACwB;AACxB,QAAM,WAAW;AACjB,SAAO;AAAA,IACL,WAAW,CAAC,EAAE,SAAS,MAAM,MAC3B,qBAAO,IAAI,aAAa;AACtB,UAAI,QAAQ,SACT,WAAW,QAAQ,EACnB,UAAU,EACV,MAAM,UAAU,MAAM,QAAQ,QAAQ,EACtC,MAAM,KAAK;AAEd,UAAI,QAAQ,SAAS;AACnB,mBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,OAAO,GAAG;AAC1D,kBAAQ,MAAM,MAAM,8BAAyB,GAAG,MAAM,KAAK,EAAE;AAAA,QAC/D;AAAA,MACF;AAEA,UAAI,QAAQ,YAAY,MAAM;AAC5B,gBAAQ,MAAM,MAAM,cAAc,MAAM,QAAQ,WAAW,IAAI;AAAA,MACjE;AAEA,UAAI,QAAQ,YAAY,IAAI;AAC1B,gBAAQ,MAAM,MAAM,cAAc,MAAM,QAAQ,WAAW,EAAE;AAAA,MAC/D;AAEA,YAAM,OAAO,OAAO,qBAAO,QAAQ,MAAM,MAAM,QAAQ,CAAC;AACxD,aAAO,KAAK;AAAA,QAAI,CAAC,YACf,gCAAmB,GAAG;AAAA,MACxB;AAAA,IACF,CAAC;AAAA,EACL;AACF;","names":[]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Kysely } from 'kysely';
|
|
2
|
+
import { EventQueries } from '../../effect/ports/EffectEventQueries.cjs';
|
|
3
|
+
import { GenericEvent, DefaultContext } from '../../types.cjs';
|
|
4
|
+
import { EventsTable } from '../kysely/types.cjs';
|
|
5
|
+
import 'effect';
|
|
6
|
+
import '../../ports/EventQueries.cjs';
|
|
7
|
+
|
|
8
|
+
declare const createEffectKyselyEventQueries: <Event extends GenericEvent<string, unknown, DefaultContext>, DB extends EventsTable = EventsTable>(db: Kysely<DB>) => EventQueries<Event>;
|
|
9
|
+
|
|
10
|
+
export { createEffectKyselyEventQueries };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Kysely } from 'kysely';
|
|
2
|
+
import { EventQueries } from '../../effect/ports/EffectEventQueries.js';
|
|
3
|
+
import { GenericEvent, DefaultContext } from '../../types.js';
|
|
4
|
+
import { EventsTable } from '../kysely/types.js';
|
|
5
|
+
import 'effect';
|
|
6
|
+
import '../../ports/EventQueries.js';
|
|
7
|
+
|
|
8
|
+
declare const createEffectKyselyEventQueries: <Event extends GenericEvent<string, unknown, DefaultContext>, DB extends EventsTable = EventsTable>(db: Kysely<DB>) => EventQueries<Event>;
|
|
9
|
+
|
|
10
|
+
export { createEffectKyselyEventQueries };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { sql } from "kysely";
|
|
3
|
+
import { mapEventRow } from "../kysely/mapEventRow.mjs";
|
|
4
|
+
const createEffectKyselyEventQueries = (db) => {
|
|
5
|
+
const eventsDb = db;
|
|
6
|
+
return {
|
|
7
|
+
getEvents: ({ filters, limit }) => Effect.gen(function* () {
|
|
8
|
+
let query = eventsDb.selectFrom("events").selectAll().where("status", "in", filters.statuses).limit(limit);
|
|
9
|
+
if (filters.context) {
|
|
10
|
+
for (const [key, value] of Object.entries(filters.context)) {
|
|
11
|
+
query = query.where(sql`context->>${key} = ${value}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
if (filters.occurredAt?.from) {
|
|
15
|
+
query = query.where("occurredAt", ">=", filters.occurredAt.from);
|
|
16
|
+
}
|
|
17
|
+
if (filters.occurredAt?.to) {
|
|
18
|
+
query = query.where("occurredAt", "<=", filters.occurredAt.to);
|
|
19
|
+
}
|
|
20
|
+
const rows = yield* Effect.promise(() => query.execute());
|
|
21
|
+
return rows.map(
|
|
22
|
+
(row) => mapEventRow(row)
|
|
23
|
+
);
|
|
24
|
+
})
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
export {
|
|
28
|
+
createEffectKyselyEventQueries
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=EffectKyselyEventQueries.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/effect-kysely/EffectKyselyEventQueries.ts"],"sourcesContent":["import { Effect } from \"effect\";\nimport type { Kysely, SqlBool } from \"kysely\";\nimport { sql } from \"kysely\";\nimport type { EventQueries } from '../../effect/ports/EffectEventQueries.mjs';\nimport type { DefaultContext, GenericEvent } from '../../types.mjs';\nimport { mapEventRow } from '../kysely/mapEventRow.mjs';\nimport type { EventsTable } from '../kysely/types.mjs';\n\nexport const createEffectKyselyEventQueries = <\n Event extends GenericEvent<string, unknown, DefaultContext>,\n DB extends EventsTable = EventsTable,\n>(\n db: Kysely<DB>,\n): EventQueries<Event> => {\n const eventsDb = db as unknown as Kysely<EventsTable>;\n return {\n getEvents: ({ filters, limit }) =>\n Effect.gen(function* () {\n let query = eventsDb\n .selectFrom(\"events\")\n .selectAll()\n .where(\"status\", \"in\", filters.statuses)\n .limit(limit);\n\n if (filters.context) {\n for (const [key, value] of Object.entries(filters.context)) {\n query = query.where(sql<SqlBool>`context->>${key} = ${value}`);\n }\n }\n\n if (filters.occurredAt?.from) {\n query = query.where(\"occurredAt\", \">=\", filters.occurredAt.from);\n }\n\n if (filters.occurredAt?.to) {\n query = query.where(\"occurredAt\", \"<=\", filters.occurredAt.to);\n }\n\n const rows = yield* Effect.promise(() => query.execute());\n return rows.map((row: EventsTable[\"events\"]) =>\n mapEventRow<Event>(row),\n );\n }),\n };\n};\n"],"mappings":"AAAA,SAAS,cAAc;AAEvB,SAAS,WAAW;AAGpB,SAAS,mBAAmB;AAGrB,MAAM,iCAAiC,CAI5C,OACwB;AACxB,QAAM,WAAW;AACjB,SAAO;AAAA,IACL,WAAW,CAAC,EAAE,SAAS,MAAM,MAC3B,OAAO,IAAI,aAAa;AACtB,UAAI,QAAQ,SACT,WAAW,QAAQ,EACnB,UAAU,EACV,MAAM,UAAU,MAAM,QAAQ,QAAQ,EACtC,MAAM,KAAK;AAEd,UAAI,QAAQ,SAAS;AACnB,mBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,OAAO,GAAG;AAC1D,kBAAQ,MAAM,MAAM,gBAAyB,GAAG,MAAM,KAAK,EAAE;AAAA,QAC/D;AAAA,MACF;AAEA,UAAI,QAAQ,YAAY,MAAM;AAC5B,gBAAQ,MAAM,MAAM,cAAc,MAAM,QAAQ,WAAW,IAAI;AAAA,MACjE;AAEA,UAAI,QAAQ,YAAY,IAAI;AAC1B,gBAAQ,MAAM,MAAM,cAAc,MAAM,QAAQ,WAAW,EAAE;AAAA,MAC/D;AAEA,YAAM,OAAO,OAAO,OAAO,QAAQ,MAAM,MAAM,QAAQ,CAAC;AACxD,aAAO,KAAK;AAAA,QAAI,CAAC,QACf,YAAmB,GAAG;AAAA,MACxB;AAAA,IACF,CAAC;AAAA,EACL;AACF;","names":[]}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var EffectKyselyEventRepository_exports = {};
|
|
20
|
+
__export(EffectKyselyEventRepository_exports, {
|
|
21
|
+
createEffectKyselyEventRepository: () => createEffectKyselyEventRepository
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(EffectKyselyEventRepository_exports);
|
|
24
|
+
var import_effect = require("effect");
|
|
25
|
+
var import_jsonb = require("../kysely/jsonb.ts");
|
|
26
|
+
const createEffectKyselyEventRepository = (db) => {
|
|
27
|
+
const eventsDb = db;
|
|
28
|
+
return {
|
|
29
|
+
save: (event) => import_effect.Effect.promise(
|
|
30
|
+
() => eventsDb.insertInto("events").values({
|
|
31
|
+
...event,
|
|
32
|
+
payload: (0, import_jsonb.jsonb)(event.payload),
|
|
33
|
+
triggeredByActor: (0, import_jsonb.jsonb)(event.triggeredByActor),
|
|
34
|
+
context: (0, import_jsonb.jsonb)(event.context),
|
|
35
|
+
publications: (0, import_jsonb.jsonb)(event.publications)
|
|
36
|
+
}).onConflict(
|
|
37
|
+
(oc) => oc.column("id").doUpdateSet({
|
|
38
|
+
topic: event.topic,
|
|
39
|
+
payload: (0, import_jsonb.jsonb)(event.payload),
|
|
40
|
+
triggeredByActor: (0, import_jsonb.jsonb)(event.triggeredByActor),
|
|
41
|
+
context: (0, import_jsonb.jsonb)(event.context),
|
|
42
|
+
status: event.status,
|
|
43
|
+
flowId: event.flowId,
|
|
44
|
+
causedByEventId: event.causedByEventId,
|
|
45
|
+
occurredAt: event.occurredAt,
|
|
46
|
+
publications: (0, import_jsonb.jsonb)(event.publications),
|
|
47
|
+
priority: event.priority
|
|
48
|
+
})
|
|
49
|
+
).execute()
|
|
50
|
+
).pipe(import_effect.Effect.asVoid),
|
|
51
|
+
saveNewEventsBatch: (events) => {
|
|
52
|
+
if (events.length === 0) return import_effect.Effect.void;
|
|
53
|
+
return import_effect.Effect.promise(
|
|
54
|
+
() => eventsDb.insertInto("events").values(
|
|
55
|
+
events.map((event) => ({
|
|
56
|
+
...event,
|
|
57
|
+
payload: (0, import_jsonb.jsonb)(event.payload),
|
|
58
|
+
triggeredByActor: (0, import_jsonb.jsonb)(event.triggeredByActor),
|
|
59
|
+
context: (0, import_jsonb.jsonb)(event.context),
|
|
60
|
+
publications: (0, import_jsonb.jsonb)(event.publications)
|
|
61
|
+
}))
|
|
62
|
+
).execute()
|
|
63
|
+
).pipe(import_effect.Effect.asVoid);
|
|
64
|
+
},
|
|
65
|
+
markEventsAsInProcess: (events) => {
|
|
66
|
+
if (events.length === 0) return import_effect.Effect.void;
|
|
67
|
+
const ids = events.map((e) => e.id);
|
|
68
|
+
return import_effect.Effect.gen(function* () {
|
|
69
|
+
const lockedRows = yield* import_effect.Effect.promise(
|
|
70
|
+
() => eventsDb.selectFrom("events").select("id").where("id", "in", ids).forUpdate().skipLocked().execute()
|
|
71
|
+
);
|
|
72
|
+
if (lockedRows.length === 0) return;
|
|
73
|
+
const lockedIds = lockedRows.map((r) => r.id);
|
|
74
|
+
yield* import_effect.Effect.promise(
|
|
75
|
+
() => eventsDb.updateTable("events").set({ status: "in-process" }).where("id", "in", lockedIds).execute()
|
|
76
|
+
).pipe(import_effect.Effect.asVoid);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
82
|
+
0 && (module.exports = {
|
|
83
|
+
createEffectKyselyEventRepository
|
|
84
|
+
});
|
|
85
|
+
//# sourceMappingURL=EffectKyselyEventRepository.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/effect-kysely/EffectKyselyEventRepository.ts"],"sourcesContent":["import { Effect } from \"effect\";\nimport type { Kysely } from \"kysely\";\nimport type { EventRepository } from \"../../effect/ports/EffectEventRepository.ts\";\nimport type { DefaultContext, GenericEvent } from \"../../types.ts\";\nimport { jsonb } from \"../kysely/jsonb.ts\";\nimport type { EventsTable } from \"../kysely/types.ts\";\n\nexport const createEffectKyselyEventRepository = <\n Event extends GenericEvent<string, unknown, DefaultContext>,\n DB extends EventsTable = EventsTable,\n>(\n db: Kysely<DB>,\n): EventRepository<Event> => {\n const eventsDb = db as unknown as Kysely<EventsTable>;\n return {\n save: (event) =>\n Effect.promise(() =>\n eventsDb\n .insertInto(\"events\")\n .values({\n ...event,\n payload: jsonb(event.payload),\n triggeredByActor: jsonb(event.triggeredByActor),\n context: jsonb(event.context),\n publications: jsonb(event.publications),\n })\n .onConflict((oc) =>\n oc.column(\"id\").doUpdateSet({\n topic: event.topic,\n payload: jsonb(event.payload),\n triggeredByActor: jsonb(event.triggeredByActor),\n context: jsonb(event.context),\n status: event.status,\n flowId: event.flowId,\n causedByEventId: event.causedByEventId,\n occurredAt: event.occurredAt,\n publications: jsonb(event.publications),\n priority: event.priority,\n }),\n )\n .execute(),\n ).pipe(Effect.asVoid),\n\n saveNewEventsBatch: (events) => {\n if (events.length === 0) return Effect.void;\n return Effect.promise(() =>\n eventsDb\n .insertInto(\"events\")\n .values(\n events.map((event) => ({\n ...event,\n payload: jsonb(event.payload),\n triggeredByActor: jsonb(event.triggeredByActor),\n context: jsonb(event.context),\n publications: jsonb(event.publications),\n })),\n )\n .execute(),\n ).pipe(Effect.asVoid);\n },\n\n markEventsAsInProcess: (events) => {\n if (events.length === 0) return Effect.void;\n const ids = events.map((e) => e.id);\n\n return Effect.gen(function* () {\n const lockedRows = yield* Effect.promise(() =>\n eventsDb\n .selectFrom(\"events\")\n .select(\"id\")\n .where(\"id\", \"in\", ids)\n .forUpdate()\n .skipLocked()\n .execute(),\n );\n\n if (lockedRows.length === 0) return;\n const lockedIds = lockedRows.map((r) => r.id);\n\n yield* Effect.promise(() =>\n eventsDb\n .updateTable(\"events\")\n .set({ status: \"in-process\" })\n .where(\"id\", \"in\", lockedIds)\n .execute(),\n ).pipe(Effect.asVoid);\n });\n },\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAuB;AAIvB,mBAAsB;AAGf,MAAM,oCAAoC,CAI/C,OAC2B;AAC3B,QAAM,WAAW;AACjB,SAAO;AAAA,IACL,MAAM,CAAC,UACL,qBAAO;AAAA,MAAQ,MACb,SACG,WAAW,QAAQ,EACnB,OAAO;AAAA,QACN,GAAG;AAAA,QACH,aAAS,oBAAM,MAAM,OAAO;AAAA,QAC5B,sBAAkB,oBAAM,MAAM,gBAAgB;AAAA,QAC9C,aAAS,oBAAM,MAAM,OAAO;AAAA,QAC5B,kBAAc,oBAAM,MAAM,YAAY;AAAA,MACxC,CAAC,EACA;AAAA,QAAW,CAAC,OACX,GAAG,OAAO,IAAI,EAAE,YAAY;AAAA,UAC1B,OAAO,MAAM;AAAA,UACb,aAAS,oBAAM,MAAM,OAAO;AAAA,UAC5B,sBAAkB,oBAAM,MAAM,gBAAgB;AAAA,UAC9C,aAAS,oBAAM,MAAM,OAAO;AAAA,UAC5B,QAAQ,MAAM;AAAA,UACd,QAAQ,MAAM;AAAA,UACd,iBAAiB,MAAM;AAAA,UACvB,YAAY,MAAM;AAAA,UAClB,kBAAc,oBAAM,MAAM,YAAY;AAAA,UACtC,UAAU,MAAM;AAAA,QAClB,CAAC;AAAA,MACH,EACC,QAAQ;AAAA,IACb,EAAE,KAAK,qBAAO,MAAM;AAAA,IAEtB,oBAAoB,CAAC,WAAW;AAC9B,UAAI,OAAO,WAAW,EAAG,QAAO,qBAAO;AACvC,aAAO,qBAAO;AAAA,QAAQ,MACpB,SACG,WAAW,QAAQ,EACnB;AAAA,UACC,OAAO,IAAI,CAAC,WAAW;AAAA,YACrB,GAAG;AAAA,YACH,aAAS,oBAAM,MAAM,OAAO;AAAA,YAC5B,sBAAkB,oBAAM,MAAM,gBAAgB;AAAA,YAC9C,aAAS,oBAAM,MAAM,OAAO;AAAA,YAC5B,kBAAc,oBAAM,MAAM,YAAY;AAAA,UACxC,EAAE;AAAA,QACJ,EACC,QAAQ;AAAA,MACb,EAAE,KAAK,qBAAO,MAAM;AAAA,IACtB;AAAA,IAEA,uBAAuB,CAAC,WAAW;AACjC,UAAI,OAAO,WAAW,EAAG,QAAO,qBAAO;AACvC,YAAM,MAAM,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE;AAElC,aAAO,qBAAO,IAAI,aAAa;AAC7B,cAAM,aAAa,OAAO,qBAAO;AAAA,UAAQ,MACvC,SACG,WAAW,QAAQ,EACnB,OAAO,IAAI,EACX,MAAM,MAAM,MAAM,GAAG,EACrB,UAAU,EACV,WAAW,EACX,QAAQ;AAAA,QACb;AAEA,YAAI,WAAW,WAAW,EAAG;AAC7B,cAAM,YAAY,WAAW,IAAI,CAAC,MAAM,EAAE,EAAE;AAE5C,eAAO,qBAAO;AAAA,UAAQ,MACpB,SACG,YAAY,QAAQ,EACpB,IAAI,EAAE,QAAQ,aAAa,CAAC,EAC5B,MAAM,MAAM,MAAM,SAAS,EAC3B,QAAQ;AAAA,QACb,EAAE,KAAK,qBAAO,MAAM;AAAA,MACtB,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Kysely } from 'kysely';
|
|
2
|
+
import { EventRepository } from '../../effect/ports/EffectEventRepository.cjs';
|
|
3
|
+
import { GenericEvent, DefaultContext } from '../../types.cjs';
|
|
4
|
+
import { EventsTable } from '../kysely/types.cjs';
|
|
5
|
+
import 'effect';
|
|
6
|
+
|
|
7
|
+
declare const createEffectKyselyEventRepository: <Event extends GenericEvent<string, unknown, DefaultContext>, DB extends EventsTable = EventsTable>(db: Kysely<DB>) => EventRepository<Event>;
|
|
8
|
+
|
|
9
|
+
export { createEffectKyselyEventRepository };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Kysely } from 'kysely';
|
|
2
|
+
import { EventRepository } from '../../effect/ports/EffectEventRepository.js';
|
|
3
|
+
import { GenericEvent, DefaultContext } from '../../types.js';
|
|
4
|
+
import { EventsTable } from '../kysely/types.js';
|
|
5
|
+
import 'effect';
|
|
6
|
+
|
|
7
|
+
declare const createEffectKyselyEventRepository: <Event extends GenericEvent<string, unknown, DefaultContext>, DB extends EventsTable = EventsTable>(db: Kysely<DB>) => EventRepository<Event>;
|
|
8
|
+
|
|
9
|
+
export { createEffectKyselyEventRepository };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { jsonb } from "../kysely/jsonb.mjs";
|
|
3
|
+
const createEffectKyselyEventRepository = (db) => {
|
|
4
|
+
const eventsDb = db;
|
|
5
|
+
return {
|
|
6
|
+
save: (event) => Effect.promise(
|
|
7
|
+
() => eventsDb.insertInto("events").values({
|
|
8
|
+
...event,
|
|
9
|
+
payload: jsonb(event.payload),
|
|
10
|
+
triggeredByActor: jsonb(event.triggeredByActor),
|
|
11
|
+
context: jsonb(event.context),
|
|
12
|
+
publications: jsonb(event.publications)
|
|
13
|
+
}).onConflict(
|
|
14
|
+
(oc) => oc.column("id").doUpdateSet({
|
|
15
|
+
topic: event.topic,
|
|
16
|
+
payload: jsonb(event.payload),
|
|
17
|
+
triggeredByActor: jsonb(event.triggeredByActor),
|
|
18
|
+
context: jsonb(event.context),
|
|
19
|
+
status: event.status,
|
|
20
|
+
flowId: event.flowId,
|
|
21
|
+
causedByEventId: event.causedByEventId,
|
|
22
|
+
occurredAt: event.occurredAt,
|
|
23
|
+
publications: jsonb(event.publications),
|
|
24
|
+
priority: event.priority
|
|
25
|
+
})
|
|
26
|
+
).execute()
|
|
27
|
+
).pipe(Effect.asVoid),
|
|
28
|
+
saveNewEventsBatch: (events) => {
|
|
29
|
+
if (events.length === 0) return Effect.void;
|
|
30
|
+
return Effect.promise(
|
|
31
|
+
() => eventsDb.insertInto("events").values(
|
|
32
|
+
events.map((event) => ({
|
|
33
|
+
...event,
|
|
34
|
+
payload: jsonb(event.payload),
|
|
35
|
+
triggeredByActor: jsonb(event.triggeredByActor),
|
|
36
|
+
context: jsonb(event.context),
|
|
37
|
+
publications: jsonb(event.publications)
|
|
38
|
+
}))
|
|
39
|
+
).execute()
|
|
40
|
+
).pipe(Effect.asVoid);
|
|
41
|
+
},
|
|
42
|
+
markEventsAsInProcess: (events) => {
|
|
43
|
+
if (events.length === 0) return Effect.void;
|
|
44
|
+
const ids = events.map((e) => e.id);
|
|
45
|
+
return Effect.gen(function* () {
|
|
46
|
+
const lockedRows = yield* Effect.promise(
|
|
47
|
+
() => eventsDb.selectFrom("events").select("id").where("id", "in", ids).forUpdate().skipLocked().execute()
|
|
48
|
+
);
|
|
49
|
+
if (lockedRows.length === 0) return;
|
|
50
|
+
const lockedIds = lockedRows.map((r) => r.id);
|
|
51
|
+
yield* Effect.promise(
|
|
52
|
+
() => eventsDb.updateTable("events").set({ status: "in-process" }).where("id", "in", lockedIds).execute()
|
|
53
|
+
).pipe(Effect.asVoid);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
export {
|
|
59
|
+
createEffectKyselyEventRepository
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=EffectKyselyEventRepository.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/effect-kysely/EffectKyselyEventRepository.ts"],"sourcesContent":["import { Effect } from \"effect\";\nimport type { Kysely } from \"kysely\";\nimport type { EventRepository } from '../../effect/ports/EffectEventRepository.mjs';\nimport type { DefaultContext, GenericEvent } from '../../types.mjs';\nimport { jsonb } from '../kysely/jsonb.mjs';\nimport type { EventsTable } from '../kysely/types.mjs';\n\nexport const createEffectKyselyEventRepository = <\n Event extends GenericEvent<string, unknown, DefaultContext>,\n DB extends EventsTable = EventsTable,\n>(\n db: Kysely<DB>,\n): EventRepository<Event> => {\n const eventsDb = db as unknown as Kysely<EventsTable>;\n return {\n save: (event) =>\n Effect.promise(() =>\n eventsDb\n .insertInto(\"events\")\n .values({\n ...event,\n payload: jsonb(event.payload),\n triggeredByActor: jsonb(event.triggeredByActor),\n context: jsonb(event.context),\n publications: jsonb(event.publications),\n })\n .onConflict((oc) =>\n oc.column(\"id\").doUpdateSet({\n topic: event.topic,\n payload: jsonb(event.payload),\n triggeredByActor: jsonb(event.triggeredByActor),\n context: jsonb(event.context),\n status: event.status,\n flowId: event.flowId,\n causedByEventId: event.causedByEventId,\n occurredAt: event.occurredAt,\n publications: jsonb(event.publications),\n priority: event.priority,\n }),\n )\n .execute(),\n ).pipe(Effect.asVoid),\n\n saveNewEventsBatch: (events) => {\n if (events.length === 0) return Effect.void;\n return Effect.promise(() =>\n eventsDb\n .insertInto(\"events\")\n .values(\n events.map((event) => ({\n ...event,\n payload: jsonb(event.payload),\n triggeredByActor: jsonb(event.triggeredByActor),\n context: jsonb(event.context),\n publications: jsonb(event.publications),\n })),\n )\n .execute(),\n ).pipe(Effect.asVoid);\n },\n\n markEventsAsInProcess: (events) => {\n if (events.length === 0) return Effect.void;\n const ids = events.map((e) => e.id);\n\n return Effect.gen(function* () {\n const lockedRows = yield* Effect.promise(() =>\n eventsDb\n .selectFrom(\"events\")\n .select(\"id\")\n .where(\"id\", \"in\", ids)\n .forUpdate()\n .skipLocked()\n .execute(),\n );\n\n if (lockedRows.length === 0) return;\n const lockedIds = lockedRows.map((r) => r.id);\n\n yield* Effect.promise(() =>\n eventsDb\n .updateTable(\"events\")\n .set({ status: \"in-process\" })\n .where(\"id\", \"in\", lockedIds)\n .execute(),\n ).pipe(Effect.asVoid);\n });\n },\n };\n};\n"],"mappings":"AAAA,SAAS,cAAc;AAIvB,SAAS,aAAa;AAGf,MAAM,oCAAoC,CAI/C,OAC2B;AAC3B,QAAM,WAAW;AACjB,SAAO;AAAA,IACL,MAAM,CAAC,UACL,OAAO;AAAA,MAAQ,MACb,SACG,WAAW,QAAQ,EACnB,OAAO;AAAA,QACN,GAAG;AAAA,QACH,SAAS,MAAM,MAAM,OAAO;AAAA,QAC5B,kBAAkB,MAAM,MAAM,gBAAgB;AAAA,QAC9C,SAAS,MAAM,MAAM,OAAO;AAAA,QAC5B,cAAc,MAAM,MAAM,YAAY;AAAA,MACxC,CAAC,EACA;AAAA,QAAW,CAAC,OACX,GAAG,OAAO,IAAI,EAAE,YAAY;AAAA,UAC1B,OAAO,MAAM;AAAA,UACb,SAAS,MAAM,MAAM,OAAO;AAAA,UAC5B,kBAAkB,MAAM,MAAM,gBAAgB;AAAA,UAC9C,SAAS,MAAM,MAAM,OAAO;AAAA,UAC5B,QAAQ,MAAM;AAAA,UACd,QAAQ,MAAM;AAAA,UACd,iBAAiB,MAAM;AAAA,UACvB,YAAY,MAAM;AAAA,UAClB,cAAc,MAAM,MAAM,YAAY;AAAA,UACtC,UAAU,MAAM;AAAA,QAClB,CAAC;AAAA,MACH,EACC,QAAQ;AAAA,IACb,EAAE,KAAK,OAAO,MAAM;AAAA,IAEtB,oBAAoB,CAAC,WAAW;AAC9B,UAAI,OAAO,WAAW,EAAG,QAAO,OAAO;AACvC,aAAO,OAAO;AAAA,QAAQ,MACpB,SACG,WAAW,QAAQ,EACnB;AAAA,UACC,OAAO,IAAI,CAAC,WAAW;AAAA,YACrB,GAAG;AAAA,YACH,SAAS,MAAM,MAAM,OAAO;AAAA,YAC5B,kBAAkB,MAAM,MAAM,gBAAgB;AAAA,YAC9C,SAAS,MAAM,MAAM,OAAO;AAAA,YAC5B,cAAc,MAAM,MAAM,YAAY;AAAA,UACxC,EAAE;AAAA,QACJ,EACC,QAAQ;AAAA,MACb,EAAE,KAAK,OAAO,MAAM;AAAA,IACtB;AAAA,IAEA,uBAAuB,CAAC,WAAW;AACjC,UAAI,OAAO,WAAW,EAAG,QAAO,OAAO;AACvC,YAAM,MAAM,OAAO,IAAI,CAAC,MAAM,EAAE,EAAE;AAElC,aAAO,OAAO,IAAI,aAAa;AAC7B,cAAM,aAAa,OAAO,OAAO;AAAA,UAAQ,MACvC,SACG,WAAW,QAAQ,EACnB,OAAO,IAAI,EACX,MAAM,MAAM,MAAM,GAAG,EACrB,UAAU,EACV,WAAW,EACX,QAAQ;AAAA,QACb;AAEA,YAAI,WAAW,WAAW,EAAG;AAC7B,cAAM,YAAY,WAAW,IAAI,CAAC,MAAM,EAAE,EAAE;AAE5C,eAAO,OAAO;AAAA,UAAQ,MACpB,SACG,YAAY,QAAQ,EACpB,IAAI,EAAE,QAAQ,aAAa,CAAC,EAC5B,MAAM,MAAM,MAAM,SAAS,EAC3B,QAAQ;AAAA,QACb,EAAE,KAAK,OAAO,MAAM;AAAA,MACtB,CAAC;AAAA,IACH;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var effect_kysely_exports = {};
|
|
20
|
+
__export(effect_kysely_exports, {
|
|
21
|
+
createEffectKyselyEventQueries: () => import_EffectKyselyEventQueries.createEffectKyselyEventQueries,
|
|
22
|
+
createEffectKyselyEventRepository: () => import_EffectKyselyEventRepository.createEffectKyselyEventRepository
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(effect_kysely_exports);
|
|
25
|
+
var import_EffectKyselyEventQueries = require("./EffectKyselyEventQueries.ts");
|
|
26
|
+
var import_EffectKyselyEventRepository = require("./EffectKyselyEventRepository.ts");
|
|
27
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
28
|
+
0 && (module.exports = {
|
|
29
|
+
createEffectKyselyEventQueries,
|
|
30
|
+
createEffectKyselyEventRepository
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/effect-kysely/index.ts"],"sourcesContent":["export type { EventsTable, TypedEventsTable } from \"../kysely/types.ts\";\nexport { createEffectKyselyEventQueries } from \"./EffectKyselyEventQueries.ts\";\nexport { createEffectKyselyEventRepository } from \"./EffectKyselyEventRepository.ts\";\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,sCAA+C;AAC/C,yCAAkD;","names":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { EventsTable, TypedEventsTable } from '../kysely/types.cjs';
|
|
2
|
+
export { createEffectKyselyEventQueries } from './EffectKyselyEventQueries.cjs';
|
|
3
|
+
export { createEffectKyselyEventRepository } from './EffectKyselyEventRepository.cjs';
|
|
4
|
+
import '../../types.cjs';
|
|
5
|
+
import 'kysely';
|
|
6
|
+
import '../../effect/ports/EffectEventQueries.cjs';
|
|
7
|
+
import 'effect';
|
|
8
|
+
import '../../ports/EventQueries.cjs';
|
|
9
|
+
import '../../effect/ports/EffectEventRepository.cjs';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { EventsTable, TypedEventsTable } from '../kysely/types.js';
|
|
2
|
+
export { createEffectKyselyEventQueries } from './EffectKyselyEventQueries.js';
|
|
3
|
+
export { createEffectKyselyEventRepository } from './EffectKyselyEventRepository.js';
|
|
4
|
+
import '../../types.js';
|
|
5
|
+
import 'kysely';
|
|
6
|
+
import '../../effect/ports/EffectEventQueries.js';
|
|
7
|
+
import 'effect';
|
|
8
|
+
import '../../ports/EventQueries.js';
|
|
9
|
+
import '../../effect/ports/EffectEventRepository.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createEffectKyselyEventQueries } from "./EffectKyselyEventQueries.mjs";
|
|
2
|
+
import { createEffectKyselyEventRepository } from "./EffectKyselyEventRepository.mjs";
|
|
3
|
+
export {
|
|
4
|
+
createEffectKyselyEventQueries,
|
|
5
|
+
createEffectKyselyEventRepository
|
|
6
|
+
};
|
|
7
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/effect-kysely/index.ts"],"sourcesContent":["export type { EventsTable, TypedEventsTable } from '../kysely/types.mjs';\nexport { createEffectKyselyEventQueries } from './EffectKyselyEventQueries.mjs';\nexport { createEffectKyselyEventRepository } from './EffectKyselyEventRepository.mjs';\n"],"mappings":"AACA,SAAS,sCAAsC;AAC/C,SAAS,yCAAyC;","names":[]}
|
|
@@ -22,6 +22,7 @@ __export(InMemoryEventBus_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(InMemoryEventBus_exports);
|
|
24
24
|
var import_createNewEvent = require("../../createNewEvent.ts");
|
|
25
|
+
var import_getSubscriptionIdsToPublish = require("../../getSubscriptionIdsToPublish.ts");
|
|
25
26
|
var import_subscriptions = require("../../subscriptions.ts");
|
|
26
27
|
function createInMemoryEventBus(withUow, options = {}) {
|
|
27
28
|
const maxRetries = options.maxRetries ?? 3;
|
|
@@ -46,21 +47,6 @@ function createInMemoryEventBus(withUow, options = {}) {
|
|
|
46
47
|
};
|
|
47
48
|
}
|
|
48
49
|
};
|
|
49
|
-
const getSubscriptionIdsToPublish = (event, callbacksBySubscriptionId) => {
|
|
50
|
-
const allSubscriptionIds = Object.keys(callbacksBySubscriptionId);
|
|
51
|
-
if (event.publications.length === 0 || event.status === "to-republish") {
|
|
52
|
-
return allSubscriptionIds;
|
|
53
|
-
}
|
|
54
|
-
const lastPublication = event.publications.reduce(
|
|
55
|
-
(latest, current) => current.publishedAt > latest.publishedAt ? current : latest
|
|
56
|
-
);
|
|
57
|
-
const failedSubscriptionIds = (lastPublication.failures ?? []).map(
|
|
58
|
-
(failure) => failure.subscriptionId
|
|
59
|
-
);
|
|
60
|
-
return allSubscriptionIds.filter(
|
|
61
|
-
(id) => failedSubscriptionIds.includes(id)
|
|
62
|
-
);
|
|
63
|
-
};
|
|
64
50
|
const eventBus = {
|
|
65
51
|
publish: async (event) => {
|
|
66
52
|
const publishedAt = /* @__PURE__ */ new Date();
|
|
@@ -77,9 +63,9 @@ function createInMemoryEventBus(withUow, options = {}) {
|
|
|
77
63
|
});
|
|
78
64
|
return;
|
|
79
65
|
}
|
|
80
|
-
const subscriptionIdsToPublish = getSubscriptionIdsToPublish(
|
|
66
|
+
const subscriptionIdsToPublish = (0, import_getSubscriptionIdsToPublish.getSubscriptionIdsToPublish)(
|
|
81
67
|
event,
|
|
82
|
-
callbacksBySubscriptionSlug
|
|
68
|
+
Object.keys(callbacksBySubscriptionSlug)
|
|
83
69
|
);
|
|
84
70
|
const failuresOrUndefined = await Promise.all(
|
|
85
71
|
subscriptionIdsToPublish.map(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/adapters/in-memory/InMemoryEventBus.ts"],"sourcesContent":["import {\n type CreateNewEvent,\n type CreateNewEventFromDefinitions,\n makeCreateNewEvent,\n} from \"../../createNewEvent.ts\";\nimport type {\n EventDefinitions,\n InferEventsFromDefinitions,\n} from \"../../eventDefinitions.ts\";\nimport type { EventBus } from \"../../ports/EventBus.ts\";\nimport type { WithEventsUow } from \"../../ports/EventRepository.ts\";\nimport {\n type GlobalSubscriberConfig,\n subscribeByTopic,\n subscribeGlobalToTopics,\n type TopicSubscriptions,\n} from \"../../subscriptions.ts\";\nimport type {\n DefaultContext,\n EventId,\n EventPublication,\n GenericEvent,\n SubscriptionId,\n} from \"../../types.ts\";\n\ntype SubscriptionsForTopic = Record<\n string,\n (event: GenericEvent<string, unknown, DefaultContext>) => Promise<void>\n>;\n\ntype CreateInMemoryEventBusOptions = {\n maxRetries?: number;\n getNow?: () => Date;\n generateId?: () => EventId;\n};\n\ntype CreateInMemoryEventBusFromDefinitionsOptions<\n Definitions extends EventDefinitions,\n> = CreateInMemoryEventBusOptions & {\n eventDefinitions: Definitions;\n};\n\ntype CreateInMemoryEventBusResult<\n Event extends GenericEvent<string, unknown, DefaultContext>,\n> = {\n eventBus: EventBus<Event>;\n createNewEvent: CreateNewEvent<Event>;\n defineSubscriptions: (\n subscriptions: TopicSubscriptions<Event>,\n ) => TopicSubscriptions<Event>;\n subscribeAll: (subscriptions: TopicSubscriptions<Event>) => void;\n subscribeGlobal: (\n subscriptions: TopicSubscriptions<Event>,\n config: GlobalSubscriberConfig<Event>,\n ) => void;\n};\n\nexport function createInMemoryEventBus<\n Event extends GenericEvent<string, unknown, DefaultContext>,\n>(\n withUow: WithEventsUow<Event>,\n options?: CreateInMemoryEventBusOptions,\n): CreateInMemoryEventBusResult<Event>;\nexport function createInMemoryEventBus<Definitions extends EventDefinitions>(\n withUow: WithEventsUow<InferEventsFromDefinitions<Definitions>>,\n options: CreateInMemoryEventBusFromDefinitionsOptions<Definitions>,\n): Omit<\n CreateInMemoryEventBusResult<InferEventsFromDefinitions<Definitions>>,\n \"createNewEvent\"\n> & {\n createNewEvent: CreateNewEventFromDefinitions<Definitions>;\n};\nexport function createInMemoryEventBus<\n Event extends GenericEvent<string, unknown, DefaultContext>,\n>(\n withUow: WithEventsUow<Event>,\n options:\n | CreateInMemoryEventBusOptions\n | CreateInMemoryEventBusFromDefinitionsOptions<EventDefinitions> = {},\n) {\n const maxRetries = options.maxRetries ?? 3;\n const eventDefinitions =\n \"eventDefinitions\" in options ? options.eventDefinitions : undefined;\n const createNewEvent = eventDefinitions\n ? makeCreateNewEvent({\n getNow: options.getNow,\n generateId: options.generateId,\n eventDefinitions,\n })\n : makeCreateNewEvent<Event>({\n getNow: options.getNow,\n generateId: options.generateId,\n });\n const subscriptions: Partial<Record<string, SubscriptionsForTopic>> = {};\n\n const executeCallback = async (\n event: Event,\n subscriptionId: string,\n callback: (\n event: GenericEvent<string, unknown, DefaultContext>,\n ) => Promise<void>,\n ): Promise<\n { subscriptionId: string; errorMessage: string; stack?: string } | undefined\n > => {\n try {\n await callback(event);\n } catch (error) {\n return {\n subscriptionId,\n errorMessage: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n }\n };\n\n const getSubscriptionIdsToPublish = (\n event: Event,\n callbacksBySubscriptionId: SubscriptionsForTopic,\n ): string[] => {\n const allSubscriptionIds = Object.keys(callbacksBySubscriptionId);\n\n if (event.publications.length === 0 || event.status === \"to-republish\") {\n return allSubscriptionIds;\n }\n\n const lastPublication = event.publications.reduce((latest, current) =>\n current.publishedAt > latest.publishedAt ? current : latest,\n );\n const failedSubscriptionIds = (lastPublication.failures ?? []).map(\n (failure) => failure.subscriptionId,\n );\n\n return allSubscriptionIds.filter((id) =>\n failedSubscriptionIds.includes(id),\n );\n };\n\n const eventBus: EventBus<Event> = {\n publish: async (event) => {\n const publishedAt = new Date();\n const topic = event.topic;\n\n const callbacksBySubscriptionSlug = subscriptions[topic];\n\n if (!callbacksBySubscriptionSlug) {\n event.publications.push({\n publishedAt,\n publishedSubscribers: [],\n });\n event.status = \"published\";\n await withUow(async (uow) => {\n await uow.eventRepository.save(event);\n });\n return;\n }\n\n const subscriptionIdsToPublish = getSubscriptionIdsToPublish(\n event,\n callbacksBySubscriptionSlug,\n );\n\n const failuresOrUndefined = await Promise.all(\n subscriptionIdsToPublish.map((subscriptionId) =>\n executeCallback(\n event,\n subscriptionId,\n callbacksBySubscriptionSlug[subscriptionId],\n ),\n ),\n );\n\n const failures = failuresOrUndefined.filter(\n (\n f,\n ): f is {\n subscriptionId: string;\n errorMessage: string;\n stack?: string;\n } => f !== undefined,\n );\n\n const publications: EventPublication[] = [\n ...event.publications,\n {\n publishedAt,\n publishedSubscribers: subscriptionIdsToPublish.map(\n (id) => id as SubscriptionId,\n ),\n ...(failures.length > 0 && { failures }),\n },\n ];\n\n if (failures.length === 0) {\n event.status = \"published\";\n } else {\n const wasMaxNumberOfErrorsReached = publications.length >= maxRetries;\n event.status = wasMaxNumberOfErrorsReached\n ? \"quarantined\"\n : \"failed-but-will-retry\";\n }\n\n event.publications = publications;\n\n await withUow(async (uow) => {\n await uow.eventRepository.save(event);\n });\n },\n\n subscribe: ({ topic, subscriptionId, callBack }) => {\n if (!subscriptions[topic]) {\n subscriptions[topic] = {};\n }\n\n const subscriptionsForTopic = subscriptions[topic];\n if (subscriptionsForTopic) {\n subscriptionsForTopic[subscriptionId] = callBack as (\n event: GenericEvent<string, unknown, DefaultContext>,\n ) => Promise<void>;\n }\n },\n };\n\n /**\n * Identity function for type inference when defining subscription maps.\n * Ensures all topics are covered and handlers have correct payload types.\n *\n * @example\n * ```typescript\n * const subscriptions = defineSubscriptions({\n * OrderCreated: [{ subscriptionId: \"notify\", handler: async (e) => {...} }],\n * OrderShipped: [], // Required even if empty\n * });\n * ```\n */\n const defineSubscriptions = (\n subscriptions: TopicSubscriptions<Event>,\n ): TopicSubscriptions<Event> => subscriptions;\n\n /**\n * Subscribe all handlers from a topic subscription map to this event bus.\n *\n * @example\n * ```typescript\n * const subscriptions = defineSubscriptions({...});\n * subscribeAll(subscriptions);\n * ```\n */\n const subscribeAll = (subscriptions: TopicSubscriptions<Event>): void => {\n subscribeByTopic(eventBus, subscriptions);\n };\n\n /**\n * Subscribe a global handler to multiple topics with optional filtering.\n *\n * @example\n * ```typescript\n * subscribeGlobal(subscriptions, {\n * subscriptionId: \"audit-log\",\n * handler: async (event) => auditLog.record(event),\n * filter: { exclude: [\"NotificationAdded\"] },\n * });\n * ```\n */\n const subscribeGlobal = (\n subscriptions: TopicSubscriptions<Event>,\n config: GlobalSubscriberConfig<Event>,\n ): void => {\n subscribeGlobalToTopics(eventBus, subscriptions, config);\n };\n\n return {\n eventBus,\n createNewEvent,\n defineSubscriptions,\n subscribeAll,\n subscribeGlobal,\n };\n}\n"],"mappings":"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","names":["subscriptions"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/in-memory/InMemoryEventBus.ts"],"sourcesContent":["import {\n type CreateNewEvent,\n type CreateNewEventFromDefinitions,\n makeCreateNewEvent,\n} from \"../../createNewEvent.ts\";\nimport type {\n EventDefinitions,\n InferEventsFromDefinitions,\n} from \"../../eventDefinitions.ts\";\nimport { getSubscriptionIdsToPublish } from \"../../getSubscriptionIdsToPublish.ts\";\nimport type { EventBus } from \"../../ports/EventBus.ts\";\nimport type { WithEventsUow } from \"../../ports/EventRepository.ts\";\nimport {\n type GlobalSubscriberConfig,\n subscribeByTopic,\n subscribeGlobalToTopics,\n type TopicSubscriptions,\n} from \"../../subscriptions.ts\";\nimport type {\n DefaultContext,\n EventId,\n EventPublication,\n GenericEvent,\n SubscriptionId,\n} from \"../../types.ts\";\n\ntype SubscriptionsForTopic = Record<\n string,\n (event: GenericEvent<string, unknown, DefaultContext>) => Promise<void>\n>;\n\ntype CreateInMemoryEventBusOptions = {\n maxRetries?: number;\n getNow?: () => Date;\n generateId?: () => EventId;\n};\n\ntype CreateInMemoryEventBusFromDefinitionsOptions<\n Definitions extends EventDefinitions,\n> = CreateInMemoryEventBusOptions & {\n eventDefinitions: Definitions;\n};\n\ntype CreateInMemoryEventBusResult<\n Event extends GenericEvent<string, unknown, DefaultContext>,\n> = {\n eventBus: EventBus<Event>;\n createNewEvent: CreateNewEvent<Event>;\n defineSubscriptions: (\n subscriptions: TopicSubscriptions<Event>,\n ) => TopicSubscriptions<Event>;\n subscribeAll: (subscriptions: TopicSubscriptions<Event>) => void;\n subscribeGlobal: (\n subscriptions: TopicSubscriptions<Event>,\n config: GlobalSubscriberConfig<Event>,\n ) => void;\n};\n\nexport function createInMemoryEventBus<\n Event extends GenericEvent<string, unknown, DefaultContext>,\n>(\n withUow: WithEventsUow<Event>,\n options?: CreateInMemoryEventBusOptions,\n): CreateInMemoryEventBusResult<Event>;\nexport function createInMemoryEventBus<Definitions extends EventDefinitions>(\n withUow: WithEventsUow<InferEventsFromDefinitions<Definitions>>,\n options: CreateInMemoryEventBusFromDefinitionsOptions<Definitions>,\n): Omit<\n CreateInMemoryEventBusResult<InferEventsFromDefinitions<Definitions>>,\n \"createNewEvent\"\n> & {\n createNewEvent: CreateNewEventFromDefinitions<Definitions>;\n};\nexport function createInMemoryEventBus<\n Event extends GenericEvent<string, unknown, DefaultContext>,\n>(\n withUow: WithEventsUow<Event>,\n options:\n | CreateInMemoryEventBusOptions\n | CreateInMemoryEventBusFromDefinitionsOptions<EventDefinitions> = {},\n) {\n const maxRetries = options.maxRetries ?? 3;\n const eventDefinitions =\n \"eventDefinitions\" in options ? options.eventDefinitions : undefined;\n const createNewEvent = eventDefinitions\n ? makeCreateNewEvent({\n getNow: options.getNow,\n generateId: options.generateId,\n eventDefinitions,\n })\n : makeCreateNewEvent<Event>({\n getNow: options.getNow,\n generateId: options.generateId,\n });\n const subscriptions: Partial<Record<string, SubscriptionsForTopic>> = {};\n\n const executeCallback = async (\n event: Event,\n subscriptionId: string,\n callback: (\n event: GenericEvent<string, unknown, DefaultContext>,\n ) => Promise<void>,\n ): Promise<\n { subscriptionId: string; errorMessage: string; stack?: string } | undefined\n > => {\n try {\n await callback(event);\n } catch (error) {\n return {\n subscriptionId,\n errorMessage: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n }\n };\n\n const eventBus: EventBus<Event> = {\n publish: async (event) => {\n const publishedAt = new Date();\n const topic = event.topic;\n\n const callbacksBySubscriptionSlug = subscriptions[topic];\n\n if (!callbacksBySubscriptionSlug) {\n event.publications.push({\n publishedAt,\n publishedSubscribers: [],\n });\n event.status = \"published\";\n await withUow(async (uow) => {\n await uow.eventRepository.save(event);\n });\n return;\n }\n\n const subscriptionIdsToPublish = getSubscriptionIdsToPublish(\n event,\n Object.keys(callbacksBySubscriptionSlug),\n );\n\n const failuresOrUndefined = await Promise.all(\n subscriptionIdsToPublish.map((subscriptionId) =>\n executeCallback(\n event,\n subscriptionId,\n callbacksBySubscriptionSlug[subscriptionId],\n ),\n ),\n );\n\n const failures = failuresOrUndefined.filter(\n (\n f,\n ): f is {\n subscriptionId: string;\n errorMessage: string;\n stack?: string;\n } => f !== undefined,\n );\n\n const publications: EventPublication[] = [\n ...event.publications,\n {\n publishedAt,\n publishedSubscribers: subscriptionIdsToPublish.map(\n (id) => id as SubscriptionId,\n ),\n ...(failures.length > 0 && { failures }),\n },\n ];\n\n if (failures.length === 0) {\n event.status = \"published\";\n } else {\n const wasMaxNumberOfErrorsReached = publications.length >= maxRetries;\n event.status = wasMaxNumberOfErrorsReached\n ? \"quarantined\"\n : \"failed-but-will-retry\";\n }\n\n event.publications = publications;\n\n await withUow(async (uow) => {\n await uow.eventRepository.save(event);\n });\n },\n\n subscribe: ({ topic, subscriptionId, callBack }) => {\n if (!subscriptions[topic]) {\n subscriptions[topic] = {};\n }\n\n const subscriptionsForTopic = subscriptions[topic];\n if (subscriptionsForTopic) {\n subscriptionsForTopic[subscriptionId] = callBack as (\n event: GenericEvent<string, unknown, DefaultContext>,\n ) => Promise<void>;\n }\n },\n };\n\n /**\n * Identity function for type inference when defining subscription maps.\n * Ensures all topics are covered and handlers have correct payload types.\n *\n * @example\n * ```typescript\n * const subscriptions = defineSubscriptions({\n * OrderCreated: [{ subscriptionId: \"notify\", handler: async (e) => {...} }],\n * OrderShipped: [], // Required even if empty\n * });\n * ```\n */\n const defineSubscriptions = (\n subscriptions: TopicSubscriptions<Event>,\n ): TopicSubscriptions<Event> => subscriptions;\n\n /**\n * Subscribe all handlers from a topic subscription map to this event bus.\n *\n * @example\n * ```typescript\n * const subscriptions = defineSubscriptions({...});\n * subscribeAll(subscriptions);\n * ```\n */\n const subscribeAll = (subscriptions: TopicSubscriptions<Event>): void => {\n subscribeByTopic(eventBus, subscriptions);\n };\n\n /**\n * Subscribe a global handler to multiple topics with optional filtering.\n *\n * @example\n * ```typescript\n * subscribeGlobal(subscriptions, {\n * subscriptionId: \"audit-log\",\n * handler: async (event) => auditLog.record(event),\n * filter: { exclude: [\"NotificationAdded\"] },\n * });\n * ```\n */\n const subscribeGlobal = (\n subscriptions: TopicSubscriptions<Event>,\n config: GlobalSubscriberConfig<Event>,\n ): void => {\n subscribeGlobalToTopics(eventBus, subscriptions, config);\n };\n\n return {\n eventBus,\n createNewEvent,\n defineSubscriptions,\n subscribeAll,\n subscribeGlobal,\n };\n}\n"],"mappings":"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","names":["subscriptions"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
makeCreateNewEvent
|
|
3
3
|
} from "../../createNewEvent.mjs";
|
|
4
|
+
import { getSubscriptionIdsToPublish } from "../../getSubscriptionIdsToPublish.mjs";
|
|
4
5
|
import {
|
|
5
6
|
subscribeByTopic,
|
|
6
7
|
subscribeGlobalToTopics
|
|
@@ -28,21 +29,6 @@ function createInMemoryEventBus(withUow, options = {}) {
|
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
31
|
};
|
|
31
|
-
const getSubscriptionIdsToPublish = (event, callbacksBySubscriptionId) => {
|
|
32
|
-
const allSubscriptionIds = Object.keys(callbacksBySubscriptionId);
|
|
33
|
-
if (event.publications.length === 0 || event.status === "to-republish") {
|
|
34
|
-
return allSubscriptionIds;
|
|
35
|
-
}
|
|
36
|
-
const lastPublication = event.publications.reduce(
|
|
37
|
-
(latest, current) => current.publishedAt > latest.publishedAt ? current : latest
|
|
38
|
-
);
|
|
39
|
-
const failedSubscriptionIds = (lastPublication.failures ?? []).map(
|
|
40
|
-
(failure) => failure.subscriptionId
|
|
41
|
-
);
|
|
42
|
-
return allSubscriptionIds.filter(
|
|
43
|
-
(id) => failedSubscriptionIds.includes(id)
|
|
44
|
-
);
|
|
45
|
-
};
|
|
46
32
|
const eventBus = {
|
|
47
33
|
publish: async (event) => {
|
|
48
34
|
const publishedAt = /* @__PURE__ */ new Date();
|
|
@@ -61,7 +47,7 @@ function createInMemoryEventBus(withUow, options = {}) {
|
|
|
61
47
|
}
|
|
62
48
|
const subscriptionIdsToPublish = getSubscriptionIdsToPublish(
|
|
63
49
|
event,
|
|
64
|
-
callbacksBySubscriptionSlug
|
|
50
|
+
Object.keys(callbacksBySubscriptionSlug)
|
|
65
51
|
);
|
|
66
52
|
const failuresOrUndefined = await Promise.all(
|
|
67
53
|
subscriptionIdsToPublish.map(
|