schemantic 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/LICENSE +21 -0
  3. package/README.md +827 -0
  4. package/dist/__tests__/setup.d.ts +6 -0
  5. package/dist/__tests__/setup.d.ts.map +1 -0
  6. package/dist/__tests__/setup.js +30 -0
  7. package/dist/__tests__/setup.js.map +1 -0
  8. package/dist/__tests__/test-config.d.ts +36 -0
  9. package/dist/__tests__/test-config.d.ts.map +1 -0
  10. package/dist/__tests__/test-config.js +563 -0
  11. package/dist/__tests__/test-config.js.map +1 -0
  12. package/dist/cli/index.d.ts +118 -0
  13. package/dist/cli/index.d.ts.map +1 -0
  14. package/dist/cli/index.js +657 -0
  15. package/dist/cli/index.js.map +1 -0
  16. package/dist/core/index.d.ts +5 -0
  17. package/dist/core/index.d.ts.map +1 -0
  18. package/dist/core/index.js +21 -0
  19. package/dist/core/index.js.map +1 -0
  20. package/dist/core/schemantic.d.ts +95 -0
  21. package/dist/core/schemantic.d.ts.map +1 -0
  22. package/dist/core/schemantic.js +543 -0
  23. package/dist/core/schemantic.js.map +1 -0
  24. package/dist/generators/api-client-generator.d.ts +145 -0
  25. package/dist/generators/api-client-generator.d.ts.map +1 -0
  26. package/dist/generators/api-client-generator.js +856 -0
  27. package/dist/generators/api-client-generator.js.map +1 -0
  28. package/dist/generators/base.d.ts +117 -0
  29. package/dist/generators/base.d.ts.map +1 -0
  30. package/dist/generators/base.js +232 -0
  31. package/dist/generators/base.js.map +1 -0
  32. package/dist/generators/enum-generator.d.ts +52 -0
  33. package/dist/generators/enum-generator.d.ts.map +1 -0
  34. package/dist/generators/enum-generator.js +178 -0
  35. package/dist/generators/enum-generator.js.map +1 -0
  36. package/dist/generators/factory.d.ts +59 -0
  37. package/dist/generators/factory.d.ts.map +1 -0
  38. package/dist/generators/factory.js +127 -0
  39. package/dist/generators/factory.js.map +1 -0
  40. package/dist/generators/hook-generator.d.ts +36 -0
  41. package/dist/generators/hook-generator.d.ts.map +1 -0
  42. package/dist/generators/hook-generator.js +495 -0
  43. package/dist/generators/hook-generator.js.map +1 -0
  44. package/dist/generators/index.d.ts +10 -0
  45. package/dist/generators/index.d.ts.map +1 -0
  46. package/dist/generators/index.js +26 -0
  47. package/dist/generators/index.js.map +1 -0
  48. package/dist/generators/object-generator.d.ts +48 -0
  49. package/dist/generators/object-generator.d.ts.map +1 -0
  50. package/dist/generators/object-generator.js +280 -0
  51. package/dist/generators/object-generator.js.map +1 -0
  52. package/dist/generators/primitive-generator.d.ts +40 -0
  53. package/dist/generators/primitive-generator.d.ts.map +1 -0
  54. package/dist/generators/primitive-generator.js +150 -0
  55. package/dist/generators/primitive-generator.js.map +1 -0
  56. package/dist/index.d.ts +17 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +42 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/parsers/base.d.ts +101 -0
  61. package/dist/parsers/base.d.ts.map +1 -0
  62. package/dist/parsers/base.js +77 -0
  63. package/dist/parsers/base.js.map +1 -0
  64. package/dist/parsers/factory.d.ts +64 -0
  65. package/dist/parsers/factory.d.ts.map +1 -0
  66. package/dist/parsers/factory.js +116 -0
  67. package/dist/parsers/factory.js.map +1 -0
  68. package/dist/parsers/index.d.ts +7 -0
  69. package/dist/parsers/index.d.ts.map +1 -0
  70. package/dist/parsers/index.js +23 -0
  71. package/dist/parsers/index.js.map +1 -0
  72. package/dist/parsers/openapi.d.ts +52 -0
  73. package/dist/parsers/openapi.d.ts.map +1 -0
  74. package/dist/parsers/openapi.js +396 -0
  75. package/dist/parsers/openapi.js.map +1 -0
  76. package/dist/plugins/branded-types.d.ts +28 -0
  77. package/dist/plugins/branded-types.d.ts.map +1 -0
  78. package/dist/plugins/branded-types.js +568 -0
  79. package/dist/plugins/branded-types.js.map +1 -0
  80. package/dist/plugins/builtin.d.ts +38 -0
  81. package/dist/plugins/builtin.d.ts.map +1 -0
  82. package/dist/plugins/builtin.js +283 -0
  83. package/dist/plugins/builtin.js.map +1 -0
  84. package/dist/plugins/index.d.ts +7 -0
  85. package/dist/plugins/index.d.ts.map +1 -0
  86. package/dist/plugins/index.js +23 -0
  87. package/dist/plugins/index.js.map +1 -0
  88. package/dist/plugins/loader.d.ts +92 -0
  89. package/dist/plugins/loader.d.ts.map +1 -0
  90. package/dist/plugins/loader.js +245 -0
  91. package/dist/plugins/loader.js.map +1 -0
  92. package/dist/plugins/manager.d.ts +99 -0
  93. package/dist/plugins/manager.d.ts.map +1 -0
  94. package/dist/plugins/manager.js +233 -0
  95. package/dist/plugins/manager.js.map +1 -0
  96. package/dist/plugins/performance-monitoring.d.ts +98 -0
  97. package/dist/plugins/performance-monitoring.d.ts.map +1 -0
  98. package/dist/plugins/performance-monitoring.js +596 -0
  99. package/dist/plugins/performance-monitoring.js.map +1 -0
  100. package/dist/plugins/request-deduplication.d.ts +27 -0
  101. package/dist/plugins/request-deduplication.d.ts.map +1 -0
  102. package/dist/plugins/request-deduplication.js +650 -0
  103. package/dist/plugins/request-deduplication.js.map +1 -0
  104. package/dist/plugins/zod-validation.d.ts +63 -0
  105. package/dist/plugins/zod-validation.d.ts.map +1 -0
  106. package/dist/plugins/zod-validation.js +743 -0
  107. package/dist/plugins/zod-validation.js.map +1 -0
  108. package/dist/types/core.d.ts +203 -0
  109. package/dist/types/core.d.ts.map +1 -0
  110. package/dist/types/core.js +24 -0
  111. package/dist/types/core.js.map +1 -0
  112. package/dist/types/index.d.ts +7 -0
  113. package/dist/types/index.d.ts.map +1 -0
  114. package/dist/types/index.js +23 -0
  115. package/dist/types/index.js.map +1 -0
  116. package/dist/types/openapi.d.ts +250 -0
  117. package/dist/types/openapi.d.ts.map +1 -0
  118. package/dist/types/openapi.js +8 -0
  119. package/dist/types/openapi.js.map +1 -0
  120. package/dist/types/schema.d.ts +68 -0
  121. package/dist/types/schema.d.ts.map +1 -0
  122. package/dist/types/schema.js +22 -0
  123. package/dist/types/schema.js.map +1 -0
  124. package/docs/ADVANCED_PLUGINS_IMPLEMENTATION.md +355 -0
  125. package/docs/ADVANCED_PLUGIN_DEVELOPMENT.md +309 -0
  126. package/docs/API.md +600 -0
  127. package/docs/CONFIGURATION.md +722 -0
  128. package/docs/FASTAPI_TUTORIAL.md +1497 -0
  129. package/docs/PLUGINS.md +683 -0
  130. package/docs/PLUGIN_DEVELOPMENT.md +720 -0
  131. package/package.json +90 -0
