@fluixi/start 0.1.0-alpha.63 → 0.1.0-alpha.65
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/dist/adapter.d.ts +77 -7
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +150 -44
- package/dist/adapters/entry.d.ts +50 -0
- package/dist/adapters/entry.d.ts.map +1 -0
- package/dist/adapters/entry.js +124 -0
- package/dist/adapters/platforms.d.ts +30 -0
- package/dist/adapters/platforms.d.ts.map +1 -0
- package/dist/adapters/platforms.js +231 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +7 -1
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +114 -4
- package/dist/commands/index.d.ts +14 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +13 -0
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +10 -1
- package/dist/config.d.ts +10 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/document.d.ts +41 -0
- package/dist/document.d.ts.map +1 -0
- package/dist/document.js +125 -0
- package/dist/generated-api.d.ts +5 -0
- package/dist/generated-api.d.ts.map +1 -0
- package/dist/generated-api.js +26 -0
- package/dist/generated-server-fns.d.ts +5 -0
- package/dist/generated-server-fns.d.ts.map +1 -0
- package/dist/generated-server-fns.js +26 -0
- package/dist/handler-core.d.ts +41 -0
- package/dist/handler-core.d.ts.map +1 -0
- package/dist/handler-core.js +50 -0
- package/dist/index.d.ts +5 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -6
- package/dist/internal.d.ts +1 -29
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +10 -102
- package/dist/preload.d.ts +5 -0
- package/dist/preload.d.ts.map +1 -0
- package/dist/preload.js +27 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +44 -6
|
@@ -0,0 +1,231 @@
|
|
|
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
|
+
// Written by the build from Vite's SSR manifest; absent for an app with no file routes.
|
|
20
|
+
const assetFile = join(ctx.serverDir, 'route-assets.json');
|
|
21
|
+
const routeAssets = existsSync(assetFile) ? JSON.parse(await readFile(assetFile, 'utf-8')) : {};
|
|
22
|
+
const middleware = join(ctx.serverDir, 'middleware.js');
|
|
23
|
+
const rel = (file) => {
|
|
24
|
+
// Node resolves a relative specifier against the importer, and the entry is
|
|
25
|
+
// written to `from`; keep it POSIX so the bundler reads it the same on Windows.
|
|
26
|
+
const path = join(ctx.serverDir, file).replace(/\\/g, '/');
|
|
27
|
+
return path.startsWith('/') ? path : `./${path}`;
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
serverEntry: rel('entry-server.js'),
|
|
31
|
+
middleware: existsSync(middleware) ? rel('middleware.js') : null,
|
|
32
|
+
template,
|
|
33
|
+
ssr: ctx.cfg.ssr,
|
|
34
|
+
mountId: ctx.cfg.mountId,
|
|
35
|
+
prerendered,
|
|
36
|
+
routeAssets,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Is `dist/client/index.html` a page, or the shell the server renders into?
|
|
41
|
+
*
|
|
42
|
+
* Vite writes the shell at the path a prerendered `/` would occupy, and every one of
|
|
43
|
+
* these platforms serves a matching file before invoking anything — so `/` is the one
|
|
44
|
+
* route an SSR app cannot render, and the CDN answers it with an empty mount element.
|
|
45
|
+
* No other route is affected: nothing exists at `/about` unless it was prerendered.
|
|
46
|
+
*/
|
|
47
|
+
function indexIsPage(template, mountId) {
|
|
48
|
+
const mount = new RegExp(`<([a-z-]+)[^>]*\\sid=["']${mountId}["'][^>]*>`, 'i').exec(template);
|
|
49
|
+
if (!mount)
|
|
50
|
+
return false;
|
|
51
|
+
return !template.slice(mount.index + mount[0].length).trimStart().startsWith('</');
|
|
52
|
+
}
|
|
53
|
+
/** Nothing to wrap: a fully static SPA has no server entry, and that is not a failure. */
|
|
54
|
+
function skipWhenStatic(ctx, name) {
|
|
55
|
+
if (ctx.hasServer)
|
|
56
|
+
return false;
|
|
57
|
+
console.log(` fluixi: no server entry — ${name} gets the static output alone.`);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Add a rule to `_redirects` without taking the file over. Apps ship their own (the
|
|
62
|
+
* docs site redirects `/` to its default locale), so ours goes last — first match
|
|
63
|
+
* wins, so the app's rules are reached first.
|
|
64
|
+
*/
|
|
65
|
+
async function appendRedirect(clientDir, rule) {
|
|
66
|
+
const file = join(clientDir, '_redirects');
|
|
67
|
+
const existing = await readFile(file, 'utf-8').catch(() => '');
|
|
68
|
+
if (existing.includes(rule))
|
|
69
|
+
return;
|
|
70
|
+
const separator = existing && !existing.endsWith('\n') ? '\n' : '';
|
|
71
|
+
await writeFile(file, `${existing}${separator}${rule}\n`);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* A SPA routes in the browser, but a cold request for `/settings/profile` finds no
|
|
75
|
+
* file, so the host has to hand back the shell for anything it cannot match.
|
|
76
|
+
*
|
|
77
|
+
* Cloudflare Pages already does this for a project with no `_worker.js` (checked on a
|
|
78
|
+
* deployed build), so its adapter writes nothing. Netlify and Vercel both 404.
|
|
79
|
+
*/
|
|
80
|
+
const SPA_FALLBACK = '/* /index.html 200';
|
|
81
|
+
/** Cloudflare caps `_routes.json` at 100 rules. */
|
|
82
|
+
const CF_MAX_RULES = 100;
|
|
83
|
+
/**
|
|
84
|
+
* The routes a prerendered page is served at, from the HTML on disk.
|
|
85
|
+
*
|
|
86
|
+
* `/` is in the list only when `index.html` holds a rendered page rather than the
|
|
87
|
+
* shell, because that is the difference between a file the platform should serve and
|
|
88
|
+
* a route the app has to render.
|
|
89
|
+
*/
|
|
90
|
+
async function prerenderedRoutes(dir, mountId) {
|
|
91
|
+
const out = [];
|
|
92
|
+
const walk = async (current) => {
|
|
93
|
+
for (const entry of await readdir(current, { withFileTypes: true })) {
|
|
94
|
+
const path = join(current, entry.name);
|
|
95
|
+
if (entry.isDirectory()) {
|
|
96
|
+
if (entry.name === 'assets' || entry.name.startsWith('.'))
|
|
97
|
+
continue;
|
|
98
|
+
await walk(path);
|
|
99
|
+
}
|
|
100
|
+
else if (entry.name.endsWith('.html')) {
|
|
101
|
+
const rel = relative(dir, path).replace(/\\/g, '/').replace(/index\.html$/, '').replace(/\.html$/, '');
|
|
102
|
+
out.push('/' + rel.replace(/\/$/, ''));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
await walk(dir);
|
|
107
|
+
const index = await readFile(join(dir, 'index.html'), 'utf-8').catch(() => '');
|
|
108
|
+
const root = indexIsPage(index, mountId);
|
|
109
|
+
return out.filter((r) => r !== '/' || root).sort();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Cloudflare Pages. `_worker.js` at the root of the published directory takes every
|
|
113
|
+
* request; `_routes.json` is what decides whether a request costs an invocation.
|
|
114
|
+
*
|
|
115
|
+
* In advanced mode the worker sees everything not excluded — so a prerendered page
|
|
116
|
+
* would wake it just to proxy the file back through the ASSETS binding. On a plan
|
|
117
|
+
* metered by request that is the whole page view budget spent on static files, so
|
|
118
|
+
* every prerendered path is excluded by name.
|
|
119
|
+
*
|
|
120
|
+
* Publish `dist/client`.
|
|
121
|
+
*/
|
|
122
|
+
export const cloudflareAdapter = {
|
|
123
|
+
name: 'cloudflare',
|
|
124
|
+
bundle: 'inline',
|
|
125
|
+
serve({ handler }) {
|
|
126
|
+
return { fetch: handler };
|
|
127
|
+
},
|
|
128
|
+
async build(ctx) {
|
|
129
|
+
if (skipWhenStatic(ctx, 'cloudflare'))
|
|
130
|
+
return;
|
|
131
|
+
const out = join(ctx.clientDir, '_worker.js');
|
|
132
|
+
const prerendered = await prerenderedRoutes(ctx.clientDir, ctx.cfg.mountId);
|
|
133
|
+
await ctx.bundleEntry(cloudflareEntry(await entrySource(ctx, out, prerendered)), out);
|
|
134
|
+
// Everything under /assets is content-hashed and immutable, so it never needs the
|
|
135
|
+
// worker. `_worker.js` itself must be excluded or Pages would route it to itself.
|
|
136
|
+
// `/` is excluded only when it was prerendered — otherwise the worker is the only
|
|
137
|
+
// thing that can answer it, and excluding it would serve the shell instead.
|
|
138
|
+
const base = ['/assets/*', '/_worker.js'];
|
|
139
|
+
const room = CF_MAX_RULES - base.length;
|
|
140
|
+
const exclude = [...base, ...prerendered.slice(0, room)];
|
|
141
|
+
if (prerendered.length > room) {
|
|
142
|
+
// Past the cap the remainder still works — it just wakes the worker to hand
|
|
143
|
+
// back a file. Worth saying, because the symptom is a bill, not an error.
|
|
144
|
+
console.warn(` fluixi: ${prerendered.length} prerendered routes exceed Cloudflare's ${CF_MAX_RULES}-rule ` +
|
|
145
|
+
`limit for _routes.json; ${prerendered.length - room} will invoke the worker to serve a static file.`);
|
|
146
|
+
}
|
|
147
|
+
await writeFile(join(ctx.clientDir, '_routes.json'), JSON.stringify({ version: 1, include: ['/*'], exclude }, null, 2) + '\n');
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
/**
|
|
151
|
+
* Netlify. The function is a v2 fetch handler under `.netlify/functions-internal/`,
|
|
152
|
+
* which is where a framework — as opposed to the user — puts one, so it cannot
|
|
153
|
+
* collide with the app's own `netlify/functions`.
|
|
154
|
+
*
|
|
155
|
+
* Publish `dist/client`.
|
|
156
|
+
*/
|
|
157
|
+
export const netlifyAdapter = {
|
|
158
|
+
name: 'netlify',
|
|
159
|
+
bundle: 'inline',
|
|
160
|
+
serve({ handler }) {
|
|
161
|
+
return { fetch: handler };
|
|
162
|
+
},
|
|
163
|
+
async build(ctx) {
|
|
164
|
+
// A SPA has no function to fall back to, so the CDN has to hand back the shell.
|
|
165
|
+
if (skipWhenStatic(ctx, 'netlify'))
|
|
166
|
+
return appendRedirect(ctx.clientDir, SPA_FALLBACK);
|
|
167
|
+
const dir = join(ctx.root, '.netlify', 'functions-internal');
|
|
168
|
+
await mkdir(dir, { recursive: true });
|
|
169
|
+
const out = join(dir, 'fluixi-server.mjs');
|
|
170
|
+
const prerendered = await prerenderedRoutes(ctx.clientDir, ctx.cfg.mountId);
|
|
171
|
+
await ctx.bundleEntry(netlifyEntry(await entrySource(ctx, out, prerendered)), out);
|
|
172
|
+
// v2 functions declare their own routing, but the manifest is what makes Netlify
|
|
173
|
+
// load an internal function at all.
|
|
174
|
+
await writeFile(join(dir, 'fluixi-server.json'), JSON.stringify({ config: { path: '/*', preferStatic: true }, version: 1 }, null, 2) + '\n');
|
|
175
|
+
// `preferStatic` is what keeps assets, prerendered pages and files like
|
|
176
|
+
// `favicon.ico` off the function — and it is also what would hand back the shell
|
|
177
|
+
// for `/`. A forced redirect is the one exception the CDN honours over a file, so
|
|
178
|
+
// it claims exactly that path and nothing else. Appended, so an app's own rules in
|
|
179
|
+
// `public/_redirects` still match first.
|
|
180
|
+
if (!prerendered.includes('/')) {
|
|
181
|
+
await appendRedirect(ctx.clientDir, '/ /.netlify/functions/fluixi-server 200!');
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* Vercel, through the Build Output API v3: `.vercel/output/` is read directly, with
|
|
187
|
+
* no framework detection and no `vercel.json` to keep in sync.
|
|
188
|
+
*
|
|
189
|
+
* `config.json` routes the filesystem first so a prerendered page is served as a
|
|
190
|
+
* file, then sends the rest to the function.
|
|
191
|
+
*/
|
|
192
|
+
export const vercelAdapter = {
|
|
193
|
+
name: 'vercel',
|
|
194
|
+
bundle: 'inline',
|
|
195
|
+
serve({ handler }) {
|
|
196
|
+
return { fetch: handler };
|
|
197
|
+
},
|
|
198
|
+
async build(ctx) {
|
|
199
|
+
const output = join(ctx.root, '.vercel', 'output');
|
|
200
|
+
await mkdir(output, { recursive: true });
|
|
201
|
+
// Static assets and prerendered HTML are served straight from here.
|
|
202
|
+
await cp(ctx.clientDir, join(output, 'static'), { recursive: true });
|
|
203
|
+
if (skipWhenStatic(ctx, 'vercel')) {
|
|
204
|
+
await writeFile(join(output, 'config.json'), JSON.stringify({
|
|
205
|
+
version: 3,
|
|
206
|
+
// The filesystem phase alone answers 404 for a client route, since nothing
|
|
207
|
+
// is on disk at that path; the catch-all is what makes it a SPA.
|
|
208
|
+
routes: [{ handle: 'filesystem' }, { src: '/(.*)', dest: '/index.html' }],
|
|
209
|
+
}, null, 2) + '\n');
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const fn = join(output, 'functions', 'index.func');
|
|
213
|
+
await mkdir(fn, { recursive: true });
|
|
214
|
+
const out = join(fn, 'index.mjs');
|
|
215
|
+
const prerendered = await prerenderedRoutes(ctx.clientDir, ctx.cfg.mountId);
|
|
216
|
+
await ctx.bundleEntry(vercelEntry(await entrySource(ctx, out, prerendered)), out);
|
|
217
|
+
await writeFile(join(fn, '.vc-config.json'), JSON.stringify({ runtime: 'nodejs20.x', handler: 'index.mjs', launcherType: 'Nodejs', shouldAddHelpers: false }, null, 2) + '\n');
|
|
218
|
+
await writeFile(join(output, 'config.json'), JSON.stringify({
|
|
219
|
+
version: 3,
|
|
220
|
+
routes: [
|
|
221
|
+
// The filesystem holds a shell at `/index.html`, so `/` has to be claimed
|
|
222
|
+
// before that phase or it is served unrendered. Skipped when `/` really is
|
|
223
|
+
// a prerendered page, which the filesystem should answer.
|
|
224
|
+
...(prerendered.includes('/') ? [] : [{ src: '/', dest: '/index' }]),
|
|
225
|
+
// A prerendered page or an asset wins; only a miss reaches the function.
|
|
226
|
+
{ handle: 'filesystem' },
|
|
227
|
+
{ src: '/(.*)', dest: '/index' },
|
|
228
|
+
],
|
|
229
|
+
}, null, 2) + '\n');
|
|
230
|
+
},
|
|
231
|
+
};
|
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,
|
|
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
|
-
|
|
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":"
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAkB,MAAM,cAAc,CAAC;AAQjE;;;;GAIG;AACH,wBAAsB,KAAK,CAAC,MAAM,GAAE,YAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAuFpE"}
|
package/dist/commands/build.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import {
|
|
2
|
+
import { mkdir, writeFile, rm, copyFile, readFile } 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(
|
|
34
|
+
step(`server bundle${bundleNote(cfg)}`);
|
|
31
35
|
await viteBuild({
|
|
32
36
|
root: cfg.root,
|
|
33
37
|
plugins,
|
|
34
|
-
ssr
|
|
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
|
|
55
|
+
ssr,
|
|
52
56
|
build: {
|
|
53
57
|
outDir: 'dist/server',
|
|
54
58
|
ssr: middlewareSrc,
|
|
@@ -56,12 +60,118 @@ export async function build(config = {}) {
|
|
|
56
60
|
},
|
|
57
61
|
});
|
|
58
62
|
}
|
|
63
|
+
await collectRouteAssets(cfg);
|
|
59
64
|
// SSG: prerender routes to static HTML in dist/client (config-driven).
|
|
60
65
|
if (cfg.prerender) {
|
|
66
|
+
// Prerendering `/` writes over the built `index.html`, and that file is also the
|
|
67
|
+
// template the server renders into. Keep the shell before it goes: without this a
|
|
68
|
+
// prerendered home page becomes the template, so every SSR response carries the
|
|
69
|
+
// home page's markup and head around the app it just rendered.
|
|
70
|
+
await copyFile(resolve(cfg.root, 'dist/client', 'index.html'), resolve(cfg.root, 'dist/server', 'template.html'));
|
|
61
71
|
process.stdout.write('\n');
|
|
62
72
|
await prerender(config);
|
|
63
73
|
}
|
|
74
|
+
// The deploy target's own output layout — a worker entry, a function manifest, a
|
|
75
|
+
// routing config. Last, so everything it wraps or copies already exists.
|
|
76
|
+
if (cfg.adapter?.build) {
|
|
77
|
+
step(`${cfg.adapter.name} output`);
|
|
78
|
+
await cfg.adapter.build({
|
|
79
|
+
cfg,
|
|
80
|
+
root: cfg.root,
|
|
81
|
+
clientDir: resolve(cfg.root, 'dist/client'),
|
|
82
|
+
serverDir: resolve(cfg.root, 'dist/server'),
|
|
83
|
+
hasServer: existsSync(resolve(cfg.root, 'dist/server', 'entry-server.js')),
|
|
84
|
+
bundleEntry: (source, outFile) => bundleEntry(cfg, plugins, source, outFile),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
64
87
|
const ms = Date.now() - startedAt;
|
|
65
88
|
process.stdout.write('\n');
|
|
66
89
|
done(`${c.bold('built')} in ${c.bold(String(ms))} ms ${c.gray('→ dist/client + dist/server')}\n`);
|
|
67
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Turn Vite's SSR manifest into the map the server needs, and stop publishing the
|
|
93
|
+
* manifest itself.
|
|
94
|
+
*
|
|
95
|
+
* The manifest is keyed by module id and the server only has a path, so the join
|
|
96
|
+
* happens here: scan the routes the way the plugin does, resolve each to its files,
|
|
97
|
+
* write a map the runtime can look a URL up in.
|
|
98
|
+
*
|
|
99
|
+
* Only routes are kept, and it goes to `dist/server`: `dist/client` is published, and
|
|
100
|
+
* a full manifest there maps every source path to its chunk for no reader.
|
|
101
|
+
*/
|
|
102
|
+
async function collectRouteAssets(cfg) {
|
|
103
|
+
const clientDir = resolve(cfg.root, 'dist/client');
|
|
104
|
+
const manifestFile = join(clientDir, '.vite', 'ssr-manifest.json');
|
|
105
|
+
if (!existsSync(manifestFile))
|
|
106
|
+
return;
|
|
107
|
+
const serverDir = resolve(cfg.root, 'dist/server');
|
|
108
|
+
const routesDir = resolve(cfg.root, cfg.routesDir);
|
|
109
|
+
if (existsSync(serverDir) && existsSync(routesDir)) {
|
|
110
|
+
const manifest = JSON.parse(await readFile(manifestFile, 'utf-8'));
|
|
111
|
+
const { scanRoutes } = await import('@fluixi/core/plugins');
|
|
112
|
+
const base = cfg.routesDir.replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/$/, '');
|
|
113
|
+
// Keyed by URL pattern: the runtime has a path, and resolving it here leaves it a
|
|
114
|
+
// lookup and nothing else.
|
|
115
|
+
const assets = {};
|
|
116
|
+
const walk = (routes, inherited) => {
|
|
117
|
+
for (const route of routes) {
|
|
118
|
+
// A match renders every layer, so layouts fold in here rather than being
|
|
119
|
+
// unioned per request.
|
|
120
|
+
const files = [...new Set([...inherited, ...(manifest[`${base}/${route.filePath}`] ?? [])])];
|
|
121
|
+
if (files.length)
|
|
122
|
+
assets[route.urlPath] = [...new Set([...(assets[route.urlPath] ?? []), ...files])];
|
|
123
|
+
walk(route.children, files);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
walk(scanRoutes(routesDir), []);
|
|
127
|
+
await writeFile(join(serverDir, 'route-assets.json'), JSON.stringify(assets, null, 2) + '\n');
|
|
128
|
+
}
|
|
129
|
+
await rm(join(clientDir, '.vite'), { recursive: true, force: true });
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Bundle a generated platform entry into one self-contained file.
|
|
133
|
+
*
|
|
134
|
+
* The entry is written next to its output so a relative import of the server bundle
|
|
135
|
+
* resolves, then removed — a stray `.fluixi-entry.js` in dist would be served as a
|
|
136
|
+
* static asset on every one of these platforms.
|
|
137
|
+
*/
|
|
138
|
+
async function bundleEntry(cfg, plugins, source, outFile) {
|
|
139
|
+
const { build: viteBuild } = await import('vite');
|
|
140
|
+
const dir = dirname(outFile);
|
|
141
|
+
const temp = join(dir, `.fluixi-entry-${Date.now()}.js`);
|
|
142
|
+
await mkdir(dir, { recursive: true });
|
|
143
|
+
await writeFile(temp, source);
|
|
144
|
+
try {
|
|
145
|
+
await viteBuild({
|
|
146
|
+
root: cfg.root,
|
|
147
|
+
plugins: plugins,
|
|
148
|
+
logLevel: 'warn',
|
|
149
|
+
// Nothing may be left to resolve: these run as an uploaded file, not an install.
|
|
150
|
+
ssr: { noExternal: true },
|
|
151
|
+
build: {
|
|
152
|
+
outDir: dir,
|
|
153
|
+
emptyOutDir: false,
|
|
154
|
+
ssr: temp,
|
|
155
|
+
rollupOptions: {
|
|
156
|
+
output: {
|
|
157
|
+
entryFileNames: basename(outFile),
|
|
158
|
+
// One file, no chunks. A lazy route would otherwise be split out beside
|
|
159
|
+
// the entry — and for Cloudflare that directory is the published one, so
|
|
160
|
+
// every server chunk would be a public download.
|
|
161
|
+
inlineDynamicImports: true,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
await rm(temp, { force: true });
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
/** `server bundle (node · external)` — the output shape is not obvious from the file. */
|
|
172
|
+
function bundleNote(cfg) {
|
|
173
|
+
const adapter = cfg.adapter;
|
|
174
|
+
if (!adapter?.bundle)
|
|
175
|
+
return '';
|
|
176
|
+
return c.gray(` (${adapter.name} · ${adapter.bundle})`);
|
|
177
|
+
}
|
|
@@ -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,
|
|
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"}
|
package/dist/commands/start.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
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,41 @@
|
|
|
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
|
+
* Reference the files this route needs in the document that renders it.
|
|
20
|
+
*
|
|
21
|
+
* A lazily-imported route is its own chunk with its own stylesheet, mentioned nowhere
|
|
22
|
+
* in the shell — without these the browser discovers them when hydration runs the
|
|
23
|
+
* dynamic import, so the markup paints before the CSS arrives.
|
|
24
|
+
*
|
|
25
|
+
* Files already named in the template are skipped: the entry chunk is in there from
|
|
26
|
+
* the client build, and preloading it again competes with the request in flight.
|
|
27
|
+
*/
|
|
28
|
+
export declare function injectPreload(template: string, files: string[]): string;
|
|
29
|
+
/**
|
|
30
|
+
* Inject the rendered app into the mount point AND lift any @fluixi/head metadata (carried back as
|
|
31
|
+
* a marker by renderRequestAsync) into <head> + <html>. If the page set a title, the template's own
|
|
32
|
+
* <title> is dropped so there's only one. No marker → identical to injectApp.
|
|
33
|
+
*/
|
|
34
|
+
export declare function injectAppAndHead(template: string, rendered: string, mountId?: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Wrap a fetch handler so a thrown/rejected error becomes a 500 instead of an
|
|
37
|
+
* unhandled rejection. `createRequestHandler` already guards the renderer; this
|
|
38
|
+
* covers the middleware chain that wraps it. `onError` lets dev map the stack first.
|
|
39
|
+
*/
|
|
40
|
+
export declare function guardHandler(handler: FetchHandler, onError?: (e: unknown) => void): FetchHandler;
|
|
41
|
+
//# 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;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAevE;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"}
|