chore(broker): typecheck clean (77 → 0)
paid down the broker's accumulated type debt. zero behavioral changes, purely type-system tightening: - broker.ts: row extraction helper for postgres-js result vs pg shape; findMemberByPubkey defaultGroups null-coalescing. - env.ts: zod default ordered before transform (zod v4 ordering). - index.ts: typed JSON.parse for the tg/token, upload-auth, file-upload, member patch and mesh-settings handlers; export SelfEditablePolicy from member-api; added bodyVersion to WSSendMessage; added the disconnect/kick/ban/unban/list_bans message types to WSClientMessage; String(key) cast for neo4j record symbol-typed keys. - jwt.ts, paths.ts, telegram-token.ts: typed JSON.parse results. - service-manager.ts: typed package.json + MCP JSON-RPC reader. - telegram-bridge.ts: typed WS message handler; missing log import; null-tolerant BridgeRow + skip rows missing memberId/displayName; typed e in catch. - types.ts: bodyVersion on WSSendMessage, manifest on WSSkillData, five new admin message types (kick/disconnect/ban/unban/list_bans). - packages/db/server.ts: drizzle constructor positional args + scoped ts-expect-error for the namespace-bag schema generic mismatch. apps/broker/src/types.ts will eventually want a real audit pass to catch every WS verb and surface the orphans, but this clears the path for 1.30.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,7 +22,7 @@ import { invite as inviteTable, mesh, meshMember, messageQueue, presence, schedu
|
||||
import { user } from "@turbostarter/db/schema/auth";
|
||||
import { handleCliSync, type CliSyncRequest } from "./cli-sync";
|
||||
import { generateId } from "@turbostarter/shared/utils";
|
||||
import { updateMemberProfile, listMeshMembers, updateMeshSettings } from "./member-api";
|
||||
import { updateMemberProfile, listMeshMembers, updateMeshSettings, type MemberUpdateRequest, type SelfEditablePolicy } from "./member-api";
|
||||
import {
|
||||
claimTask,
|
||||
completeTask,
|
||||
@@ -831,7 +831,12 @@ function handleHttpRequest(req: IncomingMessage, res: ServerResponse): void {
|
||||
req.on("data", (c: Buffer) => chunks.push(c));
|
||||
req.on("end", () => {
|
||||
try {
|
||||
const body = JSON.parse(Buffer.concat(chunks).toString());
|
||||
const body = JSON.parse(Buffer.concat(chunks).toString()) as {
|
||||
meshId?: string;
|
||||
memberId?: string;
|
||||
pubkey?: string;
|
||||
secretKey?: string;
|
||||
};
|
||||
const { meshId: tgMeshId, memberId: tgMemberId, pubkey: tgPubkey, secretKey: tgSecretKey } = body;
|
||||
if (!tgMeshId || !tgMemberId || !tgPubkey || !tgSecretKey) {
|
||||
writeJson(res, 400, { error: "meshId, memberId, pubkey, secretKey required" });
|
||||
@@ -1099,7 +1104,7 @@ function handleInviteClaimV2Post(
|
||||
const raw = Buffer.concat(chunks).toString();
|
||||
let payload: { recipient_x25519_pubkey?: string; display_name?: string };
|
||||
try {
|
||||
payload = JSON.parse(raw);
|
||||
payload = JSON.parse(raw) as { recipient_x25519_pubkey?: string; display_name?: string };
|
||||
} catch {
|
||||
writeJson(res, 400, { error: "malformed" });
|
||||
return;
|
||||
@@ -1197,7 +1202,7 @@ async function handleUploadPost(
|
||||
let tags: string[] = [];
|
||||
if (tagsRaw) {
|
||||
try {
|
||||
tags = JSON.parse(tagsRaw);
|
||||
tags = JSON.parse(tagsRaw) as string[];
|
||||
} catch {
|
||||
tags = [];
|
||||
}
|
||||
@@ -1259,7 +1264,7 @@ async function handleUploadPost(
|
||||
let fileKeys: Array<{ peerPubkey: string; sealedKey: string }> = [];
|
||||
if (encrypted && fileKeysRaw) {
|
||||
try {
|
||||
fileKeys = JSON.parse(fileKeysRaw);
|
||||
fileKeys = JSON.parse(fileKeysRaw) as Array<{ peerPubkey: string; sealedKey: string }>;
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
@@ -1364,7 +1369,7 @@ function handleMemberPatchPost(req: IncomingMessage, res: ServerResponse, meshId
|
||||
req.on("end", async () => {
|
||||
if (aborted) return;
|
||||
try {
|
||||
const body = JSON.parse(Buffer.concat(chunks).toString());
|
||||
const body = JSON.parse(Buffer.concat(chunks).toString()) as MemberUpdateRequest;
|
||||
// Auth: callerMemberId from X-Member-Id header (dashboard or CLI provides this)
|
||||
const callerMemberId = req.headers["x-member-id"] as string | undefined;
|
||||
if (!callerMemberId) { writeJson(res, 401, { ok: false, error: "X-Member-Id header required" }); return; }
|
||||
@@ -1407,7 +1412,7 @@ function handleMeshSettingsPatch(req: IncomingMessage, res: ServerResponse, mesh
|
||||
req.on("end", async () => {
|
||||
if (aborted) return;
|
||||
try {
|
||||
const body = JSON.parse(Buffer.concat(chunks).toString());
|
||||
const body = JSON.parse(Buffer.concat(chunks).toString()) as { selfEditable?: SelfEditablePolicy };
|
||||
const callerMemberId = req.headers["x-member-id"] as string | undefined;
|
||||
if (!callerMemberId) { writeJson(res, 401, { ok: false, error: "X-Member-Id header required" }); return; }
|
||||
const result = await updateMeshSettings(meshId, callerMemberId, body);
|
||||
@@ -3753,7 +3758,7 @@ function handleConnection(ws: WebSocket): void {
|
||||
const gqRecords = gqResult.records.map((r) => {
|
||||
const obj: Record<string, unknown> = {};
|
||||
for (const key of r.keys) {
|
||||
obj[key] = r.get(key);
|
||||
obj[String(key)] = r.get(key);
|
||||
}
|
||||
return obj;
|
||||
});
|
||||
@@ -3788,7 +3793,7 @@ function handleConnection(ws: WebSocket): void {
|
||||
const geRecords = geResult.records.map((r) => {
|
||||
const obj: Record<string, unknown> = {};
|
||||
for (const key of r.keys) {
|
||||
obj[key] = r.get(key);
|
||||
obj[String(key)] = r.get(key);
|
||||
}
|
||||
return obj;
|
||||
});
|
||||
@@ -3877,10 +3882,10 @@ function handleConnection(ws: WebSocket): void {
|
||||
const [peers, stateEntries, memCount, fileCount, taskCounts, streams, tables] = await Promise.all([
|
||||
listPeersInMesh(conn.meshId),
|
||||
listState(conn.meshId),
|
||||
db.execute(sql`SELECT COUNT(*) as n FROM mesh.memory WHERE mesh_id = ${conn.meshId} AND forgotten_at IS NULL`).then(r => Number(((r.rows ?? r) as any[])[0]?.n ?? 0)),
|
||||
db.execute(sql`SELECT COUNT(*) as n FROM mesh.file WHERE mesh_id = ${conn.meshId} AND deleted_at IS NULL`).then(r => Number(((r.rows ?? r) as any[])[0]?.n ?? 0)),
|
||||
db.execute(sql`SELECT COUNT(*) as n FROM mesh.memory WHERE mesh_id = ${conn.meshId} AND forgotten_at IS NULL`).then(r => Number((((r as unknown as { rows?: unknown[] }).rows ?? (r as unknown as unknown[])) as any[])[0]?.n ?? 0)),
|
||||
db.execute(sql`SELECT COUNT(*) as n FROM mesh.file WHERE mesh_id = ${conn.meshId} AND deleted_at IS NULL`).then(r => Number((((r as unknown as { rows?: unknown[] }).rows ?? (r as unknown as unknown[])) as any[])[0]?.n ?? 0)),
|
||||
db.execute(sql`SELECT status, COUNT(*) as n FROM mesh.task WHERE mesh_id = ${conn.meshId} GROUP BY status`).then(r => {
|
||||
const rows = (r.rows ?? r) as Array<{ status: string; n: string }>;
|
||||
const rows = (((r as unknown as { rows?: unknown[] }).rows ?? (r as unknown as unknown[]))) as Array<{ status: string; n: string }>;
|
||||
const counts = { open: 0, claimed: 0, done: 0 };
|
||||
for (const row of rows) counts[row.status as keyof typeof counts] = Number(row.n);
|
||||
return counts;
|
||||
|
||||
Reference in New Issue
Block a user