@12-apps/forms-core 1.19.2 → 1.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
- package/src/br.ts +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -174,7 +174,7 @@ declare function formatCep(raw: string): string;
|
|
|
174
174
|
*/
|
|
175
175
|
declare function isValidCep(raw: string): boolean;
|
|
176
176
|
/** Empty ⇒ valid (optional field); otherwise the value must be a full CEP. */
|
|
177
|
-
declare const cep: (message
|
|
177
|
+
declare const cep: (message: string) => Validator;
|
|
178
178
|
/** Canonical CNPJ shape after masking: `12.ABC.345/01DE-35`. */
|
|
179
179
|
declare const CNPJ_PATTERN: RegExp;
|
|
180
180
|
/**
|
|
@@ -199,7 +199,7 @@ declare function formatCnpj(raw: string): string;
|
|
|
199
199
|
*/
|
|
200
200
|
declare function isValidCnpj(raw: string): boolean;
|
|
201
201
|
/** Empty ⇒ valid (optional field); otherwise the check digits must match. */
|
|
202
|
-
declare const cnpj: (message
|
|
202
|
+
declare const cnpj: (message: string) => Validator;
|
|
203
203
|
/**
|
|
204
204
|
* Format a Brazilian phone as `(11) 3214-5600` / `(11) 91234-5678`.
|
|
205
205
|
*
|
package/dist/index.js
CHANGED
|
@@ -132,7 +132,7 @@ function isValidCep(raw) {
|
|
|
132
132
|
return CEP_PATTERN.test(formatCep(raw));
|
|
133
133
|
}
|
|
134
134
|
__name(isValidCep, "isValidCep");
|
|
135
|
-
var cep = /* @__PURE__ */ __name((message
|
|
135
|
+
var cep = /* @__PURE__ */ __name((message) => (value) => value.trim() === "" || isValidCep(value) ? void 0 : message, "cep");
|
|
136
136
|
var CNPJ_PATTERN = /^[0-9A-Z]{2}\.[0-9A-Z]{3}\.[0-9A-Z]{3}\/[0-9A-Z]{4}-\d{2}$/;
|
|
137
137
|
var CNPJ_LENGTH = 14;
|
|
138
138
|
function cnpjChars(raw) {
|
|
@@ -180,7 +180,7 @@ function isValidCnpj(raw) {
|
|
|
180
180
|
return cnpjCheckDigit(`${base}${first}`) === Number(chars[13]);
|
|
181
181
|
}
|
|
182
182
|
__name(isValidCnpj, "isValidCnpj");
|
|
183
|
-
var cnpj = /* @__PURE__ */ __name((message
|
|
183
|
+
var cnpj = /* @__PURE__ */ __name((message) => (value) => value.trim() === "" || isValidCnpj(value) ? void 0 : message, "cnpj");
|
|
184
184
|
function formatBrPhone(raw) {
|
|
185
185
|
const trimmed = raw?.trim();
|
|
186
186
|
if (!trimmed) return null;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/validators.ts","../src/result.ts","../src/serverAction.ts","../src/br.ts"],"sourcesContent":["/**\n * Field-level form validators\n *\n * Pure, framework-agnostic validators — NO zod, NO react-hook-form. Each\n * validator is curried and returns a `string` error message for invalid input\n * or `undefined` when the value is valid.\n *\n * @module forms-core/validators\n */\n\n/** A field validator: returns an error message, or `undefined` when valid. */\nexport type Validator = (value: string) => string | undefined;\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/**\n * Requires a non-empty value (after trimming whitespace).\n *\n * @param message - Custom error message for empty input.\n * @returns A validator returning the message for empty input, else `undefined`.\n */\nexport const required =\n (message = 'This field is required'): Validator =>\n (value) =>\n value.trim().length === 0 ? message : undefined;\n\n/**\n * Validates that the value is a well-formed email address.\n *\n * @param message - Custom error message for malformed input.\n * @returns A validator returning the message for invalid input, else `undefined`.\n */\nexport const email =\n (message = 'Enter a valid email address'): Validator =>\n (value) =>\n EMAIL_PATTERN.test(value) ? undefined : message;\n\n/**\n * Validates that the value has at least `length` characters.\n *\n * @param length - The minimum number of characters required.\n * @param message - Custom error message for too-short input.\n * @returns A validator returning the message for short input, else `undefined`.\n */\nexport const minLength =\n (length: number, message = `Must be at least ${length} characters`): Validator =>\n (value) =>\n value.length < length ? message : undefined;\n\n/** Alias of {@link minLength}. */\nexport const min = minLength;\n\n/** A map of field names to the list of validators applied to that field. */\nexport type ValidatorSchema<T> = {\n [K in keyof T]?: Validator[];\n};\n\n/** A map of field names to the first error message produced for that field. */\nexport type FieldErrors<T> = {\n [K in keyof T]?: string;\n};\n\n/**\n * The first error message a field's validators produce, or `undefined` when\n * every validator passes. Short-circuits on the first failure.\n */\nconst firstFieldError = (validators: Validator[], value: string): string | undefined => {\n for (const validate of validators) {\n const error = validate(value);\n if (error !== undefined) {\n return error;\n }\n }\n return undefined;\n};\n\n/**\n * Runs each field's validators against `values`, short-circuiting on the first\n * failing validator per field.\n *\n * @param values - The field values to validate (keyed by field name).\n * @param schema - Validators to apply per field.\n * @returns A record containing one entry per failing field; `{}` when all valid.\n */\nexport const validateFields = <T extends Record<string, string>>(\n values: T,\n schema: ValidatorSchema<T>,\n): FieldErrors<T> => {\n const errors: FieldErrors<T> = {};\n\n for (const field of Object.keys(schema) as (keyof T)[]) {\n const validators = schema[field];\n if (!validators) {\n continue;\n }\n const error = firstFieldError(validators, values[field] ?? '');\n if (error !== undefined) {\n errors[field] = error;\n }\n }\n\n return errors;\n};\n","/**\n * Discriminated-union result type for operations that can fail.\n *\n * Pure and framework-agnostic. The `ok` discriminant narrows the union so\n * consumers can safely read `data` on success or `error` on failure.\n *\n * @typeParam T - The success payload type.\n * @module forms-core/result\n */\nexport type Result<T> =\n | { ok: true; data: T }\n | {\n ok: false;\n error: string;\n /**\n * Per-field error messages, keyed by form field name, when the failure is\n * attributable to specific input(s). Lets a form highlight the exact\n * offending field(s) instead of only showing the summary `error`.\n */\n fieldErrors?: Record<string, string>;\n };\n\n/**\n * Builds a successful {@link Result} wrapping `data`.\n *\n * @typeParam T - The success payload type.\n * @param data - The value to wrap.\n * @returns `{ ok: true, data }`.\n */\nexport const ok = <T>(data: T): Result<T> => ({ ok: true, data });\n\n/**\n * Builds a failed {@link Result} wrapping an error `message`.\n *\n * @param error - A user-safe error message (never an internal/stack detail).\n * @param fieldErrors - Optional per-field messages so a form can highlight the\n * exact offending field(s). Omitted when the failure isn't field-specific.\n * @returns `{ ok: false, error, fieldErrors? }`.\n */\nexport const err = (\n error: string,\n fieldErrors?: Record<string, string>,\n): Result<never> =>\n fieldErrors ? { ok: false, error, fieldErrors } : { ok: false, error };\n","import { err, ok, type Result } from './result';\nimport { validateFields, type ValidatorSchema } from './validators';\n\n/** Generic, user-safe message returned when a handler throws. */\nconst GENERIC_ERROR = 'Something went wrong. Please try again.';\n\n/**\n * An error whose `message` is safe to show to the end user.\n *\n * Handlers throw this to surface a *specific, actionable* failure (e.g. \"a\n * product with this name already exists\") through {@link createServerAction}\n * instead of collapsing into the {@link GENERIC_ERROR}. Any error that is NOT\n * an `ActionError` is treated as unexpected: it is logged server-side and the\n * caller receives the generic message so internal details never leak.\n */\nexport class ActionError extends Error {\n /**\n * The form field this failure is attributable to (e.g. `\"name\"` for a\n * duplicate-name conflict). When set, {@link createServerAction} surfaces it\n * as `fieldErrors` so the form can highlight that exact field.\n */\n readonly field?: string;\n\n /**\n * Per-field messages when a single failure attributes to MORE than one field\n * (e.g. several duplicate SKUs across a product's variation rows). Takes\n * precedence over {@link field}; when set, {@link createServerAction} passes\n * this map straight through as `fieldErrors`.\n */\n readonly fieldErrors?: Record<string, string>;\n\n constructor(message: string, field?: string, fieldErrors?: Record<string, string>) {\n super(message);\n this.name = 'ActionError';\n this.field = field;\n this.fieldErrors = fieldErrors;\n }\n}\n\n/**\n * Wraps a handler-agnostic server action with validate-first semantics.\n *\n * The returned action:\n * 1. Runs {@link validateFields} against `input` using `schema`. On the first\n * field error it returns `err(message)` **without invoking `handler`**.\n * 2. Otherwise invokes `handler(input)` and returns `ok` with its resolved\n * value.\n * 3. Passes through the message of an {@link ActionError} thrown by `handler`\n * (a deliberate, user-safe failure), and maps any *other* exception to a\n * generic `err` message — logging the real cause server-side so production\n * failures stay diagnosable without leaking internals to the client.\n *\n * @typeParam I - The validated input shape (string fields keyed by name).\n * @typeParam O - The handler's success payload type.\n * @param schema - Per-field validators applied before the handler runs.\n * @param handler - The business logic, run only when input is valid.\n * @returns An async action that resolves to a {@link Result}.\n */\nexport const createServerAction = <I extends Record<string, string>, O>(\n schema: ValidatorSchema<I>,\n handler: (input: I) => Promise<O> | O,\n): ((input: I) => Promise<Result<O>>) => {\n return async (input: I): Promise<Result<O>> => {\n const errors = validateFields(input, schema);\n // Surface the first failing field by name so the form highlights it.\n const firstField = (Object.keys(errors) as (keyof I)[]).find(\n (field) => typeof errors[field] === 'string',\n );\n if (firstField !== undefined) {\n const message = errors[firstField] as string;\n return err(message, { [String(firstField)]: message });\n }\n\n try {\n return ok(await handler(input));\n } catch (error) {\n if (error instanceof ActionError) {\n // A multi-field map (fieldErrors) wins over the single-field shorthand so\n // one failure can highlight several inputs (e.g. duplicate variation SKUs).\n const fieldErrors =\n error.fieldErrors ?? (error.field ? { [error.field]: error.message } : undefined);\n return err(error.message, fieldErrors);\n }\n // Unexpected: log the real cause server-side (never sent to the client)\n // so prod failures are diagnosable, and return a safe generic message.\n console.error('[serverAction] handler threw an unexpected error:', error);\n return err(GENERIC_ERROR);\n }\n };\n};\n","/**\n * Brazilian document + address primitives (FUT-306).\n *\n * Masks, shape checks and check-digit validation for the two fields every\n * pt-BR form asks for — CEP and CNPJ — plus the UF list. Pure and dependency\n * free like the rest of `forms-core`, so the SAME rules run in the browser\n * (input mask + inline validation), in a route handler (before hitting a\n * lookup provider) and in a test.\n *\n * @module forms-core/br\n */\n\nimport type { Validator } from './validators';\n\n/** Brazilian federative units, for UF selects and client-side validation. */\nexport const UF_LIST = [\n 'AC', 'AL', 'AP', 'AM', 'BA', 'CE', 'DF', 'ES', 'GO',\n 'MA', 'MT', 'MS', 'MG', 'PA', 'PB', 'PR', 'PE', 'PI',\n 'RJ', 'RN', 'RS', 'RO', 'RR', 'SC', 'SP', 'SE', 'TO',\n] as const;\n\n/** A Brazilian federative unit. */\nexport type Uf = (typeof UF_LIST)[number];\n\n/** True when `value` is one of the 27 {@link UF_LIST} entries (case-sensitive). */\nexport function isUf(value: string): value is Uf {\n return (UF_LIST as readonly string[]).includes(value);\n}\n\n// ---------------------------------------------------------------------------\n// CEP\n// ---------------------------------------------------------------------------\n\n/** Canonical CEP shape after masking/normalization: `01310-100`. */\nexport const CEP_PATTERN = /^\\d{5}-\\d{3}$/;\n\n/** The bare digits of a CEP, capped at the 8 a CEP has. */\nexport function cepDigits(raw: string): string {\n return raw.replace(/\\D/g, '').slice(0, 8);\n}\n\n/**\n * Mask raw CEP input to `#####-###`: strips non-digits, caps at 8 digits and\n * inserts the hyphen once the sixth digit arrives. Pure — safe to run on every\n * keystroke and to normalize before matching {@link CEP_PATTERN}.\n */\nexport function formatCep(raw: string): string {\n const digits = cepDigits(raw);\n return digits.length > 5 ? `${digits.slice(0, 5)}-${digits.slice(5)}` : digits;\n}\n\n/**\n * True when `raw` holds a complete, well-formed CEP.\n *\n * Shape only: a CEP is a postal RANGE allocated by the Correios, so there is no\n * check digit to verify. Whether it actually EXISTS is answered by a lookup,\n * not by this function.\n */\nexport function isValidCep(raw: string): boolean {\n return CEP_PATTERN.test(formatCep(raw));\n}\n\n/** Empty ⇒ valid (optional field); otherwise the value must be a full CEP. */\nexport const cep =\n (message = 'CEP inválido — use o formato 00000-000.'): Validator =>\n (value) =>\n value.trim() === '' || isValidCep(value) ? undefined : message;\n\n// ---------------------------------------------------------------------------\n// CNPJ\n// ---------------------------------------------------------------------------\n\n/** Canonical CNPJ shape after masking: `12.ABC.345/01DE-35`. */\nexport const CNPJ_PATTERN = /^[0-9A-Z]{2}\\.[0-9A-Z]{3}\\.[0-9A-Z]{3}\\/[0-9A-Z]{4}-\\d{2}$/;\n\n/** How many characters a CNPJ has, check digits included. */\nconst CNPJ_LENGTH = 14;\n\n/**\n * Strip a CNPJ to its 14 significant characters, uppercased.\n *\n * Alphanumeric-aware (Receita Federal IN 2.229/2024, live in production since\n * 2026-07-06): the first 12 positions may be letters OR digits, so only\n * punctuation is removed — never letters.\n */\nexport function cnpjChars(raw: string): string {\n return raw.toUpperCase().replace(/[^0-9A-Z]/g, '').slice(0, CNPJ_LENGTH);\n}\n\n/**\n * Mask raw CNPJ input to `##.###.###/####-##`, accepting letters in the first\n * twelve positions and digits in the two check positions.\n */\nexport function formatCnpj(raw: string): string {\n const chars = cnpjChars(raw);\n const parts = [\n chars.slice(0, 2),\n chars.slice(2, 5),\n chars.slice(5, 8),\n chars.slice(8, 12),\n // The check digits are always numeric, even on an alphanumeric CNPJ.\n chars.slice(12, 14).replace(/\\D/g, ''),\n ];\n let masked = parts[0] as string;\n if (chars.length > 2) masked += `.${parts[1]}`;\n if (chars.length > 5) masked += `.${parts[2]}`;\n if (chars.length > 8) masked += `/${parts[3]}`;\n if (chars.length > 12) masked += `-${parts[4]}`;\n return masked;\n}\n\n/**\n * The mod-11 weights, right-to-left cycling 2…9. `slice(-n)` takes the tail so\n * the same table serves the 12-char first pass and the 13-char second.\n */\nconst CNPJ_WEIGHTS = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] as const;\n\n/**\n * A character's numeric contribution: its ASCII code minus 48. Digits `0`–`9`\n * map to 0–9 (identical to the legacy rule, so existing CNPJs keep validating)\n * and letters `A`–`Z` map to 17–42.\n */\nfunction cnpjValue(char: string): number {\n return char.charCodeAt(0) - 48;\n}\n\n/** One mod-11 check digit over the first `base.length` characters. */\nfunction cnpjCheckDigit(base: string): number {\n const weights = CNPJ_WEIGHTS.slice(-base.length);\n const sum = [...base].reduce((acc, char, i) => acc + cnpjValue(char) * (weights[i] as number), 0);\n const remainder = sum % 11;\n return remainder < 2 ? 0 : 11 - remainder;\n}\n\n/**\n * Validate a CNPJ's check digits (mod 11), alphanumeric-aware.\n *\n * Rejects the repeated-character sequences (`00000000000000`, `AAAAAAAAAAAA35`)\n * that satisfy the arithmetic but are never issued. Returns false for anything\n * that is not exactly 14 characters with numeric check digits.\n */\nexport function isValidCnpj(raw: string): boolean {\n const chars = cnpjChars(raw);\n if (chars.length !== CNPJ_LENGTH) return false;\n // Only the 12-character root/establishment may be alphanumeric; the two check\n // digits stay numeric.\n if (!/^\\d{2}$/.test(chars.slice(12))) return false;\n if (new Set(chars).size === 1) return false;\n\n const base = chars.slice(0, 12);\n const first = cnpjCheckDigit(base);\n if (first !== Number(chars[12])) return false;\n return cnpjCheckDigit(`${base}${first}`) === Number(chars[13]);\n}\n\n/** Empty ⇒ valid (optional field); otherwise the check digits must match. */\nexport const cnpj =\n (message = 'CNPJ inválido — confira os dígitos.'): Validator =>\n (value) =>\n value.trim() === '' || isValidCnpj(value) ? undefined : message;\n\n// ---------------------------------------------------------------------------\n// Telefone\n// ---------------------------------------------------------------------------\n\n/**\n * Format a Brazilian phone as `(11) 3214-5600` / `(11) 91234-5678`.\n *\n * Both sources that hand us a phone give it as bare concatenated digits — the\n * Receita Federal's `ddd_telefone_1` and an NF-e's `<fone>` — so the same\n * formatting rule serves the CNPJ lookup and the nota importer.\n *\n * Anything that is not a 10- or 11-digit national number is returned trimmed\n * but otherwise untouched: an unrecognized phone (a foreign issuer, an extension\n * appended by the emitter's software) is still better dropped into the field\n * than discarded.\n *\n * @param raw - Digits as the source supplies them, e.g. `1132145600`.\n * @returns The formatted phone, or `null` when there is nothing to format.\n */\nexport function formatBrPhone(raw: string | null | undefined): string | null {\n const trimmed = raw?.trim();\n if (!trimmed) return null;\n const digits = trimmed.replace(/\\D/g, '');\n if (digits.length !== 10 && digits.length !== 11) return trimmed;\n const ddd = digits.slice(0, 2);\n const subscriber = digits.slice(2);\n const split = subscriber.length - 4;\n return `(${ddd}) ${subscriber.slice(0, split)}-${subscriber.slice(split)}`;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/validators.ts","../src/result.ts","../src/serverAction.ts","../src/br.ts"],"sourcesContent":["/**\n * Field-level form validators\n *\n * Pure, framework-agnostic validators — NO zod, NO react-hook-form. Each\n * validator is curried and returns a `string` error message for invalid input\n * or `undefined` when the value is valid.\n *\n * @module forms-core/validators\n */\n\n/** A field validator: returns an error message, or `undefined` when valid. */\nexport type Validator = (value: string) => string | undefined;\n\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\n/**\n * Requires a non-empty value (after trimming whitespace).\n *\n * @param message - Custom error message for empty input.\n * @returns A validator returning the message for empty input, else `undefined`.\n */\nexport const required =\n (message = 'This field is required'): Validator =>\n (value) =>\n value.trim().length === 0 ? message : undefined;\n\n/**\n * Validates that the value is a well-formed email address.\n *\n * @param message - Custom error message for malformed input.\n * @returns A validator returning the message for invalid input, else `undefined`.\n */\nexport const email =\n (message = 'Enter a valid email address'): Validator =>\n (value) =>\n EMAIL_PATTERN.test(value) ? undefined : message;\n\n/**\n * Validates that the value has at least `length` characters.\n *\n * @param length - The minimum number of characters required.\n * @param message - Custom error message for too-short input.\n * @returns A validator returning the message for short input, else `undefined`.\n */\nexport const minLength =\n (length: number, message = `Must be at least ${length} characters`): Validator =>\n (value) =>\n value.length < length ? message : undefined;\n\n/** Alias of {@link minLength}. */\nexport const min = minLength;\n\n/** A map of field names to the list of validators applied to that field. */\nexport type ValidatorSchema<T> = {\n [K in keyof T]?: Validator[];\n};\n\n/** A map of field names to the first error message produced for that field. */\nexport type FieldErrors<T> = {\n [K in keyof T]?: string;\n};\n\n/**\n * The first error message a field's validators produce, or `undefined` when\n * every validator passes. Short-circuits on the first failure.\n */\nconst firstFieldError = (validators: Validator[], value: string): string | undefined => {\n for (const validate of validators) {\n const error = validate(value);\n if (error !== undefined) {\n return error;\n }\n }\n return undefined;\n};\n\n/**\n * Runs each field's validators against `values`, short-circuiting on the first\n * failing validator per field.\n *\n * @param values - The field values to validate (keyed by field name).\n * @param schema - Validators to apply per field.\n * @returns A record containing one entry per failing field; `{}` when all valid.\n */\nexport const validateFields = <T extends Record<string, string>>(\n values: T,\n schema: ValidatorSchema<T>,\n): FieldErrors<T> => {\n const errors: FieldErrors<T> = {};\n\n for (const field of Object.keys(schema) as (keyof T)[]) {\n const validators = schema[field];\n if (!validators) {\n continue;\n }\n const error = firstFieldError(validators, values[field] ?? '');\n if (error !== undefined) {\n errors[field] = error;\n }\n }\n\n return errors;\n};\n","/**\n * Discriminated-union result type for operations that can fail.\n *\n * Pure and framework-agnostic. The `ok` discriminant narrows the union so\n * consumers can safely read `data` on success or `error` on failure.\n *\n * @typeParam T - The success payload type.\n * @module forms-core/result\n */\nexport type Result<T> =\n | { ok: true; data: T }\n | {\n ok: false;\n error: string;\n /**\n * Per-field error messages, keyed by form field name, when the failure is\n * attributable to specific input(s). Lets a form highlight the exact\n * offending field(s) instead of only showing the summary `error`.\n */\n fieldErrors?: Record<string, string>;\n };\n\n/**\n * Builds a successful {@link Result} wrapping `data`.\n *\n * @typeParam T - The success payload type.\n * @param data - The value to wrap.\n * @returns `{ ok: true, data }`.\n */\nexport const ok = <T>(data: T): Result<T> => ({ ok: true, data });\n\n/**\n * Builds a failed {@link Result} wrapping an error `message`.\n *\n * @param error - A user-safe error message (never an internal/stack detail).\n * @param fieldErrors - Optional per-field messages so a form can highlight the\n * exact offending field(s). Omitted when the failure isn't field-specific.\n * @returns `{ ok: false, error, fieldErrors? }`.\n */\nexport const err = (\n error: string,\n fieldErrors?: Record<string, string>,\n): Result<never> =>\n fieldErrors ? { ok: false, error, fieldErrors } : { ok: false, error };\n","import { err, ok, type Result } from './result';\nimport { validateFields, type ValidatorSchema } from './validators';\n\n/** Generic, user-safe message returned when a handler throws. */\nconst GENERIC_ERROR = 'Something went wrong. Please try again.';\n\n/**\n * An error whose `message` is safe to show to the end user.\n *\n * Handlers throw this to surface a *specific, actionable* failure (e.g. \"a\n * product with this name already exists\") through {@link createServerAction}\n * instead of collapsing into the {@link GENERIC_ERROR}. Any error that is NOT\n * an `ActionError` is treated as unexpected: it is logged server-side and the\n * caller receives the generic message so internal details never leak.\n */\nexport class ActionError extends Error {\n /**\n * The form field this failure is attributable to (e.g. `\"name\"` for a\n * duplicate-name conflict). When set, {@link createServerAction} surfaces it\n * as `fieldErrors` so the form can highlight that exact field.\n */\n readonly field?: string;\n\n /**\n * Per-field messages when a single failure attributes to MORE than one field\n * (e.g. several duplicate SKUs across a product's variation rows). Takes\n * precedence over {@link field}; when set, {@link createServerAction} passes\n * this map straight through as `fieldErrors`.\n */\n readonly fieldErrors?: Record<string, string>;\n\n constructor(message: string, field?: string, fieldErrors?: Record<string, string>) {\n super(message);\n this.name = 'ActionError';\n this.field = field;\n this.fieldErrors = fieldErrors;\n }\n}\n\n/**\n * Wraps a handler-agnostic server action with validate-first semantics.\n *\n * The returned action:\n * 1. Runs {@link validateFields} against `input` using `schema`. On the first\n * field error it returns `err(message)` **without invoking `handler`**.\n * 2. Otherwise invokes `handler(input)` and returns `ok` with its resolved\n * value.\n * 3. Passes through the message of an {@link ActionError} thrown by `handler`\n * (a deliberate, user-safe failure), and maps any *other* exception to a\n * generic `err` message — logging the real cause server-side so production\n * failures stay diagnosable without leaking internals to the client.\n *\n * @typeParam I - The validated input shape (string fields keyed by name).\n * @typeParam O - The handler's success payload type.\n * @param schema - Per-field validators applied before the handler runs.\n * @param handler - The business logic, run only when input is valid.\n * @returns An async action that resolves to a {@link Result}.\n */\nexport const createServerAction = <I extends Record<string, string>, O>(\n schema: ValidatorSchema<I>,\n handler: (input: I) => Promise<O> | O,\n): ((input: I) => Promise<Result<O>>) => {\n return async (input: I): Promise<Result<O>> => {\n const errors = validateFields(input, schema);\n // Surface the first failing field by name so the form highlights it.\n const firstField = (Object.keys(errors) as (keyof I)[]).find(\n (field) => typeof errors[field] === 'string',\n );\n if (firstField !== undefined) {\n const message = errors[firstField] as string;\n return err(message, { [String(firstField)]: message });\n }\n\n try {\n return ok(await handler(input));\n } catch (error) {\n if (error instanceof ActionError) {\n // A multi-field map (fieldErrors) wins over the single-field shorthand so\n // one failure can highlight several inputs (e.g. duplicate variation SKUs).\n const fieldErrors =\n error.fieldErrors ?? (error.field ? { [error.field]: error.message } : undefined);\n return err(error.message, fieldErrors);\n }\n // Unexpected: log the real cause server-side (never sent to the client)\n // so prod failures are diagnosable, and return a safe generic message.\n console.error('[serverAction] handler threw an unexpected error:', error);\n return err(GENERIC_ERROR);\n }\n };\n};\n","/**\n * Brazilian document + address primitives (FUT-306).\n *\n * Masks, shape checks and check-digit validation for the two fields every\n * pt-BR form asks for — CEP and CNPJ — plus the UF list. Pure and dependency\n * free like the rest of `forms-core`, so the SAME rules run in the browser\n * (input mask + inline validation), in a route handler (before hitting a\n * lookup provider) and in a test.\n *\n * @module forms-core/br\n */\n\nimport type { Validator } from './validators';\n\n/** Brazilian federative units, for UF selects and client-side validation. */\nexport const UF_LIST = [\n 'AC', 'AL', 'AP', 'AM', 'BA', 'CE', 'DF', 'ES', 'GO',\n 'MA', 'MT', 'MS', 'MG', 'PA', 'PB', 'PR', 'PE', 'PI',\n 'RJ', 'RN', 'RS', 'RO', 'RR', 'SC', 'SP', 'SE', 'TO',\n] as const;\n\n/** A Brazilian federative unit. */\nexport type Uf = (typeof UF_LIST)[number];\n\n/** True when `value` is one of the 27 {@link UF_LIST} entries (case-sensitive). */\nexport function isUf(value: string): value is Uf {\n return (UF_LIST as readonly string[]).includes(value);\n}\n\n// ---------------------------------------------------------------------------\n// CEP\n// ---------------------------------------------------------------------------\n\n/** Canonical CEP shape after masking/normalization: `01310-100`. */\nexport const CEP_PATTERN = /^\\d{5}-\\d{3}$/;\n\n/** The bare digits of a CEP, capped at the 8 a CEP has. */\nexport function cepDigits(raw: string): string {\n return raw.replace(/\\D/g, '').slice(0, 8);\n}\n\n/**\n * Mask raw CEP input to `#####-###`: strips non-digits, caps at 8 digits and\n * inserts the hyphen once the sixth digit arrives. Pure — safe to run on every\n * keystroke and to normalize before matching {@link CEP_PATTERN}.\n */\nexport function formatCep(raw: string): string {\n const digits = cepDigits(raw);\n return digits.length > 5 ? `${digits.slice(0, 5)}-${digits.slice(5)}` : digits;\n}\n\n/**\n * True when `raw` holds a complete, well-formed CEP.\n *\n * Shape only: a CEP is a postal RANGE allocated by the Correios, so there is no\n * check digit to verify. Whether it actually EXISTS is answered by a lookup,\n * not by this function.\n */\nexport function isValidCep(raw: string): boolean {\n return CEP_PATTERN.test(formatCep(raw));\n}\n\n/** Empty ⇒ valid (optional field); otherwise the value must be a full CEP. */\nexport const cep =\n (message: string): Validator =>\n (value) =>\n value.trim() === '' || isValidCep(value) ? undefined : message;\n\n// ---------------------------------------------------------------------------\n// CNPJ\n// ---------------------------------------------------------------------------\n\n/** Canonical CNPJ shape after masking: `12.ABC.345/01DE-35`. */\nexport const CNPJ_PATTERN = /^[0-9A-Z]{2}\\.[0-9A-Z]{3}\\.[0-9A-Z]{3}\\/[0-9A-Z]{4}-\\d{2}$/;\n\n/** How many characters a CNPJ has, check digits included. */\nconst CNPJ_LENGTH = 14;\n\n/**\n * Strip a CNPJ to its 14 significant characters, uppercased.\n *\n * Alphanumeric-aware (Receita Federal IN 2.229/2024, live in production since\n * 2026-07-06): the first 12 positions may be letters OR digits, so only\n * punctuation is removed — never letters.\n */\nexport function cnpjChars(raw: string): string {\n return raw.toUpperCase().replace(/[^0-9A-Z]/g, '').slice(0, CNPJ_LENGTH);\n}\n\n/**\n * Mask raw CNPJ input to `##.###.###/####-##`, accepting letters in the first\n * twelve positions and digits in the two check positions.\n */\nexport function formatCnpj(raw: string): string {\n const chars = cnpjChars(raw);\n const parts = [\n chars.slice(0, 2),\n chars.slice(2, 5),\n chars.slice(5, 8),\n chars.slice(8, 12),\n // The check digits are always numeric, even on an alphanumeric CNPJ.\n chars.slice(12, 14).replace(/\\D/g, ''),\n ];\n let masked = parts[0] as string;\n if (chars.length > 2) masked += `.${parts[1]}`;\n if (chars.length > 5) masked += `.${parts[2]}`;\n if (chars.length > 8) masked += `/${parts[3]}`;\n if (chars.length > 12) masked += `-${parts[4]}`;\n return masked;\n}\n\n/**\n * The mod-11 weights, right-to-left cycling 2…9. `slice(-n)` takes the tail so\n * the same table serves the 12-char first pass and the 13-char second.\n */\nconst CNPJ_WEIGHTS = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2] as const;\n\n/**\n * A character's numeric contribution: its ASCII code minus 48. Digits `0`–`9`\n * map to 0–9 (identical to the legacy rule, so existing CNPJs keep validating)\n * and letters `A`–`Z` map to 17–42.\n */\nfunction cnpjValue(char: string): number {\n return char.charCodeAt(0) - 48;\n}\n\n/** One mod-11 check digit over the first `base.length` characters. */\nfunction cnpjCheckDigit(base: string): number {\n const weights = CNPJ_WEIGHTS.slice(-base.length);\n const sum = [...base].reduce((acc, char, i) => acc + cnpjValue(char) * (weights[i] as number), 0);\n const remainder = sum % 11;\n return remainder < 2 ? 0 : 11 - remainder;\n}\n\n/**\n * Validate a CNPJ's check digits (mod 11), alphanumeric-aware.\n *\n * Rejects the repeated-character sequences (`00000000000000`, `AAAAAAAAAAAA35`)\n * that satisfy the arithmetic but are never issued. Returns false for anything\n * that is not exactly 14 characters with numeric check digits.\n */\nexport function isValidCnpj(raw: string): boolean {\n const chars = cnpjChars(raw);\n if (chars.length !== CNPJ_LENGTH) return false;\n // Only the 12-character root/establishment may be alphanumeric; the two check\n // digits stay numeric.\n if (!/^\\d{2}$/.test(chars.slice(12))) return false;\n if (new Set(chars).size === 1) return false;\n\n const base = chars.slice(0, 12);\n const first = cnpjCheckDigit(base);\n if (first !== Number(chars[12])) return false;\n return cnpjCheckDigit(`${base}${first}`) === Number(chars[13]);\n}\n\n/** Empty ⇒ valid (optional field); otherwise the check digits must match. */\nexport const cnpj =\n (message: string): Validator =>\n (value) =>\n value.trim() === '' || isValidCnpj(value) ? undefined : message;\n\n// ---------------------------------------------------------------------------\n// Telefone\n// ---------------------------------------------------------------------------\n\n/**\n * Format a Brazilian phone as `(11) 3214-5600` / `(11) 91234-5678`.\n *\n * Both sources that hand us a phone give it as bare concatenated digits — the\n * Receita Federal's `ddd_telefone_1` and an NF-e's `<fone>` — so the same\n * formatting rule serves the CNPJ lookup and the nota importer.\n *\n * Anything that is not a 10- or 11-digit national number is returned trimmed\n * but otherwise untouched: an unrecognized phone (a foreign issuer, an extension\n * appended by the emitter's software) is still better dropped into the field\n * than discarded.\n *\n * @param raw - Digits as the source supplies them, e.g. `1132145600`.\n * @returns The formatted phone, or `null` when there is nothing to format.\n */\nexport function formatBrPhone(raw: string | null | undefined): string | null {\n const trimmed = raw?.trim();\n if (!trimmed) return null;\n const digits = trimmed.replace(/\\D/g, '');\n if (digits.length !== 10 && digits.length !== 11) return trimmed;\n const ddd = digits.slice(0, 2);\n const subscriber = digits.slice(2);\n const split = subscriber.length - 4;\n return `(${ddd}) ${subscriber.slice(0, split)}-${subscriber.slice(split)}`;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@12-apps/forms-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.21.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
6
7
|
"description": "Pure, framework-agnostic form primitives — field validators, the Result type, and createServerAction. Zero dependencies, shipped compiled so a Node consumer can import it (Node cannot strip types under node_modules).",
|
|
7
8
|
"exports": {
|
|
8
9
|
".": {
|
|
@@ -19,7 +20,7 @@
|
|
|
19
20
|
"typecheck": "tsc --noEmit"
|
|
20
21
|
},
|
|
21
22
|
"devDependencies": {
|
|
22
|
-
"@12-apps/typescript-config": "^1.
|
|
23
|
+
"@12-apps/typescript-config": "^1.21.0",
|
|
23
24
|
"tsup": "^8.0.0",
|
|
24
25
|
"typescript": "^5.9.2",
|
|
25
26
|
"vitest": "^3.2.4"
|
package/src/br.ts
CHANGED
|
@@ -62,7 +62,7 @@ export function isValidCep(raw: string): boolean {
|
|
|
62
62
|
|
|
63
63
|
/** Empty ⇒ valid (optional field); otherwise the value must be a full CEP. */
|
|
64
64
|
export const cep =
|
|
65
|
-
(message
|
|
65
|
+
(message: string): Validator =>
|
|
66
66
|
(value) =>
|
|
67
67
|
value.trim() === '' || isValidCep(value) ? undefined : message;
|
|
68
68
|
|
|
@@ -155,7 +155,7 @@ export function isValidCnpj(raw: string): boolean {
|
|
|
155
155
|
|
|
156
156
|
/** Empty ⇒ valid (optional field); otherwise the check digits must match. */
|
|
157
157
|
export const cnpj =
|
|
158
|
-
(message
|
|
158
|
+
(message: string): Validator =>
|
|
159
159
|
(value) =>
|
|
160
160
|
value.trim() === '' || isValidCnpj(value) ? undefined : message;
|
|
161
161
|
|