@objectstack/core 1.0.4 → 1.0.6

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 (127) hide show
  1. package/.turbo/turbo-build.log +22 -0
  2. package/CHANGELOG.md +19 -0
  3. package/dist/index.cjs +4304 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +1777 -0
  6. package/dist/index.d.ts +1776 -21
  7. package/dist/index.js +4246 -23
  8. package/dist/index.js.map +1 -0
  9. package/package.json +5 -5
  10. package/src/logger.ts +2 -2
  11. package/src/security/plugin-signature-verifier.ts +12 -11
  12. package/tsconfig.json +1 -3
  13. package/dist/api-registry-plugin.d.ts +0 -54
  14. package/dist/api-registry-plugin.d.ts.map +0 -1
  15. package/dist/api-registry-plugin.js +0 -53
  16. package/dist/api-registry-plugin.test.d.ts +0 -2
  17. package/dist/api-registry-plugin.test.d.ts.map +0 -1
  18. package/dist/api-registry-plugin.test.js +0 -334
  19. package/dist/api-registry.d.ts +0 -259
  20. package/dist/api-registry.d.ts.map +0 -1
  21. package/dist/api-registry.js +0 -600
  22. package/dist/api-registry.test.d.ts +0 -2
  23. package/dist/api-registry.test.d.ts.map +0 -1
  24. package/dist/api-registry.test.js +0 -957
  25. package/dist/contracts/data-engine.d.ts +0 -62
  26. package/dist/contracts/data-engine.d.ts.map +0 -1
  27. package/dist/contracts/data-engine.js +0 -1
  28. package/dist/contracts/http-server.d.ts +0 -119
  29. package/dist/contracts/http-server.d.ts.map +0 -1
  30. package/dist/contracts/http-server.js +0 -11
  31. package/dist/contracts/logger.d.ts +0 -63
  32. package/dist/contracts/logger.d.ts.map +0 -1
  33. package/dist/contracts/logger.js +0 -1
  34. package/dist/dependency-resolver.d.ts +0 -62
  35. package/dist/dependency-resolver.d.ts.map +0 -1
  36. package/dist/dependency-resolver.js +0 -317
  37. package/dist/dependency-resolver.test.d.ts +0 -2
  38. package/dist/dependency-resolver.test.d.ts.map +0 -1
  39. package/dist/dependency-resolver.test.js +0 -241
  40. package/dist/health-monitor.d.ts +0 -65
  41. package/dist/health-monitor.d.ts.map +0 -1
  42. package/dist/health-monitor.js +0 -269
  43. package/dist/health-monitor.test.d.ts +0 -2
  44. package/dist/health-monitor.test.d.ts.map +0 -1
  45. package/dist/health-monitor.test.js +0 -68
  46. package/dist/hot-reload.d.ts +0 -79
  47. package/dist/hot-reload.d.ts.map +0 -1
  48. package/dist/hot-reload.js +0 -313
  49. package/dist/index.d.ts.map +0 -1
  50. package/dist/kernel-base.d.ts +0 -84
  51. package/dist/kernel-base.d.ts.map +0 -1
  52. package/dist/kernel-base.js +0 -219
  53. package/dist/kernel.d.ts +0 -113
  54. package/dist/kernel.d.ts.map +0 -1
  55. package/dist/kernel.js +0 -472
  56. package/dist/kernel.test.d.ts +0 -2
  57. package/dist/kernel.test.d.ts.map +0 -1
  58. package/dist/kernel.test.js +0 -414
  59. package/dist/lite-kernel.d.ts +0 -55
  60. package/dist/lite-kernel.d.ts.map +0 -1
  61. package/dist/lite-kernel.js +0 -112
  62. package/dist/lite-kernel.test.d.ts +0 -2
  63. package/dist/lite-kernel.test.d.ts.map +0 -1
  64. package/dist/lite-kernel.test.js +0 -161
  65. package/dist/logger.d.ts +0 -71
  66. package/dist/logger.d.ts.map +0 -1
  67. package/dist/logger.js +0 -312
  68. package/dist/logger.test.d.ts +0 -2
  69. package/dist/logger.test.d.ts.map +0 -1
  70. package/dist/logger.test.js +0 -92
  71. package/dist/plugin-loader.d.ts +0 -164
  72. package/dist/plugin-loader.d.ts.map +0 -1
  73. package/dist/plugin-loader.js +0 -319
  74. package/dist/plugin-loader.test.d.ts +0 -2
  75. package/dist/plugin-loader.test.d.ts.map +0 -1
  76. package/dist/plugin-loader.test.js +0 -348
  77. package/dist/qa/adapter.d.ts +0 -14
  78. package/dist/qa/adapter.d.ts.map +0 -1
  79. package/dist/qa/adapter.js +0 -1
  80. package/dist/qa/http-adapter.d.ts +0 -16
  81. package/dist/qa/http-adapter.d.ts.map +0 -1
  82. package/dist/qa/http-adapter.js +0 -107
  83. package/dist/qa/index.d.ts +0 -4
  84. package/dist/qa/index.d.ts.map +0 -1
  85. package/dist/qa/index.js +0 -3
  86. package/dist/qa/runner.d.ts +0 -27
  87. package/dist/qa/runner.d.ts.map +0 -1
  88. package/dist/qa/runner.js +0 -157
  89. package/dist/security/index.d.ts +0 -17
  90. package/dist/security/index.d.ts.map +0 -1
  91. package/dist/security/index.js +0 -17
  92. package/dist/security/permission-manager.d.ts +0 -96
  93. package/dist/security/permission-manager.d.ts.map +0 -1
  94. package/dist/security/permission-manager.js +0 -235
  95. package/dist/security/permission-manager.test.d.ts +0 -2
  96. package/dist/security/permission-manager.test.d.ts.map +0 -1
  97. package/dist/security/permission-manager.test.js +0 -220
  98. package/dist/security/plugin-config-validator.d.ts +0 -79
  99. package/dist/security/plugin-config-validator.d.ts.map +0 -1
  100. package/dist/security/plugin-config-validator.js +0 -166
  101. package/dist/security/plugin-config-validator.test.d.ts +0 -2
  102. package/dist/security/plugin-config-validator.test.d.ts.map +0 -1
  103. package/dist/security/plugin-config-validator.test.js +0 -223
  104. package/dist/security/plugin-permission-enforcer.d.ts +0 -154
  105. package/dist/security/plugin-permission-enforcer.d.ts.map +0 -1
  106. package/dist/security/plugin-permission-enforcer.js +0 -323
  107. package/dist/security/plugin-permission-enforcer.test.d.ts +0 -2
  108. package/dist/security/plugin-permission-enforcer.test.d.ts.map +0 -1
  109. package/dist/security/plugin-permission-enforcer.test.js +0 -205
  110. package/dist/security/plugin-signature-verifier.d.ts +0 -96
  111. package/dist/security/plugin-signature-verifier.d.ts.map +0 -1
  112. package/dist/security/plugin-signature-verifier.js +0 -250
  113. package/dist/security/sandbox-runtime.d.ts +0 -115
  114. package/dist/security/sandbox-runtime.d.ts.map +0 -1
  115. package/dist/security/sandbox-runtime.js +0 -311
  116. package/dist/security/security-scanner.d.ts +0 -92
  117. package/dist/security/security-scanner.d.ts.map +0 -1
  118. package/dist/security/security-scanner.js +0 -273
  119. package/dist/types.d.ts +0 -89
  120. package/dist/types.d.ts.map +0 -1
  121. package/dist/types.js +0 -1
  122. package/dist/utils/env.d.ts +0 -20
  123. package/dist/utils/env.d.ts.map +0 -1
  124. package/dist/utils/env.js +0 -46
  125. package/dist/utils/env.test.d.ts +0 -2
  126. package/dist/utils/env.test.d.ts.map +0 -1
  127. package/dist/utils/env.test.js +0 -52
