mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
refactor: remove passthrough() from zod schemas and make validation explicit
- Replace .passthrough() with explicit field definitions across all schemas - Add missing optional fields (homeDir, happyHomeDir, happyLibDir, displayName) - Refactor RawJSONLinesSchema to use structured base schema for clarity - Improve schema validation strictness and type safety - Update Machine interface to reflect explicit fields instead of index signature
This commit is contained in:
@@ -29,10 +29,11 @@ export const MachineMetadataSchema = z.object({
|
||||
host: z.string(),
|
||||
platform: z.string(),
|
||||
happyCliVersion: z.string(),
|
||||
displayName: z.string().optional(),
|
||||
homeDir: z.string(),
|
||||
happyHomeDir: z.string(),
|
||||
happyLibDir: z.string()
|
||||
}).passthrough()
|
||||
})
|
||||
|
||||
export type MachineMetadata = z.infer<typeof MachineMetadataSchema>
|
||||
|
||||
@@ -43,7 +44,7 @@ export const RunnerStateSchema = z.object({
|
||||
startedAt: z.number().optional(),
|
||||
shutdownRequestedAt: z.number().optional(),
|
||||
shutdownSource: z.union([z.enum(['mobile-app', 'cli', 'os-signal', 'unknown']), z.string()]).optional()
|
||||
}).passthrough()
|
||||
})
|
||||
|
||||
export type RunnerState = z.infer<typeof RunnerStateSchema>
|
||||
|
||||
@@ -119,7 +120,7 @@ export const MessageMetaSchema = z.object({
|
||||
appendSystemPrompt: z.string().nullable().optional(),
|
||||
allowedTools: z.array(z.string()).nullable().optional(),
|
||||
disallowedTools: z.array(z.string()).nullable().optional()
|
||||
}).passthrough()
|
||||
})
|
||||
|
||||
export type MessageMeta = z.infer<typeof MessageMetaSchema>
|
||||
|
||||
@@ -132,7 +133,7 @@ export const UserMessageSchema = z.object({
|
||||
}),
|
||||
localKey: z.string().optional(),
|
||||
meta: MessageMetaSchema.optional()
|
||||
}).passthrough()
|
||||
})
|
||||
|
||||
export type UserMessage = z.infer<typeof UserMessageSchema>
|
||||
|
||||
@@ -143,7 +144,7 @@ export const AgentMessageSchema = z.object({
|
||||
data: z.unknown()
|
||||
}),
|
||||
meta: MessageMetaSchema.optional()
|
||||
}).passthrough()
|
||||
})
|
||||
|
||||
export type AgentMessage = z.infer<typeof AgentMessageSchema>
|
||||
|
||||
|
||||
+53
-30
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* Simplified schema that only validates fields actually used in the codebase
|
||||
* while preserving all other fields through passthrough()
|
||||
* Schema validates fields used in the codebase and keeps explicit
|
||||
* log fields required by the CLI and UI.
|
||||
*/
|
||||
|
||||
import { z } from "zod";
|
||||
@@ -12,46 +12,69 @@ export const UsageSchema = z.object({
|
||||
cache_read_input_tokens: z.number().int().nonnegative().optional(),
|
||||
output_tokens: z.number().int().nonnegative(),
|
||||
service_tier: z.string().optional(),
|
||||
}).passthrough();
|
||||
});
|
||||
|
||||
// Main schema with minimal validation for only the fields we use
|
||||
// NOTE: Schema is intentionally lenient to handle various Claude Code message formats
|
||||
// including synthetic error messages, API errors, and different SDK versions
|
||||
const RawMessageSchema = z.object({
|
||||
role: z.string().optional(),
|
||||
content: z.unknown(),
|
||||
usage: UsageSchema.optional(),
|
||||
});
|
||||
|
||||
const RawJSONLinesBaseSchema = z.object({
|
||||
uuid: z.string().optional(),
|
||||
parentUuid: z.string().nullable().optional(),
|
||||
isSidechain: z.boolean().optional(),
|
||||
isMeta: z.boolean().optional(),
|
||||
isCompactSummary: z.boolean().optional(),
|
||||
userType: z.string().optional(),
|
||||
cwd: z.string().optional(),
|
||||
sessionId: z.string().optional(),
|
||||
version: z.string().optional(),
|
||||
gitBranch: z.string().optional(),
|
||||
timestamp: z.string().optional(),
|
||||
});
|
||||
|
||||
// Main schema with validation for the fields used in the app
|
||||
// NOTE: Schema remains lenient on message content to handle SDK variations
|
||||
export const RawJSONLinesSchema = z.discriminatedUnion("type", [
|
||||
// User message - validates uuid and message.content
|
||||
z.object({
|
||||
RawJSONLinesBaseSchema.extend({
|
||||
type: z.literal("user"),
|
||||
isSidechain: z.boolean().optional(),
|
||||
isMeta: z.boolean().optional(),
|
||||
uuid: z.string(), // Used in getMessageKey()
|
||||
message: z.object({
|
||||
content: z.union([z.string(), z.any()]) // Used in sessionScanner.ts
|
||||
}).passthrough()
|
||||
}).passthrough(),
|
||||
uuid: z.string(),
|
||||
message: RawMessageSchema,
|
||||
mode: z.string().optional(),
|
||||
toolUseResult: z.unknown().optional(),
|
||||
}),
|
||||
|
||||
// Assistant message - only validates uuid and type
|
||||
// message object is optional to handle synthetic error messages (isApiErrorMessage: true)
|
||||
// which may have different structure than normal assistant messages
|
||||
z.object({
|
||||
// message object is optional to handle synthetic error messages
|
||||
RawJSONLinesBaseSchema.extend({
|
||||
uuid: z.string(),
|
||||
type: z.literal("assistant"),
|
||||
message: z.object({
|
||||
usage: UsageSchema.optional(), // Used in apiSession.ts
|
||||
}).passthrough().optional()
|
||||
}).passthrough(),
|
||||
message: RawMessageSchema.optional(),
|
||||
requestId: z.string().optional(),
|
||||
}),
|
||||
|
||||
// Summary message - validates summary and leafUuid
|
||||
z.object({
|
||||
RawJSONLinesBaseSchema.extend({
|
||||
type: z.literal("summary"),
|
||||
summary: z.string(), // Used in apiSession.ts
|
||||
leafUuid: z.string() // Used in getMessageKey()
|
||||
}).passthrough(),
|
||||
summary: z.string(),
|
||||
leafUuid: z.string(),
|
||||
}),
|
||||
|
||||
// System message - validates uuid
|
||||
z.object({
|
||||
// System message - validates uuid and subtype data used by the UI
|
||||
RawJSONLinesBaseSchema.extend({
|
||||
type: z.literal("system"),
|
||||
uuid: z.string() // Used in getMessageKey()
|
||||
}).passthrough()
|
||||
uuid: z.string(),
|
||||
subtype: z.string().optional(),
|
||||
model: z.string().optional(),
|
||||
tools: z.array(z.string()).optional(),
|
||||
session_id: z.string().optional(),
|
||||
retryAttempt: z.number().optional(),
|
||||
maxRetries: z.number().optional(),
|
||||
error: z.unknown().optional(),
|
||||
durationMs: z.number().optional(),
|
||||
}),
|
||||
]);
|
||||
|
||||
export type RawJSONLines = z.infer<typeof RawJSONLinesSchema>
|
||||
export type RawJSONLines = z.infer<typeof RawJSONLinesSchema>;
|
||||
|
||||
@@ -182,7 +182,7 @@ export class CodexMcpClient {
|
||||
params: z.object({
|
||||
msg: z.any()
|
||||
})
|
||||
}).passthrough();
|
||||
});
|
||||
|
||||
const setNotificationHandler =
|
||||
this.client.setNotificationHandler.bind(this.client) as (
|
||||
|
||||
@@ -6,7 +6,7 @@ const CodexSessionEventSchema = z.object({
|
||||
timestamp: z.string().optional(),
|
||||
type: z.string(),
|
||||
payload: z.unknown().optional()
|
||||
}).passthrough();
|
||||
});
|
||||
|
||||
export type CodexSessionEvent = z.infer<typeof CodexSessionEventSchema>;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user