@soleri/core 9.13.0 → 9.14.4

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 (119) hide show
  1. package/dist/engine/bin/soleri-engine.js +7 -2
  2. package/dist/engine/bin/soleri-engine.js.map +1 -1
  3. package/dist/flows/types.d.ts +34 -30
  4. package/dist/flows/types.d.ts.map +1 -1
  5. package/dist/index.d.ts +3 -2
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +3 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/knowledge-packs/community/.gitkeep +0 -0
  10. package/dist/knowledge-packs/salvador/salvador-craft/soleri-pack.json +10 -0
  11. package/dist/knowledge-packs/salvador/salvador-craft/vault/accessibility.json +53 -0
  12. package/dist/knowledge-packs/salvador/salvador-craft/vault/design-tokens.json +26 -0
  13. package/dist/knowledge-packs/salvador/salvador-craft/vault/design.json +33 -0
  14. package/dist/knowledge-packs/salvador/salvador-craft/vault/styling.json +44 -0
  15. package/dist/knowledge-packs/salvador/salvador-craft/vault/ux-laws.json +36 -0
  16. package/dist/knowledge-packs/salvador/salvador-craft/vault/ux.json +36 -0
  17. package/dist/knowledge-packs/salvador/salvador-engineering/soleri-pack.json +10 -0
  18. package/dist/knowledge-packs/salvador/salvador-engineering/vault/architecture.json +143 -0
  19. package/dist/knowledge-packs/salvador/salvador-engineering/vault/commercial.json +16 -0
  20. package/dist/knowledge-packs/salvador/salvador-engineering/vault/communication.json +33 -0
  21. package/dist/knowledge-packs/salvador/salvador-engineering/vault/component.json +16 -0
  22. package/dist/knowledge-packs/salvador/salvador-engineering/vault/express.json +34 -0
  23. package/dist/knowledge-packs/salvador/salvador-engineering/vault/leadership.json +33 -0
  24. package/dist/knowledge-packs/salvador/salvador-engineering/vault/methodology.json +33 -0
  25. package/dist/knowledge-packs/salvador/salvador-engineering/vault/monorepo.json +33 -0
  26. package/dist/knowledge-packs/salvador/salvador-engineering/vault/other.json +73 -0
  27. package/dist/knowledge-packs/salvador/salvador-engineering/vault/performance.json +35 -0
  28. package/dist/knowledge-packs/salvador/salvador-engineering/vault/prisma.json +33 -0
  29. package/dist/knowledge-packs/salvador/salvador-engineering/vault/product-strategy.json +42 -0
  30. package/dist/knowledge-packs/salvador/salvador-engineering/vault/react.json +47 -0
  31. package/dist/knowledge-packs/salvador/salvador-engineering/vault/security.json +34 -0
  32. package/dist/knowledge-packs/salvador/salvador-engineering/vault/testing.json +33 -0
  33. package/dist/knowledge-packs/salvador/salvador-engineering/vault/tooling.json +85 -0
  34. package/dist/knowledge-packs/salvador/salvador-engineering/vault/typescript.json +34 -0
  35. package/dist/knowledge-packs/salvador/salvador-engineering/vault/workflow.json +46 -0
  36. package/dist/knowledge-packs/salvador/salvador-uipro/soleri-pack.json +10 -0
  37. package/dist/knowledge-packs/salvador/salvador-uipro/vault/design.json +2589 -0
  38. package/dist/knowledge-packs/starter/api-design/soleri-pack.json +9 -0
  39. package/dist/knowledge-packs/starter/api-design/vault/patterns.json +137 -0
  40. package/dist/knowledge-packs/starter/architecture/soleri-pack.json +10 -0
  41. package/dist/knowledge-packs/starter/architecture/vault/patterns.json +137 -0
  42. package/dist/knowledge-packs/starter/design/soleri-pack.json +10 -0
  43. package/dist/knowledge-packs/starter/design/vault/patterns.json +137 -0
  44. package/dist/knowledge-packs/starter/nodejs/soleri-pack.json +9 -0
  45. package/dist/knowledge-packs/starter/nodejs/vault/patterns.json +137 -0
  46. package/dist/knowledge-packs/starter/react/soleri-pack.json +9 -0
  47. package/dist/knowledge-packs/starter/react/vault/patterns.json +164 -0
  48. package/dist/knowledge-packs/starter/security/soleri-pack.json +10 -0
  49. package/dist/knowledge-packs/starter/security/vault/patterns.json +137 -0
  50. package/dist/knowledge-packs/starter/testing/soleri-pack.json +9 -0
  51. package/dist/knowledge-packs/starter/testing/vault/patterns.json +128 -0
  52. package/dist/knowledge-packs/starter/typescript/soleri-pack.json +9 -0
  53. package/dist/knowledge-packs/starter/typescript/vault/patterns.json +164 -0
  54. package/dist/packs/index.d.ts +1 -1
  55. package/dist/packs/index.d.ts.map +1 -1
  56. package/dist/packs/index.js +1 -1
  57. package/dist/packs/index.js.map +1 -1
  58. package/dist/packs/resolver.d.ts +6 -0
  59. package/dist/packs/resolver.d.ts.map +1 -1
  60. package/dist/packs/resolver.js +20 -1
  61. package/dist/packs/resolver.js.map +1 -1
  62. package/dist/runtime/admin-setup-ops.js +1 -1
  63. package/dist/runtime/admin-setup-ops.js.map +1 -1
  64. package/dist/runtime/capture-ops.d.ts.map +1 -1
  65. package/dist/runtime/capture-ops.js +2 -1
  66. package/dist/runtime/capture-ops.js.map +1 -1
  67. package/dist/runtime/intake-ops.d.ts.map +1 -1
  68. package/dist/runtime/intake-ops.js +5 -5
  69. package/dist/runtime/intake-ops.js.map +1 -1
  70. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  71. package/dist/runtime/orchestrate-ops.js +26 -2
  72. package/dist/runtime/orchestrate-ops.js.map +1 -1
  73. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  74. package/dist/runtime/planning-extra-ops.js +5 -7
  75. package/dist/runtime/planning-extra-ops.js.map +1 -1
  76. package/dist/runtime/playbook-ops.d.ts.map +1 -1
  77. package/dist/runtime/playbook-ops.js +2 -1
  78. package/dist/runtime/playbook-ops.js.map +1 -1
  79. package/dist/runtime/schema-helpers.d.ts +7 -0
  80. package/dist/runtime/schema-helpers.d.ts.map +1 -0
  81. package/dist/runtime/schema-helpers.js +21 -0
  82. package/dist/runtime/schema-helpers.js.map +1 -0
  83. package/dist/runtime/sync-ops.d.ts.map +1 -1
  84. package/dist/runtime/sync-ops.js +3 -4
  85. package/dist/runtime/sync-ops.js.map +1 -1
  86. package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
  87. package/dist/runtime/vault-extra-ops.js +5 -4
  88. package/dist/runtime/vault-extra-ops.js.map +1 -1
  89. package/dist/skills/sync-skills.d.ts +26 -7
  90. package/dist/skills/sync-skills.d.ts.map +1 -1
  91. package/dist/skills/sync-skills.js +132 -32
  92. package/dist/skills/sync-skills.js.map +1 -1
  93. package/dist/skills/validate-skill-docs.d.ts +24 -0
  94. package/dist/skills/validate-skill-docs.d.ts.map +1 -0
  95. package/dist/skills/validate-skill-docs.js +476 -0
  96. package/dist/skills/validate-skill-docs.js.map +1 -0
  97. package/package.json +2 -2
  98. package/src/__tests__/deviation-detection.test.ts +49 -0
  99. package/src/enforcement/adapters/claude-code.test.ts +9 -9
  100. package/src/engine/bin/soleri-engine.ts +7 -2
  101. package/src/flows/types.ts +4 -0
  102. package/src/index.ts +15 -2
  103. package/src/packs/index.ts +6 -1
  104. package/src/packs/resolver.ts +24 -1
  105. package/src/runtime/admin-setup-ops.test.ts +2 -0
  106. package/src/runtime/admin-setup-ops.ts +1 -1
  107. package/src/runtime/capture-ops.ts +2 -1
  108. package/src/runtime/intake-ops.ts +7 -7
  109. package/src/runtime/orchestrate-ops.ts +29 -2
  110. package/src/runtime/planning-extra-ops.ts +35 -37
  111. package/src/runtime/playbook-ops.ts +2 -1
  112. package/src/runtime/schema-helpers.test.ts +45 -0
  113. package/src/runtime/schema-helpers.ts +19 -0
  114. package/src/runtime/sync-ops.ts +8 -9
  115. package/src/runtime/vault-extra-ops.ts +5 -4
  116. package/src/skills/__tests__/sync-skills.test.ts +102 -29
  117. package/src/skills/__tests__/validate-skill-docs.test.ts +58 -0
  118. package/src/skills/sync-skills.ts +152 -32
  119. package/src/skills/validate-skill-docs.ts +562 -0
