@fjell/cache 4.6.5 → 4.6.10
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/Cache.cjs.js +41 -2
- package/dist/Cache.es.js +41 -2
- package/dist/CacheMap.cjs.js +65 -3
- package/dist/CacheMap.es.js +65 -3
- package/dist/Instance.cjs.js +27 -0
- package/dist/Instance.d.ts +20 -0
- package/dist/Instance.es.js +22 -0
- package/dist/InstanceFactory.cjs.js +23 -0
- package/dist/InstanceFactory.d.ts +8 -0
- package/dist/InstanceFactory.es.js +19 -0
- package/dist/Registry.cjs.js +36 -0
- package/dist/Registry.d.ts +15 -0
- package/dist/Registry.es.js +31 -0
- package/dist/index.cjs +224 -119
- package/dist/index.cjs.js +9 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.es.js +4 -2
- package/examples/README.md +307 -0
- package/examples/aggregator-example.ts +334 -0
- package/examples/basic-cache-example.ts +273 -0
- package/examples/cache-map-example.ts +265 -0
- package/package.json +13 -12
- package/vitest.config.ts +2 -2
- package/dist/CacheRegistry.cjs.js +0 -66
- package/dist/CacheRegistry.d.ts +0 -10
- package/dist/CacheRegistry.es.js +0 -62
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
/**
|
|
3
|
+
* Basic Cache Example
|
|
4
|
+
*
|
|
5
|
+
* This example demonstrates the fundamental usage of fjell-cache for caching data models.
|
|
6
|
+
* It shows how to create caches, perform basic operations like get/set/all, and manage
|
|
7
|
+
* cached data with mock storage operations.
|
|
8
|
+
*
|
|
9
|
+
* Perfect for understanding the basics of fjell-cache before moving to advanced features.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { createCache } from '../src/Cache';
|
|
13
|
+
import { createInstance } from '../src/Instance';
|
|
14
|
+
import { createRegistry } from '../src/Registry';
|
|
15
|
+
import { ClientApi } from '@fjell/client-api';
|
|
16
|
+
import { Item, PriKey } from '@fjell/core';
|
|
17
|
+
import { createCoordinate } from '@fjell/registry';
|
|
18
|
+
|
|
19
|
+
// Define our data models
|
|
20
|
+
interface User extends Item<'user'> {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
email: string;
|
|
24
|
+
role: 'admin' | 'user' | 'guest';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface Task extends Item<'task'> {
|
|
28
|
+
id: string;
|
|
29
|
+
title: string;
|
|
30
|
+
description: string;
|
|
31
|
+
assignedTo?: string;
|
|
32
|
+
status: 'pending' | 'in-progress' | 'completed';
|
|
33
|
+
priority: 'low' | 'medium' | 'high';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Mock storage for demonstration (in real apps, this would be your database/API)
|
|
37
|
+
const mockUserStorage = new Map<string, User>();
|
|
38
|
+
const mockTaskStorage = new Map<string, Task>();
|
|
39
|
+
|
|
40
|
+
// Simple mock API for Users (simplified to avoid type issues)
|
|
41
|
+
const createUserApi = (): Partial<ClientApi<User, 'user'>> => ({
|
|
42
|
+
async all(query = {}) {
|
|
43
|
+
console.log('📦 Fetching all users from storage...');
|
|
44
|
+
return Array.from(mockUserStorage.values());
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
async one(query = {}) {
|
|
48
|
+
const users = await this.all!(query);
|
|
49
|
+
return users[0] || null;
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
async get(key: PriKey<'user'>) {
|
|
53
|
+
console.log(`🔍 Getting user with key: ${key.pk}`);
|
|
54
|
+
const user = mockUserStorage.get(String(key.pk));
|
|
55
|
+
if (!user) {
|
|
56
|
+
throw new Error(`User not found: ${key.pk}`);
|
|
57
|
+
}
|
|
58
|
+
return user;
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
async find(finder = 'all') {
|
|
62
|
+
return await this.all!({});
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Simple mock API for Tasks
|
|
67
|
+
const createTaskApi = (): Partial<ClientApi<Task, 'task'>> => ({
|
|
68
|
+
async all(query = {}) {
|
|
69
|
+
console.log('📦 Fetching all tasks from storage...');
|
|
70
|
+
return Array.from(mockTaskStorage.values());
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
async one(query = {}) {
|
|
74
|
+
const tasks = await this.all!(query);
|
|
75
|
+
return tasks[0] || null;
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
async get(key: PriKey<'task'>) {
|
|
79
|
+
console.log(`🔍 Getting task with key: ${key.pk}`);
|
|
80
|
+
const task = mockTaskStorage.get(String(key.pk));
|
|
81
|
+
if (!task) {
|
|
82
|
+
throw new Error(`Task not found: ${key.pk}`);
|
|
83
|
+
}
|
|
84
|
+
return task;
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
async find(finder = 'all') {
|
|
88
|
+
return await this.all!({});
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Helper function to create test data
|
|
93
|
+
const createTestUser = (id: string, name: string, email: string, role: 'admin' | 'user' | 'guest'): User => {
|
|
94
|
+
const user: User = {
|
|
95
|
+
id,
|
|
96
|
+
name,
|
|
97
|
+
email,
|
|
98
|
+
role,
|
|
99
|
+
key: { kt: 'user', pk: id },
|
|
100
|
+
events: {
|
|
101
|
+
created: { at: new Date() },
|
|
102
|
+
updated: { at: new Date() },
|
|
103
|
+
deleted: { at: null }
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
mockUserStorage.set(id, user);
|
|
107
|
+
console.log(`✅ Created user: ${user.name} (${user.email})`);
|
|
108
|
+
return user;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const createTestTask = (id: string, title: string, description: string, assignedTo: string, status: 'pending' | 'in-progress' | 'completed', priority: 'low' | 'medium' | 'high'): Task => {
|
|
112
|
+
const task: Task = {
|
|
113
|
+
id,
|
|
114
|
+
title,
|
|
115
|
+
description,
|
|
116
|
+
assignedTo,
|
|
117
|
+
status,
|
|
118
|
+
priority,
|
|
119
|
+
key: { kt: 'task', pk: id },
|
|
120
|
+
events: {
|
|
121
|
+
created: { at: new Date() },
|
|
122
|
+
updated: { at: new Date() },
|
|
123
|
+
deleted: { at: null }
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
mockTaskStorage.set(id, task);
|
|
127
|
+
console.log(`✅ Created task: ${task.title}`);
|
|
128
|
+
return task;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Main example function
|
|
132
|
+
export const runBasicCacheExample = async (): Promise<void> => {
|
|
133
|
+
console.log('\n🚀 Fjell-Cache Basic Example');
|
|
134
|
+
console.log('============================\n');
|
|
135
|
+
|
|
136
|
+
console.log('This example demonstrates basic cache operations with User and Task models.\n');
|
|
137
|
+
|
|
138
|
+
// Step 1: Create registry and cache instances
|
|
139
|
+
console.log('Step 1: Setting up cache infrastructure');
|
|
140
|
+
console.log('--------------------------------------');
|
|
141
|
+
|
|
142
|
+
const registry = createRegistry();
|
|
143
|
+
console.log('✅ Created cache registry');
|
|
144
|
+
|
|
145
|
+
const userApi = createUserApi() as ClientApi<User, 'user'>;
|
|
146
|
+
const taskApi = createTaskApi() as ClientApi<Task, 'task'>;
|
|
147
|
+
console.log('✅ Created mock APIs for users and tasks');
|
|
148
|
+
|
|
149
|
+
const userCache = await createCache(userApi, 'user');
|
|
150
|
+
const taskCache = await createCache(taskApi, 'task');
|
|
151
|
+
console.log('✅ Created cache instances');
|
|
152
|
+
|
|
153
|
+
const userInstance = createInstance(registry, createCoordinate('user'), userCache);
|
|
154
|
+
const taskInstance = createInstance(registry, createCoordinate('task'), taskCache);
|
|
155
|
+
console.log('✅ Created cache model instances\n');
|
|
156
|
+
|
|
157
|
+
// Step 2: Create some test data
|
|
158
|
+
console.log('Step 2: Creating test data');
|
|
159
|
+
console.log('-------------------------');
|
|
160
|
+
|
|
161
|
+
const user1 = createTestUser('user-1', 'Alice Johnson', 'alice@example.com', 'admin');
|
|
162
|
+
const user2 = createTestUser('user-2', 'Bob Smith', 'bob@example.com', 'user');
|
|
163
|
+
|
|
164
|
+
const task1 = createTestTask('task-1', 'Setup project repository', 'Initialize Git repo and create basic structure', user1.id, 'completed', 'high');
|
|
165
|
+
const task2 = createTestTask('task-2', 'Implement user authentication', 'Add login and registration functionality', user2.id, 'in-progress', 'medium');
|
|
166
|
+
|
|
167
|
+
console.log('✅ Created test users and tasks\n');
|
|
168
|
+
|
|
169
|
+
// Step 3: Cache operations - Fetch and cache all items
|
|
170
|
+
console.log('Step 3: Cache operations - Fetching all items');
|
|
171
|
+
console.log('----------------------------------------------');
|
|
172
|
+
|
|
173
|
+
const [, allUsers] = await userInstance.cache.all();
|
|
174
|
+
console.log(`📋 Cached ${allUsers.length} users:`, allUsers.map(u => u.name));
|
|
175
|
+
|
|
176
|
+
const [, allTasks] = await taskInstance.cache.all();
|
|
177
|
+
console.log(`📋 Cached ${allTasks.length} tasks:`, allTasks.map(t => t.title));
|
|
178
|
+
console.log('');
|
|
179
|
+
|
|
180
|
+
// Step 4: Individual item retrieval from cache
|
|
181
|
+
console.log('Step 4: Individual item retrieval');
|
|
182
|
+
console.log('---------------------------------');
|
|
183
|
+
|
|
184
|
+
const [, cachedUser1] = await userInstance.cache.get(user1.key);
|
|
185
|
+
console.log(`👤 Retrieved from cache: ${cachedUser1?.name} (${cachedUser1?.email})`);
|
|
186
|
+
|
|
187
|
+
const [, cachedTask1] = await taskInstance.cache.get(task1.key);
|
|
188
|
+
console.log(`📝 Retrieved from cache: ${cachedTask1?.title} - Status: ${cachedTask1?.status}`);
|
|
189
|
+
console.log('');
|
|
190
|
+
|
|
191
|
+
// Step 5: Cache hit vs miss demonstration
|
|
192
|
+
console.log('Step 5: Cache behavior demonstration');
|
|
193
|
+
console.log('-----------------------------------');
|
|
194
|
+
|
|
195
|
+
// This should hit the cache (no API call)
|
|
196
|
+
console.log('🎯 Second retrieval (should hit cache):');
|
|
197
|
+
const [, cachedUser1Again] = await userInstance.cache.retrieve(user1.key);
|
|
198
|
+
console.log(`👤 Retrieved: ${cachedUser1Again?.name} (cache hit)`);
|
|
199
|
+
|
|
200
|
+
// Create a new user and demonstrate cache miss
|
|
201
|
+
const user3 = createTestUser('user-3', 'Charlie Brown', 'charlie@example.com', 'guest');
|
|
202
|
+
|
|
203
|
+
console.log('🎯 New item retrieval (cache miss, will fetch from API):');
|
|
204
|
+
const [, cachedUser3] = await userInstance.cache.get(user3.key);
|
|
205
|
+
console.log(`👤 Retrieved: ${cachedUser3?.name} (fetched from API and cached)`);
|
|
206
|
+
console.log('');
|
|
207
|
+
|
|
208
|
+
// Step 6: Cache updates
|
|
209
|
+
console.log('Step 6: Cache updates');
|
|
210
|
+
console.log('--------------------');
|
|
211
|
+
|
|
212
|
+
const updatedTask2 = { ...task2, status: 'completed' as const, description: 'Add login and registration functionality - COMPLETED!' };
|
|
213
|
+
mockTaskStorage.set(task2.id, updatedTask2);
|
|
214
|
+
|
|
215
|
+
// Update cache with new version
|
|
216
|
+
await taskInstance.cache.set(updatedTask2.key, updatedTask2);
|
|
217
|
+
console.log(`🔄 Updated task in cache: ${updatedTask2.title} - New status: ${updatedTask2.status}`);
|
|
218
|
+
console.log('');
|
|
219
|
+
|
|
220
|
+
// Step 7: Query operations
|
|
221
|
+
console.log('Step 7: Query operations');
|
|
222
|
+
console.log('-----------------------');
|
|
223
|
+
|
|
224
|
+
const [, foundTasks] = await taskInstance.cache.find('all');
|
|
225
|
+
console.log(`🔍 Found ${foundTasks.length} tasks through cache query`);
|
|
226
|
+
|
|
227
|
+
const [, oneTask] = await taskInstance.cache.one();
|
|
228
|
+
console.log(`📝 Retrieved one task: ${oneTask?.title}`);
|
|
229
|
+
console.log('');
|
|
230
|
+
|
|
231
|
+
// Step 8: Cache statistics and management
|
|
232
|
+
console.log('Step 8: Cache management');
|
|
233
|
+
console.log('-----------------------');
|
|
234
|
+
|
|
235
|
+
console.log('📊 Cache Statistics:');
|
|
236
|
+
console.log(` 👥 Users in cache: ${allUsers.length}`);
|
|
237
|
+
console.log(` 📝 Tasks in cache: ${allTasks.length}`);
|
|
238
|
+
console.log(` 🎯 User cache coordinate: ${userInstance.coordinate.kta[0]}`);
|
|
239
|
+
console.log(` 🎯 Task cache coordinate: ${taskInstance.coordinate.kta[0]}`);
|
|
240
|
+
console.log('');
|
|
241
|
+
|
|
242
|
+
// Step 9: Cleanup demonstration
|
|
243
|
+
console.log('Step 9: Cleanup demonstration');
|
|
244
|
+
console.log('-----------------------------');
|
|
245
|
+
|
|
246
|
+
mockUserStorage.delete('user-3');
|
|
247
|
+
console.log('🗑️ Removed user from storage');
|
|
248
|
+
|
|
249
|
+
// Cache still has the old data until next fetch
|
|
250
|
+
const [, stillCachedUser3] = await userInstance.cache.retrieve(user3.key);
|
|
251
|
+
console.log(`🎯 Cache still contains removed user: ${stillCachedUser3?.name || 'null'}`);
|
|
252
|
+
|
|
253
|
+
// Fresh fetch will update cache
|
|
254
|
+
const [, freshAllUsers] = await userInstance.cache.all();
|
|
255
|
+
console.log(`📋 Fresh fetch shows ${freshAllUsers.length} users (cache updated)`);
|
|
256
|
+
console.log('');
|
|
257
|
+
|
|
258
|
+
console.log('🎉 Basic Cache Example Complete!');
|
|
259
|
+
console.log('================================\n');
|
|
260
|
+
|
|
261
|
+
console.log('Key concepts demonstrated:');
|
|
262
|
+
console.log('• Cache creation with registry and instances');
|
|
263
|
+
console.log('• Basic cache operations (get, set, all, find, one)');
|
|
264
|
+
console.log('• Cache hits vs misses');
|
|
265
|
+
console.log('• Cache updates and management');
|
|
266
|
+
console.log('• Integration with mock storage APIs');
|
|
267
|
+
console.log('• Cache lifecycle and data consistency\n');
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
// Run the example if this file is executed directly
|
|
271
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
272
|
+
runBasicCacheExample().catch(console.error);
|
|
273
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Map Example
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates the lower-level CacheMap functionality of fjell-cache.
|
|
5
|
+
* CacheMap provides direct key-value storage and retrieval with support for
|
|
6
|
+
* complex fjell keys and efficient item management.
|
|
7
|
+
*
|
|
8
|
+
* Shows how to:
|
|
9
|
+
* - Create and manage CacheMap instances directly
|
|
10
|
+
* - Store and retrieve items with complex keys
|
|
11
|
+
* - Use basic operations and key management
|
|
12
|
+
* - Handle key normalization and comparison
|
|
13
|
+
* - Manage cache lifecycle and cleanup
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { CacheMap } from '../src/CacheMap';
|
|
17
|
+
import { ComKey, Item, PriKey } from '@fjell/core';
|
|
18
|
+
|
|
19
|
+
// Define test data models
|
|
20
|
+
interface Document extends Item<'document'> {
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
content: string;
|
|
24
|
+
author: string;
|
|
25
|
+
tags: string[];
|
|
26
|
+
createdAt: Date;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface Comment extends Item<'comment', 'document'> {
|
|
30
|
+
id: string;
|
|
31
|
+
documentId: string;
|
|
32
|
+
content: string;
|
|
33
|
+
author: string;
|
|
34
|
+
createdAt: Date;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Helper to create test data
|
|
38
|
+
const createDocument = (id: string, title: string, content: string, author: string, tags: string[]): Document => ({
|
|
39
|
+
id, title, content, author, tags, createdAt: new Date(),
|
|
40
|
+
key: { kt: 'document', pk: id },
|
|
41
|
+
events: { created: { at: new Date() }, updated: { at: new Date() }, deleted: { at: null } }
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const createComment = (id: string, documentId: string, content: string, author: string): Comment => ({
|
|
45
|
+
id, documentId, content, author, createdAt: new Date(),
|
|
46
|
+
key: { kt: 'comment', pk: id, loc: [{ kt: 'document', lk: documentId }] },
|
|
47
|
+
events: { created: { at: new Date() }, updated: { at: new Date() }, deleted: { at: null } }
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export const runCacheMapExample = async (): Promise<void> => {
|
|
51
|
+
console.log('\n🚀 Fjell-Cache CacheMap Example');
|
|
52
|
+
console.log('==============================\n');
|
|
53
|
+
|
|
54
|
+
console.log('This example demonstrates direct CacheMap operations for low-level cache management.\n');
|
|
55
|
+
|
|
56
|
+
// Step 1: Create CacheMap instances
|
|
57
|
+
console.log('Step 1: Creating CacheMap instances');
|
|
58
|
+
console.log('-----------------------------------');
|
|
59
|
+
|
|
60
|
+
// Create CacheMaps for different item types
|
|
61
|
+
const documentCacheMap = new CacheMap<Document, 'document'>(['document']);
|
|
62
|
+
const commentCacheMap = new CacheMap<Comment, 'comment', 'document'>(['comment', 'document']);
|
|
63
|
+
|
|
64
|
+
console.log('✅ Created CacheMap instances for documents and comments');
|
|
65
|
+
console.log(` 📄 Document CacheMap: supports primary keys only`);
|
|
66
|
+
console.log(` 💬 Comment CacheMap: supports contained items with location hierarchy\n`);
|
|
67
|
+
|
|
68
|
+
// Step 2: Create test data
|
|
69
|
+
console.log('Step 2: Creating test data');
|
|
70
|
+
console.log('-------------------------');
|
|
71
|
+
|
|
72
|
+
const doc1 = createDocument('doc-1', 'Getting Started with Fjell', 'This is a comprehensive guide...', 'Alice', ['guide', 'tutorial']);
|
|
73
|
+
const doc2 = createDocument('doc-2', 'Advanced Caching Patterns', 'Learn advanced techniques...', 'Bob', ['advanced', 'caching']);
|
|
74
|
+
const doc3 = createDocument('doc-3', 'Performance Optimization', 'Tips for better performance...', 'Charlie', ['performance', 'optimization']);
|
|
75
|
+
|
|
76
|
+
const comment1 = createComment('comment-1', doc1.id, 'Great tutorial! Very helpful.', 'David');
|
|
77
|
+
const comment2 = createComment('comment-2', doc1.id, 'Thanks for sharing this.', 'Eve');
|
|
78
|
+
const comment3 = createComment('comment-3', doc2.id, 'Excellent advanced techniques.', 'Frank');
|
|
79
|
+
|
|
80
|
+
console.log('✅ Created test documents and comments\n');
|
|
81
|
+
|
|
82
|
+
// Step 3: Basic CacheMap operations
|
|
83
|
+
console.log('Step 3: Basic CacheMap operations');
|
|
84
|
+
console.log('---------------------------------');
|
|
85
|
+
|
|
86
|
+
// Set items in cache
|
|
87
|
+
documentCacheMap.set(doc1.key, doc1);
|
|
88
|
+
documentCacheMap.set(doc2.key, doc2);
|
|
89
|
+
documentCacheMap.set(doc3.key, doc3);
|
|
90
|
+
|
|
91
|
+
commentCacheMap.set(comment1.key, comment1);
|
|
92
|
+
commentCacheMap.set(comment2.key, comment2);
|
|
93
|
+
commentCacheMap.set(comment3.key, comment3);
|
|
94
|
+
|
|
95
|
+
console.log('📥 Stored all items in CacheMaps');
|
|
96
|
+
console.log(` 📄 Documents cached: ${documentCacheMap.values().length}`);
|
|
97
|
+
console.log(` 💬 Comments cached: ${commentCacheMap.values().length}`);
|
|
98
|
+
|
|
99
|
+
// Get individual items
|
|
100
|
+
const retrievedDoc1 = documentCacheMap.get(doc1.key);
|
|
101
|
+
const retrievedComment1 = commentCacheMap.get(comment1.key);
|
|
102
|
+
|
|
103
|
+
console.log(`\n🔍 Retrieved items by key:`);
|
|
104
|
+
console.log(` 📄 Document: "${retrievedDoc1?.title}" by ${retrievedDoc1?.author}`);
|
|
105
|
+
console.log(` 💬 Comment: "${retrievedComment1?.content}" by ${retrievedComment1?.author}`);
|
|
106
|
+
|
|
107
|
+
// Step 4: Key operations and checking
|
|
108
|
+
console.log('\n\nStep 4: Key operations and checking');
|
|
109
|
+
console.log('----------------------------------');
|
|
110
|
+
|
|
111
|
+
// Check if keys exist
|
|
112
|
+
const hasDoc1 = documentCacheMap.includesKey(doc1.key);
|
|
113
|
+
const hasDoc4 = documentCacheMap.includesKey({ kt: 'document', pk: 'doc-4' });
|
|
114
|
+
|
|
115
|
+
console.log(`🔑 Key existence checks:`);
|
|
116
|
+
console.log(` 📄 Document 1 exists: ${hasDoc1}`);
|
|
117
|
+
console.log(` 📄 Document 4 exists: ${hasDoc4}`);
|
|
118
|
+
|
|
119
|
+
// Get all keys
|
|
120
|
+
const allDocKeys = documentCacheMap.keys();
|
|
121
|
+
const allCommentKeys = commentCacheMap.keys();
|
|
122
|
+
|
|
123
|
+
console.log(`\n🗂️ All cached keys:`);
|
|
124
|
+
console.log(` 📄 Document keys: ${allDocKeys.length} items`);
|
|
125
|
+
allDocKeys.forEach(key => console.log(` - ${(key as PriKey<'document'>).pk}`));
|
|
126
|
+
|
|
127
|
+
console.log(` 💬 Comment keys: ${allCommentKeys.length} items`);
|
|
128
|
+
allCommentKeys.forEach(key => console.log(` - ${(key as ComKey<'comment', 'document'>).pk} in document ${(key as ComKey<'comment', 'document'>).loc?.[0]?.lk}`));
|
|
129
|
+
|
|
130
|
+
// Step 5: Bulk operations
|
|
131
|
+
console.log('\n\nStep 5: Bulk operations');
|
|
132
|
+
console.log('----------------------');
|
|
133
|
+
|
|
134
|
+
// Get all items
|
|
135
|
+
const allDocuments = documentCacheMap.allIn([]);
|
|
136
|
+
const allComments = commentCacheMap.allIn([]);
|
|
137
|
+
|
|
138
|
+
console.log(`📋 Retrieved all items:`);
|
|
139
|
+
console.log(` 📄 Documents: ${allDocuments.length} items`);
|
|
140
|
+
allDocuments.forEach(doc => console.log(` - "${doc.title}" (${doc.tags.join(', ')})`));
|
|
141
|
+
|
|
142
|
+
console.log(` 💬 Comments: ${allComments.length} items`);
|
|
143
|
+
allComments.forEach(comment => console.log(` - "${comment.content}" on doc ${comment.documentId}`));
|
|
144
|
+
|
|
145
|
+
// Get all values (another way to access items)
|
|
146
|
+
const allDocumentValues = documentCacheMap.values();
|
|
147
|
+
console.log(`\n📦 Document values count: ${allDocumentValues.length}`);
|
|
148
|
+
|
|
149
|
+
// Step 6: Location-based operations for contained items
|
|
150
|
+
console.log('\n\nStep 6: Location-based operations');
|
|
151
|
+
console.log('---------------------------------');
|
|
152
|
+
|
|
153
|
+
// Get comments for specific document (using location filtering)
|
|
154
|
+
const doc1Comments = commentCacheMap.allIn([{ kt: 'document' as const, lk: doc1.id }] as any);
|
|
155
|
+
|
|
156
|
+
console.log(`🔍 Location-based retrieval:`);
|
|
157
|
+
console.log(` 💬 Comments in document "${doc1.title}": ${doc1Comments.length} found`);
|
|
158
|
+
doc1Comments.forEach(comment => console.log(` - "${comment.content}" by ${comment.author}`));
|
|
159
|
+
|
|
160
|
+
// Step 7: Update operations
|
|
161
|
+
console.log('\n\nStep 7: Update operations');
|
|
162
|
+
console.log('------------------------');
|
|
163
|
+
|
|
164
|
+
// Update an existing document
|
|
165
|
+
const updatedDoc1 = {
|
|
166
|
+
...doc1,
|
|
167
|
+
title: 'Getting Started with Fjell - Updated',
|
|
168
|
+
tags: [...doc1.tags, 'updated'],
|
|
169
|
+
events: { ...doc1.events, updated: { at: new Date() } }
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
documentCacheMap.set(updatedDoc1.key, updatedDoc1);
|
|
173
|
+
const retrievedUpdatedDoc = documentCacheMap.get(updatedDoc1.key);
|
|
174
|
+
|
|
175
|
+
console.log(`🔄 Updated document:`);
|
|
176
|
+
console.log(` 📄 New title: "${retrievedUpdatedDoc?.title}"`);
|
|
177
|
+
console.log(` 🏷️ New tags: ${retrievedUpdatedDoc?.tags.join(', ')}`);
|
|
178
|
+
|
|
179
|
+
// Step 8: Deletion operations
|
|
180
|
+
console.log('\n\nStep 8: Deletion operations');
|
|
181
|
+
console.log('--------------------------');
|
|
182
|
+
|
|
183
|
+
// Delete a specific item
|
|
184
|
+
documentCacheMap.delete(doc3.key);
|
|
185
|
+
console.log(`🗑️ Deleted document: doc-3`);
|
|
186
|
+
console.log(` 📄 Documents remaining: ${documentCacheMap.values().length}`);
|
|
187
|
+
|
|
188
|
+
// Try to get deleted item
|
|
189
|
+
const deletedDocCheck = documentCacheMap.get(doc3.key);
|
|
190
|
+
console.log(` 🔍 Deleted document still exists: ${deletedDocCheck !== null}`);
|
|
191
|
+
|
|
192
|
+
// Step 9: Performance and statistics
|
|
193
|
+
console.log('\n\nStep 9: Performance and statistics');
|
|
194
|
+
console.log('---------------------------------');
|
|
195
|
+
|
|
196
|
+
console.log(`📊 CacheMap Statistics:`);
|
|
197
|
+
console.log(` 📄 Document CacheMap:`);
|
|
198
|
+
console.log(` - Items: ${documentCacheMap.values().length}`);
|
|
199
|
+
console.log(` - Keys: ${documentCacheMap.keys().length}`);
|
|
200
|
+
console.log(` - Values: ${documentCacheMap.values().length}`);
|
|
201
|
+
|
|
202
|
+
console.log(` 💬 Comment CacheMap:`);
|
|
203
|
+
console.log(` - Items: ${commentCacheMap.values().length}`);
|
|
204
|
+
console.log(` - Keys: ${commentCacheMap.keys().length}`);
|
|
205
|
+
console.log(` - Values: ${commentCacheMap.values().length}`);
|
|
206
|
+
|
|
207
|
+
// Performance test - bulk operations
|
|
208
|
+
const startTime = Date.now();
|
|
209
|
+
for (let i = 0; i < 1000; i++) {
|
|
210
|
+
const tempDoc = createDocument(`temp-${i}`, `Temp Doc ${i}`, 'Content', 'Author', ['temp']);
|
|
211
|
+
documentCacheMap.set(tempDoc.key, tempDoc);
|
|
212
|
+
}
|
|
213
|
+
const insertTime = Date.now() - startTime;
|
|
214
|
+
|
|
215
|
+
console.log(`\n⚡ Performance test:`);
|
|
216
|
+
console.log(` 📥 Inserted 1000 items in ${insertTime}ms`);
|
|
217
|
+
console.log(` 📊 Total documents: ${documentCacheMap.values().length}`);
|
|
218
|
+
|
|
219
|
+
// Clean up performance test data
|
|
220
|
+
const cleanupStart = Date.now();
|
|
221
|
+
for (let i = 0; i < 1000; i++) {
|
|
222
|
+
documentCacheMap.delete({ kt: 'document', pk: `temp-${i}` });
|
|
223
|
+
}
|
|
224
|
+
const cleanupTime = Date.now() - cleanupStart;
|
|
225
|
+
|
|
226
|
+
console.log(` 🧹 Cleaned up 1000 items in ${cleanupTime}ms`);
|
|
227
|
+
console.log(` 📊 Documents after cleanup: ${documentCacheMap.values().length}`);
|
|
228
|
+
|
|
229
|
+
// Step 10: Clone operations
|
|
230
|
+
console.log('\n\nStep 10: Clone operations');
|
|
231
|
+
console.log('------------------------');
|
|
232
|
+
|
|
233
|
+
// Clone the cache map
|
|
234
|
+
const clonedDocumentCache = documentCacheMap.clone();
|
|
235
|
+
console.log(`📋 Cloned document cache:`);
|
|
236
|
+
console.log(` 📄 Original cache: ${documentCacheMap.values().length} items`);
|
|
237
|
+
console.log(` 📄 Cloned cache: ${clonedDocumentCache.values().length} items`);
|
|
238
|
+
|
|
239
|
+
// Modify original to show independence
|
|
240
|
+
const newDoc = createDocument('doc-clone-test', 'Clone Test Doc', 'Testing cloning', 'Test Author', ['test']);
|
|
241
|
+
documentCacheMap.set(newDoc.key, newDoc);
|
|
242
|
+
|
|
243
|
+
console.log(`\n📊 After adding to original:`);
|
|
244
|
+
console.log(` 📄 Original cache: ${documentCacheMap.values().length} items`);
|
|
245
|
+
console.log(` 📄 Cloned cache: ${clonedDocumentCache.values().length} items`);
|
|
246
|
+
console.log(` ✅ Clones are independent`);
|
|
247
|
+
|
|
248
|
+
console.log('\n🎉 CacheMap Example Complete!');
|
|
249
|
+
console.log('=============================\n');
|
|
250
|
+
|
|
251
|
+
console.log('Key concepts demonstrated:');
|
|
252
|
+
console.log('• Direct CacheMap instantiation and management');
|
|
253
|
+
console.log('• Primary key and composite key operations');
|
|
254
|
+
console.log('• Bulk operations (allIn, values, keys)');
|
|
255
|
+
console.log('• Location-based filtering for contained items');
|
|
256
|
+
console.log('• Update and delete operations');
|
|
257
|
+
console.log('• Performance characteristics');
|
|
258
|
+
console.log('• Key normalization and comparison');
|
|
259
|
+
console.log('• Cache cloning and independence\n');
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// Run the example if this file is executed directly
|
|
263
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
264
|
+
runCacheMapExample().catch(console.error);
|
|
265
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fjell/cache",
|
|
3
3
|
"description": "Cache for Fjell",
|
|
4
|
-
"version": "4.6.
|
|
4
|
+
"version": "4.6.10",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cache",
|
|
7
7
|
"fjell"
|
|
@@ -19,30 +19,31 @@
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@fjell/client-api": "^4.4.
|
|
23
|
-
"@fjell/core": "^4.4.
|
|
24
|
-
"@fjell/http-api": "^4.4.
|
|
25
|
-
"@fjell/logging": "^4.4.
|
|
22
|
+
"@fjell/client-api": "^4.4.7",
|
|
23
|
+
"@fjell/core": "^4.4.7",
|
|
24
|
+
"@fjell/http-api": "^4.4.5",
|
|
25
|
+
"@fjell/logging": "^4.4.7",
|
|
26
|
+
"@fjell/registry": "^4.4.7"
|
|
26
27
|
},
|
|
27
28
|
"devDependencies": {
|
|
28
29
|
"@eslint/eslintrc": "^3.3.1",
|
|
29
|
-
"@eslint/js": "^9.
|
|
30
|
-
"@swc/core": "^1.
|
|
30
|
+
"@eslint/js": "^9.31.0",
|
|
31
|
+
"@swc/core": "^1.13.1",
|
|
31
32
|
"@tsconfig/recommended": "^1.0.10",
|
|
32
33
|
"@types/multer": "^2.0.0",
|
|
33
|
-
"@types/node": "^24.0.
|
|
34
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
35
|
-
"@typescript-eslint/parser": "^8.
|
|
34
|
+
"@types/node": "^24.0.15",
|
|
35
|
+
"@typescript-eslint/eslint-plugin": "^8.37.0",
|
|
36
|
+
"@typescript-eslint/parser": "^8.37.0",
|
|
36
37
|
"@vitest/coverage-v8": "^3.2.4",
|
|
37
38
|
"@vitest/ui": "^3.2.4",
|
|
38
39
|
"concurrently": "^9.2.0",
|
|
39
|
-
"eslint": "^9.
|
|
40
|
+
"eslint": "^9.31.0",
|
|
40
41
|
"nodemon": "^3.1.10",
|
|
41
42
|
"rimraf": "^6.0.1",
|
|
42
43
|
"ts-node": "^10.9.2",
|
|
43
44
|
"tsc-alias": "^1.8.16",
|
|
44
45
|
"typescript": "^5.8.3",
|
|
45
|
-
"vite": "^7.0.
|
|
46
|
+
"vite": "^7.0.5",
|
|
46
47
|
"vite-plugin-dts": "^4.5.4",
|
|
47
48
|
"vite-plugin-node": "^7.0.0",
|
|
48
49
|
"vitest": "^3.2.4"
|
package/vitest.config.ts
CHANGED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
4
|
-
|
|
5
|
-
const logger$1 = require('./logger.cjs.js');
|
|
6
|
-
|
|
7
|
-
function _define_property(obj, key, value) {
|
|
8
|
-
if (key in obj) {
|
|
9
|
-
Object.defineProperty(obj, key, {
|
|
10
|
-
value: value,
|
|
11
|
-
enumerable: true,
|
|
12
|
-
configurable: true,
|
|
13
|
-
writable: true
|
|
14
|
-
});
|
|
15
|
-
} else {
|
|
16
|
-
obj[key] = value;
|
|
17
|
-
}
|
|
18
|
-
return obj;
|
|
19
|
-
}
|
|
20
|
-
const logger = logger$1.default.get('CacheRegistry');
|
|
21
|
-
class CacheRegistry {
|
|
22
|
-
constructor(){
|
|
23
|
-
// TODO: My use of Generics has Boxed me into a corner where I can't reference AbstractCache without the types
|
|
24
|
-
_define_property(this, "cacheMap", {});
|
|
25
|
-
_define_property(this, "registerCache", async (cache)=>{
|
|
26
|
-
try {
|
|
27
|
-
logger.debug('Attempting to register cache with pkTypes:', cache.pkTypes);
|
|
28
|
-
const key = JSON.stringify(cache.pkTypes);
|
|
29
|
-
if (this.cacheMap[key]) {
|
|
30
|
-
logger.debug(`Cache with pkTypes ${key} already exists, will be overwritten`);
|
|
31
|
-
}
|
|
32
|
-
this.cacheMap[key] = cache;
|
|
33
|
-
logger.debug('Cache registered successfully with key:', key);
|
|
34
|
-
} catch (error) {
|
|
35
|
-
logger.error('Failed to register cache:', error);
|
|
36
|
-
throw error;
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
_define_property(this, "getCache", (kts)=>{
|
|
40
|
-
logger.debug('Attempting to get cache for key types:', kts);
|
|
41
|
-
const key = JSON.stringify(kts);
|
|
42
|
-
logger.debug('Looking up cache with key:', key);
|
|
43
|
-
const cache = this.cacheMap[key];
|
|
44
|
-
if (!cache) {
|
|
45
|
-
logger.warning(`No cache found for key types: ${key}`);
|
|
46
|
-
}
|
|
47
|
-
return cache;
|
|
48
|
-
});
|
|
49
|
-
_define_property(this, "printRegisteredCaches", ()=>{
|
|
50
|
-
logger.debug('Printing all registered caches:');
|
|
51
|
-
const cacheCount = Object.keys(this.cacheMap).length;
|
|
52
|
-
logger.debug(`Total number of registered caches: ${cacheCount}`);
|
|
53
|
-
if (cacheCount === 0) {
|
|
54
|
-
logger.debug('No caches are currently registered');
|
|
55
|
-
}
|
|
56
|
-
Object.entries(this.cacheMap).forEach(([keyTypes])=>{
|
|
57
|
-
logger.debug(`Cache with key types: ${keyTypes}`);
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
logger.debug('CacheRegistry instance created');
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
_define_property(CacheRegistry, "instance", void 0);
|
|
64
|
-
|
|
65
|
-
exports.CacheRegistry = CacheRegistry;
|
|
66
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ2FjaGVSZWdpc3RyeS5janMuanMiLCJzb3VyY2VzIjpbXSwic291cmNlc0NvbnRlbnQiOltdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
|
package/dist/CacheRegistry.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Item } from '@fjell/core';
|
|
2
|
-
import { Cache } from './Cache';
|
|
3
|
-
export declare class CacheRegistry {
|
|
4
|
-
private static instance;
|
|
5
|
-
constructor();
|
|
6
|
-
private cacheMap;
|
|
7
|
-
registerCache: <S extends string, L1 extends string = never, L2 extends string = never, L3 extends string = never, L4 extends string = never, L5 extends string = never>(cache: Cache<Item<S, L1, L2, L3, L4, L5>, S, L1, L2, L3, L4, L5>) => Promise<void>;
|
|
8
|
-
getCache: (kts: string[]) => any;
|
|
9
|
-
printRegisteredCaches: () => void;
|
|
10
|
-
}
|