Skip to content

Convert @tryghost/security to TypeScript - #1081

Merged
ErisDS merged 2 commits into
mainfrom
codex/security-typescript
Oct 21, 2026
Merged

ErisDS merged 2 commits into
mainfrom
codex/security-typescript

Conversation

@ErisDS

@ErisDS ErisDS commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

@tryghost/security has 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 the passwordHash reset-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 lint
  • corepack pnpm format:check

Packed 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=2

The 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 to passwordHash, and remove the four security import suppressions. Those Ghost changes were verified in the disposable checkout and are not included in this Framework PR.

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.
@coderabbitai

coderabbitai Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: 8bf9415f-0046-44f2-83d0-b2387b722be7
📥 Commits

Reviewing files that changed from the base of the PR and between 44801ed and c2167ee.

⛔ Files ignored due to path filters: 1
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing: 4
  • packages/security/README.md
  • packages/security/lib/tokens.ts
  • packages/security/test/tokens.test.js
  • packages/security/test/types.ts
🚧 Files skipped from review as they are similar to previous changes: 1
  • packages/security/README.md

Included review availability: This review used your included allowance. 0 included reviews remain after this review. Your included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour.

Walkthrough

The security package’s helper modules and entry point now use typed TypeScript. Package scripts and publication settings include compiled CommonJS files and declarations. New configuration and tests cover type checking, runtime behavior, and CommonJS compatibility.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature · Severity of issue fixed: Low

Suggested reviewers: kirrg001, naz

Merge Risk: ⚪ Minimal · up to c2167

No issue identified here blocks merging. Coordinate the stated Ghost dependency update before publication.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 13 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main change: converting @tryghost/security to TypeScript.
Description check Passed The description directly explains the TypeScript conversion, preserved compatibility, validation, release requirements, and related dependency changes.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 10 functions across 13 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

✨ Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@codecov-commenter

codecov-commenter commented Oct 20, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.09%. Comparing base (f044e2a) to head (c2167ee).

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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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 ErisDS left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.js against 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 removed options = options || {} was already dead — main throws on undefined.toLocaleLowerCase() too)
  • ✅ Packed tarball: no .ts sources, no types/ shim, no sourcemaps; index.js lazy getters preserved; deep require('@tryghost/security/lib/tokens') works (no exports field added — correctly avoids that breaking change)
  • ✅ Declarations are self-contained: lib/string.d.ts does not leak the @tryghost/string ambient shim (explicit return types mean no import is emitted); only node:crypto is referenced
  • ✅ Strict consumer typecheck passes (strict, skipLibCheck: false, TS 7.0.2 — note TS 7 has removed esModuleInterop: false entirely, so that compat matters only for TS ≤6 consumers, which the Ghost checkout validation covered)
  • ✅ !parts || guard in compare fixes a latent hole (old extract could return false; 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:

  1. __esModule: true is now emitted on lib/*.js. An interop consumer default-importing a deep path (import t from '@tryghost/security/lib/tokens') previously got the module; now gets undefined. 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.
  2. types/tryghost-string.d.ts will silently shadow real declarations if @tryghost/string ever ships its own. Suggest a TODO: delete when @tryghost/string publishes types in the file.
  3. vitest.config.ts coverage targeting compiled lib/**/*.js is 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.
  4. safe(string?: string | null) widens the old JSDoc {string}. Fine if it matches real Ghost call sites; tightening to required string would push null-handling to callers — your call.
  5. prepare + pretest means tsc runs 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

@ErisDS
ErisDS merged commit 8f34ce8 into main Oct 21, 2026
7 checks passed
@ErisDS
ErisDS deleted the codex/security-typescript branch October 21, 2026 15:18
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