weifuwu 0.24.0 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -183,8 +183,8 @@ async function sendResponse(res, response, opts) {
183
183
  }
184
184
  res.end();
185
185
  }
186
- async function createTestServer(handler) {
187
- const server = serve(handler, { port: 0, shutdown: false });
186
+ async function createTestServer(handler, options) {
187
+ const server = serve(handler, { ...options, port: options?.port ?? 0, shutdown: false });
188
188
  await server.ready;
189
189
  return { server, url: `http://localhost:${server.port}` };
190
190
  }
@@ -2695,9 +2695,10 @@ var Table = class {
2695
2695
  _buildSET(data) {
2696
2696
  const sets = [];
2697
2697
  const values = [];
2698
+ const d = data;
2698
2699
  for (const { prop, db } of this.colEntries) {
2699
- if (prop in data && data[prop] !== void 0) {
2700
- const val = data[prop];
2700
+ if (prop in d && d[prop] !== void 0) {
2701
+ const val = d[prop];
2701
2702
  if (val instanceof SQL) {
2702
2703
  sets.push(`"${db}" = ${val.toSQL()}`);
2703
2704
  } else {
@@ -2706,7 +2707,7 @@ var Table = class {
2706
2707
  }
2707
2708
  }
2708
2709
  }
2709
- if (this.hasColumn("updated_at") && !data.updated_at) {
2710
+ if (this.hasColumn("updated_at") && !d.updated_at) {
2710
2711
  sets.push('"updated_at" = NOW()');
2711
2712
  }
2712
2713
  return { sets, values };
@@ -3231,7 +3232,7 @@ h2{color:#dc2626}.desc{color:#555}</style>
3231
3232
  const redirectUri = form.get("redirect_uri") || "";
3232
3233
  const scope = form.get("scope") || "";
3233
3234
  const state = form.get("state") || "";
3234
- const userId = parseInt(form.get("user_id") || "0", 10);
3235
+ const userId2 = parseInt(form.get("user_id") || "0", 10);
3235
3236
  const codeChallenge = form.get("code_challenge") || "";
3236
3237
  const codeChallengeMethod = form.get("code_challenge_method") || "";
3237
3238
  if (!approve) {
@@ -3242,7 +3243,7 @@ h2{color:#dc2626}.desc{color:#555}</style>
3242
3243
  const expiresAt = new Date(Date.now() + 10 * 60 * 1e3);
3243
3244
  await pg.sql`
3244
3245
  INSERT INTO "_oauth2_codes" ("code", "client_id", "user_id", "redirect_uri", "code_challenge", "code_challenge_method", "scope", "expires_at")
3245
- VALUES (${code}, ${clientId}, ${userId}, ${redirectUri}, ${codeChallenge || null}, ${codeChallengeMethod || null}, ${scope || null}, ${expiresAt})
3246
+ VALUES (${code}, ${clientId}, ${userId2}, ${redirectUri}, ${codeChallenge || null}, ${codeChallengeMethod || null}, ${scope || null}, ${expiresAt})
3246
3247
  `;
3247
3248
  const loc = `${redirectUri}?code=${code}${state ? `&state=${state}` : ""}`;
3248
3249
  return Response.redirect(loc, 302);
@@ -3418,12 +3419,12 @@ function registerOAuthLoginRoutes(router, deps, providers) {
3418
3419
  );
3419
3420
  return row ?? null;
3420
3421
  }
3421
- async function linkProvider(userId, provider, providerId, email, name, avatarUrl) {
3422
+ async function linkProvider(userId2, provider, providerId, email, name, avatarUrl) {
3422
3423
  await sql2.unsafe(
3423
3424
  `INSERT INTO ${escapeIdent(providerTable)} (user_id, provider, provider_id, email, name, avatar_url)
3424
3425
  VALUES ($1, $2, $3, $4, $5, $6)
3425
3426
  ON CONFLICT (provider, provider_id) DO NOTHING`,
3426
- [userId, provider, providerId, email, name, avatarUrl]
3427
+ [userId2, provider, providerId, email, name, avatarUrl]
3427
3428
  );
3428
3429
  }
3429
3430
  async function findOrCreateUser(provider, providerId, email, name, avatarUrl) {
@@ -3765,7 +3766,7 @@ function user(options) {
3765
3766
  const payload = jwt2.verify(token, secret);
3766
3767
  if (payload.token_type === "client_credentials") return null;
3767
3768
  if (!hasDb || !findById) return null;
3768
- const row = await findById(payload.sub);
3769
+ const row = await findById(Number(payload.sub));
3769
3770
  if (!row) return null;
3770
3771
  return stripPassword(row);
3771
3772
  } catch {
@@ -3774,27 +3775,26 @@ function user(options) {
3774
3775
  }
3775
3776
  const headerName = options.header ?? "Authorization";
3776
3777
  async function resolveUser(req, ctx) {
3777
- const sessionUserId = ctx.session?.userId;
3778
+ const s = ctx;
3779
+ const sessionUserId = s.session?.userId;
3778
3780
  if (sessionUserId !== void 0 && sessionUserId !== null) {
3779
3781
  if (hasDb) {
3780
3782
  const row = await findById(sessionUserId);
3781
3783
  if (row) {
3782
3784
  return stripPassword(row);
3783
3785
  }
3784
- if (typeof ctx.session?.destroy === "function") {
3785
- ;
3786
- ctx.session.destroy();
3787
- } else {
3788
- delete ctx.session?.userId;
3786
+ if (typeof s.session?.destroy === "function") {
3787
+ s.session.destroy();
3788
+ } else if (s.session) {
3789
+ delete s.session.userId;
3789
3790
  }
3790
3791
  } else if (options.resolveUser) {
3791
3792
  const userData = await options.resolveUser(sessionUserId);
3792
3793
  if (userData) {
3793
3794
  return userData;
3794
3795
  }
3795
- if (typeof ctx.session?.destroy === "function") {
3796
- ;
3797
- ctx.session.destroy();
3796
+ if (typeof s.session?.destroy === "function") {
3797
+ s.session.destroy();
3798
3798
  }
3799
3799
  console.warn(`[${currentTraceId()}] user: session userId ${sessionUserId} resolved to null`);
3800
3800
  } else {
@@ -3854,7 +3854,7 @@ function user(options) {
3854
3854
  try {
3855
3855
  const payload = jwt2.verify(token, secret);
3856
3856
  if (payload.token_type === "client_credentials") return null;
3857
- const row = await findById(payload.sub);
3857
+ const row = await findById(Number(payload.sub));
3858
3858
  if (row) return stripPassword(row);
3859
3859
  } catch {
3860
3860
  }
@@ -3915,13 +3915,13 @@ function user(options) {
3915
3915
  try {
3916
3916
  const body = await parseBody2(req);
3917
3917
  const result = await login(body);
3918
- if (ctx.session) {
3919
- ;
3920
- ctx.session.userId = result.user.id;
3921
- ctx.session.role = result.user.role;
3918
+ const s = ctx;
3919
+ if (s.session) {
3920
+ s.session.userId = result.user.id;
3921
+ s.session.role = result.user.role;
3922
3922
  }
3923
3923
  const res = Response.json(result);
3924
- if (!ctx.session) {
3924
+ if (!s.session) {
3925
3925
  res.headers.set("Set-Cookie", `session=${result.token}; HttpOnly; SameSite=Lax; Path=/`);
3926
3926
  }
3927
3927
  return res;
@@ -4695,6 +4695,27 @@ function buildColumnDDL(tenantId, field) {
4695
4695
  }
4696
4696
 
4697
4697
  // tenant/rest.ts
4698
+ function userId(ctx) {
4699
+ return ctx.user?.id ?? null;
4700
+ }
4701
+ function extractCount(rows) {
4702
+ return Number(rows[0]?.count ?? 0);
4703
+ }
4704
+ function asJson(val) {
4705
+ return val;
4706
+ }
4707
+ function tableRef(s, name) {
4708
+ return s(name);
4709
+ }
4710
+ function withTenant(ctx, data) {
4711
+ ;
4712
+ data.tenant_id = ctx.tenant.id;
4713
+ return data;
4714
+ }
4715
+ function withoutTenant(data) {
4716
+ delete data.tenant_id;
4717
+ return data;
4718
+ }
4698
4719
  function zodType(field) {
4699
4720
  let t;
4700
4721
  switch (field.type) {
@@ -4752,7 +4773,7 @@ function buildRouter(sql2, usersTable) {
4752
4773
  `;
4753
4774
  await sql2`
4754
4775
  INSERT INTO "_tenant_members" ("tenant_id", "user_id", "role")
4755
- VALUES (${tenant2.id}, ${ctx.user.id}, 'admin')
4776
+ VALUES (${tenant2.id}, ${userId(ctx)}, 'admin')
4756
4777
  `;
4757
4778
  return Response.json(tenant2, { status: 201 });
4758
4779
  });
@@ -4760,7 +4781,7 @@ function buildRouter(sql2, usersTable) {
4760
4781
  const rows = await sql2`
4761
4782
  SELECT t.*, tm.role FROM "_tenants" t
4762
4783
  JOIN "_tenant_members" tm ON tm.tenant_id = t.id
4763
- WHERE tm.user_id = ${ctx.user.id}
4784
+ WHERE tm.user_id = ${userId(ctx)}
4764
4785
  `;
4765
4786
  return Response.json(rows);
4766
4787
  });
@@ -4769,7 +4790,7 @@ function buildRouter(sql2, usersTable) {
4769
4790
  if (err) return err;
4770
4791
  const { email, role = "member" } = await req.json();
4771
4792
  const [user2] = await sql2`
4772
- SELECT id FROM ${sql2(usersTable)} WHERE "email" = ${email} LIMIT 1
4793
+ SELECT id FROM ${tableRef(sql2, usersTable)} WHERE "email" = ${email} LIMIT 1
4773
4794
  `;
4774
4795
  if (!user2) return Response.json({ error: "User not found" }, { status: 404 });
4775
4796
  const [existing] = await sql2`
@@ -4786,10 +4807,10 @@ function buildRouter(sql2, usersTable) {
4786
4807
  r.delete("/sys/tenants/members/:userId", async (req, ctx) => {
4787
4808
  const err = requireAdmin(ctx);
4788
4809
  if (err) return err;
4789
- const userId = parseInt(ctx.params.userId, 10);
4810
+ const userId2 = parseInt(ctx.params.userId, 10);
4790
4811
  await sql2`
4791
4812
  DELETE FROM "_tenant_members"
4792
- WHERE tenant_id = ${ctx.tenant.id} AND user_id = ${userId}
4813
+ WHERE tenant_id = ${ctx.tenant.id} AND user_id = ${userId2}
4793
4814
  `;
4794
4815
  return Response.json({ ok: true });
4795
4816
  });
@@ -4815,7 +4836,7 @@ function buildRouter(sql2, usersTable) {
4815
4836
  }
4816
4837
  const [row] = await sql2`
4817
4838
  INSERT INTO "_user_tables" ("tenant_id", "slug", "label", "fields")
4818
- VALUES (${ctx.tenant.id}, ${body.slug}, ${body.label || ""}, ${body.fields})
4839
+ VALUES (${ctx.tenant.id}, ${body.slug}, ${body.label || ""}, ${asJson(body.fields)})
4819
4840
  RETURNING *
4820
4841
  `;
4821
4842
  return Response.json(row, { status: 201 });
@@ -4850,7 +4871,7 @@ function buildRouter(sql2, usersTable) {
4850
4871
  const merged = [...table.fields, ...newFields];
4851
4872
  await sql2`
4852
4873
  UPDATE "_user_tables"
4853
- SET fields = ${merged}
4874
+ SET fields = ${asJson(merged)}
4854
4875
  WHERE id = ${table.id}
4855
4876
  `;
4856
4877
  return Response.json({ ...table, fields: merged });
@@ -4898,7 +4919,7 @@ function buildRouter(sql2, usersTable) {
4898
4919
  [ctx.tenant.id]
4899
4920
  )
4900
4921
  ]);
4901
- return Response.json({ rows: rows2, count: Number(countResult2[0]?.count ?? 0) });
4922
+ return Response.json({ rows: rows2, count: extractCount(countResult2) });
4902
4923
  } catch {
4903
4924
  return Response.json({ error: "Invalid search_vector" }, { status: 400 });
4904
4925
  }
@@ -4914,7 +4935,7 @@ function buildRouter(sql2, usersTable) {
4914
4935
  [ctx.tenant.id]
4915
4936
  )
4916
4937
  ]);
4917
- return Response.json({ rows, count: Number(countResult[0]?.count ?? 0) });
4938
+ return Response.json({ rows, count: extractCount(countResult) });
4918
4939
  });
4919
4940
  r.post("/:_slug", async (req, ctx) => {
4920
4941
  const table = await resolveTable(ctx);
@@ -4925,11 +4946,10 @@ function buildRouter(sql2, usersTable) {
4925
4946
  shape[f.name] = zodType(f);
4926
4947
  }
4927
4948
  const zodSchema = z3.object(shape);
4928
- const parsed = zodSchema.parse(data);
4929
- parsed.tenant_id = ctx.tenant.id;
4949
+ const parsed = withTenant(ctx, zodSchema.parse(data));
4930
4950
  delete parsed.id;
4931
4951
  const name = internalName(ctx);
4932
- const [row] = await sql2`INSERT INTO ${sql2(name)} ${sql2(parsed)} RETURNING *`;
4952
+ const [row] = await sql2`INSERT INTO ${tableRef(sql2, name)} ${sql2(parsed)} RETURNING *`;
4933
4953
  return Response.json(row, { status: 201 });
4934
4954
  });
4935
4955
  r.get("/:_slug/:id", async (_req, ctx) => {
@@ -4937,7 +4957,7 @@ function buildRouter(sql2, usersTable) {
4937
4957
  if (!table) return Response.json({ error: "Table not found" }, { status: 404 });
4938
4958
  const name = internalName(ctx);
4939
4959
  const [row] = await sql2`
4940
- SELECT * FROM ${sql2(name)}
4960
+ SELECT * FROM ${tableRef(sql2, name)}
4941
4961
  WHERE id = ${parseInt(ctx.params.id, 10)} AND tenant_id = ${ctx.tenant.id}
4942
4962
  LIMIT 1
4943
4963
  `;
@@ -4953,13 +4973,12 @@ function buildRouter(sql2, usersTable) {
4953
4973
  shape[f.name] = zodType(f);
4954
4974
  }
4955
4975
  const zodSchema = z3.object(shape).partial();
4956
- const parsed = zodSchema.parse(data);
4976
+ const parsed = withoutTenant(zodSchema.parse(data));
4957
4977
  delete parsed.id;
4958
- delete parsed.tenant_id;
4959
4978
  if (Object.keys(parsed).length === 0) {
4960
4979
  const name2 = internalName(ctx);
4961
4980
  const [row2] = await sql2`
4962
- SELECT * FROM ${sql2(name2)}
4981
+ SELECT * FROM ${tableRef(sql2, name2)}
4963
4982
  WHERE id = ${parseInt(ctx.params.id, 10)} AND tenant_id = ${ctx.tenant.id}
4964
4983
  LIMIT 1
4965
4984
  `;
@@ -4967,7 +4986,7 @@ function buildRouter(sql2, usersTable) {
4967
4986
  }
4968
4987
  const name = internalName(ctx);
4969
4988
  const [row] = await sql2`
4970
- UPDATE ${sql2(name)} SET ${sql2(parsed)}
4989
+ UPDATE ${tableRef(sql2, name)} SET ${sql2(parsed)}
4971
4990
  WHERE id = ${parseInt(ctx.params.id, 10)} AND tenant_id = ${ctx.tenant.id}
4972
4991
  RETURNING *
4973
4992
  `;
@@ -4977,7 +4996,7 @@ function buildRouter(sql2, usersTable) {
4977
4996
  r.delete("/:_slug/:id", async (_req, ctx) => {
4978
4997
  const name = internalName(ctx);
4979
4998
  const result = await sql2`
4980
- DELETE FROM ${sql2(name)}
4999
+ DELETE FROM ${tableRef(sql2, name)}
4981
5000
  WHERE id = ${parseInt(ctx.params.id, 10)} AND tenant_id = ${ctx.tenant.id}
4982
5001
  RETURNING 1
4983
5002
  `;
@@ -5018,7 +5037,7 @@ function buildRouter(sql2, usersTable) {
5018
5037
  [parentId2, ctx.tenant.id]
5019
5038
  )
5020
5039
  ]);
5021
- return Response.json({ rows, count: Number(countResult[0]?.count ?? 0) });
5040
+ return Response.json({ rows, count: extractCount(countResult) });
5022
5041
  }
5023
5042
  return Response.json({ error: "POST not supported on M2M nested routes" }, { status: 400 });
5024
5043
  }
@@ -5035,7 +5054,7 @@ function buildRouter(sql2, usersTable) {
5035
5054
  [parentId, ctx.tenant.id]
5036
5055
  )
5037
5056
  ]);
5038
- return Response.json({ rows, count: Number(countResult[0]?.count ?? 0) });
5057
+ return Response.json({ rows, count: extractCount(countResult) });
5039
5058
  }
5040
5059
  const body = await req.json();
5041
5060
  const shape = {};
@@ -5047,7 +5066,7 @@ function buildRouter(sql2, usersTable) {
5047
5066
  parsed.tenant_id = ctx.tenant.id;
5048
5067
  parsed[relField.name] = parentId;
5049
5068
  delete parsed.id;
5050
- const [row] = await sql2`INSERT INTO ${sql2(childName)} ${sql2(parsed)} RETURNING *`;
5069
+ const [row] = await sql2`INSERT INTO ${tableRef(sql2, childName)} ${sql2(parsed)} RETURNING *`;
5051
5070
  return Response.json(row, { status: 201 });
5052
5071
  }
5053
5072
  r.get("/:_slug/:id/:_nested", async (req, ctx) => handleNested(req, ctx, "GET"));
@@ -5117,7 +5136,6 @@ function buildObjectType(table, ctx) {
5117
5136
  if (other.id === table.id) continue;
5118
5137
  const relField = findRelation(other.fields, table.slug);
5119
5138
  if (relField) {
5120
- const otherName = pascalCase(other.slug);
5121
5139
  fields[other.slug] = {
5122
5140
  type: new GraphQLList(new GraphQLNonNull(buildObjectType(other, ctx))),
5123
5141
  args: {
@@ -5632,7 +5650,7 @@ async function loadAgent(agents, agentId) {
5632
5650
  return row ?? null;
5633
5651
  }
5634
5652
  function createRunner(deps) {
5635
- const { sql: sql2, agents, runs, provider, modelName, userTools } = deps;
5653
+ const { sql: sql2, agents, runs, provider, userTools } = deps;
5636
5654
  function truncate(s, max = 200) {
5637
5655
  return s.length > max ? s.slice(0, max) + "..." : s;
5638
5656
  }
@@ -5942,32 +5960,32 @@ function createWSHandler(deps) {
5942
5960
  prefix: "messager:"
5943
5961
  });
5944
5962
  const userConnections = /* @__PURE__ */ new Map();
5945
- function trackConnection(userId, ws) {
5946
- let conns = userConnections.get(userId);
5963
+ function trackConnection(userId2, ws) {
5964
+ let conns = userConnections.get(userId2);
5947
5965
  if (!conns) {
5948
5966
  conns = /* @__PURE__ */ new Set();
5949
- userConnections.set(userId, conns);
5967
+ userConnections.set(userId2, conns);
5950
5968
  }
5951
5969
  conns.add(ws);
5952
5970
  }
5953
5971
  function untrackConnection(ws) {
5954
- for (const [userId, conns] of userConnections) {
5972
+ for (const [userId2, conns] of userConnections) {
5955
5973
  conns.delete(ws);
5956
- if (conns.size === 0) userConnections.delete(userId);
5974
+ if (conns.size === 0) userConnections.delete(userId2);
5957
5975
  }
5958
5976
  }
5959
5977
  return {
5960
5978
  handler: {
5961
5979
  open(ws, ctx) {
5962
- const userId = ctx.user?.id;
5963
- if (!userId) {
5980
+ const userId2 = ctx.user?.id;
5981
+ if (!userId2) {
5964
5982
  ws.close(4001, "Unauthorized");
5965
5983
  return;
5966
5984
  }
5967
5985
  },
5968
5986
  async message(ws, ctx, data) {
5969
- const userId = ctx.user?.id;
5970
- if (!userId) return;
5987
+ const userId2 = ctx.user?.id;
5988
+ if (!userId2) return;
5971
5989
  let msg;
5972
5990
  try {
5973
5991
  msg = JSON.parse(data.toString());
@@ -5981,12 +5999,12 @@ function createWSHandler(deps) {
5981
5999
  if (!content || !channel_id) return;
5982
6000
  const [row] = await sql2`
5983
6001
  INSERT INTO "_messages" ("channel_id", "sender_id", "sender_type", "content")
5984
- VALUES (${channel_id}, ${userId}, 'user', ${content})
6002
+ VALUES (${channel_id}, ${userId2}, 'user', ${content})
5985
6003
  RETURNING *
5986
6004
  `;
5987
6005
  const message = row;
5988
6006
  hub.join(`messager:${channel_id}`, ws);
5989
- trackConnection(userId, ws);
6007
+ trackConnection(userId2, ws);
5990
6008
  broadcastToChannel(hub, channel_id, { type: "message", data: message });
5991
6009
  if (agents) {
5992
6010
  const insertMsg = (data2) => sql2`
@@ -6005,7 +6023,7 @@ function createWSHandler(deps) {
6005
6023
  broadcastToChannel(hub, channel_id, {
6006
6024
  type: "typing",
6007
6025
  channel_id,
6008
- user_id: userId,
6026
+ user_id: userId2,
6009
6027
  is_typing: is_typing ?? false
6010
6028
  });
6011
6029
  break;
@@ -6016,12 +6034,12 @@ function createWSHandler(deps) {
6016
6034
  await sql2`
6017
6035
  UPDATE "_channel_members"
6018
6036
  SET last_read_id = ${last_message_id}, last_read_at = NOW()
6019
- WHERE channel_id = ${channel_id} AND member_id = ${userId} AND member_type = 'user'
6037
+ WHERE channel_id = ${channel_id} AND member_id = ${userId2} AND member_type = 'user'
6020
6038
  `;
6021
6039
  broadcastToChannel(hub, channel_id, {
6022
6040
  type: "read",
6023
6041
  channel_id,
6024
- user_id: userId,
6042
+ user_id: userId2,
6025
6043
  last_message_id
6026
6044
  });
6027
6045
  break;
@@ -6075,7 +6093,7 @@ function buildRouter3(deps) {
6075
6093
  return Response.json(channel, { status: 201 });
6076
6094
  });
6077
6095
  r.get("/channels", async (_req, ctx) => {
6078
- const userId = ctx.user?.id ?? 1;
6096
+ const userId2 = ctx.user?.id ?? 1;
6079
6097
  const rows = await sql2`
6080
6098
  SELECT c.*, (
6081
6099
  SELECT content FROM "_messages"
@@ -6084,7 +6102,7 @@ function buildRouter3(deps) {
6084
6102
  ) AS last_message
6085
6103
  FROM "_channels" c
6086
6104
  JOIN "_channel_members" m ON m.channel_id = c.id
6087
- WHERE m.member_id = ${userId} AND m.member_type = 'user'
6105
+ WHERE m.member_id = ${userId2} AND m.member_type = 'user'
6088
6106
  ORDER BY c.created_at DESC
6089
6107
  `;
6090
6108
  return Response.json(rows);
@@ -6157,9 +6175,9 @@ function buildRouter3(deps) {
6157
6175
  r.post("/channels/:id/read", async (req, ctx) => {
6158
6176
  const channelId = parseInt(ctx.params.id, 10);
6159
6177
  const body = await req.json();
6160
- const userId = body.user_id ?? ctx.user?.id ?? 1;
6178
+ const userId2 = body.user_id ?? ctx.user?.id ?? 1;
6161
6179
  await members.updateMany(
6162
- [eq("channel_id", channelId), eq("member_id", userId), eq("member_type", "user")],
6180
+ [eq("channel_id", channelId), eq("member_id", userId2), eq("member_type", "user")],
6163
6181
  { last_read_id: body.last_message_id }
6164
6182
  );
6165
6183
  return Response.json({ ok: true });
@@ -7349,8 +7367,9 @@ function errorBoundary(errorPath) {
7349
7367
  const mod = await compile(errorPath);
7350
7368
  const ErrorComponent = mod.default;
7351
7369
  if (!ErrorComponent) throw err;
7352
- const layouts = (ctx.layoutStack || []).map((l) => l.component);
7353
- const base = (ctx.mountPath || "").replace(/\/$/, "");
7370
+ const ctx2 = ctx;
7371
+ const layouts = (ctx2.layoutStack || []).map((l) => l.component);
7372
+ const base = (ctx2.mountPath || "").replace(/\/$/, "");
7354
7373
  let element = createElement2(ErrorComponent, {
7355
7374
  error: err instanceof Error ? err : new Error(String(err)),
7356
7375
  reset: () => {
@@ -7360,10 +7379,10 @@ function errorBoundary(errorPath) {
7360
7379
  const { renderToReadableStream } = await import("react-dom/server");
7361
7380
  const stream = await renderToReadableStream(element);
7362
7381
  return streamResponse(stream, {
7363
- ctx,
7382
+ ctx: ctx2,
7364
7383
  base,
7365
7384
  isDev: isDev(),
7366
- tailwind: ctx.tailwind,
7385
+ tailwind: ctx2.tailwind,
7367
7386
  status: 500
7368
7387
  });
7369
7388
  }
@@ -7743,10 +7762,10 @@ async function getSession(sql2, id2) {
7743
7762
  const { data: rows } = await sessions.readMany(sql2, { id: id2, active: true });
7744
7763
  return rows[0] ?? null;
7745
7764
  }
7746
- async function listSessions(sql2, userId) {
7765
+ async function listSessions(sql2, userId2) {
7747
7766
  const opts = { orderBy: { updated_at: "desc" } };
7748
- if (userId !== void 0) {
7749
- const { data: rows2 } = await sessions.readMany(sql2, { user_id: userId, active: true }, opts);
7767
+ if (userId2 !== void 0) {
7768
+ const { data: rows2 } = await sessions.readMany(sql2, { user_id: userId2, active: true }, opts);
7750
7769
  return rows2;
7751
7770
  }
7752
7771
  const { data: rows } = await sessions.readMany(sql2, { active: true }, opts);
@@ -8367,9 +8386,9 @@ function createWSHandler2(deps) {
8367
8386
  const { sql: sql2, model, workspace, systemPrompt, skills, skillsRegistry, permissions: permissions2, pendingQuestions } = deps;
8368
8387
  return {
8369
8388
  open(ws, ctx) {
8370
- const userId = ctx.user?.id ?? 0;
8389
+ const userId2 = ctx.user?.id ?? 0;
8371
8390
  const mountPath = ctx.mountPath ?? "";
8372
- clients2.set(ws, { userId, mountPath });
8391
+ clients2.set(ws, { userId: userId2, mountPath });
8373
8392
  },
8374
8393
  async message(ws, ctx, data) {
8375
8394
  const client = clients2.get(ws);
@@ -8878,7 +8897,7 @@ function analytics(options) {
8878
8897
  if (pg) await migratePg(pg.sql, pg.table);
8879
8898
  };
8880
8899
  const close = async () => {
8881
- if (store2) store2.stopCleanup();
8900
+ store2?.stopCleanup();
8882
8901
  };
8883
8902
  const mod = r;
8884
8903
  mod.middleware = middleware;
@@ -10193,7 +10212,7 @@ function iii(opts = {}) {
10193
10212
  mod.migrate = async () => {
10194
10213
  await stream.migrate();
10195
10214
  };
10196
- mod.shutdown = async () => {
10215
+ mod.close = async () => {
10197
10216
  for (const [, p] of pending) {
10198
10217
  clearTimeout(p.timer);
10199
10218
  p.reject(new Error("Engine shutting down"));
@@ -10205,7 +10224,6 @@ function iii(opts = {}) {
10205
10224
  triggers.clear();
10206
10225
  await stream.close();
10207
10226
  };
10208
- mod.close = mod.shutdown;
10209
10227
  return mod;
10210
10228
  }
10211
10229
 
@@ -10410,7 +10428,7 @@ function registerWorker(url) {
10410
10428
  onStream(handler) {
10411
10429
  handlers.set("__stream__", handler);
10412
10430
  },
10413
- shutdown() {
10431
+ close() {
10414
10432
  intentionalClose = true;
10415
10433
  if (reconnectTimer) clearTimeout(reconnectTimer);
10416
10434
  ws?.close();
@@ -10459,7 +10477,7 @@ var MemoryStore = class {
10459
10477
  if (entry.expires < now) this.store.delete(key);
10460
10478
  }
10461
10479
  }
10462
- close() {
10480
+ async close() {
10463
10481
  clearInterval(this.interval);
10464
10482
  this.store.clear();
10465
10483
  }
@@ -10495,6 +10513,9 @@ var RedisStore = class {
10495
10513
  async destroy(sid) {
10496
10514
  await this.redis.del(this.key(sid));
10497
10515
  }
10516
+ async close() {
10517
+ this.redis.disconnect();
10518
+ }
10498
10519
  };
10499
10520
  var COOKIE_SEPARATOR = ".";
10500
10521
  function signSessionId(sid, secret) {
@@ -10568,6 +10589,7 @@ function session(options) {
10568
10589
  } else if (options?.store === "redis") {
10569
10590
  if (!options.redis) throw new Error('session: redis client required when store: "redis"');
10570
10591
  store2 = new RedisStore(options.redis);
10592
+ closeStore = () => store2.close();
10571
10593
  } else {
10572
10594
  const mem = new MemoryStore();
10573
10595
  store2 = mem;
@@ -10647,8 +10669,8 @@ function session(options) {
10647
10669
  }
10648
10670
  return res;
10649
10671
  });
10650
- mw.close = () => {
10651
- closeStore?.();
10672
+ mw.close = async () => {
10673
+ await closeStore?.();
10652
10674
  };
10653
10675
  mw.store = store2;
10654
10676
  return mw;
@@ -10742,7 +10764,7 @@ var MemoryCache = class {
10742
10764
  }
10743
10765
  }
10744
10766
  }
10745
- close() {
10767
+ async close() {
10746
10768
  clearInterval(this.interval);
10747
10769
  this.store.clear();
10748
10770
  this.tagIndex.clear();
@@ -10883,8 +10905,8 @@ function cache2(options) {
10883
10905
  mw.store = store2;
10884
10906
  mw.invalidate = async (tag) => store2.invalidate(tag);
10885
10907
  mw.flush = async () => store2.flush();
10886
- mw.close = () => {
10887
- closeStore?.();
10908
+ mw.close = async () => {
10909
+ await closeStore?.();
10888
10910
  };
10889
10911
  return mw;
10890
10912
  }
@@ -11468,17 +11490,17 @@ function permissions(options) {
11468
11490
  );
11469
11491
  return created.id;
11470
11492
  }
11471
- async function assignRole(userId, role) {
11493
+ async function assignRole(userId2, role) {
11472
11494
  const roleId = await ensureRole(role);
11473
11495
  await sql2.unsafe(
11474
11496
  `INSERT INTO ${escapeIdent6(userRolesTable)} (user_id, role_id) VALUES ($1, $2) ON CONFLICT DO NOTHING`,
11475
- [userId, roleId]
11497
+ [userId2, roleId]
11476
11498
  );
11477
11499
  }
11478
- async function removeRole(userId, role) {
11500
+ async function removeRole(userId2, role) {
11479
11501
  await sql2.unsafe(
11480
11502
  `DELETE FROM ${escapeIdent6(userRolesTable)} WHERE user_id = $1 AND role_id = (SELECT id FROM ${escapeIdent6(rolesTable)} WHERE name = $2)`,
11481
- [userId, role]
11503
+ [userId2, role]
11482
11504
  );
11483
11505
  }
11484
11506
  async function grantPermission(role, permission) {
@@ -11494,31 +11516,31 @@ function permissions(options) {
11494
11516
  [role, permission]
11495
11517
  );
11496
11518
  }
11497
- async function getUserRoles(userId) {
11519
+ async function getUserRoles(userId2) {
11498
11520
  const rows = await sql2.unsafe(
11499
11521
  `SELECT r.name FROM ${escapeIdent6(userRolesTable)} ur
11500
11522
  JOIN ${escapeIdent6(rolesTable)} r ON r.id = ur.role_id
11501
11523
  WHERE ur.user_id = $1 ORDER BY r.name`,
11502
- [userId]
11524
+ [userId2]
11503
11525
  );
11504
11526
  return rows.map((r) => r.name);
11505
11527
  }
11506
- async function getUserPermissions(userId) {
11528
+ async function getUserPermissions(userId2) {
11507
11529
  const rows = await sql2.unsafe(
11508
11530
  `SELECT DISTINCT rp.permission FROM ${escapeIdent6(userRolesTable)} ur
11509
11531
  JOIN ${escapeIdent6(rolePermsTable)} rp ON rp.role_id = ur.role_id
11510
11532
  WHERE ur.user_id = $1 ORDER BY rp.permission`,
11511
- [userId]
11533
+ [userId2]
11512
11534
  );
11513
11535
  return rows.map((r) => r.permission);
11514
11536
  }
11515
11537
  const mw = (async (req, ctx, next) => {
11516
- const userId = ctx.user?.id;
11538
+ const userId2 = ctx.user?.id;
11517
11539
  let roles = /* @__PURE__ */ new Set();
11518
11540
  let perms = /* @__PURE__ */ new Set();
11519
- if (userId) {
11520
- const userRoles = await getUserRoles(userId);
11521
- const userPerms = userId ? await getUserPermissions(userId) : [];
11541
+ if (userId2) {
11542
+ const userRoles = await getUserRoles(userId2);
11543
+ const userPerms = userId2 ? await getUserPermissions(userId2) : [];
11522
11544
  roles = new Set(userRoles);
11523
11545
  perms = new Set(userPerms);
11524
11546
  const hasWildcard = userPerms.includes("*");
@@ -41,6 +41,14 @@ export interface KBListEntry {
41
41
  title: string;
42
42
  chunks: number;
43
43
  }
44
+ export interface KBInjected {
45
+ search(query: string, searchOptions?: KBSearchOptions): Promise<KBSearchResult[]>;
46
+ }
47
+ declare module '../types.ts' {
48
+ interface Context {
49
+ kb?: KBInjected;
50
+ }
51
+ }
44
52
  export interface KBModule {
45
53
  /**
46
54
  * Ingest a document: chunk → embed → store.
@@ -1,5 +1,6 @@
1
1
  import type { PostgresClient } from '../postgres/types.ts';
2
2
  import type { Router } from '../router.ts';
3
+ import type { Closeable } from '../types.ts';
3
4
  export interface LogdbOptions {
4
5
  pg: PostgresClient;
5
6
  table?: string;
@@ -18,7 +19,7 @@ export interface LogEntryInput {
18
19
  message: string;
19
20
  metadata?: Record<string, unknown>;
20
21
  }
21
- export interface LogdbModule extends Router {
22
+ export interface LogdbModule extends Router, Closeable {
22
23
  log(input: LogEntryInput): Promise<LogEntry>;
23
24
  migrate(): Promise<void>;
24
25
  clean(retentionMonths: number): Promise<number>;