@@ -1,600 +0,0 @@
1
- import { ApiRegistryEntrySchema } from '@objectstack/spec/api';
2
- import { getEnv } from './utils/env.js';
3
- /**
4
- * API Registry Service
5
- *
6
- * Central registry for managing API endpoints across different protocols.
7
- * Provides endpoint registration, discovery, and conflict resolution.
8
- *
9
- * **Features:**
10
- * - Multi-protocol support (REST, GraphQL, OData, WebSocket, etc.)
11
- * - Route conflict detection with configurable resolution strategies
12
- * - RBAC permission integration
13
- * - Dynamic schema linking with ObjectQL references
14
- * - Plugin API registration
15
- *
16
- * **Architecture Alignment:**
17
- * - Kubernetes: Service Discovery & API Server
18
- * - AWS API Gateway: Unified API Management
19
- * - Kong Gateway: Plugin-based API Management
20
- *
21
- * @example
22
- * ```typescript
23
- * const registry = new ApiRegistry(logger, 'priority');
24
- *
25
- * // Register an API
26
- * registry.registerApi({
27
- * id: 'customer_api',
28
- * name: 'Customer API',
29
- * type: 'rest',
30
- * version: 'v1',
31
- * basePath: '/api/v1/customers',
32
- * endpoints: [...]
33
- * });
34
- *
35
- * // Discover APIs
36
- * const apis = registry.findApis({ type: 'rest', status: 'active' });
37
- *
38
- * // Get registry snapshot
39
- * const snapshot = registry.getRegistry();
40
- * ```
41
- */
42
- export class ApiRegistry {
43
- constructor(logger, conflictResolution = 'error', version = '1.0.0') {
44
- this.apis = new Map();
45
- this.endpoints = new Map();
46
- this.routes = new Map();
47
- // Performance optimization: Auxiliary indices for O(1) lookups
48
- this.apisByType = new Map();
49
- this.apisByTag = new Map();
50
- this.apisByStatus = new Map();
51
- this.logger = logger;
52
- this.conflictResolution = conflictResolution;
53
- this.version = version;
54
- this.updatedAt = new Date().toISOString();
55
- }
56
- /**
57
- * Register an API with its endpoints
58
- *
59
- * @param api - API registry entry
60
- * @throws Error if API already registered or route conflicts detected
61
- */
62
- registerApi(api) {
63
- // Check if API already exists
64
- if (this.apis.has(api.id)) {
65
- throw new Error(`[ApiRegistry] API '${api.id}' already registered`);
66
- }
67
- // Parse and validate the input using Zod schema
68
- const fullApi = ApiRegistryEntrySchema.parse(api);
69
- // Validate and register endpoints
70
- for (const endpoint of fullApi.endpoints) {
71
- this.validateEndpoint(endpoint, fullApi.id);
72
- }
73
- // Register the API
74
- this.apis.set(fullApi.id, fullApi);
75
- // Register endpoints
76
- for (const endpoint of fullApi.endpoints) {
77
- this.registerEndpoint(fullApi.id, endpoint);
78
- }
79
- // Update auxiliary indices for performance optimization
80
- this.updateIndices(fullApi);
81
- this.updatedAt = new Date().toISOString();
82
- this.logger.info(`API registered: ${fullApi.id}`, {
83
- api: fullApi.id,
84
- type: fullApi.type,
85
- endpointCount: fullApi.endpoints.length,
86
- });
87
- }
88
- /**
89
- * Unregister an API and all its endpoints
90
- *
91
- * @param apiId - API identifier
92
- */
93
- unregisterApi(apiId) {
94
- const api = this.apis.get(apiId);
95
- if (!api) {
96
- throw new Error(`[ApiRegistry] API '${apiId}' not found`);
97
- }
98
- // Remove all endpoints
99
- for (const endpoint of api.endpoints) {
100
- this.unregisterEndpoint(apiId, endpoint.id);
101
- }
102
- // Remove from auxiliary indices
103
- this.removeFromIndices(api);
104
- // Remove the API
105
- this.apis.delete(apiId);
106
- this.updatedAt = new Date().toISOString();
107
- this.logger.info(`API unregistered: ${apiId}`);
108
- }
109
- /**
110
- * Register a single endpoint
111
- *
112
- * @param apiId - API identifier
113
- * @param endpoint - Endpoint registration
114
- * @throws Error if route conflict detected
115
- */
116
- registerEndpoint(apiId, endpoint) {
117
- const endpointKey = `${apiId}:${endpoint.id}`;
118
- // Check if endpoint already registered
119
- if (this.endpoints.has(endpointKey)) {
120
- throw new Error(`[ApiRegistry] Endpoint '${endpoint.id}' already registered for API '${apiId}'`);
121
- }
122
- // Register endpoint
123
- this.endpoints.set(endpointKey, { api: apiId, endpoint });
124
- // Register route if path is defined
125
- if (endpoint.path) {
126
- this.registerRoute(apiId, endpoint);
127
- }
128
- }
129
- /**
130
- * Unregister a single endpoint
131
- *
132
- * @param apiId - API identifier
133
- * @param endpointId - Endpoint identifier
134
- */
135
- unregisterEndpoint(apiId, endpointId) {
136
- const endpointKey = `${apiId}:${endpointId}`;
137
- const entry = this.endpoints.get(endpointKey);
138
- if (!entry) {
139
- return; // Already unregistered
140
- }
141
- // Unregister route
142
- if (entry.endpoint.path) {
143
- const routeKey = this.getRouteKey(entry.endpoint);
144
- this.routes.delete(routeKey);
145
- }
146
- // Unregister endpoint
147
- this.endpoints.delete(endpointKey);
148
- }
149
- /**
150
- * Register a route with conflict detection
151
- *
152
- * @param apiId - API identifier
153
- * @param endpoint - Endpoint registration
154
- * @throws Error if route conflict detected (based on strategy)
155
- */
156
- registerRoute(apiId, endpoint) {
157
- const routeKey = this.getRouteKey(endpoint);
158
- const priority = endpoint.priority ?? 100;
159
- const existingRoute = this.routes.get(routeKey);
160
- if (existingRoute) {
161
- // Route conflict detected
162
- this.handleRouteConflict(routeKey, apiId, endpoint, existingRoute, priority);
163
- return;
164
- }
165
- // Register route
166
- this.routes.set(routeKey, {
167
- api: apiId,
168
- endpointId: endpoint.id,
169
- priority,
170
- });
171
- }
172
- /**
173
- * Handle route conflict based on resolution strategy
174
- *
175
- * @param routeKey - Route key
176
- * @param apiId - New API identifier
177
- * @param endpoint - New endpoint
178
- * @param existingRoute - Existing route registration
179
- * @param newPriority - New endpoint priority
180
- * @throws Error if strategy is 'error'
181
- */
182
- handleRouteConflict(routeKey, apiId, endpoint, existingRoute, newPriority) {
183
- const strategy = this.conflictResolution;
184
- switch (strategy) {
185
- case 'error':
186
- throw new Error(`[ApiRegistry] Route conflict detected: '${routeKey}' is already registered by API '${existingRoute.api}' endpoint '${existingRoute.endpointId}'`);
187
- case 'priority':
188
- if (newPriority > existingRoute.priority) {
189
- // New endpoint has higher priority, replace
190
- this.logger.warn(`Route conflict: replacing '${routeKey}' (priority ${existingRoute.priority} -> ${newPriority})`, {
191
- oldApi: existingRoute.api,
192
- oldEndpoint: existingRoute.endpointId,
193
- newApi: apiId,
194
- newEndpoint: endpoint.id,
195
- });
196
- this.routes.set(routeKey, {
197
- api: apiId,
198
- endpointId: endpoint.id,
199
- priority: newPriority,
200
- });
201
- }
202
- else {
203
- // Existing endpoint has higher priority, keep it
204
- this.logger.warn(`Route conflict: keeping existing '${routeKey}' (priority ${existingRoute.priority} >= ${newPriority})`, {
205
- existingApi: existingRoute.api,
206
- existingEndpoint: existingRoute.endpointId,
207
- newApi: apiId,
208
- newEndpoint: endpoint.id,
209
- });
210
- }
211
- break;
212
- case 'first-wins':
213
- // Keep existing route
214
- this.logger.warn(`Route conflict: keeping first registered '${routeKey}'`, {
215
- existingApi: existingRoute.api,
216
- newApi: apiId,
217
- });
218
- break;
219
- case 'last-wins':
220
- // Replace with new route
221
- this.logger.warn(`Route conflict: replacing with last registered '${routeKey}'`, {
222
- oldApi: existingRoute.api,
223
- newApi: apiId,
224
- });
225
- this.routes.set(routeKey, {
226
- api: apiId,
227
- endpointId: endpoint.id,
228
- priority: newPriority,
229
- });
230
- break;
231
- default:
232
- throw new Error(`[ApiRegistry] Unknown conflict resolution strategy: ${strategy}`);
233
- }
234
- }
235
- /**
236
- * Generate a unique route key for conflict detection
237
- *
238
- * NOTE: This implementation uses exact string matching for route conflict detection.
239
- * It works well for static paths but has limitations with parameterized routes.
240
- * For example, `/api/users/:id` and `/api/users/:userId` will NOT be detected as conflicts
241
- * even though they are semantically identical parameterized patterns. Similarly,
242
- * `/api/:resource/list` and `/api/:entity/list` would also not be detected as conflicting.
243
- *
244
- * For more advanced conflict detection (e.g., path-to-regexp pattern matching),
245
- * consider integrating with your routing library's conflict detection mechanism.
246
- *
247
- * @param endpoint - Endpoint registration
248
- * @returns Route key (e.g., "GET:/api/v1/customers/:id")
249
- */
250
- getRouteKey(endpoint) {
251
- const method = endpoint.method || 'ANY';
252
- return `${method}:${endpoint.path}`;
253
- }
254
- /**
255
- * Validate endpoint registration
256
- *
257
- * @param endpoint - Endpoint to validate
258
- * @param apiId - API identifier (for error messages)
259
- * @throws Error if endpoint is invalid
260
- */
261
- validateEndpoint(endpoint, apiId) {
262
- if (!endpoint.id) {
263
- throw new Error(`[ApiRegistry] Endpoint in API '${apiId}' missing 'id' field`);
264
- }
265
- if (!endpoint.path) {
266
- throw new Error(`[ApiRegistry] Endpoint '${endpoint.id}' in API '${apiId}' missing 'path' field`);
267
- }
268
- }
269
- /**
270
- * Get an API by ID
271
- *
272
- * @param apiId - API identifier
273
- * @returns API registry entry or undefined
274
- */
275
- getApi(apiId) {
276
- return this.apis.get(apiId);
277
- }
278
- /**
279
- * Get all registered APIs
280
- *
281
- * @returns Array of all APIs
282
- */
283
- getAllApis() {
284
- return Array.from(this.apis.values());
285
- }
286
- /**
287
- * Find APIs matching query criteria
288
- *
289
- * Performance optimized with auxiliary indices for O(1) lookups on type, tags, and status.
290
- *
291
- * @param query - Discovery query parameters
292
- * @returns Matching APIs
293
- */
294
- findApis(query) {
295
- let resultIds;
296
- // Use indices for performance-optimized filtering
297
- // Start with the most restrictive filter to minimize subsequent filtering
298
- // Filter by type (using index for O(1) lookup)
299
- if (query.type) {
300
- const typeIds = this.apisByType.get(query.type);
301
- if (!typeIds || typeIds.size === 0) {
302
- return { apis: [], total: 0, filters: query };
303
- }
304
- resultIds = new Set(typeIds);
305
- }
306
- // Filter by status (using index for O(1) lookup)
307
- if (query.status) {
308
- const statusIds = this.apisByStatus.get(query.status);
309
- if (!statusIds || statusIds.size === 0) {
310
- return { apis: [], total: 0, filters: query };
311
- }
312
- if (resultIds) {
313
- // Intersect with previous results
314
- resultIds = new Set([...resultIds].filter(id => statusIds.has(id)));
315
- }
316
- else {
317
- resultIds = new Set(statusIds);
318
- }
319
- if (resultIds.size === 0) {
320
- return { apis: [], total: 0, filters: query };
321
- }
322
- }
323
- // Filter by tags (using index for O(M) lookup where M is number of tags)
324
- if (query.tags && query.tags.length > 0) {
325
- const tagMatches = new Set();
326
- for (const tag of query.tags) {
327
- const tagIds = this.apisByTag.get(tag);
328
- if (tagIds) {
329
- tagIds.forEach(id => tagMatches.add(id));
330
- }
331
- }
332
- if (tagMatches.size === 0) {
333
- return { apis: [], total: 0, filters: query };
334
- }
335
- if (resultIds) {
336
- // Intersect with previous results
337
- resultIds = new Set([...resultIds].filter(id => tagMatches.has(id)));
338
- }
339
- else {
340
- resultIds = tagMatches;
341
- }
342
- if (resultIds.size === 0) {
343
- return { apis: [], total: 0, filters: query };
344
- }
345
- }
346
- // Get the actual API objects
347
- let results;
348
- if (resultIds) {
349
- results = Array.from(resultIds)
350
- .map(id => this.apis.get(id))
351
- .filter((api) => api !== undefined);
352
- }
353
- else {
354
- results = Array.from(this.apis.values());
355
- }
356
- // Apply remaining filters that don't have indices (less common filters)
357
- // Filter by plugin source
358
- if (query.pluginSource) {
359
- results = results.filter((api) => api.metadata?.pluginSource === query.pluginSource);
360
- }
361
- // Filter by version
362
- if (query.version) {
363
- results = results.filter((api) => api.version === query.version);
364
- }
365
- // Search in name/description
366
- if (query.search) {
367
- const searchLower = query.search.toLowerCase();
368
- results = results.filter((api) => api.name.toLowerCase().includes(searchLower) ||
369
- (api.description && api.description.toLowerCase().includes(searchLower)));
370
- }
371
- return {
372
- apis: results,
373
- total: results.length,
374
- filters: query,
375
- };
376
- }
377
- /**
378
- * Get endpoint by API ID and endpoint ID
379
- *
380
- * @param apiId - API identifier
381
- * @param endpointId - Endpoint identifier
382
- * @returns Endpoint registration or undefined
383
- */
384
- getEndpoint(apiId, endpointId) {
385
- const key = `${apiId}:${endpointId}`;
386
- return this.endpoints.get(key)?.endpoint;
387
- }
388
- /**
389
- * Find endpoint by route (method + path)
390
- *
391
- * @param method - HTTP method
392
- * @param path - URL path
393
- * @returns Endpoint registration or undefined
394
- */
395
- findEndpointByRoute(method, path) {
396
- const routeKey = `${method}:${path}`;
397
- const route = this.routes.get(routeKey);
398
- if (!route) {
399
- return undefined;
400
- }
401
- const api = this.apis.get(route.api);
402
- const endpoint = this.getEndpoint(route.api, route.endpointId);
403
- if (!api || !endpoint) {
404
- return undefined;
405
- }
406
- return { api, endpoint };
407
- }
408
- /**
409
- * Get complete registry snapshot
410
- *
411
- * @returns Current registry state
412
- */
413
- getRegistry() {
414
- const apis = Array.from(this.apis.values());
415
- // Group by type
416
- const byType = {};
417
- for (const api of apis) {
418
- if (!byType[api.type]) {
419
- byType[api.type] = [];
420
- }
421
- byType[api.type].push(api);
422
- }
423
- // Group by status
424
- const byStatus = {};
425
- for (const api of apis) {
426
- const status = api.metadata?.status || 'active';
427
- if (!byStatus[status]) {
428
- byStatus[status] = [];
429
- }
430
- byStatus[status].push(api);
431
- }
432
- // Count total endpoints
433
- const totalEndpoints = apis.reduce((sum, api) => sum + api.endpoints.length, 0);
434
- return {
435
- version: this.version,
436
- conflictResolution: this.conflictResolution,
437
- apis,
438
- totalApis: apis.length,
439
- totalEndpoints,
440
- byType,
441
- byStatus,
442
- updatedAt: this.updatedAt,
443
- };
444
- }
445
- /**
446
- * Clear all registered APIs
447
- *
448
- * **⚠️ SAFETY WARNING:**
449
- * This method clears all registered APIs and should be used with caution.
450
- *
451
- * **Usage Restrictions:**
452
- * - In production environments (NODE_ENV=production), a `force: true` parameter is required
453
- * - Primarily intended for testing and development hot-reload scenarios
454
- *
455
- * @param options - Clear options
456
- * @param options.force - Force clear in production environment (default: false)
457
- * @throws Error if called in production without force flag
458
- *
459
- * @example Safe usage in tests
460
- * ```typescript
461
- * beforeEach(() => {
462
- * registry.clear(); // OK in test environment
463
- * });
464
- * ```
465
- *
466
- * @example Usage in production (requires explicit force)
467
- * ```typescript
468
- * // In production, explicit force is required
469
- * registry.clear({ force: true });
470
- * ```
471
- */
472
- clear(options = {}) {
473
- const isProduction = this.isProductionEnvironment();
474
- if (isProduction && !options.force) {
475
- throw new Error('[ApiRegistry] Cannot clear registry in production environment without force flag. ' +
476
- 'Use clear({ force: true }) if you really want to clear the registry.');
477
- }
478
- this.apis.clear();
479
- this.endpoints.clear();
480
- this.routes.clear();
481
- // Clear auxiliary indices
482
- this.apisByType.clear();
483
- this.apisByTag.clear();
484
- this.apisByStatus.clear();
485
- this.updatedAt = new Date().toISOString();
486
- if (isProduction) {
487
- this.logger.warn('API registry forcefully cleared in production', { force: options.force });
488
- }
489
- else {
490
- this.logger.info('API registry cleared');
491
- }
492
- }
493
- /**
494
- * Get registry statistics
495
- *
496
- * @returns Registry statistics
497
- */
498
- getStats() {
499
- const apis = Array.from(this.apis.values());
500
- const apisByType = {};
501
- for (const api of apis) {
502
- apisByType[api.type] = (apisByType[api.type] || 0) + 1;
503
- }
504
- const endpointsByApi = {};
505
- for (const api of apis) {
506
- endpointsByApi[api.id] = api.endpoints.length;
507
- }
508
- return {
509
- totalApis: this.apis.size,
510
- totalEndpoints: this.endpoints.size,
511
- totalRoutes: this.routes.size,
512
- apisByType,
513
- endpointsByApi,
514
- };
515
- }
516
- /**
517
- * Update auxiliary indices when an API is registered
518
- *
519
- * @param api - API entry to index
520
- * @private
521
- * @internal
522
- */
523
- updateIndices(api) {
524
- // Index by type
525
- this.ensureIndexSet(this.apisByType, api.type).add(api.id);
526
- // Index by status
527
- const status = api.metadata?.status || 'active';
528
- this.ensureIndexSet(this.apisByStatus, status).add(api.id);
529
- // Index by tags
530
- const tags = api.metadata?.tags || [];
531
- for (const tag of tags) {
532
- this.ensureIndexSet(this.apisByTag, tag).add(api.id);
533
- }
534
- }
535
- /**
536
- * Remove API from auxiliary indices when unregistered
537
- *
538
- * @param api - API entry to remove from indices
539
- * @private
540
- * @internal
541
- */
542
- removeFromIndices(api) {
543
- // Remove from type index
544
- this.removeFromIndexSet(this.apisByType, api.type, api.id);
545
- // Remove from status index
546
- const status = api.metadata?.status || 'active';
547
- this.removeFromIndexSet(this.apisByStatus, status, api.id);
548
- // Remove from tag indices
549
- const tags = api.metadata?.tags || [];
550
- for (const tag of tags) {
551
- this.removeFromIndexSet(this.apisByTag, tag, api.id);
552
- }
553
- }
554
- /**
555
- * Helper to ensure an index set exists and return it
556
- *
557
- * @param map - Index map
558
- * @param key - Index key
559
- * @returns The Set for this key (created if needed)
560
- * @private
561
- * @internal
562
- */
563
- ensureIndexSet(map, key) {
564
- let set = map.get(key);
565
- if (!set) {
566
- set = new Set();
567
- map.set(key, set);
568
- }
569
- return set;
570
- }
571
- /**
572
- * Helper to remove an ID from an index set and clean up empty sets
573
- *
574
- * @param map - Index map
575
- * @param key - Index key
576
- * @param id - API ID to remove
577
- * @private
578
- * @internal
579
- */
580
- removeFromIndexSet(map, key, id) {
581
- const set = map.get(key);
582
- if (set) {
583
- set.delete(id);
584
- // Clean up empty sets to avoid memory leaks
585
- if (set.size === 0) {
586
- map.delete(key);
587
- }
588
- }
589
- }
590
- /**
591
- * Check if running in production environment
592
- *
593
- * @returns true if NODE_ENV is 'production'
594
- * @private
595
- * @internal
596
- */
597
- isProductionEnvironment() {
598
- return getEnv('NODE_ENV') === 'production';
599
- }
600
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=api-registry.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"api-registry.test.d.ts","sourceRoot":"","sources":["../src/api-registry.test.ts"],"names":[],"mappings":""}