@adviser/cement 0.5.1 → 0.5.3

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.
Files changed (115) hide show
  1. package/cjs/keyed-ng.test.cjs +6 -5
  2. package/cjs/keyed-ng.test.cjs.map +1 -1
  3. package/cjs/on-func.cjs +15 -1
  4. package/cjs/on-func.cjs.map +1 -1
  5. package/cjs/on-func.d.ts +1 -0
  6. package/cjs/on-func.d.ts.map +1 -1
  7. package/cjs/on-func.test.cjs +34 -0
  8. package/cjs/on-func.test.cjs.map +1 -1
  9. package/cjs/resolve-once.cjs +60 -32
  10. package/cjs/resolve-once.cjs.map +1 -1
  11. package/cjs/resolve-once.d.ts +30 -19
  12. package/cjs/resolve-once.d.ts.map +1 -1
  13. package/cjs/resolve-once.test.cjs +105 -27
  14. package/cjs/resolve-once.test.cjs.map +1 -1
  15. package/cjs/result.cjs +6 -0
  16. package/cjs/result.cjs.map +1 -1
  17. package/cjs/result.d.ts +2 -0
  18. package/cjs/result.d.ts.map +1 -1
  19. package/cjs/timeouted.cjs +13 -18
  20. package/cjs/timeouted.cjs.map +1 -1
  21. package/cjs/timeouted.d.ts +23 -27
  22. package/cjs/timeouted.d.ts.map +1 -1
  23. package/cjs/timeouted.test.cjs +31 -1
  24. package/cjs/timeouted.test.cjs.map +1 -1
  25. package/cjs/utils/to-sorted.d.ts +1 -1
  26. package/cjs/utils/to-sorted.d.ts.map +1 -1
  27. package/cjs/version.cjs +1 -1
  28. package/deno.json +1 -1
  29. package/esm/keyed-ng.test.js +6 -5
  30. package/esm/keyed-ng.test.js.map +1 -1
  31. package/esm/on-func.d.ts +1 -0
  32. package/esm/on-func.d.ts.map +1 -1
  33. package/esm/on-func.js +15 -1
  34. package/esm/on-func.js.map +1 -1
  35. package/esm/on-func.test.js +34 -0
  36. package/esm/on-func.test.js.map +1 -1
  37. package/esm/resolve-once.d.ts +30 -19
  38. package/esm/resolve-once.d.ts.map +1 -1
  39. package/esm/resolve-once.js +60 -32
  40. package/esm/resolve-once.js.map +1 -1
  41. package/esm/resolve-once.test.js +73 -28
  42. package/esm/resolve-once.test.js.map +1 -1
  43. package/esm/result.d.ts +2 -0
  44. package/esm/result.d.ts.map +1 -1
  45. package/esm/result.js +6 -0
  46. package/esm/result.js.map +1 -1
  47. package/esm/timeouted.d.ts +23 -27
  48. package/esm/timeouted.d.ts.map +1 -1
  49. package/esm/timeouted.js +13 -18
  50. package/esm/timeouted.js.map +1 -1
  51. package/esm/timeouted.test.js +32 -2
  52. package/esm/timeouted.test.js.map +1 -1
  53. package/esm/utils/to-sorted.d.ts +1 -1
  54. package/esm/utils/to-sorted.d.ts.map +1 -1
  55. package/esm/version.js +1 -1
  56. package/package.json +2 -2
  57. package/src/on-func.ts +39 -1
  58. package/src/resolve-once.ts +192 -106
  59. package/src/result.ts +7 -0
  60. package/src/timeouted.ts +39 -65
  61. package/src/utils/to-sorted.ts +1 -1
  62. package/ts/cjs/keyed-ng.test.js +6 -5
  63. package/ts/cjs/keyed-ng.test.js.map +1 -1
  64. package/ts/cjs/on-func.d.ts +1 -0
  65. package/ts/cjs/on-func.d.ts.map +1 -1
  66. package/ts/cjs/on-func.js +15 -1
  67. package/ts/cjs/on-func.js.map +1 -1
  68. package/ts/cjs/on-func.test.js +34 -0
  69. package/ts/cjs/on-func.test.js.map +1 -1
  70. package/ts/cjs/resolve-once.d.ts +30 -19
  71. package/ts/cjs/resolve-once.d.ts.map +1 -1
  72. package/ts/cjs/resolve-once.js +60 -32
  73. package/ts/cjs/resolve-once.js.map +1 -1
  74. package/ts/cjs/resolve-once.test.js +105 -27
  75. package/ts/cjs/resolve-once.test.js.map +1 -1
  76. package/ts/cjs/result.d.ts +2 -0
  77. package/ts/cjs/result.d.ts.map +1 -1
  78. package/ts/cjs/result.js +6 -0
  79. package/ts/cjs/result.js.map +1 -1
  80. package/ts/cjs/timeouted.d.ts +23 -27
  81. package/ts/cjs/timeouted.d.ts.map +1 -1
  82. package/ts/cjs/timeouted.js +13 -18
  83. package/ts/cjs/timeouted.js.map +1 -1
  84. package/ts/cjs/timeouted.test.js +31 -1
  85. package/ts/cjs/timeouted.test.js.map +1 -1
  86. package/ts/cjs/utils/to-sorted.d.ts +1 -1
  87. package/ts/cjs/utils/to-sorted.d.ts.map +1 -1
  88. package/ts/cjs/version.js +1 -1
  89. package/ts/esm/keyed-ng.test.js +6 -5
  90. package/ts/esm/keyed-ng.test.js.map +1 -1
  91. package/ts/esm/on-func.d.ts +1 -0
  92. package/ts/esm/on-func.d.ts.map +1 -1
  93. package/ts/esm/on-func.js +15 -1
  94. package/ts/esm/on-func.js.map +1 -1
  95. package/ts/esm/on-func.test.js +34 -0
  96. package/ts/esm/on-func.test.js.map +1 -1
  97. package/ts/esm/resolve-once.d.ts +30 -19
  98. package/ts/esm/resolve-once.d.ts.map +1 -1
  99. package/ts/esm/resolve-once.js +60 -32
  100. package/ts/esm/resolve-once.js.map +1 -1
  101. package/ts/esm/resolve-once.test.js +73 -28
  102. package/ts/esm/resolve-once.test.js.map +1 -1
  103. package/ts/esm/result.d.ts +2 -0
  104. package/ts/esm/result.d.ts.map +1 -1
  105. package/ts/esm/result.js +6 -0
  106. package/ts/esm/result.js.map +1 -1
  107. package/ts/esm/timeouted.d.ts +23 -27
  108. package/ts/esm/timeouted.d.ts.map +1 -1
  109. package/ts/esm/timeouted.js +13 -18
  110. package/ts/esm/timeouted.js.map +1 -1
  111. package/ts/esm/timeouted.test.js +32 -2
  112. package/ts/esm/timeouted.test.js.map +1 -1
  113. package/ts/esm/utils/to-sorted.d.ts +1 -1
  114. package/ts/esm/utils/to-sorted.d.ts.map +1 -1
  115. package/ts/esm/version.js +1 -1
