claude-toolkit 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +54 -13
  3. package/bin/cli.ts +75 -19
  4. package/package.json +6 -7
  5. package/src/generator.ts +64 -26
  6. package/src/refresh-hint.ts +95 -0
  7. package/bin/postinstall.mjs +0 -69
  8. package/docs/README.md +0 -59
  9. package/docs/agents/code-reviewer.md +0 -76
  10. package/docs/agents/github-workflow.md +0 -98
  11. package/docs/best-practices/capacitor/README.md +0 -64
  12. package/docs/best-practices/capacitor/capacitor-8.md +0 -60
  13. package/docs/best-practices/capacitor/capgo-setup.md +0 -151
  14. package/docs/best-practices/capacitor/channels-and-rollouts.md +0 -101
  15. package/docs/best-practices/capacitor/live-updates-ota.md +0 -75
  16. package/docs/best-practices/capacitor/security-encryption.md +0 -68
  17. package/docs/best-practices/solidjs/README.md +0 -43
  18. package/docs/best-practices/solidjs/anti-patterns.md +0 -166
  19. package/docs/best-practices/solidjs/component-patterns.md +0 -131
  20. package/docs/best-practices/solidjs/context-and-global-state.md +0 -131
  21. package/docs/best-practices/solidjs/control-flow.md +0 -124
  22. package/docs/best-practices/solidjs/data-fetching.md +0 -205
  23. package/docs/best-practices/solidjs/effects-and-lifecycle.md +0 -113
  24. package/docs/best-practices/solidjs/performance.md +0 -100
  25. package/docs/best-practices/solidjs/props-patterns.md +0 -100
  26. package/docs/best-practices/solidjs/reactivity-model.md +0 -104
  27. package/docs/best-practices/solidjs/signals-and-state.md +0 -78
  28. package/docs/best-practices/solidjs/stores-and-nested-state.md +0 -111
  29. package/docs/best-practices/solidjs/typescript-integration.md +0 -186
  30. package/docs/best-practices/testing/README.md +0 -84
  31. package/docs/best-practices/testing/playwright-e2e.md +0 -649
  32. package/docs/best-practices/testing/storybook-interaction.md +0 -455
  33. package/docs/best-practices/testing/vitest-unit.md +0 -451
  34. package/docs/best-practices/typescript/README.md +0 -45
  35. package/docs/best-practices/typescript/any-and-unknown.md +0 -73
  36. package/docs/best-practices/typescript/deriving-vs-decoupling.md +0 -83
  37. package/docs/best-practices/typescript/discriminated-unions.md +0 -75
  38. package/docs/best-practices/typescript/enums-alternatives.md +0 -72
  39. package/docs/best-practices/typescript/essential-patterns.md +0 -119
  40. package/docs/best-practices/typescript/generics-patterns.md +0 -105
  41. package/docs/best-practices/typescript/micro-opinions.md +0 -87
  42. package/docs/best-practices/typescript/runtime-validation.md +0 -62
  43. package/docs/best-practices/typescript/satisfies-operator.md +0 -100
  44. package/docs/best-practices/typescript/tsconfig-cheat-sheet.md +0 -160
  45. package/docs/best-practices/typescript/type-organization.md +0 -64
  46. package/docs/best-practices/typescript/type-vs-interface.md +0 -80
  47. package/docs/commands/code-quality.md +0 -36
  48. package/docs/commands/onboard.md +0 -63
  49. package/docs/commands/pr-review.md +0 -91
  50. package/docs/commands/pr-summary.md +0 -50
  51. package/docs/commands/proto-check.md +0 -59
  52. package/docs/commands/ticket.md +0 -56
  53. package/docs/skills/code-style.md +0 -26
  54. package/docs/skills/systematic-debugging.md +0 -70
  55. package/docs/skills/testing-patterns.md +0 -140
  56. package/docs/skills/typescript-conventions.md +0 -283
  57. package/docs/skills/verification-before-completion.md +0 -103
  58. package/docs/stacks/capacitor-ota.md +0 -145
  59. package/docs/stacks/cloudflare-d1-kv.md +0 -144
  60. package/docs/stacks/esnext-idioms.md +0 -25
  61. package/docs/stacks/i18n-typesafe.md +0 -141
  62. package/docs/stacks/playwright-patterns.md +0 -179
  63. package/docs/stacks/protobuf-contracts.md +0 -85
  64. package/docs/stacks/rust-wasm-patterns.md +0 -106
  65. package/docs/stacks/solidjs-patterns.md +0 -110
  66. package/docs/stacks/storybook-patterns.md +0 -177
  67. package/docs/stacks/vanilla-extract-patterns.md +0 -115
  68. package/docs/stacks/vite-vitest-patterns.md +0 -485
