@fluixi/start 0.1.0-alpha.63 → 0.1.0-alpha.64

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 (41) hide show
  1. package/dist/adapter.d.ts +77 -7
  2. package/dist/adapter.d.ts.map +1 -1
  3. package/dist/adapter.js +131 -44
  4. package/dist/adapters/entry.d.ts +49 -0
  5. package/dist/adapters/entry.d.ts.map +1 -0
  6. package/dist/adapters/entry.js +122 -0
  7. package/dist/adapters/platforms.d.ts +30 -0
  8. package/dist/adapters/platforms.d.ts.map +1 -0
  9. package/dist/adapters/platforms.js +207 -0
  10. package/dist/api.d.ts.map +1 -1
  11. package/dist/api.js +7 -1
  12. package/dist/commands/build.d.ts.map +1 -1
  13. package/dist/commands/build.js +73 -4
  14. package/dist/commands/index.d.ts +14 -0
  15. package/dist/commands/index.d.ts.map +1 -0
  16. package/dist/commands/index.js +13 -0
  17. package/dist/commands/start.d.ts.map +1 -1
  18. package/dist/commands/start.js +10 -1
  19. package/dist/config.d.ts +10 -1
  20. package/dist/config.d.ts.map +1 -1
  21. package/dist/config.js +1 -0
  22. package/dist/document.d.ts +30 -0
  23. package/dist/document.d.ts.map +1 -0
  24. package/dist/document.js +100 -0
  25. package/dist/generated-api.d.ts +5 -0
  26. package/dist/generated-api.d.ts.map +1 -0
  27. package/dist/generated-api.js +26 -0
  28. package/dist/generated-server-fns.d.ts +5 -0
  29. package/dist/generated-server-fns.d.ts.map +1 -0
  30. package/dist/generated-server-fns.js +26 -0
  31. package/dist/handler-core.d.ts +36 -0
  32. package/dist/handler-core.d.ts.map +1 -0
  33. package/dist/handler-core.js +40 -0
  34. package/dist/index.d.ts +5 -7
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +9 -6
  37. package/dist/internal.d.ts +1 -29
  38. package/dist/internal.d.ts.map +1 -1
  39. package/dist/internal.js +10 -102
  40. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  41. package/package.json +39 -6
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The registry of `"use server"` functions found in this app.
3
+ *
4
+ * ```ts
5
+ * import { isServerFnRequest, handleServerFn } from '@fluixi/start/server-fns';
6
+ * ```
7
+ *
8
+ * A typed stand-in: the compiler's server-function plugin intercepts this specifier and
9
+ * serves the generated registry. It exists as a real module so the types ship with the
10
+ * package instead of an ambient `declare module` written into your `src/`.
11
+ *
12
+ * `virtual:fluixi-server-fns` still resolves.
13
+ */
14
+ function missingPlugin() {
15
+ throw new Error("[fluixi] '@fluixi/start/server-fns' was imported but the server-function plugin did not " +
16
+ 'replace it. Build with `fluixi build`/`fluixi dev`, or add the @fluixi/start plugins to ' +
17
+ 'your Vite config.');
18
+ }
19
+ /** Whether this request is an RPC call to a `"use server"` function. */
20
+ export function isServerFnRequest(_request) {
21
+ missingPlugin();
22
+ }
23
+ /** Run the addressed server function and return its response. */
24
+ export function handleServerFn(_request) {
25
+ missingPlugin();
26
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The request handler, assembled from things already in memory.
3
+ *
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
6
+ * every module has to be part of the bundle. So the assembly lives here and takes
7
+ * the template and the module as arguments; the platform entry a deploy adapter
8
+ * generates imports both statically and calls this.
9
+ *
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,
12
+ * all under middleware) is a property of the framework, not of the host.
13
+ */
14
+ import type { ResolvedConfig } from './config.js';
15
+ import { type FetchHandler } from './handler.js';
16
+ /** The shape a built `entry-server.js` exposes. Every field is optional. */
17
+ export interface ServerModule {
18
+ render?: (url: string, request: Request) => string | Promise<string>;
19
+ default?: (url: string, request: Request) => string | Promise<string>;
20
+ renderStream?: (url: string, request: Request) => unknown;
21
+ isServerFnRequest?: (request: Request) => boolean;
22
+ handleServerFn?: (request: Request) => Response | Promise<Response>;
23
+ isApiRequest?: (request: Request) => boolean;
24
+ handleApiRequest?: (request: Request) => Response | Promise<Response>;
25
+ }
26
+ export interface HandlerParts {
27
+ /** `dist/client/index.html`, as a string. */
28
+ template: string;
29
+ /** The imported server entry. `{}` for an SPA that ships none. */
30
+ mod: ServerModule;
31
+ cfg: Pick<ResolvedConfig, 'ssr' | 'mountId'>;
32
+ /** The imported `middleware.js`, if the app has one. */
33
+ middleware?: unknown;
34
+ }
35
+ export declare function createHandlerFrom({ template, mod, cfg, middleware }: HandlerParts): FetchHandler;
36
+ //# sourceMappingURL=handler-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-core.d.ts","sourceRoot":"","sources":["../src/handler-core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAwC,KAAK,YAAY,EAAE,MAAM,cAAc,CAAC;AAGvF,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtE,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;IAC1D,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;IAClD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;IACpE,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;IAC7C,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;CACvE;AAED,MAAM,WAAW,YAAY;IAC3B,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,GAAG,EAAE,YAAY,CAAC;IAClB,GAAG,EAAE,IAAI,CAAC,cAAc,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC;IAC7C,wDAAwD;IACxD,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,YAAY,GAAG,YAAY,CAwChG"}
@@ -0,0 +1,40 @@
1
+ import { injectAppAndHead, splitTemplate, guardHandler } from './document.js';
2
+ import { createRequestHandler, streamDocument } from './handler.js';
3
+ import { composeMiddleware, normalizeMiddleware } from './middleware.js';
4
+ export function createHandlerFrom({ template, mod, cfg, middleware }) {
5
+ // Streaming when the entry exports `renderStream` (a body stream): flush the split
6
+ // template head first, pipe the body, then the tail. Else inject the string render.
7
+ let core;
8
+ if (!cfg.ssr) {
9
+ // SPA: the page is the static shell; the client entry renders it. Server functions +
10
+ // middleware still run through this handler — there's just no SSR render pass.
11
+ core = createRequestHandler(async () => template);
12
+ }
13
+ else if (typeof mod.renderStream === 'function') {
14
+ const { head, tail } = splitTemplate(template, cfg.mountId);
15
+ core = async (request) => {
16
+ const url = new URL(request.url);
17
+ const body = await mod.renderStream(url.pathname + url.search, request);
18
+ return streamDocument(head, body, tail);
19
+ };
20
+ }
21
+ else {
22
+ const render = mod.render ?? mod.default;
23
+ core = createRequestHandler(async (url, request) => injectAppAndHead(template, await render(url, request), cfg.mountId));
24
+ }
25
+ // Server-function RPC dispatch. The entry-server module re-exports the dispatcher, so
26
+ // it shares the registry that the app's "use server" modules populated (a separate
27
+ // import would have an empty registry).
28
+ if (typeof mod.handleServerFn === 'function' && typeof mod.isServerFnRequest === 'function') {
29
+ const render = core;
30
+ core = async (request) => mod.isServerFnRequest(request) ? mod.handleServerFn(request) : render(request);
31
+ }
32
+ // File-based API routes, re-exported by the entry from `@fluixi/start/api-routes`.
33
+ if (typeof mod.handleApiRequest === 'function' && typeof mod.isApiRequest === 'function') {
34
+ const next = core;
35
+ core = async (request) => mod.isApiRequest(request) ? mod.handleApiRequest(request) : next(request);
36
+ }
37
+ if (!middleware)
38
+ return guardHandler(core);
39
+ return guardHandler(composeMiddleware(normalizeMiddleware(middleware), core));
40
+ }
package/dist/index.d.ts CHANGED
@@ -8,15 +8,13 @@
8
8
  export { defineConfig, resolveConfig, loadConfig } from './config.js';
