@asteby/storefront-react 0.22.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,6 +16,7 @@ var ES = {
16
16
  useHint: "Solo mostramos los usos que admite tu r\xE9gimen.",
17
17
  useChoose: "Elige el uso",
18
18
  fromDocument: "De tu {document}",
19
+ suggested: "Sugerido",
19
20
  errors: {
20
21
  required: "Completa este campo.",
21
22
  taxIdFormat: "Revisa el formato: {kind} no v\xE1lido.",
@@ -48,14 +49,34 @@ var ES = {
48
49
  qrMismatch: "El c\xF3digo QR de tu documento no coincide con los datos le\xEDdos. Revisa con calma tu RFC y tu nombre.",
49
50
  stale: "Tu documento tiene {days} d\xEDas de emitido. Si algo cambi\xF3 en tu registro, descarga uno nuevo.",
50
51
  regimeMissing: "No pudimos detectar tu r\xE9gimen. El\xEDgelo de la lista.",
51
- fieldsMissing: "No encontramos algunos datos en el documento. Compl\xE9talos para continuar."
52
+ fieldsMissing: "No encontramos algunos datos en el documento. Compl\xE9talos para continuar.",
53
+ qrPartial: "Del c\xF3digo QR solo le\xEDmos tu identificador fiscal. Completa los dem\xE1s datos o sube el PDF."
54
+ },
55
+ scan: {
56
+ title: "Escanea tu {document}",
57
+ photo: "Tomar foto del QR",
58
+ photoHint: "Enfoca el c\xF3digo QR de tu documento.",
59
+ camera: "Escanear con la c\xE1mara",
60
+ cameraTitle: "Apunta al c\xF3digo QR",
61
+ cameraGuide: "Centra el c\xF3digo QR dentro del marco.",
62
+ cameraStarting: "Abriendo la c\xE1mara\u2026",
63
+ cameraRead: "C\xF3digo le\xEDdo",
64
+ cameraDenied: "No pudimos usar la c\xE1mara. Elige una foto o sube el PDF.",
65
+ cameraUnavailable: "Este dispositivo no permite usar la c\xE1mara aqu\xED. Elige una foto o sube el PDF.",
66
+ closeCamera: "Cerrar la c\xE1mara",
67
+ choosePhoto: "Elegir una foto",
68
+ open: "Escanear {document}",
69
+ manual: "Capturar a mano",
70
+ close: "Cerrar"
52
71
  },
53
72
  errors: {
54
73
  not_pdf: "Ese archivo no es un PDF. Sube el PDF que descargaste o captura tus datos a mano.",
55
74
  too_large: "El archivo pesa m\xE1s de {size}. Sube el PDF original o captura tus datos a mano.",
56
75
  no_text: "Este PDF parece una imagen escaneada y no podemos leerlo. Captura tus datos a mano.",
57
76
  password: "Este PDF tiene contrase\xF1a y no podemos abrirlo. Desc\xE1rgalo de nuevo sin contrase\xF1a o captura tus datos a mano.",
58
- unreadable: "No pudimos leer este archivo. Int\xE9ntalo con otro o captura tus datos a mano."
77
+ unreadable: "No pudimos leer este archivo. Int\xE9ntalo con otro o captura tus datos a mano.",
78
+ no_qr: "No encontramos el c\xF3digo QR en la foto. Ac\xE9rcate, evita reflejos o sube el PDF.",
79
+ not_image: "Ese archivo no es una foto que podamos leer. Toma otra foto o sube el PDF."
59
80
  },
60
81
  summaryTitleOne: "Revisa 1 campo para continuar",
61
82
  summaryTitleMany: "Revisa {n} campos para continuar"
@@ -98,7 +119,14 @@ var ES = {
98
119
  windowTitle: "Ya pas\xF3 el plazo para facturar",
99
120
  windowBody: "Este pedido est\xE1 fuera del plazo para pedir factura. Si necesitas ayuda, contacta a la tienda.",
100
121
  unavailableTitle: "Factura no disponible",
101
- unavailableBody: "Esta tienda no emite factura de este pedido. Si la necesitas, cont\xE1ctala.",
122
+ unavailableBody: "Este pedido no se puede facturar por ahora. Si la necesitas, contacta a la tienda.",
123
+ notEnabledTitle: "Facturaci\xF3n a\xFAn no activa",
124
+ notEnabledBody: "La tienda todav\xEDa no tiene la facturaci\xF3n en l\xEDnea activa. Si necesitas factura, cont\xE1ctala.",
125
+ unknownTitle: "Factura",
126
+ unknownBody: "Por ahora no podemos mostrar el estado de la facturaci\xF3n de este pedido. Si necesitas factura, contacta a la tienda.",
127
+ unreachableBody: "No pudimos verificar la facturaci\xF3n en este momento. Int\xE9ntalo de nuevo en unos minutos o contacta a la tienda.",
128
+ sandbox: "Facturaci\xF3n en pruebas (sandbox): las facturas no tienen validez fiscal.",
129
+ windowNote: "Puedes pedirla hasta {days} d\xEDas despu\xE9s de la compra.",
102
130
  cancelledTitle: "Factura cancelada",
103
131
  cancelledBody: "Esta factura fue cancelada. Si necesitas una nueva, contacta a la tienda.",
104
132
  reasons: {
@@ -135,6 +163,7 @@ var EN = {
135
163
  useHint: "We only list the uses your regime admits.",
136
164
  useChoose: "Choose the use",
137
165
  fromDocument: "From your {document}",
166
+ suggested: "Suggested",
138
167
  errors: {
139
168
  required: "Fill in this field.",
140
169
  taxIdFormat: "Check the format: not a valid {kind}.",
@@ -167,14 +196,34 @@ var EN = {
167
196
  qrMismatch: "The QR code of your document does not match what we read. Check your tax ID and name carefully.",
168
197
  stale: "Your document was issued {days} days ago. If something changed in your registration, download a new one.",
169
198
  regimeMissing: "We could not detect your regime. Choose it from the list.",
170
- fieldsMissing: "We did not find some details in the document. Fill them in to continue."
199
+ fieldsMissing: "We did not find some details in the document. Fill them in to continue.",
200
+ qrPartial: "The QR code only gave us your tax ID. Fill in the rest or upload the PDF."
201
+ },
202
+ scan: {
203
+ title: "Scan your {document}",
204
+ photo: "Take a photo of the QR",
205
+ photoHint: "Point at the QR code of your document.",
206
+ camera: "Scan with the camera",
207
+ cameraTitle: "Point at the QR code",
208
+ cameraGuide: "Center the QR code inside the frame.",
209
+ cameraStarting: "Opening the camera\u2026",
210
+ cameraRead: "Code read",
211
+ cameraDenied: "We could not use the camera. Choose a photo or upload the PDF.",
212
+ cameraUnavailable: "This device does not allow the camera here. Choose a photo or upload the PDF.",
213
+ closeCamera: "Close the camera",
214
+ choosePhoto: "Choose a photo",
215
+ open: "Scan {document}",
216
+ manual: "Enter by hand",
217
+ close: "Close"
171
218
  },
172
219
  errors: {
173
220
  not_pdf: "That file is not a PDF. Upload the PDF you downloaded or enter your details by hand.",
174
221
  too_large: "The file is larger than {size}. Upload the original PDF or enter your details by hand.",
175
222
  no_text: "This PDF looks like a scanned image and we cannot read it. Enter your details by hand.",
176
223
  password: "This PDF is password protected and we cannot open it. Download it again without a password or enter your details by hand.",
177
- unreadable: "We could not read this file. Try another one or enter your details by hand."
224
+ unreadable: "We could not read this file. Try another one or enter your details by hand.",
225
+ no_qr: "We could not find the QR code in the photo. Move closer, avoid glare or upload the PDF.",
226
+ not_image: "That file is not a photo we can read. Take another photo or upload the PDF."
178
227
  },
179
228
  summaryTitleOne: "Check 1 field to continue",
180
229
  summaryTitleMany: "Check {n} fields to continue"
@@ -217,7 +266,14 @@ var EN = {
217
266
  windowTitle: "The invoicing period has ended",
218
267
  windowBody: "This order is outside the period to request an invoice. If you need help, contact the store.",
219
268
  unavailableTitle: "Invoice not available",
220
- unavailableBody: "This store does not issue an invoice for this order. If you need one, contact them.",
269
+ unavailableBody: "This order cannot be invoiced right now. If you need one, contact the store.",
270
+ notEnabledTitle: "Invoicing not active yet",
271
+ notEnabledBody: "The store has not turned on online invoicing yet. If you need an invoice, contact them.",
272
+ unknownTitle: "Invoice",
273
+ unknownBody: "We cannot show the invoicing status of this order right now. If you need an invoice, contact the store.",
274
+ unreachableBody: "We could not check invoicing right now. Try again in a few minutes or contact the store.",
275
+ sandbox: "Invoicing in test mode (sandbox): invoices have no fiscal validity.",
276
+ windowNote: "You can request it up to {days} days after the purchase.",
221
277
  cancelledTitle: "Invoice cancelled",
222
278
  cancelledBody: "This invoice was cancelled. If you need a new one, contact the store.",
223
279
  reasons: {
@@ -256,5 +312,5 @@ function fill(template, vars) {
256
312
  }
257
313
 
258
314
  export { fill, fiscalMessages };
259
- //# sourceMappingURL=chunk-PPQEUTXJ.js.map
260
- //# sourceMappingURL=chunk-PPQEUTXJ.js.map
315
+ //# sourceMappingURL=chunk-5XYQLOE3.js.map
316
+ //# sourceMappingURL=chunk-5XYQLOE3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/fiscal/messages.ts"],"names":[],"mappings":";AAyIA,IAAM,EAAA,GAAqB;AAAA,EACzB,MAAA,EAAQ;AAAA,IACN,MAAA,EAAQ,gBAAA;AAAA,IACR,KAAA,EAAO,KAAA;AAAA,IACP,SAAA,EAAW,6CAAA;AAAA,IACX,SAAA,EAAW,0BAAA;AAAA,IACX,aAAA,EAAe,sDAAA;AAAA,IACf,UAAA,EAAY,yBAAA;AAAA,IACZ,cAAA,EAAgB,kEAAA;AAAA,IAChB,MAAA,EAAQ,mBAAA;AAAA,IACR,UAAA,EAAY,sDAAA;AAAA,IACZ,YAAA,EAAc,qBAAA;AAAA,IACd,kBAAA,EAAoB,0FAAA;AAAA,IACpB,GAAA,EAAK,mBAAA;AAAA,IACL,OAAA,EAAS,mDAAA;AAAA,IACT,SAAA,EAAW,cAAA;AAAA,IACX,YAAA,EAAc,kBAAA;AAAA,IACd,SAAA,EAAW,UAAA;AAAA,IACX,MAAA,EAAQ;AAAA,MACN,QAAA,EAAU,sBAAA;AAAA,MACV,WAAA,EAAa,yCAAA;AAAA,MACb,YAAA,EAAc,+DAAA;AAAA,MACd,OAAA,EAAS,kCAAA;AAAA,MACT,UAAA,EAAY,sDAAA;AAAA,MACZ,aAAA,EAAe;AAAA;AACjB,GACF;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,KAAA,EAAO,oBAAA;AAAA,IACP,QAAA,EAAU,8CAAA;AAAA,IACV,UAAA,EAAY,+BAAA;AAAA,IACZ,MAAA,EAAQ,gBAAA;AAAA,IACR,MAAA,EAAQ,uBAAA;AAAA,IACR,cAAA,EAAgB,qFAAA;AAAA,IAChB,aAAA,EAAe,qFAAA;AAAA,IACf,YAAA,EAAc,iBAAA;AAAA,IACd,MAAA,EAAQ,0BAAA;AAAA,IACR,OAAA,EAAS,6BAAA;AAAA,IACT,MAAA,EAAQ,UAAA;AAAA,IACR,IAAA,EAAM,0BAAA;AAAA,IACN,WAAA,EAAa,kBAAA;AAAA,IACb,UAAA,EAAY,qDAAA;AAAA,IACZ,OAAA,EAAS,kBAAA;AAAA,IACT,UAAA,EAAY,oBAAA;AAAA,IACZ,aAAA,EAAe,yCAAA;AAAA,IACf,WAAA,EAAa,uGAAA;AAAA,IACb,QAAA,EAAU;AAAA,MACR,UAAA,EAAY,2GAAA;AAAA,MACZ,KAAA,EAAO,qGAAA;AAAA,MACP,aAAA,EAAe,4DAAA;AAAA,MACf,aAAA,EAAe,8EAAA;AAAA,MACf,SAAA,EAAW;AAAA,KACb;AAAA,IACA,IAAA,EAAM;AAAA,MACJ,KAAA,EAAO,uBAAA;AAAA,MACP,KAAA,EAAO,mBAAA;AAAA,MACP,SAAA,EAAW,yCAAA;AAAA,MACX,MAAA,EAAQ,2BAAA;AAAA,MACR,WAAA,EAAa,wBAAA;AAAA,MACb,WAAA,EAAa,0CAAA;AAAA,MACb,cAAA,EAAgB,6BAAA;AAAA,MAChB,UAAA,EAAY,oBAAA;AAAA,MACZ,YAAA,EAAc,6DAAA;AAAA,MACd,iBAAA,EAAmB,sFAAA;AAAA,MACnB,WAAA,EAAa,qBAAA;AAAA,MACb,WAAA,EAAa,iBAAA;AAAA,MACb,IAAA,EAAM,qBAAA;AAAA,MACN,MAAA,EAAQ,iBAAA;AAAA,MACR,KAAA,EAAO;AAAA,KACT;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,OAAA,EAAS,mFAAA;AAAA,MACT,SAAA,EAAW,oFAAA;AAAA,MACX,OAAA,EAAS,qFAAA;AAAA,MACT,QAAA,EAAU,yHAAA;AAAA,MACV,UAAA,EAAY,iFAAA;AAAA,MACZ,KAAA,EAAO,uFAAA;AAAA,MACP,SAAA,EAAW;AAAA,KACb;AAAA,IACA,eAAA,EAAiB,+BAAA;AAAA,IACjB,gBAAA,EAAkB;AAAA,GACpB;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,KAAA,EAAO,yBAAA;AAAA,IACP,UAAA,EAAY,OAAA;AAAA,IACZ,aAAA,EAAe,kBAAA;AAAA,IACf,YAAA,EAAc,8BAAA;AAAA,IACd,MAAA,EAAQ,SAAA;AAAA,IACR,QAAA,EAAU,kBAAA;AAAA,IACV,QAAA,EAAU,0BAAA;AAAA,IACV,WAAA,EAAa,yBAAA;AAAA,IACb,WAAA,EAAa,gDAAA;AAAA,IACb,OAAA,EAAS,iDAAA;AAAA,IACT,QAAA,EAAU,EAAE,KAAA,EAAO,KAAA,EAAO,SAAA,EAAW,0BAAA,EAAyB,UAAA,EAAY,yBAAA,EAAwB,MAAA,EAAQ,YAAA,EAAW,GAAA,EAAK,KAAA;AAAM,GAClI;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,KAAA,EAAO,SAAA;AAAA,IACP,OAAA,EAAS,mBAAA;AAAA,IACT,WAAA,EAAa,uCAAA;AAAA,IACb,aAAA,EAAe,mBAAA;AAAA,IACf,YAAA,EAAc,gGAAA;AAAA,IACd,eAAA,EAAiB,8BAAA;AAAA,IACjB,cAAA,EAAgB,oEAAA;AAAA,IAChB,cAAA,EAAgB,uGAAA;AAAA,IAChB,OAAA,EAAS,YAAA;AAAA,IACT,WAAA,EAAa,0BAAA;AAAA,IACb,UAAA,EAAY,yHAAA;AAAA,IACZ,KAAA,EAAO,cAAA;AAAA,IACP,GAAA,EAAK,eAAA;AAAA,IACL,GAAA,EAAK,eAAA;AAAA,IACL,cAAA,EAAgB,8BAAA;AAAA,IAChB,aAAA,EAAe,kDAAA;AAAA,IACf,OAAA,EAAS,uBAAA;AAAA,IACT,WAAA,EAAa,wCAAA;AAAA,IACb,UAAA,EAAY,iGAAA;AAAA,IACZ,OAAA,EAAS,uBAAA;AAAA,IACT,SAAA,EAAW,uDAAA;AAAA,IACX,WAAA,EAAa,mCAAA;AAAA,IACb,UAAA,EAAY,mGAAA;AAAA,IACZ,gBAAA,EAAkB,uBAAA;AAAA,IAClB,eAAA,EAAiB,oFAAA;AAAA,IACjB,eAAA,EAAiB,iCAAA;AAAA,IACjB,cAAA,EAAgB,0GAAA;AAAA,IAChB,YAAA,EAAc,SAAA;AAAA,IACd,WAAA,EAAa,yHAAA;AAAA,IACb,eAAA,EAAiB,uHAAA;AAAA,IACjB,OAAA,EAAS,6EAAA;AAAA,IACT,UAAA,EAAY,8DAAA;AAAA,IACZ,cAAA,EAAgB,mBAAA;AAAA,IAChB,aAAA,EAAe,2EAAA;AAAA,IACf,OAAA,EAAS;AAAA,MACP,gBAAA,EAAkB,yBAAA;AAAA,MAClB,oBAAA,EAAsB,6DAAA;AAAA,MACtB,oBAAA,EAAsB,sDAAA;AAAA,MACtB,qBAAA,EAAuB,6CAAA;AAAA,MACvB,aAAA,EAAe,yDAAA;AAAA,MACf,OAAA,EAAS;AAAA;AACX,GACF;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,MAAA,EAAQ;AAAA,MACN,UAAA,EAAY,oBAAA;AAAA,MACZ,QAAA,EAAU,sBAAA;AAAA,MACV,OAAA,EAAS;AAAA;AACX;AAEJ,CAAA;AAEA,IAAM,EAAA,GAAqB;AAAA,EACzB,MAAA,EAAQ;AAAA,IACN,MAAA,EAAQ,aAAA;AAAA,IACR,KAAA,EAAO,QAAA;AAAA,IACP,SAAA,EAAW,4BAAA;AAAA,IACX,SAAA,EAAW,YAAA;AAAA,IACX,aAAA,EAAe,kDAAA;AAAA,IACf,UAAA,EAAY,iBAAA;AAAA,IACZ,cAAA,EAAgB,0EAAA;AAAA,IAChB,MAAA,EAAQ,YAAA;AAAA,IACR,UAAA,EAAY,4DAAA;AAAA,IACZ,YAAA,EAAc,oBAAA;AAAA,IACd,kBAAA,EAAoB,4EAAA;AAAA,IACpB,GAAA,EAAK,aAAA;AAAA,IACL,OAAA,EAAS,2CAAA;AAAA,IACT,SAAA,EAAW,gBAAA;AAAA,IACX,YAAA,EAAc,sBAAA;AAAA,IACd,SAAA,EAAW,WAAA;AAAA,IACX,MAAA,EAAQ;AAAA,MACN,QAAA,EAAU,qBAAA;AAAA,MACV,WAAA,EAAa,uCAAA;AAAA,MACb,YAAA,EAAc,4CAAA;AAAA,MACd,OAAA,EAAS,iCAAA;AAAA,MACT,UAAA,EAAY,mDAAA;AAAA,MACZ,aAAA,EAAe;AAAA;AACjB,GACF;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,KAAA,EAAO,wBAAA;AAAA,IACP,QAAA,EAAU,uCAAA;AAAA,IACV,UAAA,EAAY,0BAAA;AAAA,IACZ,MAAA,EAAQ,aAAA;AAAA,IACR,MAAA,EAAQ,aAAA;AAAA,IACR,cAAA,EAAgB,mFAAA;AAAA,IAChB,aAAA,EAAe,+EAAA;AAAA,IACf,YAAA,EAAc,cAAA;AAAA,IACd,MAAA,EAAQ,4BAAA;AAAA,IACR,OAAA,EAAS,+BAAA;AAAA,IACT,MAAA,EAAQ,QAAA;AAAA,IACR,IAAA,EAAM,4BAAA;AAAA,IACN,WAAA,EAAa,qBAAA;AAAA,IACb,UAAA,EAAY,0CAAA;AAAA,IACZ,OAAA,EAAS,mBAAA;AAAA,IACT,UAAA,EAAY,qBAAA;AAAA,IACZ,aAAA,EAAe,kCAAA;AAAA,IACf,WAAA,EAAa,6FAAA;AAAA,IACb,QAAA,EAAU;AAAA,MACR,UAAA,EAAY,iGAAA;AAAA,MACZ,KAAA,EAAO,0GAAA;AAAA,MACP,aAAA,EAAe,2DAAA;AAAA,MACf,aAAA,EAAe,yEAAA;AAAA,MACf,SAAA,EAAW;AAAA,KACb;AAAA,IACA,IAAA,EAAM;AAAA,MACJ,KAAA,EAAO,sBAAA;AAAA,MACP,KAAA,EAAO,wBAAA;AAAA,MACP,SAAA,EAAW,wCAAA;AAAA,MACX,MAAA,EAAQ,sBAAA;AAAA,MACR,WAAA,EAAa,sBAAA;AAAA,MACb,WAAA,EAAa,sCAAA;AAAA,MACb,cAAA,EAAgB,0BAAA;AAAA,MAChB,UAAA,EAAY,WAAA;AAAA,MACZ,YAAA,EAAc,gEAAA;AAAA,MACd,iBAAA,EAAmB,+EAAA;AAAA,MACnB,WAAA,EAAa,kBAAA;AAAA,MACb,WAAA,EAAa,gBAAA;AAAA,MACb,IAAA,EAAM,iBAAA;AAAA,MACN,MAAA,EAAQ,eAAA;AAAA,MACR,KAAA,EAAO;AAAA,KACT;AAAA,IACA,MAAA,EAAQ;AAAA,MACN,OAAA,EAAS,sFAAA;AAAA,MACT,SAAA,EAAW,wFAAA;AAAA,MACX,OAAA,EAAS,wFAAA;AAAA,MACT,QAAA,EAAU,2HAAA;AAAA,MACV,UAAA,EAAY,6EAAA;AAAA,MACZ,KAAA,EAAO,yFAAA;AAAA,MACP,SAAA,EAAW;AAAA,KACb;AAAA,IACA,eAAA,EAAiB,2BAAA;AAAA,IACjB,gBAAA,EAAkB;AAAA,GACpB;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,KAAA,EAAO,mBAAA;AAAA,IACP,UAAA,EAAY,KAAA;AAAA,IACZ,aAAA,EAAe,iBAAA;AAAA,IACf,YAAA,EAAc,oCAAA;AAAA,IACd,MAAA,EAAQ,QAAA;AAAA,IACR,QAAA,EAAU,mBAAA;AAAA,IACV,QAAA,EAAU,sBAAA;AAAA,IACV,WAAA,EAAa,iBAAA;AAAA,IACb,WAAA,EAAa,yCAAA;AAAA,IACb,OAAA,EAAS,kDAAA;AAAA,IACT,QAAA,EAAU,EAAE,KAAA,EAAO,QAAA,EAAU,SAAA,EAAW,YAAA,EAAc,UAAA,EAAY,iBAAA,EAAmB,MAAA,EAAQ,QAAA,EAAU,GAAA,EAAK,KAAA;AAAM,GACpH;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,KAAA,EAAO,SAAA;AAAA,IACP,OAAA,EAAS,iBAAA;AAAA,IACT,WAAA,EAAa,wCAAA;AAAA,IACb,aAAA,EAAe,0BAAA;AAAA,IACf,YAAA,EAAc,0FAAA;AAAA,IACd,eAAA,EAAiB,gCAAA;AAAA,IACjB,cAAA,EAAgB,+DAAA;AAAA,IAChB,cAAA,EAAgB,8FAAA;AAAA,IAChB,OAAA,EAAS,SAAA;AAAA,IACT,WAAA,EAAa,uBAAA;AAAA,IACb,UAAA,EAAY,+FAAA;AAAA,IACZ,KAAA,EAAO,WAAA;AAAA,IACP,GAAA,EAAK,cAAA;AAAA,IACL,GAAA,EAAK,cAAA;AAAA,IACL,cAAA,EAAgB,iCAAA;AAAA,IAChB,aAAA,EAAe,qDAAA;AAAA,IACf,OAAA,EAAS,eAAA;AAAA,IACT,WAAA,EAAa,qCAAA;AAAA,IACb,UAAA,EAAY,wGAAA;AAAA,IACZ,OAAA,EAAS,mBAAA;AAAA,IACT,SAAA,EAAW,4CAAA;AAAA,IACX,WAAA,EAAa,gCAAA;AAAA,IACb,UAAA,EAAY,8FAAA;AAAA,IACZ,gBAAA,EAAkB,uBAAA;AAAA,IAClB,eAAA,EAAiB,8EAAA;AAAA,IACjB,eAAA,EAAiB,0BAAA;AAAA,IACjB,cAAA,EAAgB,yFAAA;AAAA,IAChB,YAAA,EAAc,SAAA;AAAA,IACd,WAAA,EAAa,yGAAA;AAAA,IACb,eAAA,EAAiB,0FAAA;AAAA,IACjB,OAAA,EAAS,qEAAA;AAAA,IACT,UAAA,EAAY,0DAAA;AAAA,IACZ,cAAA,EAAgB,mBAAA;AAAA,IAChB,aAAA,EAAe,uEAAA;AAAA,IACf,OAAA,EAAS;AAAA,MACP,gBAAA,EAAkB,0BAAA;AAAA,MAClB,oBAAA,EAAsB,kDAAA;AAAA,MACtB,oBAAA,EAAsB,+CAAA;AAAA,MACtB,qBAAA,EAAuB,mCAAA;AAAA,MACvB,aAAA,EAAe,gDAAA;AAAA,MACf,OAAA,EAAS;AAAA;AACX,GACF;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,MAAA,EAAQ;AAAA,MACN,UAAA,EAAY,mBAAA;AAAA,MACZ,QAAA,EAAU,qBAAA;AAAA,MACV,OAAA,EAAS;AAAA;AACX;AAEJ,CAAA;AAEA,IAAM,KAAA,GAAQ,CAAoC,IAAA,EAAS,IAAA,KAAwC;AACjG,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAClB,EAAA,MAAM,GAAA,GAA+B,EAAE,GAAG,IAAA,EAAK;AAC/C,EAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,EAAG;AACzC,IAAA,IAAI,MAAM,MAAA,EAAW;AACrB,IAAA,MAAM,CAAA,GAAI,KAAK,CAAC,CAAA;AAChB,IAAA,GAAA,CAAI,CAAC,IAAI,OAAO,CAAA,KAAM,YAAY,CAAA,KAAM,IAAA,IAAQ,CAAC,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,IAAK,OAAO,MAAM,QAAA,IAAY,CAAA,KAAM,OAAO,KAAA,CAAM,CAAA,EAA8B,CAAyC,CAAA,GAAI,CAAA;AAAA,EAC9L;AACA,EAAA,OAAO,GAAA;AACT,CAAA;AAGO,SAAS,cAAA,CAAe,QAAwB,SAAA,EAAgE;AACrH,EAAA,MAAM,IAAA,GAAA,CAAQ,UAAU,OAAA,EAAS,WAAA,GAAc,UAAA,CAAW,IAAI,IAAI,EAAA,GAAK,EAAA;AACvE,EAAA,OAAO,SAAA,GAAY,KAAA,CAAM,IAAA,EAAM,SAAS,CAAA,GAAI,IAAA;AAC9C;AAGO,SAAS,IAAA,CAAK,UAAkB,IAAA,EAA+C;AACpF,EAAA,OAAO,QAAA,CAAS,OAAA,CAAQ,YAAA,EAAc,CAAC,CAAA,EAAG,CAAA,KAAe,CAAA,IAAK,IAAA,GAAO,MAAA,CAAO,IAAA,CAAK,CAAC,CAAC,IAAI,CAAE,CAAA;AAC3F","file":"chunk-5XYQLOE3.js","sourcesContent":["/** Texts of the fiscal components. Spanish (es-MX) by default, English with `en*`, any locale with `messages`. */\nimport type { FiscalReadError } from './types.js'\n\nexport type DeepPartial<T> = { [K in keyof T]?: T[K] extends Record<string, unknown> ? DeepPartial<T[K]> : T[K] }\n\nexport type FiscalMessages = {\n fields: {\n legend: string\n taxId: string\n taxIdHint: string\n legalName: string\n /** Used when the region publishes no help for the legal name. */\n legalNameHint: string\n postalCode: string\n postalCodeHint: string\n regime: string\n regimeHint: string\n regimeChoose: string\n regimeFromDocument: string\n use: string\n useHint: string\n useChoose: string\n /** `{document}` = the document's name. */\n fromDocument: string\n /** Mark on an invoice use the region suggested (an editable preselection). */\n suggested: string\n errors: {\n required: string\n taxIdFormat: string\n postalFormat: string\n unknown: string\n personType: string\n useNotAllowed: string\n }\n }\n reader: {\n /** `{document}`. */\n title: string\n dropHint: string\n dropActive: string\n choose: string\n obtain: string\n privacyBrowser: string\n privacyServer: string\n uploadServer: string\n manual: string\n reading: string\n cancel: string\n done: string\n reviewTitle: string\n reviewHint: string\n confirm: string\n changeFile: string\n saveToAccount: string\n saveConsent: string\n warnings: { qrMismatch: string; stale: string; regimeMissing: string; fieldsMissing: string; qrPartial: string }\n /** Reading a photo or the camera (the region's `image_mode`). `{document}` where noted. */\n scan: {\n title: string\n photo: string\n photoHint: string\n camera: string\n cameraTitle: string\n cameraGuide: string\n cameraStarting: string\n cameraRead: string\n cameraDenied: string\n cameraUnavailable: string\n closeCamera: string\n choosePhoto: string\n /** Button that opens the sheet; `{document}` is the document's label. */\n open: string\n manual: string\n close: string\n }\n errors: Record<FiscalReadError, string>\n summaryTitleOne: string\n summaryTitleMany: string\n }\n toggle: {\n label: string\n payDefault: string\n payAndInvoice: string\n profileTitle: string\n change: string\n useOther: string\n useSaved: string\n manualTitle: string\n unavailable: string\n privacy: string\n fieldsOf: { taxId: string; legalName: string; postalCode: string; regime: string; use: string }\n }\n status: {\n title: string\n request: string\n requestHelp: string\n awaitingTitle: string\n awaitingBody: string\n processingTitle: string\n processingBody: string\n processingSlow: string\n refresh: string\n issuedTitle: string\n issuedBody: string\n folio: string\n pdf: string\n xml: string\n needsDataTitle: string\n needsDataBody: string\n correct: string\n reviewTitle: string\n reviewBody: string\n contact: string\n contactBy: string\n windowTitle: string\n windowBody: string\n unavailableTitle: string\n unavailableBody: string\n /** The server says the store has no invoicing active (`capabilities.invoicing.available: false`). */\n notEnabledTitle: string\n notEnabledBody: string\n /** The server does not say (older server) or could not be asked: never claims the store does not invoice. */\n unknownTitle: string\n unknownBody: string\n unreachableBody: string\n /** Invoicing in test mode (sandbox): invoices have no fiscal validity. */\n sandbox: string\n /** `{days}`: how long after the purchase an invoice can be requested. */\n windowNote: string\n cancelledTitle: string\n cancelledBody: string\n /** `reason_code` -> cause for the buyer. `default` when unknown. */\n reasons: Record<string, string>\n }\n server: { errors: Record<string, string> }\n}\n\nconst ES: FiscalMessages = {\n fields: {\n legend: 'Datos fiscales',\n taxId: 'RFC',\n taxIdHint: 'Se escribe en mayúsculas y sin espacios.',\n legalName: 'Nombre o razón social',\n legalNameHint: 'Escríbelo tal como aparece en tu registro fiscal.',\n postalCode: 'Código postal fiscal',\n postalCodeHint: 'El de tu domicilio fiscal, que puede ser distinto al de entrega.',\n regime: 'Régimen fiscal',\n regimeHint: 'Solo mostramos los que aplican a tu tipo de persona.',\n regimeChoose: 'Elige tu régimen',\n regimeFromDocument: 'Tu documento menciona más de un régimen. Elige el que usarás para esta factura.',\n use: 'Uso de la factura',\n useHint: 'Solo mostramos los usos que admite tu régimen.',\n useChoose: 'Elige el uso',\n fromDocument: 'De tu {document}',\n suggested: 'Sugerido',\n errors: {\n required: 'Completa este campo.',\n taxIdFormat: 'Revisa el formato: {kind} no válido.',\n postalFormat: 'Escribe un código postal válido, por ejemplo {example}.',\n unknown: 'Elige una opción de la lista.',\n personType: 'Este régimen no corresponde a tu tipo de persona.',\n useNotAllowed: 'Este uso no está disponible con el régimen elegido.',\n },\n },\n reader: {\n title: 'Sube tu {document}',\n dropHint: 'Arrastra el PDF aquí o toca para elegirlo',\n dropActive: 'Suelta el archivo para leerlo',\n choose: 'Elegir archivo',\n obtain: 'Descárgala aquí',\n privacyBrowser: 'La leemos en tu dispositivo. No guardamos el archivo, solo los datos que confirmes.',\n privacyServer: 'La procesamos y la descartamos al terminar. Solo guardamos los datos que confirmes.',\n uploadServer: 'Leer mi archivo',\n manual: 'Captura tus datos a mano',\n reading: 'Leyendo tu {document}…',\n cancel: 'Cancelar',\n done: 'Listo. Revisa tus datos.',\n reviewTitle: 'Revisa tus datos',\n reviewHint: 'Puedes corregir cualquier campo antes de continuar.',\n confirm: 'Usar estos datos',\n changeFile: 'Subir otro archivo',\n saveToAccount: 'Guardar en mi cuenta para la próxima',\n saveConsent: 'Guardaremos estos datos en tu cuenta para facturar más rápido. Puedes borrarlos cuando quieras.',\n warnings: {\n qrMismatch: 'El código QR de tu documento no coincide con los datos leídos. Revisa con calma tu RFC y tu nombre.',\n stale: 'Tu documento tiene {days} días de emitido. Si algo cambió en tu registro, descarga uno nuevo.',\n regimeMissing: 'No pudimos detectar tu régimen. Elígelo de la lista.',\n fieldsMissing: 'No encontramos algunos datos en el documento. Complétalos para continuar.',\n qrPartial: 'Del código QR solo leímos tu identificador fiscal. Completa los demás datos o sube el PDF.',\n },\n scan: {\n title: 'Escanea tu {document}',\n photo: 'Tomar foto del QR',\n photoHint: 'Enfoca el código QR de tu documento.',\n camera: 'Escanear con la cámara',\n cameraTitle: 'Apunta al código QR',\n cameraGuide: 'Centra el código QR dentro del marco.',\n cameraStarting: 'Abriendo la cámara…',\n cameraRead: 'Código leído',\n cameraDenied: 'No pudimos usar la cámara. Elige una foto o sube el PDF.',\n cameraUnavailable: 'Este dispositivo no permite usar la cámara aquí. Elige una foto o sube el PDF.',\n closeCamera: 'Cerrar la cámara',\n choosePhoto: 'Elegir una foto',\n open: 'Escanear {document}',\n manual: 'Capturar a mano',\n close: 'Cerrar',\n },\n errors: {\n not_pdf: 'Ese archivo no es un PDF. Sube el PDF que descargaste o captura tus datos a mano.',\n too_large: 'El archivo pesa más de {size}. Sube el PDF original o captura tus datos a mano.',\n no_text: 'Este PDF parece una imagen escaneada y no podemos leerlo. Captura tus datos a mano.',\n password: 'Este PDF tiene contraseña y no podemos abrirlo. Descárgalo de nuevo sin contraseña o captura tus datos a mano.',\n unreadable: 'No pudimos leer este archivo. Inténtalo con otro o captura tus datos a mano.',\n no_qr: 'No encontramos el código QR en la foto. Acércate, evita reflejos o sube el PDF.',\n not_image: 'Ese archivo no es una foto que podamos leer. Toma otra foto o sube el PDF.',\n },\n summaryTitleOne: 'Revisa 1 campo para continuar',\n summaryTitleMany: 'Revisa {n} campos para continuar',\n },\n toggle: {\n label: 'Necesito factura (CFDI)',\n payDefault: 'Pagar',\n payAndInvoice: 'Pagar y facturar',\n profileTitle: 'Facturaremos con estos datos',\n change: 'Cambiar',\n useOther: 'Usar otros datos',\n useSaved: 'Usar mis datos guardados',\n manualTitle: 'Datos de facturación',\n unavailable: 'Esta tienda todavía no factura en línea.',\n privacy: 'Usamos estos datos solo para emitir tu factura.',\n fieldsOf: { taxId: 'RFC', legalName: 'Nombre o razón social', postalCode: 'Código postal fiscal', regime: 'Régimen', use: 'Uso' },\n },\n status: {\n title: 'Factura',\n request: 'Solicitar factura',\n requestHelp: '¿Necesitas factura de este pedido?',\n awaitingTitle: 'Esperando tu pago',\n awaitingBody: 'Cuando se acredite tu pago emitimos la factura. Con OXXO o transferencia puede tardar un poco.',\n processingTitle: 'Estamos generando tu factura',\n processingBody: 'Suele tardar menos de un minuto. Esta página se actualiza sola.',\n processingSlow: 'Sigue en proceso. Puedes cerrar esta página: tu factura aparecerá aquí cuando esté lista.',\n refresh: 'Actualizar',\n issuedTitle: 'Tu factura está lista',\n issuedBody: 'Descárgala cuando la necesites. Los enlaces vencen en 15 minutos; vuelve a abrir esta página para generar nuevos.',\n folio: 'Folio fiscal',\n pdf: 'Descargar PDF',\n xml: 'Descargar XML',\n needsDataTitle: 'Faltan datos para tu factura',\n needsDataBody: 'No pudimos emitirla con los datos que nos diste.',\n correct: 'Corregir y reintentar',\n reviewTitle: 'La tienda está revisando tu factura',\n reviewBody: 'Necesita una revisión del negocio. No tienes que hacer nada más; si tarda, escríbenos.',\n contact: 'Contactar a la tienda',\n contactBy: 'Escríbenos a {contact} con tu número de pedido.',\n windowTitle: 'Ya pasó el plazo para facturar',\n windowBody: 'Este pedido está fuera del plazo para pedir factura. Si necesitas ayuda, contacta a la tienda.',\n unavailableTitle: 'Factura no disponible',\n unavailableBody: 'Este pedido no se puede facturar por ahora. Si la necesitas, contacta a la tienda.',\n notEnabledTitle: 'Facturación aún no activa',\n notEnabledBody: 'La tienda todavía no tiene la facturación en línea activa. Si necesitas factura, contáctala.',\n unknownTitle: 'Factura',\n unknownBody: 'Por ahora no podemos mostrar el estado de la facturación de este pedido. Si necesitas factura, contacta a la tienda.',\n unreachableBody: 'No pudimos verificar la facturación en este momento. Inténtalo de nuevo en unos minutos o contacta a la tienda.',\n sandbox: 'Facturación en pruebas (sandbox): las facturas no tienen validez fiscal.',\n windowNote: 'Puedes pedirla hasta {days} días después de la compra.',\n cancelledTitle: 'Factura cancelada',\n cancelledBody: 'Esta factura fue cancelada. Si necesitas una nueva, contacta a la tienda.',\n reasons: {\n 'invoice.tax_id': 'El RFC no es válido.',\n 'invoice.legal_name': 'El nombre o la razón social no coincide con tu registro.',\n 'invoice.tax_regime': 'El régimen fiscal no es válido para tus datos.',\n 'invoice.postal_code': 'El código postal fiscal no es válido.',\n 'invoice.use': 'El uso de la factura no es válido con tu régimen.',\n default: 'Revisa tus datos fiscales y vuelve a intentarlo.',\n },\n },\n server: {\n errors: {\n validation: 'Revisa este campo.',\n required: 'Completa este campo.',\n invalid: 'El valor no es válido.',\n },\n },\n}\n\nconst EN: FiscalMessages = {\n fields: {\n legend: 'Tax details',\n taxId: 'Tax ID',\n taxIdHint: 'Uppercase, without spaces.',\n legalName: 'Legal name',\n legalNameHint: 'Write it as it appears in your tax registration.',\n postalCode: 'Tax postal code',\n postalCodeHint: 'The one of your tax address, which can differ from the delivery address.',\n regime: 'Tax regime',\n regimeHint: 'We only list the ones that apply to your kind of taxpayer.',\n regimeChoose: 'Choose your regime',\n regimeFromDocument: 'Your document lists more than one regime. Choose the one for this invoice.',\n use: 'Invoice use',\n useHint: 'We only list the uses your regime admits.',\n useChoose: 'Choose the use',\n fromDocument: 'From your {document}',\n suggested: 'Suggested',\n errors: {\n required: 'Fill in this field.',\n taxIdFormat: 'Check the format: not a valid {kind}.',\n postalFormat: 'Enter a valid postal code, like {example}.',\n unknown: 'Choose an option from the list.',\n personType: 'This regime does not match your kind of taxpayer.',\n useNotAllowed: 'This use is not available with the chosen regime.',\n },\n },\n reader: {\n title: 'Upload your {document}',\n dropHint: 'Drag the PDF here or tap to choose it',\n dropActive: 'Drop the file to read it',\n choose: 'Choose file',\n obtain: 'Get it here',\n privacyBrowser: 'We read it on your device. We do not keep the file, only the details you confirm.',\n privacyServer: 'We process it and discard it when done. We only keep the details you confirm.',\n uploadServer: 'Read my file',\n manual: 'Enter your details by hand',\n reading: 'Reading your {document}…',\n cancel: 'Cancel',\n done: 'Done. Review your details.',\n reviewTitle: 'Review your details',\n reviewHint: 'You can fix any field before continuing.',\n confirm: 'Use these details',\n changeFile: 'Upload another file',\n saveToAccount: 'Save to my account for next time',\n saveConsent: 'We will save these details to your account to invoice faster. You can delete them any time.',\n warnings: {\n qrMismatch: 'The QR code of your document does not match what we read. Check your tax ID and name carefully.',\n stale: 'Your document was issued {days} days ago. If something changed in your registration, download a new one.',\n regimeMissing: 'We could not detect your regime. Choose it from the list.',\n fieldsMissing: 'We did not find some details in the document. Fill them in to continue.',\n qrPartial: 'The QR code only gave us your tax ID. Fill in the rest or upload the PDF.',\n },\n scan: {\n title: 'Scan your {document}',\n photo: 'Take a photo of the QR',\n photoHint: 'Point at the QR code of your document.',\n camera: 'Scan with the camera',\n cameraTitle: 'Point at the QR code',\n cameraGuide: 'Center the QR code inside the frame.',\n cameraStarting: 'Opening the camera…',\n cameraRead: 'Code read',\n cameraDenied: 'We could not use the camera. Choose a photo or upload the PDF.',\n cameraUnavailable: 'This device does not allow the camera here. Choose a photo or upload the PDF.',\n closeCamera: 'Close the camera',\n choosePhoto: 'Choose a photo',\n open: 'Scan {document}',\n manual: 'Enter by hand',\n close: 'Close',\n },\n errors: {\n not_pdf: 'That file is not a PDF. Upload the PDF you downloaded or enter your details by hand.',\n too_large: 'The file is larger than {size}. Upload the original PDF or enter your details by hand.',\n no_text: 'This PDF looks like a scanned image and we cannot read it. Enter your details by hand.',\n password: 'This PDF is password protected and we cannot open it. Download it again without a password or enter your details by hand.',\n unreadable: 'We could not read this file. Try another one or enter your details by hand.',\n no_qr: 'We could not find the QR code in the photo. Move closer, avoid glare or upload the PDF.',\n not_image: 'That file is not a photo we can read. Take another photo or upload the PDF.',\n },\n summaryTitleOne: 'Check 1 field to continue',\n summaryTitleMany: 'Check {n} fields to continue',\n },\n toggle: {\n label: 'I need an invoice',\n payDefault: 'Pay',\n payAndInvoice: 'Pay and invoice',\n profileTitle: 'We will invoice with these details',\n change: 'Change',\n useOther: 'Use other details',\n useSaved: 'Use my saved details',\n manualTitle: 'Invoice details',\n unavailable: 'This store does not invoice online yet.',\n privacy: 'We only use these details to issue your invoice.',\n fieldsOf: { taxId: 'Tax ID', legalName: 'Legal name', postalCode: 'Tax postal code', regime: 'Regime', use: 'Use' },\n },\n status: {\n title: 'Invoice',\n request: 'Request invoice',\n requestHelp: 'Do you need an invoice for this order?',\n awaitingTitle: 'Waiting for your payment',\n awaitingBody: 'We issue the invoice once your payment clears. Cash and bank transfers can take a while.',\n processingTitle: 'We are generating your invoice',\n processingBody: 'It usually takes under a minute. This page updates by itself.',\n processingSlow: 'Still in progress. You can close this page: your invoice will show up here when it is ready.',\n refresh: 'Refresh',\n issuedTitle: 'Your invoice is ready',\n issuedBody: 'Download it whenever you need. The links expire in 15 minutes; reopen this page for new ones.',\n folio: 'Tax folio',\n pdf: 'Download PDF',\n xml: 'Download XML',\n needsDataTitle: 'Your invoice needs more details',\n needsDataBody: 'We could not issue it with the details you gave us.',\n correct: 'Fix and retry',\n reviewTitle: 'The store is reviewing your invoice',\n reviewBody: 'It needs a review by the business. You do not need to do anything else; if it takes long, write to us.',\n contact: 'Contact the store',\n contactBy: 'Write to {contact} with your order number.',\n windowTitle: 'The invoicing period has ended',\n windowBody: 'This order is outside the period to request an invoice. If you need help, contact the store.',\n unavailableTitle: 'Invoice not available',\n unavailableBody: 'This order cannot be invoiced right now. If you need one, contact the store.',\n notEnabledTitle: 'Invoicing not active yet',\n notEnabledBody: 'The store has not turned on online invoicing yet. If you need an invoice, contact them.',\n unknownTitle: 'Invoice',\n unknownBody: 'We cannot show the invoicing status of this order right now. If you need an invoice, contact the store.',\n unreachableBody: 'We could not check invoicing right now. Try again in a few minutes or contact the store.',\n sandbox: 'Invoicing in test mode (sandbox): invoices have no fiscal validity.',\n windowNote: 'You can request it up to {days} days after the purchase.',\n cancelledTitle: 'Invoice cancelled',\n cancelledBody: 'This invoice was cancelled. If you need a new one, contact the store.',\n reasons: {\n 'invoice.tax_id': 'The tax ID is not valid.',\n 'invoice.legal_name': 'The legal name does not match your registration.',\n 'invoice.tax_regime': 'The tax regime is not valid for your details.',\n 'invoice.postal_code': 'The tax postal code is not valid.',\n 'invoice.use': 'The invoice use is not valid with your regime.',\n default: 'Check your tax details and try again.',\n },\n },\n server: {\n errors: {\n validation: 'Check this field.',\n required: 'Fill in this field.',\n invalid: 'The value is not valid.',\n },\n },\n}\n\nconst merge = <T extends Record<string, unknown>>(base: T, over: DeepPartial<T> | undefined): T => {\n if (!over) return base\n const out: Record<string, unknown> = { ...base }\n for (const [k, v] of Object.entries(over)) {\n if (v === undefined) continue\n const b = base[k]\n out[k] = typeof v === 'object' && v !== null && !Array.isArray(v) && typeof b === 'object' && b !== null ? merge(b as Record<string, unknown>, v as DeepPartial<Record<string, unknown>>) : v\n }\n return out as T\n}\n\n/** Messages for a locale (`es*` default, `en*`), with optional overrides (merged level by level). */\nexport function fiscalMessages(locale?: string | null, overrides?: DeepPartial<FiscalMessages> | null): FiscalMessages {\n const base = (locale ?? 'es-MX').toLowerCase().startsWith('en') ? EN : ES\n return overrides ? merge(base, overrides) : base\n}\n\n/** `{name}` placeholders. */\nexport function fill(template: string, vars: Record<string, string | number>): string {\n return template.replace(/\\{(\\w+)\\}/g, (m, k: string) => (k in vars ? String(vars[k]) : m))\n}\n"]}
@@ -34,6 +34,26 @@ function personTypeByLength(lengths) {
34
34
  return Object.keys(lengths).find((t) => lengths[t] === n) ?? null;
35
35
  };
36
36
  }
37
+ function personTypeFromPatterns(fid, taxId) {
38
+ const patterns = fid?.person_patterns;
39
+ if (!patterns) return null;
40
+ const v = normalizeTaxId(taxId);
41
+ for (const type of Object.keys(patterns).sort()) {
42
+ const re = compile(patterns[type]);
43
+ if (re?.test(v)) return type;
44
+ }
45
+ return null;
46
+ }
47
+ function personTypeResolver(fid, override) {
48
+ return override ?? ((taxId) => personTypeFromPatterns(fid, taxId));
49
+ }
50
+ function suggestInvoiceUse(fid, regime) {
51
+ if (!fid || !regime) return null;
52
+ const allowed = usesFor(fid, regime);
53
+ const preferred = fid.default_invoice_use;
54
+ if (preferred && allowed.some((u) => u.code === preferred)) return preferred;
55
+ return allowed.find((u) => u.suggested_for_regimes?.includes(regime))?.code ?? null;
56
+ }
37
57
  function regimesFor(fid, personType) {
38
58
  const all = fid?.regimes ?? [];
39
59
  if (!personType) return all;
@@ -44,8 +64,9 @@ function usesFor(fid, regime) {
44
64
  if (!regime) return all;
45
65
  return all.filter((u) => !u.allowed_regimes || u.allowed_regimes.length === 0 || u.allowed_regimes.includes(regime));
46
66
  }
47
- function validateFiscalValues(region, v, personTypeOf) {
67
+ function validateFiscalValues(region, v, personTypeOverride) {
48
68
  const fid = region.fiscal_id;
69
+ const personTypeOf = personTypeResolver(fid, personTypeOverride);
49
70
  const out = {};
50
71
  const tax = taxIdProblem(fid, v.tax_id ?? "");
51
72
  if (tax) out.tax_id = tax;
@@ -117,6 +138,6 @@ function summarizeExtraction(x, evidence, validRegimes) {
117
138
  };
118
139
  }
119
140
 
120
- export { entryLabel, maskTaxId, normalizeTaxId, personTypeByLength, postalCodeProblem, regimesFor, summarizeExtraction, taxIdProblem, toInvoiceRequest, usesFor, validateFiscalValues };
121
- //# sourceMappingURL=chunk-Q7UTJIKY.js.map
122
- //# sourceMappingURL=chunk-Q7UTJIKY.js.map
141
+ export { entryLabel, maskTaxId, normalizeTaxId, personTypeByLength, personTypeFromPatterns, personTypeResolver, postalCodeProblem, regimesFor, suggestInvoiceUse, summarizeExtraction, taxIdProblem, toInvoiceRequest, usesFor, validateFiscalValues };
142
+ //# sourceMappingURL=chunk-D5L7UZIA.js.map
143
+ //# sourceMappingURL=chunk-D5L7UZIA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/fiscal/format.ts","../src/fiscal/extraction.ts"],"names":[],"mappings":"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","file":"chunk-D5L7UZIA.js","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"]}
@@ -61,7 +61,7 @@ async function extractUploadedDocument(file, spec, region, options = {}) {
61
61
  }
62
62
 
63
63
  // src/fiscal/server.ts
64
- var NO_FISCAL_CONTEXT = Object.freeze({ available: false, region: null, invoicing: null });
64
+ var NO_FISCAL_CONTEXT = Object.freeze({ available: false, reason: "unreachable", region: null, invoicing: null });
65
65
  var FAILURE_TTL_MS = 5e3;
66
66
  var cache = /* @__PURE__ */ new WeakMap();
67
67
  async function loadFiscalContext(sf, options = {}) {
@@ -72,15 +72,14 @@ async function loadFiscalContext(sf, options = {}) {
72
72
  const opt = options.signal ? { signal: options.signal } : void 0;
73
73
  const [region, caps] = await Promise.allSettled([sf.region(opt), sf.customer.capabilities(opt)]);
74
74
  const reg = region.status === "fulfilled" ? region.value : null;
75
+ const noRegion = region.status === "rejected" && storefront.isStorefrontError(region.reason) && region.reason.status === 404;
75
76
  const invoicing = caps.status === "fulfilled" ? caps.value?.invoicing ?? null : null;
76
- const ok = region.status === "fulfilled" && caps.status === "fulfilled";
77
- const value = {
78
- available: !!reg?.fiscal_id && !!invoicing?.available,
79
- region: reg,
80
- invoicing: invoicing?.available ? invoicing : null
81
- };
82
- cache.set(sf, { at: now(), ok, value: ok ? value : value.available ? value : NO_FISCAL_CONTEXT });
83
- return ok || value.available ? value : NO_FISCAL_CONTEXT;
77
+ const ok = (region.status === "fulfilled" || noRegion) && caps.status === "fulfilled";
78
+ const available = !!reg?.fiscal_id && !!invoicing?.available;
79
+ const reason = available ? "available" : !ok ? "unreachable" : noRegion || !reg?.fiscal_id ? invoicing && !invoicing.available ? "off" : "no_region" : !invoicing ? "unknown" : "off";
80
+ const value = { available, reason, region: reg, invoicing: invoicing?.available ? invoicing : null };
81
+ cache.set(sf, { at: now(), ok, value: ok || available ? value : NO_FISCAL_CONTEXT });
82
+ return ok || available ? value : NO_FISCAL_CONTEXT;
84
83
  }
85
84
  var EVIDENCE_SOURCES = /* @__PURE__ */ new Set(["pdf_client", "pdf_server", "qr", "manual"]);
86
85
  function parseEvidence(raw) {
@@ -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","repairPath","isStorefrontError"],"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/** 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 personTypeOf?: (taxId: string) => string | null,\n): Partial<Record<FiscalFieldName, FiscalProblem>> {\n const fid = region.fiscal_id\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 } from './types.js'\n\nexport type { FiscalActionData, FiscalErrorCode, FiscalIntent } 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 region: Region | null\n invoicing: InvoicingCapabilities | null\n}\n\n/** What is assumed when the store cannot be asked: no invoicing, nothing breaks. */\nexport const NO_FISCAL_CONTEXT: Readonly<FiscalContext> = Object.freeze({ available: false, 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. Use it as the loader: `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 invoicing = caps.status === 'fulfilled' ? (caps.value?.invoicing ?? null) : null\n const ok = region.status === 'fulfilled' && caps.status === 'fulfilled'\n const value: FiscalContext = {\n available: !!reg?.fiscal_id && !!invoicing?.available,\n region: reg,\n invoicing: invoicing?.available ? invoicing : null,\n }\n cache.set(sf, { at: now(), ok, value: ok ? value : value.available ? value : NO_FISCAL_CONTEXT })\n return ok || value.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/** `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,6 +1,6 @@
1
1
  import { IdentityDocumentSpec, Region, StorefrontClient, InvoicingCapabilities, InvoiceRequest, FiscalDocumentEvidence } from '@asteby/storefront';
2
- import { b as FiscalExtractionSummary, F as FiscalActionData } from './types-BnvSbV6t.cjs';
3
- export { a as FiscalErrorCode, e as FiscalIntent } from './types-BnvSbV6t.cjs';
2
+ import { b as FiscalExtractionSummary, g as FiscalReason, F as FiscalActionData } from './types-Cgx_65eM.cjs';
3
+ export { a as FiscalErrorCode, e as FiscalIntent } from './types-Cgx_65eM.cjs';
4
4
 
5
5
  /**
6
6
  * Reads a tax document uploaded without JavaScript, on the server, with the same engine the browser uses. In memory
@@ -31,10 +31,12 @@ declare function extractUploadedDocument(file: Blob, spec: IdentityDocumentSpec,
31
31
  type FiscalContext = {
32
32
  /** The store can invoice: it has a region with a fiscal catalog and `capabilities.invoicing.available`. */
33
33
  available: boolean;
34
+ /** Why `available` is what it is. Absent on contexts built by older code: read it as `unknown`. */
35
+ reason?: FiscalReason;
34
36
  region: Region | null;
35
37
  invoicing: InvoicingCapabilities | null;
36
38
  };
37
- /** What is assumed when the store cannot be asked: no invoicing, nothing breaks. */
39
+ /** What is assumed when the store cannot be asked: nothing is known (`unreachable`), nothing breaks. */
38
40
  declare const NO_FISCAL_CONTEXT: Readonly<FiscalContext>;
39
41
  type LoadFiscalContextOptions = {
40
42
  /** How long a good answer is reused per client (default 60 s). `0` disables the cache. */
@@ -45,7 +47,8 @@ type LoadFiscalContextOptions = {
45
47
  /**
46
48
  * Asks the store for its region and what invoicing it offers, with a short cache per client. It NEVER throws: when OPS
47
49
  * does not answer (older server, outage, token without scope) it returns `NO_FISCAL_CONTEXT` and retries after a few
48
- * seconds, so a checkout without invoicing keeps working. Use it as the loader: `loader = () => loadFiscalContext(sf)`.
50
+ * seconds, so a checkout without invoicing keeps working. `reason` says which of the cases it is. Use it as the loader:
51
+ * `loader = () => loadFiscalContext(sf)`.
49
52
  */
50
53
  declare function loadFiscalContext(sf: StorefrontClient, options?: LoadFiscalContextOptions): Promise<FiscalContext>;
51
54
  /** What the action needs of a cookie to read the buyer's session. React Router's `createCookie(...)` fits as it is. */
@@ -94,4 +97,4 @@ declare function createInvoiceAction(options: CreateInvoiceActionOptions): ({ re
94
97
  }) => Promise<FiscalActionData>;
95
98
  type InvoiceAction = ReturnType<typeof createInvoiceAction>;
96
99
 
97
- export { type CreateInvoiceActionOptions, FiscalActionData, type FiscalContext, type InvoiceAction, type LoadFiscalContextOptions, NO_FISCAL_CONTEXT, type SessionCookie, UPLOAD_LIMITS, type UploadLimits, createInvoiceAction, extractUploadedDocument, invoiceFromForm, loadFiscalContext, parseEvidence };
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 };