@hazeljs/discovery 0.2.0-beta.16 → 0.2.0-beta.17

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 (42) hide show
  1. package/README.md +185 -19
  2. package/dist/__tests__/consul-backend.test.d.ts +6 -0
  3. package/dist/__tests__/consul-backend.test.d.ts.map +1 -0
  4. package/dist/__tests__/consul-backend.test.js +300 -0
  5. package/dist/__tests__/kubernetes-backend.test.d.ts +6 -0
  6. package/dist/__tests__/kubernetes-backend.test.d.ts.map +1 -0
  7. package/dist/__tests__/kubernetes-backend.test.js +261 -0
  8. package/dist/__tests__/redis-backend.test.d.ts +6 -0
  9. package/dist/__tests__/redis-backend.test.d.ts.map +1 -0
  10. package/dist/__tests__/redis-backend.test.js +280 -0
  11. package/dist/backends/consul-backend.d.ts +46 -7
  12. package/dist/backends/consul-backend.d.ts.map +1 -1
  13. package/dist/backends/consul-backend.js +23 -39
  14. package/dist/backends/kubernetes-backend.d.ts +44 -6
  15. package/dist/backends/kubernetes-backend.d.ts.map +1 -1
  16. package/dist/backends/kubernetes-backend.js +11 -32
  17. package/dist/backends/memory-backend.d.ts +0 -1
  18. package/dist/backends/memory-backend.d.ts.map +1 -1
  19. package/dist/backends/memory-backend.js +3 -32
  20. package/dist/backends/redis-backend.d.ts +11 -6
  21. package/dist/backends/redis-backend.d.ts.map +1 -1
  22. package/dist/backends/redis-backend.js +66 -46
  23. package/dist/client/discovery-client.d.ts +6 -4
  24. package/dist/client/discovery-client.d.ts.map +1 -1
  25. package/dist/client/discovery-client.js +30 -30
  26. package/dist/client/service-client.d.ts.map +1 -1
  27. package/dist/client/service-client.js +82 -17
  28. package/dist/index.d.ts +4 -1
  29. package/dist/index.d.ts.map +1 -1
  30. package/dist/index.js +8 -1
  31. package/dist/registry/service-registry.d.ts.map +1 -1
  32. package/dist/registry/service-registry.js +13 -2
  33. package/dist/utils/filter.d.ts +10 -0
  34. package/dist/utils/filter.d.ts.map +1 -0
  35. package/dist/utils/filter.js +39 -0
  36. package/dist/utils/logger.d.ts +21 -0
  37. package/dist/utils/logger.d.ts.map +1 -0
  38. package/dist/utils/logger.js +34 -0
  39. package/dist/utils/validation.d.ts +36 -0
  40. package/dist/utils/validation.d.ts.map +1 -0
  41. package/dist/utils/validation.js +109 -0
  42. package/package.json +3 -3
@@ -6,9 +6,13 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.ConsulRegistryBackend = void 0;
8
8
  const types_1 = require("../types");
