datacontract-editor 0.1.11 → 0.1.12
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 +1 -3
- package/bin/datacontract-editor.js +73 -29
- package/dist/assets/editor.worker-4.7.0.js +30 -9034
- package/dist/assets/yaml.worker-4.7.0.js +424 -33283
- package/dist/datacontract-editor.css +2 -1
- package/dist/datacontract-editor.es.js +82723 -81113
- package/dist/monaco-4.7.0.js +114767 -125937
- package/dist/rolldown-runtime-DY7j01NX.js +30 -0
- package/package.json +12 -12
- package/dist/abap-Bs8kvjsq.js +0 -1398
- package/dist/apex-Dspwrl5m.js +0 -326
- package/dist/azcli-k1BiOau1.js +0 -68
- package/dist/bat-BIP72KWc.js +0 -100
- package/dist/bicep-VhQeYhS3.js +0 -102
- package/dist/cameligo-DnuAXtHg.js +0 -174
- package/dist/clojure-BAuDPsal.js +0 -761
- package/dist/coffee-BV06qSnj.js +0 -232
- package/dist/cpp-Cz8Jeyrf.js +0 -389
- package/dist/csharp-F6hhlXFb.js +0 -326
- package/dist/csp-KzlEHCyI.js +0 -53
- package/dist/css-e4FKntZ3.js +0 -187
- package/dist/cssMode-B1QSDB35.js +0 -1618
- package/dist/cypher-CpvDGKRr.js +0 -263
- package/dist/dart-DdHeFWR0.js +0 -281
- package/dist/dockerfile-aoNXDcio.js +0 -130
- package/dist/ecl-C4Q0DYuA.js +0 -456
- package/dist/elixir-C9X42b0T.js +0 -569
- package/dist/flow9-Bff42pfB.js +0 -142
- package/dist/freemarker2-DeO1_SUs.js +0 -989
- package/dist/fsharp-C6vPAsHG.js +0 -217
- package/dist/go-DEr8zx9f.js +0 -218
- package/dist/graphql-BiNBBRFh.js +0 -151
- package/dist/handlebars-BWm3cK1O.js +0 -419
- package/dist/hcl-2T9lKuFg.js +0 -183
- package/dist/html-C8E5HqOh.js +0 -308
- package/dist/htmlMode-azLSEeE2.js +0 -1628
- package/dist/ini-CZGtssYM.js +0 -71
- package/dist/java-B260NeH3.js +0 -232
- package/dist/javascript-CkO57ldU.js +0 -70
- package/dist/jsonMode-D6ZzTi4q.js +0 -2051
- package/dist/julia-kuNLM9c5.js +0 -511
- package/dist/kotlin-Bz6kAefA.js +0 -253
- package/dist/less-AZUtS89I.js +0 -162
- package/dist/lexon-BcmgnyRb.js +0 -157
- package/dist/liquid-BBvOrvTW.js +0 -240
- package/dist/lua-Dz1RbATg.js +0 -162
- package/dist/m3-CIMooLFn.js +0 -210
- package/dist/markdown-C1BsCK9P.js +0 -229
- package/dist/mdx-T-759RLL.js +0 -165
- package/dist/mips-BovsnfLQ.js +0 -198
- package/dist/msdax-CiRB2ZB-.js +0 -375
- package/dist/mysql-BwdpPE19.js +0 -878
- package/dist/objective-c-D4nfHfso.js +0 -183
- package/dist/pascal--j5-PYtz.js +0 -251
- package/dist/pascaligo-C7TcJxNL.js +0 -164
- package/dist/perl-BzEWqUb5.js +0 -626
- package/dist/pgsql-B_KZ0mGX.js +0 -851
- package/dist/php-A19QHzQV.js +0 -500
- package/dist/pla-COeKY1hA.js +0 -137
- package/dist/postiats-BfMp7zJL.js +0 -907
- package/dist/powerquery-B0TS_5Gb.js +0 -890
- package/dist/powershell-5jSbKdYB.js +0 -239
- package/dist/protobuf-YgnOutn3.js +0 -420
- package/dist/pug-DLej6kn5.js +0 -402
- package/dist/python-BFl6pUvy.js +0 -301
- package/dist/qsharp-AhjqtSHB.js +0 -301
- package/dist/r-D-F4P80w.js +0 -243
- package/dist/razor-BisT8Eyw.js +0 -550
- package/dist/redis-hwwIlMnV.js +0 -302
- package/dist/redshift-Bcm_kjEG.js +0 -809
- package/dist/restructuredtext-CrdzfPP-.js +0 -174
- package/dist/ruby-QC2UNm1s.js +0 -511
- package/dist/rust-5aqiQ3L5.js +0 -343
- package/dist/sb-B1g1ZfLJ.js +0 -115
- package/dist/scala-C2U4R5Gb.js +0 -370
- package/dist/scheme-DeFRbslr.js +0 -108
- package/dist/scss-RSTCiCmI.js +0 -262
- package/dist/shell-BpF-iNu8.js +0 -221
- package/dist/solidity-C00r2mbQ.js +0 -1367
- package/dist/sophia-DVW5ZKDB.js +0 -199
- package/dist/sparql-B0nmpxeX.js +0 -201
- package/dist/sql-CJwKPERn.js +0 -853
- package/dist/st-DPlKG5DT.js +0 -416
- package/dist/swift-YZBAbVHV.js +0 -309
- package/dist/systemverilog-CDQSaH63.js +0 -576
- package/dist/tcl-Dlu8sp6Q.js +0 -232
- package/dist/tsMode-CXX_kvIQ.js +0 -933
- package/dist/twig-C5TO7-kw.js +0 -392
- package/dist/typescript-ST9WAw3B.js +0 -343
- package/dist/typespec-BsmjDEu7.js +0 -117
- package/dist/vb-ChHvIDrk.js +0 -372
- package/dist/wgsl-XEp1ju8c.js +0 -439
- package/dist/xml-O0qUeKUc.js +0 -95
- package/dist/yaml-CLIJKwO6.js +0 -206
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { createServer } from 'http';
|
|
4
|
-
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, readdirSync } from 'fs';
|
|
5
5
|
import { join, extname, resolve, basename } from 'path';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import { createRequire } from 'module';
|
|
@@ -111,7 +111,7 @@ async function findPort(start = 9090) {
|
|
|
111
111
|
|
|
112
112
|
return new Promise((resolve) => {
|
|
113
113
|
const server = net.createServer();
|
|
114
|
-
server.listen(start, () => {
|
|
114
|
+
server.listen(start, '127.0.0.1', () => {
|
|
115
115
|
const port = server.address().port;
|
|
116
116
|
server.close(() => resolve(port));
|
|
117
117
|
});
|
|
@@ -123,13 +123,52 @@ async function findPort(start = 9090) {
|
|
|
123
123
|
|
|
124
124
|
// Helper to send JSON response
|
|
125
125
|
function sendJson(res, statusCode, data) {
|
|
126
|
-
res.writeHead(statusCode, {
|
|
127
|
-
'Content-Type': 'application/json',
|
|
128
|
-
'Access-Control-Allow-Origin': '*'
|
|
129
|
-
});
|
|
126
|
+
res.writeHead(statusCode, { 'Content-Type': 'application/json' });
|
|
130
127
|
res.end(JSON.stringify(data));
|
|
131
128
|
}
|
|
132
129
|
|
|
130
|
+
// Index every file under dist/ once at startup, keyed by the URL path it is
|
|
131
|
+
// served at. Requests then only ever look up a key in this map: the paths handed
|
|
132
|
+
// to the filesystem come from readdirSync, never from the request, so a request
|
|
133
|
+
// path cannot address a file outside dist/ no matter how it is encoded.
|
|
134
|
+
function indexDistFiles(dir, urlPrefix = '') {
|
|
135
|
+
const files = new Map();
|
|
136
|
+
|
|
137
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
138
|
+
const urlPath = `${urlPrefix}/${entry.name}`;
|
|
139
|
+
if (entry.isDirectory()) {
|
|
140
|
+
for (const [key, value] of indexDistFiles(join(dir, entry.name), urlPath)) {
|
|
141
|
+
files.set(key, value);
|
|
142
|
+
}
|
|
143
|
+
} else if (entry.isFile()) {
|
|
144
|
+
files.set(urlPath, join(dir, entry.name));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return files;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// dist/ is a build artifact and does not change while the server runs.
|
|
152
|
+
const distFiles = indexDistFiles(distDir);
|
|
153
|
+
|
|
154
|
+
// Turn a request path into a lookup key. Returns null when the percent-encoding
|
|
155
|
+
// is malformed, which simply means no file will match.
|
|
156
|
+
function toLookupKey(url) {
|
|
157
|
+
try {
|
|
158
|
+
return decodeURIComponent(url);
|
|
159
|
+
} catch {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// The server is only ever meant to be reached by a browser on this machine.
|
|
165
|
+
// Requiring a loopback Host blocks DNS rebinding, where a page on an attacker
|
|
166
|
+
// domain resolves that domain to 127.0.0.1 and then talks to us same-origin.
|
|
167
|
+
function isLocalRequest(req) {
|
|
168
|
+
const host = (req.headers.host || '').replace(/:\d+$/, '').replace(/^\[|\]$/g, '');
|
|
169
|
+
return host === 'localhost' || host === '127.0.0.1' || host === '::1';
|
|
170
|
+
}
|
|
171
|
+
|
|
133
172
|
// Helper to read request body
|
|
134
173
|
function readBody(req) {
|
|
135
174
|
return new Promise((resolve, reject) => {
|
|
@@ -144,6 +183,13 @@ function readBody(req) {
|
|
|
144
183
|
function generateCliIndexHtml() {
|
|
145
184
|
const baseHtml = readFileSync(indexPath, 'utf-8');
|
|
146
185
|
|
|
186
|
+
// The filename comes from argv and can contain quotes or '</script>', so every
|
|
187
|
+
// injection point below goes through JSON.stringify rather than raw
|
|
188
|
+
// interpolation. encodeURIComponent alone is not enough: it leaves apostrophes
|
|
189
|
+
// untouched, which is enough to break out of a single-quoted string literal.
|
|
190
|
+
const fileUrl = JSON.stringify('/api/files/' + encodeURIComponent(targetFileName));
|
|
191
|
+
const fileNameLiteral = JSON.stringify(targetFileName);
|
|
192
|
+
|
|
147
193
|
// Replace the init call with CLI-specific configuration
|
|
148
194
|
const cliInitScript = `
|
|
149
195
|
import { init } from './datacontract-editor.es.js';
|
|
@@ -152,7 +198,7 @@ function generateCliIndexHtml() {
|
|
|
152
198
|
async function initCli() {
|
|
153
199
|
try {
|
|
154
200
|
// Fetch the file content from CLI server
|
|
155
|
-
const response = await fetch(
|
|
201
|
+
const response = await fetch(${fileUrl});
|
|
156
202
|
if (!response.ok) {
|
|
157
203
|
throw new Error('Failed to load file: ' + response.statusText);
|
|
158
204
|
}
|
|
@@ -165,7 +211,7 @@ function generateCliIndexHtml() {
|
|
|
165
211
|
persistence: 'none',
|
|
166
212
|
initialView: 'form',
|
|
167
213
|
onSave: async (yamlContent) => {
|
|
168
|
-
const saveResponse = await fetch(
|
|
214
|
+
const saveResponse = await fetch(${fileUrl}, {
|
|
169
215
|
method: 'PUT',
|
|
170
216
|
headers: { 'Content-Type': 'text/yaml' },
|
|
171
217
|
body: yamlContent
|
|
@@ -177,7 +223,7 @@ function generateCliIndexHtml() {
|
|
|
177
223
|
}
|
|
178
224
|
});
|
|
179
225
|
|
|
180
|
-
console.log('CLI mode: Loaded ${
|
|
226
|
+
console.log('CLI mode: Loaded ' + ${fileNameLiteral});
|
|
181
227
|
} catch (error) {
|
|
182
228
|
console.error('Failed to initialize CLI mode:', error);
|
|
183
229
|
alert('Failed to load file: ' + error.message);
|
|
@@ -201,13 +247,17 @@ const server = createServer(async (req, res) => {
|
|
|
201
247
|
let url = req.url.split('?')[0]; // Remove query string
|
|
202
248
|
const method = req.method;
|
|
203
249
|
|
|
204
|
-
|
|
250
|
+
if (!isLocalRequest(req)) {
|
|
251
|
+
res.writeHead(403);
|
|
252
|
+
res.end('Forbidden');
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// The editor is served from this same origin, so it never needs CORS. Not
|
|
257
|
+
// answering preflights is what stops another site from PUTing over the file
|
|
258
|
+
// being edited.
|
|
205
259
|
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
|
-
});
|
|
260
|
+
res.writeHead(204);
|
|
211
261
|
res.end();
|
|
212
262
|
return;
|
|
213
263
|
}
|
|
@@ -264,10 +314,7 @@ const server = createServer(async (req, res) => {
|
|
|
264
314
|
if (method === 'GET') {
|
|
265
315
|
try {
|
|
266
316
|
const content = readFileSync(targetFilePath, 'utf-8');
|
|
267
|
-
res.writeHead(200, {
|
|
268
|
-
'Content-Type': 'text/yaml',
|
|
269
|
-
'Access-Control-Allow-Origin': '*'
|
|
270
|
-
});
|
|
317
|
+
res.writeHead(200, { 'Content-Type': 'text/yaml' });
|
|
271
318
|
res.end(content);
|
|
272
319
|
} catch (err) {
|
|
273
320
|
sendJson(res, 500, { error: 'Failed to read file', details: err.message });
|
|
@@ -298,16 +345,11 @@ const server = createServer(async (req, res) => {
|
|
|
298
345
|
url = '/index.html';
|
|
299
346
|
}
|
|
300
347
|
|
|
301
|
-
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
// If no extension, serve index.html (SPA routing)
|
|
305
|
-
if (!ext && !existsSync(filePath)) {
|
|
306
|
-
filePath = indexPath;
|
|
307
|
-
}
|
|
348
|
+
// Serve a known file from dist/, or fall back to index.html for SPA routes
|
|
349
|
+
const lookupKey = toLookupKey(url);
|
|
350
|
+
const filePath = lookupKey === null ? undefined : distFiles.get(lookupKey);
|
|
308
351
|
|
|
309
|
-
|
|
310
|
-
if (existsSync(filePath)) {
|
|
352
|
+
if (filePath) {
|
|
311
353
|
try {
|
|
312
354
|
const content = readFileSync(filePath);
|
|
313
355
|
const contentType = mimeTypes[extname(filePath).toLowerCase()] || 'application/octet-stream';
|
|
@@ -335,7 +377,9 @@ async function start() {
|
|
|
335
377
|
const port = requestedPort || await findPort(9090);
|
|
336
378
|
const url = `http://localhost:${port}`;
|
|
337
379
|
|
|
338
|
-
server
|
|
380
|
+
// Bind to loopback only: this server exposes local files and has no auth,
|
|
381
|
+
// so it must not be reachable from the network.
|
|
382
|
+
server.listen(port, '127.0.0.1', async () => {
|
|
339
383
|
console.log(`
|
|
340
384
|
datacontract-editor v${pkg.version}
|
|
341
385
|
|