@fjell/cache 4.6.7 → 4.6.11

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 (77) hide show
  1. package/dist/Aggregator.cjs.js +26 -20
  2. package/dist/Aggregator.d.ts +17 -1
  3. package/dist/Aggregator.es.js +26 -20
  4. package/dist/Cache.cjs.js +22 -345
  5. package/dist/Cache.d.ts +25 -20
  6. package/dist/Cache.es.js +22 -346
  7. package/dist/Instance.cjs.js +23 -0
  8. package/dist/Instance.d.ts +17 -0
  9. package/dist/Instance.es.js +18 -0
  10. package/dist/InstanceFactory.cjs.js +35 -0
  11. package/dist/InstanceFactory.d.ts +8 -0
  12. package/dist/InstanceFactory.es.js +31 -0
  13. package/dist/Operations.cjs.js +43 -0
  14. package/dist/Operations.d.ts +70 -0
  15. package/dist/Operations.es.js +39 -0
  16. package/dist/Registry.cjs.js +36 -0
  17. package/dist/Registry.d.ts +15 -0
  18. package/dist/Registry.es.js +31 -0
  19. package/dist/index.cjs +459 -408
  20. package/dist/index.cjs.js +12 -3
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.ts +4 -1
  23. package/dist/index.es.js +6 -3
  24. package/dist/ops/action.cjs.js +28 -0
  25. package/dist/ops/action.d.ts +4 -0
  26. package/dist/ops/action.es.js +24 -0
  27. package/dist/ops/all.cjs.js +33 -0
  28. package/dist/ops/all.d.ts +4 -0
  29. package/dist/ops/all.es.js +29 -0
  30. package/dist/ops/allAction.cjs.js +35 -0
  31. package/dist/ops/allAction.d.ts +4 -0
  32. package/dist/ops/allAction.es.js +31 -0
  33. package/dist/ops/allFacet.cjs.js +22 -0
  34. package/dist/ops/allFacet.d.ts +4 -0
  35. package/dist/ops/allFacet.es.js +18 -0
  36. package/dist/ops/create.cjs.js +23 -0
  37. package/dist/ops/create.d.ts +4 -0
  38. package/dist/ops/create.es.js +19 -0
  39. package/dist/ops/facet.cjs.js +21 -0
  40. package/dist/ops/facet.d.ts +4 -0
  41. package/dist/ops/facet.es.js +17 -0
  42. package/dist/ops/find.cjs.js +26 -0
  43. package/dist/ops/find.d.ts +4 -0
  44. package/dist/ops/find.es.js +22 -0
  45. package/dist/ops/findOne.cjs.js +24 -0
  46. package/dist/ops/findOne.d.ts +4 -0
  47. package/dist/ops/findOne.es.js +20 -0
  48. package/dist/ops/get.cjs.js +38 -0
  49. package/dist/ops/get.d.ts +4 -0
  50. package/dist/ops/get.es.js +34 -0
  51. package/dist/ops/one.cjs.js +33 -0
  52. package/dist/ops/one.d.ts +4 -0
  53. package/dist/ops/one.es.js +29 -0
  54. package/dist/ops/remove.cjs.js +30 -0
  55. package/dist/ops/remove.d.ts +4 -0
  56. package/dist/ops/remove.es.js +26 -0
  57. package/dist/ops/reset.cjs.js +15 -0
  58. package/dist/ops/reset.d.ts +4 -0
  59. package/dist/ops/reset.es.js +11 -0
  60. package/dist/ops/retrieve.cjs.js +37 -0
  61. package/dist/ops/retrieve.d.ts +4 -0
  62. package/dist/ops/retrieve.es.js +33 -0
  63. package/dist/ops/set.cjs.js +71 -0
  64. package/dist/ops/set.d.ts +3 -0
  65. package/dist/ops/set.es.js +67 -0
  66. package/dist/ops/update.cjs.js +34 -0
  67. package/dist/ops/update.d.ts +4 -0
  68. package/dist/ops/update.es.js +30 -0
  69. package/examples/README.md +302 -0
  70. package/examples/aggregator-example.ts +328 -0
  71. package/examples/basic-cache-example.ts +270 -0
  72. package/examples/cache-map-example.ts +265 -0
  73. package/package.json +7 -6
  74. package/vitest.config.ts +2 -2
  75. package/dist/CacheRegistry.cjs.js +0 -66
  76. package/dist/CacheRegistry.d.ts +0 -10
  77. package/dist/CacheRegistry.es.js +0 -62
