Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ updates:
# npm dependencies (published dual ESM/CJS library).
# * minor + patch → grouped into ONE weekly PR.
# * major bumps are deliberately NOT grouped — Dependabot raises one PR per
# major so each can be triaged on its own (breaking changes / migrations).
# major so each can be triaged on its own (breaking changes / migrations),
# except the lockstep and pre-1.0 tool families grouped below.
# A grouped batch must never be blocked by one incompatible major.
#
# Every dependency here is a devDependency (the package has zero runtime
Expand Down Expand Up @@ -79,6 +80,14 @@ updates:
patterns:
- "vitest"
- "@vitest/*"
# publint and @arethetypeswrong/cli are pre-1.0, so breaking changes ship
# as minors; either can start failing `npm run check:package` against
# unchanged package code. This group covers ALL update types, majors
# included, so such a bump cannot block the grouped batch behind it.
package-check:
patterns:
- "publint"
- "@arethetypeswrong/*"
npm-minor-patch:
patterns:
- "*"
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ jobs:
uses: ./.github/workflows/format.yml
secrets: inherit

package-check:
needs: format
uses: ./.github/workflows/package-check.yml

test-node22:
needs: format
uses: ./.github/workflows/ci-node22.yml
Expand Down
27 changes: 27 additions & 0 deletions .github/workflows/package-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Package check

on:
workflow_call:

permissions:
contents: read

jobs:
package-check:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v7

- name: Setup Node.js 22
uses: actions/setup-node@v7
with:
node-version: 22
cache: 'npm'

- name: Install dependencies
run: npm ci

- name: Check package exports and declarations
run: npm run check:package
4 changes: 2 additions & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ jobs:
NODE_ENV: test
run: npm test

- name: Build package
run: npm run build
- name: Check package exports and declarations
run: npm run check:package

publish:
needs: ci
Expand Down
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
- **Lint**: `npm run lint` (oxlint)
- **Format**: `npm run format` (prettier)
- **Type check**: `npm run type-check`
- **Package check**: `npm run check:package` (builds, then publint + attw against the packed tarball)

## Gotchas

- The git remote is named `main`, not `origin`. Use `git push main <branch>`.
- `validator.ts` has module-level mutable state (`defaultProtocol`). Tests that call `setDefaultProtocol()` must restore the original value.
- The React hook freezes config at mount via `useRef` — config prop changes after mount are ignored.
- All URL-facing operations convert keys to `snake_case` internally, regardless of the consumer's `keyFormat` setting.
- `check:package` runs attw with `--profile node16`, so the `node10` row is still printed but every cell is marked `(ignored)` and it cannot affect the exit code. It is red for `/react` and `/server` because legacy resolution cannot see `exports` subpaths; that is a declared non-target, not a bug to fix. Do not add `typesVersions`: it would green the row for types only.
- publint and attw each catch what the other misses. attw exits 0 on a tarball containing no declarations at all, so publint is what catches missing or unpublished files; publint passes a correctly-wired `exports` map whose declarations import chunks the build never emitted, so attw is what catches that. Dropping either silently loses a class of failure.

## Releases

Expand All @@ -26,6 +29,7 @@ Dependabot runs weekly (`.github/dependabot.yml`). Minor/patch npm bumps arrive
- **React majors are ignored on purpose.** Which React line the tests run against is a support-matrix decision tied to the `peerDependencies: react >=16.8.0` claim, so it gets changed deliberately rather than by a weekly bump.
- **TypeScript majors are ignored because they are blocked upstream.** tsup bundles `rollup-plugin-dts` pinned against TypeScript 5.x, and TS 7 removed the `useCaseSensitiveFileNames` API it calls, so `npm run build` fails before emitting declarations. Remove the ignore once tsup ships a TS 7-compatible `rollup-plugin-dts`.
- **vitest and `@vitest/coverage-v8` are grouped, majors included.** `coverage-v8` peer-requires the exact matching vitest version, so raised separately neither can pass `npm ci`.
- **publint and `@arethetypeswrong/cli` are grouped on their own, majors included.** Both are pre-1.0, where a breaking change ships as a minor. Either can start failing `npm run check:package` against unchanged package code, and in the grouped `npm-minor-patch` PR that would block every unrelated bump behind it.
- **The test toolchain sets the Node floor, not the library.** jsdom requires Node 22+ and vitest requires 20+, so CI tests 22/24/26. `engines.node` is `>=20.0.0`: the published bundle has no Node-version-specific code, but nothing below 22 is exercised. Raise the floor rather than pinning the toolchain back if this ever conflicts.

## Test Setup
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ Three entry points:
| `@jackmisner/utm-toolkit/react` | Hook, provider and components (needs React) |
| `@jackmisner/utm-toolkit/server` | DOM-free normalization for ingest endpoints |

All three ship dual ESM/CJS builds with TypeScript declarations.
All three ship dual ESM/CJS builds with TypeScript declarations. The `/react` and `/server` subpaths need TypeScript `moduleResolution` set to `node16` or `nodenext` (TS 4.7+), or `bundler` (TS 5.0+); under the legacy `node`/`node10` setting only the root entry resolves types, though all three resolve at runtime in Node.

## Quick Start

Expand Down Expand Up @@ -941,6 +941,7 @@ import type {
- `engines.node` is `>=20.0.0`; CI exercises 22, 24 and 26
- The root entry is DOM-free at runtime — every `window` access sits behind a guard — so it imports and runs in Node without a DOM
- `@jackmisner/utm-toolkit/server` is the supported surface for server use: it cannot reach storage or the DOM, and that restriction is enforced by a test rather than only documented
- Types for the `/react` and `/server` subpaths need `moduleResolution` `node16`, `nodenext` or `bundler`; see [Installation](#installation)

## Migration from Existing Projects

Expand Down
Loading
Loading