@stacksjs/components 0.2.92 → 0.2.94

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 (188) hide show
  1. package/dist/{Sidebar-yb115a2w.stx → Sidebar-4hn7vw4n.stx} +18 -0
  2. package/dist/index.js +5 -5
  3. package/package.json +5 -6
  4. package/src/components/CodeBlock.stx +0 -54
  5. package/src/components/Footer.stx +0 -66
  6. package/src/components/Hero.stx +0 -25
  7. package/src/components/Installation.stx +0 -37
  8. package/src/components/index.ts +0 -4
  9. package/src/composables/index.ts +0 -4
  10. package/src/composables/useCopyCode.ts +0 -73
  11. package/src/composables/useDarkMode.ts +0 -100
  12. package/src/composables/useSEO.ts +0 -230
  13. package/src/composables/utilities/index.ts +0 -7
  14. package/src/composables/utilities/useClickOutside.ts +0 -72
  15. package/src/composables/utilities/useFocusTrap.ts +0 -159
  16. package/src/composables/utilities/useKeyboard.ts +0 -124
  17. package/src/composables/utilities/useLocalStorage.ts +0 -132
  18. package/src/composables/utilities/useMediaQuery.ts +0 -118
  19. package/src/index.ts +0 -99
  20. package/src/stx.d.ts +0 -4
  21. package/src/ui/accordion/Accordion.stx +0 -146
  22. package/src/ui/accordion/AccordionItem.stx +0 -37
  23. package/src/ui/accordion/index.ts +0 -47
  24. package/src/ui/audio/Audio.stx +0 -50
  25. package/src/ui/audio/README.md +0 -41
  26. package/src/ui/audio/index.ts +0 -13
  27. package/src/ui/auth/Login.stx +0 -161
  28. package/src/ui/auth/README.md +0 -142
  29. package/src/ui/auth/Signup.stx +0 -159
  30. package/src/ui/auth/TwoFactorChallenge.stx +0 -111
  31. package/src/ui/auth/index.ts +0 -32
  32. package/src/ui/avatar/Avatar.stx +0 -77
  33. package/src/ui/avatar/index.ts +0 -12
  34. package/src/ui/badge/Badge.stx +0 -62
  35. package/src/ui/badge/index.ts +0 -10
  36. package/src/ui/breadcrumb/Breadcrumb.stx +0 -79
  37. package/src/ui/breadcrumb/index.ts +0 -15
  38. package/src/ui/button/Button.stx +0 -74
  39. package/src/ui/button/README.md +0 -80
  40. package/src/ui/button/index.ts +0 -85
  41. package/src/ui/calendar/Calendar.stx +0 -189
  42. package/src/ui/calendar/README.md +0 -254
  43. package/src/ui/calendar/index.ts +0 -14
  44. package/src/ui/card/Card.stx +0 -68
  45. package/src/ui/card/index.ts +0 -13
  46. package/src/ui/checkbox/Checkbox.stx +0 -86
  47. package/src/ui/checkbox/index.ts +0 -16
  48. package/src/ui/combobox/Combobox.stx +0 -96
  49. package/src/ui/combobox/ComboboxButton.stx +0 -25
  50. package/src/ui/combobox/ComboboxInput.stx +0 -32
  51. package/src/ui/combobox/ComboboxOption.stx +0 -49
  52. package/src/ui/combobox/ComboboxOptions.stx +0 -16
  53. package/src/ui/combobox/README.md +0 -150
  54. package/src/ui/combobox/index.ts +0 -40
  55. package/src/ui/command-palette/CommandPalette.stx +0 -81
  56. package/src/ui/command-palette/CommandPaletteItem.stx +0 -27
  57. package/src/ui/command-palette/README.md +0 -42
  58. package/src/ui/command-palette/index.ts +0 -15
  59. package/src/ui/dialog/Dialog.stx +0 -41
  60. package/src/ui/dialog/DialogBackdrop.stx +0 -9
  61. package/src/ui/dialog/DialogDescription.stx +0 -11
  62. package/src/ui/dialog/DialogPanel.stx +0 -16
  63. package/src/ui/dialog/DialogTitle.stx +0 -11
  64. package/src/ui/dialog/README.md +0 -121
  65. package/src/ui/dialog/index.ts +0 -32
  66. package/src/ui/drawer/Drawer.stx +0 -120
  67. package/src/ui/drawer/README.md +0 -90
  68. package/src/ui/drawer/index.ts +0 -9
  69. package/src/ui/dropdown/Dropdown.stx +0 -82
  70. package/src/ui/dropdown/DropdownButton.stx +0 -29
  71. package/src/ui/dropdown/DropdownItem.stx +0 -35
  72. package/src/ui/dropdown/DropdownItems.stx +0 -18
  73. package/src/ui/dropdown/README.md +0 -114
  74. package/src/ui/dropdown/index.ts +0 -29
  75. package/src/ui/form/Form.stx +0 -95
  76. package/src/ui/form/index.ts +0 -67
  77. package/src/ui/heatmap/Heatmap.stx +0 -294
  78. package/src/ui/image/Image.stx +0 -165
  79. package/src/ui/image/README.md +0 -164
  80. package/src/ui/image/index.ts +0 -22
  81. package/src/ui/input/EmailInput.stx +0 -30
  82. package/src/ui/input/NumberInput.stx +0 -126
  83. package/src/ui/input/PasswordInput.stx +0 -143
  84. package/src/ui/input/SearchInput.stx +0 -44
  85. package/src/ui/input/TextInput.stx +0 -132
  86. package/src/ui/input/index.ts +0 -180
  87. package/src/ui/listbox/Listbox.stx +0 -101
  88. package/src/ui/listbox/ListboxButton.stx +0 -28
  89. package/src/ui/listbox/ListboxLabel.stx +0 -11
  90. package/src/ui/listbox/ListboxOption.stx +0 -49
  91. package/src/ui/listbox/ListboxOptions.stx +0 -16
  92. package/src/ui/listbox/README.md +0 -135
  93. package/src/ui/listbox/index.ts +0 -39
  94. package/src/ui/navigator/Navigator.stx +0 -113
  95. package/src/ui/navigator/README.md +0 -352
  96. package/src/ui/navigator/index.ts +0 -20
  97. package/src/ui/notification/Notification.stx +0 -117
  98. package/src/ui/notification/README.md +0 -114
  99. package/src/ui/notification/index.ts +0 -12
  100. package/src/ui/pagination/Pagination.stx +0 -150
  101. package/src/ui/pagination/index.ts +0 -11
  102. package/src/ui/payment/Checkout.stx +0 -31
  103. package/src/ui/payment/DefaultPaymentMethod.stx +0 -62
  104. package/src/ui/payment/PaymentMethods.stx +0 -104
  105. package/src/ui/payment/README.md +0 -345
  106. package/src/ui/payment/SubscriptionCheckout.stx +0 -323
  107. package/src/ui/payment/index.ts +0 -59
  108. package/src/ui/popover/Popover.stx +0 -60
  109. package/src/ui/popover/PopoverButton.stx +0 -29
  110. package/src/ui/popover/PopoverPanel.stx +0 -23
  111. package/src/ui/popover/README.md +0 -91
  112. package/src/ui/popover/index.ts +0 -22
  113. package/src/ui/portal/Portal.stx +0 -57
  114. package/src/ui/portal/index.ts +0 -6
  115. package/src/ui/progress/Progress.stx +0 -131
  116. package/src/ui/progress/index.ts +0 -12
  117. package/src/ui/radio/Radio.stx +0 -77
  118. package/src/ui/radio/index.ts +0 -15
  119. package/src/ui/radio-group/README.md +0 -139
  120. package/src/ui/radio-group/RadioGroup.stx +0 -60
  121. package/src/ui/radio-group/RadioGroupDescription.stx +0 -11
  122. package/src/ui/radio-group/RadioGroupLabel.stx +0 -11
  123. package/src/ui/radio-group/RadioGroupOption.stx +0 -58
  124. package/src/ui/radio-group/index.ts +0 -30
  125. package/src/ui/select/README.md +0 -180
  126. package/src/ui/select/Select.stx +0 -82
  127. package/src/ui/select/index.ts +0 -21
  128. package/src/ui/sidebar/Sidebar.stx +0 -458
  129. package/src/ui/sidebar/SidebarFooter.stx +0 -71
  130. package/src/ui/sidebar/SidebarHeader.stx +0 -103
  131. package/src/ui/sidebar/SidebarItem.stx +0 -86
  132. package/src/ui/sidebar/SidebarSection.stx +0 -83
  133. package/src/ui/sidebar/index.ts +0 -153
  134. package/src/ui/sidebar/themes.ts +0 -240
  135. package/src/ui/skeleton/Skeleton.stx +0 -48
  136. package/src/ui/skeleton/index.ts +0 -11
  137. package/src/ui/spinner/Spinner.stx +0 -109
  138. package/src/ui/spinner/index.ts +0 -9
  139. package/src/ui/stepper/README.md +0 -36
  140. package/src/ui/stepper/Stepper.stx +0 -17
  141. package/src/ui/stepper/StepperStep.stx +0 -57
  142. package/src/ui/stepper/index.ts +0 -19
  143. package/src/ui/storage/README.md +0 -193
  144. package/src/ui/storage/index.ts +0 -347
  145. package/src/ui/switch/README.md +0 -114
  146. package/src/ui/switch/Switch.stx +0 -72
  147. package/src/ui/switch/index.ts +0 -10
  148. package/src/ui/table/README.md +0 -39
  149. package/src/ui/table/Table.stx +0 -14
  150. package/src/ui/table/TableBody.stx +0 -10
  151. package/src/ui/table/TableCell.stx +0 -10
  152. package/src/ui/table/TableHead.stx +0 -10
  153. package/src/ui/table/TableHeader.stx +0 -20
  154. package/src/ui/table/TableRow.stx +0 -11
  155. package/src/ui/table/index.ts +0 -35
  156. package/src/ui/tabs/TabPanel.stx +0 -25
  157. package/src/ui/tabs/Tabs.stx +0 -202
  158. package/src/ui/tabs/index.ts +0 -50
  159. package/src/ui/teleport/Teleport.stx +0 -65
  160. package/src/ui/teleport/index.ts +0 -7
  161. package/src/ui/textarea/Textarea.stx +0 -108
  162. package/src/ui/textarea/index.ts +0 -20
  163. package/src/ui/tooltip/Tooltip.stx +0 -71
  164. package/src/ui/tooltip/index.ts +0 -10
  165. package/src/ui/transition/README.md +0 -30
  166. package/src/ui/transition/Transition.stx +0 -102
  167. package/src/ui/transition/index.ts +0 -25
  168. package/src/ui/video/README.md +0 -47
  169. package/src/ui/video/Video.stx +0 -45
  170. package/src/ui/video/index.ts +0 -16
  171. package/src/ui/virtual-list/VirtualList.stx +0 -67
  172. package/src/ui/virtual-list/index.ts +0 -10
  173. package/src/ui/virtual-table/VirtualTable.stx +0 -103
  174. package/src/ui/virtual-table/index.ts +0 -21
  175. package/src/utils/PROP_VALIDATION.md +0 -458
  176. package/src/utils/THEME_SYSTEM.md +0 -702
  177. package/src/utils/accessibility.ts +0 -841
  178. package/src/utils/animation/easing.ts +0 -368
  179. package/src/utils/animation/helpers.ts +0 -223
  180. package/src/utils/animation/index.ts +0 -5
  181. package/src/utils/animation/keyframes.ts +0 -230
  182. package/src/utils/error-handling.ts +0 -475
  183. package/src/utils/highlighter.ts +0 -93
  184. package/src/utils/i18n.ts +0 -634
  185. package/src/utils/keep-alive.ts +0 -819
  186. package/src/utils/lazy-loading.ts +0 -530
  187. package/src/utils/prop-validation.ts +0 -478
  188. package/src/utils/theme.ts +0 -626
