@nachoggodino/cello 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/BYLAWS.md +446 -0
  2. package/CHANGELOG.md +11 -0
  3. package/LICENSE +12 -0
  4. package/README.md +211 -0
  5. package/dist/cli/cli.d.ts +16 -0
  6. package/dist/cli/cli.js +360 -0
  7. package/dist/cli/serve.d.ts +15 -0
  8. package/dist/cli/serve.js +226 -0
  9. package/dist/evaluator/evaluate.d.ts +2 -0
  10. package/dist/evaluator/evaluate.js +129 -0
  11. package/dist/evaluator/formula.d.ts +13 -0
  12. package/dist/evaluator/formula.js +141 -0
  13. package/dist/formatter/format.d.ts +1 -0
  14. package/dist/formatter/format.js +112 -0
  15. package/dist/index.d.ts +8 -0
  16. package/dist/index.js +6 -0
  17. package/dist/parser/parse.d.ts +2 -0
  18. package/dist/parser/parse.js +552 -0
  19. package/dist/renderer/render.d.ts +2 -0
  20. package/dist/renderer/render.js +295 -0
  21. package/dist/serializer/serialize.d.ts +2 -0
  22. package/dist/serializer/serialize.js +104 -0
  23. package/dist/shared/types.d.ts +88 -0
  24. package/dist/shared/types.js +1 -0
  25. package/dist/shared/utils.d.ts +16 -0
  26. package/dist/shared/utils.js +142 -0
  27. package/dist/validator/validate.d.ts +8 -0
  28. package/dist/validator/validate.js +10 -0
  29. package/dist/version.d.ts +1 -0
  30. package/dist/version.js +1 -0
  31. package/docs/ARCHITECTURE.md +43 -0
  32. package/docs/CLI.md +58 -0
  33. package/docs/COMPLIANCE.md +82 -0
  34. package/docs/ERROR_MODEL.md +25 -0
  35. package/docs/FORMULA_SUPPORT.md +33 -0
  36. package/docs/SPEC.md +723 -0
  37. package/docs/SYNTAX_HIGHLIGHTING.md +91 -0
  38. package/examples/advanced_kpi.cel +42 -0
  39. package/examples/basic.cel +8 -0
  40. package/examples/feature_showcase.cel +37 -0
  41. package/package.json +96 -0
  42. package/syntaxes/cel.language-configuration.json +31 -0
  43. package/syntaxes/cel.tmLanguage.json +250 -0
