@actuallab/core 12.3.14 → 12.3.31

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 CHANGED
@@ -25,14 +25,20 @@ __export(index_exports, {
25
25
  AsyncLock: () => AsyncLock,
26
26
  DisposableBag: () => DisposableBag,
27
27
  EventHandlerSet: () => EventHandlerSet,
28
+ Log: () => Log,
29
+ LogLevel: () => LogLevel,
30
+ LogLevelController: () => LogLevelController,
28
31
  PromiseSource: () => PromiseSource,
29
32
  Result: () => Result,
30
33
  RetryDelayLimitExceeded: () => RetryDelayLimitExceeded,
31
34
  RetryDelayNone: () => RetryDelayNone,
32
35
  RetryDelaySeq: () => RetryDelaySeq,
33
36
  RetryDelayer: () => RetryDelayer,
37
+ RingBuffer: () => RingBuffer,
34
38
  abortSignalKey: () => abortSignalKey,
39
+ createLogProvider: () => createLogProvider,
35
40
  errorResult: () => errorResult,
41
+ initLogging: () => initLogging,
36
42
  resolvedVoidPromise: () => resolvedVoidPromise,
37
43
  result: () => result,
38
44
  resultFrom: () => resultFrom,
@@ -43,6 +49,340 @@ module.exports = __toCommonJS(index_exports);
43
49
  // src/polyfills.ts
44
50
  Symbol.metadata ??= /* @__PURE__ */ Symbol("Symbol.metadata");
45
51
 
52
+ // src/disposable.ts
53
+ var DisposableBag = class {
54
+ _items = [];
55
+ _disposed = false;
56
+ get isDisposed() {
57
+ return this._disposed;
58
+ }
59
+ add(item) {
60
+ if (this._disposed)
61
+ throw new Error("DisposableBag is already disposed.");
62
+ this._items.push(item);
63
+ }
64
+ dispose() {
65
+ if (this._disposed) return;
66
+ this._disposed = true;
67
+ const items = this._items;
68
+ this._items = [];
69
+ for (let i = items.length - 1; i >= 0; i--) {
70
+ const item = items[i];
71
+ if ("dispose" in item) item.dispose();
72
+ }
73
+ }
74
+ async disposeAsync() {
75
+ if (this._disposed) return;
76
+ this._disposed = true;
77
+ const items = this._items;
78
+ this._items = [];
79
+ for (let i = items.length - 1; i >= 0; i--) {
80
+ const item = items[i];
81
+ if ("disposeAsync" in item) await item.disposeAsync();
82
+ else if ("dispose" in item) item.dispose();
83
+ }
84
+ }
85
+ };
86
+
87
+ // src/logging-init.ts
88
+ var GlobalThisKey = "logLevels";
89
+ var StorageKey = "logLevels";
90
+ var DateStorageKey = `${StorageKey}.date`;
91
+ var MaxStorageAge = 864e5 * 3;
92
+ var sessionStorage = typeof globalThis !== "undefined" && "sessionStorage" in globalThis ? globalThis.sessionStorage : null;
93
+ function initLogging() {
94
+ Log.defaultMinLevel = 3 /* Warn */;
95
+ const minLevels = Log.minLevels;
96
+ if (typeof globalThis !== "undefined")
97
+ globalThis[GlobalThisKey] = new LogLevelController(minLevels);
98
+ const wasRestored = restore(minLevels);
99
+ if (wasRestored)
100
+ console.log("Logging: logLevels are restored");
101
+ else {
102
+ console.log("Logging: logLevels are reset");
103
+ reset(minLevels);
104
+ }
105
+ }
106
+ var LogLevelController = class {
107
+ constructor(minLevels) {
108
+ this.minLevels = minLevels;
109
+ }
110
+ /** List the current set of overrides. */
111
+ list() {
112
+ return Object.fromEntries(this.minLevels);
113
+ }
114
+ /** Print every known scope's effective level to the console as a table.
115
+ * Shows: scope, effective level, the source ('override' / 'default-key' /
116
+ * 'package-default' / 'baseline'), and the package default for reference.
117
+ * Use this from the dev console to discover what to pass to override(). */
118
+ dump() {
119
+ const baseline = Log.defaultMinLevel;
120
+ const globalOverride = this.minLevels.get("default");
121
+ const allScopes = /* @__PURE__ */ new Set([
122
+ ...Log.knownScopes,
123
+ ...Log.scopeDefaults.keys(),
124
+ ...this.minLevels.keys()
125
+ ]);
126
+ allScopes.delete("default");
127
+ const rows = {};
128
+ const sorted = [...allScopes].sort();
129
+ for (const scope of sorted) {
130
+ const override = this.minLevels.get(scope);
131
+ const pkgDefault = Log.scopeDefaults.get(scope);
132
+ let level;
133
+ let source;
134
+ if (override !== void 0) {
135
+ level = override;
136
+ source = "override";
137
+ } else if (globalOverride !== void 0) {
138
+ level = globalOverride;
139
+ source = "minLevels.get('default')";
140
+ } else if (pkgDefault !== void 0) {
141
+ level = pkgDefault;
142
+ source = "package-default";
143
+ } else {
144
+ level = baseline;
145
+ source = "baseline";
146
+ }
147
+ rows[scope] = {
148
+ level: LogLevel[level],
149
+ source,
150
+ packageDefault: pkgDefault === void 0 ? "-" : LogLevel[pkgDefault]
151
+ };
152
+ }
153
+ console.table(rows);
154
+ console.log(
155
+ `Baseline: ${LogLevel[baseline]}` + (globalOverride === void 0 ? "" : `, default override: ${LogLevel[globalOverride]}`)
156
+ );
157
+ }
158
+ /** Override the level for a single scope. Persisted to sessionStorage. */
159
+ override(scope, newLevel) {
160
+ this.minLevels.set(scope, newLevel);
161
+ persist(this.minLevels);
162
+ }
163
+ /** Override the level for every scope whose name starts with scopePrefix
164
+ * (e.g. 'rpc.' to enable verbose RPC logging). Persisted. */
165
+ overrideAll(scopePrefix, newLevel) {
166
+ const matched = /* @__PURE__ */ new Set();
167
+ for (const [scope] of this.minLevels)
168
+ if (scope.startsWith(scopePrefix)) matched.add(scope);
169
+ for (const [scope] of Log.scopeDefaults)
170
+ if (scope.startsWith(scopePrefix)) matched.add(scope);
171
+ for (const scope of matched)
172
+ this.minLevels.set(scope, newLevel);
173
+ persist(this.minLevels);
174
+ }
175
+ /** Reset all overrides; package defaults take effect again. Persisted. */
176
+ reset() {
177
+ reset(this.minLevels);
178
+ persist(this.minLevels);
179
+ }
180
+ /** Clear all overrides (no global default unless `defaultLevel` is given). */
181
+ clear(defaultLevel) {
182
+ this.minLevels.clear();
183
+ if (defaultLevel !== void 0)
184
+ this.minLevels.set("default", defaultLevel);
185
+ persist(this.minLevels);
186
+ }
187
+ };
188
+ function restore(minLevels) {
189
+ if (!sessionStorage)
190
+ return false;
191
+ const dateJson = sessionStorage.getItem(DateStorageKey);
192
+ if (!dateJson)
193
+ return false;
194
+ if (Date.now() - JSON.parse(dateJson) > MaxStorageAge)
195
+ return false;
196
+ const readJson = sessionStorage.getItem(StorageKey);
197
+ if (!readJson)
198
+ return false;
199
+ const readMinLevels = new Map(JSON.parse(readJson));
200
+ if (typeof readMinLevels.size !== "number")
201
+ return false;
202
+ minLevels.clear();
203
+ readMinLevels.forEach((value, key) => minLevels.set(key, value));
204
+ return true;
205
+ }
206
+ function persist(minLevels) {
207
+ if (!sessionStorage)
208
+ return false;
209
+ sessionStorage.setItem(DateStorageKey, JSON.stringify(Date.now()));
210
+ sessionStorage.setItem(StorageKey, JSON.stringify(Array.from(minLevels.entries())));
211
+ return true;
212
+ }
213
+ function reset(minLevels) {
214
+ minLevels.clear();
215
+ persist(minLevels);
216
+ }
217
+
218
+ // src/logging.ts
219
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
220
+ LogLevel2[LogLevel2["Debug"] = 1] = "Debug";
221
+ LogLevel2[LogLevel2["Info"] = 2] = "Info";
222
+ LogLevel2[LogLevel2["Warn"] = 3] = "Warn";
223
+ LogLevel2[LogLevel2["Error"] = 4] = "Error";
224
+ LogLevel2[LogLevel2["None"] = 1e3] = "None";
225
+ return LogLevel2;
226
+ })(LogLevel || {});
227
+ var LogRefSet = class {
228
+ constructor(capacity) {
229
+ this.capacity = capacity;
230
+ }
231
+ items = [];
232
+ idSeed = 0;
233
+ ref(data) {
234
+ const itemIndex = this.items.findIndex((el) => el.ref.target === data);
235
+ if (itemIndex >= 0) {
236
+ const existing = this.items[itemIndex];
237
+ existing.touchedAt = Date.now();
238
+ return existing.ref;
239
+ }
240
+ const id = data.__logRefId ?? this.idSeed++;
241
+ const newRef = { target: data, id };
242
+ data.__logRefId = id;
243
+ if (this.items.length >= this.capacity)
244
+ this.removeOldest();
245
+ this.items.push({ ref: newRef, touchedAt: Date.now() });
246
+ return newRef;
247
+ }
248
+ removeOldest() {
249
+ let indexToEvict = 0;
250
+ let itemToEvict = this.items[0];
251
+ for (let i = 1; i < this.items.length; i++) {
252
+ const item = this.items[i];
253
+ if (item.touchedAt < itemToEvict.touchedAt) {
254
+ itemToEvict = item;
255
+ indexToEvict = i;
256
+ }
257
+ }
258
+ this.items.splice(indexToEvict, 1);
259
+ const ref = itemToEvict.ref;
260
+ ref.target = ref.target?.toString();
261
+ }
262
+ };
263
+ var Log = class _Log {
264
+ constructor(scope, level) {
265
+ this.scope = scope;
266
+ this.level = level;
267
+ const prefix = `[${scope}]`;
268
+ switch (level) {
269
+ case 1 /* Debug */:
270
+ this.log = (...data) => console.debug(prefix, ...data);
271
+ break;
272
+ case 2 /* Info */:
273
+ this.log = (...data) => console.log(prefix, ...data);
274
+ break;
275
+ case 3 /* Warn */:
276
+ this.log = (...data) => console.warn(prefix, ...data);
277
+ break;
278
+ case 4 /* Error */:
279
+ this.log = (...data) => console.error(prefix, ...data);
280
+ break;
281
+ case 1e3 /* None */:
282
+ throw new Error("LogLevel.None cannot be used here");
283
+ }
284
+ this.trace = (...data) => console.trace(prefix, ...data);
285
+ }
286
+ static isInitialized = false;
287
+ static logRefs = new LogRefSet(10);
288
+ /** User-set overrides (persisted to sessionStorage). Empty by default. */
289
+ static minLevels = /* @__PURE__ */ new Map();
290
+ /** Per-scope defaults registered by packages via createLogProvider(). */
291
+ static scopeDefaults = /* @__PURE__ */ new Map();
292
+ /** Every scope ever requested via Log.get(). Used by logLevels.dump()
293
+ * so devs can discover what's available to override at runtime. */
294
+ static knownScopes = /* @__PURE__ */ new Set();
295
+ /** Global baseline for any scope without a default or override. */
296
+ static defaultMinLevel = 3 /* Warn */;
297
+ static loggerFactory = (scope, level) => new _Log(scope, level);
298
+ log;
299
+ trace;
300
+ static get(scope) {
301
+ if (!this.isInitialized) {
302
+ this.isInitialized = true;
303
+ initLogging();
304
+ }
305
+ this.knownScopes.add(scope);
306
+ const minLevels = this.minLevels;
307
+ const minLevel = minLevels.get(scope) ?? minLevels.get("default") ?? this.scopeDefaults.get(scope) ?? this.defaultMinLevel;
308
+ const getLogger = (level) => level >= minLevel ? this.loggerFactory(scope, level) : null;
309
+ return {
310
+ logScope: scope,
311
+ debugLog: getLogger(1 /* Debug */),
312
+ infoLog: getLogger(2 /* Info */),
313
+ warnLog: getLogger(3 /* Warn */),
314
+ errorLog: getLogger(4 /* Error */)
315
+ };
316
+ }
317
+ static ref(data) {
318
+ if (!data)
319
+ return data;
320
+ return this.logRefs.ref(data);
321
+ }
322
+ assert(predicate, ...data) {
323
+ if (!predicate)
324
+ this.log(...data);
325
+ }
326
+ };
327
+ function createLogProvider(prefix, defaults) {
328
+ for (const [scope, level] of Object.entries(defaults)) {
329
+ const fullName = prefix + scope;
330
+ Log.scopeDefaults.set(fullName, level);
331
+ Log.knownScopes.add(fullName);
332
+ }
333
+ return (scope) => Log.get(prefix + scope);
334
+ }
335
+
336
+ // src/result.ts
337
+ var Result = class _Result {
338
+ hasValue;
339
+ hasError;
340
+ _value;
341
+ _error;
342
+ constructor(value, error) {
343
+ this.hasValue = error === void 0;
344
+ this.hasError = !this.hasValue;
345
+ this._value = value;
346
+ this._error = error;
347
+ }
348
+ get value() {
349
+ if (this.hasError) throw this._error;
350
+ return this._value;
351
+ }
352
+ get error() {
353
+ return this._error;
354
+ }
355
+ get valueOrUndefined() {
356
+ return this.hasValue ? this._value : void 0;
357
+ }
358
+ static ok(value) {
359
+ return new _Result(value);
360
+ }
361
+ static error(error) {
362
+ return new _Result(void 0, error);
363
+ }
364
+ };
365
+ function result(value, error) {
366
+ return new Result(value, error);
367
+ }
368
+ function errorResult(error) {
369
+ return Result.error(error);
370
+ }
371
+ function resultFrom(fn) {
372
+ try {
373
+ return result(fn());
374
+ } catch (e) {
375
+ return errorResult(e);
376
+ }
377
+ }
378
+ async function resultFromAsync(fn) {
379
+ try {
380
+ return result(await fn());
381
+ } catch (e) {
382
+ return errorResult(e);
383
+ }
384
+ }
385
+
46
386
  // src/async-context.ts
