abxbus 2.4.32 → 2.5.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.
Files changed (56) hide show
  1. package/README.md +74 -51
  2. package/dist/cjs/BaseEvent.d.ts +45 -55
  3. package/dist/cjs/BaseEvent.js +350 -169
  4. package/dist/cjs/BaseEvent.js.map +3 -3
  5. package/dist/cjs/EventBus.d.ts +8 -1
  6. package/dist/cjs/EventBus.js +153 -85
  7. package/dist/cjs/EventBus.js.map +2 -2
  8. package/dist/cjs/EventHandler.d.ts +3 -3
  9. package/dist/cjs/EventHandler.js.map +1 -1
  10. package/dist/cjs/EventResult.js +16 -22
  11. package/dist/cjs/EventResult.js.map +2 -2
  12. package/dist/cjs/LockManager.d.ts +1 -0
  13. package/dist/cjs/LockManager.js +4 -1
  14. package/dist/cjs/LockManager.js.map +2 -2
  15. package/dist/cjs/base_event.d.ts +2 -2
  16. package/dist/cjs/bridge_ipc.d.ts +45 -0
  17. package/dist/cjs/event_handler.d.ts +1 -0
  18. package/dist/cjs/events_suck.js +1 -1
  19. package/dist/cjs/events_suck.js.map +2 -2
  20. package/dist/cjs/index.d.ts +1 -0
  21. package/dist/cjs/index.js.map +2 -2
  22. package/dist/cjs/middleware_otel_tracing.d.ts +49 -0
  23. package/dist/cjs/timing.js +1 -1
  24. package/dist/cjs/timing.js.map +2 -2
  25. package/dist/esm/BaseEvent.js +351 -170
  26. package/dist/esm/BaseEvent.js.map +3 -3
  27. package/dist/esm/EventBus.js +153 -85
  28. package/dist/esm/EventBus.js.map +2 -2
  29. package/dist/esm/EventHandler.js.map +1 -1
  30. package/dist/esm/EventResult.js +16 -22
  31. package/dist/esm/EventResult.js.map +2 -2
  32. package/dist/esm/LockManager.js +4 -1
  33. package/dist/esm/LockManager.js.map +2 -2
  34. package/dist/esm/events_suck.js +1 -1
  35. package/dist/esm/events_suck.js.map +2 -2
  36. package/dist/esm/index.js.map +2 -2
  37. package/dist/esm/timing.js +1 -1
  38. package/dist/esm/timing.js.map +2 -2
  39. package/dist/types/BaseEvent.d.ts +45 -55
  40. package/dist/types/EventBus.d.ts +8 -1
  41. package/dist/types/EventHandler.d.ts +3 -3
  42. package/dist/types/LockManager.d.ts +1 -0
  43. package/dist/types/base_event.d.ts +2 -2
  44. package/dist/types/bridge_ipc.d.ts +45 -0
  45. package/dist/types/event_handler.d.ts +1 -0
  46. package/dist/types/index.d.ts +1 -0
  47. package/dist/types/middleware_otel_tracing.d.ts +49 -0
  48. package/package.json +4 -3
  49. package/src/BaseEvent.ts +452 -219
  50. package/src/EventBus.ts +186 -99
  51. package/src/EventHandler.ts +3 -3
  52. package/src/EventResult.ts +18 -22
  53. package/src/LockManager.ts +5 -1
  54. package/src/events_suck.ts +1 -1
  55. package/src/index.ts +1 -0
  56. package/src/timing.ts +1 -1
@@ -10,9 +10,20 @@ import {
10
10
  withResolvers
11
11
  } from "./LockManager.js";
12
12
  import { _runWithTimeout } from "./timing.js";
13
- import { extractZodShape, normalizeEventResultType, toJsonSchema } from "./types.js";
13
+ import { isZodSchema, normalizeEventResultType, toJsonSchema } from "./types.js";
14
14
  import { monotonicDatetime } from "./helpers.js";
