@vertz/cli 0.2.12 → 0.2.14
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/dist/index.d.ts +18 -2
- package/dist/index.js +313 -199
- package/dist/shared/chunk-08ajrqm8.js +248 -0
- package/dist/shared/chunk-f6hd0h7q.js +17 -0
- package/dist/shared/chunk-tb59xfvg.js +247 -0
- package/dist/shared/chunk-wvn2b9k8.js +16 -0
- package/dist/vertz.js +298 -185
- package/package.json +8 -8
package/dist/vertz.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
__require,
|
|
4
|
+
loadDbContext
|
|
5
|
+
} from "./shared/chunk-08ajrqm8.js";
|
|
4
6
|
|
|
5
7
|
// src/cli.ts
|
|
6
8
|
import { Command } from "commander";
|
|
7
|
-
import { createJiti
|
|
9
|
+
import { createJiti } from "jiti";
|
|
8
10
|
|
|
9
11
|
// src/commands/build.ts
|
|
10
12
|
import { err, ok } from "@vertz/errors";
|
|
@@ -53,8 +55,11 @@ import { createCompiler as createCompiler2 } from "@vertz/compiler";
|
|
|
53
55
|
import * as esbuild from "esbuild";
|
|
54
56
|
|
|
55
57
|
// src/pipeline/orchestrator.ts
|
|
56
|
-
import {
|
|
57
|
-
import {
|
|
58
|
+
import { createCodegenPipeline, generate } from "@vertz/codegen";
|
|
59
|
+
import {
|
|
60
|
+
createCompiler,
|
|
61
|
+
OpenAPIGenerator
|
|
62
|
+
} from "@vertz/compiler";
|
|
58
63
|
var defaultPipelineConfig = {
|
|
59
64
|
sourceDir: "src",
|
|
60
65
|
outputDir: ".vertz/generated",
|
|
@@ -107,6 +112,13 @@ class PipelineOrchestrator {
|
|
|
107
112
|
if (!analyzeResult.success) {
|
|
108
113
|
success = false;
|
|
109
114
|
}
|
|
115
|
+
if (success) {
|
|
116
|
+
const dbSyncResult = await this.runDbSync();
|
|
117
|
+
stages.push(dbSyncResult);
|
|
118
|
+
if (!dbSyncResult.success) {
|
|
119
|
+
success = false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
110
122
|
if (success && this.appIR) {
|
|
111
123
|
const generateResult = await this.runCodegen();
|
|
112
124
|
stages.push(generateResult);
|
|
@@ -271,12 +283,60 @@ class PipelineOrchestrator {
|
|
|
271
283
|
}
|
|
272
284
|
async runDbSync() {
|
|
273
285
|
const startTime = performance.now();
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
286
|
+
if (!this.config.autoSyncDb) {
|
|
287
|
+
return {
|
|
288
|
+
stage: "db-sync",
|
|
289
|
+
success: true,
|
|
290
|
+
durationMs: performance.now() - startTime,
|
|
291
|
+
output: "DB sync skipped (disabled)"
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
let runner;
|
|
295
|
+
try {
|
|
296
|
+
if (this.config._dbSyncRunner) {
|
|
297
|
+
runner = await this.config._dbSyncRunner();
|
|
298
|
+
} else {
|
|
299
|
+
const { loadAutoMigrateContext } = await import("./shared/chunk-f6hd0h7q.js");
|
|
300
|
+
const ctx = await loadAutoMigrateContext();
|
|
301
|
+
const { autoMigrate } = await import("@vertz/db/internals");
|
|
302
|
+
runner = {
|
|
303
|
+
run: () => autoMigrate({
|
|
304
|
+
currentSchema: ctx.currentSchema,
|
|
305
|
+
snapshotPath: ctx.snapshotPath,
|
|
306
|
+
dialect: ctx.dialect,
|
|
307
|
+
db: ctx.db
|
|
308
|
+
}),
|
|
309
|
+
close: ctx.close
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
} catch {
|
|
313
|
+
return {
|
|
314
|
+
stage: "db-sync",
|
|
315
|
+
success: true,
|
|
316
|
+
durationMs: performance.now() - startTime,
|
|
317
|
+
output: "DB sync skipped (no db config)"
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
try {
|
|
321
|
+
await runner.run();
|
|
322
|
+
return {
|
|
323
|
+
stage: "db-sync",
|
|
324
|
+
success: true,
|
|
325
|
+
durationMs: performance.now() - startTime,
|
|
326
|
+
output: "DB sync complete"
|
|
327
|
+
};
|
|
328
|
+
} catch (error) {
|
|
329
|
+
return {
|
|
330
|
+
stage: "db-sync",
|
|
331
|
+
success: false,
|
|
332
|
+
durationMs: performance.now() - startTime,
|
|
333
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
334
|
+
};
|
|
335
|
+
} finally {
|
|
336
|
+
try {
|
|
337
|
+
await runner.close();
|
|
338
|
+
} catch {}
|
|
339
|
+
}
|
|
280
340
|
}
|
|
281
341
|
getAppIR() {
|
|
282
342
|
return this.appIR;
|
|
@@ -336,7 +396,7 @@ function getAffectedStages(category) {
|
|
|
336
396
|
case "entity":
|
|
337
397
|
return ["analyze", "openapi", "codegen"];
|
|
338
398
|
case "schema":
|
|
339
|
-
return ["codegen", "openapi"];
|
|
399
|
+
return ["db-sync", "codegen", "openapi"];
|
|
340
400
|
case "component":
|
|
341
401
|
return ["build-ui"];
|
|
342
402
|
case "config":
|
|
@@ -346,6 +406,7 @@ function getAffectedStages(category) {
|
|
|
346
406
|
return ["build-ui"];
|
|
347
407
|
}
|
|
348
408
|
}
|
|
409
|
+
var STAGE_ORDER = ["analyze", "db-sync", "codegen", "openapi", "build-ui"];
|
|
349
410
|
function getStagesForChanges(changes) {
|
|
350
411
|
const stages = new Set;
|
|
351
412
|
for (const change of changes) {
|
|
@@ -356,7 +417,7 @@ function getStagesForChanges(changes) {
|
|
|
356
417
|
if (stages.has("codegen") && !stages.has("analyze")) {
|
|
357
418
|
stages.add("analyze");
|
|
358
419
|
}
|
|
359
|
-
return
|
|
420
|
+
return STAGE_ORDER.filter((s) => stages.has(s));
|
|
360
421
|
}
|
|
361
422
|
function createWatcher(config) {
|
|
362
423
|
const {
|
|
@@ -1588,188 +1649,229 @@ function generateAction(options) {
|
|
|
1588
1649
|
}
|
|
1589
1650
|
}
|
|
1590
1651
|
|
|
1591
|
-
// src/commands/
|
|
1592
|
-
import {
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
configModule = await jiti.import(configPath);
|
|
1615
|
-
} catch {
|
|
1616
|
-
try {
|
|
1617
|
-
await access(configPath);
|
|
1618
|
-
} catch {
|
|
1619
|
-
throw new Error(`Could not find vertz.config.ts in ${process.cwd()}. Create it with a db export:
|
|
1620
|
-
|
|
1621
|
-
` + ` export const db = {
|
|
1622
|
-
` + ` dialect: 'sqlite',
|
|
1623
|
-
` + ` schema: './src/schema.ts',
|
|
1624
|
-
` + ` };
|
|
1625
|
-
`);
|
|
1652
|
+
// src/commands/start.ts
|
|
1653
|
+
import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync } from "node:fs";
|
|
1654
|
+
import { join as join6, resolve as resolve3 } from "node:path";
|
|
1655
|
+
import { err as err7, ok as ok7 } from "@vertz/errors";
|
|
1656
|
+
function discoverSSRModule(projectRoot) {
|
|
1657
|
+
const serverDir = join6(projectRoot, "dist", "server");
|
|
1658
|
+
if (!existsSync6(serverDir))
|
|
1659
|
+
return;
|
|
1660
|
+
const files = readdirSync2(serverDir).filter((f) => f.endsWith(".js"));
|
|
1661
|
+
if (files.length === 0)
|
|
1662
|
+
return;
|
|
1663
|
+
if (files.includes("app.js")) {
|
|
1664
|
+
return join6(serverDir, "app.js");
|
|
1665
|
+
}
|
|
1666
|
+
const first = files[0];
|
|
1667
|
+
return first ? join6(serverDir, first) : undefined;
|
|
1668
|
+
}
|
|
1669
|
+
function validateBuildOutputs(projectRoot, appType) {
|
|
1670
|
+
const missing = [];
|
|
1671
|
+
if (appType === "api-only" || appType === "full-stack") {
|
|
1672
|
+
const apiBuild = join6(projectRoot, ".vertz", "build", "index.js");
|
|
1673
|
+
if (!existsSync6(apiBuild)) {
|
|
1674
|
+
missing.push(".vertz/build/index.js");
|
|
1626
1675
|
}
|
|
1627
|
-
throw new Error(`Failed to load vertz.config.ts: the file exists but could not be parsed. Check for syntax errors.`);
|
|
1628
1676
|
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1677
|
+
if (appType === "ui-only" || appType === "full-stack") {
|
|
1678
|
+
const clientHtml = join6(projectRoot, "dist", "client", "index.html");
|
|
1679
|
+
if (!existsSync6(clientHtml)) {
|
|
1680
|
+
missing.push("dist/client/index.html");
|
|
1681
|
+
}
|
|
1682
|
+
const ssrModule = discoverSSRModule(projectRoot);
|
|
1683
|
+
if (!ssrModule) {
|
|
1684
|
+
missing.push("dist/server/ (no SSR module found)");
|
|
1685
|
+
}
|
|
1638
1686
|
}
|
|
1639
|
-
if (
|
|
1640
|
-
|
|
1687
|
+
if (missing.length > 0) {
|
|
1688
|
+
return err7(new Error(`Missing build outputs:
|
|
1689
|
+
- ${missing.join(`
|
|
1690
|
+
- `)}
|
|
1691
|
+
|
|
1692
|
+
Run "vertz build" first.`));
|
|
1641
1693
|
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1694
|
+
return ok7(undefined);
|
|
1695
|
+
}
|
|
1696
|
+
async function startAction(options = {}) {
|
|
1697
|
+
const { port = Number(process.env.PORT) || 3000, host = "0.0.0.0", verbose = false } = options;
|
|
1698
|
+
const projectRoot = findProjectRoot(process.cwd());
|
|
1699
|
+
if (!projectRoot) {
|
|
1700
|
+
return err7(new Error("Could not find project root. Are you in a Vertz project?"));
|
|
1644
1701
|
}
|
|
1645
|
-
|
|
1646
|
-
const migrationsDir = resolve3(cwd, dbConfig.migrationsDir ?? "./migrations");
|
|
1647
|
-
const snapshotPath = dbConfig.snapshotPath ? resolve3(cwd, dbConfig.snapshotPath) : join6(migrationsDir, "_snapshot.json");
|
|
1648
|
-
const schemaPath = resolve3(cwd, dbConfig.schema);
|
|
1649
|
-
let schemaModule;
|
|
1702
|
+
let detected;
|
|
1650
1703
|
try {
|
|
1651
|
-
|
|
1652
|
-
} catch {
|
|
1653
|
-
|
|
1654
|
-
await access(schemaPath);
|
|
1655
|
-
} catch {
|
|
1656
|
-
throw new Error(`Schema file not found: ${schemaPath}. Check the \`schema\` path in your db config.`);
|
|
1657
|
-
}
|
|
1658
|
-
throw new Error(`Failed to load schema file at ${schemaPath}. Check for syntax errors.`);
|
|
1704
|
+
detected = detectAppType(projectRoot);
|
|
1705
|
+
} catch (error) {
|
|
1706
|
+
return err7(new Error(error instanceof Error ? error.message : String(error)));
|
|
1659
1707
|
}
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1708
|
+
if (verbose) {
|
|
1709
|
+
console.log(`Detected app type: ${detected.type}`);
|
|
1710
|
+
}
|
|
1711
|
+
const validation = validateBuildOutputs(projectRoot, detected.type);
|
|
1712
|
+
if (!validation.ok) {
|
|
1713
|
+
return validation;
|
|
1714
|
+
}
|
|
1715
|
+
switch (detected.type) {
|
|
1716
|
+
case "api-only":
|
|
1717
|
+
return startApiOnly(projectRoot, port, host, verbose);
|
|
1718
|
+
case "ui-only":
|
|
1719
|
+
return startUIOnly(projectRoot, port, host, verbose);
|
|
1720
|
+
case "full-stack":
|
|
1721
|
+
return startFullStack(projectRoot, port, host, verbose);
|
|
1666
1722
|
}
|
|
1667
|
-
const currentSnapshot = createSnapshot(tables);
|
|
1668
|
-
const storage = new NodeSnapshotStorage;
|
|
1669
|
-
const savedSnapshot = await storage.load(snapshotPath);
|
|
1670
|
-
const previousSnapshot = savedSnapshot ?? { version: 1, tables: {}, enums: {} };
|
|
1671
|
-
const dialect = dbConfig.dialect === "sqlite" ? defaultSqliteDialect : defaultPostgresDialect;
|
|
1672
|
-
const connection = await createConnection(dbConfig);
|
|
1673
|
-
const migrationFiles = await loadMigrationFiles(migrationsDir);
|
|
1674
|
-
const existingFiles = migrationFiles.map((f) => f.name);
|
|
1675
|
-
const writeFile = async (path, content) => {
|
|
1676
|
-
await mkdir(dirname2(path), { recursive: true });
|
|
1677
|
-
await fsWriteFile(path, content, "utf-8");
|
|
1678
|
-
};
|
|
1679
|
-
const readFile = (path) => fsReadFile(path, "utf-8");
|
|
1680
|
-
return {
|
|
1681
|
-
queryFn: connection.queryFn,
|
|
1682
|
-
currentSnapshot,
|
|
1683
|
-
previousSnapshot,
|
|
1684
|
-
savedSnapshot: savedSnapshot ?? undefined,
|
|
1685
|
-
migrationFiles,
|
|
1686
|
-
migrationsDir,
|
|
1687
|
-
existingFiles,
|
|
1688
|
-
dialect,
|
|
1689
|
-
writeFile,
|
|
1690
|
-
readFile,
|
|
1691
|
-
close: connection.close
|
|
1692
|
-
};
|
|
1693
|
-
}
|
|
1694
|
-
function isTableDef(v) {
|
|
1695
|
-
return v !== null && typeof v === "object" && "_name" in v && "_columns" in v && typeof v._columns === "object" && v._columns !== null;
|
|
1696
|
-
}
|
|
1697
|
-
function extractTables(module) {
|
|
1698
|
-
return Object.values(module).filter(isTableDef);
|
|
1699
|
-
}
|
|
1700
|
-
function parseSqliteUrl(url) {
|
|
1701
|
-
if (!url)
|
|
1702
|
-
return DEFAULT_SQLITE_PATH;
|
|
1703
|
-
if (!url.startsWith("sqlite:"))
|
|
1704
|
-
return url;
|
|
1705
|
-
const stripped = url.slice("sqlite:".length);
|
|
1706
|
-
if (stripped.startsWith("///"))
|
|
1707
|
-
return stripped.slice(2);
|
|
1708
|
-
if (stripped.startsWith("//"))
|
|
1709
|
-
return stripped.slice(2) || DEFAULT_SQLITE_PATH;
|
|
1710
|
-
return stripped || DEFAULT_SQLITE_PATH;
|
|
1711
1723
|
}
|
|
1712
|
-
async function
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
const queryFn2 = async (sql, params) => {
|
|
1724
|
-
const stmt = db.prepare(sql);
|
|
1725
|
-
const rows = stmt.all(...params);
|
|
1726
|
-
return { rows, rowCount: rows.length };
|
|
1727
|
-
};
|
|
1728
|
-
const close2 = async () => {
|
|
1729
|
-
db.close();
|
|
1730
|
-
};
|
|
1731
|
-
return { queryFn: queryFn2, close: close2 };
|
|
1724
|
+
async function startApiOnly(projectRoot, port, host, _verbose) {
|
|
1725
|
+
const entryPath = resolve3(projectRoot, ".vertz", "build", "index.js");
|
|
1726
|
+
let mod;
|
|
1727
|
+
try {
|
|
1728
|
+
mod = await import(entryPath);
|
|
1729
|
+
} catch (error) {
|
|
1730
|
+
return err7(new Error(`Failed to import API module: ${error instanceof Error ? error.message : String(error)}`));
|
|
1731
|
+
}
|
|
1732
|
+
const handler = mod.default?.handler;
|
|
1733
|
+
if (typeof handler !== "function") {
|
|
1734
|
+
return err7(new Error("API module must export default with a .handler function."));
|
|
1732
1735
|
}
|
|
1733
|
-
|
|
1736
|
+
const server = Bun.serve({
|
|
1737
|
+
port,
|
|
1738
|
+
hostname: host,
|
|
1739
|
+
fetch: handler
|
|
1740
|
+
});
|
|
1741
|
+
console.log(`Vertz API server running at http://${host === "0.0.0.0" ? "localhost" : host}:${server.port}`);
|
|
1742
|
+
setupGracefulShutdown(server);
|
|
1743
|
+
return ok7(undefined);
|
|
1744
|
+
}
|
|
1745
|
+
async function startUIOnly(projectRoot, port, host, _verbose) {
|
|
1746
|
+
const ssrModulePath = discoverSSRModule(projectRoot);
|
|
1747
|
+
if (!ssrModulePath) {
|
|
1748
|
+
return err7(new Error('No SSR module found in dist/server/. Run "vertz build" first.'));
|
|
1749
|
+
}
|
|
1750
|
+
const templatePath = resolve3(projectRoot, "dist", "client", "index.html");
|
|
1751
|
+
const template = readFileSync(templatePath, "utf-8");
|
|
1752
|
+
let ssrModule;
|
|
1734
1753
|
try {
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
const
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1754
|
+
ssrModule = await import(ssrModulePath);
|
|
1755
|
+
} catch (error) {
|
|
1756
|
+
return err7(new Error(`Failed to import SSR module: ${error instanceof Error ? error.message : String(error)}`));
|
|
1757
|
+
}
|
|
1758
|
+
const inlineCSS = discoverInlineCSS(projectRoot);
|
|
1759
|
+
const { createSSRHandler } = await import("@vertz/ui-server/ssr");
|
|
1760
|
+
const ssrHandler = createSSRHandler({
|
|
1761
|
+
module: ssrModule,
|
|
1762
|
+
template,
|
|
1763
|
+
inlineCSS
|
|
1764
|
+
});
|
|
1765
|
+
const clientDir = resolve3(projectRoot, "dist", "client");
|
|
1766
|
+
const server = Bun.serve({
|
|
1767
|
+
port,
|
|
1768
|
+
hostname: host,
|
|
1769
|
+
async fetch(req) {
|
|
1770
|
+
const url = new URL(req.url);
|
|
1771
|
+
const pathname = url.pathname;
|
|
1772
|
+
const staticResponse = serveStaticFile(clientDir, pathname);
|
|
1773
|
+
if (staticResponse)
|
|
1774
|
+
return staticResponse;
|
|
1775
|
+
return ssrHandler(req);
|
|
1776
|
+
}
|
|
1777
|
+
});
|
|
1778
|
+
console.log(`Vertz server running at http://${host === "0.0.0.0" ? "localhost" : host}:${server.port}`);
|
|
1779
|
+
setupGracefulShutdown(server);
|
|
1780
|
+
return ok7(undefined);
|
|
1749
1781
|
}
|
|
1750
|
-
async function
|
|
1751
|
-
|
|
1782
|
+
async function startFullStack(projectRoot, port, host, _verbose) {
|
|
1783
|
+
const apiEntryPath = resolve3(projectRoot, ".vertz", "build", "index.js");
|
|
1784
|
+
let apiMod;
|
|
1752
1785
|
try {
|
|
1753
|
-
|
|
1786
|
+
apiMod = await import(apiEntryPath);
|
|
1754
1787
|
} catch (error) {
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
continue;
|
|
1765
|
-
const content = await fsReadFile(join6(dir, filename), "utf-8");
|
|
1766
|
-
files.push({
|
|
1767
|
-
name: parsed.name,
|
|
1768
|
-
sql: content,
|
|
1769
|
-
timestamp: parsed.timestamp
|
|
1770
|
-
});
|
|
1788
|
+
return err7(new Error(`Failed to import API module: ${error instanceof Error ? error.message : String(error)}`));
|
|
1789
|
+
}
|
|
1790
|
+
const apiHandler = apiMod.default?.handler;
|
|
1791
|
+
if (typeof apiHandler !== "function") {
|
|
1792
|
+
return err7(new Error("API module must export default with a .handler function."));
|
|
1793
|
+
}
|
|
1794
|
+
const ssrModulePath = discoverSSRModule(projectRoot);
|
|
1795
|
+
if (!ssrModulePath) {
|
|
1796
|
+
return err7(new Error('No SSR module found in dist/server/. Run "vertz build" first.'));
|
|
1771
1797
|
}
|
|
1772
|
-
|
|
1798
|
+
const templatePath = resolve3(projectRoot, "dist", "client", "index.html");
|
|
1799
|
+
const template = readFileSync(templatePath, "utf-8");
|
|
1800
|
+
let ssrModule;
|
|
1801
|
+
try {
|
|
1802
|
+
ssrModule = await import(ssrModulePath);
|
|
1803
|
+
} catch (error) {
|
|
1804
|
+
return err7(new Error(`Failed to import SSR module: ${error instanceof Error ? error.message : String(error)}`));
|
|
1805
|
+
}
|
|
1806
|
+
const inlineCSS = discoverInlineCSS(projectRoot);
|
|
1807
|
+
const { createSSRHandler } = await import("@vertz/ui-server/ssr");
|
|
1808
|
+
const ssrHandler = createSSRHandler({
|
|
1809
|
+
module: ssrModule,
|
|
1810
|
+
template,
|
|
1811
|
+
inlineCSS
|
|
1812
|
+
});
|
|
1813
|
+
const clientDir = resolve3(projectRoot, "dist", "client");
|
|
1814
|
+
const server = Bun.serve({
|
|
1815
|
+
port,
|
|
1816
|
+
hostname: host,
|
|
1817
|
+
async fetch(req) {
|
|
1818
|
+
const url = new URL(req.url);
|
|
1819
|
+
const pathname = url.pathname;
|
|
1820
|
+
if (pathname.startsWith("/api")) {
|
|
1821
|
+
return apiHandler(req);
|
|
1822
|
+
}
|
|
1823
|
+
const staticResponse = serveStaticFile(clientDir, pathname);
|
|
1824
|
+
if (staticResponse)
|
|
1825
|
+
return staticResponse;
|
|
1826
|
+
return ssrHandler(req);
|
|
1827
|
+
}
|
|
1828
|
+
});
|
|
1829
|
+
console.log(`Vertz full-stack server running at http://${host === "0.0.0.0" ? "localhost" : host}:${server.port}`);
|
|
1830
|
+
setupGracefulShutdown(server);
|
|
1831
|
+
return ok7(undefined);
|
|
1832
|
+
}
|
|
1833
|
+
function discoverInlineCSS(projectRoot) {
|
|
1834
|
+
const cssDir = resolve3(projectRoot, "dist", "client", "assets");
|
|
1835
|
+
if (!existsSync6(cssDir))
|
|
1836
|
+
return;
|
|
1837
|
+
const cssFiles = readdirSync2(cssDir).filter((f) => f.endsWith(".css"));
|
|
1838
|
+
if (cssFiles.length === 0)
|
|
1839
|
+
return;
|
|
1840
|
+
const result = {};
|
|
1841
|
+
for (const file of cssFiles) {
|
|
1842
|
+
const content = readFileSync(join6(cssDir, file), "utf-8");
|
|
1843
|
+
result[`/assets/${file}`] = content;
|
|
1844
|
+
}
|
|
1845
|
+
return result;
|
|
1846
|
+
}
|
|
1847
|
+
function serveStaticFile(clientDir, pathname) {
|
|
1848
|
+
if (pathname === "/" || pathname === "/index.html")
|
|
1849
|
+
return null;
|
|
1850
|
+
const filePath = resolve3(clientDir, `.${pathname}`);
|
|
1851
|
+
if (!filePath.startsWith(clientDir))
|
|
1852
|
+
return null;
|
|
1853
|
+
const file = Bun.file(filePath);
|
|
1854
|
+
if (!file.size)
|
|
1855
|
+
return null;
|
|
1856
|
+
const isHashedAsset = pathname.startsWith("/assets/");
|
|
1857
|
+
const cacheControl = isHashedAsset ? "public, max-age=31536000, immutable" : "public, max-age=3600";
|
|
1858
|
+
return new Response(file, {
|
|
1859
|
+
headers: {
|
|
1860
|
+
"Cache-Control": cacheControl,
|
|
1861
|
+
"Content-Type": file.type
|
|
1862
|
+
}
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
function setupGracefulShutdown(server) {
|
|
1866
|
+
const shutdown = () => {
|
|
1867
|
+
console.log(`
|
|
1868
|
+
Shutting down...`);
|
|
1869
|
+
server.stop();
|
|
1870
|
+
process.exit(0);
|
|
1871
|
+
};
|
|
1872
|
+
process.on("SIGINT", shutdown);
|
|
1873
|
+
process.on("SIGTERM", shutdown);
|
|
1874
|
+
process.on("SIGHUP", shutdown);
|
|
1773
1875
|
}
|
|
1774
1876
|
|
|
1775
1877
|
// src/cli.ts
|
|
@@ -1812,6 +1914,17 @@ function createCLI() {
|
|
|
1812
1914
|
process.exit(1);
|
|
1813
1915
|
}
|
|
1814
1916
|
});
|
|
1917
|
+
program.command("start").description('Start the production server (run "vertz build" first)').option("-p, --port <port>", "Server port (default: PORT env or 3000)").option("--host <host>", "Server host", "0.0.0.0").option("-v, --verbose", "Verbose output").action(async (opts) => {
|
|
1918
|
+
const result = await startAction({
|
|
1919
|
+
port: opts.port ? parseInt(opts.port, 10) : undefined,
|
|
1920
|
+
host: opts.host,
|
|
1921
|
+
verbose: opts.verbose
|
|
1922
|
+
});
|
|
1923
|
+
if (!result.ok) {
|
|
1924
|
+
console.error(result.error.message);
|
|
1925
|
+
process.exit(1);
|
|
1926
|
+
}
|
|
1927
|
+
});
|
|
1815
1928
|
program.command("generate [type] [name]").description("Generate a module, service, router, or schema").option("--dry-run", "Preview generated files without writing").option("--source-dir <dir>", "Source directory", "src").allowUnknownOption().action(async (type, name, options) => {
|
|
1816
1929
|
const validTypes = ["module", "service", "router", "schema"];
|
|
1817
1930
|
if (!type || !validTypes.includes(type)) {
|
|
@@ -1831,13 +1944,13 @@ function createCLI() {
|
|
|
1831
1944
|
}
|
|
1832
1945
|
});
|
|
1833
1946
|
program.command("codegen").description("Generate SDK and CLI clients from the compiled API").option("--dry-run", "Preview generated files without writing").option("--output <dir>", "Output directory").action(async (opts) => {
|
|
1834
|
-
const { resolve: resolve4, dirname:
|
|
1835
|
-
const { mkdir
|
|
1947
|
+
const { resolve: resolve4, dirname: dirname2 } = await import("node:path");
|
|
1948
|
+
const { mkdir, writeFile: fsWriteFile } = await import("node:fs/promises");
|
|
1836
1949
|
let config;
|
|
1837
1950
|
let compilerConfig;
|
|
1838
1951
|
try {
|
|
1839
1952
|
const configPath = resolve4(process.cwd(), "vertz.config.ts");
|
|
1840
|
-
const jiti =
|
|
1953
|
+
const jiti = createJiti(import.meta.url);
|
|
1841
1954
|
const configModule = await jiti.import(configPath);
|
|
1842
1955
|
config = configModule.codegen ?? configModule.default?.codegen;
|
|
1843
1956
|
compilerConfig = configModule.default;
|
|
@@ -1861,8 +1974,8 @@ function createCLI() {
|
|
|
1861
1974
|
const { createCodegenPipeline: createCodegenPipeline2 } = await import("@vertz/codegen");
|
|
1862
1975
|
const pipeline = createCodegenPipeline2();
|
|
1863
1976
|
const writeFile = async (path, content) => {
|
|
1864
|
-
await
|
|
1865
|
-
await
|
|
1977
|
+
await mkdir(dirname2(path), { recursive: true });
|
|
1978
|
+
await fsWriteFile(path, content, "utf-8");
|
|
1866
1979
|
};
|
|
1867
1980
|
const result = await codegenAction({
|
|
1868
1981
|
config,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertz/cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Vertz CLI — dev, build, create, and deploy commands",
|
|
@@ -34,13 +34,13 @@
|
|
|
34
34
|
"typecheck": "tsc --noEmit -p tsconfig.typecheck.json"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@vertz/codegen": "^0.2.
|
|
38
|
-
"@vertz/errors": "^0.2.
|
|
39
|
-
"@vertz/tui": "^0.2.
|
|
40
|
-
"@vertz/compiler": "^0.2.
|
|
41
|
-
"@vertz/create-vertz-app": "^0.2.
|
|
42
|
-
"@vertz/db": "^0.2.
|
|
43
|
-
"@vertz/ui-server": "^0.2.
|
|
37
|
+
"@vertz/codegen": "^0.2.13",
|
|
38
|
+
"@vertz/errors": "^0.2.13",
|
|
39
|
+
"@vertz/tui": "^0.2.13",
|
|
40
|
+
"@vertz/compiler": "^0.2.13",
|
|
41
|
+
"@vertz/create-vertz-app": "^0.2.13",
|
|
42
|
+
"@vertz/db": "^0.2.13",
|
|
43
|
+
"@vertz/ui-server": "^0.2.13",
|
|
44
44
|
"esbuild": "^0.25.0",
|
|
45
45
|
"commander": "^14.0.0",
|
|
46
46
|
"jiti": "^2.4.2"
|