diff --git a/skills/boundary-validator/references/api-contract.md b/skills/boundary-validator/references/api-contract.md index 7a0996f..cbaea5a 100644 --- a/skills/boundary-validator/references/api-contract.md +++ b/skills/boundary-validator/references/api-contract.md @@ -8,280 +8,102 @@ These constraints ensure API stability and prevent breaking changes. ## Public API (Minimal and Stable) -The safe-formdata public API consists of a single function and utility types: +Single function, no overloads, no options: ```typescript parse(formData: FormData): ParseResult +``` + +Named utility types derived from `ParseResult`: -// Named utility types derived from ParseResult +```typescript type SuccessResult = Extract }>; type FailureResult = Extract; ``` -### Constraints - -- **No overloads**: The function has exactly one signature -- **No options**: No optional parameters or configuration objects -- **No framework-specific adapters**: Works with standard FormData only +See implementation: `src/index.ts`, `src/parse.ts` -### Rationale - -A minimal API surface reduces maintenance burden, prevents feature creep, and maintains the boundary-focused philosophy. Any additional functionality belongs in separate libraries that build on top of safe-formdata. - -### Examples - -**❌ Violations**: +### Violations ```typescript -// Adding overloads +// ❌ Adding overloads function parse(formData: FormData): ParseResult; function parse(formData: FormData, options: ParseOptions): ParseResult; -// Adding options +// ❌ Adding options function parse(formData: FormData, options?: { allowDuplicates?: boolean }): ParseResult; -// Adding framework adapters +// ❌ Framework adapters function parseRequest(req: NextRequest): ParseResult; -function parseFromExpress(req: express.Request): ParseResult; -``` - -**✅ Correct**: - -```typescript -// Single, stable signature -export function parse(formData: FormData): ParseResult { - // Implementation -} ``` --- ## Type Definitions -### ParseResult (Discriminated Union) - -```typescript -export type ParseResult = - | { - data: Record; - issues: []; - } - | { - data: null; - issues: [ParseIssue, ...ParseIssue[]]; - }; -``` - -#### Constraints +See source: `src/types/ParseResult.ts`, `src/types/ParseIssue.ts`, `src/types/IssueCode.ts` -- **Must be a discriminated union**: Two distinct shapes based on success/failure -- **Success state**: `data` is a Record, `issues` is an empty array (literal type `[]`) -- **Failure state**: `data` is `null`, `issues` is a non-empty tuple (`[ParseIssue, ...ParseIssue[]]`) -- **No intermediate states**: Partial success is not allowed - -#### Type Narrowing Pattern +### Type Narrowing Pattern ```typescript if (result.data !== null) { - // TypeScript knows: data is Record - // TypeScript knows: issues is [] - console.log(result.data.username); + // data is Record, issues is [] } else { - // TypeScript knows: data is null - // TypeScript knows: issues is [ParseIssue, ...ParseIssue[]] - console.error(result.issues); + // data is null, issues is [ParseIssue, ...ParseIssue[]] } ``` -#### Important: No `.ok` Property - -Unlike some libraries (e.g., `Result` types), `ParseResult` does **not** have a `.ok` property. - -**❌ Wrong**: +### No `.ok` Property ```typescript +// ❌ Wrong if (result.ok) { /* ... */ } -``` -**✅ Correct**: - -```typescript +// ✅ Correct if (result.data !== null) { /* ... */ } ``` ---- - -### ParseIssue - -```typescript -export interface ParseIssue { - code: IssueCode; - key: string; -} -``` - -#### Constraints - -- **`code`**: Must be one of the allowed IssueCode values -- **`key`**: Must be the original FormData key that caused the issue, reported as-is without interpretation -- **Issues are informational, not exceptions** - -#### Future Considerations - -In v1.0+, additional fields may be considered: - -- `message?: string` - Human-readable error message -- `meta?: Record` - Additional metadata - -**Any such changes would require a major version bump.** - ---- - -### IssueCode - -```typescript -export type IssueCode = "invalid_key" | "forbidden_key" | "duplicate_key"; -``` - -#### Allowed Values (Fixed) - -- `invalid_key` - Key is invalid (empty, wrong type, etc.) -- `forbidden_key` - Key is forbidden (`__proto__`, `constructor`, `prototype`) -- `duplicate_key` - Key appears multiple times in FormData - -#### Constraint +### IssueCode Constraint **No additional IssueCode may be introduced without a major version bump.** -#### Examples - -**❌ Violations** (Breaking changes): - -```typescript -// Adding new issue codes (requires major version bump) -export type IssueCode = - | "invalid_key" - | "forbidden_key" - | "duplicate_key" - | "invalid_value" // NEW - Breaking change! - | "too_many_fields"; // NEW - Breaking change! - -// Removing existing codes (requires major version bump) -export type IssueCode = "invalid_key" | "forbidden_key"; -// "duplicate_key" removed - Breaking change! - -// Renaming codes (requires major version bump) -export type IssueCode = "bad_key" | "forbidden_key" | "duplicate_key"; -// "invalid_key" → "bad_key" - Breaking change! -``` - -**✅ Correct** (Non-breaking changes): - ```typescript -// v0.1.0 - Initial release -export type IssueCode = "invalid_key" | "forbidden_key" | "duplicate_key"; - -// v0.2.0 - No changes to IssueCode (patch or minor bump allowed) -export type IssueCode = "invalid_key" | "forbidden_key" | "duplicate_key"; - -// v1.0.0 - Major version allows changes -export type IssueCode = "invalid_key" | "forbidden_key" | "duplicate_key" | "invalid_value"; // OK in major version +// ❌ Adding codes — requires major version bump +export type IssueCode = "invalid_key" | "forbidden_key" | "duplicate_key" | "invalid_value"; ``` --- -## Type Documentation - -All type definitions include comprehensive JSDoc comments for IDE integration. - -See: - -- `src/types/ParseResult.ts` - Discriminated union with type narrowing examples -- `src/types/ParseIssue.ts` - Issue structure and property explanations -- `src/types/IssueCode.ts` - Security-focused issue code definitions - -### JSDoc Example - -````typescript -/** - * Result of parsing FormData. - * - * This is a discriminated union: - * - Success: `data` is a Record, `issues` is `[]` - * - Failure: `data` is `null`, `issues` contains errors - * - * Use `data !== null` to narrow the type: - * - * @example - * ```ts - * const result = parse(formData); - * - * if (result.data !== null) { - * // Success: result.data is Record - * console.log(result.data.username); - * } else { - * // Failure: result.issues is ParseIssue[] - * console.error(result.issues); - * } - * ``` - */ -export type ParseResult = - | { data: Record; issues: [] } - | { data: null; issues: [ParseIssue, ...ParseIssue[]] }; -```` - ---- - ## Versioning Policy -For complete versioning policy, see README.md Versioning section. - -### Key Points - - **Patch versions** (0.1.x): bugfixes, no API changes - **Minor versions** (0.x.0): Breaking changes allowed in 0.x (treated as effectively major) - **Major versions** (1.0.0+): Breaking changes allowed ### What Counts as Breaking -The following changes are **breaking** and require a major version bump: - - Adding/removing `IssueCode` values - Changing `ParseResult` structure - Adding required parameters to `parse()` -- Changing return type of `parse()` - Removing or renaming exported types -### What Counts as Non-Breaking - -The following changes are **non-breaking** and allowed in minor/patch versions: - -- Bugfixes in parsing logic -- Performance improvements -- Internal refactoring -- Documentation improvements -- Adding optional JSDoc comments - --- ## Review Checklist -When reviewing API changes: - - [ ] Public API remains `parse(formData): ParseResult` only -- [ ] `SuccessResult` / `FailureResult` utility types are derived from `ParseResult` (not independently defined) +- [ ] `SuccessResult` / `FailureResult` are derived from `ParseResult` (not independently defined) - [ ] No overloads added - [ ] No options parameters added - [ ] `ParseResult` structure unchanged - [ ] `IssueCode` values unchanged (or major version bump planned) - [ ] Type narrowing pattern still works (`data !== null`) -- [ ] JSDoc comments updated (if applicable) --- **Source**: AGENTS.md (lines 119-199) -**Last updated**: 2026-03-02 +**Last updated**: 2026-03-06 diff --git a/skills/boundary-validator/references/design-rules.md b/skills/boundary-validator/references/design-rules.md index 6574b7b..f4f295e 100644 --- a/skills/boundary-validator/references/design-rules.md +++ b/skills/boundary-validator/references/design-rules.md @@ -16,9 +16,7 @@ These rules define the non-negotiable constraints for safe-formdata implementati FormData keys should be treated as opaque identifiers. Any interpretation of key structure (e.g., `user[name]` → nested object) introduces assumptions about the sender's intent, which violates the boundary principle. -### Examples - -**❌ Violations**: +### Violations ```typescript // Parsing bracket notation @@ -36,13 +34,7 @@ if (key.includes("[") && key.includes("]")) { } ``` -**✅ Correct**: - -```typescript -// Treat all keys as opaque strings -const data = Object.create(null); -data[key] = value; // No interpretation -``` +See correct implementation: `src/parse.ts` --- @@ -58,9 +50,7 @@ All duplicate keys are boundary violations. Silent behavior hides information from the application. If a key appears multiple times in FormData, the application must decide how to handle it. The parser's job is only to detect and report the violation, not to resolve it. -### Examples - -**❌ Violations**: +### Violations ```typescript // Merging duplicate keys @@ -80,29 +70,7 @@ if (!data[key]) { Object.assign(data, { [key]: value }); ``` -**✅ Correct**: - -```typescript -// Detect and report duplicates -const seen = new Set(); - -for (const [key, value] of formData.entries()) { - if (seen.has(key)) { - issues.push({ - code: "duplicate_key", - key, - }); - continue; // Do not process further - } - seen.add(key); - data[key] = value; -} - -// If issues exist, return null data -if (issues.length > 0) { - return { data: null, issues }; -} -``` +See correct implementation: `src/parse.ts` --- @@ -119,9 +87,7 @@ Convenience features belong outside the boundary. The parser's sole responsibility is to establish a predictable boundary. Inference, validation, and convenience features introduce complexity and assumptions that blur the boundary. -### Examples - -**❌ Violations**: +### Violations ```typescript // Array inference @@ -153,20 +119,10 @@ function parse( allowDuplicates?: boolean; convertTypes?: boolean; }, -) { - // Configuration adds complexity -} +) {} ``` -**✅ Correct**: - -```typescript -// No inference, no validation, no options -const data = Object.create(null); -for (const [key, value] of formData.entries()) { - data[key] = value; // Store as-is -} -``` +See correct implementation: `src/parse.ts` --- @@ -181,9 +137,7 @@ for (const [key, value] of formData.entries()) { Throwing exceptions forces callers to use try-catch, which is error-prone. Returning a structured result allows applications to handle issues explicitly and predictably. -### Examples - -**❌ Violations**: +### Violations ```typescript // Throwing exceptions @@ -191,10 +145,6 @@ if (invalidKey) { throw new Error("Invalid key"); // Forces try-catch } -if (forbiddenKey) { - throw new SecurityError("Forbidden key"); -} - // Partial success if (issues.length > 0) { return { @@ -204,32 +154,7 @@ if (issues.length > 0) { } ``` -**✅ Correct**: - -```typescript -// Return structured result -const issues: ParseIssue[] = []; -const data = Object.create(null); - -for (const [key, value] of formData.entries()) { - if (typeof key !== "string" || key.length === 0) { - issues.push({ - code: "invalid_key", - key, - }); - continue; - } - - data[key] = value; -} - -// All-or-nothing: if any issues, return null data -if (issues.length > 0) { - return { data: null, issues }; -} - -return { data, issues: [] }; -``` +See correct implementation: `src/parse.ts` --- @@ -237,9 +162,9 @@ return { data, issues: [] }; If a change makes the parser: -- **smarter** -- **more convenient** -- **more opinionated** +- smarter +- more convenient +- more opinionated it likely violates the boundary. diff --git a/skills/boundary-validator/references/security-rules.md b/skills/boundary-validator/references/security-rules.md index 77cc99c..bb84fd7 100644 --- a/skills/boundary-validator/references/security-rules.md +++ b/skills/boundary-validator/references/security-rules.md @@ -10,7 +10,7 @@ These rules are non-negotiable and must be enforced in all implementations. ### Forbidden Keys -Explicitly forbid the following keys. +Explicitly forbid the following keys: - `__proto__` - `constructor` @@ -22,9 +22,7 @@ Explicitly forbid the following keys. These keys can be exploited for prototype pollution attacks, which allow attackers to modify the prototype chain of JavaScript objects and potentially execute arbitrary code or cause denial-of-service. -### Examples - -**❌ Violations**: +### Violations ```typescript // Accepting forbidden keys @@ -37,63 +35,7 @@ for (const [key, value] of formData.entries()) { } ``` -**✅ Correct**: - -```typescript -const FORBIDDEN_KEYS: ReadonlySet = new Set(["__proto__", "constructor", "prototype"]); - -for (const [key, value] of formData.entries()) { - // Check for forbidden keys - if (FORBIDDEN_KEYS.has(key)) { - issues.push({ - code: "forbidden_key", - key, - }); - continue; // Do not process forbidden keys - } - - data[key] = value; -} -``` - -### Test Cases - -Implementations must include tests that verify forbidden keys are rejected: - -```typescript -// Test: __proto__ is rejected -const formData = new FormData(); -formData.append("__proto__", "malicious"); -const result = parse(formData); - -expect(result.data).toBeNull(); -expect(result.issues).toContainEqual({ - code: "forbidden_key", - key: "__proto__", -}); - -// Test: constructor is rejected -formData.clear(); -formData.append("constructor", "malicious"); -const result2 = parse(formData); - -expect(result2.data).toBeNull(); -expect(result2.issues).toContainEqual({ - code: "forbidden_key", - key: "constructor", -}); - -// Test: prototype is rejected -formData.clear(); -formData.append("prototype", "malicious"); -const result3 = parse(formData); - -expect(result3.data).toBeNull(); -expect(result3.issues).toContainEqual({ - code: "forbidden_key", - key: "prototype", -}); -``` +See correct implementation: `src/parse.ts` --- @@ -113,57 +55,22 @@ const data = Object.create(null); Using `{}` or `new Object()` creates objects with `Object.prototype` in their prototype chain. This makes the data container vulnerable to prototype pollution and adds unnecessary properties (`toString`, `hasOwnProperty`, etc.) that could conflict with FormData keys. -### Examples - -**❌ Violations**: +### Violations ```typescript // Using plain object literal const data = {}; -// data.__proto__ === Object.prototype (vulnerable) // Using Object constructor const data = new Object(); -// data.__proto__ === Object.prototype (vulnerable) - -// Inherited properties -const data = {}; -console.log(data.toString); // [Function: toString] (inherited) -``` - -**✅ Correct**: - -```typescript -// Create prototype-less object -const data = Object.create(null); -// data.__proto__ === undefined (safe) - -// No inherited properties -console.log(data.toString); // undefined -console.log(data.hasOwnProperty); // undefined - -// Safe to use any key -data["toString"] = "my value"; // No conflict with Object.prototype.toString ``` -### Type Safety - -When using TypeScript, the type should reflect the prototype-less nature: - -```typescript -// Correct type annotation -const data: Record = Object.create(null); -``` +See correct implementation: `src/parse.ts` --- ## Security Scope -For a complete understanding of what safe-formdata does and doesn't protect against, see: - -- README.md: Security scope section -- AGENTS.md: Security guarantees section - ### In Scope (What safe-formdata protects against) - ✅ Prototype pollution via forbidden keys diff --git a/skills/boundary-validator/references/validation-patterns.md b/skills/boundary-validator/references/validation-patterns.md index 6687b05..f44dbc3 100644 --- a/skills/boundary-validator/references/validation-patterns.md +++ b/skills/boundary-validator/references/validation-patterns.md @@ -20,63 +20,25 @@ When reviewing code, search for these antipatterns. ### Antipatterns to Detect -#### Pattern: Bracket Notation Parsing - ```typescript -// ❌ Violation: Parsing [] for arrays +// ❌ Parsing bracket notation if (key.endsWith("[]")) { /* ... */ } -if (key.includes("[]")) { - /* ... */ -} -key.slice(0, -2); // Removing [] - -// ❌ Violation: Parsing [index] for arrays +key.slice(0, -2); const match = key.match(/\[(\d+)\]/); -key.replace(/\[\d+\]/, ""); - -// ❌ Violation: Parsing [name] for nested objects -const match = key.match(/\[(\w+)\]/); -if (key.includes("[") && key.includes("]")) { - /* ... */ -} -``` -#### Pattern: Dot Notation Parsing - -```typescript -// ❌ Violation: Parsing . for nested objects +// ❌ Parsing dot notation if (key.includes(".")) { /* ... */ } const parts = key.split("."); -const [parent, child] = key.split("."); - -// ❌ Violation: Path parsing -key.split(".").reduce((obj, part) => obj[part], root); -``` -#### Pattern: Special Character Interpretation - -```typescript -// ❌ Violation: Treating special characters as delimiters +// ❌ Special character interpretation key.split(/[.\[\]]/); -key.replace(/[\[\]\.]/g, ""); ``` -### Correct Patterns - -```typescript -// ✅ Correct: Keys as opaque strings -const data = Object.create(null); -data[key] = value; // No interpretation - -// ✅ Correct: String comparison without parsing -if (key === "exact_key_name") { - /* ... */ -} -``` +See correct implementation: `src/parse.ts` --- @@ -84,67 +46,24 @@ if (key === "exact_key_name") { ### Antipatterns to Detect -#### Pattern: Merge/Overwrite - ```typescript -// ❌ Violation: Object.assign (merges) +// ❌ Merge / overwrite Object.assign(data, { [key]: value }); -Object.assign(data, newData); - -// ❌ Violation: Spread operator (merges) data = { ...data, [key]: value }; -data = { ...data, ...newData }; - -// ❌ Violation: Direct overwrite without checking -data[key] = value; // If key exists, this silently overwrites -``` +data[key] = value; // without duplicate check -#### Pattern: Array Accumulation - -```typescript -// ❌ Violation: Converting to array for duplicates +// ❌ Array accumulation if (data[key]) { data[key] = Array.isArray(data[key]) ? [...data[key], value] : [data[key], value]; } -// ❌ Violation: Array push -if (!data[key]) data[key] = []; -data[key].push(value); -``` - -#### Pattern: First-Wins / Last-Wins - -```typescript -// ❌ Violation: First-wins semantics +// ❌ First-wins / last-wins if (!data[key]) { data[key] = value; } -if (data[key] === undefined) { - data[key] = value; -} - -// ❌ Violation: Last-wins (implicit in direct assignment) -data[key] = value; // Always overwrites ``` -### Correct Patterns - -```typescript -// ✅ Correct: Detect and report duplicates -const seen = new Set(); - -for (const [key, value] of formData.entries()) { - if (seen.has(key)) { - issues.push({ - code: "duplicate_key", - key, - }); - continue; // Do not process - } - seen.add(key); - data[key] = value; -} -``` +See correct implementation: `src/parse.ts` --- @@ -152,96 +71,23 @@ for (const [key, value] of formData.entries()) { ### Antipatterns to Detect -#### Pattern: Structural Inference - ```typescript -// ❌ Violation: Creating nested objects +// ❌ Structural inference const parts = key.split("."); let current = data; for (const part of parts) { - current[part] = current[part] || {}; - current = current[part]; + current = current[part] = current[part] || {}; } -// ❌ Violation: Array creation based on key pattern -if (key.endsWith("[]")) { - data[key] = []; -} -``` - -#### Pattern: Type Coercion - -```typescript -// ❌ Violation: Converting to number +// ❌ Type coercion const numValue = Number(value); -const numValue = parseInt(value); -const numValue = +value; - -// ❌ Violation: Converting to boolean const boolValue = value === "true"; -const boolValue = Boolean(value); -const boolValue = !!value; - -// ❌ Violation: Date parsing -const dateValue = new Date(value); -const dateValue = Date.parse(value); -``` - -#### Pattern: Value Validation - -```typescript -// ❌ Violation: Validating email format -if (!isValidEmail(value)) { - throw new Error("Invalid email"); -} - -// ❌ Violation: Validating required fields -if (!value || value.length === 0) { - throw new Error("Field is required"); -} - -// ❌ Violation: Schema validation -const schema = z.object({ - /* ... */ -}); -schema.parse(data); -``` - -#### Pattern: Configuration Options - -```typescript -// ❌ Violation: Adding options parameter -function parse( - formData: FormData, - options?: { - allowDuplicates?: boolean; - convertTypes?: boolean; - strict?: boolean; - }, -): ParseResult { - /* ... */ -} -// ❌ Violation: Using options internally -if (options?.allowDuplicates) { - // Different behavior based on options -} +// ❌ Configuration options +function parse(formData: FormData, options?: { allowDuplicates?: boolean }): ParseResult {} ``` -### Correct Patterns - -```typescript -// ✅ Correct: No inference, store values as-is -const data = Object.create(null); -for (const [key, value] of formData.entries()) { - data[key] = value; // Store string or File as-is -} - -// ✅ Correct: No options, single signature -export function parse(formData: FormData): ParseResult { - // Implementation -} -``` +See correct implementation: `src/parse.ts` --- @@ -249,71 +95,15 @@ export function parse(formData: FormData): ParseResult { ### Antipatterns to Detect -#### Pattern: Throwing Exceptions - ```typescript -// ❌ Violation: throw for input-derived errors -if (invalidKey) { - throw new Error("Invalid key"); -} - -if (forbiddenKey) { - throw new SecurityError("Forbidden key"); -} +// ❌ Throwing +if (invalidKey) { throw new Error("Invalid key"); } -if (duplicateKey) { - throw new DuplicateKeyError(); -} - -// ❌ Violation: throw inside loop -for (const [key, value] of formData.entries()) { - if (someCondition) { - throw new Error(); // Forces try-catch on caller - } -} -``` - -#### Pattern: Partial Success - -```typescript -// ❌ Violation: Returning partial data with issues -return { - data: partialData, // Some keys processed - issues: [...] // Some keys failed -}; - -// ❌ Violation: Allowing issues.length > 0 with non-null data -if (issues.length > 0) { - return { data, issues }; // WRONG: data should be null -} +// ❌ Partial success +return { data: partialData, issues: [...] }; ``` -### Correct Patterns - -```typescript -// ✅ Correct: Collect issues, return structured result -const issues: ParseIssue[] = []; -const data = Object.create(null); - -for (const [key, value] of formData.entries()) { - if (invalidCondition) { - issues.push({ - code: "invalid_key", - key, - }); - continue; // Do not throw - } - - data[key] = value; -} - -// ✅ Correct: All-or-nothing based on issues -if (issues.length > 0) { - return { data: null, issues }; -} - -return { data, issues: [] }; -``` +See correct implementation: `src/parse.ts` --- @@ -321,53 +111,19 @@ return { data, issues: [] }; ### Antipatterns to Detect -#### Pattern: Unsafe Object Creation - ```typescript -// ❌ Violation: Using plain object literal +// ❌ Unsafe object creation const data = {}; const data = new Object(); -// ❌ Violation: Not checking for forbidden keys before assignment +// ❌ Missing forbidden key check for (const [key, value] of formData.entries()) { - data[key] = value; // Dangerous if key === '__proto__' -} -``` - -#### Pattern: Missing Forbidden Key Check - -```typescript -// ❌ Violation: No forbidden key validation -for (const [key, value] of formData.entries()) { - if (typeof key !== "string" || key.length === 0) { - // Invalid key check - } - // Missing: forbidden key check for __proto__, constructor, prototype + // No __proto__ / constructor / prototype check data[key] = value; } ``` -### Correct Patterns - -```typescript -// ✅ Correct: Prototype-less object -const data = Object.create(null); - -// ✅ Correct: Check for forbidden keys -const FORBIDDEN_KEYS = ["__proto__", "constructor", "prototype"]; - -for (const [key, value] of formData.entries()) { - if (FORBIDDEN_KEYS.includes(key as any)) { - issues.push({ - code: "forbidden_key", - key, - }); - continue; - } - - data[key] = value; -} -``` +See correct implementation: `src/parse.ts` --- @@ -375,67 +131,49 @@ for (const [key, value] of formData.entries()) { ### Antipatterns to Detect -#### Pattern: Function Overloads - ```typescript -// ❌ Violation: Adding overloads +// ❌ Function overloads export function parse(formData: FormData): ParseResult; export function parse(formData: FormData, strict: boolean): ParseResult; -``` - -#### Pattern: Changing IssueCode -```typescript -// ❌ Violation: Adding new issue codes without major version bump -export type IssueCode = "invalid_key" | "forbidden_key" | "duplicate_key" | "invalid_value"; // NEW CODE - requires major version! +// ❌ New IssueCode without major version bump +export type IssueCode = "invalid_key" | "forbidden_key" | "duplicate_key" | "invalid_value"; ``` -### Correct Patterns - -```typescript -// ✅ Correct: Single function signature -export function parse(formData: FormData): ParseResult { - // Implementation -} - -// ✅ Correct: Stable IssueCode (v0.x) -export type IssueCode = "invalid_key" | "forbidden_key" | "duplicate_key"; -``` +See correct types: `src/types/` --- ## Grep Commands for Detection -Use these commands to search for violations: - ```bash -# Search for bracket notation parsing +# Bracket notation parsing grep -r "endsWith\('\[\]'\)" src/ grep -r "includes\('\[\]'\)" src/ grep -r "match.*\\\[" src/ -# Search for dot notation parsing +# Dot notation parsing grep -r "split\('\.'\)" src/ grep -r "includes\('\.'\)" src/ -# Search for Object.assign or spread +# Object.assign or spread grep -r "Object\.assign" src/ grep -r "\.\.\." src/ -# Search for throw statements +# Throw statements grep -r "throw new" src/ grep -r "throw " src/ -# Search for type coercion +# Type coercion grep -r "Number(" src/ grep -r "Boolean(" src/ grep -r "parseInt" src/ -# Search for plain object creation +# Unsafe object creation grep -r "= {}" src/ grep -r "new Object()" src/ -# Search for configuration options +# Configuration options grep -r "options\?" src/ grep -r "ParseOptions" src/ ```