abxbus 2.4.32 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/README.md +74 -51
  2. package/dist/cjs/BaseEvent.d.ts +46 -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/events_suck.js +1 -1
  16. package/dist/cjs/events_suck.js.map +2 -2
  17. package/dist/cjs/index.d.ts +1 -0
  18. package/dist/cjs/index.js.map +2 -2
  19. package/dist/cjs/timing.js +1 -1
  20. package/dist/cjs/timing.js.map +2 -2
  21. package/dist/esm/BaseEvent.js +351 -170
  22. package/dist/esm/BaseEvent.js.map +3 -3
  23. package/dist/esm/EventBus.js +153 -85
  24. package/dist/esm/EventBus.js.map +2 -2
  25. package/dist/esm/EventHandler.js.map +1 -1
  26. package/dist/esm/EventResult.js +16 -22
  27. package/dist/esm/EventResult.js.map +2 -2
  28. package/dist/esm/LockManager.js +4 -1
  29. package/dist/esm/LockManager.js.map +2 -2
  30. package/dist/esm/events_suck.js +1 -1
  31. package/dist/esm/events_suck.js.map +2 -2
  32. package/dist/esm/index.js.map +2 -2
  33. package/dist/esm/timing.js +1 -1
  34. package/dist/esm/timing.js.map +2 -2
  35. package/dist/types/BaseEvent.d.ts +46 -55
  36. package/dist/types/EventBus.d.ts +8 -1
  37. package/dist/types/EventHandler.d.ts +3 -3
  38. package/dist/types/LockManager.d.ts +1 -0
  39. package/dist/types/index.d.ts +1 -0
  40. package/package.json +4 -3
  41. package/src/BaseEvent.ts +456 -219
  42. package/src/EventBus.ts +186 -99
  43. package/src/EventHandler.ts +3 -3
  44. package/src/EventResult.ts +18 -22
  45. package/src/LockManager.ts +5 -1
  46. package/src/events_suck.ts +1 -1
  47. package/src/index.ts +1 -0
  48. package/src/timing.ts +1 -1
  49. package/dist/cjs/base_event.d.ts +0 -211
  50. package/dist/cjs/bridge_jsonl.d.ts +0 -26
  51. package/dist/cjs/bridge_nats.d.ts +0 -20
  52. package/dist/cjs/bridge_postgres.d.ts +0 -31
  53. package/dist/cjs/bridge_redis.d.ts +0 -34
  54. package/dist/cjs/bridge_sqlite.d.ts +0 -30
  55. package/dist/cjs/event_bus.d.ts +0 -125
  56. package/dist/cjs/event_handler.d.ts +0 -139
  57. package/dist/cjs/event_history.d.ts +0 -45
  58. package/dist/cjs/event_result.d.ts +0 -86
  59. package/dist/cjs/lock_manager.d.ts +0 -70
  60. package/dist/types/base_event.d.ts +0 -211
  61. package/dist/types/bridge_jsonl.d.ts +0 -26
  62. package/dist/types/bridge_nats.d.ts +0 -20
  63. package/dist/types/bridge_postgres.d.ts +0 -31
  64. package/dist/types/bridge_redis.d.ts +0 -34
  65. package/dist/types/bridge_sqlite.d.ts +0 -30
  66. package/dist/types/event_bus.d.ts +0 -125
  67. package/dist/types/event_handler.d.ts +0 -139
  68. package/dist/types/event_history.d.ts +0 -45
  69. package/dist/types/event_result.d.ts +0 -86
  70. package/dist/types/lock_manager.d.ts +0 -70
@@ -30,7 +30,18 @@ var import_LockManager = require("./LockManager.js");
30
30
  var import_timing = require("./timing.js");
31
31
  var import_types = require("./types.js");
32
32
  var import_helpers = require("./helpers.js");
