@fncts/io 0.0.30 → 0.0.32

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 (149) hide show
  1. package/Channel/api/runScoped.d.ts +1 -0
  2. package/Channel/api.d.ts +1 -0
  3. package/Channel/internal/ChannelExecutor.d.ts +2 -2
  4. package/Fiber/FiberMessage.d.ts +5 -5
  5. package/IO/api/blocking.d.ts +7 -0
  6. package/IO/api/provideSomeLayer.d.ts +1 -2
  7. package/IO/api/zipConcurrent.d.ts +2 -1
  8. package/IO/runtime.d.ts +4 -2
  9. package/IO.d.ts +1 -0
  10. package/Ref/Synchronized/definition.d.ts +3 -3
  11. package/Semaphore.d.ts +30 -0
  12. package/Stream/api.d.ts +24 -4
  13. package/SubscriptionRef.d.ts +3 -3
  14. package/_cjs/Channel/api/mapOutConcurrentIO.cjs +14 -16
  15. package/_cjs/Channel/api/mapOutConcurrentIO.cjs.map +1 -1
  16. package/_cjs/Channel/api/mergeAllWith.cjs +30 -32
  17. package/_cjs/Channel/api/mergeAllWith.cjs.map +1 -1
  18. package/_cjs/Channel/api/runScoped.cjs +19 -3
  19. package/_cjs/Channel/api/runScoped.cjs.map +1 -1
  20. package/_cjs/Channel/api/toPull.cjs +1 -1
  21. package/_cjs/Channel/api/toPull.cjs.map +1 -1
  22. package/_cjs/Channel/api.cjs +3 -2
  23. package/_cjs/Channel/api.cjs.map +1 -1
  24. package/_cjs/Channel/internal/ChannelExecutor.cjs +61 -52
  25. package/_cjs/Channel/internal/ChannelExecutor.cjs.map +1 -1
  26. package/_cjs/Fiber/FiberMessage.cjs +12 -11
  27. package/_cjs/Fiber/FiberMessage.cjs.map +1 -1
  28. package/_cjs/Fiber/FiberRuntime.cjs +28 -27
  29. package/_cjs/Fiber/FiberRuntime.cjs.map +1 -1
  30. package/_cjs/FiberScope/definition.cjs +2 -2
  31. package/_cjs/FiberScope/definition.cjs.map +1 -1
  32. package/_cjs/Hub/api.cjs.map +1 -1
  33. package/_cjs/{RuntimeConfig.cjs → IO/api/blocking.cjs} +13 -5
  34. package/_cjs/IO/api/blocking.cjs.map +1 -0
  35. package/_cjs/IO/api/provideSomeLayer.cjs.map +1 -1
  36. package/_cjs/IO/api/raceWith.cjs +9 -12
  37. package/_cjs/IO/api/raceWith.cjs.map +1 -1
  38. package/_cjs/IO/api/stateful.cjs +3 -3
  39. package/_cjs/IO/api/stateful.cjs.map +1 -1
  40. package/_cjs/IO/api/withChildren.cjs +5 -4
  41. package/_cjs/IO/api/withChildren.cjs.map +1 -1
  42. package/_cjs/IO/api/zipConcurrent.cjs +23 -23
  43. package/_cjs/IO/api/zipConcurrent.cjs.map +1 -1
  44. package/_cjs/IO/runtime.cjs +8 -8
  45. package/_cjs/IO/runtime.cjs.map +1 -1
  46. package/_cjs/IO.cjs +11 -0
  47. package/_cjs/IO.cjs.map +1 -1
  48. package/_cjs/Queue/api/filterInputIO.cjs.map +1 -1
  49. package/_cjs/Queue/api/filterOutputIO.cjs.map +1 -1
  50. package/_cjs/Ref/Synchronized/constructors.cjs +4 -5
  51. package/_cjs/Ref/Synchronized/constructors.cjs.map +1 -1
  52. package/_cjs/Ref/Synchronized/definition.cjs +1 -2
  53. package/_cjs/Ref/Synchronized/definition.cjs.map +1 -1
  54. package/_cjs/Semaphore.cjs +90 -0
  55. package/_cjs/Semaphore.cjs.map +1 -0
  56. package/_cjs/Stream/api.cjs +101 -61
  57. package/_cjs/Stream/api.cjs.map +1 -1
  58. package/_cjs/SubscriptionRef.cjs +2 -3
  59. package/_cjs/SubscriptionRef.cjs.map +1 -1
  60. package/_cjs/internal/BackgroundScheduler.cjs +199 -0
  61. package/_cjs/internal/BackgroundScheduler.cjs.map +1 -0
  62. package/_mjs/Cached/definition.mjs.map +1 -1
  63. package/_mjs/Channel/api/mapOutConcurrentIO.mjs +14 -16
  64. package/_mjs/Channel/api/mapOutConcurrentIO.mjs.map +1 -1
  65. package/_mjs/Channel/api/mergeAllWith.mjs +30 -32
  66. package/_mjs/Channel/api/mergeAllWith.mjs.map +1 -1
  67. package/_mjs/Channel/api/runScoped.mjs +19 -3
  68. package/_mjs/Channel/api/runScoped.mjs.map +1 -1
  69. package/_mjs/Channel/api/toPull.mjs +1 -1
  70. package/_mjs/Channel/api/toPull.mjs.map +1 -1
  71. package/_mjs/Channel/api.mjs +3 -2
  72. package/_mjs/Channel/api.mjs.map +1 -1
  73. package/_mjs/Channel/internal/ChannelExecutor.mjs +61 -52
  74. package/_mjs/Channel/internal/ChannelExecutor.mjs.map +1 -1
  75. package/_mjs/Fiber/FiberMessage.mjs +5 -5
  76. package/_mjs/Fiber/FiberMessage.mjs.map +1 -1
  77. package/_mjs/Fiber/FiberRuntime.mjs +28 -27
  78. package/_mjs/Fiber/FiberRuntime.mjs.map +1 -1
  79. package/_mjs/FiberScope/definition.mjs +2 -2
  80. package/_mjs/FiberScope/definition.mjs.map +1 -1
  81. package/_mjs/Hub/api.mjs.map +1 -1
  82. package/_mjs/IO/api/blocking.mjs +12 -0
  83. package/_mjs/IO/api/blocking.mjs.map +1 -0
  84. package/_mjs/IO/api/provideSomeLayer.mjs.map +1 -1
  85. package/_mjs/IO/api/raceWith.mjs +9 -12
  86. package/_mjs/IO/api/raceWith.mjs.map +1 -1
  87. package/_mjs/IO/api/stateful.mjs +3 -3
  88. package/_mjs/IO/api/stateful.mjs.map +1 -1
  89. package/_mjs/IO/api/withChildren.mjs +5 -4
  90. package/_mjs/IO/api/withChildren.mjs.map +1 -1
  91. package/_mjs/IO/api/zipConcurrent.mjs +23 -23
  92. package/_mjs/IO/api/zipConcurrent.mjs.map +1 -1
  93. package/_mjs/IO/runtime.mjs +6 -6
  94. package/_mjs/IO/runtime.mjs.map +1 -1
  95. package/_mjs/IO.mjs +1 -0
  96. package/_mjs/IO.mjs.map +1 -1
  97. package/_mjs/Queue/api/filterInputIO.mjs.map +1 -1
  98. package/_mjs/Queue/api/filterOutputIO.mjs.map +1 -1
  99. package/_mjs/Ref/Synchronized/constructors.mjs +4 -5
  100. package/_mjs/Ref/Synchronized/constructors.mjs.map +1 -1
  101. package/_mjs/Ref/Synchronized/definition.mjs +1 -2
  102. package/_mjs/Ref/Synchronized/definition.mjs.map +1 -1
  103. package/_mjs/Semaphore.mjs +78 -0
  104. package/_mjs/Semaphore.mjs.map +1 -0
  105. package/_mjs/Stream/api.mjs +93 -57
  106. package/_mjs/Stream/api.mjs.map +1 -1
  107. package/_mjs/SubscriptionRef.mjs +2 -3
  108. package/_mjs/SubscriptionRef.mjs.map +1 -1
  109. package/_mjs/internal/BackgroundScheduler.mjs +191 -0
  110. package/_mjs/internal/BackgroundScheduler.mjs.map +1 -0
  111. package/_src/Cached/definition.ts +1 -1
  112. package/_src/Channel/api/mapOutConcurrentIO.ts +1 -1
  113. package/_src/Channel/api/mergeAllWith.ts +1 -1
  114. package/_src/Channel/api/runScoped.ts +30 -5
  115. package/_src/Channel/api/toPull.ts +1 -1
  116. package/_src/Channel/api.ts +1 -0
  117. package/_src/Channel/internal/ChannelExecutor.ts +24 -20
  118. package/_src/Fiber/FiberMessage.ts +5 -5
  119. package/_src/Fiber/FiberRuntime.ts +1 -1
  120. package/_src/FiberRefs/api.ts +1 -1
  121. package/_src/Hub/api.ts +1 -1
  122. package/_src/IO/api/blocking.ts +9 -0
  123. package/_src/IO/api/provideSomeLayer.ts +1 -3
  124. package/_src/IO/api/raceWith.ts +1 -3
  125. package/_src/IO/api/stateful.ts +1 -1
  126. package/_src/IO/api/zipConcurrent.ts +26 -30
  127. package/_src/IO/runtime.ts +4 -4
  128. package/_src/IO.ts +1 -0
  129. package/_src/Queue/api/filterInputIO.ts +1 -1
  130. package/_src/Queue/api/filterOutputIO.ts +1 -1
  131. package/_src/Ref/Synchronized/constructors.ts +2 -2
  132. package/_src/Ref/Synchronized/definition.ts +1 -1
  133. package/_src/Semaphore.ts +81 -0
  134. package/_src/State/definition.ts +1 -1
  135. package/_src/Stream/api.ts +58 -7
  136. package/_src/Subject/Atomic.ts +1 -1
  137. package/_src/SubscriptionRef.ts +2 -2
  138. package/_src/global.ts +4 -4
  139. package/_src/index.ts +2 -2
  140. package/_src/internal/BackgroundScheduler.ts +276 -0
  141. package/global.d.ts +4 -4
  142. package/index.d.ts +2 -2
  143. package/internal/BackgroundScheduler.d.ts +47 -0
  144. package/package.json +3 -3
  145. package/RuntimeConfig.d.ts +0 -11
  146. package/_cjs/RuntimeConfig.cjs.map +0 -1
  147. package/_mjs/RuntimeConfig.mjs +0 -3
  148. package/_mjs/RuntimeConfig.mjs.map +0 -1
  149. package/_src/RuntimeConfig.ts +0 -6
