Skip to content

Commit cd6753e

Browse files
tinovyatkinclaude
andcommitted
feat: gap-closure items from the v7 plan (hk.pkl, WWDR warning, CONTRIBUTING)
Closes three misses from the original modernization plan that weren't addressed in the main v7 commit chain. 1. hk.pkl: the Git 2.53 native hook config the plan described. Binds pre-commit to oxfmt + oxlint (auto-fix) and pre-push to `npm test`. Validated with `hk validate`. CLAUDE.md already documented `hk install` as the one-time setup; previously that left contributors with no-op hooks because the config file itself wasn't committed. 2. WWDR cert expiry runtime warning in src/lib/sign-manifest.ts. If the bundled WWDR cert is within 90 days of expiry (or already expired), emit a process warning at module load time. Uses `process.emitWarning` with distinct codes `WALLETPASS_WWDR_EXPIRED` / `WALLETPASS_WWDR_EXPIRING`, which integrates with Node's native warning machinery: - prints to stderr in the standard `(pid:xxx) [CODE] Name: msg` format - suppressible via `node --disable-warning=WalletPassWWDRExpiring` - interceptable via `process.on('warning', ...)` This is the guard that should prevent another 2023-style silent expiry. The 2013-2023 G1 cert expired in production for months before anyone noticed — the whole v7 revival effort started because of that. 3. CONTRIBUTING.md: documents the Conventional Commits rules (required for release-please to work correctly), the three quality gates, the hk setup for local hooks, and explicit scope guidance (no new runtime deps, no filesystem-required features, Wallet Orders out of scope). CLAUDE.md is for AI assistants; CONTRIBUTING.md is for humans. Deliberately did NOT add a `postinstall` script to package.json — that would pollute every consumer's `npm install` with hint text for a maintainer-only tool. The hk install instructions are in CLAUDE.md and CONTRIBUTING.md where they belong. Remaining gaps from the plan, deferred to v7.1+: - zip.ts fuzz tests (branch coverage) - UpcomingPassInformationEntry + secondaryLogo schema - oxlint --type-check flag (tsgo build step already catches this) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent efa354e commit cd6753e

3 files changed

Lines changed: 156 additions & 0 deletions

File tree

CONTRIBUTING.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Contributing to `@walletpass/pass-js`
2+
3+
Thanks for wanting to help. This is a small library with a tight scope —
4+
generating valid Apple Wallet `.pkpass` files — so contributions that stay
5+
inside that scope and keep the dependency footprint minimal are most likely
6+
to land quickly.
7+
8+
## Setup
9+
10+
```sh
11+
# Prereqs: Node 24.12+ (see .nvmrc), npm 11+
12+
nvm use # or: mise use
13+
npm ci
14+
npm run build && npm test
15+
```
16+
17+
One-time Git hook wiring (optional but recommended — matches what CI runs):
18+
19+
```sh
20+
# Install hk (https://hk.jdx.dev) — Rust binary, no npm footprint
21+
brew install hk # or: cargo install hk, or: mise use hk
22+
hk install # one-time wires native Git 2.53 hooks
23+
```
24+
25+
Now `git commit` runs oxfmt + oxlint (auto-fix), and `git push` runs the
26+
test suite.
27+
28+
## Commits: Conventional Commits are required
29+
30+
Release automation (`release-please`) bumps the version and generates the
31+
changelog directly from commit messages. **Breaking the format breaks the
32+
release pipeline**, so please follow it:
33+
34+
| Prefix | Bump | Changelog section | Example |
35+
|---|---|---|---|
36+
| `feat:` | minor | Features | `feat: add appLaunchURL mapping` |
37+
| `fix:` | patch | Bug Fixes | `fix: reject invalid Date in semantics` |
38+
| `perf:` | patch | Performance | `perf: cache CRC table at module load` |
39+
| `feat!:` or `BREAKING CHANGE:` footer | **major** | Features + BREAKING note | `feat!: drop Node < 24` |
40+
| `deps:` | patch | Dependencies | `deps: bump pkijs 3.4 → 3.5` |
41+
| `docs:`, `refactor:`, `test:`, `ci:`, `chore:`, `build:`, `style:` || hidden | `docs: clarify WWDR rotation flow` |
42+
43+
Scopes are optional (`feat(nfc): ...`). Keep the subject line ≤ 72 chars.
44+
Body is free-form; put the "why" there, not the "what".
45+
46+
## Before opening a PR
47+
48+
All three quality gates must be green. They'll run in CI regardless; running
49+
them locally first saves a round-trip:
50+
51+
```sh
52+
npm run build # tsgo, must emit cleanly
53+
npm run lint # oxlint (type-aware) + oxfmt --check
54+
npm test # builds, then runs __tests__/ against dist/
55+
```
56+
57+
If you touch cryptographic code in `src/lib/sign-manifest.ts`, also manually
58+
verify the round-trip on a real pkpass with a test cert — CLAUDE.md has the
59+
recipe under "Manual QA."
60+
61+
## Scope
62+
63+
Welcome:
64+
- Bug fixes (especially around Apple spec compliance or edge-case passes).
65+
- Schema additions that track current Apple Wallet documentation.
66+
- Documentation clarifications.
67+
- Test coverage expansion.
68+
69+
Think twice or open an issue first:
70+
- New runtime dependencies — the v7 goal was to minimize these. We dropped
71+
6 deps in the rewrite; please don't re-add surface area without a clear
72+
case.
73+
- Order tracking (Wallet Orders / `order.json`) — a whole second package
74+
format with its own certificate flow; out of scope for this library.
75+
- Features that require writing to the filesystem — the library should stay
76+
bundler-friendly for Lambda / edge runtimes. Use Buffer/string APIs.
77+
78+
## Architecture notes
79+
80+
See [CLAUDE.md](./CLAUDE.md) for the non-obvious bits — why SHA-1 is
81+
deliberate in manifest hashing, how the WWDR cert is inlined for bundle
82+
safety, why tests import from `dist/` rather than `src/`, etc.