@@ -0,0 +1,9 @@
1
+ {
2
+ "id": "starter-api-design",
3
+ "name": "API Design Starter Pack",
4
+ "version": "1.0.0",
5
+ "description": "REST conventions, error responses, validation, versioning, auth patterns, pagination — essential API design patterns.",
6
+ "domains": ["api", "architecture"],
7
+ "tier": "default",
8
+ "vault": { "dir": "vault" }
9
+ }
@@ -0,0 +1,137 @@
1
+ [
2
+ {
3
+ "id": "api-001",
4
+ "type": "pattern",
5
+ "domain": "api",
6
+ "title": "Nouns for Resources, Verbs for Actions",
7
+ "description": "Use plural nouns for resource endpoints: /users, /orders, /products. Reserve verbs for non-CRUD actions on a resource: POST /users/:id/activate, POST /orders/:id/cancel. Let HTTP methods convey the operation — GET reads, POST creates, PUT replaces, PATCH updates, DELETE removes. Anti-example: GET /getUsers, POST /createOrder, DELETE /deleteUser/:id.",
8
+ "severity": "warning",
9
+ "tags": ["api", "rest", "naming"]
10
+ },
11
+ {
12
+ "id": "api-002",
13
+ "type": "pattern",
14
+ "domain": "api",
15
+ "title": "Consistent Error Response Shape (RFC 7807)",
16
+ "description": "Return errors in a uniform structure following RFC 7807 Problem Details. Every error response should include: type (URI identifying the error class), title (short human-readable summary), status (HTTP status code), detail (specific explanation for this occurrence), instance (URI identifying the specific request). Example:\n{\n \"type\": \"https://api.example.com/errors/insufficient-funds\",\n \"title\": \"Insufficient Funds\",\n \"status\": 422,\n \"detail\": \"Account balance is $10.00, but the transfer requires $25.00.\",\n \"instance\": \"/transfers/abc-123\"\n}\nThis makes error handling consistent across all clients — no guessing which field holds the message.",
17
+ "severity": "warning",
18
+ "tags": ["api", "error-handling", "rfc7807"]
19
+ },
20
+ {
21
+ "id": "api-003",
22
+ "type": "rule",
23
+ "domain": "api",
24
+ "title": "Validate Input at the Boundary",
25
+ "description": "Never trust client data. Validate every field at the API boundary before it reaches business logic or the database. Use schema validation (Zod, Joi, JSON Schema) to enforce types, ranges, formats, and required fields. Reject invalid input with a 400 response and a clear list of validation errors. Example response:\n{\n \"type\": \"https://api.example.com/errors/validation\",\n \"title\": \"Validation Error\",\n \"status\": 400,\n \"detail\": \"Request body failed validation.\",\n \"errors\": [\n { \"field\": \"email\", \"message\": \"Must be a valid email address\" },\n { \"field\": \"age\", \"message\": \"Must be between 0 and 150\" }\n ]\n}\nValidation at the boundary prevents injection, type confusion, and data corruption downstream.",
26
+ "severity": "critical",
27
+ "tags": ["api", "validation", "security"]
28
+ },
29
+ {
30
+ "id": "api-004",
31
+ "type": "pattern",
32
+ "domain": "api",
33
+ "title": "Use HTTP Status Codes Correctly",
34
+ "description": "Match the status code to the actual outcome. Common mappings: 200 OK — successful read or update. 201 Created — resource created (include Location header). 204 No Content — successful delete or update with no body. 400 Bad Request — malformed or invalid input. 401 Unauthorized — missing or invalid credentials. 403 Forbidden — valid credentials but insufficient permissions. 404 Not Found — resource does not exist. 409 Conflict — state conflict (duplicate email, outdated version). 422 Unprocessable Entity — syntactically valid but semantically wrong. 429 Too Many Requests — rate limit exceeded. Anti-pattern: returning 200 with { \"error\": true } in the body.",
35
+ "severity": "warning",
36
+ "tags": ["api", "rest", "http"]
37
+ },
38
+ {
39
+ "id": "api-005",
40
+ "type": "pattern",
41
+ "domain": "api",
42
+ "title": "Cursor-Based Pagination Over Offset",
43
+ "description": "Prefer cursor-based pagination over offset/limit. Offset pagination breaks when rows are inserted or deleted between pages, and performance degrades on large offsets (the database still scans skipped rows). Cursor pagination uses an opaque token (usually an encoded ID or timestamp) for stable, performant page traversal. Response shape:\n{\n \"data\": [...],\n \"pagination\": {\n \"next_cursor\": \"eyJpZCI6MTAwfQ==\",\n \"has_more\": true\n }\n}\nRequest: GET /users?cursor=eyJpZCI6MTAwfQ==&limit=25. Use offset only for simple admin UIs where users need to jump to arbitrary pages.",
44
+ "severity": "warning",
45
+ "tags": ["api", "pagination", "performance"]
46
+ },
47
+ {
48
+ "id": "api-006",
49
+ "type": "pattern",
50
+ "domain": "api",
51
+ "title": "Idempotency Keys for POST Operations",
52
+ "description": "Accept an Idempotency-Key header on POST endpoints to prevent duplicate operations from retries. The server stores the key and its response; if the same key is sent again, return the stored response without re-executing. This is critical for payment, order, and any state-changing endpoints where network retries could cause double-processing. Implementation: client generates a UUID, sends it as Idempotency-Key header. Server checks a key store (Redis/DB) — if found, return cached response; if not, process and store. Keys should expire after 24-48 hours.\nExample header: Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000",
53
+ "severity": "warning",
54
+ "tags": ["api", "idempotency", "reliability"]
55
+ },
56
+ {
57
+ "id": "api-007",
58
+ "type": "pattern",
59
+ "domain": "api",
60
+ "title": "Rate Limiting with Standard Headers",
61
+ "description": "Protect your API with rate limiting and communicate limits via standard headers. Include these headers on every response:\nRateLimit-Limit: 100 (max requests per window)\nRateLimit-Remaining: 42 (requests left in current window)\nRateLimit-Reset: 1620000000 (Unix timestamp when the window resets)\nWhen the limit is exceeded, return 429 Too Many Requests with a Retry-After header (seconds until the client can retry). Use sliding window or token bucket algorithms. Apply different limits per tier (free: 100/min, pro: 1000/min) and per endpoint (auth endpoints should have stricter limits).",
62
+ "severity": "warning",
63
+ "tags": ["api", "rate-limiting", "security"]
64
+ },
65
+ {
66
+ "id": "api-008",
67
+ "type": "pattern",
68
+ "domain": "api",
69
+ "title": "API Versioning via URL Prefix",
70
+ "description": "Version your API using a URL prefix: /v1/users, /v2/users. This is the most explicit and cacheable approach. Alternatives like Accept headers (Accept: application/vnd.api+json;version=2) are harder to test in browsers and less visible in logs. Rules: never break a published version — add fields, don't remove them. Deprecate old versions with a Sunset header and a deprecation notice in the response. Maintain at most 2 active versions. Document the migration path between versions.",
71
+ "severity": "warning",
72
+ "tags": ["api", "versioning", "architecture"]
73
+ },
74
+ {
75
+ "id": "api-009",
76
+ "type": "pattern",
77
+ "domain": "api",
78
+ "title": "HATEOAS Links for Discoverability",
79
+ "description": "Include hypermedia links in responses so clients can discover related actions without hardcoding URLs. Use a _links object with relation types. Example:\n{\n \"id\": \"usr_123\",\n \"name\": \"Jane\",\n \"_links\": {\n \"self\": { \"href\": \"/v1/users/usr_123\" },\n \"orders\": { \"href\": \"/v1/users/usr_123/orders\" },\n \"activate\": { \"href\": \"/v1/users/usr_123/activate\", \"method\": \"POST\" }\n }\n}\nThis reduces client-server coupling — clients follow links instead of constructing URLs. Especially valuable for public APIs with external consumers.",
80
+ "severity": "suggestion",
81
+ "tags": ["api", "rest", "hateoas", "discoverability"]
82
+ },
83
+ {
84
+ "id": "api-010",
85
+ "type": "pattern",
86
+ "domain": "api",
87
+ "title": "ETags and Conditional Requests",
88
+ "description": "Use ETags for caching and concurrency control. Return an ETag header with a hash of the response content. Clients send If-None-Match on GET requests — return 304 Not Modified if unchanged (saves bandwidth). For updates, clients send If-Match — return 412 Precondition Failed if the resource was modified since it was read (prevents lost updates). Example flow:\nGET /users/123 → 200, ETag: \"abc123\"\nGET /users/123, If-None-Match: \"abc123\" → 304 Not Modified\nPUT /users/123, If-Match: \"abc123\" → 200 (or 412 if stale)\nThis prevents both unnecessary data transfer and write conflicts.",
89
+ "severity": "suggestion",
90
+ "tags": ["api", "caching", "concurrency", "etag"]
91
+ },
92
+ {
93
+ "id": "api-011",
94
+ "type": "pattern",
95
+ "domain": "api",
96
+ "title": "Bulk Operations Over N+1 Calls",
97
+ "description": "Provide batch endpoints for operations that clients commonly perform on multiple resources. Instead of making N individual requests, clients send a single batch request. Example — bulk status update:\nPATCH /v1/orders/batch\n{\n \"operations\": [\n { \"id\": \"ord_1\", \"status\": \"shipped\" },\n { \"id\": \"ord_2\", \"status\": \"shipped\" }\n ]\n}\nReturn per-item results so partial failures are visible:\n{\n \"results\": [\n { \"id\": \"ord_1\", \"status\": \"success\" },\n { \"id\": \"ord_2\", \"status\": \"error\", \"error\": \"Order not found\" }\n ]\n}\nUse 207 Multi-Status when results are mixed. Set a reasonable max batch size (100-500 items).",
98
+ "severity": "suggestion",
99
+ "tags": ["api", "batch", "performance"]
100
+ },
101
+ {
102
+ "id": "api-012",
103
+ "type": "rule",
104
+ "domain": "api",
105
+ "title": "Short-Lived JWTs with Refresh Tokens",
106
+ "description": "Use short-lived access tokens (5-15 minutes) paired with long-lived refresh tokens (7-30 days). Access tokens are JWTs sent in the Authorization header — they are stateless and fast to validate. Refresh tokens are opaque, stored server-side, and used only to obtain new access tokens. Never store tokens in localStorage (XSS-vulnerable) — use httpOnly cookies for refresh tokens and in-memory storage for access tokens. Rotate refresh tokens on every use (one-time use). Revoke refresh tokens server-side on logout or security events.",
107
+ "severity": "critical",
108
+ "tags": ["api", "auth", "jwt", "security"]
109
+ },
110
+ {
111
+ "id": "api-013",
112
+ "type": "anti-pattern",
113
+ "domain": "api",
114
+ "title": "Exposing Internal IDs and Stack Traces",
115
+ "description": "Never expose auto-increment database IDs, internal service names, or stack traces in API responses. Sequential IDs leak information about your data volume and allow enumeration attacks. Stack traces reveal implementation details useful for attackers. Use UUIDs or prefixed opaque IDs (usr_abc123, ord_xyz789) for external identifiers. In production, log stack traces server-side but return only a generic error and a correlation ID to the client:\n{\n \"type\": \"https://api.example.com/errors/internal\",\n \"title\": \"Internal Server Error\",\n \"status\": 500,\n \"detail\": \"An unexpected error occurred.\",\n \"instance\": \"req_abc123\"\n}",
116
+ "severity": "critical",
117
+ "tags": ["api", "security", "error-handling"]
118
+ },
119
+ {
120
+ "id": "api-014",
121
+ "type": "anti-pattern",
122
+ "domain": "api",
123
+ "title": "God Endpoint That Does Everything",
124
+ "description": "Avoid single endpoints that accept an 'action' field to perform wildly different operations: POST /api { action: 'createUser' } and POST /api { action: 'deleteOrder' }. This makes routing opaque, documentation impossible, and per-endpoint auth/rate-limiting impractical. Each resource and operation should have its own endpoint with clear HTTP method semantics. If you need to orchestrate multiple operations, use a dedicated workflow endpoint (POST /v1/checkout) rather than a generic dispatch.",
125
+ "severity": "warning",
126
+ "tags": ["api", "rest", "architecture"]
127
+ },
128
+ {
129
+ "id": "api-015",
130
+ "type": "pattern",
131
+ "domain": "api",
132
+ "title": "Request Correlation IDs for Tracing",
133
+ "description": "Generate a unique correlation ID for every incoming request and propagate it through all downstream services. Accept a client-provided X-Request-ID header (validate it is a UUID), or generate one if missing. Include it in every log line, error response, and outgoing service call. This makes debugging distributed issues trivial — search by correlation ID to see the full request lifecycle. Return the ID in the response header so clients can reference it in bug reports:\nX-Request-ID: 550e8400-e29b-41d4-a716-446655440000",
134
+ "severity": "warning",
135
+ "tags": ["api", "observability", "tracing"]
136
+ }
137
+ ]
@@ -0,0 +1,10 @@
1
+ {
2
+ "id": "starter-architecture",
3
+ "name": "Architecture Starter Pack",
4
+ "version": "1.0.0",
5
+ "description": "Clean architecture, DDD patterns, API design, error handling — foundational architecture patterns and anti-patterns.",
6
+ "domains": ["architecture"],
7
+ "vault": {
8
+ "dir": "vault"
9
+ }
10
+ }
@@ -0,0 +1,137 @@
1
+ [
2
+ {
3
+ "id": "arch-001",
4
+ "type": "pattern",
5
+ "domain": "architecture",
6
+ "title": "Dependency Inversion via Interfaces",
7
+ "description": "High-level modules should not depend on low-level modules. Both should depend on abstractions. Define interfaces for external dependencies (databases, APIs, file systems) and inject implementations. This enables testing and swapping implementations without changing business logic.",
8
+ "severity": "warning",
9
+ "tags": ["solid", "dependency-injection", "clean-architecture"]
10
+ },
11
+ {
12
+ "id": "arch-002",
13
+ "type": "pattern",
14
+ "domain": "architecture",
15
+ "title": "Error Boundaries at Layer Edges",
16
+ "description": "Catch and translate errors at the boundary between architectural layers. Database errors become domain errors. HTTP errors become user-facing messages. Each layer speaks its own error language. Never let implementation details leak through error messages.",
17
+ "severity": "warning",
18
+ "tags": ["error-handling", "layers", "boundaries"]
19
+ },
20
+ {
21
+ "id": "arch-003",
22
+ "type": "pattern",
23
+ "domain": "architecture",
24
+ "title": "Command Query Separation",
25
+ "description": "Functions should either change state (command) or return data (query), never both. Commands return void or a simple status. Queries are side-effect free. This makes code predictable, testable, and easier to reason about.",
26
+ "severity": "suggestion",
27
+ "tags": ["cqs", "clean-code", "functional"]
28
+ },
29
+ {
30
+ "id": "arch-004",
31
+ "type": "pattern",
32
+ "domain": "architecture",
33
+ "title": "Idempotent Operations",
34
+ "description": "Design operations to be safely retryable. Use unique request IDs for deduplication. Make database writes idempotent with UPSERT or conditional INSERT. This is critical for distributed systems where network failures cause retries.",
35
+ "severity": "warning",
36
+ "tags": ["idempotency", "distributed-systems", "reliability"]
37
+ },
38
+ {
39
+ "id": "arch-005",
40
+ "type": "pattern",
41
+ "domain": "architecture",
42
+ "title": "Graceful Degradation",
43
+ "description": "When an external dependency fails, degrade gracefully instead of crashing. Return cached data, empty results, or reduced functionality. Log the failure for monitoring. The system should remain useful even when parts are down.",
44
+ "severity": "warning",
45
+ "tags": ["resilience", "fault-tolerance", "reliability"]
46
+ },
47
+ {
48
+ "id": "arch-006",
49
+ "type": "pattern",
50
+ "domain": "architecture",
51
+ "title": "Configuration as Data",
52
+ "description": "Express variation through configuration, not code branches. Feature flags, policy objects, and declarative config files are easier to audit, test, and change than if/else chains. Logic in config, not code.",
53
+ "severity": "suggestion",
54
+ "tags": ["configuration", "data-driven", "clean-code"]
55
+ },
56
+ {
57
+ "id": "arch-007",
58
+ "type": "pattern",
59
+ "domain": "architecture",
60
+ "title": "Reversible Migrations",
61
+ "description": "Every database migration must have a corresponding rollback. Test both up and down paths. Prefer additive changes (add column, add table) over destructive ones (drop column, rename). Make breaking schema changes in multiple steps: add new → migrate data → remove old.",
62
+ "severity": "warning",
63
+ "tags": ["database", "migrations", "schema"]
64
+ },
65
+ {
66
+ "id": "arch-008",
67
+ "type": "anti-pattern",
68
+ "domain": "architecture",
69
+ "title": "God Object",
70
+ "description": "Avoid classes or modules that know too much or do too much. A 2000-line service class with 30 methods violates single responsibility. Split by cohesion: group related data and behavior together. If a change to feature A requires modifying the same file as feature B, they should be separate modules.",
71
+ "severity": "warning",
72
+ "tags": ["solid", "cohesion", "refactoring"]
73
+ },
74
+ {
75
+ "id": "arch-009",
76
+ "type": "anti-pattern",
77
+ "domain": "architecture",
78
+ "title": "Premature Abstraction",
79
+ "description": "Don't abstract before you have three concrete examples of the same pattern. One-off helpers and single-use base classes add indirection without value. Wait for the pattern to emerge from real usage, then extract. Three similar lines are better than a premature abstraction.",
80
+ "severity": "suggestion",
81
+ "tags": ["abstraction", "yagni", "clean-code"]
82
+ },
83
+ {
84
+ "id": "arch-010",
85
+ "type": "anti-pattern",
86
+ "domain": "architecture",
87
+ "title": "Circular Dependencies",
88
+ "description": "When module A imports B and B imports A, you have a circular dependency. This signals tangled responsibilities and makes code hard to test and refactor. Fix by extracting shared types into a third module, or inverting the dependency with an interface.",
89
+ "severity": "warning",
90
+ "tags": ["dependencies", "coupling", "modules"]
91
+ },
92
+ {
93
+ "id": "arch-011",
94
+ "type": "pattern",
95
+ "domain": "architecture",
96
+ "title": "API Versioning Strategy",
97
+ "description": "Version APIs from day one using URL path (/v1/) or headers. Never break existing consumers. Deprecate old versions with sunset dates. New versions can change behavior; existing versions are frozen. Document breaking changes in changelogs.",
98
+ "severity": "warning",
99
+ "tags": ["api-design", "versioning", "backwards-compatibility"]
100
+ },
101
+ {
102
+ "id": "arch-012",
103
+ "type": "anti-pattern",
104
+ "domain": "architecture",
105
+ "title": "Shared Mutable State Without Synchronization",
106
+ "description": "Accessing shared mutable state from multiple concurrent contexts (threads, async operations, workers) without synchronization leads to race conditions. Use immutable data structures, message passing, or explicit synchronization primitives.",
107
+ "severity": "critical",
108
+ "tags": ["concurrency", "race-conditions", "state"]
109
+ },
110
+ {
111
+ "id": "arch-013",
112
+ "type": "rule",
113
+ "domain": "architecture",
114
+ "title": "Validate at System Boundaries Only",
115
+ "description": "Validate input at the edge of your system (HTTP handlers, CLI parsers, message consumers). Internal functions trust their callers. This avoids redundant validation scattered throughout the codebase and keeps business logic clean.",
116
+ "severity": "suggestion",
117
+ "tags": ["validation", "boundaries", "clean-code"]
118
+ },
119
+ {
120
+ "id": "arch-014",
121
+ "type": "pattern",
122
+ "domain": "architecture",
123
+ "title": "Repository Pattern for Data Access",
124
+ "description": "Encapsulate data access behind a repository interface. The repository exposes domain-oriented methods (findByEmail, listActive) instead of raw queries. This decouples business logic from storage implementation and makes testing trivial with in-memory implementations.",
125
+ "severity": "suggestion",
126
+ "tags": ["repository", "data-access", "clean-architecture"]
127
+ },
128
+ {
129
+ "id": "arch-015",
130
+ "type": "anti-pattern",
131
+ "domain": "architecture",
132
+ "title": "Feature Flags as Permanent Conditionals",
133
+ "description": "Feature flags are temporary. If a flag has been active for more than one release cycle, either remove the old path or make the new behavior permanent. Accumulated flags create combinatorial complexity that is impossible to test exhaustively.",
134
+ "severity": "suggestion",
135
+ "tags": ["feature-flags", "technical-debt", "complexity"]
136
+ }
137
+ ]
@@ -0,0 +1,10 @@
1
+ {
2
+ "id": "starter-design",
3
+ "name": "Frontend & Design Starter Pack",
4
+ "version": "1.0.0",
5
+ "description": "React patterns, accessibility, performance, component composition — essential frontend and UI design patterns.",
6
+ "domains": ["frontend", "accessibility"],
7
+ "vault": {
8
+ "dir": "vault"
9
+ }
10
+ }
@@ -0,0 +1,137 @@
1
+ [
2
+ {
3
+ "id": "fe-001",
4
+ "type": "pattern",
5
+ "domain": "frontend",
6
+ "title": "Composition Over Inheritance in Components",
7
+ "description": "Build components by composing smaller, focused pieces rather than extending base components. Use children, render props, or slots for flexibility. A Card component with CardHeader, CardBody, CardFooter slots is more reusable than a monolithic Card with 15 props.",
8
+ "severity": "warning",
9
+ "tags": ["react", "composition", "components"]
10
+ },
11
+ {
12
+ "id": "fe-002",
13
+ "type": "pattern",
14
+ "domain": "frontend",
15
+ "title": "Controlled Components for Forms",
16
+ "description": "Prefer controlled components (state-driven) over uncontrolled (DOM-driven) for form inputs. This gives you validation on every keystroke, conditional rendering, and predictable state. Use uncontrolled only for simple, isolated forms where performance is critical.",
17
+ "severity": "suggestion",
18
+ "tags": ["react", "forms", "state"]
19
+ },
20
+ {
21
+ "id": "fe-003",
22
+ "type": "pattern",
23
+ "domain": "frontend",
24
+ "title": "Semantic HTML Before ARIA",
25
+ "description": "Use native HTML elements for their intended purpose before reaching for ARIA attributes. A <button> is better than a <div role='button'>. A <nav> is better than <div aria-label='navigation'>. Native elements get keyboard handling, focus management, and screen reader support for free.",
26
+ "severity": "warning",
27
+ "tags": ["accessibility", "html", "aria", "wcag"]
28
+ },
29
+ {
30
+ "id": "fe-004",
31
+ "type": "pattern",
32
+ "domain": "frontend",
33
+ "title": "Error Boundaries for Resilient UI",
34
+ "description": "Wrap major UI sections in error boundaries so a crash in one component doesn't take down the entire page. Show a meaningful fallback UI with a retry option. Log errors to your monitoring service. Place boundaries at route level and around third-party components.",
35
+ "severity": "warning",
36
+ "tags": ["react", "error-handling", "resilience"]
37
+ },
38
+ {
39
+ "id": "fe-005",
40
+ "type": "pattern",
41
+ "domain": "frontend",
42
+ "title": "Lazy Loading for Route-Level Code Splitting",
43
+ "description": "Use dynamic imports (React.lazy, import()) for route-level components. This reduces initial bundle size and improves first paint. Combine with Suspense for loading states. Only eagerly load the routes users are most likely to visit first.",
44
+ "severity": "suggestion",
45
+ "tags": ["performance", "code-splitting", "react"]
46
+ },
47
+ {
48
+ "id": "fe-006",
49
+ "type": "pattern",
50
+ "domain": "accessibility",
51
+ "title": "Focus Management on Navigation",
52
+ "description": "When the user navigates to a new page or opens a modal, move focus to the appropriate element. After route change, focus the main heading or skip-to-content link. In modals, trap focus within the modal and restore it on close.",
53
+ "severity": "warning",
54
+ "tags": ["accessibility", "focus", "keyboard", "wcag"]
55
+ },
56
+ {
57
+ "id": "fe-007",
58
+ "type": "pattern",
59
+ "domain": "accessibility",
60
+ "title": "Color Contrast Minimum 4.5:1",
61
+ "description": "Ensure text has at least 4.5:1 contrast ratio against its background (WCAG AA). Large text (18px+ bold or 24px+ regular) needs 3:1 minimum. Use semantic design tokens that guarantee compliant pairs. Test with a contrast checker tool.",
62
+ "severity": "warning",
63
+ "tags": ["accessibility", "contrast", "wcag", "design-tokens"]
64
+ },
65
+ {
66
+ "id": "fe-008",
67
+ "type": "anti-pattern",
68
+ "domain": "frontend",
69
+ "title": "Prop Drilling Through Many Layers",
70
+ "description": "Passing props through 3+ intermediate components that don't use them is prop drilling. It creates tight coupling and makes refactoring painful. Use React Context for truly global state, or component composition (passing children) to reduce nesting depth.",
71
+ "severity": "suggestion",
72
+ "tags": ["react", "state", "composition"]
73
+ },
74
+ {
75
+ "id": "fe-009",
76
+ "type": "anti-pattern",
77
+ "domain": "frontend",
78
+ "title": "Inline Styles Over Design Tokens",
79
+ "description": "Don't use inline styles or raw hex colors in components. They bypass the design system, create inconsistency, and make theming impossible. Use semantic tokens (bg-surface, text-primary) that resolve to values through the token system.",
80
+ "severity": "warning",
81
+ "tags": ["design-tokens", "css", "consistency"]
82
+ },
83
+ {
84
+ "id": "fe-010",
85
+ "type": "anti-pattern",
86
+ "domain": "frontend",
87
+ "title": "useEffect for Derived State",
88
+ "description": "Don't use useEffect to sync state that can be computed from other state. If fullName = firstName + lastName, compute it during render, not in an effect. Effects for derived state cause extra re-renders and race conditions. Use useMemo for expensive computations.",
89
+ "severity": "warning",
90
+ "tags": ["react", "hooks", "performance"]
91
+ },
92
+ {
93
+ "id": "fe-011",
94
+ "type": "pattern",
95
+ "domain": "frontend",
96
+ "title": "Optimistic UI Updates",
97
+ "description": "Update the UI immediately on user action before the server confirms. Show the expected outcome with a subtle loading indicator. Roll back on error with a toast notification. This makes the app feel instant even on slow connections.",
98
+ "severity": "suggestion",
99
+ "tags": ["ux", "performance", "state"]
100
+ },
101
+ {
102
+ "id": "fe-012",
103
+ "type": "anti-pattern",
104
+ "domain": "accessibility",
105
+ "title": "Click Handlers on Non-Interactive Elements",
106
+ "description": "Don't add onClick to divs or spans without also adding role='button', tabIndex=0, and keyboard event handlers. Screen readers and keyboard users can't interact with these elements. Use a <button> element instead — it handles all of this natively.",
107
+ "severity": "warning",
108
+ "tags": ["accessibility", "keyboard", "html", "wcag"]
109
+ },
110
+ {
111
+ "id": "fe-013",
112
+ "type": "pattern",
113
+ "domain": "frontend",
114
+ "title": "Skeleton Loading States",
115
+ "description": "Show skeleton placeholders that match the layout of the content being loaded. This reduces perceived load time and prevents layout shift. Use CSS animations for shimmer effects. Skeletons are better than spinners for content-heavy pages.",
116
+ "severity": "suggestion",
117
+ "tags": ["ux", "performance", "loading"]
118
+ },
119
+ {
120
+ "id": "fe-014",
121
+ "type": "rule",
122
+ "domain": "accessibility",
123
+ "title": "Every Interactive Element Must Be Keyboard Accessible",
124
+ "description": "Every element that can be clicked must also be reachable and operable via keyboard. This means focusable (tabindex or native element), visible focus indicator (focus-visible ring), and keyboard activation (Enter/Space for buttons, Enter for links).",
125
+ "severity": "critical",
126
+ "tags": ["accessibility", "keyboard", "wcag"]
127
+ },
128
+ {
129
+ "id": "fe-015",
130
+ "type": "anti-pattern",
131
+ "domain": "frontend",
132
+ "title": "Boolean Prop Explosion",
133
+ "description": "Avoid components with many boolean props like isPrimary, isOutlined, isSmall, isDisabled. Use a variant prop with explicit values instead: variant='primary' | 'outlined' | 'ghost'. This prevents impossible states (isPrimary && isOutlined) and is self-documenting.",
134
+ "severity": "suggestion",
135
+ "tags": ["react", "api-design", "components"]
136
+ }
137
+ ]
@@ -0,0 +1,9 @@
1
+ {
2
+ "id": "starter-nodejs",
3
+ "name": "Node.js Starter Pack",
4
+ "version": "1.0.0",
5
+ "description": "Error handling, async patterns, streams, security, module system — essential Node.js patterns for production servers.",
6
+ "domains": ["nodejs", "backend"],
7
+ "tier": "default",
8
+ "vault": { "dir": "vault" }
9
+ }
@@ -0,0 +1,137 @@
1
+ [
2
+ {
3
+ "id": "node-001",
4
+ "type": "rule",
5
+ "domain": "nodejs",
6
+ "title": "Always Handle Promise Rejections",
7
+ "severity": "critical",
8
+ "description": "Unhandled promise rejections terminate the Node.js process (since v15). Every promise chain needs a .catch(), and every async function call needs try/catch or a .catch() on the returned promise. Set a global safety net, but don't rely on it:\n\nprocess.on('unhandledRejection', (reason, promise) => {\n logger.error('Unhandled rejection', { reason });\n process.exit(1);\n});\n\nThis global handler is a last resort — fix the root cause by handling rejections at the call site.",
9
+ "tags": ["nodejs", "error-handling", "async"]
10
+ },
11
+ {
12
+ "id": "node-002",
13
+ "type": "pattern",
14
+ "domain": "nodejs",
15
+ "title": "Use AbortController for Cancellable Async Operations",
16
+ "severity": "warning",
17
+ "description": "AbortController is the standard way to cancel async work — fetch requests, timers, streams, and custom operations. Pass an AbortSignal to anything long-running so callers can cancel it:\n\nasync function fetchWithTimeout(url, timeoutMs = 5000) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n try {\n const res = await fetch(url, { signal: controller.signal });\n return await res.json();\n } finally {\n clearTimeout(timeout);\n }\n}\n\nNode.js built-ins like fs, http, and timers all support AbortSignal natively.",
18
+ "tags": ["nodejs", "async", "cancellation"]
19
+ },
20
+ {
21
+ "id": "node-003",
22
+ "type": "pattern",
23
+ "domain": "nodejs",
24
+ "title": "Streams Over Buffering for Large Data",
25
+ "severity": "warning",
26
+ "description": "Never read an entire file or HTTP response into memory when you can stream it. Buffering large data causes memory spikes, GC pressure, and OOM crashes under load. Use pipeline() from node:stream/promises for safe stream composition with automatic error handling and cleanup:\n\nimport { pipeline } from 'node:stream/promises';\nimport { createReadStream, createWriteStream } from 'node:fs';\nimport { createGzip } from 'node:zlib';\n\nawait pipeline(\n createReadStream('input.log'),\n createGzip(),\n createWriteStream('input.log.gz')\n);\n\npipeline() destroys all streams on error — no leaked file descriptors.",
27
+ "tags": ["nodejs", "streams", "performance", "memory"]
28
+ },
29
+ {
30
+ "id": "node-004",
31
+ "type": "pattern",
32
+ "domain": "nodejs",
33
+ "title": "Graceful Shutdown on SIGTERM and SIGINT",
34
+ "severity": "warning",
35
+ "description": "Production servers must shut down cleanly — stop accepting new connections, finish in-flight requests, close database pools, then exit. Without this, deploys cause dropped requests and data corruption:\n\nfunction gracefulShutdown(server, db) {\n let shuttingDown = false;\n const shutdown = async (signal) => {\n if (shuttingDown) return;\n shuttingDown = true;\n console.log(`${signal} received, shutting down...`);\n server.close();\n await db.end();\n process.exit(0);\n };\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n process.on('SIGINT', () => shutdown('SIGINT'));\n}\n\nSet a hard timeout (e.g., 30s) to force exit if cleanup stalls.",
36
+ "tags": ["nodejs", "deployment", "reliability", "signals"]
37
+ },
38
+ {
39
+ "id": "node-005",
40
+ "type": "pattern",
41
+ "domain": "nodejs",
42
+ "title": "Validate Environment Variables at Startup",
43
+ "severity": "warning",
44
+ "description": "Fail fast if required environment variables are missing. Don't let the process start and fail 10 minutes later when it first tries to connect to a database. Validate at the top of your entry point:\n\nfunction requireEnv(name) {\n const value = process.env[name];\n if (!value) {\n console.error(`Missing required env var: ${name}`);\n process.exit(1);\n }\n return value;\n}\n\nconst config = {\n dbUrl: requireEnv('DATABASE_URL'),\n port: Number(process.env.PORT) || 3000,\n nodeEnv: process.env.NODE_ENV || 'development',\n};\n\nExport a frozen config object — never read process.env directly in business logic.",
45
+ "tags": ["nodejs", "configuration", "validation", "fail-fast"]
46
+ },
47
+ {
48
+ "id": "node-006",
49
+ "type": "anti-pattern",
50
+ "domain": "nodejs",
51
+ "title": "Synchronous File Operations in Request Handlers",
52
+ "severity": "critical",
53
+ "description": "Never use readFileSync, writeFileSync, or any *Sync method in request handlers. They block the entire event loop — no other request can be processed until the I/O completes. One slow disk read blocks every connected client:\n\n// BAD — blocks the event loop\napp.get('/config', (req, res) => {\n const data = fs.readFileSync('config.json', 'utf8');\n res.json(JSON.parse(data));\n});\n\n// GOOD — non-blocking\napp.get('/config', async (req, res) => {\n const data = await fs.promises.readFile('config.json', 'utf8');\n res.json(JSON.parse(data));\n});\n\nSync methods are acceptable at startup for loading config before the server starts listening.",
54
+ "tags": ["nodejs", "performance", "event-loop", "async"]
55
+ },
56
+ {
57
+ "id": "node-007",
58
+ "type": "pattern",
59
+ "domain": "nodejs",
60
+ "title": "Custom Error Subclasses for Domain Errors",
61
+ "severity": "suggestion",
62
+ "description": "Create typed error classes instead of throwing generic Error objects. This enables precise catch handling, proper HTTP status mapping, and structured logging:\n\nclass AppError extends Error {\n constructor(message, code, statusCode = 500) {\n super(message);\n this.name = this.constructor.name;\n this.code = code;\n this.statusCode = statusCode;\n }\n}\n\nclass NotFoundError extends AppError {\n constructor(resource, id) {\n super(`${resource} ${id} not found`, 'NOT_FOUND', 404);\n }\n}\n\nclass ValidationError extends AppError {\n constructor(message, fields) {\n super(message, 'VALIDATION_ERROR', 400);\n this.fields = fields;\n }\n}\n\nCatch by class: if (err instanceof NotFoundError) — not by message string matching.",
63
+ "tags": ["nodejs", "error-handling", "architecture"]
64
+ },
65
+ {
66
+ "id": "node-008",
67
+ "type": "pattern",
68
+ "domain": "nodejs",
69
+ "title": "Use node:test and node:assert for Built-in Testing",
70
+ "severity": "suggestion",
71
+ "description": "Node.js ships a capable test runner since v18. Zero dependencies, native TypeScript support (--experimental-strip-types), and fast startup. Use it before reaching for Jest or Vitest:\n\nimport { describe, it, mock } from 'node:test';\nimport assert from 'node:assert/strict';\n\ndescribe('UserService', () => {\n it('creates a user with hashed password', async () => {\n const hash = mock.fn(() => 'hashed');\n const service = new UserService({ hash });\n const user = await service.create('alice', 'pass');\n assert.equal(user.name, 'alice');\n assert.equal(hash.mock.calls.length, 1);\n });\n});\n\nRun with: node --test --test-reporter spec",
72
+ "tags": ["nodejs", "testing", "built-in"]
73
+ },
74
+ {
75
+ "id": "node-009",
76
+ "type": "rule",
77
+ "domain": "nodejs",
78
+ "title": "Prefer ESM, Understand CJS Interop",
79
+ "severity": "warning",
80
+ "description": "Use ES modules (import/export) as the default for new projects. Set \"type\": \"module\" in package.json. ESM gives you static analysis, tree-shaking, and top-level await.\n\nKey interop rules:\n- ESM can import CJS: import pkg from 'cjs-package' (gets default export)\n- CJS cannot require() ESM — use dynamic import(): const mod = await import('esm-package')\n- Named exports from CJS may need destructuring from default: import pkg from 'cjs-lib'; const { method } = pkg;\n- __dirname and __filename don't exist in ESM — use: import.meta.dirname and import.meta.filename (Node 21+), or path.dirname(fileURLToPath(import.meta.url)) for older versions",
81
+ "tags": ["nodejs", "modules", "esm", "cjs"]
82
+ },
83
+ {
84
+ "id": "node-010",
85
+ "type": "anti-pattern",
86
+ "domain": "nodejs",
87
+ "title": "User Input in Child Process Commands",
88
+ "severity": "critical",
89
+ "description": "Never interpolate user input into shell commands. This is command injection — the #1 server-side vulnerability:\n\n// DANGEROUS — command injection\nconst { exec } = require('child_process');\nexec(`git log --author=\"${userInput}\"`);\n// userInput = '\"; rm -rf / #' → catastrophic\n\n// SAFE — use array form, bypasses the shell entirely\nimport { execFile } from 'node:child_process';\nexecFile('git', ['log', `--author=${userInput}`]);\n\nRules: use execFile/spawn (not exec/execSync), never set shell: true with user data, validate and sanitize all inputs. If you need a shell, use a whitelist of allowed values.",
90
+ "tags": ["nodejs", "security", "child-process", "injection"]
91
+ },
92
+ {
93
+ "id": "node-011",
94
+ "type": "pattern",
95
+ "domain": "nodejs",
96
+ "title": "Crypto Best Practices: randomUUID, scrypt, timingSafeEqual",
97
+ "severity": "warning",
98
+ "description": "Use Node.js built-in crypto correctly — don't install third-party packages for basics:\n\nimport { randomUUID, scryptSync, timingSafeEqual, randomBytes } from 'node:crypto';\n\n// Generate IDs\nconst id = randomUUID(); // v4 UUID, cryptographically random\n\n// Hash passwords (scrypt is memory-hard, resistant to GPU attacks)\nconst salt = randomBytes(16);\nconst hash = scryptSync(password, salt, 64);\n\n// Compare secrets (constant-time, prevents timing attacks)\nconst isValid = timingSafeEqual(\n Buffer.from(providedToken),\n Buffer.from(storedToken)\n);\n\nNever use MD5/SHA for passwords. Never compare secrets with === (timing leak). Never use Math.random() for security-sensitive values.",
99
+ "tags": ["nodejs", "security", "crypto", "authentication"]
100
+ },
101
+ {
102
+ "id": "node-012",
103
+ "type": "rule",
104
+ "domain": "nodejs",
105
+ "title": "Always Handle EventEmitter 'error' Events",
106
+ "severity": "critical",
107
+ "description": "An EventEmitter that emits 'error' with no listener crashes the process with an uncaught exception. This applies to streams, HTTP servers, database connections, and any custom emitter:\n\n// This WILL crash if the file doesn't exist\nconst stream = fs.createReadStream('missing.txt');\n// Fix: always attach an error handler\nstream.on('error', (err) => {\n logger.error('Stream failed', { error: err.message });\n});\n\n// For custom emitters, consider a default error handler:\nclass MyService extends EventEmitter {\n constructor() {\n super();\n this.on('error', (err) => {\n // Prevents crash if consumer forgets to listen\n console.error('Unhandled service error:', err);\n });\n }\n}\n\nRule: if you create it or receive it, listen for 'error' on it.",
108
+ "tags": ["nodejs", "error-handling", "events", "streams"]
109
+ },
110
+ {
111
+ "id": "node-013",
112
+ "type": "pattern",
113
+ "domain": "nodejs",
114
+ "title": "Structured Logging Over Console.log",
115
+ "severity": "suggestion",
116
+ "description": "Use structured JSON logging instead of console.log in production. Structured logs are parseable by log aggregators (Datadog, CloudWatch, ELK) and support filtering, alerting, and correlation:\n\n// Minimal structured logger (no dependencies)\nconst log = (level, message, meta = {}) => {\n const entry = JSON.stringify({\n timestamp: new Date().toISOString(),\n level,\n message,\n ...meta,\n });\n process.stdout.write(entry + '\\n');\n};\n\nlog('info', 'Request handled', {\n method: 'GET',\n path: '/users',\n duration: 42,\n statusCode: 200,\n});\n\nAlways include: timestamp, level, message, request ID. Never log secrets, tokens, or passwords.",
117
+ "tags": ["nodejs", "logging", "observability", "production"]
118
+ },
119
+ {
120
+ "id": "node-014",
121
+ "type": "anti-pattern",
122
+ "domain": "nodejs",
123
+ "title": "Swallowing Errors with Empty Catch Blocks",
124
+ "severity": "warning",
125
+ "description": "An empty catch block hides failures and makes debugging impossible. Every catch must do something meaningful — log, rethrow, return a default, or translate the error:\n\n// BAD — silent failure, impossible to debug\ntry {\n await sendEmail(user);\n} catch (e) {}\n\n// GOOD — log and continue (if email is non-critical)\ntry {\n await sendEmail(user);\n} catch (err) {\n logger.warn('Email send failed', { userId: user.id, error: err.message });\n}\n\n// GOOD — rethrow with context\ntry {\n await db.query(sql);\n} catch (err) {\n throw new AppError(`Query failed: ${err.message}`, 'DB_ERROR', 500);\n}\n\nIf you genuinely don't care about the error, add a comment explaining why.",
126
+ "tags": ["nodejs", "error-handling", "debugging"]
127
+ },
128
+ {
129
+ "id": "node-015",
130
+ "type": "pattern",
131
+ "domain": "nodejs",
132
+ "title": "Use AsyncLocalStorage for Request Context",
133
+ "severity": "suggestion",
134
+ "description": "AsyncLocalStorage provides implicit context propagation through async call chains — no need to pass requestId, userId, or tracing data through every function parameter:\n\nimport { AsyncLocalStorage } from 'node:async_hooks';\n\nconst requestContext = new AsyncLocalStorage();\n\n// Middleware: set context per request\napp.use((req, res, next) => {\n const store = { requestId: randomUUID(), userId: req.user?.id };\n requestContext.run(store, next);\n});\n\n// Anywhere in the call stack:\nfunction getRequestId() {\n return requestContext.getStore()?.requestId;\n}\n\n// Logger automatically includes request ID\nconst log = (level, msg, meta) => {\n const ctx = requestContext.getStore() || {};\n console.log(JSON.stringify({ ...ctx, level, msg, ...meta }));\n};\n\nThis is how APM tools and logging frameworks implement automatic trace correlation.",
135
+ "tags": ["nodejs", "async", "context", "observability"]
136
+ }
137
+ ]
@@ -0,0 +1,9 @@
1
+ {
2
+ "id": "starter-react",
3
+ "name": "React Starter Pack",
4
+ "version": "1.0.0",
5
+ "description": "Hook rules, component patterns, state management, performance optimization, error boundaries — essential React patterns.",
6
+ "domains": ["react", "frontend"],
7
+ "tier": "default",
8
+ "vault": { "dir": "vault" }
9
+ }