Skip to content

Commit 7999767

Browse files
committed
refactor: dedupe core trimmed string readers
1 parent 67035a6 commit 7999767

4 files changed

Lines changed: 78 additions & 86 deletions

File tree

src/commands/doctor-cron-store-migration.ts

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ function normalizePayloadKind(payload: Record<string, unknown>) {
5353
}
5454

5555
function inferPayloadIfMissing(raw: Record<string, unknown>) {
56-
const message = typeof raw.message === "string" ? raw.message.trim() : "";
57-
const text = typeof raw.text === "string" ? raw.text.trim() : "";
58-
const command = typeof raw.command === "string" ? raw.command.trim() : "";
56+
const message = normalizeOptionalString(raw.message) ?? "";
57+
const text = normalizeOptionalString(raw.text) ?? "";
58+
const command = normalizeOptionalString(raw.command) ?? "";
5959
if (message) {
6060
raw.payload = { kind: "agentTurn", message };
6161
return true;
@@ -78,13 +78,13 @@ function copyTopLevelAgentTurnFields(
7878
let mutated = false;
7979

8080
const copyTrimmedString = (field: "model" | "thinking") => {
81-
const existing = payload[field];
82-
if (typeof existing === "string" && existing.trim()) {
81+
const existing = normalizeOptionalString(payload[field]);
82+
if (existing) {
8383
return;
8484
}
85-
const value = raw[field];
86-
if (typeof value === "string" && value.trim()) {
87-
payload[field] = value.trim();
85+
const value = normalizeOptionalString(raw[field]);
86+
if (value) {
87+
payload[field] = value;
8888
mutated = true;
8989
}
9090
};
@@ -112,24 +112,22 @@ function copyTopLevelAgentTurnFields(
112112
payload.deliver = raw.deliver;
113113
mutated = true;
114114
}
115-
if (
116-
typeof payload.channel !== "string" &&
117-
typeof raw.channel === "string" &&
118-
raw.channel.trim()
119-
) {
120-
payload.channel = raw.channel.trim();
115+
const channel = normalizeOptionalString(raw.channel);
116+
if (typeof payload.channel !== "string" && channel) {
117+
payload.channel = channel;
121118
mutated = true;
122119
}
123-
if (typeof payload.to !== "string" && typeof raw.to === "string" && raw.to.trim()) {
124-
payload.to = raw.to.trim();
120+
const to = normalizeOptionalString(raw.to);
121+
if (typeof payload.to !== "string" && to) {
122+
payload.to = to;
125123
mutated = true;
126124
}
125+
const rawThreadId = normalizeOptionalString(raw.threadId);
127126
if (
128127
!("threadId" in payload) &&
129-
((typeof raw.threadId === "number" && Number.isFinite(raw.threadId)) ||
130-
(typeof raw.threadId === "string" && raw.threadId.trim()))
128+
((typeof raw.threadId === "number" && Number.isFinite(raw.threadId)) || Boolean(rawThreadId))
131129
) {
132-
payload.threadId = typeof raw.threadId === "string" ? raw.threadId.trim() : raw.threadId;
130+
payload.threadId = rawThreadId ?? raw.threadId;
133131
mutated = true;
134132
}
135133
if (
@@ -139,12 +137,9 @@ function copyTopLevelAgentTurnFields(
139137
payload.bestEffortDeliver = raw.bestEffortDeliver;
140138
mutated = true;
141139
}
142-
if (
143-
typeof payload.provider !== "string" &&
144-
typeof raw.provider === "string" &&
145-
raw.provider.trim()
146-
) {
147-
payload.provider = raw.provider.trim();
140+
const provider = normalizeOptionalString(raw.provider);
141+
if (typeof payload.provider !== "string" && provider) {
142+
payload.provider = provider;
148143
mutated = true;
149144
}
150145

@@ -300,11 +295,11 @@ export function normalizeStoredCronJobs(
300295
trackIssue("legacyPayloadKind");
301296
}
302297
if (!payloadRecord.kind) {
303-
if (typeof payloadRecord.message === "string" && payloadRecord.message.trim()) {
298+
if (normalizeOptionalString(payloadRecord.message)) {
304299
payloadRecord.kind = "agentTurn";
305300
mutated = true;
306301
trackIssue("legacyPayloadKind");
307-
} else if (typeof payloadRecord.text === "string" && payloadRecord.text.trim()) {
302+
} else if (normalizeOptionalString(payloadRecord.text)) {
308303
payloadRecord.kind = "systemEvent";
309304
mutated = true;
310305
trackIssue("legacyPayloadKind");
@@ -343,8 +338,7 @@ export function normalizeStoredCronJobs(
343338
}
344339

345340
if (payloadRecord) {
346-
const hadLegacyPayloadProvider =
347-
typeof payloadRecord.provider === "string" && payloadRecord.provider.trim().length > 0;
341+
const hadLegacyPayloadProvider = Boolean(normalizeOptionalString(payloadRecord.provider));
348342
if (migrateLegacyCronPayload(payloadRecord)) {
349343
mutated = true;
350344
if (hadLegacyPayloadProvider) {
@@ -361,7 +355,7 @@ export function normalizeStoredCronJobs(
361355
sched.kind = "at";
362356
mutated = true;
363357
}
364-
const atRaw = typeof sched.at === "string" ? sched.at.trim() : "";
358+
const atRaw = normalizeOptionalString(sched.at) ?? "";
365359
const atMsRaw = sched.atMs;
366360
const parsedAtMs =
367361
typeof atMsRaw === "number"
@@ -403,8 +397,8 @@ export function normalizeStoredCronJobs(
403397
}
404398
}
405399

406-
const exprRaw = typeof sched.expr === "string" ? sched.expr.trim() : "";
407-
const legacyCronRaw = typeof sched.cron === "string" ? sched.cron.trim() : "";
400+
const exprRaw = normalizeOptionalString(sched.expr) ?? "";
401+
const legacyCronRaw = normalizeOptionalString(sched.cron) ?? "";
408402
let normalizedExpr = exprRaw;
409403
if (!normalizedExpr && legacyCronRaw) {
410404
normalizedExpr = legacyCronRaw;
@@ -461,7 +455,7 @@ export function normalizeStoredCronJobs(
461455

462456
const payloadKind =
463457
payloadRecord && typeof payloadRecord.kind === "string" ? payloadRecord.kind : "";
464-
const rawSessionTarget = typeof raw.sessionTarget === "string" ? raw.sessionTarget.trim() : "";
458+
const rawSessionTarget = normalizeOptionalString(raw.sessionTarget) ?? "";
465459
const loweredSessionTarget = normalizeLowercaseStringOrEmpty(rawSessionTarget);
466460
if (loweredSessionTarget === "main" || loweredSessionTarget === "isolated") {
467461
if (raw.sessionTarget !== loweredSessionTarget) {

src/cron/normalize.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { sanitizeAgentId } from "../routing/session-key.js";
22
import {
33
normalizeLowercaseStringOrEmpty,
44
normalizeOptionalLowercaseString,
5+
normalizeOptionalString,
56
} from "../shared/string-coerce.js";
67
import { isRecord } from "../utils.js";
78
import {
@@ -50,8 +51,8 @@ function normalizeTrimmedStringArray(
5051
): string[] | null | undefined {
5152
if (Array.isArray(value)) {
5253
const normalized = value
53-
.filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0)
54-
.map((entry) => entry.trim());
54+
.map((entry) => normalizeOptionalString(entry))
55+
.filter((entry): entry is string => Boolean(entry));
5556
if (normalized.length === 0 && value.length > 0) {
5657
return undefined;
5758
}
@@ -67,12 +68,12 @@ function coerceSchedule(schedule: UnknownRecord) {
6768
const next: UnknownRecord = { ...schedule };
6869
const rawKind = normalizeLowercaseStringOrEmpty(schedule.kind);
6970
const kind = rawKind === "at" || rawKind === "every" || rawKind === "cron" ? rawKind : undefined;
70-
const exprRaw = typeof schedule.expr === "string" ? schedule.expr.trim() : "";
71-
const legacyCronRaw = typeof schedule.cron === "string" ? schedule.cron.trim() : "";
71+
const exprRaw = normalizeOptionalString(schedule.expr) ?? "";
72+
const legacyCronRaw = normalizeOptionalString(schedule.cron) ?? "";
7273
const normalizedExpr = exprRaw || legacyCronRaw;
7374
const atMsRaw = schedule.atMs;
7475
const atRaw = schedule.at;
75-
const atString = typeof atRaw === "string" ? atRaw.trim() : "";
76+
const atString = normalizeOptionalString(atRaw) ?? "";
7677
const parsedAtMs =
7778
typeof atMsRaw === "number"
7879
? atMsRaw
@@ -154,8 +155,8 @@ function coercePayload(payload: UnknownRecord) {
154155
next.kind = kindRaw;
155156
}
156157
if (!next.kind) {
157-
const hasMessage = typeof next.message === "string" && next.message.trim().length > 0;
158-
const hasText = typeof next.text === "string" && next.text.trim().length > 0;
158+
const hasMessage = Boolean(normalizeOptionalString(next.message));
159+
const hasText = Boolean(normalizeOptionalString(next.text));
159160
if (hasMessage) {
160161
next.kind = "agentTurn";
161162
} else if (hasText) {
@@ -166,13 +167,13 @@ function coercePayload(payload: UnknownRecord) {
166167
}
167168
}
168169
if (typeof next.message === "string") {
169-
const trimmed = next.message.trim();
170+
const trimmed = normalizeOptionalString(next.message) ?? "";
170171
if (trimmed) {
171172
next.message = trimmed;
172173
}
173174
}
174175
if (typeof next.text === "string") {
175-
const trimmed = next.text.trim();
176+
const trimmed = normalizeOptionalString(next.text) ?? "";
176177
if (trimmed) {
177178
next.text = trimmed;
178179
}
@@ -288,12 +289,12 @@ function coerceDelivery(delivery: UnknownRecord) {
288289
}
289290

290291
function inferTopLevelPayload(next: UnknownRecord) {
291-
const message = typeof next.message === "string" ? next.message.trim() : "";
292+
const message = normalizeOptionalString(next.message) ?? "";
292293
if (message) {
293294
return { kind: "agentTurn", message } satisfies UnknownRecord;
294295
}
295296

296-
const text = typeof next.text === "string" ? next.text.trim() : "";
297+
const text = normalizeOptionalString(next.text) ?? "";
297298
if (text) {
298299
return { kind: "systemEvent", text } satisfies UnknownRecord;
299300
}
@@ -344,12 +345,13 @@ function normalizeWakeMode(raw: unknown) {
344345

345346
function copyTopLevelAgentTurnFields(next: UnknownRecord, payload: UnknownRecord) {
346347
const copyString = (field: "model" | "thinking") => {
347-
if (typeof payload[field] === "string" && payload[field].trim()) {
348+
if (normalizeOptionalString(payload[field])) {
348349
return;
349350
}
350351
const value = next[field];
351-
if (typeof value === "string" && value.trim()) {
352-
payload[field] = value.trim();
352+
const normalized = normalizeOptionalString(value);
353+
if (normalized) {
354+
payload[field] = normalized;
353355
}
354356
};
355357
copyString("model");

src/gateway/server-node-events.ts

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ function parseSessionKeyFromPayloadJSON(payloadJSON: string): string | null {
212212
return null;
213213
}
214214
const obj = payload as Record<string, unknown>;
215-
const sessionKey = typeof obj.sessionKey === "string" ? obj.sessionKey.trim() : "";
215+
const sessionKey = normalizeOptionalString(obj.sessionKey) ?? "";
216216
return sessionKey.length > 0 ? sessionKey : null;
217217
}
218218

@@ -270,14 +270,14 @@ export const handleNodeEvent = async (ctx: NodeEventContext, nodeId: string, evt
270270
if (!obj) {
271271
return;
272272
}
273-
const text = typeof obj.text === "string" ? obj.text.trim() : "";
273+
const text = normalizeOptionalString(obj.text) ?? "";
274274
if (!text) {
275275
return;
276276
}
277277
if (text.length > 20_000) {
278278
return;
279279
}
280-
const sessionKeyRaw = typeof obj.sessionKey === "string" ? obj.sessionKey.trim() : "";
280+
const sessionKeyRaw = normalizeOptionalString(obj.sessionKey) ?? "";
281281
const cfg = loadConfig();
282282
const rawMainKey = normalizeMainKey(cfg.session?.mainKey);
283283
const sessionKey = sessionKeyRaw.length > 0 ? sessionKeyRaw : rawMainKey;
@@ -422,7 +422,7 @@ export const handleNodeEvent = async (ctx: NodeEventContext, nodeId: string, evt
422422
return;
423423
}
424424

425-
const channelRaw = typeof link?.channel === "string" ? link.channel.trim() : "";
425+
const channelRaw = normalizeOptionalString(link?.channel) ?? "";
426426
let channel = normalizeChannelId(channelRaw) ?? undefined;
427427
let to = normalizeOptionalString(link?.to);
428428
const deliverRequested = Boolean(link?.deliver);
@@ -440,7 +440,7 @@ export const handleNodeEvent = async (ctx: NodeEventContext, nodeId: string, evt
440440
typeof entry?.lastChannel === "string"
441441
? normalizeChannelId(entry.lastChannel)
442442
: undefined;
443-
const entryTo = typeof entry?.lastTo === "string" ? entry.lastTo.trim() : "";
443+
const entryTo = normalizeOptionalString(entry?.lastTo) ?? "";
444444
if (!channel && entryChannel) {
445445
channel = entryChannel;
446446
}
@@ -577,8 +577,7 @@ export const handleNodeEvent = async (ctx: NodeEventContext, nodeId: string, evt
577577
if (!obj) {
578578
return;
579579
}
580-
const sessionKeyRaw =
581-
typeof obj.sessionKey === "string" ? obj.sessionKey.trim() : `node-${nodeId}`;
580+
const sessionKeyRaw = normalizeOptionalString(obj.sessionKey) ?? `node-${nodeId}`;
582581
if (!sessionKeyRaw) {
583582
return;
584583
}
@@ -595,15 +594,15 @@ export const handleNodeEvent = async (ctx: NodeEventContext, nodeId: string, evt
595594
return;
596595
}
597596

598-
const runId = typeof obj.runId === "string" ? obj.runId.trim() : "";
599-
const command = typeof obj.command === "string" ? obj.command.trim() : "";
597+
const runId = normalizeOptionalString(obj.runId) ?? "";
598+
const command = normalizeOptionalString(obj.command) ?? "";
600599
const exitCode =
601600
typeof obj.exitCode === "number" && Number.isFinite(obj.exitCode)
602601
? obj.exitCode
603602
: undefined;
604603
const timedOut = obj.timedOut === true;
605-
const output = typeof obj.output === "string" ? obj.output.trim() : "";
606-
const reason = typeof obj.reason === "string" ? obj.reason.trim() : "";
604+
const output = normalizeOptionalString(obj.output) ?? "";
605+
const reason = normalizeOptionalString(obj.reason) ?? "";
607606

608607
let text = "";
609608
if (evt.event === "exec.started") {
@@ -646,8 +645,7 @@ export const handleNodeEvent = async (ctx: NodeEventContext, nodeId: string, evt
646645
const environment = obj.environment;
647646
try {
648647
if (transport === "relay") {
649-
const gatewayDeviceId =
650-
typeof obj.gatewayDeviceId === "string" ? obj.gatewayDeviceId.trim() : "";
648+
const gatewayDeviceId = normalizeOptionalString(obj.gatewayDeviceId) ?? "";
651649
const currentGatewayDeviceId = loadOrCreateDeviceIdentity().deviceId;
652650
if (!gatewayDeviceId || gatewayDeviceId !== currentGatewayDeviceId) {
653651
ctx.logGateway.warn(

0 commit comments

Comments
 (0)