@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,207 @@
1
+ /**
2
+ * Deploy adapters for the three hosts that expect a specific output layout.
3
+ *
4
+ * Each one bundles the same generated entry and then writes whatever the platform
5
+ * reads to route a request. What differs is only that layout — the handler, the
6
+ * dispatch order and the render are shared.
7
+ *
8
+ * All three inline the framework: a worker or a serverless function is a file that
9
+ * gets uploaded, not an install, so nothing may be left to resolve at runtime.
10
+ */
11
+ import { mkdir, writeFile, readFile, cp, readdir } from 'node:fs/promises';
12
+ import { existsSync } from 'node:fs';
13
+ import { join, relative } from 'node:path';
14
+ import { readTemplate } from '../adapter.js';
15
+ import { cloudflareEntry, netlifyEntry, vercelEntry } from './entry.js';
16
+ /** What the generated entry needs to know about this build. */
17
+ async function entrySource(ctx, from, prerendered) {
18
+ const template = await readTemplate(ctx.clientDir, ctx.serverDir);
19
+ const middleware = join(ctx.serverDir, 'middleware.js');
20
+ const rel = (file) => {
21
+ // Node resolves a relative specifier against the importer, and the entry is
22
+ // written to `from`; keep it POSIX so the bundler reads it the same on Windows.
23
+ const path = join(ctx.serverDir, file).replace(/\\/g, '/');
24
+ return path.startsWith('/') ? path : `./${path}`;
25
+ };
26
+ return {
27
+ serverEntry: rel('entry-server.js'),
28
+ middleware: existsSync(middleware) ? rel('middleware.js') : null,
29
+ template,
30
+ ssr: ctx.cfg.ssr,
31
+ mountId: ctx.cfg.mountId,
32
+ prerendered,
33
+ };
34
+ }
35
+ /**
36
+ * Is `dist/client/index.html` a page, or the shell the server renders into?
37
+ *
38
+ * Vite writes the shell at exactly the path a prerendered `/` would occupy, and every
39
+ * one of these platforms serves a matching file before it invokes anything. Left
40
+ * alone, `/` is the one route an SSR app can never render — the CDN answers it with
41
+ * an empty mount element. Every other route is safe: no file exists at `/about`
42
+ * unless we actually prerendered it. So the adapters ask this before deciding who
43
+ * owns `/`.
44
+ */
45
+ function indexIsPage(template, mountId) {
46
+ const mount = new RegExp(`<([a-z-]+)[^>]*\\sid=["']${mountId}["'][^>]*>`, 'i').exec(template);
47
+ if (!mount)
48
+ return false;
49
+ return !template.slice(mount.index + mount[0].length).trimStart().startsWith('</');
50
+ }
51
+ /** Nothing to wrap: a fully static SPA has no server entry, and that is not a failure. */
52
+ function skipWhenStatic(ctx, name) {
53
+ if (ctx.hasServer)
54
+ return false;
55
+ console.log(` fluixi: no server entry — ${name} gets the static output alone.`);
56
+ return true;
57
+ }
58
+ /** Cloudflare caps `_routes.json` at 100 rules. */
59
+ const CF_MAX_RULES = 100;
60
+ /**
61
+ * The routes a prerendered page is served at, from the HTML on disk.
62
+ *
63
+ * `/` is in the list only when `index.html` holds a rendered page rather than the
64
+ * shell, because that is the difference between a file the platform should serve and
65
+ * a route the app has to render.
66
+ */
67
+ async function prerenderedRoutes(dir, mountId) {
68
+ const out = [];
69
+ const walk = async (current) => {
70
+ for (const entry of await readdir(current, { withFileTypes: true })) {
71
+ const path = join(current, entry.name);
72
+ if (entry.isDirectory()) {
73
+ if (entry.name === 'assets' || entry.name.startsWith('.'))
74
+ continue;
75
+ await walk(path);
76
+ }
77
+ else if (entry.name.endsWith('.html')) {
78
+ const rel = relative(dir, path).replace(/\\/g, '/').replace(/index\.html$/, '').replace(/\.html$/, '');
79
+ out.push('/' + rel.replace(/\/$/, ''));
80
+ }
81
+ }
82
+ };
83
+ await walk(dir);
84
+ const index = await readFile(join(dir, 'index.html'), 'utf-8').catch(() => '');
85
+ const root = indexIsPage(index, mountId);
86
+ return out.filter((r) => r !== '/' || root).sort();
87
+ }
88
+ /**
89
+ * Cloudflare Pages. `_worker.js` at the root of the published directory takes every
90
+ * request; `_routes.json` is what decides whether a request costs an invocation.
91
+ *
92
+ * In advanced mode the worker sees everything not excluded — so a prerendered page
93
+ * would wake it just to proxy the file back through the ASSETS binding. On a plan
94
+ * metered by request that is the whole page view budget spent on static files, so
95
+ * every prerendered path is excluded by name.
96
+ *
97
+ * Publish `dist/client`.
98
+ */
99
+ export const cloudflareAdapter = {
100
+ name: 'cloudflare',
101
+ bundle: 'inline',
102
+ serve({ handler }) {
103
+ return { fetch: handler };
104
+ },
105
+ async build(ctx) {
106
+ if (skipWhenStatic(ctx, 'cloudflare'))
107
+ return;
108
+ const out = join(ctx.clientDir, '_worker.js');
109
+ const prerendered = await prerenderedRoutes(ctx.clientDir, ctx.cfg.mountId);
110
+ await ctx.bundleEntry(cloudflareEntry(await entrySource(ctx, out, prerendered)), out);
111
+ // Everything under /assets is content-hashed and immutable, so it never needs the
112
+ // worker. `_worker.js` itself must be excluded or Pages would route it to itself.
113
+ // `/` is excluded only when it was prerendered — otherwise the worker is the only
114
+ // thing that can answer it, and excluding it would serve the shell instead.
115
+ const base = ['/assets/*', '/_worker.js'];
116
+ const room = CF_MAX_RULES - base.length;
117
+ const exclude = [...base, ...prerendered.slice(0, room)];
118
+ if (prerendered.length > room) {
119
+ // Past the cap the remainder still works — it just wakes the worker to hand
120
+ // back a file. Worth saying, because the symptom is a bill, not an error.
121
+ console.warn(` fluixi: ${prerendered.length} prerendered routes exceed Cloudflare's ${CF_MAX_RULES}-rule ` +
122
+ `limit for _routes.json; ${prerendered.length - room} will invoke the worker to serve a static file.`);
123
+ }
124
+ await writeFile(join(ctx.clientDir, '_routes.json'), JSON.stringify({ version: 1, include: ['/*'], exclude }, null, 2) + '\n');
125
+ },
126
+ };
127
+ /**
128
+ * Netlify. The function is a v2 fetch handler under `.netlify/functions-internal/`,
129
+ * which is where a framework — as opposed to the user — puts one, so it cannot
130
+ * collide with the app's own `netlify/functions`.
131
+ *
132
+ * Publish `dist/client`.
133
+ */
134
+ export const netlifyAdapter = {
135
+ name: 'netlify',
136
+ bundle: 'inline',
137
+ serve({ handler }) {
138
+ return { fetch: handler };
139
+ },
140
+ async build(ctx) {
141
+ if (skipWhenStatic(ctx, 'netlify'))
142
+ return;
143
+ const dir = join(ctx.root, '.netlify', 'functions-internal');
144
+ await mkdir(dir, { recursive: true });
145
+ const out = join(dir, 'fluixi-server.mjs');
146
+ const prerendered = await prerenderedRoutes(ctx.clientDir, ctx.cfg.mountId);
147
+ await ctx.bundleEntry(netlifyEntry(await entrySource(ctx, out, prerendered)), out);
148
+ // v2 functions declare their own routing, but the manifest is what makes Netlify
149
+ // load an internal function at all.
150
+ await writeFile(join(dir, 'fluixi-server.json'), JSON.stringify({ config: { path: '/*', preferStatic: true }, version: 1 }, null, 2) + '\n');
151
+ // `preferStatic` is what keeps assets, prerendered pages and files like
152
+ // `favicon.ico` off the function — and it is also what would hand back the shell
153
+ // for `/`. A forced redirect is the one exception the CDN honours over a file, so
154
+ // it claims exactly that path and nothing else. Appended, so an app's own rules in
155
+ // `public/_redirects` still match first.
156
+ if (!prerendered.includes('/')) {
157
+ const file = join(ctx.clientDir, '_redirects');
158
+ const existing = await readFile(file, 'utf-8').catch(() => '');
159
+ const rule = '/ /.netlify/functions/fluixi-server 200!\n';
160
+ if (!existing.includes(rule)) {
161
+ await writeFile(file, existing && !existing.endsWith('\n') ? `${existing}\n${rule}` : existing + rule);
162
+ }
163
+ }
164
+ },
165
+ };
166
+ /**
167
+ * Vercel, through the Build Output API v3: `.vercel/output/` is read directly, with
168
+ * no framework detection and no `vercel.json` to keep in sync.
169
+ *
170
+ * `config.json` routes the filesystem first so a prerendered page is served as a
171
+ * file, then sends the rest to the function.
172
+ */
173
+ export const vercelAdapter = {
174
+ name: 'vercel',
175
+ bundle: 'inline',
176
+ serve({ handler }) {
177
+ return { fetch: handler };
178
+ },
179
+ async build(ctx) {
180
+ const output = join(ctx.root, '.vercel', 'output');
181
+ await mkdir(output, { recursive: true });
182
+ // Static assets and prerendered HTML are served straight from here.
183
+ await cp(ctx.clientDir, join(output, 'static'), { recursive: true });
184
+ if (skipWhenStatic(ctx, 'vercel')) {
185
+ await writeFile(join(output, 'config.json'), JSON.stringify({ version: 3, routes: [{ handle: 'filesystem' }] }, null, 2) + '\n');
186
+ return;
187
+ }
188
+ const fn = join(output, 'functions', 'index.func');
189
+ await mkdir(fn, { recursive: true });
190
+ const out = join(fn, 'index.mjs');
191
+ const prerendered = await prerenderedRoutes(ctx.clientDir, ctx.cfg.mountId);
192
+ await ctx.bundleEntry(vercelEntry(await entrySource(ctx, out, prerendered)), out);
193
+ await writeFile(join(fn, '.vc-config.json'), JSON.stringify({ runtime: 'nodejs20.x', handler: 'index.mjs', launcherType: 'Nodejs', shouldAddHelpers: false }, null, 2) + '\n');
194
+ await writeFile(join(output, 'config.json'), JSON.stringify({
195
+ version: 3,
196
+ routes: [
197
+ // The filesystem holds a shell at `/index.html`, so `/` has to be claimed
198
+ // before that phase or it is served unrendered. Skipped when `/` really is
199
+ // a prerendered page, which the filesystem should answer.
200
+ ...(prerendered.includes('/') ? [] : [{ src: '/', dest: '/index' }]),
201
+ // A prerendered page or an asset wins; only a miss reaches the function.
202
+ { handle: 'filesystem' },
203
+ { src: '/(.*)', dest: '/index' },
204
+ ],
205
+ }, null, 2) + '\n');
206
+ },
207
+ };
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,QAAQ;IACvB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,SAAS,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAqB5G;AAED,sFAAsF;AACtF,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAS,GAAG,OAAO,CAEpE;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,IAAI,CAS5I;AAED,gGAAgG;AAChG,wBAAsB,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,CAYxG;AAkBD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,GAAG,CAiC/E;AAED,eAAO,MAAM,kBAAkB,uBAAW,CAAC"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,QAAQ;IACvB,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,6CAA6C;IAC7C,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC7B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,SAAS,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAqB5G;AAED,sFAAsF;AACtF,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,SAAS,GAAG,OAAO,CAEpE;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,IAAI,CAS5I;AAED,gGAAgG;AAChG,wBAAsB,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,CAYxG;AAkBD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,GAAG,CAuC/E;AAED,eAAO,MAAM,kBAAkB,uBAAW,CAAC"}
package/dist/api.js CHANGED
@@ -90,11 +90,17 @@ export function apiRoutesVitePlugin(opts) {
90
90
  const id = '\0' + API_VMOD;
91
91
  return {
92
92
  name: 'fluixi-api',
93
+ // Must beat node resolution: '@fluixi/start/api-routes' is a real file in this package,
94
+ // so without `pre` Vite resolves the typed stub and the generated table never loads —
95
+ // the app builds and every API route 404s.
96
+ enforce: 'pre',
93
97
  configResolved(config) {
94
98
  root = config.root || root;
95
99
  },
96
100
  resolveId(source) {
97
- return source === API_VMOD ? id : null;
101
+ // The public specifier is a real module so its types ship with the package; both
102
+ // resolve to the generated table.
103
+ return source === API_VMOD || source === '@fluixi/start/api-routes' ? id : null;
98
104
  },
99
105
  load(source) {
100
106
  if (source !== id)
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAMjD;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,MAAM,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DpE"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,cAAc,CAAC;AAOjE;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,MAAM,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAqFpE"}
@@ -1,7 +1,9 @@
1
1
  import { existsSync } from 'node:fs';
2
- import { resolve } from 'node:path';
2
+ import { mkdir, writeFile, rm, copyFile } from 'node:fs/promises';
3
+ import { resolve, dirname, join, basename } from 'node:path';
3
4
  import { resolveConfig } from '../config.js';
4
5
  import { fluixiPlugins, loadServerEnv } from '../internal.js';
6
+ import { ssrBuildOptions } from '../adapter.js';
5
7
  import { prerender } from './prerender.js';
6
8
  import { wordmark, step, done, c } from '../ui.js';
7
9
  /**
@@ -15,6 +17,8 @@ export async function build(config = {}) {
15
17
  await loadServerEnv(cfg.root, 'production'); // .env → process.env (server-only secrets)
16
18
  const { build: viteBuild } = await import('vite');
17
19
  const plugins = await fluixiPlugins(cfg);
20
+ // The deploy target decides whether the framework is bundled in or imported at runtime.
21
+ const ssr = ssrBuildOptions(cfg);
18
22
  process.stdout.write(`\n ${wordmark()} ${c.gray('building for production')}\n\n`);
19
23
  step('client bundle');
20
24
  await viteBuild({
@@ -27,11 +31,11 @@ export async function build(config = {}) {
27
31
  // functions / middleware — so build it when the entry exists; a pure static SPA skips it.
28
32
  const serverEntrySrc = resolve(cfg.root, cfg.serverEntry);
29
33
  if (cfg.ssr || existsSync(serverEntrySrc)) {
30
- step('server bundle');
34
+ step(`server bundle${bundleNote(cfg)}`);
31
35
  await viteBuild({
32
36
  root: cfg.root,
33
37
  plugins,
34
- ssr: { noExternal: cfg.ssrNoExternal.length ? cfg.ssrNoExternal : undefined },
38
+ ssr,
35
39
  build: {
36
40
  outDir: 'dist/server',
37
41
  ssr: serverEntrySrc,
@@ -48,7 +52,7 @@ export async function build(config = {}) {
48
52
  root: cfg.root,
49
53
  plugins,
50
54
  logLevel: 'warn',
51
- ssr: { noExternal: cfg.ssrNoExternal.length ? cfg.ssrNoExternal : undefined },
55
+ ssr,
52
56
  build: {
53
57
  outDir: 'dist/server',
54
58
  ssr: middlewareSrc,
@@ -58,10 +62,75 @@ export async function build(config = {}) {
58
62
  }
59
63
  // SSG: prerender routes to static HTML in dist/client (config-driven).
60
64
  if (cfg.prerender) {
65
+ // Prerendering `/` writes over the built `index.html`, and that file is also the
66
+ // template the server renders into. Keep the shell before it goes: without this a
67
+ // prerendered home page becomes the template, so every SSR response carries the
68
+ // home page's markup and head around the app it just rendered.
69
+ await copyFile(resolve(cfg.root, 'dist/client', 'index.html'), resolve(cfg.root, 'dist/server', 'template.html'));
61
70
  process.stdout.write('\n');
62
71
  await prerender(config);
63
72
  }
73
+ // The deploy target's own output layout — a worker entry, a function manifest, a
74
+ // routing config. Last, so everything it wraps or copies already exists.
75
+ if (cfg.adapter?.build) {
76
+ step(`${cfg.adapter.name} output`);
77
+ await cfg.adapter.build({
78
+ cfg,
79
+ root: cfg.root,
80
+ clientDir: resolve(cfg.root, 'dist/client'),
81
+ serverDir: resolve(cfg.root, 'dist/server'),
82
+ hasServer: existsSync(resolve(cfg.root, 'dist/server', 'entry-server.js')),
83
+ bundleEntry: (source, outFile) => bundleEntry(cfg, plugins, source, outFile),
84
+ });
85
+ }
64
86
  const ms = Date.now() - startedAt;
65
87
  process.stdout.write('\n');
66
88
  done(`${c.bold('built')} in ${c.bold(String(ms))} ms ${c.gray('→ dist/client + dist/server')}\n`);
67
89
  }
90
+ /**
91
+ * Bundle a generated platform entry into one self-contained file.
92
+ *
93
+ * The entry is written next to its output so a relative import of the server bundle
94
+ * resolves, then removed — a stray `.fluixi-entry.js` in dist would be served as a
95
+ * static asset on every one of these platforms.
96
+ */
97
+ async function bundleEntry(cfg, plugins, source, outFile) {
98
+ const { build: viteBuild } = await import('vite');
99
+ const dir = dirname(outFile);
100
+ const temp = join(dir, `.fluixi-entry-${Date.now()}.js`);
101
+ await mkdir(dir, { recursive: true });
102
+ await writeFile(temp, source);
103
+ try {
104
+ await viteBuild({
105
+ root: cfg.root,
106
+ plugins: plugins,
107
+ logLevel: 'warn',
108
+ // Nothing may be left to resolve: these run as an uploaded file, not an install.
109
+ ssr: { noExternal: true },
110
+ build: {
111
+ outDir: dir,
112
+ emptyOutDir: false,
113
+ ssr: temp,
114
+ rollupOptions: {
115
+ output: {
116
+ entryFileNames: basename(outFile),
117
+ // One file, no chunks. A lazy route would otherwise be split out beside
118
+ // the entry — and for Cloudflare that directory is the published one, so
119
+ // every server chunk would be a public download.
120
+ inlineDynamicImports: true,
121
+ },
122
+ },
123
+ },
124
+ });
125
+ }
126
+ finally {
127
+ await rm(temp, { force: true });
128
+ }
129
+ }
130
+ /** `server bundle (node · external)` — the output shape is not obvious from the file. */
131
+ function bundleNote(cfg) {
132
+ const adapter = cfg.adapter;
133
+ if (!adapter?.bundle)
134
+ return '';
135
+ return c.gray(` (${adapter.name} · ${adapter.bundle})`);
136
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The CLI commands.
3
+ *
4
+ * Kept off the package root deliberately: these reach the dev server, the build
5
+ * and the prerenderer, and a module that only wants `defineMiddleware` should not
6
+ * be able to pull a bundler in behind it. That matters at build time — a deploy
7
+ * adapter inlines everything the entry can reach, and reaching this would put
8
+ * vite in a worker.
9
+ */
10
+ export { dev } from './dev.js';
11
+ export { build } from './build.js';
12
+ export { start } from './start.js';
13
+ export { prerender } from './prerender.js';
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/commands/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The CLI commands.
3
+ *
4
+ * Kept off the package root deliberately: these reach the dev server, the build
5
+ * and the prerenderer, and a module that only wants `defineMiddleware` should not
6
+ * be able to pull a bundler in behind it. That matters at build time — a deploy
7
+ * adapter inlines everything the entry can reach, and reaching this would put
8
+ * vite in a worker.
9
+ */
10
+ export { dev } from './dev.js';
11
+ export { build } from './build.js';
12
+ export { start } from './start.js';
13
+ export { prerender } from './prerender.js';
@@ -1 +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"}
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,CAoBpE"}
@@ -14,5 +14,14 @@ export async function start(config = {}) {
14
14
  await loadServerEnv(cfg.root, 'production'); // .env → process.env (server-only secrets)
15
15
  const clientDir = resolve(cfg.root, 'dist/client');
16
16
  const handler = await createProdHandler(cfg);
17
- await nodeAdapter.serve({ handler, cfg, clientDir });
17
+ // The configured adapter, or node when there is none. An adapter that returns
18
+ // `{ fetch }` has no process to run — its output is a module the platform imports —
19
+ // so say that instead of exiting as if the server had started.
20
+ const adapter = cfg.adapter ?? nodeAdapter;
21
+ const served = await adapter.serve({ handler, cfg, clientDir });
22
+ if (served && typeof served === 'object' && 'fetch' in served) {
23
+ console.log(` fluixi: the "${adapter.name}" adapter exports a fetch handler — there is no server ` +
24
+ `to start. Deploy dist/ and let the platform invoke it, or run \`fluixi start\` with ` +
25
+ `a filesystem adapter (nodeAdapter).`);
26
+ }
18
27
  }
package/dist/config.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { Adapter } from './adapter.js';
1
2
  /**
2
3
  * Fluixi app config. Place in `fluixi.config.{ts,js,mjs}` at the app root and wrap
3
4
  * with `defineConfig` for types. Everything is optional — the defaults match the
@@ -31,6 +32,12 @@ export interface FluixiConfig {
31
32
  middleware?: string;
32
33
  /** SSR deps Vite must bundle (no-external) — e.g. ESM packages without file extensions. */
33
34
  ssrNoExternal?: string[];
35
+ /**
36
+ * Deploy target. Decides how `fluixi build` bundles the server (see the adapter's
37
+ * `bundle` mode) and how `fluixi start` serves it. Unset keeps Vite's own bundling
38
+ * heuristic and serves with `nodeAdapter`.
39
+ */
40
+ adapter?: Adapter;
34
41
  /**
35
42
  * Built-in i18n auto-discovery. Scans `<dir>/<locale>.json` and exposes them as
36
43
  * `virtual:fluixi-i18n` (`{ messages, locales, defaultLocale }`) to feed `createI18n`.
@@ -64,10 +71,12 @@ export interface ResolvedPrerender {
64
71
  routes: string[];
65
72
  crawl: boolean;
66
73
  }
67
- export type ResolvedConfig = Required<Omit<FluixiConfig, 'ssrNoExternal' | 'i18n' | 'prerender'>> & {
74
+ export type ResolvedConfig = Required<Omit<FluixiConfig, 'ssrNoExternal' | 'i18n' | 'prerender' | 'adapter'>> & {
68
75
  ssrNoExternal: string[];
69
76
  i18n: ResolvedI18n | false;
70
77
  prerender: ResolvedPrerender | false;
78
+ /** Stays optional: no adapter means "Vite's defaults + serve with node". */
79
+ adapter?: Adapter;
71
80
  };
72
81
  /** Identity helper for editor types in `fluixi.config.ts`. */
73
82
  export declare function defineConfig(config: FluixiConfig): FluixiConfig;
@@ -1 +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"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;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;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;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,CACnC,IAAI,CAAC,YAAY,EAAE,eAAe,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC,CACvE,GAAG;IACF,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,IAAI,EAAE,YAAY,GAAG,KAAK,CAAC;IAC3B,SAAS,EAAE,iBAAiB,GAAG,KAAK,CAAC;IACrC,4EAA4E;IAC5E,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,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,CAkBvE;AAgCD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,IAAI,SAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAM5E"}
package/dist/config.js CHANGED
@@ -21,6 +21,7 @@ export function resolveConfig(config = {}) {
21
21
  apiDir: config.apiDir ?? 'src/api',
22
22
  middleware: config.middleware ?? 'src/middleware.ts',
23
23
  ssrNoExternal: config.ssrNoExternal ?? [],
24
+ adapter: config.adapter,
24
25
  i18n: resolveI18n(config.i18n),
25
26
  prerender: resolvePrerender(config.prerender),
26
27
  };
@@ -0,0 +1,30 @@
1
+ import type { FetchHandler } from './handler.js';
2
+ /**
3
+ * Inject server-rendered app HTML into the template's mount element
4
+ * (`<div id="root"></div>`). Falls back to a `<!--ssr-outlet-->` marker, then to
5
+ * just before `</body>`.
6
+ */
7
+ export declare function injectApp(template: string, appHtml: string, mountId?: string): string;
8
+ /**
9
+ * Split the template at the mount point for streaming: `head` is everything up to and
10
+ * including the open mount tag (flushed first so the browser fetches assets while the
11
+ * server awaits data); `tail` is the close tag onward (client script + `</body>`).
12
+ * Mirrors injectApp's three cases (mount div, `<!--ssr-outlet-->`, before `</body>`).
13
+ */
14
+ export declare function splitTemplate(template: string, mountId?: string): {
15
+ head: string;
16
+ tail: string;
17
+ };
18
+ /**
19
+ * Inject the rendered app into the mount point AND lift any @fluixi/head metadata (carried back as
20
+ * a marker by renderRequestAsync) into <head> + <html>. If the page set a title, the template's own
21
+ * <title> is dropped so there's only one. No marker → identical to injectApp.
22
+ */
23
+ export declare function injectAppAndHead(template: string, rendered: string, mountId?: string): string;
24
+ /**
25
+ * Wrap a fetch handler so a thrown/rejected error becomes a 500 instead of an
26
+ * unhandled rejection. `createRequestHandler` already guards the renderer; this
27
+ * covers the middleware chain that wraps it. `onError` lets dev map the stack first.
28
+ */
29
+ export declare function guardHandler(handler: FetchHandler, onError?: (e: unknown) => void): FetchHandler;
30
+ //# sourceMappingURL=document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../src/document.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD;;;;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"}
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Turning a rendered app into a document, and keeping a failed render from taking
3
+ * the process with it.
4
+ *
5
+ * A leaf on purpose: this is imported by the request handler, which runs in a
6
+ * worker as often as on Node, and a runtime module that reaches into the build
7
+ * helpers drags a whole toolchain into the bundle with it.
8
+ */
9
+ import { extractHeadMarker } from '@fluixi/head';
10
+ /**
11
+ * Inject server-rendered app HTML into the template's mount element
12
+ * (`<div id="root"></div>`). Falls back to a `<!--ssr-outlet-->` marker, then to
13
+ * just before `</body>`.
14
+ */
15
+ export function injectApp(template, appHtml, mountId = 'root') {
16
+ const mount = new RegExp(`<div id=["']${mountId}["']\\s*>\\s*</div>`);
17
+ if (mount.test(template)) {
18
+ return template.replace(mount, `<div id="${mountId}">${appHtml}</div>`);
19
+ }
20
+ if (template.includes('<!--ssr-outlet-->')) {
21
+ return template.replace('<!--ssr-outlet-->', appHtml);
22
+ }
23
+ return template.replace('</body>', `<div id="${mountId}">${appHtml}</div></body>`);
24
+ }
25
+ /**
26
+ * Split the template at the mount point for streaming: `head` is everything up to and
27
+ * including the open mount tag (flushed first so the browser fetches assets while the
28
+ * server awaits data); `tail` is the close tag onward (client script + `</body>`).
29
+ * Mirrors injectApp's three cases (mount div, `<!--ssr-outlet-->`, before `</body>`).
30
+ */
31
+ export function splitTemplate(template, mountId = 'root') {
32
+ const mount = new RegExp(`<div id=["']${mountId}["']\\s*>\\s*</div>`);
33
+ const m = template.match(mount);
34
+ if (m && m.index !== undefined) {
35
+ return {
36
+ head: template.slice(0, m.index) + `<div id="${mountId}">`,
37
+ tail: `</div>` + template.slice(m.index + m[0].length),
38
+ };
39
+ }
40
+ const outlet = template.indexOf('<!--ssr-outlet-->');
41
+ if (outlet !== -1) {
42
+ return {
43
+ head: template.slice(0, outlet),
44
+ tail: template.slice(outlet + '<!--ssr-outlet-->'.length),
45
+ };
46
+ }
47
+ const body = template.indexOf('</body>');
48
+ if (body !== -1) {
49
+ return {
50
+ head: template.slice(0, body) + `<div id="${mountId}">`,
51
+ tail: `</div>` + template.slice(body),
52
+ };
53
+ }
54
+ return { head: template, tail: '' };
55
+ }
56
+ /** Set/replace an attribute on the template's <html> tag (e.g. lang). */
57
+ function setHtmlAttr(html, name, value) {
58
+ const existing = new RegExp(`(<html\\b[^>]*?)\\s${name}="[^"]*"`, 'i');
59
+ if (existing.test(html))
60
+ return html.replace(existing, `$1 ${name}="${value}"`);
61
+ return html.replace(/<html\b/i, `<html ${name}="${value}"`);
62
+ }
63
+ /**
64
+ * Inject the rendered app into the mount point AND lift any @fluixi/head metadata (carried back as
65
+ * a marker by renderRequestAsync) into <head> + <html>. If the page set a title, the template's own
66
+ * <title> is dropped so there's only one. No marker → identical to injectApp.
67
+ */
68
+ export function injectAppAndHead(template, rendered, mountId = 'root') {
69
+ const { head, body } = extractHeadMarker(rendered);
70
+ let html = injectApp(template, body, mountId);
71
+ if (head) {
72
+ if (head.headHtml) {
73
+ if (/<title[\s>]/i.test(head.headHtml))
74
+ html = html.replace(/<title>[\s\S]*?<\/title>/i, '');
75
+ html = html.replace('</head>', `${head.headHtml}</head>`);
76
+ }
77
+ for (const [k, v] of Object.entries(head.htmlAttrs))
78
+ html = setHtmlAttr(html, k, v);
79
+ }
80
+ return html;
81
+ }
82
+ /**
83
+ * Wrap a fetch handler so a thrown/rejected error becomes a 500 instead of an
84
+ * unhandled rejection. `createRequestHandler` already guards the renderer; this
85
+ * covers the middleware chain that wraps it. `onError` lets dev map the stack first.
86
+ */
87
+ export function guardHandler(handler, onError) {
88
+ return async (request) => {
89
+ try {
90
+ return await handler(request);
91
+ }
92
+ catch (e) {
93
+ onError?.(e);
94
+ return new Response(String(e?.stack || e), {
95
+ status: 500,
96
+ headers: { 'content-type': 'text/plain; charset=utf-8' },
97
+ });
98
+ }
99
+ };
100
+ }
@@ -0,0 +1,5 @@
1
+ /** Whether this request addresses a file-based API route. */
2
+ export declare function isApiRequest(_request: Request): boolean;
3
+ /** Run the matched API handler and return its response. */
4
+ export declare function handleApiRequest(_request: Request): Promise<Response>;
5
+ //# sourceMappingURL=generated-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generated-api.d.ts","sourceRoot":"","sources":["../src/generated-api.ts"],"names":[],"mappings":"AAqBA,6DAA6D;AAC7D,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAEvD;AAED,2DAA2D;AAC3D,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAErE"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The file-based API routes found under `apiDir` (default `src/api`).
3
+ *
4
+ * ```ts
5
+ * import { isApiRequest, handleApiRequest } from '@fluixi/start/api-routes';
6
+ * ```
7
+ *
8
+ * Named `api-routes` because `@fluixi/start/api` is already the API plumbing you call
9
+ * directly (`createApiHandler`, `imageLoader`, …); this is the generated route table.
10
+ *
11
+ * A typed stand-in — the scan plugin serves the real one. `virtual:fluixi-api` still
12
+ * resolves.
13
+ */
14
+ function missingPlugin() {
15
+ throw new Error("[fluixi] '@fluixi/start/api-routes' was imported but the API scan plugin did not replace " +
16
+ 'it. Build with `fluixi build`/`fluixi dev`, or add the @fluixi/start plugins to your ' +
17
+ 'Vite config.');
18
+ }
19
+ /** Whether this request addresses a file-based API route. */
20
+ export function isApiRequest(_request) {
21
+ missingPlugin();
22
+ }
23
+ /** Run the matched API handler and return its response. */
24
+ export function handleApiRequest(_request) {
25
+ missingPlugin();
26
+ }
@@ -0,0 +1,5 @@
1
+ /** Whether this request is an RPC call to a `"use server"` function. */
2
+ export declare function isServerFnRequest(_request: Request): boolean;
3
+ /** Run the addressed server function and return its response. */
4
+ export declare function handleServerFn(_request: Request): Promise<Response>;
5
+ //# sourceMappingURL=generated-server-fns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generated-server-fns.d.ts","sourceRoot":"","sources":["../src/generated-server-fns.ts"],"names":[],"mappings":"AAqBA,wEAAwE;AACxE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,iEAAiE;AACjE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAEnE"}