9
9
  export type { FluixiConfig, ResolvedConfig } from './config.js';
10
10
  export { html, svg } from '@fluixi/core';
11
- export { dev } from './commands/dev.js';
12
- export { build } from './commands/build.js';
13
- export { start } from './commands/start.js';
14
- export { prerender } from './commands/prerender.js';
15
- export { injectApp, injectAppAndHead, splitTemplate } from './internal.js';
11
+ export { injectApp, injectAppAndHead, splitTemplate } from './document.js';
16
12
  export { createRequestHandler, toNodeHandler, nodeToRequest, sendResponse, serveNode, streamDocument, withStaticFiles, } from './handler.js';
17
13
  export type { FetchHandler, RenderFn, StreamRenderFn, ServeNodeOptions } from './handler.js';
18
- export { createProdHandler, nodeAdapter, webAdapter } from './adapter.js';
19
- export type { Adapter, AdapterContext } from './adapter.js';
14
+ export { createProdHandler, nodeAdapter, webAdapter, cloudflareAdapter, netlifyAdapter, vercelAdapter, ssrBuildOptions, } from './adapter.js';
15
+ export type { Adapter, AdapterContext, AdapterBuildContext } from './adapter.js';
16
+ export { createHandlerFrom } from './handler-core.js';
17
+ export type { HandlerParts, ServerModule } from './handler-core.js';
20
18
  export { defineMiddleware, composeMiddleware, normalizeMiddleware } from './middleware.js';
