fibrae 0.2.3 → 0.3.0

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.
Files changed (120) hide show
  1. package/dist/cli/build.d.ts +34 -0
  2. package/dist/cli/build.js +92 -0
  3. package/dist/cli/build.js.map +1 -0
  4. package/dist/cli/cli.d.ts +10 -0
  5. package/dist/cli/cli.js +43 -0
  6. package/dist/cli/cli.js.map +1 -0
  7. package/dist/cli/config.d.ts +19 -0
  8. package/dist/cli/config.js +5 -0
  9. package/dist/cli/config.js.map +1 -0
  10. package/dist/cli/html.d.ts +13 -0
  11. package/dist/cli/html.js +101 -0
  12. package/dist/cli/html.js.map +1 -0
  13. package/dist/cli/index.d.ts +6 -0
  14. package/dist/cli/index.js +4 -0
  15. package/dist/cli/index.js.map +1 -0
  16. package/dist/cli/vite-plugin.d.ts +9 -0
  17. package/dist/cli/vite-plugin.js +143 -0
  18. package/dist/cli/vite-plugin.js.map +1 -0
  19. package/dist/components.d.ts +27 -29
  20. package/dist/components.js +32 -52
  21. package/dist/components.js.map +1 -1
  22. package/dist/core.js +7 -10
  23. package/dist/core.js.map +1 -1
  24. package/dist/dom.d.ts +13 -4
  25. package/dist/dom.js +91 -26
  26. package/dist/dom.js.map +1 -1
  27. package/dist/fiber-boundary.d.ts +39 -0
  28. package/dist/fiber-boundary.js +151 -0
  29. package/dist/fiber-boundary.js.map +1 -0
  30. package/dist/fiber-commit.d.ts +27 -0
  31. package/dist/fiber-commit.js +243 -0
  32. package/dist/fiber-commit.js.map +1 -0
  33. package/dist/fiber-render.d.ts +9 -9
  34. package/dist/fiber-render.js +165 -958
  35. package/dist/fiber-render.js.map +1 -1
  36. package/dist/fiber-tree.d.ts +77 -0
  37. package/dist/fiber-tree.js +152 -0
  38. package/dist/fiber-tree.js.map +1 -0
  39. package/dist/fiber-update.d.ts +46 -0
  40. package/dist/fiber-update.js +515 -0
  41. package/dist/fiber-update.js.map +1 -0
  42. package/dist/h.js.map +1 -1
  43. package/dist/index.d.ts +1 -1
  44. package/dist/index.js +1 -1
  45. package/dist/index.js.map +1 -1
  46. package/dist/jsx-runtime/index.d.ts +253 -2
  47. package/dist/live/atom.d.ts +31 -0
  48. package/dist/live/atom.js +33 -0
  49. package/dist/live/atom.js.map +1 -0
  50. package/dist/live/client.d.ts +50 -0
  51. package/dist/live/client.js +90 -0
  52. package/dist/live/client.js.map +1 -0
  53. package/dist/live/codec.d.ts +39 -0
  54. package/dist/live/codec.js +41 -0
  55. package/dist/live/codec.js.map +1 -0
  56. package/dist/live/config.d.ts +13 -0
  57. package/dist/live/config.js +11 -0
  58. package/dist/live/config.js.map +1 -0
  59. package/dist/live/index.d.ts +25 -0
  60. package/dist/live/index.js +19 -0
  61. package/dist/live/index.js.map +1 -0
  62. package/dist/live/server.d.ts +83 -0
  63. package/dist/live/server.js +106 -0
  64. package/dist/live/server.js.map +1 -0
  65. package/dist/live/sse-stream.d.ts +14 -0
  66. package/dist/live/sse-stream.js +30 -0
  67. package/dist/live/sse-stream.js.map +1 -0
  68. package/dist/live/types.d.ts +40 -0
  69. package/dist/live/types.js +20 -0
  70. package/dist/live/types.js.map +1 -0
  71. package/dist/router/History.d.ts +2 -7
  72. package/dist/router/History.js +0 -8
  73. package/dist/router/History.js.map +1 -1
  74. package/dist/router/Link.d.ts +8 -4
  75. package/dist/router/Link.js +13 -34
  76. package/dist/router/Link.js.map +1 -1
  77. package/dist/router/Navigator.d.ts +12 -1
  78. package/dist/router/Navigator.js +31 -68
  79. package/dist/router/Navigator.js.map +1 -1
  80. package/dist/router/Route.d.ts +16 -3
  81. package/dist/router/Route.js +32 -25
  82. package/dist/router/Route.js.map +1 -1
  83. package/dist/router/Router.d.ts +27 -19
  84. package/dist/router/Router.js +78 -101
  85. package/dist/router/Router.js.map +1 -1
  86. package/dist/router/RouterBuilder.d.ts +106 -34
  87. package/dist/router/RouterBuilder.js +78 -39
  88. package/dist/router/RouterBuilder.js.map +1 -1
  89. package/dist/router/RouterOutlet.d.ts +1 -18
  90. package/dist/router/RouterOutlet.js +25 -8
  91. package/dist/router/RouterOutlet.js.map +1 -1
  92. package/dist/router/RouterState.d.ts +1 -1
  93. package/dist/router/index.d.ts +5 -5
  94. package/dist/router/index.js +5 -3
  95. package/dist/router/index.js.map +1 -1
  96. package/dist/router/utils.d.ts +36 -0
  97. package/dist/router/utils.js +48 -0
  98. package/dist/router/utils.js.map +1 -0
  99. package/dist/runtime.d.ts +10 -7
  100. package/dist/runtime.js +20 -2
  101. package/dist/runtime.js.map +1 -1
  102. package/dist/server.d.ts +2 -2
  103. package/dist/server.js +15 -29
  104. package/dist/server.js.map +1 -1
  105. package/dist/shared.d.ts +58 -59
  106. package/dist/shared.js +51 -13
  107. package/dist/shared.js.map +1 -1
  108. package/dist/tracking.d.ts +4 -3
  109. package/dist/tracking.js +6 -1
  110. package/dist/tracking.js.map +1 -1
  111. package/package.json +32 -7
  112. package/dist/hydration.d.ts +0 -30
  113. package/dist/hydration.js +0 -355
  114. package/dist/hydration.js.map +0 -1
  115. package/dist/render.d.ts +0 -19
  116. package/dist/render.js +0 -285
  117. package/dist/render.js.map +0 -1
  118. package/dist/scope-utils.d.ts +0 -14
  119. package/dist/scope-utils.js +0 -29
  120. package/dist/scope-utils.js.map +0 -1
