@ng-org/shex-orm 0.1.2-alpha.3 → 0.1.2-alpha.4

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.
@@ -55,8 +55,69 @@ function buildSchema() {
55
55
  },
56
56
  },
57
57
  },
58
+ {
59
+ type: "ShapeDecl",
60
+ id: "http://example.org/CollisionTest",
61
+ shapeExpr: {
62
+ type: "Shape",
63
+ extra: [TYPE_IRI],
64
+ expression: {
65
+ type: "EachOf",
66
+ expressions: [
67
+ {
68
+ type: "TripleConstraint",
69
+ predicate: "http://example.org/collide1/foo",
70
+ valueExpr: {
71
+ type: "NodeConstraint",
72
+ datatype: "http://www.w3.org/2001/XMLSchema#string",
73
+ },
74
+ },
75
+ {
76
+ type: "TripleConstraint",
77
+ predicate: "http://example.org/collide2/foo",
78
+ valueExpr: {
79
+ type: "NodeConstraint",
80
+ datatype: "http://www.w3.org/2001/XMLSchema#string",
81
+ },
82
+ },
83
+ {
84
+ type: "TripleConstraint",
85
+ predicate: "http://example.org/collide3/foo",
86
+ valueExpr: {
87
+ type: "NodeConstraint",
88
+ datatype: "http://www.w3.org/2001/XMLSchema#string",
89
+ },
90
+ },
91
+ {
92
+ type: "TripleConstraint",
93
+ predicate: "http://example.org2/collide3/foo",
94
+ valueExpr: {
95
+ type: "NodeConstraint",
96
+ datatype: "http://www.w3.org/2001/XMLSchema#string",
97
+ },
98
+ },
99
+ {
100
+ type: "TripleConstraint",
101
+ predicate: "http://example.org:collide4/foo",
102
+ valueExpr: {
103
+ type: "NodeConstraint",
104
+ datatype: "http://www.w3.org/2001/XMLSchema#string",
105
+ },
106
+ },
107
+ {
108
+ type: "TripleConstraint",
109
+ predicate: "http://example.org/collide4/foo",
110
+ valueExpr: {
111
+ type: "NodeConstraint",
112
+ datatype: "http://www.w3.org/2001/XMLSchema#string",
113
+ },
114
+ },
115
+ ],
116
+ },
117
+ },
118
+ },
58
119
  ],
59
- };
120
+ }; // satisfies Schema;
60
121
  }
61
122
  async function buildTypingsText() {
62
123
  const schema = buildSchema();
@@ -71,6 +132,15 @@ describe("ShexJTypingTransformer", () => {
71
132
  });
72
133
  it("treats EXTRA rdf:type predicates as plural", async () => {
73
134
  const typings = await buildTypingsText();
74
- expect(typings).toMatch(/interface ExpenseCategory[\s\S]*?"@type": Set<"http:\/\/example\.org\/ExpenseCategory" \|[\s]*\(?string[\s]*&[\s]*\{[\s]*\}\)?>;/);
135
+ expect(typings).toMatch(/\"@type\": Set<\"http:\/\/example\.org\/ExpenseCategory\" | IRI & {}>;/);
136
+ });
137
+ it("handles property name collisions", async () => {
138
+ const typings = await buildTypingsText();
139
+ expect(typings).toMatch(/ collide1_foo: string/);
140
+ expect(typings).toMatch(/ collide2_foo: string/);
141
+ expect(typings).toMatch(/ org_collide3_foo: string/);
142
+ expect(typings).toMatch(/ org2_collide3_foo: string/);
143
+ expect(typings).toMatch(/ "0__http_example_org_collide4_foo"\: string/);
144
+ expect(typings).toMatch(/ "1__http_example_org_collide4_foo"\: string/);
75
145
  });
76
146
  });