15
- const RESERVED_USER_EVENT_FIELDS = /* @__PURE__ */ new Set(["bus", "emit", "first", "toString", "toJSON", "fromJSON"]);
15
+ const RESERVED_USER_EVENT_FIELDS = /* @__PURE__ */ new Set([
16
+ "bus",
17
+ "emit",
18
+ "wait",
19
+ "now",
20
+ "eventResult",
21
+ "eventResultsList",
22
+ "toString",
23
+ "toJSON",
24
+ "fromJSON"
25
+ ]);
26
+ const EVENT_TYPE_REGISTRY = /* @__PURE__ */ new Map();
16
27
  function assertNoReservedUserEventFields(data, context) {
17
28
  for (const field_name of RESERVED_USER_EVENT_FIELDS) {
18
29
  if (Object.prototype.hasOwnProperty.call(data, field_name)) {
@@ -34,6 +45,12 @@ function assertNoModelPrefixedFields(data, context) {
34
45
  }
35
46
  }
36
47
  }
48
+ function isRecord(value) {
49
+ return !!value && typeof value === "object" && !Array.isArray(value);
50
+ }
51
+ function isZodObjectSchema(value) {
52
+ return isZodSchema(value) && typeof value.safeExtend === "function" && isRecord(value.shape);
53
+ }
37
54
  function compareIsoDatetime(left, right) {
38
55
  const left_value = left ?? "";
39
56
  const right_value = right ?? "";
@@ -47,10 +64,10 @@ const BaseEventSchema = z.object({
47
64
  event_created_at: z.string().datetime(),
48
65
  event_type: z.string(),
49
66
  event_version: z.string().default("0.0.1"),
50
- event_timeout: z.number().positive().nullable(),
51
- event_slow_timeout: z.number().positive().nullable().optional(),
52
- event_handler_timeout: z.number().positive().nullable().optional(),
53
- event_handler_slow_timeout: z.number().positive().nullable().optional(),
67
+ event_timeout: z.number().nonnegative().nullable(),
68
+ event_slow_timeout: z.number().nonnegative().nullable().optional(),
69
+ event_handler_timeout: z.number().nonnegative().nullable().optional(),
70
+ event_handler_slow_timeout: z.number().nonnegative().nullable().optional(),
54
71
  event_blocks_parent_completion: z.boolean().optional(),
55
72
  event_parent_id: z.string().uuid().nullable().optional(),
56
73
  event_path: z.array(z.string()).optional(),
@@ -66,8 +83,110 @@ const BaseEventSchema = z.object({
66
83
  event_handler_completion: z.enum(EVENT_HANDLER_COMPLETION_MODES).nullable().optional()
67
84
  }).loose();
68
85
  const KNOWN_BASE_EVENT_FIELDS = new Set(Object.keys(BaseEventSchema.shape));
69
- const EVENT_CLASS_DEFAULTS = /* @__PURE__ */ new WeakMap();
70
86
  const ROOT_EVENTBUS_ID = "00000000-0000-0000-0000-000000000000";
87
+ function baseEventDefaultShape(event_type) {
88
+ return {
89
+ event_id: z.string().uuid(),
90
+ event_created_at: z.string().datetime(),
91
+ event_type: z.string().default(event_type),
92
+ event_version: z.string().default("0.0.1"),
93
+ event_timeout: z.number().nonnegative().nullable().default(null),
94
+ event_slow_timeout: z.number().nonnegative().nullable().optional(),
95
+ event_handler_timeout: z.number().nonnegative().nullable().optional(),
96
+ event_handler_slow_timeout: z.number().nonnegative().nullable().optional(),
97
+ event_blocks_parent_completion: z.boolean().default(false),
98
+ event_parent_id: z.string().uuid().nullable().optional(),
99
+ event_path: z.array(z.string()).optional(),
100
+ event_result_type: z.unknown().optional(),
101
+ event_emitted_by_handler_id: z.string().uuid().nullable().optional(),
102
+ event_pending_bus_count: z.number().nonnegative().optional(),
103
+ event_status: z.enum(["pending", "started", "completed"]).optional(),
104
+ event_started_at: z.string().datetime().nullable().optional(),
105
+ event_completed_at: z.string().datetime().nullable().optional(),
106
+ event_results: z.record(z.string(), z.unknown()).optional(),
107
+ event_concurrency: z.enum(EVENT_CONCURRENCY_MODES).nullable().optional(),
108
+ event_handler_concurrency: z.enum(EVENT_HANDLER_CONCURRENCY_MODES).nullable().optional(),
109
+ event_handler_completion: z.enum(EVENT_HANDLER_COMPLETION_MODES).nullable().optional()
110
+ };
111
+ }
112
+ function missingBaseFields(event_type, user_shape) {
113
+ return Object.fromEntries(Object.entries(baseEventDefaultShape(event_type)).filter(([key]) => !(key in user_shape)));
114
+ }
115
+ function shortcutDefaultSchema(base_field_schema, value) {
116
+ if (!base_field_schema) {
117
+ return z.unknown().optional().default(value);
118
+ }
119
+ return base_field_schema.prefault(base_field_schema.parse(value));
120
+ }
121
+ function schemaDefaultsForShortcut(event_type, raw_shape) {
122
+ const defaults = {};
123
+ const base_shape = baseEventDefaultShape(event_type);
124
+ for (const [key, value] of Object.entries(raw_shape)) {
125
+ if (key === "event_result_type") continue;
126
+ if (!isZodSchema(value)) {
127
+ defaults[key] = shortcutDefaultSchema(base_shape[key], value);
128
+ }
129
+ }
130
+ return defaults;
131
+ }
132
+ function zodFieldsForShortcut(raw_shape) {
133
+ const fields = {};
134
+ for (const [key, value] of Object.entries(raw_shape)) {
135
+ if (key === "event_result_type") continue;
136
+ if (isZodSchema(value)) {
137
+ fields[key] = value;
138
+ }
139
+ }
140
+ return fields;
141
+ }
142
+ function eventResultTypeFromObjectSchema(schema) {
143
+ const raw_event_result_type = schema.shape.event_result_type;
144
+ return raw_event_result_type === void 0 ? void 0 : normalizeEventResultType(raw_event_result_type);
145
+ }
146
+ function buildFullEventSchema(event_type, spec) {
147
+ if (isZodObjectSchema(spec)) {
148
+ const user_shape = spec.shape;
149
+ assertNoReservedUserEventFields(user_shape, `BaseEvent.extend(${event_type})`);
150
+ assertNoUnknownEventPrefixedFields(user_shape, `BaseEvent.extend(${event_type})`);
151
+ assertNoModelPrefixedFields(user_shape, `BaseEvent.extend(${event_type})`);
152
+ const full_schema2 = spec.safeExtend({
153
+ event_result_type: z.unknown().optional(),
154
+ ...missingBaseFields(event_type, user_shape)
155
+ });
156
+ return {
157
+ event_schema: full_schema2,
158
+ event_result_type: eventResultTypeFromObjectSchema(spec)
159
+ };
160
+ }
161
+ const raw_shape = isRecord(spec) ? spec : {};
162
+ assertNoReservedUserEventFields(raw_shape, `BaseEvent.extend(${event_type})`);
163
+ assertNoUnknownEventPrefixedFields(raw_shape, `BaseEvent.extend(${event_type})`);
164
+ assertNoModelPrefixedFields(raw_shape, `BaseEvent.extend(${event_type})`);
165
+ const shortcut_shape = {
166
+ ...schemaDefaultsForShortcut(event_type, raw_shape),
167
+ ...zodFieldsForShortcut(raw_shape)
168
+ };
169
+ const full_schema = z.object(shortcut_shape).safeExtend(missingBaseFields(event_type, shortcut_shape)).loose();
170
+ return {
171
+ event_schema: full_schema,
172
+ event_result_type: normalizeEventResultType(raw_shape.event_result_type),
173
+ event_version: typeof raw_shape.event_version === "string" ? raw_shape.event_version : void 0
174
+ };
175
+ }
176
+ function decodeEventSchema(schema, input) {
177
+ const decoded = z.decode(schema, input);
178
+ if (!isRecord(decoded)) {
179
+ throw new Error("BaseEvent schema must decode to an object");
180
+ }
181
+ return decoded;
182
+ }
183
+ function encodeEventSchema(schema, input) {
184
+ const encoded = z.encode(schema, input);
185
+ if (!isRecord(encoded)) {
186
+ throw new Error("BaseEvent schema must encode to an object");
187
+ }
188
+ return encoded;
189
+ }
71
190
  class BaseEvent {
72
191
  // event metadata fields
73
192
  event_id;
@@ -86,7 +205,7 @@ class BaseEvent {
86
205
  event_handler_slow_timeout;
87
206
  // optional per-event slow handler warning threshold in seconds
88
207
  event_blocks_parent_completion;
89
- // true only for children explicitly awaited via done()/eventCompleted()
208
+ // true only for children explicitly awaited via now()
90
209
  event_parent_id;
91
210
  // id of the parent event that triggered this event, if this event was emitted during handling of another event, else null
92
211
  event_path;
@@ -109,11 +228,13 @@ class BaseEvent {
109
228
  // concurrency mode for the handlers within the event
110
229
  event_handler_completion;
111
230
  // completion strategy: 'all' (default) waits for every handler, 'first' returns earliest non-undefined result and cancels the rest
231
+ event_schema;
112
232
  static event_type;
113
233
  // class name of the event, e.g. BaseEvent.extend("MyEvent").event_type === "MyEvent"
114
234
  static event_version = "0.0.1";
115
- static schema = BaseEventSchema;
116
- // zod schema for the event data fields, used to parse and validate event data when creating a new event
235
+ static event_result_type;
236
+ static event_schema = BaseEventSchema;
237
+ // generated Zod schema for local TS event data validation; never sent over the wire
117
238
  // internal runtime state
118
239
  event_bus;
119
240
  // bus that dispatched this event, also used by event.emit(child)
@@ -121,6 +242,7 @@ class BaseEvent {
121
242
  // underlying event object that was dispatched, if this is a bus-scoped proxy wrapping it
122
243
  _event_dispatch_context;
123
244
  // captured AsyncLocalStorage context at dispatch site, used to restore that context when running handlers
245
+ _event_fields_set;
124
246
  _event_completed_signal;
125
247
  _lock_for_event_handler;
126
248
  constructor(data = {}) {
@@ -128,34 +250,42 @@ class BaseEvent {
128
250
  assertNoUnknownEventPrefixedFields(data, "BaseEvent");
129
251
  assertNoModelPrefixedFields(data, "BaseEvent");
130
252
  const ctor = this.constructor;
131
- const ctor_defaults = EVENT_CLASS_DEFAULTS.get(ctor) ?? {};
132
- const merged_data = {
133
- ...ctor_defaults,
134
- ...data
135
- };
253
+ const explicit_event_fields = new Set(Object.keys(data ?? {}));
254
+ const merged_data = { ...data };
136
255
  const event_type = merged_data.event_type ?? ctor.event_type ?? ctor.name;
137
256
  const event_version = merged_data.event_version ?? ctor.event_version ?? "0.0.1";
138
257
  const raw_event_result_type = merged_data.event_result_type ?? ctor.event_result_type;
139
258
  const event_result_type = normalizeEventResultType(raw_event_result_type);
140
- const event_id = merged_data.event_id ?? uuidv7();
141
- const event_created_at = monotonicDatetime(merged_data.event_created_at);
142
- const event_timeout = merged_data.event_timeout ?? null;
143
- const event_blocks_parent_completion = merged_data.event_blocks_parent_completion ?? false;
259
+ const event_schema = ctor.event_schema ?? BaseEventSchema;
144
260
  const base_data = {
145
261
  ...merged_data,
146
- event_id,
147
- event_created_at,
262
+ event_id: merged_data.event_id ?? uuidv7(),
263
+ event_created_at: merged_data.event_created_at ?? monotonicDatetime(),
148
264
  event_type,
149
265
  event_version,
150
- event_timeout,
151
- event_blocks_parent_completion,
152
266
  event_result_type
153
267
  };
154
- const schema = ctor.schema ?? BaseEventSchema;
155
- const parsed = schema.parse(base_data);
268
+ if (event_schema === BaseEventSchema) {
269
+ base_data.event_timeout ??= null;
270
+ base_data.event_blocks_parent_completion ??= false;
271
+ }
272
+ const parsed = decodeEventSchema(event_schema, base_data);
156
273
  Object.assign(this, parsed);
274
+ Object.defineProperty(this, "event_schema", {
275
+ value: event_schema,
276
+ writable: true,
277
+ enumerable: false,
278
+ configurable: true
279
+ });
280
+ Object.defineProperty(this, "_event_fields_set", {
281
+ value: explicit_event_fields,
282
+ writable: true,
283
+ enumerable: false,
284
+ configurable: true
285
+ });
157
286
  const parsed_path = parsed.event_path;
158
287
  this.event_path = Array.isArray(parsed_path) ? [...parsed_path] : [];
288
+ this.event_created_at = monotonicDatetime(parsed.event_created_at);
159
289
  this.event_results = hydrateEventResults(this, parsed.event_results);
160
290
  this.event_pending_bus_count = typeof parsed.event_pending_bus_count === "number" ? Math.max(0, Number(parsed.event_pending_bus_count)) : 0;
161
291
  const parsed_status = parsed.event_status;
@@ -164,7 +294,7 @@ class BaseEvent {
164
294
  this.event_completed_at = parsed.event_completed_at === null || parsed.event_completed_at === void 0 ? null : monotonicDatetime(parsed.event_completed_at);
165
295
  this.event_parent_id = typeof parsed.event_parent_id === "string" ? parsed.event_parent_id : null;
166
296
  this.event_emitted_by_handler_id = typeof parsed.event_emitted_by_handler_id === "string" ? parsed.event_emitted_by_handler_id : null;
167
- this.event_result_type = event_result_type;
297
+ this.event_result_type = normalizeEventResultType(parsed.event_result_type ?? event_result_type);
168
298
  this._event_completed_signal = null;
169
299
  this._lock_for_event_handler = null;
170
300
  this._event_dispatch_context = void 0;
@@ -173,23 +303,13 @@ class BaseEvent {
173
303
  toString() {
174
304
  return `${this.event_type}#${this.event_id.slice(-4)}`;
175
305
  }
176
- static extend(event_type, shape = {}) {
177
- const raw_shape = shape;
178
- assertNoReservedUserEventFields(raw_shape, `BaseEvent.extend(${event_type})`);
179
- assertNoUnknownEventPrefixedFields(raw_shape, `BaseEvent.extend(${event_type})`);
180
- assertNoModelPrefixedFields(raw_shape, `BaseEvent.extend(${event_type})`);
181
- const raw_event_result_type = raw_shape.event_result_type;
182
- const event_result_type = normalizeEventResultType(raw_event_result_type);
183
- const event_version = typeof raw_shape.event_version === "string" ? raw_shape.event_version : void 0;
184
- const event_defaults = Object.fromEntries(
185
- Object.entries(raw_shape).filter(
186
- ([key, value]) => key !== "event_result_type" && key !== "event_version" && !(value instanceof z.ZodType)
187
- )
188
- );
189
- const zod_shape = extractZodShape(raw_shape);
190
- const full_schema = BaseEventSchema.extend(zod_shape);
306
+ static extend(event_type, shape) {
307
+ const built = buildFullEventSchema(event_type, shape ?? {});
308
+ const full_schema = built.event_schema;
309
+ const event_result_type = built.event_result_type;
310
+ const event_version = built.event_version;
191
311
  class ExtendedEvent extends BaseEvent {
192
- static schema = full_schema;
312
+ static event_schema = full_schema;
193
313
  static event_type = event_type;
194
314
  static event_version = event_version ?? BaseEvent.event_version;
195
315
  static event_result_type = event_result_type;
@@ -200,23 +320,36 @@ class BaseEvent {
200
320
  function EventFactory(data) {
201
321
  return new ExtendedEvent(data);
202
322
  }
203
- EventFactory.schema = full_schema;
323
+ EventFactory.event_schema = full_schema;
204
324
  EventFactory.event_type = event_type;
205
325
  EventFactory.event_version = event_version ?? BaseEvent.event_version;
206
326
  EventFactory.event_result_type = event_result_type;
207
327
  EventFactory.class = ExtendedEvent;
208
328
  EventFactory.fromJSON = (data) => ExtendedEvent.fromJSON(data);
209
329
  EventFactory.prototype = ExtendedEvent.prototype;
210
- EVENT_CLASS_DEFAULTS.set(ExtendedEvent, event_defaults);
330
+ EVENT_TYPE_REGISTRY.set(event_type, ExtendedEvent);
211
331
  return EventFactory;
212
332
  }
213
333
  static fromJSON(data) {
214
334
  if (!data || typeof data !== "object") {
215
- const schema = this.schema ?? BaseEventSchema;
216
- const parsed = schema.parse(data);
335
+ const event_schema = this.event_schema ?? BaseEventSchema;
336
+ const parsed = decodeEventSchema(event_schema, data);
217
337
  return new this(parsed);
218
338
  }
219
339
  const record = { ...data };
340
+ if (this === BaseEvent) {
341
+ const event_type = record.event_type;
342
+ if (typeof event_type === "string") {
343
+ const KnownEvent = EVENT_TYPE_REGISTRY.get(event_type);
344
+ if (KnownEvent) {
345
+ return KnownEvent.fromJSON(record);
346
+ }
347
+ }
348
+ }
349
+ const ctor = this;
350
+ if (this !== BaseEvent && ctor.event_result_type && record.event_result_type !== void 0) {
351
+ delete record.event_result_type;
352
+ }
220
353
  if (record.event_result_type !== void 0 && record.event_result_type !== null) {
221
354
  record.event_result_type = normalizeEventResultType(record.event_result_type);
222
355
  }
@@ -237,18 +370,47 @@ class BaseEvent {
237
370
  toJSON() {
238
371
  const record = {};
239
372
  for (const [key, value] of Object.entries(this)) {
240
- if (key.startsWith("_") || key === "bus" || key === "event_bus" || key === "event_results") continue;
373
+ if (key.startsWith("_") || key === "bus" || key === "event_bus" || key === "event_schema" || key === "event_results") continue;
241
374
  if (value === void 0 || typeof value === "function") continue;
242
375
  record[key] = value;
243
376
  }
244
377
  const event_results = Object.fromEntries(
245
378
  Array.from(this.event_results.entries()).map(([handler_id, result]) => [handler_id, result.toJSON()])
246
379
  );
247
- return {
380
+ const event_schema = this.constructor.event_schema ?? this.event_schema ?? BaseEventSchema;
381
+ const encoded = encodeEventSchema(event_schema, {
248
382
  ...record,
249
383
  event_id: this.event_id,
250
384
  event_type: this.event_type,
251
385
  event_version: this.event_version,
386
+ event_result_type: this.event_result_type,
387
+ // static configuration options
388
+ event_timeout: this.event_timeout,
389
+ event_slow_timeout: this.event_slow_timeout,
390
+ event_concurrency: this.event_concurrency,
391
+ event_handler_concurrency: this.event_handler_concurrency,
392
+ event_handler_completion: this.event_handler_completion,
393
+ event_handler_slow_timeout: this.event_handler_slow_timeout,
394
+ event_handler_timeout: this.event_handler_timeout,
395
+ event_blocks_parent_completion: this.event_blocks_parent_completion,
396
+ // mutable parent/child/bus tracking runtime state
397
+ event_parent_id: this.event_parent_id,
398
+ event_path: this.event_path,
399
+ event_emitted_by_handler_id: this.event_emitted_by_handler_id,
400
+ event_pending_bus_count: this.event_pending_bus_count,
401
+ // mutable runtime status and timestamps
402
+ event_status: this.event_status,
403
+ event_created_at: this.event_created_at,
404
+ event_started_at: this.event_started_at ?? null,
405
+ event_completed_at: this.event_completed_at ?? null,
406
+ ...Object.keys(event_results).length > 0 ? { event_results } : {}
407
+ });
408
+ delete encoded.event_schema;
409
+ return {
410
+ ...encoded,
411
+ event_id: this.event_id,
412
+ event_type: this.event_type,
413
+ event_version: this.event_version,
252
414
  event_result_type: this.event_result_type ? toJsonSchema(this.event_result_type) : this.event_result_type,
253
415
  // static configuration options
254
416
  event_timeout: this.event_timeout,
@@ -273,13 +435,12 @@ class BaseEvent {
273
435
  ...Object.keys(event_results).length > 0 ? { event_results } : {}
274
436
  };
275
437
  }
276
- _createSlowEventWarningTimer() {
277
- const event_slow_timeout = this.event_slow_timeout ?? this.event_bus?.event_slow_timeout ?? null;
278
- const event_warn_ms = event_slow_timeout === null ? null : event_slow_timeout * 1e3;
438
+ _createSlowEventWarningTimer(event_slow_timeout = this.event_slow_timeout ?? null, bus_name) {
439
+ const event_warn_ms = event_slow_timeout === null || event_slow_timeout <= 0 ? null : event_slow_timeout * 1e3;
279
440
  if (event_warn_ms === null) {
280
441
  return null;
281
442
  }
282
- const name = this.event_bus?.name ?? "EventBus";
443
+ const name = bus_name ?? this.event_bus?.name ?? "EventBus";
283
444
  return setTimeout(() => {
284
445
  if (this.event_status === "completed") {
285
446
  return;
@@ -365,7 +526,13 @@ class BaseEvent {
365
526
  return Array.from(original.event_results.values()).filter((result) => result.eventbus_id === this.event_bus.id);
366
527
  }
367
528
  _isFirstModeWinningResult(entry) {
368
- return entry.status === "completed" && entry.result !== void 0 && entry.result !== null && !(entry.result instanceof BaseEvent);
529
+ return BaseEvent._defaultResultInclude(entry.result, entry);
530
+ }
531
+ static _defaultResultInclude(result, event_result) {
532
+ return event_result.status === "completed" && result !== void 0 && result !== null && !(result instanceof Error) && !(result instanceof BaseEvent) && event_result.error === void 0;
533
+ }
534
+ static _includeEventResult(include, event_result) {
535
+ return include(event_result.result, event_result);
369
536
  }
370
537
  _markFirstModeWinnerIfNeeded(original, entry, first_state) {
371
538
  if (first_state.found || !this._isFirstModeWinningResult(entry)) {
@@ -378,9 +545,13 @@ class BaseEvent {
378
545
  if (!this.event_bus) {
379
546
  throw new Error("event has no bus attached");
380
547
  }
381
- await this.event_bus.locks._runWithHandlerLock(original, this.event_bus.event_handler_concurrency, async (handler_lock) => {
382
- await entry.runHandler(handler_lock);
383
- });
548
+ await this.event_bus.locks._runWithHandlerLock(
549
+ original,
550
+ original.event_handler_concurrency ?? this.event_bus.event_handler_concurrency,
551
+ async (handler_lock) => {
552
+ await entry.runHandler(handler_lock);
553
+ }
554
+ );
384
555
  }
385
556
  // Run all pending handler results for the current bus context.
386
557
  async _runHandlers(pending_entries) {
@@ -391,7 +562,7 @@ class BaseEvent {
391
562
  }
392
563
  const resolved_completion = original.event_handler_completion ?? this.event_bus?.event_handler_completion ?? "all";
393
564
  if (resolved_completion === "first") {
394
- if (original._getHandlerLock(this.event_bus?.event_handler_concurrency) !== null) {
565
+ if (original._getHandlerLock(original.event_handler_concurrency ?? this.event_bus?.event_handler_concurrency ?? "serial") !== null) {
395
566
  for (const entry of pending_results) {
396
567
  await this._runHandlerWithLock(original, entry);
397
568
  if (!this._isFirstModeWinningResult(entry)) {
@@ -418,7 +589,7 @@ class BaseEvent {
418
589
  }
419
590
  _getHandlerLock(default_concurrency) {
420
591
  const original = this._event_original ?? this;
421
- const resolved = original.event_handler_concurrency ?? default_concurrency ?? original.event_bus?.event_handler_concurrency ?? "serial";
592
+ const resolved = original.event_handler_concurrency ?? default_concurrency ?? "serial";
422
593
  if (resolved === "parallel") {
423
594
  return null;
424
595
  }
@@ -530,18 +701,18 @@ class BaseEvent {
530
701
  cancelChildEvent(child);
531
702
  }
532
703
  }
533
- // Cancel all handler results for an event except the winner, used by first() mode.
704
+ // Cancel all handler results for an event except the winner, used by event_handler_completion='first'.
534
705
  // Cancels pending handlers immediately, aborts started handlers via _signalAbort(),
535
706
  // and cancels any child events emitted by the losing handlers.
536
707
  _markRemainingFirstModeResultCancelled(winner) {
537
- const cause = new Error("first() resolved: another handler returned a result first");
708
+ const cause = new Error("event_handler_completion='first' resolved: another handler returned a result first");
538
709
  const bus_id = winner.eventbus_id;
539
710
  for (const result of this.event_results.values()) {
540
711
  if (result === winner) continue;
541
712
  if (result.eventbus_id !== bus_id) continue;
542
713
  if (result.status === "pending") {
543
714
  result._markError(
544
- new EventHandlerCancelledError(`Cancelled: first() resolved`, {
715
+ new EventHandlerCancelledError(`Cancelled: event_handler_completion='first' resolved`, {
545
716
  event_result: result,
546
717
  cause
547
718
  })
@@ -556,7 +727,7 @@ class BaseEvent {
556
727
  original_child._markCancelled(cause);
557
728
  }
558
729
  result._lock?.exitHandlerRun();
559
- const aborted_error = new EventHandlerAbortedError(`Aborted: first() resolved`, {
730
+ const aborted_error = new EventHandlerAbortedError(`Aborted: event_handler_completion='first' resolved`, {
560
731
  event_result: result,
561
732
  cause
562
733
  });
@@ -633,92 +804,41 @@ class BaseEvent {
633
804
  parent_id = parent.event_parent_id;
634
805
  }
635
806
  }
636
- // awaitable that triggers immediate (queue-jump) processing of the event on all buses where it is queued
637
- // use eventCompleted() to wait for normal queue-order completion without queue-jumping.
638
- done(options = {}) {
639
- if (!this.event_bus) {
640
- return Promise.reject(new Error("event has no bus attached"));
641
- }
807
+ _withEventResultMethods(promise) {
808
+ const chainable = promise;
809
+ chainable.eventResult = async (options) => {
810
+ const event = await promise;
811
+ return event.eventResult(options);
812
+ };
813
+ chainable.eventResultsList = async (options) => {
814
+ const event = await promise;
815
+ return event.eventResultsList(options);
816
+ };
817
+ return chainable;
818
+ }
819
+ _timeoutPromise(timeout, message, fn) {
820
+ return timeout === null || timeout <= 0 ? fn() : _runWithTimeout(timeout, () => new Error(message()), fn);
821
+ }
822
+ _orderedEventResults() {
642
823
  const original = this._event_original ?? this;
643
- original._markBlocksParentCompletionIfAwaitedFromEmittingHandler();
644
- const raise_if_any = options.raise_if_any ?? true;
645
- const completion_promise = this.event_status === "completed" ? Promise.resolve(original) : this.event_bus._processEventImmediately(this);
646
- if (!raise_if_any) {
647
- return completion_promise;
648
- }
649
- return completion_promise.then((completed_event) => {
650
- const first_error = completed_event._firstProcessingError();
651
- if (first_error !== void 0) {
652
- if (first_error instanceof Error) {
653
- throw first_error;
654
- }
655
- throw new Error(String(first_error));
656
- }
657
- return completed_event;
658
- });
824
+ return Array.from(original.event_results.values()).sort(
825
+ (a, b) => compareIsoDatetime(a.completed_at, b.completed_at)
826
+ );
659
827
  }
660
- // returns the first non-undefined handler result value, cancelling remaining handlers
661
- // when any handler completes. Works with all event_handler_concurrency modes:
662
- // parallel: races all handlers, returns first non-undefined, aborts the rest
663
- // serial: runs handlers sequentially, returns first non-undefined, skips remaining
664
- first() {
665
- if (!this.event_bus) {
666
- return Promise.reject(new Error("event has no bus attached"));
667
- }
828
+ _orderedEventResultsByRegistration() {
668
829
  const original = this._event_original ?? this;
669
- original.event_handler_completion = "first";
670
- return this.done({ raise_if_any: false }).then((completed_event) => {
671
- const first_error = completed_event._firstProcessingError({ ignore_first_mode_control_errors: true });
672
- if (first_error !== void 0) {
673
- if (first_error instanceof Error) {
674
- throw first_error;
675
- }
676
- throw new Error(String(first_error));
677
- }
678
- const orig = completed_event._event_original ?? completed_event;
679
- return Array.from(orig.event_results.values()).filter(
680
- (result) => result.status === "completed" && result.result !== void 0 && result.result !== null && !(result.result instanceof BaseEvent)
681
- ).sort((a, b) => compareIsoDatetime(a.completed_at, b.completed_at)).map((result) => result.result).at(0);
682
- });
830
+ return Array.from(original.event_results.values()).sort(
831
+ (a, b) => compareIsoDatetime(a.handler.handler_registered_at, b.handler.handler_registered_at) || compareIsoDatetime(a.started_at, b.started_at) || a.handler_id.localeCompare(b.handler_id)
832
+ );
683
833
  }
684
- async eventResultsList(include_or_options, maybe_options) {
685
- const default_include = (_result, event_result) => event_result.status === "completed" && event_result.result !== void 0 && event_result.result !== null && !(event_result.result instanceof Error) && !(event_result.result instanceof BaseEvent) && event_result.error === void 0;
686
- let options;
687
- let include;
688
- if (typeof include_or_options === "function") {
689
- options = maybe_options ?? {};
690
- include = include_or_options;
691
- } else {
692
- options = include_or_options ?? {};
693
- include = options.include ?? default_include;
694
- }
834
+ _collectResultValues(options = {}, order = "completion") {
835
+ const include = options.include ?? BaseEvent._defaultResultInclude;
695
836
  const raise_if_any = options.raise_if_any ?? true;
696
- const raise_if_none = options.raise_if_none ?? true;
697
- const original = this._event_original ?? this;
698
- const resolved_timeout_seconds = options.timeout ?? original.event_timeout ?? this.event_bus?.event_timeout ?? null;
699
- let completed_event;
700
- if (resolved_timeout_seconds === null) {
701
- completed_event = await this.done({ raise_if_any: false });
702
- } else {
703
- completed_event = await _runWithTimeout(
704
- resolved_timeout_seconds,
705
- () => new Error(`Timed out waiting for ${original.event_type} results after ${resolved_timeout_seconds}s`),
706
- () => this.done({ raise_if_any: false })
707
- );
708
- }
709
- const all_results = Array.from(completed_event.event_results.values());
837
+ const raise_if_none = options.raise_if_none ?? false;
838
+ const all_results = order === "registration" ? this._orderedEventResultsByRegistration() : this._orderedEventResults();
710
839
  const error_results = all_results.filter((event_result) => event_result.error !== void 0 || event_result.result instanceof Error);
711
- if (raise_if_any && error_results.length > 0) {
712
- if (error_results.length === 1) {
713
- const first_error = error_results[0];
714
- if (first_error.error instanceof Error) {
715
- throw first_error.error;
716
- }
717
- if (first_error.result instanceof Error) {
718
- throw first_error.result;
719
- }
720
- throw new Error(String(first_error.error ?? first_error.result));
721
- }
840
+ const included_results = all_results.filter((event_result) => BaseEvent._includeEventResult(include, event_result));
841
+ if (error_results.length > 0 && raise_if_any) {
722
842
  const errors = error_results.map((event_result) => {
723
843
  if (event_result.error instanceof Error) {
724
844
  return event_result.error;
@@ -728,28 +848,104 @@ class BaseEvent {
728
848
  }
729
849
  return new Error(String(event_result.error ?? event_result.result));
730
850
  });
731
- throw new AggregateError(
732
- errors,
733
- `Event ${completed_event.event_type}#${completed_event.event_id.slice(-4)} had ${errors.length} handler error(s)`
734
- );
851
+ if (errors.length === 1) {
852
+ throw errors[0];
853
+ }
854
+ throw new AggregateError(errors, `Event ${this.event_type}#${this.event_id.slice(-4)} had ${errors.length} handler error(s)`);
735
855
  }
736
- const included_results = all_results.filter((event_result) => include(event_result.result, event_result));
737
856
  if (raise_if_none && included_results.length === 0) {
738
857
  throw new Error(
739
- `Expected at least one handler to return a non-null result, but none did: ${completed_event.event_type}#${completed_event.event_id.slice(-4)}`
858
+ `Expected at least one handler to return a non-null result, but none did: ${this.event_type}#${this.event_id.slice(-4)}`
740
859
  );
741
860
  }
742
861
  return included_results.map((event_result) => event_result.result);
743
862
  }
744
- // awaitable that waits for the event to be processed in normal queue order by the _runloop
745
- eventCompleted() {
863
+ _hasIncludedResult(options = {}) {
864
+ const include = options.include ?? BaseEvent._defaultResultInclude;
865
+ return this._orderedEventResults().some((event_result) => BaseEvent._includeEventResult(include, event_result));
866
+ }
867
+ async _waitForFirstResultOrCompletion(options = {}) {
746
868
  const original = this._event_original ?? this;
869
+ if (options.timeout !== void 0 && options.timeout !== null && options.timeout < 0) {
870
+ throw new Error("timeout must be >= 0 or null");
871
+ }
872
+ if (!this.event_bus && original.event_status !== "completed") {
873
+ throw new Error("event has no bus attached");
874
+ }
875
+ if (original.event_status === "completed" || this._hasIncludedResult(options)) {
876
+ return this;
877
+ }
878
+ const waitForResult = async () => {
879
+ for (; ; ) {
880
+ if (original.event_status === "completed" || this._hasIncludedResult(options)) {
881
+ return this;
882
+ }
883
+ await new Promise((resolve) => setTimeout(resolve, 1));
884
+ }
885
+ };
886
+ const timeout = options.timeout ?? null;
887
+ return this._timeoutPromise(timeout, () => `Timed out waiting for ${original.event_type} result after ${timeout}s`, waitForResult);
888
+ }
889
+ // Active awaitable that triggers immediate (queue-jump) processing of the event on all buses where it is queued.
890
+ now(options = {}) {
891
+ const original = this._event_original ?? this;
892
+ if (options.timeout !== void 0 && options.timeout !== null && options.timeout < 0) {
893
+ return this._withEventResultMethods(Promise.reject(new Error("timeout must be >= 0 or null")));
894
+ }
895
+ if (!this.event_bus && original.event_status !== "completed") {
896
+ return this._withEventResultMethods(Promise.reject(new Error("event has no bus attached")));
897
+ }
747
898
  original._markBlocksParentCompletionIfAwaitedFromEmittingHandler();
748
- if (this.event_status === "completed") {
749
- return Promise.resolve(this);
899
+ const resolved_timeout_seconds = options.timeout ?? null;
900
+ const processing = original.event_status === "completed" ? Promise.resolve(this) : this._timeoutPromise(
901
+ resolved_timeout_seconds,
902
+ () => `Timed out waiting for ${original.event_type} completion after ${resolved_timeout_seconds}s`,
903
+ () => this.event_bus._processEventImmediately(this)
904
+ );
905
+ if (options.first_result) {
906
+ void processing.catch(() => void 0);
907
+ return this._withEventResultMethods(this._waitForFirstResultOrCompletion(options));
908
+ }
909
+ return this._withEventResultMethods(processing);
910
+ }
911
+ // Passive awaitable that waits for normal queue-order processing without forcing execution.
912
+ wait(options = {}) {
913
+ const original = this._event_original ?? this;
914
+ if (options.timeout !== void 0 && options.timeout !== null && options.timeout < 0) {
915
+ return this._withEventResultMethods(Promise.reject(new Error("timeout must be >= 0 or null")));
916
+ }
917
+ if (!this.event_bus && original.event_status !== "completed") {
918
+ return this._withEventResultMethods(Promise.reject(new Error("event has no bus attached")));
919
+ }
920
+ if (options.first_result) {
921
+ return this._withEventResultMethods(this._waitForFirstResultOrCompletion(options));
922
+ }
923
+ if (original.event_status === "completed") {
924
+ return this._withEventResultMethods(Promise.resolve(this));
750
925
  }
751
926
  this._notifyDoneListeners();
752
- return this._event_completed_signal.promise;
927
+ const timeout = options.timeout ?? null;
928
+ return this._withEventResultMethods(
929
+ this._timeoutPromise(
930
+ timeout,
931
+ () => `Timed out waiting for ${original.event_type} completion after ${timeout}s`,
932
+ () => this._event_completed_signal.promise.then(() => this)
933
+ )
934
+ );
935
+ }
936
+ async eventResult(options = {}) {
937
+ const original = this._event_original ?? this;
938
+ if (original.event_status === "pending" && original.event_results.size === 0) {
939
+ await this.now({ first_result: true });
940
+ }
941
+ return this._collectResultValues(options, "registration").at(0);
942
+ }
943
+ async eventResultsList(options = {}) {
944
+ const original = this._event_original ?? this;
945
+ if (original.event_status === "pending" && original.event_results.size === 0) {
946
+ await this.now({ first_result: false });
947
+ }
948
+ return this._collectResultValues(options, "registration");
753
949
  }
754
950
  _markBlocksParentCompletionIfAwaitedFromEmittingHandler() {
755
951
  const original = this._event_original ?? this;
@@ -843,23 +1039,8 @@ class BaseEvent {
843
1039
  get event_errors() {
844
1040
  return Array.from(this.event_results.values()).filter((event_result) => event_result.error !== void 0 && event_result.completed_at !== null).sort((event_result_a, event_result_b) => compareIsoDatetime(event_result_a.completed_at, event_result_b.completed_at)).map((event_result) => event_result.error);
845
1041
  }
846
- _isFirstModeControlError(error) {
847
- if (!(error instanceof EventHandlerCancelledError || error instanceof EventHandlerAbortedError)) {
848
- return false;
849
- }
850
- if (error.message.includes("first() resolved")) {
851
- return true;
852
- }
853
- return error.cause instanceof Error && error.cause.message.includes("first() resolved");
854
- }
855
- _firstProcessingError(options = {}) {
856
- const ignore_first_mode_control_errors = options.ignore_first_mode_control_errors ?? false;
857
- return Array.from(this.event_results.values()).filter((event_result) => event_result.error !== void 0 && event_result.completed_at !== null).filter((event_result) => ignore_first_mode_control_errors ? !this._isFirstModeControlError(event_result.error) : true).sort((event_result_a, event_result_b) => compareIsoDatetime(event_result_a.completed_at, event_result_b.completed_at)).map((event_result) => event_result.error).at(0);
858
- }
859
- // Returns the first non-undefined completed handler result, sorted by completion time.
860
- // Useful after first() or done() to get the winning result value.
861
- get event_result() {
862
- return Array.from(this.event_results.values()).filter((event_result) => event_result.completed_at !== null && event_result.result !== void 0).sort((event_result_a, event_result_b) => compareIsoDatetime(event_result_a.completed_at, event_result_b.completed_at)).map((event_result) => event_result.result).at(0);
1042
+ _firstProcessingError() {
1043
+ return Array.from(this.event_results.values()).filter((event_result) => event_result.error !== void 0 && event_result.completed_at !== null).sort((event_result_a, event_result_b) => compareIsoDatetime(event_result_a.completed_at, event_result_b.completed_at)).map((event_result) => event_result.error).at(0);
863
1044
  }
864
1045
  _areAllChildrenComplete(visited = /* @__PURE__ */ new Set()) {
865
1046
  const original = this._event_original ?? this;