@@ -0,0 +1,34 @@
1
+ /**
2
+ * SSG build pipeline.
3
+ *
4
+ * Orchestrates: Vite client build → route discovery → pre-render → write HTML.
5
+ */
6
+ import * as Effect from "effect/Effect";
7
+ import * as Layer from "effect/Layer";
8
+ import { FileSystem } from "@effect/platform";
9
+ import { Path } from "@effect/platform";
10
+ import { Router, RouterHandlers } from "../router/index.js";
11
+ import type { HeadData } from "../router/index.js";
12
+ import type { VElement } from "../shared.js";
13
+ export interface BuildOptions {
14
+ /** The fibrae router instance */
15
+ readonly router: Router.Router;
16
+ /** Layer providing RouterHandlers */
17
+ readonly handlersLayer: Layer.Layer<RouterHandlers>;
18
+ /** Wraps each route's rendered element in the app shell */
19
+ readonly appShell: (element: VElement) => VElement;
20
+ /** Output directory */
21
+ readonly outDir: string;
22
+ /** Base path prefix for routes (e.g. "/app") */
23
+ readonly basePath?: string;
24
+ /** Path to client JS bundle (relative to site root) */
25
+ readonly clientScript?: string;
26
+ /** Default page title */
27
+ readonly title?: string;
28
+ /** Global head tags injected into every page */
29
+ readonly headTags?: HeadData;
30
+ }
31
+ /**
32
+ * Pre-render all routes marked with `prerender: true` to static HTML files.
33
+ */
34
+ export declare const build: (options: BuildOptions) => Effect.Effect<void, unknown, FileSystem.FileSystem | Path.Path>;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * SSG build pipeline.
3
+ *
4
+ * Orchestrates: Vite client build → route discovery → pre-render → write HTML.
5
+ */
6
+ import * as Effect from "effect/Effect";
7
+ import * as Layer from "effect/Layer";
8
+ import { FileSystem } from "@effect/platform";
9
+ import { Path } from "@effect/platform";
10
+ import { renderToStringWith, SSRAtomRegistryLayer } from "../server.js";
11
+ import { Router, RouterHandlers, getPrerenderRoutes } from "../router/index.js";
12
+ import { buildPage } from "./html.js";
13
+ /**
14
+ * Render a single route to an HTML string.
15
+ */
16
+ const renderRoute = (config) => Effect.gen(function* () {
17
+ const serverLayer = Router.serverLayer({
18
+ router: config.router,
19
+ pathname: config.pathname,
20
+ search: "",
21
+ basePath: config.basePath,
22
+ });
23
+ const fullLayer = Layer.provideMerge(serverLayer, Layer.merge(config.handlersLayer, SSRAtomRegistryLayer));
24
+ const { html, dehydratedState, head } = yield* Effect.gen(function* () {
25
+ const { element, head } = yield* Router.CurrentRouteElement;
26
+ const renderResult = yield* renderToStringWith(config.appShell(element));
27
+ return { ...renderResult, head };
28
+ }).pipe(Effect.provide(fullLayer));
29
+ return yield* buildPage({
30
+ html,
31
+ dehydratedState: dehydratedState,
32
+ clientScript: config.clientScript,
33
+ title: config.title,
34
+ head,
35
+ headTags: config.headTags,
36
+ });
37
+ });
38
+ /**
39
+ * Compute all (pathname, handler) pairs from prerender routes.
40
+ */
41
+ const expandRoutes = (prerenderRoutes, basePath) => Effect.all(prerenderRoutes.flatMap(({ handler, paramSets }) => paramSets.map((params) => handler.route
42
+ .interpolate(params)
43
+ .pipe(Effect.map((pathname) => ({ pathname: basePath + pathname, handler }))))));
44
+ /**
45
+ * Write an HTML string to the appropriate file path under outDir.
46
+ * Creates directories as needed.
47
+ *
48
+ * / → outDir/index.html
49
+ * /about → outDir/about/index.html
50
+ * /posts/1 → outDir/posts/1/index.html
51
+ */
52
+ const writePageFile = (outDir, pathname, html) => Effect.gen(function* () {
53
+ const fs = yield* FileSystem.FileSystem;
54
+ const path = yield* Path.Path;
55
+ const filePath = pathname === "/" || pathname === ""
56
+ ? path.join(outDir, "index.html")
57
+ : path.join(outDir, pathname, "index.html");
58
+ yield* fs.makeDirectory(path.dirname(filePath), { recursive: true });
59
+ yield* fs.writeFileString(filePath, html);
60
+ });
61
+ /**
62
+ * Pre-render all routes marked with `prerender: true` to static HTML files.
63
+ */
64
+ export const build = (options) => Effect.gen(function* () {
65
+ const { router, handlersLayer, appShell, outDir, basePath = "", clientScript, title, headTags, } = options;
66
+ // Resolve handlers to get prerender routes
67
+ const handlers = yield* Effect.provide(Effect.flatMap(RouterHandlers, (h) => getPrerenderRoutes(h)), handlersLayer);
68
+ const routes = yield* expandRoutes(handlers, basePath);
69
+ if (routes.length === 0) {
70
+ yield* Effect.log("No prerender routes found.");
71
+ return;
72
+ }
73
+ yield* Effect.log(`Pre-rendering ${routes.length} page(s)...`);
74
+ // Render all routes
75
+ const pages = yield* Effect.all(routes.map(({ pathname }) => renderRoute({
76
+ router,
77
+ handlersLayer,
78
+ appShell,
79
+ pathname,
80
+ basePath,
81
+ clientScript,
82
+ title,
83
+ headTags,
84
+ }).pipe(Effect.map((html) => ({ pathname, html })))));
85
+ // Write all pages to disk
86
+ yield* Effect.forEach(pages, ({ pathname, html }) => Effect.gen(function* () {
87
+ yield* writePageFile(outDir, pathname, html);
88
+ yield* Effect.log(` ${pathname} → ${outDir}${pathname === "/" ? "/index.html" : `${pathname}/index.html`}`);
89
+ }), { discard: true });
90
+ yield* Effect.log(`Done. ${pages.length} page(s) written to ${outDir}/`);
91
+ });
92
+ //# sourceMappingURL=build.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.js","sourceRoot":"","sources":["../../src/cli/build.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * fibrae CLI — static site generation for fibrae apps.
4
+ *
5
+ * Commands:
6
+ * fibrae build Pre-render routes and build client bundle
7
+ * fibrae dev Start Vite dev server with on-demand SSR
8
+ * fibrae preview Serve the built output
9
+ */
10
+ export {};
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * fibrae CLI — static site generation for fibrae apps.
4
+ *
5
+ * Commands:
6
+ * fibrae build Pre-render routes and build client bundle
7
+ * fibrae dev Start Vite dev server with on-demand SSR
8
+ * fibrae preview Serve the built output
9
+ */
10
+ const [command] = process.argv.slice(2);
11
+ const commands = {
12
+ async build() {
13
+ const { build } = await import("vite");
14
+ await build();
15
+ },
16
+ async dev() {
17
+ const { createServer } = await import("vite");
18
+ const server = await createServer({ server: { open: true } });
19
+ await server.listen();
20
+ server.printUrls();
21
+ },
22
+ async preview() {
23
+ const { preview } = await import("vite");
24
+ const server = await preview();
25
+ server.printUrls();
26
+ },
27
+ };
28
+ const run = commands[command ?? ""];
29
+ if (!run) {
30
+ console.log(`Usage: fibrae <command>
31
+
32
+ Commands:
33
+ build Pre-render routes and build client bundle
34
+ dev Start Vite dev server with on-demand SSR
35
+ preview Serve the built output`);
36
+ process.exit(command ? 1 : 0);
37
+ }
38
+ run().catch((err) => {
39
+ console.error(err);
40
+ process.exit(1);
41
+ });
42
+ export {};
43
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli/cli.ts"],"names":[],"mappings":";AACA;;;;;;;GAOG;AAEH,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAExC,MAAM,QAAQ,GAAwC;IACpD,KAAK,CAAC,KAAK;QACT,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,KAAK,EAAE,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,GAAG;QACP,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QAC9D,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;QACtB,MAAM,CAAC,SAAS,EAAE,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,MAAM,OAAO,EAAE,CAAC;QAC/B,MAAM,CAAC,SAAS,EAAE,CAAC;IACrB,CAAC;CACF,CAAC;AAEF,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;AAEpC,IAAI,CAAC,GAAG,EAAE,CAAC;IACT,OAAO,CAAC,GAAG,CAAC;;;;;kCAKoB,CAAC,CAAC;IAClC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAClB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Configuration types for fibrae-cli.
3
+ */
4
+ import type { HeadData } from "../router/RouterBuilder.js";
5
+ export interface FibraeConfig {
6
+ /** Module path that exports { router, handlers, App } */
7
+ readonly entry: string;
8
+ /** Client hydration entry point */
9
+ readonly client: string;
10
+ /** Output directory (default: "dist") */
11
+ readonly outDir?: string;
12
+ /** Base path prefix for routes */
13
+ readonly basePath?: string;
14
+ /** Default page title */
15
+ readonly title?: string;
16
+ /** Global head tags injected into every page (fonts, analytics, meta, etc.) */
17
+ readonly headTags?: HeadData;
18
+ }
19
+ export declare const defineConfig: (config: FibraeConfig) => FibraeConfig;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Configuration types for fibrae-cli.
3
+ */
4
+ export const defineConfig = (config) => config;
5
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAmBH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,MAAoB,EAAgB,EAAE,CAAC,MAAM,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { HeadData } from "../router/RouterBuilder.js";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Option from "effect/Option";
4
+ export interface PageOptions {
5
+ readonly html: string;
6
+ readonly dehydratedState: ReadonlyArray<unknown>;
7
+ readonly clientScript?: string;
8
+ readonly title?: string;
9
+ readonly head: Option.Option<HeadData>;
10
+ /** Global head tags from config, merged with per-route head */
11
+ readonly headTags?: HeadData;
12
+ }
13
+ export declare const buildPage: (options: PageOptions) => Effect.Effect<string, unknown, never>;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * HTML page template for static site generation.
3
+ *
4
+ * Uses fibrae JSX to generate full HTML documents wrapping
5
+ * pre-rendered content with dehydrated state and client scripts.
6
+ */
7
+ import { h } from "../h.js";
8
+ import { renderToString } from "../server.js";
9
+ import * as Effect from "effect/Effect";
10
+ import * as Option from "effect/Option";
11
+ import * as Array from "effect/Array";
12
+ const metaToElement = (meta) => {
13
+ if ("title" in meta)
14
+ return Option.none();
15
+ if ("charset" in meta)
16
+ return Option.some(h("meta", { charset: meta.charset }));
17
+ if ("script:ld+json" in meta)
18
+ return Option.some(h("script", {
19
+ type: "application/ld+json",
20
+ dangerouslySetInnerHTML: JSON.stringify(meta["script:ld+json"]),
21
+ }));
22
+ if ("name" in meta)
23
+ return Option.some(h("meta", { name: meta.name, content: meta.content }));
24
+ if ("property" in meta)
25
+ return Option.some(h("meta", { property: meta.property, content: meta.content }));
26
+ if ("httpEquiv" in meta)
27
+ return Option.some(h("meta", { "http-equiv": meta.httpEquiv, content: meta.content }));
28
+ if ("tagName" in meta) {
29
+ const { tagName, ...attrs } = meta;
30
+ return Option.some(h(tagName, attrs));
31
+ }
32
+ return Option.none();
33
+ };
34
+ /**
35
+ * Get the dedup key for a meta descriptor.
36
+ * Per TanStack Router pattern: meta tags with the same name/property are
37
+ * deduplicated, with per-route tags winning over global tags.
38
+ */
39
+ const metaKey = (meta) => {
40
+ if ("name" in meta)
41
+ return `name:${meta.name}`;
42
+ if ("property" in meta)
43
+ return `property:${meta.property}`;
44
+ if ("httpEquiv" in meta)
45
+ return `httpEquiv:${meta.httpEquiv}`;
46
+ if ("charset" in meta)
47
+ return "charset";
48
+ return undefined;
49
+ };
50
+ /**
51
+ * Merge meta arrays with deduplication. Per-route entries override globals
52
+ * when they share the same name/property/httpEquiv key.
53
+ */
54
+ const dedupMeta = (global, perRoute) => {
55
+ const routeKeys = new Set(perRoute.map(metaKey).filter(Boolean));
56
+ const filtered = global.filter((m) => {
57
+ const key = metaKey(m);
58
+ return key === undefined || !routeKeys.has(key);
59
+ });
60
+ return [...filtered, ...perRoute];
61
+ };
62
+ const buildHeadChildren = (title, head, headTags) => {
63
+ const headData = Option.getOrUndefined(head);
64
+ // Per-route title wins, then global headTags title, then config title
65
+ const pageTitle = headData?.title ?? headTags?.title ?? title;
66
+ // Merge global headTags with per-route head.
67
+ // Meta tags are deduplicated by name/property (per-route wins).
68
+ // Links and scripts are concatenated (global first, then per-route).
69
+ const allMeta = dedupMeta(headTags?.meta ?? [], headData?.meta ?? []);
70
+ const allLinks = [...(headTags?.links ?? []), ...(headData?.links ?? [])];
71
+ const allScripts = [...(headTags?.scripts ?? []), ...(headData?.scripts ?? [])];
72
+ return [
73
+ h("meta", { charset: "UTF-8" }),
74
+ h("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0" }),
75
+ ...Option.match(Option.fromNullable(pageTitle), {
76
+ onNone: () => [],
77
+ onSome: (t) => [h("title", {}, [t])],
78
+ }),
79
+ ...Array.filterMap(allMeta, metaToElement),
80
+ ...allLinks.map((attrs) => h("link", attrs)),
81
+ ...allScripts.flatMap((script) => script.src
82
+ ? [h("script", { type: script.type, src: script.src })]
83
+ : script.content
84
+ ? [h("script", { type: script.type, dangerouslySetInnerHTML: script.content })]
85
+ : []),
86
+ ];
87
+ };
88
+ const PageShell = (props) => h("html", { lang: "en" }, [
89
+ h("head", {}, buildHeadChildren(props.title, props.head, props.headTags)),
90
+ h("body", {}, [
91
+ h("div", { id: "root", dangerouslySetInnerHTML: props.html }),
92
+ h("script", {
93
+ type: "application/json",
94
+ id: "__fibrae-state__",
95
+ dangerouslySetInnerHTML: JSON.stringify(props.dehydratedState),
96
+ }),
97
+ ...(props.clientScript ? [h("script", { type: "module", src: props.clientScript })] : []),
98
+ ]),
99
+ ]);
100
+ export const buildPage = (options) => renderToString(PageShell(options)).pipe(Effect.map(({ html }) => `<!DOCTYPE html>\n${html}`));
101
+ //# sourceMappingURL=html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"html.js","sourceRoot":"","sources":["../../src/cli/html.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { buildPage } from "./html.js";
2
+ export type { PageOptions } from "./html.js";
3
+ export { build } from "./build.js";
4
+ export type { BuildOptions } from "./build.js";
5
+ export { defineConfig } from "./config.js";
6
+ export type { FibraeConfig } from "./config.js";
@@ -0,0 +1,4 @@
1
+ export { buildPage } from "./html.js";
2
+ export { build } from "./build.js";
3
+ export { defineConfig } from "./config.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Vite plugin for fibrae static site generation.
3
+ *
4
+ * Hooks into Vite's build pipeline to pre-render routes after the client build.
5
+ * In dev mode, provides on-demand SSR middleware.
6
+ */
7
+ import type { Plugin } from "vite";
8
+ import type { FibraeConfig } from "./config.js";
9
+ export declare const fibrae: (config: FibraeConfig) => Plugin<any>;
@@ -0,0 +1,143 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Option from "effect/Option";
3
+ import * as Layer from "effect/Layer";
4
+ import { NodeContext } from "@effect/platform-node";
5
+ import { Router, RouterHandlers } from "../router/index.js";
6
+ import { renderToStringWith, SSRAtomRegistryLayer } from "../server.js";
7
+ import { buildPage } from "./html.js";
8
+ /**
9
+ * Render a single page for the dev server.
10
+ */
11
+ const renderDevPage = (opts) => Effect.gen(function* () {
12
+ const serverLayer = Router.serverLayer({
13
+ router: opts.router,
14
+ pathname: opts.pathname,
15
+ search: "",
16
+ basePath: opts.basePath,
17
+ });
18
+ const fullLayer = Layer.provideMerge(serverLayer, Layer.merge(opts.handlersLayer, SSRAtomRegistryLayer));
19
+ const { html, dehydratedState, head } = yield* Effect.gen(function* () {
20
+ const { head } = yield* Router.CurrentRouteElement;
21
+ const renderResult = opts.App
22
+ ? yield* renderToStringWith(opts.App())
23
+ : yield* Effect.gen(function* () {
24
+ const { element } = yield* Router.CurrentRouteElement;
25
+ const app = opts.appShell ? opts.appShell(element) : element;
26
+ return yield* renderToStringWith(app);
27
+ });
28
+ return { ...renderResult, head };
29
+ }).pipe(Effect.provide(fullLayer));
30
+ return yield* buildPage({
31
+ html,
32
+ dehydratedState: dehydratedState,
33
+ clientScript: opts.clientScript,
34
+ title: opts.title,
35
+ head,
36
+ headTags: opts.headTags,
37
+ });
38
+ });
39
+ /**
40
+ * Walk the Vite module graph from an entry and collect CSS module URLs.
41
+ * Injects these as <style> tags in the SSR HTML to prevent FOUC.
42
+ */
43
+ const collectCss = async (server, entryUrl) => {
44
+ // Warm the module graph so CSS deps are discovered
45
+ await server.transformRequest(entryUrl);
46
+ const seen = new Set();
47
+ const cssUrls = [];
48
+ const walk = (mod) => {
49
+ if (!mod?.id || seen.has(mod.id))
50
+ return;
51
+ seen.add(mod.id);
52
+ if (mod.id.endsWith(".css")) {
53
+ cssUrls.push(mod.url);
54
+ }
55
+ for (const dep of mod.importedModules) {
56
+ walk(dep);
57
+ }
58
+ };
59
+ walk(await server.moduleGraph.getModuleByUrl(entryUrl));
60
+ return cssUrls;
61
+ };
62
+ export const fibrae = (config) => {
63
+ let _resolvedConfig;
64
+ return {
65
+ name: "fibrae-ssg",
66
+ configResolved(resolved) {
67
+ _resolvedConfig = resolved;
68
+ },
69
+ configureServer(server) {
70
+ server.middlewares.use(async (req, res, next) => {
71
+ const url = req.url;
72
+ if (!url || url.startsWith("/@") || url.includes(".")) {
73
+ return next();
74
+ }
75
+ try {
76
+ const appModule = await server.ssrLoadModule(config.entry);
77
+ const { router, handlersLayer, appShell, App } = appModule;
78
+ if (!router || !handlersLayer) {
79
+ return next();
80
+ }
81
+ // Only SSR routes the router knows about; pass everything else through
82
+ if (Option.isNone(router.matchRoute(url))) {
83
+ return next();
84
+ }
85
+ const rawHtml = await Effect.runPromise(renderDevPage({
86
+ router,
87
+ handlersLayer,
88
+ App,
89
+ appShell,
90
+ pathname: url,
91
+ basePath: config.basePath ?? "",
92
+ clientScript: config.client,
93
+ title: config.title,
94
+ headTags: config.headTags,
95
+ }));
96
+ // Collect CSS from client entry's module graph and inject into <head>
97
+ const cssUrls = await collectCss(server, config.client);
98
+ const cssTags = cssUrls.map((u) => `<link rel="stylesheet" href="${u}">`).join("\n");
99
+ const withCss = cssTags ? rawHtml.replace("</head>", `${cssTags}\n</head>`) : rawHtml;
100
+ // Let Vite inject HMR client
101
+ const result = await server.transformIndexHtml(url, withCss);
102
+ res.setHeader("Content-Type", "text/html");
103
+ res.end(result);
104
+ }
105
+ catch {
106
+ next();
107
+ }
108
+ });
109
+ },
110
+ async closeBundle() {
111
+ if (_resolvedConfig.command !== "build")
112
+ return;
113
+ const outDir = config.outDir ?? _resolvedConfig.build.outDir ?? "dist";
114
+ try {
115
+ const entryPath = new URL(config.entry, `file://${process.cwd()}/`).pathname;
116
+ const appModule = await import(entryPath);
117
+ const { router, handlersLayer, appShell } = appModule;
118
+ if (!router || !handlersLayer) {
119
+ console.warn("[fibrae-ssg] Entry module missing router or handlersLayer exports. Skipping SSG.");
120
+ return;
121
+ }
122
+ const { build: ssgBuild } = await import("./build.js");
123
+ const clientScript = config.client
124
+ ? `/${config.client.replace(/\.tsx?$/, ".js")}`
125
+ : undefined;
126
+ await Effect.runPromise(ssgBuild({
127
+ router,
128
+ handlersLayer,
129
+ appShell: appShell ?? ((el) => el),
130
+ outDir,
131
+ basePath: config.basePath ?? "",
132
+ clientScript,
133
+ title: config.title,
134
+ headTags: config.headTags,
135
+ }).pipe(Effect.provide(NodeContext.layer)));
136
+ }
137
+ catch (e) {
138
+ console.error("[fibrae-ssg] Pre-render failed:", e);
139
+ }
140
+ },
141
+ };
142
+ };
143
+ //# sourceMappingURL=vite-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-plugin.js","sourceRoot":"","sources":["../../src/cli/vite-plugin.ts"],"names":[],"mappings":"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"}
@@ -1,12 +1,11 @@
1
1
  import * as Context from "effect/Context";
