@apex-stack/core 0.6.0 → 0.7.1

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.
@@ -1,321 +0,0 @@
1
- import {
2
- loadComponents
3
- } from "./chunk-4VG3CZ6H.js";
4
- import {
5
- createApiHandler,
6
- createMcpHandler,
7
- loadApiRoutes,
8
- loadMiddleware,
9
- runMiddleware
10
- } from "./chunk-HCNNKT4A.js";
11
- import "./chunk-2C2HRLIY.js";
12
- import {
13
- loadStores,
14
- matchRoute,
15
- renderIslandsPage,
16
- renderPage,
17
- resolveApexConfig,
18
- scanPages
19
- } from "./chunk-DVNFDYEO.js";
20
- import "./chunk-PMLGY6Z3.js";
21
-
22
- // src/dev/server.ts
23
- import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2 } from "fs";
24
- import { createServer as createHttpServer } from "http";
25
- import { createRequire } from "module";
26
- import { join } from "path";
27
- import { fileURLToPath, pathToFileURL } from "url";
28
- import { apex } from "@apex-stack/vite";
29
- import {
30
- createApp,
31
- defineEventHandler,
32
- fromNodeMiddleware,
33
- getRequestHeaders,
34
- setResponseHeader,
35
- setResponseStatus,
36
- toNodeListener
37
- } from "h3";
38
- import { createServer as createViteServer } from "vite";
39
-
40
- // src/dev/errorPage.ts
41
- import { existsSync, readFileSync } from "fs";
42
- function esc(s) {
43
- return s.replace(
44
- /[&<>"]/g,
45
- (c) => c === "&" ? "&amp;" : c === "<" ? "&lt;" : c === ">" ? "&gt;" : "&quot;"
46
- );
47
- }
48
- function firstFileFrame(stack, root) {
49
- const re = /(?:file:\/\/\/?)?((?:[A-Za-z]:[\\/]|\/)[^\s():]+):(\d+):(\d+)/g;
50
- const frames = [];
51
- let m = re.exec(stack);
52
- while (m) {
53
- const raw = m[1];
54
- if (raw && (/^[A-Za-z]:[\\/]/.test(raw) || raw.startsWith("/"))) {
55
- const file = raw.replace(/\//g, process.platform === "win32" ? "\\" : "/");
56
- if (existsSync(file) && !file.includes("node_modules")) {
57
- frames.push({ file, line: Number(m[2] ?? 0), col: Number(m[3] ?? 0) });
58
- }
59
- }
60
- m = re.exec(stack);
61
- }
62
- return frames.find((f) => f.file.startsWith(root)) ?? frames[0];
63
- }
64
- function codeFrame(frame) {
65
- let lines;
66
- try {
67
- lines = readFileSync(frame.file, "utf8").split("\n");
68
- } catch {
69
- return "";
70
- }
71
- const start = Math.max(0, frame.line - 4);
72
- const end = Math.min(lines.length, frame.line + 3);
73
- const rows = [];
74
- for (let i = start; i < end; i++) {
75
- const n = i + 1;
76
- const active = n === frame.line;
77
- const num = String(n).padStart(4, " ");
78
- rows.push(
79
- `<div class="cf-row${active ? " active" : ""}"><span class="cf-num">${num}</span><span class="cf-code">${esc(lines[i] || "")}</span></div>`
80
- );
81
- }
82
- return `<div class="frame"><div class="frame-file">${esc(frame.file)}<span class="frame-pos">:${frame.line}:${frame.col}</span></div><pre class="cf">${rows.join("")}</pre></div>`;
83
- }
84
- var SHELL_CSS = `
85
- :root{color-scheme:dark light}
86
- *{box-sizing:border-box}
87
- body{margin:0;min-height:100vh;background:#0a0e1a;color:#f4f7ff;
88
- font:15px/1.6 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
89
- display:flex;align-items:flex-start;justify-content:center;padding:clamp(1.5rem,6vh,5rem) 1.25rem}
90
- @media(prefers-color-scheme:light){body{background:#f4f7ff;color:#0a0e1a}}
91
- .wrap{width:100%;max-width:860px}
92
- .brand{display:flex;align-items:center;gap:.55rem;font-weight:600;letter-spacing:-.01em;margin-bottom:1.6rem}
93
- .mark{width:20px;height:20px}
94
- .pill{font-size:.8rem;font-weight:600;padding:.15rem .6rem;border-radius:999px;margin-left:.4rem}
95
- .pill.err{color:#fb7185;background:rgba(244,63,94,.12);border:1px solid rgba(244,63,94,.35)}
96
- .pill.nf{color:#818cf8;background:rgba(129,140,248,.12);border:1px solid rgba(129,140,248,.35)}
97
- .kind{font:600 .95rem/1 ui-monospace,SFMono-Regular,Menlo,monospace;color:#fb7185;margin:0 0 .5rem}
98
- .kind.nf{color:#818cf8}
99
- h1{font-size:clamp(1.4rem,3.2vw,2rem);line-height:1.2;letter-spacing:-.02em;margin:0 0 .7rem;word-break:break-word}
100
- .sub{color:#9aa6c4;margin:0 0 1.8rem}
101
- .sub code{background:rgba(130,140,200,.14);border-radius:.35rem;padding:.05rem .35rem;font-size:.9em}
102
- .frame{border:1px solid rgba(130,140,200,.18);border-radius:12px;overflow:hidden;margin:0 0 1.4rem;background:#11172b}
103
- @media(prefers-color-scheme:light){.frame{background:#fff}}
104
- .frame-file{font:.8rem/1 ui-monospace,SFMono-Regular,Menlo,monospace;color:#9aa6c4;padding:.7rem .9rem;border-bottom:1px solid rgba(130,140,200,.18);overflow-x:auto;white-space:nowrap}
105
- .frame-pos{color:#fb7185}
106
- .cf{margin:0;padding:.6rem 0;overflow-x:auto;font:.82rem/1.7 ui-monospace,SFMono-Regular,Menlo,monospace}
107
- .cf-row{display:flex;padding:0 .9rem;white-space:pre}
108
- .cf-row.active{background:rgba(244,63,94,.12);box-shadow:inset 3px 0 0 #f43f5e}
109
- .cf-num{color:#5b6890;-webkit-user-select:none;user-select:none;padding-right:1.1rem;text-align:right}
110
- .cf-row.active .cf-num{color:#fb7185}
111
- .cf-code{color:#f4f7ff}
112
- @media(prefers-color-scheme:light){.cf-code{color:#0a0e1a}}
113
- details{border:1px solid rgba(130,140,200,.18);border-radius:12px;background:#11172b;overflow:hidden}
114
- @media(prefers-color-scheme:light){details{background:#fff}}
115
- summary{cursor:pointer;padding:.7rem .9rem;font-size:.85rem;color:#9aa6c4;user-select:none}
116
- summary:hover{color:#f4f7ff}
117
- @media(prefers-color-scheme:light){summary:hover{color:#0a0e1a}}
118
- .stack{margin:0;padding:0 .9rem 1rem;font:.78rem/1.7 ui-monospace,SFMono-Regular,Menlo,monospace;color:#9aa6c4;overflow-x:auto;white-space:pre-wrap;word-break:break-word}
119
- .routes{list-style:none;padding:0;margin:0 0 1.4rem;display:flex;flex-wrap:wrap;gap:.5rem}
120
- .routes li{font:.85rem/1 ui-monospace,SFMono-Regular,Menlo,monospace;color:#818cf8;background:rgba(129,140,248,.1);border:1px solid rgba(129,140,248,.25);border-radius:.45rem;padding:.35rem .6rem}
121
- .foot{margin-top:1.8rem;color:#5b6890;font-size:.82rem}
122
- `;
123
- var MARK = `<svg class="mark" viewBox="0 0 64 64" aria-hidden="true"><defs><linearGradient id="g" x1="0" y1="1" x2="1" y2="0"><stop offset="0" stop-color="#22d3ee"/><stop offset="1" stop-color="#6366f1"/></linearGradient></defs><path d="M32 7 L32 35 L20 56 L4 56 Z" fill="url(#g)"/><path d="M32 7 L60 56 L44 56 L32 35 Z" fill="#6366f1"/></svg>`;
124
- function page(title, bodyInner) {
125
- return `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/><title>${esc(title)}</title><style>${SHELL_CSS}</style></head><body><div class="wrap">${bodyInner}</div></body></html>`;
126
- }
127
- function renderErrorPage(error, opts) {
128
- const kind = error.name || "Error";
129
- const message = error.message || String(error);
130
- const stack = error.stack || "";
131
- const frame = stack ? firstFileFrame(stack, opts.root) : void 0;
132
- const frameHtml = frame ? codeFrame(frame) : "";
133
- const body = `
134
- <div class="brand">${MARK} Apex JS <span class="pill err">Dev error</span></div>
135
- <p class="kind">${esc(kind)}</p>
136
- <h1>${esc(message)}</h1>
137
- <p class="sub">While rendering <code>${esc(opts.url)}</code></p>
138
- ${frameHtml}
139
- <details${frameHtml ? "" : " open"}><summary>Stack trace</summary><pre class="stack">${esc(stack)}</pre></details>
140
- <p class="foot">Fix the error and save \u2014 this page reloads automatically.</p>`;
141
- return page(`${kind} \u2014 Apex JS`, body);
142
- }
143
- function renderNotFoundPage(url, routes) {
144
- const list = routes.length ? routes.map((r) => `<li>${esc(r.pattern)}</li>`).join("") : "<li>no pages yet \u2014 add pages/index.alpine</li>";
145
- const body = `
146
- <div class="brand">${MARK} Apex JS <span class="pill nf">404</span></div>
147
- <p class="kind nf">404 \u2014 Not found</p>
148
- <h1>No route matches <code>${esc(url)}</code></h1>
149
- <p class="sub">These routes are available:</p>
150
- <ul class="routes">${list}</ul>
151
- <p class="foot">Add a file under <code>pages/</code> to create a new route.</p>`;
152
- return page("404 \u2014 Apex JS", body);
153
- }
154
-
155
- // src/dev/server.ts
156
- async function startDevServer(options) {
157
- const port = options.port ?? 3e3;
158
- const pageId = options.pageId ?? "/pages/index.alpine";
159
- const req = createRequire(import.meta.url);
160
- const tryResolve = (spec) => {
161
- try {
162
- return req.resolve(spec);
163
- } catch {
164
- return void 0;
165
- }
166
- };
167
- const alpine = tryResolve("alpinejs");
168
- const kit = tryResolve("@apex-stack/kit");
169
- const coreClient = fileURLToPath(new URL("./client.js", import.meta.url));
170
- const coreSelf = fileURLToPath(new URL("./index.js", import.meta.url));
171
- const alias = {
172
- "@apex-stack/core/client": coreClient,
173
- "@apex-stack/core": coreSelf
174
- };
175
- if (alpine) alias.alpinejs = alpine;
176
- if (kit) alias["@apex-stack/kit"] = kit;
177
- const plugins = [apex({ clientRuntime: "@apex-stack/core/client" })];
178
- let hasTailwind = false;
179
- try {
180
- const reqProj = createRequire(join(options.root, "package.json"));
181
- const twMod = await import(pathToFileURL(reqProj.resolve("@tailwindcss/vite")).href);
182
- const tw = twMod.default ?? twMod;
183
- plugins.unshift(tw());
184
- hasTailwind = true;
185
- } catch {
186
- }
187
- const appCssRel = ["app.css", "styles/app.css", "src/app.css"].find(
188
- (p) => existsSync2(join(options.root, p))
189
- );
190
- let appCss = appCssRel ? `/${appCssRel}` : void 0;
191
- if (appCssRel && !hasTailwind) {
192
- const css = readFileSync2(join(options.root, appCssRel), "utf8");
193
- if (/@import\s+['"]tailwindcss['"]|@tailwind\b/.test(css)) {
194
- console.warn(
195
- `
196
- \u26A0 ${appCssRel} imports Tailwind, but it isn't installed \u2014 skipping it (styles won't apply).
197
- Fix: npm i -D tailwindcss @tailwindcss/vite
198
- `
199
- );
200
- appCss = void 0;
201
- }
202
- }
203
- const vite = await createViteServer({
204
- root: options.root,
205
- appType: "custom",
206
- // Derive the HMR port from the dev port so multiple `apex dev` instances
207
- // don't all fight over Vite's default 24678.
208
- server: { middlewareMode: true, fs: { strict: false }, hmr: { port: port + 1 } },
209
- resolve: { alias },
210
- // User apps depend on `@apex-stack/core`, so the client module imports the runtime
211
- // from `@apex-stack/core/client` (a re-export) rather than the internal kit package.
212
- plugins,
213
- optimizeDeps: { include: ["alpinejs"] }
214
- });
215
- const ssrLoad = (id) => {
216
- const resolved = id[0] === "/" && !id.startsWith(options.root) ? join(options.root, id).replace(/\\/g, "/") : id;
217
- return vite.ssrLoadModule(resolved);
218
- };
219
- const { runtimeConfig, publicConfig } = await resolveApexConfig(
220
- options.root,
221
- (id) => ssrLoad(id)
222
- );
223
- const app = createApp();
224
- app.use(fromNodeMiddleware(vite.middlewares));
225
- app.use(
226
- defineEventHandler(async (event) => {
227
- const mws = await loadMiddleware(options.root, (id) => ssrLoad(id));
228
- if (!mws.length) return;
229
- const { redirect, locals } = await runMiddleware(mws, {
230
- url: event.path || "/",
231
- method: event.method,
232
- config: runtimeConfig,
233
- headers: getRequestHeaders(event)
234
- });
235
- event.context.apexLocals = locals;
236
- if (redirect) {
237
- setResponseStatus(event, redirect.status);
238
- setResponseHeader(event, "Location", redirect.to);
239
- return "";
240
- }
241
- })
242
- );
243
- const loadEntries = () => loadApiRoutes(options.root, (id) => ssrLoad(id));
244
- app.use(
245
- "/api",
246
- defineEventHandler(
247
- (event) => loadEntries().then((e) => createApiHandler(e, runtimeConfig)(event))
248
- )
249
- );
250
- app.use(
251
- "/mcp",
252
- defineEventHandler(
253
- (event) => loadEntries().then((e) => createMcpHandler(e, runtimeConfig)(event))
254
- )
255
- );
256
- app.use(
257
- defineEventHandler(async (event) => {
258
- const url = event.path || "/";
259
- try {
260
- const routes = scanPages(options.root);
261
- const matched = routes.length ? matchRoute(routes, url) : { pageId, params: {} };
262
- if (!matched) {
263
- setResponseStatus(event, 404);
264
- setResponseHeader(event, "Content-Type", "text/html");
265
- return await vite.transformIndexHtml(url, renderNotFoundPage(url, routes));
266
- }
267
- const { registry, css: componentCss } = await loadComponents(
268
- options.root,
269
- (id) => ssrLoad(id)
270
- );
271
- const stores = await loadStores(options.root, (id) => ssrLoad(id));
272
- const layoutsDir = join(options.root, "layouts");
273
- const layouts = existsSync2(layoutsDir) ? readdirSync(layoutsDir).filter((f) => f.endsWith(".alpine")).map((f) => f.replace(/\.alpine$/, "")) : [];
274
- const render = options.islands ? renderIslandsPage : renderPage;
275
- const html = await render({
276
- loadModule: (id) => ssrLoad(id),
277
- pageId: matched.pageId,
278
- params: matched.params,
279
- url,
280
- registry,
281
- componentCss,
282
- stores,
283
- appCss,
284
- layouts,
285
- runtimeConfig,
286
- publicConfig,
287
- locals: event.context.apexLocals ?? {},
288
- errorPageId: existsSync2(join(options.root, "pages", "error.alpine")) ? "/pages/error.alpine" : void 0,
289
- transformHtml: (u, doc) => vite.transformIndexHtml(u, doc)
290
- });
291
- setResponseHeader(event, "Content-Type", "text/html");
292
- return html;
293
- } catch (err) {
294
- const error = err;
295
- vite.ssrFixStacktrace(error);
296
- setResponseStatus(event, 500);
297
- setResponseHeader(event, "Content-Type", "text/html");
298
- const html = renderErrorPage(error, { url, root: options.root });
299
- try {
300
- return await vite.transformIndexHtml(url, html);
301
- } catch {
302
- return html;
303
- }
304
- }
305
- })
306
- );
307
- const server = createHttpServer(toNodeListener(app));
308
- await new Promise((resolve) => server.listen(port, resolve));
309
- return {
310
- vite,
311
- server,
312
- port,
313
- close: async () => {
314
- await vite.close();
315
- await new Promise((resolve, reject) => server.close((e) => e ? reject(e) : resolve()));
316
- }
317
- };
318
- }
319
- export {
320
- startDevServer
321
- };