@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
@@ -0,0 +1,552 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { columnLetter, inferType, parseSheetFormat, parseTrailingModifiers, splitDelimitedLine } from "../shared/utils.js";
4
+ const DEFAULT_ANON_SHEET_NAME = "Sheet1";
5
+ export function parse(text, options = {}) {
6
+ const workbook = {
7
+ version: "1.0",
8
+ sheets: [],
9
+ diagnostics: []
10
+ };
11
+ const state = {
12
+ currentSheet: null,
13
+ currentHeaders: [],
14
+ previousRowByColumn: new Map(),
15
+ jsonBufferBySheet: new Map(),
16
+ consumedDelimitedHeaderBySheet: new Set()
17
+ };
18
+ const sourceLines = text.replace(/\r\n/g, "\n").split("\n");
19
+ const runtime = {
20
+ workbook,
21
+ options,
22
+ state,
23
+ injectedLines: [],
24
+ ensureSheet: () => ensureSheet(workbook, state, options),
25
+ pushDiagnostic: (d) => pushDiagnostic(workbook, options, d)
26
+ };
27
+ let lineNumber = 0;
28
+ let sourceIndex = 0;
29
+ while (runtime.injectedLines.length > 0 || sourceIndex < sourceLines.length) {
30
+ const fromInjected = runtime.injectedLines.length > 0;
31
+ const rawLine = fromInjected
32
+ ? (runtime.injectedLines.shift() ?? "")
33
+ : (sourceLines[sourceIndex] ?? "");
34
+ if (!fromInjected && sourceIndex < sourceLines.length) {
35
+ sourceIndex += 1;
36
+ }
37
+ lineNumber += 1;
38
+ const trimmed = rawLine.trim();
39
+ if (trimmed.startsWith("//")) {
40
+ continue;
41
+ }
42
+ if (tryHandleSheetDeclaration(runtime, trimmed, lineNumber)) {
43
+ continue;
44
+ }
45
+ if (trimmed.length === 0) {
46
+ resetRowTracking(runtime.state);
47
+ continue;
48
+ }
49
+ const sheet = runtime.ensureSheet();
50
+ if (sheet.format.kind === "json") {
51
+ bufferJsonLine(runtime.state, sheet.name, rawLine);
52
+ continue;
53
+ }
54
+ if (tryHandleExternalSource(runtime, sheet, trimmed, lineNumber)) {
55
+ continue;
56
+ }
57
+ if (sheet.format.kind === "cello" && tryHandleHeaderDirective(runtime, sheet, rawLine, trimmed, lineNumber)) {
58
+ continue;
59
+ }
60
+ if (sheet.format.kind === "cello" && tryHandleDefaultsDirective(runtime, sheet, rawLine, trimmed, lineNumber)) {
61
+ continue;
62
+ }
63
+ if (sheet.format.kind === "delimited") {
64
+ handleDelimitedLine(runtime.state, sheet, rawLine, lineNumber);
65
+ continue;
66
+ }
67
+ if (sheet.format.kind === "markdown") {
68
+ handleMarkdownLine(runtime.state, sheet, trimmed, lineNumber);
69
+ continue;
70
+ }
71
+ if (!rawLine.includes("|")) {
72
+ runtime.pushDiagnostic({
73
+ level: "warning",
74
+ line: lineNumber,
75
+ sheet: sheet.name,
76
+ message: `Skipped non-row line: ${trimmed}`
77
+ });
78
+ continue;
79
+ }
80
+ handleNativeLine(runtime, sheet, rawLine, lineNumber);
81
+ }
82
+ finalizeJsonSheets(runtime);
83
+ return workbook;
84
+ }
85
+ function ensureSheet(workbook, state, options) {
86
+ if (!state.currentSheet) {
87
+ state.currentSheet = createSheet(options.anonymousSheetName ?? DEFAULT_ANON_SHEET_NAME, undefined);
88
+ workbook.sheets.push(state.currentSheet);
89
+ resetSheetTracking(state);
90
+ }
91
+ return state.currentSheet;
92
+ }
93
+ function pushDiagnostic(workbook, options, diagnostic) {
94
+ workbook.diagnostics.push(diagnostic);
95
+ if (options.strict && diagnostic.level === "error") {
96
+ throw new Error(`Parse error: ${diagnostic.message}${diagnostic.line ? ` (line ${diagnostic.line})` : ""}`);
97
+ }
98
+ }
99
+ function resetSheetTracking(state) {
100
+ state.currentHeaders = [];
101
+ resetRowTracking(state);
102
+ }
103
+ function resetRowTracking(state) {
104
+ state.previousRowByColumn = new Map();
105
+ }
106
+ function tryHandleSheetDeclaration(runtime, trimmed, lineNumber) {
107
+ const sheetMatch = trimmed.match(/^@sheet\s+(.+?)(?:\s+\[(.+)\])?$/);
108
+ if (!sheetMatch) {
109
+ return false;
110
+ }
111
+ const rawName = sheetMatch[1];
112
+ const rawFormat = sheetMatch[2];
113
+ if (!rawName) {
114
+ runtime.pushDiagnostic({ level: "warning", line: lineNumber, message: "Invalid @sheet declaration." });
115
+ return true;
116
+ }
117
+ const nextSheet = createSheet(rawName.trim(), rawFormat?.trim());
118
+ runtime.workbook.sheets.push(nextSheet);
119
+ runtime.state.currentSheet = nextSheet;
120
+ resetSheetTracking(runtime.state);
121
+ return true;
122
+ }
123
+ function tryHandleExternalSource(runtime, sheet, trimmed, lineNumber) {
124
+ const externalSourceMatch = trimmed.match(/^->\s+(.+)$/);
125
+ if (!externalSourceMatch) {
126
+ return false;
127
+ }
128
+ if (sheet.rows.length > 0) {
129
+ runtime.pushDiagnostic({
130
+ level: "warning",
131
+ line: lineNumber,
132
+ sheet: sheet.name,
133
+ message: "External sheet source (-> path) must appear before any rows in a sheet."
134
+ });
135
+ return true;
136
+ }
137
+ const rawPath = (externalSourceMatch[1] ?? "").trim();
138
+ const baseDir = runtime.options.baseDir ?? getDefaultBaseDir();
139
+ const filePath = resolve(baseDir, rawPath);
140
+ try {
141
+ const externalText = readExternalSource(runtime.options, rawPath, baseDir, filePath).replace(/\r\n/g, "\n");
142
+ const externalLines = externalText.split("\n");
143
+ runtime.injectedLines = externalLines.concat(runtime.injectedLines);
144
+ }
145
+ catch (err) {
146
+ const message = err instanceof Error ? err.message : String(err);
147
+ runtime.pushDiagnostic({
148
+ level: "warning",
149
+ line: lineNumber,
150
+ sheet: sheet.name,
151
+ message: `Failed to load external sheet source "${rawPath}": ${message}`
152
+ });
153
+ }
154
+ return true;
155
+ }
156
+ function getDefaultBaseDir() {
157
+ return typeof process !== "undefined" && typeof process.cwd === "function" ? process.cwd() : ".";
158
+ }
159
+ function readExternalSource(options, rawPath, baseDir, resolvedPath) {
160
+ return options.readExternalSource
161
+ ? options.readExternalSource(rawPath, { baseDir, resolvedPath })
162
+ : readFileSync(resolvedPath, "utf8");
163
+ }
164
+ function bufferJsonLine(state, sheetName, rawLine) {
165
+ const current = state.jsonBufferBySheet.get(sheetName) ?? [];
166
+ current.push(rawLine);
167
+ state.jsonBufferBySheet.set(sheetName, current);
168
+ }
169
+ function finalizeJsonSheets(runtime) {
170
+ for (const sheet of runtime.workbook.sheets) {
171
+ if (sheet.format.kind !== "json") {
172
+ continue;
173
+ }
174
+ const raw = (runtime.state.jsonBufferBySheet.get(sheet.name) ?? []).join("\n").trim();
175
+ if (raw.length === 0) {
176
+ continue;
177
+ }
178
+ try {
179
+ const parsed = JSON.parse(raw);
180
+ if (!Array.isArray(parsed) || (parsed[0] && typeof parsed[0] !== "object")) {
181
+ throw new Error("JSON sheet expects an array of flat objects.");
182
+ }
183
+ const first = parsed[0];
184
+ const headers = Object.keys(first ?? {}).map((name) => ({ name, modifiers: [] }));
185
+ if (headers.length > 0) {
186
+ applyHeaders(runtime.state, sheet, headers, 0);
187
+ }
188
+ let previousRowByColumn = mapLatestVisibleCells(sheet.rows[sheet.rows.length - 1] ?? {
189
+ index: 0,
190
+ kind: "data",
191
+ sourceLine: 0,
192
+ modifiers: [],
193
+ cells: []
194
+ });
195
+ for (const obj of parsed) {
196
+ const cells = headers.map((h) => stringifyJsonValue(obj[h.name]));
197
+ previousRowByColumn = appendDataRow(sheet, cells, 0, previousRowByColumn, headers);
198
+ }
199
+ }
200
+ catch (err) {
201
+ const message = err instanceof Error ? err.message : String(err);
202
+ runtime.workbook.diagnostics.push({
203
+ level: "warning",
204
+ sheet: sheet.name,
205
+ message: `JSON parse failed. Falling back to single text row: ${message}`
206
+ });
207
+ const row = parseDataCells([raw], {
208
+ rowIndex: sheet.rows.length + 1,
209
+ lineNumber: 0,
210
+ rowModifiers: [],
211
+ previousRowByColumn: new Map(),
212
+ currentHeaders: []
213
+ });
214
+ sheet.rows.push(row);
215
+ registerColumns(sheet, row.cells, []);
216
+ }
217
+ }
218
+ }
219
+ function createSheet(name, rawFormat) {
220
+ return {
221
+ name,
222
+ format: parseSheetFormat(rawFormat),
223
+ rows: [],
224
+ columns: []
225
+ };
226
+ }
227
+ function tryHandleHeaderDirective(runtime, sheet, rawLine, trimmed, lineNumber) {
228
+ if (!/^@header(?:\s|$)/.test(trimmed)) {
229
+ return false;
230
+ }
231
+ const markerStart = rawLine.indexOf("@header");
232
+ const body = rawLine.slice(markerStart + "@header".length).trim();
233
+ if (!body.includes("|")) {
234
+ runtime.pushDiagnostic({
235
+ level: "warning",
236
+ line: lineNumber,
237
+ sheet: sheet.name,
238
+ message: "@header must be followed by a pipe-separated row."
239
+ });
240
+ return true;
241
+ }
242
+ applyHeaders(runtime.state, sheet, parseHeadersFromLine(body), lineNumber);
243
+ return true;
244
+ }
245
+ function parseHeadersFromLine(line) {
246
+ return splitNativeCells(line).map((token) => {
247
+ const parsed = parseTrailingModifiers(token.trim());
248
+ return { name: parsed.base, modifiers: parsed.modifiers.filter((modifier) => modifier.key !== "default") };
249
+ });
250
+ }
251
+ function applyHeaders(state, sheet, headers, lineNumber) {
252
+ pushHeaderRow(sheet, headers, lineNumber);
253
+ state.currentHeaders = headers;
254
+ applyHeadersToColumns(sheet, headers);
255
+ }
256
+ function tryHandleDefaultsDirective(runtime, sheet, rawLine, trimmed, lineNumber) {
257
+ if (!/^@defaults(?:\s|$)/.test(trimmed)) {
258
+ return false;
259
+ }
260
+ const markerStart = rawLine.indexOf("@defaults");
261
+ const body = rawLine.slice(markerStart + "@defaults".length).trim();
262
+ if (!body.includes("|")) {
263
+ runtime.pushDiagnostic({
264
+ level: "warning",
265
+ line: lineNumber,
266
+ sheet: sheet.name,
267
+ message: "@defaults must be followed by a pipe-separated row."
268
+ });
269
+ return true;
270
+ }
271
+ applyDefaults(runtime.state, sheet, splitNativeCells(body));
272
+ return true;
273
+ }
274
+ function applyDefaults(state, sheet, defaults) {
275
+ const maxColumns = Math.max(defaults.length, state.currentHeaders.length);
276
+ const nextHeaders = [];
277
+ for (let idx = 0; idx < maxColumns; idx += 1) {
278
+ const header = state.currentHeaders[idx] ?? { name: "", modifiers: [] };
279
+ const rawDefault = defaults[idx]?.trim() ?? "";
280
+ const modifiers = rawDefault.length > 0
281
+ ? upsertDefaultModifier(header.modifiers, rawDefault)
282
+ : header.modifiers;
283
+ nextHeaders[idx] = { name: header.name, modifiers };
284
+ }
285
+ state.currentHeaders = nextHeaders;
286
+ applyHeadersToColumns(sheet, nextHeaders);
287
+ }
288
+ function upsertDefaultModifier(modifiers, formula) {
289
+ const normalized = formula.startsWith("=") ? formula : `=${formula}`;
290
+ const defaultModifier = {
291
+ raw: `default:${normalized}`,
292
+ key: "default",
293
+ value: normalized
294
+ };
295
+ return [...modifiers.filter((modifier) => modifier.key !== "default"), defaultModifier];
296
+ }
297
+ function pushHeaderRow(sheet, headers, lineNumber) {
298
+ const index = sheet.rows.length + 1;
299
+ sheet.rows.push({
300
+ index,
301
+ kind: "header",
302
+ sourceLine: lineNumber,
303
+ modifiers: [],
304
+ cells: headers.map((header, col) => createValueCell(index, col + 1, header.name, header.modifiers, "text"))
305
+ });
306
+ }
307
+ function splitNativeCells(line) {
308
+ const tokens = line.split("|").map((t) => t.trim());
309
+ return trimPipeEdgeTokens(tokens);
310
+ }
311
+ function splitNativeRow(line) {
312
+ const firstPipe = line.indexOf("|");
313
+ if (firstPipe === -1) {
314
+ return { rowModifiers: [], cells: [line] };
315
+ }
316
+ const rowPrefix = line.slice(0, firstPipe).trim();
317
+ const body = line.slice(firstPipe);
318
+ const cells = splitNativeCells(body);
319
+ if (rowPrefix.length === 0) {
320
+ return { rowModifiers: [], cells };
321
+ }
322
+ const parsed = parseTrailingModifiers(rowPrefix);
323
+ if (parsed.base.length === 0) {
324
+ return { rowModifiers: parsed.modifiers, cells };
325
+ }
326
+ return { rowModifiers: [], unsupportedPrefix: rowPrefix, cells };
327
+ }
328
+ function handleNativeLine(runtime, sheet, rawLine, lineNumber) {
329
+ const { rowModifiers, unsupportedPrefix, cells } = splitNativeRow(rawLine);
330
+ if (unsupportedPrefix) {
331
+ runtime.pushDiagnostic({
332
+ level: "warning",
333
+ line: lineNumber,
334
+ sheet: sheet.name,
335
+ message: `Ignored unsupported row prefix "${unsupportedPrefix}". Row references are not supported.`
336
+ });
337
+ }
338
+ runtime.state.previousRowByColumn = appendDataRow(sheet, cells, lineNumber, runtime.state.previousRowByColumn, runtime.state.currentHeaders, rowModifiers);
339
+ }
340
+ function handleDelimitedLine(state, sheet, rawLine, lineNumber) {
341
+ const parts = splitDelimitedLine(rawLine, sheet.format.delimiter).map((v) => v.trim());
342
+ const headerConsumedKey = sheet.name;
343
+ if (!sheet.format.noHeader && !state.consumedDelimitedHeaderBySheet.has(headerConsumedKey)) {
344
+ applyHeaders(state, sheet, toHeaderDefs(parts), lineNumber);
345
+ state.consumedDelimitedHeaderBySheet.add(headerConsumedKey);
346
+ return;
347
+ }
348
+ state.previousRowByColumn = appendDataRow(sheet, parts, lineNumber, state.previousRowByColumn, state.currentHeaders);
349
+ }
350
+ function handleMarkdownLine(state, sheet, trimmed, lineNumber) {
351
+ if (isMarkdownSeparator(trimmed) || !trimmed.includes("|")) {
352
+ return;
353
+ }
354
+ const parts = parseMarkdownLine(trimmed);
355
+ if (state.currentHeaders.length === 0) {
356
+ applyHeaders(state, sheet, toHeaderDefs(parts), lineNumber);
357
+ return;
358
+ }
359
+ state.previousRowByColumn = appendDataRow(sheet, parts, lineNumber, state.previousRowByColumn, state.currentHeaders);
360
+ }
361
+ function parseDataCells(cells, context) {
362
+ const parsedCells = [];
363
+ const currentByColumn = new Map();
364
+ const maxColumn = Math.max(cells.length, getLastDefaultColumnIndex(context.currentHeaders));
365
+ for (let idx = 0; idx < maxColumn; idx += 1) {
366
+ const col = idx + 1;
367
+ const token = cells[idx]?.trim() ?? "";
368
+ const defaultFormula = getColumnDefaultFormula(context.currentHeaders[idx]);
369
+ if (token === "<") {
370
+ const left = currentByColumn.get(col - 1);
371
+ if (left) {
372
+ left.colspan += 1;
373
+ }
374
+ parsedCells.push(createMergeCell(context.rowIndex, col, "merge-left"));
375
+ if (left) {
376
+ currentByColumn.set(col, left);
377
+ }
378
+ continue;
379
+ }
380
+ if (token === "^") {
381
+ const above = context.previousRowByColumn.get(col);
382
+ if (above) {
383
+ above.rowspan += 1;
384
+ }
385
+ parsedCells.push(createMergeCell(context.rowIndex, col, "merge-up"));
386
+ if (above) {
387
+ currentByColumn.set(col, above);
388
+ }
389
+ continue;
390
+ }
391
+ if (token.length === 0 && defaultFormula) {
392
+ const formulaCell = createFormulaCell(context.rowIndex, col, defaultFormula);
393
+ parsedCells.push(formulaCell);
394
+ currentByColumn.set(col, formulaCell);
395
+ continue;
396
+ }
397
+ if (token.startsWith("=")) {
398
+ const formulaCell = createFormulaCell(context.rowIndex, col, token);
399
+ parsedCells.push(formulaCell);
400
+ currentByColumn.set(col, formulaCell);
401
+ continue;
402
+ }
403
+ const extracted = parseTrailingModifiers(token);
404
+ const inferred = inferType(extracted.base);
405
+ const cell = createValueCell(context.rowIndex, col, inferred.parsed, extracted.modifiers, inferred.inferredType, token);
406
+ parsedCells.push(cell);
407
+ currentByColumn.set(col, cell);
408
+ }
409
+ return createDataRow(context.rowIndex, context.lineNumber, parsedCells, context.rowModifiers);
410
+ }
411
+ function appendDataRow(sheet, cells, lineNumber, previousRowByColumn, currentHeaders, rowModifiers = []) {
412
+ const row = parseDataCells(cells, {
413
+ rowIndex: sheet.rows.length + 1,
414
+ lineNumber,
415
+ rowModifiers,
416
+ previousRowByColumn,
417
+ currentHeaders
418
+ });
419
+ sheet.rows.push(row);
420
+ registerColumns(sheet, row.cells, currentHeaders);
421
+ return mapLatestVisibleCells(row);
422
+ }
423
+ function registerColumns(sheet, cells, currentHeaders) {
424
+ const maxCol = cells.length;
425
+ for (let col = 1; col <= maxCol; col += 1) {
426
+ if (sheet.columns[col - 1]) {
427
+ continue;
428
+ }
429
+ const header = currentHeaders[col - 1];
430
+ sheet.columns[col - 1] = createColumnNode(col, header);
431
+ }
432
+ }
433
+ function applyHeadersToColumns(sheet, headers) {
434
+ for (let i = 0; i < headers.length; i += 1) {
435
+ const header = headers[i];
436
+ if (!header) {
437
+ continue;
438
+ }
439
+ sheet.columns[i] = createColumnNode(i + 1, header);
440
+ }
441
+ }
442
+ function mapLatestVisibleCells(row) {
443
+ const map = new Map();
444
+ for (const cell of row.cells) {
445
+ if (cell.kind === "merge-left" || cell.kind === "merge-up") {
446
+ continue;
447
+ }
448
+ map.set(cell.col, cell);
449
+ }
450
+ return map;
451
+ }
452
+ function isMarkdownSeparator(trimmed) {
453
+ const collapsed = trimmed.replaceAll(" ", "");
454
+ return /^(\|)?[:\-|]+(\|)?$/.test(collapsed);
455
+ }
456
+ function parseMarkdownLine(trimmed) {
457
+ const parts = trimmed.split("|").map((p) => p.trim());
458
+ return trimPipeEdgeTokens(parts);
459
+ }
460
+ function trimPipeEdgeTokens(tokens) {
461
+ return tokens
462
+ .filter((_, idx) => !(idx === 0 && tokens[0] === ""))
463
+ .filter((_, idx, arr) => !(idx === arr.length - 1 && arr[idx] === ""));
464
+ }
465
+ function toHeaderDefs(values) {
466
+ return values.map((name) => ({ name, modifiers: [] }));
467
+ }
468
+ function getLastDefaultColumnIndex(headers) {
469
+ for (let idx = headers.length - 1; idx >= 0; idx -= 1) {
470
+ if (getColumnDefaultFormula(headers[idx])) {
471
+ return idx + 1;
472
+ }
473
+ }
474
+ return 0;
475
+ }
476
+ function getColumnDefaultFormula(header) {
477
+ const formula = header?.modifiers.find((modifier) => modifier.key === "default")?.value?.trim();
478
+ if (!formula) {
479
+ return undefined;
480
+ }
481
+ return formula.startsWith("=") ? formula : `=${formula}`;
482
+ }
483
+ function createColumnNode(index, header) {
484
+ return {
485
+ index,
486
+ letter: columnLetter(index),
487
+ ...(header?.name ? { name: header.name } : {}),
488
+ modifiers: header?.modifiers ?? [],
489
+ hidden: Boolean(header?.modifiers.some((m) => m.key === "hidden"))
490
+ };
491
+ }
492
+ function createDataRow(index, sourceLine, cells, modifiers) {
493
+ return {
494
+ index,
495
+ kind: "data",
496
+ sourceLine,
497
+ modifiers,
498
+ cells
499
+ };
500
+ }
501
+ function createValueCell(row, col, value, modifiers, inferredType, raw = value === null ? "" : String(value)) {
502
+ return {
503
+ row,
504
+ col,
505
+ raw,
506
+ kind: inferredType === "empty" ? "empty" : "value",
507
+ inferredType,
508
+ value,
509
+ modifiers,
510
+ colspan: 1,
511
+ rowspan: 1
512
+ };
513
+ }
514
+ function createFormulaCell(row, col, formula) {
515
+ return {
516
+ row,
517
+ col,
518
+ raw: formula,
519
+ kind: "formula",
520
+ inferredType: "text",
521
+ value: formula,
522
+ formula,
523
+ modifiers: [],
524
+ colspan: 1,
525
+ rowspan: 1
526
+ };
527
+ }
528
+ function createMergeCell(row, col, kind) {
529
+ return {
530
+ row,
531
+ col,
532
+ raw: kind === "merge-left" ? "<" : "^",
533
+ kind,
534
+ inferredType: "empty",
535
+ value: null,
536
+ modifiers: [],
537
+ colspan: 0,
538
+ rowspan: 0
539
+ };
540
+ }
541
+ function stringifyJsonValue(value) {
542
+ if (value === null || value === undefined) {
543
+ return "";
544
+ }
545
+ if (typeof value === "string") {
546
+ return value;
547
+ }
548
+ if (typeof value === "number" || typeof value === "boolean") {
549
+ return String(value);
550
+ }
551
+ return JSON.stringify(value);
552
+ }
@@ -0,0 +1,2 @@
1
+ import type { RenderOptions, WorkbookAst } from "../shared/types.js";
2
+ export declare function render(input: string | WorkbookAst, options?: RenderOptions): Promise<string>;