|
| 1 | +# @maxmind/device-tracking |
| 2 | + |
| 3 | +A thin TypeScript loader that validates inputs, |
| 4 | +dynamically imports a remote fingerprinting module from MaxMind's servers, and |
| 5 | +returns a device tracking token. Runs in the browser. |
| 6 | + |
| 7 | +## Commands |
| 8 | + |
| 9 | +```sh |
| 10 | +pnpm test # Jest (ESM via --experimental-vm-modules) |
| 11 | +pnpm test:watch # Jest in watch mode |
| 12 | +pnpm run lint # ESLint + tsc --noEmit |
| 13 | +pnpm run build # Clean build to dist/ |
| 14 | +pnpm run prettier:ts # Format src/**/*.ts |
| 15 | +``` |
| 16 | + |
| 17 | +Run a single test file: |
| 18 | + |
| 19 | +```sh |
| 20 | +pnpm test -- src/loader.spec.ts |
| 21 | +``` |
| 22 | + |
| 23 | +Jest prints an `ExperimentalWarning` about VM Modules on every run. This is |
| 24 | +expected and harmless. |
| 25 | + |
| 26 | +## Architecture |
| 27 | + |
| 28 | +``` |
| 29 | +src/ |
| 30 | + index.ts # Public API: trackDevice() with input validation |
| 31 | + loader.ts # Singleton module loader with caching and timeout |
| 32 | + dynamic-import.ts # Thin wrapper around import() for test mocking |
| 33 | + types.ts # TrackDeviceOptions, TrackResult interfaces |
| 34 | + *.spec.ts # Co-located test files |
| 35 | +``` |
| 36 | + |
| 37 | +- `index.ts` is the only public entry point (`package.json` exports map). |
| 38 | +- `loader.ts` caches module promises per-host; clears cache on failure for retry. |
| 39 | +- `dynamic-import.ts` exists solely to make `import()` mockable in Jest. |
| 40 | +- Tests use `jest.unstable_mockModule` (ESM-compatible mocking). |
| 41 | + |
| 42 | +## Conventions |
| 43 | + |
| 44 | +- **ESM only** — `"type": "module"` in package.json, `.js` extensions in imports. |
| 45 | +- **Strict TypeScript** — `strict: true`, target ES2022, module Node16. |
| 46 | +- **Prettier** — single quotes, trailing commas (es5). |
| 47 | +- **Formatting separate from logic** — keep style-only changes in their own commits. |
| 48 | +- **`fixup!` commits** — prefix fixup commits with `fixup! <original subject>` for autosquash. |
| 49 | +- **Error messages include context** — URLs, received values, types. |
| 50 | +- **`@internal` JSDoc tag** — marks exports that exist only for testing (e.g. `resetModuleCache`). |
| 51 | + |
| 52 | +## Testing notes |
| 53 | + |
| 54 | +- Test environment is jsdom (browser globals). |
| 55 | +- Tests co-locate next to source files (`*.spec.ts`). |
| 56 | +- The `moduleNameMapper` in jest.config.js strips `.js` extensions for ts-jest. |
| 57 | +- Loader tests use real dynamic import (which fails in Node) to exercise error paths. |
| 58 | +- Mocked-module tests use `jest.unstable_mockModule` + dynamic `import()` to get fresh modules. |
| 59 | +- Fake timers are used for timeout tests — always call `jest.useRealTimers()` in `afterEach`. |
| 60 | + |
| 61 | +## Tooling |
| 62 | + |
| 63 | +- **mise** manages Node, pnpm, and precious versions (see `mise.toml`). |
| 64 | +- **precious** is the code-quality runner (config: `.precious.toml`). |
| 65 | +- **Git hooks** live in `.githooks/`. Enable with: `git config core.hooksPath .githooks` |
| 66 | +- GitHub Actions: test, lint, CodeQL, zizmor, release workflows. |
0 commit comments