|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Commands |
| 6 | + |
| 7 | +```bash |
| 8 | +# Install all dependencies (root + all packages) |
| 9 | +npm install |
| 10 | + |
| 11 | +# Run all tests across all packages |
| 12 | +npm test |
| 13 | + |
| 14 | +# Run tests for a single package |
| 15 | +npm test -w packages/spacecat-shared-utils |
| 16 | + |
| 17 | +# Run integration tests (data-access only, requires Docker) |
| 18 | +npm run test:it -w packages/spacecat-shared-data-access |
| 19 | + |
| 20 | +# Lint all packages |
| 21 | +npm run lint |
| 22 | + |
| 23 | +# Lint a single package |
| 24 | +npm run lint -w packages/spacecat-shared-utils |
| 25 | + |
| 26 | +# Generate API docs |
| 27 | +npm run docs |
| 28 | + |
| 29 | +# Dry-run semantic release (on non-main branches) |
| 30 | +npm run semantic-release-dry |
| 31 | + |
| 32 | +# Clean node_modules and lock file |
| 33 | +npm run clean |
| 34 | +``` |
| 35 | + |
| 36 | +Node >=22 <25 is required (see `.nvmrc` for exact version). The project is pure ESM (`"type": "module"`). |
| 37 | + |
| 38 | +Coverage enforcement: **100% lines/statements, 97% branches** per package (see each package's `.nycrc.json`). |
| 39 | + |
| 40 | +--- |
| 41 | + |
| 42 | +## Architecture Overview |
| 43 | + |
| 44 | +This is the **foundation library layer** for all SpaceCat Node.js services. It's an npm workspaces monorepo (no lerna/nx) containing 22 packages that provide data access, API clients, utilities, and auth used by `spacecat-api-service`, `spacecat-audit-worker`, `spacecat-import-worker`, and other platform services. |
| 45 | + |
| 46 | +### Monorepo Structure |
| 47 | + |
| 48 | +``` |
| 49 | +spacecat-shared/ |
| 50 | +├── packages/ # 22 npm workspace packages |
| 51 | +│ ├── spacecat-shared-data-access/ # Core data layer (PostgREST + Aurora PostgreSQL) |
| 52 | +│ ├── spacecat-shared-http-utils/ # HTTP response helpers + auth middleware |
| 53 | +│ ├── spacecat-shared-utils/ # General utilities (validation, S3, SQS, helpers) |
| 54 | +│ ├── spacecat-shared-rum-api-client/ # RUM Bundler API client |
| 55 | +│ ├── spacecat-shared-vault-secrets/ # HashiCorp Vault secrets (replaces helix-shared-secrets) |
| 56 | +│ ├── spacecat-shared-*-client/ # 13+ API clients (ahrefs, google, slack, etc.) |
| 57 | +│ ├── spacecat-shared-html-analyzer/ # HTML analysis utilities |
| 58 | +│ └── spacecat-shared-example/ # Example/template package |
| 59 | +├── docs/ # Generated API docs |
| 60 | +├── eslint.config.js # Root ESLint flat config |
| 61 | +├── .releaserc.cjs # Root semantic-release config |
| 62 | +└── package.json # Monorepo root (private, workspaces: ./packages/*) |
| 63 | +``` |
| 64 | + |
| 65 | +### Package Catalog |
| 66 | + |
| 67 | +| Category | Packages | Purpose | |
| 68 | +|----------|----------|---------| |
| 69 | +| **Data layer** | `data-access` | Entity models/collections backed by PostgreSQL via PostgREST | |
| 70 | +| **HTTP/Auth** | `http-utils` | HTTP response helpers, auth handlers (ScopedApiKey, LegacyApiKey, IMS) | |
| 71 | +| **Utilities** | `utils` | Validation, S3/SQS helpers, schemas, date functions | |
| 72 | +| **Secrets** | `vault-secrets` | HashiCorp Vault integration (AppRole auth, KV secrets) | |
| 73 | +| **HTML** | `html-analyzer` | HTML analysis and extraction | |
| 74 | +| **API clients** | `ahrefs-client`, `athena-client`, `brand-client`, `cloud-manager-client`, `content-client`, `drs-client`, `google-client`, `gpt-client`, `ims-client`, `launchdarkly-client`, `rum-api-client`, `scrape-client`, `slack-client`, `splunk-client`, `tier-client`, `tokowaka-client` | External service integrations | |
| 75 | +| **Example** | `example` | Template for creating new packages | |
| 76 | + |
| 77 | +### Package-Level CLAUDE.md Files |
| 78 | + |
| 79 | +Some packages have their own CLAUDE.md with deeper architectural guidance: |
| 80 | + |
| 81 | +- [`packages/spacecat-shared-data-access/CLAUDE.md`](packages/spacecat-shared-data-access/CLAUDE.md) — Entity pattern, SchemaBuilder DSL, PostgREST queries, integration tests |
| 82 | +- [`packages/spacecat-shared-vault-secrets/CLAUDE.md`](packages/spacecat-shared-vault-secrets/CLAUDE.md) — Vault pipeline, AppRole auth, E2E validation |
| 83 | +- [`packages/spacecat-shared-google-client/docs/CLAUDE.md`](packages/spacecat-shared-google-client/docs/CLAUDE.md) — GSC client, OAuth, environment variables |
| 84 | + |
| 85 | +--- |
| 86 | + |
| 87 | +## Key Patterns |
| 88 | + |
| 89 | +### Per-Package File Structure |
| 90 | + |
| 91 | +Each package follows this layout: |
| 92 | + |
| 93 | +``` |
| 94 | +packages/spacecat-shared-<name>/ |
| 95 | +├── src/ # Source code |
| 96 | +│ └── index.js # Package entry point (barrel exports) |
| 97 | +├── test/ # Tests (mirrors src/ structure) |
| 98 | +├── package.json # Package metadata, dependencies, scripts |
| 99 | +├── .releaserc.cjs # semantic-release config (extends monorepo) |
| 100 | +├── .nycrc.json # c8 coverage config |
| 101 | +├── README.md |
| 102 | +└── CHANGELOG.md # Auto-generated by semantic-release |
| 103 | +``` |
| 104 | + |
| 105 | +### Entity Pattern (data-access) |
| 106 | + |
| 107 | +The data-access package defines entities with 4 files per entity: |
| 108 | + |
| 109 | +``` |
| 110 | +src/models/<entity>/ |
| 111 | + <entity>.schema.js # SchemaBuilder definition (attributes, references, indexes) |
| 112 | + <entity>.model.js # Extends BaseModel (business logic, constants) |
| 113 | + <entity>.collection.js # Extends BaseCollection (custom queries) |
| 114 | + index.js # Re-exports model, collection, schema |
| 115 | +``` |
| 116 | + |
| 117 | +See [`packages/spacecat-shared-data-access/CLAUDE.md`](packages/spacecat-shared-data-access/CLAUDE.md) for the SchemaBuilder DSL, attribute options, and field mapping details. |
| 118 | + |
| 119 | +### Field Mapping Convention |
| 120 | + |
| 121 | +Models use **camelCase**, the database uses **snake_case**. Mapping is automatic via `postgrest.utils.js`. Override with `postgrestField: 'custom_name'` on individual attributes. |
| 122 | + |
| 123 | +### TypeScript Declarations |
| 124 | + |
| 125 | +Packages provide `.d.ts` files for public APIs — no TypeScript source code. Type declarations live alongside the source files they describe. |
| 126 | + |
| 127 | +### API Client Pattern |
| 128 | + |
| 129 | +API clients typically follow this structure: |
| 130 | + |
| 131 | +```js |
| 132 | +export default class FooClient { |
| 133 | + constructor(config, log) { /* ... */ } |
| 134 | + async someOperation(params) { /* ... */ } |
| 135 | +} |
| 136 | + |
| 137 | +// Factory function exported from index.js |
| 138 | +export function createFooClient(config, log) { |
| 139 | + return new FooClient(config, log); |
| 140 | +} |
| 141 | +``` |
| 142 | + |
| 143 | +--- |
| 144 | + |
| 145 | +## Testing |
| 146 | + |
| 147 | +**Framework:** Mocha + Chai + chai-as-promised + sinon + sinon-chai |
| 148 | +**Coverage:** c8 (per-package `.nycrc.json`) |
| 149 | +**HTTP mocking:** nock |
| 150 | +**Reporters:** mocha-multi-reporters (spec + xunit) |
| 151 | + |
| 152 | +### Running Tests |
| 153 | + |
| 154 | +```bash |
| 155 | +# All packages |
| 156 | +npm test |
| 157 | + |
| 158 | +# Single package |
| 159 | +npm test -w packages/spacecat-shared-utils |
| 160 | + |
| 161 | +# Integration tests (data-access only — requires Docker + ECR access) |
| 162 | +npm run test:it -w packages/spacecat-shared-data-access |
| 163 | +``` |
| 164 | + |
| 165 | +### Test Conventions |
| 166 | + |
| 167 | +- Tests live in `test/` (or `test/unit/`) and mirror the `src/` directory structure |
| 168 | +- Use `describe`/`it` blocks with `expect` assertions |
| 169 | +- sinon stubs for external dependencies; nock for HTTP mocking |
| 170 | +- Each test file uses a sandbox: `const sandbox = sinon.createSandbox()` with `afterEach(() => sandbox.restore())` |
| 171 | +- Coverage thresholds are enforced per package |
| 172 | + |
| 173 | +### Integration Tests (data-access) |
| 174 | + |
| 175 | +Integration tests require the `mysticat-data-service` Docker image from ECR: |
| 176 | + |
| 177 | +```bash |
| 178 | +# ECR login (one-time) |
| 179 | +aws ecr get-login-password --profile spacecat-dev --region us-east-1 \ |
| 180 | + | docker login --username AWS --password-stdin 682033462621.dkr.ecr.us-east-1.amazonaws.com |
| 181 | + |
| 182 | +npm run test:it -w packages/spacecat-shared-data-access |
| 183 | +``` |
| 184 | + |
| 185 | +--- |
| 186 | + |
| 187 | +## Working with the Monorepo |
| 188 | + |
| 189 | +### Inter-Package Dependencies |
| 190 | + |
| 191 | +Some packages depend on others in the monorepo (e.g., `data-access` depends on `utils`). These are declared as regular npm dependencies with the published `@adobe/spacecat-shared-*` package name and version. |
| 192 | + |
| 193 | +### Running Commands for a Single Package |
| 194 | + |
| 195 | +Use the `-w` (workspace) flag: |
| 196 | + |
| 197 | +```bash |
| 198 | +npm test -w packages/spacecat-shared-<name> |
| 199 | +npm run lint -w packages/spacecat-shared-<name> |
| 200 | +``` |
| 201 | + |
| 202 | +### Adding a New Package |
| 203 | + |
| 204 | +1. Copy `packages/spacecat-shared-example/` as a starting template |
| 205 | +2. Rename the directory to `spacecat-shared-<name>` |
| 206 | +3. Update `package.json`: name (`@adobe/spacecat-shared-<name>`), description, dependencies |
| 207 | +4. Update `.releaserc.cjs` (inherits from root) |
| 208 | +5. Implement source in `src/`, tests in `test/` |
| 209 | +6. Ensure 100% coverage with `.nycrc.json` thresholds |
| 210 | +7. The root `workspaces: ["./packages/*"]` auto-discovers it |
| 211 | + |
| 212 | +### Adding a New Entity (data-access) |
| 213 | + |
| 214 | +1. Create 4 files in `src/models/<entity>/`: `schema.js`, `model.js`, `collection.js`, `index.js` |
| 215 | +2. Register the entity in `src/models/index.js` |
| 216 | +3. The corresponding DB migration must be created in [mysticat-data-service](https://github.com/adobe/mysticat-data-service) |
| 217 | +4. Add unit tests in `test/unit/models/<entity>/` |
| 218 | + |
| 219 | +### Adding a New Import Type (data-access) |
| 220 | + |
| 221 | +1. Update three locations in `src/models/site/config.js`: `IMPORT_TYPES`, `IMPORT_TYPE_SCHEMAS`, `DEFAULT_IMPORT_CONFIGS` |
| 222 | +2. Update three corresponding assertions in `test/unit/models/site/config.test.js` |
| 223 | + |
| 224 | +See [`packages/spacecat-shared-data-access/CLAUDE.md`](packages/spacecat-shared-data-access/CLAUDE.md) for full details including code examples. |
| 225 | + |
| 226 | +--- |
| 227 | + |
| 228 | +## Commits and Releases |
| 229 | + |
| 230 | +- **Conventional commits** are required — use `npm run commit` (commitizen) or write them manually |
| 231 | +- **Pre-commit hook** (husky + lint-staged) runs ESLint on staged `.js` files |
| 232 | +- **semantic-release** with `semantic-release-monorepo` handles per-package versioning |
| 233 | +- Release happens automatically on push to `main` — each package is released independently based on commits touching its files |
| 234 | +- **Branch:** `main` is the only release branch |
| 235 | + |
| 236 | +### After Publishing |
| 237 | + |
| 238 | +When a new version of a `spacecat-shared-*` package is published, update the dependency version in all consuming repos (`spacecat-api-service`, `spacecat-audit-worker`, `spacecat-import-worker`, etc.). |
| 239 | + |
| 240 | +--- |
| 241 | + |
| 242 | +## Linting |
| 243 | + |
| 244 | +- **Config:** `eslint.config.js` (ESLint flat config) |
| 245 | +- **Base:** `@adobe/eslint-config-helix` (recommended + source + test presets) |
| 246 | +- **Parser:** `@babel/eslint-parser` with ESM + import assertions |
| 247 | +- **Test overrides:** `no-console: off`, `func-names: off` |
| 248 | +- **Ignored:** `.vscode`, `.idea`, `coverage`, `docs`, `vendor`, `dist`, `.releaserc.cjs`, `test/fixtures` |
0 commit comments