@ng-org/shex-orm 0.1.2

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.
Files changed (64) hide show
  1. package/README.md +240 -0
  2. package/dist/ShexJTypes.d.ts +542 -0
  3. package/dist/ShexJTypes.d.ts.map +1 -0
  4. package/dist/ShexJTypes.js +10 -0
  5. package/dist/build.d.ts +8 -0
  6. package/dist/build.d.ts.map +1 -0
  7. package/dist/build.js +72 -0
  8. package/dist/cli.d.ts +3 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +15 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +1 -0
  14. package/dist/schema-converter/__tests__/typingTransformer.test.d.ts +2 -0
  15. package/dist/schema-converter/__tests__/typingTransformer.test.d.ts.map +1 -0
  16. package/dist/schema-converter/__tests__/typingTransformer.test.js +76 -0
  17. package/dist/schema-converter/converter.d.ts +12 -0
  18. package/dist/schema-converter/converter.d.ts.map +1 -0
  19. package/dist/schema-converter/converter.js +79 -0
  20. package/dist/schema-converter/templates/schema.ejs +8 -0
  21. package/dist/schema-converter/templates/shapeTypes.ejs +14 -0
  22. package/dist/schema-converter/templates/typings.ejs +14 -0
  23. package/dist/schema-converter/transformers/ShexJSchemaTransformer.d.ts +348 -0
  24. package/dist/schema-converter/transformers/ShexJSchemaTransformer.d.ts.map +1 -0
  25. package/dist/schema-converter/transformers/ShexJSchemaTransformer.js +239 -0
  26. package/dist/schema-converter/transformers/ShexJTypingTransformer.d.ts +366 -0
  27. package/dist/schema-converter/transformers/ShexJTypingTransformer.d.ts.map +1 -0
  28. package/dist/schema-converter/transformers/ShexJTypingTransformer.js +623 -0
  29. package/dist/schema-converter/util/ShapeInterfaceDeclaration.d.ts +5 -0
  30. package/dist/schema-converter/util/ShapeInterfaceDeclaration.d.ts.map +1 -0
  31. package/dist/schema-converter/util/ShapeInterfaceDeclaration.js +1 -0
  32. package/dist/schema-converter/util/annotateReadablePredicates.d.ts +8 -0
  33. package/dist/schema-converter/util/annotateReadablePredicates.d.ts.map +1 -0
  34. package/dist/schema-converter/util/annotateReadablePredicates.js +148 -0
  35. package/dist/schema-converter/util/dedupeObjectTypeMembers.d.ts +3 -0
  36. package/dist/schema-converter/util/dedupeObjectTypeMembers.d.ts.map +1 -0
  37. package/dist/schema-converter/util/dedupeObjectTypeMembers.js +47 -0
  38. package/dist/schema-converter/util/getRdfTypesForTripleConstraint.d.ts +4 -0
  39. package/dist/schema-converter/util/getRdfTypesForTripleConstraint.d.ts.map +1 -0
  40. package/dist/schema-converter/util/getRdfTypesForTripleConstraint.js +98 -0
  41. package/dist/types.d.ts +37 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/dist/types.js +10 -0
  44. package/dist/util/forAllShapes.d.ts +2 -0
  45. package/dist/util/forAllShapes.d.ts.map +1 -0
  46. package/dist/util/forAllShapes.js +25 -0
  47. package/package.json +67 -0
  48. package/src/ShexJTypes.ts +616 -0
  49. package/src/build.ts +106 -0
  50. package/src/cli.ts +23 -0
  51. package/src/index.ts +1 -0
  52. package/src/schema-converter/__tests__/typingTransformer.test.ts +85 -0
  53. package/src/schema-converter/converter.ts +128 -0
  54. package/src/schema-converter/templates/schema.ejs +8 -0
  55. package/src/schema-converter/templates/shapeTypes.ejs +14 -0
  56. package/src/schema-converter/templates/typings.ejs +14 -0
  57. package/src/schema-converter/transformers/ShexJSchemaTransformer.ts +284 -0
  58. package/src/schema-converter/transformers/ShexJTypingTransformer.ts +807 -0
  59. package/src/schema-converter/util/ShapeInterfaceDeclaration.ts +5 -0
  60. package/src/schema-converter/util/annotateReadablePredicates.ts +173 -0
  61. package/src/schema-converter/util/dedupeObjectTypeMembers.ts +61 -0
  62. package/src/schema-converter/util/getRdfTypesForTripleConstraint.ts +153 -0
  63. package/src/types.ts +51 -0
  64. package/src/util/forAllShapes.ts +39 -0