@@ -1,819 +0,0 @@
1
- /**
2
- * KeepAlive utilities for @stacksjs/components
3
- *
4
- * Provides component caching and state preservation with LRU (Least Recently Used)
5
- * cache strategy for optimizing component lifecycle management
6
- *
7
- * @example
8
- * ```ts
9
- * import { createKeepAlive } from '@stacksjs/components'
10
- *
11
- * // Create KeepAlive manager with max 10 components
12
- * const keepAlive = createKeepAlive({ max: 10 })
13
- *
14
- * // Cache component
15
- * const cached = keepAlive.cache('my-component', componentInstance, {
16
- * state: { count: 0 },
17
- * onActivate: (comp) => console.log('Component activated'),
18
- * onDeactivate: (comp) => console.log('Component deactivated'),
19
- * })
20
- *
21
- * // Retrieve cached component
22
- * const instance = keepAlive.get('my-component')
23
- *
24
- * // Prune least recently used
25
- * keepAlive.prune()
26
- * ```
27
- */
28
-
29
- /**
30
- * Component instance interface
31
- */
32
- export interface ComponentInstance {
33
- /** Component ID */
34
- id: string
35
- /** Component type/name */
36
- type: string
37
- /** DOM element */
38
- element?: HTMLElement
39
- /** Component state */
40
- state?: Record<string, any>
41
- /** Custom component data */
42
- data?: Record<string, any>
43
- /** Lifecycle hooks */
44
- hooks?: ComponentHooks
45
- /** Timestamp when created */
46
- createdAt: number
47
- /** Timestamp when last accessed */
48
- lastAccessedAt: number
49
- }
50
-
51
- /**
52
- * Component lifecycle hooks
53
- */
54
- export interface ComponentHooks {
55
- /** Called when component is mounted */
56
- onMount?: (instance: ComponentInstance) => void
57
- /** Called before component is unmounted */
58
- onUnmount?: (instance: ComponentInstance) => void
59
- /** Called when component is activated from cache */
60
- onActivate?: (instance: ComponentInstance) => void
61
- /** Called when component is deactivated to cache */
62
- onDeactivate?: (instance: ComponentInstance) => void
63
- /** Called when component state is restored */
64
- onRestore?: (instance: ComponentInstance, state: Record<string, any>) => void
65
- /** Called when component state is saved */
66
- onSave?: (instance: ComponentInstance) => Record<string, any>
67
- }
68
-
69
- /**
70
- * KeepAlive cache options
71
- */
72
- export interface KeepAliveOptions {
73
- /** Maximum number of cached components (LRU eviction when exceeded) */
74
- max?: number
75
- /** Time to live for cached components in milliseconds (0 = no expiry) */
76
- ttl?: number
77
- /** Include/exclude patterns for component types */
78
- include?: RegExp | string[]
79
- /** Exclude patterns for component types */
80
- exclude?: RegExp | string[]
81
- /** Global hooks for all cached components */
82
- hooks?: ComponentHooks
83
- /** Enable debug logging */
84
- debug?: boolean
85
- }
86
-
87
- /**
88
- * Cache entry with metadata
89
- */
90
- interface CacheEntry {
91
- /** Component instance */
92
- instance: ComponentInstance
93
- /** Access count */
94
- accessCount: number
95
- /** Creation timestamp */
96
- createdAt: number
97
- /** Last access timestamp */
98
- lastAccessedAt: number
99
- /** Expiry timestamp (if TTL set) */
100
- expiresAt?: number
101
- }
102
-
103
- /**
104
- * KeepAlive cache statistics
105
- */
106
- export interface CacheStats {
107
- /** Total cache hits */
108
- hits: number
109
- /** Total cache misses */
110
- misses: number
111
- /** Current cache size */
112
- size: number
113
- /** Maximum cache size */
114
- max: number
115
- /** Hit rate (0-1) */
116
- hitRate: number
117
- /** Cache keys */
118
- keys: string[]
119
- }
120
-
121
- /**
122
- * KeepAlive manager interface
123
- */
124
- export interface KeepAlive {
125
- /** Cache a component instance */
126
- cache: (key: string, instance: ComponentInstance, hooks?: ComponentHooks) => ComponentInstance
127
- /** Get cached component instance */
128
- get: (key: string) => ComponentInstance | null
129
- /** Check if key exists in cache */
130
- has: (key: string) => boolean
131
- /** Remove component from cache */
132
- remove: (key: string) => boolean
133
- /** Clear all cached components */
134
- clear: () => void
135
- /** Prune expired or least recently used components */
136
- prune: () => number
137
- /** Activate cached component */
138
- activate: (key: string) => ComponentInstance | null
139
- /** Deactivate component to cache */
140
- deactivate: (key: string) => boolean
141
- /** Get cache statistics */
142
- getStats: () => CacheStats
143
- /** Get all cached keys */
144
- keys: () => string[]
145
- /** Get cache size */
146
- size: () => number
147
- }
148
-
149
- /**
150
- * Create a KeepAlive manager
151
- *
152
- * Implements LRU (Least Recently Used) cache strategy for component instances
153
- *
154
- * @param options - KeepAlive configuration options
155
- * @returns KeepAlive manager instance
156
- *
157
- * @example
158
- * ```ts
159
- * const keepAlive = createKeepAlive({
160
- * max: 10,
161
- * ttl: 5 * 60 * 1000, // 5 minutes
162
- * include: ['user-profile', 'dashboard'],
163
- * hooks: {
164
- * onActivate: (comp) => console.log(`Activated: ${comp.id}`),
165
- * onDeactivate: (comp) => console.log(`Deactivated: ${comp.id}`),
166
- * },
167
- * })
168
- * ```
169
- */
170
- export function createKeepAlive(options: KeepAliveOptions = {}): KeepAlive {
171
- const {
172
- max = 10,
173
- ttl = 0,
174
- include,
175
- exclude,
176
- hooks: globalHooks,
177
- debug = false,
178
- } = options
179
-
180
- // Cache storage (Map maintains insertion order for LRU)
181
- const cache = new Map<string, CacheEntry>()
182
-
183
- // Statistics
184
- let hits = 0
185
- let misses = 0
186
-
187
- /**
188
- * Log debug message
189
- */
190
- function log(...args: any[]): void {
191
- if (debug) {
192
- // eslint-disable-next-line no-console
193
- console.log('[KeepAlive]', ...args)
194
- }
195
- }
196
-
197
- /**
198
- * Check if component type should be cached
199
- */
200
- function shouldCache(type: string): boolean {
201
- // Check exclude patterns first
202
- if (exclude) {
203
- if (exclude instanceof RegExp && exclude.test(type)) {
204
- return false
205
- }
206
- if (Array.isArray(exclude) && exclude.includes(type)) {
207
- return false
208
- }
209
- }
210
-
211
- // Check include patterns
212
- if (include) {
213
- if (include instanceof RegExp && !include.test(type)) {
214
- return false
215
- }
216
- if (Array.isArray(include) && !include.includes(type)) {
217
- return false
218
- }
219
- }
220
-
221
- return true
222
- }
223
-
224
- /**
225
- * Check if cache entry is expired
226
- */
227
- function isExpired(entry: CacheEntry): boolean {
228
- if (!ttl || !entry.expiresAt) {
229
- return false
230
- }
231
- return Date.now() > entry.expiresAt
232
- }
233
-
234
- /**
235
- * Update entry access time (for LRU)
236
- */
237
- function touch(key: string): void {
238
- const entry = cache.get(key)
239
- if (entry) {
240
- entry.lastAccessedAt = Date.now()
241
- entry.accessCount++
242
- entry.instance.lastAccessedAt = entry.lastAccessedAt
243
-
244
- // Move to end (most recently used)
245
- cache.delete(key)
246
- cache.set(key, entry)
247
-
248
- log(`Touched: ${key} (access count: ${entry.accessCount})`)
249
- }
250
- }
251
-
252
- /**
253
- * Evict least recently used entry
254
- */
255
- function evictLRU(): boolean {
256
- if (cache.size === 0) {
257
- return false
258
- }
259
-
260
- // First entry is least recently used (Map maintains insertion order)
261
- const firstKey = cache.keys().next().value as string | undefined
262
- if (!firstKey) {
263
- return false
264
- }
265
- const entry = cache.get(firstKey)
266
-
267
- if (entry) {
268
- log(`Evicting LRU: ${firstKey}`)
269
-
270
- // Call deactivate hook
271
- const hooks = entry.instance.hooks
272
- if (hooks?.onDeactivate) {
273
- hooks.onDeactivate(entry.instance)
274
- }
275
- if (globalHooks?.onDeactivate) {
276
- globalHooks.onDeactivate(entry.instance)
277
- }
278
-
279
- cache.delete(firstKey)
280
- return true
281
- }
282
-
283
- return false
284
- }
285
-
286
- /**
287
- * Cache a component instance
288
- */
289
- function cacheComponent(
290
- key: string,
291
- instance: ComponentInstance,
292
- hooks?: ComponentHooks,
293
- ): ComponentInstance {
294
- // Check if type should be cached
295
- if (!shouldCache(instance.type)) {
296
- log(`Type not cacheable: ${instance.type}`)
297
- return instance
298
- }
299
-
300
- // Check cache size limit
301
- if (cache.size >= max) {
302
- evictLRU()
303
- }
304
-
305
- const now = Date.now()
306
-
307
- // Create cache entry
308
- const entry: CacheEntry = {
309
- instance: {
310
- ...instance,
311
- hooks: hooks || instance.hooks,
312
- createdAt: instance.createdAt || now,
313
- lastAccessedAt: now,
314
- },
315
- accessCount: 0,
316
- createdAt: now,
317
- lastAccessedAt: now,
318
- expiresAt: ttl ? now + ttl : undefined,
319
- }
320
-
321
- cache.set(key, entry)
322
-
323
- log(`Cached: ${key} (size: ${cache.size}/${max})`)
324
-
325
- // Call cache hook
326
- if (hooks?.onMount) {
327
- hooks.onMount(entry.instance)
328
- }
329
- if (globalHooks?.onMount) {
330
- globalHooks.onMount(entry.instance)
331
- }
332
-
333
- return entry.instance
334
- }
335
-
336
- /**
337
- * Get cached component instance
338
- */
339
- function get(key: string): ComponentInstance | null {
340
- const entry = cache.get(key)
341
-
342
- if (!entry) {
343
- misses++
344
- log(`Miss: ${key}`)
345
- return null
346
- }
347
-
348
- // Check expiry
349
- if (isExpired(entry)) {
350
- log(`Expired: ${key}`)
351
- cache.delete(key)
352
- misses++
353
- return null
354
- }
355
-
356
- hits++
357
- touch(key)
358
- log(`Hit: ${key}`)
359
-
360
- return entry.instance
361
- }
362
-
363
- /**
364
- * Check if key exists in cache
365
- */
366
- function has(key: string): boolean {
367
- const entry = cache.get(key)
368
- if (!entry) {
369
- return false
370
- }
371
- if (isExpired(entry)) {
372
- cache.delete(key)
373
- return false
374
- }
375
- return true
376
- }
377
-
378
- /**
379
- * Remove component from cache
380
- */
381
- function remove(key: string): boolean {
382
- const entry = cache.get(key)
383
-
384
- if (!entry) {
385
- return false
386
- }
387
-
388
- log(`Removing: ${key}`)
389
-
390
- // Call unmount hook
391
- const hooks = entry.instance.hooks
392
- if (hooks?.onUnmount) {
393
- hooks.onUnmount(entry.instance)
394
- }
395
- if (globalHooks?.onUnmount) {
396
- globalHooks.onUnmount(entry.instance)
397
- }
398
-
399
- return cache.delete(key)
400
- }
401
-
402
- /**
403
- * Clear all cached components
404
- */
405
- function clear(): void {
406
- log('Clearing cache')
407
-
408
- // Call unmount hooks for all entries
409
- cache.forEach((entry) => {
410
- const hooks = entry.instance.hooks
411
- if (hooks?.onUnmount) {
412
- hooks.onUnmount(entry.instance)
413
- }
414
- if (globalHooks?.onUnmount) {
415
- globalHooks.onUnmount(entry.instance)
416
- }
417
- })
418
-
419
- cache.clear()
420
- hits = 0
421
- misses = 0
422
- }
423
-
424
- /**
425
- * Prune expired or excess entries
426
- */
427
- function prune(): number {
428
- let pruned = 0
429
-
430
- // Remove expired entries
431
- const _now = Date.now()
432
- for (const [key, entry] of cache.entries()) {
433
- if (isExpired(entry)) {
434
- log(`Pruning expired: ${key}`)
435
- remove(key)
436
- pruned++
437
- }
438
- }
439
-
440
- // Evict excess entries (LRU)
441
- while (cache.size > max) {
442
- if (evictLRU()) {
443
- pruned++
444
- }
445
- else {
446
- break
447
- }
448
- }
449
-
450
- if (pruned > 0) {
451
- log(`Pruned ${pruned} entries`)
452
- }
453
-
454
- return pruned
455
- }
456
-
457
- /**
458
- * Activate cached component
459
- */
460
- function activate(key: string): ComponentInstance | null {
461
- const instance = get(key)
462
-
463
- if (!instance) {
464
- return null
465
- }
466
-
467
- log(`Activating: ${key}`)
468
-
469
- // Call activate hook
470
- const hooks = instance.hooks
471
- if (hooks?.onActivate) {
472
- hooks.onActivate(instance)
473
- }
474
- if (globalHooks?.onActivate) {
475
- globalHooks.onActivate(instance)
476
- }
477
-
478
- // Restore state if saved
479
- if (instance.state && hooks?.onRestore) {
480
- hooks.onRestore(instance, instance.state)
481
- }
482
-
483
- return instance
484
- }
485
-
486
- /**
487
- * Deactivate component to cache
488
- */
489
- function deactivate(key: string): boolean {
490
- const entry = cache.get(key)
491
-
492
- if (!entry) {
493
- return false
494
- }
495
-
496
- log(`Deactivating: ${key}`)
497
-
498
- const instance = entry.instance
499
- const hooks = instance.hooks
500
-
501
- // Save state
502
- if (hooks?.onSave) {
503
- instance.state = hooks.onSave(instance)
504
- }
505
-
506
- // Call deactivate hook
507
- if (hooks?.onDeactivate) {
508
- hooks.onDeactivate(instance)
509
- }
510
- if (globalHooks?.onDeactivate) {
511
- globalHooks.onDeactivate(instance)
512
- }
513
-
514
- return true
515
- }
516
-
517
- /**
518
- * Get cache statistics
519
- */
520
- function getStats(): CacheStats {
521
- const total = hits + misses
522
- return {
523
- hits,
524
- misses,
525
- size: cache.size,
526
- max,
527
- hitRate: total > 0 ? hits / total : 0,
528
- keys: Array.from(cache.keys()),
529
- }
530
- }
531
-
532
- /**
533
- * Get all cached keys
534
- */
535
- function keys(): string[] {
536
- return Array.from(cache.keys())
537
- }
538
-
539
- /**
540
- * Get cache size
541
- */
542
- function size(): number {
543
- return cache.size
544
- }
545
-
546
- // Return KeepAlive manager
547
- return {
548
- cache: cacheComponent,
549
- get,
550
- has,
551
- remove,
552
- clear,
553
- prune,
554
- activate,
555
- deactivate,
556
- getStats,
557
- keys,
558
- size,
559
- }
560
- }
561
-
562
- /**
563
- * Create a simple LRU cache (without component-specific features)
564
- *
565
- * Generic LRU cache implementation for any type of data
566
- *
567
- * @param maxSize - Maximum number of items to cache
568
- * @returns LRU cache instance
569
- *
570
- * @example
571
- * ```ts
572
- * const cache = createLRUCache<string>(100)
573
- *
574
- * cache.set('key1', 'value1')
575
- * const value = cache.get('key1') // 'value1'
576
- * cache.has('key1') // true
577
- * ```
578
- */
579
- export function createLRUCache<T = any>(maxSize: number) {
580
- const cache = new Map<string, T>()
581
-
582
- return {
583
- /**
584
- * Set a value in cache
585
- */
586
- set(key: string, value: T): void {
587
- // Delete and re-add to move to end
588
- if (cache.has(key)) {
589
- cache.delete(key)
590
- }
591
-
592
- cache.set(key, value)
593
-
594
- // Evict oldest if over max size
595
- if (cache.size > maxSize) {
596
- const firstKey = cache.keys().next().value as string | undefined
597
- if (firstKey) {
598
- cache.delete(firstKey)
599
- }
600
- }
601
- },
602
-
603
- /**
604
- * Get a value from cache
605
- */
606
- get(key: string): T | undefined {
607
- if (!cache.has(key)) {
608
- return undefined
609
- }
610
-
611
- const value = cache.get(key)!
612
-
613
- // Move to end (most recently used)
614
- cache.delete(key)
615
- cache.set(key, value)
616
-
617
- return value
618
- },
619
-
620
- /**
621
- * Check if key exists
622
- */
623
- has(key: string): boolean {
624
- return cache.has(key)
625
- },
626
-
627
- /**
628
- * Delete a key
629
- */
630
- delete(key: string): boolean {
631
- return cache.delete(key)
632
- },
633
-
634
- /**
635
- * Clear all entries
636
- */
637
- clear(): void {
638
- cache.clear()
639
- },
640
-
641
- /**
642
- * Get cache size
643
- */
644
- get size(): number {
645
- return cache.size
646
- },
647
-
648
- /**
649
- * Get all keys
650
- */
651
- keys(): string[] {
652
- return Array.from(cache.keys())
653
- },
654
-
655
- /**
656
- * Get all values
657
- */
658
- values(): T[] {
659
- return Array.from(cache.values())
660
- },
661
-
662
- /**
663
- * Iterate over entries
664
- */
665
- forEach(callback: (value: T, key: string) => void): void {
666
- cache.forEach(callback)
667
- },
668
- }
669
- }
670
-
671
- /**
672
- * State manager for component state preservation
673
- *
674
- * Manages component state with automatic serialization/deserialization
675
- *
676
- * @example
677
- * ```ts
678
- * const stateManager = createStateManager()
679
- *
680
- * // Save state
681
- * stateManager.save('my-component', { count: 5 })
682
- *
683
- * // Restore state
684
- * const state = stateManager.restore('my-component')
685
- * console.log(state.count) // 5
686
- * ```
687
- */
688
- export function createStateManager() {
689
- const states = new Map<string, Record<string, any>>()
690
-
691
- return {
692
- /**
693
- * Save component state
694
- */
695
- save(key: string, state: Record<string, any>): void {
696
- // Deep clone to avoid mutations
697
- states.set(key, JSON.parse(JSON.stringify(state)))
698
- },
699
-
700
- /**
701
- * Restore component state
702
- */
703
- restore(key: string): Record<string, any> | null {
704
- const state = states.get(key)
705
- return state ? JSON.parse(JSON.stringify(state)) : null
706
- },
707
-
708
- /**
709
- * Check if state exists
710
- */
711
- has(key: string): boolean {
712
- return states.has(key)
713
- },
714
-
715
- /**
716
- * Remove saved state
717
- */
718
- remove(key: string): boolean {
719
- return states.delete(key)
720
- },
721
-
722
- /**
723
- * Clear all saved states
724
- */
725
- clear(): void {
726
- states.clear()
727
- },
728
-
729
- /**
730
- * Get all state keys
731
- */
732
- keys(): string[] {
733
- return Array.from(states.keys())
734
- },
735
-
736
- /**
737
- * Get number of saved states
738
- */
739
- get size(): number {
740
- return states.size
741
- },
742
- }
743
- }
744
-
745
- /**
746
- * Create a component pool for object reuse
747
- *
748
- * Useful for frequently created/destroyed components to reduce GC pressure
749
- *
750
- * @param factory - Function to create new instances
751
- * @param options - Pool configuration
752
- * @param options.maxSize - Maximum pool size
753
- * @param options.reset - Function to reset instance before reuse
754
- * @returns Component pool
755
- *
756
- * @example
757
- * ```ts
758
- * const pool = createComponentPool(
759
- * () => ({ id: '', type: '', state: {} }),
760
- * { maxSize: 50 }
761
- * )
762
- *
763
- * const instance = pool.acquire()
764
- * // Use instance...
765
- * pool.release(instance)
766
- * ```
767
- */
768
- export function createComponentPool<T>(
769
- factory: () => T,
770
- options: { maxSize?: number, reset?: (instance: T) => void } = {},
771
- ) {
772
- const { maxSize = 100, reset } = options
773
- const pool: T[] = []
774
-
775
- return {
776
- /**
777
- * Acquire instance from pool
778
- */
779
- acquire(): T {
780
- if (pool.length > 0) {
781
- return pool.pop()!
782
- }
783
- return factory()
784
- },
785
-
786
- /**
787
- * Release instance back to pool
788
- */
789
- release(instance: T): void {
790
- if (pool.length < maxSize) {
791
- if (reset) {
792
- reset(instance)
793
- }
794
- pool.push(instance)
795
- }
796
- },
797
-
798
- /**
799
- * Clear the pool
800
- */
801
- clear(): void {
802
- pool.length = 0
803
- },
804
-
805
- /**
806
- * Get pool size
807
- */
808
- get size(): number {
809
- return pool.length
810
- },
811
-
812
- /**
813
- * Get max pool size
814
- */
815
- get maxSize(): number {
816
- return maxSize
817
- },
818
- }
819
- }