@zingage/scheduling 1.0.0 → 1.1.0

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 CHANGED
@@ -29,7 +29,11 @@ The package measures elapsed time around synchronous callbacks and result accumu
29
29
 
30
30
  If A uses 1 ms and finishes, connected B gets the remaining 3 ms. If A exhausts the budget, A replenishes it before yielding. B can then use the new 4 ms while A waits. A does not replenish the budget when it resumes. Unrelated C uses a separate budget and cannot consume A/B's allowance.
31
31
 
32
- One callback or iterator step cannot be interrupted. Nested helpers started inside a synchronous callback cannot interrupt that containing callback. Their work counts once toward a shared budget. Garbage collection and operating-system pauses inside a measured step count as elapsed processing time. This is not CPU-time accounting or a hard event-loop delay bound. Work outside the helpers does not count. Ordinary callbacks must be synchronous. `timeSlicedMap` and `timeSlicedForEach` also accept generator callbacks. They do not accept async functions.
32
+ One callback or iterator step cannot be interrupted. Nested helpers started inside a synchronous callback cannot interrupt that containing callback. Their work counts once toward a shared budget. Garbage collection and operating-system pauses inside a measured step count as elapsed processing time. This is not CPU-time accounting or a hard event-loop delay bound. Work outside the helpers does not count. `timeSlicedMap` and `timeSlicedForEach` also accept generator callbacks.
33
+
34
+ ## Synchronous callbacks
35
+
36
+ Ordinary callbacks, predicates, key functions, and `measure` work must be synchronous. The types accept any result, including generic results such as `(item) => item`. TypeScript cannot reject a promise result without also rejecting generic results. So the rule is checked at runtime: if a non-generator callback returns a promise or other thenable (an object or function with a `then` method), the helper throws a `TypeError` before it uses the value. An `async` callback compiles, and throws on its first call. For async work, use a generator callback with `yield* waitFor(...)`.
33
37
 
34
38
  The constructor is `new TimeSliceBudget(sliceMs = DEFAULT_TIME_SLICE_MS, now = performance.now, yieldToEventLoop = setImmediate)`. `sliceMs` must be positive and finite. `now` must be a monotonic clock in milliseconds. The budget binds the clock to `performance` because Node's default clock requires that receiver. The default yield function is Node's promise-based `setImmediate`. A custom yield function must let the event loop advance; `Promise.resolve()` does not provide that behavior. Optionally pass a budget directly as each helper's last argument to share its allowance.
35
39
 
