batchkit 0.2.0 → 0.3.1
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 -0
- 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 +42 -17
- 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 +23 -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 +1 -1
- package/src/batch.ts +24 -4
- package/src/index.ts +3 -0
- package/src/match.ts +3 -3
- package/src/trace.ts +53 -18
package/dist/batch.d.ts
ADDED
|
@@ -0,0 +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,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,CAiTf"}
|
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,34 @@ function onIdle(options) {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// src/trace.ts
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
69
|
+
var devtoolsHook = null;
|
|
70
|
+
var pendingBatchers = [];
|
|
71
|
+
function __setDevtoolsHook(hook) {
|
|
72
|
+
devtoolsHook = hook;
|
|
73
|
+
if (hook && pendingBatchers.length > 0) {
|
|
74
|
+
for (const pending of pendingBatchers) {
|
|
75
|
+
const emitter = hook.onBatcherCreated({
|
|
76
|
+
fn: pending.fn,
|
|
77
|
+
name: pending.name,
|
|
78
|
+
stack: pending.stack
|
|
79
|
+
});
|
|
80
|
+
pending.setEmitter(emitter);
|
|
81
|
+
}
|
|
82
|
+
pendingBatchers.length = 0;
|
|
83
|
+
} else if (!hook) {
|
|
84
|
+
pendingBatchers.length = 0;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function registerBatcher(info, setEmitter) {
|
|
88
|
+
if (devtoolsHook) {
|
|
89
|
+
const emitter = devtoolsHook.onBatcherCreated(info);
|
|
90
|
+
setEmitter(emitter);
|
|
91
|
+
} else {
|
|
92
|
+
pendingBatchers.push({ ...info, setEmitter });
|
|
72
93
|
}
|
|
73
|
-
return;
|
|
74
94
|
}
|
|
75
|
-
function createTracer(name, handler) {
|
|
95
|
+
function createTracer(name, handler, getDevtoolsEmitter) {
|
|
76
96
|
let batchCounter = 0;
|
|
77
|
-
let registeredWithDevtools = false;
|
|
78
97
|
function emit(event) {
|
|
79
98
|
const timestamp = performance.now();
|
|
80
99
|
const fullEvent = {
|
|
@@ -84,13 +103,9 @@ function createTracer(name, handler) {
|
|
|
84
103
|
if (handler) {
|
|
85
104
|
handler(fullEvent);
|
|
86
105
|
}
|
|
87
|
-
const
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
devtools.register({ name, registeredAt: timestamp });
|
|
91
|
-
registeredWithDevtools = true;
|
|
92
|
-
}
|
|
93
|
-
devtools.emit(name, fullEvent);
|
|
106
|
+
const devtoolsEmitter = getDevtoolsEmitter?.();
|
|
107
|
+
if (devtoolsEmitter) {
|
|
108
|
+
devtoolsEmitter(fullEvent);
|
|
94
109
|
}
|
|
95
110
|
}
|
|
96
111
|
function nextBatchId() {
|
|
@@ -110,7 +125,12 @@ function batch(fn, match, options = {}) {
|
|
|
110
125
|
trace: traceHandler
|
|
111
126
|
} = options;
|
|
112
127
|
const scheduler = schedule ?? (waitMs ? wait(waitMs) : microtask);
|
|
113
|
-
|
|
128
|
+
let devtoolsEmitter;
|
|
129
|
+
const creationStack = new Error().stack;
|
|
130
|
+
registerBatcher({ fn, name, stack: creationStack }, (emitter) => {
|
|
131
|
+
devtoolsEmitter = emitter;
|
|
132
|
+
});
|
|
133
|
+
const tracer = createTracer(name, traceHandler, () => devtoolsEmitter);
|
|
114
134
|
const matchFn = normalizeMatch(match);
|
|
115
135
|
const isIndexedMatch = isIndexed(match);
|
|
116
136
|
const indexedMatcher = isIndexedMatch ? createIndexedMatcher() : null;
|
|
@@ -119,6 +139,7 @@ function batch(fn, match, options = {}) {
|
|
|
119
139
|
let cleanup = null;
|
|
120
140
|
let isScheduled = false;
|
|
121
141
|
let currentAbortController = null;
|
|
142
|
+
let inFlightRequests = [];
|
|
122
143
|
function scheduleDispatch() {
|
|
123
144
|
if (isScheduled || queue.length === 0)
|
|
124
145
|
return;
|
|
@@ -179,6 +200,7 @@ function batch(fn, match, options = {}) {
|
|
|
179
200
|
}
|
|
180
201
|
if (uniqueKeys.length === 0)
|
|
181
202
|
return;
|
|
203
|
+
inFlightRequests = chunk;
|
|
182
204
|
tracer.emit({
|
|
183
205
|
type: "dispatch",
|
|
184
206
|
batchId,
|
|
@@ -259,6 +281,7 @@ function batch(fn, match, options = {}) {
|
|
|
259
281
|
}
|
|
260
282
|
} finally {
|
|
261
283
|
currentAbortController = null;
|
|
284
|
+
inFlightRequests = [];
|
|
262
285
|
}
|
|
263
286
|
}
|
|
264
287
|
function getSingle(key, options2) {
|
|
@@ -286,8 +309,9 @@ function batch(fn, match, options = {}) {
|
|
|
286
309
|
const onAbort = () => {
|
|
287
310
|
request.aborted = true;
|
|
288
311
|
reject(new DOMException("Aborted", "AbortError"));
|
|
289
|
-
const
|
|
290
|
-
|
|
312
|
+
const allPendingAborted = queue.every((r) => r.aborted);
|
|
313
|
+
const allInFlightAborted = inFlightRequests.length > 0 && inFlightRequests.every((r) => r.aborted);
|
|
314
|
+
if (allPendingAborted && allInFlightAborted && currentAbortController) {
|
|
291
315
|
currentAbortController.abort();
|
|
292
316
|
}
|
|
293
317
|
};
|
|
@@ -338,5 +362,6 @@ export {
|
|
|
338
362
|
onAnimationFrame,
|
|
339
363
|
indexed,
|
|
340
364
|
batch,
|
|
365
|
+
__setDevtoolsHook,
|
|
341
366
|
BatchError
|
|
342
367
|
};
|
|
@@ -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,23 @@
|
|
|
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 registerBatcher(info: {
|
|
13
|
+
fn: {
|
|
14
|
+
toString(): string;
|
|
15
|
+
};
|
|
16
|
+
name: string | undefined;
|
|
17
|
+
stack: string | undefined;
|
|
18
|
+
}, setEmitter: (emitter: ((event: TraceEvent) => void) | undefined) => void): void;
|
|
19
|
+
export declare function createTracer<K>(name: string | undefined, handler: TraceHandler<K> | undefined, getDevtoolsEmitter: (() => ((event: TraceEvent<K>) => void) | undefined) | undefined): {
|
|
20
|
+
emit: (event: TraceEventData<K>) => void;
|
|
21
|
+
nextBatchId: () => string;
|
|
22
|
+
};
|
|
23
|
+
//# 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;AAYD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAiBjE;AAED,wBAAgB,eAAe,CAC7B,IAAI,EAAE;IACJ,EAAE,EAAE;QAAE,QAAQ,IAAI,MAAM,CAAA;KAAE,CAAC;IAC3B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3B,EACD,UAAU,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC,GAAG,SAAS,KAAK,IAAI,GACvE,IAAI,CAON;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
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, registerBatcher } 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,15 @@ 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 creationStack = new Error().stack;
|
|
34
|
+
|
|
35
|
+
registerBatcher({ fn, name, stack: creationStack }, (emitter) => {
|
|
36
|
+
devtoolsEmitter = emitter;
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const tracer = createTracer(name, traceHandler, () => devtoolsEmitter);
|
|
31
40
|
|
|
32
41
|
const matchFn = normalizeMatch(match);
|
|
33
42
|
const isIndexedMatch = isIndexed(match);
|
|
@@ -38,6 +47,7 @@ export function batch<K, V>(
|
|
|
38
47
|
let cleanup: (() => void) | null = null;
|
|
39
48
|
let isScheduled = false;
|
|
40
49
|
let currentAbortController: AbortController | null = null;
|
|
50
|
+
let inFlightRequests: PendingRequest<K, V>[] = [];
|
|
41
51
|
|
|
42
52
|
function scheduleDispatch(): void {
|
|
43
53
|
if (isScheduled || queue.length === 0) return;
|
|
@@ -115,6 +125,8 @@ export function batch<K, V>(
|
|
|
115
125
|
|
|
116
126
|
if (uniqueKeys.length === 0) return;
|
|
117
127
|
|
|
128
|
+
inFlightRequests = chunk;
|
|
129
|
+
|
|
118
130
|
tracer.emit({
|
|
119
131
|
type: 'dispatch',
|
|
120
132
|
batchId,
|
|
@@ -210,6 +222,7 @@ export function batch<K, V>(
|
|
|
210
222
|
}
|
|
211
223
|
} finally {
|
|
212
224
|
currentAbortController = null;
|
|
225
|
+
inFlightRequests = [];
|
|
213
226
|
}
|
|
214
227
|
}
|
|
215
228
|
|
|
@@ -246,8 +259,15 @@ export function batch<K, V>(
|
|
|
246
259
|
request.aborted = true;
|
|
247
260
|
reject(new DOMException('Aborted', 'AbortError'));
|
|
248
261
|
|
|
249
|
-
const
|
|
250
|
-
|
|
262
|
+
const allPendingAborted = queue.every((r) => r.aborted);
|
|
263
|
+
const allInFlightAborted =
|
|
264
|
+
inFlightRequests.length > 0 &&
|
|
265
|
+
inFlightRequests.every((r) => r.aborted);
|
|
266
|
+
if (
|
|
267
|
+
allPendingAborted &&
|
|
268
|
+
allInFlightAborted &&
|
|
269
|
+
currentAbortController
|
|
270
|
+
) {
|
|
251
271
|
currentAbortController.abort();
|
|
252
272
|
}
|
|
253
273
|
};
|
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,69 @@
|
|
|
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
|
+
interface PendingBatcher {
|
|
12
|
+
fn: { toString(): string };
|
|
13
|
+
name: string | undefined;
|
|
14
|
+
stack: string | undefined;
|
|
15
|
+
setEmitter: (emitter: ((event: TraceEvent) => void) | undefined) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let devtoolsHook: DevtoolsHook | null = null;
|
|
19
|
+
const pendingBatchers: PendingBatcher[] = [];
|
|
20
|
+
|
|
21
|
+
export function __setDevtoolsHook(hook: DevtoolsHook | null): void {
|
|
22
|
+
devtoolsHook = hook;
|
|
23
|
+
|
|
24
|
+
if (hook && pendingBatchers.length > 0) {
|
|
25
|
+
for (const pending of pendingBatchers) {
|
|
26
|
+
const emitter = hook.onBatcherCreated({
|
|
27
|
+
fn: pending.fn,
|
|
28
|
+
name: pending.name,
|
|
29
|
+
stack: pending.stack,
|
|
30
|
+
});
|
|
31
|
+
pending.setEmitter(emitter);
|
|
32
|
+
}
|
|
33
|
+
pendingBatchers.length = 0;
|
|
34
|
+
} else if (!hook) {
|
|
35
|
+
// clear pending batchers when hook is removed
|
|
36
|
+
pendingBatchers.length = 0;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
9
39
|
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
40
|
+
export function registerBatcher(
|
|
41
|
+
info: {
|
|
42
|
+
fn: { toString(): string };
|
|
43
|
+
name: string | undefined;
|
|
44
|
+
stack: string | undefined;
|
|
45
|
+
},
|
|
46
|
+
setEmitter: (emitter: ((event: TraceEvent) => void) | undefined) => void,
|
|
47
|
+
): void {
|
|
48
|
+
if (devtoolsHook) {
|
|
49
|
+
const emitter = devtoolsHook.onBatcherCreated(info);
|
|
50
|
+
setEmitter(emitter);
|
|
51
|
+
} else {
|
|
52
|
+
pendingBatchers.push({ ...info, setEmitter });
|
|
14
53
|
}
|
|
15
|
-
return undefined;
|
|
16
54
|
}
|
|
17
55
|
|
|
18
56
|
export function createTracer<K>(
|
|
19
57
|
name: string | undefined,
|
|
20
58
|
handler: TraceHandler<K> | undefined,
|
|
59
|
+
getDevtoolsEmitter:
|
|
60
|
+
| (() => ((event: TraceEvent<K>) => void) | undefined)
|
|
61
|
+
| undefined,
|
|
21
62
|
) {
|
|
22
63
|
let batchCounter = 0;
|
|
23
|
-
let registeredWithDevtools = false;
|
|
24
64
|
|
|
25
65
|
function emit(event: TraceEventData<K>) {
|
|
26
66
|
const timestamp = performance.now();
|
|
27
|
-
|
|
28
67
|
const fullEvent = {
|
|
29
68
|
...event,
|
|
30
69
|
timestamp,
|
|
@@ -34,13 +73,9 @@ export function createTracer<K>(
|
|
|
34
73
|
handler(fullEvent);
|
|
35
74
|
}
|
|
36
75
|
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
|
|
40
|
-
devtools.register({ name, registeredAt: timestamp });
|
|
41
|
-
registeredWithDevtools = true;
|
|
42
|
-
}
|
|
43
|
-
devtools.emit(name, fullEvent);
|
|
76
|
+
const devtoolsEmitter = getDevtoolsEmitter?.();
|
|
77
|
+
if (devtoolsEmitter) {
|
|
78
|
+
devtoolsEmitter(fullEvent);
|
|
44
79
|
}
|
|
45
80
|
}
|
|
46
81
|
|