Schema Builder
Summaries
Reducer-based column summaries that work in both buffered and stream mode.
Summaries are reducer-based so the exact same schema works in buffered mode and in stream mode.
The recommended public shape is the callback builder:
summary.cell({...})for reducer-based value cellssummary.label("TOTAL", { style })for static labelssummary.empty()for alignment when a summary row intentionally skips a column
A single summary cell
import { createExcelSchema } from "@chronicstone/typed-xlsx";
const schema = createExcelSchema<{ amount: number }>()
.column("amount", {
accessor: "amount",
summary: (summary) => [
summary.cell({
init: () => 0,
step: (acc, row) => acc + row.amount,
finalize: (acc) => acc,
style: {
numFmt: "$#,##0.00",
},
}),
],
})
.build();
Multiple summary rows
When you return multiple cells from the builder callback, each summary index becomes its own summary row. This is how you recreate layouts like "TOTAL BEFORE VAT" followed by "TOTAL".
import { createExcelSchema } from "@chronicstone/typed-xlsx";
const schema = createExcelSchema<{ amount: number }>()
.column("label", {
accessor: () => "",
summary: (summary) => [summary.label("TOTAL BEFORE VAT"), summary.label("TOTAL")],
})
.column("amount", {
accessor: "amount",
summary: (summary) => [
summary.cell({
init: () => 0,
step: (acc, row) => acc + row.amount,
finalize: (acc) => acc,
}),
summary.cell({
init: () => 0,
step: (acc, row) => acc + row.amount,
finalize: (acc) => acc * 1.2,
style: { numFmt: "$#,##0.00" },
}),
],
})
.build();
Styled label and empty cells
import { createExcelSchema } from "@chronicstone/typed-xlsx";
const schema = createExcelSchema<{ amount: number }>()
.column("label", {
accessor: () => "",
summary: (summary) => [
summary.label("TOTAL", {
style: {
font: { bold: true },
alignment: { horizontal: "right" },
},
}),
summary.empty(),
],
})
.column("amount", {
accessor: "amount",
summary: (summary) => [
summary.cell({
init: () => 0,
step: (acc, row) => acc + row.amount,
finalize: (acc) => acc,
style: { numFmt: "$#,##0.00" },
}),
summary.cell({
init: () => 0,
step: (acc, row) => acc + row.amount,
finalize: (acc) => acc * 1.2,
style: { numFmt: "$#,##0.00" },
}),
],
})
.build();
The important idea is that summaries no longer receive rows: T[]. They accumulate progressively with init, step, and finalize, which is what makes stream mode possible without holding the full dataset in memory.