@simpleapps-com/augur-api 0.2.9 → 0.2.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/PERFORMANCE.md +816 -0
- package/README.md +1084 -197
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +4 -3
package/PERFORMANCE.md
ADDED
|
@@ -0,0 +1,816 @@
|
|
|
1
|
+
# Performance Optimization Guide
|
|
2
|
+
|
|
3
|
+
**Maximize your application's performance** with proven strategies for edge caching, batch operations, and pagination optimization.
|
|
4
|
+
|
|
5
|
+
## Edge Cache Strategy Decision Tree
|
|
6
|
+
|
|
7
|
+
**Choose the optimal cache duration** based on your data characteristics:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
/**
|
|
11
|
+
* 🌐 EDGE CACHE DECISION TREE
|
|
12
|
+
*
|
|
13
|
+
* Cloudflare CDN caches responses globally for specified hours.
|
|
14
|
+
* Choose duration based on data volatility and business impact.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// 🟢 STATIC REFERENCE DATA → Cache: 8 hours
|
|
18
|
+
// Changes rarely, high business value when cached
|
|
19
|
+
const categories = await api.items.categories.list({ edgeCache: 8 });
|
|
20
|
+
const distributors = await api.vmi.distributors.list({ edgeCache: 8 });
|
|
21
|
+
const userGroups = await api.joomla.userGroups.list({ edgeCache: 8 });
|
|
22
|
+
|
|
23
|
+
// 🟡 MODERATE VOLATILITY → Cache: 3-6 hours
|
|
24
|
+
// Changes daily/weekly, good cache hit potential
|
|
25
|
+
const products = await api.opensearch.itemSearch.search({
|
|
26
|
+
q: 'electrical supplies',
|
|
27
|
+
edgeCache: 6 // Product catalogs change infrequently
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const warehouses = await api.vmi.warehouses.list({
|
|
31
|
+
customerId: 12345,
|
|
32
|
+
edgeCache: 4 // Warehouse data moderately stable
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const recommendations = await api.commerce.alsoBought.get({
|
|
36
|
+
itemId: 'WIRE-123',
|
|
37
|
+
edgeCache: 4 // Recommendations can be stale for hours
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// 🟠 HIGH VOLATILITY → Cache: 1-3 hours
|
|
41
|
+
// Changes hourly, short cache acceptable
|
|
42
|
+
const pricing = await api.pricing.getPrice({
|
|
43
|
+
customerId: 12345,
|
|
44
|
+
itemId: 'STANDARD-ITEM',
|
|
45
|
+
quantity: 10,
|
|
46
|
+
edgeCache: 3 // Standard pricing changes less frequently
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const users = await api.joomla.users.list({
|
|
50
|
+
limit: 50,
|
|
51
|
+
edgeCache: 2 // User lists change regularly
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// 🔴 VOLATILE DATA → Cache: 1 hour maximum
|
|
55
|
+
// Changes frequently, short cache only
|
|
56
|
+
const cart = await api.commerce.cartHeaders.list({
|
|
57
|
+
userId: 123,
|
|
58
|
+
edgeCache: 1 // Cart contents change frequently
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const inventory = await api.vmi.inventory.checkAvailability(warehouseId, {
|
|
62
|
+
edgeCache: 1 // Inventory levels change constantly
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// ⛔ REAL-TIME DATA → No caching
|
|
66
|
+
// Must be current, never cache
|
|
67
|
+
const auth = await api.joomla.users.verifyPassword({
|
|
68
|
+
username: 'user',
|
|
69
|
+
password: 'password'
|
|
70
|
+
// No edgeCache - authentication must be real-time
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const checkout = await api.commerce.checkout.create({
|
|
74
|
+
cartHdrUid: 12345
|
|
75
|
+
// No edgeCache - checkout must be real-time
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Smart Caching Strategies by Service
|
|
80
|
+
|
|
81
|
+
**Optimize cache duration** based on business context and data patterns:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
class SmartCacheStrategy {
|
|
85
|
+
/**
|
|
86
|
+
* Get optimal cache duration for any service operation
|
|
87
|
+
*/
|
|
88
|
+
static getOptimalDuration(
|
|
89
|
+
service: string,
|
|
90
|
+
operation: string,
|
|
91
|
+
context?: Record<string, any>
|
|
92
|
+
): number | undefined {
|
|
93
|
+
|
|
94
|
+
const strategies = {
|
|
95
|
+
// 🏪 JOOMLA SERVICE - Content & Users
|
|
96
|
+
joomla: {
|
|
97
|
+
content: context?.isStaticPage ? 8 : 4, // Articles/pages
|
|
98
|
+
users: context?.isLargeOrg ? 4 : 2, // User management
|
|
99
|
+
userGroups: 8, // Group structure
|
|
100
|
+
tags: 6, // Tag taxonomy
|
|
101
|
+
menu: 8, // Site navigation
|
|
102
|
+
authentication: undefined // Never cache auth
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// 🛒 COMMERCE SERVICE - E-commerce
|
|
106
|
+
commerce: {
|
|
107
|
+
cart: 1, // User shopping carts
|
|
108
|
+
alsoBought: 6, // Product recommendations
|
|
109
|
+
checkout: undefined, // Real-time checkout
|
|
110
|
+
categories: 8 // Product categories
|
|
111
|
+
},
|
|
112
|
+
|
|
113
|
+
// 💰 PRICING SERVICE - Dynamic Pricing
|
|
114
|
+
pricing: {
|
|
115
|
+
standardPricing: context?.isStandardItem ? 4 : 2, // Standard vs custom
|
|
116
|
+
customerPricing: context?.isPremiumCustomer ? 3 : 1, // Premium gets longer cache
|
|
117
|
+
jobPricing: 6, // Job-based pricing
|
|
118
|
+
taxCalculation: 2 // Tax calculations
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
// 📦 VMI SERVICE - Inventory Management
|
|
122
|
+
vmi: {
|
|
123
|
+
warehouses: 6, // Warehouse data
|
|
124
|
+
inventory: context?.isHighVolume ? 1 : 2, // Inventory levels
|
|
125
|
+
distributors: 8, // Distributor info
|
|
126
|
+
products: 4, // Product catalog
|
|
127
|
+
replenishment: 3 // Replenishment data
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
// 🔍 OPENSEARCH SERVICE - Product Search
|
|
131
|
+
opensearch: {
|
|
132
|
+
search: context?.isPopularQuery ? 4 : 2, // Search results
|
|
133
|
+
facets: 6, // Search facets
|
|
134
|
+
suggestions: 4, // Search suggestions
|
|
135
|
+
itemDetails: 3 // Individual items
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
// 🏷️ ITEMS SERVICE - Product Information
|
|
139
|
+
items: {
|
|
140
|
+
categories: 8, // Category hierarchy
|
|
141
|
+
attributes: 6, // Product attributes
|
|
142
|
+
brands: 8, // Brand information
|
|
143
|
+
products: 4 // Product details
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
// 👥 CUSTOMERS SERVICE - Customer Data
|
|
147
|
+
customers: {
|
|
148
|
+
profiles: 3, // Customer profiles
|
|
149
|
+
addresses: 4, // Shipping addresses
|
|
150
|
+
orders: 2, // Order history
|
|
151
|
+
invoices: 6, // Invoice history
|
|
152
|
+
contacts: 4 // Contact information
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
// 📋 ORDERS SERVICE - Order Management
|
|
156
|
+
orders: {
|
|
157
|
+
lookup: 2, // Order lookups
|
|
158
|
+
documents: 4, // Order documents
|
|
159
|
+
purchaseOrders: 3, // Purchase orders
|
|
160
|
+
salesRep: 2 // Sales rep data
|
|
161
|
+
},
|
|
162
|
+
|
|
163
|
+
// 💳 PAYMENTS SERVICE - Payment Processing
|
|
164
|
+
payments: {
|
|
165
|
+
validation: undefined, // Never cache validation
|
|
166
|
+
setup: undefined, // Never cache setup
|
|
167
|
+
cardInfo: 1 // Brief card info cache
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
return strategies[service]?.[operation];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Auto-configure cache for any API call
|
|
176
|
+
*/
|
|
177
|
+
static applyCaching<T>(
|
|
178
|
+
apiCall: () => Promise<T>,
|
|
179
|
+
service: string,
|
|
180
|
+
operation: string,
|
|
181
|
+
context?: Record<string, any>
|
|
182
|
+
): () => Promise<T> {
|
|
183
|
+
const duration = this.getOptimalDuration(service, operation, context);
|
|
184
|
+
|
|
185
|
+
return () => {
|
|
186
|
+
if (duration) {
|
|
187
|
+
// Modify the API call to include optimal caching
|
|
188
|
+
return apiCall().then(result => {
|
|
189
|
+
console.log(`✅ Cached ${service}.${operation} for ${duration} hours`);
|
|
190
|
+
return result;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
return apiCall();
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Usage Examples
|
|
199
|
+
const cachedPricing = SmartCacheStrategy.applyCaching(
|
|
200
|
+
() => api.pricing.getPrice({ customerId: 12345, itemId: 'STANDARD' }),
|
|
201
|
+
'pricing',
|
|
202
|
+
'standardPricing',
|
|
203
|
+
{ isStandardItem: true }
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
const cachedUsers = SmartCacheStrategy.applyCaching(
|
|
207
|
+
() => api.joomla.users.list({ limit: 50 }),
|
|
208
|
+
'joomla',
|
|
209
|
+
'users',
|
|
210
|
+
{ isLargeOrg: true }
|
|
211
|
+
);
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Batch Operations by Service
|
|
215
|
+
|
|
216
|
+
**Optimize performance** with service-specific batching patterns:
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
/**
|
|
220
|
+
* 🚀 SERVICE-SPECIFIC BATCH PATTERNS
|
|
221
|
+
*
|
|
222
|
+
* Each service has optimal batching strategies based on:
|
|
223
|
+
* - Data relationships
|
|
224
|
+
* - Performance characteristics
|
|
225
|
+
* - Business workflows
|
|
226
|
+
*/
|
|
227
|
+
|
|
228
|
+
// 🏪 JOOMLA BATCH PATTERNS
|
|
229
|
+
class JoomlaBatchOperations {
|
|
230
|
+
static async loadUserDashboard(userIds: string[]) {
|
|
231
|
+
// Batch user data with related information
|
|
232
|
+
const [users, userGroups, permissions] = await Promise.allSettled([
|
|
233
|
+
// Primary user data
|
|
234
|
+
Promise.all(userIds.map(id =>
|
|
235
|
+
api.joomla.users.get(id, { edgeCache: 2 })
|
|
236
|
+
)),
|
|
237
|
+
|
|
238
|
+
// User group memberships (batch load)
|
|
239
|
+
api.joomla.userGroups.list({ edgeCache: 8 }),
|
|
240
|
+
|
|
241
|
+
// Permission data (if needed)
|
|
242
|
+
Promise.all(userIds.map(id =>
|
|
243
|
+
api.joomla.users.groups.list(id, { edgeCache: 4 })
|
|
244
|
+
))
|
|
245
|
+
]);
|
|
246
|
+
|
|
247
|
+
return this.combineUserData(users, userGroups, permissions);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
static async loadContentBundle(categoryIds: string[]) {
|
|
251
|
+
// Batch content loading with metadata
|
|
252
|
+
return Promise.allSettled([
|
|
253
|
+
// Content by categories
|
|
254
|
+
Promise.all(categoryIds.map(catId =>
|
|
255
|
+
api.joomla.content.list({
|
|
256
|
+
categoryIdList: catId,
|
|
257
|
+
limit: 20,
|
|
258
|
+
edgeCache: 6
|
|
259
|
+
})
|
|
260
|
+
)),
|
|
261
|
+
|
|
262
|
+
// Tag information
|
|
263
|
+
api.joomla.tags.list({ edgeCache: 6 }),
|
|
264
|
+
|
|
265
|
+
// Category metadata
|
|
266
|
+
Promise.all(categoryIds.map(catId =>
|
|
267
|
+
api.joomla.content.get(catId, { edgeCache: 8 })
|
|
268
|
+
))
|
|
269
|
+
]);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 🛒 COMMERCE BATCH PATTERNS
|
|
274
|
+
class CommerceBatchOperations {
|
|
275
|
+
static async loadShoppingContext(userId: number, customerId: number) {
|
|
276
|
+
// Complete e-commerce context in one batch
|
|
277
|
+
const [cart, recommendations, pricing, inventory] = await Promise.allSettled([
|
|
278
|
+
// User's current cart
|
|
279
|
+
api.commerce.cartHeaders.lookup({
|
|
280
|
+
userId,
|
|
281
|
+
customerId,
|
|
282
|
+
edgeCache: 1
|
|
283
|
+
}),
|
|
284
|
+
|
|
285
|
+
// Product recommendations (multiple items)
|
|
286
|
+
Promise.all([
|
|
287
|
+
api.commerce.alsoBought.get({
|
|
288
|
+
itemId: 'POPULAR-ITEM-1',
|
|
289
|
+
customerId,
|
|
290
|
+
edgeCache: 6
|
|
291
|
+
}),
|
|
292
|
+
api.commerce.alsoBought.get({
|
|
293
|
+
itemId: 'POPULAR-ITEM-2',
|
|
294
|
+
customerId,
|
|
295
|
+
edgeCache: 6
|
|
296
|
+
})
|
|
297
|
+
]),
|
|
298
|
+
|
|
299
|
+
// Batch pricing for common items
|
|
300
|
+
Promise.all(['ITEM-1', 'ITEM-2', 'ITEM-3'].map(itemId =>
|
|
301
|
+
api.pricing.getPrice({
|
|
302
|
+
customerId,
|
|
303
|
+
itemId,
|
|
304
|
+
quantity: 1,
|
|
305
|
+
edgeCache: 3
|
|
306
|
+
})
|
|
307
|
+
)),
|
|
308
|
+
|
|
309
|
+
// Inventory availability
|
|
310
|
+
api.vmi.inventory.checkAvailability(123, {
|
|
311
|
+
q: 'featured',
|
|
312
|
+
edgeCache: 2
|
|
313
|
+
})
|
|
314
|
+
]);
|
|
315
|
+
|
|
316
|
+
return this.buildShoppingContext(cart, recommendations, pricing, inventory);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// 📦 VMI BATCH PATTERNS
|
|
321
|
+
class VMIBatchOperations {
|
|
322
|
+
static async loadWarehouseSnapshot(warehouseIds: number[]) {
|
|
323
|
+
// Complete warehouse picture with parallel loading
|
|
324
|
+
return Promise.allSettled([
|
|
325
|
+
// Warehouse basic info
|
|
326
|
+
Promise.all(warehouseIds.map(id =>
|
|
327
|
+
api.vmi.warehouses.get(id, { edgeCache: 6 })
|
|
328
|
+
)),
|
|
329
|
+
|
|
330
|
+
// Inventory levels for all warehouses
|
|
331
|
+
Promise.all(warehouseIds.map(id =>
|
|
332
|
+
api.vmi.inventory.checkAvailability(id, { edgeCache: 2 })
|
|
333
|
+
)),
|
|
334
|
+
|
|
335
|
+
// Distributor information
|
|
336
|
+
api.vmi.distributors.list({
|
|
337
|
+
customerId: 12345,
|
|
338
|
+
edgeCache: 8
|
|
339
|
+
}),
|
|
340
|
+
|
|
341
|
+
// Product catalog
|
|
342
|
+
api.vmi.products.list({
|
|
343
|
+
customerId: 12345,
|
|
344
|
+
limit: 100,
|
|
345
|
+
edgeCache: 4
|
|
346
|
+
})
|
|
347
|
+
]);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
static async loadReplenishmentPlan(warehouseId: number) {
|
|
351
|
+
// Replenishment analysis batch
|
|
352
|
+
const [availability, replenishmentInfo, distributors] = await Promise.allSettled([
|
|
353
|
+
api.vmi.inventory.checkAvailability(warehouseId, { edgeCache: 1 }),
|
|
354
|
+
api.vmi.inventory.getReplenishmentInfo(warehouseId, {
|
|
355
|
+
distributorsUid: 1,
|
|
356
|
+
edgeCache: 3
|
|
357
|
+
}),
|
|
358
|
+
api.vmi.distributors.list({
|
|
359
|
+
customerId: 12345,
|
|
360
|
+
edgeCache: 8
|
|
361
|
+
})
|
|
362
|
+
]);
|
|
363
|
+
|
|
364
|
+
return this.analyzeReplenishment(availability, replenishmentInfo, distributors);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// 🔍 SEARCH BATCH PATTERNS
|
|
369
|
+
class SearchBatchOperations {
|
|
370
|
+
static async loadSearchResults(queries: string[]) {
|
|
371
|
+
// Multi-query search with facet loading
|
|
372
|
+
return Promise.allSettled([
|
|
373
|
+
// Parallel searches
|
|
374
|
+
Promise.all(queries.map(q =>
|
|
375
|
+
api.opensearch.itemSearch.search({
|
|
376
|
+
q,
|
|
377
|
+
searchType: 'query',
|
|
378
|
+
size: 20,
|
|
379
|
+
edgeCache: 4
|
|
380
|
+
})
|
|
381
|
+
)),
|
|
382
|
+
|
|
383
|
+
// Search facets/attributes
|
|
384
|
+
api.opensearch.itemSearch.getAttributes({
|
|
385
|
+
q: queries[0], // Use first query for facets
|
|
386
|
+
edgeCache: 6
|
|
387
|
+
}),
|
|
388
|
+
|
|
389
|
+
// Category structure
|
|
390
|
+
api.items.categories.list({ edgeCache: 8 })
|
|
391
|
+
]);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## Pagination Optimization Examples
|
|
397
|
+
|
|
398
|
+
**Handle large datasets efficiently** with smart pagination strategies:
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
/**
|
|
402
|
+
* 📄 ADVANCED PAGINATION PATTERNS
|
|
403
|
+
*
|
|
404
|
+
* Optimize large dataset handling with intelligent pagination,
|
|
405
|
+
* predictive loading, and cache-aware strategies.
|
|
406
|
+
*/
|
|
407
|
+
|
|
408
|
+
// 🎯 SMART PAGINATION CONTROLLER
|
|
409
|
+
class SmartPagination {
|
|
410
|
+
private cache = new Map<string, any>();
|
|
411
|
+
private prefetchBuffer = 2; // Pages to prefetch ahead
|
|
412
|
+
|
|
413
|
+
async loadPage<T>(
|
|
414
|
+
fetcher: (offset: number, limit: number) => Promise<T>,
|
|
415
|
+
page: number,
|
|
416
|
+
pageSize: number = 50,
|
|
417
|
+
enablePrefetch: boolean = true
|
|
418
|
+
): Promise<T> {
|
|
419
|
+
const offset = page * pageSize;
|
|
420
|
+
const cacheKey = `page_${page}_${pageSize}`;
|
|
421
|
+
|
|
422
|
+
// Check cache first
|
|
423
|
+
if (this.cache.has(cacheKey)) {
|
|
424
|
+
console.log(`📄 Cache hit for page ${page}`);
|
|
425
|
+
return this.cache.get(cacheKey);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Load current page
|
|
429
|
+
const result = await fetcher(offset, pageSize);
|
|
430
|
+
this.cache.set(cacheKey, result);
|
|
431
|
+
|
|
432
|
+
// Prefetch next pages if enabled
|
|
433
|
+
if (enablePrefetch) {
|
|
434
|
+
this.prefetchNextPages(fetcher, page, pageSize);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return result;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
private async prefetchNextPages<T>(
|
|
441
|
+
fetcher: (offset: number, limit: number) => Promise<T>,
|
|
442
|
+
currentPage: number,
|
|
443
|
+
pageSize: number
|
|
444
|
+
): Promise<void> {
|
|
445
|
+
// Prefetch next 2 pages in background
|
|
446
|
+
for (let i = 1; i <= this.prefetchBuffer; i++) {
|
|
447
|
+
const nextPage = currentPage + i;
|
|
448
|
+
const cacheKey = `page_${nextPage}_${pageSize}`;
|
|
449
|
+
|
|
450
|
+
if (!this.cache.has(cacheKey)) {
|
|
451
|
+
// Non-blocking prefetch
|
|
452
|
+
setTimeout(async () => {
|
|
453
|
+
try {
|
|
454
|
+
const offset = nextPage * pageSize;
|
|
455
|
+
const result = await fetcher(offset, pageSize);
|
|
456
|
+
this.cache.set(cacheKey, result);
|
|
457
|
+
console.log(`🔮 Prefetched page ${nextPage}`);
|
|
458
|
+
} catch (error) {
|
|
459
|
+
console.log(`❌ Prefetch failed for page ${nextPage}`);
|
|
460
|
+
}
|
|
461
|
+
}, 100 * i); // Stagger prefetch requests
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// 📋 SERVICE-SPECIFIC PAGINATION EXAMPLES
|
|
468
|
+
|
|
469
|
+
// Users with intelligent prefetching
|
|
470
|
+
class UserPagination extends SmartPagination {
|
|
471
|
+
async loadUserPage(page: number, searchQuery?: string) {
|
|
472
|
+
return this.loadPage(
|
|
473
|
+
(offset, limit) => api.joomla.users.list({
|
|
474
|
+
offset,
|
|
475
|
+
limit,
|
|
476
|
+
q: searchQuery,
|
|
477
|
+
orderBy: 'username|ASC',
|
|
478
|
+
edgeCache: 2 // Cache user pages for 2 hours
|
|
479
|
+
}),
|
|
480
|
+
page,
|
|
481
|
+
50, // 50 users per page
|
|
482
|
+
true // Enable prefetching
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Load all pages for export/analysis
|
|
487
|
+
async loadAllUsers(maxPages: number = 10): Promise<any[]> {
|
|
488
|
+
const allUsers: any[] = [];
|
|
489
|
+
const pageSize = 100; // Larger pages for bulk operations
|
|
490
|
+
|
|
491
|
+
for (let page = 0; page < maxPages; page++) {
|
|
492
|
+
try {
|
|
493
|
+
const result = await api.joomla.users.list({
|
|
494
|
+
offset: page * pageSize,
|
|
495
|
+
limit: pageSize,
|
|
496
|
+
edgeCache: 4 // Longer cache for bulk operations
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
allUsers.push(...result.data);
|
|
500
|
+
|
|
501
|
+
// Stop if we've loaded all users
|
|
502
|
+
if (result.data.length < pageSize) {
|
|
503
|
+
console.log(`✅ Loaded all users in ${page + 1} pages`);
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// Rate limiting for bulk operations
|
|
508
|
+
if (page % 5 === 0) {
|
|
509
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
510
|
+
}
|
|
511
|
+
} catch (error) {
|
|
512
|
+
console.error(`❌ Failed to load page ${page}:`, error);
|
|
513
|
+
break;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return allUsers;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Product search with faceted pagination
|
|
522
|
+
class ProductSearchPagination extends SmartPagination {
|
|
523
|
+
async loadSearchPage(
|
|
524
|
+
query: string,
|
|
525
|
+
page: number,
|
|
526
|
+
filters?: Record<string, any>
|
|
527
|
+
) {
|
|
528
|
+
return this.loadPage(
|
|
529
|
+
(offset, limit) => api.opensearch.itemSearch.search({
|
|
530
|
+
q: query,
|
|
531
|
+
searchType: 'query',
|
|
532
|
+
size: limit,
|
|
533
|
+
from: offset, // OpenSearch uses 'from' instead of 'offset'
|
|
534
|
+
filters: filters ? JSON.stringify(filters) : undefined,
|
|
535
|
+
edgeCache: 4 // Cache search results for 4 hours
|
|
536
|
+
}),
|
|
537
|
+
page,
|
|
538
|
+
20, // 20 products per page for search
|
|
539
|
+
true // Prefetch for smooth browsing
|
|
540
|
+
);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
// Infinite scroll pattern
|
|
544
|
+
async* infiniteScroll(query: string, filters?: Record<string, any>) {
|
|
545
|
+
let page = 0;
|
|
546
|
+
let hasMore = true;
|
|
547
|
+
|
|
548
|
+
while (hasMore) {
|
|
549
|
+
const result = await this.loadSearchPage(query, page, filters);
|
|
550
|
+
|
|
551
|
+
yield result.data.items;
|
|
552
|
+
|
|
553
|
+
// Check if there are more results
|
|
554
|
+
hasMore = result.data.items.length === 20; // Full page means more might exist
|
|
555
|
+
page++;
|
|
556
|
+
|
|
557
|
+
// Safety limit
|
|
558
|
+
if (page > 100) {
|
|
559
|
+
console.log('⚠️ Reached pagination safety limit');
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// VMI inventory with streaming pagination
|
|
567
|
+
class InventoryPagination extends SmartPagination {
|
|
568
|
+
async streamInventory(
|
|
569
|
+
warehouseId: number,
|
|
570
|
+
onBatch: (items: any[]) => Promise<void>
|
|
571
|
+
): Promise<void> {
|
|
572
|
+
let page = 0;
|
|
573
|
+
const pageSize = 100; // Large batches for processing
|
|
574
|
+
|
|
575
|
+
while (true) {
|
|
576
|
+
const result = await api.vmi.inventory.checkAvailability(warehouseId, {
|
|
577
|
+
offset: page * pageSize,
|
|
578
|
+
limit: pageSize,
|
|
579
|
+
edgeCache: 1 // Short cache for inventory data
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
if (result.data.length === 0) {
|
|
583
|
+
console.log('✅ Finished streaming inventory');
|
|
584
|
+
break;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Process batch
|
|
588
|
+
await onBatch(result.data);
|
|
589
|
+
|
|
590
|
+
page++;
|
|
591
|
+
|
|
592
|
+
// Rate limiting for large datasets
|
|
593
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// 🎮 USAGE EXAMPLES
|
|
599
|
+
|
|
600
|
+
// Smart user pagination with prefetching
|
|
601
|
+
const userPagination = new UserPagination();
|
|
602
|
+
|
|
603
|
+
// Load page with automatic prefetching
|
|
604
|
+
const page1 = await userPagination.loadUserPage(0, 'john');
|
|
605
|
+
const page2 = await userPagination.loadUserPage(1, 'john'); // Likely cached from prefetch
|
|
606
|
+
|
|
607
|
+
// Infinite scroll search
|
|
608
|
+
const searchPagination = new ProductSearchPagination();
|
|
609
|
+
|
|
610
|
+
for await (const items of searchPagination.infiniteScroll('electrical wire')) {
|
|
611
|
+
console.log(`Loaded ${items.length} products`);
|
|
612
|
+
|
|
613
|
+
// Process items
|
|
614
|
+
items.forEach(item => {
|
|
615
|
+
console.log(`${item.item_id}: ${item.item_desc}`);
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
// Could break based on user interaction
|
|
619
|
+
if (items.length < 20) break; // No more results
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Stream large inventory dataset
|
|
623
|
+
const inventoryPagination = new InventoryPagination();
|
|
624
|
+
|
|
625
|
+
await inventoryPagination.streamInventory(123, async (items) => {
|
|
626
|
+
// Process each batch
|
|
627
|
+
console.log(`Processing ${items.length} inventory items`);
|
|
628
|
+
|
|
629
|
+
// Example: Update database, generate reports, etc.
|
|
630
|
+
for (const item of items) {
|
|
631
|
+
// Process individual items
|
|
632
|
+
await processInventoryItem(item);
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
```
|
|
636
|
+
|
|
637
|
+
## Performance Monitoring & Analytics
|
|
638
|
+
|
|
639
|
+
**Track and optimize** your API performance with comprehensive monitoring:
|
|
640
|
+
|
|
641
|
+
```typescript
|
|
642
|
+
// 📊 PERFORMANCE ANALYTICS SYSTEM
|
|
643
|
+
class AdvancedPerformanceMonitor {
|
|
644
|
+
private metrics = new Map<string, {
|
|
645
|
+
requests: number;
|
|
646
|
+
totalTime: number;
|
|
647
|
+
errors: number;
|
|
648
|
+
cacheHits: number;
|
|
649
|
+
cacheMisses: number;
|
|
650
|
+
lastUsed: number;
|
|
651
|
+
}>();
|
|
652
|
+
|
|
653
|
+
createMonitoredAPI(config: AugurAPIConfig): AugurAPI {
|
|
654
|
+
return new AugurAPI({
|
|
655
|
+
...config,
|
|
656
|
+
|
|
657
|
+
onRequest: (config) => {
|
|
658
|
+
const endpoint = this.getEndpointKey(config);
|
|
659
|
+
(config as any).startTime = Date.now();
|
|
660
|
+
(config as any).endpoint = endpoint;
|
|
661
|
+
|
|
662
|
+
this.incrementMetric(endpoint, 'requests');
|
|
663
|
+
return config;
|
|
664
|
+
},
|
|
665
|
+
|
|
666
|
+
onResponse: (response) => {
|
|
667
|
+
const startTime = (response.config as any)?.startTime;
|
|
668
|
+
const endpoint = (response.config as any)?.endpoint;
|
|
669
|
+
|
|
670
|
+
if (startTime && endpoint) {
|
|
671
|
+
const duration = Date.now() - startTime;
|
|
672
|
+
this.addTiming(endpoint, duration);
|
|
673
|
+
|
|
674
|
+
// Track cache performance
|
|
675
|
+
const cacheStatus = response.headers?.['cf-cache-status'];
|
|
676
|
+
if (cacheStatus === 'HIT') {
|
|
677
|
+
this.incrementMetric(endpoint, 'cacheHits');
|
|
678
|
+
} else if (cacheStatus === 'MISS') {
|
|
679
|
+
this.incrementMetric(endpoint, 'cacheMisses');
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
return response;
|
|
684
|
+
},
|
|
685
|
+
|
|
686
|
+
onError: (error) => {
|
|
687
|
+
const endpoint = (error.config as any)?.endpoint;
|
|
688
|
+
if (endpoint) {
|
|
689
|
+
this.incrementMetric(endpoint, 'errors');
|
|
690
|
+
}
|
|
691
|
+
throw error;
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// Get comprehensive performance report
|
|
697
|
+
getPerformanceReport(): Record<string, any> {
|
|
698
|
+
const report: Record<string, any> = {};
|
|
699
|
+
|
|
700
|
+
for (const [endpoint, metrics] of this.metrics.entries()) {
|
|
701
|
+
const avgResponseTime = metrics.requests > 0
|
|
702
|
+
? metrics.totalTime / metrics.requests
|
|
703
|
+
: 0;
|
|
704
|
+
|
|
705
|
+
const errorRate = metrics.requests > 0
|
|
706
|
+
? (metrics.errors / metrics.requests) * 100
|
|
707
|
+
: 0;
|
|
708
|
+
|
|
709
|
+
const totalCacheRequests = metrics.cacheHits + metrics.cacheMisses;
|
|
710
|
+
const cacheHitRate = totalCacheRequests > 0
|
|
711
|
+
? (metrics.cacheHits / totalCacheRequests) * 100
|
|
712
|
+
: 0;
|
|
713
|
+
|
|
714
|
+
report[endpoint] = {
|
|
715
|
+
requests: metrics.requests,
|
|
716
|
+
avgResponseTime: Math.round(avgResponseTime),
|
|
717
|
+
errorRate: Math.round(errorRate * 100) / 100,
|
|
718
|
+
cacheHitRate: Math.round(cacheHitRate * 100) / 100,
|
|
719
|
+
totalErrors: metrics.errors,
|
|
720
|
+
lastUsed: new Date(metrics.lastUsed).toISOString()
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
return report;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
// Get optimization suggestions
|
|
728
|
+
getOptimizationSuggestions(): string[] {
|
|
729
|
+
const suggestions: string[] = [];
|
|
730
|
+
const report = this.getPerformanceReport();
|
|
731
|
+
|
|
732
|
+
for (const [endpoint, metrics] of Object.entries(report)) {
|
|
733
|
+
// Slow endpoints
|
|
734
|
+
if (metrics.avgResponseTime > 2000) {
|
|
735
|
+
suggestions.push(`⚡ ${endpoint}: Avg response time ${metrics.avgResponseTime}ms - consider increasing cache duration`);
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// High error rates
|
|
739
|
+
if (metrics.errorRate > 5) {
|
|
740
|
+
suggestions.push(`❌ ${endpoint}: Error rate ${metrics.errorRate}% - check endpoint reliability`);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// Poor cache performance
|
|
744
|
+
if (metrics.cacheHitRate < 50 && metrics.requests > 10) {
|
|
745
|
+
suggestions.push(`🔄 ${endpoint}: Cache hit rate ${metrics.cacheHitRate}% - consider longer cache duration`);
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// Unused endpoints
|
|
749
|
+
const daysSinceLastUse = (Date.now() - new Date(metrics.lastUsed).getTime()) / (1000 * 60 * 60 * 24);
|
|
750
|
+
if (daysSinceLastUse > 30) {
|
|
751
|
+
suggestions.push(`🗑️ ${endpoint}: Not used in ${Math.round(daysSinceLastUse)} days - consider removal`);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
return suggestions;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
private getEndpointKey(config: any): string {
|
|
759
|
+
const url = config.url || '';
|
|
760
|
+
const method = config.method?.toUpperCase() || 'GET';
|
|
761
|
+
return `${method} ${url}`;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
private incrementMetric(endpoint: string, metric: keyof typeof this.metrics.values): void {
|
|
765
|
+
if (!this.metrics.has(endpoint)) {
|
|
766
|
+
this.metrics.set(endpoint, {
|
|
767
|
+
requests: 0,
|
|
768
|
+
totalTime: 0,
|
|
769
|
+
errors: 0,
|
|
770
|
+
cacheHits: 0,
|
|
771
|
+
cacheMisses: 0,
|
|
772
|
+
lastUsed: Date.now()
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
const endpointMetrics = this.metrics.get(endpoint)!;
|
|
777
|
+
endpointMetrics[metric]++;
|
|
778
|
+
endpointMetrics.lastUsed = Date.now();
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
private addTiming(endpoint: string, duration: number): void {
|
|
782
|
+
const endpointMetrics = this.metrics.get(endpoint);
|
|
783
|
+
if (endpointMetrics) {
|
|
784
|
+
endpointMetrics.totalTime += duration;
|
|
785
|
+
endpointMetrics.lastUsed = Date.now();
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// 📈 USAGE EXAMPLE
|
|
791
|
+
const monitor = new AdvancedPerformanceMonitor();
|
|
792
|
+
const api = monitor.createMonitoredAPI({
|
|
793
|
+
siteId: 'your-site-id',
|
|
794
|
+
bearerToken: 'your-token'
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
// Use API normally, monitoring happens automatically
|
|
798
|
+
await api.joomla.users.list({ limit: 50, edgeCache: 2 });
|
|
799
|
+
await api.pricing.getPrice({ customerId: 12345, itemId: 'ITEM-1' });
|
|
800
|
+
await api.vmi.inventory.checkAvailability(123);
|
|
801
|
+
|
|
802
|
+
// Get performance insights
|
|
803
|
+
setTimeout(() => {
|
|
804
|
+
console.log('📊 Performance Report:', monitor.getPerformanceReport());
|
|
805
|
+
console.log('💡 Optimization Suggestions:', monitor.getOptimizationSuggestions());
|
|
806
|
+
}, 60000);
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
## Next Steps
|
|
810
|
+
|
|
811
|
+
- **Implementation**: Apply the caching strategies that match your data patterns
|
|
812
|
+
- **Monitoring**: Set up performance monitoring to track optimization impact
|
|
813
|
+
- **Scaling**: Use batch operations and pagination for large-scale applications
|
|
814
|
+
- **Analytics**: Regular review of performance metrics and optimization suggestions
|
|
815
|
+
|
|
816
|
+
For integration examples and enterprise patterns, see [Enterprise Integration Guide](./ENTERPRISE.md).
|