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.
Files changed (182) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +463 -0
  3. package/dist/cli.js +1064 -0
  4. package/package.json +49 -0
  5. package/platforms/claude/adapter.ts +115 -0
  6. package/platforms/junie/adapter.ts +254 -0
  7. package/platforms/junie/agents-template.md +113 -0
  8. package/platforms/junie/hook-translations.md +84 -0
  9. package/platforms/shared/detector.ts +27 -0
  10. package/platforms/shared/installer.ts +202 -0
  11. package/platforms/shared/types.ts +78 -0
  12. package/plugins/ima-claude/.claude-plugin/plugin.json +25 -0
  13. package/plugins/ima-claude/agents/explorer.md +30 -0
  14. package/plugins/ima-claude/agents/implementer.md +30 -0
  15. package/plugins/ima-claude/agents/memory.md +42 -0
  16. package/plugins/ima-claude/agents/reviewer.md +53 -0
  17. package/plugins/ima-claude/agents/tester.md +33 -0
  18. package/plugins/ima-claude/agents/wp-developer.md +46 -0
  19. package/plugins/ima-claude/hooks/README.md +145 -0
  20. package/plugins/ima-claude/hooks/atlassian_prereqs.py +112 -0
  21. package/plugins/ima-claude/hooks/block_sed_edits.py +59 -0
  22. package/plugins/ima-claude/hooks/bootstrap.sh +90 -0
  23. package/plugins/ima-claude/hooks/bootstrap_utility_check.py +94 -0
  24. package/plugins/ima-claude/hooks/composer_autoload_check.py +70 -0
  25. package/plugins/ima-claude/hooks/docs_organization.py +104 -0
  26. package/plugins/ima-claude/hooks/enforce_rg_over_grep.py +56 -0
  27. package/plugins/ima-claude/hooks/fp_utility_check.py +90 -0
  28. package/plugins/ima-claude/hooks/hook_logger.py +69 -0
  29. package/plugins/ima-claude/hooks/hooks.json +239 -0
  30. package/plugins/ima-claude/hooks/jira_issue_fetch.py +79 -0
  31. package/plugins/ima-claude/hooks/jquery_in_wordpress.py +92 -0
  32. package/plugins/ima-claude/hooks/memory_bootstrap.py +79 -0
  33. package/plugins/ima-claude/hooks/memory_store_reminder.py +75 -0
  34. package/plugins/ima-claude/hooks/prompt_coach.py +125 -0
  35. package/plugins/ima-claude/hooks/prompt_coach_digest.md +48 -0
  36. package/plugins/ima-claude/hooks/prompt_coach_system.md +30 -0
  37. package/plugins/ima-claude/hooks/sequential_thinking_check.py +81 -0
  38. package/plugins/ima-claude/hooks/serena_over_grep.py +96 -0
  39. package/plugins/ima-claude/hooks/serena_over_read.py +66 -0
  40. package/plugins/ima-claude/hooks/serena_project_check.py +133 -0
  41. package/plugins/ima-claude/hooks/sql_injection_check.py +73 -0
  42. package/plugins/ima-claude/hooks/task_master_after_plan.py +31 -0
  43. package/plugins/ima-claude/hooks/task_master_before_impl.py +93 -0
  44. package/plugins/ima-claude/hooks/tavily_extract_advanced.py +48 -0
  45. package/plugins/ima-claude/hooks/vestige_before_external.py +86 -0
  46. package/plugins/ima-claude/hooks/webfetch_to_tavily.py +42 -0
  47. package/plugins/ima-claude/hooks/websearch_to_tavily.py +41 -0
  48. package/plugins/ima-claude/hooks/wp_security_check.py +150 -0
  49. package/plugins/ima-claude/personalities/README.md +45 -0
  50. package/plugins/ima-claude/personalities/enable-40k.md +69 -0
  51. package/plugins/ima-claude/personalities/enable-templars.md +69 -0
  52. package/plugins/ima-claude/skills/.research-summary.md +340 -0
  53. package/plugins/ima-claude/skills/architect/SKILL.md +304 -0
  54. package/plugins/ima-claude/skills/compound-bridge/SKILL.md +200 -0
  55. package/plugins/ima-claude/skills/discourse/SKILL.md +440 -0
  56. package/plugins/ima-claude/skills/discourse-admin/SKILL.md +192 -0
  57. package/plugins/ima-claude/skills/discourse-admin/references/api-endpoints.md +441 -0
  58. package/plugins/ima-claude/skills/discourse-admin/references/gotchas.md +107 -0
  59. package/plugins/ima-claude/skills/discourse-admin/references/staging-defaults.md +98 -0
  60. package/plugins/ima-claude/skills/discourse-admin/scripts/discourse-admin.py +319 -0
  61. package/plugins/ima-claude/skills/docs-organize/SKILL.md +254 -0
  62. package/plugins/ima-claude/skills/docs-organize/templates/active-README.md +50 -0
  63. package/plugins/ima-claude/skills/docs-organize/templates/archive-README.md +57 -0
  64. package/plugins/ima-claude/skills/docs-organize/templates/docs-README.md +43 -0
  65. package/plugins/ima-claude/skills/docs-organize/templates/phase-archive-README.md +83 -0
  66. package/plugins/ima-claude/skills/docs-organize/templates/section-README.md +48 -0
  67. package/plugins/ima-claude/skills/docs-organize/templates/transient-README.md +79 -0
  68. package/plugins/ima-claude/skills/docs-organize/templates/transient-gitignore +9 -0
  69. package/plugins/ima-claude/skills/ember-discourse/SKILL.md +496 -0
  70. package/plugins/ima-claude/skills/functional-programmer/SKILL.md +258 -0
  71. package/plugins/ima-claude/skills/ima-bootstrap/SKILL.md +278 -0
  72. package/plugins/ima-claude/skills/ima-bootstrap/references/bootstrap-patterns.md +356 -0
  73. package/plugins/ima-claude/skills/ima-bootstrap/references/ima-brand.md +273 -0
  74. package/plugins/ima-claude/skills/ima-bootstrap/references/theme-integration.md +212 -0
  75. package/plugins/ima-claude/skills/ima-brand/SKILL.md +108 -0
  76. package/plugins/ima-claude/skills/ima-brand/references/brand-identity.md +140 -0
  77. package/plugins/ima-claude/skills/ima-brand/references/digital-standards.md +180 -0
  78. package/plugins/ima-claude/skills/ima-brand/references/visual-system.md +173 -0
  79. package/plugins/ima-claude/skills/ima-forms-expert/SKILL.md +175 -0
  80. package/plugins/ima-claude/skills/ima-forms-expert/references/container-components.md +154 -0
  81. package/plugins/ima-claude/skills/ima-forms-expert/references/examples.md +328 -0
  82. package/plugins/ima-claude/skills/ima-forms-expert/references/field-components.md +298 -0
  83. package/plugins/ima-claude/skills/ima-forms-expert/references/form-factory.md +193 -0
  84. package/plugins/ima-claude/skills/ima-forms-expert/references/quick-reference.md +153 -0
  85. package/plugins/ima-claude/skills/ima-forms-expert/references/validation-engine.md +336 -0
  86. package/plugins/ima-claude/skills/jira-checkpoint/SKILL.md +178 -0
  87. package/plugins/ima-claude/skills/jquery/SKILL.md +413 -0
  88. package/plugins/ima-claude/skills/js-fp/SKILL.md +463 -0
  89. package/plugins/ima-claude/skills/js-fp/core-principles.md +487 -0
  90. package/plugins/ima-claude/skills/js-fp/examples/pure-functions.js +260 -0
  91. package/plugins/ima-claude/skills/js-fp/examples/tests/pure-functions.test.js +262 -0
  92. package/plugins/ima-claude/skills/js-fp/references/anti-patterns.md +120 -0
  93. package/plugins/ima-claude/skills/js-fp/references/performance-patterns.md +116 -0
  94. package/plugins/ima-claude/skills/js-fp/references/testing-patterns.md +134 -0
  95. package/plugins/ima-claude/skills/js-fp-api/SKILL.md +280 -0
  96. package/plugins/ima-claude/skills/js-fp-api/examples/crud-endpoint.js +258 -0
  97. package/plugins/ima-claude/skills/js-fp-api/references/middleware-patterns.md +134 -0
  98. package/plugins/ima-claude/skills/js-fp-api/references/security-sql.md +110 -0
  99. package/plugins/ima-claude/skills/js-fp-api/references/validation-patterns.md +165 -0
  100. package/plugins/ima-claude/skills/js-fp-react/SKILL.md +447 -0
  101. package/plugins/ima-claude/skills/js-fp-react/examples/ProductCard.tsx +65 -0
  102. package/plugins/ima-claude/skills/js-fp-react/references/hooks-advanced.md +136 -0
  103. package/plugins/ima-claude/skills/js-fp-react/references/performance-patterns.md +175 -0
  104. package/plugins/ima-claude/skills/js-fp-vue/SKILL.md +322 -0
  105. package/plugins/ima-claude/skills/js-fp-vue/references/complete-examples.md +397 -0
  106. package/plugins/ima-claude/skills/js-fp-vue/references/composables-advanced.md +282 -0
  107. package/plugins/ima-claude/skills/js-fp-vue/references/reactivity-patterns.md +348 -0
  108. package/plugins/ima-claude/skills/js-fp-vue/references/testing.md +314 -0
  109. package/plugins/ima-claude/skills/js-fp-wordpress/SKILL.md +301 -0
  110. package/plugins/ima-claude/skills/js-fp-wordpress/references/ajax-patterns.md +192 -0
  111. package/plugins/ima-claude/skills/js-fp-wordpress/references/event-patterns.md +136 -0
  112. package/plugins/ima-claude/skills/js-fp-wordpress/references/wp-integration.md +248 -0
  113. package/plugins/ima-claude/skills/livecanvas/SKILL.md +209 -0
  114. package/plugins/ima-claude/skills/livecanvas/references/livecanvas-features.md +311 -0
  115. package/plugins/ima-claude/skills/livecanvas/references/loops-and-logic.md +730 -0
  116. package/plugins/ima-claude/skills/livecanvas/references/picostrap.md +227 -0
  117. package/plugins/ima-claude/skills/mcp-atlassian/SKILL.md +339 -0
  118. package/plugins/ima-claude/skills/mcp-context7/SKILL.md +109 -0
  119. package/plugins/ima-claude/skills/mcp-memory/SKILL.md +182 -0
  120. package/plugins/ima-claude/skills/mcp-qdrant/SKILL.md +233 -0
  121. package/plugins/ima-claude/skills/mcp-sequential/SKILL.md +149 -0
  122. package/plugins/ima-claude/skills/mcp-serena/SKILL.md +174 -0
  123. package/plugins/ima-claude/skills/mcp-tavily/SKILL.md +118 -0
  124. package/plugins/ima-claude/skills/mcp-vestige/SKILL.md +259 -0
  125. package/plugins/ima-claude/skills/php-authnet/SKILL.md +275 -0
  126. package/plugins/ima-claude/skills/php-authnet/references/api-reference.md +624 -0
  127. package/plugins/ima-claude/skills/php-authnet/references/sandbox-testing.md +424 -0
  128. package/plugins/ima-claude/skills/php-fp/SKILL.md +333 -0
  129. package/plugins/ima-claude/skills/php-fp/examples/pure-functions.php +403 -0
  130. package/plugins/ima-claude/skills/php-fp/examples/tests/PureFunctionsTest.php +515 -0
  131. package/plugins/ima-claude/skills/php-fp/references/core-principles.md +277 -0
  132. package/plugins/ima-claude/skills/php-fp/references/testing-patterns.md +374 -0
  133. package/plugins/ima-claude/skills/php-fp-wordpress/SKILL.md +216 -0
  134. package/plugins/ima-claude/skills/php-fp-wordpress/references/fp-patterns.md +275 -0
  135. package/plugins/ima-claude/skills/php-fp-wordpress/references/plugin-architecture.md +295 -0
  136. package/plugins/ima-claude/skills/php-fp-wordpress/references/security-examples.md +203 -0
  137. package/plugins/ima-claude/skills/php-fp-wordpress/references/testing-strategy.md +259 -0
  138. package/plugins/ima-claude/skills/phpunit-wp/SKILL.md +716 -0
  139. package/plugins/ima-claude/skills/playwright/SKILL.md +434 -0
  140. package/plugins/ima-claude/skills/playwright/references/accessibility-testing.md +153 -0
  141. package/plugins/ima-claude/skills/playwright/references/ci-cd.md +268 -0
  142. package/plugins/ima-claude/skills/playwright/references/network-mocking.md +270 -0
  143. package/plugins/ima-claude/skills/playwright/references/visual-regression.md +215 -0
  144. package/plugins/ima-claude/skills/py-fp/SKILL.md +663 -0
  145. package/plugins/ima-claude/skills/py-fp/examples/pure-functions.py +185 -0
  146. package/plugins/ima-claude/skills/py-fp/examples/tests/test_pure_functions.py +244 -0
  147. package/plugins/ima-claude/skills/py-fp/references/core-principles.md +381 -0
  148. package/plugins/ima-claude/skills/py-fp/references/testing-patterns.md +283 -0
  149. package/plugins/ima-claude/skills/quasar-fp/SKILL.md +327 -0
  150. package/plugins/ima-claude/skills/quasar-fp/metadata.json +85 -0
  151. package/plugins/ima-claude/skills/quasar-fp/references/component-patterns.md +257 -0
  152. package/plugins/ima-claude/skills/quasar-fp/references/theme-integration.md +233 -0
  153. package/plugins/ima-claude/skills/quasar-fp/references/utility-classes.md +237 -0
  154. package/plugins/ima-claude/skills/quickstart/SKILL.md +129 -0
  155. package/plugins/ima-claude/skills/rails/SKILL.md +359 -0
  156. package/plugins/ima-claude/skills/resume-session/SKILL.md +68 -0
  157. package/plugins/ima-claude/skills/rg/SKILL.md +205 -0
  158. package/plugins/ima-claude/skills/ruby-fp/SKILL.md +336 -0
  159. package/plugins/ima-claude/skills/save-session/SKILL.md +81 -0
  160. package/plugins/ima-claude/skills/scorecard/SKILL.md +96 -0
  161. package/plugins/ima-claude/skills/skill-analyzer/SKILL.md +127 -0
  162. package/plugins/ima-claude/skills/skill-analyzer/references/advanced-checklist.md +44 -0
  163. package/plugins/ima-claude/skills/skill-analyzer/references/core-checklist.md +60 -0
  164. package/plugins/ima-claude/skills/skill-analyzer/scripts/analyze_skill.py +418 -0
  165. package/plugins/ima-claude/skills/skill-creator/LICENSE.txt +202 -0
  166. package/plugins/ima-claude/skills/skill-creator/SKILL.md +343 -0
  167. package/plugins/ima-claude/skills/skill-creator/references/output-patterns.md +82 -0
  168. package/plugins/ima-claude/skills/skill-creator/references/workflows.md +28 -0
  169. package/plugins/ima-claude/skills/skill-creator/scripts/init_skill.py +303 -0
  170. package/plugins/ima-claude/skills/skill-creator/scripts/package_skill.py +110 -0
  171. package/plugins/ima-claude/skills/skill-creator/scripts/quick_validate.py +103 -0
  172. package/plugins/ima-claude/skills/task-master/SKILL.md +51 -0
  173. package/plugins/ima-claude/skills/task-planner/SKILL.md +228 -0
  174. package/plugins/ima-claude/skills/task-runner/SKILL.md +192 -0
  175. package/plugins/ima-claude/skills/unit-testing/SKILL.md +198 -0
  176. package/plugins/ima-claude/skills/unit-testing/references/mock-patterns.md +181 -0
  177. package/plugins/ima-claude/skills/unit-testing/references/tdd-workflow.md +177 -0
  178. package/plugins/ima-claude/skills/unit-testing/references/test-strategy.md +126 -0
  179. package/plugins/ima-claude/skills/wp-local/SKILL.md +246 -0
  180. package/plugins/ima-claude/skills/wp-local/references/configuration.md +198 -0
  181. package/plugins/ima-claude/skills/wp-local/references/wp-cli-reference.md +406 -0
  182. package/plugins/ima-claude/skills/wp-local/scripts/wp-local.sh +61 -0
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Pure Functions Examples
3
+ *
4
+ * Demonstrating core FP concepts with practical examples
5
+ */
6
+
7
+ // ============================================================================
8
+ // 1. BASIC PURITY
9
+ // ============================================================================
10
+
11
+ // ❌ Impure: Depends on external state
12
+ let taxRate = 0.1
13
+ const calculateTotalImpure = (price) => price * (1 + taxRate)
14
+
15
+ // ✅ Pure: All dependencies explicit
16
+ const calculateTotal = (price, taxRate) => price * (1 + taxRate)
17
+
18
+ // ✅ Pure with pre-configuration
19
+ const createTaxCalculator = (taxRate) => (price) => price * (1 + taxRate)
20
+ const calculateWithTax = createTaxCalculator(0.1)
21
+
22
+ // ============================================================================
23
+ // 2. COMPOSITION WITHOUT UTILITIES
24
+ // ============================================================================
25
+
26
+ // Data validation example
27
+ const validateRequired = (value) => value != null && value !== ''
28
+ const validateEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)
29
+ const validateLength = (min, max) => (value) =>
30
+ value.length >= min && value.length <= max
31
+
32
+ // Compose with early returns (no pipe utility needed)
33
+ const validateUserEmail = (email) => {
34
+ if (!validateRequired(email)) {
35
+ return { valid: false, error: 'Email is required' }
36
+ }
37
+
38
+ if (!validateEmail(email)) {
39
+ return { valid: false, error: 'Invalid email format' }
40
+ }
41
+
42
+ if (!validateLength(5, 100)(email)) {
43
+ return { valid: false, error: 'Email must be 5-100 characters' }
44
+ }
45
+
46
+ return { valid: true, value: email }
47
+ }
48
+
49
+ // ============================================================================
50
+ // 3. DEPENDENCY INJECTION
51
+ // ============================================================================
52
+
53
+ // Pure business logic
54
+ const hashPassword = (password, hasher) => hasher.hash(password)
55
+ const saveToDatabase = (data, database) => database.save(data)
56
+
57
+ // Compose with explicit dependencies
58
+ const saveUser = async (userData, hasher, database) => {
59
+ const hashedPassword = await hashPassword(userData.password, hasher)
60
+ const userToSave = { ...userData, password: hashedPassword }
61
+ return saveToDatabase(userToSave, database)
62
+ }
63
+
64
+ // Function factory for convenience
65
+ const createUserService = (hasher, database) => ({
66
+ save: (userData) => saveUser(userData, hasher, database),
67
+ find: (id) => database.findById(id)
68
+ })
69
+
70
+ // ============================================================================
71
+ // 4. IMMUTABILITY
72
+ // ============================================================================
73
+
74
+ // Pure array operations
75
+ const users = [
76
+ { id: 1, name: 'Alice', active: true },
77
+ { id: 2, name: 'Bob', active: false },
78
+ { id: 3, name: 'Charlie', active: true }
79
+ ]
80
+
81
+ // Add user (immutable)
82
+ const addUser = (users, newUser) => [...users, newUser]
83
+
84
+ // Remove user (immutable)
85
+ const removeUser = (users, userId) =>
86
+ users.filter(user => user.id !== userId)
87
+
88
+ // Update user (immutable)
89
+ const updateUser = (users, userId, updates) =>
90
+ users.map(user =>
91
+ user.id === userId ? { ...user, ...updates } : user
92
+ )
93
+
94
+ // Filter active users
95
+ const getActiveUsers = (users) => users.filter(user => user.active)
96
+
97
+ // ============================================================================
98
+ // 5. DATA TRANSFORMATION PIPELINE
99
+ // ============================================================================
100
+
101
+ // Transform user data without utilities
102
+ const processUserData = (rawData) => {
103
+ // Normalize
104
+ const normalized = {
105
+ id: rawData.id,
106
+ name: rawData.name?.trim() || '',
107
+ email: rawData.email?.toLowerCase() || ''
108
+ }
109
+
110
+ // Validate
111
+ const emailValidation = validateUserEmail(normalized.email)
112
+ if (!emailValidation.valid) {
113
+ return { valid: false, error: emailValidation.error }
114
+ }
115
+
116
+ // Enhance
117
+ const enhanced = {
118
+ ...normalized,
119
+ displayName: normalized.name || 'Anonymous',
120
+ createdAt: new Date().toISOString()
121
+ }
122
+
123
+ return { valid: true, data: enhanced }
124
+ }
125
+
126
+ // ============================================================================
127
+ // 6. ERROR HANDLING
128
+ // ============================================================================
129
+
130
+ // Result type pattern
131
+ const divide = (a, b) =>
132
+ b === 0
133
+ ? { success: false, error: 'Division by zero' }
134
+ : { success: true, data: a / b }
135
+
136
+ // Chain operations with early returns
137
+ const calculate = (a, b, c) => {
138
+ const step1 = divide(a, b)
139
+ if (!step1.success) return step1
140
+
141
+ const step2 = divide(step1.data, c)
142
+ return step2
143
+ }
144
+
145
+ // Try-catch wrapper for async
146
+ const tryCatch = (fn) => async (...args) => {
147
+ try {
148
+ const data = await fn(...args)
149
+ return { success: true, data }
150
+ } catch (error) {
151
+ return { success: false, error: error.message }
152
+ }
153
+ }
154
+
155
+ // ============================================================================
156
+ // 7. CONFIGURATION PRE-COMPILATION (Performance)
157
+ // ============================================================================
158
+
159
+ // Problem: O(items × validators) - repeated config access
160
+ const validateItemsSlow = (items, validators) => {
161
+ return items.map(item => {
162
+ const errors = []
163
+ for (const validator of validators) {
164
+ if (!validator.validate(item[validator.field])) {
165
+ errors.push(validator.message)
166
+ }
167
+ }
168
+ return { item, valid: errors.length === 0, errors }
169
+ })
170
+ }
171
+
172
+ // Solution: O(validators + items) - pre-compiled
173
+ const createItemValidator = (validators) => {
174
+ // Pre-compile validator functions
175
+ const compiledValidators = validators.map(v => ({
176
+ field: v.field,
177
+ validate: v.validate,
178
+ message: v.message
179
+ }))
180
+
181
+ return (item) => {
182
+ const errors = []
183
+ for (const validator of compiledValidators) {
184
+ if (!validator.validate(item[validator.field])) {
185
+ errors.push(validator.message)
186
+ }
187
+ }
188
+ return { item, valid: errors.length === 0, errors }
189
+ }
190
+ }
191
+
192
+ const validateItemsFast = (items, validators) => {
193
+ const validator = createItemValidator(validators)
194
+ return items.map(validator)
195
+ }
196
+
197
+ // ============================================================================
198
+ // 8. MEMOIZATION (Pure Functions Only)
199
+ // ============================================================================
200
+
201
+ const memoize = (fn) => {
202
+ const cache = new Map()
203
+ return (...args) => {
204
+ const key = JSON.stringify(args)
205
+ if (cache.has(key)) return cache.get(key)
206
+ const result = fn(...args)
207
+ cache.set(key, result)
208
+ return result
209
+ }
210
+ }
211
+
212
+ // Expensive calculation (pure)
213
+ const fibonacci = (n) => {
214
+ if (n <= 1) return n
215
+ return fibonacci(n - 1) + fibonacci(n - 2)
216
+ }
217
+
218
+ const fibonacciMemoized = memoize(fibonacci)
219
+
220
+ // ============================================================================
221
+ // EXPORTS FOR TESTING
222
+ // ============================================================================
223
+
224
+ module.exports = {
225
+ // Basic purity
226
+ calculateTotal,
227
+ createTaxCalculator,
228
+
229
+ // Composition
230
+ validateRequired,
231
+ validateEmail,
232
+ validateLength,
233
+ validateUserEmail,
234
+
235
+ // Dependency injection
236
+ saveUser,
237
+ createUserService,
238
+
239
+ // Immutability
240
+ addUser,
241
+ removeUser,
242
+ updateUser,
243
+ getActiveUsers,
244
+
245
+ // Data transformation
246
+ processUserData,
247
+
248
+ // Error handling
249
+ divide,
250
+ calculate,
251
+ tryCatch,
252
+
253
+ // Performance
254
+ createItemValidator,
255
+ validateItemsFast,
256
+
257
+ // Memoization
258
+ memoize,
259
+ fibonacciMemoized
260
+ }
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Pure Functions Test Suite
3
+ *
4
+ * Demonstrating comprehensive testing enabled by purity
5
+ */
6
+
7
+ const {
8
+ calculateTotal,
9
+ createTaxCalculator,
10
+ validateRequired,
11
+ validateEmail,
12
+ validateUserEmail,
13
+ addUser,
14
+ removeUser,
15
+ updateUser,
16
+ divide,
17
+ calculate,
18
+ memoize
19
+ } = require('../pure-functions')
20
+
21
+ describe('Pure Functions - Comprehensive Testing', () => {
22
+ // ============================================================================
23
+ // BASIC PURITY TESTS
24
+ // ============================================================================
25
+
26
+ describe('calculateTotal', () => {
27
+ it('calculates total with tax', () => {
28
+ expect(calculateTotal(100, 0.1)).toBe(110)
29
+ expect(calculateTotal(50, 0.2)).toBe(60)
30
+ })
31
+
32
+ it('handles zero price', () => {
33
+ expect(calculateTotal(0, 0.1)).toBe(0)
34
+ })
35
+
36
+ it('handles zero tax rate', () => {
37
+ expect(calculateTotal(100, 0)).toBe(100)
38
+ })
39
+ })
40
+
41
+ describe('createTaxCalculator', () => {
42
+ it('creates specialized calculator', () => {
43
+ const calc = createTaxCalculator(0.1)
44
+ expect(calc(100)).toBe(110)
45
+ expect(calc(50)).toBe(55)
46
+ })
47
+ })
48
+
49
+ // ============================================================================
50
+ // VALIDATION TESTS (All Edge Cases)
51
+ // ============================================================================
52
+
53
+ describe('validateRequired', () => {
54
+ it('validates non-empty values', () => {
55
+ expect(validateRequired('test')).toBe(true)
56
+ expect(validateRequired(0)).toBe(true)
57
+ expect(validateRequired(false)).toBe(true)
58
+ })
59
+
60
+ it('rejects empty values', () => {
61
+ expect(validateRequired('')).toBe(false)
62
+ expect(validateRequired(null)).toBe(false)
63
+ expect(validateRequired(undefined)).toBe(false)
64
+ })
65
+ })
66
+
67
+ describe('validateEmail', () => {
68
+ it('validates correct emails', () => {
69
+ expect(validateEmail('test@example.com')).toBe(true)
70
+ expect(validateEmail('user.name@domain.co.uk')).toBe(true)
71
+ })
72
+
73
+ it('rejects invalid emails', () => {
74
+ expect(validateEmail('invalid')).toBe(false)
75
+ expect(validateEmail('missing@domain')).toBe(false)
76
+ expect(validateEmail('@example.com')).toBe(false)
77
+ })
78
+ })
79
+
80
+ describe('validateUserEmail - Composition', () => {
81
+ it('validates complete email', () => {
82
+ const result = validateUserEmail('test@example.com')
83
+ expect(result.valid).toBe(true)
84
+ expect(result.value).toBe('test@example.com')
85
+ })
86
+
87
+ it('rejects missing email', () => {
88
+ const result = validateUserEmail('')
89
+ expect(result.valid).toBe(false)
90
+ expect(result.error).toBe('Email is required')
91
+ })
92
+
93
+ it('rejects invalid format', () => {
94
+ const result = validateUserEmail('invalid')
95
+ expect(result.valid).toBe(false)
96
+ expect(result.error).toBe('Invalid email format')
97
+ })
98
+
99
+ it('rejects too short', () => {
100
+ const result = validateUserEmail('a@b')
101
+ expect(result.valid).toBe(false)
102
+ expect(result.error).toBe('Email must be 5-100 characters')
103
+ })
104
+
105
+ it('rejects too long', () => {
106
+ const longEmail = 'a'.repeat(100) + '@example.com'
107
+ const result = validateUserEmail(longEmail)
108
+ expect(result.valid).toBe(false)
109
+ expect(result.error).toBe('Email must be 5-100 characters')
110
+ })
111
+ })
112
+
113
+ // ============================================================================
114
+ // IMMUTABILITY TESTS
115
+ // ============================================================================
116
+
117
+ describe('Immutable Array Operations', () => {
118
+ const users = [
119
+ { id: 1, name: 'Alice', active: true },
120
+ { id: 2, name: 'Bob', active: false }
121
+ ]
122
+
123
+ describe('addUser', () => {
124
+ it('adds user without mutation', () => {
125
+ const newUser = { id: 3, name: 'Charlie', active: true }
126
+ const result = addUser(users, newUser)
127
+
128
+ expect(result).toHaveLength(3)
129
+ expect(result[2]).toEqual(newUser)
130
+ expect(users).toHaveLength(2) // Original unchanged
131
+ })
132
+ })
133
+
134
+ describe('removeUser', () => {
135
+ it('removes user without mutation', () => {
136
+ const result = removeUser(users, 1)
137
+
138
+ expect(result).toHaveLength(1)
139
+ expect(result[0].id).toBe(2)
140
+ expect(users).toHaveLength(2) // Original unchanged
141
+ })
142
+ })
143
+
144
+ describe('updateUser', () => {
145
+ it('updates user without mutation', () => {
146
+ const result = updateUser(users, 1, { name: 'Alice Updated' })
147
+
148
+ expect(result[0].name).toBe('Alice Updated')
149
+ expect(users[0].name).toBe('Alice') // Original unchanged
150
+ })
151
+
152
+ it('returns same array if user not found', () => {
153
+ const result = updateUser(users, 999, { name: 'Not Found' })
154
+
155
+ expect(result).toHaveLength(2)
156
+ expect(result).toEqual(users)
157
+ })
158
+ })
159
+ })
160
+
161
+ // ============================================================================
162
+ // ERROR HANDLING TESTS
163
+ // ============================================================================
164
+
165
+ describe('divide', () => {
166
+ it('divides numbers successfully', () => {
167
+ const result = divide(10, 2)
168
+ expect(result.success).toBe(true)
169
+ expect(result.data).toBe(5)
170
+ })
171
+
172
+ it('handles division by zero', () => {
173
+ const result = divide(10, 0)
174
+ expect(result.success).toBe(false)
175
+ expect(result.error).toBe('Division by zero')
176
+ })
177
+ })
178
+
179
+ describe('calculate - Chained Operations', () => {
180
+ it('calculates successfully', () => {
181
+ const result = calculate(20, 2, 5)
182
+ expect(result.success).toBe(true)
183
+ expect(result.data).toBe(2) // (20 / 2) / 5 = 2
184
+ })
185
+
186
+ it('short-circuits on first error', () => {
187
+ const result = calculate(20, 0, 5)
188
+ expect(result.success).toBe(false)
189
+ expect(result.error).toBe('Division by zero')
190
+ })
191
+
192
+ it('short-circuits on second error', () => {
193
+ const result = calculate(20, 2, 0)
194
+ expect(result.success).toBe(false)
195
+ expect(result.error).toBe('Division by zero')
196
+ })
197
+ })
198
+
199
+ // ============================================================================
200
+ // SYSTEMATIC EDGE CASE TESTING
201
+ // ============================================================================
202
+
203
+ describe('Edge Cases - All JavaScript Types', () => {
204
+ const allTypes = [
205
+ null,
206
+ undefined,
207
+ true,
208
+ false,
209
+ 0,
210
+ 1,
211
+ -1,
212
+ '',
213
+ 'string',
214
+ [],
215
+ [1, 2],
216
+ {},
217
+ { key: 'value' },
218
+ NaN,
219
+ Infinity,
220
+ -Infinity
221
+ ]
222
+
223
+ describe('validateRequired - handles all types', () => {
224
+ allTypes.forEach(input => {
225
+ it(`handles ${typeof input}: ${JSON.stringify(input)}`, () => {
226
+ expect(() => validateRequired(input)).not.toThrow()
227
+ })
228
+ })
229
+ })
230
+ })
231
+
232
+ // ============================================================================
233
+ // MEMOIZATION TESTS
234
+ // ============================================================================
235
+
236
+ describe('memoize', () => {
237
+ it('caches results', () => {
238
+ const expensive = jest.fn((n) => n * 2)
239
+ const memoized = memoize(expensive)
240
+
241
+ expect(memoized(5)).toBe(10)
242
+ expect(expensive).toHaveBeenCalledTimes(1)
243
+
244
+ expect(memoized(5)).toBe(10)
245
+ expect(expensive).toHaveBeenCalledTimes(1) // Still 1, cached
246
+
247
+ expect(memoized(10)).toBe(20)
248
+ expect(expensive).toHaveBeenCalledTimes(2) // New input
249
+ })
250
+
251
+ it('works with multiple arguments', () => {
252
+ const add = jest.fn((a, b) => a + b)
253
+ const memoized = memoize(add)
254
+
255
+ expect(memoized(2, 3)).toBe(5)
256
+ expect(add).toHaveBeenCalledTimes(1)
257
+
258
+ expect(memoized(2, 3)).toBe(5)
259
+ expect(add).toHaveBeenCalledTimes(1) // Cached
260
+ })
261
+ })
262
+ })
@@ -0,0 +1,120 @@
1
+ # Anti-Patterns Reference
2
+
3
+ Common FP anti-patterns to avoid.
4
+
5
+ ## 1. Impure Functions Disguised as Pure
6
+
7
+ ```javascript
8
+ // ANTI-PATTERN: Hidden mutation
9
+ function processUser(user) {
10
+ user.lastProcessed = new Date() // Mutation!
11
+ return validateUser(user)
12
+ }
13
+
14
+ // CORRECT: Returns new object
15
+ function processUser(user) {
16
+ return {
17
+ ...user,
18
+ lastProcessed: new Date()
19
+ }
20
+ }
21
+ ```
22
+
23
+ ## 2. Configuration in Hot Paths
24
+
25
+ ```javascript
26
+ // ANTI-PATTERN: O(records x fields) - schema accessed every iteration
27
+ function processRecords(records, schema) {
28
+ return records.map(record => {
29
+ return schema.fields.reduce((obj, field) => {
30
+ obj[field.name] = transformField(record[field.name], field.type)
31
+ return obj
32
+ }, {})
33
+ })
34
+ }
35
+
36
+ // CORRECT: Pre-compile the transformation
37
+ function createRecordProcessor(schema) {
38
+ const transformers = schema.fields.map(field =>
39
+ value => transformField(value, field.type)
40
+ )
41
+ return record => transformers.reduce((obj, transform, i) => {
42
+ obj[schema.fields[i].name] = transform(record[schema.fields[i].name])
43
+ return obj
44
+ }, {})
45
+ }
46
+ const processor = createRecordProcessor(schema) // Setup once
47
+ const results = records.map(processor) // Fast execution
48
+ ```
49
+
50
+ ## 3. Over-Engineering Simple Cases
51
+
52
+ ```javascript
53
+ // ANTI-PATTERN: Complex machinery for simple validation
54
+ const validateEmail = createAdvancedValidator({
55
+ type: 'email',
56
+ rules: [createRule('format', emailRegex), createRule('length', {min: 5, max: 254})],
57
+ transformers: [createTransformer('lowercase'), createTransformer('trim')]
58
+ })
59
+
60
+ // CORRECT: Simple, direct approach
61
+ const validateEmail = (email) => {
62
+ const trimmed = email.trim().toLowerCase()
63
+ return trimmed.length >= 5 && trimmed.length <= 254 && emailRegex.test(trimmed)
64
+ }
65
+ ```
66
+
67
+ ## 4. Inline Validation Mixed with Business Logic
68
+
69
+ ```javascript
70
+ // ANTI-PATTERN
71
+ if (!body || !body.email) {
72
+ return errorResponse(c, 400, 'Email is required')
73
+ }
74
+ // ... more business logic
75
+
76
+ // CORRECT: Separate validation function
77
+ const validateAuthRequest = (body) => {
78
+ if (!body || !body.email) throw new ValidationError('Email is required')
79
+ return { email: body.email }
80
+ }
81
+ ```
82
+
83
+ ## 5. Per-Request Service Instantiation
84
+
85
+ ```javascript
86
+ // ANTI-PATTERN
87
+ authRoutes.post('/request', async (c) => {
88
+ const authService = new AuthService(env) // Created every request!
89
+ // ...
90
+ })
91
+
92
+ // CORRECT: Middleware DI or service container
93
+ app.use(ctx => { ctx.authService = authService }) // Created once
94
+ ```
95
+
96
+ ## 6. Creating Custom FP Utilities
97
+
98
+ > **Note**: This is about not CREATING your own FP utilities. Using established libraries (lodash, Ramda, etc.) is perfectly fine.
99
+
100
+ ```javascript
101
+ // ANTI-PATTERN: Creating your own pipe/compose/curry utilities
102
+ const pipe = (...fns) => x => fns.reduce((v, f) => f(v), x)
103
+ const compose = (...fns) => x => fns.reduceRight((v, f) => f(v), x)
104
+
105
+ // CORRECT: Native function calls with early returns
106
+ const validateUser = (userData) => {
107
+ const requiredCheck = validateRequired(['email', 'name'])(userData)
108
+ if (!requiredCheck.valid) return requiredCheck
109
+ return validateEmail(userData)
110
+ }
111
+ ```
112
+
113
+ ## Quick Checklist
114
+
115
+ - [ ] No hidden mutations in function bodies
116
+ - [ ] Configuration accessed once, not per-item
117
+ - [ ] Complexity matches the problem size
118
+ - [ ] Validation separated from business logic
119
+ - [ ] Services instantiated once, injected via context
120
+ - [ ] No custom FP utility functions (pipe, compose, curry)