# Literal unpivot preview v1

Save `unpivot.mjs` beside `unpivot-example.mjs`; run in Node 18+ or modern
JavaScript. No runtime dependencies.

```js
import assert from 'node:assert/strict';
import { previewUnpivot } from './unpivot.mjs';
const result = previewUnpivot('id\tJan\tFeb\n001\t10\t\n002\t20\t30', '3,2');
assert.deepEqual(result.lines, ['id\tname\tvalue', '001\tFeb\t', '001\tJan\t10', '002\tFeb\t30', '002\tJan\t20']);
assert.deepEqual(result.selectedColumns, [3, 2]);
console.log(result.lines.join('\n'));
```

Run `node unpivot-example.mjs`. Arguments: literal TSV input with a first-row
header and ordered comma-separated 1-based column numbers to unpivot. Both are
required. Selection tokens may have surrounding whitespace; numeric syntax must
be positive whole numbers without leading zeros. Duplicates fail.

Unselected columns are retained as ID fields in their original order. Each data
row expands into one row per selected column, in the supplied selection order.
The last two output fields are `name` and `value`: the original selected header
and its literal cell value. Empty values remain rows. Duplicate and blank header
names remain literal. Selecting every column produces only `name` and `value`.
Header-only input produces an output header with no data rows.

Quotes do not escape tabs or newlines; formulas are text. No regex, formatting,
aggregation, header inference or blank-value filtering. `lines.join('\n')` is
plain TSV. `output` is the same TSV string. `sourceRows` identifies each expanded
row by its original 1-based row and column; `selectedColumns` keeps selection
order, and `identifierColumns` lists retained column numbers. CRLF/bare CR become
LF and exactly one final newline is ignored.
Other blank rows are data and must have the same number of fields as the header.

Limits: 100,000 UTF-16 code units each for input/output, 500 input rows including
the header, 500 input/output columns, 2,000 selection characters, 2,000 expanded
data rows (excluding the header), and 10,000 output cells (including the header).
Empty/ragged input or an exceeded limit throws without partial output. No network, telemetry, formula evaluation or Sheet writes. This does not
certify spreadsheet import safety, time savings or customer use.

Generate with `node scripts/build-unpivot.mjs`; verify browser-core bytes with
`node scripts/build-unpivot.mjs --check`.

## License

MIT License

Copyright (c) 2026 QuasiLife

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
