@blackcatinformatics/purrdf 0.3.3 → 0.4.3

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
@@ -14,6 +14,10 @@ It is the same engine, byte-for-byte behavior, that ships as the `purrdf`
14
14
  Rust crates, the `purrdf` PyPI package, and `libpurrdf` — PurRDF's rule is
15
15
  **one engine, one behavior, every language**.
16
16
 
17
+ > **Try it live** — the [RDF-1.2 playground](https://blackcat-informatics.github.io/purrdf/playground/)
18
+ > runs this package in your browser: parse, SPARQL, SHACL, serialize, and
19
+ > canonicalize/compare RDF-1.2 graphs client-side, with no install.
20
+
17
21
  ## Why this instead of an incumbent RDF/JS library?
18
22
 
19
23
  No incumbent RDF/JS library carries the RDF 1.2 features:
@@ -40,7 +44,7 @@ since ~2021 (Chrome/Edge 91+, Firefox 89+, Safari 16.4+) and Node ≥ 18.
40
44
  ## Quickstart
41
45
 
42
46
  ```js
43
- import { ready, DataFactory, Dataset } from "@blackcatinformatics/purrdf";
47
+ import { ready, DataFactory, Dataset, QueryEngine } from "@blackcatinformatics/purrdf";
44
48
 
45
49
  await ready(); // one-time async wasm instantiation
46
50
 
@@ -63,6 +67,13 @@ ds.add(f.quad(f.namedNode("https://ex/stmt"), f.namedNode("https://ex/asserts"),
63
67
  // Quoted triples + directions survive a round-trip through N-Quads.
64
68
  const nq = ds.serialize("nquads");
65
69
  const reparsed = Dataset.parse(nq, "nquads");
70
+
71
+ const engine = new QueryEngine();
72
+ const names = engine.select(
73
+ reparsed,
74
+ "SELECT ?message WHERE { <https://ex/s> <https://ex/says> ?message }",
75
+ );
76
+ console.log(names.rows[0].message.value);
66
77
  ```
67
78
 
68
79
  ## API surface
@@ -73,18 +84,48 @@ const reparsed = Dataset.parse(nq, "nquads");
73
84
  `fromTerm`, `fromQuad`.
74
85
  - `Dataset` — `Dataset.parse(input, format, base?)`, `serialize(format)`,
75
86
  `add` / `delete` / `has` / `match` / `quads` / `size`, iteration.
76
- Formats: `turtle`, `ntriples`, `nquads`, `trig`, `rdfxml`.
87
+ Formats: `turtle`, `ntriples`, `nquads`, `trig`, `rdfxml` (`serialize` also `jsonld`).
88
+ - `Dataset.canonicalize()` / `Dataset.isomorphic(other)` — RDFC-1.0 canonical N-Quads
89
+ and RDF graph-identity (isomorphism under blank-node relabeling).
90
+ - `Dataset.visualModel(options?)` / `visualExport(options?)` /
91
+ `visualSvg(options?)` — the renderer-neutral RDF 1.2 model, complete semantic
92
+ scene and deterministic geometry, or self-contained SVG paired with that export.
93
+ Returned objects are structured-clone-safe and preserve triple-term identity,
94
+ assertion graphs, reifier/annotation graph context, nesting, and diagnostics.
95
+ - `QueryEngine` — a reusable SPARQL execution context with a native plan cache,
96
+ typed `select` / `ask` / `construct` / `describe` helpers, atomic `update`,
97
+ and `queryRaw` serialization for SPARQL Results JSON/XML/CSV/TSV plus graph
98
+ formats. `Dataset.query(...)` remains as the compatibility raw-string helper.
99
+ - `shaclValidateToSarif(shapesTtl, dataNt)` / `shaclEntail(shapesTtl, dataNt)` — SHACL
100
+ validation to a SARIF 2.1.0 report and SHACL-AF `sh:rule` entailment to N-Triples.
77
101
  - `Sink`, `datasetToStream`, `streamToDataset` — the async RDF/JS
78
102
  Stream/Sink primitives over the synchronous engine surface.
79
103
  - SPARQL evaluation over the in-memory dataset (no server required).
80
104
 
81
105
  Full typings ship in `index.d.ts`.
82
106
 
107
+ ```js
108
+ const { svg, export: graph } = reparsed.visualSvg({
109
+ mode: "compact",
110
+ vocabulary: [{ prefix: "ex", namespace: "https://ex/" }],
111
+ svg: { title: "RDF 1.2 graph", embedMetadata: true },
112
+ });
113
+
114
+ console.log(graph.model.statements, graph.model.relations);
115
+ document.querySelector("#graph").innerHTML = svg;
116
+ ```
117
+
118
+ Compact mode keeps ordinary asserted RDF as directed predicate-labelled edges and
119
+ promotes statements only when they need identity. Incidence mode exposes exact
120
+ subject/predicate/object ports. Table mode scales statement inspection without
121
+ discarding the same underlying model.
122
+
83
123
  ## Scope
84
124
 
85
125
  In-memory only, by design: no persistent store and no network I/O inside the
86
- wasm module. For the container transport (GTS), SHACL validation, SPARQL
87
- result serializers, and the rest of the toolkit, see the
126
+ wasm module. This package provides no network resolver, so remote `SERVICE`
127
+ and `LOAD` fail explicitly. For the container transport (GTS), native APIs, and the
128
+ rest of the toolkit, see the
88
129
  [main repository](https://github.com/Blackcat-Informatics/purrdf).
89
130
 
90
131
  ## Supply chain
package/index.d.ts CHANGED
@@ -1,37 +1,403 @@
1
1
  // SPDX-FileCopyrightText: 2026 Blackcat Informatics® Inc. <paudley@blackcatinformatics.ca>
2
2
  // SPDX-License-Identifier: MIT OR Apache-2.0
3
3
 
4
- // The wasm-bindgen-generated class declarations (DataFactory/Dataset/Quad/Sink/Term
5
- // and the free `version()` function) are the source of truth for the engine surface.
6
- export {
7
- DataFactory,
8
- Dataset,
9
- Quad,
10
- Sink,
11
- Term,
12
- version,
13
- } from "./pkg/purrdf_wasm.js";
14
-
15
- import type { Dataset, Quad } from "./pkg/purrdf_wasm.js";
16
-
17
- /**
18
- * Instantiate the wasm module. Idempotent; await once before using any other API.
19
- * In Node the wasm bytes load from the colocated file automatically; in a browser,
20
- * pass the bytes/URL or omit to fetch the colocated `.wasm`.
21
- *
22
- * After `ready()`, the RDF/JS surface augmentations are live: `Dataset` is iterable
23
- * (`for (const quad of dataset)`); `Dataset.add`/`Dataset.delete` return the dataset
24
- * instance so calls chain (`ds.add(q1).add(q2)`); `Term.equals`/`Quad.equals` return
25
- * `false` for `null`/`undefined` instead of throwing; and
26
- * `DataFactory.literal(value, languageOrDatatype)` accepts a `NamedNode` datatype as
27
- * the RDF/JS spec allows (dispatching to `typedLiteral`).
28
- */
29
- export function ready(wasmBytesOrUrl?: BufferSource | URL | string): Promise<void>;
4
+ export type LiteralDirection = "ltr" | "rtl";
30
5
 
31
- /** An RDF/JS Stream (async iterable) of the dataset's quads. */
32
- export function datasetToStream(dataset: Dataset): AsyncIterableIterator<Quad>;
6
+ export type TermType =
7
+ | "NamedNode"
8
+ | "BlankNode"
9
+ | "Literal"
10
+ | "Variable"
11
+ | "DefaultGraph"
12
+ | "Quad";
13
+
14
+ export interface DirectionalLanguage {
15
+ readonly language: string;
16
+ readonly direction: LiteralDirection;
17
+ }
18
+
19
+ export type LanguageOrDatatype =
20
+ | string
21
+ | NamedNode
22
+ | DirectionalLanguage
23
+ | null
24
+ | undefined;
25
+
26
+ export type QuadGraph = NamedNode | BlankNode | DefaultGraph;
27
+ export type RdfTerm = NamedNode | BlankNode | Literal | Variable | DefaultGraph | QuotedTriple;
28
+ export type QueryGraphFormat =
29
+ | "turtle"
30
+ | "ttl"
31
+ | "ntriples"
32
+ | "nt"
33
+ | "nquads"
34
+ | "nq"
35
+ | "trig"
36
+ | "rdfxml"
37
+ | "jsonld"
38
+ | "text/turtle"
39
+ | "application/n-triples"
40
+ | "application/n-quads"
41
+ | "application/trig"
42
+ | "application/rdf+xml"
43
+ | "application/ld+json";
44
+ export type QueryResultsFormat =
45
+ | "json"
46
+ | "srj"
47
+ | "xml"
48
+ | "csv"
49
+ | "tsv"
50
+ | "application/sparql-results+json"
51
+ | "application/sparql-results+xml"
52
+ | "text/csv"
53
+ | "text/tab-separated-values";
54
+ export type QueryRawFormat = QueryGraphFormat | QueryResultsFormat;
55
+
56
+ export interface QueryOptions {
57
+ readonly base?: string | null;
58
+ }
59
+
60
+ export interface QueryRawOptions extends QueryOptions {
61
+ readonly format?: QueryRawFormat | string | null;
62
+ }
63
+
64
+ export type QueryBindingRow = Record<string, RdfTerm | undefined>;
65
+
66
+ export interface SelectResult {
67
+ readonly kind: "select";
68
+ readonly variables: string[];
69
+ readonly rows: QueryBindingRow[];
70
+ }
71
+
72
+ export interface AskResult {
73
+ readonly kind: "ask";
74
+ readonly boolean: boolean;
75
+ }
76
+
77
+ export interface GraphResult {
78
+ readonly kind: "graph";
79
+ readonly dataset: Dataset;
80
+ }
81
+
82
+ export type QueryResult = SelectResult | AskResult | GraphResult;
83
+
84
+ export type VisualMode = "compact" | "incidence" | "table";
85
+ export type VisualLabelPolicy = "compact" | "full";
86
+ export type VisualTableField =
87
+ | "statement"
88
+ | "assertedIn"
89
+ | "reifiers"
90
+ | "annotations"
91
+ | "referencedBy"
92
+ | "depth"
93
+ | "diagnostics";
94
+
95
+ export interface VisualVocabularyMapping {
96
+ readonly prefix: string;
97
+ readonly namespace: string;
98
+ }
99
+
100
+ export interface VisualRoleRule {
101
+ readonly predicateIri: string;
102
+ readonly role: string;
103
+ }
104
+
105
+ export interface VisualLayoutOptions {
106
+ readonly margin?: number;
107
+ readonly rankSpacing?: number;
108
+ readonly nodeSpacing?: number;
109
+ readonly componentSpacing?: number;
110
+ readonly componentWrapWidth?: number;
111
+ readonly crossingSweeps?: number;
112
+ readonly maxNodeWidth?: number;
113
+ }
114
+
115
+ export interface VisualSvgOptions {
116
+ readonly embedMetadata?: boolean;
117
+ readonly includeStyles?: boolean;
118
+ readonly title?: string;
119
+ }
120
+
121
+ export interface VisualizationOptions {
122
+ readonly mode?: VisualMode;
123
+ readonly focus?: string | null;
124
+ readonly roleRules?: readonly VisualRoleRule[];
125
+ readonly vocabulary?: readonly VisualVocabularyMapping[];
126
+ readonly graph?: string | null;
127
+ readonly graphs?: readonly string[];
128
+ readonly labelPolicy?: VisualLabelPolicy;
129
+ readonly maxStatements?: number;
130
+ readonly maxTerms?: number;
131
+ readonly tableFields?: readonly VisualTableField[];
132
+ readonly layout?: VisualLayoutOptions;
133
+ readonly svg?: VisualSvgOptions;
134
+ }
135
+
136
+ export type VisualValueRef =
137
+ | { readonly kind: "term"; readonly id: string }
138
+ | { readonly kind: "statement"; readonly id: string };
139
+
140
+ export type VisualTermValue =
141
+ | { readonly kind: "iri"; readonly value: string }
142
+ | { readonly kind: "blank"; readonly label: string; readonly scope: number }
143
+ | {
144
+ readonly kind: "literal";
145
+ readonly lexical_form: string;
146
+ readonly datatype: string;
147
+ readonly language: string | null;
148
+ readonly direction: LiteralDirection | null;
149
+ };
150
+
151
+ export interface VisualTerm {
152
+ readonly id: string;
153
+ readonly value: VisualTermValue;
154
+ readonly label: string;
155
+ readonly roles: readonly unknown[];
156
+ }
157
+
158
+ export interface VisualStatement {
159
+ readonly id: string;
160
+ readonly subject: VisualValueRef;
161
+ readonly predicate: string;
162
+ readonly object: VisualValueRef;
163
+ readonly asserted_in: readonly string[];
164
+ readonly nesting_depth: number;
165
+ readonly incoming_references: number;
166
+ readonly dialect: "rdf12" | "symmetricRdf12" | "generalizedRdf";
167
+ readonly roles: readonly unknown[];
168
+ }
33
169
 
34
- /** Consume an (async) iterable of quads into a new Dataset via the engine's Sink. */
170
+ export type VisualRelation =
171
+ | {
172
+ readonly kind: "reifies";
173
+ readonly id: string;
174
+ readonly reifier: string;
175
+ readonly statement: string;
176
+ readonly graph: string;
177
+ }
178
+ | {
179
+ readonly kind: "annotation";
180
+ readonly id: string;
181
+ readonly reifier: string;
182
+ readonly predicate: string;
183
+ readonly object: VisualValueRef;
184
+ readonly graph: string;
185
+ };
186
+
187
+ export interface VisualDiagnostic {
188
+ readonly id: string;
189
+ readonly code: string;
190
+ readonly message: string;
191
+ readonly target: string | null;
192
+ readonly dialect: VisualStatement["dialect"];
193
+ }
194
+
195
+ export interface VisualModel {
196
+ readonly terms: readonly VisualTerm[];
197
+ readonly statements: readonly VisualStatement[];
198
+ readonly assertions: readonly Readonly<Record<string, unknown>>[];
199
+ readonly relations: readonly VisualRelation[];
200
+ readonly graphs: readonly Readonly<Record<string, unknown>>[];
201
+ readonly references: readonly Readonly<Record<string, unknown>>[];
202
+ readonly table: Readonly<Record<string, unknown>>;
203
+ readonly diagnostics: readonly VisualDiagnostic[];
204
+ }
205
+
206
+ export interface VisualScene {
207
+ readonly schema_version: "purrdf-viz-scene-1";
208
+ readonly mode: VisualMode;
209
+ readonly nodes: readonly Readonly<Record<string, unknown>>[];
210
+ readonly edges: readonly Readonly<Record<string, unknown>>[];
211
+ readonly groups: readonly Readonly<Record<string, unknown>>[];
212
+ readonly legend: readonly Readonly<Record<string, unknown>>[];
213
+ readonly table: Readonly<Record<string, unknown>> | null;
214
+ }
215
+
216
+ export interface VisualLayout {
217
+ readonly schema_version: "purrdf-viz-layout-1";
218
+ readonly mode: VisualMode;
219
+ readonly width: number;
220
+ readonly height: number;
221
+ readonly nodes: readonly Readonly<Record<string, unknown>>[];
222
+ readonly edges: readonly Readonly<Record<string, unknown>>[];
223
+ readonly table: Readonly<Record<string, unknown>> | null;
224
+ readonly legend: readonly Readonly<Record<string, unknown>>[];
225
+ }
226
+
227
+ export interface VisualExport {
228
+ readonly schema_version: "purrdf-viz-export-1";
229
+ readonly spec: Readonly<Record<string, unknown>>;
230
+ readonly spec_hash: string;
231
+ readonly model_hash: string;
232
+ readonly scene_hash: string;
233
+ readonly model: VisualModel;
234
+ readonly scene: VisualScene;
235
+ readonly layout: VisualLayout;
236
+ readonly element_index: readonly Readonly<Record<string, unknown>>[];
237
+ readonly diagnostics: readonly VisualDiagnostic[];
238
+ }
239
+
240
+ export interface VisualSvgDocument {
241
+ readonly svg: string;
242
+ readonly export: VisualExport;
243
+ }
244
+
245
+ export class Term {
246
+ private constructor();
247
+ free(): void;
248
+ readonly termType: TermType;
249
+ readonly value: string;
250
+ readonly language: string;
251
+ readonly direction: "" | LiteralDirection;
252
+ readonly datatype: NamedNode | undefined;
253
+ readonly subject: RdfTerm | undefined;
254
+ readonly predicate: NamedNode | undefined;
255
+ readonly object: RdfTerm | undefined;
256
+ readonly graph: DefaultGraph | undefined;
257
+ equals(other: Term | null | undefined): boolean;
258
+ }
259
+
260
+ export interface NamedNode extends Term {
261
+ readonly termType: "NamedNode";
262
+ readonly value: string;
263
+ readonly language: "";
264
+ readonly direction: "";
265
+ readonly datatype: undefined;
266
+ }
267
+
268
+ export interface BlankNode extends Term {
269
+ readonly termType: "BlankNode";
270
+ readonly value: string;
271
+ readonly language: "";
272
+ readonly direction: "";
273
+ readonly datatype: undefined;
274
+ }
275
+
276
+ export interface Literal extends Term {
277
+ readonly termType: "Literal";
278
+ readonly value: string;
279
+ readonly language: string;
280
+ readonly direction: "" | LiteralDirection;
281
+ readonly datatype: NamedNode;
282
+ }
283
+
284
+ export interface Variable extends Term {
285
+ readonly termType: "Variable";
286
+ readonly value: string;
287
+ readonly language: "";
288
+ readonly direction: "";
289
+ readonly datatype: undefined;
290
+ }
291
+
292
+ export interface DefaultGraph extends Term {
293
+ readonly termType: "DefaultGraph";
294
+ readonly value: "";
295
+ readonly language: "";
296
+ readonly direction: "";
297
+ readonly datatype: undefined;
298
+ }
299
+
300
+ export interface QuotedTriple extends Term {
301
+ readonly termType: "Quad";
302
+ readonly value: "";
303
+ readonly language: "";
304
+ readonly direction: "";
305
+ readonly datatype: undefined;
306
+ readonly subject: RdfTerm;
307
+ readonly predicate: NamedNode;
308
+ readonly object: RdfTerm;
309
+ readonly graph: DefaultGraph;
310
+ }
311
+
312
+ export class Quad {
313
+ private constructor();
314
+ free(): void;
315
+ readonly termType: "Quad";
316
+ readonly value: "";
317
+ readonly subject: RdfTerm;
318
+ readonly predicate: NamedNode;
319
+ readonly object: RdfTerm;
320
+ readonly graph: QuadGraph;
321
+ asTerm(): QuotedTriple;
322
+ equals(other: Quad | null | undefined): boolean;
323
+ }
324
+
325
+ export class DataFactory {
326
+ constructor();
327
+ namedNode(value: string): NamedNode;
328
+ blankNode(value?: string | null): BlankNode;
329
+ literal(value: string, languageOrDatatype?: LanguageOrDatatype): Literal;
330
+ typedLiteral(value: string, datatype: NamedNode): Literal;
331
+ directionalLiteral(
332
+ value: string,
333
+ language: string,
334
+ direction: LiteralDirection,
335
+ ): Literal;
336
+ variable(value: string): Variable;
337
+ defaultGraph(): DefaultGraph;
338
+ quad(
339
+ subject: RdfTerm,
340
+ predicate: NamedNode,
341
+ object: RdfTerm,
342
+ graph?: QuadGraph | null,
343
+ ): Quad;
344
+ quotedTriple(subject: RdfTerm, predicate: NamedNode, object: RdfTerm): QuotedTriple;
345
+ fromTerm<T extends Term>(original: T): T;
346
+ fromQuad(original: Quad): Quad;
347
+ dataset(quads?: Iterable<Quad> | null): Dataset;
348
+ }
349
+
350
+ export class Dataset implements Iterable<Quad> {
351
+ constructor();
352
+ static parse(input: string, format: string, base?: string | null): Dataset;
353
+ static from(quads?: Iterable<Quad> | null): Dataset;
354
+ readonly size: number;
355
+ add(quad: Quad): this;
356
+ delete(quad: Quad): this;
357
+ has(quad: Quad): boolean;
358
+ match(
359
+ subject?: Term | null,
360
+ predicate?: Term | null,
361
+ object?: Term | null,
362
+ graph?: Term | null,
363
+ ): Dataset;
364
+ quads(): Quad[];
365
+ visualModel(options?: VisualizationOptions | null): VisualModel;
366
+ visualExport(options?: VisualizationOptions | null): VisualExport;
367
+ visualSvg(options?: VisualizationOptions | null): VisualSvgDocument;
368
+ serialize(format: string): string;
369
+ query(sparql: string, base?: string | null): string;
370
+ canonicalize(): string;
371
+ isomorphic(other: Dataset): boolean;
372
+ toStream(): AsyncIterableIterator<Quad>;
373
+ [Symbol.iterator](): IterableIterator<Quad>;
374
+ free(): void;
375
+ }
376
+
377
+ export class QueryEngine {
378
+ constructor();
379
+ query(dataset: Dataset, sparql: string, options?: QueryOptions | null): QueryResult;
380
+ select(dataset: Dataset, sparql: string, options?: QueryOptions | null): SelectResult;
381
+ ask(dataset: Dataset, sparql: string, options?: QueryOptions | null): boolean;
382
+ construct(dataset: Dataset, sparql: string, options?: QueryOptions | null): Dataset;
383
+ describe(dataset: Dataset, sparql: string, options?: QueryOptions | null): Dataset;
384
+ update(dataset: Dataset, sparql: string, options?: QueryOptions | null): Dataset;
385
+ queryRaw(dataset: Dataset, sparql: string, options?: QueryRawOptions | null): string;
386
+ free(): void;
387
+ }
388
+
389
+ export class Sink {
390
+ constructor();
391
+ push(quad: Quad): void;
392
+ finish(): Dataset;
393
+ free(): void;
394
+ }
395
+
396
+ export function ready(wasmBytesOrUrl?: BufferSource | URL | string): Promise<void>;
397
+ export function datasetToStream(dataset: Dataset): AsyncIterableIterator<Quad>;
35
398
  export function streamToDataset(
36
399
  quadStream: AsyncIterable<Quad> | Iterable<Quad>,
37
400
  ): Promise<Dataset>;
401
+ export function shaclEntail(shapesTtl: string, dataNt: string): string;
402
+ export function shaclValidateToSarif(shapesTtl: string, dataNt: string): string;
403
+ export function version(): string;