package/BYLAWS.md ADDED
@@ -0,0 +1,446 @@
1
+ <div align="center">
2
+ <img src="playground/public/cello-logo-bylaws.svg" alt="cello" width="180" />
3
+ <h1>Cello Bylaws v1.0</h1>
4
+ <p><strong>Plain-text spreadsheets for humans, agents, diffs, and HTML previews.</strong></p>
5
+ <p>
6
+ <img alt="Format: .cel" src="https://img.shields.io/badge/format-.cel-e7662f?style=flat-square" />
7
+ <img alt="Syntax: BYLAWS first" src="https://img.shields.io/badge/syntax-BYLAWS--first-496f91?style=flat-square" />
8
+ <img alt="Renderer: HTML" src="https://img.shields.io/badge/render-HTML-5a7d54?style=flat-square" />
9
+ <img alt="Version: v1.0" src="https://img.shields.io/badge/version-v1.0-6f5f95?style=flat-square" />
10
+ </p>
11
+ </div>
12
+
13
+ ---
14
+
15
+ Cello is a plain-text spreadsheet format. It defines how spreadsheets are written as text, not which app edits them or who is allowed to use them.
16
+
17
+ This document is the canonical rulebook for `.cel` files. It states the core bylaws first, then the syntax and behavior rules that follow from them.
18
+
19
+ ## 0. 📜 First bylaws
20
+
21
+ ### A. Cello is free
22
+
23
+ Cello is open source. It will never be paid-only, metered, limited, or locked behind usage caps. Anyone can use it, implement it, render it, and extend tooling around it without permission or quota.
24
+
25
+ ### B. Cello is a format
26
+
27
+ Cello is not an app, not an editor, and not a hosted product. It is a convention for writing spreadsheets as text so different tools can parse, render, validate, diff, and generate the same source.
28
+
29
+ ### C. Cello is light
30
+
31
+ Cello exists to be lighter than the alternatives. A `.cel` file should be quick to open, quick to render, easy to diff, easy to generate, and cheap to move through both human and machine workflows.
32
+
33
+ ## 1. 📄 Workbooks and sheets
34
+
35
+ A `.cel` file contains one or more sheets. A sheet starts with `@sheet`.
36
+
37
+ ```cel
38
+ @sheet Summary
39
+ | Metric | Value |
40
+ ```
41
+
42
+ Syntax:
43
+
44
+ ```cel
45
+ @sheet Name [format]
46
+ ```
47
+
48
+ Rules:
49
+
50
+ 1. `Name` is case-sensitive.
51
+ 2. `[format]` is optional. If omitted, the sheet uses native Cello syntax.
52
+ 3. Everything after a sheet declaration belongs to that sheet until the next `@sheet`.
53
+ 4. If a file has no `@sheet`, it is treated as a single anonymous native Cello sheet.
54
+
55
+ Examples:
56
+
57
+ ```cel
58
+ @sheet Report
59
+ @sheet Sales [csv]
60
+ @sheet Export [tsv]
61
+ @sheet RawData [json]
62
+ ```
63
+
64
+ ## 2. 🧾 Input formats
65
+
66
+ Cello sheets can be written directly, or loaded from common data formats.
67
+
68
+ | Format | Example | Notes |
69
+ |---|---|---|
70
+ | Native Cello | `@sheet Report` | Full Cello syntax: formulas, modifiers, merges, headers |
71
+ | CSV | `@sheet Sales [csv]` | Comma-separated values |
72
+ | TSV | `@sheet Export [tsv]` or `@sheet Export [\t]` | Tab-separated values |
73
+ | Excel-style | `@sheet Sales [excel]` or `@sheet Sales [;]` | Semicolon-separated values |
74
+ | Custom delimiter | `@sheet Data [,]` | Any single-character delimiter |
75
+ | Markdown table | `@sheet Data [markdown]` | First row becomes headers; separator row is ignored |
76
+ | JSON | `@sheet Data [json]` | Flat arrays of objects |
77
+
78
+ Delimited sheets use their first row as column headers by default.
79
+
80
+ ```cel
81
+ @sheet Sales [csv]
82
+ product,price,quantity
83
+ Apple,1.2,5
84
+ Pear,0.9,3
85
+ ```
86
+
87
+ Use `:noheader` when the first row is data.
88
+
89
+ ```cel
90
+ @sheet Raw [csv:noheader]
91
+ Apple,1.2,5
92
+ Pear,0.9,3
93
+ ```
94
+
95
+ In `noheader` sheets, columns are still available by coordinate references such as `A1`, `B1`, and `C1`.
96
+
97
+ ## 3. 🔗 External sources
98
+
99
+ A sheet can load its content from another file. The source line must appear immediately after the sheet declaration and before any row content.
100
+
101
+ ```cel
102
+ @sheet Sales [csv]
103
+ -> ./exports/sales.csv
104
+
105
+ @sheet Summary
106
+ @header | Metric | Value |
107
+ | Revenue | =SUM(Sales!amount[*]) |
108
+ ```
109
+
110
+ Rules:
111
+
112
+ 1. The declared sheet format controls how the external file is parsed.
113
+ 2. Relative paths are resolved from the parser base directory.
114
+ 3. If loading fails, Cello records a diagnostic and continues parsing the rest of the workbook.
115
+
116
+ ## 4. 🧱 Native rows and cells
117
+
118
+ Native Cello rows are written with pipe-separated cells.
119
+
120
+ ```cel
121
+ | Product | Price | Quantity |
122
+ | Apple | 1.20 | 5 |
123
+ | Pear | 0.90 | 3 |
124
+ ```
125
+
126
+ Rules:
127
+
128
+ 1. A native data row contains cells separated by `|`.
129
+ 2. Write native rows in the form `| cell | cell |` for predictable parsing and readable diffs.
130
+ 3. Blank lines do not consume row numbers and are not rendered.
131
+ 4. Multiple spaces inside cells may be used for alignment in source files.
132
+
133
+ ```cel
134
+ | A | B |
135
+
136
+ | C | D |
137
+ ```
138
+
139
+ In this example, `C` and `D` are on row 2. The blank line is ignored.
140
+
141
+ ## 5. 🏷️ Column headers
142
+
143
+ A header row assigns names to columns. Named columns make formulas easier to read than coordinate references.
144
+
145
+ ```cel
146
+ @header | Product | Price | Quantity | Total |
147
+ | Apple | 1.20 | 5 | =Price*Quantity |
148
+ | Pear | 0.90 | 3 | =Price*Quantity |
149
+ ```
150
+
151
+ Rules:
152
+
153
+ 1. Header rows use `@header` followed by a pipe-separated row: `@header | Product | Price | Quantity |`.
154
+ 2. Headers apply from the next data row downward.
155
+ 3. A later header row replaces the active column names from that point on.
156
+ 4. Header rows render as table headers.
157
+ 5. Header modifiers apply to every cell in that column.
158
+
159
+ ```cel
160
+ @header | Product | Price[€][2d] | Quantity[0d] | Total[€][2d] |
161
+ | Apple | 1.20 | 5 | =Price*Quantity |
162
+ ```
163
+
164
+ Column letters are always available as well. The first column is `A`, then `B`, `C`, and so on.
165
+
166
+ ## 6. 🎚️ Row-level formatting
167
+
168
+ Rows can carry formatting modifiers before the first pipe. These modifiers apply to every cell in that row.
169
+
170
+ ```cel
171
+ [bold][bg:#f5f5f5] | Total | =SUM(Amount) |
172
+ ```
173
+
174
+ This is formatting only. Only modifiers should appear before the first pipe in public `.cel` files; row references are not part of the public Cello format.
175
+
176
+ Modifier precedence is:
177
+
178
+ ```text
179
+ cell > row > column
180
+ ```
181
+
182
+ That means a cell modifier overrides a row modifier, and a row modifier overrides a column modifier.
183
+
184
+ ## 7. 🔤 Data types
185
+
186
+ Cello infers basic data types automatically.
187
+
188
+ | Type | Rule | Example |
189
+ |---|---|---|
190
+ | Number | Numeric value | `42`, `3.14` |
191
+ | Date | ISO date | `2026-01-15` |
192
+ | Boolean | Uppercase literal | `TRUE`, `FALSE` |
193
+ | Text | Anything else | `North`, `pending`, `A-001` |
194
+
195
+ Use double quotes to force text when a value looks like another type.
196
+
197
+ ```cel
198
+ | "00123" | "TRUE" | "2026-01-15" |
199
+ ```
200
+
201
+ The quotes are type markers. They prevent automatic number, boolean, or date inference.
202
+
203
+ ## 8. 🧮 Formulas
204
+
205
+ Any cell starting with `=` is a formula.
206
+
207
+ ```cel
208
+ | =Price*Quantity |
209
+ | =SUM(Total) |
210
+ | =B2*C2 |
211
+ ```
212
+
213
+ Formulas support both coordinate references and named column references.
214
+
215
+ | Syntax | Meaning |
216
+ |---|---|
217
+ | `=B2*C2` | Coordinate reference |
218
+ | `=Price*Quantity` | Named column reference on the current row |
219
+ | `=SUM(Total)` | Sum values above the formula row in the `Total` column |
220
+ | `=SUM(Total[*])` | Sum the full `Total` column |
221
+ | `=SUM(Total[2:5])` | Sum rows 2 through 5 in the `Total` column |
222
+ | `=Sales!B4` | Coordinate reference on another sheet |
223
+ | `=SUM(Sales!Total[*])` | Named column reference on another sheet |
224
+ | `=SUM(!!Amount)` | Reference the first sheet in the workbook |
225
+
226
+ Same-sheet named references are context-aware:
227
+
228
+ 1. In scalar formulas, a bare column name refers to the current row.
229
+ 2. In aggregate formulas, a bare column name refers to rows above the formula row.
230
+ 3. Use `[*]` to force the full data range.
231
+
232
+ ```cel
233
+ @header | Product | Revenue | Cost | Profit |
234
+ | A | 1200 | 800 | =Revenue-Cost |
235
+ | B | 1800 | 900 | =Revenue-Cost |
236
+ | Total | =SUM(Revenue) | =SUM(Cost) | =SUM(Profit) |
237
+ ```
238
+
239
+ The total row does not include itself when using `SUM(Revenue)`.
240
+
241
+ ## 9. 📐 Named ranges
242
+
243
+ Named ranges are based on column headers.
244
+
245
+ ```cel
246
+ @header | Month | Revenue |
247
+ | Jan | 1200 |
248
+ | Feb | 1800 |
249
+ | Mar | 1500 |
250
+ | Total | =SUM(Revenue) |
251
+ ```
252
+
253
+ Range forms:
254
+
255
+ | Syntax | Meaning |
256
+ |---|---|
257
+ | `Revenue` | Current row in scalar context, previous rows in aggregate context |
258
+ | `Revenue[*]` | Full data span of the column |
259
+ | `Revenue[2:5]` | Rows 2 through 5 |
260
+ | `Sales!Revenue` | Named column on another sheet |
261
+ | `Sales!Revenue[*]` | Full named column on another sheet |
262
+
263
+ Use named ranges when formulas should remain readable after columns move.
264
+
265
+ ## 10. ↔️ Merges
266
+
267
+ Cello supports horizontal and vertical merges.
268
+
269
+ ```cel
270
+ | ## Quarterly Report | < | < | 2026 |
271
+ | Region | City | Owner | Revenue |
272
+ | North | Madrid | Ana | 1200 |
273
+ | ^ | Bilbao | Luis | 900 |
274
+ ```
275
+
276
+ Rules:
277
+
278
+ 1. `<` merges with the visible cell on the left.
279
+ 2. `^` merges with the visible cell above.
280
+ 3. Merge tokens must appear alone in the cell.
281
+ 4. Merge tokens do not carry values or modifiers of their own.
282
+
283
+ ## 11. 🎨 Modifiers
284
+
285
+ Modifiers are attached directly to headers, row prefixes, or cell values.
286
+
287
+ ```cel
288
+ @header | Metric | Revenue[€][2d] | Margin[%][1d] |
289
+ [bold] | Total | =SUM(Revenue) | =AVG(Margin) |
290
+ | Critical[bg:red][#fff] | 1200 | 0.42 |
291
+ ```
292
+
293
+ Scopes:
294
+
295
+ | Location | Example | Scope |
296
+ |---|---|---|
297
+ | Column header | `@header | Revenue[€][2d] |` | Every cell in that column |
298
+ | Row modifiers | `[bold] | Total | ... |` | Every cell in that row |
299
+ | Cell value | `Late[bg:red][#fff]` | That cell only |
300
+
301
+ Supported modifiers:
302
+
303
+ | Modifier | Meaning |
304
+ |---|---|
305
+ | `[€]` | Display number with euro prefix |
306
+ | `[$]` | Display number with dollar prefix |
307
+ | `[£]` | Display number with pound prefix |
308
+ | `[%]` | Display number as a percentage |
309
+ | `[0d]`, `[1d]`, `[2d]` | Decimal places |
310
+ | `[bold]` | Bold text |
311
+ | `[italic]` | Italic text |
312
+ | `[#rrggbb]` | Text color |
313
+ | `[bg:#rrggbb]` | Background color |
314
+ | `[colorname]` | CSS named text color |
315
+ | `[bg:colorname]` | CSS named background color |
316
+ | `[#bg:#rrggbb:#rrggbb]` | Background and text color shorthand |
317
+ | `[tone:ok]`, `[tone:warn]`, `[tone:error]`, `[tone:info]`, `[tone:muted]`, `[tone:accent]` | Semantic tone preset |
318
+ | `[hidden]` | Parsed as hidden metadata for tooling |
319
+
320
+ Named CSS colors such as `red`, `blue`, `green`, `orange`, and `gold` are accepted.
321
+ Tone presets map to renderer-defined CSS classes so embedding clients can override their colors with custom CSS.
322
+
323
+ ## 12. 🧩 Column default formulas
324
+
325
+ A column can define a default formula for empty cells in that column with a non-rendered `@defaults` row.
326
+
327
+ ```cel
328
+ @header | Product | Price[€][2d] | Quantity[0d] | Total[€][2d] |
329
+ @defaults | | | | =Price*Quantity |
330
+ | Apple | 1.20 | 5 | |
331
+ | Pear | 0.90 | 3 | |
332
+ | Override | 10 | 2 | 99 |
333
+ ```
334
+
335
+ Rules:
336
+
337
+ 1. Defaults are declared with `@defaults | ... |` below the active header.
338
+ 2. `@defaults` rows are configuration rows. They do not render and do not consume row numbers.
339
+ 3. Defaults only apply to empty cells.
340
+ 4. Explicit cell values and formulas always win.
341
+ 5. `default` is a column-level behavior; do not use it as header, row, or cell formatting.
342
+ 6. The leading `=` is optional.
343
+
344
+ Header, row, and cell-level default modifiers are ignored:
345
+
346
+ ```cel
347
+ @header | Total[default:=Price*Quantity] |
348
+ | [default:=Price*Quantity] |
349
+ ```
350
+
351
+ ## 13. ✍️ Inline formatting
352
+
353
+ Cell text supports a small Markdown-like formatting set.
354
+
355
+ | Syntax | Result |
356
+ |---|---|
357
+ | `*text*` | Bold |
358
+ | `_text_` | Italic |
359
+ | `~~text~~` | Strikethrough |
360
+ | `# text` | Heading-style cell |
361
+ | `## text` | Larger heading-style cell |
362
+
363
+ Examples:
364
+
365
+ ```cel
366
+ | *Priority* |
367
+ | _Estimated_ |
368
+ | ~~Deprecated~~ |
369
+ | ## Total |
370
+ ```
371
+
372
+ `#` and `##` apply to the whole cell.
373
+
374
+ ## 14. 💬 Comments
375
+
376
+ Comments use `//` and are valid outside rows.
377
+
378
+ ```cel
379
+ // Data exported from CRM
380
+ @sheet Sales [csv]
381
+ product,amount
382
+ Enterprise,1200
383
+ SMB,800
384
+ ```
385
+
386
+ Rules:
387
+
388
+ 1. A comment line starts with `//`.
389
+ 2. Comments are not rendered.
390
+ 3. Comments inside cell content are not supported.
391
+
392
+ ## 15. 🔒 Reserved tokens
393
+
394
+ These tokens have special meaning in Cello.
395
+
396
+ | Token | Meaning |
397
+ |---|---|
398
+ | `@sheet` | Sheet declaration |
399
+ | `[format]` | Sheet format or modifier block |
400
+ | `->` | External source line |
401
+ | `|` | Cell separator in native rows |
402
+ | `@header` | Header row marker |
403
+ | `=` | Formula prefix |
404
+ | `!` | Cross-sheet reference separator |
405
+ | `!!` | First-sheet alias |
406
+ | `[n:m]` | Named column row slice |
407
+ | `[*]` | Full named column span |
408
+ | `<` | Horizontal merge token |
409
+ | `^` | Vertical merge token |
410
+ | `//` | Comment line |
411
+ | `"..."` | Force text type |
412
+
413
+ ## 16. 🛟 Error handling and resilience
414
+
415
+ Cello is resilient by default. Local issues should not prevent the rest of the workbook from rendering.
416
+
417
+ Rules:
418
+
419
+ 1. Formula evaluation errors render as cell-level error values.
420
+ 2. Non-parseable formulas fall back to their raw formula text.
421
+ 3. Unknown cell syntax is treated as plain text when possible.
422
+ 4. Invalid or unsupported sheet content records diagnostics and parsing continues.
423
+ 5. Broken external sources record diagnostics and the remaining workbook still renders.
424
+
425
+ This behavior is intentional. A `.cel` file should be useful even when part of it is incomplete, generated, or temporarily invalid.
426
+
427
+ ## 📚 Quick reference
428
+
429
+ | Task | Syntax |
430
+ |---|---|
431
+ | Start a native sheet | `@sheet Summary` |
432
+ | Start a CSV sheet | `@sheet Sales [csv]` |
433
+ | Load an external CSV | `@sheet Sales [csv]` then `-> ./sales.csv` |
434
+ | Define headers | `@header | Product | Price | Quantity |` |
435
+ | Write a row | `| Apple | 1.20 | 5 |` |
436
+ | Format a row | `[bold] | Total | =SUM(Amount) |` |
437
+ | Format a column | `@header | Amount[€][2d] |` |
438
+ | Format a cell | `Late[bg:red][#fff]` |
439
+ | Write a formula | `=Price*Quantity` |
440
+ | Sum previous rows | `=SUM(Amount)` |
441
+ | Sum a full column | `=SUM(Amount[*])` |
442
+ | Reference another sheet | `=SUM(Sales!Amount[*])` |
443
+ | Merge right | `<` |
444
+ | Merge down | `^` |
445
+ | Force text | `"00123"` |
446
+ | Add a comment | `// source: CRM export` |
package/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## 0.1.0 - 2026-05-15
8
+
9
+ - Initial public package metadata for `@nachoggodino/cello` as a GPLv3 package.
10
+ - Added parser, HyperFormula-backed evaluator, validator, renderer, serializer, and live preview CLI commands.
11
+ - Added `.cel` examples and test coverage for API, CLI, parser, evaluator, renderer, serializer, and validator behavior.
package/LICENSE ADDED
@@ -0,0 +1,12 @@
1
+ SPDX-License-Identifier: GPL-3.0-only
2
+
3
+ Copyright (c) 2026 Cello contributors
4
+
5
+ Cello is licensed under the GNU General Public License version 3 only.
6
+
7
+ This package includes HyperFormula as a production dependency and configures it
8
+ with licenseKey: "gpl-v3". HyperFormula is distributed under GPLv3 or a
9
+ commercial license from Handsontable. This package uses the GPLv3 option.
10
+
11
+ The full GPLv3 license text is available at:
12
+ https://www.gnu.org/licenses/gpl-3.0.txt
package/README.md ADDED
@@ -0,0 +1,211 @@
1
+ # Cello
2
+
3
+ Plain-text spreadsheets with formulas. Cello gives you a readable `.cel` format, a TypeScript API, and a CLI that can parse, evaluate, validate, format, serialize, render, and serve workbooks as self-contained HTML.
4
+
5
+ It is useful when you want spreadsheet-like calculations in files that are easy to diff, review, generate, and keep in source control.
6
+
7
+ The npm package is `@nachoggodino/cello`. It is licensed as GPLv3 because formula evaluation uses HyperFormula under its GPLv3 option.
8
+
9
+ ## Features
10
+
11
+ - Multi-sheet `.cel` workbooks.
12
+ - Native Cello rows plus CSV, TSV, semicolon, Markdown table, JSON, and external-source sheets.
13
+ - Formula evaluation through HyperFormula, including cross-sheet references and named columns.
14
+ - HTML rendering with tabs, merged cells, inline formatting, and cell/row/column modifiers.
15
+ - JSON AST output for tooling.
16
+ - Validation diagnostics that return proper process exit codes.
17
+ - Library API and `cello` CLI.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ npm install @nachoggodino/cello
23
+ ```
24
+
25
+ Requirements:
26
+
27
+ - Node.js 22 or newer.
28
+
29
+ For local development in this repository:
30
+
31
+ ```bash
32
+ npm install
33
+ npm run build
34
+ npm test
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ Create `sales.cel`:
40
+
41
+ ```cel
42
+ @sheet Sales [csv]
43
+ product,price,quantity
44
+ Apple,1.2,5
45
+ Pear,0.9,3
46
+
47
+ @sheet KPI
48
+ @header | Metric | Value |
49
+ | Revenue | =Sales!B2*Sales!C2 + Sales!B3*Sales!C3 |
50
+ ```
51
+
52
+ Render it:
53
+
54
+ ```bash
55
+ cello render sales.cel -o sales.html
56
+ ```
57
+
58
+ Or open a live preview:
59
+
60
+ ```bash
61
+ cello serve sales.cel --open
62
+ ```
63
+
64
+ Rendered preview:
65
+
66
+ | Metric | Value |
67
+ | --- | ---: |
68
+ | Revenue | 8.7 |
69
+
70
+ For a larger example with named references, slices, and cross-sheet formulas, see [examples/advanced_kpi.cel](examples/advanced_kpi.cel).
71
+
72
+ ## CLI
73
+
74
+ ```bash
75
+ cello help
76
+ cello --version
77
+ cello parse <file.cel>
78
+ cello evaluate <file.cel>
79
+ cello format <file.cel> [--check] [-o out.cel]
80
+ cello validate <file.cel>
81
+ cello render <file.cel> [-o out.html] [--no-eval] [--format document|fragment]
82
+ cello serialize <file.cel> [-o out.cel]
83
+ cello serve <file.cel> [--port 4321] [--host 127.0.0.1] [--open] [--no-eval]
84
+ ```
85
+
86
+ Exit codes:
87
+
88
+ - `0` means the command completed successfully.
89
+ - `1` means invalid arguments, validation diagnostics, or runtime failure.
90
+
91
+ Command details:
92
+
93
+ - `parse` prints the workbook AST as JSON.
94
+ - `evaluate` prints the AST with computed formula values.
95
+ - `format` pretty-prints native Cello pipe tables, writes in place by default, supports `-o/--out`, and uses `--check` to report formatting drift with exit code `1`.
96
+ - `validate` prints `{ "valid": boolean, "diagnostics": [...] }`; it exits `1` when diagnostics exist.
97
+ - `render` writes self-contained HTML with `-o/--out`, or prints HTML to stdout. `--format document` is the default full HTML document; `--format fragment` emits an embeddable chunk without `html`/`head`/`body` wrappers.
98
+ - `serialize` converts the parsed AST back to `.cel` text.
99
+ - `serve` starts a local live-preview server and only opens the browser when `--open` is provided.
100
+
101
+ ## Library API
102
+
103
+ ```ts
104
+ import { evaluate, format, parse, render, serialize, validate } from "@nachoggodino/cello";
105
+
106
+ const source = `
107
+ @sheet KPI
108
+ | Revenue | =1.2*5 + 0.9*3 |
109
+ `;
110
+
111
+ const ast = parse(source);
112
+ const evaluated = await evaluate(ast);
113
+ const pretty = format(source);
114
+ const result = await validate(source);
115
+ const html = await render(source);
116
+ const fragment = await render(source, { format: "fragment" });
117
+ const text = serialize(evaluated);
118
+
119
+ console.log(result.valid, pretty, html, text);
120
+ ```
121
+
122
+ Primary exports:
123
+
124
+ - `parse(text, options?)`
125
+ - `evaluate(ast, options?)`
126
+ - `format(text)`
127
+ - `validate(text, options?)`
128
+ - `render(input, options?)`
129
+ - `serialize(ast)`
130
+
131
+ ## Format Overview
132
+
133
+ Native Cello sheets use pipe-delimited rows:
134
+
135
+ ```cel
136
+ @sheet Report
137
+
138
+ @header | Region | Revenue[€][2d] | Units[0d] |
139
+ | Madrid | 4280 | 15 |
140
+ | Barcelona | 2080 | 7 |
141
+ | Valencia | 760 | 2 |
142
+ | ## Total | =SUM(Revenue) | =SUM(Units) |
143
+ ```
144
+
145
+ Rendered preview:
146
+
147
+ | Region | Revenue | Units |
148
+ | --- | ---: | ---: |
149
+ | Madrid | €4,280.00 | 15 |
150
+ | Barcelona | €2,080.00 | 7 |
151
+ | Valencia | €760.00 | 2 |
152
+ | Total | €7,120.00 | 24 |
153
+
154
+ Useful syntax:
155
+
156
+ - `@sheet Name [format]` starts a sheet.
157
+ - `@header | Column | Names |` declares named columns.
158
+ - `@defaults | | | =Formula |` declares non-rendered column default formulas.
159
+ - `| cell | cell |` declares rows.
160
+ - `[bold] | ... |` applies row-level modifiers.
161
+ - `=A1+B1`, `=SUM(Revenue)`, and `=Sales!Amount` create formulas.
162
+ - `!!Amount` references a named column on the first sheet.
163
+ - `<` merges with the cell on the left; `^` merges with the cell above.
164
+ - `[€]`, `[2d]`, `[bold]`, `[bg:#fff9c4]`, and similar modifiers affect rendering.
165
+
166
+ The canonical syntax rules live in [BYLAWS.md](BYLAWS.md). The public specification lives in [docs/SPEC.md](docs/SPEC.md).
167
+
168
+ Editor integrations can reuse the TextMate grammar and VS Code language configuration documented in [docs/SYNTAX_HIGHLIGHTING.md](docs/SYNTAX_HIGHLIGHTING.md).
169
+
170
+ ## Package Contents
171
+
172
+ The npm package publishes only the built library/CLI output and user-facing metadata:
173
+
174
+ - `dist/`
175
+ - `docs/`
176
+ - `examples/`
177
+ - `syntaxes/`
178
+ - `BYLAWS.md`
179
+ - `README.md`
180
+ - `CHANGELOG.md`
181
+ - `LICENSE`
182
+
183
+ ## Development
184
+
185
+ ```bash
186
+ npm run build
187
+ npm run typecheck
188
+ npm test
189
+ npm run coverage
190
+ ```
191
+
192
+ Repository layout:
193
+
194
+ - `src/parser/` parses workbooks into ASTs.
195
+ - `src/evaluator/` computes formulas.
196
+ - `src/formatter/` pretty-prints native Cello pipe tables.
197
+ - `src/validator/` reports parse/evaluation diagnostics.
198
+ - `src/renderer/` creates self-contained HTML.
199
+ - `src/serializer/` converts ASTs back to `.cel`.
200
+ - `src/cli/` exposes the command-line interface.
201
+ - `tests/` covers unit, integration, and fixture behavior.
202
+
203
+ ## Versioning
204
+
205
+ This project uses Semantic Versioning. See [CHANGELOG.md](CHANGELOG.md) for release history.
206
+
207
+ ## License
208
+
209
+ GPL-3.0-only.
210
+
211
+ Cello uses HyperFormula for formula evaluation and configures it with `licenseKey: "gpl-v3"`. HyperFormula is available under GPLv3 or a commercial license from Handsontable; this package uses the GPLv3 option.
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import { startServe } from "./serve.js";
4
+ export interface CliDeps {
5
+ cwd: string;
6
+ readFileFn: typeof readFile;
7
+ writeFileFn: typeof writeFile;
8
+ startServeFn: typeof startServe;
9
+ stayOpenFn: () => Promise<number>;
10
+ stdoutWrite: (text: string) => void;
11
+ stderrWrite: (text: string) => void;
12
+ }
13
+ export declare function createCliDeps(overrides?: Partial<CliDeps>): CliDeps;
14
+ export declare function runCli(argv: string[], deps?: CliDeps): Promise<number>;
15
+ export declare function runMain(argv: string[], deps?: CliDeps, exitFn?: (code: number) => void, runCliFn?: (argv: string[], deps: CliDeps) => Promise<number>, stderrWrite?: (text: string) => void): Promise<void>;
16
+ export declare function isDirectCliExecution(argvPath: string, moduleUrl: string): boolean;