@@ -41,6 +45,8 @@ The constructor is `new TimeSliceBudget(sliceMs = DEFAULT_TIME_SLICE_MS, now = p
41
45
  | `timeSlicedMap` | Returns transformed values in input order. |
42
46
  | `timeSlicedFlatMap` | Flattens each callback's list incrementally, including large lists. |
43
47
  | `timeSlicedFilter` | Returns matching items in input order. |
48
+ | `timeSlicedSome` | Returns `true` at the first match; stops there. |
49
+ | `timeSlicedFind` | Returns the first match, or `undefined`; stops there. |
44
50
  | `timeSlicedGroupBy` | Returns a `Map` with first-seen key order and input order within each group. |
45
51
  | `timeSlicedSort` | Returns a new stable sorted array with a synchronous comparator. |
46
52
  | `timeSlicedMapFromEntries` | Builds a `Map`; the last value wins for repeated keys. |
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { waitFor, timeSlicedFilter, timeSlicedFlatMap, timeSlicedForEach, timeSlicedGroupBy, timeSlicedMap, timeSlicedMapFromEntries, TimeSliceBudget, } from "./time-slicing.js";
2
2
  export { timeSlicedSort } from "./time-sliced-sort.js";
3
+ export { timeSlicedFind, timeSlicedSome } from "./time-sliced-search.js";
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,wBAAwB,EACxB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,wBAAwB,EACxB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { waitFor, timeSlicedFilter, timeSlicedFlatMap, timeSlicedForEach, timeSlicedGroupBy, timeSlicedMap, timeSlicedMapFromEntries, TimeSliceBudget, } from "./time-slicing.js";
2
2
  export { timeSlicedSort } from "./time-sliced-sort.js";
3
+ export { timeSlicedFind, timeSlicedSome } from "./time-sliced-search.js";
3
4
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,wBAAwB,EACxB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,aAAa,EACb,wBAAwB,EACxB,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC"}
@@ -0,0 +1,10 @@
1
+ export declare const GENERATOR_HINT = "For async work, use a generator callback with `yield* waitFor(...)`.";
2
+ export declare const TRAVERSAL_HINT = "For async work, use a generator callback with `yield* waitFor(...)` in timeSlicedMap or timeSlicedForEach first.";
3
+ /**
4
+ * Returns `value` unchanged, or throws a `TypeError` if it is a thenable.
5
+ *
6
+ * TypeScript cannot reject a promise result without also rejecting generic
7
+ * results, so the synchronous callback rule is checked here at runtime.
8
+ */
9
+ export declare function assertNotThenable<Value>(value: Value, helper: string, hint: string): Value;
10
+ //# sourceMappingURL=synchronous-result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synchronous-result.d.ts","sourceRoot":"","sources":["../src/synchronous-result.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,cAAc,yEAC6C,CAAC;AACzE,eAAO,MAAM,cAAc,qHACyF,CAAC;AAErH;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EACrC,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,GACX,KAAK,CAYP"}
@@ -0,0 +1,18 @@
1
+ export const GENERATOR_HINT = "For async work, use a generator callback with `yield* waitFor(...)`.";
2
+ export const TRAVERSAL_HINT = "For async work, use a generator callback with `yield* waitFor(...)` in timeSlicedMap or timeSlicedForEach first.";
3
+ /**
4
+ * Returns `value` unchanged, or throws a `TypeError` if it is a thenable.
5
+ *
6
+ * TypeScript cannot reject a promise result without also rejecting generic
7
+ * results, so the synchronous callback rule is checked here at runtime.
8
+ */
9
+ export function assertNotThenable(value, helper, hint) {
10
+ if (((typeof value === "object" && value !== null) ||
11
+ typeof value === "function") &&
12
+ "then" in value &&
13
+ typeof value.then === "function") {
14
+ throw new TypeError(`${helper} received a promise or other thenable from a synchronous callback. ${hint}`);
15
+ }
16
+ return value;
17
+ }
18
+ //# sourceMappingURL=synchronous-result.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"synchronous-result.js","sourceRoot":"","sources":["../src/synchronous-result.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,cAAc,GACzB,sEAAsE,CAAC;AACzE,MAAM,CAAC,MAAM,cAAc,GACzB,kHAAkH,CAAC;AAErH;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAY,EACZ,MAAc,EACd,IAAY;IAEZ,IACE,CAAC,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;QAC5C,OAAO,KAAK,KAAK,UAAU,CAAC;QAC9B,MAAM,IAAI,KAAK;QACf,OAAO,KAAK,CAAC,IAAI,KAAK,UAAU,EAChC,CAAC;QACD,MAAM,IAAI,SAAS,CACjB,GAAG,MAAM,sEAAsE,IAAI,EAAE,CACtF,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,47 @@
1
+ import { TimeSliceBudget } from "./time-slicing.js";
2
+ /**
3
+ * Returns whether any item matches a synchronous predicate, in input order.
4
+ *
5
+ * Stops at the first match. It does not advance the iterable past that item,
6
+ * and it builds no intermediate array. Measures each predicate call, including
7
+ * calls that throw. One predicate call cannot be interrupted. Throws a
8
+ * `TypeError` if the predicate returns a promise or other thenable.
9
+ * The optional budget defaults to a new independent `TimeSliceBudget`.
10
+ *
11
+ * @example Check for a negative number.
12
+ * const hasNegative = await timeSlicedSome([3, -1, 2], (value) => value < 0);
13
+ *
14
+ * @example Share a budget with other work, and stop early on a lazy source.
15
+ * const budget = new TimeSliceBudget();
16
+ * const found = await timeSlicedSome(map.values(), (entry) => entry.active, budget);
17
+ */
18
+ export declare function timeSlicedSome<Item>(items: Iterable<Item>, predicate: (item: Item, index: number) => boolean, budget?: TimeSliceBudget): Promise<boolean>;
19
+ /**
20
+ * Returns the first item that matches a type-guard predicate, narrowed.
21
+ *
22
+ * @example Find the first string.
23
+ * const text = await timeSlicedFind(
24
+ * [1, "a", 2],
25
+ * (value): value is string => typeof value === "string",
26
+ * );
27
+ */
28
+ export declare function timeSlicedFind<Item, Found extends Item>(items: Iterable<Item>, predicate: (item: Item, index: number) => item is Found, budget?: TimeSliceBudget): Promise<Found | undefined>;
29
+ /**
30
+ * Returns the first item that matches a synchronous predicate, or `undefined`.
31
+ *
32
+ * Stops at the first match. It does not advance the iterable past that item,
33
+ * and it builds no intermediate array. Measures each predicate call, including
34
+ * calls that throw. One predicate call cannot be interrupted. Throws a
35
+ * `TypeError` if the predicate returns a promise or other thenable. As with
36
+ * `Array.prototype.find`, a matching `undefined` item also returns `undefined`.
37
+ * The optional budget defaults to a new independent `TimeSliceBudget`.
38
+ *
39
+ * @example Find the first even number.
40
+ * const even = await timeSlicedFind([1, 4, 6], (value) => value % 2 === 0);
41
+ *
42
+ * @example Use the index and a shared budget.
43
+ * const budget = new TimeSliceBudget();
44
+ * const late = await timeSlicedFind(["a", "b"], (_, index) => index > 0, budget);
45
+ */
46
+ export declare function timeSlicedFind<Item>(items: Iterable<Item>, predicate: (item: Item, index: number) => boolean, budget?: TimeSliceBudget): Promise<Item | undefined>;
47
+ //# sourceMappingURL=time-sliced-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-sliced-search.d.ts","sourceRoot":"","sources":["../src/time-sliced-search.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AA0BpD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,cAAc,CAAC,IAAI,EACvC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EACrB,SAAS,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,EACjD,MAAM,kBAAwB,GAC7B,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,KAAK,SAAS,IAAI,EACrD,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EACrB,SAAS,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,IAAI,KAAK,EACvD,MAAM,CAAC,EAAE,eAAe,GACvB,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;AAC9B;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,IAAI,EACjC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EACrB,SAAS,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,EACjD,MAAM,CAAC,EAAE,eAAe,GACvB,OAAO,CAAC,IAAI,GAAG,SAAS,CAAC,CAAC"}
@@ -0,0 +1,41 @@
1
+ import { TRAVERSAL_HINT, assertNotThenable } from "./synchronous-result.js";
2
+ import { TimeSliceBudget } from "./time-slicing.js";
3
+ async function findFirstMatch(helper, items, predicate, budget) {
4
+ const position = { index: 0 };
5
+ // oxlint-disable-next-line zingage/no-restricted-syntax -- Stop at the first match; native iteration closes the iterator without advancing it.
6
+ for (const item of items) {
7
+ const matched = budget.measure(() => assertNotThenable(predicate(item, position.index), helper, TRAVERSAL_HINT));
8
+ position.index += 1;
9
+ const pending = budget.yieldIfNeeded();
10
+ if (pending !== undefined)
11
+ await pending;
12
+ if (matched)
13
+ return { item };
14
+ }
15
+ return undefined;
16
+ }
17
+ /**
18
+ * Returns whether any item matches a synchronous predicate, in input order.
19
+ *
20
+ * Stops at the first match. It does not advance the iterable past that item,
21
+ * and it builds no intermediate array. Measures each predicate call, including
22
+ * calls that throw. One predicate call cannot be interrupted. Throws a
23
+ * `TypeError` if the predicate returns a promise or other thenable.
24
+ * The optional budget defaults to a new independent `TimeSliceBudget`.
25
+ *
26
+ * @example Check for a negative number.
27
+ * const hasNegative = await timeSlicedSome([3, -1, 2], (value) => value < 0);
28
+ *
29
+ * @example Share a budget with other work, and stop early on a lazy source.
30
+ * const budget = new TimeSliceBudget();
31
+ * const found = await timeSlicedSome(map.values(), (entry) => entry.active, budget);
32
+ */
33
+ export async function timeSlicedSome(items, predicate, budget = new TimeSliceBudget()) {
34
+ return ((await findFirstMatch("timeSlicedSome", items, predicate, budget)) !==
35
+ undefined);
36
+ }
37
+ export async function timeSlicedFind(items, predicate, budget = new TimeSliceBudget()) {
38
+ return (await findFirstMatch("timeSlicedFind", items, predicate, budget))
39
+ ?.item;
40
+ }
41
+ //# sourceMappingURL=time-sliced-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"time-sliced-search.js","sourceRoot":"","sources":["../src/time-sliced-search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,KAAK,UAAU,cAAc,CAC3B,MAAc,EACd,KAAqB,EACrB,SAAiD,EACjD,MAAuB;IAEvB,MAAM,QAAQ,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;IAC9B,+IAA+I;IAC/I,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAClC,iBAAiB,CACf,SAAS,CAAC,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,EAC/B,MAAM,EACN,cAAc,CACf,CACF,CAAC;QACF,QAAQ,CAAC,KAAK,IAAI,CAAC,CAAC;QACpB,MAAM,OAAO,GAAG,MAAM,CAAC,aAAa,EAAE,CAAC;QACvC,IAAI,OAAO,KAAK,SAAS;YAAE,MAAM,OAAO,CAAC;QACzC,IAAI,OAAO;YAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC/B,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAqB,EACrB,SAAiD,EACjD,MAAM,GAAG,IAAI,eAAe,EAAE;IAE9B,OAAO,CACL,CAAC,MAAM,cAAc,CAAC,gBAAgB,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QAClE,SAAS,CACV,CAAC;AACJ,CAAC;AAsCD,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,KAAqB,EACrB,SAAiD,EACjD,MAAM,GAAG,IAAI,eAAe,EAAE;IAE9B,OAAO,CAAC,MAAM,cAAc,CAAC,gBAAgB,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QACvE,EAAE,IAAI,CAAC;AACX,CAAC"}
@@ -1,4 +1,3 @@
1
- type Synchronous<Result> = Result extends PromiseLike<unknown> ? never : Result;
2
1
  /**
3
2
  * Tracks measured synchronous work before yielding to the event loop.
4
3
  *
@@ -29,6 +28,8 @@ export declare class TimeSliceBudget {
29
28
  *
30
29
  * Nested measurements on this budget count once. This method does not yield.
31
30
  * Call `yieldIfNeeded` at a safe boundary after handling the result.
31
+ * `work` must be synchronous. If it returns a promise or other thenable, this
32
+ * method throws a `TypeError` after charging the elapsed time.
32
33
  *
33
34
  * @example Measure one segment and then check the budget.
34
35
  * const budget = new TimeSliceBudget();
@@ -36,7 +37,7 @@ export declare class TimeSliceBudget {
36
37
  * const pending = budget.yieldIfNeeded();
37
38
  * if (pending !== undefined) await pending;
38
39
  */
39
- measure<Result>(work: () => Result & Synchronous<Result>): Result;
40
+ measure<Result>(work: () => Result): Result;
40
41
  /**
41
42
  * Returns a yield promise if the allowance is exhausted, or `undefined` otherwise.
42
43
  *
@@ -81,6 +82,9 @@ export declare function waitFor<Value>(promise: PromiseLike<Value>): CallbackGen
81
82
  * finishes before the next item starts. Measures synchronous callback work and
82
83
  * every generator `next()`/`throw()` segment, excluding promise waits.
83
84
  * Iterator advancement is unmeasured. Unhandled failures stop the traversal.
85
+ * A non-generator callback must be synchronous. If it returns a promise or
86
+ * other thenable (an `async` function, for example), the traversal throws a
87
+ * `TypeError` on that first call. The type checker does not catch this.
84
88
  * The optional budget defaults to a new independent `TimeSliceBudget`.
85
89
  *
86
90
  * @example Run a synchronous visitor.
@@ -96,17 +100,21 @@ export declare function timeSlicedForEach<Item, Result>(items: Iterable<Item>, c
96
100
  /**
97
101
  * Runs a synchronous visitor in input order with an optional processing budget.
98
102
  *
103
+ * Throws a `TypeError` if the visitor returns a promise or other thenable.
104
+ *
99
105
  * @example
100
106
  * await timeSlicedForEach([1, 2], (value) => console.log(value));
101
107
  */
102
- export declare function timeSlicedForEach<Item, Result>(items: Iterable<Item>, callback: (item: Item, index: number) => Result & Synchronous<Result>, budget?: TimeSliceBudget): Promise<void>;
108
+ export declare function timeSlicedForEach<Item, Result>(items: Iterable<Item>, callback: (item: Item, index: number) => Result, budget?: TimeSliceBudget): Promise<void>;
103
109
  /**
104
110
  * Returns transformed values in input order using sequential callbacks.
105
111
  *
106
112
  * Accepts synchronous callbacks or generators that yield promises. Each generator
107
113
  * finishes before the next item starts. Measures every synchronous segment,
108
114
  * including `throw()` recovery and result accumulation. Promise waits and iterator
109
- * advancement are unmeasured. Ordinary async callbacks are not supported.
115
+ * advancement are unmeasured. Ordinary async callbacks are not supported: if a
116
+ * non-generator callback returns a promise or other thenable, the traversal
117
+ * throws a `TypeError` on that first call. The type checker does not catch this.
110
118
  * The optional budget defaults to a new independent `TimeSliceBudget`.
111
119
  *
112
120
  * @example Transform synchronous values.
@@ -122,10 +130,12 @@ export declare function timeSlicedMap<Item, Result>(items: Iterable<Item>, callb
122
130
  /**
123
131
  * Maps synchronous values in input order with an optional processing budget.
124
132
  *
133
+ * Throws a `TypeError` if the callback returns a promise or other thenable.
134
+ *
125
135
  * @example
126
136
  * const doubled = await timeSlicedMap([1, 2], (value) => value * 2);
127
137
  */
128
- export declare function timeSlicedMap<Item, Result>(items: Iterable<Item>, callback: (item: Item, index: number) => Result & Synchronous<Result>, budget?: TimeSliceBudget): Promise<Result[]>;
138
+ export declare function timeSlicedMap<Item, Result>(items: Iterable<Item>, callback: (item: Item, index: number) => Result, budget?: TimeSliceBudget): Promise<Result[]>;
129
139
  /**
130
140
  * Maps each item to a synchronous list and flattens the lists in input order.
131
141
  *
@@ -149,7 +159,8 @@ export declare function timeSlicedFlatMap<Item, Result>(items: Iterable<Item>, c
149
159
  * Returns items whose synchronous predicate is true, preserving input order.
150
160
  *
151
161
  * Measures predicate work and result accumulation. One predicate call cannot be
152
- * interrupted. The optional budget defaults to a new independent instance.
162
+ * interrupted. Throws a `TypeError` if the predicate returns a promise or other
163
+ * thenable. The optional budget defaults to a new independent instance.
153
164
  *
154
165
  * @example Keep odd numbers.
155
166
  * const odd = await timeSlicedFilter([1, 2, 3], (value) => value % 2 === 1);
@@ -165,6 +176,8 @@ export declare function timeSlicedFilter<Item>(items: Iterable<Item>, predicate:
165
176
  * Groups items by a synchronous key, preserving key and group insertion order.
166
177
  *
167
178
  * Uses `Map` key equality. Measures key selection and group accumulation.
179
+ * `keyOf` must be synchronous. If it returns a promise or other thenable, the
180
+ * operation throws a `TypeError` before it stores the key.
168
181
  * The optional budget defaults to a new independent `TimeSliceBudget`.
169
182
  *
170
183
  * @example Group strings by length.
@@ -176,7 +189,7 @@ export declare function timeSlicedFilter<Item>(items: Iterable<Item>, predicate:
176
189
  * return yield* waitFor(timeSlicedGroupBy(values, (value) => value % 2, budget));
177
190
  * }, budget);
178
191
  */
179
- export declare function timeSlicedGroupBy<Item, Key>(items: Iterable<Item>, keyOf: (item: Item, index: number) => Key & Synchronous<Key>, budget?: TimeSliceBudget): Promise<Map<Key, Item[]>>;
192
+ export declare function timeSlicedGroupBy<Item, Key>(items: Iterable<Item>, keyOf: (item: Item, index: number) => Key, budget?: TimeSliceBudget): Promise<Map<Key, Item[]>>;
180
193
  /**
181
194
  * Builds a Map from entries in input order; the last value for a repeated key wins.
182
195
  *
@@ -1 +1 @@
1
- {"version":3,"file":"time-slicing.d.ts","sourceRoot":"","sources":["../src/time-slicing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"time-slicing.d.ts","sourceRoot":"","sources":["../src/time-slicing.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { setImmediate } from "node:timers/promises";
3
+ import { GENERATOR_HINT, TRAVERSAL_HINT, assertNotThenable, } from "./synchronous-result.js";
3
4
  const DEFAULT_TIME_SLICE_MS = 4;
4
5
  const performanceNow = performance.now.bind(performance);
5
6
  /**
@@ -42,6 +43,8 @@ export class TimeSliceBudget {
42
43
  *
43
44
  * Nested measurements on this budget count once. This method does not yield.
44
45
  * Call `yieldIfNeeded` at a safe boundary after handling the result.
46
+ * `work` must be synchronous. If it returns a promise or other thenable, this
47
+ * method throws a `TypeError` after charging the elapsed time.
45
48
  *
46
49
  * @example Measure one segment and then check the budget.
47
50
  * const budget = new TimeSliceBudget();
@@ -54,7 +57,7 @@ export class TimeSliceBudget {
54
57
  const startedAt = outermost ? this.#now() : 0;
55
58
  this.#depth += 1;
56
59
  try {
57
- return work();
60
+ return assertNotThenable(work(), "TimeSliceBudget.measure", GENERATOR_HINT);
58
61
  }
59
62
  finally {
60
63
  this.#depth -= 1;
@@ -159,11 +162,11 @@ async function runCallbackGenerator(generator, budget) {
159
162
  }
160
163
  return state.step.value;
161
164
  }
162
- async function traverse(items, callback, accept, budget) {
165
+ async function traverse(helper, items, callback, accept, budget) {
163
166
  const position = { index: 0 };
164
167
  // oxlint-disable-next-line zingage/no-restricted-syntax -- Native iteration owns cleanup while callbacks and budget yields settle sequentially.
165
168
  for (const item of items) {
166
- const value = budget.measure(() => callback(item, position.index));
169
+ const value = budget.measure(() => assertNotThenable(callback(item, position.index), helper, GENERATOR_HINT));
167
170
  const result = isCallbackGenerator(value)
168
171
  ? await runCallbackGenerator(value, budget)
169
172
  : value;
@@ -175,11 +178,11 @@ async function traverse(items, callback, accept, budget) {
175
178
  }
176
179
  }
177
180
  export async function timeSlicedForEach(items, callback, budget = new TimeSliceBudget()) {
178
- await traverse(items, callback, () => { }, budget);
181
+ await traverse("timeSlicedForEach", items, callback, () => { }, budget);
179
182
  }
180
183
  export async function timeSlicedMap(items, callback, budget = new TimeSliceBudget()) {
181
184
  const results = [];
182
- await traverse(items, callback, (result) => {
185
+ await traverse("timeSlicedMap", items, callback, (result) => {
183
186
  // oxlint-disable-next-line zingage/no-restricted-syntax -- Append to the local result to avoid quadratic copying.
184
187
  results.push(result);
185
188
  }, budget);
@@ -227,7 +230,8 @@ export async function timeSlicedFlatMap(items, callback, budget = new TimeSliceB
227
230
  * Returns items whose synchronous predicate is true, preserving input order.
228
231
  *
229
232
  * Measures predicate work and result accumulation. One predicate call cannot be
230
- * interrupted. The optional budget defaults to a new independent instance.
233
+ * interrupted. Throws a `TypeError` if the predicate returns a promise or other
234
+ * thenable. The optional budget defaults to a new independent instance.
231
235
  *
232
236
  * @example Keep odd numbers.
233
237
  * const odd = await timeSlicedFilter([1, 2, 3], (value) => value % 2 === 1);
@@ -241,7 +245,7 @@ export async function timeSlicedFlatMap(items, callback, budget = new TimeSliceB
241
245
  export async function timeSlicedFilter(items, predicate, budget = new TimeSliceBudget()) {
242
246
  const results = [];
243
247
  await timeSlicedForEach(items, (item, index) => {
244
- if (predicate(item, index)) {
248
+ if (assertNotThenable(predicate(item, index), "timeSlicedFilter", TRAVERSAL_HINT)) {
245
249
  // oxlint-disable-next-line zingage/no-restricted-syntax -- Append to the local result without quadratic copying.
246
250
  results.push(item);
247
251
  }
@@ -252,6 +256,8 @@ export async function timeSlicedFilter(items, predicate, budget = new TimeSliceB
252
256
  * Groups items by a synchronous key, preserving key and group insertion order.
253
257
  *
254
258
  * Uses `Map` key equality. Measures key selection and group accumulation.
259
+ * `keyOf` must be synchronous. If it returns a promise or other thenable, the
260
+ * operation throws a `TypeError` before it stores the key.
255
261
  * The optional budget defaults to a new independent `TimeSliceBudget`.
256
262
  *
257
263
  * @example Group strings by length.
@@ -266,7 +272,7 @@ export async function timeSlicedFilter(items, predicate, budget = new TimeSliceB
266
272
  export async function timeSlicedGroupBy(items, keyOf, budget = new TimeSliceBudget()) {
267
273
  const groups = new Map();
268
274
  await timeSlicedForEach(items, (item, index) => {
269
- const key = keyOf(item, index);
275
+ const key = assertNotThenable(keyOf(item, index), "timeSlicedGroupBy", TRAVERSAL_HINT);
270
276
  const group = groups.get(key);
271
277
  if (group === undefined)
272
278
  groups.set(key, [item]);
@@ -1 +1 @@
1
- {"version":3,"file":"time-slicing.js","sourceRoot":"","sources":["../src/time-slicing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"time-slicing.js","sourceRoot":"","sources":["../src/time-slicing.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zingage/scheduling",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/joinava/sensitive-packages.git",
package/src/index.ts CHANGED
@@ -9,3 +9,4 @@ export {
9
9
  TimeSliceBudget,
10
10
  } from "./time-slicing.js";
11
11
  export { timeSlicedSort } from "./time-sliced-sort.js";
12
+ export { timeSlicedFind, timeSlicedSome } from "./time-sliced-search.js";
@@ -0,0 +1,28 @@
1
+ export const GENERATOR_HINT =
2
+ "For async work, use a generator callback with `yield* waitFor(...)`.";
3
+ export const TRAVERSAL_HINT =
4
+ "For async work, use a generator callback with `yield* waitFor(...)` in timeSlicedMap or timeSlicedForEach first.";
5
+
6
+ /**
7
+ * Returns `value` unchanged, or throws a `TypeError` if it is a thenable.
8
+ *
9
+ * TypeScript cannot reject a promise result without also rejecting generic
10
+ * results, so the synchronous callback rule is checked here at runtime.
11
+ */
12
+ export function assertNotThenable<Value>(
13
+ value: Value,
14
+ helper: string,
15
+ hint: string,
16
+ ): Value {
17
+ if (
18
+ ((typeof value === "object" && value !== null) ||
19
+ typeof value === "function") &&
20
+ "then" in value &&
21
+ typeof value.then === "function"
22
+ ) {
23
+ throw new TypeError(
24
+ `${helper} received a promise or other thenable from a synchronous callback. ${hint}`,
25
+ );
26
+ }
27
+ return value;
28
+ }
@@ -0,0 +1,98 @@
1
+ import { TRAVERSAL_HINT, assertNotThenable } from "./synchronous-result.js";
2
+ import { TimeSliceBudget } from "./time-slicing.js";
3
+
4
+ async function findFirstMatch<Item>(
5
+ helper: string,
6
+ items: Iterable<Item>,
7
+ predicate: (item: Item, index: number) => boolean,
8
+ budget: TimeSliceBudget,
9
+ ): Promise<{ item: Item } | undefined> {
10
+ const position = { index: 0 };
11
+ // oxlint-disable-next-line zingage/no-restricted-syntax -- Stop at the first match; native iteration closes the iterator without advancing it.
12
+ for (const item of items) {
13
+ const matched = budget.measure(() =>
14
+ assertNotThenable(
15
+ predicate(item, position.index),
16
+ helper,
17
+ TRAVERSAL_HINT,
18
+ ),
19
+ );
20
+ position.index += 1;
21
+ const pending = budget.yieldIfNeeded();
22
+ if (pending !== undefined) await pending;
23
+ if (matched) return { item };
24
+ }
25
+ return undefined;
26
+ }
27
+
28
+ /**
29
+ * Returns whether any item matches a synchronous predicate, in input order.
30
+ *
31
+ * Stops at the first match. It does not advance the iterable past that item,
32
+ * and it builds no intermediate array. Measures each predicate call, including
33
+ * calls that throw. One predicate call cannot be interrupted. Throws a
34
+ * `TypeError` if the predicate returns a promise or other thenable.
35
+ * The optional budget defaults to a new independent `TimeSliceBudget`.
36
+ *
37
+ * @example Check for a negative number.
38
+ * const hasNegative = await timeSlicedSome([3, -1, 2], (value) => value < 0);
39
+ *
40
+ * @example Share a budget with other work, and stop early on a lazy source.
41
+ * const budget = new TimeSliceBudget();
42
+ * const found = await timeSlicedSome(map.values(), (entry) => entry.active, budget);
43
+ */
44
+ export async function timeSlicedSome<Item>(
45
+ items: Iterable<Item>,
46
+ predicate: (item: Item, index: number) => boolean,
47
+ budget = new TimeSliceBudget(),
48
+ ): Promise<boolean> {
49
+ return (
50
+ (await findFirstMatch("timeSlicedSome", items, predicate, budget)) !==
51
+ undefined
52
+ );
53
+ }
54
+
55
+ /**
56
+ * Returns the first item that matches a type-guard predicate, narrowed.
57
+ *
58
+ * @example Find the first string.
59
+ * const text = await timeSlicedFind(
60
+ * [1, "a", 2],
61
+ * (value): value is string => typeof value === "string",
62
+ * );
63
+ */
64
+ export function timeSlicedFind<Item, Found extends Item>(
65
+ items: Iterable<Item>,
66
+ predicate: (item: Item, index: number) => item is Found,
67
+ budget?: TimeSliceBudget,
68
+ ): Promise<Found | undefined>;
69
+ /**
70
+ * Returns the first item that matches a synchronous predicate, or `undefined`.
71
+ *
72
+ * Stops at the first match. It does not advance the iterable past that item,
73
+ * and it builds no intermediate array. Measures each predicate call, including
74
+ * calls that throw. One predicate call cannot be interrupted. Throws a
75
+ * `TypeError` if the predicate returns a promise or other thenable. As with
76
+ * `Array.prototype.find`, a matching `undefined` item also returns `undefined`.
77
+ * The optional budget defaults to a new independent `TimeSliceBudget`.
78
+ *
79
+ * @example Find the first even number.
80
+ * const even = await timeSlicedFind([1, 4, 6], (value) => value % 2 === 0);
81
+ *
82
+ * @example Use the index and a shared budget.
83
+ * const budget = new TimeSliceBudget();
84
+ * const late = await timeSlicedFind(["a", "b"], (_, index) => index > 0, budget);
85
+ */
86
+ export function timeSlicedFind<Item>(
87
+ items: Iterable<Item>,
88
+ predicate: (item: Item, index: number) => boolean,
89
+ budget?: TimeSliceBudget,
90
+ ): Promise<Item | undefined>;
91
+ export async function timeSlicedFind<Item>(
92
+ items: Iterable<Item>,
93
+ predicate: (item: Item, index: number) => boolean,
94
+ budget = new TimeSliceBudget(),
95
+ ): Promise<Item | undefined> {
96
+ return (await findFirstMatch("timeSlicedFind", items, predicate, budget))
97
+ ?.item;
98
+ }
@@ -1,11 +1,14 @@
1
1
  import assert from "node:assert/strict";
2
2
  import { setImmediate } from "node:timers/promises";
3
+ import {
4
+ GENERATOR_HINT,
5
+ TRAVERSAL_HINT,
6
+ assertNotThenable,
7
+ } from "./synchronous-result.js";
3
8
 
4
9
  const DEFAULT_TIME_SLICE_MS = 4;
5
10
  const performanceNow = performance.now.bind(performance);
6
11
 
7
- type Synchronous<Result> = Result extends PromiseLike<unknown> ? never : Result;
8
-
9
12
  /**
10
13
  * Tracks measured synchronous work before yielding to the event loop.
11
14
  *
@@ -55,6 +58,8 @@ export class TimeSliceBudget {
55
58
  *
56
59
  * Nested measurements on this budget count once. This method does not yield.
57
60
  * Call `yieldIfNeeded` at a safe boundary after handling the result.
61
+ * `work` must be synchronous. If it returns a promise or other thenable, this
62
+ * method throws a `TypeError` after charging the elapsed time.
58
63
  *
59
64
  * @example Measure one segment and then check the budget.
60
65
  * const budget = new TimeSliceBudget();
@@ -62,12 +67,16 @@ export class TimeSliceBudget {
62
67
  * const pending = budget.yieldIfNeeded();
63
68
  * if (pending !== undefined) await pending;
64
69
  */
65
- measure<Result>(work: () => Result & Synchronous<Result>): Result {
70
+ measure<Result>(work: () => Result): Result {
66
71
  const outermost = this.#depth === 0;
67
72
  const startedAt = outermost ? this.#now() : 0;
68
73
  this.#depth += 1;
69
74
  try {
70
- return work();
75
+ return assertNotThenable(
76
+ work(),
77
+ "TimeSliceBudget.measure",
78
+ GENERATOR_HINT,
79
+ );
71
80
  } finally {
72
81
  this.#depth -= 1;
73
82
  if (outermost) this.#remainingMs -= this.#now() - startedAt;
@@ -197,19 +206,17 @@ async function runCallbackGenerator<Result>(
197
206
  }
198
207
 
199
208
  async function traverse<Item, Result>(
209
+ helper: string,
200
210
  items: Iterable<Item>,
201
- callback: (
202
- item: Item,
203
- index: number,
204
- ) => (Result & Synchronous<Result>) | CallbackGenerator<Result>,
211
+ callback: (item: Item, index: number) => Result | CallbackGenerator<Result>,
205
212
  accept: (result: Result) => void,
206
213
  budget: TimeSliceBudget,
207
214
  ): Promise<void> {
208
215
  const position = { index: 0 };
209
216
  // oxlint-disable-next-line zingage/no-restricted-syntax -- Native iteration owns cleanup while callbacks and budget yields settle sequentially.
210
217
  for (const item of items) {
211
- const value = budget.measure<Result | CallbackGenerator<Result>>(() =>
212
- callback(item, position.index),
218
+ const value = budget.measure(() =>
219
+ assertNotThenable(callback(item, position.index), helper, GENERATOR_HINT),
213
220
  );
214
221
  const result = isCallbackGenerator<Result>(value)
215
222
  ? await runCallbackGenerator(value, budget)
@@ -228,6 +235,9 @@ async function traverse<Item, Result>(
228
235
  * finishes before the next item starts. Measures synchronous callback work and
229
236
  * every generator `next()`/`throw()` segment, excluding promise waits.
230
237
  * Iterator advancement is unmeasured. Unhandled failures stop the traversal.
238
+ * A non-generator callback must be synchronous. If it returns a promise or
239
+ * other thenable (an `async` function, for example), the traversal throws a
240
+ * `TypeError` on that first call. The type checker does not catch this.
231
241
  * The optional budget defaults to a new independent `TimeSliceBudget`.
232
242
  *
233
243
  * @example Run a synchronous visitor.
@@ -247,23 +257,22 @@ export function timeSlicedForEach<Item, Result>(
247
257
  /**
248
258
  * Runs a synchronous visitor in input order with an optional processing budget.
249
259
  *
260
+ * Throws a `TypeError` if the visitor returns a promise or other thenable.
261
+ *
250
262
  * @example
251
263
  * await timeSlicedForEach([1, 2], (value) => console.log(value));
252
264
  */
253
265
  export function timeSlicedForEach<Item, Result>(
254
266
  items: Iterable<Item>,
255
- callback: (item: Item, index: number) => Result & Synchronous<Result>,
267
+ callback: (item: Item, index: number) => Result,
256
268
  budget?: TimeSliceBudget,
257
269
  ): Promise<void>;
258
270
  export async function timeSlicedForEach<Item, Result>(
259
271
  items: Iterable<Item>,
260
- callback: (
261
- item: Item,
262
- index: number,
263
- ) => (Result & Synchronous<Result>) | CallbackGenerator<Result>,
272
+ callback: (item: Item, index: number) => Result | CallbackGenerator<Result>,
264
273
  budget = new TimeSliceBudget(),
265
274
  ): Promise<void> {
266
- await traverse(items, callback, () => {}, budget);
275
+ await traverse("timeSlicedForEach", items, callback, () => {}, budget);
267
276
  }
268
277
 
269
278
  /**
@@ -272,7 +281,9 @@ export async function timeSlicedForEach<Item, Result>(
272
281
  * Accepts synchronous callbacks or generators that yield promises. Each generator
273
282
  * finishes before the next item starts. Measures every synchronous segment,
274
283
  * including `throw()` recovery and result accumulation. Promise waits and iterator
275
- * advancement are unmeasured. Ordinary async callbacks are not supported.
284
+ * advancement are unmeasured. Ordinary async callbacks are not supported: if a
285
+ * non-generator callback returns a promise or other thenable, the traversal
286
+ * throws a `TypeError` on that first call. The type checker does not catch this.
276
287
  * The optional budget defaults to a new independent `TimeSliceBudget`.
277
288
  *
278
289
  * @example Transform synchronous values.
@@ -292,24 +303,24 @@ export function timeSlicedMap<Item, Result>(
292
303
  /**
293
304
  * Maps synchronous values in input order with an optional processing budget.
294
305
  *
306
+ * Throws a `TypeError` if the callback returns a promise or other thenable.
307
+ *
295
308
  * @example
296
309
  * const doubled = await timeSlicedMap([1, 2], (value) => value * 2);
297
310
  */
298
311
  export function timeSlicedMap<Item, Result>(
299
312
  items: Iterable<Item>,
300
- callback: (item: Item, index: number) => Result & Synchronous<Result>,
313
+ callback: (item: Item, index: number) => Result,
301
314
  budget?: TimeSliceBudget,
302
315
  ): Promise<Result[]>;
303
316
  export async function timeSlicedMap<Item, Result>(
304
317
  items: Iterable<Item>,
305
- callback: (
306
- item: Item,
307
- index: number,
308
- ) => (Result & Synchronous<Result>) | CallbackGenerator<Result>,
318
+ callback: (item: Item, index: number) => Result | CallbackGenerator<Result>,
309
319
  budget = new TimeSliceBudget(),
310
320
  ): Promise<Result[]> {
311
321
  const results: Result[] = [];
312
322
  await traverse(
323
+ "timeSlicedMap",
313
324
  items,
314
325
  callback,
315
326
  (result) => {
@@ -370,7 +381,8 @@ export async function timeSlicedFlatMap<Item, Result>(
370
381
  * Returns items whose synchronous predicate is true, preserving input order.
371
382
  *
372
383
  * Measures predicate work and result accumulation. One predicate call cannot be
373
- * interrupted. The optional budget defaults to a new independent instance.
384
+ * interrupted. Throws a `TypeError` if the predicate returns a promise or other
385
+ * thenable. The optional budget defaults to a new independent instance.
374
386
  *
375
387
  * @example Keep odd numbers.
376
388
  * const odd = await timeSlicedFilter([1, 2, 3], (value) => value % 2 === 1);
@@ -390,7 +402,13 @@ export async function timeSlicedFilter<Item>(
390
402
  await timeSlicedForEach(
391
403
  items,
392
404
  (item, index) => {
393
- if (predicate(item, index)) {
405
+ if (
406
+ assertNotThenable(
407
+ predicate(item, index),
408
+ "timeSlicedFilter",
409
+ TRAVERSAL_HINT,
410
+ )
411
+ ) {
394
412
  // oxlint-disable-next-line zingage/no-restricted-syntax -- Append to the local result without quadratic copying.
395
413
  results.push(item);
396
414
  }
@@ -404,6 +422,8 @@ export async function timeSlicedFilter<Item>(
404
422
  * Groups items by a synchronous key, preserving key and group insertion order.
405
423
  *
406
424
  * Uses `Map` key equality. Measures key selection and group accumulation.
425
+ * `keyOf` must be synchronous. If it returns a promise or other thenable, the
426
+ * operation throws a `TypeError` before it stores the key.
407
427
  * The optional budget defaults to a new independent `TimeSliceBudget`.
408
428
  *
409
429
  * @example Group strings by length.
@@ -417,14 +437,18 @@ export async function timeSlicedFilter<Item>(
417
437
  */
418
438
  export async function timeSlicedGroupBy<Item, Key>(
419
439
  items: Iterable<Item>,
420
- keyOf: (item: Item, index: number) => Key & Synchronous<Key>,
440
+ keyOf: (item: Item, index: number) => Key,
421
441
  budget = new TimeSliceBudget(),
422
442
  ): Promise<Map<Key, Item[]>> {
423
443
  const groups = new Map<Key, Item[]>();
424
444
  await timeSlicedForEach(
425
445
  items,
426
446
  (item, index) => {
427
- const key = keyOf(item, index);
447
+ const key = assertNotThenable(
448
+ keyOf(item, index),
449
+ "timeSlicedGroupBy",
450
+ TRAVERSAL_HINT,
451
+ );
428
452
  const group = groups.get(key);
429
453
  if (group === undefined) groups.set(key, [item]);
430
454
  // oxlint-disable-next-line zingage/no-restricted-syntax -- Each local group accumulates without quadratic copying.