abxbus 2.4.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.
- package/README.md +647 -0
- package/dist/cjs/async_context.d.ts +12 -0
- package/dist/cjs/async_context.js +70 -0
- package/dist/cjs/async_context.js.map +7 -0
- package/dist/cjs/base_event.d.ts +207 -0
- package/dist/cjs/base_event.js +871 -0
- package/dist/cjs/base_event.js.map +7 -0
- package/dist/cjs/bridge_jsonl.d.ts +26 -0
- package/dist/cjs/bridge_jsonl.js +170 -0
- package/dist/cjs/bridge_jsonl.js.map +7 -0
- package/dist/cjs/bridge_nats.d.ts +20 -0
- package/dist/cjs/bridge_nats.js +108 -0
- package/dist/cjs/bridge_nats.js.map +7 -0
- package/dist/cjs/bridge_postgres.d.ts +31 -0
- package/dist/cjs/bridge_postgres.js +251 -0
- package/dist/cjs/bridge_postgres.js.map +7 -0
- package/dist/cjs/bridge_redis.d.ts +34 -0
- package/dist/cjs/bridge_redis.js +175 -0
- package/dist/cjs/bridge_redis.js.map +7 -0
- package/dist/cjs/bridge_sqlite.d.ts +30 -0
- package/dist/cjs/bridge_sqlite.js +255 -0
- package/dist/cjs/bridge_sqlite.js.map +7 -0
- package/dist/cjs/bridges.d.ts +49 -0
- package/dist/cjs/bridges.js +326 -0
- package/dist/cjs/bridges.js.map +7 -0
- package/dist/cjs/event_bus.d.ts +127 -0
- package/dist/cjs/event_bus.js +1058 -0
- package/dist/cjs/event_bus.js.map +7 -0
- package/dist/cjs/event_handler.d.ts +139 -0
- package/dist/cjs/event_handler.js +299 -0
- package/dist/cjs/event_handler.js.map +7 -0
- package/dist/cjs/event_history.d.ts +45 -0
- package/dist/cjs/event_history.js +192 -0
- package/dist/cjs/event_history.js.map +7 -0
- package/dist/cjs/event_result.d.ts +86 -0
- package/dist/cjs/event_result.js +446 -0
- package/dist/cjs/event_result.js.map +7 -0
- package/dist/cjs/events_suck.d.ts +40 -0
- package/dist/cjs/events_suck.js +59 -0
- package/dist/cjs/events_suck.js.map +7 -0
- package/dist/cjs/helpers.d.ts +1 -0
- package/dist/cjs/helpers.js +84 -0
- package/dist/cjs/helpers.js.map +7 -0
- package/dist/cjs/index.d.ts +17 -0
- package/dist/cjs/index.js +54 -0
- package/dist/cjs/index.js.map +7 -0
- package/dist/cjs/lock_manager.d.ts +70 -0
- package/dist/cjs/lock_manager.js +343 -0
- package/dist/cjs/lock_manager.js.map +7 -0
- package/dist/cjs/logging.d.ts +16 -0
- package/dist/cjs/logging.js +216 -0
- package/dist/cjs/logging.js.map +7 -0
- package/dist/cjs/middlewares.d.ts +13 -0
- package/dist/cjs/middlewares.js +17 -0
- package/dist/cjs/middlewares.js.map +7 -0
- package/dist/cjs/optional_deps.d.ts +3 -0
- package/dist/cjs/optional_deps.js +64 -0
- package/dist/cjs/optional_deps.js.map +7 -0
- package/dist/cjs/package.json +5 -0
- package/dist/cjs/retry.d.ts +52 -0
- package/dist/cjs/retry.js +257 -0
- package/dist/cjs/retry.js.map +7 -0
- package/dist/cjs/timing.d.ts +3 -0
- package/dist/cjs/timing.js +76 -0
- package/dist/cjs/timing.js.map +7 -0
- package/dist/cjs/type_inference.test.d.ts +1 -0
- package/dist/cjs/types.d.ts +36 -0
- package/dist/cjs/types.js +104 -0
- package/dist/cjs/types.js.map +7 -0
- package/dist/esm/async_context.js +50 -0
- package/dist/esm/async_context.js.map +7 -0
- package/dist/esm/base_event.js +857 -0
- package/dist/esm/base_event.js.map +7 -0
- package/dist/esm/bridge_jsonl.js +150 -0
- package/dist/esm/bridge_jsonl.js.map +7 -0
- package/dist/esm/bridge_nats.js +88 -0
- package/dist/esm/bridge_nats.js.map +7 -0
- package/dist/esm/bridge_postgres.js +231 -0
- package/dist/esm/bridge_postgres.js.map +7 -0
- package/dist/esm/bridge_redis.js +155 -0
- package/dist/esm/bridge_redis.js.map +7 -0
- package/dist/esm/bridge_sqlite.js +235 -0
- package/dist/esm/bridge_sqlite.js.map +7 -0
- package/dist/esm/bridges.js +306 -0
- package/dist/esm/bridges.js.map +7 -0
- package/dist/esm/event_bus.js +1046 -0
- package/dist/esm/event_bus.js.map +7 -0
- package/dist/esm/event_handler.js +279 -0
- package/dist/esm/event_handler.js.map +7 -0
- package/dist/esm/event_history.js +172 -0
- package/dist/esm/event_history.js.map +7 -0
- package/dist/esm/event_result.js +426 -0
- package/dist/esm/event_result.js.map +7 -0
- package/dist/esm/events_suck.js +39 -0
- package/dist/esm/events_suck.js.map +7 -0
- package/dist/esm/helpers.js +64 -0
- package/dist/esm/helpers.js.map +7 -0
- package/dist/esm/index.js +47 -0
- package/dist/esm/index.js.map +7 -0
- package/dist/esm/lock_manager.js +323 -0
- package/dist/esm/lock_manager.js.map +7 -0
- package/dist/esm/logging.js +196 -0
- package/dist/esm/logging.js.map +7 -0
- package/dist/esm/middlewares.js +1 -0
- package/dist/esm/middlewares.js.map +7 -0
- package/dist/esm/optional_deps.js +44 -0
- package/dist/esm/optional_deps.js.map +7 -0
- package/dist/esm/retry.js +237 -0
- package/dist/esm/retry.js.map +7 -0
- package/dist/esm/timing.js +56 -0
- package/dist/esm/timing.js.map +7 -0
- package/dist/esm/types.js +84 -0
- package/dist/esm/types.js.map +7 -0
- package/dist/types/async_context.d.ts +12 -0
- package/dist/types/base_event.d.ts +207 -0
- package/dist/types/bridge_jsonl.d.ts +26 -0
- package/dist/types/bridge_nats.d.ts +20 -0
- package/dist/types/bridge_postgres.d.ts +31 -0
- package/dist/types/bridge_redis.d.ts +34 -0
- package/dist/types/bridge_sqlite.d.ts +30 -0
- package/dist/types/bridges.d.ts +49 -0
- package/dist/types/event_bus.d.ts +127 -0
- package/dist/types/event_handler.d.ts +139 -0
- package/dist/types/event_history.d.ts +45 -0
- package/dist/types/event_result.d.ts +86 -0
- package/dist/types/events_suck.d.ts +40 -0
- package/dist/types/helpers.d.ts +1 -0
- package/dist/types/index.d.ts +17 -0
- package/dist/types/lock_manager.d.ts +70 -0
- package/dist/types/logging.d.ts +16 -0
- package/dist/types/middlewares.d.ts +13 -0
- package/dist/types/optional_deps.d.ts +3 -0
- package/dist/types/retry.d.ts +52 -0
- package/dist/types/timing.d.ts +3 -0
- package/dist/types/type_inference.test.d.ts +1 -0
- package/dist/types/types.d.ts +36 -0
- package/package.json +87 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { BaseEvent } from "./base_event.js";
|
|
2
|
+
import { EventHandlerCancelledError, EventHandlerTimeoutError } from "./event_handler.js";
|
|
3
|
+
const logTree = (bus) => {
|
|
4
|
+
const parent_to_children = /* @__PURE__ */ new Map();
|
|
5
|
+
const addChild = (parent_id, child) => {
|
|
6
|
+
const existing = parent_to_children.get(parent_id) ?? [];
|
|
7
|
+
existing.push(child);
|
|
8
|
+
parent_to_children.set(parent_id, existing);
|
|
9
|
+
};
|
|
10
|
+
const root_events = [];
|
|
11
|
+
const seen = /* @__PURE__ */ new Set();
|
|
12
|
+
for (const event of bus.event_history.values()) {
|
|
13
|
+
const parent_id = event.event_parent_id;
|
|
14
|
+
if (!parent_id || parent_id === event.event_id || !bus.event_history.has(parent_id)) {
|
|
15
|
+
if (!seen.has(event.event_id)) {
|
|
16
|
+
root_events.push(event);
|
|
17
|
+
seen.add(event.event_id);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (root_events.length === 0) {
|
|
22
|
+
return "(No events in history)";
|
|
23
|
+
}
|
|
24
|
+
const nodes_by_id = /* @__PURE__ */ new Map();
|
|
25
|
+
for (const root of root_events) {
|
|
26
|
+
nodes_by_id.set(root.event_id, root);
|
|
27
|
+
for (const descendant of root.event_descendants) {
|
|
28
|
+
nodes_by_id.set(descendant.event_id, descendant);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
for (const node of nodes_by_id.values()) {
|
|
32
|
+
const parent_id = node.event_parent_id;
|
|
33
|
+
if (!parent_id || parent_id === node.event_id) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (!nodes_by_id.has(parent_id)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
addChild(parent_id, node);
|
|
40
|
+
}
|
|
41
|
+
for (const children of parent_to_children.values()) {
|
|
42
|
+
children.sort((a, b) => a.event_created_at < b.event_created_at ? -1 : a.event_created_at > b.event_created_at ? 1 : 0);
|
|
43
|
+
}
|
|
44
|
+
const lines = [];
|
|
45
|
+
const bus_label = typeof bus.toString === "function" ? bus.toString() : bus.name;
|
|
46
|
+
lines.push(`\u{1F4CA} Event History Tree for ${bus_label}`);
|
|
47
|
+
lines.push("=".repeat(80));
|
|
48
|
+
root_events.sort((a, b) => a.event_created_at < b.event_created_at ? -1 : a.event_created_at > b.event_created_at ? 1 : 0);
|
|
49
|
+
const visited = /* @__PURE__ */ new Set();
|
|
50
|
+
root_events.forEach((event, index) => {
|
|
51
|
+
lines.push(buildTreeLine(event, "", index === root_events.length - 1, parent_to_children, visited));
|
|
52
|
+
});
|
|
53
|
+
lines.push("=".repeat(80));
|
|
54
|
+
return lines.join("\n");
|
|
55
|
+
};
|
|
56
|
+
const buildTreeLine = (event, indent, is_last, parent_to_children, visited) => {
|
|
57
|
+
const connector = is_last ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
58
|
+
const status_icon = event.event_status === "completed" ? "\u2705" : event.event_status === "started" ? "\u{1F3C3}" : "\u23F3";
|
|
59
|
+
const created_at = formatTimestamp(event.event_created_at);
|
|
60
|
+
let timing = `[${created_at}`;
|
|
61
|
+
if (event.event_completed_at) {
|
|
62
|
+
const created_ms = Date.parse(event.event_created_at);
|
|
63
|
+
const completed_ms = Date.parse(event.event_completed_at);
|
|
64
|
+
if (!Number.isNaN(created_ms) && !Number.isNaN(completed_ms)) {
|
|
65
|
+
const duration = (completed_ms - created_ms) / 1e3;
|
|
66
|
+
timing += ` (${duration.toFixed(3)}s)`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
timing += "]";
|
|
70
|
+
const line = `${indent}${connector}${status_icon} ${event.event_type}#${event.event_id.slice(-4)} ${timing}`;
|
|
71
|
+
if (visited.has(event.event_id)) {
|
|
72
|
+
return line;
|
|
73
|
+
}
|
|
74
|
+
visited.add(event.event_id);
|
|
75
|
+
const extension = is_last ? " " : "\u2502 ";
|
|
76
|
+
const new_indent = indent + extension;
|
|
77
|
+
const result_items = [];
|
|
78
|
+
for (const result of event.event_results.values()) {
|
|
79
|
+
result_items.push({ type: "result", result });
|
|
80
|
+
}
|
|
81
|
+
const children = parent_to_children.get(event.event_id) ?? [];
|
|
82
|
+
const printed_child_ids = new Set(event.event_results.size > 0 ? event.event_results.keys() : []);
|
|
83
|
+
for (const child of children) {
|
|
84
|
+
if (!printed_child_ids.has(child.event_id) && !child.event_emitted_by_handler_id) {
|
|
85
|
+
result_items.push({ type: "child", child });
|
|
86
|
+
printed_child_ids.add(child.event_id);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (result_items.length === 0) {
|
|
90
|
+
return line;
|
|
91
|
+
}
|
|
92
|
+
const child_lines = [];
|
|
93
|
+
result_items.forEach((item, index) => {
|
|
94
|
+
const is_last_item = index === result_items.length - 1;
|
|
95
|
+
if (item.type === "result") {
|
|
96
|
+
child_lines.push(buildResultLine(item.result, new_indent, is_last_item, parent_to_children, visited));
|
|
97
|
+
} else {
|
|
98
|
+
child_lines.push(buildTreeLine(item.child, new_indent, is_last_item, parent_to_children, visited));
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
return [line, ...child_lines].join("\n");
|
|
102
|
+
};
|
|
103
|
+
const buildResultLine = (result, indent, is_last, parent_to_children, visited) => {
|
|
104
|
+
const connector = is_last ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
|
|
105
|
+
const status_icon = result.status === "completed" ? "\u2705" : result.status === "error" ? "\u274C" : result.status === "started" ? "\u{1F3C3}" : "\u23F3";
|
|
106
|
+
const handler_label = result.handler_name && result.handler_name !== "anonymous" ? result.handler_name : result.handler_file_path ? result.handler_file_path : "anonymous";
|
|
107
|
+
const handler_display = `${result.eventbus_label}.${handler_label}#${result.handler_id.slice(-4)}`;
|
|
108
|
+
let line = `${indent}${connector}${status_icon} ${handler_display}`;
|
|
109
|
+
if (result.started_at) {
|
|
110
|
+
line += ` [${formatTimestamp(result.started_at)}`;
|
|
111
|
+
if (result.completed_at) {
|
|
112
|
+
const started_ms = Date.parse(result.started_at);
|
|
113
|
+
const completed_ms = Date.parse(result.completed_at);
|
|
114
|
+
if (!Number.isNaN(started_ms) && !Number.isNaN(completed_ms)) {
|
|
115
|
+
const duration = (completed_ms - started_ms) / 1e3;
|
|
116
|
+
line += ` (${duration.toFixed(3)}s)`;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
line += "]";
|
|
120
|
+
}
|
|
121
|
+
if (result.status === "error" && result.error) {
|
|
122
|
+
if (result.error instanceof EventHandlerTimeoutError) {
|
|
123
|
+
line += ` \u23F1\uFE0F Timeout: ${result.error.message}`;
|
|
124
|
+
} else if (result.error instanceof EventHandlerCancelledError) {
|
|
125
|
+
line += ` \u{1F6AB} Cancelled: ${result.error.message}`;
|
|
126
|
+
} else {
|
|
127
|
+
const error_name = result.error instanceof Error ? result.error.name : "Error";
|
|
128
|
+
const error_message = result.error instanceof Error ? result.error.message : String(result.error);
|
|
129
|
+
line += ` \u2620\uFE0F ${error_name}: ${error_message}`;
|
|
130
|
+
}
|
|
131
|
+
} else if (result.status === "completed") {
|
|
132
|
+
line += ` \u2192 ${formatResultValue(result.result)}`;
|
|
133
|
+
}
|
|
134
|
+
const extension = is_last ? " " : "\u2502 ";
|
|
135
|
+
const new_indent = indent + extension;
|
|
136
|
+
const direct_children = result.event_children;
|
|
137
|
+
if (direct_children.length === 0) {
|
|
138
|
+
return line;
|
|
139
|
+
}
|
|
140
|
+
const child_lines = [];
|
|
141
|
+
const parent_children = parent_to_children.get(result.event_id) ?? [];
|
|
142
|
+
const emitted_children = parent_children.filter((child) => child.event_emitted_by_handler_id === result.handler_id);
|
|
143
|
+
const children_by_id = /* @__PURE__ */ new Map();
|
|
144
|
+
direct_children.forEach((child) => {
|
|
145
|
+
children_by_id.set(child.event_id, child);
|
|
146
|
+
});
|
|
147
|
+
emitted_children.forEach((child) => {
|
|
148
|
+
if (!children_by_id.has(child.event_id)) {
|
|
149
|
+
children_by_id.set(child.event_id, child);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
const children_to_print = Array.from(children_by_id.values()).filter((child) => !visited.has(child.event_id));
|
|
153
|
+
children_to_print.forEach((child, index) => {
|
|
154
|
+
child_lines.push(buildTreeLine(child, new_indent, index === children_to_print.length - 1, parent_to_children, visited));
|
|
155
|
+
});
|
|
156
|
+
return [line, ...child_lines].join("\n");
|
|
157
|
+
};
|
|
158
|
+
const formatTimestamp = (value) => {
|
|
159
|
+
if (!value) {
|
|
160
|
+
return "N/A";
|
|
161
|
+
}
|
|
162
|
+
const date = new Date(value);
|
|
163
|
+
if (Number.isNaN(date.getTime())) {
|
|
164
|
+
return "N/A";
|
|
165
|
+
}
|
|
166
|
+
return date.toISOString().slice(11, 23);
|
|
167
|
+
};
|
|
168
|
+
const formatResultValue = (value) => {
|
|
169
|
+
if (value === null || value === void 0) {
|
|
170
|
+
return "None";
|
|
171
|
+
}
|
|
172
|
+
if (value instanceof BaseEvent) {
|
|
173
|
+
return `Event(${value.event_type}#${value.event_id.slice(-4)})`;
|
|
174
|
+
}
|
|
175
|
+
if (typeof value === "string") {
|
|
176
|
+
return JSON.stringify(value);
|
|
177
|
+
}
|
|
178
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
179
|
+
return String(value);
|
|
180
|
+
}
|
|
181
|
+
if (Array.isArray(value)) {
|
|
182
|
+
return `list(${value.length} items)`;
|
|
183
|
+
}
|
|
184
|
+
if (typeof value === "object") {
|
|
185
|
+
return `dict(${Object.keys(value).length} items)`;
|
|
186
|
+
}
|
|
187
|
+
return `${typeof value}(...)`;
|
|
188
|
+
};
|
|
189
|
+
export {
|
|
190
|
+
buildResultLine,
|
|
191
|
+
buildTreeLine,
|
|
192
|
+
formatResultValue,
|
|
193
|
+
formatTimestamp,
|
|
194
|
+
logTree
|
|
195
|
+
};
|
|
196
|
+
//# sourceMappingURL=logging.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/logging.ts"],
|
|
4
|
+
"sourcesContent": ["import { BaseEvent } from './base_event.js'\nimport { EventResult } from './event_result.js'\nimport { EventHandlerCancelledError, EventHandlerTimeoutError } from './event_handler.js'\n\ntype LogTreeBus = {\n name: string\n event_history: {\n values(): IterableIterator<BaseEvent>\n has(event_id: string): boolean\n }\n toString?: () => string\n}\n\nexport const logTree = (bus: LogTreeBus): string => {\n const parent_to_children = new Map<string, BaseEvent[]>()\n\n const addChild = (parent_id: string, child: BaseEvent): void => {\n const existing = parent_to_children.get(parent_id) ?? []\n existing.push(child)\n parent_to_children.set(parent_id, existing)\n }\n\n const root_events: BaseEvent[] = []\n const seen = new Set<string>()\n\n for (const event of bus.event_history.values()) {\n const parent_id = event.event_parent_id\n if (!parent_id || parent_id === event.event_id || !bus.event_history.has(parent_id)) {\n if (!seen.has(event.event_id)) {\n root_events.push(event)\n seen.add(event.event_id)\n }\n }\n }\n\n if (root_events.length === 0) {\n return '(No events in history)'\n }\n\n const nodes_by_id = new Map<string, BaseEvent>()\n for (const root of root_events) {\n nodes_by_id.set(root.event_id, root)\n for (const descendant of root.event_descendants) {\n nodes_by_id.set(descendant.event_id, descendant)\n }\n }\n\n for (const node of nodes_by_id.values()) {\n const parent_id = node.event_parent_id\n if (!parent_id || parent_id === node.event_id) {\n continue\n }\n if (!nodes_by_id.has(parent_id)) {\n continue\n }\n addChild(parent_id, node)\n }\n\n for (const children of parent_to_children.values()) {\n children.sort((a, b) => (a.event_created_at < b.event_created_at ? -1 : a.event_created_at > b.event_created_at ? 1 : 0))\n }\n\n const lines: string[] = []\n const bus_label = typeof bus.toString === 'function' ? bus.toString() : bus.name\n lines.push(`\uD83D\uDCCA Event History Tree for ${bus_label}`)\n lines.push('='.repeat(80))\n\n root_events.sort((a, b) => (a.event_created_at < b.event_created_at ? -1 : a.event_created_at > b.event_created_at ? 1 : 0))\n const visited = new Set<string>()\n root_events.forEach((event, index) => {\n lines.push(buildTreeLine(event, '', index === root_events.length - 1, parent_to_children, visited))\n })\n\n lines.push('='.repeat(80))\n\n return lines.join('\\n')\n}\n\nexport const buildTreeLine = (\n event: BaseEvent,\n indent: string,\n is_last: boolean,\n parent_to_children: Map<string, BaseEvent[]>,\n visited: Set<string>\n): string => {\n const connector = is_last ? '\u2514\u2500\u2500 ' : '\u251C\u2500\u2500 '\n const status_icon = event.event_status === 'completed' ? '\u2705' : event.event_status === 'started' ? '\uD83C\uDFC3' : '\u23F3'\n\n const created_at = formatTimestamp(event.event_created_at)\n let timing = `[${created_at}`\n if (event.event_completed_at) {\n const created_ms = Date.parse(event.event_created_at)\n const completed_ms = Date.parse(event.event_completed_at)\n if (!Number.isNaN(created_ms) && !Number.isNaN(completed_ms)) {\n const duration = (completed_ms - created_ms) / 1000\n timing += ` (${duration.toFixed(3)}s)`\n }\n }\n timing += ']'\n\n const line = `${indent}${connector}${status_icon} ${event.event_type}#${event.event_id.slice(-4)} ${timing}`\n\n if (visited.has(event.event_id)) {\n return line\n }\n visited.add(event.event_id)\n\n const extension = is_last ? ' ' : '\u2502 '\n const new_indent = indent + extension\n\n const result_items: Array<{ type: 'result'; result: EventResult } | { type: 'child'; child: BaseEvent }> = []\n for (const result of event.event_results.values()) {\n result_items.push({ type: 'result', result })\n }\n const children = parent_to_children.get(event.event_id) ?? []\n const printed_child_ids = new Set<string>(event.event_results.size > 0 ? event.event_results.keys() : [])\n for (const child of children) {\n if (!printed_child_ids.has(child.event_id) && !child.event_emitted_by_handler_id) {\n result_items.push({ type: 'child', child })\n printed_child_ids.add(child.event_id)\n }\n }\n\n if (result_items.length === 0) {\n return line\n }\n\n const child_lines: string[] = []\n result_items.forEach((item, index) => {\n const is_last_item = index === result_items.length - 1\n if (item.type === 'result') {\n child_lines.push(buildResultLine(item.result, new_indent, is_last_item, parent_to_children, visited))\n } else {\n child_lines.push(buildTreeLine(item.child, new_indent, is_last_item, parent_to_children, visited))\n }\n })\n\n return [line, ...child_lines].join('\\n')\n}\n\nexport const buildResultLine = (\n result: EventResult,\n indent: string,\n is_last: boolean,\n parent_to_children: Map<string, BaseEvent[]>,\n visited: Set<string>\n): string => {\n const connector = is_last ? '\u2514\u2500\u2500 ' : '\u251C\u2500\u2500 '\n const status_icon = result.status === 'completed' ? '\u2705' : result.status === 'error' ? '\u274C' : result.status === 'started' ? '\uD83C\uDFC3' : '\u23F3'\n\n const handler_label =\n result.handler_name && result.handler_name !== 'anonymous'\n ? result.handler_name\n : result.handler_file_path\n ? result.handler_file_path\n : 'anonymous'\n const handler_display = `${result.eventbus_label}.${handler_label}#${result.handler_id.slice(-4)}`\n let line = `${indent}${connector}${status_icon} ${handler_display}`\n\n if (result.started_at) {\n line += ` [${formatTimestamp(result.started_at)}`\n if (result.completed_at) {\n const started_ms = Date.parse(result.started_at)\n const completed_ms = Date.parse(result.completed_at)\n if (!Number.isNaN(started_ms) && !Number.isNaN(completed_ms)) {\n const duration = (completed_ms - started_ms) / 1000\n line += ` (${duration.toFixed(3)}s)`\n }\n }\n line += ']'\n }\n\n if (result.status === 'error' && result.error) {\n if (result.error instanceof EventHandlerTimeoutError) {\n line += ` \u23F1\uFE0F Timeout: ${result.error.message}`\n } else if (result.error instanceof EventHandlerCancelledError) {\n line += ` \uD83D\uDEAB Cancelled: ${result.error.message}`\n } else {\n const error_name = result.error instanceof Error ? result.error.name : 'Error'\n const error_message = result.error instanceof Error ? result.error.message : String(result.error)\n line += ` \u2620\uFE0F ${error_name}: ${error_message}`\n }\n } else if (result.status === 'completed') {\n line += ` \u2192 ${formatResultValue(result.result)}`\n }\n\n const extension = is_last ? ' ' : '\u2502 '\n const new_indent = indent + extension\n\n const direct_children = result.event_children\n if (direct_children.length === 0) {\n return line\n }\n\n const child_lines: string[] = []\n const parent_children = parent_to_children.get(result.event_id) ?? []\n const emitted_children = parent_children.filter((child) => child.event_emitted_by_handler_id === result.handler_id)\n const children_by_id = new Map<string, BaseEvent>()\n direct_children.forEach((child) => {\n children_by_id.set(child.event_id, child)\n })\n emitted_children.forEach((child) => {\n if (!children_by_id.has(child.event_id)) {\n children_by_id.set(child.event_id, child)\n }\n })\n const children_to_print = Array.from(children_by_id.values()).filter((child) => !visited.has(child.event_id))\n\n children_to_print.forEach((child, index) => {\n child_lines.push(buildTreeLine(child, new_indent, index === children_to_print.length - 1, parent_to_children, visited))\n })\n\n return [line, ...child_lines].join('\\n')\n}\n\nexport const formatTimestamp = (value?: string): string => {\n if (!value) {\n return 'N/A'\n }\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) {\n return 'N/A'\n }\n return date.toISOString().slice(11, 23)\n}\n\nexport const formatResultValue = (value: unknown): string => {\n if (value === null || value === undefined) {\n return 'None'\n }\n if (value instanceof BaseEvent) {\n return `Event(${value.event_type}#${value.event_id.slice(-4)})`\n }\n if (typeof value === 'string') {\n return JSON.stringify(value)\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value)\n }\n if (Array.isArray(value)) {\n return `list(${value.length} items)`\n }\n if (typeof value === 'object') {\n return `dict(${Object.keys(value as Record<string, unknown>).length} items)`\n }\n return `${typeof value}(...)`\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=middlewares.js.map
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const isNodeRuntime = () => {
|
|
2
|
+
const maybe_process = globalThis.process;
|
|
3
|
+
return typeof maybe_process?.versions?.node === "string";
|
|
4
|
+
};
|
|
5
|
+
const missingDependencyError = (bridge_name, package_name) => new Error(`${bridge_name} requires optional dependency "${package_name}". Install it with: npm install ${package_name}`);
|
|
6
|
+
const assertOptionalDependencyAvailable = (bridge_name, package_name) => {
|
|
7
|
+
if (!isNodeRuntime()) return;
|
|
8
|
+
const maybe_process = globalThis.process;
|
|
9
|
+
const get_builtin_module = maybe_process?.getBuiltinModule;
|
|
10
|
+
let require_fn;
|
|
11
|
+
try {
|
|
12
|
+
require_fn = Function('return typeof require === "function" ? require : undefined')();
|
|
13
|
+
} catch {
|
|
14
|
+
require_fn = void 0;
|
|
15
|
+
}
|
|
16
|
+
if (!require_fn && typeof get_builtin_module === "function") {
|
|
17
|
+
const module_builtin = get_builtin_module("module");
|
|
18
|
+
const create_require = module_builtin?.createRequire;
|
|
19
|
+
if (typeof create_require === "function") {
|
|
20
|
+
const cwd = typeof maybe_process?.cwd === "function" ? maybe_process.cwd() : ".";
|
|
21
|
+
require_fn = create_require(`${cwd}/package.json`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (!require_fn) return;
|
|
25
|
+
try {
|
|
26
|
+
require_fn.resolve(package_name);
|
|
27
|
+
} catch {
|
|
28
|
+
throw missingDependencyError(bridge_name, package_name);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const importOptionalDependency = async (bridge_name, package_name) => {
|
|
32
|
+
const dynamic_import = Function("module_name", "return import(module_name)");
|
|
33
|
+
try {
|
|
34
|
+
return await dynamic_import(package_name);
|
|
35
|
+
} catch {
|
|
36
|
+
throw missingDependencyError(bridge_name, package_name);
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
export {
|
|
40
|
+
assertOptionalDependencyAvailable,
|
|
41
|
+
importOptionalDependency,
|
|
42
|
+
isNodeRuntime
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=optional_deps.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/optional_deps.ts"],
|
|
4
|
+
"sourcesContent": ["export const isNodeRuntime = (): boolean => {\n const maybe_process = (globalThis as { process?: { versions?: { node?: string } } }).process\n return typeof maybe_process?.versions?.node === 'string'\n}\n\nconst missingDependencyError = (bridge_name: string, package_name: string): Error =>\n new Error(`${bridge_name} requires optional dependency \"${package_name}\". Install it with: npm install ${package_name}`)\n\nexport const assertOptionalDependencyAvailable = (bridge_name: string, package_name: string): void => {\n if (!isNodeRuntime()) return\n\n const maybe_process = (\n globalThis as {\n process?: { getBuiltinModule?: (name: string) => any; cwd?: () => string }\n }\n ).process\n const get_builtin_module = maybe_process?.getBuiltinModule\n\n let require_fn: { resolve: (specifier: string) => string } | undefined\n try {\n require_fn = Function('return typeof require === \"function\" ? require : undefined')() as\n | { resolve: (specifier: string) => string }\n | undefined\n } catch {\n require_fn = undefined\n }\n\n if (!require_fn && typeof get_builtin_module === 'function') {\n const module_builtin = get_builtin_module('module')\n const create_require = module_builtin?.createRequire\n if (typeof create_require === 'function') {\n const cwd = typeof maybe_process?.cwd === 'function' ? maybe_process.cwd() : '.'\n require_fn = create_require(`${cwd}/package.json`) as { resolve: (specifier: string) => string }\n }\n }\n\n if (!require_fn) return\n try {\n require_fn.resolve(package_name)\n } catch {\n throw missingDependencyError(bridge_name, package_name)\n }\n}\n\nexport const importOptionalDependency = async (bridge_name: string, package_name: string): Promise<any> => {\n const dynamic_import = Function('module_name', 'return import(module_name)') as (module_name: string) => Promise<unknown>\n try {\n return (await dynamic_import(package_name)) as any\n } catch {\n throw missingDependencyError(bridge_name, package_name)\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAO,MAAM,gBAAgB,MAAe;AAC1C,QAAM,gBAAiB,WAA8D;AACrF,SAAO,OAAO,eAAe,UAAU,SAAS;AAClD;AAEA,MAAM,yBAAyB,CAAC,aAAqB,iBACnD,IAAI,MAAM,GAAG,WAAW,kCAAkC,YAAY,mCAAmC,YAAY,EAAE;AAElH,MAAM,oCAAoC,CAAC,aAAqB,iBAA+B;AACpG,MAAI,CAAC,cAAc,EAAG;AAEtB,QAAM,gBACJ,WAGA;AACF,QAAM,qBAAqB,eAAe;AAE1C,MAAI;AACJ,MAAI;AACF,iBAAa,SAAS,4DAA4D,EAAE;AAAA,EAGtF,QAAQ;AACN,iBAAa;AAAA,EACf;AAEA,MAAI,CAAC,cAAc,OAAO,uBAAuB,YAAY;AAC3D,UAAM,iBAAiB,mBAAmB,QAAQ;AAClD,UAAM,iBAAiB,gBAAgB;AACvC,QAAI,OAAO,mBAAmB,YAAY;AACxC,YAAM,MAAM,OAAO,eAAe,QAAQ,aAAa,cAAc,IAAI,IAAI;AAC7E,mBAAa,eAAe,GAAG,GAAG,eAAe;AAAA,IACnD;AAAA,EACF;AAEA,MAAI,CAAC,WAAY;AACjB,MAAI;AACF,eAAW,QAAQ,YAAY;AAAA,EACjC,QAAQ;AACN,UAAM,uBAAuB,aAAa,YAAY;AAAA,EACxD;AACF;AAEO,MAAM,2BAA2B,OAAO,aAAqB,iBAAuC;AACzG,QAAM,iBAAiB,SAAS,eAAe,4BAA4B;AAC3E,MAAI;AACF,WAAQ,MAAM,eAAe,YAAY;AAAA,EAC3C,QAAQ;AACN,UAAM,uBAAuB,aAAa,YAAY;AAAA,EACxD;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { createAsyncLocalStorage } from "./async_context.js";
|
|
2
|
+
class RetryTimeoutError extends Error {
|
|
3
|
+
timeout_seconds;
|
|
4
|
+
attempt;
|
|
5
|
+
constructor(message, params) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = "RetryTimeoutError";
|
|
8
|
+
this.timeout_seconds = params.timeout_seconds;
|
|
9
|
+
this.attempt = params.attempt;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
class SemaphoreTimeoutError extends Error {
|
|
13
|
+
semaphore_name;
|
|
14
|
+
semaphore_limit;
|
|
15
|
+
timeout_seconds;
|
|
16
|
+
constructor(message, params) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "SemaphoreTimeoutError";
|
|
19
|
+
this.semaphore_name = params.semaphore_name;
|
|
20
|
+
this.semaphore_limit = params.semaphore_limit;
|
|
21
|
+
this.timeout_seconds = params.timeout_seconds;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const retry_context_storage = createAsyncLocalStorage();
|
|
25
|
+
function getHeldSemaphores() {
|
|
26
|
+
return retry_context_storage?.getStore() ?? /* @__PURE__ */ new Set();
|
|
27
|
+
}
|
|
28
|
+
function runWithHeldSemaphores(held, fn) {
|
|
29
|
+
if (!retry_context_storage) return fn();
|
|
30
|
+
return retry_context_storage.run(held, fn);
|
|
31
|
+
}
|
|
32
|
+
let _next_instance_id = 1;
|
|
33
|
+
const _instance_ids = /* @__PURE__ */ new WeakMap();
|
|
34
|
+
function scopedSemaphoreKey(base_name, scope, context) {
|
|
35
|
+
if (scope === "class" && context && typeof context === "object") {
|
|
36
|
+
return `${context.constructor?.name ?? "Object"}.${base_name}`;
|
|
37
|
+
}
|
|
38
|
+
if (scope === "instance" && context && typeof context === "object") {
|
|
39
|
+
let id = _instance_ids.get(context);
|
|
40
|
+
if (id === void 0) {
|
|
41
|
+
id = _next_instance_id++;
|
|
42
|
+
_instance_ids.set(context, id);
|
|
43
|
+
}
|
|
44
|
+
return `${id}.${base_name}`;
|
|
45
|
+
}
|
|
46
|
+
return base_name;
|
|
47
|
+
}
|
|
48
|
+
class RetrySemaphore {
|
|
49
|
+
size;
|
|
50
|
+
inUse;
|
|
51
|
+
waiters;
|
|
52
|
+
constructor(size) {
|
|
53
|
+
this.size = size;
|
|
54
|
+
this.inUse = 0;
|
|
55
|
+
this.waiters = [];
|
|
56
|
+
}
|
|
57
|
+
async acquire() {
|
|
58
|
+
if (this.size === Infinity) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (this.inUse < this.size) {
|
|
62
|
+
this.inUse += 1;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
await new Promise((resolve) => {
|
|
66
|
+
this.waiters.push(resolve);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
release() {
|
|
70
|
+
if (this.size === Infinity) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const next = this.waiters.shift();
|
|
74
|
+
if (next) {
|
|
75
|
+
next();
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this.inUse = Math.max(0, this.inUse - 1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const SEMAPHORE_REGISTRY = /* @__PURE__ */ new Map();
|
|
82
|
+
function getOrCreateSemaphore(name, limit) {
|
|
83
|
+
const existing = SEMAPHORE_REGISTRY.get(name);
|
|
84
|
+
if (existing && existing.size === limit) return existing;
|
|
85
|
+
const sem = new RetrySemaphore(limit);
|
|
86
|
+
SEMAPHORE_REGISTRY.set(name, sem);
|
|
87
|
+
return sem;
|
|
88
|
+
}
|
|
89
|
+
function clearSemaphoreRegistry() {
|
|
90
|
+
SEMAPHORE_REGISTRY.clear();
|
|
91
|
+
}
|
|
92
|
+
function retry(options = {}) {
|
|
93
|
+
const {
|
|
94
|
+
max_attempts = 1,
|
|
95
|
+
retry_after = 0,
|
|
96
|
+
retry_backoff_factor = 1,
|
|
97
|
+
retry_on_errors,
|
|
98
|
+
timeout,
|
|
99
|
+
semaphore_limit,
|
|
100
|
+
semaphore_name: semaphore_name_option,
|
|
101
|
+
semaphore_lax = true,
|
|
102
|
+
semaphore_scope = "global",
|
|
103
|
+
semaphore_timeout
|
|
104
|
+
} = options;
|
|
105
|
+
return function decorator(target, _context) {
|
|
106
|
+
const fn_name = target.name || _context?.name || "anonymous";
|
|
107
|
+
const effective_max_attempts = Math.max(1, max_attempts);
|
|
108
|
+
const effective_retry_after = Math.max(0, retry_after);
|
|
109
|
+
async function retryWrapper(...args) {
|
|
110
|
+
const base_name = typeof semaphore_name_option === "function" ? semaphore_name_option(...args) : semaphore_name_option ?? fn_name;
|
|
111
|
+
const sem_name = typeof base_name === "string" ? base_name : String(base_name);
|
|
112
|
+
const scoped_key = scopedSemaphoreKey(sem_name, semaphore_scope, this);
|
|
113
|
+
const held = getHeldSemaphores();
|
|
114
|
+
const needs_semaphore = semaphore_limit != null && semaphore_limit > 0;
|
|
115
|
+
const is_reentrant = needs_semaphore && held.has(scoped_key);
|
|
116
|
+
let semaphore = null;
|
|
117
|
+
let semaphore_acquired = false;
|
|
118
|
+
if (needs_semaphore && !is_reentrant) {
|
|
119
|
+
semaphore = getOrCreateSemaphore(scoped_key, semaphore_limit);
|
|
120
|
+
const effective_sem_timeout = semaphore_timeout != null ? semaphore_timeout : timeout != null ? timeout * Math.max(1, semaphore_limit - 1) : null;
|
|
121
|
+
if (effective_sem_timeout != null && effective_sem_timeout > 0) {
|
|
122
|
+
semaphore_acquired = await acquireWithTimeout(semaphore, effective_sem_timeout * 1e3);
|
|
123
|
+
if (!semaphore_acquired) {
|
|
124
|
+
if (!semaphore_lax) {
|
|
125
|
+
throw new SemaphoreTimeoutError(
|
|
126
|
+
`Failed to acquire semaphore "${scoped_key}" within ${effective_sem_timeout}s (limit=${semaphore_limit})`,
|
|
127
|
+
{ semaphore_name: scoped_key, semaphore_limit, timeout_seconds: effective_sem_timeout }
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
await semaphore.acquire();
|
|
133
|
+
semaphore_acquired = true;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
const new_held = new Set(held);
|
|
137
|
+
if (semaphore_acquired) {
|
|
138
|
+
new_held.add(scoped_key);
|
|
139
|
+
}
|
|
140
|
+
const runRetryLoop = async () => {
|
|
141
|
+
for (let attempt = 1; attempt <= effective_max_attempts; attempt++) {
|
|
142
|
+
try {
|
|
143
|
+
if (timeout != null && timeout > 0) {
|
|
144
|
+
return await _runWithTimeout(() => Promise.resolve(target.apply(this, args)), timeout * 1e3, attempt);
|
|
145
|
+
} else {
|
|
146
|
+
return await Promise.resolve(target.apply(this, args));
|
|
147
|
+
}
|
|
148
|
+
} catch (error) {
|
|
149
|
+
if (retry_on_errors && retry_on_errors.length > 0) {
|
|
150
|
+
const is_retryable = retry_on_errors.some(
|
|
151
|
+
(matcher) => typeof matcher === "string" ? error?.name === matcher : matcher instanceof RegExp ? matcher.test(String(error)) : error instanceof matcher
|
|
152
|
+
);
|
|
153
|
+
if (!is_retryable) throw error;
|
|
154
|
+
}
|
|
155
|
+
if (attempt >= effective_max_attempts) throw error;
|
|
156
|
+
const delay_seconds = effective_retry_after * Math.pow(retry_backoff_factor, attempt - 1);
|
|
157
|
+
if (delay_seconds > 0) {
|
|
158
|
+
await sleep(delay_seconds * 1e3);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
throw new Error(`retry(${fn_name}): unexpected end of retry loop`);
|
|
163
|
+
};
|
|
164
|
+
try {
|
|
165
|
+
return await runWithHeldSemaphores(new_held, runRetryLoop);
|
|
166
|
+
} finally {
|
|
167
|
+
if (semaphore_acquired && semaphore) {
|
|
168
|
+
semaphore.release();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
Object.defineProperty(retryWrapper, "name", { value: fn_name, configurable: true });
|
|
173
|
+
return retryWrapper;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
async function acquireWithTimeout(semaphore, timeout_ms) {
|
|
177
|
+
return new Promise((resolve) => {
|
|
178
|
+
let settled = false;
|
|
179
|
+
const timer = setTimeout(() => {
|
|
180
|
+
if (!settled) {
|
|
181
|
+
settled = true;
|
|
182
|
+
resolve(false);
|
|
183
|
+
}
|
|
184
|
+
}, timeout_ms);
|
|
185
|
+
semaphore.acquire().then(() => {
|
|
186
|
+
if (!settled) {
|
|
187
|
+
settled = true;
|
|
188
|
+
clearTimeout(timer);
|
|
189
|
+
resolve(true);
|
|
190
|
+
} else {
|
|
191
|
+
semaphore.release();
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
async function _runWithTimeout(fn, timeout_ms, attempt) {
|
|
197
|
+
return new Promise((resolve, reject) => {
|
|
198
|
+
let settled = false;
|
|
199
|
+
const timer = setTimeout(() => {
|
|
200
|
+
if (!settled) {
|
|
201
|
+
settled = true;
|
|
202
|
+
reject(
|
|
203
|
+
new RetryTimeoutError(`Timed out after ${timeout_ms / 1e3}s (attempt ${attempt})`, {
|
|
204
|
+
timeout_seconds: timeout_ms / 1e3,
|
|
205
|
+
attempt
|
|
206
|
+
})
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}, timeout_ms);
|
|
210
|
+
fn().then(
|
|
211
|
+
(value) => {
|
|
212
|
+
if (!settled) {
|
|
213
|
+
settled = true;
|
|
214
|
+
clearTimeout(timer);
|
|
215
|
+
resolve(value);
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
(error) => {
|
|
219
|
+
if (!settled) {
|
|
220
|
+
settled = true;
|
|
221
|
+
clearTimeout(timer);
|
|
222
|
+
reject(error);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
function sleep(ms) {
|
|
229
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
230
|
+
}
|
|
231
|
+
export {
|
|
232
|
+
RetryTimeoutError,
|
|
233
|
+
SemaphoreTimeoutError,
|
|
234
|
+
clearSemaphoreRegistry,
|
|
235
|
+
retry
|
|
236
|
+
};
|
|
237
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/retry.ts"],
|
|
4
|
+
"sourcesContent": ["import { createAsyncLocalStorage, type AsyncLocalStorageLike } from './async_context.js'\n\n// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface RetryOptions {\n /** Total number of attempts including the initial call (1 = no retry, 3 = up to 2 retries). Default: 1 */\n max_attempts?: number\n\n /** Seconds to wait between retries. Default: 0 */\n retry_after?: number\n\n /** Multiplier applied to retry_after after each attempt for exponential backoff. Default: 1.0 (constant delay) */\n retry_backoff_factor?: number\n\n /** Only retry when the thrown error matches one of these matchers. Accepts error class constructors,\n * string error names (matched against error.name), or RegExp patterns (tested against String(error)).\n * Default: undefined (retry on any error) */\n retry_on_errors?: Array<(new (...args: any[]) => Error) | string | RegExp>\n\n /** Per-attempt timeout in seconds. Default: undefined (no per-attempt timeout) */\n timeout?: number | null\n\n /** Maximum concurrent executions sharing this semaphore. Default: undefined (no concurrency limit) */\n semaphore_limit?: number | null\n\n /** Semaphore identifier. Functions with the same name share the same concurrency slot pool. Default: function name.\n * If a function is provided, it receives the same arguments as the wrapped function. */\n semaphore_name?: string | ((...args: any[]) => string) | null\n\n /** If true, proceed without concurrency limit when semaphore acquisition times out. Default: true */\n semaphore_lax?: boolean\n\n /** Semaphore scoping strategy. Default: 'global'\n * - 'global': all calls share one semaphore (keyed by semaphore_name)\n * - 'class': all instances of the same class share one semaphore (keyed by className.semaphore_name)\n * - 'instance': each object instance gets its own semaphore (keyed by instanceId.semaphore_name)\n * 'class' and 'instance' require `this` to be an object; they fall back to 'global' for standalone calls. */\n semaphore_scope?: 'global' | 'class' | 'instance'\n\n /** Maximum seconds to wait for semaphore acquisition. Default: undefined \u2192 timeout * max(1, limit - 1) */\n semaphore_timeout?: number | null\n}\n\n// \u2500\u2500\u2500 Errors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Thrown when a single attempt exceeds the per-attempt timeout. */\nexport class RetryTimeoutError extends Error {\n timeout_seconds: number\n attempt: number\n\n constructor(message: string, params: { timeout_seconds: number; attempt: number }) {\n super(message)\n this.name = 'RetryTimeoutError'\n this.timeout_seconds = params.timeout_seconds\n this.attempt = params.attempt\n }\n}\n\n/** Thrown (when semaphore_lax=false) if the semaphore cannot be acquired within the timeout. */\nexport class SemaphoreTimeoutError extends Error {\n semaphore_name: string\n semaphore_limit: number\n timeout_seconds: number\n\n constructor(message: string, params: { semaphore_name: string; semaphore_limit: number; timeout_seconds: number }) {\n super(message)\n this.name = 'SemaphoreTimeoutError'\n this.semaphore_name = params.semaphore_name\n this.semaphore_limit = params.semaphore_limit\n this.timeout_seconds = params.timeout_seconds\n }\n}\n\n// \u2500\u2500\u2500 Re-entrancy tracking via AsyncLocalStorage \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Prevents deadlocks when a retry()-wrapped function calls another retry()-wrapped\n// function that shares the same semaphore (or calls itself recursively).\n//\n// Each async call stack tracks which semaphore names it currently holds. When a\n// nested call encounters a semaphore it already holds, it skips acquisition and\n// runs directly within the parent's slot.\n//\n// Uses the same AsyncLocalStorage polyfill as the rest of abxbus (see async_context.ts)\n// so it works in Node.js and gracefully degrades to a no-op in browsers.\n\ntype ReentrantStore = Set<string>\n\n// Separate AsyncLocalStorage instance for retry re-entrancy tracking.\n// Created via the shared factory in async_context.ts (returns null in browsers).\nconst retry_context_storage: AsyncLocalStorageLike | null = createAsyncLocalStorage()\n\nfunction getHeldSemaphores(): ReentrantStore {\n return (retry_context_storage?.getStore() as ReentrantStore | undefined) ?? new Set()\n}\n\nfunction runWithHeldSemaphores<T>(held: ReentrantStore, fn: () => T): T {\n if (!retry_context_storage) return fn()\n return retry_context_storage.run(held, fn)\n}\n\n// \u2500\u2500\u2500 Semaphore scope helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nlet _next_instance_id = 1\nconst _instance_ids = new WeakMap<object, number>()\n\nfunction scopedSemaphoreKey(base_name: string, scope: 'global' | 'class' | 'instance', context: unknown): string {\n if (scope === 'class' && context && typeof context === 'object') {\n return `${(context as object).constructor?.name ?? 'Object'}.${base_name}`\n }\n if (scope === 'instance' && context && typeof context === 'object') {\n let id = _instance_ids.get(context as object)\n if (id === undefined) {\n id = _next_instance_id++\n _instance_ids.set(context as object, id)\n }\n return `${id}.${base_name}`\n }\n return base_name\n}\n\n// \u2500\u2500\u2500 Global semaphore registry \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nclass RetrySemaphore {\n readonly size: number\n private inUse: number\n private waiters: Array<() => void>\n\n constructor(size: number) {\n this.size = size\n this.inUse = 0\n this.waiters = []\n }\n\n async acquire(): Promise<void> {\n if (this.size === Infinity) {\n return\n }\n if (this.inUse < this.size) {\n this.inUse += 1\n return\n }\n await new Promise<void>((resolve) => {\n this.waiters.push(resolve)\n })\n }\n\n release(): void {\n if (this.size === Infinity) {\n return\n }\n const next = this.waiters.shift()\n if (next) {\n // Handoff: keep the permit accounted for and transfer it directly to the waiter.\n next()\n return\n }\n this.inUse = Math.max(0, this.inUse - 1)\n }\n}\n\nconst SEMAPHORE_REGISTRY = new Map<string, RetrySemaphore>()\n\nfunction getOrCreateSemaphore(name: string, limit: number): RetrySemaphore {\n const existing = SEMAPHORE_REGISTRY.get(name)\n if (existing && existing.size === limit) return existing\n const sem = new RetrySemaphore(limit)\n SEMAPHORE_REGISTRY.set(name, sem)\n return sem\n}\n\n/** Reset the global semaphore registry. Useful in tests. */\nexport function clearSemaphoreRegistry(): void {\n SEMAPHORE_REGISTRY.clear()\n}\n\n// \u2500\u2500\u2500 retry() decorator / higher-order wrapper \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// Usage as a higher-order function (works on any async function):\n//\n// const fetchWithRetry = retry({ max_attempts: 3, retry_after: 1 })(async (url: string) => {\n// return await fetch(url)\n// })\n//\n// Usage as a TC39 Stage 3 decorator on class methods (TS 5.0+):\n//\n// class ApiClient {\n// @retry({ max_attempts: 3, retry_after: 1 })\n// async fetchData(): Promise<Data> { ... }\n// }\n//\n// Usage on event bus handlers:\n//\n// bus.on(MyEvent, retry({ max_attempts: 3 })(async (event) => {\n// await riskyOperation(event.data)\n// }))\n\nexport function retry(options: RetryOptions = {}) {\n const {\n max_attempts = 1,\n retry_after = 0,\n retry_backoff_factor = 1.0,\n retry_on_errors,\n timeout,\n semaphore_limit,\n semaphore_name: semaphore_name_option,\n semaphore_lax = true,\n semaphore_scope = 'global',\n semaphore_timeout,\n } = options\n\n return function decorator<T extends (...args: any[]) => any>(target: T, _context?: ClassMethodDecoratorContext): T {\n const fn_name = target.name || (_context?.name as string) || 'anonymous'\n const effective_max_attempts = Math.max(1, max_attempts)\n const effective_retry_after = Math.max(0, retry_after)\n\n async function retryWrapper(this: any, ...args: any[]): Promise<any> {\n const base_name = typeof semaphore_name_option === 'function' ? semaphore_name_option(...args) : (semaphore_name_option ?? fn_name)\n const sem_name = typeof base_name === 'string' ? base_name : String(base_name)\n // \u2500\u2500 Resolve scoped semaphore key at call time (uses `this` for class/instance scopes) \u2500\u2500\n const scoped_key = scopedSemaphoreKey(sem_name, semaphore_scope, this)\n\n // \u2500\u2500 Check re-entrancy: skip semaphore if we already hold it in this async context \u2500\u2500\n const held = getHeldSemaphores()\n const needs_semaphore = semaphore_limit != null && semaphore_limit > 0\n const is_reentrant = needs_semaphore && held.has(scoped_key)\n\n // \u2500\u2500 Semaphore acquisition (held across all retry attempts, skipped if re-entrant) \u2500\u2500\n let semaphore: RetrySemaphore | null = null\n let semaphore_acquired = false\n\n if (needs_semaphore && !is_reentrant) {\n semaphore = getOrCreateSemaphore(scoped_key, semaphore_limit!)\n\n const effective_sem_timeout =\n semaphore_timeout != null ? semaphore_timeout : timeout != null ? timeout * Math.max(1, semaphore_limit! - 1) : null\n\n if (effective_sem_timeout != null && effective_sem_timeout > 0) {\n semaphore_acquired = await acquireWithTimeout(semaphore, effective_sem_timeout * 1000)\n if (!semaphore_acquired) {\n if (!semaphore_lax) {\n throw new SemaphoreTimeoutError(\n `Failed to acquire semaphore \"${scoped_key}\" within ${effective_sem_timeout}s (limit=${semaphore_limit})`,\n { semaphore_name: scoped_key, semaphore_limit: semaphore_limit!, timeout_seconds: effective_sem_timeout }\n )\n }\n // lax mode: proceed without concurrency limit\n }\n } else {\n // No timeout configured: wait indefinitely for a slot\n await semaphore.acquire()\n semaphore_acquired = true\n }\n }\n\n // \u2500\u2500 Build the set of held semaphores for nested calls \u2500\u2500\n const new_held = new Set(held)\n if (semaphore_acquired) {\n new_held.add(scoped_key)\n }\n\n // \u2500\u2500 Retry loop (runs inside the semaphore and re-entrancy context) \u2500\u2500\n const runRetryLoop = async (): Promise<any> => {\n for (let attempt = 1; attempt <= effective_max_attempts; attempt++) {\n try {\n if (timeout != null && timeout > 0) {\n return await _runWithTimeout(() => Promise.resolve(target.apply(this, args)), timeout * 1000, attempt)\n } else {\n return await Promise.resolve(target.apply(this, args))\n }\n } catch (error) {\n // Check if this error type should trigger a retry\n if (retry_on_errors && retry_on_errors.length > 0) {\n const is_retryable = retry_on_errors.some((matcher) =>\n typeof matcher === 'string'\n ? (error as Error)?.name === matcher\n : matcher instanceof RegExp\n ? matcher.test(String(error))\n : error instanceof matcher\n )\n if (!is_retryable) throw error\n }\n\n // Last attempt: rethrow\n if (attempt >= effective_max_attempts) throw error\n\n // Wait before next attempt with exponential backoff\n const delay_seconds = effective_retry_after * Math.pow(retry_backoff_factor, attempt - 1)\n if (delay_seconds > 0) {\n await sleep(delay_seconds * 1000)\n }\n }\n }\n\n // Unreachable, but satisfies the type checker\n throw new Error(`retry(${fn_name}): unexpected end of retry loop`)\n }\n\n try {\n return await runWithHeldSemaphores(new_held, runRetryLoop)\n } finally {\n if (semaphore_acquired && semaphore) {\n semaphore.release()\n }\n }\n }\n\n Object.defineProperty(retryWrapper, 'name', { value: fn_name, configurable: true })\n return retryWrapper as unknown as T\n }\n}\n\n// \u2500\u2500\u2500 Internal helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Try to acquire a semaphore within a timeout. Returns true if acquired, false if timed out.\n * If the semaphore is acquired after the timeout (due to the waiter remaining queued),\n * it is immediately released to avoid leaking slots.\n */\nasync function acquireWithTimeout(semaphore: RetrySemaphore, timeout_ms: number): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n let settled = false\n\n const timer = setTimeout(() => {\n if (!settled) {\n settled = true\n resolve(false)\n }\n }, timeout_ms)\n\n semaphore.acquire().then(() => {\n if (!settled) {\n settled = true\n clearTimeout(timer)\n resolve(true)\n } else {\n // Acquired after timeout fired \u2014 release immediately to avoid slot leak\n semaphore.release()\n }\n })\n })\n}\n\n/** Run fn() with a timeout. Rejects with RetryTimeoutError if the timeout fires first. */\nasync function _runWithTimeout<T>(fn: () => Promise<T>, timeout_ms: number, attempt: number): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n let settled = false\n\n const timer = setTimeout(() => {\n if (!settled) {\n settled = true\n reject(\n new RetryTimeoutError(`Timed out after ${timeout_ms / 1000}s (attempt ${attempt})`, {\n timeout_seconds: timeout_ms / 1000,\n attempt,\n })\n )\n }\n }, timeout_ms)\n\n fn().then(\n (value) => {\n if (!settled) {\n settled = true\n clearTimeout(timer)\n resolve(value)\n }\n },\n (error) => {\n if (!settled) {\n settled = true\n clearTimeout(timer)\n reject(error)\n }\n }\n )\n })\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms))\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
async function _runWithTimeout(timeout_seconds, on_timeout, fn) {
|
|
2
|
+
const task = Promise.resolve().then(fn);
|
|
3
|
+
if (timeout_seconds === null) {
|
|
4
|
+
return await task;
|
|
5
|
+
}
|
|
6
|
+
const timeout_ms = timeout_seconds * 1e3;
|
|
7
|
+
return await new Promise((resolve, reject) => {
|
|
8
|
+
let settled = false;
|
|
9
|
+
const finishResolve = (value) => {
|
|
10
|
+
if (settled) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
settled = true;
|
|
14
|
+
clearTimeout(timer);
|
|
15
|
+
resolve(value);
|
|
16
|
+
};
|
|
17
|
+
const finishReject = (error) => {
|
|
18
|
+
if (settled) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
settled = true;
|
|
22
|
+
clearTimeout(timer);
|
|
23
|
+
reject(error);
|
|
24
|
+
};
|
|
25
|
+
const timer = setTimeout(() => {
|
|
26
|
+
if (settled) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
settled = true;
|
|
30
|
+
reject(on_timeout());
|
|
31
|
+
void task.catch(() => void 0);
|
|
32
|
+
}, timeout_ms);
|
|
33
|
+
task.then(finishResolve).catch(finishReject);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async function _runWithSlowMonitor(slow_timer, fn) {
|
|
37
|
+
try {
|
|
38
|
+
return await fn();
|
|
39
|
+
} finally {
|
|
40
|
+
if (slow_timer) {
|
|
41
|
+
clearTimeout(slow_timer);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function _runWithAbortMonitor(fn, abort_signal) {
|
|
46
|
+
const task = Promise.resolve().then(fn);
|
|
47
|
+
const raced = Promise.race([task, abort_signal]);
|
|
48
|
+
void task.catch(() => void 0);
|
|
49
|
+
return await raced;
|
|
50
|
+
}
|
|
51
|
+
export {
|
|
52
|
+
_runWithAbortMonitor,
|
|
53
|
+
_runWithSlowMonitor,
|
|
54
|
+
_runWithTimeout
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=timing.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/timing.ts"],
|
|
4
|
+
"sourcesContent": ["export async function _runWithTimeout<T>(timeout_seconds: number | null, on_timeout: () => Error, fn: () => Promise<T>): Promise<T> {\n const task = Promise.resolve().then(fn)\n if (timeout_seconds === null) {\n return await task\n }\n const timeout_ms = timeout_seconds * 1000\n return await new Promise<T>((resolve, reject) => {\n let settled = false\n const finishResolve = (value: T) => {\n if (settled) {\n return\n }\n settled = true\n clearTimeout(timer)\n resolve(value)\n }\n const finishReject = (error: unknown) => {\n if (settled) {\n return\n }\n settled = true\n clearTimeout(timer)\n reject(error)\n }\n const timer = setTimeout(() => {\n if (settled) {\n return\n }\n settled = true\n reject(on_timeout())\n void task.catch(() => undefined)\n }, timeout_ms)\n task.then(finishResolve).catch(finishReject)\n })\n}\n\nexport async function _runWithSlowMonitor<T>(slow_timer: ReturnType<typeof setTimeout> | null, fn: () => Promise<T>): Promise<T> {\n try {\n return await fn()\n } finally {\n if (slow_timer) {\n clearTimeout(slow_timer)\n }\n }\n}\n\nexport async function _runWithAbortMonitor<T>(fn: () => T | Promise<T>, abort_signal: Promise<never>): Promise<T> {\n const task = Promise.resolve().then(fn)\n const raced = Promise.race([task, abort_signal])\n void task.catch(() => undefined)\n return await raced\n}\n"],
|
|
5
|
+
"mappings": "AAAA,eAAsB,gBAAmB,iBAAgC,YAAyB,IAAkC;AAClI,QAAM,OAAO,QAAQ,QAAQ,EAAE,KAAK,EAAE;AACtC,MAAI,oBAAoB,MAAM;AAC5B,WAAO,MAAM;AAAA,EACf;AACA,QAAM,aAAa,kBAAkB;AACrC,SAAO,MAAM,IAAI,QAAW,CAAC,SAAS,WAAW;AAC/C,QAAI,UAAU;AACd,UAAM,gBAAgB,CAAC,UAAa;AAClC,UAAI,SAAS;AACX;AAAA,MACF;AACA,gBAAU;AACV,mBAAa,KAAK;AAClB,cAAQ,KAAK;AAAA,IACf;AACA,UAAM,eAAe,CAAC,UAAmB;AACvC,UAAI,SAAS;AACX;AAAA,MACF;AACA,gBAAU;AACV,mBAAa,KAAK;AAClB,aAAO,KAAK;AAAA,IACd;AACA,UAAM,QAAQ,WAAW,MAAM;AAC7B,UAAI,SAAS;AACX;AAAA,MACF;AACA,gBAAU;AACV,aAAO,WAAW,CAAC;AACnB,WAAK,KAAK,MAAM,MAAM,MAAS;AAAA,IACjC,GAAG,UAAU;AACb,SAAK,KAAK,aAAa,EAAE,MAAM,YAAY;AAAA,EAC7C,CAAC;AACH;AAEA,eAAsB,oBAAuB,YAAkD,IAAkC;AAC/H,MAAI;AACF,WAAO,MAAM,GAAG;AAAA,EAClB,UAAE;AACA,QAAI,YAAY;AACd,mBAAa,UAAU;AAAA,IACzB;AAAA,EACF;AACF;AAEA,eAAsB,qBAAwB,IAA0B,cAA0C;AAChH,QAAM,OAAO,QAAQ,QAAQ,EAAE,KAAK,EAAE;AACtC,QAAM,QAAQ,QAAQ,KAAK,CAAC,MAAM,YAAY,CAAC;AAC/C,OAAK,KAAK,MAAM,MAAM,MAAS;AAC/B,SAAO,MAAM;AACf;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|