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,397 @@
1
+ # Complete Component Examples
2
+
3
+ Full working examples demonstrating Vue FP patterns.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [Product Card Component](#product-card-component)
8
+ 2. [User Dashboard with Wrapper](#user-dashboard-with-wrapper)
9
+ 3. [Form with Validation](#form-with-validation)
10
+
11
+ ---
12
+
13
+ ## Product Card Component
14
+
15
+ Complete example with pure composable, component, and styling.
16
+
17
+ ```vue
18
+ <!-- ProductCard.vue -->
19
+ <script setup lang="ts">
20
+ import type { Ref } from 'vue'
21
+ import { computed, readonly, toRef } from 'vue'
22
+
23
+ interface Product {
24
+ id: string
25
+ name: string
26
+ price: number
27
+ inStock: boolean
28
+ }
29
+
30
+ // ───── Pure composable ─────
31
+ const useProductLogic = (product: Readonly<Ref<Product>>) => {
32
+ const displayData = computed(() => ({
33
+ ...product.value,
34
+ formattedPrice: `$${product.value.price.toFixed(2)}`,
35
+ availability: product.value.inStock ? 'In Stock' : 'Out of Stock'
36
+ }))
37
+
38
+ const cssClasses = computed(() => ({
39
+ card: `product-card ${product.value.inStock ? 'in-stock' : 'out-of-stock'}`,
40
+ price: `product-price ${product.value.inStock ? 'available' : 'unavailable'}`
41
+ }))
42
+
43
+ return { displayData, cssClasses }
44
+ }
45
+
46
+ // ───── Component setup ─────
47
+ const props = defineProps<{
48
+ product: Product
49
+ }>()
50
+
51
+ const emit = defineEmits<{
52
+ addToCart: [productId: string]
53
+ }>()
54
+
55
+ const { displayData, cssClasses } = useProductLogic(
56
+ readonly(toRef(props, 'product'))
57
+ )
58
+
59
+ const handleAddToCart = () => {
60
+ if (props.product.inStock) {
61
+ emit('addToCart', props.product.id)
62
+ }
63
+ }
64
+ </script>
65
+
66
+ <template>
67
+ <div :class="cssClasses.card">
68
+ <h3>{{ displayData.name }}</h3>
69
+ <p :class="cssClasses.price">{{ displayData.formattedPrice }}</p>
70
+ <p class="availability">{{ displayData.availability }}</p>
71
+ <button
72
+ @click="handleAddToCart"
73
+ :disabled="!product.inStock"
74
+ >
75
+ Add to Cart
76
+ </button>
77
+ </div>
78
+ </template>
79
+
80
+ <style scoped>
81
+ .product-card {
82
+ border: 1px solid #ddd;
83
+ padding: 1rem;
84
+ border-radius: 8px;
85
+ }
86
+
87
+ .product-card.in-stock {
88
+ border-color: #28a745;
89
+ }
90
+
91
+ .product-card.out-of-stock {
92
+ opacity: 0.6;
93
+ }
94
+
95
+ .product-price.unavailable {
96
+ text-decoration: line-through;
97
+ }
98
+ </style>
99
+ ```
100
+
101
+ ---
102
+
103
+ ## User Dashboard with Wrapper
104
+
105
+ Pure component + wrapper pattern with API integration.
106
+
107
+ ### Pure Component
108
+
109
+ ```vue
110
+ <!-- UserListPure.vue -->
111
+ <script setup lang="ts">
112
+ interface User {
113
+ id: string
114
+ name: string
115
+ email: string
116
+ role: 'admin' | 'user'
117
+ }
118
+
119
+ defineProps<{
120
+ users: User[]
121
+ selectedId: string | null
122
+ }>()
123
+
124
+ const emit = defineEmits<{
125
+ select: [userId: string]
126
+ delete: [userId: string]
127
+ }>()
128
+ </script>
129
+
130
+ <template>
131
+ <ul class="user-list">
132
+ <li
133
+ v-for="user in users"
134
+ :key="user.id"
135
+ :class="{ selected: user.id === selectedId }"
136
+ @click="emit('select', user.id)"
137
+ >
138
+ <span class="name">{{ user.name }}</span>
139
+ <span class="email">{{ user.email }}</span>
140
+ <span :class="['role', user.role]">{{ user.role }}</span>
141
+ <button @click.stop="emit('delete', user.id)">Delete</button>
142
+ </li>
143
+ </ul>
144
+ </template>
145
+ ```
146
+
147
+ ### Wrapper Component
148
+
149
+ ```vue
150
+ <!-- UserDashboard.vue -->
151
+ <script setup lang="ts">
152
+ import { ref, onMounted } from 'vue'
153
+ import UserListPure from './UserListPure.vue'
154
+
155
+ interface User {
156
+ id: string
157
+ name: string
158
+ email: string
159
+ role: 'admin' | 'user'
160
+ }
161
+
162
+ // Injected or imported API
163
+ const userApi = {
164
+ getUsers: async (): Promise<User[]> => {
165
+ const response = await fetch('/api/users')
166
+ return response.json()
167
+ },
168
+ deleteUser: async (id: string): Promise<void> => {
169
+ await fetch(`/api/users/${id}`, { method: 'DELETE' })
170
+ }
171
+ }
172
+
173
+ // State
174
+ const users = ref<User[]>([])
175
+ const selectedId = ref<string | null>(null)
176
+ const loading = ref(true)
177
+ const error = ref<Error | null>(null)
178
+
179
+ // Side effects isolated to wrapper
180
+ const fetchUsers = async () => {
181
+ loading.value = true
182
+ error.value = null
183
+ try {
184
+ users.value = await userApi.getUsers()
185
+ } catch (e) {
186
+ error.value = e as Error
187
+ } finally {
188
+ loading.value = false
189
+ }
190
+ }
191
+
192
+ const handleSelect = (userId: string) => {
193
+ selectedId.value = userId
194
+ }
195
+
196
+ const handleDelete = async (userId: string) => {
197
+ try {
198
+ await userApi.deleteUser(userId)
199
+ await fetchUsers() // Refresh list
200
+ if (selectedId.value === userId) {
201
+ selectedId.value = null
202
+ }
203
+ } catch (e) {
204
+ error.value = e as Error
205
+ }
206
+ }
207
+
208
+ onMounted(fetchUsers)
209
+ </script>
210
+
211
+ <template>
212
+ <div class="user-dashboard">
213
+ <h2>Users</h2>
214
+
215
+ <div v-if="loading" class="loading">Loading users...</div>
216
+
217
+ <div v-else-if="error" class="error">
218
+ Error: {{ error.message }}
219
+ <button @click="fetchUsers">Retry</button>
220
+ </div>
221
+
222
+ <UserListPure
223
+ v-else
224
+ :users="users"
225
+ :selected-id="selectedId"
226
+ @select="handleSelect"
227
+ @delete="handleDelete"
228
+ />
229
+ </div>
230
+ </template>
231
+ ```
232
+
233
+ ---
234
+
235
+ ## Form with Validation
236
+
237
+ Pure validation logic with form component.
238
+
239
+ ### Validation Composable
240
+
241
+ ```typescript
242
+ // composables/useFormValidation.ts
243
+ import type { Ref } from 'vue'
244
+ import { computed, ref } from 'vue'
245
+
246
+ type Validator<T> = (value: T) => string | null
247
+
248
+ interface FieldConfig<T> {
249
+ value: Ref<T>
250
+ validators: Validator<T>[]
251
+ }
252
+
253
+ export const useFormValidation = <T extends Record<string, unknown>>(
254
+ fields: { [K in keyof T]: FieldConfig<T[K]> }
255
+ ) => {
256
+ const touched = ref<Record<keyof T, boolean>>(
257
+ Object.keys(fields).reduce((acc, key) => ({ ...acc, [key]: false }), {} as Record<keyof T, boolean>)
258
+ )
259
+
260
+ const errors = computed(() => {
261
+ const result: Partial<Record<keyof T, string>> = {}
262
+
263
+ for (const [key, config] of Object.entries(fields) as [keyof T, FieldConfig<T[keyof T]>][]) {
264
+ for (const validator of config.validators) {
265
+ const error = validator(config.value.value)
266
+ if (error) {
267
+ result[key] = error
268
+ break
269
+ }
270
+ }
271
+ }
272
+
273
+ return result
274
+ })
275
+
276
+ const isValid = computed(() => Object.keys(errors.value).length === 0)
277
+
278
+ const touch = (field: keyof T) => {
279
+ touched.value[field] = true
280
+ }
281
+
282
+ const touchAll = () => {
283
+ for (const key of Object.keys(fields)) {
284
+ touched.value[key as keyof T] = true
285
+ }
286
+ }
287
+
288
+ const getError = (field: keyof T) => {
289
+ return touched.value[field] ? errors.value[field] : null
290
+ }
291
+
292
+ return { errors, isValid, touched, touch, touchAll, getError }
293
+ }
294
+
295
+ // Common validators
296
+ export const required = (msg = 'Required'): Validator<string> =>
297
+ (value) => value.trim() ? null : msg
298
+
299
+ export const minLength = (min: number, msg?: string): Validator<string> =>
300
+ (value) => value.length >= min ? null : msg ?? `Min ${min} characters`
301
+
302
+ export const email = (msg = 'Invalid email'): Validator<string> =>
303
+ (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) ? null : msg
304
+ ```
305
+
306
+ ### Form Component
307
+
308
+ ```vue
309
+ <!-- UserForm.vue -->
310
+ <script setup lang="ts">
311
+ import { ref } from 'vue'
312
+ import { useFormValidation, required, minLength, email } from '@/composables/useFormValidation'
313
+
314
+ const emit = defineEmits<{
315
+ submit: [data: { name: string; email: string }]
316
+ }>()
317
+
318
+ // Form state
319
+ const name = ref('')
320
+ const userEmail = ref('')
321
+
322
+ // Validation
323
+ const { isValid, touch, touchAll, getError } = useFormValidation({
324
+ name: {
325
+ value: name,
326
+ validators: [required(), minLength(2)]
327
+ },
328
+ email: {
329
+ value: userEmail,
330
+ validators: [required(), email()]
331
+ }
332
+ })
333
+
334
+ const handleSubmit = () => {
335
+ touchAll()
336
+ if (isValid.value) {
337
+ emit('submit', { name: name.value, email: userEmail.value })
338
+ }
339
+ }
340
+ </script>
341
+
342
+ <template>
343
+ <form @submit.prevent="handleSubmit" class="user-form">
344
+ <div class="field">
345
+ <label for="name">Name</label>
346
+ <input
347
+ id="name"
348
+ v-model="name"
349
+ @blur="touch('name')"
350
+ :class="{ error: getError('name') }"
351
+ />
352
+ <span v-if="getError('name')" class="error-msg">{{ getError('name') }}</span>
353
+ </div>
354
+
355
+ <div class="field">
356
+ <label for="email">Email</label>
357
+ <input
358
+ id="email"
359
+ type="email"
360
+ v-model="userEmail"
361
+ @blur="touch('email')"
362
+ :class="{ error: getError('email') }"
363
+ />
364
+ <span v-if="getError('email')" class="error-msg">{{ getError('email') }}</span>
365
+ </div>
366
+
367
+ <button type="submit" :disabled="!isValid">Submit</button>
368
+ </form>
369
+ </template>
370
+
371
+ <style scoped>
372
+ .user-form .field {
373
+ margin-bottom: 1rem;
374
+ }
375
+
376
+ .user-form input.error {
377
+ border-color: #dc3545;
378
+ }
379
+
380
+ .user-form .error-msg {
381
+ color: #dc3545;
382
+ font-size: 0.875rem;
383
+ }
384
+ </style>
385
+ ```
386
+
387
+ ---
388
+
389
+ ## Pattern Summary
390
+
391
+ | Pattern | Example | Benefit |
392
+ |---------|---------|---------|
393
+ | Pure Composable | `useProductLogic` | 100% testable logic |
394
+ | Wrapper Pattern | `UserDashboard` + `UserListPure` | Isolated side effects |
395
+ | Validation Composable | `useFormValidation` | Reusable, pure validation |
396
+ | Computed CSS | `cssClasses` | Cached class computation |
397
+ | Event Delegation | `emit('select')` | Unidirectional data flow |
@@ -0,0 +1,282 @@
1
+ # Composables Advanced Patterns
2
+
3
+ Advanced composable patterns for Vue.js FP architecture.
4
+
5
+ ## Table of Contents
6
+
7
+ 1. [Composable Factory Pattern](#composable-factory-pattern)
8
+ 2. [State Management with Composables](#state-management-with-composables)
9
+ 3. [Provide/Inject for Dependency Injection](#provideinject-for-dependency-injection)
10
+ 4. [Lifecycle Integration](#lifecycle-integration)
11
+ 5. [Composable Composition](#composable-composition)
12
+
13
+ ---
14
+
15
+ ## Composable Factory Pattern
16
+
17
+ Pre-compile expensive configurations for performance.
18
+
19
+ ```typescript
20
+ // composables/useUserFactory.ts
21
+ import type { Ref } from 'vue'
22
+ import { computed } from 'vue'
23
+
24
+ interface UserData {
25
+ id: string
26
+ name: string
27
+ email: string
28
+ }
29
+
30
+ interface UserConfig {
31
+ showEmail: boolean
32
+ variant: 'compact' | 'detailed'
33
+ }
34
+
35
+ // Factory creates optimized composable
36
+ const createUserComposable = (defaultConfig: UserConfig) => {
37
+ // Pre-compile static configuration (hot path optimization)
38
+ const compiledClasses = {
39
+ compact: {
40
+ card: 'user-card user-card--compact',
41
+ name: 'user-card__name user-card__name--compact'
42
+ },
43
+ detailed: {
44
+ card: 'user-card user-card--detailed',
45
+ name: 'user-card__name user-card__name--detailed'
46
+ }
47
+ }
48
+
49
+ return (userData: Readonly<Ref<UserData>>, config?: Partial<UserConfig>) => {
50
+ const mergedConfig = { ...defaultConfig, ...config }
51
+
52
+ const displayData = computed(() => ({
53
+ ...userData.value,
54
+ displayName: userData.value.name.trim()
55
+ }))
56
+
57
+ // O(1) lookup instead of string concatenation
58
+ const cssClasses = computed(() => compiledClasses[mergedConfig.variant])
59
+
60
+ return { displayData, cssClasses }
61
+ }
62
+ }
63
+
64
+ // Usage: Pre-compile for different contexts
65
+ export const useCompactUser = createUserComposable({
66
+ showEmail: false,
67
+ variant: 'compact'
68
+ })
69
+
70
+ export const useDetailedUser = createUserComposable({
71
+ showEmail: true,
72
+ variant: 'detailed'
73
+ })
74
+ ```
75
+
76
+ ---
77
+
78
+ ## State Management with Composables
79
+
80
+ Use composables for 95% of state management. Only use Pinia/Vuex for truly global state.
81
+
82
+ ```typescript
83
+ // composables/useUserState.ts
84
+ import { ref, readonly } from 'vue'
85
+
86
+ interface UserData {
87
+ id: string
88
+ name: string
89
+ email: string
90
+ }
91
+
92
+ export const useUserState = () => {
93
+ const users = ref<UserData[]>([])
94
+ const loading = ref(false)
95
+
96
+ // Pure functions for state transitions
97
+ const addUser = (user: UserData) => {
98
+ users.value = [...users.value, user] // Immutable
99
+ }
100
+
101
+ const updateUser = (userId: string, updates: Partial<UserData>) => {
102
+ users.value = users.value.map(user =>
103
+ user.id === userId ? { ...user, ...updates } : user
104
+ ) // Immutable
105
+ }
106
+
107
+ const removeUser = (userId: string) => {
108
+ users.value = users.value.filter(user => user.id !== userId) // Immutable
109
+ }
110
+
111
+ // Side effect isolated
112
+ const fetchUsers = async (userApi: { getUsers: () => Promise<UserData[]> }) => {
113
+ loading.value = true
114
+ try {
115
+ users.value = await userApi.getUsers()
116
+ } finally {
117
+ loading.value = false
118
+ }
119
+ }
120
+
121
+ return {
122
+ users: readonly(users),
123
+ loading: readonly(loading),
124
+ addUser,
125
+ updateUser,
126
+ removeUser,
127
+ fetchUsers
128
+ }
129
+ }
130
+ ```
131
+
132
+ ---
133
+
134
+ ## Provide/Inject for Dependency Injection
135
+
136
+ Use Vue's provide/inject for cross-component dependency injection.
137
+
138
+ ```typescript
139
+ // composables/useApiProvider.ts
140
+ import { provide, inject, type InjectionKey } from 'vue'
141
+
142
+ interface ApiClient {
143
+ getUser: (id: string) => Promise<UserData>
144
+ updateUser: (user: UserData) => Promise<void>
145
+ }
146
+
147
+ // Type-safe injection key
148
+ const API_KEY: InjectionKey<ApiClient> = Symbol('api')
149
+
150
+ // Provider composable (use in root component)
151
+ export const useApiProvider = (client: ApiClient) => {
152
+ provide(API_KEY, client)
153
+ }
154
+
155
+ // Consumer composable (use in child components)
156
+ export const useApi = (): ApiClient => {
157
+ const api = inject(API_KEY)
158
+ if (!api) {
159
+ throw new Error('useApi must be used within ApiProvider')
160
+ }
161
+ return api
162
+ }
163
+
164
+ // Mock provider for testing
165
+ export const useMockApiProvider = () => {
166
+ const mockClient: ApiClient = {
167
+ getUser: async (id) => ({ id, name: 'Mock User', email: 'mock@test.com' }),
168
+ updateUser: async () => {}
169
+ }
170
+ provide(API_KEY, mockClient)
171
+ }
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Lifecycle Integration
177
+
178
+ Integrate composables with Vue lifecycle hooks.
179
+
180
+ ```typescript
181
+ // composables/usePolling.ts
182
+ import { ref, readonly, onMounted, onUnmounted } from 'vue'
183
+
184
+ export const usePolling = <T>(
185
+ fetcher: () => Promise<T>,
186
+ intervalMs: number = 60000
187
+ ) => {
188
+ const data = ref<T | null>(null)
189
+ const error = ref<Error | null>(null)
190
+ const loading = ref(false)
191
+
192
+ let intervalId: ReturnType<typeof setInterval> | null = null
193
+
194
+ const fetch = async () => {
195
+ loading.value = true
196
+ try {
197
+ data.value = await fetcher()
198
+ error.value = null
199
+ } catch (e) {
200
+ error.value = e as Error
201
+ } finally {
202
+ loading.value = false
203
+ }
204
+ }
205
+
206
+ const start = () => {
207
+ fetch() // Initial fetch
208
+ intervalId = setInterval(fetch, intervalMs)
209
+ }
210
+
211
+ const stop = () => {
212
+ if (intervalId) {
213
+ clearInterval(intervalId)
214
+ intervalId = null
215
+ }
216
+ }
217
+
218
+ // Lifecycle integration
219
+ onMounted(start)
220
+ onUnmounted(stop)
221
+
222
+ return {
223
+ data: readonly(data),
224
+ error: readonly(error),
225
+ loading: readonly(loading),
226
+ refetch: fetch,
227
+ stop
228
+ }
229
+ }
230
+ ```
231
+
232
+ ---
233
+
234
+ ## Composable Composition
235
+
236
+ Compose multiple composables together.
237
+
238
+ ```typescript
239
+ // composables/useUserDashboard.ts
240
+ import { computed } from 'vue'
241
+ import { useUserState } from './useUserState'
242
+ import { usePolling } from './usePolling'
243
+
244
+ export const useUserDashboard = (userApi: { getUsers: () => Promise<UserData[]> }) => {
245
+ // Compose state management
246
+ const { users, loading: stateLoading, addUser, updateUser, removeUser } = useUserState()
247
+
248
+ // Compose polling
249
+ const { data: freshUsers, loading: pollLoading, refetch } = usePolling(
250
+ userApi.getUsers,
251
+ 30000
252
+ )
253
+
254
+ // Derived state from composed composables
255
+ const isLoading = computed(() => stateLoading.value || pollLoading.value)
256
+ const userCount = computed(() => users.value.length)
257
+ const hasUsers = computed(() => userCount.value > 0)
258
+
259
+ return {
260
+ users,
261
+ isLoading,
262
+ userCount,
263
+ hasUsers,
264
+ addUser,
265
+ updateUser,
266
+ removeUser,
267
+ refetch
268
+ }
269
+ }
270
+ ```
271
+
272
+ ---
273
+
274
+ ## When to Use These Patterns
275
+
276
+ | Pattern | Use When |
277
+ |---------|----------|
278
+ | Factory | Multiple similar composables with different configs |
279
+ | State Management | Local/feature state (95% of cases) |
280
+ | Provide/Inject | Cross-component dependencies, testing |
281
+ | Lifecycle Integration | Timers, subscriptions, cleanup needed |
282
+ | Composition | Building complex features from simple parts |