@apex-stack/core 0.1.10 → 0.1.11
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/build-QRHQUUZC.js +235 -0
- package/dist/chunk-2L2T47AH.js +93 -0
- package/dist/chunk-4VG3CZ6H.js +21 -0
- package/dist/chunk-DSUIB3JH.js +144 -0
- package/dist/chunk-PAMD24NK.js +171 -0
- package/dist/chunk-QZTDKUXK.js +127 -0
- package/dist/cli.js +23 -664
- package/dist/dev-HDRJEPPN.js +38 -0
- package/dist/index.js +8 -3
- package/dist/make-4LINTKZH.js +89 -0
- package/dist/mcp-DL4J6JFJ.js +56 -0
- package/dist/migrate-NOGFOFV2.js +38 -0
- package/dist/server-VOIXBL4I.js +9 -0
- package/dist/start-VJJXI4W3.js +132 -0
- package/package.json +1 -1
- package/dist/chunk-IEXQ7E5C.js +0 -426
package/dist/cli.js
CHANGED
|
@@ -1,517 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
loadComponents,
|
|
8
|
-
matchRoute,
|
|
9
|
-
renderIslandsPage,
|
|
10
|
-
renderPage,
|
|
11
|
-
scanPages,
|
|
12
|
-
startDevServer
|
|
13
|
-
} from "./chunk-IEXQ7E5C.js";
|
|
3
|
+
banner,
|
|
4
|
+
color,
|
|
5
|
+
spinner
|
|
6
|
+
} from "./chunk-2L2T47AH.js";
|
|
14
7
|
|
|
15
8
|
// src/cli.ts
|
|
16
|
-
import {
|
|
17
|
-
import { defineCommand as defineCommand7, runMain } from "citty";
|
|
18
|
-
|
|
19
|
-
// src/commands/build.ts
|
|
20
|
-
import { cpSync, existsSync as existsSync2, mkdirSync, readdirSync as readdirSync2, rmSync, writeFileSync } from "fs";
|
|
21
|
-
import { dirname, join as join3, resolve } from "path";
|
|
22
|
-
import { apex as apex3 } from "@apex-stack/vite";
|
|
23
|
-
import { defineCommand } from "citty";
|
|
24
|
-
import { createServer as createViteServer } from "vite";
|
|
25
|
-
|
|
26
|
-
// src/build/buildClient.ts
|
|
27
|
-
import { readFileSync } from "fs";
|
|
28
|
-
import { join } from "path";
|
|
29
|
-
import { apex } from "@apex-stack/vite";
|
|
30
|
-
import { build } from "vite";
|
|
31
|
-
var VIRT = "virtual:apex-client:";
|
|
32
|
-
function entryName(pageId) {
|
|
33
|
-
return pageId.replace(/^\/pages\//, "").replace(/\.alpine$/, "").replace(/[^a-zA-Z0-9]+/g, "_");
|
|
34
|
-
}
|
|
35
|
-
async function buildClient(root, routes, outDir) {
|
|
36
|
-
const input = {};
|
|
37
|
-
for (const r of routes) input[entryName(r.pageId)] = `${VIRT}${r.pageId}`;
|
|
38
|
-
const entryPlugin = {
|
|
39
|
-
name: "apex:client-entries",
|
|
40
|
-
resolveId(id) {
|
|
41
|
-
if (id.startsWith(VIRT)) return `\0${id}`;
|
|
42
|
-
},
|
|
43
|
-
load(id) {
|
|
44
|
-
if (id.startsWith(`\0${VIRT}`)) {
|
|
45
|
-
const pageId = id.slice(`\0${VIRT}`.length);
|
|
46
|
-
return [
|
|
47
|
-
`import Alpine from 'alpinejs'`,
|
|
48
|
-
`import ${JSON.stringify(pageId)}`,
|
|
49
|
-
`window.Alpine = Alpine`,
|
|
50
|
-
`Alpine.start()`
|
|
51
|
-
].join("\n");
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
await build({
|
|
56
|
-
root,
|
|
57
|
-
logLevel: "warn",
|
|
58
|
-
plugins: [apex({ clientRuntime: "@apex-stack/core/client" }), entryPlugin],
|
|
59
|
-
build: {
|
|
60
|
-
outDir,
|
|
61
|
-
emptyOutDir: false,
|
|
62
|
-
manifest: true,
|
|
63
|
-
rollupOptions: { input }
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
const manifest = JSON.parse(readFileSync(join(outDir, ".vite", "manifest.json"), "utf8"));
|
|
67
|
-
const hrefs = /* @__PURE__ */ new Map();
|
|
68
|
-
for (const r of routes) {
|
|
69
|
-
const virt = `${VIRT}${r.pageId}`;
|
|
70
|
-
const entry = Object.values(manifest).find(
|
|
71
|
-
(m) => m.isEntry && (m.src === virt || m.src === `\0${virt}`)
|
|
72
|
-
);
|
|
73
|
-
if (entry) hrefs.set(r.pageId, `/${entry.file}`);
|
|
74
|
-
}
|
|
75
|
-
return hrefs;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// src/build/buildServer.ts
|
|
79
|
-
import { existsSync, readdirSync } from "fs";
|
|
80
|
-
import { isAbsolute, join as join2 } from "path";
|
|
81
|
-
import { apex as apex2 } from "@apex-stack/vite";
|
|
82
|
-
import { build as build2 } from "vite";
|
|
83
|
-
async function buildServer(root, routes, outDir) {
|
|
84
|
-
const ids = routes.map((r) => r.pageId);
|
|
85
|
-
const compDir = join2(root, "components");
|
|
86
|
-
if (existsSync(compDir)) {
|
|
87
|
-
for (const f of readdirSync(compDir).filter((f2) => f2.endsWith(".alpine"))) {
|
|
88
|
-
ids.push(`/components/${f}`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
const apiDir = join2(root, "server", "api");
|
|
92
|
-
if (existsSync(apiDir)) {
|
|
93
|
-
for (const f of readdirSync(apiDir).filter((f2) => /\.(ts|js|mjs)$/.test(f2))) {
|
|
94
|
-
ids.push(`/server/api/${f}`);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
const input = {};
|
|
98
|
-
for (const id of ids) input[entryName2(id)] = join2(root, id.slice(1));
|
|
99
|
-
const result = await build2({
|
|
100
|
-
root,
|
|
101
|
-
logLevel: "warn",
|
|
102
|
-
plugins: [apex2({ clientRuntime: "@apex-stack/core/client" })],
|
|
103
|
-
build: {
|
|
104
|
-
ssr: true,
|
|
105
|
-
target: "esnext",
|
|
106
|
-
// Node target — allow top-level await in server modules
|
|
107
|
-
outDir: join2(outDir, "server"),
|
|
108
|
-
emptyOutDir: false,
|
|
109
|
-
rollupOptions: {
|
|
110
|
-
input,
|
|
111
|
-
// Externalize every package import (bare specifier) — deps are resolved at
|
|
112
|
-
// runtime from node_modules. Only the app's own relative/absolute files are
|
|
113
|
-
// bundled. This keeps native/workspace deps (@libsql/client, drizzle, …) out.
|
|
114
|
-
external: (id) => !id.startsWith(".") && !isAbsolute(id),
|
|
115
|
-
output: { format: "esm", entryFileNames: "[name].mjs" }
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
const byFacade = /* @__PURE__ */ new Map();
|
|
120
|
-
for (const chunk of result.output) {
|
|
121
|
-
if (chunk.type === "chunk" && chunk.isEntry && chunk.facadeModuleId) {
|
|
122
|
-
byFacade.set(chunk.facadeModuleId, chunk.fileName);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
const modules = {};
|
|
126
|
-
for (const id of ids) {
|
|
127
|
-
const abs = join2(root, id.slice(1));
|
|
128
|
-
const file = byFacade.get(abs);
|
|
129
|
-
if (file) modules[id] = file;
|
|
130
|
-
}
|
|
131
|
-
return { modules };
|
|
132
|
-
}
|
|
133
|
-
function entryName2(id) {
|
|
134
|
-
return id.replace(/^\//, "").replace(/\.(alpine|ts|js|mjs)$/, "").replace(/[^a-zA-Z0-9]+/g, "_");
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// src/commands/build.ts
|
|
138
|
-
function outFile(pattern) {
|
|
139
|
-
const clean = pattern.replace(/^\//, "");
|
|
140
|
-
return clean === "" ? "index.html" : `${clean}/index.html`;
|
|
141
|
-
}
|
|
142
|
-
var buildCommand = defineCommand({
|
|
143
|
-
meta: { name: "build", description: "Prerender pages to deployable HTML + client bundles" },
|
|
144
|
-
args: {
|
|
145
|
-
root: { type: "positional", required: false, description: "Project root", default: "." },
|
|
146
|
-
outDir: { type: "string", description: "Output directory", default: "dist" },
|
|
147
|
-
islands: { type: "boolean", description: "Static-first islands mode (zero-JS static)", default: false },
|
|
148
|
-
server: { type: "boolean", description: "Build a Node server (dynamic routes + API/MCP)", default: false }
|
|
149
|
-
},
|
|
150
|
-
async run({ args }) {
|
|
151
|
-
const root = resolve(process.cwd(), args.root);
|
|
152
|
-
const outDir = resolve(root, args.outDir);
|
|
153
|
-
rmSync(outDir, { recursive: true, force: true });
|
|
154
|
-
const routes = scanPages(root);
|
|
155
|
-
const staticRoutes = routes.filter((r) => !r.isDynamic);
|
|
156
|
-
const dynamic = routes.filter((r) => r.isDynamic);
|
|
157
|
-
if (args.server) {
|
|
158
|
-
return buildServerTarget(root, outDir, args.outDir, routes);
|
|
159
|
-
}
|
|
160
|
-
const hrefs = args.islands ? /* @__PURE__ */ new Map() : await buildClient(root, staticRoutes, outDir);
|
|
161
|
-
const vite = await createViteServer({
|
|
162
|
-
root,
|
|
163
|
-
appType: "custom",
|
|
164
|
-
server: { middlewareMode: true },
|
|
165
|
-
plugins: [apex3({ clientRuntime: "@apex-stack/core/client" })]
|
|
166
|
-
});
|
|
167
|
-
try {
|
|
168
|
-
const { registry, css: componentCss } = await loadComponents(
|
|
169
|
-
root,
|
|
170
|
-
(id) => vite.ssrLoadModule(id)
|
|
171
|
-
);
|
|
172
|
-
for (const route of staticRoutes) {
|
|
173
|
-
const common = {
|
|
174
|
-
loadModule: (id) => vite.ssrLoadModule(id),
|
|
175
|
-
pageId: route.pageId,
|
|
176
|
-
url: route.pattern,
|
|
177
|
-
registry,
|
|
178
|
-
componentCss
|
|
179
|
-
};
|
|
180
|
-
const html = args.islands ? await renderIslandsPage(common) : await renderPage({ ...common, clientHref: hrefs.get(route.pageId) });
|
|
181
|
-
const dest = join3(outDir, outFile(route.pattern));
|
|
182
|
-
mkdirSync(dirname(dest), { recursive: true });
|
|
183
|
-
writeFileSync(dest, html);
|
|
184
|
-
console.log(` \u2713 ${route.pattern} \u2192 ${outFile(route.pattern)}`);
|
|
185
|
-
}
|
|
186
|
-
const pub = join3(root, "public");
|
|
187
|
-
if (existsSync2(pub)) cpSync(pub, outDir, { recursive: true });
|
|
188
|
-
console.log(
|
|
189
|
-
`
|
|
190
|
-
Built ${staticRoutes.length} page(s) \u2192 ${args.outDir}/` + (args.islands ? " (islands / static-first)" : " (prerendered + hydrated)")
|
|
191
|
-
);
|
|
192
|
-
if (dynamic.length) {
|
|
193
|
-
console.log(
|
|
194
|
-
` Skipped ${dynamic.length} dynamic route(s): ${dynamic.map((r) => r.pattern).join(", ")} (server target on the roadmap).`
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
console.log();
|
|
198
|
-
} finally {
|
|
199
|
-
await vite.close();
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
async function buildServerTarget(root, outDir, outLabel, routes) {
|
|
204
|
-
const clientHrefs = await buildClient(root, routes, outDir);
|
|
205
|
-
const server = await buildServer(root, routes, outDir);
|
|
206
|
-
const components = {};
|
|
207
|
-
const compDir = join3(root, "components");
|
|
208
|
-
if (existsSync2(compDir)) {
|
|
209
|
-
for (const f of readdirSync2(compDir).filter((f2) => f2.endsWith(".alpine"))) {
|
|
210
|
-
const sf = server.modules[`/components/${f}`];
|
|
211
|
-
if (sf) components[f.replace(/\.alpine$/, "")] = sf;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
const api = [];
|
|
215
|
-
const apiDir = join3(root, "server", "api");
|
|
216
|
-
if (existsSync2(apiDir)) {
|
|
217
|
-
for (const f of readdirSync2(apiDir).filter((f2) => /\.(ts|js|mjs)$/.test(f2))) {
|
|
218
|
-
const sf = server.modules[`/server/api/${f}`];
|
|
219
|
-
if (sf) api.push({ name: f.replace(/\.(ts|js|mjs)$/, ""), serverFile: sf });
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
const manifest = {
|
|
223
|
-
islands: false,
|
|
224
|
-
routes: routes.map((r) => ({
|
|
225
|
-
...r,
|
|
226
|
-
serverFile: server.modules[r.pageId],
|
|
227
|
-
clientHref: clientHrefs.get(r.pageId)
|
|
228
|
-
})),
|
|
229
|
-
components,
|
|
230
|
-
api
|
|
231
|
-
};
|
|
232
|
-
writeFileSync(join3(outDir, "apex-manifest.json"), JSON.stringify(manifest, null, 2));
|
|
233
|
-
const pub = join3(root, "public");
|
|
234
|
-
if (existsSync2(pub)) cpSync(pub, outDir, { recursive: true });
|
|
235
|
-
console.log(
|
|
236
|
-
`
|
|
237
|
-
Built server target \u2192 ${outLabel}/ (${routes.length} route(s), ${api.length} API module(s))
|
|
238
|
-
Run it: apex start
|
|
239
|
-
`
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// src/commands/make.ts
|
|
244
|
-
import { existsSync as existsSync3, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
245
|
-
import { dirname as dirname2, join as join4, resolve as resolve2 } from "path";
|
|
246
|
-
import { defineCommand as defineCommand2 } from "citty";
|
|
247
|
-
function pageTemplate(name) {
|
|
248
|
-
return `<script server lang="ts">
|
|
249
|
-
export function loader() {
|
|
250
|
-
return { title: '${name}' }
|
|
251
|
-
}
|
|
252
|
-
</script>
|
|
253
|
-
|
|
254
|
-
<template x-data>
|
|
255
|
-
<main>
|
|
256
|
-
<h1 x-text="title"></h1>
|
|
257
|
-
</main>
|
|
258
|
-
</template>
|
|
259
|
-
|
|
260
|
-
<style scoped>
|
|
261
|
-
main { max-width: 40rem; margin: 3rem auto; font-family: system-ui, sans-serif; }
|
|
262
|
-
</style>
|
|
263
|
-
`;
|
|
264
|
-
}
|
|
265
|
-
function componentTemplate() {
|
|
266
|
-
return `<template x-data="{ count: 0 }">
|
|
267
|
-
<button @click="count++" x-text="count"></button>
|
|
268
|
-
</template>
|
|
269
|
-
|
|
270
|
-
<style scoped>
|
|
271
|
-
button { cursor: pointer; }
|
|
272
|
-
</style>
|
|
273
|
-
`;
|
|
274
|
-
}
|
|
275
|
-
function apiTemplate(name) {
|
|
276
|
-
return `import { defineApexRoute } from '@apex-stack/core'
|
|
277
|
-
import { z } from 'zod'
|
|
278
|
-
|
|
279
|
-
// GET /api/${name} \xB7 MCP tool "${name}"
|
|
280
|
-
export default defineApexRoute({
|
|
281
|
-
method: 'GET',
|
|
282
|
-
description: 'Describe what ${name} does',
|
|
283
|
-
input: { name: z.string() },
|
|
284
|
-
mcp: true,
|
|
285
|
-
handler: ({ input }) => ({ message: \`Hello, \${input.name}!\` }),
|
|
286
|
-
})
|
|
287
|
-
`;
|
|
288
|
-
}
|
|
289
|
-
function plan(kind, name, root) {
|
|
290
|
-
switch (kind) {
|
|
291
|
-
case "page":
|
|
292
|
-
return { path: join4(root, "pages", `${name}.alpine`), contents: pageTemplate(name) };
|
|
293
|
-
case "component":
|
|
294
|
-
return { path: join4(root, "components", `${name}.alpine`), contents: componentTemplate() };
|
|
295
|
-
case "api":
|
|
296
|
-
return { path: join4(root, "server", "api", `${name}.ts`), contents: apiTemplate(name) };
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
var makeCommand = defineCommand2({
|
|
300
|
-
meta: { name: "make", description: "Generate a page, component, or API route" },
|
|
301
|
-
args: {
|
|
302
|
-
kind: { type: "positional", required: true, description: "page | component | api" },
|
|
303
|
-
name: { type: "positional", required: true, description: "Name (about, Counter, todos, \u2026)" },
|
|
304
|
-
root: { type: "string", description: "Project root", default: "." }
|
|
305
|
-
},
|
|
306
|
-
run({ args }) {
|
|
307
|
-
const kind = args.kind;
|
|
308
|
-
if (kind !== "page" && kind !== "component" && kind !== "api") {
|
|
309
|
-
console.error(`
|
|
310
|
-
Unknown type "${args.kind}". Use: page | component | api
|
|
311
|
-
`);
|
|
312
|
-
process.exit(1);
|
|
313
|
-
}
|
|
314
|
-
const root = resolve2(process.cwd(), args.root);
|
|
315
|
-
const { path, contents } = plan(kind, args.name, root);
|
|
316
|
-
if (existsSync3(path)) {
|
|
317
|
-
console.error(`
|
|
318
|
-
\u2717 Already exists: ${path}
|
|
319
|
-
`);
|
|
320
|
-
process.exit(1);
|
|
321
|
-
}
|
|
322
|
-
mkdirSync2(dirname2(path), { recursive: true });
|
|
323
|
-
writeFileSync2(path, contents);
|
|
324
|
-
console.log(`
|
|
325
|
-
\u2713 Created ${path.replace(`${root}/`, "")}
|
|
326
|
-
`);
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
// src/commands/mcp.ts
|
|
331
|
-
import { defineCommand as defineCommand3 } from "citty";
|
|
332
|
-
var mcpCommand = defineCommand3({
|
|
333
|
-
meta: { name: "mcp", description: "Inspect the local MCP server (list or call tools)" },
|
|
334
|
-
args: {
|
|
335
|
-
url: { type: "string", description: "MCP endpoint URL", default: "http://localhost:3000/mcp" },
|
|
336
|
-
call: { type: "string", description: "Name of a tool to call" },
|
|
337
|
-
args: { type: "string", description: "JSON arguments for --call", default: "{}" }
|
|
338
|
-
},
|
|
339
|
-
async run({ args }) {
|
|
340
|
-
const { Client } = await import("@modelcontextprotocol/sdk/client/index.js");
|
|
341
|
-
const { StreamableHTTPClientTransport } = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
342
|
-
const client = new Client({ name: "apex-mcp-cli", version: "0.0.0" });
|
|
343
|
-
try {
|
|
344
|
-
await client.connect(new StreamableHTTPClientTransport(new URL(args.url)));
|
|
345
|
-
} catch (err) {
|
|
346
|
-
console.error(
|
|
347
|
-
`
|
|
348
|
-
Could not reach an MCP server at ${args.url}
|
|
349
|
-
Is \`apex dev\` running? ${err.message}
|
|
350
|
-
`
|
|
351
|
-
);
|
|
352
|
-
process.exit(1);
|
|
353
|
-
}
|
|
354
|
-
if (args.call) {
|
|
355
|
-
const result = await client.callTool({
|
|
356
|
-
name: args.call,
|
|
357
|
-
arguments: JSON.parse(args.args)
|
|
358
|
-
});
|
|
359
|
-
console.log(`
|
|
360
|
-
\x1B[36m${args.call}\x1B[0m(${args.args}) \u2192`);
|
|
361
|
-
for (const part of result.content) {
|
|
362
|
-
console.log(" " + (part.text ?? JSON.stringify(part)));
|
|
363
|
-
}
|
|
364
|
-
console.log();
|
|
365
|
-
} else {
|
|
366
|
-
const { tools } = await client.listTools();
|
|
367
|
-
console.log(`
|
|
368
|
-
\x1B[36mMCP tools\x1B[0m at ${args.url} (${tools.length})
|
|
369
|
-
`);
|
|
370
|
-
for (const t of tools) {
|
|
371
|
-
const props = t.inputSchema?.properties;
|
|
372
|
-
const sig = props ? Object.entries(props).map(([k, v]) => `${k}: ${v.type ?? "any"}`).join(", ") : "";
|
|
373
|
-
console.log(` \u2022 \x1B[1m${t.name}\x1B[0m(${sig})`);
|
|
374
|
-
if (t.description) console.log(` ${t.description}`);
|
|
375
|
-
}
|
|
376
|
-
console.log(`
|
|
377
|
-
Call one: apex mcp --call <name> --args '{...}'
|
|
378
|
-
`);
|
|
379
|
-
}
|
|
380
|
-
await client.close();
|
|
381
|
-
}
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
// src/commands/migrate.ts
|
|
385
|
-
import { createRequire } from "module";
|
|
386
|
-
import { join as join5, resolve as resolve3 } from "path";
|
|
387
|
-
import { pathToFileURL } from "url";
|
|
388
|
-
import { defineCommand as defineCommand4 } from "citty";
|
|
389
|
-
var migrateCommand = defineCommand4({
|
|
390
|
-
meta: { name: "migrate", description: "Apply pending SQL migrations (db/migrations/*.sql)" },
|
|
391
|
-
args: {
|
|
392
|
-
db: { type: "string", description: "SQLite file path (libSQL)", default: "data.db" },
|
|
393
|
-
driver: { type: "string", description: "sqlite | postgres | pglite", default: "sqlite" },
|
|
394
|
-
url: { type: "string", description: "Connection URL (postgres) \u2014 overrides --db" },
|
|
395
|
-
dir: { type: "string", description: "Migrations directory", default: "db/migrations" },
|
|
396
|
-
root: { type: "string", description: "Project root", default: "." }
|
|
397
|
-
},
|
|
398
|
-
async run({ args }) {
|
|
399
|
-
const root = resolve3(process.cwd(), args.root);
|
|
400
|
-
let data;
|
|
401
|
-
try {
|
|
402
|
-
const require2 = createRequire(join5(root, "package.json"));
|
|
403
|
-
data = await import(pathToFileURL(require2.resolve("@apex-stack/data")).href);
|
|
404
|
-
} catch {
|
|
405
|
-
console.error("\n @apex-stack/data is not installed in this project. Run: npm i @apex-stack/data\n");
|
|
406
|
-
process.exit(1);
|
|
407
|
-
}
|
|
408
|
-
const config = args.driver === "postgres" ? { driver: "postgres", url: args.url } : args.driver === "pglite" ? { driver: "pglite", dir: args.url } : resolve3(root, args.db);
|
|
409
|
-
const handle = await data.createDb(config);
|
|
410
|
-
const applied = await data.applyMigrations(handle, resolve3(root, args.dir));
|
|
411
|
-
await handle.close();
|
|
412
|
-
console.log(
|
|
413
|
-
applied.length ? `
|
|
414
|
-
\u2713 Applied ${applied.length} migration(s): ${applied.join(", ")}
|
|
415
|
-
` : "\n \u2713 Up to date \u2014 no pending migrations.\n"
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
});
|
|
9
|
+
import { defineCommand as defineCommand2, runMain } from "citty";
|
|
419
10
|
|
|
420
11
|
// src/commands/new.ts
|
|
421
12
|
import { spawn, spawnSync } from "child_process";
|
|
422
|
-
import { cpSync
|
|
423
|
-
import { basename, join
|
|
13
|
+
import { cpSync, existsSync, readdirSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
14
|
+
import { basename, join, resolve } from "path";
|
|
424
15
|
import { fileURLToPath } from "url";
|
|
425
|
-
import { defineCommand
|
|
426
|
-
|
|
427
|
-
// src/ui.ts
|
|
428
|
-
var TTY = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR && process.env.TERM !== "dumb";
|
|
429
|
-
var RESET = "\x1B[0m";
|
|
430
|
-
function truecolor(r, g, b, s) {
|
|
431
|
-
return TTY ? `\x1B[38;2;${r};${g};${b}m${s}${RESET}` : s;
|
|
432
|
-
}
|
|
433
|
-
function style(code, s) {
|
|
434
|
-
return TTY ? `\x1B[${code}m${s}${RESET}` : s;
|
|
435
|
-
}
|
|
436
|
-
var color = {
|
|
437
|
-
cyan: (s) => truecolor(34, 211, 238, s),
|
|
438
|
-
indigo: (s) => truecolor(129, 140, 248, s),
|
|
439
|
-
green: (s) => truecolor(52, 211, 153, s),
|
|
440
|
-
red: (s) => truecolor(248, 113, 113, s),
|
|
441
|
-
gray: (s) => truecolor(154, 166, 196, s),
|
|
442
|
-
bold: (s) => style("1", s),
|
|
443
|
-
dim: (s) => style("2", s)
|
|
444
|
-
};
|
|
445
|
-
var LOGO = [
|
|
446
|
-
" \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557",
|
|
447
|
-
"\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2588\u2588\u2557\u2588\u2588\u2554\u255D",
|
|
448
|
-
"\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u255A\u2588\u2588\u2588\u2554\u255D ",
|
|
449
|
-
"\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2588\u2588\u2557 ",
|
|
450
|
-
"\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2554\u255D \u2588\u2588\u2557",
|
|
451
|
-
"\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D"
|
|
452
|
-
];
|
|
453
|
-
var FROM = [99, 102, 241];
|
|
454
|
-
var TO = [34, 211, 238];
|
|
455
|
-
function banner(subtitle = "The full-stack, AI-native meta-framework for Alpine.js") {
|
|
456
|
-
const width = LOGO[0].length;
|
|
457
|
-
const rows = LOGO.map((line) => {
|
|
458
|
-
if (!TTY) return ` ${line}`;
|
|
459
|
-
let out = " ";
|
|
460
|
-
for (let i = 0; i < line.length; i++) {
|
|
461
|
-
const t = width > 1 ? i / (width - 1) : 0;
|
|
462
|
-
const r = Math.round(FROM[0] + (TO[0] - FROM[0]) * t);
|
|
463
|
-
const g = Math.round(FROM[1] + (TO[1] - FROM[1]) * t);
|
|
464
|
-
const b = Math.round(FROM[2] + (TO[2] - FROM[2]) * t);
|
|
465
|
-
out += `\x1B[38;2;${r};${g};${b}m${line[i]}`;
|
|
466
|
-
}
|
|
467
|
-
return out + RESET;
|
|
468
|
-
});
|
|
469
|
-
return `
|
|
470
|
-
${rows.join("\n")}
|
|
471
|
-
${color.gray(subtitle)}
|
|
472
|
-
`;
|
|
473
|
-
}
|
|
474
|
-
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
475
|
-
function spinner(text) {
|
|
476
|
-
if (!TTY) {
|
|
477
|
-
process.stdout.write(` ${text}
|
|
478
|
-
`);
|
|
479
|
-
return {
|
|
480
|
-
succeed: (t) => console.log(` ${color.green("\u2713")} ${t || text}`),
|
|
481
|
-
fail: (t) => console.log(` ${color.red("\u2717")} ${t || text}`),
|
|
482
|
-
stop: () => {
|
|
483
|
-
}
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
let i = 0;
|
|
487
|
-
process.stdout.write("\x1B[?25l");
|
|
488
|
-
const id = setInterval(() => {
|
|
489
|
-
process.stdout.write(`\r\x1B[2K ${color.cyan(FRAMES[i++ % FRAMES.length])} ${text}`);
|
|
490
|
-
}, 80);
|
|
491
|
-
const end = (symbol, t) => {
|
|
492
|
-
clearInterval(id);
|
|
493
|
-
process.stdout.write(`\r\x1B[2K ${symbol} ${t || text}
|
|
494
|
-
\x1B[?25h`);
|
|
495
|
-
};
|
|
496
|
-
return {
|
|
497
|
-
succeed: (t) => end(color.green("\u2713"), t),
|
|
498
|
-
fail: (t) => end(color.red("\u2717"), t),
|
|
499
|
-
stop: () => {
|
|
500
|
-
clearInterval(id);
|
|
501
|
-
process.stdout.write("\r\x1B[2K\x1B[?25h");
|
|
502
|
-
}
|
|
503
|
-
};
|
|
504
|
-
}
|
|
505
|
-
function ready(rows) {
|
|
506
|
-
const w = Math.max(...rows.map(([l]) => l.length));
|
|
507
|
-
console.log();
|
|
508
|
-
for (const [label, value] of rows) {
|
|
509
|
-
console.log(` ${color.cyan("\u279C")} ${color.bold(label.padEnd(w))} ${color.cyan(value)}`);
|
|
510
|
-
}
|
|
511
|
-
console.log();
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
// src/commands/new.ts
|
|
16
|
+
import { defineCommand } from "citty";
|
|
515
17
|
var TEMPLATE_DIR = fileURLToPath(new URL("../templates/default", import.meta.url));
|
|
516
18
|
function detectPackageManager() {
|
|
517
19
|
const ua = process.env.npm_config_user_agent || "";
|
|
@@ -531,7 +33,7 @@ function installAsync(pm, cwd) {
|
|
|
531
33
|
child.on("error", () => res(false));
|
|
532
34
|
});
|
|
533
35
|
}
|
|
534
|
-
var newCommand =
|
|
36
|
+
var newCommand = defineCommand({
|
|
535
37
|
meta: { name: "new", description: "Scaffold a new Apex JS app" },
|
|
536
38
|
args: {
|
|
537
39
|
dir: { type: "positional", required: false, description: "Target directory", default: "apex-app" },
|
|
@@ -540,21 +42,21 @@ var newCommand = defineCommand5({
|
|
|
540
42
|
},
|
|
541
43
|
async run({ args }) {
|
|
542
44
|
const dir = String(args.dir);
|
|
543
|
-
const target =
|
|
45
|
+
const target = resolve(process.cwd(), dir);
|
|
544
46
|
const name = basename(target);
|
|
545
47
|
process.stdout.write(banner());
|
|
546
48
|
const log = console.log;
|
|
547
|
-
if (
|
|
49
|
+
if (existsSync(target) && readdirSync(target).length > 0) {
|
|
548
50
|
console.error(` ${color.red("\u2717")} Target directory is not empty: ${target}
|
|
549
51
|
`);
|
|
550
52
|
process.exit(1);
|
|
551
53
|
}
|
|
552
|
-
|
|
553
|
-
const gitignore =
|
|
554
|
-
if (
|
|
54
|
+
cpSync(TEMPLATE_DIR, target, { recursive: true });
|
|
55
|
+
const gitignore = join(target, "_gitignore");
|
|
56
|
+
if (existsSync(gitignore)) renameSync(gitignore, join(target, ".gitignore"));
|
|
555
57
|
for (const rel of ["package.json", "README.md"]) {
|
|
556
|
-
const file =
|
|
557
|
-
if (
|
|
58
|
+
const file = join(target, rel);
|
|
59
|
+
if (existsSync(file)) writeFileSync(file, readFileSync(file, "utf8").replaceAll("{{name}}", name));
|
|
558
60
|
}
|
|
559
61
|
log(` ${color.green("\u2713")} Created ${color.bold(dir)}`);
|
|
560
62
|
const pm = detectPackageManager();
|
|
@@ -588,150 +90,7 @@ var newCommand = defineCommand5({
|
|
|
588
90
|
}
|
|
589
91
|
});
|
|
590
92
|
|
|
591
|
-
// src/commands/start.ts
|
|
592
|
-
import { existsSync as existsSync6 } from "fs";
|
|
593
|
-
import { join as join8, resolve as resolve5 } from "path";
|
|
594
|
-
import { defineCommand as defineCommand6 } from "citty";
|
|
595
|
-
|
|
596
|
-
// src/prod/server.ts
|
|
597
|
-
import { createServer as createHttpServer } from "http";
|
|
598
|
-
import { existsSync as existsSync5, readFileSync as readFileSync3, statSync } from "fs";
|
|
599
|
-
import { join as join7 } from "path";
|
|
600
|
-
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
601
|
-
import {
|
|
602
|
-
createApp,
|
|
603
|
-
defineEventHandler,
|
|
604
|
-
getRequestURL,
|
|
605
|
-
setResponseHeader,
|
|
606
|
-
setResponseStatus,
|
|
607
|
-
toNodeListener
|
|
608
|
-
} from "h3";
|
|
609
|
-
var MIME = {
|
|
610
|
-
".js": "text/javascript",
|
|
611
|
-
".mjs": "text/javascript",
|
|
612
|
-
".css": "text/css",
|
|
613
|
-
".html": "text/html",
|
|
614
|
-
".json": "application/json",
|
|
615
|
-
".svg": "image/svg+xml",
|
|
616
|
-
".png": "image/png",
|
|
617
|
-
".jpg": "image/jpeg",
|
|
618
|
-
".ico": "image/x-icon",
|
|
619
|
-
".woff2": "font/woff2"
|
|
620
|
-
};
|
|
621
|
-
async function startProdServer(options) {
|
|
622
|
-
const dir = options.dir;
|
|
623
|
-
const port = options.port ?? 3e3;
|
|
624
|
-
const manifest = JSON.parse(readFileSync3(join7(dir, "apex-manifest.json"), "utf8"));
|
|
625
|
-
const importServer = (relFile) => import(pathToFileURL2(join7(dir, "server", relFile)).href);
|
|
626
|
-
const registry = {};
|
|
627
|
-
let componentCss = "";
|
|
628
|
-
for (const [name, file] of Object.entries(manifest.components)) {
|
|
629
|
-
const mod = await importServer(file);
|
|
630
|
-
registry[name] = { template: mod.template, rootXData: mod.rootXData, scopeId: mod.scopeId };
|
|
631
|
-
if (mod.css) componentCss += `${mod.css}
|
|
632
|
-
`;
|
|
633
|
-
}
|
|
634
|
-
const apiEntries = [];
|
|
635
|
-
for (const { name, serverFile } of manifest.api) {
|
|
636
|
-
const mod = await importServer(serverFile);
|
|
637
|
-
apiEntries.push(...expandApiModule(name, mod.default));
|
|
638
|
-
}
|
|
639
|
-
const serverFileFor = new Map(manifest.routes.map((r) => [r.pageId, r.serverFile]));
|
|
640
|
-
const loadModule = (id) => importServer(serverFileFor.get(id));
|
|
641
|
-
const app = createApp();
|
|
642
|
-
app.use(
|
|
643
|
-
defineEventHandler((event) => {
|
|
644
|
-
if (event.method !== "GET") return;
|
|
645
|
-
const path = decodeURIComponent(getRequestURL(event).pathname);
|
|
646
|
-
if (path === "/" || path.startsWith("/api") || path === "/mcp") return;
|
|
647
|
-
const file = join7(dir, path);
|
|
648
|
-
if (!file.startsWith(dir) || !existsSync5(file) || !statSync(file).isFile()) return;
|
|
649
|
-
const ext = path.slice(path.lastIndexOf("."));
|
|
650
|
-
setResponseHeader(event, "Content-Type", MIME[ext] ?? "application/octet-stream");
|
|
651
|
-
if (path.startsWith("/assets/")) setResponseHeader(event, "Cache-Control", "public, max-age=31536000, immutable");
|
|
652
|
-
return readFileSync3(file);
|
|
653
|
-
})
|
|
654
|
-
);
|
|
655
|
-
if (apiEntries.length) app.use("/api", createApiHandler(apiEntries));
|
|
656
|
-
if (hasMcpRoutes(apiEntries)) app.use("/mcp", createMcpHandler(apiEntries));
|
|
657
|
-
app.use(
|
|
658
|
-
defineEventHandler(async (event) => {
|
|
659
|
-
const url = getRequestURL(event).pathname;
|
|
660
|
-
const matched = matchRoute(manifest.routes, url);
|
|
661
|
-
if (!matched) {
|
|
662
|
-
setResponseStatus(event, 404);
|
|
663
|
-
setResponseHeader(event, "Content-Type", "text/html");
|
|
664
|
-
return `<!DOCTYPE html><h1>404 \u2014 ${url}</h1>`;
|
|
665
|
-
}
|
|
666
|
-
const route = manifest.routes.find((r) => r.pageId === matched.pageId);
|
|
667
|
-
const render = manifest.islands ? renderIslandsPage : renderPage;
|
|
668
|
-
const html = await render({
|
|
669
|
-
loadModule,
|
|
670
|
-
pageId: matched.pageId,
|
|
671
|
-
params: matched.params,
|
|
672
|
-
url,
|
|
673
|
-
registry,
|
|
674
|
-
componentCss,
|
|
675
|
-
clientHref: route?.clientHref
|
|
676
|
-
});
|
|
677
|
-
setResponseHeader(event, "Content-Type", "text/html");
|
|
678
|
-
return html;
|
|
679
|
-
})
|
|
680
|
-
);
|
|
681
|
-
const server = createHttpServer(toNodeListener(app));
|
|
682
|
-
await new Promise((resolve7) => server.listen(port, resolve7));
|
|
683
|
-
return { server, port };
|
|
684
|
-
}
|
|
685
|
-
|
|
686
|
-
// src/commands/start.ts
|
|
687
|
-
var startCommand = defineCommand6({
|
|
688
|
-
meta: { name: "start", description: "Run a production build (from `apex build --server`)" },
|
|
689
|
-
args: {
|
|
690
|
-
dir: { type: "positional", required: false, description: "Build directory", default: "dist" },
|
|
691
|
-
port: { type: "string", description: "Port to listen on", default: "3000" }
|
|
692
|
-
},
|
|
693
|
-
async run({ args }) {
|
|
694
|
-
const dir = resolve5(process.cwd(), args.dir);
|
|
695
|
-
if (!existsSync6(join8(dir, "apex-manifest.json"))) {
|
|
696
|
-
console.error(`
|
|
697
|
-
No build found in ${args.dir}/. Run \`apex build --server\` first.
|
|
698
|
-
`);
|
|
699
|
-
process.exit(1);
|
|
700
|
-
}
|
|
701
|
-
const { port } = await startProdServer({ dir, port: Number(args.port) });
|
|
702
|
-
console.log(`
|
|
703
|
-
\x1B[36mApex JS\x1B[0m production server
|
|
704
|
-
\u2192 http://localhost:${port}
|
|
705
|
-
`);
|
|
706
|
-
}
|
|
707
|
-
});
|
|
708
|
-
|
|
709
93
|
// src/cli.ts
|
|
710
|
-
var dev = defineCommand7({
|
|
711
|
-
meta: { name: "dev", description: "Start the Apex JS development server" },
|
|
712
|
-
args: {
|
|
713
|
-
root: { type: "positional", required: false, description: "Project root", default: "." },
|
|
714
|
-
port: { type: "string", description: "Port to listen on", default: "3000" },
|
|
715
|
-
islands: { type: "boolean", description: "Render in islands mode (static-first)", default: false }
|
|
716
|
-
},
|
|
717
|
-
async run({ args }) {
|
|
718
|
-
const root = resolve6(process.cwd(), args.root);
|
|
719
|
-
const port = Number(args.port);
|
|
720
|
-
process.stdout.write(banner());
|
|
721
|
-
const sp = spinner(`Starting dev server${args.islands ? " (islands mode)" : ""}\u2026`);
|
|
722
|
-
try {
|
|
723
|
-
const { port: actual } = await startDevServer({ root, port, islands: args.islands });
|
|
724
|
-
sp.succeed("Dev server ready");
|
|
725
|
-
ready([
|
|
726
|
-
["Local", `http://localhost:${actual}/`],
|
|
727
|
-
["MCP", `http://localhost:${actual}/mcp`]
|
|
728
|
-
]);
|
|
729
|
-
} catch (err) {
|
|
730
|
-
sp.fail("Failed to start the dev server");
|
|
731
|
-
throw err;
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
});
|
|
735
94
|
var COMMANDS = [
|
|
736
95
|
["new", "Scaffold a new app"],
|
|
737
96
|
["dev", "Start the dev server (SSR + hydrate, API + MCP)"],
|
|
@@ -741,19 +100,19 @@ var COMMANDS = [
|
|
|
741
100
|
["migrate", "Apply pending database migrations"],
|
|
742
101
|
["mcp", "Inspect the MCP server \u2014 list or call tools"]
|
|
743
102
|
];
|
|
744
|
-
var main =
|
|
103
|
+
var main = defineCommand2({
|
|
745
104
|
meta: {
|
|
746
105
|
name: "apex",
|
|
747
106
|
description: "The full-stack meta-framework for Alpine.js"
|
|
748
107
|
},
|
|
749
108
|
subCommands: {
|
|
750
109
|
new: newCommand,
|
|
751
|
-
dev,
|
|
752
|
-
build: buildCommand,
|
|
753
|
-
start: startCommand,
|
|
754
|
-
make: makeCommand,
|
|
755
|
-
migrate: migrateCommand,
|
|
756
|
-
mcp: mcpCommand
|
|
110
|
+
dev: () => import("./dev-HDRJEPPN.js").then((m) => m.devCommand),
|
|
111
|
+
build: () => import("./build-QRHQUUZC.js").then((m) => m.buildCommand),
|
|
112
|
+
start: () => import("./start-VJJXI4W3.js").then((m) => m.startCommand),
|
|
113
|
+
make: () => import("./make-4LINTKZH.js").then((m) => m.makeCommand),
|
|
114
|
+
migrate: () => import("./migrate-NOGFOFV2.js").then((m) => m.migrateCommand),
|
|
115
|
+
mcp: () => import("./mcp-DL4J6JFJ.js").then((m) => m.mcpCommand)
|
|
757
116
|
},
|
|
758
117
|
// Shown for a bare `apex` (no subcommand): the brand banner + a command menu.
|
|
759
118
|
run({ rawArgs }) {
|