Repository navigation
Convert @tryghost/security to TypeScript - #1081
Merged
Merged
Conversation
Publish declarations alongside the existing CommonJS entry points so typed consumers can use security helpers without import suppressions. Keep root loading lazy and preserve lib subpaths and token behavior.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1081 +/- ##
==========================================
- Coverage 98.13% 98.09% -0.04%
==========================================
Files 87 81 -6
Lines 3210 3104 -106
Branches 574 560 -14
==========================================
- Hits 3150 3045 -105
Misses 16 16
+ Partials 44 43 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Merge the reset-token parameter rename from main and carry it through the TypeScript implementation and declaration checks. Preserve the fixed-token regression and CommonJS package contract.
ErisDS
commented
Oct 21, 2026
ErisDS
left a comment
Member
Author
There was a problem hiding this comment.
Review: TypeScript migration gap analysis
Reviewed with independent verification (not just reading the diff):
Verified locally:
- ✅ Full pipeline: build →
test:types→ 42 tests → 100% stmt coverage → lint, all green - ✅ Byte-compatibility: ran main's
tokens.js/secret.js/url.jsagainst the compiled TS output with identical inputs — token bytes identical, reset tokens cross-validate in both directions (old↔new), expired/tampered/garbage paths return identical results, and no-arg calls throw identically (the removedoptions = options || {}was already dead — main throws onundefined.toLocaleLowerCase()too) - ✅ Packed tarball: no
.tssources, notypes/shim, no sourcemaps;index.jslazy getters preserved; deeprequire('@tryghost/security/lib/tokens')works (noexportsfield added — correctly avoids that breaking change) - ✅ Declarations are self-contained:
lib/string.d.tsdoes not leak the@tryghost/stringambient shim (explicit return types mean no import is emitted); onlynode:cryptois referenced - ✅ Strict consumer typecheck passes (strict,
skipLibCheck: false, TS 7.0.2 — note TS 7 has removedesModuleInterop: falseentirely, so that compat matters only for TS ≤6 consumers, which the Ghost checkout validation covered) - ✅
!parts ||guard incomparefixes a latent hole (oldextractcould returnfalse; old code relied on boolean boxing →undefined→isNaN→invalid_expiry) — same observable behaviour, now explicit and tested
No breaking gaps found. Cosmetic notes, none blocking:
__esModule: trueis now emitted onlib/*.js. An interop consumer default-importing a deep path (import t from '@tryghost/security/lib/tokens') previously got the module; now getsundefined. TS consumers get a compile error (good); untyped JS consumers doing this wouldn't. Ghost's call sites were verified, so this is awareness only — maybe worth a line in the release notes for the major bump.types/tryghost-string.d.tswill silently shadow real declarations if@tryghost/stringever ships its own. Suggest aTODO: delete when @tryghost/string publishes typesin the file.vitest.config.tscoverage targeting compiledlib/**/*.jsis the right call (tests the shipped artifact) but looks like a mistake at a glance — a one-line comment would stop a future "fix" back to.ts.safe(string?: string | null)widens the old JSDoc{string}. Fine if it matches real Ghost call sites; tightening to requiredstringwould push null-handling to callers — your call.prepare+pretestmeanstscruns twice in an install+test flow. Harmless, just noise.
Merging on the basis of green CI (Node 22/24, CodeQL, Required checks pass) and the verification above.
Generated by Claude Code
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
@tryghost/securityhas no declarations, so TypeScript consumers need import suppressions. Convert the package to TypeScript and publish declarations alongside the existing CommonJS JavaScript. Preserve lazy root exports,lib/*entry points, token behavior, and thepasswordHashreset-token API merged in #1083.Validation
corepack pnpm --filter @tryghost/security test— 42 tests pass, including build, typechecks, CommonJS compatibility and the pre-rename token regression.corepack pnpm lintcorepack pnpm format:checkPacked the compiled package into Ghost's invite conversion checkout, removed all four security import suppressions, and updated the reset-token callers to
passwordHash. Full source and test typechecks pass, along with 132 focused consumer tests:pnpm --filter ghost test:types pnpm --filter ghost exec vitest run \ test/unit/server/auth/passwordreset-security-compat.test.js \ test/unit/server/services/invites/ \ test/unit/lib/generate-password.test.ts \ test/unit/server/models/invite.test.js \ test/unit/server/models/user.test.js \ test/unit/frontend/services/routing/controllers/collection.test.js \ test/unit/frontend/services/routing/controllers/channel.test.js \ test/unit/frontend/services/routing/controllers/rss.test.js \ --maxWorkers=2The password-reset compatibility file contains three temporary checks in the validation checkout. They exercise Ghost's real generation/reset orchestration with database doubles, including acceptance of an old token and invalidation after a stored-hash change.
Also verified that Ghost resolves the packed JavaScript and declarations without TypeScript source, CommonJS consumers typecheck with
esModuleInterop: false, and invitation/reset token bytes match the published package.Release
Ship with the next major version of
@tryghost/security. Ghost's adoption must bump the dependency, update the two production reset-token callers and five test fixtures topasswordHash, and remove the four security import suppressions. Those Ghost changes were verified in the disposable checkout and are not included in this Framework PR.