abxbus 2.5.4 → 2.5.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/BaseEvent.d.ts +53 -28
- package/dist/cjs/BaseEvent.js +148 -28
- package/dist/cjs/BaseEvent.js.map +2 -2
- package/dist/cjs/LockManager.js +1 -1
- package/dist/cjs/LockManager.js.map +2 -2
- package/dist/cjs/events_suck.d.ts +8 -15
- package/dist/cjs/events_suck.js +1 -1
- package/dist/cjs/events_suck.js.map +2 -2
- package/dist/cjs/jsonschema.d.ts +6 -0
- package/dist/cjs/jsonschema.js +155 -0
- package/dist/cjs/jsonschema.js.map +7 -0
- package/dist/cjs/retry.d.ts +2 -0
- package/dist/cjs/retry.js +110 -35
- package/dist/cjs/retry.js.map +3 -3
- package/dist/cjs/types.d.ts +6 -10
- package/dist/cjs/types.js +9 -16
- package/dist/cjs/types.js.map +2 -2
- package/dist/esm/BaseEvent.js +148 -28
- package/dist/esm/BaseEvent.js.map +2 -2
- package/dist/esm/LockManager.js +1 -1
- package/dist/esm/LockManager.js.map +2 -2
- package/dist/esm/events_suck.js +1 -1
- package/dist/esm/events_suck.js.map +2 -2
- package/dist/esm/jsonschema.js +135 -0
- package/dist/esm/jsonschema.js.map +7 -0
- package/dist/esm/retry.js +110 -35
- package/dist/esm/retry.js.map +3 -3
- package/dist/esm/types.js +8 -15
- package/dist/esm/types.js.map +2 -2
- package/dist/types/BaseEvent.d.ts +53 -28
- package/dist/types/events_suck.d.ts +8 -15
- package/dist/types/jsonschema.d.ts +6 -0
- package/dist/types/retry.d.ts +2 -0
- package/dist/types/types.d.ts +6 -10
- package/package.json +1 -1
- package/src/BaseEvent.ts +321 -80
- package/src/LockManager.ts +1 -1
- package/src/events_suck.ts +20 -22
- package/src/jsonschema.ts +146 -0
- package/src/retry.ts +132 -38
- package/src/types.ts +10 -19
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export type JsonSchema = boolean | z.core.JSONSchema.JSONSchema
|
|
4
|
+
type JsonSchemaObject = z.core.JSONSchema.JSONSchema
|
|
5
|
+
|
|
6
|
+
const isJsonSchemaObject = (value: unknown): value is JsonSchemaObject => {
|
|
7
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const isJsonSchema = (value: unknown): value is JsonSchema => {
|
|
11
|
+
if (typeof value === 'boolean') {
|
|
12
|
+
return true
|
|
13
|
+
}
|
|
14
|
+
if (!isJsonSchemaObject(value)) {
|
|
15
|
+
return false
|
|
16
|
+
}
|
|
17
|
+
if ('_zod' in value || '_def' in value || '~standard' in value) {
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
return true
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const nullUnionCandidates = (schema: Record<string, unknown>): Record<string, unknown>[] | null => {
|
|
24
|
+
if (Array.isArray(schema.type) && schema.type.includes('null')) {
|
|
25
|
+
const non_null_types = schema.type.filter((item): item is string => typeof item === 'string' && item !== 'null')
|
|
26
|
+
if (non_null_types.length > 0) {
|
|
27
|
+
return [{ type: non_null_types.length === 1 ? non_null_types[0] : non_null_types }, { type: 'null' }]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const normalizeJsonSchema = (schema: JsonSchema): JsonSchema => {
|
|
35
|
+
const normalized = normalizeJsonSchemaValue(schema)
|
|
36
|
+
if (!isJsonSchemaObject(normalized)) {
|
|
37
|
+
return normalized as JsonSchema
|
|
38
|
+
}
|
|
39
|
+
const schema_record = { ...normalized } as JsonSchemaObject
|
|
40
|
+
const definitions = schema_record.$defs
|
|
41
|
+
const root_ref = rootRefForSchema(schema_record, definitions)
|
|
42
|
+
if (!root_ref || !definitions) {
|
|
43
|
+
schema_record.$schema ??= 'https://json-schema.org/draft/2020-12/schema'
|
|
44
|
+
return schema_record
|
|
45
|
+
}
|
|
46
|
+
const root_name = root_ref.slice('#/$defs/'.length)
|
|
47
|
+
const root_schema = definitions[root_name]
|
|
48
|
+
if (!isJsonSchemaObject(root_schema)) {
|
|
49
|
+
schema_record.$schema ??= 'https://json-schema.org/draft/2020-12/schema'
|
|
50
|
+
return schema_record
|
|
51
|
+
}
|
|
52
|
+
const rewritten_root = rewriteJsonSchemaRefs(root_schema, { [root_ref]: '#' }) as JsonSchemaObject
|
|
53
|
+
const remaining_defs = Object.fromEntries(Object.entries(definitions).filter(([name]) => name !== root_name))
|
|
54
|
+
if (Object.keys(remaining_defs).length > 0) {
|
|
55
|
+
rewritten_root.$defs = rewriteJsonSchemaRefs(remaining_defs, { [root_ref]: '#' }) as Record<string, JsonSchemaObject>
|
|
56
|
+
}
|
|
57
|
+
rewritten_root.$schema ??= schema_record.$schema ?? 'https://json-schema.org/draft/2020-12/schema'
|
|
58
|
+
setTitleFromInlinedRootDefinition(rewritten_root, root_name)
|
|
59
|
+
return rewritten_root
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const rootRefForSchema = (schema: JsonSchemaObject, definitions: Record<string, JsonSchemaObject> | undefined): string | null => {
|
|
63
|
+
if (typeof schema.$ref === 'string' && schema.$ref.startsWith('#/$defs/')) {
|
|
64
|
+
return schema.$ref
|
|
65
|
+
}
|
|
66
|
+
if (!definitions) {
|
|
67
|
+
return null
|
|
68
|
+
}
|
|
69
|
+
const root = schemaWithoutSchemaAndDefinitions(schema)
|
|
70
|
+
for (const [name, definition] of Object.entries(definitions)) {
|
|
71
|
+
if (JSON.stringify(definition) === JSON.stringify(root)) {
|
|
72
|
+
return `#/$defs/${name}`
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const schemaWithoutSchemaAndDefinitions = (schema: JsonSchemaObject): Record<string, unknown> => {
|
|
79
|
+
const root: Record<string, unknown> = {}
|
|
80
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
81
|
+
if (key !== '$schema' && key !== '$defs') {
|
|
82
|
+
root[key] = value
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return root
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const setTitleFromInlinedRootDefinition = (schema: JsonSchemaObject, root_name: string): void => {
|
|
89
|
+
if (root_name.startsWith('__schema')) return
|
|
90
|
+
schema.title ??= root_name
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const rewriteJsonSchemaRefs = (schema: unknown, refs: Record<string, string>): unknown => {
|
|
94
|
+
if (Array.isArray(schema)) {
|
|
95
|
+
return schema.map((item) => rewriteJsonSchemaRefs(item, refs))
|
|
96
|
+
}
|
|
97
|
+
if (!isJsonSchemaObject(schema)) {
|
|
98
|
+
return schema
|
|
99
|
+
}
|
|
100
|
+
const rewritten: Record<string, unknown> = {}
|
|
101
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
102
|
+
rewritten[key] = rewriteJsonSchemaRefs(value, refs)
|
|
103
|
+
}
|
|
104
|
+
if (typeof rewritten.$ref === 'string' && rewritten.$ref in refs) {
|
|
105
|
+
rewritten.$ref = refs[rewritten.$ref]
|
|
106
|
+
}
|
|
107
|
+
return rewritten
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const normalizeJsonSchemaValue = (schema: unknown): unknown => {
|
|
111
|
+
if (Array.isArray(schema)) {
|
|
112
|
+
return schema.map((item) => normalizeJsonSchemaValue(item))
|
|
113
|
+
}
|
|
114
|
+
if (!isJsonSchemaObject(schema)) {
|
|
115
|
+
return schema
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const normalized: Record<string, unknown> = {}
|
|
119
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
120
|
+
normalized[key] = normalizeJsonSchemaValue(value)
|
|
121
|
+
}
|
|
122
|
+
if (Array.isArray(normalized.required) && normalized.required.every((item) => typeof item === 'string')) {
|
|
123
|
+
normalized.required = [...normalized.required].sort()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const null_union_candidates = nullUnionCandidates(normalized)
|
|
127
|
+
if (null_union_candidates !== null) {
|
|
128
|
+
const merged: Record<string, unknown> = { anyOf: normalizeJsonSchemaValue(null_union_candidates) }
|
|
129
|
+
for (const [key, value] of Object.entries(normalized)) {
|
|
130
|
+
if (key !== 'type') {
|
|
131
|
+
merged[key] = value
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return merged
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return normalized
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export const toJsonSchema = (schema: z.core.$ZodType): JsonSchema => {
|
|
141
|
+
return normalizeJsonSchema(z.toJSONSchema(schema) as JsonSchema)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const fromJsonSchema = (schema: JsonSchema): z.ZodTypeAny => {
|
|
145
|
+
return z.fromJSONSchema(schema)
|
|
146
|
+
}
|
package/src/retry.ts
CHANGED
|
@@ -21,6 +21,8 @@ type RetryDecorator = {
|
|
|
21
21
|
|
|
22
22
|
const MULTIPROCESS_SEMAPHORE_DIRNAME = 'browser_use_semaphores'
|
|
23
23
|
const MULTIPROCESS_STALE_LOCK_MS = 5 * 60 * 1000
|
|
24
|
+
const RETRY_SLOW_WARNING_THROTTLE_MS = 2000
|
|
25
|
+
const RETRY_SLOW_WARNING_ARGS_MAX_LENGTH = 80
|
|
24
26
|
|
|
25
27
|
let multiprocess_fallback_reason_logged: string | null = null
|
|
26
28
|
|
|
@@ -44,6 +46,9 @@ export interface RetryOptions {
|
|
|
44
46
|
/** Per-attempt timeout in seconds. Default: undefined (no per-attempt timeout) */
|
|
45
47
|
timeout?: number | null
|
|
46
48
|
|
|
49
|
+
/** Emit a warning when a decorated call exceeds this many seconds. Default: undefined (disabled) */
|
|
50
|
+
slow_timeout?: number | null
|
|
51
|
+
|
|
47
52
|
/** Maximum concurrent executions sharing this semaphore. Default: undefined (no concurrency limit) */
|
|
48
53
|
semaphore_limit?: number | null
|
|
49
54
|
|
|
@@ -238,6 +243,7 @@ export function retry(options: RetryOptions = {}): RetryDecorator {
|
|
|
238
243
|
retry_backoff_factor = 1.0,
|
|
239
244
|
retry_on_errors,
|
|
240
245
|
timeout,
|
|
246
|
+
slow_timeout,
|
|
241
247
|
semaphore_limit,
|
|
242
248
|
semaphore_name: semaphore_name_option,
|
|
243
249
|
semaphore_lax = true,
|
|
@@ -245,10 +251,13 @@ export function retry(options: RetryOptions = {}): RetryDecorator {
|
|
|
245
251
|
semaphore_timeout,
|
|
246
252
|
} = options
|
|
247
253
|
|
|
248
|
-
const decorateFunction = <T extends AnyFunction>(target: T, _context?: ClassMethodDecoratorContext): T => {
|
|
249
|
-
const
|
|
254
|
+
const decorateFunction = <T extends AnyFunction>(target: T, _context?: ClassMethodDecoratorContext, owner_name?: string | null): T => {
|
|
255
|
+
const base_fn_name = target.name || (_context?.name as string) || 'anonymous'
|
|
256
|
+
let fn_name = owner_name ? `${owner_name}.${base_fn_name}` : base_fn_name
|
|
250
257
|
const effective_max_attempts = Math.max(1, max_attempts)
|
|
251
258
|
const effective_retry_after = Math.max(0, retry_after)
|
|
259
|
+
const effective_slow_timeout_ms = slow_timeout != null && slow_timeout > 0 ? slow_timeout * 1000 : null
|
|
260
|
+
let last_slow_warning_at = 0
|
|
252
261
|
|
|
253
262
|
const shouldRetry = (error: unknown): boolean => {
|
|
254
263
|
if (!retry_on_errors || retry_on_errors.length === 0) return true
|
|
@@ -275,6 +284,14 @@ export function retry(options: RetryOptions = {}): RetryDecorator {
|
|
|
275
284
|
}
|
|
276
285
|
}
|
|
277
286
|
|
|
287
|
+
const emitSlowWarningIfDue = (args: any[], start_time: number): void => {
|
|
288
|
+
if (effective_slow_timeout_ms == null) return
|
|
289
|
+
const now = Date.now()
|
|
290
|
+
if (now - last_slow_warning_at < RETRY_SLOW_WARNING_THROTTLE_MS) return
|
|
291
|
+
last_slow_warning_at = now
|
|
292
|
+
console.warn(`Warning: ${fn_name}(${formatRetrySlowWarningArgs(args)}) slow (${((now - start_time) / 1000).toFixed(1)}s)`)
|
|
293
|
+
}
|
|
294
|
+
|
|
278
295
|
const runRetryLoopFromThenable = async (
|
|
279
296
|
this_arg: any,
|
|
280
297
|
args: any[],
|
|
@@ -373,26 +390,45 @@ export function retry(options: RetryOptions = {}): RetryDecorator {
|
|
|
373
390
|
|
|
374
391
|
// ── Retry loop (runs inside the semaphore and re-entrancy context) ──
|
|
375
392
|
const runRetryLoop = async (): Promise<any> => {
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
393
|
+
const call_started_at = Date.now()
|
|
394
|
+
const warning_args = [...args]
|
|
395
|
+
const slow_warning_timer =
|
|
396
|
+
effective_slow_timeout_ms == null
|
|
397
|
+
? null
|
|
398
|
+
: setTimeout(() => emitSlowWarningIfDue(warning_args, call_started_at), effective_slow_timeout_ms)
|
|
399
|
+
const finishSlowWarning = (): void => {
|
|
400
|
+
if (slow_warning_timer !== null) {
|
|
401
|
+
clearTimeout(slow_warning_timer)
|
|
402
|
+
}
|
|
403
|
+
if (effective_slow_timeout_ms != null && Date.now() - call_started_at >= effective_slow_timeout_ms) {
|
|
404
|
+
emitSlowWarningIfDue(warning_args, call_started_at)
|
|
405
|
+
}
|
|
406
|
+
}
|
|
385
407
|
|
|
386
|
-
|
|
387
|
-
|
|
408
|
+
try {
|
|
409
|
+
for (let attempt = 1; attempt <= effective_max_attempts; attempt++) {
|
|
410
|
+
try {
|
|
411
|
+
if (timeout != null && timeout > 0) {
|
|
412
|
+
return await _runWithTimeout(() => Promise.resolve(target.apply(this, args)), timeout * 1000, attempt)
|
|
413
|
+
} else {
|
|
414
|
+
return await Promise.resolve(target.apply(this, args))
|
|
415
|
+
}
|
|
416
|
+
} catch (error) {
|
|
417
|
+
if (!shouldRetry(error)) throw error
|
|
418
|
+
|
|
419
|
+
// Last attempt: rethrow
|
|
420
|
+
if (attempt >= effective_max_attempts) throw error
|
|
388
421
|
|
|
389
|
-
|
|
390
|
-
|
|
422
|
+
// Wait before next attempt with exponential backoff
|
|
423
|
+
await asyncRetryDelay(attempt)
|
|
424
|
+
}
|
|
391
425
|
}
|
|
392
|
-
}
|
|
393
426
|
|
|
394
|
-
|
|
395
|
-
|
|
427
|
+
// Unreachable, but satisfies the type checker
|
|
428
|
+
throw new Error(`retry(${fn_name}): unexpected end of retry loop`)
|
|
429
|
+
} finally {
|
|
430
|
+
finishSlowWarning()
|
|
431
|
+
}
|
|
396
432
|
}
|
|
397
433
|
|
|
398
434
|
try {
|
|
@@ -452,27 +488,52 @@ export function retry(options: RetryOptions = {}): RetryDecorator {
|
|
|
452
488
|
}
|
|
453
489
|
|
|
454
490
|
const runRetryLoop = (): any => {
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
491
|
+
const call_started_at = Date.now()
|
|
492
|
+
const warning_args = [...args]
|
|
493
|
+
const slow_warning_timer =
|
|
494
|
+
effective_slow_timeout_ms == null
|
|
495
|
+
? null
|
|
496
|
+
: setTimeout(() => emitSlowWarningIfDue(warning_args, call_started_at), effective_slow_timeout_ms)
|
|
497
|
+
const finishSlowWarning = (): void => {
|
|
498
|
+
if (slow_warning_timer !== null) {
|
|
499
|
+
clearTimeout(slow_warning_timer)
|
|
500
|
+
}
|
|
501
|
+
if (effective_slow_timeout_ms != null && Date.now() - call_started_at >= effective_slow_timeout_ms) {
|
|
502
|
+
emitSlowWarningIfDue(warning_args, call_started_at)
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
let finish_on_return = true
|
|
507
|
+
try {
|
|
508
|
+
for (let attempt = 1; attempt <= effective_max_attempts; attempt++) {
|
|
509
|
+
const attempt_started_at = Date.now()
|
|
510
|
+
try {
|
|
511
|
+
const result = target.apply(this, args)
|
|
512
|
+
if (isThenable(result)) {
|
|
513
|
+
finish_on_return = false
|
|
514
|
+
return runRetryLoopFromThenable(this, args, result, attempt).finally(finishSlowWarning)
|
|
515
|
+
}
|
|
516
|
+
if (timeout != null && timeout > 0 && Date.now() - attempt_started_at > timeout * 1000) {
|
|
517
|
+
throw new RetryTimeoutError(`Timed out after ${timeout}s (attempt ${attempt})`, {
|
|
518
|
+
timeout_seconds: timeout,
|
|
519
|
+
attempt,
|
|
520
|
+
})
|
|
521
|
+
}
|
|
522
|
+
return result
|
|
523
|
+
} catch (error) {
|
|
524
|
+
if (!shouldRetry(error)) throw error
|
|
525
|
+
if (attempt >= effective_max_attempts) {
|
|
526
|
+
throw error
|
|
527
|
+
}
|
|
528
|
+
syncRetryDelay(attempt)
|
|
467
529
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
530
|
+
}
|
|
531
|
+
throw new Error(`retry(${fn_name}): unexpected end of retry loop`)
|
|
532
|
+
} finally {
|
|
533
|
+
if (finish_on_return) {
|
|
534
|
+
finishSlowWarning()
|
|
473
535
|
}
|
|
474
536
|
}
|
|
475
|
-
throw new Error(`retry(${fn_name}): unexpected end of retry loop`)
|
|
476
537
|
}
|
|
477
538
|
|
|
478
539
|
try {
|
|
@@ -494,7 +555,8 @@ export function retry(options: RetryOptions = {}): RetryDecorator {
|
|
|
494
555
|
_context.addInitializer(function (this: unknown) {
|
|
495
556
|
const owner_name = findDecoratedMethodOwnerName(this, _context, retryWrapper)
|
|
496
557
|
if (owner_name) {
|
|
497
|
-
|
|
558
|
+
fn_name = `${owner_name}.${target.name || (_context.name as string) || 'anonymous'}`
|
|
559
|
+
Object.defineProperty(retryWrapper, 'name', { value: fn_name, configurable: true })
|
|
498
560
|
}
|
|
499
561
|
})
|
|
500
562
|
}
|
|
@@ -507,7 +569,13 @@ export function retry(options: RetryOptions = {}): RetryDecorator {
|
|
|
507
569
|
descriptor?: LegacyMethodDescriptor
|
|
508
570
|
): T | LegacyMethodDescriptor {
|
|
509
571
|
if (descriptor?.value && typeof descriptor.value === 'function') {
|
|
510
|
-
|
|
572
|
+
const owner_name =
|
|
573
|
+
target && (typeof target === 'object' || typeof target === 'function')
|
|
574
|
+
? typeof target === 'function'
|
|
575
|
+
? target.name
|
|
576
|
+
: (target as { constructor?: { name?: string } }).constructor?.name
|
|
577
|
+
: null
|
|
578
|
+
descriptor.value = decorateFunction(descriptor.value, undefined, owner_name)
|
|
511
579
|
return descriptor
|
|
512
580
|
}
|
|
513
581
|
if (typeof target === 'function') {
|
|
@@ -569,6 +637,32 @@ function isThenable(value: unknown): value is PromiseLike<unknown> {
|
|
|
569
637
|
)
|
|
570
638
|
}
|
|
571
639
|
|
|
640
|
+
function formatRetrySlowWarningArgs(args: any[]): string {
|
|
641
|
+
const preview = args.map(formatRetrySlowWarningValue).join(', ')
|
|
642
|
+
if (preview.length > RETRY_SLOW_WARNING_ARGS_MAX_LENGTH) {
|
|
643
|
+
return `${preview.slice(0, RETRY_SLOW_WARNING_ARGS_MAX_LENGTH - 3).replace(/,?\s*$/, '')}...`
|
|
644
|
+
}
|
|
645
|
+
return preview
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function formatRetrySlowWarningValue(value: unknown): string {
|
|
649
|
+
let text: string
|
|
650
|
+
if (typeof value === 'string') {
|
|
651
|
+
text = value
|
|
652
|
+
} else if (value === null || value === undefined) {
|
|
653
|
+
text = String(value)
|
|
654
|
+
} else if (typeof value === 'object') {
|
|
655
|
+
try {
|
|
656
|
+
text = JSON.stringify(value)
|
|
657
|
+
} catch {
|
|
658
|
+
text = String(value)
|
|
659
|
+
}
|
|
660
|
+
} else {
|
|
661
|
+
text = String(value)
|
|
662
|
+
}
|
|
663
|
+
return text.replace(/['"]/g, '').slice(0, 3)
|
|
664
|
+
}
|
|
665
|
+
|
|
572
666
|
/**
|
|
573
667
|
* Try to acquire a semaphore within a timeout. Returns true if acquired, false if timed out.
|
|
574
668
|
* If the semaphore is acquired after the timeout (due to the waiter remaining queued),
|
package/src/types.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
|
-
import type { BaseEvent } from './BaseEvent.js'
|
|
2
|
+
import type { BaseEvent, EventClass as BaseEventClass } from './BaseEvent.js'
|
|
3
|
+
import { fromJsonSchema, isJsonSchema, type JsonSchema } from './jsonschema.js'
|
|
3
4
|
|
|
4
5
|
export type EventStatus = 'pending' | 'started' | 'completed'
|
|
6
|
+
export type { EventClass } from './BaseEvent.js'
|
|
5
7
|
|
|
6
|
-
export type
|
|
7
|
-
|
|
8
|
-
export type EventPattern<T extends BaseEvent = BaseEvent> = string | EventClass<T>
|
|
8
|
+
export type EventPattern<T extends BaseEvent = BaseEvent> = string | BaseEventClass<T>
|
|
9
9
|
|
|
10
10
|
export type EventWithResultSchema<TResult> = BaseEvent & { __event_result_type__?: TResult }
|
|
11
11
|
|
|
@@ -13,7 +13,7 @@ export type EventResultType<TEvent extends BaseEvent> = TEvent extends { __event
|
|
|
13
13
|
|
|
14
14
|
export type EventResultTypeConstructor = StringConstructor | NumberConstructor | BooleanConstructor | ArrayConstructor | ObjectConstructor
|
|
15
15
|
|
|
16
|
-
export type EventResultTypeInput = z.ZodTypeAny | EventResultTypeConstructor |
|
|
16
|
+
export type EventResultTypeInput = z.ZodTypeAny | EventResultTypeConstructor | JsonSchema
|
|
17
17
|
|
|
18
18
|
export type EventHandlerReturn<T extends BaseEvent = BaseEvent> = EventResultType<T> | BaseEvent | null | void
|
|
19
19
|
|
|
@@ -74,7 +74,7 @@ export const normalizeEventPattern = (event_pattern: EventPattern | '*'): string
|
|
|
74
74
|
} catch {
|
|
75
75
|
preview = String(event_pattern).slice(0, 30)
|
|
76
76
|
}
|
|
77
|
-
throw new Error('bus.on(match_pattern, ...) must be a string event type, "*", or a BaseEvent class, got: ' + preview)
|
|
77
|
+
throw new Error('bus.on(match_pattern, ...) must be a string event type, "*", or a BaseEvent.extend() event class, got: ' + preview)
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
export const isZodSchema = (value: unknown): value is z.ZodTypeAny => !!value && typeof (value as z.ZodTypeAny).safeParse === 'function'
|
|
@@ -107,19 +107,7 @@ export const extractZodShape = (raw: Record<string, unknown>): z.ZodRawShape =>
|
|
|
107
107
|
return shape as z.ZodRawShape
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
export
|
|
111
|
-
if (!schema || !isZodSchema(schema)) return schema
|
|
112
|
-
const zod_any = z as unknown as { toJSONSchema: (input: z.ZodTypeAny) => unknown }
|
|
113
|
-
// Cross-language roundtrips preserve core structural types; constraint keywords may not roundtrip exactly.
|
|
114
|
-
return zod_any.toJSONSchema(schema)
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export const fromJsonSchema = (schema: unknown): z.ZodTypeAny => {
|
|
118
|
-
const zod_any = z as unknown as { fromJSONSchema: (input: unknown) => z.ZodTypeAny }
|
|
119
|
-
return zod_any.fromJSONSchema(schema)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export const normalizeEventResultType = (value: EventResultTypeInput): z.ZodTypeAny | undefined => {
|
|
110
|
+
export function normalizeEventResultType(value: unknown): z.ZodTypeAny | undefined {
|
|
123
111
|
if (value === undefined || value === null) {
|
|
124
112
|
return undefined
|
|
125
113
|
}
|
|
@@ -130,5 +118,8 @@ export const normalizeEventResultType = (value: EventResultTypeInput): z.ZodType
|
|
|
130
118
|
if (constructor_schema) {
|
|
131
119
|
return constructor_schema
|
|
132
120
|
}
|
|
121
|
+
if (!isJsonSchema(value)) {
|
|
122
|
+
throw new Error(`event_result_type must be a Zod schema, constructor shorthand, or JSON Schema value, got: ${typeof value}`)
|
|
123
|
+
}
|
|
133
124
|
return fromJsonSchema(value)
|
|
134
125
|
}
|