@adviser/cement 0.5.5 → 0.5.6
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/cjs/evento.cjs +157 -0
- package/cjs/evento.cjs.map +1 -0
- package/cjs/evento.d.ts +88 -0
- package/cjs/evento.d.ts.map +1 -0
- package/cjs/evento.test.cjs +522 -0
- package/cjs/evento.test.cjs.map +1 -0
- package/cjs/evento.test.d.ts +2 -0
- package/cjs/evento.test.d.ts.map +1 -0
- package/cjs/resolve-once.d.ts +1 -1
- package/cjs/resolve-once.d.ts.map +1 -1
- package/cjs/result.cjs +6 -2
- package/cjs/result.cjs.map +1 -1
- package/cjs/result.d.ts +2 -2
- package/cjs/result.d.ts.map +1 -1
- package/cjs/result.test.cjs +53 -0
- package/cjs/result.test.cjs.map +1 -1
- package/cjs/types.d.ts +2 -2
- package/cjs/types.d.ts.map +1 -1
- package/cjs/version.cjs +1 -1
- package/cjs/wait-for-value.test.cjs +25 -0
- package/cjs/wait-for-value.test.cjs.map +1 -1
- package/deno.json +1 -1
- package/esm/evento.d.ts +88 -0
- package/esm/evento.d.ts.map +1 -0
- package/esm/evento.js +155 -0
- package/esm/evento.js.map +1 -0
- package/esm/evento.test.d.ts +2 -0
- package/esm/evento.test.d.ts.map +1 -0
- package/esm/evento.test.js +520 -0
- package/esm/evento.test.js.map +1 -0
- package/esm/resolve-once.d.ts +1 -1
- package/esm/resolve-once.d.ts.map +1 -1
- package/esm/result.d.ts +2 -2
- package/esm/result.d.ts.map +1 -1
- package/esm/result.js +6 -2
- package/esm/result.js.map +1 -1
- package/esm/result.test.js +53 -0
- package/esm/result.test.js.map +1 -1
- package/esm/types.d.ts +2 -2
- package/esm/types.d.ts.map +1 -1
- package/esm/version.js +1 -1
- package/esm/wait-for-value.test.js +25 -0
- package/esm/wait-for-value.test.js.map +1 -1
- package/package.json +5 -5
- package/src/evento.ts +513 -0
- package/src/result.ts +9 -4
- package/ts/cjs/evento.d.ts +88 -0
- package/ts/cjs/evento.d.ts.map +1 -0
- package/ts/cjs/evento.js +157 -0
- package/ts/cjs/evento.js.map +1 -0
- package/ts/cjs/evento.test.d.ts +2 -0
- package/ts/cjs/evento.test.d.ts.map +1 -0
- package/ts/cjs/evento.test.js +522 -0
- package/ts/cjs/evento.test.js.map +1 -0
- package/ts/cjs/resolve-once.d.ts +1 -1
- package/ts/cjs/resolve-once.d.ts.map +1 -1
- package/ts/cjs/result.d.ts +2 -2
- package/ts/cjs/result.d.ts.map +1 -1
- package/ts/cjs/result.js +6 -2
- package/ts/cjs/result.js.map +1 -1
- package/ts/cjs/result.test.js +53 -0
- package/ts/cjs/result.test.js.map +1 -1
- package/ts/cjs/types.d.ts +2 -2
- package/ts/cjs/types.d.ts.map +1 -1
- package/ts/cjs/version.js +1 -1
- package/ts/cjs/wait-for-value.test.js +25 -0
- package/ts/cjs/wait-for-value.test.js.map +1 -1
- package/ts/esm/evento.d.ts +88 -0
- package/ts/esm/evento.d.ts.map +1 -0
- package/ts/esm/evento.js +155 -0
- package/ts/esm/evento.js.map +1 -0
- package/ts/esm/evento.test.d.ts +2 -0
- package/ts/esm/evento.test.d.ts.map +1 -0
- package/ts/esm/evento.test.js +520 -0
- package/ts/esm/evento.test.js.map +1 -0
- package/ts/esm/resolve-once.d.ts +1 -1
- package/ts/esm/resolve-once.d.ts.map +1 -1
- package/ts/esm/result.d.ts +2 -2
- package/ts/esm/result.d.ts.map +1 -1
- package/ts/esm/result.js +6 -2
- package/ts/esm/result.js.map +1 -1
- package/ts/esm/result.test.js +53 -0
- package/ts/esm/result.test.js.map +1 -1
- package/ts/esm/types.d.ts +2 -2
- package/ts/esm/types.d.ts.map +1 -1
- package/ts/esm/version.js +1 -1
- package/ts/esm/wait-for-value.test.js +25 -0
- package/ts/esm/wait-for-value.test.js.map +1 -1
package/src/evento.ts
ADDED
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
import { AppContext } from "./app-context.js";
|
|
2
|
+
import { Option } from "./option.js";
|
|
3
|
+
import { ResolveOnce } from "./resolve-once.js";
|
|
4
|
+
import { exception2Result, Result } from "./result.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Encoder/decoder interface for transforming request and response data.
|
|
8
|
+
* Used to serialize/deserialize data for event handling.
|
|
9
|
+
*
|
|
10
|
+
* @typeParam REQ - The request type to encode
|
|
11
|
+
* @typeParam RES - The response type to decode
|
|
12
|
+
*/
|
|
13
|
+
export interface EventoEnDecoder<REQ, RES> {
|
|
14
|
+
/**
|
|
15
|
+
* Encodes request arguments into a serializable format.
|
|
16
|
+
*
|
|
17
|
+
* @param args - The request data to encode
|
|
18
|
+
* @returns A Result containing the encoded data or an error
|
|
19
|
+
*/
|
|
20
|
+
encode(args: REQ): Promise<Result<unknown>>;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Decodes serialized data back into response format.
|
|
24
|
+
*
|
|
25
|
+
* @param data - The serialized data to decode
|
|
26
|
+
* @returns A Result containing the decoded response or an error
|
|
27
|
+
*/
|
|
28
|
+
decode(data: unknown): Promise<Result<RES>>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Interface for sending data during event handling.
|
|
33
|
+
* Provides lifecycle hooks (start, send, done) for managing event communication.
|
|
34
|
+
*
|
|
35
|
+
* @typeParam INREQ - The input request type
|
|
36
|
+
* @typeParam REQ - The validated request type
|
|
37
|
+
* @typeParam RES - The response type
|
|
38
|
+
*/
|
|
39
|
+
export interface EventoSend<INREQ, REQ, RES> {
|
|
40
|
+
/**
|
|
41
|
+
* Optional hook called once before the first handler processes the event.
|
|
42
|
+
*
|
|
43
|
+
* @param trigger - The trigger context
|
|
44
|
+
* @returns A Result indicating success or failure
|
|
45
|
+
*/
|
|
46
|
+
start?(trigger: HandleTriggerCtx<INREQ, REQ, RES>): Promise<Result<void>>;
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Sends data during event handling.
|
|
50
|
+
*
|
|
51
|
+
* @typeParam IS - Input send data type
|
|
52
|
+
* @typeParam OS - Output send data type
|
|
53
|
+
* @param trigger - The trigger context
|
|
54
|
+
* @param data - The data to send
|
|
55
|
+
* @returns A Result containing the response or an error
|
|
56
|
+
*/
|
|
57
|
+
send<IS, OS>(trigger: HandleTriggerCtx<INREQ, REQ, RES>, data: IS): Promise<Result<OS>>;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Optional hook called after all handlers have finished processing.
|
|
61
|
+
*
|
|
62
|
+
* @param trigger - The trigger context
|
|
63
|
+
* @returns A Result indicating success or failure
|
|
64
|
+
*/
|
|
65
|
+
done?(trigger: HandleTriggerCtx<INREQ, REQ, RES>): Promise<Result<void>>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// export interface ActiveTriggerCtx<INREQ, REQ, RES> {
|
|
69
|
+
// request?: INREQ;
|
|
70
|
+
// ctx: AppContext;
|
|
71
|
+
// enRequest?: unknown;
|
|
72
|
+
// validated?: REQ;
|
|
73
|
+
// send: EventoSend<INREQ, REQ, RES>;
|
|
74
|
+
// encoder: EventoEnDecoder<INREQ, RES>;
|
|
75
|
+
// }
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Base interface for trigger context containing core dependencies.
|
|
79
|
+
*
|
|
80
|
+
* @typeParam INREQ - The input request type
|
|
81
|
+
* @typeParam REQ - The validated request type
|
|
82
|
+
* @typeParam RES - The response type
|
|
83
|
+
*/
|
|
84
|
+
export interface TriggerCtxBase<INREQ, REQ, RES> {
|
|
85
|
+
send: EventoSend<INREQ, REQ, RES>;
|
|
86
|
+
ctx: AppContext;
|
|
87
|
+
encoder: EventoEnDecoder<INREQ, RES>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Readonly version of the base trigger context.
|
|
92
|
+
*/
|
|
93
|
+
export type ReadonlyTriggerCtxBase<INREQ, REQ, RES> = Readonly<TriggerCtxBase<INREQ, REQ, RES>>;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Parameters for creating a trigger context.
|
|
97
|
+
* Requires send, but ctx and encoder are optional and will use defaults.
|
|
98
|
+
*/
|
|
99
|
+
export type TriggerCtxBaseParams<INREQ, REQ, RES> = Pick<ReadonlyTriggerCtxBase<INREQ, REQ, RES>, "send"> &
|
|
100
|
+
Partial<Pick<ReadonlyTriggerCtxBase<INREQ, REQ, RES>, "ctx" | "encoder">>;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Complete parameters for triggering an event, including optional request data.
|
|
104
|
+
*/
|
|
105
|
+
export type TriggerCtxParams<INREQ, REQ, RES> = TriggerCtxBaseParams<INREQ, REQ, RES> & { request?: INREQ; enRequest?: unknown };
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Union type representing different trigger context states.
|
|
109
|
+
* Can have raw request, encoded request, or both.
|
|
110
|
+
*/
|
|
111
|
+
export type TriggerCtx<INREQ, REQ, RES> =
|
|
112
|
+
| (ReadonlyTriggerCtxBase<INREQ, REQ, RES> & { request: INREQ })
|
|
113
|
+
| (ReadonlyTriggerCtxBase<INREQ, REQ, RES> & { enRequest: unknown })
|
|
114
|
+
| (ReadonlyTriggerCtxBase<INREQ, REQ, RES> & { enRequest: unknown; request: INREQ });
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Context provided to validation handlers.
|
|
118
|
+
* Contains the encoded request data for validation.
|
|
119
|
+
*/
|
|
120
|
+
export interface ValidateTriggerCtx<INREQ, REQ, RES> extends ReadonlyTriggerCtxBase<INREQ, REQ, RES> {
|
|
121
|
+
readonly request?: INREQ;
|
|
122
|
+
readonly enRequest: unknown;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Mutable parts of the handle trigger context.
|
|
127
|
+
* Contains all forms of the request: raw, encoded, and validated.
|
|
128
|
+
*/
|
|
129
|
+
export interface MutableHandleTriggerCtx<INREQ, REQ> {
|
|
130
|
+
request: INREQ;
|
|
131
|
+
enRequest: unknown;
|
|
132
|
+
validated: REQ;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Context provided to event handlers.
|
|
137
|
+
* Combines validated request data with base context.
|
|
138
|
+
*/
|
|
139
|
+
export type HandleTriggerCtx<INREQ, REQ, RES> = Readonly<MutableHandleTriggerCtx<INREQ, REQ>> &
|
|
140
|
+
ReadonlyTriggerCtxBase<INREQ, REQ, RES>;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Result values that event handlers can return to control flow.
|
|
144
|
+
*/
|
|
145
|
+
export const EventoResult = {
|
|
146
|
+
/** Continue processing subsequent handlers */
|
|
147
|
+
Continue: "continue",
|
|
148
|
+
/** Stop processing and skip remaining handlers */
|
|
149
|
+
Stop: "stop",
|
|
150
|
+
} as const;
|
|
151
|
+
|
|
152
|
+
export type EventoResultType = (typeof EventoResult)[keyof typeof EventoResult];
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Operations for registering handlers in different positions.
|
|
156
|
+
*/
|
|
157
|
+
export enum EventoOp {
|
|
158
|
+
/** Add handler to the end of the list */
|
|
159
|
+
Push = "push",
|
|
160
|
+
/** Add handler to the beginning of the list */
|
|
161
|
+
Unshift = "unshift",
|
|
162
|
+
/** Add handler at a specific position */
|
|
163
|
+
Position = "position",
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Types of event handlers.
|
|
168
|
+
*/
|
|
169
|
+
export enum EventoType {
|
|
170
|
+
/** Wildcard handlers run only if no regular handlers match */
|
|
171
|
+
WildCard = "wildcard",
|
|
172
|
+
/** Regular handlers run first */
|
|
173
|
+
Regular = "regular",
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// export type EventoOrderType = typeof EventoOrder[keyof typeof EventoOrder];
|
|
177
|
+
/**
|
|
178
|
+
* Event handler interface.
|
|
179
|
+
* Handlers can optionally validate requests before handling them.
|
|
180
|
+
*
|
|
181
|
+
* @typeParam INREQ - The input request type (defaults to unknown)
|
|
182
|
+
* @typeParam REQ - The validated request type (defaults to unknown)
|
|
183
|
+
* @typeParam RES - The response type (defaults to unknown)
|
|
184
|
+
*/
|
|
185
|
+
export interface EventoHandler<INREQ = unknown, REQ = unknown, RES = unknown> {
|
|
186
|
+
/** Handler type (defaults to Regular if not specified) */
|
|
187
|
+
readonly type?: EventoType;
|
|
188
|
+
/** Unique identifier for this handler */
|
|
189
|
+
readonly hash: string;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Handles the validated event.
|
|
193
|
+
*
|
|
194
|
+
* @param trigger - The trigger context with validated data
|
|
195
|
+
* @returns A Result indicating whether to continue or stop processing
|
|
196
|
+
*/
|
|
197
|
+
handle(trigger: HandleTriggerCtx<INREQ, REQ, RES>): Promise<Result<EventoResultType>>;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Optional validation method.
|
|
201
|
+
* Return Some(validated data) to handle this event, or None to skip.
|
|
202
|
+
*
|
|
203
|
+
* @param trigger - The trigger context for validation
|
|
204
|
+
* @returns A Result containing Option of validated data
|
|
205
|
+
*/
|
|
206
|
+
validate?(trigger: ValidateTriggerCtx<INREQ, REQ, RES>): Promise<Result<Option<REQ>>>;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Simple handler operation (push or unshift).
|
|
211
|
+
*/
|
|
212
|
+
export interface EventoHandlerOpSimple {
|
|
213
|
+
readonly type: EventoType;
|
|
214
|
+
readonly op: EventoOp.Push | EventoOp.Unshift;
|
|
215
|
+
readonly handler: EventoHandler;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Position-based handler operation for inserting at a specific index.
|
|
220
|
+
*/
|
|
221
|
+
export interface EventoHandlerOpPosition {
|
|
222
|
+
readonly type: EventoType;
|
|
223
|
+
readonly op: EventoOp.Position;
|
|
224
|
+
readonly handler: EventoHandler;
|
|
225
|
+
readonly idx: number;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Union type for all handler operations.
|
|
230
|
+
*/
|
|
231
|
+
export type EventoHandlerOp = EventoHandlerOpSimple | EventoHandlerOpPosition;
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Creates an unregister function for removing a handler from the handler list.
|
|
235
|
+
*
|
|
236
|
+
* @param item - The handler to create an unregister function for
|
|
237
|
+
* @param actions - The handler list containing the handler
|
|
238
|
+
* @returns A function that removes the handler when called
|
|
239
|
+
*/
|
|
240
|
+
function unregFunc(item: EventoHandler, actions: EventoHandler[]): () => void {
|
|
241
|
+
return (): void => {
|
|
242
|
+
const index = actions.findIndex((x) => x.hash === item.hash);
|
|
243
|
+
if (index >= 0) {
|
|
244
|
+
actions.splice(index, 1);
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Event handling system with validation and encoding support.
|
|
251
|
+
* Manages regular and wildcard handlers with customizable execution order.
|
|
252
|
+
*
|
|
253
|
+
* Regular handlers are processed first. If any regular handler matches and processes
|
|
254
|
+
* the event, wildcard handlers are skipped. Wildcard handlers only run if no regular
|
|
255
|
+
* handlers matched.
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```typescript
|
|
259
|
+
* // Define your types
|
|
260
|
+
* interface MyRequest { action: string; value: number; }
|
|
261
|
+
* interface MyResponse { success: boolean; data?: unknown; }
|
|
262
|
+
*
|
|
263
|
+
* // Create an encoder/decoder
|
|
264
|
+
* class MyEncoder implements EventoEnDecoder<MyRequest, MyResponse> {
|
|
265
|
+
* async encode(args: MyRequest): Promise<Result<unknown>> {
|
|
266
|
+
* return Result.Ok(args);
|
|
267
|
+
* }
|
|
268
|
+
* async decode(data: unknown): Promise<Result<MyResponse>> {
|
|
269
|
+
* return Result.Ok(data as MyResponse);
|
|
270
|
+
* }
|
|
271
|
+
* }
|
|
272
|
+
*
|
|
273
|
+
* // Create a send implementation
|
|
274
|
+
* class MySend implements EventoSend<MyRequest, MyRequest, MyResponse> {
|
|
275
|
+
* async send<IS, OS>(trigger: HandleTriggerCtx<MyRequest, MyRequest, MyResponse>, data: IS): Promise<Result<OS>> {
|
|
276
|
+
* // Send the response data
|
|
277
|
+
* console.log('Sending:', data);
|
|
278
|
+
* return Result.Ok(data as OS);
|
|
279
|
+
* }
|
|
280
|
+
* }
|
|
281
|
+
*
|
|
282
|
+
* // Initialize Evento
|
|
283
|
+
* const evento = new Evento(new MyEncoder());
|
|
284
|
+
* const send = new MySend();
|
|
285
|
+
*
|
|
286
|
+
* // Register regular handlers (run first)
|
|
287
|
+
* evento.push({
|
|
288
|
+
* hash: 'update-handler',
|
|
289
|
+
* validate: async (ctx) => {
|
|
290
|
+
* const req = ctx.enRequest as MyRequest;
|
|
291
|
+
* // Only handle "update" actions
|
|
292
|
+
* if (req.action === 'update') {
|
|
293
|
+
* return Result.Ok(Option.Some(req));
|
|
294
|
+
* }
|
|
295
|
+
* return Result.Ok(Option.None());
|
|
296
|
+
* },
|
|
297
|
+
* handle: async (ctx) => {
|
|
298
|
+
* await ctx.send.send(ctx, {
|
|
299
|
+
* success: true,
|
|
300
|
+
* data: { updated: ctx.validated.value }
|
|
301
|
+
* });
|
|
302
|
+
* return Result.Ok(EventoResult.Continue);
|
|
303
|
+
* }
|
|
304
|
+
* });
|
|
305
|
+
*
|
|
306
|
+
* // Register wildcard handler (runs if no regular handlers match)
|
|
307
|
+
* evento.push({
|
|
308
|
+
* hash: 'default-handler',
|
|
309
|
+
* type: EventoType.WildCard,
|
|
310
|
+
* handle: async (ctx) => {
|
|
311
|
+
* await ctx.send.send(ctx, { success: false });
|
|
312
|
+
* return Result.Ok(EventoResult.Stop);
|
|
313
|
+
* }
|
|
314
|
+
* });
|
|
315
|
+
*
|
|
316
|
+
* // Trigger an event
|
|
317
|
+
* await evento.trigger({
|
|
318
|
+
* send,
|
|
319
|
+
* request: { action: 'update', value: 42 }
|
|
320
|
+
* });
|
|
321
|
+
* ```
|
|
322
|
+
*/
|
|
323
|
+
export class Evento {
|
|
324
|
+
private actions: EventoHandler[] = [];
|
|
325
|
+
private wildcards: EventoHandler[] = [];
|
|
326
|
+
|
|
327
|
+
private encoder: EventoEnDecoder<unknown, unknown>;
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Creates a new Evento instance.
|
|
331
|
+
*
|
|
332
|
+
* @param encoder - The default encoder/decoder for requests and responses
|
|
333
|
+
*/
|
|
334
|
+
constructor(encoder: EventoEnDecoder<unknown, unknown>) {
|
|
335
|
+
this.encoder = encoder;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Returns copies of the current handler lists.
|
|
340
|
+
*
|
|
341
|
+
* @returns An object containing arrays of regular actions and wildcard handlers
|
|
342
|
+
*/
|
|
343
|
+
handlers(): {
|
|
344
|
+
actions: EventoHandler[];
|
|
345
|
+
wildcards: EventoHandler[];
|
|
346
|
+
} {
|
|
347
|
+
return {
|
|
348
|
+
actions: [...this.actions],
|
|
349
|
+
wildcards: [...this.wildcards],
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Registers handlers at the end of their respective lists.
|
|
355
|
+
*
|
|
356
|
+
* @param hdls - One or more handlers or arrays of handlers
|
|
357
|
+
* @returns Array of unregister functions, one for each handler
|
|
358
|
+
*/
|
|
359
|
+
push(...hdls: (EventoHandler | EventoHandler[])[]): (() => void)[] {
|
|
360
|
+
return this.register(
|
|
361
|
+
...hdls.flat().map((handler) => {
|
|
362
|
+
return {
|
|
363
|
+
handler,
|
|
364
|
+
type: handler.type ?? EventoType.Regular,
|
|
365
|
+
op: EventoOp.Push as const,
|
|
366
|
+
};
|
|
367
|
+
}),
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Registers handlers at the beginning of their respective lists.
|
|
373
|
+
*
|
|
374
|
+
* @param hdls - One or more handlers or arrays of handlers
|
|
375
|
+
* @returns Array of unregister functions, one for each handler
|
|
376
|
+
*/
|
|
377
|
+
unshift(...hdls: (EventoHandler | EventoHandler[])[]): (() => void)[] {
|
|
378
|
+
return this.register(
|
|
379
|
+
...hdls.flat().map((handler) => {
|
|
380
|
+
return {
|
|
381
|
+
handler: handler,
|
|
382
|
+
type: handler.type ?? EventoType.Regular,
|
|
383
|
+
op: EventoOp.Unshift as const,
|
|
384
|
+
};
|
|
385
|
+
}),
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Registers handlers with specific operations.
|
|
391
|
+
* If a handler with the same hash already exists, returns its unregister function
|
|
392
|
+
* without adding a duplicate.
|
|
393
|
+
*
|
|
394
|
+
* @param hdls - Handler operations specifying where to place each handler
|
|
395
|
+
* @returns Array of unregister functions, one for each handler
|
|
396
|
+
* @throws Error if an unknown operation is specified
|
|
397
|
+
*/
|
|
398
|
+
register(...hdls: EventoHandlerOp[]): (() => void)[] {
|
|
399
|
+
return hdls.map((item) => {
|
|
400
|
+
const handlers = item.type === EventoType.WildCard ? this.wildcards : this.actions;
|
|
401
|
+
const hasHandler = handlers.find((h) => h.hash === item.handler.hash);
|
|
402
|
+
if (hasHandler) {
|
|
403
|
+
return unregFunc(hasHandler, handlers);
|
|
404
|
+
}
|
|
405
|
+
switch (item.op) {
|
|
406
|
+
case EventoOp.Push:
|
|
407
|
+
handlers.push(item.handler);
|
|
408
|
+
return unregFunc(item.handler, handlers);
|
|
409
|
+
case EventoOp.Unshift:
|
|
410
|
+
handlers.unshift(item.handler);
|
|
411
|
+
return unregFunc(item.handler, handlers);
|
|
412
|
+
case EventoOp.Position:
|
|
413
|
+
handlers.splice(item.idx, 0, item.handler);
|
|
414
|
+
return unregFunc(item.handler, handlers);
|
|
415
|
+
default:
|
|
416
|
+
throw new Error(`Unknown position`);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Triggers event processing through registered handlers.
|
|
423
|
+
*
|
|
424
|
+
* Process flow:
|
|
425
|
+
* 1. Encodes the request if not already encoded
|
|
426
|
+
* 2. Validates against each handler (regular handlers first)
|
|
427
|
+
* 3. For matching handlers, calls handle() method
|
|
428
|
+
* 4. If any regular handler matches, wildcard handlers are skipped
|
|
429
|
+
* 5. Calls optional start/done lifecycle hooks on the send interface
|
|
430
|
+
*
|
|
431
|
+
* @typeParam INREQ - The input request type
|
|
432
|
+
* @typeParam REQ - The validated request type
|
|
433
|
+
* @typeParam RES - The response type
|
|
434
|
+
* @param ictx - The trigger context parameters
|
|
435
|
+
* @returns A Result containing an array of handler hashes that processed the event
|
|
436
|
+
*/
|
|
437
|
+
async trigger<INREQ, REQ, RES>(ictx: TriggerCtxParams<INREQ, REQ, RES>): Promise<Result<string[]>> {
|
|
438
|
+
return exception2Result(async (): Promise<Result<string[]>> => {
|
|
439
|
+
const ctx: ReadonlyTriggerCtxBase<INREQ, REQ, RES> & Partial<MutableHandleTriggerCtx<INREQ, REQ>> = {
|
|
440
|
+
...ictx,
|
|
441
|
+
encoder: ictx.encoder ?? (this.encoder as EventoEnDecoder<INREQ, RES>),
|
|
442
|
+
ctx: ictx.ctx ?? new AppContext(),
|
|
443
|
+
};
|
|
444
|
+
const results: string[] = [];
|
|
445
|
+
// this skips encoding if already encoded
|
|
446
|
+
if (!ctx.enRequest) {
|
|
447
|
+
const rUnk = await this.encoder.encode(ctx.request as never);
|
|
448
|
+
if (rUnk.isErr()) {
|
|
449
|
+
return Result.Err(rUnk);
|
|
450
|
+
}
|
|
451
|
+
const unk = rUnk.unwrap();
|
|
452
|
+
ctx.enRequest = unk;
|
|
453
|
+
}
|
|
454
|
+
const validateCtx = {
|
|
455
|
+
...ctx,
|
|
456
|
+
enRequest: ctx.enRequest,
|
|
457
|
+
request: ctx.request,
|
|
458
|
+
};
|
|
459
|
+
const startOnce = new ResolveOnce<Result<HandleTriggerCtx<INREQ, REQ, RES>>>();
|
|
460
|
+
for (const hdl of [...this.actions, "breakpoint", ...this.wildcards]) {
|
|
461
|
+
if (typeof hdl === "string") {
|
|
462
|
+
if (results.length > 0) {
|
|
463
|
+
// we handled actions so we do not process wildcards
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
const rData = await Promise.resolve(hdl.validate ? hdl.validate(validateCtx) : Result.Ok(Option.Some(ctx.enRequest)));
|
|
469
|
+
if (rData.isErr()) {
|
|
470
|
+
return Result.Err(rData);
|
|
471
|
+
}
|
|
472
|
+
const data = rData.Ok();
|
|
473
|
+
if (data.IsNone()) {
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
const hdlCtx = {
|
|
477
|
+
...ctx,
|
|
478
|
+
validated: data.Unwrap() as REQ,
|
|
479
|
+
request: ctx.request as INREQ,
|
|
480
|
+
enRequest: ctx.enRequest,
|
|
481
|
+
}; // satisfies HandleTriggerCtx<INREQ, REQ, RES>;
|
|
482
|
+
if (ctx.send.start) {
|
|
483
|
+
const rStart = await startOnce.once(() =>
|
|
484
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
485
|
+
ctx.send.start!(hdlCtx).then((rv): Result<HandleTriggerCtx<INREQ, REQ, RES>> => {
|
|
486
|
+
if (rv.isErr()) {
|
|
487
|
+
return Result.Err(rv);
|
|
488
|
+
}
|
|
489
|
+
return Result.Ok(hdlCtx);
|
|
490
|
+
}),
|
|
491
|
+
);
|
|
492
|
+
if (rStart.isErr()) {
|
|
493
|
+
return Result.Err(rStart);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
const rHandle = await hdl.handle(hdlCtx);
|
|
497
|
+
if (rHandle.isErr()) {
|
|
498
|
+
return Result.Err(rHandle);
|
|
499
|
+
}
|
|
500
|
+
results.push(hdl.hash);
|
|
501
|
+
if (rHandle.Ok() === EventoResult.Stop) {
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (ctx.send.done && startOnce.state === "processed" && startOnce.value) {
|
|
506
|
+
if (startOnce.value.isOk()) {
|
|
507
|
+
await ctx.send.done(startOnce.value.Ok());
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return Result.Ok(results);
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
}
|
package/src/result.ts
CHANGED
|
@@ -127,7 +127,8 @@ export class ResultError<T extends Error> extends Result<never, T> {
|
|
|
127
127
|
export type WithoutResult<T> = T extends Result<infer U> ? U : T;
|
|
128
128
|
|
|
129
129
|
// type WithoutPromise<T> = T extends Promise<infer U> ? U : T;
|
|
130
|
-
type WithResult<T> =
|
|
130
|
+
type WithResult<T> =
|
|
131
|
+
T extends Promise<infer U> ? Promise<U extends Result<unknown> ? U : Result<U>> : T extends Result<unknown> ? T : Result<T>;
|
|
131
132
|
|
|
132
133
|
/**
|
|
133
134
|
* Wraps a function to convert thrown exceptions into Result.Err values.
|
|
@@ -152,13 +153,17 @@ type WithResult<T> = T extends Promise<infer U> ? Promise<Result<U>> : Result<T>
|
|
|
152
153
|
* });
|
|
153
154
|
* ```
|
|
154
155
|
*/
|
|
155
|
-
export function exception2Result<FN extends () => Promise<
|
|
156
|
+
export function exception2Result<FN extends () => Promise<TT> | TT, TT>(fn: FN): WithResult<ReturnType<FN>> {
|
|
156
157
|
try {
|
|
157
158
|
const res = fn();
|
|
158
159
|
if (isPromise(res)) {
|
|
159
|
-
return res
|
|
160
|
+
return res
|
|
161
|
+
.then((value) => {
|
|
162
|
+
return (Result.Is(value) ? value : Result.Ok(value)) as WithResult<ReturnType<FN>>;
|
|
163
|
+
})
|
|
164
|
+
.catch((e) => Result.Err(e)) as WithResult<ReturnType<FN>>;
|
|
160
165
|
}
|
|
161
|
-
return Result.Ok(res) as WithResult<ReturnType<FN>>;
|
|
166
|
+
return (Result.Is(res) ? res : Result.Ok(res)) as WithResult<ReturnType<FN>>;
|
|
162
167
|
} catch (e) {
|
|
163
168
|
return Result.Err(e as Error) as WithResult<ReturnType<FN>>;
|
|
164
169
|
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { AppContext } from "./app-context.js";
|
|
2
|
+
import { Option } from "./option.js";
|
|
3
|
+
import { Result } from "./result.js";
|
|
4
|
+
export interface EventoEnDecoder<REQ, RES> {
|
|
5
|
+
encode(args: REQ): Promise<Result<unknown>>;
|
|
6
|
+
decode(data: unknown): Promise<Result<RES>>;
|
|
7
|
+
}
|
|
8
|
+
export interface EventoSend<INREQ, REQ, RES> {
|
|
9
|
+
start?(trigger: HandleTriggerCtx<INREQ, REQ, RES>): Promise<Result<void>>;
|
|
10
|
+
send<IS, OS>(trigger: HandleTriggerCtx<INREQ, REQ, RES>, data: IS): Promise<Result<OS>>;
|
|
11
|
+
done?(trigger: HandleTriggerCtx<INREQ, REQ, RES>): Promise<Result<void>>;
|
|
12
|
+
}
|
|
13
|
+
export interface TriggerCtxBase<INREQ, REQ, RES> {
|
|
14
|
+
send: EventoSend<INREQ, REQ, RES>;
|
|
15
|
+
ctx: AppContext;
|
|
16
|
+
encoder: EventoEnDecoder<INREQ, RES>;
|
|
17
|
+
}
|
|
18
|
+
export type ReadonlyTriggerCtxBase<INREQ, REQ, RES> = Readonly<TriggerCtxBase<INREQ, REQ, RES>>;
|
|
19
|
+
export type TriggerCtxBaseParams<INREQ, REQ, RES> = Pick<ReadonlyTriggerCtxBase<INREQ, REQ, RES>, "send"> & Partial<Pick<ReadonlyTriggerCtxBase<INREQ, REQ, RES>, "ctx" | "encoder">>;
|
|
20
|
+
export type TriggerCtxParams<INREQ, REQ, RES> = TriggerCtxBaseParams<INREQ, REQ, RES> & {
|
|
21
|
+
request?: INREQ;
|
|
22
|
+
enRequest?: unknown;
|
|
23
|
+
};
|
|
24
|
+
export type TriggerCtx<INREQ, REQ, RES> = (ReadonlyTriggerCtxBase<INREQ, REQ, RES> & {
|
|
25
|
+
request: INREQ;
|
|
26
|
+
}) | (ReadonlyTriggerCtxBase<INREQ, REQ, RES> & {
|
|
27
|
+
enRequest: unknown;
|
|
28
|
+
}) | (ReadonlyTriggerCtxBase<INREQ, REQ, RES> & {
|
|
29
|
+
enRequest: unknown;
|
|
30
|
+
request: INREQ;
|
|
31
|
+
});
|
|
32
|
+
export interface ValidateTriggerCtx<INREQ, REQ, RES> extends ReadonlyTriggerCtxBase<INREQ, REQ, RES> {
|
|
33
|
+
readonly request?: INREQ;
|
|
34
|
+
readonly enRequest: unknown;
|
|
35
|
+
}
|
|
36
|
+
export interface MutableHandleTriggerCtx<INREQ, REQ> {
|
|
37
|
+
request: INREQ;
|
|
38
|
+
enRequest: unknown;
|
|
39
|
+
validated: REQ;
|
|
40
|
+
}
|
|
41
|
+
export type HandleTriggerCtx<INREQ, REQ, RES> = Readonly<MutableHandleTriggerCtx<INREQ, REQ>> & ReadonlyTriggerCtxBase<INREQ, REQ, RES>;
|
|
42
|
+
export declare const EventoResult: {
|
|
43
|
+
readonly Continue: "continue";
|
|
44
|
+
readonly Stop: "stop";
|
|
45
|
+
};
|
|
46
|
+
export type EventoResultType = (typeof EventoResult)[keyof typeof EventoResult];
|
|
47
|
+
export declare enum EventoOp {
|
|
48
|
+
Push = "push",
|
|
49
|
+
Unshift = "unshift",
|
|
50
|
+
Position = "position"
|
|
51
|
+
}
|
|
52
|
+
export declare enum EventoType {
|
|
53
|
+
WildCard = "wildcard",
|
|
54
|
+
Regular = "regular"
|
|
55
|
+
}
|
|
56
|
+
export interface EventoHandler<INREQ = unknown, REQ = unknown, RES = unknown> {
|
|
57
|
+
readonly type?: EventoType;
|
|
58
|
+
readonly hash: string;
|
|
59
|
+
handle(trigger: HandleTriggerCtx<INREQ, REQ, RES>): Promise<Result<EventoResultType>>;
|
|
60
|
+
validate?(trigger: ValidateTriggerCtx<INREQ, REQ, RES>): Promise<Result<Option<REQ>>>;
|
|
61
|
+
}
|
|
62
|
+
export interface EventoHandlerOpSimple {
|
|
63
|
+
readonly type: EventoType;
|
|
64
|
+
readonly op: EventoOp.Push | EventoOp.Unshift;
|
|
65
|
+
readonly handler: EventoHandler;
|
|
66
|
+
}
|
|
67
|
+
export interface EventoHandlerOpPosition {
|
|
68
|
+
readonly type: EventoType;
|
|
69
|
+
readonly op: EventoOp.Position;
|
|
70
|
+
readonly handler: EventoHandler;
|
|
71
|
+
readonly idx: number;
|
|
72
|
+
}
|
|
73
|
+
export type EventoHandlerOp = EventoHandlerOpSimple | EventoHandlerOpPosition;
|
|
74
|
+
export declare class Evento {
|
|
75
|
+
private actions;
|
|
76
|
+
private wildcards;
|
|
77
|
+
private encoder;
|
|
78
|
+
constructor(encoder: EventoEnDecoder<unknown, unknown>);
|
|
79
|
+
handlers(): {
|
|
80
|
+
actions: EventoHandler[];
|
|
81
|
+
wildcards: EventoHandler[];
|
|
82
|
+
};
|
|
83
|
+
push(...hdls: (EventoHandler | EventoHandler[])[]): (() => void)[];
|
|
84
|
+
unshift(...hdls: (EventoHandler | EventoHandler[])[]): (() => void)[];
|
|
85
|
+
register(...hdls: EventoHandlerOp[]): (() => void)[];
|
|
86
|
+
trigger<INREQ, REQ, RES>(ictx: TriggerCtxParams<INREQ, REQ, RES>): Promise<Result<string[]>>;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=evento.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"evento.d.ts","sourceRoot":"","sources":["../../../src/evento.ts"],"names":[],"mappings":"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"}
|