@invompt/invoml 1.0.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +190 -0
- package/README.md +320 -0
- package/dist/cli/invoml.d.ts +2 -0
- package/dist/cli/invoml.js +78 -0
- package/dist/src/calculator.d.ts +3 -0
- package/dist/src/calculator.js +164 -0
- package/dist/src/discounts.d.ts +6 -0
- package/dist/src/discounts.js +46 -0
- package/dist/src/format.d.ts +3 -0
- package/dist/src/format.js +14 -0
- package/dist/src/html-css.d.ts +4 -0
- package/dist/src/html-css.js +300 -0
- package/dist/src/html-renderer.d.ts +5 -0
- package/dist/src/html-renderer.js +365 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.js +8 -0
- package/dist/src/markdown.d.ts +8 -0
- package/dist/src/markdown.js +89 -0
- package/dist/src/parser.d.ts +10 -0
- package/dist/src/parser.js +16 -0
- package/dist/src/rounding.d.ts +5 -0
- package/dist/src/rounding.js +24 -0
- package/dist/src/schema.d.ts +6 -0
- package/dist/src/schema.js +33 -0
- package/dist/src/serializer.d.ts +8 -0
- package/dist/src/serializer.js +181 -0
- package/dist/src/style.d.ts +21 -0
- package/dist/src/style.js +70 -0
- package/dist/src/tax.d.ts +3 -0
- package/dist/src/tax.js +40 -0
- package/dist/src/types.d.ts +132 -0
- package/dist/src/types.js +10 -0
- package/invoml-v1.0.schema.json +258 -0
- package/package.json +65 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/invompt/InvoML/blob/main/invoml-v1.0.schema.json",
|
|
4
|
+
"title": "InvoML v1.0",
|
|
5
|
+
"description": "Invoice Markup Language v1.0 — compact invoice format for AI structured output",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["$invoml", "meta", "items"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"$invoml": { "type": "string", "const": "1.0", "description": "InvoML specification version. Must be \"1.0\"." },
|
|
10
|
+
"meta": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"description": "Document metadata — type, number, dates, currency, and tax configuration.",
|
|
13
|
+
"required": ["documentType", "number", "issueDate", "currency"],
|
|
14
|
+
"properties": {
|
|
15
|
+
"documentType": { "type": "string", "enum": ["invoice", "quote", "credit_note", "receipt"], "description": "The type of commercial document." },
|
|
16
|
+
"number": { "type": "string", "minLength": 1, "description": "Unique document identifier (e.g. INV-2026-001)." },
|
|
17
|
+
"issueDate": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$", "description": "Date the document was issued (YYYY-MM-DD)." },
|
|
18
|
+
"dueDate": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$", "description": "Payment due date (YYYY-MM-DD). Optional." },
|
|
19
|
+
"expiryDate": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$", "description": "Expiry date for quotes and estimates (YYYY-MM-DD). Optional." },
|
|
20
|
+
"currency": { "type": "string", "pattern": "^[A-Z]{3}$", "description": "ISO 4217 currency code (e.g. USD, EUR, GBP)." },
|
|
21
|
+
"locale": { "type": "string", "description": "BCP 47 locale tag for number/date formatting (e.g. en-US, de-DE). Optional." },
|
|
22
|
+
"reference": { "type": "string", "description": "External reference such as a purchase order number. Optional." },
|
|
23
|
+
"creditNoteReference": { "type": "string", "description": "Invoice number this credit note references. Required when documentType is credit_note." },
|
|
24
|
+
"tax": {
|
|
25
|
+
"description": "Tax configuration. Either a simple single-rate tax or a full multi-category tax system.",
|
|
26
|
+
"oneOf": [
|
|
27
|
+
{
|
|
28
|
+
"type": "object",
|
|
29
|
+
"description": "Simple tax — a single label and rate applied to all items.",
|
|
30
|
+
"required": ["label", "rate"],
|
|
31
|
+
"properties": {
|
|
32
|
+
"label": { "type": "string", "minLength": 1, "description": "Display name for the tax (e.g. VAT, GST, Sales Tax)." },
|
|
33
|
+
"rate": { "type": "number", "description": "Tax rate as a percentage (e.g. 10 for 10%)." },
|
|
34
|
+
"inclusive": { "type": "boolean", "description": "If true, item prices already include this tax." }
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"type": "object",
|
|
39
|
+
"description": "Full tax — multiple categories with individual rates and behaviors.",
|
|
40
|
+
"required": ["categories"],
|
|
41
|
+
"properties": {
|
|
42
|
+
"system": { "type": "string", "description": "Name of the tax system (e.g. GST, VAT). For display purposes." },
|
|
43
|
+
"compound": { "type": "boolean", "description": "If true, all tax categories apply to the full base (not split by line item category)." },
|
|
44
|
+
"inclusive": { "type": "boolean", "description": "If true, item prices already include tax. Tax is backed out during calculation." },
|
|
45
|
+
"categories": {
|
|
46
|
+
"type": "array",
|
|
47
|
+
"description": "List of tax categories. At least one must be marked as default.",
|
|
48
|
+
"minItems": 1,
|
|
49
|
+
"items": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"description": "A single tax category with its rate and behavior flags.",
|
|
52
|
+
"required": ["id", "label", "rate"],
|
|
53
|
+
"properties": {
|
|
54
|
+
"id": { "type": "string", "minLength": 1, "description": "Unique identifier for this category. Referenced by items via taxCategory." },
|
|
55
|
+
"label": { "type": "string", "minLength": 1, "description": "Display name (e.g. Standard Rate, Reduced Rate, Exempt)." },
|
|
56
|
+
"rate": { "type": "number", "description": "Tax rate as a percentage." },
|
|
57
|
+
"default": { "type": "boolean", "description": "If true, items without an explicit taxCategory use this category." },
|
|
58
|
+
"exempt": { "type": "boolean", "description": "If true, this category carries a 0% effective tax regardless of rate." },
|
|
59
|
+
"reverseCharge": { "type": "boolean", "description": "If true, tax is reported but not collected. Buyer self-assesses." },
|
|
60
|
+
"withholding": { "type": "boolean", "description": "If true, this tax is withheld by the buyer and remitted to the tax authority." },
|
|
61
|
+
"inclusive": { "type": "boolean", "description": "Reserved. Inclusive tax is set at the top-level tax configuration, not per category." }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
"if": { "properties": { "documentType": { "const": "credit_note" } } },
|
|
71
|
+
"then": { "required": ["documentType", "number", "issueDate", "currency", "creditNoteReference"] }
|
|
72
|
+
},
|
|
73
|
+
"from": {
|
|
74
|
+
"type": "object",
|
|
75
|
+
"description": "The issuer (seller, freelancer, service provider). Use content for free-form Markdown or structured fields.",
|
|
76
|
+
"properties": {
|
|
77
|
+
"content": { "type": "string", "description": "Free-form text with Markdown formatting (bold, italic, lists, links). Contains the full party display text. Example: '**Acme Corp**\\n123 Main St\\nNew York, NY 10001'. If provided, structured fields below are ignored for rendering." },
|
|
78
|
+
"name": { "type": "string", "description": "Legal or business name." },
|
|
79
|
+
"address": { "type": "string", "description": "Full postal address." },
|
|
80
|
+
"taxId": { "type": "string", "description": "Tax identification number (VAT number, EIN, RUC, etc.)." },
|
|
81
|
+
"email": { "type": "string", "format": "email", "description": "Contact email address." },
|
|
82
|
+
"phone": { "type": "string", "description": "Contact phone number." },
|
|
83
|
+
"website": { "type": "string", "format": "uri", "description": "Website URL." },
|
|
84
|
+
"businessNumber": { "type": "string", "description": "Business registration number (ABN, SIRET, etc.)." },
|
|
85
|
+
"attention": { "type": "string", "description": "Attention line — name of a specific person." },
|
|
86
|
+
"countryCode": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "ISO 3166-1 alpha-2 country code." }
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"to": {
|
|
90
|
+
"type": "object",
|
|
91
|
+
"description": "The recipient (buyer, client, customer).",
|
|
92
|
+
"properties": {
|
|
93
|
+
"content": { "type": "string", "description": "Free-form text with Markdown formatting (bold, italic, lists, links). Contains the full party display text. Example: '**Client LLC**\\n456 Oak Ave\\nSan Francisco, CA 94102'. If provided, structured fields below are ignored for rendering." },
|
|
94
|
+
"name": { "type": "string", "description": "Legal or business name." },
|
|
95
|
+
"address": { "type": "string", "description": "Full postal address." },
|
|
96
|
+
"taxId": { "type": "string", "description": "Tax identification number." },
|
|
97
|
+
"email": { "type": "string", "format": "email", "description": "Contact email address." },
|
|
98
|
+
"phone": { "type": "string", "description": "Contact phone number." },
|
|
99
|
+
"website": { "type": "string", "format": "uri", "description": "Website URL." },
|
|
100
|
+
"businessNumber": { "type": "string", "description": "Business registration number." },
|
|
101
|
+
"attention": { "type": "string", "description": "Attention line — name of a specific person." },
|
|
102
|
+
"countryCode": { "type": "string", "pattern": "^[A-Z]{2}$", "description": "ISO 3166-1 alpha-2 country code." }
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"items": {
|
|
106
|
+
"type": "array",
|
|
107
|
+
"description": "Line items on the document. At least one is required.",
|
|
108
|
+
"minItems": 1,
|
|
109
|
+
"items": {
|
|
110
|
+
"type": "object",
|
|
111
|
+
"description": "A single line item.",
|
|
112
|
+
"required": ["description", "quantity", "unitPrice"],
|
|
113
|
+
"properties": {
|
|
114
|
+
"description": { "type": "string", "minLength": 1, "description": "Description of the good or service." },
|
|
115
|
+
"quantity": { "type": "number", "description": "Quantity (e.g. 10 hours, 3 units)." },
|
|
116
|
+
"unitPrice": { "type": "number", "description": "Price per unit before discounts." },
|
|
117
|
+
"unit": { "type": "string", "description": "Unit label (e.g. hours, units, kg). Optional." },
|
|
118
|
+
"discount": {
|
|
119
|
+
"description": "Line-level discount. Either a shorthand string (e.g. \"10%\" or \"50\") or a structured object. Omit this field entirely when no discount applies. Do not set to null.",
|
|
120
|
+
"oneOf": [
|
|
121
|
+
{ "type": "string", "pattern": "^(\\d+(\\.\\d+)?%|\\d+(\\.\\d+)?)$", "description": "Shorthand: \"10%\" for percentage or \"50\" for fixed amount." },
|
|
122
|
+
{
|
|
123
|
+
"type": "object",
|
|
124
|
+
"description": "Structured discount with explicit type and value.",
|
|
125
|
+
"required": ["type", "value"],
|
|
126
|
+
"properties": {
|
|
127
|
+
"type": { "type": "string", "enum": ["percentage", "fixed"], "description": "Whether the discount is a percentage of the line total or a fixed amount." },
|
|
128
|
+
"value": { "type": "number", "description": "The discount value (percentage points or currency amount)." }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
"taxCategory": { "type": "string", "description": "ID of the tax category this item belongs to. References a category in meta.tax.categories. Omit when using the default tax category. Do not set to null." },
|
|
134
|
+
"amount": { "type": "number", "description": "Computed by the runtime. Line total after line-level discount. Do not set manually." },
|
|
135
|
+
"taxAmount": { "type": "number", "description": "Computed by the runtime. Tax amount for this line. Do not set manually." }
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
"discounts": {
|
|
140
|
+
"type": "array",
|
|
141
|
+
"description": "Invoice-level discounts applied after the subtotal. Applied in order (cascading — each discount reduces the base for the next).",
|
|
142
|
+
"items": {
|
|
143
|
+
"type": "object",
|
|
144
|
+
"description": "An invoice-level discount.",
|
|
145
|
+
"required": ["type", "value"],
|
|
146
|
+
"properties": {
|
|
147
|
+
"type": { "type": "string", "enum": ["percentage", "fixed"], "description": "Whether the discount is a percentage or a fixed amount." },
|
|
148
|
+
"value": { "type": "number", "description": "The discount value." },
|
|
149
|
+
"label": { "type": "string", "description": "Display label (e.g. Early Payment Discount, Loyalty Discount). Optional." }
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
"payment": {
|
|
154
|
+
"type": "object",
|
|
155
|
+
"description": "Payment instructions. Use content for free-form Markdown or structured fields for specific payment methods.",
|
|
156
|
+
"properties": {
|
|
157
|
+
"title": { "type": "string", "description": "Custom title for the payment section." },
|
|
158
|
+
"content": { "type": "string", "description": "Free-form Markdown payment instructions. If provided, structured fields are ignored in rendering." },
|
|
159
|
+
"method": { "type": "string", "enum": ["bank-international", "bank-domestic", "crypto", "card", "other"], "description": "Payment method type." },
|
|
160
|
+
"beneficiary": { "type": "string", "description": "Name of the payment beneficiary." },
|
|
161
|
+
"bank": { "type": "string", "description": "Bank name." },
|
|
162
|
+
"iban": { "type": "string", "description": "International Bank Account Number." },
|
|
163
|
+
"swift": { "type": "string", "description": "SWIFT/BIC code." },
|
|
164
|
+
"routingNumber": { "type": "string", "description": "Bank routing number (US domestic transfers)." },
|
|
165
|
+
"accountNumber": { "type": "string", "description": "Bank account number." },
|
|
166
|
+
"cryptoAddress": { "type": "string", "description": "Cryptocurrency wallet address." },
|
|
167
|
+
"cryptoNetwork": { "type": "string", "description": "Cryptocurrency network (e.g. Bitcoin, Ethereum, Solana)." }
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
"sections": {
|
|
171
|
+
"type": "object",
|
|
172
|
+
"description": "Custom named sections for additional content (terms, scope of work, etc.). Keys must be alphanumeric with hyphens/underscores.",
|
|
173
|
+
"patternProperties": {
|
|
174
|
+
"^[a-zA-Z0-9_-]+$": {
|
|
175
|
+
"type": "object",
|
|
176
|
+
"description": "A custom content section.",
|
|
177
|
+
"required": ["title", "content"],
|
|
178
|
+
"properties": {
|
|
179
|
+
"title": { "type": "string", "description": "Section heading." },
|
|
180
|
+
"content": { "type": "string", "description": "Section body in Markdown." }
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
"additionalProperties": false
|
|
185
|
+
},
|
|
186
|
+
"notes": { "type": "string", "description": "Free-form notes displayed at the bottom of the document." },
|
|
187
|
+
"style": {
|
|
188
|
+
"type": "object",
|
|
189
|
+
"description": "Presentation hints for renderers. Controls block ordering, visual arrangement, and appearance. Machine consumers MUST ignore this field.",
|
|
190
|
+
"properties": {
|
|
191
|
+
"template": { "type": "string", "description": "Named visual theme shorthand (e.g. professional, minimal, modern). Renderers may ignore unknown names." },
|
|
192
|
+
"order": {
|
|
193
|
+
"type": "array",
|
|
194
|
+
"description": "Explicit block rendering sequence. Blocks not listed are not rendered. Valid block names: header, from, to, items, totals, payment, notes, section:{key} (where {key} matches a key in sections). Example: [\"header\", \"from\", \"to\", \"items\", \"totals\", \"section:scope\", \"payment\", \"notes\"]",
|
|
195
|
+
"items": { "type": "string", "minLength": 1 },
|
|
196
|
+
"minItems": 1,
|
|
197
|
+
"uniqueItems": true
|
|
198
|
+
},
|
|
199
|
+
"properties": {
|
|
200
|
+
"type": "object",
|
|
201
|
+
"description": "Document-level CSS-compatible style hints (e.g. font-family, --accent).",
|
|
202
|
+
"additionalProperties": { "type": "string" }
|
|
203
|
+
},
|
|
204
|
+
"blocks": {
|
|
205
|
+
"type": "object",
|
|
206
|
+
"description": "Per-block CSS-compatible style hints. Keys must be valid block names (header, from, to, items, totals, payment, notes) or section:{key} references.",
|
|
207
|
+
"additionalProperties": {
|
|
208
|
+
"type": "object",
|
|
209
|
+
"additionalProperties": { "type": "string" }
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
"additionalProperties": false
|
|
214
|
+
},
|
|
215
|
+
"totals": {
|
|
216
|
+
"type": "object",
|
|
217
|
+
"description": "Computed totals. Populated by the runtime after calculation. AI models should NOT set these fields.",
|
|
218
|
+
"properties": {
|
|
219
|
+
"subtotal": { "type": "number", "description": "Sum of all line item amounts before invoice-level discounts." },
|
|
220
|
+
"discountDetails": {
|
|
221
|
+
"type": "array",
|
|
222
|
+
"description": "Breakdown of each invoice-level discount applied.",
|
|
223
|
+
"items": {
|
|
224
|
+
"type": "object",
|
|
225
|
+
"properties": {
|
|
226
|
+
"label": { "type": "string", "description": "Discount label." },
|
|
227
|
+
"amount": { "type": "number", "description": "Discount amount in currency." }
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
"afterDiscounts": { "type": "number", "description": "Subtotal minus all invoice-level discounts." },
|
|
232
|
+
"taxDetails": {
|
|
233
|
+
"type": "array",
|
|
234
|
+
"description": "Per-category tax breakdown.",
|
|
235
|
+
"items": {
|
|
236
|
+
"type": "object",
|
|
237
|
+
"properties": {
|
|
238
|
+
"category": { "type": "string", "description": "Tax category ID." },
|
|
239
|
+
"label": { "type": "string", "description": "Tax category display label." },
|
|
240
|
+
"rate": { "type": "number", "description": "Tax rate as a percentage." },
|
|
241
|
+
"base": { "type": "number", "description": "Taxable base amount for this category." },
|
|
242
|
+
"amount": { "type": "number", "description": "Computed tax amount." },
|
|
243
|
+
"inclusive": { "type": "boolean", "description": "Whether this tax is already included in item prices." },
|
|
244
|
+
"withholding": { "type": "boolean", "description": "Whether this tax is withheld by the buyer and remitted to the tax authority." }
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
"taxTotal": { "type": "number", "description": "Total tax amount (excluding withholding)." },
|
|
249
|
+
"withholdingTotal": { "type": "number", "description": "Total withholding tax amount." },
|
|
250
|
+
"total": { "type": "number", "description": "Grand total: afterDiscounts + taxTotal - withholdingTotal (or afterDiscounts if inclusive)." },
|
|
251
|
+
"prepaidAmount": { "type": "number", "description": "Amount already paid. Deducted from total to compute amountDue." },
|
|
252
|
+
"amountDue": { "type": "number", "description": "Final amount owed: total - prepaidAmount." },
|
|
253
|
+
"currencySymbol": { "type": "string", "description": "Currency symbol for display (e.g. $, EUR, GBP). Set by the runtime." },
|
|
254
|
+
"locale": { "type": "string", "description": "Locale used for number formatting. Set by the runtime." }
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@invompt/invoml",
|
|
3
|
+
"version": "1.0.0-alpha.4",
|
|
4
|
+
"description": "InvoML — Invoice Markup Language: parse, validate, calculate",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/src/index.js",
|
|
7
|
+
"types": "dist/src/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/src/index.d.ts",
|
|
11
|
+
"import": "./dist/src/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./invoml-v1.0.schema.json": "./invoml-v1.0.schema.json",
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"invoml-v1.0.schema.json",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE"
|
|
21
|
+
],
|
|
22
|
+
"bin": {
|
|
23
|
+
"invoml": "dist/cli/invoml.js"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc --project tsconfig.build.json",
|
|
27
|
+
"prepublishOnly": "npm run build && npm test",
|
|
28
|
+
"test": "vitest run",
|
|
29
|
+
"test:watch": "vitest",
|
|
30
|
+
"cli": "tsx cli/invoml.ts"
|
|
31
|
+
},
|
|
32
|
+
"repository": {
|
|
33
|
+
"type": "git",
|
|
34
|
+
"url": "git+https://github.com/invompt/InvoML.git"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/invompt/InvoML",
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/invompt/InvoML/issues"
|
|
39
|
+
},
|
|
40
|
+
"keywords": [
|
|
41
|
+
"invoice",
|
|
42
|
+
"invoml",
|
|
43
|
+
"invoice-markup-language",
|
|
44
|
+
"ai",
|
|
45
|
+
"structured-output",
|
|
46
|
+
"json-schema",
|
|
47
|
+
"billing",
|
|
48
|
+
"tax"
|
|
49
|
+
],
|
|
50
|
+
"engines": {
|
|
51
|
+
"node": ">=18"
|
|
52
|
+
},
|
|
53
|
+
"license": "Apache-2.0",
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/node": "^25.5.0",
|
|
56
|
+
"tsx": "^4.19.0",
|
|
57
|
+
"typescript": "^5.7.0",
|
|
58
|
+
"vitest": "^3.0.0"
|
|
59
|
+
},
|
|
60
|
+
"dependencies": {
|
|
61
|
+
"ajv": "^8.17.0",
|
|
62
|
+
"ajv-formats": "^3.0.0",
|
|
63
|
+
"decimal.js": "^10.5.0"
|
|
64
|
+
}
|
|
65
|
+
}
|