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
70 changes: 34 additions & 36 deletions .claude/rules/typescript.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
---

paths:
- "**/*.ts"
- "**/*.ts"
- "**/*.mts"
- "**/*.tsx"
- "**/*.ts"
- "**/*.ts"
- "**/*.mts"
- "**/*.tsx"

---

# TypeScript Guidelines
Expand Down Expand Up @@ -57,53 +59,49 @@ Follow these TypeScript best practices when writing code to ensure type safety a

```typescript
interface Task {
id: string;
content: string;
isCompleted: boolean;
dueDate?: Date;
labels: string[];
id: string
content: string
isCompleted: boolean
dueDate?: Date
labels: string[]
}

type TaskAction =
| { type: 'ADD_TASK'; payload: Omit<Task, 'id'> }
| { type: 'COMPLETE_TASK'; payload: string }
| { type: 'DELETE_TASK'; payload: string }
| { type: 'UPDATE_TASK'; payload: { id: string; content: string } };
| { type: 'ADD_TASK'; payload: Omit<Task, 'id'> }
| { type: 'COMPLETE_TASK'; payload: string }
| { type: 'DELETE_TASK'; payload: string }
| { type: 'UPDATE_TASK'; payload: { id: string; content: string } }
```

### Good: Generic function

```typescript
function filterItems<T, K extends keyof T>(
items: T[],
property: K,
value: T[K]
): T[] {
return items.filter(item => item[property] === value);
function filterItems<T, K extends keyof T>(items: T[], property: K, value: T[K]): T[] {
return items.filter((item) => item[property] === value)
}
```

### Good: Type guard

```typescript
function isTask(value: unknown): value is Task {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'content' in value &&
'isCompleted' in value
);
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'content' in value &&
'isCompleted' in value
)
}
```

### Good: Readonly properties

```typescript
interface UserSettings {
readonly id: string;
theme: 'light' | 'dark';
notifications: boolean;
readonly id: string
theme: 'light' | 'dark'
notifications: boolean
}
```

Expand All @@ -112,7 +110,7 @@ interface UserSettings {
```typescript
// Don't do this
function processData(data: any) {
return data.map(item => item.value);
return data.map((item) => item.value)
}
```

Expand All @@ -121,7 +119,7 @@ function processData(data: any) {
```typescript
// Don't do this
function calculateTotal(items) {
return items.reduce((total, item) => total + item.price, 0);
return items.reduce((total, item) => total + item.price, 0)
}
```

Expand All @@ -130,12 +128,12 @@ function calculateTotal(items) {
```typescript
// Don't do this - use interface for extensible objects
type User = {
id: string;
name: string;
id: string
name: string
}

type AdminUser = User & {
permissions: string[];
permissions: string[]
}
```

Expand All @@ -144,7 +142,7 @@ type AdminUser = User & {
```typescript
// Don't do this - use discriminated unions
type Action = {
type: string;
payload: any;
type: string
payload: any
}
```
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
cache: npm
node-version-file: .node-version
registry-url: https://npm.pkg.github.com/
scope: '@doist'
scope: "@doist"

- name: Publish package to GitHub Packages registry
run: npm publish
Expand Down
25 changes: 10 additions & 15 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,41 @@

## [1.1.3](https://github.com/Doist/interaction-trace/compare/interaction-trace-v1.1.2...interaction-trace-v1.1.3) (2026-09-30)


### Bug Fixes

* Update Help Center links to canonical paths ([#200](https://github.com/Doist/interaction-trace/issues/200)) ([eea4007](https://github.com/Doist/interaction-trace/commit/eea40073c1be08c431bb7268dde6616f36bd10a7))
- Update Help Center links to canonical paths ([#200](https://github.com/Doist/interaction-trace/issues/200)) ([eea4007](https://github.com/Doist/interaction-trace/commit/eea40073c1be08c431bb7268dde6616f36bd10a7))

## [1.1.2](https://github.com/Doist/interaction-trace/compare/interaction-trace-v1.1.1...interaction-trace-v1.1.2) (2026-04-04)


### Bug Fixes

* **security:** add --no flag to npm exec in pre-commit hook ([#81](https://github.com/Doist/interaction-trace/issues/81)) ([20ea4fb](https://github.com/Doist/interaction-trace/commit/20ea4fb81c3de975d4f55519e05875c7e570a8c7))
- **security:** add --no flag to npm exec in pre-commit hook ([#81](https://github.com/Doist/interaction-trace/issues/81)) ([20ea4fb](https://github.com/Doist/interaction-trace/commit/20ea4fb81c3de975d4f55519e05875c7e570a8c7))

## [1.1.1](https://github.com/Doist/interaction-trace/compare/interaction-trace-v1.1.0...interaction-trace-v1.1.1) (2026-04-04)


### Bug Fixes

* **security:** replace npx with npm exec in pre-commit hook ([#79](https://github.com/Doist/interaction-trace/issues/79)) ([09d513e](https://github.com/Doist/interaction-trace/commit/09d513ea840958af9c68fa1687715da73b623af9))
- **security:** replace npx with npm exec in pre-commit hook ([#79](https://github.com/Doist/interaction-trace/issues/79)) ([09d513e](https://github.com/Doist/interaction-trace/commit/09d513ea840958af9c68fa1687715da73b623af9))

## [1.1.0](https://github.com/Doist/interaction-trace/compare/interaction-trace-v1.0.0...interaction-trace-v1.1.0) (2026-02-06)


### Features

* allow isEnabled() to return undefined to defer to sampleRate ([#26](https://github.com/Doist/interaction-trace/issues/26)) ([7691707](https://github.com/Doist/interaction-trace/commit/7691707d2defb39a050c7228d180c5d26988d967))
- allow isEnabled() to return undefined to defer to sampleRate ([#26](https://github.com/Doist/interaction-trace/issues/26)) ([7691707](https://github.com/Doist/interaction-trace/commit/7691707d2defb39a050c7228d180c5d26988d967))

## 1.0.0 (2026-02-06)


### Features

* add core interaction trace monitoring implementation ([#8](https://github.com/Doist/interaction-trace/issues/8)) ([9435478](https://github.com/Doist/interaction-trace/commit/943547839054c1f26abfa3ada7219f9904f2ee37))
* add core types for interaction trace library ([#7](https://github.com/Doist/interaction-trace/issues/7)) ([dc8016b](https://github.com/Doist/interaction-trace/commit/dc8016bcd722e9d197ab9c9de2e345d3bd05f1fc))
* add release-please and npm publish workflows ([#21](https://github.com/Doist/interaction-trace/issues/21)) ([d118523](https://github.com/Doist/interaction-trace/commit/d118523f56f1812004375d781cdb481324072d7f))
* **react:** implement useInteractionTrace hook with pre-typed API ([#14](https://github.com/Doist/interaction-trace/issues/14)) ([dca346d](https://github.com/Doist/interaction-trace/commit/dca346d64a458acda8e8014891edc4817318eda0))
* **types:** support pre-typed APIs via signInteractionTrace.withTypes() ([#11](https://github.com/Doist/interaction-trace/issues/11)) ([c191fe0](https://github.com/Doist/interaction-trace/commit/c191fe06cf463e26d5c30161bd20facebd2374b5))
- add core interaction trace monitoring implementation ([#8](https://github.com/Doist/interaction-trace/issues/8)) ([9435478](https://github.com/Doist/interaction-trace/commit/943547839054c1f26abfa3ada7219f9904f2ee37))
- add core types for interaction trace library ([#7](https://github.com/Doist/interaction-trace/issues/7)) ([dc8016b](https://github.com/Doist/interaction-trace/commit/dc8016bcd722e9d197ab9c9de2e345d3bd05f1fc))
- add release-please and npm publish workflows ([#21](https://github.com/Doist/interaction-trace/issues/21)) ([d118523](https://github.com/Doist/interaction-trace/commit/d118523f56f1812004375d781cdb481324072d7f))
- **react:** implement useInteractionTrace hook with pre-typed API ([#14](https://github.com/Doist/interaction-trace/issues/14)) ([dca346d](https://github.com/Doist/interaction-trace/commit/dca346d64a458acda8e8014891edc4817318eda0))
- **types:** support pre-typed APIs via signInteractionTrace.withTypes() ([#11](https://github.com/Doist/interaction-trace/issues/11)) ([c191fe0](https://github.com/Doist/interaction-trace/commit/c191fe06cf463e26d5c30161bd20facebd2374b5))


### Bug Fixes

* add legacy resolution fields for moduleResolution "node" and Jest ([#24](https://github.com/Doist/interaction-trace/issues/24)) ([6bb3d69](https://github.com/Doist/interaction-trace/commit/6bb3d694e13a8a1d5873bdaf5997e0de62216187))
- add legacy resolution fields for moduleResolution "node" and Jest ([#24](https://github.com/Doist/interaction-trace/issues/24)) ([6bb3d69](https://github.com/Doist/interaction-trace/commit/6bb3d694e13a8a1d5873bdaf5997e0de62216187))

## Changelog
2 changes: 0 additions & 2 deletions CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,13 @@ We pledge to act and interact in ways that contribute to an open, welcoming, div
## Our Standards

Examples of behavior that contributes to a positive environment for our community include:

- Demonstrating empathy and kindness toward other people
- Being respectful of differing opinions, viewpoints, and experiences
- Giving and gracefully accepting constructive feedback
- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
- Focusing on what is best not just for us as individuals, but for the overall community

Examples of unacceptable behavior include:

- The use of sexualized language or imagery, and sexual attention or advances of any kind
- Trolling, insulting or derogatory comments, and personal or political attacks
- Public or private harassment
Expand Down
5 changes: 0 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,13 @@ If you intend to change the public API, or make any non-trivial changes to the i
## Your First Pull Request

Working on your first Pull Request? You can learn how from this free video series:

- [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github)

If you decide to fix an issue, please be sure to check the comment thread in case somebody is already working on a fix. If nobody is working on it at the moment, please leave a comment stating that you intend to work on it so other people don't accidentally duplicate your effort.

## Project Setup

Before you can contribute to the codebase, you will need to fork the interaction-trace repository, and the following steps will help you hit the ground running:

1. Fork the repository (click the <kbd>Fork</kbd> button at the top right of [this page](https://github.com/Doist/interaction-trace));

2. Clone your fork locally;
Expand All @@ -50,7 +48,6 @@ cd interaction-trace
## Development Workflow

After cloning interaction-trace and installing all dependencies, several commands are at your disposal:

- `npm run build`: Builds the `@doist/interaction-trace` package for publishing to [npm](https://www.npmjs.com/);
- `npm run check`: Validates code quality and types with Biome and TypeScript;
- `npm run fix`: Automatically fixes linting issues with Biome;
Expand All @@ -66,7 +63,6 @@ The release process for interaction-trace is automated with [release-please](htt
Pull requests are actively monitored, and only need the approval of one or more core team members. We will review your pull request and either merge it, request changes to it, or close it with an explanation.

Before submitting a pull request, please take the following into consideration:

- Fork [the repository](https://github.com/Doist/interaction-trace) and create your branch from `main`;
- Follow the [Commit Message Guidelines](#commit-message-guidelines) below;
- Add tests for code that should be tested (like bug fixes);
Expand All @@ -78,7 +74,6 @@ Before submitting a pull request, please take the following into consideration:
#### Atomic Commits

If possible, make [atomic commits](https://en.wikipedia.org/wiki/Atomic_commit), which means:

- A commit should not mix whitespace and/or code style changes with functional code changes;
- A commit should contain exactly one self-contained functional change;
- A commit should not create an inconsistent state (e.g., test errors, linting errors, partial fix, etc.).
Expand Down
34 changes: 17 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ const cleanup = initInteractionTraceMonitor({
enrollment: {
sampleRate: 10, // 10% of sessions
isEnabled: () => {
if (user.isInternal) return true // force enable for internal users
return undefined // everyone else: use sampleRate
if (user.isInternal) return true // force enable for internal users
return undefined // everyone else: use sampleRate
},
},
abortSignal: controller.signal, // Optional: auto-cleanup when aborted
Expand Down Expand Up @@ -64,9 +64,9 @@ type MyTraces = {

const signAppTrace = signInteractionTrace.withTypes<MyTraces>()

signAppTrace('open modal', { modalId: 'settings' }) // ✅ Works
signAppTrace('opne modal', { modalId: 'settings' }) // ❌ Error: typo caught
signAppTrace('open modal', { formId: 'x' }) // ❌ Error: wrong details
signAppTrace('open modal', { modalId: 'settings' }) // ✅ Works
signAppTrace('opne modal', { modalId: 'settings' }) // ❌ Error: typo caught
signAppTrace('open modal', { formId: 'x' }) // ❌ Error: wrong details
```

### React Integration
Expand Down Expand Up @@ -102,28 +102,28 @@ function SettingsModal() {

### Enrollment Options

| Option | Type | Description |
|--------|------|-------------|
| `sampleRate` | `number` | Percentage of sessions to enroll (0-100) |
| `persistKey` | `string` | sessionStorage key for enrollment state. Default: `'interaction-trace-enrolled'` |
| `isEnabled` | `() => boolean \| undefined` | Override function. `true`/`false` override sampling, `undefined` defers to `sampleRate` |
| Option | Type | Description |
| ------------ | ---------------------------- | --------------------------------------------------------------------------------------- |
| `sampleRate` | `number` | Percentage of sessions to enroll (0-100) |
| `persistKey` | `string` | sessionStorage key for enrollment state. Default: `'interaction-trace-enrolled'` |
| `isEnabled` | `() => boolean \| undefined` | Override function. `true`/`false` override sampling, `undefined` defers to `sampleRate` |

### Reporter Interface

The reporter receives a `TraceReport` object:

```typescript
type TraceReport = {
id: string // Unique trace ID (crypto.randomUUID())
duration: number // Total LoAF duration (ms)
inp: number | undefined // INP value if captured (ms)
id: string // Unique trace ID (crypto.randomUUID())
duration: number // Total LoAF duration (ms)
inp: number | undefined // INP value if captured (ms)
details: {
name: string // Trace name from signInteractionTrace()
[key: string]: unknown // Additional details
name: string // Trace name from signInteractionTrace()
[key: string]: unknown // Additional details
}
device: {
memoryGB: string // Bucketed: "0.25-2", "3-7", "8-plus", or "unknown"
cpuCores: string // Bucketed: "1-8", "9-16", "17-plus"
memoryGB: string // Bucketed: "0.25-2", "3-7", "8-plus", or "unknown"
cpuCores: string // Bucketed: "1-8", "9-16", "17-plus"
}
}
```
Expand Down
Loading
Loading