batchkit 0.2.0-beta.1 → 0.3.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/dist/batch.d.ts +3 -0
- package/dist/batch.d.ts.map +1 -1
- package/dist/errors.d.ts +4 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -18
- package/dist/indexed.d.ts +2 -0
- package/dist/indexed.d.ts.map +1 -0
- package/dist/match.d.ts +6 -0
- package/dist/match.d.ts.map +1 -0
- package/dist/schedulers.d.ts +8 -0
- package/dist/schedulers.d.ts.map +1 -0
- package/dist/trace.d.ts +17 -0
- package/dist/trace.d.ts.map +1 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +6 -1
- package/src/batch.ts +25 -4
- package/src/index.ts +3 -0
- package/src/match.ts +3 -3
- package/src/trace.ts +19 -19
package/dist/batch.d.ts
ADDED
package/dist/batch.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,OAAO,EACP,OAAO,EACP,YAAY,EAEZ,KAAK,
|
|
1
|
+
{"version":3,"file":"batch.d.ts","sourceRoot":"","sources":["../src/batch.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,OAAO,EACP,OAAO,EACP,YAAY,EAEZ,KAAK,EAIN,MAAM,SAAS,CAAC;AAEjB,wBAAgB,KAAK,CAAC,CAAC,EAAE,CAAC,EACxB,EAAE,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,EACjB,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAClB,OAAO,GAAE,YAAY,CAAC,CAAC,CAAM,GAC5B,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAkTf"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,UAAW,SAAQ,KAAK;gBACvB,OAAO,EAAE,MAAM;CAI5B"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { batch } from './batch';
|
|
2
|
+
export { BatchError } from './errors';
|
|
3
|
+
export { indexed } from './indexed';
|
|
4
|
+
export { onAnimationFrame, onIdle } from './schedulers';
|
|
5
|
+
export type { DevtoolsHook } from './trace';
|
|
6
|
+
export { __setDevtoolsHook } from './trace';
|
|
7
|
+
export type { Batcher, BatchFn, BatchOptions, GetOptions, Match, MatchFn, Scheduler, TraceEvent, TraceEventData, TraceHandler, } from './types';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACxD,YAAY,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAE5C,YAAY,EACV,OAAO,EACP,OAAO,EACP,YAAY,EACZ,UAAU,EACV,KAAK,EACL,OAAO,EACP,SAAS,EACT,UAAU,EACV,cAAc,EACd,YAAY,GACb,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ function isIndexed(match) {
|
|
|
14
14
|
return match === indexed;
|
|
15
15
|
}
|
|
16
16
|
function isKeyMatch(match) {
|
|
17
|
-
return typeof match === "string";
|
|
17
|
+
return typeof match === "string" || typeof match === "number";
|
|
18
18
|
}
|
|
19
19
|
function normalizeMatch(match) {
|
|
20
20
|
if (isIndexed(match)) {
|
|
@@ -66,15 +66,15 @@ function onIdle(options) {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// src/trace.ts
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
var devtoolsHook = null;
|
|
70
|
+
function __setDevtoolsHook(hook) {
|
|
71
|
+
devtoolsHook = hook;
|
|
72
|
+
}
|
|
73
|
+
function getDevtoolsHook() {
|
|
74
|
+
return devtoolsHook;
|
|
74
75
|
}
|
|
75
|
-
function createTracer(name, handler) {
|
|
76
|
+
function createTracer(name, handler, getDevtoolsEmitter) {
|
|
76
77
|
let batchCounter = 0;
|
|
77
|
-
let registeredWithDevtools = false;
|
|
78
78
|
function emit(event) {
|
|
79
79
|
const timestamp = performance.now();
|
|
80
80
|
const fullEvent = {
|
|
@@ -84,13 +84,9 @@ function createTracer(name, handler) {
|
|
|
84
84
|
if (handler) {
|
|
85
85
|
handler(fullEvent);
|
|
86
86
|
}
|
|
87
|
-
const
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
devtools.register({ name, registeredAt: timestamp });
|
|
91
|
-
registeredWithDevtools = true;
|
|
92
|
-
}
|
|
93
|
-
devtools.emit(name, fullEvent);
|
|
87
|
+
const devtoolsEmitter = getDevtoolsEmitter?.();
|
|
88
|
+
if (devtoolsEmitter) {
|
|
89
|
+
devtoolsEmitter(fullEvent);
|
|
94
90
|
}
|
|
95
91
|
}
|
|
96
92
|
function nextBatchId() {
|
|
@@ -110,7 +106,13 @@ function batch(fn, match, options = {}) {
|
|
|
110
106
|
trace: traceHandler
|
|
111
107
|
} = options;
|
|
112
108
|
const scheduler = schedule ?? (waitMs ? wait(waitMs) : microtask);
|
|
113
|
-
|
|
109
|
+
let devtoolsEmitter;
|
|
110
|
+
const hook = getDevtoolsHook();
|
|
111
|
+
if (hook) {
|
|
112
|
+
const stack = new Error().stack;
|
|
113
|
+
devtoolsEmitter = hook.onBatcherCreated({ fn, name, stack });
|
|
114
|
+
}
|
|
115
|
+
const tracer = createTracer(name, traceHandler, () => devtoolsEmitter);
|
|
114
116
|
const matchFn = normalizeMatch(match);
|
|
115
117
|
const isIndexedMatch = isIndexed(match);
|
|
116
118
|
const indexedMatcher = isIndexedMatch ? createIndexedMatcher() : null;
|
|
@@ -119,6 +121,7 @@ function batch(fn, match, options = {}) {
|
|
|
119
121
|
let cleanup = null;
|
|
120
122
|
let isScheduled = false;
|
|
121
123
|
let currentAbortController = null;
|
|
124
|
+
let inFlightRequests = [];
|
|
122
125
|
function scheduleDispatch() {
|
|
123
126
|
if (isScheduled || queue.length === 0)
|
|
124
127
|
return;
|
|
@@ -179,6 +182,7 @@ function batch(fn, match, options = {}) {
|
|
|
179
182
|
}
|
|
180
183
|
if (uniqueKeys.length === 0)
|
|
181
184
|
return;
|
|
185
|
+
inFlightRequests = chunk;
|
|
182
186
|
tracer.emit({
|
|
183
187
|
type: "dispatch",
|
|
184
188
|
batchId,
|
|
@@ -259,6 +263,7 @@ function batch(fn, match, options = {}) {
|
|
|
259
263
|
}
|
|
260
264
|
} finally {
|
|
261
265
|
currentAbortController = null;
|
|
266
|
+
inFlightRequests = [];
|
|
262
267
|
}
|
|
263
268
|
}
|
|
264
269
|
function getSingle(key, options2) {
|
|
@@ -286,8 +291,9 @@ function batch(fn, match, options = {}) {
|
|
|
286
291
|
const onAbort = () => {
|
|
287
292
|
request.aborted = true;
|
|
288
293
|
reject(new DOMException("Aborted", "AbortError"));
|
|
289
|
-
const
|
|
290
|
-
|
|
294
|
+
const allPendingAborted = queue.every((r) => r.aborted);
|
|
295
|
+
const allInFlightAborted = inFlightRequests.length > 0 && inFlightRequests.every((r) => r.aborted);
|
|
296
|
+
if (allPendingAborted && allInFlightAborted && currentAbortController) {
|
|
291
297
|
currentAbortController.abort();
|
|
292
298
|
}
|
|
293
299
|
};
|
|
@@ -338,5 +344,6 @@ export {
|
|
|
338
344
|
onAnimationFrame,
|
|
339
345
|
indexed,
|
|
340
346
|
batch,
|
|
347
|
+
__setDevtoolsHook,
|
|
341
348
|
BatchError
|
|
342
349
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexed.d.ts","sourceRoot":"","sources":["../src/indexed.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,EAAE,OAAO,MAA0B,CAAC"}
|
package/dist/match.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Match, MatchFn } from './types';
|
|
2
|
+
export declare function isIndexed<K, V>(match: Match<K, V>): match is symbol;
|
|
3
|
+
export declare function isKeyMatch<K, V>(match: Match<K, V>): match is keyof V;
|
|
4
|
+
export declare function normalizeMatch<K, V>(match: Match<K, V>): MatchFn<K, V> | null;
|
|
5
|
+
export declare function createIndexedMatcher<K, V>(): (results: Record<string, V>, key: K) => V | undefined;
|
|
6
|
+
//# sourceMappingURL=match.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"match.d.ts","sourceRoot":"","sources":["../src/match.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAE9C,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,IAAI,MAAM,CAEnE;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,IAAI,MAAM,CAAC,CAErE;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAkB7E;AAED,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,CAAC,KAAK,CAC5C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAC1B,GAAG,EAAE,CAAC,KACH,CAAC,GAAG,SAAS,CAEjB"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Scheduler } from './types';
|
|
2
|
+
export declare const microtask: Scheduler;
|
|
3
|
+
export declare function wait(ms: number): Scheduler;
|
|
4
|
+
export declare const onAnimationFrame: Scheduler;
|
|
5
|
+
export declare function onIdle(options?: {
|
|
6
|
+
timeout?: number;
|
|
7
|
+
}): Scheduler;
|
|
8
|
+
//# sourceMappingURL=schedulers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schedulers.d.ts","sourceRoot":"","sources":["../src/schedulers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,eAAO,MAAM,SAAS,EAAE,SAYvB,CAAC;AAEF,wBAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAK1C;AAED,eAAO,MAAM,gBAAgB,EAAE,SAG9B,CAAC;AAEF,wBAAgB,MAAM,CAAC,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAahE"}
|
package/dist/trace.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { TraceEvent, TraceEventData, TraceHandler } from './types';
|
|
2
|
+
export interface DevtoolsHook {
|
|
3
|
+
onBatcherCreated(info: {
|
|
4
|
+
fn: {
|
|
5
|
+
toString(): string;
|
|
6
|
+
};
|
|
7
|
+
name: string | undefined;
|
|
8
|
+
stack: string | undefined;
|
|
9
|
+
}): ((event: TraceEvent) => void) | undefined;
|
|
10
|
+
}
|
|
11
|
+
export declare function __setDevtoolsHook(hook: DevtoolsHook | null): void;
|
|
12
|
+
export declare function getDevtoolsHook(): DevtoolsHook | null;
|
|
13
|
+
export declare function createTracer<K>(name: string | undefined, handler: TraceHandler<K> | undefined, getDevtoolsEmitter: (() => ((event: TraceEvent<K>) => void) | undefined) | undefined): {
|
|
14
|
+
emit: (event: TraceEventData<K>) => void;
|
|
15
|
+
nextBatchId: () => string;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=trace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../src/trace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAExE,MAAM,WAAW,YAAY;IAC3B,gBAAgB,CAAC,IAAI,EAAE;QACrB,EAAE,EAAE;YAAE,QAAQ,IAAI,MAAM,CAAA;SAAE,CAAC;QAC3B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;QACzB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;KAC3B,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CAC/C;AAID,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAEjE;AAED,wBAAgB,eAAe,IAAI,YAAY,GAAG,IAAI,CAErD;AAED,wBAAgB,YAAY,CAAC,CAAC,EAC5B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS,EACpC,kBAAkB,EACd,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC,GACpD,SAAS;kBAIQ,eAAe,CAAC,CAAC;uBAiBd,MAAM;EAK/B"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export type BatchFn<K, V> = (keys: K[], signal: AbortSignal) => Promise<V[] | Record<string, V>>;
|
|
2
|
+
export type Match<K, V> = keyof V | symbol | MatchFn<K, V>;
|
|
3
|
+
export type MatchFn<K, V> = (results: V[], key: K) => V | undefined;
|
|
4
|
+
export type IndexedMatchFn<K, V> = (results: Record<string, V>, key: K) => V | undefined;
|
|
5
|
+
export type Scheduler = (dispatch: () => void) => () => void;
|
|
6
|
+
export type TraceHandler<K = unknown> = (event: TraceEvent<K>) => void;
|
|
7
|
+
export type TraceEventData<K = unknown> = {
|
|
8
|
+
type: 'get';
|
|
9
|
+
key: K;
|
|
10
|
+
} | {
|
|
11
|
+
type: 'dedup';
|
|
12
|
+
key: K;
|
|
13
|
+
} | {
|
|
14
|
+
type: 'schedule';
|
|
15
|
+
batchId: string;
|
|
16
|
+
size: number;
|
|
17
|
+
} | {
|
|
18
|
+
type: 'dispatch';
|
|
19
|
+
batchId: string;
|
|
20
|
+
keys: K[];
|
|
21
|
+
} | {
|
|
22
|
+
type: 'resolve';
|
|
23
|
+
batchId: string;
|
|
24
|
+
duration: number;
|
|
25
|
+
} | {
|
|
26
|
+
type: 'error';
|
|
27
|
+
batchId: string;
|
|
28
|
+
error: Error;
|
|
29
|
+
} | {
|
|
30
|
+
type: 'abort';
|
|
31
|
+
batchId: string;
|
|
32
|
+
};
|
|
33
|
+
export type TraceEvent<K = unknown> = TraceEventData<K> & {
|
|
34
|
+
timestamp: number;
|
|
35
|
+
};
|
|
36
|
+
export interface BatchOptions<K = unknown> {
|
|
37
|
+
wait?: number;
|
|
38
|
+
schedule?: Scheduler;
|
|
39
|
+
max?: number;
|
|
40
|
+
key?: (k: K) => unknown;
|
|
41
|
+
name?: string;
|
|
42
|
+
trace?: TraceHandler<K>;
|
|
43
|
+
}
|
|
44
|
+
export interface GetOptions {
|
|
45
|
+
signal?: AbortSignal;
|
|
46
|
+
}
|
|
47
|
+
export interface Batcher<K, V> {
|
|
48
|
+
get(key: K, options?: GetOptions): Promise<V>;
|
|
49
|
+
get(keys: K[], options?: GetOptions): Promise<V[]>;
|
|
50
|
+
flush(): Promise<void>;
|
|
51
|
+
abort(): void;
|
|
52
|
+
readonly name?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface PendingRequest<K, V> {
|
|
55
|
+
key: K;
|
|
56
|
+
resolve: (value: V) => void;
|
|
57
|
+
reject: (error: Error) => void;
|
|
58
|
+
signal?: AbortSignal;
|
|
59
|
+
aborted: boolean;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "batchkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A modern TypeScript library for batching async operations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -28,6 +28,11 @@
|
|
|
28
28
|
],
|
|
29
29
|
"author": "",
|
|
30
30
|
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/SirajChokshi/batchkit.git",
|
|
34
|
+
"directory": "packages/core"
|
|
35
|
+
},
|
|
31
36
|
"devDependencies": {
|
|
32
37
|
"bun-types": "latest",
|
|
33
38
|
"typescript": "5.3.3"
|
package/src/batch.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BatchError } from './errors';
|
|
2
2
|
import { createIndexedMatcher, isIndexed, normalizeMatch } from './match';
|
|
3
3
|
import { microtask, wait } from './schedulers';
|
|
4
|
-
import { createTracer } from './trace';
|
|
4
|
+
import { createTracer, getDevtoolsHook } from './trace';
|
|
5
5
|
import type {
|
|
6
6
|
Batcher,
|
|
7
7
|
BatchFn,
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
Match,
|
|
11
11
|
PendingRequest,
|
|
12
12
|
Scheduler,
|
|
13
|
+
TraceEvent,
|
|
13
14
|
} from './types';
|
|
14
15
|
|
|
15
16
|
export function batch<K, V>(
|
|
@@ -27,7 +28,16 @@ export function batch<K, V>(
|
|
|
27
28
|
} = options;
|
|
28
29
|
|
|
29
30
|
const scheduler: Scheduler = schedule ?? (waitMs ? wait(waitMs) : microtask);
|
|
30
|
-
|
|
31
|
+
|
|
32
|
+
let devtoolsEmitter: ((event: TraceEvent) => void) | undefined;
|
|
33
|
+
const hook = getDevtoolsHook();
|
|
34
|
+
|
|
35
|
+
if (hook) {
|
|
36
|
+
const stack = new Error().stack;
|
|
37
|
+
devtoolsEmitter = hook.onBatcherCreated({ fn, name, stack });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const tracer = createTracer(name, traceHandler, () => devtoolsEmitter);
|
|
31
41
|
|
|
32
42
|
const matchFn = normalizeMatch(match);
|
|
33
43
|
const isIndexedMatch = isIndexed(match);
|
|
@@ -38,6 +48,7 @@ export function batch<K, V>(
|
|
|
38
48
|
let cleanup: (() => void) | null = null;
|
|
39
49
|
let isScheduled = false;
|
|
40
50
|
let currentAbortController: AbortController | null = null;
|
|
51
|
+
let inFlightRequests: PendingRequest<K, V>[] = [];
|
|
41
52
|
|
|
42
53
|
function scheduleDispatch(): void {
|
|
43
54
|
if (isScheduled || queue.length === 0) return;
|
|
@@ -115,6 +126,8 @@ export function batch<K, V>(
|
|
|
115
126
|
|
|
116
127
|
if (uniqueKeys.length === 0) return;
|
|
117
128
|
|
|
129
|
+
inFlightRequests = chunk;
|
|
130
|
+
|
|
118
131
|
tracer.emit({
|
|
119
132
|
type: 'dispatch',
|
|
120
133
|
batchId,
|
|
@@ -210,6 +223,7 @@ export function batch<K, V>(
|
|
|
210
223
|
}
|
|
211
224
|
} finally {
|
|
212
225
|
currentAbortController = null;
|
|
226
|
+
inFlightRequests = [];
|
|
213
227
|
}
|
|
214
228
|
}
|
|
215
229
|
|
|
@@ -246,8 +260,15 @@ export function batch<K, V>(
|
|
|
246
260
|
request.aborted = true;
|
|
247
261
|
reject(new DOMException('Aborted', 'AbortError'));
|
|
248
262
|
|
|
249
|
-
const
|
|
250
|
-
|
|
263
|
+
const allPendingAborted = queue.every((r) => r.aborted);
|
|
264
|
+
const allInFlightAborted =
|
|
265
|
+
inFlightRequests.length > 0 &&
|
|
266
|
+
inFlightRequests.every((r) => r.aborted);
|
|
267
|
+
if (
|
|
268
|
+
allPendingAborted &&
|
|
269
|
+
allInFlightAborted &&
|
|
270
|
+
currentAbortController
|
|
271
|
+
) {
|
|
251
272
|
currentAbortController.abort();
|
|
252
273
|
}
|
|
253
274
|
};
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,8 @@ export { batch } from './batch';
|
|
|
2
2
|
export { BatchError } from './errors';
|
|
3
3
|
export { indexed } from './indexed';
|
|
4
4
|
export { onAnimationFrame, onIdle } from './schedulers';
|
|
5
|
+
export type { DevtoolsHook } from './trace';
|
|
6
|
+
export { __setDevtoolsHook } from './trace';
|
|
5
7
|
|
|
6
8
|
export type {
|
|
7
9
|
Batcher,
|
|
@@ -12,5 +14,6 @@ export type {
|
|
|
12
14
|
MatchFn,
|
|
13
15
|
Scheduler,
|
|
14
16
|
TraceEvent,
|
|
17
|
+
TraceEventData,
|
|
15
18
|
TraceHandler,
|
|
16
19
|
} from './types';
|
package/src/match.ts
CHANGED
|
@@ -6,7 +6,7 @@ export function isIndexed<K, V>(match: Match<K, V>): match is symbol {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export function isKeyMatch<K, V>(match: Match<K, V>): match is keyof V {
|
|
9
|
-
return typeof match === 'string';
|
|
9
|
+
return typeof match === 'string' || typeof match === 'number';
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export function normalizeMatch<K, V>(match: Match<K, V>): MatchFn<K, V> | null {
|
|
@@ -16,11 +16,11 @@ export function normalizeMatch<K, V>(match: Match<K, V>): MatchFn<K, V> | null {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
if (isKeyMatch<K, V>(match)) {
|
|
19
|
-
const key = match;
|
|
19
|
+
const key = match as string | number;
|
|
20
20
|
return (results: V[], requestedKey: K) => {
|
|
21
21
|
return results.find(
|
|
22
22
|
(item) =>
|
|
23
|
-
(item as Record<string, unknown>)[key
|
|
23
|
+
(item as Record<string | number, unknown>)[key] === requestedKey,
|
|
24
24
|
);
|
|
25
25
|
};
|
|
26
26
|
}
|
package/src/trace.ts
CHANGED
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
import type { TraceEvent, TraceEventData, TraceHandler } from './types';
|
|
2
2
|
|
|
3
|
-
interface
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
export interface DevtoolsHook {
|
|
4
|
+
onBatcherCreated(info: {
|
|
5
|
+
fn: { toString(): string };
|
|
6
|
+
name: string | undefined;
|
|
7
|
+
stack: string | undefined;
|
|
8
|
+
}): ((event: TraceEvent) => void) | undefined;
|
|
6
9
|
}
|
|
7
10
|
|
|
8
|
-
|
|
11
|
+
let devtoolsHook: DevtoolsHook | null = null;
|
|
9
12
|
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return
|
|
13
|
+
export function __setDevtoolsHook(hook: DevtoolsHook | null): void {
|
|
14
|
+
devtoolsHook = hook;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getDevtoolsHook(): DevtoolsHook | null {
|
|
18
|
+
return devtoolsHook;
|
|
16
19
|
}
|
|
17
20
|
|
|
18
21
|
export function createTracer<K>(
|
|
19
22
|
name: string | undefined,
|
|
20
23
|
handler: TraceHandler<K> | undefined,
|
|
24
|
+
getDevtoolsEmitter:
|
|
25
|
+
| (() => ((event: TraceEvent<K>) => void) | undefined)
|
|
26
|
+
| undefined,
|
|
21
27
|
) {
|
|
22
28
|
let batchCounter = 0;
|
|
23
|
-
let registeredWithDevtools = false;
|
|
24
29
|
|
|
25
30
|
function emit(event: TraceEventData<K>) {
|
|
26
31
|
const timestamp = performance.now();
|
|
27
|
-
|
|
28
32
|
const fullEvent = {
|
|
29
33
|
...event,
|
|
30
34
|
timestamp,
|
|
@@ -34,13 +38,9 @@ export function createTracer<K>(
|
|
|
34
38
|
handler(fullEvent);
|
|
35
39
|
}
|
|
36
40
|
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
devtools.register({ name, registeredAt: timestamp });
|
|
41
|
-
registeredWithDevtools = true;
|
|
42
|
-
}
|
|
43
|
-
devtools.emit(name, fullEvent);
|
|
41
|
+
const devtoolsEmitter = getDevtoolsEmitter?.();
|
|
42
|
+
if (devtoolsEmitter) {
|
|
43
|
+
devtoolsEmitter(fullEvent);
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|