@lde/pipeline 0.43.0 → 0.45.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/distribution/importResolver.js +4 -4
- package/dist/distribution/resolver.d.ts +2 -2
- package/dist/distribution/resolver.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/pipeline.d.ts +6 -9
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +173 -71
- package/dist/pipelineEvent.d.ts +273 -0
- package/dist/pipelineEvent.d.ts.map +1 -0
- package/dist/pipelineEvent.js +35 -0
- package/dist/reporter.d.ts +14 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/sparql/reader.d.ts +16 -7
- package/dist/sparql/reader.d.ts.map +1 -1
- package/dist/sparql/reader.js +16 -5
- package/dist/stage.d.ts +19 -7
- package/dist/stage.d.ts.map +1 -1
- package/dist/stage.js +70 -51
- package/dist/stageTally.d.ts +56 -0
- package/dist/stageTally.d.ts.map +1 -0
- package/dist/stageTally.js +164 -0
- package/dist/unreadValueTally.d.ts +2 -2
- package/dist/unreadValueTally.d.ts.map +1 -1
- package/dist/unreadValueTally.js +1 -1
- package/dist/writer/writer.d.ts +1 -1
- package/dist/writer/writer.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/combineReporters.d.ts +0 -16
- package/dist/combineReporters.d.ts.map +0 -1
- package/dist/combineReporters.js +0 -57
- package/dist/progressReporter.d.ts +0 -157
- package/dist/progressReporter.d.ts.map +0 -1
- /package/dist/{progressReporter.js → reporter.js} +0 -0
package/dist/stage.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { NotSupported } from './sparql/reader.js';
|
|
1
|
+
import { NoItemsSelected, NotSupported } from './sparql/reader.js';
|
|
2
2
|
import { batch } from './batch.js';
|
|
3
3
|
import { AsyncQueue } from './asyncQueue.js';
|
|
4
|
+
import { StageTally } from './stageTally.js';
|
|
4
5
|
export class Stage {
|
|
5
6
|
name;
|
|
6
7
|
stages;
|
|
@@ -56,15 +57,21 @@ export class Stage {
|
|
|
56
57
|
const distribution = this.sourceFor
|
|
57
58
|
? this.sourceFor(dataset, resolved)
|
|
58
59
|
: resolved;
|
|
60
|
+
const tally = new StageTally(this.transformNames);
|
|
59
61
|
if (this.itemSelector) {
|
|
60
|
-
|
|
62
|
+
const result = await this.runWithSelector(tally.selected(this.itemSelector.select(distribution, this.batchSize, {
|
|
61
63
|
timeout,
|
|
62
|
-
}), dataset, distribution, writer, options);
|
|
64
|
+
})), dataset, distribution, writer, tally, options);
|
|
65
|
+
if (!(result instanceof NotSupported)) {
|
|
66
|
+
options?.onComplete?.(tally.summary());
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
63
69
|
}
|
|
64
|
-
const
|
|
65
|
-
if (
|
|
66
|
-
return
|
|
70
|
+
const chains = await this.readAll(dataset, distribution, timeout, tally);
|
|
71
|
+
if (chains instanceof NotSupported) {
|
|
72
|
+
return chains;
|
|
67
73
|
}
|
|
74
|
+
const streams = chains.map(({ stream }) => stream);
|
|
68
75
|
// The non-selector path has no batch, so `project` is forbidden here (the
|
|
69
76
|
// constructor rejects `project` without an `itemSelector`). It therefore
|
|
70
77
|
// only ever writes quads, and `Out` is `Quad` at runtime – tsc cannot see
|
|
@@ -73,6 +80,7 @@ export class Stage {
|
|
|
73
80
|
// Quads the readers produced (before any validation filtering); used to
|
|
74
81
|
// enforce `expectsOutput` below.
|
|
75
82
|
let produced = 0;
|
|
83
|
+
const write = (quads) => tally.written(quads, (timedQuads) => quadWriter.write(dataset, timedQuads));
|
|
76
84
|
if (this.validation) {
|
|
77
85
|
const buffer = [];
|
|
78
86
|
for (const stream of streams) {
|
|
@@ -84,16 +92,16 @@ export class Stage {
|
|
|
84
92
|
const onInvalid = this.validation.onInvalid ?? 'write';
|
|
85
93
|
if (onInvalid === 'write') {
|
|
86
94
|
await Promise.all([
|
|
87
|
-
|
|
95
|
+
write((async function* () {
|
|
88
96
|
yield* buffer;
|
|
89
97
|
})()),
|
|
90
|
-
this.validation.validator.validate(buffer, dataset),
|
|
98
|
+
tally.validated(this.validation.validator.validate(buffer, dataset)),
|
|
91
99
|
]);
|
|
92
100
|
}
|
|
93
101
|
else {
|
|
94
|
-
const accepted = await this.validateBuffer(buffer, dataset);
|
|
102
|
+
const accepted = await tally.validated(this.validateBuffer(buffer, dataset));
|
|
95
103
|
if (accepted.length > 0) {
|
|
96
|
-
await
|
|
104
|
+
await write((async function* () {
|
|
97
105
|
yield* accepted;
|
|
98
106
|
})());
|
|
99
107
|
}
|
|
@@ -101,15 +109,19 @@ export class Stage {
|
|
|
101
109
|
}
|
|
102
110
|
else if (this.expectsOutput) {
|
|
103
111
|
// Only thread the per-quad counter through when the count is actually
|
|
104
|
-
// needed
|
|
105
|
-
await
|
|
112
|
+
// needed.
|
|
113
|
+
await write(countQuads(mergeStreams(streams), (count) => {
|
|
106
114
|
produced = count;
|
|
107
115
|
}));
|
|
108
116
|
}
|
|
109
117
|
else {
|
|
110
|
-
await
|
|
118
|
+
await write(mergeStreams(streams));
|
|
119
|
+
}
|
|
120
|
+
for (const { settle } of chains) {
|
|
121
|
+
settle();
|
|
111
122
|
}
|
|
112
123
|
this.assertProduced(produced);
|
|
124
|
+
options?.onComplete?.(tally.summary());
|
|
113
125
|
}
|
|
114
126
|
/**
|
|
115
127
|
* Throw when {@link StageOptions.expectsOutput} is set but the stage produced
|
|
@@ -121,14 +133,14 @@ export class Stage {
|
|
|
121
133
|
throw new Error(`Stage '${this.name}' expected output but produced none`);
|
|
122
134
|
}
|
|
123
135
|
}
|
|
124
|
-
async runWithSelector(selector, dataset, distribution, writer, options) {
|
|
136
|
+
async runWithSelector(selector, dataset, distribution, writer, tally, options) {
|
|
125
137
|
// Peek the first batch to detect an empty selector before starting the
|
|
126
138
|
// writer (important because e.g. SparqlUpdateWriter does CLEAR GRAPH).
|
|
127
139
|
const batches = batch(selector, this.batchSize);
|
|
128
140
|
const iter = batches[Symbol.asyncIterator]();
|
|
129
141
|
const first = await iter.next();
|
|
130
142
|
if (first.done) {
|
|
131
|
-
return new
|
|
143
|
+
return new NoItemsSelected();
|
|
132
144
|
}
|
|
133
145
|
// Reconstruct a full iterable including the peeked first batch.
|
|
134
146
|
const allBatches = (async function* () {
|
|
@@ -178,19 +190,16 @@ export class Stage {
|
|
|
178
190
|
}
|
|
179
191
|
track((async () => {
|
|
180
192
|
// Run all readers for this batch in parallel.
|
|
181
|
-
const readerOutputs = await Promise.all(this.readers.map(async (
|
|
182
|
-
const result = await
|
|
183
|
-
bindings,
|
|
184
|
-
timeout: options?.timeout,
|
|
185
|
-
});
|
|
193
|
+
const readerOutputs = await Promise.all(this.readers.map(async (normalized) => {
|
|
194
|
+
const result = await this.read(normalized, dataset, distribution, { bindings, timeout: options?.timeout }, tally);
|
|
186
195
|
if (result instanceof NotSupported)
|
|
187
196
|
return [];
|
|
188
197
|
hasResults = true;
|
|
189
|
-
const stream = this.applyTransforms(transforms, result, dataset, distribution);
|
|
190
198
|
const quads = [];
|
|
191
|
-
for await (const quad of stream) {
|
|
199
|
+
for await (const quad of result.stream) {
|
|
192
200
|
quads.push(quad);
|
|
193
201
|
}
|
|
202
|
+
result.settle();
|
|
194
203
|
return quads;
|
|
195
204
|
}));
|
|
196
205
|
const batchQuads = readerOutputs.flat();
|
|
@@ -201,19 +210,21 @@ export class Stage {
|
|
|
201
210
|
if (this.validation &&
|
|
202
211
|
batchQuads.length > 0 &&
|
|
203
212
|
onInvalid !== 'write') {
|
|
204
|
-
acceptedQuads = await this.validateBuffer(batchQuads, dataset);
|
|
213
|
+
acceptedQuads = await tally.validated(this.validateBuffer(batchQuads, dataset));
|
|
205
214
|
}
|
|
206
215
|
// The one type-changing seam: project the root-complete batch into
|
|
207
216
|
// output items, or pass the quads through for a plain stage (`Out`
|
|
208
217
|
// is `Quad` at runtime when there is no projection).
|
|
209
|
-
const
|
|
210
|
-
|
|
218
|
+
const project = this.project;
|
|
219
|
+
const items = project
|
|
220
|
+
? tally.projected(() => project(acceptedQuads, {
|
|
211
221
|
dataset,
|
|
212
222
|
distribution,
|
|
213
223
|
stage: this.name,
|
|
214
224
|
bindings,
|
|
225
|
+
count: countIn(tally),
|
|
215
226
|
valueUnread: options?.valueUnread ?? ignoreUnreadValue,
|
|
216
|
-
})
|
|
227
|
+
}))
|
|
217
228
|
: acceptedQuads;
|
|
218
229
|
for await (const item of items) {
|
|
219
230
|
await queue.push(item);
|
|
@@ -222,7 +233,7 @@ export class Stage {
|
|
|
222
233
|
if (this.validation &&
|
|
223
234
|
batchQuads.length > 0 &&
|
|
224
235
|
onInvalid === 'write') {
|
|
225
|
-
pendingValidations.push(this.validation.validator.validate(batchQuads, dataset));
|
|
236
|
+
pendingValidations.push(tally.validated(this.validation.validator.validate(batchQuads, dataset)));
|
|
226
237
|
}
|
|
227
238
|
itemsProcessed += bindings.length;
|
|
228
239
|
options?.onProgress?.(itemsProcessed, quadsGenerated);
|
|
@@ -246,7 +257,7 @@ export class Stage {
|
|
|
246
257
|
const dispatchPromise = dispatch();
|
|
247
258
|
const writePromise = (async () => {
|
|
248
259
|
try {
|
|
249
|
-
await writer.write(dataset,
|
|
260
|
+
await tally.written(queue, (items) => writer.write(dataset, items));
|
|
250
261
|
}
|
|
251
262
|
catch (err) {
|
|
252
263
|
queue.abort(err);
|
|
@@ -275,49 +286,51 @@ export class Stage {
|
|
|
275
286
|
// 'skip': discard
|
|
276
287
|
return [];
|
|
277
288
|
}
|
|
278
|
-
async readAll(dataset, distribution, timeout) {
|
|
279
|
-
const results = await Promise.all(this.readers.map(
|
|
280
|
-
|
|
281
|
-
timeout,
|
|
282
|
-
});
|
|
283
|
-
if (result instanceof NotSupported)
|
|
284
|
-
return result;
|
|
285
|
-
return this.applyTransforms(transforms, result, dataset, distribution);
|
|
286
|
-
}));
|
|
287
|
-
const streams = [];
|
|
289
|
+
async readAll(dataset, distribution, timeout, tally) {
|
|
290
|
+
const results = await Promise.all(this.readers.map((normalized) => this.read(normalized, dataset, distribution, { timeout }, tally)));
|
|
291
|
+
const chains = [];
|
|
288
292
|
for (const result of results) {
|
|
289
293
|
if (!(result instanceof NotSupported)) {
|
|
290
|
-
|
|
294
|
+
chains.push(result);
|
|
291
295
|
}
|
|
292
296
|
}
|
|
293
|
-
if (
|
|
297
|
+
if (chains.length === 0) {
|
|
294
298
|
return new NotSupported('All readers returned NotSupported');
|
|
295
299
|
}
|
|
296
|
-
return
|
|
300
|
+
return chains;
|
|
297
301
|
}
|
|
298
302
|
/**
|
|
299
|
-
*
|
|
300
|
-
* supplying the {@link ReaderContext}
|
|
301
|
-
* call's output (see {@link AttachedReader});
|
|
302
|
-
*
|
|
303
|
+
* Run one reader and fold its attached transforms over its output, in order,
|
|
304
|
+
* supplying the {@link ReaderContext}, each layer timed by `tally`. A
|
|
305
|
+
* transform sees one `read()` call's output (see {@link AttachedReader});
|
|
306
|
+
* `NotSupported` is returned as is and never reaches a transform.
|
|
303
307
|
*/
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
308
|
+
async read({ reader, transforms, firstTransform }, dataset, distribution, readOptions, tally) {
|
|
309
|
+
const started = performance.now();
|
|
310
|
+
const result = await reader.read(dataset, distribution, readOptions);
|
|
311
|
+
tally.addRead(performance.now() - started);
|
|
312
|
+
if (result instanceof NotSupported)
|
|
313
|
+
return result;
|
|
307
314
|
const context = {
|
|
308
315
|
dataset,
|
|
309
316
|
distribution,
|
|
310
317
|
stage: this.name,
|
|
318
|
+
count: countIn(tally),
|
|
311
319
|
};
|
|
312
|
-
return transforms.
|
|
320
|
+
return tally.chain(result, transforms.map((transform) => (quads) => transform(quads, context)), firstTransform);
|
|
321
|
+
}
|
|
322
|
+
/** Every attached transform’s name, readers in order, for the phase tally. */
|
|
323
|
+
get transformNames() {
|
|
324
|
+
return this.readers.flatMap(({ transforms, firstTransform }) => transforms.map((transform, index) => transform.name || `transform ${firstTransform + index + 1}`));
|
|
313
325
|
}
|
|
314
326
|
}
|
|
315
327
|
/** Normalise the {@link StageReaders} union to reader + transforms pairs. */
|
|
316
328
|
function normalizeReaders(readers) {
|
|
317
329
|
const list = Array.isArray(readers) ? readers : [readers];
|
|
330
|
+
let firstTransform = 0;
|
|
318
331
|
return list.map((entry) => {
|
|
319
332
|
if ('read' in entry) {
|
|
320
|
-
return { reader: entry, transforms: [] };
|
|
333
|
+
return { reader: entry, transforms: [], firstTransform };
|
|
321
334
|
}
|
|
322
335
|
const { reader, transform } = entry;
|
|
323
336
|
const transforms = transform === undefined
|
|
@@ -325,7 +338,9 @@ function normalizeReaders(readers) {
|
|
|
325
338
|
: Array.isArray(transform)
|
|
326
339
|
? [...transform]
|
|
327
340
|
: [transform];
|
|
328
|
-
|
|
341
|
+
const normalized = { reader, transforms, firstTransform };
|
|
342
|
+
firstTransform += transforms.length;
|
|
343
|
+
return normalized;
|
|
329
344
|
});
|
|
330
345
|
}
|
|
331
346
|
async function* mergeStreams(streams) {
|
|
@@ -350,6 +365,10 @@ async function* countQuads(stream, onCount) {
|
|
|
350
365
|
}
|
|
351
366
|
onCount(count);
|
|
352
367
|
}
|
|
368
|
+
/** The {@link ReaderContext.count} that adds to `tally`. */
|
|
369
|
+
function countIn(tally) {
|
|
370
|
+
return (name, amount = 1) => tally.count(name, amount);
|
|
371
|
+
}
|
|
353
372
|
/** The {@link BatchContext.valueUnread} a stage run without a receiver hands
|
|
354
373
|
* its transform. */
|
|
355
374
|
function ignoreUnreadValue() {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { StageSummary } from './pipelineEvent.js';
|
|
2
|
+
/**
|
|
3
|
+
* Adds up, over every batch one {@link Stage.run} runs, where it spends its
|
|
4
|
+
* time, phase by phase, and what its transforms and projection count.
|
|
5
|
+
*
|
|
6
|
+
* A stage streams: a reader’s quads flow through its transforms into the
|
|
7
|
+
* writer, and every layer pulls from the one before it. Timing a layer from its
|
|
8
|
+
* first quad to its last would count the layers upstream of it too. So the tally
|
|
9
|
+
* times only the wait on each layer’s `next()`, and takes a layer’s own time as
|
|
10
|
+
* its wait minus the wait of the layer it pulls from. That holds as long as a
|
|
11
|
+
* layer pulls from upstream only while its own `next()` is pending, which an
|
|
12
|
+
* async generator does; a transform that prefetches in the background has its
|
|
13
|
+
* time undercounted, never below zero.
|
|
14
|
+
*/
|
|
15
|
+
export declare class StageTally {
|
|
16
|
+
private selectMs;
|
|
17
|
+
private readMs;
|
|
18
|
+
private validateMs;
|
|
19
|
+
private projectMs;
|
|
20
|
+
private writeMs;
|
|
21
|
+
private readonly transforms;
|
|
22
|
+
private readonly counts;
|
|
23
|
+
/** `transformNames` lists every attached transform, readers in order. */
|
|
24
|
+
constructor(transformNames: readonly string[]);
|
|
25
|
+
/** The item selector, timed while it is iterated. */
|
|
26
|
+
selected<T>(selector: AsyncIterable<T>): AsyncIterable<T>;
|
|
27
|
+
/** How long a reader took to answer, before its quads stream. */
|
|
28
|
+
addRead(milliseconds: number): void;
|
|
29
|
+
/**
|
|
30
|
+
* Time one reader’s output and the transforms folded over it. `apply` builds
|
|
31
|
+
* the next layer from the one before; `firstTransform` is the position of the
|
|
32
|
+
* reader’s first transform among all of the stage’s.
|
|
33
|
+
*
|
|
34
|
+
* Returns the timed stream and a `settle` to call once that stream is drained,
|
|
35
|
+
* which adds this chain’s times to the tally.
|
|
36
|
+
*/
|
|
37
|
+
chain<T>(source: AsyncIterable<T>, layers: readonly ((upstream: AsyncIterable<T>) => AsyncIterable<T>)[], firstTransform: number): {
|
|
38
|
+
stream: AsyncIterable<T>;
|
|
39
|
+
settle: () => void;
|
|
40
|
+
};
|
|
41
|
+
/** Time validating one buffer of quads. */
|
|
42
|
+
validated<T>(validation: Promise<T>): Promise<T>;
|
|
43
|
+
/** Time projecting one batch: building the items, then iterating them. */
|
|
44
|
+
projected<T>(project: () => Iterable<T> | AsyncIterable<T>): AsyncIterable<T>;
|
|
45
|
+
/**
|
|
46
|
+
* Time the writer: `write` is handed the items to write and runs to
|
|
47
|
+
* completion. The time it spent waiting on those items is not its own.
|
|
48
|
+
*/
|
|
49
|
+
written<T>(items: AsyncIterable<T>, write: (items: AsyncIterable<T>) => Promise<void>): Promise<void>;
|
|
50
|
+
/** Add `amount` to the count called `name`. */
|
|
51
|
+
count(name: string, amount: number): void;
|
|
52
|
+
/** What the stage counted, and its phases rounded to whole milliseconds. */
|
|
53
|
+
summary(): StageSummary;
|
|
54
|
+
private phases;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=stageTally.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stageTally.d.ts","sourceRoot":"","sources":["../src/stageTally.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,YAAY,EAEb,MAAM,oBAAoB,CAAC;AAE5B;;;;;;;;;;;;GAYG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,SAAS,CAAK;IACtB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsB;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IAErD,yEAAyE;gBAC7D,cAAc,EAAE,SAAS,MAAM,EAAE;IAI7C,qDAAqD;IACrD,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC;IAMzD,iEAAiE;IACjE,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAInC;;;;;;;OAOG;IACH,KAAK,CAAC,CAAC,EACL,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,EACxB,MAAM,EAAE,SAAS,CAAC,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC,CAAC,EAAE,EACrE,cAAc,EAAE,MAAM,GACrB;QAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;QAAC,MAAM,EAAE,MAAM,IAAI,CAAA;KAAE;IA6BnD,2CAA2C;IACrC,SAAS,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAStD,0EAA0E;IAC1E,SAAS,CAAC,CAAC,EACT,OAAO,EAAE,MAAM,QAAQ,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,GAC5C,aAAa,CAAC,CAAC,CAAC;IASnB;;;OAGG;IACG,OAAO,CAAC,CAAC,EACb,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,EACvB,KAAK,EAAE,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAChD,OAAO,CAAC,IAAI,CAAC;IAchB,+CAA+C;IAC/C,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAIzC,4EAA4E;IAC5E,OAAO,IAAI,YAAY;IAIvB,OAAO,CAAC,MAAM;CAaf"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adds up, over every batch one {@link Stage.run} runs, where it spends its
|
|
3
|
+
* time, phase by phase, and what its transforms and projection count.
|
|
4
|
+
*
|
|
5
|
+
* A stage streams: a reader’s quads flow through its transforms into the
|
|
6
|
+
* writer, and every layer pulls from the one before it. Timing a layer from its
|
|
7
|
+
* first quad to its last would count the layers upstream of it too. So the tally
|
|
8
|
+
* times only the wait on each layer’s `next()`, and takes a layer’s own time as
|
|
9
|
+
* its wait minus the wait of the layer it pulls from. That holds as long as a
|
|
10
|
+
* layer pulls from upstream only while its own `next()` is pending, which an
|
|
11
|
+
* async generator does; a transform that prefetches in the background has its
|
|
12
|
+
* time undercounted, never below zero.
|
|
13
|
+
*/
|
|
14
|
+
export class StageTally {
|
|
15
|
+
selectMs = 0;
|
|
16
|
+
readMs = 0;
|
|
17
|
+
validateMs = 0;
|
|
18
|
+
projectMs = 0;
|
|
19
|
+
writeMs = 0;
|
|
20
|
+
transforms;
|
|
21
|
+
counts = {};
|
|
22
|
+
/** `transformNames` lists every attached transform, readers in order. */
|
|
23
|
+
constructor(transformNames) {
|
|
24
|
+
this.transforms = transformNames.map((name) => ({ name, durationMs: 0 }));
|
|
25
|
+
}
|
|
26
|
+
/** The item selector, timed while it is iterated. */
|
|
27
|
+
selected(selector) {
|
|
28
|
+
return timed(selector, (milliseconds) => {
|
|
29
|
+
this.selectMs += milliseconds;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/** How long a reader took to answer, before its quads stream. */
|
|
33
|
+
addRead(milliseconds) {
|
|
34
|
+
this.readMs += milliseconds;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Time one reader’s output and the transforms folded over it. `apply` builds
|
|
38
|
+
* the next layer from the one before; `firstTransform` is the position of the
|
|
39
|
+
* reader’s first transform among all of the stage’s.
|
|
40
|
+
*
|
|
41
|
+
* Returns the timed stream and a `settle` to call once that stream is drained,
|
|
42
|
+
* which adds this chain’s times to the tally.
|
|
43
|
+
*/
|
|
44
|
+
chain(source, layers, firstTransform) {
|
|
45
|
+
// waits[0] is the reader’s, waits[k] the k-th transform’s, each including
|
|
46
|
+
// the waits before it.
|
|
47
|
+
const waits = layers.map(() => 0).concat(0);
|
|
48
|
+
let stream = timed(source, (milliseconds) => {
|
|
49
|
+
waits[0] += milliseconds;
|
|
50
|
+
});
|
|
51
|
+
// A transform’s own work before it returns its stream is its time alone.
|
|
52
|
+
const built = layers.map(() => 0);
|
|
53
|
+
layers.forEach((layer, index) => {
|
|
54
|
+
const started = performance.now();
|
|
55
|
+
const upstream = layer(stream);
|
|
56
|
+
built[index] = performance.now() - started;
|
|
57
|
+
stream = timed(upstream, (milliseconds) => {
|
|
58
|
+
waits[index + 1] += milliseconds;
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
stream,
|
|
63
|
+
settle: () => {
|
|
64
|
+
this.readMs += waits[0];
|
|
65
|
+
for (let index = 0; index < layers.length; index++) {
|
|
66
|
+
this.transforms[firstTransform + index].durationMs +=
|
|
67
|
+
built[index] + Math.max(0, waits[index + 1] - waits[index]);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** Time validating one buffer of quads. */
|
|
73
|
+
async validated(validation) {
|
|
74
|
+
const started = performance.now();
|
|
75
|
+
try {
|
|
76
|
+
return await validation;
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
this.validateMs += performance.now() - started;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** Time projecting one batch: building the items, then iterating them. */
|
|
83
|
+
projected(project) {
|
|
84
|
+
const started = performance.now();
|
|
85
|
+
const items = project();
|
|
86
|
+
this.projectMs += performance.now() - started;
|
|
87
|
+
return timed(items, (milliseconds) => {
|
|
88
|
+
this.projectMs += milliseconds;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Time the writer: `write` is handed the items to write and runs to
|
|
93
|
+
* completion. The time it spent waiting on those items is not its own.
|
|
94
|
+
*/
|
|
95
|
+
async written(items, write) {
|
|
96
|
+
let waitedMs = 0;
|
|
97
|
+
const started = performance.now();
|
|
98
|
+
try {
|
|
99
|
+
await write(timed(items, (milliseconds) => {
|
|
100
|
+
waitedMs += milliseconds;
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
this.writeMs += Math.max(0, performance.now() - started - waitedMs);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** Add `amount` to the count called `name`. */
|
|
108
|
+
count(name, amount) {
|
|
109
|
+
this.counts[name] = (this.counts[name] ?? 0) + amount;
|
|
110
|
+
}
|
|
111
|
+
/** What the stage counted, and its phases rounded to whole milliseconds. */
|
|
112
|
+
summary() {
|
|
113
|
+
return { phases: this.phases(), counts: { ...this.counts } };
|
|
114
|
+
}
|
|
115
|
+
phases() {
|
|
116
|
+
return {
|
|
117
|
+
selectMs: Math.round(this.selectMs),
|
|
118
|
+
readMs: Math.round(this.readMs),
|
|
119
|
+
transforms: [...sumByName(this.transforms)].map(([name, durationMs]) => ({
|
|
120
|
+
name,
|
|
121
|
+
durationMs: Math.round(durationMs),
|
|
122
|
+
})),
|
|
123
|
+
validateMs: Math.round(this.validateMs),
|
|
124
|
+
projectMs: Math.round(this.projectMs),
|
|
125
|
+
writeMs: Math.round(this.writeMs),
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/** Add up the durations of transforms sharing a name, in order first seen. */
|
|
130
|
+
function sumByName(transforms) {
|
|
131
|
+
const sums = new Map();
|
|
132
|
+
for (const { name, durationMs } of transforms) {
|
|
133
|
+
sums.set(name, (sums.get(name) ?? 0) + durationMs);
|
|
134
|
+
}
|
|
135
|
+
return sums;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Pass `source` through unchanged, reporting each wait on its `next()` to
|
|
139
|
+
* `onWait`. Accepts a plain iterable too, for a projection that returns an
|
|
140
|
+
* array.
|
|
141
|
+
*/
|
|
142
|
+
function timed(source, onWait) {
|
|
143
|
+
return {
|
|
144
|
+
[Symbol.asyncIterator]() {
|
|
145
|
+
const iterator = Symbol.asyncIterator in source
|
|
146
|
+
? source[Symbol.asyncIterator]()
|
|
147
|
+
: source[Symbol.iterator]();
|
|
148
|
+
return {
|
|
149
|
+
async next() {
|
|
150
|
+
const started = performance.now();
|
|
151
|
+
try {
|
|
152
|
+
return await iterator.next();
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
onWait(performance.now() - started);
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
async return(value) {
|
|
159
|
+
return (await iterator.return?.(value)) ?? { done: true, value };
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { UnreadValue, ValuesUnread } from './
|
|
1
|
+
import type { UnreadValue, ValuesUnread } from './pipelineEvent.js';
|
|
2
2
|
/**
|
|
3
3
|
* The values a dataset’s stages could not read, tallied per (target, field)
|
|
4
|
-
* for
|
|
4
|
+
* for the `valuesUnread` event. Bounded by the schema, not the
|
|
5
5
|
* data: one entry per field that rejected anything, each holding a count and
|
|
6
6
|
* at most {@link MAX_EXAMPLES} distinct examples.
|
|
7
7
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unreadValueTally.d.ts","sourceRoot":"","sources":["../src/unreadValueTally.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"unreadValueTally.d.ts","sourceRoot":"","sources":["../src/unreadValueTally.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAKpE;;;;;;;GAOG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAGpB;IAEJ,0DAA0D;IAC1D,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI;IAiB7D,mEAAmE;IACnE,OAAO,IAAI,SAAS,YAAY,EAAE;CAGnC"}
|
package/dist/unreadValueTally.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const MAX_EXAMPLES = 5;
|
|
3
3
|
/**
|
|
4
4
|
* The values a dataset’s stages could not read, tallied per (target, field)
|
|
5
|
-
* for
|
|
5
|
+
* for the `valuesUnread` event. Bounded by the schema, not the
|
|
6
6
|
* data: one entry per field that rejected anything, each holding a count and
|
|
7
7
|
* at most {@link MAX_EXAMPLES} distinct examples.
|
|
8
8
|
*
|
package/dist/writer/writer.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Dataset } from '@lde/dataset';
|
|
2
2
|
import type { Quad } from '@rdfjs/types';
|
|
3
3
|
import type { ProvenanceStore } from '../provenance/store.js';
|
|
4
|
-
import type { ItemsRejected } from '../
|
|
4
|
+
import type { ItemsRejected } from '../pipelineEvent.js';
|
|
5
5
|
/**
|
|
6
6
|
* Context for one pipeline run, handed to {@link Writer.openRun}.
|
|
7
7
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../src/writer/writer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../src/writer/writer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB;;;;OAIG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,eAAe,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpC;;;;;;;OAOG;IACH,aAAa,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,GAAG,IAAI,CAAC;IACjE;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAC;CACvC;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa,CAAC,IAAI,GAAG,IAAI;IACxC;;;;;;OAMG;IACH,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpE;AAED;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,SAAS,GAAG,QAAQ,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS,CAAC,IAAI,GAAG,IAAI,CAAE,SAAQ,aAAa,CAAC,IAAI,CAAC;IACjE;;;;;;;;;;OAUG;IACH,KAAK,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjE;;;;;;;;;OASG;IACH,KAAK,CAAC,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAExC;;;;;;;;;OASG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAExB;;;;;;OAMG;IACH,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErC;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,CAAC,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzC;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,MAAM,CAAC,IAAI,GAAG,IAAI;IACjC;;;;OAIG;IACH,OAAO,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;CACxD"}
|
package/package.json
CHANGED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { ProgressReporter } from './progressReporter.js';
|
|
2
|
-
/**
|
|
3
|
-
* Combine several {@link ProgressReporter}s into one that forwards every
|
|
4
|
-
* lifecycle call to each child that implements it. Lets a single run be
|
|
5
|
-
* observed by more than one reporter – e.g. a console reporter alongside a
|
|
6
|
-
* verdict-collecting one.
|
|
7
|
-
*
|
|
8
|
-
* Each method is dispatched to the children in array order; a child that does
|
|
9
|
-
* not implement a given (optional) method is skipped for that call.
|
|
10
|
-
*
|
|
11
|
-
* Internal to the package: not re-exported from `index.ts`. {@link Pipeline}
|
|
12
|
-
* uses it to normalise a `reporter` array into the single reporter its call
|
|
13
|
-
* sites expect, so the broader API need not grow a new public symbol.
|
|
14
|
-
*/
|
|
15
|
-
export declare function combineReporters(reporters: readonly ProgressReporter[]): ProgressReporter;
|
|
16
|
-
//# sourceMappingURL=combineReporters.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"combineReporters.d.ts","sourceRoot":"","sources":["../src/combineReporters.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAU9D;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,SAAS,gBAAgB,EAAE,GACrC,gBAAgB,CAiDlB"}
|
package/dist/combineReporters.js
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Combine several {@link ProgressReporter}s into one that forwards every
|
|
3
|
-
* lifecycle call to each child that implements it. Lets a single run be
|
|
4
|
-
* observed by more than one reporter – e.g. a console reporter alongside a
|
|
5
|
-
* verdict-collecting one.
|
|
6
|
-
*
|
|
7
|
-
* Each method is dispatched to the children in array order; a child that does
|
|
8
|
-
* not implement a given (optional) method is skipped for that call.
|
|
9
|
-
*
|
|
10
|
-
* Internal to the package: not re-exported from `index.ts`. {@link Pipeline}
|
|
11
|
-
* uses it to normalise a `reporter` array into the single reporter its call
|
|
12
|
-
* sites expect, so the broader API need not grow a new public symbol.
|
|
13
|
-
*/
|
|
14
|
-
export function combineReporters(reporters) {
|
|
15
|
-
const forward = (method, ...args) => {
|
|
16
|
-
for (const reporter of reporters) {
|
|
17
|
-
// Cast to the concrete signature for `method`: indexing by a generic key
|
|
18
|
-
// yields a union of method types TS won't call directly, even though the
|
|
19
|
-
// arguments are correlated.
|
|
20
|
-
const handler = reporter[method];
|
|
21
|
-
// Every method is optional; notify only the children that implement it.
|
|
22
|
-
// Invoke through `call` so a class-based reporter keeps its `this`: a bare
|
|
23
|
-
// `handler?.(...)` would drop the receiver and break methods that touch
|
|
24
|
-
// instance state (e.g. ConsoleReporter’s spinner).
|
|
25
|
-
handler?.call(reporter, ...args);
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
// Listing every method explicitly (rather than a Proxy) keeps the forwarding
|
|
29
|
-
// type-safe: typing the result as `Required<ProgressReporter>` forces a new
|
|
30
|
-
// entry here whenever the interface grows, so a forgotten method fails to
|
|
31
|
-
// compile instead of silently going unforwarded.
|
|
32
|
-
const combined = {
|
|
33
|
-
pipelineStart: (...args) => forward('pipelineStart', ...args),
|
|
34
|
-
datasetsSelected: (...args) => forward('datasetsSelected', ...args),
|
|
35
|
-
selectionEmpty: (...args) => forward('selectionEmpty', ...args),
|
|
36
|
-
datasetStart: (...args) => forward('datasetStart', ...args),
|
|
37
|
-
distributionProbed: (...args) => forward('distributionProbed', ...args),
|
|
38
|
-
importStarted: (...args) => forward('importStarted', ...args),
|
|
39
|
-
importFailed: (...args) => forward('importFailed', ...args),
|
|
40
|
-
distributionValidated: (...args) => forward('distributionValidated', ...args),
|
|
41
|
-
distributionSelected: (...args) => forward('distributionSelected', ...args),
|
|
42
|
-
stageStart: (...args) => forward('stageStart', ...args),
|
|
43
|
-
stageProgress: (...args) => forward('stageProgress', ...args),
|
|
44
|
-
stageComplete: (...args) => forward('stageComplete', ...args),
|
|
45
|
-
stageFailed: (...args) => forward('stageFailed', ...args),
|
|
46
|
-
stageSkipped: (...args) => forward('stageSkipped', ...args),
|
|
47
|
-
datasetValidated: (...args) => forward('datasetValidated', ...args),
|
|
48
|
-
datasetComplete: (...args) => forward('datasetComplete', ...args),
|
|
49
|
-
datasetSkipped: (...args) => forward('datasetSkipped', ...args),
|
|
50
|
-
itemsRejected: (...args) => forward('itemsRejected', ...args),
|
|
51
|
-
valuesUnread: (...args) => forward('valuesUnread', ...args),
|
|
52
|
-
pipelineComplete: (...args) => forward('pipelineComplete', ...args),
|
|
53
|
-
timeoutTightened: (...args) => forward('timeoutTightened', ...args),
|
|
54
|
-
timeoutRelaxed: (...args) => forward('timeoutRelaxed', ...args),
|
|
55
|
-
};
|
|
56
|
-
return combined;
|
|
57
|
-
}
|