@@ -0,0 +1,650 @@
1
+ "use strict";
2
+ /**
3
+ * Advanced Request Deduplication Plugin
4
+ *
5
+ * Implements intelligent request deduplication at the client level with
6
+ * sophisticated caching strategies, memory management, and performance optimization.
7
+ *
8
+ * Key Features:
9
+ * - Request fingerprinting with content-based hashing
10
+ * - Intelligent cache invalidation strategies
11
+ * - Memory-efficient pending request management
12
+ * - Configurable TTL and size limits
13
+ * - Request coalescing for identical concurrent requests
14
+ * - Stale-while-revalidate patterns
15
+ *
16
+ * Architecture:
17
+ * - Non-blocking request deduplication
18
+ * - LRU cache with TTL support
19
+ * - WeakMap-based cleanup for memory efficiency
20
+ * - Configurable cache policies per endpoint
21
+ */
22
+ Object.defineProperty(exports, "__esModule", { value: true });
23
+ exports.requestDeduplicationPlugin = void 0;
24
+ /**
25
+ * Advanced Request Deduplication Plugin Implementation
26
+ */
27
+ exports.requestDeduplicationPlugin = {
28
+ name: "request-deduplication",
29
+ version: "2.0.0",
30
+ description: "Advanced request deduplication with intelligent caching and memory management",
31
+ /**
32
+ * Enhance API client with request deduplication capabilities
33
+ */
34
+ afterClientGeneration: async (generatedClient, context) => {
35
+ const options = getPluginOptions(context);
36
+ if (!options.enabled) {
37
+ return;
38
+ }
39
+ try {
40
+ // Add deduplication middleware to client
41
+ const deduplicationCode = generateDeduplicationCode(generatedClient, options);
42
+ generatedClient.content += deduplicationCode;
43
+ // Update dependencies
44
+ generatedClient.dependencies.push("crypto-js"); // For hashing
45
+ // Add deduplication exports
46
+ generatedClient.exports.push("RequestDeduplicator", "CacheManager", "RequestCoalescer", "clearRequestCache", "getCacheStats");
47
+ }
48
+ catch (error) {
49
+ console.warn("Request deduplication plugin afterClientGeneration failed:", error);
50
+ }
51
+ },
52
+ };
53
+ /**
54
+ * Get plugin options from generation context
55
+ */
56
+ function getPluginOptions(context) {
57
+ const pluginConfig = context?.config?.plugins?.find((p) => p.name === "request-deduplication");
58
+ return {
59
+ enabled: true,
60
+ defaultTTL: 5 * 60 * 1000, // 5 minutes
61
+ maxCacheSize: 1000,
62
+ maxPendingRequests: 100,
63
+ enableStaleWhileRevalidate: true,
64
+ enableCoalescing: true,
65
+ enableDebugLogging: false,
66
+ endpointPolicies: {},
67
+ ...(pluginConfig?.options || {}),
68
+ };
69
+ }
70
+ /**
71
+ * Generate request deduplication code for API client
72
+ */
73
+ function generateDeduplicationCode(_generatedClient, options) {
74
+ return `
75
+
76
+ /**
77
+ * Advanced Request Deduplication System
78
+ */
79
+
80
+ /**
81
+ * LRU Cache implementation with TTL support
82
+ */
83
+ class LRUCacheWithTTL<K, V> {
84
+ private readonly maxSize: number;
85
+ private readonly cache = new Map<K, { value: V; timestamp: number; ttl: number }>();
86
+ private readonly accessOrder = new Map<K, number>();
87
+ private accessCounter = 0;
88
+
89
+ constructor(maxSize: number = ${options.maxCacheSize || 1000}) {
90
+ this.maxSize = maxSize;
91
+ }
92
+
93
+ set(key: K, value: V, ttl: number = ${options.defaultTTL || 5 * 60 * 1000}): void {
94
+ const now = Date.now();
95
+
96
+ // Remove expired entries before adding new one
97
+ this.cleanup();
98
+
99
+ // If at capacity, remove LRU item
100
+ if (this.cache.size >= this.maxSize && !this.cache.has(key)) {
101
+ this.evictLRU();
102
+ }
103
+
104
+ this.cache.set(key, {
105
+ value,
106
+ timestamp: now,
107
+ ttl
108
+ });
109
+
110
+ this.accessOrder.set(key, ++this.accessCounter);
111
+ }
112
+
113
+ get(key: K): V | undefined {
114
+ const entry = this.cache.get(key);
115
+ if (!entry) {
116
+ return undefined;
117
+ }
118
+
119
+ const now = Date.now();
120
+ if (now - entry.timestamp > entry.ttl) {
121
+ // Entry is expired
122
+ this.cache.delete(key);
123
+ this.accessOrder.delete(key);
124
+ return undefined;
125
+ }
126
+
127
+ // Update access order
128
+ this.accessOrder.set(key, ++this.accessCounter);
129
+ return entry.value;
130
+ }
131
+
132
+ has(key: K): boolean {
133
+ return this.get(key) !== undefined;
134
+ }
135
+
136
+ delete(key: K): boolean {
137
+ const result = this.cache.delete(key);
138
+ this.accessOrder.delete(key);
139
+ return result;
140
+ }
141
+
142
+ clear(): void {
143
+ this.cache.clear();
144
+ this.accessOrder.clear();
145
+ this.accessCounter = 0;
146
+ }
147
+
148
+ size(): number {
149
+ this.cleanup();
150
+ return this.cache.size;
151
+ }
152
+
153
+ private cleanup(): void {
154
+ const now = Date.now();
155
+ const expiredKeys: K[] = [];
156
+
157
+ for (const [key, entry] of this.cache) {
158
+ if (now - entry.timestamp > entry.ttl) {
159
+ expiredKeys.push(key);
160
+ }
161
+ }
162
+
163
+ for (const key of expiredKeys) {
164
+ this.cache.delete(key);
165
+ this.accessOrder.delete(key);
166
+ }
167
+ }
168
+
169
+ private evictLRU(): void {
170
+ let oldestKey: K | undefined;
171
+ let oldestAccess = Infinity;
172
+
173
+ for (const [key, accessTime] of this.accessOrder) {
174
+ if (accessTime < oldestAccess) {
175
+ oldestAccess = accessTime;
176
+ oldestKey = key;
177
+ }
178
+ }
179
+
180
+ if (oldestKey !== undefined) {
181
+ this.cache.delete(oldestKey);
182
+ this.accessOrder.delete(oldestKey);
183
+ }
184
+ }
185
+
186
+ getStats(): { size: number; hitRate: number; evictions: number } {
187
+ return {
188
+ size: this.cache.size,
189
+ hitRate: 0, // Would need to track hits/misses for this
190
+ evictions: 0 // Would need to track evictions for this
191
+ };
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Request fingerprint generator
197
+ */
198
+ class RequestFingerprintGenerator {
199
+ /**
200
+ * Generate a unique fingerprint for a request
201
+ */
202
+ static generate(
203
+ method: string,
204
+ url: string,
205
+ headers: Record<string, string> = {},
206
+ body?: unknown,
207
+ params?: Record<string, unknown>
208
+ ): string {
209
+ const fingerprint = {
210
+ method: method.toUpperCase(),
211
+ url: this.normalizeURL(url),
212
+ headers: this.normalizeHeaders(headers),
213
+ body: body ? JSON.stringify(body) : undefined,
214
+ params: params ? JSON.stringify(params) : undefined
215
+ };
216
+
217
+ // Simple hash generation (in production, use crypto-js or similar)
218
+ return this.simpleHash(JSON.stringify(fingerprint));
219
+ }
220
+
221
+ private static normalizeURL(url: string): string {
222
+ try {
223
+ const urlObj = new URL(url);
224
+ // Sort query parameters for consistent fingerprinting
225
+ const params = new URLSearchParams(urlObj.search);
226
+ const sortedParams = new URLSearchParams();
227
+ Array.from(params.keys()).sort().forEach(key => {
228
+ params.getAll(key).forEach(value => sortedParams.append(key, value));
229
+ });
230
+ urlObj.search = sortedParams.toString();
231
+ return urlObj.toString();
232
+ } catch {
233
+ return url;
234
+ }
235
+ }
236
+
237
+ private static normalizeHeaders(headers: Record<string, string>): Record<string, string> {
238
+ const normalized: Record<string, string> = {};
239
+
240
+ // Only include cache-relevant headers and normalize case
241
+ const relevantHeaders = ['authorization', 'content-type', 'accept', 'cache-control'];
242
+
243
+ for (const [key, value] of Object.entries(headers)) {
244
+ const lowerKey = key.toLowerCase();
245
+ if (relevantHeaders.includes(lowerKey)) {
246
+ normalized[lowerKey] = value;
247
+ }
248
+ }
249
+
250
+ return normalized;
251
+ }
252
+
253
+ private static simpleHash(str: string): string {
254
+ let hash = 0;
255
+ for (let i = 0; i < str.length; i++) {
256
+ const char = str.charCodeAt(i);
257
+ hash = ((hash << 5) - hash) + char;
258
+ hash = hash & hash; // Convert to 32-bit integer
259
+ }
260
+ return hash.toString(36);
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Request coalescing manager
266
+ */
267
+ class RequestCoalescer {
268
+ private readonly pendingRequests = new Map<string, Promise<unknown>>();
269
+ private readonly maxPending: number;
270
+
271
+ constructor(maxPending = ${options.maxPendingRequests || 100}) {
272
+ this.maxPending = maxPending;
273
+ }
274
+
275
+ /**
276
+ * Coalesce identical requests
277
+ */
278
+ async coalesce<T>(
279
+ fingerprint: string,
280
+ requestFactory: () => Promise<T>
281
+ ): Promise<T> {
282
+ // Check if identical request is already pending
283
+ const existingRequest = this.pendingRequests.get(fingerprint);
284
+ if (existingRequest) {
285
+ ${options.enableDebugLogging
286
+ ? "console.debug(`Request coalesced: ${fingerprint}`);"
287
+ : ""}
288
+ return existingRequest as Promise<T>;
289
+ }
290
+
291
+ // Check if we're at capacity
292
+ if (this.pendingRequests.size >= this.maxPending) {
293
+ ${options.enableDebugLogging
294
+ ? "console.warn(`Request coalescing at capacity, executing directly`);"
295
+ : ""}
296
+ return requestFactory();
297
+ }
298
+
299
+ // Create new request and track it
300
+ const requestPromise = requestFactory()
301
+ .finally(() => {
302
+ // Clean up when request completes
303
+ this.pendingRequests.delete(fingerprint);
304
+ });
305
+
306
+ this.pendingRequests.set(fingerprint, requestPromise);
307
+ return requestPromise;
308
+ }
309
+
310
+ /**
311
+ * Clear all pending requests
312
+ */
313
+ clear(): void {
314
+ this.pendingRequests.clear();
315
+ }
316
+
317
+ /**
318
+ * Get current pending request count
319
+ */
320
+ getPendingCount(): number {
321
+ return this.pendingRequests.size;
322
+ }
323
+ }
324
+
325
+ /**
326
+ * Cache manager with stale-while-revalidate support
327
+ */
328
+ class CacheManager<T = unknown> {
329
+ private readonly cache = new LRUCacheWithTTL<string, CacheEntry<T>>();
330
+ private readonly staleRevalidatePromises = new Map<string, Promise<T>>();
331
+
332
+ constructor(private readonly options: {
333
+ defaultTTL: number;
334
+ enableStaleWhileRevalidate: boolean;
335
+ debugLogging: boolean;
336
+ }) {}
337
+
338
+ /**
339
+ * Get cached response or undefined if not cached/expired
340
+ */
341
+ get(fingerprint: string): T | undefined {
342
+ const entry = this.cache.get(fingerprint);
343
+
344
+ if (!entry) {
345
+ return undefined;
346
+ }
347
+
348
+ const now = Date.now();
349
+ const age = now - entry.timestamp;
350
+
351
+ if (age > entry.ttl) {
352
+ // Entry is expired
353
+ if (this.options.enableStaleWhileRevalidate && !entry.stale) {
354
+ // Mark as stale but return it
355
+ entry.stale = true;
356
+ this.cache.set(fingerprint, entry, entry.ttl);
357
+ ${options.enableDebugLogging
358
+ ? "this.options.debugLogging && console.debug(`Serving stale data: ${fingerprint}`);"
359
+ : ""}
360
+ return entry.data;
361
+ }
362
+
363
+ // Entry is too old even for stale-while-revalidate
364
+ this.cache.delete(fingerprint);
365
+ return undefined;
366
+ }
367
+
368
+ ${options.enableDebugLogging
369
+ ? "this.options.debugLogging && console.debug(`Cache hit: ${fingerprint}`);"
370
+ : ""}
371
+ return entry.data;
372
+ }
373
+
374
+ /**
375
+ * Set cached response
376
+ */
377
+ set(fingerprint: string, data: T, ttl?: number): void {
378
+ const entry: CacheEntry<T> = {
379
+ data,
380
+ timestamp: Date.now(),
381
+ ttl: ttl || this.options.defaultTTL,
382
+ stale: false
383
+ };
384
+
385
+ this.cache.set(fingerprint, entry, entry.ttl);
386
+ ${options.enableDebugLogging
387
+ ? "this.options.debugLogging && console.debug(`Cache set: ${fingerprint}`);"
388
+ : ""}
389
+ }
390
+
391
+ /**
392
+ * Check if entry exists and is fresh
393
+ */
394
+ hasFresh(fingerprint: string): boolean {
395
+ const entry = this.cache.get(fingerprint);
396
+ if (!entry) return false;
397
+
398
+ const age = Date.now() - entry.timestamp;
399
+ return age <= entry.ttl && !entry.stale;
400
+ }
401
+
402
+ /**
403
+ * Check if entry exists (including stale)
404
+ */
405
+ has(fingerprint: string): boolean {
406
+ return this.cache.has(fingerprint);
407
+ }
408
+
409
+ /**
410
+ * Invalidate cache entry
411
+ */
412
+ invalidate(fingerprint: string): void {
413
+ this.cache.delete(fingerprint);
414
+ this.staleRevalidatePromises.delete(fingerprint);
415
+ ${options.enableDebugLogging
416
+ ? "this.options.debugLogging && console.debug(`Cache invalidated: ${fingerprint}`);"
417
+ : ""}
418
+ }
419
+
420
+ /**
421
+ * Clear entire cache
422
+ */
423
+ clear(): void {
424
+ this.cache.clear();
425
+ this.staleRevalidatePromises.clear();
426
+ }
427
+
428
+ /**
429
+ * Get cache statistics
430
+ */
431
+ getStats(): { size: number; hitRate: number } {
432
+ return {
433
+ size: this.cache.size(),
434
+ hitRate: 0 // Would need hit/miss tracking
435
+ };
436
+ }
437
+
438
+ /**
439
+ * Revalidate stale entry in background
440
+ */
441
+ revalidateInBackground<U extends T>(
442
+ fingerprint: string,
443
+ revalidator: () => Promise<U>
444
+ ): void {
445
+ if (this.staleRevalidatePromises.has(fingerprint)) {
446
+ return; // Already revalidating
447
+ }
448
+
449
+ const revalidationPromise = revalidator()
450
+ .then(freshData => {
451
+ this.set(fingerprint, freshData);
452
+ return freshData;
453
+ })
454
+ .catch(error => {
455
+ ${options.enableDebugLogging
456
+ ? "this.options.debugLogging && console.warn(`Background revalidation failed: ${fingerprint}`, error);"
457
+ : ""}
458
+ throw error;
459
+ })
460
+ .finally(() => {
461
+ this.staleRevalidatePromises.delete(fingerprint);
462
+ });
463
+
464
+ this.staleRevalidatePromises.set(fingerprint, revalidationPromise);
465
+ }
466
+ }
467
+
468
+ /**
469
+ * Main request deduplicator
470
+ */
471
+ export class RequestDeduplicator {
472
+ private readonly cacheManager: CacheManager;
473
+ private readonly coalescer: RequestCoalescer;
474
+ private readonly endpointPolicies: Record<string, any>;
475
+
476
+ constructor() {
477
+ this.cacheManager = new CacheManager({
478
+ defaultTTL: ${options.defaultTTL || 5 * 60 * 1000},
479
+ enableStaleWhileRevalidate: ${options.enableStaleWhileRevalidate || true},
480
+ debugLogging: ${options.enableDebugLogging || false}
481
+ });
482
+
483
+ this.coalescer = new RequestCoalescer(${options.maxPendingRequests || 100});
484
+ this.endpointPolicies = ${JSON.stringify(options.endpointPolicies || {})};
485
+ }
486
+
487
+ /**
488
+ * Execute request with deduplication
489
+ */
490
+ async execute<T>(
491
+ method: string,
492
+ url: string,
493
+ requestFactory: () => Promise<T>,
494
+ options: {
495
+ headers?: Record<string, string>;
496
+ body?: unknown;
497
+ params?: Record<string, unknown>;
498
+ skipCache?: boolean;
499
+ customTTL?: number;
500
+ } = {}
501
+ ): Promise<T> {
502
+ if (options.skipCache) {
503
+ return requestFactory();
504
+ }
505
+
506
+ // Generate request fingerprint
507
+ const fingerprint = RequestFingerprintGenerator.generate(
508
+ method,
509
+ url,
510
+ options.headers,
511
+ options.body,
512
+ options.params
513
+ );
514
+
515
+ // Check endpoint-specific policy
516
+ const policy = this.getEndpointPolicy(url);
517
+ if (!policy.enabled) {
518
+ return requestFactory();
519
+ }
520
+
521
+ // Try cache first
522
+ const cachedResult = this.cacheManager.get(fingerprint);
523
+ if (cachedResult !== undefined) {
524
+ // If stale and stale-while-revalidate is enabled, revalidate in background
525
+ if (policy.staleWhileRevalidate && !this.cacheManager.hasFresh(fingerprint)) {
526
+ this.cacheManager.revalidateInBackground(fingerprint, requestFactory);
527
+ }
528
+ return cachedResult as T;
529
+ }
530
+
531
+ // No cache hit, execute request with coalescing
532
+ const result = await this.coalescer.coalesce(fingerprint, requestFactory);
533
+
534
+ // Cache the result
535
+ const ttl = options.customTTL || policy.ttl || ${options.defaultTTL || 5 * 60 * 1000};
536
+ this.cacheManager.set(fingerprint, result, ttl);
537
+
538
+ return result;
539
+ }
540
+
541
+ /**
542
+ * Invalidate cache for specific pattern
543
+ */
544
+ invalidate(pattern: string | RegExp): void {
545
+ // In a real implementation, this would iterate through cache keys
546
+ // and invalidate matching entries
547
+ if (typeof pattern === 'string') {
548
+ const fingerprint = RequestFingerprintGenerator.generate('GET', pattern);
549
+ this.cacheManager.invalidate(fingerprint);
550
+ }
551
+ }
552
+
553
+ /**
554
+ * Clear all caches
555
+ */
556
+ clearCache(): void {
557
+ this.cacheManager.clear();
558
+ this.coalescer.clear();
559
+ }
560
+
561
+ /**
562
+ * Get cache statistics
563
+ */
564
+ getStats(): {
565
+ cacheSize: number;
566
+ pendingRequests: number;
567
+ cacheHitRate: number;
568
+ } {
569
+ return {
570
+ cacheSize: this.cacheManager.getStats().size,
571
+ pendingRequests: this.coalescer.getPendingCount(),
572
+ cacheHitRate: this.cacheManager.getStats().hitRate
573
+ };
574
+ }
575
+
576
+ private getEndpointPolicy(url: string): {
577
+ enabled: boolean;
578
+ ttl: number;
579
+ staleWhileRevalidate: boolean;
580
+ } {
581
+ // Simple URL matching - in production, use more sophisticated matching
582
+ for (const [pattern, policy] of Object.entries(this.endpointPolicies)) {
583
+ if (url.includes(pattern)) {
584
+ return {
585
+ enabled: policy.enabled ?? true,
586
+ ttl: policy.ttl ?? ${options.defaultTTL || 5 * 60 * 1000},
587
+ staleWhileRevalidate: policy.staleWhileRevalidate ?? ${options.enableStaleWhileRevalidate || true}
588
+ };
589
+ }
590
+ }
591
+
592
+ return {
593
+ enabled: true,
594
+ ttl: ${options.defaultTTL || 5 * 60 * 1000},
595
+ staleWhileRevalidate: ${options.enableStaleWhileRevalidate || true}
596
+ };
597
+ }
598
+ }
599
+
600
+ /**
601
+ * Global deduplicator instance
602
+ */
603
+ const globalDeduplicator = new RequestDeduplicator();
604
+
605
+ /**
606
+ * Utility functions
607
+ */
608
+ export function clearRequestCache(): void {
609
+ globalDeduplicator.clearCache();
610
+ }
611
+
612
+ export function getCacheStats(): ReturnType<RequestDeduplicator['getStats']> {
613
+ return globalDeduplicator.getStats();
614
+ }
615
+
616
+ export function invalidateCache(pattern: string | RegExp): void {
617
+ globalDeduplicator.invalidate(pattern);
618
+ }
619
+
620
+ /**
621
+ * Request wrapper with deduplication
622
+ */
623
+ export function withDeduplication<T extends (...args: any[]) => Promise<any>>(
624
+ requestFn: T,
625
+ options: {
626
+ method: string;
627
+ urlExtractor: (...args: Parameters<T>) => string;
628
+ headersExtractor?: (...args: Parameters<T>) => Record<string, string>;
629
+ bodyExtractor?: (...args: Parameters<T>) => unknown;
630
+ paramsExtractor?: (...args: Parameters<T>) => Record<string, unknown>;
631
+ }
632
+ ): T {
633
+ return ((...args: Parameters<T>) => {
634
+ const url = options.urlExtractor(...args);
635
+ const headers = options.headersExtractor?.(...args);
636
+ const body = options.bodyExtractor?.(...args);
637
+ const params = options.paramsExtractor?.(...args);
638
+
639
+ return globalDeduplicator.execute(
640
+ options.method,
641
+ url,
642
+ () => requestFn(...args),
643
+ { headers, body, params }
644
+ );
645
+ }) as T;
646
+ }`;
647
+ }
648
+ // Export plugin for registration
649
+ exports.default = exports.requestDeduplicationPlugin;
650
+ //# sourceMappingURL=request-deduplication.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request-deduplication.js","sourceRoot":"","sources":["../../src/plugins/request-deduplication.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Advanced Zod Validation Plugin
3
+ *
4
+ * Generates Zod schemas from OpenAPI specifications for runtime validation.
5
+ * Implements sophisticated type-level validation mapping with performance optimizations.
6
+ *
7
+ * Key Features:
8
+ * - Type-safe request/response validation pipelines
9
+ * - Performance-optimized validation caching
10
+ * - Branded type integration for compile-time safety
11
+ * - Runtime type guard generation
12
+ *
13
+ * Architecture:
14
+ * - Schema transformation with memoization
15
+ * - Lazy evaluation for expensive operations
16
+ * - Memory-efficient validation cache management
17
+ */
18
+ import { SchemanticPlugin } from "../types/core";
19
+ type ZodType<T = unknown> = {
20
+ parse(data: unknown): T;
21
+ safeParse(data: unknown): {
22
+ success: true;
23
+ data: T;
24
+ } | {
25
+ success: false;
26
+ error: {
27
+ errors: Array<{
28
+ path: (string | number)[];
29
+ message: string;
30
+ }>;
31
+ };
32
+ };
33
+ pipe<U>(schema: ZodType<U>): ZodType<U>;
34
+ };
35
+ /**
36
+ * Advanced Zod Validation Plugin Implementation
37
+ */
38
+ export declare const zodValidationPlugin: SchemanticPlugin;
39
+ export declare function memoizedValidation<T>(data: unknown, schema: ZodType<T>, cacheKey?: string, schemaVersion?: string): T;
40
+ /**
41
+ * Get performance statistics for the plugin
42
+ */
43
+ export declare function getZodValidationPerformanceStats(): Record<string, {
44
+ avg: number;
45
+ min: number;
46
+ max: number;
47
+ count: number;
48
+ }>;
49
+ /**
50
+ * Clear performance metrics and caches
51
+ */
52
+ export declare function clearZodValidationCaches(): void;
53
+ /**
54
+ * Get validation cache statistics for monitoring
55
+ */
56
+ export declare function getValidationCacheStats(): {
57
+ size: number;
58
+ maxSize: number;
59
+ hitRate?: number;
60
+ averageAccessTime?: number;
61
+ };
62
+ export default zodValidationPlugin;
63
+ //# sourceMappingURL=zod-validation.d.ts.map