@actuallab/core 12.1.62
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/index.cjs +406 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +135 -0
- package/dist/index.d.ts +135 -0
- package/dist/index.js +363 -0
- package/dist/index.js.map +1 -0
- package/package.json +32 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
AsyncContext: () => AsyncContext,
|
|
24
|
+
AsyncContextKey: () => AsyncContextKey,
|
|
25
|
+
AsyncLock: () => AsyncLock,
|
|
26
|
+
DisposableBag: () => DisposableBag,
|
|
27
|
+
EventHandlerSet: () => EventHandlerSet,
|
|
28
|
+
PromiseSource: () => PromiseSource,
|
|
29
|
+
Result: () => Result,
|
|
30
|
+
RetryDelayLimitExceeded: () => RetryDelayLimitExceeded,
|
|
31
|
+
RetryDelayNone: () => RetryDelayNone,
|
|
32
|
+
RetryDelaySeq: () => RetryDelaySeq,
|
|
33
|
+
RetryDelayer: () => RetryDelayer,
|
|
34
|
+
abortSignalKey: () => abortSignalKey,
|
|
35
|
+
errorResult: () => errorResult,
|
|
36
|
+
resolvedVoidPromise: () => resolvedVoidPromise,
|
|
37
|
+
result: () => result,
|
|
38
|
+
resultFrom: () => resultFrom,
|
|
39
|
+
resultFromAsync: () => resultFromAsync
|
|
40
|
+
});
|
|
41
|
+
module.exports = __toCommonJS(index_exports);
|
|
42
|
+
|
|
43
|
+
// src/polyfills.ts
|
|
44
|
+
Symbol.metadata ??= /* @__PURE__ */ Symbol("Symbol.metadata");
|
|
45
|
+
|
|
46
|
+
// src/async-context.ts
|
|
47
|
+
var AsyncContextKey = class {
|
|
48
|
+
id;
|
|
49
|
+
defaultValue;
|
|
50
|
+
constructor(name, defaultValue) {
|
|
51
|
+
this.id = Symbol(name);
|
|
52
|
+
this.defaultValue = defaultValue;
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
var abortSignalKey = new AsyncContextKey("AbortSignal", void 0);
|
|
56
|
+
var AsyncContext = class _AsyncContext {
|
|
57
|
+
static current = void 0;
|
|
58
|
+
/** Immutable empty singleton — avoids allocating new AsyncContext(). */
|
|
59
|
+
static empty = new _AsyncContext();
|
|
60
|
+
static _defaults = /* @__PURE__ */ new Map();
|
|
61
|
+
_values;
|
|
62
|
+
constructor(values) {
|
|
63
|
+
this._values = values ?? /* @__PURE__ */ new Map();
|
|
64
|
+
}
|
|
65
|
+
get(key) {
|
|
66
|
+
if (this._values.has(key.id)) return this._values.get(key.id);
|
|
67
|
+
if (_AsyncContext._defaults.has(key.id)) return _AsyncContext._defaults.get(key.id);
|
|
68
|
+
return key.defaultValue;
|
|
69
|
+
}
|
|
70
|
+
with(key, value) {
|
|
71
|
+
const copy = new Map(this._values);
|
|
72
|
+
copy.set(key.id, value);
|
|
73
|
+
return new _AsyncContext(copy);
|
|
74
|
+
}
|
|
75
|
+
activate() {
|
|
76
|
+
const prev = _AsyncContext.current;
|
|
77
|
+
_AsyncContext.current = this;
|
|
78
|
+
return { dispose: () => {
|
|
79
|
+
_AsyncContext.current = prev;
|
|
80
|
+
} };
|
|
81
|
+
}
|
|
82
|
+
run(fn) {
|
|
83
|
+
const prev = _AsyncContext.current;
|
|
84
|
+
_AsyncContext.current = this;
|
|
85
|
+
try {
|
|
86
|
+
return fn();
|
|
87
|
+
} finally {
|
|
88
|
+
_AsyncContext.current = prev;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** Strip THIS exact instance from args if it's the last element (reference equality). */
|
|
92
|
+
stripFromArgs(args) {
|
|
93
|
+
return args[args.length - 1] === this ? args.slice(0, -1) : args;
|
|
94
|
+
}
|
|
95
|
+
/** Resolve: return ctx if provided, otherwise AsyncContext.current. */
|
|
96
|
+
static from(ctx) {
|
|
97
|
+
return ctx ?? _AsyncContext.current;
|
|
98
|
+
}
|
|
99
|
+
/** Extract AsyncContext from last arg if instanceof AsyncContext, else fall back to .current. */
|
|
100
|
+
static fromArgs(args) {
|
|
101
|
+
const last = args[args.length - 1];
|
|
102
|
+
return last instanceof _AsyncContext ? last : _AsyncContext.current;
|
|
103
|
+
}
|
|
104
|
+
static setDefault(key, value) {
|
|
105
|
+
_AsyncContext._defaults.set(key.id, value);
|
|
106
|
+
}
|
|
107
|
+
static getOrCreate() {
|
|
108
|
+
return _AsyncContext.current ?? _AsyncContext.empty;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// src/async-lock.ts
|
|
113
|
+
var AsyncLock = class {
|
|
114
|
+
_queue = [];
|
|
115
|
+
_locked = false;
|
|
116
|
+
get isLocked() {
|
|
117
|
+
return this._locked;
|
|
118
|
+
}
|
|
119
|
+
async acquire() {
|
|
120
|
+
if (!this._locked) {
|
|
121
|
+
this._locked = true;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
return new Promise((resolve) => {
|
|
125
|
+
this._queue.push(resolve);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
release() {
|
|
129
|
+
if (!this._locked)
|
|
130
|
+
throw new Error("AsyncLock is not locked.");
|
|
131
|
+
const next = this._queue.shift();
|
|
132
|
+
if (next !== void 0) {
|
|
133
|
+
next();
|
|
134
|
+
} else {
|
|
135
|
+
this._locked = false;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async run(fn) {
|
|
139
|
+
await this.acquire();
|
|
140
|
+
try {
|
|
141
|
+
return await fn();
|
|
142
|
+
} finally {
|
|
143
|
+
this.release();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// src/disposable.ts
|
|
149
|
+
var DisposableBag = class {
|
|
150
|
+
_items = [];
|
|
151
|
+
_disposed = false;
|
|
152
|
+
get isDisposed() {
|
|
153
|
+
return this._disposed;
|
|
154
|
+
}
|
|
155
|
+
add(item) {
|
|
156
|
+
if (this._disposed)
|
|
157
|
+
throw new Error("DisposableBag is already disposed.");
|
|
158
|
+
this._items.push(item);
|
|
159
|
+
}
|
|
160
|
+
dispose() {
|
|
161
|
+
if (this._disposed) return;
|
|
162
|
+
this._disposed = true;
|
|
163
|
+
const items = this._items;
|
|
164
|
+
this._items = [];
|
|
165
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
166
|
+
const item = items[i];
|
|
167
|
+
if (item != null && "dispose" in item) item.dispose();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async disposeAsync() {
|
|
171
|
+
if (this._disposed) return;
|
|
172
|
+
this._disposed = true;
|
|
173
|
+
const items = this._items;
|
|
174
|
+
this._items = [];
|
|
175
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
176
|
+
const item = items[i];
|
|
177
|
+
if (item == null) continue;
|
|
178
|
+
if ("disposeAsync" in item) await item.disposeAsync();
|
|
179
|
+
else if ("dispose" in item) item.dispose();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// src/promise-source.ts
|
|
185
|
+
var PromiseSource = class {
|
|
186
|
+
promise;
|
|
187
|
+
_resolve;
|
|
188
|
+
_reject;
|
|
189
|
+
_isCompleted = false;
|
|
190
|
+
constructor() {
|
|
191
|
+
this.promise = new Promise((resolve, reject) => {
|
|
192
|
+
this._resolve = resolve;
|
|
193
|
+
this._reject = reject;
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
get isCompleted() {
|
|
197
|
+
return this._isCompleted;
|
|
198
|
+
}
|
|
199
|
+
resolve(value) {
|
|
200
|
+
if (this._isCompleted) return false;
|
|
201
|
+
this._isCompleted = true;
|
|
202
|
+
this._resolve(value);
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
205
|
+
reject(reason) {
|
|
206
|
+
if (this._isCompleted) return false;
|
|
207
|
+
this._isCompleted = true;
|
|
208
|
+
this._reject(reason);
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
var resolvedVoidPromise = Promise.resolve();
|
|
213
|
+
|
|
214
|
+
// src/events.ts
|
|
215
|
+
var EventHandlerSet = class {
|
|
216
|
+
_handlers = /* @__PURE__ */ new Set();
|
|
217
|
+
get count() {
|
|
218
|
+
return this._handlers.size;
|
|
219
|
+
}
|
|
220
|
+
add(handler) {
|
|
221
|
+
this._handlers.add(handler);
|
|
222
|
+
}
|
|
223
|
+
remove(handler) {
|
|
224
|
+
this._handlers.delete(handler);
|
|
225
|
+
}
|
|
226
|
+
trigger(arg) {
|
|
227
|
+
for (const handler of this._handlers) handler(arg);
|
|
228
|
+
}
|
|
229
|
+
clear() {
|
|
230
|
+
this._handlers.clear();
|
|
231
|
+
}
|
|
232
|
+
whenNext() {
|
|
233
|
+
const ps = new PromiseSource();
|
|
234
|
+
const handler = (arg) => {
|
|
235
|
+
this._handlers.delete(handler);
|
|
236
|
+
ps.resolve(arg);
|
|
237
|
+
};
|
|
238
|
+
this._handlers.add(handler);
|
|
239
|
+
return ps.promise;
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
// src/result.ts
|
|
244
|
+
var Result = class _Result {
|
|
245
|
+
hasValue;
|
|
246
|
+
hasError;
|
|
247
|
+
_value;
|
|
248
|
+
_error;
|
|
249
|
+
constructor(value, error) {
|
|
250
|
+
this.hasValue = error === void 0;
|
|
251
|
+
this.hasError = !this.hasValue;
|
|
252
|
+
this._value = value;
|
|
253
|
+
this._error = error;
|
|
254
|
+
}
|
|
255
|
+
get value() {
|
|
256
|
+
if (this.hasError) throw this._error;
|
|
257
|
+
return this._value;
|
|
258
|
+
}
|
|
259
|
+
get error() {
|
|
260
|
+
return this._error;
|
|
261
|
+
}
|
|
262
|
+
get valueOrUndefined() {
|
|
263
|
+
return this.hasValue ? this._value : void 0;
|
|
264
|
+
}
|
|
265
|
+
static ok(value) {
|
|
266
|
+
return new _Result(value);
|
|
267
|
+
}
|
|
268
|
+
static error(error) {
|
|
269
|
+
return new _Result(void 0, error);
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
function result(value, error) {
|
|
273
|
+
return new Result(value, error);
|
|
274
|
+
}
|
|
275
|
+
function errorResult(error) {
|
|
276
|
+
return Result.error(error);
|
|
277
|
+
}
|
|
278
|
+
function resultFrom(fn) {
|
|
279
|
+
try {
|
|
280
|
+
return result(fn());
|
|
281
|
+
} catch (e) {
|
|
282
|
+
return errorResult(e);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
async function resultFromAsync(fn) {
|
|
286
|
+
try {
|
|
287
|
+
return result(await fn());
|
|
288
|
+
} catch (e) {
|
|
289
|
+
return errorResult(e);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// src/retry-delay-seq.ts
|
|
294
|
+
var DefaultSpread = 0.1;
|
|
295
|
+
var DefaultMultiplier = 1.41421356237;
|
|
296
|
+
function applySpread(origin, spread) {
|
|
297
|
+
if (spread <= 0) return origin;
|
|
298
|
+
const delta = origin * spread;
|
|
299
|
+
return origin + (Math.random() * 2 - 1) * delta;
|
|
300
|
+
}
|
|
301
|
+
var RetryDelaySeq = class _RetryDelaySeq {
|
|
302
|
+
min;
|
|
303
|
+
max;
|
|
304
|
+
spread;
|
|
305
|
+
multiplier;
|
|
306
|
+
constructor(min, max, spread = DefaultSpread, multiplier = DefaultMultiplier) {
|
|
307
|
+
this.min = min;
|
|
308
|
+
this.max = max;
|
|
309
|
+
this.spread = spread;
|
|
310
|
+
this.multiplier = multiplier;
|
|
311
|
+
}
|
|
312
|
+
static fixed(delayMs, spread = DefaultSpread) {
|
|
313
|
+
return new _RetryDelaySeq(delayMs, delayMs, spread, 1);
|
|
314
|
+
}
|
|
315
|
+
static exp(minMs, maxMs, spread = DefaultSpread, multiplier = DefaultMultiplier) {
|
|
316
|
+
return new _RetryDelaySeq(minMs, maxMs, spread, multiplier);
|
|
317
|
+
}
|
|
318
|
+
getDelay(failureCount) {
|
|
319
|
+
if (this.min < 0) throw new Error("RetryDelaySeq.min must be non-negative.");
|
|
320
|
+
if (failureCount <= 0) return 0;
|
|
321
|
+
if (this.multiplier <= 1) {
|
|
322
|
+
return Math.max(0, this.spread <= 0 ? this.min : applySpread(this.min, this.spread));
|
|
323
|
+
}
|
|
324
|
+
const multiplier = Math.pow(this.multiplier, failureCount - 1);
|
|
325
|
+
const raw = Math.min(this.max, this.min * multiplier);
|
|
326
|
+
return Math.max(0, applySpread(raw, this.spread));
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// src/retry-delayer.ts
|
|
331
|
+
var RetryDelayNone = {
|
|
332
|
+
promise: Promise.resolve(),
|
|
333
|
+
endsAt: 0,
|
|
334
|
+
isLimitExceeded: false
|
|
335
|
+
};
|
|
336
|
+
var RetryDelayLimitExceeded = {
|
|
337
|
+
promise: Promise.resolve(),
|
|
338
|
+
endsAt: 0,
|
|
339
|
+
isLimitExceeded: true
|
|
340
|
+
};
|
|
341
|
+
var RetryDelayer = class {
|
|
342
|
+
_cancelController = new AbortController();
|
|
343
|
+
delays = RetryDelaySeq.fixed(1e3);
|
|
344
|
+
limit = void 0;
|
|
345
|
+
cancelDelaysChanged = new EventHandlerSet();
|
|
346
|
+
getDelay(tryIndex, cancellationSignal) {
|
|
347
|
+
if (this.limit !== void 0 && tryIndex >= this.limit)
|
|
348
|
+
return RetryDelayLimitExceeded;
|
|
349
|
+
const delayMs = this.delays.getDelay(tryIndex);
|
|
350
|
+
if (tryIndex === 0 || delayMs <= 0)
|
|
351
|
+
return RetryDelayNone;
|
|
352
|
+
const actualDelayMs = Math.max(1, delayMs);
|
|
353
|
+
const endsAt = Date.now() + actualDelayMs;
|
|
354
|
+
const cancelSignal = this._cancelController.signal;
|
|
355
|
+
const promise = new Promise((resolve, reject) => {
|
|
356
|
+
const timer = setTimeout(() => {
|
|
357
|
+
cleanup();
|
|
358
|
+
resolve();
|
|
359
|
+
}, actualDelayMs);
|
|
360
|
+
const onCancel = () => {
|
|
361
|
+
clearTimeout(timer);
|
|
362
|
+
cleanup();
|
|
363
|
+
resolve();
|
|
364
|
+
};
|
|
365
|
+
const onAbort = () => {
|
|
366
|
+
clearTimeout(timer);
|
|
367
|
+
cleanup();
|
|
368
|
+
reject(new Error("Retry delay aborted."));
|
|
369
|
+
};
|
|
370
|
+
const cleanup = () => {
|
|
371
|
+
cancelSignal.removeEventListener("abort", onCancel);
|
|
372
|
+
cancellationSignal?.removeEventListener("abort", onAbort);
|
|
373
|
+
};
|
|
374
|
+
cancelSignal.addEventListener("abort", onCancel, { once: true });
|
|
375
|
+
cancellationSignal?.addEventListener("abort", onAbort, { once: true });
|
|
376
|
+
});
|
|
377
|
+
return { promise, endsAt, isLimitExceeded: false };
|
|
378
|
+
}
|
|
379
|
+
cancelDelays() {
|
|
380
|
+
const old = this._cancelController;
|
|
381
|
+
this._cancelController = new AbortController();
|
|
382
|
+
old.abort();
|
|
383
|
+
this.cancelDelaysChanged.trigger();
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
387
|
+
0 && (module.exports = {
|
|
388
|
+
AsyncContext,
|
|
389
|
+
AsyncContextKey,
|
|
390
|
+
AsyncLock,
|
|
391
|
+
DisposableBag,
|
|
392
|
+
EventHandlerSet,
|
|
393
|
+
PromiseSource,
|
|
394
|
+
Result,
|
|
395
|
+
RetryDelayLimitExceeded,
|
|
396
|
+
RetryDelayNone,
|
|
397
|
+
RetryDelaySeq,
|
|
398
|
+
RetryDelayer,
|
|
399
|
+
abortSignalKey,
|
|
400
|
+
errorResult,
|
|
401
|
+
resolvedVoidPromise,
|
|
402
|
+
result,
|
|
403
|
+
resultFrom,
|
|
404
|
+
resultFromAsync
|
|
405
|
+
});
|
|
406
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/polyfills.ts","../src/async-context.ts","../src/async-lock.ts","../src/disposable.ts","../src/promise-source.ts","../src/events.ts","../src/result.ts","../src/retry-delay-seq.ts","../src/retry-delayer.ts"],"sourcesContent":["import \"./polyfills.js\";\r\nexport { AsyncContext, AsyncContextKey, abortSignalKey } from \"./async-context.js\";\r\nexport { AsyncLock } from \"./async-lock.js\";\r\nexport type { Disposable, AsyncDisposable } from \"./disposable.js\";\r\nexport { DisposableBag } from \"./disposable.js\";\r\nexport { EventHandlerSet } from \"./events.js\";\r\nexport { PromiseSource, resolvedVoidPromise } from \"./promise-source.js\";\r\nexport type { IResult } from \"./result.js\";\r\nexport { Result, result, errorResult, resultFrom, resultFromAsync } from \"./result.js\";\r\nexport { RetryDelaySeq } from \"./retry-delay-seq.js\";\r\nexport type { RetryDelay } from \"./retry-delayer.js\";\r\nexport { RetryDelayer, RetryDelayNone, RetryDelayLimitExceeded } from \"./retry-delayer.js\";\r\n","// TC39 decorator metadata polyfill — required for Symbol.metadata support\r\n// in runtimes that don't yet implement it natively.\r\n(Symbol as any).metadata ??= Symbol(\"Symbol.metadata\");\r\n","import type { Disposable } from \"./disposable.js\";\r\n\r\n/** Typed key for values stored in AsyncContext — each subsystem defines its own keys. */\r\nexport class AsyncContextKey<T> {\r\n readonly id: symbol;\r\n readonly defaultValue: T;\r\n\r\n constructor(name: string, defaultValue: T) {\r\n this.id = Symbol(name);\r\n this.defaultValue = defaultValue;\r\n }\r\n}\r\n\r\n/** General-purpose typed context container — named for forward-compatibility with TC39 AsyncContext proposal. */\r\nexport const abortSignalKey =\r\n new AsyncContextKey<AbortSignal | undefined>(\"AbortSignal\", undefined);\r\n\r\nexport class AsyncContext {\r\n static current: AsyncContext | undefined = undefined;\r\n\r\n /** Immutable empty singleton — avoids allocating new AsyncContext(). */\r\n static readonly empty: AsyncContext = new AsyncContext();\r\n\r\n private static _defaults = new Map<symbol, unknown>();\r\n\r\n private _values: Map<symbol, unknown>;\r\n\r\n constructor(values?: Map<symbol, unknown>) {\r\n this._values = values ?? new Map();\r\n }\r\n\r\n get<T>(key: AsyncContextKey<T>): T {\r\n if (this._values.has(key.id)) return this._values.get(key.id) as T;\r\n if (AsyncContext._defaults.has(key.id)) return AsyncContext._defaults.get(key.id) as T;\r\n return key.defaultValue;\r\n }\r\n\r\n with<T>(key: AsyncContextKey<T>, value: T): AsyncContext {\r\n const copy = new Map(this._values);\r\n copy.set(key.id, value);\r\n return new AsyncContext(copy);\r\n }\r\n\r\n activate(): Disposable {\r\n const prev = AsyncContext.current;\r\n AsyncContext.current = this;\r\n return { dispose: () => { AsyncContext.current = prev; } };\r\n }\r\n\r\n run<R>(fn: () => R): R {\r\n const prev = AsyncContext.current;\r\n AsyncContext.current = this;\r\n try {\r\n return fn();\r\n } finally {\r\n AsyncContext.current = prev;\r\n }\r\n }\r\n\r\n /** Strip THIS exact instance from args if it's the last element (reference equality). */\r\n stripFromArgs(args: unknown[]): unknown[] {\r\n return args[args.length - 1] === this ? args.slice(0, -1) : args;\r\n }\r\n\r\n /** Resolve: return ctx if provided, otherwise AsyncContext.current. */\r\n static from(ctx: AsyncContext | undefined): AsyncContext | undefined {\r\n return ctx ?? AsyncContext.current;\r\n }\r\n\r\n /** Extract AsyncContext from last arg if instanceof AsyncContext, else fall back to .current. */\r\n static fromArgs(args: unknown[]): AsyncContext | undefined {\r\n const last = args[args.length - 1];\r\n return last instanceof AsyncContext ? last : AsyncContext.current;\r\n }\r\n\r\n static setDefault<T>(key: AsyncContextKey<T>, value: T): void {\r\n AsyncContext._defaults.set(key.id, value);\r\n }\r\n\r\n static getOrCreate(): AsyncContext {\r\n return AsyncContext.current ?? AsyncContext.empty;\r\n }\r\n}\r\n","/** Promise-based mutual exclusion lock for serializing async operations. */\r\nexport class AsyncLock {\r\n private _queue: (() => void)[] = [];\r\n private _locked = false;\r\n\r\n get isLocked(): boolean {\r\n return this._locked;\r\n }\r\n\r\n async acquire(): Promise<void> {\r\n if (!this._locked) {\r\n this._locked = true;\r\n return;\r\n }\r\n return new Promise<void>((resolve) => {\r\n this._queue.push(resolve);\r\n });\r\n }\r\n\r\n release(): void {\r\n if (!this._locked)\r\n throw new Error(\"AsyncLock is not locked.\");\r\n\r\n const next = this._queue.shift();\r\n if (next !== undefined) {\r\n // Transfer lock to the next waiter\r\n next();\r\n } else {\r\n this._locked = false;\r\n }\r\n }\r\n\r\n async run<T>(fn: () => T | Promise<T>): Promise<T> {\r\n await this.acquire();\r\n try {\r\n return await fn();\r\n } finally {\r\n this.release();\r\n }\r\n }\r\n}\r\n","/** Synchronous dispose pattern, similar to .NET's IDisposable. */\r\nexport interface Disposable {\r\n dispose(): void;\r\n}\r\n\r\n/** Asynchronous dispose pattern, similar to .NET's IAsyncDisposable. */\r\nexport interface AsyncDisposable {\r\n disposeAsync(): Promise<void>;\r\n}\r\n\r\n/** Aggregates multiple disposables and disposes them together. */\r\nexport class DisposableBag implements Disposable, AsyncDisposable {\r\n private _items: (Disposable | AsyncDisposable)[] = [];\r\n private _disposed = false;\r\n\r\n get isDisposed(): boolean {\r\n return this._disposed;\r\n }\r\n\r\n add(item: Disposable | AsyncDisposable): void {\r\n if (this._disposed)\r\n throw new Error(\"DisposableBag is already disposed.\");\r\n this._items.push(item);\r\n }\r\n\r\n dispose(): void {\r\n if (this._disposed) return;\r\n this._disposed = true;\r\n const items = this._items;\r\n this._items = [];\r\n // Dispose in reverse order (LIFO)\r\n for (let i = items.length - 1; i >= 0; i--) {\r\n const item = items[i];\r\n if (item != null && \"dispose\" in item) item.dispose();\r\n }\r\n }\r\n\r\n async disposeAsync(): Promise<void> {\r\n if (this._disposed) return;\r\n this._disposed = true;\r\n const items = this._items;\r\n this._items = [];\r\n for (let i = items.length - 1; i >= 0; i--) {\r\n const item = items[i];\r\n if (item == null) continue;\r\n if (\"disposeAsync\" in item) await item.disposeAsync();\r\n else if (\"dispose\" in item) item.dispose();\r\n }\r\n }\r\n}\r\n","/** Externally-resolvable promise — similar to .NET's TaskCompletionSource<T>. */\r\nexport class PromiseSource<T> {\r\n readonly promise: Promise<T>;\r\n private _resolve!: (value: T | PromiseLike<T>) => void;\r\n private _reject!: (reason?: unknown) => void;\r\n private _isCompleted = false;\r\n\r\n constructor() {\r\n this.promise = new Promise<T>((resolve, reject) => {\r\n this._resolve = resolve;\r\n this._reject = reject;\r\n });\r\n }\r\n\r\n get isCompleted(): boolean {\r\n return this._isCompleted;\r\n }\r\n\r\n resolve(value: T): boolean {\r\n if (this._isCompleted) return false;\r\n this._isCompleted = true;\r\n this._resolve(value);\r\n return true;\r\n }\r\n\r\n reject(reason?: unknown): boolean {\r\n if (this._isCompleted) return false;\r\n this._isCompleted = true;\r\n this._reject(reason);\r\n return true;\r\n }\r\n}\r\n\r\nexport const resolvedVoidPromise: Promise<void> = Promise.resolve();","import { PromiseSource } from \"./promise-source.js\";\r\n\r\n/** Typed pub/sub event handler set — similar to .NET's EventHandler<T> multicast delegate. */\r\nexport class EventHandlerSet<T> {\r\n private _handlers: Set<(arg: T) => void> = new Set();\r\n\r\n get count(): number {\r\n return this._handlers.size;\r\n }\r\n\r\n add(handler: (arg: T) => void): void {\r\n this._handlers.add(handler);\r\n }\r\n\r\n remove(handler: (arg: T) => void): void {\r\n this._handlers.delete(handler);\r\n }\r\n\r\n trigger(arg: T): void {\r\n for (const handler of this._handlers) handler(arg);\r\n }\r\n\r\n clear(): void {\r\n this._handlers.clear();\r\n }\r\n\r\n whenNext(): Promise<T> {\r\n const ps = new PromiseSource<T>();\r\n const handler = (arg: T) => {\r\n this._handlers.delete(handler);\r\n ps.resolve(arg);\r\n };\r\n this._handlers.add(handler);\r\n return ps.promise;\r\n }\r\n}\r\n","/** Common interface for types that carry a value-or-error outcome. */\r\nexport interface IResult<T> {\r\n readonly hasValue: boolean;\r\n readonly hasError: boolean;\r\n readonly value: T;\r\n readonly error: unknown;\r\n readonly valueOrUndefined: T | undefined;\r\n}\r\n\r\n/** Immutable value-or-error container — similar to .NET's Result<T>. */\r\nexport class Result<T> implements IResult<T> {\r\n readonly hasValue: boolean;\r\n readonly hasError: boolean;\r\n private readonly _value: T | undefined;\r\n private readonly _error: unknown;\r\n\r\n public constructor(value: T | undefined, error?: unknown) {\r\n this.hasValue = error === undefined;\r\n this.hasError = !this.hasValue;\r\n this._value = value;\r\n this._error = error;\r\n }\r\n\r\n get value(): T {\r\n if (this.hasError) throw this._error;\r\n return this._value as T;\r\n }\r\n\r\n get error(): unknown {\r\n return this._error;\r\n }\r\n\r\n get valueOrUndefined(): T | undefined {\r\n return this.hasValue ? this._value : undefined;\r\n }\r\n\r\n static ok<T>(value: T): Result<T> {\r\n return new Result<T>(value);\r\n }\r\n\r\n static error<T>(error: unknown): Result<T> {\r\n return new Result<T>(undefined, error);\r\n }\r\n}\r\n\r\nexport function result<T>(value: T, error?: unknown): Result<T> {\r\n return new Result<T>(value, error);\r\n}\r\n\r\nexport function errorResult<T>(error: unknown): Result<T> {\r\n return Result.error(error);\r\n}\r\n\r\nexport function resultFrom<T>(fn: () => T): Result<T> {\r\n try {\r\n return result(fn());\r\n } catch (e) {\r\n return errorResult(e);\r\n }\r\n}\r\n\r\nexport async function resultFromAsync<T>(fn: () => Promise<T>): Promise<Result<T>> {\r\n try {\r\n return result(await fn());\r\n } catch (e) {\r\n return errorResult(e);\r\n }\r\n}\r\n","// .NET counterpart: ActualLab.Time.RetryDelaySeq\r\n\r\nconst DefaultSpread = 0.1;\r\nconst DefaultMultiplier = 1.41421356237; // Math.sqrt(2)\r\n\r\nfunction applySpread(origin: number, spread: number): number {\r\n if (spread <= 0) return origin;\r\n const delta = origin * spread;\r\n return origin + (Math.random() * 2 - 1) * delta;\r\n}\r\n\r\nexport class RetryDelaySeq {\r\n readonly min: number;\r\n readonly max: number;\r\n readonly spread: number;\r\n readonly multiplier: number;\r\n\r\n constructor(min: number, max: number, spread: number = DefaultSpread, multiplier: number = DefaultMultiplier) {\r\n this.min = min;\r\n this.max = max;\r\n this.spread = spread;\r\n this.multiplier = multiplier;\r\n }\r\n\r\n static fixed(delayMs: number, spread: number = DefaultSpread): RetryDelaySeq {\r\n return new RetryDelaySeq(delayMs, delayMs, spread, 1);\r\n }\r\n\r\n static exp(\r\n minMs: number,\r\n maxMs: number,\r\n spread: number = DefaultSpread,\r\n multiplier: number = DefaultMultiplier,\r\n ): RetryDelaySeq {\r\n return new RetryDelaySeq(minMs, maxMs, spread, multiplier);\r\n }\r\n\r\n getDelay(failureCount: number): number {\r\n if (this.min < 0) throw new Error(\"RetryDelaySeq.min must be non-negative.\");\r\n if (failureCount <= 0) return 0;\r\n\r\n if (this.multiplier <= 1) {\r\n // Fixed — no exponential component\r\n return Math.max(0, this.spread <= 0 ? this.min : applySpread(this.min, this.spread));\r\n }\r\n\r\n const multiplier = Math.pow(this.multiplier, failureCount - 1);\r\n const raw = Math.min(this.max, this.min * multiplier);\r\n return Math.max(0, applySpread(raw, this.spread));\r\n }\r\n}\r\n","// .NET counterparts: ActualLab.Net.RetryDelayer, ActualLab.Net.RetryDelay\r\n\r\nimport { EventHandlerSet } from \"./events.js\";\r\nimport { RetryDelaySeq } from \"./retry-delay-seq.js\";\r\n\r\nexport interface RetryDelay {\r\n readonly promise: Promise<void>;\r\n readonly endsAt: number; // Date.now()-based timestamp, 0 = no delay\r\n readonly isLimitExceeded: boolean;\r\n}\r\n\r\nexport const RetryDelayNone: RetryDelay = {\r\n promise: Promise.resolve(),\r\n endsAt: 0,\r\n isLimitExceeded: false,\r\n};\r\n\r\nexport const RetryDelayLimitExceeded: RetryDelay = {\r\n promise: Promise.resolve(),\r\n endsAt: 0,\r\n isLimitExceeded: true,\r\n};\r\n\r\nexport class RetryDelayer {\r\n private _cancelController = new AbortController();\r\n\r\n delays: RetryDelaySeq = RetryDelaySeq.fixed(1000);\r\n limit: number | undefined = undefined;\r\n\r\n readonly cancelDelaysChanged = new EventHandlerSet<void>();\r\n\r\n getDelay(tryIndex: number, cancellationSignal?: AbortSignal): RetryDelay {\r\n if (this.limit !== undefined && tryIndex >= this.limit)\r\n return RetryDelayLimitExceeded;\r\n\r\n const delayMs = this.delays.getDelay(tryIndex);\r\n if (tryIndex === 0 || delayMs <= 0)\r\n return RetryDelayNone;\r\n\r\n const actualDelayMs = Math.max(1, delayMs);\r\n const endsAt = Date.now() + actualDelayMs;\r\n\r\n const cancelSignal = this._cancelController.signal;\r\n const promise = new Promise<void>((resolve, reject) => {\r\n const timer = setTimeout(() => {\r\n cleanup();\r\n resolve();\r\n }, actualDelayMs);\r\n\r\n const onCancel = () => {\r\n clearTimeout(timer);\r\n cleanup();\r\n resolve(); // cancelDelays resolves, does NOT reject\r\n };\r\n\r\n const onAbort = () => {\r\n clearTimeout(timer);\r\n cleanup();\r\n reject(new Error(\"Retry delay aborted.\"));\r\n };\r\n\r\n const cleanup = () => {\r\n cancelSignal.removeEventListener(\"abort\", onCancel);\r\n cancellationSignal?.removeEventListener(\"abort\", onAbort);\r\n };\r\n\r\n cancelSignal.addEventListener(\"abort\", onCancel, { once: true });\r\n cancellationSignal?.addEventListener(\"abort\", onAbort, { once: true });\r\n });\r\n\r\n return { promise, endsAt, isLimitExceeded: false };\r\n }\r\n\r\n cancelDelays(): void {\r\n const old = this._cancelController;\r\n this._cancelController = new AbortController();\r\n old.abort();\r\n this.cancelDelaysChanged.trigger();\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/** Synchronous dispose pattern, similar to .NET's IDisposable. */
|
|
2
|
+
interface Disposable {
|
|
3
|
+
dispose(): void;
|
|
4
|
+
}
|
|
5
|
+
/** Asynchronous dispose pattern, similar to .NET's IAsyncDisposable. */
|
|
6
|
+
interface AsyncDisposable {
|
|
7
|
+
disposeAsync(): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
/** Aggregates multiple disposables and disposes them together. */
|
|
10
|
+
declare class DisposableBag implements Disposable, AsyncDisposable {
|
|
11
|
+
private _items;
|
|
12
|
+
private _disposed;
|
|
13
|
+
get isDisposed(): boolean;
|
|
14
|
+
add(item: Disposable | AsyncDisposable): void;
|
|
15
|
+
dispose(): void;
|
|
16
|
+
disposeAsync(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Typed key for values stored in AsyncContext — each subsystem defines its own keys. */
|
|
20
|
+
declare class AsyncContextKey<T> {
|
|
21
|
+
readonly id: symbol;
|
|
22
|
+
readonly defaultValue: T;
|
|
23
|
+
constructor(name: string, defaultValue: T);
|
|
24
|
+
}
|
|
25
|
+
/** General-purpose typed context container — named for forward-compatibility with TC39 AsyncContext proposal. */
|
|
26
|
+
declare const abortSignalKey: AsyncContextKey<AbortSignal | undefined>;
|
|
27
|
+
declare class AsyncContext {
|
|
28
|
+
static current: AsyncContext | undefined;
|
|
29
|
+
/** Immutable empty singleton — avoids allocating new AsyncContext(). */
|
|
30
|
+
static readonly empty: AsyncContext;
|
|
31
|
+
private static _defaults;
|
|
32
|
+
private _values;
|
|
33
|
+
constructor(values?: Map<symbol, unknown>);
|
|
34
|
+
get<T>(key: AsyncContextKey<T>): T;
|
|
35
|
+
with<T>(key: AsyncContextKey<T>, value: T): AsyncContext;
|
|
36
|
+
activate(): Disposable;
|
|
37
|
+
run<R>(fn: () => R): R;
|
|
38
|
+
/** Strip THIS exact instance from args if it's the last element (reference equality). */
|
|
39
|
+
stripFromArgs(args: unknown[]): unknown[];
|
|
40
|
+
/** Resolve: return ctx if provided, otherwise AsyncContext.current. */
|
|
41
|
+
static from(ctx: AsyncContext | undefined): AsyncContext | undefined;
|
|
42
|
+
/** Extract AsyncContext from last arg if instanceof AsyncContext, else fall back to .current. */
|
|
43
|
+
static fromArgs(args: unknown[]): AsyncContext | undefined;
|
|
44
|
+
static setDefault<T>(key: AsyncContextKey<T>, value: T): void;
|
|
45
|
+
static getOrCreate(): AsyncContext;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Promise-based mutual exclusion lock for serializing async operations. */
|
|
49
|
+
declare class AsyncLock {
|
|
50
|
+
private _queue;
|
|
51
|
+
private _locked;
|
|
52
|
+
get isLocked(): boolean;
|
|
53
|
+
acquire(): Promise<void>;
|
|
54
|
+
release(): void;
|
|
55
|
+
run<T>(fn: () => T | Promise<T>): Promise<T>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Typed pub/sub event handler set — similar to .NET's EventHandler<T> multicast delegate. */
|
|
59
|
+
declare class EventHandlerSet<T> {
|
|
60
|
+
private _handlers;
|
|
61
|
+
get count(): number;
|
|
62
|
+
add(handler: (arg: T) => void): void;
|
|
63
|
+
remove(handler: (arg: T) => void): void;
|
|
64
|
+
trigger(arg: T): void;
|
|
65
|
+
clear(): void;
|
|
66
|
+
whenNext(): Promise<T>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Externally-resolvable promise — similar to .NET's TaskCompletionSource<T>. */
|
|
70
|
+
declare class PromiseSource<T> {
|
|
71
|
+
readonly promise: Promise<T>;
|
|
72
|
+
private _resolve;
|
|
73
|
+
private _reject;
|
|
74
|
+
private _isCompleted;
|
|
75
|
+
constructor();
|
|
76
|
+
get isCompleted(): boolean;
|
|
77
|
+
resolve(value: T): boolean;
|
|
78
|
+
reject(reason?: unknown): boolean;
|
|
79
|
+
}
|
|
80
|
+
declare const resolvedVoidPromise: Promise<void>;
|
|
81
|
+
|
|
82
|
+
/** Common interface for types that carry a value-or-error outcome. */
|
|
83
|
+
interface IResult<T> {
|
|
84
|
+
readonly hasValue: boolean;
|
|
85
|
+
readonly hasError: boolean;
|
|
86
|
+
readonly value: T;
|
|
87
|
+
readonly error: unknown;
|
|
88
|
+
readonly valueOrUndefined: T | undefined;
|
|
89
|
+
}
|
|
90
|
+
/** Immutable value-or-error container — similar to .NET's Result<T>. */
|
|
91
|
+
declare class Result<T> implements IResult<T> {
|
|
92
|
+
readonly hasValue: boolean;
|
|
93
|
+
readonly hasError: boolean;
|
|
94
|
+
private readonly _value;
|
|
95
|
+
private readonly _error;
|
|
96
|
+
constructor(value: T | undefined, error?: unknown);
|
|
97
|
+
get value(): T;
|
|
98
|
+
get error(): unknown;
|
|
99
|
+
get valueOrUndefined(): T | undefined;
|
|
100
|
+
static ok<T>(value: T): Result<T>;
|
|
101
|
+
static error<T>(error: unknown): Result<T>;
|
|
102
|
+
}
|
|
103
|
+
declare function result<T>(value: T, error?: unknown): Result<T>;
|
|
104
|
+
declare function errorResult<T>(error: unknown): Result<T>;
|
|
105
|
+
declare function resultFrom<T>(fn: () => T): Result<T>;
|
|
106
|
+
declare function resultFromAsync<T>(fn: () => Promise<T>): Promise<Result<T>>;
|
|
107
|
+
|
|
108
|
+
declare class RetryDelaySeq {
|
|
109
|
+
readonly min: number;
|
|
110
|
+
readonly max: number;
|
|
111
|
+
readonly spread: number;
|
|
112
|
+
readonly multiplier: number;
|
|
113
|
+
constructor(min: number, max: number, spread?: number, multiplier?: number);
|
|
114
|
+
static fixed(delayMs: number, spread?: number): RetryDelaySeq;
|
|
115
|
+
static exp(minMs: number, maxMs: number, spread?: number, multiplier?: number): RetryDelaySeq;
|
|
116
|
+
getDelay(failureCount: number): number;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface RetryDelay {
|
|
120
|
+
readonly promise: Promise<void>;
|
|
121
|
+
readonly endsAt: number;
|
|
122
|
+
readonly isLimitExceeded: boolean;
|
|
123
|
+
}
|
|
124
|
+
declare const RetryDelayNone: RetryDelay;
|
|
125
|
+
declare const RetryDelayLimitExceeded: RetryDelay;
|
|
126
|
+
declare class RetryDelayer {
|
|
127
|
+
private _cancelController;
|
|
128
|
+
delays: RetryDelaySeq;
|
|
129
|
+
limit: number | undefined;
|
|
130
|
+
readonly cancelDelaysChanged: EventHandlerSet<void>;
|
|
131
|
+
getDelay(tryIndex: number, cancellationSignal?: AbortSignal): RetryDelay;
|
|
132
|
+
cancelDelays(): void;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export { AsyncContext, AsyncContextKey, type AsyncDisposable, AsyncLock, type Disposable, DisposableBag, EventHandlerSet, type IResult, PromiseSource, Result, type RetryDelay, RetryDelayLimitExceeded, RetryDelayNone, RetryDelaySeq, RetryDelayer, abortSignalKey, errorResult, resolvedVoidPromise, result, resultFrom, resultFromAsync };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/** Synchronous dispose pattern, similar to .NET's IDisposable. */
|
|
2
|
+
interface Disposable {
|
|
3
|
+
dispose(): void;
|
|
4
|
+
}
|
|
5
|
+
/** Asynchronous dispose pattern, similar to .NET's IAsyncDisposable. */
|
|
6
|
+
interface AsyncDisposable {
|
|
7
|
+
disposeAsync(): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
/** Aggregates multiple disposables and disposes them together. */
|
|
10
|
+
declare class DisposableBag implements Disposable, AsyncDisposable {
|
|
11
|
+
private _items;
|
|
12
|
+
private _disposed;
|
|
13
|
+
get isDisposed(): boolean;
|
|
14
|
+
add(item: Disposable | AsyncDisposable): void;
|
|
15
|
+
dispose(): void;
|
|
16
|
+
disposeAsync(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Typed key for values stored in AsyncContext — each subsystem defines its own keys. */
|
|
20
|
+
declare class AsyncContextKey<T> {
|
|
21
|
+
readonly id: symbol;
|
|
22
|
+
readonly defaultValue: T;
|
|
23
|
+
constructor(name: string, defaultValue: T);
|
|
24
|
+
}
|
|
25
|
+
/** General-purpose typed context container — named for forward-compatibility with TC39 AsyncContext proposal. */
|
|
26
|
+
declare const abortSignalKey: AsyncContextKey<AbortSignal | undefined>;
|
|
27
|
+
declare class AsyncContext {
|
|
28
|
+
static current: AsyncContext | undefined;
|
|
29
|
+
/** Immutable empty singleton — avoids allocating new AsyncContext(). */
|
|
30
|
+
static readonly empty: AsyncContext;
|
|
31
|
+
private static _defaults;
|
|
32
|
+
private _values;
|
|
33
|
+
constructor(values?: Map<symbol, unknown>);
|
|
34
|
+
get<T>(key: AsyncContextKey<T>): T;
|
|
35
|
+
with<T>(key: AsyncContextKey<T>, value: T): AsyncContext;
|
|
36
|
+
activate(): Disposable;
|
|
37
|
+
run<R>(fn: () => R): R;
|
|
38
|
+
/** Strip THIS exact instance from args if it's the last element (reference equality). */
|
|
39
|
+
stripFromArgs(args: unknown[]): unknown[];
|
|
40
|
+
/** Resolve: return ctx if provided, otherwise AsyncContext.current. */
|
|
41
|
+
static from(ctx: AsyncContext | undefined): AsyncContext | undefined;
|
|
42
|
+
/** Extract AsyncContext from last arg if instanceof AsyncContext, else fall back to .current. */
|
|
43
|
+
static fromArgs(args: unknown[]): AsyncContext | undefined;
|
|
44
|
+
static setDefault<T>(key: AsyncContextKey<T>, value: T): void;
|
|
45
|
+
static getOrCreate(): AsyncContext;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Promise-based mutual exclusion lock for serializing async operations. */
|
|
49
|
+
declare class AsyncLock {
|
|
50
|
+
private _queue;
|
|
51
|
+
private _locked;
|
|
52
|
+
get isLocked(): boolean;
|
|
53
|
+
acquire(): Promise<void>;
|
|
54
|
+
release(): void;
|
|
55
|
+
run<T>(fn: () => T | Promise<T>): Promise<T>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Typed pub/sub event handler set — similar to .NET's EventHandler<T> multicast delegate. */
|
|
59
|
+
declare class EventHandlerSet<T> {
|
|
60
|
+
private _handlers;
|
|
61
|
+
get count(): number;
|
|
62
|
+
add(handler: (arg: T) => void): void;
|
|
63
|
+
remove(handler: (arg: T) => void): void;
|
|
64
|
+
trigger(arg: T): void;
|
|
65
|
+
clear(): void;
|
|
66
|
+
whenNext(): Promise<T>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Externally-resolvable promise — similar to .NET's TaskCompletionSource<T>. */
|
|
70
|
+
declare class PromiseSource<T> {
|
|
71
|
+
readonly promise: Promise<T>;
|
|
72
|
+
private _resolve;
|
|
73
|
+
private _reject;
|
|
74
|
+
private _isCompleted;
|
|
75
|
+
constructor();
|
|
76
|
+
get isCompleted(): boolean;
|
|
77
|
+
resolve(value: T): boolean;
|
|
78
|
+
reject(reason?: unknown): boolean;
|
|
79
|
+
}
|
|
80
|
+
declare const resolvedVoidPromise: Promise<void>;
|
|
81
|
+
|
|
82
|
+
/** Common interface for types that carry a value-or-error outcome. */
|
|
83
|
+
interface IResult<T> {
|
|
84
|
+
readonly hasValue: boolean;
|
|
85
|
+
readonly hasError: boolean;
|
|
86
|
+
readonly value: T;
|
|
87
|
+
readonly error: unknown;
|
|
88
|
+
readonly valueOrUndefined: T | undefined;
|
|
89
|
+
}
|
|
90
|
+
/** Immutable value-or-error container — similar to .NET's Result<T>. */
|
|
91
|
+
declare class Result<T> implements IResult<T> {
|
|
92
|
+
readonly hasValue: boolean;
|
|
93
|
+
readonly hasError: boolean;
|
|
94
|
+
private readonly _value;
|
|
95
|
+
private readonly _error;
|
|
96
|
+
constructor(value: T | undefined, error?: unknown);
|
|
97
|
+
get value(): T;
|
|
98
|
+
get error(): unknown;
|
|
99
|
+
get valueOrUndefined(): T | undefined;
|
|
100
|
+
static ok<T>(value: T): Result<T>;
|
|
101
|
+
static error<T>(error: unknown): Result<T>;
|
|
102
|
+
}
|
|
103
|
+
declare function result<T>(value: T, error?: unknown): Result<T>;
|
|
104
|
+
declare function errorResult<T>(error: unknown): Result<T>;
|
|
105
|
+
declare function resultFrom<T>(fn: () => T): Result<T>;
|
|
106
|
+
declare function resultFromAsync<T>(fn: () => Promise<T>): Promise<Result<T>>;
|
|
107
|
+
|
|
108
|
+
declare class RetryDelaySeq {
|
|
109
|
+
readonly min: number;
|
|
110
|
+
readonly max: number;
|
|
111
|
+
readonly spread: number;
|
|
112
|
+
readonly multiplier: number;
|
|
113
|
+
constructor(min: number, max: number, spread?: number, multiplier?: number);
|
|
114
|
+
static fixed(delayMs: number, spread?: number): RetryDelaySeq;
|
|
115
|
+
static exp(minMs: number, maxMs: number, spread?: number, multiplier?: number): RetryDelaySeq;
|
|
116
|
+
getDelay(failureCount: number): number;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface RetryDelay {
|
|
120
|
+
readonly promise: Promise<void>;
|
|
121
|
+
readonly endsAt: number;
|
|
122
|
+
readonly isLimitExceeded: boolean;
|
|
123
|
+
}
|
|
124
|
+
declare const RetryDelayNone: RetryDelay;
|
|
125
|
+
declare const RetryDelayLimitExceeded: RetryDelay;
|
|
126
|
+
declare class RetryDelayer {
|
|
127
|
+
private _cancelController;
|
|
128
|
+
delays: RetryDelaySeq;
|
|
129
|
+
limit: number | undefined;
|
|
130
|
+
readonly cancelDelaysChanged: EventHandlerSet<void>;
|
|
131
|
+
getDelay(tryIndex: number, cancellationSignal?: AbortSignal): RetryDelay;
|
|
132
|
+
cancelDelays(): void;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export { AsyncContext, AsyncContextKey, type AsyncDisposable, AsyncLock, type Disposable, DisposableBag, EventHandlerSet, type IResult, PromiseSource, Result, type RetryDelay, RetryDelayLimitExceeded, RetryDelayNone, RetryDelaySeq, RetryDelayer, abortSignalKey, errorResult, resolvedVoidPromise, result, resultFrom, resultFromAsync };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
// src/polyfills.ts
|
|
2
|
+
Symbol.metadata ??= /* @__PURE__ */ Symbol("Symbol.metadata");
|
|
3
|
+
|
|
4
|
+
// src/async-context.ts
|
|
5
|
+
var AsyncContextKey = class {
|
|
6
|
+
id;
|
|
7
|
+
defaultValue;
|
|
8
|
+
constructor(name, defaultValue) {
|
|
9
|
+
this.id = Symbol(name);
|
|
10
|
+
this.defaultValue = defaultValue;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
var abortSignalKey = new AsyncContextKey("AbortSignal", void 0);
|
|
14
|
+
var AsyncContext = class _AsyncContext {
|
|
15
|
+
static current = void 0;
|
|
16
|
+
/** Immutable empty singleton — avoids allocating new AsyncContext(). */
|
|
17
|
+
static empty = new _AsyncContext();
|
|
18
|
+
static _defaults = /* @__PURE__ */ new Map();
|
|
19
|
+
_values;
|
|
20
|
+
constructor(values) {
|
|
21
|
+
this._values = values ?? /* @__PURE__ */ new Map();
|
|
22
|
+
}
|
|
23
|
+
get(key) {
|
|
24
|
+
if (this._values.has(key.id)) return this._values.get(key.id);
|
|
25
|
+
if (_AsyncContext._defaults.has(key.id)) return _AsyncContext._defaults.get(key.id);
|
|
26
|
+
return key.defaultValue;
|
|
27
|
+
}
|
|
28
|
+
with(key, value) {
|
|
29
|
+
const copy = new Map(this._values);
|
|
30
|
+
copy.set(key.id, value);
|
|
31
|
+
return new _AsyncContext(copy);
|
|
32
|
+
}
|
|
33
|
+
activate() {
|
|
34
|
+
const prev = _AsyncContext.current;
|
|
35
|
+
_AsyncContext.current = this;
|
|
36
|
+
return { dispose: () => {
|
|
37
|
+
_AsyncContext.current = prev;
|
|
38
|
+
} };
|
|
39
|
+
}
|
|
40
|
+
run(fn) {
|
|
41
|
+
const prev = _AsyncContext.current;
|
|
42
|
+
_AsyncContext.current = this;
|
|
43
|
+
try {
|
|
44
|
+
return fn();
|
|
45
|
+
} finally {
|
|
46
|
+
_AsyncContext.current = prev;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Strip THIS exact instance from args if it's the last element (reference equality). */
|
|
50
|
+
stripFromArgs(args) {
|
|
51
|
+
return args[args.length - 1] === this ? args.slice(0, -1) : args;
|
|
52
|
+
}
|
|
53
|
+
/** Resolve: return ctx if provided, otherwise AsyncContext.current. */
|
|
54
|
+
static from(ctx) {
|
|
55
|
+
return ctx ?? _AsyncContext.current;
|
|
56
|
+
}
|
|
57
|
+
/** Extract AsyncContext from last arg if instanceof AsyncContext, else fall back to .current. */
|
|
58
|
+
static fromArgs(args) {
|
|
59
|
+
const last = args[args.length - 1];
|
|
60
|
+
return last instanceof _AsyncContext ? last : _AsyncContext.current;
|
|
61
|
+
}
|
|
62
|
+
static setDefault(key, value) {
|
|
63
|
+
_AsyncContext._defaults.set(key.id, value);
|
|
64
|
+
}
|
|
65
|
+
static getOrCreate() {
|
|
66
|
+
return _AsyncContext.current ?? _AsyncContext.empty;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// src/async-lock.ts
|
|
71
|
+
var AsyncLock = class {
|
|
72
|
+
_queue = [];
|
|
73
|
+
_locked = false;
|
|
74
|
+
get isLocked() {
|
|
75
|
+
return this._locked;
|
|
76
|
+
}
|
|
77
|
+
async acquire() {
|
|
78
|
+
if (!this._locked) {
|
|
79
|
+
this._locked = true;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
this._queue.push(resolve);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
release() {
|
|
87
|
+
if (!this._locked)
|
|
88
|
+
throw new Error("AsyncLock is not locked.");
|
|
89
|
+
const next = this._queue.shift();
|
|
90
|
+
if (next !== void 0) {
|
|
91
|
+
next();
|
|
92
|
+
} else {
|
|
93
|
+
this._locked = false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async run(fn) {
|
|
97
|
+
await this.acquire();
|
|
98
|
+
try {
|
|
99
|
+
return await fn();
|
|
100
|
+
} finally {
|
|
101
|
+
this.release();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// src/disposable.ts
|
|
107
|
+
var DisposableBag = class {
|
|
108
|
+
_items = [];
|
|
109
|
+
_disposed = false;
|
|
110
|
+
get isDisposed() {
|
|
111
|
+
return this._disposed;
|
|
112
|
+
}
|
|
113
|
+
add(item) {
|
|
114
|
+
if (this._disposed)
|
|
115
|
+
throw new Error("DisposableBag is already disposed.");
|
|
116
|
+
this._items.push(item);
|
|
117
|
+
}
|
|
118
|
+
dispose() {
|
|
119
|
+
if (this._disposed) return;
|
|
120
|
+
this._disposed = true;
|
|
121
|
+
const items = this._items;
|
|
122
|
+
this._items = [];
|
|
123
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
124
|
+
const item = items[i];
|
|
125
|
+
if (item != null && "dispose" in item) item.dispose();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async disposeAsync() {
|
|
129
|
+
if (this._disposed) return;
|
|
130
|
+
this._disposed = true;
|
|
131
|
+
const items = this._items;
|
|
132
|
+
this._items = [];
|
|
133
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
134
|
+
const item = items[i];
|
|
135
|
+
if (item == null) continue;
|
|
136
|
+
if ("disposeAsync" in item) await item.disposeAsync();
|
|
137
|
+
else if ("dispose" in item) item.dispose();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// src/promise-source.ts
|
|
143
|
+
var PromiseSource = class {
|
|
144
|
+
promise;
|
|
145
|
+
_resolve;
|
|
146
|
+
_reject;
|
|
147
|
+
_isCompleted = false;
|
|
148
|
+
constructor() {
|
|
149
|
+
this.promise = new Promise((resolve, reject) => {
|
|
150
|
+
this._resolve = resolve;
|
|
151
|
+
this._reject = reject;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
get isCompleted() {
|
|
155
|
+
return this._isCompleted;
|
|
156
|
+
}
|
|
157
|
+
resolve(value) {
|
|
158
|
+
if (this._isCompleted) return false;
|
|
159
|
+
this._isCompleted = true;
|
|
160
|
+
this._resolve(value);
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
reject(reason) {
|
|
164
|
+
if (this._isCompleted) return false;
|
|
165
|
+
this._isCompleted = true;
|
|
166
|
+
this._reject(reason);
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
var resolvedVoidPromise = Promise.resolve();
|
|
171
|
+
|
|
172
|
+
// src/events.ts
|
|
173
|
+
var EventHandlerSet = class {
|
|
174
|
+
_handlers = /* @__PURE__ */ new Set();
|
|
175
|
+
get count() {
|
|
176
|
+
return this._handlers.size;
|
|
177
|
+
}
|
|
178
|
+
add(handler) {
|
|
179
|
+
this._handlers.add(handler);
|
|
180
|
+
}
|
|
181
|
+
remove(handler) {
|
|
182
|
+
this._handlers.delete(handler);
|
|
183
|
+
}
|
|
184
|
+
trigger(arg) {
|
|
185
|
+
for (const handler of this._handlers) handler(arg);
|
|
186
|
+
}
|
|
187
|
+
clear() {
|
|
188
|
+
this._handlers.clear();
|
|
189
|
+
}
|
|
190
|
+
whenNext() {
|
|
191
|
+
const ps = new PromiseSource();
|
|
192
|
+
const handler = (arg) => {
|
|
193
|
+
this._handlers.delete(handler);
|
|
194
|
+
ps.resolve(arg);
|
|
195
|
+
};
|
|
196
|
+
this._handlers.add(handler);
|
|
197
|
+
return ps.promise;
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// src/result.ts
|
|
202
|
+
var Result = class _Result {
|
|
203
|
+
hasValue;
|
|
204
|
+
hasError;
|
|
205
|
+
_value;
|
|
206
|
+
_error;
|
|
207
|
+
constructor(value, error) {
|
|
208
|
+
this.hasValue = error === void 0;
|
|
209
|
+
this.hasError = !this.hasValue;
|
|
210
|
+
this._value = value;
|
|
211
|
+
this._error = error;
|
|
212
|
+
}
|
|
213
|
+
get value() {
|
|
214
|
+
if (this.hasError) throw this._error;
|
|
215
|
+
return this._value;
|
|
216
|
+
}
|
|
217
|
+
get error() {
|
|
218
|
+
return this._error;
|
|
219
|
+
}
|
|
220
|
+
get valueOrUndefined() {
|
|
221
|
+
return this.hasValue ? this._value : void 0;
|
|
222
|
+
}
|
|
223
|
+
static ok(value) {
|
|
224
|
+
return new _Result(value);
|
|
225
|
+
}
|
|
226
|
+
static error(error) {
|
|
227
|
+
return new _Result(void 0, error);
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
function result(value, error) {
|
|
231
|
+
return new Result(value, error);
|
|
232
|
+
}
|
|
233
|
+
function errorResult(error) {
|
|
234
|
+
return Result.error(error);
|
|
235
|
+
}
|
|
236
|
+
function resultFrom(fn) {
|
|
237
|
+
try {
|
|
238
|
+
return result(fn());
|
|
239
|
+
} catch (e) {
|
|
240
|
+
return errorResult(e);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
async function resultFromAsync(fn) {
|
|
244
|
+
try {
|
|
245
|
+
return result(await fn());
|
|
246
|
+
} catch (e) {
|
|
247
|
+
return errorResult(e);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// src/retry-delay-seq.ts
|
|
252
|
+
var DefaultSpread = 0.1;
|
|
253
|
+
var DefaultMultiplier = 1.41421356237;
|
|
254
|
+
function applySpread(origin, spread) {
|
|
255
|
+
if (spread <= 0) return origin;
|
|
256
|
+
const delta = origin * spread;
|
|
257
|
+
return origin + (Math.random() * 2 - 1) * delta;
|
|
258
|
+
}
|
|
259
|
+
var RetryDelaySeq = class _RetryDelaySeq {
|
|
260
|
+
min;
|
|
261
|
+
max;
|
|
262
|
+
spread;
|
|
263
|
+
multiplier;
|
|
264
|
+
constructor(min, max, spread = DefaultSpread, multiplier = DefaultMultiplier) {
|
|
265
|
+
this.min = min;
|
|
266
|
+
this.max = max;
|
|
267
|
+
this.spread = spread;
|
|
268
|
+
this.multiplier = multiplier;
|
|
269
|
+
}
|
|
270
|
+
static fixed(delayMs, spread = DefaultSpread) {
|
|
271
|
+
return new _RetryDelaySeq(delayMs, delayMs, spread, 1);
|
|
272
|
+
}
|
|
273
|
+
static exp(minMs, maxMs, spread = DefaultSpread, multiplier = DefaultMultiplier) {
|
|
274
|
+
return new _RetryDelaySeq(minMs, maxMs, spread, multiplier);
|
|
275
|
+
}
|
|
276
|
+
getDelay(failureCount) {
|
|
277
|
+
if (this.min < 0) throw new Error("RetryDelaySeq.min must be non-negative.");
|
|
278
|
+
if (failureCount <= 0) return 0;
|
|
279
|
+
if (this.multiplier <= 1) {
|
|
280
|
+
return Math.max(0, this.spread <= 0 ? this.min : applySpread(this.min, this.spread));
|
|
281
|
+
}
|
|
282
|
+
const multiplier = Math.pow(this.multiplier, failureCount - 1);
|
|
283
|
+
const raw = Math.min(this.max, this.min * multiplier);
|
|
284
|
+
return Math.max(0, applySpread(raw, this.spread));
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// src/retry-delayer.ts
|
|
289
|
+
var RetryDelayNone = {
|
|
290
|
+
promise: Promise.resolve(),
|
|
291
|
+
endsAt: 0,
|
|
292
|
+
isLimitExceeded: false
|
|
293
|
+
};
|
|
294
|
+
var RetryDelayLimitExceeded = {
|
|
295
|
+
promise: Promise.resolve(),
|
|
296
|
+
endsAt: 0,
|
|
297
|
+
isLimitExceeded: true
|
|
298
|
+
};
|
|
299
|
+
var RetryDelayer = class {
|
|
300
|
+
_cancelController = new AbortController();
|
|
301
|
+
delays = RetryDelaySeq.fixed(1e3);
|
|
302
|
+
limit = void 0;
|
|
303
|
+
cancelDelaysChanged = new EventHandlerSet();
|
|
304
|
+
getDelay(tryIndex, cancellationSignal) {
|
|
305
|
+
if (this.limit !== void 0 && tryIndex >= this.limit)
|
|
306
|
+
return RetryDelayLimitExceeded;
|
|
307
|
+
const delayMs = this.delays.getDelay(tryIndex);
|
|
308
|
+
if (tryIndex === 0 || delayMs <= 0)
|
|
309
|
+
return RetryDelayNone;
|
|
310
|
+
const actualDelayMs = Math.max(1, delayMs);
|
|
311
|
+
const endsAt = Date.now() + actualDelayMs;
|
|
312
|
+
const cancelSignal = this._cancelController.signal;
|
|
313
|
+
const promise = new Promise((resolve, reject) => {
|
|
314
|
+
const timer = setTimeout(() => {
|
|
315
|
+
cleanup();
|
|
316
|
+
resolve();
|
|
317
|
+
}, actualDelayMs);
|
|
318
|
+
const onCancel = () => {
|
|
319
|
+
clearTimeout(timer);
|
|
320
|
+
cleanup();
|
|
321
|
+
resolve();
|
|
322
|
+
};
|
|
323
|
+
const onAbort = () => {
|
|
324
|
+
clearTimeout(timer);
|
|
325
|
+
cleanup();
|
|
326
|
+
reject(new Error("Retry delay aborted."));
|
|
327
|
+
};
|
|
328
|
+
const cleanup = () => {
|
|
329
|
+
cancelSignal.removeEventListener("abort", onCancel);
|
|
330
|
+
cancellationSignal?.removeEventListener("abort", onAbort);
|
|
331
|
+
};
|
|
332
|
+
cancelSignal.addEventListener("abort", onCancel, { once: true });
|
|
333
|
+
cancellationSignal?.addEventListener("abort", onAbort, { once: true });
|
|
334
|
+
});
|
|
335
|
+
return { promise, endsAt, isLimitExceeded: false };
|
|
336
|
+
}
|
|
337
|
+
cancelDelays() {
|
|
338
|
+
const old = this._cancelController;
|
|
339
|
+
this._cancelController = new AbortController();
|
|
340
|
+
old.abort();
|
|
341
|
+
this.cancelDelaysChanged.trigger();
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
export {
|
|
345
|
+
AsyncContext,
|
|
346
|
+
AsyncContextKey,
|
|
347
|
+
AsyncLock,
|
|
348
|
+
DisposableBag,
|
|
349
|
+
EventHandlerSet,
|
|
350
|
+
PromiseSource,
|
|
351
|
+
Result,
|
|
352
|
+
RetryDelayLimitExceeded,
|
|
353
|
+
RetryDelayNone,
|
|
354
|
+
RetryDelaySeq,
|
|
355
|
+
RetryDelayer,
|
|
356
|
+
abortSignalKey,
|
|
357
|
+
errorResult,
|
|
358
|
+
resolvedVoidPromise,
|
|
359
|
+
result,
|
|
360
|
+
resultFrom,
|
|
361
|
+
resultFromAsync
|
|
362
|
+
};
|
|
363
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/polyfills.ts","../src/async-context.ts","../src/async-lock.ts","../src/disposable.ts","../src/promise-source.ts","../src/events.ts","../src/result.ts","../src/retry-delay-seq.ts","../src/retry-delayer.ts"],"sourcesContent":["// TC39 decorator metadata polyfill — required for Symbol.metadata support\r\n// in runtimes that don't yet implement it natively.\r\n(Symbol as any).metadata ??= Symbol(\"Symbol.metadata\");\r\n","import type { Disposable } from \"./disposable.js\";\r\n\r\n/** Typed key for values stored in AsyncContext — each subsystem defines its own keys. */\r\nexport class AsyncContextKey<T> {\r\n readonly id: symbol;\r\n readonly defaultValue: T;\r\n\r\n constructor(name: string, defaultValue: T) {\r\n this.id = Symbol(name);\r\n this.defaultValue = defaultValue;\r\n }\r\n}\r\n\r\n/** General-purpose typed context container — named for forward-compatibility with TC39 AsyncContext proposal. */\r\nexport const abortSignalKey =\r\n new AsyncContextKey<AbortSignal | undefined>(\"AbortSignal\", undefined);\r\n\r\nexport class AsyncContext {\r\n static current: AsyncContext | undefined = undefined;\r\n\r\n /** Immutable empty singleton — avoids allocating new AsyncContext(). */\r\n static readonly empty: AsyncContext = new AsyncContext();\r\n\r\n private static _defaults = new Map<symbol, unknown>();\r\n\r\n private _values: Map<symbol, unknown>;\r\n\r\n constructor(values?: Map<symbol, unknown>) {\r\n this._values = values ?? new Map();\r\n }\r\n\r\n get<T>(key: AsyncContextKey<T>): T {\r\n if (this._values.has(key.id)) return this._values.get(key.id) as T;\r\n if (AsyncContext._defaults.has(key.id)) return AsyncContext._defaults.get(key.id) as T;\r\n return key.defaultValue;\r\n }\r\n\r\n with<T>(key: AsyncContextKey<T>, value: T): AsyncContext {\r\n const copy = new Map(this._values);\r\n copy.set(key.id, value);\r\n return new AsyncContext(copy);\r\n }\r\n\r\n activate(): Disposable {\r\n const prev = AsyncContext.current;\r\n AsyncContext.current = this;\r\n return { dispose: () => { AsyncContext.current = prev; } };\r\n }\r\n\r\n run<R>(fn: () => R): R {\r\n const prev = AsyncContext.current;\r\n AsyncContext.current = this;\r\n try {\r\n return fn();\r\n } finally {\r\n AsyncContext.current = prev;\r\n }\r\n }\r\n\r\n /** Strip THIS exact instance from args if it's the last element (reference equality). */\r\n stripFromArgs(args: unknown[]): unknown[] {\r\n return args[args.length - 1] === this ? args.slice(0, -1) : args;\r\n }\r\n\r\n /** Resolve: return ctx if provided, otherwise AsyncContext.current. */\r\n static from(ctx: AsyncContext | undefined): AsyncContext | undefined {\r\n return ctx ?? AsyncContext.current;\r\n }\r\n\r\n /** Extract AsyncContext from last arg if instanceof AsyncContext, else fall back to .current. */\r\n static fromArgs(args: unknown[]): AsyncContext | undefined {\r\n const last = args[args.length - 1];\r\n return last instanceof AsyncContext ? last : AsyncContext.current;\r\n }\r\n\r\n static setDefault<T>(key: AsyncContextKey<T>, value: T): void {\r\n AsyncContext._defaults.set(key.id, value);\r\n }\r\n\r\n static getOrCreate(): AsyncContext {\r\n return AsyncContext.current ?? AsyncContext.empty;\r\n }\r\n}\r\n","/** Promise-based mutual exclusion lock for serializing async operations. */\r\nexport class AsyncLock {\r\n private _queue: (() => void)[] = [];\r\n private _locked = false;\r\n\r\n get isLocked(): boolean {\r\n return this._locked;\r\n }\r\n\r\n async acquire(): Promise<void> {\r\n if (!this._locked) {\r\n this._locked = true;\r\n return;\r\n }\r\n return new Promise<void>((resolve) => {\r\n this._queue.push(resolve);\r\n });\r\n }\r\n\r\n release(): void {\r\n if (!this._locked)\r\n throw new Error(\"AsyncLock is not locked.\");\r\n\r\n const next = this._queue.shift();\r\n if (next !== undefined) {\r\n // Transfer lock to the next waiter\r\n next();\r\n } else {\r\n this._locked = false;\r\n }\r\n }\r\n\r\n async run<T>(fn: () => T | Promise<T>): Promise<T> {\r\n await this.acquire();\r\n try {\r\n return await fn();\r\n } finally {\r\n this.release();\r\n }\r\n }\r\n}\r\n","/** Synchronous dispose pattern, similar to .NET's IDisposable. */\r\nexport interface Disposable {\r\n dispose(): void;\r\n}\r\n\r\n/** Asynchronous dispose pattern, similar to .NET's IAsyncDisposable. */\r\nexport interface AsyncDisposable {\r\n disposeAsync(): Promise<void>;\r\n}\r\n\r\n/** Aggregates multiple disposables and disposes them together. */\r\nexport class DisposableBag implements Disposable, AsyncDisposable {\r\n private _items: (Disposable | AsyncDisposable)[] = [];\r\n private _disposed = false;\r\n\r\n get isDisposed(): boolean {\r\n return this._disposed;\r\n }\r\n\r\n add(item: Disposable | AsyncDisposable): void {\r\n if (this._disposed)\r\n throw new Error(\"DisposableBag is already disposed.\");\r\n this._items.push(item);\r\n }\r\n\r\n dispose(): void {\r\n if (this._disposed) return;\r\n this._disposed = true;\r\n const items = this._items;\r\n this._items = [];\r\n // Dispose in reverse order (LIFO)\r\n for (let i = items.length - 1; i >= 0; i--) {\r\n const item = items[i];\r\n if (item != null && \"dispose\" in item) item.dispose();\r\n }\r\n }\r\n\r\n async disposeAsync(): Promise<void> {\r\n if (this._disposed) return;\r\n this._disposed = true;\r\n const items = this._items;\r\n this._items = [];\r\n for (let i = items.length - 1; i >= 0; i--) {\r\n const item = items[i];\r\n if (item == null) continue;\r\n if (\"disposeAsync\" in item) await item.disposeAsync();\r\n else if (\"dispose\" in item) item.dispose();\r\n }\r\n }\r\n}\r\n","/** Externally-resolvable promise — similar to .NET's TaskCompletionSource<T>. */\r\nexport class PromiseSource<T> {\r\n readonly promise: Promise<T>;\r\n private _resolve!: (value: T | PromiseLike<T>) => void;\r\n private _reject!: (reason?: unknown) => void;\r\n private _isCompleted = false;\r\n\r\n constructor() {\r\n this.promise = new Promise<T>((resolve, reject) => {\r\n this._resolve = resolve;\r\n this._reject = reject;\r\n });\r\n }\r\n\r\n get isCompleted(): boolean {\r\n return this._isCompleted;\r\n }\r\n\r\n resolve(value: T): boolean {\r\n if (this._isCompleted) return false;\r\n this._isCompleted = true;\r\n this._resolve(value);\r\n return true;\r\n }\r\n\r\n reject(reason?: unknown): boolean {\r\n if (this._isCompleted) return false;\r\n this._isCompleted = true;\r\n this._reject(reason);\r\n return true;\r\n }\r\n}\r\n\r\nexport const resolvedVoidPromise: Promise<void> = Promise.resolve();","import { PromiseSource } from \"./promise-source.js\";\r\n\r\n/** Typed pub/sub event handler set — similar to .NET's EventHandler<T> multicast delegate. */\r\nexport class EventHandlerSet<T> {\r\n private _handlers: Set<(arg: T) => void> = new Set();\r\n\r\n get count(): number {\r\n return this._handlers.size;\r\n }\r\n\r\n add(handler: (arg: T) => void): void {\r\n this._handlers.add(handler);\r\n }\r\n\r\n remove(handler: (arg: T) => void): void {\r\n this._handlers.delete(handler);\r\n }\r\n\r\n trigger(arg: T): void {\r\n for (const handler of this._handlers) handler(arg);\r\n }\r\n\r\n clear(): void {\r\n this._handlers.clear();\r\n }\r\n\r\n whenNext(): Promise<T> {\r\n const ps = new PromiseSource<T>();\r\n const handler = (arg: T) => {\r\n this._handlers.delete(handler);\r\n ps.resolve(arg);\r\n };\r\n this._handlers.add(handler);\r\n return ps.promise;\r\n }\r\n}\r\n","/** Common interface for types that carry a value-or-error outcome. */\r\nexport interface IResult<T> {\r\n readonly hasValue: boolean;\r\n readonly hasError: boolean;\r\n readonly value: T;\r\n readonly error: unknown;\r\n readonly valueOrUndefined: T | undefined;\r\n}\r\n\r\n/** Immutable value-or-error container — similar to .NET's Result<T>. */\r\nexport class Result<T> implements IResult<T> {\r\n readonly hasValue: boolean;\r\n readonly hasError: boolean;\r\n private readonly _value: T | undefined;\r\n private readonly _error: unknown;\r\n\r\n public constructor(value: T | undefined, error?: unknown) {\r\n this.hasValue = error === undefined;\r\n this.hasError = !this.hasValue;\r\n this._value = value;\r\n this._error = error;\r\n }\r\n\r\n get value(): T {\r\n if (this.hasError) throw this._error;\r\n return this._value as T;\r\n }\r\n\r\n get error(): unknown {\r\n return this._error;\r\n }\r\n\r\n get valueOrUndefined(): T | undefined {\r\n return this.hasValue ? this._value : undefined;\r\n }\r\n\r\n static ok<T>(value: T): Result<T> {\r\n return new Result<T>(value);\r\n }\r\n\r\n static error<T>(error: unknown): Result<T> {\r\n return new Result<T>(undefined, error);\r\n }\r\n}\r\n\r\nexport function result<T>(value: T, error?: unknown): Result<T> {\r\n return new Result<T>(value, error);\r\n}\r\n\r\nexport function errorResult<T>(error: unknown): Result<T> {\r\n return Result.error(error);\r\n}\r\n\r\nexport function resultFrom<T>(fn: () => T): Result<T> {\r\n try {\r\n return result(fn());\r\n } catch (e) {\r\n return errorResult(e);\r\n }\r\n}\r\n\r\nexport async function resultFromAsync<T>(fn: () => Promise<T>): Promise<Result<T>> {\r\n try {\r\n return result(await fn());\r\n } catch (e) {\r\n return errorResult(e);\r\n }\r\n}\r\n","// .NET counterpart: ActualLab.Time.RetryDelaySeq\r\n\r\nconst DefaultSpread = 0.1;\r\nconst DefaultMultiplier = 1.41421356237; // Math.sqrt(2)\r\n\r\nfunction applySpread(origin: number, spread: number): number {\r\n if (spread <= 0) return origin;\r\n const delta = origin * spread;\r\n return origin + (Math.random() * 2 - 1) * delta;\r\n}\r\n\r\nexport class RetryDelaySeq {\r\n readonly min: number;\r\n readonly max: number;\r\n readonly spread: number;\r\n readonly multiplier: number;\r\n\r\n constructor(min: number, max: number, spread: number = DefaultSpread, multiplier: number = DefaultMultiplier) {\r\n this.min = min;\r\n this.max = max;\r\n this.spread = spread;\r\n this.multiplier = multiplier;\r\n }\r\n\r\n static fixed(delayMs: number, spread: number = DefaultSpread): RetryDelaySeq {\r\n return new RetryDelaySeq(delayMs, delayMs, spread, 1);\r\n }\r\n\r\n static exp(\r\n minMs: number,\r\n maxMs: number,\r\n spread: number = DefaultSpread,\r\n multiplier: number = DefaultMultiplier,\r\n ): RetryDelaySeq {\r\n return new RetryDelaySeq(minMs, maxMs, spread, multiplier);\r\n }\r\n\r\n getDelay(failureCount: number): number {\r\n if (this.min < 0) throw new Error(\"RetryDelaySeq.min must be non-negative.\");\r\n if (failureCount <= 0) return 0;\r\n\r\n if (this.multiplier <= 1) {\r\n // Fixed — no exponential component\r\n return Math.max(0, this.spread <= 0 ? this.min : applySpread(this.min, this.spread));\r\n }\r\n\r\n const multiplier = Math.pow(this.multiplier, failureCount - 1);\r\n const raw = Math.min(this.max, this.min * multiplier);\r\n return Math.max(0, applySpread(raw, this.spread));\r\n }\r\n}\r\n","// .NET counterparts: ActualLab.Net.RetryDelayer, ActualLab.Net.RetryDelay\r\n\r\nimport { EventHandlerSet } from \"./events.js\";\r\nimport { RetryDelaySeq } from \"./retry-delay-seq.js\";\r\n\r\nexport interface RetryDelay {\r\n readonly promise: Promise<void>;\r\n readonly endsAt: number; // Date.now()-based timestamp, 0 = no delay\r\n readonly isLimitExceeded: boolean;\r\n}\r\n\r\nexport const RetryDelayNone: RetryDelay = {\r\n promise: Promise.resolve(),\r\n endsAt: 0,\r\n isLimitExceeded: false,\r\n};\r\n\r\nexport const RetryDelayLimitExceeded: RetryDelay = {\r\n promise: Promise.resolve(),\r\n endsAt: 0,\r\n isLimitExceeded: true,\r\n};\r\n\r\nexport class RetryDelayer {\r\n private _cancelController = new AbortController();\r\n\r\n delays: RetryDelaySeq = RetryDelaySeq.fixed(1000);\r\n limit: number | undefined = undefined;\r\n\r\n readonly cancelDelaysChanged = new EventHandlerSet<void>();\r\n\r\n getDelay(tryIndex: number, cancellationSignal?: AbortSignal): RetryDelay {\r\n if (this.limit !== undefined && tryIndex >= this.limit)\r\n return RetryDelayLimitExceeded;\r\n\r\n const delayMs = this.delays.getDelay(tryIndex);\r\n if (tryIndex === 0 || delayMs <= 0)\r\n return RetryDelayNone;\r\n\r\n const actualDelayMs = Math.max(1, delayMs);\r\n const endsAt = Date.now() + actualDelayMs;\r\n\r\n const cancelSignal = this._cancelController.signal;\r\n const promise = new Promise<void>((resolve, reject) => {\r\n const timer = setTimeout(() => {\r\n cleanup();\r\n resolve();\r\n }, actualDelayMs);\r\n\r\n const onCancel = () => {\r\n clearTimeout(timer);\r\n cleanup();\r\n resolve(); // cancelDelays resolves, does NOT reject\r\n };\r\n\r\n const onAbort = () => {\r\n clearTimeout(timer);\r\n cleanup();\r\n reject(new Error(\"Retry delay aborted.\"));\r\n };\r\n\r\n const cleanup = () => {\r\n cancelSignal.removeEventListener(\"abort\", onCancel);\r\n cancellationSignal?.removeEventListener(\"abort\", onAbort);\r\n };\r\n\r\n cancelSignal.addEventListener(\"abort\", onCancel, { once: true });\r\n cancellationSignal?.addEventListener(\"abort\", onAbort, { once: true });\r\n });\r\n\r\n return { promise, endsAt, isLimitExceeded: false };\r\n }\r\n\r\n cancelDelays(): void {\r\n const old = this._cancelController;\r\n this._cancelController = new AbortController();\r\n old.abort();\r\n this.cancelDelaysChanged.trigger();\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@actuallab/core",
|
|
3
|
+
"version": "12.1.62",
|
|
4
|
+
"description": "Core primitives for ActualLab TypeScript libraries",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsup --config ../../tsup.config.ts src/index.ts",
|
|
26
|
+
"clean": "rm -rf dist"
|
|
27
|
+
},
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"license": "MIT"
|
|
32
|
+
}
|