@openuji/vocab-build 0.1.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/README.md ADDED
@@ -0,0 +1,221 @@
1
+ # @openuji/vocab-build
2
+
3
+ Generate publishable semantic web vocabulary assets from JSON-LD schema source files.
4
+
5
+ ## Features
6
+
7
+ - 🎯 **Dual Mode Support**: Editor's Draft (ED) and Technical Report (TR) versioned snapshots
8
+ - 📦 **Complete Asset Generation**: JSON-LD contexts, Turtle/RDF vocabularies, and HTML documentation
9
+ - 🔒 **TR Immutability**: Prevents accidental overwrites of published snapshots
10
+ - 🎨 **Modern HTML Output**: Responsive, accessible vocabulary pages with copy-to-clipboard
11
+ - 🔄 **Redirect Manifests**: Support for Netlify, Cloudflare, and generic JSON formats
12
+ - ✅ **Full Validation**: Zod-based schema validation with detailed error messages
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @openuji/vocab-build
18
+ # or
19
+ pnpm add @openuji/vocab-build
20
+ ```
21
+
22
+ ## CLI Usage
23
+
24
+ ### Build Command
25
+
26
+ Build vocabulary assets in Editor's Draft mode:
27
+
28
+ ```bash
29
+ vocab-build build \
30
+ --input vocab.core.jsonld \
31
+ --out dist \
32
+ --module core \
33
+ --mode ED
34
+ ```
35
+
36
+ Build a Technical Report snapshot:
37
+
38
+ ```bash
39
+ vocab-build build \
40
+ --input vocab.core.jsonld \
41
+ --out dist \
42
+ --module core \
43
+ --mode TR \
44
+ --version 1.0.0
45
+ ```
46
+
47
+ ### Validate Command
48
+
49
+ Validate a vocabulary source file:
50
+
51
+ ```bash
52
+ vocab-build validate --input vocab.core.jsonld
53
+ ```
54
+
55
+ ### Release Command
56
+
57
+ Convenience command for releasing a TR version:
58
+
59
+ ```bash
60
+ vocab-build release --input vocab.core.jsonld
61
+ ```
62
+
63
+ ### CLI Options
64
+
65
+ | Option | Description | Default |
66
+ |--------|-------------|---------|
67
+ | `-i, --input <path>` | Path to vocab source file (required) | - |
68
+ | `-o, --out <dir>` | Output directory | `dist` |
69
+ | `-m, --module <core\|ui>` | Module name (required) | - |
70
+ | `--mode <ED\|TR>` | Build mode (required) | - |
71
+ | `-v, --version <semver>` | Version for TR mode (x.y.z) | - |
72
+ | `-f, --force` | Overwrite existing TR snapshot | `false` |
73
+ | `-b, --base-url <url>` | Base URL for deployment | - |
74
+ | `-r, --redirects <type>` | Redirect type (none\|netlify\|cloudflare\|json) | `netlify` |
75
+ | `-s, --strict` | Fail on unknown fields | `false` |
76
+
77
+ ## Library API
78
+
79
+ ```typescript
80
+ import { buildVocab, validateVocab } from '@openuji/vocab-build';
81
+
82
+ const result = await buildVocab({
83
+ input: 'vocab.core.jsonld',
84
+ output: 'dist',
85
+ module: 'core',
86
+ mode: 'ED',
87
+ });
88
+
89
+ if (result.success) {
90
+ console.log('Generated files:', result.files);
91
+ } else {
92
+ console.error('Errors:', result.errors);
93
+ }
94
+ ```
95
+
96
+ ## Input Format
97
+
98
+ Vocabulary source files use an extended JSON-LD format:
99
+
100
+ ```json
101
+ {
102
+ "module": "core",
103
+ "namespace": "https://ujm.specs.openuji.org/ns#",
104
+ "docBase": "https://ujm.specs.openuji.org/ns",
105
+ "title": "User Journey Map Core Vocabulary",
106
+ "description": "Core vocabulary for defining user journey maps",
107
+ "status": "ED",
108
+ "updated": "2025-12-23",
109
+ "terms": [
110
+ {
111
+ "id": "Journey",
112
+ "kind": "Class",
113
+ "label": "Journey",
114
+ "comment": "A complete user journey"
115
+ },
116
+ {
117
+ "id": "start",
118
+ "kind": "Property",
119
+ "label": "start",
120
+ "comment": "The starting step",
121
+ "domain": "https://ujm.specs.openuji.org/ns#Journey",
122
+ "range": "https://ujm.specs.openuji.org/ns#Step"
123
+ }
124
+ ],
125
+ "context": {
126
+ "Journey": {
127
+ "@id": "https://ujm.specs.openuji.org/ns#Journey",
128
+ "@type": "@id"
129
+ }
130
+ }
131
+ }
132
+ ```
133
+
134
+ ### Required Fields
135
+
136
+ - `module`: `"core"` or `"ui"`
137
+ - `namespace`: Base IRI ending in `#`
138
+ - `docBase`: Base document IRI without `#`
139
+ - `title`, `description`: Human-readable strings
140
+ - `status`: `"ED"` or `"TR"`
141
+ - `terms`: Array of term definitions
142
+
143
+ ### ED-Specific
144
+
145
+ - `updated`: ISO date string (YYYY-MM-DD)
146
+
147
+ ### TR-Specific
148
+
149
+ - `version`: SemVer string (x.y.z)
150
+
151
+ ### Term Definition
152
+
153
+ - `id`: Fragment name (valid identifier)
154
+ - `kind`: `"Class"` or `"Property"`
155
+ - `label`, `comment`: Human-readable strings
156
+ - `domain`, `range`: Optional IRIs
157
+ - `deprecated`: Optional boolean
158
+ - `seeAlso`: Optional array of URLs
159
+
160
+ ## Output Structure
161
+
162
+ ### For `core` module:
163
+
164
+ ```
165
+ dist/
166
+ ns/
167
+ index.html # Latest HTML
168
+ ns.ttl # Latest Turtle
169
+ contexts/
170
+ core.jsonld # Latest JSON-LD context
171
+ ED/
172
+ core/
173
+ index.html # Editor's Draft
174
+ TR/
175
+ core/
176
+ 1.0.0/
177
+ index.html # TR snapshot
178
+ context.jsonld
179
+ ns.ttl
180
+ _redirects # Optional redirect manifest
181
+ ```
182
+
183
+ ### For `ui` module:
184
+
185
+ ```
186
+ dist/
187
+ ui/
188
+ index.html
189
+ ui.ttl
190
+ contexts/
191
+ ui.jsonld
192
+ ED/
193
+ ui/
194
+ index.html
195
+ TR/
196
+ ui/
197
+ 1.0.0/
198
+ index.html
199
+ context.jsonld
200
+ ui.ttl
201
+ ```
202
+
203
+ ## Development
204
+
205
+ ```bash
206
+ # Install dependencies
207
+ pnpm install
208
+
209
+ # Build
210
+ pnpm build
211
+
212
+ # Run tests
213
+ pnpm test
214
+
215
+ # Type check
216
+ pnpm typecheck
217
+ ```
218
+
219
+ ## License
220
+
221
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { buildVocab, validateVocab } from './index.js';
4
+ import { readFile } from 'fs/promises';
5
+ const program = new Command();
6
+ program
7
+ .name('vocab-build')
8
+ .description('Generate publishable semantic web vocabulary assets from JSON-LD schema source files')
9
+ .version('0.1.0');
10
+ /**
11
+ * Build command
12
+ */
13
+ program
14
+ .command('build')
15
+ .description('Build vocabulary assets')
16
+ .requiredOption('-i, --input <path>', 'Path to vocab source file')
17
+ .option('-o, --out <dir>', 'Output directory', 'dist')
18
+ .requiredOption('-m, --module <core|ui>', 'Module name (core or ui)')
19
+ .requiredOption('--mode <ED|TR>', 'Build mode (ED or TR)')
20
+ .option('-v, --version <semver>', 'Version for TR mode (x.y.z)')
21
+ .option('-f, --force', 'Overwrite existing TR snapshot', false)
22
+ .option('-b, --base-url <url>', 'Base URL for deployment')
23
+ .option('-g, --git', 'Use git commit hash and date', false)
24
+ .option('-r, --redirects <type>', 'Redirect type (none|netlify|cloudflare|json)', 'netlify')
25
+ .option('-s, --strict', 'Fail on unknown fields', false)
26
+ .action(async (options) => {
27
+ const config = {
28
+ input: options.input,
29
+ output: options.out,
30
+ module: options.module,
31
+ mode: options.mode,
32
+ version: options.version,
33
+ force: options.force,
34
+ baseUrl: options.baseUrl,
35
+ git: options.git,
36
+ redirects: options.redirects,
37
+ strict: options.strict,
38
+ };
39
+ console.log('🏗️ Building vocabulary...');
40
+ console.log(` Input: ${config.input}`);
41
+ console.log(` Module: ${config.module}`);
42
+ console.log(` Mode: ${config.mode}`);
43
+ if (config.version) {
44
+ console.log(` Version: ${config.version}`);
45
+ }
46
+ const result = await buildVocab(config);
47
+ if (result.success) {
48
+ console.log('\n✅ Build successful!\n');
49
+ console.log('Generated files:');
50
+ result.files.forEach(file => console.log(` - ${file}`));
51
+ }
52
+ else {
53
+ console.error('\n❌ Build failed:\n');
54
+ result.errors?.forEach(error => console.error(` ${error}`));
55
+ process.exit(1);
56
+ }
57
+ });
58
+ /**
59
+ * Validate command
60
+ */
61
+ program
62
+ .command('validate')
63
+ .description('Validate vocabulary source file')
64
+ .requiredOption('-i, --input <path>', 'Path to vocab source file')
65
+ .action(async (options) => {
66
+ console.log('🔍 Validating vocabulary source...');
67
+ console.log(` Input: ${options.input}`);
68
+ const result = await validateVocab(options.input);
69
+ if (result.success) {
70
+ console.log('\n✅ Validation successful!');
71
+ }
72
+ else {
73
+ console.error('\n❌ Validation failed:\n');
74
+ result.errors?.forEach(error => console.error(` ${error}`));
75
+ process.exit(1);
76
+ }
77
+ });
78
+ /**
79
+ * Release command (convenience)
80
+ */
81
+ program
82
+ .command('release')
83
+ .description('Release a new TR version (builds TR + updates latest + generates redirects)')
84
+ .requiredOption('-i, --input <path>', 'Path to vocab source file')
85
+ .option('-o, --out <dir>', 'Output directory', 'dist')
86
+ .option('-b, --base-url <url>', 'Base URL for deployment')
87
+ .action(async (options) => {
88
+ console.log('🚀 Releasing vocabulary...');
89
+ // Read source to get version and module
90
+ const sourceContent = await readFile(options.input, 'utf-8');
91
+ const sourceData = JSON.parse(sourceContent);
92
+ if (!sourceData.version) {
93
+ console.error('❌ Source file must include a version for release');
94
+ process.exit(1);
95
+ }
96
+ if (sourceData.status !== 'TR') {
97
+ console.error('❌ Source file status must be "TR" for release');
98
+ process.exit(1);
99
+ }
100
+ const config = {
101
+ input: options.input,
102
+ output: options.out,
103
+ module: sourceData.module,
104
+ mode: 'TR',
105
+ version: sourceData.version,
106
+ baseUrl: options.baseUrl,
107
+ redirects: 'netlify',
108
+ force: false,
109
+ git: false,
110
+ strict: false,
111
+ };
112
+ const result = await buildVocab(config);
113
+ if (result.success) {
114
+ console.log('\n✅ Release successful!\n');
115
+ console.log(`Released ${config.module} v${config.version}`);
116
+ console.log('\nGenerated files:');
117
+ result.files.forEach(file => console.log(` - ${file}`));
118
+ }
119
+ else {
120
+ console.error('\n❌ Release failed:\n');
121
+ result.errors?.forEach(error => console.error(` ${error}`));
122
+ process.exit(1);
123
+ }
124
+ });
125
+ program.parse();
126
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { VocabSource } from '../model.js';
2
+ /**
3
+ * Generate JSON-LD context from vocabulary source
4
+ */
5
+ export declare function generateContext(source: VocabSource): object;
6
+ /**
7
+ * Format JSON-LD context with Prettier for consistent output
8
+ */
9
+ export declare function formatContext(context: object): Promise<string>;
10
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/generate/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAkB,MAAM,aAAa,CAAC;AAG/D;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAiC3D;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMpE"}
@@ -0,0 +1,48 @@
1
+ import prettier from 'prettier';
2
+ /**
3
+ * Generate JSON-LD context from vocabulary source
4
+ */
5
+ export function generateContext(source) {
6
+ const context = {
7
+ '@version': 0.1,
8
+ };
9
+ // Add custom context mappings if provided
10
+ if (source.context) {
11
+ Object.assign(context, source.context);
12
+ }
13
+ else {
14
+ // Auto-generate context from terms if not provided
15
+ for (const term of source.terms) {
16
+ context[term.id] = {
17
+ '@id': `${source.namespace}${term.id}`,
18
+ };
19
+ }
20
+ }
21
+ // Sort keys for deterministic output
22
+ const sortedContext = {};
23
+ const keys = Object.keys(context).sort((a, b) => {
24
+ // Put @version first
25
+ if (a === '@version')
26
+ return -1;
27
+ if (b === '@version')
28
+ return 1;
29
+ return a.localeCompare(b);
30
+ });
31
+ for (const key of keys) {
32
+ sortedContext[key] = context[key];
33
+ }
34
+ return {
35
+ '@context': sortedContext,
36
+ };
37
+ }
38
+ /**
39
+ * Format JSON-LD context with Prettier for consistent output
40
+ */
41
+ export async function formatContext(context) {
42
+ return prettier.format(JSON.stringify(context, null, 2), {
43
+ parser: 'json',
44
+ printWidth: 80,
45
+ tabWidth: 2,
46
+ });
47
+ }
48
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/generate/context.ts"],"names":[],"mappings":"AACA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,MAAmB;IAC/C,MAAM,OAAO,GAA4B;QACrC,UAAU,EAAE,GAAG;KAClB,CAAC;IAEF,0CAA0C;IAC1C,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACjB,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAC3C,CAAC;SAAM,CAAC;QACJ,mDAAmD;QACnD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YAC9B,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG;gBACf,KAAK,EAAE,GAAG,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,EAAE,EAAE;aACzC,CAAC;QACN,CAAC;IACL,CAAC;IAED,qCAAqC;IACrC,MAAM,aAAa,GAA4B,EAAE,CAAC;IAClD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC5C,qBAAqB;QACrB,IAAI,CAAC,KAAK,UAAU;YAAE,OAAO,CAAC,CAAC,CAAC;QAChC,IAAI,CAAC,KAAK,UAAU;YAAE,OAAO,CAAC,CAAC;QAC/B,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEH,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACrB,aAAa,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IAED,OAAO;QACH,UAAU,EAAE,aAAa;KAC5B,CAAC;AACN,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAAe;IAC/C,OAAO,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;QACrD,MAAM,EAAE,MAAM;QACd,UAAU,EAAE,EAAE;QACd,QAAQ,EAAE,CAAC;KACd,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { VocabSource } from '../model.js';
2
+ export interface HTMLOptions {
3
+ mode: 'ED' | 'TR';
4
+ version?: string;
5
+ baseUrl?: string;
6
+ }
7
+ /**
8
+ * Generate HTML vocabulary page
9
+ */
10
+ export declare function generateHTML(source: VocabSource, options: HTMLOptions): string;
11
+ //# sourceMappingURL=html.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.d.ts","sourceRoot":"","sources":["../../src/generate/html.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAkB,MAAM,aAAa,CAAC;AAO/D,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,WAAW,GAAG,MAAM,CA8B9E"}
@@ -0,0 +1,37 @@
1
+ import nunjucks from 'nunjucks';
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, join } from 'path';
4
+ const __filename = fileURLToPath(import.meta.url);
5
+ const __dirname = dirname(__filename);
6
+ /**
7
+ * Generate HTML vocabulary page
8
+ */
9
+ export function generateHTML(source, options) {
10
+ // Configure Nunjucks to load templates from the templates directory
11
+ const templatesDir = join(__dirname, '..', 'templates');
12
+ const env = nunjucks.configure(templatesDir, {
13
+ autoescape: true,
14
+ trimBlocks: true,
15
+ lstripBlocks: true,
16
+ });
17
+ // Group terms by kind
18
+ const classes = source.terms.filter((t) => t.kind === 'Class').sort((a, b) => a.id.localeCompare(b.id));
19
+ const properties = source.terms.filter((t) => t.kind === 'Property').sort((a, b) => a.id.localeCompare(b.id));
20
+ // Prepare template data
21
+ const templateData = {
22
+ title: source.title,
23
+ description: source.description,
24
+ namespace: source.namespace,
25
+ docBase: source.docBase,
26
+ module: source.module,
27
+ status: source.status,
28
+ mode: options.mode,
29
+ version: options.version || source.version,
30
+ updated: source.updated,
31
+ classes,
32
+ properties,
33
+ baseUrl: options.baseUrl || '',
34
+ };
35
+ return env.render('vocab.html', templateData);
36
+ }
37
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/generate/html.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,UAAU,CAAC;AAEhC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAErC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAQtC;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,MAAmB,EAAE,OAAoB;IAClE,oEAAoE;IACpE,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;IACxD,MAAM,GAAG,GAAG,QAAQ,CAAC,SAAS,CAAC,YAAY,EAAE;QACzC,UAAU,EAAE,IAAI;QAChB,UAAU,EAAE,IAAI;QAChB,YAAY,EAAE,IAAI;KACrB,CAAC,CAAC;IAEH,sBAAsB;IACtB,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAiB,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAiB,EAAE,CAAiB,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACxJ,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAiB,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAiB,EAAE,CAAiB,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAE9J,wBAAwB;IACxB,MAAM,YAAY,GAAG;QACjB,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,MAAM,CAAC,OAAO;QAC1C,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,OAAO;QACP,UAAU;QACV,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE;KACjC,CAAC;IAEF,OAAO,GAAG,CAAC,MAAM,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;AAClD,CAAC"}
@@ -0,0 +1,17 @@
1
+ export type RedirectType = 'none' | 'netlify' | 'cloudflare' | 'json';
2
+ export interface RedirectConfig {
3
+ type: RedirectType;
4
+ module: 'core' | 'ui';
5
+ latestVersion?: string;
6
+ baseUrl?: string;
7
+ }
8
+ export interface Redirect {
9
+ from: string;
10
+ to: string;
11
+ status?: number;
12
+ }
13
+ /**
14
+ * Generate redirect rules based on configuration
15
+ */
16
+ export declare function generateRedirects(config: RedirectConfig): string;
17
+ //# sourceMappingURL=redirects.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redirects.d.ts","sourceRoot":"","sources":["../../src/generate/redirects.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,YAAY,GAAG,MAAM,CAAC;AAEtE,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAmChE"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Generate redirect rules based on configuration
3
+ */
4
+ export function generateRedirects(config) {
5
+ if (config.type === 'none') {
6
+ return '';
7
+ }
8
+ const redirects = [];
9
+ const pathPrefix = config.module === 'core' ? 'ns' : 'ui';
10
+ // Trailing slash normalization
11
+ redirects.push({
12
+ from: `/${pathPrefix}`,
13
+ to: `/${pathPrefix}/`,
14
+ status: 301,
15
+ });
16
+ // TR latest version redirect
17
+ if (config.latestVersion) {
18
+ redirects.push({
19
+ from: `/TR/${config.module}/`,
20
+ to: `/TR/${config.module}/${config.latestVersion}/`,
21
+ status: 302,
22
+ });
23
+ }
24
+ // Format based on type
25
+ switch (config.type) {
26
+ case 'netlify':
27
+ return formatNetlifyRedirects(redirects);
28
+ case 'cloudflare':
29
+ return formatCloudflareRedirects(redirects);
30
+ case 'json':
31
+ return formatJsonRedirects(redirects);
32
+ default:
33
+ return '';
34
+ }
35
+ }
36
+ /**
37
+ * Format redirects for Netlify _redirects file
38
+ */
39
+ function formatNetlifyRedirects(redirects) {
40
+ return redirects.map(r => `${r.from} ${r.to} ${r.status || 301}`).join('\n') + '\n';
41
+ }
42
+ /**
43
+ * Format redirects for Cloudflare Pages
44
+ */
45
+ function formatCloudflareRedirects(redirects) {
46
+ const rules = redirects.map(r => ({
47
+ source: r.from,
48
+ destination: r.to,
49
+ permanent: r.status === 301,
50
+ }));
51
+ return JSON.stringify({ redirects: rules }, null, 2);
52
+ }
53
+ /**
54
+ * Format redirects as generic JSON
55
+ */
56
+ function formatJsonRedirects(redirects) {
57
+ return JSON.stringify(redirects, null, 2);
58
+ }
59
+ //# sourceMappingURL=redirects.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redirects.js","sourceRoot":"","sources":["../../src/generate/redirects.ts"],"names":[],"mappings":"AAeA;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAsB;IACpD,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QACzB,OAAO,EAAE,CAAC;IACd,CAAC;IAED,MAAM,SAAS,GAAe,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;IAE1D,+BAA+B;IAC/B,SAAS,CAAC,IAAI,CAAC;QACX,IAAI,EAAE,IAAI,UAAU,EAAE;QACtB,EAAE,EAAE,IAAI,UAAU,GAAG;QACrB,MAAM,EAAE,GAAG;KACd,CAAC,CAAC;IAEH,6BAA6B;IAC7B,IAAI,MAAM,CAAC,aAAa,EAAE,CAAC;QACvB,SAAS,CAAC,IAAI,CAAC;YACX,IAAI,EAAE,OAAO,MAAM,CAAC,MAAM,GAAG;YAC7B,EAAE,EAAE,OAAO,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,aAAa,GAAG;YACnD,MAAM,EAAE,GAAG;SACd,CAAC,CAAC;IACP,CAAC;IAED,uBAAuB;IACvB,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,SAAS;YACV,OAAO,sBAAsB,CAAC,SAAS,CAAC,CAAC;QAC7C,KAAK,YAAY;YACb,OAAO,yBAAyB,CAAC,SAAS,CAAC,CAAC;QAChD,KAAK,MAAM;YACP,OAAO,mBAAmB,CAAC,SAAS,CAAC,CAAC;QAC1C;YACI,OAAO,EAAE,CAAC;IAClB,CAAC;AACL,CAAC;AAED;;GAEG;AACH,SAAS,sBAAsB,CAAC,SAAqB;IACjD,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAC1F,CAAC;AAED;;GAEG;AACH,SAAS,yBAAyB,CAAC,SAAqB;IACpD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC9B,MAAM,EAAE,CAAC,CAAC,IAAI;QACd,WAAW,EAAE,CAAC,CAAC,EAAE;QACjB,SAAS,EAAE,CAAC,CAAC,MAAM,KAAK,GAAG;KAC9B,CAAC,CAAC,CAAC;IACJ,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AACzD,CAAC;AAED;;GAEG;AACH,SAAS,mBAAmB,CAAC,SAAqB;IAC9C,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { VocabSource } from '../model.js';
2
+ export interface TurtleOptions {
3
+ mode: 'ED' | 'TR';
4
+ version?: string;
5
+ }
6
+ /**
7
+ * Generate Turtle/RDF vocabulary from source
8
+ */
9
+ export declare function generateTurtle(source: VocabSource, options: TurtleOptions): string;
10
+ //# sourceMappingURL=turtle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turtle.d.ts","sourceRoot":"","sources":["../../src/generate/turtle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAuGlF"}
@@ -0,0 +1,66 @@
1
+ import { Writer, DataFactory } from 'n3';
2
+ const { namedNode, literal } = DataFactory;
3
+ /**
4
+ * Generate Turtle/RDF vocabulary from source
5
+ */
6
+ export function generateTurtle(source, options) {
7
+ const writer = new Writer({
8
+ prefixes: {
9
+ rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
10
+ rdfs: 'http://www.w3.org/2000/01/rdf-schema#',
11
+ owl: 'http://www.w3.org/2002/07/owl#',
12
+ xsd: 'http://www.w3.org/2001/XMLSchema#',
13
+ '': source.namespace,
14
+ },
15
+ });
16
+ // Add ontology header
17
+ const ontologyIRI = source.docBase;
18
+ writer.addQuad(namedNode(ontologyIRI), namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), namedNode('http://www.w3.org/2002/07/owl#Ontology'));
19
+ writer.addQuad(namedNode(ontologyIRI), namedNode('http://www.w3.org/2000/01/rdf-schema#label'), literal(source.title));
20
+ writer.addQuad(namedNode(ontologyIRI), namedNode('http://www.w3.org/2000/01/rdf-schema#comment'), literal(source.description));
21
+ // Add version metadata for TR
22
+ if (options.mode === 'TR' && options.version) {
23
+ writer.addQuad(namedNode(ontologyIRI), namedNode('http://www.w3.org/2002/07/owl#versionInfo'), literal(options.version));
24
+ const versionIRI = `${ontologyIRI}/TR/${source.module}/${options.version}/vocab.ttl`;
25
+ writer.addQuad(namedNode(ontologyIRI), namedNode('http://www.w3.org/2002/07/owl#versionIRI'), namedNode(versionIRI));
26
+ }
27
+ // Add imports for UI module (imports core)
28
+ if (source.module === 'ui') {
29
+ writer.addQuad(namedNode(ontologyIRI), namedNode('http://www.w3.org/2002/07/owl#imports'), namedNode('https://ujm.specs.openuji.org/ns'));
30
+ }
31
+ // Add terms
32
+ for (const term of source.terms) {
33
+ const termIRI = `${source.namespace}${term.id}`;
34
+ const termType = term.kind === 'Class'
35
+ ? 'http://www.w3.org/2000/01/rdf-schema#Class'
36
+ : 'http://www.w3.org/1999/02/22-rdf-syntax-ns#Property';
37
+ writer.addQuad(namedNode(termIRI), namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type'), namedNode(termType));
38
+ writer.addQuad(namedNode(termIRI), namedNode('http://www.w3.org/2000/01/rdf-schema#label'), literal(term.label));
39
+ writer.addQuad(namedNode(termIRI), namedNode('http://www.w3.org/2000/01/rdf-schema#comment'), literal(term.comment));
40
+ // Add domain and range if present
41
+ if (term.domain) {
42
+ writer.addQuad(namedNode(termIRI), namedNode('http://www.w3.org/2000/01/rdf-schema#domain'), namedNode(term.domain));
43
+ }
44
+ if (term.range) {
45
+ writer.addQuad(namedNode(termIRI), namedNode('http://www.w3.org/2000/01/rdf-schema#range'), namedNode(term.range));
46
+ }
47
+ // Add deprecated marker
48
+ if (term.deprecated) {
49
+ writer.addQuad(namedNode(termIRI), namedNode('http://www.w3.org/2002/07/owl#deprecated'), literal('true', namedNode('http://www.w3.org/2001/XMLSchema#boolean')));
50
+ }
51
+ // Add seeAlso links
52
+ if (term.seeAlso) {
53
+ for (const url of term.seeAlso) {
54
+ writer.addQuad(namedNode(termIRI), namedNode('http://www.w3.org/2000/01/rdf-schema#seeAlso'), namedNode(url));
55
+ }
56
+ }
57
+ }
58
+ let result = '';
59
+ writer.end((error, output) => {
60
+ if (error)
61
+ throw error;
62
+ result = output;
63
+ });
64
+ return result;
65
+ }
66
+ //# sourceMappingURL=turtle.js.map