Skip to content

feat!: shrink the public export surface to what it means to publish - #563

Closed
cjbarth wants to merge 1 commit into
masterfrom
feat/audit-export-surface
Closed

feat!: shrink the public export surface to what it means to publish#563
cjbarth wants to merge 1 commit into
masterfrom
feat/audit-export-surface

Conversation

@cjbarth

@cjbarth cjbarth commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Closes #551

index.ts did export * from "./utils", so every helper written for signed-xml.ts to use became public API at the same time. That is how findChilds stayed published with no callers, and how each new helper joined the surface without anyone choosing to publish it.

The keep list is informed by the consumer survey on the issue, which checked what dependents and public code actually import rather than what we call.

What stays

Export Why
derToPem, pemToDer, normalizePem key-format conversion is a real consumer need; pemToDer has a confirmed importer, and three separate projects hand-rolled a normalizePem, which says the need is real and the export merely undiscoverable
findAncestorNs already part of the custom-canonicalization contract in practice — akira-io/node-efatura, kacheablecr-jpg/nervus-backend and dressoria/sri-signing-worker all import { C14nCanonicalization, findAncestorNs } from "xml-crypto"

What goes

findAttr, findChildren, findChilds, isDescendantOf, isArrayHasLength, encodeSpecialCharactersInAttribute, encodeSpecialCharactersInText, validateDigestValue, BASE64_REGEX, EXTRACT_X509_CERTS, PEM_FORMAT_REGEX. None has a confirmed importer; every code-search hit was a same-named local helper, a fork, a vendored copy, or a CVE-reproduction corpus.

validateDigestValue goes internal despite the issue's "check before dropping" note. Nobody imports it, so the risk of pushing someone toward a naive === is not realised — and publishing the constant-time comparison is itself what invites reimplementation. It belongs behind the API that already uses it.

BASE64_REGEX and PEM_FORMAT_REGEX lose the export keyword outright since no sibling uses them. The rest stay exported from utils.ts for their siblings but are no longer re-exported to consumers — a sibling and a consumer reaching a helper through the same export keyword is the root cause, and index.ts is now the only place that can widen the surface.

lib/ was public too

index.ts only governs the barrel. With no exports map, every file under lib/ was reachable by path, and consumers do reach: shunkica/fiskalizacija2-js does

import { Sha256 } from "xml-crypto/lib/hash-algorithms.js";

for a class we never exported. So package.json now declares an exports map naming the entry point and package.json and nothing else. Verified against a real package resolution: the barrel resolves, xml-crypto/lib/hash-algorithms.js fails with ERR_PACKAGE_PATH_NOT_EXPORTED, xml-crypto/package.json still resolves. main and types stay for resolvers that ignore exports.

The bundled algorithm classes remain reachable through the registries that name them, which the README now documents:

const Sha256 = new SignedXml().HashAlgorithms["http://www.w3.org/2001/04/xmlenc#sha256"];

Guard against regrowth

test/public-api-tests.spec.ts pins both the runtime export names and the declared subpaths, so widening either shows up as a reviewable diff rather than a side effect of adding a helper or a file. Confirmed it fails on master, listing exactly the names withdrawn here.

README fix found along the way

The README documented an xpath export that 6.x does not have — require("xml-crypto").xpath is undefined, so the verification example would have thrown on select(...). The examples now use the xpath package directly, whose select() takes the expression first; verified the corrected example validates test/static/valid_signature.xml. That section is replaced with the actual export list.

Ordering

#559 deprecates every name withdrawn here, with a runtime warning, and is meant to land on 6.x first. This branch will need a rebase after that.

Verification

npm run build && npm test && npm run lint clean; 243 passing (241 + 2).

🤖 Generated with Claude Code

`index.ts` did `export * from "./utils"`, so every helper written for
`signed-xml.ts` to use became public API at the same time. That is how
`findChilds` stayed published with no callers and how each new helper joined
the surface without anyone choosing to publish it.

The export list is explicit now, and it names the four helpers a consumer has
a real use for: `derToPem`, `pemToDer` and `normalizePem` for key-format
conversion, and `findAncestorNs`, which is already part of the custom
canonicalization contract in practice — three projects on GitHub import it
alongside `C14nCanonicalization` to canonicalize a document subset by hand.
See the survey on #551.

`validateDigestValue` goes internal despite the "check before dropping" note.
Nobody imports it, and publishing the constant-time comparison is what invites
someone to reimplement it; it belongs behind the API that already uses it.

`BASE64_REGEX` and `PEM_FORMAT_REGEX` lose their `export` keyword outright,
since no sibling module uses them. The rest stay exported from `utils.ts` for
their siblings but are no longer re-exported to consumers — a sibling and a
consumer reaching a helper through the same `export` keyword is what caused
this, and `index.ts` is the only place that can now widen the surface.

`index.ts` only governs the barrel, though. With no `exports` map every file
under `lib/` was reachable by path, and consumers do reach: one published
package imports `xml-crypto/lib/hash-algorithms.js` for a class we never
exported. Declare an `exports` map naming the entry point and `package.json`
and nothing else, so a deep import fails loudly instead of quietly depending on
the build layout. The bundled algorithm classes stay reachable through the
`HashAlgorithms`, `SignatureAlgorithms` and `CanonicalizationAlgorithms`
registries that name them.

`test/public-api-tests.spec.ts` pins both the runtime export names and the
declared subpaths, so widening either is a deliberate edit rather than a side
effect of adding a helper or a file.

The README documented an `xpath` export that 6.x does not have — the
verification example destructured it and would have thrown. Point the examples
at the `xpath` package instead, whose `select()` takes the expression first,
and replace that section with the actual export list.

BREAKING CHANGE: `findAttr`, `findChildren`, `findChilds`, `isDescendantOf`,
`isArrayHasLength`, `encodeSpecialCharactersInAttribute`,
`encodeSpecialCharactersInText`, `validateDigestValue`, `BASE64_REGEX`,
`EXTRACT_X509_CERTS` and `PEM_FORMAT_REGEX` are no longer exported, and
subpaths into `lib/` no longer resolve. See the Upgrading section of the README
for replacements.

Closes #551

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 9, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 59 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: c5669635-ae0e-4dda-9558-e396a8f65b1c

📥 Commits

Reviewing files that changed from the base of the PR and between 0409418 and 34eae24.

📒 Files selected for processing (5)
  • README.md
  • package.json
  • src/index.ts
  • src/utils.ts
  • test/public-api-tests.spec.ts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cjbarth

cjbarth commented Sep 9, 2026

Copy link
Copy Markdown
Contributor Author

Closed in favour of #569 — the same commits, opened from cjbarth/xml-crypto instead of a branch pushed directly to this repo by mistake. The branch here has been deleted.

🤖 Generated with Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Audit the public export surface and deprecate internal helpers ahead of 7.0

1 participant