@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.
- package/dist/{Sidebar-yb115a2w.stx → Sidebar-4hn7vw4n.stx} +18 -0
- package/dist/index.js +5 -5
- package/package.json +5 -6
- package/src/components/CodeBlock.stx +0 -54
- package/src/components/Footer.stx +0 -66
- package/src/components/Hero.stx +0 -25
- package/src/components/Installation.stx +0 -37
- package/src/components/index.ts +0 -4
- package/src/composables/index.ts +0 -4
- package/src/composables/useCopyCode.ts +0 -73
- package/src/composables/useDarkMode.ts +0 -100
- package/src/composables/useSEO.ts +0 -230
- package/src/composables/utilities/index.ts +0 -7
- package/src/composables/utilities/useClickOutside.ts +0 -72
- package/src/composables/utilities/useFocusTrap.ts +0 -159
- package/src/composables/utilities/useKeyboard.ts +0 -124
- package/src/composables/utilities/useLocalStorage.ts +0 -132
- package/src/composables/utilities/useMediaQuery.ts +0 -118
- package/src/index.ts +0 -99
- package/src/stx.d.ts +0 -4
- package/src/ui/accordion/Accordion.stx +0 -146
- package/src/ui/accordion/AccordionItem.stx +0 -37
- package/src/ui/accordion/index.ts +0 -47
- package/src/ui/audio/Audio.stx +0 -50
- package/src/ui/audio/README.md +0 -41
- package/src/ui/audio/index.ts +0 -13
- package/src/ui/auth/Login.stx +0 -161
- package/src/ui/auth/README.md +0 -142
- package/src/ui/auth/Signup.stx +0 -159
- package/src/ui/auth/TwoFactorChallenge.stx +0 -111
- package/src/ui/auth/index.ts +0 -32
- package/src/ui/avatar/Avatar.stx +0 -77
- package/src/ui/avatar/index.ts +0 -12
- package/src/ui/badge/Badge.stx +0 -62
- package/src/ui/badge/index.ts +0 -10
- package/src/ui/breadcrumb/Breadcrumb.stx +0 -79
- package/src/ui/breadcrumb/index.ts +0 -15
- package/src/ui/button/Button.stx +0 -74
- package/src/ui/button/README.md +0 -80
- package/src/ui/button/index.ts +0 -85
- package/src/ui/calendar/Calendar.stx +0 -189
- package/src/ui/calendar/README.md +0 -254
- package/src/ui/calendar/index.ts +0 -14
- package/src/ui/card/Card.stx +0 -68
- package/src/ui/card/index.ts +0 -13
- package/src/ui/checkbox/Checkbox.stx +0 -86
- package/src/ui/checkbox/index.ts +0 -16
- package/src/ui/combobox/Combobox.stx +0 -96
- package/src/ui/combobox/ComboboxButton.stx +0 -25
- package/src/ui/combobox/ComboboxInput.stx +0 -32
- package/src/ui/combobox/ComboboxOption.stx +0 -49
- package/src/ui/combobox/ComboboxOptions.stx +0 -16
- package/src/ui/combobox/README.md +0 -150
- package/src/ui/combobox/index.ts +0 -40
- package/src/ui/command-palette/CommandPalette.stx +0 -81
- package/src/ui/command-palette/CommandPaletteItem.stx +0 -27
- package/src/ui/command-palette/README.md +0 -42
- package/src/ui/command-palette/index.ts +0 -15
- package/src/ui/dialog/Dialog.stx +0 -41
- package/src/ui/dialog/DialogBackdrop.stx +0 -9
- package/src/ui/dialog/DialogDescription.stx +0 -11
- package/src/ui/dialog/DialogPanel.stx +0 -16
- package/src/ui/dialog/DialogTitle.stx +0 -11
- package/src/ui/dialog/README.md +0 -121
- package/src/ui/dialog/index.ts +0 -32
- package/src/ui/drawer/Drawer.stx +0 -120
- package/src/ui/drawer/README.md +0 -90
- package/src/ui/drawer/index.ts +0 -9
- package/src/ui/dropdown/Dropdown.stx +0 -82
- package/src/ui/dropdown/DropdownButton.stx +0 -29
- package/src/ui/dropdown/DropdownItem.stx +0 -35
- package/src/ui/dropdown/DropdownItems.stx +0 -18
- package/src/ui/dropdown/README.md +0 -114
- package/src/ui/dropdown/index.ts +0 -29
- package/src/ui/form/Form.stx +0 -95
- package/src/ui/form/index.ts +0 -67
- package/src/ui/heatmap/Heatmap.stx +0 -294
- package/src/ui/image/Image.stx +0 -165
- package/src/ui/image/README.md +0 -164
- package/src/ui/image/index.ts +0 -22
- package/src/ui/input/EmailInput.stx +0 -30
- package/src/ui/input/NumberInput.stx +0 -126
- package/src/ui/input/PasswordInput.stx +0 -143
- package/src/ui/input/SearchInput.stx +0 -44
- package/src/ui/input/TextInput.stx +0 -132
- package/src/ui/input/index.ts +0 -180
- package/src/ui/listbox/Listbox.stx +0 -101
- package/src/ui/listbox/ListboxButton.stx +0 -28
- package/src/ui/listbox/ListboxLabel.stx +0 -11
- package/src/ui/listbox/ListboxOption.stx +0 -49
- package/src/ui/listbox/ListboxOptions.stx +0 -16
- package/src/ui/listbox/README.md +0 -135
- package/src/ui/listbox/index.ts +0 -39
- package/src/ui/navigator/Navigator.stx +0 -113
- package/src/ui/navigator/README.md +0 -352
- package/src/ui/navigator/index.ts +0 -20
- package/src/ui/notification/Notification.stx +0 -117
- package/src/ui/notification/README.md +0 -114
- package/src/ui/notification/index.ts +0 -12
- package/src/ui/pagination/Pagination.stx +0 -150
- package/src/ui/pagination/index.ts +0 -11
- package/src/ui/payment/Checkout.stx +0 -31
- package/src/ui/payment/DefaultPaymentMethod.stx +0 -62
- package/src/ui/payment/PaymentMethods.stx +0 -104
- package/src/ui/payment/README.md +0 -345
- package/src/ui/payment/SubscriptionCheckout.stx +0 -323
- package/src/ui/payment/index.ts +0 -59
- package/src/ui/popover/Popover.stx +0 -60
- package/src/ui/popover/PopoverButton.stx +0 -29
- package/src/ui/popover/PopoverPanel.stx +0 -23
- package/src/ui/popover/README.md +0 -91
- package/src/ui/popover/index.ts +0 -22
- package/src/ui/portal/Portal.stx +0 -57
- package/src/ui/portal/index.ts +0 -6
- package/src/ui/progress/Progress.stx +0 -131
- package/src/ui/progress/index.ts +0 -12
- package/src/ui/radio/Radio.stx +0 -77
- package/src/ui/radio/index.ts +0 -15
- package/src/ui/radio-group/README.md +0 -139
- package/src/ui/radio-group/RadioGroup.stx +0 -60
- package/src/ui/radio-group/RadioGroupDescription.stx +0 -11
- package/src/ui/radio-group/RadioGroupLabel.stx +0 -11
- package/src/ui/radio-group/RadioGroupOption.stx +0 -58
- package/src/ui/radio-group/index.ts +0 -30
- package/src/ui/select/README.md +0 -180
- package/src/ui/select/Select.stx +0 -82
- package/src/ui/select/index.ts +0 -21
- package/src/ui/sidebar/Sidebar.stx +0 -458
- package/src/ui/sidebar/SidebarFooter.stx +0 -71
- package/src/ui/sidebar/SidebarHeader.stx +0 -103
- package/src/ui/sidebar/SidebarItem.stx +0 -86
- package/src/ui/sidebar/SidebarSection.stx +0 -83
- package/src/ui/sidebar/index.ts +0 -153
- package/src/ui/sidebar/themes.ts +0 -240
- package/src/ui/skeleton/Skeleton.stx +0 -48
- package/src/ui/skeleton/index.ts +0 -11
- package/src/ui/spinner/Spinner.stx +0 -109
- package/src/ui/spinner/index.ts +0 -9
- package/src/ui/stepper/README.md +0 -36
- package/src/ui/stepper/Stepper.stx +0 -17
- package/src/ui/stepper/StepperStep.stx +0 -57
- package/src/ui/stepper/index.ts +0 -19
- package/src/ui/storage/README.md +0 -193
- package/src/ui/storage/index.ts +0 -347
- package/src/ui/switch/README.md +0 -114
- package/src/ui/switch/Switch.stx +0 -72
- package/src/ui/switch/index.ts +0 -10
- package/src/ui/table/README.md +0 -39
- package/src/ui/table/Table.stx +0 -14
- package/src/ui/table/TableBody.stx +0 -10
- package/src/ui/table/TableCell.stx +0 -10
- package/src/ui/table/TableHead.stx +0 -10
- package/src/ui/table/TableHeader.stx +0 -20
- package/src/ui/table/TableRow.stx +0 -11
- package/src/ui/table/index.ts +0 -35
- package/src/ui/tabs/TabPanel.stx +0 -25
- package/src/ui/tabs/Tabs.stx +0 -202
- package/src/ui/tabs/index.ts +0 -50
- package/src/ui/teleport/Teleport.stx +0 -65
- package/src/ui/teleport/index.ts +0 -7
- package/src/ui/textarea/Textarea.stx +0 -108
- package/src/ui/textarea/index.ts +0 -20
- package/src/ui/tooltip/Tooltip.stx +0 -71
- package/src/ui/tooltip/index.ts +0 -10
- package/src/ui/transition/README.md +0 -30
- package/src/ui/transition/Transition.stx +0 -102
- package/src/ui/transition/index.ts +0 -25
- package/src/ui/video/README.md +0 -47
- package/src/ui/video/Video.stx +0 -45
- package/src/ui/video/index.ts +0 -16
- package/src/ui/virtual-list/VirtualList.stx +0 -67
- package/src/ui/virtual-list/index.ts +0 -10
- package/src/ui/virtual-table/VirtualTable.stx +0 -103
- package/src/ui/virtual-table/index.ts +0 -21
- package/src/utils/PROP_VALIDATION.md +0 -458
- package/src/utils/THEME_SYSTEM.md +0 -702
- package/src/utils/accessibility.ts +0 -841
- package/src/utils/animation/easing.ts +0 -368
- package/src/utils/animation/helpers.ts +0 -223
- package/src/utils/animation/index.ts +0 -5
- package/src/utils/animation/keyframes.ts +0 -230
- package/src/utils/error-handling.ts +0 -475
- package/src/utils/highlighter.ts +0 -93
- package/src/utils/i18n.ts +0 -634
- package/src/utils/keep-alive.ts +0 -819
- package/src/utils/lazy-loading.ts +0 -530
- package/src/utils/prop-validation.ts +0 -478
- package/src/utils/theme.ts +0 -626
package/src/utils/keep-alive.ts
DELETED
|
@@ -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
|
-
}
|