21
19
  export type { Middleware, MiddlewareNext } from './middleware.js';
22
20
  export { addInterceptor, clearInterceptors, fetchWithInterceptors } from './interceptors.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGhE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE3E,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,aAAa,EACb,YAAY,EACZ,SAAS,EACT,cAAc,EACd,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAE7F,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1E,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE5D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3F,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAElE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC7F,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC7D,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAI9G,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACtE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGhE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAOzC,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE3E,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,aAAa,EACb,YAAY,EACZ,SAAS,EACT,cAAc,EACd,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAE7F,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,aAAa,EACb,eAAe,GAChB,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEpE,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC3F,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAElE,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC7F,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC7D,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAI9G,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -9,15 +9,18 @@ export { defineConfig, resolveConfig, loadConfig } from './config.js';
9
9
  // The html``/svg`` template tags — so a meta-framework app can author without JSX:
10
10
  // `import { html } from '@fluixi/start'`. Inert markers; the compiler lowers them.
11
11
  export { html, svg } from '@fluixi/core';
12
- export { dev } from './commands/dev.js';
13
- export { build } from './commands/build.js';
14
- export { start } from './commands/start.js';
15
- export { prerender } from './commands/prerender.js';
16
- export { injectApp, injectAppAndHead, splitTemplate } from './internal.js';
12
+ // The commands live at `@fluixi/start/commands`. Re-exporting them here would put
13
+ // the dev server and the bundler behind every runtime import of this package —
14
+ // and a deploy adapter, which inlines everything its entry can reach, would carry
15
+ // vite into a worker.
16
+ // From the leaf, not internal.js: that one reaches the dev server's env loader,
17
+ // and with it vite, which has no business in a runtime import of this package.
18
+ export { injectApp, injectAppAndHead, splitTemplate } from './document.js';
17
19
  // The runtime-neutral request layer — the seam adapters (edge/worker/Bun/Deno) build on.
18
20
  export { createRequestHandler, toNodeHandler, nodeToRequest, sendResponse, serveNode, streamDocument, withStaticFiles, } from './handler.js';
19
21
  // Deploy adapters: the portable prod handler + the node/web reference adapters.
20
- export { createProdHandler, nodeAdapter, webAdapter } from './adapter.js';
22
+ export { createProdHandler, nodeAdapter, webAdapter, cloudflareAdapter, netlifyAdapter, vercelAdapter, ssrBuildOptions, } from './adapter.js';
23
+ export { createHandlerFrom } from './handler-core.js';
21
24
  // Request middleware — a `(request, next) => Response` chain run before render.
22
25
  export { defineMiddleware, composeMiddleware, normalizeMiddleware } from './middleware.js';
23
26
  // HTTP interceptors — request/response/error pipeline around fetch (server-fn RPC + app fetch).
@@ -1,5 +1,4 @@
1
1
  import type { ResolvedConfig } from './config.js';
2
- import type { FetchHandler } from './handler.js';
3
2
  /** The virtual module that registers every `"use server"` fn + re-exports the dispatcher. */
4
3
  export declare const SERVER_FNS_VMOD = "virtual:fluixi-server-fns";
5
4
  /** The virtual module exposing auto-discovered translations: `{ messages, locales, defaultLocale }`. */
@@ -50,34 +49,7 @@ export declare function startTypesPlugin(cfg: ResolvedConfig): any;
50
49
  * bits only (optimizeDeps, ssr.noExternal via config, etc.).
51
50
  */
52
51
  export declare function fluixiPlugins(cfg: ResolvedConfig): Promise<any[]>;
