@ifc-lite/cli 0.26.0 → 0.27.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 (128) hide show
  1. package/dist/commands/ask.d.ts +8 -0
  2. package/dist/commands/ask.d.ts.map +1 -1
  3. package/dist/commands/ask.js +10 -9
  4. package/dist/commands/ask.js.map +1 -1
  5. package/dist/commands/bsdd.d.ts.map +1 -1
  6. package/dist/commands/bsdd.js +49 -13
  7. package/dist/commands/bsdd.js.map +1 -1
  8. package/dist/commands/diff-classification-label.d.ts +12 -0
  9. package/dist/commands/diff-classification-label.d.ts.map +1 -0
  10. package/dist/commands/diff-classification-label.js +20 -0
  11. package/dist/commands/diff-classification-label.js.map +1 -0
  12. package/dist/commands/diff-engine.d.ts.map +1 -1
  13. package/dist/commands/diff-engine.js +33 -11
  14. package/dist/commands/diff-engine.js.map +1 -1
  15. package/dist/commands/diff-test-helpers.d.ts +12 -0
  16. package/dist/commands/diff-test-helpers.d.ts.map +1 -1
  17. package/dist/commands/diff-test-helpers.js +68 -0
  18. package/dist/commands/diff-test-helpers.js.map +1 -1
  19. package/dist/commands/diff-unit-scale-fixtures.d.ts +34 -0
  20. package/dist/commands/diff-unit-scale-fixtures.d.ts.map +1 -0
  21. package/dist/commands/diff-unit-scale-fixtures.js +94 -0
  22. package/dist/commands/diff-unit-scale-fixtures.js.map +1 -0
  23. package/dist/commands/export.d.ts +11 -0
  24. package/dist/commands/export.d.ts.map +1 -1
  25. package/dist/commands/export.js +9 -14
  26. package/dist/commands/export.js.map +1 -1
  27. package/dist/commands/merge.d.ts.map +1 -1
  28. package/dist/commands/merge.js +8 -0
  29. package/dist/commands/merge.js.map +1 -1
  30. package/dist/commands/query-aggregation.d.ts +27 -4
  31. package/dist/commands/query-aggregation.d.ts.map +1 -1
  32. package/dist/commands/query-aggregation.js +67 -6
  33. package/dist/commands/query-aggregation.js.map +1 -1
  34. package/dist/commands/query-output.d.ts +7 -0
  35. package/dist/commands/query-output.d.ts.map +1 -1
  36. package/dist/commands/query-output.js +63 -27
  37. package/dist/commands/query-output.js.map +1 -1
  38. package/dist/commands/query-storey.d.ts +24 -0
  39. package/dist/commands/query-storey.d.ts.map +1 -0
  40. package/dist/commands/query-storey.js +71 -0
  41. package/dist/commands/query-storey.js.map +1 -0
  42. package/dist/commands/query.d.ts +2 -25
  43. package/dist/commands/query.d.ts.map +1 -1
  44. package/dist/commands/query.js +39 -158
  45. package/dist/commands/query.js.map +1 -1
  46. package/dist/commands/schedule-aggregate.d.ts +51 -0
  47. package/dist/commands/schedule-aggregate.d.ts.map +1 -0
  48. package/dist/commands/schedule-aggregate.js +121 -0
  49. package/dist/commands/schedule-aggregate.js.map +1 -0
  50. package/dist/commands/schedule-columns.d.ts +15 -0
  51. package/dist/commands/schedule-columns.d.ts.map +1 -0
  52. package/dist/commands/schedule-columns.js +63 -0
  53. package/dist/commands/schedule-columns.js.map +1 -0
  54. package/dist/commands/schedule-group.d.ts +65 -0
  55. package/dist/commands/schedule-group.d.ts.map +1 -0
  56. package/dist/commands/schedule-group.js +0 -0
  57. package/dist/commands/schedule-group.js.map +1 -0
  58. package/dist/commands/schedule-presets.d.ts +21 -0
  59. package/dist/commands/schedule-presets.d.ts.map +1 -0
  60. package/dist/commands/schedule-presets.js +62 -0
  61. package/dist/commands/schedule-presets.js.map +1 -0
  62. package/dist/commands/schedule-render-html.d.ts +13 -0
  63. package/dist/commands/schedule-render-html.d.ts.map +1 -0
  64. package/dist/commands/schedule-render-html.js +100 -0
  65. package/dist/commands/schedule-render-html.js.map +1 -0
  66. package/dist/commands/schedule-render-md.d.ts +12 -0
  67. package/dist/commands/schedule-render-md.d.ts.map +1 -0
  68. package/dist/commands/schedule-render-md.js +77 -0
  69. package/dist/commands/schedule-render-md.js.map +1 -0
  70. package/dist/commands/schedule-render.d.ts +51 -0
  71. package/dist/commands/schedule-render.d.ts.map +1 -0
  72. package/dist/commands/schedule-render.js +205 -0
  73. package/dist/commands/schedule-render.js.map +1 -0
  74. package/dist/commands/schedule-spec.d.ts +29 -0
  75. package/dist/commands/schedule-spec.d.ts.map +1 -0
  76. package/dist/commands/schedule-spec.js +111 -0
  77. package/dist/commands/schedule-spec.js.map +1 -0
  78. package/dist/commands/schedule.d.ts +23 -0
  79. package/dist/commands/schedule.d.ts.map +1 -0
  80. package/dist/commands/schedule.js +249 -0
  81. package/dist/commands/schedule.js.map +1 -0
  82. package/dist/commands/schema.d.ts.map +1 -1
  83. package/dist/commands/schema.js +192 -17
  84. package/dist/commands/schema.js.map +1 -1
  85. package/dist/commands/stats-aggregation.d.ts +31 -6
  86. package/dist/commands/stats-aggregation.d.ts.map +1 -1
  87. package/dist/commands/stats-aggregation.js +79 -11
  88. package/dist/commands/stats-aggregation.js.map +1 -1
  89. package/dist/commands/stats.d.ts.map +1 -1
  90. package/dist/commands/stats.js +4 -4
  91. package/dist/commands/stats.js.map +1 -1
  92. package/dist/commands/validate.d.ts +7 -4
  93. package/dist/commands/validate.d.ts.map +1 -1
  94. package/dist/commands/validate.js +9 -6
  95. package/dist/commands/validate.js.map +1 -1
  96. package/dist/commands/where-filter.d.ts +42 -0
  97. package/dist/commands/where-filter.d.ts.map +1 -0
  98. package/dist/commands/where-filter.js +88 -0
  99. package/dist/commands/where-filter.js.map +1 -0
  100. package/dist/energy-export.d.ts.map +1 -1
  101. package/dist/energy-export.js +12 -2
  102. package/dist/energy-export.js.map +1 -1
  103. package/dist/geometry-report.d.ts.map +1 -1
  104. package/dist/geometry-report.js +15 -3
  105. package/dist/geometry-report.js.map +1 -1
  106. package/dist/headless-backend.d.ts +20 -5
  107. package/dist/headless-backend.d.ts.map +1 -1
  108. package/dist/headless-backend.js +131 -90
  109. package/dist/headless-backend.js.map +1 -1
  110. package/dist/index.js +22 -0
  111. package/dist/index.js.map +1 -1
  112. package/dist/output.d.ts +13 -0
  113. package/dist/output.d.ts.map +1 -1
  114. package/dist/output.js +21 -0
  115. package/dist/output.js.map +1 -1
  116. package/dist/property-filter-match.d.ts +12 -0
  117. package/dist/property-filter-match.d.ts.map +1 -0
  118. package/dist/property-filter-match.js +17 -0
  119. package/dist/property-filter-match.js.map +1 -0
  120. package/dist/query-overlay-relations.d.ts +29 -0
  121. package/dist/query-overlay-relations.d.ts.map +1 -0
  122. package/dist/query-overlay-relations.js +88 -0
  123. package/dist/query-overlay-relations.js.map +1 -0
  124. package/dist/query-overlay.d.ts +44 -0
  125. package/dist/query-overlay.d.ts.map +1 -0
  126. package/dist/query-overlay.js +100 -0
  127. package/dist/query-overlay.js.map +1 -0
  128. package/package.json +21 -21