47
387
  var AsyncContextKey = class {
48
388
  id;
@@ -150,41 +490,6 @@ var AsyncLock = class {
150
490
  }
151
491
  };
152
492
 
153
- // src/disposable.ts
154
- var DisposableBag = class {
155
- _items = [];
156
- _disposed = false;
157
- get isDisposed() {
158
- return this._disposed;
159
- }
160
- add(item) {
161
- if (this._disposed)
162
- throw new Error("DisposableBag is already disposed.");
163
- this._items.push(item);
164
- }
165
- dispose() {
166
- if (this._disposed) return;
167
- this._disposed = true;
168
- const items = this._items;
169
- this._items = [];
170
- for (let i = items.length - 1; i >= 0; i--) {
171
- const item = items[i];
172
- if ("dispose" in item) item.dispose();
173
- }
174
- }
175
- async disposeAsync() {
176
- if (this._disposed) return;
177
- this._disposed = true;
178
- const items = this._items;
179
- this._items = [];
180
- for (let i = items.length - 1; i >= 0; i--) {
181
- const item = items[i];
182
- if ("disposeAsync" in item) await item.disposeAsync();
183
- else if ("dispose" in item) item.dispose();
184
- }
185
- }
186
- };
187
-
188
493
  // src/promise-source.ts