@@ -812,9 +812,10 @@ function combineChunksProducer<Err, Elem>(
812
812
  ): Channel<never, Err, Conc<Elem>, unknown, never, never, any> {
813
813
  return Channel.fromIO(latch.take).zipRight(
814
814
  Channel.readWithCause(
815
- (chunk) => Channel.fromIO(handoff.offer(Take.chunk(chunk))).zipRight(combineChunksProducer(handoff, latch)),
815
+ (chunk: Conc<Elem>) =>
816
+ Channel.fromIO(handoff.offer(Take.chunk(chunk))).zipRight(combineChunksProducer(handoff, latch)),
816
817
  (cause) => Channel.fromIO(handoff.offer(Take.failCause(cause))),
817
- () => Channel.fromIO(handoff.offer(Take.end)).zipRight(combineChunksProducer(handoff, latch)),
818
+ () => Channel.fromIO(handoff.offer(Take.end)),
818
819
  ),
819
820
  );
820
821
  }
@@ -991,6 +992,13 @@ export function debounce(duration: Lazy<Duration>, __tsplusTrace?: string) {
991
992
  };
992
993
  }
993
994
 
995
+ /**
996
+ * @tsplus static fncts.io.StreamOps defer
997
+ */
998
+ export function defer<R, E, A>(self: Lazy<Stream<R, E, A>>): Stream<R, E, A> {
999
+ return new Stream(Channel.defer(self().channel));
1000
+ }
1001
+
994
1002
  function defaultIfEmptyWriter<R, E, A, R1, E1, B>(
995
1003
  fb: Stream<R1, E1, B>,
996
1004
  __tsplusTrace?: string,
@@ -1092,7 +1100,7 @@ export function distributedWithDynamic<E, A>(
1092
1100
  );
1093
1101
  const add = Δ(
1094
1102
  Do((Δ) => {
1095
- const queuesLock = Δ(TSemaphore.make(1).commit);
1103
+ const queuesLock = Δ(Semaphore(1));
1096
1104
  const newQueue = Δ(
1097
1105
  Ref.make<UIO<readonly [symbol, Queue<Exit<Maybe<E>, A>>]>>(
1098
1106
  Do((Δ) => {
@@ -2189,14 +2197,27 @@ function mapIOLoop<R, E, A, R1, E1, B>(
2189
2197
  *
2190
2198
  * @note This combinator destroys the chunking structure. It's recommended to use chunkN afterwards.
2191
2199
  *
2192
- * @tsplus pipeable fncts.io.Stream mapIOC
2200
+ * @tsplus pipeable fncts.io.Stream mapIOConcurrently
2193
2201
  */
2194
- export function mapIOC<A, R1, E1, B>(n: number, f: (a: A) => IO<R1, E1, B>, __tsplusTrace?: string) {
2202
+ export function mapIOConcurrently<A, R1, E1, B>(n: number, f: (a: A) => IO<R1, E1, B>, __tsplusTrace?: string) {
2195
2203
  return <R, E>(stream: Stream<R, E, A>): Stream<R | R1, E | E1, B> => {
2196
2204
  return new Stream(stream.channel.concatMap(Channel.writeChunk).mapOutConcurrentIO(n, f).mapOut(Conc.single));
2197
2205
  };
2198
2206
  }
2199
2207
 
2208
+ /**
2209
+ * @tsplus pipeable fncts.io.Stream mapIOConcurrentlyUnordered
2210
+ */
2211
+ export function mapIOConcurrentlyUnordered<A, R1, E1, B>(n: number, f: (a: A) => IO<R1, E1, B>) {
2212
+ return <R, E>(self: Stream<R, E, A>): Stream<R | R1, E | E1, B> => {
2213
+ return self.pipeThroughChannelOrFail(
2214
+ Channel.id<never, Conc<A>, any>()
2215
+ .concatMap((chunk) => Channel.writeChunk(chunk))
2216
+ .mergeMap((inp) => Stream.fromIO(f(inp)).channel, n, 16),
2217
+ );
2218
+ };
2219
+ }
2220
+
2200
2221
  /**
2201
2222
  * Maps each element of this stream to another stream and returns the
2202
2223
  * non-deterministic merge of those streams, executing up to `n` inner streams
@@ -2243,7 +2264,7 @@ export function mergeEither<R1, E1, B>(fb: Stream<R1, E1, B>, __tsplusTrace?: st
2243
2264
  };
2244
2265
  }
2245
2266
 
2246
- export function mergeWithHandler<R, E>(
2267
+ function mergeWithHandler<R, E>(
2247
2268
  terminate: boolean,
2248
2269
  __tsplusTrace?: string,
2249
2270
  ): (exit: Exit<E, unknown>) => MergeDecision<R, E, unknown, E, unknown> {
@@ -2366,6 +2387,15 @@ export function pipeThrough<A, R1, E1, L, Z>(sa: Sink<R1, E1, A, L, Z>, __tsplus
2366
2387
  };
2367
2388
  }
2368
2389
 
2390
+ /**
2391
+ * @tsplus pipeable fncts.io.Stream pipeThroughChannelOrFail
2392
+ */
2393
+ export function pipeThroughChannelOrFail<A, R1, E1, B>(channel: Channel<R1, never, Conc<A>, any, E1, Conc<B>, any>) {
2394
+ return <R, E>(self: Stream<R, E, A>): Stream<R | R1, E | E1, B> => {
2395
+ return new Stream(self.channel >>> channel);
2396
+ };
2397
+ }
2398
+
2369
2399
  /**
2370
2400
  * Provides the stream with its required environment, which eliminates
2371
2401
  * its dependency on `R`.
@@ -2603,6 +2633,27 @@ export function runIntoQueueScoped<RA, RB, EA, EB, E1, A, B>(
2603
2633
  };
2604
2634
  }
2605
2635
 
2636
+ /**
2637
+ * Like runIntoQueue, but provides the result as a scoped IO
2638
+ * to allow for scope composition.
2639
+ *
2640
+ * @tsplus pipeable fncts.io.Stream runIntoQueueElementsScoped
2641
+ */
2642
+ export function runIntoQueueElementsScoped<E, A>(queue: Lazy<Queue.Enqueue<Exit<Maybe<E>, A>>>) {
2643
+ return <R>(self: Stream<R, E, A>): IO<R | Scope, never, void> => {
2644
+ return IO.defer(() => {
2645
+ const queue0 = queue();
2646
+ const writer: Channel<R, E, Conc<A>, any, never, Exit<Maybe<E>, A>, any> = Channel.readWithCause(
2647
+ (inp: Conc<A>) => Channel.fromIO(queue0.offerAll(inp.map((a) => Exit.succeed(a)))) > writer,
2648
+ (cause) => Channel.fromIO(queue0.offer(Exit.failCause(cause.map((e) => Just(e))))),
2649
+ () => Channel.fromIO(queue0.offer(Exit.fail(Nothing()))),
2650
+ );
2651
+
2652
+ return (self.channel >>> writer).drain.runScoped.asUnit;
2653
+ });
2654
+ };
2655
+ }
2656
+
2606
2657
  /**
2607
2658
  * Like `Stream#runIntoHub`, but provides the result as a `Managed` to allow for scope
2608
2659
  * composition.
@@ -2947,7 +2998,7 @@ export function toQueueOfElements(capacity = 2, __tsplusTrace?: string) {
2947
2998
  return <R, E, A>(stream: Stream<R, E, A>): IO<R | Scope, never, Queue.Dequeue<Exit<Maybe<E>, A>>> => {
2948
2999
  return Do((Δ) => {
2949
3000
  const queue = Δ(IO.acquireRelease(Queue.makeBounded<Exit<Maybe<E>, A>>(capacity), (_) => _.shutdown));
2950
- Δ(stream.runIntoElementsScoped(queue).fork);
3001
+ Δ(stream.runIntoQueueElementsScoped(queue).forkScoped);
2951
3002
  return queue;
2952
3003
  });
2953
3004
  };
@@ -1,4 +1,4 @@
1
- import type { Emitter} from "@fncts/io/Push/definition";
1
+ import type { Emitter } from "@fncts/io/Push/definition";
2
2
  import type { Subject } from "@fncts/io/Subject/definition";
3
3
 
4
4
  import { Multicast, Push } from "@fncts/io/Push";
@@ -6,7 +6,7 @@ export type SubscriptionRefTypeId = typeof SubscriptionRefTypeId;
6
6
 
7
7
  export class SubscriptionRefInternal<A> extends PSynchronizedInternal<never, never, never, never, A, A> {
8
8
  readonly [SubscriptionRefTypeId]: SubscriptionRefTypeId = SubscriptionRefTypeId;
9
- constructor(readonly semaphore: TSemaphore, readonly hub: Hub<A>, readonly ref: Ref<A>) {
9
+ constructor(readonly semaphore: Semaphore, readonly hub: Hub<A>, readonly ref: Ref<A>) {
10
10
  super(semaphore, ref.get, (a) => ref.set(a));
11
11
  }
12
12
  changes: Stream<never, never, A> = Stream.unwrapScoped(
@@ -50,7 +50,7 @@ export function concrete<A>(_: SubscriptionRef<A>): asserts _ is SubscriptionRef
50
50
  */
51
51
  export function make<A>(value: Lazy<A>): UIO<SubscriptionRef<A>> {
52
52
  return Do((Δ) => {
53
- const semaphore = Δ(TSemaphore.make(1).commit);
53
+ const semaphore = Δ(Semaphore(1));
54
54
  const hub = Δ(Hub.makeUnbounded<A>());
55
55
  const ref = Δ(Ref.make(value));
56
56
  return new SubscriptionRefInternal(semaphore, hub, ref);
package/_src/global.ts CHANGED
@@ -99,10 +99,6 @@ import { Random } from "@fncts/io/Random/definition";
99
99
  * @tsplus global
100
100
  */
101
101
  import { PRef, Ref } from "@fncts/io/Ref/definition";
102
- /**
103
- * @tsplus global
104
- */
105
- import { RuntimeConfig } from "@fncts/io/RuntimeConfig";
106
102
  /**
107
103
  * @tsplus global
108
104
  */
@@ -119,6 +115,10 @@ import { Finalizer } from "@fncts/io/Scope/Finalizer";
119
115
  * @tsplus global
120
116
  */
121
117
  import { ScopedRef } from "@fncts/io/ScopedRef/definition";
118
+ /**
119
+ * @tsplus global
120
+ */
121
+ import { Semaphore } from "@fncts/io/Semaphore";
122
122
  /**
123
123
  * @tsplus global
124
124
  */
package/_src/index.ts CHANGED
@@ -5,7 +5,6 @@ export type {} from "./Channel.js";
5
5
  export type {} from "./Clock.js";
6
6
  export type {} from "./Console.js";
7
7
  export type {} from "./CountdownLatch.js";
8
- export type {} from "./demo.js";
9
8
  export type {} from "./Fiber.js";
10
9
  export type {} from "./FiberDescriptor.js";
11
10
  export type {} from "./FiberRef.js";
@@ -22,18 +21,19 @@ export type {} from "./Layer.js";
22
21
  export type {} from "./Logger.js";
23
22
  export type {} from "./LogLevel.js";
24
23
  export type {} from "./LogSpan.js";
24
+ export type {} from "./MVar.js";
25
25
  export type {} from "./Push.js";
26
26
  export type {} from "./Queue.js";
27
27
  export type {} from "./Random.js";
28
28
  export type {} from "./Ref.js";
29
29
  export type {} from "./RefSubject.js";
30
30
  export type {} from "./Reloadable.js";
31
- export type {} from "./RuntimeConfig.js";
32
31
  export type {} from "./RuntimeFlag.js";
33
32
  export type {} from "./RuntimeFlags.js";
34
33
  export type {} from "./Schedule.js";
35
34
  export type {} from "./Scope.js";
36
35
  export type {} from "./ScopedRef.js";
36
+ export type {} from "./Semaphore.js";
37
37
  export type {} from "./Sink.js";
38
38
  export type {} from "./State.js";
39
39
  export type {} from "./STM.js";
@@ -0,0 +1,276 @@
1
+ import type { Scheduler } from "@fncts/io/internal/Scheduler";
2
+
3
+ interface IdleDeadline {
4
+ timeRemaining(): number;
5
+ readonly didTimeout: boolean;
6
+ }
7
+
8
+ declare const requestIdleCallback: ((callback: (deadline: IdleDeadline) => void) => number) | undefined;
9
+ declare const cancelIdleCallback: ((handle: number | undefined) => void) | undefined;
10
+ declare const requestAnimationFrame: ((callback: (time: number) => void) => void) | undefined;
11
+ interface Navigator {
12
+ scheduling:
13
+ | {
14
+ isInputPending: (() => boolean) | undefined;
15
+ }
16
+ | undefined;
17
+ }
18
+ declare const navigator: Navigator;
19
+
20
+ interface WhenReady<T> {
21
+ promise: () => Promise<T>;
22
+ resolve: (value: T) => void;
23
+ }
24
+
25
+ function whenReady<T>(): WhenReady<T> {
26
+ const observers: Array<(value: T) => void> = [];
27
+
28
+ const promise = () => new Promise<T>((resolve) => observers.push(resolve));
29
+
30
+ return {
31
+ promise,
32
+ resolve: (value) => observers.forEach((observer) => observer(value)),
33
+ };
34
+ }
35
+
36
+ interface Task {
37
+ ready: () => Promise<void>;
38
+ resolve: () => void;
39
+ }
40
+
41
+ interface State {
42
+ tasks: Array<Task>;
43
+ frameTimeElapsed: boolean;
44
+ onIdleCallback: WhenReady<void>;
45
+ onAnimationFrame: WhenReady<void>;
46
+ frameWorkStartTime: number | undefined;
47
+ idleDeadline: IdleDeadline | undefined;
48
+ }
49
+
50
+ export class BackgroundScheduler implements Scheduler {
51
+ state: State = {
52
+ tasks: [],
53
+ idleDeadline: undefined,
54
+ frameTimeElapsed: false,
55
+ onIdleCallback: whenReady(),
56
+ onAnimationFrame: whenReady(),
57
+ frameWorkStartTime: undefined,
58
+ };
59
+
60
+ isTracking = false;
61
+ idleCallbackId: number | undefined;
62
+ lastCallTime = 0;
63
+ lastResult = false;
64
+ globalId = 0;
65
+ running = new Set<number>();
66
+ callbacks: Array<() => void> = [];
67
+ promiseEscapeId: number | undefined;
68
+
69
+ scheduleTask(task: () => void) {
70
+ this.yieldBackgroundOrContinue().then(() => {
71
+ task();
72
+ });
73
+ }
74
+
75
+ createTask(): Task {
76
+ const wr = whenReady<void>();
77
+ const item = { ready: wr.promise, resolve: wr.resolve };
78
+ this.state.tasks.push(item);
79
+ if (this.state.tasks.length === 1) {
80
+ this.startTracking();
81
+ }
82
+ return item;
83
+ }
84
+
85
+ startTracking(): void {
86
+ if (this.isTracking) {
87
+ return;
88
+ }
89
+
90
+ this.isTracking = true;
91
+
92
+ const reset = () => {
93
+ this.state.idleDeadline = undefined;
94
+ this.state.frameTimeElapsed = false;
95
+ this.state.frameWorkStartTime = undefined;
96
+ };
97
+
98
+ const loop = () => {
99
+ if (typeof requestIdleCallback !== "undefined") {
100
+ this.idleCallbackId = requestIdleCallback((deadline) => {
101
+ reset();
102
+ this.state.idleDeadline = deadline;
103
+ this.state.onIdleCallback.resolve();
104
+ this.state.onIdleCallback = whenReady();
105
+ });
106
+ }
107
+
108
+ const cb = () => {
109
+ reset();
110
+ this.state.onAnimationFrame.resolve();
111
+ this.state.onAnimationFrame = whenReady();
112
+ if (this.state.tasks.length === 0) {
113
+ this.isTracking = false;
114
+ if (typeof cancelIdleCallback !== "undefined") {
115
+ cancelIdleCallback(this.idleCallbackId);
116
+ }
117
+ } else {
118
+ loop();
119
+ }
120
+ };
121
+
122
+ if (typeof requestAnimationFrame !== "undefined") {
123
+ requestAnimationFrame(cb);
124
+ } else {
125
+ setTimeout(cb, 0);
126
+ }
127
+ };
128
+
129
+ loop();
130
+ }
131
+
132
+ removeTask(task: Task) {
133
+ const index = this.state.tasks.indexOf(task);
134
+ if (index !== -1) {
135
+ this.state.tasks.splice(index, 1);
136
+ }
137
+ }
138
+
139
+ nextTask() {
140
+ if (this.state.tasks.length > 0) {
141
+ this.state.tasks[0]!.resolve();
142
+ }
143
+ }
144
+
145
+ isTimeToYield(): boolean {
146
+ const now = Date.now();
147
+
148
+ if (!this.lastResult && now - this.lastCallTime === 0) {
149
+ return this.lastResult;
150
+ }
151
+
152
+ this.lastCallTime = now;
153
+ this.lastResult =
154
+ now >= this.calculateDeadline() ||
155
+ (typeof navigator !== "undefined" && navigator.scheduling?.isInputPending?.() === true);
156
+
157
+ if (this.lastResult) {
158
+ this.state.frameTimeElapsed = true;
159
+ }
160
+
161
+ return this.lastResult;
162
+ }
163
+
164
+ calculateDeadline(): number {
165
+ if (this.state.frameWorkStartTime === undefined) {
166
+ return -1;
167
+ }
168
+
169
+ const idleDeadline =
170
+ this.state.idleDeadline === undefined
171
+ ? Number.MAX_SAFE_INTEGER
172
+ : Date.now() + this.state.idleDeadline.timeRemaining();
173
+
174
+ return Math.min(this.state.frameWorkStartTime + 5, idleDeadline);
175
+ }
176
+
177
+ requestPromiseEscape(callback: () => void): number {
178
+ const id = this.globalId;
179
+
180
+ this.running.add(id);
181
+
182
+ Promise.resolve().then(() => {
183
+ Promise.resolve().then(() => {
184
+ if (this.running.has(id)) {
185
+ callback();
186
+ this.running.delete(id);
187
+ }
188
+ });
189
+ });
190
+
191
+ this.globalId += 1;
192
+
193
+ return id;
194
+ }
195
+
196
+ cancelPromiseEscape(id: number | undefined): void {
197
+ if (id !== undefined) {
198
+ this.running.delete(id);
199
+ }
200
+ }
201
+
202
+ requestNextTask(callback: () => void): void {
203
+ if (this.callbacks.length === 0) {
204
+ const channel = new MessageChannel();
205
+ channel.port2.postMessage(undefined);
206
+ // @ts-expect-error
207
+ channel.port1.onmessage = (): void => {
208
+ channel.port1.close();
209
+ channel.port2.close();
210
+
211
+ const callbacksCopy = this.callbacks;
212
+ this.callbacks = [];
213
+ for (const callback of callbacksCopy) {
214
+ callback();
215
+ }
216
+ };
217
+ }
218
+
219
+ this.callbacks.push(callback);
220
+ }
221
+
222
+ async yieldControl(): Promise<void> {
223
+ this.cancelPromiseEscape(this.promiseEscapeId);
224
+
225
+ const task = this.createTask();
226
+
227
+ await this.schedule();
228
+
229
+ if (this.state.tasks[0] !== task) {
230
+ await task.ready();
231
+ if (this.isTimeToYield()) {
232
+ await this.schedule();
233
+ }
234
+ }
235
+
236
+ this.removeTask(task);
237
+
238
+ this.cancelPromiseEscape(this.promiseEscapeId);
239
+
240
+ this.promiseEscapeId = this.requestPromiseEscape(() => {
241
+ this.nextTask();
242
+ });
243
+ }
244
+
245
+ async schedule(): Promise<void> {
246
+ if (this.state.frameTimeElapsed) {
247
+ await this.state.onAnimationFrame.promise();
248
+ }
249
+
250
+ if (typeof requestIdleCallback === "undefined") {
251
+ await new Promise<void>((resolve) => this.requestNextTask(resolve));
252
+
253
+ if (typeof navigator !== "undefined" && navigator.scheduling?.isInputPending?.() === true) {
254
+ await this.schedule();
255
+ } else if (this.state.frameWorkStartTime === undefined) {
256
+ this.state.frameWorkStartTime = Date.now();
257
+ }
258
+ } else {
259
+ await this.state.onIdleCallback.promise();
260
+
261
+ if (this.state.frameWorkStartTime === undefined) {
262
+ this.state.frameWorkStartTime = Date.now();
263
+ }
264
+ }
265
+ }
266
+
267
+ yieldBackgroundOrContinue(): Promise<void> {
268
+ if (this.isTimeToYield()) {
269
+ return this.yieldControl();
270
+ }
271
+
272
+ return Promise.resolve();
273
+ }
274
+ }
275
+
276
+ export const backgroundScheduler = new BackgroundScheduler();
package/global.d.ts CHANGED
@@ -98,10 +98,6 @@ import { Random } from "@fncts/io/Random/definition";
98
98
  * @tsplus global
99
99
  */
100
100
  import { PRef, Ref } from "@fncts/io/Ref/definition";
101
- /**
102
- * @tsplus global
103
- */
104
- import { RuntimeConfig } from "@fncts/io/RuntimeConfig";
105
101
  /**
106
102
  * @tsplus global
107
103
  */
@@ -118,6 +114,10 @@ import { Finalizer } from "@fncts/io/Scope/Finalizer";
118
114
  * @tsplus global
119
115
  */
120
116
  import { ScopedRef } from "@fncts/io/ScopedRef/definition";
117
+ /**
118
+ * @tsplus global
119
+ */
120
+ import { Semaphore } from "@fncts/io/Semaphore";
121
121
  /**
122
122
  * @tsplus global
123
123
  */
package/index.d.ts CHANGED
@@ -4,7 +4,6 @@ export type {} from "./Channel.js";
4
4
  export type {} from "./Clock.js";
5
5
  export type {} from "./Console.js";
6
6
  export type {} from "./CountdownLatch.js";
7
- export type {} from "./demo.js";
8
7
  export type {} from "./Fiber.js";
9
8
  export type {} from "./FiberDescriptor.js";
10
9
  export type {} from "./FiberRef.js";
@@ -21,18 +20,19 @@ export type {} from "./Layer.js";
21
20
  export type {} from "./Logger.js";
22
21
  export type {} from "./LogLevel.js";
23
22
  export type {} from "./LogSpan.js";
23
+ export type {} from "./MVar.js";
24
24
  export type {} from "./Push.js";
25
25
  export type {} from "./Queue.js";
26
26
  export type {} from "./Random.js";
27
27
  export type {} from "./Ref.js";
28
28
  export type {} from "./RefSubject.js";
29
29
  export type {} from "./Reloadable.js";
30
- export type {} from "./RuntimeConfig.js";
31
30
  export type {} from "./RuntimeFlag.js";
32
31
  export type {} from "./RuntimeFlags.js";
33
32
  export type {} from "./Schedule.js";
34
33
  export type {} from "./Scope.js";
35
34
  export type {} from "./ScopedRef.js";
35
+ export type {} from "./Semaphore.js";
36
36
  export type {} from "./Sink.js";
37
37
  export type {} from "./State.js";
38
38
  export type {} from "./STM.js";
@@ -0,0 +1,47 @@
1
+ import type { Scheduler } from "@fncts/io/internal/Scheduler";
2
+ interface IdleDeadline {
3
+ timeRemaining(): number;
4
+ readonly didTimeout: boolean;
5
+ }
6
+ interface WhenReady<T> {
7
+ promise: () => Promise<T>;
8
+ resolve: (value: T) => void;
9
+ }
10
+ interface Task {
11
+ ready: () => Promise<void>;
12
+ resolve: () => void;
13
+ }
14
+ interface State {
15
+ tasks: Array<Task>;
16
+ frameTimeElapsed: boolean;
17
+ onIdleCallback: WhenReady<void>;
18
+ onAnimationFrame: WhenReady<void>;
19
+ frameWorkStartTime: number | undefined;
20
+ idleDeadline: IdleDeadline | undefined;
21
+ }
22
+ export declare class BackgroundScheduler implements Scheduler {
23
+ state: State;
24
+ isTracking: boolean;
25
+ idleCallbackId: number | undefined;
26
+ lastCallTime: number;
27
+ lastResult: boolean;
28
+ globalId: number;
29
+ running: Set<number>;
30
+ callbacks: Array<() => void>;
31
+ promiseEscapeId: number | undefined;
32
+ scheduleTask(task: () => void): void;
33
+ createTask(): Task;
34
+ startTracking(): void;
35
+ removeTask(task: Task): void;
36
+ nextTask(): void;
37
+ isTimeToYield(): boolean;
38
+ calculateDeadline(): number;
39
+ requestPromiseEscape(callback: () => void): number;
40
+ cancelPromiseEscape(id: number | undefined): void;
41
+ requestNextTask(callback: () => void): void;
42
+ yieldControl(): Promise<void>;
43
+ schedule(): Promise<void>;
44
+ yieldBackgroundOrContinue(): Promise<void>;
45
+ }
46
+ export declare const backgroundScheduler: BackgroundScheduler;
47
+ export {};
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@fncts/io",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "dependencies": {
5
- "@fncts/base": "0.0.25",
6
- "@fncts/transformers": "0.0.4",
5
+ "@fncts/base": "0.0.27",
6
+ "@fncts/transformers": "0.0.5",
7
7
  "@fncts/typelevel": "0.0.15"
8
8
  },
9
9
  "exports": {
@@ -1,11 +0,0 @@
1
- import { CaseClass } from "@fncts/base/data/CaseClass";
2
- import { Cause } from "@fncts/base/data/Cause/definition";
3
- import { Supervisor } from "@fncts/io/Supervisor/definition";
4
- import { Logger } from "@fncts/io/Logger/definition";
5
- export declare class RuntimeConfig extends CaseClass<{
6
- readonly reportFailure: (e: Cause<unknown>) => void;
7
- readonly supervisor: Supervisor<any>;
8
- readonly yieldOpCount: number;
9
- readonly logger: Logger<string, any>;
10
- }> {
11
- }
@@ -1 +0,0 @@
1
- {"version":3,"file":"RuntimeConfig.cjs","mappings":";;;;;;;;;AAAM,MAAOA,aAAc,SAAQC,yBAKjC;AAAG","names":["RuntimeConfig","tsplus_module_1"],"sourceRoot":"","sources":["../_src/RuntimeConfig.ts"],"sourcesContent":[null]}
@@ -1,3 +0,0 @@
1
- import * as tsplus_module_1 from "@fncts/base/data/CaseClass";
2
- export class RuntimeConfig extends tsplus_module_1.CaseClass {}
3
- //# sourceMappingURL=RuntimeConfig.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"RuntimeConfig.mjs","mappings":";AAAA,OAAM,MAAOA,aAAc,SAAQC,yBAKjC","names":["RuntimeConfig","tsplus_module_1"],"sourceRoot":"","sources":["../_src/RuntimeConfig.ts"],"sourcesContent":[null]}
@@ -1,6 +0,0 @@
1
- export class RuntimeConfig extends CaseClass<{
2
- readonly reportFailure: (e: Cause<unknown>) => void;
3
- readonly supervisor: Supervisor<any>;
4
- readonly yieldOpCount: number;
5
- readonly logger: Logger<string, any>;
6
- }> {}