datacontract-editor 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.
Files changed (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +54 -0
  3. package/bin/datacontract-editor.js +373 -0
  4. package/dist/abap-Bs8kvjsq.js +1398 -0
  5. package/dist/apex-Dspwrl5m.js +326 -0
  6. package/dist/assets/editor.worker-CKX8jfJ6.js +9037 -0
  7. package/dist/assets/yaml.worker-BqEbEWOg.js +26455 -0
  8. package/dist/azcli-k1BiOau1.js +68 -0
  9. package/dist/bat-BIP72KWc.js +100 -0
  10. package/dist/bicep-VhQeYhS3.js +102 -0
  11. package/dist/cameligo-DnuAXtHg.js +174 -0
  12. package/dist/clojure-BAuDPsal.js +761 -0
  13. package/dist/coffee-BV06qSnj.js +232 -0
  14. package/dist/cpp-Cz8Jeyrf.js +389 -0
  15. package/dist/csharp-F6hhlXFb.js +326 -0
  16. package/dist/csp-KzlEHCyI.js +53 -0
  17. package/dist/css-e4FKntZ3.js +187 -0
  18. package/dist/cssMode-CzVfCu2q.js +1618 -0
  19. package/dist/cypher-CpvDGKRr.js +263 -0
  20. package/dist/dart-DdHeFWR0.js +281 -0
  21. package/dist/datacontract-editor.css +1 -0
  22. package/dist/datacontract-editor.es.js +7 -0
  23. package/dist/dockerfile-aoNXDcio.js +130 -0
  24. package/dist/ecl-C4Q0DYuA.js +456 -0
  25. package/dist/elixir-C9X42b0T.js +569 -0
  26. package/dist/embed-BE-6FjUW.js +170984 -0
  27. package/dist/flow9-Bff42pfB.js +142 -0
  28. package/dist/freemarker2-DuISTfha.js +989 -0
  29. package/dist/fsharp-C6vPAsHG.js +217 -0
  30. package/dist/go-DEr8zx9f.js +218 -0
  31. package/dist/graphql-BiNBBRFh.js +151 -0
  32. package/dist/handlebars-CF6nf90Y.js +419 -0
  33. package/dist/hcl-2T9lKuFg.js +183 -0
  34. package/dist/html-Bd2leUjH.js +308 -0
  35. package/dist/htmlMode-D5ZCqfos.js +1628 -0
  36. package/dist/index.html +27 -0
  37. package/dist/ini-CZGtssYM.js +71 -0
  38. package/dist/java-B260NeH3.js +232 -0
  39. package/dist/javascript-DM0zQPyb.js +70 -0
  40. package/dist/jsonMode-kmlqwbu7.js +2051 -0
  41. package/dist/julia-kuNLM9c5.js +511 -0
  42. package/dist/kotlin-Bz6kAefA.js +253 -0
  43. package/dist/less-AZUtS89I.js +162 -0
  44. package/dist/lexon-BcmgnyRb.js +157 -0
  45. package/dist/liquid-BqQPT6Qf.js +240 -0
  46. package/dist/logo_fuchsia_v2.svg +13 -0
  47. package/dist/lua-Dz1RbATg.js +162 -0
  48. package/dist/m3-CIMooLFn.js +210 -0
  49. package/dist/markdown-C1BsCK9P.js +229 -0
  50. package/dist/mdx-8KgfCRJw.js +165 -0
  51. package/dist/mips-BovsnfLQ.js +198 -0
  52. package/dist/msdax-CiRB2ZB-.js +375 -0
  53. package/dist/mysql-BwdpPE19.js +878 -0
  54. package/dist/objective-c-D4nfHfso.js +183 -0
  55. package/dist/odcs.svg +24 -0
  56. package/dist/pascal--j5-PYtz.js +251 -0
  57. package/dist/pascaligo-C7TcJxNL.js +164 -0
  58. package/dist/perl-BzEWqUb5.js +626 -0
  59. package/dist/pgsql-B_KZ0mGX.js +851 -0
  60. package/dist/php-A19QHzQV.js +500 -0
  61. package/dist/pla-COeKY1hA.js +137 -0
  62. package/dist/postiats-BfMp7zJL.js +907 -0
  63. package/dist/powerquery-B0TS_5Gb.js +890 -0
  64. package/dist/powershell-5jSbKdYB.js +239 -0
  65. package/dist/protobuf-YgnOutn3.js +420 -0
  66. package/dist/pug-DLej6kn5.js +402 -0
  67. package/dist/python-CmHenqwx.js +301 -0
  68. package/dist/qsharp-AhjqtSHB.js +301 -0
  69. package/dist/r-D-F4P80w.js +243 -0
  70. package/dist/razor-D0q5qc6y.js +550 -0
  71. package/dist/redis-hwwIlMnV.js +302 -0
  72. package/dist/redshift-Bcm_kjEG.js +809 -0
  73. package/dist/restructuredtext-CrdzfPP-.js +174 -0
  74. package/dist/ruby-QC2UNm1s.js +511 -0
  75. package/dist/rust-5aqiQ3L5.js +343 -0
  76. package/dist/sb-B1g1ZfLJ.js +115 -0
  77. package/dist/scala-C2U4R5Gb.js +370 -0
  78. package/dist/scheme-DeFRbslr.js +108 -0
  79. package/dist/scss-RSTCiCmI.js +262 -0
  80. package/dist/shell-BpF-iNu8.js +221 -0
  81. package/dist/solidity-C00r2mbQ.js +1367 -0
  82. package/dist/sophia-DVW5ZKDB.js +199 -0
  83. package/dist/sparql-B0nmpxeX.js +201 -0
  84. package/dist/sql-CJwKPERn.js +853 -0
  85. package/dist/st-DPlKG5DT.js +416 -0
  86. package/dist/swift-YZBAbVHV.js +309 -0
  87. package/dist/systemverilog-CDQSaH63.js +576 -0
  88. package/dist/tcl-Dlu8sp6Q.js +232 -0
  89. package/dist/tsMode-DiTuI5n4.js +933 -0
  90. package/dist/twig-C5TO7-kw.js +392 -0
  91. package/dist/typescript-ozsvnTYx.js +343 -0
  92. package/dist/typespec-BsmjDEu7.js +117 -0
  93. package/dist/vb-ChHvIDrk.js +372 -0
  94. package/dist/wgsl-XEp1ju8c.js +439 -0
  95. package/dist/xml-oViOWYBd.js +95 -0
  96. package/dist/yaml-BdXw910y.js +206 -0
  97. package/package.json +51 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Entropy Data GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # Data Contract Editor
2
+
3
+ A web-based editor for creating and managing data contracts using the [Open Data Contract Standard](https://bitol-io.github.io/open-data-contract-standard/latest/) (ODCS).
4
+
5
+ ![Screenshot](docs/screenshot.png)
6
+
7
+ ## Features
8
+
9
+ - **Editing Modes**:
10
+ - **Visual Editor**: Define data models and relationships using a visual interface
11
+ - **Form Editor**: Get guided input from a simple form interface
12
+ - **YAML Editor**: Edit data contracts in YAML format
13
+ - **Real-time Preview**: Live preview of data contracts with syntax validation
14
+ - **Validation**: Get instant feedback on your data contracts
15
+ - **Data Contract CLI Integration**: Run tests against your contracts using the Data Contract CLI API Server.
16
+
17
+
18
+ ## Usage
19
+
20
+ ### Web Editor
21
+
22
+ https://editor.datacontract.com
23
+
24
+
25
+ ### Standalone Application
26
+
27
+ Coming soon!
28
+
29
+ ```
30
+ npx datacontract-editor datacontract.yaml
31
+ ```
32
+
33
+
34
+ ### Data Contract CLI
35
+
36
+ Coming soon!
37
+
38
+ You can start the editor from the Data Contract CLI:
39
+
40
+ ```
41
+ datacontract editor datacontract.yaml
42
+ ```
43
+
44
+
45
+
46
+ ### Entropy Data
47
+
48
+ [Entropy Data](https://entropy-data.com) is our commercial offering for managing data products with data contracts.
49
+
50
+
51
+
52
+ ## License
53
+
54
+ This project is maintained by [Entropy Data](https://entropy-data.com) and licensed under the [MIT LICENSE](LICENSE).
@@ -0,0 +1,373 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createServer } from 'http';
4
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
5
+ import { join, extname, resolve, basename } from 'path';
6
+ import { fileURLToPath } from 'url';
7
+ import { createRequire } from 'module';
8
+
9
+ const __dirname = fileURLToPath(new URL('.', import.meta.url));
10
+ const distDir = join(__dirname, '..', 'dist');
11
+
12
+ // Read package.json for version
13
+ const require = createRequire(import.meta.url);
14
+ const pkg = require('../package.json');
15
+
16
+ // Parse command line arguments
17
+ function parseArgs() {
18
+ const args = process.argv.slice(2);
19
+ let port = null;
20
+ let targetFile = null;
21
+
22
+ for (let i = 0; i < args.length; i++) {
23
+ if (args[i] === '-p' || args[i] === '--port') {
24
+ port = parseInt(args[i + 1], 10);
25
+ i++; // Skip next arg
26
+ } else if (args[i] === '-h' || args[i] === '--help') {
27
+ console.log(`
28
+ datacontract-editor v${pkg.version}
29
+
30
+ Usage: datacontract-editor [options] [file]
31
+
32
+ Options:
33
+ -p, --port <port> Port to run the server on (default: auto-detect from 9090)
34
+ -h, --help Show this help message
35
+
36
+ Examples:
37
+ datacontract-editor # Start editor without a file
38
+ datacontract-editor datacontract.yaml # Edit a specific file
39
+ datacontract-editor -p 3000 my.yaml # Use a specific port
40
+ `);
41
+ process.exit(0);
42
+ } else if (!args[i].startsWith('-')) {
43
+ targetFile = args[i];
44
+ }
45
+ }
46
+
47
+ return { port, targetFile };
48
+ }
49
+
50
+ const { port: requestedPort, targetFile } = parseArgs();
51
+
52
+ // Resolve target file path if provided
53
+ let targetFilePath = null;
54
+ let targetFileName = null;
55
+ let isNewFile = false;
56
+ if (targetFile) {
57
+ targetFilePath = resolve(process.cwd(), targetFile);
58
+ targetFileName = basename(targetFile);
59
+
60
+ // Validate it's a YAML file
61
+ if (!targetFile.endsWith('.yaml') && !targetFile.endsWith('.yml')) {
62
+ console.error('Error: File must be a YAML file (.yaml or .yml)');
63
+ process.exit(1);
64
+ }
65
+
66
+ // Create empty file if it doesn't exist
67
+ if (!existsSync(targetFilePath)) {
68
+ writeFileSync(targetFilePath, '', 'utf-8');
69
+ isNewFile = true;
70
+ }
71
+ }
72
+
73
+ // MIME types for static files
74
+ const mimeTypes = {
75
+ '.html': 'text/html',
76
+ '.js': 'application/javascript',
77
+ '.mjs': 'application/javascript',
78
+ '.css': 'text/css',
79
+ '.json': 'application/json',
80
+ '.svg': 'image/svg+xml',
81
+ '.png': 'image/png',
82
+ '.jpg': 'image/jpeg',
83
+ '.jpeg': 'image/jpeg',
84
+ '.gif': 'image/gif',
85
+ '.ico': 'image/x-icon',
86
+ '.woff': 'font/woff',
87
+ '.woff2': 'font/woff2',
88
+ '.ttf': 'font/ttf',
89
+ '.yaml': 'text/yaml',
90
+ '.yml': 'text/yaml'
91
+ };
92
+
93
+ // Check if dist directory exists
94
+ if (!existsSync(distDir)) {
95
+ console.error('Error: dist/ directory not found.');
96
+ console.error('Please run "npm run build" first.');
97
+ process.exit(1);
98
+ }
99
+
100
+ // Check if index.html exists
101
+ const indexPath = join(distDir, 'index.html');
102
+ if (!existsSync(indexPath)) {
103
+ console.error('Error: dist/index.html not found.');
104
+ console.error('Please run "npm run build" first.');
105
+ process.exit(1);
106
+ }
107
+
108
+ // Find available port
109
+ async function findPort(start = 9090) {
110
+ const net = await import('net');
111
+
112
+ return new Promise((resolve) => {
113
+ const server = net.createServer();
114
+ server.listen(start, () => {
115
+ const port = server.address().port;
116
+ server.close(() => resolve(port));
117
+ });
118
+ server.on('error', () => {
119
+ resolve(findPort(start + 1));
120
+ });
121
+ });
122
+ }
123
+
124
+ // Helper to send JSON response
125
+ function sendJson(res, statusCode, data) {
126
+ res.writeHead(statusCode, {
127
+ 'Content-Type': 'application/json',
128
+ 'Access-Control-Allow-Origin': '*'
129
+ });
130
+ res.end(JSON.stringify(data));
131
+ }
132
+
133
+ // Helper to read request body
134
+ function readBody(req) {
135
+ return new Promise((resolve, reject) => {
136
+ let body = '';
137
+ req.on('data', chunk => body += chunk);
138
+ req.on('end', () => resolve(body));
139
+ req.on('error', reject);
140
+ });
141
+ }
142
+
143
+ // Generate CLI-specific index.html with injected config
144
+ function generateCliIndexHtml() {
145
+ const baseHtml = readFileSync(indexPath, 'utf-8');
146
+
147
+ // Replace the init call with CLI-specific configuration
148
+ const cliInitScript = `
149
+ import { init } from './datacontract-editor.es.js';
150
+
151
+ // CLI mode: Auto-load file from local server
152
+ async function initCli() {
153
+ try {
154
+ // Fetch the file content from CLI server
155
+ const response = await fetch('/api/files/${encodeURIComponent(targetFileName)}');
156
+ if (!response.ok) {
157
+ throw new Error('Failed to load file: ' + response.statusText);
158
+ }
159
+ const yaml = await response.text();
160
+
161
+ init({
162
+ container: '#root',
163
+ mode: 'CLI',
164
+ yaml: yaml,
165
+ enablePersistence: false,
166
+ initialView: 'form',
167
+ onSave: async (yamlContent) => {
168
+ const saveResponse = await fetch('/api/files/${encodeURIComponent(targetFileName)}', {
169
+ method: 'PUT',
170
+ headers: { 'Content-Type': 'text/yaml' },
171
+ body: yamlContent
172
+ });
173
+ if (!saveResponse.ok) {
174
+ throw new Error('Failed to save file');
175
+ }
176
+ console.log('File saved successfully');
177
+ }
178
+ });
179
+
180
+ console.log('CLI mode: Loaded ${targetFileName}');
181
+ } catch (error) {
182
+ console.error('Failed to initialize CLI mode:', error);
183
+ alert('Failed to load file: ' + error.message);
184
+ }
185
+ }
186
+
187
+ initCli();
188
+ `;
189
+
190
+ // Replace the existing init script
191
+ const modifiedHtml = baseHtml.replace(
192
+ /<script type="module">[\s\S]*?<\/script>/,
193
+ `<script type="module">${cliInitScript}</script>`
194
+ );
195
+
196
+ return modifiedHtml;
197
+ }
198
+
199
+ // Create HTTP server
200
+ const server = createServer(async (req, res) => {
201
+ let url = req.url.split('?')[0]; // Remove query string
202
+ const method = req.method;
203
+
204
+ // Handle CORS preflight
205
+ if (method === 'OPTIONS') {
206
+ res.writeHead(204, {
207
+ 'Access-Control-Allow-Origin': '*',
208
+ 'Access-Control-Allow-Methods': 'GET, PUT, POST, OPTIONS',
209
+ 'Access-Control-Allow-Headers': 'Content-Type'
210
+ });
211
+ res.end();
212
+ return;
213
+ }
214
+
215
+ // Serve modified index.html for CLI mode
216
+ if (targetFilePath && (url === '/' || url === '/index.html')) {
217
+ try {
218
+ const cliHtml = generateCliIndexHtml();
219
+ res.writeHead(200, { 'Content-Type': 'text/html' });
220
+ res.end(cliHtml);
221
+ return;
222
+ } catch (err) {
223
+ console.error('Failed to generate CLI index.html:', err);
224
+ res.writeHead(500);
225
+ res.end('Server Error');
226
+ return;
227
+ }
228
+ }
229
+
230
+ // API Routes (only available in CLI mode with a target file)
231
+ if (url.startsWith('/api/')) {
232
+ // Health check - always available
233
+ if (url === '/api/health' && method === 'GET') {
234
+ sendJson(res, 200, { status: 'ok', version: pkg.version });
235
+ return;
236
+ }
237
+
238
+ // Config endpoint - returns CLI mode info
239
+ if (url === '/api/config' && method === 'GET') {
240
+ if (targetFilePath) {
241
+ sendJson(res, 200, {
242
+ mode: 'CLI',
243
+ filename: targetFileName,
244
+ filepath: targetFilePath
245
+ });
246
+ } else {
247
+ // No file specified - not in CLI mode
248
+ sendJson(res, 200, { mode: null });
249
+ }
250
+ return;
251
+ }
252
+
253
+ // File operations - only available when a target file is specified
254
+ if (url.startsWith('/api/files/')) {
255
+ const requestedFile = decodeURIComponent(url.replace('/api/files/', ''));
256
+
257
+ // Security check: only allow access to the specified target file
258
+ if (!targetFilePath || requestedFile !== targetFileName) {
259
+ sendJson(res, 403, { error: 'Access denied. Only the specified target file can be accessed.' });
260
+ return;
261
+ }
262
+
263
+ // GET - Read file
264
+ if (method === 'GET') {
265
+ try {
266
+ const content = readFileSync(targetFilePath, 'utf-8');
267
+ res.writeHead(200, {
268
+ 'Content-Type': 'text/yaml',
269
+ 'Access-Control-Allow-Origin': '*'
270
+ });
271
+ res.end(content);
272
+ } catch (err) {
273
+ sendJson(res, 500, { error: 'Failed to read file', details: err.message });
274
+ }
275
+ return;
276
+ }
277
+
278
+ // PUT - Write file
279
+ if (method === 'PUT') {
280
+ try {
281
+ const content = await readBody(req);
282
+ writeFileSync(targetFilePath, content, 'utf-8');
283
+ sendJson(res, 200, { success: true, filename: targetFileName });
284
+ } catch (err) {
285
+ sendJson(res, 500, { error: 'Failed to write file', details: err.message });
286
+ }
287
+ return;
288
+ }
289
+ }
290
+
291
+ // Unknown API route
292
+ sendJson(res, 404, { error: 'Not found' });
293
+ return;
294
+ }
295
+
296
+ // Default to index.html for root
297
+ if (url === '/') {
298
+ url = '/index.html';
299
+ }
300
+
301
+ let filePath = join(distDir, url);
302
+ const ext = extname(filePath).toLowerCase();
303
+
304
+ // If no extension, serve index.html (SPA routing)
305
+ if (!ext && !existsSync(filePath)) {
306
+ filePath = indexPath;
307
+ }
308
+
309
+ // Try to serve the file
310
+ if (existsSync(filePath)) {
311
+ try {
312
+ const content = readFileSync(filePath);
313
+ const contentType = mimeTypes[extname(filePath).toLowerCase()] || 'application/octet-stream';
314
+ res.writeHead(200, { 'Content-Type': contentType });
315
+ res.end(content);
316
+ } catch (err) {
317
+ res.writeHead(500);
318
+ res.end('Server Error');
319
+ }
320
+ } else {
321
+ // For SPA, serve index.html for unknown routes
322
+ try {
323
+ const content = readFileSync(indexPath);
324
+ res.writeHead(200, { 'Content-Type': 'text/html' });
325
+ res.end(content);
326
+ } catch (err) {
327
+ res.writeHead(404);
328
+ res.end('Not Found');
329
+ }
330
+ }
331
+ });
332
+
333
+ // Start server
334
+ async function start() {
335
+ const port = requestedPort || await findPort(9090);
336
+ const url = `http://localhost:${port}`;
337
+
338
+ server.listen(port, async () => {
339
+ console.log(`
340
+ datacontract-editor v${pkg.version}
341
+
342
+ Server running at ${url}${targetFileName ? `
343
+ ${isNewFile ? 'Creating' : 'Editing'}: ${targetFileName}` : ''}
344
+
345
+ Press Ctrl+C to stop
346
+ `);
347
+
348
+ // Open browser
349
+ try {
350
+ const open = (await import('open')).default;
351
+ await open(url);
352
+ } catch (err) {
353
+ console.log(` Could not open browser automatically.`);
354
+ console.log(` Please open ${url} manually.\n`);
355
+ }
356
+ });
357
+ }
358
+
359
+ // Handle graceful shutdown
360
+ process.on('SIGINT', () => {
361
+ console.log('\n Shutting down...\n');
362
+ server.close(() => {
363
+ process.exit(0);
364
+ });
365
+ });
366
+
367
+ process.on('SIGTERM', () => {
368
+ server.close(() => {
369
+ process.exit(0);
370
+ });
371
+ });
372
+
373
+ start();