53
- /**
54
- * Inject server-rendered app HTML into the template's mount element
55
- * (`<div id="root"></div>`). Falls back to a `<!--ssr-outlet-->` marker, then to
56
- * just before `</body>`.
57
- */
58
- export declare function injectApp(template: string, appHtml: string, mountId?: string): string;
59
- /**
60
- * Split the template at the mount point for streaming: `head` is everything up to and
61
- * including the open mount tag (flushed first so the browser fetches assets while the
62
- * server awaits data); `tail` is the close tag onward (client script + `</body>`).
63
- * Mirrors injectApp's three cases (mount div, `<!--ssr-outlet-->`, before `</body>`).
64
- */
65
- export declare function splitTemplate(template: string, mountId?: string): {
66
- head: string;
67
- tail: string;
68
- };
69
- /**
70
- * Inject the rendered app into the mount point AND lift any @fluixi/head metadata (carried back as
71
- * a marker by renderRequestAsync) into <head> + <html>. If the page set a title, the template's own
72
- * <title> is dropped so there's only one. No marker → identical to injectApp.
73
- */
74
- export declare function injectAppAndHead(template: string, rendered: string, mountId?: string): string;
75
- /**
76
- * Wrap a fetch handler so a thrown/rejected error becomes a 500 instead of an
77
- * unhandled rejection. `createRequestHandler` already guards the renderer; this
78
- * covers the middleware chain that wraps it. `onError` lets dev map the stack first.
79
- */
80
- export declare function guardHandler(handler: FetchHandler, onError?: (e: unknown) => void): FetchHandler;
52
+ export { injectApp, splitTemplate, injectAppAndHead, guardHandler, } from './document.js';
81
53
  /**
82
54
  * A render fires the route's data fetches eagerly; if the backend is down those
83
55
  * promises can reject AFTER we've already responded, with nothing awaiting them —
@@ -1 +1 @@
1
- {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,6FAA6F;AAC7F,eAAO,MAAM,eAAe,8BAA8B,CAAC;AAE3D,wGAAwG;AACxG,eAAO,MAAM,SAAS,wBAAwB,CAAC;AAE/C;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,GAAG,CAoCrG;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM7E;AA6DD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,GAAG,CA4BzD;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAoBvE;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,SAAS,GAAG,MAAM,CASrF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAwBhG;AASD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,SAAS,GAAG,MAAM,CAW7F;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,YAAY,EACrB,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,GAC7B,YAAY,CAYd;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CASzC"}
1
+ {"version":3,"file":"internal.d.ts","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,6FAA6F;AAC7F,eAAO,MAAM,eAAe,8BAA8B,CAAC;AAE3D,wGAAwG;AACxG,eAAO,MAAM,SAAS,wBAAwB,CAAC;AAE/C;;;;;;;;;;GAUG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,GAAG,CAoCrG;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM7E;AA6DD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,GAAG,GAAG,CA0BzD;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAoBvE;AAED,OAAO,EACL,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,YAAY,GACb,MAAM,eAAe,CAAC;AAIvB;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CASzC"}
package/dist/internal.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { readdirSync, readFileSync, writeFileSync, existsSync } from 'node:fs';
2
2
  import { resolve as resolvePath, relative as relativePath } from 'node:path';
3
- import { extractHeadMarker } from '@fluixi/head';
4
3
  /** The virtual module that registers every `"use server"` fn + re-exports the dispatcher. */
5
4
  export const SERVER_FNS_VMOD = 'virtual:fluixi-server-fns';
6
5
  /** The virtual module exposing auto-discovered translations: `{ messages, locales, defaultLocale }`. */
@@ -142,18 +141,17 @@ export function startTypesPlugin(cfg) {
142
141
  root = config.root || root;
143
142
  },
