@girardmedia/bootspring 3.3.2 → 3.4.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 (171) hide show
  1. package/assets/agents/accessibility-auditor.md +39 -0
  2. package/assets/agents/api-designer.md +40 -0
  3. package/assets/agents/auth-implementer.md +64 -0
  4. package/assets/agents/bug-hunter.md +42 -0
  5. package/assets/agents/bundle-analyzer.md +40 -0
  6. package/assets/agents/cache-optimizer.md +55 -0
  7. package/assets/agents/changelog-writer.md +55 -0
  8. package/assets/agents/ci-cd-builder.md +40 -0
  9. package/assets/agents/code-explainer.md +39 -0
  10. package/assets/agents/code-reviewer.md +39 -0
  11. package/assets/agents/cost-optimizer.md +57 -0
  12. package/assets/agents/cron-scheduler.md +51 -0
  13. package/assets/agents/data-seeder.md +56 -0
  14. package/assets/agents/database-architect.md +40 -0
  15. package/assets/agents/dependency-updater.md +40 -0
  16. package/assets/agents/deploy-checker.md +40 -0
  17. package/assets/agents/docker-optimizer.md +40 -0
  18. package/assets/agents/documentation-writer.md +40 -0
  19. package/assets/agents/email-builder.md +55 -0
  20. package/assets/agents/env-setup.md +40 -0
  21. package/assets/agents/error-handler.md +40 -0
  22. package/assets/agents/eslint-fixer.md +46 -0
  23. package/assets/agents/feature-flagger.md +69 -0
  24. package/assets/agents/git-detective.md +39 -0
  25. package/assets/agents/graphql-builder.md +60 -0
  26. package/assets/agents/incident-responder.md +59 -0
  27. package/assets/agents/log-analyzer.md +39 -0
  28. package/assets/agents/migration-planner.md +41 -0
  29. package/assets/agents/monorepo-navigator.md +39 -0
  30. package/assets/agents/nextjs-expert.md +57 -0
  31. package/assets/agents/notification-builder.md +56 -0
  32. package/assets/agents/onboarding-guide.md +39 -0
  33. package/assets/agents/performance-profiler.md +40 -0
  34. package/assets/agents/prisma-expert.md +57 -0
  35. package/assets/agents/rate-limiter.md +58 -0
  36. package/assets/agents/react-expert.md +58 -0
  37. package/assets/agents/refactorer.md +42 -0
  38. package/assets/agents/regex-builder.md +46 -0
  39. package/assets/agents/release-manager.md +40 -0
  40. package/assets/agents/s3-manager.md +58 -0
  41. package/assets/agents/schema-validator.md +40 -0
  42. package/assets/agents/search-builder.md +62 -0
  43. package/assets/agents/security-auditor.md +39 -0
  44. package/assets/agents/sitemap-generator.md +53 -0
  45. package/assets/agents/stripe-integrator.md +59 -0
  46. package/assets/agents/tailwind-expert.md +55 -0
  47. package/assets/agents/tech-debt-tracker.md +39 -0
  48. package/assets/agents/test-writer.md +42 -0
  49. package/assets/agents/type-fixer.md +45 -0
  50. package/assets/agents/webhook-builder.md +54 -0
  51. package/assets/rules/cpp.md +53 -0
  52. package/assets/rules/css.md +52 -0
  53. package/assets/rules/go.md +50 -0
  54. package/assets/rules/html.md +52 -0
  55. package/assets/rules/java.md +51 -0
  56. package/assets/rules/kotlin.md +50 -0
  57. package/assets/rules/php.md +51 -0
  58. package/assets/rules/python.md +51 -0
  59. package/assets/rules/ruby.md +51 -0
  60. package/assets/rules/rust.md +49 -0
  61. package/assets/rules/shell.md +52 -0
  62. package/assets/rules/sql.md +49 -0
  63. package/assets/rules/swift.md +50 -0
  64. package/assets/rules/typescript.md +52 -0
  65. package/assets/rules/yaml-json.md +51 -0
  66. package/assets/skills/accessibility.md +210 -0
  67. package/assets/skills/agent-patterns.md +387 -0
  68. package/assets/skills/ai-integration.md +263 -0
  69. package/assets/skills/animation-patterns.md +224 -0
  70. package/assets/skills/api-design.md +218 -0
  71. package/assets/skills/api-gateway.md +341 -0
  72. package/assets/skills/api-versioning.md +226 -0
  73. package/assets/skills/astro-patterns.md +233 -0
  74. package/assets/skills/auth-patterns.md +248 -0
  75. package/assets/skills/aws-patterns.md +171 -0
  76. package/assets/skills/background-jobs.md +162 -0
  77. package/assets/skills/browser-extensions.md +309 -0
  78. package/assets/skills/caching-patterns.md +253 -0
  79. package/assets/skills/ci-cd.md +251 -0
  80. package/assets/skills/cli-development.md +296 -0
  81. package/assets/skills/code-review.md +185 -0
  82. package/assets/skills/cron-patterns.md +327 -0
  83. package/assets/skills/data-fetching.md +231 -0
  84. package/assets/skills/database-migrations.md +346 -0
  85. package/assets/skills/database-patterns.md +219 -0
  86. package/assets/skills/debugging.md +281 -0
  87. package/assets/skills/design-system.md +289 -0
  88. package/assets/skills/django-patterns.md +182 -0
  89. package/assets/skills/docker-patterns.md +235 -0
  90. package/assets/skills/e2e-testing.md +287 -0
  91. package/assets/skills/edge-computing.md +268 -0
  92. package/assets/skills/electron-patterns.md +266 -0
  93. package/assets/skills/email-templates.md +206 -0
  94. package/assets/skills/error-handling.md +265 -0
  95. package/assets/skills/event-driven.md +232 -0
  96. package/assets/skills/express-patterns.md +239 -0
  97. package/assets/skills/fastapi-patterns.md +198 -0
  98. package/assets/skills/feature-flags.md +212 -0
  99. package/assets/skills/figma-to-code.md +298 -0
  100. package/assets/skills/file-upload.md +228 -0
  101. package/assets/skills/forms-patterns.md +264 -0
  102. package/assets/skills/gcp-patterns.md +189 -0
  103. package/assets/skills/git-workflow.md +187 -0
  104. package/assets/skills/golang-patterns.md +185 -0
  105. package/assets/skills/graphql-patterns.md +244 -0
  106. package/assets/skills/i18n-patterns.md +172 -0
  107. package/assets/skills/image-processing.md +350 -0
  108. package/assets/skills/java-springboot.md +226 -0
  109. package/assets/skills/kotlin-patterns.md +207 -0
  110. package/assets/skills/kubernetes-patterns.md +326 -0
  111. package/assets/skills/laravel-patterns.md +261 -0
  112. package/assets/skills/llm-fine-tuning.md +335 -0
  113. package/assets/skills/load-testing.md +303 -0
  114. package/assets/skills/logging-observability.md +228 -0
  115. package/assets/skills/markdown-processing.md +318 -0
  116. package/assets/skills/mcp-server-patterns.md +292 -0
  117. package/assets/skills/microservices.md +272 -0
  118. package/assets/skills/migration-patterns.md +239 -0
  119. package/assets/skills/mongodb-patterns.md +189 -0
  120. package/assets/skills/monorepo-patterns.md +287 -0
  121. package/assets/skills/nextjs-app-router.md +237 -0
  122. package/assets/skills/notification-patterns.md +348 -0
  123. package/assets/skills/oauth-patterns.md +246 -0
  124. package/assets/skills/payment-integration.md +222 -0
  125. package/assets/skills/pdf-generation.md +307 -0
  126. package/assets/skills/performance-optimization.md +277 -0
  127. package/assets/skills/php-patterns.md +210 -0
  128. package/assets/skills/prisma-patterns.md +241 -0
  129. package/assets/skills/prompt-engineering.md +193 -0
  130. package/assets/skills/pwa-patterns.md +247 -0
  131. package/assets/skills/python-patterns.md +158 -0
  132. package/assets/skills/python-testing.md +172 -0
  133. package/assets/skills/queue-patterns.md +295 -0
  134. package/assets/skills/rag-patterns.md +159 -0
  135. package/assets/skills/rate-limiting.md +319 -0
  136. package/assets/skills/react-components.md +201 -0
  137. package/assets/skills/react-native-patterns.md +299 -0
  138. package/assets/skills/real-time-patterns.md +181 -0
  139. package/assets/skills/redis-patterns.md +188 -0
  140. package/assets/skills/refactoring.md +218 -0
  141. package/assets/skills/regex-patterns.md +191 -0
  142. package/assets/skills/remix-patterns.md +262 -0
  143. package/assets/skills/responsive-design.md +199 -0
  144. package/assets/skills/ruby-rails-patterns.md +178 -0
  145. package/assets/skills/rust-patterns.md +211 -0
  146. package/assets/skills/search-patterns.md +227 -0
  147. package/assets/skills/security-hardening.md +237 -0
  148. package/assets/skills/seo-patterns.md +179 -0
  149. package/assets/skills/serverless-patterns.md +223 -0
  150. package/assets/skills/sql-optimization.md +154 -0
  151. package/assets/skills/state-management.md +254 -0
  152. package/assets/skills/storybook-patterns.md +330 -0
  153. package/assets/skills/svelte-patterns.md +258 -0
  154. package/assets/skills/swift-patterns.md +227 -0
  155. package/assets/skills/tailwind-patterns.md +272 -0
  156. package/assets/skills/tdd-workflow.md +199 -0
  157. package/assets/skills/terraform-patterns.md +270 -0
  158. package/assets/skills/testing-react.md +240 -0
  159. package/assets/skills/testing-vitest.md +232 -0
  160. package/assets/skills/typescript-strict.md +159 -0
  161. package/assets/skills/video-processing.md +340 -0
  162. package/assets/skills/vue-patterns.md +247 -0
  163. package/assets/skills/web-workers.md +327 -0
  164. package/assets/skills/webhooks-patterns.md +283 -0
  165. package/assets/skills/websocket-patterns.md +306 -0
  166. package/dist/cli/index.js +941 -958
  167. package/dist/core/index.d.ts +341 -11
  168. package/dist/core.js +58 -95
  169. package/dist/mcp/index.d.ts +33 -1
  170. package/dist/mcp-server.js +177 -255
  171. package/package.json +4 -1
