ldkit 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/esm/cli.js CHANGED
@@ -1,11 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { argv } from "node:process";
3
3
  import { styleText } from "node:util";
4
- import { readFileSync } from "node:fs";
4
+ import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
+ import { join } from "node:path";
5
6
  import { Argument, Command } from "commander";
6
7
  import { contextToSchema } from "./scripts/context_to_schema.js";
7
8
  import { shexcToSchema, shexjToSchema } from "./scripts/shex_to_schema.js";
9
+ import { shaclToSchema } from "./scripts/shacl_to_schema.js";
8
10
  import { schemaToScript } from "./scripts/schema_to_script.js";
11
+ import { schemaToPackage } from "./scripts/schema_to_package.js";
9
12
  const asciiArt = String.raw `
10
13
  _ ____ _ _ _
11
14
  | | | _ \| | _(_) |_
@@ -85,6 +88,56 @@ program.command("shexj-to-schema")
85
88
  console.error(styleText("red", `${error.message}`));
86
89
  }
87
90
  });
91
+ program.command("shacl-to-schema")
92
+ .description("Convert a SHACL shapes graph from a file or URL to a LDkit schema")
93
+ .addArgument(new Argument("<method>", "type of input").choices([
94
+ "url",
95
+ "file",
96
+ "arg",
97
+ ]))
98
+ .argument("<input>", "input SHACL Turtle - file, URL, or string")
99
+ .option("--prefix-alias <mapping>", "rename a SHACL prefix in generated schema names (format: prefix=Alias). Repeatable.", (value, previous) => [...previous, value], [])
100
+ .action(async (method, input, opts) => {
101
+ try {
102
+ const resolvedInput = await resolve(method, input);
103
+ const prefixAliases = parsePrefixAliases(opts.prefixAlias);
104
+ const { schemas, extraNamespaces } = shaclToSchema(resolvedInput, {
105
+ prefixAliases,
106
+ });
107
+ console.log(schemaToScript(schemas, extraNamespaces));
108
+ }
109
+ catch (error) {
110
+ console.error(styleText("red", `${error.message}`));
111
+ }
112
+ });
113
+ program.command("shacl-to-package")
114
+ .description("Convert a SHACL shapes graph into a directory of per-namespace LDkit schema files (one .ts per prefix plus an index.ts barrel)")
115
+ .addArgument(new Argument("<method>", "type of input").choices([
116
+ "url",
117
+ "file",
118
+ "arg",
119
+ ]))
120
+ .argument("<input>", "input SHACL Turtle - file, URL, or string")
121
+ .argument("<outDir>", "output directory for the generated package")
122
+ .option("--prefix-alias <mapping>", "rename a SHACL prefix in generated schema names AND in the per-namespace file name (format: prefix=Alias). Repeatable.", (value, previous) => [...previous, value], [])
123
+ .action(async (method, input, outDir, opts) => {
124
+ try {
125
+ const resolvedInput = await resolve(method, input);
126
+ const prefixAliases = parsePrefixAliases(opts.prefixAlias);
127
+ const { schemas, extraNamespaces, schemaSourcePrefixes } = shaclToSchema(resolvedInput, { prefixAliases });
128
+ const { files } = schemaToPackage(schemas, extraNamespaces, {
129
+ prefixAliases,
130
+ schemaSourcePrefixes,
131
+ });
132
+ mkdirSync(outDir, { recursive: true });
133
+ for (const [base, contents] of files) {
134
+ writeFileSync(join(outDir, `${base}.ts`), contents);
135
+ }
136
+ }
137
+ catch (error) {
138
+ console.error(styleText("red", `${error.message}`));
139
+ }
140
+ });
88
141
  // Check if no arguments were provided
89
142
  if (argv.length <= 2) {
90
143
  console.log(styleText("red", asciiArt));
@@ -93,6 +146,21 @@ if (argv.length <= 2) {
93
146
  else {
94
147
  program.parse(argv);
95
148
  }
149
+ function parsePrefixAliases(pairs) {
150
+ if (!pairs || pairs.length === 0)
151
+ return {};
152
+ const result = {};
153
+ for (const pair of pairs) {
154
+ const eq = pair.indexOf("=");
155
+ if (eq <= 0 || eq === pair.length - 1) {
156
+ throw new Error(`Invalid --prefix-alias value "${pair}" (expected format: prefix=Alias)`);
157
+ }
158
+ const prefix = pair.substring(0, eq);
159
+ const alias = pair.substring(eq + 1);
160
+ result[prefix] = alias;
161
+ }
162
+ return result;
163
+ }
96
164
  async function resolve(method, input) {
97
165
  if (method === "url") {
98
166
  try {
@@ -63,6 +63,10 @@ export declare function resolveOptions(options?: Options): {
63
63
  httpRetryDelayFallback?: number;
64
64
  httpRetryDelayLimit?: number;
65
65
  httpRetryStatusCodes?: number[];
66
+ httpRetryBodyCount?: number;
67
+ httpRetryBodyDelayFallback?: number;
68
+ httpRetryBodyAllowUnsafe?: boolean;
69
+ httpRetryBodyMaxBytes?: number;
66
70
  httpAbortSignal?: AbortSignal;
67
71
  httpCache?: boolean;
68
72
  fetch?: typeof fetch;
@@ -1 +1 @@
1
- {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/library/options.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EAElB,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;GAQG;AACH,MAAM,MAAM,OAAO,GAAG;IACpB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;AAU1B;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEvD;AAED,wBAAgB,cAAc,CAAC,OAAO,GAAE,OAAY;YAxCzC,YAAY;eACV,MAAM;UACV,MAAM;cACF,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2CnC;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,CAclE"}
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/library/options.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,YAAY,EAElB,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;GAQG;AACH,MAAM,MAAM,OAAO,GAAG;IACpB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;AAU1B;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEvD;AAED,wBAAgB,cAAc,CAAC,OAAO,GAAE,OAAY;YAxCzC,YAAY;eACV,MAAM;UACV,MAAM;cACF,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2CnC;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,CAclE"}
@@ -0,0 +1,10 @@
1
+ import { type ExtraNamespace, type SchemaSpec } from "./schema_to_script.js";
2
+ export type SchemaToPackageOptions = {
3
+ prefixAliases?: Record<string, string>;
4
+ schemaSourcePrefixes?: Map<string, string>;
5
+ };
6
+ export type SchemaPackage = {
7
+ files: Map<string, string>;
8
+ };
9
+ export declare function schemaToPackage(schemas: SchemaSpec[], extraNamespaces?: ExtraNamespace[], options?: SchemaToPackageOptions): SchemaPackage;
10
+ //# sourceMappingURL=schema_to_package.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema_to_package.d.ts","sourceRoot":"","sources":["../../src/scripts/schema_to_package.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,UAAU,EAEhB,MAAM,uBAAuB,CAAC;AAE/B,MAAM,MAAM,sBAAsB,GAAG;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,oBAAoB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC5B,CAAC;AAKF,wBAAgB,eAAe,CAC7B,OAAO,EAAE,UAAU,EAAE,EACrB,eAAe,GAAE,cAAc,EAAO,EACtC,OAAO,GAAE,sBAA2B,GACnC,aAAa,CAkDf"}
@@ -0,0 +1,109 @@
1
+ import { schemaToScript, } from "./schema_to_script.js";
2
+ const FALLBACK_FILE = "_unknown";
3
+ const NAMESPACES_FILE = "namespaces";
4
+ export function schemaToPackage(schemas, extraNamespaces = [], options = {}) {
5
+ const aliases = options.prefixAliases ?? {};
6
+ const sourcePrefixes = options.schemaSourcePrefixes ?? new Map();
7
+ const fileForPrefix = (prefix) => {
8
+ if (!prefix)
9
+ return FALLBACK_FILE;
10
+ const aliased = aliases[prefix];
11
+ return (aliased ?? prefix).toLowerCase();
12
+ };
13
+ const groups = new Map();
14
+ const schemaLocations = new Map();
15
+ for (const schema of schemas) {
16
+ const file = fileForPrefix(sourcePrefixes.get(schema.name));
17
+ schemaLocations.set(schema.name, file);
18
+ let bucket = groups.get(file);
19
+ if (!bucket) {
20
+ bucket = [];
21
+ groups.set(file, bucket);
22
+ }
23
+ bucket.push(schema);
24
+ }
25
+ const extraNamespaceTermsOverride = collectGlobalNamespaceTerms(extraNamespaces, schemas);
26
+ const files = new Map();
27
+ for (const [file, fileSchemas] of [...groups.entries()].toSorted(([a], [b]) => a.localeCompare(b))) {
28
+ const scopedExtras = filterExtras(extraNamespaces, fileSchemas);
29
+ const contents = schemaToScript(fileSchemas, scopedExtras, {
30
+ schemaLocations,
31
+ currentFile: file,
32
+ extraNamespacesImportFrom: NAMESPACES_FILE,
33
+ });
34
+ files.set(file, contents);
35
+ }
36
+ if (extraNamespaces.length > 0) {
37
+ files.set(NAMESPACES_FILE, buildNamespacesFile(extraNamespaces, extraNamespaceTermsOverride));
38
+ }
39
+ files.set("index", buildIndex(files));
40
+ return { files };
41
+ }
42
+ function buildNamespacesFile(extras, termsByPrefix) {
43
+ const lines = [`import { createNamespace } from "ldkit";`, ""];
44
+ const sorted = [...extras].sort((a, b) => a.prefix.localeCompare(b.prefix));
45
+ for (const ns of sorted) {
46
+ const terms = [...(termsByPrefix.get(ns.prefix) ?? new Set())]
47
+ .toSorted();
48
+ lines.push(`export const ${ns.prefix} = createNamespace(`);
49
+ lines.push(` {`);
50
+ lines.push(` iri: ${JSON.stringify(ns.iri)},`);
51
+ lines.push(` prefix: ${JSON.stringify(`${ns.prefix}:`)},`);
52
+ lines.push(` terms: [`);
53
+ for (const term of terms) {
54
+ lines.push(` ${JSON.stringify(term)},`);
55
+ }
56
+ lines.push(` ],`);
57
+ lines.push(` } as const,`);
58
+ lines.push(`);`);
59
+ lines.push("");
60
+ }
61
+ return lines.join("\n");
62
+ }
63
+ function filterExtras(extras, schemas) {
64
+ if (extras.length === 0)
65
+ return [];
66
+ const usedIris = collectUsedIris(schemas);
67
+ return extras.filter((ns) => [...usedIris].some((iri) => iri.startsWith(ns.iri)));
68
+ }
69
+ function collectUsedIris(schemas) {
70
+ const used = new Set();
71
+ for (const schema of schemas) {
72
+ for (const t of schema.type)
73
+ used.add(t);
74
+ for (const prop of Object.values(schema.properties)) {
75
+ used.add(prop.id);
76
+ if (prop.type)
77
+ used.add(prop.type);
78
+ if (prop.schema) {
79
+ for (const iri of collectUsedIris([prop.schema]))
80
+ used.add(iri);
81
+ }
82
+ }
83
+ }
84
+ return used;
85
+ }
86
+ function collectGlobalNamespaceTerms(extras, schemas) {
87
+ const sortedExtras = [...extras].sort((a, b) => b.iri.length - a.iri.length);
88
+ const result = new Map();
89
+ const allIris = collectUsedIris(schemas);
90
+ for (const iri of allIris) {
91
+ for (const ns of sortedExtras) {
92
+ if (iri.startsWith(ns.iri)) {
93
+ const term = iri.substring(ns.iri.length);
94
+ let bucket = result.get(ns.prefix);
95
+ if (!bucket) {
96
+ bucket = new Set();
97
+ result.set(ns.prefix, bucket);
98
+ }
99
+ bucket.add(term);
100
+ break;
101
+ }
102
+ }
103
+ }
104
+ return result;
105
+ }
106
+ function buildIndex(files) {
107
+ const names = [...files.keys()].filter((f) => f !== "index").toSorted();
108
+ return names.map((name) => `export * from "./${name}";`).join("\n") + "\n";
109
+ }