weifuwu 0.16.2 → 0.16.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +659 -1294
- package/cli.ts +103 -5
- package/dist/cli.js +98 -5
- package/dist/client-router.d.ts +296 -0
- package/dist/csrf.d.ts +8 -0
- package/dist/hub.d.ts +12 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +405 -83
- package/dist/messager/types.d.ts +2 -0
- package/dist/messager/ws.d.ts +1 -0
- package/dist/serve.d.ts +1 -0
- package/dist/use-action.d.ts +14 -0
- package/dist/use-websocket.d.ts +17 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -122,6 +122,14 @@ function serve(handler, options) {
|
|
|
122
122
|
const ready = new Promise((r) => {
|
|
123
123
|
resolveReady = r;
|
|
124
124
|
});
|
|
125
|
+
if (options?.shutdown !== false) {
|
|
126
|
+
process.on("SIGTERM", () => {
|
|
127
|
+
server.close();
|
|
128
|
+
});
|
|
129
|
+
process.on("SIGINT", () => {
|
|
130
|
+
server.close();
|
|
131
|
+
});
|
|
132
|
+
}
|
|
125
133
|
if (options?.signal) {
|
|
126
134
|
if (options.signal.aborted) {
|
|
127
135
|
server.close();
|
|
@@ -968,9 +976,16 @@ ${src}`;
|
|
|
968
976
|
`import{createElement}from'react';`,
|
|
969
977
|
`import P from${JSON.stringify(entryPath)};`,
|
|
970
978
|
`const p=window.__WEIFUWU_PROPS;`,
|
|
971
|
-
`
|
|
972
|
-
`hydrateRoot(
|
|
979
|
+
`const c=document.getElementById('__weifuwu_root');`,
|
|
980
|
+
`const r=hydrateRoot(c,createElement(P,p));`,
|
|
981
|
+
`window.__WEIFUWU_ROOT=r;`
|
|
973
982
|
].join("");
|
|
983
|
+
const publicEnv = {};
|
|
984
|
+
for (const key of Object.keys(process.env)) {
|
|
985
|
+
if (key.startsWith("WEIFUWU_PUBLIC_")) {
|
|
986
|
+
publicEnv[`process.env.${key}`] = JSON.stringify(process.env[key]);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
974
989
|
const result = await esbuild.build({
|
|
975
990
|
stdin: { contents: code, loader: "tsx", resolveDir: pagesDir },
|
|
976
991
|
bundle: true,
|
|
@@ -978,6 +993,8 @@ ${src}`;
|
|
|
978
993
|
jsx: "automatic",
|
|
979
994
|
jsxImportSource: "react",
|
|
980
995
|
alias: resolveAliases(),
|
|
996
|
+
banner: { js: "self.process={env:{}};" },
|
|
997
|
+
define: Object.keys(publicEnv).length > 0 ? publicEnv : void 0,
|
|
981
998
|
write: false,
|
|
982
999
|
minify: true
|
|
983
1000
|
});
|
|
@@ -1028,9 +1045,13 @@ ${src}`;
|
|
|
1028
1045
|
const loadFn = loadMod?.default;
|
|
1029
1046
|
const loadProps = loadFn ? await loadFn({ params: ctx.params, query: ctx.query }) : {};
|
|
1030
1047
|
const allProps = { ...loadProps, params: ctx.params, query: ctx.query };
|
|
1031
|
-
let element = createElement(
|
|
1032
|
-
|
|
1033
|
-
|
|
1048
|
+
let element = createElement(
|
|
1049
|
+
"div",
|
|
1050
|
+
{ id: "__weifuwu_root" },
|
|
1051
|
+
createElement(TsxContext.Provider, {
|
|
1052
|
+
value: { params: ctx.params, query: ctx.query, user: ctx.user, parsed: ctx.parsed }
|
|
1053
|
+
}, createElement(Component, allProps))
|
|
1054
|
+
);
|
|
1034
1055
|
if (layoutPaths.length === 0) {
|
|
1035
1056
|
element = createElement(
|
|
1036
1057
|
"html",
|
|
@@ -1042,7 +1063,7 @@ ${src}`;
|
|
|
1042
1063
|
createElement("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
|
|
1043
1064
|
createElement("title", null, "weifuwu")
|
|
1044
1065
|
),
|
|
1045
|
-
createElement("body", null,
|
|
1066
|
+
createElement("body", null, element)
|
|
1046
1067
|
);
|
|
1047
1068
|
} else {
|
|
1048
1069
|
for (let i = layoutPaths.length - 1; i >= 0; i--) {
|
|
@@ -1059,6 +1080,11 @@ ${src}`;
|
|
|
1059
1080
|
}
|
|
1060
1081
|
const stream = await renderToReadableStream(element);
|
|
1061
1082
|
const body = await readStream(stream);
|
|
1083
|
+
if (layoutPaths.length > 0 && (body.match(/__weifuwu_root/g) || []).length > 1) {
|
|
1084
|
+
console.warn(
|
|
1085
|
+
'[weifuwu/tsx] <div id="__weifuwu_root"> is auto-injected by the framework. Remove the duplicate from your root layout to avoid hydration conflicts.'
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1062
1088
|
const scripts = [];
|
|
1063
1089
|
scripts.push(`<script>window.__WEIFUWU_PROPS=${JSON.stringify(allProps)}</script>`);
|
|
1064
1090
|
const bundle = await this.getOrBuildClientBundle(entryPath, layoutPaths, this.pagesDir);
|
|
@@ -1747,7 +1773,7 @@ function upload(options) {
|
|
|
1747
1773
|
// rate-limit.ts
|
|
1748
1774
|
function rateLimit(options) {
|
|
1749
1775
|
const max = options?.max ?? 100;
|
|
1750
|
-
const
|
|
1776
|
+
const window2 = options?.window ?? 6e4;
|
|
1751
1777
|
const getKey = options?.key ?? ((req) => {
|
|
1752
1778
|
const forwarded = req.headers.get("x-forwarded-for");
|
|
1753
1779
|
if (forwarded) return forwarded.split(",")[0].trim();
|
|
@@ -1764,19 +1790,19 @@ function rateLimit(options) {
|
|
|
1764
1790
|
for (const [key, entry] of hits) {
|
|
1765
1791
|
if (entry.reset < now) hits.delete(key);
|
|
1766
1792
|
}
|
|
1767
|
-
},
|
|
1793
|
+
}, window2);
|
|
1768
1794
|
if (interval.unref) interval.unref();
|
|
1769
1795
|
const mw = async (req, ctx, next) => {
|
|
1770
1796
|
const key = getKey(req);
|
|
1771
1797
|
const now = Date.now();
|
|
1772
1798
|
const entry = hits.get(key);
|
|
1773
1799
|
if (!entry || entry.reset < now) {
|
|
1774
|
-
hits.set(key, { count: 1, reset: now +
|
|
1800
|
+
hits.set(key, { count: 1, reset: now + window2 });
|
|
1775
1801
|
const res2 = await next(req, ctx);
|
|
1776
1802
|
const headers2 = new Headers(res2.headers);
|
|
1777
1803
|
headers2.set("X-RateLimit-Limit", String(max));
|
|
1778
1804
|
headers2.set("X-RateLimit-Remaining", String(max - 1));
|
|
1779
|
-
headers2.set("X-RateLimit-Reset", String(Math.ceil((now +
|
|
1805
|
+
headers2.set("X-RateLimit-Reset", String(Math.ceil((now + window2) / 1e3)));
|
|
1780
1806
|
return new Response(res2.body, { status: res2.status, statusText: res2.statusText, headers: headers2 });
|
|
1781
1807
|
}
|
|
1782
1808
|
entry.count++;
|
|
@@ -1910,10 +1936,10 @@ function helmet(options) {
|
|
|
1910
1936
|
}
|
|
1911
1937
|
|
|
1912
1938
|
// request-id.ts
|
|
1913
|
-
import
|
|
1939
|
+
import crypto2 from "node:crypto";
|
|
1914
1940
|
function requestId(options) {
|
|
1915
1941
|
const header = options?.header ?? "X-Request-ID";
|
|
1916
|
-
const gen = options?.generator ?? (() =>
|
|
1942
|
+
const gen = options?.generator ?? (() => crypto2.randomUUID());
|
|
1917
1943
|
return async (req, ctx, next) => {
|
|
1918
1944
|
const existing = req.headers.get(header);
|
|
1919
1945
|
const id2 = existing ?? gen();
|
|
@@ -2949,7 +2975,7 @@ import jwt2 from "jsonwebtoken";
|
|
|
2949
2975
|
import { z as z2 } from "zod";
|
|
2950
2976
|
|
|
2951
2977
|
// user/oauth2.ts
|
|
2952
|
-
import
|
|
2978
|
+
import crypto3 from "node:crypto";
|
|
2953
2979
|
import jwt from "jsonwebtoken";
|
|
2954
2980
|
function createOAuth2Server(deps) {
|
|
2955
2981
|
const { pg, users, jwtSecret, expiresIn } = deps;
|
|
@@ -2968,8 +2994,8 @@ function createOAuth2Server(deps) {
|
|
|
2968
2994
|
};
|
|
2969
2995
|
}
|
|
2970
2996
|
async function registerClient(data) {
|
|
2971
|
-
const clientId =
|
|
2972
|
-
const clientSecret =
|
|
2997
|
+
const clientId = crypto3.randomUUID();
|
|
2998
|
+
const clientSecret = crypto3.randomBytes(32).toString("hex");
|
|
2973
2999
|
const [row] = await pg.sql`
|
|
2974
3000
|
INSERT INTO "_oauth2_clients" ("name", "client_id", "client_secret", "redirect_uris")
|
|
2975
3001
|
VALUES (${data.name}, ${clientId}, ${clientSecret}, ${pg.sql.array(data.redirectUris)})
|
|
@@ -3117,7 +3143,7 @@ h2{color:#dc2626}.desc{color:#555}</style>
|
|
|
3117
3143
|
const loc2 = `${redirectUri}?error=access_denied${state ? `&state=${state}` : ""}`;
|
|
3118
3144
|
return Response.redirect(loc2, 302);
|
|
3119
3145
|
}
|
|
3120
|
-
const code =
|
|
3146
|
+
const code = crypto3.randomUUID();
|
|
3121
3147
|
const expiresAt = new Date(Date.now() + 10 * 60 * 1e3);
|
|
3122
3148
|
await pg.sql`
|
|
3123
3149
|
INSERT INTO "_oauth2_codes" ("code", "client_id", "user_id", "redirect_uri", "code_challenge", "code_challenge_method", "scope", "expires_at")
|
|
@@ -3182,7 +3208,7 @@ h2{color:#dc2626}.desc{color:#555}</style>
|
|
|
3182
3208
|
if (stored.code_challenge_method === "plain") {
|
|
3183
3209
|
expected = codeVerifier;
|
|
3184
3210
|
} else {
|
|
3185
|
-
expected =
|
|
3211
|
+
expected = crypto3.createHash("sha256").update(codeVerifier).digest().toString("base64url");
|
|
3186
3212
|
}
|
|
3187
3213
|
if (expected !== stored.code_challenge) {
|
|
3188
3214
|
return Response.json({ error: "invalid_grant", error_description: "code_verifier mismatch" }, { status: 400 });
|
|
@@ -3199,7 +3225,7 @@ h2{color:#dc2626}.desc{color:#555}</style>
|
|
|
3199
3225
|
jwtSecret,
|
|
3200
3226
|
{ expiresIn }
|
|
3201
3227
|
);
|
|
3202
|
-
const refreshToken =
|
|
3228
|
+
const refreshToken = crypto3.randomUUID();
|
|
3203
3229
|
const refreshExpires = new Date(Date.now() + 30 * 24 * 60 * 60 * 1e3);
|
|
3204
3230
|
await pg.sql`
|
|
3205
3231
|
INSERT INTO "_oauth2_tokens" ("token", "client_id", "user_id", "scope", "expires_at")
|
|
@@ -3465,9 +3491,65 @@ function redis(opts) {
|
|
|
3465
3491
|
return mw;
|
|
3466
3492
|
}
|
|
3467
3493
|
|
|
3494
|
+
// hub.ts
|
|
3495
|
+
function createHub(opts) {
|
|
3496
|
+
const prefix = opts?.prefix ?? "hub:";
|
|
3497
|
+
const channels2 = /* @__PURE__ */ new Map();
|
|
3498
|
+
let redisPub;
|
|
3499
|
+
let redisSub = null;
|
|
3500
|
+
if (opts?.redis) {
|
|
3501
|
+
redisPub = opts.redis;
|
|
3502
|
+
redisSub = opts.redis.duplicate();
|
|
3503
|
+
redisSub.on("message", (rawChannel, rawData) => {
|
|
3504
|
+
if (!rawChannel.startsWith(prefix)) return;
|
|
3505
|
+
const key = rawChannel.slice(prefix.length);
|
|
3506
|
+
const members = channels2.get(key);
|
|
3507
|
+
if (!members) return;
|
|
3508
|
+
for (const ws of members) {
|
|
3509
|
+
try {
|
|
3510
|
+
ws.send(rawData);
|
|
3511
|
+
} catch {
|
|
3512
|
+
}
|
|
3513
|
+
}
|
|
3514
|
+
});
|
|
3515
|
+
}
|
|
3516
|
+
function join5(key, ws) {
|
|
3517
|
+
if (!channels2.has(key)) {
|
|
3518
|
+
channels2.set(key, /* @__PURE__ */ new Set());
|
|
3519
|
+
redisSub?.subscribe(`${prefix}${key}`);
|
|
3520
|
+
}
|
|
3521
|
+
channels2.get(key).add(ws);
|
|
3522
|
+
}
|
|
3523
|
+
function leave(ws) {
|
|
3524
|
+
for (const [, members] of channels2) {
|
|
3525
|
+
members.delete(ws);
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
function broadcast(key, data) {
|
|
3529
|
+
const msg = JSON.stringify(data);
|
|
3530
|
+
const members = channels2.get(key);
|
|
3531
|
+
if (members) {
|
|
3532
|
+
for (const ws of members) {
|
|
3533
|
+
try {
|
|
3534
|
+
ws.send(msg);
|
|
3535
|
+
} catch {
|
|
3536
|
+
}
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
redisPub?.publish(`${prefix}${key}`, msg);
|
|
3540
|
+
}
|
|
3541
|
+
async function close() {
|
|
3542
|
+
channels2.clear();
|
|
3543
|
+
if (redisSub) {
|
|
3544
|
+
await redisSub.quit();
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
return { join: join5, leave, broadcast, close };
|
|
3548
|
+
}
|
|
3549
|
+
|
|
3468
3550
|
// queue/index.ts
|
|
3469
3551
|
import { Redis as IORedis2 } from "ioredis";
|
|
3470
|
-
import
|
|
3552
|
+
import crypto4 from "node:crypto";
|
|
3471
3553
|
function cronNext(expr, from = /* @__PURE__ */ new Date()) {
|
|
3472
3554
|
const parts = expr.trim().split(/\s+/);
|
|
3473
3555
|
if (parts.length !== 5) throw new Error(`Invalid cron expression "${expr}": expected 5 fields`);
|
|
@@ -3558,7 +3640,7 @@ function queue(opts) {
|
|
|
3558
3640
|
if (job.schedule) {
|
|
3559
3641
|
try {
|
|
3560
3642
|
const nextRun = cronNext(job.schedule);
|
|
3561
|
-
const nextJob = { ...job, id:
|
|
3643
|
+
const nextJob = { ...job, id: crypto4.randomUUID(), runAt: nextRun, createdAt: Date.now() };
|
|
3562
3644
|
redis2.zadd(jobKey, nextRun, JSON.stringify(nextJob)).catch(() => {
|
|
3563
3645
|
});
|
|
3564
3646
|
} catch {
|
|
@@ -3576,7 +3658,7 @@ function queue(opts) {
|
|
|
3576
3658
|
}
|
|
3577
3659
|
}
|
|
3578
3660
|
mw.add = function add(type, payload, opts2) {
|
|
3579
|
-
const id2 =
|
|
3661
|
+
const id2 = crypto4.randomUUID();
|
|
3580
3662
|
let runAt;
|
|
3581
3663
|
if (opts2?.schedule) {
|
|
3582
3664
|
runAt = cronNext(opts2.schedule);
|
|
@@ -4809,40 +4891,17 @@ function agent(options) {
|
|
|
4809
4891
|
}
|
|
4810
4892
|
|
|
4811
4893
|
// messager/ws.ts
|
|
4812
|
-
var channels = /* @__PURE__ */ new Map();
|
|
4813
4894
|
var userConnections = /* @__PURE__ */ new Map();
|
|
4895
|
+
var hub;
|
|
4814
4896
|
function broadcastToChannel(channelId, data) {
|
|
4815
|
-
|
|
4816
|
-
if (!members) return;
|
|
4817
|
-
const msg = JSON.stringify(data);
|
|
4818
|
-
for (const { ws } of members) {
|
|
4819
|
-
try {
|
|
4820
|
-
ws.send(msg);
|
|
4821
|
-
} catch {
|
|
4822
|
-
}
|
|
4823
|
-
}
|
|
4824
|
-
}
|
|
4825
|
-
function subscribe(ws, userId, channelId) {
|
|
4826
|
-
if (!channels.has(channelId)) channels.set(channelId, /* @__PURE__ */ new Set());
|
|
4827
|
-
channels.get(channelId).add({ ws, userId });
|
|
4828
|
-
if (!userConnections.has(userId)) userConnections.set(userId, /* @__PURE__ */ new Set());
|
|
4829
|
-
userConnections.get(userId).add(ws);
|
|
4830
|
-
}
|
|
4831
|
-
function unsubscribe(ws) {
|
|
4832
|
-
for (const [, members] of channels) {
|
|
4833
|
-
for (const m of members) {
|
|
4834
|
-
if (m.ws === ws) {
|
|
4835
|
-
members.delete(m);
|
|
4836
|
-
break;
|
|
4837
|
-
}
|
|
4838
|
-
}
|
|
4839
|
-
}
|
|
4840
|
-
for (const [, conns] of userConnections) {
|
|
4841
|
-
conns.delete(ws);
|
|
4842
|
-
}
|
|
4897
|
+
hub?.broadcast(`messager:${channelId}`, data);
|
|
4843
4898
|
}
|
|
4844
4899
|
function createWSHandler(deps) {
|
|
4845
4900
|
const { sql: sql2, agents } = deps;
|
|
4901
|
+
hub = createHub({
|
|
4902
|
+
redis: deps.redis,
|
|
4903
|
+
prefix: "messager:"
|
|
4904
|
+
});
|
|
4846
4905
|
return {
|
|
4847
4906
|
open(ws, ctx) {
|
|
4848
4907
|
const userId = ctx.user?.id;
|
|
@@ -4871,8 +4930,10 @@ function createWSHandler(deps) {
|
|
|
4871
4930
|
RETURNING *
|
|
4872
4931
|
`;
|
|
4873
4932
|
const message = row;
|
|
4933
|
+
hub.join(`messager:${channel_id}`, ws);
|
|
4934
|
+
if (!userConnections.has(userId)) userConnections.set(userId, /* @__PURE__ */ new Set());
|
|
4935
|
+
userConnections.get(userId).add(ws);
|
|
4874
4936
|
broadcastToChannel(channel_id, { type: "message", data: message });
|
|
4875
|
-
subscribe(ws, userId, channel_id);
|
|
4876
4937
|
if (agents) {
|
|
4877
4938
|
const agentMembers = await sql2`
|
|
4878
4939
|
SELECT member_id FROM "_channel_members"
|
|
@@ -4898,7 +4959,9 @@ function createWSHandler(deps) {
|
|
|
4898
4959
|
break;
|
|
4899
4960
|
}
|
|
4900
4961
|
case "typing": {
|
|
4901
|
-
if (channel_id)
|
|
4962
|
+
if (channel_id) {
|
|
4963
|
+
hub.join(`messager:${channel_id}`, ws);
|
|
4964
|
+
}
|
|
4902
4965
|
broadcastToChannel(channel_id, {
|
|
4903
4966
|
type: "typing",
|
|
4904
4967
|
channel_id,
|
|
@@ -4909,7 +4972,7 @@ function createWSHandler(deps) {
|
|
|
4909
4972
|
}
|
|
4910
4973
|
case "read": {
|
|
4911
4974
|
if (!channel_id || !last_message_id) return;
|
|
4912
|
-
|
|
4975
|
+
hub.join(`messager:${channel_id}`, ws);
|
|
4913
4976
|
await sql2`
|
|
4914
4977
|
UPDATE "_channel_members"
|
|
4915
4978
|
SET last_read_id = ${last_message_id}, last_read_at = NOW()
|
|
@@ -4926,22 +4989,28 @@ function createWSHandler(deps) {
|
|
|
4926
4989
|
}
|
|
4927
4990
|
},
|
|
4928
4991
|
close(ws) {
|
|
4929
|
-
|
|
4992
|
+
hub?.leave(ws);
|
|
4993
|
+
for (const [, conns] of userConnections) {
|
|
4994
|
+
conns.delete(ws);
|
|
4995
|
+
}
|
|
4930
4996
|
},
|
|
4931
4997
|
error(ws) {
|
|
4932
|
-
|
|
4998
|
+
hub?.leave(ws);
|
|
4999
|
+
for (const [, conns] of userConnections) {
|
|
5000
|
+
conns.delete(ws);
|
|
5001
|
+
}
|
|
4933
5002
|
}
|
|
4934
5003
|
};
|
|
4935
5004
|
}
|
|
4936
5005
|
|
|
4937
5006
|
// messager/rest.ts
|
|
4938
5007
|
function buildRouter3(deps) {
|
|
4939
|
-
const { sql: sql2, channels:
|
|
5008
|
+
const { sql: sql2, channels: channels2, members, messages: messages2, agents } = deps;
|
|
4940
5009
|
const r = new Router();
|
|
4941
5010
|
r.post("/channels", async (req) => {
|
|
4942
5011
|
const body = await req.json();
|
|
4943
5012
|
if (!body.name) return Response.json({ error: "name is required" }, { status: 400 });
|
|
4944
|
-
const channel = await
|
|
5013
|
+
const channel = await channels2.insert({
|
|
4945
5014
|
name: body.name,
|
|
4946
5015
|
type: body.type || "channel",
|
|
4947
5016
|
created_by: body.created_by || 1
|
|
@@ -4984,14 +5053,14 @@ function buildRouter3(deps) {
|
|
|
4984
5053
|
});
|
|
4985
5054
|
r.get("/channels/:id", async (_req, ctx) => {
|
|
4986
5055
|
const id2 = parseInt(ctx.params.id, 10);
|
|
4987
|
-
const ch = await
|
|
5056
|
+
const ch = await channels2.read(id2);
|
|
4988
5057
|
if (!ch) return Response.json({ error: "Channel not found" }, { status: 404 });
|
|
4989
5058
|
const { data: memberRows } = await members.readMany({ channel_id: id2 });
|
|
4990
5059
|
return Response.json({ channel: ch, members: memberRows });
|
|
4991
5060
|
});
|
|
4992
5061
|
r.delete("/channels/:id", async (_req, ctx) => {
|
|
4993
5062
|
const id2 = parseInt(ctx.params.id, 10);
|
|
4994
|
-
await
|
|
5063
|
+
await channels2.delete(id2);
|
|
4995
5064
|
return Response.json({ ok: true });
|
|
4996
5065
|
});
|
|
4997
5066
|
r.post("/channels/:id/members", async (req, ctx) => {
|
|
@@ -5092,8 +5161,9 @@ function messager(options) {
|
|
|
5092
5161
|
const pg = options.pg;
|
|
5093
5162
|
const sql2 = pg.sql;
|
|
5094
5163
|
const agents = options.agents;
|
|
5164
|
+
const redis2 = options.redis;
|
|
5095
5165
|
const base = new PgModule(pg);
|
|
5096
|
-
const
|
|
5166
|
+
const channels2 = pg.table("_channels", {
|
|
5097
5167
|
id: serial("id").primaryKey(),
|
|
5098
5168
|
tenant_id: text("tenant_id"),
|
|
5099
5169
|
name: text("name").notNull().default(""),
|
|
@@ -5125,16 +5195,16 @@ function messager(options) {
|
|
|
5125
5195
|
});
|
|
5126
5196
|
return {
|
|
5127
5197
|
migrate: async () => {
|
|
5128
|
-
await
|
|
5129
|
-
await
|
|
5198
|
+
await channels2.create();
|
|
5199
|
+
await channels2.createIndex("tenant_id");
|
|
5130
5200
|
await members.create();
|
|
5131
5201
|
await members.createIndex("member_id");
|
|
5132
5202
|
await members.createIndex(["channel_id", "member_id", "member_type"], { unique: true });
|
|
5133
5203
|
await messages2.create();
|
|
5134
5204
|
await messages2.createIndex(["channel_id", "created_at"], { desc: true });
|
|
5135
5205
|
},
|
|
5136
|
-
router: () => buildRouter3({ sql: sql2, channels:
|
|
5137
|
-
wsHandler: () => createWSHandler({ sql: sql2, agents }),
|
|
5206
|
+
router: () => buildRouter3({ sql: sql2, channels: channels2, members, messages: messages2, agents }),
|
|
5207
|
+
wsHandler: () => createWSHandler({ sql: sql2, agents, redis: redis2 }),
|
|
5138
5208
|
async send(channelId, content, opts) {
|
|
5139
5209
|
const msg = await messages2.insert({
|
|
5140
5210
|
channel_id: channelId,
|
|
@@ -5242,7 +5312,7 @@ function createGateway(config, getPort) {
|
|
|
5242
5312
|
}
|
|
5243
5313
|
|
|
5244
5314
|
// deploy/manager.ts
|
|
5245
|
-
import
|
|
5315
|
+
import crypto5 from "node:crypto";
|
|
5246
5316
|
function createManager(config, apps, manager) {
|
|
5247
5317
|
const router = new Router();
|
|
5248
5318
|
const auth2 = (req, ctx, next) => {
|
|
@@ -5251,7 +5321,7 @@ function createManager(config, apps, manager) {
|
|
|
5251
5321
|
const token = header.replace("Bearer ", "");
|
|
5252
5322
|
const tokenBuf = Buffer.from(token);
|
|
5253
5323
|
const secretBuf = Buffer.from(config.deployToken);
|
|
5254
|
-
if (tokenBuf.length !== secretBuf.length || !
|
|
5324
|
+
if (tokenBuf.length !== secretBuf.length || !crypto5.timingSafeEqual(tokenBuf, secretBuf)) {
|
|
5255
5325
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
|
5256
5326
|
}
|
|
5257
5327
|
return next(req, ctx);
|
|
@@ -5350,10 +5420,10 @@ function createManager(config, apps, manager) {
|
|
|
5350
5420
|
const rawBody = await req.text();
|
|
5351
5421
|
if (config.webhookSecret) {
|
|
5352
5422
|
const sig = req.headers.get("x-hub-signature-256") ?? "";
|
|
5353
|
-
const expected = `sha256=${
|
|
5423
|
+
const expected = `sha256=${crypto5.createHmac("sha256", config.webhookSecret).update(rawBody).digest("hex")}`;
|
|
5354
5424
|
const sigBuf = Buffer.from(sig);
|
|
5355
5425
|
const expectedBuf = Buffer.from(expected);
|
|
5356
|
-
if (sigBuf.length !== expectedBuf.length || !
|
|
5426
|
+
if (sigBuf.length !== expectedBuf.length || !crypto5.timingSafeEqual(sigBuf, expectedBuf)) {
|
|
5357
5427
|
return Response.json({ error: "invalid signature" }, { status: 401 });
|
|
5358
5428
|
}
|
|
5359
5429
|
}
|
|
@@ -6310,7 +6380,7 @@ async function buildRouter4(deps) {
|
|
|
6310
6380
|
skills: allSkills,
|
|
6311
6381
|
systemPrompt: session.system_prompt || systemPrompt
|
|
6312
6382
|
});
|
|
6313
|
-
const
|
|
6383
|
+
const history2 = await getHistory(sql2, sessionId);
|
|
6314
6384
|
await addTextMessage(sql2, sessionId, "user", content);
|
|
6315
6385
|
const stream = executeGenerator({
|
|
6316
6386
|
sessionId,
|
|
@@ -6318,7 +6388,7 @@ async function buildRouter4(deps) {
|
|
|
6318
6388
|
model,
|
|
6319
6389
|
tools,
|
|
6320
6390
|
systemPrompt: sysPrompt,
|
|
6321
|
-
messages:
|
|
6391
|
+
messages: history2,
|
|
6322
6392
|
sql: sql2
|
|
6323
6393
|
});
|
|
6324
6394
|
return createSSEStream(stream);
|
|
@@ -6398,7 +6468,7 @@ function createWSHandler2(deps) {
|
|
|
6398
6468
|
skills: allSkills,
|
|
6399
6469
|
systemPrompt: session.system_prompt || systemPrompt
|
|
6400
6470
|
});
|
|
6401
|
-
const
|
|
6471
|
+
const history2 = await getHistory(sql2, session_id);
|
|
6402
6472
|
await addTextMessage(sql2, session_id, "user", content);
|
|
6403
6473
|
const stream = executeGenerator({
|
|
6404
6474
|
sessionId: session_id,
|
|
@@ -6406,7 +6476,7 @@ function createWSHandler2(deps) {
|
|
|
6406
6476
|
model,
|
|
6407
6477
|
tools,
|
|
6408
6478
|
systemPrompt: sysPrompt,
|
|
6409
|
-
messages:
|
|
6479
|
+
messages: history2,
|
|
6410
6480
|
sql: sql2,
|
|
6411
6481
|
abortSignal: controller.signal
|
|
6412
6482
|
});
|
|
@@ -6843,6 +6913,251 @@ function mailer(options) {
|
|
|
6843
6913
|
return { send, close };
|
|
6844
6914
|
}
|
|
6845
6915
|
|
|
6916
|
+
// use-websocket.ts
|
|
6917
|
+
import { useEffect, useRef, useCallback, useState } from "react";
|
|
6918
|
+
var RECONNECT_DELAY = 3e3;
|
|
6919
|
+
var MAX_RETRIES = 10;
|
|
6920
|
+
function resolveUrl(url) {
|
|
6921
|
+
return typeof url === "function" ? url() : url;
|
|
6922
|
+
}
|
|
6923
|
+
function useWebsocket(url, options) {
|
|
6924
|
+
const { onMessage, reconnect: reconnectOpt = true, protocols, enabled = true } = options ?? {};
|
|
6925
|
+
const [lastMessage, setLastMessage] = useState(null);
|
|
6926
|
+
const [readyState, setReadyState] = useState(WebSocket.CLOSED);
|
|
6927
|
+
const wsRef = useRef(null);
|
|
6928
|
+
const retryRef = useRef(0);
|
|
6929
|
+
const timerRef = useRef(void 0);
|
|
6930
|
+
const mountedRef = useRef(true);
|
|
6931
|
+
const shouldReconnectRef = useRef(true);
|
|
6932
|
+
const urlRef = useRef(url);
|
|
6933
|
+
const optsRef = useRef({ onMessage, reconnectOpt, protocols });
|
|
6934
|
+
urlRef.current = url;
|
|
6935
|
+
optsRef.current = { onMessage, reconnectOpt, protocols };
|
|
6936
|
+
const cleanup = useCallback(() => {
|
|
6937
|
+
clearTimeout(timerRef.current);
|
|
6938
|
+
wsRef.current?.close();
|
|
6939
|
+
wsRef.current = null;
|
|
6940
|
+
}, []);
|
|
6941
|
+
const connect = useCallback(() => {
|
|
6942
|
+
if (!mountedRef.current || !enabled) return;
|
|
6943
|
+
const resolved = resolveUrl(urlRef.current);
|
|
6944
|
+
if (!resolved) return;
|
|
6945
|
+
wsRef.current?.close();
|
|
6946
|
+
const ws = new WebSocket(resolved, optsRef.current.protocols);
|
|
6947
|
+
wsRef.current = ws;
|
|
6948
|
+
setReadyState(WebSocket.CONNECTING);
|
|
6949
|
+
ws.addEventListener("open", () => {
|
|
6950
|
+
if (!mountedRef.current) return;
|
|
6951
|
+
retryRef.current = 0;
|
|
6952
|
+
setReadyState(WebSocket.OPEN);
|
|
6953
|
+
});
|
|
6954
|
+
ws.addEventListener("message", (e) => {
|
|
6955
|
+
if (!mountedRef.current) return;
|
|
6956
|
+
const data = typeof e.data === "string" ? e.data : String(e.data);
|
|
6957
|
+
setLastMessage(data);
|
|
6958
|
+
optsRef.current.onMessage?.(data);
|
|
6959
|
+
});
|
|
6960
|
+
ws.addEventListener("close", () => {
|
|
6961
|
+
if (!mountedRef.current) return;
|
|
6962
|
+
setReadyState(WebSocket.CLOSED);
|
|
6963
|
+
const ro = optsRef.current.reconnectOpt;
|
|
6964
|
+
if (ro && shouldReconnectRef.current && mountedRef.current) {
|
|
6965
|
+
const maxRetries = typeof ro === "object" ? ro.maxRetries ?? MAX_RETRIES : MAX_RETRIES;
|
|
6966
|
+
const delay = typeof ro === "object" ? ro.delay ?? RECONNECT_DELAY : RECONNECT_DELAY;
|
|
6967
|
+
if (retryRef.current < maxRetries) {
|
|
6968
|
+
retryRef.current++;
|
|
6969
|
+
timerRef.current = setTimeout(() => connect(), delay);
|
|
6970
|
+
}
|
|
6971
|
+
}
|
|
6972
|
+
});
|
|
6973
|
+
}, [enabled]);
|
|
6974
|
+
useEffect(() => {
|
|
6975
|
+
mountedRef.current = true;
|
|
6976
|
+
shouldReconnectRef.current = true;
|
|
6977
|
+
if (enabled) connect();
|
|
6978
|
+
return () => {
|
|
6979
|
+
mountedRef.current = false;
|
|
6980
|
+
cleanup();
|
|
6981
|
+
};
|
|
6982
|
+
}, [enabled, connect, cleanup]);
|
|
6983
|
+
const send = useCallback((data) => {
|
|
6984
|
+
wsRef.current?.send(data);
|
|
6985
|
+
}, []);
|
|
6986
|
+
const close = useCallback(() => {
|
|
6987
|
+
shouldReconnectRef.current = false;
|
|
6988
|
+
cleanup();
|
|
6989
|
+
setReadyState(WebSocket.CLOSED);
|
|
6990
|
+
}, [cleanup]);
|
|
6991
|
+
const reconnectFn = useCallback(() => {
|
|
6992
|
+
retryRef.current = 0;
|
|
6993
|
+
shouldReconnectRef.current = true;
|
|
6994
|
+
cleanup();
|
|
6995
|
+
connect();
|
|
6996
|
+
}, [cleanup, connect]);
|
|
6997
|
+
return { send, close, readyState, lastMessage, reconnect: reconnectFn };
|
|
6998
|
+
}
|
|
6999
|
+
|
|
7000
|
+
// use-action.ts
|
|
7001
|
+
import { useState as useState2, useCallback as useCallback2, useRef as useRef2 } from "react";
|
|
7002
|
+
function getCsrfToken() {
|
|
7003
|
+
if (typeof document === "undefined") return void 0;
|
|
7004
|
+
const match = document.cookie.match(/(?:^|;\s*)_csrf=([^;]+)/);
|
|
7005
|
+
return match ? decodeURIComponent(match[1]) : void 0;
|
|
7006
|
+
}
|
|
7007
|
+
function useAction(url, options) {
|
|
7008
|
+
const { method = "POST", headers, onSuccess, onError } = options ?? {};
|
|
7009
|
+
const [data, setData] = useState2(null);
|
|
7010
|
+
const [error, setError] = useState2(null);
|
|
7011
|
+
const [pending, setPending] = useState2(false);
|
|
7012
|
+
const mountedRef = useRef2(true);
|
|
7013
|
+
const submit = useCallback2(async (body) => {
|
|
7014
|
+
setPending(true);
|
|
7015
|
+
setError(null);
|
|
7016
|
+
try {
|
|
7017
|
+
const csrfToken = getCsrfToken();
|
|
7018
|
+
const hdrs = { ...headers };
|
|
7019
|
+
if (csrfToken) hdrs["x-csrf-token"] = csrfToken;
|
|
7020
|
+
if (body && typeof body === "object" && !(body instanceof FormData)) {
|
|
7021
|
+
hdrs["content-type"] = "application/json";
|
|
7022
|
+
}
|
|
7023
|
+
const res = await fetch(url, {
|
|
7024
|
+
method,
|
|
7025
|
+
headers: hdrs,
|
|
7026
|
+
body: body instanceof FormData ? body : body !== void 0 ? JSON.stringify(body) : void 0
|
|
7027
|
+
});
|
|
7028
|
+
if (!res.ok) {
|
|
7029
|
+
const text2 = await res.text();
|
|
7030
|
+
throw new Error(text2 || `HTTP ${res.status}`);
|
|
7031
|
+
}
|
|
7032
|
+
const result = res.status === 204 ? void 0 : await res.json();
|
|
7033
|
+
if (mountedRef.current) {
|
|
7034
|
+
setData(result);
|
|
7035
|
+
onSuccess?.(result);
|
|
7036
|
+
}
|
|
7037
|
+
return result;
|
|
7038
|
+
} catch (err) {
|
|
7039
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
7040
|
+
if (mountedRef.current) {
|
|
7041
|
+
setError(e);
|
|
7042
|
+
onError?.(e);
|
|
7043
|
+
}
|
|
7044
|
+
return void 0;
|
|
7045
|
+
} finally {
|
|
7046
|
+
if (mountedRef.current) setPending(false);
|
|
7047
|
+
}
|
|
7048
|
+
}, [url, method, headers, onSuccess, onError]);
|
|
7049
|
+
const reset = useCallback2(() => {
|
|
7050
|
+
setData(null);
|
|
7051
|
+
setError(null);
|
|
7052
|
+
}, []);
|
|
7053
|
+
return { submit, data, error, pending, reset };
|
|
7054
|
+
}
|
|
7055
|
+
|
|
7056
|
+
// client-router.ts
|
|
7057
|
+
import { createElement as createElement2, useCallback as useCallback3 } from "react";
|
|
7058
|
+
async function navigate(href) {
|
|
7059
|
+
if (typeof document === "undefined") return;
|
|
7060
|
+
const url = new URL(href, location.origin);
|
|
7061
|
+
if (url.origin !== location.origin) {
|
|
7062
|
+
location.href = href;
|
|
7063
|
+
return;
|
|
7064
|
+
}
|
|
7065
|
+
const html = await fetch(url.pathname + url.search, {
|
|
7066
|
+
headers: { accept: "text/html" }
|
|
7067
|
+
}).then((r) => r.text());
|
|
7068
|
+
const doc = new DOMParser().parseFromString(html, "text/html");
|
|
7069
|
+
const rootEl = doc.getElementById("__weifuwu_root");
|
|
7070
|
+
if (!rootEl) {
|
|
7071
|
+
location.href = href;
|
|
7072
|
+
return;
|
|
7073
|
+
}
|
|
7074
|
+
const newHtml = rootEl.innerHTML;
|
|
7075
|
+
const propsMatch = html.match(/window\.__WEIFUWU_PROPS=(.+?)<\/script>/);
|
|
7076
|
+
if (!propsMatch) {
|
|
7077
|
+
location.href = href;
|
|
7078
|
+
return;
|
|
7079
|
+
}
|
|
7080
|
+
const bundleMatch = html.match(/src="(\/__wfw\/client\/[^"]+\.js)"/);
|
|
7081
|
+
const bundleUrl = bundleMatch ? bundleMatch[1] : null;
|
|
7082
|
+
const currentRoot = document.getElementById("__weifuwu_root");
|
|
7083
|
+
if (!currentRoot) {
|
|
7084
|
+
location.href = href;
|
|
7085
|
+
return;
|
|
7086
|
+
}
|
|
7087
|
+
;
|
|
7088
|
+
window.__WEIFUWU_ROOT?.unmount();
|
|
7089
|
+
currentRoot.innerHTML = newHtml;
|
|
7090
|
+
window.__WEIFUWU_PROPS = JSON.parse(propsMatch[1]);
|
|
7091
|
+
history.pushState(null, "", url.pathname + url.search);
|
|
7092
|
+
if (bundleUrl) {
|
|
7093
|
+
const cacheBust = bundleUrl.includes("?") ? "&_t=" : "?_t=";
|
|
7094
|
+
try {
|
|
7095
|
+
await import(
|
|
7096
|
+
/* @vite-ignore */
|
|
7097
|
+
`${bundleUrl}${cacheBust}${Date.now()}`
|
|
7098
|
+
);
|
|
7099
|
+
} catch (e) {
|
|
7100
|
+
console.error("[weifuwu/router] hydration failed:", e);
|
|
7101
|
+
}
|
|
7102
|
+
}
|
|
7103
|
+
}
|
|
7104
|
+
function useNavigate() {
|
|
7105
|
+
return useCallback3((href) => navigate(href), []);
|
|
7106
|
+
}
|
|
7107
|
+
function Link({ href, children, onClick, ...props }) {
|
|
7108
|
+
const handleClick = useCallback3((e) => {
|
|
7109
|
+
if (e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
7110
|
+
e.preventDefault();
|
|
7111
|
+
navigate(href);
|
|
7112
|
+
onClick?.(e);
|
|
7113
|
+
}, [href, onClick]);
|
|
7114
|
+
return createElement2("a", { href, onClick: handleClick, ...props }, children);
|
|
7115
|
+
}
|
|
7116
|
+
|
|
7117
|
+
// csrf.ts
|
|
7118
|
+
function csrf(options) {
|
|
7119
|
+
const cookieName = options?.cookie ?? "_csrf";
|
|
7120
|
+
const headerName = options?.header ?? "x-csrf-token";
|
|
7121
|
+
const bodyKey = options?.key ?? "_csrf";
|
|
7122
|
+
const excluded = new Set(options?.excludeMethods ?? ["GET", "HEAD", "OPTIONS"]);
|
|
7123
|
+
return async (req, ctx, next) => {
|
|
7124
|
+
const method = req.method.toUpperCase();
|
|
7125
|
+
if (excluded.has(method)) {
|
|
7126
|
+
let token = getCookies(req)[cookieName];
|
|
7127
|
+
if (!token) {
|
|
7128
|
+
token = crypto.randomUUID();
|
|
7129
|
+
ctx.csrfToken = token;
|
|
7130
|
+
} else {
|
|
7131
|
+
;
|
|
7132
|
+
ctx.csrfToken = token;
|
|
7133
|
+
}
|
|
7134
|
+
const res = await next(req, ctx);
|
|
7135
|
+
const tokenToSet = ctx.csrfToken;
|
|
7136
|
+
if (tokenToSet && !getCookies(req)[cookieName]) {
|
|
7137
|
+
return setCookie(res, cookieName, tokenToSet, {
|
|
7138
|
+
httpOnly: true,
|
|
7139
|
+
sameSite: "strict",
|
|
7140
|
+
path: "/"
|
|
7141
|
+
});
|
|
7142
|
+
}
|
|
7143
|
+
return res;
|
|
7144
|
+
}
|
|
7145
|
+
const cookieToken = getCookies(req)[cookieName];
|
|
7146
|
+
let headerToken = req.headers.get(headerName);
|
|
7147
|
+
if (!headerToken) {
|
|
7148
|
+
try {
|
|
7149
|
+
const body = await req.clone().json();
|
|
7150
|
+
headerToken = body[bodyKey];
|
|
7151
|
+
} catch {
|
|
7152
|
+
}
|
|
7153
|
+
}
|
|
7154
|
+
if (!cookieToken || !headerToken || cookieToken !== headerToken) {
|
|
7155
|
+
return new Response("CSRF token mismatch", { status: 403 });
|
|
7156
|
+
}
|
|
7157
|
+
return next(req, ctx);
|
|
7158
|
+
};
|
|
7159
|
+
}
|
|
7160
|
+
|
|
6846
7161
|
// logdb/rest.ts
|
|
6847
7162
|
function createHandler(entries) {
|
|
6848
7163
|
return async (req, ctx) => {
|
|
@@ -7010,10 +7325,10 @@ function logdb(options) {
|
|
|
7010
7325
|
}
|
|
7011
7326
|
|
|
7012
7327
|
// iii/client.ts
|
|
7013
|
-
import
|
|
7328
|
+
import crypto6 from "node:crypto";
|
|
7014
7329
|
|
|
7015
7330
|
// iii/stream.ts
|
|
7016
|
-
var
|
|
7331
|
+
var channels = /* @__PURE__ */ new Map();
|
|
7017
7332
|
function notify(stream, group, item, event, data) {
|
|
7018
7333
|
const keys = [
|
|
7019
7334
|
`${stream}`,
|
|
@@ -7022,7 +7337,7 @@ function notify(stream, group, item, event, data) {
|
|
|
7022
7337
|
];
|
|
7023
7338
|
const msg = JSON.stringify({ type: "stream", stream_name: stream, group_id: group, item_id: item, event, data });
|
|
7024
7339
|
for (const key of keys) {
|
|
7025
|
-
const subs =
|
|
7340
|
+
const subs = channels.get(key);
|
|
7026
7341
|
if (!subs) continue;
|
|
7027
7342
|
for (const ws of subs) {
|
|
7028
7343
|
try {
|
|
@@ -7347,14 +7662,14 @@ function createStream(opts) {
|
|
|
7347
7662
|
...store,
|
|
7348
7663
|
subscribe(ws, sub) {
|
|
7349
7664
|
const key = sub.item_id ? `${sub.stream_name}:${sub.group_id}:${sub.item_id}` : sub.group_id ? `${sub.stream_name}:${sub.group_id}` : sub.stream_name;
|
|
7350
|
-
if (!
|
|
7351
|
-
|
|
7665
|
+
if (!channels.has(key)) channels.set(key, /* @__PURE__ */ new Set());
|
|
7666
|
+
channels.get(key).add(ws);
|
|
7352
7667
|
if (redisSub && sub.stream_name) {
|
|
7353
7668
|
redisSub.subscribe(`iii:stream:${sub.stream_name}`);
|
|
7354
7669
|
}
|
|
7355
7670
|
},
|
|
7356
7671
|
unsubscribe(ws) {
|
|
7357
|
-
for (const [, subs] of
|
|
7672
|
+
for (const [, subs] of channels) subs.delete(ws);
|
|
7358
7673
|
},
|
|
7359
7674
|
async migrate() {
|
|
7360
7675
|
if (opts?.pg) {
|
|
@@ -7536,7 +7851,7 @@ function iii(opts = {}) {
|
|
|
7536
7851
|
registerBuiltin("stream::send", (p) => stream.send(p.stream_name, p.group_id, p.type, p.data, p.id));
|
|
7537
7852
|
registerBuiltin("stream::update", (p) => stream.update(p.stream_name, p.group_id, p.item_id, p.ops));
|
|
7538
7853
|
function addLocalWorker(worker) {
|
|
7539
|
-
const workerId =
|
|
7854
|
+
const workerId = crypto6.randomUUID();
|
|
7540
7855
|
const reg = {
|
|
7541
7856
|
id: workerId,
|
|
7542
7857
|
name: worker.name,
|
|
@@ -7551,7 +7866,7 @@ function iii(opts = {}) {
|
|
|
7551
7866
|
const triggerIds = [];
|
|
7552
7867
|
for (const t of worker.getTriggers()) {
|
|
7553
7868
|
if (t.input.function_id === fn.id) {
|
|
7554
|
-
const tid =
|
|
7869
|
+
const tid = crypto6.randomUUID();
|
|
7555
7870
|
triggers.set(tid, {
|
|
7556
7871
|
id: tid,
|
|
7557
7872
|
type: t.input.type,
|
|
@@ -7580,7 +7895,7 @@ function iii(opts = {}) {
|
|
|
7580
7895
|
if (!worker) return;
|
|
7581
7896
|
const handler = async (payload) => {
|
|
7582
7897
|
if (!worker.ws) throw new Error(`Worker "${worker.name}" disconnected`);
|
|
7583
|
-
const invocationId =
|
|
7898
|
+
const invocationId = crypto6.randomUUID();
|
|
7584
7899
|
return new Promise((resolve11, reject) => {
|
|
7585
7900
|
const timer = setTimeout(() => {
|
|
7586
7901
|
pending.delete(invocationId);
|
|
@@ -7614,7 +7929,7 @@ function iii(opts = {}) {
|
|
|
7614
7929
|
}
|
|
7615
7930
|
const wsHandler = createWsHandler({
|
|
7616
7931
|
registerRemoteWorker(ws, name) {
|
|
7617
|
-
const id2 =
|
|
7932
|
+
const id2 = crypto6.randomUUID();
|
|
7618
7933
|
workers.set(id2, { id: id2, name, ws, functions: [], triggers: [] });
|
|
7619
7934
|
return id2;
|
|
7620
7935
|
},
|
|
@@ -7625,7 +7940,7 @@ function iii(opts = {}) {
|
|
|
7625
7940
|
addRemoteFunction(workerId, id2);
|
|
7626
7941
|
},
|
|
7627
7942
|
registerRemoteTrigger(workerId, input) {
|
|
7628
|
-
const tid =
|
|
7943
|
+
const tid = crypto6.randomUUID();
|
|
7629
7944
|
const reg = { id: tid, ...input, workerId };
|
|
7630
7945
|
triggers.set(tid, reg);
|
|
7631
7946
|
const worker = workers.get(workerId);
|
|
@@ -7990,6 +8305,7 @@ function registerWorker(url) {
|
|
|
7990
8305
|
};
|
|
7991
8306
|
}
|
|
7992
8307
|
export {
|
|
8308
|
+
Link,
|
|
7993
8309
|
Router,
|
|
7994
8310
|
TsxContext,
|
|
7995
8311
|
agent,
|
|
@@ -7997,10 +8313,12 @@ export {
|
|
|
7997
8313
|
auth,
|
|
7998
8314
|
compress,
|
|
7999
8315
|
cors,
|
|
8316
|
+
createHub,
|
|
8000
8317
|
createOpenAI,
|
|
8001
8318
|
createSSEStream,
|
|
8002
8319
|
createTestServer,
|
|
8003
8320
|
createWorker,
|
|
8321
|
+
csrf,
|
|
8004
8322
|
defineConfig,
|
|
8005
8323
|
deleteCookie,
|
|
8006
8324
|
deploy,
|
|
@@ -8021,6 +8339,7 @@ export {
|
|
|
8021
8339
|
logger,
|
|
8022
8340
|
mailer,
|
|
8023
8341
|
messager,
|
|
8342
|
+
navigate,
|
|
8024
8343
|
openai,
|
|
8025
8344
|
opencode,
|
|
8026
8345
|
postgres,
|
|
@@ -8043,7 +8362,10 @@ export {
|
|
|
8043
8362
|
tool2 as tool,
|
|
8044
8363
|
tsx,
|
|
8045
8364
|
upload,
|
|
8365
|
+
useAction,
|
|
8366
|
+
useNavigate,
|
|
8046
8367
|
useTsx,
|
|
8368
|
+
useWebsocket,
|
|
8047
8369
|
user,
|
|
8048
8370
|
validate
|
|
8049
8371
|
};
|