@velarscript/cli 0.12.1 → 0.14.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 +5 -13
- package/dist/browser-process-owner.d.ts +23 -0
- package/dist/browser-process-owner.d.ts.map +1 -1
- package/dist/browser-process-owner.js +46 -2
- package/dist/browser-process-owner.js.map +1 -1
- package/dist/browser-test-runner.d.ts.map +1 -1
- package/dist/browser-test-runner.js +110 -14
- package/dist/browser-test-runner.js.map +1 -1
- package/dist/cli.js +186 -106
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +28 -4
- package/dist/config.js.map +1 -1
- package/dist/dev-server.d.ts +18 -0
- package/dist/dev-server.d.ts.map +1 -1
- package/dist/dev-server.js +168 -8
- package/dist/dev-server.js.map +1 -1
- package/dist/extension-metadata.d.ts +6 -0
- package/dist/extension-metadata.d.ts.map +1 -1
- package/dist/extension-metadata.js +71 -4
- package/dist/extension-metadata.js.map +1 -1
- package/dist/format-guard.d.ts +20 -0
- package/dist/format-guard.d.ts.map +1 -0
- package/dist/format-guard.js +13 -0
- package/dist/format-guard.js.map +1 -0
- package/dist/framework-host.d.ts.map +1 -1
- package/dist/framework-host.js +15 -2
- package/dist/framework-host.js.map +1 -1
- package/dist/language-server-bundle-entry.js +0 -2
- package/dist/language-server-bundle-entry.js.map +1 -1
- package/dist/language-server-tool.d.ts.map +1 -1
- package/dist/language-server-tool.js +0 -2
- package/dist/language-server-tool.js.map +1 -1
- package/dist/language-server.d.ts +1 -1
- package/dist/language-server.d.ts.map +1 -1
- package/dist/language-server.js +87 -293
- package/dist/language-server.js.map +1 -1
- package/dist/local-request-guard.d.ts +18 -0
- package/dist/local-request-guard.d.ts.map +1 -0
- package/dist/local-request-guard.js +75 -0
- package/dist/local-request-guard.js.map +1 -0
- package/dist/mechanical-fixer.d.ts.map +1 -1
- package/dist/mechanical-fixer.js +116 -2
- package/dist/mechanical-fixer.js.map +1 -1
- package/dist/module-assets.d.ts +23 -0
- package/dist/module-assets.d.ts.map +1 -1
- package/dist/module-assets.js +216 -14
- package/dist/module-assets.js.map +1 -1
- package/dist/node-application.d.ts +8 -7
- package/dist/node-application.d.ts.map +1 -1
- package/dist/node-application.js +63 -32
- package/dist/node-application.js.map +1 -1
- package/dist/node-runtime-dependencies.d.ts +3 -0
- package/dist/node-runtime-dependencies.d.ts.map +1 -0
- package/dist/node-runtime-dependencies.js +32 -0
- package/dist/node-runtime-dependencies.js.map +1 -0
- package/dist/npm.d.ts +29 -0
- package/dist/npm.d.ts.map +1 -1
- package/dist/npm.js +39 -2
- package/dist/npm.js.map +1 -1
- package/dist/official-language-server-extensions.d.ts.map +1 -1
- package/dist/official-language-server-extensions.js +13 -0
- package/dist/official-language-server-extensions.js.map +1 -1
- package/dist/ownership-graph.d.ts.map +1 -1
- package/dist/ownership-graph.js +155 -38
- package/dist/ownership-graph.js.map +1 -1
- package/dist/package-manager.d.ts.map +1 -1
- package/dist/package-manager.js +4 -1
- package/dist/package-manager.js.map +1 -1
- package/dist/preview-server.d.ts.map +1 -1
- package/dist/preview-server.js +12 -4
- package/dist/preview-server.js.map +1 -1
- package/dist/production-build.d.ts.map +1 -1
- package/dist/production-build.js +24 -6
- package/dist/production-build.js.map +1 -1
- package/dist/production-verifier.js +3 -0
- package/dist/production-verifier.js.map +1 -1
- package/dist/project-check.d.ts +14 -1
- package/dist/project-check.d.ts.map +1 -1
- package/dist/project-check.js +17 -4
- package/dist/project-check.js.map +1 -1
- package/dist/project-format.d.ts +9 -0
- package/dist/project-format.d.ts.map +1 -1
- package/dist/project-format.js +13 -0
- package/dist/project-format.js.map +1 -1
- package/dist/project-semantic.d.ts.map +1 -1
- package/dist/project-semantic.js +15 -4
- package/dist/project-semantic.js.map +1 -1
- package/dist/project.d.ts +25 -0
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +341 -42
- package/dist/project.js.map +1 -1
- package/dist/reproduction.d.ts +18 -0
- package/dist/reproduction.d.ts.map +1 -1
- package/dist/reproduction.js +47 -6
- package/dist/reproduction.js.map +1 -1
- package/dist/stable-order.d.ts +17 -0
- package/dist/stable-order.d.ts.map +1 -0
- package/dist/stable-order.js +19 -0
- package/dist/stable-order.js.map +1 -0
- package/dist/standard-modules.d.ts +2 -2
- package/dist/standard-modules.d.ts.map +1 -1
- package/dist/standard-modules.js +2 -2
- package/dist/standard-modules.js.map +1 -1
- package/dist/static-deployment.d.ts.map +1 -1
- package/dist/static-deployment.js +27 -0
- package/dist/static-deployment.js.map +1 -1
- package/dist/test-runner.d.ts.map +1 -1
- package/dist/test-runner.js +196 -100
- package/dist/test-runner.js.map +1 -1
- package/dist/test-worker.d.ts +64 -0
- package/dist/test-worker.d.ts.map +1 -0
- package/dist/test-worker.js +161 -0
- package/dist/test-worker.js.map +1 -0
- package/dist/typescript-declarations.d.ts +1 -1
- package/dist/typescript-declarations.d.ts.map +1 -1
- package/dist/typescript-declarations.js +233 -21
- package/dist/typescript-declarations.js.map +1 -1
- package/dist/unowned-errors.d.ts +15 -2
- package/dist/unowned-errors.d.ts.map +1 -1
- package/dist/unowned-errors.js +13 -2
- package/dist/unowned-errors.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/workspace-index.d.ts.map +1 -1
- package/dist/workspace-index.js +42 -17
- package/dist/workspace-index.js.map +1 -1
- package/package.json +8 -8
- package/skill/ai-skill-node.md +91 -22
- package/skill/ai-skill-server.md +173 -0
- package/skill/ai-skill-web.md +51 -1
- package/skill/ai-skill.md +36 -13
- package/dist/official-script-language-service.d.ts +0 -2
- package/dist/official-script-language-service.d.ts.map +0 -1
- package/dist/official-script-language-service.js +0 -10
- package/dist/official-script-language-service.js.map +0 -1
- package/dist/official-tool-assets.d.ts +0 -3
- package/dist/official-tool-assets.d.ts.map +0 -1
- package/dist/official-tool-assets.js +0 -60
- package/dist/official-tool-assets.js.map +0 -1
- package/dist/script-language-service.d.ts +0 -82
- package/dist/script-language-service.d.ts.map +0 -1
- package/dist/script-language-service.js +0 -18
- package/dist/script-language-service.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -7,12 +7,10 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
7
7
|
}
|
|
8
8
|
return path;
|
|
9
9
|
};
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { tmpdir } from "node:os";
|
|
10
|
+
import { copyFile, lstat, mkdir, mkdtemp, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
|
|
11
|
+
import { basename, dirname, extname, isAbsolute, join, parse as parsePath, relative, resolve } from "node:path";
|
|
12
|
+
import { homedir, tmpdir } from "node:os";
|
|
14
13
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
15
|
-
import { formatSource } from "@velarscript/compiler";
|
|
16
14
|
import { createVelarProject, parseCreateArguments } from "create-velar";
|
|
17
15
|
import { projectImportKey } from "./project.js";
|
|
18
16
|
import { checkResolvedProject, discoverVelarSources, formatCheckOutput, projectTestModules } from "./project-check.js";
|
|
@@ -21,10 +19,11 @@ import { runDevServer } from "./dev-server.js";
|
|
|
21
19
|
import { nodeApplicationConfig, nodeApplicationEntry, nodeApplicationLauncherSource, runNodeApplication, runNodeDevelopment, } from "./node-application.js";
|
|
22
20
|
import { createFrameworkArtifacts } from "./framework-host.js";
|
|
23
21
|
import { resolveVelarProject } from "./config.js";
|
|
24
|
-
import {
|
|
22
|
+
import { standardModuleClosure, standardModuleSource, standardModuleSources } from "./standard-modules.js";
|
|
25
23
|
import { runTests } from "./test-runner.js";
|
|
26
24
|
import { runProgram } from "./program-runner.js";
|
|
27
|
-
import { buildProductionFramework, writeProductionManifest } from "./production-build.js";
|
|
25
|
+
import { buildProductionFramework, PRODUCTION_MANIFEST_NAME, writeProductionManifest } from "./production-build.js";
|
|
26
|
+
import { formatSourceChecked } from "./format-guard.js";
|
|
28
27
|
import { VELAR_VERSION } from "./version.js";
|
|
29
28
|
import { assertRequiredPublicAssets, copyPublicAssets, writeStaticDeployment } from "./static-deployment.js";
|
|
30
29
|
import { verifyProductionBuild } from "./production-verifier.js";
|
|
@@ -37,8 +36,8 @@ import { loadApplicationPackageHost, validateApplicationPackageResult } from "./
|
|
|
37
36
|
import { buildLanguageServerTool } from "./language-server-tool.js";
|
|
38
37
|
import { applyProjectMechanicalFixes } from "./mechanical-fixer.js";
|
|
39
38
|
import { bundleStandaloneJavaScript, needsStandaloneJavaScriptBundle } from "./standalone-build.js";
|
|
40
|
-
import { resolveInstalledPackageRoot } from "./installed-package.js";
|
|
41
39
|
import { BUILD_STAGING_MARKER } from "./build-staging.js";
|
|
40
|
+
import { writeServerConfigurationDependency, writeWebSocketDependency } from "./node-runtime-dependencies.js";
|
|
42
41
|
import { assertUniqueEmbeddedModuleOutputs, embeddedModuleFileContents, embeddedModuleOutputPath, VELAR_EMBEDDED_MODULE_MARKER, } from "./embedded-modules.js";
|
|
43
42
|
import { resourceOutputRelativePath, writeBuildResourcePackageManifests, writeProjectResources } from "./resource-output.js";
|
|
44
43
|
async function main(arguments_) {
|
|
@@ -97,10 +96,11 @@ async function main(arguments_) {
|
|
|
97
96
|
core: "ai-skill.md",
|
|
98
97
|
web: "ai-skill-web.md",
|
|
99
98
|
node: "ai-skill-node.md",
|
|
99
|
+
server: "ai-skill-server.md",
|
|
100
100
|
desktop: "ai-skill-desktop.md",
|
|
101
101
|
});
|
|
102
102
|
if (rest.length > 1 || files[kind] === undefined) {
|
|
103
|
-
process.stderr.write("velar skill: expected core, web, node, or desktop\n");
|
|
103
|
+
process.stderr.write("velar skill: expected core, web, node, server, or desktop\n");
|
|
104
104
|
return 2;
|
|
105
105
|
}
|
|
106
106
|
process.stdout.write(await readFile(new URL(`../skill/${files[kind]}`, import.meta.url), "utf8"));
|
|
@@ -338,10 +338,19 @@ async function main(arguments_) {
|
|
|
338
338
|
return 1;
|
|
339
339
|
}
|
|
340
340
|
const changed = [];
|
|
341
|
+
const unstable = [];
|
|
341
342
|
try {
|
|
342
343
|
for (const input of inputs) {
|
|
343
344
|
const source = await readVelarSourceFile(input);
|
|
344
|
-
const formatted =
|
|
345
|
+
const { text: formatted, stable } = formatSourceChecked(source, { extensions: formattingExtensions });
|
|
346
|
+
// Formatting is idempotent by contract. A result the formatter would
|
|
347
|
+
// change again is a formatter defect, and writing it would replace a
|
|
348
|
+
// module that compiles with one that may not, so the file keeps the
|
|
349
|
+
// bytes the author wrote and the command reports the defect.
|
|
350
|
+
if (!stable) {
|
|
351
|
+
unstable.push(input);
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
345
354
|
if (formatted === source)
|
|
346
355
|
continue;
|
|
347
356
|
changed.push(input);
|
|
@@ -353,6 +362,12 @@ async function main(arguments_) {
|
|
|
353
362
|
process.stderr.write(`velar format: ${hostErrorMessage(error)}\n`);
|
|
354
363
|
return 1;
|
|
355
364
|
}
|
|
365
|
+
if (unstable.length > 0) {
|
|
366
|
+
for (const input of unstable) {
|
|
367
|
+
process.stderr.write(`velar format: ${displayPath(input)}: the formatter did not reach a fixed point; the file was left unchanged\n`);
|
|
368
|
+
}
|
|
369
|
+
return 1;
|
|
370
|
+
}
|
|
356
371
|
if (parsed.check && changed.length > 0) {
|
|
357
372
|
for (const input of changed)
|
|
358
373
|
process.stderr.write(`${displayPath(input)} is not formatted\n`);
|
|
@@ -381,8 +396,11 @@ async function main(arguments_) {
|
|
|
381
396
|
projectConfig = await resolveVelarProject(parsed.input);
|
|
382
397
|
if (projectConfig.framework)
|
|
383
398
|
await runDevServer(projectConfig, parsed.port ?? 5173);
|
|
384
|
-
else if (nodeApplicationConfig(projectConfig))
|
|
385
|
-
|
|
399
|
+
else if (nodeApplicationConfig(projectConfig)) {
|
|
400
|
+
if (parsed.port !== null)
|
|
401
|
+
throw new Error("Node application host and port belong to velar/server configuration; --port is available only to Web and Desktop development servers");
|
|
402
|
+
await runNodeDevelopment(projectConfig);
|
|
403
|
+
}
|
|
386
404
|
else
|
|
387
405
|
throw new Error("the project does not declare a Web, Desktop, or Node application target");
|
|
388
406
|
}
|
|
@@ -400,11 +418,7 @@ async function main(arguments_) {
|
|
|
400
418
|
}
|
|
401
419
|
try {
|
|
402
420
|
const projectConfig = await resolveVelarProject(parsed.input);
|
|
403
|
-
|
|
404
|
-
...(parsed.host === null ? {} : { host: parsed.host }),
|
|
405
|
-
...(parsed.port === null ? {} : { port: parsed.port }),
|
|
406
|
-
};
|
|
407
|
-
return await runNodeApplication(projectConfig, overrides);
|
|
421
|
+
return await runNodeApplication(projectConfig);
|
|
408
422
|
}
|
|
409
423
|
catch (error) {
|
|
410
424
|
process.stderr.write(`velar serve: ${hostErrorMessage(error)}\n`);
|
|
@@ -442,7 +456,7 @@ async function main(arguments_) {
|
|
|
442
456
|
}
|
|
443
457
|
return runTests(projectConfig, parsed.input);
|
|
444
458
|
}
|
|
445
|
-
const parsed = command === "package" ? parsePackageArguments(rest) : parseCommandArguments(rest);
|
|
459
|
+
const parsed = command === "package" ? parsePackageArguments(rest) : parseCommandArguments(rest, command === "build");
|
|
446
460
|
if (typeof parsed === "string") {
|
|
447
461
|
process.stderr.write(`velar ${command}: ${parsed}\n`);
|
|
448
462
|
return 2;
|
|
@@ -470,7 +484,13 @@ async function main(arguments_) {
|
|
|
470
484
|
}
|
|
471
485
|
if (command === "check") {
|
|
472
486
|
const count = checked.compiled.size;
|
|
473
|
-
|
|
487
|
+
// D89: a passing check that carried advisories says how many. Folding them
|
|
488
|
+
// into a silent "checked N modules" would hide the one thing the advisory
|
|
489
|
+
// channel exists to make impossible to miss; the exit code stays 0 either
|
|
490
|
+
// way, because an advisory is not a failure.
|
|
491
|
+
const advisories = checked.advisories.length;
|
|
492
|
+
process.stdout.write(`Checked ${count} module${count === 1 ? "" : "s"} from ${displayInput(parsed.input, projectConfig)}`
|
|
493
|
+
+ `${advisories > 0 ? ` — ${advisories} advisor${advisories === 1 ? "y" : "ies"}` : ""}\n`);
|
|
474
494
|
return 0;
|
|
475
495
|
}
|
|
476
496
|
if (command === "package") {
|
|
@@ -490,7 +510,7 @@ async function main(arguments_) {
|
|
|
490
510
|
if (buildRequests > 1)
|
|
491
511
|
throw new Error("application package host requested more than one framework build");
|
|
492
512
|
const outputDirectory = packageFrameworkOutput(projectConfig.root, requestedOutput);
|
|
493
|
-
frameworkBuild = writeFrameworkProductionApplication(project, outputDirectory);
|
|
513
|
+
frameworkBuild = writeFrameworkProductionApplication(project, outputDirectory, { forced: false, declared: false });
|
|
494
514
|
await frameworkBuild;
|
|
495
515
|
},
|
|
496
516
|
});
|
|
@@ -535,9 +555,15 @@ async function main(arguments_) {
|
|
|
535
555
|
return 0;
|
|
536
556
|
}
|
|
537
557
|
const outputDirectory = parsed.outputDirectory ? resolve(parsed.outputDirectory) : projectConfig.outDir;
|
|
558
|
+
// `outDir` is the project manifest's own declaration of a directory velar
|
|
559
|
+
// owns, and config.ts already refuses one that is the project root or that
|
|
560
|
+
// overlaps the entry or the public directory. `--out-dir` carries no such
|
|
561
|
+
// declaration, so a path it names has to prove ownership before the build
|
|
562
|
+
// replaces what is there.
|
|
563
|
+
const replacement = { forced: parsed.force, declared: outputDirectory === projectConfig.outDir };
|
|
538
564
|
if (project.framework) {
|
|
539
565
|
try {
|
|
540
|
-
await writeFrameworkProductionApplication(project, outputDirectory);
|
|
566
|
+
await writeFrameworkProductionApplication(project, outputDirectory, replacement);
|
|
541
567
|
}
|
|
542
568
|
catch (error) {
|
|
543
569
|
process.stderr.write(`velar build: ${hostErrorMessage(error)}\n`);
|
|
@@ -549,7 +575,7 @@ async function main(arguments_) {
|
|
|
549
575
|
const nodeConfig = nodeApplicationConfig(projectConfig);
|
|
550
576
|
if (nodeConfig) {
|
|
551
577
|
try {
|
|
552
|
-
await writeNodeProductionApplication(project, outputDirectory, nodeConfig);
|
|
578
|
+
await writeNodeProductionApplication(project, outputDirectory, nodeConfig, replacement);
|
|
553
579
|
}
|
|
554
580
|
catch (error) {
|
|
555
581
|
process.stderr.write(`velar build: ${hostErrorMessage(error)}\n`);
|
|
@@ -558,7 +584,14 @@ async function main(arguments_) {
|
|
|
558
584
|
process.stdout.write(`Built production Node app -> ${outputDirectory}\n`);
|
|
559
585
|
return 0;
|
|
560
586
|
}
|
|
561
|
-
|
|
587
|
+
let staging;
|
|
588
|
+
try {
|
|
589
|
+
staging = await prepareBuildStaging(outputDirectory, replacement);
|
|
590
|
+
}
|
|
591
|
+
catch (error) {
|
|
592
|
+
process.stderr.write(`velar build: ${hostErrorMessage(error)}\n`);
|
|
593
|
+
return 1;
|
|
594
|
+
}
|
|
562
595
|
try {
|
|
563
596
|
assertUniqueEmbeddedModuleOutputs(project.modules.map((module) => ({
|
|
564
597
|
ownerPath: join(staging, module.relativePath.replace(/\.vel$/, ".js")),
|
|
@@ -582,12 +615,12 @@ async function main(arguments_) {
|
|
|
582
615
|
process.stdout.write(`Built ${project.modules.length} module${project.modules.length === 1 ? "" : "s"} -> ${outputDirectory}\n`);
|
|
583
616
|
return 0;
|
|
584
617
|
}
|
|
585
|
-
async function writeFrameworkProductionApplication(project, outputDirectory) {
|
|
618
|
+
async function writeFrameworkProductionApplication(project, outputDirectory, replacement) {
|
|
586
619
|
if (!project.framework)
|
|
587
620
|
throw new Error("the checked project has no framework host");
|
|
588
621
|
const framework = project.framework;
|
|
589
622
|
await assertRequiredPublicAssets(project.publicRoot, project.projectRoot, framework.host.requiredPublicAssets?.(framework.config) ?? []);
|
|
590
|
-
const staging = await prepareBuildStaging(outputDirectory);
|
|
623
|
+
const staging = await prepareBuildStaging(outputDirectory, replacement);
|
|
591
624
|
try {
|
|
592
625
|
await copyPublicAssets(project.publicRoot, staging);
|
|
593
626
|
const production = await buildProductionFramework(project, staging);
|
|
@@ -608,9 +641,12 @@ async function writeFrameworkProductionApplication(project, outputDirectory) {
|
|
|
608
641
|
throw error;
|
|
609
642
|
}
|
|
610
643
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
644
|
+
/** The receipt a node build leaves in its output; it also proves velar owns the directory. */
|
|
645
|
+
const NODE_BUILD_MANIFEST_NAME = "velar-node.json";
|
|
646
|
+
async function writeNodeProductionApplication(project, outputDirectory, config, replacement) {
|
|
647
|
+
const application = nodeApplicationEntry(project, config);
|
|
648
|
+
const entry = application.entry;
|
|
649
|
+
const staging = await prepareBuildStaging(outputDirectory, replacement);
|
|
614
650
|
try {
|
|
615
651
|
assertUniqueEmbeddedModuleOutputs(project.modules.map((module) => ({
|
|
616
652
|
ownerPath: join(staging, module.relativePath.replace(/\.vel$/u, ".js")),
|
|
@@ -625,18 +661,18 @@ async function writeNodeProductionApplication(project, outputDirectory, config)
|
|
|
625
661
|
await writeBuildResourcePackageManifests(project, staging);
|
|
626
662
|
await writeNodeStandardModules(staging, project);
|
|
627
663
|
await copyPublicAssets(project.publicRoot, join(staging, "public"), true);
|
|
664
|
+
if (requiredNodeStandardModules(project).has("velar/server")) {
|
|
665
|
+
await copyConventionalServerConfiguration(project.projectRoot, staging);
|
|
666
|
+
}
|
|
628
667
|
const entryPath = `./${relative(project.sourceRoot, entry.inputPath).replace(/\.vel$/u, ".js").replaceAll("\\", "/")}`;
|
|
629
668
|
const launcher = ".velar-node-entry.mjs";
|
|
630
|
-
await writeFile(join(staging, launcher), nodeApplicationLauncherSource(entryPath, config,
|
|
669
|
+
await writeFile(join(staging, launcher), nodeApplicationLauncherSource(entryPath, config, false, application.kind), "utf8");
|
|
631
670
|
await writeFile(join(staging, "package.json"), `${JSON.stringify({ name: "velar-node-build", private: true, type: "module" }, null, 2)}\n`, "utf8");
|
|
632
|
-
await writeFile(join(staging,
|
|
633
|
-
formatVersion:
|
|
671
|
+
await writeFile(join(staging, NODE_BUILD_MANIFEST_NAME), `${JSON.stringify({
|
|
672
|
+
formatVersion: 2,
|
|
634
673
|
kind: "velar-node-build",
|
|
635
674
|
entry: launcher,
|
|
636
675
|
app: config.app,
|
|
637
|
-
host: config.host,
|
|
638
|
-
port: config.port,
|
|
639
|
-
maxBodyBytes: config.maxBodyBytes,
|
|
640
676
|
sourceMaps: config.build.sourceMaps,
|
|
641
677
|
}, null, 2)}\n`, "utf8");
|
|
642
678
|
await replaceOutputDirectory(staging, outputDirectory);
|
|
@@ -646,6 +682,37 @@ async function writeNodeProductionApplication(project, outputDirectory, config)
|
|
|
646
682
|
throw error;
|
|
647
683
|
}
|
|
648
684
|
}
|
|
685
|
+
const CONVENTIONAL_SERVER_CONFIGURATION_FILE = "application.yml";
|
|
686
|
+
const UNSUPPORTED_CONVENTIONAL_SERVER_CONFIGURATION_FILES = ["application.yaml", "application.json"];
|
|
687
|
+
async function copyConventionalServerConfiguration(projectRoot, outputRoot) {
|
|
688
|
+
for (const name of UNSUPPORTED_CONVENTIONAL_SERVER_CONFIGURATION_FILES) {
|
|
689
|
+
const path = join(projectRoot, name);
|
|
690
|
+
try {
|
|
691
|
+
const metadata = await lstat(path);
|
|
692
|
+
if (metadata.isSymbolicLink() || !metadata.isFile())
|
|
693
|
+
throw new Error(`Conventional Server configuration '${path}' must be a regular file`);
|
|
694
|
+
throw new Error(`Unsupported conventional Server configuration '${path}'; rename it to application.yml or use a non-conventional explicit configuration path`);
|
|
695
|
+
}
|
|
696
|
+
catch (error) {
|
|
697
|
+
if (!isHostErrorCode(error, "ENOENT"))
|
|
698
|
+
throw error;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
const path = join(projectRoot, CONVENTIONAL_SERVER_CONFIGURATION_FILE);
|
|
702
|
+
try {
|
|
703
|
+
const metadata = await lstat(path);
|
|
704
|
+
if (metadata.isSymbolicLink() || !metadata.isFile())
|
|
705
|
+
throw new Error(`Conventional Server configuration '${path}' must be a regular file`);
|
|
706
|
+
if (metadata.size > 1024 * 1024)
|
|
707
|
+
throw new Error(`Conventional Server configuration '${path}' cannot exceed 1 MiB`);
|
|
708
|
+
}
|
|
709
|
+
catch (error) {
|
|
710
|
+
if (isHostErrorCode(error, "ENOENT"))
|
|
711
|
+
return;
|
|
712
|
+
throw error;
|
|
713
|
+
}
|
|
714
|
+
await copyFile(path, join(outputRoot, CONVENTIONAL_SERVER_CONFIGURATION_FILE));
|
|
715
|
+
}
|
|
649
716
|
function packageFrameworkOutput(root, input) {
|
|
650
717
|
if (!isAbsolute(input))
|
|
651
718
|
throw new Error("application package host requested a non-absolute framework output path");
|
|
@@ -661,8 +728,9 @@ function packageFrameworkOutput(root, input) {
|
|
|
661
728
|
* output. A process cut can happen before or after either rename, so recovery
|
|
662
729
|
* also finishes restoring the previous output when installation never began.
|
|
663
730
|
*/
|
|
664
|
-
async function prepareBuildStaging(outputDirectory) {
|
|
731
|
+
async function prepareBuildStaging(outputDirectory, replacement) {
|
|
665
732
|
const normalizedOutput = resolve(outputDirectory);
|
|
733
|
+
await assertReplaceableBuildOutput(normalizedOutput, replacement);
|
|
666
734
|
const parent = dirname(normalizedOutput);
|
|
667
735
|
await mkdir(parent, { recursive: true });
|
|
668
736
|
await recoverInterruptedBuilds(normalizedOutput);
|
|
@@ -677,6 +745,67 @@ async function prepareBuildStaging(outputDirectory) {
|
|
|
677
745
|
await writeFile(join(staging, BUILD_STAGING_MARKER), `${JSON.stringify(ownership, null, 2)}\n`, "utf8");
|
|
678
746
|
return staging;
|
|
679
747
|
}
|
|
748
|
+
/**
|
|
749
|
+
* A build replaces its output directory wholesale, so the path is accepted only
|
|
750
|
+
* when velar owns it: absent, empty, carrying a manifest a previous build wrote,
|
|
751
|
+
* or named by the project manifest's own `outDir`. Every other destructive path
|
|
752
|
+
* in this repository makes the same test first — `assertReplaceableReleaseOutput`
|
|
753
|
+
* in scripts/release-toolchain.mjs, `prepareDirectory` in reproduction.ts, and
|
|
754
|
+
* `createVelarProject` — and `--out-dir` was the one an author runs daily that
|
|
755
|
+
* did not. `--force` waives the ownership test and nothing else: the home
|
|
756
|
+
* directory, the filesystem root and a symbolic link stay refused.
|
|
757
|
+
*/
|
|
758
|
+
async function assertReplaceableBuildOutput(outputDirectory, replacement) {
|
|
759
|
+
const directory = resolve(outputDirectory);
|
|
760
|
+
if (directory === parsePath(directory).root || directory === resolve(homedir())) {
|
|
761
|
+
throw new Error(`refusing to replace '${directory}': a build output cannot be the filesystem root or the home directory`);
|
|
762
|
+
}
|
|
763
|
+
let metadata;
|
|
764
|
+
try {
|
|
765
|
+
metadata = await lstat(directory);
|
|
766
|
+
}
|
|
767
|
+
catch (error) {
|
|
768
|
+
if (isHostErrorCode(error, "ENOENT") || isHostErrorCode(error, "ENOTDIR"))
|
|
769
|
+
return;
|
|
770
|
+
throw error;
|
|
771
|
+
}
|
|
772
|
+
if (metadata.isSymbolicLink())
|
|
773
|
+
throw new Error(`refusing to replace '${directory}': it is a symbolic link`);
|
|
774
|
+
if (!metadata.isDirectory())
|
|
775
|
+
throw new Error(`refusing to replace '${directory}': it is not a directory`);
|
|
776
|
+
if (replacement.forced || replacement.declared)
|
|
777
|
+
return;
|
|
778
|
+
if ((await readdir(directory)).length === 0)
|
|
779
|
+
return;
|
|
780
|
+
if (await isBuildOutputDirectory(directory))
|
|
781
|
+
return;
|
|
782
|
+
throw new Error(`refusing to replace '${directory}': it is not empty and was not produced by velar build (pass --force to overwrite)`);
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* The three build shapes leave three different receipts: a framework build
|
|
786
|
+
* writes `velar-build.json`, a node build writes `velar-node.json`, and a build
|
|
787
|
+
* cut between the two renames leaves the staging marker naming this same output.
|
|
788
|
+
*/
|
|
789
|
+
async function isBuildOutputDirectory(directory) {
|
|
790
|
+
if (await buildStagingOwnership(directory, directory, null))
|
|
791
|
+
return true;
|
|
792
|
+
return await buildManifestKind(join(directory, PRODUCTION_MANIFEST_NAME)) === "velar-framework-build"
|
|
793
|
+
|| await buildManifestKind(join(directory, NODE_BUILD_MANIFEST_NAME)) === "velar-node-build";
|
|
794
|
+
}
|
|
795
|
+
async function buildManifestKind(path) {
|
|
796
|
+
try {
|
|
797
|
+
const metadata = await lstat(path);
|
|
798
|
+
if (!metadata.isFile() || metadata.isSymbolicLink())
|
|
799
|
+
return null;
|
|
800
|
+
const parsed = JSON.parse(await readFile(path, "utf8"));
|
|
801
|
+
return Number.isSafeInteger(parsed.formatVersion) && typeof parsed.kind === "string" ? parsed.kind : null;
|
|
802
|
+
}
|
|
803
|
+
catch (error) {
|
|
804
|
+
if (isHostErrorCode(error, "ENOENT") || isHostErrorCode(error, "ENOTDIR") || error instanceof SyntaxError)
|
|
805
|
+
return null;
|
|
806
|
+
throw error;
|
|
807
|
+
}
|
|
808
|
+
}
|
|
680
809
|
async function recoverInterruptedBuilds(outputDirectory) {
|
|
681
810
|
const parent = dirname(outputDirectory);
|
|
682
811
|
const prefix = `.velar-${basename(outputDirectory)}-`;
|
|
@@ -789,7 +918,8 @@ async function writeNodeStandardModules(outputRoot, project, replaceExisting = f
|
|
|
789
918
|
await writeNodeStandardModulePackage(packageRoot, used, project);
|
|
790
919
|
if (used.has("velar/websocket"))
|
|
791
920
|
await writeWebSocketDependency(dirname(packageRoot));
|
|
792
|
-
|
|
921
|
+
if (used.has("velar/server"))
|
|
922
|
+
await writeServerConfigurationDependency(dirname(packageRoot));
|
|
793
923
|
return;
|
|
794
924
|
}
|
|
795
925
|
const ownership = await generatedRuntimePackageOwnership(packageRoot);
|
|
@@ -810,50 +940,14 @@ async function writeNodeStandardModules(outputRoot, project, replaceExisting = f
|
|
|
810
940
|
await rename(staging, packageRoot);
|
|
811
941
|
if (used.has("velar/websocket"))
|
|
812
942
|
await writeWebSocketDependency(dirname(packageRoot));
|
|
813
|
-
|
|
943
|
+
if (used.has("velar/server"))
|
|
944
|
+
await writeServerConfigurationDependency(dirname(packageRoot));
|
|
814
945
|
}
|
|
815
946
|
catch (error) {
|
|
816
947
|
await rm(staging, { recursive: true, force: true });
|
|
817
948
|
throw error;
|
|
818
949
|
}
|
|
819
950
|
}
|
|
820
|
-
async function writeWebSocketDependency(nodeModulesRoot) {
|
|
821
|
-
const target = join(nodeModulesRoot, "ws");
|
|
822
|
-
try {
|
|
823
|
-
const manifest = JSON.parse(await readFile(join(target, "package.json"), "utf8"));
|
|
824
|
-
if (manifest.name !== "ws" || manifest.version !== "8.21.1")
|
|
825
|
-
throw new Error(`Refusing to use incompatible WebSocket runtime package '${target}'`);
|
|
826
|
-
return;
|
|
827
|
-
}
|
|
828
|
-
catch (error) {
|
|
829
|
-
if (!isHostErrorCode(error, "ENOENT"))
|
|
830
|
-
throw error;
|
|
831
|
-
}
|
|
832
|
-
const require = createRequire(import.meta.url);
|
|
833
|
-
const source = dirname(require.resolve("ws/package.json"));
|
|
834
|
-
await cp(source, target, { recursive: true, errorOnExist: true });
|
|
835
|
-
}
|
|
836
|
-
async function writeAdapterDependencies(nodeModulesRoot, used) {
|
|
837
|
-
const require = createRequire(import.meta.url);
|
|
838
|
-
for (const [moduleName, dependency] of STANDARD_MODULE_ADAPTER_DEPENDENCIES) {
|
|
839
|
-
if (!used.has(moduleName))
|
|
840
|
-
continue;
|
|
841
|
-
const target = join(nodeModulesRoot, dependency.packageName);
|
|
842
|
-
try {
|
|
843
|
-
const manifest = JSON.parse(await readFile(join(target, "package.json"), "utf8"));
|
|
844
|
-
if (manifest.name !== dependency.packageName || manifest.version !== dependency.version) {
|
|
845
|
-
throw new Error(`Refusing to use incompatible ${moduleName} runtime package '${target}'`);
|
|
846
|
-
}
|
|
847
|
-
continue;
|
|
848
|
-
}
|
|
849
|
-
catch (error) {
|
|
850
|
-
if (!isHostErrorCode(error, "ENOENT"))
|
|
851
|
-
throw error;
|
|
852
|
-
}
|
|
853
|
-
const source = await resolveInstalledPackageRoot(dependency.packageName, dependency.packageName, require);
|
|
854
|
-
await cp(source, target, { recursive: true, errorOnExist: true });
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
951
|
function requiredNodeStandardModules(project) {
|
|
858
952
|
const sources = standardModuleSources(project.compilerExtensions);
|
|
859
953
|
const roots = new Set(project.modules.flatMap((module) => module.result.dependencies
|
|
@@ -1018,13 +1112,17 @@ function parseFormatArguments(arguments_) {
|
|
|
1018
1112
|
}
|
|
1019
1113
|
return { input, check };
|
|
1020
1114
|
}
|
|
1021
|
-
function parseCommandArguments(arguments_) {
|
|
1115
|
+
function parseCommandArguments(arguments_, allowForce = false) {
|
|
1022
1116
|
let input = null;
|
|
1023
1117
|
let output = null;
|
|
1024
1118
|
let outputDirectory = null;
|
|
1119
|
+
let force = false;
|
|
1025
1120
|
for (let index = 0; index < arguments_.length; index += 1) {
|
|
1026
1121
|
const argument = arguments_[index];
|
|
1027
|
-
if (
|
|
1122
|
+
if (allowForce && argument === "--force") {
|
|
1123
|
+
force = true;
|
|
1124
|
+
}
|
|
1125
|
+
else if (argument === "--out" || argument === "--out-dir") {
|
|
1028
1126
|
const value = arguments_[index + 1];
|
|
1029
1127
|
if (!value || value.startsWith("--")) {
|
|
1030
1128
|
return `${argument} requires a path`;
|
|
@@ -1049,7 +1147,7 @@ function parseCommandArguments(arguments_) {
|
|
|
1049
1147
|
input = argument;
|
|
1050
1148
|
}
|
|
1051
1149
|
}
|
|
1052
|
-
return { input, output, outputDirectory };
|
|
1150
|
+
return { input, output, outputDirectory, force };
|
|
1053
1151
|
}
|
|
1054
1152
|
function parseReproArguments(arguments_) {
|
|
1055
1153
|
let input = null;
|
|
@@ -1089,7 +1187,7 @@ function parsePackageArguments(arguments_) {
|
|
|
1089
1187
|
return `unexpected extra input '${arguments_[1]}'`;
|
|
1090
1188
|
if (arguments_[0]?.startsWith("-"))
|
|
1091
1189
|
return `unknown option '${arguments_[0]}'`;
|
|
1092
|
-
return { input: arguments_[0] ?? null, output: null, outputDirectory: null };
|
|
1190
|
+
return { input: arguments_[0] ?? null, output: null, outputDirectory: null, force: false };
|
|
1093
1191
|
}
|
|
1094
1192
|
function parseDevArguments(arguments_) {
|
|
1095
1193
|
let input = null;
|
|
@@ -1118,26 +1216,8 @@ function parseDevArguments(arguments_) {
|
|
|
1118
1216
|
}
|
|
1119
1217
|
function parseServeArguments(arguments_) {
|
|
1120
1218
|
let input = null;
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
for (let index = 0; index < arguments_.length; index += 1) {
|
|
1124
|
-
const argument = arguments_[index];
|
|
1125
|
-
if (argument === "--host") {
|
|
1126
|
-
const value = arguments_[index + 1];
|
|
1127
|
-
if (!value || value.startsWith("--") || value.length > 255 || value.includes("\0"))
|
|
1128
|
-
return "--host requires a bounded hostname or IP address";
|
|
1129
|
-
host = value;
|
|
1130
|
-
index += 1;
|
|
1131
|
-
}
|
|
1132
|
-
else if (argument === "--port") {
|
|
1133
|
-
const value = arguments_[index + 1];
|
|
1134
|
-
const parsed = value ? Number(value) : Number.NaN;
|
|
1135
|
-
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65_535)
|
|
1136
|
-
return "--port requires an integer from 1 to 65535";
|
|
1137
|
-
port = parsed;
|
|
1138
|
-
index += 1;
|
|
1139
|
-
}
|
|
1140
|
-
else if (argument.startsWith("--")) {
|
|
1219
|
+
for (const argument of arguments_) {
|
|
1220
|
+
if (argument.startsWith("--")) {
|
|
1141
1221
|
return `unknown option '${argument}'`;
|
|
1142
1222
|
}
|
|
1143
1223
|
else if (input) {
|
|
@@ -1147,7 +1227,7 @@ function parseServeArguments(arguments_) {
|
|
|
1147
1227
|
input = argument;
|
|
1148
1228
|
}
|
|
1149
1229
|
}
|
|
1150
|
-
return { input
|
|
1230
|
+
return { input };
|
|
1151
1231
|
}
|
|
1152
1232
|
function parseTestArguments(arguments_) {
|
|
1153
1233
|
let input = null;
|
|
@@ -1303,8 +1383,8 @@ function printHelp(output = process.stdout) {
|
|
|
1303
1383
|
" velar remove <package>...",
|
|
1304
1384
|
" velar update [package...]",
|
|
1305
1385
|
" velar dev [entry.vel | project-directory] [--port <port>]",
|
|
1306
|
-
" velar serve [project-directory]
|
|
1307
|
-
" velar build [entry.vel | project-directory] [--out-dir <directory>]",
|
|
1386
|
+
" velar serve [project-directory]",
|
|
1387
|
+
" velar build [entry.vel | project-directory] [--out-dir <directory>] [--force]",
|
|
1308
1388
|
" velar run [entry.vel | project-directory] [--stack] [-- <program-arguments>...]",
|
|
1309
1389
|
" velar verify [project-directory | build-directory]",
|
|
1310
1390
|
" velar preview [project-directory | build-directory] [--port <port>]",
|
|
@@ -1316,7 +1396,7 @@ function printHelp(output = process.stdout) {
|
|
|
1316
1396
|
" velar format [file.vel | project-directory] [--check]",
|
|
1317
1397
|
" velar fix [entry.vel | project-directory]",
|
|
1318
1398
|
" velar repro [entry.vel | project-directory] [--out-dir <directory>]",
|
|
1319
|
-
" velar skill [core|web|node|desktop]",
|
|
1399
|
+
" velar skill [core|web|node|server|desktop]",
|
|
1320
1400
|
" velar lsp",
|
|
1321
1401
|
" velar --version",
|
|
1322
1402
|
"",
|
|
@@ -1334,9 +1414,9 @@ function printCommandHelp(command, output = process.stdout) {
|
|
|
1334
1414
|
add: ["Usage: velar add <package[@version]>... [--dev]", "Adds npm registry packages and activates packages that declare velar.extension metadata."],
|
|
1335
1415
|
remove: ["Usage: velar remove <package>...", "Removes npm packages and their extension-owned VelarScript project configuration."],
|
|
1336
1416
|
update: ["Usage: velar update [package...]", "Updates all or selected direct dependencies within package.json ranges through npm."],
|
|
1337
|
-
dev: ["Usage: velar dev [entry.vel | project-directory] [--port <1-65535>]", "Watches a framework app or last-good Node
|
|
1338
|
-
serve: ["Usage: velar serve [project-directory]
|
|
1339
|
-
build: ["Usage: velar build [entry.vel | project-directory] [--out-dir <directory>]", " velar build <single.vel> --out <file.js>", "Builds isolated Web/Desktop output, a standalone Node application, or JavaScript modules."],
|
|
1417
|
+
dev: ["Usage: velar dev [entry.vel | project-directory] [--port <1-65535>]", "Watches a framework app or last-good Node server factory; --port applies only to Web and Desktop development servers."],
|
|
1418
|
+
serve: ["Usage: velar serve [project-directory]", "Checks and runs a Node server factory with production runtime behavior; host and port belong to velar/server configuration."],
|
|
1419
|
+
build: ["Usage: velar build [entry.vel | project-directory] [--out-dir <directory>] [--force]", " velar build <single.vel> --out <file.js>", "Builds isolated Web/Desktop output, a standalone Node application, or JavaScript modules.", "--out-dir refuses a directory that is not empty and was not produced by a previous build; --force replaces one anyway."],
|
|
1340
1420
|
package: ["Usage: velar package [project-directory]", "Packages an application through its target-owned native packaging host."],
|
|
1341
1421
|
run: ["Usage: velar run [entry.vel | project-directory] [--stack] [-- <program-arguments>...]", "Compiles the resolved Core project and executes its entry module once on Node.js; arguments after '--' reach the program.", "--stack prints the full Node.js trace behind an uncaught program error instead of the VelarScript frames."],
|
|
1342
1422
|
verify: ["Usage: velar verify [project-directory | build-directory]", "Verifies the exact production manifest, inventory, sizes, hashes, and relationships."],
|
|
@@ -1356,7 +1436,7 @@ function printCommandHelp(command, output = process.stdout) {
|
|
|
1356
1436
|
"The bundle is extracted to a temporary directory and re-checked first; if the copy stops reproducing, the command says so rather than reporting a clean reproduction.",
|
|
1357
1437
|
"The default location is .velar/repro inside the project, replaced on each run; a directory named with --out-dir must be empty.",
|
|
1358
1438
|
],
|
|
1359
|
-
skill: ["Usage: velar skill [core|web|node|desktop]", "Prints one packaged, owner-specific VelarScript AI skill brief verbatim to stdout; the default is core."],
|
|
1439
|
+
skill: ["Usage: velar skill [core|web|node|server|desktop]", "Prints one packaged, owner-specific VelarScript AI skill brief verbatim to stdout; the default is core."],
|
|
1360
1440
|
lsp: ["Usage: velar lsp", "Runs the stdio language server for an editor host."],
|
|
1361
1441
|
};
|
|
1362
1442
|
output.write(["VelarScript Compiler", "", ...(details[command] ?? []), ""].join("\n"));
|