ima-claude 2.9.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.
- package/LICENSE +21 -0
- package/README.md +463 -0
- package/dist/cli.js +1064 -0
- package/package.json +49 -0
- package/platforms/claude/adapter.ts +115 -0
- package/platforms/junie/adapter.ts +254 -0
- package/platforms/junie/agents-template.md +113 -0
- package/platforms/junie/hook-translations.md +84 -0
- package/platforms/shared/detector.ts +27 -0
- package/platforms/shared/installer.ts +202 -0
- package/platforms/shared/types.ts +78 -0
- package/plugins/ima-claude/.claude-plugin/plugin.json +25 -0
- package/plugins/ima-claude/agents/explorer.md +30 -0
- package/plugins/ima-claude/agents/implementer.md +30 -0
- package/plugins/ima-claude/agents/memory.md +42 -0
- package/plugins/ima-claude/agents/reviewer.md +53 -0
- package/plugins/ima-claude/agents/tester.md +33 -0
- package/plugins/ima-claude/agents/wp-developer.md +46 -0
- package/plugins/ima-claude/hooks/README.md +145 -0
- package/plugins/ima-claude/hooks/atlassian_prereqs.py +112 -0
- package/plugins/ima-claude/hooks/block_sed_edits.py +59 -0
- package/plugins/ima-claude/hooks/bootstrap.sh +90 -0
- package/plugins/ima-claude/hooks/bootstrap_utility_check.py +94 -0
- package/plugins/ima-claude/hooks/composer_autoload_check.py +70 -0
- package/plugins/ima-claude/hooks/docs_organization.py +104 -0
- package/plugins/ima-claude/hooks/enforce_rg_over_grep.py +56 -0
- package/plugins/ima-claude/hooks/fp_utility_check.py +90 -0
- package/plugins/ima-claude/hooks/hook_logger.py +69 -0
- package/plugins/ima-claude/hooks/hooks.json +239 -0
- package/plugins/ima-claude/hooks/jira_issue_fetch.py +79 -0
- package/plugins/ima-claude/hooks/jquery_in_wordpress.py +92 -0
- package/plugins/ima-claude/hooks/memory_bootstrap.py +79 -0
- package/plugins/ima-claude/hooks/memory_store_reminder.py +75 -0
- package/plugins/ima-claude/hooks/prompt_coach.py +125 -0
- package/plugins/ima-claude/hooks/prompt_coach_digest.md +48 -0
- package/plugins/ima-claude/hooks/prompt_coach_system.md +30 -0
- package/plugins/ima-claude/hooks/sequential_thinking_check.py +81 -0
- package/plugins/ima-claude/hooks/serena_over_grep.py +96 -0
- package/plugins/ima-claude/hooks/serena_over_read.py +66 -0
- package/plugins/ima-claude/hooks/serena_project_check.py +133 -0
- package/plugins/ima-claude/hooks/sql_injection_check.py +73 -0
- package/plugins/ima-claude/hooks/task_master_after_plan.py +31 -0
- package/plugins/ima-claude/hooks/task_master_before_impl.py +93 -0
- package/plugins/ima-claude/hooks/tavily_extract_advanced.py +48 -0
- package/plugins/ima-claude/hooks/vestige_before_external.py +86 -0
- package/plugins/ima-claude/hooks/webfetch_to_tavily.py +42 -0
- package/plugins/ima-claude/hooks/websearch_to_tavily.py +41 -0
- package/plugins/ima-claude/hooks/wp_security_check.py +150 -0
- package/plugins/ima-claude/personalities/README.md +45 -0
- package/plugins/ima-claude/personalities/enable-40k.md +69 -0
- package/plugins/ima-claude/personalities/enable-templars.md +69 -0
- package/plugins/ima-claude/skills/.research-summary.md +340 -0
- package/plugins/ima-claude/skills/architect/SKILL.md +304 -0
- package/plugins/ima-claude/skills/compound-bridge/SKILL.md +200 -0
- package/plugins/ima-claude/skills/discourse/SKILL.md +440 -0
- package/plugins/ima-claude/skills/discourse-admin/SKILL.md +192 -0
- package/plugins/ima-claude/skills/discourse-admin/references/api-endpoints.md +441 -0
- package/plugins/ima-claude/skills/discourse-admin/references/gotchas.md +107 -0
- package/plugins/ima-claude/skills/discourse-admin/references/staging-defaults.md +98 -0
- package/plugins/ima-claude/skills/discourse-admin/scripts/discourse-admin.py +319 -0
- package/plugins/ima-claude/skills/docs-organize/SKILL.md +254 -0
- package/plugins/ima-claude/skills/docs-organize/templates/active-README.md +50 -0
- package/plugins/ima-claude/skills/docs-organize/templates/archive-README.md +57 -0
- package/plugins/ima-claude/skills/docs-organize/templates/docs-README.md +43 -0
- package/plugins/ima-claude/skills/docs-organize/templates/phase-archive-README.md +83 -0
- package/plugins/ima-claude/skills/docs-organize/templates/section-README.md +48 -0
- package/plugins/ima-claude/skills/docs-organize/templates/transient-README.md +79 -0
- package/plugins/ima-claude/skills/docs-organize/templates/transient-gitignore +9 -0
- package/plugins/ima-claude/skills/ember-discourse/SKILL.md +496 -0
- package/plugins/ima-claude/skills/functional-programmer/SKILL.md +258 -0
- package/plugins/ima-claude/skills/ima-bootstrap/SKILL.md +278 -0
- package/plugins/ima-claude/skills/ima-bootstrap/references/bootstrap-patterns.md +356 -0
- package/plugins/ima-claude/skills/ima-bootstrap/references/ima-brand.md +273 -0
- package/plugins/ima-claude/skills/ima-bootstrap/references/theme-integration.md +212 -0
- package/plugins/ima-claude/skills/ima-brand/SKILL.md +108 -0
- package/plugins/ima-claude/skills/ima-brand/references/brand-identity.md +140 -0
- package/plugins/ima-claude/skills/ima-brand/references/digital-standards.md +180 -0
- package/plugins/ima-claude/skills/ima-brand/references/visual-system.md +173 -0
- package/plugins/ima-claude/skills/ima-forms-expert/SKILL.md +175 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/container-components.md +154 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/examples.md +328 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/field-components.md +298 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/form-factory.md +193 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/quick-reference.md +153 -0
- package/plugins/ima-claude/skills/ima-forms-expert/references/validation-engine.md +336 -0
- package/plugins/ima-claude/skills/jira-checkpoint/SKILL.md +178 -0
- package/plugins/ima-claude/skills/jquery/SKILL.md +413 -0
- package/plugins/ima-claude/skills/js-fp/SKILL.md +463 -0
- package/plugins/ima-claude/skills/js-fp/core-principles.md +487 -0
- package/plugins/ima-claude/skills/js-fp/examples/pure-functions.js +260 -0
- package/plugins/ima-claude/skills/js-fp/examples/tests/pure-functions.test.js +262 -0
- package/plugins/ima-claude/skills/js-fp/references/anti-patterns.md +120 -0
- package/plugins/ima-claude/skills/js-fp/references/performance-patterns.md +116 -0
- package/plugins/ima-claude/skills/js-fp/references/testing-patterns.md +134 -0
- package/plugins/ima-claude/skills/js-fp-api/SKILL.md +280 -0
- package/plugins/ima-claude/skills/js-fp-api/examples/crud-endpoint.js +258 -0
- package/plugins/ima-claude/skills/js-fp-api/references/middleware-patterns.md +134 -0
- package/plugins/ima-claude/skills/js-fp-api/references/security-sql.md +110 -0
- package/plugins/ima-claude/skills/js-fp-api/references/validation-patterns.md +165 -0
- package/plugins/ima-claude/skills/js-fp-react/SKILL.md +447 -0
- package/plugins/ima-claude/skills/js-fp-react/examples/ProductCard.tsx +65 -0
- package/plugins/ima-claude/skills/js-fp-react/references/hooks-advanced.md +136 -0
- package/plugins/ima-claude/skills/js-fp-react/references/performance-patterns.md +175 -0
- package/plugins/ima-claude/skills/js-fp-vue/SKILL.md +322 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/complete-examples.md +397 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/composables-advanced.md +282 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/reactivity-patterns.md +348 -0
- package/plugins/ima-claude/skills/js-fp-vue/references/testing.md +314 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/SKILL.md +301 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/references/ajax-patterns.md +192 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/references/event-patterns.md +136 -0
- package/plugins/ima-claude/skills/js-fp-wordpress/references/wp-integration.md +248 -0
- package/plugins/ima-claude/skills/livecanvas/SKILL.md +209 -0
- package/plugins/ima-claude/skills/livecanvas/references/livecanvas-features.md +311 -0
- package/plugins/ima-claude/skills/livecanvas/references/loops-and-logic.md +730 -0
- package/plugins/ima-claude/skills/livecanvas/references/picostrap.md +227 -0
- package/plugins/ima-claude/skills/mcp-atlassian/SKILL.md +339 -0
- package/plugins/ima-claude/skills/mcp-context7/SKILL.md +109 -0
- package/plugins/ima-claude/skills/mcp-memory/SKILL.md +182 -0
- package/plugins/ima-claude/skills/mcp-qdrant/SKILL.md +233 -0
- package/plugins/ima-claude/skills/mcp-sequential/SKILL.md +149 -0
- package/plugins/ima-claude/skills/mcp-serena/SKILL.md +174 -0
- package/plugins/ima-claude/skills/mcp-tavily/SKILL.md +118 -0
- package/plugins/ima-claude/skills/mcp-vestige/SKILL.md +259 -0
- package/plugins/ima-claude/skills/php-authnet/SKILL.md +275 -0
- package/plugins/ima-claude/skills/php-authnet/references/api-reference.md +624 -0
- package/plugins/ima-claude/skills/php-authnet/references/sandbox-testing.md +424 -0
- package/plugins/ima-claude/skills/php-fp/SKILL.md +333 -0
- package/plugins/ima-claude/skills/php-fp/examples/pure-functions.php +403 -0
- package/plugins/ima-claude/skills/php-fp/examples/tests/PureFunctionsTest.php +515 -0
- package/plugins/ima-claude/skills/php-fp/references/core-principles.md +277 -0
- package/plugins/ima-claude/skills/php-fp/references/testing-patterns.md +374 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/SKILL.md +216 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/fp-patterns.md +275 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/plugin-architecture.md +295 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/security-examples.md +203 -0
- package/plugins/ima-claude/skills/php-fp-wordpress/references/testing-strategy.md +259 -0
- package/plugins/ima-claude/skills/phpunit-wp/SKILL.md +716 -0
- package/plugins/ima-claude/skills/playwright/SKILL.md +434 -0
- package/plugins/ima-claude/skills/playwright/references/accessibility-testing.md +153 -0
- package/plugins/ima-claude/skills/playwright/references/ci-cd.md +268 -0
- package/plugins/ima-claude/skills/playwright/references/network-mocking.md +270 -0
- package/plugins/ima-claude/skills/playwright/references/visual-regression.md +215 -0
- package/plugins/ima-claude/skills/py-fp/SKILL.md +663 -0
- package/plugins/ima-claude/skills/py-fp/examples/pure-functions.py +185 -0
- package/plugins/ima-claude/skills/py-fp/examples/tests/test_pure_functions.py +244 -0
- package/plugins/ima-claude/skills/py-fp/references/core-principles.md +381 -0
- package/plugins/ima-claude/skills/py-fp/references/testing-patterns.md +283 -0
- package/plugins/ima-claude/skills/quasar-fp/SKILL.md +327 -0
- package/plugins/ima-claude/skills/quasar-fp/metadata.json +85 -0
- package/plugins/ima-claude/skills/quasar-fp/references/component-patterns.md +257 -0
- package/plugins/ima-claude/skills/quasar-fp/references/theme-integration.md +233 -0
- package/plugins/ima-claude/skills/quasar-fp/references/utility-classes.md +237 -0
- package/plugins/ima-claude/skills/quickstart/SKILL.md +129 -0
- package/plugins/ima-claude/skills/rails/SKILL.md +359 -0
- package/plugins/ima-claude/skills/resume-session/SKILL.md +68 -0
- package/plugins/ima-claude/skills/rg/SKILL.md +205 -0
- package/plugins/ima-claude/skills/ruby-fp/SKILL.md +336 -0
- package/plugins/ima-claude/skills/save-session/SKILL.md +81 -0
- package/plugins/ima-claude/skills/scorecard/SKILL.md +96 -0
- package/plugins/ima-claude/skills/skill-analyzer/SKILL.md +127 -0
- package/plugins/ima-claude/skills/skill-analyzer/references/advanced-checklist.md +44 -0
- package/plugins/ima-claude/skills/skill-analyzer/references/core-checklist.md +60 -0
- package/plugins/ima-claude/skills/skill-analyzer/scripts/analyze_skill.py +418 -0
- package/plugins/ima-claude/skills/skill-creator/LICENSE.txt +202 -0
- package/plugins/ima-claude/skills/skill-creator/SKILL.md +343 -0
- package/plugins/ima-claude/skills/skill-creator/references/output-patterns.md +82 -0
- package/plugins/ima-claude/skills/skill-creator/references/workflows.md +28 -0
- package/plugins/ima-claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/plugins/ima-claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/plugins/ima-claude/skills/skill-creator/scripts/quick_validate.py +103 -0
- package/plugins/ima-claude/skills/task-master/SKILL.md +51 -0
- package/plugins/ima-claude/skills/task-planner/SKILL.md +228 -0
- package/plugins/ima-claude/skills/task-runner/SKILL.md +192 -0
- package/plugins/ima-claude/skills/unit-testing/SKILL.md +198 -0
- package/plugins/ima-claude/skills/unit-testing/references/mock-patterns.md +181 -0
- package/plugins/ima-claude/skills/unit-testing/references/tdd-workflow.md +177 -0
- package/plugins/ima-claude/skills/unit-testing/references/test-strategy.md +126 -0
- package/plugins/ima-claude/skills/wp-local/SKILL.md +246 -0
- package/plugins/ima-claude/skills/wp-local/references/configuration.md +198 -0
- package/plugins/ima-claude/skills/wp-local/references/wp-cli-reference.md +406 -0
- package/plugins/ima-claude/skills/wp-local/scripts/wp-local.sh +61 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Performance Patterns Reference
|
|
2
|
+
|
|
3
|
+
Evidence-based optimization through pure functions.
|
|
4
|
+
|
|
5
|
+
## Core Formula
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
Pure Function + Closure + Pre-compilation = Exceptional Performance
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## The Pre-Compilation Pattern
|
|
12
|
+
|
|
13
|
+
**Problem**: O(records x fields) - configuration accessed every iteration
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
// SLOW: 100,000 records x 24 fields = 2.4 million iterations
|
|
17
|
+
function processRecords(records, schema) {
|
|
18
|
+
return records.map(record => {
|
|
19
|
+
return schema.fields.reduce((obj, field) => {
|
|
20
|
+
obj[field.name] = transformField(record[field.name], field.type)
|
|
21
|
+
return obj
|
|
22
|
+
}, {})
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Solution**: Pre-compile configuration into closures
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
// FAST: O(records + fields) - configuration extracted once
|
|
31
|
+
export const mapRecord = ({prefix, fields}) => {
|
|
32
|
+
const allowedFields = Object.keys(fields)
|
|
33
|
+
|
|
34
|
+
// Return optimized mapping function
|
|
35
|
+
return (record) => {
|
|
36
|
+
const map = {}
|
|
37
|
+
allowedFields.forEach((key) => {
|
|
38
|
+
const val = record[prefix + '_' + key]
|
|
39
|
+
map[key] = fields[key].formatter ? fields[key].formatter(val) : val
|
|
40
|
+
})
|
|
41
|
+
return map
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Usage
|
|
46
|
+
const mapper = mapRecord(meta) // Configure once
|
|
47
|
+
const objects = records.map(mapper) // Linear performance
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
**Result**: 10-25x improvement on large datasets
|
|
51
|
+
|
|
52
|
+
## Function Factory Pattern
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
// Create specialized functions - configuration cost paid once
|
|
56
|
+
const createValidator = (rules) => (value) => {
|
|
57
|
+
const errors = []
|
|
58
|
+
for (const rule of rules) {
|
|
59
|
+
if (!rule.validator(value)) {
|
|
60
|
+
errors.push(rule.message)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return errors.length === 0 ? { valid: true } : { valid: false, errors }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Configure once
|
|
67
|
+
const validateEmail = createValidator([
|
|
68
|
+
{ validator: v => typeof v === 'string', message: 'Must be string' },
|
|
69
|
+
{ validator: v => v.includes('@'), message: 'Must contain @' }
|
|
70
|
+
])
|
|
71
|
+
|
|
72
|
+
// Use many times (fast)
|
|
73
|
+
emails.map(validateEmail)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Triple-Layer Curry Pattern
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
(Configuration) => (Parameters) => (Context) => Result
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
export const selectUserByEmail = (meta) => (user) => async (ctx) => {
|
|
84
|
+
const sql = getSelectStmt(meta)({whereSet: [{field: 'email'}]})
|
|
85
|
+
const results = await ctx.db.query({sql, values: [user.email]})
|
|
86
|
+
return mapRecord(meta)(results[0])
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Layer 1: Configuration (compile-time)
|
|
90
|
+
const configuredQuery = selectUserByEmail(initMeta(userMeta))
|
|
91
|
+
|
|
92
|
+
// Layers 2+3: Runtime execution
|
|
93
|
+
const user = await configuredQuery({email: 'test@example.com'})(context)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## When to Optimize
|
|
97
|
+
|
|
98
|
+
1. **Measure first** - Profile before optimizing
|
|
99
|
+
2. **>10K items** - Pre-compilation matters at scale
|
|
100
|
+
3. **Repeated config access** - Look for nested loops accessing setup data
|
|
101
|
+
4. **Hot paths** - Focus on frequently called code
|
|
102
|
+
|
|
103
|
+
## When NOT to Optimize
|
|
104
|
+
|
|
105
|
+
- Small datasets (<1K items)
|
|
106
|
+
- One-time operations
|
|
107
|
+
- Code readability suffers significantly
|
|
108
|
+
- No measured performance problem
|
|
109
|
+
|
|
110
|
+
## Quick Optimization Checklist
|
|
111
|
+
|
|
112
|
+
1. [ ] Identify repeated configuration access in loops
|
|
113
|
+
2. [ ] Extract configuration phase outside execution loop
|
|
114
|
+
3. [ ] Use closures to cache configuration
|
|
115
|
+
4. [ ] Verify linear execution (not quadratic)
|
|
116
|
+
5. [ ] Benchmark before/after with real data
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Testing Patterns Reference
|
|
2
|
+
|
|
3
|
+
Comprehensive edge-case testing enabled by pure functions.
|
|
4
|
+
|
|
5
|
+
## Core Principle
|
|
6
|
+
|
|
7
|
+
Test systematically against ALL data types and edge cases. Pure functions make this practical:
|
|
8
|
+
- Same input always produces same output
|
|
9
|
+
- No side effects - tests don't interfere
|
|
10
|
+
- Fast execution - pure functions run quickly
|
|
11
|
+
- Predictable behavior - easy to reason about
|
|
12
|
+
|
|
13
|
+
## Test Matrix Template
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
describe('FunctionUnderTest', () => {
|
|
17
|
+
describe('valid inputs', () => {
|
|
18
|
+
it('should handle expected use cases', () => {
|
|
19
|
+
expect(validateIntegerGreaterThan(0)(5)).toBe(true)
|
|
20
|
+
expect(validateIntegerGreaterThan(0)('5')).toBe(true) // String numbers
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
describe('invalid types', () => {
|
|
25
|
+
// Test against ALL JavaScript types
|
|
26
|
+
it('should reject null', () => expect(fn(null)).toBe(false))
|
|
27
|
+
it('should reject undefined', () => expect(fn(undefined)).toBe(false))
|
|
28
|
+
it('should reject NaN', () => expect(fn(NaN)).toBe(false))
|
|
29
|
+
it('should reject boolean', () => expect(fn(true)).toBe(false))
|
|
30
|
+
it('should reject array', () => expect(fn([])).toBe(false))
|
|
31
|
+
it('should reject object', () => expect(fn({})).toBe(false))
|
|
32
|
+
it('should reject function', () => expect(fn(() => {})).toBe(false))
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe('edge cases', () => {
|
|
36
|
+
// Boundary values, empty inputs, extreme values
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
describe('composition', () => {
|
|
40
|
+
// Test function combinations
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Parameterized Test Pattern
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
describe('calculateDiscount', () => {
|
|
49
|
+
describe('valid inputs', () => {
|
|
50
|
+
const testCases = [
|
|
51
|
+
[100, 0.1, 10],
|
|
52
|
+
[50, 0.2, 10],
|
|
53
|
+
[0, 0.1, 0]
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
testCases.forEach(([price, rate, expected]) => {
|
|
57
|
+
it(`${rate*100}% discount on ${price} = ${expected}`, () => {
|
|
58
|
+
expect(calculateDiscount(price, rate)).toBe(expected)
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
describe('invalid types', () => {
|
|
64
|
+
const invalidTypes = [null, undefined, NaN, true, [], {}, '100']
|
|
65
|
+
|
|
66
|
+
invalidTypes.forEach(input => {
|
|
67
|
+
it(`handles ${typeof input} (${String(input)}) gracefully`, () => {
|
|
68
|
+
expect(() => calculateDiscount(input, 0.1)).not.toThrow()
|
|
69
|
+
expect(calculateDiscount(input, 0.1)).toBe(0)
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Testing Composed Functions
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
describe('validateCreditCard', () => {
|
|
80
|
+
const validCard = {
|
|
81
|
+
name: 'John Doe',
|
|
82
|
+
number: '4111111111111111',
|
|
83
|
+
expDate: '12/26',
|
|
84
|
+
cvv: '123'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
it('accepts valid complete card', () => {
|
|
88
|
+
expect(validateCreditCard(validCard)).toEqual({ valid: true })
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
// Test each field independently
|
|
92
|
+
const fields = ['name', 'number', 'expDate', 'cvv']
|
|
93
|
+
fields.forEach(field => {
|
|
94
|
+
it(`rejects missing ${field}`, () => {
|
|
95
|
+
const invalid = { ...validCard, [field]: '' }
|
|
96
|
+
expect(validateCreditCard(invalid).valid).toBe(false)
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
})
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Testing Pure Functions with Dependencies
|
|
103
|
+
|
|
104
|
+
```javascript
|
|
105
|
+
// Function under test
|
|
106
|
+
const saveUser = async (userData, hasher, database) => {
|
|
107
|
+
const hashedPassword = await hasher.hash(userData.password)
|
|
108
|
+
return database.save({ ...userData, password: hashedPassword })
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Test with mock dependencies
|
|
112
|
+
describe('saveUser', () => {
|
|
113
|
+
const mockHasher = { hash: jest.fn(p => `hashed_${p}`) }
|
|
114
|
+
const mockDb = { save: jest.fn(user => ({ id: 1, ...user })) }
|
|
115
|
+
|
|
116
|
+
it('hashes password and saves user', async () => {
|
|
117
|
+
const result = await saveUser(
|
|
118
|
+
{ email: 'test@example.com', password: 'secret' },
|
|
119
|
+
mockHasher,
|
|
120
|
+
mockDb
|
|
121
|
+
)
|
|
122
|
+
expect(mockHasher.hash).toHaveBeenCalledWith('secret')
|
|
123
|
+
expect(result.password).toBe('hashed_secret')
|
|
124
|
+
})
|
|
125
|
+
})
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Quick Checklist
|
|
129
|
+
|
|
130
|
+
- [ ] Test all JavaScript types (null, undefined, NaN, boolean, array, object, function)
|
|
131
|
+
- [ ] Test boundary values (0, -1, MAX_SAFE_INTEGER, empty string, empty array)
|
|
132
|
+
- [ ] Test composition of functions together
|
|
133
|
+
- [ ] Mock dependencies for isolation
|
|
134
|
+
- [ ] Use parameterized tests for data variations
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "js-fp-api"
|
|
3
|
+
description: "FP API patterns for Node.js with security-first SQL and middleware DI - references js-fp core"
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# JavaScript FP - Node.js API
|
|
7
|
+
|
|
8
|
+
Functional programming patterns for Node.js APIs with security-first SQL, middleware dependency injection, and self-contained routes.
|
|
9
|
+
|
|
10
|
+
## When to Use This Skill
|
|
11
|
+
|
|
12
|
+
- Building REST APIs with Node.js
|
|
13
|
+
- Need security-first SQL patterns
|
|
14
|
+
- Implementing middleware-based dependency injection
|
|
15
|
+
- Self-contained route architecture
|
|
16
|
+
- Testing API endpoints comprehensively
|
|
17
|
+
|
|
18
|
+
## Core Philosophy
|
|
19
|
+
|
|
20
|
+
**Self-contained routes** (300-500 lines max) with **security-first SQL**, **middleware DI**, and **pure business logic separation** when genuinely reusable (3+ routes).
|
|
21
|
+
|
|
22
|
+
**Foundation**: This skill builds on `js-fp` core principles. Reference `../js-fp/SKILL.md` for purity, composition, dependency injection, and testing patterns.
|
|
23
|
+
|
|
24
|
+
## CRITICAL: Security-First SQL (MANDATORY)
|
|
25
|
+
|
|
26
|
+
**Rule**: NEVER use string concatenation for SQL. ALWAYS use parameterized queries.
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
// NEVER: SQL injection vulnerability
|
|
30
|
+
const sql = `SELECT * FROM events WHERE domain LIKE '%${domain}%'` // DANGER!
|
|
31
|
+
|
|
32
|
+
// ALWAYS: Parameterized queries returning {sql, params}
|
|
33
|
+
const buildDomainFilter = (domain) => {
|
|
34
|
+
const validation = validateDomain(domain)
|
|
35
|
+
if (!validation.valid) throw new Error(validation.error)
|
|
36
|
+
if (validation.domain === 'all') return { sql: '', params: {} }
|
|
37
|
+
|
|
38
|
+
return {
|
|
39
|
+
sql: 'AND from_address LIKE @domain_pattern',
|
|
40
|
+
params: { domain_pattern: `%${validation.domain}%` }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Deep Dive**: See `references/security-sql.md` for advanced SQL patterns, triple-layer curry, and domain whitelisting.
|
|
46
|
+
|
|
47
|
+
## Mandatory Architecture
|
|
48
|
+
|
|
49
|
+
### File Structure
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
/api/
|
|
53
|
+
├── middleware/ # DI only (database.js, auth.js)
|
|
54
|
+
├── shared/ # ONLY if 3+ routes use it
|
|
55
|
+
│ ├── validators.js # Domain whitelisting, validation
|
|
56
|
+
│ ├── filters.js # SQL builders returning {sql, params}
|
|
57
|
+
│ └── constants.js # Static config
|
|
58
|
+
├── business/ # Pure functions ONLY (calculations, transformations)
|
|
59
|
+
└── routes/
|
|
60
|
+
├── [domain]/ # Logical grouping
|
|
61
|
+
│ ├── index.js # Route orchestrator
|
|
62
|
+
│ └── [endpoint].js # 300-500 lines MAX
|
|
63
|
+
└── [simple].js # Standalone endpoints
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Self-Contained Route Pattern (ENFORCE)
|
|
67
|
+
|
|
68
|
+
```javascript
|
|
69
|
+
import { Hono } from 'hono'
|
|
70
|
+
import { validateDomain } from '../../shared/validators.js'
|
|
71
|
+
import { buildDomainFilter } from '../../shared/filters.js'
|
|
72
|
+
|
|
73
|
+
const route = new Hono()
|
|
74
|
+
|
|
75
|
+
// ───── Route-scoped validation ─────
|
|
76
|
+
const validateRequest = (ctx) => {
|
|
77
|
+
const domain = ctx.req.query('domain')
|
|
78
|
+
const validation = validateDomain(domain)
|
|
79
|
+
if (!validation.valid) {
|
|
80
|
+
const error = new Error(validation.error)
|
|
81
|
+
error.status = 400
|
|
82
|
+
throw error
|
|
83
|
+
}
|
|
84
|
+
return { domain: validation.domain }
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ───── Security-first SQL building ─────
|
|
88
|
+
const buildQuery = (env, { domain }) => {
|
|
89
|
+
const table = getTableReference(env)('events')
|
|
90
|
+
const filters = buildDomainFilter(domain)
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
sql: `SELECT * FROM ${table} WHERE 1=1 ${filters.sql}`,
|
|
94
|
+
params: filters.params
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// ───── Business logic (keep in-route if <50 lines) ─────
|
|
99
|
+
const processData = (raw) => raw.map(r => ({
|
|
100
|
+
...r,
|
|
101
|
+
timestamp: r.timestamp + 'Z'
|
|
102
|
+
}))
|
|
103
|
+
|
|
104
|
+
// ───── Clean orchestration ─────
|
|
105
|
+
route.get('/', async (c) => {
|
|
106
|
+
try {
|
|
107
|
+
const params = validateRequest(c)
|
|
108
|
+
const { sql, params: qp } = buildQuery(c.env, params)
|
|
109
|
+
const raw = await c.db.queryWithParams(sql, qp) // Middleware-injected
|
|
110
|
+
return c.json({ success: true, data: processData(raw) })
|
|
111
|
+
} catch (error) {
|
|
112
|
+
return c.json({ success: false, error: error.message }, error.status || 500)
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
export default route
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Middleware Dependency Injection
|
|
120
|
+
|
|
121
|
+
Inject dependencies via middleware context, not per-request instantiation.
|
|
122
|
+
|
|
123
|
+
```javascript
|
|
124
|
+
// middleware/database.js
|
|
125
|
+
export const databaseMiddleware = async (c, next) => {
|
|
126
|
+
const db = createDatabaseClient(c.env.DATABASE_URL)
|
|
127
|
+
c.db = {
|
|
128
|
+
query: (sql) => db.query(sql),
|
|
129
|
+
queryWithParams: (sql, params) => db.query(sql, params),
|
|
130
|
+
transaction: (fn) => db.transaction(fn)
|
|
131
|
+
}
|
|
132
|
+
await next()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Usage in routes: await c.db.queryWithParams(sql, params)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Deep Dive**: See `references/middleware-patterns.md` for function factories, auth middleware, and testing patterns.
|
|
139
|
+
|
|
140
|
+
## When to Extract to business/
|
|
141
|
+
|
|
142
|
+
**Extract ONLY if**:
|
|
143
|
+
- Function is genuinely reusable (3+ routes use it)
|
|
144
|
+
- Pure calculation/transformation
|
|
145
|
+
- No side effects
|
|
146
|
+
- 100% testable
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
// business/calculations.js
|
|
150
|
+
export const calculateTotalRevenue = (orders) =>
|
|
151
|
+
orders.reduce((sum, order) => sum + order.total, 0)
|
|
152
|
+
|
|
153
|
+
export const calculateAverageOrderValue = (orders) => {
|
|
154
|
+
if (orders.length === 0) return 0
|
|
155
|
+
return calculateTotalRevenue(orders) / orders.length
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Anti-Patterns (REJECT)
|
|
160
|
+
|
|
161
|
+
```javascript
|
|
162
|
+
// Routes >500 lines → Split to business/
|
|
163
|
+
// Service layer files → Keep in-route or business/
|
|
164
|
+
// String concatenation SQL → Use {sql, params}
|
|
165
|
+
// Manual client init → Use middleware DI
|
|
166
|
+
// Validation files used by <3 routes → Keep in-route
|
|
167
|
+
// Abstraction layers → Direct implementation
|
|
168
|
+
// Complex error handling frameworks → Simple try/catch
|
|
169
|
+
// Over-engineered logging → Simple logger DI
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Testing Strategy
|
|
173
|
+
|
|
174
|
+
### Pure Business Logic Tests
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
import { calculateTotalRevenue } from '../calculations.js'
|
|
178
|
+
|
|
179
|
+
describe('calculateTotalRevenue', () => {
|
|
180
|
+
it('sums order totals', () => {
|
|
181
|
+
const orders = [{ total: 100 }, { total: 200 }, { total: 50 }]
|
|
182
|
+
expect(calculateTotalRevenue(orders)).toBe(350)
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
it('handles empty array', () => {
|
|
186
|
+
expect(calculateTotalRevenue([])).toBe(0)
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Route Integration Tests
|
|
192
|
+
|
|
193
|
+
```javascript
|
|
194
|
+
describe('GET /orders', () => {
|
|
195
|
+
const mockDb = {
|
|
196
|
+
queryWithParams: jest.fn().mockResolvedValue([{ id: 1, total: 100 }])
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
it('returns orders successfully', async () => {
|
|
200
|
+
const res = await app.request('/orders', { method: 'GET' }, { db: mockDb })
|
|
201
|
+
expect(res.status).toBe(200)
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
it('validates required parameters', async () => {
|
|
205
|
+
const res = await app.request('/orders?domain=invalid', { method: 'GET' }, { db: mockDb })
|
|
206
|
+
expect(res.status).toBe(400)
|
|
207
|
+
})
|
|
208
|
+
})
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Deep Dive**: See `references/middleware-patterns.md` for comprehensive testing patterns.
|
|
212
|
+
|
|
213
|
+
## Quality Gates
|
|
214
|
+
|
|
215
|
+
Before implementing any API endpoint:
|
|
216
|
+
|
|
217
|
+
1. **Security-first SQL**: Using `{sql, params}` pattern?
|
|
218
|
+
2. **Route size**: Can route be <500 lines?
|
|
219
|
+
3. **Validation**: Route-scoped closures or shared (3+ routes)?
|
|
220
|
+
4. **Middleware DI**: Using `c.db`, `c.bq`, `c.logger`?
|
|
221
|
+
5. **Pure business logic**: Extracted if reusable (3+ routes)?
|
|
222
|
+
6. **Testing**: Can inject mocks for all dependencies?
|
|
223
|
+
7. **FP principles**: Pure functions, explicit dependencies?
|
|
224
|
+
|
|
225
|
+
## When to Load Reference Files
|
|
226
|
+
|
|
227
|
+
### Security Deep-Dive
|
|
228
|
+
**File**: `references/security-sql.md`
|
|
229
|
+
**Load When**:
|
|
230
|
+
- Building complex multi-filter queries
|
|
231
|
+
- Implementing triple-layer curry pattern
|
|
232
|
+
- Training team on SQL security
|
|
233
|
+
- Debugging injection vulnerabilities
|
|
234
|
+
**Contains**: Parameterized queries, SQL builders, domain whitelisting, security checklist
|
|
235
|
+
|
|
236
|
+
### Middleware Patterns
|
|
237
|
+
**File**: `references/middleware-patterns.md`
|
|
238
|
+
**Load When**:
|
|
239
|
+
- Building custom middleware
|
|
240
|
+
- Implementing function factories for O(1) performance
|
|
241
|
+
- Setting up auth middleware
|
|
242
|
+
- Writing comprehensive integration tests
|
|
243
|
+
**Contains**: Context-based DI, function factories, auth patterns, testing with mocks
|
|
244
|
+
|
|
245
|
+
### Validation Strategies
|
|
246
|
+
**File**: `references/validation-patterns.md`
|
|
247
|
+
**Load When**:
|
|
248
|
+
- Implementing complex validation logic
|
|
249
|
+
- Building composable validators
|
|
250
|
+
- Deciding where to place validation code
|
|
251
|
+
- Implementing error accumulation
|
|
252
|
+
**Contains**: validateAll utility, composition patterns, result objects, when to extract
|
|
253
|
+
|
|
254
|
+
### Working Examples
|
|
255
|
+
**Directory**: `examples/`
|
|
256
|
+
**Load When**: Need complete working API examples
|
|
257
|
+
**Contains**: Full CRUD endpoints, authentication, authorization, testing
|
|
258
|
+
|
|
259
|
+
## Foundation Reference
|
|
260
|
+
|
|
261
|
+
**Core FP Principles**: `../js-fp/SKILL.md`
|
|
262
|
+
- Purity and side effect isolation
|
|
263
|
+
- Composition patterns
|
|
264
|
+
- Dependency injection
|
|
265
|
+
- Immutability
|
|
266
|
+
- Testing strategies
|
|
267
|
+
|
|
268
|
+
**Deep Dive**: `../js-fp/core-principles.md` for complete FP philosophy
|
|
269
|
+
|
|
270
|
+
## Success Metrics
|
|
271
|
+
|
|
272
|
+
- **Security**: Zero SQL injection vulnerabilities
|
|
273
|
+
- **Testability**: 95%+ coverage for pure functions
|
|
274
|
+
- **Maintainability**: Routes under 500 lines
|
|
275
|
+
- **Performance**: Sub-200ms response times
|
|
276
|
+
- **Code Quality**: Clear, self-documenting code
|
|
277
|
+
|
|
278
|
+
## Philosophy
|
|
279
|
+
|
|
280
|
+
*"Self-contained routes with security-first SQL and minimal abstraction - optimize for readability and security over clever architecture."*
|