@formfeed/engine 0.3.11 → 0.3.13

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.
Files changed (47) hide show
  1. package/index.cjs +2872 -280
  2. package/index.js +2872 -280
  3. package/package.json +3 -2
  4. package/types/frame/audit.d.cts +15 -0
  5. package/types/frame/audit.d.ts +15 -0
  6. package/types/frame/audit.entry.d.cts +1 -0
  7. package/types/frame/audit.entry.d.ts +1 -0
  8. package/types/frame/text-edit.d.cts +8 -1
  9. package/types/frame/text-edit.d.ts +8 -1
  10. package/types/index.d.cts +8 -1
  11. package/types/index.d.ts +8 -1
  12. package/types/lib/accessibility/index.d.cts +201 -0
  13. package/types/lib/accessibility/index.d.ts +201 -0
  14. package/types/lib/accessibility/static.d.cts +40 -0
  15. package/types/lib/accessibility/static.d.ts +40 -0
  16. package/types/lib/assemble.d.cts +16 -0
  17. package/types/lib/assemble.d.ts +16 -0
  18. package/types/lib/einvoice/check.d.cts +181 -0
  19. package/types/lib/einvoice/check.d.ts +181 -0
  20. package/types/lib/einvoice/cii.d.cts +18 -0
  21. package/types/lib/einvoice/cii.d.ts +18 -0
  22. package/types/lib/einvoice/codes.generated.d.cts +14 -0
  23. package/types/lib/einvoice/codes.generated.d.ts +14 -0
  24. package/types/lib/einvoice/decimal.d.cts +29 -0
  25. package/types/lib/einvoice/decimal.d.ts +29 -0
  26. package/types/lib/einvoice/display.d.cts +43 -0
  27. package/types/lib/einvoice/display.d.ts +43 -0
  28. package/types/lib/einvoice/html-text.d.cts +17 -0
  29. package/types/lib/einvoice/html-text.d.ts +17 -0
  30. package/types/lib/einvoice/index.d.cts +45 -0
  31. package/types/lib/einvoice/index.d.ts +45 -0
  32. package/types/lib/einvoice/locate.d.cts +21 -0
  33. package/types/lib/einvoice/locate.d.ts +21 -0
  34. package/types/lib/einvoice/report.d.cts +59 -0
  35. package/types/lib/einvoice/report.d.ts +59 -0
  36. package/types/lib/einvoice/schema.d.cts +293 -0
  37. package/types/lib/einvoice/schema.d.ts +293 -0
  38. package/types/lib/einvoice/skeleton.d.cts +10 -0
  39. package/types/lib/einvoice/skeleton.d.ts +10 -0
  40. package/types/lib/generated/frames.d.cts +4 -2
  41. package/types/lib/generated/frames.d.ts +4 -2
  42. package/types/lib/helpers/document.d.cts +5 -0
  43. package/types/lib/helpers/document.d.ts +5 -0
  44. package/types/lib/preview.d.cts +11 -0
  45. package/types/lib/preview.d.ts +11 -0
  46. package/types/lib/source-edit.d.cts +5 -0
  47. package/types/lib/source-edit.d.ts +5 -0
package/index.cjs CHANGED
@@ -30,16 +30,42 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // packages/engine/src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ ACCESSIBILITY_HEADER_LENGTH: () => ACCESSIBILITY_HEADER_LENGTH,
34
+ ACCESSIBILITY_MAX_ELEMENTS: () => ACCESSIBILITY_MAX_ELEMENTS,
35
+ ACCESSIBILITY_MAX_FAILURES: () => ACCESSIBILITY_MAX_FAILURES,
36
+ AUDIT_CODES: () => AUDIT_CODES,
37
+ AUDIT_RULES: () => AUDIT_RULES,
38
+ AUDIT_SEVERITY: () => AUDIT_SEVERITY,
39
+ AUDIT_SNIPPET_LENGTH: () => AUDIT_SNIPPET_LENGTH,
40
+ AUDIT_TITLES: () => AUDIT_TITLES,
41
+ CII_GUIDELINES: () => CII_GUIDELINES,
42
+ CODE_LIST_RELEASE: () => CODE_LIST_RELEASE,
33
43
  DEFAULT_CHROME_PADDING: () => DEFAULT_CHROME_PADDING,
34
44
  DESIGN_LIMITS: () => DESIGN_LIMITS,
35
45
  DESIGN_VERSION: () => DESIGN_VERSION,
46
+ DISPLAY_TERMS: () => DISPLAY_TERMS,
36
47
  DefaultHelperRegistry: () => DefaultHelperRegistry,
48
+ EINVOICE_FLAVOURS: () => EINVOICE_FLAVOURS,
49
+ EINVOICE_FLAVOUR_NAMES: () => EINVOICE_FLAVOUR_NAMES,
50
+ EINVOICE_MAX_MESSAGES: () => EINVOICE_MAX_MESSAGES,
51
+ EINVOICE_PROFILES: () => EINVOICE_PROFILES,
52
+ EINVOICE_PROFILE_NAMES: () => EINVOICE_PROFILE_NAMES,
53
+ EINVOICE_SPEC_VERSIONS: () => EINVOICE_SPEC_VERSIONS,
54
+ EINVOICE_XML_NAME: () => EINVOICE_XML_NAME,
37
55
  EPC_MAX_BYTES: () => EPC_MAX_BYTES,
38
56
  EngineSyntaxError: () => EngineSyntaxError,
57
+ INVOICE_DATA_KEY: () => INVOICE_DATA_KEY,
39
58
  OFFICE_FILLED_MAX_BYTES: () => OFFICE_FILLED_MAX_BYTES,
40
59
  OfficeTemplateError: () => OfficeTemplateError,
60
+ PDFUA_STANDARD: () => PDFUA_STANDARD,
41
61
  RenderError: () => RenderError,
42
62
  RenderLimitError: () => RenderLimitError,
63
+ UA_CHECKS: () => UA_CHECKS,
64
+ UA_REFUSALS: () => UA_REFUSALS,
65
+ VAT_CATEGORIES: () => VAT_CATEGORIES,
66
+ accessibilityHeader: () => accessibilityHeader,
67
+ accessibilityHeaderValue: () => accessibilityHeaderValue,
68
+ accessibilityResult: () => accessibilityResult,
43
69
  analyzeHandlebars: () => analyzeHandlebars,
44
70
  analyzeJinja2: () => analyzeJinja2,
45
71
  analyzeLiquid: () => analyzeLiquid,
