fix(build): shrink published package from ~202 MB to ~3.5 MB (PER-15197)#127
Conversation
v2.7.5's hybrid tsup build shipped the entire per-file build/ tree —
every internal module bundled with the OpenAPI client inlined, in both
CJS and ESM, plus a .map for every file (708 maps, 77 MB) and the test
specs. Result: ~202 MB published, breaking AWS Lambda's code-size limit.
The published artifact only needs the self-contained build/index.js /
build/index.mjs bundles (0 relative imports; axios/lodash/pino/url-parse
externalized) plus type declarations. The per-file outputs exist solely
so the test suite can import internal modules by relative path, so they
must keep being built — but they don't need to be published.
- package.json files: publish only build/index.{js,mjs} + build/**/*.d.ts
(exclude per-file JS, all maps, and tests) instead of the whole build/
- tsup.config.ts: sourcemap false (maps are unused by tests, never shipped)
Verified via npm pack: 202 MB -> 3.5 MB unpacked (195 KB packed), 0 maps,
0 test files. CJS + ESM consume tests pass; module-import suite passes 6/6.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR addresses a major npm package size regression by changing what gets published to npm and by disabling source map emission in the tsup build, restoring the published permitio artifact to a small footprint suitable for environments like AWS Lambda.
Changes:
- Disable
tsupsource map emission to avoid generating hundreds of.mapfiles. - Restrict the npm published contents to the public entry bundles (
build/index.js,build/index.mjs) and TypeScript declarations, excluding built tests.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| tsup.config.ts | Turns off source map generation in the build output. |
| package.json | Narrows the npm files list to publish only entry bundles + .d.ts, excluding build/tests/**. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
zeevmoney
left a comment
There was a problem hiding this comment.
Reviewed at b3c7682 (rebased on current main). The two changed lines are correct — verified by reproducing the tsup + tsc --emitDeclarationOnly build and running npm pack --dry-run against the real files array:
- Every package.json artifact pointer resolves in the tarball:
main→build/index.js,module→build/index.mjs,typings/types/exports.types→build/index.d.ts(matched bybuild/**/*.d.ts). The.d.tsgraph re-exports siblings by relative path and all of them ship. !build/tests/**is necessary and correctly ordered — without it,build/**/*.d.tswould leak test/fixture declarations. It drops nothing the public type graph needs.build/index.js/.mjsare self-contained: all 8 runtime deps are independencies(correctly externalized, not inlined), and there are no dynamicrequire/importorfs/__dirnameasset reads in runtime source.sourcemap: falseis safe — nothing (tests, typedoc, runtime) consumes maps, anddeclarationMapis unset, so no.d.ts.mapleak.
Approving. The notes below are non-blocking — none is on a line this PR changes.
Non-blocking notes (informational)
1. No CI guard against the size regressing again (follow-up).
Nothing in the publish workflow checks tarball size, so a repeat of the 202 MB regression would ship silently. Subtler: the module-import tests run against the fully-populated build/ and deep-import paths (require('../../api/api-client')) that are not in the published tarball, so the suite passes without validating the package as shipped. A future change that made index.js reference a sibling would keep CI green while breaking consumers. Suggest a post-yarn build step in .github/workflows/node_sdk_publish.yaml: npm pack + a size-threshold assert, and/or install the tarball in a temp dir and require('permitio') to smoke-test the real entry point.
2. package.json files lists CHANGELOG.md, which doesn't exist at repo root. Pre-existing and harmless (npm silently ignores missing files entries); untouched by this diff.
3. src/logger.ts:14 uses pino-8's deprecated prettyPrint option (moved to transport in pino 8). Pre-existing, resolves fine since pino-pretty is a dependency. Unrelated to this PR.
Summary
The
permitionpm package size exploded in v2.7.5 — from 7.82 MB (v2.7.2) to ~202 MB (v2.7.5) — breaking a customer's AWS Lambda deploy (exceeds the code-size limit). This PR brings the published package down to ~3.5 MB unpacked / 195 KB packed with zero functional changes.Fixes PER-15197. Reported by a customer (Matthew Karges) in #support; their workaround was pinning to v2.7.2.
Closes #90 (published package contains many unnecessary files).
Root cause
v2.7.5's "hybrid build" (PR #118, commit
2cdc9ef) switched fromtsctotsupbut the config caused the entire per-filebuild/tree to be published:With
splitting: false+ a per-file entry glob, the large generatedsrc/openapiclient got duplicated in full into every output bundle, then multiplied by two formats and a.mapper file. The published tarball also still contained the test specs.Measured breakdown of the bloat: 77 MB of source maps (708
.mapfiles),build/api62 MB,build/openapi22 MB, test specs ~109 MB.The fix
The published artifact only needs the self-contained
build/index.js/build/index.mjsbundles (verified: 0 relative imports; onlyaxios/lodash/pino/url-parseexternalized, all declareddependencies) plus the type declarations. The per-file outputs exist solely so the test suite can import internal modules by relative path (require('../../api/api-client'),../../enforcement/enforcer,../../index), so they must keep being built — they just don't need to be published.package.json—files: publish only the bundle + types instead of the wholebuild/:tsup.config.ts:sourcemap: false— maps are unused by the test suite and were ~77 MB of dead weight.No source code changed; the hybrid ESM/CJS output is preserved.
Results (
npm pack).mapfilesSmaller than the last known-good v2.7.2 (7.82 MB).
Verification
require('./build/index.js')→Permitinstantiates;.check/.api/.elementspresentimport { Permit } from './build/index.mjs'→ samenpm pack: 3.5 MB, onlyindex.js/index.mjs+.d.ts; no maps, no testsbuild/api/*.js,build/enforcement/*.js, …);yarn testunaffectedFollow-ups (post-merge)
References
create-hybrid-build), commit2cdc9ef🤖 Generated with Claude Code