@formfeed/engine 0.3.10 → 0.3.12

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 (71) hide show
  1. package/index.cjs +4728 -438
  2. package/index.js +4728 -438
  3. package/package.json +3 -2
  4. package/types/frame/fit.d.cts +13 -0
  5. package/types/frame/fit.d.ts +13 -0
  6. package/types/frame/fit.entry.d.cts +1 -0
  7. package/types/frame/fit.entry.d.ts +1 -0
  8. package/types/frame/stage.d.cts +22 -0
  9. package/types/frame/stage.d.ts +22 -0
  10. package/types/frame/stage.entry.d.cts +1 -0
  11. package/types/frame/stage.entry.d.ts +1 -0
  12. package/types/frame/text-edit.d.cts +82 -0
  13. package/types/frame/text-edit.d.ts +82 -0
  14. package/types/frame/text-edit.entry.d.cts +1 -0
  15. package/types/frame/text-edit.entry.d.ts +1 -0
  16. package/types/index.d.cts +23 -14
  17. package/types/index.d.ts +23 -14
  18. package/types/lib/assemble.d.cts +13 -0
  19. package/types/lib/assemble.d.ts +13 -0
  20. package/types/lib/design/emit.d.cts +20 -0
  21. package/types/lib/design/emit.d.ts +20 -0
  22. package/types/lib/design/fonts.d.cts +27 -0
  23. package/types/lib/design/fonts.d.ts +27 -0
  24. package/types/lib/design/hash.d.cts +6 -0
  25. package/types/lib/design/hash.d.ts +6 -0
  26. package/types/lib/design/index.d.cts +12 -0
  27. package/types/lib/design/index.d.ts +12 -0
  28. package/types/lib/design/layers.d.cts +12 -0
  29. package/types/lib/design/layers.d.ts +12 -0
  30. package/types/lib/design/parse.d.cts +25 -0
  31. package/types/lib/design/parse.d.ts +25 -0
  32. package/types/lib/design/starters.d.cts +14 -0
  33. package/types/lib/design/starters.d.ts +14 -0
  34. package/types/lib/design/types.d.cts +171 -0
  35. package/types/lib/design/types.d.ts +171 -0
  36. package/types/lib/dialect.d.cts +32 -0
  37. package/types/lib/dialect.d.ts +32 -0
  38. package/types/lib/einvoice/check.d.cts +181 -0
  39. package/types/lib/einvoice/check.d.ts +181 -0
  40. package/types/lib/einvoice/cii.d.cts +18 -0
  41. package/types/lib/einvoice/cii.d.ts +18 -0
  42. package/types/lib/einvoice/codes.generated.d.cts +14 -0
  43. package/types/lib/einvoice/codes.generated.d.ts +14 -0
  44. package/types/lib/einvoice/decimal.d.cts +29 -0
  45. package/types/lib/einvoice/decimal.d.ts +29 -0
  46. package/types/lib/einvoice/display.d.cts +43 -0
  47. package/types/lib/einvoice/display.d.ts +43 -0
  48. package/types/lib/einvoice/html-text.d.cts +17 -0
  49. package/types/lib/einvoice/html-text.d.ts +17 -0
  50. package/types/lib/einvoice/index.d.cts +45 -0
  51. package/types/lib/einvoice/index.d.ts +45 -0
  52. package/types/lib/einvoice/locate.d.cts +21 -0
  53. package/types/lib/einvoice/locate.d.ts +21 -0
  54. package/types/lib/einvoice/report.d.cts +59 -0
  55. package/types/lib/einvoice/report.d.ts +59 -0
  56. package/types/lib/einvoice/schema.d.cts +293 -0
  57. package/types/lib/einvoice/schema.d.ts +293 -0
  58. package/types/lib/einvoice/skeleton.d.cts +10 -0
  59. package/types/lib/einvoice/skeleton.d.ts +10 -0
  60. package/types/lib/generated/frames.d.cts +6 -0
  61. package/types/lib/generated/frames.d.ts +6 -0
  62. package/types/lib/preview.d.cts +22 -2
  63. package/types/lib/preview.d.ts +22 -2
  64. package/types/lib/source-edit.d.cts +117 -0
  65. package/types/lib/source-edit.d.ts +117 -0
  66. package/types/lib/source-positions.d.cts +12 -3
  67. package/types/lib/source-positions.d.ts +12 -3
  68. package/types/lib/source-scan.d.cts +61 -0
  69. package/types/lib/source-scan.d.ts +61 -0
  70. package/types/lib/translation.d.cts +17 -0
  71. package/types/lib/translation.d.ts +17 -0
