@voltro/cli 0.2.1 → 0.2.2
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/CHANGELOG.md +16 -0
- package/bin/voltro.mjs +20 -1
- package/dist/apiBuild-OpZROja5.js +2 -0
- package/dist/apiBuild-o70rjpVJ.js +184 -0
- package/dist/bin.js +2 -2
- package/dist/{commands-BgOfGwLc.js → commands-DQy4812j.js} +1992 -3032
- package/dist/{dev-BGYmeKEx.js → dev--jHe1vcu.js} +599 -595
- package/dist/dev-BKkZglQV.js +2 -0
- package/dist/index.js +1 -1
- package/dist/serveCommand-93rRdEp0.js +1077 -0
- package/dist/serveEntry.d.ts +22 -0
- package/dist/serveEntry.js +4 -0
- package/dist/startup.js +1 -1
- package/dist/{startupRunner-DAexyPkM.js → startupRunner-DhlX9nqd.js} +21 -9
- package/package.json +22 -17
- package/templates/agent-docs/deployment.md +12 -1
- package/templates/apps/api-ai/package.json +7 -7
- package/templates/apps/api-auth/package.json +8 -8
- package/templates/apps/api-backend/package.json +7 -7
- package/templates/apps/api-backend-deactivation/package.json +7 -7
- package/templates/apps/api-backend-mail/package.json +8 -8
- package/templates/apps/api-backend-mariadb/package.json +9 -9
- package/templates/apps/api-backend-storage/package.json +8 -8
- package/templates/apps/api-data-advanced/package.json +8 -8
- package/templates/apps/api-durable/package.json +8 -8
- package/templates/apps/api-feature-flags/package.json +9 -9
- package/templates/apps/api-governance/package.json +8 -8
- package/templates/apps/api-kv/package.json +8 -8
- package/templates/apps/api-moderation/package.json +8 -8
- package/templates/apps/api-observability/package.json +8 -8
- package/templates/apps/api-ratelimit/package.json +8 -8
- package/templates/apps/api-rbac/package.json +8 -8
- package/templates/apps/api-rest/package.json +7 -7
- package/templates/apps/api-saas/package.json +11 -11
- package/templates/apps/api-search/package.json +8 -8
- package/templates/apps/api-versioning/package.json +8 -8
- package/templates/apps/api-webhooks/package.json +8 -8
- package/templates/apps/changelog/package.json +6 -6
- package/templates/apps/edge-functions/package.json +2 -2
- package/templates/apps/frontend-admin/package.json +8 -8
- package/templates/apps/frontend-app/package.json +8 -8
- package/templates/apps/frontend-blank/package.json +7 -7
- package/templates/apps/frontend-contact/package.json +7 -7
- package/templates/apps/frontend-dashboard/package.json +7 -7
- package/templates/apps/frontend-docs/package.json +7 -7
- package/templates/apps/frontend-i18n/package.json +6 -6
- package/templates/apps/frontend-landing/package.json +7 -7
- package/templates/apps/frontend-spa/package.json +7 -7
- package/templates/apps/frontend-ssr/package.json +7 -7
- package/templates/apps/frontend-ssr-api/package.json +8 -8
- package/templates/apps/frontend-static-blog/package.json +6 -6
- package/dist/apiBuild-B0dd-oDo.js +0 -62
- package/dist/apiBuild-D09wjKKO.js +0 -2
- package/dist/dev-DoTIxucX.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -39,6 +39,22 @@ _Changes staged for the next release accumulate here (rolled up from
|
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
42
|
+
## [0.2.2] — 2026-07-17
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- **@voltro/cli** — `voltro build` now precompiles the whole serve path (framework + effect + `@voltro` inlined, app modules as lazy chunks) into a single **serve bundle** (`.framework/dist-api/serveBundle/serveEntry.js`), and `voltro serve` boots from it in-process — no child `node --import tsx`, no CLI command graph, no per-module resolution of the ~2700-module framework graph. This cuts `serve: ready` from ~1000 ms to ~180 ms (~5–6×) on both driverless (memory) and driver-backed (postgres) apps; the win is larger on a cold scale-to-zero container where module resolution dominates. The app's declared SQL driver is inlined into the bundle so it shares the framework's single effect instance (only the native leaf like `pg` stays external, resolved at runtime so it survives the deploy relocation). Fully fallback-safe: a missing, stale, or corrupt bundle degrades to the standard tsx serve path, so it can never stop `voltro serve` from booting. Nothing to configure — building an API app produces the bundle and serving prefers it automatically.
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
|
|
50
|
+
- **@voltro/database** — The core-table registry (`registerCoreTables` / `requireActors` / `requireTenants`) now stores its state on a process-global `Symbol.for` singleton, the same mechanism the main table registry already uses — instead of module-local `let` bindings. Module-local state splits when the `@voltro/database` module is duplicated in a process (e.g. resolved through both the `.` and `./sql` entry points, or a bundled framework copy alongside an externally-resolved one): one instance's `registerCoreTables` becomes invisible to the instance that reads it, surfacing as a spurious `core 'actors' table not registered` at store construction. Pinning it to `globalThis` makes every copy share one store, matching the table registry's already-global behaviour.
|
|
51
|
+
|
|
52
|
+
### Internal (no consumer-facing effect)
|
|
53
|
+
|
|
54
|
+
- **@voltro/cli** — `appModuleLoader` now accepts lazy `() => import()` loaders alongside eager module namespaces (the eager path — today's `apiEntry.js` bundle — is unchanged). Groundwork for the serve bundle: app modules registered as lazy loaders evaluate on first `importAppModule` (during `runServe`, after `registerCoreTables`) rather than eagerly at bundle-import time. No consumer-facing effect on its own.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
42
58
|
## [0.2.1] — 2026-07-17
|
|
43
59
|
|
|
44
60
|
### Fixed
|
package/bin/voltro.mjs
CHANGED
|
@@ -4,7 +4,26 @@ import { spawn } from 'node:child_process'
|
|
|
4
4
|
import { existsSync } from 'node:fs'
|
|
5
5
|
import { createRequire } from 'node:module'
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
7
|
-
import { dirname, resolve } from 'node:path'
|
|
7
|
+
import { dirname, join, resolve } from 'node:path'
|
|
8
|
+
|
|
9
|
+
const argv = process.argv.slice(2)
|
|
10
|
+
if (argv[0] === 'serve') {
|
|
11
|
+
const appArg = argv.slice(1).find((a) => !a.startsWith('-'))
|
|
12
|
+
const serveRoot = appArg ? resolve(process.cwd(), appArg) : process.cwd()
|
|
13
|
+
const serveBundle = join(serveRoot, '.framework', 'dist-api', 'serveBundle', 'serveEntry.js')
|
|
14
|
+
if (existsSync(serveBundle)) {
|
|
15
|
+
try {
|
|
16
|
+
if (serveRoot !== process.cwd()) process.chdir(serveRoot)
|
|
17
|
+
const mod = await import(pathToFileURL(serveBundle).href)
|
|
18
|
+
if (typeof mod.runServe === 'function') {
|
|
19
|
+
const code = await mod.runServe(argv.slice(1))
|
|
20
|
+
process.exit(typeof code === 'number' ? code : 0)
|
|
21
|
+
}
|
|
22
|
+
} catch (error) {
|
|
23
|
+
console.warn('[voltro] serve bundle failed to load — using the standard serve path:', error?.message ?? error)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
8
27
|
|
|
9
28
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
10
29
|
const built = resolve(here, '..', 'dist', 'bin.js')
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { d as e, et as t } from "./dev--jHe1vcu.js";
|
|
2
|
+
import { isAbsolute as n, join as r, relative as i, resolve as a } from "node:path";
|
|
3
|
+
import { createLogger as o } from "@voltro/logger";
|
|
4
|
+
import { existsSync as s, promises as c } from "node:fs";
|
|
5
|
+
import { createRequire as l } from "node:module";
|
|
6
|
+
//#region src/apiBuild.ts
|
|
7
|
+
var u = o({ scope: "voltro:build" }), d = async (e) => {
|
|
8
|
+
let n = r(e, "package.json");
|
|
9
|
+
if (!s(n)) return [];
|
|
10
|
+
let i = {};
|
|
11
|
+
try {
|
|
12
|
+
let e = JSON.parse(await c.readFile(n, "utf8"));
|
|
13
|
+
i = {
|
|
14
|
+
...e.dependencies,
|
|
15
|
+
...e.devDependencies,
|
|
16
|
+
...e.optionalDependencies
|
|
17
|
+
};
|
|
18
|
+
} catch {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
let a = l(n);
|
|
22
|
+
return t.filter((e) => {
|
|
23
|
+
if (i[e] === void 0) return !1;
|
|
24
|
+
try {
|
|
25
|
+
return a.resolve(e), !0;
|
|
26
|
+
} catch {
|
|
27
|
+
return !1;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}, f = /(?:\.(?:query|mutation|action|stream)(?:\.server)?\.ts|\.workflow(?:\.server)?\.tsx|\.(?:subscribe|reaction|aggregate|agent|tool|webhook|cron|trigger)(?:\.server)?\.tsx?|\.(?:startup|seed)\.tsx?|\.(?:entity|schema|relations)\.ts|(?:^|\/)schema\.ts)$/, p = (e) => /\.(ts|tsx)$/.test(e) && !/\.(test|spec)\.[tj]sx?$/.test(e), m = async (t) => (await e(t)).filter((e) => f.test(e) && p(e) && !e.includes(`${t}/.framework/`)), h = {
|
|
31
|
+
name: "externalize-bare",
|
|
32
|
+
setup(e) {
|
|
33
|
+
e.onResolve({ filter: /.*/ }, (e) => {
|
|
34
|
+
let t = e.path;
|
|
35
|
+
if (!(t.startsWith(".") || n(t))) return {
|
|
36
|
+
path: t,
|
|
37
|
+
external: !0
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}, g = async (e) => {
|
|
42
|
+
let t = await import("esbuild"), n = r(e, ".framework"), o = r(n, "dist-api");
|
|
43
|
+
await c.mkdir(n, { recursive: !0 });
|
|
44
|
+
let l = [...await m(e)], d = r(e, "app.config.ts");
|
|
45
|
+
if (s(d) && l.push(d), l.length === 0) return u.warn("api build: no handlers or app.config found — nothing to precompile", { root: e }), null;
|
|
46
|
+
let f = r(e, "tsconfig.json"), g = {
|
|
47
|
+
bundle: !0,
|
|
48
|
+
format: "esm",
|
|
49
|
+
platform: "node",
|
|
50
|
+
target: "node22",
|
|
51
|
+
logLevel: "silent",
|
|
52
|
+
jsx: "automatic",
|
|
53
|
+
jsxImportSource: "react",
|
|
54
|
+
absWorkingDir: e,
|
|
55
|
+
...s(f) ? { tsconfig: f } : {},
|
|
56
|
+
plugins: [h]
|
|
57
|
+
}, _ = await t.build({
|
|
58
|
+
...g,
|
|
59
|
+
entryPoints: l,
|
|
60
|
+
write: !1,
|
|
61
|
+
metafile: !0,
|
|
62
|
+
outdir: o
|
|
63
|
+
}), v = Object.keys(_.metafile.inputs).map((t) => a(e, t)).filter((t) => t.startsWith(`${e}/`) && !t.includes("/node_modules/") && !t.includes("/.framework/") && p(t)), y = [
|
|
64
|
+
"// GENERATED BY `voltro build` — do not edit; overwritten on next build.",
|
|
65
|
+
"// Precompiled api entry: the single bundle the serve runtime loads instead",
|
|
66
|
+
"// of importing every handler/schema source at boot. See apiBuild.ts /",
|
|
67
|
+
"// appModuleLoader.ts.",
|
|
68
|
+
""
|
|
69
|
+
], b = [];
|
|
70
|
+
v.forEach((t, n) => {
|
|
71
|
+
y.push(`import * as m${n} from ${JSON.stringify(t)}`), b.push(` ${JSON.stringify(i(e, t))}: m${n},`);
|
|
72
|
+
}), y.push("", "export const modules: Record<string, unknown> = {", ...b, "}", "");
|
|
73
|
+
let x = r(n, "apiEntry.ts");
|
|
74
|
+
return await c.writeFile(x, y.join("\n"), "utf8"), await t.build({
|
|
75
|
+
...g,
|
|
76
|
+
entryPoints: [x],
|
|
77
|
+
outfile: r(o, "apiEntry.js"),
|
|
78
|
+
metafile: !1,
|
|
79
|
+
write: !0
|
|
80
|
+
}), u.info("api build: precompiled bundle written", {
|
|
81
|
+
modules: v.length,
|
|
82
|
+
out: r(".framework", "dist-api", "apiEntry.js")
|
|
83
|
+
}), r(o, "apiEntry.js");
|
|
84
|
+
}, _ = (e) => r(e, ".framework", "dist-api", "apiEntry.js"), v = [
|
|
85
|
+
"better-sqlite3",
|
|
86
|
+
"mysql2",
|
|
87
|
+
"tedious",
|
|
88
|
+
"pg",
|
|
89
|
+
"pg-native",
|
|
90
|
+
"@tursodatabase/*",
|
|
91
|
+
"@libsql/*",
|
|
92
|
+
"ioredis",
|
|
93
|
+
"@vlasky/zongji",
|
|
94
|
+
"nodemailer"
|
|
95
|
+
], y = [
|
|
96
|
+
"tsx",
|
|
97
|
+
"esbuild",
|
|
98
|
+
"vite",
|
|
99
|
+
"@vitejs/*",
|
|
100
|
+
"@tailwindcss/*",
|
|
101
|
+
"rolldown",
|
|
102
|
+
"lightningcss"
|
|
103
|
+
], b = (e, t) => t.some((t) => t.endsWith("/*") ? e === t.slice(0, -2) || e.startsWith(t.slice(0, -1)) : e === t || e.startsWith(`${t}/`)), x = (e, t) => `const { createRequire } = require('node:module')
|
|
104
|
+
const { join } = require('node:path')
|
|
105
|
+
const reqApp = createRequire(join(process.cwd(), 'package.json'))
|
|
106
|
+
const drivers = ${JSON.stringify(t)}\nlet mod\nfor (const d of drivers) {\n try { mod = createRequire(reqApp.resolve(d + '/package.json'))(${JSON.stringify(e)}); break } catch (e) {}\n}\nif (!mod) { try { mod = reqApp(${JSON.stringify(e)}) } catch (e) {} }\nif (!mod) mod = require(${JSON.stringify(e)})\nmodule.exports = mod\n`, S = (e) => ({
|
|
107
|
+
name: "native-leaf-runtime-resolver",
|
|
108
|
+
setup(t) {
|
|
109
|
+
t.onResolve({ filter: /.*/ }, (e) => {
|
|
110
|
+
let t = e.path;
|
|
111
|
+
if (!(t.startsWith(".") || n(t) || t.startsWith("node:"))) {
|
|
112
|
+
if (b(t, v)) return {
|
|
113
|
+
path: t,
|
|
114
|
+
namespace: "native-leaf"
|
|
115
|
+
};
|
|
116
|
+
if (b(t, y)) return {
|
|
117
|
+
path: t,
|
|
118
|
+
external: !0
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
}), t.onLoad({
|
|
122
|
+
filter: /.*/,
|
|
123
|
+
namespace: "native-leaf"
|
|
124
|
+
}, (t) => ({
|
|
125
|
+
contents: x(t.path, e),
|
|
126
|
+
loader: "js"
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
}), C = (e) => r(e, ".framework", "dist-api", "serveBundle", "serveEntry.js"), w = async (e) => {
|
|
130
|
+
let t = await import("esbuild"), n = r(e, ".framework"), o = r(n, "dist-api", "serveBundle");
|
|
131
|
+
await c.mkdir(n, { recursive: !0 });
|
|
132
|
+
let l = [...await m(e)], f = r(e, "app.config.ts");
|
|
133
|
+
if (s(f) && l.push(f), l.length === 0) return null;
|
|
134
|
+
let g = r(e, "tsconfig.json"), _ = await t.build({
|
|
135
|
+
entryPoints: l,
|
|
136
|
+
bundle: !0,
|
|
137
|
+
write: !1,
|
|
138
|
+
metafile: !0,
|
|
139
|
+
format: "esm",
|
|
140
|
+
platform: "node",
|
|
141
|
+
absWorkingDir: e,
|
|
142
|
+
jsx: "automatic",
|
|
143
|
+
jsxImportSource: "react",
|
|
144
|
+
logLevel: "silent",
|
|
145
|
+
plugins: [h],
|
|
146
|
+
outdir: r(o, ".probe"),
|
|
147
|
+
...s(g) ? { tsconfig: g } : {}
|
|
148
|
+
}), v = Object.keys(_.metafile.inputs).map((t) => a(e, t)).filter((t) => t.startsWith(`${e}/`) && !t.includes("/node_modules/") && !t.includes("/.framework/") && p(t)), y = await d(e), b = [
|
|
149
|
+
"// GENERATED BY `voltro build` — do not edit; overwritten on next build.",
|
|
150
|
+
"import { join as __join } from 'node:path'",
|
|
151
|
+
"import { runServe as __runServe, registerAppModules, registerDriver, loadDotEnv } from '@voltro/cli/serveEntry'",
|
|
152
|
+
...y.map((e, t) => `import * as __drv${t} from ${JSON.stringify(e)}`),
|
|
153
|
+
"const __root = process.cwd()",
|
|
154
|
+
"registerAppModules({",
|
|
155
|
+
...v.map((t) => ` [__join(__root, ${JSON.stringify(i(e, t))})]: () => import(${JSON.stringify(t)}),`),
|
|
156
|
+
"})",
|
|
157
|
+
...y.map((e, t) => `registerDriver(${JSON.stringify(e)}, __drv${t})`),
|
|
158
|
+
"export const runServe = (args = []) => { loadDotEnv([]); return __runServe(args) }",
|
|
159
|
+
""
|
|
160
|
+
], x = r(n, "serveEntry.ts");
|
|
161
|
+
return await c.writeFile(x, b.join("\n"), "utf8"), await t.build({
|
|
162
|
+
entryPoints: [x],
|
|
163
|
+
bundle: !0,
|
|
164
|
+
format: "esm",
|
|
165
|
+
platform: "node",
|
|
166
|
+
target: "node22",
|
|
167
|
+
outdir: o,
|
|
168
|
+
splitting: !0,
|
|
169
|
+
write: !0,
|
|
170
|
+
plugins: [S(y)],
|
|
171
|
+
banner: { js: "import { createRequire as __voltroRequire } from 'node:module'; const require = __voltroRequire(import.meta.url);" },
|
|
172
|
+
jsx: "automatic",
|
|
173
|
+
jsxImportSource: "react",
|
|
174
|
+
logLevel: "silent",
|
|
175
|
+
absWorkingDir: e,
|
|
176
|
+
...s(g) ? { tsconfig: g } : {}
|
|
177
|
+
}), u.info("api build: serve bundle written", {
|
|
178
|
+
appModules: v.length,
|
|
179
|
+
drivers: y.length > 0 ? y.join(", ") : "none (memory)",
|
|
180
|
+
out: r(".framework", "dist-api", "serveBundle", "serveEntry.js")
|
|
181
|
+
}), r(o, "serveEntry.js");
|
|
182
|
+
};
|
|
183
|
+
//#endregion
|
|
184
|
+
export { C as a, w as i, m as n, g as r, _ as t };
|
package/dist/bin.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import { n as t } from "./commands-
|
|
2
|
+
import { At as e } from "./dev--jHe1vcu.js";
|
|
3
|
+
import { n as t } from "./commands-DQy4812j.js";
|
|
4
4
|
import { createLogger as n } from "@voltro/logger";
|
|
5
5
|
//#region src/bin.ts
|
|
6
6
|
var r = n({ scope: "voltro:cli" }), i = process.argv.slice(2);
|