@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.
- package/Channel/api/runScoped.d.ts +1 -0
- package/Channel/api.d.ts +1 -0
- package/Channel/internal/ChannelExecutor.d.ts +2 -2
- package/Fiber/FiberMessage.d.ts +5 -5
- package/IO/api/blocking.d.ts +7 -0
- package/IO/api/provideSomeLayer.d.ts +1 -2
- package/IO/api/zipConcurrent.d.ts +2 -1
- package/IO/runtime.d.ts +4 -2
- package/IO.d.ts +1 -0
- package/Ref/Synchronized/definition.d.ts +3 -3
- package/Semaphore.d.ts +30 -0
- package/Stream/api.d.ts +24 -4
- package/SubscriptionRef.d.ts +3 -3
- package/_cjs/Channel/api/mapOutConcurrentIO.cjs +14 -16
- package/_cjs/Channel/api/mapOutConcurrentIO.cjs.map +1 -1
- package/_cjs/Channel/api/mergeAllWith.cjs +30 -32
- package/_cjs/Channel/api/mergeAllWith.cjs.map +1 -1
- package/_cjs/Channel/api/runScoped.cjs +19 -3
- package/_cjs/Channel/api/runScoped.cjs.map +1 -1
- package/_cjs/Channel/api/toPull.cjs +1 -1
- package/_cjs/Channel/api/toPull.cjs.map +1 -1
- package/_cjs/Channel/api.cjs +3 -2
- package/_cjs/Channel/api.cjs.map +1 -1
- package/_cjs/Channel/internal/ChannelExecutor.cjs +61 -52
- package/_cjs/Channel/internal/ChannelExecutor.cjs.map +1 -1
- package/_cjs/Fiber/FiberMessage.cjs +12 -11
- package/_cjs/Fiber/FiberMessage.cjs.map +1 -1
- package/_cjs/Fiber/FiberRuntime.cjs +28 -27
- package/_cjs/Fiber/FiberRuntime.cjs.map +1 -1
- package/_cjs/FiberScope/definition.cjs +2 -2
- package/_cjs/FiberScope/definition.cjs.map +1 -1
- package/_cjs/Hub/api.cjs.map +1 -1
- package/_cjs/{RuntimeConfig.cjs → IO/api/blocking.cjs} +13 -5
- package/_cjs/IO/api/blocking.cjs.map +1 -0
- package/_cjs/IO/api/provideSomeLayer.cjs.map +1 -1
- package/_cjs/IO/api/raceWith.cjs +9 -12
- package/_cjs/IO/api/raceWith.cjs.map +1 -1
- package/_cjs/IO/api/stateful.cjs +3 -3
- package/_cjs/IO/api/stateful.cjs.map +1 -1
- package/_cjs/IO/api/withChildren.cjs +5 -4
- package/_cjs/IO/api/withChildren.cjs.map +1 -1
- package/_cjs/IO/api/zipConcurrent.cjs +23 -23
- package/_cjs/IO/api/zipConcurrent.cjs.map +1 -1
- package/_cjs/IO/runtime.cjs +8 -8
- package/_cjs/IO/runtime.cjs.map +1 -1
- package/_cjs/IO.cjs +11 -0
- package/_cjs/IO.cjs.map +1 -1
- package/_cjs/Queue/api/filterInputIO.cjs.map +1 -1
- package/_cjs/Queue/api/filterOutputIO.cjs.map +1 -1
- package/_cjs/Ref/Synchronized/constructors.cjs +4 -5
- package/_cjs/Ref/Synchronized/constructors.cjs.map +1 -1
- package/_cjs/Ref/Synchronized/definition.cjs +1 -2
- package/_cjs/Ref/Synchronized/definition.cjs.map +1 -1
- package/_cjs/Semaphore.cjs +90 -0
- package/_cjs/Semaphore.cjs.map +1 -0
- package/_cjs/Stream/api.cjs +101 -61
- package/_cjs/Stream/api.cjs.map +1 -1
- package/_cjs/SubscriptionRef.cjs +2 -3
- package/_cjs/SubscriptionRef.cjs.map +1 -1
- package/_cjs/internal/BackgroundScheduler.cjs +199 -0
- package/_cjs/internal/BackgroundScheduler.cjs.map +1 -0
- package/_mjs/Cached/definition.mjs.map +1 -1
- package/_mjs/Channel/api/mapOutConcurrentIO.mjs +14 -16
- package/_mjs/Channel/api/mapOutConcurrentIO.mjs.map +1 -1
- package/_mjs/Channel/api/mergeAllWith.mjs +30 -32
- package/_mjs/Channel/api/mergeAllWith.mjs.map +1 -1
- package/_mjs/Channel/api/runScoped.mjs +19 -3
- package/_mjs/Channel/api/runScoped.mjs.map +1 -1
- package/_mjs/Channel/api/toPull.mjs +1 -1
- package/_mjs/Channel/api/toPull.mjs.map +1 -1
- package/_mjs/Channel/api.mjs +3 -2
- package/_mjs/Channel/api.mjs.map +1 -1
- package/_mjs/Channel/internal/ChannelExecutor.mjs +61 -52
- package/_mjs/Channel/internal/ChannelExecutor.mjs.map +1 -1
- package/_mjs/Fiber/FiberMessage.mjs +5 -5
- package/_mjs/Fiber/FiberMessage.mjs.map +1 -1
- package/_mjs/Fiber/FiberRuntime.mjs +28 -27
- package/_mjs/Fiber/FiberRuntime.mjs.map +1 -1
- package/_mjs/FiberScope/definition.mjs +2 -2
- package/_mjs/FiberScope/definition.mjs.map +1 -1
- package/_mjs/Hub/api.mjs.map +1 -1
- package/_mjs/IO/api/blocking.mjs +12 -0
- package/_mjs/IO/api/blocking.mjs.map +1 -0
- package/_mjs/IO/api/provideSomeLayer.mjs.map +1 -1
- package/_mjs/IO/api/raceWith.mjs +9 -12
- package/_mjs/IO/api/raceWith.mjs.map +1 -1
- package/_mjs/IO/api/stateful.mjs +3 -3
- package/_mjs/IO/api/stateful.mjs.map +1 -1
- package/_mjs/IO/api/withChildren.mjs +5 -4
- package/_mjs/IO/api/withChildren.mjs.map +1 -1
- package/_mjs/IO/api/zipConcurrent.mjs +23 -23
- package/_mjs/IO/api/zipConcurrent.mjs.map +1 -1
- package/_mjs/IO/runtime.mjs +6 -6
- package/_mjs/IO/runtime.mjs.map +1 -1
- package/_mjs/IO.mjs +1 -0
- package/_mjs/IO.mjs.map +1 -1
- package/_mjs/Queue/api/filterInputIO.mjs.map +1 -1
- package/_mjs/Queue/api/filterOutputIO.mjs.map +1 -1
- package/_mjs/Ref/Synchronized/constructors.mjs +4 -5
- package/_mjs/Ref/Synchronized/constructors.mjs.map +1 -1
- package/_mjs/Ref/Synchronized/definition.mjs +1 -2
- package/_mjs/Ref/Synchronized/definition.mjs.map +1 -1
- package/_mjs/Semaphore.mjs +78 -0
- package/_mjs/Semaphore.mjs.map +1 -0
- package/_mjs/Stream/api.mjs +93 -57
- package/_mjs/Stream/api.mjs.map +1 -1
- package/_mjs/SubscriptionRef.mjs +2 -3
- package/_mjs/SubscriptionRef.mjs.map +1 -1
- package/_mjs/internal/BackgroundScheduler.mjs +191 -0
- package/_mjs/internal/BackgroundScheduler.mjs.map +1 -0
- package/_src/Cached/definition.ts +1 -1
- package/_src/Channel/api/mapOutConcurrentIO.ts +1 -1
- package/_src/Channel/api/mergeAllWith.ts +1 -1
- package/_src/Channel/api/runScoped.ts +30 -5
- package/_src/Channel/api/toPull.ts +1 -1
- package/_src/Channel/api.ts +1 -0
- package/_src/Channel/internal/ChannelExecutor.ts +24 -20
- package/_src/Fiber/FiberMessage.ts +5 -5
- package/_src/Fiber/FiberRuntime.ts +1 -1
- package/_src/FiberRefs/api.ts +1 -1
- package/_src/Hub/api.ts +1 -1
- package/_src/IO/api/blocking.ts +9 -0
- package/_src/IO/api/provideSomeLayer.ts +1 -3
- package/_src/IO/api/raceWith.ts +1 -3
- package/_src/IO/api/stateful.ts +1 -1
- package/_src/IO/api/zipConcurrent.ts +26 -30
- package/_src/IO/runtime.ts +4 -4
- package/_src/IO.ts +1 -0
- package/_src/Queue/api/filterInputIO.ts +1 -1
- package/_src/Queue/api/filterOutputIO.ts +1 -1
- package/_src/Ref/Synchronized/constructors.ts +2 -2
- package/_src/Ref/Synchronized/definition.ts +1 -1
- package/_src/Semaphore.ts +81 -0
- package/_src/State/definition.ts +1 -1
- package/_src/Stream/api.ts +58 -7
- package/_src/Subject/Atomic.ts +1 -1
- package/_src/SubscriptionRef.ts +2 -2
- package/_src/global.ts +4 -4
- package/_src/index.ts +2 -2
- package/_src/internal/BackgroundScheduler.ts +276 -0
- package/global.d.ts +4 -4
- package/index.d.ts +2 -2
- package/internal/BackgroundScheduler.d.ts +47 -0
- package/package.json +3 -3
- package/RuntimeConfig.d.ts +0 -11
- package/_cjs/RuntimeConfig.cjs.map +0 -1
- package/_mjs/RuntimeConfig.mjs +0 -3
- package/_mjs/RuntimeConfig.mjs.map +0 -1
- package/_src/RuntimeConfig.ts +0 -6
package/_src/Stream/api.ts
CHANGED
|
@@ -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) =>
|
|
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))
|
|
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 = Δ(
|
|
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
|
|
2200
|
+
* @tsplus pipeable fncts.io.Stream mapIOConcurrently
|
|
2193
2201
|
*/
|
|
2194
|
-
export function
|
|
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
|
-
|
|
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.
|
|
3001
|
+
Δ(stream.runIntoQueueElementsScoped(queue).forkScoped);
|
|
2951
3002
|
return queue;
|
|
2952
3003
|
});
|
|
2953
3004
|
};
|
package/_src/Subject/Atomic.ts
CHANGED
package/_src/SubscriptionRef.ts
CHANGED
|
@@ -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:
|
|
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 = Δ(
|
|
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.
|
|
3
|
+
"version": "0.0.32",
|
|
4
4
|
"dependencies": {
|
|
5
|
-
"@fncts/base": "0.0.
|
|
6
|
-
"@fncts/transformers": "0.0.
|
|
5
|
+
"@fncts/base": "0.0.27",
|
|
6
|
+
"@fncts/transformers": "0.0.5",
|
|
7
7
|
"@fncts/typelevel": "0.0.15"
|
|
8
8
|
},
|
|
9
9
|
"exports": {
|
package/RuntimeConfig.d.ts
DELETED
|
@@ -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]}
|
package/_mjs/RuntimeConfig.mjs
DELETED
|
@@ -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]}
|