@useavalon/avalon 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -0
- package/mod.ts +301 -0
- package/package.json +85 -0
- package/src/build/README.md +310 -0
- package/src/build/integration-bundler-plugin.ts +116 -0
- package/src/build/integration-config.ts +168 -0
- package/src/build/integration-detection-plugin.ts +117 -0
- package/src/build/integration-resolver-plugin.ts +90 -0
- package/src/build/island-manifest.ts +269 -0
- package/src/build/island-types-generator.ts +476 -0
- package/src/build/mdx-island-transform.ts +464 -0
- package/src/build/mdx-plugin.ts +98 -0
- package/src/build/page-island-transform.ts +598 -0
- package/src/build/prop-extractors/index.ts +21 -0
- package/src/build/prop-extractors/lit.ts +140 -0
- package/src/build/prop-extractors/qwik.ts +16 -0
- package/src/build/prop-extractors/solid.ts +125 -0
- package/src/build/prop-extractors/svelte.ts +194 -0
- package/src/build/prop-extractors/vue.ts +111 -0
- package/src/build/sidecar-file-manager.ts +104 -0
- package/src/build/sidecar-renderer.ts +30 -0
- package/src/client/adapters/index.ts +13 -0
- package/src/client/adapters/lit-adapter.ts +654 -0
- package/src/client/adapters/preact-adapter.ts +331 -0
- package/src/client/adapters/qwik-adapter.ts +345 -0
- package/src/client/adapters/react-adapter.ts +353 -0
- package/src/client/adapters/solid-adapter.ts +451 -0
- package/src/client/adapters/svelte-adapter.ts +524 -0
- package/src/client/adapters/vue-adapter.ts +467 -0
- package/src/client/components.ts +35 -0
- package/src/client/css-hmr-handler.ts +344 -0
- package/src/client/framework-adapter.ts +462 -0
- package/src/client/hmr-coordinator.ts +396 -0
- package/src/client/hmr-error-overlay.js +533 -0
- package/src/client/main.js +816 -0
- package/src/client/tests/css-hmr-handler.test.ts +360 -0
- package/src/client/tests/framework-adapter.test.ts +519 -0
- package/src/client/tests/hmr-coordinator.test.ts +176 -0
- package/src/client/tests/hydration-option-parsing.test.ts +107 -0
- package/src/client/tests/lit-adapter.test.ts +427 -0
- package/src/client/tests/preact-adapter.test.ts +353 -0
- package/src/client/tests/qwik-adapter.test.ts +343 -0
- package/src/client/tests/react-adapter.test.ts +317 -0
- package/src/client/tests/solid-adapter.test.ts +396 -0
- package/src/client/tests/svelte-adapter.test.ts +387 -0
- package/src/client/tests/vue-adapter.test.ts +407 -0
- package/src/client/types/framework-runtime.d.ts +68 -0
- package/src/client/types/vite-hmr.d.ts +46 -0
- package/src/client/types/vite-virtual-modules.d.ts +60 -0
- package/src/components/Image.tsx +123 -0
- package/src/components/IslandErrorBoundary.tsx +145 -0
- package/src/components/LayoutDataErrorBoundary.tsx +141 -0
- package/src/components/LayoutErrorBoundary.tsx +127 -0
- package/src/components/PersistentIsland.tsx +52 -0
- package/src/components/StreamingErrorBoundary.tsx +233 -0
- package/src/components/StreamingLayout.tsx +538 -0
- package/src/components/tests/component-analyzer.test.ts +96 -0
- package/src/components/tests/component-detection.test.ts +347 -0
- package/src/components/tests/persistent-islands.test.ts +398 -0
- package/src/core/components/component-analyzer.ts +192 -0
- package/src/core/components/component-detection.ts +508 -0
- package/src/core/components/enhanced-framework-detector.ts +500 -0
- package/src/core/components/framework-registry.ts +563 -0
- package/src/core/components/tests/enhanced-framework-detector.test.ts +577 -0
- package/src/core/components/tests/framework-registry.test.ts +465 -0
- package/src/core/content/mdx-processor.ts +46 -0
- package/src/core/integrations/README.md +282 -0
- package/src/core/integrations/index.ts +19 -0
- package/src/core/integrations/loader.ts +125 -0
- package/src/core/integrations/registry.ts +195 -0
- package/src/core/islands/island-persistence.ts +325 -0
- package/src/core/islands/island-state-serializer.ts +258 -0
- package/src/core/islands/persistent-island-context.tsx +80 -0
- package/src/core/islands/use-persistent-state.ts +68 -0
- package/src/core/layout/enhanced-layout-resolver.ts +322 -0
- package/src/core/layout/layout-cache-manager.ts +485 -0
- package/src/core/layout/layout-composer.ts +357 -0
- package/src/core/layout/layout-data-loader.ts +516 -0
- package/src/core/layout/layout-discovery.ts +243 -0
- package/src/core/layout/layout-matcher.ts +299 -0
- package/src/core/layout/layout-types.ts +110 -0
- package/src/core/layout/tests/enhanced-layout-resolver.test.ts +477 -0
- package/src/core/layout/tests/layout-cache-optimization.test.ts +149 -0
- package/src/core/layout/tests/layout-composer.test.ts +486 -0
- package/src/core/layout/tests/layout-data-loader.test.ts +443 -0
- package/src/core/layout/tests/layout-discovery.test.ts +253 -0
- package/src/core/layout/tests/layout-matcher.test.ts +480 -0
- package/src/core/modules/framework-module-resolver.ts +273 -0
- package/src/core/modules/tests/framework-module-resolver.test.ts +263 -0
- package/src/core/modules/tests/module-resolution-integration.test.ts +117 -0
- package/src/islands/component-analysis.ts +213 -0
- package/src/islands/css-utils.ts +565 -0
- package/src/islands/discovery/index.ts +80 -0
- package/src/islands/discovery/registry.ts +340 -0
- package/src/islands/discovery/resolver.ts +477 -0
- package/src/islands/discovery/scanner.ts +386 -0
- package/src/islands/discovery/tests/island-discovery.test.ts +881 -0
- package/src/islands/discovery/types.ts +117 -0
- package/src/islands/discovery/validator.ts +544 -0
- package/src/islands/discovery/watcher.ts +368 -0
- package/src/islands/framework-detection.ts +428 -0
- package/src/islands/integration-loader.ts +490 -0
- package/src/islands/island.tsx +565 -0
- package/src/islands/render-cache.ts +550 -0
- package/src/islands/types.ts +80 -0
- package/src/islands/universal-css-collector.ts +157 -0
- package/src/islands/universal-head-collector.ts +137 -0
- package/src/layout-system.d.ts +592 -0
- package/src/layout-system.ts +218 -0
- package/src/middleware/__tests__/discovery.test.ts +107 -0
- package/src/middleware/discovery.ts +268 -0
- package/src/middleware/executor.ts +315 -0
- package/src/middleware/index.ts +76 -0
- package/src/middleware/types.ts +99 -0
- package/src/nitro/build-config.ts +576 -0
- package/src/nitro/config.ts +483 -0
- package/src/nitro/error-handler.ts +636 -0
- package/src/nitro/index.ts +173 -0
- package/src/nitro/island-manifest.ts +584 -0
- package/src/nitro/middleware-adapter.ts +260 -0
- package/src/nitro/renderer.ts +1458 -0
- package/src/nitro/route-discovery.ts +439 -0
- package/src/nitro/types.ts +321 -0
- package/src/render/collect-css.ts +198 -0
- package/src/render/error-pages.ts +79 -0
- package/src/render/isolated-ssr-renderer.ts +654 -0
- package/src/render/ssr.ts +1030 -0
- package/src/schemas/api.ts +30 -0
- package/src/schemas/core.ts +64 -0
- package/src/schemas/index.ts +212 -0
- package/src/schemas/layout.ts +279 -0
- package/src/schemas/routing/index.ts +38 -0
- package/src/schemas/routing.ts +376 -0
- package/src/types/as-island.ts +20 -0
- package/src/types/image.d.ts +106 -0
- package/src/types/index.d.ts +22 -0
- package/src/types/island-jsx.d.ts +33 -0
- package/src/types/island-prop.d.ts +20 -0
- package/src/types/layout.ts +285 -0
- package/src/types/mdx.d.ts +6 -0
- package/src/types/routing.ts +555 -0
- package/src/types/tests/layout-types.test.ts +197 -0
- package/src/types/types.ts +5 -0
- package/src/types/urlpattern.d.ts +49 -0
- package/src/types/vite-env.d.ts +11 -0
- package/src/utils/dev-logger.ts +299 -0
- package/src/utils/fs.ts +151 -0
- package/src/vite-plugin/auto-discover.ts +551 -0
- package/src/vite-plugin/config.ts +266 -0
- package/src/vite-plugin/errors.ts +127 -0
- package/src/vite-plugin/image-optimization.ts +151 -0
- package/src/vite-plugin/integration-activator.ts +126 -0
- package/src/vite-plugin/island-sidecar-plugin.ts +176 -0
- package/src/vite-plugin/module-discovery.ts +189 -0
- package/src/vite-plugin/nitro-integration.ts +1334 -0
- package/src/vite-plugin/plugin.ts +329 -0
- package/src/vite-plugin/tests/image-optimization.test.ts +54 -0
- package/src/vite-plugin/types.ts +327 -0
- package/src/vite-plugin/validation.ts +228 -0
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
import type { LayoutHandler, ResolvedLayout, LayoutData } from './layout-types.ts';
|
|
2
|
+
|
|
3
|
+
export interface CacheEntry<T> {
|
|
4
|
+
value: T;
|
|
5
|
+
timestamp: number;
|
|
6
|
+
ttl: number;
|
|
7
|
+
accessCount: number;
|
|
8
|
+
lastAccessed: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface CacheStats {
|
|
12
|
+
hits: number;
|
|
13
|
+
misses: number;
|
|
14
|
+
evictions: number;
|
|
15
|
+
totalEntries: number;
|
|
16
|
+
memoryUsage: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CacheConfig {
|
|
20
|
+
defaultTtl: number;
|
|
21
|
+
maxEntries: number;
|
|
22
|
+
cleanupInterval: number;
|
|
23
|
+
enableStats: boolean;
|
|
24
|
+
enableCompression?: boolean;
|
|
25
|
+
intelligentInvalidation?: boolean;
|
|
26
|
+
preloadThreshold?: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class LayoutCacheManager {
|
|
30
|
+
private readonly resolvedLayouts = new Map<string, CacheEntry<ResolvedLayout>>();
|
|
31
|
+
private readonly layoutHandlers = new Map<string, CacheEntry<LayoutHandler>>();
|
|
32
|
+
private readonly layoutData = new Map<string, CacheEntry<LayoutData>>();
|
|
33
|
+
private readonly dependencyGraph = new Map<string, Set<string>>(); // Track cache dependencies
|
|
34
|
+
private readonly accessOrder = new Map<string, number>(); // LRU tracking
|
|
35
|
+
private accessCounter = 0;
|
|
36
|
+
private stats: CacheStats = {
|
|
37
|
+
hits: 0,
|
|
38
|
+
misses: 0,
|
|
39
|
+
evictions: 0,
|
|
40
|
+
totalEntries: 0,
|
|
41
|
+
memoryUsage: 0,
|
|
42
|
+
};
|
|
43
|
+
private cleanupTimer?: ReturnType<typeof setInterval>;
|
|
44
|
+
|
|
45
|
+
constructor(private readonly config: CacheConfig) {
|
|
46
|
+
// Don't start cleanup timer in test environment
|
|
47
|
+
if (process.env.NODE_ENV !== 'test') {
|
|
48
|
+
this.startCleanupTimer();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Resolved layouts cache
|
|
53
|
+
setResolvedLayout(key: string, layout: ResolvedLayout, ttl?: number): void {
|
|
54
|
+
const entry: CacheEntry<ResolvedLayout> = {
|
|
55
|
+
value: layout,
|
|
56
|
+
timestamp: Date.now(),
|
|
57
|
+
ttl: ttl || this.config.defaultTtl,
|
|
58
|
+
accessCount: 0,
|
|
59
|
+
lastAccessed: Date.now(),
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
this.resolvedLayouts.set(key, entry);
|
|
63
|
+
this.updateAccessOrder(key);
|
|
64
|
+
this.updateStats();
|
|
65
|
+
this.enforceMaxEntries();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
getResolvedLayout(key: string): ResolvedLayout | null {
|
|
69
|
+
const entry = this.resolvedLayouts.get(key);
|
|
70
|
+
if (!entry) {
|
|
71
|
+
this.stats.misses++;
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (this.isExpired(entry)) {
|
|
76
|
+
this.resolvedLayouts.delete(key);
|
|
77
|
+
this.accessOrder.delete(key);
|
|
78
|
+
this.stats.misses++;
|
|
79
|
+
this.stats.evictions++;
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
entry.accessCount++;
|
|
84
|
+
entry.lastAccessed = Date.now();
|
|
85
|
+
this.updateAccessOrder(key);
|
|
86
|
+
this.stats.hits++;
|
|
87
|
+
return entry.value;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Layout handlers cache
|
|
91
|
+
setLayoutHandler(key: string, handler: LayoutHandler, ttl?: number): void {
|
|
92
|
+
const entry: CacheEntry<LayoutHandler> = {
|
|
93
|
+
value: handler,
|
|
94
|
+
timestamp: Date.now(),
|
|
95
|
+
ttl: ttl || this.config.defaultTtl,
|
|
96
|
+
accessCount: 0,
|
|
97
|
+
lastAccessed: Date.now(),
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
this.layoutHandlers.set(key, entry);
|
|
101
|
+
this.updateAccessOrder(key);
|
|
102
|
+
this.updateStats();
|
|
103
|
+
this.enforceMaxEntries();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
getLayoutHandler(key: string): LayoutHandler | null {
|
|
107
|
+
const entry = this.layoutHandlers.get(key);
|
|
108
|
+
if (!entry) {
|
|
109
|
+
this.stats.misses++;
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (this.isExpired(entry)) {
|
|
114
|
+
this.layoutHandlers.delete(key);
|
|
115
|
+
this.accessOrder.delete(key);
|
|
116
|
+
this.stats.misses++;
|
|
117
|
+
this.stats.evictions++;
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
entry.accessCount++;
|
|
122
|
+
entry.lastAccessed = Date.now();
|
|
123
|
+
this.updateAccessOrder(key);
|
|
124
|
+
this.stats.hits++;
|
|
125
|
+
return entry.value;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Layout data cache
|
|
129
|
+
setLayoutData(key: string, data: LayoutData, ttl?: number): void {
|
|
130
|
+
const entry: CacheEntry<LayoutData> = {
|
|
131
|
+
value: data,
|
|
132
|
+
timestamp: Date.now(),
|
|
133
|
+
ttl: ttl || this.config.defaultTtl,
|
|
134
|
+
accessCount: 0,
|
|
135
|
+
lastAccessed: Date.now(),
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
this.layoutData.set(key, entry);
|
|
139
|
+
this.updateAccessOrder(key);
|
|
140
|
+
this.updateStats();
|
|
141
|
+
this.enforceMaxEntries();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
getLayoutData(key: string): LayoutData | null {
|
|
145
|
+
const entry = this.layoutData.get(key);
|
|
146
|
+
if (!entry) {
|
|
147
|
+
this.stats.misses++;
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (this.isExpired(entry)) {
|
|
152
|
+
this.layoutData.delete(key);
|
|
153
|
+
this.accessOrder.delete(key);
|
|
154
|
+
this.stats.misses++;
|
|
155
|
+
this.stats.evictions++;
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
entry.accessCount++;
|
|
160
|
+
entry.lastAccessed = Date.now();
|
|
161
|
+
this.updateAccessOrder(key);
|
|
162
|
+
this.stats.hits++;
|
|
163
|
+
return entry.value;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Cache invalidation with intelligent dependency tracking
|
|
167
|
+
invalidateResolvedLayout(key: string): boolean {
|
|
168
|
+
const deleted = this.resolvedLayouts.delete(key);
|
|
169
|
+
this.accessOrder.delete(key);
|
|
170
|
+
this.invalidateDependents(key);
|
|
171
|
+
return deleted;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
invalidateLayoutHandler(key: string): boolean {
|
|
175
|
+
const deleted = this.layoutHandlers.delete(key);
|
|
176
|
+
this.accessOrder.delete(key);
|
|
177
|
+
this.invalidateDependents(key);
|
|
178
|
+
return deleted;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
invalidateLayoutData(key: string): boolean {
|
|
182
|
+
const deleted = this.layoutData.delete(key);
|
|
183
|
+
this.accessOrder.delete(key);
|
|
184
|
+
this.invalidateDependents(key);
|
|
185
|
+
return deleted;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Intelligent invalidation based on file path changes
|
|
189
|
+
invalidateByFilePath(filePath: string): number {
|
|
190
|
+
let invalidated = 0;
|
|
191
|
+
const normalizedPath = this.normalizePath(filePath);
|
|
192
|
+
|
|
193
|
+
// Invalidate all entries that depend on this file
|
|
194
|
+
for (const key of this.resolvedLayouts.keys()) {
|
|
195
|
+
if (this.isKeyAffectedByPath(key, normalizedPath)) {
|
|
196
|
+
this.resolvedLayouts.delete(key);
|
|
197
|
+
this.accessOrder.delete(key);
|
|
198
|
+
invalidated++;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
for (const key of this.layoutHandlers.keys()) {
|
|
203
|
+
if (this.isKeyAffectedByPath(key, normalizedPath)) {
|
|
204
|
+
this.layoutHandlers.delete(key);
|
|
205
|
+
this.accessOrder.delete(key);
|
|
206
|
+
invalidated++;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
for (const key of this.layoutData.keys()) {
|
|
211
|
+
if (this.isKeyAffectedByPath(key, normalizedPath)) {
|
|
212
|
+
this.layoutData.delete(key);
|
|
213
|
+
this.accessOrder.delete(key);
|
|
214
|
+
invalidated++;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
this.updateStats();
|
|
219
|
+
return invalidated;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Add cache dependency tracking
|
|
223
|
+
addDependency(key: string, dependsOn: string): void {
|
|
224
|
+
if (!this.dependencyGraph.has(dependsOn)) {
|
|
225
|
+
this.dependencyGraph.set(dependsOn, new Set());
|
|
226
|
+
}
|
|
227
|
+
this.dependencyGraph.get(dependsOn)!.add(key);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Remove cache dependency
|
|
231
|
+
removeDependency(key: string, dependsOn: string): void {
|
|
232
|
+
const dependents = this.dependencyGraph.get(dependsOn);
|
|
233
|
+
if (dependents) {
|
|
234
|
+
dependents.delete(key);
|
|
235
|
+
if (dependents.size === 0) {
|
|
236
|
+
this.dependencyGraph.delete(dependsOn);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
invalidateByPattern(pattern: RegExp): number {
|
|
242
|
+
let invalidated = 0;
|
|
243
|
+
|
|
244
|
+
for (const key of this.resolvedLayouts.keys()) {
|
|
245
|
+
if (pattern.test(key)) {
|
|
246
|
+
this.resolvedLayouts.delete(key);
|
|
247
|
+
invalidated++;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
for (const key of this.layoutHandlers.keys()) {
|
|
252
|
+
if (pattern.test(key)) {
|
|
253
|
+
this.layoutHandlers.delete(key);
|
|
254
|
+
invalidated++;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
for (const key of this.layoutData.keys()) {
|
|
259
|
+
if (pattern.test(key)) {
|
|
260
|
+
this.layoutData.delete(key);
|
|
261
|
+
invalidated++;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
this.updateStats();
|
|
266
|
+
return invalidated;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Clear all caches
|
|
270
|
+
clear(): void {
|
|
271
|
+
this.resolvedLayouts.clear();
|
|
272
|
+
this.layoutHandlers.clear();
|
|
273
|
+
this.layoutData.clear();
|
|
274
|
+
this.dependencyGraph.clear();
|
|
275
|
+
this.accessOrder.clear();
|
|
276
|
+
this.accessCounter = 0;
|
|
277
|
+
this.resetStats();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Cache statistics
|
|
281
|
+
getStats(): CacheStats {
|
|
282
|
+
this.updateStats();
|
|
283
|
+
return { ...this.stats };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
getHitRate(): number {
|
|
287
|
+
const total = this.stats.hits + this.stats.misses;
|
|
288
|
+
return total === 0 ? 0 : this.stats.hits / total;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Private methods
|
|
292
|
+
private isExpired(entry: CacheEntry<any>): boolean {
|
|
293
|
+
return Date.now() - entry.timestamp > entry.ttl;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
private invalidateDependents(key: string): void {
|
|
297
|
+
const dependents = this.dependencyGraph.get(key);
|
|
298
|
+
if (dependents) {
|
|
299
|
+
for (const dependent of dependents) {
|
|
300
|
+
this.resolvedLayouts.delete(dependent);
|
|
301
|
+
this.layoutHandlers.delete(dependent);
|
|
302
|
+
this.layoutData.delete(dependent);
|
|
303
|
+
this.accessOrder.delete(dependent);
|
|
304
|
+
// Recursively invalidate dependents of dependents
|
|
305
|
+
this.invalidateDependents(dependent);
|
|
306
|
+
}
|
|
307
|
+
this.dependencyGraph.delete(key);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
private normalizePath(filePath: string): string {
|
|
312
|
+
return filePath.replaceAll('\\', '/').toLowerCase();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
private isKeyAffectedByPath(key: string, filePath: string): boolean {
|
|
316
|
+
// Check if the cache key contains or is related to the file path
|
|
317
|
+
const normalizedKey = key.toLowerCase();
|
|
318
|
+
return (
|
|
319
|
+
normalizedKey.includes(filePath) ||
|
|
320
|
+
normalizedKey.includes(filePath.replace('_layout.tsx', '')) ||
|
|
321
|
+
normalizedKey.includes(filePath.replace('.tsx', ''))
|
|
322
|
+
);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private updateAccessOrder(key: string): void {
|
|
326
|
+
this.accessOrder.set(key, ++this.accessCounter);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private updateStats(): void {
|
|
330
|
+
if (!this.config.enableStats) return;
|
|
331
|
+
|
|
332
|
+
this.stats.totalEntries = this.resolvedLayouts.size + this.layoutHandlers.size + this.layoutData.size;
|
|
333
|
+
|
|
334
|
+
// Estimate memory usage (rough calculation)
|
|
335
|
+
this.stats.memoryUsage = this.estimateMemoryUsage();
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
private estimateMemoryUsage(): number {
|
|
339
|
+
let size = 0;
|
|
340
|
+
|
|
341
|
+
// Rough estimation of memory usage
|
|
342
|
+
for (const [key, entry] of this.resolvedLayouts) {
|
|
343
|
+
size += key.length * 2; // UTF-16 characters
|
|
344
|
+
size += JSON.stringify(entry.value).length * 2;
|
|
345
|
+
size += 64; // Entry metadata overhead
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
for (const [key] of this.layoutHandlers) {
|
|
349
|
+
size += key.length * 2;
|
|
350
|
+
size += 256; // Estimated handler size
|
|
351
|
+
size += 64; // Entry metadata overhead
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
for (const [key, entry] of this.layoutData) {
|
|
355
|
+
size += key.length * 2;
|
|
356
|
+
size += JSON.stringify(entry.value).length * 2;
|
|
357
|
+
size += 64; // Entry metadata overhead
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return size;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private enforceMaxEntries(): void {
|
|
364
|
+
const totalEntries = this.resolvedLayouts.size + this.layoutHandlers.size + this.layoutData.size;
|
|
365
|
+
|
|
366
|
+
if (totalEntries <= this.config.maxEntries) return;
|
|
367
|
+
|
|
368
|
+
// Use access order for more efficient LRU eviction
|
|
369
|
+
const sortedByAccess = Array.from(this.accessOrder.entries())
|
|
370
|
+
.sort((a, b) => a[1] - b[1]) // Sort by access order (oldest first)
|
|
371
|
+
.map(([key]) => key);
|
|
372
|
+
|
|
373
|
+
// Remove oldest entries until we're under the limit
|
|
374
|
+
const toRemove = totalEntries - this.config.maxEntries;
|
|
375
|
+
let removed = 0;
|
|
376
|
+
|
|
377
|
+
for (const key of sortedByAccess) {
|
|
378
|
+
if (removed >= toRemove) break;
|
|
379
|
+
|
|
380
|
+
// Try to remove from each cache
|
|
381
|
+
let wasRemoved = false;
|
|
382
|
+
if (this.resolvedLayouts.has(key)) {
|
|
383
|
+
this.resolvedLayouts.delete(key);
|
|
384
|
+
wasRemoved = true;
|
|
385
|
+
}
|
|
386
|
+
if (this.layoutHandlers.has(key)) {
|
|
387
|
+
this.layoutHandlers.delete(key);
|
|
388
|
+
wasRemoved = true;
|
|
389
|
+
}
|
|
390
|
+
if (this.layoutData.has(key)) {
|
|
391
|
+
this.layoutData.delete(key);
|
|
392
|
+
wasRemoved = true;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (wasRemoved) {
|
|
396
|
+
this.accessOrder.delete(key);
|
|
397
|
+
this.stats.evictions++;
|
|
398
|
+
removed++;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
private startCleanupTimer(): void {
|
|
404
|
+
if (this.cleanupTimer) {
|
|
405
|
+
clearInterval(this.cleanupTimer);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
this.cleanupTimer = setInterval(() => {
|
|
409
|
+
this.cleanup();
|
|
410
|
+
}, this.config.cleanupInterval);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
private cleanup(): void {
|
|
414
|
+
const now = Date.now();
|
|
415
|
+
|
|
416
|
+
// Clean expired resolved layouts
|
|
417
|
+
for (const [key, entry] of this.resolvedLayouts) {
|
|
418
|
+
if (now - entry.timestamp > entry.ttl) {
|
|
419
|
+
this.resolvedLayouts.delete(key);
|
|
420
|
+
this.accessOrder.delete(key);
|
|
421
|
+
this.stats.evictions++;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Clean expired layout handlers
|
|
426
|
+
for (const [key, entry] of this.layoutHandlers) {
|
|
427
|
+
if (now - entry.timestamp > entry.ttl) {
|
|
428
|
+
this.layoutHandlers.delete(key);
|
|
429
|
+
this.accessOrder.delete(key);
|
|
430
|
+
this.stats.evictions++;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// Clean expired layout data
|
|
435
|
+
for (const [key, entry] of this.layoutData) {
|
|
436
|
+
if (now - entry.timestamp > entry.ttl) {
|
|
437
|
+
this.layoutData.delete(key);
|
|
438
|
+
this.accessOrder.delete(key);
|
|
439
|
+
this.stats.evictions++;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Clean up orphaned access order entries
|
|
444
|
+
for (const key of this.accessOrder.keys()) {
|
|
445
|
+
if (!this.resolvedLayouts.has(key) && !this.layoutHandlers.has(key) && !this.layoutData.has(key)) {
|
|
446
|
+
this.accessOrder.delete(key);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
this.updateStats();
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
private resetStats(): void {
|
|
454
|
+
this.stats = {
|
|
455
|
+
hits: 0,
|
|
456
|
+
misses: 0,
|
|
457
|
+
evictions: 0,
|
|
458
|
+
totalEntries: 0,
|
|
459
|
+
memoryUsage: 0,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Cleanup resources
|
|
464
|
+
destroy(): void {
|
|
465
|
+
if (this.cleanupTimer) {
|
|
466
|
+
clearInterval(this.cleanupTimer);
|
|
467
|
+
this.cleanupTimer = undefined;
|
|
468
|
+
}
|
|
469
|
+
this.clear();
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Default cache configuration
|
|
474
|
+
export const defaultCacheConfig: CacheConfig = {
|
|
475
|
+
defaultTtl: 5 * 60 * 1000, // 5 minutes
|
|
476
|
+
maxEntries: 1000,
|
|
477
|
+
cleanupInterval: 60 * 1000, // 1 minute
|
|
478
|
+
enableStats: true,
|
|
479
|
+
enableCompression: true,
|
|
480
|
+
intelligentInvalidation: true,
|
|
481
|
+
preloadThreshold: 0.8, // Preload when cache is 80% full
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
// Global cache instance
|
|
485
|
+
export const layoutCache = new LayoutCacheManager(defaultCacheConfig);
|