@l-etabli/events 0.5.0 → 0.6.1
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 +146 -155
- package/dist/adapters/in-memory/InMemoryEventBus.cjs +8 -3
- package/dist/adapters/in-memory/InMemoryEventBus.cjs.map +1 -1
- package/dist/adapters/in-memory/InMemoryEventBus.d.cts +10 -2
- package/dist/adapters/in-memory/InMemoryEventBus.d.ts +10 -2
- package/dist/adapters/in-memory/InMemoryEventBus.mjs +11 -4
- package/dist/adapters/in-memory/InMemoryEventBus.mjs.map +1 -1
- package/dist/adapters/in-memory/index.d.cts +1 -0
- package/dist/adapters/in-memory/index.d.ts +1 -0
- package/dist/adapters/kysely/KyselyEventQueries.cjs +27 -22
- 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 +27 -22
- package/dist/adapters/kysely/KyselyEventQueries.mjs.map +1 -1
- package/dist/adapters/kysely/KyselyEventRepository.cjs +43 -36
- 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 -36
- package/dist/adapters/kysely/KyselyEventRepository.mjs.map +1 -1
- package/dist/adapters/kysely/migration.cjs +1 -1
- package/dist/adapters/kysely/migration.cjs.map +1 -1
- package/dist/adapters/kysely/migration.mjs +1 -1
- package/dist/adapters/kysely/migration.mjs.map +1 -1
- package/dist/adapters/kysely/types.cjs.map +1 -1
- package/dist/adapters/kysely/types.d.cts +7 -3
- package/dist/adapters/kysely/types.d.ts +7 -3
- package/dist/createNewEvent.cjs +19 -12
- package/dist/createNewEvent.cjs.map +1 -1
- package/dist/createNewEvent.d.cts +43 -39
- package/dist/createNewEvent.d.ts +43 -39
- package/dist/createNewEvent.mjs +19 -12
- package/dist/createNewEvent.mjs.map +1 -1
- package/dist/eventDefinitions.cjs +32 -0
- package/dist/eventDefinitions.cjs.map +1 -0
- package/dist/eventDefinitions.d.cts +20 -0
- package/dist/eventDefinitions.d.ts +20 -0
- package/dist/eventDefinitions.mjs +7 -0
- package/dist/eventDefinitions.mjs.map +1 -0
- package/dist/index.cjs +5 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -1
- package/dist/types.cjs +37 -0
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +42 -3
- package/dist/types.d.ts +42 -3
- package/dist/types.mjs +25 -0
- package/dist/types.mjs.map +1 -1
- package/package.json +2 -2
- package/src/adapters/in-memory/InMemoryEventBus.ts +63 -9
- package/src/adapters/kysely/KyselyEventQueries.ts +35 -29
- package/src/adapters/kysely/KyselyEventRepository.ts +65 -57
- package/src/adapters/kysely/migration.ts +3 -1
- package/src/adapters/kysely/types.ts +7 -2
- package/src/createNewEvent.ts +116 -48
- package/src/eventDefinitions.ts +53 -0
- package/src/index.ts +2 -1
- package/src/types.ts +74 -2
package/src/createNewEvent.ts
CHANGED
|
@@ -1,87 +1,155 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
EventDefinitions,
|
|
3
|
+
InferEventsFromDefinitions,
|
|
4
|
+
} from "./eventDefinitions.ts";
|
|
5
|
+
import type { Actor, DefaultContext, EventId, GenericEvent } from "./types.ts";
|
|
2
6
|
|
|
3
7
|
type MakeCreateNewEventOptions = {
|
|
4
8
|
getNow?: () => Date;
|
|
5
9
|
generateId?: () => EventId;
|
|
6
10
|
};
|
|
7
11
|
|
|
8
|
-
type
|
|
12
|
+
type MakeCreateNewEventFromDefinitionsOptions<
|
|
13
|
+
Definitions extends EventDefinitions,
|
|
14
|
+
> = MakeCreateNewEventOptions & {
|
|
15
|
+
eventDefinitions: Definitions;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type DeclaredContextForEvent<
|
|
19
|
+
Event extends GenericEvent<string, unknown, DefaultContext>,
|
|
20
|
+
Topic extends Event["topic"],
|
|
21
|
+
> = Exclude<Extract<Event, { topic: Topic }>["context"], undefined>;
|
|
22
|
+
|
|
23
|
+
type ContextParamForEvent<
|
|
9
24
|
Event extends GenericEvent<string, unknown, DefaultContext>,
|
|
10
25
|
Topic extends Event["topic"],
|
|
11
|
-
> =
|
|
26
|
+
> = [DeclaredContextForEvent<Event, Topic>] extends [never]
|
|
12
27
|
? { context?: undefined }
|
|
13
|
-
: { context:
|
|
28
|
+
: { context: DeclaredContextForEvent<Event, Topic> };
|
|
29
|
+
|
|
30
|
+
type BaseCreateNewEventParams = {
|
|
31
|
+
triggeredByActor: Actor;
|
|
32
|
+
id?: EventId;
|
|
33
|
+
occurredAt?: Date;
|
|
34
|
+
flowId?: string;
|
|
35
|
+
causedByEventId?: EventId;
|
|
36
|
+
};
|
|
14
37
|
|
|
15
38
|
type CreateNewEventParams<
|
|
16
39
|
Event extends GenericEvent<string, unknown, DefaultContext>,
|
|
17
40
|
Topic extends Event["topic"],
|
|
18
|
-
> = {
|
|
41
|
+
> = BaseCreateNewEventParams & {
|
|
19
42
|
topic: Topic;
|
|
20
43
|
payload: Extract<Event, { topic: Topic }>["payload"];
|
|
21
|
-
triggeredByUserId: UserId;
|
|
22
|
-
id?: EventId;
|
|
23
|
-
occurredAt?: Date;
|
|
24
44
|
priority?: number;
|
|
25
|
-
} &
|
|
45
|
+
} & ContextParamForEvent<Event, Topic>;
|
|
46
|
+
|
|
47
|
+
type PayloadFromDefinitions<
|
|
48
|
+
Definitions extends EventDefinitions,
|
|
49
|
+
Topic extends keyof Definitions & string,
|
|
50
|
+
> = Definitions[Topic] extends {
|
|
51
|
+
_payload?: infer Payload;
|
|
52
|
+
}
|
|
53
|
+
? Payload
|
|
54
|
+
: never;
|
|
55
|
+
|
|
56
|
+
type DeclaredContextFromDefinitions<
|
|
57
|
+
Definitions extends EventDefinitions,
|
|
58
|
+
Topic extends keyof Definitions & string,
|
|
59
|
+
> = Exclude<
|
|
60
|
+
Definitions[Topic] extends { _context?: infer Context } ? Context : never,
|
|
61
|
+
undefined
|
|
62
|
+
>;
|
|
63
|
+
|
|
64
|
+
type ContextParamFromDefinitions<
|
|
65
|
+
Definitions extends EventDefinitions,
|
|
66
|
+
Topic extends keyof Definitions & string,
|
|
67
|
+
> = [DeclaredContextFromDefinitions<Definitions, Topic>] extends [never]
|
|
68
|
+
? { context?: undefined }
|
|
69
|
+
: { context: DeclaredContextFromDefinitions<Definitions, Topic> };
|
|
70
|
+
|
|
71
|
+
type CreateNewEventParamsFromDefinitions<
|
|
72
|
+
Definitions extends EventDefinitions,
|
|
73
|
+
Topic extends keyof Definitions & string,
|
|
74
|
+
> = BaseCreateNewEventParams & {
|
|
75
|
+
topic: Topic;
|
|
76
|
+
payload: PayloadFromDefinitions<Definitions, Topic>;
|
|
77
|
+
} & ContextParamFromDefinitions<Definitions, Topic>;
|
|
26
78
|
|
|
27
79
|
/**
|
|
28
80
|
* Creates a typed event creator factory for your event union.
|
|
29
81
|
* Provides type-safe event creation where topic constrains payload type.
|
|
30
|
-
*
|
|
31
|
-
* @param options.getNow - Function to get current time (default: `() => new Date()`)
|
|
32
|
-
* @param options.generateId - Function to generate event IDs (default: `() => crypto.randomUUID()`)
|
|
33
|
-
*
|
|
34
|
-
* @example
|
|
35
|
-
* ```typescript
|
|
36
|
-
* type MyEvents =
|
|
37
|
-
* | GenericEvent<"UserCreated", { email: string }>
|
|
38
|
-
* | GenericEvent<"OrderPlaced", { orderId: string }>;
|
|
39
|
-
*
|
|
40
|
-
* // Standalone usage:
|
|
41
|
-
* const createEvent = makeCreateNewEvent<MyEvents>();
|
|
42
|
-
*
|
|
43
|
-
* // Or get it from createInMemoryEventBus (recommended):
|
|
44
|
-
* const { eventBus, createEvent } = createInMemoryEventBus<MyEvents>(withUow);
|
|
45
|
-
*
|
|
46
|
-
* // Type-safe: payload must match topic
|
|
47
|
-
* createEvent({ topic: "UserCreated", payload: { email: "a@b.com" }, triggeredByUserId: "u1" }); // OK
|
|
48
|
-
* createEvent({ topic: "UserCreated", payload: { orderId: "123" }, triggeredByUserId: "u1" }); // Error!
|
|
49
|
-
*
|
|
50
|
-
* // For testing, inject deterministic functions:
|
|
51
|
-
* const createEvent = makeCreateNewEvent<MyEvents>({
|
|
52
|
-
* getNow: () => new Date("2024-01-01"),
|
|
53
|
-
* generateId: () => "test-id",
|
|
54
|
-
* });
|
|
55
|
-
* ```
|
|
56
82
|
*/
|
|
57
|
-
|
|
58
83
|
export type CreateNewEvent<
|
|
59
84
|
Event extends GenericEvent<string, unknown, DefaultContext>,
|
|
60
85
|
> = <Topic extends Event["topic"]>(
|
|
61
86
|
params: CreateNewEventParams<Event, Topic>,
|
|
62
87
|
) => Extract<Event, { topic: Topic }>;
|
|
63
88
|
|
|
64
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Creates a typed event creator factory directly from event definitions.
|
|
91
|
+
* Priority is injected from the canonical topic definition.
|
|
92
|
+
*/
|
|
93
|
+
export type CreateNewEventFromDefinitions<
|
|
94
|
+
Definitions extends EventDefinitions,
|
|
95
|
+
> = <Topic extends keyof Definitions & string>(
|
|
96
|
+
params: CreateNewEventParamsFromDefinitions<Definitions, Topic>,
|
|
97
|
+
) => Extract<InferEventsFromDefinitions<Definitions>, { topic: Topic }>;
|
|
98
|
+
|
|
99
|
+
export function makeCreateNewEvent<
|
|
65
100
|
Event extends GenericEvent<string, unknown, DefaultContext>,
|
|
101
|
+
>(options?: MakeCreateNewEventOptions): CreateNewEvent<Event>;
|
|
102
|
+
export function makeCreateNewEvent<Definitions extends EventDefinitions>(
|
|
103
|
+
options: MakeCreateNewEventFromDefinitionsOptions<Definitions>,
|
|
104
|
+
): CreateNewEventFromDefinitions<Definitions>;
|
|
105
|
+
export function makeCreateNewEvent<
|
|
106
|
+
Event extends GenericEvent<string, unknown, DefaultContext>,
|
|
107
|
+
Definitions extends EventDefinitions,
|
|
66
108
|
>(
|
|
67
|
-
options:
|
|
68
|
-
|
|
109
|
+
options:
|
|
110
|
+
| MakeCreateNewEventOptions
|
|
111
|
+
| MakeCreateNewEventFromDefinitionsOptions<Definitions> = {},
|
|
112
|
+
): CreateNewEvent<Event> | CreateNewEventFromDefinitions<Definitions> {
|
|
69
113
|
const getNow = options.getNow ?? (() => new Date());
|
|
70
114
|
const generateId =
|
|
71
115
|
options.generateId ?? (() => crypto.randomUUID() as EventId);
|
|
116
|
+
const eventDefinitions =
|
|
117
|
+
"eventDefinitions" in options ? options.eventDefinitions : undefined;
|
|
118
|
+
|
|
119
|
+
return ((params: {
|
|
120
|
+
topic: string;
|
|
121
|
+
payload: unknown;
|
|
122
|
+
triggeredByActor: Actor;
|
|
123
|
+
id?: EventId;
|
|
124
|
+
occurredAt?: Date;
|
|
125
|
+
flowId?: string;
|
|
126
|
+
causedByEventId?: EventId;
|
|
127
|
+
context?: DefaultContext;
|
|
128
|
+
priority?: number;
|
|
129
|
+
}) => {
|
|
130
|
+
const definitionPriority = eventDefinitions?.[params.topic]?.priority;
|
|
131
|
+
const priority =
|
|
132
|
+
definitionPriority ??
|
|
133
|
+
("priority" in params
|
|
134
|
+
? (params.priority as number | undefined)
|
|
135
|
+
: undefined);
|
|
72
136
|
|
|
73
|
-
|
|
74
|
-
params: CreateNewEventParams<Event, Topic>,
|
|
75
|
-
): Extract<Event, { topic: Topic }> =>
|
|
76
|
-
({
|
|
137
|
+
return {
|
|
77
138
|
id: params.id ?? generateId(),
|
|
78
139
|
topic: params.topic,
|
|
79
140
|
payload: params.payload,
|
|
80
|
-
|
|
141
|
+
triggeredByActor: params.triggeredByActor,
|
|
81
142
|
occurredAt: params.occurredAt ?? getNow(),
|
|
82
143
|
status: "never-published",
|
|
83
144
|
publications: [],
|
|
84
|
-
priority:
|
|
85
|
-
context: params.context,
|
|
86
|
-
|
|
87
|
-
|
|
145
|
+
...(priority !== undefined ? { priority } : {}),
|
|
146
|
+
...(params.context !== undefined ? { context: params.context } : {}),
|
|
147
|
+
...(params.flowId !== undefined ? { flowId: params.flowId } : {}),
|
|
148
|
+
...(params.causedByEventId !== undefined
|
|
149
|
+
? { causedByEventId: params.causedByEventId }
|
|
150
|
+
: {}),
|
|
151
|
+
};
|
|
152
|
+
}) as unknown as
|
|
153
|
+
| CreateNewEvent<Event>
|
|
154
|
+
| CreateNewEventFromDefinitions<Definitions>;
|
|
155
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { DefaultContext, GenericEvent } from "./types.ts";
|
|
2
|
+
|
|
3
|
+
export type EventDefinition<
|
|
4
|
+
Payload,
|
|
5
|
+
Context extends DefaultContext = undefined,
|
|
6
|
+
> = {
|
|
7
|
+
priority?: number;
|
|
8
|
+
_payload?: Payload;
|
|
9
|
+
_context?: Context;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type EventDefinitions = Record<
|
|
13
|
+
string,
|
|
14
|
+
EventDefinition<unknown, DefaultContext>
|
|
15
|
+
>;
|
|
16
|
+
|
|
17
|
+
type PayloadFromDefinition<Definition> = Definition extends EventDefinition<
|
|
18
|
+
infer Payload,
|
|
19
|
+
DefaultContext
|
|
20
|
+
>
|
|
21
|
+
? Payload
|
|
22
|
+
: never;
|
|
23
|
+
|
|
24
|
+
type ContextFromDefinition<Definition> = Definition extends EventDefinition<
|
|
25
|
+
unknown,
|
|
26
|
+
infer Context
|
|
27
|
+
>
|
|
28
|
+
? Context
|
|
29
|
+
: never;
|
|
30
|
+
|
|
31
|
+
export type InferEventsFromDefinitions<Definitions extends EventDefinitions> = {
|
|
32
|
+
[Topic in keyof Definitions & string]: GenericEvent<
|
|
33
|
+
Topic,
|
|
34
|
+
PayloadFromDefinition<Definitions[Topic]>,
|
|
35
|
+
ContextFromDefinition<Definitions[Topic]>
|
|
36
|
+
>;
|
|
37
|
+
}[keyof Definitions & string];
|
|
38
|
+
|
|
39
|
+
type DefineEventOptions = {
|
|
40
|
+
priority?: number;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const defineEvent = <
|
|
44
|
+
Payload,
|
|
45
|
+
Context extends DefaultContext = undefined,
|
|
46
|
+
>(
|
|
47
|
+
options: DefineEventOptions = {},
|
|
48
|
+
): EventDefinition<Payload, Context> =>
|
|
49
|
+
options as EventDefinition<Payload, Context>;
|
|
50
|
+
|
|
51
|
+
export const defineEvents = <Definitions extends EventDefinitions>(
|
|
52
|
+
definitions: Definitions,
|
|
53
|
+
): Definitions => definitions;
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export * from "./adapters/in-memory/index.ts";
|
|
2
2
|
export * from "./createEventCrawler.ts";
|
|
3
3
|
export * from "./createNewEvent.ts";
|
|
4
|
+
export * from "./eventDefinitions.ts";
|
|
4
5
|
export type * from "./ports/EventBus.ts";
|
|
5
6
|
export type * from "./ports/EventQueries.ts";
|
|
6
7
|
export type * from "./ports/EventRepository.ts";
|
|
7
8
|
export * from "./subscriptions.ts";
|
|
8
|
-
export
|
|
9
|
+
export * from "./types.ts";
|
package/src/types.ts
CHANGED
|
@@ -12,6 +12,74 @@ export type SubscriptionId = Flavor<string, "SubscriptionId">;
|
|
|
12
12
|
/** Unique identifier for a user who triggered an event. */
|
|
13
13
|
export type UserId = Flavor<string, "UserId">;
|
|
14
14
|
|
|
15
|
+
/** Actor representing a user action. */
|
|
16
|
+
export type UserActor<Id extends string = string> = {
|
|
17
|
+
kind: "user";
|
|
18
|
+
id: Id;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** Actor representing platform or infrastructure initiated work. */
|
|
22
|
+
export type SystemActor = {
|
|
23
|
+
kind: "system";
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** Actor representing a background worker or job. */
|
|
27
|
+
export type WorkerActor<Id extends string = string> = {
|
|
28
|
+
kind: "worker";
|
|
29
|
+
id?: Id;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** Actor representing an API key. */
|
|
33
|
+
export type ApiKeyActor<Id extends string = string> = {
|
|
34
|
+
kind: "api-key";
|
|
35
|
+
id: Id;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** Actor representing an anonymous caller. */
|
|
39
|
+
export type AnonymousActor = {
|
|
40
|
+
kind: "anonymous";
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
/** JSON-serializable actor metadata persisted alongside the event. */
|
|
44
|
+
export type Actor =
|
|
45
|
+
| UserActor
|
|
46
|
+
| SystemActor
|
|
47
|
+
| WorkerActor
|
|
48
|
+
| ApiKeyActor
|
|
49
|
+
| AnonymousActor;
|
|
50
|
+
|
|
51
|
+
/** Creates a typed user actor. */
|
|
52
|
+
export const createUserActor = <Id extends string>(id: Id): UserActor<Id> => ({
|
|
53
|
+
kind: "user",
|
|
54
|
+
id,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/** Creates a typed system actor. */
|
|
58
|
+
export const createSystemActor = (): SystemActor => ({
|
|
59
|
+
kind: "system",
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/** Creates a typed worker actor. */
|
|
63
|
+
export const createWorkerActor = <Id extends string>(
|
|
64
|
+
id?: Id,
|
|
65
|
+
): WorkerActor<Id> => ({
|
|
66
|
+
kind: "worker",
|
|
67
|
+
...(id !== undefined ? { id } : {}),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/** Creates a typed API key actor. */
|
|
71
|
+
export const createApiKeyActor = <Id extends string>(
|
|
72
|
+
id: Id,
|
|
73
|
+
): ApiKeyActor<Id> => ({
|
|
74
|
+
kind: "api-key",
|
|
75
|
+
id,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
/** Creates a typed anonymous actor. */
|
|
79
|
+
export const createAnonymousActor = (): AnonymousActor => ({
|
|
80
|
+
kind: "anonymous",
|
|
81
|
+
});
|
|
82
|
+
|
|
15
83
|
/** Unique identifier for an event. */
|
|
16
84
|
export type EventId = Flavor<string, "EventId">;
|
|
17
85
|
|
|
@@ -91,8 +159,12 @@ export type GenericEvent<
|
|
|
91
159
|
status: EventStatus;
|
|
92
160
|
/** History of publication attempts. */
|
|
93
161
|
publications: EventPublication[];
|
|
94
|
-
/**
|
|
95
|
-
|
|
162
|
+
/** Actor identifier that triggered the action that created this event. */
|
|
163
|
+
triggeredByActor: Actor;
|
|
164
|
+
/** Optional correlation identifier for a request or cross-event flow. */
|
|
165
|
+
flowId?: string;
|
|
166
|
+
/** Optional parent event identifier for cascading events. */
|
|
167
|
+
causedByEventId?: EventId;
|
|
96
168
|
/** Optional priority for processing order (not yet implemented in crawler). */
|
|
97
169
|
priority?: number;
|
|
98
170
|
/** Optional context for filtering events (e.g., by tenant). */
|