diff --git a/.claude/prompts/readme-docs.md b/.claude/prompts/readme-docs.md new file mode 100644 index 0000000..38d6734 --- /dev/null +++ b/.claude/prompts/readme-docs.md @@ -0,0 +1,23 @@ +You are helping update THIS repository’s documentation. Ultrathink. + +Goals: + +- Keep all documentation in README.md as the single source of truth. +- Use the existing README.md as the PRIMARY source of truth. +- VALIDATE every section; FLAG or FIX anything deprecated or incorrect. +- Reorganize into this structure (create sections if missing): + 1. Overview + 2. Setup (prereqs, install, run, common pitfalls) + 3. Testing (unit + functional, coverage, troubleshooting) + 4. Architecture (directory layout, stack, integrations/flows) + 5. Endpoints (routes, methods, purpose, auth/flags notes) + 6. References/Links + +Strict rules: + +- **Preserve all existing images, screenshots, and diagrams exactly as they are. Never remove or drop them.** +- Cross-check commands and scripts against package.json and repo code. +- Keep commands copy-paste runnable; mark uncertain bits as “Needs verification”. +- Do NOT introduce new secrets or internal hostnames that aren't already present in the existing README — this doesn't mean stripping internal URLs/hostnames the README already documents (e.g. devbox/testbox addresses) when "use the existing README as the PRIMARY source of truth" says to keep them. +- Keep it concise and actionable. +- Also update CLAUDE.md with any new relevant info that will help future documentation tasks. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4ce7a67 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,63 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +`@pipedrive/app-extensions-sdk` is a small client-side TypeScript library published to npm. It runs inside the iframe of a Pipedrive custom UI extension (panel, modal, or floating window) and lets that extension talk to the parent Pipedrive window via `postMessage`/`MessageChannel` — issuing commands (resize, open a modal, show a snackbar, etc.) and listening for events (visibility changes, user settings changes). There is no server component; the entire implementation lives in `src/`. + +## Commands + +- `npm run build` — compile `src/` with Rollup into `dist/` (CJS `dist/index.js` + UMD `dist/index.umd.js`, both via `tsc`/rollup-typescript, UMD is minified with terser). +- `npm run watch` — Rollup in watch mode. +- `npm test` — run Jest (`--passWithNoTests`; there are currently no test files, though the harness is fully configured — see Testing below). +- `npm run coverage` — Jest with coverage. +- `npm run lint` — `tsc --noEmit` followed by ESLint over `**/*.{js,jsx,ts,tsx}`. +- `npm run format` — Prettier write + `eslint --fix`. +- `npm run docs:ai` — invokes Claude Code headlessly with `.claude/prompts/readme-docs.md` as an appended system prompt to reconcile `README.md` against the current source/scripts. + +Node >=22 and npm >=8 are required (`devEngines` in package.json); `.nvmrc` pins Node 24. A Husky `pre-commit` hook runs `npm run format && npm run lint` — don't bypass it. + +## Testing + +Jest is configured (`jest.config.js`) with `jsdom` environment and `testRegex: '__tests__/.*\.test\.[tj]s?$'` — new tests belong in a `__tests__/` directory (e.g. `src/__tests__/index.test.ts`) using that naming convention, not colocated `*.test.ts` files next to source. Coverage thresholds are currently set to 0, so nothing is enforced yet. + +## Architecture + +Everything lives in four files under `src/`: + +- **`types.ts`** — the source of truth for the protocol: `Command` and `Event` enums (the full set of message types the parent window understands), plus the `Args` / `CommandResponse` mapped types that tie each `Command` to its request/response shape. Adding a new SDK capability starts here: add the enum member, then extend `Args`/`CommandResponse`. +- **`index.ts`** — the `AppExtensionsSDK` class. Two message-passing primitives underpin everything: + - `postMessage()` (private) opens a `MessageChannel`, posts `{ payload, id: identifier }` to `this.window` (the parent, by default `window.parent`), and resolves/rejects based on the response received on `channel.port1`. `execute()` (public) is the typed wrapper around this for `Command`s. + - `listen()` sets up either a `MessageChannel`-based subscription (for events proxied from the parent, e.g. `USER_SETTINGS_CHANGE`) or, for `PAGE_VISIBILITY_STATE`, a native `document.visibilitychange` listener — that one event never leaves the iframe. `listen()` also side-effects `this.userSettings` when a `USER_SETTINGS_CHANGE` payload arrives. + - The SDK must be constructed with an `identifier` (auto-detected from the `?id=` URL query param via `detectIdentifier()` if not passed) and must have `initialize()` awaited before `execute()` will work — `execute()` throws if `initialized` is false. +- **`utils.ts`** — small DOM-dependent helpers used by the constructor: `detectIdentifier` (reads `?id=`), `detectUserSettings` (reads `?theme=`), `detectIframeFocus` (fires a callback on the iframe's first `focus` after a `blur`, used to emit the `FOCUSED` tracking event). +- **`umd.ts`** — a separate Rollup entry point that re-exports `AppExtensionsSDK` with all enums (`Command`, `Event`, `Modal`, `Color`, etc.) attached as static properties, for consumers loading the library via a plain `