@orkestrel/workflow 0.0.16 → 0.0.18
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/README.md +12 -6
- package/dist/src/browser/index.d.ts +53 -52
- package/dist/src/browser/index.js +55 -72
- package/dist/src/browser/index.js.map +1 -1
- package/dist/src/core/index.cjs +1356 -833
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +1951 -1407
- package/dist/src/core/index.d.ts +1951 -1407
- package/dist/src/core/index.js +1341 -825
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +16 -21
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +17 -16
- package/dist/src/server/index.d.ts +17 -16
- package/dist/src/server/index.js +17 -22
- package/dist/src/server/index.js.map +1 -1
- package/package.json +20 -21
package/README.md
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
# @orkestrel/workflow
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
> Orchestration as data: a JSON-serializable `Workflow → Phase → Task` tree that a UI or an
|
|
4
|
+
> LLM authors, a store persists, and a thin engine drives by composing the shipped execution
|
|
5
|
+
> substrate.
|
|
6
|
+
|
|
7
|
+
Author the definition as plain JSON, register the functions its tasks name, and
|
|
8
|
+
hand both to the runner: it builds the live tree, runs each phase in turn with
|
|
9
|
+
that phase's tasks concurrent, and resolves the settled result. Host-independent,
|
|
10
|
+
with browser-native and Node-native scheduler backends beside the default. Part
|
|
11
|
+
of the `@orkestrel` line.
|
|
6
12
|
|
|
7
13
|
## Install
|
|
8
14
|
|
|
@@ -33,13 +39,13 @@ attempt. Runtime pause gates are intentionally not persisted.
|
|
|
33
39
|
|
|
34
40
|
Provider sessions, external processes, MCP projection, journals, leases, and
|
|
35
41
|
distributed fencing remain integration concerns rather than core workflow
|
|
36
|
-
behavior. See [guides/
|
|
37
|
-
shipped contract. The proposed integration architecture
|
|
42
|
+
behavior. See [guides/workflow.md](./guides/workflow.md) for the complete
|
|
43
|
+
shipped contract. The proposed integration architecture lives with the
|
|
38
44
|
package that implements it, as `plan/PROPOSAL.md` in `@orkestrel/supervisor`.
|
|
39
45
|
|
|
40
46
|
## Package
|
|
41
47
|
|
|
42
|
-
Published as
|
|
48
|
+
Published as environment-scoped entry points: `.` provides the shared
|
|
43
49
|
environment-agnostic core and default scheduler, `./browser` adds browser-native
|
|
44
50
|
schedulers, and `./server` adds the Node-native scheduler. Core ships dual
|
|
45
51
|
ESM+CJS builds; `./browser` is ESM-only.
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { SchedulerInterface } from '@orkestrel/workflow';
|
|
2
|
-
import { SchedulerOptions } from '@orkestrel/workflow';
|
|
3
|
-
import { SchedulerPriority } from '@orkestrel/workflow';
|
|
1
|
+
import type { SchedulerInterface } from '@orkestrel/workflow';
|
|
2
|
+
import type { SchedulerOptions } from '@orkestrel/workflow';
|
|
3
|
+
import type { SchedulerPriority } from '@orkestrel/workflow';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
* built on the Prioritized Task Scheduling API (`scheduler.postTask`)
|
|
8
|
-
* zero-delay macrotask where it is absent
|
|
6
|
+
* Implements the browser {@link SchedulerInterface} — the browser-native cooperative-yield backend
|
|
7
|
+
* built on the Prioritized Task Scheduling API (`scheduler.postTask`) at the mapped priority,
|
|
8
|
+
* falling back to a zero-delay macrotask where it is absent, and rejecting an aborted wait
|
|
9
|
+
* with the caller's own reason.
|
|
9
10
|
*
|
|
10
11
|
* @remarks
|
|
11
12
|
* - **`yield` prefers `scheduler.postTask`, honouring priority.** When `globalThis`
|
|
@@ -13,12 +14,12 @@ import { SchedulerPriority } from '@orkestrel/workflow';
|
|
|
13
14
|
* priority (`user` → `'user-blocking'`, `normal` → `'user-visible'`, `background` →
|
|
14
15
|
* `'background'`), so the host genuinely regains control and the urgency hint is
|
|
15
16
|
* honoured. The capability is feature-detected through guards (`isRecord` / `isFunction`),
|
|
16
|
-
* never an `as
|
|
17
|
-
* it **falls back** to a `setTimeout(0)` macrotask — still a real host-turn,
|
|
18
|
-
*
|
|
17
|
+
* never an `as`. Where the API is absent (Firefox today, older engines),
|
|
18
|
+
* it **falls back** to a `setTimeout(0)` macrotask — still a real host-turn, without
|
|
19
|
+
* priority. `delay(ms)` is always a real `setTimeout`.
|
|
19
20
|
* - **Abort fidelity is verbatim.** The shared `scheduleHost` lifecycle links an owned
|
|
20
21
|
* settlement composite before scheduling, preserving the exact caller reason without
|
|
21
|
-
* invoking caller-owned signal methods. The caller signal is
|
|
22
|
+
* invoking caller-owned signal methods. The caller signal is not handed to `postTask`;
|
|
22
23
|
* an internal controller cancels that native task. An unexpected native promise rejection
|
|
23
24
|
* is routed back as the exact host failure instead of being discarded.
|
|
24
25
|
* - **Event-free.** A pure functional primitive — no Emitter, no events.
|
|
@@ -39,26 +40,25 @@ import { SchedulerPriority } from '@orkestrel/workflow';
|
|
|
39
40
|
export declare class BrowserScheduler implements SchedulerInterface {
|
|
40
41
|
#private;
|
|
41
42
|
/**
|
|
42
|
-
*
|
|
43
|
-
* `setTimeout(0)` macrotask where the API is absent), then
|
|
43
|
+
* Yields control to the host through `scheduler.postTask` at the given priority (or a
|
|
44
|
+
* `setTimeout(0)` macrotask where the API is absent), then resumes; abort rejects with
|
|
44
45
|
* `signal.reason`.
|
|
45
46
|
*/
|
|
46
47
|
yield(options?: SchedulerOptions): Promise<void>;
|
|
47
48
|
/**
|
|
48
|
-
*
|
|
49
|
+
* Resumes after at least `ms` milliseconds through `setTimeout`; abort rejects with
|
|
49
50
|
* `signal.reason`.
|
|
50
51
|
*
|
|
51
52
|
* @remarks
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* throwing.
|
|
53
|
+
* Pass a non-negative finite `ms`. The primitive does no validation: it passes `ms`
|
|
54
|
+
* straight to the host `setTimeout`, which clamps a negative value or `NaN` to ~0 — so an
|
|
55
|
+
* out-of-domain `ms` resolves on the next host turn rather than throwing.
|
|
56
56
|
*/
|
|
57
57
|
delay(ms: number, options?: SchedulerOptions): Promise<void>;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
|
-
*
|
|
61
|
+
* Creates the browser-native cooperative-yield {@link SchedulerInterface} — `yield()` uses
|
|
62
62
|
* the Prioritized Task Scheduling API (`scheduler.postTask`) at the requested priority
|
|
63
63
|
* when present, falling back to a `setTimeout(0)` macrotask; `delay(ms)` is a real
|
|
64
64
|
* `setTimeout`.
|
|
@@ -85,13 +85,13 @@ export declare class BrowserScheduler implements SchedulerInterface {
|
|
|
85
85
|
export declare function createBrowserScheduler(): SchedulerInterface;
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
88
|
+
* Creates the frame-aligned cooperative-yield {@link SchedulerInterface} — `yield()` resumes
|
|
89
|
+
* before the next paint through `requestAnimationFrame`; `delay(ms)` is a real
|
|
90
90
|
* `setTimeout`.
|
|
91
91
|
*
|
|
92
92
|
* @remarks
|
|
93
|
-
* Use it for work that
|
|
94
|
-
*
|
|
93
|
+
* Use it for work that batches per render frame (animation, incremental DOM updates) and
|
|
94
|
+
* naturally pauses while the tab is hidden. `yield` is abort-aware: pass `options.signal`
|
|
95
95
|
* and a pending yield rejects with the signal's `reason` verbatim, cancelling the pending
|
|
96
96
|
* frame request. `options.priority` is accepted but a no-op — a frame callback has no
|
|
97
97
|
* priority dimension.
|
|
@@ -109,8 +109,8 @@ export declare function createBrowserScheduler(): SchedulerInterface;
|
|
|
109
109
|
export declare function createFrameScheduler(): SchedulerInterface;
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
|
-
*
|
|
113
|
-
* the host is idle
|
|
112
|
+
* Creates the idle-time cooperative-yield {@link SchedulerInterface} — `yield()` resumes when
|
|
113
|
+
* the host is idle through `requestIdleCallback` when present, falling back to a `setTimeout(0)`
|
|
114
114
|
* macrotask; `delay(ms)` is a real `setTimeout`.
|
|
115
115
|
*
|
|
116
116
|
* @remarks
|
|
@@ -133,14 +133,15 @@ export declare function createFrameScheduler(): SchedulerInterface;
|
|
|
133
133
|
export declare function createIdleScheduler(): SchedulerInterface;
|
|
134
134
|
|
|
135
135
|
/**
|
|
136
|
-
*
|
|
137
|
-
* whose `yield` resumes
|
|
136
|
+
* Implements the frame-aligned {@link SchedulerInterface} — a browser cooperative-yield backend
|
|
137
|
+
* whose `yield` resumes before the next paint through `requestAnimationFrame`, rejecting an
|
|
138
|
+
* aborted wait with the caller's own reason. A `priority` hint is accepted and does nothing.
|
|
138
139
|
*
|
|
139
140
|
* @remarks
|
|
140
141
|
* - **`yield` resumes before the next paint.** `yield()` waits on `requestAnimationFrame`,
|
|
141
142
|
* so the resumption is aligned to the browser's render loop — ideal for work that
|
|
142
|
-
*
|
|
143
|
-
*
|
|
143
|
+
* batches per frame (animation, incremental DOM updates) and pauses while the tab is
|
|
144
|
+
* hidden (the host throttles rAF). `delay(ms)` is a real `setTimeout`, unaligned to
|
|
144
145
|
* frames. `options.priority` is accepted for contract compliance but a no-op — a frame
|
|
145
146
|
* callback has no priority dimension.
|
|
146
147
|
* - **Abort fidelity is verbatim, with cleanup.** The shared `scheduleHost` lifecycle links
|
|
@@ -164,50 +165,50 @@ export declare function createIdleScheduler(): SchedulerInterface;
|
|
|
164
165
|
export declare class FrameScheduler implements SchedulerInterface {
|
|
165
166
|
#private;
|
|
166
167
|
/**
|
|
167
|
-
*
|
|
168
|
-
* `requestAnimationFrame`, then
|
|
168
|
+
* Yields control to the host until the moment before the next paint through
|
|
169
|
+
* `requestAnimationFrame`, then resumes; abort rejects with `signal.reason`.
|
|
169
170
|
*/
|
|
170
171
|
yield(options?: SchedulerOptions): Promise<void>;
|
|
171
172
|
/**
|
|
172
|
-
*
|
|
173
|
+
* Resumes after at least `ms` milliseconds through `setTimeout`; abort rejects with
|
|
173
174
|
* `signal.reason`.
|
|
174
175
|
*
|
|
175
176
|
* @remarks
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
* throwing.
|
|
177
|
+
* Pass a non-negative finite `ms`. The primitive does no validation: it passes `ms`
|
|
178
|
+
* straight to the host `setTimeout`, which clamps a negative value or `NaN` to ~0 — so an
|
|
179
|
+
* out-of-domain `ms` resolves on the next host turn rather than throwing.
|
|
180
180
|
*/
|
|
181
181
|
delay(ms: number, options?: SchedulerOptions): Promise<void>;
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
/**
|
|
185
|
-
*
|
|
185
|
+
* Declares the narrowed `requestIdleCallback` / `cancelIdleCallback` pair feature-detected off
|
|
186
186
|
* `globalThis`.
|
|
187
187
|
*
|
|
188
188
|
* @remarks
|
|
189
189
|
* A `request` taking a callback and returning a numeric handle, and a `cancel` taking that
|
|
190
190
|
* handle. {@link IdleScheduler} feature-detects the pair through a guard (`isFunction`,
|
|
191
|
-
* never an `as`
|
|
191
|
+
* never an `as`) and resolves to `undefined` when the API is absent (Safari
|
|
192
192
|
* today), so `yield` falls back to a macrotask.
|
|
193
193
|
*/
|
|
194
|
-
export declare interface
|
|
194
|
+
export declare interface IdleInterface {
|
|
195
195
|
readonly request: (callback: () => void) => number;
|
|
196
196
|
readonly cancel: (handle: number) => void;
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
/**
|
|
200
|
-
*
|
|
201
|
-
* `yield` resumes when the host is idle
|
|
202
|
-
* zero-delay macrotask where it is absent
|
|
200
|
+
* Implements the idle-time {@link SchedulerInterface} — a browser cooperative-yield backend whose
|
|
201
|
+
* `yield` resumes when the host is idle through `requestIdleCallback`, falling back to a
|
|
202
|
+
* zero-delay macrotask where it is absent, and rejecting an aborted wait with the caller's own
|
|
203
|
+
* reason. A `priority` hint is accepted and does nothing.
|
|
203
204
|
*
|
|
204
205
|
* @remarks
|
|
205
206
|
* - **`yield` resumes during idle time.** When `globalThis` exposes `requestIdleCallback`,
|
|
206
207
|
* `yield()` waits on it, so the resumption happens when the browser has spare time after
|
|
207
208
|
* rendering and input — ideal for low-priority background work that must not contend with
|
|
208
209
|
* the user. The capability is feature-detected through a guard (`isFunction`), never an
|
|
209
|
-
* `as
|
|
210
|
-
* `setTimeout(0)` macrotask — still a real host-turn,
|
|
210
|
+
* `as`. Where the API is absent (Safari today), it **falls back** to a
|
|
211
|
+
* `setTimeout(0)` macrotask — still a real host-turn, not idle-gated. `delay(ms)` is
|
|
211
212
|
* always a real `setTimeout`. `options.priority` is accepted for contract compliance but a
|
|
212
213
|
* no-op — idle scheduling has no priority dimension.
|
|
213
214
|
* - **Abort fidelity is verbatim, with cleanup.** The shared `scheduleHost` lifecycle links
|
|
@@ -231,27 +232,27 @@ export declare interface IdleAPI {
|
|
|
231
232
|
export declare class IdleScheduler implements SchedulerInterface {
|
|
232
233
|
#private;
|
|
233
234
|
/**
|
|
234
|
-
*
|
|
235
|
-
* `setTimeout(0)` macrotask where the API is absent), then
|
|
235
|
+
* Yields control to the host until it is idle through `requestIdleCallback` (or a
|
|
236
|
+
* `setTimeout(0)` macrotask where the API is absent), then resumes; abort rejects with
|
|
236
237
|
* `signal.reason`.
|
|
237
238
|
*/
|
|
238
239
|
yield(options?: SchedulerOptions): Promise<void>;
|
|
239
240
|
/**
|
|
240
|
-
*
|
|
241
|
+
* Resumes after at least `ms` milliseconds through `setTimeout`; abort rejects with
|
|
241
242
|
* `signal.reason`.
|
|
242
243
|
*
|
|
243
244
|
* @remarks
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
* throwing.
|
|
245
|
+
* Pass a non-negative finite `ms`. The primitive does no validation: it passes `ms`
|
|
246
|
+
* straight to the host `setTimeout`, which clamps a negative value or `NaN` to ~0 — so an
|
|
247
|
+
* out-of-domain `ms` resolves on the next host turn rather than throwing.
|
|
248
248
|
*/
|
|
249
249
|
delay(ms: number, options?: SchedulerOptions): Promise<void>;
|
|
250
250
|
}
|
|
251
251
|
|
|
252
252
|
/**
|
|
253
|
-
*
|
|
254
|
-
*
|
|
253
|
+
* Maps each portable {@link SchedulerPriority} to the browser-native `postTask` priority —
|
|
254
|
+
* `user` to `'user-blocking'`, `normal` to `'user-visible'`, and `background` to
|
|
255
|
+
* `'background'`.
|
|
255
256
|
*
|
|
256
257
|
* @remarks
|
|
257
258
|
* A `user` hint maps to the most urgent `'user-blocking'`, `normal` to the default
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { scheduleHost } from "../core/index.js";
|
|
1
|
+
import { delayHost, scheduleHost } from "../core/index.js";
|
|
2
2
|
import { isFunction, isPromise, isRecord } from "@orkestrel/contract";
|
|
3
3
|
//#region src/browser/constants.ts
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Maps each portable {@link SchedulerPriority} to the browser-native `postTask` priority —
|
|
6
|
+
* `user` to `'user-blocking'`, `normal` to `'user-visible'`, and `background` to
|
|
7
|
+
* `'background'`.
|
|
7
8
|
*
|
|
8
9
|
* @remarks
|
|
9
10
|
* A `user` hint maps to the most urgent `'user-blocking'`, `normal` to the default
|
|
@@ -19,9 +20,10 @@ var POST_TASK_PRIORITY = Object.freeze({
|
|
|
19
20
|
//#endregion
|
|
20
21
|
//#region src/browser/BrowserScheduler.ts
|
|
21
22
|
/**
|
|
22
|
-
*
|
|
23
|
-
* built on the Prioritized Task Scheduling API (`scheduler.postTask`)
|
|
24
|
-
* zero-delay macrotask where it is absent
|
|
23
|
+
* Implements the browser {@link SchedulerInterface} — the browser-native cooperative-yield backend
|
|
24
|
+
* built on the Prioritized Task Scheduling API (`scheduler.postTask`) at the mapped priority,
|
|
25
|
+
* falling back to a zero-delay macrotask where it is absent, and rejecting an aborted wait
|
|
26
|
+
* with the caller's own reason.
|
|
25
27
|
*
|
|
26
28
|
* @remarks
|
|
27
29
|
* - **`yield` prefers `scheduler.postTask`, honouring priority.** When `globalThis`
|
|
@@ -29,12 +31,12 @@ var POST_TASK_PRIORITY = Object.freeze({
|
|
|
29
31
|
* priority (`user` → `'user-blocking'`, `normal` → `'user-visible'`, `background` →
|
|
30
32
|
* `'background'`), so the host genuinely regains control and the urgency hint is
|
|
31
33
|
* honoured. The capability is feature-detected through guards (`isRecord` / `isFunction`),
|
|
32
|
-
* never an `as
|
|
33
|
-
* it **falls back** to a `setTimeout(0)` macrotask — still a real host-turn,
|
|
34
|
-
*
|
|
34
|
+
* never an `as`. Where the API is absent (Firefox today, older engines),
|
|
35
|
+
* it **falls back** to a `setTimeout(0)` macrotask — still a real host-turn, without
|
|
36
|
+
* priority. `delay(ms)` is always a real `setTimeout`.
|
|
35
37
|
* - **Abort fidelity is verbatim.** The shared `scheduleHost` lifecycle links an owned
|
|
36
38
|
* settlement composite before scheduling, preserving the exact caller reason without
|
|
37
|
-
* invoking caller-owned signal methods. The caller signal is
|
|
39
|
+
* invoking caller-owned signal methods. The caller signal is not handed to `postTask`;
|
|
38
40
|
* an internal controller cancels that native task. An unexpected native promise rejection
|
|
39
41
|
* is routed back as the exact host failure instead of being discarded.
|
|
40
42
|
* - **Event-free.** A pure functional primitive — no Emitter, no events.
|
|
@@ -54,27 +56,26 @@ var POST_TASK_PRIORITY = Object.freeze({
|
|
|
54
56
|
*/
|
|
55
57
|
var BrowserScheduler = class {
|
|
56
58
|
/**
|
|
57
|
-
*
|
|
58
|
-
* `setTimeout(0)` macrotask where the API is absent), then
|
|
59
|
+
* Yields control to the host through `scheduler.postTask` at the given priority (or a
|
|
60
|
+
* `setTimeout(0)` macrotask where the API is absent), then resumes; abort rejects with
|
|
59
61
|
* `signal.reason`.
|
|
60
62
|
*/
|
|
61
63
|
yield(options) {
|
|
62
64
|
const post = this.#postTask();
|
|
63
|
-
if (post === void 0) return
|
|
65
|
+
if (post === void 0) return delayHost(0, options?.signal);
|
|
64
66
|
return this.#yieldVia(post, options?.priority ?? "normal", options?.signal);
|
|
65
67
|
}
|
|
66
68
|
/**
|
|
67
|
-
*
|
|
69
|
+
* Resumes after at least `ms` milliseconds through `setTimeout`; abort rejects with
|
|
68
70
|
* `signal.reason`.
|
|
69
71
|
*
|
|
70
72
|
* @remarks
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* throwing.
|
|
73
|
+
* Pass a non-negative finite `ms`. The primitive does no validation: it passes `ms`
|
|
74
|
+
* straight to the host `setTimeout`, which clamps a negative value or `NaN` to ~0 — so an
|
|
75
|
+
* out-of-domain `ms` resolves on the next host turn rather than throwing.
|
|
75
76
|
*/
|
|
76
77
|
delay(ms, options) {
|
|
77
|
-
return
|
|
78
|
+
return delayHost(ms, options?.signal);
|
|
78
79
|
}
|
|
79
80
|
#postTask() {
|
|
80
81
|
const candidate = Reflect.get(globalThis, "scheduler");
|
|
@@ -94,24 +95,19 @@ var BrowserScheduler = class {
|
|
|
94
95
|
return () => internal.abort();
|
|
95
96
|
}, signal);
|
|
96
97
|
}
|
|
97
|
-
#timer(ms, signal) {
|
|
98
|
-
return scheduleHost((complete) => {
|
|
99
|
-
const handle = setTimeout(complete, ms);
|
|
100
|
-
return () => clearTimeout(handle);
|
|
101
|
-
}, signal);
|
|
102
|
-
}
|
|
103
98
|
};
|
|
104
99
|
//#endregion
|
|
105
100
|
//#region src/browser/FrameScheduler.ts
|
|
106
101
|
/**
|
|
107
|
-
*
|
|
108
|
-
* whose `yield` resumes
|
|
102
|
+
* Implements the frame-aligned {@link SchedulerInterface} — a browser cooperative-yield backend
|
|
103
|
+
* whose `yield` resumes before the next paint through `requestAnimationFrame`, rejecting an
|
|
104
|
+
* aborted wait with the caller's own reason. A `priority` hint is accepted and does nothing.
|
|
109
105
|
*
|
|
110
106
|
* @remarks
|
|
111
107
|
* - **`yield` resumes before the next paint.** `yield()` waits on `requestAnimationFrame`,
|
|
112
108
|
* so the resumption is aligned to the browser's render loop — ideal for work that
|
|
113
|
-
*
|
|
114
|
-
*
|
|
109
|
+
* batches per frame (animation, incremental DOM updates) and pauses while the tab is
|
|
110
|
+
* hidden (the host throttles rAF). `delay(ms)` is a real `setTimeout`, unaligned to
|
|
115
111
|
* frames. `options.priority` is accepted for contract compliance but a no-op — a frame
|
|
116
112
|
* callback has no priority dimension.
|
|
117
113
|
* - **Abort fidelity is verbatim, with cleanup.** The shared `scheduleHost` lifecycle links
|
|
@@ -134,24 +130,23 @@ var BrowserScheduler = class {
|
|
|
134
130
|
*/
|
|
135
131
|
var FrameScheduler = class {
|
|
136
132
|
/**
|
|
137
|
-
*
|
|
138
|
-
* `requestAnimationFrame`, then
|
|
133
|
+
* Yields control to the host until the moment before the next paint through
|
|
134
|
+
* `requestAnimationFrame`, then resumes; abort rejects with `signal.reason`.
|
|
139
135
|
*/
|
|
140
136
|
yield(options) {
|
|
141
137
|
return this.#frame(options?.signal);
|
|
142
138
|
}
|
|
143
139
|
/**
|
|
144
|
-
*
|
|
140
|
+
* Resumes after at least `ms` milliseconds through `setTimeout`; abort rejects with
|
|
145
141
|
* `signal.reason`.
|
|
146
142
|
*
|
|
147
143
|
* @remarks
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
* throwing.
|
|
144
|
+
* Pass a non-negative finite `ms`. The primitive does no validation: it passes `ms`
|
|
145
|
+
* straight to the host `setTimeout`, which clamps a negative value or `NaN` to ~0 — so an
|
|
146
|
+
* out-of-domain `ms` resolves on the next host turn rather than throwing.
|
|
152
147
|
*/
|
|
153
148
|
delay(ms, options) {
|
|
154
|
-
return
|
|
149
|
+
return delayHost(ms, options?.signal);
|
|
155
150
|
}
|
|
156
151
|
#frame(signal) {
|
|
157
152
|
return scheduleHost((complete) => {
|
|
@@ -159,27 +154,22 @@ var FrameScheduler = class {
|
|
|
159
154
|
return () => cancelAnimationFrame(handle);
|
|
160
155
|
}, signal);
|
|
161
156
|
}
|
|
162
|
-
#sleep(ms, signal) {
|
|
163
|
-
return scheduleHost((complete) => {
|
|
164
|
-
const handle = setTimeout(complete, ms);
|
|
165
|
-
return () => clearTimeout(handle);
|
|
166
|
-
}, signal);
|
|
167
|
-
}
|
|
168
157
|
};
|
|
169
158
|
//#endregion
|
|
170
159
|
//#region src/browser/IdleScheduler.ts
|
|
171
160
|
/**
|
|
172
|
-
*
|
|
173
|
-
* `yield` resumes when the host is idle
|
|
174
|
-
* zero-delay macrotask where it is absent
|
|
161
|
+
* Implements the idle-time {@link SchedulerInterface} — a browser cooperative-yield backend whose
|
|
162
|
+
* `yield` resumes when the host is idle through `requestIdleCallback`, falling back to a
|
|
163
|
+
* zero-delay macrotask where it is absent, and rejecting an aborted wait with the caller's own
|
|
164
|
+
* reason. A `priority` hint is accepted and does nothing.
|
|
175
165
|
*
|
|
176
166
|
* @remarks
|
|
177
167
|
* - **`yield` resumes during idle time.** When `globalThis` exposes `requestIdleCallback`,
|
|
178
168
|
* `yield()` waits on it, so the resumption happens when the browser has spare time after
|
|
179
169
|
* rendering and input — ideal for low-priority background work that must not contend with
|
|
180
170
|
* the user. The capability is feature-detected through a guard (`isFunction`), never an
|
|
181
|
-
* `as
|
|
182
|
-
* `setTimeout(0)` macrotask — still a real host-turn,
|
|
171
|
+
* `as`. Where the API is absent (Safari today), it **falls back** to a
|
|
172
|
+
* `setTimeout(0)` macrotask — still a real host-turn, not idle-gated. `delay(ms)` is
|
|
183
173
|
* always a real `setTimeout`. `options.priority` is accepted for contract compliance but a
|
|
184
174
|
* no-op — idle scheduling has no priority dimension.
|
|
185
175
|
* - **Abort fidelity is verbatim, with cleanup.** The shared `scheduleHost` lifecycle links
|
|
@@ -202,29 +192,28 @@ var FrameScheduler = class {
|
|
|
202
192
|
*/
|
|
203
193
|
var IdleScheduler = class {
|
|
204
194
|
/**
|
|
205
|
-
*
|
|
206
|
-
* `setTimeout(0)` macrotask where the API is absent), then
|
|
195
|
+
* Yields control to the host until it is idle through `requestIdleCallback` (or a
|
|
196
|
+
* `setTimeout(0)` macrotask where the API is absent), then resumes; abort rejects with
|
|
207
197
|
* `signal.reason`.
|
|
208
198
|
*/
|
|
209
199
|
yield(options) {
|
|
210
|
-
const idle = this.#
|
|
211
|
-
if (idle === void 0) return
|
|
200
|
+
const idle = this.#idleCallback();
|
|
201
|
+
if (idle === void 0) return delayHost(0, options?.signal);
|
|
212
202
|
return this.#idle(idle, options?.signal);
|
|
213
203
|
}
|
|
214
204
|
/**
|
|
215
|
-
*
|
|
205
|
+
* Resumes after at least `ms` milliseconds through `setTimeout`; abort rejects with
|
|
216
206
|
* `signal.reason`.
|
|
217
207
|
*
|
|
218
208
|
* @remarks
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
* throwing.
|
|
209
|
+
* Pass a non-negative finite `ms`. The primitive does no validation: it passes `ms`
|
|
210
|
+
* straight to the host `setTimeout`, which clamps a negative value or `NaN` to ~0 — so an
|
|
211
|
+
* out-of-domain `ms` resolves on the next host turn rather than throwing.
|
|
223
212
|
*/
|
|
224
213
|
delay(ms, options) {
|
|
225
|
-
return
|
|
214
|
+
return delayHost(ms, options?.signal);
|
|
226
215
|
}
|
|
227
|
-
#
|
|
216
|
+
#idleCallback() {
|
|
228
217
|
const request = Reflect.get(globalThis, "requestIdleCallback");
|
|
229
218
|
const cancel = Reflect.get(globalThis, "cancelIdleCallback");
|
|
230
219
|
if (!isFunction(request) || !isFunction(cancel)) return void 0;
|
|
@@ -239,12 +228,6 @@ var IdleScheduler = class {
|
|
|
239
228
|
return () => idle.cancel(handle);
|
|
240
229
|
}, signal);
|
|
241
230
|
}
|
|
242
|
-
#sleep(ms, signal) {
|
|
243
|
-
return scheduleHost((complete) => {
|
|
244
|
-
const handle = setTimeout(complete, ms);
|
|
245
|
-
return () => clearTimeout(handle);
|
|
246
|
-
}, signal);
|
|
247
|
-
}
|
|
248
231
|
#request(request, callback) {
|
|
249
232
|
return Number(Reflect.apply(request, globalThis, [callback]));
|
|
250
233
|
}
|
|
@@ -255,7 +238,7 @@ var IdleScheduler = class {
|
|
|
255
238
|
//#endregion
|
|
256
239
|
//#region src/browser/factories.ts
|
|
257
240
|
/**
|
|
258
|
-
*
|
|
241
|
+
* Creates the browser-native cooperative-yield {@link SchedulerInterface} — `yield()` uses
|
|
259
242
|
* the Prioritized Task Scheduling API (`scheduler.postTask`) at the requested priority
|
|
260
243
|
* when present, falling back to a `setTimeout(0)` macrotask; `delay(ms)` is a real
|
|
261
244
|
* `setTimeout`.
|
|
@@ -283,13 +266,13 @@ function createBrowserScheduler() {
|
|
|
283
266
|
return new BrowserScheduler();
|
|
284
267
|
}
|
|
285
268
|
/**
|
|
286
|
-
*
|
|
287
|
-
*
|
|
269
|
+
* Creates the frame-aligned cooperative-yield {@link SchedulerInterface} — `yield()` resumes
|
|
270
|
+
* before the next paint through `requestAnimationFrame`; `delay(ms)` is a real
|
|
288
271
|
* `setTimeout`.
|
|
289
272
|
*
|
|
290
273
|
* @remarks
|
|
291
|
-
* Use it for work that
|
|
292
|
-
*
|
|
274
|
+
* Use it for work that batches per render frame (animation, incremental DOM updates) and
|
|
275
|
+
* naturally pauses while the tab is hidden. `yield` is abort-aware: pass `options.signal`
|
|
293
276
|
* and a pending yield rejects with the signal's `reason` verbatim, cancelling the pending
|
|
294
277
|
* frame request. `options.priority` is accepted but a no-op — a frame callback has no
|
|
295
278
|
* priority dimension.
|
|
@@ -308,8 +291,8 @@ function createFrameScheduler() {
|
|
|
308
291
|
return new FrameScheduler();
|
|
309
292
|
}
|
|
310
293
|
/**
|
|
311
|
-
*
|
|
312
|
-
* the host is idle
|
|
294
|
+
* Creates the idle-time cooperative-yield {@link SchedulerInterface} — `yield()` resumes when
|
|
295
|
+
* the host is idle through `requestIdleCallback` when present, falling back to a `setTimeout(0)`
|
|
313
296
|
* macrotask; `delay(ms)` is a real `setTimeout`.
|
|
314
297
|
*
|
|
315
298
|
* @remarks
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/browser/constants.ts","../../../src/browser/BrowserScheduler.ts","../../../src/browser/FrameScheduler.ts","../../../src/browser/IdleScheduler.ts","../../../src/browser/factories.ts"],"sourcesContent":["import type { SchedulerPriority } from '@src/core'\n\n/**\n * The browser-native `postTask` priority for each portable {@link SchedulerPriority} — the\n * Prioritized Task Scheduling API's three levels.\n *\n * @remarks\n * A `user` hint maps to the most urgent `'user-blocking'`, `normal` to the default\n * `'user-visible'`, and `background` to `'background'`. {@link BrowserScheduler} reads this\n * map to translate the caller's portable priority into the value passed to\n * `scheduler.postTask`, so the urgency hint is honoured by the host.\n */\nexport const POST_TASK_PRIORITY: Readonly<Record<SchedulerPriority, string>> = Object.freeze({\n\tuser: 'user-blocking',\n\tnormal: 'user-visible',\n\tbackground: 'background',\n})\n","import type { SchedulerInterface, SchedulerOptions, SchedulerPriority } from '@src/core'\nimport { scheduleHost } from '@src/core'\nimport { isFunction, isPromise, isRecord } from '@orkestrel/contract'\nimport { POST_TASK_PRIORITY } from './constants.js'\n\n/**\n * The browser {@link SchedulerInterface} — the browser-native cooperative-yield backend\n * built on the Prioritized Task Scheduling API (`scheduler.postTask`), falling back to a\n * zero-delay macrotask where it is absent.\n *\n * @remarks\n * - **`yield` prefers `scheduler.postTask`, honouring priority.** When `globalThis`\n * exposes a `scheduler` with a `postTask` method, `yield()` posts a task at the mapped\n * priority (`user` → `'user-blocking'`, `normal` → `'user-visible'`, `background` →\n * `'background'`), so the host genuinely regains control and the urgency hint is\n * honoured. The capability is feature-detected through guards (`isRecord` / `isFunction`),\n * never an `as` (AGENTS §14). Where the API is absent (Firefox today, older engines),\n * it **falls back** to a `setTimeout(0)` macrotask — still a real host-turn, just\n * without priority. `delay(ms)` is always a real `setTimeout`.\n * - **Abort fidelity is verbatim.** The shared `scheduleHost` lifecycle links an owned\n * settlement composite before scheduling, preserving the exact caller reason without\n * invoking caller-owned signal methods. The caller signal is NOT handed to `postTask`;\n * an internal controller cancels that native task. An unexpected native promise rejection\n * is routed back as the exact host failure instead of being discarded.\n * - **Event-free.** A pure functional primitive — no Emitter, no events.\n *\n * @example\n * ```ts\n * import { createAbort } from '@orkestrel/abort'\n * import { BrowserScheduler } from '@orkestrel/workflow/browser'\n *\n * const abort = createAbort()\n * const scheduler = new BrowserScheduler()\n * while (!abort.signal.aborted) {\n * \tdoSomeWork()\n * \tawait scheduler.yield({ priority: 'background', signal: abort.signal })\n * }\n * ```\n */\nexport class BrowserScheduler implements SchedulerInterface {\n\t/**\n\t * Yield control to the host via `scheduler.postTask` at the given priority (or a\n\t * `setTimeout(0)` macrotask where the API is absent), then resume; abort rejects with\n\t * `signal.reason`.\n\t */\n\tyield(options?: SchedulerOptions): Promise<void> {\n\t\tconst post = this.#postTask()\n\t\tif (post === undefined) return this.#timer(0, options?.signal)\n\t\treturn this.#yieldVia(post, options?.priority ?? 'normal', options?.signal)\n\t}\n\n\t/**\n\t * Resume after at least `ms` milliseconds via `setTimeout`; abort rejects with\n\t * `signal.reason`.\n\t *\n\t * @remarks\n\t * `ms` should be a non-negative finite number. The primitive does no validation: it\n\t * passes `ms` straight to the host `setTimeout`, which clamps a negative value or\n\t * `NaN` to ~0 — so an out-of-domain `ms` resolves on the next host turn rather than\n\t * throwing.\n\t */\n\tdelay(ms: number, options?: SchedulerOptions): Promise<void> {\n\t\treturn this.#timer(ms, options?.signal)\n\t}\n\n\t// === Private\n\n\t// Feature-detect the Prioritized Task Scheduling API through guards (no `as`): the\n\t// global `scheduler` must be a record carrying a callable `postTask`. Returns the\n\t// narrowed `postTask` function, or `undefined` when the API is absent (the fallback).\n\t#postTask(): ((callback: () => void, options: Record<string, unknown>) => unknown) | undefined {\n\t\tconst candidate: unknown = Reflect.get(globalThis, 'scheduler')\n\t\tif (!isRecord(candidate)) return undefined\n\t\tconst post = candidate.postTask\n\t\tif (!isFunction(post)) return undefined\n\t\treturn (callback, options) => Reflect.apply(post, candidate, [callback, options])\n\t}\n\n\t// A `scheduler.postTask` boundary with an internal cancellation controller; `scheduleHost`\n\t// owns caller linking and first-settlement arbitration, including native promise failure.\n\t#yieldVia(\n\t\tpost: (callback: () => void, options: Record<string, unknown>) => unknown,\n\t\tpriority: SchedulerPriority,\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\treturn scheduleHost((complete, failure) => {\n\t\t\tconst internal = new AbortController()\n\t\t\tconst task = post(complete, {\n\t\t\t\tpriority: POST_TASK_PRIORITY[priority],\n\t\t\t\tsignal: internal.signal,\n\t\t\t})\n\t\t\tif (isPromise(task)) void task.catch(failure)\n\t\t\treturn () => internal.abort()\n\t\t}, signal)\n\t}\n\n\t// The browser timer boundary shared by `delay` and fallback `yield`.\n\t#timer(ms: number, signal?: AbortSignal): Promise<void> {\n\t\treturn scheduleHost((complete) => {\n\t\t\tconst handle = setTimeout(complete, ms)\n\t\t\treturn () => clearTimeout(handle)\n\t\t}, signal)\n\t}\n}\n","import type { SchedulerInterface, SchedulerOptions } from '@src/core'\nimport { scheduleHost } from '@src/core'\n\n/**\n * The frame-aligned {@link SchedulerInterface} — a browser cooperative-yield backend\n * whose `yield` resumes just before the next paint via `requestAnimationFrame`.\n *\n * @remarks\n * - **`yield` resumes before the next paint.** `yield()` waits on `requestAnimationFrame`,\n * so the resumption is aligned to the browser's render loop — ideal for work that\n * should batch per frame (animation, incremental DOM updates) and pause while the tab\n * is hidden (the host throttles rAF). `delay(ms)` is a real `setTimeout`, unaligned to\n * frames. `options.priority` is accepted for contract compliance but a no-op — a frame\n * callback has no priority dimension.\n * - **Abort fidelity is verbatim, with cleanup.** The shared `scheduleHost` lifecycle links\n * an owned settlement composite before requesting a frame, never invokes caller-owned\n * signal methods, and cancels the native handle when abort wins.\n * - **Event-free.** A pure functional primitive — no Emitter, no events.\n *\n * @example\n * ```ts\n * import { createAbort } from '@orkestrel/abort'\n * import { FrameScheduler } from '@orkestrel/workflow/browser'\n *\n * const abort = createAbort()\n * const scheduler = new FrameScheduler()\n * while (!abort.signal.aborted) {\n * \trenderOneFrameOfWork()\n * \tawait scheduler.yield({ signal: abort.signal }) // resume before the next paint\n * }\n * ```\n */\nexport class FrameScheduler implements SchedulerInterface {\n\t/**\n\t * Yield control to the host until just before the next paint via\n\t * `requestAnimationFrame`, then resume; abort rejects with `signal.reason`.\n\t */\n\tyield(options?: SchedulerOptions): Promise<void> {\n\t\treturn this.#frame(options?.signal)\n\t}\n\n\t/**\n\t * Resume after at least `ms` milliseconds via `setTimeout`; abort rejects with\n\t * `signal.reason`.\n\t *\n\t * @remarks\n\t * `ms` should be a non-negative finite number. The primitive does no validation: it\n\t * passes `ms` straight to the host `setTimeout`, which clamps a negative value or\n\t * `NaN` to ~0 — so an out-of-domain `ms` resolves on the next host turn rather than\n\t * throwing.\n\t */\n\tdelay(ms: number, options?: SchedulerOptions): Promise<void> {\n\t\treturn this.#sleep(ms, options?.signal)\n\t}\n\n\t// === Private\n\n\t// The frame request boundary; `scheduleHost` owns cancellation lifecycle.\n\t#frame(signal?: AbortSignal): Promise<void> {\n\t\treturn scheduleHost((complete) => {\n\t\t\tconst handle = requestAnimationFrame(complete)\n\t\t\treturn () => cancelAnimationFrame(handle)\n\t\t}, signal)\n\t}\n\n\t// The browser timer boundary; `scheduleHost` owns cancellation lifecycle.\n\t#sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\t\treturn scheduleHost((complete) => {\n\t\t\tconst handle = setTimeout(complete, ms)\n\t\t\treturn () => clearTimeout(handle)\n\t\t}, signal)\n\t}\n}\n","import type { SchedulerInterface, SchedulerOptions } from '@src/core'\nimport { scheduleHost } from '@src/core'\nimport type { AnyFunction } from '@orkestrel/contract'\nimport type { IdleAPI } from './types.js'\nimport { isFunction } from '@orkestrel/contract'\n\n/**\n * The idle-time {@link SchedulerInterface} — a browser cooperative-yield backend whose\n * `yield` resumes when the host is idle via `requestIdleCallback`, falling back to a\n * zero-delay macrotask where it is absent.\n *\n * @remarks\n * - **`yield` resumes during idle time.** When `globalThis` exposes `requestIdleCallback`,\n * `yield()` waits on it, so the resumption happens when the browser has spare time after\n * rendering and input — ideal for low-priority background work that must not contend with\n * the user. The capability is feature-detected through a guard (`isFunction`), never an\n * `as` (AGENTS §14). Where the API is absent (Safari today), it **falls back** to a\n * `setTimeout(0)` macrotask — still a real host-turn, just not idle-gated. `delay(ms)` is\n * always a real `setTimeout`. `options.priority` is accepted for contract compliance but a\n * no-op — idle scheduling has no priority dimension.\n * - **Abort fidelity is verbatim, with cleanup.** The shared `scheduleHost` lifecycle links\n * an owned settlement composite before scheduling, never invokes caller-owned signal\n * methods, and cancels the idle callback or fallback timer when abort wins.\n * - **Event-free.** A pure functional primitive — no Emitter, no events.\n *\n * @example\n * ```ts\n * import { createAbort } from '@orkestrel/abort'\n * import { IdleScheduler } from '@orkestrel/workflow/browser'\n *\n * const abort = createAbort()\n * const scheduler = new IdleScheduler()\n * while (!abort.signal.aborted) {\n * \tdoLowPriorityWork()\n * \tawait scheduler.yield({ signal: abort.signal }) // resume when the host is idle\n * }\n * ```\n */\nexport class IdleScheduler implements SchedulerInterface {\n\t/**\n\t * Yield control to the host until it is idle via `requestIdleCallback` (or a\n\t * `setTimeout(0)` macrotask where the API is absent), then resume; abort rejects with\n\t * `signal.reason`.\n\t */\n\tyield(options?: SchedulerOptions): Promise<void> {\n\t\tconst idle = this.#idleAPI()\n\t\tif (idle === undefined) return this.#sleep(0, options?.signal)\n\t\treturn this.#idle(idle, options?.signal)\n\t}\n\n\t/**\n\t * Resume after at least `ms` milliseconds via `setTimeout`; abort rejects with\n\t * `signal.reason`.\n\t *\n\t * @remarks\n\t * `ms` should be a non-negative finite number. The primitive does no validation: it\n\t * passes `ms` straight to the host `setTimeout`, which clamps a negative value or\n\t * `NaN` to ~0 — so an out-of-domain `ms` resolves on the next host turn rather than\n\t * throwing.\n\t */\n\tdelay(ms: number, options?: SchedulerOptions): Promise<void> {\n\t\treturn this.#sleep(ms, options?.signal)\n\t}\n\n\t// === Private\n\n\t// Feature-detect `requestIdleCallback` / `cancelIdleCallback` off `globalThis` through\n\t// guards (no `as`): both must be callable. Returns the narrowed pair, or `undefined`\n\t// when the API is absent (the macrotask fallback).\n\t#idleAPI(): IdleAPI | undefined {\n\t\tconst request: unknown = Reflect.get(globalThis, 'requestIdleCallback')\n\t\tconst cancel: unknown = Reflect.get(globalThis, 'cancelIdleCallback')\n\t\tif (!isFunction(request) || !isFunction(cancel)) return undefined\n\t\treturn {\n\t\t\trequest: this.#request.bind(this, request),\n\t\t\tcancel: this.#cancel.bind(this, cancel),\n\t\t}\n\t}\n\n\t// The idle callback boundary; `scheduleHost` owns cancellation lifecycle.\n\t#idle(idle: IdleAPI, signal?: AbortSignal): Promise<void> {\n\t\treturn scheduleHost((complete) => {\n\t\t\tconst handle = idle.request(complete)\n\t\t\treturn () => idle.cancel(handle)\n\t\t}, signal)\n\t}\n\n\t// The browser timer boundary shared by `delay` and fallback `yield`.\n\t#sleep(ms: number, signal?: AbortSignal): Promise<void> {\n\t\treturn scheduleHost((complete) => {\n\t\t\tconst handle = setTimeout(complete, ms)\n\t\t\treturn () => clearTimeout(handle)\n\t\t}, signal)\n\t}\n\n\t#request(request: AnyFunction, callback: () => void): number {\n\t\treturn Number(Reflect.apply(request, globalThis, [callback]))\n\t}\n\n\t#cancel(cancel: AnyFunction, handle: number): void {\n\t\tReflect.apply(cancel, globalThis, [handle])\n\t}\n}\n","import type { SchedulerInterface } from '@src/core'\nimport { BrowserScheduler } from './BrowserScheduler.js'\nimport { FrameScheduler } from './FrameScheduler.js'\nimport { IdleScheduler } from './IdleScheduler.js'\n\n/**\n * Create the browser-native cooperative-yield {@link SchedulerInterface} — `yield()` uses\n * the Prioritized Task Scheduling API (`scheduler.postTask`) at the requested priority\n * when present, falling back to a `setTimeout(0)` macrotask; `delay(ms)` is a real\n * `setTimeout`.\n *\n * @remarks\n * The default browser scheduler: it honours `options.priority` (`user` /\n * `normal` / `background`) when `scheduler.postTask` is available and degrades to a plain\n * macrotask elsewhere. Both methods are abort-aware: pass `options.signal` and a pending\n * yield/delay rejects with the signal's exact `reason`; the shared owned-signal lifecycle\n * cancels the native handle without invoking caller listener methods. Prefer\n * {@link createFrameScheduler} for paint-aligned work or\n * {@link createIdleScheduler} for idle-time background work.\n *\n * @returns A {@link SchedulerInterface} backed by `scheduler.postTask` (or a macrotask)\n *\n * @example\n * ```ts\n * import { createBrowserScheduler } from '@orkestrel/workflow/browser'\n *\n * const scheduler = createBrowserScheduler()\n * await scheduler.yield({ priority: 'background' })\n * ```\n */\nexport function createBrowserScheduler(): SchedulerInterface {\n\treturn new BrowserScheduler()\n}\n\n/**\n * Create the frame-aligned cooperative-yield {@link SchedulerInterface} — `yield()` resumes\n * just before the next paint via `requestAnimationFrame`; `delay(ms)` is a real\n * `setTimeout`.\n *\n * @remarks\n * Use it for work that should batch per render frame (animation, incremental DOM updates)\n * and naturally pause while the tab is hidden. `yield` is abort-aware: pass `options.signal`\n * and a pending yield rejects with the signal's `reason` verbatim, cancelling the pending\n * frame request. `options.priority` is accepted but a no-op — a frame callback has no\n * priority dimension.\n *\n * @returns A {@link SchedulerInterface} backed by `requestAnimationFrame`\n *\n * @example\n * ```ts\n * import { createFrameScheduler } from '@orkestrel/workflow/browser'\n *\n * const scheduler = createFrameScheduler()\n * await scheduler.yield() // resumes before the next paint\n * ```\n */\nexport function createFrameScheduler(): SchedulerInterface {\n\treturn new FrameScheduler()\n}\n\n/**\n * Create the idle-time cooperative-yield {@link SchedulerInterface} — `yield()` resumes when\n * the host is idle via `requestIdleCallback` when present, falling back to a `setTimeout(0)`\n * macrotask; `delay(ms)` is a real `setTimeout`.\n *\n * @remarks\n * Use it for low-priority background work that must not contend with rendering or input.\n * Where `requestIdleCallback` is absent (Safari today) it degrades to a plain macrotask.\n * `yield` is abort-aware: pass `options.signal` and a pending yield rejects with the\n * signal's `reason` verbatim, cancelling the pending idle callback. `options.priority` is\n * accepted but a no-op — idle scheduling has no priority dimension.\n *\n * @returns A {@link SchedulerInterface} backed by `requestIdleCallback` (or a macrotask)\n *\n * @example\n * ```ts\n * import { createIdleScheduler } from '@orkestrel/workflow/browser'\n *\n * const scheduler = createIdleScheduler()\n * await scheduler.yield() // resumes when the host is idle\n * ```\n */\nexport function createIdleScheduler(): SchedulerInterface {\n\treturn new IdleScheduler()\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/browser/constants.ts","../../../src/browser/BrowserScheduler.ts","../../../src/browser/FrameScheduler.ts","../../../src/browser/IdleScheduler.ts","../../../src/browser/factories.ts"],"sourcesContent":["import type { SchedulerPriority } from '@src/core'\n\n/**\n * Maps each portable {@link SchedulerPriority} to the browser-native `postTask` priority —\n * `user` to `'user-blocking'`, `normal` to `'user-visible'`, and `background` to\n * `'background'`.\n *\n * @remarks\n * A `user` hint maps to the most urgent `'user-blocking'`, `normal` to the default\n * `'user-visible'`, and `background` to `'background'`. {@link BrowserScheduler} reads this\n * map to translate the caller's portable priority into the value passed to\n * `scheduler.postTask`, so the urgency hint is honoured by the host.\n */\nexport const POST_TASK_PRIORITY: Readonly<Record<SchedulerPriority, string>> = Object.freeze({\n\tuser: 'user-blocking',\n\tnormal: 'user-visible',\n\tbackground: 'background',\n})\n","import type { SchedulerInterface, SchedulerOptions, SchedulerPriority } from '@src/core'\nimport { delayHost, scheduleHost } from '@src/core'\nimport { isFunction, isPromise, isRecord } from '@orkestrel/contract'\nimport { POST_TASK_PRIORITY } from './constants.js'\n\n/**\n * Implements the browser {@link SchedulerInterface} — the browser-native cooperative-yield backend\n * built on the Prioritized Task Scheduling API (`scheduler.postTask`) at the mapped priority,\n * falling back to a zero-delay macrotask where it is absent, and rejecting an aborted wait\n * with the caller's own reason.\n *\n * @remarks\n * - **`yield` prefers `scheduler.postTask`, honouring priority.** When `globalThis`\n * exposes a `scheduler` with a `postTask` method, `yield()` posts a task at the mapped\n * priority (`user` → `'user-blocking'`, `normal` → `'user-visible'`, `background` →\n * `'background'`), so the host genuinely regains control and the urgency hint is\n * honoured. The capability is feature-detected through guards (`isRecord` / `isFunction`),\n * never an `as`. Where the API is absent (Firefox today, older engines),\n * it **falls back** to a `setTimeout(0)` macrotask — still a real host-turn, without\n * priority. `delay(ms)` is always a real `setTimeout`.\n * - **Abort fidelity is verbatim.** The shared `scheduleHost` lifecycle links an owned\n * settlement composite before scheduling, preserving the exact caller reason without\n * invoking caller-owned signal methods. The caller signal is not handed to `postTask`;\n * an internal controller cancels that native task. An unexpected native promise rejection\n * is routed back as the exact host failure instead of being discarded.\n * - **Event-free.** A pure functional primitive — no Emitter, no events.\n *\n * @example\n * ```ts\n * import { createAbort } from '@orkestrel/abort'\n * import { BrowserScheduler } from '@orkestrel/workflow/browser'\n *\n * const abort = createAbort()\n * const scheduler = new BrowserScheduler()\n * while (!abort.signal.aborted) {\n * \tdoSomeWork()\n * \tawait scheduler.yield({ priority: 'background', signal: abort.signal })\n * }\n * ```\n */\nexport class BrowserScheduler implements SchedulerInterface {\n\t/**\n\t * Yields control to the host through `scheduler.postTask` at the given priority (or a\n\t * `setTimeout(0)` macrotask where the API is absent), then resumes; abort rejects with\n\t * `signal.reason`.\n\t */\n\tyield(options?: SchedulerOptions): Promise<void> {\n\t\tconst post = this.#postTask()\n\t\tif (post === undefined) return delayHost(0, options?.signal)\n\t\treturn this.#yieldVia(post, options?.priority ?? 'normal', options?.signal)\n\t}\n\n\t/**\n\t * Resumes after at least `ms` milliseconds through `setTimeout`; abort rejects with\n\t * `signal.reason`.\n\t *\n\t * @remarks\n\t * Pass a non-negative finite `ms`. The primitive does no validation: it passes `ms`\n\t * straight to the host `setTimeout`, which clamps a negative value or `NaN` to ~0 — so an\n\t * out-of-domain `ms` resolves on the next host turn rather than throwing.\n\t */\n\tdelay(ms: number, options?: SchedulerOptions): Promise<void> {\n\t\treturn delayHost(ms, options?.signal)\n\t}\n\n\t// === Private\n\n\t// Feature-detect the Prioritized Task Scheduling API through guards (no `as`): the\n\t// global `scheduler` must be a record carrying a callable `postTask`. Returns the\n\t// narrowed `postTask` function, or `undefined` when the API is absent (the fallback).\n\t#postTask(): ((callback: () => void, options: Record<string, unknown>) => unknown) | undefined {\n\t\tconst candidate: unknown = Reflect.get(globalThis, 'scheduler')\n\t\tif (!isRecord(candidate)) return undefined\n\t\tconst post = candidate.postTask\n\t\tif (!isFunction(post)) return undefined\n\t\treturn (callback, options) => Reflect.apply(post, candidate, [callback, options])\n\t}\n\n\t// A `scheduler.postTask` boundary with an internal cancellation controller; `scheduleHost`\n\t// owns caller linking and first-settlement arbitration, including native promise failure.\n\t#yieldVia(\n\t\tpost: (callback: () => void, options: Record<string, unknown>) => unknown,\n\t\tpriority: SchedulerPriority,\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\treturn scheduleHost((complete, failure) => {\n\t\t\tconst internal = new AbortController()\n\t\t\tconst task = post(complete, {\n\t\t\t\tpriority: POST_TASK_PRIORITY[priority],\n\t\t\t\tsignal: internal.signal,\n\t\t\t})\n\t\t\tif (isPromise(task)) void task.catch(failure)\n\t\t\treturn () => internal.abort()\n\t\t}, signal)\n\t}\n}\n","import type { SchedulerInterface, SchedulerOptions } from '@src/core'\nimport { delayHost, scheduleHost } from '@src/core'\n\n/**\n * Implements the frame-aligned {@link SchedulerInterface} — a browser cooperative-yield backend\n * whose `yield` resumes before the next paint through `requestAnimationFrame`, rejecting an\n * aborted wait with the caller's own reason. A `priority` hint is accepted and does nothing.\n *\n * @remarks\n * - **`yield` resumes before the next paint.** `yield()` waits on `requestAnimationFrame`,\n * so the resumption is aligned to the browser's render loop — ideal for work that\n * batches per frame (animation, incremental DOM updates) and pauses while the tab is\n * hidden (the host throttles rAF). `delay(ms)` is a real `setTimeout`, unaligned to\n * frames. `options.priority` is accepted for contract compliance but a no-op — a frame\n * callback has no priority dimension.\n * - **Abort fidelity is verbatim, with cleanup.** The shared `scheduleHost` lifecycle links\n * an owned settlement composite before requesting a frame, never invokes caller-owned\n * signal methods, and cancels the native handle when abort wins.\n * - **Event-free.** A pure functional primitive — no Emitter, no events.\n *\n * @example\n * ```ts\n * import { createAbort } from '@orkestrel/abort'\n * import { FrameScheduler } from '@orkestrel/workflow/browser'\n *\n * const abort = createAbort()\n * const scheduler = new FrameScheduler()\n * while (!abort.signal.aborted) {\n * \trenderOneFrameOfWork()\n * \tawait scheduler.yield({ signal: abort.signal }) // resume before the next paint\n * }\n * ```\n */\nexport class FrameScheduler implements SchedulerInterface {\n\t/**\n\t * Yields control to the host until the moment before the next paint through\n\t * `requestAnimationFrame`, then resumes; abort rejects with `signal.reason`.\n\t */\n\tyield(options?: SchedulerOptions): Promise<void> {\n\t\treturn this.#frame(options?.signal)\n\t}\n\n\t/**\n\t * Resumes after at least `ms` milliseconds through `setTimeout`; abort rejects with\n\t * `signal.reason`.\n\t *\n\t * @remarks\n\t * Pass a non-negative finite `ms`. The primitive does no validation: it passes `ms`\n\t * straight to the host `setTimeout`, which clamps a negative value or `NaN` to ~0 — so an\n\t * out-of-domain `ms` resolves on the next host turn rather than throwing.\n\t */\n\tdelay(ms: number, options?: SchedulerOptions): Promise<void> {\n\t\treturn delayHost(ms, options?.signal)\n\t}\n\n\t// === Private\n\n\t// The frame request boundary; `scheduleHost` owns cancellation lifecycle.\n\t#frame(signal?: AbortSignal): Promise<void> {\n\t\treturn scheduleHost((complete) => {\n\t\t\tconst handle = requestAnimationFrame(complete)\n\t\t\treturn () => cancelAnimationFrame(handle)\n\t\t}, signal)\n\t}\n}\n","import type { SchedulerInterface, SchedulerOptions } from '@src/core'\nimport type { AnyFunction } from '@orkestrel/contract'\nimport type { IdleInterface } from './types.js'\nimport { delayHost, scheduleHost } from '@src/core'\nimport { isFunction } from '@orkestrel/contract'\n\n/**\n * Implements the idle-time {@link SchedulerInterface} — a browser cooperative-yield backend whose\n * `yield` resumes when the host is idle through `requestIdleCallback`, falling back to a\n * zero-delay macrotask where it is absent, and rejecting an aborted wait with the caller's own\n * reason. A `priority` hint is accepted and does nothing.\n *\n * @remarks\n * - **`yield` resumes during idle time.** When `globalThis` exposes `requestIdleCallback`,\n * `yield()` waits on it, so the resumption happens when the browser has spare time after\n * rendering and input — ideal for low-priority background work that must not contend with\n * the user. The capability is feature-detected through a guard (`isFunction`), never an\n * `as`. Where the API is absent (Safari today), it **falls back** to a\n * `setTimeout(0)` macrotask — still a real host-turn, not idle-gated. `delay(ms)` is\n * always a real `setTimeout`. `options.priority` is accepted for contract compliance but a\n * no-op — idle scheduling has no priority dimension.\n * - **Abort fidelity is verbatim, with cleanup.** The shared `scheduleHost` lifecycle links\n * an owned settlement composite before scheduling, never invokes caller-owned signal\n * methods, and cancels the idle callback or fallback timer when abort wins.\n * - **Event-free.** A pure functional primitive — no Emitter, no events.\n *\n * @example\n * ```ts\n * import { createAbort } from '@orkestrel/abort'\n * import { IdleScheduler } from '@orkestrel/workflow/browser'\n *\n * const abort = createAbort()\n * const scheduler = new IdleScheduler()\n * while (!abort.signal.aborted) {\n * \tdoLowPriorityWork()\n * \tawait scheduler.yield({ signal: abort.signal }) // resume when the host is idle\n * }\n * ```\n */\nexport class IdleScheduler implements SchedulerInterface {\n\t/**\n\t * Yields control to the host until it is idle through `requestIdleCallback` (or a\n\t * `setTimeout(0)` macrotask where the API is absent), then resumes; abort rejects with\n\t * `signal.reason`.\n\t */\n\tyield(options?: SchedulerOptions): Promise<void> {\n\t\tconst idle = this.#idleCallback()\n\t\tif (idle === undefined) return delayHost(0, options?.signal)\n\t\treturn this.#idle(idle, options?.signal)\n\t}\n\n\t/**\n\t * Resumes after at least `ms` milliseconds through `setTimeout`; abort rejects with\n\t * `signal.reason`.\n\t *\n\t * @remarks\n\t * Pass a non-negative finite `ms`. The primitive does no validation: it passes `ms`\n\t * straight to the host `setTimeout`, which clamps a negative value or `NaN` to ~0 — so an\n\t * out-of-domain `ms` resolves on the next host turn rather than throwing.\n\t */\n\tdelay(ms: number, options?: SchedulerOptions): Promise<void> {\n\t\treturn delayHost(ms, options?.signal)\n\t}\n\n\t// === Private\n\n\t// Feature-detect `requestIdleCallback` / `cancelIdleCallback` off `globalThis` through\n\t// guards (no `as`): both must be callable. Returns the narrowed pair, or `undefined`\n\t// when the API is absent (the macrotask fallback).\n\t#idleCallback(): IdleInterface | undefined {\n\t\tconst request: unknown = Reflect.get(globalThis, 'requestIdleCallback')\n\t\tconst cancel: unknown = Reflect.get(globalThis, 'cancelIdleCallback')\n\t\tif (!isFunction(request) || !isFunction(cancel)) return undefined\n\t\treturn {\n\t\t\trequest: this.#request.bind(this, request),\n\t\t\tcancel: this.#cancel.bind(this, cancel),\n\t\t}\n\t}\n\n\t// The idle yield boundary; `scheduleHost` owns cancellation lifecycle.\n\t#idle(idle: IdleInterface, signal?: AbortSignal): Promise<void> {\n\t\treturn scheduleHost((complete) => {\n\t\t\tconst handle = idle.request(complete)\n\t\t\treturn () => idle.cancel(handle)\n\t\t}, signal)\n\t}\n\n\t#request(request: AnyFunction, callback: () => void): number {\n\t\treturn Number(Reflect.apply(request, globalThis, [callback]))\n\t}\n\n\t#cancel(cancel: AnyFunction, handle: number): void {\n\t\tReflect.apply(cancel, globalThis, [handle])\n\t}\n}\n","import type { SchedulerInterface } from '@src/core'\nimport { BrowserScheduler } from './BrowserScheduler.js'\nimport { FrameScheduler } from './FrameScheduler.js'\nimport { IdleScheduler } from './IdleScheduler.js'\n\n/**\n * Creates the browser-native cooperative-yield {@link SchedulerInterface} — `yield()` uses\n * the Prioritized Task Scheduling API (`scheduler.postTask`) at the requested priority\n * when present, falling back to a `setTimeout(0)` macrotask; `delay(ms)` is a real\n * `setTimeout`.\n *\n * @remarks\n * The default browser scheduler: it honours `options.priority` (`user` /\n * `normal` / `background`) when `scheduler.postTask` is available and degrades to a plain\n * macrotask elsewhere. Both methods are abort-aware: pass `options.signal` and a pending\n * yield/delay rejects with the signal's exact `reason`; the shared owned-signal lifecycle\n * cancels the native handle without invoking caller listener methods. Prefer\n * {@link createFrameScheduler} for paint-aligned work or\n * {@link createIdleScheduler} for idle-time background work.\n *\n * @returns A {@link SchedulerInterface} backed by `scheduler.postTask` (or a macrotask)\n *\n * @example\n * ```ts\n * import { createBrowserScheduler } from '@orkestrel/workflow/browser'\n *\n * const scheduler = createBrowserScheduler()\n * await scheduler.yield({ priority: 'background' })\n * ```\n */\nexport function createBrowserScheduler(): SchedulerInterface {\n\treturn new BrowserScheduler()\n}\n\n/**\n * Creates the frame-aligned cooperative-yield {@link SchedulerInterface} — `yield()` resumes\n * before the next paint through `requestAnimationFrame`; `delay(ms)` is a real\n * `setTimeout`.\n *\n * @remarks\n * Use it for work that batches per render frame (animation, incremental DOM updates) and\n * naturally pauses while the tab is hidden. `yield` is abort-aware: pass `options.signal`\n * and a pending yield rejects with the signal's `reason` verbatim, cancelling the pending\n * frame request. `options.priority` is accepted but a no-op — a frame callback has no\n * priority dimension.\n *\n * @returns A {@link SchedulerInterface} backed by `requestAnimationFrame`\n *\n * @example\n * ```ts\n * import { createFrameScheduler } from '@orkestrel/workflow/browser'\n *\n * const scheduler = createFrameScheduler()\n * await scheduler.yield() // resumes before the next paint\n * ```\n */\nexport function createFrameScheduler(): SchedulerInterface {\n\treturn new FrameScheduler()\n}\n\n/**\n * Creates the idle-time cooperative-yield {@link SchedulerInterface} — `yield()` resumes when\n * the host is idle through `requestIdleCallback` when present, falling back to a `setTimeout(0)`\n * macrotask; `delay(ms)` is a real `setTimeout`.\n *\n * @remarks\n * Use it for low-priority background work that must not contend with rendering or input.\n * Where `requestIdleCallback` is absent (Safari today) it degrades to a plain macrotask.\n * `yield` is abort-aware: pass `options.signal` and a pending yield rejects with the\n * signal's `reason` verbatim, cancelling the pending idle callback. `options.priority` is\n * accepted but a no-op — idle scheduling has no priority dimension.\n *\n * @returns A {@link SchedulerInterface} backed by `requestIdleCallback` (or a macrotask)\n *\n * @example\n * ```ts\n * import { createIdleScheduler } from '@orkestrel/workflow/browser'\n *\n * const scheduler = createIdleScheduler()\n * await scheduler.yield() // resumes when the host is idle\n * ```\n */\nexport function createIdleScheduler(): SchedulerInterface {\n\treturn new IdleScheduler()\n}\n"],"mappings":"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"}
|