hk.pkl

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// hk (jdx/hk) — Git 2.53 native hook manager
2+
// See CLAUDE.md § "Git hooks (optional)" for install instructions.
3+
// Generated from `hk init`, customized for pass-js.
4+
5+
amends "package://github.com/jdx/hk/releases/download/v1.44.3/hk@1.44.3#/Config.pkl"
6+
7+
local linters = new Mapping<String, Step> {
8+
// oxfmt is per-file, fast, and safe to auto-fix.
9+
["oxfmt"] {
10+
glob = List("*.ts")
11+
check = "oxfmt --check {{files}}"
12+
fix = "oxfmt {{files}}"
13+
}
14+
// oxlint runs project-wide because type-aware mode needs the whole
15+
// program (tsgo loads tsconfig.json). Gated on *.ts changes so it
16+
// doesn't fire for README-only commits.
17+
["oxlint"] {
18+
glob = List("*.ts")
19+
check = "oxlint --type-aware"
20+
fix = "oxlint --type-aware --fix"
21+
}
22+
}
23+
24+
hooks {
25+
["pre-commit"] {
26+
fix = true
27+
stash = "git"
28+
steps = linters
29+
}
30+
["pre-push"] {
31+
steps = new Mapping<String, Step> {
32+
// Full test suite including the build + openssl signing round-trip.
33+
// Takes ~1s on a modern laptop; worth catching before a public push.
34+
["test"] {
35+
check = "npm test"
36+
}
37+
}
38+
}
39+
["fix"] {
40+
fix = true
41+
steps = linters
42+
}
43+
["check"] {
44+
steps = linters
45+
}
46+
}

src/lib/sign-manifest.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,34 @@ const OID_RSA_ENCRYPTION = '1.2.840.113549.1.1.1';
7676

7777
const APPLE_WWDR_CA = parsePkiCertificate(APPLE_WWDR_CA_PEM);
7878

79+
// Emit a process warning if the bundled WWDR cert is within 90 days of
80+
// expiry (or already expired). The 2013–2023 G1 silently expired and every
81+
// downstream user shipped broken passes for months before anyone noticed —
82+
// this is the guard that should catch the next rotation.
83+
//
84+
// Uses process.emitWarning (rather than console.warn) so consumers can
85+
// silence or intercept it the standard Node way:
86+
// node --disable-warning=WalletPassWWDRExpiring app.js
87+
// process.on('warning', w => { if (w.code === 'WALLETPASS_WWDR_EXPIRED') ... })
88+
const WWDR_WARN_WINDOW_MS = 90 * 24 * 60 * 60 * 1000;
89+
const wwdrNotAfter = APPLE_WWDR_CA.notAfter.value;
90+
const msUntilExpiry = wwdrNotAfter.getTime() - Date.now();
91+
if (msUntilExpiry < WWDR_WARN_WINDOW_MS) {
92+
const when = wwdrNotAfter.toISOString().slice(0, 10);
93+
const days = Math.ceil(msUntilExpiry / (24 * 60 * 60 * 1000));
94+
if (msUntilExpiry < 0) {
95+
process.emitWarning(
96+
`Bundled Apple WWDR certificate expired on ${when}. Signed passes will fail validation. Upgrade @walletpass/pass-js or override via APPLE_WWDR_CERT_PEM. See https://www.apple.com/certificateauthority/`,
97+
{ type: 'WalletPassWWDRExpired', code: 'WALLETPASS_WWDR_EXPIRED' },
98+
);
99+
} else {
100+
process.emitWarning(
101+
`Bundled Apple WWDR certificate expires on ${when} (${days} days). Upgrade @walletpass/pass-js before then to avoid silently shipping invalid passes.`,
102+
{ type: 'WalletPassWWDRExpiring', code: 'WALLETPASS_WWDR_EXPIRING' },
103+
);
104+
}
105+
}
106+
79107
// ─── Public API ─────────────────────────────────────────────────────────────
80108

81109
// Sign the manifest.json of an Apple Wallet pass bundle.

0 commit comments

Comments
 (0)