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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/events_suck.ts"],
|
|
4
|
-
"sourcesContent": ["import { EventBus } from './EventBus.js'\nimport { BaseEvent } from './BaseEvent.js'\n\nimport type { EventClass, EventResultType } from './types.js'\n\ntype EventMap = Record<string, EventClass<BaseEvent>>\ntype
|
|
5
|
-
"mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;
|
|
4
|
+
"sourcesContent": ["import { EventBus } from './EventBus.js'\nimport { BaseEvent } from './BaseEvent.js'\n\nimport type { EventClass, EventResultType } from './types.js'\n\ntype EventMap = Record<string, EventClass<BaseEvent, never>>\ntype FunctionMap = Record<string, (...args: never[]) => unknown>\ntype ExtraDict = Record<string, unknown>\n\ntype EventFieldsFromFn<TFunc extends FunctionMap[string]> =\n Parameters<TFunc> extends [infer TArg, ...unknown[]] ? (TArg extends Record<string, unknown> ? TArg : ExtraDict) : ExtraDict\n\ntype EventFromFn<TFunc extends FunctionMap[string]> = BaseEvent &\n EventFieldsFromFn<TFunc> & {\n __event_result_type__?: Awaited<ReturnType<TFunc>>\n }\n\nexport type GeneratedEvents<TEvents extends FunctionMap> = {\n by_name: { [K in keyof TEvents]: EventClass<EventFromFn<TEvents[K]>, EventFieldsFromFn<TEvents[K]> & ExtraDict> }\n} & {\n [K in keyof TEvents]: EventClass<EventFromFn<TEvents[K]>, EventFieldsFromFn<TEvents[K]> & ExtraDict>\n}\n\ntype EventInit<TEventClass extends EventClass<BaseEvent>> = [ConstructorParameters<TEventClass>[0]] extends [undefined]\n ? {}\n : NonNullable<ConstructorParameters<TEventClass>[0]>\n\ntype EventMethodArgs<TEventClass extends EventClass<BaseEvent>> =\n {} extends EventInit<TEventClass>\n ? [init?: EventInit<TEventClass>, extra?: Record<string, unknown>]\n : [init: EventInit<TEventClass>, extra?: Record<string, unknown>]\n\ntype EventMethodResult<TEventClass extends EventClass<BaseEvent>> = EventResultType<InstanceType<TEventClass>> | undefined\n\nexport type EventsSuckClient<TEvents extends EventMap> = {\n bus: EventBus\n} & {\n [K in keyof TEvents]: (...args: EventMethodArgs<TEvents[K]>) => Promise<EventMethodResult<TEvents[K]>>\n}\n\nexport type EventsSuckClientClass<TEvents extends EventMap> = new (bus?: EventBus) => EventsSuckClient<TEvents>\n\ntype DynamicWrappedClient = {\n bus: EventBus\n} & Record<string, (...args: unknown[]) => Promise<unknown>>\n\nexport const make_events = <TEvents extends FunctionMap>(events: TEvents): GeneratedEvents<TEvents> => {\n const by_name = {} as GeneratedEvents<TEvents>['by_name']\n for (const [event_name] of Object.entries(events) as Array<[keyof TEvents, TEvents[keyof TEvents]]>) {\n if (!/^[A-Za-z_$][\\w$]*$/.test(String(event_name))) {\n throw new Error(`Invalid event name: ${String(event_name)}`)\n }\n by_name[event_name] = BaseEvent.extend(String(event_name), {}) as unknown as GeneratedEvents<TEvents>['by_name'][typeof event_name]\n }\n return Object.assign({ by_name }, by_name) as GeneratedEvents<TEvents>\n}\n\nexport const wrap = <TEvents extends EventMap>(class_name: string, methods: TEvents): EventsSuckClientClass<TEvents> => {\n class WrappedClient {\n bus: EventBus\n\n constructor(bus?: EventBus) {\n this.bus = bus ?? new EventBus(`${class_name}Bus`)\n }\n }\n\n Object.defineProperty(WrappedClient, 'name', { value: class_name })\n\n for (const [method_name, EventCtor] of Object.entries(methods)) {\n Object.defineProperty(WrappedClient.prototype, method_name, {\n value: async function (this: DynamicWrappedClient, init?: Record<string, unknown>, extra?: Record<string, unknown>) {\n const payload = { ...(init ?? {}), ...(extra ?? {}) }\n return await this.bus\n .emit((EventCtor as EventClass<BaseEvent, Record<string, unknown>>)(payload))\n .now({ first_result: true })\n .eventResult()\n },\n writable: true,\n configurable: true,\n })\n }\n\n return WrappedClient as unknown as EventsSuckClientClass<TEvents>\n}\n\n// Intentionally no make_event()/make_handler() helpers in TypeScript.\n// Prefer the explicit inline pattern:\n// const FooCreateEvent = BaseEvent.extend('FooCreateEvent', {\n// id: z.string().nullable().optional(),\n// name: z.string(),\n// age: z.number(),\n// })\n// bus.on(FooCreateEvent, ({ id, name, age, ...extra }) => impl.create(id, { name, age }))\nexport const events_suck = { make_events, wrap } as const\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,gBAAgB;AACzB,SAAS,iBAAiB;AA6CnB,MAAM,cAAc,CAA8B,WAA8C;AACrG,QAAM,UAAU,CAAC;AACjB,aAAW,CAAC,UAAU,KAAK,OAAO,QAAQ,MAAM,GAAqD;AACnG,QAAI,CAAC,qBAAqB,KAAK,OAAO,UAAU,CAAC,GAAG;AAClD,YAAM,IAAI,MAAM,uBAAuB,OAAO,UAAU,CAAC,EAAE;AAAA,IAC7D;AACA,YAAQ,UAAU,IAAI,UAAU,OAAO,OAAO,UAAU,GAAG,CAAC,CAAC;AAAA,EAC/D;AACA,SAAO,OAAO,OAAO,EAAE,QAAQ,GAAG,OAAO;AAC3C;AAEO,MAAM,OAAO,CAA2B,YAAoB,YAAqD;AAAA,EACtH,MAAM,cAAc;AAAA,IAClB;AAAA,IAEA,YAAY,KAAgB;AAC1B,WAAK,MAAM,OAAO,IAAI,SAAS,GAAG,UAAU,KAAK;AAAA,IACnD;AAAA,EACF;AAEA,SAAO,eAAe,eAAe,QAAQ,EAAE,OAAO,WAAW,CAAC;AAElE,aAAW,CAAC,aAAa,SAAS,KAAK,OAAO,QAAQ,OAAO,GAAG;AAC9D,WAAO,eAAe,cAAc,WAAW,aAAa;AAAA,MAC1D,OAAO,eAA4C,MAAgC,OAAiC;AAClH,cAAM,UAAU,EAAE,GAAI,QAAQ,CAAC,GAAI,GAAI,SAAS,CAAC,EAAG;AACpD,eAAO,MAAM,KAAK,IACf,KAAM,UAA6D,OAAO,CAAC,EAC3E,IAAI,EAAE,cAAc,KAAK,CAAC,EAC1B,YAAY;AAAA,MACjB;AAAA,MACA,UAAU;AAAA,MACV,cAAc;AAAA,IAChB,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAUO,MAAM,cAAc,EAAE,aAAa,KAAK;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
const isJsonSchemaObject = (value) => {
|
|
3
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
+
};
|
|
5
|
+
const isJsonSchema = (value) => {
|
|
6
|
+
if (typeof value === "boolean") {
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
if (!isJsonSchemaObject(value)) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
if ("_zod" in value || "_def" in value || "~standard" in value) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
return true;
|
|
16
|
+
};
|
|
17
|
+
const nullUnionCandidates = (schema) => {
|
|
18
|
+
if (Array.isArray(schema.type) && schema.type.includes("null")) {
|
|
19
|
+
const non_null_types = schema.type.filter((item) => typeof item === "string" && item !== "null");
|
|
20
|
+
if (non_null_types.length > 0) {
|
|
21
|
+
return [{ type: non_null_types.length === 1 ? non_null_types[0] : non_null_types }, { type: "null" }];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return null;
|
|
25
|
+
};
|
|
26
|
+
const normalizeJsonSchema = (schema) => {
|
|
27
|
+
const normalized = normalizeJsonSchemaValue(schema);
|
|
28
|
+
if (!isJsonSchemaObject(normalized)) {
|
|
29
|
+
return normalized;
|
|
30
|
+
}
|
|
31
|
+
const schema_record = { ...normalized };
|
|
32
|
+
const definitions = schema_record.$defs;
|
|
33
|
+
const root_ref = rootRefForSchema(schema_record, definitions);
|
|
34
|
+
if (!root_ref || !definitions) {
|
|
35
|
+
schema_record.$schema ??= "https://json-schema.org/draft/2020-12/schema";
|
|
36
|
+
return schema_record;
|
|
37
|
+
}
|
|
38
|
+
const root_name = root_ref.slice("#/$defs/".length);
|
|
39
|
+
const root_schema = definitions[root_name];
|
|
40
|
+
if (!isJsonSchemaObject(root_schema)) {
|
|
41
|
+
schema_record.$schema ??= "https://json-schema.org/draft/2020-12/schema";
|
|
42
|
+
return schema_record;
|
|
43
|
+
}
|
|
44
|
+
const rewritten_root = rewriteJsonSchemaRefs(root_schema, { [root_ref]: "#" });
|
|
45
|
+
const remaining_defs = Object.fromEntries(Object.entries(definitions).filter(([name]) => name !== root_name));
|
|
46
|
+
if (Object.keys(remaining_defs).length > 0) {
|
|
47
|
+
rewritten_root.$defs = rewriteJsonSchemaRefs(remaining_defs, { [root_ref]: "#" });
|
|
48
|
+
}
|
|
49
|
+
rewritten_root.$schema ??= schema_record.$schema ?? "https://json-schema.org/draft/2020-12/schema";
|
|
50
|
+
setTitleFromInlinedRootDefinition(rewritten_root, root_name);
|
|
51
|
+
return rewritten_root;
|
|
52
|
+
};
|
|
53
|
+
const rootRefForSchema = (schema, definitions) => {
|
|
54
|
+
if (typeof schema.$ref === "string" && schema.$ref.startsWith("#/$defs/")) {
|
|
55
|
+
return schema.$ref;
|
|
56
|
+
}
|
|
57
|
+
if (!definitions) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const root = schemaWithoutSchemaAndDefinitions(schema);
|
|
61
|
+
for (const [name, definition] of Object.entries(definitions)) {
|
|
62
|
+
if (JSON.stringify(definition) === JSON.stringify(root)) {
|
|
63
|
+
return `#/$defs/${name}`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
};
|
|
68
|
+
const schemaWithoutSchemaAndDefinitions = (schema) => {
|
|
69
|
+
const root = {};
|
|
70
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
71
|
+
if (key !== "$schema" && key !== "$defs") {
|
|
72
|
+
root[key] = value;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return root;
|
|
76
|
+
};
|
|
77
|
+
const setTitleFromInlinedRootDefinition = (schema, root_name) => {
|
|
78
|
+
if (root_name.startsWith("__schema")) return;
|
|
79
|
+
schema.title ??= root_name;
|
|
80
|
+
};
|
|
81
|
+
const rewriteJsonSchemaRefs = (schema, refs) => {
|
|
82
|
+
if (Array.isArray(schema)) {
|
|
83
|
+
return schema.map((item) => rewriteJsonSchemaRefs(item, refs));
|
|
84
|
+
}
|
|
85
|
+
if (!isJsonSchemaObject(schema)) {
|
|
86
|
+
return schema;
|
|
87
|
+
}
|
|
88
|
+
const rewritten = {};
|
|
89
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
90
|
+
rewritten[key] = rewriteJsonSchemaRefs(value, refs);
|
|
91
|
+
}
|
|
92
|
+
if (typeof rewritten.$ref === "string" && rewritten.$ref in refs) {
|
|
93
|
+
rewritten.$ref = refs[rewritten.$ref];
|
|
94
|
+
}
|
|
95
|
+
return rewritten;
|
|
96
|
+
};
|
|
97
|
+
const normalizeJsonSchemaValue = (schema) => {
|
|
98
|
+
if (Array.isArray(schema)) {
|
|
99
|
+
return schema.map((item) => normalizeJsonSchemaValue(item));
|
|
100
|
+
}
|
|
101
|
+
if (!isJsonSchemaObject(schema)) {
|
|
102
|
+
return schema;
|
|
103
|
+
}
|
|
104
|
+
const normalized = {};
|
|
105
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
106
|
+
normalized[key] = normalizeJsonSchemaValue(value);
|
|
107
|
+
}
|
|
108
|
+
if (Array.isArray(normalized.required) && normalized.required.every((item) => typeof item === "string")) {
|
|
109
|
+
normalized.required = [...normalized.required].sort();
|
|
110
|
+
}
|
|
111
|
+
const null_union_candidates = nullUnionCandidates(normalized);
|
|
112
|
+
if (null_union_candidates !== null) {
|
|
113
|
+
const merged = { anyOf: normalizeJsonSchemaValue(null_union_candidates) };
|
|
114
|
+
for (const [key, value] of Object.entries(normalized)) {
|
|
115
|
+
if (key !== "type") {
|
|
116
|
+
merged[key] = value;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return merged;
|
|
120
|
+
}
|
|
121
|
+
return normalized;
|
|
122
|
+
};
|
|
123
|
+
const toJsonSchema = (schema) => {
|
|
124
|
+
return normalizeJsonSchema(z.toJSONSchema(schema));
|
|
125
|
+
};
|
|
126
|
+
const fromJsonSchema = (schema) => {
|
|
127
|
+
return z.fromJSONSchema(schema);
|
|
128
|
+
};
|
|
129
|
+
export {
|
|
130
|
+
fromJsonSchema,
|
|
131
|
+
isJsonSchema,
|
|
132
|
+
normalizeJsonSchema,
|
|
133
|
+
toJsonSchema
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=jsonschema.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/jsonschema.ts"],
|
|
4
|
+
"sourcesContent": ["import { z } from 'zod'\n\nexport type JsonSchema = boolean | z.core.JSONSchema.JSONSchema\ntype JsonSchemaObject = z.core.JSONSchema.JSONSchema\n\nconst isJsonSchemaObject = (value: unknown): value is JsonSchemaObject => {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nexport const isJsonSchema = (value: unknown): value is JsonSchema => {\n if (typeof value === 'boolean') {\n return true\n }\n if (!isJsonSchemaObject(value)) {\n return false\n }\n if ('_zod' in value || '_def' in value || '~standard' in value) {\n return false\n }\n return true\n}\n\nconst nullUnionCandidates = (schema: Record<string, unknown>): Record<string, unknown>[] | null => {\n if (Array.isArray(schema.type) && schema.type.includes('null')) {\n const non_null_types = schema.type.filter((item): item is string => typeof item === 'string' && item !== 'null')\n if (non_null_types.length > 0) {\n return [{ type: non_null_types.length === 1 ? non_null_types[0] : non_null_types }, { type: 'null' }]\n }\n }\n\n return null\n}\n\nexport const normalizeJsonSchema = (schema: JsonSchema): JsonSchema => {\n const normalized = normalizeJsonSchemaValue(schema)\n if (!isJsonSchemaObject(normalized)) {\n return normalized as JsonSchema\n }\n const schema_record = { ...normalized } as JsonSchemaObject\n const definitions = schema_record.$defs\n const root_ref = rootRefForSchema(schema_record, definitions)\n if (!root_ref || !definitions) {\n schema_record.$schema ??= 'https://json-schema.org/draft/2020-12/schema'\n return schema_record\n }\n const root_name = root_ref.slice('#/$defs/'.length)\n const root_schema = definitions[root_name]\n if (!isJsonSchemaObject(root_schema)) {\n schema_record.$schema ??= 'https://json-schema.org/draft/2020-12/schema'\n return schema_record\n }\n const rewritten_root = rewriteJsonSchemaRefs(root_schema, { [root_ref]: '#' }) as JsonSchemaObject\n const remaining_defs = Object.fromEntries(Object.entries(definitions).filter(([name]) => name !== root_name))\n if (Object.keys(remaining_defs).length > 0) {\n rewritten_root.$defs = rewriteJsonSchemaRefs(remaining_defs, { [root_ref]: '#' }) as Record<string, JsonSchemaObject>\n }\n rewritten_root.$schema ??= schema_record.$schema ?? 'https://json-schema.org/draft/2020-12/schema'\n setTitleFromInlinedRootDefinition(rewritten_root, root_name)\n return rewritten_root\n}\n\nconst rootRefForSchema = (schema: JsonSchemaObject, definitions: Record<string, JsonSchemaObject> | undefined): string | null => {\n if (typeof schema.$ref === 'string' && schema.$ref.startsWith('#/$defs/')) {\n return schema.$ref\n }\n if (!definitions) {\n return null\n }\n const root = schemaWithoutSchemaAndDefinitions(schema)\n for (const [name, definition] of Object.entries(definitions)) {\n if (JSON.stringify(definition) === JSON.stringify(root)) {\n return `#/$defs/${name}`\n }\n }\n return null\n}\n\nconst schemaWithoutSchemaAndDefinitions = (schema: JsonSchemaObject): Record<string, unknown> => {\n const root: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(schema)) {\n if (key !== '$schema' && key !== '$defs') {\n root[key] = value\n }\n }\n return root\n}\n\nconst setTitleFromInlinedRootDefinition = (schema: JsonSchemaObject, root_name: string): void => {\n if (root_name.startsWith('__schema')) return\n schema.title ??= root_name\n}\n\nconst rewriteJsonSchemaRefs = (schema: unknown, refs: Record<string, string>): unknown => {\n if (Array.isArray(schema)) {\n return schema.map((item) => rewriteJsonSchemaRefs(item, refs))\n }\n if (!isJsonSchemaObject(schema)) {\n return schema\n }\n const rewritten: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(schema)) {\n rewritten[key] = rewriteJsonSchemaRefs(value, refs)\n }\n if (typeof rewritten.$ref === 'string' && rewritten.$ref in refs) {\n rewritten.$ref = refs[rewritten.$ref]\n }\n return rewritten\n}\n\nconst normalizeJsonSchemaValue = (schema: unknown): unknown => {\n if (Array.isArray(schema)) {\n return schema.map((item) => normalizeJsonSchemaValue(item))\n }\n if (!isJsonSchemaObject(schema)) {\n return schema\n }\n\n const normalized: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(schema)) {\n normalized[key] = normalizeJsonSchemaValue(value)\n }\n if (Array.isArray(normalized.required) && normalized.required.every((item) => typeof item === 'string')) {\n normalized.required = [...normalized.required].sort()\n }\n\n const null_union_candidates = nullUnionCandidates(normalized)\n if (null_union_candidates !== null) {\n const merged: Record<string, unknown> = { anyOf: normalizeJsonSchemaValue(null_union_candidates) }\n for (const [key, value] of Object.entries(normalized)) {\n if (key !== 'type') {\n merged[key] = value\n }\n }\n return merged\n }\n\n return normalized\n}\n\nexport const toJsonSchema = (schema: z.core.$ZodType): JsonSchema => {\n return normalizeJsonSchema(z.toJSONSchema(schema) as JsonSchema)\n}\n\nexport const fromJsonSchema = (schema: JsonSchema): z.ZodTypeAny => {\n return z.fromJSONSchema(schema)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/esm/retry.js
CHANGED
|
@@ -2,6 +2,8 @@ import { createAsyncLocalStorage } from "./async_context.js";
|
|
|
2
2
|
import { isNodeRuntime } from "./optional_deps.js";
|
|
3
3
|
const MULTIPROCESS_SEMAPHORE_DIRNAME = "browser_use_semaphores";
|
|
4
4
|
const MULTIPROCESS_STALE_LOCK_MS = 5 * 60 * 1e3;
|
|
5
|
+
const RETRY_SLOW_WARNING_THROTTLE_MS = 2e3;
|
|
6
|
+
const RETRY_SLOW_WARNING_ARGS_MAX_LENGTH = 80;
|
|
5
7
|
let multiprocess_fallback_reason_logged = null;
|
|
6
8
|
class RetryTimeoutError extends Error {
|
|
7
9
|
timeout_seconds;
|
|
@@ -107,16 +109,20 @@ function retry(options = {}) {
|
|
|
107
109
|
retry_backoff_factor = 1,
|
|
108
110
|
retry_on_errors,
|
|
109
111
|
timeout,
|
|
112
|
+
slow_timeout,
|
|
110
113
|
semaphore_limit,
|
|
111
114
|
semaphore_name: semaphore_name_option,
|
|
112
115
|
semaphore_lax = true,
|
|
113
116
|
semaphore_scope = "global",
|
|
114
117
|
semaphore_timeout
|
|
115
118
|
} = options;
|
|
116
|
-
const decorateFunction = (target, _context) => {
|
|
117
|
-
const
|
|
119
|
+
const decorateFunction = (target, _context, owner_name) => {
|
|
120
|
+
const base_fn_name = target.name || _context?.name || "anonymous";
|
|
121
|
+
let fn_name = owner_name ? `${owner_name}.${base_fn_name}` : base_fn_name;
|
|
118
122
|
const effective_max_attempts = Math.max(1, max_attempts);
|
|
119
123
|
const effective_retry_after = Math.max(0, retry_after);
|
|
124
|
+
const effective_slow_timeout_ms = slow_timeout != null && slow_timeout > 0 ? slow_timeout * 1e3 : null;
|
|
125
|
+
let last_slow_warning_at = 0;
|
|
120
126
|
const shouldRetry = (error) => {
|
|
121
127
|
if (!retry_on_errors || retry_on_errors.length === 0) return true;
|
|
122
128
|
return retry_on_errors.some(
|
|
@@ -135,6 +141,13 @@ function retry(options = {}) {
|
|
|
135
141
|
sleepSync(delay_seconds * 1e3);
|
|
136
142
|
}
|
|
137
143
|
};
|
|
144
|
+
const emitSlowWarningIfDue = (args, start_time) => {
|
|
145
|
+
if (effective_slow_timeout_ms == null) return;
|
|
146
|
+
const now = Date.now();
|
|
147
|
+
if (now - last_slow_warning_at < RETRY_SLOW_WARNING_THROTTLE_MS) return;
|
|
148
|
+
last_slow_warning_at = now;
|
|
149
|
+
console.warn(`Warning: ${fn_name}(${formatRetrySlowWarningArgs(args)}) slow (${((now - start_time) / 1e3).toFixed(1)}s)`);
|
|
150
|
+
};
|
|
138
151
|
const runRetryLoopFromThenable = async (this_arg, args, first_thenable, first_attempt) => {
|
|
139
152
|
let current_result = first_thenable;
|
|
140
153
|
for (let attempt = first_attempt; attempt <= effective_max_attempts; attempt++) {
|
|
@@ -214,20 +227,35 @@ function retry(options = {}) {
|
|
|
214
227
|
new_held.add(scoped_key);
|
|
215
228
|
}
|
|
216
229
|
const runRetryLoop = async () => {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
230
|
+
const call_started_at = Date.now();
|
|
231
|
+
const warning_args = [...args];
|
|
232
|
+
const slow_warning_timer = effective_slow_timeout_ms == null ? null : setTimeout(() => emitSlowWarningIfDue(warning_args, call_started_at), effective_slow_timeout_ms);
|
|
233
|
+
const finishSlowWarning = () => {
|
|
234
|
+
if (slow_warning_timer !== null) {
|
|
235
|
+
clearTimeout(slow_warning_timer);
|
|
236
|
+
}
|
|
237
|
+
if (effective_slow_timeout_ms != null && Date.now() - call_started_at >= effective_slow_timeout_ms) {
|
|
238
|
+
emitSlowWarningIfDue(warning_args, call_started_at);
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
try {
|
|
242
|
+
for (let attempt = 1; attempt <= effective_max_attempts; attempt++) {
|
|
243
|
+
try {
|
|
244
|
+
if (timeout != null && timeout > 0) {
|
|
245
|
+
return await _runWithTimeout(() => Promise.resolve(target.apply(this, args)), timeout * 1e3, attempt);
|
|
246
|
+
} else {
|
|
247
|
+
return await Promise.resolve(target.apply(this, args));
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
if (!shouldRetry(error)) throw error;
|
|
251
|
+
if (attempt >= effective_max_attempts) throw error;
|
|
252
|
+
await asyncRetryDelay(attempt);
|
|
223
253
|
}
|
|
224
|
-
} catch (error) {
|
|
225
|
-
if (!shouldRetry(error)) throw error;
|
|
226
|
-
if (attempt >= effective_max_attempts) throw error;
|
|
227
|
-
await asyncRetryDelay(attempt);
|
|
228
254
|
}
|
|
255
|
+
throw new Error(`retry(${fn_name}): unexpected end of retry loop`);
|
|
256
|
+
} finally {
|
|
257
|
+
finishSlowWarning();
|
|
229
258
|
}
|
|
230
|
-
throw new Error(`retry(${fn_name}): unexpected end of retry loop`);
|
|
231
259
|
};
|
|
232
260
|
try {
|
|
233
261
|
return await runWithHeldSemaphores(new_held, runRetryLoop);
|
|
@@ -277,27 +305,48 @@ function retry(options = {}) {
|
|
|
277
305
|
}
|
|
278
306
|
};
|
|
279
307
|
const runRetryLoop = () => {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
308
|
+
const call_started_at = Date.now();
|
|
309
|
+
const warning_args = [...args];
|
|
310
|
+
const slow_warning_timer = effective_slow_timeout_ms == null ? null : setTimeout(() => emitSlowWarningIfDue(warning_args, call_started_at), effective_slow_timeout_ms);
|
|
311
|
+
const finishSlowWarning = () => {
|
|
312
|
+
if (slow_warning_timer !== null) {
|
|
313
|
+
clearTimeout(slow_warning_timer);
|
|
314
|
+
}
|
|
315
|
+
if (effective_slow_timeout_ms != null && Date.now() - call_started_at >= effective_slow_timeout_ms) {
|
|
316
|
+
emitSlowWarningIfDue(warning_args, call_started_at);
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
let finish_on_return = true;
|
|
320
|
+
try {
|
|
321
|
+
for (let attempt = 1; attempt <= effective_max_attempts; attempt++) {
|
|
322
|
+
const attempt_started_at = Date.now();
|
|
323
|
+
try {
|
|
324
|
+
const result = target.apply(this, args);
|
|
325
|
+
if (isThenable(result)) {
|
|
326
|
+
finish_on_return = false;
|
|
327
|
+
return runRetryLoopFromThenable(this, args, result, attempt).finally(finishSlowWarning);
|
|
328
|
+
}
|
|
329
|
+
if (timeout != null && timeout > 0 && Date.now() - attempt_started_at > timeout * 1e3) {
|
|
330
|
+
throw new RetryTimeoutError(`Timed out after ${timeout}s (attempt ${attempt})`, {
|
|
331
|
+
timeout_seconds: timeout,
|
|
332
|
+
attempt
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
return result;
|
|
336
|
+
} catch (error) {
|
|
337
|
+
if (!shouldRetry(error)) throw error;
|
|
338
|
+
if (attempt >= effective_max_attempts) {
|
|
339
|
+
throw error;
|
|
340
|
+
}
|
|
341
|
+
syncRetryDelay(attempt);
|
|
292
342
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
343
|
+
}
|
|
344
|
+
throw new Error(`retry(${fn_name}): unexpected end of retry loop`);
|
|
345
|
+
} finally {
|
|
346
|
+
if (finish_on_return) {
|
|
347
|
+
finishSlowWarning();
|
|
298
348
|
}
|
|
299
349
|
}
|
|
300
|
-
throw new Error(`retry(${fn_name}): unexpected end of retry loop`);
|
|
301
350
|
};
|
|
302
351
|
try {
|
|
303
352
|
const result = runWithHeldSemaphores(new_held, runRetryLoop);
|
|
@@ -315,9 +364,10 @@ function retry(options = {}) {
|
|
|
315
364
|
Object.defineProperty(retryWrapper, "name", { value: fn_name, configurable: true });
|
|
316
365
|
if (_context?.kind === "method" && typeof _context.addInitializer === "function") {
|
|
317
366
|
_context.addInitializer(function() {
|
|
318
|
-
const
|
|
319
|
-
if (
|
|
320
|
-
|
|
367
|
+
const owner_name2 = findDecoratedMethodOwnerName(this, _context, retryWrapper);
|
|
368
|
+
if (owner_name2) {
|
|
369
|
+
fn_name = `${owner_name2}.${target.name || _context.name || "anonymous"}`;
|
|
370
|
+
Object.defineProperty(retryWrapper, "name", { value: fn_name, configurable: true });
|
|
321
371
|
}
|
|
322
372
|
});
|
|
323
373
|
}
|
|
@@ -325,7 +375,8 @@ function retry(options = {}) {
|
|
|
325
375
|
};
|
|
326
376
|
function decorator(target, context_or_property_key, descriptor) {
|
|
327
377
|
if (descriptor?.value && typeof descriptor.value === "function") {
|
|
328
|
-
|
|
378
|
+
const owner_name = target && (typeof target === "object" || typeof target === "function") ? typeof target === "function" ? target.name : target.constructor?.name : null;
|
|
379
|
+
descriptor.value = decorateFunction(descriptor.value, void 0, owner_name);
|
|
329
380
|
return descriptor;
|
|
330
381
|
}
|
|
331
382
|
if (typeof target === "function") {
|
|
@@ -372,6 +423,30 @@ function isAsyncFunction(fn) {
|
|
|
372
423
|
function isThenable(value) {
|
|
373
424
|
return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
|
|
374
425
|
}
|
|
426
|
+
function formatRetrySlowWarningArgs(args) {
|
|
427
|
+
const preview = args.map(formatRetrySlowWarningValue).join(", ");
|
|
428
|
+
if (preview.length > RETRY_SLOW_WARNING_ARGS_MAX_LENGTH) {
|
|
429
|
+
return `${preview.slice(0, RETRY_SLOW_WARNING_ARGS_MAX_LENGTH - 3).replace(/,?\s*$/, "")}...`;
|
|
430
|
+
}
|
|
431
|
+
return preview;
|
|
432
|
+
}
|
|
433
|
+
function formatRetrySlowWarningValue(value) {
|
|
434
|
+
let text;
|
|
435
|
+
if (typeof value === "string") {
|
|
436
|
+
text = value;
|
|
437
|
+
} else if (value === null || value === void 0) {
|
|
438
|
+
text = String(value);
|
|
439
|
+
} else if (typeof value === "object") {
|
|
440
|
+
try {
|
|
441
|
+
text = JSON.stringify(value);
|
|
442
|
+
} catch {
|
|
443
|
+
text = String(value);
|
|
444
|
+
}
|
|
445
|
+
} else {
|
|
446
|
+
text = String(value);
|
|
447
|
+
}
|
|
448
|
+
return text.replace(/['"]/g, "").slice(0, 3);
|
|
449
|
+
}
|
|
375
450
|
async function acquireWithTimeout(semaphore, timeout_ms) {
|
|
376
451
|
return new Promise((resolve) => {
|
|
377
452
|
let settled = false;
|