@fluixi/start 0.1.0-alpha.73 → 0.1.0-alpha.75

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 (123) hide show
  1. package/dist/adapter.cjs +750 -0
  2. package/dist/adapter.d.ts +9 -9
  3. package/dist/adapter.d.ts.map +1 -1
  4. package/dist/adapter.js +10 -10
  5. package/dist/adapter.mjs +719 -0
  6. package/dist/adapters/entry.cjs +122 -0
  7. package/dist/adapters/entry.js +1 -1
  8. package/dist/adapters/entry.mjs +95 -0
  9. package/dist/adapters/platforms.cjs +311 -0
  10. package/dist/adapters/platforms.d.ts +2 -2
  11. package/dist/adapters/platforms.js +8 -8
  12. package/dist/adapters/platforms.mjs +284 -0
  13. package/dist/api-HD7KU3BS.mjs +114 -0
  14. package/dist/api.cjs +145 -0
  15. package/dist/api.js +1 -1
  16. package/dist/api.mjs +116 -0
  17. package/dist/commands/api-HD7KU3BS.mjs +114 -0
  18. package/dist/commands/api-PBT4QZ34.mjs +16 -0
  19. package/dist/commands/api-PJL37LV3.mjs +17 -0
  20. package/dist/commands/build.cjs +877 -0
  21. package/dist/commands/build.js +6 -6
  22. package/dist/commands/build.mjs +717 -0
  23. package/dist/commands/chunk-GFQ4MUIC.mjs +117 -0
  24. package/dist/commands/chunk-MTM6M3TC.mjs +115 -0
  25. package/dist/commands/dev.cjs +670 -0
  26. package/dist/commands/dev.js +3 -3
  27. package/dist/commands/dev.mjs +512 -0
  28. package/dist/commands/index.cjs +1145 -0
  29. package/dist/commands/index.d.ts +1 -1
  30. package/dist/commands/index.js +1 -1
  31. package/dist/commands/index.mjs +982 -0
  32. package/dist/commands/prerender.cjs +404 -0
  33. package/dist/commands/prerender.d.ts +1 -1
  34. package/dist/commands/prerender.js +2 -2
  35. package/dist/commands/prerender.mjs +377 -0
  36. package/dist/commands/start.cjs +463 -0
  37. package/dist/commands/start.d.ts +1 -1
  38. package/dist/commands/start.js +2 -2
  39. package/dist/commands/start.mjs +428 -0
  40. package/dist/config.cjs +150 -0
  41. package/dist/config.d.ts +24 -7
  42. package/dist/config.d.ts.map +1 -1
  43. package/dist/config.js +8 -6
  44. package/dist/config.mjs +114 -0
  45. package/dist/define-config.cjs +32 -0
  46. package/dist/define-config.d.ts +12 -0
  47. package/dist/define-config.d.ts.map +1 -0
  48. package/dist/define-config.js +3 -0
  49. package/dist/define-config.mjs +7 -0
  50. package/dist/di.cjs +200 -0
  51. package/dist/di.d.ts +4 -4
  52. package/dist/di.d.ts.map +1 -1
  53. package/dist/di.js +9 -9
  54. package/dist/di.mjs +169 -0
  55. package/dist/document.cjs +115 -0
  56. package/dist/document.d.ts +1 -1
  57. package/dist/document.js +1 -1
  58. package/dist/document.mjs +86 -0
  59. package/dist/generated-api.cjs +43 -0
  60. package/dist/generated-api.js +1 -1
  61. package/dist/generated-api.mjs +18 -0
  62. package/dist/generated-server-fns.cjs +43 -0
  63. package/dist/generated-server-fns.mjs +18 -0
  64. package/dist/handler-core.cjs +211 -0
  65. package/dist/handler-core.d.ts +2 -2
  66. package/dist/handler-core.js +2 -2
  67. package/dist/handler-core.mjs +185 -0
  68. package/dist/handler.cjs +182 -0
  69. package/dist/handler.d.ts +3 -3
  70. package/dist/handler.js +3 -3
  71. package/dist/handler.mjs +152 -0
  72. package/dist/head.cjs +25 -0
  73. package/dist/head.d.ts +1 -1
  74. package/dist/head.js +1 -1
  75. package/dist/head.mjs +4 -0
  76. package/dist/image-loader.cjs +37 -0
  77. package/dist/image-loader.d.ts +25 -0
  78. package/dist/image-loader.d.ts.map +1 -0
  79. package/dist/image-loader.js +18 -0
  80. package/dist/image-loader.mjs +12 -0
  81. package/dist/image.cjs +151 -0
  82. package/dist/image.d.ts +2 -19
  83. package/dist/image.d.ts.map +1 -1
  84. package/dist/image.js +5 -21
  85. package/dist/image.mjs +126 -0
  86. package/dist/index.cjs +303 -0
  87. package/dist/index.d.ts +28 -8
  88. package/dist/index.d.ts.map +1 -1
  89. package/dist/index.js +33 -17
  90. package/dist/index.mjs +262 -0
  91. package/dist/interceptors.cjs +72 -0
  92. package/dist/interceptors.js +1 -1
  93. package/dist/interceptors.mjs +44 -0
  94. package/dist/internal.cjs +393 -0
  95. package/dist/internal.d.ts +6 -6
  96. package/dist/internal.d.ts.map +1 -1
  97. package/dist/internal.js +23 -15
  98. package/dist/internal.mjs +223 -0
  99. package/dist/middleware.cjs +58 -0
  100. package/dist/middleware.d.ts +3 -3
  101. package/dist/middleware.d.ts.map +1 -1
  102. package/dist/middleware.js +1 -1
  103. package/dist/middleware.mjs +31 -0
  104. package/dist/preload.cjs +42 -0
  105. package/dist/preload.js +1 -1
  106. package/dist/preload.mjs +18 -0
  107. package/dist/router.cjs +25 -0
  108. package/dist/router.d.ts +3 -3
  109. package/dist/router.js +3 -3
  110. package/dist/router.mjs +4 -0
  111. package/dist/server-fn-setup.cjs +46 -0
  112. package/dist/server-fn-setup.d.ts +1 -1
  113. package/dist/server-fn-setup.js +2 -2
  114. package/dist/server-fn-setup.mjs +22 -0
  115. package/dist/server-fn.cjs +109 -0
  116. package/dist/server-fn.d.ts +2 -2
  117. package/dist/server-fn.js +2 -2
  118. package/dist/server-fn.mjs +79 -0
  119. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  120. package/dist/ui.cjs +123 -0
  121. package/dist/ui.js +1 -1
  122. package/dist/ui.mjs +93 -0
  123. package/package.json +63 -25
