@fjell/cache 4.6.14 ā 4.6.16
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/Aggregator.d.ts +3 -3
- package/dist/Cache.d.ts +5 -5
- package/dist/CacheMap.d.ts +1 -1
- package/dist/Instance.d.ts +4 -4
- package/dist/InstanceFactory.d.ts +3 -3
- package/dist/Operations.d.ts +4 -4
- package/dist/index.js +715 -0
- package/dist/index.js.map +7 -0
- package/dist/logger.d.ts +1 -1
- package/dist/ops/action.d.ts +3 -3
- package/dist/ops/all.d.ts +3 -3
- package/dist/ops/allAction.d.ts +3 -3
- package/dist/ops/allFacet.d.ts +3 -3
- package/dist/ops/create.d.ts +3 -3
- package/dist/ops/facet.d.ts +3 -3
- package/dist/ops/find.d.ts +3 -3
- package/dist/ops/findOne.d.ts +3 -3
- package/dist/ops/get.d.ts +3 -3
- package/dist/ops/one.d.ts +3 -3
- package/dist/ops/remove.d.ts +3 -3
- package/dist/ops/reset.d.ts +3 -3
- package/dist/ops/retrieve.d.ts +3 -3
- package/dist/ops/set.d.ts +2 -2
- package/dist/ops/update.d.ts +3 -3
- package/package.json +14 -18
- package/dist/Aggregator.cjs.js +0 -322
- package/dist/Aggregator.es.js +0 -317
- package/dist/Cache.cjs.js +0 -35
- package/dist/Cache.es.js +0 -30
- package/dist/CacheMap.cjs.js +0 -170
- package/dist/CacheMap.es.js +0 -166
- package/dist/Instance.cjs.js +0 -23
- package/dist/Instance.es.js +0 -18
- package/dist/InstanceFactory.cjs.js +0 -35
- package/dist/InstanceFactory.es.js +0 -31
- package/dist/Operations.cjs.js +0 -43
- package/dist/Operations.es.js +0 -39
- package/dist/Registry.cjs.js +0 -36
- package/dist/Registry.es.js +0 -31
- package/dist/index.cjs +0 -940
- package/dist/index.cjs.js +0 -26
- package/dist/index.cjs.map +0 -1
- package/dist/index.es.js +0 -8
- package/dist/logger.cjs.js +0 -10
- package/dist/logger.es.js +0 -6
- package/dist/ops/action.cjs.js +0 -28
- package/dist/ops/action.es.js +0 -24
- package/dist/ops/all.cjs.js +0 -33
- package/dist/ops/all.es.js +0 -29
- package/dist/ops/allAction.cjs.js +0 -35
- package/dist/ops/allAction.es.js +0 -31
- package/dist/ops/allFacet.cjs.js +0 -22
- package/dist/ops/allFacet.es.js +0 -18
- package/dist/ops/create.cjs.js +0 -23
- package/dist/ops/create.es.js +0 -19
- package/dist/ops/facet.cjs.js +0 -21
- package/dist/ops/facet.es.js +0 -17
- package/dist/ops/find.cjs.js +0 -26
- package/dist/ops/find.es.js +0 -22
- package/dist/ops/findOne.cjs.js +0 -24
- package/dist/ops/findOne.es.js +0 -20
- package/dist/ops/get.cjs.js +0 -38
- package/dist/ops/get.es.js +0 -34
- package/dist/ops/one.cjs.js +0 -33
- package/dist/ops/one.es.js +0 -29
- package/dist/ops/remove.cjs.js +0 -30
- package/dist/ops/remove.es.js +0 -26
- package/dist/ops/reset.cjs.js +0 -15
- package/dist/ops/reset.es.js +0 -11
- package/dist/ops/retrieve.cjs.js +0 -37
- package/dist/ops/retrieve.es.js +0 -33
- package/dist/ops/set.cjs.js +0 -71
- package/dist/ops/set.es.js +0 -67
- package/dist/ops/update.cjs.js +0 -34
- package/dist/ops/update.es.js +0 -30
- package/docs/docs.config.ts +0 -75
- package/docs/index.html +0 -18
- package/docs/package.json +0 -34
- package/docs/public/README.md +0 -96
- package/docs/public/examples-README.md +0 -302
- package/docs/public/test.txt +0 -0
- package/docs/src/index.css +0 -3
- package/docs/src/main.tsx +0 -12
- package/docs/src/test/setup.ts +0 -1
- package/docs/tsconfig.node.json +0 -15
- package/examples/README.md +0 -302
- package/examples/aggregator-example.ts +0 -329
- package/examples/basic-cache-example.ts +0 -270
- package/examples/cache-map-example.ts +0 -265
- package/vitest.config.ts +0 -44
|
@@ -1,329 +0,0 @@
|
|
|
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
|
-
// eslint-disable-next-line max-params
|
|
132
|
-
orderId?: string): SupportTicket => {
|
|
133
|
-
const ticket: SupportTicket = {
|
|
134
|
-
id, customerId, subject, priority, status, description, orderId,
|
|
135
|
-
key: { kt: 'ticket', pk: id },
|
|
136
|
-
events: { created: { at: new Date() }, updated: { at: new Date() }, deleted: { at: null } },
|
|
137
|
-
// Add references for aggregation
|
|
138
|
-
refs: {
|
|
139
|
-
customer: { kt: 'customer', pk: customerId },
|
|
140
|
-
...(orderId && { order: { kt: 'order', pk: orderId } })
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
mockTickets.set(id, ticket);
|
|
144
|
-
return ticket;
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
export const runAggregatorExample = async (): Promise<void> => {
|
|
148
|
-
console.log('\nš Fjell-Cache Aggregator Example');
|
|
149
|
-
console.log('=================================\n');
|
|
150
|
-
|
|
151
|
-
console.log('This example demonstrates advanced caching with entity relationships and aggregation.\n');
|
|
152
|
-
|
|
153
|
-
// Step 1: Create test data
|
|
154
|
-
console.log('Step 1: Creating business entities');
|
|
155
|
-
console.log('----------------------------------');
|
|
156
|
-
|
|
157
|
-
const customer1 = createCustomer('cust-1', 'Acme Corp', 'contact@acme.com', 'Acme Corporation', 'gold');
|
|
158
|
-
const customer2 = createCustomer('cust-2', 'TechStart Inc', 'hello@techstart.io', 'TechStart Inc', 'silver');
|
|
159
|
-
|
|
160
|
-
const product1 = createProduct('prod-1', 'Premium Widget', 299.99, 'widgets', true);
|
|
161
|
-
const product2 = createProduct('prod-2', 'Standard Widget', 199.99, 'widgets', true);
|
|
162
|
-
const product3 = createProduct('prod-3', 'Budget Widget', 99.99, 'widgets', false);
|
|
163
|
-
|
|
164
|
-
const order1 = createOrder('order-1', customer1.id, 499.98, 'shipped', [product1.id, product2.id]);
|
|
165
|
-
const order2 = createOrder('order-2', customer2.id, 199.99, 'pending', [product2.id]);
|
|
166
|
-
|
|
167
|
-
const ticket1 = createSupportTicket('ticket-1', customer1.id, 'Widget not working', 'high', 'open', 'The premium widget stopped working after 2 days', order1.id);
|
|
168
|
-
const ticket2 = createSupportTicket('ticket-2', customer2.id, 'General inquiry', 'low', 'resolved', 'Question about widget compatibility');
|
|
169
|
-
|
|
170
|
-
console.log(`ā
Created ${mockCustomers.size} customers, ${mockProducts.size} products, ${mockOrders.size} orders, ${mockTickets.size} tickets\n`);
|
|
171
|
-
|
|
172
|
-
// Step 2: Set up cache infrastructure
|
|
173
|
-
console.log('Step 2: Setting up cache infrastructure');
|
|
174
|
-
console.log('--------------------------------------');
|
|
175
|
-
|
|
176
|
-
const registry = createRegistry();
|
|
177
|
-
console.log('ā
Created registry');
|
|
178
|
-
|
|
179
|
-
// Create individual caches for each entity type
|
|
180
|
-
const customerApi = createMockApi(mockCustomers) as ClientApi<Customer, 'customer'>;
|
|
181
|
-
const orderApi = createMockApi(mockOrders) as ClientApi<Order, 'order'>;
|
|
182
|
-
const productApi = createMockApi(mockProducts) as ClientApi<Product, 'product'>;
|
|
183
|
-
const ticketApi = createMockApi(mockTickets) as ClientApi<SupportTicket, 'ticket'>;
|
|
184
|
-
|
|
185
|
-
const customerCache = await createCache(customerApi, createCoordinate('customer'), registry);
|
|
186
|
-
const orderCache = await createCache(orderApi, createCoordinate('order'), registry);
|
|
187
|
-
const productCache = await createCache(productApi, createCoordinate('product'), registry);
|
|
188
|
-
const ticketCache = await createCache(ticketApi, createCoordinate('ticket'), registry);
|
|
189
|
-
|
|
190
|
-
console.log('ā
Created individual caches for each entity type');
|
|
191
|
-
|
|
192
|
-
// Step 3: Create aggregated caches
|
|
193
|
-
console.log('\nStep 3: Creating aggregated caches');
|
|
194
|
-
console.log('----------------------------------');
|
|
195
|
-
|
|
196
|
-
// Create order aggregator that automatically populates customer data
|
|
197
|
-
const orderAggregator = await createAggregator(orderCache, {
|
|
198
|
-
aggregates: {
|
|
199
|
-
customer: { cache: customerCache, optional: false }, // Required reference
|
|
200
|
-
},
|
|
201
|
-
events: {}
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
// Create support ticket aggregator with both customer and order references
|
|
205
|
-
const ticketAggregator = await createAggregator(ticketCache, {
|
|
206
|
-
aggregates: {
|
|
207
|
-
customer: { cache: customerCache, optional: false }, // Required reference
|
|
208
|
-
order: { cache: orderCache, optional: true }, // Optional reference (not all tickets relate to orders)
|
|
209
|
-
},
|
|
210
|
-
events: {}
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
console.log('ā
Created aggregated caches with relationship mappings (aggregators are now instances)\n');
|
|
214
|
-
|
|
215
|
-
// Step 4: Basic aggregation - Fetch orders with customer data
|
|
216
|
-
console.log('Step 4: Order aggregation with customer data');
|
|
217
|
-
console.log('--------------------------------------------');
|
|
218
|
-
|
|
219
|
-
const [, orders] = await orderAggregator.all();
|
|
220
|
-
console.log(`š Fetched ${orders.length} orders`);
|
|
221
|
-
|
|
222
|
-
for (const order of orders) {
|
|
223
|
-
console.log(`\nš¦ Order ${order.id}:`);
|
|
224
|
-
console.log(` Amount: $${order.total}`);
|
|
225
|
-
console.log(` Status: ${order.status}`);
|
|
226
|
-
console.log(` Items: ${order.items.join(', ')}`);
|
|
227
|
-
|
|
228
|
-
// Populate the order with customer data
|
|
229
|
-
const populatedOrder = await orderAggregator.populate(order);
|
|
230
|
-
if (populatedOrder.aggs?.customer?.item) {
|
|
231
|
-
const customer = populatedOrder.aggs.customer.item;
|
|
232
|
-
console.log(` š¤ Customer: ${customer.name} (${customer.email})`);
|
|
233
|
-
console.log(` š¢ Company: ${customer.company} - ${customer.tier} tier`);
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Step 5: Complex aggregation - Support tickets with multiple references
|
|
238
|
-
console.log('\n\nStep 5: Support ticket aggregation with multiple references');
|
|
239
|
-
console.log('----------------------------------------------------------');
|
|
240
|
-
|
|
241
|
-
const [, tickets] = await ticketAggregator.all();
|
|
242
|
-
console.log(`š« Fetched ${tickets.length} support tickets`);
|
|
243
|
-
|
|
244
|
-
for (const ticket of tickets) {
|
|
245
|
-
console.log(`\nš« Ticket ${ticket.id}:`);
|
|
246
|
-
console.log(` Subject: ${ticket.subject}`);
|
|
247
|
-
console.log(` Priority: ${ticket.priority}`);
|
|
248
|
-
console.log(` Status: ${ticket.status}`);
|
|
249
|
-
console.log(` Description: ${ticket.description}`);
|
|
250
|
-
|
|
251
|
-
// Populate the ticket with all related data
|
|
252
|
-
const populatedTicket = await ticketAggregator.populate(ticket);
|
|
253
|
-
|
|
254
|
-
// Customer data (required reference)
|
|
255
|
-
if (populatedTicket.aggs?.customer?.item) {
|
|
256
|
-
const customer = populatedTicket.aggs.customer.item;
|
|
257
|
-
console.log(` š¤ Customer: ${customer.name} (${customer.email}) - ${customer.tier} tier`);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Order data (optional reference)
|
|
261
|
-
if (populatedTicket.aggs?.order?.item) {
|
|
262
|
-
const order = populatedTicket.aggs.order.item;
|
|
263
|
-
console.log(` š¦ Related Order: ${order.id} - $${order.total} (${order.status})`);
|
|
264
|
-
} else {
|
|
265
|
-
console.log(` š¦ No related order`);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// Step 6: Individual item retrieval with aggregation
|
|
270
|
-
console.log('\n\nStep 6: Individual item retrieval with aggregation');
|
|
271
|
-
console.log('-------------------------------------------------');
|
|
272
|
-
|
|
273
|
-
const [, specificOrder] = await orderAggregator.get(order1.key);
|
|
274
|
-
if (specificOrder) {
|
|
275
|
-
console.log(`š Retrieved specific order: ${specificOrder.id}`);
|
|
276
|
-
|
|
277
|
-
const populatedSpecificOrder = await orderAggregator.populate(specificOrder);
|
|
278
|
-
if (populatedSpecificOrder.aggs?.customer?.item) {
|
|
279
|
-
const customer = populatedSpecificOrder.aggs.customer.item;
|
|
280
|
-
console.log(` Automatically populated customer: ${customer.name}`);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Step 7: Demonstrating cache efficiency with aggregation
|
|
285
|
-
console.log('\n\nStep 7: Cache efficiency demonstration');
|
|
286
|
-
console.log('-------------------------------------');
|
|
287
|
-
|
|
288
|
-
console.log('šÆ First population (will fetch from storage):');
|
|
289
|
-
const startTime1 = Date.now();
|
|
290
|
-
const populated1 = await orderAggregator.populate(order1);
|
|
291
|
-
const time1 = Date.now() - startTime1;
|
|
292
|
-
console.log(` Populated order with customer data in ${time1}ms`);
|
|
293
|
-
|
|
294
|
-
console.log('šÆ Second population (should use cached data):');
|
|
295
|
-
const startTime2 = Date.now();
|
|
296
|
-
const populated2 = await orderAggregator.populate(order1);
|
|
297
|
-
const time2 = Date.now() - startTime2;
|
|
298
|
-
console.log(` Populated same order in ${time2}ms (cached)`);
|
|
299
|
-
|
|
300
|
-
console.log(` š Cache efficiency: ${Math.round(((time1 - time2) / time1) * 100)}% faster on second call`);
|
|
301
|
-
|
|
302
|
-
// Step 8: Aggregate management and statistics
|
|
303
|
-
console.log('\n\nStep 8: Aggregate management and statistics');
|
|
304
|
-
console.log('------------------------------------------');
|
|
305
|
-
|
|
306
|
-
console.log('š Cache Statistics:');
|
|
307
|
-
console.log(` š„ Customers cached: ${mockCustomers.size}`);
|
|
308
|
-
console.log(` š¦ Orders cached: ${mockOrders.size}`);
|
|
309
|
-
console.log(` š¦ Products cached: ${mockProducts.size}`);
|
|
310
|
-
console.log(` š« Tickets cached: ${mockTickets.size}`);
|
|
311
|
-
console.log(` š Order aggregator references: customer (required)`);
|
|
312
|
-
console.log(` š Ticket aggregator references: customer (required), order (optional)`);
|
|
313
|
-
|
|
314
|
-
console.log('\nš Aggregator Example Complete!');
|
|
315
|
-
console.log('===============================\n');
|
|
316
|
-
|
|
317
|
-
console.log('Key concepts demonstrated:');
|
|
318
|
-
console.log('⢠Creating aggregated caches with entity relationships');
|
|
319
|
-
console.log('⢠Automatic population of referenced entities');
|
|
320
|
-
console.log('⢠Required vs optional aggregate references');
|
|
321
|
-
console.log('⢠Cache efficiency through aggregation');
|
|
322
|
-
console.log('⢠Complex business model relationships');
|
|
323
|
-
console.log('⢠Performance benefits of cached aggregates\n');
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
// Run the example if this file is executed directly
|
|
327
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
328
|
-
runAggregatorExample().catch(console.error);
|
|
329
|
-
}
|
|
@@ -1,270 +0,0 @@
|
|
|
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, createCoordinate('user'), registry);
|
|
150
|
-
const taskCache = await createCache(taskApi, createCoordinate('task'), registry);
|
|
151
|
-
console.log('ā
Created cache instances (which are now also instances)');
|
|
152
|
-
console.log('ā
Created cache model instances\n');
|
|
153
|
-
|
|
154
|
-
// Step 2: Create some test data
|
|
155
|
-
console.log('Step 2: Creating test data');
|
|
156
|
-
console.log('-------------------------');
|
|
157
|
-
|
|
158
|
-
const user1 = createTestUser('user-1', 'Alice Johnson', 'alice@example.com', 'admin');
|
|
159
|
-
const user2 = createTestUser('user-2', 'Bob Smith', 'bob@example.com', 'user');
|
|
160
|
-
|
|
161
|
-
const task1 = createTestTask('task-1', 'Setup project repository', 'Initialize Git repo and create basic structure', user1.id, 'completed', 'high');
|
|
162
|
-
const task2 = createTestTask('task-2', 'Implement user authentication', 'Add login and registration functionality', user2.id, 'in-progress', 'medium');
|
|
163
|
-
|
|
164
|
-
console.log('ā
Created test users and tasks\n');
|
|
165
|
-
|
|
166
|
-
// Step 3: Cache operations - Fetch and cache all items
|
|
167
|
-
console.log('Step 3: Cache operations - Fetching all items');
|
|
168
|
-
console.log('----------------------------------------------');
|
|
169
|
-
|
|
170
|
-
const [, allUsers] = await userCache.operations.all();
|
|
171
|
-
console.log(`š Cached ${allUsers.length} users:`, allUsers.map((u: User) => u.name));
|
|
172
|
-
|
|
173
|
-
const [, allTasks] = await taskCache.operations.all();
|
|
174
|
-
console.log(`š Cached ${allTasks.length} tasks:`, allTasks.map((t: Task) => t.title));
|
|
175
|
-
console.log('');
|
|
176
|
-
|
|
177
|
-
// Step 4: Individual item retrieval from cache
|
|
178
|
-
console.log('Step 4: Individual item retrieval');
|
|
179
|
-
console.log('---------------------------------');
|
|
180
|
-
|
|
181
|
-
const [, cachedUser1] = await userCache.operations.get(user1.key);
|
|
182
|
-
console.log(`š¤ Retrieved from cache: ${cachedUser1?.name} (${cachedUser1?.email})`);
|
|
183
|
-
|
|
184
|
-
const [, cachedTask1] = await taskCache.operations.get(task1.key);
|
|
185
|
-
console.log(`š Retrieved from cache: ${cachedTask1?.title} - Status: ${cachedTask1?.status}`);
|
|
186
|
-
console.log('');
|
|
187
|
-
|
|
188
|
-
// Step 5: Cache hit vs miss demonstration
|
|
189
|
-
console.log('Step 5: Cache behavior demonstration');
|
|
190
|
-
console.log('-----------------------------------');
|
|
191
|
-
|
|
192
|
-
// This should hit the cache (no API call)
|
|
193
|
-
console.log('šÆ Second retrieval (should hit cache):');
|
|
194
|
-
const [, cachedUser1Again] = await userCache.operations.retrieve(user1.key);
|
|
195
|
-
console.log(`š¤ Retrieved: ${cachedUser1Again?.name} (cache hit)`);
|
|
196
|
-
|
|
197
|
-
// Create a new user and demonstrate cache miss
|
|
198
|
-
const user3 = createTestUser('user-3', 'Charlie Brown', 'charlie@example.com', 'guest');
|
|
199
|
-
|
|
200
|
-
console.log('šÆ New item retrieval (cache miss, will fetch from API):');
|
|
201
|
-
const [, cachedUser3] = await userCache.operations.get(user3.key);
|
|
202
|
-
console.log(`š¤ Retrieved: ${cachedUser3?.name} (fetched from API and cached)`);
|
|
203
|
-
console.log('');
|
|
204
|
-
|
|
205
|
-
// Step 6: Cache updates
|
|
206
|
-
console.log('Step 6: Cache updates');
|
|
207
|
-
console.log('--------------------');
|
|
208
|
-
|
|
209
|
-
const updatedTask2 = { ...task2, status: 'completed' as const, description: 'Add login and registration functionality - COMPLETED!' };
|
|
210
|
-
mockTaskStorage.set(task2.id, updatedTask2);
|
|
211
|
-
|
|
212
|
-
// Update cache with new version
|
|
213
|
-
await taskCache.operations.set(updatedTask2.key, updatedTask2);
|
|
214
|
-
console.log(`š Updated task in cache: ${updatedTask2.title} - New status: ${updatedTask2.status}`);
|
|
215
|
-
console.log('');
|
|
216
|
-
|
|
217
|
-
// Step 7: Query operations
|
|
218
|
-
console.log('Step 7: Query operations');
|
|
219
|
-
console.log('-----------------------');
|
|
220
|
-
|
|
221
|
-
const [, foundTasks] = await taskCache.operations.find('all');
|
|
222
|
-
console.log(`š Found ${foundTasks.length} tasks through cache query`);
|
|
223
|
-
|
|
224
|
-
const [, oneTask] = await taskCache.operations.one();
|
|
225
|
-
console.log(`š Retrieved one task: ${oneTask?.title}`);
|
|
226
|
-
console.log('');
|
|
227
|
-
|
|
228
|
-
// Step 8: Cache statistics and management
|
|
229
|
-
console.log('Step 8: Cache management');
|
|
230
|
-
console.log('-----------------------');
|
|
231
|
-
|
|
232
|
-
console.log('š Cache Statistics:');
|
|
233
|
-
console.log(` š„ Users in cache: ${allUsers.length}`);
|
|
234
|
-
console.log(` š Tasks in cache: ${allTasks.length}`);
|
|
235
|
-
console.log(` šÆ User cache coordinate: ${userCache.coordinate.kta[0]}`);
|
|
236
|
-
console.log(` šÆ Task cache coordinate: ${taskCache.coordinate.kta[0]}`);
|
|
237
|
-
console.log('');
|
|
238
|
-
|
|
239
|
-
// Step 9: Cleanup demonstration
|
|
240
|
-
console.log('Step 9: Cleanup demonstration');
|
|
241
|
-
console.log('-----------------------------');
|
|
242
|
-
|
|
243
|
-
mockUserStorage.delete('user-3');
|
|
244
|
-
console.log('šļø Removed user from storage');
|
|
245
|
-
|
|
246
|
-
// Cache still has the old data until next fetch
|
|
247
|
-
const [, stillCachedUser3] = await userCache.operations.retrieve(user3.key);
|
|
248
|
-
console.log(`šÆ Cache still contains removed user: ${stillCachedUser3?.name || 'null'}`);
|
|
249
|
-
|
|
250
|
-
// Fresh fetch will update cache
|
|
251
|
-
const [, freshAllUsers] = await userCache.operations.all();
|
|
252
|
-
console.log(`š Fresh fetch shows ${freshAllUsers.length} users (cache updated)`);
|
|
253
|
-
console.log('');
|
|
254
|
-
|
|
255
|
-
console.log('š Basic Cache Example Complete!');
|
|
256
|
-
console.log('================================\n');
|
|
257
|
-
|
|
258
|
-
console.log('Key concepts demonstrated:');
|
|
259
|
-
console.log('⢠Cache creation with registry and instances');
|
|
260
|
-
console.log('⢠Basic cache operations (get, set, all, find, one)');
|
|
261
|
-
console.log('⢠Cache hits vs misses');
|
|
262
|
-
console.log('⢠Cache updates and management');
|
|
263
|
-
console.log('⢠Integration with mock storage APIs');
|
|
264
|
-
console.log('⢠Cache lifecycle and data consistency\n');
|
|
265
|
-
};
|
|
266
|
-
|
|
267
|
-
// Run the example if this file is executed directly
|
|
268
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
269
|
-
runBasicCacheExample().catch(console.error);
|
|
270
|
-
}
|