2
2
  import * as Effect from "effect/Effect";
3
- import * as Stream from "effect/Stream";
4
3
  import { type ComponentError, type VElement } from "./shared.js";
5
4
  declare const ErrorBoundaryChannel_base: Context.TagClass<ErrorBoundaryChannel, "ErrorBoundaryChannel", {
6
5
  /** Report an error to this boundary. Used by event handlers and stream subscriptions. */
7
6
  readonly reportError: (error: unknown) => Effect.Effect<void, never, never>;
8
7
  /** Optional unique identifier for this boundary (for debugging/logging) */
9
- readonly boundaryId?: string;
8
+ readonly boundaryId?: string | undefined;
10
9
  }>;
11
10
  /**
12
11
  * Error boundary channel - used by ErrorBoundary for async error reporting.
@@ -33,42 +32,41 @@ export declare class ErrorBoundaryChannel extends ErrorBoundaryChannel_base {
33
32
  */
34
33
  export declare const Suspense: (props: {
35
34
  fallback: VElement;
36
- threshold?: number;
37
- children?: VElement | VElement[];
35
+ threshold?: number | undefined;
36
+ children?: VElement[] | VElement | undefined;
38
37
  }) => VElement;
39
38
  /**
40
- * Creates an Effect-native error boundary around children.
39
+ * Error boundary component catches errors in its subtree and shows a fallback.
41
40
  *
42
- * Returns a `Stream<VElement, ComponentError, never>` that can be piped with
43
- * `Stream.catchTags` for fully typed error handling.
41
+ * Supports recovery: when children re-emit (e.g. route change), the boundary
42
+ * resets and shows the new content. Children are "parked" during error state
43
+ * (subscriptions stay alive), similar to how Suspense works.
44
44
  *
45
45
  * @example
46
46
  * ```tsx
47
- * // Create a boundary with typed error handlers as a wrapper component
48
- * const SafeApp = () => ErrorBoundary(<App />).pipe(
49
- * Stream.catchTags({
50
- * RenderError: (e) => Stream.succeed(<div>Render failed: {e.componentName}</div>),
51
- * StreamError: (e) => Stream.succeed(<div>Stream failed: {e.phase}</div>),
52
- * EventHandlerError: (e) => Stream.succeed(<div>Event {e.eventType} failed</div>),
53
- * })
54
- * );
55
- *
56
- * // Use it like any other component
57
- * <SafeApp />
47
+ * <ErrorBoundary fallback={(error) => <div>Error: {error._tag}</div>}>
48
+ * <RouterOutlet />
49
+ * </ErrorBoundary>
58
50
  * ```
59
51
  *
60
- * **How it works:**
61
- * 1. `ErrorBoundary()` returns a Stream that first emits a BOUNDARY marker element
62
- * 2. The renderer detects this marker and sets up error handling
63
- * 3. If any error occurs in the subtree, the Stream fails with that ComponentError
64
- * 4. `Stream.catchTags` catches the error and produces a fallback Stream
65
- * 5. The fallback VElement is rendered in place of the failed content
52
+ * The `error` parameter is a `ComponentError` union — match on `_tag` for
53
+ * per-type handling:
66
54
  *
67
- * **Nesting:** Boundaries nest naturally - inner boundary catches first, unhandled
68
- * errors propagate to outer boundary following Effect/Stream error propagation rules.
55
+ * ```tsx
56
+ * const fallback = (error: ComponentError) => {
57
+ * switch (error._tag) {
58
+ * case "RenderError": return <div>Render failed: {error.componentName}</div>
59
+ * case "StreamError": return <div>Stream failed in {error.phase}</div>
60
+ * case "EventHandlerError": return <div>Event {error.eventType} failed</div>
61
+ * }
62
+ * }
63
+ * ```
69
64
  *
70
- * @param children - The VElement(s) to wrap in an error boundary
71
- * @returns Stream<VElement, ComponentError, never> - pipe with Stream.catchTags to handle errors
65
+ * @param props.fallback - Function that receives the error and returns fallback UI
66
+ * @param props.children - Child components to wrap in the error boundary
72
67
  */
73
- export declare const ErrorBoundary: (children: VElement | VElement[]) => Stream.Stream<VElement, ComponentError, never>;
68
+ export declare const ErrorBoundary: (props: {
69
+ fallback: (error: ComponentError) => VElement;
70
+ children?: VElement[] | VElement | undefined;
71
+ }) => VElement;
74
72
  export {};