@@ -0,0 +1,25 @@
1
+ import { type DesignDocument } from './types.cjs';
2
+ /**
3
+ * Reads a design as it arrives from `settings.design`: from the editor, but also from the API or a
4
+ * pushed `design.json`, so nothing is trusted. Every value is checked, numbers are clamped to sane
5
+ * ranges, and a problem names where it is; a design with problems is not half loaded (the editor opens
6
+ * the template as code and says so).
7
+ */
8
+ export declare const DESIGN_LIMITS: {
9
+ readonly layers: 200;
10
+ readonly textPerLayer: 5000;
11
+ readonly bytes: number;
12
+ };
13
+ export type ParsedDesign = {
14
+ design: DesignDocument;
15
+ problems: [];
16
+ } | {
17
+ design: null;
18
+ problems: string[];
19
+ };
20
+ export declare const layerIdPattern: RegExp;
21
+ /** A data path the emitter writes as is in every engine: dotted identifiers. */
22
+ export declare const dataPathPattern: RegExp;
23
+ /** A design's size as the gateway bounds it: the UTF-8 bytes of its JSON. */
24
+ export declare function designBytes(value: unknown): number;
25
+ export declare function parseDesign(value: unknown): ParsedDesign;
@@ -0,0 +1,25 @@
1
+ import { type DesignDocument } from './types.js';
2
+ /**
3
+ * Reads a design as it arrives from `settings.design`: from the editor, but also from the API or a
4
+ * pushed `design.json`, so nothing is trusted. Every value is checked, numbers are clamped to sane
5
+ * ranges, and a problem names where it is; a design with problems is not half loaded (the editor opens
6
+ * the template as code and says so).
7
+ */
8
+ export declare const DESIGN_LIMITS: {
9
+ readonly layers: 200;
10
+ readonly textPerLayer: 5000;
11
+ readonly bytes: number;
12
+ };
13
+ export type ParsedDesign = {
14
+ design: DesignDocument;
15
+ problems: [];
16
+ } | {
17
+ design: null;
18
+ problems: string[];
19
+ };
20
+ export declare const layerIdPattern: RegExp;
21
+ /** A data path the emitter writes as is in every engine: dotted identifiers. */
22
+ export declare const dataPathPattern: RegExp;
23
+ /** A design's size as the gateway bounds it: the UTF-8 bytes of its JSON. */
24
+ export declare function designBytes(value: unknown): number;
25
+ export declare function parseDesign(value: unknown): ParsedDesign;
@@ -0,0 +1,14 @@
1
+ import { type CanvasSize, type DesignDocument } from './types.cjs';
2
+ /**
3
+ * Designs a new image template can start from (plan 16 §4.2). Titles stay English here; the app words
4
+ * them per locale by `id`. Sample data uses the placeholders of CLAUDE.md, never names that could be
5
+ * real.
6
+ */
7
+ export interface DesignStarter {
8
+ id: 'blank' | 'article' | 'quote' | 'event';
9
+ title: string;
10
+ size: CanvasSize;
11
+ design: DesignDocument;
12
+ sampleData: Record<string, unknown>;
13
+ }
14
+ export declare const designStarters: DesignStarter[];
@@ -0,0 +1,14 @@
1
+ import { type CanvasSize, type DesignDocument } from './types.js';
2
+ /**
3
+ * Designs a new image template can start from (plan 16 §4.2). Titles stay English here; the app words
4
+ * them per locale by `id`. Sample data uses the placeholders of CLAUDE.md, never names that could be
5
+ * real.
6
+ */
7
+ export interface DesignStarter {
8
+ id: 'blank' | 'article' | 'quote' | 'event';
9
+ title: string;
10
+ size: CanvasSize;
11
+ design: DesignDocument;
12
+ sampleData: Record<string, unknown>;
13
+ }
14
+ export declare const designStarters: DesignStarter[];
@@ -0,0 +1,171 @@
1
+ /**
2
+ * A visual design (plan 16 §4.1): the layer model the editor's canvas edits, stored as
3
+ * `settings.design` and turned into the template's body and CSS by `emitDesign`. Renders never read
4
+ * it; the emitted HTML is the template. Coordinates are CSS pixels of the image, whose size stays in
5
+ * `settings.image`.
6
+ */
7
+ export declare const DESIGN_VERSION = 1;
8
+ /** A colour: a literal, or a brand kit colour by name with the literal it falls back to. */
9
+ export type Paint = {
10
+ color: string;
11
+ } | {
12
+ brand: string;
13
+ fallback: string;
14
+ };
15
+ export interface Shadow {
16
+ x: number;
17
+ y: number;
18
+ blur: number;
19
+ color: string;
20
+ }
21
+ export interface Stroke {
22
+ width: number;
23
+ paint: Paint;
24
+ }
25
+ /** A font: a family by source, or one of the brand kit's two. */
26
+ export type FontChoice = {
27
+ family: string;
28
+ source: 'google' | 'organisation' | 'system';
29
+ } | {
30
+ brand: 'heading' | 'body';
31
+ };
32
+ /** How a data value is shown: through one of the helpers every engine has. */
33
+ export type TextFormat = {
34
+ helper: 'upper' | 'lower' | 'title' | 'money';
35
+ } | {
36
+ helper: 'number';
37
+ decimals: number;
38
+ } | {
39
+ helper: 'date';
40
+ pattern: string;
41
+ } | {
42
+ helper: 'truncate';
43
+ length: number;
44
+ } | {
45
+ helper: 'default';
46
+ value: string;
47
+ };
48
+ /** Text of a text layer: literal text, a data path (a chip in the editor), a line break. */
49
+ export type TextItem = {
50
+ text: string;
51
+ } | {
52
+ path: string;
53
+ format?: TextFormat;
54
+ } | {
55
+ break: true;
56
+ };
57
+ export interface TextStyle {
58
+ font: FontChoice;
59
+ weight: number;
60
+ size: number;
61
+ lineHeight: number;
62
+ letterSpacing: number;
63
+ color: Paint;
64
+ align: 'left' | 'center' | 'right' | 'justify';
65
+ italic?: boolean;
66
+ uppercase?: boolean;
67
+ underline?: boolean;
68
+ /** A line through the text; with `underline` both lines are drawn. */
69
+ strike?: boolean;
70
+ }
71
+ /** Where an image comes from; an empty `url`, `asset` or `path` is one not chosen yet (`imageIsEmpty`). */
72
+ export type ImageSource = {
73
+ url: string;
74
+ } | {
75
+ asset: string;
76
+ } | {
77
+ path: string;
78
+ } | {
79
+ brandLogo: 'primary' | 'inverse' | 'mark';
80
+ };
81
+ /** What a code encodes: literal text or a data path. */
82
+ export type Binding = {
83
+ text: string;
84
+ } | {
85
+ path: string;
86
+ };
87
+ export type BarcodeSymbology = 'code128' | 'ean13' | 'ean8' | 'upca' | 'itf14' | 'code39';
88
+ export interface LayerBase {
89
+ /** Stable, `[a-z][a-z0-9-]{0,31}`: the class `l-<id>` and `data-ff-layer`. Never renamed. */
90
+ id: string;
91
+ /** What the layers panel shows. */
92
+ name: string;
93
+ x: number;
94
+ y: number;
95
+ width: number;
96
+ height: number;
97
+ /** Degrees, clockwise like CSS. */
98
+ rotation: number;
99
+ opacity: number;
100
+ /** False: the layer is not emitted at all. */
101
+ visible: boolean;
102
+ /** Editor only. */
103
+ locked?: boolean;
104
+ /** Emitted inside an if block: shown only when the data path has a value. */
105
+ showWhen?: {
106
+ path: string;
107
+ } | null;
108
+ radius?: number;
109
+ shadow?: Shadow | null;
110
+ stroke?: Stroke | null;
111
+ }
112
+ export interface TextLayer extends LayerBase {
113
+ type: 'text';
114
+ content: TextItem[];
115
+ style: TextStyle;
116
+ /** `fixed` clips, `shrink` makes the text smaller until it fits (`data-ff-fit`), `grow` lets the box grow. */
117
+ sizing: 'fixed' | 'shrink' | 'grow';
118
+ minFontSize?: number;
119
+ maxLines?: number;
120
+ valign: 'top' | 'middle' | 'bottom';
121
+ fill?: Paint | null;
122
+ padding?: number;
123
+ }
124
+ export interface ImageLayer extends LayerBase {
125
+ type: 'image';
126
+ source: ImageSource;
127
+ fit: 'cover' | 'contain' | 'fill';
128
+ /** Which point stays in view when `cover` crops, in percent. */
129
+ focus?: {
130
+ x: number;
131
+ y: number;
132
+ };
133
+ }
134
+ export interface ShapeLayer extends LayerBase {
135
+ type: 'shape';
136
+ shape: 'rectangle' | 'ellipse' | 'line';
137
+ fill: Paint | null;
138
+ }
139
+ export interface QrLayer extends LayerBase {
140
+ type: 'qr';
141
+ value: Binding;
142
+ ecc: 'L' | 'M' | 'Q' | 'H';
143
+ color: Paint;
144
+ background: Paint | null;
145
+ }
146
+ export interface BarcodeLayer extends LayerBase {
147
+ type: 'barcode';
148
+ value: Binding;
149
+ symbology: BarcodeSymbology;
150
+ showText: boolean;
151
+ }
152
+ export type DesignLayer = TextLayer | ImageLayer | ShapeLayer | QrLayer | BarcodeLayer;
153
+ export type LayerType = DesignLayer['type'];
154
+ export interface DesignDocument {
155
+ version: typeof DESIGN_VERSION;
156
+ /** Hash of the body and CSS the emitter last wrote (`designOutputHash`); other code means it was edited. */
157
+ output: string;
158
+ /** The canvas colour; null is transparent, which only shows when `settings.image.transparent` is set. */
159
+ background: Paint | null;
160
+ /** Bottom to top: the stacking order is the order in the HTML. */
161
+ layers: DesignLayer[];
162
+ guides?: {
163
+ x: number[];
164
+ y: number[];
165
+ };
166
+ }
167
+ /** The image a design is drawn on, from `settings.image`. */
168
+ export interface CanvasSize {
169
+ width: number;
170
+ height: number;
171
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * A visual design (plan 16 §4.1): the layer model the editor's canvas edits, stored as
3
+ * `settings.design` and turned into the template's body and CSS by `emitDesign`. Renders never read
4
+ * it; the emitted HTML is the template. Coordinates are CSS pixels of the image, whose size stays in
5
+ * `settings.image`.
6
+ */
7
+ export declare const DESIGN_VERSION = 1;
8
+ /** A colour: a literal, or a brand kit colour by name with the literal it falls back to. */
9
+ export type Paint = {
10
+ color: string;
11
+ } | {
12
+ brand: string;
13
+ fallback: string;
14
+ };
15
+ export interface Shadow {
16
+ x: number;
17
+ y: number;
18
+ blur: number;
19
+ color: string;
20
+ }
21
+ export interface Stroke {
22
+ width: number;
23
+ paint: Paint;
24
+ }
25
+ /** A font: a family by source, or one of the brand kit's two. */
26
+ export type FontChoice = {
27
+ family: string;
28
+ source: 'google' | 'organisation' | 'system';
29
+ } | {
30
+ brand: 'heading' | 'body';
31
+ };
32
+ /** How a data value is shown: through one of the helpers every engine has. */
33
+ export type TextFormat = {
34
+ helper: 'upper' | 'lower' | 'title' | 'money';
35
+ } | {
36
+ helper: 'number';
37
+ decimals: number;
38
+ } | {
39
+ helper: 'date';
40
+ pattern: string;
41
+ } | {
42
+ helper: 'truncate';
43
+ length: number;
44
+ } | {
45
+ helper: 'default';
46
+ value: string;
47
+ };
48
+ /** Text of a text layer: literal text, a data path (a chip in the editor), a line break. */
49
+ export type TextItem = {
50
+ text: string;
51
+ } | {
52
+ path: string;
53
+ format?: TextFormat;
54
+ } | {
55
+ break: true;
56
+ };
57
+ export interface TextStyle {
58
+ font: FontChoice;
59
+ weight: number;
60
+ size: number;
61
+ lineHeight: number;
62
+ letterSpacing: number;
63
+ color: Paint;
64
+ align: 'left' | 'center' | 'right' | 'justify';
65
+ italic?: boolean;
66
+ uppercase?: boolean;
67
+ underline?: boolean;
68
+ /** A line through the text; with `underline` both lines are drawn. */
69
+ strike?: boolean;
70
+ }
71
+ /** Where an image comes from; an empty `url`, `asset` or `path` is one not chosen yet (`imageIsEmpty`). */
72
+ export type ImageSource = {
73
+ url: string;
74
+ } | {
75
+ asset: string;
76
+ } | {
77
+ path: string;
78
+ } | {
79
+ brandLogo: 'primary' | 'inverse' | 'mark';
80
+ };
81
+ /** What a code encodes: literal text or a data path. */
82
+ export type Binding = {
83
+ text: string;
84
+ } | {
85
+ path: string;
86
+ };
87
+ export type BarcodeSymbology = 'code128' | 'ean13' | 'ean8' | 'upca' | 'itf14' | 'code39';
88
+ export interface LayerBase {
89
+ /** Stable, `[a-z][a-z0-9-]{0,31}`: the class `l-<id>` and `data-ff-layer`. Never renamed. */
90
+ id: string;
91
+ /** What the layers panel shows. */
92
+ name: string;
93
+ x: number;
94
+ y: number;
95
+ width: number;
96
+ height: number;
97
+ /** Degrees, clockwise like CSS. */
98
+ rotation: number;
99
+ opacity: number;
100
+ /** False: the layer is not emitted at all. */
101
+ visible: boolean;
102
+ /** Editor only. */
103
+ locked?: boolean;
104
+ /** Emitted inside an if block: shown only when the data path has a value. */
105
+ showWhen?: {
106
+ path: string;
107
+ } | null;
108
+ radius?: number;
109
+ shadow?: Shadow | null;
110
+ stroke?: Stroke | null;
111
+ }
112
+ export interface TextLayer extends LayerBase {
113
+ type: 'text';
114
+ content: TextItem[];
115
+ style: TextStyle;
116
+ /** `fixed` clips, `shrink` makes the text smaller until it fits (`data-ff-fit`), `grow` lets the box grow. */
117
+ sizing: 'fixed' | 'shrink' | 'grow';
118
+ minFontSize?: number;
119
+ maxLines?: number;
120
+ valign: 'top' | 'middle' | 'bottom';
121
+ fill?: Paint | null;
122
+ padding?: number;
123
+ }
124
+ export interface ImageLayer extends LayerBase {
125
+ type: 'image';
126
+ source: ImageSource;
127
+ fit: 'cover' | 'contain' | 'fill';
128
+ /** Which point stays in view when `cover` crops, in percent. */
129
+ focus?: {
130
+ x: number;
131
+ y: number;
132
+ };
133
+ }
134
+ export interface ShapeLayer extends LayerBase {
135
+ type: 'shape';
136
+ shape: 'rectangle' | 'ellipse' | 'line';
137
+ fill: Paint | null;
138
+ }
139
+ export interface QrLayer extends LayerBase {
140
+ type: 'qr';
141
+ value: Binding;
142
+ ecc: 'L' | 'M' | 'Q' | 'H';
143
+ color: Paint;
144
+ background: Paint | null;
145
+ }
146
+ export interface BarcodeLayer extends LayerBase {
147
+ type: 'barcode';
148
+ value: Binding;
149
+ symbology: BarcodeSymbology;
150
+ showText: boolean;
151
+ }
152
+ export type DesignLayer = TextLayer | ImageLayer | ShapeLayer | QrLayer | BarcodeLayer;
153
+ export type LayerType = DesignLayer['type'];
154
+ export interface DesignDocument {
155
+ version: typeof DESIGN_VERSION;
156
+ /** Hash of the body and CSS the emitter last wrote (`designOutputHash`); other code means it was edited. */
157
+ output: string;
158
+ /** The canvas colour; null is transparent, which only shows when `settings.image.transparent` is set. */
159
+ background: Paint | null;
160
+ /** Bottom to top: the stacking order is the order in the HTML. */
161
+ layers: DesignLayer[];
162
+ guides?: {
163
+ x: number[];
164
+ y: number[];
165
+ };
166
+ }
167
+ /** The image a design is drawn on, from `settings.image`. */
168
+ export interface CanvasSize {
169
+ width: number;
170
+ height: number;
171
+ }
@@ -0,0 +1,32 @@
1
+ import type { EngineId } from './types.cjs';
2
+ /**
3
+ * Template syntax per engine, for code that writes templates: the snippet library (spec 06 §2) and
4
+ * the design emitter (plan 16 §4.2). Jinja2 passes filter arguments in parentheses, Liquid after a
5
+ * colon (and cannot build object literals, so options are keyword arguments), Handlebars calls
6
+ * helpers with positional arguments and a hash.
7
+ */
8
+ /** Handlebars has no loop variable: inside `{{#each}}` the item's fields are read directly. */
9
+ export declare const loop: (engine: EngineId, item: string, list: string, body: string) => string;
10
+ /** An expression, optionally through one filter written the Jinja2 way (`date('dd.MM.yyyy')`). */
11
+ export declare const val: (engine: EngineId, expr: string, filter?: string) => string;
12
+ export declare const ifElse: (engine: EngineId, cond: string, then: string, otherwise: string) => string;
13
+ /** A `t` call with optional placeholder params (`{ rate: 'invoice.vat_rate' }`, values are expressions). */
14
+ export declare const tr: (engine: EngineId, key: string, params?: Record<string, string>) => string;
15
+ /** `body` only when `cond` has a value. */
16
+ export declare const ifBlock: (engine: EngineId, cond: string, body: string) => string;
17
+ /**
18
+ * A string literal the engine reads back as `value`. Liquid has no escapes, so it takes whichever
19
+ * quote the value lacks and turns an apostrophe into a typographic one when it contains both.
20
+ */
21
+ export declare function stringLiteral(engine: EngineId, value: string): string;
22
+ /**
23
+ * `input` through a filter with literal-or-expression arguments, as one output tag: Jinja2
24
+ * `{{ x | f(a, b) }}`, Liquid `{{ x | f: a, b }}`, Handlebars `{{f x a b}}`.
25
+ */
26
+ export declare function filterCall(engine: EngineId, input: string, name: string, args?: string[]): string;
27
+ /**
28
+ * A helper that takes an input and named options, as one output tag: Jinja2
29
+ * `{{ f(x, { a: 1 }) }}`, Liquid `{{ x | f: a: 1 }}`, Handlebars `{{f x a=1}}`. Option values are
30
+ * expressions or literals the caller wrote with `stringLiteral`.
31
+ */
32
+ export declare function helperCall(engine: EngineId, name: string, input: string, options?: Record<string, string>): string;
@@ -0,0 +1,32 @@
1
+ import type { EngineId } from './types.js';
2
+ /**
3
+ * Template syntax per engine, for code that writes templates: the snippet library (spec 06 §2) and
4
+ * the design emitter (plan 16 §4.2). Jinja2 passes filter arguments in parentheses, Liquid after a
5
+ * colon (and cannot build object literals, so options are keyword arguments), Handlebars calls
6
+ * helpers with positional arguments and a hash.
7
+ */
8
+ /** Handlebars has no loop variable: inside `{{#each}}` the item's fields are read directly. */
9
+ export declare const loop: (engine: EngineId, item: string, list: string, body: string) => string;
10
+ /** An expression, optionally through one filter written the Jinja2 way (`date('dd.MM.yyyy')`). */
11
+ export declare const val: (engine: EngineId, expr: string, filter?: string) => string;
12
+ export declare const ifElse: (engine: EngineId, cond: string, then: string, otherwise: string) => string;
13
+ /** A `t` call with optional placeholder params (`{ rate: 'invoice.vat_rate' }`, values are expressions). */
14
+ export declare const tr: (engine: EngineId, key: string, params?: Record<string, string>) => string;
15
+ /** `body` only when `cond` has a value. */
16
+ export declare const ifBlock: (engine: EngineId, cond: string, body: string) => string;
17
+ /**
18
+ * A string literal the engine reads back as `value`. Liquid has no escapes, so it takes whichever
19
+ * quote the value lacks and turns an apostrophe into a typographic one when it contains both.
20
+ */
21
+ export declare function stringLiteral(engine: EngineId, value: string): string;
22
+ /**
23
+ * `input` through a filter with literal-or-expression arguments, as one output tag: Jinja2
24
+ * `{{ x | f(a, b) }}`, Liquid `{{ x | f: a, b }}`, Handlebars `{{f x a b}}`.
25
+ */
26
+ export declare function filterCall(engine: EngineId, input: string, name: string, args?: string[]): string;
27
+ /**
28
+ * A helper that takes an input and named options, as one output tag: Jinja2
29
+ * `{{ f(x, { a: 1 }) }}`, Liquid `{{ x | f: a: 1 }}`, Handlebars `{{f x a=1}}`. Option values are
30
+ * expressions or literals the caller wrote with `stringLiteral`.
31
+ */
32
+ export declare function helperCall(engine: EngineId, name: string, input: string, options?: Record<string, string>): string;
@@ -0,0 +1,181 @@
1
+ import { type EinvoiceProfile, type VatCategory } from './schema.cjs';
2
+ /**
3
+ * Reads an `_invoice` block against EN 16931 (spec 17 §4.3): the shape first, then the rules a
4
+ * request can be held to before anything is rendered. What comes out is either the invoice with
5
+ * every number as exact decimal text, ready for the XML, or the list of what is wrong, each finding
6
+ * with the field and the rule it breaks.
7
+ *
8
+ * Nothing is computed for the caller and nothing is rounded into place. A total that is a cent off
9
+ * is a finding, because an e-invoice whose XML disagrees with the seller's books is a tax problem,
10
+ * and correcting it here would hide exactly that.
11
+ *
12
+ * The full rule set (the official schematron, some 430 assertions) runs in the sidecar on every
13
+ * document. This is the part worth knowing early: it needs no JVM, so the gateway answers a bad
14
+ * request at once, and the editor and the CLI say the same thing offline.
15
+ */
16
+ export interface InvoiceProblem {
17
+ /** What kind of finding, for a caller that words it in its own language. */
18
+ code: InvoiceProblemCode;
19
+ /** The field inside `_invoice`, dotted, list items as `lines[1]` (zero-based). */
20
+ path: string;
21
+ /** The rule of EN 16931 (`BR-CO-15`) or of the French specifications (`BR-FR-10`), where one applies. */
22
+ rule?: string;
23
+ /** The same in English, naming the field and the rule. */
24
+ message: string;
25
+ /** The values `message` was built from. */
26
+ args: Record<string, string | number>;
27
+ }
28
+ export type InvoiceProblemCode = 'missing' | 'invalid' | 'not_a_number' | 'decimals' | 'negative' | 'code_list' | 'date' | 'date_order' | 'scheme_required' | 'vat_id_prefix' | 'iban' | 'sum' | 'breakdown_missing' | 'breakdown_duplicate' | 'rate_required' | 'rate_positive' | 'rate_zero' | 'rate_forbidden' | 'amount_zero' | 'exemption_reason_required' | 'exemption_reason_forbidden' | 'seller_tax_id_required' | 'buyer_tax_id_required' | 'vat_id_forbidden' | 'only_category' | 'delivery_required' | 'account_required' | 'means_code_required' | 'due_or_terms_required' | 'seller_identifier_required' | 'siren_mismatch' | 'fr_seller_siren' | 'fr_buyer_siren' | 'fr_operation' | 'fr_vat_on_debits' | 'profile';
29
+ export interface Identifier {
30
+ value: string;
31
+ scheme?: string;
32
+ }
33
+ export interface InvoiceAddress {
34
+ street?: string;
35
+ street2?: string;
36
+ street3?: string;
37
+ postcode?: string;
38
+ city?: string;
39
+ country: string;
40
+ subdivision?: string;
41
+ }
42
+ export interface InvoiceParty {
43
+ name: string;
44
+ trading_name?: string;
45
+ id?: Identifier;
46
+ legal_registration?: Identifier;
47
+ vat_id?: string;
48
+ address: InvoiceAddress;
49
+ contact?: {
50
+ name?: string;
51
+ phone?: string;
52
+ email?: string;
53
+ };
54
+ electronic_address?: Required<Identifier>;
55
+ }
56
+ export interface InvoiceAllowance {
57
+ amount: string;
58
+ reason?: string;
59
+ reason_code?: string;
60
+ base_amount?: string;
61
+ percent?: string;
62
+ }
63
+ export interface InvoiceTax {
64
+ category: VatCategory;
65
+ /** Percent as decimal text; absent for category O only. */
66
+ rate?: string;
67
+ }
68
+ export interface InvoiceLine {
69
+ id: string;
70
+ name: string;
71
+ description?: string;
72
+ quantity: string;
73
+ unit_code: string;
74
+ net_price: string;
75
+ price_base_quantity?: string;
76
+ net_amount: string;
77
+ tax: InvoiceTax;
78
+ period?: {
79
+ start?: string;
80
+ end?: string;
81
+ };
82
+ allowances: InvoiceAllowance[];
83
+ charges: InvoiceAllowance[];
84
+ buyer_accounting_reference?: string;
85
+ note?: string;
86
+ seller_item_id?: string;
87
+ buyer_item_id?: string;
88
+ standard_item_id?: Identifier;
89
+ order_line_reference?: string;
90
+ }
91
+ export interface InvoiceBreakdown extends InvoiceTax {
92
+ basis: string;
93
+ amount: string;
94
+ exemption_reason?: string;
95
+ exemption_reason_code?: string;
96
+ }
97
+ /**
98
+ * An invoice that passed: every amount is decimal text with at most two places, every code is in
99
+ * its list, and the totals add up. The XML is written from this and from nothing else.
100
+ */
101
+ export interface Invoice {
102
+ number: string;
103
+ issue_date: string;
104
+ type_code: string;
105
+ currency: string;
106
+ accounting_currency?: string;
107
+ buyer_reference?: string;
108
+ order_reference?: string;
109
+ contract_reference?: string;
110
+ notes: Array<{
111
+ text: string;
112
+ subject_code?: string;
113
+ }>;
114
+ period?: {
115
+ start?: string;
116
+ end?: string;
117
+ };
118
+ preceding_invoices: Array<{
119
+ number: string;
120
+ issue_date?: string;
121
+ }>;
122
+ /** The business process (BT-23); French invoices carry their billing framework here. */
123
+ business_process?: string;
124
+ seller: InvoiceParty & {
125
+ tax_number?: string;
126
+ legal_info?: string;
127
+ vat_on_debits?: boolean;
128
+ };
129
+ buyer: InvoiceParty;
130
+ delivery?: {
131
+ name?: string;
132
+ date?: string;
133
+ address?: InvoiceAddress;
134
+ };
135
+ lines: InvoiceLine[];
136
+ allowances: Array<InvoiceAllowance & {
137
+ tax: InvoiceTax;
138
+ }>;
139
+ charges: Array<InvoiceAllowance & {
140
+ tax: InvoiceTax;
141
+ }>;
142
+ breakdown: InvoiceBreakdown[];
143
+ totals: {
144
+ line_net: string;
145
+ allowances?: string;
146
+ charges?: string;
147
+ tax_basis: string;
148
+ tax_total: string;
149
+ tax_total_accounting?: string;
150
+ grand: string;
151
+ prepaid?: string;
152
+ rounding?: string;
153
+ due: string;
154
+ };
155
+ payment?: {
156
+ means_code?: string;
157
+ iban?: string;
158
+ bic?: string;
159
+ account_name?: string;
160
+ reference?: string;
161
+ terms?: string;
162
+ due_date?: string;
163
+ mandate_id?: string;
164
+ creditor_id?: string;
165
+ debited_iban?: string;
166
+ };
167
+ }
168
+ export type InvoiceCheck = {
169
+ ok: true;
170
+ invoice: Invoice;
171
+ problems: [];
172
+ } | {
173
+ ok: false;
174
+ invoice: null;
175
+ problems: InvoiceProblem[];
176
+ };
177
+ export interface CheckOptions {
178
+ /** The profile the XML will be written in; `basic` cannot carry everything `en16931` can. */
179
+ profile?: EinvoiceProfile;
180
+ }
181
+ export declare function checkInvoice(data: unknown, options?: CheckOptions): InvoiceCheck;