package/src/build.ts ADDED
@@ -0,0 +1,106 @@
1
+ // Copyright (c) 2025 Laurin Weger, Par le Peuple, NextGraph.org developers
2
+ // All rights reserved.
3
+ // Copyright (c) 2023 Jackson Morgan
4
+ // Licensed under the Apache License, Version 2.0
5
+ // <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
6
+ // or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
7
+ // at your option. All files in the project carrying such
8
+ // notice may not be copied, modified, or distributed except
9
+ // according to those terms.
10
+ // SPDX-License-Identifier: Apache-2.0 OR MIT
11
+
12
+ import fs from "fs-extra";
13
+ import path from "path";
14
+ import type { Schema } from "./ShexJTypes.ts";
15
+ import parser from "@shexjs/parser";
16
+ import { shexJConverter } from "./schema-converter/converter.ts";
17
+ import { renderFile } from "ejs";
18
+ import prettier from "prettier";
19
+ import loading from "loading-cli";
20
+ import { dirname } from "node:path";
21
+ import { fileURLToPath } from "node:url";
22
+ import { forAllShapes } from "./util/forAllShapes.ts";
23
+ import annotateReadablePredicates from "./schema-converter/util/annotateReadablePredicates.ts";
24
+
25
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
26
+ // @ts-ignore
27
+ const __dirname = dirname(fileURLToPath(import.meta.url));
28
+
29
+ interface BuildOptions {
30
+ input: string;
31
+ output: string;
32
+ baseIRI?: string;
33
+ }
34
+
35
+ export async function build({
36
+ input: inputFile,
37
+ output: outputFile,
38
+ baseIRI = "https://nextgraph.org/shapes#",
39
+ }: BuildOptions) {
40
+ const load = loading("Preparing Environment");
41
+ load.start();
42
+ // Prepare new folder by clearing/and/or creating it
43
+ if (fs.existsSync(outputFile)) {
44
+ await fs.promises.rm(outputFile, { recursive: true });
45
+ }
46
+ await fs.promises.mkdir(outputFile);
47
+
48
+ const fileTemplates: string[] = [];
49
+
50
+ // Pre-annotate schema with readablePredicate to unify naming across outputs
51
+ fileTemplates.push("schema", "typings", "shapeTypes");
52
+
53
+ load.text = "Generating Schema Documents";
54
+ await forAllShapes(inputFile, async (fileName, shexC) => {
55
+ // Convert to ShexJ
56
+ let schema: Schema;
57
+ try {
58
+ // Prase Shex schema to JSON.
59
+ // TODO: Do we need the base IRI?
60
+ // @ts-ignore ...
61
+ schema = parser.construct(baseIRI).parse(shexC);
62
+ } catch (err) {
63
+ const errMessage =
64
+ err instanceof Error
65
+ ? err.message
66
+ : typeof err === "string"
67
+ ? err
68
+ : "Unknown Error";
69
+ console.error(`Error processing ${fileName}: ${errMessage}`);
70
+ return;
71
+ }
72
+
73
+ // Add readable predicates to schema as the single source of truth.
74
+ // @ts-ignore ...
75
+ annotateReadablePredicates(schema);
76
+
77
+ const [typings, compactSchema] = await shexJConverter(schema);
78
+
79
+ await Promise.all(
80
+ fileTemplates.map(async (templateName) => {
81
+ const finalContent = await renderFile(
82
+ path.join(
83
+ __dirname,
84
+ "schema-converter",
85
+ "templates",
86
+ `${templateName}.ejs`
87
+ ),
88
+ {
89
+ typings: typings.typings,
90
+ fileName,
91
+ schema: JSON.stringify(schema, null, 2),
92
+ compactSchema: JSON.stringify(compactSchema, null, 2),
93
+ }
94
+ );
95
+ await fs.promises.writeFile(
96
+ path.join(outputFile, `${fileName}.${templateName}.ts`),
97
+ await prettier.format(finalContent, {
98
+ parser: "typescript",
99
+ })
100
+ );
101
+ })
102
+ );
103
+ });
104
+
105
+ load.stop();
106
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { program } from "commander";
4
+ import { build } from "./build.ts";
5
+
6
+ program
7
+ .name("NG-ORM")
8
+ .description("CLI to some JavaScript string utilities")
9
+ .version("0.1.0");
10
+
11
+ program
12
+ .command("build")
13
+ .description("Build contents of a shex folder into Shape Types")
14
+ .option("-i, --input <inputPath>", "Provide the input path", "./.shapes")
15
+ .option("-o, --output <outputPath>", "Provide the output path", "./.orm")
16
+ .option(
17
+ "-b, --baseIRI <baseIri>",
18
+ "The base IRI for anonymous shapes",
19
+ "https://nextgraph.org/shapes#"
20
+ )
21
+ .action(build);
22
+
23
+ program.parse();
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./types.ts";
@@ -0,0 +1,85 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import annotateReadablePredicates from "../util/annotateReadablePredicates.ts";
3
+ import { shexJConverter } from "../converter.ts";
4
+
5
+ const TYPE_IRI = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
6
+
7
+ function buildSchema() {
8
+ return {
9
+ type: "Schema",
10
+ shapes: [
11
+ {
12
+ type: "ShapeDecl",
13
+ id: "http://example.org/Expense",
14
+ shapeExpr: {
15
+ type: "Shape",
16
+ expression: {
17
+ type: "EachOf",
18
+ expressions: [
19
+ {
20
+ type: "TripleConstraint",
21
+ predicate: TYPE_IRI,
22
+ valueExpr: {
23
+ type: "NodeConstraint",
24
+ values: [
25
+ {
26
+ value: "http://example.org/Expense",
27
+ },
28
+ ],
29
+ },
30
+ },
31
+ ],
32
+ },
33
+ },
34
+ },
35
+ {
36
+ type: "ShapeDecl",
37
+ id: "http://example.org/ExpenseCategory",
38
+ shapeExpr: {
39
+ type: "Shape",
40
+ extra: [TYPE_IRI],
41
+ expression: {
42
+ type: "EachOf",
43
+ expressions: [
44
+ {
45
+ type: "TripleConstraint",
46
+ predicate: TYPE_IRI,
47
+ valueExpr: {
48
+ type: "NodeConstraint",
49
+ values: [
50
+ {
51
+ value: "http://example.org/ExpenseCategory",
52
+ },
53
+ ],
54
+ },
55
+ },
56
+ ],
57
+ },
58
+ },
59
+ },
60
+ ],
61
+ };
62
+ }
63
+
64
+ async function buildTypingsText(): Promise<string> {
65
+ const schema = buildSchema();
66
+ annotateReadablePredicates(schema as any);
67
+ const [typings] = await shexJConverter(schema as any);
68
+ return typings.typingsString;
69
+ }
70
+
71
+ describe("ShexJTypingTransformer", () => {
72
+ it("emits literal unions for rdf:type constraints", async () => {
73
+ const typings = await buildTypingsText();
74
+ expect(typings).toMatch(
75
+ /interface Expense[\s\S]*?"@type": "http:\/\/example\.org\/Expense";/
76
+ );
77
+ });
78
+
79
+ it("treats EXTRA rdf:type predicates as plural", async () => {
80
+ const typings = await buildTypingsText();
81
+ expect(typings).toMatch(
82
+ /interface ExpenseCategory[\s\S]*?"@type": Set<"http:\/\/example\.org\/ExpenseCategory" \|[\s]*\(?string[\s]*&[\s]*\{[\s]*\}\)?>;/
83
+ );
84
+ });
85
+ });
@@ -0,0 +1,128 @@
1
+ // Copyright (c) 2025 Laurin Weger, Par le Peuple, NextGraph.org developers
2
+ // All rights reserved.
3
+ // Copyright (c) 2023 Jackson Morgan
4
+ // Licensed under the Apache License, Version 2.0
5
+ // <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
6
+ // or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
7
+ // at your option. All files in the project carrying such
8
+ // notice may not be copied, modified, or distributed except
9
+ // according to those terms.
10
+ // SPDX-License-Identifier: Apache-2.0 OR MIT
11
+
12
+ import type { Schema } from "@ldo/traverser-shexj";
13
+ import { jsonld2graphobject } from "jsonld2graphobject";
14
+ import * as dom from "dts-dom";
15
+ import {
16
+ ShexJTypingTransformerCompact,
17
+ additionalCompactEnumAliases,
18
+ } from "./transformers/ShexJTypingTransformer.ts";
19
+ import { ShexJSchemaTransformerCompact } from "./transformers/ShexJSchemaTransformer.ts";
20
+ import type { Schema as ShapeSchema, Shape } from "../types.ts";
21
+
22
+ export interface TypingReturn {
23
+ typingsString: string;
24
+ typings: {
25
+ typingString: string;
26
+ dts: dom.TopLevelDeclaration;
27
+ }[];
28
+ }
29
+
30
+ export async function shexJConverter(
31
+ shexj: Schema
32
+ ): Promise<[TypingReturn, ShapeSchema]> {
33
+ // Prepare processed schema (names still rely on context visitor)
34
+ const processedShexj: Schema = (await jsonld2graphobject(
35
+ {
36
+ ...shexj,
37
+ "@id": "SCHEMA",
38
+ "@context": "http://www.w3.org/ns/shex.jsonld",
39
+ },
40
+ "SCHEMA"
41
+ )) as unknown as Schema;
42
+
43
+ additionalCompactEnumAliases.clear();
44
+ const declarations = await ShexJTypingTransformerCompact.transform(
45
+ processedShexj,
46
+ "Schema",
47
+ null
48
+ );
49
+
50
+ const compactSchemaShapesUnflattened =
51
+ await ShexJSchemaTransformerCompact.transform(
52
+ processedShexj,
53
+ "Schema",
54
+ null
55
+ );
56
+ const compactSchema = flattenSchema(compactSchemaShapesUnflattened);
57
+
58
+ // Append only enum aliases (no interface Id aliases in compact format now)
59
+ const hasName = (d: unknown): d is { name: string } =>
60
+ typeof (d as { name?: unknown }).name === "string";
61
+ additionalCompactEnumAliases.forEach((alias) => {
62
+ const exists = declarations.some((d) => hasName(d) && d.name === alias);
63
+ if (!exists)
64
+ declarations.push(
65
+ dom.create.alias(alias, dom.create.namedTypeReference("IRI"))
66
+ );
67
+ });
68
+
69
+ const typings = declarations.map((declaration) => ({
70
+ typingString: dom
71
+ .emit(declaration, {
72
+ rootFlags: dom.ContextFlags.InAmbientNamespace,
73
+ })
74
+ .replace(/\r\n/g, "\n"),
75
+ dts: declaration,
76
+ }));
77
+ const header = `export type IRI = string;\n\n`;
78
+ const typingsString =
79
+ header + typings.map((t) => `export ${t.typingString}`).join("");
80
+
81
+ return [{ typingsString, typings }, compactSchema];
82
+ }
83
+
84
+ /** Shapes may be nested. Put all to their root and give nested ones ids. */
85
+ function flattenSchema(shapes: Shape[]): ShapeSchema {
86
+ let schema: ShapeSchema = {};
87
+
88
+ for (const shape of shapes) {
89
+ schema[shape.iri] = shape;
90
+
91
+ // Find nested, unflattened (i.e. anonymous) schemas in predicates' dataTypes.
92
+ for (const pred of shape.predicates) {
93
+ for (let i = 0; i < pred.dataTypes.length; i++) {
94
+ const dt = pred.dataTypes[i];
95
+ if (
96
+ dt.valType === "shape" &&
97
+ typeof dt.shape === "object" &&
98
+ dt.shape !== null
99
+ ) {
100
+ // create a deterministic id for the nested shape; include index if multiple shape entries exist
101
+ const shapeCount = pred.dataTypes.filter(
102
+ (d) => d.valType === "shape"
103
+ ).length;
104
+ const newId =
105
+ shape.iri +
106
+ "||" +
107
+ pred.iri +
108
+ (shapeCount > 1 ? `||${i}` : "");
109
+
110
+ // Recurse
111
+ const flattened = flattenSchema([
112
+ {
113
+ ...(dt.shape as Shape),
114
+ iri: newId,
115
+ },
116
+ ]);
117
+ // Replace the nested schema with its new id.
118
+ dt.shape = newId;
119
+
120
+ schema = { ...schema, ...flattened };
121
+ }
122
+ }
123
+ }
124
+ // Flatten / Recurse
125
+ }
126
+
127
+ return schema;
128
+ }
@@ -0,0 +1,8 @@
1
+ import type { Schema } from "@ng-org/shex-orm";
2
+
3
+ /**
4
+ * =============================================================================
5
+ * <%- fileName %>Schema: Schema for <%- fileName %>
6
+ * =============================================================================
7
+ */
8
+ export const <%- fileName %>Schema: Schema = <%- compactSchema %>;
@@ -0,0 +1,14 @@
1
+ import type { ShapeType } from "@ng-org/shex-orm";
2
+ import { <%- fileName %>Schema } from "./<%- fileName %>.schema";
3
+ import type {
4
+ <% typings.forEach((typing)=> { if (!/Id$/.test(typing.dts.name)) { -%>
5
+ <%- typing.dts.name %>,
6
+ <% } }); -%>} from "./<%- fileName %>.typings";
7
+
8
+ // ShapeTypes for <%- fileName %>
9
+ <% typings.forEach((typing)=> { if (!/Id$/.test(typing.dts.name)) { -%>
10
+ export const <%- typing.dts.name %>ShapeType: ShapeType<<%- typing.dts.name %>> = {
11
+ schema: <%- fileName %>Schema,
12
+ shape: "<%- typing.dts.shapeId %>",
13
+ };
14
+ <% } }); -%>
@@ -0,0 +1,14 @@
1
+ export type IRI = string;
2
+
3
+ /**
4
+ * =============================================================================
5
+ * Typescript Typings for <%- fileName %>
6
+ * =============================================================================
7
+ */
8
+
9
+ <% typings.forEach((typing)=> { -%>
10
+ /**
11
+ * <%- typing.dts.name %> Type
12
+ */
13
+ export <%- typing.typingString -%>
14
+ <% }); -%>
@@ -0,0 +1,284 @@
1
+ // Copyright (c) 2025 Laurin Weger, Par le Peuple, NextGraph.org developers
2
+ // All rights reserved.
3
+ // Licensed under the Apache License, Version 2.0
4
+ // <LICENSE-APACHE2 or http://www.apache.org/licenses/LICENSE-2.0>
5
+ // or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
6
+ // at your option. All files in the project carrying such
7
+ // notice may not be copied, modified, or distributed except
8
+ // according to those terms.
9
+ // SPDX-License-Identifier: Apache-2.0 OR MIT
10
+
11
+ import ShexJTraverser from "@ldo/traverser-shexj";
12
+ import type { Predicate, DataType, Shape } from "../../types.ts";
13
+ import type { ObjectLiteral } from "../../ShexJTypes.ts";
14
+
15
+ const rdfDataTypeToBasic = (dataType: string) => {
16
+ switch (dataType) {
17
+ case "http://www.w3.org/2001/XMLSchema#string":
18
+ case "http://www.w3.org/2001/XMLSchema#ENTITIES":
19
+ case "http://www.w3.org/2001/XMLSchema#ENTITY":
20
+ case "http://www.w3.org/2001/XMLSchema#ID":
21
+ case "http://www.w3.org/2001/XMLSchema#IDREF":
22
+ case "http://www.w3.org/2001/XMLSchema#IDREFS":
23
+ case "http://www.w3.org/2001/XMLSchema#language":
24
+ case "http://www.w3.org/2001/XMLSchema#Name":
25
+ case "http://www.w3.org/2001/XMLSchema#NCName":
26
+ case "http://www.w3.org/2001/XMLSchema#NMTOKEN":
27
+ case "http://www.w3.org/2001/XMLSchema#NMTOKENS":
28
+ case "http://www.w3.org/2001/XMLSchema#normalizedString":
29
+ case "http://www.w3.org/2001/XMLSchema#QName":
30
+ case "http://www.w3.org/2001/XMLSchema#token":
31
+ return "string";
32
+ case "http://www.w3.org/2001/XMLSchema#date":
33
+ case "http://www.w3.org/2001/XMLSchema#dateTime":
34
+ case "http://www.w3.org/2001/XMLSchema#duration":
35
+ case "http://www.w3.org/2001/XMLSchema#gDay":
36
+ case "http://www.w3.org/2001/XMLSchema#gMonth":
37
+ case "http://www.w3.org/2001/XMLSchema#gMonthDay":
38
+ case "http://www.w3.org/2001/XMLSchema#gYear":
39
+ case "http://www.w3.org/2001/XMLSchema#gYearMonth":
40
+ case "http://www.w3.org/2001/XMLSchema#time":
41
+ return "string";
42
+ case "http://www.w3.org/2001/XMLSchema#byte":
43
+ case "http://www.w3.org/2001/XMLSchema#decimal":
44
+ case "http://www.w3.org/2001/XMLSchema#double":
45
+ case "http://www.w3.org/2001/XMLSchema#float":
46
+ case "http://www.w3.org/2001/XMLSchema#int":
47
+ case "http://www.w3.org/2001/XMLSchema#integer":
48
+ case "http://www.w3.org/2001/XMLSchema#long":
49
+ case "http://www.w3.org/2001/XMLSchema#negativeInteger":
50
+ case "http://www.w3.org/2001/XMLSchema#nonNegativeInteger":
51
+ case "http://www.w3.org/2001/XMLSchema#nonPositiveInteger":
52
+ case "http://www.w3.org/2001/XMLSchema#positiveInteger":
53
+ case "http://www.w3.org/2001/XMLSchema#short":
54
+ case "http://www.w3.org/2001/XMLSchema#unsignedLong":
55
+ case "http://www.w3.org/2001/XMLSchema#unsignedInt":
56
+ case "http://www.w3.org/2001/XMLSchema#unsignedShort":
57
+ case "http://www.w3.org/2001/XMLSchema#unsignedByte":
58
+ return "number";
59
+ case "http://www.w3.org/2001/XMLSchema#boolean":
60
+ return "boolean";
61
+ case "http://www.w3.org/2001/XMLSchema#hexBinary":
62
+ return "string";
63
+ case "http://www.w3.org/2001/XMLSchema#anyURI":
64
+ return "iri";
65
+ default:
66
+ return "string";
67
+ }
68
+ };
69
+
70
+ export const ShexJSchemaTransformerCompact = ShexJTraverser.createTransformer<
71
+ {
72
+ Schema: { return: Shape[] };
73
+ ShapeDecl: { return: Shape };
74
+ Shape: { return: Shape };
75
+ EachOf: { return: Shape };
76
+ TripleConstraint: { return: Predicate };
77
+ NodeConstraint: { return: DataType };
78
+ ShapeOr: { return: DataType[] };
79
+ ShapeAnd: { return: never };
80
+ ShapeNot: { return: never };
81
+ ShapeExternal: { return: never };
82
+ },
83
+ null
84
+ >({
85
+ Schema: {
86
+ transformer: async (_schema, getTransformedChildren) => {
87
+ const transformedChildren = await getTransformedChildren();
88
+
89
+ return transformedChildren.shapes || [];
90
+ },
91
+ },
92
+
93
+ ShapeDecl: {
94
+ transformer: async (shapeDecl, getTransformedChildren) => {
95
+ const schema = await getTransformedChildren();
96
+ const shape = schema.shapeExpr as Shape;
97
+
98
+ return { ...shape, iri: shapeDecl.id } as Shape;
99
+ },
100
+ },
101
+
102
+ Shape: {
103
+ transformer: async (_shape, getTransformedChildren) => {
104
+ // TODO: We don't handles those
105
+ _shape.closed;
106
+
107
+ const transformedChildren = await getTransformedChildren();
108
+ const compactShape = transformedChildren.expression as Shape;
109
+
110
+ for (const extra of _shape.extra || []) {
111
+ const extraPredicate = compactShape.predicates.find(
112
+ (p) => p.iri === extra
113
+ );
114
+ if (extraPredicate) extraPredicate.extra = true;
115
+ }
116
+
117
+ return compactShape;
118
+ },
119
+ },
120
+
121
+ // EachOf contains the `expressions` array of properties (TripleConstraint)
122
+ EachOf: {
123
+ transformer: async (eachOf, getTransformedChildren) => {
124
+ const transformedChildren = await getTransformedChildren();
125
+
126
+ return {
127
+ iri: "",
128
+ predicates: transformedChildren.expressions.map(
129
+ // We disregard cases where properties are referenced (strings)
130
+ // or where they consist of Unions or Intersections (not supported).
131
+ (expr) => expr as Predicate
132
+ ),
133
+ };
134
+ },
135
+ },
136
+
137
+ TripleConstraint: {
138
+ transformer: async (
139
+ tripleConstraint,
140
+ getTransformedChildren,
141
+ _setReturnPointer
142
+ ) => {
143
+ const transformedChildren = await getTransformedChildren();
144
+
145
+ const commonProperties = {
146
+ maxCardinality: tripleConstraint.max ?? 1,
147
+ minCardinality: tripleConstraint.min ?? 1,
148
+ iri: tripleConstraint.predicate,
149
+ // @ts-expect-error The ldo library does not have our modded readablePredicate property.
150
+ readablePredicate: tripleConstraint.readablePredicate,
151
+ } satisfies Partial<Predicate>;
152
+ // Make property based on object type which is either a parsed schema, literal or type.
153
+ if (typeof transformedChildren.valueExpr === "string") {
154
+ // Reference to nested object
155
+ return {
156
+ dataTypes: [
157
+ {
158
+ valType: "shape",
159
+ shape: transformedChildren.valueExpr,
160
+ },
161
+ ],
162
+ ...commonProperties,
163
+ };
164
+ } else if (
165
+ transformedChildren.valueExpr &&
166
+ (transformedChildren.valueExpr as Shape).predicates
167
+ ) {
168
+ // Nested object
169
+ return {
170
+ dataTypes: [
171
+ {
172
+ valType: "shape",
173
+ shape: transformedChildren.valueExpr as Shape,
174
+ },
175
+ ],
176
+ ...commonProperties,
177
+ };
178
+ } else if (Array.isArray(transformedChildren.valueExpr)) {
179
+ return {
180
+ dataTypes: transformedChildren.valueExpr, // DataType[]
181
+ ...commonProperties,
182
+ };
183
+ } else {
184
+ // type or literal
185
+ const nodeConstraint =
186
+ transformedChildren.valueExpr as DataType;
187
+ return {
188
+ dataTypes: [
189
+ {
190
+ valType: nodeConstraint.valType,
191
+ literals: nodeConstraint.literals,
192
+ },
193
+ ],
194
+ ...commonProperties,
195
+ };
196
+ }
197
+ },
198
+ },
199
+
200
+ NodeConstraint: {
201
+ transformer: async (nodeConstraint) => {
202
+ if (nodeConstraint.datatype) {
203
+ return {
204
+ valType: rdfDataTypeToBasic(nodeConstraint.datatype),
205
+ };
206
+ }
207
+ if (nodeConstraint.nodeKind) {
208
+ // Something reference-like.
209
+ return { valType: "iri" };
210
+ }
211
+ if (nodeConstraint.values) {
212
+ return {
213
+ valType: "literal",
214
+ literals: nodeConstraint.values.map(
215
+ // TODO: We do not convert them to number or boolean or lang tag.
216
+ // And we don't have an annotation of the literal's type.
217
+ (valueRecord) => {
218
+ // If valueRecord is a string (IRIREF), return it directly
219
+ if (typeof valueRecord === "string") {
220
+ return valueRecord;
221
+ }
222
+ // Handle ObjectLiteral (has .value property)
223
+ if ("value" in valueRecord) {
224
+ return valueRecord.value;
225
+ }
226
+ // Handle other types with .id property (if any)
227
+ if ("id" in valueRecord) {
228
+ return (valueRecord as any).id;
229
+ }
230
+ // Handle Language type (has .languageTag)
231
+ if ("languageTag" in valueRecord) {
232
+ return valueRecord.languageTag;
233
+ }
234
+ // Handle stem-based types (IriStem, LiteralStem, LanguageStem)
235
+ if ("stem" in valueRecord) {
236
+ return valueRecord.stem as string;
237
+ }
238
+ // Fallback - should not happen in well-formed ShEx
239
+ return undefined;
240
+ }
241
+ ),
242
+ };
243
+ }
244
+
245
+ // Maybe we should throw instead...
246
+ throw {
247
+ error: new Error("Could not parse Node Constraint"),
248
+ nodeConstraint,
249
+ };
250
+ },
251
+ },
252
+
253
+ // Transformer from ShapeOr
254
+ ShapeOr: {
255
+ transformer: async (shapeOr, getTransformedChildren) => {
256
+ const { shapeExprs } = await getTransformedChildren();
257
+ // Either a shape IRI, a nested shape or a node CompactSchemaValue (node constraint).
258
+ return (
259
+ Array.isArray(shapeExprs) ? shapeExprs : [shapeExprs]
260
+ ) as DataType[];
261
+ },
262
+ },
263
+
264
+ // Transformer from ShapeAnd
265
+ ShapeAnd: {
266
+ transformer: async () => {
267
+ throw new Error("ShapeAnd not supported (compact)");
268
+ },
269
+ },
270
+
271
+ // Transformer from ShapeNot - not supported.
272
+ ShapeNot: {
273
+ transformer: async () => {
274
+ throw new Error("ShapeNot not supported (compact)");
275
+ },
276
+ },
277
+
278
+ // Transformer from ShapeExternal - not supported.
279
+ ShapeExternal: {
280
+ transformer: async () => {
281
+ throw new Error("ShapeExternal not supported (compact)");
282
+ },
283
+ },
284
+ });