bosia 0.6.9 → 0.6.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/package.json +1 -1
- package/src/cli/index.ts +6 -0
- package/src/cli/sync.ts +16 -0
- package/src/core/routeTypes.ts +2 -0
- package/src/core/server.ts +78 -75
- package/src/lib/index.ts +2 -0
- package/templates/default/package.json +2 -1
- package/templates/default/tsconfig.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bosia",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A fast, batteries-included fullstack framework — SSR · Svelte 5 Runes · Bun · ElysiaJS. File-based routing inspired by SvelteKit. No Node.js, no Vite, no adapters.",
|
|
6
6
|
"keywords": [
|
package/src/cli/index.ts
CHANGED
|
@@ -26,6 +26,11 @@ async function main() {
|
|
|
26
26
|
await runBuild();
|
|
27
27
|
break;
|
|
28
28
|
}
|
|
29
|
+
case "sync": {
|
|
30
|
+
const { runSync } = await import("./sync.ts");
|
|
31
|
+
await runSync();
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
29
34
|
case "start": {
|
|
30
35
|
const { runStart } = await import("./start.ts");
|
|
31
36
|
await runStart();
|
|
@@ -80,6 +85,7 @@ Commands:
|
|
|
80
85
|
create <name> [--template <t>] Scaffold a new Bosia project
|
|
81
86
|
dev Start the development server
|
|
82
87
|
build Build for production
|
|
88
|
+
sync Generate .bosia/ codegen (routes, $types, env) without building
|
|
83
89
|
start Run the production server
|
|
84
90
|
test [args] Run tests with bun test (auto-loads .env.test, sets BOSIA_ENV=test)
|
|
85
91
|
add <component...> [-y] Add one or more UI components from the registry
|
package/src/cli/sync.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { scanRoutes } from "../core/scanner.ts";
|
|
2
|
+
import { generateRoutesFile } from "../core/routeFile.ts";
|
|
3
|
+
import { generateRouteTypes, ensureRootDirs } from "../core/routeTypes.ts";
|
|
4
|
+
import { loadEnv, classifyEnvVars } from "../core/env.ts";
|
|
5
|
+
import { generateEnvModules } from "../core/envCodegen.ts";
|
|
6
|
+
|
|
7
|
+
export async function runSync() {
|
|
8
|
+
const envMode = process.env.NODE_ENV === "production" ? "production" : "development";
|
|
9
|
+
const classifiedEnv = classifyEnvVars(loadEnv(envMode));
|
|
10
|
+
const manifest = scanRoutes();
|
|
11
|
+
generateRoutesFile(manifest);
|
|
12
|
+
generateRouteTypes(manifest);
|
|
13
|
+
ensureRootDirs();
|
|
14
|
+
generateEnvModules(classifiedEnv);
|
|
15
|
+
console.log("✅ Bosia codegen ready (.bosia/routes.ts, types, env modules)");
|
|
16
|
+
}
|
package/src/core/routeTypes.ts
CHANGED
|
@@ -100,6 +100,7 @@ export function generateRouteTypes(manifest: RouteManifest): void {
|
|
|
100
100
|
`export type PageMetadataLoad = (event: _MetadataEvent) => Metadata | Promise<Metadata>;`,
|
|
101
101
|
);
|
|
102
102
|
lines.push(`export type Action = (event: _RequestEvent) => any;`);
|
|
103
|
+
lines.push(`export type Actions = Record<string, Action>;`);
|
|
103
104
|
lines.push(`export type PageData = Awaited<ReturnType<typeof _pageLoad>>;`);
|
|
104
105
|
} else {
|
|
105
106
|
lines.push(``);
|
|
@@ -107,6 +108,7 @@ export function generateRouteTypes(manifest: RouteManifest): void {
|
|
|
107
108
|
`export type PageMetadataLoad = (event: _MetadataEvent) => Metadata | Promise<Metadata>;`,
|
|
108
109
|
);
|
|
109
110
|
lines.push(`export type Action = (event: _RequestEvent) => any;`);
|
|
111
|
+
lines.push(`export type Actions = Record<string, Action>;`);
|
|
110
112
|
lines.push(`export type PageData = {};`);
|
|
111
113
|
}
|
|
112
114
|
lines.push(`export type PageProps = { data: PageData; params: Params };`);
|
package/src/core/server.ts
CHANGED
|
@@ -332,82 +332,10 @@ async function resolve(event: RequestEvent): Promise<Response> {
|
|
|
332
332
|
}
|
|
333
333
|
}
|
|
334
334
|
|
|
335
|
-
// Static files
|
|
336
|
-
if (isStaticPath(path)) {
|
|
337
|
-
// Prod fast path: single Map lookup, no per-request stat calls.
|
|
338
|
-
if (staticManifest) {
|
|
339
|
-
const hit = lookupStatic(staticManifest, path);
|
|
340
|
-
if (hit) {
|
|
341
|
-
return new Response(
|
|
342
|
-
Bun.file(hit.absPath),
|
|
343
|
-
hit.cacheControl
|
|
344
|
-
? { headers: { "Cache-Control": hit.cacheControl } }
|
|
345
|
-
: undefined,
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
return new Response("Not Found", { status: 404 });
|
|
349
|
-
}
|
|
350
|
-
// Dev: keep the per-request fallthrough so files dropped into `public/`
|
|
351
|
-
// mid-session are served without a restart.
|
|
352
|
-
if (path.startsWith("/dist/client/")) {
|
|
353
|
-
const resolved = safePath(
|
|
354
|
-
`${OUT_DIR}/client`,
|
|
355
|
-
path.split("?")[0].slice("/dist/client".length),
|
|
356
|
-
);
|
|
357
|
-
if (resolved) {
|
|
358
|
-
const file = Bun.file(resolved);
|
|
359
|
-
if (await file.exists()) {
|
|
360
|
-
return new Response(file, { headers: { "Cache-Control": "no-cache" } });
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
return new Response("Not Found", { status: 404 });
|
|
364
|
-
}
|
|
365
|
-
const pubPath = safePath("./public", path);
|
|
366
|
-
if (pubPath) {
|
|
367
|
-
const pub = Bun.file(pubPath);
|
|
368
|
-
if (await pub.exists()) return new Response(pub);
|
|
369
|
-
}
|
|
370
|
-
const distPath = safePath(OUT_DIR, path);
|
|
371
|
-
if (distPath) {
|
|
372
|
-
const dist = Bun.file(distPath);
|
|
373
|
-
if (await dist.exists()) return new Response(dist);
|
|
374
|
-
}
|
|
375
|
-
const staticPath = safePath(`${OUT_DIR}/static`, path);
|
|
376
|
-
if (staticPath) {
|
|
377
|
-
const staticFile = Bun.file(staticPath);
|
|
378
|
-
if (await staticFile.exists()) return new Response(staticFile);
|
|
379
|
-
}
|
|
380
|
-
return new Response("Not Found", { status: 404 });
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// Prerendered pages — serve static HTML built at build time.
|
|
384
|
-
// SKIP in dev: prerender runs with NODE_ENV=production, which disables the
|
|
385
|
-
// inspector plugin and the dev-only error pipeline. Serving its output back
|
|
386
|
-
// in dev would mask errors (the badge stays empty, the SSE reload script
|
|
387
|
-
// isn't injected, and the page can't auto-recover when the source is fixed).
|
|
388
|
-
// Live SSR every request in dev so /about behaves like every other route.
|
|
389
|
-
if (!isDev) {
|
|
390
|
-
// Try both `<path>/index.html` (always/ignore mode) and `<path>.html` (never mode)
|
|
391
|
-
const prerenderCandidates =
|
|
392
|
-
path === "/"
|
|
393
|
-
? ["index.html"]
|
|
394
|
-
: [`${path}/index.html`, `${path.replace(/\/$/, "")}.html`];
|
|
395
|
-
for (const candidate of prerenderCandidates) {
|
|
396
|
-
const prerenderPath = safePath(`${OUT_DIR}/prerendered`, candidate);
|
|
397
|
-
if (!prerenderPath) continue;
|
|
398
|
-
const prerenderFile = Bun.file(prerenderPath);
|
|
399
|
-
if (await prerenderFile.exists()) {
|
|
400
|
-
return new Response(prerenderFile, {
|
|
401
|
-
headers: {
|
|
402
|
-
"Content-Type": "text/html; charset=utf-8",
|
|
403
|
-
"Cache-Control": "public, max-age=3600",
|
|
404
|
-
},
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
335
|
// API routes (+server.ts) — resolve with `.json` alias preference.
|
|
336
|
+
// Matched BEFORE static fallthrough so explicit handlers shadow extension-
|
|
337
|
+
// based static detection (e.g. `/uploads/[...path]/+server.ts` can serve
|
|
338
|
+
// `.webp` URLs that would otherwise be intercepted by isStaticPath).
|
|
411
339
|
const apiMatch = await resolveApiMatch(apiRoutes, path);
|
|
412
340
|
if (apiMatch) {
|
|
413
341
|
try {
|
|
@@ -514,6 +442,81 @@ async function resolve(event: RequestEvent): Promise<Response> {
|
|
|
514
442
|
}
|
|
515
443
|
}
|
|
516
444
|
|
|
445
|
+
// Static files — fallthrough after API routes so explicit handlers win.
|
|
446
|
+
if (isStaticPath(path)) {
|
|
447
|
+
// Prod fast path: single Map lookup, no per-request stat calls.
|
|
448
|
+
if (staticManifest) {
|
|
449
|
+
const hit = lookupStatic(staticManifest, path);
|
|
450
|
+
if (hit) {
|
|
451
|
+
return new Response(
|
|
452
|
+
Bun.file(hit.absPath),
|
|
453
|
+
hit.cacheControl
|
|
454
|
+
? { headers: { "Cache-Control": hit.cacheControl } }
|
|
455
|
+
: undefined,
|
|
456
|
+
);
|
|
457
|
+
}
|
|
458
|
+
return new Response("Not Found", { status: 404 });
|
|
459
|
+
}
|
|
460
|
+
// Dev: keep the per-request fallthrough so files dropped into `public/`
|
|
461
|
+
// mid-session are served without a restart.
|
|
462
|
+
if (path.startsWith("/dist/client/")) {
|
|
463
|
+
const resolved = safePath(
|
|
464
|
+
`${OUT_DIR}/client`,
|
|
465
|
+
path.split("?")[0].slice("/dist/client".length),
|
|
466
|
+
);
|
|
467
|
+
if (resolved) {
|
|
468
|
+
const file = Bun.file(resolved);
|
|
469
|
+
if (await file.exists()) {
|
|
470
|
+
return new Response(file, { headers: { "Cache-Control": "no-cache" } });
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
return new Response("Not Found", { status: 404 });
|
|
474
|
+
}
|
|
475
|
+
const pubPath = safePath("./public", path);
|
|
476
|
+
if (pubPath) {
|
|
477
|
+
const pub = Bun.file(pubPath);
|
|
478
|
+
if (await pub.exists()) return new Response(pub);
|
|
479
|
+
}
|
|
480
|
+
const distPath = safePath(OUT_DIR, path);
|
|
481
|
+
if (distPath) {
|
|
482
|
+
const dist = Bun.file(distPath);
|
|
483
|
+
if (await dist.exists()) return new Response(dist);
|
|
484
|
+
}
|
|
485
|
+
const staticPath = safePath(`${OUT_DIR}/static`, path);
|
|
486
|
+
if (staticPath) {
|
|
487
|
+
const staticFile = Bun.file(staticPath);
|
|
488
|
+
if (await staticFile.exists()) return new Response(staticFile);
|
|
489
|
+
}
|
|
490
|
+
return new Response("Not Found", { status: 404 });
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Prerendered pages — serve static HTML built at build time.
|
|
494
|
+
// SKIP in dev: prerender runs with NODE_ENV=production, which disables the
|
|
495
|
+
// inspector plugin and the dev-only error pipeline. Serving its output back
|
|
496
|
+
// in dev would mask errors (the badge stays empty, the SSE reload script
|
|
497
|
+
// isn't injected, and the page can't auto-recover when the source is fixed).
|
|
498
|
+
// Live SSR every request in dev so /about behaves like every other route.
|
|
499
|
+
if (!isDev) {
|
|
500
|
+
// Try both `<path>/index.html` (always/ignore mode) and `<path>.html` (never mode)
|
|
501
|
+
const prerenderCandidates =
|
|
502
|
+
path === "/"
|
|
503
|
+
? ["index.html"]
|
|
504
|
+
: [`${path}/index.html`, `${path.replace(/\/$/, "")}.html`];
|
|
505
|
+
for (const candidate of prerenderCandidates) {
|
|
506
|
+
const prerenderPath = safePath(`${OUT_DIR}/prerendered`, candidate);
|
|
507
|
+
if (!prerenderPath) continue;
|
|
508
|
+
const prerenderFile = Bun.file(prerenderPath);
|
|
509
|
+
if (await prerenderFile.exists()) {
|
|
510
|
+
return new Response(prerenderFile, {
|
|
511
|
+
headers: {
|
|
512
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
513
|
+
"Cache-Control": "public, max-age=3600",
|
|
514
|
+
},
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
517
520
|
// Resolve the page route once; reuse for trailing-slash, form-action, and SSR phases.
|
|
518
521
|
const pageMatch = findMatch(serverRoutes, path);
|
|
519
522
|
|
package/src/lib/index.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"dev": "bosia dev",
|
|
7
7
|
"build": "bosia build",
|
|
8
8
|
"start": "bosia start",
|
|
9
|
-
"check": "
|
|
9
|
+
"check": "bosia sync && svelte-check --tsconfig ./tsconfig.json && tsc --noEmit",
|
|
10
10
|
"format": "prettier --write .",
|
|
11
11
|
"format:check": "prettier --check ."
|
|
12
12
|
},
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"@types/bun": "latest",
|
|
20
20
|
"prettier": "^3.3.0",
|
|
21
21
|
"prettier-plugin-svelte": "^3.2.0",
|
|
22
|
+
"svelte-check": "^4.4.8",
|
|
22
23
|
"typescript": "^5"
|
|
23
24
|
}
|
|
24
25
|
}
|