@@ -1,145 +0,0 @@
1
- # Capacitor OTA (Capgo) Patterns
2
-
3
- > Capacitor 8 native runtime and Capgo over-the-air live updates: channels, encryption, staged rollouts, and store-safe delivery.
4
-
5
- **Type:** Stack Skill (requires `capacitor` stack)
6
- **Source:** [`stacks/capacitor/skills/ct-capacitor-ota/SKILL.md`](../../stacks/capacitor/skills/ct-capacitor-ota/SKILL.md)
7
- **Directory Mappings:** `capacitor.config.ts`, `capacitor.config.json`, `ios/`, `android/`
8
- **File Extensions:** _(none — keyed on config + content)_
9
-
10
- ## Overview
11
-
12
- Capacitor wraps a web app in a native iOS/Android shell. Capgo's `@capgo/capacitor-updater` lets you replace the web bundle that shell loads, shipping JS/HTML/CSS fixes in minutes instead of waiting on store review.
13
-
14
- **Golden rule:** OTA updates the **web layer only**. Native code changes (new plugin, native dependency, Capacitor major bump) require a new store binary. Never OTA a bundle whose native contract differs from the installed shell.
15
-
16
- ## Versions (as of June 2026)
17
-
18
- | Package | Version | Notes |
19
- | -------------------------- | ------- | ---------------------------------------------------------------- |
20
- | `@capacitor/core` & CLI | ^8.3.x | 8.3.1 (2026-04-16); Node 22+, SPM default for new iOS apps |
21
- | `@capgo/capacitor-updater` | ^8.x | Major version tracks Capacitor major; v8 stores channels locally |
22
- | `@capgo/cli` | latest | `npx @capgo/cli@latest …` — pin in CI |
23
-
24
- ## Setup
25
-
26
- ```bash
27
- npm i @capgo/capacitor-updater && npx cap sync
28
- npx @capgo/cli@latest init <API_KEY> # adds app, injects notifyAppReady, builds, uploads, tests
29
- ```
30
-
31
- ## Critical: notifyAppReady()
32
-
33
- Call it as early as possible after the bundle boots. If the plugin doesn't hear it within `appReadyTimeout` (default 10s), it assumes the bundle is broken and **auto-rolls back** to the previous one — the safety net that makes OTA reversible.
34
-
35
- ```typescript
36
- import { CapacitorUpdater } from "@capgo/capacitor-updater";
37
-
38
- CapacitorUpdater.notifyAppReady(); // top of bootstrap, before heavy async work
39
- ```
40
-
41
- ## Configuration
42
-
43
- ```typescript
44
- // capacitor.config.ts
45
- import type { CapacitorConfig } from "@capacitor/cli";
46
-
47
- const config: CapacitorConfig = {
48
- appId: "com.example.app",
49
- appName: "Example",
50
- webDir: "dist",
51
- plugins: {
52
- CapacitorUpdater: {
53
- autoUpdate: "atBackground", // apply when app backgrounds
54
- appReadyTimeout: 10000,
55
- autoDeleteFailed: true,
56
- autoDeletePrevious: true,
57
- resetWhenUpdate: true, // wipe OTA bundles on native app update
58
- // defaultChannel: "Development", // TEST/QA BUILDS ONLY — omit in production
59
- },
60
- },
61
- };
62
-
63
- export default config;
64
- ```
65
-
66
- ### autoUpdate modes
67
-
68
- | Value | Behavior |
69
- | ---------------------------- | ------------------------------------------------------------ |
70
- | `false` / `"off"` | No automatic updates — drive everything from JS |
71
- | `"atBackground"` *(default)* | Download silently, apply when app moves to background |
72
- | `"atInstall"` | Apply right after a fresh install / native update |
73
- | `"onLaunch"` | Apply on next launch, then background |
74
- | `"always"` | Apply immediately whenever a check runs |
75
- | `"onlyDownload"` | Download only; emit `updateAvailable` for you to act |
76
-
77
- `periodCheckDelay` sets poll frequency (seconds, **minimum 600** / 10 min).
78
-
79
- ## Channels
80
-
81
- A channel points to one JS bundle. Swap which bundle a channel points to for instant rollout/rollback without rebuilding.
82
-
83
- ```bash
84
- npx @capgo/cli@latest bundle upload --channel=production # build → upload → assign
85
- npx @capgo/cli@latest channel set production -s default # make it the cloud default
86
- npx @capgo/cli@latest channel set beta --self-assign # allow in-app setChannel()
87
- ```
88
-
89
- **Precedence** (highest first): forced device mapping → cloud per-device override → config `defaultChannel` → cloud Default Channel.
90
-
91
- Ship production binaries **without** `defaultChannel` so users follow the cloud default. Recommended ladder:
92
-
93
- | Channel | Bundle version | Audience |
94
- | ------------- | -------------- | ------------------------- |
95
- | `development` | `1.2.3-dev.1` | dev / emulator builds |
96
- | `qa` | `1.2.3-qa.1` | QA verification |
97
- | `staging` | `1.2.3-rc.1` | production-like sign-off |
98
- | `production` | `1.2.3` | end users (store version) |
99
-
100
- ```typescript
101
- // Runtime switch — needs "Allow device self-assignment" on the channel (v8+).
102
- await CapacitorUpdater.setChannel({ channel: "beta", triggerAutoUpdate: true });
103
- ```
104
-
105
- ## End-to-end encryption (v2)
106
-
107
- Hybrid RSA-2048 + AES-256: a random AES key encrypts each bundle; your private RSA key signs the key + checksum; the app decrypts with the embedded public key.
108
-
109
- ```bash
110
- npx @capgo/cli@latest key create
111
- npx @capgo/cli@latest bundle upload --key-v2 --channel=production
112
- ```
113
-
114
- - `.capgo_key_v2` (private) — **never commit**; store as a CI secret.
115
- - `.capgo_key_v2.pub` (public) — safe to commit.
116
- - With encryption configured, the plugin **strictly enforces** a valid signature; tampered/unsigned bundles are rejected.
117
-
118
- ## Staged rollouts & monitoring
119
-
120
- - Start at ~10% of users, widen over 24h; schedule heavy rollouts 1–4 AM local, Wi-Fi-preferred.
121
- - Targets: ~95% adoption within 24h, ~82% global success rate.
122
- - Roll back instantly from the channel dashboard (crown icon on the previous build).
123
- - Watch the `downloadFailed` / `appReady` ratio in Capgo stats; a spike means roll back.
124
-
125
- ## Store compliance
126
-
127
- OTA of JS/HTML/CSS is permitted — Apple §3.3.2 (since iOS 4.3) and Google Play (webview/JS VM code is exempt). Shipping native code OTA is not.
128
-
129
- ## Anti-Patterns
130
-
131
- | Anti-pattern | Why it's wrong |
132
- | ----------------------------------------- | ------------------------------------------------------------------------ |
133
- | **Not calling `notifyAppReady()`** | Every bundle auto-rolls back after `appReadyTimeout`. The #1 OTA failure. |
134
- | **OTA-ing a native change** | Bundle needs a native API the shell lacks → crash. Bump the binary first. |
135
- | **`defaultChannel` in production builds** | Pins users off the cloud default; breaks instant rollback. |
136
- | **Committing `.capgo_key_v2`** | Leaks your signing key. Commit only the `.pub`. |
137
- | **`periodCheckDelay` < 600** | Silently clamped; never polls faster than 10 min. |
138
- | **100% rollout with no canary** | A bad bundle hits everyone at once. Stage it. |
139
- | **Unpinned `@capgo/cli@latest` in CI** | A CLI minor can change upload behavior mid-pipeline. Pin it. |
140
-
141
- ## See Also
142
-
143
- - [Capacitor & OTA Best Practices](../best-practices/capacitor/README.md) — full reference set
144
- - [ct-vite-vitest-patterns](vite-vitest-patterns.md) — the build that produces `webDir`
145
- - [ct-playwright-patterns](playwright-patterns.md) — E2E-verify a bundle before upload
@@ -1,144 +0,0 @@
1
- # Cloudflare D1 and KV Patterns
2
-
3
- > Cloudflare D1 SQL database and KV cache patterns.
4
-
5
- **Type:** Stack Skill (requires `cloudflare` stack)
6
- **Source:** [`stacks/cloudflare/skills/ct-cloudflare-d1-kv/SKILL.md`](../stacks/cloudflare/skills/ct-cloudflare-d1-kv/SKILL.md)
7
- **Directory Mappings:** `src/db/`, `migrations/`
8
- **File Extensions:** `.sql`
9
-
10
- ## Overview
11
-
12
- D1 is Cloudflare's serverless SQLite database, and KV is an eventually-consistent key-value store. Together they provide primary data storage and read-heavy caching for Workers.
13
-
14
- ## D1 Database
15
-
16
- ### Prepared Statements
17
-
18
- Always use prepared statements with `.bind()` -- never concatenate SQL strings.
19
-
20
- ```rust
21
- let stmt = db.prepare("SELECT * FROM users WHERE id = ?1")
22
- .bind(&[id.into()])?;
23
- let user = stmt.first::<User>(None).await?;
24
- ```
25
-
26
- ### Batch Operations
27
-
28
- Execute multiple statements in a single round trip. Batch operations are transactional -- all succeed or all fail.
29
-
30
- ```rust
31
- let results = db.batch(vec![
32
- db.prepare("INSERT INTO events ...").bind(&[...])?,
33
- db.prepare("INSERT INTO event_members ...").bind(&[...])?,
34
- ]).await?;
35
- ```
36
-
37
- ### Migration Workflow
38
-
39
- ```bash
40
- wrangler d1 migrations create DB "add_users_table" # Create migration
41
- wrangler d1 migrations apply DB --local # Apply locally
42
- wrangler d1 migrations apply DB --remote # Apply to remote
43
- ```
44
-
45
- ### Schema Conventions
46
-
47
- - Use `TEXT` for IDs (UUIDs/nanoids, not auto-increment integers)
48
- - Use `TEXT` for timestamps in ISO 8601 format
49
- - Always add `created_at` and `updated_at` columns
50
- - Create indexes for columns used in WHERE clauses and JOINs
51
- - Use `IF NOT EXISTS` / `IF EXISTS` for idempotent migrations
52
- - D1 has a **5MB response size limit** -- always use LIMIT
53
-
54
- ## KV Namespace
55
-
56
- ### Read-Through Caching Pattern
57
-
58
- ```rust
59
- async fn get_user_cached(kv: &KvStore, db: &D1Database, user_id: &str) -> Result<User> {
60
- let cache_key = format!("user:{}", user_id);
61
-
62
- // Try cache first
63
- if let Some(cached) = kv.get(&cache_key).text().await? {
64
- if let Ok(user) = serde_json::from_str::<User>(&cached) {
65
- return Ok(user);
66
- }
67
- }
68
-
69
- // Cache miss -- query DB, then populate cache with TTL
70
- let user = /* query D1 */;
71
- kv.put(&cache_key, &json)?.expiration_ttl(3600).execute().await?;
72
- Ok(user)
73
- }
74
- ```
75
-
76
- ### TTL Strategies
77
-
78
- | Data Type | TTL | Rationale |
79
- |---|---|---|
80
- | User profile | 1 hour | Changes infrequently |
81
- | Session data | 24 hours | Matches session lifetime |
82
- | Config/settings | 5 minutes | Needs faster propagation |
83
- | Public listings | 15 minutes | Balances freshness and load |
84
-
85
- ### Key Naming Conventions
86
-
87
- Use colon-separated hierarchical keys:
88
- ```
89
- user:{userId}
90
- user:{userId}:profile
91
- event:{eventId}
92
- event:{eventId}:members
93
- session:{sessionToken}
94
- cache:feed:{userId}:page:{pageNum}
95
- ```
96
-
97
- ### Cache Invalidation
98
-
99
- Invalidate on write operations by deleting the corresponding cache key after updating D1.
100
-
101
- ## Hyperdrive (External Database Acceleration)
102
-
103
- Hyperdrive accelerates connections to external PostgreSQL/MySQL databases from Workers by maintaining regional connection pools close to your database, eliminating per-request TCP/TLS handshake overhead.
104
-
105
- ### Setup
106
-
107
- ```toml
108
- # wrangler.toml
109
- [[hyperdrive]]
110
- binding = "HYPERDRIVE"
111
- id = "<hyperdrive-config-id>"
112
- ```
113
-
114
- ```typescript
115
- // Access the connection string in your Worker
116
- const sql = env.HYPERDRIVE.connectionString;
117
- // Pass to your Postgres/MySQL client (e.g., node-postgres, drizzle)
118
- ```
119
-
120
- ### When to Use
121
-
122
- | Scenario | Use |
123
- |---|---|
124
- | Simple data, edge-native | **D1** (serverless SQLite) |
125
- | Read-heavy caching layer | **KV** (eventually consistent) |
126
- | Full relational DB (Postgres/MySQL) | **Hyperdrive** + external DB |
127
-
128
- ### Key Points
129
-
130
- - Always use Hyperdrive when connecting to external databases from Workers
131
- - Caches common read queries automatically, reducing origin load
132
- - Connection pool size is configurable per Hyperdrive config (minimum 5 connections)
133
- - Understands read vs write queries for intelligent caching
134
-
135
- ## Anti-patterns
136
-
137
- | Anti-pattern | Why it's wrong |
138
- |---|---|
139
- | **Unbounded queries** | Always use LIMIT. D1 has a 5MB response limit. |
140
- | **Missing indexes** | D1 is SQLite; without indexes, every query is a full table scan. |
141
- | **KV as primary data store** | KV is eventually consistent with no query capability. Use D1 for primary data. |
142
- | **Ignoring batch operations** | Each D1 call is a network round trip. Batch for performance. |
143
- | **KV cache without TTL** | Stale data without TTL persists indefinitely. |
144
- | **Not testing migrations locally** | Always apply with `--local` before `--remote`. |
@@ -1,25 +0,0 @@
1
- # ESNext Idioms
2
-
3
- > Modern ECMAScript runtime idioms — Temporal, iterator helpers, `using`, structuredClone.
4
-
5
- **Type:** Stack Skill (requires `esnext` stack)
6
- **Source:** [`stacks/esnext/skills/ct-esnext-idioms/SKILL.md`](../../stacks/esnext/skills/ct-esnext-idioms/SKILL.md)
7
- **Detected by:** tsconfig `target`/`module`/`lib` = `ESNext`/`ES2022+`, package.json `"type":"module"`, `engines.node >= 20`, or a `.mjs` file
8
- **File Extensions:** `.mjs`, `.cjs`
9
-
10
- ## Overview
11
-
12
- Guidance for preferring current ECMAScript **runtime features** over legacy patterns and hand-rolled polyfills. It is availability-aware: each newer feature notes the runtime/polyfill it needs, so the recommendation never generates code the target can't run. Types are covered by `ct-typescript-conventions`; general code structure by `ct-code-style`.
13
-
14
- ## Topics
15
-
16
- - **Dates → `Temporal`** — immutable, timezone-correct calendar/clock math (polyfill until baseline).
17
- - **Resource management → `using` / `await using`** — deterministic cleanup via `Symbol.dispose`/`asyncDispose`.
18
- - **Iterator helpers** — lazy `map`/`filter`/`take`, `Iterator.concat`, `Array.fromAsync`.
19
- - **Clone & group** — `structuredClone`, `Object.groupBy` / `Map.groupBy`.
20
- - **Async ergonomics** — `Promise.withResolvers`, `AbortSignal.timeout`/`any`, top-level await.
21
- - **Immutable array ops** — `at`, `findLast`, `toSorted`/`toReversed`/`with`.
22
- - **Errors** — `Error({ cause })`, `AggregateError`.
23
- - **ESM hygiene** — `import.meta.dirname` over `__dirname`, dynamic `import()`.
24
-
25
- See the [skill source](../../stacks/esnext/skills/ct-esnext-idioms/SKILL.md) for the full patterns, availability notes, and anti-patterns table.
@@ -1,141 +0,0 @@
1
- # Typesafe-i18n Patterns
2
-
3
- > Typesafe-i18n internationalization patterns for SolidJS.
4
-
5
- **Type:** Stack Skill (requires `i18n-typesafe` stack)
6
- **Source:** [`stacks/i18n-typesafe/skills/ct-i18n-typesafe/SKILL.md`](../stacks/i18n-typesafe/skills/ct-i18n-typesafe/SKILL.md)
7
- **Directory Mappings:** `src/locales/`, `src/i18n/`
8
-
9
- ## Overview
10
-
11
- typesafe-i18n provides fully type-safe translations with autocompletion. Translation keys are checked at compile time -- missing or misspelled keys cause TypeScript errors.
12
-
13
- ## Setup
14
-
15
- ### Directory Structure
16
-
17
- ```text
18
- src/i18n/
19
- i18n-types.ts # Auto-generated types
20
- i18n-util.ts # Auto-generated utilities
21
- i18n-solid.tsx # SolidJS adapter
22
- en/index.ts # Base locale (source of truth)
23
- fr/index.ts # French translations
24
- ```
25
-
26
- ### Configuration (`.typesafe-i18n.json`)
27
-
28
- ```json
29
- {
30
- "baseLocale": "en",
31
- "adapter": "solid",
32
- "outputPath": "src/i18n/{locale}/"
33
- }
34
- ```
35
-
36
- ### Run the Generator
37
-
38
- ```bash
39
- npx typesafe-i18n # Watches base locale and regenerates types
40
- ```
41
-
42
- ## Base Locale
43
-
44
- The base locale is the source of truth. All types are derived from it.
45
-
46
- ```ts
47
- // src/i18n/en/index.ts
48
- const en = {
49
- common: {
50
- welcome: 'Welcome, {name:string}!',
51
- itemCount: '{count:number} {{item|items}}',
52
- save: 'Save',
53
- },
54
- auth: {
55
- signIn: 'Sign in',
56
- signOut: 'Sign out',
57
- },
58
- } satisfies BaseTranslation;
59
- ```
60
-
61
- ## Adding Translations
62
-
63
- Other locales use the `Translation` type derived from the base locale. Missing keys or wrong parameter types cause compile errors.
64
-
65
- ```ts
66
- // src/i18n/fr/index.ts
67
- const fr = {
68
- common: {
69
- welcome: 'Bienvenue, {name} !',
70
- itemCount: '{count} {{article|articles}}',
71
- save: 'Enregistrer',
72
- },
73
- auth: {
74
- signIn: 'Se connecter',
75
- signOut: 'Se deconnecter',
76
- },
77
- } satisfies Translation;
78
- ```
79
-
80
- ## Usage in Components
81
-
82
- ```tsx
83
- const { LL } = useI18nContext();
84
-
85
- return (
86
- <div>
87
- <h1>{LL().events.title()}</h1>
88
- <span>{LL().events.attendees({ count: event.memberCount })}</span>
89
- </div>
90
- );
91
- ```
92
-
93
- ### Provider Setup
94
-
95
- ```tsx
96
- <I18nProvider locale="en">
97
- <Router />
98
- </I18nProvider>
99
- ```
100
-
101
- ## Parameterized Translations
102
-
103
- | Syntax | Example | Description |
104
- | ------------------------ | ---------------------------- | ------------------------ |
105
- | `{name:string}` | `'Hello {name:string}'` | Named string parameter |
106
- | `{count:number}` | `'{count:number} items'` | Named number parameter |
107
- | `{{singular\|plural}}` | `'{count} {{item\|items}}'` | Plural forms |
108
- | `{0:string}` | `'Click {0:string}'` | Positional parameter |
109
-
110
- ## Namespace Organization
111
-
112
- Organize translations by feature/domain. Keep namespaces flat (one level deep):
113
-
114
- ```ts
115
- const en = {
116
- common: { ... }, // Global
117
- auth: { ... }, // Authentication
118
- events: { ... }, // Events feature
119
- profile: { ... }, // User profile
120
- settings: { ... }, // Settings
121
- admin: { ... }, // Admin panel
122
- };
123
- ```
124
-
125
- ## Anti-patterns
126
-
127
- | Anti-pattern | Why it's wrong |
128
- | ------------------------------ | ------------------------------------------------------------------------------- |
129
- | **Hardcoded strings** | Every user-visible string must go through `LL()`. |
130
- | **Missing translations** | `Translation` type catches these at compile time -- run `tsc --noEmit` in CI. |
131
- | **Interpolating HTML** | Do not embed HTML in translation strings. Use component composition. |
132
- | **Dynamic key access** | `LL()[dynamicKey]()` bypasses type safety. Use conditional rendering. |
133
- | **Forgetting to load locale** | Call `loadLocale()` before rendering. |
134
- | **Over-splitting namespaces** | Too many small namespaces add overhead. Group by feature, not component. |
135
-
136
- ## Best Practices Reference
137
-
138
- | Topic | Guide |
139
- | ---------------------------------------- | ------------------------------------------------------------------------------- |
140
- | `satisfies` pattern used in translations | [The satisfies Operator](../best-practices/typescript/satisfies-operator.md) |
141
- | i18n provider and context pattern | [Context & Global State](../best-practices/solidjs/context-and-global-state.md) |
@@ -1,179 +0,0 @@
1
- # Playwright E2E Patterns
2
-
3
- > Playwright E2E testing with Page Objects, fixtures, auth, network mocking, and CI/CD.
4
-
5
- **Type:** Stack Skill (requires `playwright` stack)
6
- **Source:** [`stacks/playwright/skills/ct-playwright-patterns/SKILL.md`](../../stacks/playwright/skills/ct-playwright-patterns/SKILL.md)
7
- **Directory Mappings:** `e2e/`, `playwright.config.ts`
8
- **File Extensions:** `.spec.ts`
9
-
10
- ## Overview
11
-
12
- E2E tests sit at the top of the testing pyramid. They exercise full user journeys in real browsers. Write fewer, make each one count.
13
-
14
- ## Project Structure
15
-
16
- ```
17
- e2e/
18
- fixtures/ # Custom Playwright fixtures
19
- index.ts # Extended test with page objects
20
- pages/ # Page Object classes
21
- components/ # Component Object classes (reusable fragments)
22
- specs/ # Test files organized by feature
23
- helpers/ # Utilities (data factories, API helpers)
24
- .auth/ # storageState files (gitignored)
25
- ```
26
-
27
- ## Page Object Model + Fixtures
28
-
29
- Combine Page Objects with custom fixtures. Tests receive pre-built page objects:
30
-
31
- ```typescript
32
- // e2e/pages/LoginPage.ts
33
- export class LoginPage {
34
- private readonly emailInput: Locator;
35
- private readonly submitButton: Locator;
36
-
37
- constructor(private readonly page: Page) {
38
- this.emailInput = page.getByLabel("Email");
39
- this.submitButton = page.getByRole("button", { name: "Sign in" });
40
- }
41
-
42
- async goto() { await this.page.goto("/login"); }
43
- async login(email: string, password: string) {
44
- await this.emailInput.fill(email);
45
- await this.submitButton.click();
46
- }
47
- }
48
-
49
- // e2e/fixtures/index.ts
50
- export const test = base.extend<Fixtures>({
51
- loginPage: async ({ page }, use) => { await use(new LoginPage(page)); },
52
- });
53
- ```
54
-
55
- Fixtures are lazy (created on demand) and composable (can depend on each other).
56
-
57
- ## Authentication
58
-
59
- Use project dependencies with `storageState` to authenticate once and reuse:
60
-
61
- ```typescript
62
- // e2e/auth.setup.ts
63
- setup("authenticate", async ({ page }) => {
64
- await page.goto("/login");
65
- await page.getByLabel("Email").fill("test@example.com");
66
- await page.getByRole("button", { name: "Sign in" }).click();
67
- await page.waitForURL("/dashboard");
68
- await page.context().storageState({ path: "e2e/.auth/user.json" });
69
- });
70
- ```
71
-
72
- Config: `dependencies: ["setup"]` + `storageState: "e2e/.auth/user.json"`. Add `e2e/.auth/` to `.gitignore`.
73
-
74
- For multi-role testing, create separate setup files per role.
75
-
76
- ## Locator Priority
77
-
78
- 1. `getByRole()` -- accessibility semantics, survives refactors
79
- 2. `getByLabel()` -- form elements
80
- 3. `getByPlaceholder()` -- input hints
81
- 4. `getByText()` -- visible text
82
- 5. `getByTestId()` -- stable but less semantic
83
- 6. CSS/XPath -- **avoid**
84
-
85
- ## Web-First Assertions
86
-
87
- Always use auto-retrying assertions:
88
-
89
- ```typescript
90
- // GOOD
91
- await expect(page.getByRole("alert")).toBeVisible();
92
- await expect(page).toHaveURL("/dashboard");
93
-
94
- // BAD -- checks once, no retry
95
- expect(await page.getByRole("alert").isVisible()).toBeTruthy();
96
- ```
97
-
98
- Key assertions: `toBeVisible()`, `toBeEnabled()`, `toHaveText()`, `toHaveURL()`, `toHaveCount()`, `toHaveScreenshot()`, `toMatchAriaSnapshot()`.
99
-
100
- ## Network Mocking
101
-
102
- ```typescript
103
- // Mock API
104
- await page.route("**/api/users", async (route) => {
105
- await route.fulfill({ status: 200, contentType: "application/json", body: JSON.stringify([{ name: "Alice" }]) });
106
- });
107
-
108
- // Block analytics
109
- await page.route("**/analytics/**", (route) => route.abort());
110
- ```
111
-
112
- Mock ~80% of API calls for speed; keep ~20% hitting real endpoints. Always call `route.continue()`, `route.fulfill()`, or `route.abort()`. Use `page.unrouteAll()` in cleanup.
113
-
114
- ## WebSocket Mocking (v1.53+)
115
-
116
- ```typescript
117
- await page.routeWebSocket("wss://example.com/ws", (ws) => {
118
- ws.onMessage((message) => { ws.send("mocked response"); });
119
- });
120
- ```
121
-
122
- ## Accessibility Testing
123
-
124
- ```typescript
125
- import AxeBuilder from "@axe-core/playwright";
126
-
127
- test("WCAG 2.1 AA", async ({ page }) => {
128
- await page.goto("/");
129
- const results = await new AxeBuilder({ page }).withTags(["wcag2a", "wcag2aa", "wcag21aa"]).analyze();
130
- expect(results.violations).toEqual([]);
131
- });
132
- ```
133
-
134
- Aria snapshots (v1.52+): `await expect(nav).toMatchAriaSnapshot(...)`.
135
-
136
- ## Parallelization
137
-
138
- - File-level parallel (default) -- start here
139
- - In-file: `test.describe.configure({ mode: "parallel" })`
140
- - Full: `fullyParallel: true` in config
141
-
142
- Each worker gets its own `BrowserContext` (isolated cookies/storage). Use `mode: "serial"` sparingly.
143
-
144
- ## CI/CD
145
-
146
- - Use official Playwright Docker image (`mcr.microsoft.com/playwright:v1.58.0-noble`)
147
- - Upload reports + traces as artifacts
148
- - Use `--shard` for suites exceeding 5 minutes
149
- - `retries: process.env.CI ? 2 : 0`
150
- - Traces: `trace: "on-first-retry"`
151
-
152
- ## Flaky Test Prevention
153
-
154
- | Cause | Fix |
155
- | ---------------------- | ------------------------------------------------ |
156
- | `waitForTimeout()` | Web-first assertions or `waitForResponse` |
157
- | Brittle selectors | `getByRole`, `getByLabel`, `getByTestId` |
158
- | Shared state | Fresh `BrowserContext` per test |
159
- | External API flakiness | Mock with `page.route()` |
160
- | Animation timing | `--force-prefers-reduced-motion` |
161
- | Missing `await` | #1 source of false-passing tests. Lint for it. |
162
-
163
- ## Anti-Patterns
164
-
165
- 1. **Using `waitForTimeout()`** -- Use web-first assertions or explicit wait conditions.
166
- 2. **Fragile CSS/XPath selectors** -- Use role-based and semantic locators.
167
- 3. **Using `{ force: true }`** -- Masks real actionability problems.
168
- 4. **Missing `await` on assertions** -- #1 source of false-passing tests.
169
- 5. **Tests without assertions** -- A test that clicks without asserting is just a script.
170
- 6. **Using Playwright for unit tests** -- Use Vitest for pure logic.
171
- 7. **Committing raw codegen output** -- Always refactor into Page Objects.
172
- 8. **Not cleaning up route handlers** -- Use `page.unrouteAll()`.
173
-
174
- ## See Also
175
-
176
- - [ct-testing-patterns](../skills/testing-patterns.md) -- Framework-agnostic TDD practices
177
- - [Playwright E2E Best Practices](../best-practices/testing/playwright-e2e.md) -- SolidJS-specific E2E patterns
178
- - [ct-vite-vitest-patterns](vite-vitest-patterns.md) -- Unit testing layer
179
- - [ct-storybook-patterns](storybook-patterns.md) -- Interaction testing layer