@@ -0,0 +1,211 @@
1
+ /*! @fluixi/start v0.1.0-alpha.75 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/handler-core.ts
22
+ var handler_core_exports = {};
23
+ __export(handler_core_exports, {
24
+ createHandlerFrom: () => createHandlerFrom
25
+ });
26
+ module.exports = __toCommonJS(handler_core_exports);
27
+
28
+ // src/document.ts
29
+ var import_head = require("@fluixi/head");
30
+ function injectApp(template, appHtml, mountId = "root") {
31
+ const mount = new RegExp(`<div id=["']${mountId}["']\\s*>\\s*</div>`);
32
+ if (mount.test(template)) {
33
+ return template.replace(mount, `<div id="${mountId}">${appHtml}</div>`);
34
+ }
35
+ if (template.includes("<!--ssr-outlet-->")) {
36
+ return template.replace("<!--ssr-outlet-->", appHtml);
37
+ }
38
+ return template.replace("</body>", `<div id="${mountId}">${appHtml}</div></body>`);
39
+ }
40
+ function splitTemplate(template, mountId = "root") {
41
+ const mount = new RegExp(`<div id=["']${mountId}["']\\s*>\\s*</div>`);
42
+ const m = template.match(mount);
43
+ if (m && m.index !== void 0) {
44
+ return {
45
+ head: template.slice(0, m.index) + `<div id="${mountId}">`,
46
+ tail: `</div>` + template.slice(m.index + m[0].length)
47
+ };
48
+ }
49
+ const outlet = template.indexOf("<!--ssr-outlet-->");
50
+ if (outlet !== -1) {
51
+ return {
52
+ head: template.slice(0, outlet),
53
+ tail: template.slice(outlet + "<!--ssr-outlet-->".length)
54
+ };
55
+ }
56
+ const body = template.indexOf("</body>");
57
+ if (body !== -1) {
58
+ return {
59
+ head: template.slice(0, body) + `<div id="${mountId}">`,
60
+ tail: `</div>` + template.slice(body)
61
+ };
62
+ }
63
+ return { head: template, tail: "" };
64
+ }
65
+ function injectPreload(template, files) {
66
+ const tags = files.filter((file) => !template.includes(file)).map(
67
+ (file) => file.endsWith(".css") ? `<link rel="stylesheet" href="${file}">` : (
68
+ // `modulepreload` also fetches the chunk's own static imports.
69
+ `<link rel="modulepreload" href="${file}">`
70
+ )
71
+ );
72
+ if (tags.length === 0) return template;
73
+ const head = template.indexOf("</head>");
74
+ if (head === -1) return template;
75
+ return template.slice(0, head) + tags.join("") + template.slice(head);
76
+ }
77
+ function setHtmlAttr(html, name, value) {
78
+ const existing = new RegExp(`(<html\\b[^>]*?)\\s${name}="[^"]*"`, "i");
79
+ if (existing.test(html)) return html.replace(existing, `$1 ${name}="${value}"`);
80
+ return html.replace(/<html\b/i, `<html ${name}="${value}"`);
81
+ }
82
+ function injectAppAndHead(template, rendered, mountId = "root") {
83
+ const { head, body } = (0, import_head.extractHeadMarker)(rendered);
84
+ let html = injectApp(template, body, mountId);
85
+ if (head) {
86
+ if (head.headHtml) {
87
+ if (/<title[\s>]/i.test(head.headHtml)) html = html.replace(/<title>[\s\S]*?<\/title>/i, "");
88
+ html = html.replace("</head>", `${head.headHtml}</head>`);
89
+ }
90
+ for (const [k, v] of Object.entries(head.htmlAttrs)) html = setHtmlAttr(html, k, v);
91
+ }
92
+ return html;
93
+ }
94
+ function guardHandler(handler, onError) {
95
+ return async (request) => {
96
+ try {
97
+ return await handler(request);
98
+ } catch (e) {
99
+ onError?.(e);
100
+ return new Response(String(e?.stack || e), {
101
+ status: 500,
102
+ headers: { "content-type": "text/plain; charset=utf-8" }
103
+ });
104
+ }
105
+ };
106
+ }
107
+
108
+ // src/handler.ts
109
+ function streamDocument(head, body, tail) {
110
+ const enc = new TextEncoder();
111
+ const stream = new ReadableStream({
112
+ async start(controller) {
113
+ try {
114
+ controller.enqueue(enc.encode(head));
115
+ const reader = body.getReader();
116
+ for (; ; ) {
117
+ const { done, value } = await reader.read();
118
+ if (done) break;
119
+ controller.enqueue(value);
120
+ }
121
+ controller.enqueue(enc.encode(tail));
122
+ controller.close();
123
+ } catch (err) {
124
+ controller.error(err);
125
+ }
126
+ }
127
+ });
128
+ return new Response(stream, {
129
+ status: 200,
130
+ headers: { "content-type": "text/html; charset=utf-8" }
131
+ });
132
+ }
133
+ function createRequestHandler(render) {
134
+ return async (request) => {
135
+ const url = new URL(request.url);
136
+ try {
137
+ const html = await render(url.pathname + url.search, request);
138
+ return new Response(html, {
139
+ status: 200,
140
+ headers: { "content-type": "text/html; charset=utf-8" }
141
+ });
142
+ } catch (e) {
143
+ return new Response(String(e?.stack || e), {
144
+ status: 500,
145
+ headers: { "content-type": "text/plain; charset=utf-8" }
146
+ });
147
+ }
148
+ };
149
+ }
150
+
151
+ // src/middleware.ts
152
+ function normalizeMiddleware(mod) {
153
+ const def = mod?.default ?? mod;
154
+ if (def == null) return [];
155
+ if (Array.isArray(def)) return def.filter((m) => typeof m === "function");
156
+ return typeof def === "function" ? [def] : [];
157
+ }
158
+ function composeMiddleware(middlewares, core) {
159
+ if (middlewares.length === 0) return core;
160
+ return (request) => {
161
+ let lastCalled = -1;
162
+ const dispatch = (i) => {
163
+ if (i <= lastCalled) {
164
+ return Promise.reject(new Error("middleware called next() more than once"));
165
+ }
166
+ lastCalled = i;
167
+ const mw = middlewares[i];
168
+ if (!mw) return Promise.resolve(core(request));
169
+ return Promise.resolve(mw(request, () => dispatch(i + 1)));
170
+ };
171
+ return dispatch(0);
172
+ };
173
+ }
174
+
175
+ // src/handler-core.ts
176
+ function createHandlerFrom({ template, mod, cfg, middleware, preload }) {
177
+ const withPreload = (html, pathname) => preload ? injectPreload(html, preload(pathname)) : html;
178
+ let core;
179
+ if (!cfg.ssr) {
180
+ core = createRequestHandler(async (url) => withPreload(template, url.split("?")[0]));
181
+ } else if (typeof mod.renderStream === "function") {
182
+ core = async (request) => {
183
+ const url = new URL(request.url);
184
+ const { head, tail } = splitTemplate(withPreload(template, url.pathname), cfg.mountId);
185
+ const body = await mod.renderStream(url.pathname + url.search, request);
186
+ return streamDocument(head, body, tail);
187
+ };
188
+ } else {
189
+ const render = mod.render ?? mod.default;
190
+ core = createRequestHandler(
191
+ async (url, request) => (
192
+ // `url` here is `pathname + search`; matching only ever wants the path.
193
+ injectAppAndHead(withPreload(template, url.split("?")[0]), await render(url, request), cfg.mountId)
194
+ )
195
+ );
196
+ }
197
+ if (typeof mod.handleServerFn === "function" && typeof mod.isServerFnRequest === "function") {
198
+ const render = core;
199
+ core = async (request) => mod.isServerFnRequest(request) ? mod.handleServerFn(request) : render(request);
200
+ }
201
+ if (typeof mod.handleApiRequest === "function" && typeof mod.isApiRequest === "function") {
202
+ const next = core;
203
+ core = async (request) => mod.isApiRequest(request) ? mod.handleApiRequest(request) : next(request);
204
+ }
205
+ if (!middleware) return guardHandler(core);
206
+ return guardHandler(composeMiddleware(normalizeMiddleware(middleware), core));
207
+ }
208
+ // Annotate the CommonJS export names for ESM import in node:
209
+ 0 && (module.exports = {
210
+ createHandlerFrom
211
+ });
@@ -2,13 +2,13 @@
2
2
  * The request handler, assembled from things already in memory.
3
3
  *
4
4
  * `createProdHandler` reads the template off disk and imports the server entry by
5
- * path fine on Node, impossible in a worker, where there is no filesystem and
5
+ * path: fine on Node, impossible in a worker, where there is no filesystem and
6
6
  * every module has to be part of the bundle. So the assembly lives here and takes
7
7
  * the template and the module as arguments; the platform entry a deploy adapter
8
8
  * generates imports both statically and calls this.
9
9
  *
10
10
  * One assembly, so an edge deploy cannot quietly behave differently from `fluixi
11
- * start` the dispatch order (server functions, then API routes, then render,
11
+ * start`: the dispatch order (server functions, then API routes, then render,
12
12
  * all under middleware) is a property of the framework, not of the host.
13
13
  */
