@fluixi/start 0.1.0-alpha.43
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/LICENSE +21 -0
- package/README.md +101 -0
- package/dist/adapter.d.ts +32 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +78 -0
- package/dist/api.d.ts +35 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +120 -0
- package/dist/commands/build.d.ts +8 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +67 -0
- package/dist/commands/dev.d.ts +8 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +91 -0
- package/dist/commands/prerender.d.ts +11 -0
- package/dist/commands/prerender.d.ts.map +1 -0
- package/dist/commands/prerender.js +78 -0
- package/dist/commands/start.d.ts +9 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +18 -0
- package/dist/config.d.ts +82 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +116 -0
- package/dist/di.d.ts +63 -0
- package/dist/di.d.ts.map +1 -0
- package/dist/di.js +210 -0
- package/dist/handler.d.ts +42 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +152 -0
- package/dist/head.d.ts +14 -0
- package/dist/head.d.ts.map +1 -0
- package/dist/head.js +13 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/interceptors.d.ts +15 -0
- package/dist/interceptors.d.ts.map +1 -0
- package/dist/interceptors.js +47 -0
- package/dist/internal.d.ts +87 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +302 -0
- package/dist/middleware.d.ts +38 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +39 -0
- package/dist/router.d.ts +13 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +12 -0
- package/dist/server-fn-setup.d.ts +8 -0
- package/dist/server-fn-setup.d.ts.map +1 -0
- package/dist/server-fn-setup.js +17 -0
- package/dist/server-fn.d.ts +33 -0
- package/dist/server-fn.d.ts.map +1 -0
- package/dist/server-fn.js +96 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -0
- package/dist/ui.d.ts +35 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +99 -0
- package/package.json +86 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { writeFile, mkdir } from 'node:fs/promises';
|
|
2
|
+
import { resolve, join, dirname } from 'node:path';
|
|
3
|
+
import { resolveConfig } from '../config.js';
|
|
4
|
+
import { createProdHandler } from '../adapter.js';
|
|
5
|
+
import { step, done, c } from '../ui.js';
|
|
6
|
+
// dist/client path for a route url: '/' → index.html, '/about' → about/index.html
|
|
7
|
+
export function htmlPath(url) {
|
|
8
|
+
const clean = url.split(/[?#]/)[0].replace(/\/+$/, '');
|
|
9
|
+
return clean === '' ? 'index.html' : `${clean.replace(/^\//, '')}/index.html`;
|
|
10
|
+
}
|
|
11
|
+
// internal links from rendered HTML — skip files (.js/.css/…), anchors, queries, external
|
|
12
|
+
export function extractLinks(html) {
|
|
13
|
+
const out = new Set();
|
|
14
|
+
for (const m of html.matchAll(/href=["'](\/[^"'#?]*)["']/g)) {
|
|
15
|
+
const href = m[1];
|
|
16
|
+
if (/\.[a-z0-9]+$/i.test(href))
|
|
17
|
+
continue;
|
|
18
|
+
out.add(href.replace(/\/+$/, '') || '/');
|
|
19
|
+
}
|
|
20
|
+
return [...out];
|
|
21
|
+
}
|
|
22
|
+
const norm = (u) => {
|
|
23
|
+
const p = (u.startsWith('/') ? u : '/' + u).replace(/\/+$/, '');
|
|
24
|
+
return p || '/';
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Render routes to static HTML in dist/client, via the real prod handler — so each page is
|
|
28
|
+
* byte-identical to what `fluixi start` would serve (assets, __FX_DATA__, hydration), it just
|
|
29
|
+
* lives on disk. Seeds from `prerender.routes` (default `/`) and, when `crawl`, follows internal
|
|
30
|
+
* links. Returns the written route paths.
|
|
31
|
+
*/
|
|
32
|
+
export async function prerender(config = {}) {
|
|
33
|
+
const cfg = resolveConfig(config);
|
|
34
|
+
if (!cfg.prerender)
|
|
35
|
+
return [];
|
|
36
|
+
if (!cfg.ssr) {
|
|
37
|
+
process.stdout.write(` ${c.yellow('!')} prerender needs ssr:true — skipped\n`);
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
step('prerender');
|
|
41
|
+
const clientDir = resolve(cfg.root, 'dist/client');
|
|
42
|
+
const handler = await createProdHandler(config);
|
|
43
|
+
const { routes, crawl } = cfg.prerender;
|
|
44
|
+
const queue = routes.map(norm);
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
const written = [];
|
|
47
|
+
while (queue.length) {
|
|
48
|
+
const url = queue.shift();
|
|
49
|
+
if (seen.has(url))
|
|
50
|
+
continue;
|
|
51
|
+
seen.add(url);
|
|
52
|
+
let html;
|
|
53
|
+
try {
|
|
54
|
+
const res = await handler(new Request('http://prerender.local' + url));
|
|
55
|
+
if (!res.ok) {
|
|
56
|
+
process.stdout.write(` ${c.yellow('skip')} ${url} ${c.gray('→ ' + res.status)}\n`);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (!(res.headers.get('content-type') || '').includes('html'))
|
|
60
|
+
continue;
|
|
61
|
+
html = await res.text();
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
process.stdout.write(` ${c.yellow('skip')} ${url} ${c.gray('→ ' + e.message)}\n`);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const file = join(clientDir, htmlPath(url));
|
|
68
|
+
await mkdir(dirname(file), { recursive: true });
|
|
69
|
+
await writeFile(file, html);
|
|
70
|
+
written.push(url);
|
|
71
|
+
if (crawl)
|
|
72
|
+
for (const link of extractLinks(html))
|
|
73
|
+
if (!seen.has(link))
|
|
74
|
+
queue.push(link);
|
|
75
|
+
}
|
|
76
|
+
done(`${c.bold('prerendered')} ${c.bold(String(written.length))} page(s) ${c.gray('→ dist/client')}\n`);
|
|
77
|
+
return written;
|
|
78
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { FluixiConfig } from '../config.js';
|
|
2
|
+
/**
|
|
3
|
+
* Production server: build the runtime-neutral SSR fetch handler from the `fluixi
|
|
4
|
+
* build` output and serve it via the Node adapter (static files from dist/client +
|
|
5
|
+
* SSR for everything else). Other runtimes (edge/Bun/Deno) use the same
|
|
6
|
+
* `createProdHandler` with their own adapter — `export default { fetch }`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function start(config?: FluixiConfig): Promise<void>;
|
|
9
|
+
//# sourceMappingURL=start.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAKjD;;;;;GAKG;AACH,wBAAsB,KAAK,CAAC,MAAM,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAQpE"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { resolveConfig } from '../config.js';
|
|
3
|
+
import { installCrashGuards, loadServerEnv } from '../internal.js';
|
|
4
|
+
import { createProdHandler, nodeAdapter } from '../adapter.js';
|
|
5
|
+
/**
|
|
6
|
+
* Production server: build the runtime-neutral SSR fetch handler from the `fluixi
|
|
7
|
+
* build` output and serve it via the Node adapter (static files from dist/client +
|
|
8
|
+
* SSR for everything else). Other runtimes (edge/Bun/Deno) use the same
|
|
9
|
+
* `createProdHandler` with their own adapter — `export default { fetch }`.
|
|
10
|
+
*/
|
|
11
|
+
export async function start(config = {}) {
|
|
12
|
+
const cfg = resolveConfig(config);
|
|
13
|
+
installCrashGuards();
|
|
14
|
+
await loadServerEnv(cfg.root, 'production'); // .env → process.env (server-only secrets)
|
|
15
|
+
const clientDir = resolve(cfg.root, 'dist/client');
|
|
16
|
+
const handler = await createProdHandler(cfg);
|
|
17
|
+
await nodeAdapter.serve({ handler, cfg, clientDir });
|
|
18
|
+
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fluixi app config. Place in `fluixi.config.{ts,js,mjs}` at the app root and wrap
|
|
3
|
+
* with `defineConfig` for types. Everything is optional — the defaults match the
|
|
4
|
+
* conventional layout (src/entry-server.ts, src/entry-client.ts, index.html).
|
|
5
|
+
*/
|
|
6
|
+
export interface FluixiConfig {
|
|
7
|
+
/** App root directory. Default: process.cwd(). */
|
|
8
|
+
root?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Render mode. `true` (default) = SSR: the server entry renders HTML per request. `false`
|
|
11
|
+
* = SPA: the server serves the static shell and the client renders — but you still get the
|
|
12
|
+
* full @fluixi/start server (file routing, middleware, env, and `/_server` server functions).
|
|
13
|
+
* The same app code runs either way; flip this to choose where the first paint happens.
|
|
14
|
+
*/
|
|
15
|
+
ssr?: boolean;
|
|
16
|
+
/** Dev/prod server port. Default: 3000. */
|
|
17
|
+
port?: number;
|
|
18
|
+
/** Server entry exporting `render(url): string | Promise<string>`. Default: src/entry-server.ts */
|
|
19
|
+
serverEntry?: string;
|
|
20
|
+
/** Client hydration entry. Default: src/entry-client.ts */
|
|
21
|
+
clientEntry?: string;
|
|
22
|
+
/** HTML template. Default: index.html */
|
|
23
|
+
template?: string;
|
|
24
|
+
/** Element id the app HTML is injected into (`<div id="…"></div>`). Default: root */
|
|
25
|
+
mountId?: string;
|
|
26
|
+
/** File-routes directory (passed to fluixiRoutesPlugin). Default: src/routes */
|
|
27
|
+
routesDir?: string;
|
|
28
|
+
/** File-based API routes directory; files export GET/POST/… handlers served at `/api/*`. Default: src/api */
|
|
29
|
+
apiDir?: string;
|
|
30
|
+
/** Request middleware module (default-exports `defineMiddleware([...])`). Default: src/middleware.ts */
|
|
31
|
+
middleware?: string;
|
|
32
|
+
/** SSR deps Vite must bundle (no-external) — e.g. ESM packages without file extensions. */
|
|
33
|
+
ssrNoExternal?: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Built-in i18n auto-discovery. Scans `<dir>/<locale>.json` and exposes them as
|
|
36
|
+
* `virtual:fluixi-i18n` (`{ messages, locales, defaultLocale }`) to feed `createI18n`.
|
|
37
|
+
* `false` disables it; an object configures the folder / locales / default. Default: enabled
|
|
38
|
+
* with `src/i18n` — a no-op until that folder has `*.json` files, so it's effectively opt-in.
|
|
39
|
+
*/
|
|
40
|
+
i18n?: boolean | {
|
|
41
|
+
dir?: string;
|
|
42
|
+
defaultLocale?: string;
|
|
43
|
+
locales?: string[];
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Static Site Generation. When set, `fluixi build` prerenders routes to static HTML in
|
|
47
|
+
* `dist/client` (served by any static host; hydrates on the client). `true` prerenders `/`
|
|
48
|
+
* and crawls internal `<a href>` links. An object sets explicit `routes` (for dynamic paths
|
|
49
|
+
* with no incoming link) and toggles `crawl`. Requires `ssr: true` (it renders to prerender).
|
|
50
|
+
*/
|
|
51
|
+
prerender?: boolean | {
|
|
52
|
+
routes?: string[];
|
|
53
|
+
crawl?: boolean;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** Resolved i18n discovery config (or `false` when disabled). */
|
|
57
|
+
export interface ResolvedI18n {
|
|
58
|
+
dir: string;
|
|
59
|
+
defaultLocale?: string;
|
|
60
|
+
locales?: string[];
|
|
61
|
+
}
|
|
62
|
+
/** Resolved SSG config (or `false` when disabled). */
|
|
63
|
+
export interface ResolvedPrerender {
|
|
64
|
+
routes: string[];
|
|
65
|
+
crawl: boolean;
|
|
66
|
+
}
|
|
67
|
+
export type ResolvedConfig = Required<Omit<FluixiConfig, 'ssrNoExternal' | 'i18n' | 'prerender'>> & {
|
|
68
|
+
ssrNoExternal: string[];
|
|
69
|
+
i18n: ResolvedI18n | false;
|
|
70
|
+
prerender: ResolvedPrerender | false;
|
|
71
|
+
};
|
|
72
|
+
/** Identity helper for editor types in `fluixi.config.ts`. */
|
|
73
|
+
export declare function defineConfig(config: FluixiConfig): FluixiConfig;
|
|
74
|
+
/** Apply defaults. */
|
|
75
|
+
export declare function resolveConfig(config?: FluixiConfig): ResolvedConfig;
|
|
76
|
+
/**
|
|
77
|
+
* Load `fluixi.config.{ts,mts,mjs,js}` from the app root. TypeScript configs are
|
|
78
|
+
* bundled on the fly with esbuild and imported; plain ESM is imported directly.
|
|
79
|
+
* No config is fine too — every field has a default.
|
|
80
|
+
*/
|
|
81
|
+
export declare function loadConfig(root?: string): Promise<FluixiConfig>;
|
|
82
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mGAAmG;IACnG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6GAA6G;IAC7G,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wGAAwG;IACxG,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2FAA2F;IAC3F,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,OAAO,GAAG;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAC9E;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG;QAAE,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAC9D;AAED,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,sDAAsD;AACtD,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,EAAE,eAAe,GAAG,MAAM,GAAG,WAAW,CAAC,CAAC,GAAG;IAClG,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,IAAI,EAAE,YAAY,GAAG,KAAK,CAAC;IAC3B,SAAS,EAAE,iBAAiB,GAAG,KAAK,CAAC;CACtC,CAAC;AAEF,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAE/D;AAED,sBAAsB;AACtB,wBAAgB,aAAa,CAAC,MAAM,GAAE,YAAiB,GAAG,cAAc,CAiBvE;AAgCD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,IAAI,SAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAM5E"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { pathToFileURL } from 'node:url';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { writeFile, unlink } from 'node:fs/promises';
|
|
4
|
+
import { resolve, isAbsolute } from 'node:path';
|
|
5
|
+
/** Identity helper for editor types in `fluixi.config.ts`. */
|
|
6
|
+
export function defineConfig(config) {
|
|
7
|
+
return config;
|
|
8
|
+
}
|
|
9
|
+
/** Apply defaults. */
|
|
10
|
+
export function resolveConfig(config = {}) {
|
|
11
|
+
const root = resolve(config.root ?? process.cwd());
|
|
12
|
+
return {
|
|
13
|
+
root,
|
|
14
|
+
ssr: config.ssr ?? true,
|
|
15
|
+
port: config.port ?? 3000,
|
|
16
|
+
serverEntry: config.serverEntry ?? 'src/entry-server.ts',
|
|
17
|
+
clientEntry: config.clientEntry ?? 'src/entry-client.ts',
|
|
18
|
+
template: config.template ?? 'index.html',
|
|
19
|
+
mountId: config.mountId ?? 'root',
|
|
20
|
+
routesDir: config.routesDir ?? 'src/routes',
|
|
21
|
+
apiDir: config.apiDir ?? 'src/api',
|
|
22
|
+
middleware: config.middleware ?? 'src/middleware.ts',
|
|
23
|
+
ssrNoExternal: config.ssrNoExternal ?? [],
|
|
24
|
+
i18n: resolveI18n(config.i18n),
|
|
25
|
+
prerender: resolvePrerender(config.prerender),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/** `false`/unset → disabled; `true` → crawl from `/`; object → explicit routes + crawl toggle. */
|
|
29
|
+
function resolvePrerender(prerender) {
|
|
30
|
+
if (!prerender)
|
|
31
|
+
return false;
|
|
32
|
+
const opts = typeof prerender === 'object' ? prerender : {};
|
|
33
|
+
return {
|
|
34
|
+
routes: opts.routes?.length ? opts.routes : ['/'],
|
|
35
|
+
crawl: opts.crawl ?? true,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/** `false` → disabled; otherwise a folder (default `src/i18n`) + optional locales/default. */
|
|
39
|
+
function resolveI18n(i18n) {
|
|
40
|
+
if (i18n === false)
|
|
41
|
+
return false;
|
|
42
|
+
const opts = typeof i18n === 'object' ? i18n : {};
|
|
43
|
+
return {
|
|
44
|
+
dir: opts.dir ?? 'src/i18n',
|
|
45
|
+
defaultLocale: opts.defaultLocale,
|
|
46
|
+
locales: opts.locales,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
// Config filenames probed, in order. TS first so a `.ts` config wins over a stray
|
|
50
|
+
// build artifact; `.mts`/`.mjs`/`.js` cover plain-ESM configs.
|
|
51
|
+
const CONFIG_NAMES = [
|
|
52
|
+
'fluixi.config.ts',
|
|
53
|
+
'fluixi.config.mts',
|
|
54
|
+
'fluixi.config.mjs',
|
|
55
|
+
'fluixi.config.js',
|
|
56
|
+
];
|
|
57
|
+
/**
|
|
58
|
+
* Load `fluixi.config.{ts,mts,mjs,js}` from the app root. TypeScript configs are
|
|
59
|
+
* bundled on the fly with esbuild and imported; plain ESM is imported directly.
|
|
60
|
+
* No config is fine too — every field has a default.
|
|
61
|
+
*/
|
|
62
|
+
export async function loadConfig(root = process.cwd()) {
|
|
63
|
+
for (const name of CONFIG_NAMES) {
|
|
64
|
+
const file = resolve(root, name);
|
|
65
|
+
if (existsSync(file))
|
|
66
|
+
return loadConfigFile(file);
|
|
67
|
+
}
|
|
68
|
+
return {};
|
|
69
|
+
}
|
|
70
|
+
async function loadConfigFile(file) {
|
|
71
|
+
// Plain ESM (.mjs/.js) — import directly, no transform needed.
|
|
72
|
+
if (!/\.[mc]?ts$/.test(file)) {
|
|
73
|
+
const mod = await import(/* @vite-ignore */ pathToFileURL(file).href);
|
|
74
|
+
return (mod.default ?? mod);
|
|
75
|
+
}
|
|
76
|
+
// TypeScript — bundle with esbuild, keeping bare specifiers external so
|
|
77
|
+
// `import { defineConfig } from '@fluixi/start'` and node builtins resolve from
|
|
78
|
+
// the app. Relative imports (shared constants, env helpers) ARE bundled in. The
|
|
79
|
+
// output is written to a temp `.mjs` beside the source so those externals resolve
|
|
80
|
+
// against the app's node_modules, then imported and removed.
|
|
81
|
+
const esbuild = await import('esbuild');
|
|
82
|
+
const { outputFiles } = await esbuild.build({
|
|
83
|
+
entryPoints: [file],
|
|
84
|
+
bundle: true,
|
|
85
|
+
write: false,
|
|
86
|
+
format: 'esm',
|
|
87
|
+
platform: 'node',
|
|
88
|
+
target: `node${process.versions.node.split('.')[0]}`,
|
|
89
|
+
sourcemap: 'inline',
|
|
90
|
+
plugins: [
|
|
91
|
+
{
|
|
92
|
+
name: 'fluixi:externalize-bare',
|
|
93
|
+
setup(b) {
|
|
94
|
+
b.onResolve({ filter: /.*/ }, (args) => {
|
|
95
|
+
if (args.kind === 'entry-point')
|
|
96
|
+
return null;
|
|
97
|
+
if (args.path.startsWith('.') || isAbsolute(args.path))
|
|
98
|
+
return null;
|
|
99
|
+
return { path: args.path, external: true };
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
const tempFile = `${file}.timestamp-${Date.now()}-${Math.random()
|
|
106
|
+
.toString(16)
|
|
107
|
+
.slice(2)}.mjs`;
|
|
108
|
+
await writeFile(tempFile, outputFiles[0].text);
|
|
109
|
+
try {
|
|
110
|
+
const mod = await import(/* @vite-ignore */ pathToFileURL(tempFile).href);
|
|
111
|
+
return (mod.default ?? mod);
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
await unlink(tempFile).catch(() => { });
|
|
115
|
+
}
|
|
116
|
+
}
|
package/dist/di.d.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export interface Token<T> {
|
|
2
|
+
readonly id: symbol;
|
|
3
|
+
readonly name: string;
|
|
4
|
+
/** Tree-shakable root default (Angular's `providedIn: 'root'`). Used when no provider is found. */
|
|
5
|
+
readonly factory?: () => T;
|
|
6
|
+
}
|
|
7
|
+
/** Create a typed injection token. Give it a `factory` to be self-providing at the root. */
|
|
8
|
+
export declare function injectionToken<T>(name: string, options?: {
|
|
9
|
+
factory?: () => T;
|
|
10
|
+
}): Token<T>;
|
|
11
|
+
export type Provider<T = any> = {
|
|
12
|
+
provide: Token<T>;
|
|
13
|
+
useValue: T;
|
|
14
|
+
} | {
|
|
15
|
+
provide: Token<T>;
|
|
16
|
+
useClass: new (...args: any[]) => T;
|
|
17
|
+
deps?: ReadonlyArray<Token<any>>;
|
|
18
|
+
} | {
|
|
19
|
+
provide: Token<T>;
|
|
20
|
+
useFactory: (...deps: any[]) => T;
|
|
21
|
+
deps?: ReadonlyArray<Token<any>>;
|
|
22
|
+
} | {
|
|
23
|
+
provide: Token<T>;
|
|
24
|
+
useExisting: Token<T>;
|
|
25
|
+
};
|
|
26
|
+
interface Rec {
|
|
27
|
+
provider: Provider;
|
|
28
|
+
built: boolean;
|
|
29
|
+
building: boolean;
|
|
30
|
+
value: unknown;
|
|
31
|
+
}
|
|
32
|
+
export declare class Injector {
|
|
33
|
+
#private;
|
|
34
|
+
readonly parent: Injector | null;
|
|
35
|
+
readonly records: Map<symbol, Rec>;
|
|
36
|
+
constructor(parent?: Injector | null);
|
|
37
|
+
register(providers: ReadonlyArray<Provider>): this;
|
|
38
|
+
/** Resolve a token from this injector, walking up the parent chain, then the token's factory. */
|
|
39
|
+
get<T>(token: Token<T>): T;
|
|
40
|
+
/** Run every built service's `onDestroy()`. Owner-scoped injectors call this on cleanup. */
|
|
41
|
+
dispose(): void;
|
|
42
|
+
}
|
|
43
|
+
/** Resolve a dependency from the current injection context. */
|
|
44
|
+
export declare function inject<T>(token: Token<T>): T;
|
|
45
|
+
/**
|
|
46
|
+
* Provide services for the CURRENT component/route and its descendants (owner-scoped). The
|
|
47
|
+
* injector is disposed — running each service's `onDestroy()` — when the component unmounts.
|
|
48
|
+
*/
|
|
49
|
+
export declare function provide(providers: ReadonlyArray<Provider>): Injector;
|
|
50
|
+
/**
|
|
51
|
+
* Provide app-wide services. On the client these are singletons; during SSR each request gets
|
|
52
|
+
* its OWN instances (the root injector is per-request) — so a stateful root service can never
|
|
53
|
+
* leak between concurrent requests. Call at module load (e.g. in entry-client / entry-server).
|
|
54
|
+
*/
|
|
55
|
+
export declare function provideRoot(providers: ReadonlyArray<Provider>): void;
|
|
56
|
+
/** Run `fn` with an explicit injector as the injection context (e.g. inside a server function). */
|
|
57
|
+
export declare function runInInjectionContext<R>(injector: Injector, fn: () => R): R;
|
|
58
|
+
/** The injector for the current scope (advanced — for passing to runInInjectionContext later). */
|
|
59
|
+
export declare function getInjector(): Injector;
|
|
60
|
+
/** A standalone injector (parent defaults to the root) — for tests / manual hierarchies. */
|
|
61
|
+
export declare function createInjector(providers: ReadonlyArray<Provider>, parent?: Injector | null): Injector;
|
|
62
|
+
export {};
|
|
63
|
+
//# sourceMappingURL=di.d.ts.map
|
package/dist/di.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"di.d.ts","sourceRoot":"","sources":["../src/di.ts"],"names":[],"mappings":"AAiCA,MAAM,WAAW,KAAK,CAAC,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mGAAmG;IACnG,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;CAC5B;AAED,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAEzF;AAED,MAAM,MAAM,QAAQ,CAAC,CAAC,GAAG,GAAG,IACxB;IAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAAC,QAAQ,EAAE,CAAC,CAAA;CAAE,GAClC;IAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAAC,QAAQ,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;CAAE,GAC5F;IAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAAC,UAAU,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAAC,IAAI,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;CAAE,GAC1F;IAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAAC,WAAW,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;CAAE,CAAC;AAIjD,UAAU,GAAG;IACX,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,qBAAa,QAAQ;;IAEP,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,IAAI;IAD5C,QAAQ,CAAC,OAAO,mBAA0B;gBACrB,MAAM,GAAE,QAAQ,GAAG,IAAW;IAEnD,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,IAAI;IAOlD,iGAAiG;IACjG,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;IA4C1B,4FAA4F;IAC5F,OAAO,IAAI,IAAI;CAahB;AAoDD,+DAA+D;AAC/D,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAE5C;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAapE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,IAAI,CAKpE;AAED,mGAAmG;AACnG,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAQ3E;AAED,kGAAkG;AAClG,wBAAgB,WAAW,IAAI,QAAQ,CAEtC;AAED,4FAA4F;AAC5F,wBAAgB,cAAc,CAAC,SAAS,EAAE,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,IAAI,GAAG,QAAQ,CAErG"}
|
package/dist/di.js
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fluixi/start/di — dependency injection, the Angular way, but SSR-correct and reactive.
|
|
3
|
+
*
|
|
4
|
+
* Three things make this distinct from Angular's DI:
|
|
5
|
+
*
|
|
6
|
+
* 1. **SSR request isolation, for free.** A "root" provider is a singleton on the CLIENT but
|
|
7
|
+
* a fresh instance PER REQUEST on the server — because the root injector lives in the
|
|
8
|
+
* per-request `RequestContext` (the same machinery that isolates resources/markers). So
|
|
9
|
+
* a stateful root service (a per-user cache, a DB unit-of-work) never leaks across two
|
|
10
|
+
* concurrent SSR requests. No zone, no `providedIn` ceremony, no manual request injectors.
|
|
11
|
+
*
|
|
12
|
+
* 2. **The reactive owner tree IS the injector hierarchy.** `provide()` in a component
|
|
13
|
+
* attaches a child injector to that component's reactive owner; descendants `inject()`
|
|
14
|
+
* by walking the owner chain — exactly how context resolves. Component-scoped services
|
|
15
|
+
* are disposed with the component (their `onDestroy()` runs on owner cleanup).
|
|
16
|
+
*
|
|
17
|
+
* 3. **Function injection, tree-shakable tokens.** No decorators/reflect-metadata. A token
|
|
18
|
+
* can carry a `factory` (Angular's `providedIn: 'root'`), so an unused service is dropped
|
|
19
|
+
* by the bundler; a used one self-provides at the root with no registration.
|
|
20
|
+
*
|
|
21
|
+
* const Logger = injectionToken<Logger>('Logger', { factory: () => new ConsoleLogger() });
|
|
22
|
+
* class UserService { #log = inject(Logger); load() { this.#log.info('…'); } }
|
|
23
|
+
* const Users = injectionToken('Users', { factory: () => new UserService() });
|
|
24
|
+
* // in a component / route / server function:
|
|
25
|
+
* const users = inject(Users);
|
|
26
|
+
*
|
|
27
|
+
* Client-safe: imports only `@fluixi/core` (owner) + `@fluixi/dom`'s pure `getRequestContext`.
|
|
28
|
+
*/
|
|
29
|
+
import { getOwner, onCleanup } from '@fluixi/core';
|
|
30
|
+
import { getRequestContext } from '@fluixi/dom';
|
|
31
|
+
/** Create a typed injection token. Give it a `factory` to be self-providing at the root. */
|
|
32
|
+
export function injectionToken(name, options) {
|
|
33
|
+
return { id: Symbol(name), name, factory: options?.factory };
|
|
34
|
+
}
|
|
35
|
+
export class Injector {
|
|
36
|
+
parent;
|
|
37
|
+
records = new Map();
|
|
38
|
+
constructor(parent = null) {
|
|
39
|
+
this.parent = parent;
|
|
40
|
+
}
|
|
41
|
+
register(providers) {
|
|
42
|
+
for (const p of providers) {
|
|
43
|
+
this.records.set(p.provide.id, { provider: p, built: false, building: false, value: undefined });
|
|
44
|
+
}
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
/** Resolve a token from this injector, walking up the parent chain, then the token's factory. */
|
|
48
|
+
get(token) {
|
|
49
|
+
let inj = this;
|
|
50
|
+
while (inj) {
|
|
51
|
+
const rec = inj.records.get(token.id);
|
|
52
|
+
if (rec)
|
|
53
|
+
return inj.#build(token, rec);
|
|
54
|
+
inj = inj.parent;
|
|
55
|
+
}
|
|
56
|
+
// Unprovided but self-providing → instantiate at the ROOT of this chain (so it's a
|
|
57
|
+
// per-request singleton on the server, an app singleton on the client).
|
|
58
|
+
if (token.factory)
|
|
59
|
+
return rootOf(this).#fromFactory(token);
|
|
60
|
+
throw new Error(`[fluixi/di] No provider for "${token.name}". Register it with provide()/provideRoot(), ` +
|
|
61
|
+
`or give the token a { factory } to self-provide.`);
|
|
62
|
+
}
|
|
63
|
+
#build(token, rec) {
|
|
64
|
+
if (rec.built)
|
|
65
|
+
return rec.value;
|
|
66
|
+
if (rec.building)
|
|
67
|
+
throw new Error(`[fluixi/di] Circular dependency while resolving "${token.name}".`);
|
|
68
|
+
rec.building = true;
|
|
69
|
+
const p = rec.provider;
|
|
70
|
+
try {
|
|
71
|
+
if ('useValue' in p)
|
|
72
|
+
rec.value = p.useValue;
|
|
73
|
+
else if ('useExisting' in p)
|
|
74
|
+
rec.value = this.get(p.useExisting);
|
|
75
|
+
else {
|
|
76
|
+
const deps = (p.deps ?? []).map((d) => this.get(d)); // deps resolve from where the provider lives
|
|
77
|
+
rec.value = 'useClass' in p ? new p.useClass(...deps) : p.useFactory(...deps);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
rec.building = false;
|
|
82
|
+
}
|
|
83
|
+
rec.built = true;
|
|
84
|
+
return rec.value;
|
|
85
|
+
}
|
|
86
|
+
#fromFactory(token) {
|
|
87
|
+
let rec = this.records.get(token.id);
|
|
88
|
+
if (!rec) {
|
|
89
|
+
rec = { provider: { provide: token, useFactory: token.factory }, built: false, building: false, value: undefined };
|
|
90
|
+
this.records.set(token.id, rec);
|
|
91
|
+
}
|
|
92
|
+
return this.#build(token, rec);
|
|
93
|
+
}
|
|
94
|
+
/** Run every built service's `onDestroy()`. Owner-scoped injectors call this on cleanup. */
|
|
95
|
+
dispose() {
|
|
96
|
+
for (const rec of this.records.values()) {
|
|
97
|
+
const v = rec.value;
|
|
98
|
+
if (rec.built && v && typeof v.onDestroy === 'function') {
|
|
99
|
+
try {
|
|
100
|
+
v.onDestroy();
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
console.error('[fluixi/di] onDestroy threw —', e);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
this.records.clear();
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function rootOf(inj) {
|
|
111
|
+
let i = inj;
|
|
112
|
+
while (i.parent)
|
|
113
|
+
i = i.parent;
|
|
114
|
+
return i;
|
|
115
|
+
}
|
|
116
|
+
// ── Scoping: root (per-request on server / singleton on client) + owner tree ──
|
|
117
|
+
const _rootProviders = [];
|
|
118
|
+
const _reqInjectors = new WeakMap(); // per-request root (server)
|
|
119
|
+
const _ownerInjectors = new WeakMap(); // per reactive owner (component scope)
|
|
120
|
+
let _clientRoot = null;
|
|
121
|
+
let _explicit = null;
|
|
122
|
+
/** The root injector — per-request during SSR (isolated), a module singleton on the client. */
|
|
123
|
+
function rootInjector() {
|
|
124
|
+
const ctx = getRequestContext();
|
|
125
|
+
if (ctx) {
|
|
126
|
+
let inj = _reqInjectors.get(ctx);
|
|
127
|
+
if (!inj)
|
|
128
|
+
_reqInjectors.set(ctx, (inj = new Injector(null).register(_rootProviders)));
|
|
129
|
+
return inj;
|
|
130
|
+
}
|
|
131
|
+
return (_clientRoot ??= new Injector(null).register(_rootProviders));
|
|
132
|
+
}
|
|
133
|
+
/** Nearest ancestor owner injector, else the root — the parent of a new component injector. */
|
|
134
|
+
function parentInjectorOf(owner) {
|
|
135
|
+
let o = owner?.parent;
|
|
136
|
+
while (o) {
|
|
137
|
+
const inj = _ownerInjectors.get(o);
|
|
138
|
+
if (inj)
|
|
139
|
+
return inj;
|
|
140
|
+
o = o.parent;
|
|
141
|
+
}
|
|
142
|
+
return rootInjector();
|
|
143
|
+
}
|
|
144
|
+
/** The injector for the current scope: explicit context → nearest owner injector → root. */
|
|
145
|
+
function currentInjector() {
|
|
146
|
+
if (_explicit)
|
|
147
|
+
return _explicit;
|
|
148
|
+
let o = getOwner();
|
|
149
|
+
while (o) {
|
|
150
|
+
const inj = _ownerInjectors.get(o);
|
|
151
|
+
if (inj)
|
|
152
|
+
return inj;
|
|
153
|
+
o = o.parent;
|
|
154
|
+
}
|
|
155
|
+
return rootInjector();
|
|
156
|
+
}
|
|
157
|
+
// ── Public API ───────────────────────────────────────────────────────────────
|
|
158
|
+
/** Resolve a dependency from the current injection context. */
|
|
159
|
+
export function inject(token) {
|
|
160
|
+
return currentInjector().get(token);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Provide services for the CURRENT component/route and its descendants (owner-scoped). The
|
|
164
|
+
* injector is disposed — running each service's `onDestroy()` — when the component unmounts.
|
|
165
|
+
*/
|
|
166
|
+
export function provide(providers) {
|
|
167
|
+
const owner = getOwner();
|
|
168
|
+
if (!owner) {
|
|
169
|
+
throw new Error('[fluixi/di] provide() must run inside a component/owner scope. Use provideRoot() for app-wide services.');
|
|
170
|
+
}
|
|
171
|
+
let inj = _ownerInjectors.get(owner);
|
|
172
|
+
if (!inj) {
|
|
173
|
+
inj = new Injector(parentInjectorOf(owner));
|
|
174
|
+
_ownerInjectors.set(owner, inj);
|
|
175
|
+
onCleanup(() => inj.dispose());
|
|
176
|
+
}
|
|
177
|
+
inj.register(providers);
|
|
178
|
+
return inj;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Provide app-wide services. On the client these are singletons; during SSR each request gets
|
|
182
|
+
* its OWN instances (the root injector is per-request) — so a stateful root service can never
|
|
183
|
+
* leak between concurrent requests. Call at module load (e.g. in entry-client / entry-server).
|
|
184
|
+
*/
|
|
185
|
+
export function provideRoot(providers) {
|
|
186
|
+
_rootProviders.push(...providers);
|
|
187
|
+
_clientRoot?.register(providers); // apply to an already-created client root
|
|
188
|
+
const ctx = getRequestContext();
|
|
189
|
+
if (ctx)
|
|
190
|
+
_reqInjectors.get(ctx)?.register(providers); // and the active request root
|
|
191
|
+
}
|
|
192
|
+
/** Run `fn` with an explicit injector as the injection context (e.g. inside a server function). */
|
|
193
|
+
export function runInInjectionContext(injector, fn) {
|
|
194
|
+
const prev = _explicit;
|
|
195
|
+
_explicit = injector;
|
|
196
|
+
try {
|
|
197
|
+
return fn();
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
_explicit = prev;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/** The injector for the current scope (advanced — for passing to runInInjectionContext later). */
|
|
204
|
+
export function getInjector() {
|
|
205
|
+
return currentInjector();
|
|
206
|
+
}
|
|
207
|
+
/** A standalone injector (parent defaults to the root) — for tests / manual hierarchies. */
|
|
208
|
+
export function createInjector(providers, parent) {
|
|
209
|
+
return new Injector(parent === undefined ? rootInjector() : parent).register(providers);
|
|
210
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type IncomingMessage, type ServerResponse, type Server } from 'node:http';
|
|
2
|
+
/**
|
|
3
|
+
* The runtime-neutral request layer. Everything renders through a Web
|
|
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.
|
|
6
|
+
* This is the seam deploy adapters plug into.
|
|
7
|
+
*/
|
|
8
|
+
/** Produce page HTML for a request URL (pathname + search). */
|
|
9
|
+
export type RenderFn = (url: string, request: Request) => string | Promise<string>;
|
|
10
|
+
/** Web-standard request handler. */
|
|
11
|
+
export type FetchHandler = (request: Request) => Promise<Response>;
|
|
12
|
+
/** Produce a web `ReadableStream` of the app body (no document shell) for a URL. */
|
|
13
|
+
export type StreamRenderFn = (url: string, request: Request) => ReadableStream<Uint8Array> | Promise<ReadableStream<Uint8Array>>;
|
|
14
|
+
/**
|
|
15
|
+
* Compose a streamed HTML Response: flush `head` immediately (browser starts fetching
|
|
16
|
+
* assets while the server awaits data), then pipe the app `body` stream into the mount
|
|
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
|
|
19
|
+
* should guard the pre-flush path for a real 500.
|
|
20
|
+
*/
|
|
21
|
+
export declare function streamDocument(head: string, body: ReadableStream<Uint8Array>, tail: string): Response;
|
|
22
|
+
/** Wrap a render function as a fetch handler (Node/Bun/Deno/edge-portable). */
|
|
23
|
+
export declare function createRequestHandler(render: RenderFn): FetchHandler;
|
|
24
|
+
/** Build a Web `Request` from a Node `IncomingMessage`. */
|
|
25
|
+
export declare function nodeToRequest(req: IncomingMessage): Request;
|
|
26
|
+
/** Write a Web `Response` (string or streamed body) to a Node `ServerResponse`. */
|
|
27
|
+
export declare function sendResponse(res: ServerResponse, response: Response): Promise<void>;
|
|
28
|
+
/** Adapt a `FetchHandler` to a Node `(req, res)` handler. */
|
|
29
|
+
export declare function toNodeHandler(handler: FetchHandler): (req: IncomingMessage, res: ServerResponse) => Promise<void>;
|
|
30
|
+
export interface ServeNodeOptions {
|
|
31
|
+
port?: number;
|
|
32
|
+
onListen?: (port: number) => void;
|
|
33
|
+
}
|
|
34
|
+
/** The reference **Node adapter**: serve a `FetchHandler` over `node:http`. */
|
|
35
|
+
export declare function serveNode(handler: FetchHandler, opts?: ServeNodeOptions): Server;
|
|
36
|
+
/**
|
|
37
|
+
* Wrap a handler to serve static files from `dir` first (for filesystem runtimes —
|
|
38
|
+
* Node/Bun/Deno). Edge runtimes serve assets via the platform, so they use the bare
|
|
39
|
+
* handler instead.
|
|
40
|
+
*/
|
|
41
|
+
export declare function withStaticFiles(handler: FetchHandler, dir: string): FetchHandler;
|
|
42
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAMrH;;;;;GAKG;AAEH,+DAA+D;AAC/D,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAEnF,oCAAoC;AACpC,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEnE,oFAAoF;AACpF,MAAM,MAAM,cAAc,GAAG,CAC3B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,KACb,cAAc,CAAC,UAAU,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAC;AAEtE;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,cAAc,CAAC,UAAU,CAAC,EAChC,IAAI,EAAE,MAAM,GACX,QAAQ,CAuBV;AAED,+EAA+E;AAC/E,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,QAAQ,GAAG,YAAY,CAgBnE;AAID,2DAA2D;AAC3D,wBAAgB,aAAa,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAe3D;AAED,mFAAmF;AACnF,wBAAsB,YAAY,CAAC,GAAG,EAAE,cAAc,EAAE,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBzF;AAED,6DAA6D;AAC7D,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,IACnC,KAAK,eAAe,EAAE,KAAK,cAAc,KAAG,OAAO,CAAC,IAAI,CAAC,CAIxE;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED,+EAA+E;AAC/E,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,EAAE,IAAI,GAAE,gBAAqB,GAAG,MAAM,CAapF;AASD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAoBhF"}
|