@volynets/reflex 0.1.1 → 0.1.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.
@@ -0,0 +1,74 @@
1
+ import type { WatcherQueue, EffectNode } from "./scheduler.types";
2
+
3
+ const INITIAL_QUEUE_CAPACITY = 16;
4
+
5
+ function growWatcherQueue(queue: WatcherQueue): void {
6
+ const ring = queue.ring;
7
+ const capacity = ring.length;
8
+ if (capacity === 0) {
9
+ ring.length = INITIAL_QUEUE_CAPACITY;
10
+ return;
11
+ }
12
+
13
+ const size = queue.size;
14
+ const head = queue.head;
15
+ const mask = capacity - 1;
16
+ const nextCapacity = capacity << 1;
17
+ const next = new Array<EffectNode>(nextCapacity);
18
+
19
+ for (let i = 0; i < size; ++i) {
20
+ next[i] = ring[(head + i) & mask]!;
21
+ }
22
+
23
+ ring.length = nextCapacity;
24
+ for (let i = 0; i < size; ++i) {
25
+ ring[i] = next[i]!;
26
+ }
27
+
28
+ queue.head = 0;
29
+ queue.tail = size;
30
+ }
31
+
32
+ function pushWatcherQueue(this: WatcherQueue, node: EffectNode): void {
33
+ const ring = this.ring;
34
+ if (this.size === ring.length) {
35
+ growWatcherQueue(this);
36
+ }
37
+
38
+ const tail = this.tail;
39
+ ring[tail] = node;
40
+ this.tail = (tail + 1) & (ring.length - 1);
41
+ ++this.size;
42
+ }
43
+
44
+ function shiftWatcherQueue(this: WatcherQueue): EffectNode | null {
45
+ if (this.size === 0) {
46
+ return null;
47
+ }
48
+
49
+ const ring = this.ring;
50
+ const head = this.head;
51
+ const node = ring[head]!;
52
+ ring[head] = undefined as never;
53
+ this.head = (head + 1) & (ring.length - 1);
54
+ --this.size;
55
+ return node;
56
+ }
57
+
58
+ function clearWatcherQueue(this: WatcherQueue): void {
59
+ this.head = 0;
60
+ this.tail = 0;
61
+ this.size = 0;
62
+ }
63
+
64
+ export function createWatcherQueue(): WatcherQueue {
65
+ return {
66
+ ring: [],
67
+ head: 0,
68
+ tail: 0,
69
+ size: 0,
70
+ push: pushWatcherQueue,
71
+ shift: shiftWatcherQueue,
72
+ clear: clearWatcherQueue,
73
+ };
74
+ }
@@ -0,0 +1,54 @@
1
+ import type { ExecutionContext, ReactiveNode } from "@reflex/runtime";
2
+ import type {
3
+ EffectSchedulerMode,
4
+ SchedulerPhase,
5
+ } from "./scheduler.constants";
6
+
7
+ export type EffectNode = ReactiveNode<undefined | Destructor>;
8
+
9
+ export interface WatcherQueue {
10
+ readonly ring: EffectNode[];
11
+ head: number;
12
+ tail: number;
13
+ size: number;
14
+
15
+ push(node: EffectNode): void;
16
+ shift(): EffectNode | null;
17
+ clear(): void;
18
+ }
19
+
20
+ export function noopNotifySettled(): void {}
21
+
22
+ export interface SchedulerCore {
23
+ readonly queue: WatcherQueue;
24
+ flush(): void;
25
+ enterBatch(): void;
26
+ leaveBatch(): boolean;
27
+ reset(): void;
28
+
29
+ get batchDepth(): number;
30
+ get phase(): SchedulerPhase;
31
+ }
32
+
33
+ export interface EffectScheduler {
34
+ readonly ring: EffectNode[];
35
+ readonly mode: EffectSchedulerMode;
36
+ readonly context: ExecutionContext;
37
+ readonly runtimeNotifySettled: (() => void) | undefined;
38
+
39
+ enqueue(node: ReactiveNode): void;
40
+ batch<T>(fn: () => T): T;
41
+ flush(): void;
42
+ notifySettled(): void;
43
+ reset(): void;
44
+
45
+ get head(): number;
46
+ get batchDepth(): number;
47
+ get phase(): SchedulerPhase;
48
+ }
49
+
50
+ export type SchedulerBatch = EffectScheduler["batch"];
51
+ export type SchedulerEnqueue = EffectScheduler["enqueue"];
52
+ export type SchedulerNotifySettled = EffectScheduler["notifySettled"];
53
+ export type SchedulerRuntimeNotifySettled =
54
+ EffectScheduler["runtimeNotifySettled"];
@@ -0,0 +1,3 @@
1
+ export * from "./scheduler.eager";
2
+ export * from "./scheduler.flush";
3
+ export * from "./scheduler.sab";
@@ -0,0 +1,46 @@
1
+ import type { ExecutionContext } from "@reflex/runtime";
2
+ import { EffectSchedulerMode } from "../scheduler.constants";
3
+ import {
4
+ createSchedulerCore,
5
+ isRuntimeInactive,
6
+ createSchedulerInstance,
7
+ tryEnqueue,
8
+ } from "../scheduler.core";
9
+ import type { EffectNode, EffectScheduler } from "../scheduler.types";
10
+
11
+ export function createEagerScheduler(
12
+ context: ExecutionContext,
13
+ ): EffectScheduler {
14
+ const core = createSchedulerCore();
15
+ const queue = core.queue;
16
+ const notifySettled = (): void => {
17
+ if (isRuntimeInactive(context, core) && queue.size !== 0) {
18
+ core.flush();
19
+ }
20
+ };
21
+ const enqueueToQueue = tryEnqueue.bind(null, queue);
22
+ const enqueue = (node: EffectNode) => {
23
+ if (!enqueueToQueue(node)) return;
24
+ if (isRuntimeInactive(context, core)) core.flush();
25
+ };
26
+ const batch = <T>(fn: () => T): T => {
27
+ core.enterBatch();
28
+ try {
29
+ return fn();
30
+ } finally {
31
+ if (core.leaveBatch() && queue.size !== 0) {
32
+ core.flush();
33
+ }
34
+ }
35
+ };
36
+
37
+ return createSchedulerInstance(
38
+ EffectSchedulerMode.Eager,
39
+ context,
40
+ core,
41
+ enqueue,
42
+ batch,
43
+ notifySettled,
44
+ notifySettled,
45
+ );
46
+ }
@@ -0,0 +1,35 @@
1
+ import type { ExecutionContext } from "@reflex/runtime";
2
+ import { EffectSchedulerMode } from "../scheduler.constants";
3
+ import {
4
+ createSchedulerCore,
5
+ createSchedulerInstance,
6
+ tryEnqueue,
7
+ } from "../scheduler.core";
8
+ import type { EffectScheduler } from "../scheduler.types";
9
+ import { noopNotifySettled } from "../scheduler.types";
10
+
11
+ export function createFlushScheduler(
12
+ context: ExecutionContext,
13
+ ): EffectScheduler {
14
+ const core = createSchedulerCore();
15
+ const queue = core.queue;
16
+ const enqueue = tryEnqueue.bind(null, queue);
17
+ const batch = <T>(fn: () => T): T => {
18
+ core.enterBatch();
19
+ try {
20
+ return fn();
21
+ } finally {
22
+ core.leaveBatch();
23
+ }
24
+ };
25
+
26
+ return createSchedulerInstance(
27
+ EffectSchedulerMode.Flush,
28
+ context,
29
+ core,
30
+ enqueue,
31
+ batch,
32
+ noopNotifySettled,
33
+ undefined,
34
+ );
35
+ }
@@ -0,0 +1,37 @@
1
+ import type { ExecutionContext } from "@reflex/runtime";
2
+ import { EffectSchedulerMode } from "../scheduler.constants";
3
+ import {
4
+ createSchedulerCore,
5
+ createSchedulerInstance,
6
+ isContextSettled,
7
+ tryEnqueue,
8
+ } from "../scheduler.core";
9
+ import type { EffectScheduler } from "../scheduler.types";
10
+ import { noopNotifySettled } from "../scheduler.types";
11
+
12
+ export function createSabScheduler(context: ExecutionContext): EffectScheduler {
13
+ const core = createSchedulerCore();
14
+ const queue = core.queue;
15
+ const enqueue = tryEnqueue.bind(null, queue);
16
+
17
+ const batch = <T>(fn: () => T): T => {
18
+ core.enterBatch();
19
+ try {
20
+ return fn();
21
+ } finally {
22
+ if (core.leaveBatch() && queue.size !== 0 && isContextSettled(context)) {
23
+ core.flush();
24
+ }
25
+ }
26
+ };
27
+
28
+ return createSchedulerInstance(
29
+ EffectSchedulerMode.SAB,
30
+ context,
31
+ core,
32
+ enqueue,
33
+ batch,
34
+ noopNotifySettled,
35
+ undefined,
36
+ );
37
+ }
@@ -0,0 +1,4 @@
1
+ // eslint-disable-next-line @typescript-eslint/triple-slash-reference
2
+ /// <reference path="../globals.d.ts" />
3
+
4
+ export * from "./resource";