@@ -15,11 +15,13 @@
15
15
  */
16
16
 
17
17
  import { Future } from "./future.js";
18
- import { UnPromisify, isPromise } from "./is-promise.js";
18
+ import { isPromise } from "./is-promise.js";
19
19
  import { UnregFn } from "./lru-map-set.js";
20
20
  import { Result } from "./result.js";
21
21
  import { Option } from "./option.js";
22
22
  import { KeyedIf, KeyedNg, KeyedNgItem, KeyedNgItemWithoutValue, KeyedNgOptions } from "./keyed-ng.js";
23
+ import { runtimeFn } from "./runtime.js";
24
+ import { Writable } from "ts-essentials";
23
25
 
24
26
  /**
25
27
  * Internal item representing a queued function in a ResolveSeq sequence.
@@ -143,33 +145,58 @@ export class ResolveSeq<T, CTX extends NonNullable<object> = object> {
143
145
  type ResolveState = "initial" | "processed" | "waiting" | "processing";
144
146
 
145
147
  /**
146
- * Type helper that unwraps Promise types to their resolved value type.
148
+ * Type helper that awaits Promise types and passes through non-Promise types.
149
+ * This ensures that if a function returns a Promise<T>, the once method also returns Promise<T>,
150
+ * and if the function returns T (non-Promise), once returns T directly.
147
151
  *
148
- * @template R - The type to unwrap
152
+ * Uses the built-in Awaited utility type to properly handle nested Promises and thenable objects.
153
+ *
154
+ * @template R - The type to process
149
155
  *
150
156
  * @example
151
157
  * ```typescript
152
158
  * type A = ResultOnce<Promise<number>>; // Promise<number>
153
159
  * type B = ResultOnce<string>; // string
160
+ * type C = ResultOnce<number>; // number
161
+ * type D = ResultOnce<Promise<Promise<number>>>; // Promise<number>
154
162
  * ```
155
163
  */
