@asteby/storefront-react 0.26.0 → 0.26.1

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.
@@ -138,7 +138,30 @@ function logMisconfigured(scopes) {
138
138
  `[@asteby/storefront-react] La credencial de la tienda no tiene permisos${scopes.length ? ` (${scopes.join(", ")})` : ""}; la facturaci\xF3n no funciona hasta repararlo en ${storefront.repairPath()}.`
139
139
  );
140
140
  }
141
- var formField = (field, prefix) => field ? field.replace(/^(fiscal|invoice)\./, `${prefix}.`) : void 0;
141
+ var CONTROL_OF = {
142
+ tax_id: "tax_id",
143
+ rfc: "tax_id",
144
+ legal_name: "legal_name",
145
+ razon_social: "legal_name",
146
+ postal_code: "postal_code",
147
+ zip: "postal_code",
148
+ cp: "postal_code",
149
+ tax_regime: "tax_regime",
150
+ regimen: "tax_regime",
151
+ use: "use",
152
+ invoice_use: "use",
153
+ cfdi_use: "use",
154
+ uso_cfdi: "use"
155
+ };
156
+ var formField = (field, prefix) => {
157
+ if (!field) return void 0;
158
+ const control = CONTROL_OF[field.replace(/^(fiscal|invoice)\./, "")];
159
+ return control ? `${prefix}.${control}` : void 0;
160
+ };
161
+ function orderInvoiceBody(invoice) {
162
+ const { requested: _requested, document: _document, use, ...rest } = invoice;
163
+ return { ...rest, ...use ? { invoice_use: use } : {} };
164
+ }
142
165
  function toFailure(err, intent, prefix, values) {
143
166
  const carry = values ? { values } : {};
144
167
  if (!storefront.isStorefrontError(err)) return fail(intent, "unavailable", carry);
@@ -246,7 +269,7 @@ function createInvoiceAction(options) {
246
269
  if (!token) return fail(intent, "unauthorized", { values: echo });
247
270
  const orderId = text("order_id");
248
271
  if (!orderId) return fail(intent, "validation");
249
- const result = await sf.customer.requestInvoice(orderId, invoice, w);
272
+ const result = await sf.customer.requestInvoice(orderId, orderInvoiceBody(invoice), w);
250
273
  return { ok: true, intent, step: result.status === "issued" ? "issued" : "pending", result, ...result.invoice ? { invoice: result.invoice } : {} };
251
274
  }
252
275
  case "extract-document": {
@@ -280,8 +303,10 @@ exports.NO_FISCAL_CONTEXT = NO_FISCAL_CONTEXT;
280
303
  exports.UPLOAD_LIMITS = UPLOAD_LIMITS;
281
304
  exports.createInvoiceAction = createInvoiceAction;
282
305
  exports.extractUploadedDocument = extractUploadedDocument;
306
+ exports.formField = formField;
283
307
  exports.invoiceFromForm = invoiceFromForm;
284
308
  exports.loadFiscalContext = loadFiscalContext;
309
+ exports.orderInvoiceBody = orderInvoiceBody;
285
310
  exports.parseEvidence = parseEvidence;
286
311
  //# sourceMappingURL=fiscal-server.cjs.map
287
312
  //# sourceMappingURL=fiscal-server.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/fiscal/format.ts","../src/fiscal/extraction.ts","../src/fiscal/server-extract.ts","../src/fiscal/server.ts"],"names":["IdentityDocumentError","readPdfText","extractIdentityDocument","catalogsFromRegion","buildDocumentEvidence","isStorefrontError","repairPath"],"mappings":"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","file":"fiscal-server.cjs","sourcesContent":["/**\n * Pure helpers over the region's fiscal catalog (`Region.fiscal_id`). They know no country: the tax id format, the\n * regimes, the invoice uses and the postal code format are all data.\n */\nimport type { InvoiceRequest, Region, RegionFiscalEntry, RegionFiscalId } from '@asteby/storefront'\nimport type { FiscalFieldName, FiscalFieldValues } from './types.js'\n\nexport type FiscalRegion = Pick<Region, 'address'> & { fiscal_id?: RegionFiscalId }\n\n/** Uppercase, no spaces, hyphens or dots: how a tax id is compared and sent. */\nexport function normalizeTaxId(raw: string): string {\n return raw.replace(/[\\s\\-.]/g, '').toUpperCase()\n}\n\n/** Masks a tax id for display and logs: first 2 and last 3 characters stay (`PE********ABC`). */\nexport function maskTaxId(taxId: string): string {\n const v = taxId.trim()\n return v.length <= 5 ? '*'.repeat(v.length) : `${v.slice(0, 2)}${'*'.repeat(v.length - 5)}${v.slice(-3)}`\n}\n\nconst compile = (pattern: string | undefined): RegExp | null => {\n if (!pattern) return null\n try {\n return new RegExp(pattern)\n } catch {\n return null\n }\n}\n\n/** `null` when the tax id is valid for the region (its pattern or one of its generic ids); otherwise why not. */\nexport function taxIdProblem(fid: RegionFiscalId | undefined, raw: string): 'required' | 'format' | null {\n const v = normalizeTaxId(raw)\n if (!v) return 'required'\n if (!fid) return null\n if (fid.generic_ids.includes(v)) return null\n const re = compile(fid.pattern)\n return re && !re.test(v) ? 'format' : null\n}\n\nexport function postalCodeProblem(region: FiscalRegion, raw: string): 'required' | 'format' | null {\n const v = raw.trim()\n if (!v) return 'required'\n const re = compile(region.address?.postal_code?.pattern)\n return re && !re.test(v) ? 'format' : null\n}\n\n/**\n * Builds the function that tells a person type from a tax id when the region does not publish one. Pass the length\n * of the tax id per type, e.g. `personTypeByLength({ individual: 13, company: 12 })`. Without a function the regimes\n * are not narrowed by person type (the server still checks the combination).\n */\nexport function personTypeByLength(lengths: Record<string, number>): (taxId: string) => string | null {\n return (taxId) => {\n const n = normalizeTaxId(taxId).length\n return Object.keys(lengths).find((t) => lengths[t] === n) ?? null\n }\n}\n\n/**\n * Tells a person type from a tax id with the region's own patterns (`fiscal_id.person_patterns`, DATA of the region\n * pack). `null` when the region publishes none or none matches. A pattern that does not compile is ignored.\n */\nexport function personTypeFromPatterns(fid: RegionFiscalId | undefined, taxId: string): string | null {\n const patterns = fid?.person_patterns\n if (!patterns) return null\n const v = normalizeTaxId(taxId)\n for (const type of Object.keys(patterns).sort()) {\n const re = compile(patterns[type])\n if (re?.test(v)) return type\n }\n return null\n}\n\n/** The function to use for the person type: the store's override, else the region's patterns. */\nexport function personTypeResolver(fid: RegionFiscalId | undefined, override?: (taxId: string) => string | null): (taxId: string) => string | null {\n return override ?? ((taxId) => personTypeFromPatterns(fid, taxId))\n}\n\n/**\n * The invoice use to suggest for a regime, from region data only: the organization's `default_invoice_use` when the\n * regime admits it, else the first use whose `suggested_for_regimes` lists the regime (and whose `allowed_regimes`\n * admits it). `null` when the region suggests nothing: then nothing is preselected. A suggestion, never a rule.\n */\nexport function suggestInvoiceUse(fid: RegionFiscalId | undefined, regime: string | null | undefined): string | null {\n if (!fid || !regime) return null\n const allowed = usesFor(fid, regime)\n const preferred = fid.default_invoice_use\n if (preferred && allowed.some((u) => u.code === preferred)) return preferred\n return allowed.find((u) => u.suggested_for_regimes?.includes(regime))?.code ?? null\n}\n\n/** Regimes that fit a person type (all when the type is unknown). */\nexport function regimesFor(fid: RegionFiscalId | undefined, personType: string | null): RegionFiscalEntry[] {\n const all = fid?.regimes ?? []\n if (!personType) return all\n return all.filter((r) => r.person_types.length === 0 || r.person_types.includes(personType))\n}\n\n/** Invoice uses a regime admits (all when no regime is chosen yet). */\nexport function usesFor(fid: RegionFiscalId | undefined, regime: string | null | undefined): RegionFiscalEntry[] {\n const all = fid?.invoice_uses ?? []\n if (!regime) return all\n return all.filter((u) => !u.allowed_regimes || u.allowed_regimes.length === 0 || u.allowed_regimes.includes(regime))\n}\n\nexport type FiscalProblem = 'required' | 'format' | 'unknown' | 'person_type' | 'use_not_allowed'\n\n/** Client-side check of the whole form against the region; the server stays the authority. */\nexport function validateFiscalValues(\n region: FiscalRegion,\n v: Partial<FiscalFieldValues>,\n personTypeOverride?: (taxId: string) => string | null,\n): Partial<Record<FiscalFieldName, FiscalProblem>> {\n const fid = region.fiscal_id\n const personTypeOf = personTypeResolver(fid, personTypeOverride)\n const out: Partial<Record<FiscalFieldName, FiscalProblem>> = {}\n const tax = taxIdProblem(fid, v.tax_id ?? '')\n if (tax) out.tax_id = tax\n if (!(v.legal_name ?? '').trim()) out.legal_name = 'required'\n if (postalCodeProblem(region, v.postal_code ?? '')) out.postal_code = postalCodeProblem(region, v.postal_code ?? '')!\n const regime = v.tax_regime ?? ''\n if (!regime) out.tax_regime = 'required'\n else if (fid) {\n const entry = fid.regimes.find((r) => r.code === regime)\n if (!entry) out.tax_regime = 'unknown'\n else if (!tax && !fid.generic_ids.includes(normalizeTaxId(v.tax_id ?? ''))) {\n const person = personTypeOf?.(v.tax_id ?? '') ?? null\n if (person && entry.person_types.length > 0 && !entry.person_types.includes(person)) out.tax_regime = 'person_type'\n }\n }\n const use = v.use ?? ''\n if (!use) out.use = 'required'\n else if (fid) {\n const entry = fid.invoice_uses.find((u) => u.code === use)\n if (!entry) out.use = 'unknown'\n else if (regime && entry.allowed_regimes && entry.allowed_regimes.length > 0 && !entry.allowed_regimes.includes(regime)) out.use = 'use_not_allowed'\n }\n return out\n}\n\n/** Form values -> the API's `InvoiceRequest` (without `document`). */\nexport function toInvoiceRequest(v: Partial<FiscalFieldValues>, countryCode?: string): InvoiceRequest {\n return {\n requested: true,\n tax_id: normalizeTaxId(v.tax_id ?? ''),\n legal_name: (v.legal_name ?? '').trim(),\n tax_regime: v.tax_regime ?? '',\n use: v.use ?? '',\n postal_code: (v.postal_code ?? '').trim(),\n ...(countryCode ? { country_code: countryCode } : {}),\n }\n}\n\n/** `label_en` for English locales when the region has it. */\nexport function entryLabel(e: RegionFiscalEntry, locale?: string | null): string {\n return (locale ?? 'es-MX').toLowerCase().startsWith('en') && e.label_en ? e.label_en : e.label\n}\n","/** Turns what the engine read into what the UI shows. Shared by the browser reader and the server upload. */\nimport type { FiscalDocumentEvidence } from '@asteby/storefront'\nimport type { IdentityExtraction } from '@asteby/storefront/fiscal-document'\nimport { normalizeTaxId } from './format.js'\nimport type { FiscalExtractionSummary, FiscalFieldName } from './types.js'\n\nconst first = (v: string | string[] | undefined): string => (Array.isArray(v) ? (v[0] ?? '') : (v ?? ''))\n\n/** `evidence` comes from `buildDocumentEvidence`. Only the conventional fields are kept: nothing else can leak. */\nexport function summarizeExtraction(x: IdentityExtraction, evidence: FiscalDocumentEvidence, validRegimes?: Iterable<string>): FiscalExtractionSummary {\n const f = x.fields\n const valid = validRegimes ? new Set(validRegimes) : null\n const raw = f.tax_regime\n const candidates = (Array.isArray(raw) ? raw : raw ? [raw] : []).filter((c) => !valid || valid.has(c))\n const values: FiscalExtractionSummary['values'] = {}\n const from: FiscalFieldName[] = []\n const put = (name: FiscalFieldName, v: string) => {\n if (!v) return\n values[name] = v\n from.push(name)\n }\n put('tax_id', normalizeTaxId(first(f.tax_id)))\n put('legal_name', first(f.legal_name).trim())\n put('postal_code', first(f.postal_code).trim())\n if (candidates.length === 1) put('tax_regime', candidates[0]!)\n else if (candidates.length > 1) from.push('tax_regime')\n return {\n kind: x.kind,\n values,\n regime_candidates: candidates,\n from_document: from,\n qr_match: x.qr.match,\n stale: x.stale,\n age_days: x.age_days,\n missing: [...x.missing],\n evidence,\n }\n}\n","/**\n * Reads a tax document uploaded without JavaScript, on the server, with the same engine the browser uses. In memory\n * only: the bytes are never written to disk, never logged and are dropped when this returns.\n */\nimport type { IdentityDocumentSpec, Region } from '@asteby/storefront'\nimport { IdentityDocumentError, buildDocumentEvidence, catalogsFromRegion, extractIdentityDocument, readPdfText } from '@asteby/storefront/fiscal-document'\nimport { summarizeExtraction } from './extraction.js'\nimport type { FiscalExtractionSummary } from './types.js'\n\nexport type UploadLimits = { maxBytes: number; maxPages: number; timeoutMs: number }\n\n/** 5 MiB, 6 pages, 3 s: the same as the browser reader. */\nexport const UPLOAD_LIMITS: Readonly<UploadLimits> = Object.freeze({ maxBytes: 5 * 1024 * 1024, maxPages: 6, timeoutMs: 3000 })\n\nconst PDF_MAGIC = [0x25, 0x50, 0x44, 0x46, 0x2d] // %PDF-\n\nexport async function extractUploadedDocument(\n file: Blob,\n spec: IdentityDocumentSpec,\n region: Pick<Region, 'fiscal_id'>,\n options: { limits?: Partial<UploadLimits>; now?: Date } = {},\n): Promise<FiscalExtractionSummary> {\n const lim = { ...UPLOAD_LIMITS, ...options.limits }\n // The region can only lower the limits.\n const maxBytes = Math.min(lim.maxBytes, spec.max_bytes || lim.maxBytes)\n const maxPages = Math.min(lim.maxPages, spec.max_pages || lim.maxPages)\n if (file.size > maxBytes) throw new IdentityDocumentError('too_large', 'file over the size limit', { reason: 'bytes' })\n const bytes = new Uint8Array(await file.arrayBuffer())\n if (!PDF_MAGIC.every((b, i) => bytes[i] === b)) throw new IdentityDocumentError('not_pdf', 'not a PDF')\n // The engine is the only reader. QR cross-check needs a canvas, which a server does not have: `qr_match` stays null.\n const { text, sha256 } = await readPdfText(bytes, { maxBytes, maxPages, timeoutMs: lim.timeoutMs })\n const x = extractIdentityDocument(text, spec, {\n catalogs: region.fiscal_id ? catalogsFromRegion(region.fiscal_id) : {},\n ...(options.now ? { now: options.now } : {}),\n })\n return summarizeExtraction(x, buildDocumentEvidence(x, { sha256, source: 'pdf_server' }), region.fiscal_id?.regimes.map((r) => r.code))\n}\n","/**\n * Server side of the fiscal components (`@asteby/storefront-react/fiscal/server`): `loadFiscalContext` for the loader and\n * `createInvoiceAction` for the action. No React, no router: Web `Request`/`Response` only, so any framework mounts it.\n *\n * The browser never talks to OPS. The action uses the store's `sk_` client and the buyer's session token (read from the\n * store's cookie). A tax document uploaded WITHOUT JavaScript is read in memory here with the same engine the browser\n * uses, and discarded: no disk, no logs, no copy.\n */\nimport {\n isStorefrontError,\n repairPath,\n type FiscalDocumentEvidence,\n type FiscalIdentityInput,\n type InvoiceRequest,\n type InvoicingCapabilities,\n type Region,\n type StorefrontClient,\n} from '@asteby/storefront'\nimport { normalizeTaxId } from './format.js'\nimport { extractUploadedDocument, UPLOAD_LIMITS, type UploadLimits } from './server-extract.js'\nimport type { FiscalActionData, FiscalErrorCode, FiscalIntent, FiscalReason } from './types.js'\n\nexport type { FiscalActionData, FiscalErrorCode, FiscalIntent, FiscalReason } from './types.js'\nexport { UPLOAD_LIMITS, extractUploadedDocument } from './server-extract.js'\nexport type { UploadLimits } from './server-extract.js'\n\n// ---- context ---------------------------------------------------------------------------------------------------------\n\nexport type FiscalContext = {\n /** The store can invoice: it has a region with a fiscal catalog and `capabilities.invoicing.available`. */\n available: boolean\n /** Why `available` is what it is. Absent on contexts built by older code: read it as `unknown`. */\n reason?: FiscalReason\n region: Region | null\n invoicing: InvoicingCapabilities | null\n}\n\n/** What is assumed when the store cannot be asked: nothing is known (`unreachable`), nothing breaks. */\nexport const NO_FISCAL_CONTEXT: Readonly<FiscalContext> = Object.freeze({ available: false, reason: 'unreachable' as const, region: null, invoicing: null })\n\nconst FAILURE_TTL_MS = 5_000\nconst cache = new WeakMap<object, { at: number; ok: boolean; value: FiscalContext }>()\n\nexport type LoadFiscalContextOptions = {\n /** How long a good answer is reused per client (default 60 s). `0` disables the cache. */\n ttlMs?: number\n signal?: AbortSignal\n now?: () => number\n}\n\n/**\n * Asks the store for its region and what invoicing it offers, with a short cache per client. It NEVER throws: when OPS\n * does not answer (older server, outage, token without scope) it returns `NO_FISCAL_CONTEXT` and retries after a few\n * seconds, so a checkout without invoicing keeps working. `reason` says which of the cases it is. Use it as the loader:\n * `loader = () => loadFiscalContext(sf)`.\n */\nexport async function loadFiscalContext(sf: StorefrontClient, options: LoadFiscalContextOptions = {}): Promise<FiscalContext> {\n const now = options.now ?? Date.now\n const ttl = options.ttlMs ?? 60_000\n const hit = cache.get(sf)\n if (hit && now() - hit.at < (hit.ok ? ttl : FAILURE_TTL_MS)) return hit.value\n const opt = options.signal ? { signal: options.signal } : undefined\n const [region, caps] = await Promise.allSettled([sf.region(opt), sf.customer.capabilities(opt)])\n const reg = region.status === 'fulfilled' ? region.value : null\n const noRegion = region.status === 'rejected' && isStorefrontError(region.reason) && region.reason.status === 404\n const invoicing = caps.status === 'fulfilled' ? (caps.value?.invoicing ?? null) : null\n const ok = (region.status === 'fulfilled' || noRegion) && caps.status === 'fulfilled'\n const available = !!reg?.fiscal_id && !!invoicing?.available\n const reason: FiscalReason = available\n ? 'available'\n : !ok\n ? 'unreachable'\n : noRegion || !reg?.fiscal_id\n ? invoicing && !invoicing.available\n ? 'off'\n : 'no_region'\n : !invoicing\n ? 'unknown'\n : 'off'\n const value: FiscalContext = { available, reason, region: reg, invoicing: invoicing?.available ? invoicing : null }\n cache.set(sf, { at: now(), ok, value: ok || available ? value : NO_FISCAL_CONTEXT })\n return ok || available ? value : NO_FISCAL_CONTEXT\n}\n\n// ---- action ----------------------------------------------------------------------------------------------------------\n\n/** What the action needs of a cookie to read the buyer's session. React Router's `createCookie(...)` fits as it is. */\nexport type SessionCookie = { parse(header: string | null): unknown }\n\nexport type CreateInvoiceActionOptions = {\n storefront: StorefrontClient\n /** The store's cookie for the buyer session token (`createCookie('sf_customer', ...)`), to know who is asking. */\n customerCookie?: SessionCookie\n /** Alternative to `customerCookie`: returns the buyer's session token, or `null` for a guest. */\n readCustomerToken?: (request: Request) => string | null | undefined | Promise<string | null | undefined>\n /** Shopper IP for the per-shopper rate limit (see `clientIp` of the client). */\n clientIp?: (request: Request) => string | null | undefined\n /** Region + capabilities source, for tests or a shared cache (default `loadFiscalContext`). */\n context?: (sf: StorefrontClient) => Promise<FiscalContext>\n /** Limits of the no-JS upload (defaults: 5 MiB, 6 pages, 3 s). The region's own limits can only lower them. */\n limits?: Partial<UploadLimits>\n /** Clock, for the freshness check. */\n now?: () => Date\n /** Rejects cross-site posts (default `true`): `Origin` and `Sec-Fetch-Site` must match the site. */\n sameSiteOnly?: boolean\n /** Prefix of the form fields (default `invoice`). */\n namePrefix?: string\n}\n\nconst EVIDENCE_SOURCES = new Set(['pdf_client', 'pdf_server', 'qr', 'manual'])\n\n/** Reads the evidence the reader put in the form, keeping only the known keys (a form is untrusted input). */\nexport function parseEvidence(raw: unknown): FiscalDocumentEvidence | undefined {\n if (typeof raw !== 'string' || !raw || raw.length > 4096) return undefined\n let o: unknown\n try {\n o = JSON.parse(raw)\n } catch {\n return undefined\n }\n if (typeof o !== 'object' || o === null) return undefined\n const e = o as Record<string, unknown>\n if (typeof e.kind !== 'string' || !e.kind || typeof e.source !== 'string' || !EVIDENCE_SOURCES.has(e.source)) return undefined\n const str = (v: unknown, max: number) => (typeof v === 'string' && v.length <= max ? v : null)\n return {\n kind: e.kind.slice(0, 64),\n document_id: str(e.document_id, 64),\n issued_on: str(e.issued_on, 32),\n sha256: typeof e.sha256 === 'string' && /^[0-9a-f]{64}$/.test(e.sha256) ? e.sha256 : null,\n ...(typeof e.extractor === 'string' ? { extractor: e.extractor.slice(0, 64) } : {}),\n source: e.source as FiscalDocumentEvidence['source'],\n qr_match: typeof e.qr_match === 'boolean' ? e.qr_match : null,\n }\n}\n\n/** Reads the `invoice.*` fields of a form into the API's `InvoiceRequest`. */\nexport function invoiceFromForm(form: FormData, prefix = 'invoice'): InvoiceRequest {\n const text = (k: string) => {\n const v = form.get(`${prefix}.${k}`)\n return typeof v === 'string' ? v.trim() : ''\n }\n const evidence = parseEvidence(form.get(`${prefix}.document`))\n const out: InvoiceRequest = {\n requested: true,\n tax_id: normalizeTaxId(text('tax_id')),\n legal_name: text('legal_name'),\n tax_regime: text('tax_regime'),\n use: text('use'),\n postal_code: text('postal_code'),\n }\n if (evidence) out.document = evidence\n if (form.get(`${prefix}.save`)) out.save = true\n return out\n}\n\nconst fail = (intent: FiscalIntent | undefined, code: FiscalErrorCode, extra: Partial<FiscalActionData> = {}): FiscalActionData => ({\n ok: false,\n ...(intent ? { intent } : {}),\n ...extra,\n error: { code, ...(extra.error?.field ? { field: extra.error.field } : {}), ...(extra.error?.blocked ? { blocked: extra.error.blocked } : {}) },\n})\n\nconst LOG_EVERY_MS = 60_000\nlet lastLogAt = -Infinity\nfunction logMisconfigured(scopes: readonly string[]): void {\n const t = Date.now()\n if (t - lastLogAt < LOG_EVERY_MS) return\n lastLogAt = t\n console.error(\n `[@asteby/storefront-react] La credencial de la tienda no tiene permisos${scopes.length ? ` (${scopes.join(', ')})` : ''}; ` +\n `la facturación no funciona hasta repararlo en ${repairPath()}.`,\n )\n}\n\n/** `fiscal.tax_regime` (profile) and `invoice.tax_regime` (cart, order) both name the same form control. */\nconst formField = (field: string | undefined, prefix: string) => (field ? field.replace(/^(fiscal|invoice)\\./, `${prefix}.`) : undefined)\n\nfunction toFailure(err: unknown, intent: FiscalIntent, prefix: string, values?: Partial<FiscalActionData['values']>): FiscalActionData {\n const carry: Partial<FiscalActionData> = values ? { values } : {}\n if (!isStorefrontError(err)) return fail(intent, 'unavailable', carry)\n if (err.code === 'insufficient_scope') {\n logMisconfigured(err.requiredScopes)\n return fail(intent, 'store_misconfigured', carry)\n }\n const details = (err.details ?? {}) as { reason?: unknown; blocked_reason?: unknown }\n switch (err.status) {\n case 401:\n return fail(intent, 'unauthorized', carry)\n case 404:\n return fail(intent, 'not_found', carry)\n case 409: {\n const why = typeof details.reason === 'string' ? details.reason : typeof details.blocked_reason === 'string' ? details.blocked_reason : ''\n return fail(intent, why === 'already_checked_out' ? 'not_found' : 'blocked', { ...carry, ...(why ? { error: { code: 'blocked', blocked: why } } : {}) })\n }\n case 422:\n case 400: {\n const field = formField(err.field, prefix)\n return fail(intent, 'validation', {\n ...carry,\n ...(field ? { fieldErrors: { [field]: err.message || 'invalid' }, error: { code: 'validation', field } } : {}),\n })\n }\n case 429:\n return fail(intent, 'rate_limited', carry)\n case 403:\n return fail(intent, 'unsupported', carry)\n default:\n return fail(intent, 'unavailable', carry)\n }\n}\n\nfunction sameSite(request: Request): boolean {\n const site = request.headers.get('sec-fetch-site')\n if (site && site !== 'same-origin' && site !== 'same-site' && site !== 'none') return false\n const origin = request.headers.get('origin')\n if (!origin) return true\n try {\n const host = request.headers.get('x-forwarded-host') ?? request.headers.get('host') ?? new URL(request.url).host\n return new URL(origin).host === host.split(',')[0]!.trim()\n } catch {\n return false\n }\n}\n\n/**\n * The invoicing action: one `POST` form with an `intent` field.\n *\n * | intent | fields | does |\n * | ------------------ | --------------------------------------------------- | ---------------------------------------------------------- |\n * | `set-invoice` | cart_id, invoice.* (`invoice.want` unset withdraws) | `carts.setInvoice`: validates against the region BEFORE paying |\n * | `save-profile` | invoice.*, consent | `customer.update({ fiscal })`, needs a session and consent |\n * | `request-invoice` | order_id, invoice.* | `customer.requestInvoice` (invoice after the purchase) |\n * | `clear-profile` | - | `customer.update({ fiscal: null })` (the buyer's right to delete) |\n * | `extract-document` | document (file), document_kind? | reads the file in memory, discards it, `step: 'review'` |\n *\n * It never redirects: it returns a `FiscalActionData` for the page to render (`<InvoiceToggle data={...}>`), with 422s\n * mapped to the field (`fieldErrors['invoice.tax_regime']`). The tax id of the buyer is never logged.\n */\nexport function createInvoiceAction(options: CreateInvoiceActionOptions) {\n const { storefront: sf } = options\n const prefix = options.namePrefix ?? 'invoice'\n const loadContext = options.context ?? ((c: StorefrontClient) => loadFiscalContext(c))\n const limits: UploadLimits = { ...UPLOAD_LIMITS, ...options.limits }\n\n async function sessionToken(request: Request): Promise<string | null> {\n if (options.readCustomerToken) return (await options.readCustomerToken(request)) || null\n if (!options.customerCookie) return null\n const v = await options.customerCookie.parse(request.headers.get('cookie'))\n return typeof v === 'string' && v ? v : null\n }\n\n return async function invoiceAction({ request }: { request: Request }): Promise<FiscalActionData> {\n if (options.sameSiteOnly !== false && !sameSite(request)) return fail(undefined, 'unsupported')\n // A file bigger than the limit is refused before it is buffered.\n const declared = Number(request.headers.get('content-length') ?? 0)\n if (declared > limits.maxBytes + 256 * 1024) return fail('extract-document', 'too_large')\n const form = await request.formData().catch(() => null)\n if (!form) return fail(undefined, 'validation')\n const intent = form.get('intent') as FiscalIntent\n const ip = options.clientIp?.(request)\n const token = await sessionToken(request)\n const w = { ...(ip ? { clientIp: ip } : {}), ...(token ? { customerToken: token } : {}) }\n const invoice = invoiceFromForm(form, prefix)\n const echo = { tax_id: invoice.tax_id ?? '', legal_name: invoice.legal_name ?? '', postal_code: invoice.postal_code ?? '', tax_regime: invoice.tax_regime ?? '', use: invoice.use ?? '' }\n const text = (name: string) => {\n const v = form.get(name)\n return typeof v === 'string' ? v.trim() : ''\n }\n\n try {\n switch (intent) {\n case 'set-invoice': {\n const cartId = text('cart_id')\n if (!cartId) return fail(intent, 'validation')\n if (!form.get(`${prefix}.want`) && !form.get(`${prefix}.requested`)) {\n await sf.carts.setInvoice(cartId, null, w)\n return { ok: true, intent, step: 'cleared' }\n }\n await sf.carts.setInvoice(cartId, invoice, w)\n return { ok: true, intent, step: 'saved', values: echo }\n }\n case 'save-profile': {\n if (!token) return fail(intent, 'unauthorized', { values: echo })\n if (!form.get('consent')) return fail(intent, 'consent_required', { values: echo })\n const fiscal: FiscalIdentityInput = {\n tax_id: invoice.tax_id ?? '',\n legal_name: invoice.legal_name ?? '',\n tax_regime: invoice.tax_regime ?? '',\n postal_code: invoice.postal_code ?? '',\n invoice_use: invoice.use ?? '',\n ...(invoice.document ? { document: invoice.document } : {}),\n }\n await sf.customer.update({ fiscal }, w)\n return { ok: true, intent, step: 'saved', values: echo }\n }\n case 'clear-profile': {\n if (!token) return fail(intent, 'unauthorized')\n await sf.customer.update({ fiscal: null }, w)\n return { ok: true, intent, step: 'cleared' }\n }\n case 'request-invoice': {\n if (!token) return fail(intent, 'unauthorized', { values: echo })\n const orderId = text('order_id')\n if (!orderId) return fail(intent, 'validation')\n const result = await sf.customer.requestInvoice(orderId, invoice, w)\n return { ok: true, intent, step: result.status === 'issued' ? 'issued' : 'pending', result, ...(result.invoice ? { invoice: result.invoice } : {}) }\n }\n case 'extract-document': {\n const ctx = await loadContext(sf)\n const region = ctx.region\n const kind = text('document_kind')\n const specs = region?.fiscal_id?.documents ?? []\n const spec = (kind ? specs.find((d) => d.kind === kind) : specs[0]) ?? undefined\n if (!ctx.available || !region || !spec) return fail(intent, 'unsupported')\n const file = form.get('document')\n if (!file || typeof file === 'string' || file.size === 0) return fail(intent, 'not_pdf')\n try {\n const extraction = await extractUploadedDocument(file, spec, region, { limits, ...(options.now ? { now: options.now() } : {}) })\n return { ok: true, intent, step: 'review', extraction }\n } catch (err) {\n const code = (err as { code?: string } | null)?.code\n const known: FiscalErrorCode[] = ['not_pdf', 'too_large', 'no_text', 'password', 'unreadable']\n // Never log the error: it can carry text from the document.\n return fail(intent, known.includes(code as FiscalErrorCode) ? (code as FiscalErrorCode) : 'unreadable')\n }\n }\n default:\n return fail(undefined, 'validation')\n }\n } catch (err) {\n return toFailure(err, intent, prefix, intent === 'clear-profile' ? undefined : echo)\n }\n }\n}\n\nexport type InvoiceAction = ReturnType<typeof createInvoiceAction>\n"]}
1
+ {"version":3,"sources":["../src/fiscal/format.ts","../src/fiscal/extraction.ts","../src/fiscal/server-extract.ts","../src/fiscal/server.ts"],"names":["IdentityDocumentError","readPdfText","extractIdentityDocument","catalogsFromRegion","buildDocumentEvidence","isStorefrontError","repairPath"],"mappings":"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","file":"fiscal-server.cjs","sourcesContent":["/**\n * Pure helpers over the region's fiscal catalog (`Region.fiscal_id`). They know no country: the tax id format, the\n * regimes, the invoice uses and the postal code format are all data.\n */\nimport type { InvoiceRequest, Region, RegionFiscalEntry, RegionFiscalId } from '@asteby/storefront'\nimport type { FiscalFieldName, FiscalFieldValues } from './types.js'\n\nexport type FiscalRegion = Pick<Region, 'address'> & { fiscal_id?: RegionFiscalId }\n\n/** Uppercase, no spaces, hyphens or dots: how a tax id is compared and sent. */\nexport function normalizeTaxId(raw: string): string {\n return raw.replace(/[\\s\\-.]/g, '').toUpperCase()\n}\n\n/** Masks a tax id for display and logs: first 2 and last 3 characters stay (`PE********ABC`). */\nexport function maskTaxId(taxId: string): string {\n const v = taxId.trim()\n return v.length <= 5 ? '*'.repeat(v.length) : `${v.slice(0, 2)}${'*'.repeat(v.length - 5)}${v.slice(-3)}`\n}\n\nconst compile = (pattern: string | undefined): RegExp | null => {\n if (!pattern) return null\n try {\n return new RegExp(pattern)\n } catch {\n return null\n }\n}\n\n/** `null` when the tax id is valid for the region (its pattern or one of its generic ids); otherwise why not. */\nexport function taxIdProblem(fid: RegionFiscalId | undefined, raw: string): 'required' | 'format' | null {\n const v = normalizeTaxId(raw)\n if (!v) return 'required'\n if (!fid) return null\n if (fid.generic_ids.includes(v)) return null\n const re = compile(fid.pattern)\n return re && !re.test(v) ? 'format' : null\n}\n\nexport function postalCodeProblem(region: FiscalRegion, raw: string): 'required' | 'format' | null {\n const v = raw.trim()\n if (!v) return 'required'\n const re = compile(region.address?.postal_code?.pattern)\n return re && !re.test(v) ? 'format' : null\n}\n\n/**\n * Builds the function that tells a person type from a tax id when the region does not publish one. Pass the length\n * of the tax id per type, e.g. `personTypeByLength({ individual: 13, company: 12 })`. Without a function the regimes\n * are not narrowed by person type (the server still checks the combination).\n */\nexport function personTypeByLength(lengths: Record<string, number>): (taxId: string) => string | null {\n return (taxId) => {\n const n = normalizeTaxId(taxId).length\n return Object.keys(lengths).find((t) => lengths[t] === n) ?? null\n }\n}\n\n/**\n * Tells a person type from a tax id with the region's own patterns (`fiscal_id.person_patterns`, DATA of the region\n * pack). `null` when the region publishes none or none matches. A pattern that does not compile is ignored.\n */\nexport function personTypeFromPatterns(fid: RegionFiscalId | undefined, taxId: string): string | null {\n const patterns = fid?.person_patterns\n if (!patterns) return null\n const v = normalizeTaxId(taxId)\n for (const type of Object.keys(patterns).sort()) {\n const re = compile(patterns[type])\n if (re?.test(v)) return type\n }\n return null\n}\n\n/** The function to use for the person type: the store's override, else the region's patterns. */\nexport function personTypeResolver(fid: RegionFiscalId | undefined, override?: (taxId: string) => string | null): (taxId: string) => string | null {\n return override ?? ((taxId) => personTypeFromPatterns(fid, taxId))\n}\n\n/**\n * The invoice use to suggest for a regime, from region data only: the organization's `default_invoice_use` when the\n * regime admits it, else the first use whose `suggested_for_regimes` lists the regime (and whose `allowed_regimes`\n * admits it). `null` when the region suggests nothing: then nothing is preselected. A suggestion, never a rule.\n */\nexport function suggestInvoiceUse(fid: RegionFiscalId | undefined, regime: string | null | undefined): string | null {\n if (!fid || !regime) return null\n const allowed = usesFor(fid, regime)\n const preferred = fid.default_invoice_use\n if (preferred && allowed.some((u) => u.code === preferred)) return preferred\n return allowed.find((u) => u.suggested_for_regimes?.includes(regime))?.code ?? null\n}\n\n/** Regimes that fit a person type (all when the type is unknown). */\nexport function regimesFor(fid: RegionFiscalId | undefined, personType: string | null): RegionFiscalEntry[] {\n const all = fid?.regimes ?? []\n if (!personType) return all\n return all.filter((r) => r.person_types.length === 0 || r.person_types.includes(personType))\n}\n\n/** Invoice uses a regime admits (all when no regime is chosen yet). */\nexport function usesFor(fid: RegionFiscalId | undefined, regime: string | null | undefined): RegionFiscalEntry[] {\n const all = fid?.invoice_uses ?? []\n if (!regime) return all\n return all.filter((u) => !u.allowed_regimes || u.allowed_regimes.length === 0 || u.allowed_regimes.includes(regime))\n}\n\nexport type FiscalProblem = 'required' | 'format' | 'unknown' | 'person_type' | 'use_not_allowed'\n\n/** Client-side check of the whole form against the region; the server stays the authority. */\nexport function validateFiscalValues(\n region: FiscalRegion,\n v: Partial<FiscalFieldValues>,\n personTypeOverride?: (taxId: string) => string | null,\n): Partial<Record<FiscalFieldName, FiscalProblem>> {\n const fid = region.fiscal_id\n const personTypeOf = personTypeResolver(fid, personTypeOverride)\n const out: Partial<Record<FiscalFieldName, FiscalProblem>> = {}\n const tax = taxIdProblem(fid, v.tax_id ?? '')\n if (tax) out.tax_id = tax\n if (!(v.legal_name ?? '').trim()) out.legal_name = 'required'\n if (postalCodeProblem(region, v.postal_code ?? '')) out.postal_code = postalCodeProblem(region, v.postal_code ?? '')!\n const regime = v.tax_regime ?? ''\n if (!regime) out.tax_regime = 'required'\n else if (fid) {\n const entry = fid.regimes.find((r) => r.code === regime)\n if (!entry) out.tax_regime = 'unknown'\n else if (!tax && !fid.generic_ids.includes(normalizeTaxId(v.tax_id ?? ''))) {\n const person = personTypeOf?.(v.tax_id ?? '') ?? null\n if (person && entry.person_types.length > 0 && !entry.person_types.includes(person)) out.tax_regime = 'person_type'\n }\n }\n const use = v.use ?? ''\n if (!use) out.use = 'required'\n else if (fid) {\n const entry = fid.invoice_uses.find((u) => u.code === use)\n if (!entry) out.use = 'unknown'\n else if (regime && entry.allowed_regimes && entry.allowed_regimes.length > 0 && !entry.allowed_regimes.includes(regime)) out.use = 'use_not_allowed'\n }\n return out\n}\n\n/** Form values -> the API's `InvoiceRequest` (without `document`). */\nexport function toInvoiceRequest(v: Partial<FiscalFieldValues>, countryCode?: string): InvoiceRequest {\n return {\n requested: true,\n tax_id: normalizeTaxId(v.tax_id ?? ''),\n legal_name: (v.legal_name ?? '').trim(),\n tax_regime: v.tax_regime ?? '',\n use: v.use ?? '',\n postal_code: (v.postal_code ?? '').trim(),\n ...(countryCode ? { country_code: countryCode } : {}),\n }\n}\n\n/** `label_en` for English locales when the region has it. */\nexport function entryLabel(e: RegionFiscalEntry, locale?: string | null): string {\n return (locale ?? 'es-MX').toLowerCase().startsWith('en') && e.label_en ? e.label_en : e.label\n}\n","/** Turns what the engine read into what the UI shows. Shared by the browser reader and the server upload. */\nimport type { FiscalDocumentEvidence } from '@asteby/storefront'\nimport type { IdentityExtraction } from '@asteby/storefront/fiscal-document'\nimport { normalizeTaxId } from './format.js'\nimport type { FiscalExtractionSummary, FiscalFieldName } from './types.js'\n\nconst first = (v: string | string[] | undefined): string => (Array.isArray(v) ? (v[0] ?? '') : (v ?? ''))\n\n/** `evidence` comes from `buildDocumentEvidence`. Only the conventional fields are kept: nothing else can leak. */\nexport function summarizeExtraction(x: IdentityExtraction, evidence: FiscalDocumentEvidence, validRegimes?: Iterable<string>): FiscalExtractionSummary {\n const f = x.fields\n const valid = validRegimes ? new Set(validRegimes) : null\n const raw = f.tax_regime\n const candidates = (Array.isArray(raw) ? raw : raw ? [raw] : []).filter((c) => !valid || valid.has(c))\n const values: FiscalExtractionSummary['values'] = {}\n const from: FiscalFieldName[] = []\n const put = (name: FiscalFieldName, v: string) => {\n if (!v) return\n values[name] = v\n from.push(name)\n }\n put('tax_id', normalizeTaxId(first(f.tax_id)))\n put('legal_name', first(f.legal_name).trim())\n put('postal_code', first(f.postal_code).trim())\n if (candidates.length === 1) put('tax_regime', candidates[0]!)\n else if (candidates.length > 1) from.push('tax_regime')\n return {\n kind: x.kind,\n values,\n regime_candidates: candidates,\n from_document: from,\n qr_match: x.qr.match,\n stale: x.stale,\n age_days: x.age_days,\n missing: [...x.missing],\n evidence,\n }\n}\n","/**\n * Reads a tax document uploaded without JavaScript, on the server, with the same engine the browser uses. In memory\n * only: the bytes are never written to disk, never logged and are dropped when this returns.\n */\nimport type { IdentityDocumentSpec, Region } from '@asteby/storefront'\nimport { IdentityDocumentError, buildDocumentEvidence, catalogsFromRegion, extractIdentityDocument, readPdfText } from '@asteby/storefront/fiscal-document'\nimport { summarizeExtraction } from './extraction.js'\nimport type { FiscalExtractionSummary } from './types.js'\n\nexport type UploadLimits = { maxBytes: number; maxPages: number; timeoutMs: number }\n\n/** 5 MiB, 6 pages, 3 s: the same as the browser reader. */\nexport const UPLOAD_LIMITS: Readonly<UploadLimits> = Object.freeze({ maxBytes: 5 * 1024 * 1024, maxPages: 6, timeoutMs: 3000 })\n\nconst PDF_MAGIC = [0x25, 0x50, 0x44, 0x46, 0x2d] // %PDF-\n\nexport async function extractUploadedDocument(\n file: Blob,\n spec: IdentityDocumentSpec,\n region: Pick<Region, 'fiscal_id'>,\n options: { limits?: Partial<UploadLimits>; now?: Date } = {},\n): Promise<FiscalExtractionSummary> {\n const lim = { ...UPLOAD_LIMITS, ...options.limits }\n // The region can only lower the limits.\n const maxBytes = Math.min(lim.maxBytes, spec.max_bytes || lim.maxBytes)\n const maxPages = Math.min(lim.maxPages, spec.max_pages || lim.maxPages)\n if (file.size > maxBytes) throw new IdentityDocumentError('too_large', 'file over the size limit', { reason: 'bytes' })\n const bytes = new Uint8Array(await file.arrayBuffer())\n if (!PDF_MAGIC.every((b, i) => bytes[i] === b)) throw new IdentityDocumentError('not_pdf', 'not a PDF')\n // The engine is the only reader. QR cross-check needs a canvas, which a server does not have: `qr_match` stays null.\n const { text, sha256 } = await readPdfText(bytes, { maxBytes, maxPages, timeoutMs: lim.timeoutMs })\n const x = extractIdentityDocument(text, spec, {\n catalogs: region.fiscal_id ? catalogsFromRegion(region.fiscal_id) : {},\n ...(options.now ? { now: options.now } : {}),\n })\n return summarizeExtraction(x, buildDocumentEvidence(x, { sha256, source: 'pdf_server' }), region.fiscal_id?.regimes.map((r) => r.code))\n}\n","/**\n * Server side of the fiscal components (`@asteby/storefront-react/fiscal/server`): `loadFiscalContext` for the loader and\n * `createInvoiceAction` for the action. No React, no router: Web `Request`/`Response` only, so any framework mounts it.\n *\n * The browser never talks to OPS. The action uses the store's `sk_` client and the buyer's session token (read from the\n * store's cookie). A tax document uploaded WITHOUT JavaScript is read in memory here with the same engine the browser\n * uses, and discarded: no disk, no logs, no copy.\n */\nimport {\n isStorefrontError,\n repairPath,\n type FiscalDocumentEvidence,\n type FiscalIdentityInput,\n type InvoiceRequest,\n type InvoicingCapabilities,\n type Region,\n type StorefrontClient,\n} from '@asteby/storefront'\nimport { normalizeTaxId } from './format.js'\nimport { extractUploadedDocument, UPLOAD_LIMITS, type UploadLimits } from './server-extract.js'\nimport type { FiscalActionData, FiscalErrorCode, FiscalIntent, FiscalReason } from './types.js'\n\nexport type { FiscalActionData, FiscalErrorCode, FiscalIntent, FiscalReason } from './types.js'\nexport { UPLOAD_LIMITS, extractUploadedDocument } from './server-extract.js'\nexport type { UploadLimits } from './server-extract.js'\n\n// ---- context ---------------------------------------------------------------------------------------------------------\n\nexport type FiscalContext = {\n /** The store can invoice: it has a region with a fiscal catalog and `capabilities.invoicing.available`. */\n available: boolean\n /** Why `available` is what it is. Absent on contexts built by older code: read it as `unknown`. */\n reason?: FiscalReason\n region: Region | null\n invoicing: InvoicingCapabilities | null\n}\n\n/** What is assumed when the store cannot be asked: nothing is known (`unreachable`), nothing breaks. */\nexport const NO_FISCAL_CONTEXT: Readonly<FiscalContext> = Object.freeze({ available: false, reason: 'unreachable' as const, region: null, invoicing: null })\n\nconst FAILURE_TTL_MS = 5_000\nconst cache = new WeakMap<object, { at: number; ok: boolean; value: FiscalContext }>()\n\nexport type LoadFiscalContextOptions = {\n /** How long a good answer is reused per client (default 60 s). `0` disables the cache. */\n ttlMs?: number\n signal?: AbortSignal\n now?: () => number\n}\n\n/**\n * Asks the store for its region and what invoicing it offers, with a short cache per client. It NEVER throws: when OPS\n * does not answer (older server, outage, token without scope) it returns `NO_FISCAL_CONTEXT` and retries after a few\n * seconds, so a checkout without invoicing keeps working. `reason` says which of the cases it is. Use it as the loader:\n * `loader = () => loadFiscalContext(sf)`.\n */\nexport async function loadFiscalContext(sf: StorefrontClient, options: LoadFiscalContextOptions = {}): Promise<FiscalContext> {\n const now = options.now ?? Date.now\n const ttl = options.ttlMs ?? 60_000\n const hit = cache.get(sf)\n if (hit && now() - hit.at < (hit.ok ? ttl : FAILURE_TTL_MS)) return hit.value\n const opt = options.signal ? { signal: options.signal } : undefined\n const [region, caps] = await Promise.allSettled([sf.region(opt), sf.customer.capabilities(opt)])\n const reg = region.status === 'fulfilled' ? region.value : null\n const noRegion = region.status === 'rejected' && isStorefrontError(region.reason) && region.reason.status === 404\n const invoicing = caps.status === 'fulfilled' ? (caps.value?.invoicing ?? null) : null\n const ok = (region.status === 'fulfilled' || noRegion) && caps.status === 'fulfilled'\n const available = !!reg?.fiscal_id && !!invoicing?.available\n const reason: FiscalReason = available\n ? 'available'\n : !ok\n ? 'unreachable'\n : noRegion || !reg?.fiscal_id\n ? invoicing && !invoicing.available\n ? 'off'\n : 'no_region'\n : !invoicing\n ? 'unknown'\n : 'off'\n const value: FiscalContext = { available, reason, region: reg, invoicing: invoicing?.available ? invoicing : null }\n cache.set(sf, { at: now(), ok, value: ok || available ? value : NO_FISCAL_CONTEXT })\n return ok || available ? value : NO_FISCAL_CONTEXT\n}\n\n// ---- action ----------------------------------------------------------------------------------------------------------\n\n/** What the action needs of a cookie to read the buyer's session. React Router's `createCookie(...)` fits as it is. */\nexport type SessionCookie = { parse(header: string | null): unknown }\n\nexport type CreateInvoiceActionOptions = {\n storefront: StorefrontClient\n /** The store's cookie for the buyer session token (`createCookie('sf_customer', ...)`), to know who is asking. */\n customerCookie?: SessionCookie\n /** Alternative to `customerCookie`: returns the buyer's session token, or `null` for a guest. */\n readCustomerToken?: (request: Request) => string | null | undefined | Promise<string | null | undefined>\n /** Shopper IP for the per-shopper rate limit (see `clientIp` of the client). */\n clientIp?: (request: Request) => string | null | undefined\n /** Region + capabilities source, for tests or a shared cache (default `loadFiscalContext`). */\n context?: (sf: StorefrontClient) => Promise<FiscalContext>\n /** Limits of the no-JS upload (defaults: 5 MiB, 6 pages, 3 s). The region's own limits can only lower them. */\n limits?: Partial<UploadLimits>\n /** Clock, for the freshness check. */\n now?: () => Date\n /** Rejects cross-site posts (default `true`): `Origin` and `Sec-Fetch-Site` must match the site. */\n sameSiteOnly?: boolean\n /** Prefix of the form fields (default `invoice`). */\n namePrefix?: string\n}\n\nconst EVIDENCE_SOURCES = new Set(['pdf_client', 'pdf_server', 'qr', 'manual'])\n\n/** Reads the evidence the reader put in the form, keeping only the known keys (a form is untrusted input). */\nexport function parseEvidence(raw: unknown): FiscalDocumentEvidence | undefined {\n if (typeof raw !== 'string' || !raw || raw.length > 4096) return undefined\n let o: unknown\n try {\n o = JSON.parse(raw)\n } catch {\n return undefined\n }\n if (typeof o !== 'object' || o === null) return undefined\n const e = o as Record<string, unknown>\n if (typeof e.kind !== 'string' || !e.kind || typeof e.source !== 'string' || !EVIDENCE_SOURCES.has(e.source)) return undefined\n const str = (v: unknown, max: number) => (typeof v === 'string' && v.length <= max ? v : null)\n return {\n kind: e.kind.slice(0, 64),\n document_id: str(e.document_id, 64),\n issued_on: str(e.issued_on, 32),\n sha256: typeof e.sha256 === 'string' && /^[0-9a-f]{64}$/.test(e.sha256) ? e.sha256 : null,\n ...(typeof e.extractor === 'string' ? { extractor: e.extractor.slice(0, 64) } : {}),\n source: e.source as FiscalDocumentEvidence['source'],\n qr_match: typeof e.qr_match === 'boolean' ? e.qr_match : null,\n }\n}\n\n/** Reads the `invoice.*` fields of a form into the API's `InvoiceRequest`. */\nexport function invoiceFromForm(form: FormData, prefix = 'invoice'): InvoiceRequest {\n const text = (k: string) => {\n const v = form.get(`${prefix}.${k}`)\n return typeof v === 'string' ? v.trim() : ''\n }\n const evidence = parseEvidence(form.get(`${prefix}.document`))\n const out: InvoiceRequest = {\n requested: true,\n tax_id: normalizeTaxId(text('tax_id')),\n legal_name: text('legal_name'),\n tax_regime: text('tax_regime'),\n use: text('use'),\n postal_code: text('postal_code'),\n }\n if (evidence) out.document = evidence\n if (form.get(`${prefix}.save`)) out.save = true\n return out\n}\n\nconst fail = (intent: FiscalIntent | undefined, code: FiscalErrorCode, extra: Partial<FiscalActionData> = {}): FiscalActionData => ({\n ok: false,\n ...(intent ? { intent } : {}),\n ...extra,\n error: { code, ...(extra.error?.field ? { field: extra.error.field } : {}), ...(extra.error?.blocked ? { blocked: extra.error.blocked } : {}) },\n})\n\nconst LOG_EVERY_MS = 60_000\nlet lastLogAt = -Infinity\nfunction logMisconfigured(scopes: readonly string[]): void {\n const t = Date.now()\n if (t - lastLogAt < LOG_EVERY_MS) return\n lastLogAt = t\n console.error(\n `[@asteby/storefront-react] La credencial de la tienda no tiene permisos${scopes.length ? ` (${scopes.join(', ')})` : ''}; ` +\n `la facturación no funciona hasta repararlo en ${repairPath()}.`,\n )\n}\n\n/**\n * The form control a server field belongs to. The server names a field by what it validated (`fiscal.invoice_use`,\n * `invoice.requested`, an alias like `razon_social`), the form names the control (`invoice.use`): a field that maps to no\n * control returns `undefined` so it is NOT reported as a marked field (the buyer would see \"check the marked fields\" with\n * nothing marked).\n */\nconst CONTROL_OF: Record<string, string> = {\n tax_id: 'tax_id',\n rfc: 'tax_id',\n legal_name: 'legal_name',\n razon_social: 'legal_name',\n postal_code: 'postal_code',\n zip: 'postal_code',\n cp: 'postal_code',\n tax_regime: 'tax_regime',\n regimen: 'tax_regime',\n use: 'use',\n invoice_use: 'use',\n cfdi_use: 'use',\n uso_cfdi: 'use',\n}\n\n/** `fiscal.tax_regime` (profile) and `invoice.tax_regime` (cart, order) both name the same form control. */\nexport const formField = (field: string | undefined, prefix: string) => {\n if (!field) return undefined\n const control = CONTROL_OF[field.replace(/^(fiscal|invoice)\\./, '')]\n return control ? `${prefix}.${control}` : undefined\n}\n\n/**\n * The body of `request-invoice`: only the fiscal values, with the names every server version accepts (`invoice_use`, not\n * `use`; no `requested` flag, an order that is being invoiced is requested by definition; no evidence object, the order\n * keeps the data it was validated with). An OPS that predates contract 1.9 answered 400 `requested: unknown field` to the\n * full `InvoiceRequest` the checkout sends.\n */\nexport function orderInvoiceBody(invoice: InvoiceRequest): InvoiceRequest {\n const { requested: _requested, document: _document, use, ...rest } = invoice\n return { ...rest, ...(use ? { invoice_use: use } : {}) }\n}\n\nfunction toFailure(err: unknown, intent: FiscalIntent, prefix: string, values?: Partial<FiscalActionData['values']>): FiscalActionData {\n const carry: Partial<FiscalActionData> = values ? { values } : {}\n if (!isStorefrontError(err)) return fail(intent, 'unavailable', carry)\n if (err.code === 'insufficient_scope') {\n logMisconfigured(err.requiredScopes)\n return fail(intent, 'store_misconfigured', carry)\n }\n const details = (err.details ?? {}) as { reason?: unknown; blocked_reason?: unknown }\n switch (err.status) {\n case 401:\n return fail(intent, 'unauthorized', carry)\n case 404:\n return fail(intent, 'not_found', carry)\n case 409: {\n const why = typeof details.reason === 'string' ? details.reason : typeof details.blocked_reason === 'string' ? details.blocked_reason : ''\n return fail(intent, why === 'already_checked_out' ? 'not_found' : 'blocked', { ...carry, ...(why ? { error: { code: 'blocked', blocked: why } } : {}) })\n }\n case 422:\n case 400: {\n const field = formField(err.field, prefix)\n return fail(intent, 'validation', {\n ...carry,\n ...(field ? { fieldErrors: { [field]: err.message || 'invalid' }, error: { code: 'validation', field } } : {}),\n })\n }\n case 429:\n return fail(intent, 'rate_limited', carry)\n case 403:\n return fail(intent, 'unsupported', carry)\n default:\n return fail(intent, 'unavailable', carry)\n }\n}\n\nfunction sameSite(request: Request): boolean {\n const site = request.headers.get('sec-fetch-site')\n if (site && site !== 'same-origin' && site !== 'same-site' && site !== 'none') return false\n const origin = request.headers.get('origin')\n if (!origin) return true\n try {\n const host = request.headers.get('x-forwarded-host') ?? request.headers.get('host') ?? new URL(request.url).host\n return new URL(origin).host === host.split(',')[0]!.trim()\n } catch {\n return false\n }\n}\n\n/**\n * The invoicing action: one `POST` form with an `intent` field.\n *\n * | intent | fields | does |\n * | ------------------ | --------------------------------------------------- | ---------------------------------------------------------- |\n * | `set-invoice` | cart_id, invoice.* (`invoice.want` unset withdraws) | `carts.setInvoice`: validates against the region BEFORE paying |\n * | `save-profile` | invoice.*, consent | `customer.update({ fiscal })`, needs a session and consent |\n * | `request-invoice` | order_id, invoice.* | `customer.requestInvoice` (invoice after the purchase) |\n * | `clear-profile` | - | `customer.update({ fiscal: null })` (the buyer's right to delete) |\n * | `extract-document` | document (file), document_kind? | reads the file in memory, discards it, `step: 'review'` |\n *\n * It never redirects: it returns a `FiscalActionData` for the page to render (`<InvoiceToggle data={...}>`), with 422s\n * mapped to the field (`fieldErrors['invoice.tax_regime']`). The tax id of the buyer is never logged.\n */\nexport function createInvoiceAction(options: CreateInvoiceActionOptions) {\n const { storefront: sf } = options\n const prefix = options.namePrefix ?? 'invoice'\n const loadContext = options.context ?? ((c: StorefrontClient) => loadFiscalContext(c))\n const limits: UploadLimits = { ...UPLOAD_LIMITS, ...options.limits }\n\n async function sessionToken(request: Request): Promise<string | null> {\n if (options.readCustomerToken) return (await options.readCustomerToken(request)) || null\n if (!options.customerCookie) return null\n const v = await options.customerCookie.parse(request.headers.get('cookie'))\n return typeof v === 'string' && v ? v : null\n }\n\n return async function invoiceAction({ request }: { request: Request }): Promise<FiscalActionData> {\n if (options.sameSiteOnly !== false && !sameSite(request)) return fail(undefined, 'unsupported')\n // A file bigger than the limit is refused before it is buffered.\n const declared = Number(request.headers.get('content-length') ?? 0)\n if (declared > limits.maxBytes + 256 * 1024) return fail('extract-document', 'too_large')\n const form = await request.formData().catch(() => null)\n if (!form) return fail(undefined, 'validation')\n const intent = form.get('intent') as FiscalIntent\n const ip = options.clientIp?.(request)\n const token = await sessionToken(request)\n const w = { ...(ip ? { clientIp: ip } : {}), ...(token ? { customerToken: token } : {}) }\n const invoice = invoiceFromForm(form, prefix)\n const echo = { tax_id: invoice.tax_id ?? '', legal_name: invoice.legal_name ?? '', postal_code: invoice.postal_code ?? '', tax_regime: invoice.tax_regime ?? '', use: invoice.use ?? '' }\n const text = (name: string) => {\n const v = form.get(name)\n return typeof v === 'string' ? v.trim() : ''\n }\n\n try {\n switch (intent) {\n case 'set-invoice': {\n const cartId = text('cart_id')\n if (!cartId) return fail(intent, 'validation')\n if (!form.get(`${prefix}.want`) && !form.get(`${prefix}.requested`)) {\n await sf.carts.setInvoice(cartId, null, w)\n return { ok: true, intent, step: 'cleared' }\n }\n await sf.carts.setInvoice(cartId, invoice, w)\n return { ok: true, intent, step: 'saved', values: echo }\n }\n case 'save-profile': {\n if (!token) return fail(intent, 'unauthorized', { values: echo })\n if (!form.get('consent')) return fail(intent, 'consent_required', { values: echo })\n const fiscal: FiscalIdentityInput = {\n tax_id: invoice.tax_id ?? '',\n legal_name: invoice.legal_name ?? '',\n tax_regime: invoice.tax_regime ?? '',\n postal_code: invoice.postal_code ?? '',\n invoice_use: invoice.use ?? '',\n ...(invoice.document ? { document: invoice.document } : {}),\n }\n await sf.customer.update({ fiscal }, w)\n return { ok: true, intent, step: 'saved', values: echo }\n }\n case 'clear-profile': {\n if (!token) return fail(intent, 'unauthorized')\n await sf.customer.update({ fiscal: null }, w)\n return { ok: true, intent, step: 'cleared' }\n }\n case 'request-invoice': {\n if (!token) return fail(intent, 'unauthorized', { values: echo })\n const orderId = text('order_id')\n if (!orderId) return fail(intent, 'validation')\n const result = await sf.customer.requestInvoice(orderId, orderInvoiceBody(invoice), w)\n return { ok: true, intent, step: result.status === 'issued' ? 'issued' : 'pending', result, ...(result.invoice ? { invoice: result.invoice } : {}) }\n }\n case 'extract-document': {\n const ctx = await loadContext(sf)\n const region = ctx.region\n const kind = text('document_kind')\n const specs = region?.fiscal_id?.documents ?? []\n const spec = (kind ? specs.find((d) => d.kind === kind) : specs[0]) ?? undefined\n if (!ctx.available || !region || !spec) return fail(intent, 'unsupported')\n const file = form.get('document')\n if (!file || typeof file === 'string' || file.size === 0) return fail(intent, 'not_pdf')\n try {\n const extraction = await extractUploadedDocument(file, spec, region, { limits, ...(options.now ? { now: options.now() } : {}) })\n return { ok: true, intent, step: 'review', extraction }\n } catch (err) {\n const code = (err as { code?: string } | null)?.code\n const known: FiscalErrorCode[] = ['not_pdf', 'too_large', 'no_text', 'password', 'unreadable']\n // Never log the error: it can carry text from the document.\n return fail(intent, known.includes(code as FiscalErrorCode) ? (code as FiscalErrorCode) : 'unreadable')\n }\n }\n default:\n return fail(undefined, 'validation')\n }\n } catch (err) {\n return toFailure(err, intent, prefix, intent === 'clear-profile' ? undefined : echo)\n }\n }\n}\n\nexport type InvoiceAction = ReturnType<typeof createInvoiceAction>\n"]}
@@ -78,6 +78,15 @@ type CreateInvoiceActionOptions = {
78
78
  declare function parseEvidence(raw: unknown): FiscalDocumentEvidence | undefined;
79
79
  /** Reads the `invoice.*` fields of a form into the API's `InvoiceRequest`. */
80
80
  declare function invoiceFromForm(form: FormData, prefix?: string): InvoiceRequest;
81
+ /** `fiscal.tax_regime` (profile) and `invoice.tax_regime` (cart, order) both name the same form control. */
82
+ declare const formField: (field: string | undefined, prefix: string) => string | undefined;
83
+ /**
84
+ * The body of `request-invoice`: only the fiscal values, with the names every server version accepts (`invoice_use`, not
85
+ * `use`; no `requested` flag, an order that is being invoiced is requested by definition; no evidence object, the order
86
+ * keeps the data it was validated with). An OPS that predates contract 1.9 answered 400 `requested: unknown field` to the
87
+ * full `InvoiceRequest` the checkout sends.
88
+ */
89
+ declare function orderInvoiceBody(invoice: InvoiceRequest): InvoiceRequest;
81
90
  /**
82
91
  * The invoicing action: one `POST` form with an `intent` field.
83
92
  *
@@ -97,4 +106,4 @@ declare function createInvoiceAction(options: CreateInvoiceActionOptions): ({ re
97
106
  }) => Promise<FiscalActionData>;
98
107
  type InvoiceAction = ReturnType<typeof createInvoiceAction>;
99
108
 
100
- export { type CreateInvoiceActionOptions, FiscalActionData, type FiscalContext, FiscalReason, type InvoiceAction, type LoadFiscalContextOptions, NO_FISCAL_CONTEXT, type SessionCookie, UPLOAD_LIMITS, type UploadLimits, createInvoiceAction, extractUploadedDocument, invoiceFromForm, loadFiscalContext, parseEvidence };
109
+ export { type CreateInvoiceActionOptions, FiscalActionData, type FiscalContext, FiscalReason, type InvoiceAction, type LoadFiscalContextOptions, NO_FISCAL_CONTEXT, type SessionCookie, UPLOAD_LIMITS, type UploadLimits, createInvoiceAction, extractUploadedDocument, formField, invoiceFromForm, loadFiscalContext, orderInvoiceBody, parseEvidence };
@@ -78,6 +78,15 @@ type CreateInvoiceActionOptions = {
78
78
  declare function parseEvidence(raw: unknown): FiscalDocumentEvidence | undefined;
79
79
  /** Reads the `invoice.*` fields of a form into the API's `InvoiceRequest`. */
80
80
  declare function invoiceFromForm(form: FormData, prefix?: string): InvoiceRequest;
81
+ /** `fiscal.tax_regime` (profile) and `invoice.tax_regime` (cart, order) both name the same form control. */
82
+ declare const formField: (field: string | undefined, prefix: string) => string | undefined;
83
+ /**
84
+ * The body of `request-invoice`: only the fiscal values, with the names every server version accepts (`invoice_use`, not
85
+ * `use`; no `requested` flag, an order that is being invoiced is requested by definition; no evidence object, the order
86
+ * keeps the data it was validated with). An OPS that predates contract 1.9 answered 400 `requested: unknown field` to the
87
+ * full `InvoiceRequest` the checkout sends.
88
+ */
89
+ declare function orderInvoiceBody(invoice: InvoiceRequest): InvoiceRequest;
81
90
  /**
82
91
  * The invoicing action: one `POST` form with an `intent` field.
83
92
  *
@@ -97,4 +106,4 @@ declare function createInvoiceAction(options: CreateInvoiceActionOptions): ({ re
97
106
  }) => Promise<FiscalActionData>;
98
107
  type InvoiceAction = ReturnType<typeof createInvoiceAction>;
99
108
 
100
- export { type CreateInvoiceActionOptions, FiscalActionData, type FiscalContext, FiscalReason, type InvoiceAction, type LoadFiscalContextOptions, NO_FISCAL_CONTEXT, type SessionCookie, UPLOAD_LIMITS, type UploadLimits, createInvoiceAction, extractUploadedDocument, invoiceFromForm, loadFiscalContext, parseEvidence };
109
+ export { type CreateInvoiceActionOptions, FiscalActionData, type FiscalContext, FiscalReason, type InvoiceAction, type LoadFiscalContextOptions, NO_FISCAL_CONTEXT, type SessionCookie, UPLOAD_LIMITS, type UploadLimits, createInvoiceAction, extractUploadedDocument, formField, invoiceFromForm, loadFiscalContext, orderInvoiceBody, parseEvidence };
@@ -97,7 +97,30 @@ function logMisconfigured(scopes) {
97
97
  `[@asteby/storefront-react] La credencial de la tienda no tiene permisos${scopes.length ? ` (${scopes.join(", ")})` : ""}; la facturaci\xF3n no funciona hasta repararlo en ${repairPath()}.`
98
98
  );
99
99
  }
100
- var formField = (field, prefix) => field ? field.replace(/^(fiscal|invoice)\./, `${prefix}.`) : void 0;
100
+ var CONTROL_OF = {
101
+ tax_id: "tax_id",
102
+ rfc: "tax_id",
103
+ legal_name: "legal_name",
104
+ razon_social: "legal_name",
105
+ postal_code: "postal_code",
106
+ zip: "postal_code",
107
+ cp: "postal_code",
108
+ tax_regime: "tax_regime",
109
+ regimen: "tax_regime",
110
+ use: "use",
111
+ invoice_use: "use",
112
+ cfdi_use: "use",
113
+ uso_cfdi: "use"
114
+ };
115
+ var formField = (field, prefix) => {
116
+ if (!field) return void 0;
117
+ const control = CONTROL_OF[field.replace(/^(fiscal|invoice)\./, "")];
118
+ return control ? `${prefix}.${control}` : void 0;
119
+ };
120
+ function orderInvoiceBody(invoice) {
121
+ const { requested: _requested, document: _document, use, ...rest } = invoice;
122
+ return { ...rest, ...use ? { invoice_use: use } : {} };
123
+ }
101
124
  function toFailure(err, intent, prefix, values) {
102
125
  const carry = values ? { values } : {};
103
126
  if (!isStorefrontError(err)) return fail(intent, "unavailable", carry);
@@ -205,7 +228,7 @@ function createInvoiceAction(options) {
205
228
  if (!token) return fail(intent, "unauthorized", { values: echo });
206
229
  const orderId = text("order_id");
207
230
  if (!orderId) return fail(intent, "validation");
208
- const result = await sf.customer.requestInvoice(orderId, invoice, w);
231
+ const result = await sf.customer.requestInvoice(orderId, orderInvoiceBody(invoice), w);
209
232
  return { ok: true, intent, step: result.status === "issued" ? "issued" : "pending", result, ...result.invoice ? { invoice: result.invoice } : {} };
210
233
  }
211
234
  case "extract-document": {
@@ -235,6 +258,6 @@ function createInvoiceAction(options) {
235
258
  };
236
259
  }
237
260
 
238
- export { NO_FISCAL_CONTEXT, UPLOAD_LIMITS, createInvoiceAction, extractUploadedDocument, invoiceFromForm, loadFiscalContext, parseEvidence };
261
+ export { NO_FISCAL_CONTEXT, UPLOAD_LIMITS, createInvoiceAction, extractUploadedDocument, formField, invoiceFromForm, loadFiscalContext, orderInvoiceBody, parseEvidence };
239
262
  //# sourceMappingURL=fiscal-server.js.map
240
263
  //# sourceMappingURL=fiscal-server.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/fiscal/server-extract.ts","../src/fiscal/server.ts"],"names":[],"mappings":"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","file":"fiscal-server.js","sourcesContent":["/**\n * Reads a tax document uploaded without JavaScript, on the server, with the same engine the browser uses. In memory\n * only: the bytes are never written to disk, never logged and are dropped when this returns.\n */\nimport type { IdentityDocumentSpec, Region } from '@asteby/storefront'\nimport { IdentityDocumentError, buildDocumentEvidence, catalogsFromRegion, extractIdentityDocument, readPdfText } from '@asteby/storefront/fiscal-document'\nimport { summarizeExtraction } from './extraction.js'\nimport type { FiscalExtractionSummary } from './types.js'\n\nexport type UploadLimits = { maxBytes: number; maxPages: number; timeoutMs: number }\n\n/** 5 MiB, 6 pages, 3 s: the same as the browser reader. */\nexport const UPLOAD_LIMITS: Readonly<UploadLimits> = Object.freeze({ maxBytes: 5 * 1024 * 1024, maxPages: 6, timeoutMs: 3000 })\n\nconst PDF_MAGIC = [0x25, 0x50, 0x44, 0x46, 0x2d] // %PDF-\n\nexport async function extractUploadedDocument(\n file: Blob,\n spec: IdentityDocumentSpec,\n region: Pick<Region, 'fiscal_id'>,\n options: { limits?: Partial<UploadLimits>; now?: Date } = {},\n): Promise<FiscalExtractionSummary> {\n const lim = { ...UPLOAD_LIMITS, ...options.limits }\n // The region can only lower the limits.\n const maxBytes = Math.min(lim.maxBytes, spec.max_bytes || lim.maxBytes)\n const maxPages = Math.min(lim.maxPages, spec.max_pages || lim.maxPages)\n if (file.size > maxBytes) throw new IdentityDocumentError('too_large', 'file over the size limit', { reason: 'bytes' })\n const bytes = new Uint8Array(await file.arrayBuffer())\n if (!PDF_MAGIC.every((b, i) => bytes[i] === b)) throw new IdentityDocumentError('not_pdf', 'not a PDF')\n // The engine is the only reader. QR cross-check needs a canvas, which a server does not have: `qr_match` stays null.\n const { text, sha256 } = await readPdfText(bytes, { maxBytes, maxPages, timeoutMs: lim.timeoutMs })\n const x = extractIdentityDocument(text, spec, {\n catalogs: region.fiscal_id ? catalogsFromRegion(region.fiscal_id) : {},\n ...(options.now ? { now: options.now } : {}),\n })\n return summarizeExtraction(x, buildDocumentEvidence(x, { sha256, source: 'pdf_server' }), region.fiscal_id?.regimes.map((r) => r.code))\n}\n","/**\n * Server side of the fiscal components (`@asteby/storefront-react/fiscal/server`): `loadFiscalContext` for the loader and\n * `createInvoiceAction` for the action. No React, no router: Web `Request`/`Response` only, so any framework mounts it.\n *\n * The browser never talks to OPS. The action uses the store's `sk_` client and the buyer's session token (read from the\n * store's cookie). A tax document uploaded WITHOUT JavaScript is read in memory here with the same engine the browser\n * uses, and discarded: no disk, no logs, no copy.\n */\nimport {\n isStorefrontError,\n repairPath,\n type FiscalDocumentEvidence,\n type FiscalIdentityInput,\n type InvoiceRequest,\n type InvoicingCapabilities,\n type Region,\n type StorefrontClient,\n} from '@asteby/storefront'\nimport { normalizeTaxId } from './format.js'\nimport { extractUploadedDocument, UPLOAD_LIMITS, type UploadLimits } from './server-extract.js'\nimport type { FiscalActionData, FiscalErrorCode, FiscalIntent, FiscalReason } from './types.js'\n\nexport type { FiscalActionData, FiscalErrorCode, FiscalIntent, FiscalReason } from './types.js'\nexport { UPLOAD_LIMITS, extractUploadedDocument } from './server-extract.js'\nexport type { UploadLimits } from './server-extract.js'\n\n// ---- context ---------------------------------------------------------------------------------------------------------\n\nexport type FiscalContext = {\n /** The store can invoice: it has a region with a fiscal catalog and `capabilities.invoicing.available`. */\n available: boolean\n /** Why `available` is what it is. Absent on contexts built by older code: read it as `unknown`. */\n reason?: FiscalReason\n region: Region | null\n invoicing: InvoicingCapabilities | null\n}\n\n/** What is assumed when the store cannot be asked: nothing is known (`unreachable`), nothing breaks. */\nexport const NO_FISCAL_CONTEXT: Readonly<FiscalContext> = Object.freeze({ available: false, reason: 'unreachable' as const, region: null, invoicing: null })\n\nconst FAILURE_TTL_MS = 5_000\nconst cache = new WeakMap<object, { at: number; ok: boolean; value: FiscalContext }>()\n\nexport type LoadFiscalContextOptions = {\n /** How long a good answer is reused per client (default 60 s). `0` disables the cache. */\n ttlMs?: number\n signal?: AbortSignal\n now?: () => number\n}\n\n/**\n * Asks the store for its region and what invoicing it offers, with a short cache per client. It NEVER throws: when OPS\n * does not answer (older server, outage, token without scope) it returns `NO_FISCAL_CONTEXT` and retries after a few\n * seconds, so a checkout without invoicing keeps working. `reason` says which of the cases it is. Use it as the loader:\n * `loader = () => loadFiscalContext(sf)`.\n */\nexport async function loadFiscalContext(sf: StorefrontClient, options: LoadFiscalContextOptions = {}): Promise<FiscalContext> {\n const now = options.now ?? Date.now\n const ttl = options.ttlMs ?? 60_000\n const hit = cache.get(sf)\n if (hit && now() - hit.at < (hit.ok ? ttl : FAILURE_TTL_MS)) return hit.value\n const opt = options.signal ? { signal: options.signal } : undefined\n const [region, caps] = await Promise.allSettled([sf.region(opt), sf.customer.capabilities(opt)])\n const reg = region.status === 'fulfilled' ? region.value : null\n const noRegion = region.status === 'rejected' && isStorefrontError(region.reason) && region.reason.status === 404\n const invoicing = caps.status === 'fulfilled' ? (caps.value?.invoicing ?? null) : null\n const ok = (region.status === 'fulfilled' || noRegion) && caps.status === 'fulfilled'\n const available = !!reg?.fiscal_id && !!invoicing?.available\n const reason: FiscalReason = available\n ? 'available'\n : !ok\n ? 'unreachable'\n : noRegion || !reg?.fiscal_id\n ? invoicing && !invoicing.available\n ? 'off'\n : 'no_region'\n : !invoicing\n ? 'unknown'\n : 'off'\n const value: FiscalContext = { available, reason, region: reg, invoicing: invoicing?.available ? invoicing : null }\n cache.set(sf, { at: now(), ok, value: ok || available ? value : NO_FISCAL_CONTEXT })\n return ok || available ? value : NO_FISCAL_CONTEXT\n}\n\n// ---- action ----------------------------------------------------------------------------------------------------------\n\n/** What the action needs of a cookie to read the buyer's session. React Router's `createCookie(...)` fits as it is. */\nexport type SessionCookie = { parse(header: string | null): unknown }\n\nexport type CreateInvoiceActionOptions = {\n storefront: StorefrontClient\n /** The store's cookie for the buyer session token (`createCookie('sf_customer', ...)`), to know who is asking. */\n customerCookie?: SessionCookie\n /** Alternative to `customerCookie`: returns the buyer's session token, or `null` for a guest. */\n readCustomerToken?: (request: Request) => string | null | undefined | Promise<string | null | undefined>\n /** Shopper IP for the per-shopper rate limit (see `clientIp` of the client). */\n clientIp?: (request: Request) => string | null | undefined\n /** Region + capabilities source, for tests or a shared cache (default `loadFiscalContext`). */\n context?: (sf: StorefrontClient) => Promise<FiscalContext>\n /** Limits of the no-JS upload (defaults: 5 MiB, 6 pages, 3 s). The region's own limits can only lower them. */\n limits?: Partial<UploadLimits>\n /** Clock, for the freshness check. */\n now?: () => Date\n /** Rejects cross-site posts (default `true`): `Origin` and `Sec-Fetch-Site` must match the site. */\n sameSiteOnly?: boolean\n /** Prefix of the form fields (default `invoice`). */\n namePrefix?: string\n}\n\nconst EVIDENCE_SOURCES = new Set(['pdf_client', 'pdf_server', 'qr', 'manual'])\n\n/** Reads the evidence the reader put in the form, keeping only the known keys (a form is untrusted input). */\nexport function parseEvidence(raw: unknown): FiscalDocumentEvidence | undefined {\n if (typeof raw !== 'string' || !raw || raw.length > 4096) return undefined\n let o: unknown\n try {\n o = JSON.parse(raw)\n } catch {\n return undefined\n }\n if (typeof o !== 'object' || o === null) return undefined\n const e = o as Record<string, unknown>\n if (typeof e.kind !== 'string' || !e.kind || typeof e.source !== 'string' || !EVIDENCE_SOURCES.has(e.source)) return undefined\n const str = (v: unknown, max: number) => (typeof v === 'string' && v.length <= max ? v : null)\n return {\n kind: e.kind.slice(0, 64),\n document_id: str(e.document_id, 64),\n issued_on: str(e.issued_on, 32),\n sha256: typeof e.sha256 === 'string' && /^[0-9a-f]{64}$/.test(e.sha256) ? e.sha256 : null,\n ...(typeof e.extractor === 'string' ? { extractor: e.extractor.slice(0, 64) } : {}),\n source: e.source as FiscalDocumentEvidence['source'],\n qr_match: typeof e.qr_match === 'boolean' ? e.qr_match : null,\n }\n}\n\n/** Reads the `invoice.*` fields of a form into the API's `InvoiceRequest`. */\nexport function invoiceFromForm(form: FormData, prefix = 'invoice'): InvoiceRequest {\n const text = (k: string) => {\n const v = form.get(`${prefix}.${k}`)\n return typeof v === 'string' ? v.trim() : ''\n }\n const evidence = parseEvidence(form.get(`${prefix}.document`))\n const out: InvoiceRequest = {\n requested: true,\n tax_id: normalizeTaxId(text('tax_id')),\n legal_name: text('legal_name'),\n tax_regime: text('tax_regime'),\n use: text('use'),\n postal_code: text('postal_code'),\n }\n if (evidence) out.document = evidence\n if (form.get(`${prefix}.save`)) out.save = true\n return out\n}\n\nconst fail = (intent: FiscalIntent | undefined, code: FiscalErrorCode, extra: Partial<FiscalActionData> = {}): FiscalActionData => ({\n ok: false,\n ...(intent ? { intent } : {}),\n ...extra,\n error: { code, ...(extra.error?.field ? { field: extra.error.field } : {}), ...(extra.error?.blocked ? { blocked: extra.error.blocked } : {}) },\n})\n\nconst LOG_EVERY_MS = 60_000\nlet lastLogAt = -Infinity\nfunction logMisconfigured(scopes: readonly string[]): void {\n const t = Date.now()\n if (t - lastLogAt < LOG_EVERY_MS) return\n lastLogAt = t\n console.error(\n `[@asteby/storefront-react] La credencial de la tienda no tiene permisos${scopes.length ? ` (${scopes.join(', ')})` : ''}; ` +\n `la facturación no funciona hasta repararlo en ${repairPath()}.`,\n )\n}\n\n/** `fiscal.tax_regime` (profile) and `invoice.tax_regime` (cart, order) both name the same form control. */\nconst formField = (field: string | undefined, prefix: string) => (field ? field.replace(/^(fiscal|invoice)\\./, `${prefix}.`) : undefined)\n\nfunction toFailure(err: unknown, intent: FiscalIntent, prefix: string, values?: Partial<FiscalActionData['values']>): FiscalActionData {\n const carry: Partial<FiscalActionData> = values ? { values } : {}\n if (!isStorefrontError(err)) return fail(intent, 'unavailable', carry)\n if (err.code === 'insufficient_scope') {\n logMisconfigured(err.requiredScopes)\n return fail(intent, 'store_misconfigured', carry)\n }\n const details = (err.details ?? {}) as { reason?: unknown; blocked_reason?: unknown }\n switch (err.status) {\n case 401:\n return fail(intent, 'unauthorized', carry)\n case 404:\n return fail(intent, 'not_found', carry)\n case 409: {\n const why = typeof details.reason === 'string' ? details.reason : typeof details.blocked_reason === 'string' ? details.blocked_reason : ''\n return fail(intent, why === 'already_checked_out' ? 'not_found' : 'blocked', { ...carry, ...(why ? { error: { code: 'blocked', blocked: why } } : {}) })\n }\n case 422:\n case 400: {\n const field = formField(err.field, prefix)\n return fail(intent, 'validation', {\n ...carry,\n ...(field ? { fieldErrors: { [field]: err.message || 'invalid' }, error: { code: 'validation', field } } : {}),\n })\n }\n case 429:\n return fail(intent, 'rate_limited', carry)\n case 403:\n return fail(intent, 'unsupported', carry)\n default:\n return fail(intent, 'unavailable', carry)\n }\n}\n\nfunction sameSite(request: Request): boolean {\n const site = request.headers.get('sec-fetch-site')\n if (site && site !== 'same-origin' && site !== 'same-site' && site !== 'none') return false\n const origin = request.headers.get('origin')\n if (!origin) return true\n try {\n const host = request.headers.get('x-forwarded-host') ?? request.headers.get('host') ?? new URL(request.url).host\n return new URL(origin).host === host.split(',')[0]!.trim()\n } catch {\n return false\n }\n}\n\n/**\n * The invoicing action: one `POST` form with an `intent` field.\n *\n * | intent | fields | does |\n * | ------------------ | --------------------------------------------------- | ---------------------------------------------------------- |\n * | `set-invoice` | cart_id, invoice.* (`invoice.want` unset withdraws) | `carts.setInvoice`: validates against the region BEFORE paying |\n * | `save-profile` | invoice.*, consent | `customer.update({ fiscal })`, needs a session and consent |\n * | `request-invoice` | order_id, invoice.* | `customer.requestInvoice` (invoice after the purchase) |\n * | `clear-profile` | - | `customer.update({ fiscal: null })` (the buyer's right to delete) |\n * | `extract-document` | document (file), document_kind? | reads the file in memory, discards it, `step: 'review'` |\n *\n * It never redirects: it returns a `FiscalActionData` for the page to render (`<InvoiceToggle data={...}>`), with 422s\n * mapped to the field (`fieldErrors['invoice.tax_regime']`). The tax id of the buyer is never logged.\n */\nexport function createInvoiceAction(options: CreateInvoiceActionOptions) {\n const { storefront: sf } = options\n const prefix = options.namePrefix ?? 'invoice'\n const loadContext = options.context ?? ((c: StorefrontClient) => loadFiscalContext(c))\n const limits: UploadLimits = { ...UPLOAD_LIMITS, ...options.limits }\n\n async function sessionToken(request: Request): Promise<string | null> {\n if (options.readCustomerToken) return (await options.readCustomerToken(request)) || null\n if (!options.customerCookie) return null\n const v = await options.customerCookie.parse(request.headers.get('cookie'))\n return typeof v === 'string' && v ? v : null\n }\n\n return async function invoiceAction({ request }: { request: Request }): Promise<FiscalActionData> {\n if (options.sameSiteOnly !== false && !sameSite(request)) return fail(undefined, 'unsupported')\n // A file bigger than the limit is refused before it is buffered.\n const declared = Number(request.headers.get('content-length') ?? 0)\n if (declared > limits.maxBytes + 256 * 1024) return fail('extract-document', 'too_large')\n const form = await request.formData().catch(() => null)\n if (!form) return fail(undefined, 'validation')\n const intent = form.get('intent') as FiscalIntent\n const ip = options.clientIp?.(request)\n const token = await sessionToken(request)\n const w = { ...(ip ? { clientIp: ip } : {}), ...(token ? { customerToken: token } : {}) }\n const invoice = invoiceFromForm(form, prefix)\n const echo = { tax_id: invoice.tax_id ?? '', legal_name: invoice.legal_name ?? '', postal_code: invoice.postal_code ?? '', tax_regime: invoice.tax_regime ?? '', use: invoice.use ?? '' }\n const text = (name: string) => {\n const v = form.get(name)\n return typeof v === 'string' ? v.trim() : ''\n }\n\n try {\n switch (intent) {\n case 'set-invoice': {\n const cartId = text('cart_id')\n if (!cartId) return fail(intent, 'validation')\n if (!form.get(`${prefix}.want`) && !form.get(`${prefix}.requested`)) {\n await sf.carts.setInvoice(cartId, null, w)\n return { ok: true, intent, step: 'cleared' }\n }\n await sf.carts.setInvoice(cartId, invoice, w)\n return { ok: true, intent, step: 'saved', values: echo }\n }\n case 'save-profile': {\n if (!token) return fail(intent, 'unauthorized', { values: echo })\n if (!form.get('consent')) return fail(intent, 'consent_required', { values: echo })\n const fiscal: FiscalIdentityInput = {\n tax_id: invoice.tax_id ?? '',\n legal_name: invoice.legal_name ?? '',\n tax_regime: invoice.tax_regime ?? '',\n postal_code: invoice.postal_code ?? '',\n invoice_use: invoice.use ?? '',\n ...(invoice.document ? { document: invoice.document } : {}),\n }\n await sf.customer.update({ fiscal }, w)\n return { ok: true, intent, step: 'saved', values: echo }\n }\n case 'clear-profile': {\n if (!token) return fail(intent, 'unauthorized')\n await sf.customer.update({ fiscal: null }, w)\n return { ok: true, intent, step: 'cleared' }\n }\n case 'request-invoice': {\n if (!token) return fail(intent, 'unauthorized', { values: echo })\n const orderId = text('order_id')\n if (!orderId) return fail(intent, 'validation')\n const result = await sf.customer.requestInvoice(orderId, invoice, w)\n return { ok: true, intent, step: result.status === 'issued' ? 'issued' : 'pending', result, ...(result.invoice ? { invoice: result.invoice } : {}) }\n }\n case 'extract-document': {\n const ctx = await loadContext(sf)\n const region = ctx.region\n const kind = text('document_kind')\n const specs = region?.fiscal_id?.documents ?? []\n const spec = (kind ? specs.find((d) => d.kind === kind) : specs[0]) ?? undefined\n if (!ctx.available || !region || !spec) return fail(intent, 'unsupported')\n const file = form.get('document')\n if (!file || typeof file === 'string' || file.size === 0) return fail(intent, 'not_pdf')\n try {\n const extraction = await extractUploadedDocument(file, spec, region, { limits, ...(options.now ? { now: options.now() } : {}) })\n return { ok: true, intent, step: 'review', extraction }\n } catch (err) {\n const code = (err as { code?: string } | null)?.code\n const known: FiscalErrorCode[] = ['not_pdf', 'too_large', 'no_text', 'password', 'unreadable']\n // Never log the error: it can carry text from the document.\n return fail(intent, known.includes(code as FiscalErrorCode) ? (code as FiscalErrorCode) : 'unreadable')\n }\n }\n default:\n return fail(undefined, 'validation')\n }\n } catch (err) {\n return toFailure(err, intent, prefix, intent === 'clear-profile' ? undefined : echo)\n }\n }\n}\n\nexport type InvoiceAction = ReturnType<typeof createInvoiceAction>\n"]}
1
+ {"version":3,"sources":["../src/fiscal/server-extract.ts","../src/fiscal/server.ts"],"names":[],"mappings":"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","file":"fiscal-server.js","sourcesContent":["/**\n * Reads a tax document uploaded without JavaScript, on the server, with the same engine the browser uses. In memory\n * only: the bytes are never written to disk, never logged and are dropped when this returns.\n */\nimport type { IdentityDocumentSpec, Region } from '@asteby/storefront'\nimport { IdentityDocumentError, buildDocumentEvidence, catalogsFromRegion, extractIdentityDocument, readPdfText } from '@asteby/storefront/fiscal-document'\nimport { summarizeExtraction } from './extraction.js'\nimport type { FiscalExtractionSummary } from './types.js'\n\nexport type UploadLimits = { maxBytes: number; maxPages: number; timeoutMs: number }\n\n/** 5 MiB, 6 pages, 3 s: the same as the browser reader. */\nexport const UPLOAD_LIMITS: Readonly<UploadLimits> = Object.freeze({ maxBytes: 5 * 1024 * 1024, maxPages: 6, timeoutMs: 3000 })\n\nconst PDF_MAGIC = [0x25, 0x50, 0x44, 0x46, 0x2d] // %PDF-\n\nexport async function extractUploadedDocument(\n file: Blob,\n spec: IdentityDocumentSpec,\n region: Pick<Region, 'fiscal_id'>,\n options: { limits?: Partial<UploadLimits>; now?: Date } = {},\n): Promise<FiscalExtractionSummary> {\n const lim = { ...UPLOAD_LIMITS, ...options.limits }\n // The region can only lower the limits.\n const maxBytes = Math.min(lim.maxBytes, spec.max_bytes || lim.maxBytes)\n const maxPages = Math.min(lim.maxPages, spec.max_pages || lim.maxPages)\n if (file.size > maxBytes) throw new IdentityDocumentError('too_large', 'file over the size limit', { reason: 'bytes' })\n const bytes = new Uint8Array(await file.arrayBuffer())\n if (!PDF_MAGIC.every((b, i) => bytes[i] === b)) throw new IdentityDocumentError('not_pdf', 'not a PDF')\n // The engine is the only reader. QR cross-check needs a canvas, which a server does not have: `qr_match` stays null.\n const { text, sha256 } = await readPdfText(bytes, { maxBytes, maxPages, timeoutMs: lim.timeoutMs })\n const x = extractIdentityDocument(text, spec, {\n catalogs: region.fiscal_id ? catalogsFromRegion(region.fiscal_id) : {},\n ...(options.now ? { now: options.now } : {}),\n })\n return summarizeExtraction(x, buildDocumentEvidence(x, { sha256, source: 'pdf_server' }), region.fiscal_id?.regimes.map((r) => r.code))\n}\n","/**\n * Server side of the fiscal components (`@asteby/storefront-react/fiscal/server`): `loadFiscalContext` for the loader and\n * `createInvoiceAction` for the action. No React, no router: Web `Request`/`Response` only, so any framework mounts it.\n *\n * The browser never talks to OPS. The action uses the store's `sk_` client and the buyer's session token (read from the\n * store's cookie). A tax document uploaded WITHOUT JavaScript is read in memory here with the same engine the browser\n * uses, and discarded: no disk, no logs, no copy.\n */\nimport {\n isStorefrontError,\n repairPath,\n type FiscalDocumentEvidence,\n type FiscalIdentityInput,\n type InvoiceRequest,\n type InvoicingCapabilities,\n type Region,\n type StorefrontClient,\n} from '@asteby/storefront'\nimport { normalizeTaxId } from './format.js'\nimport { extractUploadedDocument, UPLOAD_LIMITS, type UploadLimits } from './server-extract.js'\nimport type { FiscalActionData, FiscalErrorCode, FiscalIntent, FiscalReason } from './types.js'\n\nexport type { FiscalActionData, FiscalErrorCode, FiscalIntent, FiscalReason } from './types.js'\nexport { UPLOAD_LIMITS, extractUploadedDocument } from './server-extract.js'\nexport type { UploadLimits } from './server-extract.js'\n\n// ---- context ---------------------------------------------------------------------------------------------------------\n\nexport type FiscalContext = {\n /** The store can invoice: it has a region with a fiscal catalog and `capabilities.invoicing.available`. */\n available: boolean\n /** Why `available` is what it is. Absent on contexts built by older code: read it as `unknown`. */\n reason?: FiscalReason\n region: Region | null\n invoicing: InvoicingCapabilities | null\n}\n\n/** What is assumed when the store cannot be asked: nothing is known (`unreachable`), nothing breaks. */\nexport const NO_FISCAL_CONTEXT: Readonly<FiscalContext> = Object.freeze({ available: false, reason: 'unreachable' as const, region: null, invoicing: null })\n\nconst FAILURE_TTL_MS = 5_000\nconst cache = new WeakMap<object, { at: number; ok: boolean; value: FiscalContext }>()\n\nexport type LoadFiscalContextOptions = {\n /** How long a good answer is reused per client (default 60 s). `0` disables the cache. */\n ttlMs?: number\n signal?: AbortSignal\n now?: () => number\n}\n\n/**\n * Asks the store for its region and what invoicing it offers, with a short cache per client. It NEVER throws: when OPS\n * does not answer (older server, outage, token without scope) it returns `NO_FISCAL_CONTEXT` and retries after a few\n * seconds, so a checkout without invoicing keeps working. `reason` says which of the cases it is. Use it as the loader:\n * `loader = () => loadFiscalContext(sf)`.\n */\nexport async function loadFiscalContext(sf: StorefrontClient, options: LoadFiscalContextOptions = {}): Promise<FiscalContext> {\n const now = options.now ?? Date.now\n const ttl = options.ttlMs ?? 60_000\n const hit = cache.get(sf)\n if (hit && now() - hit.at < (hit.ok ? ttl : FAILURE_TTL_MS)) return hit.value\n const opt = options.signal ? { signal: options.signal } : undefined\n const [region, caps] = await Promise.allSettled([sf.region(opt), sf.customer.capabilities(opt)])\n const reg = region.status === 'fulfilled' ? region.value : null\n const noRegion = region.status === 'rejected' && isStorefrontError(region.reason) && region.reason.status === 404\n const invoicing = caps.status === 'fulfilled' ? (caps.value?.invoicing ?? null) : null\n const ok = (region.status === 'fulfilled' || noRegion) && caps.status === 'fulfilled'\n const available = !!reg?.fiscal_id && !!invoicing?.available\n const reason: FiscalReason = available\n ? 'available'\n : !ok\n ? 'unreachable'\n : noRegion || !reg?.fiscal_id\n ? invoicing && !invoicing.available\n ? 'off'\n : 'no_region'\n : !invoicing\n ? 'unknown'\n : 'off'\n const value: FiscalContext = { available, reason, region: reg, invoicing: invoicing?.available ? invoicing : null }\n cache.set(sf, { at: now(), ok, value: ok || available ? value : NO_FISCAL_CONTEXT })\n return ok || available ? value : NO_FISCAL_CONTEXT\n}\n\n// ---- action ----------------------------------------------------------------------------------------------------------\n\n/** What the action needs of a cookie to read the buyer's session. React Router's `createCookie(...)` fits as it is. */\nexport type SessionCookie = { parse(header: string | null): unknown }\n\nexport type CreateInvoiceActionOptions = {\n storefront: StorefrontClient\n /** The store's cookie for the buyer session token (`createCookie('sf_customer', ...)`), to know who is asking. */\n customerCookie?: SessionCookie\n /** Alternative to `customerCookie`: returns the buyer's session token, or `null` for a guest. */\n readCustomerToken?: (request: Request) => string | null | undefined | Promise<string | null | undefined>\n /** Shopper IP for the per-shopper rate limit (see `clientIp` of the client). */\n clientIp?: (request: Request) => string | null | undefined\n /** Region + capabilities source, for tests or a shared cache (default `loadFiscalContext`). */\n context?: (sf: StorefrontClient) => Promise<FiscalContext>\n /** Limits of the no-JS upload (defaults: 5 MiB, 6 pages, 3 s). The region's own limits can only lower them. */\n limits?: Partial<UploadLimits>\n /** Clock, for the freshness check. */\n now?: () => Date\n /** Rejects cross-site posts (default `true`): `Origin` and `Sec-Fetch-Site` must match the site. */\n sameSiteOnly?: boolean\n /** Prefix of the form fields (default `invoice`). */\n namePrefix?: string\n}\n\nconst EVIDENCE_SOURCES = new Set(['pdf_client', 'pdf_server', 'qr', 'manual'])\n\n/** Reads the evidence the reader put in the form, keeping only the known keys (a form is untrusted input). */\nexport function parseEvidence(raw: unknown): FiscalDocumentEvidence | undefined {\n if (typeof raw !== 'string' || !raw || raw.length > 4096) return undefined\n let o: unknown\n try {\n o = JSON.parse(raw)\n } catch {\n return undefined\n }\n if (typeof o !== 'object' || o === null) return undefined\n const e = o as Record<string, unknown>\n if (typeof e.kind !== 'string' || !e.kind || typeof e.source !== 'string' || !EVIDENCE_SOURCES.has(e.source)) return undefined\n const str = (v: unknown, max: number) => (typeof v === 'string' && v.length <= max ? v : null)\n return {\n kind: e.kind.slice(0, 64),\n document_id: str(e.document_id, 64),\n issued_on: str(e.issued_on, 32),\n sha256: typeof e.sha256 === 'string' && /^[0-9a-f]{64}$/.test(e.sha256) ? e.sha256 : null,\n ...(typeof e.extractor === 'string' ? { extractor: e.extractor.slice(0, 64) } : {}),\n source: e.source as FiscalDocumentEvidence['source'],\n qr_match: typeof e.qr_match === 'boolean' ? e.qr_match : null,\n }\n}\n\n/** Reads the `invoice.*` fields of a form into the API's `InvoiceRequest`. */\nexport function invoiceFromForm(form: FormData, prefix = 'invoice'): InvoiceRequest {\n const text = (k: string) => {\n const v = form.get(`${prefix}.${k}`)\n return typeof v === 'string' ? v.trim() : ''\n }\n const evidence = parseEvidence(form.get(`${prefix}.document`))\n const out: InvoiceRequest = {\n requested: true,\n tax_id: normalizeTaxId(text('tax_id')),\n legal_name: text('legal_name'),\n tax_regime: text('tax_regime'),\n use: text('use'),\n postal_code: text('postal_code'),\n }\n if (evidence) out.document = evidence\n if (form.get(`${prefix}.save`)) out.save = true\n return out\n}\n\nconst fail = (intent: FiscalIntent | undefined, code: FiscalErrorCode, extra: Partial<FiscalActionData> = {}): FiscalActionData => ({\n ok: false,\n ...(intent ? { intent } : {}),\n ...extra,\n error: { code, ...(extra.error?.field ? { field: extra.error.field } : {}), ...(extra.error?.blocked ? { blocked: extra.error.blocked } : {}) },\n})\n\nconst LOG_EVERY_MS = 60_000\nlet lastLogAt = -Infinity\nfunction logMisconfigured(scopes: readonly string[]): void {\n const t = Date.now()\n if (t - lastLogAt < LOG_EVERY_MS) return\n lastLogAt = t\n console.error(\n `[@asteby/storefront-react] La credencial de la tienda no tiene permisos${scopes.length ? ` (${scopes.join(', ')})` : ''}; ` +\n `la facturación no funciona hasta repararlo en ${repairPath()}.`,\n )\n}\n\n/**\n * The form control a server field belongs to. The server names a field by what it validated (`fiscal.invoice_use`,\n * `invoice.requested`, an alias like `razon_social`), the form names the control (`invoice.use`): a field that maps to no\n * control returns `undefined` so it is NOT reported as a marked field (the buyer would see \"check the marked fields\" with\n * nothing marked).\n */\nconst CONTROL_OF: Record<string, string> = {\n tax_id: 'tax_id',\n rfc: 'tax_id',\n legal_name: 'legal_name',\n razon_social: 'legal_name',\n postal_code: 'postal_code',\n zip: 'postal_code',\n cp: 'postal_code',\n tax_regime: 'tax_regime',\n regimen: 'tax_regime',\n use: 'use',\n invoice_use: 'use',\n cfdi_use: 'use',\n uso_cfdi: 'use',\n}\n\n/** `fiscal.tax_regime` (profile) and `invoice.tax_regime` (cart, order) both name the same form control. */\nexport const formField = (field: string | undefined, prefix: string) => {\n if (!field) return undefined\n const control = CONTROL_OF[field.replace(/^(fiscal|invoice)\\./, '')]\n return control ? `${prefix}.${control}` : undefined\n}\n\n/**\n * The body of `request-invoice`: only the fiscal values, with the names every server version accepts (`invoice_use`, not\n * `use`; no `requested` flag, an order that is being invoiced is requested by definition; no evidence object, the order\n * keeps the data it was validated with). An OPS that predates contract 1.9 answered 400 `requested: unknown field` to the\n * full `InvoiceRequest` the checkout sends.\n */\nexport function orderInvoiceBody(invoice: InvoiceRequest): InvoiceRequest {\n const { requested: _requested, document: _document, use, ...rest } = invoice\n return { ...rest, ...(use ? { invoice_use: use } : {}) }\n}\n\nfunction toFailure(err: unknown, intent: FiscalIntent, prefix: string, values?: Partial<FiscalActionData['values']>): FiscalActionData {\n const carry: Partial<FiscalActionData> = values ? { values } : {}\n if (!isStorefrontError(err)) return fail(intent, 'unavailable', carry)\n if (err.code === 'insufficient_scope') {\n logMisconfigured(err.requiredScopes)\n return fail(intent, 'store_misconfigured', carry)\n }\n const details = (err.details ?? {}) as { reason?: unknown; blocked_reason?: unknown }\n switch (err.status) {\n case 401:\n return fail(intent, 'unauthorized', carry)\n case 404:\n return fail(intent, 'not_found', carry)\n case 409: {\n const why = typeof details.reason === 'string' ? details.reason : typeof details.blocked_reason === 'string' ? details.blocked_reason : ''\n return fail(intent, why === 'already_checked_out' ? 'not_found' : 'blocked', { ...carry, ...(why ? { error: { code: 'blocked', blocked: why } } : {}) })\n }\n case 422:\n case 400: {\n const field = formField(err.field, prefix)\n return fail(intent, 'validation', {\n ...carry,\n ...(field ? { fieldErrors: { [field]: err.message || 'invalid' }, error: { code: 'validation', field } } : {}),\n })\n }\n case 429:\n return fail(intent, 'rate_limited', carry)\n case 403:\n return fail(intent, 'unsupported', carry)\n default:\n return fail(intent, 'unavailable', carry)\n }\n}\n\nfunction sameSite(request: Request): boolean {\n const site = request.headers.get('sec-fetch-site')\n if (site && site !== 'same-origin' && site !== 'same-site' && site !== 'none') return false\n const origin = request.headers.get('origin')\n if (!origin) return true\n try {\n const host = request.headers.get('x-forwarded-host') ?? request.headers.get('host') ?? new URL(request.url).host\n return new URL(origin).host === host.split(',')[0]!.trim()\n } catch {\n return false\n }\n}\n\n/**\n * The invoicing action: one `POST` form with an `intent` field.\n *\n * | intent | fields | does |\n * | ------------------ | --------------------------------------------------- | ---------------------------------------------------------- |\n * | `set-invoice` | cart_id, invoice.* (`invoice.want` unset withdraws) | `carts.setInvoice`: validates against the region BEFORE paying |\n * | `save-profile` | invoice.*, consent | `customer.update({ fiscal })`, needs a session and consent |\n * | `request-invoice` | order_id, invoice.* | `customer.requestInvoice` (invoice after the purchase) |\n * | `clear-profile` | - | `customer.update({ fiscal: null })` (the buyer's right to delete) |\n * | `extract-document` | document (file), document_kind? | reads the file in memory, discards it, `step: 'review'` |\n *\n * It never redirects: it returns a `FiscalActionData` for the page to render (`<InvoiceToggle data={...}>`), with 422s\n * mapped to the field (`fieldErrors['invoice.tax_regime']`). The tax id of the buyer is never logged.\n */\nexport function createInvoiceAction(options: CreateInvoiceActionOptions) {\n const { storefront: sf } = options\n const prefix = options.namePrefix ?? 'invoice'\n const loadContext = options.context ?? ((c: StorefrontClient) => loadFiscalContext(c))\n const limits: UploadLimits = { ...UPLOAD_LIMITS, ...options.limits }\n\n async function sessionToken(request: Request): Promise<string | null> {\n if (options.readCustomerToken) return (await options.readCustomerToken(request)) || null\n if (!options.customerCookie) return null\n const v = await options.customerCookie.parse(request.headers.get('cookie'))\n return typeof v === 'string' && v ? v : null\n }\n\n return async function invoiceAction({ request }: { request: Request }): Promise<FiscalActionData> {\n if (options.sameSiteOnly !== false && !sameSite(request)) return fail(undefined, 'unsupported')\n // A file bigger than the limit is refused before it is buffered.\n const declared = Number(request.headers.get('content-length') ?? 0)\n if (declared > limits.maxBytes + 256 * 1024) return fail('extract-document', 'too_large')\n const form = await request.formData().catch(() => null)\n if (!form) return fail(undefined, 'validation')\n const intent = form.get('intent') as FiscalIntent\n const ip = options.clientIp?.(request)\n const token = await sessionToken(request)\n const w = { ...(ip ? { clientIp: ip } : {}), ...(token ? { customerToken: token } : {}) }\n const invoice = invoiceFromForm(form, prefix)\n const echo = { tax_id: invoice.tax_id ?? '', legal_name: invoice.legal_name ?? '', postal_code: invoice.postal_code ?? '', tax_regime: invoice.tax_regime ?? '', use: invoice.use ?? '' }\n const text = (name: string) => {\n const v = form.get(name)\n return typeof v === 'string' ? v.trim() : ''\n }\n\n try {\n switch (intent) {\n case 'set-invoice': {\n const cartId = text('cart_id')\n if (!cartId) return fail(intent, 'validation')\n if (!form.get(`${prefix}.want`) && !form.get(`${prefix}.requested`)) {\n await sf.carts.setInvoice(cartId, null, w)\n return { ok: true, intent, step: 'cleared' }\n }\n await sf.carts.setInvoice(cartId, invoice, w)\n return { ok: true, intent, step: 'saved', values: echo }\n }\n case 'save-profile': {\n if (!token) return fail(intent, 'unauthorized', { values: echo })\n if (!form.get('consent')) return fail(intent, 'consent_required', { values: echo })\n const fiscal: FiscalIdentityInput = {\n tax_id: invoice.tax_id ?? '',\n legal_name: invoice.legal_name ?? '',\n tax_regime: invoice.tax_regime ?? '',\n postal_code: invoice.postal_code ?? '',\n invoice_use: invoice.use ?? '',\n ...(invoice.document ? { document: invoice.document } : {}),\n }\n await sf.customer.update({ fiscal }, w)\n return { ok: true, intent, step: 'saved', values: echo }\n }\n case 'clear-profile': {\n if (!token) return fail(intent, 'unauthorized')\n await sf.customer.update({ fiscal: null }, w)\n return { ok: true, intent, step: 'cleared' }\n }\n case 'request-invoice': {\n if (!token) return fail(intent, 'unauthorized', { values: echo })\n const orderId = text('order_id')\n if (!orderId) return fail(intent, 'validation')\n const result = await sf.customer.requestInvoice(orderId, orderInvoiceBody(invoice), w)\n return { ok: true, intent, step: result.status === 'issued' ? 'issued' : 'pending', result, ...(result.invoice ? { invoice: result.invoice } : {}) }\n }\n case 'extract-document': {\n const ctx = await loadContext(sf)\n const region = ctx.region\n const kind = text('document_kind')\n const specs = region?.fiscal_id?.documents ?? []\n const spec = (kind ? specs.find((d) => d.kind === kind) : specs[0]) ?? undefined\n if (!ctx.available || !region || !spec) return fail(intent, 'unsupported')\n const file = form.get('document')\n if (!file || typeof file === 'string' || file.size === 0) return fail(intent, 'not_pdf')\n try {\n const extraction = await extractUploadedDocument(file, spec, region, { limits, ...(options.now ? { now: options.now() } : {}) })\n return { ok: true, intent, step: 'review', extraction }\n } catch (err) {\n const code = (err as { code?: string } | null)?.code\n const known: FiscalErrorCode[] = ['not_pdf', 'too_large', 'no_text', 'password', 'unreadable']\n // Never log the error: it can carry text from the document.\n return fail(intent, known.includes(code as FiscalErrorCode) ? (code as FiscalErrorCode) : 'unreadable')\n }\n }\n default:\n return fail(undefined, 'validation')\n }\n } catch (err) {\n return toFailure(err, intent, prefix, intent === 'clear-profile' ? undefined : echo)\n }\n }\n}\n\nexport type InvoiceAction = ReturnType<typeof createInvoiceAction>\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/storefront-react",
3
- "version": "0.26.0",
3
+ "version": "0.26.1",
4
4
  "description": "React bindings for @asteby/storefront — SSR-safe provider, optimistic cart, Money, responsive Image, prices, variant selection, SEO meta and JSON-LD, section registry and server-rendered theme CSS.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {