/** Accepted input types. File paths are intentionally not supported. */ type Input = File | Blob | ArrayBuffer | Uint8Array; type Format = 'xlsx' | 'xls' | 'csv'; type Severity = 'error' | 'warning'; type IssueCode = 'PASSWORD_PROTECTED' | 'ENCRYPTED_DEFAULT_PASSWORD' | 'CORRUPTED' | 'UNSUPPORTED_FORMAT' | 'FILE_TOO_LARGE' | 'EMPTY_FILE' | 'FORMAT_NOT_ALLOWED' | 'SHEET_NOT_FOUND' | 'COLUMN_COUNT' | 'HEADER_MISMATCH' | 'HEADER_ORDER' | 'ROWS_TOO_FEW' | 'ROWS_TOO_MANY' | 'CELL_ERRORS' | 'PERSONAL_DATA' | 'HAS_MACROS' | 'HIDDEN_CONTENT' | 'MERGED_HEADER' | 'EXTENSION_MISMATCH' | 'RAGGED_ROWS' | 'DUPLICATE_HEADER' | 'TEMPLATE_LOAD_FAILED' | 'CELL_TYPE' | 'CELL_DATE' | 'KR_BRN' | 'KR_PHONE' | 'KR_ZIP' | 'KR_AMOUNT' | 'KR_RRN' | 'RRN_CHECKSUM' | 'KR_CRN' | 'CRN_CHECKSUM' | 'LEADING_ZERO_LOSS' | 'CELL_REQUIRED' | 'CELL_ENUM' | 'CELL_PATTERN' | 'CELL_RANGE' | 'CELL_LENGTH' | 'CELL_UNIQUE' | 'FORMULA_INJECTION' | 'PII_FORBIDDEN' | 'ROW_REQUIRE' | 'ROW_FORBID' | 'ROW_COMPARE' | 'ROW_UNIQUE_WITH' | 'BLANK_ROW' | 'TOTALS_ROW' | 'PRECISION_LOSS' | 'SCIENTIFIC_NOTATION' | 'COLUMN_MISSING' | 'COLUMN_EXTRA' | 'COLUMN_DUPLICATE'; interface Issue { code: IssueCode; severity: Severity; message: string; [k: string]: unknown; } /** A cell- or row-level issue from `validate()` (spec §5 A). */ interface CellIssue extends Issue { /** Row number as Excel shows it (1-based, header included). */ row: number; /** Column letter; `validate()` fills it only from the `columnLetters` option. */ col?: string; /** Rule column key. */ key?: string; /** The column's header text from the rules, when the issue has a key (Ruling V39). */ header?: string; /** Which rule fired: a rules JSON path such as `enum`, `type`, `rrn.checksum`, `rowRules[0]`, `rows.blank`. */ rule: string; /** Original value (masked when it is personal data). */ raw?: unknown; /** Cleaned value (masked when it is personal data). */ value?: unknown; } /** Issues grouped by column and code (spec §5 E). */ interface SummaryItem { key?: string; col?: string; header?: string; code: IssueCode; severity: Severity; count: number; /** Up to 3, in row order. */ examples: { row: number; raw: unknown; }[]; } interface ValidateResult { ok: boolean; errors: CellIssue[]; warnings: CellIssue[]; summary: SummaryItem[]; /** Cleaned rows keyed by column key, with `$row`. Never masked. */ rows: Record[]; } interface FileInfo { name?: string; size: number; format?: Format; extension?: string; encrypted: boolean; hasMacros: boolean; } interface FileMeta { title?: string; author?: string; lastModifiedBy?: string; created?: string; modified?: string; application?: string; } interface CsvInfo { encoding: string; delimiter: string; } type Delimiter = ',' | ';' | '\t'; type ColumnType = 'text' | 'number' | 'boolean' | 'empty' | 'mixed'; interface SheetInfo { name: string; index: number; hidden: boolean; protected: boolean; /** e.g. 'A1:F120' */ range: string; columnCount: number; rowCount: number; columns: string[]; columnTypes: ColumnType[]; emptyCells: number; errorCells: { cell: string; value: string; }[]; mergedCells: string[]; hiddenRows: number[]; hiddenColumns: string[]; formulaCount: number; personalData?: { type: 'rrn'; count: number; cells: string[]; }; preview: string[][]; } interface InspectResult { file: FileInfo; meta?: FileMeta; csv?: CsvInfo; sheets: SheetInfo[]; definedNames: string[]; issues: Issue[]; } interface CheckRules { /** Exact match in order, compared after NFC normalization. */ headers?: string[]; columns?: number; rows?: { min?: number; max?: number; }; sheet?: string | number; headerRow?: number; maxSize?: number; accept?: Format[]; locale?: string; /** v2 rules: message templates by issue code, used for every v2 issue (a column's `message` wins). */ messages?: Partial>; readRows?: boolean; /** With readRows: cap on data rows × column width. Default 2,000,000. */ maxCells?: number; /** CSV delimiter; detected when omitted. */ delimiter?: Delimiter; /** Cap on the total decompressed bytes of an xlsx. Default 200 MB. */ maxUncompressedSize?: number; /** Data rows kept in `info.sheets[0].preview`. Default 5. */ previewRows?: number; /** Warning codes to report as errors. */ failOn?: IssueCode[]; /** Set false to opt out of anonymous usage counting. */ telemetry?: boolean; } interface CheckResult { ok: boolean; errors: Issue[]; warnings: Issue[]; info: InspectResult; /** Row objects as readRows() returns them; only when rules.readRows is true. */ rows?: Record[]; /** v2 rules only: issues grouped by column and code. */ summary?: SummaryItem[]; /** v2 rules only: the report for a server re-check (spec §5 G). */ report?: CheckReport; /** v2 rules only: which file column each rule column was found in. */ columns?: HeaderColumn[]; } /** A rule column found in the file: its key, the header text as written, and the column letter. */ interface HeaderColumn { key: string; source: string; col: string; } /** `result.report` (spec §5 G, research §6). A hint for the server, never proof. */ interface CheckReport { schemaId?: string; schemaVersion?: string; /** `sha256:` of the rules' canonical JSON. */ schemaHash: string; /** `82excel@` */ lib: string; file: { name?: string; size: number; sha256: string; sheet: string; format: Format; }; header: { row: number; columns: HeaderColumn[]; }; stats: { rows: number; errors: number; warnings: number; }; /** The result's errors then warnings; personal data masked. */ issues: Issue[]; /** ISO 8601 time of the check. */ checkedAt: string; } /** Column value types (spec §4). */ type ColumnValueType = 'text' | 'number' | 'integer' | 'date' | 'boolean' | 'email' | 'kr.phone' | 'kr.brn' | 'kr.crn' | 'kr.rrn' | 'kr.zip' | 'kr.amount'; type PhoneKind = 'mobile' | 'landline' | 'any'; type PiiKind = 'rrn' | 'card' | 'phone' | 'email'; type CompareOp = '<' | '<=' | '=' | '!=' | '>=' | '>'; /** One column rule as written in JSON. Only `key` is required. */ interface ColumnRule { /** Row-object key. Must not start with `$` (reserved, e.g. `$row`). */ key: string; /** Header text to match; defaults to `key`. */ header?: string; aliases?: string[]; /** The column must exist in the file. Default true. */ required?: boolean; /** Empty cells allowed. Default true. */ nullable?: boolean; /** Default 'text'. */ type?: ColumnValueType; unique?: boolean; enum?: (string | number)[]; /** Regular expression source, compiled with the `u` flag. Applied to the normalized value. */ pattern?: string; min?: number; max?: number; minLength?: number; /** Default 32,767 (Excel's cell maximum); longer values skip the pattern test. */ maxLength?: number; /** Tokens: YYYY YY MM M DD D (M/D = 1–2 digits, MM/DD = exactly 2); literals `. - / space 년 월 일`. */ date?: { formats?: string[]; pivot?: number; }; phone?: { kind?: PhoneKind; }; boolean?: { true?: string[]; false?: string[]; }; rrn?: { checksum?: 'warn' | 'off'; }; crn?: { checksum?: 'warn' | 'off'; }; /** Severity of this column's rule violations. Default 'error'. */ severity?: Severity; /** allow: no PII detection on this column. forbid: detection is an error. Unset: detection warns. */ pii?: 'allow' | 'forbid'; message?: string; } interface RowRuleWhen { col: string; eq?: string | number | boolean; neq?: string | number | boolean; in?: (string | number | boolean)[]; empty?: true; notEmpty?: true; } interface RowRuleBase { severity?: Severity; message?: string; } interface RowRuleWhenThen extends RowRuleBase { when: RowRuleWhen; require?: string[]; forbid?: string[]; } interface RowRuleCompare extends RowRuleBase { compare: [string, CompareOp, string]; } interface RowRuleUniqueWith extends RowRuleBase { uniqueWith: string[]; } type RowRule = RowRuleWhenThen | RowRuleCompare | RowRuleUniqueWith; type BlankPolicy = 'skip' | 'warn' | 'error' | 'stop'; type TotalsPolicy = 'warn' | 'ignore' | 'error'; type FormulaInjectionPolicy = 'warn' | 'error' | 'escape' | 'off'; /** 0.2 rules as written in JSON (spec §4). `columns` being an array is what makes them v2. */ interface RulesV2 { $schema?: string; id?: string; version?: string; columns: ColumnRule[]; columnsOrder?: 'any' | 'strict'; extraColumns?: 'ignore' | 'warn' | 'error'; rows?: { min?: number; max?: number; blank?: BlankPolicy; totals?: TotalsPolicy; }; rowRules?: RowRule[]; formulaInjection?: FormulaInjectionPolicy; pii?: { detect?: PiiKind[]; mask?: boolean; }; normalize?: { trim?: boolean; invisible?: boolean; fullwidthDigits?: boolean; }; sheet?: string | number; headerRow?: number; maxSize?: number; accept?: Format[]; locale?: string; /** Global message templates by issue code (a column's `message` wins over these). */ messages?: Partial>; readRows?: boolean; maxCells?: number; delimiter?: Delimiter; maxUncompressedSize?: number; previewRows?: number; failOn?: IssueCode[]; telemetry?: boolean; } /** A column rule after defaults are applied. Type-specific options are filled only for their type. */ interface NormalizedColumn { readonly key: string; readonly header: string; readonly aliases: readonly string[]; readonly required: boolean; readonly nullable: boolean; readonly type: ColumnValueType; readonly unique: boolean; readonly enum?: readonly (string | number)[]; readonly pattern?: string; readonly min?: number; readonly max?: number; readonly minLength?: number; readonly maxLength: number; readonly date?: { readonly formats: readonly string[]; readonly pivot: number; }; readonly phone?: { readonly kind: PhoneKind; }; readonly boolean?: { readonly true: readonly string[]; readonly false: readonly string[]; }; readonly rrn?: { readonly checksum: 'warn' | 'off'; }; readonly crn?: { readonly checksum: 'warn' | 'off'; }; readonly severity: Severity; readonly pii?: 'allow' | 'forbid'; readonly message?: string; } /** The deeply frozen result of `defineRules()`. */ interface Rules extends Readonly> { readonly columns: readonly NormalizedColumn[]; readonly columnsOrder: 'any' | 'strict'; readonly extraColumns: 'ignore' | 'warn' | 'error'; readonly rows: { readonly min?: number; readonly max?: number; readonly blank: BlankPolicy; readonly totals: TotalsPolicy; }; readonly rowRules: readonly RowRule[]; readonly formulaInjection: FormulaInjectionPolicy; readonly pii: { readonly detect: readonly PiiKind[]; readonly mask: boolean; }; readonly normalize: { readonly trim: boolean; readonly invisible: boolean; readonly fullwidthDigits: boolean; }; /** Standard Schema v1 (https://standard-schema.dev): validates one row object, synchronously. Not enumerable. */ readonly '~standard': { readonly version: 1; readonly vendor: '82excel'; readonly validate: (value: unknown) => StandardResult; }; } /** Standard Schema result: `{ value }` on success, `{ issues }` on failure. */ type StandardResult = { readonly value: Record; readonly issues?: undefined; } | { readonly issues: readonly { readonly message: string; readonly path?: readonly string[]; }[]; }; /** Shared result shape of every value normalizer (Tasks 3–4). */ type NormResult = { ok: true; value: T; warning?: IssueCode; } | { ok: false; code: IssueCode; params?: Record; }; export type { BlankPolicy as B, CheckResult as C, Delimiter as D, FileInfo as F, HeaderColumn as H, Input as I, NormalizedColumn as N, PhoneKind as P, RulesV2 as R, Severity as S, ValidateResult as V, CheckRules as a, Rules as b, InspectResult as c, IssueCode as d, Issue as e, CellIssue as f, CheckReport as g, ColumnRule as h, ColumnType as i, ColumnValueType as j, CompareOp as k, CsvInfo as l, FileMeta as m, Format as n, FormulaInjectionPolicy as o, PiiKind as p, RowRule as q, SheetInfo as r, StandardResult as s, SummaryItem as t, NormResult as u };