@lde/pipeline-void 0.28.4 → 0.28.5

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
@@ -10,14 +10,16 @@ Returns all VoID stages in their recommended execution order. The ordering is op
10
10
 
11
11
  Accepts an optional `VoidStagesOptions` object:
12
12
 
13
- | Option | Default | Description |
14
- | ---------------- | ------- | --------------------------------------------------------------------- |
15
- | `timeout` | 60 000 | SPARQL query timeout in milliseconds |
16
- | `batchSize` | 10 | Maximum class bindings per executor call (per-class stages only) |
17
- | `maxConcurrency` | 10 | Maximum concurrent in-flight executor batches (per-class stages only) |
18
- | `perClass` | — | Override per-class iteration for all five per-class stages |
19
- | `uriSpaces` | — | When provided, includes the object URI space stage |
20
- | `vocabularies` | — | Additional vocabulary namespace URIs to detect beyond the built-in defaults |
13
+ | Option | Default | Description |
14
+ | ---------------- | ------- | --------------------------------------------------------------------------------------------------------------- |
15
+ | `batchSize` | 10 | Maximum class bindings per executor call (per-class stages only) |
16
+ | `maxConcurrency` | 10 | Maximum concurrent in-flight executor batches (per-class stages only) |
17
+ | `perClass` | | Override per-class iteration for all five per-class stages |
18
+ | `uriSpaces` | — | When provided, includes the object URI space stage |
19
+ | `vocabularies` | — | Additional vocabulary namespace URIs to detect beyond the built-in defaults |
20
+ | `transforms` | — | Transforms to attach to bundled stages, keyed by `VOID_STAGE_NAMES` (see [Stage transforms](#stage-transforms)) |
21
+
22
+ Per-request timeouts are configured at the `Pipeline` level via `PipelineOptions.timeout`, not per VoID stage.
21
23
 
22
24
  ```typescript
23
25
  import { voidStages } from '@lde/pipeline-void';
@@ -37,7 +39,7 @@ await new Pipeline({
37
39
 
38
40
  ### Individual stage factories
39
41
 
40
- Global and domain-specific factories accept `VoidStageOptions` (`timeout`) and return `Promise<Stage>`. Per-class factories accept `PerClassVoidStageOptions` (`timeout`, `batchSize`, `maxConcurrency`, `perClass`) — they default `perClass` to `true`; set it to `false` to run them as monolithic queries instead.
42
+ Global and domain-specific factories accept `VoidStageOptions` (`transform`) and return `Promise<Stage>`. Per-class factories accept `PerClassVoidStageOptions` (`transform`, `batchSize`, `maxConcurrency`, `perClass`) — they default `perClass` to `true`; set it to `false` to run them as monolithic queries instead.
41
43
 
42
44
  #### Global stages (one CONSTRUCT query per dataset):
43
45
 
@@ -65,12 +67,40 @@ Global and domain-specific factories accept `VoidStageOptions` (`timeout`) and r
65
67
 
66
68
  #### Domain-specific stages:
67
69
 
68
- | Factory | Description |
69
- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
70
+ | Factory | Description |
71
+ | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
70
72
  | `detectVocabularies()` | [`entity-properties.rq`](queries/entity-properties.rq) — Entity properties with automatic `void:vocabulary` detection. Accepts `DetectVocabulariesOptions` with an optional `vocabularies` array to extend the built-in defaults. |
71
- | `uriSpaces(uriSpaceMap)` | [`object-uri-space.rq`](queries/object-uri-space.rq) — Object URI namespace linksets, aggregated against a provided URI space map |
73
+ | `uriSpaces(uriSpaceMap)` | [`object-uri-space.rq`](queries/object-uri-space.rq) — Object URI namespace linksets, aggregated against a provided URI space map |
74
+
75
+ ## Stage transforms
76
+
77
+ A VoID stage decorates its executor’s output with a `QuadTransform<ExecutorContext>` attached as data (see [@lde/pipeline](../pipeline)’s extension model and [ADR 2](../../docs/decisions/0002-unify-pipeline-extension-on-quad-transforms.md)). It runs once per executor call and may fire its own SPARQL queries against the `distribution` in scope — so write it to accept being called more than once: a global stage calls it once over the complete output, a per-class stage with batching enabled once per batch (one class at `batchSize: 1`).
78
+
79
+ Two transform factories are built in:
72
80
 
73
- ## Executor decorators
81
+ - `withVocabularies(vocabularies?)` — passes through all quads and appends `void:vocabulary` triples for detected vocabulary namespace prefixes in `void:property` quads. The built-in defaults are exported as `defaultVocabularies` (sourced from `@zazuko/prefixes`); `detectVocabularies()` attaches it to the `entity-properties.rq` stage.
82
+ - `withUriSpaces(uriSpaceMap)` — consumes `void:Linkset` quads, matches each `void:objectsTarget` against the configured URI space prefixes using `startsWith`, and aggregates triple counts per matched space. Emits `void:objectsTarget` pointing to the target dataset IRI (taken from the metadata quad subjects), not the raw prefix; unmatched linksets are discarded. `uriSpaces(uriSpaceMap)` attaches it to the `object-uri-space.rq` stage.
74
83
 
75
- - `VocabularyExecutor` Wraps an executor; detects known vocabulary namespace prefixes in `void:property` quads and appends `void:vocabulary` triples. The built-in defaults are exported as `defaultVocabularies` (sourced from `@zazuko/prefixes`).
76
- - `UriSpaceExecutor` — Wraps an executor; consumes `void:Linkset` quads, matches each `void:objectsTarget` against configured URI space prefixes using `startsWith`, and aggregates triple counts per matched space. Emits `void:objectsTarget` pointing to the target dataset IRI (taken from the metadata quad subjects), not the raw prefix. Unmatched linksets are discarded.
84
+ ### Attaching your own transform
85
+
86
+ Pass a `transform` to an individual factory, or route transforms through `voidStages` with the `transforms` map keyed by `VOID_STAGE_NAMES` — so you can decorate a stage you never construct. Where a stage already carries a built-in transform, your transform composes after it. An invalid stage name is a compile error.
87
+
88
+ ```typescript
89
+ import { voidStages, VOID_STAGE_NAMES } from '@lde/pipeline-void';
90
+ import type { ExecutorContext, QuadTransform } from '@lde/pipeline-void';
91
+
92
+ const sampleSubjects: QuadTransform<ExecutorContext> = async function* (
93
+ quads,
94
+ { dataset, distribution },
95
+ ) {
96
+ yield* quads; // pass the stage’s subsets through unchanged …
97
+ // … then fire a sample SELECT against `distribution` and append measurements.
98
+ };
99
+
100
+ const stages = await voidStages({
101
+ batchSize: 1,
102
+ transforms: {
103
+ [VOID_STAGE_NAMES.subjectUriSpace]: sampleSubjects,
104
+ },
105
+ });
106
+ ```
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { Stage, NotSupported } from '@lde/pipeline';
2
+ export type { AttachedExecutor, ExecutorContext, QuadTransform, } from '@lde/pipeline';
2
3
  export * from './stage.js';
3
- export * from './vocabularyAnalyzer.js';
4
- export * from './uriSpaceExecutor.js';
4
+ export * from './vocabularyTransform.js';
5
+ export * from './uriSpaceTransform.js';
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpD,cAAc,YAAY,CAAC;AAC3B,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AACpD,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { Stage, NotSupported } from '@lde/pipeline';
2
2
  export * from './stage.js';
3
- export * from './vocabularyAnalyzer.js';
4
- export * from './uriSpaceExecutor.js';
3
+ export * from './vocabularyTransform.js';
4
+ export * from './uriSpaceTransform.js';
package/dist/stage.d.ts CHANGED
@@ -1,5 +1,34 @@
1
- import { Stage } from '@lde/pipeline';
1
+ import { Stage, type ExecutorContext, type QuadTransform } from '@lde/pipeline';
2
2
  import type { Quad } from '@rdfjs/types';
3
+ /**
4
+ * Stable names for every VoID stage, equal to the underlying query filename.
5
+ *
6
+ * Consumers reference these constants – e.g. when routing a transform through
7
+ * {@link VoidStagesOptions.transforms} – instead of hard-coding `.rq`
8
+ * filenames, so internal query names never leak into consumer code.
9
+ */
10
+ export declare const VOID_STAGE_NAMES: {
11
+ readonly subjects: "subjects.rq";
12
+ readonly properties: "properties.rq";
13
+ readonly objectLiterals: "object-literals.rq";
14
+ readonly objectUris: "object-uris.rq";
15
+ readonly datatypes: "datatypes.rq";
16
+ readonly triples: "triples.rq";
17
+ readonly classPartitions: "class-partition.rq";
18
+ readonly classPropertySubjects: "class-properties-subjects.rq";
19
+ readonly classPropertyObjects: "class-properties-objects.rq";
20
+ readonly perClassDatatypes: "class-property-datatypes.rq";
21
+ readonly perClassObjectClasses: "class-property-object-classes.rq";
22
+ readonly perClassLanguages: "class-property-languages.rq";
23
+ readonly licenses: "licenses.rq";
24
+ readonly vocabularies: "entity-properties.rq";
25
+ readonly subjectUriSpace: "subject-uri-space.rq";
26
+ readonly objectUriSpace: "object-uri-space.rq";
27
+ };
28
+ /** The name of a VoID stage. @see VOID_STAGE_NAMES */
29
+ export type VoidStageName = (typeof VOID_STAGE_NAMES)[keyof typeof VOID_STAGE_NAMES];
30
+ /** A transform, or transforms, decorating a VoID stage's executor output. */
31
+ export type VoidStageTransform = QuadTransform<ExecutorContext> | QuadTransform<ExecutorContext>[];
3
32
  /**
4
33
  * Options for configuring VoID stage execution.
5
34
  *
@@ -9,6 +38,14 @@ import type { Quad } from '@rdfjs/types';
9
38
  * extend it as more knobs are added.
10
39
  */
11
40
  export interface VoidStageOptions {
41
+ /**
42
+ * Transform(s) decorating this stage's executor output before the stage
43
+ * merges executors. For a global stage the transform sees the executor's
44
+ * complete output; for a per-class stage it sees one batch – one class at
45
+ * `batchSize: 1`. Built-in transforms (e.g. {@link uriSpaces}) compose
46
+ * with these, built-in first.
47
+ */
48
+ transform?: VoidStageTransform;
12
49
  }
13
50
  /**
14
51
  * Options for per-class VoID stages that iterate over classes.
@@ -26,12 +63,25 @@ export interface PerClassVoidStageOptions extends VoidStageOptions {
26
63
  }
27
64
  /**
28
65
  * Options for the {@link voidStages} convenience function.
66
+ *
67
+ * The single-stage `transform` seam is intentionally absent here: a bundle
68
+ * spans many stages, so transforms are routed per stage via
69
+ * {@link VoidStagesOptions.transforms}.
29
70
  */
30
- export interface VoidStagesOptions extends PerClassVoidStageOptions {
71
+ export interface VoidStagesOptions extends Omit<PerClassVoidStageOptions, 'transform'> {
31
72
  /** When provided, includes the object URI space stage using this map. */
32
73
  uriSpaces?: ReadonlyMap<string, readonly Quad[]>;
33
74
  /** Additional vocabulary namespace URIs to detect beyond the built-in defaults. */
34
75
  vocabularies?: readonly string[];
76
+ /**
77
+ * Transforms to attach to bundled stages, keyed by {@link VOID_STAGE_NAMES}.
78
+ *
79
+ * Each transform decorates the executor of the named stage – so a consumer
80
+ * can wrap a stage it never constructs. Where a stage already carries a
81
+ * built-in transform ({@link uriSpaces}, {@link detectVocabularies}), the
82
+ * consumer transform composes after it. An invalid key is a compile error.
83
+ */
84
+ transforms?: Partial<Record<VoidStageName, VoidStageTransform>>;
35
85
  }
36
86
  export declare function subjectUriSpaces(options?: VoidStageOptions): Promise<Stage>;
37
87
  export declare function classPartitions(options?: VoidStageOptions): Promise<Stage>;
@@ -1 +1 @@
1
- {"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../src/stage.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EAMN,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAezC;;;;;;;GAOG;AAEH,MAAM,WAAW,gBAAgB;CAAG;AAEpC;;;;;GAKG;AACH,MAAM,WAAW,wBAAyB,SAAQ,gBAAgB;IAChE,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,wBAAwB;IACjE,yEAAyE;IACzE,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,IAAI,EAAE,CAAC,CAAC;IACjD,mFAAmF;IACnF,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AA0DD,wBAAgB,gBAAgB,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,KAAK,CAAC,CAE3E;AAED,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,KAAK,CAAC,CAE1E;AAED,wBAAgB,mBAAmB,CACjC,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,KAAK,CAAC,CAEhB;AAED,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,KAAK,CAAC,CAE1E;AAED,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,KAAK,CAAC,CAE1E;AAED,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,KAAK,CAAC,CAExE;AAED,wBAAgB,YAAY,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,KAAK,CAAC,CAEvE;AAED,wBAAgB,qBAAqB,CACnC,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,KAAK,CAAC,CAKhB;AAED,wBAAgB,oBAAoB,CAClC,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,KAAK,CAAC,CAKhB;AAED,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,KAAK,CAAC,CAEzE;AAED,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,KAAK,CAAC,CAEzE;AAID,wBAAgB,qBAAqB,CACnC,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,KAAK,CAAC,CAKhB;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,KAAK,CAAC,CAKhB;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,KAAK,CAAC,CAKhB;AAID,wBAAgB,SAAS,CACvB,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,IAAI,EAAE,CAAC,EACjD,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,KAAK,CAAC,CAMhB;AAED,MAAM,WAAW,yBAA0B,SAAQ,gBAAgB;IACjE,mFAAmF;IACnF,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED,wBAAgB,kBAAkB,CAChC,OAAO,CAAC,EAAE,yBAAyB,GAClC,OAAO,CAAC,KAAK,CAAC,CAWhB;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAC9B,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,KAAK,EAAE,CAAC,CAgClB"}
1
+ {"version":3,"file":"stage.d.ts","sourceRoot":"","sources":["../src/stage.ts"],"names":[],"mappings":"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"}
package/dist/stage.js CHANGED
@@ -2,26 +2,54 @@ import { Stage, SparqlConstructExecutor, SparqlItemSelector, readQueryFile, } fr
2
2
  import { assertSafeIri } from '@lde/dataset';
3
3
  import { resolve, dirname } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
- import { VocabularyExecutor, defaultVocabularies, } from './vocabularyAnalyzer.js';
6
- import { UriSpaceExecutor } from './uriSpaceExecutor.js';
5
+ import { withVocabularies, defaultVocabularies, } from './vocabularyTransform.js';
6
+ import { withUriSpaces } from './uriSpaceTransform.js';
7
7
  const queriesDir = resolve(dirname(fileURLToPath(import.meta.url)), '..', 'queries');
8
+ /**
9
+ * Stable names for every VoID stage, equal to the underlying query filename.
10
+ *
11
+ * Consumers reference these constants – e.g. when routing a transform through
12
+ * {@link VoidStagesOptions.transforms} – instead of hard-coding `.rq`
13
+ * filenames, so internal query names never leak into consumer code.
14
+ */
15
+ export const VOID_STAGE_NAMES = {
16
+ subjects: 'subjects.rq',
17
+ properties: 'properties.rq',
18
+ objectLiterals: 'object-literals.rq',
19
+ objectUris: 'object-uris.rq',
20
+ datatypes: 'datatypes.rq',
21
+ triples: 'triples.rq',
22
+ classPartitions: 'class-partition.rq',
23
+ classPropertySubjects: 'class-properties-subjects.rq',
24
+ classPropertyObjects: 'class-properties-objects.rq',
25
+ perClassDatatypes: 'class-property-datatypes.rq',
26
+ perClassObjectClasses: 'class-property-object-classes.rq',
27
+ perClassLanguages: 'class-property-languages.rq',
28
+ licenses: 'licenses.rq',
29
+ vocabularies: 'entity-properties.rq',
30
+ subjectUriSpace: 'subject-uri-space.rq',
31
+ objectUriSpace: 'object-uri-space.rq',
32
+ };
8
33
  async function createVoidStage(filename, options) {
9
34
  const query = await readQueryFile(resolve(queriesDir, filename));
10
- const executor = options?.executor?.(query) ?? new SparqlConstructExecutor({ query });
11
- if (options?.perClass) {
12
- return new Stage({
13
- name: filename,
14
- itemSelector: classSelector(),
15
- executors: executor,
16
- batchSize: options?.batchSize,
17
- maxConcurrency: options?.maxConcurrency,
18
- });
19
- }
35
+ const executor = {
36
+ executor: new SparqlConstructExecutor({ query }),
37
+ transform: options?.transform,
38
+ };
20
39
  return new Stage({
21
40
  name: filename,
22
41
  executors: executor,
42
+ itemSelector: options?.perClass ? classSelector() : undefined,
43
+ batchSize: options?.batchSize,
44
+ maxConcurrency: options?.maxConcurrency,
23
45
  });
24
46
  }
47
+ /** Normalise a {@link VoidStageTransform} to an array. */
48
+ function asTransforms(transform) {
49
+ if (transform === undefined)
50
+ return [];
51
+ return Array.isArray(transform) ? [...transform] : [transform];
52
+ }
25
53
  function classSelector() {
26
54
  return {
27
55
  // Forward `options` so the Pipeline’s per-dataset TimeoutPolicy
@@ -48,76 +76,79 @@ function classSelector() {
48
76
  }
49
77
  // Global stages
50
78
  export function subjectUriSpaces(options) {
51
- return createVoidStage('subject-uri-space.rq', options);
79
+ return createVoidStage(VOID_STAGE_NAMES.subjectUriSpace, options);
52
80
  }
53
81
  export function classPartitions(options) {
54
- return createVoidStage('class-partition.rq', options);
82
+ return createVoidStage(VOID_STAGE_NAMES.classPartitions, options);
55
83
  }
56
84
  export function countObjectLiterals(options) {
57
- return createVoidStage('object-literals.rq', options);
85
+ return createVoidStage(VOID_STAGE_NAMES.objectLiterals, options);
58
86
  }
59
87
  export function countObjectUris(options) {
60
- return createVoidStage('object-uris.rq', options);
88
+ return createVoidStage(VOID_STAGE_NAMES.objectUris, options);
61
89
  }
62
90
  export function countProperties(options) {
63
- return createVoidStage('properties.rq', options);
91
+ return createVoidStage(VOID_STAGE_NAMES.properties, options);
64
92
  }
65
93
  export function countSubjects(options) {
66
- return createVoidStage('subjects.rq', options);
94
+ return createVoidStage(VOID_STAGE_NAMES.subjects, options);
67
95
  }
68
96
  export function countTriples(options) {
69
- return createVoidStage('triples.rq', options);
97
+ return createVoidStage(VOID_STAGE_NAMES.triples, options);
70
98
  }
71
99
  export function classPropertySubjects(options) {
72
- return createVoidStage('class-properties-subjects.rq', {
100
+ return createVoidStage(VOID_STAGE_NAMES.classPropertySubjects, {
73
101
  ...options,
74
102
  perClass: options?.perClass ?? true,
75
103
  });
76
104
  }
77
105
  export function classPropertyObjects(options) {
78
- return createVoidStage('class-properties-objects.rq', {
106
+ return createVoidStage(VOID_STAGE_NAMES.classPropertyObjects, {
79
107
  ...options,
80
108
  perClass: options?.perClass ?? true,
81
109
  });
82
110
  }
83
111
  export function countDatatypes(options) {
84
- return createVoidStage('datatypes.rq', options);
112
+ return createVoidStage(VOID_STAGE_NAMES.datatypes, options);
85
113
  }
86
114
  export function detectLicenses(options) {
87
- return createVoidStage('licenses.rq', options);
115
+ return createVoidStage(VOID_STAGE_NAMES.licenses, options);
88
116
  }
89
117
  // Per-class stages
90
118
  export function perClassObjectClasses(options) {
91
- return createVoidStage('class-property-object-classes.rq', {
119
+ return createVoidStage(VOID_STAGE_NAMES.perClassObjectClasses, {
92
120
  ...options,
93
121
  perClass: options?.perClass ?? true,
94
122
  });
95
123
  }
96
124
  export function perClassDatatypes(options) {
97
- return createVoidStage('class-property-datatypes.rq', {
125
+ return createVoidStage(VOID_STAGE_NAMES.perClassDatatypes, {
98
126
  ...options,
99
127
  perClass: options?.perClass ?? true,
100
128
  });
101
129
  }
102
130
  export function perClassLanguages(options) {
103
- return createVoidStage('class-property-languages.rq', {
131
+ return createVoidStage(VOID_STAGE_NAMES.perClassLanguages, {
104
132
  ...options,
105
133
  perClass: options?.perClass ?? true,
106
134
  });
107
135
  }
108
- // Domain-specific executor stages
136
+ // Stages with a built-in transform
109
137
  export function uriSpaces(uriSpaceMap, options) {
110
- return createVoidStage('object-uri-space.rq', {
111
- ...options,
112
- executor: (query) => new UriSpaceExecutor(new SparqlConstructExecutor({ query }), uriSpaceMap),
138
+ return createVoidStage(VOID_STAGE_NAMES.objectUriSpace, {
139
+ transform: [
140
+ withUriSpaces(uriSpaceMap),
141
+ ...asTransforms(options?.transform),
142
+ ],
113
143
  });
114
144
  }
115
145
  export function detectVocabularies(options) {
116
- return createVoidStage('entity-properties.rq', {
117
- ...options,
118
- executor: (query) => new VocabularyExecutor(new SparqlConstructExecutor({ query }), options?.vocabularies
119
- ? [...defaultVocabularies, ...options.vocabularies]
120
- : undefined),
146
+ const { vocabularies, transform } = options ?? {};
147
+ const allVocabularies = vocabularies
148
+ ? [...defaultVocabularies, ...vocabularies]
149
+ : undefined;
150
+ return createVoidStage(VOID_STAGE_NAMES.vocabularies, {
151
+ transform: [withVocabularies(allVocabularies), ...asTransforms(transform)],
121
152
  });
122
153
  }
123
154
  /**
@@ -129,27 +160,37 @@ export function detectVocabularies(options) {
129
160
  * when the cache is cold.
130
161
  */
131
162
  export async function voidStages(options) {
132
- const { uriSpaces: uriSpaceMap, vocabularies, ...stageOptions } = options ?? {};
163
+ const { uriSpaces: uriSpaceMap, vocabularies, transforms, ...stageOptions } = options ?? {};
164
+ // Merge the shared per-stage options with the transform routed to a stage.
165
+ const withTransform = (name) => ({
166
+ ...stageOptions,
167
+ transform: transforms?.[name],
168
+ });
133
169
  return Promise.all([
134
170
  // Global counting stages.
135
- countSubjects(stageOptions),
136
- countProperties(stageOptions),
137
- countObjectLiterals(stageOptions),
138
- countObjectUris(stageOptions),
139
- countDatatypes(stageOptions),
140
- countTriples(stageOptions),
171
+ countSubjects(withTransform(VOID_STAGE_NAMES.subjects)),
172
+ countProperties(withTransform(VOID_STAGE_NAMES.properties)),
173
+ countObjectLiterals(withTransform(VOID_STAGE_NAMES.objectLiterals)),
174
+ countObjectUris(withTransform(VOID_STAGE_NAMES.objectUris)),
175
+ countDatatypes(withTransform(VOID_STAGE_NAMES.datatypes)),
176
+ countTriples(withTransform(VOID_STAGE_NAMES.triples)),
141
177
  // Cache warming — must precede per-class stages.
142
- classPartitions(stageOptions),
178
+ classPartitions(withTransform(VOID_STAGE_NAMES.classPartitions)),
143
179
  // Per-class stages.
144
- classPropertySubjects(stageOptions),
145
- classPropertyObjects(stageOptions),
146
- perClassDatatypes(stageOptions),
147
- perClassObjectClasses(stageOptions),
148
- perClassLanguages(stageOptions),
180
+ classPropertySubjects(withTransform(VOID_STAGE_NAMES.classPropertySubjects)),
181
+ classPropertyObjects(withTransform(VOID_STAGE_NAMES.classPropertyObjects)),
182
+ perClassDatatypes(withTransform(VOID_STAGE_NAMES.perClassDatatypes)),
183
+ perClassObjectClasses(withTransform(VOID_STAGE_NAMES.perClassObjectClasses)),
184
+ perClassLanguages(withTransform(VOID_STAGE_NAMES.perClassLanguages)),
149
185
  // Other stages.
150
- detectLicenses(stageOptions),
151
- detectVocabularies({ ...stageOptions, vocabularies }),
152
- subjectUriSpaces(stageOptions),
153
- ...(uriSpaceMap ? [uriSpaces(uriSpaceMap, stageOptions)] : []),
186
+ detectLicenses(withTransform(VOID_STAGE_NAMES.licenses)),
187
+ detectVocabularies({
188
+ ...withTransform(VOID_STAGE_NAMES.vocabularies),
189
+ vocabularies,
190
+ }),
191
+ subjectUriSpaces(withTransform(VOID_STAGE_NAMES.subjectUriSpace)),
192
+ ...(uriSpaceMap
193
+ ? [uriSpaces(uriSpaceMap, withTransform(VOID_STAGE_NAMES.objectUriSpace))]
194
+ : []),
154
195
  ]);
155
196
  }
@@ -0,0 +1,17 @@
1
+ import type { ExecutorContext, QuadTransform } from '@lde/pipeline';
2
+ import type { Quad } from '@rdfjs/types';
3
+ /**
4
+ * Creates a {@link QuadTransform} that consumes `void:Linkset` quads from a
5
+ * stage's executor output, matches each `void:objectsTarget` against the
6
+ * configured URI space prefixes using `startsWith`, and aggregates triple
7
+ * counts per matched space.
8
+ *
9
+ * Emitted `void:objectsTarget` values point to the target dataset IRI (taken
10
+ * from the metadata quad subjects), not the raw URI space prefix. Unmatched
11
+ * linksets are discarded.
12
+ *
13
+ * Attach it to the `object-uri-space.rq` stage's executor – directly via
14
+ * {@link uriSpaces} or through the `transforms` map of {@link voidStages}.
15
+ */
16
+ export declare function withUriSpaces(uriSpaces: ReadonlyMap<string, readonly Quad[]>): QuadTransform<ExecutorContext>;
17
+ //# sourceMappingURL=uriSpaceTransform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uriSpaceTransform.d.ts","sourceRoot":"","sources":["../src/uriSpaceTransform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAgBzC;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,IAAI,EAAE,CAAC,GAC9C,aAAa,CAAC,eAAe,CAAC,CAGhC"}
@@ -1,4 +1,3 @@
1
- import { NotSupported, } from '@lde/pipeline';
2
1
  import { DataFactory } from 'n3';
3
2
  const { namedNode, quad, literal, blankNode } = DataFactory;
4
3
  const RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
@@ -11,30 +10,22 @@ const voidObjectsTarget = namedNode(`${VOID}objectsTarget`);
11
10
  const voidTriples = namedNode(`${VOID}triples`);
12
11
  const xsdInteger = namedNode(`${XSD}integer`);
13
12
  /**
14
- * Executor decorator that consumes `void:Linkset` quads from the inner executor,
15
- * matches each `void:objectsTarget` against configured URI space prefixes using
16
- * `startsWith`, and aggregates triple counts per matched space.
13
+ * Creates a {@link QuadTransform} that consumes `void:Linkset` quads from a
14
+ * stage's executor output, matches each `void:objectsTarget` against the
15
+ * configured URI space prefixes using `startsWith`, and aggregates triple
16
+ * counts per matched space.
17
17
  *
18
- * Emitted `void:objectsTarget` values point to the target dataset IRI (taken from
19
- * the metadata quad subjects), not the raw URI space prefix. Unmatched linksets
20
- * are discarded.
18
+ * Emitted `void:objectsTarget` values point to the target dataset IRI (taken
19
+ * from the metadata quad subjects), not the raw URI space prefix. Unmatched
20
+ * linksets are discarded.
21
+ *
22
+ * Attach it to the `object-uri-space.rq` stage's executor – directly via
23
+ * {@link uriSpaces} or through the `transforms` map of {@link voidStages}.
21
24
  */
22
- export class UriSpaceExecutor {
23
- inner;
24
- uriSpaces;
25
- constructor(inner, uriSpaces) {
26
- this.inner = inner;
27
- this.uriSpaces = uriSpaces;
28
- }
29
- async execute(dataset, distribution, options) {
30
- const result = await this.inner.execute(dataset, distribution, options);
31
- if (result instanceof NotSupported) {
32
- return result;
33
- }
34
- return withUriSpaces(result, dataset.iri.toString(), this.uriSpaces);
35
- }
25
+ export function withUriSpaces(uriSpaces) {
26
+ return (quads, { dataset }) => aggregateUriSpaces(quads, dataset.iri.toString(), uriSpaces);
36
27
  }
37
- async function* withUriSpaces(quads, datasetIri, uriSpaces) {
28
+ async function* aggregateUriSpaces(quads, datasetIri, uriSpaces) {
38
29
  // Group inner quads by subject (each subject = one Linkset).
39
30
  const linksets = new Map();
40
31
  for await (const q of quads) {
@@ -0,0 +1,17 @@
1
+ import type { ExecutorContext, QuadTransform } from '@lde/pipeline';
2
+ export declare const defaultVocabularies: readonly string[];
3
+ /**
4
+ * Creates a {@link QuadTransform} that passes through all quads from a stage's
5
+ * executor output and appends `void:vocabulary` triples for detected
6
+ * vocabulary prefixes.
7
+ *
8
+ * Inspects quads with predicate `void:property` to detect known vocabulary
9
+ * namespace prefixes, then yields the corresponding `void:vocabulary` quads
10
+ * after the executor output has been consumed.
11
+ *
12
+ * Attach it to the `entity-properties.rq` stage's executor – directly via
13
+ * {@link detectVocabularies} or through the `transforms` map of
14
+ * {@link voidStages}.
15
+ */
16
+ export declare function withVocabularies(vocabularies?: readonly string[]): QuadTransform<ExecutorContext>;
17
+ //# sourceMappingURL=vocabularyTransform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vocabularyTransform.d.ts","sourceRoot":"","sources":["../src/vocabularyTransform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAWpE,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAEhD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,YAAY,GAAE,SAAS,MAAM,EAAwB,GACpD,aAAa,CAAC,eAAe,CAAC,CAGhC"}
@@ -1,4 +1,3 @@
1
- import { NotSupported, } from '@lde/pipeline';
2
1
  import prefixes from '@zazuko/prefixes';
3
2
  import { DataFactory } from 'n3';
4
3
  const { namedNode, quad } = DataFactory;
@@ -9,29 +8,22 @@ export const defaultVocabularies = [
9
8
  ...new Set(Object.values(prefixes)),
10
9
  ];
11
10
  /**
12
- * Executor decorator that passes through all quads from the inner executor
13
- * and appends `void:vocabulary` triples for detected vocabulary prefixes.
11
+ * Creates a {@link QuadTransform} that passes through all quads from a stage's
12
+ * executor output and appends `void:vocabulary` triples for detected
13
+ * vocabulary prefixes.
14
14
  *
15
15
  * Inspects quads with predicate `void:property` to detect known vocabulary
16
16
  * namespace prefixes, then yields the corresponding `void:vocabulary` quads
17
- * after all inner quads have been consumed.
17
+ * after the executor output has been consumed.
18
+ *
19
+ * Attach it to the `entity-properties.rq` stage's executor – directly via
20
+ * {@link detectVocabularies} or through the `transforms` map of
21
+ * {@link voidStages}.
18
22
  */
19
- export class VocabularyExecutor {
20
- inner;
21
- vocabularies;
22
- constructor(inner, vocabularies = defaultVocabularies) {
23
- this.inner = inner;
24
- this.vocabularies = vocabularies;
25
- }
26
- async execute(dataset, distribution, options) {
27
- const result = await this.inner.execute(dataset, distribution, options);
28
- if (result instanceof NotSupported) {
29
- return result;
30
- }
31
- return withVocabularies(result, dataset.iri.toString(), this.vocabularies);
32
- }
23
+ export function withVocabularies(vocabularies = defaultVocabularies) {
24
+ return (quads, { dataset }) => appendVocabularies(quads, dataset.iri.toString(), vocabularies);
33
25
  }
34
- async function* withVocabularies(quads, datasetIri, vocabularies) {
26
+ async function* appendVocabularies(quads, datasetIri, vocabularies) {
35
27
  const detectedVocabularies = new Set();
36
28
  for await (const q of quads) {
37
29
  yield q;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lde/pipeline-void",
3
- "version": "0.28.4",
3
+ "version": "0.28.5",
4
4
  "description": "VOiD (Vocabulary of Interlinked Datasets) statistical analysis for RDF datasets",
5
5
  "repository": {
6
6
  "url": "git+https://github.com/ldelements/lde.git",
@@ -33,6 +33,6 @@
33
33
  },
34
34
  "peerDependencies": {
35
35
  "@lde/dataset": "0.7.4",
36
- "@lde/pipeline": "0.30.4"
36
+ "@lde/pipeline": "0.30.5"
37
37
  }
38
38
  }
@@ -1,19 +0,0 @@
1
- import { Dataset, Distribution } from '@lde/dataset';
2
- import { NotSupported, type Executor, type ExecuteOptions } from '@lde/pipeline';
3
- import type { Quad } from '@rdfjs/types';
4
- /**
5
- * Executor decorator that consumes `void:Linkset` quads from the inner executor,
6
- * matches each `void:objectsTarget` against configured URI space prefixes using
7
- * `startsWith`, and aggregates triple counts per matched space.
8
- *
9
- * Emitted `void:objectsTarget` values point to the target dataset IRI (taken from
10
- * the metadata quad subjects), not the raw URI space prefix. Unmatched linksets
11
- * are discarded.
12
- */
13
- export declare class UriSpaceExecutor implements Executor {
14
- private readonly inner;
15
- private readonly uriSpaces;
16
- constructor(inner: Executor, uriSpaces: ReadonlyMap<string, readonly Quad[]>);
17
- execute(dataset: Dataset, distribution: Distribution, options?: ExecuteOptions): Promise<AsyncIterable<Quad> | NotSupported>;
18
- }
19
- //# sourceMappingURL=uriSpaceExecutor.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"uriSpaceExecutor.d.ts","sourceRoot":"","sources":["../src/uriSpaceExecutor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EACL,YAAY,EACZ,KAAK,QAAQ,EACb,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAgBzC;;;;;;;;GAQG;AACH,qBAAa,gBAAiB,YAAW,QAAQ;IAE7C,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBADT,KAAK,EAAE,QAAQ,EACf,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,IAAI,EAAE,CAAC;IAG5D,OAAO,CACX,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,YAAY,EAC1B,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC;CAO/C"}
@@ -1,19 +0,0 @@
1
- import { Dataset, Distribution } from '@lde/dataset';
2
- import { NotSupported, type Executor, type ExecuteOptions } from '@lde/pipeline';
3
- import type { Quad } from '@rdfjs/types';
4
- export declare const defaultVocabularies: readonly string[];
5
- /**
6
- * Executor decorator that passes through all quads from the inner executor
7
- * and appends `void:vocabulary` triples for detected vocabulary prefixes.
8
- *
9
- * Inspects quads with predicate `void:property` to detect known vocabulary
10
- * namespace prefixes, then yields the corresponding `void:vocabulary` quads
11
- * after all inner quads have been consumed.
12
- */
13
- export declare class VocabularyExecutor implements Executor {
14
- private readonly inner;
15
- private readonly vocabularies;
16
- constructor(inner: Executor, vocabularies?: readonly string[]);
17
- execute(dataset: Dataset, distribution: Distribution, options?: ExecuteOptions): Promise<AsyncIterable<Quad> | NotSupported>;
18
- }
19
- //# sourceMappingURL=vocabularyAnalyzer.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"vocabularyAnalyzer.d.ts","sourceRoot":"","sources":["../src/vocabularyAnalyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EACL,YAAY,EACZ,KAAK,QAAQ,EACb,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAUzC,eAAO,MAAM,mBAAmB,EAAE,SAAS,MAAM,EAEhD,CAAC;AAEF;;;;;;;GAOG;AACH,qBAAa,kBAAmB,YAAW,QAAQ;IAE/C,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,YAAY;gBADZ,KAAK,EAAE,QAAQ,EACf,YAAY,GAAE,SAAS,MAAM,EAAwB;IAGlE,OAAO,CACX,OAAO,EAAE,OAAO,EAChB,YAAY,EAAE,YAAY,EAC1B,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC;CAO/C"}