9
+ const filter_1 = require("../utils/filter");
10
+ const logger_1 = require("../utils/logger");
11
+ const validation_1 = require("../utils/validation");
9
12
  class ConsulRegistryBackend {
10
13
  constructor(consul, config = {}) {
11
14
  this.checkIntervals = new Map();
15
+ (0, validation_1.validateConsulBackendConfig)(config);
12
16
  this.consul = consul;
13
17
  this.ttl = config.ttl || '30s';
14
18
  }
@@ -51,19 +55,21 @@ class ConsulRegistryBackend {
51
55
  * Update service instance heartbeat
52
56
  */
53
57
  async heartbeat(instanceId) {
58
+ const logger = logger_1.DiscoveryLogger.getLogger();
54
59
  const checkId = `service:${instanceId}`;
55
60
  try {
56
61
  // Pass TTL check
57
62
  await this.consul.agent.check.pass(checkId);
58
63
  }
59
- catch {
60
- // Silently fail - will be retried on next heartbeat
64
+ catch (error) {
65
+ logger.warn(`Consul heartbeat failed for ${instanceId}, will retry on next heartbeat`, error);
61
66
  }
62
67
  }
63
68
  /**
64
69
  * Get all instances of a service
65
70
  */
66
71
  async getInstances(serviceName, filter) {
72
+ const logger = logger_1.DiscoveryLogger.getLogger();
67
73
  try {
68
74
  const result = await this.consul.health.service({
69
75
  service: serviceName,
@@ -99,12 +105,10 @@ class ConsulRegistryBackend {
99
105
  };
100
106
  });
101
107
  // Apply additional filters
102
- if (filter) {
103
- return this.applyFilter(instances, filter);
104
- }
105
- return instances;
108
+ return (0, filter_1.applyServiceFilter)(instances, filter);
106
109
  }
107
- catch {
110
+ catch (error) {
111
+ logger.error(`Failed to get instances for service "${serviceName}" from Consul`, error);
108
112
  return [];
109
113
  }
110
114
  }
@@ -112,6 +116,7 @@ class ConsulRegistryBackend {
112
116
  * Get a specific service instance
113
117
  */
114
118
  async getInstance(instanceId) {
119
+ const logger = logger_1.DiscoveryLogger.getLogger();
115
120
  try {
116
121
  const services = await this.consul.agent.service.list();
117
122
  const service = services[instanceId];
@@ -144,7 +149,8 @@ class ConsulRegistryBackend {
144
149
  registeredAt: service.Meta?.registeredAt ? new Date(service.Meta.registeredAt) : new Date(),
145
150
  };
146
151
  }
147
- catch {
152
+ catch (error) {
153
+ logger.error(`Failed to get instance "${instanceId}" from Consul`, error);
148
154
  return null;
149
155
  }
150
156
  }
@@ -152,11 +158,13 @@ class ConsulRegistryBackend {
152
158
  * Get all registered services
153
159
  */
154
160
  async getAllServices() {
161
+ const logger = logger_1.DiscoveryLogger.getLogger();
155
162
  try {
156
163
  const services = await this.consul.catalog.service.list();
157
164
  return Object.keys(services);
158
165
  }
159
- catch {
166
+ catch (error) {
167
+ logger.error('Failed to list services from Consul', error);
160
168
  return [];
161
169
  }
162
170
  }
@@ -164,6 +172,7 @@ class ConsulRegistryBackend {
164
172
  * Update service instance status
165
173
  */
166
174
  async updateStatus(instanceId, status) {
175
+ const logger = logger_1.DiscoveryLogger.getLogger();
167
176
  const checkId = `service:${instanceId}`;
168
177
  try {
169
178
  if (status === types_1.ServiceStatus.UP) {
@@ -176,8 +185,8 @@ class ConsulRegistryBackend {
176
185
  await this.consul.agent.check.warn(checkId);
177
186
  }
178
187
  }
179
- catch {
180
- // Silently fail - status will be updated on next heartbeat
188
+ catch (error) {
189
+ logger.warn(`Failed to update status for ${instanceId} in Consul`, error);
181
190
  }
182
191
  }
183
192
  /**
@@ -201,6 +210,7 @@ class ConsulRegistryBackend {
201
210
  * Start TTL check updates for a service
202
211
  */
203
212
  startTTLCheck(instanceId, checkId) {
213
+ const logger = logger_1.DiscoveryLogger.getLogger();
204
214
  // Parse TTL to get interval (update at 2/3 of TTL)
205
215
  const ttlSeconds = this.parseTTL(this.ttl);
206
216
  const intervalMs = (ttlSeconds * 1000 * 2) / 3;
@@ -208,8 +218,8 @@ class ConsulRegistryBackend {
208
218
  try {
209
219
  await this.consul.agent.check.pass(checkId);
210
220
  }
211
- catch {
212
- // Silently fail - will be retried on next interval
221
+ catch (error) {
222
+ logger.warn(`TTL check pass failed for ${instanceId}`, error);
213
223
  }
214
224
  }, intervalMs);
215
225
  this.checkIntervals.set(instanceId, interval);
@@ -245,31 +255,5 @@ class ConsulRegistryBackend {
245
255
  return 30;
246
256
  }
247
257
  }
248
- /**
249
- * Apply filter to instances
250
- */
251
- applyFilter(instances, filter) {
252
- return instances.filter((instance) => {
253
- if (filter.zone && instance.zone !== filter.zone) {
254
- return false;
255
- }
256
- if (filter.status && instance.status !== filter.status) {
257
- return false;
258
- }
259
- if (filter.tags && filter.tags.length > 0) {
260
- if (!instance.tags || !filter.tags.every((tag) => instance.tags.includes(tag))) {
261
- return false;
262
- }
263
- }
264
- if (filter.metadata) {
265
- for (const [key, value] of Object.entries(filter.metadata)) {
266
- if (!instance.metadata || instance.metadata[key] !== value) {
267
- return false;
268
- }
269
- }
270
- }
271
- return true;
272
- });
273
- }
274
258
  }
275
259
  exports.ConsulRegistryBackend = ConsulRegistryBackend;
@@ -4,7 +4,50 @@
4
4
  */
5
5
  import { RegistryBackend } from './registry-backend';
6
6
  import { ServiceInstance, ServiceFilter } from '../types';
7
- type KubeConfig = any;
7
+ /**
8
+ * Minimal type definitions for the Kubernetes client API surface we use.
9
+ * These mirror the shapes exposed by `@kubernetes/client-node`.
10
+ */
11
+ export interface KubeConfig {
12
+ makeApiClient(apiClass: new () => any): any;
13
+ }
14
+ export interface K8sEndpointAddress {
15
+ ip: string;
16
+ targetRef?: {
17
+ name?: string;
18
+ };
19
+ nodeName?: string;
20
+ }
21
+ export interface K8sEndpointPort {
22
+ port: number;
23
+ }
24
+ export interface K8sEndpointSubset {
25
+ ports?: K8sEndpointPort[];
26
+ addresses?: K8sEndpointAddress[];
27
+ notReadyAddresses?: K8sEndpointAddress[];
28
+ }
29
+ export interface K8sObjectMeta {
30
+ name?: string;
31
+ annotations?: Record<string, string>;
32
+ labels?: Record<string, string>;
33
+ creationTimestamp?: string;
34
+ }
35
+ export interface K8sEndpoints {
36
+ metadata?: K8sObjectMeta;
37
+ subsets?: K8sEndpointSubset[];
38
+ }
39
+ export interface K8sService {
40
+ metadata?: K8sObjectMeta;
41
+ }
42
+ export interface K8sApiResponse<T> {
43
+ body: T;
44
+ }
45
+ export interface CoreV1ApiLike {
46
+ readNamespacedEndpoints(name: string, namespace: string): Promise<K8sApiResponse<K8sEndpoints>>;
47
+ listNamespacedService(namespace: string, pretty?: string, allowWatchBookmarks?: boolean, _continue?: string, fieldSelector?: string, labelSelector?: string): Promise<K8sApiResponse<{
48
+ items: K8sService[];
49
+ }>>;
50
+ }
8
51
  export interface KubernetesBackendConfig {
9
52
  namespace?: string;
10
53
  labelSelector?: string;
@@ -56,10 +99,5 @@ export declare class KubernetesRegistryBackend implements RegistryBackend {
56
99
  * Create a ServiceInstance from Kubernetes endpoint data
57
100
  */
58
101
  private createServiceInstance;
59
- /**
60
- * Apply filter to instances
61
- */
62
- private applyFilter;
63
102
  }
64
- export {};
65
103
  //# sourceMappingURL=kubernetes-backend.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"kubernetes-backend.d.ts","sourceRoot":"","sources":["../../src/backends/kubernetes-backend.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAiB,MAAM,UAAU,CAAC;AAIzE,KAAK,UAAU,GAAG,GAAG,CAAC;AAItB,MAAM,WAAW,uBAAuB;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,yBAA0B,YAAW,eAAe;IAC/D,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;gBAE3B,UAAU,EAAE,UAAU,EAAE,MAAM,GAAE,uBAA4B;IASxE;;;;OAIG;IACG,QAAQ,CAAC,SAAS,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzD;;;OAGG;IACG,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD;;;OAGG;IACG,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKnD;;OAEG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA6D3F;;OAEG;IACG,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAQtE;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAmBzC;;;OAGG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvE;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAI9B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAsC7B;;OAEG;IACH,OAAO,CAAC,WAAW;CA2BpB"}
1
+ {"version":3,"file":"kubernetes-backend.d.ts","sourceRoot":"","sources":["../../src/backends/kubernetes-backend.ts"],"names":[],"mappings":"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"}
@@ -6,8 +6,12 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.KubernetesRegistryBackend = void 0;
8
8
  const types_1 = require("../types");
9
+ const filter_1 = require("../utils/filter");
10
+ const logger_1 = require("../utils/logger");
11
+ const validation_1 = require("../utils/validation");
9
12
  class KubernetesRegistryBackend {
10
13
  constructor(kubeConfig, config = {}) {
14
+ (0, validation_1.validateKubernetesBackendConfig)(config);
11
15
  // Import CoreV1Api dynamically to avoid build errors when @kubernetes/client-node is not installed
12
16
  // eslint-disable-next-line @typescript-eslint/no-require-imports
13
17
  const { CoreV1Api: CoreV1ApiClass } = require('@kubernetes/client-node');
@@ -44,6 +48,7 @@ class KubernetesRegistryBackend {
44
48
  * Get all instances of a service from Kubernetes Endpoints
45
49
  */
46
50
  async getInstances(serviceName, filter) {
51
+ const logger = logger_1.DiscoveryLogger.getLogger();
47
52
  try {
48
53
  // Get service endpoints
49
54
  const endpointsResponse = await this.k8sApi.readNamespacedEndpoints(serviceName, this.namespace);
@@ -73,12 +78,10 @@ class KubernetesRegistryBackend {
73
78
  }
74
79
  }
75
80
  // Apply filters
76
- if (filter) {
77
- return this.applyFilter(instances, filter);
78
- }
79
- return instances;
81
+ return (0, filter_1.applyServiceFilter)(instances, filter);
80
82
  }
81
- catch {
83
+ catch (error) {
84
+ logger.error(`Failed to get instances for service "${serviceName}" from Kubernetes`, error);
82
85
  return [];
83
86
  }
84
87
  }
@@ -95,11 +98,13 @@ class KubernetesRegistryBackend {
95
98
  * Get all registered services in the namespace
96
99
  */
97
100
  async getAllServices() {
101
+ const logger = logger_1.DiscoveryLogger.getLogger();
98
102
  try {
99
103
  const servicesResponse = await this.k8sApi.listNamespacedService(this.namespace, undefined, undefined, undefined, undefined, this.labelSelector);
100
104
  return servicesResponse.body.items.map((service) => service.metadata?.name || '');
101
105
  }
102
- catch {
106
+ catch (error) {
107
+ logger.error('Failed to list services from Kubernetes', error);
103
108
  return [];
104
109
  }
105
110
  }
@@ -144,31 +149,5 @@ class KubernetesRegistryBackend {
144
149
  registeredAt: new Date(metadata?.creationTimestamp || Date.now()),
145
150
  };
146
151
  }
147
- /**
148
- * Apply filter to instances
149
- */
150
- applyFilter(instances, filter) {
151
- return instances.filter((instance) => {
152
- if (filter.zone && instance.zone !== filter.zone) {
153
- return false;
154
- }
155
- if (filter.status && instance.status !== filter.status) {
156
- return false;
157
- }
158
- if (filter.tags && filter.tags.length > 0) {
159
- if (!instance.tags || !filter.tags.every((tag) => instance.tags.includes(tag))) {
160
- return false;
161
- }
162
- }
163
- if (filter.metadata) {
164
- for (const [key, value] of Object.entries(filter.metadata)) {
165
- if (!instance.metadata || instance.metadata[key] !== value) {
166
- return false;
167
- }
168
- }
169
- }
170
- return true;
171
- });
172
- }
173
152
  }
174
153
  exports.KubernetesRegistryBackend = KubernetesRegistryBackend;
@@ -17,6 +17,5 @@ export declare class MemoryRegistryBackend implements RegistryBackend {
17
17
  getAllServices(): Promise<string[]>;
18
18
  updateStatus(instanceId: string, status: string): Promise<void>;
19
19
  cleanup(): Promise<void>;
20
- private applyFilter;
21
20
  }
22
21
  //# sourceMappingURL=memory-backend.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory-backend.d.ts","sourceRoot":"","sources":["../../src/backends/memory-backend.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAiB,MAAM,UAAU,CAAC;AAEzE,qBAAa,qBAAsB,YAAW,eAAe;IAC3D,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,cAAc,SAAQ;IAK5B,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAUlD,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB7C,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ5C,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAoBrF,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAIhE,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAInC,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO/D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB9B,OAAO,CAAC,WAAW;CA+BpB"}
1
+ {"version":3,"file":"memory-backend.d.ts","sourceRoot":"","sources":["../../src/backends/memory-backend.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAiB,MAAM,UAAU,CAAC;AAGzE,qBAAa,qBAAsB,YAAW,eAAe;IAC3D,OAAO,CAAC,SAAS,CAAsC;IACvD,OAAO,CAAC,YAAY,CAAkC;IACtD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,cAAc,SAAQ;IAK5B,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAUlD,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB7C,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ5C,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IAgBrF,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAIhE,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAInC,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO/D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAe/B"}
@@ -6,6 +6,7 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.MemoryRegistryBackend = void 0;
8
8
  const types_1 = require("../types");
9
+ const filter_1 = require("../utils/filter");
9
10
  class MemoryRegistryBackend {
10
11
  constructor(expirationTime = 90000) {
11
12
  this.instances = new Map();
@@ -46,7 +47,7 @@ class MemoryRegistryBackend {
46
47
  const instanceIds = this.serviceIndex.get(serviceName);
47
48
  if (!instanceIds)
48
49
  return [];
49
- let instances = [];
50
+ const instances = [];
50
51
  for (const id of instanceIds) {
51
52
  const instance = this.instances.get(id);
52
53
  if (instance) {
@@ -54,10 +55,7 @@ class MemoryRegistryBackend {
54
55
  }
55
56
  }
56
57
  // Apply filters
57
- if (filter) {
58
- instances = this.applyFilter(instances, filter);
59
- }
60
- return instances;
58
+ return (0, filter_1.applyServiceFilter)(instances, filter);
61
59
  }
62
60
  async getInstance(instanceId) {
63
61
  return this.instances.get(instanceId) || null;
@@ -84,32 +82,5 @@ class MemoryRegistryBackend {
84
82
  await this.deregister(id);
85
83
  }
86
84
  }
87
- applyFilter(instances, filter) {
88
- return instances.filter((instance) => {
89
- // Filter by zone
90
- if (filter.zone && instance.zone !== filter.zone) {
91
- return false;
92
- }
93
- // Filter by status
94
- if (filter.status && instance.status !== filter.status) {
95
- return false;
96
- }
97
- // Filter by tags
98
- if (filter.tags && filter.tags.length > 0) {
99
- if (!instance.tags || !filter.tags.every((tag) => instance.tags.includes(tag))) {
100
- return false;
101
- }
102
- }
103
- // Filter by metadata
104
- if (filter.metadata) {
105
- for (const [key, value] of Object.entries(filter.metadata)) {
106
- if (!instance.metadata || instance.metadata[key] !== value) {
107
- return false;
108
- }
109
- }
110
- }
111
- return true;
112
- });
113
- }
114
85
  }
115
86
  exports.MemoryRegistryBackend = MemoryRegistryBackend;
@@ -17,7 +17,16 @@ export declare class RedisRegistryBackend implements RegistryBackend {
17
17
  private redis;
18
18
  private readonly keyPrefix;
19
19
  private readonly ttl;
20
+ private connected;
20
21
  constructor(redis: Redis, config?: RedisBackendConfig);
22
+ /**
23
+ * Set up Redis connection event handlers for resilience
24
+ */
25
+ private setupConnectionHandlers;
26
+ /**
27
+ * Check Redis connectivity before operations
28
+ */
29
+ private ensureConnected;
21
30
  /**
22
31
  * Register a service instance
23
32
  */
@@ -31,7 +40,7 @@ export declare class RedisRegistryBackend implements RegistryBackend {
31
40
  */
32
41
  heartbeat(instanceId: string): Promise<void>;
33
42
  /**
34
- * Get all instances of a service
43
+ * Get all instances of a service (uses MGET for efficiency)
35
44
  */
36
45
  getInstances(serviceName: string, filter?: ServiceFilter): Promise<ServiceInstance[]>;
37
46
  /**
@@ -39,7 +48,7 @@ export declare class RedisRegistryBackend implements RegistryBackend {
39
48
  */
40
49
  getInstance(instanceId: string): Promise<ServiceInstance | null>;
41
50
  /**
42
- * Get all registered services
51
+ * Get all registered services using SCAN (safe for production)
43
52
  */
44
53
  getAllServices(): Promise<string[]>;
45
54
  /**
@@ -63,9 +72,5 @@ export declare class RedisRegistryBackend implements RegistryBackend {
63
72
  * Get Redis key for service set
64
73
  */
65
74
  private getServiceSetKey;
66
- /**
67
- * Apply filter to instances
68
- */
69
- private applyFilter;
70
75
  }
71
76
  //# sourceMappingURL=redis-backend.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"redis-backend.d.ts","sourceRoot":"","sources":["../../src/backends/redis-backend.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAiB,MAAM,UAAU,CAAC;AACzE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,oBAAqB,YAAW,eAAe;IAC1D,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;gBAEjB,KAAK,EAAE,KAAK,EAAE,MAAM,GAAE,kBAAuB;IAMzD;;OAEG;IACG,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAcxD;;OAEG;IACG,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBnD;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAqBlD;;OAEG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA2B3F;;OAEG;IACG,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAiBtE;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAUzC;;OAEG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYrE;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B9B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAIxB;;OAEG;IACH,OAAO,CAAC,WAAW;CA+BpB"}
1
+ {"version":3,"file":"redis-backend.d.ts","sourceRoot":"","sources":["../../src/backends/redis-backend.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAiB,MAAM,UAAU,CAAC;AAIzE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAErC,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,oBAAqB,YAAW,eAAe;IAC1D,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,SAAS,CAAQ;gBAEb,KAAK,EAAE,KAAK,EAAE,MAAM,GAAE,kBAAuB;IAUzD;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAuB/B;;OAEG;IACH,OAAO,CAAC,eAAe;IAMvB;;OAEG;IACG,QAAQ,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBxD;;OAEG;IACG,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBnD;;OAEG;IACG,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBlD;;OAEG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA8B3F;;OAEG;IACG,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAmBtE;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAoBzC;;OAEG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAcrE;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA6B9B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAGzB"}
@@ -6,16 +6,52 @@
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.RedisRegistryBackend = void 0;
8
8
  const types_1 = require("../types");
9
+ const filter_1 = require("../utils/filter");
10
+ const logger_1 = require("../utils/logger");
11
+ const validation_1 = require("../utils/validation");
9
12
  class RedisRegistryBackend {
10
13
  constructor(redis, config = {}) {
14
+ this.connected = true;
15
+ (0, validation_1.validateRedisBackendConfig)(config);
11
16
  this.redis = redis;
12
17
  this.keyPrefix = config.keyPrefix || 'hazeljs:discovery:';
13
18
  this.ttl = config.ttl || 90; // 90 seconds default
19
+ this.setupConnectionHandlers();
20
+ }
21
+ /**
22
+ * Set up Redis connection event handlers for resilience
23
+ */
24
+ setupConnectionHandlers() {
25
+ const logger = logger_1.DiscoveryLogger.getLogger();
26
+ this.redis.on('error', (err) => {
27
+ this.connected = false;
28
+ logger.error('Redis connection error', err);
29
+ });
30
+ this.redis.on('connect', () => {
31
+ this.connected = true;
32
+ logger.info('Redis connected');
33
+ });
34
+ this.redis.on('reconnecting', () => {
35
+ logger.warn('Redis reconnecting...');
36
+ });
37
+ this.redis.on('close', () => {
38
+ this.connected = false;
39
+ logger.warn('Redis connection closed');
40
+ });
41
+ }
42
+ /**
43
+ * Check Redis connectivity before operations
44
+ */
45
+ ensureConnected() {
46
+ if (!this.connected) {
47
+ throw new Error('Redis backend is not connected');
48
+ }
14
49
  }
15
50
  /**
16
51
  * Register a service instance
17
52
  */
18
53
  async register(instance) {
54
+ this.ensureConnected();
19
55
  const key = this.getInstanceKey(instance.id);
20
56
  const serviceSetKey = this.getServiceSetKey(instance.name);
21
57
  // Store instance data
@@ -29,6 +65,7 @@ class RedisRegistryBackend {
29
65
  * Deregister a service instance
30
66
  */
31
67
  async deregister(instanceId) {
68
+ this.ensureConnected();
32
69
  const key = this.getInstanceKey(instanceId);
33
70
  // Get instance to find service name
34
71
  const data = await this.redis.get(key);
@@ -45,6 +82,7 @@ class RedisRegistryBackend {
45
82
  * Update service instance heartbeat
46
83
  */
47
84
  async heartbeat(instanceId) {
85
+ this.ensureConnected();
48
86
  const key = this.getInstanceKey(instanceId);
49
87
  // Get current instance
50
88
  const data = await this.redis.get(key);
@@ -61,33 +99,36 @@ class RedisRegistryBackend {
61
99
  }
62
100
  }
63
101
  /**
64
- * Get all instances of a service
102
+ * Get all instances of a service (uses MGET for efficiency)
65
103
  */
66
104
  async getInstances(serviceName, filter) {
105
+ this.ensureConnected();
67
106
  const serviceSetKey = this.getServiceSetKey(serviceName);
68
107
  // Get all instance IDs for this service
69
108
  const instanceIds = await this.redis.smembers(serviceSetKey);
70
109
  if (instanceIds.length === 0) {
71
110
  return [];
72
111
  }
73
- // Get all instance data
112
+ // Batch-fetch all instances with MGET
113
+ const keys = instanceIds.map((id) => this.getInstanceKey(id));
114
+ const results = await this.redis.mget(...keys);
74
115
  const instances = [];
75
- for (const id of instanceIds) {
76
- const instance = await this.getInstance(id);
77
- if (instance) {
116
+ for (const data of results) {
117
+ if (data) {
118
+ const instance = JSON.parse(data);
119
+ instance.lastHeartbeat = new Date(instance.lastHeartbeat);
120
+ instance.registeredAt = new Date(instance.registeredAt);
78
121
  instances.push(instance);
79
122
  }
80
123
  }
81
124
  // Apply filters
82
- if (filter) {
83
- return this.applyFilter(instances, filter);
84
- }
85
- return instances;
125
+ return (0, filter_1.applyServiceFilter)(instances, filter);
86
126
  }
87
127
  /**
88
128
  * Get a specific service instance
89
129
  */
90
130
  async getInstance(instanceId) {
131
+ this.ensureConnected();
91
132
  const key = this.getInstanceKey(instanceId);
92
133
  const data = await this.redis.get(key);
93
134
  if (!data) {
@@ -100,20 +141,28 @@ class RedisRegistryBackend {
100
141
  return instance;
101
142
  }
102
143
  /**
103
- * Get all registered services
144
+ * Get all registered services using SCAN (safe for production)
104
145
  */
105
146
  async getAllServices() {
147
+ this.ensureConnected();
106
148
  const pattern = `${this.keyPrefix}service:*`;
107
- const keys = await this.redis.keys(pattern);
108
- return keys.map((key) => {
109
- const serviceName = key.replace(`${this.keyPrefix}service:`, '');
110
- return serviceName;
111
- });
149
+ const prefixLen = `${this.keyPrefix}service:`.length;
150
+ const services = [];
151
+ let cursor = '0';
152
+ do {
153
+ const [nextCursor, keys] = await this.redis.scan(cursor, 'MATCH', pattern, 'COUNT', 100);
154
+ cursor = nextCursor;
155
+ for (const key of keys) {
156
+ services.push(key.substring(prefixLen));
157
+ }
158
+ } while (cursor !== '0');
159
+ return services;
112
160
  }
113
161
  /**
114
162
  * Update service instance status
115
163
  */
116
164
  async updateStatus(instanceId, status) {
165
+ this.ensureConnected();
117
166
  const key = this.getInstanceKey(instanceId);
118
167
  const data = await this.redis.get(key);
119
168
  if (data) {
@@ -127,8 +176,9 @@ class RedisRegistryBackend {
127
176
  * Note: Redis handles expiration automatically via TTL
128
177
  */
129
178
  async cleanup() {
179
+ this.ensureConnected();
130
180
  // Redis automatically removes expired keys
131
- // This method can be used for additional cleanup if needed
181
+ // This method cleans up stale entries in service sets
132
182
  const services = await this.getAllServices();
133
183
  for (const serviceName of services) {
134
184
  const serviceSetKey = this.getServiceSetKey(serviceName);
@@ -166,35 +216,5 @@ class RedisRegistryBackend {
166
216
  getServiceSetKey(serviceName) {
167
217
  return `${this.keyPrefix}service:${serviceName}`;
168
218
  }
169
- /**
170
- * Apply filter to instances
171
- */
172
- applyFilter(instances, filter) {
173
- return instances.filter((instance) => {
174
- // Filter by zone
175
- if (filter.zone && instance.zone !== filter.zone) {
176
- return false;
177
- }
178
- // Filter by status
179
- if (filter.status && instance.status !== filter.status) {
180
- return false;
181
- }
182
- // Filter by tags
183
- if (filter.tags && filter.tags.length > 0) {
184
- if (!instance.tags || !filter.tags.every((tag) => instance.tags.includes(tag))) {
185
- return false;
186
- }
187
- }
188
- // Filter by metadata
189
- if (filter.metadata) {
190
- for (const [key, value] of Object.entries(filter.metadata)) {
191
- if (!instance.metadata || instance.metadata[key] !== value) {
192
- return false;
193
- }
194
- }
195
- }
196
- return true;
197
- });
198
- }
199
219
  }
200
220
  exports.RedisRegistryBackend = RedisRegistryBackend;
@@ -10,6 +10,7 @@ export declare class DiscoveryClient {
10
10
  private backend;
11
11
  private cache;
12
12
  private loadBalancerFactory;
13
+ private refreshIntervalHandle;
13
14
  constructor(config?: DiscoveryClientConfig, backend?: RegistryBackend);
14
15
  /**
15
16
  * Get all instances of a service
@@ -36,12 +37,13 @@ export declare class DiscoveryClient {
36
37
  */
37
38
  getLoadBalancerFactory(): LoadBalancerFactory;
38
39
  /**
39
- * Start cache refresh interval
40
+ * Close the discovery client and release all resources.
41
+ * Stops the cache refresh interval and clears the cache.
40
42
  */
41
- private startRefreshInterval;
43
+ close(): void;
42
44
  /**
43
- * Apply filter to instances
45
+ * Start cache refresh interval
44
46
  */
45
- private applyFilter;
47
+ private startRefreshInterval;
46
48
  }
47
49
  //# sourceMappingURL=discovery-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"discovery-client.d.ts","sourceRoot":"","sources":["../../src/client/discovery-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,qBAAa,eAAe;IAMxB,OAAO,CAAC,MAAM;IALhB,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,KAAK,CAA0E;IACvF,OAAO,CAAC,mBAAmB,CAAsB;gBAGvC,MAAM,GAAE,qBAA0B,EAC1C,OAAO,CAAC,EAAE,eAAe;IAU3B;;OAEG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA2B3F;;OAEG;IACG,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,QAAQ,GAAE,MAAsB,EAChC,MAAM,CAAC,EAAE,aAAa,GACrB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAQlC;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIzC;;OAEG;IACH,UAAU,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAQtC;;OAEG;IACH,UAAU,IAAI,eAAe;IAI7B;;OAEG;IACH,sBAAsB,IAAI,mBAAmB;IAI7C;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAa5B;;OAEG;IACH,OAAO,CAAC,WAAW;CAepB"}
1
+ {"version":3,"file":"discovery-client.d.ts","sourceRoot":"","sources":["../../src/client/discovery-client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACjF,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAKlE,qBAAa,eAAe;IAOxB,OAAO,CAAC,MAAM;IANhB,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,KAAK,CAA0E;IACvF,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,qBAAqB,CAA+B;gBAGlD,MAAM,GAAE,qBAA0B,EAC1C,OAAO,CAAC,EAAE,eAAe;IAY3B;;OAEG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IA2B3F;;OAEG;IACG,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,QAAQ,GAAE,MAAsB,EAChC,MAAM,CAAC,EAAE,aAAa,GACrB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAQlC;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIzC;;OAEG;IACH,UAAU,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI;IAQtC;;OAEG;IACH,UAAU,IAAI,eAAe;IAI7B;;OAEG;IACH,sBAAsB,IAAI,mBAAmB;IAI7C;;;OAGG;IACH,KAAK,IAAI,IAAI;IAQb;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAkB7B"}