@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.
Files changed (88) hide show
  1. package/cjs/evento.cjs +157 -0
  2. package/cjs/evento.cjs.map +1 -0
  3. package/cjs/evento.d.ts +88 -0
  4. package/cjs/evento.d.ts.map +1 -0
  5. package/cjs/evento.test.cjs +522 -0
  6. package/cjs/evento.test.cjs.map +1 -0
  7. package/cjs/evento.test.d.ts +2 -0
  8. package/cjs/evento.test.d.ts.map +1 -0
  9. package/cjs/resolve-once.d.ts +1 -1
  10. package/cjs/resolve-once.d.ts.map +1 -1
  11. package/cjs/result.cjs +6 -2
  12. package/cjs/result.cjs.map +1 -1
  13. package/cjs/result.d.ts +2 -2
  14. package/cjs/result.d.ts.map +1 -1
  15. package/cjs/result.test.cjs +53 -0
  16. package/cjs/result.test.cjs.map +1 -1
  17. package/cjs/types.d.ts +2 -2
  18. package/cjs/types.d.ts.map +1 -1
  19. package/cjs/version.cjs +1 -1
  20. package/cjs/wait-for-value.test.cjs +25 -0
  21. package/cjs/wait-for-value.test.cjs.map +1 -1
  22. package/deno.json +1 -1
  23. package/esm/evento.d.ts +88 -0
  24. package/esm/evento.d.ts.map +1 -0
  25. package/esm/evento.js +155 -0
  26. package/esm/evento.js.map +1 -0
  27. package/esm/evento.test.d.ts +2 -0
  28. package/esm/evento.test.d.ts.map +1 -0
  29. package/esm/evento.test.js +520 -0
  30. package/esm/evento.test.js.map +1 -0
  31. package/esm/resolve-once.d.ts +1 -1
  32. package/esm/resolve-once.d.ts.map +1 -1
  33. package/esm/result.d.ts +2 -2
  34. package/esm/result.d.ts.map +1 -1
  35. package/esm/result.js +6 -2
  36. package/esm/result.js.map +1 -1
  37. package/esm/result.test.js +53 -0
  38. package/esm/result.test.js.map +1 -1
  39. package/esm/types.d.ts +2 -2
  40. package/esm/types.d.ts.map +1 -1
  41. package/esm/version.js +1 -1
  42. package/esm/wait-for-value.test.js +25 -0
  43. package/esm/wait-for-value.test.js.map +1 -1
  44. package/package.json +5 -5
  45. package/src/evento.ts +513 -0
  46. package/src/result.ts +9 -4
  47. package/ts/cjs/evento.d.ts +88 -0
  48. package/ts/cjs/evento.d.ts.map +1 -0
  49. package/ts/cjs/evento.js +157 -0
  50. package/ts/cjs/evento.js.map +1 -0
  51. package/ts/cjs/evento.test.d.ts +2 -0
  52. package/ts/cjs/evento.test.d.ts.map +1 -0
  53. package/ts/cjs/evento.test.js +522 -0
  54. package/ts/cjs/evento.test.js.map +1 -0
  55. package/ts/cjs/resolve-once.d.ts +1 -1
  56. package/ts/cjs/resolve-once.d.ts.map +1 -1
  57. package/ts/cjs/result.d.ts +2 -2
  58. package/ts/cjs/result.d.ts.map +1 -1
  59. package/ts/cjs/result.js +6 -2
  60. package/ts/cjs/result.js.map +1 -1
  61. package/ts/cjs/result.test.js +53 -0
  62. package/ts/cjs/result.test.js.map +1 -1
  63. package/ts/cjs/types.d.ts +2 -2
  64. package/ts/cjs/types.d.ts.map +1 -1
  65. package/ts/cjs/version.js +1 -1
  66. package/ts/cjs/wait-for-value.test.js +25 -0
  67. package/ts/cjs/wait-for-value.test.js.map +1 -1
  68. package/ts/esm/evento.d.ts +88 -0
  69. package/ts/esm/evento.d.ts.map +1 -0
  70. package/ts/esm/evento.js +155 -0
  71. package/ts/esm/evento.js.map +1 -0
  72. package/ts/esm/evento.test.d.ts +2 -0
  73. package/ts/esm/evento.test.d.ts.map +1 -0
  74. package/ts/esm/evento.test.js +520 -0
  75. package/ts/esm/evento.test.js.map +1 -0
  76. package/ts/esm/resolve-once.d.ts +1 -1
  77. package/ts/esm/resolve-once.d.ts.map +1 -1
  78. package/ts/esm/result.d.ts +2 -2
  79. package/ts/esm/result.d.ts.map +1 -1
  80. package/ts/esm/result.js +6 -2
  81. package/ts/esm/result.js.map +1 -1
  82. package/ts/esm/result.test.js +53 -0
  83. package/ts/esm/result.test.js.map +1 -1
  84. package/ts/esm/types.d.ts +2 -2
  85. package/ts/esm/types.d.ts.map +1 -1
  86. package/ts/esm/version.js +1 -1
  87. package/ts/esm/wait-for-value.test.js +25 -0
  88. 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> = T extends Promise<infer U> ? Promise<Result<U>> : Result<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<T> | T, T>(fn: FN): WithResult<ReturnType<FN>> {
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.then((value) => Result.Ok(value)).catch((e) => Result.Err(e)) as WithResult<ReturnType<FN>>;
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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"}