144
143
  buildStart() {
145
- const blocks = [
146
- `declare module 'virtual:fluixi-server-fns' {`,
147
- ` export function isServerFnRequest(request: Request): boolean;`,
148
- ` export function handleServerFn(request: Request): Promise<Response>;`,
149
- `}`,
150
- `declare module 'virtual:fluixi-api' {`,
151
- ` export function isApiRequest(request: Request): boolean;`,
152
- ` export function handleApiRequest(request: Request): Promise<Response>;`,
153
- `}`,
154
- ];
144
+ // Only i18n is generated now. `virtual:fluixi-server-fns` and `virtual:fluixi-api`
145
+ // have fixed signatures, so they moved to real package entries
146
+ // (`@fluixi/start/server-fns`, `@fluixi/start/api-routes`) whose types ship with the
147
+ // package. i18n cannot follow: its `messages` type is derived from the app's own JSON
148
+ // (`typeof import('./i18n/en.json')`) and its `locales` is a literal tuple — that is
149
+ // what makes keys autocomplete, and no static entry can express it.
150
+ const blocks = [];
155
151
  if (cfg.i18n)
156
152
  blocks.push(...i18nTypeBlock(root, cfg.i18n));
153
+ if (blocks.length === 0)
154
+ return; // no i18n configured — nothing left to declare
157
155
  const content = `// Auto-generated by @fluixi/start — do not edit.\n${blocks.join('\n')}\n`;
158
156
  const file = resolvePath(root, 'src/fluixi-start-env.d.ts');
159
157
  try {
@@ -196,97 +194,7 @@ export async function fluixiPlugins(cfg) {
196
194
  plugins.push(startTypesPlugin(cfg));
197
195
  return plugins;
198
196
  }
199
- /**
200
- * Inject server-rendered app HTML into the template's mount element
201
- * (`<div id="root"></div>`). Falls back to a `<!--ssr-outlet-->` marker, then to
202
- * just before `</body>`.
203
- */
204
- export function injectApp(template, appHtml, mountId = 'root') {
205
- const mount = new RegExp(`<div id=["']${mountId}["']\\s*>\\s*</div>`);
206
- if (mount.test(template)) {
207
- return template.replace(mount, `<div id="${mountId}">${appHtml}</div>`);
208
- }
209
- if (template.includes('<!--ssr-outlet-->')) {
210
- return template.replace('<!--ssr-outlet-->', appHtml);
211
- }
212
- return template.replace('</body>', `<div id="${mountId}">${appHtml}</div></body>`);
213
- }
214
- /**
215
- * Split the template at the mount point for streaming: `head` is everything up to and
216
- * including the open mount tag (flushed first so the browser fetches assets while the
217
- * server awaits data); `tail` is the close tag onward (client script + `</body>`).
218
- * Mirrors injectApp's three cases (mount div, `<!--ssr-outlet-->`, before `</body>`).
219
- */
220
- export function splitTemplate(template, mountId = 'root') {
221
- const mount = new RegExp(`<div id=["']${mountId}["']\\s*>\\s*</div>`);
222
- const m = template.match(mount);
223
- if (m && m.index !== undefined) {
224
- return {
225
- head: template.slice(0, m.index) + `<div id="${mountId}">`,
226
- tail: `</div>` + template.slice(m.index + m[0].length),
227
- };
228
- }
229
- const outlet = template.indexOf('<!--ssr-outlet-->');
230
- if (outlet !== -1) {
231
- return {
232
- head: template.slice(0, outlet),
233
- tail: template.slice(outlet + '<!--ssr-outlet-->'.length),
234
- };
235
- }
236
- const body = template.indexOf('</body>');
237
- if (body !== -1) {
238
- return {
239
- head: template.slice(0, body) + `<div id="${mountId}">`,
240
- tail: `</div>` + template.slice(body),
241
- };
242
- }
243
- return { head: template, tail: '' };
244
- }
245
- /** Set/replace an attribute on the template's <html> tag (e.g. lang). */
246
- function setHtmlAttr(html, name, value) {
247
- const existing = new RegExp(`(<html\\b[^>]*?)\\s${name}="[^"]*"`, 'i');
248
- if (existing.test(html))
249
- return html.replace(existing, `$1 ${name}="${value}"`);
250
- return html.replace(/<html\b/i, `<html ${name}="${value}"`);
251
- }
252
- /**
253
- * Inject the rendered app into the mount point AND lift any @fluixi/head metadata (carried back as
254
- * a marker by renderRequestAsync) into <head> + <html>. If the page set a title, the template's own
255
- * <title> is dropped so there's only one. No marker → identical to injectApp.
256
- */
257
- export function injectAppAndHead(template, rendered, mountId = 'root') {
258
- const { head, body } = extractHeadMarker(rendered);
259
- let html = injectApp(template, body, mountId);
260
- if (head) {
261
- if (head.headHtml) {
262
- if (/<title[\s>]/i.test(head.headHtml))
263
- html = html.replace(/<title>[\s\S]*?<\/title>/i, '');
264
- html = html.replace('</head>', `${head.headHtml}</head>`);
265
- }
266
- for (const [k, v] of Object.entries(head.htmlAttrs))
267
- html = setHtmlAttr(html, k, v);
268
- }
269
- return html;
270
- }
271
- /**
272
- * Wrap a fetch handler so a thrown/rejected error becomes a 500 instead of an
273
- * unhandled rejection. `createRequestHandler` already guards the renderer; this
274
- * covers the middleware chain that wraps it. `onError` lets dev map the stack first.
275
- */
276
- export function guardHandler(handler, onError) {
277
- return async (request) => {
278
- try {
279
- return await handler(request);
280
- }
281
- catch (e) {
282
- onError?.(e);
283
- return new Response(String(e?.stack || e), {
284
- status: 500,
285
- headers: { 'content-type': 'text/plain; charset=utf-8' },
286
- });
287
- }
288
- };
289
- }
197
+ export { injectApp, splitTemplate, injectAppAndHead, guardHandler, } from './document.js';
290
198
  let _guardsInstalled = false;
291
199
  /**
292
200
  * A render fires the route's data fetches eagerly; if the backend is down those