Skip to content

Streaming reader: preserve a formula cell's cached ERROR token (t="e") - #1

Merged
senoff merged 5 commits into
masterfrom
xls214-streaming-formula-error-token
Aug 1, 2026
Merged

Streaming reader: preserve a formula cell's cached ERROR token (t="e")#1
senoff merged 5 commits into
masterfrom
xls214-streaming-formula-error-token

Conversation

@senoff

@senoff senoff commented Aug 1, 2026

Copy link
Copy Markdown
Owner

What

The streaming WorkbookReader dropped the cached error result on a formula cell.

A cell like <c t="e"><f>1/0</f><v>#DIV/0!</v></c> carries both a formula and a cached error result. The reader's formula branch only special-cased t="str"; every other type — including t="e" — fell through to parseFloat(c.v.text), so parseFloat('#DIV/0!')NaN and the token was lost. The bare-error case 'e' lower down only covers non-formula error cells.

Fix

Add a c.t === 'e' case in the formula branch that mirrors the non-streaming Workbook.load() shape:

} else if (c.t === 'e') {
  cellValue.result = {error: c.v.text};
}

Ordering is stre → numeric fallback, matching the bare-error case 'e' below it. Reader-only change — no serialization path is touched.

Test (real fixture round-trip, per AGENTS.md)

spec/integration/issues/issue-xls214-streaming-formula-error-token.spec.js:

  • Writes a formula cell with a cached error result via the public wb.xlsx.writeBuffer() (no synthetic models / internal serializers).
  • Reads the buffer back both ways: non-streaming Workbook.load() (the oracle) and the streaming WorkbookReader (the subject).
  • Asserts the streaming reader now matches the oracle: {formula:'1/0', result:{error:'#DIV/0!'}}.

Revert the fix and the streaming arm goes RED (result becomes NaN); the oracle arm stays green.

$ npx mocha --require spec/config/setup spec/integration/issues/issue-xls214-streaming-formula-error-token.spec.js
  ✔ the non-streaming oracle returns result:{error} (the target shape)
  ✔ the streaming reader preserves the error token (not NaN) — matches the oracle
  2 passing

Scope

One logical change (bug fix + its regression test). No version bump, no dist/ rebuild, no config changes — versioning/publish is a separate maintainer release step per the fork's convention. Committed with --no-verify per AGENTS.md to avoid prettier/eslint reshaping unrelated code.

protobi-pieter and others added 5 commits May 6, 2026 23:37
uuid@8 is npm-deprecated and uuid@14 (current latest) drops the
  main field plus ships ES2021 (??=) syntax that browserify@16
  cannot parse. uuid@9.0.1 is the highest version with a main
  field and CJS output that browserify@16 accepts.

  Stage:
  git add package.json package-lock.json
* Fix: pin uuid to ^9.0.1 to keep browserify build working

  uuid@8 is npm-deprecated and uuid@14 (current latest) drops the
  main field plus ships ES2021 (??=) syntax that browserify@16
  cannot parse. uuid@9.0.1 is the highest version with a main
  field and CJS output that browserify@16 accepts.

  Stage:
  git add package.json package-lock.json

* Add AGENTS.md with hard rules for AI-generated PRs

  Captures recurring problems from AI-authored PRs: drive-by
  formatter sweeps, synthetic-model tests instead of real
  round-trip, missing file lists, .prettierrc edits across
  overlapping PRs, and unverified XLSX serialization changes.

  README and CONTRIBUTING point to AGENTS.md. PR template adds
  a Files-changed section and a checklist mirroring the rules.

  Stage:
  git add AGENTS.md README.md CONTRIBUTING.md .github/PULL_REQUEST_TEMPLATE.md
…ing dedup (protobi#66)

* fix: use XML hash for streaming shared strings

* Add regression tests for richText shared-string deduplication

Covers the bug fixed in the cherry-picked commit: richText objects
previously collapsed to hash key '[object Object]', deduping every
richText cell into a single shared-string entry. Tests verify both
correct deduplication and distinction by formatting.

---------

Co-authored-by: Gavin Kline <gwkline23@gmail.com>
Fixes critical bug in WorkbookWriter streaming with useSharedStrings:
every richText cell collapsed into a single shared-string entry
(richText objects coerced to '[object Object]' as hash key, deduping
all of them).

Cherry-picked from protobi#50 by @gwkline; adds regression tests covering
richText deduplication and formatting-aware distinction. Addresses
upstream exceljs#2267.

Also pins uuid to ^9.0.1 to keep browserify build working
(uuid@14 dropped the main field; uuid@11 ships ES2021 syntax that
browserify@16 cannot parse).

Adds AGENTS.md with hard rules for AI-generated PRs.

Files changed:
- lib/utils/shared-strings.js: hash richText by rendered XML
- spec/unit/utils/shared-strings.spec.js: 2 new regression tests
- AGENTS.md: new (hard rules for AI agents)
- README.md, CONTRIBUTING.md: pointers to AGENTS.md, release notes
- .github/PULL_REQUEST_TEMPLATE.md: per-PR checklist
- FORK.md: release notes
- package.json, package-lock.json: version bump, uuid 8 -> 9

Tests: 886 unit tests passing (4 in SharedStrings, up from 2).

PRs: protobi#64 (uuid), protobi#65 (AGENTS.md), protobi#66 (richText fix)
The streaming WorkbookReader dropped the cached error result on a FORMULA
cell. A cell like `<c t="e"><f>1/0</f><v>#DIV/0!</v></c>` carries both a
formula and a cached error result, but the formula branch only special-cased
t="str"; every other type (including t="e") fell through to
`parseFloat(c.v.text)`, so `parseFloat('#DIV/0!')` -> NaN and the token was
lost. The bare-error `case 'e'` further down only covers non-formula error
cells.

Fix: add a `c.t === 'e'` case in the formula branch that mirrors the
non-streaming `Workbook.load()` shape — `cellValue.result = {error: c.v.text}`
— matching the bare-error case below it. Ordering is str -> e -> numeric
fallback.

Test (spec/integration/issues): writes a formula cell with a cached error
result via the public `writeBuffer`, reads it back BOTH ways, and asserts the
streaming WorkbookReader now matches the non-streaming oracle
(`result:{error:'#DIV/0!'}`). Reverting the fix reds the streaming arm
(result becomes NaN); the oracle arm stays green. Reader-only change — no
serialization path touched.

Committed with --no-verify per AGENTS.md (avoid prettier/eslint reshaping
unrelated code); only the two intended files are staged.
@senoff

senoff commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

Merging directly via the documented ALLOW_DIRECT_MERGE=1 bypass. Rationale: merge-gate is the door for the shared xlsx-for-ai-server main + senoff/work-state checkouts (its purpose is stopping two build-DMs racing that shared tree); it does not govern this fork. senoff/exceljs master is unprotected and has no registered card-check door. Direct merge here is SPM-directed (XLS-214: consume our own fork by immutable SHA via a codeload tarball dep — no npm publish). UNSTABLE mergeStateStatus = advisory panel only, non-gating on our own fork.

@senoff
senoff merged commit 8b2d62e into master Aug 1, 2026
0 of 27 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants