-
Notifications
You must be signed in to change notification settings - Fork 0
docs: add noridocs for all source directories #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a13b0a7
c2bffcd
3151031
5ccc0eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,7 +10,6 @@ coverage/ | |
|
|
||
| # Config files | ||
| tsconfig.json | ||
| tsconfig.cjs.json | ||
| .gitignore | ||
| .prettierrc | ||
| .eslintrc* | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # CLAUDE.md | ||
|
|
||
| ## Quick Reference | ||
|
|
||
| - **Build**: `npm run build` (tsup, dual ESM/CJS) | ||
| - **Test**: `npm test` (vitest + jsdom) | ||
| - **Lint**: `npm run lint` (oxlint) | ||
| - **Format**: `npm run format` (prettier) | ||
| - **Type check**: `npm run type-check` | ||
|
|
||
| ## 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. | ||
|
|
||
| ## Releases | ||
|
|
||
| Releases are done via `npm run release:patch|minor|major` which creates a `release/<version>` branch from main. Pushing the tag triggers the publish workflow. | ||
|
|
||
| ## Test Setup | ||
|
|
||
| `__tests__/setup.ts` mocks `sessionStorage` and `window.location` globally before each test. Tests that need specific URLs must override `location.href` and `location.search`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # Noridoc: __tests__ | ||
|
|
||
| Path: @/__tests__ | ||
|
|
||
| ### Overview | ||
|
|
||
| - Test suite for the library, mirroring the `@/src` directory structure with subdirectories for `config/`, `core/`, and `react/`. | ||
| - Uses vitest with jsdom environment, `@testing-library/react` for React component/hook tests, and a global setup file for browser API mocks. | ||
| - Coverage thresholds are enforced at 80% for statements, branches, functions, and lines (configured in `@/vitest.config.ts`). | ||
|
|
||
| ### How it fits into the larger codebase | ||
|
|
||
| - Tests exercise all public API surfaces from `@/src/core`, `@/src/config`, and `@/src/react`. | ||
| - Coverage excludes barrel `index.ts` files and the `@/src/types` directory, since these contain only re-exports and type definitions. | ||
| - The test setup (`setup.ts`) provides the mock environment that all tests rely on: a sessionStorage mock backed by a plain object with `vi.fn()` wrappers, and a `window.location` mock defaulting to `https://example.com`. | ||
| - CI runs tests across Node 18, 20, and 22. | ||
|
|
||
| ### Core Implementation | ||
|
|
||
| - **`setup.ts`**: Creates a fresh sessionStorage mock and location mock in `beforeEach`, ensuring tests are isolated. The storage mock implements `getItem`, `setItem`, `removeItem`, `clear`, `length`, and `key`. Location is stubbed with `href`, `search`, `hash`, `pathname`, `protocol`, `host`, and `hostname`. | ||
| - **`core/` tests**: Cover capture (URL parsing, allowed parameters, key format conversion, SSR fallback), storage (write/read/clear, format conversion, validation of stored data, silent failure), appender (query/fragment placement, preserveExisting, remove, extract), keys (bidirectional conversion, standard and custom keys, detection, validation), and validator (protocol, domain, normalization, mutable default protocol). | ||
| - **`config/` tests**: Cover `createConfig` merging semantics (nullish coalescing, array replacement, object merge), `validateConfig` error messages, and `loadConfigFromJson` fallback behavior. | ||
| - **`react/` tests**: Use `@testing-library/react` `renderHook` and `render` to test `useUtmTracking` (auto-capture, manual capture, clear, appendToUrl with share context and exclusions) and `UtmProvider`/`useUtmContext` (context propagation, error on missing provider). | ||
|
|
||
| ### Things to Know | ||
|
|
||
| - The sessionStorage mock uses `vi.fn()` wrappers, which means tests can assert on call counts and arguments (`sessionStorage.setItem` calls, etc.). | ||
| - `window.location` is stubbed globally rather than using JSDOM's location, so tests that need specific URLs must override `location.href` and `location.search` in their setup. | ||
| - The `beforeEach` in `setup.ts` resets both mocks, so each test starts with empty storage and a clean `https://example.com` location. | ||
|
|
||
| Created and maintained by Nori. | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,32 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Noridoc: config | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Path: @/src/config | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Overview | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Defines default configuration values and provides utilities for creating, merging, and validating configuration objects. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Produces `ResolvedUtmConfig` objects (all fields required) from partial `UtmConfig` input, establishing the configuration contract for the rest of the library. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Re-exported through `@/src/index.ts` and consumed directly by `@/src/react/useUtmTracking.ts` and `@/src/debug`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### How it fits into the larger codebase | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `createConfig()` is the primary entry point, called by `useUtmTracking` in `@/src/react` to resolve user-provided partial config into a complete `ResolvedUtmConfig`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `@/src/debug` imports `getDefaultConfig()` from here as a fallback when no config is provided to diagnostic functions. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `DEFAULT_CONFIG` and `STANDARD_UTM_PARAMETERS` are the canonical definitions of default behavior (enabled, snake_case, sessionStorage key `utm_parameters`, auto-capture on mount, append to shares, the 6 standard UTM params). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - The config system does not perform side effects -- it is pure data transformation. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Core Implementation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `createConfig()` merges a partial user config with defaults using nullish coalescing (`??`) for scalar fields. Array fields (`allowedParameters`, `excludeFromShares`) are replaced wholesale when provided by the user, not merged. Object fields (`defaultParams`, `shareContextParams`) are shallow-merged. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `mergeConfig()` follows the same semantics but takes a `ResolvedUtmConfig` as the base instead of implicitly using defaults -- useful for layering configurations. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `loadConfigFromJson()` accepts `unknown` input, validates it is a non-null non-array object, then delegates to `createConfig()`. Invalid input falls back to defaults with a `console.warn`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `validateConfig()` performs runtime type checking on each config field and returns an array of error message strings (empty array means valid). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `getDefaultConfig()` returns a shallow copy of `DEFAULT_CONFIG` with cloned arrays and objects to prevent mutation of the shared constant. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Things to Know | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Array replacement (not merge) for `allowedParameters` is intentional: if a consumer provides `allowedParameters: ['utm_source']`, they get only that parameter, not the union with defaults. This is a deliberate design choice. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `STANDARD_UTM_PARAMETERS` is declared `as const` and used both as the default `allowedParameters` value and as the source of truth in tests. It defines the 6 standard UTM params: source, medium, campaign, term, content, id. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `validateConfig()` and `createConfig()` are independent -- `createConfig()` does not call `validateConfig()`. Validation is opt-in for consumers who want to check config before using it. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+5
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Align heading hierarchy to avoid MD001 warnings. Suggested fix-### Overview
+## Overview
@@
-### How it fits into the larger codebase
+## How it fits into the larger codebase
@@
-### Core Implementation
+## Core Implementation
@@
-### Things to Know
+## Things to Know📝 Committable suggestion
Suggested change
🧰 Tools🪛 markdownlint-cli2 (0.20.0)[warning] 5-5: Heading levels should only increment by one level at a time (MD001, heading-increment) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Created and maintained by Nori. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| # Noridoc: core | ||
|
|
||
| Path: @/src/core | ||
|
|
||
| ### Overview | ||
|
|
||
| - Framework-agnostic core logic for capturing UTM parameters from URLs, persisting them in sessionStorage, appending them to outbound URLs, converting between key formats, and validating URLs. | ||
| - This is the heart of the library. Every other module (`@/src/react`, `@/src/debug`) builds on top of these utilities. | ||
| - All functions are SSR-safe, returning empty/null/unchanged values when browser APIs are unavailable. | ||
|
|
||
| ### How it fits into the larger codebase | ||
|
|
||
| - `@/src/react/useUtmTracking.ts` orchestrates the core modules: it calls `captureUtmParameters` on mount, `storeUtmParameters`/`getStoredUtmParameters` for persistence, `appendUtmParameters` for URL generation, and `convertParams`/`isSnakeCaseUtmKey` for format handling. | ||
| - `@/src/debug` imports from `capture` and `storage` to assemble diagnostic snapshots. | ||
| - `@/src/index.ts` re-exports everything from this module for direct consumer use without React. | ||
| - All modules import types from `@/src/types`. | ||
|
|
||
| ### Core Implementation | ||
|
|
||
| The data flow through the core modules follows this path: | ||
|
|
||
| ```text | ||
| URL string | ||
| | | ||
| v | ||
| [capture.ts] -- parses URL, filters to utm_* keys, applies allowedParameters, converts key format | ||
| | | ||
| v | ||
| UtmParameters object | ||
| | | ||
| v | ||
| [storage.ts] -- serializes to JSON, writes/reads sessionStorage, validates on read | ||
| | | ||
| v | ||
| [appender.ts] -- converts params to snake_case, merges into target URL query/fragment | ||
| | | ||
| v | ||
| URL string with UTM params | ||
| ``` | ||
|
Comment on lines
5
to
39
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix heading levels and add a code‑fence language. MD001 and MD040 are both triggered here. Suggested fix-### Overview
+## Overview
@@
-### How it fits into the larger codebase
+## How it fits into the larger codebase
@@
-### Core Implementation
+## Core Implementation
@@
-```
+```text
@@
-### Things to Know
+## Things to Know🧰 Tools🪛 markdownlint-cli2 (0.20.0)[warning] 5-5: Heading levels should only increment by one level at a time (MD001, heading-increment) [warning] 22-22: Fenced code blocks should have a language specified (MD040, fenced-code-language) 🤖 Prompt for AI Agents |
||
|
|
||
| - **keys.ts**: Bidirectional key conversion between `snake_case` and `camelCase`. Uses lookup tables (`SNAKE_TO_CAMEL`, `CAMEL_TO_SNAKE`) for the 6 standard keys and regex-based conversion for custom keys. `isSnakeCaseUtmKey` checks for `utm_` prefix; `isCamelCaseUtmKey` checks for `utm` followed by an uppercase letter. `detectKeyFormat` scans keys and returns the first format found, defaulting to `snake_case` for empty objects. | ||
|
|
||
| - **capture.ts**: `captureUtmParameters()` takes a URL string (defaulting to `window.location.href`), parses it via `new URL()`, iterates `searchParams`, and filters to keys passing `isSnakeCaseUtmKey`. Optionally filters by an `allowedParameters` set and converts output via `convertParams`. | ||
|
|
||
| - **storage.ts**: Uses sessionStorage with a configurable key (default: `utm_parameters`). Write operations skip empty param objects and fail silently with `console.warn`. Read operations validate parsed JSON with `isValidStoredData()`, which checks that all keys pass `isUtmKey` and all values are strings or undefined. | ||
|
|
||
| - **appender.ts**: `appendUtmParameters()` always converts input params to snake_case before appending to URLs (URL parameters are conventionally snake_case). Supports query string or fragment placement via `AppendOptions.toFragment`. Uses a custom `buildQueryString()` that omits `=` for empty-string values. When adding to query, it also cleans conflicting UTM params from the fragment (and vice versa). `removeUtmParameters()` strips UTM params from both query and fragment. `extractUtmParameters()` pulls UTM params from both locations, with fragment params taking precedence. | ||
|
|
||
| - **validator.ts**: `validateUrl()` checks protocol (http/https only), domain (must contain a `.` for TLD), and parsability. `normalizeUrl()` prepends a configurable default protocol (module-level `let defaultProtocol`). `setDefaultProtocol()` mutates this module-level state. | ||
|
|
||
| ### Things to Know | ||
|
|
||
| - **Key invariant**: All URL-facing operations use snake_case keys. The `appender` always converts to snake_case before manipulating URLs, regardless of what format the consumer passes in. This means URLs always contain `utm_source`, never `utmSource`. | ||
| - **SSR safety pattern**: Each module that accesses browser APIs (`window`, `sessionStorage`, `URL`, `document`) checks for their existence before use and returns a safe fallback (empty object, null, or unchanged URL). This is consistent across all core modules. | ||
| - **Silent failure**: Storage and capture operations never throw. Errors produce `console.warn` messages and return fallback values. The appender returns the original URL unchanged on failure. | ||
| - **validator.ts mutable state**: `defaultProtocol` is module-level mutable state modified via `setDefaultProtocol()`. This is global -- all callers share the same default protocol. Tests that call `setDefaultProtocol()` should restore the original value. | ||
| - **Fragment parameter handling in appender**: When appending to query, conflicting UTM params are removed from the fragment. When appending to fragment, conflicting UTM params are removed from the query. Only fragments that contain `=` are treated as parameter-bearing; plain anchors like `#section` are left alone. | ||
|
|
||
| Created and maintained by Nori. | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,31 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # Noridoc: debug | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Path: @/src/debug | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Overview | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Development-time diagnostic utilities for inspecting UTM tracking state (config, URL params, stored params, storage availability). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Provides console-formatted output, status check messages, and an opt-in `window.utmDebug` helper object for browser console access. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Not part of the production data flow; intended for troubleshooting. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### How it fits into the larger codebase | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Imports `captureUtmParameters` from `@/src/core/capture`, `getStoredUtmParameters`/`isSessionStorageAvailable`/`getRawStoredValue` from `@/src/core/storage`, and `getDefaultConfig` from `@/src/config/defaults`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Re-exported through `@/src/index.ts` so consumers can call these functions directly. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - Does not depend on or interact with `@/src/react` -- it operates on the core layer only. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - All functions accept an optional `ResolvedUtmConfig`; when omitted, they fall back to `getDefaultConfig()`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Core Implementation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `getDiagnostics()` assembles a `DiagnosticInfo` snapshot: resolves config, captures URL params via `captureUtmParameters`, reads stored params via `getStoredUtmParameters`, and checks `isSessionStorageAvailable()`. SSR-safe (returns empty URL and empty params when `window` is unavailable). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `debugUtmState()` calls `getDiagnostics()` and formats output using `console.group`/`console.table` for structured browser console display. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `checkUtmTracking()` calls `getDiagnostics()` and returns an array of status strings with emoji prefixes indicating state (e.g., whether params are in the URL, in storage, or if there is a mismatch suggesting the hook has not initialized yet). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `installDebugHelpers()` checks for `?debug_utm=true` in the URL query string. If present, it attaches a `window.utmDebug` object with `state()`, `check()`, `diagnostics()`, and `raw()` methods. Only activates in browser environments. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ### Things to Know | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `installDebugHelpers()` is gated solely by the `debug_utm=true` URL parameter. It does not check `process.env` or `import.meta.env.DEV`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - The `window.utmDebug` object is attached via a cast to `Record<string, unknown>` to avoid TypeScript errors on the global augmentation. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - `checkUtmTracking()` detects a potential timing issue: when URL params exist but storage is empty and `captureOnMount` is enabled, it warns that the hook may not have initialized yet. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+5
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Align heading hierarchy to avoid MD001 warnings. The file jumps from H1 to H3. Please use H2 for the section headings. Suggested fix-### Overview
+## Overview
@@
-### How it fits into the larger codebase
+## How it fits into the larger codebase
@@
-### Core Implementation
+## Core Implementation
@@
-### Things to Know
+## Things to Know📝 Committable suggestion
Suggested change
🧰 Tools🪛 LanguageTool[uncategorized] ~29-~29: Use a comma before ‘but’ if it connects two independent clauses (unless they are closely connected and short). (COMMA_COMPOUND_SENTENCE_2) 🪛 markdownlint-cli2 (0.20.0)[warning] 5-5: Heading levels should only increment by one level at a time (MD001, heading-increment) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Created and maintained by Nori. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| # Noridoc: src | ||
|
|
||
| Path: @/src | ||
|
|
||
| ### Overview | ||
|
|
||
| - Root source directory for `@jackmisner/utm-toolkit`, a TypeScript library for capturing, storing, and appending UTM tracking parameters. | ||
| - Contains framework-agnostic core utilities (`@/src/core`, `@/src/config`, `@/src/debug`, `@/src/types`) and an optional React integration (`@/src/react`). | ||
| - Exposes two package entry points: `@/src/index.ts` (main, imported as `@jackmisner/utm-toolkit`) and `@/src/react/index.ts` (imported as `@jackmisner/utm-toolkit/react`). | ||
|
|
||
| ### How it fits into the larger codebase | ||
|
|
||
| - `@/src/index.ts` is the main barrel export that re-exports everything from `core`, `config`, `debug`, and `types`. This is what consumers get when they `import from '@jackmisner/utm-toolkit'`. | ||
| - `@/src/react/index.ts` is the second entry point for React-specific exports, built as a separate bundle with React externalized. | ||
| - `@/tsup.config.ts` defines these two entry points and produces dual ESM/CJS output with TypeScript declarations. | ||
| - `@/__tests__` mirrors this directory structure for testing. | ||
| - The library has zero runtime dependencies. React is an optional peer dependency used only by `@/src/react`. | ||
|
|
||
| ### Core Implementation | ||
|
|
||
| The library follows a layered architecture: | ||
|
|
||
| ```text | ||
| Consumer API | ||
| | | ||
| +--> src/index.ts (barrel) -----> core/ config/ debug/ types/ | ||
| | | ||
| +--> src/react/index.ts --------> react/ (useUtmTracking, UtmProvider) | ||
| | | ||
| +--> core/ config/ types/ | ||
| ``` | ||
|
|
||
| - **types/** (`@/src/types`): Shared type definitions consumed by all other modules. Defines the dual key format system (snake_case/camelCase) and configuration interfaces. | ||
| - **config/** (`@/src/config`): Pure configuration creation and validation. Merges partial user config with defaults to produce `ResolvedUtmConfig`. | ||
| - **core/** (`@/src/core`): Framework-agnostic UTM operations -- capture from URLs, persist in sessionStorage, append to outbound URLs, convert key formats, validate URLs. All SSR-safe. | ||
| - **debug/** (`@/src/debug`): Development-time diagnostics. Assembles state snapshots and provides formatted console output and optional `window.utmDebug` helpers. | ||
| - **react/** (`@/src/react`): React hook and context provider that orchestrate the core modules into stateful React APIs with auto-capture-on-mount behavior. | ||
|
|
||
| **Key data flow**: URL with UTM params --> `capture` --> `store` in sessionStorage --> `appendToUrl` for outbound link generation. | ||
|
|
||
| ### Things to Know | ||
|
|
||
| - **Dual key format invariant**: The library supports both `snake_case` (URL convention) and `camelCase` (TypeScript convention) throughout, but all URL-facing operations always convert to snake_case internally. This is enforced in `@/src/core/appender.ts`. | ||
| - **SSR safety**: Every module that touches browser APIs (`window`, `sessionStorage`, `URL`, `document`) guards against their absence. The library can be imported and initialized on the server without errors. | ||
| - **Two entry points**: The package.json `exports` map defines separate conditional exports for `.` and `./react`, each with ESM/CJS/types variants. React is externalized in the build so it is not bundled into the output. | ||
| - **No runtime dependencies**: The library is self-contained. All functionality is implemented from scratch using standard Web APIs (`URL`, `URLSearchParams`, `sessionStorage`). | ||
|
Comment on lines
5
to
46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix heading levels and add a code‑fence language. Suggested fix-### Overview
+## Overview
@@
-### How it fits into the larger codebase
+## How it fits into the larger codebase
@@
-### Core Implementation
+## Core Implementation
@@
-```
+```text
@@
-### Things to Know
+## Things to Know🧰 Tools🪛 markdownlint-cli2 (0.20.0)[warning] 5-5: Heading levels should only increment by one level at a time (MD001, heading-increment) [warning] 23-23: Fenced code blocks should have a language specified (MD040, fenced-code-language) 🤖 Prompt for AI Agents |
||
|
|
||
| Created and maintained by Nori. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Align heading hierarchy to avoid MD001 warnings.
Suggested fix
🧰 Tools
🪛 LanguageTool
[uncategorized] ~20-~20: Loose punctuation mark.
Context: ...## Core Implementation -
setup.ts: Creates a fresh sessionStorage mock and...(UNLIKELY_OPENING_PUNCTUATION)
[style] ~23-~23: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...omJson
fallback behavior. - **react/tests**: Use@testing-library/reactrender...(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
🪛 markdownlint-cli2 (0.20.0)
[warning] 5-5: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3
(MD001, heading-increment)
[warning] 20-20: Strong style
Expected: underscore; Actual: asterisk
(MD050, strong-style)
[warning] 20-20: Strong style
Expected: underscore; Actual: asterisk
(MD050, strong-style)
[warning] 21-21: Strong style
Expected: underscore; Actual: asterisk
(MD050, strong-style)
[warning] 21-21: Strong style
Expected: underscore; Actual: asterisk
(MD050, strong-style)
[warning] 22-22: Strong style
Expected: underscore; Actual: asterisk
(MD050, strong-style)
[warning] 22-22: Strong style
Expected: underscore; Actual: asterisk
(MD050, strong-style)
[warning] 23-23: Strong style
Expected: underscore; Actual: asterisk
(MD050, strong-style)
[warning] 23-23: Strong style
Expected: underscore; Actual: asterisk
(MD050, strong-style)
🤖 Prompt for AI Agents