189
494
  var PromiseSource = class {
190
495
  promise;
@@ -244,55 +549,97 @@ var EventHandlerSet = class {
244
549
  }
245
550
  };
246
551
 
247
- // src/result.ts
248
- var Result = class _Result {
249
- hasValue;
250
- hasError;
251
- _value;
252
- _error;
253
- constructor(value, error) {
254
- this.hasValue = error === void 0;
255
- this.hasError = !this.hasValue;
256
- this._value = value;
257
- this._error = error;
258
- }
259
- get value() {
260
- if (this.hasError) throw this._error;
261
- return this._value;
262
- }
263
- get error() {
264
- return this._error;
265
- }
266
- get valueOrUndefined() {
267
- return this.hasValue ? this._value : void 0;
268
- }
269
- static ok(value) {
270
- return new _Result(value);
552
+ // src/ring-buffer.ts
553
+ var RingBuffer = class {
554
+ _buffer;
555
+ _start = 0;
556
+ _end = 0;
557
+ _count = 0;
558
+ capacity;
559
+ get count() {
560
+ return this._count;
561
+ }
562
+ get isEmpty() {
563
+ return this._count === 0;
564
+ }
565
+ get isFull() {
566
+ return this._count === this.capacity;
567
+ }
568
+ get remainingCapacity() {
569
+ return this.capacity - this._count;
570
+ }
571
+ get hasRemainingCapacity() {
572
+ return this._count < this.capacity;
573
+ }
574
+ constructor(capacity) {
575
+ if (capacity < 1)
576
+ throw new RangeError(`RingBuffer capacity must be >= 1 (was ${capacity}).`);
577
+ this.capacity = capacity;
578
+ this._buffer = new Array(capacity);
579
+ }
580
+ /** Read the item at `index` (0-indexed, from head). */
581
+ get(index) {
582
+ if (index < 0 || index >= this._count)
583
+ throw new RangeError(`Index ${index} out of range [0, ${this._count}).`);
584
+ return this._buffer[(this._start + index) % this.capacity];
585
+ }
586
+ /** Add an item at the tail. Throws if the buffer is full. */
587
+ pushTail(item) {
588
+ if (this._count >= this.capacity)
589
+ throw new Error("RingBuffer is full.");
590
+ this._buffer[this._end] = item;
591
+ this._end = (this._end + 1) % this.capacity;
592
+ this._count++;
593
+ }
594
+ /** Add an item at the tail, evicting the head item if the buffer is full. */
595
+ pushTailAndMoveHeadIfFull(item) {
596
+ if (this._count >= this.capacity) {
597
+ this._buffer[this._start] = void 0;
598
+ this._start = (this._start + 1) % this.capacity;
599
+ this._count--;
600
+ }
601
+ this.pushTail(item);
602
+ }
603
+ /** Remove the head item. Throws if the buffer is empty. */
604
+ pullHead() {
605
+ if (this._count === 0)
606
+ throw new Error("RingBuffer is empty.");
607
+ const head = this._buffer[this._start];
608
+ this._buffer[this._start] = void 0;
609
+ this._start = (this._start + 1) % this.capacity;
610
+ this._count--;
611
+ return head;
612
+ }
613
+ /**
614
+ * Advance the head by `skipCount` slots, releasing references to skipped
615
+ * items so they can be garbage-collected.
616
+ */
617
+ moveHead(skipCount) {
618
+ if (skipCount === 0) return;
619
+ if (skipCount < 0 || skipCount > this._count)
620
+ throw new RangeError(`skipCount ${skipCount} out of range [0, ${this._count}].`);
621
+ for (let k = 0; k < skipCount; k++) {
622
+ this._buffer[(this._start + k) % this.capacity] = void 0;
623
+ }
624
+ this._start = (this._start + skipCount) % this.capacity;
625
+ this._count -= skipCount;
271
626
  }
272
- static error(error) {
273
- return new _Result(void 0, error);
627
+ /** Remove all items. */
628
+ clear() {
629
+ for (let i = 0; i < this.capacity; i++) this._buffer[i] = void 0;
630
+ this._start = 0;
631
+ this._end = 0;
632
+ this._count = 0;
633
+ }
634
+ /** Copy the buffer's contents to a new array, head-first. */
635
+ toArray() {
636
+ const out = new Array(this._count);
637
+ for (let i = 0; i < this._count; i++) {
638
+ out[i] = this._buffer[(this._start + i) % this.capacity];
639
+ }
640
+ return out;
274
641
  }
275
642
  };
276
- function result(value, error) {
277
- return new Result(value, error);
278
- }
279
- function errorResult(error) {
280
- return Result.error(error);
281
- }
282
- function resultFrom(fn) {
283
- try {
284
- return result(fn());
285
- } catch (e) {
286
- return errorResult(e);
287
- }
288
- }
289
- async function resultFromAsync(fn) {
290
- try {
291
- return result(await fn());
292
- } catch (e) {
293
- return errorResult(e);
294
- }
295
- }
296
643
 
297
644
  // src/retry-delay-seq.ts
298
645
  var DefaultSpread = 0.1;
@@ -399,14 +746,20 @@ var RetryDelayer = class {
399
746
  AsyncLock,
400
747
  DisposableBag,
401
748
  EventHandlerSet,
749
+ Log,
750
+ LogLevel,
751
+ LogLevelController,
402
752
  PromiseSource,
403
753
  Result,
404
754
  RetryDelayLimitExceeded,
405
755
  RetryDelayNone,
406
756
  RetryDelaySeq,
407
757
  RetryDelayer,
758
+ RingBuffer,
408
759
  abortSignalKey,
760
+ createLogProvider,
409
761
  errorResult,
762
+ initLogging,
410
763
  resolvedVoidPromise,
411
764
  result,
412
765
  resultFrom,