@@ -0,0 +1,302 @@
1
+ # Fjell-Cache Examples
2
+
3
+ This directory contains examples demonstrating how to use fjell-cache for caching data models and managing complex business relationships with different patterns and complexity levels.
4
+
5
+ ## Examples
6
+
7
+ ### 1. `basic-cache-example.ts` ⭐ **Start Here!**
8
+ **Perfect for beginners!** Demonstrates the fundamental way to use fjell-cache for data caching:
9
+ - **Basic cache operations** - Create caches with coordinates and registries, use operations API
10
+ - **Simple data models** - User and Task entities with mock storage
11
+ - **Cache-as-Instance architecture** - Caches extend Instance from fjell-registry
12
+ - **Cache hits vs misses** - Understand cache behavior and performance benefits
13
+ - **Cache management** - Updates, deletions, and data consistency through operations
14
+
15
+ Great for understanding the fundamentals of fjell-cache data management.
16
+
17
+ ### 2. `aggregator-example.ts` 🏗️ **Advanced Business Relationships**
18
+ **Complete business relationship management!** Demonstrates advanced caching patterns with entity relationships:
19
+ - **Multiple interconnected models**: Customer, Order, Product, SupportTicket
20
+ - **Automatic reference population**: Orders automatically include customer data
21
+ - **Required vs optional aggregates**: Flexible relationship management
22
+ - **Complex business scenarios**: E-commerce platform with customer management
23
+ - **Performance optimization**: Cache efficiency through aggregated data
24
+
25
+ Shows how fjell-cache handles enterprise data relationship patterns.
26
+
27
+ ### 3. `cache-map-example.ts` 🔧 **Low-Level Cache Operations**
28
+ **Direct cache management!** Demonstrates lower-level CacheMap functionality:
29
+ - **Direct CacheMap usage**: Create and manage cache maps without higher-level abstractions
30
+ - **Primary and composite keys**: Handle both simple and complex key structures
31
+ - **Location-based operations**: Filter contained items by location hierarchy
32
+ - **Performance characteristics**: Bulk operations and efficiency testing
33
+ - **Cache lifecycle**: Cloning, cleanup, and memory management
34
+
35
+ Perfect for understanding the underlying cache mechanisms and advanced use cases.
36
+
37
+ ## Key Concepts Demonstrated
38
+
39
+ ### Basic Caching Operations (basic-cache-example.ts)
40
+ ```typescript
41
+ // Import fjell-cache functionality
42
+ import { createCache } from '@fjell/cache';
43
+ import { createCoordinate, createRegistry } from '@fjell/registry';
44
+ import { ClientApi } from '@fjell/client-api';
45
+
46
+ // Create a registry for cache management
47
+ const registry = createRegistry();
48
+
49
+ // Create a cache instance with API integration
50
+ const userApi = createUserApi(); // Your API implementation
51
+ const userCache = await createCache(userApi, createCoordinate('user'), registry);
52
+
53
+ // Cache is now an instance - no need for separate createInstance call
54
+ // Perform cache operations through the operations API
55
+ const [cacheMap, allUsers] = await userCache.operations.all();
56
+ const [, cachedUser] = await userCache.operations.get(userKey);
57
+ const [, retrievedUser] = await userCache.operations.retrieve(userKey); // Cache hit!
58
+
59
+ await userCache.operations.set(userKey, updatedUser);
60
+ ```
61
+
62
+ ### Advanced Aggregation (aggregator-example.ts)
63
+ ```typescript
64
+ // Create aggregated cache with relationships
65
+ const orderAggregator = await createAggregator(orderCache, {
66
+ aggregates: {
67
+ customer: { cache: customerCache, optional: false }, // Required reference
68
+ product: { cache: productCache, optional: true }, // Optional reference
69
+ },
70
+ events: {}
71
+ });
72
+
73
+ // Automatically populate related entities
74
+ const populatedOrder = await orderAggregator.populate(order);
75
+ if (populatedOrder.aggs?.customer?.item) {
76
+ const customer = populatedOrder.aggs.customer.item;
77
+ console.log(`Order for: ${customer.name} (${customer.email})`);
78
+ }
79
+
80
+ // Aggregator is now itself an instance - access operations directly
81
+ const [, orders] = await orderAggregator.all();
82
+ ```
83
+
84
+ ### Direct Cache Management (cache-map-example.ts)
85
+ ```typescript
86
+ // Create CacheMap instances directly
87
+ const documentCacheMap = new CacheMap<Document, 'document'>(['document']);
88
+ const commentCacheMap = new CacheMap<Comment, 'comment', 'document'>(['comment', 'document']);
89
+
90
+ // Basic operations
91
+ documentCacheMap.set(documentKey, document);
92
+ const retrievedDoc = documentCacheMap.get(documentKey);
93
+ const hasDoc = documentCacheMap.includesKey(documentKey);
94
+
95
+ // Bulk operations
96
+ const allDocuments = documentCacheMap.allIn([]);
97
+ const allKeys = documentCacheMap.keys();
98
+ const allValues = documentCacheMap.values();
99
+
100
+ // Location-based filtering for contained items
101
+ const commentsInDoc = commentCacheMap.allIn([{ kt: 'document', lk: documentId }]);
102
+
103
+ // Performance operations
104
+ const clonedCache = documentCacheMap.clone();
105
+ documentCacheMap.delete(documentKey);
106
+ ```
107
+
108
+ ### Data Model Patterns
109
+
110
+ #### Primary Items
111
+ - Standalone entities (User, Customer, Document)
112
+ - No location hierarchy constraints
113
+ - Simple key structure: `Item<'keyType'>`
114
+
115
+ #### Contained Items
116
+ - Nested within other entities or locations
117
+ - Multi-level location keys for organization
118
+ - Complex key structure: `Item<'keyType', 'location1', 'location2', ...>`
119
+
120
+ #### Aggregated Items
121
+ - Items with automatic reference population
122
+ - Business relationships through cache aggregation
123
+ - Performance optimized through cached references
124
+
125
+ ## Running Examples
126
+
127
+ ```bash
128
+ # Start with the basic example (recommended)
129
+ npx tsx examples/basic-cache-example.ts
130
+
131
+ # Run the aggregator example
132
+ npx tsx examples/aggregator-example.ts
133
+
134
+ # Run the cache map example
135
+ npx tsx examples/cache-map-example.ts
136
+
137
+ # Or with Node.js
138
+ node -r esbuild-register examples/basic-cache-example.ts
139
+ ```
140
+
141
+ ## Integration with Real Applications
142
+
143
+ All examples use the actual fjell-cache functionality! In production applications:
144
+
145
+ ```typescript
146
+ import { createCache, createRegistry, createInstance, createAggregator } from '@fjell/cache';
147
+ import { ClientApi } from '@fjell/client-api';
148
+
149
+ // Basic cache setup
150
+ const registry = createRegistry();
151
+
152
+ const userCache = await createCache(userApi, 'user');
153
+ const userInstance = createInstance(registry, createCoordinate('user'), userCache);
154
+
155
+ // With aggregation for business relationships
156
+ const orderAggregator = await createAggregator(orderCache, {
157
+ aggregates: {
158
+ customer: { cache: customerCache, optional: false },
159
+ items: { cache: productCache, optional: true }
160
+ },
161
+ events: {
162
+ orderUpdated: async (key, item) => {
163
+ // Custom event handling
164
+ await notifyCustomer(item);
165
+ }
166
+ }
167
+ });
168
+
169
+ // Advanced cache configuration
170
+ const options = {
171
+ cacheSize: 10000,
172
+ ttl: 3600000, // 1 hour
173
+ refreshThreshold: 0.8,
174
+ compression: true
175
+ };
176
+ ```
177
+
178
+ ## Cache Operation Types
179
+
180
+ ### Basic Operations
181
+ - **all()**: Get all items and update cache
182
+ - **get()**: Get item by key, fetch from API if not cached
183
+ - **retrieve()**: Get item by key, return null if not cached
184
+ - **set()**: Store item in cache
185
+ - **one()**: Get single item
186
+ - **find()**: Search items with finder methods
187
+
188
+ ### Aggregation Operations
189
+ - **populate()**: Automatically populate item with related entities
190
+ - **populateAggregate()**: Populate specific aggregate relationship
191
+ - **populateEvent()**: Handle population events
192
+
193
+ ### Cache Management
194
+ - **allIn()**: Get items by location (for contained items)
195
+ - **queryIn()**: Query items by location with filtering
196
+ - **clone()**: Create independent cache copy
197
+ - **delete()**: Remove item from cache
198
+ - **clear()**: Clear all cache contents
199
+
200
+ ### Business Operations
201
+ ```typescript
202
+ // Cache with business logic integration
203
+ const cache = await createCache(api, 'order', {
204
+ hooks: {
205
+ beforeGet: async (key) => { /* validation */ },
206
+ afterSet: async (key, item) => { /* notifications */ }
207
+ },
208
+ validators: {
209
+ status: (status) => ['pending', 'shipped', 'delivered'].includes(status)
210
+ },
211
+ aggregates: {
212
+ customer: customerCache,
213
+ items: productCache
214
+ }
215
+ });
216
+ ```
217
+
218
+ ## When to Use What
219
+
220
+ **Use `basic-cache-example.ts` approach when:**
221
+ - Learning fjell-cache fundamentals
222
+ - Building simple applications with caching needs
223
+ - Need basic get/set cache operations
224
+ - Working with independent data models
225
+
226
+ **Use `aggregator-example.ts` approach when:**
227
+ - Managing complex business relationships
228
+ - Need automatic population of related entities
229
+ - Building enterprise applications with interconnected data
230
+ - Require performance optimization through aggregated caching
231
+ - Working with customer/order/product type relationships
232
+
233
+ **Use `cache-map-example.ts` approach when:**
234
+ - Need direct control over cache operations
235
+ - Building custom caching solutions
236
+ - Working with contained items and location hierarchies
237
+ - Require maximum performance and minimal overhead
238
+ - Implementing cache-based data structures
239
+
240
+ ## Advanced Features
241
+
242
+ ### Cache Aggregation
243
+ ```typescript
244
+ // Complex aggregation with optional and required references
245
+ const ticketAggregator = await createAggregator(ticketCache, {
246
+ aggregates: {
247
+ customer: { cache: customerCache, optional: false }, // Always populated
248
+ order: { cache: orderCache, optional: true }, // Only if orderId exists
249
+ assignee: { cache: userCache, optional: true } // Only if assigned
250
+ },
251
+ events: {
252
+ ticketAssigned: async (key, ticket) => {
253
+ await notifyAssignee(ticket);
254
+ }
255
+ }
256
+ });
257
+
258
+ // Automatic population includes all available references
259
+ const populatedTicket = await ticketAggregator.populate(ticket);
260
+ ```
261
+
262
+ ### Performance Optimization
263
+ ```typescript
264
+ // Cache with coordinate and registry
265
+ const cache = await createCache(api, createCoordinate('product'), registry);
266
+
267
+ // Bulk operations for efficiency
268
+ const [cacheMap, allProducts] = await cache.operations.all();
269
+ const productMap = new Map(allProducts.map(p => [p.id, p]));
270
+
271
+ // Access cache properties for optimization
272
+ console.log(`Cache coordinate: ${cache.coordinate.kta.join(', ')}`);
273
+ console.log(`Cached items: ${cache.cacheMap.size()}`);
274
+ ```
275
+
276
+ ### Storage Integration
277
+ Fjell-cache works with any storage backend through the ClientApi interface:
278
+ - SQL databases (PostgreSQL, MySQL, SQLite)
279
+ - NoSQL databases (MongoDB, DynamoDB, Redis)
280
+ - REST APIs and GraphQL endpoints
281
+ - In-memory stores and mock data
282
+ - File systems and cloud storage
283
+ - Custom data sources
284
+
285
+ ### Error Handling and Resilience
286
+ ```typescript
287
+ // Cache with proper error handling through the API layer
288
+ const resilientCache = await createCache(api, createCoordinate('user'), registry);
289
+
290
+ // Error handling in operations
291
+ try {
292
+ const [, user] = await resilientCache.operations.get(userKey);
293
+ return user;
294
+ } catch (error) {
295
+ // Handle cache errors gracefully
296
+ console.error('Cache operation failed:', error);
297
+ // Fallback to direct API call
298
+ return await api.get(userKey);
299
+ }
300
+ ```
301
+
302
+ This provides the foundation for building scalable, maintainable applications with intelligent caching using fjell-cache.
@@ -0,0 +1,328 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ /**
3
+ * Aggregator Example
4
+ *
5
+ * This example demonstrates advanced fjell-cache functionality using the Aggregator
6
+ * for managing related entities with automatic population of references.
7
+ *
8
+ * Shows how to:
9
+ * - Create aggregated caches with references between entities
10
+ * - Populate items with their related data automatically
11
+ * - Handle optional vs required aggregates
12
+ * - Manage complex business relationships through caching
13
+ */
14
+
15
+ import { createAggregator } from '../src/Aggregator';
16
+ import { createCache } from '../src/Cache';
17
+ import { createInstance } from '../src/Instance';
18
+ import { createRegistry } from '../src/Registry';
19
+ import { ClientApi } from '@fjell/client-api';
20
+ import { Item, PriKey } from '@fjell/core';
21
+ import { createCoordinate } from '@fjell/registry';
22
+
23
+ // Define our business models with relationships
24
+ interface Customer extends Item<'customer'> {
25
+ id: string;
26
+ name: string;
27
+ email: string;
28
+ company: string;
29
+ tier: 'bronze' | 'silver' | 'gold' | 'platinum';
30
+ }
31
+
32
+ interface Order extends Item<'order'> {
33
+ id: string;
34
+ customerId: string;
35
+ total: number;
36
+ status: 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled';
37
+ items: string[]; // Array of product IDs
38
+ orderDate: Date;
39
+ }
40
+
41
+ interface Product extends Item<'product'> {
42
+ id: string;
43
+ name: string;
44
+ price: number;
45
+ category: string;
46
+ inStock: boolean;
47
+ }
48
+
49
+ interface SupportTicket extends Item<'ticket'> {
50
+ id: string;
51
+ customerId: string;
52
+ orderId?: string; // Optional reference to order
53
+ subject: string;
54
+ priority: 'low' | 'medium' | 'high' | 'urgent';
55
+ status: 'open' | 'in-progress' | 'resolved' | 'closed';
56
+ description: string;
57
+ }
58
+
59
+ // Mock storage
60
+ const mockCustomers = new Map<string, Customer>();
61
+ const mockOrders = new Map<string, Order>();
62
+ const mockProducts = new Map<string, Product>();
63
+ const mockTickets = new Map<string, SupportTicket>();
64
+
65
+ // Helper to create mock APIs
66
+ const createMockApi = <T extends Item<any>>(storage: Map<string, T>) => {
67
+ return {
68
+ async all(_query = {}) {
69
+ console.log(`📦 Fetching all items from ${storage.constructor.name}...`);
70
+ return Array.from(storage.values());
71
+ },
72
+ async one(query = {}) {
73
+ const items = await this.all!(query);
74
+ return items[0] || null;
75
+ },
76
+ async get(key: PriKey<any>) {
77
+ const item = storage.get(String(key.pk));
78
+ if (!item) {
79
+ throw new Error(`Item not found: ${key.pk}`);
80
+ }
81
+ return item;
82
+ },
83
+ async find(_finder = 'all') {
84
+ return await this.all!({});
85
+ }
86
+ } as Partial<ClientApi<T, any>>;
87
+ };
88
+
89
+ // Test data creation helpers
90
+ const createCustomer = (id: string, name: string, email: string, company: string, tier: 'bronze' | 'silver' | 'gold' | 'platinum'): Customer => {
91
+ const customer: Customer = {
92
+ id, name, email, company, tier,
93
+ key: { kt: 'customer', pk: id },
94
+ events: { created: { at: new Date() }, updated: { at: new Date() }, deleted: { at: null } }
95
+ };
96
+ mockCustomers.set(id, customer);
97
+ return customer;
98
+ };
99
+
100
+ const createProduct = (id: string, name: string, price: number, category: string, inStock: boolean): Product => {
101
+ const product: Product = {
102
+ id, name, price, category, inStock,
103
+ key: { kt: 'product', pk: id },
104
+ events: { created: { at: new Date() }, updated: { at: new Date() }, deleted: { at: null } }
105
+ };
106
+ mockProducts.set(id, product);
107
+ return product;
108
+ };
109
+
110
+ const createOrder = (id: string, customerId: string, total: number, status: 'pending' | 'processing' | 'shipped' | 'delivered' | 'cancelled', items: string[]): Order => {
111
+ const order: Order = {
112
+ id, customerId, total, status, items, orderDate: new Date(),
113
+ key: { kt: 'order', pk: id },
114
+ events: { created: { at: new Date() }, updated: { at: new Date() }, deleted: { at: null } },
115
+ // Add references for aggregation
116
+ refs: {
117
+ customer: { kt: 'customer', pk: customerId }
118
+ }
119
+ };
120
+ mockOrders.set(id, order);
121
+ return order;
122
+ };
123
+
124
+ const createSupportTicket = (
125
+ id: string,
126
+ customerId: string,
127
+ subject: string,
128
+ priority: 'low' | 'medium' | 'high' | 'urgent',
129
+ status: 'open' | 'in-progress' | 'resolved' | 'closed',
130
+ description: string,
131
+ orderId?: string): SupportTicket => {
132
+ const ticket: SupportTicket = {
133
+ id, customerId, subject, priority, status, description, orderId,
134
+ key: { kt: 'ticket', pk: id },
135
+ events: { created: { at: new Date() }, updated: { at: new Date() }, deleted: { at: null } },
136
+ // Add references for aggregation
137
+ refs: {
138
+ customer: { kt: 'customer', pk: customerId },
139
+ ...(orderId && { order: { kt: 'order', pk: orderId } })
140
+ }
141
+ };
142
+ mockTickets.set(id, ticket);
143
+ return ticket;
144
+ };
145
+
146
+ export const runAggregatorExample = async (): Promise<void> => {
147
+ console.log('\n🚀 Fjell-Cache Aggregator Example');
148
+ console.log('=================================\n');
149
+
150
+ console.log('This example demonstrates advanced caching with entity relationships and aggregation.\n');
151
+
152
+ // Step 1: Create test data
153
+ console.log('Step 1: Creating business entities');
154
+ console.log('----------------------------------');
155
+
156
+ const customer1 = createCustomer('cust-1', 'Acme Corp', 'contact@acme.com', 'Acme Corporation', 'gold');
157
+ const customer2 = createCustomer('cust-2', 'TechStart Inc', 'hello@techstart.io', 'TechStart Inc', 'silver');
158
+
159
+ const product1 = createProduct('prod-1', 'Premium Widget', 299.99, 'widgets', true);
160
+ const product2 = createProduct('prod-2', 'Standard Widget', 199.99, 'widgets', true);
161
+ const product3 = createProduct('prod-3', 'Budget Widget', 99.99, 'widgets', false);
162
+
163
+ const order1 = createOrder('order-1', customer1.id, 499.98, 'shipped', [product1.id, product2.id]);
164
+ const order2 = createOrder('order-2', customer2.id, 199.99, 'pending', [product2.id]);
165
+
166
+ const ticket1 = createSupportTicket('ticket-1', customer1.id, 'Widget not working', 'high', 'open', 'The premium widget stopped working after 2 days', order1.id);
167
+ const ticket2 = createSupportTicket('ticket-2', customer2.id, 'General inquiry', 'low', 'resolved', 'Question about widget compatibility');
168
+
169
+ console.log(`✅ Created ${mockCustomers.size} customers, ${mockProducts.size} products, ${mockOrders.size} orders, ${mockTickets.size} tickets\n`);
170
+
171
+ // Step 2: Set up cache infrastructure
172
+ console.log('Step 2: Setting up cache infrastructure');
173
+ console.log('--------------------------------------');
174
+
175
+ const registry = createRegistry();
176
+ console.log('✅ Created registry');
177
+
178
+ // Create individual caches for each entity type
179
+ const customerApi = createMockApi(mockCustomers) as ClientApi<Customer, 'customer'>;
180
+ const orderApi = createMockApi(mockOrders) as ClientApi<Order, 'order'>;
181
+ const productApi = createMockApi(mockProducts) as ClientApi<Product, 'product'>;
182
+ const ticketApi = createMockApi(mockTickets) as ClientApi<SupportTicket, 'ticket'>;
183
+
184
+ const customerCache = await createCache(customerApi, createCoordinate('customer'), registry);
185
+ const orderCache = await createCache(orderApi, createCoordinate('order'), registry);
186
+ const productCache = await createCache(productApi, createCoordinate('product'), registry);
187
+ const ticketCache = await createCache(ticketApi, createCoordinate('ticket'), registry);
188
+
189
+ console.log('✅ Created individual caches for each entity type');
190
+
191
+ // Step 3: Create aggregated caches
192
+ console.log('\nStep 3: Creating aggregated caches');
193
+ console.log('----------------------------------');
194
+
195
+ // Create order aggregator that automatically populates customer data
196
+ const orderAggregator = await createAggregator(orderCache, {
197
+ aggregates: {
198
+ customer: { cache: customerCache, optional: false }, // Required reference
199
+ },
200
+ events: {}
201
+ });
202
+
203
+ // Create support ticket aggregator with both customer and order references
204
+ const ticketAggregator = await createAggregator(ticketCache, {
205
+ aggregates: {
206
+ customer: { cache: customerCache, optional: false }, // Required reference
207
+ order: { cache: orderCache, optional: true }, // Optional reference (not all tickets relate to orders)
208
+ },
209
+ events: {}
210
+ });
211
+
212
+ console.log('✅ Created aggregated caches with relationship mappings (aggregators are now instances)\n');
213
+
214
+ // Step 4: Basic aggregation - Fetch orders with customer data
215
+ console.log('Step 4: Order aggregation with customer data');
216
+ console.log('--------------------------------------------');
217
+
218
+ const [, orders] = await orderAggregator.all();
219
+ console.log(`📋 Fetched ${orders.length} orders`);
220
+
221
+ for (const order of orders) {
222
+ console.log(`\n📦 Order ${order.id}:`);
223
+ console.log(` Amount: $${order.total}`);
224
+ console.log(` Status: ${order.status}`);
225
+ console.log(` Items: ${order.items.join(', ')}`);
226
+
227
+ // Populate the order with customer data
228
+ const populatedOrder = await orderAggregator.populate(order);
229
+ if (populatedOrder.aggs?.customer?.item) {
230
+ const customer = populatedOrder.aggs.customer.item;
231
+ console.log(` 👤 Customer: ${customer.name} (${customer.email})`);
232
+ console.log(` 🏢 Company: ${customer.company} - ${customer.tier} tier`);
233
+ }
234
+ }
235
+
236
+ // Step 5: Complex aggregation - Support tickets with multiple references
237
+ console.log('\n\nStep 5: Support ticket aggregation with multiple references');
238
+ console.log('----------------------------------------------------------');
239
+
240
+ const [, tickets] = await ticketAggregator.all();
241
+ console.log(`🎫 Fetched ${tickets.length} support tickets`);
242
+
243
+ for (const ticket of tickets) {
244
+ console.log(`\n🎫 Ticket ${ticket.id}:`);
245
+ console.log(` Subject: ${ticket.subject}`);
246
+ console.log(` Priority: ${ticket.priority}`);
247
+ console.log(` Status: ${ticket.status}`);
248
+ console.log(` Description: ${ticket.description}`);
249
+
250
+ // Populate the ticket with all related data
251
+ const populatedTicket = await ticketAggregator.populate(ticket);
252
+
253
+ // Customer data (required reference)
254
+ if (populatedTicket.aggs?.customer?.item) {
255
+ const customer = populatedTicket.aggs.customer.item;
256
+ console.log(` 👤 Customer: ${customer.name} (${customer.email}) - ${customer.tier} tier`);
257
+ }
258
+
259
+ // Order data (optional reference)
260
+ if (populatedTicket.aggs?.order?.item) {
261
+ const order = populatedTicket.aggs.order.item;
262
+ console.log(` 📦 Related Order: ${order.id} - $${order.total} (${order.status})`);
263
+ } else {
264
+ console.log(` 📦 No related order`);
265
+ }
266
+ }
267
+
268
+ // Step 6: Individual item retrieval with aggregation
269
+ console.log('\n\nStep 6: Individual item retrieval with aggregation');
270
+ console.log('-------------------------------------------------');
271
+
272
+ const [, specificOrder] = await orderAggregator.get(order1.key);
273
+ if (specificOrder) {
274
+ console.log(`🔍 Retrieved specific order: ${specificOrder.id}`);
275
+
276
+ const populatedSpecificOrder = await orderAggregator.populate(specificOrder);
277
+ if (populatedSpecificOrder.aggs?.customer?.item) {
278
+ const customer = populatedSpecificOrder.aggs.customer.item;
279
+ console.log(` Automatically populated customer: ${customer.name}`);
280
+ }
281
+ }
282
+
283
+ // Step 7: Demonstrating cache efficiency with aggregation
284
+ console.log('\n\nStep 7: Cache efficiency demonstration');
285
+ console.log('-------------------------------------');
286
+
287
+ console.log('🎯 First population (will fetch from storage):');
288
+ const startTime1 = Date.now();
289
+ const populated1 = await orderAggregator.populate(order1);
290
+ const time1 = Date.now() - startTime1;
291
+ console.log(` Populated order with customer data in ${time1}ms`);
292
+
293
+ console.log('🎯 Second population (should use cached data):');
294
+ const startTime2 = Date.now();
295
+ const populated2 = await orderAggregator.populate(order1);
296
+ const time2 = Date.now() - startTime2;
297
+ console.log(` Populated same order in ${time2}ms (cached)`);
298
+
299
+ console.log(` 📊 Cache efficiency: ${Math.round(((time1 - time2) / time1) * 100)}% faster on second call`);
300
+
301
+ // Step 8: Aggregate management and statistics
302
+ console.log('\n\nStep 8: Aggregate management and statistics');
303
+ console.log('------------------------------------------');
304
+
305
+ console.log('📊 Cache Statistics:');
306
+ console.log(` 👥 Customers cached: ${mockCustomers.size}`);
307
+ console.log(` 📦 Orders cached: ${mockOrders.size}`);
308
+ console.log(` 📦 Products cached: ${mockProducts.size}`);
309
+ console.log(` 🎫 Tickets cached: ${mockTickets.size}`);
310
+ console.log(` 🔗 Order aggregator references: customer (required)`);
311
+ console.log(` 🔗 Ticket aggregator references: customer (required), order (optional)`);
312
+
313
+ console.log('\n🎉 Aggregator Example Complete!');
314
+ console.log('===============================\n');
315
+
316
+ console.log('Key concepts demonstrated:');
317
+ console.log('• Creating aggregated caches with entity relationships');
318
+ console.log('• Automatic population of referenced entities');
319
+ console.log('• Required vs optional aggregate references');
320
+ console.log('• Cache efficiency through aggregation');
321
+ console.log('• Complex business model relationships');
322
+ console.log('• Performance benefits of cached aggregates\n');
323
+ };
324
+
325
+ // Run the example if this file is executed directly
326
+ if (import.meta.url === `file://${process.argv[1]}`) {
327
+ runAggregatorExample().catch(console.error);
328
+ }