156
- export type ResultOnce<R> = R extends Promise<infer T> ? Promise<T> : R;
164
+ export type ResultOnce<R> = R extends Promise<unknown> ? Promise<Awaited<R>> : R;
165
+
166
+ export interface OnceActionArg<R, CTX> {
167
+ readonly ctx: CTX;
168
+ readonly self: ResolveOnceIf<R, CTX>;
169
+ }
157
170
 
171
+ // export type OnceAction<R, CTX, RET> = (arg: OnceActionArg<R, CTX>) => RET extends Promise<R> ? Promise<R> : R;
158
172
  /**
159
173
  * Interface defining the contract for ResolveOnce-like objects.
160
174
  * @template R - The return type
161
175
  * @template CTX - Optional context type
162
176
  */
163
- export interface ResolveOnceIf<R, CTX = void> {
177
+ export interface ResolveOnceIf<R, CTX> {
164
178
  get ready(): boolean;
165
- get value(): UnPromisify<R> | undefined;
179
+ get value(): R | undefined;
166
180
  get error(): Error | undefined;
167
181
  get state(): ResolveState;
168
182
 
169
- once<R>(fn: (c?: CTX) => R): ResultOnce<R>;
170
- reset<R>(fn?: (c?: CTX) => R): ResultOnce<R>;
183
+ setResetAfter(ms?: number): void;
184
+
185
+ once<RET extends R | Promise<R>>(fn: (arg: OnceActionArg<R, CTX>) => RET): ResultOnce<RET>;
186
+ reset<RET extends R | Promise<R>>(fn?: (arg: OnceActionArg<R, CTX>) => RET): ResultOnce<RET>;
187
+
188
+ setProcessed(state: StateInstance): void;
171
189
  }
172
190
 
191
+ export interface SyncOrAsyncIf<T> {
192
+ get value(): T | undefined;
193
+ get error(): Error | undefined;
194
+ get queueLength(): number;
195
+
196
+ resolve<RET extends T | Promise<T>>(fn: () => RET): ResultOnce<RET>;
197
+ }
198
+ export type SyncOrAsync<T> = Option<SyncOrAsyncIf<T>>;
199
+
173
200
  /**
174
201
  * Synchronous version of ResolveOnce for functions that return non-promise values.
175
202
  *
@@ -180,7 +207,7 @@ export interface ResolveOnceIf<R, CTX = void> {
180
207
  * @template CTX - Optional context type
181
208
  * @internal
182
209
  */
183
- export class SyncResolveOnce<T, CTX = void> {
210
+ export class SyncResolveOnce<T, CTX> implements SyncOrAsyncIf<T> {
184
211
  #value?: T;
185
212
  #error?: Error;
186
213
 
@@ -223,10 +250,10 @@ export class SyncResolveOnce<T, CTX = void> {
223
250
  * @returns The result of the function
224
251
  * @throws Error if the function returned a promise (use AsyncResolveOnce instead)
225
252
  */
226
- resolve(fn: (ctx?: CTX) => T): T {
253
+ resolve<RET extends T | Promise<T>>(fn: () => RET): ResultOnce<RET> {
227
254
  if (this.#state.isProcessing()) {
228
255
  try {
229
- this.#value = fn(this.#rOnce._ctx);
256
+ this.#value = fn() as unknown as T;
230
257
  } catch (e) {
231
258
  this.#error = e as Error;
232
259
  } finally {
@@ -240,24 +267,24 @@ export class SyncResolveOnce<T, CTX = void> {
240
267
  if (this.#error) {
241
268
  throw this.#error;
242
269
  }
243
- return this.#value as T;
270
+ return this.#value as ResultOnce<RET>;
244
271
  }
245
272
 
246
- /**
247
- * Resets the cached state, allowing the function to be executed again.
248
- *
249
- * @param fn - Optional function to execute immediately after reset
250
- * @returns The result if fn provided, undefined otherwise
251
- */
252
- reset(fn?: (c?: CTX) => T): T | undefined {
253
- this.#value = undefined;
254
- this.#error = undefined;
255
- if (fn) {
256
- this.#state.setProcessing();
257
- return this.resolve(fn);
258
- }
259
- return undefined as T;
260
- }
273
+ // /**
274
+ // * Resets the cached state, allowing the function to be executed again.
275
+ // *
276
+ // * @param fn - Optional function to execute immediately after reset
277
+ // * @returns The result if fn provided, undefined otherwise
278
+ // */
279
+ // reset(fn?: () => RET): T | undefined {
280
+ // this.#value = undefined;
281
+ // this.#error = undefined;
282
+ // if (fn) {
283
+ // this.#state.setProcessing();
284
+ // return this.resolve(fn);
285
+ // }
286
+ // return undefined as T;
287
+ // }
261
288
  }
262
289
 
263
290
  /**
@@ -267,19 +294,19 @@ export class SyncResolveOnce<T, CTX = void> {
267
294
  */
268
295
  class AsyncResolveItem<T, CTX> {
269
296
  readonly id: number = Math.random();
270
- readonly #toResolve: Promise<UnPromisify<T>>;
271
- #value: Option<UnPromisify<T>> = Option.None();
297
+ readonly #toResolve: Promise<T>;
298
+ #value: Option<T> = Option.None();
272
299
  #error?: Error;
273
300
  readonly #state: StateInstance;
274
- readonly #rOnce: ResolveOnce<T, CTX>;
301
+ readonly #rOnce: ResolveOnceIf<T, CTX>;
275
302
 
276
- constructor(fn: Promise<UnPromisify<T>>, rOnce: ResolveOnce<T, CTX>, state: StateInstance) {
303
+ constructor(fn: Promise<T>, rOnce: ResolveOnceIf<T, CTX>, state: StateInstance) {
277
304
  this.#toResolve = fn;
278
305
  this.#state = state;
279
306
  this.#rOnce = rOnce;
280
307
  }
281
308
 
282
- get value(): UnPromisify<T> | undefined {
309
+ get value(): T | undefined {
283
310
  return this.#value.IsSome() ? this.#value.unwrap() : undefined;
284
311
  }
285
312
 
@@ -287,7 +314,7 @@ class AsyncResolveItem<T, CTX> {
287
314
  return this.#error;
288
315
  }
289
316
 
290
- readonly #queue: Future<UnPromisify<T>>[] = [];
317
+ readonly #queue: Future<T>[] = [];
291
318
 
292
319
  get queuelength(): number {
293
320
  return this.#queue.length;
@@ -300,7 +327,7 @@ class AsyncResolveItem<T, CTX> {
300
327
  return this.#state.isProcessed() && this.#queue.length === 0;
301
328
  }
302
329
 
303
- #resolveFuture(future?: Future<UnPromisify<T>>): void {
330
+ #resolveFuture(future?: Future<T>): void {
304
331
  if (!future) {
305
332
  return;
306
333
  }
@@ -313,7 +340,7 @@ class AsyncResolveItem<T, CTX> {
313
340
  }
314
341
  }
315
342
 
316
- #promiseResult(): Promise<UnPromisify<T>> {
343
+ #promiseResult(): Promise<T> {
317
344
  if (this.#error) {
318
345
  return Promise.reject(this.#error);
319
346
  }
@@ -326,9 +353,9 @@ class AsyncResolveItem<T, CTX> {
326
353
  /**
327
354
  * Resolves the async operation, queuing the request if already in progress.
328
355
  */
329
- resolve(): T {
356
+ resolve<RET extends T | Promise<T>>(_fn: () => RET): ResultOnce<RET> {
330
357
  if (this.#state.isWaiting()) {
331
- const future = new Future<UnPromisify<T>>();
358
+ const future = new Future<T>();
332
359
  this.#queue.push(future);
333
360
  this.#toResolve
334
361
  .then((value) => {
@@ -344,11 +371,11 @@ class AsyncResolveItem<T, CTX> {
344
371
  this.#resolveFuture(this.#queue.shift());
345
372
  }
346
373
  });
347
- return future.asPromise() as T;
374
+ return future.asPromise() as ResultOnce<RET>;
348
375
  }
349
376
 
350
377
  if (this.#state.isProcessed()) {
351
- return this.#promiseResult() as T;
378
+ return this.#promiseResult() as ResultOnce<RET>;
352
379
  }
353
380
  // if (this.#state.isWaiting()) {
354
381
  // const future = new Future<UnPromisify<T>>();
@@ -371,23 +398,23 @@ class AsyncResolveItem<T, CTX> {
371
398
  * @internal
372
399
  */
373
400
 
374
- function isAsyncResolveOnce<T, CTX>(obj: SyncOrAsync<T, CTX>): obj is Option<AsyncResolveOnce<T, CTX>> {
401
+ function isAsyncResolveOnce<T, CTX>(obj: SyncOrAsync<T>): obj is Option<AsyncResolveOnce<T, CTX>> {
375
402
  return obj.IsSome() && obj.Unwrap() instanceof AsyncResolveOnce;
376
403
  }
377
404
 
378
- export class AsyncResolveOnce<T, CTX = void> {
405
+ export class AsyncResolveOnce<T, CTX> implements SyncOrAsyncIf<T> {
379
406
  // #state: ResolveState = "initial";
380
407
  readonly #state: StateInstance;
381
408
 
382
409
  readonly #queue: AsyncResolveItem<T, CTX>[];
383
410
 
384
- readonly #rOnce: ResolveOnce<T, CTX>;
385
- //readonly #ctx?: CTX;
386
- constructor(rOnce: ResolveOnce<T, CTX>, state: StateInstance, prev: SyncOrAsync<T, CTX>) {
411
+ readonly #rOnce: ResolveOnceIf<T, CTX>;
412
+ //readonly #ctx?: RET;
413
+ constructor(rOnce: ResolveOnceIf<T, CTX>, state: StateInstance, prev: SyncOrAsync<T>) {
387
414
  this.#state = state;
388
415
  this.#rOnce = rOnce;
389
416
  if (isAsyncResolveOnce(prev)) {
390
- this.#queue = [...prev.unwrap().#queue];
417
+ this.#queue = [...(prev.unwrap().#queue as AsyncResolveItem<T, CTX>[])];
391
418
  } else {
392
419
  this.#queue = [];
393
420
  }
@@ -411,7 +438,7 @@ export class AsyncResolveOnce<T, CTX = void> {
411
438
  /**
412
439
  * Gets the cached resolved value if available.
413
440
  */
414
- get value(): UnPromisify<T> | undefined {
441
+ get value(): T | undefined {
415
442
  if (this.#state.isInitial()) {
416
443
  return undefined;
417
444
  }
@@ -435,16 +462,16 @@ export class AsyncResolveOnce<T, CTX = void> {
435
462
  * @param fn - The async function to execute
436
463
  * @returns A promise that resolves to the function's result
437
464
  */
438
- resolve(fn: (ctx?: CTX) => T): T {
465
+ resolve<RET extends T | Promise<T>>(fn: () => RET): ResultOnce<RET> {
439
466
  if (this.#state.isProcessing()) {
440
467
  this.#state.setWaiting();
441
- let promiseResult: Promise<UnPromisify<T>>;
468
+ let promiseResult: Promise<T>;
442
469
  try {
443
- const couldBePromise = fn(this.#rOnce._ctx);
470
+ const couldBePromise = fn();
444
471
  if (!isPromise(couldBePromise)) {
445
- promiseResult = Promise.resolve(couldBePromise as UnPromisify<T>);
472
+ promiseResult = Promise.resolve(couldBePromise) as Promise<T>;
446
473
  } else {
447
- promiseResult = couldBePromise as Promise<UnPromisify<T>>;
474
+ promiseResult = couldBePromise as Promise<T>;
448
475
  }
449
476
  } catch (e) {
450
477
  promiseResult = Promise.reject(e as Error);
@@ -459,22 +486,22 @@ export class AsyncResolveOnce<T, CTX = void> {
459
486
  .reverse()
460
487
  .forEach((idx) => this.#queue.splice(idx, 1));
461
488
 
462
- return this.#active().resolve();
489
+ return this.#active().resolve(fn);
463
490
  }
464
491
 
465
- /**
466
- * Resets the cached state, allowing the function to be executed again.
467
- *
468
- * @param fn - Optional function to execute immediately after reset
469
- * @returns The result if fn provided, undefined otherwise
470
- */
471
- reset(fn?: (c?: CTX) => T): T {
472
- this.#state.setProcessing();
473
- if (fn) {
474
- return this.resolve(fn);
475
- }
476
- return undefined as T;
477
- }
492
+ // /**
493
+ // * Resets the cached state, allowing the function to be executed again.
494
+ // *
495
+ // * @param fn - Optional function to execute immediately after reset
496
+ // * @returns The result if fn provided, undefined otherwise
497
+ // */
498
+ // reset(fn?: () => ResultOnce<T>): T {
499
+ // this.#state.setProcessing();
500
+ // if (fn) {
501
+ // return this.resolve(fn);
502
+ // }
503
+ // return undefined as T;
504
+ // }
478
505
  }
479
506
 
480
507
  /**
@@ -504,6 +531,7 @@ export class AsyncResolveOnce<T, CTX = void> {
504
531
 
505
532
  export interface ResolveOnceOpts {
506
533
  readonly resetAfter?: number; // milliseconds after which to reset the cached value
534
+ readonly skipUnref?: boolean; // skip unref() on the reset timer
507
535
  }
508
536
 
509
537
  class StateInstance {
@@ -548,20 +576,24 @@ class StateInstance {
548
576
  }
549
577
  }
550
578
 
551
- type SyncOrAsync<T, CTX> = Option<SyncResolveOnce<T, CTX> | AsyncResolveOnce<T, CTX>>;
579
+ // type SyncOrAsync<T, CTX> = Option<SyncResolveOnce<T, CTX> | AsyncResolveOnce<T, CTX>>;
552
580
 
553
581
  export class ResolveOnce<T, CTX = void> implements ResolveOnceIf<T, CTX> {
554
582
  #state = new StateInstance();
555
583
 
556
- #syncOrAsync: SyncOrAsync<T, CTX> = Option.None();
584
+ #syncOrAsync: SyncOrAsync<T> = Option.None();
557
585
 
558
- readonly #opts: ResolveOnceOpts;
586
+ readonly #opts: Writable<ResolveOnceOpts>;
559
587
  resetAfterTimer?: ReturnType<typeof setTimeout>;
560
588
 
561
- readonly _ctx?: CTX;
589
+ readonly _onceArg: OnceActionArg<T, CTX>;
590
+
562
591
  constructor(ctx?: CTX, opts?: ResolveOnceOpts) {
563
- this._ctx = ctx;
564
- this.#opts = opts ?? {};
592
+ this.#opts = { ...(opts ?? {}) };
593
+ this._onceArg = {
594
+ ctx: ctx as CTX,
595
+ self: this,
596
+ };
565
597
  }
566
598
 
567
599
  get state(): ResolveState {
@@ -583,8 +615,49 @@ export class ResolveOnce<T, CTX = void> implements ResolveOnceIf<T, CTX> {
583
615
  this.#state.setProcessed();
584
616
  if (typeof this.#opts.resetAfter === "number" && this.#opts.resetAfter > 0) {
585
617
  this.resetAfterTimer = setTimeout(() => {
586
- this.reset();
618
+ void this.reset();
587
619
  }, this.#opts.resetAfter);
620
+ if (!this.#opts.skipUnref && this.resetAfterTimer) {
621
+ // node solution
622
+ const runtime = runtimeFn();
623
+ switch (true) {
624
+ case runtime.isDeno:
625
+ {
626
+ let id = this.resetAfterTimer as unknown as number;
627
+ if (typeof Deno.unrefTimer === "function") {
628
+ if (typeof this.resetAfterTimer === "number") {
629
+ id = this.resetAfterTimer;
630
+ } else {
631
+ try {
632
+ const ret = Reflect.ownKeys(this.resetAfterTimer).find((key) => {
633
+ return key.toString().includes("timerId");
634
+ });
635
+ if (ret) {
636
+ id = this.resetAfterTimer[ret as keyof typeof this.resetAfterTimer] as unknown as number;
637
+ // eslint-disable-next-line no-console
638
+ console.warn("Deno.unrefTimer timerId from struct:", id, "version:", globalThis.Deno?.version);
639
+ }
640
+ } catch (e) {
641
+ // eslint-disable-next-line no-console
642
+ console.warn(
643
+ "Deno.unrefTimer failed to get timerId",
644
+ e,
645
+ "id:",
646
+ this.resetAfterTimer,
647
+ "version:",
648
+ globalThis.Deno?.version,
649
+ );
650
+ }
651
+ }
652
+ Deno.unrefTimer(id);
653
+ }
654
+ }
655
+ break;
656
+ case runtime.isNodeIsh:
657
+ (this.resetAfterTimer as unknown as { unref: () => void }).unref();
658
+ break;
659
+ }
660
+ }
588
661
  }
589
662
  }
590
663
  }
@@ -593,11 +666,11 @@ export class ResolveOnce<T, CTX = void> implements ResolveOnceIf<T, CTX> {
593
666
  return !this.#state.isInitial();
594
667
  }
595
668
 
596
- get value(): UnPromisify<T> | undefined {
669
+ get value(): T | undefined {
597
670
  if (this.#state.isInitial()) {
598
671
  return undefined;
599
672
  }
600
- return this.#syncOrAsync.Unwrap().value as UnPromisify<T>;
673
+ return this.#syncOrAsync.Unwrap().value;
601
674
  }
602
675
 
603
676
  get queueLength(): number {
@@ -634,58 +707,69 @@ export class ResolveOnce<T, CTX = void> implements ResolveOnceIf<T, CTX> {
634
707
  // this.#state = value;
635
708
  // }
636
709
 
637
- once<R>(fn: (c: CTX, prev?: T) => R): ResultOnce<R> {
638
- let resultFn: (ctx: CTX) => R;
710
+ once<RET extends T | Promise<T>>(fn: (arg: OnceActionArg<T, CTX>) => RET): ResultOnce<RET> {
711
+ let resultFn: () => RET;
639
712
  if (this.#state.isInitial()) {
640
713
  const state = this.#state;
641
714
  try {
642
715
  state.setProcessing();
643
- let prev: T | undefined = undefined;
644
- if (this.#syncOrAsync.IsSome()) {
645
- prev = this.#syncOrAsync.Unwrap().value as T;
646
- }
647
- const isSyncOrAsync = fn(this._ctx ?? ({} as CTX), prev);
716
+ // let prev: T | undefined = undefined;
717
+ // if (this.#syncOrAsync.IsSome()) {
718
+ // prev = this.#syncOrAsync.Unwrap().value as T;
719
+ // }
720
+ const isSyncOrAsync = fn(this._onceArg);
648
721
  if (isPromise(isSyncOrAsync)) {
649
722
  this.#syncOrAsync = Option.Some(new AsyncResolveOnce<T, CTX>(this, state, this.#syncOrAsync));
650
723
  } else {
651
724
  this.#syncOrAsync = Option.Some(new SyncResolveOnce<T, CTX>(this, state));
652
725
  }
653
- resultFn = (): R => isSyncOrAsync;
726
+ resultFn = (): RET => isSyncOrAsync;
654
727
  } catch (e) {
655
728
  this.#syncOrAsync = Option.Some(new SyncResolveOnce<T, CTX>(this, state));
656
- resultFn = (): R => {
729
+ resultFn = (): RET => {
657
730
  throw e;
658
731
  };
659
732
  }
660
733
  } else {
661
- resultFn = fn;
734
+ resultFn = (): RET => fn(this._onceArg);
662
735
  }
663
736
  if (!this.#syncOrAsync) {
664
737
  throw new Error(`ResolveOnce.once impossible: state=${this.#state.getResolveState()}`);
665
738
  }
666
- return this.#syncOrAsync.Unwrap().resolve(resultFn as (c?: CTX) => never) as ResultOnce<R>;
739
+ return this.#syncOrAsync.Unwrap().resolve(resultFn);
667
740
  }
668
741
 
669
- reset<R>(fn?: (c: CTX) => R): ResultOnce<R> {
742
+ reset<RET extends T | Promise<T>>(fn?: (arg: OnceActionArg<T, CTX>) => RET): ResultOnce<RET> {
670
743
  if (this.#state.isInitial()) {
671
744
  if (!fn) {
672
- return undefined as ResultOnce<R>;
745
+ return undefined as ResultOnce<RET>;
673
746
  }
674
- return this.once(fn as (c: CTX) => R);
747
+ return this.once(fn);
675
748
  }
676
749
  if (this.#state.isProcessing()) {
677
750
  // eslint-disable-next-line no-console
678
751
  console.warn("ResolveOnce.reset dropped was called while processing");
679
- return undefined as ResultOnce<R>;
752
+ return undefined as ResultOnce<RET>;
680
753
  }
681
- let ret = undefined as ResultOnce<R>;
754
+ let ret = undefined as ResultOnce<RET>;
682
755
  this.#state = new StateInstance();
683
756
  if (fn) {
684
- ret = this.once(fn as (c: CTX) => R);
757
+ ret = this.once(fn);
685
758
  // ret = this.#syncOrAsync.Unwrap().reset(fn as (c?: CTX) => never) as ResultOnce<R>
686
759
  }
687
760
  return ret;
688
761
  }
762
+
763
+ setResetAfter(ms?: number): void {
764
+ if (this.resetAfterTimer) {
765
+ clearTimeout(this.resetAfterTimer);
766
+ }
767
+ if (typeof ms === "number" && ms > 0) {
768
+ this.#opts.resetAfter = ms;
769
+ } else {
770
+ this.#opts.resetAfter = undefined;
771
+ }
772
+ }
689
773
  }
690
774
 
691
775
  // /**
@@ -788,19 +872,21 @@ export interface KeyedResolveOnceItem<K, T, CTX extends NonNullable<object>> {
788
872
  * .once(({ givenKey, ctx }) => fetchUser(givenKey, ctx));
789
873
  * ```
790
874
  */
791
- export class KeyedResolvOnce<T extends WithOptionalReset<PT>, K = string, CTX extends NonNullable<object> = object, PT = T>
792
- implements
793
- Omit<
794
- // KeyedIf<ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>>, WithOptionalReset<T>, K>
795
- KeyedIf<
796
- KeyedNgItem<K, ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>>, CTX>,
797
- ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>>,
798
- K,
799
- CTX
800
- >,
801
- "entries" | "forEach" | "onSet" | "onDelete" | "values" | "setParam"
802
- >
803
- {
875
+ export class KeyedResolvOnce<
876
+ T extends WithOptionalReset<PT>,
877
+ K = string,
878
+ CTX extends NonNullable<object> = object,
879
+ PT = T,
880
+ > implements Omit<
881
+ // KeyedIf<ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>>, WithOptionalReset<T>, K>
882
+ KeyedIf<
883
+ KeyedNgItem<K, ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>>, CTX>,
884
+ ResolveOnce<T, KeyedNgItemWithoutValue<K, CTX>>,
885
+ K,
886
+ CTX
887
+ >,
888
+ "entries" | "forEach" | "onSet" | "onDelete" | "values" | "setParam"
889
+ > {
804
890
  /** @internal */
805
891
  readonly _keyed: KeyedNg<K, ResolveOnce<WithOptionalReset<T>, KeyedNgItemWithoutValue<K, CTX>>, CTX>;
806
892
 
@@ -981,7 +1067,7 @@ export class KeyedResolvOnce<T extends WithOptionalReset<PT>, K = string, CTX ex
981
1067
  */
982
1068
  unget(key: K): void {
983
1069
  const item = this._keyed.getItem(key);
984
- item.value.reset?.();
1070
+ void item.value.reset?.();
985
1071
  return this._keyed.delete(item.givenKey);
986
1072
  }
987
1073
 
@@ -993,7 +1079,7 @@ export class KeyedResolvOnce<T extends WithOptionalReset<PT>, K = string, CTX ex
993
1079
  */
994
1080
  reset(): void {
995
1081
  for (const v of this._keyed.values()) {
996
- v.value.reset?.();
1082
+ void v.value.reset?.();
997
1083
  }
998
1084
  }
999
1085
 
package/src/result.ts CHANGED
@@ -57,6 +57,13 @@ export abstract class Result<T, E = Error> {
57
57
  return false;
58
58
  }
59
59
 
60
+ static AsyncOk<T = void, E = Error>(...args: T[]): Promise<Result<T, E>> {
61
+ return Promise.resolve(Result.Ok<T, E>(...args));
62
+ }
63
+ static AsyncErr<T, E extends Error = Error>(t: E | string | Result<unknown, E>): Promise<Result<T, E>> {
64
+ return Promise.resolve(Result.Err<T, E>(t));
65
+ }
66
+
60
67
  isOk(): boolean {
61
68
  return this.is_ok();
62
69
  }