@@ -48,16 +74,21 @@ __export(index_exports, {
48
74
  apitemplateRegions: () => apitemplateRegions,
49
75
  applyRegionEdit: () => applyRegionEdit,
50
76
  assembleDocument: () => assembleDocument,
77
+ auditMessage: () => auditMessage,
78
+ auditRuntime: () => auditRuntime,
51
79
  brandCss: () => brandCss,
52
80
  builtinHelpers: () => builtinHelpers,
53
81
  canvasDocument: () => canvasDocument,
82
+ capMessages: () => capMessages,
54
83
  chartInitScript: () => chartInitScript,
55
84
  chartMarkup: () => chartMarkup,
56
85
  chartPayload: () => chartPayload,
86
+ checkInvoice: () => checkInvoice,
57
87
  chipValues: () => chipValues,
58
88
  convertScss: () => convertScss,
59
89
  createHelperRegistry: () => createHelperRegistry,
60
90
  dataPathPattern: () => dataPathPattern,
91
+ defaultEinvoiceOptions: () => defaultEinvoiceOptions,
61
92
  defaultHelpers: () => defaultHelpers,
62
93
  defaultLayer: () => defaultLayer,
63
94
  defaultLimits: () => defaultLimits,
@@ -68,9 +99,14 @@ __export(index_exports, {
68
99
  designOutputHash: () => designOutputHash,
69
100
  designStarters: () => designStarters,
70
101
  diffSchemas: () => diffSchemas,
102
+ displayCheck: () => displayCheck,
103
+ displayMissingText: () => displayMissingText,
71
104
  editAttribute: () => editAttribute,
72
105
  editableRegion: () => editableRegion,
73
106
  editableRegionAt: () => editableRegionAt,
107
+ einvoiceOptionsSchema: () => einvoiceOptionsSchema,
108
+ einvoiceResult: () => einvoiceResult,
109
+ einvoiceXml: () => einvoiceXml,
74
110
  emitDesign: () => emitDesign,
75
111
  emptyBrand: () => emptyBrand,
76
112
  emptyDesign: () => emptyDesign,
@@ -90,6 +126,7 @@ __export(index_exports, {
90
126
  googleFontsUrl: () => googleFontsUrl,
91
127
  handlebarsEngine: () => handlebarsEngine,
92
128
  helperDocs: () => helperDocs,
129
+ htmlText: () => htmlText,
93
130
  ibanChecksum: () => ibanChecksum,
94
131
  imageIsEmpty: () => imageIsEmpty,
95
132
  imageMarkup: () => imageMarkup,
@@ -100,11 +137,18 @@ __export(index_exports, {
100
137
  importPdfmonkey: () => importPdfmonkey,
101
138
  inferSchema: () => inferSchema,
102
139
  inferSchemaFromDataSets: () => inferSchemaFromDataSets,
140
+ invoiceAmountText: () => invoiceAmountText,
141
+ invoiceLineOf: () => invoiceLineOf,
142
+ invoicePathSegments: () => invoicePathSegments,
143
+ invoiceSchema: () => invoiceSchema,
144
+ invoiceSkeleton: () => invoiceSkeleton,
145
+ invoiceToCii: () => invoiceToCii,
103
146
  isApitemplateHtmlTemplate: () => isApitemplateHtmlTemplate,
104
147
  isApitemplateRegion: () => isApitemplateRegion,
105
148
  isOfficeKind: () => isOfficeKind,
106
149
  isOutputFormat: () => isOutputFormat,
107
150
  isScss: () => isScss,
151
+ isUaSetting: () => isUaSetting,
108
152
  jinja2Engine: () => jinja2Engine,
109
153
  jinjaTags: () => jinjaTags,
110
154
  jsreportTemplates: () => jsreportTemplates,
@@ -113,6 +157,7 @@ __export(index_exports, {
113
157
  liquidBuiltinFilters: () => liquidBuiltinFilters,
114
158
  liquidEngine: () => liquidEngine,
115
159
  liquidTags: () => liquidTags,
160
+ locateJsonPath: () => locateJsonPath,
116
161
  mergeSampleData: () => mergeSampleData,
117
162
  mergeSettings: () => mergeSettings,
118
163
  mergeSnippetCss: () => mergeSnippetCss,
@@ -139,12 +184,16 @@ __export(index_exports, {
139
184
  readJsreportExport: () => readJsreportExport,
140
185
  regionChips: () => regionChips,
141
186
  regionItems: () => regionItems,
187
+ regionPosition: () => regionPosition,
142
188
  renderOffice: () => renderOffice,
143
189
  renderVersion: () => renderVersion,
190
+ resolveEinvoiceOptions: () => resolveEinvoiceOptions,
144
191
  resolvePath: () => resolvePath,
145
192
  resolveTranslation: () => resolveTranslation,
193
+ resolveUa: () => resolveUa,
146
194
  scanBlocks: () => scanBlocks,
147
195
  schemaPaths: () => schemaPaths,
196
+ shownAmounts: () => shownAmounts,
148
197
  slugFromName: () => slugFromName,
149
198
  snippetText: () => snippetText,
150
199
  snippets: () => snippets,
@@ -153,6 +202,8 @@ __export(index_exports, {
153
202
  splitApitemplateCss: () => splitApitemplateCss,
154
203
  starterDocument: () => starterDocument,
155
204
  starterPresentation: () => starterPresentation,
205
+ staticAudit: () => staticAudit,
206
+ staticAuditDiagnostics: () => staticAuditDiagnostics,
156
207
  strftimeToDateFns: () => strftimeToDateFns,
157
208
  svgDataUri: () => svgDataUri,
158
209
  toDate: () => toDate,
@@ -167,10 +218,10 @@ module.exports = __toCommonJS(index_exports);
167
218
 
168
219
  // packages/engine/src/lib/errors.ts
169
220
  var EngineSyntaxError = class extends Error {
170
- constructor(engine, message, line, column) {
221
+ constructor(engine, message, line2, column) {
171
222
  super(message);
172
223
  this.engine = engine;
173
- this.line = line;
224
+ this.line = line2;
174
225
  this.column = column;
175
226
  }
176
227
  name = "EngineSyntaxError";
@@ -186,10 +237,10 @@ var RenderLimitError = class extends Error {
186
237
  name = "RenderLimitError";
187
238
  };
188
239
  var RenderError = class extends Error {
189
- constructor(engine, message, line, column) {
240
+ constructor(engine, message, line2, column) {
190
241
  super(message);
191
242
  this.engine = engine;
192
- this.line = line;
243
+ this.line = line2;
193
244
  this.column = column;
194
245
  }
195
246
  name = "RenderError";
@@ -258,7 +309,7 @@ function toNumber(value) {
258
309
 
259
310
  // packages/engine/src/lib/codes/epc.ts
260
311
  function epcPayload(input) {
261
- const amount = input.amount === void 0 || input.amount === "" ? "" : `EUR${toNumber(input.amount).toFixed(2)}`;
312
+ const amount2 = input.amount === void 0 || input.amount === "" ? "" : `EUR${toNumber(input.amount).toFixed(2)}`;
262
313
  return [
263
314
  "BCD",
264
315
  "002",
@@ -267,7 +318,7 @@ function epcPayload(input) {
267
318
  (input.bic ?? "").replace(/\s/g, ""),
268
319
  String(input.name ?? "").slice(0, 70),
269
320
  String(input.iban ?? "").replace(/\s/g, ""),
270
- amount,
321
+ amount2,
271
322
  (input.purpose ?? "").slice(0, 4),
272
323
  (input.reference ?? "").slice(0, 35),
273
324
  input.reference ? "" : (input.text ?? "").slice(0, 140),
@@ -317,9 +368,9 @@ var IBAN_LENGTHS = {
317
368
  };
318
369
  var EPC_MAX_BYTES = 331;
319
370
  var byteLength = (value) => new TextEncoder().encode(value).length;
320
- var normaliseIban = (iban) => iban.replace(/\s/g, "").toUpperCase();
321
- function ibanChecksum(iban) {
322
- const rearranged = normaliseIban(iban).slice(4) + normaliseIban(iban).slice(0, 4);
371
+ var normaliseIban = (iban2) => iban2.replace(/\s/g, "").toUpperCase();
372
+ function ibanChecksum(iban2) {
373
+ const rearranged = normaliseIban(iban2).slice(4) + normaliseIban(iban2).slice(0, 4);
323
374
  const digits = [...rearranged].map((c) => c >= "A" && c <= "Z" ? String(c.charCodeAt(0) - 55) : c).join("");
324
375
  let remainder = 0;
325
376
  for (const digit of digits) remainder = (remainder * 10 + Number(digit)) % 97;
@@ -330,30 +381,30 @@ function validateEpc(input) {
330
381
  const name = String(input.name ?? "").trim();
331
382
  if (!name) problems.push({ field: "name", code: "required" });
332
383
  else if (name.length > 70) problems.push({ field: "name", code: "too_long", max: 70, actual: name.length });
333
- const iban = normaliseIban(String(input.iban ?? ""));
334
- if (!iban) problems.push({ field: "iban", code: "required" });
335
- else if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$/.test(iban)) problems.push({ field: "iban", code: "iban_characters" });
384
+ const iban2 = normaliseIban(String(input.iban ?? ""));
385
+ if (!iban2) problems.push({ field: "iban", code: "required" });
386
+ else if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$/.test(iban2)) problems.push({ field: "iban", code: "iban_characters" });
336
387
  else {
337
- const expected = IBAN_LENGTHS[iban.slice(0, 2)];
338
- if (expected !== void 0 && iban.length !== expected)
339
- problems.push({ field: "iban", code: "iban_country_length", max: expected, actual: iban.length });
340
- else if (!ibanChecksum(iban)) problems.push({ field: "iban", code: "iban_checksum" });
388
+ const expected = IBAN_LENGTHS[iban2.slice(0, 2)];
389
+ if (expected !== void 0 && iban2.length !== expected)
390
+ problems.push({ field: "iban", code: "iban_country_length", max: expected, actual: iban2.length });
391
+ else if (!ibanChecksum(iban2)) problems.push({ field: "iban", code: "iban_checksum" });
341
392
  }
342
393
  const bic = String(input.bic ?? "").replace(/\s/g, "").toUpperCase();
343
394
  if (bic && !/^[A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?$/.test(bic)) problems.push({ field: "bic", code: "bic_format" });
344
395
  if (input.amount !== void 0 && input.amount !== "") {
345
- const amount = toNumber(input.amount);
346
- if (Number.isNaN(amount)) problems.push({ field: "amount", code: "not_a_number" });
347
- else if (amount < 0.01 || amount > 99999999999e-2) problems.push({ field: "amount", code: "out_of_range" });
396
+ const amount2 = toNumber(input.amount);
397
+ if (Number.isNaN(amount2)) problems.push({ field: "amount", code: "not_a_number" });
398
+ else if (amount2 < 0.01 || amount2 > 99999999999e-2) problems.push({ field: "amount", code: "out_of_range" });
348
399
  }
349
400
  const purpose = String(input.purpose ?? "");
350
401
  if (purpose.length > 4) problems.push({ field: "purpose", code: "too_long", max: 4, actual: purpose.length });
351
402
  const reference = String(input.reference ?? "").trim();
352
- const text2 = String(input.text ?? "").trim();
353
- if (reference && text2) problems.push({ field: "reference", code: "reference_or_text" });
403
+ const text4 = String(input.text ?? "").trim();
404
+ if (reference && text4) problems.push({ field: "reference", code: "reference_or_text" });
354
405
  if (reference.length > 35)
355
406
  problems.push({ field: "reference", code: "too_long", max: 35, actual: reference.length });
356
- if (text2.length > 140) problems.push({ field: "text", code: "too_long", max: 140, actual: text2.length });
407
+ if (text4.length > 140) problems.push({ field: "text", code: "too_long", max: 140, actual: text4.length });
357
408
  const bytes = byteLength(epcPayload(input));
358
409
  if (bytes > EPC_MAX_BYTES)
359
410
  problems.push({ field: "payload", code: "too_long", max: EPC_MAX_BYTES, actual: bytes });
@@ -391,9 +442,12 @@ var barcodeTypes = {
391
442
  };
392
443
  var ownTextLayout = /* @__PURE__ */ new Set(["ean13", "ean8", "upca"]);
393
444
  function codeText(helper, value) {
394
- const text2 = value === void 0 || value === null ? "" : String(value);
395
- if (text2 === "") throw new Error(`${helper}: nothing to encode, the value is empty or missing from the data`);
396
- return text2;
445
+ const text4 = value === void 0 || value === null ? "" : String(value);
446
+ if (text4 === "")
447
+ throw new Error(
448
+ `${helper}: nothing to encode, the value is empty or missing from the data`
449
+ );
450
+ return text4;
397
451
  }
398
452
  var codeHelpers = [
399
453
  {
@@ -401,18 +455,25 @@ var codeHelpers = [
401
455
  aliases: ["qr"],
402
456
  doc: {
403
457
  signature: "qrcode(value, { size = 160, ecc = 'M', margin = 1, color, background })",
404
- description: "QR code as an SVG data URI for an <img> src.",
458
+ description: "QR code as an SVG data URI for an <img> src. Give the image an `alt` that says where the code leads: a screen reader cannot scan it.",
405
459
  examples: {
406
- jinja2: '<img src="{{ qrcode(order.url, { size: 120 }) }}">',
407
- liquid: '<img src="{{ order.url | qrcode: size: 120 }}">',
408
- handlebars: '<img src="{{qrcode order.url size=120}}">'
460
+ jinja2: '<img src="{{ qrcode(order.url, { size: 120 }) }}" alt="QR code: {{ order.url }}">',
461
+ liquid: '<img src="{{ order.url | qrcode: size: 120 }}" alt="QR code: {{ order.url }}">',
462
+ handlebars: '<img src="{{qrcode order.url size=120}}" alt="QR code: {{order.url}}">'
409
463
  },
410
464
  category: "code"
411
465
  },
412
466
  fn: (ctx, value, options) => {
413
467
  const o = options ?? {};
414
468
  const svg = qrSvg(codeText("qrcode", value), o);
415
- if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, width: o.size, height: o.size, alt: "QR code" });
469
+ if (ctx.drawing)
470
+ return ctx.drawing({
471
+ kind: "svg",
472
+ svg,
473
+ width: o.size,
474
+ height: o.size,
475
+ alt: "QR code"
476
+ });
416
477
  return svgDataUri(svg);
417
478
  }
418
479
  },
@@ -420,28 +481,29 @@ var codeHelpers = [
420
481
  name: "barcode",
421
482
  doc: {
422
483
  signature: "barcode(value, { type = 'code128', height = 12, width, text = true, scale = 2 })",
423
- description: "Barcode (code128, ean13, ean8, upc, itf14, code39, datamatrix, pdf417) as an SVG data URI.",
484
+ description: "Barcode (code128, ean13, ean8, upc, itf14, code39, datamatrix, pdf417) as an SVG data URI. Give the image an `alt` with the value it encodes.",
424
485
  examples: {
425
- jinja2: `<img src="{{ barcode(item.sku, { type: 'code128' }) }}">`,
426
- liquid: `<img src="{{ item.sku | barcode: type: 'code128' }}">`,
427
- handlebars: `<img src="{{barcode item.sku type='code128'}}">`
486
+ jinja2: `<img src="{{ barcode(item.sku, { type: 'code128' }) }}" alt="Barcode {{ item.sku }}">`,
487
+ liquid: `<img src="{{ item.sku | barcode: type: 'code128' }}" alt="Barcode {{ item.sku }}">`,
488
+ handlebars: `<img src="{{barcode item.sku type='code128'}}" alt="Barcode {{item.sku}}">`
428
489
  },
429
490
  category: "code"
430
491
  },
431
492
  fn: (ctx, value, options) => {
432
493
  const o = options ?? {};
433
494
  const bcid = barcodeTypes[String(o.type ?? "code128").toLowerCase()] ?? "code128";
434
- const text2 = codeText("barcode", value);
495
+ const text4 = codeText("barcode", value);
435
496
  const svg = (0, import_bwip_js.toSVG)({
436
497
  bcid,
437
- text: text2,
498
+ text: text4,
438
499
  height: o.height ?? 12,
439
500
  ...o.width === void 0 ? {} : { width: o.width },
440
501
  scale: o.scale ?? 2,
441
502
  includetext: o.text !== false,
442
503
  ...ownTextLayout.has(bcid) ? {} : { textxalign: "center" }
443
504
  });
444
- if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, alt: `Barcode ${text2}` });
505
+ if (ctx.drawing)
506
+ return ctx.drawing({ kind: "svg", svg, alt: `Barcode ${text4}` });
445
507
  return svgDataUri(svg);
446
508
  }
447
509
  },
@@ -450,11 +512,11 @@ var codeHelpers = [
450
512
  aliases: ["epc_qr", "girocode"],
451
513
  doc: {
452
514
  signature: "epcQr({ name, iban, bic?, amount?, reference?, text? }, { size })",
453
- description: "SEPA credit transfer QR code (EPC069-12 / GiroCode) that banking apps scan to prefill a payment.",
515
+ description: "SEPA credit transfer QR code (EPC069-12 / GiroCode) that banking apps scan to prefill a payment. Give the image an `alt`, and print the bank details as text as well: the code is a picture.",
454
516
  examples: {
455
- jinja2: '<img src="{{ epcQr({ name: company.name, iban: company.iban, amount: invoice.total, reference: invoice.number }) }}">',
456
- liquid: '<img src="{{ company.iban | epcQr: name: company.name, amount: invoice.total, reference: invoice.number }}">',
457
- handlebars: '<img src="{{epcQr name=company.name iban=company.iban amount=invoice.total reference=invoice.number}}">'
517
+ jinja2: '<img src="{{ epcQr({ name: company.name, iban: company.iban, amount: invoice.total, reference: invoice.number }) }}" alt="QR code to pay invoice {{ invoice.number }}">',
518
+ liquid: '<img src="{{ company.iban | epcQr: name: company.name, amount: invoice.total, reference: invoice.number }}" alt="QR code to pay invoice {{ invoice.number }}">',
519
+ handlebars: '<img src="{{epcQr name=company.name iban=company.iban amount=invoice.total reference=invoice.number}}" alt="QR code to pay invoice {{invoice.number}}">'
458
520
  },
459
521
  category: "code"
460
522
  },
@@ -467,18 +529,33 @@ var codeHelpers = [
467
529
  ...input && typeof input === "object" ? input : typeof input === "string" && input ? { iban: input } : {},
468
530
  ...options ?? {}
469
531
  };
470
- const payment = {};
532
+ const payment2 = {};
471
533
  const qr = {};
472
534
  for (const [key, value] of Object.entries(fields))
473
- (EPC_FIELDS.has(key) ? payment : qr)[key] = value;
535
+ (EPC_FIELDS.has(key) ? payment2 : qr)[key] = value;
474
536
  const o = { ecc: "M", ...qr };
475
- const svg = qrSvg(epcPayload(payment), o);
476
- if (ctx.drawing) return ctx.drawing({ kind: "svg", svg, width: o.size, height: o.size, alt: "GiroCode" });
537
+ const svg = qrSvg(epcPayload(payment2), o);
538
+ if (ctx.drawing)
539
+ return ctx.drawing({
540
+ kind: "svg",
541
+ svg,
542
+ width: o.size,
543
+ height: o.size,
544
+ alt: "GiroCode"
545
+ });
477
546
  return svgDataUri(svg);
478
547
  }
479
548
  }
480
549
  ];
481
- var EPC_FIELDS = /* @__PURE__ */ new Set(["name", "iban", "bic", "amount", "reference", "text", "purpose"]);
550
+ var EPC_FIELDS = /* @__PURE__ */ new Set([
551
+ "name",
552
+ "iban",
553
+ "bic",
554
+ "amount",
555
+ "reference",
556
+ "text",
557
+ "purpose"
558
+ ]);
482
559
 
483
560
  // packages/engine/src/lib/helpers/format.ts
484
561
  var import_tz = require("@date-fns/tz");
@@ -496,8 +573,8 @@ var import_nl = require("n2words/nl");
496
573
  function resolveTranslation(dictionaries, locale, key) {
497
574
  const lang = locale.split("-")[0] ?? locale;
498
575
  for (const candidate of [locale, lang, "en"]) {
499
- const text2 = dictionaries?.[candidate]?.[key];
500
- if (typeof text2 === "string") return { text: text2, from: candidate };
576
+ const text4 = dictionaries?.[candidate]?.[key];
577
+ if (typeof text4 === "string") return { text: text4, from: candidate };
501
578
  }
502
579
  return { text: key, from: null };
503
580
  }
@@ -645,10 +722,10 @@ var formatHelpers = [
645
722
  },
646
723
  category: "format"
647
724
  },
648
- fn: (ctx, value, amount, unit) => {
725
+ fn: (ctx, value, amount2, unit) => {
649
726
  const d = toDate(value, ctx.timezone);
650
727
  if (!d) return str(value);
651
- const n = toNumber(amount);
728
+ const n = toNumber(amount2);
652
729
  const u = str(unit || "days").replace(/s?$/, "s");
653
730
  return (0, import_date_fns.add)(d, { [u]: n }).toISOString();
654
731
  }
@@ -977,9 +1054,9 @@ var formatHelpers = [
977
1054
  category: "text"
978
1055
  },
979
1056
  fn: (ctx, key, params) => {
980
- const { text: text2 } = resolveTranslation(ctx.i18n, ctx.locale, str(key));
1057
+ const { text: text4 } = resolveTranslation(ctx.i18n, ctx.locale, str(key));
981
1058
  const p = params ?? {};
982
- return text2.replace(/\{(\w+)\}/g, (_, name) => str(p[name]));
1059
+ return text4.replace(/\{(\w+)\}/g, (_, name) => str(p[name]));
983
1060
  }
984
1061
  },
985
1062
  {
@@ -1250,7 +1327,16 @@ var collectionHelpers = [
1250
1327
  ];
1251
1328
 
1252
1329
  // packages/engine/src/lib/helpers/document.ts
1253
- var CHART_PALETTE = ["#3E63DD", "#12A594", "#E5484D", "#F76B15", "#8E4EC6", "#FFC53D", "#0090FF", "#978365"];
1330
+ var CHART_PALETTE = [
1331
+ "#3E63DD",
1332
+ "#12A594",
1333
+ "#E5484D",
1334
+ "#F76B15",
1335
+ "#8E4EC6",
1336
+ "#FFC53D",
1337
+ "#0090FF",
1338
+ "#978365"
1339
+ ];
1254
1340
  var SLICED = /* @__PURE__ */ new Set(["pie", "doughnut", "polarArea"]);
1255
1341
  var colour = (value) => typeof value === "string" && value ? value : Array.isArray(value) && value.length ? value.map(String) : void 0;
1256
1342
  function chartDataFromPlain(spec) {
@@ -1264,8 +1350,13 @@ function chartDataFromPlain(spec) {
1264
1350
  });
1265
1351
  const own = colour(s.color);
1266
1352
  const fill = SLICED.has(type) ? own ?? labels.map((_, i) => CHART_PALETTE[i % CHART_PALETTE.length]) : own ?? CHART_PALETTE[index % CHART_PALETTE.length];
1267
- const dataset = { data, backgroundColor: fill, borderColor: fill };
1268
- if (s.label !== void 0 && s.label !== null && s.label !== "") dataset["label"] = String(s.label);
1353
+ const dataset = {
1354
+ data,
1355
+ backgroundColor: fill,
1356
+ borderColor: fill
1357
+ };
1358
+ if (s.label !== void 0 && s.label !== null && s.label !== "")
1359
+ dataset["label"] = String(s.label);
1269
1360
  if (type === "line") dataset["fill"] = false;
1270
1361
  return dataset;
1271
1362
  });
@@ -1282,7 +1373,8 @@ function chartPayload(spec) {
1282
1373
  const options = { ...spec.options ?? {} };
1283
1374
  options["animation"] = false;
1284
1375
  options["responsive"] = false;
1285
- if (!isPlain(spec)) return { type: spec.type ?? "bar", data: spec.data ?? {}, options };
1376
+ if (!isPlain(spec))
1377
+ return { type: spec.type ?? "bar", data: spec.data ?? {}, options };
1286
1378
  const data = chartDataFromPlain(spec);
1287
1379
  const unnamed = data.datasets.length === 1 && data.datasets[0]?.["label"] === void 0;
1288
1380
  const plugins = asObject(options["plugins"]);
@@ -1295,7 +1387,8 @@ function chartMarkup(spec, id) {
1295
1387
  const height = toNumber(spec.height ?? 240) || 240;
1296
1388
  const payload = escapeHtml(JSON.stringify(chartPayload(spec)));
1297
1389
  const idAttr = id ? ` id="${escapeHtml(id)}"` : "";
1298
- return `<canvas class="ff-chart"${idAttr} width="${width}" height="${height}" style="width:${width}px;height:${height}px" data-ff-chart="${payload}"></canvas>`;
1390
+ const alt = typeof spec.alt === "string" && spec.alt.trim() ? ` role="img" aria-label="${escapeHtml(spec.alt.trim())}"` : "";
1391
+ return `<canvas class="ff-chart"${idAttr}${alt} width="${width}" height="${height}" style="width:${width}px;height:${height}px" data-ff-chart="${payload}"></canvas>`;
1299
1392
  }
1300
1393
  function imageMarkup(url, options = {}) {
1301
1394
  const styles = [
@@ -1316,18 +1409,21 @@ var documentHelpers = [
1316
1409
  name: "chart",
1317
1410
  html: true,
1318
1411
  doc: {
1319
- signature: "chart({ type, labels, values, label, color } | { type, labels, series } | { type, data, options }, { width, height })",
1320
- description: "Draws a Chart.js chart (bar, line, pie, doughnut, radar, \u2026) from plain data, `labels` with `values` or several `series` of `{ label, values, color }`, or from a full Chart.js `data` and `options`; animations are disabled so PDF and preview match.",
1412
+ signature: "chart({ type, labels, values, label, color } | { type, labels, series } | { type, data, options }, { width, height, alt })",
1413
+ description: "Draws a Chart.js chart (bar, line, pie, doughnut, radar, \u2026) from plain data, `labels` with `values` or several `series` of `{ label, values, color }`, or from a full Chart.js `data` and `options`; animations are disabled so PDF and preview match. `alt` says in words what the chart shows: a screen reader reads it in place of the picture, and a tagged PDF needs it.",
1321
1414
  examples: {
1322
- jinja2: "{{ chart({ type: 'line', labels: sales.labels, values: sales.quarters, width: 480 }) }}",
1323
- liquid: "{{ '' | chart: type: 'line', labels: sales.labels, values: sales.quarters, width: 480 }}",
1324
- handlebars: "{{chart type='line' labels=sales.labels values=sales.quarters width=480}}"
1415
+ jinja2: "{{ chart({ type: 'line', labels: sales.labels, values: sales.quarters, width: 480, alt: 'Sales by quarter' }) }}",
1416
+ liquid: "{{ '' | chart: type: 'line', labels: sales.labels, values: sales.quarters, width: 480, alt: 'Sales by quarter' }}",
1417
+ handlebars: "{{chart type='line' labels=sales.labels values=sales.quarters width=480 alt='Sales by quarter'}}"
1325
1418
  },
1326
1419
  category: "document"
1327
1420
  },
1328
1421
  fn: (ctx, spec, extra) => {
1329
1422
  if (ctx.mode === "office") throw new Error(officeUnsupported("chart"));
1330
- return chartMarkup({ ...asObject(spec), ...asObject(extra) });
1423
+ return chartMarkup({
1424
+ ...asObject(spec),
1425
+ ...asObject(extra)
1426
+ });
1331
1427
  }
1332
1428
  },
1333
1429
  {
@@ -1335,18 +1431,24 @@ var documentHelpers = [
1335
1431
  html: true,
1336
1432
  doc: {
1337
1433
  signature: "image(url, { width, height, fit, alt })",
1338
- description: "Places an image with fixed size and crop mode (`cover`, `contain`, `fill`); the render-worker fetches it through its cache and blocks private hosts.",
1434
+ description: "Places an image with fixed size and crop mode (`cover`, `contain`, `fill`); the render-worker fetches it through its cache and blocks private hosts. `alt` says what it shows; without one the image counts as decoration and a screen reader passes over it.",
1339
1435
  examples: {
1340
- jinja2: "{{ image(product.photo, { width: 120, height: 80, fit: 'cover' }) }}",
1341
- liquid: "{{ product.photo | image: width: 120, height: 80, fit: 'cover' }}",
1342
- handlebars: "{{image product.photo width=120 height=80 fit='cover'}}"
1436
+ jinja2: "{{ image(product.photo, { width: 120, height: 80, fit: 'cover', alt: product.name }) }}",
1437
+ liquid: "{{ product.photo | image: width: 120, height: 80, fit: 'cover', alt: product.name }}",
1438
+ handlebars: "{{image product.photo width=120 height=80 fit='cover' alt=product.name}}"
1343
1439
  },
1344
1440
  category: "document"
1345
1441
  },
1346
1442
  fn: (ctx, url, options) => {
1347
1443
  const o = asObject(options);
1348
1444
  if (ctx.drawing)
1349
- return ctx.drawing({ kind: "url", url: resolveAsset(ctx.assetBaseUrl, String(url ?? "")), width: o.width, height: o.height, alt: o.alt });
1445
+ return ctx.drawing({
1446
+ kind: "url",
1447
+ url: resolveAsset(ctx.assetBaseUrl, String(url ?? "")),
1448
+ width: o.width,
1449
+ height: o.height,
1450
+ alt: o.alt
1451
+ });
1350
1452
  return imageMarkup(String(url ?? ""), o);
1351
1453
  }
1352
1454
  }
@@ -1510,22 +1612,22 @@ function unavailableHelper(name) {
1510
1612
  }
1511
1613
 
1512
1614
  // packages/engine/src/lib/analysis/tags.ts
1513
- function rangeAt(line, column, length) {
1615
+ function rangeAt(line2, column, length) {
1514
1616
  return {
1515
- start: { line, column },
1516
- end: { line, column: column + Math.max(1, length) }
1617
+ start: { line: line2, column },
1618
+ end: { line: line2, column: column + Math.max(1, length) }
1517
1619
  };
1518
1620
  }
1519
1621
  function positionOf(source, offset) {
1520
- let line = 1;
1622
+ let line2 = 1;
1521
1623
  let last = -1;
1522
1624
  for (let i = 0; i < offset && i < source.length; i++) {
1523
1625
  if (source.charCodeAt(i) === 10) {
1524
- line++;
1626
+ line2++;
1525
1627
  last = i;
1526
1628
  }
1527
1629
  }
1528
- return { line, column: offset - last };
1630
+ return { line: line2, column: offset - last };
1529
1631
  }
1530
1632
  function rangeOf(source, start, end) {
1531
1633
  return { start: positionOf(source, start), end: positionOf(source, end) };
@@ -1771,12 +1873,12 @@ function toSyntaxError(e) {
1771
1873
  const err2 = e;
1772
1874
  const message = String(err2.message ?? e);
1773
1875
  const m = /Parse error on line (\d+)/.exec(message);
1774
- const line = err2.lineNumber ?? (m ? Number(m[1]) : 1);
1876
+ const line2 = err2.lineNumber ?? (m ? Number(m[1]) : 1);
1775
1877
  const column = typeof err2.column === "number" ? err2.column + 1 : 1;
1776
1878
  return new EngineSyntaxError(
1777
1879
  "handlebars",
1778
1880
  message.split("\n")[0] ?? message,
1779
- line,
1881
+ line2,
1780
1882
  column
1781
1883
  );
1782
1884
  }
@@ -2086,48 +2188,48 @@ var SPEC = /^(?:([\s\S])?([<>=^]))?([+\- ])?(#)?(0)?(\d+)?([,_])?(?:\.(\d+))?([b
2086
2188
  function group(digits, separator) {
2087
2189
  return digits.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
2088
2190
  }
2089
- function pythonExponent(text2) {
2090
- return text2.replace(/e([+-])(\d)$/i, (m, sign, digit) => `${m[0]}${sign}0${digit}`);
2191
+ function pythonExponent(text4) {
2192
+ return text4.replace(/e([+-])(\d)$/i, (m, sign, digit) => `${m[0]}${sign}0${digit}`);
2091
2193
  }
2092
2194
  function formatNumber(value, type, precision, alternate) {
2093
2195
  if (!Number.isFinite(value)) return Number.isNaN(value) ? "nan" : "inf";
2094
- const abs = Math.abs(value);
2196
+ const abs2 = Math.abs(value);
2095
2197
  switch (type) {
2096
2198
  case "f":
2097
2199
  case "F":
2098
- return abs.toFixed(precision ?? 6);
2200
+ return abs2.toFixed(precision ?? 6);
2099
2201
  case "%":
2100
- return `${(abs * 100).toFixed(precision ?? 6)}%`;
2202
+ return `${(abs2 * 100).toFixed(precision ?? 6)}%`;
2101
2203
  case "e":
2102
2204
  case "E": {
2103
- const text2 = pythonExponent(abs.toExponential(precision ?? 6));
2104
- return type === "E" ? text2.toUpperCase() : text2;
2205
+ const text4 = pythonExponent(abs2.toExponential(precision ?? 6));
2206
+ return type === "E" ? text4.toUpperCase() : text4;
2105
2207
  }
2106
2208
  case "g":
2107
2209
  case "G": {
2108
2210
  const p = precision === 0 ? 1 : precision ?? 6;
2109
- const exp = abs === 0 ? 0 : Math.floor(Math.log10(abs));
2110
- let text2 = exp < -4 || exp >= p ? pythonExponent(abs.toExponential(p - 1)) : abs.toFixed(Math.max(0, p - 1 - exp));
2211
+ const exp = abs2 === 0 ? 0 : Math.floor(Math.log10(abs2));
2212
+ let text4 = exp < -4 || exp >= p ? pythonExponent(abs2.toExponential(p - 1)) : abs2.toFixed(Math.max(0, p - 1 - exp));
2111
2213
  if (!alternate) {
2112
- const [mantissa = "", exponent] = text2.split("e");
2214
+ const [mantissa = "", exponent] = text4.split("e");
2113
2215
  const trimmed = mantissa.includes(".") ? mantissa.replace(/0+$/, "").replace(/\.$/, "") : mantissa;
2114
- text2 = exponent === void 0 ? trimmed : `${trimmed}e${exponent}`;
2216
+ text4 = exponent === void 0 ? trimmed : `${trimmed}e${exponent}`;
2115
2217
  }
2116
- return type === "G" ? text2.toUpperCase() : text2;
2218
+ return type === "G" ? text4.toUpperCase() : text4;
2117
2219
  }
2118
2220
  case "d":
2119
2221
  case "n":
2120
- return String(Math.trunc(abs));
2222
+ return String(Math.trunc(abs2));
2121
2223
  case "b":
2122
- return (alternate ? "0b" : "") + Math.trunc(abs).toString(2);
2224
+ return (alternate ? "0b" : "") + Math.trunc(abs2).toString(2);
2123
2225
  case "o":
2124
- return (alternate ? "0o" : "") + Math.trunc(abs).toString(8);
2226
+ return (alternate ? "0o" : "") + Math.trunc(abs2).toString(8);
2125
2227
  case "x":
2126
- return (alternate ? "0x" : "") + Math.trunc(abs).toString(16);
2228
+ return (alternate ? "0x" : "") + Math.trunc(abs2).toString(16);
2127
2229
  case "X":
2128
- return (alternate ? "0X" : "") + Math.trunc(abs).toString(16).toUpperCase();
2230
+ return (alternate ? "0X" : "") + Math.trunc(abs2).toString(16).toUpperCase();
2129
2231
  default:
2130
- return precision === void 0 ? String(abs) : formatNumber(abs, "g", precision, alternate);
2232
+ return precision === void 0 ? String(abs2) : formatNumber(abs2, "g", precision, alternate);
2131
2233
  }
2132
2234
  }
2133
2235
  function formatValue(value, spec) {
@@ -2182,35 +2284,35 @@ function lookupField(name, positional, named, next) {
2182
2284
  function pythonFormat(template, positional, named = {}) {
2183
2285
  let auto = 0;
2184
2286
  const next = () => auto++;
2185
- const render = (text2) => {
2287
+ const render = (text4) => {
2186
2288
  let out = "";
2187
- for (let i = 0; i < text2.length; i++) {
2188
- const ch = text2[i];
2289
+ for (let i = 0; i < text4.length; i++) {
2290
+ const ch = text4[i];
2189
2291
  if (ch === "}") {
2190
2292
  out += "}";
2191
- if (text2[i + 1] === "}") i++;
2293
+ if (text4[i + 1] === "}") i++;
2192
2294
  continue;
2193
2295
  }
2194
2296
  if (ch !== "{") {
2195
2297
  out += ch;
2196
2298
  continue;
2197
2299
  }
2198
- if (text2[i + 1] === "{") {
2300
+ if (text4[i + 1] === "{") {
2199
2301
  out += "{";
2200
2302
  i++;
2201
2303
  continue;
2202
2304
  }
2203
2305
  let depth = 1;
2204
2306
  let j = i + 1;
2205
- for (; j < text2.length && depth > 0; j++) {
2206
- if (text2[j] === "{") depth++;
2207
- else if (text2[j] === "}") depth--;
2307
+ for (; j < text4.length && depth > 0; j++) {
2308
+ if (text4[j] === "{") depth++;
2309
+ else if (text4[j] === "}") depth--;
2208
2310
  }
2209
2311
  if (depth > 0) {
2210
- out += text2.slice(i);
2312
+ out += text4.slice(i);
2211
2313
  break;
2212
2314
  }
2213
- const field = text2.slice(i + 1, j - 1);
2315
+ const field = text4.slice(i + 1, j - 1);
2214
2316
  const colon = field.indexOf(":");
2215
2317
  const head = colon < 0 ? field : field.slice(0, colon);
2216
2318
  const name = head.replace(/![rsa]$/, "");
@@ -2314,7 +2416,7 @@ var stringMethods = {
2314
2416
  return out;
2315
2417
  },
2316
2418
  split: (s, sep, max2) => sep === void 0 || sep === null ? s.trim().split(/\s+/) : s.split(String(sep), max2 === void 0 ? void 0 : Number(max2) + 1),
2317
- join: (s, list2) => Array.isArray(list2) ? list2.join(s) : String(list2),
2419
+ join: (s, list3) => Array.isArray(list3) ? list3.join(s) : String(list3),
2318
2420
  format: (s, ...args) => {
2319
2421
  const named = keywordArgs(args);
2320
2422
  return pythonFormat(s, named ? args.slice(0, -1) : args, named ?? {});
@@ -2548,9 +2650,9 @@ function toSyntaxError2(e, fallbackLine = 1) {
2548
2650
  const fromMessage = /\[Line (\d+), Column (\d+)\]/.exec(
2549
2651
  String(err2.message ?? "")
2550
2652
  );
2551
- const line = typeof err2.lineno === "number" ? err2.lineno + 1 : fromMessage ? Number(fromMessage[1]) : fallbackLine;
2653
+ const line2 = typeof err2.lineno === "number" ? err2.lineno + 1 : fromMessage ? Number(fromMessage[1]) : fallbackLine;
2552
2654
  const column = typeof err2.colno === "number" ? err2.colno + 1 : fromMessage ? Number(fromMessage[2]) : 1;
2553
- return new EngineSyntaxError("jinja2", message, line, column);
2655
+ return new EngineSyntaxError("jinja2", message, line2, column);
2554
2656
  }
2555
2657
  function nodeRange(node, length = 1) {
2556
2658
  return rangeAt(node.lineno + 1, node.colno + 1, length);
@@ -3045,12 +3147,12 @@ var validationContext3 = {
3045
3147
  };
3046
3148
  function toSyntaxError3(e) {
3047
3149
  const err2 = e;
3048
- let line = err2.line ?? 1;
3150
+ let line2 = err2.line ?? 1;
3049
3151
  let column = err2.col ?? 1;
3050
3152
  const pos = err2.token?.getPosition?.();
3051
- if (pos) [line, column] = pos;
3153
+ if (pos) [line2, column] = pos;
3052
3154
  const message = String(err2.message ?? e).replace(/,\s*line:\d+,\s*col:\d+/, "").trim();
3053
- return new EngineSyntaxError("liquid", message, line, column);
3155
+ return new EngineSyntaxError("liquid", message, line2, column);
3054
3156
  }
3055
3157
  var identifier = /(?<![\w."'])([A-Za-z_][\w]*)((?:\.[A-Za-z_]\w*|\[\d+\])*)/g;
3056
3158
  function pathsIn(expression, base, source, out, scopes) {
@@ -3561,8 +3663,8 @@ function unionTypes(a, b) {
3561
3663
  ...Array.isArray(b) ? b : b ? [b] : []
3562
3664
  ]);
3563
3665
  if (set.has("number")) set.delete("integer");
3564
- const list2 = [...set];
3565
- return list2.length === 1 ? list2[0] : list2;
3666
+ const list3 = [...set];
3667
+ return list3.length === 1 ? list3[0] : list3;
3566
3668
  }
3567
3669
  function merge(a, b) {
3568
3670
  if (!a) return b;
@@ -3733,9 +3835,9 @@ function layeredPartials(own, shared2, onShared) {
3733
3835
  }
3734
3836
 
3735
3837
  // packages/engine/src/lib/dialect.ts
3736
- var loop = (engine, item, list2, body) => engine === "handlebars" ? `{{#each ${list2}}}
3838
+ var loop = (engine, item, list3, body) => engine === "handlebars" ? `{{#each ${list3}}}
3737
3839
  ${body.replace(new RegExp(`(?<![\\w.])${item}\\.`, "g"), "")}
3738
- {{/each}}` : `{% for ${item} in ${list2} %}
3840
+ {{/each}}` : `{% for ${item} in ${list3} %}
3739
3841
  ${body}
3740
3842
  {% endfor %}`;
3741
3843
  var val = (engine, expr, filter) => {
@@ -4304,12 +4406,12 @@ var stripComments = (css2) => css2.replace(/\/\*[\s\S]*?\*\//g, "");
4304
4406
  var selectorKey = (prelude) => (prelude.split(";").pop() ?? "").trim().replace(/\s*,\s*/g, ", ").replace(/\s+/g, " ");
4305
4407
  function mergeSnippetCss(css2, snippetCss) {
4306
4408
  if (!snippetCss?.trim()) return css2;
4307
- const defined = new Set(
4409
+ const defined2 = new Set(
4308
4410
  [...stripComments(css2).matchAll(/([^{}]+)\{/g)].map(
4309
4411
  (m) => selectorKey(m[1] ?? "")
4310
4412
  )
4311
4413
  );
4312
- const missing = [...stripComments(snippetCss).matchAll(/([^{}]+)\{[^{}]*\}/g)].filter((m) => !defined.has(selectorKey(m[1] ?? ""))).map((m) => m[0].trim());
4414
+ const missing = [...stripComments(snippetCss).matchAll(/([^{}]+)\{[^{}]*\}/g)].filter((m) => !defined2.has(selectorKey(m[1] ?? ""))).map((m) => m[0].trim());
4313
4415
  if (!missing.length) return css2;
4314
4416
  const base = css2.trimEnd();
4315
4417
  return `${base}${base ? "\n\n" : ""}${missing.join("\n")}
@@ -4355,9 +4457,9 @@ function pick(input, ...keys) {
4355
4457
  function toLength(value) {
4356
4458
  if (value === void 0 || value === null || value === "") return void 0;
4357
4459
  if (typeof value === "number") return `${value}mm`;
4358
- const text2 = String(value).trim();
4359
- if (/^-?\d+(\.\d+)?$/.test(text2)) return `${text2}mm`;
4360
- if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text2)) return text2.toLowerCase();
4460
+ const text4 = String(value).trim();
4461
+ if (/^-?\d+(\.\d+)?$/.test(text4)) return `${text4}mm`;
4462
+ if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text4)) return text4.toLowerCase();
4361
4463
  return void 0;
4362
4464
  }
4363
4465
  function toMarginLength(value) {
@@ -4377,7 +4479,7 @@ function convertHeaderFooter(html) {
4377
4479
  if (typeof html !== "string" || !html.trim()) return void 0;
4378
4480
  return chromePlaceholders.reduce((out, [pattern, span]) => out.replace(pattern, span), html);
4379
4481
  }
4380
- var escapeText = (text2) => text2.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4482
+ var escapeText = (text4) => text4.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4381
4483
  var visibleMarkup = (html) => html.replace(/<style[\s\S]*?<\/style>/gi, "").trim();
4382
4484
  function apitemplateChrome(take, kind) {
4383
4485
  const custom = take(`custom_${kind}`);
@@ -4391,7 +4493,7 @@ function apitemplateChrome(take, kind) {
4391
4493
  if (!slots.some(Boolean)) return { fromSlots: false };
4392
4494
  const sizeText = size === void 0 ? "" : String(size).trim();
4393
4495
  const fontSize2 = /^\d+(\.\d+)?$/.test(sizeText) ? `${sizeText}px` : /^\d+(\.\d+)?(px|pt|mm)$/.test(sizeText) ? sizeText : "9px";
4394
- const cells = slots.map((text2, i) => `<span style="flex:1;text-align:${["left", "center", "right"][i]}">${escapeText(text2)}</span>`).join("");
4496
+ const cells = slots.map((text4, i) => `<span style="flex:1;text-align:${["left", "center", "right"][i]}">${escapeText(text4)}</span>`).join("");
4395
4497
  const styles = customHtml.match(/<style[\s\S]*?<\/style>/gi)?.join("") ?? "";
4396
4498
  return {
4397
4499
  html: convertHeaderFooter(`${styles}<div style="display:flex;gap:4mm;font-size:${fontSize2}">${cells}</div>`),
@@ -4511,15 +4613,15 @@ function parseSample(value) {
4511
4613
  }
4512
4614
  function positionOf2(source, index) {
4513
4615
  const before = source.slice(0, index);
4514
- const line = before.split("\n").length;
4616
+ const line2 = before.split("\n").length;
4515
4617
  const column = index - before.lastIndexOf("\n");
4516
- return { line, column };
4618
+ return { line: line2, column };
4517
4619
  }
4518
- function notesFor(source, pattern, note, { once = false } = {}) {
4620
+ function notesFor(source, pattern, note2, { once = false } = {}) {
4519
4621
  const global = new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`);
4520
4622
  const out = [];
4521
4623
  for (const m of source.matchAll(global)) {
4522
- out.push({ ...note, ...positionOf2(source, m.index ?? 0) });
4624
+ out.push({ ...note2, ...positionOf2(source, m.index ?? 0) });
4523
4625
  if (once) break;
4524
4626
  }
4525
4627
  return out;
@@ -4542,19 +4644,19 @@ function checkTemplate(engineId, html, sampleData, name, docs) {
4542
4644
  docs: d.code === "unknown-filter" ? docs.filters : d.code.startsWith("missing-") ? docs.sampleData : docs.syntax
4543
4645
  });
4544
4646
  for (const d of engine.analyze(html, { sampleData }).diagnostics) {
4545
- const note = fromDiagnostic(d);
4647
+ const note2 = fromDiagnostic(d);
4546
4648
  if (d.severity === "error") {
4547
- if (!errors.some((x) => x.code === note.code && x.line === note.line)) errors.push(note);
4548
- } else warnings.push(note);
4649
+ if (!errors.some((x) => x.code === note2.code && x.line === note2.line)) errors.push(note2);
4650
+ } else warnings.push(note2);
4549
4651
  }
4550
4652
  return { errors, warnings };
4551
4653
  }
4552
4654
  function lengthMm(value) {
4553
4655
  if (value === void 0 || value === null || value === "") return void 0;
4554
4656
  if (typeof value === "number" && Number.isFinite(value)) return `${value}mm`;
4555
- const text2 = String(value).trim();
4556
- if (/^-?\d+(\.\d+)?$/.test(text2)) return `${text2}mm`;
4557
- if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text2)) return text2.toLowerCase();
4657
+ const text4 = String(value).trim();
4658
+ if (/^-?\d+(\.\d+)?$/.test(text4)) return `${text4}mm`;
4659
+ if (/^-?\d+(\.\d+)?(mm|cm|in|px|pt)$/i.test(text4)) return text4.toLowerCase();
4558
4660
  return void 0;
4559
4661
  }
4560
4662
  function toBase64(value) {
@@ -4641,17 +4743,17 @@ var STYLE_BLOCK = /<style\b[^>]*>([\s\S]*?)<\/style\s*>/gi;
4641
4743
  var HEAD_MARKUP = /<script\b[\s\S]*?<\/script\s*>|<!--[\s\S]*?-->|<(?:link|meta|base)\b[^>]*>/gi;
4642
4744
  var TEMPLATED = /\{\{|\{%/;
4643
4745
  function splitApitemplateCss(source) {
4644
- const styleElement = (rules) => `<style>
4645
- ${rules}
4746
+ const styleElement = (rules2) => `<style>
4747
+ ${rules2}
4646
4748
  </style>`;
4647
4749
  if (!/<\/?(style|link|script|meta|base)\b|<!--/i.test(source))
4648
4750
  return TEMPLATED.test(source) ? { css: "", head: "", body: styleElement(source.trim()) } : { css: source, head: "", body: "" };
4649
4751
  const styles = [];
4650
4752
  const head = [];
4651
4753
  const body = [];
4652
- let rest = source.replace(STYLE_BLOCK, (block, rules) => {
4653
- if (TEMPLATED.test(rules)) body.push(block.trim());
4654
- else styles.push(rules.replace(/^\n+|\s+$/g, ""));
4754
+ let rest = source.replace(STYLE_BLOCK, (block, rules2) => {
4755
+ if (TEMPLATED.test(rules2)) body.push(block.trim());
4756
+ else styles.push(rules2.replace(/^\n+|\s+$/g, ""));
4655
4757
  return "\n";
4656
4758
  });
4657
4759
  rest = rest.replace(HEAD_MARKUP, (tag) => {
@@ -4674,7 +4776,7 @@ function importApitemplate(input) {
4674
4776
  if (kind === "image") changes.push(`${format} template imported as an image template`);
4675
4777
  const mapped = mapApitemplateSettings(input.settings ?? void 0);
4676
4778
  const settings = mapped.settings;
4677
- for (const note of mapped.notes) changes.push(note);
4779
+ for (const note2 of mapped.notes) changes.push(note2);
4678
4780
  for (const key of mapped.ignored)
4679
4781
  warnings.push({ code: "setting-ignored", message: `Setting "${key}" has no Formfeed equivalent and was dropped.`, docs: "/migrate/apitemplate-io#settings" });
4680
4782
  if (settings.header?.html || settings.footer?.html) changes.push("header and footer templates moved to the Header and Footer tabs");
@@ -5071,11 +5173,11 @@ function inlineIncludes(source, blocks, errors, changes) {
5071
5173
  }
5072
5174
  var cssPaperFormats = { a3: "A3", a4: "A4", a5: "A5", letter: "Letter" };
5073
5175
  var isoSizes = { a0: [841, 1189], a1: [594, 841], a2: [420, 594], a6: [105, 148] };
5074
- var escapeHtml2 = (text2) => text2.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5176
+ var escapeHtml2 = (text4) => text4.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
5075
5177
  function simpleHeaderFooter(part, margin) {
5076
5178
  const cells = ["left", "center", "right"].map((side) => part[side] ?? "");
5077
5179
  if (!cells.some((c) => c.trim())) return void 0;
5078
- const cell3 = (text2, align) => `<div style="flex: 1; text-align: ${align}">${escapeHtml2(text2).replace(/\[page\]/g, '<span class="pageNumber"></span>').replace(/\[topage\]/g, '<span class="totalPages"></span>')}</div>`;
5180
+ const cell3 = (text4, align) => `<div style="flex: 1; text-align: ${align}">${escapeHtml2(text4).replace(/\[page\]/g, '<span class="pageNumber"></span>').replace(/\[topage\]/g, '<span class="totalPages"></span>')}</div>`;
5079
5181
  return `<div style="display: flex; width: 100%; font-size: 10px; padding: 0 ${margin.right} 0 ${margin.left}">${cell3(cells[0] ?? "", "left")}${cell3(cells[1] ?? "", "center")}${cell3(cells[2] ?? "", "right")}</div>`;
5080
5182
  }
5081
5183
  function mapSettings(input, kind, warnings, changes) {
@@ -5244,11 +5346,11 @@ var DOCS3 = "/migrate/jsreport";
5244
5346
  var COLLECTIONS = ["templates", "data", "assets", "components", "scripts", "folders"];
5245
5347
  function readJsreportExport(files) {
5246
5348
  const bundle = { templates: [], data: [], assets: [], components: [], scripts: [], folders: [] };
5247
- for (const [path, text2] of Object.entries(files)) {
5349
+ for (const [path, text4] of Object.entries(files)) {
5248
5350
  const normalized = path.replace(/\\/g, "/").replace(/^\.?\//, "");
5249
5351
  if (normalized === "metadata.json") {
5250
5352
  try {
5251
- bundle.version = String(JSON.parse(text2).reporterVersion ?? "");
5353
+ bundle.version = String(JSON.parse(text4).reporterVersion ?? "");
5252
5354
  } catch {
5253
5355
  }
5254
5356
  continue;
@@ -5256,7 +5358,7 @@ function readJsreportExport(files) {
5256
5358
  const collection = normalized.split("/")[0];
5257
5359
  if (!COLLECTIONS.includes(collection) || !normalized.endsWith(".json")) continue;
5258
5360
  try {
5259
- bundle[collection].push(JSON.parse(text2));
5361
+ bundle[collection].push(JSON.parse(text4));
5260
5362
  } catch {
5261
5363
  }
5262
5364
  }
@@ -5281,9 +5383,9 @@ function pathOf2(bundle, entity) {
5281
5383
  function jsreportTemplates(bundle) {
5282
5384
  return bundle.templates.map((t) => ({ ref: t.shortid ?? t._id ?? t.name ?? "", name: t.name ?? "", path: pathOf2(bundle, t), engine: t.engine ?? "", recipe: t.recipe ?? "" })).sort((a, b) => a.path.localeCompare(b.path));
5283
5385
  }
5284
- function byPath(bundle, list2, ref) {
5386
+ function byPath(bundle, list3, ref) {
5285
5387
  const wanted = ref.replace(/^\.?\/+/, "");
5286
- return list2.find((e) => pathOf2(bundle, e) === wanted) ?? list2.find((e) => e.name === wanted.split("/").pop());
5388
+ return list3.find((e) => pathOf2(bundle, e) === wanted) ?? list3.find((e) => e.name === wanted.split("/").pop());
5287
5389
  }
5288
5390
  function decodeText(base64) {
5289
5391
  const binary = atob(base64);
@@ -5298,7 +5400,7 @@ function assetBase64(content) {
5298
5400
  }
5299
5401
  return toBase64(content);
5300
5402
  }
5301
- var escapeMustaches = (text2) => text2.replace(/\{\{/g, "\\{{");
5403
+ var escapeMustaches = (text4) => text4.replace(/\{\{/g, "\\{{");
5302
5404
  function resolveAssets(source, bundle, options, warnings, changes) {
5303
5405
  const v3 = /\{\{\{?\s*asset\s+(["'])([^"']+)\1(?:\s+(["'])([^"']+)\3)?\s*\}?\}\}/g;
5304
5406
  const v2 = /\{#asset\s+([^\s}]+)(?:\s+@encoding=(\w+))?\s*\}/g;
@@ -5536,9 +5638,10 @@ function importJsreport(bundle, ref, options = {}) {
5536
5638
  }
5537
5639
 
5538
5640
  // packages/engine/src/lib/generated/frames.ts
5539
- var textEditRuntime = '"use strict";(()=>{var H=`\nhtml.ff-edit-mode [data-ff-edit] { cursor: text; }\nhtml.ff-edit-mode [data-ff-edit]:hover { outline: 1px dashed rgba(37,99,235,.85); outline-offset: 2px; }\nhtml.ff-edit-mode [data-ff-edit="t"]:hover { outline-color: rgba(147,51,234,.9); }\n.ff-editing { outline: 2px solid #2563eb !important; outline-offset: 2px; cursor: text; }\n.ff-editing .ff-chip { background: rgba(37,99,235,.14) !important; box-shadow: inset 0 0 0 1px rgba(37,99,235,.45) !important; border-radius: 3px !important; padding: 0 1px !important; cursor: default; }\n.ff-edit-badge { position: absolute; z-index: 2147483647; font: 500 11px/1.4 system-ui, sans-serif !important; color: #fff !important; background: #2563eb !important; padding: 1px 6px !important; border-radius: 4px !important; pointer-events: none; white-space: nowrap; }\n`;function R(s,l={}){let g=l.clicks!==!1,p=l.live===!0,o=s.document,i=null,a=null,c=e=>{try{s.parent.postMessage(e,"*")}catch{}},b=o.createElement("style");b.setAttribute("data-formfeed","edit"),b.setAttribute("data-pagedjs-ignore",""),b.textContent=H,(o.head??o.documentElement).appendChild(b),g&&o.documentElement.classList.add("ff-edit-mode");let D=e=>(e&&typeof e.closest=="function"?e:null)?.closest("[data-ff-edit]")??null;o.addEventListener("click",e=>{if(!g)return;if(i&&!i.done&&i.element.contains(e.target)){e.stopImmediatePropagation();return}let t=D(e.target);if(!t)return;e.preventDefault(),e.stopImmediatePropagation(),i&&!i.done&&y();let n=t.getAttribute("data-ff-src")??"",r=t.getBoundingClientRect();a={src:n,element:t,x:e.clientX,y:e.clientY},c({type:"formfeed:edit-request",src:n,edit:t.getAttribute("data-ff-edit"),text:t.textContent??"",count:o.querySelectorAll(`[data-ff-src="${n.replace(/"/g,"")}"]`).length,split:t.hasAttribute("data-split-from")||t.hasAttribute("data-split-to"),rect:{left:r.left,top:r.top,width:r.width,height:r.height}})},!0),o.addEventListener("keydown",e=>{if(!g||i&&!i.done||!(e.ctrlKey||e.metaKey)||e.altKey)return;let t=e.key.toLowerCase();(t==="z"||t==="y")&&(e.preventDefault(),c({type:t==="y"||e.shiftKey?"formfeed:redo":"formfeed:undo"}))}),s.addEventListener("message",e=>{if(e.source!==s.parent)return;let t=e.data;!t||typeof t.type!="string"||(t.type==="formfeed:edit-begin"?F(t):(t.type==="formfeed:edit-refuse"||t.type==="formfeed:edit-end")&&(a=null,i&&!i.done&&h(!1)))});function F(e){i&&!i.done&&h(!1);let t=e.layer?o.querySelector(`[data-ff-layer="${String(e.layer).replace(/"/g,"")}"] > .ff-text`):a&&a.src===e.src?a.element:o.querySelector(`[data-ff-src="${String(e.src).replace(/"/g,"")}"]`),n=e.at,r=e.layer?n&&Number.isFinite(n.x)&&Number.isFinite(n.y)?{x:Number(n.x),y:Number(n.y)}:null:a&&a.src===e.src?a:null;if(a=null,!t||!Array.isArray(e.items)){c({type:"formfeed:edit-cancel",token:String(e.token)});return}let m=Array.from(t.childNodes),f=o.createDocumentFragment();for(let u of e.items)if(u.kind==="text")f.appendChild(o.createTextNode(String(u.text)));else if(u.kind==="break")f.appendChild(o.createElement("br"));else if(u.kind==="chip"){let d=o.createElement("span");d.className="ff-chip",d.setAttribute("data-ff-chip",String(u.index)),d.setAttribute("contenteditable","false"),u.title&&(d.title=String(u.title)),d.textContent=String(u.text),f.appendChild(d)}for(let u of m)t.removeChild(u);t.appendChild(f),t.contentEditable="true",t.classList.add("ff-editing"),t.setAttribute("role","textbox"),e.label&&t.setAttribute("aria-label",String(e.label)),t.spellcheck=!0,i={token:String(e.token),element:t,saved:m,allowBreaks:e.allowBreaks===!0,badge:e.note?B(t,String(e.note)):null,done:!1},t.addEventListener("keydown",E),t.addEventListener("beforeinput",v),t.addEventListener("paste",C),t.addEventListener("drop",L),t.addEventListener("focusout",A),p&&t.addEventListener("input",x),t.focus(),e.select==="all"?w(t):P(t,r),c({type:"formfeed:edit-started",token:i.token}),typeof e.insert=="string"&&e.insert&&k(e.insert.replace(/\\r\\n?|\\n/g," "))}function k(e){if(typeof o.execCommand=="function"&&o.execCommand("insertText",!1,e))return;let t=s.getSelection(),n=t&&t.rangeCount?t.getRangeAt(0):null;n&&(n.deleteContents(),n.insertNode(o.createTextNode(e)),n.collapse(!1),p&&x())}function x(){i&&!i.done&&c({type:"formfeed:edit-input",token:i.token,items:S(i.element)})}function B(e,t){let n=o.createElement("div");n.className="ff-edit-badge",n.textContent=t;let r=e.getBoundingClientRect();return n.style.left=`${r.left+s.scrollX}px`,n.style.top=`${Math.max(0,r.top+s.scrollY-22)}px`,o.body.appendChild(n),n}function w(e){let t=s.getSelection();if(!t)return;let n=o.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}function P(e,t){let n=s.getSelection();if(!n)return;let r=null,m=o;if(t){if(m.caretRangeFromPoint)r=m.caretRangeFromPoint(t.x,t.y);else if(m.caretPositionFromPoint){let f=m.caretPositionFromPoint(t.x,t.y);f&&(r=o.createRange(),r.setStart(f.offsetNode,f.offset))}}if(!r||!e.contains(r.startContainer))r=o.createRange(),r.selectNodeContents(e),r.collapse(!1);else if(t){let f=r.startContainer,d=(f.nodeType===1?f:f.parentElement)?.closest("[data-ff-chip]")??null;if(d&&d!==e&&e.contains(d)){let N=d.getBoundingClientRect();r=o.createRange(),t.x<N.left+N.width/2?r.setStartBefore(d):r.setStartAfter(d),r.collapse(!0)}}n.removeAllRanges(),n.addRange(r)}function E(e){let t=e.key;t==="Escape"?(e.preventDefault(),h(!0)):t==="Enter"&&!e.shiftKey?(e.preventDefault(),y()):t==="Enter"?(e.preventDefault(),i?.allowBreaks&&M()):t==="Tab"?(e.preventDefault(),y()):(e.ctrlKey||e.metaKey)&&["b","i","u"].includes(t.toLowerCase())&&(e.preventDefault(),p&&i&&!e.altKey&&c({type:"formfeed:edit-style",token:i.token,key:t.toLowerCase()}))}function v(e){let t=e.inputType??"";(t.startsWith("format")||t==="insertFromDrop"||t==="insertParagraph"||t==="insertLineBreak")&&e.preventDefault()}function C(e){e.preventDefault(),k((e.clipboardData?.getData("text/plain")??"").replace(/\\r\\n?|\\n/g," "))}function L(e){e.preventDefault()}function A(){s.setTimeout(()=>{i&&!i.done&&(!o.hasFocus()||!i.element.contains(o.activeElement))&&y()},0)}function M(){let e=s.getSelection();if(!e||!e.rangeCount)return;let t=e.getRangeAt(0);t.deleteContents();let n=o.createElement("br");t.insertNode(n),n.nextSibling||n.parentNode?.appendChild(o.createElement("br"));let r=o.createRange();r.setStartAfter(n),r.collapse(!0),e.removeAllRanges(),e.addRange(r)}function T(e){e.done=!0;let t=e.element;t.removeEventListener("keydown",E),t.removeEventListener("beforeinput",v),t.removeEventListener("paste",C),t.removeEventListener("drop",L),t.removeEventListener("focusout",A),t.removeEventListener("input",x),t.removeAttribute("contenteditable"),t.removeAttribute("role"),t.removeAttribute("aria-label"),t.classList.remove("ff-editing"),e.badge?.remove(),i===e&&(i=null)}function y(){let e=i;if(!e||e.done)return;let t=S(e.element);T(e),c({type:"formfeed:edit-commit",token:e.token,items:t})}function h(e){let t=i;if(!t||t.done)return;let n=t.element;for(;n.firstChild;)n.removeChild(n.firstChild);for(let r of t.saved)n.appendChild(r);T(t),e&&c({type:"formfeed:edit-cancel",token:t.token})}}function S(s){let l=[],g=o=>{let i=l[l.length-1];i?.kind==="text"?i.text+=o:o&&l.push({kind:"text",text:o})},p=o=>{for(let i of Array.from(o.childNodes))if(i.nodeType===3)g(i.data);else if(i.nodeType===1){let a=i,c=a.getAttribute("data-ff-chip");c!==null?l.push({kind:"chip",index:Number(c)}):a.tagName==="BR"?l.push({kind:"break"}):((a.tagName==="DIV"||a.tagName==="P")&&l.length&&l.push({kind:"break"}),p(a))}};for(p(s);l[l.length-1]?.kind==="break";)l.pop();return l}R(window);})();';
5641
+ var textEditRuntime = '"use strict";(()=>{var O=`\nhtml.ff-edit-mode [data-ff-edit] { cursor: text; }\nhtml.ff-edit-mode [data-ff-edit]:hover { outline: 1px dashed rgba(37,99,235,.85); outline-offset: 2px; }\nhtml.ff-edit-mode [data-ff-edit="t"]:hover { outline-color: rgba(147,51,234,.9); }\n.ff-editing { outline: 2px solid #2563eb !important; outline-offset: 2px; cursor: text; }\n.ff-editing .ff-chip { background: rgba(37,99,235,.14) !important; box-shadow: inset 0 0 0 1px rgba(37,99,235,.45) !important; border-radius: 3px !important; padding: 0 1px !important; cursor: default; }\n.ff-edit-badge { position: absolute; z-index: 2147483647; font: 500 11px/1.4 system-ui, sans-serif !important; color: #fff !important; background: #2563eb !important; padding: 1px 6px !important; border-radius: 4px !important; pointer-events: none; white-space: nowrap; }\n`;function F(l,d={}){let m=d.clicks!==!1,g=d.live===!0,o=l.document,i=null,s=null,f=e=>{try{l.parent.postMessage(e,"*")}catch{}},v=m?o.querySelector(\'meta[name="formfeed:document"]\')?.getAttribute("content")??null:null,h=!1;l.addEventListener("blur",()=>{h=!0}),l.addEventListener("focus",()=>{h=!1});let y=o.createElement("style");y.setAttribute("data-formfeed","edit"),y.setAttribute("data-pagedjs-ignore",""),y.textContent=O,(o.head??o.documentElement).appendChild(y),m&&o.documentElement.classList.add("ff-edit-mode");let M=e=>(e&&typeof e.closest=="function"?e:null)?.closest("[data-ff-edit]")??null;o.addEventListener("click",e=>{if(!m)return;if(i&&!i.done&&i.element.contains(e.target)){e.stopImmediatePropagation();return}let t=M(e.target);t&&(e.preventDefault(),e.stopImmediatePropagation(),C(t,{x:e.clientX,y:e.clientY}))},!0);function C(e,t){i&&!i.done&&x();let n=e.getAttribute("data-ff-src")??"",r=e.getBoundingClientRect(),u=Array.from(o.querySelectorAll(`[data-ff-src="${n.replace(/"/g,"")}"]`));s={src:n,element:e,point:t},f({type:"formfeed:edit-request",src:n,doc:v,edit:e.getAttribute("data-ff-edit"),text:e.textContent??"",elements:e.childElementCount,count:u.length,index:Math.max(0,u.indexOf(e)),split:e.hasAttribute("data-split-from")||e.hasAttribute("data-split-to"),rect:{left:r.left,top:r.top,width:r.width,height:r.height}})}o.addEventListener("keydown",e=>{if(!m||i&&!i.done||!(e.ctrlKey||e.metaKey)||e.altKey)return;let t=e.key.toLowerCase();(t==="z"||t==="y")&&(e.preventDefault(),f({type:t==="y"||e.shiftKey?"formfeed:redo":"formfeed:undo"}))}),l.addEventListener("message",e=>{if(e.source!==l.parent)return;let t=e.data;!t||typeof t.type!="string"||(t.type==="formfeed:edit-begin"?P(t):t.type==="formfeed:edit-refuse"||t.type==="formfeed:edit-end"?(s=null,i&&!i.done&&E(!1)):t.type==="formfeed:edit-open"&&B(t))});function B(e){if(!m||typeof e.src!="string"||i&&!i.done)return;let t=Array.from(o.querySelectorAll(`[data-ff-src="${e.src.replace(/"/g,"")}"][data-ff-edit]`)),n=Number.isInteger(e.index)?Number(e.index):0,r=t[n]??t[0];r&&C(r,null)}if(m){let e=()=>f({type:"formfeed:edit-ready",doc:v}),t=l;t.PagedConfig?t.formfeedEditReady=e:o.readyState==="loading"?o.addEventListener("DOMContentLoaded",e):e()}function P(e){i&&!i.done&&E(!1);let t=e.layer?o.querySelector(`[data-ff-layer="${String(e.layer).replace(/"/g,"")}"] > .ff-text`):s&&s.src===e.src?s.element:o.querySelector(`[data-ff-src="${String(e.src).replace(/"/g,"")}"]`),n=e.at,r=e.layer?n&&Number.isFinite(n.x)&&Number.isFinite(n.y)?{x:Number(n.x),y:Number(n.y)}:null:s&&s.src===e.src?s.point:null;s=null;let u=h&&!o.hasFocus();if(!t||!Array.isArray(e.items)||u){f({type:"formfeed:edit-cancel",token:String(e.token)});return}let c=Array.from(t.childNodes),b=o.createDocumentFragment();for(let a of e.items)if(a.kind==="text")b.appendChild(o.createTextNode(String(a.text)));else if(a.kind==="break")b.appendChild(o.createElement("br"));else if(a.kind==="chip"){let p=o.createElement("span");p.className="ff-chip",p.setAttribute("data-ff-chip",String(a.index)),p.setAttribute("contenteditable","false"),a.title&&(p.title=String(a.title)),p.textContent=String(a.text),b.appendChild(p)}for(let a of c)t.removeChild(a);t.appendChild(b),t.contentEditable="true",t.classList.add("ff-editing"),t.setAttribute("role","textbox"),e.label&&t.setAttribute("aria-label",String(e.label)),t.spellcheck=!0,i={token:String(e.token),element:t,saved:c,allowBreaks:e.allowBreaks===!0,badge:e.note?H(t,String(e.note)):null,done:!1},t.addEventListener("keydown",A),t.addEventListener("beforeinput",T),t.addEventListener("paste",S),t.addEventListener("drop",N),t.addEventListener("focusout",R),g&&t.addEventListener("input",k),t.focus(),e.select==="all"?I(t):K(t,r),f({type:"formfeed:edit-started",token:i.token}),typeof e.insert=="string"&&e.insert&&L(e.insert.replace(/\\r\\n?|\\n/g," "))}function L(e){if(typeof o.execCommand=="function"&&o.execCommand("insertText",!1,e))return;let t=l.getSelection(),n=t&&t.rangeCount?t.getRangeAt(0):null;n&&(n.deleteContents(),n.insertNode(o.createTextNode(e)),n.collapse(!1),g&&k())}function k(){i&&!i.done&&f({type:"formfeed:edit-input",token:i.token,items:D(i.element)})}function H(e,t){let n=o.createElement("div");n.className="ff-edit-badge",n.textContent=t;let r=e.getBoundingClientRect();return n.style.left=`${r.left+l.scrollX}px`,n.style.top=`${Math.max(0,r.top+l.scrollY-22)}px`,o.body.appendChild(n),n}function I(e){let t=l.getSelection();if(!t)return;let n=o.createRange();n.selectNodeContents(e),t.removeAllRanges(),t.addRange(n)}function K(e,t){let n=l.getSelection();if(!n)return;let r=null,u=o;if(t){if(u.caretRangeFromPoint)r=u.caretRangeFromPoint(t.x,t.y);else if(u.caretPositionFromPoint){let c=u.caretPositionFromPoint(t.x,t.y);c&&(r=o.createRange(),r.setStart(c.offsetNode,c.offset))}}if(!r||!e.contains(r.startContainer))r=o.createRange(),r.selectNodeContents(e),r.collapse(!1);else if(t){let c=r.startContainer,a=(c.nodeType===1?c:c.parentElement)?.closest("[data-ff-chip]")??null;if(a&&a!==e&&e.contains(a)){let p=a.getBoundingClientRect();r=o.createRange(),t.x<p.left+p.width/2?r.setStartBefore(a):r.setStartAfter(a),r.collapse(!0)}}n.removeAllRanges(),n.addRange(r)}function A(e){let t=e.key;t==="Escape"?(e.preventDefault(),E(!0)):t==="Enter"&&!e.shiftKey?(e.preventDefault(),x()):t==="Enter"?(e.preventDefault(),i?.allowBreaks&&q()):t==="Tab"?(e.preventDefault(),x()):(e.ctrlKey||e.metaKey)&&["b","i","u"].includes(t.toLowerCase())&&(e.preventDefault(),g&&i&&!e.altKey&&f({type:"formfeed:edit-style",token:i.token,key:t.toLowerCase()}))}function T(e){let t=e.inputType??"";(t.startsWith("format")||t==="insertFromDrop"||t==="insertParagraph"||t==="insertLineBreak")&&e.preventDefault()}function S(e){e.preventDefault(),L((e.clipboardData?.getData("text/plain")??"").replace(/\\r\\n?|\\n/g," "))}function N(e){e.preventDefault()}function R(){l.setTimeout(()=>{i&&!i.done&&(!o.hasFocus()||!i.element.contains(o.activeElement))&&x()},0)}function q(){let e=l.getSelection();if(!e||!e.rangeCount)return;let t=e.getRangeAt(0);t.deleteContents();let n=o.createElement("br");t.insertNode(n),n.nextSibling||n.parentNode?.appendChild(o.createElement("br"));let r=o.createRange();r.setStartAfter(n),r.collapse(!0),e.removeAllRanges(),e.addRange(r)}function w(e){e.done=!0;let t=e.element;t.removeEventListener("keydown",A),t.removeEventListener("beforeinput",T),t.removeEventListener("paste",S),t.removeEventListener("drop",N),t.removeEventListener("focusout",R),t.removeEventListener("input",k),t.removeAttribute("contenteditable"),t.removeAttribute("role"),t.removeAttribute("aria-label"),t.classList.remove("ff-editing"),e.badge?.remove(),i===e&&(i=null)}function x(){let e=i;if(!e||e.done)return;let t=D(e.element);w(e),f({type:"formfeed:edit-commit",token:e.token,items:t})}function E(e){let t=i;if(!t||t.done)return;let n=t.element;for(;n.firstChild;)n.removeChild(n.firstChild);for(let r of t.saved)n.appendChild(r);w(t),e&&f({type:"formfeed:edit-cancel",token:t.token})}}function D(l){let d=[],m=o=>{let i=d[d.length-1];i?.kind==="text"?i.text+=o:o&&d.push({kind:"text",text:o})},g=o=>{for(let i of Array.from(o.childNodes))if(i.nodeType===3)m(i.data);else if(i.nodeType===1){let s=i,f=s.getAttribute("data-ff-chip");f!==null?d.push({kind:"chip",index:Number(f)}):s.tagName==="BR"?d.push({kind:"break"}):((s.tagName==="DIV"||s.tagName==="P")&&d.length&&d.push({kind:"break"}),g(s))}};for(g(l);d[d.length-1]?.kind==="break";)d.pop();return d}F(window);})();';
5540
5642
  var fitRuntime = '"use strict";(()=>{function a(t,e,n){if(e===t)return t.scrollHeight<=t.clientHeight+.5&&t.scrollWidth<=t.clientWidth+.5;let o=n.getComputedStyle(t),i=t.clientHeight-(parseFloat(o.paddingTop)||0)-(parseFloat(o.paddingBottom)||0);return e.scrollHeight<=i+.5&&e.scrollWidth<=e.clientWidth+.5}function m(t,e){let n=t.firstElementChild??t;n.style.fontSize="";let o=parseFloat(e.getComputedStyle(n).fontSize)||16,i=Math.min(o,parseFloat(t.getAttribute("data-ff-fit-min")??"")||o/2);if(a(t,n,e))return o;let l=i,s=o;for(let d=0;d<14;d++){let r=(l+s)/2;n.style.fontSize=`${r}px`,a(t,n,e)?l=r:s=r}let f=Math.floor(l*100)/100;return n.style.fontSize=`${f}px`,f}function c(t){let e=()=>{for(let o of Array.from(t.document.querySelectorAll(\'[data-ff-fit="shrink"]\')))m(o,t);t.document.documentElement.setAttribute("data-ff-fit","done")};t.formfeedFit=e;let n=t.document.fonts;n?n.ready.then(e):t.addEventListener("load",e)}c(window);})();';
5541
- var stageRuntime = '"use strict";(()=>{var I=`\nhtml.ff-edit-mode [data-ff-edit] { cursor: text; }\nhtml.ff-edit-mode [data-ff-edit]:hover { outline: 1px dashed rgba(37,99,235,.85); outline-offset: 2px; }\nhtml.ff-edit-mode [data-ff-edit="t"]:hover { outline-color: rgba(147,51,234,.9); }\n.ff-editing { outline: 2px solid #2563eb !important; outline-offset: 2px; cursor: text; }\n.ff-editing .ff-chip { background: rgba(37,99,235,.14) !important; box-shadow: inset 0 0 0 1px rgba(37,99,235,.45) !important; border-radius: 3px !important; padding: 0 1px !important; cursor: default; }\n.ff-edit-badge { position: absolute; z-index: 2147483647; font: 500 11px/1.4 system-ui, sans-serif !important; color: #fff !important; background: #2563eb !important; padding: 1px 6px !important; border-radius: 4px !important; pointer-events: none; white-space: nowrap; }\n`;function D(a,l={}){let f=l.clicks!==!1,c=l.live===!0,o=a.document,n=null,s=null,u=e=>{try{a.parent.postMessage(e,"*")}catch{}},p=o.createElement("style");p.setAttribute("data-formfeed","edit"),p.setAttribute("data-pagedjs-ignore",""),p.textContent=I,(o.head??o.documentElement).appendChild(p),f&&o.documentElement.classList.add("ff-edit-mode");let d=e=>(e&&typeof e.closest=="function"?e:null)?.closest("[data-ff-edit]")??null;o.addEventListener("click",e=>{if(!f)return;if(n&&!n.done&&n.element.contains(e.target)){e.stopImmediatePropagation();return}let t=d(e.target);if(!t)return;e.preventDefault(),e.stopImmediatePropagation(),n&&!n.done&&v();let r=t.getAttribute("data-ff-src")??"",i=t.getBoundingClientRect();s={src:r,element:t,x:e.clientX,y:e.clientY},u({type:"formfeed:edit-request",src:r,edit:t.getAttribute("data-ff-edit"),text:t.textContent??"",count:o.querySelectorAll(`[data-ff-src="${r.replace(/"/g,"")}"]`).length,split:t.hasAttribute("data-split-from")||t.hasAttribute("data-split-to"),rect:{left:i.left,top:i.top,width:i.width,height:i.height}})},!0),o.addEventListener("keydown",e=>{if(!f||n&&!n.done||!(e.ctrlKey||e.metaKey)||e.altKey)return;let t=e.key.toLowerCase();(t==="z"||t==="y")&&(e.preventDefault(),u({type:t==="y"||e.shiftKey?"formfeed:redo":"formfeed:undo"}))}),a.addEventListener("message",e=>{if(e.source!==a.parent)return;let t=e.data;!t||typeof t.type!="string"||(t.type==="formfeed:edit-begin"?b(t):(t.type==="formfeed:edit-refuse"||t.type==="formfeed:edit-end")&&(s=null,n&&!n.done&&L(!1)))});function b(e){n&&!n.done&&L(!1);let t=e.layer?o.querySelector(`[data-ff-layer="${String(e.layer).replace(/"/g,"")}"] > .ff-text`):s&&s.src===e.src?s.element:o.querySelector(`[data-ff-src="${String(e.src).replace(/"/g,"")}"]`),r=e.at,i=e.layer?r&&Number.isFinite(r.x)&&Number.isFinite(r.y)?{x:Number(r.x),y:Number(r.y)}:null:s&&s.src===e.src?s:null;if(s=null,!t||!Array.isArray(e.items)){u({type:"formfeed:edit-cancel",token:String(e.token)});return}let x=Array.from(t.childNodes),g=o.createDocumentFragment();for(let y of e.items)if(y.kind==="text")g.appendChild(o.createTextNode(String(y.text)));else if(y.kind==="break")g.appendChild(o.createElement("br"));else if(y.kind==="chip"){let m=o.createElement("span");m.className="ff-chip",m.setAttribute("data-ff-chip",String(y.index)),m.setAttribute("contenteditable","false"),y.title&&(m.title=String(y.title)),m.textContent=String(y.text),g.appendChild(m)}for(let y of x)t.removeChild(y);t.appendChild(g),t.contentEditable="true",t.classList.add("ff-editing"),t.setAttribute("role","textbox"),e.label&&t.setAttribute("aria-label",String(e.label)),t.spellcheck=!0,n={token:String(e.token),element:t,saved:x,allowBreaks:e.allowBreaks===!0,badge:e.note?E(t,String(e.note)):null,done:!1},t.addEventListener("keydown",C),t.addEventListener("beforeinput",S),t.addEventListener("paste",A),t.addEventListener("drop",T),t.addEventListener("focusout",N),c&&t.addEventListener("input",k),t.focus(),e.select==="all"?H(t):M(t,i),u({type:"formfeed:edit-started",token:n.token}),typeof e.insert=="string"&&e.insert&&h(e.insert.replace(/\\r\\n?|\\n/g," "))}function h(e){if(typeof o.execCommand=="function"&&o.execCommand("insertText",!1,e))return;let t=a.getSelection(),r=t&&t.rangeCount?t.getRangeAt(0):null;r&&(r.deleteContents(),r.insertNode(o.createTextNode(e)),r.collapse(!1),c&&k())}function k(){n&&!n.done&&u({type:"formfeed:edit-input",token:n.token,items:w(n.element)})}function E(e,t){let r=o.createElement("div");r.className="ff-edit-badge",r.textContent=t;let i=e.getBoundingClientRect();return r.style.left=`${i.left+a.scrollX}px`,r.style.top=`${Math.max(0,i.top+a.scrollY-22)}px`,o.body.appendChild(r),r}function H(e){let t=a.getSelection();if(!t)return;let r=o.createRange();r.selectNodeContents(e),t.removeAllRanges(),t.addRange(r)}function M(e,t){let r=a.getSelection();if(!r)return;let i=null,x=o;if(t){if(x.caretRangeFromPoint)i=x.caretRangeFromPoint(t.x,t.y);else if(x.caretPositionFromPoint){let g=x.caretPositionFromPoint(t.x,t.y);g&&(i=o.createRange(),i.setStart(g.offsetNode,g.offset))}}if(!i||!e.contains(i.startContainer))i=o.createRange(),i.selectNodeContents(e),i.collapse(!1);else if(t){let g=i.startContainer,m=(g.nodeType===1?g:g.parentElement)?.closest("[data-ff-chip]")??null;if(m&&m!==e&&e.contains(m)){let F=m.getBoundingClientRect();i=o.createRange(),t.x<F.left+F.width/2?i.setStartBefore(m):i.setStartAfter(m),i.collapse(!0)}}r.removeAllRanges(),r.addRange(i)}function C(e){let t=e.key;t==="Escape"?(e.preventDefault(),L(!0)):t==="Enter"&&!e.shiftKey?(e.preventDefault(),v()):t==="Enter"?(e.preventDefault(),n?.allowBreaks&&P()):t==="Tab"?(e.preventDefault(),v()):(e.ctrlKey||e.metaKey)&&["b","i","u"].includes(t.toLowerCase())&&(e.preventDefault(),c&&n&&!e.altKey&&u({type:"formfeed:edit-style",token:n.token,key:t.toLowerCase()}))}function S(e){let t=e.inputType??"";(t.startsWith("format")||t==="insertFromDrop"||t==="insertParagraph"||t==="insertLineBreak")&&e.preventDefault()}function A(e){e.preventDefault(),h((e.clipboardData?.getData("text/plain")??"").replace(/\\r\\n?|\\n/g," "))}function T(e){e.preventDefault()}function N(){a.setTimeout(()=>{n&&!n.done&&(!o.hasFocus()||!n.element.contains(o.activeElement))&&v()},0)}function P(){let e=a.getSelection();if(!e||!e.rangeCount)return;let t=e.getRangeAt(0);t.deleteContents();let r=o.createElement("br");t.insertNode(r),r.nextSibling||r.parentNode?.appendChild(o.createElement("br"));let i=o.createRange();i.setStartAfter(r),i.collapse(!0),e.removeAllRanges(),e.addRange(i)}function R(e){e.done=!0;let t=e.element;t.removeEventListener("keydown",C),t.removeEventListener("beforeinput",S),t.removeEventListener("paste",A),t.removeEventListener("drop",T),t.removeEventListener("focusout",N),t.removeEventListener("input",k),t.removeAttribute("contenteditable"),t.removeAttribute("role"),t.removeAttribute("aria-label"),t.classList.remove("ff-editing"),e.badge?.remove(),n===e&&(n=null)}function v(){let e=n;if(!e||e.done)return;let t=w(e.element);R(e),u({type:"formfeed:edit-commit",token:e.token,items:t})}function L(e){let t=n;if(!t||t.done)return;let r=t.element;for(;r.firstChild;)r.removeChild(r.firstChild);for(let i of t.saved)r.appendChild(i);R(t),e&&u({type:"formfeed:edit-cancel",token:t.token})}}function w(a){let l=[],f=o=>{let n=l[l.length-1];n?.kind==="text"?n.text+=o:o&&l.push({kind:"text",text:o})},c=o=>{for(let n of Array.from(o.childNodes))if(n.nodeType===3)f(n.data);else if(n.nodeType===1){let s=n,u=s.getAttribute("data-ff-chip");u!==null?l.push({kind:"chip",index:Number(u)}):s.tagName==="BR"?l.push({kind:"break"}):((s.tagName==="DIV"||s.tagName==="P")&&l.length&&l.push({kind:"break"}),c(s))}};for(c(a);l[l.length-1]?.kind==="break";)l.pop();return l}function K(a,l){return Array.from(a.querySelectorAll("[data-ff-layer]")).map(f=>{let c=f.querySelector(":scope > .ff-text"),o=!1;if(c&&f.style.height!=="auto"){let n=l.getComputedStyle(f),s=f.clientHeight-(parseFloat(n.paddingTop)||0)-(parseFloat(n.paddingBottom)||0);o=n.overflow==="hidden"&&(c.scrollHeight>s+1||c.scrollWidth>c.clientWidth+1)}return{id:f.getAttribute("data-ff-layer")??"",width:f.offsetWidth,height:f.offsetHeight,overflow:o}})}function B(a){let l=a.document,f=p=>{try{a.parent.postMessage(p,"*")}catch{}},c=p=>typeof p=="string"?l.querySelector(`[data-ff-layer="${p.replace(/"/g,"")}"]`):null,o=()=>{for(let p of Array.from(l.querySelectorAll("[data-ff-layer]")))for(let d of["left","top","width","height","transform"])p.style.removeProperty(d)},n=()=>{a.formfeedFit?.(),f({type:"formfeed:ready",layers:K(l,a)})};a.addEventListener("message",p=>{if(p.source!==a.parent)return;let d=p.data;if(!(!d||typeof d.type!="string")){if(d.type==="formfeed:layer-style"){let b=c(d.id);if(!b)return;let h=(k,E)=>{typeof E=="number"&&Number.isFinite(E)&&b.style.setProperty(k,`${E}px`)};h("left",d.left),h("top",d.top),h("width",d.width),h("height",d.height),typeof d.rotation=="number"&&Number.isFinite(d.rotation)&&b.style.setProperty("transform",d.rotation?`rotate(${d.rotation}deg)`:"none")}else if(d.type==="formfeed:layer-style-clear")o();else if(d.type==="formfeed:css"&&typeof d.css=="string"){let b=l.querySelector(\'style[data-formfeed="template"]\');b&&(b.textContent=d.css),o(),n()}}}),D(a,{clicks:!1,live:!0});let s=l.fonts,u=()=>s?void s.ready.then(n):n();l.readyState==="complete"?u():a.addEventListener("load",u)}B(window);})();';
5643
+ var stageRuntime = '"use strict";(()=>{var W=`\nhtml.ff-edit-mode [data-ff-edit] { cursor: text; }\nhtml.ff-edit-mode [data-ff-edit]:hover { outline: 1px dashed rgba(37,99,235,.85); outline-offset: 2px; }\nhtml.ff-edit-mode [data-ff-edit="t"]:hover { outline-color: rgba(147,51,234,.9); }\n.ff-editing { outline: 2px solid #2563eb !important; outline-offset: 2px; cursor: text; }\n.ff-editing .ff-chip { background: rgba(37,99,235,.14) !important; box-shadow: inset 0 0 0 1px rgba(37,99,235,.45) !important; border-radius: 3px !important; padding: 0 1px !important; cursor: default; }\n.ff-edit-badge { position: absolute; z-index: 2147483647; font: 500 11px/1.4 system-ui, sans-serif !important; color: #fff !important; background: #2563eb !important; padding: 1px 6px !important; border-radius: 4px !important; pointer-events: none; white-space: nowrap; }\n`;function H(a,l={}){let f=l.clicks!==!1,p=l.live===!0,n=a.document,o=null,d=null,u=e=>{try{a.parent.postMessage(e,"*")}catch{}},m=f?n.querySelector(\'meta[name="formfeed:document"]\')?.getAttribute("content")??null:null,s=!1;a.addEventListener("blur",()=>{s=!0}),a.addEventListener("focus",()=>{s=!1});let g=n.createElement("style");g.setAttribute("data-formfeed","edit"),g.setAttribute("data-pagedjs-ignore",""),g.textContent=W,(n.head??n.documentElement).appendChild(g),f&&n.documentElement.classList.add("ff-edit-mode");let x=e=>(e&&typeof e.closest=="function"?e:null)?.closest("[data-ff-edit]")??null;n.addEventListener("click",e=>{if(!f)return;if(o&&!o.done&&o.element.contains(e.target)){e.stopImmediatePropagation();return}let t=x(e.target);t&&(e.preventDefault(),e.stopImmediatePropagation(),v(t,{x:e.clientX,y:e.clientY}))},!0);function v(e,t){o&&!o.done&&L();let r=e.getAttribute("data-ff-src")??"",i=e.getBoundingClientRect(),b=Array.from(n.querySelectorAll(`[data-ff-src="${r.replace(/"/g,"")}"]`));d={src:r,element:e,point:t},u({type:"formfeed:edit-request",src:r,doc:m,edit:e.getAttribute("data-ff-edit"),text:e.textContent??"",elements:e.childElementCount,count:b.length,index:Math.max(0,b.indexOf(e)),split:e.hasAttribute("data-split-from")||e.hasAttribute("data-split-to"),rect:{left:i.left,top:i.top,width:i.width,height:i.height}})}n.addEventListener("keydown",e=>{if(!f||o&&!o.done||!(e.ctrlKey||e.metaKey)||e.altKey)return;let t=e.key.toLowerCase();(t==="z"||t==="y")&&(e.preventDefault(),u({type:t==="y"||e.shiftKey?"formfeed:redo":"formfeed:undo"}))}),a.addEventListener("message",e=>{if(e.source!==a.parent)return;let t=e.data;!t||typeof t.type!="string"||(t.type==="formfeed:edit-begin"?B(t):t.type==="formfeed:edit-refuse"||t.type==="formfeed:edit-end"?(d=null,o&&!o.done&&S(!1)):t.type==="formfeed:edit-open"&&E(t))});function E(e){if(!f||typeof e.src!="string"||o&&!o.done)return;let t=Array.from(n.querySelectorAll(`[data-ff-src="${e.src.replace(/"/g,"")}"][data-ff-edit]`)),r=Number.isInteger(e.index)?Number(e.index):0,i=t[r]??t[0];i&&v(i,null)}if(f){let e=()=>u({type:"formfeed:edit-ready",doc:m}),t=a;t.PagedConfig?t.formfeedEditReady=e:n.readyState==="loading"?n.addEventListener("DOMContentLoaded",e):e()}function B(e){o&&!o.done&&S(!1);let t=e.layer?n.querySelector(`[data-ff-layer="${String(e.layer).replace(/"/g,"")}"] > .ff-text`):d&&d.src===e.src?d.element:n.querySelector(`[data-ff-src="${String(e.src).replace(/"/g,"")}"]`),r=e.at,i=e.layer?r&&Number.isFinite(r.x)&&Number.isFinite(r.y)?{x:Number(r.x),y:Number(r.y)}:null:d&&d.src===e.src?d.point:null;d=null;let b=s&&!n.hasFocus();if(!t||!Array.isArray(e.items)||b){u({type:"formfeed:edit-cancel",token:String(e.token)});return}let y=Array.from(t.childNodes),k=n.createDocumentFragment();for(let c of e.items)if(c.kind==="text")k.appendChild(n.createTextNode(String(c.text)));else if(c.kind==="break")k.appendChild(n.createElement("br"));else if(c.kind==="chip"){let h=n.createElement("span");h.className="ff-chip",h.setAttribute("data-ff-chip",String(c.index)),h.setAttribute("contenteditable","false"),c.title&&(h.title=String(c.title)),h.textContent=String(c.text),k.appendChild(h)}for(let c of y)t.removeChild(c);t.appendChild(k),t.contentEditable="true",t.classList.add("ff-editing"),t.setAttribute("role","textbox"),e.label&&t.setAttribute("aria-label",String(e.label)),t.spellcheck=!0,o={token:String(e.token),element:t,saved:y,allowBreaks:e.allowBreaks===!0,badge:e.note?I(t,String(e.note)):null,done:!1},t.addEventListener("keydown",T),t.addEventListener("beforeinput",N),t.addEventListener("paste",w),t.addEventListener("drop",R),t.addEventListener("focusout",F),p&&t.addEventListener("input",C),t.focus(),e.select==="all"?q(t):K(t,i),u({type:"formfeed:edit-started",token:o.token}),typeof e.insert=="string"&&e.insert&&A(e.insert.replace(/\\r\\n?|\\n/g," "))}function A(e){if(typeof n.execCommand=="function"&&n.execCommand("insertText",!1,e))return;let t=a.getSelection(),r=t&&t.rangeCount?t.getRangeAt(0):null;r&&(r.deleteContents(),r.insertNode(n.createTextNode(e)),r.collapse(!1),p&&C())}function C(){o&&!o.done&&u({type:"formfeed:edit-input",token:o.token,items:D(o.element)})}function I(e,t){let r=n.createElement("div");r.className="ff-edit-badge",r.textContent=t;let i=e.getBoundingClientRect();return r.style.left=`${i.left+a.scrollX}px`,r.style.top=`${Math.max(0,i.top+a.scrollY-22)}px`,n.body.appendChild(r),r}function q(e){let t=a.getSelection();if(!t)return;let r=n.createRange();r.selectNodeContents(e),t.removeAllRanges(),t.addRange(r)}function K(e,t){let r=a.getSelection();if(!r)return;let i=null,b=n;if(t){if(b.caretRangeFromPoint)i=b.caretRangeFromPoint(t.x,t.y);else if(b.caretPositionFromPoint){let y=b.caretPositionFromPoint(t.x,t.y);y&&(i=n.createRange(),i.setStart(y.offsetNode,y.offset))}}if(!i||!e.contains(i.startContainer))i=n.createRange(),i.selectNodeContents(e),i.collapse(!1);else if(t){let y=i.startContainer,c=(y.nodeType===1?y:y.parentElement)?.closest("[data-ff-chip]")??null;if(c&&c!==e&&e.contains(c)){let h=c.getBoundingClientRect();i=n.createRange(),t.x<h.left+h.width/2?i.setStartBefore(c):i.setStartAfter(c),i.collapse(!0)}}r.removeAllRanges(),r.addRange(i)}function T(e){let t=e.key;t==="Escape"?(e.preventDefault(),S(!0)):t==="Enter"&&!e.shiftKey?(e.preventDefault(),L()):t==="Enter"?(e.preventDefault(),o?.allowBreaks&&$()):t==="Tab"?(e.preventDefault(),L()):(e.ctrlKey||e.metaKey)&&["b","i","u"].includes(t.toLowerCase())&&(e.preventDefault(),p&&o&&!e.altKey&&u({type:"formfeed:edit-style",token:o.token,key:t.toLowerCase()}))}function N(e){let t=e.inputType??"";(t.startsWith("format")||t==="insertFromDrop"||t==="insertParagraph"||t==="insertLineBreak")&&e.preventDefault()}function w(e){e.preventDefault(),A((e.clipboardData?.getData("text/plain")??"").replace(/\\r\\n?|\\n/g," "))}function R(e){e.preventDefault()}function F(){a.setTimeout(()=>{o&&!o.done&&(!n.hasFocus()||!o.element.contains(n.activeElement))&&L()},0)}function $(){let e=a.getSelection();if(!e||!e.rangeCount)return;let t=e.getRangeAt(0);t.deleteContents();let r=n.createElement("br");t.insertNode(r),r.nextSibling||r.parentNode?.appendChild(n.createElement("br"));let i=n.createRange();i.setStartAfter(r),i.collapse(!0),e.removeAllRanges(),e.addRange(i)}function M(e){e.done=!0;let t=e.element;t.removeEventListener("keydown",T),t.removeEventListener("beforeinput",N),t.removeEventListener("paste",w),t.removeEventListener("drop",R),t.removeEventListener("focusout",F),t.removeEventListener("input",C),t.removeAttribute("contenteditable"),t.removeAttribute("role"),t.removeAttribute("aria-label"),t.classList.remove("ff-editing"),e.badge?.remove(),o===e&&(o=null)}function L(){let e=o;if(!e||e.done)return;let t=D(e.element);M(e),u({type:"formfeed:edit-commit",token:e.token,items:t})}function S(e){let t=o;if(!t||t.done)return;let r=t.element;for(;r.firstChild;)r.removeChild(r.firstChild);for(let i of t.saved)r.appendChild(i);M(t),e&&u({type:"formfeed:edit-cancel",token:t.token})}}function D(a){let l=[],f=n=>{let o=l[l.length-1];o?.kind==="text"?o.text+=n:n&&l.push({kind:"text",text:n})},p=n=>{for(let o of Array.from(n.childNodes))if(o.nodeType===3)f(o.data);else if(o.nodeType===1){let d=o,u=d.getAttribute("data-ff-chip");u!==null?l.push({kind:"chip",index:Number(u)}):d.tagName==="BR"?l.push({kind:"break"}):((d.tagName==="DIV"||d.tagName==="P")&&l.length&&l.push({kind:"break"}),p(d))}};for(p(a);l[l.length-1]?.kind==="break";)l.pop();return l}function O(a,l){return Array.from(a.querySelectorAll("[data-ff-layer]")).map(f=>{let p=f.querySelector(":scope > .ff-text"),n=!1;if(p&&f.style.height!=="auto"){let o=l.getComputedStyle(f),d=f.clientHeight-(parseFloat(o.paddingTop)||0)-(parseFloat(o.paddingBottom)||0);n=o.overflow==="hidden"&&(p.scrollHeight>d+1||p.scrollWidth>p.clientWidth+1)}return{id:f.getAttribute("data-ff-layer")??"",width:f.offsetWidth,height:f.offsetHeight,overflow:n}})}function P(a){let l=a.document,f=m=>{try{a.parent.postMessage(m,"*")}catch{}},p=m=>typeof m=="string"?l.querySelector(`[data-ff-layer="${m.replace(/"/g,"")}"]`):null,n=()=>{for(let m of Array.from(l.querySelectorAll("[data-ff-layer]")))for(let s of["left","top","width","height","transform"])m.style.removeProperty(s)},o=()=>{a.formfeedFit?.(),f({type:"formfeed:ready",layers:O(l,a)})};a.addEventListener("message",m=>{if(m.source!==a.parent)return;let s=m.data;if(!(!s||typeof s.type!="string")){if(s.type==="formfeed:layer-style"){let g=p(s.id);if(!g)return;let x=(v,E)=>{typeof E=="number"&&Number.isFinite(E)&&g.style.setProperty(v,`${E}px`)};x("left",s.left),x("top",s.top),x("width",s.width),x("height",s.height),typeof s.rotation=="number"&&Number.isFinite(s.rotation)&&g.style.setProperty("transform",s.rotation?`rotate(${s.rotation}deg)`:"none")}else if(s.type==="formfeed:layer-style-clear")n();else if(s.type==="formfeed:css"&&typeof s.css=="string"){let g=l.querySelector(\'style[data-formfeed="template"]\');g&&(g.textContent=s.css),n(),o()}}}),H(a,{clicks:!1,live:!0});let d=l.fonts,u=()=>d?void d.ready.then(o):o();l.readyState==="complete"?u():a.addEventListener("load",u)}P(window);})();';
5644
+ var auditRuntime = '"use strict";(()=>{var I={"document-title":"error","image-alt":"error","figure-name":"error","heading-order":"error","table-rows":"error","table-cell-empty":"error","masked-text":"error","filtered-text":"warning","document-language":"warning","table-headers":"warning","link-name":"warning","generated-text":"warning","fixed-text":"warning","running-text":"warning"};var S=120;var O=".formfeed-chrome, .ff-running-header, .ff-running-footer",_=".pageNumber, .totalPages, .date, .title, .url, .ff-page-no, .ff-page-total",k=new Set(["script","style","template","noscript","head"]),j=2e4,q=10,B=new Set(["page","pages","of","seite","seiten","von","p","s"]),u=t=>(t??"").replace(/\\s+/g," ").trim(),D=t=>t.replace(/\\s+/g,"").toLowerCase(),$=t=>/[\\p{L}\\p{N}]/u.test(t),Y=/^data-(ref|id|split-(from|to|original)|(previous-|next-)?break-(before|after|inside)|(align-)?last-split-element|nth-of-type|counter-.+|page(-number)?|undisplayed|after-page|following|has-notes|children|item-num)$/;function X(t){let n=t.cloneNode(!1);n.removeAttribute("data-ff-src"),n.removeAttribute("data-ff-edit");let i=t.closest(".pagedjs_page")!==null;for(let s of Array.from(n.attributes))i&&Y.test(s.name)?n.removeAttribute(s.name):s.value.length>48&&n.setAttribute(s.name,`${s.value.slice(0,40)}\\u2026`);let o=n.outerHTML.replace(new RegExp(`</${t.tagName}>$`,"i"),"");return o.length>S?`${o.slice(0,S-1)}\\u2026`:o}function F(t,n={}){let i=t.document,o=i.body,s=[],d=new Set,a=n.findings!==!1,c=(e,r,l={})=>{if(!a)return;let m=r?.closest("[data-ff-src]")?.getAttribute("data-ff-src")??null,h=r?X(r):"",g=`${e}|${m??h}|${JSON.stringify(l)}`;d.has(g)||(d.add(g),s.push({code:e,severity:I[e],src:m,snippet:h,args:l}))},f=new WeakMap,A=e=>{let r=f.get(e);if(r!==void 0)return r;let l=t.getComputedStyle(e),m=l.display==="none"||l.visibility==="hidden"||l.visibility==="collapse"||e.parentElement!==null&&e.parentElement!==i.documentElement&&A(e.parentElement);return f.set(e,m),m},p=e=>e.closest(\'[aria-hidden="true"]\')!==null,E=e=>/^(presentation|none)$/.test(e.getAttribute("role")??""),H=e=>e.closest(O)!==null,y=e=>$(e.textContent??""),x=e=>{let r=u(e.getAttribute("aria-label"));return r||u((e.getAttribute("aria-labelledby")??"").split(/\\s+/).filter(Boolean).map(l=>e.ownerDocument.getElementById(l)?.textContent??"").join(" "))},P=e=>x(e)||u(e.textContent)||u(Array.from(e.querySelectorAll("img[alt]")).map(r=>r.getAttribute("alt")).join(" "))||u(e.getAttribute("title")),T=e=>{let r=e.getAttribute("href")??"";return{href:r.startsWith("#")?r:e.href??r,name:P(e)}};u(i.title)||c("document-title",null),!u(i.documentElement.getAttribute("lang"))&&!n.locale&&c("document-language",null);let M=n.pages??(n.pageHeight&&n.pageHeight>0?Math.ceil(i.documentElement.scrollHeight/n.pageHeight):1),C=[],v=[],R={"masked-text":[],"filtered-text":[],"fixed-text":[]},w=(e,r)=>{R[e].some(l=>l.contains(r))||(R[e].push(r),c(e,r))},V=a&&ne(i),G=o?Array.from(o.querySelectorAll("*")).slice(0,j):[];for(let e of G){let r=e.tagName.toLowerCase();if(k.has(r)||H(e)||A(e))continue;if(r==="a"&&e.hasAttribute("href")){let g=T(e);C.push(g),!g.name&&!p(e)&&c("link-name",e)}if(!a)continue;let l=t.getComputedStyle(e),m=l.getPropertyValue("mask-image")||l.getPropertyValue("-webkit-mask-image");if(m&&m!=="none"&&y(e)&&w("masked-text",e),l.filter&&l.filter!=="none"&&y(e)&&w("filtered-text",e),l.position==="fixed"&&M>1&&y(e)&&w("fixed-text",e),V)for(let g of["::before","::after"]){let U=re(t.getComputedStyle(e,g).content);U&&c("generated-text",e,{text:U})}if(p(e))continue;r==="img"&&!E(e)?e.hasAttribute("alt")||x(e)||u(e.getAttribute("title"))||c("image-alt",e):(r==="canvas"||e.getAttribute("role")==="img"&&r!=="img")&&(x(e)||u(e.getAttribute("title"))||u(e.querySelector(":scope > title")?.textContent)||c("figure-name",e,{tag:r}));let h=/^h[1-6]$/.test(r)?Number(r[1]):e.getAttribute("role")==="heading"?Number(e.getAttribute("aria-level"))||2:0;h&&v.push({element:e,level:h}),r==="table"&&!E(e)&&Q(e,c)}let b=0;for(let{element:e,level:r}of v)(b===0?r!==1:r>b+1)&&c("heading-order",e,{level:r,previous:b}),b=r;let N=[],W=a&&o?D(J(o)):"",L=0;for(let e of["header","footer"])for(let r of z(i,e,n[e])){for(let l of Array.from(r.querySelectorAll("a[href]")))N.push(T(l));if(a)for(let{text:l,element:m}of K(r)){if(L>=q||W.includes(D(l)))continue;let h=s.length;c("running-text",m,{text:l.slice(0,80),part:e}),s.length>h&&L++}}return{findings:s,links:C,templateLinks:N}}function z(t,n,i){if(i!==void 0){if(!i.trim())return[];let o=new DOMParser().parseFromString(`<!doctype html><body>${i}</body>`,"text/html");return o.body?[o.body]:[]}return Array.from(t.querySelectorAll(`.ff-running-${n}, .formfeed-chrome[data-ff-chrome="${n}"]`))}function J(t){let n=[],i=o=>{if(o.nodeType===3){n.push(o.nodeValue??"");return}if(o.nodeType!==1)return;let s=o;if(!(k.has(s.tagName.toLowerCase())||s.matches(O)))for(let d of Array.from(o.childNodes))i(d)};return i(t),n.join(" ")}function K(t){let n=[],i=o=>{let s=o.tagName.toLowerCase();if(k.has(s)||o.matches(_))return;let d=Array.from(o.children).some(a=>a.matches(_));for(let a of Array.from(o.childNodes))if(a.nodeType===1)i(a);else if(a.nodeType===3&&!d)for(let c of(a.nodeValue??"").split(/\\s*[\xB7\u2022|]\\s*|\\s{2,}|\\n/)){let f=u(c);(f.toLowerCase().match(/[\\p{L}\\p{N}]+/gu)??[]).some(p=>!B.has(p))&&n.push({text:f,element:o})}};return i(t),n}function Q(t,n){let i=Array.from(t.rows);if(!(t.querySelector(\'th, [role="columnheader"], [role="rowheader"]\')!==null)){i.length>=2&&n("table-headers",t);return}let s=te(i),d=s.find(c=>c!==s[0]);d!==void 0&&n("table-rows",t,{first:s[0]??0,other:d});let a=i.flatMap(c=>Array.from(c.cells).filter(f=>f.tagName==="TD"&&ee(f)));a[0]&&n("table-cell-empty",a[0],{count:a.length})}var Z=\'img[alt]:not([alt=""]), svg, canvas, input, select, textarea, button, meter, progress\';function ee(t){return!/[^ \\t\\n\\r\\f]/.test(t.textContent??"")&&t.querySelector(Z)===null}function te(t){let n=[];return t.map(i=>{let o=[],s=0,d=()=>{for(;(n[s]??0)>0;)o[s]=n[s]-1,s++};for(let a of Array.from(i.cells)){d();let c=Math.max(1,a.rowSpan||1)-1;for(let f=Math.max(1,a.colSpan||1);f>0;f--)o[s++]=c}d();for(let a=s;a<n.length;a++)(n[a]??0)>0&&(o[a]=n[a]-1,s=a+1);return n=o,s})}function ne(t){let n=i=>{for(let o of Array.from(i??[])){let s=o;if(s.selectorText&&/::?(before|after)/.test(s.selectorText)&&s.style?.content||o.cssRules&&n(o.cssRules))return!0}return!1};for(let i of Array.from(t.styleSheets))try{if(n(i.cssRules))return!0}catch{return!0}return!1}function re(t){if(!t||t==="none"||t==="normal")return"";let n=(t.match(/"(?:[^"\\\\]|\\\\.)*"/g)??[]).map(o=>o.slice(1,-1).replace(/\\\\(.)/g,"$1"));if(/\\bcounters?\\(/.test(t))return u(t).slice(0,80);let i=u(n.join(""));return $(i)?i.slice(0,80):""}window.formfeedAudit=t=>F(window,t);})();';
5542
5645
 
5543
5646
  // packages/engine/src/lib/assemble.ts
5544
5647
  var DEFAULT_CHROME_PADDING = "0 10mm";
@@ -5773,6 +5876,13 @@ document.addEventListener('click', function (event) {
5773
5876
  if (!el || el.nodeType !== 1) return;
5774
5877
  try {
5775
5878
  var annotated = el.closest ? el.closest('[data-ff-src]') : null;
5879
+ // the paper around the template (the page's margins, the pane behind it) is no element of it:
5880
+ // a click there asks for nothing
5881
+ if (!annotated && (
5882
+ el === document.documentElement || el === document.body ||
5883
+ el.querySelector('[data-ff-src]') ||
5884
+ (el.closest && el.closest('[class*="pagedjs_"]') === el)
5885
+ )) return;
5776
5886
  parent.postMessage({
5777
5887
  type: 'formfeed:inspect',
5778
5888
  src: annotated ? annotated.getAttribute('data-ff-src') : null,
@@ -5786,6 +5896,27 @@ document.addEventListener('click', function (event) {
5786
5896
  }, true);
5787
5897
  </script>`;
5788
5898
  var textEditScript = `<script>${textEditRuntime.replace(/<\/script/gi, "<\\/script")}</script>`;
5899
+ function auditScript(mode) {
5900
+ return `<script>${auditRuntime.replace(/<\/script/gi, "<\\/script")}</script><script>
5901
+ (function () {
5902
+ window.formfeedRunAudit = function (pages) {
5903
+ try {
5904
+ var report = window.formfeedAudit({
5905
+ locale: true,
5906
+ pages: pages,
5907
+ pageHeight: parseFloat(getComputedStyle(document.body).minHeight) || 0,
5908
+ });
5909
+ parent.postMessage({ type: 'formfeed:audit', findings: report.findings }, '*');
5910
+ } catch (e) {}
5911
+ };
5912
+ ${mode === "flow" ? "window.addEventListener('load', function () { (document.fonts ? document.fonts.ready : Promise.resolve()).then(function () { window.formfeedRunAudit(); }); });" : ""}
5913
+ })();
5914
+ </script>`;
5915
+ }
5916
+ function documentMeta(options) {
5917
+ const name = (options.document ?? "").replace(/[^A-Za-z0-9_-]/g, "");
5918
+ return name ? `<meta name="formfeed:document" content="${name}">` : "";
5919
+ }
5789
5920
  var pageNavScript = `<script>
5790
5921
  (function () {
5791
5922
  var pages = function () { return document.querySelectorAll('.pagedjs_page'); };
@@ -5893,6 +6024,15 @@ function intoHead(document, markup) {
5893
6024
  ${markup}`
5894
6025
  );
5895
6026
  }
6027
+ function intoBody(document, atStart, atEnd = "") {
6028
+ const own = document.indexOf('<body class="formfeed-body');
6029
+ const open2 = own >= 0 ? own : document.search(/<body[^>]*>/);
6030
+ if (open2 < 0) return document;
6031
+ const content = document.indexOf(">", open2) + 1;
6032
+ const close = document.lastIndexOf("</body>");
6033
+ const end = close >= content ? close : document.length;
6034
+ return `${document.slice(0, content)}${atStart}${document.slice(content, end)}${atEnd}${document.slice(end)}`;
6035
+ }
5896
6036
  function chromeBoxStyle(settings, which) {
5897
6037
  const margin = settings.margin ?? {};
5898
6038
  const left = margin.left ?? "0px";
@@ -5916,12 +6056,16 @@ html { background: #e5e7eb; }
5916
6056
  body.formfeed-preview { position: relative; box-sizing: border-box; width: ${paper.width}; min-height: ${paper.height}; margin: 24px auto; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.2), 0 8px 24px rgba(0,0,0,.08); padding: ${margin.top ?? "0"} ${margin.right ?? "0"} ${margin.bottom ?? "0"} ${margin.left ?? "0"}; }
5917
6057
  .formfeed-chrome { color: #6b7280; font: 10px/1.3 system-ui, sans-serif; }
5918
6058
  </style>` : `<style data-formfeed="preview">html { background: #e5e7eb; } body.formfeed-preview { margin: 24px auto; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.2); }</style>`;
5919
- const header = headerHtml ? `<div class="formfeed-chrome" style="${pdf ? flowChromeStyle(settings, "header") : `${chromeBoxStyle(settings, "header")}margin-bottom:8px`}">${headerHtml}</div>` : "";
5920
- const footer = footerHtml ? `<div class="formfeed-chrome" style="${pdf ? flowChromeStyle(settings, "footer") : `${chromeBoxStyle(settings, "footer")}margin-top:8px`}">${pageNumberSpans(footerHtml, "1", "1")}</div>` : "";
5921
- return intoHead(
5922
- draft.document,
5923
- `${chrome}${options.editing ? textEditScript : ""}${inspectScript}${zoomGestureScript}${pdf ? overflowScript("flow") : ""}`
5924
- ).replace(/(<body[^>]*>)/, `$1${header}`).replace("</body>", `${footer}</body>`);
6059
+ const header = headerHtml ? `<div class="formfeed-chrome" data-ff-chrome="header" style="${pdf ? flowChromeStyle(settings, "header") : `${chromeBoxStyle(settings, "header")}margin-bottom:8px`}">${headerHtml}</div>` : "";
6060
+ const footer = footerHtml ? `<div class="formfeed-chrome" data-ff-chrome="footer" style="${pdf ? flowChromeStyle(settings, "footer") : `${chromeBoxStyle(settings, "footer")}margin-top:8px`}">${pageNumberSpans(footerHtml, "1", "1")}</div>` : "";
6061
+ return intoBody(
6062
+ intoHead(
6063
+ draft.document,
6064
+ `${documentMeta(options)}${chrome}${options.editing ? textEditScript : ""}${inspectScript}${zoomGestureScript}${pdf ? overflowScript("flow") : ""}${pdf && options.audit ? auditScript("flow") : ""}`
6065
+ ),
6066
+ header,
6067
+ footer
6068
+ );
5925
6069
  }
5926
6070
  function canvasDocument(draft) {
5927
6071
  const chrome = `<style data-formfeed="stage">html, body { background: transparent !important; margin: 0 !important; }</style>`;
@@ -5948,14 +6092,17 @@ html { background: #e5e7eb; }
5948
6092
  `;
5949
6093
  const selectorGuard = `<script>(function(){var q=DocumentFragment.prototype.querySelectorAll;DocumentFragment.prototype.querySelectorAll=function(s){try{return q.call(this,s);}catch(e){console.warn('formfeed paged preview: skipped a selector Paged.js could not use:',s);return q.call(this,':not(*)');}};})();</script>`;
5950
6094
  const boxSizingGuard = `<script>(function(){var rule='.pagedjs_pagebox *';var o=new MutationObserver(function(ms){ms.forEach(function(m){m.addedNodes.forEach(function(n){if(n.nodeName==='STYLE'&&n.textContent&&n.textContent.indexOf(rule)>=0){n.textContent=n.textContent.split(rule).join('.pagedjs_pagebox [class*="pagedjs_"]');o.disconnect();}});});});o.observe(document.documentElement,{childList:true,subtree:true});})();</script>`;
5951
- const config = `<script>window.PagedConfig = { auto: true, after: function (flow) { try { if (window.formfeedDrawCharts) window.formfeedDrawCharts(); } catch (e) {} try { if (window.formfeedFit) window.formfeedFit(); } catch (e) {} try { parent.postMessage({ type: 'formfeed:pages', pages: flow.total }, '*'); } catch (e) {} if (window.formfeedCheckOverflow) window.formfeedCheckOverflow(); } };</script>`;
6095
+ const config = `<script>window.PagedConfig = { auto: true, after: function (flow) { try { if (window.formfeedDrawCharts) window.formfeedDrawCharts(); } catch (e) {} try { if (window.formfeedFit) window.formfeedFit(); } catch (e) {} try { parent.postMessage({ type: 'formfeed:pages', pages: flow.total }, '*'); } catch (e) {} if (window.formfeedCheckOverflow) window.formfeedCheckOverflow(); if (window.formfeedRunAudit) window.formfeedRunAudit(flow.total); try { if (window.formfeedEditReady) window.formfeedEditReady(); } catch (e) {} } };</script>`;
5952
6096
  const script = `<script src="${options.pagedScriptUrl}"></script>`;
5953
6097
  const header = headerHtml ? `<div class="ff-running-header">${pageNumberSpans(headerHtml, '<span class="ff-page-no"></span>', '<span class="ff-page-total"></span>')}</div>` : "";
5954
6098
  const footer = footerHtml ? `<div class="ff-running-footer">${pageNumberSpans(footerHtml, '<span class="ff-page-no"></span>', '<span class="ff-page-total"></span>')}</div>` : "";
5955
- return intoHead(
5956
- draft.document,
5957
- `<style data-formfeed="paged">${runningCss}</style>${selectorGuard}${boxSizingGuard}${overflowScript("paged")}${config}${script}${options.editing ? textEditScript : ""}${inspectScript}${pageNavScript}${zoomGestureScript}`
5958
- ).replace(/(<body[^>]*>)/, `$1${header}${footer}`);
6099
+ return intoBody(
6100
+ intoHead(
6101
+ draft.document,
6102
+ `${documentMeta(options)}<style data-formfeed="paged">${runningCss}</style>${selectorGuard}${boxSizingGuard}${overflowScript("paged")}${options.audit ? auditScript("paged") : ""}${config}${script}${options.editing ? textEditScript : ""}${inspectScript}${pageNavScript}${zoomGestureScript}`
6103
+ ),
6104
+ `${header}${footer}`
6105
+ );
5959
6106
  }
5960
6107
 
5961
6108
  // packages/engine/src/lib/source-scan.ts
@@ -6187,11 +6334,11 @@ function locate(starts, offset) {
6187
6334
  }
6188
6335
  return { line: low + 1, column: offset - (starts[low] ?? 0) + 1 };
6189
6336
  }
6190
- function offsetOf(starts, sourceLength, line, column) {
6191
- const start = starts[line - 1];
6337
+ function offsetOf(starts, sourceLength, line2, column) {
6338
+ const start = starts[line2 - 1];
6192
6339
  if (start === void 0 || column < 1) return null;
6193
6340
  const offset = start + column - 1;
6194
- const next = starts[line] ?? sourceLength + 1;
6341
+ const next = starts[line2] ?? sourceLength + 1;
6195
6342
  return offset < next ? offset : null;
6196
6343
  }
6197
6344
 
@@ -6347,7 +6494,10 @@ function editableRegionAt(source, engine, at) {
6347
6494
  const hasText = region.parts.some(
6348
6495
  (p) => p.kind === "text" && decodeText2(source.slice(p.start, p.end)).text.trim() !== ""
6349
6496
  );
6350
- return hasText ? region : { reason: "no-text" };
6497
+ const hasField = region.parts.some(
6498
+ (p) => p.kind === "chip" && p.chip === "expression"
6499
+ );
6500
+ return hasText || hasField ? region : { reason: "no-text" };
6351
6501
  }
6352
6502
  function findRegionAgain(source, engine, outer) {
6353
6503
  const first = source.indexOf(outer);
@@ -6355,6 +6505,9 @@ function findRegionAgain(source, engine, outer) {
6355
6505
  const found = editableRegionAt(source, engine, first);
6356
6506
  return "reason" in found ? null : found;
6357
6507
  }
6508
+ function regionPosition(source, region) {
6509
+ return locate(lineStarts(source), region.tagStart);
6510
+ }
6358
6511
  function regionChips(source, region) {
6359
6512
  const chips = [];
6360
6513
  for (const part of region.parts)
@@ -6439,18 +6592,18 @@ function applyRegionEdit(source, engine, region, items) {
6439
6592
  const rewritten = source.slice(0, region.start) + whole + source.slice(region.end);
6440
6593
  return readsBack(rewritten, engine, region.tagStart, expected) ? { source: rewritten } : { error: "invalid" };
6441
6594
  }
6442
- function translationItems(text2) {
6595
+ function translationItems(text4) {
6443
6596
  const items = [];
6444
6597
  const names = [];
6445
6598
  let last = 0;
6446
- for (const match of text2.matchAll(/\{(\w+)\}/g)) {
6599
+ for (const match of text4.matchAll(/\{(\w+)\}/g)) {
6447
6600
  if (match.index > last)
6448
- items.push({ kind: "text", text: text2.slice(last, match.index) });
6601
+ items.push({ kind: "text", text: text4.slice(last, match.index) });
6449
6602
  items.push({ kind: "chip", index: names.length });
6450
6603
  names.push(match[1] ?? "");
6451
6604
  last = match.index + match[0].length;
6452
6605
  }
6453
- if (last < text2.length) items.push({ kind: "text", text: text2.slice(last) });
6606
+ if (last < text4.length) items.push({ kind: "text", text: text4.slice(last) });
6454
6607
  return { items, names };
6455
6608
  }
6456
6609
  function translationText(items, names) {
@@ -6694,9 +6847,9 @@ function translationCall(engine, inner) {
6694
6847
  );
6695
6848
  return match ? { key: match[2] ?? "", params: parseParams(match[3] ?? "", "=", " ") } : null;
6696
6849
  }
6697
- function parseParams(text2, assign, separator) {
6850
+ function parseParams(text4, assign, separator) {
6698
6851
  const params = {};
6699
- for (const piece of splitTopLevel(text2, separator)) {
6852
+ for (const piece of splitTopLevel(text4, separator)) {
6700
6853
  const at = piece.indexOf(assign);
6701
6854
  if (at <= 0) continue;
6702
6855
  const name = piece.slice(0, at).trim().replace(/^['"]|['"]$/g, "");
@@ -6704,12 +6857,12 @@ function parseParams(text2, assign, separator) {
6704
6857
  }
6705
6858
  return params;
6706
6859
  }
6707
- function splitTopLevel(text2, separator) {
6860
+ function splitTopLevel(text4, separator) {
6708
6861
  const pieces = [];
6709
6862
  let depth = 0;
6710
6863
  let quote2 = null;
6711
6864
  let current = "";
6712
- for (const ch of text2) {
6865
+ for (const ch of text4) {
6713
6866
  if (quote2) {
6714
6867
  if (ch === quote2) quote2 = null;
6715
6868
  } else if (ch === '"' || ch === "'") quote2 = ch;
@@ -6734,7 +6887,7 @@ function entityChar(reference) {
6734
6887
  return code === void 0 ? null : String.fromCharCode(code);
6735
6888
  }
6736
6889
  function decodeText2(raw) {
6737
- let text2 = "";
6890
+ let text4 = "";
6738
6891
  const map = [];
6739
6892
  let i = 0;
6740
6893
  while (i < raw.length) {
@@ -6744,23 +6897,23 @@ function decodeText2(raw) {
6744
6897
  const decoded = match ? entityChar(match[1] ?? "") : null;
6745
6898
  if (match && decoded !== null) {
6746
6899
  for (let u = 0; u < decoded.length; u++) map.push(i);
6747
- text2 += decoded;
6900
+ text4 += decoded;
6748
6901
  i += match[0].length;
6749
6902
  continue;
6750
6903
  }
6751
6904
  }
6752
- text2 += raw[i];
6905
+ text4 += raw[i];
6753
6906
  map.push(i);
6754
6907
  i++;
6755
6908
  }
6756
6909
  map.push(raw.length);
6757
- return { text: text2, map };
6910
+ return { text: text4, map };
6758
6911
  }
6759
- function encodeText(text2, keepNbsp, allBraces = false) {
6912
+ function encodeText(text4, keepNbsp, allBraces = false) {
6760
6913
  let out = "";
6761
- for (let k = 0; k < text2.length; k++) {
6762
- const ch = text2[k] ?? "";
6763
- const next = text2[k + 1];
6914
+ for (let k = 0; k < text4.length; k++) {
6915
+ const ch = text4[k] ?? "";
6916
+ const next = text4[k + 1];
6764
6917
  if (ch === "&") out += "&amp;";
6765
6918
  else if (ch === "<") out += "&lt;";
6766
6919
  else if (ch === ">") out += "&gt;";
@@ -6772,8 +6925,8 @@ function encodeText(text2, keepNbsp, allBraces = false) {
6772
6925
  }
6773
6926
  return out;
6774
6927
  }
6775
- function encodeTemplateText(text2) {
6776
- return encodeText(text2, true);
6928
+ function encodeTemplateText(text4) {
6929
+ return encodeText(text4, true);
6777
6930
  }
6778
6931
  function spanOf(part) {
6779
6932
  return part ? [part.start, part.end] : [0, 0];
@@ -6807,7 +6960,7 @@ function rewriteSegment(source, segStart, segEnd, parts, next, breakSpelling) {
6807
6960
  const to = map[old.length - suffix] ?? segEnd;
6808
6961
  const middle = next.slice(prefix, next.length - suffix);
6809
6962
  const keepNbsp = old.slice(prefix, old.length - suffix).includes(NBSP);
6810
- const written = middle.split(BREAK).map((text2) => encodeText(text2, keepNbsp)).join(breakSpelling);
6963
+ const written = middle.split(BREAK).map((text4) => encodeText(text4, keepNbsp)).join(breakSpelling);
6811
6964
  return source.slice(segStart, from) + written + source.slice(to, segEnd);
6812
6965
  }
6813
6966
  var isHighSurrogate = (code) => code >= 55296 && code <= 56319;
@@ -6836,10 +6989,10 @@ var editAttribute = "data-ff-edit";
6836
6989
  function parseSourceAttribute(value) {
6837
6990
  const match = /^(body|header|footer):(\d+):(\d+)$/.exec(value ?? "");
6838
6991
  if (!match) return null;
6839
- const line = Number(match[2]);
6992
+ const line2 = Number(match[2]);
6840
6993
  const column = Number(match[3]);
6841
- if (line < 1 || column < 1) return null;
6842
- return { file: match[1], line, column };
6994
+ if (line2 < 1 || column < 1) return null;
6995
+ return { file: match[1], line: line2, column };
6843
6996
  }
6844
6997
  function annotateSourcePositions(source, engine, file, options = {}) {
6845
6998
  const scanner = new Scanner(source, engine);
@@ -6886,12 +7039,12 @@ function annotateSourcePositions(source, engine, file, options = {}) {
6886
7039
  const after = source[nameEnd];
6887
7040
  const plainName = after === void 0 || after === ">" || after === "/" || /\s/.test(after);
6888
7041
  if (plainName && !source.slice(nameEnd, end).toLowerCase().includes(sourceAttribute)) {
6889
- const { line, column } = locate(lines, i);
7042
+ const { line: line2, column } = locate(lines, i);
6890
7043
  const region = options.editable ? editableRegionAt(source, engine, i) : null;
6891
7044
  const edit = region && !("reason" in region) ? ` ${editAttribute}="${region.kind}"` : "";
6892
7045
  out.push(
6893
7046
  source.slice(copied, nameEnd),
6894
- ` ${sourceAttribute}="${file}:${line}:${column}"${edit}`
7047
+ ` ${sourceAttribute}="${file}:${line2}:${column}"${edit}`
6895
7048
  );
6896
7049
  copied = nameEnd;
6897
7050
  }
@@ -7011,8 +7164,8 @@ function parseDesign(value) {
7011
7164
  let guides;
7012
7165
  const g = v["guides"];
7013
7166
  if (g && typeof g === "object") {
7014
- const list2 = (x) => (Array.isArray(x) ? x : []).filter((n) => typeof n === "number" && Number.isFinite(n)).slice(0, 100);
7015
- guides = { x: list2(g["x"]), y: list2(g["y"]) };
7167
+ const list3 = (x) => (Array.isArray(x) ? x : []).filter((n) => typeof n === "number" && Number.isFinite(n)).slice(0, 100);
7168
+ guides = { x: list3(g["x"]), y: list3(g["y"]) };
7016
7169
  }
7017
7170
  if (problems.length) return { design: null, problems };
7018
7171
  return {
@@ -7562,11 +7715,11 @@ function googleFontsUrl(uses) {
7562
7715
 
7563
7716
  // packages/engine/src/lib/design/hash.ts
7564
7717
  function designOutputHash(html, css2) {
7565
- const text2 = `${html}\0${css2}`;
7718
+ const text4 = `${html}\0${css2}`;
7566
7719
  let a = 2166136261;
7567
7720
  let b = 16777619 ^ 2654435769;
7568
- for (let i = 0; i < text2.length; i++) {
7569
- const code = text2.charCodeAt(i);
7721
+ for (let i = 0; i < text4.length; i++) {
7722
+ const code = text4.charCodeAt(i);
7570
7723
  if (code === 13) continue;
7571
7724
  a = Math.imul(a ^ code, 16777619) >>> 0;
7572
7725
  b = Math.imul(b ^ code, 16777619) >>> 0;
@@ -7676,7 +7829,7 @@ var tokenPattern2 = /^[a-z][a-z0-9-]{0,31}$/;
7676
7829
  function emitDesign(design2, engine, size) {
7677
7830
  const lines = ['<div class="ff-design">'];
7678
7831
  const map = {};
7679
- const rules = [];
7832
+ const rules2 = [];
7680
7833
  for (const layer of design2.layers) {
7681
7834
  if (!layer.visible) continue;
7682
7835
  const markup = layerHtml(layer, engine);
@@ -7684,7 +7837,7 @@ function emitDesign(design2, engine, size) {
7684
7837
  ` ${layer.showWhen?.path ? ifBlock(engine, layer.showWhen.path, markup) : markup}`
7685
7838
  );
7686
7839
  map[layer.id] = { start: lines.length, end: lines.length };
7687
- rules.push(...layerCss(layer));
7840
+ rules2.push(...layerCss(layer));
7688
7841
  }
7689
7842
  lines.push("</div>");
7690
7843
  const html = `${lines.join("\n")}
@@ -7709,7 +7862,7 @@ function emitDesign(design2, engine, size) {
7709
7862
  })} }`,
7710
7863
  ".ff-layer { position: absolute; box-sizing: border-box; margin: 0; }"
7711
7864
  ];
7712
- const css2 = `${[...head, ...rules].join("\n")}
7865
+ const css2 = `${[...head, ...rules2].join("\n")}
7713
7866
  `;
7714
7867
  return { html, css: css2, map, output: designOutputHash(html, css2) };
7715
7868
  }
@@ -7793,32 +7946,32 @@ function imageSource(source, engine) {
7793
7946
  return `{{ brand.logo.${source.brandLogo} }}`;
7794
7947
  }
7795
7948
  function layerCss(layer) {
7796
- const text2 = layer.type === "text" ? layer : null;
7949
+ const text4 = layer.type === "text" ? layer : null;
7797
7950
  const box = {
7798
7951
  left: px(layer.x),
7799
7952
  top: px(layer.y),
7800
7953
  width: px(layer.width),
7801
- height: text2?.sizing === "grow" ? void 0 : px(layer.height),
7954
+ height: text4?.sizing === "grow" ? void 0 : px(layer.height),
7802
7955
  opacity: layer.opacity < 1 ? num(layer.opacity) : void 0,
7803
7956
  transform: layer.rotation ? `rotate(${num(layer.rotation)}deg)` : void 0,
7804
7957
  "border-radius": layer.type === "shape" && layer.shape === "ellipse" ? "50%" : layer.radius ? px(layer.radius) : void 0,
7805
7958
  border: layer.stroke && layer.stroke.width > 0 ? `${px(layer.stroke.width)} solid ${paint(layer.stroke.paint)}` : void 0,
7806
- "box-shadow": layer.shadow && !text2 ? shadow(layer.shadow) : void 0
7959
+ "box-shadow": layer.shadow && !text4 ? shadow(layer.shadow) : void 0
7807
7960
  };
7808
- if (text2) {
7961
+ if (text4) {
7809
7962
  Object.assign(box, {
7810
7963
  display: "flex",
7811
7964
  "flex-direction": "column",
7812
- "justify-content": text2.valign === "middle" ? "center" : text2.valign === "bottom" ? "flex-end" : "flex-start",
7813
- overflow: text2.sizing === "grow" ? void 0 : "hidden",
7814
- padding: text2.padding ? px(text2.padding) : void 0,
7815
- background: text2.fill ? paint(text2.fill) : void 0
7965
+ "justify-content": text4.valign === "middle" ? "center" : text4.valign === "bottom" ? "flex-end" : "flex-start",
7966
+ overflow: text4.sizing === "grow" ? void 0 : "hidden",
7967
+ padding: text4.padding ? px(text4.padding) : void 0,
7968
+ background: text4.fill ? paint(text4.fill) : void 0
7816
7969
  });
7817
- const s = text2.style;
7818
- const clamp = text2.maxLines ? {
7970
+ const s = text4.style;
7971
+ const clamp = text4.maxLines ? {
7819
7972
  display: "-webkit-box",
7820
7973
  "-webkit-box-orient": "vertical",
7821
- "-webkit-line-clamp": String(text2.maxLines),
7974
+ "-webkit-line-clamp": String(text4.maxLines),
7822
7975
  overflow: "hidden"
7823
7976
  } : {};
7824
7977
  const inner = decl({
@@ -7834,7 +7987,7 @@ function layerCss(layer) {
7834
7987
  "font-style": s.italic ? "italic" : void 0,
7835
7988
  "text-transform": s.uppercase ? "uppercase" : void 0,
7836
7989
  "text-decoration": [s.underline && "underline", s.strike && "line-through"].filter(Boolean).join(" ") || void 0,
7837
- "text-shadow": text2.shadow ? shadow(text2.shadow) : void 0,
7990
+ "text-shadow": text4.shadow ? shadow(text4.shadow) : void 0,
7838
7991
  "overflow-wrap": "break-word",
7839
7992
  ...clamp
7840
7993
  });
@@ -8173,6 +8326,2445 @@ var designStarters = [
8173
8326
  }
8174
8327
  ];
8175
8328
 
8329
+ // packages/engine/src/lib/einvoice/codes.generated.ts
8330
+ var list2 = (codes) => new Set(codes.split(" "));
8331
+ var CODE_LIST_RELEASE = "1.09.2";
8332
+ var documentTypeCodes = list2(
8333
+ "71 80 81 82 83 84 102 130 202 203 204 211 218 219 261 262 295 296 308 325 326 331 380 381 382 383 384 385 386 387 388 389 390 393 394 395 396 420 456 457 458 471 472 473 500 501 502 503 527 532 553 575 623 633 751 780 817 870 875 876 877 935"
8334
+ );
8335
+ var countryCodes = list2(
8336
+ "1A AD AE AF AG AI AL AM AO AQ AR AS AT AU AW AX AZ BA BB BD BE BF BG BH BI BJ BL BM BN BO BQ BR BS BT BV BW BY BZ CA CC CD CF CG CH CI CK CL CM CN CO CR CU CV CW CX CY CZ DE DJ DK DM DO DZ EC EE EG EH ER ES ET FI FJ FK FM FO FR GA GB GD GE GF GG GH GI GL GM GN GP GQ GR GS GT GU GW GY HK HM HN HR HT HU ID IE IL IM IN IO IQ IR IS IT JE JM JO JP KE KG KH KI KM KN KP KR KW KY KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD ME MF MG MH MK ML MM MN MO MP MQ MR MS MT MU MV MW MX MY MZ NA NC NE NF NG NI NL NO NP NR NU NZ OM PA PE PF PG PH PK PL PM PN PR PS PT PW PY QA RE RO RS RU RW SA SB SC SD SE SG SH SI SJ SK SL SM SN SO SR SS ST SV SX SY SZ TC TD TF TG TH TJ TK TL TM TN TO TR TT TV TW TZ UA UG UM US UY UZ VA VC VE VG VI VN VU WF WS XI YE YT ZA ZM ZW 1A XI"
8337
+ );
8338
+ var unitCodes = list2(
8339
+ "10 11 13 14 15 20 21 22 23 24 25 27 28 33 34 35 37 38 40 41 56 57 58 59 60 61 74 77 80 81 85 87 89 91 1I 2A 2B 2C 2G 2H 2I 2J 2K 2L 2M 2N 2P 2Q 2R 2U 2X 2Y 2Z 3B 3C 4C 4G 4H 4K 4L 4M 4N 4O 4P 4Q 4R 4T 4U 4W 4X 5A 5B 5E 5J A10 A11 A12 A13 A14 A15 A16 A17 A18 A19 A2 A20 A21 A22 A23 A24 A26 A27 A28 A29 A3 A30 A31 A32 A33 A34 A35 A36 A37 A38 A39 A4 A40 A41 A42 A43 A44 A45 A47 A48 A49 A5 A53 A54 A55 A56 A59 A6 A68 A69 A7 A70 A71 A73 A74 A75 A76 A8 A84 A85 A86 A87 A88 A89 A9 A90 A91 A93 A94 A95 A96 A97 A98 A99 AA AB ACR ACT AD AE AH AI AK AL AMH AMP ANN APZ AQ AS ASM ASU ATM AWG AY AZ B1 B10 B11 B12 B13 B14 B15 B16 B17 B18 B19 B20 B21 B22 B23 B24 B25 B26 B27 B28 B29 B3 B30 B31 B32 B33 B34 B35 B4 B41 B42 B43 B44 B45 B46 B47 B48 B49 B50 B52 B53 B54 B55 B56 B57 B58 B59 B60 B61 B62 B63 B64 B66 B67 B68 B69 B7 B70 B71 B72 B73 B74 B75 B76 B77 B78 B79 B8 B80 B81 B82 B83 B84 B85 B86 B87 B88 B89 B90 B91 B92 B93 B94 B95 B96 B97 B98 B99 BAR BB BFT BHP BIL BLD BLL BP BPM BQL BTU BUA BUI C0 C10 C11 C12 C13 C14 C15 C16 C17 C18 C19 C20 C21 C22 C23 C24 C25 C26 C27 C28 C29 C3 C30 C31 C32 C33 C34 C35 C36 C37 C38 C39 C40 C41 C42 C43 C44 C45 C46 C47 C48 C49 C50 C51 C52 C53 C54 C55 C56 C57 C58 C59 C60 C61 C62 C63 C64 C65 C66 C67 C68 C69 C7 C70 C71 C72 C73 C74 C75 C76 C78 C79 C8 C80 C81 C82 C83 C84 C85 C86 C87 C88 C89 C9 C90 C91 C92 C93 C94 C95 C96 C97 C99 CCT CDL CEL CEN CG CGM CKG CLF CLT CMK CMQ CMT CNP CNT COU CTG CTM CTN CUR CWA CWI D03 D04 D1 D10 D11 D12 D13 D15 D16 D17 D18 D19 D2 D20 D21 D22 D23 D24 D25 D26 D27 D29 D30 D31 D32 D33 D34 D36 D41 D42 D43 D44 D45 D46 D47 D48 D49 D5 D50 D51 D52 D53 D54 D55 D56 D57 D58 D59 D6 D60 D61 D62 D63 D65 D68 D69 D73 D74 D77 D78 D80 D81 D82 D83 D85 D86 D87 D88 D89 D91 D93 D94 D95 DAA DAD DAY DB DBM DBW DD DEC DG DJ DLT DMA DMK DMO DMQ DMT DN DPC DPR DPT DRA DRI DRL DT DTN DWT DZN DZP E01 E07 E08 E09 E10 E12 E14 E15 E16 E17 E18 E19 E20 E21 E22 E23 E25 E27 E28 E30 E31 E32 E33 E34 E35 E36 E37 E38 E39 E4 E40 E41 E42 E43 E44 E45 E46 E47 E48 E49 E50 E51 E52 E53 E54 E55 E56 E57 E58 E59 E60 E61 E62 E63 E64 E65 E66 E67 E68 E69 E70 E71 E72 E73 E74 E75 E76 E77 E78 E79 E80 E81 E82 E83 E84 E85 E86 E87 E88 E89 E90 E91 E92 E93 E94 E95 E96 E97 E98 E99 EA EB EQ F01 F02 F03 F04 F05 F06 F07 F08 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 F32 F33 F34 F35 F36 F37 F38 F39 F40 F41 F42 F43 F44 F45 F46 F47 F48 F49 F50 F51 F52 F53 F54 F55 F56 F57 F58 F59 F60 F61 F62 F63 F64 F65 F66 F67 F68 F69 F70 F71 F72 F73 F74 F75 F76 F77 F78 F79 F80 F81 F82 F83 F84 F85 F86 F87 F88 F89 F90 F91 F92 F93 F94 F95 F96 F97 F98 F99 FAH FAR FBM FC FF FH FIT FL FNU FOT FP FR FS FTK FTQ G01 G04 G05 G06 G08 G09 G10 G11 G12 G13 G14 G15 G16 G17 G18 G19 G2 G20 G21 G23 G24 G25 G26 G27 G28 G29 G3 G30 G31 G32 G33 G34 G35 G36 G37 G38 G39 G40 G41 G42 G43 G44 G45 G46 G47 G48 G49 G50 G51 G52 G53 G54 G55 G56 G57 G58 G59 G60 G61 G62 G63 G64 G65 G66 G67 G68 G69 G70 G71 G72 G73 G74 G75 G76 G77 G78 G79 G80 G81 G82 G83 G84 G85 G86 G87 G88 G89 G90 G91 G92 G93 G94 G95 G96 G97 G98 G99 GB GBQ GDW GE GF GFI GGR GIA GIC GII GIP GJ GL GLD GLI GLL GM GO GP GQ GRM GRN GRO GV GWH H03 H04 H05 H06 H07 H08 H09 H10 H11 H12 H13 H14 H15 H16 H18 H19 H20 H21 H22 H23 H24 H25 H26 H27 H28 H29 H30 H31 H32 H33 H34 H35 H36 H37 H38 H39 H40 H41 H42 H43 H44 H45 H46 H47 H48 H49 H50 H51 H52 H53 H54 H55 H56 H57 H58 H59 H60 H61 H62 H63 H64 H65 H66 H67 H68 H69 H70 H71 H72 H73 H74 H75 H76 H77 H79 H80 H81 H82 H83 H84 H85 H87 H88 H89 H90 H91 H92 H93 H94 H95 H96 H98 H99 HA HAD HBA HBX HC HDW HEA HGM HH HIU HKM HLT HM HMO HMQ HMT HPA HTZ HUR HWE IA IE INH INK INQ ISD IU IUG IV J10 J12 J13 J14 J15 J16 J17 J18 J19 J2 J20 J21 J22 J23 J24 J25 J26 J27 J28 J29 J30 J31 J32 J33 J34 J35 J36 J38 J39 J40 J41 J42 J43 J44 J45 J46 J47 J48 J49 J50 J51 J52 J53 J54 J55 J56 J57 J58 J59 J60 J61 J62 J63 J64 J65 J66 J67 J68 J69 J70 J71 J72 J73 J74 J75 J76 J78 J79 J81 J82 J83 J84 J85 J87 J90 J91 J92 J93 J95 J96 J97 J98 J99 JE JK JM JNT JOU JPS JWL K1 K10 K11 K12 K13 K14 K15 K16 K17 K18 K19 K2 K20 K21 K22 K23 K26 K27 K28 K3 K30 K31 K32 K33 K34 K35 K36 K37 K38 K39 K40 K41 K42 K43 K45 K46 K47 K48 K49 K50 K51 K52 K53 K54 K55 K58 K59 K6 K60 K61 K62 K63 K64 K65 K66 K67 K68 K69 K70 K71 K73 K74 K75 K76 K77 K78 K79 K80 K81 K82 K83 K84 K85 K86 K87 K88 K89 K90 K91 K92 K93 K94 K95 K96 K97 K98 K99 KA KAT KB KBA KCC KDW KEL KGM KGS KHY KHZ KI KIC KIP KJ KJO KL KLK KLX KMA KMH KMK KMQ KMT KNI KNM KNS KNT KO KPA KPH KPO KPP KR KSD KSH KT KTN KUR KVA KVR KVT KW KWH KWN KWO KWS KWT KWY KX L10 L11 L12 L13 L14 L15 L16 L17 L18 L19 L2 L20 L21 L23 L24 L25 L26 L27 L28 L29 L30 L31 L32 L33 L34 L35 L36 L37 L38 L39 L40 L41 L42 L43 L44 L45 L46 L47 L48 L49 L50 L51 L52 L53 L54 L55 L56 L57 L58 L59 L60 L63 L64 L65 L66 L67 L68 L69 L70 L71 L72 L73 L74 L75 L76 L77 L78 L79 L80 L81 L82 L83 L84 L85 L86 L87 L88 L89 L90 L91 L92 L93 L94 L95 L96 L98 L99 LA LAC LBR LBT LD LEF LF LH LK LM LN LO LP LPA LR LS LTN LTR LUB LUM LUX LY M1 M10 M11 M12 M13 M14 M15 M16 M17 M18 M19 M20 M21 M22 M23 M24 M25 M26 M27 M29 M30 M31 M32 M33 M34 M35 M36 M37 M38 M39 M4 M40 M41 M42 M43 M44 M45 M46 M47 M48 M49 M5 M50 M51 M52 M53 M55 M56 M57 M58 M59 M60 M61 M62 M63 M64 M65 M66 M67 M68 M69 M7 M70 M71 M72 M73 M74 M75 M76 M77 M78 M79 M80 M81 M82 M83 M84 M85 M86 M87 M88 M89 M9 M90 M91 M92 M93 M94 M95 M96 M97 M98 M99 MAH MAL MAM MAR MAW MBE MBF MBR MC MCU MD MGM MHZ MIK MIL MIN MIO MIU MKD MKM MKW MLD MLT MMK MMQ MMT MND MNJ MON MPA MQD MQH MQM MQS MQW MRD MRM MRW MSK MTK MTQ MTR MTS MTZ MVA MWH N1 N10 N11 N12 N13 N14 N15 N16 N17 N18 N19 N20 N21 N22 N23 N24 N25 N26 N27 N28 N29 N3 N30 N31 N32 N33 N34 N35 N36 N37 N38 N39 N40 N41 N42 N43 N44 N45 N46 N47 N48 N49 N50 N51 N52 N53 N54 N55 N56 N57 N58 N59 N60 N61 N62 N63 N64 N65 N66 N67 N68 N69 N70 N71 N72 N73 N74 N75 N76 N77 N78 N79 N80 N81 N82 N83 N84 N85 N86 N87 N88 N89 N90 N91 N92 N93 N94 N95 N96 N97 N98 N99 NA NAR NCL NEW NF NIL NIU NL NM3 NMI NMP NPT NT NTU NU NX OA ODE ODG ODK ODM OHM ON ONZ OPM OT OZA OZI P1 P10 P11 P12 P13 P14 P15 P16 P17 P18 P19 P2 P20 P21 P22 P23 P24 P25 P26 P27 P28 P29 P30 P31 P32 P33 P34 P35 P36 P37 P38 P39 P40 P41 P42 P43 P44 P45 P46 P47 P48 P49 P5 P50 P51 P52 P53 P54 P55 P56 P57 P58 P59 P60 P61 P62 P63 P64 P65 P66 P67 P68 P69 P70 P71 P72 P73 P74 P75 P76 P77 P78 P79 P80 P81 P82 P83 P84 P85 P86 P87 P88 P89 P90 P91 P92 P93 P94 P95 P96 P97 P98 P99 PAL PD PFL PGL PI PLA PO PQ PR PS PTD PTI PTL PTN Q10 Q11 Q12 Q13 Q14 Q15 Q16 Q17 Q18 Q19 Q20 Q21 Q22 Q23 Q24 Q25 Q26 Q27 Q28 Q29 Q3 Q30 Q31 Q32 Q33 Q34 Q35 Q36 Q37 Q38 Q39 Q40 Q41 Q42 QA QAN QB QR QTD QTI QTL QTR R1 R9 RH RM ROM RP RPM RPS RT S3 S4 SAN SCO SCR SEC SET SG SIE SM3 SMI SQ SQR SR STC STI STK STL STN STW SW SX SYR T0 T3 TAH TAN TI TIC TIP TKM TMS TNE TP TPI TPR TQD TRL TST TTS U1 U2 UB UC VA VLT VP W2 WA WB WCD WE WEB WEE WG WHR WM WSD WTT X1 X1A X1B X1D X1F X1G X1W X2C X3A X3H X43 X44 X4A X4B X4C X4D X4F X4G X4H X5H X5L X5M X6H X6P X7A X7B X8A X8B X8C XAA XAB XAC XAD XAE XAF XAG XAH XAI XAJ XAL XAM XAP XAT XAV XB4 XBA XBB XBC XBD XBE XBF XBG XBH XBI XBJ XBK XBL XBM XBN XBO XBP XBQ XBR XBS XBT XBU XBV XBW XBX XBY XBZ XCA XCB XCC XCD XCE XCF XCG XCH XCI XCJ XCK XCL XCM XCN XCO XCP XCQ XCR XCS XCT XCU XCV XCW XCX XCY XCZ XDA XDB XDC XDG XDH XDI XDJ XDK XDL XDM XDN XDP XDR XDS XDT XDU XDV XDW XDX XDY XEC XED XEE XEF XEG XEH XEI XEN XFB XFC XFD XFE XFI XFL XFO XFP XFR XFT XFW XFX XGB XGI XGL XGR XGU XGY XGZ XHA XHB XHC XHG XHN XHR XIA XIB XIC XID XIE XIF XIG XIH XIK XIL XIN XIZ XJB XJC XJG XJR XJT XJY XKG XKI XLE XLG XLT XLU XLV XLZ XMA XMB XMC XME XMR XMS XMT XMW XMX XNA XNE XNF XNG XNS XNT XNU XNV XO1 XO2 XO3 XO4 XO5 XO6 XO7 XO8 XO9 XOA XOB XOC XOD XOE XOF XOG XOH XOI XOJ XOK XOL XOM XON XOP XOQ XOR XOS XOT XOU XOV XOW XOX XOY XOZ XP1 XP2 XP3 XP4 XPA XPB XPC XPD XPE XPF XPG XPH XPI XPJ XPK XPL XPN XPO XPP XPR XPT XPU XPV XPX XPY XPZ XQA XQB XQC XQD XQF XQG XQH XQJ XQK XQL XQM XQN XQP XQQ XQR XQS XRD XRG XRJ XRK XRL XRO XRT XRZ XSA XSB XSC XSD XSE XSH XSI XSK XSL XSM XSO XSP XSS XST XSU XSV XSW XSX XSY XSZ XT1 XTB XTC XTD XTE XTG XTI XTK XTL XTN XTO XTR XTS XTT XTU XTV XTW XTY XTZ XUC XUN XVA XVG XVI XVK XVL XVN XVO XVP XVQ XVR XVS XVY XWA XWB XWC XWD XWF XWG XWH XWJ XWK XWL XWM XWN XWP XWQ XWR XWS XWT XWU XWV XWW XWX XWY XWZ XXA XXB XXC XXD XXF XXG XXH XXJ XXK XYA XYB XYC XYD XYF XYG XYH XYJ XYK XYL XYM XYN XYP XYQ XYR XYS XYT XYV XYW XYX XYY XYZ XZA XZB XZC XZD XZF XZG XZH XZJ XZK XZL XZM XZN XZP XZQ XZR XZS XZT XZU XZV XZW XZX XZY XZZ YDK YDQ YRD Z11 Z9 ZP ZZ"
8340
+ );
8341
+ var electronicAddressSchemes = list2(
8342
+ "0002 0007 0009 0037 0060 0088 0096 0097 0106 0130 0135 0142 0147 0151 0154 0158 0170 0177 0183 0184 0188 0190 0191 0192 0193 0194 0195 0196 0198 0199 0200 0201 0202 0203 0204 0205 0208 0209 0210 0211 0212 0213 0215 0216 0217 0218 0221 0225 0230 0235 0240 0242 0244 0245 0246 0248 9910 9913 9914 9915 9918 9919 9920 9922 9923 9924 9925 9926 9927 9928 9929 9930 9931 9932 9933 9934 9935 9936 9937 9938 9939 9940 9941 9942 9943 9944 9945 9946 9947 9948 9949 9950 9951 9952 9953 9957 9959 AN AQ AS AU EM"
8343
+ );
8344
+ var currencyCodes = list2(
8345
+ "AED AFN ALL AMD AOA ARS AUD AWG AZN BAM BBD BDT BHD BIF BMD BND BOB BOV BRL BSD BTN BWP BYN BZD CAD CDF CHE CHF CHW CLF CLP CNH CNY COP COU CRC CUP CVE CZK DJF DKK DOP DZD EGP ERN ETB EUR FJD FKP GBP GEL GHS GIP GMD GNF GTQ GYD HKD HNL HTG HUF IDR ILS INR IQD IRR ISK JMD JOD JPY KES KGS KHR KMF KPW KRW KWD KYD KZT LAK LBP LKR LRD LSL LYD MAD MDL MGA MKD MMK MNT MOP MRU MUR MVR MWK MXN MXV MYR MZN NAD NGN NIO NOK NPR NZD OMR PAB PEN PGK PHP PKR PLN PYG QAR RON RSD RUB RWF SAR SBD SCR SDG SEK SGD SHP SLE SOS SRD SSP STN SVC SYP SZL THB TJS TMT TND TOP TRY TTD TWD TZS UAH UGX USD USN UYI UYU UYW UZS VED VES VND VUV WST XAF XAG XAU XBA XBB XBC XBD XCD XCG XDR XOF XPD XPF XPT XSU XTS XUA XXX YER ZAR ZMW ZWG"
8346
+ );
8347
+ var paymentMeansCodes = list2(
8348
+ "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 74 75 76 77 78 91 92 93 94 95 96 97 98 ZZZ"
8349
+ );
8350
+
8351
+ // packages/engine/src/lib/einvoice/decimal.ts
8352
+ function decimalText(value) {
8353
+ if (typeof value === "string") {
8354
+ const text5 = value.trim();
8355
+ return /^-?\d+(\.\d+)?$/.test(text5) ? trimZeros(text5) : null;
8356
+ }
8357
+ if (typeof value !== "number" || !Number.isFinite(value)) return null;
8358
+ const text4 = value.toPrecision(15);
8359
+ return /^-?\d+(\.\d+)?$/.test(text4) ? trimZeros(text4) : null;
8360
+ }
8361
+ function trimZeros(text4) {
8362
+ const trimmed = text4.includes(".") ? text4.replace(/0+$/, "").replace(/\.$/, "") : text4;
8363
+ return trimmed === "-0" ? "0" : trimmed;
8364
+ }
8365
+ function decimalPlaces(text4) {
8366
+ const dot = text4.indexOf(".");
8367
+ return dot === -1 ? 0 : text4.length - dot - 1;
8368
+ }
8369
+ function toUnits(text4, scale) {
8370
+ const negative = text4.startsWith("-");
8371
+ const [whole = "0", fraction = ""] = (negative ? text4.slice(1) : text4).split(
8372
+ "."
8373
+ );
8374
+ const units = BigInt(whole + fraction.padEnd(scale, "0").slice(0, scale));
8375
+ return negative ? -units : units;
8376
+ }
8377
+ function fromUnits(units, scale, places = scale) {
8378
+ const negative = units < 0n;
8379
+ const digits = (negative ? -units : units).toString().padStart(scale + 1, "0");
8380
+ const whole = digits.slice(0, digits.length - scale);
8381
+ const fraction = digits.slice(digits.length - scale).padEnd(places, "0");
8382
+ const text4 = places > 0 ? `${whole}.${fraction.slice(0, places)}` : whole;
8383
+ return negative && /[1-9]/.test(text4) ? `-${text4}` : text4;
8384
+ }
8385
+ function withPlaces(text4, min) {
8386
+ const places = decimalPlaces(text4);
8387
+ if (places >= min) return text4;
8388
+ return places === 0 ? `${text4}.${"0".repeat(min)}` : text4 + "0".repeat(min - places);
8389
+ }
8390
+ function divideRounded(value, divisor) {
8391
+ const negative = value < 0n !== divisor < 0n;
8392
+ const a = value < 0n ? -value : value;
8393
+ const b = divisor < 0n ? -divisor : divisor;
8394
+ const quotient = (a * 2n + b) / (b * 2n);
8395
+ return negative ? -quotient : quotient;
8396
+ }
8397
+ var abs = (value) => value < 0n ? -value : value;
8398
+
8399
+ // packages/engine/src/lib/einvoice/schema.ts
8400
+ var import_zod = require("zod");
8401
+ var numeric = import_zod.z.union([import_zod.z.number(), import_zod.z.string()]);
8402
+ var text2 = import_zod.z.string().trim().min(1);
8403
+ var isoDate2 = import_zod.z.string().regex(/^\d{4}-\d{2}-\d{2}$/, "Expected a date as YYYY-MM-DD");
8404
+ var identifier2 = import_zod.z.union([
8405
+ text2,
8406
+ import_zod.z.object({ value: text2, scheme: text2.optional() })
8407
+ ]);
8408
+ var address = import_zod.z.object({
8409
+ street: text2.optional(),
8410
+ street2: text2.optional(),
8411
+ street3: text2.optional(),
8412
+ postcode: text2.optional(),
8413
+ city: text2.optional(),
8414
+ /** ISO 3166-1 alpha-2 (BT-40, BT-55, BT-80). */
8415
+ country: text2,
8416
+ /** Region or province (BT-39, BT-54, BT-79). */
8417
+ subdivision: text2.optional()
8418
+ });
8419
+ var contact = import_zod.z.object({
8420
+ name: text2.optional(),
8421
+ phone: text2.optional(),
8422
+ email: text2.optional()
8423
+ });
8424
+ var party = import_zod.z.object({
8425
+ name: text2,
8426
+ /** The name the business trades under, where it differs (BT-28, BT-45). */
8427
+ trading_name: text2.optional(),
8428
+ /** An identifier of the party (BT-29, BT-46). */
8429
+ id: identifier2.optional(),
8430
+ /** The register entry (BT-30, BT-47): a SIREN, a company number. */
8431
+ legal_registration: identifier2.optional(),
8432
+ vat_id: text2.optional(),
8433
+ address,
8434
+ contact: contact.optional(),
8435
+ /** Where the party receives electronic invoices (BT-34, BT-49); a bare e-mail address is scheme EM. */
8436
+ electronic_address: identifier2.optional()
8437
+ });
8438
+ var period = import_zod.z.object({ start: isoDate2.optional(), end: isoDate2.optional() });
8439
+ var VAT_CATEGORIES = ["S", "Z", "E", "AE", "K", "G", "O"];
8440
+ var vatCategory = import_zod.z.enum(VAT_CATEGORIES);
8441
+ var lineTax = import_zod.z.object({
8442
+ category: vatCategory,
8443
+ /** Percent, `19` for 19 %. Left out for category O only. */
8444
+ rate: numeric.optional()
8445
+ });
8446
+ var allowanceOrCharge = import_zod.z.object({
8447
+ amount: numeric,
8448
+ reason: text2.optional(),
8449
+ /** UNTDID 5189 for an allowance, UNTDID 7161 for a charge. */
8450
+ reason_code: text2.optional(),
8451
+ base_amount: numeric.optional(),
8452
+ percent: numeric.optional()
8453
+ });
8454
+ var line = import_zod.z.object({
8455
+ id: import_zod.z.union([text2, import_zod.z.number()]),
8456
+ name: text2,
8457
+ description: text2.optional(),
8458
+ quantity: numeric,
8459
+ /** UN/ECE Recommendation 20: C62 a piece, HUR an hour, DAY a day, KGM a kilogram. */
8460
+ unit_code: text2.default("C62"),
8461
+ net_price: numeric,
8462
+ /** The quantity the price is for, where it is not one unit (BT-149). */
8463
+ price_base_quantity: numeric.optional(),
8464
+ net_amount: numeric,
8465
+ tax: lineTax,
8466
+ period: period.optional(),
8467
+ allowances: import_zod.z.array(allowanceOrCharge).optional(),
8468
+ charges: import_zod.z.array(allowanceOrCharge).optional(),
8469
+ buyer_accounting_reference: text2.optional(),
8470
+ note: text2.optional(),
8471
+ /** The seller's and the buyer's article numbers (BT-155, BT-156), a GTIN and the like (BT-157). */
8472
+ seller_item_id: text2.optional(),
8473
+ buyer_item_id: text2.optional(),
8474
+ standard_item_id: identifier2.optional(),
8475
+ /** The line of the buyer's order this one answers (BT-132). */
8476
+ order_line_reference: text2.optional()
8477
+ });
8478
+ var breakdown = import_zod.z.object({
8479
+ category: vatCategory,
8480
+ rate: numeric.optional(),
8481
+ basis: numeric,
8482
+ amount: numeric,
8483
+ exemption_reason: text2.optional(),
8484
+ /** A VATEX code (BT-121), for example VATEX-EU-AE for reverse charge. */
8485
+ exemption_reason_code: text2.optional()
8486
+ });
8487
+ var totals = import_zod.z.object({
8488
+ line_net: numeric,
8489
+ allowances: numeric.optional(),
8490
+ charges: numeric.optional(),
8491
+ tax_basis: numeric,
8492
+ tax_total: numeric,
8493
+ /** The VAT total in the accounting currency (BT-111). */
8494
+ tax_total_accounting: numeric.optional(),
8495
+ grand: numeric,
8496
+ prepaid: numeric.optional(),
8497
+ rounding: numeric.optional(),
8498
+ due: numeric
8499
+ });
8500
+ var payment = import_zod.z.object({
8501
+ /** UNTDID 4461: 58 SEPA credit transfer, 59 SEPA direct debit, 30 credit transfer, 48 card. */
8502
+ means_code: import_zod.z.union([text2, import_zod.z.number()]).optional(),
8503
+ iban: text2.optional(),
8504
+ bic: text2.optional(),
8505
+ account_name: text2.optional(),
8506
+ /** What the payer quotes with the payment (BT-83). */
8507
+ reference: text2.optional(),
8508
+ terms: text2.optional(),
8509
+ due_date: isoDate2.optional(),
8510
+ /** Direct debit: the mandate (BT-89), the creditor's identifier (BT-90), the account debited (BT-91). */
8511
+ mandate_id: text2.optional(),
8512
+ creditor_id: text2.optional(),
8513
+ debited_iban: text2.optional()
8514
+ });
8515
+ var note = import_zod.z.object({
8516
+ text: text2,
8517
+ /** UNTDID 4451, which French invoices use for their statutory mentions (PMT, PMD, AAB). */
8518
+ subject_code: text2.optional()
8519
+ });
8520
+ var invoiceSchema = import_zod.z.object({
8521
+ number: text2,
8522
+ issue_date: isoDate2,
8523
+ /** UNTDID 1001: 380 invoice, 381 credit note, 384 corrected invoice, 389 self-billed. */
8524
+ type_code: import_zod.z.union([text2, import_zod.z.number()]).default("380"),
8525
+ currency: text2,
8526
+ /** The currency the VAT is accounted in, where the invoice is in another (BT-6); goes with `totals.tax_total_accounting`. */
8527
+ accounting_currency: text2.optional(),
8528
+ /** The buyer's routing reference (BT-10); a German authority's Leitweg-ID goes here. */
8529
+ buyer_reference: text2.optional(),
8530
+ order_reference: text2.optional(),
8531
+ contract_reference: text2.optional(),
8532
+ note: text2.optional(),
8533
+ notes: import_zod.z.array(note).optional(),
8534
+ period: period.optional(),
8535
+ /** The invoices a credit note or a correction refers to (BG-3). */
8536
+ preceding_invoices: import_zod.z.array(import_zod.z.object({ number: text2, issue_date: isoDate2.optional() })).optional(),
8537
+ /** France (spec 17 §4.4): what was sold, and the billing framework's code where the plain case does not fit. */
8538
+ operation: import_zod.z.enum(["goods", "services", "mixed"]).optional(),
8539
+ operation_code: text2.optional(),
8540
+ seller: party.extend({
8541
+ tax_number: text2.optional(),
8542
+ /** Further legal information the invoice must state (BT-33): register court, capital. */
8543
+ legal_info: text2.optional(),
8544
+ /** France: VAT is owed when the invoice is issued, not when it is paid. */
8545
+ vat_on_debits: import_zod.z.boolean().optional()
8546
+ }),
8547
+ buyer: party.extend({
8548
+ /** France: the buyer's SIREN, nine digits. */
8549
+ siren: text2.optional()
8550
+ }),
8551
+ delivery: import_zod.z.object({
8552
+ name: text2.optional(),
8553
+ date: isoDate2.optional(),
8554
+ address: address.optional()
8555
+ }).optional(),
8556
+ lines: import_zod.z.array(line).min(1),
8557
+ /** Allowances and charges on the whole document (BG-20, BG-21), each with the VAT it falls under. */
8558
+ allowances: import_zod.z.array(allowanceOrCharge.extend({ tax: lineTax })).optional(),
8559
+ charges: import_zod.z.array(allowanceOrCharge.extend({ tax: lineTax })).optional(),
8560
+ tax: import_zod.z.object({ breakdown: import_zod.z.array(breakdown).min(1) }),
8561
+ totals,
8562
+ payment: payment.optional()
8563
+ });
8564
+ var EINVOICE_PROFILES = ["basic", "en16931"];
8565
+ var EINVOICE_FLAVOURS = ["factur-x", "zugferd"];
8566
+ var einvoiceOptionsSchema = import_zod.z.object({
8567
+ profile: import_zod.z.string().optional(),
8568
+ flavour: import_zod.z.string().optional(),
8569
+ xml: import_zod.z.enum(["embedded", "both"]).optional(),
8570
+ display_check: import_zod.z.enum(["warn", "strict"]).optional()
8571
+ }).strict();
8572
+ var defaultEinvoiceOptions = {
8573
+ profile: "en16931",
8574
+ flavour: "factur-x",
8575
+ xml: "embedded",
8576
+ display_check: "warn"
8577
+ };
8578
+ var knownUnsupported = {
8579
+ minimum: "MINIMUM carries no invoice lines and is not an invoice on its own; use basic or en16931",
8580
+ "basic-wl": "BASIC WL carries no invoice lines and is not an invoice on its own; use basic or en16931",
8581
+ basicwl: "BASIC WL carries no invoice lines and is not an invoice on its own; use basic or en16931",
8582
+ extended: "EXTENDED is not offered; use en16931, which carries the full European standard",
8583
+ xrechnung: "XRechnung is not offered yet; use en16931"
8584
+ };
8585
+ function resolveEinvoiceOptions(...layers) {
8586
+ let merged = null;
8587
+ for (const layer of layers) {
8588
+ if (layer === false) merged = null;
8589
+ else if (layer) merged = { ...merged ?? {}, ...definedOnly(layer) };
8590
+ }
8591
+ if (!merged) return { ok: true, options: null };
8592
+ const profile = (merged.profile ?? defaultEinvoiceOptions.profile).trim().toLowerCase();
8593
+ const flavour = (merged.flavour ?? defaultEinvoiceOptions.flavour).trim().toLowerCase();
8594
+ if (!EINVOICE_PROFILES.includes(profile))
8595
+ return {
8596
+ ok: false,
8597
+ field: "profile",
8598
+ value: profile,
8599
+ reason: knownUnsupported[profile] ?? `Unknown profile; use ${EINVOICE_PROFILES.join(" or ")}`
8600
+ };
8601
+ if (!EINVOICE_FLAVOURS.includes(flavour))
8602
+ return {
8603
+ ok: false,
8604
+ field: "flavour",
8605
+ value: flavour,
8606
+ reason: `Unknown flavour; use ${EINVOICE_FLAVOURS.join(" or ")}`
8607
+ };
8608
+ return {
8609
+ ok: true,
8610
+ options: {
8611
+ profile,
8612
+ flavour,
8613
+ xml: merged.xml ?? defaultEinvoiceOptions.xml,
8614
+ display_check: merged.display_check ?? defaultEinvoiceOptions.display_check
8615
+ }
8616
+ };
8617
+ }
8618
+ function definedOnly(layer) {
8619
+ return Object.fromEntries(
8620
+ Object.entries(layer).filter(([, value]) => value !== void 0)
8621
+ );
8622
+ }
8623
+
8624
+ // packages/engine/src/lib/einvoice/check.ts
8625
+ var REQUIRED = {
8626
+ number: ["BR-02", "BT-1"],
8627
+ issue_date: ["BR-03", "BT-2"],
8628
+ currency: ["BR-05", "BT-5"],
8629
+ seller: ["BR-06", "BG-4"],
8630
+ "seller.name": ["BR-06", "BT-27"],
8631
+ "seller.address": ["BR-08", "BG-5"],
8632
+ "seller.address.country": ["BR-09", "BT-40"],
8633
+ buyer: ["BR-07", "BG-7"],
8634
+ "buyer.name": ["BR-07", "BT-44"],
8635
+ "buyer.address": ["BR-10", "BG-8"],
8636
+ "buyer.address.country": ["BR-11", "BT-55"],
8637
+ lines: ["BR-16", "BG-25"],
8638
+ "lines[].id": ["BR-21", "BT-126"],
8639
+ "lines[].quantity": ["BR-22", "BT-129"],
8640
+ "lines[].net_amount": ["BR-24", "BT-131"],
8641
+ "lines[].name": ["BR-25", "BT-153"],
8642
+ "lines[].net_price": ["BR-26", "BT-146"],
8643
+ "lines[].tax": ["BR-CO-04", "BT-151"],
8644
+ "lines[].tax.category": ["BR-CO-04", "BT-151"],
8645
+ tax: ["BR-CO-18", "BG-23"],
8646
+ "tax.breakdown": ["BR-CO-18", "BG-23"],
8647
+ "tax.breakdown[].basis": ["BR-45", "BT-116"],
8648
+ "tax.breakdown[].amount": ["BR-46", "BT-117"],
8649
+ "tax.breakdown[].category": ["BR-47", "BT-118"],
8650
+ totals: ["BR-12", "BG-22"],
8651
+ "totals.line_net": ["BR-12", "BT-106"],
8652
+ "totals.tax_basis": ["BR-13", "BT-109"],
8653
+ "totals.tax_total": ["BR-CO-14", "BT-110"],
8654
+ "totals.grand": ["BR-14", "BT-112"],
8655
+ "totals.due": ["BR-15", "BT-115"],
8656
+ "allowances[].amount": ["BR-31", "BT-92"],
8657
+ "allowances[].tax": ["BR-32", "BT-95"],
8658
+ "allowances[].tax.category": ["BR-32", "BT-95"],
8659
+ "charges[].amount": ["BR-36", "BT-99"],
8660
+ "charges[].tax": ["BR-37", "BT-102"],
8661
+ "charges[].tax.category": ["BR-37", "BT-102"],
8662
+ "lines[].allowances[].amount": ["BR-41", "BT-136"],
8663
+ "lines[].charges[].amount": ["BR-43", "BT-141"],
8664
+ "delivery.address.country": ["BR-57", "BT-80"],
8665
+ "preceding_invoices[].number": ["BR-55", "BT-25"]
8666
+ };
8667
+ var RULE_PREFIX = {
8668
+ S: "BR-S",
8669
+ Z: "BR-Z",
8670
+ E: "BR-E",
8671
+ AE: "BR-AE",
8672
+ K: "BR-IC",
8673
+ G: "BR-G",
8674
+ O: "BR-O"
8675
+ };
8676
+ var UNIT_SUGGESTIONS = {
8677
+ piece: "C62",
8678
+ pieces: "C62",
8679
+ pcs: "C62",
8680
+ pc: "C62",
8681
+ stk: "C62",
8682
+ st\u00FCck: "C62",
8683
+ unit: "C62",
8684
+ units: "C62",
8685
+ each: "C62",
8686
+ hour: "HUR",
8687
+ hours: "HUR",
8688
+ h: "HUR",
8689
+ std: "HUR",
8690
+ stunde: "HUR",
8691
+ stunden: "HUR",
8692
+ day: "DAY",
8693
+ days: "DAY",
8694
+ tag: "DAY",
8695
+ tage: "DAY",
8696
+ month: "MON",
8697
+ months: "MON",
8698
+ monat: "MON",
8699
+ year: "ANN",
8700
+ min: "MIN",
8701
+ minute: "MIN",
8702
+ minutes: "MIN",
8703
+ kg: "KGM",
8704
+ g: "GRM",
8705
+ t: "TNE",
8706
+ l: "LTR",
8707
+ litre: "LTR",
8708
+ liter: "LTR",
8709
+ m: "MTR",
8710
+ km: "KMT",
8711
+ m2: "MTK",
8712
+ qm: "MTK",
8713
+ m3: "MTQ",
8714
+ kwh: "KWH",
8715
+ flat: "LS",
8716
+ pauschal: "LS"
8717
+ };
8718
+ var FRENCH_FRAMEWORKS = /* @__PURE__ */ new Set([
8719
+ "B1",
8720
+ "S1",
8721
+ "M1",
8722
+ "B2",
8723
+ "S2",
8724
+ "M2",
8725
+ "B4",
8726
+ "S4",
8727
+ "M4",
8728
+ "S5",
8729
+ "S6",
8730
+ "B7",
8731
+ "S7"
8732
+ ]);
8733
+ var FRENCH_OPERATION = { goods: "B1", services: "S1", mixed: "M1" };
8734
+ var CREDIT_TRANSFER = /* @__PURE__ */ new Set(["30", "58"]);
8735
+ function checkInvoice(data, options = {}) {
8736
+ const problems = [];
8737
+ const add2 = (code, path, message, extra = {}) => {
8738
+ problems.push({
8739
+ code,
8740
+ path,
8741
+ ...extra.rule ? { rule: extra.rule } : {},
8742
+ message: extra.rule ? `${message} (${extra.rule})` : message,
8743
+ args: {
8744
+ path,
8745
+ ...extra.rule ? { rule: extra.rule } : {},
8746
+ ...extra.args
8747
+ }
8748
+ });
8749
+ };
8750
+ const parsed = invoiceSchema.safeParse(data);
8751
+ if (!parsed.success) {
8752
+ for (const issue of parsed.error.issues) {
8753
+ const path = pathText(issue.path);
8754
+ const required = REQUIRED[path.replace(/\[\d+\]/g, "[]")];
8755
+ const absent = valueAt(data, issue.path) === void 0;
8756
+ if (absent && required)
8757
+ add2("missing", path, `_invoice.${path} is missing (${required[1]})`, {
8758
+ rule: required[0],
8759
+ args: { term: required[1] }
8760
+ });
8761
+ else if (absent) add2("missing", path, `_invoice.${path} is missing`);
8762
+ else if (issue.code === "too_small" && path === "lines")
8763
+ add2(
8764
+ "missing",
8765
+ path,
8766
+ "_invoice.lines is empty: an invoice has at least one line (BG-25)",
8767
+ {
8768
+ rule: "BR-16",
8769
+ args: { term: "BG-25" }
8770
+ }
8771
+ );
8772
+ else if (issue.code === "too_small" && path === "tax.breakdown")
8773
+ add2(
8774
+ "missing",
8775
+ path,
8776
+ "_invoice.tax.breakdown is empty: an invoice has at least one VAT breakdown (BG-23)",
8777
+ {
8778
+ rule: "BR-CO-18",
8779
+ args: { term: "BG-23" }
8780
+ }
8781
+ );
8782
+ else
8783
+ add2("invalid", path, `_invoice.${path}: ${issue.message}`, {
8784
+ args: { detail: issue.message }
8785
+ });
8786
+ }
8787
+ return { ok: false, invoice: null, problems };
8788
+ }
8789
+ const invoice = normalise(parsed.data, add2, options);
8790
+ if (problems.length > 0) return { ok: false, invoice: null, problems };
8791
+ rules(invoice, add2);
8792
+ return problems.length > 0 ? { ok: false, invoice: null, problems } : { ok: true, invoice, problems: [] };
8793
+ }
8794
+ function pathText(path) {
8795
+ let out = "";
8796
+ for (const key of path)
8797
+ out += typeof key === "number" ? `[${key}]` : out ? `.${String(key)}` : String(key);
8798
+ return out;
8799
+ }
8800
+ function valueAt(data, path) {
8801
+ let value = data;
8802
+ for (const key of path) {
8803
+ if (value === null || typeof value !== "object") return void 0;
8804
+ value = value[key];
8805
+ }
8806
+ return value === null ? void 0 : value;
8807
+ }
8808
+ function normalise(input, add2, options) {
8809
+ const amount2 = (value, path) => {
8810
+ const text4 = decimalText(value);
8811
+ if (text4 === null) {
8812
+ add2("not_a_number", path, `_invoice.${path} is not a number`);
8813
+ return "0";
8814
+ }
8815
+ if (decimalPlaces(text4) > 2) {
8816
+ add2(
8817
+ "decimals",
8818
+ path,
8819
+ `_invoice.${path} has more than 2 decimals; an amount of the invoice is exact to the cent`,
8820
+ {
8821
+ args: { max: 2, value: text4 }
8822
+ }
8823
+ );
8824
+ return "0";
8825
+ }
8826
+ return text4;
8827
+ };
8828
+ const number = (value, path, max2) => {
8829
+ const text4 = decimalText(value);
8830
+ if (text4 === null) {
8831
+ add2("not_a_number", path, `_invoice.${path} is not a number`);
8832
+ return "0";
8833
+ }
8834
+ if (decimalPlaces(text4) > max2) {
8835
+ add2("decimals", path, `_invoice.${path} has more than ${max2} decimals`, {
8836
+ args: { max: max2, value: text4 }
8837
+ });
8838
+ return "0";
8839
+ }
8840
+ return text4;
8841
+ };
8842
+ const optionalAmount = (value, path) => value === void 0 ? void 0 : amount2(value, path);
8843
+ const date2 = (value, path) => {
8844
+ if (value === void 0) return void 0;
8845
+ const [year = 0, month = 0, day = 0] = value.split("-").map(Number);
8846
+ const real = new Date(Date.UTC(year, month - 1, day));
8847
+ if (real.getUTCFullYear() !== year || real.getUTCMonth() !== month - 1 || real.getUTCDate() !== day) {
8848
+ add2("date", path, `_invoice.${path} is not a date of the calendar`, {
8849
+ args: { value }
8850
+ });
8851
+ }
8852
+ return value;
8853
+ };
8854
+ const code = (value, path, list3, name, rule2) => {
8855
+ const upper = value.trim().toUpperCase();
8856
+ if (!list3.has(upper))
8857
+ add2(
8858
+ "code_list",
8859
+ path,
8860
+ `_invoice.${path}: "${value}" is not a code of ${name}`,
8861
+ {
8862
+ rule: rule2,
8863
+ args: { value, list: name }
8864
+ }
8865
+ );
8866
+ return upper;
8867
+ };
8868
+ const period3 = (value, path, rule2) => {
8869
+ if (!value || value.start === void 0 && value.end === void 0)
8870
+ return void 0;
8871
+ const start = date2(value.start, `${path}.start`);
8872
+ const end = date2(value.end, `${path}.end`);
8873
+ if (start && end && end < start)
8874
+ add2("date_order", path, `_invoice.${path} ends before it starts`, {
8875
+ rule: rule2
8876
+ });
8877
+ return { ...start ? { start } : {}, ...end ? { end } : {} };
8878
+ };
8879
+ const identifier4 = (value) => {
8880
+ if (value === void 0) return void 0;
8881
+ if (typeof value === "string") return { value };
8882
+ return {
8883
+ value: value.value,
8884
+ ...value.scheme ? { scheme: value.scheme } : {}
8885
+ };
8886
+ };
8887
+ const address3 = (value, path) => ({
8888
+ ...defined({
8889
+ street: value.street,
8890
+ street2: value.street2,
8891
+ street3: value.street3,
8892
+ postcode: value.postcode,
8893
+ city: value.city,
8894
+ subdivision: value.subdivision
8895
+ }),
8896
+ country: code(
8897
+ value.country,
8898
+ `${path}.country`,
8899
+ countryCodes,
8900
+ "ISO 3166-1 (two letters, as in DE)",
8901
+ "BR-CL-14"
8902
+ )
8903
+ });
8904
+ const vatId = (value, path) => {
8905
+ if (value === void 0) return void 0;
8906
+ const id = value.replace(/\s/g, "").toUpperCase();
8907
+ const prefix = id.slice(0, 2);
8908
+ if (!(countryCodes.has(prefix) || prefix === "EL") || !/^[A-Z]{2}/.test(id))
8909
+ add2(
8910
+ "vat_id_prefix",
8911
+ path,
8912
+ `_invoice.${path} must start with the country code of the VAT ID, as in DE123456789`,
8913
+ {
8914
+ rule: "BR-CO-09",
8915
+ args: { value }
8916
+ }
8917
+ );
8918
+ return id;
8919
+ };
8920
+ const electronicAddress = (value, path, rule2) => {
8921
+ const id = identifier4(value);
8922
+ if (!id) return void 0;
8923
+ const scheme = id.scheme?.trim().toUpperCase() ?? (/^[^@\s]+@[^@\s]+$/.test(id.value) ? "EM" : void 0);
8924
+ if (!scheme) {
8925
+ add2(
8926
+ "scheme_required",
8927
+ path,
8928
+ `_invoice.${path} needs its scheme: write { "value": \u2026, "scheme": \u2026 } with a code of the EAS list, or an e-mail address`,
8929
+ { rule: rule2 }
8930
+ );
8931
+ return void 0;
8932
+ }
8933
+ if (!electronicAddressSchemes.has(scheme))
8934
+ add2(
8935
+ "code_list",
8936
+ `${path}.scheme`,
8937
+ `_invoice.${path}.scheme: "${scheme}" is not a code of the EAS list (EM is an e-mail address)`,
8938
+ {
8939
+ rule: "BR-CL-25",
8940
+ args: { value: scheme, list: "the EAS list" }
8941
+ }
8942
+ );
8943
+ return { value: id.value, scheme };
8944
+ };
8945
+ const party3 = (value, path, addressRule) => {
8946
+ const id = identifier4(value.id);
8947
+ const registration = identifier4(value.legal_registration);
8948
+ const electronic = electronicAddress(
8949
+ value.electronic_address,
8950
+ `${path}.electronic_address`,
8951
+ addressRule
8952
+ );
8953
+ const vat = vatId(value.vat_id, `${path}.vat_id`);
8954
+ return {
8955
+ name: value.name,
8956
+ ...defined({ trading_name: value.trading_name, vat_id: vat }),
8957
+ ...id ? { id } : {},
8958
+ ...registration ? { legal_registration: registration } : {},
8959
+ address: address3(value.address, `${path}.address`),
8960
+ ...value.contact && Object.values(value.contact).some((v) => v !== void 0) ? { contact: defined(value.contact) } : {},
8961
+ ...electronic ? { electronic_address: electronic } : {}
8962
+ };
8963
+ };
8964
+ const tax = (value, path) => ({
8965
+ category: value.category,
8966
+ ...value.rate === void 0 || value.rate === null ? {} : { rate: number(value.rate, `${path}.rate`, 4) }
8967
+ });
8968
+ const allowance = (value, path) => ({
8969
+ amount: amount2(value.amount, `${path}.amount`),
8970
+ ...defined({
8971
+ reason: value.reason,
8972
+ reason_code: value.reason_code?.trim().toUpperCase(),
8973
+ base_amount: optionalAmount(value.base_amount, `${path}.base_amount`),
8974
+ percent: value.percent === void 0 ? void 0 : number(value.percent, `${path}.percent`, 4)
8975
+ })
8976
+ });
8977
+ const seller = party3(input.seller, "seller", "BR-62");
8978
+ const buyer = party3(input.buyer, "buyer", "BR-63");
8979
+ if (input.buyer.siren !== void 0) {
8980
+ const siren = input.buyer.siren.replace(/\s/g, "");
8981
+ if (buyer.legal_registration && buyer.legal_registration.value.replace(/\s/g, "") !== siren)
8982
+ add2(
8983
+ "siren_mismatch",
8984
+ "buyer.siren",
8985
+ "_invoice.buyer.siren and _invoice.buyer.legal_registration name different registrations; state one of them"
8986
+ );
8987
+ else
8988
+ buyer.legal_registration = {
8989
+ value: siren,
8990
+ scheme: buyer.legal_registration?.scheme ?? "0002"
8991
+ };
8992
+ }
8993
+ const notes = [
8994
+ ...input.note ? [{ text: input.note }] : [],
8995
+ ...(input.notes ?? []).map((note2) => ({
8996
+ text: note2.text,
8997
+ ...note2.subject_code ? { subject_code: note2.subject_code.trim().toUpperCase() } : {}
8998
+ }))
8999
+ ];
9000
+ const delivery = input.delivery ? defined({
9001
+ name: input.delivery.name,
9002
+ date: date2(input.delivery.date, "delivery.date"),
9003
+ address: input.delivery.address ? address3(input.delivery.address, "delivery.address") : void 0
9004
+ }) : void 0;
9005
+ let businessProcess;
9006
+ if (input.operation_code !== void 0)
9007
+ businessProcess = input.operation_code.trim().toUpperCase();
9008
+ else if (input.operation !== void 0)
9009
+ businessProcess = FRENCH_OPERATION[input.operation];
9010
+ const payment2 = input.payment ? defined({
9011
+ means_code: input.payment.means_code === void 0 ? void 0 : code(
9012
+ String(input.payment.means_code),
9013
+ "payment.means_code",
9014
+ paymentMeansCodes,
9015
+ "UNTDID 4461 (58 is a SEPA credit transfer)",
9016
+ "BR-CL-16"
9017
+ ),
9018
+ iban: iban(input.payment.iban, "payment.iban", add2),
9019
+ bic: input.payment.bic?.replace(/\s/g, "").toUpperCase(),
9020
+ account_name: input.payment.account_name,
9021
+ reference: input.payment.reference,
9022
+ terms: input.payment.terms,
9023
+ due_date: date2(input.payment.due_date, "payment.due_date"),
9024
+ mandate_id: input.payment.mandate_id,
9025
+ creditor_id: input.payment.creditor_id,
9026
+ debited_iban: iban(
9027
+ input.payment.debited_iban,
9028
+ "payment.debited_iban",
9029
+ add2
9030
+ )
9031
+ }) : void 0;
9032
+ const rounding = optionalAmount(input.totals.rounding, "totals.rounding");
9033
+ if (options.profile === "basic" && rounding !== void 0 && toUnits(rounding, 2) !== 0n)
9034
+ add2(
9035
+ "profile",
9036
+ "totals.rounding",
9037
+ "_invoice.totals.rounding cannot be carried by the profile basic, which has no rounding amount (BT-114); use en16931",
9038
+ {
9039
+ args: { profile: "basic", term: "BT-114" }
9040
+ }
9041
+ );
9042
+ return {
9043
+ number: input.number,
9044
+ issue_date: date2(input.issue_date, "issue_date") ?? input.issue_date,
9045
+ type_code: code(
9046
+ String(input.type_code),
9047
+ "type_code",
9048
+ documentTypeCodes,
9049
+ "UNTDID 1001 (380 is an invoice, 381 a credit note)",
9050
+ "BR-CL-01"
9051
+ ),
9052
+ currency: code(
9053
+ input.currency,
9054
+ "currency",
9055
+ currencyCodes,
9056
+ "ISO 4217 (three letters, as in EUR)",
9057
+ "BR-CL-04"
9058
+ ),
9059
+ ...defined({
9060
+ accounting_currency: input.accounting_currency === void 0 ? void 0 : code(
9061
+ input.accounting_currency,
9062
+ "accounting_currency",
9063
+ currencyCodes,
9064
+ "ISO 4217 (three letters, as in EUR)",
9065
+ "BR-CL-05"
9066
+ ),
9067
+ buyer_reference: input.buyer_reference,
9068
+ order_reference: input.order_reference,
9069
+ contract_reference: input.contract_reference,
9070
+ period: period3(input.period, "period", "BR-29"),
9071
+ business_process: businessProcess
9072
+ }),
9073
+ notes,
9074
+ preceding_invoices: (input.preceding_invoices ?? []).map(
9075
+ (reference, i) => ({
9076
+ number: reference.number,
9077
+ ...defined({
9078
+ issue_date: date2(
9079
+ reference.issue_date,
9080
+ `preceding_invoices[${i}].issue_date`
9081
+ )
9082
+ })
9083
+ })
9084
+ ),
9085
+ seller: {
9086
+ ...seller,
9087
+ ...defined({
9088
+ tax_number: input.seller.tax_number,
9089
+ legal_info: input.seller.legal_info,
9090
+ vat_on_debits: input.seller.vat_on_debits
9091
+ })
9092
+ },
9093
+ buyer,
9094
+ ...delivery && Object.keys(delivery).length > 0 ? { delivery } : {},
9095
+ lines: input.lines.map((line2, i) => {
9096
+ const path = `lines[${i}]`;
9097
+ const unit = line2.unit_code.trim();
9098
+ const known = unitCodes.has(unit.toUpperCase()) || unitCodes.has(unit);
9099
+ if (!known) {
9100
+ const suggestion = UNIT_SUGGESTIONS[unit.toLowerCase()];
9101
+ add2(
9102
+ "code_list",
9103
+ `${path}.unit_code`,
9104
+ `_invoice.${path}.unit_code: "${unit}" is not a code of UN/ECE Recommendation 20${suggestion ? `; ${suggestion} is the code for it` : " (C62 is a piece, HUR an hour, DAY a day)"}`,
9105
+ {
9106
+ rule: "BR-CL-23",
9107
+ args: {
9108
+ value: unit,
9109
+ list: "UN/ECE Recommendation 20",
9110
+ ...suggestion ? { suggestion } : {}
9111
+ }
9112
+ }
9113
+ );
9114
+ }
9115
+ const price = number(line2.net_price, `${path}.net_price`, 6);
9116
+ if (price.startsWith("-"))
9117
+ add2(
9118
+ "negative",
9119
+ `${path}.net_price`,
9120
+ `_invoice.${path}.net_price must not be negative; a credit is a negative quantity`,
9121
+ { rule: "BR-27" }
9122
+ );
9123
+ const standard = identifier4(line2.standard_item_id);
9124
+ if (standard && !standard.scheme)
9125
+ add2(
9126
+ "scheme_required",
9127
+ `${path}.standard_item_id`,
9128
+ `_invoice.${path}.standard_item_id needs its scheme: write { "value": \u2026, "scheme": \u2026 }, 0160 for a GTIN`,
9129
+ { rule: "BR-64" }
9130
+ );
9131
+ return {
9132
+ id: String(line2.id),
9133
+ name: line2.name,
9134
+ quantity: number(line2.quantity, `${path}.quantity`, 6),
9135
+ unit_code: unitCodes.has(unit) ? unit : unit.toUpperCase(),
9136
+ net_price: price,
9137
+ net_amount: amount2(line2.net_amount, `${path}.net_amount`),
9138
+ tax: tax(line2.tax, `${path}.tax`),
9139
+ allowances: (line2.allowances ?? []).map(
9140
+ (a, j) => allowance(a, `${path}.allowances[${j}]`)
9141
+ ),
9142
+ charges: (line2.charges ?? []).map(
9143
+ (c, j) => allowance(c, `${path}.charges[${j}]`)
9144
+ ),
9145
+ ...defined({
9146
+ description: line2.description,
9147
+ price_base_quantity: line2.price_base_quantity === void 0 ? void 0 : number(
9148
+ line2.price_base_quantity,
9149
+ `${path}.price_base_quantity`,
9150
+ 6
9151
+ ),
9152
+ period: period3(line2.period, `${path}.period`, "BR-30"),
9153
+ buyer_accounting_reference: line2.buyer_accounting_reference,
9154
+ note: line2.note,
9155
+ seller_item_id: line2.seller_item_id,
9156
+ buyer_item_id: line2.buyer_item_id,
9157
+ standard_item_id: standard,
9158
+ order_line_reference: line2.order_line_reference
9159
+ })
9160
+ };
9161
+ }),
9162
+ allowances: (input.allowances ?? []).map((a, i) => ({
9163
+ ...allowance(a, `allowances[${i}]`),
9164
+ tax: tax(a.tax, `allowances[${i}].tax`)
9165
+ })),
9166
+ charges: (input.charges ?? []).map((c, i) => ({
9167
+ ...allowance(c, `charges[${i}]`),
9168
+ tax: tax(c.tax, `charges[${i}].tax`)
9169
+ })),
9170
+ breakdown: input.tax.breakdown.map((row2, i) => ({
9171
+ ...tax(row2, `tax.breakdown[${i}]`),
9172
+ basis: amount2(row2.basis, `tax.breakdown[${i}].basis`),
9173
+ amount: amount2(row2.amount, `tax.breakdown[${i}].amount`),
9174
+ ...defined({
9175
+ exemption_reason: row2.exemption_reason,
9176
+ exemption_reason_code: row2.exemption_reason_code?.trim()
9177
+ })
9178
+ })),
9179
+ totals: {
9180
+ line_net: amount2(input.totals.line_net, "totals.line_net"),
9181
+ tax_basis: amount2(input.totals.tax_basis, "totals.tax_basis"),
9182
+ tax_total: amount2(input.totals.tax_total, "totals.tax_total"),
9183
+ grand: amount2(input.totals.grand, "totals.grand"),
9184
+ due: amount2(input.totals.due, "totals.due"),
9185
+ ...defined({
9186
+ allowances: optionalAmount(
9187
+ input.totals.allowances,
9188
+ "totals.allowances"
9189
+ ),
9190
+ charges: optionalAmount(input.totals.charges, "totals.charges"),
9191
+ prepaid: optionalAmount(input.totals.prepaid, "totals.prepaid"),
9192
+ tax_total_accounting: optionalAmount(
9193
+ input.totals.tax_total_accounting,
9194
+ "totals.tax_total_accounting"
9195
+ ),
9196
+ rounding
9197
+ })
9198
+ },
9199
+ ...payment2 && Object.keys(payment2).length > 0 ? { payment: payment2 } : {}
9200
+ };
9201
+ }
9202
+ function iban(value, path, add2) {
9203
+ if (value === void 0) return void 0;
9204
+ const normalised = normaliseIban(value);
9205
+ if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}$/.test(normalised) || !ibanChecksum(normalised))
9206
+ add2(
9207
+ "iban",
9208
+ path,
9209
+ `_invoice.${path} is not a valid IBAN: its check digits do not match`
9210
+ );
9211
+ return normalised;
9212
+ }
9213
+ function defined(value) {
9214
+ return Object.fromEntries(
9215
+ Object.entries(value).filter(([, v]) => v !== void 0)
9216
+ );
9217
+ }
9218
+ var cents = (text4) => text4 === void 0 ? 0n : toUnits(text4, 2);
9219
+ var money = (value) => fromUnits(value, 2);
9220
+ var rateKey = (tax) => `${tax.category}|${tax.rate ?? ""}`;
9221
+ function rules(invoice, add2) {
9222
+ const { totals: totals2 } = invoice;
9223
+ const sum = (path, stated, expected, rule2, formula) => {
9224
+ if (stated !== expected)
9225
+ add2(
9226
+ "sum",
9227
+ path,
9228
+ `_invoice.${path} is ${money(stated)}, but ${formula} ${money(expected)}`,
9229
+ {
9230
+ rule: rule2,
9231
+ args: { value: money(stated), expected: money(expected) }
9232
+ }
9233
+ );
9234
+ };
9235
+ const lineNet = invoice.lines.reduce(
9236
+ (total, line2) => total + cents(line2.net_amount),
9237
+ 0n
9238
+ );
9239
+ const allowanceSum = invoice.allowances.reduce(
9240
+ (total, a) => total + cents(a.amount),
9241
+ 0n
9242
+ );
9243
+ const chargeSum = invoice.charges.reduce(
9244
+ (total, c) => total + cents(c.amount),
9245
+ 0n
9246
+ );
9247
+ const taxSum = invoice.breakdown.reduce(
9248
+ (total, row2) => total + cents(row2.amount),
9249
+ 0n
9250
+ );
9251
+ sum(
9252
+ "totals.line_net",
9253
+ cents(totals2.line_net),
9254
+ lineNet,
9255
+ "BR-CO-10",
9256
+ "the net amounts of the lines add up to"
9257
+ );
9258
+ sum(
9259
+ "totals.allowances",
9260
+ cents(totals2.allowances),
9261
+ allowanceSum,
9262
+ "BR-CO-11",
9263
+ "the allowances of the document add up to"
9264
+ );
9265
+ sum(
9266
+ "totals.charges",
9267
+ cents(totals2.charges),
9268
+ chargeSum,
9269
+ "BR-CO-12",
9270
+ "the charges of the document add up to"
9271
+ );
9272
+ sum(
9273
+ "totals.tax_basis",
9274
+ cents(totals2.tax_basis),
9275
+ cents(totals2.line_net) - cents(totals2.allowances) + cents(totals2.charges),
9276
+ "BR-CO-13",
9277
+ "line_net less allowances plus charges is"
9278
+ );
9279
+ sum(
9280
+ "totals.tax_total",
9281
+ cents(totals2.tax_total),
9282
+ taxSum,
9283
+ "BR-CO-14",
9284
+ "the VAT amounts of the breakdown add up to"
9285
+ );
9286
+ sum(
9287
+ "totals.grand",
9288
+ cents(totals2.grand),
9289
+ cents(totals2.tax_basis) + cents(totals2.tax_total),
9290
+ "BR-CO-15",
9291
+ "tax_basis plus tax_total is"
9292
+ );
9293
+ sum(
9294
+ "totals.due",
9295
+ cents(totals2.due),
9296
+ cents(totals2.grand) - cents(totals2.prepaid) + cents(totals2.rounding),
9297
+ "BR-CO-16",
9298
+ "grand less prepaid plus rounding is"
9299
+ );
9300
+ if (invoice.accounting_currency !== void 0 && totals2.tax_total_accounting === void 0)
9301
+ add2(
9302
+ "missing",
9303
+ "totals.tax_total_accounting",
9304
+ `_invoice.totals.tax_total_accounting is missing: with accounting_currency ${invoice.accounting_currency} the invoice states its VAT total in that currency (BT-111)`,
9305
+ {
9306
+ rule: "BR-53",
9307
+ args: { term: "BT-111" }
9308
+ }
9309
+ );
9310
+ if (invoice.accounting_currency === void 0 && totals2.tax_total_accounting !== void 0)
9311
+ add2(
9312
+ "missing",
9313
+ "accounting_currency",
9314
+ "_invoice.accounting_currency is missing: totals.tax_total_accounting needs the currency it is stated in (BT-6)",
9315
+ {
9316
+ rule: "BR-53",
9317
+ args: { term: "BT-6" }
9318
+ }
9319
+ );
9320
+ const used = /* @__PURE__ */ new Map();
9321
+ const use = (tax, value, path) => {
9322
+ const entry = used.get(rateKey(tax)) ?? {
9323
+ tax,
9324
+ basis: 0n,
9325
+ items: 0,
9326
+ first: path
9327
+ };
9328
+ entry.basis += value;
9329
+ entry.items += 1;
9330
+ used.set(rateKey(tax), entry);
9331
+ };
9332
+ invoice.lines.forEach(
9333
+ (line2, i) => use(line2.tax, cents(line2.net_amount), `lines[${i}]`)
9334
+ );
9335
+ invoice.allowances.forEach(
9336
+ (a, i) => use(a.tax, -cents(a.amount), `allowances[${i}]`)
9337
+ );
9338
+ invoice.charges.forEach(
9339
+ (c, i) => use(c.tax, cents(c.amount), `charges[${i}]`)
9340
+ );
9341
+ const seen = /* @__PURE__ */ new Set();
9342
+ invoice.breakdown.forEach((row2, i) => {
9343
+ const path = `tax.breakdown[${i}]`;
9344
+ const prefix = RULE_PREFIX[row2.category];
9345
+ const shown = row2.rate === void 0 ? row2.category : `${row2.category} at ${row2.rate} %`;
9346
+ if (seen.has(rateKey(row2)))
9347
+ add2(
9348
+ "breakdown_duplicate",
9349
+ path,
9350
+ `_invoice.tax.breakdown lists category ${shown} twice; one entry per category and rate`,
9351
+ {
9352
+ args: { category: row2.category, rate: row2.rate ?? "" }
9353
+ }
9354
+ );
9355
+ seen.add(rateKey(row2));
9356
+ const entry = used.get(rateKey(row2));
9357
+ const expected = entry?.basis ?? 0n;
9358
+ if (cents(row2.basis) !== expected)
9359
+ add2(
9360
+ "sum",
9361
+ `${path}.basis`,
9362
+ `_invoice.${path}.basis is ${money(cents(row2.basis))}, but the lines, charges and allowances of category ${shown} come to ${money(expected)}`,
9363
+ {
9364
+ rule: `${prefix}-08`,
9365
+ args: {
9366
+ value: money(cents(row2.basis)),
9367
+ expected: money(expected),
9368
+ category: row2.category,
9369
+ rate: row2.rate ?? ""
9370
+ }
9371
+ }
9372
+ );
9373
+ if (row2.rate !== void 0) {
9374
+ const computed = divideRounded(
9375
+ cents(row2.basis) * toUnits(row2.rate, 4),
9376
+ 1000000n
9377
+ );
9378
+ const items = BigInt(Math.max(1, entry?.items ?? 1));
9379
+ const tolerance = (items + 1n) / 2n > 100n ? 100n : (items + 1n) / 2n;
9380
+ if (abs(cents(row2.amount) - computed) > tolerance)
9381
+ add2(
9382
+ "sum",
9383
+ `${path}.amount`,
9384
+ `_invoice.${path}.amount is ${money(cents(row2.amount))}, but ${row2.rate} % of ${money(cents(row2.basis))} is ${money(computed)}`,
9385
+ {
9386
+ rule: "BR-CO-17",
9387
+ args: {
9388
+ value: money(cents(row2.amount)),
9389
+ expected: money(computed),
9390
+ rate: row2.rate,
9391
+ basis: money(cents(row2.basis))
9392
+ }
9393
+ }
9394
+ );
9395
+ }
9396
+ });
9397
+ for (const [key, entry] of used) {
9398
+ if (seen.has(key)) continue;
9399
+ const shown = entry.tax.rate === void 0 ? entry.tax.category : `${entry.tax.category} at ${entry.tax.rate} %`;
9400
+ add2(
9401
+ "breakdown_missing",
9402
+ "tax.breakdown",
9403
+ `_invoice.tax.breakdown has no entry for category ${shown}, which _invoice.${entry.first} falls under`,
9404
+ {
9405
+ rule: `${RULE_PREFIX[entry.tax.category]}-01`,
9406
+ args: {
9407
+ category: entry.tax.category,
9408
+ rate: entry.tax.rate ?? "",
9409
+ used: entry.first
9410
+ }
9411
+ }
9412
+ );
9413
+ }
9414
+ const rate = (tax, path, at) => {
9415
+ const prefix = RULE_PREFIX[tax.category];
9416
+ const number = at === "line" || at === "breakdown" ? "05" : at === "allowance" ? "06" : "07";
9417
+ if (tax.category === "O") {
9418
+ if (tax.rate !== void 0)
9419
+ add2(
9420
+ "rate_forbidden",
9421
+ `${path}.rate`,
9422
+ `_invoice.${path}.rate must be left out: category O is not subject to VAT and has no rate`,
9423
+ { rule: `${prefix}-${number}` }
9424
+ );
9425
+ return;
9426
+ }
9427
+ if (tax.rate === void 0) {
9428
+ add2(
9429
+ "rate_required",
9430
+ `${path}.rate`,
9431
+ `_invoice.${path}.rate is missing: category ${tax.category} states its rate${tax.category === "S" ? "" : ", which is 0"}`,
9432
+ {
9433
+ rule: at === "breakdown" ? "BR-48" : `${prefix}-${number}`,
9434
+ args: { category: tax.category }
9435
+ }
9436
+ );
9437
+ return;
9438
+ }
9439
+ const value = toUnits(tax.rate, 4);
9440
+ if (tax.category === "S" && value <= 0n)
9441
+ add2(
9442
+ "rate_positive",
9443
+ `${path}.rate`,
9444
+ `_invoice.${path}.rate is ${tax.rate}: category S is standard rated and needs a rate above 0; a rate of 0 is category Z or E`,
9445
+ {
9446
+ rule: `${prefix}-${number}`,
9447
+ args: { rate: tax.rate }
9448
+ }
9449
+ );
9450
+ if (tax.category !== "S" && value !== 0n)
9451
+ add2(
9452
+ "rate_zero",
9453
+ `${path}.rate`,
9454
+ `_invoice.${path}.rate is ${tax.rate}: category ${tax.category} carries a rate of 0`,
9455
+ {
9456
+ rule: `${prefix}-${number}`,
9457
+ args: { rate: tax.rate, category: tax.category }
9458
+ }
9459
+ );
9460
+ };
9461
+ invoice.lines.forEach((line2, i) => rate(line2.tax, `lines[${i}].tax`, "line"));
9462
+ invoice.allowances.forEach(
9463
+ (a, i) => rate(a.tax, `allowances[${i}].tax`, "allowance")
9464
+ );
9465
+ invoice.charges.forEach((c, i) => rate(c.tax, `charges[${i}].tax`, "charge"));
9466
+ const categories = new Set(
9467
+ [...used.values()].map((entry) => entry.tax.category)
9468
+ );
9469
+ invoice.breakdown.forEach((row2, i) => {
9470
+ const path = `tax.breakdown[${i}]`;
9471
+ const prefix = RULE_PREFIX[row2.category];
9472
+ rate(row2, path, "breakdown");
9473
+ categories.add(row2.category);
9474
+ const reason = row2.exemption_reason !== void 0 || row2.exemption_reason_code !== void 0;
9475
+ if (row2.category === "S" || row2.category === "Z") {
9476
+ if (reason)
9477
+ add2(
9478
+ "exemption_reason_forbidden",
9479
+ `${path}.exemption_reason`,
9480
+ `_invoice.${path} must not state an exemption reason: category ${row2.category} is not an exemption`,
9481
+ {
9482
+ rule: `${prefix}-10`,
9483
+ args: { category: row2.category }
9484
+ }
9485
+ );
9486
+ } else if (!reason) {
9487
+ add2(
9488
+ "exemption_reason_required",
9489
+ `${path}.exemption_reason`,
9490
+ `_invoice.${path}.exemption_reason is missing: category ${row2.category} says why no VAT is charged, as text or as exemption_reason_code`,
9491
+ {
9492
+ rule: `${prefix}-10`,
9493
+ args: { category: row2.category }
9494
+ }
9495
+ );
9496
+ }
9497
+ if (row2.category !== "S" && cents(row2.amount) !== 0n)
9498
+ add2(
9499
+ "amount_zero",
9500
+ `${path}.amount`,
9501
+ `_invoice.${path}.amount is ${money(cents(row2.amount))}: category ${row2.category} carries no VAT, so the amount is 0`,
9502
+ {
9503
+ rule: `${prefix}-09`,
9504
+ args: { value: money(cents(row2.amount)), category: row2.category }
9505
+ }
9506
+ );
9507
+ });
9508
+ const sellerTaxId = invoice.seller.vat_id !== void 0 || invoice.seller.tax_number !== void 0;
9509
+ for (const category of categories) {
9510
+ const prefix = RULE_PREFIX[category];
9511
+ if (category === "O") {
9512
+ if (invoice.seller.vat_id !== void 0)
9513
+ add2(
9514
+ "vat_id_forbidden",
9515
+ "seller.vat_id",
9516
+ "_invoice.seller.vat_id must be left out: an invoice of category O is not subject to VAT; the seller is named by tax_number or legal_registration",
9517
+ { rule: "BR-O-02" }
9518
+ );
9519
+ if (invoice.buyer.vat_id !== void 0)
9520
+ add2(
9521
+ "vat_id_forbidden",
9522
+ "buyer.vat_id",
9523
+ "_invoice.buyer.vat_id must be left out: an invoice of category O is not subject to VAT",
9524
+ { rule: "BR-O-02" }
9525
+ );
9526
+ if (categories.size > 1)
9527
+ add2(
9528
+ "only_category",
9529
+ "tax.breakdown",
9530
+ "_invoice mixes category O with others: an invoice that is not subject to VAT has that one category only",
9531
+ { rule: "BR-O-11" }
9532
+ );
9533
+ continue;
9534
+ }
9535
+ if (category === "K" || category === "G") {
9536
+ if (invoice.seller.vat_id === void 0)
9537
+ add2(
9538
+ "seller_tax_id_required",
9539
+ "seller.vat_id",
9540
+ `_invoice.seller.vat_id is missing: category ${category} names the seller's VAT ID`,
9541
+ {
9542
+ rule: `${prefix}-02`,
9543
+ args: { category }
9544
+ }
9545
+ );
9546
+ } else if (!sellerTaxId) {
9547
+ add2(
9548
+ "seller_tax_id_required",
9549
+ "seller.vat_id",
9550
+ `_invoice.seller.vat_id is missing: category ${category} names the seller's VAT ID, or tax_number where there is none`,
9551
+ {
9552
+ rule: `${prefix}-02`,
9553
+ args: { category }
9554
+ }
9555
+ );
9556
+ }
9557
+ if (category === "AE" && invoice.buyer.vat_id === void 0 && invoice.buyer.legal_registration === void 0)
9558
+ add2(
9559
+ "buyer_tax_id_required",
9560
+ "buyer.vat_id",
9561
+ "_invoice.buyer.vat_id is missing: with reverse charge the buyer owes the VAT, so the invoice names the buyer's VAT ID or legal_registration",
9562
+ {
9563
+ rule: "BR-AE-02",
9564
+ args: { category }
9565
+ }
9566
+ );
9567
+ if (category === "K") {
9568
+ if (invoice.buyer.vat_id === void 0)
9569
+ add2(
9570
+ "buyer_tax_id_required",
9571
+ "buyer.vat_id",
9572
+ "_invoice.buyer.vat_id is missing: an intra-community supply names the buyer's VAT ID",
9573
+ {
9574
+ rule: "BR-IC-02",
9575
+ args: { category }
9576
+ }
9577
+ );
9578
+ if (invoice.delivery?.date === void 0 && invoice.period === void 0)
9579
+ add2(
9580
+ "delivery_required",
9581
+ "delivery.date",
9582
+ "_invoice.delivery.date is missing: an intra-community supply states when it was delivered, or the invoicing period",
9583
+ { rule: "BR-IC-11" }
9584
+ );
9585
+ if (invoice.delivery?.address === void 0)
9586
+ add2(
9587
+ "delivery_required",
9588
+ "delivery.address.country",
9589
+ "_invoice.delivery.address.country is missing: an intra-community supply states the country it was delivered to",
9590
+ { rule: "BR-IC-12" }
9591
+ );
9592
+ }
9593
+ }
9594
+ if (invoice.seller.id === void 0 && invoice.seller.legal_registration === void 0 && invoice.seller.vat_id === void 0)
9595
+ add2(
9596
+ "seller_identifier_required",
9597
+ "seller.vat_id",
9598
+ "_invoice.seller needs an identifier the buyer can match: vat_id, legal_registration or id",
9599
+ { rule: "BR-CO-26" }
9600
+ );
9601
+ const payment2 = invoice.payment;
9602
+ const account = payment2?.iban !== void 0 || payment2?.bic !== void 0 || payment2?.account_name !== void 0;
9603
+ if (payment2 && payment2.means_code === void 0 && (account || payment2.mandate_id !== void 0 || payment2.debited_iban !== void 0))
9604
+ add2(
9605
+ "means_code_required",
9606
+ "payment.means_code",
9607
+ "_invoice.payment.means_code is missing: payment details say how the invoice is paid, 58 for a SEPA credit transfer, 59 for a SEPA direct debit",
9608
+ { rule: "BR-49" }
9609
+ );
9610
+ if (payment2?.means_code !== void 0 && CREDIT_TRANSFER.has(payment2.means_code) && payment2.iban === void 0)
9611
+ add2(
9612
+ "account_required",
9613
+ "payment.iban",
9614
+ `_invoice.payment.iban is missing: a payment by credit transfer (code ${payment2.means_code}) names the account`,
9615
+ {
9616
+ rule: "BR-61",
9617
+ args: { means: payment2.means_code }
9618
+ }
9619
+ );
9620
+ if (cents(totals2.due) > 0n && payment2?.due_date === void 0 && payment2?.terms === void 0)
9621
+ add2(
9622
+ "due_or_terms_required",
9623
+ "payment.due_date",
9624
+ "_invoice.payment.due_date is missing: an invoice with an amount due states when it is due, or its payment terms",
9625
+ { rule: "BR-CO-25" }
9626
+ );
9627
+ if (invoice.seller.address.country === "FR" && invoice.buyer.address.country === "FR") {
9628
+ const digits = (id) => (id?.value ?? "").replace(/\s/g, "");
9629
+ if (!/^\d{9}$/.test(digits(invoice.seller.legal_registration)))
9630
+ add2(
9631
+ "fr_seller_siren",
9632
+ "seller.legal_registration",
9633
+ "_invoice.seller.legal_registration is missing or not nine digits: an invoice between French businesses names the seller's SIREN (BT-30)",
9634
+ { rule: "BR-FR-10" }
9635
+ );
9636
+ if (!/^\d{9}$/.test(digits(invoice.buyer.legal_registration)))
9637
+ add2(
9638
+ "fr_buyer_siren",
9639
+ "buyer.siren",
9640
+ "_invoice.buyer.siren is missing or not nine digits: an invoice between French businesses names the buyer's SIREN (BT-47)",
9641
+ { rule: "BR-FR-11" }
9642
+ );
9643
+ if (invoice.business_process === void 0)
9644
+ add2(
9645
+ "fr_operation",
9646
+ "operation",
9647
+ "_invoice.operation is missing: an invoice between French businesses says whether it is for goods, services or mixed (BT-23)",
9648
+ { rule: "BR-FR-08" }
9649
+ );
9650
+ else if (!FRENCH_FRAMEWORKS.has(invoice.business_process))
9651
+ add2(
9652
+ "code_list",
9653
+ "operation_code",
9654
+ `_invoice.operation_code: "${invoice.business_process}" is not a billing framework of the French specifications (B1, S1, M1 and their variants)`,
9655
+ {
9656
+ rule: "BR-FR-08",
9657
+ args: {
9658
+ value: invoice.business_process,
9659
+ list: "the French billing frameworks"
9660
+ }
9661
+ }
9662
+ );
9663
+ if (invoice.seller.vat_on_debits === void 0)
9664
+ add2(
9665
+ "fr_vat_on_debits",
9666
+ "seller.vat_on_debits",
9667
+ "_invoice.seller.vat_on_debits is missing: an invoice between French businesses says whether the seller pays VAT on invoicing (true) or on payment (false)"
9668
+ );
9669
+ }
9670
+ }
9671
+
9672
+ // packages/engine/src/lib/einvoice/cii.ts
9673
+ var CII_GUIDELINES = {
9674
+ basic: "urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:basic",
9675
+ en16931: "urn:cen.eu:en16931:2017"
9676
+ };
9677
+ var EINVOICE_XML_NAME = "factur-x.xml";
9678
+ function leaf(name, text4, attributes) {
9679
+ if (text4 === void 0 || text4 === "") return void 0;
9680
+ const kept = Object.entries(attributes ?? {}).filter(
9681
+ (entry) => entry[1] !== void 0
9682
+ );
9683
+ return {
9684
+ name,
9685
+ text: text4,
9686
+ ...kept.length > 0 ? { attributes: Object.fromEntries(kept) } : {}
9687
+ };
9688
+ }
9689
+ function group2(name, children) {
9690
+ const kept = children.filter((child2) => !!child2);
9691
+ return kept.length > 0 ? { name, children: kept } : void 0;
9692
+ }
9693
+ var date = (name, value) => value === void 0 ? void 0 : group2(name, [
9694
+ leaf("udt:DateTimeString", value.replace(/-/g, ""), { format: "102" })
9695
+ ]);
9696
+ var amount = (name, value, attributes) => value === void 0 ? void 0 : leaf(name, withPlaces(value, 2), attributes);
9697
+ var identifier3 = (plain, withScheme, id) => id === void 0 ? void 0 : id.scheme ? leaf(withScheme, id.value, { schemeID: id.scheme }) : leaf(plain, id.value);
9698
+ function address2(value) {
9699
+ if (!value) return void 0;
9700
+ return group2("ram:PostalTradeAddress", [
9701
+ leaf("ram:PostcodeCode", value.postcode),
9702
+ leaf("ram:LineOne", value.street),
9703
+ leaf("ram:LineTwo", value.street2),
9704
+ leaf("ram:LineThree", value.street3),
9705
+ leaf("ram:CityName", value.city),
9706
+ leaf("ram:CountryID", value.country),
9707
+ leaf("ram:CountrySubDivisionName", value.subdivision)
9708
+ ]);
9709
+ }
9710
+ function party2(name, value, full) {
9711
+ return group2(name, [
9712
+ identifier3("ram:ID", "ram:GlobalID", value.id),
9713
+ leaf("ram:Name", value.name),
9714
+ full ? leaf("ram:Description", value.legal_info) : void 0,
9715
+ group2("ram:SpecifiedLegalOrganization", [
9716
+ value.legal_registration ? leaf("ram:ID", value.legal_registration.value, {
9717
+ schemeID: value.legal_registration.scheme
9718
+ }) : void 0,
9719
+ leaf("ram:TradingBusinessName", value.trading_name)
9720
+ ]),
9721
+ full && value.contact ? group2("ram:DefinedTradeContact", [
9722
+ leaf("ram:PersonName", value.contact.name),
9723
+ group2("ram:TelephoneUniversalCommunication", [
9724
+ leaf("ram:CompleteNumber", value.contact.phone)
9725
+ ]),
9726
+ group2("ram:EmailURIUniversalCommunication", [
9727
+ leaf("ram:URIID", value.contact.email)
9728
+ ])
9729
+ ]) : void 0,
9730
+ address2(value.address),
9731
+ value.electronic_address ? group2("ram:URIUniversalCommunication", [
9732
+ leaf("ram:URIID", value.electronic_address.value, {
9733
+ schemeID: value.electronic_address.scheme
9734
+ })
9735
+ ]) : void 0,
9736
+ group2("ram:SpecifiedTaxRegistration", [
9737
+ leaf("ram:ID", value.vat_id, { schemeID: "VA" })
9738
+ ]),
9739
+ group2("ram:SpecifiedTaxRegistration", [
9740
+ leaf("ram:ID", value.tax_number, { schemeID: "FC" })
9741
+ ])
9742
+ ]);
9743
+ }
9744
+ var taxOf = (tax) => [
9745
+ leaf("ram:TypeCode", "VAT"),
9746
+ leaf("ram:CategoryCode", tax.category),
9747
+ leaf("ram:RateApplicablePercent", tax.rate)
9748
+ ];
9749
+ function allowanceOrCharge2(value, charge) {
9750
+ return group2("ram:SpecifiedTradeAllowanceCharge", [
9751
+ group2("ram:ChargeIndicator", [
9752
+ leaf("udt:Indicator", charge ? "true" : "false")
9753
+ ]),
9754
+ leaf("ram:CalculationPercent", value.percent),
9755
+ amount("ram:BasisAmount", value.base_amount),
9756
+ amount("ram:ActualAmount", value.amount),
9757
+ leaf("ram:ReasonCode", value.reason_code),
9758
+ leaf("ram:Reason", value.reason),
9759
+ value.tax ? group2("ram:CategoryTradeTax", taxOf(value.tax)) : void 0
9760
+ ]);
9761
+ }
9762
+ var period2 = (value) => value === void 0 ? void 0 : group2("ram:BillingSpecifiedPeriod", [
9763
+ date("ram:StartDateTime", value.start),
9764
+ date("ram:EndDateTime", value.end)
9765
+ ]);
9766
+ function invoiceToCii(invoice, options) {
9767
+ const full = options.profile === "en16931";
9768
+ const { totals: totals2, payment: payment2 } = invoice;
9769
+ const lines = invoice.lines.map(
9770
+ (line2) => group2("ram:IncludedSupplyChainTradeLineItem", [
9771
+ group2("ram:AssociatedDocumentLineDocument", [
9772
+ leaf("ram:LineID", line2.id),
9773
+ group2("ram:IncludedNote", [leaf("ram:Content", line2.note)])
9774
+ ]),
9775
+ group2("ram:SpecifiedTradeProduct", [
9776
+ line2.standard_item_id ? leaf("ram:GlobalID", line2.standard_item_id.value, {
9777
+ schemeID: line2.standard_item_id.scheme
9778
+ }) : void 0,
9779
+ full ? leaf("ram:SellerAssignedID", line2.seller_item_id) : void 0,
9780
+ full ? leaf("ram:BuyerAssignedID", line2.buyer_item_id) : void 0,
9781
+ leaf("ram:Name", line2.name),
9782
+ full ? leaf("ram:Description", line2.description) : void 0
9783
+ ]),
9784
+ group2("ram:SpecifiedLineTradeAgreement", [
9785
+ full ? group2("ram:BuyerOrderReferencedDocument", [
9786
+ leaf("ram:LineID", line2.order_line_reference)
9787
+ ]) : void 0,
9788
+ group2("ram:NetPriceProductTradePrice", [
9789
+ leaf("ram:ChargeAmount", withPlaces(line2.net_price, 2)),
9790
+ leaf("ram:BasisQuantity", line2.price_base_quantity, {
9791
+ unitCode: line2.unit_code
9792
+ })
9793
+ ])
9794
+ ]),
9795
+ group2("ram:SpecifiedLineTradeDelivery", [
9796
+ leaf("ram:BilledQuantity", line2.quantity, { unitCode: line2.unit_code })
9797
+ ]),
9798
+ group2("ram:SpecifiedLineTradeSettlement", [
9799
+ group2("ram:ApplicableTradeTax", taxOf(line2.tax)),
9800
+ period2(line2.period),
9801
+ ...line2.allowances.map((a) => allowanceOrCharge2(a, false)),
9802
+ ...line2.charges.map((c) => allowanceOrCharge2(c, true)),
9803
+ group2("ram:SpecifiedTradeSettlementLineMonetarySummation", [
9804
+ amount("ram:LineTotalAmount", line2.net_amount)
9805
+ ]),
9806
+ full ? group2("ram:ReceivableSpecifiedTradeAccountingAccount", [
9807
+ leaf("ram:ID", line2.buyer_accounting_reference)
9808
+ ]) : void 0
9809
+ ])
9810
+ ])
9811
+ );
9812
+ const agreement = group2("ram:ApplicableHeaderTradeAgreement", [
9813
+ leaf("ram:BuyerReference", invoice.buyer_reference),
9814
+ party2("ram:SellerTradeParty", invoice.seller, full),
9815
+ party2("ram:BuyerTradeParty", invoice.buyer, full),
9816
+ group2("ram:BuyerOrderReferencedDocument", [
9817
+ leaf("ram:IssuerAssignedID", invoice.order_reference)
9818
+ ]),
9819
+ group2("ram:ContractReferencedDocument", [
9820
+ leaf("ram:IssuerAssignedID", invoice.contract_reference)
9821
+ ])
9822
+ ]);
9823
+ const shipTo = invoice.delivery;
9824
+ const delivery = group2("ram:ApplicableHeaderTradeDelivery", [
9825
+ group2("ram:ShipToTradeParty", [
9826
+ leaf("ram:Name", shipTo?.name),
9827
+ address2(shipTo?.address)
9828
+ ]),
9829
+ group2("ram:ActualDeliverySupplyChainEvent", [
9830
+ date("ram:OccurrenceDateTime", shipTo?.date)
9831
+ ])
9832
+ ]) ?? { name: "ram:ApplicableHeaderTradeDelivery" };
9833
+ const settlement = group2("ram:ApplicableHeaderTradeSettlement", [
9834
+ leaf("ram:CreditorReferenceID", payment2?.creditor_id),
9835
+ leaf("ram:PaymentReference", payment2?.reference),
9836
+ leaf("ram:TaxCurrencyCode", invoice.accounting_currency),
9837
+ leaf("ram:InvoiceCurrencyCode", invoice.currency),
9838
+ payment2?.means_code ? group2("ram:SpecifiedTradeSettlementPaymentMeans", [
9839
+ leaf("ram:TypeCode", payment2.means_code),
9840
+ group2("ram:PayerPartyDebtorFinancialAccount", [
9841
+ leaf("ram:IBANID", payment2.debited_iban)
9842
+ ]),
9843
+ group2("ram:PayeePartyCreditorFinancialAccount", [
9844
+ leaf("ram:IBANID", payment2.iban),
9845
+ full ? leaf("ram:AccountName", payment2.account_name) : void 0
9846
+ ]),
9847
+ full ? group2("ram:PayeeSpecifiedCreditorFinancialInstitution", [
9848
+ leaf("ram:BICID", payment2.bic)
9849
+ ]) : void 0
9850
+ ]) : void 0,
9851
+ ...invoice.breakdown.map(
9852
+ (row2) => group2("ram:ApplicableTradeTax", [
9853
+ amount("ram:CalculatedAmount", row2.amount),
9854
+ leaf("ram:TypeCode", "VAT"),
9855
+ leaf("ram:ExemptionReason", row2.exemption_reason),
9856
+ amount("ram:BasisAmount", row2.basis),
9857
+ leaf("ram:CategoryCode", row2.category),
9858
+ leaf("ram:ExemptionReasonCode", row2.exemption_reason_code),
9859
+ // France: VAT owed on invoicing rather than on payment is the code for "date of the
9860
+ // invoice" (BT-8). Nothing is written for the default, which the specifications leave unsaid.
9861
+ invoice.seller.vat_on_debits ? leaf("ram:DueDateTypeCode", "5") : void 0,
9862
+ leaf("ram:RateApplicablePercent", row2.rate)
9863
+ ])
9864
+ ),
9865
+ period2(invoice.period),
9866
+ ...invoice.allowances.map((a) => allowanceOrCharge2(a, false)),
9867
+ ...invoice.charges.map((c) => allowanceOrCharge2(c, true)),
9868
+ group2("ram:SpecifiedTradePaymentTerms", [
9869
+ leaf("ram:Description", payment2?.terms),
9870
+ date("ram:DueDateDateTime", payment2?.due_date),
9871
+ leaf("ram:DirectDebitMandateID", payment2?.mandate_id)
9872
+ ]),
9873
+ group2("ram:SpecifiedTradeSettlementHeaderMonetarySummation", [
9874
+ amount("ram:LineTotalAmount", totals2.line_net),
9875
+ amount("ram:ChargeTotalAmount", totals2.charges),
9876
+ amount("ram:AllowanceTotalAmount", totals2.allowances),
9877
+ amount("ram:TaxBasisTotalAmount", totals2.tax_basis),
9878
+ amount("ram:TaxTotalAmount", totals2.tax_total, {
9879
+ currencyID: invoice.currency
9880
+ }),
9881
+ invoice.accounting_currency ? amount("ram:TaxTotalAmount", totals2.tax_total_accounting, {
9882
+ currencyID: invoice.accounting_currency
9883
+ }) : void 0,
9884
+ full ? amount("ram:RoundingAmount", totals2.rounding) : void 0,
9885
+ amount("ram:GrandTotalAmount", totals2.grand),
9886
+ amount("ram:TotalPrepaidAmount", totals2.prepaid),
9887
+ amount("ram:DuePayableAmount", totals2.due)
9888
+ ]),
9889
+ ...invoice.preceding_invoices.map(
9890
+ (reference) => group2("ram:InvoiceReferencedDocument", [
9891
+ leaf("ram:IssuerAssignedID", reference.number),
9892
+ reference.issue_date ? group2("ram:FormattedIssueDateTime", [
9893
+ leaf(
9894
+ "qdt:DateTimeString",
9895
+ reference.issue_date.replace(/-/g, ""),
9896
+ { format: "102" }
9897
+ )
9898
+ ]) : void 0
9899
+ ])
9900
+ )
9901
+ ]);
9902
+ const root = {
9903
+ name: "rsm:CrossIndustryInvoice",
9904
+ attributes: {
9905
+ "xmlns:rsm": "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100",
9906
+ "xmlns:qdt": "urn:un:unece:uncefact:data:standard:QualifiedDataType:100",
9907
+ "xmlns:ram": "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100",
9908
+ "xmlns:udt": "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
9909
+ },
9910
+ children: [
9911
+ group2("rsm:ExchangedDocumentContext", [
9912
+ group2("ram:BusinessProcessSpecifiedDocumentContextParameter", [
9913
+ leaf("ram:ID", invoice.business_process)
9914
+ ]),
9915
+ group2("ram:GuidelineSpecifiedDocumentContextParameter", [
9916
+ leaf("ram:ID", CII_GUIDELINES[options.profile])
9917
+ ])
9918
+ ]),
9919
+ group2("rsm:ExchangedDocument", [
9920
+ leaf("ram:ID", invoice.number),
9921
+ leaf("ram:TypeCode", invoice.type_code),
9922
+ date("ram:IssueDateTime", invoice.issue_date),
9923
+ ...invoice.notes.map(
9924
+ (note2) => group2("ram:IncludedNote", [
9925
+ leaf("ram:Content", note2.text),
9926
+ leaf("ram:SubjectCode", note2.subject_code)
9927
+ ])
9928
+ )
9929
+ ]),
9930
+ group2("rsm:SupplyChainTradeTransaction", [
9931
+ ...lines,
9932
+ agreement,
9933
+ delivery,
9934
+ settlement
9935
+ ])
9936
+ ].filter((child2) => !!child2)
9937
+ };
9938
+ return `<?xml version="1.0" encoding="UTF-8"?>
9939
+ ${serialise(root, 0)}`;
9940
+ }
9941
+ var xmlCharacter = (code) => code === 9 || code === 10 || code === 13 || code >= 32 && !(code >= 55296 && code <= 57343) && code !== 65534 && code !== 65535;
9942
+ function carried(text4) {
9943
+ let out = "";
9944
+ for (const character of text4)
9945
+ if (xmlCharacter(character.codePointAt(0) ?? 0)) out += character;
9946
+ return out;
9947
+ }
9948
+ var escapeText2 = (text4) => carried(text4).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
9949
+ var escapeAttribute2 = (text4) => escapeText2(text4).replace(/"/g, "&quot;");
9950
+ function serialise(element, depth) {
9951
+ const indent = " ".repeat(depth);
9952
+ const attributes = Object.entries(element.attributes ?? {}).map(([name, value]) => ` ${name}="${escapeAttribute2(value)}"`).join("");
9953
+ if (element.children === void 0) {
9954
+ return element.text === void 0 ? `${indent}<${element.name}${attributes}/>
9955
+ ` : `${indent}<${element.name}${attributes}>${escapeText2(element.text)}</${element.name}>
9956
+ `;
9957
+ }
9958
+ const children = element.children.map((child2) => serialise(child2, depth + 1)).join("");
9959
+ return `${indent}<${element.name}${attributes}>
9960
+ ${children}${indent}</${element.name}>
9961
+ `;
9962
+ }
9963
+
9964
+ // packages/engine/src/lib/einvoice/display.ts
9965
+ var DISPLAY_TERMS = {
9966
+ "BT-1": "invoice number",
9967
+ "BT-109": "net total",
9968
+ "BT-110": "VAT total",
9969
+ "BT-112": "total",
9970
+ "BT-115": "amount due",
9971
+ "BT-117": "VAT amount"
9972
+ };
9973
+ var DIGIT_0 = 48;
9974
+ var DIGIT_9 = 57;
9975
+ var DOT = 46;
9976
+ var COMMA = 44;
9977
+ var SPACES = /* @__PURE__ */ new Set([32, 160, 8239, 8201]);
9978
+ var APOSTROPHES = /* @__PURE__ */ new Set([39, 8217]);
9979
+ var DASHES = /* @__PURE__ */ new Set([45, 8208, 8209, 8210, 8211, 8722]);
9980
+ var INVISIBLE = /* @__PURE__ */ new Set([173, 8203, 8204, 8205, 8288, 65279]);
9981
+ var isDigit = (code) => code >= DIGIT_0 && code <= DIGIT_9;
9982
+ var MAX_GROUPS = 6;
9983
+ function shownAmounts(text4) {
9984
+ const found = /* @__PURE__ */ new Set();
9985
+ let run2 = "";
9986
+ const flush = () => {
9987
+ const trimmed = run2.replace(/[^0-9]+$/, "");
9988
+ run2 = "";
9989
+ if (!trimmed) return;
9990
+ const parts = trimmed.split(" ").map((part) => part.replace(/[.,]+$/, "")).filter((part) => part !== "");
9991
+ for (let start = 0; start < parts.length; start += 1) {
9992
+ for (const cents2 of readings(parts[start] ?? "")) found.add(cents2);
9993
+ const first = parts[start] ?? "";
9994
+ if (!/^\d{1,3}$/.test(first)) continue;
9995
+ let whole = first;
9996
+ for (let end = start + 1; end < parts.length && end - start < MAX_GROUPS; end += 1) {
9997
+ const part = parts[end] ?? "";
9998
+ const last = /^(\d{3})(?:[.,](\d+))?$/.exec(part);
9999
+ if (!last) break;
10000
+ const cents2 = toCents(whole + last[1], last[2] ?? "");
10001
+ if (cents2 !== null) found.add(cents2);
10002
+ if (last[2] !== void 0) break;
10003
+ whole += last[1];
10004
+ }
10005
+ }
10006
+ };
10007
+ for (const character of text4) {
10008
+ const code = character.codePointAt(0) ?? 0;
10009
+ if (INVISIBLE.has(code)) continue;
10010
+ if (isDigit(code)) run2 += character;
10011
+ else if (run2 && (code === DOT || code === COMMA)) run2 += character;
10012
+ else if (run2 && SPACES.has(code)) run2 += " ";
10013
+ else if (run2 && APOSTROPHES.has(code)) continue;
10014
+ else flush();
10015
+ }
10016
+ flush();
10017
+ return found;
10018
+ }
10019
+ function readings(part) {
10020
+ if (/^\d+$/.test(part)) {
10021
+ const cents2 = toCents(part, "");
10022
+ return cents2 === null ? [] : [cents2];
10023
+ }
10024
+ const out = [];
10025
+ for (const [group3, decimal] of [
10026
+ [".", ","],
10027
+ [",", "."]
10028
+ ]) {
10029
+ const pieces = part.split(decimal);
10030
+ if (pieces.length > 2) continue;
10031
+ const [whole = "", fraction] = pieces;
10032
+ if (fraction !== void 0 && !/^\d+$/.test(fraction)) continue;
10033
+ const groups = whole.split(group3);
10034
+ const grouped = groups.every(
10035
+ (digits, index) => /^\d+$/.test(digits) && (index === 0 ? groups.length === 1 || digits.length <= 3 : digits.length === 3)
10036
+ );
10037
+ if (!grouped) continue;
10038
+ const cents2 = toCents(groups.join(""), fraction ?? "");
10039
+ if (cents2 !== null) out.push(cents2);
10040
+ }
10041
+ return out;
10042
+ }
10043
+ function toCents(whole, fraction) {
10044
+ if (whole.length > 18 || /[1-9]/.test(fraction.slice(2))) return null;
10045
+ return BigInt(whole) * 100n + BigInt((fraction + "00").slice(0, 2));
10046
+ }
10047
+ function squeeze(text4) {
10048
+ let out = "";
10049
+ for (const character of text4) {
10050
+ const code = character.codePointAt(0) ?? 0;
10051
+ if (INVISIBLE.has(code) || /\s/.test(character) || SPACES.has(code))
10052
+ continue;
10053
+ out += DASHES.has(code) ? "-" : character.toLowerCase();
10054
+ }
10055
+ return out;
10056
+ }
10057
+ function displayCheck(text4, invoice) {
10058
+ const whole = typeof text4 === "string" ? text4 : text4.join("\n");
10059
+ const amounts = shownAmounts(whole);
10060
+ const squeezed = squeeze(whole);
10061
+ const values = [];
10062
+ values.push({
10063
+ term: "BT-1",
10064
+ path: "number",
10065
+ value: invoice.number,
10066
+ shown: squeezed.includes(squeeze(invoice.number))
10067
+ });
10068
+ const amount2 = (term, path, value) => {
10069
+ const cents2 = abs(toUnits(value, 2));
10070
+ if (cents2 === 0n) return;
10071
+ values.push({
10072
+ term,
10073
+ path,
10074
+ // as the XML writes it and a reader expects it: `3760.40`, not `3760.4`
10075
+ value: withPlaces(value, 2),
10076
+ shown: amounts.has(cents2)
10077
+ });
10078
+ };
10079
+ const { totals: totals2 } = invoice;
10080
+ amount2("BT-109", "totals.tax_basis", totals2.tax_basis);
10081
+ amount2("BT-110", "totals.tax_total", totals2.tax_total);
10082
+ amount2("BT-112", "totals.grand", totals2.grand);
10083
+ if (toUnits(totals2.due, 2) !== toUnits(totals2.grand, 2))
10084
+ amount2("BT-115", "totals.due", totals2.due);
10085
+ if (invoice.breakdown.length > 1)
10086
+ invoice.breakdown.forEach(
10087
+ (row2, index) => amount2("BT-117", `tax.breakdown[${index}].amount`, row2.amount)
10088
+ );
10089
+ const unique = (terms) => [...new Set(terms)];
10090
+ return {
10091
+ checked: unique(values.map((value) => value.term)),
10092
+ missing: unique(
10093
+ values.filter((value) => !value.shown).map((value) => value.term)
10094
+ ),
10095
+ values
10096
+ };
10097
+ }
10098
+ function displayMissingText(check) {
10099
+ const missing = check.values.filter((value) => !value.shown);
10100
+ if (missing.length === 0) return null;
10101
+ const named = missing.map(
10102
+ (value) => `the ${DISPLAY_TERMS[value.term] ?? value.term} ${value.value} (${value.term})`
10103
+ );
10104
+ const list3 = named.length === 1 ? named[0] : `${named.slice(0, -1).join(", ")} and ${named[named.length - 1]}`;
10105
+ return `The PDF does not show ${list3} of the e-invoice`;
10106
+ }
10107
+
10108
+ // packages/engine/src/lib/einvoice/locate.ts
10109
+ function invoicePathSegments(path) {
10110
+ const segments = [];
10111
+ for (const part of path.split(".")) {
10112
+ if (!part) continue;
10113
+ const match = /^([^[\]]*)((?:\[\d+\])*)$/.exec(part);
10114
+ if (!match) {
10115
+ segments.push(part);
10116
+ continue;
10117
+ }
10118
+ if (match[1]) segments.push(match[1]);
10119
+ for (const index of match[2]?.matchAll(/\[(\d+)\]/g) ?? [])
10120
+ segments.push(Number(index[1]));
10121
+ }
10122
+ return segments;
10123
+ }
10124
+ function locateJsonPath(text4, path) {
10125
+ let offset = skipSpace(text4, 0);
10126
+ if (offset >= text4.length) return null;
10127
+ let best = { offset, depth: 0 };
10128
+ try {
10129
+ for (let depth = 0; depth < path.length; depth += 1) {
10130
+ const segment = path[depth];
10131
+ const found = typeof segment === "number" ? arrayItem(text4, offset, segment) : objectMember(text4, offset, String(segment));
10132
+ if (!found) break;
10133
+ best = { offset: found.at, depth: depth + 1 };
10134
+ offset = found.value;
10135
+ }
10136
+ } catch {
10137
+ }
10138
+ return { ...positionAt(text4, best.offset), depth: best.depth };
10139
+ }
10140
+ function positionAt(text4, offset) {
10141
+ let line2 = 1;
10142
+ let lineStart = 0;
10143
+ for (let i = 0; i < offset; i += 1)
10144
+ if (text4.charCodeAt(i) === 10) {
10145
+ line2 += 1;
10146
+ lineStart = i + 1;
10147
+ }
10148
+ return { line: line2, column: offset - lineStart + 1 };
10149
+ }
10150
+ function skipSpace(text4, from) {
10151
+ let i = from;
10152
+ while (i < text4.length && /\s/.test(text4[i] ?? "")) i += 1;
10153
+ return i;
10154
+ }
10155
+ function stringEnd(text4, from) {
10156
+ if (text4[from] !== '"') throw new Error("string");
10157
+ for (let i = from + 1; i < text4.length; i += 1) {
10158
+ if (text4[i] === "\\") i += 1;
10159
+ else if (text4[i] === '"') return i + 1;
10160
+ }
10161
+ throw new Error("string");
10162
+ }
10163
+ function valueEnd(text4, from) {
10164
+ const start = skipSpace(text4, from);
10165
+ const first = text4[start];
10166
+ if (first === '"') return stringEnd(text4, start);
10167
+ if (first === "{" || first === "[") {
10168
+ const close = first === "{" ? "}" : "]";
10169
+ let i2 = skipSpace(text4, start + 1);
10170
+ if (text4[i2] === close) return i2 + 1;
10171
+ for (; ; ) {
10172
+ if (first === "{") {
10173
+ i2 = skipSpace(text4, stringEnd(text4, skipSpace(text4, i2)));
10174
+ if (text4[i2] !== ":") throw new Error("colon");
10175
+ i2 += 1;
10176
+ }
10177
+ i2 = skipSpace(text4, valueEnd(text4, i2));
10178
+ if (text4[i2] === close) return i2 + 1;
10179
+ if (text4[i2] !== ",") throw new Error("comma");
10180
+ i2 += 1;
10181
+ }
10182
+ }
10183
+ let i = start;
10184
+ while (i < text4.length && !/[\s,\]}]/.test(text4[i] ?? "")) i += 1;
10185
+ if (i === start) throw new Error("value");
10186
+ return i;
10187
+ }
10188
+ function objectMember(text4, from, key) {
10189
+ const start = skipSpace(text4, from);
10190
+ if (text4[start] !== "{") return null;
10191
+ let i = skipSpace(text4, start + 1);
10192
+ while (text4[i] !== "}") {
10193
+ const at = skipSpace(text4, i);
10194
+ const end = stringEnd(text4, at);
10195
+ const colon = skipSpace(text4, end);
10196
+ if (text4[colon] !== ":") throw new Error("colon");
10197
+ const value = skipSpace(text4, colon + 1);
10198
+ if (JSON.parse(text4.slice(at, end)) === key) return { at, value };
10199
+ i = skipSpace(text4, valueEnd(text4, value));
10200
+ if (text4[i] === ",") i = skipSpace(text4, i + 1);
10201
+ else if (text4[i] !== "}") throw new Error("comma");
10202
+ }
10203
+ return null;
10204
+ }
10205
+ function arrayItem(text4, from, index) {
10206
+ const start = skipSpace(text4, from);
10207
+ if (text4[start] !== "[") return null;
10208
+ let i = skipSpace(text4, start + 1);
10209
+ for (let n = 0; text4[i] !== "]"; n += 1) {
10210
+ const at = skipSpace(text4, i);
10211
+ if (n === index) return { at, value: at };
10212
+ i = skipSpace(text4, valueEnd(text4, at));
10213
+ if (text4[i] === ",") i = skipSpace(text4, i + 1);
10214
+ else if (text4[i] !== "]") throw new Error("comma");
10215
+ }
10216
+ return null;
10217
+ }
10218
+
10219
+ // packages/engine/src/lib/einvoice/report.ts
10220
+ var EINVOICE_MAX_MESSAGES = 50;
10221
+ function capMessages(messages, max2 = EINVOICE_MAX_MESSAGES) {
10222
+ const sorted = [
10223
+ ...messages.filter((message) => message.severity === "error"),
10224
+ ...messages.filter((message) => message.severity !== "error")
10225
+ ];
10226
+ return sorted.length > max2 ? { messages: sorted.slice(0, max2), truncated: true } : { messages: sorted };
10227
+ }
10228
+ function einvoiceResult(options, specVersion, validation, display) {
10229
+ return {
10230
+ profile: options.profile,
10231
+ flavour: options.flavour,
10232
+ spec_version: specVersion,
10233
+ validation: validation ? {
10234
+ valid: validation.valid,
10235
+ schematron: validation.schematron,
10236
+ pdfa: validation.pdfa,
10237
+ ...capMessages(validation.messages)
10238
+ } : null,
10239
+ display: display ? { checked: display.checked, missing: display.missing } : null
10240
+ };
10241
+ }
10242
+ var EINVOICE_FLAVOUR_NAMES = {
10243
+ "factur-x": "Factur-X",
10244
+ zugferd: "ZUGFeRD"
10245
+ };
10246
+ var EINVOICE_PROFILE_NAMES = {
10247
+ basic: "Basic",
10248
+ en16931: "EN 16931"
10249
+ };
10250
+ function invoiceLineOf(location) {
10251
+ if (!location) return null;
10252
+ const match = /IncludedSupplyChainTradeLineItem\[(\d+)\]/.exec(location);
10253
+ return match ? Number(match[1]) : null;
10254
+ }
10255
+
10256
+ // packages/engine/src/lib/einvoice/html-text.ts
10257
+ var INLINE = /* @__PURE__ */ new Set([
10258
+ "a",
10259
+ "abbr",
10260
+ "b",
10261
+ "bdi",
10262
+ "bdo",
10263
+ "cite",
10264
+ "code",
10265
+ "data",
10266
+ "del",
10267
+ "dfn",
10268
+ "em",
10269
+ "font",
10270
+ "i",
10271
+ "ins",
10272
+ "kbd",
10273
+ "label",
10274
+ "mark",
10275
+ "nobr",
10276
+ "output",
10277
+ "q",
10278
+ "s",
10279
+ "samp",
10280
+ "small",
10281
+ "span",
10282
+ "strong",
10283
+ "sub",
10284
+ "sup",
10285
+ "time",
10286
+ "tt",
10287
+ "u",
10288
+ "var",
10289
+ "wbr"
10290
+ ]);
10291
+ var SKIPPED = ["script", "style", "template", "noscript", "head", "title"];
10292
+ var ENTITIES = {
10293
+ amp: 38,
10294
+ lt: 60,
10295
+ gt: 62,
10296
+ quot: 34,
10297
+ apos: 39,
10298
+ nbsp: 160,
10299
+ ensp: 8194,
10300
+ emsp: 8195,
10301
+ thinsp: 8201,
10302
+ shy: 173,
10303
+ minus: 8722,
10304
+ ndash: 8211,
10305
+ mdash: 8212,
10306
+ hyphen: 8208,
10307
+ euro: 8364,
10308
+ pound: 163,
10309
+ yen: 165,
10310
+ cent: 162,
10311
+ sect: 167,
10312
+ middot: 183,
10313
+ bull: 8226,
10314
+ times: 215,
10315
+ auml: 228,
10316
+ ouml: 246,
10317
+ uuml: 252,
10318
+ Auml: 196,
10319
+ Ouml: 214,
10320
+ Uuml: 220,
10321
+ szlig: 223,
10322
+ eacute: 233,
10323
+ egrave: 232,
10324
+ agrave: 224,
10325
+ ccedil: 231
10326
+ };
10327
+ function decodeEntities(text4) {
10328
+ return text4.replace(
10329
+ /&(#x[0-9a-f]+|#\d+|[a-z][a-z0-9]*);/gi,
10330
+ (whole, reference) => {
10331
+ const code = reference.startsWith("#") ? reference[1] === "x" || reference[1] === "X" ? Number.parseInt(reference.slice(2), 16) : Number.parseInt(reference.slice(1), 10) : ENTITIES[reference];
10332
+ return code !== void 0 && Number.isFinite(code) && code > 0 && code <= 1114111 ? String.fromCodePoint(code) : whole;
10333
+ }
10334
+ );
10335
+ }
10336
+ function htmlText(html) {
10337
+ let source = html.replace(/<!--[\s\S]*?-->/g, "");
10338
+ for (const tag2 of SKIPPED)
10339
+ source = source.replace(
10340
+ new RegExp(`<${tag2}\\b[^>]*>[\\s\\S]*?</${tag2}\\s*>`, "gi"),
10341
+ "\n"
10342
+ );
10343
+ let out = "";
10344
+ let position = 0;
10345
+ const tag = /<\/?([a-zA-Z][a-zA-Z0-9-]*)(?:"[^"]*"|'[^']*'|[^'">])*>/g;
10346
+ for (let match = tag.exec(source); match; match = tag.exec(source)) {
10347
+ out += source.slice(position, match.index);
10348
+ position = match.index + match[0].length;
10349
+ const name = (match[1] ?? "").toLowerCase();
10350
+ if (name === "br" || !INLINE.has(name)) out += "\n";
10351
+ }
10352
+ out += source.slice(position);
10353
+ return decodeEntities(out).split("\n").map((line2) => line2.replace(/[ \t\r\f]+/g, " ").trim()).filter((line2) => line2 !== "").join("\n");
10354
+ }
10355
+
10356
+ // packages/engine/src/lib/einvoice/skeleton.ts
10357
+ function invoiceSkeleton(issueDate = "2026-01-15") {
10358
+ const due = /* @__PURE__ */ new Date(`${issueDate}T00:00:00Z`);
10359
+ due.setUTCDate(due.getUTCDate() + 14);
10360
+ const number = `RE-${issueDate.slice(0, 4)}-0001`;
10361
+ return {
10362
+ number,
10363
+ issue_date: issueDate,
10364
+ type_code: "380",
10365
+ currency: "EUR",
10366
+ seller: {
10367
+ name: "Fennlor Studio GmbH",
10368
+ vat_id: "DE000000000",
10369
+ address: {
10370
+ street: "Musterstra\xDFe 1",
10371
+ postcode: "12345",
10372
+ city: "Musterstadt",
10373
+ country: "DE"
10374
+ },
10375
+ contact: {
10376
+ name: "Erika Mustermann",
10377
+ email: "rechnung@fennlor.example",
10378
+ phone: "+49 30 0000000"
10379
+ }
10380
+ },
10381
+ buyer: {
10382
+ name: "Olvarest GmbH",
10383
+ address: {
10384
+ street: "Beispielweg 2",
10385
+ postcode: "54321",
10386
+ city: "Beispielstadt",
10387
+ country: "DE"
10388
+ }
10389
+ },
10390
+ delivery: { date: issueDate },
10391
+ lines: [
10392
+ {
10393
+ id: "1",
10394
+ name: "Consulting",
10395
+ quantity: 8,
10396
+ unit_code: "HUR",
10397
+ net_price: 120,
10398
+ net_amount: 960,
10399
+ tax: { category: "S", rate: 19 }
10400
+ }
10401
+ ],
10402
+ tax: {
10403
+ breakdown: [{ category: "S", rate: 19, basis: 960, amount: 182.4 }]
10404
+ },
10405
+ totals: {
10406
+ line_net: 960,
10407
+ tax_basis: 960,
10408
+ tax_total: 182.4,
10409
+ grand: 1142.4,
10410
+ due: 1142.4
10411
+ },
10412
+ payment: {
10413
+ means_code: "58",
10414
+ iban: "DE36000000000000000000",
10415
+ reference: number,
10416
+ terms: "Zahlbar innerhalb von 14 Tagen ohne Abzug",
10417
+ due_date: due.toISOString().slice(0, 10)
10418
+ }
10419
+ };
10420
+ }
10421
+
10422
+ // packages/engine/src/lib/einvoice/index.ts
10423
+ var INVOICE_DATA_KEY = "_invoice";
10424
+ var EINVOICE_SPEC_VERSIONS = {
10425
+ "factur-x": "1.09",
10426
+ zugferd: "2.5"
10427
+ };
10428
+ function invoiceAmountText(amount2) {
10429
+ return withPlaces(amount2, 2);
10430
+ }
10431
+ function einvoiceXml(data, options) {
10432
+ const block = data !== null && typeof data === "object" ? data[INVOICE_DATA_KEY] : void 0;
10433
+ if (block === void 0 || block === null)
10434
+ return {
10435
+ ok: false,
10436
+ problems: [
10437
+ {
10438
+ code: "missing",
10439
+ path: "",
10440
+ message: "The data has no _invoice block: an e-invoice is built from it",
10441
+ args: { path: "" }
10442
+ }
10443
+ ]
10444
+ };
10445
+ const check = checkInvoice(block, options);
10446
+ if (!check.ok) return { ok: false, problems: check.problems };
10447
+ return {
10448
+ ok: true,
10449
+ xml: invoiceToCii(check.invoice, options),
10450
+ invoice: check.invoice
10451
+ };
10452
+ }
10453
+
10454
+ // packages/engine/src/lib/accessibility/index.ts
10455
+ var PDFUA_STANDARD = "PDF/UA-1";
10456
+ var UA_CHECKS = ["strict", "report"];
10457
+ function resolveUa(setting) {
10458
+ if (setting === true) return { check: "strict" };
10459
+ if (!setting || typeof setting !== "object" || Array.isArray(setting))
10460
+ return null;
10461
+ const check = setting.check;
10462
+ return { check: check === "report" ? "report" : "strict" };
10463
+ }
10464
+ var UA_REFUSALS = {
10465
+ office: "pdf.ua is not available for Word and PowerPoint templates yet",
10466
+ merge: "pdf.ua cannot be combined with post.merge_after: the tags of merged documents do not survive the merge"
10467
+ };
10468
+ function isUaSetting(value) {
10469
+ if (value === null || typeof value === "boolean") return true;
10470
+ if (typeof value !== "object" || Array.isArray(value)) return false;
10471
+ const entries = Object.entries(value);
10472
+ return entries.every(
10473
+ ([key, entry]) => key === "check" && UA_CHECKS.includes(entry)
10474
+ );
10475
+ }
10476
+ var AUDIT_CODES = [
10477
+ "document-title",
10478
+ "image-alt",
10479
+ "figure-name",
10480
+ "heading-order",
10481
+ "table-rows",
10482
+ "table-cell-empty",
10483
+ "masked-text",
10484
+ "filtered-text",
10485
+ "document-language",
10486
+ "table-headers",
10487
+ "link-name",
10488
+ "generated-text",
10489
+ "fixed-text",
10490
+ "running-text"
10491
+ ];
10492
+ var AUDIT_SEVERITY = {
10493
+ "document-title": "error",
10494
+ "image-alt": "error",
10495
+ "figure-name": "error",
10496
+ "heading-order": "error",
10497
+ "table-rows": "error",
10498
+ "table-cell-empty": "error",
10499
+ "masked-text": "error",
10500
+ "filtered-text": "warning",
10501
+ "document-language": "warning",
10502
+ "table-headers": "warning",
10503
+ "link-name": "warning",
10504
+ "generated-text": "warning",
10505
+ "fixed-text": "warning",
10506
+ "running-text": "warning"
10507
+ };
10508
+ var AUDIT_RULES = {
10509
+ "document-title": ["7.1-9", "7.1-10"],
10510
+ "image-alt": ["7.3-1"],
10511
+ "figure-name": ["7.3-1"],
10512
+ "heading-order": ["7.4.2-1"],
10513
+ "table-rows": ["7.2-43"],
10514
+ "table-cell-empty": ["7.2-43"],
10515
+ "masked-text": ["7.20-2"],
10516
+ "filtered-text": [],
10517
+ "document-language": [],
10518
+ "table-headers": [],
10519
+ "link-name": [],
10520
+ "generated-text": [],
10521
+ "fixed-text": [],
10522
+ "running-text": []
10523
+ };
10524
+ var AUDIT_TITLES = {
10525
+ "document-title": "No document title",
10526
+ "image-alt": "Image without an alt attribute",
10527
+ "figure-name": "Figure without a name",
10528
+ "heading-order": "Heading level skipped",
10529
+ "table-rows": "Table rows of different lengths",
10530
+ "table-cell-empty": "Empty table cell",
10531
+ "masked-text": "Text under a CSS mask",
10532
+ "filtered-text": "Text under a CSS filter",
10533
+ "document-language": "No language stated",
10534
+ "table-headers": "Table without header cells",
10535
+ "link-name": "Link without a name",
10536
+ "generated-text": "Text generated by CSS",
10537
+ "fixed-text": "Fixed text on every page",
10538
+ "running-text": "Only in the header or footer"
10539
+ };
10540
+ var AUDIT_SNIPPET_LENGTH = 120;
10541
+ var text3 = (value) => String(value ?? "");
10542
+ function auditMessage(finding2) {
10543
+ const args = finding2.args;
10544
+ switch (finding2.code) {
10545
+ case "document-title":
10546
+ return "The document has no title. A PDF viewer shows the title in its title bar and a screen reader says it first: set pdf.metadata.title, or give the page a <title>.";
10547
+ case "image-alt":
10548
+ return 'The image has no alt attribute. Say what it shows, or write alt="" if it is decoration.';
10549
+ case "figure-name":
10550
+ return `The ${text3(args["tag"])} has no text alternative. Give it an aria-label that says what it shows; the chart helper takes alt.`;
10551
+ case "heading-order":
10552
+ return args["previous"] === 0 ? `The first heading is an h${text3(args["level"])}. Start with an h1 and size it with CSS.` : `This h${text3(args["level"])} follows an h${text3(args["previous"])}, so a heading level is skipped. Make it an h${Number(args["previous"]) + 1} and size it with CSS.`;
10553
+ case "table-rows":
10554
+ return `The rows of this table span different numbers of columns (${text3(args["first"])} and ${text3(args["other"])}). Even them out with colspan.`;
10555
+ case "table-cell-empty":
10556
+ return Number(args["count"]) > 1 ? `${text3(args["count"])} cells of this table are empty. Chromium writes no cell for an empty td, so their rows come out shorter than the others: put a non-breaking space (&nbsp;) or a dash into each.` : "This table cell is empty. Chromium writes no cell for an empty td, so its row comes out shorter than the others: put a non-breaking space (&nbsp;) or a dash into it.";
10557
+ case "masked-text":
10558
+ return "This element has a CSS mask and holds text. Chromium then writes the text in a way PDF/UA-1 does not allow, and no repair reaches it: mask an element without text instead.";
10559
+ case "filtered-text":
10560
+ return "This element has a CSS filter and holds text. The filter turns the text into a picture, which nobody can have read out or select.";
10561
+ case "document-language":
10562
+ return "The document names no language. Set lang on the html element, or the locale in the settings.";
10563
+ case "table-headers":
10564
+ return "The table has no header cells. Use th for the titles of its columns or rows, or a screen reader says the cells without them.";
10565
+ case "link-name":
10566
+ return "The link has no text. A screen reader says its address instead: give it text, an aria-label, or an image with alt.";
10567
+ case "generated-text":
10568
+ return `\u201C${text3(args["text"])}\u201D comes from CSS (::before or ::after). Generated text is not read out: write it into the template.`;
10569
+ case "fixed-text":
10570
+ return "This element is position: fixed, so it is printed on every page and read once per page, one after the other.";
10571
+ case "running-text":
10572
+ return `\u201C${text3(args["text"])}\u201D stands only in the ${text3(args["part"])}. A screen reader skips page headers and footers, so say it in the body too.`;
10573
+ }
10574
+ }
10575
+ var ACCESSIBILITY_MAX_FAILURES = 20;
10576
+ var ACCESSIBILITY_MAX_ELEMENTS = 5;
10577
+ var elementOf = (finding2) => finding2.code === "running-text" || finding2.code === "generated-text" ? text3(finding2.args["text"]) : finding2.snippet;
10578
+ function accessibilityResult(options, verdict, findings) {
10579
+ const failures = verdict.failures.map((failure) => {
10580
+ const about = findings.filter(
10581
+ (finding2) => AUDIT_RULES[finding2.code].includes(failure.rule)
10582
+ );
10583
+ const first = about[0];
10584
+ return {
10585
+ rule: failure.rule,
10586
+ ...first ? { code: first.code } : {},
10587
+ count: failure.checks,
10588
+ message: first ? auditMessage(first) : failure.description.replace(/\s+/g, " ").trim(),
10589
+ elements: [...new Set(about.map(elementOf).filter(Boolean))].slice(
10590
+ 0,
10591
+ ACCESSIBILITY_MAX_ELEMENTS
10592
+ )
10593
+ };
10594
+ });
10595
+ const warnings = [];
10596
+ for (const finding2 of findings) {
10597
+ if (finding2.severity !== "warning") continue;
10598
+ let warning = warnings.find((entry) => entry.code === finding2.code);
10599
+ if (!warning)
10600
+ warnings.push(warning = { code: finding2.code, count: 0, elements: [] });
10601
+ warning.count++;
10602
+ const element = elementOf(finding2);
10603
+ if (element && warning.elements.length < ACCESSIBILITY_MAX_ELEMENTS && !warning.elements.includes(element))
10604
+ warning.elements.push(element);
10605
+ }
10606
+ return {
10607
+ standard: PDFUA_STANDARD,
10608
+ check: options.check,
10609
+ conformant: verdict.compliant,
10610
+ validator: verdict.validator,
10611
+ rules: verdict.rules,
10612
+ failures: failures.slice(0, ACCESSIBILITY_MAX_FAILURES),
10613
+ warnings,
10614
+ ...failures.length > ACCESSIBILITY_MAX_FAILURES ? { truncated: true } : {}
10615
+ };
10616
+ }
10617
+ function accessibilityHeader(result) {
10618
+ return result.conformant ? "conformant" : "not-conformant";
10619
+ }
10620
+ var ACCESSIBILITY_HEADER_LENGTH = 8e3;
10621
+ function accessibilityHeaderValue(result, limit = ACCESSIBILITY_HEADER_LENGTH) {
10622
+ const encode = (report2) => encodeURIComponent(JSON.stringify(report2));
10623
+ const keep = (report2, elements) => ({
10624
+ ...report2,
10625
+ truncated: true,
10626
+ failures: report2.failures.map((failure) => ({
10627
+ ...failure,
10628
+ elements: failure.elements.slice(0, elements)
10629
+ })),
10630
+ warnings: report2.warnings.map((warning) => ({
10631
+ ...warning,
10632
+ elements: warning.elements.slice(0, elements)
10633
+ }))
10634
+ });
10635
+ let value = encode(result);
10636
+ if (value.length <= limit) return value;
10637
+ for (const elements of [1, 0]) {
10638
+ value = encode(keep(result, elements));
10639
+ if (value.length <= limit) return value;
10640
+ }
10641
+ let report = keep(result, 0);
10642
+ while (report.failures.length + report.warnings.length > 0) {
10643
+ report = report.warnings.length ? { ...report, warnings: report.warnings.slice(0, -1) } : { ...report, failures: report.failures.slice(0, -1) };
10644
+ value = encode(report);
10645
+ if (value.length <= limit) return value;
10646
+ }
10647
+ return value;
10648
+ }
10649
+
10650
+ // packages/engine/src/lib/accessibility/static.ts
10651
+ var finding = (code, src, snippet) => ({
10652
+ code,
10653
+ severity: AUDIT_SEVERITY[code],
10654
+ src,
10655
+ snippet,
10656
+ args: {}
10657
+ });
10658
+ var NAMING = /* @__PURE__ */ new Set(["alt", "aria-label", "aria-labelledby", "title"]);
10659
+ function imageIsNamed(attributes, scanner, base) {
10660
+ const n = attributes.length;
10661
+ let i = 0;
10662
+ let named = false;
10663
+ while (i < n) {
10664
+ const ch = attributes[i];
10665
+ if (/[\s/>]/.test(ch)) {
10666
+ i++;
10667
+ continue;
10668
+ }
10669
+ if (ch === "{" && scanner.skipTemplate(base + i) !== null) return null;
10670
+ const start = i;
10671
+ while (i < n && !/[\s=/>]/.test(attributes[i])) i++;
10672
+ const name = attributes.slice(start, i).toLowerCase();
10673
+ while (i < n && /\s/.test(attributes[i])) i++;
10674
+ let value = "";
10675
+ if (attributes[i] === "=") {
10676
+ i++;
10677
+ while (i < n && /\s/.test(attributes[i])) i++;
10678
+ const quote2 = attributes[i];
10679
+ if (quote2 === '"' || quote2 === "'") {
10680
+ const end = scanner.skipTo(base + i + 1, quote2) - base;
10681
+ value = attributes.slice(i + 1, Math.max(i + 1, end - 1));
10682
+ i = end;
10683
+ } else {
10684
+ const from = i;
10685
+ while (i < n && !/[\s>]/.test(attributes[i])) i++;
10686
+ value = attributes.slice(from, i);
10687
+ }
10688
+ }
10689
+ if (NAMING.has(name)) named = true;
10690
+ else if (name === "aria-hidden" && value.trim() !== "false") named = true;
10691
+ else if (name === "role" && /^(presentation|none)$/.test(value.trim()))
10692
+ named = true;
10693
+ }
10694
+ return named;
10695
+ }
10696
+ function staticAuditDiagnostics(input, ua) {
10697
+ return staticAudit(input).map((found) => {
10698
+ const at = /^body:(\d+):(\d+)$/.exec(found.src ?? "");
10699
+ return {
10700
+ severity: found.severity === "error" && ua.check === "strict" ? "error" : "warning",
10701
+ code: `accessibility-${found.code}`,
10702
+ message: auditMessage(found),
10703
+ ...at ? { location: { line: Number(at[1]), column: Number(at[2]) } } : {}
10704
+ };
10705
+ });
10706
+ }
10707
+ function staticAudit(input) {
10708
+ const findings = [];
10709
+ if (!input.title?.trim() && !/<title[\s>]/i.test(input.head ?? ""))
10710
+ findings.push(finding("document-title", null, ""));
10711
+ const source = input.html;
10712
+ const scanner = new Scanner(source, input.engine);
10713
+ const lines = lineStarts(source);
10714
+ const n = source.length;
10715
+ let i = 0;
10716
+ while (i < n) {
10717
+ const ch = source[i];
10718
+ if (ch === "{") {
10719
+ i = scanner.skipTemplate(i) ?? i + 1;
10720
+ continue;
10721
+ }
10722
+ if (ch !== "<") {
10723
+ i++;
10724
+ continue;
10725
+ }
10726
+ if (source.startsWith("<!--", i)) {
10727
+ i = scanner.skipTo(i + 4, "-->");
10728
+ continue;
10729
+ }
10730
+ const next = source[i + 1];
10731
+ if (next === "!" || next === "?") {
10732
+ i = scanner.tagEnd(i + 2);
10733
+ continue;
10734
+ }
10735
+ if (next === "/") {
10736
+ i += 2;
10737
+ continue;
10738
+ }
10739
+ tagName.lastIndex = i + 1;
10740
+ const name = tagName.exec(source)?.[0];
10741
+ if (!name) {
10742
+ i++;
10743
+ continue;
10744
+ }
10745
+ const nameEnd = i + 1 + name.length;
10746
+ const end = scanner.tagEnd(nameEnd);
10747
+ if (name.toLowerCase() === "img") {
10748
+ const named = imageIsNamed(source.slice(nameEnd, end), scanner, nameEnd);
10749
+ if (named === false) {
10750
+ const { line: line2, column } = locate(lines, i);
10751
+ const tag = source.slice(i, end).replace(/\s+/g, " ");
10752
+ findings.push(
10753
+ finding(
10754
+ "image-alt",
10755
+ `body:${line2}:${column}`,
10756
+ tag.length > AUDIT_SNIPPET_LENGTH ? `${tag.slice(0, AUDIT_SNIPPET_LENGTH - 1)}\u2026` : tag
10757
+ )
10758
+ );
10759
+ }
10760
+ }
10761
+ i = end;
10762
+ if (rawTextElements.has(name.toLowerCase()) && source[end - 1] === ">")
10763
+ i = scanner.skipToClosingTag(end, name);
10764
+ }
10765
+ return findings;
10766
+ }
10767
+
8176
10768
  // packages/engine/src/lib/office/errors.ts
8177
10769
  var OfficeError = class extends Error {
8178
10770
  constructor(code, message, details = {}) {
@@ -9492,12 +12084,12 @@ function assertWellFormed(tokens, part) {
9492
12084
  }
9493
12085
  var FORBIDDEN = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/;
9494
12086
  var ENTITY = /&(?:#x[0-9A-Fa-f]+|#\d+|amp|lt|gt|quot|apos);/y;
9495
- function checkText(text2, part) {
9496
- if (FORBIDDEN.test(text2)) throw invalid2("a character XML does not allow", part);
9497
- if (text2.includes("<")) throw invalid2("unescaped < in text", part);
9498
- for (let i = text2.indexOf("&"); i !== -1; i = text2.indexOf("&", i + 1)) {
12087
+ function checkText(text4, part) {
12088
+ if (FORBIDDEN.test(text4)) throw invalid2("a character XML does not allow", part);
12089
+ if (text4.includes("<")) throw invalid2("unescaped < in text", part);
12090
+ for (let i = text4.indexOf("&"); i !== -1; i = text4.indexOf("&", i + 1)) {
9499
12091
  ENTITY.lastIndex = i;
9500
- if (!ENTITY.test(text2)) throw invalid2(`invalid entity reference near "${text2.slice(i, i + 12)}"`, part);
12092
+ if (!ENTITY.test(text4)) throw invalid2(`invalid entity reference near "${text4.slice(i, i + 12)}"`, part);
9501
12093
  }
9502
12094
  }
9503
12095
  function checkAttributes(raw, part) {
@@ -9532,13 +12124,13 @@ function decodeText3(raw) {
9532
12124
  }
9533
12125
  });
9534
12126
  }
9535
- function escapeText2(text2) {
9536
- return text2.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
12127
+ function escapeText3(text4) {
12128
+ return text4.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
9537
12129
  }
9538
12130
  var FORBIDDEN_GLOBAL = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
9539
- function stripForbidden(text2) {
12131
+ function stripForbidden(text4) {
9540
12132
  let removed = 0;
9541
- const out = text2.replace(FORBIDDEN_GLOBAL, () => {
12133
+ const out = text4.replace(FORBIDDEN_GLOBAL, () => {
9542
12134
  removed++;
9543
12135
  return "";
9544
12136
  });
@@ -9751,7 +12343,7 @@ function textWidthEmu(xml2) {
9751
12343
  function inlinePicture(picture, rId, id) {
9752
12344
  const a = "http://schemas.openxmlformats.org/drawingml/2006/main";
9753
12345
  const name = `Formfeed picture ${id}`;
9754
- const alt = escapeText2(picture.alt).replace(/"/g, "&quot;");
12346
+ const alt = escapeText3(picture.alt).replace(/"/g, "&quot;");
9755
12347
  const { widthEmu: cx, heightEmu: cy } = picture;
9756
12348
  return `<w:drawing><wp:inline xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" distT="0" distB="0" distL="0" distR="0"><wp:extent cx="${cx}" cy="${cy}"/><wp:effectExtent l="0" t="0" r="0" b="0"/><wp:docPr id="${id}" name="${name}" descr="${alt}"/><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="${a}" noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic xmlns:a="${a}"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="${id}" name="${name}" descr="${alt}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${rId}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing>`;
9757
12349
  }
@@ -9789,13 +12381,13 @@ function placeDrawings(xml2, pattern, markupFor) {
9789
12381
  pattern.lastIndex = 0;
9790
12382
  if (pattern.test(t.raw)) {
9791
12383
  const segments = t.raw.split(pattern);
9792
- let text2 = segments[0];
12384
+ let text4 = segments[0];
9793
12385
  for (let s = 1; s < segments.length; s += 2) {
9794
12386
  const index = Number(segments[s]);
9795
- text2 += `</w:t></w:r><w:r>${runProps}${markupFor(index)}</w:r>${runOpen}${runProps}<w:t xml:space="preserve">` + segments[s + 1];
12387
+ text4 += `</w:t></w:r><w:r>${runProps}${markupFor(index)}</w:r>${runOpen}${runProps}<w:t xml:space="preserve">` + segments[s + 1];
9796
12388
  }
9797
12389
  out[textOpen] = withPreserve(out[textOpen]);
9798
- out.push(text2);
12390
+ out.push(text4);
9799
12391
  continue;
9800
12392
  }
9801
12393
  }
@@ -9808,7 +12400,7 @@ function withPreserve(openTag) {
9808
12400
  }
9809
12401
  function withRelationships(rels2, relationships) {
9810
12402
  const type = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/image";
9811
- const added = relationships.map((r) => `<Relationship Id="${r.id}" Type="${type}" Target="${escapeText2(r.target)}"/>`).join("");
12403
+ const added = relationships.map((r) => `<Relationship Id="${r.id}" Type="${type}" Target="${escapeText3(r.target)}"/>`).join("");
9812
12404
  if (!rels2)
9813
12405
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">${added}</Relationships>`;
9814
12406
  const end = rels2.lastIndexOf("</Relationships>");
@@ -10050,11 +12642,11 @@ function fromTemplateOutput(output, template, part) {
10050
12642
  let last = 0;
10051
12643
  let inText = false;
10052
12644
  const emit = (segment) => {
10053
- const { text: text2, removed } = stripForbidden(segment);
12645
+ const { text: text4, removed } = stripForbidden(segment);
10054
12646
  removedCharacters += removed;
10055
- if (!inText && text2.trim() !== "")
10056
- throw new OfficeError("office_document_invalid", `${part ?? "part"}: text outside a text element: "${text2.trim().slice(0, 40)}"`);
10057
- xml2 += escapeText2(text2);
12647
+ if (!inText && text4.trim() !== "")
12648
+ throw new OfficeError("office_document_invalid", `${part ?? "part"}: text outside a text element: "${text4.trim().slice(0, 40)}"`);
12649
+ xml2 += escapeText3(text4);
10058
12650
  };
10059
12651
  for (const m of output.matchAll(placeholder)) {
10060
12652
  emit(output.slice(last, m.index));
@@ -10096,15 +12688,15 @@ function layoutText(xml2, flavour, part) {
10096
12688
  out.push(token2.raw);
10097
12689
  continue;
10098
12690
  }
10099
- const text2 = token2.raw.replace(/\r\n?/g, "\n");
12691
+ const text4 = token2.raw.replace(/\r\n?/g, "\n");
10100
12692
  if (flavour === "wordprocessing") {
10101
- const pieces = text2.split(/(\n|\t)/);
10102
- if (pieces.length > 1 || /^\s|\s$/.test(text2)) out[textOpenIndex] = preserveSpace(out[textOpenIndex]);
12693
+ const pieces = text4.split(/(\n|\t)/);
12694
+ if (pieces.length > 1 || /^\s|\s$/.test(text4)) out[textOpenIndex] = preserveSpace(out[textOpenIndex]);
10103
12695
  out.push(
10104
12696
  pieces.map((p) => p === "\n" ? '</w:t><w:br/><w:t xml:space="preserve">' : p === " " ? '</w:t><w:tab/><w:t xml:space="preserve">' : p).join("")
10105
12697
  );
10106
12698
  } else {
10107
- out.push(text2.split("\n").join(`</a:t></a:r><a:br>${runProperties}</a:br><a:r>${runProperties}<a:t>`));
12699
+ out.push(text4.split("\n").join(`</a:t></a:r><a:br>${runProperties}</a:br><a:r>${runProperties}<a:t>`));
10108
12700
  }
10109
12701
  }
10110
12702
  return out.join("");
@@ -10137,17 +12729,17 @@ var AUTOCORRECTED = [
10137
12729
  [/\u200B|\u200C|\u200D|\uFEFF/g, ""]
10138
12730
  ];
10139
12731
  function normaliseTags(template, part) {
10140
- const { text: text2, origin, placeholders } = stripPlaceholders(template);
12732
+ const { text: text4, origin, placeholders } = stripPlaceholders(template);
10141
12733
  const sourceRange = (from, to) => [
10142
12734
  origin[from] ?? template.source.length,
10143
12735
  (origin[to - 1] ?? template.source.length - 1) + 1
10144
12736
  ];
10145
12737
  const diagnostics = [];
10146
12738
  const edits = [];
10147
- for (const tag of findTags(text2)) {
12739
+ for (const tag of findTags(text4)) {
10148
12740
  const inside = placeholders.filter((p) => p.at > tag.start && p.at < tag.end);
10149
12741
  const paragraph2 = paragraphOf(template, placeholders, tag.start);
10150
- const raw = text2.slice(tag.start, tag.end);
12742
+ const raw = text4.slice(tag.start, tag.end);
10151
12743
  const unsafe = inside.filter((p) => {
10152
12744
  const piece = template.markup[p.index];
10153
12745
  return !piece || piece.paragraphBoundary || piece.containerBoundary;
@@ -10183,7 +12775,7 @@ function normaliseTags(template, part) {
10183
12775
  for (const p of placeholders) {
10184
12776
  const raw = template.markup[p.index]?.raw ?? "";
10185
12777
  for (const m of raw.matchAll(/<w:instrText\b[^>]*>([^<]*)<\/w:instrText>/g)) {
10186
- const code = decodeEntities(m[1] ?? "");
12778
+ const code = decodeEntities2(m[1] ?? "");
10187
12779
  if (!/\{\{|\{%|\{#/.test(code)) continue;
10188
12780
  const paragraph2 = paragraphOf(template, placeholders, p.at);
10189
12781
  if (reported.has(paragraph2)) continue;
@@ -10200,38 +12792,38 @@ function normaliseTags(template, part) {
10200
12792
  }
10201
12793
  return { template: { ...template, source: applyEdits(template.source, edits) }, diagnostics };
10202
12794
  }
10203
- var ENTITIES = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'" };
10204
- function decodeEntities(text2) {
10205
- return text2.replace(/&(#x[0-9a-f]+|#\d+|amp|lt|gt|quot|apos);/gi, (whole, name) => {
10206
- if (name[0] !== "#") return ENTITIES[name.toLowerCase()] ?? whole;
12795
+ var ENTITIES2 = { amp: "&", lt: "<", gt: ">", quot: '"', apos: "'" };
12796
+ function decodeEntities2(text4) {
12797
+ return text4.replace(/&(#x[0-9a-f]+|#\d+|amp|lt|gt|quot|apos);/gi, (whole, name) => {
12798
+ if (name[0] !== "#") return ENTITIES2[name.toLowerCase()] ?? whole;
10207
12799
  const code = name[1] === "x" || name[1] === "X" ? parseInt(name.slice(2), 16) : parseInt(name.slice(1), 10);
10208
12800
  return Number.isFinite(code) ? String.fromCodePoint(code) : whole;
10209
12801
  });
10210
12802
  }
10211
12803
  function stripPlaceholders(template) {
10212
12804
  const pattern = placeholderPattern(template.nonce);
10213
- let text2 = "";
12805
+ let text4 = "";
10214
12806
  const origin = [];
10215
12807
  const placeholders = [];
10216
12808
  let last = 0;
10217
12809
  for (const m of template.source.matchAll(pattern)) {
10218
12810
  for (let i = last; i < (m.index ?? 0); i++) {
10219
- text2 += template.source[i];
12811
+ text4 += template.source[i];
10220
12812
  origin.push(i);
10221
12813
  }
10222
- placeholders.push({ index: Number(m[1]), at: text2.length, from: m.index ?? 0, to: (m.index ?? 0) + m[0].length });
12814
+ placeholders.push({ index: Number(m[1]), at: text4.length, from: m.index ?? 0, to: (m.index ?? 0) + m[0].length });
10223
12815
  last = (m.index ?? 0) + m[0].length;
10224
12816
  }
10225
12817
  for (let i = last; i < template.source.length; i++) {
10226
- text2 += template.source[i];
12818
+ text4 += template.source[i];
10227
12819
  origin.push(i);
10228
12820
  }
10229
- return { text: text2, origin, placeholders };
12821
+ return { text: text4, origin, placeholders };
10230
12822
  }
10231
12823
  function listTags(template, part) {
10232
- const { text: text2, placeholders } = stripPlaceholders(template);
10233
- return findTags(text2).map((tag) => {
10234
- const raw = text2.slice(tag.start, tag.end);
12824
+ const { text: text4, placeholders } = stripPlaceholders(template);
12825
+ return findTags(text4).map((tag) => {
12826
+ const raw = text4.slice(tag.start, tag.end);
10235
12827
  const kind = raw.startsWith("{#") ? "comment" : raw.startsWith("{%") || /^\{\{~?\s*[#/^]/.test(raw) || /^\{\{~?\s*else\b/.test(raw) ? "block" : "output";
10236
12828
  return { part, paragraph: paragraphOf(template, placeholders, tag.start), text: raw.replace(/\s+/g, " ").trim(), kind };
10237
12829
  });
@@ -10252,15 +12844,15 @@ function undoAutocorrect(tag) {
10252
12844
  for (const [pattern, replacement] of AUTOCORRECTED) out = out.replace(pattern, replacement);
10253
12845
  return out;
10254
12846
  }
10255
- function findTags(text2) {
12847
+ function findTags(text4) {
10256
12848
  const tags = [];
10257
- for (let i = 0; i < text2.length - 1; i++) {
10258
- const opener = OPENERS.find((o) => text2.startsWith(o, i));
12849
+ for (let i = 0; i < text4.length - 1; i++) {
12850
+ const opener = OPENERS.find((o) => text4.startsWith(o, i));
10259
12851
  if (!opener) continue;
10260
12852
  const closer = CLOSERS[opener];
10261
- const end = text2.indexOf(closer, i + opener.length);
12853
+ const end = text4.indexOf(closer, i + opener.length);
10262
12854
  if (end === -1) continue;
10263
- const stop = end + closer.length + (opener === "{{" && text2[end + closer.length] === "}" ? 1 : 0);
12855
+ const stop = end + closer.length + (opener === "{{" && text4[end + closer.length] === "}" ? 1 : 0);
10264
12856
  tags.push({ start: i, end: stop });
10265
12857
  i = stop - 1;
10266
12858
  }
@@ -10290,8 +12882,8 @@ var CONTENT = /* @__PURE__ */ new Set(["w:drawing", "w:pict", "w:object", "w:fld
10290
12882
  var JINJA_OPEN = /^(for|if|unless|case|capture|raw|with|macro|call|filter|block|tablerow|comment|autoescape)\b/;
10291
12883
  var JINJA_CLOSE = /^end(for|if|unless|case|capture|raw|with|macro|call|filter|block|tablerow|comment|autoescape)\b/;
10292
12884
  var JINJA_MIDDLE = /^(else|elif|elsif|when)\b/;
10293
- function blockRole(text2) {
10294
- const t = text2.trim();
12885
+ function blockRole(text4) {
12886
+ const t = text4.trim();
10295
12887
  let m = /^\{%-?\s*([\s\S]*?)\s*-?%\}$/.exec(t);
10296
12888
  if (m && !t.slice(2, -2).includes("%}")) {
10297
12889
  const body = m[1] ?? "";
@@ -10323,14 +12915,14 @@ function dropTaggedFallbacks(xml2, part) {
10323
12915
  if (t.kind === "open" && t.name === "mc:Fallback" && !t.selfClosing) {
10324
12916
  let depth = 1;
10325
12917
  let j = i + 1;
10326
- let text2 = "";
12918
+ let text4 = "";
10327
12919
  for (; j < tokens.length && depth > 0; j++) {
10328
12920
  const u = tokens[j];
10329
12921
  if (u.kind === "open" && u.name === "mc:Fallback" && !u.selfClosing) depth++;
10330
12922
  else if (u.kind === "close" && u.name === "mc:Fallback") depth--;
10331
- else if (u.kind === "text") text2 += decodeText3(u.raw);
12923
+ else if (u.kind === "text") text4 += decodeText3(u.raw);
10332
12924
  }
10333
- if (/\{\{|\{%|\{#/.test(text2)) {
12925
+ if (/\{\{|\{%|\{#/.test(text4)) {
10334
12926
  i = j - 1;
10335
12927
  continue;
10336
12928
  }
@@ -10397,15 +12989,15 @@ function applyStructure(xml2, part) {
10397
12989
  }
10398
12990
  }
10399
12991
  const replacements = [];
10400
- const element = (tag) => `<${STRUCTURAL_ELEMENT}>${escapeText2(tag)}</${STRUCTURAL_ELEMENT}>`;
12992
+ const element = (tag) => `<${STRUCTURAL_ELEMENT}>${escapeText3(tag)}</${STRUCTURAL_ELEMENT}>`;
10401
12993
  for (const [row2, tag] of replacedRows) replacements.push({ start: row2.start, end: row2.end, text: element(tag) });
10402
12994
  const cellsEmptied = /* @__PURE__ */ new Map();
10403
12995
  for (const [span, s] of structural) {
10404
12996
  if (rowOf.has(span)) continue;
10405
12997
  const keep = s.info.sectionProperties;
10406
- let text2 = element(s.tag);
10407
- if (keep) text2 = s.role === "open" ? `<w:p>${keep}</w:p>${text2}` : `${text2}<w:p>${keep}</w:p>`;
10408
- replacements.push({ start: span.start, end: span.end, text: text2 });
12998
+ let text4 = element(s.tag);
12999
+ if (keep) text4 = s.role === "open" ? `<w:p>${keep}</w:p>${text4}` : `${text4}<w:p>${keep}</w:p>`;
13000
+ replacements.push({ start: span.start, end: span.end, text: text4 });
10409
13001
  const holder = span.parent && BODIES.has(span.parent.name) ? span.parent : null;
10410
13002
  if (holder && !keep) cellsEmptied.set(holder, (cellsEmptied.get(holder) ?? 0) + 1);
10411
13003
  }
@@ -10459,7 +13051,7 @@ function collectParagraphs(tokens, root) {
10459
13051
  const out = [];
10460
13052
  const walk3 = (span) => {
10461
13053
  if (PARAGRAPH.has(span.name)) {
10462
- let text2 = "";
13054
+ let text4 = "";
10463
13055
  let hasContent = false;
10464
13056
  let sectionProperties = null;
10465
13057
  let inText = false;
@@ -10470,10 +13062,10 @@ function collectParagraphs(tokens, root) {
10470
13062
  else if (CONTENT.has(t.name)) hasContent = true;
10471
13063
  else if (t.name === "w:sectPr") sectionProperties = "";
10472
13064
  } else if (t.kind === "close" && TEXT.has(t.name)) inText = false;
10473
- else if (t.kind === "text" && inText) text2 += decodeText3(t.raw);
13065
+ else if (t.kind === "text" && inText) text4 += decodeText3(t.raw);
10474
13066
  }
10475
13067
  if (sectionProperties !== null) sectionProperties = propertiesOf(tokens, span);
10476
- out.push({ span, number: out.length + 1, text: text2, hasContent, sectionProperties });
13068
+ out.push({ span, number: out.length + 1, text: text4, hasContent, sectionProperties });
10477
13069
  }
10478
13070
  for (const child2 of span.children) walk3(child2);
10479
13071
  };
@@ -10495,9 +13087,9 @@ function isInside(span, container) {
10495
13087
  function rewrite(tokens, replacements) {
10496
13088
  const byStart = /* @__PURE__ */ new Map();
10497
13089
  for (const r of replacements) {
10498
- const list2 = byStart.get(r.start) ?? [];
10499
- list2.push({ end: r.end, text: r.text });
10500
- byStart.set(r.start, list2);
13090
+ const list3 = byStart.get(r.start) ?? [];
13091
+ list3.push({ end: r.end, text: r.text });
13092
+ byStart.set(r.start, list3);
10501
13093
  }
10502
13094
  let out = "";
10503
13095
  for (let i = 0; i < tokens.length; i++) {
@@ -10546,7 +13138,7 @@ function boxOf(container, tokens) {
10546
13138
  return x === null || y === null || cx === null || cy === null ? null : { x, y, cx, cy };
10547
13139
  }
10548
13140
  function slidePicture(picture, rId, id, box) {
10549
- const alt = escapeText2(picture.alt).replace(/"/g, "&quot;");
13141
+ const alt = escapeText3(picture.alt).replace(/"/g, "&quot;");
10550
13142
  return `<p:pic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><p:nvPicPr><p:cNvPr id="${id}" name="Formfeed picture ${id}" descr="${alt}"/><p:cNvPicPr><a:picLocks noChangeAspect="1"/></p:cNvPicPr><p:nvPr/></p:nvPicPr><p:blipFill><a:blip r:embed="${rId}"/><a:stretch><a:fillRect/></a:stretch></p:blipFill><p:spPr><a:xfrm><a:off x="${box.x}" y="${box.y}"/><a:ext cx="${box.cx}" cy="${box.cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></p:spPr></p:pic>`;
10551
13143
  }
10552
13144
  function layoutPictures(pictures, box, growsWithText = false) {
@@ -10581,7 +13173,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
10581
13173
  for (const container of containers) {
10582
13174
  const indexes = [];
10583
13175
  const edits = [];
10584
- let text2 = "";
13176
+ let text4 = "";
10585
13177
  let inText = false;
10586
13178
  for (let i = container.start + 1; i < container.end; i++) {
10587
13179
  const t = tokens[i];
@@ -10590,7 +13182,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
10590
13182
  else if (t.kind === "text" && inText) {
10591
13183
  pattern.lastIndex = 0;
10592
13184
  if (!pattern.test(t.raw)) {
10593
- text2 += decodeText3(t.raw);
13185
+ text4 += decodeText3(t.raw);
10594
13186
  continue;
10595
13187
  }
10596
13188
  const segments = t.raw.split(pattern);
@@ -10599,7 +13191,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
10599
13191
  if (s % 2 === 1) indexes.push(Number(segments[s]));
10600
13192
  else kept += segments[s];
10601
13193
  }
10602
- text2 += decodeText3(kept);
13194
+ text4 += decodeText3(kept);
10603
13195
  edits.push({ at: i, raw: kept });
10604
13196
  }
10605
13197
  }
@@ -10618,7 +13210,7 @@ function placeSlidePictures(xml2, pattern, drawingFor, pictureMarkup, slide) {
10618
13210
  }
10619
13211
  const autoFit = tokens.slice(container.start, container.end).some((t) => t.kind === "open" && t.name === "a:spAutoFit");
10620
13212
  const placed = layoutPictures(pictures, box, autoFit).map((b, i) => pictureMarkup(pictures[i], b)).join("");
10621
- const onlyPictures = container.name === "p:sp" && text2.trim() === "";
13213
+ const onlyPictures = container.name === "p:sp" && text4.trim() === "";
10622
13214
  if (onlyPictures) replacements.push({ start: container.start, end: container.end, text: placed });
10623
13215
  else {
10624
13216
  for (const edit of edits) replacements.push({ start: edit.at, end: edit.at, text: edit.raw });
@@ -10891,8 +13483,8 @@ function starterTags(engine) {
10891
13483
  codeFit: engine === "liquid" ? "{{ offer.url | qrcode }}" : "{{ qrcode(offer.url) }}"
10892
13484
  };
10893
13485
  }
10894
- var paragraph = (text2, style2, bold = false) => `<w:p>${style2 ? `<w:pPr><w:pStyle w:val="${style2}"/></w:pPr>` : ""}<w:r>${bold ? "<w:rPr><w:b/></w:rPr>" : ""}<w:t xml:space="preserve">${escapeText2(text2)}</w:t></w:r></w:p>`;
10895
- var cell = (text2, width, bold = false) => `<w:tc><w:tcPr><w:tcW w:w="${width}" w:type="dxa"/></w:tcPr>${paragraph(text2, void 0, bold)}</w:tc>`;
13486
+ var paragraph = (text4, style2, bold = false) => `<w:p>${style2 ? `<w:pPr><w:pStyle w:val="${style2}"/></w:pPr>` : ""}<w:r>${bold ? "<w:rPr><w:b/></w:rPr>" : ""}<w:t xml:space="preserve">${escapeText3(text4)}</w:t></w:r></w:p>`;
13487
+ var cell = (text4, width, bold = false) => `<w:tc><w:tcPr><w:tcW w:w="${width}" w:type="dxa"/></w:tcPr>${paragraph(text4, void 0, bold)}</w:tc>`;
10896
13488
  var row = (cells) => `<w:tr>${cells}</w:tr>`;
10897
13489
  function starterDocument(engine) {
10898
13490
  const t = starterTags(engine);
@@ -10935,7 +13527,7 @@ function starterDocument(engine) {
10935
13527
  ["word/document.xml", `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><w:document xmlns:w="${W}"><w:body>${body}</w:body></w:document>`]
10936
13528
  ];
10937
13529
  return {
10938
- bytes: writeZip(files.map(([name, text2]) => ({ name, data: encoder2.encode(text2) }))),
13530
+ bytes: writeZip(files.map(([name, text4]) => ({ name, data: encoder2.encode(text4) }))),
10939
13531
  sampleData: starterSampleData()
10940
13532
  };
10941
13533
  }
@@ -10963,15 +13555,15 @@ var CM = 36e4;
10963
13555
  var FONT = "Carlito";
10964
13556
  var ACCENT = "0F766E";
10965
13557
  var emptyTree = '<p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr/>';
10966
- var run = (text2, size, options = {}) => `<a:r><a:rPr lang="en-GB" sz="${size * 100}"${options.bold ? ' b="1"' : ""} dirty="0">${options.color ? `<a:solidFill><a:srgbClr val="${options.color}"/></a:solidFill>` : ""}<a:latin typeface="${FONT}"/></a:rPr><a:t>${escapeText2(text2)}</a:t></a:r>`;
10967
- var para = (text2, size, options = {}) => `<a:p>${run(text2, size, options)}</a:p>`;
13558
+ var run = (text4, size, options = {}) => `<a:r><a:rPr lang="en-GB" sz="${size * 100}"${options.bold ? ' b="1"' : ""} dirty="0">${options.color ? `<a:solidFill><a:srgbClr val="${options.color}"/></a:solidFill>` : ""}<a:latin typeface="${FONT}"/></a:rPr><a:t>${escapeText3(text4)}</a:t></a:r>`;
13559
+ var para = (text4, size, options = {}) => `<a:p>${run(text4, size, options)}</a:p>`;
10968
13560
  function textBox(id, name, x, y, cx, cy, paragraphs) {
10969
13561
  return `<p:sp><p:nvSpPr><p:cNvPr id="${id}" name="${name}"/><p:cNvSpPr txBox="1"/><p:nvPr/></p:nvSpPr><p:spPr><a:xfrm><a:off x="${x}" y="${y}"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom><a:noFill/></p:spPr><p:txBody><a:bodyPr wrap="square" rtlCol="0"><a:noAutofit/></a:bodyPr><a:lstStyle/>${paragraphs}</p:txBody></p:sp>`;
10970
13562
  }
10971
- function cell2(text2, header) {
13563
+ function cell2(text4, header) {
10972
13564
  const fill = header ? `<a:solidFill><a:srgbClr val="${ACCENT}"/></a:solidFill>` : "<a:noFill/>";
10973
- const line = `<a:lnB w="6350"><a:solidFill><a:srgbClr val="BFBFBF"/></a:solidFill></a:lnB>`;
10974
- return `<a:tc><a:txBody><a:bodyPr/><a:lstStyle/>${para(text2, 14, header ? { bold: true, color: "FFFFFF" } : {})}</a:txBody><a:tcPr marL="91440" marR="91440" marT="45720" marB="45720">${line}${fill}</a:tcPr></a:tc>`;
13565
+ const line2 = `<a:lnB w="6350"><a:solidFill><a:srgbClr val="BFBFBF"/></a:solidFill></a:lnB>`;
13566
+ return `<a:tc><a:txBody><a:bodyPr/><a:lstStyle/>${para(text4, 14, header ? { bold: true, color: "FFFFFF" } : {})}</a:txBody><a:tcPr marL="91440" marR="91440" marT="45720" marB="45720">${line2}${fill}</a:tcPr></a:tc>`;
10975
13567
  }
10976
13568
  var tableRow = (a, b, header = false) => `<a:tr h="${Math.round(0.9 * CM)}">${cell2(a, header)}${cell2(b, header)}</a:tr>`;
10977
13569
  function table2(id, x, y, rows) {
@@ -10981,8 +13573,8 @@ function table2(id, x, y, rows) {
10981
13573
  function theme() {
10982
13574
  const scheme = (name, color) => `<a:${name}><a:srgbClr val="${color}"/></a:${name}>`;
10983
13575
  const fill = `<a:solidFill><a:schemeClr val="phClr"/></a:solidFill>`;
10984
- const line = `<a:ln w="6350"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:ln>`;
10985
- return `<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Formfeed"><a:themeElements><a:clrScheme name="Formfeed">${scheme("dk1", "000000")}${scheme("lt1", "FFFFFF")}${scheme("dk2", "1F2937")}${scheme("lt2", "F3F4F6")}${scheme("accent1", ACCENT)}${scheme("accent2", "F59E0B")}${scheme("accent3", "2563EB")}${scheme("accent4", "DC2626")}${scheme("accent5", "7C3AED")}${scheme("accent6", "059669")}${scheme("hlink", "2563EB")}${scheme("folHlink", "7C3AED")}</a:clrScheme><a:fontScheme name="Formfeed"><a:majorFont><a:latin typeface="${FONT}"/><a:ea typeface=""/><a:cs typeface=""/></a:majorFont><a:minorFont><a:latin typeface="${FONT}"/><a:ea typeface=""/><a:cs typeface=""/></a:minorFont></a:fontScheme><a:fmtScheme name="Formfeed"><a:fillStyleLst>${fill}${fill}${fill}</a:fillStyleLst><a:lnStyleLst>${line}${line}${line}</a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst/></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst>${fill}${fill}${fill}</a:bgFillStyleLst></a:fmtScheme></a:themeElements></a:theme>`;
13576
+ const line2 = `<a:ln w="6350"><a:solidFill><a:schemeClr val="phClr"/></a:solidFill></a:ln>`;
13577
+ return `<a:theme xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" name="Formfeed"><a:themeElements><a:clrScheme name="Formfeed">${scheme("dk1", "000000")}${scheme("lt1", "FFFFFF")}${scheme("dk2", "1F2937")}${scheme("lt2", "F3F4F6")}${scheme("accent1", ACCENT)}${scheme("accent2", "F59E0B")}${scheme("accent3", "2563EB")}${scheme("accent4", "DC2626")}${scheme("accent5", "7C3AED")}${scheme("accent6", "059669")}${scheme("hlink", "2563EB")}${scheme("folHlink", "7C3AED")}</a:clrScheme><a:fontScheme name="Formfeed"><a:majorFont><a:latin typeface="${FONT}"/><a:ea typeface=""/><a:cs typeface=""/></a:majorFont><a:minorFont><a:latin typeface="${FONT}"/><a:ea typeface=""/><a:cs typeface=""/></a:minorFont></a:fontScheme><a:fmtScheme name="Formfeed"><a:fillStyleLst>${fill}${fill}${fill}</a:fillStyleLst><a:lnStyleLst>${line2}${line2}${line2}</a:lnStyleLst><a:effectStyleLst><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst/></a:effectStyle><a:effectStyle><a:effectLst/></a:effectStyle></a:effectStyleLst><a:bgFillStyleLst>${fill}${fill}${fill}</a:bgFillStyleLst></a:fmtScheme></a:themeElements></a:theme>`;
10986
13578
  }
10987
13579
  var xml = (body) => `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>${body}`;
10988
13580
  var rels = (entries) => xml(
@@ -11061,7 +13653,7 @@ function starterPresentation(engine) {
11061
13653
  ];
11062
13654
  const encoder2 = new TextEncoder();
11063
13655
  return {
11064
- bytes: writeZip(files.map(([name, text2]) => ({ name, data: encoder2.encode(text2) }))),
13656
+ bytes: writeZip(files.map(([name, text4]) => ({ name, data: encoder2.encode(text4) }))),
11065
13657
  sampleData: starterSampleData()
11066
13658
  };
11067
13659
  }