@@ -0,0 +1,185 @@
1
+ ---
2
+ name: golang-patterns
3
+ description: Go patterns for interfaces, error handling, goroutines, channels, context, testing, and project layout.
4
+ ---
5
+
6
+ # Go Patterns
7
+
8
+ ## When to Use
9
+
10
+ Apply these patterns when writing Go 1.21+ code. Use this skill for designing
11
+ interfaces, handling errors idiomatically, managing concurrency with goroutines
12
+ and channels, propagating cancellation with context, and structuring projects.
13
+
14
+ ## How It Works
15
+
16
+ ### Small Interfaces
17
+
18
+ Define interfaces where they are consumed, not where they are implemented. Keep
19
+ interfaces small (1-3 methods). Accept interfaces, return structs.
20
+
21
+ ```go
22
+ // In the consumer package, not the provider
23
+ type UserStore interface {
24
+ GetUser(ctx context.Context, id string) (*User, error)
25
+ }
26
+
27
+ type Handler struct {
28
+ store UserStore // accepts interface
29
+ }
30
+
31
+ func NewHandler(s UserStore) *Handler {
32
+ return &Handler{store: s} // returns concrete struct
33
+ }
34
+ ```
35
+
36
+ ### Error Handling
37
+
38
+ Wrap errors with `fmt.Errorf("context: %w", err)` to build an error chain. Define
39
+ sentinel errors for expected conditions. Use `errors.Is` and `errors.As` to inspect.
40
+
41
+ ```go
42
+ var ErrNotFound = errors.New("not found")
43
+
44
+ func (s *Store) GetUser(ctx context.Context, id string) (*User, error) {
45
+ row := s.db.QueryRowContext(ctx, "SELECT ... WHERE id = $1", id)
46
+ var u User
47
+ if err := row.Scan(&u.ID, &u.Name); err != nil {
48
+ if errors.Is(err, sql.ErrNoRows) {
49
+ return nil, fmt.Errorf("get user %s: %w", id, ErrNotFound)
50
+ }
51
+ return nil, fmt.Errorf("get user %s: %w", id, err)
52
+ }
53
+ return &u, nil
54
+ }
55
+ ```
56
+
57
+ ### Goroutines and Channels
58
+
59
+ Always ensure goroutines can exit. Use `errgroup` for fan-out with error propagation.
60
+ Use buffered channels when the producer should not block.
61
+
62
+ ```go
63
+ import "golang.org/x/sync/errgroup"
64
+
65
+ func FetchAll(ctx context.Context, urls []string) ([]Response, error) {
66
+ g, ctx := errgroup.WithContext(ctx)
67
+ results := make([]Response, len(urls))
68
+
69
+ for i, url := range urls {
70
+ g.Go(func() error {
71
+ resp, err := fetch(ctx, url)
72
+ if err != nil {
73
+ return err
74
+ }
75
+ results[i] = resp
76
+ return nil
77
+ })
78
+ }
79
+
80
+ if err := g.Wait(); err != nil {
81
+ return nil, err
82
+ }
83
+ return results, nil
84
+ }
85
+ ```
86
+
87
+ ### Context
88
+
89
+ Pass `context.Context` as the first parameter of every function that does I/O or
90
+ may be long-running. Never store context in a struct. Use `context.WithTimeout`
91
+ for deadlines.
92
+
93
+ ```go
94
+ func (s *Service) Process(ctx context.Context, id string) error {
95
+ ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
96
+ defer cancel()
97
+
98
+ data, err := s.store.Fetch(ctx, id)
99
+ if err != nil {
100
+ return fmt.Errorf("process %s: %w", id, err)
101
+ }
102
+ return s.transform(ctx, data)
103
+ }
104
+ ```
105
+
106
+ ### Functional Options
107
+
108
+ Use the functional options pattern for configurable constructors instead of
109
+ large config structs with zero values.
110
+
111
+ ```go
112
+ type Server struct {
113
+ port int
114
+ timeout time.Duration
115
+ }
116
+
117
+ type Option func(*Server)
118
+
119
+ func WithPort(p int) Option { return func(s *Server) { s.port = p } }
120
+ func WithTimeout(d time.Duration) Option { return func(s *Server) { s.timeout = d } }
121
+
122
+ func NewServer(opts ...Option) *Server {
123
+ s := &Server{port: 8080, timeout: 30 * time.Second} // defaults
124
+ for _, opt := range opts {
125
+ opt(s)
126
+ }
127
+ return s
128
+ }
129
+ ```
130
+
131
+ ### Project Layout
132
+
133
+ ```
134
+ project/
135
+ cmd/
136
+ server/main.go # entrypoint
137
+ internal/
138
+ user/ # domain package
139
+ user.go
140
+ store.go
141
+ handler.go
142
+ handler_test.go
143
+ pkg/ # public library code (use sparingly)
144
+ go.mod
145
+ go.sum
146
+ ```
147
+
148
+ Keep `main.go` minimal: parse config, wire dependencies, start server. All logic
149
+ lives in `internal/` packages.
150
+
151
+ ## Examples
152
+
153
+ **Pattern: Table-driven config validation**
154
+ ```go
155
+ type rule struct {
156
+ name string
157
+ check func() error
158
+ }
159
+
160
+ func Validate(cfg Config) error {
161
+ rules := []rule{
162
+ {"port range", func() error { if cfg.Port < 1 || cfg.Port > 65535 { return errors.New("invalid port") }; return nil }},
163
+ {"timeout positive", func() error { if cfg.Timeout <= 0 { return errors.New("timeout must be positive") }; return nil }},
164
+ }
165
+ for _, r := range rules {
166
+ if err := r.check(); err != nil {
167
+ return fmt.Errorf("validate %s: %w", r.name, err)
168
+ }
169
+ }
170
+ return nil
171
+ }
172
+ ```
173
+
174
+ ## Checklist
175
+
176
+ - [ ] Interfaces defined at the consumer, 1-3 methods each
177
+ - [ ] Errors wrapped with `%w` and context about what failed
178
+ - [ ] Sentinel errors for expected conditions (`ErrNotFound`, `ErrConflict`)
179
+ - [ ] Every goroutine has a clear exit path (context cancellation, done channel)
180
+ - [ ] `errgroup` for fan-out with error propagation
181
+ - [ ] `context.Context` is the first parameter for I/O functions
182
+ - [ ] `defer cancel()` immediately after `context.WithTimeout`
183
+ - [ ] Functional options for constructors with optional configuration
184
+ - [ ] Business logic in `internal/`, entrypoints in `cmd/`
185
+ - [ ] `go vet` and `golangci-lint` pass with zero warnings
@@ -0,0 +1,244 @@
1
+ ---
2
+ name: graphql-patterns
3
+ description: GraphQL patterns for schema design, resolvers, dataloaders, subscriptions, federation, and error handling.
4
+ ---
5
+
6
+ # GraphQL Patterns
7
+
8
+ ## When to Use
9
+
10
+ Apply these patterns when designing and implementing GraphQL APIs. Use this skill
11
+ for structuring schemas, writing efficient resolvers, solving N+1 problems with
12
+ DataLoader, implementing real-time subscriptions, federating across services, and
13
+ handling errors gracefully.
14
+
15
+ ## How It Works
16
+
17
+ ### Schema Design
18
+
19
+ Design schemas around the client's data needs, not your database tables. Use
20
+ clear naming conventions. Prefer object types over primitives for extensibility.
21
+
22
+ ```graphql
23
+ type Query {
24
+ project(id: ID!): Project
25
+ projects(filter: ProjectFilter, first: Int = 20, after: String): ProjectConnection!
26
+ }
27
+
28
+ type Mutation {
29
+ createProject(input: CreateProjectInput!): CreateProjectPayload!
30
+ updateProject(id: ID!, input: UpdateProjectInput!): UpdateProjectPayload!
31
+ deleteProject(id: ID!): DeleteProjectPayload!
32
+ }
33
+
34
+ type Project {
35
+ id: ID!
36
+ name: String!
37
+ description: String
38
+ status: ProjectStatus!
39
+ owner: User!
40
+ tasks(first: Int = 10, after: String): TaskConnection!
41
+ createdAt: DateTime!
42
+ updatedAt: DateTime!
43
+ }
44
+
45
+ enum ProjectStatus {
46
+ ACTIVE
47
+ ARCHIVED
48
+ DRAFT
49
+ }
50
+
51
+ input CreateProjectInput {
52
+ name: String!
53
+ description: String
54
+ }
55
+
56
+ type CreateProjectPayload {
57
+ project: Project
58
+ errors: [UserError!]!
59
+ }
60
+
61
+ type UserError {
62
+ field: String
63
+ message: String!
64
+ code: ErrorCode!
65
+ }
66
+ ```
67
+
68
+ ### Relay-Style Pagination
69
+
70
+ Use cursor-based pagination with `Connection`, `Edge`, and `PageInfo` types. This
71
+ scales better than offset pagination and handles real-time insertions.
72
+
73
+ ```graphql
74
+ type ProjectConnection {
75
+ edges: [ProjectEdge!]!
76
+ pageInfo: PageInfo!
77
+ totalCount: Int!
78
+ }
79
+
80
+ type ProjectEdge {
81
+ node: Project!
82
+ cursor: String!
83
+ }
84
+
85
+ type PageInfo {
86
+ hasNextPage: Boolean!
87
+ hasPreviousPage: Boolean!
88
+ startCursor: String
89
+ endCursor: String
90
+ }
91
+ ```
92
+
93
+ ### Resolvers
94
+
95
+ Keep resolvers thin. Delegate to service/data layer. Return the right types
96
+ for your schema. Use context for auth and shared services.
97
+
98
+ ```typescript
99
+ const resolvers = {
100
+ Query: {
101
+ project: async (_, { id }, ctx) => {
102
+ const project = await ctx.services.project.findById(id)
103
+ if (!project) return null
104
+ if (!ctx.user.canView(project)) throw new ForbiddenError('Not authorized')
105
+ return project
106
+ },
107
+
108
+ projects: async (_, { filter, first, after }, ctx) => {
109
+ return ctx.services.project.paginate({ filter, first, after, userId: ctx.user.id })
110
+ },
111
+ },
112
+
113
+ Project: {
114
+ owner: (project, _, ctx) => {
115
+ return ctx.loaders.user.load(project.ownerId)
116
+ },
117
+
118
+ tasks: (project, { first, after }, ctx) => {
119
+ return ctx.services.task.paginateByProject(project.id, { first, after })
120
+ },
121
+ },
122
+
123
+ Mutation: {
124
+ createProject: async (_, { input }, ctx) => {
125
+ try {
126
+ const project = await ctx.services.project.create(input, ctx.user)
127
+ return { project, errors: [] }
128
+ } catch (err) {
129
+ return { project: null, errors: [{ field: 'name', message: err.message, code: 'INVALID_INPUT' }] }
130
+ }
131
+ },
132
+ },
133
+ }
134
+ ```
135
+
136
+ ### DataLoader for N+1 Prevention
137
+
138
+ DataLoader batches individual loads into a single query per tick. Create a new
139
+ DataLoader per request to avoid stale caches across users.
140
+
141
+ ```typescript
142
+ import DataLoader from 'dataloader'
143
+
144
+ function createLoaders(db: Database) {
145
+ return {
146
+ user: new DataLoader<string, User>(async (ids) => {
147
+ const users = await db.user.findMany({ where: { id: { in: [...ids] } } })
148
+ const map = new Map(users.map(u => [u.id, u]))
149
+ return ids.map(id => map.get(id) ?? new Error(`User ${id} not found`))
150
+ }),
151
+
152
+ projectsByOwner: new DataLoader<string, Project[]>(async (ownerIds) => {
153
+ const projects = await db.project.findMany({ where: { ownerId: { in: [...ownerIds] } } })
154
+ const grouped = new Map<string, Project[]>()
155
+ for (const p of projects) {
156
+ const list = grouped.get(p.ownerId) ?? []
157
+ list.push(p)
158
+ grouped.set(p.ownerId, list)
159
+ }
160
+ return ownerIds.map(id => grouped.get(id) ?? [])
161
+ }),
162
+ }
163
+ }
164
+
165
+ // In context factory (per request)
166
+ const context = ({ req }) => ({
167
+ user: authenticate(req),
168
+ loaders: createLoaders(db),
169
+ services,
170
+ })
171
+ ```
172
+
173
+ ### Subscriptions
174
+
175
+ Use subscriptions for real-time updates. Backed by a pub/sub system (Redis,
176
+ in-memory for dev). Filter events server-side.
177
+
178
+ ```typescript
179
+ const resolvers = {
180
+ Subscription: {
181
+ taskUpdated: {
182
+ subscribe: (_, { projectId }, ctx) => {
183
+ if (!ctx.user.canView(projectId)) throw new ForbiddenError()
184
+ return ctx.pubsub.asyncIterableIterator(`TASK_UPDATED:${projectId}`)
185
+ },
186
+ },
187
+ },
188
+ }
189
+
190
+ // In mutation resolver after updating a task:
191
+ await pubsub.publish(`TASK_UPDATED:${task.projectId}`, { taskUpdated: task })
192
+ ```
193
+
194
+ ### Federation
195
+
196
+ Split your graph across services. Each service owns its types. Use `@key` for
197
+ entity resolution and `extend` for cross-service references.
198
+
199
+ ```graphql
200
+ # Users service
201
+ type User @key(fields: "id") {
202
+ id: ID!
203
+ name: String!
204
+ email: String!
205
+ }
206
+
207
+ # Projects service
208
+ type User @key(fields: "id") {
209
+ id: ID!
210
+ projects: [Project!]!
211
+ }
212
+
213
+ type Project @key(fields: "id") {
214
+ id: ID!
215
+ name: String!
216
+ owner: User!
217
+ }
218
+ ```
219
+
220
+ ## Examples
221
+
222
+ **Pattern: Query complexity limiting**
223
+ ```typescript
224
+ import { createComplexityPlugin } from 'graphql-query-complexity'
225
+
226
+ const complexityPlugin = createComplexityPlugin({
227
+ maximumComplexity: 1000,
228
+ estimators: [fieldExtensionsEstimator(), simpleEstimator({ defaultComplexity: 1 })],
229
+ onComplete: (complexity) => { console.log('Query complexity:', complexity) },
230
+ })
231
+ ```
232
+
233
+ ## Checklist
234
+
235
+ - [ ] Schema designed around client needs, not database structure
236
+ - [ ] Input types for mutations; payload types with `errors` field for user errors
237
+ - [ ] Cursor-based pagination (`Connection`/`Edge`/`PageInfo`) for all lists
238
+ - [ ] DataLoader created per-request to batch and cache entity lookups
239
+ - [ ] Resolvers are thin: delegate to service/data layer
240
+ - [ ] Subscriptions filter events server-side (not client-side)
241
+ - [ ] Query depth and complexity limits enforced
242
+ - [ ] `@key` directives for federated entity resolution
243
+ - [ ] N+1 queries verified absent via query logging in dev
244
+ - [ ] Error codes in `UserError` type, not raw exception messages
@@ -0,0 +1,172 @@
1
+ ---
2
+ name: i18n-patterns
3
+ description: Internationalization patterns with i18next, ICU message format, RTL layout, Intl API, and locale detection.
4
+ ---
5
+
6
+ # Internationalization Patterns
7
+
8
+ ## When to Use
9
+ Apply these patterns when your app supports multiple languages or locales -- even if you start with just English. Retrofitting i18n is far more expensive than building it in from the start. This skill covers string externalization, ICU message formatting, plurals, right-to-left layout, date/number formatting with the Intl API, and automatic locale detection.
10
+
11
+ ## How It Works
12
+
13
+ ### i18next Setup -- Foundation
14
+
15
+ ```typescript
16
+ import i18next from "i18next";
17
+ import Backend from "i18next-http-backend";
18
+ import LanguageDetector from "i18next-browser-languagedetector";
19
+
20
+ await i18next
21
+ .use(Backend)
22
+ .use(LanguageDetector)
23
+ .init({
24
+ fallbackLng: "en",
25
+ supportedLngs: ["en", "es", "fr", "de", "ja", "ar"],
26
+ ns: ["common", "auth", "dashboard"],
27
+ defaultNS: "common",
28
+ interpolation: { escapeValue: false },
29
+ detection: {
30
+ order: ["querystring", "cookie", "navigator", "htmlTag"],
31
+ caches: ["cookie"],
32
+ },
33
+ backend: { loadPath: "/locales/{{lng}}/{{ns}}.json" },
34
+ });
35
+ ```
36
+
37
+ ### Translation Files -- Organize by Namespace
38
+
39
+ ```json
40
+ // locales/en/common.json
41
+ {
42
+ "nav": { "home": "Home", "settings": "Settings", "logout": "Log out" },
43
+ "actions": { "save": "Save", "cancel": "Cancel", "delete": "Delete" }
44
+ }
45
+
46
+ // locales/es/common.json
47
+ {
48
+ "nav": { "home": "Inicio", "settings": "Configuracion", "logout": "Cerrar sesion" },
49
+ "actions": { "save": "Guardar", "cancel": "Cancelar", "delete": "Eliminar" }
50
+ }
51
+ ```
52
+
53
+ ### ICU Message Format -- Plurals and Gender
54
+
55
+ ```json
56
+ {
57
+ "items_count": "{count, plural, =0 {No items} one {# item} other {# items}}",
58
+ "greeting": "{gender, select, male {He} female {She} other {They}} updated their profile."
59
+ }
60
+ ```
61
+
62
+ ```typescript
63
+ t("items_count", { count: 0 }); // "No items"
64
+ t("items_count", { count: 1 }); // "1 item"
65
+ t("items_count", { count: 42 }); // "42 items"
66
+ ```
67
+
68
+ | Language | Plural Categories |
69
+ |----------|------------------|
70
+ | English | one, other |
71
+ | French | one, many, other |
72
+ | Arabic | zero, one, two, few, many, other |
73
+ | Japanese | other (no plurals) |
74
+
75
+ Always use ICU plurals -- never `count === 1 ? "item" : "items"` in code.
76
+
77
+ ### React Integration
78
+
79
+ ```tsx
80
+ import { useTranslation, Trans } from "react-i18next";
81
+
82
+ function Dashboard() {
83
+ const { t } = useTranslation("dashboard");
84
+ return (
85
+ <div>
86
+ <h1>{t("title")}</h1>
87
+ <p>{t("items_count", { count: orders.length })}</p>
88
+ <Trans i18nKey="welcome_message" t={t}>
89
+ Welcome, <strong>{{ name: user.name }}</strong>!
90
+ Check your <a href="/inbox">inbox</a>.
91
+ </Trans>
92
+ </div>
93
+ );
94
+ }
95
+ ```
96
+
97
+ ### Date, Number, and Currency Formatting
98
+
99
+ ```typescript
100
+ // Dates
101
+ new Intl.DateTimeFormat("de-DE", { dateStyle: "long", timeStyle: "short" })
102
+ .format(new Date()); // "26. Mai 2026, 14:30"
103
+
104
+ // Numbers
105
+ new Intl.NumberFormat("en-US", { notation: "compact", compactDisplay: "short" })
106
+ .format(1_500_000); // "1.5M"
107
+
108
+ // Currency
109
+ new Intl.NumberFormat("ja-JP", { style: "currency", currency: "JPY" })
110
+ .format(1500); // "Y1,500"
111
+
112
+ // Relative time
113
+ new Intl.RelativeTimeFormat("en", { numeric: "auto" })
114
+ .format(-1, "day"); // "yesterday"
115
+ ```
116
+
117
+ ### RTL (Right-to-Left) Layout
118
+
119
+ ```css
120
+ /* Use logical properties -- not left/right */
121
+ .sidebar {
122
+ margin-inline-start: 1rem;
123
+ padding-inline-end: 2rem;
124
+ border-inline-start: 2px solid;
125
+ }
126
+ ```
127
+
128
+ ```tsx
129
+ function App() {
130
+ const { i18n } = useTranslation();
131
+ const dir = ["ar", "he", "fa"].includes(i18n.language) ? "rtl" : "ltr";
132
+ return (
133
+ <html lang={i18n.language} dir={dir}>
134
+ <body>{/* ... */}</body>
135
+ </html>
136
+ );
137
+ }
138
+ ```
139
+
140
+ ### Locale Detection Strategy
141
+
142
+ ```typescript
143
+ import { match } from "@formatjs/intl-localematcher";
144
+ import Negotiator from "negotiator";
145
+
146
+ // Priority: URL param > cookie > Accept-Language header > default
147
+ function detectLocale(req: Request): string {
148
+ const negotiator = new Negotiator(req);
149
+ const languages = negotiator.languages();
150
+ return match(languages, ["en", "es", "fr", "de", "ja", "ar"], "en");
151
+ }
152
+ ```
153
+
154
+ ## Examples
155
+
156
+ | Pattern | Purpose | Key API |
157
+ |---------|---------|---------|
158
+ | ICU plurals | Correct grammar per language | i18next-icu |
159
+ | Intl.DateTimeFormat | Locale-aware dates | Built-in |
160
+ | Intl.NumberFormat | Numbers and currency | Built-in |
161
+ | CSS logical properties | RTL-compatible layout | Native CSS |
162
+ | Accept-Language | Server-side locale detection | Negotiator |
163
+
164
+ ## Checklist
165
+ - [ ] All user-visible strings externalized into translation files
166
+ - [ ] Plurals use ICU format, not ternary operators
167
+ - [ ] Dates, numbers, and currencies formatted with `Intl` API
168
+ - [ ] CSS uses logical properties (`inline-start` / `inline-end`)
169
+ - [ ] `dir` attribute set on `<html>` based on language direction
170
+ - [ ] Locale detection follows priority: URL > cookie > header > default
171
+ - [ ] Translation keys are namespaced (`auth.login`, not `login`)
172
+ - [ ] No string concatenation for translated sentences