weifuwu 0.18.2 → 0.18.4
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/cli.ts +10 -101
- package/dist/cli.js +7 -95
- package/dist/dist/agent/client.d.ts +2 -0
- package/dist/dist/agent/index.d.ts +2 -0
- package/dist/dist/agent/migrate.d.ts +6 -0
- package/dist/dist/agent/rest.d.ts +13 -0
- package/dist/dist/agent/run.d.ts +17 -0
- package/dist/dist/agent/types.d.ts +51 -0
- package/dist/dist/ai/workflow.d.ts +14 -0
- package/dist/dist/analytics.d.ts +15 -0
- package/dist/dist/client-locale.d.ts +5 -0
- package/dist/dist/client-pref.d.ts +3 -0
- package/dist/dist/client-state.d.ts +22 -0
- package/dist/dist/client-theme.d.ts +7 -0
- package/dist/dist/compress.d.ts +6 -0
- package/dist/dist/cookie.d.ts +12 -0
- package/dist/dist/deploy/config.d.ts +2 -0
- package/dist/dist/deploy/gateway.d.ts +2 -0
- package/dist/dist/deploy/index.d.ts +4 -0
- package/dist/dist/deploy/manager.d.ts +16 -0
- package/dist/dist/deploy/process.d.ts +14 -0
- package/dist/dist/deploy/types.d.ts +62 -0
- package/dist/dist/head.d.ts +6 -0
- package/dist/dist/helmet.d.ts +18 -0
- package/dist/dist/iii/client.d.ts +2 -0
- package/dist/dist/iii/index.d.ts +4 -0
- package/dist/dist/iii/register-worker.d.ts +10 -0
- package/dist/dist/iii/rest.d.ts +3 -0
- package/dist/dist/iii/stream.d.ts +82 -0
- package/dist/dist/iii/types.d.ts +133 -0
- package/dist/dist/iii/worker.d.ts +2 -0
- package/dist/dist/iii/ws.d.ts +29 -0
- package/dist/dist/index.js +8180 -0
- package/dist/dist/messager/agent.d.ts +6 -0
- package/dist/dist/messager/client.d.ts +2 -0
- package/dist/dist/messager/index.d.ts +2 -0
- package/dist/dist/messager/migrate.d.ts +2 -0
- package/dist/dist/messager/rest.d.ts +15 -0
- package/dist/dist/messager/types.d.ts +56 -0
- package/dist/dist/messager/ws.d.ts +14 -0
- package/dist/dist/preferences.d.ts +14 -0
- package/dist/dist/react.d.ts +12 -0
- package/dist/dist/react.js +637 -0
- package/dist/dist/request-id.d.ts +6 -0
- package/dist/dist/seo.d.ts +39 -0
- package/dist/dist/ssr/compile.d.ts +2 -0
- package/dist/{ssr → dist/ssr}/index.d.ts +1 -1
- package/dist/{ssr → dist/ssr}/index.js +372 -375
- package/dist/dist/ssr/ssr.d.ts +2 -0
- package/dist/dist/tenant/client.d.ts +2 -0
- package/dist/dist/tenant/graphql.d.ts +3 -0
- package/dist/dist/tenant/index.d.ts +2 -0
- package/dist/dist/tenant/migrate.d.ts +6 -0
- package/dist/dist/tenant/rest.d.ts +3 -0
- package/dist/dist/tenant/schema.d.ts +5 -0
- package/dist/dist/tenant/types.d.ts +48 -0
- package/dist/dist/tenant/utils.d.ts +10 -0
- package/dist/dist/types.d.ts +19 -0
- package/dist/dist/use-flash-message.d.ts +1 -0
- package/dist/error-boundary.d.ts +2 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +213 -53
- package/dist/layout.d.ts +2 -0
- package/dist/live.d.ts +6 -0
- package/dist/not-found.d.ts +2 -0
- package/dist/router.d.ts +9 -9
- package/dist/ssr.d.ts +2 -0
- package/dist/stream.d.ts +14 -0
- package/dist/tailwind.d.ts +2 -0
- package/package.json +3 -4
- package/dist/ssr/ssr.d.ts +0 -3
- /package/dist/{ssr/compile.d.ts → compile.d.ts} +0 -0
- /package/dist/{ssr → dist/ssr}/error-boundary.d.ts +0 -0
- /package/dist/{ssr → dist/ssr}/layout.d.ts +0 -0
- /package/dist/{ssr → dist/ssr}/live.d.ts +0 -0
- /package/dist/{ssr → dist/ssr}/not-found.d.ts +0 -0
- /package/dist/{ssr → dist/ssr}/stream.d.ts +0 -0
- /package/dist/{ssr → dist/ssr}/tailwind.d.ts +0 -0
|
@@ -231,279 +231,6 @@ function setCtx(value) {
|
|
|
231
231
|
}
|
|
232
232
|
var TsxContext = createContext(DEFAULT_CTX);
|
|
233
233
|
|
|
234
|
-
// ssr/ssr.ts
|
|
235
|
-
var als = new AsyncLocalStorage();
|
|
236
|
-
__registerAls(() => als.getStore());
|
|
237
|
-
var isDev = process.env.NODE_ENV !== "production";
|
|
238
|
-
function id2(s) {
|
|
239
|
-
return createHash2("md5").update(s).digest("hex").slice(0, 8);
|
|
240
|
-
}
|
|
241
|
-
async function buildClientBundle(entryPath, layoutPaths) {
|
|
242
|
-
try {
|
|
243
|
-
const absEntry = resolve2(entryPath);
|
|
244
|
-
const absLayouts = layoutPaths.map((p) => resolve2(p));
|
|
245
|
-
const layoutImports = absLayouts.map((p) => `import${JSON.stringify(p)};`).join("");
|
|
246
|
-
const _sc = `(function(){var k='__WEIFUWU_CTX_STORE';var s=typeof globalThis!='undefined'&&globalThis[k];if(!s)return function(){};return function(v){s._ctx={...s._ctx,...v};s._snapshot={params:s._ctx.params,query:s._ctx.query,user:s._ctx.user,parsed:s._ctx.parsed,prefs:s._ctx.prefs,env:s._ctx.env};s._listeners.forEach(function(fn){fn()})}})()`;
|
|
247
|
-
const code = [
|
|
248
|
-
layoutImports,
|
|
249
|
-
`import{hydrateRoot}from'react-dom/client';`,
|
|
250
|
-
`import{createElement,useState,useEffect}from'react';`,
|
|
251
|
-
`import{TsxContext}from'weifuwu/react';`,
|
|
252
|
-
`import P from${JSON.stringify(absEntry)};`,
|
|
253
|
-
`var setCtx=${_sc};`,
|
|
254
|
-
`const c=document.getElementById('__weifuwu_root');`,
|
|
255
|
-
`if(window.__WEIFUWU_PROPS)setCtx({loaderData:window.__WEIFUWU_PROPS});`,
|
|
256
|
-
`if(!window.__WFW_ROOT){`,
|
|
257
|
-
`function App(){`,
|
|
258
|
-
`const[p,setP]=useState({C:P});`,
|
|
259
|
-
`useEffect(()=>{window.__WFW_SET_PAGE=(C)=>{setCtx({loaderData:window.__WEIFUWU_PROPS});setP({C})}},[]);`,
|
|
260
|
-
`const ctx=window.__WEIFUWU_CTX||{};`,
|
|
261
|
-
`return createElement(TsxContext.Provider,{value:ctx},`,
|
|
262
|
-
`createElement(p.C,null))`,
|
|
263
|
-
`}`,
|
|
264
|
-
`window.__WFW_ROOT=hydrateRoot(c,createElement(App));`,
|
|
265
|
-
`}else{`,
|
|
266
|
-
`window.__WFW_SET_PAGE?.(P);`,
|
|
267
|
-
`}`
|
|
268
|
-
].join("");
|
|
269
|
-
const { default: esbuild2 } = await import("esbuild");
|
|
270
|
-
const result = await esbuild2.build({
|
|
271
|
-
stdin: { contents: code, loader: "tsx", resolveDir: dirname2(absEntry) },
|
|
272
|
-
bundle: true,
|
|
273
|
-
format: "esm",
|
|
274
|
-
jsx: "automatic",
|
|
275
|
-
jsxImportSource: "react",
|
|
276
|
-
banner: { js: "self.process={env:{}};" },
|
|
277
|
-
loader: { ".node": "empty" },
|
|
278
|
-
write: false,
|
|
279
|
-
minify: true
|
|
280
|
-
});
|
|
281
|
-
return result.outputFiles[0].contents;
|
|
282
|
-
} catch (err) {
|
|
283
|
-
console.error("hydration bundle failed:", err);
|
|
284
|
-
return null;
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
var bundleRegistry = /* @__PURE__ */ new Map();
|
|
288
|
-
function serializeLoaderData(ctx) {
|
|
289
|
-
const data = {};
|
|
290
|
-
for (const key of Object.keys(ctx)) {
|
|
291
|
-
if (!["params", "query", "mountPath", "layoutStack"].includes(key)) {
|
|
292
|
-
data[key] = ctx[key];
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
return data;
|
|
296
|
-
}
|
|
297
|
-
function ssr(path) {
|
|
298
|
-
const entryId = id2(resolve2(path));
|
|
299
|
-
const bundleKey = `/__ssr/${entryId}.js`;
|
|
300
|
-
let bundleBuilt = false;
|
|
301
|
-
return async (req, ctx) => {
|
|
302
|
-
const pageMod = await compileTsx(path);
|
|
303
|
-
const Component = pageMod.default;
|
|
304
|
-
if (!Component) return new Response("", { status: 500 });
|
|
305
|
-
const layouts = ctx.layoutStack || [];
|
|
306
|
-
const layoutComponents = layouts.map((l) => l.component);
|
|
307
|
-
const layoutPaths = layouts.map((l) => l.path);
|
|
308
|
-
const base = (ctx.mountPath || "").replace(/\/$/, "");
|
|
309
|
-
const loaderData = serializeLoaderData(ctx);
|
|
310
|
-
const ctxValue = {
|
|
311
|
-
params: ctx.params,
|
|
312
|
-
query: ctx.query,
|
|
313
|
-
user: ctx.user ?? {},
|
|
314
|
-
parsed: ctx.parsed ?? {},
|
|
315
|
-
prefs: ctx.prefs ?? {},
|
|
316
|
-
loaderData,
|
|
317
|
-
env: ctx.env ?? {}
|
|
318
|
-
};
|
|
319
|
-
return als.run(ctxValue, async () => {
|
|
320
|
-
setCtx(ctxValue);
|
|
321
|
-
let element = createElement(
|
|
322
|
-
TsxContext.Provider,
|
|
323
|
-
{ value: ctxValue },
|
|
324
|
-
createElement(
|
|
325
|
-
"div",
|
|
326
|
-
{ id: "__weifuwu_root" },
|
|
327
|
-
createElement(Component, null)
|
|
328
|
-
)
|
|
329
|
-
);
|
|
330
|
-
if (layoutComponents.length === 0) {
|
|
331
|
-
element = createElement(
|
|
332
|
-
"html",
|
|
333
|
-
{ lang: "en" },
|
|
334
|
-
createElement(
|
|
335
|
-
"head",
|
|
336
|
-
null,
|
|
337
|
-
createElement("meta", { charSet: "utf-8" }),
|
|
338
|
-
createElement("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
|
|
339
|
-
createElement("title", null, "weifuwu")
|
|
340
|
-
),
|
|
341
|
-
createElement("body", null, element)
|
|
342
|
-
);
|
|
343
|
-
} else {
|
|
344
|
-
for (const L of layoutComponents.toReversed()) {
|
|
345
|
-
element = createElement(L, { children: element });
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
let bundle = null;
|
|
349
|
-
if (!bundleBuilt) {
|
|
350
|
-
const buf = await buildClientBundle(path, layoutPaths);
|
|
351
|
-
if (buf) {
|
|
352
|
-
bundleRegistry.set(bundleKey, buf);
|
|
353
|
-
bundleBuilt = true;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
if (bundleRegistry.has(bundleKey)) {
|
|
357
|
-
bundle = { url: bundleKey };
|
|
358
|
-
}
|
|
359
|
-
const { renderToReadableStream } = await import("react-dom/server");
|
|
360
|
-
const stream = await renderToReadableStream(element);
|
|
361
|
-
return streamResponse(stream, {
|
|
362
|
-
ctx,
|
|
363
|
-
base,
|
|
364
|
-
isDev,
|
|
365
|
-
bundle,
|
|
366
|
-
loaderData
|
|
367
|
-
});
|
|
368
|
-
});
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
function ssrBundleHandler() {
|
|
372
|
-
return (req, ctx) => {
|
|
373
|
-
const url = new URL(req.url);
|
|
374
|
-
const buf = bundleRegistry.get(url.pathname);
|
|
375
|
-
return buf ? new Response(buf, {
|
|
376
|
-
headers: { "content-type": "application/javascript; charset=utf-8" }
|
|
377
|
-
}) : new Response("", { status: 404 });
|
|
378
|
-
};
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// ssr/layout.ts
|
|
382
|
-
function layout(path) {
|
|
383
|
-
return async (req, ctx, next) => {
|
|
384
|
-
const mod = await compileTsx(path);
|
|
385
|
-
const Component = mod.default;
|
|
386
|
-
if (!Component) throw new Error(`Layout ${path} has no default export`);
|
|
387
|
-
ctx.layoutStack = [...ctx.layoutStack || [], { path, component: Component }];
|
|
388
|
-
return next(req, ctx);
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// ssr/tailwind.ts
|
|
393
|
-
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
394
|
-
import { relative, resolve as resolve3 } from "node:path";
|
|
395
|
-
var isDev2 = process.env.NODE_ENV !== "production";
|
|
396
|
-
function tailwind(cssPath, scanDir) {
|
|
397
|
-
let compiledCss = "";
|
|
398
|
-
let twWatcher = null;
|
|
399
|
-
return async (req, ctx, next) => {
|
|
400
|
-
const url = new URL(req.url);
|
|
401
|
-
if (url.pathname === "/__wfw/style.css") {
|
|
402
|
-
if (!compiledCss) compiledCss = await compile(cssPath, scanDir);
|
|
403
|
-
return new Response(compiledCss || "", {
|
|
404
|
-
headers: { "content-type": "text/css; charset=utf-8" }
|
|
405
|
-
});
|
|
406
|
-
}
|
|
407
|
-
ctx.compiledTailwindCss = compiledCss;
|
|
408
|
-
if (isDev2 && !twWatcher) {
|
|
409
|
-
twWatcher = watchFile(cssPath, () => {
|
|
410
|
-
compiledCss = "";
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
return next(req, ctx);
|
|
414
|
-
};
|
|
415
|
-
}
|
|
416
|
-
async function compile(cssPath, scanDir) {
|
|
417
|
-
try {
|
|
418
|
-
const inputFile = resolve3(cssPath);
|
|
419
|
-
if (!existsSync2(inputFile)) {
|
|
420
|
-
mkdirSync2(dirname3(inputFile), { recursive: true });
|
|
421
|
-
writeFileSync(inputFile, '@import "tailwindcss"\n', "utf-8");
|
|
422
|
-
}
|
|
423
|
-
const { default: tailwindPlugin } = await import("@tailwindcss/postcss");
|
|
424
|
-
const { default: postcss } = await import("postcss");
|
|
425
|
-
let src = readFileSync2(inputFile, "utf-8");
|
|
426
|
-
const scanSource = scanDir ? relative(dirname3(inputFile), scanDir) || "." : ".";
|
|
427
|
-
const sourcePath = scanSource === "." ? "./" : `./${scanSource}/`;
|
|
428
|
-
src = `@source "${sourcePath}";
|
|
429
|
-
${src}`;
|
|
430
|
-
const result = await postcss([tailwindPlugin()]).process(src, { from: inputFile });
|
|
431
|
-
return result.css;
|
|
432
|
-
} catch (err) {
|
|
433
|
-
console.warn("Tailwind CSS processing failed:", err.message);
|
|
434
|
-
return "";
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
function dirname3(p) {
|
|
438
|
-
return p.substring(0, p.lastIndexOf("/")) || "/";
|
|
439
|
-
}
|
|
440
|
-
function watchFile(path, onChange) {
|
|
441
|
-
let watcher = null;
|
|
442
|
-
import("chokidar").then((chokidar2) => {
|
|
443
|
-
watcher = chokidar2.default.watch(resolve3(path), { persistent: false });
|
|
444
|
-
watcher.on("change", onChange);
|
|
445
|
-
});
|
|
446
|
-
return watcher;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
// ssr/not-found.ts
|
|
450
|
-
function notFound(path) {
|
|
451
|
-
return async (req, ctx) => {
|
|
452
|
-
if (!path) return new Response("Not Found", { status: 404 });
|
|
453
|
-
const mod = await compileTsx(path);
|
|
454
|
-
const Component = mod?.default;
|
|
455
|
-
const body = Component ? "404 - Not Found" : "404 - Not Found";
|
|
456
|
-
return new Response(body, {
|
|
457
|
-
status: 404,
|
|
458
|
-
headers: { "content-type": "text/html; charset=utf-8" }
|
|
459
|
-
});
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
// ssr/error-boundary.ts
|
|
464
|
-
import { createElement as createElement2 } from "react";
|
|
465
|
-
import { TextEncoder as TextEncoder2 } from "node:util";
|
|
466
|
-
function errorBoundary(errorPath) {
|
|
467
|
-
return async (req, ctx, next) => {
|
|
468
|
-
try {
|
|
469
|
-
return await next(req, ctx);
|
|
470
|
-
} catch (err) {
|
|
471
|
-
const mod = await compileTsx(errorPath);
|
|
472
|
-
const ErrorComponent = mod.default;
|
|
473
|
-
if (!ErrorComponent) throw err;
|
|
474
|
-
const layouts = (ctx.layoutStack || []).map((l) => l.component);
|
|
475
|
-
const stream = await import("react-dom/server").then((m) => m.renderToReadableStream(
|
|
476
|
-
createElement2(ErrorComponent, {
|
|
477
|
-
error: err instanceof Error ? err : new Error(String(err)),
|
|
478
|
-
reset: () => {
|
|
479
|
-
}
|
|
480
|
-
})
|
|
481
|
-
));
|
|
482
|
-
const reader = stream.getReader();
|
|
483
|
-
const chunks = [];
|
|
484
|
-
while (true) {
|
|
485
|
-
const { done, value } = await reader.read();
|
|
486
|
-
if (done) break;
|
|
487
|
-
chunks.push(value);
|
|
488
|
-
}
|
|
489
|
-
const encoder = new TextEncoder2();
|
|
490
|
-
const body = chunks.reduce((acc, c) => {
|
|
491
|
-
const merged = new Uint8Array(acc.length + c.length);
|
|
492
|
-
merged.set(acc);
|
|
493
|
-
merged.set(c, acc.length);
|
|
494
|
-
return merged;
|
|
495
|
-
}, new Uint8Array(0));
|
|
496
|
-
return new Response(body, {
|
|
497
|
-
status: 500,
|
|
498
|
-
headers: { "content-type": "text/html; charset=utf-8" }
|
|
499
|
-
});
|
|
500
|
-
}
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// ssr/live.ts
|
|
505
|
-
import chokidar from "chokidar";
|
|
506
|
-
|
|
507
234
|
// router.ts
|
|
508
235
|
import { WebSocketServer } from "ws";
|
|
509
236
|
var createTrieNode = () => ({
|
|
@@ -632,6 +359,11 @@ var Router = class _Router {
|
|
|
632
359
|
return this;
|
|
633
360
|
}
|
|
634
361
|
route(method, path, ...args) {
|
|
362
|
+
const last = args[args.length - 1];
|
|
363
|
+
if (last instanceof _Router) {
|
|
364
|
+
this._mountRouter(path, last);
|
|
365
|
+
return this;
|
|
366
|
+
}
|
|
635
367
|
const handler = args.pop();
|
|
636
368
|
const middlewares = args;
|
|
637
369
|
const segments = this.splitPath(path);
|
|
@@ -779,117 +511,383 @@ var Router = class _Router {
|
|
|
779
511
|
wildcardIdx = i;
|
|
780
512
|
}
|
|
781
513
|
}
|
|
782
|
-
const segment = segments[i];
|
|
783
|
-
if (!segment) break;
|
|
784
|
-
const next = matchTrieNode(node, segment, params);
|
|
785
|
-
if (!next) {
|
|
786
|
-
if (wildcardHandler) {
|
|
787
|
-
params["*"] = segments.slice(wildcardIdx).join("/");
|
|
788
|
-
return { handler: wildcardHandler, middlewares: wildcardMws, pathMws, params };
|
|
789
|
-
}
|
|
790
|
-
return null;
|
|
514
|
+
const segment = segments[i];
|
|
515
|
+
if (!segment) break;
|
|
516
|
+
const next = matchTrieNode(node, segment, params);
|
|
517
|
+
if (!next) {
|
|
518
|
+
if (wildcardHandler) {
|
|
519
|
+
params["*"] = segments.slice(wildcardIdx).join("/");
|
|
520
|
+
return { handler: wildcardHandler, middlewares: wildcardMws, pathMws, params };
|
|
521
|
+
}
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
node = next;
|
|
525
|
+
}
|
|
526
|
+
pathMws.push(...node.pathMws);
|
|
527
|
+
const handler = node.handlers.get(method) || node.handlers.get("*");
|
|
528
|
+
if (handler) {
|
|
529
|
+
if (node.wildcard) params["*"] = segments.slice(segments.length).join("/");
|
|
530
|
+
return {
|
|
531
|
+
handler,
|
|
532
|
+
middlewares: node.middlewares.get(method) || node.middlewares.get("*") || [],
|
|
533
|
+
pathMws,
|
|
534
|
+
params
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
if (wildcardHandler) {
|
|
538
|
+
params["*"] = segments.slice(wildcardIdx).join("/");
|
|
539
|
+
return { handler: wildcardHandler, middlewares: wildcardMws, pathMws, params };
|
|
540
|
+
}
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
matchWsTrie(root, segments) {
|
|
544
|
+
let node = root;
|
|
545
|
+
const params = {};
|
|
546
|
+
for (const segment of segments) {
|
|
547
|
+
const next = matchWsNode(node, segment, params);
|
|
548
|
+
if (!next) return null;
|
|
549
|
+
node = next;
|
|
550
|
+
}
|
|
551
|
+
return node.handler ? { handler: node.handler, middlewares: node.middlewares, params } : null;
|
|
552
|
+
}
|
|
553
|
+
async handle(req, ctx, segments, query) {
|
|
554
|
+
const match = this.matchTrie(req.method, segments);
|
|
555
|
+
if (match?.handler) {
|
|
556
|
+
const { handler, middlewares: routeMws, pathMws, params } = match;
|
|
557
|
+
const allMws = this.globalMws.length + pathMws.length + routeMws.length === 0 ? [] : [...this.globalMws, ...pathMws, ...routeMws];
|
|
558
|
+
const ctxWithMatch = { ...ctx, params: { ...ctx.params, ...params } };
|
|
559
|
+
try {
|
|
560
|
+
return await this.runChain(allMws, handler, req, ctxWithMatch);
|
|
561
|
+
} catch (e) {
|
|
562
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
563
|
+
return this.errorHandler ? this.errorHandler(err, req, ctxWithMatch) : new Response("Internal Server Error", { status: 500 });
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
if (this.globalMws.length > 0) {
|
|
567
|
+
try {
|
|
568
|
+
const delegate = () => new Response("Not Found", { status: 404 });
|
|
569
|
+
return await this.runChain(this.globalMws, delegate, req, ctx);
|
|
570
|
+
} catch (e) {
|
|
571
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
572
|
+
return this.errorHandler ? this.errorHandler(err, req, ctx) : new Response("Internal Server Error", { status: 500 });
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return new Response("Not Found", { status: 404 });
|
|
576
|
+
}
|
|
577
|
+
async runChain(middlewares, finalHandler, req, ctx) {
|
|
578
|
+
let index = 0;
|
|
579
|
+
const dispatch = async (req2, ctx2) => {
|
|
580
|
+
if (index < middlewares.length) {
|
|
581
|
+
const mw = middlewares[index++];
|
|
582
|
+
return mw ? await mw(req2, ctx2, dispatch) : new Response("Middleware error", { status: 500 });
|
|
583
|
+
}
|
|
584
|
+
return await finalHandler(req2, ctx2);
|
|
585
|
+
};
|
|
586
|
+
return dispatch(req, ctx);
|
|
587
|
+
}
|
|
588
|
+
};
|
|
589
|
+
function upgradeSocket(wss, req, socket, head, handler, ctx) {
|
|
590
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
591
|
+
if (handler.open) {
|
|
592
|
+
handler.open(ws, ctx);
|
|
593
|
+
}
|
|
594
|
+
ws.on("message", (data) => {
|
|
595
|
+
handler.message?.(ws, ctx, data);
|
|
596
|
+
});
|
|
597
|
+
ws.on("close", () => {
|
|
598
|
+
handler.close?.(ws, ctx);
|
|
599
|
+
});
|
|
600
|
+
ws.on("error", (err) => {
|
|
601
|
+
handler.error?.(ws, ctx, err);
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
function sendHttpResponseOnSocket(socket, response) {
|
|
606
|
+
const statusLine = `HTTP/1.1 ${response.status} ${response.statusText}`;
|
|
607
|
+
const headerLines = [statusLine];
|
|
608
|
+
response.headers.forEach((value, key) => {
|
|
609
|
+
headerLines.push(`${key}: ${value}`);
|
|
610
|
+
});
|
|
611
|
+
headerLines.push("Connection: close");
|
|
612
|
+
headerLines.push("");
|
|
613
|
+
const headerStr = headerLines.join("\r\n");
|
|
614
|
+
response.arrayBuffer().then((buf) => {
|
|
615
|
+
const body = Buffer.from(buf);
|
|
616
|
+
socket.write(headerStr + "\r\n" + body.toString());
|
|
617
|
+
socket.end();
|
|
618
|
+
}).catch(() => {
|
|
619
|
+
socket.write(headerStr + "\r\n");
|
|
620
|
+
socket.end();
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
// ssr/ssr.ts
|
|
625
|
+
var als = new AsyncLocalStorage();
|
|
626
|
+
__registerAls(() => als.getStore());
|
|
627
|
+
var isDev = process.env.NODE_ENV !== "production";
|
|
628
|
+
var bundleCache = /* @__PURE__ */ new Map();
|
|
629
|
+
function id2(s) {
|
|
630
|
+
return createHash2("md5").update(s).digest("hex").slice(0, 8);
|
|
631
|
+
}
|
|
632
|
+
function serializeLoaderData(ctx) {
|
|
633
|
+
const data = {};
|
|
634
|
+
for (const key of Object.keys(ctx)) {
|
|
635
|
+
if (!["params", "query", "mountPath", "layoutStack"].includes(key)) {
|
|
636
|
+
data[key] = ctx[key];
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
return data;
|
|
640
|
+
}
|
|
641
|
+
async function buildClientBundle(entryPath, layoutPaths) {
|
|
642
|
+
try {
|
|
643
|
+
const absEntry = resolve2(entryPath);
|
|
644
|
+
const absLayouts = layoutPaths.map((p) => resolve2(p));
|
|
645
|
+
const layoutImports = absLayouts.map((p) => `import${JSON.stringify(p)};`).join("");
|
|
646
|
+
const _sc = `(function(){var k='__WEIFUWU_CTX_STORE';var s=typeof globalThis!='undefined'&&globalThis[k];if(!s)return function(){};return function(v){s._ctx={...s._ctx,...v};s._snapshot={params:s._ctx.params,query:s._ctx.query,user:s._ctx.user,parsed:s._ctx.parsed,prefs:s._ctx.prefs,env:s._ctx.env};s._listeners.forEach(function(fn){fn()})}})()`;
|
|
647
|
+
const code = [
|
|
648
|
+
layoutImports,
|
|
649
|
+
`import{hydrateRoot}from'react-dom/client';`,
|
|
650
|
+
`import{createElement,useState,useEffect}from'react';`,
|
|
651
|
+
`import{TsxContext}from'weifuwu/react';`,
|
|
652
|
+
`import P from${JSON.stringify(absEntry)};`,
|
|
653
|
+
`var setCtx=${_sc};`,
|
|
654
|
+
`const c=document.getElementById('__weifuwu_root');`,
|
|
655
|
+
`if(window.__WEIFUWU_PROPS)setCtx({loaderData:window.__WEIFUWU_PROPS});`,
|
|
656
|
+
`if(!window.__WFW_ROOT){`,
|
|
657
|
+
`function App(){`,
|
|
658
|
+
`const[p,setP]=useState({C:P});`,
|
|
659
|
+
`useEffect(()=>{window.__WFW_SET_PAGE=(C)=>{setCtx({loaderData:window.__WEIFUWU_PROPS});setP({C})}},[]);`,
|
|
660
|
+
`const ctx=window.__WEIFUWU_CTX||{};`,
|
|
661
|
+
`return createElement(TsxContext.Provider,{value:ctx},`,
|
|
662
|
+
`createElement(p.C,null))`,
|
|
663
|
+
`}`,
|
|
664
|
+
`window.__WFW_ROOT=hydrateRoot(c,createElement(App));`,
|
|
665
|
+
`}else{`,
|
|
666
|
+
`window.__WFW_SET_PAGE?.(P);`,
|
|
667
|
+
`}`
|
|
668
|
+
].join("");
|
|
669
|
+
const { default: esbuild2 } = await import("esbuild");
|
|
670
|
+
const result = await esbuild2.build({
|
|
671
|
+
stdin: { contents: code, loader: "tsx", resolveDir: dirname2(absEntry) },
|
|
672
|
+
bundle: true,
|
|
673
|
+
format: "esm",
|
|
674
|
+
jsx: "automatic",
|
|
675
|
+
jsxImportSource: "react",
|
|
676
|
+
banner: { js: "self.process={env:{}};" },
|
|
677
|
+
loader: { ".node": "empty" },
|
|
678
|
+
write: false,
|
|
679
|
+
minify: true
|
|
680
|
+
});
|
|
681
|
+
return result.outputFiles[0].contents;
|
|
682
|
+
} catch (err) {
|
|
683
|
+
console.error("hydration bundle failed:", err);
|
|
684
|
+
return null;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
function ssr(path) {
|
|
688
|
+
const entryId = id2(resolve2(path));
|
|
689
|
+
const bundleKey = `/__ssr/${entryId}.js`;
|
|
690
|
+
const r = new Router();
|
|
691
|
+
r.get("/__ssr/:path", (req, ctx) => {
|
|
692
|
+
const buf = bundleCache.get("/__ssr/" + ctx.params.path);
|
|
693
|
+
return buf ? new Response(buf, {
|
|
694
|
+
headers: { "content-type": "application/javascript; charset=utf-8" }
|
|
695
|
+
}) : new Response("", { status: 404 });
|
|
696
|
+
});
|
|
697
|
+
r.get("/", async (req, ctx) => {
|
|
698
|
+
const pageMod = await compileTsx(path);
|
|
699
|
+
const Component = pageMod.default;
|
|
700
|
+
if (!Component) return new Response("", { status: 500 });
|
|
701
|
+
const layouts = ctx.layoutStack || [];
|
|
702
|
+
const layoutComponents = layouts.map((l) => l.component);
|
|
703
|
+
const layoutPaths = layouts.map((l) => l.path);
|
|
704
|
+
const base = (ctx.mountPath || "").replace(/\/$/, "");
|
|
705
|
+
const loaderData = serializeLoaderData(ctx);
|
|
706
|
+
const ctxValue = {
|
|
707
|
+
params: ctx.params,
|
|
708
|
+
query: ctx.query,
|
|
709
|
+
user: ctx.user ?? {},
|
|
710
|
+
parsed: ctx.parsed ?? {},
|
|
711
|
+
prefs: ctx.prefs ?? {},
|
|
712
|
+
loaderData,
|
|
713
|
+
env: ctx.env ?? {}
|
|
714
|
+
};
|
|
715
|
+
return als.run(ctxValue, async () => {
|
|
716
|
+
setCtx(ctxValue);
|
|
717
|
+
let element = createElement(
|
|
718
|
+
TsxContext.Provider,
|
|
719
|
+
{ value: ctxValue },
|
|
720
|
+
createElement(
|
|
721
|
+
"div",
|
|
722
|
+
{ id: "__weifuwu_root" },
|
|
723
|
+
createElement(Component, null)
|
|
724
|
+
)
|
|
725
|
+
);
|
|
726
|
+
if (layoutComponents.length === 0) {
|
|
727
|
+
element = createElement(
|
|
728
|
+
"html",
|
|
729
|
+
{ lang: "en" },
|
|
730
|
+
createElement(
|
|
731
|
+
"head",
|
|
732
|
+
null,
|
|
733
|
+
createElement("meta", { charSet: "utf-8" }),
|
|
734
|
+
createElement("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
|
|
735
|
+
createElement("title", null, "weifuwu")
|
|
736
|
+
),
|
|
737
|
+
createElement("body", null, element)
|
|
738
|
+
);
|
|
739
|
+
} else {
|
|
740
|
+
for (const L of layoutComponents.toReversed()) {
|
|
741
|
+
element = createElement(L, { children: element });
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
let bundle = null;
|
|
745
|
+
if (!bundleCache.has(bundleKey)) {
|
|
746
|
+
const buf = await buildClientBundle(path, layoutPaths);
|
|
747
|
+
if (buf) bundleCache.set(bundleKey, buf);
|
|
791
748
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
pathMws.push(...node.pathMws);
|
|
795
|
-
const handler = node.handlers.get(method) || node.handlers.get("*");
|
|
796
|
-
if (handler) {
|
|
797
|
-
if (node.wildcard) params["*"] = segments.slice(segments.length).join("/");
|
|
798
|
-
return {
|
|
799
|
-
handler,
|
|
800
|
-
middlewares: node.middlewares.get(method) || node.middlewares.get("*") || [],
|
|
801
|
-
pathMws,
|
|
802
|
-
params
|
|
803
|
-
};
|
|
804
|
-
}
|
|
805
|
-
if (wildcardHandler) {
|
|
806
|
-
params["*"] = segments.slice(wildcardIdx).join("/");
|
|
807
|
-
return { handler: wildcardHandler, middlewares: wildcardMws, pathMws, params };
|
|
808
|
-
}
|
|
809
|
-
return null;
|
|
810
|
-
}
|
|
811
|
-
matchWsTrie(root, segments) {
|
|
812
|
-
let node = root;
|
|
813
|
-
const params = {};
|
|
814
|
-
for (const segment of segments) {
|
|
815
|
-
const next = matchWsNode(node, segment, params);
|
|
816
|
-
if (!next) return null;
|
|
817
|
-
node = next;
|
|
818
|
-
}
|
|
819
|
-
return node.handler ? { handler: node.handler, middlewares: node.middlewares, params } : null;
|
|
820
|
-
}
|
|
821
|
-
async handle(req, ctx, segments, query) {
|
|
822
|
-
const match = this.matchTrie(req.method, segments);
|
|
823
|
-
if (match?.handler) {
|
|
824
|
-
const { handler, middlewares: routeMws, pathMws, params } = match;
|
|
825
|
-
const allMws = this.globalMws.length + pathMws.length + routeMws.length === 0 ? [] : [...this.globalMws, ...pathMws, ...routeMws];
|
|
826
|
-
const ctxWithMatch = { ...ctx, params: { ...ctx.params, ...params } };
|
|
827
|
-
try {
|
|
828
|
-
return await this.runChain(allMws, handler, req, ctxWithMatch);
|
|
829
|
-
} catch (e) {
|
|
830
|
-
const err = e instanceof Error ? e : new Error(String(e));
|
|
831
|
-
return this.errorHandler ? this.errorHandler(err, req, ctxWithMatch) : new Response("Internal Server Error", { status: 500 });
|
|
749
|
+
if (bundleCache.has(bundleKey)) {
|
|
750
|
+
bundle = { url: bundleKey };
|
|
832
751
|
}
|
|
752
|
+
const { renderToReadableStream } = await import("react-dom/server");
|
|
753
|
+
const stream = await renderToReadableStream(element);
|
|
754
|
+
return streamResponse(stream, {
|
|
755
|
+
ctx,
|
|
756
|
+
base,
|
|
757
|
+
isDev,
|
|
758
|
+
bundle,
|
|
759
|
+
loaderData
|
|
760
|
+
});
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
return r;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// ssr/layout.ts
|
|
767
|
+
function layout(path) {
|
|
768
|
+
return async (req, ctx, next) => {
|
|
769
|
+
const mod = await compileTsx(path);
|
|
770
|
+
const Component = mod.default;
|
|
771
|
+
if (!Component) throw new Error(`Layout ${path} has no default export`);
|
|
772
|
+
ctx.layoutStack = [...ctx.layoutStack || [], { path, component: Component }];
|
|
773
|
+
return next(req, ctx);
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// ssr/tailwind.ts
|
|
778
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
779
|
+
import { relative, resolve as resolve3 } from "node:path";
|
|
780
|
+
var isDev2 = process.env.NODE_ENV !== "production";
|
|
781
|
+
function tailwind(cssPath, scanDir) {
|
|
782
|
+
let compiledCss = "";
|
|
783
|
+
let twWatcher = null;
|
|
784
|
+
return async (req, ctx, next) => {
|
|
785
|
+
const url = new URL(req.url);
|
|
786
|
+
if (url.pathname === "/__wfw/style.css") {
|
|
787
|
+
if (!compiledCss) compiledCss = await compile(cssPath, scanDir);
|
|
788
|
+
return new Response(compiledCss || "", {
|
|
789
|
+
headers: { "content-type": "text/css; charset=utf-8" }
|
|
790
|
+
});
|
|
833
791
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
}
|
|
839
|
-
const err = e instanceof Error ? e : new Error(String(e));
|
|
840
|
-
return this.errorHandler ? this.errorHandler(err, req, ctx) : new Response("Internal Server Error", { status: 500 });
|
|
841
|
-
}
|
|
792
|
+
ctx.compiledTailwindCss = compiledCss;
|
|
793
|
+
if (isDev2 && !twWatcher) {
|
|
794
|
+
twWatcher = watchFile(cssPath, () => {
|
|
795
|
+
compiledCss = "";
|
|
796
|
+
});
|
|
842
797
|
}
|
|
843
|
-
return
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
return await finalHandler(req2, ctx2);
|
|
853
|
-
};
|
|
854
|
-
return dispatch(req, ctx);
|
|
855
|
-
}
|
|
856
|
-
};
|
|
857
|
-
function upgradeSocket(wss, req, socket, head, handler, ctx) {
|
|
858
|
-
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
859
|
-
if (handler.open) {
|
|
860
|
-
handler.open(ws, ctx);
|
|
798
|
+
return next(req, ctx);
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
async function compile(cssPath, scanDir) {
|
|
802
|
+
try {
|
|
803
|
+
const inputFile = resolve3(cssPath);
|
|
804
|
+
if (!existsSync2(inputFile)) {
|
|
805
|
+
mkdirSync2(dirname3(inputFile), { recursive: true });
|
|
806
|
+
writeFileSync(inputFile, '@import "tailwindcss"\n', "utf-8");
|
|
861
807
|
}
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
})
|
|
808
|
+
const { default: tailwindPlugin } = await import("@tailwindcss/postcss");
|
|
809
|
+
const { default: postcss } = await import("postcss");
|
|
810
|
+
let src = readFileSync2(inputFile, "utf-8");
|
|
811
|
+
const scanSource = scanDir ? relative(dirname3(inputFile), scanDir) || "." : ".";
|
|
812
|
+
const sourcePath = scanSource === "." ? "./" : `./${scanSource}/`;
|
|
813
|
+
src = `@source "${sourcePath}";
|
|
814
|
+
${src}`;
|
|
815
|
+
const result = await postcss([tailwindPlugin()]).process(src, { from: inputFile });
|
|
816
|
+
return result.css;
|
|
817
|
+
} catch (err) {
|
|
818
|
+
console.warn("Tailwind CSS processing failed:", err.message);
|
|
819
|
+
return "";
|
|
820
|
+
}
|
|
872
821
|
}
|
|
873
|
-
function
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
const headerStr = headerLines.join("\r\n");
|
|
882
|
-
response.arrayBuffer().then((buf) => {
|
|
883
|
-
const body = Buffer.from(buf);
|
|
884
|
-
socket.write(headerStr + "\r\n" + body.toString());
|
|
885
|
-
socket.end();
|
|
886
|
-
}).catch(() => {
|
|
887
|
-
socket.write(headerStr + "\r\n");
|
|
888
|
-
socket.end();
|
|
822
|
+
function dirname3(p) {
|
|
823
|
+
return p.substring(0, p.lastIndexOf("/")) || "/";
|
|
824
|
+
}
|
|
825
|
+
function watchFile(path, onChange) {
|
|
826
|
+
let watcher = null;
|
|
827
|
+
import("chokidar").then((chokidar2) => {
|
|
828
|
+
watcher = chokidar2.default.watch(resolve3(path), { persistent: false });
|
|
829
|
+
watcher.on("change", onChange);
|
|
889
830
|
});
|
|
831
|
+
return watcher;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// ssr/not-found.ts
|
|
835
|
+
function notFound(path) {
|
|
836
|
+
return async (req, ctx) => {
|
|
837
|
+
if (!path) return new Response("Not Found", { status: 404 });
|
|
838
|
+
const mod = await compileTsx(path);
|
|
839
|
+
const Component = mod?.default;
|
|
840
|
+
const body = Component ? "404 - Not Found" : "404 - Not Found";
|
|
841
|
+
return new Response(body, {
|
|
842
|
+
status: 404,
|
|
843
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
844
|
+
});
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// ssr/error-boundary.ts
|
|
849
|
+
import { createElement as createElement2 } from "react";
|
|
850
|
+
import { TextEncoder as TextEncoder2 } from "node:util";
|
|
851
|
+
function errorBoundary(errorPath) {
|
|
852
|
+
return async (req, ctx, next) => {
|
|
853
|
+
try {
|
|
854
|
+
return await next(req, ctx);
|
|
855
|
+
} catch (err) {
|
|
856
|
+
const mod = await compileTsx(errorPath);
|
|
857
|
+
const ErrorComponent = mod.default;
|
|
858
|
+
if (!ErrorComponent) throw err;
|
|
859
|
+
const layouts = (ctx.layoutStack || []).map((l) => l.component);
|
|
860
|
+
const stream = await import("react-dom/server").then((m) => m.renderToReadableStream(
|
|
861
|
+
createElement2(ErrorComponent, {
|
|
862
|
+
error: err instanceof Error ? err : new Error(String(err)),
|
|
863
|
+
reset: () => {
|
|
864
|
+
}
|
|
865
|
+
})
|
|
866
|
+
));
|
|
867
|
+
const reader = stream.getReader();
|
|
868
|
+
const chunks = [];
|
|
869
|
+
while (true) {
|
|
870
|
+
const { done, value } = await reader.read();
|
|
871
|
+
if (done) break;
|
|
872
|
+
chunks.push(value);
|
|
873
|
+
}
|
|
874
|
+
const encoder = new TextEncoder2();
|
|
875
|
+
const body = chunks.reduce((acc, c) => {
|
|
876
|
+
const merged = new Uint8Array(acc.length + c.length);
|
|
877
|
+
merged.set(acc);
|
|
878
|
+
merged.set(c, acc.length);
|
|
879
|
+
return merged;
|
|
880
|
+
}, new Uint8Array(0));
|
|
881
|
+
return new Response(body, {
|
|
882
|
+
status: 500,
|
|
883
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
};
|
|
890
887
|
}
|
|
891
888
|
|
|
892
889
|
// ssr/live.ts
|
|
890
|
+
import chokidar from "chokidar";
|
|
893
891
|
var clients = /* @__PURE__ */ new Set();
|
|
894
892
|
function broadcastReload() {
|
|
895
893
|
for (const ws of clients) {
|
|
@@ -931,6 +929,5 @@ export {
|
|
|
931
929
|
liveReload,
|
|
932
930
|
notFound,
|
|
933
931
|
ssr,
|
|
934
|
-
ssrBundleHandler,
|
|
935
932
|
tailwind
|
|
936
933
|
};
|