src/lib/messaging/validators.ts
File Relationships
Symbols by Kind
function
14
type
5
All Symbols
| Name | Kind | Visibility | Status | Lines | Signature |
|---|---|---|---|---|---|
| parseIncomingMessage | function | exported- | 163-168 | parseIncomingMessage(
input: unknown,
): : { type: string; payload?: unknown } | null |
|
| parseRulePayload | function | exported- | 175-178 | parseRulePayload(input: unknown): : FieldRule | null |
|
| parseSettingsPayload | function | exported- | 185-188 | parseSettingsPayload(input: unknown): : Partial<Settings> | null |
|
| parseIgnoredFieldPayload | function | exported- | 195-200 | parseIgnoredFieldPayload(
input: unknown,
): : { urlPattern: string; selector: string; label: string } | null |
|
| parseSaveFieldCachePayload | function | exported- | 207-214 | parseSaveFieldCachePayload(
input: unknown,
): : { url: string; fields: DetectedFieldSummary[] } | null |
|
| parseSavedFormPayload | function | exported- | 221-224 | parseSavedFormPayload(input: unknown): : SavedForm | null |
|
| parseApplyTemplatePayload | function | exported- | 231-233 | parseApplyTemplatePayload(input: unknown): : SavedForm | null |
|
| parseStartWatchingPayload | function | exported- | 240-245 | parseStartWatchingPayload(
input: unknown,
): : { autoRefill?: boolean; debounceMs?: number; shadowDOM?: boolean } | null |
|
| parseStringPayload | function | exported- | 252-255 | parseStringPayload(input: unknown): : string | null |
|
| SaveFieldOverridePayload | type | exported- | 271-273 | type SaveFieldOverridePayload |
|
| parseSaveFieldOverridePayload | function | exported- | 280-285 | parseSaveFieldOverridePayload(
input: unknown,
): : SaveFieldOverridePayload | null |
|
| DeleteFieldOverridePayload | type | exported- | 294-296 | type DeleteFieldOverridePayload |
|
| parseDeleteFieldOverridePayload | function | exported- | 303-308 | parseDeleteFieldOverridePayload(
input: unknown,
): : DeleteFieldOverridePayload | null |
|
| DemoReplayStartPayload | type | exported- | 320-322 | type DemoReplayStartPayload |
|
| parseDemoReplayStartPayload | function | exported- | 325-330 | parseDemoReplayStartPayload(
input: unknown,
): : DemoReplayStartPayload | null |
|
| DemoRecordScreenStartPayload | type | exported- | 338-340 | type DemoRecordScreenStartPayload |
|
| parseDemoRecordScreenStartPayload | function | exported- | 343-348 | parseDemoRecordScreenStartPayload(
input: unknown,
): : DemoRecordScreenStartPayload | null |
|
| DemoGetStreamIdPayload | type | exported- | 356-358 | type DemoGetStreamIdPayload |
|
| parseDemoGetStreamIdPayload | function | exported- | 361-366 | parseDemoGetStreamIdPayload(
input: unknown,
): : DemoGetStreamIdPayload | null |
Full Source
/**
* Full Zod validators for extension messages.
*
* Used in the background service worker and options page where strict
* schema validation is acceptable. Content script should use
* `light-validators.ts` instead for performance.
*/
import { z } from "zod";
import type { FieldRule, SavedForm, Settings } from "@/types";
import type { DetectedFieldSummary } from "@/types";
import { FIELD_TYPES } from "@/types";
const messageSchema = z.object({
type: z.string().min(1),
payload: z.unknown().optional(),
});
const settingsSchema = z
.object({
autoFillOnLoad: z.boolean(),
defaultStrategy: z.enum(["ai", "tensorflow", "random"]),
useChromeAI: z.boolean(),
forceAIFirst: z.boolean(),
shortcut: z.string(),
locale: z.enum(["pt-BR", "en-US"]),
uiLanguage: z.enum(["auto", "en", "pt_BR", "es"]),
highlightFilled: z.boolean(),
cacheEnabled: z.boolean(),
showFieldIcon: z.boolean(),
fieldIconPosition: z.enum(["above", "inside", "below"]),
showPanel: z.boolean(),
fillEmptyOnly: z.boolean(),
detectionPipeline: z
.array(z.object({ name: z.string().min(1), enabled: z.boolean() }))
.optional(),
debugLog: z.boolean(),
logLevel: z.enum(["debug", "info", "warn", "error"]),
watcherEnabled: z.boolean(),
watcherAutoRefill: z.boolean(),
watcherShadowDOM: z.boolean(),
watcherDebounceMs: z.number().int().min(100).max(5000),
aiTimeoutMs: z.number().int().min(2000).max(15000),
showAiBadge: z.boolean(),
showFillToast: z.boolean(),
logMaxEntries: z.number().int().min(100).max(10000),
})
.partial()
.strict();
const generatorParamsSchema = z
.object({
min: z.number().optional(),
max: z.number().optional(),
formatted: z.boolean().optional(),
length: z.number().int().min(1).max(128).optional(),
onlyNumbers: z.boolean().optional(),
onlyLetters: z.boolean().optional(),
prefix: z.string().optional(),
suffix: z.string().optional(),
pattern: z.string().optional(),
options: z.array(z.string()).optional(),
probability: z.number().min(0).max(1).optional(),
dateFormat: z.enum(["iso", "br", "us"]).optional(),
})
.passthrough()
.optional();
const fieldRuleSchema = z
.object({
id: z.string().min(1),
urlPattern: z.string().min(1),
fieldSelector: z.string().min(1),
fieldName: z.string().optional(),
fieldType: z.enum(FIELD_TYPES),
fixedValue: z.string().optional(),
generator: z.enum(["auto", "ai", "tensorflow", ...FIELD_TYPES]),
aiPrompt: z.string().optional(),
generatorParams: generatorParamsSchema,
selectOptionIndex: z.number().optional(),
priority: z.number().min(0).max(100),
createdAt: z.number(),
updatedAt: z.number(),
})
.strict();
const ignoredFieldPayloadSchema = z
.object({
urlPattern: z.string().min(1),
selector: z.string().min(1),
label: z.string(),
})
.strict();
const detectedFieldSummarySchema = z
.object({
selector: z.string().min(1),
fieldType: z.string().min(1),
label: z.string(),
name: z.string().optional(),
id: z.string().optional(),
placeholder: z.string().optional(),
required: z.boolean().optional(),
contextualType: z.string().optional(),
detectionMethod: z.string().optional(),
options: z
.array(
z.object({
value: z.string(),
text: z.string(),
}),
)
.optional(),
checkboxValue: z.string().optional(),
checkboxChecked: z.boolean().optional(),
})
.strict();
const saveFieldCachePayloadSchema = z
.object({
url: z.string().min(1),
fields: z.array(detectedFieldSummarySchema),
})
.strict();
const templateFieldSchema = z
.object({
key: z.string().min(1),
label: z.string(),
mode: z.enum(["fixed", "generator"]),
fixedValue: z.string().optional(),
generatorType: z.enum(FIELD_TYPES).optional(),
matchByFieldType: z.enum(FIELD_TYPES).optional(),
})
.strict();
const savedFormSchema = z
.object({
id: z.string().min(1),
name: z.string().min(1),
urlPattern: z.string().min(1),
fields: z.record(z.string(), z.string()),
templateFields: z.array(templateFieldSchema).optional(),
createdAt: z.number(),
updatedAt: z.number(),
isDefault: z.boolean().optional(),
})
.strict();
const startWatchingSchema = z
.object({
autoRefill: z.boolean().optional(),
debounceMs: z.number().int().min(100).max(5000).optional(),
shadowDOM: z.boolean().optional(),
})
.strict();
/**
* Parses and validates a raw extension message envelope.
* @param input - Raw message from `chrome.runtime.onMessage`
* @returns Parsed `{ type, payload }` or `null` if invalid
*/
export function parseIncomingMessage(
input: unknown,
): { type: string; payload?: unknown } | null {
const result = messageSchema.safeParse(input);
return result.success ? result.data : null;
}
/**
* Parses and validates a field rule payload against the strict Zod schema.
* @param input - Raw payload from a `SAVE_RULE` message
* @returns Validated `FieldRule` or `null` if validation fails
*/
export function parseRulePayload(input: unknown): FieldRule | null {
const result = fieldRuleSchema.safeParse(input);
return result.success ? (result.data as FieldRule) : null;
}
/**
* Parses and validates a partial settings payload.
* @param input - Raw settings object from a `SAVE_SETTINGS` message
* @returns Validated partial `Settings` or `null`
*/
export function parseSettingsPayload(input: unknown): Partial<Settings> | null {
const result = settingsSchema.safeParse(input);
return result.success ? (result.data as Partial<Settings>) : null;
}
/**
* Parses and validates an ignored-field payload.
* @param input - Raw payload from an `ADD_IGNORED_FIELD` message
* @returns Validated object or `null`
*/
export function parseIgnoredFieldPayload(
input: unknown,
): { urlPattern: string; selector: string; label: string } | null {
const result = ignoredFieldPayloadSchema.safeParse(input);
return result.success ? result.data : null;
}
/**
* Parses and validates a field detection cache payload.
* @param input - Raw payload containing URL and detected field summaries
* @returns Validated cache payload or `null`
*/
export function parseSaveFieldCachePayload(
input: unknown,
): { url: string; fields: DetectedFieldSummary[] } | null {
const result = saveFieldCachePayloadSchema.safeParse(input);
return result.success
? (result.data as { url: string; fields: DetectedFieldSummary[] })
: null;
}
/**
* Parses and validates a saved form payload.
* @param input - Raw payload from a `SAVE_FORM` message
* @returns Validated `SavedForm` or `null`
*/
export function parseSavedFormPayload(input: unknown): SavedForm | null {
const result = savedFormSchema.safeParse(input);
return result.success ? (result.data as SavedForm) : null;
}
/**
* Parses a template-apply payload (same schema as saved form).
* @param input - Raw payload from an `APPLY_TEMPLATE` message
* @returns Validated `SavedForm` or `null`
*/
export function parseApplyTemplatePayload(input: unknown): SavedForm | null {
return parseSavedFormPayload(input);
}
/**
* Parses the optional payload for `START_WATCHING` messages.
* @param input - Raw payload (may be `undefined`)
* @returns Object with optional `autoRefill` flag, or `null` if invalid
*/
export function parseStartWatchingPayload(
input: unknown,
): { autoRefill?: boolean; debounceMs?: number; shadowDOM?: boolean } | null {
const result = startWatchingSchema.safeParse(input ?? {});
return result.success ? result.data : null;
}
/**
* Parses a payload expected to be a non-empty string.
* @param input - Raw payload value
* @returns The string, or `null` if not a valid non-empty string
*/
export function parseStringPayload(input: unknown): string | null {
const result = z.string().min(1).safeParse(input);
return result.success ? result.data : null;
}
const saveFieldOverridePayloadSchema = z
.object({
url: z.string().min(1),
fieldSelector: z.string().min(1),
fieldName: z.string().optional(),
fieldType: z.enum(FIELD_TYPES),
generator: z.enum(["auto", "ai", "tensorflow", ...FIELD_TYPES]),
fixedValue: z.string().optional(),
aiPrompt: z.string().optional(),
generatorParams: generatorParamsSchema,
selectOptionIndex: z.number().optional(),
})
.strict();
export type SaveFieldOverridePayload = z.infer<
typeof saveFieldOverridePayloadSchema
>;
/**
* Parses and validates a `SAVE_FIELD_OVERRIDE` payload.
* @param input - Raw payload
* @returns Validated payload or `null` if invalid
*/
export function parseSaveFieldOverridePayload(
input: unknown,
): SaveFieldOverridePayload | null {
const result = saveFieldOverridePayloadSchema.safeParse(input);
return result.success ? result.data : null;
}
const deleteFieldOverridePayloadSchema = z
.object({
url: z.string().min(1),
fieldSelector: z.string().min(1),
})
.strict();
export type DeleteFieldOverridePayload = z.infer<
typeof deleteFieldOverridePayloadSchema
>;
/**
* Parses and validates a `DELETE_FIELD_OVERRIDE` payload.
* @param input - Raw payload
* @returns Validated `{ url, fieldSelector }` or `null` if invalid
*/
export function parseDeleteFieldOverridePayload(
input: unknown,
): DeleteFieldOverridePayload | null {
const result = deleteFieldOverridePayloadSchema.safeParse(input);
return result.success ? result.data : null;
}
// ── Demo message validators ──────────────────────────────────────────────
const demoReplayStartPayloadSchema = z
.object({
flowId: z.string().min(1),
tabId: z.number().int().positive(),
config: z.record(z.string(), z.unknown()).optional(),
})
.strict();
export type DemoReplayStartPayload = z.infer<
typeof demoReplayStartPayloadSchema
>;
/** Parses payload for `DEMO_REPLAY_START`. */
export function parseDemoReplayStartPayload(
input: unknown,
): DemoReplayStartPayload | null {
const result = demoReplayStartPayloadSchema.safeParse(input);
return result.success ? result.data : null;
}
const demoRecordScreenStartPayloadSchema = z
.object({
tabId: z.number().int().positive(),
})
.strict();
export type DemoRecordScreenStartPayload = z.infer<
typeof demoRecordScreenStartPayloadSchema
>;
/** Parses payload for `DEMO_RECORD_SCREEN_START`. */
export function parseDemoRecordScreenStartPayload(
input: unknown,
): DemoRecordScreenStartPayload | null {
const result = demoRecordScreenStartPayloadSchema.safeParse(input);
return result.success ? result.data : null;
}
const demoGetStreamIdPayloadSchema = z
.object({
tabId: z.number().int().positive(),
})
.strict();
export type DemoGetStreamIdPayload = z.infer<
typeof demoGetStreamIdPayloadSchema
>;
/** Parses payload for `DEMO_GET_STREAM_ID`. */
export function parseDemoGetStreamIdPayload(
input: unknown,
): DemoGetStreamIdPayload | null {
const result = demoGetStreamIdPayloadSchema.safeParse(input);
return result.success ? result.data : null;
}