@fluixi/start 0.1.0-alpha.73 → 0.1.0-alpha.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter.cjs +750 -0
- package/dist/adapter.d.ts +9 -9
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +10 -10
- package/dist/adapter.mjs +719 -0
- package/dist/adapters/entry.cjs +122 -0
- package/dist/adapters/entry.js +1 -1
- package/dist/adapters/entry.mjs +95 -0
- package/dist/adapters/platforms.cjs +311 -0
- package/dist/adapters/platforms.d.ts +2 -2
- package/dist/adapters/platforms.js +8 -8
- package/dist/adapters/platforms.mjs +284 -0
- package/dist/api-HD7KU3BS.mjs +114 -0
- package/dist/api.cjs +145 -0
- package/dist/api.js +1 -1
- package/dist/api.mjs +116 -0
- package/dist/commands/api-HD7KU3BS.mjs +114 -0
- package/dist/commands/api-PBT4QZ34.mjs +16 -0
- package/dist/commands/api-PJL37LV3.mjs +17 -0
- package/dist/commands/build.cjs +877 -0
- package/dist/commands/build.js +6 -6
- package/dist/commands/build.mjs +717 -0
- package/dist/commands/chunk-GFQ4MUIC.mjs +117 -0
- package/dist/commands/chunk-MTM6M3TC.mjs +115 -0
- package/dist/commands/dev.cjs +670 -0
- package/dist/commands/dev.js +3 -3
- package/dist/commands/dev.mjs +512 -0
- package/dist/commands/index.cjs +1145 -0
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/index.js +1 -1
- package/dist/commands/index.mjs +982 -0
- package/dist/commands/prerender.cjs +404 -0
- package/dist/commands/prerender.d.ts +1 -1
- package/dist/commands/prerender.js +2 -2
- package/dist/commands/prerender.mjs +377 -0
- package/dist/commands/start.cjs +463 -0
- package/dist/commands/start.d.ts +1 -1
- package/dist/commands/start.js +2 -2
- package/dist/commands/start.mjs +428 -0
- package/dist/config.cjs +150 -0
- package/dist/config.d.ts +24 -7
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +8 -6
- package/dist/config.mjs +114 -0
- package/dist/define-config.cjs +32 -0
- package/dist/define-config.d.ts +12 -0
- package/dist/define-config.d.ts.map +1 -0
- package/dist/define-config.js +3 -0
- package/dist/define-config.mjs +7 -0
- package/dist/di.cjs +200 -0
- package/dist/di.d.ts +4 -4
- package/dist/di.d.ts.map +1 -1
- package/dist/di.js +9 -9
- package/dist/di.mjs +169 -0
- package/dist/document.cjs +115 -0
- package/dist/document.d.ts +1 -1
- package/dist/document.js +1 -1
- package/dist/document.mjs +86 -0
- package/dist/generated-api.cjs +43 -0
- package/dist/generated-api.js +1 -1
- package/dist/generated-api.mjs +18 -0
- package/dist/generated-server-fns.cjs +43 -0
- package/dist/generated-server-fns.mjs +18 -0
- package/dist/handler-core.cjs +211 -0
- package/dist/handler-core.d.ts +2 -2
- package/dist/handler-core.js +2 -2
- package/dist/handler-core.mjs +185 -0
- package/dist/handler.cjs +182 -0
- package/dist/handler.d.ts +3 -3
- package/dist/handler.js +3 -3
- package/dist/handler.mjs +152 -0
- package/dist/head.cjs +25 -0
- package/dist/head.d.ts +1 -1
- package/dist/head.js +1 -1
- package/dist/head.mjs +4 -0
- package/dist/image-loader.cjs +37 -0
- package/dist/image-loader.d.ts +25 -0
- package/dist/image-loader.d.ts.map +1 -0
- package/dist/image-loader.js +18 -0
- package/dist/image-loader.mjs +12 -0
- package/dist/image.cjs +151 -0
- package/dist/image.d.ts +2 -19
- package/dist/image.d.ts.map +1 -1
- package/dist/image.js +5 -21
- package/dist/image.mjs +126 -0
- package/dist/index.cjs +303 -0
- package/dist/index.d.ts +28 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -17
- package/dist/index.mjs +262 -0
- package/dist/interceptors.cjs +72 -0
- package/dist/interceptors.js +1 -1
- package/dist/interceptors.mjs +44 -0
- package/dist/internal.cjs +393 -0
- package/dist/internal.d.ts +6 -6
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +23 -15
- package/dist/internal.mjs +223 -0
- package/dist/middleware.cjs +58 -0
- package/dist/middleware.d.ts +3 -3
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +1 -1
- package/dist/middleware.mjs +31 -0
- package/dist/preload.cjs +42 -0
- package/dist/preload.js +1 -1
- package/dist/preload.mjs +18 -0
- package/dist/router.cjs +25 -0
- package/dist/router.d.ts +3 -3
- package/dist/router.js +3 -3
- package/dist/router.mjs +4 -0
- package/dist/server-fn-setup.cjs +46 -0
- package/dist/server-fn-setup.d.ts +1 -1
- package/dist/server-fn-setup.js +2 -2
- package/dist/server-fn-setup.mjs +22 -0
- package/dist/server-fn.cjs +109 -0
- package/dist/server-fn.d.ts +2 -2
- package/dist/server-fn.js +2 -2
- package/dist/server-fn.mjs +79 -0
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/dist/ui.cjs +123 -0
- package/dist/ui.js +1 -1
- package/dist/ui.mjs +93 -0
- package/package.json +63 -25
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
// src/commands/start.ts
|
|
2
|
+
import { resolve as resolve3 } from "node:path";
|
|
3
|
+
|
|
4
|
+
// src/config.ts
|
|
5
|
+
import { resolve, isAbsolute } from "node:path";
|
|
6
|
+
function resolveConfig(config = {}) {
|
|
7
|
+
const root = resolve(config.root ?? process.cwd());
|
|
8
|
+
return {
|
|
9
|
+
root,
|
|
10
|
+
ssr: config.ssr ?? true,
|
|
11
|
+
port: config.port ?? 3e3,
|
|
12
|
+
serverEntry: config.serverEntry ?? "src/entry-server.ts",
|
|
13
|
+
clientEntry: config.clientEntry ?? "src/entry-client.ts",
|
|
14
|
+
template: config.template ?? "index.html",
|
|
15
|
+
mountId: config.mountId ?? "root",
|
|
16
|
+
routesDir: config.routesDir ?? "src/routes",
|
|
17
|
+
apiDir: config.apiDir ?? "src/api",
|
|
18
|
+
middleware: config.middleware ?? "src/middleware.ts",
|
|
19
|
+
ssrNoExternal: config.ssrNoExternal ?? [],
|
|
20
|
+
resolve: config.resolve ?? [],
|
|
21
|
+
vitePlugin: config.vitePlugin ?? {},
|
|
22
|
+
plugins: config.plugins ?? [],
|
|
23
|
+
adapter: config.adapter,
|
|
24
|
+
i18n: resolveI18n(config.i18n),
|
|
25
|
+
prerender: resolvePrerender(config.prerender)
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function resolvePrerender(prerender) {
|
|
29
|
+
if (!prerender) return false;
|
|
30
|
+
const opts = typeof prerender === "object" ? prerender : {};
|
|
31
|
+
return {
|
|
32
|
+
routes: opts.routes?.length ? opts.routes : ["/"],
|
|
33
|
+
crawl: opts.crawl ?? true
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function resolveI18n(i18n) {
|
|
37
|
+
if (i18n === false) return false;
|
|
38
|
+
const opts = typeof i18n === "object" ? i18n : {};
|
|
39
|
+
return {
|
|
40
|
+
dir: opts.dir ?? "src/i18n",
|
|
41
|
+
defaultLocale: opts.defaultLocale,
|
|
42
|
+
locales: opts.locales
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/document.ts
|
|
47
|
+
import { extractHeadMarker } from "@fluixi/head";
|
|
48
|
+
function injectApp(template, appHtml, mountId = "root") {
|
|
49
|
+
const mount = new RegExp(`<div id=["']${mountId}["']\\s*>\\s*</div>`);
|
|
50
|
+
if (mount.test(template)) {
|
|
51
|
+
return template.replace(mount, `<div id="${mountId}">${appHtml}</div>`);
|
|
52
|
+
}
|
|
53
|
+
if (template.includes("<!--ssr-outlet-->")) {
|
|
54
|
+
return template.replace("<!--ssr-outlet-->", appHtml);
|
|
55
|
+
}
|
|
56
|
+
return template.replace("</body>", `<div id="${mountId}">${appHtml}</div></body>`);
|
|
57
|
+
}
|
|
58
|
+
function splitTemplate(template, mountId = "root") {
|
|
59
|
+
const mount = new RegExp(`<div id=["']${mountId}["']\\s*>\\s*</div>`);
|
|
60
|
+
const m = template.match(mount);
|
|
61
|
+
if (m && m.index !== void 0) {
|
|
62
|
+
return {
|
|
63
|
+
head: template.slice(0, m.index) + `<div id="${mountId}">`,
|
|
64
|
+
tail: `</div>` + template.slice(m.index + m[0].length)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const outlet = template.indexOf("<!--ssr-outlet-->");
|
|
68
|
+
if (outlet !== -1) {
|
|
69
|
+
return {
|
|
70
|
+
head: template.slice(0, outlet),
|
|
71
|
+
tail: template.slice(outlet + "<!--ssr-outlet-->".length)
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const body = template.indexOf("</body>");
|
|
75
|
+
if (body !== -1) {
|
|
76
|
+
return {
|
|
77
|
+
head: template.slice(0, body) + `<div id="${mountId}">`,
|
|
78
|
+
tail: `</div>` + template.slice(body)
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return { head: template, tail: "" };
|
|
82
|
+
}
|
|
83
|
+
function injectPreload(template, files) {
|
|
84
|
+
const tags = files.filter((file) => !template.includes(file)).map(
|
|
85
|
+
(file) => file.endsWith(".css") ? `<link rel="stylesheet" href="${file}">` : (
|
|
86
|
+
// `modulepreload` also fetches the chunk's own static imports.
|
|
87
|
+
`<link rel="modulepreload" href="${file}">`
|
|
88
|
+
)
|
|
89
|
+
);
|
|
90
|
+
if (tags.length === 0) return template;
|
|
91
|
+
const head = template.indexOf("</head>");
|
|
92
|
+
if (head === -1) return template;
|
|
93
|
+
return template.slice(0, head) + tags.join("") + template.slice(head);
|
|
94
|
+
}
|
|
95
|
+
function setHtmlAttr(html, name, value) {
|
|
96
|
+
const existing = new RegExp(`(<html\\b[^>]*?)\\s${name}="[^"]*"`, "i");
|
|
97
|
+
if (existing.test(html)) return html.replace(existing, `$1 ${name}="${value}"`);
|
|
98
|
+
return html.replace(/<html\b/i, `<html ${name}="${value}"`);
|
|
99
|
+
}
|
|
100
|
+
function injectAppAndHead(template, rendered, mountId = "root") {
|
|
101
|
+
const { head, body } = extractHeadMarker(rendered);
|
|
102
|
+
let html = injectApp(template, body, mountId);
|
|
103
|
+
if (head) {
|
|
104
|
+
if (head.headHtml) {
|
|
105
|
+
if (/<title[\s>]/i.test(head.headHtml)) html = html.replace(/<title>[\s\S]*?<\/title>/i, "");
|
|
106
|
+
html = html.replace("</head>", `${head.headHtml}</head>`);
|
|
107
|
+
}
|
|
108
|
+
for (const [k, v] of Object.entries(head.htmlAttrs)) html = setHtmlAttr(html, k, v);
|
|
109
|
+
}
|
|
110
|
+
return html;
|
|
111
|
+
}
|
|
112
|
+
function guardHandler(handler, onError) {
|
|
113
|
+
return async (request) => {
|
|
114
|
+
try {
|
|
115
|
+
return await handler(request);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
onError?.(e);
|
|
118
|
+
return new Response(String(e?.stack || e), {
|
|
119
|
+
status: 500,
|
|
120
|
+
headers: { "content-type": "text/plain; charset=utf-8" }
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/internal.ts
|
|
127
|
+
async function loadServerEnv(root, mode) {
|
|
128
|
+
const { loadEnv } = await import("vite");
|
|
129
|
+
const env = loadEnv(mode, root, "");
|
|
130
|
+
for (const [k, v] of Object.entries(env)) {
|
|
131
|
+
if (process.env[k] === void 0) process.env[k] = v;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
var _guardsInstalled = false;
|
|
135
|
+
function installCrashGuards() {
|
|
136
|
+
if (_guardsInstalled) return;
|
|
137
|
+
_guardsInstalled = true;
|
|
138
|
+
process.on(
|
|
139
|
+
"unhandledRejection",
|
|
140
|
+
(reason) => console.warn("[fluixi] unhandled rejection (backend likely down) —", reason?.message || reason)
|
|
141
|
+
);
|
|
142
|
+
process.on(
|
|
143
|
+
"uncaughtException",
|
|
144
|
+
(err) => console.warn("[fluixi] uncaught exception —", err?.message || err)
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/adapter.ts
|
|
149
|
+
import { readFile } from "node:fs/promises";
|
|
150
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
151
|
+
import { resolve as resolve2, join as join2, dirname } from "node:path";
|
|
152
|
+
import { pathToFileURL } from "node:url";
|
|
153
|
+
|
|
154
|
+
// src/handler.ts
|
|
155
|
+
import { createServer as createHttpServer } from "node:http";
|
|
156
|
+
import { stat } from "node:fs/promises";
|
|
157
|
+
import { createReadStream } from "node:fs";
|
|
158
|
+
import { Readable } from "node:stream";
|
|
159
|
+
import { join, extname } from "node:path";
|
|
160
|
+
function streamDocument(head, body, tail) {
|
|
161
|
+
const enc = new TextEncoder();
|
|
162
|
+
const stream = new ReadableStream({
|
|
163
|
+
async start(controller) {
|
|
164
|
+
try {
|
|
165
|
+
controller.enqueue(enc.encode(head));
|
|
166
|
+
const reader = body.getReader();
|
|
167
|
+
for (; ; ) {
|
|
168
|
+
const { done, value } = await reader.read();
|
|
169
|
+
if (done) break;
|
|
170
|
+
controller.enqueue(value);
|
|
171
|
+
}
|
|
172
|
+
controller.enqueue(enc.encode(tail));
|
|
173
|
+
controller.close();
|
|
174
|
+
} catch (err) {
|
|
175
|
+
controller.error(err);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
return new Response(stream, {
|
|
180
|
+
status: 200,
|
|
181
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function createRequestHandler(render) {
|
|
185
|
+
return async (request) => {
|
|
186
|
+
const url = new URL(request.url);
|
|
187
|
+
try {
|
|
188
|
+
const html = await render(url.pathname + url.search, request);
|
|
189
|
+
return new Response(html, {
|
|
190
|
+
status: 200,
|
|
191
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
192
|
+
});
|
|
193
|
+
} catch (e) {
|
|
194
|
+
return new Response(String(e?.stack || e), {
|
|
195
|
+
status: 500,
|
|
196
|
+
headers: { "content-type": "text/plain; charset=utf-8" }
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function nodeToRequest(req) {
|
|
202
|
+
const host = req.headers.host ?? "localhost";
|
|
203
|
+
const url = `http://${host}${req.url ?? "/"}`;
|
|
204
|
+
const headers = new Headers();
|
|
205
|
+
for (const [k, v] of Object.entries(req.headers)) {
|
|
206
|
+
if (Array.isArray(v)) v.forEach((x) => headers.append(k, x));
|
|
207
|
+
else if (v != null) headers.set(k, v);
|
|
208
|
+
}
|
|
209
|
+
const method = req.method ?? "GET";
|
|
210
|
+
const init = { method, headers };
|
|
211
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
212
|
+
init.body = req;
|
|
213
|
+
init.duplex = "half";
|
|
214
|
+
}
|
|
215
|
+
return new Request(url, init);
|
|
216
|
+
}
|
|
217
|
+
async function sendResponse(res, response) {
|
|
218
|
+
res.statusCode = response.status;
|
|
219
|
+
response.headers.forEach((value, key) => res.setHeader(key, value));
|
|
220
|
+
const body = response.body;
|
|
221
|
+
if (!body) {
|
|
222
|
+
res.end(await response.text().catch(() => ""));
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const reader = body.getReader();
|
|
226
|
+
try {
|
|
227
|
+
for (; ; ) {
|
|
228
|
+
const { done, value } = await reader.read();
|
|
229
|
+
if (done) break;
|
|
230
|
+
res.write(value);
|
|
231
|
+
}
|
|
232
|
+
} finally {
|
|
233
|
+
res.end();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
function toNodeHandler(handler) {
|
|
237
|
+
return async (req, res) => {
|
|
238
|
+
const response = await handler(nodeToRequest(req));
|
|
239
|
+
await sendResponse(res, response);
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function serveNode(handler, opts = {}) {
|
|
243
|
+
const port = opts.port ?? 3e3;
|
|
244
|
+
const node = toNodeHandler(handler);
|
|
245
|
+
const server = createHttpServer((req, res) => {
|
|
246
|
+
node(req, res).catch((e) => {
|
|
247
|
+
res.statusCode = 500;
|
|
248
|
+
res.end(String(e?.stack || e));
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
server.listen(
|
|
252
|
+
port,
|
|
253
|
+
() => opts.onListen ? opts.onListen(port) : console.log(` fluixi → http://localhost:${port}`)
|
|
254
|
+
);
|
|
255
|
+
return server;
|
|
256
|
+
}
|
|
257
|
+
var MIME = {
|
|
258
|
+
".js": "text/javascript",
|
|
259
|
+
".mjs": "text/javascript",
|
|
260
|
+
".css": "text/css",
|
|
261
|
+
".html": "text/html",
|
|
262
|
+
".json": "application/json",
|
|
263
|
+
".svg": "image/svg+xml",
|
|
264
|
+
".png": "image/png",
|
|
265
|
+
".jpg": "image/jpeg",
|
|
266
|
+
".jpeg": "image/jpeg",
|
|
267
|
+
".webp": "image/webp",
|
|
268
|
+
".ico": "image/x-icon",
|
|
269
|
+
".woff": "font/woff",
|
|
270
|
+
".woff2": "font/woff2",
|
|
271
|
+
".map": "application/json"
|
|
272
|
+
};
|
|
273
|
+
function withStaticFiles(handler, dir) {
|
|
274
|
+
return async (request) => {
|
|
275
|
+
const { pathname } = new URL(request.url);
|
|
276
|
+
if (pathname !== "/" && extname(pathname)) {
|
|
277
|
+
const file = join(dir, decodeURIComponent(pathname));
|
|
278
|
+
try {
|
|
279
|
+
const s = await stat(file);
|
|
280
|
+
if (s.isFile()) {
|
|
281
|
+
const body = Readable.toWeb(createReadStream(file));
|
|
282
|
+
return new Response(body, {
|
|
283
|
+
headers: {
|
|
284
|
+
"content-type": MIME[extname(file).toLowerCase()] ?? "application/octet-stream",
|
|
285
|
+
"content-length": String(s.size)
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
} catch {
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return handler(request);
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// src/middleware.ts
|
|
297
|
+
function normalizeMiddleware(mod) {
|
|
298
|
+
const def = mod?.default ?? mod;
|
|
299
|
+
if (def == null) return [];
|
|
300
|
+
if (Array.isArray(def)) return def.filter((m) => typeof m === "function");
|
|
301
|
+
return typeof def === "function" ? [def] : [];
|
|
302
|
+
}
|
|
303
|
+
function composeMiddleware(middlewares, core) {
|
|
304
|
+
if (middlewares.length === 0) return core;
|
|
305
|
+
return (request) => {
|
|
306
|
+
let lastCalled = -1;
|
|
307
|
+
const dispatch = (i) => {
|
|
308
|
+
if (i <= lastCalled) {
|
|
309
|
+
return Promise.reject(new Error("middleware called next() more than once"));
|
|
310
|
+
}
|
|
311
|
+
lastCalled = i;
|
|
312
|
+
const mw = middlewares[i];
|
|
313
|
+
if (!mw) return Promise.resolve(core(request));
|
|
314
|
+
return Promise.resolve(mw(request, () => dispatch(i + 1)));
|
|
315
|
+
};
|
|
316
|
+
return dispatch(0);
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/handler-core.ts
|
|
321
|
+
function createHandlerFrom({ template, mod, cfg, middleware, preload }) {
|
|
322
|
+
const withPreload = (html, pathname) => preload ? injectPreload(html, preload(pathname)) : html;
|
|
323
|
+
let core;
|
|
324
|
+
if (!cfg.ssr) {
|
|
325
|
+
core = createRequestHandler(async (url) => withPreload(template, url.split("?")[0]));
|
|
326
|
+
} else if (typeof mod.renderStream === "function") {
|
|
327
|
+
core = async (request) => {
|
|
328
|
+
const url = new URL(request.url);
|
|
329
|
+
const { head, tail } = splitTemplate(withPreload(template, url.pathname), cfg.mountId);
|
|
330
|
+
const body = await mod.renderStream(url.pathname + url.search, request);
|
|
331
|
+
return streamDocument(head, body, tail);
|
|
332
|
+
};
|
|
333
|
+
} else {
|
|
334
|
+
const render = mod.render ?? mod.default;
|
|
335
|
+
core = createRequestHandler(
|
|
336
|
+
async (url, request) => (
|
|
337
|
+
// `url` here is `pathname + search`; matching only ever wants the path.
|
|
338
|
+
injectAppAndHead(withPreload(template, url.split("?")[0]), await render(url, request), cfg.mountId)
|
|
339
|
+
)
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
if (typeof mod.handleServerFn === "function" && typeof mod.isServerFnRequest === "function") {
|
|
343
|
+
const render = core;
|
|
344
|
+
core = async (request) => mod.isServerFnRequest(request) ? mod.handleServerFn(request) : render(request);
|
|
345
|
+
}
|
|
346
|
+
if (typeof mod.handleApiRequest === "function" && typeof mod.isApiRequest === "function") {
|
|
347
|
+
const next = core;
|
|
348
|
+
core = async (request) => mod.isApiRequest(request) ? mod.handleApiRequest(request) : next(request);
|
|
349
|
+
}
|
|
350
|
+
if (!middleware) return guardHandler(core);
|
|
351
|
+
return guardHandler(composeMiddleware(normalizeMiddleware(middleware), core));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/preload.ts
|
|
355
|
+
import { matchRoutes } from "@fluixi/core/router";
|
|
356
|
+
function createPreloader(assets) {
|
|
357
|
+
const patterns = Object.keys(assets ?? {});
|
|
358
|
+
if (!patterns.length) return () => [];
|
|
359
|
+
const routes = patterns.map((path) => ({ path, meta: { path } }));
|
|
360
|
+
return (pathname) => {
|
|
361
|
+
const matched = matchRoutes(routes, pathname)?.matched;
|
|
362
|
+
const leaf = matched?.[matched.length - 1];
|
|
363
|
+
const pattern = leaf?.route.meta?.path;
|
|
364
|
+
return pattern ? assets[pattern] ?? [] : [];
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// src/adapter.ts
|
|
369
|
+
async function createProdHandler(config = {}) {
|
|
370
|
+
const cfg = resolveConfig(config);
|
|
371
|
+
const clientDir = resolve2(cfg.root, "dist/client");
|
|
372
|
+
const template = await readTemplate(clientDir, resolve2(cfg.root, "dist/server"));
|
|
373
|
+
const serverEntry = resolve2(cfg.root, "dist/server", "entry-server.js");
|
|
374
|
+
const mod = existsSync(serverEntry) ? await import(
|
|
375
|
+
/* @vite-ignore */
|
|
376
|
+
pathToFileURL(serverEntry).href
|
|
377
|
+
) : {};
|
|
378
|
+
const mwFile = resolve2(cfg.root, "dist/server", "middleware.js");
|
|
379
|
+
const middleware = existsSync(mwFile) ? await import(
|
|
380
|
+
/* @vite-ignore */
|
|
381
|
+
pathToFileURL(mwFile).href
|
|
382
|
+
) : void 0;
|
|
383
|
+
return createHandlerFrom({ template, mod, cfg, middleware, preload: await loadPreloader(cfg) });
|
|
384
|
+
}
|
|
385
|
+
async function readTemplate(clientDir, serverDir) {
|
|
386
|
+
const stashed = join2(serverDir, "template.html");
|
|
387
|
+
return readFile(existsSync(stashed) ? stashed : join2(clientDir, "index.html"), "utf-8");
|
|
388
|
+
}
|
|
389
|
+
async function loadPreloader(cfg) {
|
|
390
|
+
const file = resolve2(cfg.root, "dist/server", "route-assets.json");
|
|
391
|
+
if (!existsSync(file)) return void 0;
|
|
392
|
+
try {
|
|
393
|
+
return createPreloader(JSON.parse(await readFile(file, "utf-8")));
|
|
394
|
+
} catch {
|
|
395
|
+
return void 0;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
var nodeAdapter = {
|
|
399
|
+
name: "node",
|
|
400
|
+
// A Node server runs from the app directory, so node_modules is right there: keep the
|
|
401
|
+
// framework as imports instead of copying it into the bundle.
|
|
402
|
+
bundle: "external",
|
|
403
|
+
async serve({ handler, cfg, clientDir }) {
|
|
404
|
+
serveNode(withStaticFiles(handler, clientDir), {
|
|
405
|
+
port: cfg.port,
|
|
406
|
+
onListen: (p) => console.log(` fluixi start (node) → http://localhost:${p}`)
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
// src/commands/start.ts
|
|
412
|
+
async function start(config = {}) {
|
|
413
|
+
const cfg = resolveConfig(config);
|
|
414
|
+
installCrashGuards();
|
|
415
|
+
await loadServerEnv(cfg.root, "production");
|
|
416
|
+
const clientDir = resolve3(cfg.root, "dist/client");
|
|
417
|
+
const handler = await createProdHandler(cfg);
|
|
418
|
+
const adapter = cfg.adapter ?? nodeAdapter;
|
|
419
|
+
const served = await adapter.serve({ handler, cfg, clientDir });
|
|
420
|
+
if (served && typeof served === "object" && "fetch" in served) {
|
|
421
|
+
console.log(
|
|
422
|
+
` fluixi: the "${adapter.name}" adapter exports a fetch handler — there is no server to start. Deploy dist/ and let the platform invoke it, or run \`fluixi start\` with a filesystem adapter (nodeAdapter).`
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
export {
|
|
427
|
+
start
|
|
428
|
+
};
|
package/dist/config.cjs
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/*! @fluixi/start v0.1.0-alpha.75 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
|
|
31
|
+
// src/config.ts
|
|
32
|
+
var config_exports = {};
|
|
33
|
+
__export(config_exports, {
|
|
34
|
+
defineConfig: () => defineConfig,
|
|
35
|
+
loadConfig: () => loadConfig,
|
|
36
|
+
resolveConfig: () => resolveConfig
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(config_exports);
|
|
39
|
+
var import_node_url = require("node:url");
|
|
40
|
+
var import_node_fs = require("node:fs");
|
|
41
|
+
var import_promises = require("node:fs/promises");
|
|
42
|
+
var import_node_path = require("node:path");
|
|
43
|
+
|
|
44
|
+
// src/define-config.ts
|
|
45
|
+
function defineConfig(config) {
|
|
46
|
+
return config;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/config.ts
|
|
50
|
+
function resolveConfig(config = {}) {
|
|
51
|
+
const root = (0, import_node_path.resolve)(config.root ?? process.cwd());
|
|
52
|
+
return {
|
|
53
|
+
root,
|
|
54
|
+
ssr: config.ssr ?? true,
|
|
55
|
+
port: config.port ?? 3e3,
|
|
56
|
+
serverEntry: config.serverEntry ?? "src/entry-server.ts",
|
|
57
|
+
clientEntry: config.clientEntry ?? "src/entry-client.ts",
|
|
58
|
+
template: config.template ?? "index.html",
|
|
59
|
+
mountId: config.mountId ?? "root",
|
|
60
|
+
routesDir: config.routesDir ?? "src/routes",
|
|
61
|
+
apiDir: config.apiDir ?? "src/api",
|
|
62
|
+
middleware: config.middleware ?? "src/middleware.ts",
|
|
63
|
+
ssrNoExternal: config.ssrNoExternal ?? [],
|
|
64
|
+
resolve: config.resolve ?? [],
|
|
65
|
+
vitePlugin: config.vitePlugin ?? {},
|
|
66
|
+
plugins: config.plugins ?? [],
|
|
67
|
+
adapter: config.adapter,
|
|
68
|
+
i18n: resolveI18n(config.i18n),
|
|
69
|
+
prerender: resolvePrerender(config.prerender)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function resolvePrerender(prerender) {
|
|
73
|
+
if (!prerender) return false;
|
|
74
|
+
const opts = typeof prerender === "object" ? prerender : {};
|
|
75
|
+
return {
|
|
76
|
+
routes: opts.routes?.length ? opts.routes : ["/"],
|
|
77
|
+
crawl: opts.crawl ?? true
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function resolveI18n(i18n) {
|
|
81
|
+
if (i18n === false) return false;
|
|
82
|
+
const opts = typeof i18n === "object" ? i18n : {};
|
|
83
|
+
return {
|
|
84
|
+
dir: opts.dir ?? "src/i18n",
|
|
85
|
+
defaultLocale: opts.defaultLocale,
|
|
86
|
+
locales: opts.locales
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
var CONFIG_NAMES = [
|
|
90
|
+
"fluixi.config.ts",
|
|
91
|
+
"fluixi.config.mts",
|
|
92
|
+
"fluixi.config.mjs",
|
|
93
|
+
"fluixi.config.js"
|
|
94
|
+
];
|
|
95
|
+
async function loadConfig(root = process.cwd()) {
|
|
96
|
+
for (const name of CONFIG_NAMES) {
|
|
97
|
+
const file = (0, import_node_path.resolve)(root, name);
|
|
98
|
+
if ((0, import_node_fs.existsSync)(file)) return loadConfigFile(file);
|
|
99
|
+
}
|
|
100
|
+
return {};
|
|
101
|
+
}
|
|
102
|
+
async function loadConfigFile(file) {
|
|
103
|
+
if (!/\.[mc]?ts$/.test(file)) {
|
|
104
|
+
const mod = await import(
|
|
105
|
+
/* @vite-ignore */
|
|
106
|
+
(0, import_node_url.pathToFileURL)(file).href
|
|
107
|
+
);
|
|
108
|
+
return mod.default ?? mod;
|
|
109
|
+
}
|
|
110
|
+
const esbuild = await import("esbuild");
|
|
111
|
+
const { outputFiles } = await esbuild.build({
|
|
112
|
+
entryPoints: [file],
|
|
113
|
+
bundle: true,
|
|
114
|
+
write: false,
|
|
115
|
+
format: "esm",
|
|
116
|
+
platform: "node",
|
|
117
|
+
target: `node${process.versions.node.split(".")[0]}`,
|
|
118
|
+
sourcemap: "inline",
|
|
119
|
+
plugins: [
|
|
120
|
+
{
|
|
121
|
+
name: "fluixi:externalize-bare",
|
|
122
|
+
setup(b) {
|
|
123
|
+
b.onResolve({ filter: /.*/ }, (args) => {
|
|
124
|
+
if (args.kind === "entry-point") return null;
|
|
125
|
+
if (args.path.startsWith(".") || (0, import_node_path.isAbsolute)(args.path)) return null;
|
|
126
|
+
return { path: args.path, external: true };
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
]
|
|
131
|
+
});
|
|
132
|
+
const tempFile = `${file}.timestamp-${Date.now()}-${Math.random().toString(16).slice(2)}.mjs`;
|
|
133
|
+
await (0, import_promises.writeFile)(tempFile, outputFiles[0].text);
|
|
134
|
+
try {
|
|
135
|
+
const mod = await import(
|
|
136
|
+
/* @vite-ignore */
|
|
137
|
+
(0, import_node_url.pathToFileURL)(tempFile).href
|
|
138
|
+
);
|
|
139
|
+
return mod.default ?? mod;
|
|
140
|
+
} finally {
|
|
141
|
+
await (0, import_promises.unlink)(tempFile).catch(() => {
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
146
|
+
0 && (module.exports = {
|
|
147
|
+
defineConfig,
|
|
148
|
+
loadConfig,
|
|
149
|
+
resolveConfig
|
|
150
|
+
});
|
package/dist/config.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Adapter } from './adapter.js';
|
|
|
2
2
|
import type { ComponentResolverRule } from '@fluixi/compiler';
|
|
3
3
|
/**
|
|
4
4
|
* Fluixi app config. Place in `fluixi.config.{ts,js,mjs}` at the app root and wrap
|
|
5
|
-
* with `defineConfig` for types. Everything is optional
|
|
5
|
+
* with `defineConfig` for types. Everything is optional, the defaults match the
|
|
6
6
|
* conventional layout (src/entry-server.ts, src/entry-client.ts, index.html).
|
|
7
7
|
*/
|
|
8
8
|
export interface FluixiConfig {
|
|
@@ -10,7 +10,7 @@ export interface FluixiConfig {
|
|
|
10
10
|
root?: string;
|
|
11
11
|
/**
|
|
12
12
|
* Render mode. `true` (default) = SSR: the server entry renders HTML per request. `false`
|
|
13
|
-
* = SPA: the server serves the static shell and the client renders
|
|
13
|
+
* = SPA: the server serves the static shell and the client renders, but you still get the
|
|
14
14
|
* full @fluixi/start server (file routing, middleware, env, and `/_server` server functions).
|
|
15
15
|
* The same app code runs either way; flip this to choose where the first paint happens.
|
|
16
16
|
*/
|
|
@@ -31,7 +31,7 @@ export interface FluixiConfig {
|
|
|
31
31
|
apiDir?: string;
|
|
32
32
|
/** Request middleware module (default-exports `defineMiddleware([...])`). Default: src/middleware.ts */
|
|
33
33
|
middleware?: string;
|
|
34
|
-
/** SSR deps Vite must bundle (no-external)
|
|
34
|
+
/** SSR deps Vite must bundle (no-external), e.g. ESM packages without file extensions. */
|
|
35
35
|
ssrNoExternal?: string[];
|
|
36
36
|
/**
|
|
37
37
|
* Where a capitalised tag with no import in scope comes from.
|
|
@@ -41,9 +41,26 @@ export interface FluixiConfig {
|
|
|
41
41
|
* so a whole directory is one rule rather than a list to maintain.
|
|
42
42
|
*
|
|
43
43
|
* Only needed for tags you don't import. A `load:` directive works on your own import
|
|
44
|
-
* too
|
|
44
|
+
* too: see the docs on load strategies.
|
|
45
45
|
*/
|
|
46
46
|
resolve?: readonly ComponentResolverRule[];
|
|
47
|
+
/**
|
|
48
|
+
* Options handed to the fluixi vite plugin.
|
|
49
|
+
*
|
|
50
|
+
* A start app does not write `fluixi()` into a vite config, the framework adds the
|
|
51
|
+
* plugin itself, so this is where its options go: `propsDestructure`, and anything
|
|
52
|
+
* else the compiler takes. `resolve` above is the shorthand for the one most apps
|
|
53
|
+
* set, and wins over a `resolve` given here.
|
|
54
|
+
*/
|
|
55
|
+
vitePlugin?: Record<string, unknown>;
|
|
56
|
+
/**
|
|
57
|
+
* Extra vite plugins, appended after the framework's own.
|
|
58
|
+
*
|
|
59
|
+
* A start app has no vite config of its own, so a plugin it needs but the framework
|
|
60
|
+
* does not ship, a css engine for instance, is declared here instead of forcing a
|
|
61
|
+
* second config file into the project.
|
|
62
|
+
*/
|
|
63
|
+
plugins?: unknown[];
|
|
47
64
|
/**
|
|
48
65
|
* Deploy target. Decides how `fluixi build` bundles the server (see the adapter's
|
|
49
66
|
* `bundle` mode) and how `fluixi start` serves it. Unset keeps Vite's own bundling
|
|
@@ -54,7 +71,7 @@ export interface FluixiConfig {
|
|
|
54
71
|
* Built-in i18n auto-discovery. Scans `<dir>/<locale>.json` and exposes them as
|
|
55
72
|
* `virtual:fluixi-i18n` (`{ messages, locales, defaultLocale }`) to feed `createI18n`.
|
|
56
73
|
* `false` disables it; an object configures the folder / locales / default. Default: enabled
|
|
57
|
-
* with `src/i18n
|
|
74
|
+
* with `src/i18n`: a no-op until that folder has `*.json` files, so it's effectively opt-in.
|
|
58
75
|
*/
|
|
59
76
|
i18n?: boolean | {
|
|
60
77
|
dir?: string;
|
|
@@ -91,13 +108,13 @@ export type ResolvedConfig = Required<Omit<FluixiConfig, 'ssrNoExternal' | 'i18n
|
|
|
91
108
|
adapter?: Adapter;
|
|
92
109
|
};
|
|
93
110
|
/** Identity helper for editor types in `fluixi.config.ts`. */
|
|
94
|
-
export
|
|
111
|
+
export { defineConfig } from './define-config.js';
|
|
95
112
|
/** Apply defaults. */
|
|
96
113
|
export declare function resolveConfig(config?: FluixiConfig): ResolvedConfig;
|
|
97
114
|
/**
|
|
98
115
|
* Load `fluixi.config.{ts,mts,mjs,js}` from the app root. TypeScript configs are
|
|
99
116
|
* bundled on the fly with esbuild and imported; plain ESM is imported directly.
|
|
100
|
-
* No config is fine too
|
|
117
|
+
* No config is fine too: every field has a default.
|
|
101
118
|
*/
|
|
102
119
|
export declare function loadConfig(root?: string): Promise<FluixiConfig>;
|
|
103
120
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAE9D;;;;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,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAE9D;;;;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,0FAA0F;IAC1F,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC3C;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB;;;;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;AAG9D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,sBAAsB;AACtB,wBAAgB,aAAa,CAAC,MAAM,GAAE,YAAiB,GAAG,cAAc,CAqBvE;AAgCD;;;;GAIG;AACH,wBAAsB,UAAU,CAAC,IAAI,SAAgB,GAAG,OAAO,CAAC,YAAY,CAAC,CAM5E"}
|