@fncts/io 0.0.30 → 0.0.32

Sign up to get free protection for your applications and to get access to all the features.
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
- }> {}