@@ -1 +1 @@
1
- {"version":3,"file":"annotateReadablePredicates.d.ts","sourceRoot":"","sources":["../../../src/schema-converter/util/annotateReadablePredicates.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,EAA8C,MAAM,OAAO,CAAC;AAUhF;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAmJvE"}
1
+ {"version":3,"file":"annotateReadablePredicates.d.ts","sourceRoot":"","sources":["../../../src/schema-converter/util/annotateReadablePredicates.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,MAAM,EAA8C,MAAM,OAAO,CAAC;AAUhF;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CA0MvE"}
@@ -8,9 +8,9 @@
8
8
  // according to those terms.
9
9
  // SPDX-License-Identifier: Apache-2.0 OR MIT
10
10
  // Split IRI by colon, slash and hash; drop empties
11
- const splitIriTokens = (iri) => iri.split(/[:/#]+/).filter(Boolean);
11
+ const splitIriTokens = (iri) => iri.split(/[:/#.]+/).filter(Boolean);
12
12
  // Keep dots and dashes (so 0.1 stays as 0.1) but sanitize everything else
13
- const sanitize = (s) => s.replace(/[^\w.\-]/g, "_");
13
+ const sanitize = (s) => s.replace(/[^\w.\-@]/g, "_");
14
14
  /**
15
15
  * Annotate EachOf-level TripleConstraints with a collision-free readablePredicate.
16
16
  * Rule: for any group that shares the same local token, rename all members using
@@ -23,73 +23,102 @@ export default function annotateReadablePredicates(schema) {
23
23
  eachOf.type !== "EachOf" ||
24
24
  !Array.isArray(eachOf.expressions))
25
25
  return;
26
- const tcs = eachOf.expressions.filter((e) => typeof e === "object" &&
26
+ const tripleConstraints = eachOf.expressions.filter((e) => typeof e === "object" &&
27
27
  e !== null &&
28
28
  e.type === "TripleConstraint");
29
- if (tcs.length > 0) {
30
- // Group by local token (last segment of IRI) and set a base readablePredicate for all
31
- const readableNameToPredicatesMap = new Map();
32
- for (const tripleConstraint of tcs) {
33
- // Use the name based on the IRI ending.
34
- let readableName;
35
- // Special case rdfs:type => @type
36
- if (tripleConstraint.predicate ===
37
- "http://www.w3.org/1999/02/22-rdf-syntax-ns#type") {
38
- readableName = "@type";
29
+ if (tripleConstraints.length > 0) {
30
+ // Add a triple constraint (tc) to parent tree node.
31
+ // The tree branches on name collisions.
32
+ const addToPreds = (depth, parent, iriElements, tc) => {
33
+ // Get the name of the next IRI part (e.g. the foaf from foaf_name).
34
+ // It can be that we are out of bounds. In that case we use "".
35
+ // That way the final name remains the same, unless we still collisions.
36
+ // In that case, we enumerate.
37
+ const key = iriElements[depth] ?? "";
38
+ // Case no collision: Add triple constraint as leaf.
39
+ if (!parent.children[key]) {
40
+ parent.children[key] = {
41
+ leaf: { tc, iriElements },
42
+ children: {},
43
+ };
39
44
  }
40
- else {
41
- const tokens = splitIriTokens(tripleConstraint.predicate);
42
- readableName = tokens.length
43
- ? tokens[tokens.length - 1]
44
- : tripleConstraint.predicate;
45
- }
46
- // default base name for non-colliders
47
- tripleConstraint.readablePredicate = readableName;
48
- const groupMembers = readableNameToPredicatesMap.get(readableName) ?? [];
49
- groupMembers.push(tripleConstraint);
50
- readableNameToPredicatesMap.set(readableName, groupMembers);
51
- }
52
- // Resolve each group (rename all in collisions)
53
- for (const [, groupMembers] of readableNameToPredicatesMap) {
54
- if (groupMembers.length <= 1)
55
- continue;
56
- const used = new Set();
57
- const local = splitIriTokens(groupMembers[0].predicate).slice(-1)[0] ??
58
- "";
59
- for (const tc of groupMembers) {
60
- const tokens = splitIriTokens(tc.predicate);
61
- let localIdx = tokens.lastIndexOf(local);
62
- if (localIdx === -1)
63
- localIdx = Math.max(tokens.length - 1, 0);
64
- let prefixIdx = localIdx - 1;
65
- let assigned = false;
66
- while (prefixIdx >= 0) {
67
- const cand = `${sanitize(tokens[prefixIdx])}_${sanitize(tokens[localIdx])}`;
68
- if (!used.has(cand)) {
69
- tc.readablePredicate = cand;
70
- used.add(cand);
71
- assigned = true;
72
- break;
73
- }
74
- prefixIdx -= 1;
45
+ else if (key === "") {
46
+ // Case out of bounds but not the only one
47
+ // Add a counter prefix
48
+ const node = parent.children[key];
49
+ if (node.leaf) {
50
+ // If this child has a leaf, that means it didn't have children before.
51
+ // Add a __counter prefix__ now.
52
+ node.children = {
53
+ ["0"]: {
54
+ leaf: node.leaf,
55
+ children: {},
56
+ },
57
+ };
58
+ // Remove moved leaf from old node.
59
+ node.leaf = false;
75
60
  }
76
- if (!assigned) {
77
- const iriNoProto = tc.predicate.replace(/^[a-z]+:\/\//i, "");
78
- const composite = sanitize(iriNoProto
79
- .split(/[:/#]+/)
80
- .slice(0, -1)
81
- .join("_") || "iri");
82
- let cand = `${composite}_${sanitize(tokens[localIdx] || local)}`;
83
- let n = 1;
84
- while (used.has(cand))
85
- cand = `${cand}_${n++}`;
86
- tc.readablePredicate = cand;
87
- used.add(cand);
61
+ // Add counter to iriElements -> will be picked up by recursion call.
62
+ iriElements[depth + 1] = Object.keys(node.children).length.toString();
63
+ addToPreds(depth + 1, node, iriElements, tc);
64
+ }
65
+ else {
66
+ // Case collision: create a new child
67
+ const node = parent.children[key];
68
+ if (node.leaf) {
69
+ // If this child has a leaf, that means it didn't have children before.
70
+ // Move the leaf to the new children.
71
+ const childKey = node.leaf.iriElements[depth + 1] ?? "";
72
+ node.children = {
73
+ [childKey]: {
74
+ leaf: node.leaf,
75
+ children: {},
76
+ },
77
+ };
78
+ // Remove moved leaf from old node.
79
+ node.leaf = false;
88
80
  }
81
+ addToPreds(depth + 1, node, iriElements, tc);
82
+ }
83
+ };
84
+ // Root structure to keep names for triple constraints.
85
+ // Keys are the readable names of the predicates
86
+ const rootPredTree = {
87
+ leaf: false,
88
+ children: {},
89
+ };
90
+ // Add all triple constraints to root tree.
91
+ for (const tripleConstraint of tripleConstraints) {
92
+ const iri = tripleConstraint.predicate;
93
+ if (iri === "http://www.w3.org/1999/02/22-rdf-syntax-ns#type") {
94
+ // Special case: convert type predicate to @type
95
+ addToPreds(0, rootPredTree, ["@type"], tripleConstraint);
96
+ }
97
+ else {
98
+ addToPreds(0, rootPredTree,
99
+ // Divide IRI in sanitized parts, start from end (property name)
100
+ splitIriTokens(iri).map(sanitize).reverse(), tripleConstraint);
89
101
  }
90
102
  }
103
+ // Traverse tree and annotate
104
+ const annotatePreds = (parentTree, accumulatedName) => {
105
+ // If we reached the leaf, annotate with name
106
+ if (parentTree?.leaf) {
107
+ parentTree.leaf.tc.readablePredicate = accumulatedName;
108
+ return;
109
+ }
110
+ // Recurse for all children.
111
+ for (const key of Object.keys(parentTree.children ?? {})) {
112
+ const name = accumulatedName === ""
113
+ ? key // Just use name
114
+ : `${key}_${accumulatedName}`; // Make composite.
115
+ // Annotate children
116
+ annotatePreds(parentTree.children[key], name);
117
+ }
118
+ };
119
+ annotatePreds(rootPredTree, "");
91
120
  // Recurse into nested valueExpr shapes of each TC
92
- for (const tc of tcs) {
121
+ for (const tc of tripleConstraints) {
93
122
  const ve = tc.valueExpr;
94
123
  if (ve && typeof ve === "object") {
95
124
  const t = ve.type;
package/package.json CHANGED
@@ -1,22 +1,11 @@
1
1
  {
2
2
  "name": "@ng-org/shex-orm",
3
- "version": "0.1.2-alpha.3",
3
+ "version": "0.1.2-alpha.4",
4
4
  "description": "",
5
5
  "type": "module",
6
- "main": "src/index.ts",
6
+ "main": "./dist/index.js",
7
7
  "bin": {
8
- "rdf-orm": "./src/cli.ts"
9
- },
10
- "scripts": {
11
- "build": "pnpm run build:ts",
12
- "build:ts": "rm -rf dist && tsc && pnpm run update-permission && pnpm copy-templates",
13
- "copy-templates": "mkdir -p dist && rsync -qav --include=\"*/\" --include=\"*.ejs\" --exclude=\"*\" src/ dist/",
14
- "update-permission": "chmod +x ./dist/cli.js",
15
- "test": "vitest run",
16
- "prepublishOnly": "pnpm run build",
17
- "lint": "eslint src/** --fix --no-error-on-unmatched-pattern",
18
- "test:init": "rm -rf ./example-init && cp -R ./example-init-placeholder ./example-init && cd ./example-init && ../dist/index.js init",
19
- "test:create": "rm -rf ./example-create && ./dist/index.js create ./example-create"
8
+ "rdf-orm": "./dist/cli.js"
20
9
  },
21
10
  "authors": [
22
11
  "Laurin Weger",
@@ -28,39 +17,37 @@
28
17
  "@types/ejs": "^3.1.1",
29
18
  "@types/fs-extra": "^9.0.13",
30
19
  "@types/jsonld": "^1.5.15",
31
- "@types/prompts": "^2.4.9",
32
20
  "@types/shexj": "^2.1.4",
33
21
  "typescript": "^5.9.2",
34
22
  "vitest": "^3.2.4"
35
23
  },
36
24
  "dependencies": {
37
- "@jeswr/shacl2shex": "^1.1.0",
38
25
  "@ldo/traverser-shexj": "1.0.0-alpha.28",
39
26
  "@ldo/type-traverser": "1.0.0-alpha.28",
40
27
  "@shexjs/parser": "^1.0.0-alpha.24",
41
- "child-process-promise": "^2.2.1",
42
28
  "commander": "^14.0.1",
43
29
  "dts-dom": "~3.6.0",
44
30
  "ejs": "^3.1.8",
45
31
  "fs-extra": "^10.1.0",
46
32
  "jsonld2graphobject": "^0.0.5",
47
33
  "loading-cli": "^1.1.0",
48
- "prettier": "^3.0.3",
49
- "prompts": "^2.4.2",
50
- "rdf-dereference-store": "^1.4.0",
51
- "rdf-namespaces": "^1.13.1",
52
- "ts-morph": "^24.0.0",
53
- "type-fest": "^2.19.0"
34
+ "prettier": "^3.0.3"
54
35
  },
55
36
  "files": [
56
37
  "dist"
57
38
  ],
58
39
  "publishConfig": {
59
- "bin": {
60
- "rdf-orm": "./dist/cli.js"
61
- },
62
- "access": "public",
63
- "main": "./dist/index.js",
64
- "types": "./dist/index.d.ts"
65
- }
66
- }
40
+ "access": "public"
41
+ },
42
+ "scripts": {
43
+ "build": "pnpm run build:ts",
44
+ "build:ts": "rm -rf dist && tsc && pnpm run update-permission && pnpm copy-templates",
45
+ "copy-templates": "mkdir -p dist && rsync -qav --include=\"*/\" --include=\"*.ejs\" --exclude=\"*\" src/ dist/",
46
+ "update-permission": "chmod +x ./dist/cli.js",
47
+ "test": "vitest run",
48
+ "lint": "eslint src/** --fix --no-error-on-unmatched-pattern",
49
+ "test:init": "rm -rf ./example-init && cp -R ./example-init-placeholder ./example-init && cd ./example-init && ../dist/index.js init",
50
+ "test:create": "rm -rf ./example-create && ./dist/index.js create ./example-create"
51
+ },
52
+ "types": "./dist/index.d.ts"
53
+ }
package/src/cli.ts DELETED
@@ -1,23 +0,0 @@
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 DELETED
@@ -1 +0,0 @@
1
- export * from "./types.ts";