33
- const RESERVED_USER_EVENT_FIELDS = /* @__PURE__ */ new Set(["bus", "emit", "first", "toString", "toJSON", "fromJSON"]);
33
+ const RESERVED_USER_EVENT_FIELDS = /* @__PURE__ */ new Set([
34
+ "bus",
35
+ "emit",
36
+ "wait",
37
+ "now",
38
+ "eventResult",
39
+ "eventResultsList",
40
+ "toString",
41
+ "toJSON",
42
+ "fromJSON"
43
+ ]);
44
+ const EVENT_TYPE_REGISTRY = /* @__PURE__ */ new Map();
34
45
  function assertNoReservedUserEventFields(data, context) {
35
46
  for (const field_name of RESERVED_USER_EVENT_FIELDS) {
36
47
  if (Object.prototype.hasOwnProperty.call(data, field_name)) {
@@ -52,6 +63,12 @@ function assertNoModelPrefixedFields(data, context) {
52
63
  }
53
64
  }
54
65
  }
66
+ function isRecord(value) {
67
+ return !!value && typeof value === "object" && !Array.isArray(value);
68
+ }
69
+ function isZodObjectSchema(value) {
70
+ return (0, import_types.isZodSchema)(value) && typeof value.safeExtend === "function" && isRecord(value.shape);
71
+ }
55
72
  function compareIsoDatetime(left, right) {
56
73
  const left_value = left ?? "";
57
74
  const right_value = right ?? "";
@@ -65,10 +82,10 @@ const BaseEventSchema = import_zod.z.object({
65
82
  event_created_at: import_zod.z.string().datetime(),
66
83
  event_type: import_zod.z.string(),
67
84
  event_version: import_zod.z.string().default("0.0.1"),
68
- event_timeout: import_zod.z.number().positive().nullable(),
69
- event_slow_timeout: import_zod.z.number().positive().nullable().optional(),
70
- event_handler_timeout: import_zod.z.number().positive().nullable().optional(),
71
- event_handler_slow_timeout: import_zod.z.number().positive().nullable().optional(),
85
+ event_timeout: import_zod.z.number().nonnegative().nullable(),
86
+ event_slow_timeout: import_zod.z.number().nonnegative().nullable().optional(),
87
+ event_handler_timeout: import_zod.z.number().nonnegative().nullable().optional(),
88
+ event_handler_slow_timeout: import_zod.z.number().nonnegative().nullable().optional(),
72
89
  event_blocks_parent_completion: import_zod.z.boolean().optional(),
73
90
  event_parent_id: import_zod.z.string().uuid().nullable().optional(),
74
91
  event_path: import_zod.z.array(import_zod.z.string()).optional(),
@@ -84,8 +101,110 @@ const BaseEventSchema = import_zod.z.object({
84
101
  event_handler_completion: import_zod.z.enum(import_LockManager.EVENT_HANDLER_COMPLETION_MODES).nullable().optional()
85
102
  }).loose();
86
103
  const KNOWN_BASE_EVENT_FIELDS = new Set(Object.keys(BaseEventSchema.shape));
87
- const EVENT_CLASS_DEFAULTS = /* @__PURE__ */ new WeakMap();
88
104
  const ROOT_EVENTBUS_ID = "00000000-0000-0000-0000-000000000000";
105
+ function baseEventDefaultShape(event_type) {
106
+ return {
107
+ event_id: import_zod.z.string().uuid(),
108
+ event_created_at: import_zod.z.string().datetime(),
109
+ event_type: import_zod.z.string().default(event_type),
110
+ event_version: import_zod.z.string().default("0.0.1"),
111
+ event_timeout: import_zod.z.number().nonnegative().nullable().default(null),
112
+ event_slow_timeout: import_zod.z.number().nonnegative().nullable().optional(),
113
+ event_handler_timeout: import_zod.z.number().nonnegative().nullable().optional(),
114
+ event_handler_slow_timeout: import_zod.z.number().nonnegative().nullable().optional(),
115
+ event_blocks_parent_completion: import_zod.z.boolean().default(false),
116
+ event_parent_id: import_zod.z.string().uuid().nullable().optional(),
117
+ event_path: import_zod.z.array(import_zod.z.string()).optional(),
118
+ event_result_type: import_zod.z.unknown().optional(),
119
+ event_emitted_by_handler_id: import_zod.z.string().uuid().nullable().optional(),
120
+ event_pending_bus_count: import_zod.z.number().nonnegative().optional(),
121
+ event_status: import_zod.z.enum(["pending", "started", "completed"]).optional(),
122
+ event_started_at: import_zod.z.string().datetime().nullable().optional(),
123
+ event_completed_at: import_zod.z.string().datetime().nullable().optional(),
124
+ event_results: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional(),
125
+ event_concurrency: import_zod.z.enum(import_LockManager.EVENT_CONCURRENCY_MODES).nullable().optional(),
126
+ event_handler_concurrency: import_zod.z.enum(import_LockManager.EVENT_HANDLER_CONCURRENCY_MODES).nullable().optional(),
127
+ event_handler_completion: import_zod.z.enum(import_LockManager.EVENT_HANDLER_COMPLETION_MODES).nullable().optional()
128
+ };
129
+ }
130
+ function missingBaseFields(event_type, user_shape) {
131
+ return Object.fromEntries(Object.entries(baseEventDefaultShape(event_type)).filter(([key]) => !(key in user_shape)));
132
+ }
133
+ function shortcutDefaultSchema(base_field_schema, value) {
134
+ if (!base_field_schema) {
135
+ return import_zod.z.unknown().optional().default(value);
136
+ }
137
+ return base_field_schema.prefault(base_field_schema.parse(value));
138
+ }
139
+ function schemaDefaultsForShortcut(event_type, raw_shape) {
140
+ const defaults = {};
141
+ const base_shape = baseEventDefaultShape(event_type);
142
+ for (const [key, value] of Object.entries(raw_shape)) {
143
+ if (key === "event_result_type") continue;
144
+ if (!(0, import_types.isZodSchema)(value)) {
145
+ defaults[key] = shortcutDefaultSchema(base_shape[key], value);
146
+ }
147
+ }
148
+ return defaults;
149
+ }
150
+ function zodFieldsForShortcut(raw_shape) {
151
+ const fields = {};
152
+ for (const [key, value] of Object.entries(raw_shape)) {
153
+ if (key === "event_result_type") continue;
154
+ if ((0, import_types.isZodSchema)(value)) {
155
+ fields[key] = value;
156
+ }
157
+ }
158
+ return fields;
159
+ }
160
+ function eventResultTypeFromObjectSchema(schema) {
161
+ const raw_event_result_type = schema.shape.event_result_type;
162
+ return raw_event_result_type === void 0 ? void 0 : (0, import_types.normalizeEventResultType)(raw_event_result_type);
163
+ }
164
+ function buildFullEventSchema(event_type, spec) {
165
+ if (isZodObjectSchema(spec)) {
166
+ const user_shape = spec.shape;
167
+ assertNoReservedUserEventFields(user_shape, `BaseEvent.extend(${event_type})`);
168
+ assertNoUnknownEventPrefixedFields(user_shape, `BaseEvent.extend(${event_type})`);
169
+ assertNoModelPrefixedFields(user_shape, `BaseEvent.extend(${event_type})`);
170
+ const full_schema2 = spec.safeExtend({
171
+ event_result_type: import_zod.z.unknown().optional(),
172
+ ...missingBaseFields(event_type, user_shape)
173
+ });
174
+ return {
175
+ event_schema: full_schema2,
176
+ event_result_type: eventResultTypeFromObjectSchema(spec)
177
+ };
178
+ }
179
+ const raw_shape = isRecord(spec) ? spec : {};
180
+ assertNoReservedUserEventFields(raw_shape, `BaseEvent.extend(${event_type})`);
181
+ assertNoUnknownEventPrefixedFields(raw_shape, `BaseEvent.extend(${event_type})`);
182
+ assertNoModelPrefixedFields(raw_shape, `BaseEvent.extend(${event_type})`);
183
+ const shortcut_shape = {
184
+ ...schemaDefaultsForShortcut(event_type, raw_shape),
185
+ ...zodFieldsForShortcut(raw_shape)
186
+ };
187
+ const full_schema = import_zod.z.object(shortcut_shape).safeExtend(missingBaseFields(event_type, shortcut_shape)).loose();
188
+ return {
189
+ event_schema: full_schema,
190
+ event_result_type: (0, import_types.normalizeEventResultType)(raw_shape.event_result_type),
191
+ event_version: typeof raw_shape.event_version === "string" ? raw_shape.event_version : void 0
192
+ };
193
+ }
194
+ function decodeEventSchema(schema, input) {
195
+ const decoded = import_zod.z.decode(schema, input);
196
+ if (!isRecord(decoded)) {
197
+ throw new Error("BaseEvent schema must decode to an object");
198
+ }
199
+ return decoded;
200
+ }
201
+ function encodeEventSchema(schema, input) {
202
+ const encoded = import_zod.z.encode(schema, input);
203
+ if (!isRecord(encoded)) {
204
+ throw new Error("BaseEvent schema must encode to an object");
205
+ }
206
+ return encoded;
207
+ }
89
208
  class BaseEvent {
90
209
  // event metadata fields
91
210
  event_id;
@@ -104,7 +223,7 @@ class BaseEvent {
104
223
  event_handler_slow_timeout;
105
224
  // optional per-event slow handler warning threshold in seconds
106
225
  event_blocks_parent_completion;
107
- // true only for children explicitly awaited via done()/eventCompleted()
226
+ // true only for children explicitly awaited via now()
108
227
  event_parent_id;
109
228
  // id of the parent event that triggered this event, if this event was emitted during handling of another event, else null
110
229
  event_path;
@@ -127,11 +246,13 @@ class BaseEvent {
127
246
  // concurrency mode for the handlers within the event
128
247
  event_handler_completion;
129
248
  // completion strategy: 'all' (default) waits for every handler, 'first' returns earliest non-undefined result and cancels the rest
249
+ event_schema;
130
250
  static event_type;
131
251
  // class name of the event, e.g. BaseEvent.extend("MyEvent").event_type === "MyEvent"
132
252
  static event_version = "0.0.1";
133
- static schema = BaseEventSchema;
134
- // zod schema for the event data fields, used to parse and validate event data when creating a new event
253
+ static event_result_type;
254
+ static event_schema = BaseEventSchema;
255
+ // generated Zod schema for local TS event data validation; never sent over the wire
135
256
  // internal runtime state
136
257
  event_bus;
137
258
  // bus that dispatched this event, also used by event.emit(child)
@@ -139,6 +260,7 @@ class BaseEvent {
139
260
  // underlying event object that was dispatched, if this is a bus-scoped proxy wrapping it
140
261
  _event_dispatch_context;
141
262
  // captured AsyncLocalStorage context at dispatch site, used to restore that context when running handlers
263
+ _event_fields_set;
142
264
  _event_completed_signal;
143
265
  _lock_for_event_handler;
144
266
  constructor(data = {}) {
@@ -146,34 +268,42 @@ class BaseEvent {
146
268
  assertNoUnknownEventPrefixedFields(data, "BaseEvent");
147
269
  assertNoModelPrefixedFields(data, "BaseEvent");
148
270
  const ctor = this.constructor;
149
- const ctor_defaults = EVENT_CLASS_DEFAULTS.get(ctor) ?? {};
150
- const merged_data = {
151
- ...ctor_defaults,
152
- ...data
153
- };
271
+ const explicit_event_fields = new Set(Object.keys(data ?? {}));
272
+ const merged_data = { ...data };
154
273
  const event_type = merged_data.event_type ?? ctor.event_type ?? ctor.name;
155
274
  const event_version = merged_data.event_version ?? ctor.event_version ?? "0.0.1";
156
275
  const raw_event_result_type = merged_data.event_result_type ?? ctor.event_result_type;
157
276
  const event_result_type = (0, import_types.normalizeEventResultType)(raw_event_result_type);
158
- const event_id = merged_data.event_id ?? (0, import_uuid.v7)();
159
- const event_created_at = (0, import_helpers.monotonicDatetime)(merged_data.event_created_at);
160
- const event_timeout = merged_data.event_timeout ?? null;
161
- const event_blocks_parent_completion = merged_data.event_blocks_parent_completion ?? false;
277
+ const event_schema = ctor.event_schema ?? BaseEventSchema;
162
278
  const base_data = {
163
279
  ...merged_data,
164
- event_id,
165
- event_created_at,
280
+ event_id: merged_data.event_id ?? (0, import_uuid.v7)(),
281
+ event_created_at: merged_data.event_created_at ?? (0, import_helpers.monotonicDatetime)(),
166
282
  event_type,
167
283
  event_version,
168
- event_timeout,
169
- event_blocks_parent_completion,
170
284
  event_result_type
171
285
  };
172
- const schema = ctor.schema ?? BaseEventSchema;
173
- const parsed = schema.parse(base_data);
286
+ if (event_schema === BaseEventSchema) {
287
+ base_data.event_timeout ??= null;
288
+ base_data.event_blocks_parent_completion ??= false;
289
+ }
290
+ const parsed = decodeEventSchema(event_schema, base_data);
174
291
  Object.assign(this, parsed);
292
+ Object.defineProperty(this, "event_schema", {
293
+ value: event_schema,
294
+ writable: true,
295
+ enumerable: false,
296
+ configurable: true
297
+ });
298
+ Object.defineProperty(this, "_event_fields_set", {
299
+ value: explicit_event_fields,
300
+ writable: true,
301
+ enumerable: false,
302
+ configurable: true
303
+ });
175
304
  const parsed_path = parsed.event_path;
176
305
  this.event_path = Array.isArray(parsed_path) ? [...parsed_path] : [];
306
+ this.event_created_at = (0, import_helpers.monotonicDatetime)(parsed.event_created_at);
177
307
  this.event_results = hydrateEventResults(this, parsed.event_results);
178
308
  this.event_pending_bus_count = typeof parsed.event_pending_bus_count === "number" ? Math.max(0, Number(parsed.event_pending_bus_count)) : 0;
179
309
  const parsed_status = parsed.event_status;
@@ -182,7 +312,7 @@ class BaseEvent {
182
312
  this.event_completed_at = parsed.event_completed_at === null || parsed.event_completed_at === void 0 ? null : (0, import_helpers.monotonicDatetime)(parsed.event_completed_at);
183
313
  this.event_parent_id = typeof parsed.event_parent_id === "string" ? parsed.event_parent_id : null;
184
314
  this.event_emitted_by_handler_id = typeof parsed.event_emitted_by_handler_id === "string" ? parsed.event_emitted_by_handler_id : null;
185
- this.event_result_type = event_result_type;
315
+ this.event_result_type = (0, import_types.normalizeEventResultType)(parsed.event_result_type ?? event_result_type);
186
316
  this._event_completed_signal = null;
187
317
  this._lock_for_event_handler = null;
188
318
  this._event_dispatch_context = void 0;
@@ -191,23 +321,13 @@ class BaseEvent {
191
321
  toString() {
192
322
  return `${this.event_type}#${this.event_id.slice(-4)}`;
193
323
  }
194
- static extend(event_type, shape = {}) {
195
- const raw_shape = shape;
196
- assertNoReservedUserEventFields(raw_shape, `BaseEvent.extend(${event_type})`);
197
- assertNoUnknownEventPrefixedFields(raw_shape, `BaseEvent.extend(${event_type})`);
198
- assertNoModelPrefixedFields(raw_shape, `BaseEvent.extend(${event_type})`);
199
- const raw_event_result_type = raw_shape.event_result_type;
200
- const event_result_type = (0, import_types.normalizeEventResultType)(raw_event_result_type);
201
- const event_version = typeof raw_shape.event_version === "string" ? raw_shape.event_version : void 0;
202
- const event_defaults = Object.fromEntries(
203
- Object.entries(raw_shape).filter(
204
- ([key, value]) => key !== "event_result_type" && key !== "event_version" && !(value instanceof import_zod.z.ZodType)
205
- )
206
- );
207
- const zod_shape = (0, import_types.extractZodShape)(raw_shape);
208
- const full_schema = BaseEventSchema.extend(zod_shape);
324
+ static extend(event_type, shape) {
325
+ const built = buildFullEventSchema(event_type, shape ?? {});
326
+ const full_schema = built.event_schema;
327
+ const event_result_type = built.event_result_type;
328
+ const event_version = built.event_version;
209
329
  class ExtendedEvent extends BaseEvent {
210
- static schema = full_schema;
330
+ static event_schema = full_schema;
211
331
  static event_type = event_type;
212
332
  static event_version = event_version ?? BaseEvent.event_version;
213
333
  static event_result_type = event_result_type;
@@ -218,23 +338,36 @@ class BaseEvent {
218
338
  function EventFactory(data) {
219
339
  return new ExtendedEvent(data);
220
340
  }
221
- EventFactory.schema = full_schema;
341
+ EventFactory.event_schema = full_schema;
222
342
  EventFactory.event_type = event_type;
223
343
  EventFactory.event_version = event_version ?? BaseEvent.event_version;
224
344
  EventFactory.event_result_type = event_result_type;
225
345
  EventFactory.class = ExtendedEvent;
226
346
  EventFactory.fromJSON = (data) => ExtendedEvent.fromJSON(data);
227
347
  EventFactory.prototype = ExtendedEvent.prototype;
228
- EVENT_CLASS_DEFAULTS.set(ExtendedEvent, event_defaults);
348
+ EVENT_TYPE_REGISTRY.set(event_type, ExtendedEvent);
229
349
  return EventFactory;
230
350
  }
231
351
  static fromJSON(data) {
232
352
  if (!data || typeof data !== "object") {
233
- const schema = this.schema ?? BaseEventSchema;
234
- const parsed = schema.parse(data);
353
+ const event_schema = this.event_schema ?? BaseEventSchema;
354
+ const parsed = decodeEventSchema(event_schema, data);
235
355
  return new this(parsed);
236
356
  }
237
357
  const record = { ...data };
358
+ if (this === BaseEvent) {
359
+ const event_type = record.event_type;
360
+ if (typeof event_type === "string") {
361
+ const KnownEvent = EVENT_TYPE_REGISTRY.get(event_type);
362
+ if (KnownEvent) {
363
+ return KnownEvent.fromJSON(record);
364
+ }
365
+ }
366
+ }
367
+ const ctor = this;
368
+ if (this !== BaseEvent && ctor.event_result_type && record.event_result_type !== void 0) {
369
+ delete record.event_result_type;
370
+ }
238
371
  if (record.event_result_type !== void 0 && record.event_result_type !== null) {
239
372
  record.event_result_type = (0, import_types.normalizeEventResultType)(record.event_result_type);
240
373
  }
@@ -255,18 +388,47 @@ class BaseEvent {
255
388
  toJSON() {
256
389
  const record = {};
257
390
  for (const [key, value] of Object.entries(this)) {
258
- if (key.startsWith("_") || key === "bus" || key === "event_bus" || key === "event_results") continue;
391
+ if (key.startsWith("_") || key === "bus" || key === "event_bus" || key === "event_schema" || key === "event_results") continue;
259
392
  if (value === void 0 || typeof value === "function") continue;
260
393
  record[key] = value;
261
394
  }
262
395
  const event_results = Object.fromEntries(
263
396
  Array.from(this.event_results.entries()).map(([handler_id, result]) => [handler_id, result.toJSON()])
264
397
  );
265
- return {
398
+ const event_schema = this.constructor.event_schema ?? this.event_schema ?? BaseEventSchema;
399
+ const encoded = encodeEventSchema(event_schema, {
266
400
  ...record,
267
401
  event_id: this.event_id,
268
402
  event_type: this.event_type,
269
403
  event_version: this.event_version,
404
+ event_result_type: this.event_result_type,
405
+ // static configuration options
406
+ event_timeout: this.event_timeout,
407
+ event_slow_timeout: this.event_slow_timeout,
408
+ event_concurrency: this.event_concurrency,
409
+ event_handler_concurrency: this.event_handler_concurrency,
410
+ event_handler_completion: this.event_handler_completion,
411
+ event_handler_slow_timeout: this.event_handler_slow_timeout,
412
+ event_handler_timeout: this.event_handler_timeout,
413
+ event_blocks_parent_completion: this.event_blocks_parent_completion,
414
+ // mutable parent/child/bus tracking runtime state
415
+ event_parent_id: this.event_parent_id,
416
+ event_path: this.event_path,
417
+ event_emitted_by_handler_id: this.event_emitted_by_handler_id,
418
+ event_pending_bus_count: this.event_pending_bus_count,
419
+ // mutable runtime status and timestamps
420
+ event_status: this.event_status,
421
+ event_created_at: this.event_created_at,
422
+ event_started_at: this.event_started_at ?? null,
423
+ event_completed_at: this.event_completed_at ?? null,
424
+ ...Object.keys(event_results).length > 0 ? { event_results } : {}
425
+ });
426
+ delete encoded.event_schema;
427
+ return {
428
+ ...encoded,
429
+ event_id: this.event_id,
430
+ event_type: this.event_type,
431
+ event_version: this.event_version,
270
432
  event_result_type: this.event_result_type ? (0, import_types.toJsonSchema)(this.event_result_type) : this.event_result_type,
271
433
  // static configuration options
272
434
  event_timeout: this.event_timeout,
@@ -291,13 +453,12 @@ class BaseEvent {
291
453
  ...Object.keys(event_results).length > 0 ? { event_results } : {}
292
454
  };
293
455
  }
294
- _createSlowEventWarningTimer() {
295
- const event_slow_timeout = this.event_slow_timeout ?? this.event_bus?.event_slow_timeout ?? null;
296
- const event_warn_ms = event_slow_timeout === null ? null : event_slow_timeout * 1e3;
456
+ _createSlowEventWarningTimer(event_slow_timeout = this.event_slow_timeout ?? null, bus_name) {
457
+ const event_warn_ms = event_slow_timeout === null || event_slow_timeout <= 0 ? null : event_slow_timeout * 1e3;
297
458
  if (event_warn_ms === null) {
298
459
  return null;
299
460
  }
300
- const name = this.event_bus?.name ?? "EventBus";
461
+ const name = bus_name ?? this.event_bus?.name ?? "EventBus";
301
462
  return setTimeout(() => {
302
463
  if (this.event_status === "completed") {
303
464
  return;
@@ -383,7 +544,13 @@ class BaseEvent {
383
544
  return Array.from(original.event_results.values()).filter((result) => result.eventbus_id === this.event_bus.id);
384
545
  }
385
546
  _isFirstModeWinningResult(entry) {
386
- return entry.status === "completed" && entry.result !== void 0 && entry.result !== null && !(entry.result instanceof BaseEvent);
547
+ return BaseEvent._defaultResultInclude(entry.result, entry);
548
+ }
549
+ static _defaultResultInclude(result, event_result) {
550
+ return event_result.status === "completed" && result !== void 0 && result !== null && !(result instanceof Error) && !(result instanceof BaseEvent) && event_result.error === void 0;
551
+ }
552
+ static _includeEventResult(include, event_result) {
553
+ return include(event_result.result, event_result);
387
554
  }
388
555
  _markFirstModeWinnerIfNeeded(original, entry, first_state) {
389
556
  if (first_state.found || !this._isFirstModeWinningResult(entry)) {
@@ -396,9 +563,13 @@ class BaseEvent {
396
563
  if (!this.event_bus) {
397
564
  throw new Error("event has no bus attached");
398
565
  }
399
- await this.event_bus.locks._runWithHandlerLock(original, this.event_bus.event_handler_concurrency, async (handler_lock) => {
400
- await entry.runHandler(handler_lock);
401
- });
566
+ await this.event_bus.locks._runWithHandlerLock(
567
+ original,
568
+ original.event_handler_concurrency ?? this.event_bus.event_handler_concurrency,
569
+ async (handler_lock) => {
570
+ await entry.runHandler(handler_lock);
571
+ }
572
+ );
402
573
  }
403
574
  // Run all pending handler results for the current bus context.
404
575
  async _runHandlers(pending_entries) {
@@ -409,7 +580,7 @@ class BaseEvent {
409
580
  }
410
581
  const resolved_completion = original.event_handler_completion ?? this.event_bus?.event_handler_completion ?? "all";
411
582
  if (resolved_completion === "first") {
412
- if (original._getHandlerLock(this.event_bus?.event_handler_concurrency) !== null) {
583
+ if (original._getHandlerLock(original.event_handler_concurrency ?? this.event_bus?.event_handler_concurrency ?? "serial") !== null) {
413
584
  for (const entry of pending_results) {
414
585
  await this._runHandlerWithLock(original, entry);
415
586
  if (!this._isFirstModeWinningResult(entry)) {
@@ -436,7 +607,7 @@ class BaseEvent {
436
607
  }
437
608
  _getHandlerLock(default_concurrency) {
438
609
  const original = this._event_original ?? this;
439
- const resolved = original.event_handler_concurrency ?? default_concurrency ?? original.event_bus?.event_handler_concurrency ?? "serial";
610
+ const resolved = original.event_handler_concurrency ?? default_concurrency ?? "serial";
440
611
  if (resolved === "parallel") {
441
612
  return null;
442
613
  }
@@ -548,18 +719,18 @@ class BaseEvent {
548
719
  cancelChildEvent(child);
549
720
  }
550
721
  }
551
- // Cancel all handler results for an event except the winner, used by first() mode.
722
+ // Cancel all handler results for an event except the winner, used by event_handler_completion='first'.
552
723
  // Cancels pending handlers immediately, aborts started handlers via _signalAbort(),
553
724
  // and cancels any child events emitted by the losing handlers.
554
725
  _markRemainingFirstModeResultCancelled(winner) {
555
- const cause = new Error("first() resolved: another handler returned a result first");
726
+ const cause = new Error("event_handler_completion='first' resolved: another handler returned a result first");
556
727
  const bus_id = winner.eventbus_id;
557
728
  for (const result of this.event_results.values()) {
558
729
  if (result === winner) continue;
559
730
  if (result.eventbus_id !== bus_id) continue;
560
731
  if (result.status === "pending") {
561
732
  result._markError(
562
- new import_EventHandler.EventHandlerCancelledError(`Cancelled: first() resolved`, {
733
+ new import_EventHandler.EventHandlerCancelledError(`Cancelled: event_handler_completion='first' resolved`, {
563
734
  event_result: result,
564
735
  cause
565
736
  })
@@ -574,7 +745,7 @@ class BaseEvent {
574
745
  original_child._markCancelled(cause);
575
746
  }
576
747
  result._lock?.exitHandlerRun();
577
- const aborted_error = new import_EventHandler.EventHandlerAbortedError(`Aborted: first() resolved`, {
748
+ const aborted_error = new import_EventHandler.EventHandlerAbortedError(`Aborted: event_handler_completion='first' resolved`, {
578
749
  event_result: result,
579
750
  cause
580
751
  });
@@ -651,92 +822,41 @@ class BaseEvent {
651
822
  parent_id = parent.event_parent_id;
652
823
  }
653
824
  }
654
- // awaitable that triggers immediate (queue-jump) processing of the event on all buses where it is queued
655
- // use eventCompleted() to wait for normal queue-order completion without queue-jumping.
656
- done(options = {}) {
657
- if (!this.event_bus) {
658
- return Promise.reject(new Error("event has no bus attached"));
659
- }
825
+ _withEventResultMethods(promise) {
826
+ const chainable = promise;
827
+ chainable.eventResult = async (options) => {
828
+ const event = await promise;
829
+ return event.eventResult(options);
830
+ };
831
+ chainable.eventResultsList = async (options) => {
832
+ const event = await promise;
833
+ return event.eventResultsList(options);
834
+ };
835
+ return chainable;
836
+ }
837
+ _timeoutPromise(timeout, message, fn) {
838
+ return timeout === null || timeout <= 0 ? fn() : (0, import_timing._runWithTimeout)(timeout, () => new Error(message()), fn);
839
+ }
840
+ _orderedEventResults() {
660
841
  const original = this._event_original ?? this;
661
- original._markBlocksParentCompletionIfAwaitedFromEmittingHandler();
662
- const raise_if_any = options.raise_if_any ?? true;
663
- const completion_promise = this.event_status === "completed" ? Promise.resolve(original) : this.event_bus._processEventImmediately(this);
664
- if (!raise_if_any) {
665
- return completion_promise;
666
- }
667
- return completion_promise.then((completed_event) => {
668
- const first_error = completed_event._firstProcessingError();
669
- if (first_error !== void 0) {
670
- if (first_error instanceof Error) {
671
- throw first_error;
672
- }
673
- throw new Error(String(first_error));
674
- }
675
- return completed_event;
676
- });
842
+ return Array.from(original.event_results.values()).sort(
843
+ (a, b) => compareIsoDatetime(a.completed_at, b.completed_at)
844
+ );
677
845
  }
678
- // returns the first non-undefined handler result value, cancelling remaining handlers
679
- // when any handler completes. Works with all event_handler_concurrency modes:
680
- // parallel: races all handlers, returns first non-undefined, aborts the rest
681
- // serial: runs handlers sequentially, returns first non-undefined, skips remaining
682
- first() {
683
- if (!this.event_bus) {
684
- return Promise.reject(new Error("event has no bus attached"));
685
- }
846
+ _orderedEventResultsByRegistration() {
686
847
  const original = this._event_original ?? this;
687
- original.event_handler_completion = "first";
688
- return this.done({ raise_if_any: false }).then((completed_event) => {
689
- const first_error = completed_event._firstProcessingError({ ignore_first_mode_control_errors: true });
690
- if (first_error !== void 0) {
691
- if (first_error instanceof Error) {
692
- throw first_error;
693
- }
694
- throw new Error(String(first_error));
695
- }
696
- const orig = completed_event._event_original ?? completed_event;
697
- return Array.from(orig.event_results.values()).filter(
698
- (result) => result.status === "completed" && result.result !== void 0 && result.result !== null && !(result.result instanceof BaseEvent)
699
- ).sort((a, b) => compareIsoDatetime(a.completed_at, b.completed_at)).map((result) => result.result).at(0);
700
- });
848
+ return Array.from(original.event_results.values()).sort(
849
+ (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)
850
+ );
701
851
  }
702
- async eventResultsList(include_or_options, maybe_options) {
703
- 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;
704
- let options;
705
- let include;
706
- if (typeof include_or_options === "function") {
707
- options = maybe_options ?? {};
708
- include = include_or_options;
709
- } else {
710
- options = include_or_options ?? {};
711
- include = options.include ?? default_include;
712
- }
852
+ _collectResultValues(options = {}, order = "completion") {
853
+ const include = options.include ?? BaseEvent._defaultResultInclude;
713
854
  const raise_if_any = options.raise_if_any ?? true;
714
- const raise_if_none = options.raise_if_none ?? true;
715
- const original = this._event_original ?? this;
716
- const resolved_timeout_seconds = options.timeout ?? original.event_timeout ?? this.event_bus?.event_timeout ?? null;
717
- let completed_event;
718
- if (resolved_timeout_seconds === null) {
719
- completed_event = await this.done({ raise_if_any: false });
720
- } else {
721
- completed_event = await (0, import_timing._runWithTimeout)(
722
- resolved_timeout_seconds,
723
- () => new Error(`Timed out waiting for ${original.event_type} results after ${resolved_timeout_seconds}s`),
724
- () => this.done({ raise_if_any: false })
725
- );
726
- }
727
- const all_results = Array.from(completed_event.event_results.values());
855
+ const raise_if_none = options.raise_if_none ?? false;
856
+ const all_results = order === "registration" ? this._orderedEventResultsByRegistration() : this._orderedEventResults();
728
857
  const error_results = all_results.filter((event_result) => event_result.error !== void 0 || event_result.result instanceof Error);
729
- if (raise_if_any && error_results.length > 0) {
730
- if (error_results.length === 1) {
731
- const first_error = error_results[0];
732
- if (first_error.error instanceof Error) {
733
- throw first_error.error;
734
- }
735
- if (first_error.result instanceof Error) {
736
- throw first_error.result;
737
- }
738
- throw new Error(String(first_error.error ?? first_error.result));
739
- }
858
+ const included_results = all_results.filter((event_result) => BaseEvent._includeEventResult(include, event_result));
859
+ if (error_results.length > 0 && raise_if_any) {
740
860
  const errors = error_results.map((event_result) => {
741
861
  if (event_result.error instanceof Error) {
742
862
  return event_result.error;
@@ -746,28 +866,104 @@ class BaseEvent {
746
866
  }
747
867
  return new Error(String(event_result.error ?? event_result.result));
748
868
  });
749
- throw new AggregateError(
750
- errors,
751
- `Event ${completed_event.event_type}#${completed_event.event_id.slice(-4)} had ${errors.length} handler error(s)`
752
- );
869
+ if (errors.length === 1) {
870
+ throw errors[0];
871
+ }
872
+ throw new AggregateError(errors, `Event ${this.event_type}#${this.event_id.slice(-4)} had ${errors.length} handler error(s)`);
753
873
  }
754
- const included_results = all_results.filter((event_result) => include(event_result.result, event_result));
755
874
  if (raise_if_none && included_results.length === 0) {
756
875
  throw new Error(
757
- `Expected at least one handler to return a non-null result, but none did: ${completed_event.event_type}#${completed_event.event_id.slice(-4)}`
876
+ `Expected at least one handler to return a non-null result, but none did: ${this.event_type}#${this.event_id.slice(-4)}`
758
877
  );
759
878
  }
760
879
  return included_results.map((event_result) => event_result.result);
761
880
  }
762
- // awaitable that waits for the event to be processed in normal queue order by the _runloop
763
- eventCompleted() {
881
+ _hasIncludedResult(options = {}) {
882
+ const include = options.include ?? BaseEvent._defaultResultInclude;
883
+ return this._orderedEventResults().some((event_result) => BaseEvent._includeEventResult(include, event_result));
884
+ }
885
+ async _waitForFirstResultOrCompletion(options = {}) {
764
886
  const original = this._event_original ?? this;
887
+ if (options.timeout !== void 0 && options.timeout !== null && options.timeout < 0) {
888
+ throw new Error("timeout must be >= 0 or null");
889
+ }
890
+ if (!this.event_bus && original.event_status !== "completed") {
891
+ throw new Error("event has no bus attached");
892
+ }
893
+ if (original.event_status === "completed" || this._hasIncludedResult(options)) {
894
+ return this;
895
+ }
896
+ const waitForResult = async () => {
897
+ for (; ; ) {
898
+ if (original.event_status === "completed" || this._hasIncludedResult(options)) {
899
+ return this;
900
+ }
901
+ await new Promise((resolve) => setTimeout(resolve, 1));
902
+ }
903
+ };
904
+ const timeout = options.timeout ?? null;
905
+ return this._timeoutPromise(timeout, () => `Timed out waiting for ${original.event_type} result after ${timeout}s`, waitForResult);
906
+ }
907
+ // Active awaitable that triggers immediate (queue-jump) processing of the event on all buses where it is queued.
908
+ now(options = {}) {
909
+ const original = this._event_original ?? this;
910
+ if (options.timeout !== void 0 && options.timeout !== null && options.timeout < 0) {
911
+ return this._withEventResultMethods(Promise.reject(new Error("timeout must be >= 0 or null")));
912
+ }
913
+ if (!this.event_bus && original.event_status !== "completed") {
914
+ return this._withEventResultMethods(Promise.reject(new Error("event has no bus attached")));
915
+ }
765
916
  original._markBlocksParentCompletionIfAwaitedFromEmittingHandler();
766
- if (this.event_status === "completed") {
767
- return Promise.resolve(this);
917
+ const resolved_timeout_seconds = options.timeout ?? null;
918
+ const processing = original.event_status === "completed" ? Promise.resolve(this) : this._timeoutPromise(
919
+ resolved_timeout_seconds,
920
+ () => `Timed out waiting for ${original.event_type} completion after ${resolved_timeout_seconds}s`,
921
+ () => this.event_bus._processEventImmediately(this)
922
+ );
923
+ if (options.first_result) {
924
+ void processing.catch(() => void 0);
925
+ return this._withEventResultMethods(this._waitForFirstResultOrCompletion(options));
926
+ }
927
+ return this._withEventResultMethods(processing);
928
+ }
929
+ // Passive awaitable that waits for normal queue-order processing without forcing execution.
930
+ wait(options = {}) {
931
+ const original = this._event_original ?? this;
932
+ if (options.timeout !== void 0 && options.timeout !== null && options.timeout < 0) {
933
+ return this._withEventResultMethods(Promise.reject(new Error("timeout must be >= 0 or null")));
934
+ }
935
+ if (!this.event_bus && original.event_status !== "completed") {
936
+ return this._withEventResultMethods(Promise.reject(new Error("event has no bus attached")));
937
+ }
938
+ if (options.first_result) {
939
+ return this._withEventResultMethods(this._waitForFirstResultOrCompletion(options));
940
+ }
941
+ if (original.event_status === "completed") {
942
+ return this._withEventResultMethods(Promise.resolve(this));
768
943
  }
769
944
  this._notifyDoneListeners();
770
- return this._event_completed_signal.promise;
945
+ const timeout = options.timeout ?? null;
946
+ return this._withEventResultMethods(
947
+ this._timeoutPromise(
948
+ timeout,
949
+ () => `Timed out waiting for ${original.event_type} completion after ${timeout}s`,
950
+ () => this._event_completed_signal.promise.then(() => this)
951
+ )
952
+ );
953
+ }
954
+ async eventResult(options = {}) {
955
+ const original = this._event_original ?? this;
956
+ if (original.event_status === "pending" && original.event_results.size === 0) {
957
+ await this.now({ first_result: true });
958
+ }
959
+ return this._collectResultValues(options, "registration").at(0);
960
+ }
961
+ async eventResultsList(options = {}) {
962
+ const original = this._event_original ?? this;
963
+ if (original.event_status === "pending" && original.event_results.size === 0) {
964
+ await this.now({ first_result: false });
965
+ }
966
+ return this._collectResultValues(options, "registration");
771
967
  }
772
968
  _markBlocksParentCompletionIfAwaitedFromEmittingHandler() {
773
969
  const original = this._event_original ?? this;
@@ -861,23 +1057,8 @@ class BaseEvent {
861
1057
  get event_errors() {
862
1058
  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);
863
1059
  }
864
- _isFirstModeControlError(error) {
865
- if (!(error instanceof import_EventHandler.EventHandlerCancelledError || error instanceof import_EventHandler.EventHandlerAbortedError)) {
866
- return false;
867
- }
868
- if (error.message.includes("first() resolved")) {
869
- return true;
870
- }
871
- return error.cause instanceof Error && error.cause.message.includes("first() resolved");
872
- }
873
- _firstProcessingError(options = {}) {
874
- const ignore_first_mode_control_errors = options.ignore_first_mode_control_errors ?? false;
875
- 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);
876
- }
877
- // Returns the first non-undefined completed handler result, sorted by completion time.
878
- // Useful after first() or done() to get the winning result value.
879
- get event_result() {
880
- 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);
1060
+ _firstProcessingError() {
1061
+ 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);
881
1062
  }
882
1063
  _areAllChildrenComplete(visited = /* @__PURE__ */ new Set()) {
883
1064
  const original = this._event_original ?? this;