14
14
  import type { ResolvedConfig } from './config.js';
@@ -8,7 +8,7 @@ export function createHandlerFrom({ template, mod, cfg, middleware, preload }) {
8
8
  let core;
9
9
  if (!cfg.ssr) {
10
10
  // SPA: the page is the static shell; the client entry renders it. Server functions +
11
- // middleware still run through this handler there's just no SSR render pass. The
11
+ // middleware still run through this handler: there's just no SSR render pass. The
12
12
  // path is still known, though, and the route it names still loads its own chunk, so
13
13
  // the shell can say so and save the client a round trip it would otherwise start
14
14
  // only after the router had booted.
@@ -18,7 +18,7 @@ export function createHandlerFrom({ template, mod, cfg, middleware, preload }) {
18
18
  core = async (request) => {
19
19
  const url = new URL(request.url);
20
20
  // Split per request rather than once: the preload links belong in the head, and
21
- // the head is the part that flushes before the body is known which is the whole
21
+ // the head is the part that flushes before the body is known, which is the whole
22
22
  // reason streaming wants them, since the browser can start fetching the route's
23
23
  // chunk while the server is still awaiting data for it.
24
24
  const { head, tail } = splitTemplate(withPreload(template, url.pathname), cfg.mountId);
@@ -0,0 +1,185 @@
1
+ /*! @fluixi/start v0.1.0-alpha.75 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
+
3
+ // src/document.ts
4
+ import { extractHeadMarker } from "@fluixi/head";
5
+ function injectApp(template, appHtml, mountId = "root") {
6
+ const mount = new RegExp(`<div id=["']${mountId}["']\\s*>\\s*</div>`);
7
+ if (mount.test(template)) {
8
+ return template.replace(mount, `<div id="${mountId}">${appHtml}</div>`);
9
+ }
10
+ if (template.includes("<!--ssr-outlet-->")) {
11
+ return template.replace("<!--ssr-outlet-->", appHtml);
12
+ }
13
+ return template.replace("</body>", `<div id="${mountId}">${appHtml}</div></body>`);
14
+ }
15
+ function splitTemplate(template, mountId = "root") {
16
+ const mount = new RegExp(`<div id=["']${mountId}["']\\s*>\\s*</div>`);
17
+ const m = template.match(mount);
18
+ if (m && m.index !== void 0) {
19
+ return {
20
+ head: template.slice(0, m.index) + `<div id="${mountId}">`,
21
+ tail: `</div>` + template.slice(m.index + m[0].length)
22
+ };
23
+ }
24
+ const outlet = template.indexOf("<!--ssr-outlet-->");
25
+ if (outlet !== -1) {
26
+ return {
27
+ head: template.slice(0, outlet),
28
+ tail: template.slice(outlet + "<!--ssr-outlet-->".length)
29
+ };
30
+ }
31
+ const body = template.indexOf("</body>");
32
+ if (body !== -1) {
33
+ return {
34
+ head: template.slice(0, body) + `<div id="${mountId}">`,
35
+ tail: `</div>` + template.slice(body)
36
+ };
37
+ }
38
+ return { head: template, tail: "" };
39
+ }
40
+ function injectPreload(template, files) {
41
+ const tags = files.filter((file) => !template.includes(file)).map(
42
+ (file) => file.endsWith(".css") ? `<link rel="stylesheet" href="${file}">` : (
43
+ // `modulepreload` also fetches the chunk's own static imports.
44
+ `<link rel="modulepreload" href="${file}">`
45
+ )
46
+ );
47
+ if (tags.length === 0) return template;
48
+ const head = template.indexOf("</head>");
49
+ if (head === -1) return template;
50
+ return template.slice(0, head) + tags.join("") + template.slice(head);
51
+ }
52
+ function setHtmlAttr(html, name, value) {
53
+ const existing = new RegExp(`(<html\\b[^>]*?)\\s${name}="[^"]*"`, "i");
54
+ if (existing.test(html)) return html.replace(existing, `$1 ${name}="${value}"`);
55
+ return html.replace(/<html\b/i, `<html ${name}="${value}"`);
56
+ }
57
+ function injectAppAndHead(template, rendered, mountId = "root") {
58
+ const { head, body } = extractHeadMarker(rendered);
59
+ let html = injectApp(template, body, mountId);
60
+ if (head) {
61
+ if (head.headHtml) {
62
+ if (/<title[\s>]/i.test(head.headHtml)) html = html.replace(/<title>[\s\S]*?<\/title>/i, "");
63
+ html = html.replace("</head>", `${head.headHtml}</head>`);
64
+ }
65
+ for (const [k, v] of Object.entries(head.htmlAttrs)) html = setHtmlAttr(html, k, v);
66
+ }
67
+ return html;
68
+ }
69
+ function guardHandler(handler, onError) {
70
+ return async (request) => {
71
+ try {
72
+ return await handler(request);
73
+ } catch (e) {
74
+ onError?.(e);
75
+ return new Response(String(e?.stack || e), {
76
+ status: 500,
77
+ headers: { "content-type": "text/plain; charset=utf-8" }
78
+ });
79
+ }
80
+ };
81
+ }
82
+
83
+ // src/handler.ts
84
+ function streamDocument(head, body, tail) {
85
+ const enc = new TextEncoder();
86
+ const stream = new ReadableStream({
87
+ async start(controller) {
88
+ try {
89
+ controller.enqueue(enc.encode(head));
90
+ const reader = body.getReader();
91
+ for (; ; ) {
92
+ const { done, value } = await reader.read();
93
+ if (done) break;
94
+ controller.enqueue(value);
95
+ }
96
+ controller.enqueue(enc.encode(tail));
97
+ controller.close();
98
+ } catch (err) {
99
+ controller.error(err);
100
+ }
101
+ }
102
+ });
103
+ return new Response(stream, {
104
+ status: 200,
105
+ headers: { "content-type": "text/html; charset=utf-8" }
106
+ });
107
+ }
108
+ function createRequestHandler(render) {
109
+ return async (request) => {
110
+ const url = new URL(request.url);
111
+ try {
112
+ const html = await render(url.pathname + url.search, request);
113
+ return new Response(html, {
114
+ status: 200,
115
+ headers: { "content-type": "text/html; charset=utf-8" }
116
+ });
117
+ } catch (e) {
118
+ return new Response(String(e?.stack || e), {
119
+ status: 500,
120
+ headers: { "content-type": "text/plain; charset=utf-8" }
121
+ });
122
+ }
123
+ };
124
+ }
125
+
126
+ // src/middleware.ts
127
+ function normalizeMiddleware(mod) {
128
+ const def = mod?.default ?? mod;
129
+ if (def == null) return [];
130
+ if (Array.isArray(def)) return def.filter((m) => typeof m === "function");
131
+ return typeof def === "function" ? [def] : [];
132
+ }
133
+ function composeMiddleware(middlewares, core) {
134
+ if (middlewares.length === 0) return core;
135
+ return (request) => {
136
+ let lastCalled = -1;
137
+ const dispatch = (i) => {
138
+ if (i <= lastCalled) {
139
+ return Promise.reject(new Error("middleware called next() more than once"));
140
+ }
141
+ lastCalled = i;
142
+ const mw = middlewares[i];
143
+ if (!mw) return Promise.resolve(core(request));
144
+ return Promise.resolve(mw(request, () => dispatch(i + 1)));
145
+ };
146
+ return dispatch(0);
147
+ };
148
+ }
149
+
150
+ // src/handler-core.ts
151
+ function createHandlerFrom({ template, mod, cfg, middleware, preload }) {
152
+ const withPreload = (html, pathname) => preload ? injectPreload(html, preload(pathname)) : html;
153
+ let core;
154
+ if (!cfg.ssr) {
155
+ core = createRequestHandler(async (url) => withPreload(template, url.split("?")[0]));
156
+ } else if (typeof mod.renderStream === "function") {
157
+ core = async (request) => {
158
+ const url = new URL(request.url);
159
+ const { head, tail } = splitTemplate(withPreload(template, url.pathname), cfg.mountId);
160
+ const body = await mod.renderStream(url.pathname + url.search, request);
161
+ return streamDocument(head, body, tail);
162
+ };
163
+ } else {
164
+ const render = mod.render ?? mod.default;
165
+ core = createRequestHandler(
166
+ async (url, request) => (
167
+ // `url` here is `pathname + search`; matching only ever wants the path.
168
+ injectAppAndHead(withPreload(template, url.split("?")[0]), await render(url, request), cfg.mountId)
169
+ )
170
+ );
171
+ }
172
+ if (typeof mod.handleServerFn === "function" && typeof mod.isServerFnRequest === "function") {
173
+ const render = core;
174
+ core = async (request) => mod.isServerFnRequest(request) ? mod.handleServerFn(request) : render(request);
175
+ }
176
+ if (typeof mod.handleApiRequest === "function" && typeof mod.isApiRequest === "function") {
177
+ const next = core;
178
+ core = async (request) => mod.isApiRequest(request) ? mod.handleApiRequest(request) : next(request);
179
+ }
180
+ if (!middleware) return guardHandler(core);
181
+ return guardHandler(composeMiddleware(normalizeMiddleware(middleware), core));
182
+ }
183
+ export {
184
+ createHandlerFrom
185
+ };
@@ -0,0 +1,182 @@
1
+ /*! @fluixi/start v0.1.0-alpha.75 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/handler.ts
22
+ var handler_exports = {};
23
+ __export(handler_exports, {
24
+ createRequestHandler: () => createRequestHandler,
25
+ nodeToRequest: () => nodeToRequest,
26
+ sendResponse: () => sendResponse,
27
+ serveNode: () => serveNode,
28
+ streamDocument: () => streamDocument,
29
+ toNodeHandler: () => toNodeHandler,
30
+ withStaticFiles: () => withStaticFiles
31
+ });
32
+ module.exports = __toCommonJS(handler_exports);
33
+ var import_node_http = require("node:http");
34
+ var import_promises = require("node:fs/promises");
35
+ var import_node_fs = require("node:fs");
36
+ var import_node_stream = require("node:stream");
37
+ var import_node_path = require("node:path");
38
+ function streamDocument(head, body, tail) {
39
+ const enc = new TextEncoder();
40
+ const stream = new ReadableStream({
41
+ async start(controller) {
42
+ try {
43
+ controller.enqueue(enc.encode(head));
44
+ const reader = body.getReader();
45
+ for (; ; ) {
46
+ const { done, value } = await reader.read();
47
+ if (done) break;
48
+ controller.enqueue(value);
49
+ }
50
+ controller.enqueue(enc.encode(tail));
51
+ controller.close();
52
+ } catch (err) {
53
+ controller.error(err);
54
+ }
55
+ }
56
+ });
57
+ return new Response(stream, {
58
+ status: 200,
59
+ headers: { "content-type": "text/html; charset=utf-8" }
60
+ });
61
+ }
62
+ function createRequestHandler(render) {
63
+ return async (request) => {
64
+ const url = new URL(request.url);
65
+ try {
66
+ const html = await render(url.pathname + url.search, request);
67
+ return new Response(html, {
68
+ status: 200,
69
+ headers: { "content-type": "text/html; charset=utf-8" }
70
+ });
71
+ } catch (e) {
72
+ return new Response(String(e?.stack || e), {
73
+ status: 500,
74
+ headers: { "content-type": "text/plain; charset=utf-8" }
75
+ });
76
+ }
77
+ };
78
+ }
79
+ function nodeToRequest(req) {
80
+ const host = req.headers.host ?? "localhost";
81
+ const url = `http://${host}${req.url ?? "/"}`;
82
+ const headers = new Headers();
83
+ for (const [k, v] of Object.entries(req.headers)) {
84
+ if (Array.isArray(v)) v.forEach((x) => headers.append(k, x));
85
+ else if (v != null) headers.set(k, v);
86
+ }
87
+ const method = req.method ?? "GET";
88
+ const init = { method, headers };
89
+ if (method !== "GET" && method !== "HEAD") {
90
+ init.body = req;
91
+ init.duplex = "half";
92
+ }
93
+ return new Request(url, init);
94
+ }
95
+ async function sendResponse(res, response) {
96
+ res.statusCode = response.status;
97
+ response.headers.forEach((value, key) => res.setHeader(key, value));
98
+ const body = response.body;
99
+ if (!body) {
100
+ res.end(await response.text().catch(() => ""));
101
+ return;
102
+ }
103
+ const reader = body.getReader();
104
+ try {
105
+ for (; ; ) {
106
+ const { done, value } = await reader.read();
107
+ if (done) break;
108
+ res.write(value);
109
+ }
110
+ } finally {
111
+ res.end();
112
+ }
113
+ }
114
+ function toNodeHandler(handler) {
115
+ return async (req, res) => {
116
+ const response = await handler(nodeToRequest(req));
117
+ await sendResponse(res, response);
118
+ };
119
+ }
120
+ function serveNode(handler, opts = {}) {
121
+ const port = opts.port ?? 3e3;
122
+ const node = toNodeHandler(handler);
123
+ const server = (0, import_node_http.createServer)((req, res) => {
124
+ node(req, res).catch((e) => {
125
+ res.statusCode = 500;
126
+ res.end(String(e?.stack || e));
127
+ });
128
+ });
129
+ server.listen(
130
+ port,
131
+ () => opts.onListen ? opts.onListen(port) : console.log(` fluixi → http://localhost:${port}`)
132
+ );
133
+ return server;
134
+ }
135
+ var MIME = {
136
+ ".js": "text/javascript",
137
+ ".mjs": "text/javascript",
138
+ ".css": "text/css",
139
+ ".html": "text/html",
140
+ ".json": "application/json",
141
+ ".svg": "image/svg+xml",
142
+ ".png": "image/png",
143
+ ".jpg": "image/jpeg",
144
+ ".jpeg": "image/jpeg",
145
+ ".webp": "image/webp",
146
+ ".ico": "image/x-icon",
147
+ ".woff": "font/woff",
148
+ ".woff2": "font/woff2",
149
+ ".map": "application/json"
150
+ };
151
+ function withStaticFiles(handler, dir) {
152
+ return async (request) => {
153
+ const { pathname } = new URL(request.url);
154
+ if (pathname !== "/" && (0, import_node_path.extname)(pathname)) {
155
+ const file = (0, import_node_path.join)(dir, decodeURIComponent(pathname));
156
+ try {
157
+ const s = await (0, import_promises.stat)(file);
158
+ if (s.isFile()) {
159
+ const body = import_node_stream.Readable.toWeb((0, import_node_fs.createReadStream)(file));
160
+ return new Response(body, {
161
+ headers: {
162
+ "content-type": MIME[(0, import_node_path.extname)(file).toLowerCase()] ?? "application/octet-stream",
163
+ "content-length": String(s.size)
164
+ }
165
+ });
166
+ }
167
+ } catch {
168
+ }
169
+ }
170
+ return handler(request);
171
+ };
172
+ }
173
+ // Annotate the CommonJS export names for ESM import in node:
174
+ 0 && (module.exports = {
175
+ createRequestHandler,
176
+ nodeToRequest,
177
+ sendResponse,
178
+ serveNode,
179
+ streamDocument,
180
+ toNodeHandler,
181
+ withStaticFiles
182
+ });
package/dist/handler.d.ts CHANGED
@@ -2,7 +2,7 @@ import { type IncomingMessage, type ServerResponse, type Server } from 'node:htt
2
2
  /**
3
3
  * The runtime-neutral request layer. Everything renders through a Web
4
4
  * `Request → Response` handler, so the same core works in Node, Bun, Deno, and
5
- * edge/worker runtimes only the *bridge* (Node http here) is platform-specific.
5
+ * edge/worker runtimes: only the *bridge* (Node http here) is platform-specific.
6
6
  * This is the seam deploy adapters plug into.
7
7
  */
8
8
  /** Produce page HTML for a request URL (pathname + search). */
@@ -15,7 +15,7 @@ export type StreamRenderFn = (url: string, request: Request) => ReadableStream<U
15
15
  * Compose a streamed HTML Response: flush `head` immediately (browser starts fetching
16
16
  * assets while the server awaits data), then pipe the app `body` stream into the mount
17
17
  * point, then `tail` (client script + closing tags). A render error after the head is
18
- * flushed can only error the stream (status is already 200) truncated HTML; callers
18
+ * flushed can only error the stream (status is already 200), truncated HTML; callers
19
19
  * should guard the pre-flush path for a real 500.
20
20
  */
21
21
  export declare function streamDocument(head: string, body: ReadableStream<Uint8Array>, tail: string): Response;
@@ -34,7 +34,7 @@ export interface ServeNodeOptions {
34
34
  /** The reference **Node adapter**: serve a `FetchHandler` over `node:http`. */
35
35
  export declare function serveNode(handler: FetchHandler, opts?: ServeNodeOptions): Server;
36
36
  /**
37
- * Wrap a handler to serve static files from `dir` first (for filesystem runtimes
37
+ * Wrap a handler to serve static files from `dir` first (for filesystem runtimes ,
38
38
  * Node/Bun/Deno). Edge runtimes serve assets via the platform, so they use the bare
39
39
  * handler instead.
40
40
  */
package/dist/handler.js CHANGED
@@ -7,7 +7,7 @@ import { join, extname } from 'node:path';
7
7
  * Compose a streamed HTML Response: flush `head` immediately (browser starts fetching
8
8
  * assets while the server awaits data), then pipe the app `body` stream into the mount
9
9
  * point, then `tail` (client script + closing tags). A render error after the head is
10
- * flushed can only error the stream (status is already 200) truncated HTML; callers
10
+ * flushed can only error the stream (status is already 200), truncated HTML; callers
11
11
  * should guard the pre-flush path for a real 500.
12
12
  */
13
13
  export function streamDocument(head, body, tail) {
@@ -70,7 +70,7 @@ export function nodeToRequest(req) {
70
70
  const method = req.method ?? 'GET';
71
71
  const init = { method, headers };
72
72
  if (method !== 'GET' && method !== 'HEAD') {
73
- init.body = req; // Node readable fine for undici/Node fetch
73
+ init.body = req; // Node readable: fine for undici/Node fetch
74
74
  init.duplex = 'half';
75
75
  }
76
76
  return new Request(url, init);
@@ -124,7 +124,7 @@ const MIME = {
124
124
  '.ico': 'image/x-icon', '.woff': 'font/woff', '.woff2': 'font/woff2', '.map': 'application/json',
125
125
  };
126
126
  /**
127
- * Wrap a handler to serve static files from `dir` first (for filesystem runtimes
127
+ * Wrap a handler to serve static files from `dir` first (for filesystem runtimes ,
128
128
  * Node/Bun/Deno). Edge runtimes serve assets via the platform, so they use the bare
129
129
  * handler instead.
130
130
  */