weifuwu 0.18.0 → 0.18.2

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/index.js CHANGED
@@ -270,6 +270,8 @@ var Router = class _Router {
270
270
  }
271
271
  node.pathMws.push(arg2);
272
272
  }
273
+ } else if (arg1 instanceof _Router) {
274
+ this._mountRouter("/", arg1);
273
275
  } else if (typeof arg1 === "function") {
274
276
  this.globalMws.push(arg1);
275
277
  }
@@ -369,13 +371,13 @@ var Router = class _Router {
369
371
  const mw = match.middlewares[index++];
370
372
  return mw(innerReq, ctx2, dispatch);
371
373
  }
372
- return await new Promise((resolve11) => {
374
+ return await new Promise((resolve12) => {
373
375
  try {
374
376
  upgradeSocket(router.wss, req, socket, head, match.handler, ctx2);
375
- resolve11(new Response(null, { status: 101 }));
377
+ resolve12(new Response(null, { status: 101 }));
376
378
  } catch {
377
379
  socket.destroy();
378
- resolve11(new Response("WebSocket upgrade failed", { status: 500 }));
380
+ resolve12(new Response("WebSocket upgrade failed", { status: 500 }));
379
381
  }
380
382
  });
381
383
  };
@@ -561,19 +563,6 @@ function sendHttpResponseOnSocket(socket, response) {
561
563
  });
562
564
  }
563
565
 
