weifuwu 0.18.1 → 0.18.3
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/README.md +127 -61
- package/cli.ts +7 -6
- package/dist/cli.js +7 -6
- package/dist/index.d.ts +1 -2
- package/dist/index.js +520 -1002
- package/dist/router.d.ts +10 -9
- package/dist/ssr/compile.d.ts +2 -0
- package/dist/ssr/error-boundary.d.ts +2 -0
- package/dist/ssr/index.d.ts +7 -0
- package/dist/ssr/index.js +933 -0
- package/dist/ssr/layout.d.ts +2 -0
- package/dist/ssr/live.d.ts +6 -0
- package/dist/ssr/not-found.d.ts +2 -0
- package/dist/ssr/ssr.d.ts +2 -0
- package/dist/ssr/stream.d.ts +14 -0
- package/dist/ssr/tailwind.d.ts +2 -0
- package/dist/types.d.ts +5 -0
- package/package.json +4 -3
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
|
}
|
|
@@ -304,6 +306,11 @@ var Router = class _Router {
|
|
|
304
306
|
return this;
|
|
305
307
|
}
|
|
306
308
|
route(method, path2, ...args) {
|
|
309
|
+
const last = args[args.length - 1];
|
|
310
|
+
if (last instanceof _Router) {
|
|
311
|
+
this._mountRouter(path2, last);
|
|
312
|
+
return this;
|
|
313
|
+
}
|
|
307
314
|
const handler = args.pop();
|
|
308
315
|
const middlewares = args;
|
|
309
316
|
const segments = this.splitPath(path2);
|
|
@@ -369,13 +376,13 @@ var Router = class _Router {
|
|
|
369
376
|
const mw = match.middlewares[index++];
|
|
370
377
|
return mw(innerReq, ctx2, dispatch);
|
|
371
378
|
}
|
|
372
|
-
return await new Promise((
|
|
379
|
+
return await new Promise((resolve12) => {
|
|
373
380
|
try {
|
|
374
381
|
upgradeSocket(router.wss, req, socket, head, match.handler, ctx2);
|
|
375
|
-
|
|
382
|
+
resolve12(new Response(null, { status: 101 }));
|
|
376
383
|
} catch {
|
|
377
384
|
socket.destroy();
|
|
378
|
-
|
|
385
|
+
resolve12(new Response("WebSocket upgrade failed", { status: 500 }));
|
|
379
386
|
}
|
|
380
387
|
});
|
|
381
388
|
};
|
|
@@ -561,19 +568,6 @@ function sendHttpResponseOnSocket(socket, response) {
|
|
|
561
568
|
});
|
|
562
569
|
}
|
|
563
570
|
|
|
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
571
|
// tsx-context.ts
|
|
578
572
|
import { createContext } from "react";
|
|
579
573
|
var DEFAULT_CTX = { params: {}, query: {}, parsed: {}, prefs: {}, loaderData: {}, env: {}, user: {} };
|
|
@@ -604,845 +598,6 @@ function setCtx(value) {
|
|
|
604
598
|
}
|
|
605
599
|
var TsxContext = createContext(DEFAULT_CTX);
|
|
606
600
|
|
|
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
601
|
// middleware.ts
|
|
1447
602
|
function logger(options) {
|
|
1448
603
|
return async (req, ctx, next) => {
|
|
@@ -1598,12 +753,12 @@ function auth(options) {
|
|
|
1598
753
|
}
|
|
1599
754
|
|
|
1600
755
|
// static.ts
|
|
1601
|
-
import { createHash
|
|
756
|
+
import { createHash } from "node:crypto";
|
|
1602
757
|
import { open, realpath } from "node:fs/promises";
|
|
1603
|
-
import { extname, resolve as
|
|
758
|
+
import { extname, resolve as resolve2, normalize, sep } from "node:path";
|
|
1604
759
|
import { Readable } from "node:stream";
|
|
1605
760
|
function serveStatic(root, options) {
|
|
1606
|
-
const rootDir =
|
|
761
|
+
const rootDir = resolve2(root);
|
|
1607
762
|
const opts = options ?? {};
|
|
1608
763
|
return async (req, ctx) => {
|
|
1609
764
|
const relativePath = ctx.params["*"] ?? new URL(req.url).pathname.slice(1);
|
|
@@ -1611,8 +766,8 @@ function serveStatic(root, options) {
|
|
|
1611
766
|
if (decoded.includes("..") || decoded.includes("\0")) {
|
|
1612
767
|
return new Response("Forbidden", { status: 403 });
|
|
1613
768
|
}
|
|
1614
|
-
let filePath = normalize(
|
|
1615
|
-
if (!filePath.startsWith(rootDir +
|
|
769
|
+
let filePath = normalize(resolve2(rootDir, decoded));
|
|
770
|
+
if (!filePath.startsWith(rootDir + sep) && filePath !== rootDir) {
|
|
1616
771
|
return new Response("Forbidden", { status: 403 });
|
|
1617
772
|
}
|
|
1618
773
|
let fileHandle;
|
|
@@ -1620,15 +775,15 @@ function serveStatic(root, options) {
|
|
|
1620
775
|
fileHandle = await open(filePath, "r");
|
|
1621
776
|
let stat = await fileHandle.stat();
|
|
1622
777
|
const realPath = await realpath(filePath);
|
|
1623
|
-
if (!realPath.startsWith(rootDir +
|
|
778
|
+
if (!realPath.startsWith(rootDir + sep) && realPath !== rootDir) {
|
|
1624
779
|
await fileHandle.close();
|
|
1625
780
|
return new Response("Forbidden", { status: 403 });
|
|
1626
781
|
}
|
|
1627
782
|
if (stat.isDirectory()) {
|
|
1628
783
|
await fileHandle.close();
|
|
1629
784
|
const indexFile = opts.index ?? "index.html";
|
|
1630
|
-
filePath =
|
|
1631
|
-
if (!filePath.startsWith(rootDir +
|
|
785
|
+
filePath = resolve2(filePath, indexFile);
|
|
786
|
+
if (!filePath.startsWith(rootDir + sep)) {
|
|
1632
787
|
return new Response("Forbidden", { status: 403 });
|
|
1633
788
|
}
|
|
1634
789
|
fileHandle = await open(filePath, "r");
|
|
@@ -1639,7 +794,7 @@ function serveStatic(root, options) {
|
|
|
1639
794
|
}
|
|
1640
795
|
}
|
|
1641
796
|
const mimeType = MIME_TYPES[extname(filePath).toLowerCase()] ?? "application/octet-stream";
|
|
1642
|
-
const etag = `"${
|
|
797
|
+
const etag = `"${createHash("md5").update(`${stat.size}-${stat.mtimeMs}`).digest("hex")}"`;
|
|
1643
798
|
const ifNoneMatch = req.headers.get("if-none-match");
|
|
1644
799
|
if (ifNoneMatch === etag) {
|
|
1645
800
|
await fileHandle.close();
|
|
@@ -1860,7 +1015,7 @@ function deleteCookie(res, name, options) {
|
|
|
1860
1015
|
// upload.ts
|
|
1861
1016
|
import { writeFile, mkdir } from "node:fs/promises";
|
|
1862
1017
|
import { randomUUID } from "node:crypto";
|
|
1863
|
-
import { join
|
|
1018
|
+
import { join, extname as extname2 } from "node:path";
|
|
1864
1019
|
var extensionMimeMap = {
|
|
1865
1020
|
".jpg": "image/jpeg",
|
|
1866
1021
|
".jpeg": "image/jpeg",
|
|
@@ -1927,7 +1082,7 @@ function upload(options) {
|
|
|
1927
1082
|
};
|
|
1928
1083
|
if (saveDir) {
|
|
1929
1084
|
const safeName = value.name.replace(/[/\\\0]/g, "_");
|
|
1930
|
-
const filePath =
|
|
1085
|
+
const filePath = join(saveDir, `${randomUUID()}-${safeName}`);
|
|
1931
1086
|
await writeFile(filePath, buf);
|
|
1932
1087
|
uf.path = filePath;
|
|
1933
1088
|
}
|
|
@@ -2117,12 +1272,12 @@ function requestId(options) {
|
|
|
2117
1272
|
const gen = options?.generator ?? (() => crypto2.randomUUID());
|
|
2118
1273
|
return async (req, ctx, next) => {
|
|
2119
1274
|
const existing = req.headers.get(header);
|
|
2120
|
-
const
|
|
2121
|
-
ctx.requestId =
|
|
1275
|
+
const id3 = existing ?? gen();
|
|
1276
|
+
ctx.requestId = id3;
|
|
2122
1277
|
const res = await next(req, ctx);
|
|
2123
1278
|
if (res.headers.has(header)) return res;
|
|
2124
1279
|
const h = new Headers(res.headers);
|
|
2125
|
-
h.set(header,
|
|
1280
|
+
h.set(header, id3);
|
|
2126
1281
|
return new Response(res.body, { status: res.status, statusText: res.statusText, headers: h });
|
|
2127
1282
|
};
|
|
2128
1283
|
}
|
|
@@ -2344,10 +1499,10 @@ function resolveRef(path2, ctx) {
|
|
|
2344
1499
|
const after = path2.slice(7);
|
|
2345
1500
|
const dot = after.indexOf(".");
|
|
2346
1501
|
if (dot === -1) return ctx.nodeOutputs.get(after);
|
|
2347
|
-
const
|
|
1502
|
+
const id3 = after.slice(0, dot);
|
|
2348
1503
|
const propPath = after.slice(dot + 1);
|
|
2349
|
-
const output = ctx.nodeOutputs.get(
|
|
2350
|
-
if (output === void 0) throw new Error(`Node "${
|
|
1504
|
+
const output = ctx.nodeOutputs.get(id3);
|
|
1505
|
+
if (output === void 0) throw new Error(`Node "${id3}" has no output yet`);
|
|
2351
1506
|
return getByPath(output, propPath.startsWith("output") ? propPath.slice(7).split(".").filter(Boolean) : propPath.split("."));
|
|
2352
1507
|
}
|
|
2353
1508
|
if (path2.startsWith("$var.")) {
|
|
@@ -2873,18 +2028,18 @@ var Table = class {
|
|
|
2873
2028
|
`;
|
|
2874
2029
|
return rows;
|
|
2875
2030
|
}
|
|
2876
|
-
async read(sql2,
|
|
2031
|
+
async read(sql2, id3, opts) {
|
|
2877
2032
|
if (opts?.select?.length) {
|
|
2878
2033
|
const columns = opts.select.map((c) => `"${c}"`).join(", ");
|
|
2879
2034
|
const [row2] = await sql2.unsafe(
|
|
2880
2035
|
`SELECT ${columns} FROM "${this.tableName}" WHERE id = $1 LIMIT 1`,
|
|
2881
|
-
[
|
|
2036
|
+
[id3]
|
|
2882
2037
|
);
|
|
2883
2038
|
return row2 ?? void 0;
|
|
2884
2039
|
}
|
|
2885
2040
|
const [row] = await sql2`
|
|
2886
2041
|
SELECT * FROM ${sql2(this.tableName)}
|
|
2887
|
-
WHERE ${sql2("id")} = ${
|
|
2042
|
+
WHERE ${sql2("id")} = ${id3} LIMIT 1
|
|
2888
2043
|
`;
|
|
2889
2044
|
return row ?? void 0;
|
|
2890
2045
|
}
|
|
@@ -2918,11 +2073,11 @@ var Table = class {
|
|
|
2918
2073
|
const rows = await sql2.unsafe(query, values);
|
|
2919
2074
|
return { count, data: rows };
|
|
2920
2075
|
}
|
|
2921
|
-
async update(sql2,
|
|
2076
|
+
async update(sql2, id3, data) {
|
|
2922
2077
|
const { sets, values: setValues } = this._buildSET(data);
|
|
2923
2078
|
if (sets.length === 0) return void 0;
|
|
2924
2079
|
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,
|
|
2080
|
+
const rows = await sql2.unsafe(query, [...setValues, id3]);
|
|
2926
2081
|
return rows[0] ?? void 0;
|
|
2927
2082
|
}
|
|
2928
2083
|
async updateMany(sql2, where, data) {
|
|
@@ -2936,22 +2091,22 @@ var Table = class {
|
|
|
2936
2091
|
);
|
|
2937
2092
|
return rows.length;
|
|
2938
2093
|
}
|
|
2939
|
-
async delete(sql2,
|
|
2094
|
+
async delete(sql2, id3) {
|
|
2940
2095
|
if (this.hasColumn("deleted_at")) {
|
|
2941
2096
|
const [row2] = await sql2.unsafe(
|
|
2942
2097
|
`UPDATE "${this.tableName}" SET "deleted_at" = NOW() WHERE id = $1 RETURNING *`,
|
|
2943
|
-
[
|
|
2098
|
+
[id3]
|
|
2944
2099
|
);
|
|
2945
2100
|
return row2 ?? void 0;
|
|
2946
2101
|
}
|
|
2947
2102
|
const [row] = await sql2`
|
|
2948
|
-
DELETE FROM ${sql2(this.tableName)} WHERE ${sql2("id")} = ${
|
|
2103
|
+
DELETE FROM ${sql2(this.tableName)} WHERE ${sql2("id")} = ${id3} RETURNING *
|
|
2949
2104
|
`;
|
|
2950
2105
|
return row ?? void 0;
|
|
2951
2106
|
}
|
|
2952
|
-
async hardDelete(sql2,
|
|
2107
|
+
async hardDelete(sql2, id3) {
|
|
2953
2108
|
const [row] = await sql2`
|
|
2954
|
-
DELETE FROM ${sql2(this.tableName)} WHERE ${sql2("id")} = ${
|
|
2109
|
+
DELETE FROM ${sql2(this.tableName)} WHERE ${sql2("id")} = ${id3} RETURNING *
|
|
2955
2110
|
`;
|
|
2956
2111
|
return row ?? void 0;
|
|
2957
2112
|
}
|
|
@@ -3047,23 +2202,23 @@ var BoundTable = class _BoundTable {
|
|
|
3047
2202
|
async insertMany(data) {
|
|
3048
2203
|
return await this.inner.insertMany(this.sql, data);
|
|
3049
2204
|
}
|
|
3050
|
-
async read(
|
|
3051
|
-
return await this.inner.read(this.sql,
|
|
2205
|
+
async read(id3, opts) {
|
|
2206
|
+
return await this.inner.read(this.sql, id3, opts);
|
|
3052
2207
|
}
|
|
3053
2208
|
async readMany(where, opts) {
|
|
3054
2209
|
return await this.inner.readMany(this.sql, where, opts);
|
|
3055
2210
|
}
|
|
3056
|
-
async update(
|
|
3057
|
-
return await this.inner.update(this.sql,
|
|
2211
|
+
async update(id3, data) {
|
|
2212
|
+
return await this.inner.update(this.sql, id3, data);
|
|
3058
2213
|
}
|
|
3059
2214
|
async updateMany(where, data) {
|
|
3060
2215
|
return await this.inner.updateMany(this.sql, where, data);
|
|
3061
2216
|
}
|
|
3062
|
-
async delete(
|
|
3063
|
-
return await this.inner.delete(this.sql,
|
|
2217
|
+
async delete(id3) {
|
|
2218
|
+
return await this.inner.delete(this.sql, id3);
|
|
3064
2219
|
}
|
|
3065
|
-
async hardDelete(
|
|
3066
|
-
return await this.inner.hardDelete(this.sql,
|
|
2220
|
+
async hardDelete(id3) {
|
|
2221
|
+
return await this.inner.hardDelete(this.sql, id3);
|
|
3067
2222
|
}
|
|
3068
2223
|
async deleteMany(where) {
|
|
3069
2224
|
return await this.inner.deleteMany(this.sql, where);
|
|
@@ -3530,8 +2685,8 @@ function user(options) {
|
|
|
3530
2685
|
const { data: rows } = await users.readMany({ email });
|
|
3531
2686
|
return rows[0];
|
|
3532
2687
|
}
|
|
3533
|
-
async function findById(
|
|
3534
|
-
return await users.read(
|
|
2688
|
+
async function findById(id3) {
|
|
2689
|
+
return await users.read(id3);
|
|
3535
2690
|
}
|
|
3536
2691
|
async function register(data) {
|
|
3537
2692
|
const { email, password, name } = RegisterSchema.parse(data);
|
|
@@ -3687,7 +2842,7 @@ function createHub(opts) {
|
|
|
3687
2842
|
}
|
|
3688
2843
|
});
|
|
3689
2844
|
}
|
|
3690
|
-
function
|
|
2845
|
+
function join6(key, ws) {
|
|
3691
2846
|
if (!channels.has(key)) {
|
|
3692
2847
|
channels.set(key, /* @__PURE__ */ new Set());
|
|
3693
2848
|
redisSub?.subscribe(`${prefix}${key}`);
|
|
@@ -3728,7 +2883,7 @@ function createHub(opts) {
|
|
|
3728
2883
|
await redisSub.quit();
|
|
3729
2884
|
}
|
|
3730
2885
|
}
|
|
3731
|
-
return { join:
|
|
2886
|
+
return { join: join6, leave, broadcast, close };
|
|
3732
2887
|
}
|
|
3733
2888
|
|
|
3734
2889
|
// queue/index.ts
|
|
@@ -3854,7 +3009,7 @@ function queue(opts) {
|
|
|
3854
3009
|
}
|
|
3855
3010
|
}
|
|
3856
3011
|
mw.add = function add(type, payload, opts2) {
|
|
3857
|
-
const
|
|
3012
|
+
const id3 = crypto4.randomUUID();
|
|
3858
3013
|
let runAt;
|
|
3859
3014
|
if (opts2?.schedule) {
|
|
3860
3015
|
runAt = cronNext(opts2.schedule);
|
|
@@ -3863,9 +3018,9 @@ function queue(opts) {
|
|
|
3863
3018
|
} else {
|
|
3864
3019
|
runAt = Date.now();
|
|
3865
3020
|
}
|
|
3866
|
-
const job = { id:
|
|
3021
|
+
const job = { id: id3, type, payload, createdAt: Date.now(), runAt };
|
|
3867
3022
|
if (opts2?.schedule) job.schedule = opts2.schedule;
|
|
3868
|
-
return redis2.zadd(jobKey, runAt, JSON.stringify(job)).then(() =>
|
|
3023
|
+
return redis2.zadd(jobKey, runAt, JSON.stringify(job)).then(() => id3);
|
|
3869
3024
|
};
|
|
3870
3025
|
mw.process = function process2(type, handler) {
|
|
3871
3026
|
handlers.set(type, handler);
|
|
@@ -4803,8 +3958,8 @@ import { createOpenAI as createOpenAI2 } from "@ai-sdk/openai";
|
|
|
4803
3958
|
// agent/rest.ts
|
|
4804
3959
|
function buildRouter2(deps) {
|
|
4805
3960
|
const { agents: agentsTable, knowledge, runner } = deps;
|
|
4806
|
-
async function getAgent(
|
|
4807
|
-
const row = await agentsTable.read(
|
|
3961
|
+
async function getAgent(id3) {
|
|
3962
|
+
const row = await agentsTable.read(id3);
|
|
4808
3963
|
return row ?? null;
|
|
4809
3964
|
}
|
|
4810
3965
|
const r = new Router();
|
|
@@ -4831,8 +3986,8 @@ function buildRouter2(deps) {
|
|
|
4831
3986
|
return Response.json(agent2);
|
|
4832
3987
|
});
|
|
4833
3988
|
r.patch("/agents/:id", async (req, ctx) => {
|
|
4834
|
-
const
|
|
4835
|
-
const agent2 = await getAgent(
|
|
3989
|
+
const id3 = parseInt(ctx.params.id, 10);
|
|
3990
|
+
const agent2 = await getAgent(id3);
|
|
4836
3991
|
if (!agent2) return Response.json({ error: "Agent not found" }, { status: 404 });
|
|
4837
3992
|
const body = await req.json();
|
|
4838
3993
|
const updateData = {};
|
|
@@ -4844,23 +3999,23 @@ function buildRouter2(deps) {
|
|
|
4844
3999
|
if (Object.keys(updateData).length === 0) {
|
|
4845
4000
|
return Response.json({ error: "No fields to update" }, { status: 400 });
|
|
4846
4001
|
}
|
|
4847
|
-
const row = await agentsTable.update(
|
|
4002
|
+
const row = await agentsTable.update(id3, updateData);
|
|
4848
4003
|
return Response.json(row);
|
|
4849
4004
|
});
|
|
4850
4005
|
r.delete("/agents/:id", async (_req, ctx) => {
|
|
4851
|
-
const
|
|
4852
|
-
const row = await agentsTable.delete(
|
|
4006
|
+
const id3 = parseInt(ctx.params.id, 10);
|
|
4007
|
+
const row = await agentsTable.delete(id3);
|
|
4853
4008
|
if (!row) return Response.json({ error: "Agent not found" }, { status: 404 });
|
|
4854
4009
|
return Response.json({ ok: true });
|
|
4855
4010
|
});
|
|
4856
4011
|
r.post("/agents/:id/run", async (req, ctx) => {
|
|
4857
|
-
const
|
|
4012
|
+
const id3 = parseInt(ctx.params.id, 10);
|
|
4858
4013
|
const body = await req.json();
|
|
4859
4014
|
if (!body.input && !body.messages) {
|
|
4860
4015
|
return Response.json({ error: "input or messages is required" }, { status: 400 });
|
|
4861
4016
|
}
|
|
4862
4017
|
try {
|
|
4863
|
-
const result = await runner.run(
|
|
4018
|
+
const result = await runner.run(id3, body);
|
|
4864
4019
|
if ("stream" in result) {
|
|
4865
4020
|
return new Response(result.stream, {
|
|
4866
4021
|
headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache" }
|
|
@@ -5271,15 +4426,15 @@ function buildRouter3(deps) {
|
|
|
5271
4426
|
return Response.json(rows);
|
|
5272
4427
|
});
|
|
5273
4428
|
r.get("/channels/:id", async (_req, ctx) => {
|
|
5274
|
-
const
|
|
5275
|
-
const ch = await channels.read(
|
|
4429
|
+
const id3 = parseInt(ctx.params.id, 10);
|
|
4430
|
+
const ch = await channels.read(id3);
|
|
5276
4431
|
if (!ch) return Response.json({ error: "Channel not found" }, { status: 404 });
|
|
5277
|
-
const { data: memberRows } = await members.readMany({ channel_id:
|
|
4432
|
+
const { data: memberRows } = await members.readMany({ channel_id: id3 });
|
|
5278
4433
|
return Response.json({ channel: ch, members: memberRows });
|
|
5279
4434
|
});
|
|
5280
4435
|
r.delete("/channels/:id", async (_req, ctx) => {
|
|
5281
|
-
const
|
|
5282
|
-
await channels.delete(
|
|
4436
|
+
const id3 = parseInt(ctx.params.id, 10);
|
|
4437
|
+
await channels.delete(id3);
|
|
5283
4438
|
return Response.json({ ok: true });
|
|
5284
4439
|
});
|
|
5285
4440
|
r.post("/channels/:id/members", async (req, ctx) => {
|
|
@@ -5538,14 +4693,14 @@ function forkApp(opts) {
|
|
|
5538
4693
|
return { child, port: opts.port };
|
|
5539
4694
|
}
|
|
5540
4695
|
function stopProcess(mp, timeout = 1e4) {
|
|
5541
|
-
return new Promise((
|
|
4696
|
+
return new Promise((resolve12) => {
|
|
5542
4697
|
const timer = setTimeout(() => {
|
|
5543
4698
|
mp.child.kill("SIGKILL");
|
|
5544
|
-
|
|
4699
|
+
resolve12();
|
|
5545
4700
|
}, timeout);
|
|
5546
4701
|
mp.child.on("exit", () => {
|
|
5547
4702
|
clearTimeout(timer);
|
|
5548
|
-
|
|
4703
|
+
resolve12();
|
|
5549
4704
|
});
|
|
5550
4705
|
mp.child.kill("SIGTERM");
|
|
5551
4706
|
});
|
|
@@ -5974,6 +5129,370 @@ function ensureCertificates(config) {
|
|
|
5974
5129
|
// opencode/client.ts
|
|
5975
5130
|
import { createOpenAI as createOpenAI3 } from "@ai-sdk/openai";
|
|
5976
5131
|
|
|
5132
|
+
// opencode/rest.ts
|
|
5133
|
+
import { join as join4 } from "node:path";
|
|
5134
|
+
|
|
5135
|
+
// ssr/ssr.ts
|
|
5136
|
+
import { createElement } from "react";
|
|
5137
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
5138
|
+
import { dirname as dirname2, resolve as resolve4 } from "node:path";
|
|
5139
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
5140
|
+
|
|
5141
|
+
// ssr/compile.ts
|
|
5142
|
+
import * as esbuild from "esbuild";
|
|
5143
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2 } from "node:fs";
|
|
5144
|
+
import { join as join2, resolve as resolve3, dirname } from "node:path";
|
|
5145
|
+
import { pathToFileURL } from "node:url";
|
|
5146
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
5147
|
+
var OUT_DIR = ".weifuwu/ssr";
|
|
5148
|
+
var cache = /* @__PURE__ */ new Map();
|
|
5149
|
+
var externals = [
|
|
5150
|
+
"react",
|
|
5151
|
+
"react-dom",
|
|
5152
|
+
"esbuild",
|
|
5153
|
+
"graphql",
|
|
5154
|
+
"ws",
|
|
5155
|
+
"zod",
|
|
5156
|
+
"@graphql-tools/schema",
|
|
5157
|
+
"ai"
|
|
5158
|
+
];
|
|
5159
|
+
var _alias = null;
|
|
5160
|
+
function resolveAliases() {
|
|
5161
|
+
if (_alias) return _alias;
|
|
5162
|
+
const configFiles = ["tsconfig.json", "jsconfig.json"];
|
|
5163
|
+
for (const file of configFiles) {
|
|
5164
|
+
const p = resolve3(file);
|
|
5165
|
+
if (existsSync2(p)) {
|
|
5166
|
+
try {
|
|
5167
|
+
const config = JSON.parse(readFileSync2(p, "utf-8"));
|
|
5168
|
+
const paths = config.compilerOptions?.paths;
|
|
5169
|
+
if (paths) {
|
|
5170
|
+
const alias = {};
|
|
5171
|
+
for (const [key, values] of Object.entries(paths)) {
|
|
5172
|
+
const cleanKey = key.replace("/*", "");
|
|
5173
|
+
const val = values[0]?.replace("/*", "");
|
|
5174
|
+
if (val) alias[cleanKey] = resolve3(dirname(p), val);
|
|
5175
|
+
}
|
|
5176
|
+
_alias = alias;
|
|
5177
|
+
return alias;
|
|
5178
|
+
}
|
|
5179
|
+
} catch {
|
|
5180
|
+
}
|
|
5181
|
+
}
|
|
5182
|
+
}
|
|
5183
|
+
_alias = {};
|
|
5184
|
+
return {};
|
|
5185
|
+
}
|
|
5186
|
+
function id(s) {
|
|
5187
|
+
return createHash2("md5").update(s).digest("hex").slice(0, 8);
|
|
5188
|
+
}
|
|
5189
|
+
async function compileTsx(path2) {
|
|
5190
|
+
if (cache.has(path2)) return cache.get(path2);
|
|
5191
|
+
const absPath = resolve3(path2);
|
|
5192
|
+
mkdirSync(OUT_DIR, { recursive: true });
|
|
5193
|
+
const hash = id(absPath);
|
|
5194
|
+
const outPath = join2(OUT_DIR, hash + ".js");
|
|
5195
|
+
await esbuild.build({
|
|
5196
|
+
entryPoints: { [hash]: absPath },
|
|
5197
|
+
outdir: OUT_DIR,
|
|
5198
|
+
format: "esm",
|
|
5199
|
+
platform: "node",
|
|
5200
|
+
jsx: "automatic",
|
|
5201
|
+
jsxImportSource: "react",
|
|
5202
|
+
bundle: true,
|
|
5203
|
+
external: externals,
|
|
5204
|
+
alias: resolveAliases(),
|
|
5205
|
+
write: true,
|
|
5206
|
+
allowOverwrite: true
|
|
5207
|
+
});
|
|
5208
|
+
const mod = await import(pathToFileURL(outPath).href);
|
|
5209
|
+
cache.set(path2, mod);
|
|
5210
|
+
return mod;
|
|
5211
|
+
}
|
|
5212
|
+
|
|
5213
|
+
// ssr/stream.ts
|
|
5214
|
+
import { TextDecoder, TextEncoder as TextEncoder2 } from "node:util";
|
|
5215
|
+
var _publicEnv = null;
|
|
5216
|
+
function getPublicEnv() {
|
|
5217
|
+
if (_publicEnv) return _publicEnv;
|
|
5218
|
+
_publicEnv = {};
|
|
5219
|
+
for (const key of Object.keys(process.env)) {
|
|
5220
|
+
if (key.startsWith("WEIFUWU_PUBLIC_")) {
|
|
5221
|
+
_publicEnv[key] = process.env[key];
|
|
5222
|
+
}
|
|
5223
|
+
}
|
|
5224
|
+
return _publicEnv;
|
|
5225
|
+
}
|
|
5226
|
+
function buildHeadPayload(opts) {
|
|
5227
|
+
const { ctx, base, compiledTailwindCss } = opts;
|
|
5228
|
+
let result = "";
|
|
5229
|
+
if (ctx.prefs?.theme) {
|
|
5230
|
+
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>
|
|
5231
|
+
`;
|
|
5232
|
+
}
|
|
5233
|
+
if (compiledTailwindCss) {
|
|
5234
|
+
result += `<link rel="stylesheet" href="${base}/__wfw/style.css" />
|
|
5235
|
+
`;
|
|
5236
|
+
}
|
|
5237
|
+
const localeData = ctx.parsed?.__localeData ?? globalThis.__LOCALE_DATA__;
|
|
5238
|
+
if (localeData && Object.keys(localeData).length > 0) {
|
|
5239
|
+
result += `<script>window.__LOCALE_DATA__=${JSON.stringify(localeData)}</script>
|
|
5240
|
+
`;
|
|
5241
|
+
}
|
|
5242
|
+
const loaderData = opts.loaderData || {};
|
|
5243
|
+
const ctxData = {
|
|
5244
|
+
params: ctx.params,
|
|
5245
|
+
query: ctx.query,
|
|
5246
|
+
user: ctx.user,
|
|
5247
|
+
parsed: ctx.parsed,
|
|
5248
|
+
prefs: ctx.prefs,
|
|
5249
|
+
loaderData
|
|
5250
|
+
};
|
|
5251
|
+
const publicEnv = getPublicEnv();
|
|
5252
|
+
if (Object.keys(publicEnv).length > 0) {
|
|
5253
|
+
ctxData.env = publicEnv;
|
|
5254
|
+
}
|
|
5255
|
+
result += `<script>window.__WEIFUWU_CTX=${JSON.stringify(ctxData)}</script>
|
|
5256
|
+
`;
|
|
5257
|
+
return result;
|
|
5258
|
+
}
|
|
5259
|
+
function buildBodyScripts(opts) {
|
|
5260
|
+
const parts = [];
|
|
5261
|
+
if (opts.loaderData && Object.keys(opts.loaderData).length > 0) {
|
|
5262
|
+
parts.push(`<script>window.__WEIFUWU_PROPS=${JSON.stringify(opts.loaderData)}</script>`);
|
|
5263
|
+
}
|
|
5264
|
+
if (opts.bundle) {
|
|
5265
|
+
parts.push(`<script type="module" src="${opts.base}${opts.bundle.url}"></script>`);
|
|
5266
|
+
}
|
|
5267
|
+
return parts.join("\n");
|
|
5268
|
+
}
|
|
5269
|
+
function streamResponse(reactStream, opts) {
|
|
5270
|
+
const decoder = new TextDecoder();
|
|
5271
|
+
const encoder2 = new TextEncoder2();
|
|
5272
|
+
const headPayload = buildHeadPayload(opts);
|
|
5273
|
+
let buffer = "";
|
|
5274
|
+
let headFlushed = false;
|
|
5275
|
+
let extractedHead = "";
|
|
5276
|
+
const output = new ReadableStream({
|
|
5277
|
+
async start(controller) {
|
|
5278
|
+
try {
|
|
5279
|
+
const reader = reactStream.getReader();
|
|
5280
|
+
async function push(chunk) {
|
|
5281
|
+
buffer += decoder.decode(chunk, { stream: true });
|
|
5282
|
+
if (!extractedHead) {
|
|
5283
|
+
const m = buffer.match(/<template id="__wfw_head">([\s\S]*?)<\/template>/);
|
|
5284
|
+
if (m) {
|
|
5285
|
+
extractedHead = m[1];
|
|
5286
|
+
buffer = buffer.replace(m[0], "");
|
|
5287
|
+
}
|
|
5288
|
+
}
|
|
5289
|
+
if (!headFlushed) {
|
|
5290
|
+
const idx = buffer.indexOf("</head>");
|
|
5291
|
+
if (idx !== -1) {
|
|
5292
|
+
const before = buffer.slice(0, idx);
|
|
5293
|
+
let injection = "";
|
|
5294
|
+
if (extractedHead) injection += "\n" + extractedHead;
|
|
5295
|
+
injection += headPayload;
|
|
5296
|
+
controller.enqueue(encoder2.encode(before + injection));
|
|
5297
|
+
buffer = buffer.slice(idx);
|
|
5298
|
+
headFlushed = true;
|
|
5299
|
+
}
|
|
5300
|
+
return;
|
|
5301
|
+
}
|
|
5302
|
+
controller.enqueue(encoder2.encode(buffer));
|
|
5303
|
+
buffer = "";
|
|
5304
|
+
}
|
|
5305
|
+
while (true) {
|
|
5306
|
+
const { done, value } = await reader.read();
|
|
5307
|
+
if (done) break;
|
|
5308
|
+
await push(value);
|
|
5309
|
+
}
|
|
5310
|
+
buffer = buffer.replace(/<template id="__wfw_head">[\s\S]*?<\/template>/g, "");
|
|
5311
|
+
if (buffer) controller.enqueue(encoder2.encode(buffer));
|
|
5312
|
+
const body = buildBodyScripts(opts);
|
|
5313
|
+
if (body) controller.enqueue(encoder2.encode("\n" + body));
|
|
5314
|
+
if (opts.isDev) {
|
|
5315
|
+
controller.enqueue(encoder2.encode(
|
|
5316
|
+
`
|
|
5317
|
+
<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>`
|
|
5318
|
+
));
|
|
5319
|
+
}
|
|
5320
|
+
} catch {
|
|
5321
|
+
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>`;
|
|
5322
|
+
controller.enqueue(encoder2.encode(fallback));
|
|
5323
|
+
} finally {
|
|
5324
|
+
controller.close();
|
|
5325
|
+
}
|
|
5326
|
+
}
|
|
5327
|
+
});
|
|
5328
|
+
return new Response(output, {
|
|
5329
|
+
status: opts.status ?? 200,
|
|
5330
|
+
headers: { "content-type": "text/html; charset=utf-8" }
|
|
5331
|
+
});
|
|
5332
|
+
}
|
|
5333
|
+
|
|
5334
|
+
// ssr/ssr.ts
|
|
5335
|
+
var als = new AsyncLocalStorage();
|
|
5336
|
+
__registerAls(() => als.getStore());
|
|
5337
|
+
var isDev = process.env.NODE_ENV !== "production";
|
|
5338
|
+
var bundleCache = /* @__PURE__ */ new Map();
|
|
5339
|
+
function id2(s) {
|
|
5340
|
+
return createHash3("md5").update(s).digest("hex").slice(0, 8);
|
|
5341
|
+
}
|
|
5342
|
+
function serializeLoaderData(ctx) {
|
|
5343
|
+
const data = {};
|
|
5344
|
+
for (const key of Object.keys(ctx)) {
|
|
5345
|
+
if (!["params", "query", "mountPath", "layoutStack"].includes(key)) {
|
|
5346
|
+
data[key] = ctx[key];
|
|
5347
|
+
}
|
|
5348
|
+
}
|
|
5349
|
+
return data;
|
|
5350
|
+
}
|
|
5351
|
+
async function buildClientBundle(entryPath, layoutPaths) {
|
|
5352
|
+
try {
|
|
5353
|
+
const absEntry = resolve4(entryPath);
|
|
5354
|
+
const absLayouts = layoutPaths.map((p) => resolve4(p));
|
|
5355
|
+
const layoutImports = absLayouts.map((p) => `import${JSON.stringify(p)};`).join("");
|
|
5356
|
+
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()})}})()`;
|
|
5357
|
+
const code = [
|
|
5358
|
+
layoutImports,
|
|
5359
|
+
`import{hydrateRoot}from'react-dom/client';`,
|
|
5360
|
+
`import{createElement,useState,useEffect}from'react';`,
|
|
5361
|
+
`import{TsxContext}from'weifuwu/react';`,
|
|
5362
|
+
`import P from${JSON.stringify(absEntry)};`,
|
|
5363
|
+
`var setCtx=${_sc};`,
|
|
5364
|
+
`const c=document.getElementById('__weifuwu_root');`,
|
|
5365
|
+
`if(window.__WEIFUWU_PROPS)setCtx({loaderData:window.__WEIFUWU_PROPS});`,
|
|
5366
|
+
`if(!window.__WFW_ROOT){`,
|
|
5367
|
+
`function App(){`,
|
|
5368
|
+
`const[p,setP]=useState({C:P});`,
|
|
5369
|
+
`useEffect(()=>{window.__WFW_SET_PAGE=(C)=>{setCtx({loaderData:window.__WEIFUWU_PROPS});setP({C})}},[]);`,
|
|
5370
|
+
`const ctx=window.__WEIFUWU_CTX||{};`,
|
|
5371
|
+
`return createElement(TsxContext.Provider,{value:ctx},`,
|
|
5372
|
+
`createElement(p.C,null))`,
|
|
5373
|
+
`}`,
|
|
5374
|
+
`window.__WFW_ROOT=hydrateRoot(c,createElement(App));`,
|
|
5375
|
+
`}else{`,
|
|
5376
|
+
`window.__WFW_SET_PAGE?.(P);`,
|
|
5377
|
+
`}`
|
|
5378
|
+
].join("");
|
|
5379
|
+
const { default: esbuild2 } = await import("esbuild");
|
|
5380
|
+
const result = await esbuild2.build({
|
|
5381
|
+
stdin: { contents: code, loader: "tsx", resolveDir: dirname2(absEntry) },
|
|
5382
|
+
bundle: true,
|
|
5383
|
+
format: "esm",
|
|
5384
|
+
jsx: "automatic",
|
|
5385
|
+
jsxImportSource: "react",
|
|
5386
|
+
banner: { js: "self.process={env:{}};" },
|
|
5387
|
+
loader: { ".node": "empty" },
|
|
5388
|
+
write: false,
|
|
5389
|
+
minify: true
|
|
5390
|
+
});
|
|
5391
|
+
return result.outputFiles[0].contents;
|
|
5392
|
+
} catch (err) {
|
|
5393
|
+
console.error("hydration bundle failed:", err);
|
|
5394
|
+
return null;
|
|
5395
|
+
}
|
|
5396
|
+
}
|
|
5397
|
+
function ssr(path2) {
|
|
5398
|
+
const entryId = id2(resolve4(path2));
|
|
5399
|
+
const bundleKey = `/__ssr/${entryId}.js`;
|
|
5400
|
+
const r = new Router();
|
|
5401
|
+
r.get("/__ssr/:path", (req, ctx) => {
|
|
5402
|
+
const buf = bundleCache.get("/__ssr/" + ctx.params.path);
|
|
5403
|
+
return buf ? new Response(buf, {
|
|
5404
|
+
headers: { "content-type": "application/javascript; charset=utf-8" }
|
|
5405
|
+
}) : new Response("", { status: 404 });
|
|
5406
|
+
});
|
|
5407
|
+
r.get("/", async (req, ctx) => {
|
|
5408
|
+
const pageMod = await compileTsx(path2);
|
|
5409
|
+
const Component = pageMod.default;
|
|
5410
|
+
if (!Component) return new Response("", { status: 500 });
|
|
5411
|
+
const layouts = ctx.layoutStack || [];
|
|
5412
|
+
const layoutComponents = layouts.map((l) => l.component);
|
|
5413
|
+
const layoutPaths = layouts.map((l) => l.path);
|
|
5414
|
+
const base = (ctx.mountPath || "").replace(/\/$/, "");
|
|
5415
|
+
const loaderData = serializeLoaderData(ctx);
|
|
5416
|
+
const ctxValue = {
|
|
5417
|
+
params: ctx.params,
|
|
5418
|
+
query: ctx.query,
|
|
5419
|
+
user: ctx.user ?? {},
|
|
5420
|
+
parsed: ctx.parsed ?? {},
|
|
5421
|
+
prefs: ctx.prefs ?? {},
|
|
5422
|
+
loaderData,
|
|
5423
|
+
env: ctx.env ?? {}
|
|
5424
|
+
};
|
|
5425
|
+
return als.run(ctxValue, async () => {
|
|
5426
|
+
setCtx(ctxValue);
|
|
5427
|
+
let element = createElement(
|
|
5428
|
+
TsxContext.Provider,
|
|
5429
|
+
{ value: ctxValue },
|
|
5430
|
+
createElement(
|
|
5431
|
+
"div",
|
|
5432
|
+
{ id: "__weifuwu_root" },
|
|
5433
|
+
createElement(Component, null)
|
|
5434
|
+
)
|
|
5435
|
+
);
|
|
5436
|
+
if (layoutComponents.length === 0) {
|
|
5437
|
+
element = createElement(
|
|
5438
|
+
"html",
|
|
5439
|
+
{ lang: "en" },
|
|
5440
|
+
createElement(
|
|
5441
|
+
"head",
|
|
5442
|
+
null,
|
|
5443
|
+
createElement("meta", { charSet: "utf-8" }),
|
|
5444
|
+
createElement("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
|
|
5445
|
+
createElement("title", null, "weifuwu")
|
|
5446
|
+
),
|
|
5447
|
+
createElement("body", null, element)
|
|
5448
|
+
);
|
|
5449
|
+
} else {
|
|
5450
|
+
for (const L of layoutComponents.toReversed()) {
|
|
5451
|
+
element = createElement(L, { children: element });
|
|
5452
|
+
}
|
|
5453
|
+
}
|
|
5454
|
+
let bundle = null;
|
|
5455
|
+
if (!bundleCache.has(bundleKey)) {
|
|
5456
|
+
const buf = await buildClientBundle(path2, layoutPaths);
|
|
5457
|
+
if (buf) bundleCache.set(bundleKey, buf);
|
|
5458
|
+
}
|
|
5459
|
+
if (bundleCache.has(bundleKey)) {
|
|
5460
|
+
bundle = { url: bundleKey };
|
|
5461
|
+
}
|
|
5462
|
+
const { renderToReadableStream } = await import("react-dom/server");
|
|
5463
|
+
const stream = await renderToReadableStream(element);
|
|
5464
|
+
return streamResponse(stream, {
|
|
5465
|
+
ctx,
|
|
5466
|
+
base,
|
|
5467
|
+
isDev,
|
|
5468
|
+
bundle,
|
|
5469
|
+
loaderData
|
|
5470
|
+
});
|
|
5471
|
+
});
|
|
5472
|
+
});
|
|
5473
|
+
return r;
|
|
5474
|
+
}
|
|
5475
|
+
|
|
5476
|
+
// ssr/layout.ts
|
|
5477
|
+
function layout(path2) {
|
|
5478
|
+
return async (req, ctx, next) => {
|
|
5479
|
+
const mod = await compileTsx(path2);
|
|
5480
|
+
const Component = mod.default;
|
|
5481
|
+
if (!Component) throw new Error(`Layout ${path2} has no default export`);
|
|
5482
|
+
ctx.layoutStack = [...ctx.layoutStack || [], { path: path2, component: Component }];
|
|
5483
|
+
return next(req, ctx);
|
|
5484
|
+
};
|
|
5485
|
+
}
|
|
5486
|
+
|
|
5487
|
+
// ssr/tailwind.ts
|
|
5488
|
+
var isDev2 = process.env.NODE_ENV !== "production";
|
|
5489
|
+
|
|
5490
|
+
// ssr/error-boundary.ts
|
|
5491
|
+
import { createElement as createElement2 } from "react";
|
|
5492
|
+
|
|
5493
|
+
// ssr/live.ts
|
|
5494
|
+
import chokidar from "chokidar";
|
|
5495
|
+
|
|
5977
5496
|
// opencode/session.ts
|
|
5978
5497
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
5979
5498
|
import { join as join3 } from "node:path";
|
|
@@ -6001,12 +5520,12 @@ var messages = pgTable("_opencode_messages", {
|
|
|
6001
5520
|
created_at: timestamptz("created_at")
|
|
6002
5521
|
});
|
|
6003
5522
|
async function createSession(sql2, opts, cwd, mountPath) {
|
|
6004
|
-
const
|
|
6005
|
-
const ws = computeSessionWorkspace(cwd, mountPath,
|
|
5523
|
+
const id3 = randomUUID2();
|
|
5524
|
+
const ws = computeSessionWorkspace(cwd, mountPath, id3);
|
|
6006
5525
|
await mkdir2(ws, { recursive: true });
|
|
6007
5526
|
const [row] = await sql2`
|
|
6008
5527
|
INSERT INTO "_opencode_sessions" ("id", "user_id", "title", "model", "workspace", "system_prompt")
|
|
6009
|
-
VALUES (${
|
|
5528
|
+
VALUES (${id3}, ${opts.userId ?? 0}, ${opts.title ?? null}, ${opts.model ?? "deepseek-v4-flash"}, ${ws}, ${opts.systemPrompt ?? null})
|
|
6010
5529
|
RETURNING *
|
|
6011
5530
|
`;
|
|
6012
5531
|
return row;
|
|
@@ -6015,8 +5534,8 @@ function computeSessionWorkspace(cwd, mountPath, sessionId) {
|
|
|
6015
5534
|
const name = !mountPath || mountPath === "/" ? "default" : mountPath.replace(/^\//, "");
|
|
6016
5535
|
return join3(cwd, ".sessions", name, sessionId);
|
|
6017
5536
|
}
|
|
6018
|
-
async function getSession(sql2,
|
|
6019
|
-
const { data: rows } = await sessions.readMany(sql2, { id:
|
|
5537
|
+
async function getSession(sql2, id3) {
|
|
5538
|
+
const { data: rows } = await sessions.readMany(sql2, { id: id3, active: true });
|
|
6020
5539
|
return rows[0] ?? null;
|
|
6021
5540
|
}
|
|
6022
5541
|
async function listSessions(sql2, userId) {
|
|
@@ -6028,8 +5547,8 @@ async function listSessions(sql2, userId) {
|
|
|
6028
5547
|
const { data: rows } = await sessions.readMany(sql2, { active: true }, opts);
|
|
6029
5548
|
return rows;
|
|
6030
5549
|
}
|
|
6031
|
-
async function deleteSession(sql2,
|
|
6032
|
-
await sessions.update(sql2,
|
|
5550
|
+
async function deleteSession(sql2, id3) {
|
|
5551
|
+
await sessions.update(sql2, id3, { active: false, updated_at: sql`NOW()` });
|
|
6033
5552
|
}
|
|
6034
5553
|
async function getHistory(sql2, sessionId, limit = 50) {
|
|
6035
5554
|
const { data: rows } = await messages.readMany(sql2, { session_id: sessionId }, {
|
|
@@ -6210,10 +5729,10 @@ function createBashTool(ctx) {
|
|
|
6210
5729
|
return { stdout: "", stderr: "Command denied: potentially dangerous command", exitCode: 1 };
|
|
6211
5730
|
}
|
|
6212
5731
|
const cwd = workdir ? `${ctx.workspace}/${workdir}` : ctx.workspace;
|
|
6213
|
-
return new Promise((
|
|
5732
|
+
return new Promise((resolve12) => {
|
|
6214
5733
|
const child = exec(command, { cwd, timeout: timeout * 1e3, maxBuffer: 1024 * 1024 }, (error, stdout, stderr) => {
|
|
6215
5734
|
const truncated = stdout.length > 1e6 || stderr.length > 1e6;
|
|
6216
|
-
|
|
5735
|
+
resolve12({
|
|
6217
5736
|
stdout: stdout.slice(0, 1e6),
|
|
6218
5737
|
stderr: stderr.slice(0, 1e6),
|
|
6219
5738
|
exitCode: error?.code ?? 0,
|
|
@@ -6230,7 +5749,7 @@ function createBashTool(ctx) {
|
|
|
6230
5749
|
import { tool as tool4 } from "ai";
|
|
6231
5750
|
import { z as z6 } from "zod";
|
|
6232
5751
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
6233
|
-
import { resolve as
|
|
5752
|
+
import { resolve as resolve5 } from "node:path";
|
|
6234
5753
|
function createReadTool(ctx) {
|
|
6235
5754
|
return tool4({
|
|
6236
5755
|
description: "Read file contents. Supports offset and limit for reading specific line ranges.",
|
|
@@ -6240,7 +5759,7 @@ function createReadTool(ctx) {
|
|
|
6240
5759
|
limit: z6.number().optional().describe("Number of lines to read")
|
|
6241
5760
|
}),
|
|
6242
5761
|
execute: async ({ path: path2, offset, limit }) => {
|
|
6243
|
-
const resolved =
|
|
5762
|
+
const resolved = resolve5(ctx.workspace, path2);
|
|
6244
5763
|
if (!isPathAllowed(resolved, ctx.workspace, ctx.permissions)) {
|
|
6245
5764
|
return { error: "Path not allowed", content: null, totalLines: 0 };
|
|
6246
5765
|
}
|
|
@@ -6271,8 +5790,8 @@ function createReadTool(ctx) {
|
|
|
6271
5790
|
// opencode/tools/write.ts
|
|
6272
5791
|
import { tool as tool5 } from "ai";
|
|
6273
5792
|
import { z as z7 } from "zod";
|
|
6274
|
-
import { writeFileSync
|
|
6275
|
-
import { resolve as
|
|
5793
|
+
import { writeFileSync, mkdirSync as mkdirSync2 } from "node:fs";
|
|
5794
|
+
import { resolve as resolve6, dirname as dirname3 } from "node:path";
|
|
6276
5795
|
function createWriteTool(ctx) {
|
|
6277
5796
|
return tool5({
|
|
6278
5797
|
description: "Create or overwrite a file. Parent directories are created automatically.",
|
|
@@ -6281,12 +5800,12 @@ function createWriteTool(ctx) {
|
|
|
6281
5800
|
content: z7.string().describe("File content")
|
|
6282
5801
|
}),
|
|
6283
5802
|
execute: async ({ path: path2, content }) => {
|
|
6284
|
-
const resolved =
|
|
5803
|
+
const resolved = resolve6(ctx.workspace, path2);
|
|
6285
5804
|
if (!isPathAllowed(resolved, ctx.workspace, ctx.permissions)) {
|
|
6286
5805
|
return { error: "Path not allowed" };
|
|
6287
5806
|
}
|
|
6288
|
-
mkdirSync2(
|
|
6289
|
-
|
|
5807
|
+
mkdirSync2(dirname3(resolved), { recursive: true });
|
|
5808
|
+
writeFileSync(resolved, content, "utf-8");
|
|
6290
5809
|
return { path: path2, size: content.length };
|
|
6291
5810
|
}
|
|
6292
5811
|
});
|
|
@@ -6295,8 +5814,8 @@ function createWriteTool(ctx) {
|
|
|
6295
5814
|
// opencode/tools/edit.ts
|
|
6296
5815
|
import { tool as tool6 } from "ai";
|
|
6297
5816
|
import { z as z8 } from "zod";
|
|
6298
|
-
import { readFileSync as readFileSync4, writeFileSync as
|
|
6299
|
-
import { resolve as
|
|
5817
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
|
|
5818
|
+
import { resolve as resolve7 } from "node:path";
|
|
6300
5819
|
function createEditTool(ctx) {
|
|
6301
5820
|
return tool6({
|
|
6302
5821
|
description: "Perform exact string replacements in a file. If oldString appears multiple times, provide more surrounding context.",
|
|
@@ -6307,7 +5826,7 @@ function createEditTool(ctx) {
|
|
|
6307
5826
|
replaceAll: z8.boolean().default(false).describe("Replace all occurrences")
|
|
6308
5827
|
}),
|
|
6309
5828
|
execute: async ({ path: path2, oldString, newString, replaceAll }) => {
|
|
6310
|
-
const resolved =
|
|
5829
|
+
const resolved = resolve7(ctx.workspace, path2);
|
|
6311
5830
|
if (!isPathAllowed(resolved, ctx.workspace, ctx.permissions)) {
|
|
6312
5831
|
return { error: "Path not allowed" };
|
|
6313
5832
|
}
|
|
@@ -6318,7 +5837,7 @@ function createEditTool(ctx) {
|
|
|
6318
5837
|
}
|
|
6319
5838
|
const count = content.split(oldString).length - 1;
|
|
6320
5839
|
const result2 = content.replaceAll(oldString, newString);
|
|
6321
|
-
|
|
5840
|
+
writeFileSync2(resolved, result2, "utf-8");
|
|
6322
5841
|
return { path: path2, replaced: count };
|
|
6323
5842
|
}
|
|
6324
5843
|
const firstIdx = content.indexOf(oldString);
|
|
@@ -6330,7 +5849,7 @@ function createEditTool(ctx) {
|
|
|
6330
5849
|
return { error: "Found multiple matches. Provide more surrounding context in oldString.", replaced: 0 };
|
|
6331
5850
|
}
|
|
6332
5851
|
const result = content.replace(oldString, newString);
|
|
6333
|
-
|
|
5852
|
+
writeFileSync2(resolved, result, "utf-8");
|
|
6334
5853
|
return { path: path2, replaced: 1 };
|
|
6335
5854
|
}
|
|
6336
5855
|
});
|
|
@@ -6340,7 +5859,7 @@ function createEditTool(ctx) {
|
|
|
6340
5859
|
import { tool as tool7 } from "ai";
|
|
6341
5860
|
import { z as z9 } from "zod";
|
|
6342
5861
|
import { execFileSync } from "node:child_process";
|
|
6343
|
-
import { resolve as
|
|
5862
|
+
import { resolve as resolve8 } from "node:path";
|
|
6344
5863
|
import { existsSync as existsSync3 } from "node:fs";
|
|
6345
5864
|
function createGrepTool(ctx) {
|
|
6346
5865
|
return tool7({
|
|
@@ -6352,7 +5871,7 @@ function createGrepTool(ctx) {
|
|
|
6352
5871
|
context: z9.number().default(0).describe("Number of context lines before and after each match")
|
|
6353
5872
|
}),
|
|
6354
5873
|
execute: async ({ pattern, include, path: path2, context }) => {
|
|
6355
|
-
const searchDir = path2 ?
|
|
5874
|
+
const searchDir = path2 ? resolve8(ctx.workspace, path2) : ctx.workspace;
|
|
6356
5875
|
try {
|
|
6357
5876
|
let stdout;
|
|
6358
5877
|
if (existsSync3("/usr/bin/rg") || existsSync3("/usr/local/bin/rg")) {
|
|
@@ -6384,7 +5903,7 @@ function createGrepTool(ctx) {
|
|
|
6384
5903
|
import { tool as tool8 } from "ai";
|
|
6385
5904
|
import { z as z10 } from "zod";
|
|
6386
5905
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
6387
|
-
import { resolve as
|
|
5906
|
+
import { resolve as resolve9 } from "node:path";
|
|
6388
5907
|
function createGlobTool(ctx) {
|
|
6389
5908
|
return tool8({
|
|
6390
5909
|
description: "Find files matching a glob pattern.",
|
|
@@ -6393,7 +5912,7 @@ function createGlobTool(ctx) {
|
|
|
6393
5912
|
path: z10.string().optional().describe("Subdirectory relative to workspace")
|
|
6394
5913
|
}),
|
|
6395
5914
|
execute: async ({ pattern, path: path2 }) => {
|
|
6396
|
-
const searchDir = path2 ?
|
|
5915
|
+
const searchDir = path2 ? resolve9(ctx.workspace, path2) : ctx.workspace;
|
|
6397
5916
|
try {
|
|
6398
5917
|
const stdout = execFileSync2("find", [
|
|
6399
5918
|
searchDir,
|
|
@@ -6454,7 +5973,7 @@ function createQuestionTool(ctx) {
|
|
|
6454
5973
|
options: z12.array(z12.string()).optional().describe("Optional multiple choice options")
|
|
6455
5974
|
}),
|
|
6456
5975
|
execute: async ({ question, options }, { toolCallId }) => {
|
|
6457
|
-
return new Promise((
|
|
5976
|
+
return new Promise((resolve12, reject) => {
|
|
6458
5977
|
const timeout = setTimeout(() => {
|
|
6459
5978
|
ctx.pendingQuestions.delete(toolCallId);
|
|
6460
5979
|
reject(new Error("Question timed out"));
|
|
@@ -6462,7 +5981,7 @@ function createQuestionTool(ctx) {
|
|
|
6462
5981
|
ctx.pendingQuestions.set(toolCallId, {
|
|
6463
5982
|
resolve: (answer) => {
|
|
6464
5983
|
clearTimeout(timeout);
|
|
6465
|
-
|
|
5984
|
+
resolve12(answer);
|
|
6466
5985
|
},
|
|
6467
5986
|
reject: (err) => {
|
|
6468
5987
|
clearTimeout(timeout);
|
|
@@ -6563,15 +6082,15 @@ async function buildRouter4(deps) {
|
|
|
6563
6082
|
return Response.json(sessions2);
|
|
6564
6083
|
});
|
|
6565
6084
|
router.get("/sessions/:id", async (_req, ctx) => {
|
|
6566
|
-
const
|
|
6567
|
-
const session = await getSession(sql2,
|
|
6085
|
+
const id3 = ctx.params.id;
|
|
6086
|
+
const session = await getSession(sql2, id3);
|
|
6568
6087
|
if (!session) return new Response("Not Found", { status: 404 });
|
|
6569
|
-
const messages2 = await getHistory(sql2,
|
|
6088
|
+
const messages2 = await getHistory(sql2, id3);
|
|
6570
6089
|
return Response.json({ session, messages: messages2 });
|
|
6571
6090
|
});
|
|
6572
6091
|
router.delete("/sessions/:id", async (_req, ctx) => {
|
|
6573
|
-
const
|
|
6574
|
-
await deleteSession(sql2,
|
|
6092
|
+
const id3 = ctx.params.id;
|
|
6093
|
+
await deleteSession(sql2, id3);
|
|
6575
6094
|
return new Response(null, { status: 204 });
|
|
6576
6095
|
});
|
|
6577
6096
|
router.post("/sessions/:id/message", async (req, ctx) => {
|
|
@@ -6609,8 +6128,8 @@ async function buildRouter4(deps) {
|
|
|
6609
6128
|
});
|
|
6610
6129
|
try {
|
|
6611
6130
|
const uiDir = new URL("../opencode/ui/", import.meta.url).pathname;
|
|
6612
|
-
|
|
6613
|
-
router.
|
|
6131
|
+
router.use(layout(join4(uiDir, "layout.tsx")));
|
|
6132
|
+
router.get("/", ssr(join4(uiDir, "page.tsx")));
|
|
6614
6133
|
} catch (e) {
|
|
6615
6134
|
console.warn("[opencode] UI not available:", e);
|
|
6616
6135
|
}
|
|
@@ -6745,7 +6264,7 @@ function createWSHandler2(deps) {
|
|
|
6745
6264
|
import { readFile } from "node:fs/promises";
|
|
6746
6265
|
import { glob } from "node:fs/promises";
|
|
6747
6266
|
import { homedir } from "node:os";
|
|
6748
|
-
import { resolve as
|
|
6267
|
+
import { resolve as resolve10 } from "node:path";
|
|
6749
6268
|
import { parse as parseYaml } from "yaml";
|
|
6750
6269
|
var SEARCH_DIRS = [
|
|
6751
6270
|
(ws) => `${ws}/.opencode/skills`,
|
|
@@ -6783,7 +6302,7 @@ async function scanDir(dir) {
|
|
|
6783
6302
|
try {
|
|
6784
6303
|
const files = [];
|
|
6785
6304
|
for await (const entry of glob("*/SKILL.md", { cwd: dir })) {
|
|
6786
|
-
const skill = await parseSkillFile(
|
|
6305
|
+
const skill = await parseSkillFile(resolve10(dir, entry));
|
|
6787
6306
|
if (skill) files.push(skill);
|
|
6788
6307
|
}
|
|
6789
6308
|
return files;
|
|
@@ -7119,7 +6638,7 @@ function analytics(options) {
|
|
|
7119
6638
|
// preferences.ts
|
|
7120
6639
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
7121
6640
|
import { existsSync as existsSync4 } from "node:fs";
|
|
7122
|
-
import { join as
|
|
6641
|
+
import { join as join5, resolve as resolve11 } from "node:path";
|
|
7123
6642
|
var defaults = {
|
|
7124
6643
|
locale: { default: "en", cookie: "locale", fromAcceptLanguage: true },
|
|
7125
6644
|
theme: { default: "system", cookie: "theme" }
|
|
@@ -7168,24 +6687,24 @@ async function handlePrefSwitch(req, value, cookieName, load) {
|
|
|
7168
6687
|
});
|
|
7169
6688
|
}
|
|
7170
6689
|
function preferences(options) {
|
|
7171
|
-
const dir = options.dir ?
|
|
6690
|
+
const dir = options.dir ? resolve11(options.dir) : void 0;
|
|
7172
6691
|
const localeOpts = { ...defaults.locale, ...options.locale };
|
|
7173
6692
|
const themeOpts = { ...defaults.theme, ...options.theme };
|
|
7174
|
-
const
|
|
6693
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
7175
6694
|
function validLocale(locale) {
|
|
7176
6695
|
return /^[\w-]+$/.test(locale) && !locale.includes("..");
|
|
7177
6696
|
}
|
|
7178
6697
|
async function load(locale) {
|
|
7179
6698
|
if (!dir) return {};
|
|
7180
6699
|
if (!validLocale(locale)) return {};
|
|
7181
|
-
const cached =
|
|
6700
|
+
const cached = cache2.get(locale);
|
|
7182
6701
|
if (cached) return cached;
|
|
7183
|
-
const filePath =
|
|
6702
|
+
const filePath = join5(dir, `${locale}.json`);
|
|
7184
6703
|
if (!existsSync4(filePath)) return {};
|
|
7185
6704
|
try {
|
|
7186
6705
|
const content = await readFile2(filePath, "utf-8");
|
|
7187
6706
|
const data = JSON.parse(content);
|
|
7188
|
-
|
|
6707
|
+
cache2.set(locale, data);
|
|
7189
6708
|
return data;
|
|
7190
6709
|
} catch {
|
|
7191
6710
|
return {};
|
|
@@ -7532,9 +7051,9 @@ function listHandler(entries) {
|
|
|
7532
7051
|
}
|
|
7533
7052
|
function getHandler(entries) {
|
|
7534
7053
|
return async (_req, ctx) => {
|
|
7535
|
-
const
|
|
7536
|
-
if (!
|
|
7537
|
-
const row = await entries.read(parseInt(
|
|
7054
|
+
const id3 = ctx.params?.id;
|
|
7055
|
+
if (!id3) return Response.json({ error: "id is required" }, { status: 400 });
|
|
7056
|
+
const row = await entries.read(parseInt(id3));
|
|
7538
7057
|
if (!row) return Response.json({ error: "not found" }, { status: 404 });
|
|
7539
7058
|
return Response.json(parseMetadata(row));
|
|
7540
7059
|
};
|
|
@@ -7752,8 +7271,8 @@ function createMemoryStore(channels) {
|
|
|
7752
7271
|
}));
|
|
7753
7272
|
return { streams, count: streams.length };
|
|
7754
7273
|
},
|
|
7755
|
-
async send(stream, group, type, data,
|
|
7756
|
-
notify(channels, stream, group,
|
|
7274
|
+
async send(stream, group, type, data, id3) {
|
|
7275
|
+
notify(channels, stream, group, id3 ?? "", "send", { type, data });
|
|
7757
7276
|
},
|
|
7758
7277
|
async update(stream, group, item, ops) {
|
|
7759
7278
|
const k = key(stream, group, item);
|
|
@@ -7833,8 +7352,8 @@ function createPgStore(channels, pg) {
|
|
|
7833
7352
|
}));
|
|
7834
7353
|
return { streams, count: streams.length };
|
|
7835
7354
|
},
|
|
7836
|
-
async send(stream, group, type, data,
|
|
7837
|
-
notify(channels, stream, group,
|
|
7355
|
+
async send(stream, group, type, data, id3) {
|
|
7356
|
+
notify(channels, stream, group, id3 ?? "", "send", { type, data });
|
|
7838
7357
|
},
|
|
7839
7358
|
async update(stream, group, item, ops) {
|
|
7840
7359
|
const { value: oldVal } = await this.get(stream, group, item);
|
|
@@ -7930,8 +7449,8 @@ function createRedisStore(channels, redis2, ttl) {
|
|
|
7930
7449
|
}));
|
|
7931
7450
|
return { streams, count: streams.length };
|
|
7932
7451
|
},
|
|
7933
|
-
async send(stream, group, type, data,
|
|
7934
|
-
notify(channels, stream, group,
|
|
7452
|
+
async send(stream, group, type, data, id3) {
|
|
7453
|
+
notify(channels, stream, group, id3 ?? "", "send", { type, data });
|
|
7935
7454
|
},
|
|
7936
7455
|
async update(stream, group, item, ops) {
|
|
7937
7456
|
const hk = hashKey(stream, group);
|
|
@@ -8144,9 +7663,9 @@ function iii(opts = {}) {
|
|
|
8144
7663
|
const functions = /* @__PURE__ */ new Map();
|
|
8145
7664
|
const triggers = /* @__PURE__ */ new Map();
|
|
8146
7665
|
const pending = /* @__PURE__ */ new Map();
|
|
8147
|
-
function registerBuiltin(
|
|
8148
|
-
functions.set(
|
|
8149
|
-
id:
|
|
7666
|
+
function registerBuiltin(id3, handler) {
|
|
7667
|
+
functions.set(id3, {
|
|
7668
|
+
id: id3,
|
|
8150
7669
|
handler,
|
|
8151
7670
|
workerId: "__iii__",
|
|
8152
7671
|
workerName: "__iii__",
|
|
@@ -8201,34 +7720,34 @@ function iii(opts = {}) {
|
|
|
8201
7720
|
}
|
|
8202
7721
|
workers.set(workerId, reg);
|
|
8203
7722
|
}
|
|
8204
|
-
function addRemoteFunction(workerId,
|
|
7723
|
+
function addRemoteFunction(workerId, id3) {
|
|
8205
7724
|
const worker = workers.get(workerId);
|
|
8206
7725
|
if (!worker) return;
|
|
8207
7726
|
const handler = async (payload) => {
|
|
8208
7727
|
if (!worker.ws) throw new Error(`Worker "${worker.name}" disconnected`);
|
|
8209
7728
|
const invocationId = crypto6.randomUUID();
|
|
8210
|
-
return new Promise((
|
|
7729
|
+
return new Promise((resolve12, reject) => {
|
|
8211
7730
|
const timer = setTimeout(() => {
|
|
8212
7731
|
pending.delete(invocationId);
|
|
8213
|
-
reject(new Error(`Invocation timed out for "${
|
|
7732
|
+
reject(new Error(`Invocation timed out for "${id3}"`));
|
|
8214
7733
|
}, 3e4);
|
|
8215
|
-
pending.set(invocationId, { resolve:
|
|
7734
|
+
pending.set(invocationId, { resolve: resolve12, reject, timer });
|
|
8216
7735
|
worker.ws.send(JSON.stringify({
|
|
8217
7736
|
type: "invoke",
|
|
8218
7737
|
invocation_id: invocationId,
|
|
8219
|
-
function_id:
|
|
7738
|
+
function_id: id3,
|
|
8220
7739
|
payload
|
|
8221
7740
|
}));
|
|
8222
7741
|
});
|
|
8223
7742
|
};
|
|
8224
7743
|
const fnReg = {
|
|
8225
|
-
id:
|
|
7744
|
+
id: id3,
|
|
8226
7745
|
handler,
|
|
8227
7746
|
workerId,
|
|
8228
7747
|
workerName: worker.name,
|
|
8229
7748
|
triggers: []
|
|
8230
7749
|
};
|
|
8231
|
-
functions.set(
|
|
7750
|
+
functions.set(id3, fnReg);
|
|
8232
7751
|
worker.functions.push(fnReg);
|
|
8233
7752
|
}
|
|
8234
7753
|
function removeWorker(workerId) {
|
|
@@ -8241,15 +7760,15 @@ function iii(opts = {}) {
|
|
|
8241
7760
|
let engineRef = null;
|
|
8242
7761
|
const wsHandler = createWsHandler({
|
|
8243
7762
|
registerRemoteWorker(ws, name) {
|
|
8244
|
-
const
|
|
8245
|
-
workers.set(
|
|
8246
|
-
return
|
|
7763
|
+
const id3 = crypto6.randomUUID();
|
|
7764
|
+
workers.set(id3, { id: id3, name, ws, functions: [], triggers: [] });
|
|
7765
|
+
return id3;
|
|
8247
7766
|
},
|
|
8248
7767
|
unregisterRemoteWorker(workerId) {
|
|
8249
7768
|
removeWorker(workerId);
|
|
8250
7769
|
},
|
|
8251
|
-
registerRemoteFunction(workerId,
|
|
8252
|
-
addRemoteFunction(workerId,
|
|
7770
|
+
registerRemoteFunction(workerId, id3) {
|
|
7771
|
+
addRemoteFunction(workerId, id3);
|
|
8253
7772
|
},
|
|
8254
7773
|
registerRemoteTrigger(workerId, input) {
|
|
8255
7774
|
const tid = crypto6.randomUUID();
|
|
@@ -8258,11 +7777,11 @@ function iii(opts = {}) {
|
|
|
8258
7777
|
const worker = workers.get(workerId);
|
|
8259
7778
|
if (worker) worker.triggers.push(reg);
|
|
8260
7779
|
},
|
|
8261
|
-
unregisterRemoteFunction(workerId,
|
|
8262
|
-
functions.delete(
|
|
7780
|
+
unregisterRemoteFunction(workerId, id3) {
|
|
7781
|
+
functions.delete(id3);
|
|
8263
7782
|
const worker = workers.get(workerId);
|
|
8264
7783
|
if (worker) {
|
|
8265
|
-
worker.functions = worker.functions.filter((f) => f.id !==
|
|
7784
|
+
worker.functions = worker.functions.filter((f) => f.id !== id3);
|
|
8266
7785
|
}
|
|
8267
7786
|
},
|
|
8268
7787
|
unregisterRemoteTrigger(workerId, functionId) {
|
|
@@ -8397,12 +7916,12 @@ function createWorker(name) {
|
|
|
8397
7916
|
const triggers = /* @__PURE__ */ new Map();
|
|
8398
7917
|
return {
|
|
8399
7918
|
name,
|
|
8400
|
-
registerFunction(
|
|
8401
|
-
functions.set(
|
|
7919
|
+
registerFunction(id3, handler) {
|
|
7920
|
+
functions.set(id3, handler);
|
|
8402
7921
|
return this;
|
|
8403
7922
|
},
|
|
8404
|
-
unregisterFunction(
|
|
8405
|
-
functions.delete(
|
|
7923
|
+
unregisterFunction(id3) {
|
|
7924
|
+
functions.delete(id3);
|
|
8406
7925
|
return this;
|
|
8407
7926
|
},
|
|
8408
7927
|
registerTrigger(input) {
|
|
@@ -8414,10 +7933,10 @@ function createWorker(name) {
|
|
|
8414
7933
|
return this;
|
|
8415
7934
|
},
|
|
8416
7935
|
getFunctions() {
|
|
8417
|
-
return Array.from(functions.entries()).map(([
|
|
7936
|
+
return Array.from(functions.entries()).map(([id3, handler]) => ({ id: id3, handler }));
|
|
8418
7937
|
},
|
|
8419
7938
|
getTriggers() {
|
|
8420
|
-
return Array.from(triggers.entries()).map(([
|
|
7939
|
+
return Array.from(triggers.entries()).map(([id3, input]) => ({ id: id3, input }));
|
|
8421
7940
|
}
|
|
8422
7941
|
};
|
|
8423
7942
|
}
|
|
@@ -8453,8 +7972,8 @@ function registerWorker(url) {
|
|
|
8453
7972
|
function connect() {
|
|
8454
7973
|
if (intentionalClose) return;
|
|
8455
7974
|
ws = new WebSocket(url);
|
|
8456
|
-
ready = new Promise((
|
|
8457
|
-
resolveReady =
|
|
7975
|
+
ready = new Promise((resolve12) => {
|
|
7976
|
+
resolveReady = resolve12;
|
|
8458
7977
|
});
|
|
8459
7978
|
ws.onopen = () => {
|
|
8460
7979
|
reconnectAttempt = 0;
|
|
@@ -8537,15 +8056,15 @@ function registerWorker(url) {
|
|
|
8537
8056
|
}
|
|
8538
8057
|
connect();
|
|
8539
8058
|
return {
|
|
8540
|
-
registerFunction(
|
|
8541
|
-
handlers.set(
|
|
8542
|
-
registeredFunctionIds.add(
|
|
8543
|
-
send({ type: "register_function", id:
|
|
8059
|
+
registerFunction(id3, handler) {
|
|
8060
|
+
handlers.set(id3, handler);
|
|
8061
|
+
registeredFunctionIds.add(id3);
|
|
8062
|
+
send({ type: "register_function", id: id3 });
|
|
8544
8063
|
},
|
|
8545
|
-
unregisterFunction(
|
|
8546
|
-
handlers.delete(
|
|
8547
|
-
registeredFunctionIds.delete(
|
|
8548
|
-
send({ type: "unregister_function", id:
|
|
8064
|
+
unregisterFunction(id3) {
|
|
8065
|
+
handlers.delete(id3);
|
|
8066
|
+
registeredFunctionIds.delete(id3);
|
|
8067
|
+
send({ type: "unregister_function", id: id3 });
|
|
8549
8068
|
},
|
|
8550
8069
|
registerTrigger(input) {
|
|
8551
8070
|
registeredTriggers.add(JSON.stringify(input));
|
|
@@ -8574,13 +8093,13 @@ function registerWorker(url) {
|
|
|
8574
8093
|
}
|
|
8575
8094
|
return Promise.resolve(fn(request.payload, ctx));
|
|
8576
8095
|
}
|
|
8577
|
-
return new Promise((
|
|
8096
|
+
return new Promise((resolve12, reject) => {
|
|
8578
8097
|
const invocationId = genId();
|
|
8579
8098
|
const timer = setTimeout(() => {
|
|
8580
8099
|
pendingInvocations.delete(invocationId);
|
|
8581
8100
|
reject(new Error(`Invocation timed out for "${request.function_id}"`));
|
|
8582
8101
|
}, request.timeout_ms || 3e4);
|
|
8583
|
-
pendingInvocations.set(invocationId, { resolve:
|
|
8102
|
+
pendingInvocations.set(invocationId, { resolve: resolve12, reject, timer });
|
|
8584
8103
|
send({
|
|
8585
8104
|
type: "invoke",
|
|
8586
8105
|
invocation_id: invocationId,
|
|
@@ -8655,7 +8174,6 @@ export {
|
|
|
8655
8174
|
streamText,
|
|
8656
8175
|
tenant,
|
|
8657
8176
|
tool2 as tool,
|
|
8658
|
-
tsx,
|
|
8659
8177
|
upload,
|
|
8660
8178
|
user,
|
|
8661
8179
|
validate
|