@@ -0,0 +1,100 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * HTML rendering for `ifc-lite schedule --format html`.
6
+ *
7
+ * Emits a complete, standalone HTML document (`<!doctype html>` through
8
+ * `</html>`) with one `<table>`: a `<thead>` header row and a `<tbody>` data
9
+ * row per entity, then — with `--subtotals` — a subtotal `<tr>` per contiguous
10
+ * group and a closing grand-total `<tr>`, using the exact same `subtotalCells`
11
+ * layout as CSV/Markdown so a value/label lands in the same column across
12
+ * every output format. A standalone document (not a bare `<table>` fragment)
13
+ * so `ifc-lite schedule ... --format html > schedule.html` opens directly in a
14
+ * browser.
15
+ *
16
+ * Cell text is untrusted model data (IFC attribute/property/quantity values
17
+ * authored by whoever produced the model) landing in an HTML document, so
18
+ * every cell — headers included — is escaped: `&` first (so escaping the
19
+ * other characters doesn't get re-escaped), then `<`, `>`, `"`, `'`. Subtotal
20
+ * rows carry a `class` for CSS hooks, not to change escaping.
21
+ *
22
+ * Contract, and how it differs from `schedule-render-md.ts`: THIS renderer's
23
+ * output is a document a browser executes as HTML the moment it's opened, so
24
+ * full HTML-escaping is mandatory here — unlike the Markdown renderer, which
25
+ * only neutralises table-structure characters and deliberately leaves inline
26
+ * HTML in a cell alone (GFM permits it). Both renderers treat cell content as
27
+ * untrusted; they differ because their output has a different execution
28
+ * model.
29
+ */
30
+ import { columnValueToCsv } from './export.js';
31
+ import { subtotalCells } from './schedule-render.js';
32
+ /** Escape one cell's text for HTML text content (and safe inside a quoted attribute). */
33
+ function escapeHtml(value) {
34
+ const text = columnValueToCsv(value);
35
+ return text
36
+ .replace(/&/g, '&amp;')
37
+ .replace(/</g, '&lt;')
38
+ .replace(/>/g, '&gt;')
39
+ .replace(/"/g, '&quot;')
40
+ .replace(/'/g, '&#39;');
41
+ }
42
+ function tr(cells, cssClass) {
43
+ const attr = cssClass ? ` class="${cssClass}"` : '';
44
+ return ` <tr${attr}>${cells.map(c => `<td>${escapeHtml(c)}</td>`).join('')}</tr>`;
45
+ }
46
+ function documentShell(columns, bodyRows) {
47
+ const headCells = columns.map(c => `<th>${escapeHtml(c.header)}</th>`).join('');
48
+ return [
49
+ '<!doctype html>',
50
+ '<html lang="en">',
51
+ '<head>',
52
+ '<meta charset="utf-8">',
53
+ '<title>Schedule</title>',
54
+ '<style>',
55
+ 'table { border-collapse: collapse; font-family: sans-serif; font-size: 14px; }',
56
+ 'th, td { border: 1px solid #ccc; padding: 4px 8px; text-align: left; }',
57
+ 'th { background: #f0f0f0; }',
58
+ 'tr.subtotal td, tr.total td { font-weight: bold; background: #fafafa; }',
59
+ '</style>',
60
+ '</head>',
61
+ '<body>',
62
+ ' <table>',
63
+ ` <thead><tr>${headCells}</tr></thead>`,
64
+ ' <tbody>',
65
+ ...bodyRows,
66
+ ' </tbody>',
67
+ ' </table>',
68
+ '</body>',
69
+ '</html>',
70
+ '',
71
+ ].join('\n');
72
+ }
73
+ /** Render the schedule as a standalone HTML document containing one `<table>`. */
74
+ export function renderScheduleHtml(columns, rows) {
75
+ return documentShell(columns, rows.map(row => tr(row)));
76
+ }
77
+ /**
78
+ * Render a schedule with `--subtotals` as a standalone HTML document: grouped
79
+ * rows followed by their subtotal `<tr class="subtotal">`, then the grand
80
+ * total `<tr class="total">` — the same layout `renderScheduleCsvWithSubtotals`
81
+ * produces.
82
+ */
83
+ export function renderScheduleHtmlWithSubtotals(columns, plan) {
84
+ const bodyRows = [];
85
+ const subtotalClass = (data) => (data.kind === 'total' ? 'total' : 'subtotal');
86
+ if (plan.groups.length > 0) {
87
+ for (const group of plan.groups) {
88
+ for (const row of group.rows)
89
+ bodyRows.push(tr(row));
90
+ bodyRows.push(tr(subtotalCells(columns, group.subtotal), subtotalClass(group.subtotal)));
91
+ }
92
+ }
93
+ else {
94
+ for (const row of plan.rows)
95
+ bodyRows.push(tr(row));
96
+ }
97
+ bodyRows.push(tr(subtotalCells(columns, plan.total), subtotalClass(plan.total)));
98
+ return documentShell(columns, bodyRows);
99
+ }
100
+ //# sourceMappingURL=schedule-render-html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-render-html.js","sourceRoot":"","sources":["../../src/commands/schedule-render-html.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { ScheduleColumn } from './schedule-columns.js';
2
+ import type { SubtotalPlan } from './schedule-aggregate.js';
3
+ import type { ScheduleRow } from './schedule-render.js';
4
+ /** Render the schedule as a GFM Markdown table. */
5
+ export declare function renderScheduleMarkdown(columns: ScheduleColumn[], rows: ScheduleRow[]): string;
6
+ /**
7
+ * Render a schedule with `--subtotals` as a GFM Markdown table: grouped rows
8
+ * followed by their subtotal row, then the grand total — the same layout
9
+ * `renderScheduleCsvWithSubtotals` produces.
10
+ */
11
+ export declare function renderScheduleMarkdownWithSubtotals(columns: ScheduleColumn[], plan: SubtotalPlan): string;
12
+ //# sourceMappingURL=schedule-render-md.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-render-md.d.ts","sourceRoot":"","sources":["../../src/commands/schedule-render-md.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAkBxD,mDAAmD;AACnD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,MAAM,CAG7F;AAED;;;;GAIG;AACH,wBAAgB,mCAAmC,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CAYzG"}
@@ -0,0 +1,77 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * Markdown (GFM table) rendering for `ifc-lite schedule --format md`.
6
+ *
7
+ * Same row/column shape as `renderScheduleCsv`/`renderScheduleCsvWithSubtotals`
8
+ * (see `schedule-render.ts`): one header row, one `---` separator row (GFM
9
+ * table syntax), then one row per entity, then — with `--subtotals` — a
10
+ * subtotal row per contiguous group and a closing grand-total row, using the
11
+ * exact same `subtotalCells` layout as CSV so a value/label lands in the same
12
+ * column across every output format.
13
+ *
14
+ * A GFM table cell is delimited by `|` and terminated by a newline, so both
15
+ * must be neutralised or the cell would fracture the row: a literal `|`
16
+ * becomes `\|` and an embedded newline becomes `<br>` (GFM tables render
17
+ * inline HTML in a cell, so this stays a single visual line without losing the
18
+ * break). The backslash introduced by the `|` escape is itself escaped first
19
+ * so a value already containing a backslash can't produce an unintended
20
+ * escape sequence.
21
+ *
22
+ * Contract, and how it differs from `schedule-render-html.ts`: this renderer
23
+ * does NOT HTML-escape (`&`/`<`/`>`/`"`/`'` pass through verbatim) — only
24
+ * table-structure characters are neutralised. That is deliberate, not an
25
+ * oversight: GFM's own spec renders inline HTML in a cell, so escaping `<`
26
+ * would defeat legitimate uses (e.g. a deliberate `<br>`) without this tool's
27
+ * involvement being what makes the difference. It is safe in THIS codebase
28
+ * because the output only ever reaches `process.stdout` in `schedule.ts` —
29
+ * nothing here writes it to a file or hands it to an HTML-rendering pipeline.
30
+ * A caller who pipes this Markdown into a raw-HTML-enabled renderer (e.g.
31
+ * `remark`+`rehype-raw` with no sanitize schema) is responsible for treating
32
+ * cell content as untrusted model text and sanitising it there — the same way
33
+ * `escapeHtml` in `schedule-render-html.ts` treats it as untrusted for a
34
+ * renderer that unconditionally executes markup.
35
+ */
36
+ import { columnValueToCsv } from './export.js';
37
+ import { subtotalCells } from './schedule-render.js';
38
+ /** Escape one cell's text for a GFM table: backslash, then `|`, then newline. */
39
+ function escapeMarkdownCell(value) {
40
+ const text = columnValueToCsv(value);
41
+ return text.replace(/\\/g, '\\\\').replace(/\|/g, '\\|').replace(/\r\n|\r|\n/g, '<br>');
42
+ }
43
+ function headerAndSeparator(columns) {
44
+ const header = `| ${columns.map(c => escapeMarkdownCell(c.header)).join(' | ')} |`;
45
+ const separator = `| ${columns.map(() => '---').join(' | ')} |`;
46
+ return [header, separator];
47
+ }
48
+ function rowLine(row) {
49
+ return `| ${row.map(escapeMarkdownCell).join(' | ')} |`;
50
+ }
51
+ /** Render the schedule as a GFM Markdown table. */
52
+ export function renderScheduleMarkdown(columns, rows) {
53
+ const lines = [...headerAndSeparator(columns), ...rows.map(rowLine)];
54
+ return lines.join('\n');
55
+ }
56
+ /**
57
+ * Render a schedule with `--subtotals` as a GFM Markdown table: grouped rows
58
+ * followed by their subtotal row, then the grand total — the same layout
59
+ * `renderScheduleCsvWithSubtotals` produces.
60
+ */
61
+ export function renderScheduleMarkdownWithSubtotals(columns, plan) {
62
+ const lines = headerAndSeparator(columns);
63
+ if (plan.groups.length > 0) {
64
+ for (const group of plan.groups) {
65
+ for (const row of group.rows)
66
+ lines.push(rowLine(row));
67
+ lines.push(rowLine(subtotalCells(columns, group.subtotal)));
68
+ }
69
+ }
70
+ else {
71
+ for (const row of plan.rows)
72
+ lines.push(rowLine(row));
73
+ }
74
+ lines.push(rowLine(subtotalCells(columns, plan.total)));
75
+ return lines.join('\n');
76
+ }
77
+ //# sourceMappingURL=schedule-render-md.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-render-md.js","sourceRoot":"","sources":["../../src/commands/schedule-render-md.ts"],"names":[],"mappings":"AAAA;;+DAE+D;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGrD,iFAAiF;AACjF,SAAS,kBAAkB,CAAC,KAAc;IACxC,MAAM,IAAI,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;AAC1F,CAAC;AAED,SAAS,kBAAkB,CAAC,OAAyB;IACnD,MAAM,MAAM,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACnF,MAAM,SAAS,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IAChE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,OAAO,CAAC,GAAc;IAC7B,OAAO,KAAK,GAAG,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;AAC1D,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,sBAAsB,CAAC,OAAyB,EAAE,IAAmB;IACnF,MAAM,KAAK,GAAG,CAAC,GAAG,kBAAkB,CAAC,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IACrE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mCAAmC,CAAC,OAAyB,EAAE,IAAkB;IAC/F,MAAM,KAAK,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC1C,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChC,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI;gBAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;YACvD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IACxD,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACxD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,51 @@
1
+ import type { ScheduleColumn } from './schedule-columns.js';
2
+ import type { SubtotalPlan, SubtotalRowData } from './schedule-aggregate.js';
3
+ /** One resolved row: the raw value for each column, in column order. */
4
+ export type ScheduleRow = unknown[];
5
+ /**
6
+ * Render the schedule as RFC-4180 CSV. Headers and cells both go through the
7
+ * shared escaper so a header or value containing a comma, quote, newline, or a
8
+ * leading formula trigger is correctly quoted/neutralised.
9
+ */
10
+ export declare function renderScheduleCsv(columns: ScheduleColumn[], rows: ScheduleRow[]): string;
11
+ /**
12
+ * Render the schedule as a JSON array of row objects keyed by header. Raw
13
+ * values keep their type; a missing value is `null`.
14
+ */
15
+ export declare function renderScheduleJson(columns: ScheduleColumn[], rows: ScheduleRow[]): Record<string, unknown>[];
16
+ /**
17
+ * A subtotal/total row as a full-width cell array. The label sits in a
18
+ * column none of this row's aggregations target (see `chooseLabelCol`); each
19
+ * numeric aggregation fills its own target column; every other column is
20
+ * blank.
21
+ *
22
+ * `--subtotals` allows more than one aggregation on the same `--columns`
23
+ * header (`sum:Area,avg:Area` is a reasonable request, and the JSON renderer
24
+ * already keys those by `spec` without collision — see `subtotalToJson`).
25
+ * The tabular formats have only one cell per column, so when two or more
26
+ * aggregations target the same column their values are joined into one cell
27
+ * as `"sum: 30, avg: 15"` instead of the second one silently overwriting the
28
+ * first. A single aggregation on a column keeps rendering as its bare value
29
+ * (a number, same as before) so the common case is unaffected.
30
+ *
31
+ * When every declared column is an aggregation target there is no free slot
32
+ * for the label at all (`--columns Area --subtotals sum:Area`), so the label
33
+ * takes a targeted column. Those displaced aggregations are appended to the
34
+ * label cell as `Total (sum: 30, avg: 15)` rather than dropped, which is what
35
+ * used to happen: the tabular formats printed a bare `Total` while JSON still
36
+ * reported the values.
37
+ *
38
+ * Exported so the Markdown/HTML renderers (`schedule-render-md.ts`,
39
+ * `schedule-render-html.ts`) lay out subtotal/total rows identically to CSV —
40
+ * same label, same column placement — instead of re-deriving the shape.
41
+ */
42
+ export declare function subtotalCells(columns: ScheduleColumn[], data: SubtotalRowData): unknown[];
43
+ /**
44
+ * Render a schedule with `--subtotals`. Grouped output emits each contiguous
45
+ * group's rows followed by its subtotal row; the grand total closes the table.
46
+ * Without `--group-by` only the flat rows and the grand total are emitted.
47
+ */
48
+ export declare function renderScheduleCsvWithSubtotals(columns: ScheduleColumn[], plan: SubtotalPlan): string;
49
+ /** Render a schedule with `--subtotals` as flat JSON rows plus `__row`-marked subtotal/total objects. */
50
+ export declare function renderScheduleJsonWithSubtotals(columns: ScheduleColumn[], plan: SubtotalPlan): Record<string, unknown>[];
51
+ //# sourceMappingURL=schedule-render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-render.d.ts","sourceRoot":"","sources":["../../src/commands/schedule-render.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAiB,MAAM,yBAAyB,CAAC;AAE5F,wEAAwE;AACxE,MAAM,MAAM,WAAW,GAAG,OAAO,EAAE,CAAC;AAEpC;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,MAAM,CAOxF;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAE5G;AAwDD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,EAAE,CAkCzF;AASD;;;;GAIG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CAgBpG;AAUD,yGAAyG;AACzG,wBAAgB,+BAA+B,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAYxH"}
@@ -0,0 +1,205 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * CSV / JSON rendering for `ifc-lite schedule`.
6
+ *
7
+ * A schedule is a list of header names plus one raw value per (row, column).
8
+ * CSV routes every cell through `escapeCsvCell` (RFC 4180 quoting + the
9
+ * spreadsheet-formula-injection guard) shared with the `export` command; a
10
+ * missing value renders as an empty CSV cell and as JSON `null`.
11
+ */
12
+ import { escapeCsvCell } from '@ifc-lite/export';
13
+ import { columnValueToCsv } from './export.js';
14
+ /**
15
+ * Render the schedule as RFC-4180 CSV. Headers and cells both go through the
16
+ * shared escaper so a header or value containing a comma, quote, newline, or a
17
+ * leading formula trigger is correctly quoted/neutralised.
18
+ */
19
+ export function renderScheduleCsv(columns, rows) {
20
+ const sep = ',';
21
+ const headerLine = columns.map(c => escapeCsvCell(c.header, { delimiter: sep })).join(sep);
22
+ const dataLines = rows.map(row => row.map(value => escapeCsvCell(columnValueToCsv(value), { delimiter: sep })).join(sep));
23
+ return [headerLine, ...dataLines].join('\n');
24
+ }
25
+ /**
26
+ * Render the schedule as a JSON array of row objects keyed by header. Raw
27
+ * values keep their type; a missing value is `null`.
28
+ */
29
+ export function renderScheduleJson(columns, rows) {
30
+ return rows.map(row => scheduleRowToJson(columns, row));
31
+ }
32
+ /** One data row as a header-keyed object; a missing value is `null`. */
33
+ function scheduleRowToJson(columns, row) {
34
+ const obj = {};
35
+ columns.forEach((col, i) => {
36
+ const value = row[i];
37
+ obj[col.header] = value == null ? null : value;
38
+ });
39
+ return obj;
40
+ }
41
+ /**
42
+ * The subtotal/total label for a group: `Subtotal (H1=v1, H2=v2)` or `Total`,
43
+ * suffixed with `: <n>` when a `count` aggregation is present (the row count of
44
+ * the group / whole schedule).
45
+ */
46
+ function subtotalLabel(data) {
47
+ const base = data.kind === 'total'
48
+ ? 'Total'
49
+ : `Subtotal (${data.groupValues.map(g => `${g.header}=${g.value == null ? '' : String(g.value)}`).join(', ')})`;
50
+ const count = data.values.find(v => v.spec === 'count');
51
+ return count ? `${base}: ${count.value}` : base;
52
+ }
53
+ /**
54
+ * Pick the column that carries the subtotal/total label. A forced choice
55
+ * (the first group column, or column 0 for the grand total) can collide with
56
+ * a `--subtotals` aggregation targeting that same column — `sum:Area` when
57
+ * `Area` is the first declared column, or the first group-by header — and a
58
+ * fixed-width row array has no spare slot to hold both a text label and a
59
+ * numeric value in the same cell. Prefer, in order: the first group column
60
+ * that is not itself an aggregation target (keeps the label meaningfully
61
+ * tied to a group value), then the first column overall that is not a
62
+ * target, and only when every column is a target — no free slot exists at
63
+ * all — fall back to the historical group/0 choice (that one column's
64
+ * aggregation is what the label displaces; every other aggregation still
65
+ * lands in its own column).
66
+ */
67
+ function chooseLabelCol(columns, data) {
68
+ const targetIdx = new Set();
69
+ for (const v of data.values) {
70
+ if (v.spec === 'count')
71
+ continue;
72
+ const header = v.spec.slice(v.spec.indexOf(':') + 1);
73
+ const idx = columns.findIndex(c => c.header === header);
74
+ if (idx !== -1)
75
+ targetIdx.add(idx);
76
+ }
77
+ for (const g of data.groupValues) {
78
+ if (!targetIdx.has(g.index))
79
+ return g.index;
80
+ }
81
+ for (let i = 0; i < columns.length; i++) {
82
+ if (!targetIdx.has(i))
83
+ return i;
84
+ }
85
+ return data.groupValues.length > 0 ? data.groupValues[0].index : 0;
86
+ }
87
+ /**
88
+ * A subtotal/total row as a full-width cell array. The label sits in a
89
+ * column none of this row's aggregations target (see `chooseLabelCol`); each
90
+ * numeric aggregation fills its own target column; every other column is
91
+ * blank.
92
+ *
93
+ * `--subtotals` allows more than one aggregation on the same `--columns`
94
+ * header (`sum:Area,avg:Area` is a reasonable request, and the JSON renderer
95
+ * already keys those by `spec` without collision — see `subtotalToJson`).
96
+ * The tabular formats have only one cell per column, so when two or more
97
+ * aggregations target the same column their values are joined into one cell
98
+ * as `"sum: 30, avg: 15"` instead of the second one silently overwriting the
99
+ * first. A single aggregation on a column keeps rendering as its bare value
100
+ * (a number, same as before) so the common case is unaffected.
101
+ *
102
+ * When every declared column is an aggregation target there is no free slot
103
+ * for the label at all (`--columns Area --subtotals sum:Area`), so the label
104
+ * takes a targeted column. Those displaced aggregations are appended to the
105
+ * label cell as `Total (sum: 30, avg: 15)` rather than dropped, which is what
106
+ * used to happen: the tabular formats printed a bare `Total` while JSON still
107
+ * reported the values.
108
+ *
109
+ * Exported so the Markdown/HTML renderers (`schedule-render-md.ts`,
110
+ * `schedule-render-html.ts`) lay out subtotal/total rows identically to CSV —
111
+ * same label, same column placement — instead of re-deriving the shape.
112
+ */
113
+ export function subtotalCells(columns, data) {
114
+ const cells = columns.map(() => '');
115
+ const labelCol = chooseLabelCol(columns, data);
116
+ // Group every non-count aggregation by its target column index so a
117
+ // collision can be detected before any cell is written. Aggregations
118
+ // targeting the label's own column are kept too (under `labelCol`), to be
119
+ // folded into the label text rather than dropped.
120
+ const byIdx = new Map();
121
+ for (const v of data.values) {
122
+ if (v.spec === 'count')
123
+ continue;
124
+ const header = v.spec.slice(v.spec.indexOf(':') + 1);
125
+ const idx = columns.findIndex(c => c.header === header);
126
+ if (idx === -1)
127
+ continue; // header is not a declared column: nowhere to put it
128
+ const bucket = byIdx.get(idx);
129
+ if (bucket)
130
+ bucket.push(v);
131
+ else
132
+ byIdx.set(idx, [v]);
133
+ }
134
+ const label = subtotalLabel(data);
135
+ const displaced = byIdx.get(labelCol);
136
+ cells[labelCol] = displaced ? `${label} (${joinAggValues(displaced)})` : label;
137
+ for (const [idx, values] of byIdx) {
138
+ if (idx === labelCol)
139
+ continue; // already folded into the label cell
140
+ if (values.length === 1) {
141
+ cells[idx] = values[0].value == null ? '' : values[0].value;
142
+ continue;
143
+ }
144
+ // Two-plus aggregations on one column: join as "mode: value" pairs so
145
+ // every value survives instead of the last one winning.
146
+ cells[idx] = joinAggValues(values);
147
+ }
148
+ return cells;
149
+ }
150
+ /** `sum:Area`/`avg:Area` values as the shared `"sum: 30, avg: 15"` cell text. */
151
+ function joinAggValues(values) {
152
+ return values
153
+ .map(v => `${v.spec.slice(0, v.spec.indexOf(':'))}: ${v.value == null ? '' : v.value}`)
154
+ .join(', ');
155
+ }
156
+ /**
157
+ * Render a schedule with `--subtotals`. Grouped output emits each contiguous
158
+ * group's rows followed by its subtotal row; the grand total closes the table.
159
+ * Without `--group-by` only the flat rows and the grand total are emitted.
160
+ */
161
+ export function renderScheduleCsvWithSubtotals(columns, plan) {
162
+ const sep = ',';
163
+ const escapeRow = (row) => row.map(value => escapeCsvCell(columnValueToCsv(value), { delimiter: sep })).join(sep);
164
+ const lines = [columns.map(c => escapeCsvCell(c.header, { delimiter: sep })).join(sep)];
165
+ if (plan.groups.length > 0) {
166
+ for (const group of plan.groups) {
167
+ for (const row of group.rows)
168
+ lines.push(escapeRow(row));
169
+ lines.push(escapeRow(subtotalCells(columns, group.subtotal)));
170
+ }
171
+ }
172
+ else {
173
+ for (const row of plan.rows)
174
+ lines.push(escapeRow(row));
175
+ }
176
+ lines.push(escapeRow(subtotalCells(columns, plan.total)));
177
+ return lines.join('\n');
178
+ }
179
+ /** The JSON form of a subtotal/total row: `__row` marker + group + aggregated fields. */
180
+ function subtotalToJson(data) {
181
+ const obj = { __row: data.kind };
182
+ for (const g of data.groupValues)
183
+ obj[g.header] = g.value == null ? null : g.value;
184
+ for (const v of data.values)
185
+ obj[v.spec] = v.value;
186
+ return obj;
187
+ }
188
+ /** Render a schedule with `--subtotals` as flat JSON rows plus `__row`-marked subtotal/total objects. */
189
+ export function renderScheduleJsonWithSubtotals(columns, plan) {
190
+ const out = [];
191
+ if (plan.groups.length > 0) {
192
+ for (const group of plan.groups) {
193
+ for (const row of group.rows)
194
+ out.push(scheduleRowToJson(columns, row));
195
+ out.push(subtotalToJson(group.subtotal));
196
+ }
197
+ }
198
+ else {
199
+ for (const row of plan.rows)
200
+ out.push(scheduleRowToJson(columns, row));
201
+ }
202
+ out.push(subtotalToJson(plan.total));
203
+ return out;
204
+ }
205
+ //# sourceMappingURL=schedule-render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-render.js","sourceRoot":"","sources":["../../src/commands/schedule-render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ /** The reusable definition persisted by `--save` and consumed by `--spec`. */
2
+ export interface ScheduleSpec {
3
+ preset?: string;
4
+ type?: string;
5
+ columns?: string;
6
+ where?: string;
7
+ sort?: string;
8
+ groupBy?: string;
9
+ subtotals?: string;
10
+ format?: string;
11
+ }
12
+ /**
13
+ * Load a `--spec` file. Fatal (not a thrown exception the top-level handler
14
+ * reformats) on every way a spec can fail to be a usable schedule definition:
15
+ * the file doesn't exist or isn't readable, its content isn't valid JSON,
16
+ * isn't a JSON object, or has a declared field that isn't a string — each
17
+ * would otherwise surface only once `schedule.ts` tries to use the bad value
18
+ * (or, for a non-object/wrong-type field, might silently coerce into an empty
19
+ * `--columns`/`--type`, producing an empty schedule with no error at all).
20
+ */
21
+ export declare function loadScheduleSpec(path: string): Promise<ScheduleSpec>;
22
+ /**
23
+ * Write the schedule definition actually used by this invocation (after
24
+ * `--preset` defaults are folded in, so a saved spec is self-contained and
25
+ * `--spec`-loadable without also needing the original `--preset`) to `path`.
26
+ * Undefined fields are omitted rather than written as `null`.
27
+ */
28
+ export declare function saveScheduleSpec(path: string, spec: ScheduleSpec): Promise<void>;
29
+ //# sourceMappingURL=schedule-spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-spec.d.ts","sourceRoot":"","sources":["../../src/commands/schedule-spec.ts"],"names":[],"mappings":"AA6BA,8EAA8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAMD;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAwD1E;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAWtF"}
@@ -0,0 +1,111 @@
1
+ /* This Source Code Form is subject to the terms of the Mozilla Public
2
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
+ /**
5
+ * `--save`/`--spec` reusable schedule definitions for `ifc-lite schedule`.
6
+ *
7
+ * A spec is a small JSON file holding exactly the flag strings the CLI itself
8
+ * accepts — `--type`, `--columns`, `--where`, `--sort`, `--group-by`,
9
+ * `--subtotals`, `--format` — plus an optional `preset` field naming a
10
+ * `--preset` to start from. It is a plain file at whatever path the caller
11
+ * gives `--spec`/`--save`, not a config-directory convention: `--save` writes
12
+ * one, `--spec` reads one back.
13
+ *
14
+ * Precedence when both a spec and an explicit flag are given (`schedule.ts`):
15
+ * explicit CLI flag > spec field > preset field (whether `--preset` on the
16
+ * command line or `preset` inside the spec) — the same "most specific wins"
17
+ * rule `--preset` already uses against explicit flags.
18
+ *
19
+ * A spec naming an unresolvable `preset`, or missing/malformed in a way that
20
+ * would otherwise silently resolve to an empty schedule, is a `fatal(...)`:
21
+ * failing loudly beats emitting a table with no rows or columns.
22
+ */
23
+ import { readFile, writeFile } from 'node:fs/promises';
24
+ import { fatal } from '../output.js';
25
+ import { logger } from '../logger.js';
26
+ import { parseWhereFilter } from './where-filter.js';
27
+ const SPEC_FIELDS = [
28
+ 'preset', 'type', 'columns', 'where', 'sort', 'groupBy', 'subtotals', 'format',
29
+ ];
30
+ /**
31
+ * Load a `--spec` file. Fatal (not a thrown exception the top-level handler
32
+ * reformats) on every way a spec can fail to be a usable schedule definition:
33
+ * the file doesn't exist or isn't readable, its content isn't valid JSON,
34
+ * isn't a JSON object, or has a declared field that isn't a string — each
35
+ * would otherwise surface only once `schedule.ts` tries to use the bad value
36
+ * (or, for a non-object/wrong-type field, might silently coerce into an empty
37
+ * `--columns`/`--type`, producing an empty schedule with no error at all).
38
+ */
39
+ export async function loadScheduleSpec(path) {
40
+ let content;
41
+ try {
42
+ content = await readFile(path, 'utf-8');
43
+ }
44
+ catch (err) {
45
+ fatal(`--spec "${path}" could not be read: ${err.message}`);
46
+ }
47
+ let parsed;
48
+ try {
49
+ parsed = JSON.parse(content);
50
+ }
51
+ catch (err) {
52
+ fatal(`--spec "${path}" is not valid JSON: ${err.message}`);
53
+ }
54
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
55
+ fatal(`--spec "${path}" must be a JSON object with schedule fields (type, columns, where, sort, groupBy, subtotals, format, preset), got ${Array.isArray(parsed) ? 'an array' : typeof parsed}.`);
56
+ }
57
+ const obj = parsed;
58
+ // An unrecognised key (a mistyped "group-by" instead of "groupBy", say) used
59
+ // to be silently ignored by the loop below, which only ever reads the
60
+ // fields it already knows about — the mistyped key had no effect and the
61
+ // command exited 0 with whatever the OTHER, correctly-spelled fields
62
+ // produced (an ungrouped schedule, for a mistyped "groupBy"), never
63
+ // surfacing that the field was dropped. Reject it up front instead.
64
+ const unknownKeys = Object.keys(obj).filter(k => !SPEC_FIELDS.includes(k));
65
+ if (unknownKeys.length > 0) {
66
+ fatal(`--spec "${path}" has unrecognised field(s): ${unknownKeys.join(', ')}. Valid fields: ${SPEC_FIELDS.join(', ')}.`);
67
+ }
68
+ const spec = {};
69
+ for (const field of SPEC_FIELDS) {
70
+ if (!(field in obj))
71
+ continue;
72
+ const value = obj[field];
73
+ if (typeof value !== 'string') {
74
+ fatal(`--spec "${path}": field "${field}" must be a string, got ${Array.isArray(value) ? 'an array' : typeof value}.`);
75
+ }
76
+ spec[field] = value;
77
+ }
78
+ if (spec.preset === undefined && spec.type === undefined) {
79
+ fatal(`--spec "${path}" declares neither "preset" nor "type" — nothing to schedule. Expected at least one, plus "columns" unless a preset supplies it.`);
80
+ }
81
+ // Validate `where` the same way an explicit --where flag is (schedule.ts
82
+ // calls the identical parseWhereFilter once it applies the filter) —
83
+ // eagerly here, rather than deferring the same fatal(...) until the run
84
+ // gets around to using it (by which point --save, if also given, may
85
+ // already have written a spec built from this same broken value).
86
+ if (spec.where !== undefined) {
87
+ parseWhereFilter(spec.where);
88
+ }
89
+ return spec;
90
+ }
91
+ /**
92
+ * Write the schedule definition actually used by this invocation (after
93
+ * `--preset` defaults are folded in, so a saved spec is self-contained and
94
+ * `--spec`-loadable without also needing the original `--preset`) to `path`.
95
+ * Undefined fields are omitted rather than written as `null`.
96
+ */
97
+ export async function saveScheduleSpec(path, spec) {
98
+ const out = {};
99
+ for (const field of SPEC_FIELDS) {
100
+ if (spec[field] !== undefined)
101
+ out[field] = spec[field];
102
+ }
103
+ try {
104
+ await writeFile(path, JSON.stringify(out, null, 2) + '\n');
105
+ }
106
+ catch (err) {
107
+ fatal(`--save "${path}" could not be written: ${err.message}`);
108
+ }
109
+ logger.info(`Schedule spec written to ${path}`);
110
+ }
111
+ //# sourceMappingURL=schedule-spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schedule-spec.js","sourceRoot":"","sources":["../../src/commands/schedule-spec.ts"],"names":[],"mappings":"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"}