564
- // tsx-instance.ts
565
- import { createElement } from "react";
566
- import { renderToReadableStream } from "react-dom/server";
567
- import * as esbuild from "esbuild";
568
- import { readdirSync, statSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
569
- import { join, relative, resolve as resolve2, sep, dirname, basename } from "node:path";
570
- import { pathToFileURL } from "node:url";
571
- import { createHash } from "node:crypto";
572
- import vm from "node:vm";
573
- import { createRequire } from "node:module";
574
- import { AsyncLocalStorage } from "node:async_hooks";
575
- import chokidar from "chokidar";
576
-
577
566
  // tsx-context.ts
578
567
  import { createContext } from "react";
579
568
  var DEFAULT_CTX = { params: {}, query: {}, parsed: {}, prefs: {}, loaderData: {}, env: {}, user: {} };
@@ -604,845 +593,6 @@ function setCtx(value) {
604
593
  }
605
594
  var TsxContext = createContext(DEFAULT_CTX);
606
595
 
607
- // tsx-instance.ts
608
- var als = new AsyncLocalStorage();
609
- __registerAls(() => als.getStore());
610
- var liveReloadClients = /* @__PURE__ */ new Set();
611
- function broadcastReload() {
612
- for (const ws of liveReloadClients) {
613
- try {
614
- ws.send("reload");
615
- } catch {
616
- liveReloadClients.delete(ws);
617
- }
618
- }
619
- }
620
- var isDev = process.env.NODE_ENV !== "production";
621
- var _tailwindPlugin = null;
622
- var _postcss = null;
623
- var _cjsRequire = createRequire(import.meta.url);
624
- function loadSSRModule(code) {
625
- const ctx = vm.createContext(Object.create(globalThis));
626
- const mod = { exports: {} };
627
- ctx.require = (name) => _cjsRequire(name);
628
- ctx.module = mod;
629
- ctx.exports = mod.exports;
630
- new vm.Script(code).runInContext(ctx);
631
- return mod.exports;
632
- }
633
- function id(s) {
634
- return createHash("md5").update(s).digest("hex").slice(0, 8);
635
- }
636
- var _alias = null;
637
- function resolveAliases() {
638
- if (_alias) return _alias;
639
- const configFiles = ["tsconfig.json", "jsconfig.json"];
640
- for (const file of configFiles) {
641
- const p = resolve2(file);
642
- if (existsSync2(p)) {
643
- try {
644
- const config = JSON.parse(readFileSync2(p, "utf-8"));
645
- const paths = config.compilerOptions?.paths;
646
- if (paths) {
647
- const alias = {};
648
- for (const [key, values] of Object.entries(paths)) {
649
- const cleanKey = key.replace("/*", "");
650
- const val = values[0]?.replace("/*", "");
651
- if (val) alias[cleanKey] = resolve2(dirname(p), val);
652
- }
653
- _alias = alias;
654
- return alias;
655
- }
656
- } catch {
657
- }
658
- }
659
- }
660
- _alias = {};
661
- return {};
662
- }
663
- function scanPages(dir) {
664
- const pages = [];
665
- function walk(current) {
666
- let entries;
667
- try {
668
- entries = readdirSync(current);
669
- } catch {
670
- return;
671
- }
672
- const dirs = [];
673
- for (const name of entries) {
674
- const full = join(current, name);
675
- const st = statSync(full);
676
- if (st.isDirectory()) {
677
- if (!name.startsWith(".")) dirs.push(full);
678
- }
679
- }
680
- const pagePath = join(current, "page.tsx");
681
- const tsPagePath = join(current, "page.ts");
682
- let entryPath = "";
683
- if (existsSync2(pagePath)) {
684
- entryPath = pagePath;
685
- } else if (existsSync2(tsPagePath)) {
686
- entryPath = tsPagePath;
687
- }
688
- if (entryPath) {
689
- let relPath = relative(dir, entryPath).replace(sep, "/");
690
- relPath = relPath.replace(/\/page\.tsx?$/, "");
691
- relPath = relPath.replace(/^page\.tsx?$/, "");
692
- const route = filePathToRoute(relPath);
693
- const layouts = resolveLayouts(current, dir);
694
- const loadPath = existsSync2(join(current, "load.ts")) ? join(current, "load.ts") : void 0;
695
- const rPath = existsSync2(join(current, "route.ts")) ? join(current, "route.ts") : void 0;
696
- pages.push({
697
- route,
698
- entryPath,
699
- loadPath,
700
- layouts,
701
- routePath: rPath
702
- });
703
- } else {
704
- const rPath = join(current, "route.ts");
705
- if (existsSync2(rPath)) {
706
- let relPath = relative(dir, rPath).replace(sep, "/");
707
- relPath = relPath.replace(/\/route\.tsx?$/, "");
708
- const route = filePathToRoute(relPath);
709
- pages.push({
710
- route,
711
- entryPath: "",
712
- layouts: [],
713
- routePath: rPath,
714
- routeOnly: true
715
- });
716
- }
717
- }
718
- for (const d of dirs) walk(d);
719
- }
720
- walk(dir);
721
- return pages;
722
- }
723
- function filePathToRoute(relPath) {
724
- let route = relPath.replace(/\\/g, "/");
725
- route = route.replace(/\[\.\.\.(\w+)\]/g, "*");
726
- route = route.replace(/\[(\w+)\]/g, ":$1");
727
- return route.startsWith("/") ? route : "/" + route;
728
- }
729
- function resolveLayouts(dir, pagesDir) {
730
- const layouts = [];
731
- let current = dir;
732
- while (current.startsWith(pagesDir)) {
733
- const p = join(current, "layout.tsx");
734
- if (existsSync2(p)) {
735
- layouts.push(p);
736
- }
737
- const parent = dirname(current);
738
- if (parent === current) break;
739
- current = parent;
740
- }
741
- return layouts.reverse();
742
- }
743
- async function compileAll(files, outDir, platform, alias) {
744
- const entryPoints = {};
745
- for (const f of files) {
746
- entryPoints[id(f)] = f;
747
- }
748
- const isBrowser = platform === "browser";
749
- await esbuild.build({
750
- entryPoints,
751
- outdir: outDir,
752
- format: "esm",
753
- platform: "node",
754
- jsx: "automatic",
755
- jsxImportSource: "react",
756
- bundle: true,
757
- external: isBrowser ? void 0 : [
758
- "react",
759
- "react-dom",
760
- "esbuild",
761
- "graphql",
762
- "ws",
763
- "zod",
764
- "@graphql-tools/schema",
765
- "ai"
766
- ],
767
- write: true,
768
- alias,
769
- allowOverwrite: true
770
- });
771
- }
772
- function compiledUrl(filePath, outDir) {
773
- const hash = id(join(outDir, id(filePath)));
774
- const p = join(outDir, id(filePath) + ".js");
775
- return pathToFileURL(p).href;
776
- }
777
- var TsxInstance = class {
778
- uiDir;
779
- pagesDir;
780
- outDir;
781
- router;
782
- pageModules = /* @__PURE__ */ new Map();
783
- layoutModules = /* @__PURE__ */ new Map();
784
- loadModules = /* @__PURE__ */ new Map();
785
- routeModules = /* @__PURE__ */ new Map();
786
- allFiles = [];
787
- nodeEntries = {};
788
- compiledTailwindCss = "";
789
- // client bundle cache (per-instance)
790
- clientBundleCache = /* @__PURE__ */ new Map();
791
- clientBuildParams = /* @__PURE__ */ new Map();
792
- clientRouteLog = /* @__PURE__ */ new Set();
793
- // file watchers (dev mode, stored for cleanup)
794
- watcher = null;
795
- twWatcher = null;
796
- debounceTimer = null;
797
- constructor(options) {
798
- this.uiDir = resolve2(options.dir);
799
- this.pagesDir = existsSync2(join(this.uiDir, "pages")) ? join(this.uiDir, "pages") : this.uiDir;
800
- this.outDir = join(this.uiDir, ".weifuwu", "ssr");
801
- this.router = new Router();
802
- }
803
- async build() {
804
- const pages = scanPages(this.pagesDir);
805
- if (pages.length === 0) return attachStop(this.router, this);
806
- const allFiles = /* @__PURE__ */ new Set();
807
- for (const p of pages) {
808
- if (p.entryPath) allFiles.add(p.entryPath);
809
- if (p.loadPath) allFiles.add(p.loadPath);
810
- for (const lp of p.layouts) allFiles.add(lp);
811
- if (p.routePath) allFiles.add(p.routePath);
812
- }
813
- const nfPath = join(this.pagesDir, "not-found.tsx");
814
- const hasNotFound = existsSync2(nfPath);
815
- if (hasNotFound) {
816
- allFiles.add(nfPath);
817
- const rootLayouts = resolveLayouts(this.pagesDir, this.pagesDir);
818
- for (const lp of rootLayouts) allFiles.add(lp);
819
- }
820
- mkdirSync(this.outDir, { recursive: true });
821
- this.allFiles = [...allFiles];
822
- await compileAll(this.allFiles, this.outDir, "node", resolveAliases());
823
- const methods = ["POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
824
- for (const p of pages) {
825
- const nodeKey = p.entryPath || p.routePath || "";
826
- this.nodeEntries[nodeKey] = { route: p.route, entryPath: p.entryPath || "", layouts: p.layouts, loadPath: p.loadPath, routePath: p.routePath };
827
- if (p.routeOnly && p.routePath) {
828
- const rUrl = compiledUrl(p.routePath, this.outDir);
829
- const modR = await import(rUrl);
830
- const handlers = /* @__PURE__ */ new Map();
831
- for (const m of ["GET", ...methods]) {
832
- if (modR[m]) handlers.set(m, modR[m]);
833
- }
834
- this.routeModules.set(p.routePath, handlers);
835
- this.router.route(
836
- "GET",
837
- p.route,
838
- (req, ctx) => this.routeModules.get(p.routePath)?.get("GET")?.(req, ctx) ?? new Response("", { status: 501 })
839
- );
840
- for (const m of methods) {
841
- this.router.route(
842
- m,
843
- p.route,
844
- (req, ctx) => this.routeModules.get(p.routePath)?.get(m)?.(req, ctx) ?? new Response("", { status: 501 })
845
- );
846
- }
847
- continue;
848
- }
849
- const pageUrl = compiledUrl(p.entryPath, this.outDir);
850
- this.pageModules.set(p.entryPath, await import(pageUrl));
851
- if (p.loadPath) {
852
- const loadUrl = compiledUrl(p.loadPath, this.outDir);
853
- this.loadModules.set(p.loadPath, await import(loadUrl));
854
- }
855
- for (const lp of p.layouts) {
856
- const lUrl = compiledUrl(lp, this.outDir);
857
- this.layoutModules.set(lp, await import(lUrl));
858
- }
859
- if (p.routePath) {
860
- const rUrl = compiledUrl(p.routePath, this.outDir);
861
- const modR = await import(rUrl);
862
- const handlers = /* @__PURE__ */ new Map();
863
- for (const m of methods) {
864
- if (modR[m]) handlers.set(m, modR[m]);
865
- }
866
- this.routeModules.set(p.routePath, handlers);
867
- }
868
- const handler = this.makeSsrHandler(p.entryPath, p.layouts, p.loadPath);
869
- this.router.get(p.route, handler);
870
- if (p.routePath) {
871
- for (const m of methods) {
872
- this.router.route(
873
- m,
874
- p.route,
875
- (req, ctx) => this.routeModules.get(p.routePath)?.get(m)?.(req, ctx) ?? new Response("", { status: 501 })
876
- );
877
- }
878
- }
879
- }
880
- if (hasNotFound) {
881
- const nfUrl = compiledUrl(nfPath, this.outDir);
882
- this.pageModules.set(nfPath, await import(nfUrl));
883
- const rootLayouts = resolveLayouts(this.pagesDir, this.pagesDir);
884
- for (const lp of rootLayouts) {
885
- if (!this.layoutModules.has(lp)) {
886
- const lUrl = compiledUrl(lp, this.outDir);
887
- this.layoutModules.set(lp, await import(lUrl));
888
- }
889
- }
890
- const handler = async (req, ctx) => {
891
- const base = (ctx.mountPath || "").replace(/\/$/, "");
892
- const nfMod = this.pageModules.get(nfPath);
893
- if (!nfMod) return new Response("Not Found", { status: 404 });
894
- const NfComponent = nfMod.default;
895
- const ctxValue = {
896
- params: ctx.params,
897
- query: ctx.query,
898
- user: ctx.user ?? {},
899
- parsed: ctx.parsed ?? {},
900
- prefs: ctx.prefs ?? {},
901
- loaderData: {},
902
- env: ctx.env ?? {}
903
- };
904
- return als.run(ctxValue, async () => {
905
- setCtx(ctxValue);
906
- let element = createElement(
907
- TsxContext.Provider,
908
- { value: ctxValue },
909
- createElement(NfComponent, { params: ctx.params, query: ctx.query })
910
- );
911
- for (let i = rootLayouts.length - 1; i >= 0; i--) {
912
- const LMod = this.layoutModules.get(rootLayouts[i]);
913
- if (!LMod) continue;
914
- element = createElement(LMod.default, { children: element });
915
- }
916
- const stream = await renderToReadableStream(element);
917
- return streamResponse(stream, {
918
- ctx,
919
- base,
920
- compiledTailwindCss: this.compiledTailwindCss,
921
- isDev,
922
- status: 404
923
- });
924
- });
925
- };
926
- this.router.all("/*", handler);
927
- }
928
- for (const p of pages) {
929
- if (p.entryPath) {
930
- const rootLayouts = resolveLayouts(this.pagesDir, this.pagesDir);
931
- this.registerClientBundleRoute(p.entryPath, p.layouts.length > 0 ? p.layouts : rootLayouts, this.pagesDir);
932
- }
933
- }
934
- await this.setupTailwind();
935
- if (isDev) {
936
- this.router.ws("/__weifuwu/livereload", {
937
- open: (ws) => {
938
- liveReloadClients.add(ws);
939
- ws.on("close", () => liveReloadClients.delete(ws));
940
- ws.on("error", () => liveReloadClients.delete(ws));
941
- }
942
- });
943
- this.startFileWatcher();
944
- }
945
- return attachStop(this.router, this);
946
- }
947
- /**
948
- * Clean up file watchers and pending timers. Call when shutting down
949
- * to prevent resource leaks.
950
- */
951
- stop() {
952
- this.watcher?.close();
953
- this.twWatcher?.close();
954
- if (this.debounceTimer) clearTimeout(this.debounceTimer);
955
- this.debounceTimer = null;
956
- }
957
- // ── Tailwind CSS ──────────────────────────────────────────────────────────
958
- async compileTailwind() {
959
- if (this.compiledTailwindCss) return this.compiledTailwindCss;
960
- try {
961
- _tailwindPlugin ??= (await import("@tailwindcss/postcss")).default;
962
- _postcss ??= (await import("postcss")).default;
963
- } catch {
964
- return "";
965
- }
966
- const inputFile = resolve2(this.uiDir, "app.css");
967
- if (!existsSync2(inputFile)) {
968
- mkdirSync(this.uiDir, { recursive: true });
969
- writeFileSync(inputFile, '@import "tailwindcss"\n', "utf-8");
970
- console.log("\u2139 weifuwu/tsx: created " + relative(process.cwd(), inputFile));
971
- }
972
- try {
973
- let src = readFileSync2(inputFile, "utf-8");
974
- const sourceRel = relative(this.uiDir, this.pagesDir) || ".";
975
- const sourcePath = sourceRel === "." ? "./" : `./${sourceRel}/`;
976
- src = `@source "${sourcePath}";
977
- ${src}`;
978
- const result = await _postcss([_tailwindPlugin()]).process(src, { from: inputFile });
979
- this.compiledTailwindCss = result.css;
980
- } catch (err) {
981
- console.warn("Tailwind CSS processing failed:", err.message);
982
- }
983
- return this.compiledTailwindCss;
984
- }
985
- async setupTailwind() {
986
- await this.compileTailwind();
987
- this.router.get("/__wfw/style.css", () => new Response(this.compiledTailwindCss || "", {
988
- headers: { "content-type": "text/css; charset=utf-8" }
989
- }));
990
- if (isDev) {
991
- const inputFile = resolve2(this.uiDir, "app.css");
992
- this.twWatcher = chokidar.watch(inputFile, { persistent: false });
993
- this.twWatcher.on("change", async () => {
994
- this.compiledTailwindCss = "";
995
- await this.compileTailwind();
996
- broadcastReload();
997
- });
998
- }
999
- }
1000
- // ── client bundle ─────────────────────────────────────────────────────────
1001
- async buildClientBundle(entryPath, layoutPaths, pagesDir) {
1002
- try {
1003
- const layoutImports = layoutPaths.map((p) => `import${JSON.stringify(p)};`).join("");
1004
- 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()})}})()`;
1005
- const code = [
1006
- layoutImports,
1007
- `import{hydrateRoot}from'react-dom/client';`,
1008
- `import{createElement,useState,useEffect}from'react';`,
1009
- `import{TsxContext}from'weifuwu/react';`,
1010
- `import P from${JSON.stringify(entryPath)};`,
1011
- `var setCtx=${_sc};`,
1012
- `const c=document.getElementById('__weifuwu_root');`,
1013
- `if(window.__WEIFUWU_PROPS)setCtx({loaderData:window.__WEIFUWU_PROPS});`,
1014
- `if(!window.__WFW_ROOT){`,
1015
- `function App(){`,
1016
- `const[p,setP]=useState({C:P});`,
1017
- `useEffect(()=>{window.__WFW_SET_PAGE=(C)=>{setCtx({loaderData:window.__WEIFUWU_PROPS});setP({C})}},[]);`,
1018
- `const ctx=window.__WEIFUWU_CTX||{};`,
1019
- `return createElement(TsxContext.Provider,{value:ctx},`,
1020
- `createElement(p.C,null))`,
1021
- `}`,
1022
- `window.__WFW_ROOT=hydrateRoot(c,createElement(App));`,
1023
- `}else{`,
1024
- `window.__WFW_SET_PAGE?.(P);`,
1025
- `}`
1026
- ].join("");
1027
- const publicEnv = {};
1028
- for (const key of Object.keys(process.env)) {
1029
- if (key.startsWith("WEIFUWU_PUBLIC_")) {
1030
- publicEnv[`process.env.${key}`] = JSON.stringify(process.env[key]);
1031
- }
1032
- }
1033
- const result = await esbuild.build({
1034
- stdin: { contents: code, loader: "tsx", resolveDir: pagesDir },
1035
- bundle: true,
1036
- format: "esm",
1037
- jsx: "automatic",
1038
- jsxImportSource: "react",
1039
- alias: resolveAliases(),
1040
- banner: { js: "self.process={env:{}};" },
1041
- define: Object.keys(publicEnv).length > 0 ? publicEnv : void 0,
1042
- loader: { ".node": "empty" },
1043
- write: false,
1044
- minify: true
1045
- });
1046
- return result.outputFiles[0].contents;
1047
- } catch (err) {
1048
- console.error("hydration bundle failed:", err);
1049
- return null;
1050
- }
1051
- }
1052
- async getOrBuildClientBundle(entryPath, layoutPaths, pagesDir) {
1053
- const key = id(entryPath);
1054
- const url = `/__wfw/client/${key}.js`;
1055
- this.clientBuildParams.set(key, { entryPath, layoutPaths, pagesDir });
1056
- if (!this.clientBundleCache.has(key)) {
1057
- const buf = await this.buildClientBundle(entryPath, layoutPaths, pagesDir);
1058
- if (!buf) return null;
1059
- this.clientBundleCache.set(key, buf);
1060
- }
1061
- return { url };
1062
- }
1063
- registerClientBundleRoute(entryPath, layoutPaths, pagesDir) {
1064
- const key = id(entryPath);
1065
- const url = `/__wfw/client/${key}.js`;
1066
- this.clientBuildParams.set(key, { entryPath, layoutPaths, pagesDir });
1067
- if (!this.clientRouteLog.has(url)) {
1068
- this.router.get(url, async () => {
1069
- let buf = this.clientBundleCache.get(key);
1070
- if (!buf) {
1071
- const params = this.clientBuildParams.get(key);
1072
- if (params) {
1073
- const rebuilt = await this.buildClientBundle(params.entryPath, params.layoutPaths, params.pagesDir);
1074
- if (rebuilt) {
1075
- this.clientBundleCache.set(key, rebuilt);
1076
- buf = rebuilt;
1077
- }
1078
- }
1079
- }
1080
- return buf ? new Response(buf, {
1081
- headers: { "content-type": "application/javascript; charset=utf-8" }
1082
- }) : new Response("", { status: 500 });
1083
- });
1084
- this.clientRouteLog.add(url);
1085
- }
1086
- }
1087
- // ── SSR handler ───────────────────────────────────────────────────────────
1088
- makeSsrHandler(entryPath, layoutPaths, loadPath) {
1089
- return async (req, ctx) => {
1090
- const base = (ctx.mountPath || "").replace(/\/$/, "");
1091
- const pageMod = this.pageModules.get(entryPath);
1092
- if (!pageMod) return new Response("", { status: 500 });
1093
- const Component = pageMod.default;
1094
- const loadMod = loadPath ? this.loadModules.get(loadPath) : void 0;
1095
- const loadFn = loadMod?.default;
1096
- const loadProps = loadFn ? await loadFn({ params: ctx.params, query: ctx.query }) : {};
1097
- const ctxValue = {
1098
- params: ctx.params,
1099
- query: ctx.query,
1100
- user: ctx.user ?? {},
1101
- parsed: ctx.parsed ?? {},
1102
- prefs: ctx.prefs ?? {},
1103
- loaderData: loadProps,
1104
- env: ctx.env ?? {}
1105
- };
1106
- return als.run(ctxValue, async () => {
1107
- setCtx(ctxValue);
1108
- let element = createElement(
1109
- TsxContext.Provider,
1110
- { value: ctxValue },
1111
- createElement(
1112
- "div",
1113
- { id: "__weifuwu_root" },
1114
- createElement(Component, null)
1115
- )
1116
- );
1117
- if (layoutPaths.length === 0) {
1118
- element = createElement(
1119
- "html",
1120
- { lang: "en" },
1121
- createElement(
1122
- "head",
1123
- null,
1124
- createElement("meta", { charSet: "utf-8" }),
1125
- createElement("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
1126
- createElement("title", null, "weifuwu")
1127
- ),
1128
- createElement("body", null, element)
1129
- );
1130
- } else {
1131
- for (let i = layoutPaths.length - 1; i >= 0; i--) {
1132
- const lp = layoutPaths[i];
1133
- const LMod = this.layoutModules.get(lp);
1134
- if (!LMod) continue;
1135
- const Layout = LMod.default;
1136
- const isRoot = i === 0;
1137
- element = createElement(
1138
- Layout,
1139
- { children: element }
1140
- );
1141
- }
1142
- }
1143
- const bundle = await this.getOrBuildClientBundle(entryPath, layoutPaths, this.pagesDir);
1144
- const stream = await renderToReadableStream(element);
1145
- return streamResponse(stream, {
1146
- ctx,
1147
- base,
1148
- compiledTailwindCss: this.compiledTailwindCss,
1149
- isDev,
1150
- bundle,
1151
- loaderData: loadProps
1152
- });
1153
- });
1154
- };
1155
- }
1156
- // ── dev file watcher ──────────────────────────────────────────────────────
1157
- startFileWatcher() {
1158
- const pending = /* @__PURE__ */ new Set();
1159
- this.watcher = chokidar.watch(this.uiDir, {
1160
- ignored: /(^|[/\\])\.(?!\.)|node_modules|[/\\]\.weifuwu[/\\]|[/\\]dist[/\\]/,
1161
- persistent: false,
1162
- ignoreInitial: true
1163
- });
1164
- this.watcher.on("all", async (event, filePath) => {
1165
- if (event !== "change" && event !== "add") return;
1166
- if (!/\.tsx?$/.test(filePath)) return;
1167
- pending.add(filePath);
1168
- if (this.debounceTimer) clearTimeout(this.debounceTimer);
1169
- this.debounceTimer = setTimeout(async () => {
1170
- this.debounceTimer = null;
1171
- const files = [...pending];
1172
- pending.clear();
1173
- const exists = files.filter((f) => existsSync2(f));
1174
- const allKnown = exists.every(
1175
- (f) => this.pageModules.has(f) || this.layoutModules.has(f) || this.loadModules.has(f) || this.routeModules.has(f)
1176
- );
1177
- if (allKnown) {
1178
- for (const f of exists) await this.recompileAndSwap(f);
1179
- this.compiledTailwindCss = "";
1180
- await this.compileTailwind();
1181
- broadcastReload();
1182
- } else {
1183
- await this.recompileAll();
1184
- }
1185
- }, 50);
1186
- });
1187
- }
1188
- async recompileAndSwap(filePath) {
1189
- try {
1190
- const result = await esbuild.build({
1191
- entryPoints: { [id(filePath)]: filePath },
1192
- outdir: this.outDir,
1193
- format: "cjs",
1194
- platform: "node",
1195
- jsx: "automatic",
1196
- jsxImportSource: "react",
1197
- bundle: true,
1198
- external: ["react", "react-dom", "esbuild", "graphql", "ws", "zod", "@graphql-tools/schema", "ai"],
1199
- alias: resolveAliases(),
1200
- write: false
1201
- });
1202
- const code = new TextDecoder().decode(result.outputFiles[0].contents);
1203
- const mod = loadSSRModule(code);
1204
- const name = basename(filePath);
1205
- if (name === "layout.tsx") {
1206
- this.layoutModules.set(filePath, mod);
1207
- this.clientBundleCache.clear();
1208
- } else if (name === "route.ts") {
1209
- const handlers = /* @__PURE__ */ new Map();
1210
- for (const m of ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]) {
1211
- if (mod[m]) handlers.set(m, mod[m]);
1212
- }
1213
- this.routeModules.set(filePath, handlers);
1214
- } else if (name === "load.ts") {
1215
- this.loadModules.set(filePath, mod);
1216
- } else {
1217
- this.pageModules.set(filePath, mod);
1218
- this.clientBundleCache.delete(id(filePath));
1219
- }
1220
- } catch (err) {
1221
- console.error("recompile failed:", err.message);
1222
- }
1223
- }
1224
- async recompileAll() {
1225
- try {
1226
- const freshPages = scanPages(this.pagesDir);
1227
- const freshFiles = /* @__PURE__ */ new Set();
1228
- const nodeEntries = {};
1229
- for (const p of freshPages) {
1230
- const nodeKey = p.entryPath || p.routePath || "";
1231
- nodeEntries[nodeKey] = { route: p.route, entryPath: p.entryPath, layouts: p.layouts, loadPath: p.loadPath, routePath: p.routePath };
1232
- if (p.entryPath) freshFiles.add(p.entryPath);
1233
- if (p.loadPath) freshFiles.add(p.loadPath);
1234
- for (const lp of p.layouts) freshFiles.add(lp);
1235
- if (p.routePath) freshFiles.add(p.routePath);
1236
- }
1237
- const nfPath = join(this.pagesDir, "not-found.tsx");
1238
- if (existsSync2(nfPath)) {
1239
- freshFiles.add(nfPath);
1240
- const rootLayouts = resolveLayouts(this.pagesDir, this.pagesDir);
1241
- for (const lp of rootLayouts) freshFiles.add(lp);
1242
- }
1243
- this.allFiles = [...freshFiles];
1244
- const result = await esbuild.build({
1245
- entryPoints: Object.fromEntries(this.allFiles.map((f) => [id(f), f])),
1246
- outdir: this.outDir,
1247
- format: "cjs",
1248
- platform: "node",
1249
- jsx: "automatic",
1250
- jsxImportSource: "react",
1251
- bundle: true,
1252
- external: ["react", "react-dom", "esbuild", "graphql", "ws", "zod", "@graphql-tools/schema", "ai"],
1253
- alias: resolveAliases(),
1254
- write: false
1255
- });
1256
- for (const file of result.outputFiles) {
1257
- const code = new TextDecoder().decode(file.contents);
1258
- const mod = loadSSRModule(code);
1259
- const srcPath = this.allFiles.find((f) => file.path.endsWith(id(f) + ".js"));
1260
- if (!srcPath) continue;
1261
- const name = basename(srcPath);
1262
- if (name === "layout.tsx") {
1263
- this.layoutModules.set(srcPath, mod);
1264
- } else if (name === "route.ts") {
1265
- const handlers = /* @__PURE__ */ new Map();
1266
- for (const m of ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"]) {
1267
- if (mod[m]) handlers.set(m, mod[m]);
1268
- }
1269
- this.routeModules.set(srcPath, handlers);
1270
- } else if (name === "load.ts") {
1271
- this.loadModules.set(srcPath, mod);
1272
- } else if (name !== "not-found.tsx") {
1273
- this.pageModules.set(srcPath, mod);
1274
- }
1275
- }
1276
- const methods = ["POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
1277
- for (const [key, entry] of Object.entries(nodeEntries)) {
1278
- if (this.nodeEntries[key]) continue;
1279
- if (entry.routePath && !entry.entryPath) {
1280
- this.router.route(
1281
- "GET",
1282
- entry.route,
1283
- (req, ctx) => this.routeModules.get(entry.routePath)?.get("GET")?.(req, ctx) ?? new Response("", { status: 501 })
1284
- );
1285
- for (const m of methods) {
1286
- this.router.route(
1287
- m,
1288
- entry.route,
1289
- (req, ctx) => this.routeModules.get(entry.routePath)?.get(m)?.(req, ctx) ?? new Response("", { status: 501 })
1290
- );
1291
- }
1292
- }
1293
- if (entry.entryPath) {
1294
- const handler = this.makeSsrHandler(entry.entryPath, entry.layouts, entry.loadPath);
1295
- this.router.get(entry.route, handler);
1296
- if (entry.routePath) {
1297
- for (const m of methods) {
1298
- this.router.route(
1299
- m,
1300
- entry.route,
1301
- (req, ctx) => this.routeModules.get(entry.routePath)?.get(m)?.(req, ctx) ?? new Response("", { status: 501 })
1302
- );
1303
- }
1304
- }
1305
- }
1306
- console.log("\u2139 weifuwu/tsx: registered new route " + entry.route);
1307
- }
1308
- this.nodeEntries = nodeEntries;
1309
- this.clientBundleCache.clear();
1310
- this.compiledTailwindCss = "";
1311
- await this.compileTailwind();
1312
- broadcastReload();
1313
- } catch (err) {
1314
- console.error("recompile all failed:", err.message);
1315
- }
1316
- }
1317
- };
1318
- function attachStop(router, instance) {
1319
- ;
1320
- router.stop = () => instance.stop();
1321
- return router;
1322
- }
1323
- function streamResponse(reactStream, opts) {
1324
- const decoder = new TextDecoder();
1325
- const encoder2 = new TextEncoder();
1326
- const headPayload = buildHeadPayload(opts);
1327
- let buffer = "";
1328
- let headFlushed = false;
1329
- let extractedHead = "";
1330
- const output = new ReadableStream({
1331
- async start(controller) {
1332
- try {
1333
- const reader = reactStream.getReader();
1334
- async function push(chunk) {
1335
- buffer += decoder.decode(chunk, { stream: true });
1336
- if (!extractedHead) {
1337
- const m = buffer.match(/<template id="__wfw_head">([\s\S]*?)<\/template>/);
1338
- if (m) {
1339
- extractedHead = m[1];
1340
- buffer = buffer.replace(m[0], "");
1341
- }
1342
- }
1343
- if (!headFlushed) {
1344
- const idx = buffer.indexOf("</head>");
1345
- if (idx !== -1) {
1346
- const before = buffer.slice(0, idx);
1347
- let injection = "";
1348
- if (extractedHead) injection += "\n" + extractedHead;
1349
- injection += headPayload;
1350
- controller.enqueue(encoder2.encode(before + injection));
1351
- buffer = buffer.slice(idx);
1352
- headFlushed = true;
1353
- }
1354
- return;
1355
- }
1356
- controller.enqueue(encoder2.encode(buffer));
1357
- buffer = "";
1358
- }
1359
- while (true) {
1360
- const { done, value } = await reader.read();
1361
- if (done) break;
1362
- await push(value);
1363
- }
1364
- buffer = buffer.replace(/<template id="__wfw_head">[\s\S]*?<\/template>/g, "");
1365
- if (buffer) controller.enqueue(encoder2.encode(buffer));
1366
- const body = buildBodyScripts(opts);
1367
- if (body) controller.enqueue(encoder2.encode("\n" + body));
1368
- if (opts.isDev) {
1369
- controller.enqueue(encoder2.encode(
1370
- `
1371
- <script>(function(){var ws=new WebSocket((location.protocol==='https:'?'wss:':'ws:')+'//'+location.host+'${opts.base}/__weifuwu/livereload');ws.onmessage=function(e){if(e.data==='reload')location.reload()};ws.onclose=function(){setTimeout(function(){location.reload()},500)}})()</script>`
1372
- ));
1373
- }
1374
- } catch (err) {
1375
- const fallback = `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>500</title></head><body><h1>500 - Internal Server Error</h1></body></html>`;
1376
- controller.enqueue(encoder2.encode(fallback));
1377
- } finally {
1378
- controller.close();
1379
- }
1380
- }
1381
- });
1382
- return new Response(output, {
1383
- status: opts.status ?? 200,
1384
- headers: { "content-type": "text/html; charset=utf-8" }
1385
- });
1386
- }
1387
- var _publicEnv = null;
1388
- function getPublicEnv() {
1389
- if (_publicEnv) return _publicEnv;
1390
- _publicEnv = {};
1391
- for (const key of Object.keys(process.env)) {
1392
- if (key.startsWith("WEIFUWU_PUBLIC_")) {
1393
- _publicEnv[key] = process.env[key];
1394
- }
1395
- }
1396
- return _publicEnv;
1397
- }
1398
- function buildHeadPayload(opts) {
1399
- const { ctx, base, compiledTailwindCss } = opts;
1400
- let result = "";
1401
- if (ctx.prefs?.theme) {
1402
- result += `<script>!function(){var t=(document.cookie.match(/(?:^|;\\s*)theme=([^;]+)/)||[])[1]||'system';if(t==='system'){t=window.matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light'}document.documentElement.setAttribute('data-theme',t)}()</script>
1403
- `;
1404
- }
1405
- if (compiledTailwindCss) {
1406
- result += `<link rel="stylesheet" href="${base}/__wfw/style.css" />
1407
- `;
1408
- }
1409
- const localeData = ctx.parsed?.__localeData ?? globalThis.__LOCALE_DATA__;
1410
- if (localeData && Object.keys(localeData).length > 0) {
1411
- result += `<script>window.__LOCALE_DATA__=${JSON.stringify(localeData)}</script>
1412
- `;
1413
- }
1414
- const ctxData = {
1415
- params: ctx.params,
1416
- query: ctx.query,
1417
- user: ctx.user,
1418
- parsed: ctx.parsed,
1419
- prefs: ctx.prefs
1420
- };
1421
- const publicEnv = getPublicEnv();
1422
- if (Object.keys(publicEnv).length > 0) {
1423
- ctxData.env = publicEnv;
1424
- }
1425
- result += `<script>window.__WEIFUWU_CTX=${JSON.stringify(ctxData)}</script>
1426
- `;
1427
- return result;
1428
- }
1429
- function buildBodyScripts(opts) {
1430
- const parts = [];
1431
- if (opts.loaderData && Object.keys(opts.loaderData).length > 0) {
1432
- parts.push(`<script>window.__WEIFUWU_PROPS=${JSON.stringify(opts.loaderData)}</script>`);
1433
- }
1434
- if (opts.bundle) {
1435
- parts.push(`<script type="module" src="${opts.base}${opts.bundle.url}"></script>`);
1436
- }
1437
- return parts.join("\n");
1438
- }
1439
-
1440
- // tsx.ts
1441
- async function tsx(options) {
1442
- const instance = new TsxInstance(options);
1443
- return instance.build();
1444
- }
1445
-
1446
596
  // middleware.ts
1447
597
  function logger(options) {
1448
598
  return async (req, ctx, next) => {
@@ -1598,12 +748,12 @@ function auth(options) {
1598
748
  }
1599
749
 
1600
750
  // static.ts
1601
- import { createHash as createHash2 } from "node:crypto";
751
+ import { createHash } from "node:crypto";
1602
752
  import { open, realpath } from "node:fs/promises";
1603
- import { extname, resolve as resolve3, normalize, sep as sep2 } from "node:path";
753
+ import { extname, resolve as resolve2, normalize, sep } from "node:path";
1604
754
  import { Readable } from "node:stream";
1605
755
  function serveStatic(root, options) {
1606
- const rootDir = resolve3(root);
756
+ const rootDir = resolve2(root);
1607
757
  const opts = options ?? {};
1608
758
  return async (req, ctx) => {
1609
759
  const relativePath = ctx.params["*"] ?? new URL(req.url).pathname.slice(1);
@@ -1611,8 +761,8 @@ function serveStatic(root, options) {
1611
761
  if (decoded.includes("..") || decoded.includes("\0")) {
1612
762
  return new Response("Forbidden", { status: 403 });
1613
763
  }
1614
- let filePath = normalize(resolve3(rootDir, decoded));
1615
- if (!filePath.startsWith(rootDir + sep2) && filePath !== rootDir) {
764
+ let filePath = normalize(resolve2(rootDir, decoded));
765
+ if (!filePath.startsWith(rootDir + sep) && filePath !== rootDir) {
1616
766
  return new Response("Forbidden", { status: 403 });
1617
767
  }
1618
768
  let fileHandle;
@@ -1620,15 +770,15 @@ function serveStatic(root, options) {
1620
770
  fileHandle = await open(filePath, "r");
1621
771
  let stat = await fileHandle.stat();
1622
772
  const realPath = await realpath(filePath);
1623
- if (!realPath.startsWith(rootDir + sep2) && realPath !== rootDir) {
773
+ if (!realPath.startsWith(rootDir + sep) && realPath !== rootDir) {
1624
774
  await fileHandle.close();
1625
775
  return new Response("Forbidden", { status: 403 });
1626
776
  }
1627
777
  if (stat.isDirectory()) {
1628
778
  await fileHandle.close();
1629
779
  const indexFile = opts.index ?? "index.html";
1630
- filePath = resolve3(filePath, indexFile);
1631
- if (!filePath.startsWith(rootDir + sep2)) {
780
+ filePath = resolve2(filePath, indexFile);
781
+ if (!filePath.startsWith(rootDir + sep)) {
1632
782
  return new Response("Forbidden", { status: 403 });
1633
783
  }
1634
784
  fileHandle = await open(filePath, "r");
@@ -1639,7 +789,7 @@ function serveStatic(root, options) {
1639
789
  }
1640
790
  }
1641
791
  const mimeType = MIME_TYPES[extname(filePath).toLowerCase()] ?? "application/octet-stream";
1642
- const etag = `"${createHash2("md5").update(`${stat.size}-${stat.mtimeMs}`).digest("hex")}"`;
792
+ const etag = `"${createHash("md5").update(`${stat.size}-${stat.mtimeMs}`).digest("hex")}"`;
1643
793
  const ifNoneMatch = req.headers.get("if-none-match");
1644
794
  if (ifNoneMatch === etag) {
1645
795
  await fileHandle.close();
@@ -1860,7 +1010,7 @@ function deleteCookie(res, name, options) {
1860
1010
  // upload.ts
1861
1011
  import { writeFile, mkdir } from "node:fs/promises";
1862
1012
  import { randomUUID } from "node:crypto";
1863
- import { join as join2, extname as extname2 } from "node:path";
1013
+ import { join, extname as extname2 } from "node:path";
1864
1014
  var extensionMimeMap = {
1865
1015
  ".jpg": "image/jpeg",
1866
1016
  ".jpeg": "image/jpeg",
@@ -1927,7 +1077,7 @@ function upload(options) {
1927
1077
  };
1928
1078
  if (saveDir) {
1929
1079
  const safeName = value.name.replace(/[/\\\0]/g, "_");
1930
- const filePath = join2(saveDir, `${randomUUID()}-${safeName}`);
1080
+ const filePath = join(saveDir, `${randomUUID()}-${safeName}`);
1931
1081
  await writeFile(filePath, buf);
1932
1082
  uf.path = filePath;
1933
1083
  }
@@ -2117,12 +1267,12 @@ function requestId(options) {
2117
1267
  const gen = options?.generator ?? (() => crypto2.randomUUID());
2118
1268
  return async (req, ctx, next) => {
2119
1269
  const existing = req.headers.get(header);
2120
- const id2 = existing ?? gen();
2121
- ctx.requestId = id2;
1270
+ const id3 = existing ?? gen();
1271
+ ctx.requestId = id3;
2122
1272
  const res = await next(req, ctx);
2123
1273
  if (res.headers.has(header)) return res;
2124
1274
  const h = new Headers(res.headers);
2125
- h.set(header, id2);
1275
+ h.set(header, id3);
2126
1276
  return new Response(res.body, { status: res.status, statusText: res.statusText, headers: h });
2127
1277
  };
2128
1278
  }
@@ -2292,7 +1442,7 @@ function graphql(handler) {
2292
1442
  }
2293
1443
  return executeQuery(schema, params, options, req, ctx);
2294
1444
  });
2295
- return { router: () => r };
1445
+ return r;
2296
1446
  }
2297
1447
 
2298
1448
  // ai.ts
@@ -2319,7 +1469,7 @@ async function aiStream(handler) {
2319
1469
  const result = streamText4(options);
2320
1470
  return result.toTextStreamResponse();
2321
1471
  });
2322
- return { router: () => r };
1472
+ return r;
2323
1473
  }
2324
1474
 
2325
1475
  // ai/workflow.ts
@@ -2344,10 +1494,10 @@ function resolveRef(path2, ctx) {
2344
1494
  const after = path2.slice(7);
2345
1495
  const dot = after.indexOf(".");
2346
1496
  if (dot === -1) return ctx.nodeOutputs.get(after);
2347
- const id2 = after.slice(0, dot);
1497
+ const id3 = after.slice(0, dot);
2348
1498
  const propPath = after.slice(dot + 1);
2349
- const output = ctx.nodeOutputs.get(id2);
2350
- if (output === void 0) throw new Error(`Node "${id2}" has no output yet`);
1499
+ const output = ctx.nodeOutputs.get(id3);
1500
+ if (output === void 0) throw new Error(`Node "${id3}" has no output yet`);
2351
1501
  return getByPath(output, propPath.startsWith("output") ? propPath.slice(7).split(".").filter(Boolean) : propPath.split("."));
2352
1502
  }
2353
1503
  if (path2.startsWith("$var.")) {
@@ -2873,18 +2023,18 @@ var Table = class {
2873
2023
  `;
2874
2024
  return rows;
2875
2025
  }
2876
- async read(sql2, id2, opts) {
2026
+ async read(sql2, id3, opts) {
2877
2027
  if (opts?.select?.length) {
2878
2028
  const columns = opts.select.map((c) => `"${c}"`).join(", ");
2879
2029
  const [row2] = await sql2.unsafe(
2880
2030
  `SELECT ${columns} FROM "${this.tableName}" WHERE id = $1 LIMIT 1`,
2881
- [id2]
2031
+ [id3]
2882
2032
  );
2883
2033
  return row2 ?? void 0;
2884
2034
  }
2885
2035
  const [row] = await sql2`
2886
2036
  SELECT * FROM ${sql2(this.tableName)}
2887
- WHERE ${sql2("id")} = ${id2} LIMIT 1
2037
+ WHERE ${sql2("id")} = ${id3} LIMIT 1
2888
2038
  `;
2889
2039
  return row ?? void 0;
2890
2040
  }
@@ -2918,11 +2068,11 @@ var Table = class {
2918
2068
  const rows = await sql2.unsafe(query, values);
2919
2069
  return { count, data: rows };
2920
2070
  }
2921
- async update(sql2, id2, data) {
2071
+ async update(sql2, id3, data) {
2922
2072
  const { sets, values: setValues } = this._buildSET(data);
2923
2073
  if (sets.length === 0) return void 0;
2924
2074
  const query = `UPDATE "${this.tableName}" AS t SET ${sets.join(", ")} FROM (SELECT ctid FROM "${this.tableName}" WHERE id = $${setValues.length + 1} LIMIT 1) AS sub WHERE t.ctid = sub.ctid RETURNING t.*`;
2925
- const rows = await sql2.unsafe(query, [...setValues, id2]);
2075
+ const rows = await sql2.unsafe(query, [...setValues, id3]);
2926
2076
  return rows[0] ?? void 0;
2927
2077
  }
2928
2078
  async updateMany(sql2, where, data) {
@@ -2936,22 +2086,22 @@ var Table = class {
2936
2086
  );
2937
2087
  return rows.length;
2938
2088
  }
2939
- async delete(sql2, id2) {
2089
+ async delete(sql2, id3) {
2940
2090
  if (this.hasColumn("deleted_at")) {
2941
2091
  const [row2] = await sql2.unsafe(
2942
2092
  `UPDATE "${this.tableName}" SET "deleted_at" = NOW() WHERE id = $1 RETURNING *`,
2943
- [id2]
2093
+ [id3]
2944
2094
  );
2945
2095
  return row2 ?? void 0;
2946
2096
  }
2947
2097
  const [row] = await sql2`
2948
- DELETE FROM ${sql2(this.tableName)} WHERE ${sql2("id")} = ${id2} RETURNING *
2098
+ DELETE FROM ${sql2(this.tableName)} WHERE ${sql2("id")} = ${id3} RETURNING *
2949
2099
  `;
2950
2100
  return row ?? void 0;
2951
2101
  }
2952
- async hardDelete(sql2, id2) {
2102
+ async hardDelete(sql2, id3) {
2953
2103
  const [row] = await sql2`
2954
- DELETE FROM ${sql2(this.tableName)} WHERE ${sql2("id")} = ${id2} RETURNING *
2104
+ DELETE FROM ${sql2(this.tableName)} WHERE ${sql2("id")} = ${id3} RETURNING *
2955
2105
  `;
2956
2106
  return row ?? void 0;
2957
2107
  }
@@ -3047,23 +2197,23 @@ var BoundTable = class _BoundTable {
3047
2197
  async insertMany(data) {
3048
2198
  return await this.inner.insertMany(this.sql, data);
3049
2199
  }
3050
- async read(id2, opts) {
3051
- return await this.inner.read(this.sql, id2, opts);
2200
+ async read(id3, opts) {
2201
+ return await this.inner.read(this.sql, id3, opts);
3052
2202
  }
3053
2203
  async readMany(where, opts) {
3054
2204
  return await this.inner.readMany(this.sql, where, opts);
3055
2205
  }
3056
- async update(id2, data) {
3057
- return await this.inner.update(this.sql, id2, data);
2206
+ async update(id3, data) {
2207
+ return await this.inner.update(this.sql, id3, data);
3058
2208
  }
3059
2209
  async updateMany(where, data) {
3060
2210
  return await this.inner.updateMany(this.sql, where, data);
3061
2211
  }
3062
- async delete(id2) {
3063
- return await this.inner.delete(this.sql, id2);
2212
+ async delete(id3) {
2213
+ return await this.inner.delete(this.sql, id3);
3064
2214
  }
3065
- async hardDelete(id2) {
3066
- return await this.inner.hardDelete(this.sql, id2);
2215
+ async hardDelete(id3) {
2216
+ return await this.inner.hardDelete(this.sql, id3);
3067
2217
  }
3068
2218
  async deleteMany(where) {
3069
2219
  return await this.inner.deleteMany(this.sql, where);
@@ -3530,8 +2680,8 @@ function user(options) {
3530
2680
  const { data: rows } = await users.readMany({ email });
3531
2681
  return rows[0];
3532
2682
  }
3533
- async function findById(id2) {
3534
- return await users.read(id2);
2683
+ async function findById(id3) {
2684
+ return await users.read(id3);
3535
2685
  }
3536
2686
  async function register(data) {
3537
2687
  const { email, password, name } = RegisterSchema.parse(data);
@@ -3592,8 +2742,8 @@ function user(options) {
3592
2742
  };
3593
2743
  }
3594
2744
  function router() {
3595
- const r = new Router();
3596
- r.post("/register", async (req) => {
2745
+ const r2 = new Router();
2746
+ r2.post("/register", async (req) => {
3597
2747
  try {
3598
2748
  const body = await req.json();
3599
2749
  const result = await register(body);
@@ -3606,7 +2756,7 @@ function user(options) {
3606
2756
  return Response.json({ error: err.message }, { status });
3607
2757
  }
3608
2758
  });
3609
- r.post("/login", async (req) => {
2759
+ r2.post("/login", async (req) => {
3610
2760
  try {
3611
2761
  const body = await req.json();
3612
2762
  const result = await login(body);
@@ -3622,30 +2772,29 @@ function user(options) {
3622
2772
  }
3623
2773
  });
3624
2774
  if (oauth2) {
3625
- r.get("/oauth/authorize", (req, ctx) => oauth2.authorizeHandler(req, ctx));
3626
- r.post("/oauth/consent", (req) => oauth2.consentHandler(req));
3627
- r.post("/oauth/token", (req) => oauth2.tokenHandler(req));
3628
- }
3629
- return r;
3630
- }
3631
- const mod = {
3632
- router,
3633
- middleware,
3634
- migrate,
3635
- register,
3636
- login,
3637
- verify,
3638
- registerClient: oauth2 ? (data) => oauth2.registerClient(data) : async () => {
3639
- throw new Error("OAuth2 server is not enabled");
3640
- },
3641
- getClient: oauth2 ? (clientId) => oauth2.getClient(clientId) : async () => {
3642
- throw new Error("OAuth2 server is not enabled");
3643
- },
3644
- revokeClient: oauth2 ? (clientId) => oauth2.revokeClient(clientId) : async () => {
3645
- throw new Error("OAuth2 server is not enabled");
3646
- },
3647
- close: () => base.close()
2775
+ r2.get("/oauth/authorize", (req, ctx) => oauth2.authorizeHandler(req, ctx));
2776
+ r2.post("/oauth/consent", (req) => oauth2.consentHandler(req));
2777
+ r2.post("/oauth/token", (req) => oauth2.tokenHandler(req));
2778
+ }
2779
+ return r2;
2780
+ }
2781
+ const r = router();
2782
+ const mod = r;
2783
+ mod.middleware = middleware;
2784
+ mod.migrate = migrate;
2785
+ mod.register = register;
2786
+ mod.login = login;
2787
+ mod.verify = verify;
2788
+ mod.registerClient = oauth2 ? (data) => oauth2.registerClient(data) : async () => {
2789
+ throw new Error("OAuth2 server is not enabled");
2790
+ };
2791
+ mod.getClient = oauth2 ? (clientId) => oauth2.getClient(clientId) : async () => {
2792
+ throw new Error("OAuth2 server is not enabled");
2793
+ };
2794
+ mod.revokeClient = oauth2 ? (clientId) => oauth2.revokeClient(clientId) : async () => {
2795
+ throw new Error("OAuth2 server is not enabled");
3648
2796
  };
2797
+ mod.close = () => base.close();
3649
2798
  return mod;
3650
2799
  }
3651
2800
 
@@ -3688,7 +2837,7 @@ function createHub(opts) {
3688
2837
  }
3689
2838
  });
3690
2839
  }
3691
- function join5(key, ws) {
2840
+ function join6(key, ws) {
3692
2841
  if (!channels.has(key)) {
3693
2842
  channels.set(key, /* @__PURE__ */ new Set());
3694
2843
  redisSub?.subscribe(`${prefix}${key}`);
@@ -3729,7 +2878,7 @@ function createHub(opts) {
3729
2878
  await redisSub.quit();
3730
2879
  }
3731
2880
  }
3732
- return { join: join5, leave, broadcast, close };
2881
+ return { join: join6, leave, broadcast, close };
3733
2882
  }
3734
2883
 
3735
2884
  // queue/index.ts
@@ -3855,7 +3004,7 @@ function queue(opts) {
3855
3004
  }
3856
3005
  }
3857
3006
  mw.add = function add(type, payload, opts2) {
3858
- const id2 = crypto4.randomUUID();
3007
+ const id3 = crypto4.randomUUID();
3859
3008
  let runAt;
3860
3009
  if (opts2?.schedule) {
3861
3010
  runAt = cronNext(opts2.schedule);
@@ -3864,9 +3013,9 @@ function queue(opts) {
3864
3013
  } else {
3865
3014
  runAt = Date.now();
3866
3015
  }
3867
- const job = { id: id2, type, payload, createdAt: Date.now(), runAt };
3016
+ const job = { id: id3, type, payload, createdAt: Date.now(), runAt };
3868
3017
  if (opts2?.schedule) job.schedule = opts2.schedule;
3869
- return redis2.zadd(jobKey, runAt, JSON.stringify(job)).then(() => id2);
3018
+ return redis2.zadd(jobKey, runAt, JSON.stringify(job)).then(() => id3);
3870
3019
  };
3871
3020
  mw.process = function process2(type, handler) {
3872
3021
  handlers.set(type, handler);
@@ -4724,76 +3873,78 @@ function tenant(options) {
4724
3873
  const sql2 = pg.sql;
4725
3874
  const usersTable = options.usersTable;
4726
3875
  const base = new PgModule(pg);
4727
- return {
4728
- migrate: async () => {
4729
- await sql2.unsafe(`CREATE EXTENSION IF NOT EXISTS "vector"`);
4730
- const tenants = pgTable("_tenants", {
4731
- id: text("id").primaryKey().default(sql`gen_random_uuid()`),
4732
- name: text("name").notNull(),
4733
- created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
4734
- });
4735
- await tenants.create(sql2);
4736
- const members = pgTable("_tenant_members", {
4737
- id: serial("id").primaryKey(),
4738
- tenant_id: text("tenant_id").notNull().references("_tenants", "id", "cascade"),
4739
- user_id: integer("user_id").notNull(),
4740
- role: text("role").notNull().default("member"),
4741
- created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
4742
- });
4743
- await members.create(sql2);
4744
- await members.createIndex(sql2, "user_id");
4745
- await sql2.unsafe(`CREATE UNIQUE INDEX IF NOT EXISTS "_tenant_members_unique_idx" ON "_tenant_members" ("tenant_id", "user_id")`);
4746
- const tables = pgTable("_user_tables", {
4747
- id: serial("id").primaryKey(),
4748
- tenant_id: text("tenant_id").notNull().references("_tenants", "id", "cascade"),
4749
- slug: text("slug").notNull(),
4750
- label: text("label").notNull().default(""),
4751
- fields: jsonb("fields").notNull().default(sql`'[]'::jsonb`),
4752
- created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
4753
- });
4754
- await tables.create(sql2);
4755
- await tables.createIndex(sql2, "tenant_id");
4756
- await sql2.unsafe(`CREATE UNIQUE INDEX IF NOT EXISTS "_user_tables_unique_idx" ON "_user_tables" ("tenant_id", "slug")`);
4757
- },
4758
- middleware() {
4759
- return async (req, ctx, next) => {
4760
- const user2 = ctx.user;
4761
- if (!user2) {
4762
- return new Response("Unauthorized", { status: 401 });
4763
- }
4764
- const members = await sql2`
4765
- SELECT tm.role, t.id, t.name
4766
- FROM "_tenant_members" tm
4767
- JOIN "_tenants" t ON t.id = tm.tenant_id
4768
- WHERE tm.user_id = ${user2.id}
4769
- `;
4770
- if (members.length === 0) {
4771
- return new Response("No tenant found. Create one via POST /sys/tenants.", { status: 403 });
4772
- }
4773
- if (members.length === 1) {
4774
- const m = members[0];
4775
- ctx.tenant = { id: m.id, name: m.name, role: m.role };
4776
- return next(req, ctx);
4777
- }
4778
- const headerId = req.headers.get("X-Tenant-ID");
4779
- if (!headerId) {
4780
- return Response.json({
4781
- error: "Multiple tenants. Set X-Tenant-ID header.",
4782
- tenants: members.map((m) => ({ id: m.id, name: m.name, role: m.role }))
4783
- }, { status: 300 });
4784
- }
4785
- const member = members.find((m) => m.id === headerId);
4786
- if (!member) {
4787
- return new Response("Tenant not found", { status: 403 });
4788
- }
4789
- ctx.tenant = { id: member.id, name: member.name, role: member.role };
3876
+ async function migrate() {
3877
+ await sql2.unsafe(`CREATE EXTENSION IF NOT EXISTS "vector"`);
3878
+ const tenants = pgTable("_tenants", {
3879
+ id: text("id").primaryKey().default(sql`gen_random_uuid()`),
3880
+ name: text("name").notNull(),
3881
+ created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
3882
+ });
3883
+ await tenants.create(sql2);
3884
+ const members = pgTable("_tenant_members", {
3885
+ id: serial("id").primaryKey(),
3886
+ tenant_id: text("tenant_id").notNull().references("_tenants", "id", "cascade"),
3887
+ user_id: integer("user_id").notNull(),
3888
+ role: text("role").notNull().default("member"),
3889
+ created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
3890
+ });
3891
+ await members.create(sql2);
3892
+ await members.createIndex(sql2, "user_id");
3893
+ await sql2.unsafe(`CREATE UNIQUE INDEX IF NOT EXISTS "_tenant_members_unique_idx" ON "_tenant_members" ("tenant_id", "user_id")`);
3894
+ const tables = pgTable("_user_tables", {
3895
+ id: serial("id").primaryKey(),
3896
+ tenant_id: text("tenant_id").notNull().references("_tenants", "id", "cascade"),
3897
+ slug: text("slug").notNull(),
3898
+ label: text("label").notNull().default(""),
3899
+ fields: jsonb("fields").notNull().default(sql`'[]'::jsonb`),
3900
+ created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
3901
+ });
3902
+ await tables.create(sql2);
3903
+ await tables.createIndex(sql2, "tenant_id");
3904
+ await sql2.unsafe(`CREATE UNIQUE INDEX IF NOT EXISTS "_user_tables_unique_idx" ON "_user_tables" ("tenant_id", "slug")`);
3905
+ }
3906
+ function middleware() {
3907
+ return async (req, ctx, next) => {
3908
+ const user2 = ctx.user;
3909
+ if (!user2) {
3910
+ return new Response("Unauthorized", { status: 401 });
3911
+ }
3912
+ const members = await sql2`
3913
+ SELECT tm.role, t.id, t.name
3914
+ FROM "_tenant_members" tm
3915
+ JOIN "_tenants" t ON t.id = tm.tenant_id
3916
+ WHERE tm.user_id = ${user2.id}
3917
+ `;
3918
+ if (members.length === 0) {
3919
+ return new Response("No tenant found. Create one via POST /sys/tenants.", { status: 403 });
3920
+ }
3921
+ if (members.length === 1) {
3922
+ const m = members[0];
3923
+ ctx.tenant = { id: m.id, name: m.name, role: m.role };
4790
3924
  return next(req, ctx);
4791
- };
4792
- },
4793
- router: () => buildRouter(sql2, usersTable),
4794
- graphql: () => buildGraphQLHandler(sql2),
4795
- close: () => base.close()
4796
- };
3925
+ }
3926
+ const headerId = req.headers.get("X-Tenant-ID");
3927
+ if (!headerId) {
3928
+ return Response.json({
3929
+ error: "Multiple tenants. Set X-Tenant-ID header.",
3930
+ tenants: members.map((m) => ({ id: m.id, name: m.name, role: m.role }))
3931
+ }, { status: 300 });
3932
+ }
3933
+ const member = members.find((m) => m.id === headerId);
3934
+ if (!member) {
3935
+ return new Response("Tenant not found", { status: 403 });
3936
+ }
3937
+ ctx.tenant = { id: member.id, name: member.name, role: member.role };
3938
+ return next(req, ctx);
3939
+ };
3940
+ }
3941
+ const r = buildRouter(sql2, usersTable);
3942
+ const mod = r;
3943
+ mod.migrate = migrate;
3944
+ mod.middleware = middleware;
3945
+ mod.graphql = () => buildGraphQLHandler(sql2);
3946
+ mod.close = () => base.close();
3947
+ return mod;
4797
3948
  }
4798
3949
 
4799
3950
  // agent/client.ts
@@ -4802,8 +3953,8 @@ import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
4802
3953
  // agent/rest.ts
4803
3954
  function buildRouter2(deps) {
4804
3955
  const { agents: agentsTable, knowledge, runner } = deps;
4805
- async function getAgent(id2) {
4806
- const row = await agentsTable.read(id2);
3956
+ async function getAgent(id3) {
3957
+ const row = await agentsTable.read(id3);
4807
3958
  return row ?? null;
4808
3959
  }
4809
3960
  const r = new Router();
@@ -4830,8 +3981,8 @@ function buildRouter2(deps) {
4830
3981
  return Response.json(agent2);
4831
3982
  });
4832
3983
  r.patch("/agents/:id", async (req, ctx) => {
4833
- const id2 = parseInt(ctx.params.id, 10);
4834
- const agent2 = await getAgent(id2);
3984
+ const id3 = parseInt(ctx.params.id, 10);
3985
+ const agent2 = await getAgent(id3);
4835
3986
  if (!agent2) return Response.json({ error: "Agent not found" }, { status: 404 });
4836
3987
  const body = await req.json();
4837
3988
  const updateData = {};
@@ -4843,23 +3994,23 @@ function buildRouter2(deps) {
4843
3994
  if (Object.keys(updateData).length === 0) {
4844
3995
  return Response.json({ error: "No fields to update" }, { status: 400 });
4845
3996
  }
4846
- const row = await agentsTable.update(id2, updateData);
3997
+ const row = await agentsTable.update(id3, updateData);
4847
3998
  return Response.json(row);
4848
3999
  });
4849
4000
  r.delete("/agents/:id", async (_req, ctx) => {
4850
- const id2 = parseInt(ctx.params.id, 10);
4851
- const row = await agentsTable.delete(id2);
4001
+ const id3 = parseInt(ctx.params.id, 10);
4002
+ const row = await agentsTable.delete(id3);
4852
4003
  if (!row) return Response.json({ error: "Agent not found" }, { status: 404 });
4853
4004
  return Response.json({ ok: true });
4854
4005
  });
4855
4006
  r.post("/agents/:id/run", async (req, ctx) => {
4856
- const id2 = parseInt(ctx.params.id, 10);
4007
+ const id3 = parseInt(ctx.params.id, 10);
4857
4008
  const body = await req.json();
4858
4009
  if (!body.input && !body.messages) {
4859
4010
  return Response.json({ error: "input or messages is required" }, { status: 400 });
4860
4011
  }
4861
4012
  try {
4862
- const result = await runner.run(id2, body);
4013
+ const result = await runner.run(id3, body);
4863
4014
  if ("stream" in result) {
4864
4015
  return new Response(result.stream, {
4865
4016
  headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache" }
@@ -5070,18 +4221,18 @@ function agent(options) {
5070
4221
  });
5071
4222
  const runner = createRunner({ sql: sql2, agents: agentsTable, knowledge: knowledgeTable, getModel, getEmbeddingModel, userTools: options.tools });
5072
4223
  const base = new PgModule(pg);
5073
- return {
5074
- migrate: async () => {
5075
- await agentsTable.create();
5076
- await agentsTable.createIndex("tenant_id");
5077
- await knowledgeTable.create();
5078
- await knowledgeTable.createIndex("agent_id");
5079
- },
5080
- router: () => buildRouter2({ agents: agentsTable, knowledge: knowledgeTable, runner }),
5081
- run: (agentId, params) => runner.run(agentId, params),
5082
- addKnowledge: (agentId, title, content) => runner.addKnowledge(agentId, title, content),
5083
- close: () => base.close()
4224
+ const r = buildRouter2({ agents: agentsTable, knowledge: knowledgeTable, runner });
4225
+ const mod = r;
4226
+ mod.migrate = async () => {
4227
+ await agentsTable.create();
4228
+ await agentsTable.createIndex("tenant_id");
4229
+ await knowledgeTable.create();
4230
+ await knowledgeTable.createIndex("agent_id");
5084
4231
  };
4232
+ mod.run = (agentId, params) => runner.run(agentId, params);
4233
+ mod.addKnowledge = (agentId, title, content) => runner.addKnowledge(agentId, title, content);
4234
+ mod.close = () => base.close();
4235
+ return mod;
5085
4236
  }
5086
4237
 
5087
4238
  // messager/agent.ts
@@ -5270,15 +4421,15 @@ function buildRouter3(deps) {
5270
4421
  return Response.json(rows);
5271
4422
  });
5272
4423
  r.get("/channels/:id", async (_req, ctx) => {
5273
- const id2 = parseInt(ctx.params.id, 10);
5274
- const ch = await channels.read(id2);
4424
+ const id3 = parseInt(ctx.params.id, 10);
4425
+ const ch = await channels.read(id3);
5275
4426
  if (!ch) return Response.json({ error: "Channel not found" }, { status: 404 });
5276
- const { data: memberRows } = await members.readMany({ channel_id: id2 });
4427
+ const { data: memberRows } = await members.readMany({ channel_id: id3 });
5277
4428
  return Response.json({ channel: ch, members: memberRows });
5278
4429
  });
5279
4430
  r.delete("/channels/:id", async (_req, ctx) => {
5280
- const id2 = parseInt(ctx.params.id, 10);
5281
- await channels.delete(id2);
4431
+ const id3 = parseInt(ctx.params.id, 10);
4432
+ await channels.delete(id3);
5282
4433
  return Response.json({ ok: true });
5283
4434
  });
5284
4435
  r.post("/channels/:id/members", async (req, ctx) => {
@@ -5390,31 +4541,32 @@ function messager(options) {
5390
4541
  mime_type: text("mime_type"),
5391
4542
  created_at: timestamptz("created_at").notNull().default(sql`NOW()`)
5392
4543
  });
5393
- return {
5394
- migrate: async () => {
5395
- await channels.create();
5396
- await channels.createIndex("tenant_id");
5397
- await members.create();
5398
- await members.createIndex("member_id");
5399
- await members.createIndex(["channel_id", "member_id", "member_type"], { unique: true });
5400
- await messages2.create();
5401
- await messages2.createIndex(["channel_id", "created_at"], { desc: true });
5402
- },
5403
- router: () => buildRouter3({ sql: sql2, channels, members, messages: messages2, agents, hub }),
5404
- wsHandler: () => wsResult.handler,
5405
- async send(channelId, content, opts) {
5406
- const msg = await messages2.insert({
5407
- channel_id: channelId,
5408
- sender_id: opts?.sender_id ?? 0,
5409
- sender_type: opts?.sender_type ?? "system",
5410
- type: opts?.type ?? "text",
5411
- content
5412
- });
5413
- broadcastToChannel(hub, channelId, { type: "message", data: msg });
5414
- return msg;
5415
- },
5416
- close: () => base.close()
4544
+ const r = buildRouter3({ sql: sql2, channels, members, messages: messages2, agents, hub });
4545
+ async function send(channelId, content, opts) {
4546
+ const msg = await messages2.insert({
4547
+ channel_id: channelId,
4548
+ sender_id: opts?.sender_id ?? 0,
4549
+ sender_type: opts?.sender_type ?? "system",
4550
+ type: opts?.type ?? "text",
4551
+ content
4552
+ });
4553
+ broadcastToChannel(hub, channelId, { type: "message", data: msg });
4554
+ return msg;
4555
+ }
4556
+ const mod = r;
4557
+ mod.migrate = async () => {
4558
+ await channels.create();
4559
+ await channels.createIndex("tenant_id");
4560
+ await members.create();
4561
+ await members.createIndex("member_id");
4562
+ await members.createIndex(["channel_id", "member_id", "member_type"], { unique: true });
4563
+ await messages2.create();
4564
+ await messages2.createIndex(["channel_id", "created_at"], { desc: true });
5417
4565
  };
4566
+ mod.wsHandler = () => wsResult.handler;
4567
+ mod.send = send;
4568
+ mod.close = () => base.close();
4569
+ return mod;
5418
4570
  }
5419
4571
 
5420
4572
  // deploy/index.ts
@@ -5536,14 +4688,14 @@ function forkApp(opts) {
5536
4688
  return { child, port: opts.port };
5537
4689
  }
5538
4690
  function stopProcess(mp, timeout = 1e4) {
5539
- return new Promise((resolve11) => {
4691
+ return new Promise((resolve12) => {
5540
4692
  const timer = setTimeout(() => {
5541
4693
  mp.child.kill("SIGKILL");
5542
- resolve11();
4694
+ resolve12();
5543
4695
  }, timeout);
5544
4696
  mp.child.on("exit", () => {
5545
4697
  clearTimeout(timer);
5546
- resolve11();
4698
+ resolve12();
5547
4699
  });
5548
4700
  mp.child.kill("SIGTERM");
5549
4701
  });
@@ -5972,6 +5124,366 @@ function ensureCertificates(config) {
5972
5124
  // opencode/client.ts
5973
5125
  import { createOpenAI as createOpenAI3 } from "@ai-sdk/openai";
5974
5126
 
5127
+ // opencode/rest.ts
5128
+ import { join as join4 } from "node:path";
5129
+
5130
+ // ssr/ssr.ts
5131
+ import { createElement } from "react";
5132
+ import { createHash as createHash3 } from "node:crypto";
5133
+ import { dirname as dirname2, resolve as resolve4 } from "node:path";
5134
+ import { AsyncLocalStorage } from "node:async_hooks";
5135
+
5136
+ // ssr/compile.ts
5137
+ import * as esbuild from "esbuild";
5138
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2 } from "node:fs";
5139
+ import { join as join2, resolve as resolve3, dirname } from "node:path";
5140
+ import { pathToFileURL } from "node:url";
5141
+ import { createHash as createHash2 } from "node:crypto";
5142
+ var OUT_DIR = ".weifuwu/ssr";
5143
+ var cache = /* @__PURE__ */ new Map();
5144
+ var externals = [
5145
+ "react",
5146
+ "react-dom",
5147
+ "esbuild",
5148
+ "graphql",
5149
+ "ws",
5150
+ "zod",
5151
+ "@graphql-tools/schema",
5152
+ "ai"
5153
+ ];
5154
+ var _alias = null;
5155
+ function resolveAliases() {
5156
+ if (_alias) return _alias;
5157
+ const configFiles = ["tsconfig.json", "jsconfig.json"];
5158
+ for (const file of configFiles) {
5159
+ const p = resolve3(file);
5160
+ if (existsSync2(p)) {
5161
+ try {
5162
+ const config = JSON.parse(readFileSync2(p, "utf-8"));
5163
+ const paths = config.compilerOptions?.paths;
5164
+ if (paths) {
5165
+ const alias = {};
5166
+ for (const [key, values] of Object.entries(paths)) {
5167
+ const cleanKey = key.replace("/*", "");
5168
+ const val = values[0]?.replace("/*", "");
5169
+ if (val) alias[cleanKey] = resolve3(dirname(p), val);
5170
+ }
5171
+ _alias = alias;
5172
+ return alias;
5173
+ }
5174
+ } catch {
5175
+ }
5176
+ }
5177
+ }
5178
+ _alias = {};
5179
+ return {};
5180
+ }
5181
+ function id(s) {
5182
+ return createHash2("md5").update(s).digest("hex").slice(0, 8);
5183
+ }
5184
+ async function compileTsx(path2) {
5185
+ if (cache.has(path2)) return cache.get(path2);
5186
+ const absPath = resolve3(path2);
5187
+ mkdirSync(OUT_DIR, { recursive: true });
5188
+ const hash = id(absPath);
5189
+ const outPath = join2(OUT_DIR, hash + ".js");
5190
+ await esbuild.build({
5191
+ entryPoints: { [hash]: absPath },
5192
+ outdir: OUT_DIR,
5193
+ format: "esm",
5194
+ platform: "node",
5195
+ jsx: "automatic",
5196
+ jsxImportSource: "react",
5197
+ bundle: true,
5198
+ external: externals,
5199
+ alias: resolveAliases(),
5200
+ write: true,
5201
+ allowOverwrite: true
5202
+ });
5203
+ const mod = await import(pathToFileURL(outPath).href);
5204
+ cache.set(path2, mod);
5205
+ return mod;
5206
+ }
5207
+
5208
+ // ssr/stream.ts
5209
+ import { TextDecoder, TextEncoder as TextEncoder2 } from "node:util";
5210
+ var _publicEnv = null;
5211
+ function getPublicEnv() {
5212
+ if (_publicEnv) return _publicEnv;
5213
+ _publicEnv = {};
5214
+ for (const key of Object.keys(process.env)) {
5215
+ if (key.startsWith("WEIFUWU_PUBLIC_")) {
5216
+ _publicEnv[key] = process.env[key];
5217
+ }
5218
+ }
5219
+ return _publicEnv;
5220
+ }
5221
+ function buildHeadPayload(opts) {
5222
+ const { ctx, base, compiledTailwindCss } = opts;
5223
+ let result = "";
5224
+ if (ctx.prefs?.theme) {
5225
+ result += `<script>!function(){var t=(document.cookie.match(/(?:^|;\\s*)theme=([^;]+)/)||[])[1]||'system';if(t==='system'){t=window.matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light'}document.documentElement.setAttribute('data-theme',t)}()</script>
5226
+ `;
5227
+ }
5228
+ if (compiledTailwindCss) {
5229
+ result += `<link rel="stylesheet" href="${base}/__wfw/style.css" />
5230
+ `;
5231
+ }
5232
+ const localeData = ctx.parsed?.__localeData ?? globalThis.__LOCALE_DATA__;
5233
+ if (localeData && Object.keys(localeData).length > 0) {
5234
+ result += `<script>window.__LOCALE_DATA__=${JSON.stringify(localeData)}</script>
5235
+ `;
5236
+ }
5237
+ const loaderData = opts.loaderData || {};
5238
+ const ctxData = {
5239
+ params: ctx.params,
5240
+ query: ctx.query,
5241
+ user: ctx.user,
5242
+ parsed: ctx.parsed,
5243
+ prefs: ctx.prefs,
5244
+ loaderData
5245
+ };
5246
+ const publicEnv = getPublicEnv();
5247
+ if (Object.keys(publicEnv).length > 0) {
5248
+ ctxData.env = publicEnv;
5249
+ }
5250
+ result += `<script>window.__WEIFUWU_CTX=${JSON.stringify(ctxData)}</script>
5251
+ `;
5252
+ return result;
5253
+ }
5254
+ function buildBodyScripts(opts) {
5255
+ const parts = [];
5256
+ if (opts.loaderData && Object.keys(opts.loaderData).length > 0) {
5257
+ parts.push(`<script>window.__WEIFUWU_PROPS=${JSON.stringify(opts.loaderData)}</script>`);
5258
+ }
5259
+ if (opts.bundle) {
5260
+ parts.push(`<script type="module" src="${opts.base}${opts.bundle.url}"></script>`);
5261
+ }
5262
+ return parts.join("\n");
5263
+ }
5264
+ function streamResponse(reactStream, opts) {
5265
+ const decoder = new TextDecoder();
5266
+ const encoder2 = new TextEncoder2();
5267
+ const headPayload = buildHeadPayload(opts);
5268
+ let buffer = "";
5269
+ let headFlushed = false;
5270
+ let extractedHead = "";
5271
+ const output = new ReadableStream({
5272
+ async start(controller) {
5273
+ try {
5274
+ const reader = reactStream.getReader();
5275
+ async function push(chunk) {
5276
+ buffer += decoder.decode(chunk, { stream: true });
5277
+ if (!extractedHead) {
5278
+ const m = buffer.match(/<template id="__wfw_head">([\s\S]*?)<\/template>/);
5279
+ if (m) {
5280
+ extractedHead = m[1];
5281
+ buffer = buffer.replace(m[0], "");
5282
+ }
5283
+ }
5284
+ if (!headFlushed) {
5285
+ const idx = buffer.indexOf("</head>");
5286
+ if (idx !== -1) {
5287
+ const before = buffer.slice(0, idx);
5288
+ let injection = "";
5289
+ if (extractedHead) injection += "\n" + extractedHead;
5290
+ injection += headPayload;
5291
+ controller.enqueue(encoder2.encode(before + injection));
5292
+ buffer = buffer.slice(idx);
5293
+ headFlushed = true;
5294
+ }
5295
+ return;
5296
+ }
5297
+ controller.enqueue(encoder2.encode(buffer));
5298
+ buffer = "";
5299
+ }
5300
+ while (true) {
5301
+ const { done, value } = await reader.read();
5302
+ if (done) break;
5303
+ await push(value);
5304
+ }
5305
+ buffer = buffer.replace(/<template id="__wfw_head">[\s\S]*?<\/template>/g, "");
5306
+ if (buffer) controller.enqueue(encoder2.encode(buffer));
5307
+ const body = buildBodyScripts(opts);
5308
+ if (body) controller.enqueue(encoder2.encode("\n" + body));
5309
+ if (opts.isDev) {
5310
+ controller.enqueue(encoder2.encode(
5311
+ `
5312
+ <script>(function(){var ws=new WebSocket((location.protocol==='https:'?'wss:':'ws:')+'//'+location.host+'${opts.base}/__weifuwu/livereload');ws.onmessage=function(e){if(e.data==='reload')location.reload()};ws.onclose=function(){setTimeout(function(){location.reload()},500)}})()</script>`
5313
+ ));
5314
+ }
5315
+ } catch {
5316
+ const fallback = `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>500</title></head><body><h1>500 - Internal Server Error</h1></body></html>`;
5317
+ controller.enqueue(encoder2.encode(fallback));
5318
+ } finally {
5319
+ controller.close();
5320
+ }
5321
+ }
5322
+ });
5323
+ return new Response(output, {
5324
+ status: opts.status ?? 200,
5325
+ headers: { "content-type": "text/html; charset=utf-8" }
5326
+ });
5327
+ }
5328
+
5329
+ // ssr/ssr.ts
5330
+ var als = new AsyncLocalStorage();
5331
+ __registerAls(() => als.getStore());
5332
+ var isDev = process.env.NODE_ENV !== "production";
5333
+ function id2(s) {
5334
+ return createHash3("md5").update(s).digest("hex").slice(0, 8);
5335
+ }
5336
+ async function buildClientBundle(entryPath, layoutPaths) {
5337
+ try {
5338
+ const absEntry = resolve4(entryPath);
5339
+ const absLayouts = layoutPaths.map((p) => resolve4(p));
5340
+ const layoutImports = absLayouts.map((p) => `import${JSON.stringify(p)};`).join("");
5341
+ 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()})}})()`;
5342
+ const code = [
5343
+ layoutImports,
5344
+ `import{hydrateRoot}from'react-dom/client';`,
5345
+ `import{createElement,useState,useEffect}from'react';`,
5346
+ `import{TsxContext}from'weifuwu/react';`,
5347
+ `import P from${JSON.stringify(absEntry)};`,
5348
+ `var setCtx=${_sc};`,
5349
+ `const c=document.getElementById('__weifuwu_root');`,
5350
+ `if(window.__WEIFUWU_PROPS)setCtx({loaderData:window.__WEIFUWU_PROPS});`,
5351
+ `if(!window.__WFW_ROOT){`,
5352
+ `function App(){`,
5353
+ `const[p,setP]=useState({C:P});`,
5354
+ `useEffect(()=>{window.__WFW_SET_PAGE=(C)=>{setCtx({loaderData:window.__WEIFUWU_PROPS});setP({C})}},[]);`,
5355
+ `const ctx=window.__WEIFUWU_CTX||{};`,
5356
+ `return createElement(TsxContext.Provider,{value:ctx},`,
5357
+ `createElement(p.C,null))`,
5358
+ `}`,
5359
+ `window.__WFW_ROOT=hydrateRoot(c,createElement(App));`,
5360
+ `}else{`,
5361
+ `window.__WFW_SET_PAGE?.(P);`,
5362
+ `}`
5363
+ ].join("");
5364
+ const { default: esbuild2 } = await import("esbuild");
5365
+ const result = await esbuild2.build({
5366
+ stdin: { contents: code, loader: "tsx", resolveDir: dirname2(absEntry) },
5367
+ bundle: true,
5368
+ format: "esm",
5369
+ jsx: "automatic",
5370
+ jsxImportSource: "react",
5371
+ banner: { js: "self.process={env:{}};" },
5372
+ loader: { ".node": "empty" },
5373
+ write: false,
5374
+ minify: true
5375
+ });
5376
+ return result.outputFiles[0].contents;
5377
+ } catch (err) {
5378
+ console.error("hydration bundle failed:", err);
5379
+ return null;
5380
+ }
5381
+ }
5382
+ var bundleRegistry = /* @__PURE__ */ new Map();
5383
+ function serializeLoaderData(ctx) {
5384
+ const data = {};
5385
+ for (const key of Object.keys(ctx)) {
5386
+ if (!["params", "query", "mountPath", "layoutStack"].includes(key)) {
5387
+ data[key] = ctx[key];
5388
+ }
5389
+ }
5390
+ return data;
5391
+ }
5392
+ function ssr(path2) {
5393
+ const entryId = id2(resolve4(path2));
5394
+ const bundleKey = `/__ssr/${entryId}.js`;
5395
+ let bundleBuilt = false;
5396
+ return async (req, ctx) => {
5397
+ const pageMod = await compileTsx(path2);
5398
+ const Component = pageMod.default;
5399
+ if (!Component) return new Response("", { status: 500 });
5400
+ const layouts = ctx.layoutStack || [];
5401
+ const layoutComponents = layouts.map((l) => l.component);
5402
+ const layoutPaths = layouts.map((l) => l.path);
5403
+ const base = (ctx.mountPath || "").replace(/\/$/, "");
5404
+ const loaderData = serializeLoaderData(ctx);
5405
+ const ctxValue = {
5406
+ params: ctx.params,
5407
+ query: ctx.query,
5408
+ user: ctx.user ?? {},
5409
+ parsed: ctx.parsed ?? {},
5410
+ prefs: ctx.prefs ?? {},
5411
+ loaderData,
5412
+ env: ctx.env ?? {}
5413
+ };
5414
+ return als.run(ctxValue, async () => {
5415
+ setCtx(ctxValue);
5416
+ let element = createElement(
5417
+ TsxContext.Provider,
5418
+ { value: ctxValue },
5419
+ createElement(
5420
+ "div",
5421
+ { id: "__weifuwu_root" },
5422
+ createElement(Component, null)
5423
+ )
5424
+ );
5425
+ if (layoutComponents.length === 0) {
5426
+ element = createElement(
5427
+ "html",
5428
+ { lang: "en" },
5429
+ createElement(
5430
+ "head",
5431
+ null,
5432
+ createElement("meta", { charSet: "utf-8" }),
5433
+ createElement("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
5434
+ createElement("title", null, "weifuwu")
5435
+ ),
5436
+ createElement("body", null, element)
5437
+ );
5438
+ } else {
5439
+ for (const L of layoutComponents.toReversed()) {
5440
+ element = createElement(L, { children: element });
5441
+ }
5442
+ }
5443
+ let bundle = null;
5444
+ if (!bundleBuilt) {
5445
+ const buf = await buildClientBundle(path2, layoutPaths);
5446
+ if (buf) {
5447
+ bundleRegistry.set(bundleKey, buf);
5448
+ bundleBuilt = true;
5449
+ }
5450
+ }
5451
+ if (bundleRegistry.has(bundleKey)) {
5452
+ bundle = { url: bundleKey };
5453
+ }
5454
+ const { renderToReadableStream } = await import("react-dom/server");
5455
+ const stream = await renderToReadableStream(element);
5456
+ return streamResponse(stream, {
5457
+ ctx,
5458
+ base,
5459
+ isDev,
5460
+ bundle,
5461
+ loaderData
5462
+ });
5463
+ });
5464
+ };
5465
+ }
5466
+
5467
+ // ssr/layout.ts
5468
+ function layout(path2) {
5469
+ return async (req, ctx, next) => {
5470
+ const mod = await compileTsx(path2);
5471
+ const Component = mod.default;
5472
+ if (!Component) throw new Error(`Layout ${path2} has no default export`);
5473
+ ctx.layoutStack = [...ctx.layoutStack || [], { path: path2, component: Component }];
5474
+ return next(req, ctx);
5475
+ };
5476
+ }
5477
+
5478
+ // ssr/tailwind.ts
5479
+ var isDev2 = process.env.NODE_ENV !== "production";
5480
+
5481
+ // ssr/error-boundary.ts
5482
+ import { createElement as createElement2 } from "react";
5483
+
5484
+ // ssr/live.ts
5485
+ import chokidar from "chokidar";
5486
+
5975
5487
  // opencode/session.ts
5976
5488
  import { randomUUID as randomUUID2 } from "node:crypto";
5977
5489
  import { join as join3 } from "node:path";
@@ -5999,12 +5511,12 @@ var messages = pgTable("_opencode_messages", {
5999
5511
  created_at: timestamptz("created_at")
6000
5512
  });
6001
5513
  async function createSession(sql2, opts, cwd, mountPath) {
6002
- const id2 = randomUUID2();
6003
- const ws = computeSessionWorkspace(cwd, mountPath, id2);
5514
+ const id3 = randomUUID2();
5515
+ const ws = computeSessionWorkspace(cwd, mountPath, id3);
6004
5516
  await mkdir2(ws, { recursive: true });
6005
5517
  const [row] = await sql2`
6006
5518
  INSERT INTO "_opencode_sessions" ("id", "user_id", "title", "model", "workspace", "system_prompt")
6007
- VALUES (${id2}, ${opts.userId ?? 0}, ${opts.title ?? null}, ${opts.model ?? "deepseek-v4-flash"}, ${ws}, ${opts.systemPrompt ?? null})
5519
+ VALUES (${id3}, ${opts.userId ?? 0}, ${opts.title ?? null}, ${opts.model ?? "deepseek-v4-flash"}, ${ws}, ${opts.systemPrompt ?? null})
6008
5520
  RETURNING *
6009
5521
  `;
6010
5522
  return row;
@@ -6013,8 +5525,8 @@ function computeSessionWorkspace(cwd, mountPath, sessionId) {
6013
5525
  const name = !mountPath || mountPath === "/" ? "default" : mountPath.replace(/^\//, "");
6014
5526
  return join3(cwd, ".sessions", name, sessionId);
6015
5527
  }
6016
- async function getSession(sql2, id2) {
6017
- const { data: rows } = await sessions.readMany(sql2, { id: id2, active: true });
5528
+ async function getSession(sql2, id3) {
5529
+ const { data: rows } = await sessions.readMany(sql2, { id: id3, active: true });
6018
5530
  return rows[0] ?? null;
6019
5531
  }
6020
5532
  async function listSessions(sql2, userId) {
@@ -6026,8 +5538,8 @@ async function listSessions(sql2, userId) {
6026
5538
  const { data: rows } = await sessions.readMany(sql2, { active: true }, opts);
6027
5539
  return rows;
6028
5540
  }
6029
- async function deleteSession(sql2, id2) {
6030
- await sessions.update(sql2, id2, { active: false, updated_at: sql`NOW()` });
5541
+ async function deleteSession(sql2, id3) {
5542
+ await sessions.update(sql2, id3, { active: false, updated_at: sql`NOW()` });
6031
5543
  }
6032
5544
  async function getHistory(sql2, sessionId, limit = 50) {
6033
5545
  const { data: rows } = await messages.readMany(sql2, { session_id: sessionId }, {
@@ -6208,10 +5720,10 @@ function createBashTool(ctx) {
6208
5720
  return { stdout: "", stderr: "Command denied: potentially dangerous command", exitCode: 1 };
6209
5721
  }
6210
5722
  const cwd = workdir ? `${ctx.workspace}/${workdir}` : ctx.workspace;
6211
- return new Promise((resolve11) => {
5723
+ return new Promise((resolve12) => {
6212
5724
  const child = exec(command, { cwd, timeout: timeout * 1e3, maxBuffer: 1024 * 1024 }, (error, stdout, stderr) => {
6213
5725
  const truncated = stdout.length > 1e6 || stderr.length > 1e6;
6214
- resolve11({
5726
+ resolve12({
6215
5727
  stdout: stdout.slice(0, 1e6),
6216
5728
  stderr: stderr.slice(0, 1e6),
6217
5729
  exitCode: error?.code ?? 0,
@@ -6228,7 +5740,7 @@ function createBashTool(ctx) {
6228
5740
  import { tool as tool4 } from "ai";
6229
5741
  import { z as z6 } from "zod";
6230
5742
  import { readFileSync as readFileSync3 } from "node:fs";
6231
- import { resolve as resolve4 } from "node:path";
5743
+ import { resolve as resolve5 } from "node:path";
6232
5744
  function createReadTool(ctx) {
6233
5745
  return tool4({
6234
5746
  description: "Read file contents. Supports offset and limit for reading specific line ranges.",
@@ -6238,7 +5750,7 @@ function createReadTool(ctx) {
6238
5750
  limit: z6.number().optional().describe("Number of lines to read")
6239
5751
  }),
6240
5752
  execute: async ({ path: path2, offset, limit }) => {
6241
- const resolved = resolve4(ctx.workspace, path2);
5753
+ const resolved = resolve5(ctx.workspace, path2);
6242
5754
  if (!isPathAllowed(resolved, ctx.workspace, ctx.permissions)) {
6243
5755
  return { error: "Path not allowed", content: null, totalLines: 0 };
6244
5756
  }
@@ -6269,8 +5781,8 @@ function createReadTool(ctx) {
6269
5781
  // opencode/tools/write.ts
6270
5782
  import { tool as tool5 } from "ai";
6271
5783
  import { z as z7 } from "zod";
6272
- import { writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "node:fs";
6273
- import { resolve as resolve5, dirname as dirname2 } from "node:path";
5784
+ import { writeFileSync, mkdirSync as mkdirSync2 } from "node:fs";
5785
+ import { resolve as resolve6, dirname as dirname3 } from "node:path";
6274
5786
  function createWriteTool(ctx) {
6275
5787
  return tool5({
6276
5788
  description: "Create or overwrite a file. Parent directories are created automatically.",
@@ -6279,12 +5791,12 @@ function createWriteTool(ctx) {
6279
5791
  content: z7.string().describe("File content")
6280
5792
  }),
6281
5793
  execute: async ({ path: path2, content }) => {
6282
- const resolved = resolve5(ctx.workspace, path2);
5794
+ const resolved = resolve6(ctx.workspace, path2);
6283
5795
  if (!isPathAllowed(resolved, ctx.workspace, ctx.permissions)) {
6284
5796
  return { error: "Path not allowed" };
6285
5797
  }
6286
- mkdirSync2(dirname2(resolved), { recursive: true });
6287
- writeFileSync2(resolved, content, "utf-8");
5798
+ mkdirSync2(dirname3(resolved), { recursive: true });
5799
+ writeFileSync(resolved, content, "utf-8");
6288
5800
  return { path: path2, size: content.length };
6289
5801
  }
6290
5802
  });
@@ -6293,8 +5805,8 @@ function createWriteTool(ctx) {
6293
5805
  // opencode/tools/edit.ts
6294
5806
  import { tool as tool6 } from "ai";
6295
5807
  import { z as z8 } from "zod";
6296
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
6297
- import { resolve as resolve6 } from "node:path";
5808
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
5809
+ import { resolve as resolve7 } from "node:path";
6298
5810
  function createEditTool(ctx) {
6299
5811
  return tool6({
6300
5812
  description: "Perform exact string replacements in a file. If oldString appears multiple times, provide more surrounding context.",
@@ -6305,7 +5817,7 @@ function createEditTool(ctx) {
6305
5817
  replaceAll: z8.boolean().default(false).describe("Replace all occurrences")
6306
5818
  }),
6307
5819
  execute: async ({ path: path2, oldString, newString, replaceAll }) => {
6308
- const resolved = resolve6(ctx.workspace, path2);
5820
+ const resolved = resolve7(ctx.workspace, path2);
6309
5821
  if (!isPathAllowed(resolved, ctx.workspace, ctx.permissions)) {
6310
5822
  return { error: "Path not allowed" };
6311
5823
  }
@@ -6316,7 +5828,7 @@ function createEditTool(ctx) {
6316
5828
  }
6317
5829
  const count = content.split(oldString).length - 1;
6318
5830
  const result2 = content.replaceAll(oldString, newString);
6319
- writeFileSync3(resolved, result2, "utf-8");
5831
+ writeFileSync2(resolved, result2, "utf-8");
6320
5832
  return { path: path2, replaced: count };
6321
5833
  }
6322
5834
  const firstIdx = content.indexOf(oldString);
@@ -6328,7 +5840,7 @@ function createEditTool(ctx) {
6328
5840
  return { error: "Found multiple matches. Provide more surrounding context in oldString.", replaced: 0 };
6329
5841
  }
6330
5842
  const result = content.replace(oldString, newString);
6331
- writeFileSync3(resolved, result, "utf-8");
5843
+ writeFileSync2(resolved, result, "utf-8");
6332
5844
  return { path: path2, replaced: 1 };
6333
5845
  }
6334
5846
  });
@@ -6338,7 +5850,7 @@ function createEditTool(ctx) {
6338
5850
  import { tool as tool7 } from "ai";
6339
5851
  import { z as z9 } from "zod";
6340
5852
  import { execFileSync } from "node:child_process";
6341
- import { resolve as resolve7 } from "node:path";
5853
+ import { resolve as resolve8 } from "node:path";
6342
5854
  import { existsSync as existsSync3 } from "node:fs";
6343
5855
  function createGrepTool(ctx) {
6344
5856
  return tool7({
@@ -6350,7 +5862,7 @@ function createGrepTool(ctx) {
6350
5862
  context: z9.number().default(0).describe("Number of context lines before and after each match")
6351
5863
  }),
6352
5864
  execute: async ({ pattern, include, path: path2, context }) => {
6353
- const searchDir = path2 ? resolve7(ctx.workspace, path2) : ctx.workspace;
5865
+ const searchDir = path2 ? resolve8(ctx.workspace, path2) : ctx.workspace;
6354
5866
  try {
6355
5867
  let stdout;
6356
5868
  if (existsSync3("/usr/bin/rg") || existsSync3("/usr/local/bin/rg")) {
@@ -6382,7 +5894,7 @@ function createGrepTool(ctx) {
6382
5894
  import { tool as tool8 } from "ai";
6383
5895
  import { z as z10 } from "zod";
6384
5896
  import { execFileSync as execFileSync2 } from "node:child_process";
6385
- import { resolve as resolve8 } from "node:path";
5897
+ import { resolve as resolve9 } from "node:path";
6386
5898
  function createGlobTool(ctx) {
6387
5899
  return tool8({
6388
5900
  description: "Find files matching a glob pattern.",
@@ -6391,7 +5903,7 @@ function createGlobTool(ctx) {
6391
5903
  path: z10.string().optional().describe("Subdirectory relative to workspace")
6392
5904
  }),
6393
5905
  execute: async ({ pattern, path: path2 }) => {
6394
- const searchDir = path2 ? resolve8(ctx.workspace, path2) : ctx.workspace;
5906
+ const searchDir = path2 ? resolve9(ctx.workspace, path2) : ctx.workspace;
6395
5907
  try {
6396
5908
  const stdout = execFileSync2("find", [
6397
5909
  searchDir,
@@ -6452,7 +5964,7 @@ function createQuestionTool(ctx) {
6452
5964
  options: z12.array(z12.string()).optional().describe("Optional multiple choice options")
6453
5965
  }),
6454
5966
  execute: async ({ question, options }, { toolCallId }) => {
6455
- return new Promise((resolve11, reject) => {
5967
+ return new Promise((resolve12, reject) => {
6456
5968
  const timeout = setTimeout(() => {
6457
5969
  ctx.pendingQuestions.delete(toolCallId);
6458
5970
  reject(new Error("Question timed out"));
@@ -6460,7 +5972,7 @@ function createQuestionTool(ctx) {
6460
5972
  ctx.pendingQuestions.set(toolCallId, {
6461
5973
  resolve: (answer) => {
6462
5974
  clearTimeout(timeout);
6463
- resolve11(answer);
5975
+ resolve12(answer);
6464
5976
  },
6465
5977
  reject: (err) => {
6466
5978
  clearTimeout(timeout);
@@ -6561,15 +6073,15 @@ async function buildRouter4(deps) {
6561
6073
  return Response.json(sessions2);
6562
6074
  });
6563
6075
  router.get("/sessions/:id", async (_req, ctx) => {
6564
- const id2 = ctx.params.id;
6565
- const session = await getSession(sql2, id2);
6076
+ const id3 = ctx.params.id;
6077
+ const session = await getSession(sql2, id3);
6566
6078
  if (!session) return new Response("Not Found", { status: 404 });
6567
- const messages2 = await getHistory(sql2, id2);
6079
+ const messages2 = await getHistory(sql2, id3);
6568
6080
  return Response.json({ session, messages: messages2 });
6569
6081
  });
6570
6082
  router.delete("/sessions/:id", async (_req, ctx) => {
6571
- const id2 = ctx.params.id;
6572
- await deleteSession(sql2, id2);
6083
+ const id3 = ctx.params.id;
6084
+ await deleteSession(sql2, id3);
6573
6085
  return new Response(null, { status: 204 });
6574
6086
  });
6575
6087
  router.post("/sessions/:id/message", async (req, ctx) => {
@@ -6607,8 +6119,8 @@ async function buildRouter4(deps) {
6607
6119
  });
6608
6120
  try {
6609
6121
  const uiDir = new URL("../opencode/ui/", import.meta.url).pathname;
6610
- const uiRouter = await tsx({ dir: uiDir });
6611
- router.use("/", uiRouter);
6122
+ router.use(layout(join4(uiDir, "layout.tsx")));
6123
+ router.get("/", ssr(join4(uiDir, "page.tsx")));
6612
6124
  } catch (e) {
6613
6125
  console.warn("[opencode] UI not available:", e);
6614
6126
  }
@@ -6743,7 +6255,7 @@ function createWSHandler2(deps) {
6743
6255
  import { readFile } from "node:fs/promises";
6744
6256
  import { glob } from "node:fs/promises";
6745
6257
  import { homedir } from "node:os";
6746
- import { resolve as resolve9 } from "node:path";
6258
+ import { resolve as resolve10 } from "node:path";
6747
6259
  import { parse as parseYaml } from "yaml";
6748
6260
  var SEARCH_DIRS = [
6749
6261
  (ws) => `${ws}/.opencode/skills`,
@@ -6781,7 +6293,7 @@ async function scanDir(dir) {
6781
6293
  try {
6782
6294
  const files = [];
6783
6295
  for await (const entry of glob("*/SKILL.md", { cwd: dir })) {
6784
- const skill = await parseSkillFile(resolve9(dir, entry));
6296
+ const skill = await parseSkillFile(resolve10(dir, entry));
6785
6297
  if (skill) files.push(skill);
6786
6298
  }
6787
6299
  return files;
@@ -6838,47 +6350,47 @@ async function opencode(options) {
6838
6350
  const model = provider.chat(modelName);
6839
6351
  const pendingQuestions = /* @__PURE__ */ new Map();
6840
6352
  const base = new PgModule(pg);
6841
- return {
6842
- migrate: async () => {
6843
- const sessions2 = pgTable("_opencode_sessions", {
6844
- id: uuid("id").default(sql`gen_random_uuid()`).primaryKey(),
6845
- tenant_id: text("tenant_id"),
6846
- user_id: integer("user_id").default(0),
6847
- title: text("title"),
6848
- agent_type: text("agent_type").default("build"),
6849
- model: text("model").default("deepseek-v4-flash"),
6850
- system_prompt: text("system_prompt"),
6851
- workspace: text("workspace"),
6852
- metadata: jsonb("metadata").default(sql`'{}'::jsonb`),
6853
- active: boolean_("active").default(true),
6854
- created_at: timestamptz("created_at").default(sql`NOW()`),
6855
- updated_at: timestamptz("updated_at").default(sql`NOW()`)
6856
- });
6857
- await sessions2.create(sql2);
6858
- await sessions2.createIndex(sql2, "user_id");
6859
- const messages2 = pgTable("_opencode_messages", {
6860
- id: serial("id").primaryKey(),
6861
- session_id: uuid("session_id").notNull().references("_opencode_sessions", "id", "cascade"),
6862
- role: text("role").notNull(),
6863
- content: text("content"),
6864
- tool_calls: jsonb("tool_calls"),
6865
- tool_results: jsonb("tool_results"),
6866
- tokens_in: integer("tokens_in").default(0),
6867
- tokens_out: integer("tokens_out").default(0),
6868
- created_at: timestamptz("created_at").default(sql`NOW()`)
6869
- });
6870
- await messages2.create(sql2);
6871
- await messages2.createIndex(sql2, ["session_id", "created_at"]);
6872
- },
6873
- router: () => buildRouter4({ sql: sql2, model, workspace, systemPrompt, skills: manualSkills, skillsRegistry, permissions, pendingQuestions }),
6874
- wsHandler: () => createWSHandler2({ sql: sql2, model, workspace, systemPrompt, skills: manualSkills, skillsRegistry, permissions, pendingQuestions }),
6875
- close: () => base.close()
6353
+ const r = await buildRouter4({ sql: sql2, model, workspace, systemPrompt, skills: manualSkills, skillsRegistry, permissions, pendingQuestions });
6354
+ const mod = r;
6355
+ mod.migrate = async () => {
6356
+ const sessions2 = pgTable("_opencode_sessions", {
6357
+ id: uuid("id").default(sql`gen_random_uuid()`).primaryKey(),
6358
+ tenant_id: text("tenant_id"),
6359
+ user_id: integer("user_id").default(0),
6360
+ title: text("title"),
6361
+ agent_type: text("agent_type").default("build"),
6362
+ model: text("model").default("deepseek-v4-flash"),
6363
+ system_prompt: text("system_prompt"),
6364
+ workspace: text("workspace"),
6365
+ metadata: jsonb("metadata").default(sql`'{}'::jsonb`),
6366
+ active: boolean_("active").default(true),
6367
+ created_at: timestamptz("created_at").default(sql`NOW()`),
6368
+ updated_at: timestamptz("updated_at").default(sql`NOW()`)
6369
+ });
6370
+ await sessions2.create(sql2);
6371
+ await sessions2.createIndex(sql2, "user_id");
6372
+ const messages2 = pgTable("_opencode_messages", {
6373
+ id: serial("id").primaryKey(),
6374
+ session_id: uuid("session_id").notNull().references("_opencode_sessions", "id", "cascade"),
6375
+ role: text("role").notNull(),
6376
+ content: text("content"),
6377
+ tool_calls: jsonb("tool_calls"),
6378
+ tool_results: jsonb("tool_results"),
6379
+ tokens_in: integer("tokens_in").default(0),
6380
+ tokens_out: integer("tokens_out").default(0),
6381
+ created_at: timestamptz("created_at").default(sql`NOW()`)
6382
+ });
6383
+ await messages2.create(sql2);
6384
+ await messages2.createIndex(sql2, ["session_id", "created_at"]);
6876
6385
  };
6386
+ mod.wsHandler = () => createWSHandler2({ sql: sql2, model, workspace, systemPrompt, skills: manualSkills, skillsRegistry, permissions, pendingQuestions });
6387
+ mod.close = () => base.close();
6388
+ return mod;
6877
6389
  }
6878
6390
 
6879
6391
  // health.ts
6880
6392
  function health(options) {
6881
- const path2 = options?.path ?? "/health";
6393
+ const path2 = options?.path ?? "/";
6882
6394
  const r = new Router();
6883
6395
  const handler = async () => {
6884
6396
  try {
@@ -7099,24 +6611,25 @@ function analytics(options) {
7099
6611
  headers: { "content-type": "text/html; charset=utf-8" }
7100
6612
  });
7101
6613
  };
7102
- const router = () => {
7103
- const r = new Router();
7104
- r.get("/__analytics/data", handler);
7105
- r.get("/__analytics", handler);
7106
- return r;
7107
- };
6614
+ const r = new Router();
6615
+ r.get("/__analytics/data", handler);
6616
+ r.get("/__analytics", handler);
7108
6617
  const migrate = async () => {
7109
6618
  if (pg) await migratePg(pg.sql, pg.table);
7110
6619
  };
7111
6620
  const close = async () => {
7112
6621
  };
7113
- return { middleware, router, migrate, close };
6622
+ const mod = r;
6623
+ mod.middleware = middleware;
6624
+ mod.migrate = migrate;
6625
+ mod.close = close;
6626
+ return mod;
7114
6627
  }
7115
6628
 
7116
6629
  // preferences.ts
7117
6630
  import { readFile as readFile2 } from "node:fs/promises";
7118
6631
  import { existsSync as existsSync4 } from "node:fs";
7119
- import { join as join4, resolve as resolve10 } from "node:path";
6632
+ import { join as join5, resolve as resolve11 } from "node:path";
7120
6633
  var defaults = {
7121
6634
  locale: { default: "en", cookie: "locale", fromAcceptLanguage: true },
7122
6635
  theme: { default: "system", cookie: "theme" }
@@ -7165,24 +6678,24 @@ async function handlePrefSwitch(req, value, cookieName, load) {
7165
6678
  });
7166
6679
  }
7167
6680
  function preferences(options) {
7168
- const dir = options.dir ? resolve10(options.dir) : void 0;
6681
+ const dir = options.dir ? resolve11(options.dir) : void 0;
7169
6682
  const localeOpts = { ...defaults.locale, ...options.locale };
7170
6683
  const themeOpts = { ...defaults.theme, ...options.theme };
7171
- const cache = /* @__PURE__ */ new Map();
6684
+ const cache2 = /* @__PURE__ */ new Map();
7172
6685
  function validLocale(locale) {
7173
6686
  return /^[\w-]+$/.test(locale) && !locale.includes("..");
7174
6687
  }
7175
6688
  async function load(locale) {
7176
6689
  if (!dir) return {};
7177
6690
  if (!validLocale(locale)) return {};
7178
- const cached = cache.get(locale);
6691
+ const cached = cache2.get(locale);
7179
6692
  if (cached) return cached;
7180
- const filePath = join4(dir, `${locale}.json`);
6693
+ const filePath = join5(dir, `${locale}.json`);
7181
6694
  if (!existsSync4(filePath)) return {};
7182
6695
  try {
7183
6696
  const content = await readFile2(filePath, "utf-8");
7184
6697
  const data = JSON.parse(content);
7185
- cache.set(locale, data);
6698
+ cache2.set(locale, data);
7186
6699
  return data;
7187
6700
  } catch {
7188
6701
  return {};
@@ -7529,9 +7042,9 @@ function listHandler(entries) {
7529
7042
  }
7530
7043
  function getHandler(entries) {
7531
7044
  return async (_req, ctx) => {
7532
- const id2 = ctx.params?.id;
7533
- if (!id2) return Response.json({ error: "id is required" }, { status: 400 });
7534
- const row = await entries.read(parseInt(id2));
7045
+ const id3 = ctx.params?.id;
7046
+ if (!id3) return Response.json({ error: "id is required" }, { status: 400 });
7047
+ const row = await entries.read(parseInt(id3));
7535
7048
  if (!row) return Response.json({ error: "not found" }, { status: 404 });
7536
7049
  return Response.json(parseMetadata(row));
7537
7050
  };
@@ -7585,11 +7098,11 @@ function logdb(options) {
7585
7098
  return row;
7586
7099
  }
7587
7100
  function router() {
7588
- const r = new Router();
7589
- r.post("/", createHandler(entries));
7590
- r.get("/", listHandler(entries));
7591
- r.get("/:id", getHandler(entries));
7592
- return r;
7101
+ const r2 = new Router();
7102
+ r2.post("/", createHandler(entries));
7103
+ r2.get("/", listHandler(entries));
7104
+ r2.get("/:id", getHandler(entries));
7105
+ return r2;
7593
7106
  }
7594
7107
  async function clean(retentionMonths) {
7595
7108
  const cutoff = /* @__PURE__ */ new Date();
@@ -7611,21 +7124,22 @@ function logdb(options) {
7611
7124
  }
7612
7125
  return dropped;
7613
7126
  }
7614
- return {
7615
- log,
7616
- router,
7617
- migrate: async () => {
7618
- await entries.create({ partitionBy: partitionBy("range", "created_at") });
7619
- await entries.createIndex(["created_at", "id"]);
7620
- await entries.createIndex(["level"]);
7621
- await entries.createIndex(["source"]);
7622
- await entries.createIndex(["level", "created_at"]);
7623
- await ensurePartitions(sql2, tableName);
7624
- },
7625
- clean,
7626
- close: async () => {
7627
- }
7127
+ async function migrate() {
7128
+ await entries.create({ partitionBy: partitionBy("range", "created_at") });
7129
+ await entries.createIndex(["created_at", "id"]);
7130
+ await entries.createIndex(["level"]);
7131
+ await entries.createIndex(["source"]);
7132
+ await entries.createIndex(["level", "created_at"]);
7133
+ await ensurePartitions(sql2, tableName);
7134
+ }
7135
+ const r = router();
7136
+ const mod = r;
7137
+ mod.log = log;
7138
+ mod.migrate = migrate;
7139
+ mod.clean = clean;
7140
+ mod.close = async () => {
7628
7141
  };
7142
+ return mod;
7629
7143
  }
7630
7144
 
7631
7145
  // iii/client.ts
@@ -7748,8 +7262,8 @@ function createMemoryStore(channels) {
7748
7262
  }));
7749
7263
  return { streams, count: streams.length };
7750
7264
  },
7751
- async send(stream, group, type, data, id2) {
7752
- notify(channels, stream, group, id2 ?? "", "send", { type, data });
7265
+ async send(stream, group, type, data, id3) {
7266
+ notify(channels, stream, group, id3 ?? "", "send", { type, data });
7753
7267
  },
7754
7268
  async update(stream, group, item, ops) {
7755
7269
  const k = key(stream, group, item);
@@ -7829,8 +7343,8 @@ function createPgStore(channels, pg) {
7829
7343
  }));
7830
7344
  return { streams, count: streams.length };
7831
7345
  },
7832
- async send(stream, group, type, data, id2) {
7833
- notify(channels, stream, group, id2 ?? "", "send", { type, data });
7346
+ async send(stream, group, type, data, id3) {
7347
+ notify(channels, stream, group, id3 ?? "", "send", { type, data });
7834
7348
  },
7835
7349
  async update(stream, group, item, ops) {
7836
7350
  const { value: oldVal } = await this.get(stream, group, item);
@@ -7926,8 +7440,8 @@ function createRedisStore(channels, redis2, ttl) {
7926
7440
  }));
7927
7441
  return { streams, count: streams.length };
7928
7442
  },
7929
- async send(stream, group, type, data, id2) {
7930
- notify(channels, stream, group, id2 ?? "", "send", { type, data });
7443
+ async send(stream, group, type, data, id3) {
7444
+ notify(channels, stream, group, id3 ?? "", "send", { type, data });
7931
7445
  },
7932
7446
  async update(stream, group, item, ops) {
7933
7447
  const hk = hashKey(stream, group);
@@ -8140,9 +7654,9 @@ function iii(opts = {}) {
8140
7654
  const functions = /* @__PURE__ */ new Map();
8141
7655
  const triggers = /* @__PURE__ */ new Map();
8142
7656
  const pending = /* @__PURE__ */ new Map();
8143
- function registerBuiltin(id2, handler) {
8144
- functions.set(id2, {
8145
- id: id2,
7657
+ function registerBuiltin(id3, handler) {
7658
+ functions.set(id3, {
7659
+ id: id3,
8146
7660
  handler,
8147
7661
  workerId: "__iii__",
8148
7662
  workerName: "__iii__",
@@ -8197,34 +7711,34 @@ function iii(opts = {}) {
8197
7711
  }
8198
7712
  workers.set(workerId, reg);
8199
7713
  }
8200
- function addRemoteFunction(workerId, id2) {
7714
+ function addRemoteFunction(workerId, id3) {
8201
7715
  const worker = workers.get(workerId);
8202
7716
  if (!worker) return;
8203
7717
  const handler = async (payload) => {
8204
7718
  if (!worker.ws) throw new Error(`Worker "${worker.name}" disconnected`);
8205
7719
  const invocationId = crypto6.randomUUID();
8206
- return new Promise((resolve11, reject) => {
7720
+ return new Promise((resolve12, reject) => {
8207
7721
  const timer = setTimeout(() => {
8208
7722
  pending.delete(invocationId);
8209
- reject(new Error(`Invocation timed out for "${id2}"`));
7723
+ reject(new Error(`Invocation timed out for "${id3}"`));
8210
7724
  }, 3e4);
8211
- pending.set(invocationId, { resolve: resolve11, reject, timer });
7725
+ pending.set(invocationId, { resolve: resolve12, reject, timer });
8212
7726
  worker.ws.send(JSON.stringify({
8213
7727
  type: "invoke",
8214
7728
  invocation_id: invocationId,
8215
- function_id: id2,
7729
+ function_id: id3,
8216
7730
  payload
8217
7731
  }));
8218
7732
  });
8219
7733
  };
8220
7734
  const fnReg = {
8221
- id: id2,
7735
+ id: id3,
8222
7736
  handler,
8223
7737
  workerId,
8224
7738
  workerName: worker.name,
8225
7739
  triggers: []
8226
7740
  };
8227
- functions.set(id2, fnReg);
7741
+ functions.set(id3, fnReg);
8228
7742
  worker.functions.push(fnReg);
8229
7743
  }
8230
7744
  function removeWorker(workerId) {
@@ -8234,17 +7748,18 @@ function iii(opts = {}) {
8234
7748
  for (const t of reg.triggers) triggers.delete(t.id);
8235
7749
  workers.delete(workerId);
8236
7750
  }
7751
+ let engineRef = null;
8237
7752
  const wsHandler = createWsHandler({
8238
7753
  registerRemoteWorker(ws, name) {
8239
- const id2 = crypto6.randomUUID();
8240
- workers.set(id2, { id: id2, name, ws, functions: [], triggers: [] });
8241
- return id2;
7754
+ const id3 = crypto6.randomUUID();
7755
+ workers.set(id3, { id: id3, name, ws, functions: [], triggers: [] });
7756
+ return id3;
8242
7757
  },
8243
7758
  unregisterRemoteWorker(workerId) {
8244
7759
  removeWorker(workerId);
8245
7760
  },
8246
- registerRemoteFunction(workerId, id2) {
8247
- addRemoteFunction(workerId, id2);
7761
+ registerRemoteFunction(workerId, id3) {
7762
+ addRemoteFunction(workerId, id3);
8248
7763
  },
8249
7764
  registerRemoteTrigger(workerId, input) {
8250
7765
  const tid = crypto6.randomUUID();
@@ -8253,11 +7768,11 @@ function iii(opts = {}) {
8253
7768
  const worker = workers.get(workerId);
8254
7769
  if (worker) worker.triggers.push(reg);
8255
7770
  },
8256
- unregisterRemoteFunction(workerId, id2) {
8257
- functions.delete(id2);
7771
+ unregisterRemoteFunction(workerId, id3) {
7772
+ functions.delete(id3);
8258
7773
  const worker = workers.get(workerId);
8259
7774
  if (worker) {
8260
- worker.functions = worker.functions.filter((f) => f.id !== id2);
7775
+ worker.functions = worker.functions.filter((f) => f.id !== id3);
8261
7776
  }
8262
7777
  },
8263
7778
  unregisterRemoteTrigger(workerId, functionId) {
@@ -8304,7 +7819,7 @@ function iii(opts = {}) {
8304
7819
  }));
8305
7820
  return;
8306
7821
  }
8307
- const ctx = { engine: module, functionId, workerName: fn.workerName };
7822
+ const ctx = { engine: engineRef, functionId, workerName: fn.workerName };
8308
7823
  Promise.resolve(fn.handler(payload, ctx)).then((result) => {
8309
7824
  ws.send(JSON.stringify({ type: "invoke_result", invocation_id: invocationId, result }));
8310
7825
  }).catch((err) => {
@@ -8312,70 +7827,78 @@ function iii(opts = {}) {
8312
7827
  });
8313
7828
  }
8314
7829
  });
8315
- const module = {
8316
- router: () => {
8317
- const r = buildRouter5(module, wsHandler);
8318
- module.router = () => r;
8319
- return r;
8320
- },
8321
- wsHandler: () => wsHandler,
8322
- addWorker: addLocalWorker,
8323
- removeWorker: (worker) => {
8324
- for (const [wid, reg] of workers) {
8325
- if (reg.name === worker.name) {
8326
- removeWorker(wid);
8327
- return;
8328
- }
7830
+ function removeWorkerByName(worker) {
7831
+ for (const [wid, reg] of workers) {
7832
+ if (reg.name === worker.name) {
7833
+ removeWorker(wid);
7834
+ return;
8329
7835
  }
8330
- },
8331
- trigger(request) {
8332
- const fn = functions.get(request.function_id);
8333
- if (!fn) throw new Error(`Function "${request.function_id}" not found`);
8334
- const ctx = { engine: module, functionId: request.function_id, workerName: fn.workerName };
8335
- if (request.action === "void") {
8336
- queueMicrotask(() => fn.handler(request.payload, ctx));
8337
- return Promise.resolve(void 0);
8338
- }
8339
- return Promise.resolve(fn.handler(request.payload, ctx));
8340
- },
8341
- listWorkers: () => Array.from(workers.values()).map((w) => ({
7836
+ }
7837
+ }
7838
+ function trigger(request) {
7839
+ const fn = functions.get(request.function_id);
7840
+ if (!fn) throw new Error(`Function "${request.function_id}" not found`);
7841
+ const ctx = { engine: engineRef, functionId: request.function_id, workerName: fn.workerName };
7842
+ if (request.action === "void") {
7843
+ queueMicrotask(() => fn.handler(request.payload, ctx));
7844
+ return Promise.resolve(void 0);
7845
+ }
7846
+ return Promise.resolve(fn.handler(request.payload, ctx));
7847
+ }
7848
+ function listWorkers() {
7849
+ return Array.from(workers.values()).map((w) => ({
8342
7850
  id: w.id,
8343
7851
  name: w.name,
8344
7852
  status: "connected",
8345
7853
  connectedAt: Date.now(),
8346
7854
  functionCount: w.functions.length,
8347
7855
  triggerCount: w.triggers.length
8348
- })),
8349
- listFunctions: () => Array.from(functions.values()).map((f) => ({
7856
+ }));
7857
+ }
7858
+ function listFunctions() {
7859
+ return Array.from(functions.values()).map((f) => ({
8350
7860
  id: f.id,
8351
7861
  workerId: f.workerId,
8352
7862
  workerName: f.workerName,
8353
7863
  triggers: f.triggers
8354
- })),
8355
- listTriggers: () => Array.from(triggers.values()).map((t) => ({
7864
+ }));
7865
+ }
7866
+ function listTriggers() {
7867
+ return Array.from(triggers.values()).map((t) => ({
8356
7868
  id: t.id,
8357
7869
  type: t.type,
8358
7870
  function_id: t.function_id,
8359
7871
  config: t.config,
8360
7872
  workerId: t.workerId
8361
- })),
8362
- migrate: async () => {
8363
- await stream.migrate();
8364
- },
8365
- shutdown: async () => {
8366
- for (const [, p] of pending) {
8367
- clearTimeout(p.timer);
8368
- p.reject(new Error("Engine shutting down"));
8369
- }
8370
- pending.clear();
8371
- for (const [, reg] of workers) reg.ws?.close();
8372
- workers.clear();
8373
- functions.clear();
8374
- triggers.clear();
8375
- await stream.close();
8376
- }
7873
+ }));
7874
+ }
7875
+ const routerMethods = { listWorkers, listFunctions, listTriggers, trigger };
7876
+ const r = buildRouter5(routerMethods, wsHandler);
7877
+ engineRef = r;
7878
+ const mod = r;
7879
+ mod.wsHandler = () => wsHandler;
7880
+ mod.addWorker = addLocalWorker;
7881
+ mod.removeWorker = removeWorkerByName;
7882
+ mod.trigger = trigger;
7883
+ mod.listWorkers = listWorkers;
7884
+ mod.listFunctions = listFunctions;
7885
+ mod.listTriggers = listTriggers;
7886
+ mod.migrate = async () => {
7887
+ await stream.migrate();
8377
7888
  };
8378
- return module;
7889
+ mod.shutdown = async () => {
7890
+ for (const [, p] of pending) {
7891
+ clearTimeout(p.timer);
7892
+ p.reject(new Error("Engine shutting down"));
7893
+ }
7894
+ pending.clear();
7895
+ for (const [, reg] of workers) reg.ws?.close();
7896
+ workers.clear();
7897
+ functions.clear();
7898
+ triggers.clear();
7899
+ await stream.close();
7900
+ };
7901
+ return mod;
8379
7902
  }
8380
7903
 
8381
7904
  // iii/worker.ts
@@ -8384,12 +7907,12 @@ function createWorker(name) {
8384
7907
  const triggers = /* @__PURE__ */ new Map();
8385
7908
  return {
8386
7909
  name,
8387
- registerFunction(id2, handler) {
8388
- functions.set(id2, handler);
7910
+ registerFunction(id3, handler) {
7911
+ functions.set(id3, handler);
8389
7912
  return this;
8390
7913
  },
8391
- unregisterFunction(id2) {
8392
- functions.delete(id2);
7914
+ unregisterFunction(id3) {
7915
+ functions.delete(id3);
8393
7916
  return this;
8394
7917
  },
8395
7918
  registerTrigger(input) {
@@ -8401,10 +7924,10 @@ function createWorker(name) {
8401
7924
  return this;
8402
7925
  },
8403
7926
  getFunctions() {
8404
- return Array.from(functions.entries()).map(([id2, handler]) => ({ id: id2, handler }));
7927
+ return Array.from(functions.entries()).map(([id3, handler]) => ({ id: id3, handler }));
8405
7928
  },
8406
7929
  getTriggers() {
8407
- return Array.from(triggers.entries()).map(([id2, input]) => ({ id: id2, input }));
7930
+ return Array.from(triggers.entries()).map(([id3, input]) => ({ id: id3, input }));
8408
7931
  }
8409
7932
  };
8410
7933
  }
@@ -8440,8 +7963,8 @@ function registerWorker(url) {
8440
7963
  function connect() {
8441
7964
  if (intentionalClose) return;
8442
7965
  ws = new WebSocket(url);
8443
- ready = new Promise((resolve11) => {
8444
- resolveReady = resolve11;
7966
+ ready = new Promise((resolve12) => {
7967
+ resolveReady = resolve12;
8445
7968
  });
8446
7969
  ws.onopen = () => {
8447
7970
  reconnectAttempt = 0;
@@ -8524,15 +8047,15 @@ function registerWorker(url) {
8524
8047
  }
8525
8048
  connect();
8526
8049
  return {
8527
- registerFunction(id2, handler) {
8528
- handlers.set(id2, handler);
8529
- registeredFunctionIds.add(id2);
8530
- send({ type: "register_function", id: id2 });
8050
+ registerFunction(id3, handler) {
8051
+ handlers.set(id3, handler);
8052
+ registeredFunctionIds.add(id3);
8053
+ send({ type: "register_function", id: id3 });
8531
8054
  },
8532
- unregisterFunction(id2) {
8533
- handlers.delete(id2);
8534
- registeredFunctionIds.delete(id2);
8535
- send({ type: "unregister_function", id: id2 });
8055
+ unregisterFunction(id3) {
8056
+ handlers.delete(id3);
8057
+ registeredFunctionIds.delete(id3);
8058
+ send({ type: "unregister_function", id: id3 });
8536
8059
  },
8537
8060
  registerTrigger(input) {
8538
8061
  registeredTriggers.add(JSON.stringify(input));
@@ -8561,13 +8084,13 @@ function registerWorker(url) {
8561
8084
  }
8562
8085
  return Promise.resolve(fn(request.payload, ctx));
8563
8086
  }
8564
- return new Promise((resolve11, reject) => {
8087
+ return new Promise((resolve12, reject) => {
8565
8088
  const invocationId = genId();
8566
8089
  const timer = setTimeout(() => {
8567
8090
  pendingInvocations.delete(invocationId);
8568
8091
  reject(new Error(`Invocation timed out for "${request.function_id}"`));
8569
8092
  }, request.timeout_ms || 3e4);
8570
- pendingInvocations.set(invocationId, { resolve: resolve11, reject, timer });
8093
+ pendingInvocations.set(invocationId, { resolve: resolve12, reject, timer });
8571
8094
  send({
8572
8095
  type: "invoke",
8573
8096
  invocation_id: invocationId,
@@ -8642,7 +8165,6 @@ export {
8642
8165
  streamText,
8643
8166
  tenant,
8644
8167
  tool2 as tool,
8645
- tsx,
8646
8168
  upload,
8647
8169
  user,
8648
8170
  validate