@fjell/registry 4.4.4 → 4.4.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.
- package/README.md +546 -0
- package/dist/Coordinate.cjs +8 -5
- package/dist/Coordinate.d.ts +1 -1
- package/dist/Coordinate.js +8 -5
- package/dist/Instance.cjs +4 -4
- package/dist/Instance.d.ts +3 -4
- package/dist/Instance.js +4 -4
- package/dist/Registry.cjs +99 -90
- package/dist/Registry.d.ts +3 -42
- package/dist/Registry.js +99 -90
- package/dist/RegistryHub.cjs +78 -0
- package/dist/RegistryHub.d.ts +3 -0
- package/dist/RegistryHub.js +74 -0
- package/dist/errors/CoordinateError.cjs +70 -0
- package/dist/errors/CoordinateError.d.ts +28 -0
- package/dist/errors/CoordinateError.js +63 -0
- package/dist/errors/InstanceError.cjs +101 -0
- package/dist/errors/InstanceError.d.ts +42 -0
- package/dist/errors/InstanceError.js +92 -0
- package/dist/errors/RegistryError.cjs +82 -0
- package/dist/errors/RegistryError.d.ts +31 -0
- package/dist/errors/RegistryError.js +75 -0
- package/dist/errors/RegistryHubError.cjs +92 -0
- package/dist/errors/RegistryHubError.d.ts +39 -0
- package/dist/errors/RegistryHubError.js +84 -0
- package/dist/errors/index.d.ts +4 -0
- package/dist/index.cjs +501 -112
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +6 -2
- package/dist/types.d.ts +90 -0
- package/docs/TIMING_NODE_OPTIMIZATION.md +207 -0
- package/docs/TIMING_README.md +170 -0
- package/docs/memory-data/scaling-10-instances.json +526 -0
- package/docs/memory-data/scaling-100-instances.json +526 -0
- package/docs/memory-data/scaling-1000-instances.json +276 -0
- package/docs/memory-data/scaling-10000-instances.json +126 -0
- package/docs/memory-data/scaling-20-instances.json +526 -0
- package/docs/memory-data/scaling-200-instances.json +526 -0
- package/docs/memory-data/scaling-2000-instances.json +276 -0
- package/docs/memory-data/scaling-50-instances.json +526 -0
- package/docs/memory-data/scaling-500-instances.json +276 -0
- package/docs/memory-data/scaling-5000-instances.json +126 -0
- package/docs/memory-overhead.svg +120 -0
- package/docs/memory.md +430 -0
- package/docs/timing-range.svg +174 -0
- package/docs/timing.md +483 -0
- package/examples/README.md +187 -0
- package/examples/multi-level-keys.ts +374 -0
- package/examples/registry-hub-types.ts +437 -0
- package/examples/simple-example.ts +250 -0
- package/package.json +5 -3
- package/dist/Definition.cjs +0 -18
- package/dist/Definition.d.ts +0 -5
- package/dist/Definition.js +0 -14
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* RegistryHub with Different Service Types Example
|
|
5
|
+
*
|
|
6
|
+
* This example demonstrates how to use a RegistryHub to organize and retrieve
|
|
7
|
+
* services of different types. Perfect for large applications that need to
|
|
8
|
+
* categorize services by their purpose (data access, business logic, infrastructure, etc.)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createRegistryHub } from '../src/RegistryHub';
|
|
12
|
+
import { createRegistry } from '../src/Registry';
|
|
13
|
+
import { createInstance } from '../src/Instance';
|
|
14
|
+
|
|
15
|
+
// ===================================================================
|
|
16
|
+
// Service Interfaces - Different types of services
|
|
17
|
+
// ===================================================================
|
|
18
|
+
|
|
19
|
+
interface AuthService {
|
|
20
|
+
login(username: string, password: string): Promise<string>;
|
|
21
|
+
logout(token: string): Promise<void>;
|
|
22
|
+
validateToken(token: string): boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface UserRepository {
|
|
26
|
+
findById(id: string): Promise<any>;
|
|
27
|
+
save(user: any): Promise<void>;
|
|
28
|
+
delete(id: string): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface CacheService {
|
|
32
|
+
get(key: string): Promise<any>;
|
|
33
|
+
set(key: string, value: any, ttl?: number): Promise<void>;
|
|
34
|
+
invalidate(key: string): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface EmailService {
|
|
38
|
+
sendEmail(to: string, subject: string, body: string): Promise<boolean>;
|
|
39
|
+
sendTemplate(template: string, data: any): Promise<boolean>;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
interface LoggerService {
|
|
43
|
+
info(message: string): void;
|
|
44
|
+
error(message: string, error?: Error): void;
|
|
45
|
+
debug(message: string): void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ===================================================================
|
|
49
|
+
// Service Implementations - Multiple implementations per type
|
|
50
|
+
// ===================================================================
|
|
51
|
+
|
|
52
|
+
class JwtAuthService implements AuthService {
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
54
|
+
async login(username: string, password: string): Promise<string> {
|
|
55
|
+
console.log(`🔐 JWT Auth: Logging in ${username}`);
|
|
56
|
+
return `jwt-token-${username}-${Date.now()}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async logout(token: string): Promise<void> {
|
|
60
|
+
console.log(`🔐 JWT Auth: Logging out token ${token.substring(0, 10)}...`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
validateToken(token: string): boolean {
|
|
64
|
+
console.log(`🔐 JWT Auth: Validating token ${token.substring(0, 10)}...`);
|
|
65
|
+
return token.startsWith('jwt-token-');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
class OAuth2AuthService implements AuthService {
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
71
|
+
async login(username: string, password: string): Promise<string> {
|
|
72
|
+
console.log(`🔒 OAuth2 Auth: Logging in ${username}`);
|
|
73
|
+
return `oauth2-token-${username}-${Date.now()}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async logout(token: string): Promise<void> {
|
|
77
|
+
console.log(`🔒 OAuth2 Auth: Logging out token ${token.substring(0, 10)}...`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
validateToken(token: string): boolean {
|
|
81
|
+
console.log(`🔒 OAuth2 Auth: Validating token ${token.substring(0, 10)}...`);
|
|
82
|
+
return token.startsWith('oauth2-token-');
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
class PostgreSqlUserRepository implements UserRepository {
|
|
87
|
+
async findById(id: string): Promise<any> {
|
|
88
|
+
console.log(`🐘 PostgreSQL: Finding user ${id}`);
|
|
89
|
+
return { id, name: `User ${id}`, source: 'postgresql' };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async save(user: any): Promise<void> {
|
|
93
|
+
console.log(`🐘 PostgreSQL: Saving user ${user.id}`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async delete(id: string): Promise<void> {
|
|
97
|
+
console.log(`🐘 PostgreSQL: Deleting user ${id}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
class MongoUserRepository implements UserRepository {
|
|
102
|
+
async findById(id: string): Promise<any> {
|
|
103
|
+
console.log(`🍃 MongoDB: Finding user ${id}`);
|
|
104
|
+
return { id, name: `User ${id}`, source: 'mongodb' };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async save(user: any): Promise<void> {
|
|
108
|
+
console.log(`🍃 MongoDB: Saving user ${user.id}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async delete(id: string): Promise<void> {
|
|
112
|
+
console.log(`🍃 MongoDB: Deleting user ${id}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
class RedisCacheService implements CacheService {
|
|
117
|
+
async get(key: string): Promise<any> {
|
|
118
|
+
console.log(`🔴 Redis Cache: Getting ${key}`);
|
|
119
|
+
return `cached-value-${key}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async set(key: string, value: any, ttl?: number): Promise<void> {
|
|
123
|
+
console.log(`🔴 Redis Cache: Setting ${key} (TTL: ${ttl || 'none'})`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async invalidate(key: string): Promise<void> {
|
|
127
|
+
console.log(`🔴 Redis Cache: Invalidating ${key}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
class MemoryCacheService implements CacheService {
|
|
132
|
+
private cache = new Map<string, any>();
|
|
133
|
+
|
|
134
|
+
async get(key: string): Promise<any> {
|
|
135
|
+
console.log(`💾 Memory Cache: Getting ${key}`);
|
|
136
|
+
return this.cache.get(key) || null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async set(key: string, value: any, ttl?: number): Promise<void> {
|
|
140
|
+
console.log(`💾 Memory Cache: Setting ${key} (TTL: ${ttl || 'none'})`);
|
|
141
|
+
this.cache.set(key, value);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async invalidate(key: string): Promise<void> {
|
|
145
|
+
console.log(`💾 Memory Cache: Invalidating ${key}`);
|
|
146
|
+
this.cache.delete(key);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
class SmtpEmailService implements EmailService {
|
|
151
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
152
|
+
async sendEmail(to: string, subject: string, body: string): Promise<boolean> {
|
|
153
|
+
console.log(`📧 SMTP Email: Sending to ${to} - ${subject}`);
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async sendTemplate(template: string, data: any): Promise<boolean> {
|
|
158
|
+
console.log(`📧 SMTP Email: Sending template ${template} with data:`, data);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
class SendGridEmailService implements EmailService {
|
|
164
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
165
|
+
async sendEmail(to: string, subject: string, body: string): Promise<boolean> {
|
|
166
|
+
console.log(`📨 SendGrid Email: Sending to ${to} - ${subject}`);
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async sendTemplate(template: string, data: any): Promise<boolean> {
|
|
171
|
+
console.log(`📨 SendGrid Email: Sending template ${template} with data:`, data);
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
class ConsoleLoggerService implements LoggerService {
|
|
177
|
+
info(message: string): void {
|
|
178
|
+
console.log(`ℹ️ INFO: ${message}`);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
error(message: string, error?: Error): void {
|
|
182
|
+
console.log(`❌ ERROR: ${message}`, error);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
debug(message: string): void {
|
|
186
|
+
console.log(`🐛 DEBUG: ${message}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
class FileLoggerService implements LoggerService {
|
|
191
|
+
info(message: string): void {
|
|
192
|
+
console.log(`📝 FILE INFO: ${message}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
error(message: string, error?: Error): void {
|
|
196
|
+
console.log(`📝 FILE ERROR: ${message}`, error);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
debug(message: string): void {
|
|
200
|
+
console.log(`📝 FILE DEBUG: ${message}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ===================================================================
|
|
205
|
+
// Main Example
|
|
206
|
+
// ===================================================================
|
|
207
|
+
|
|
208
|
+
async function demonstrateRegistryHub() {
|
|
209
|
+
console.log('🚀 Registry Hub with Different Service Types Example\n');
|
|
210
|
+
|
|
211
|
+
// ================================================================
|
|
212
|
+
// Step 1: Create the RegistryHub
|
|
213
|
+
// ================================================================
|
|
214
|
+
console.log('1️⃣ Creating RegistryHub...');
|
|
215
|
+
const hub = createRegistryHub();
|
|
216
|
+
|
|
217
|
+
// ================================================================
|
|
218
|
+
// Step 2: Create different registries for different service types
|
|
219
|
+
// ================================================================
|
|
220
|
+
console.log('\n2️⃣ Creating specialized registries...');
|
|
221
|
+
|
|
222
|
+
// Business logic services
|
|
223
|
+
const servicesRegistry = createRegistry('services');
|
|
224
|
+
hub.registerRegistry(servicesRegistry);
|
|
225
|
+
|
|
226
|
+
// Data access layer
|
|
227
|
+
const dataRegistry = createRegistry('data');
|
|
228
|
+
hub.registerRegistry(dataRegistry);
|
|
229
|
+
|
|
230
|
+
// Infrastructure services
|
|
231
|
+
const infraRegistry = createRegistry('infrastructure');
|
|
232
|
+
hub.registerRegistry(infraRegistry);
|
|
233
|
+
|
|
234
|
+
// Communication services
|
|
235
|
+
const communicationRegistry = createRegistry('communication');
|
|
236
|
+
hub.registerRegistry(communicationRegistry);
|
|
237
|
+
|
|
238
|
+
console.log('✅ Created registries:', hub.getRegisteredTypes().join(', '));
|
|
239
|
+
|
|
240
|
+
// ================================================================
|
|
241
|
+
// Step 3: Register services in their appropriate registries
|
|
242
|
+
// ================================================================
|
|
243
|
+
console.log('\n3️⃣ Registering services by type and scope...');
|
|
244
|
+
|
|
245
|
+
// SERVICES REGISTRY - Business logic with different implementations by environment
|
|
246
|
+
servicesRegistry.createInstance(['auth'], ['prod'], (coordinate, context) => {
|
|
247
|
+
const service = new JwtAuthService();
|
|
248
|
+
const instance = createInstance(context.registry, coordinate);
|
|
249
|
+
(instance as any).login = service.login.bind(service);
|
|
250
|
+
(instance as any).logout = service.logout.bind(service);
|
|
251
|
+
(instance as any).validateToken = service.validateToken.bind(service);
|
|
252
|
+
return instance;
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
servicesRegistry.createInstance(['auth'], ['dev'], (coordinate, context) => {
|
|
256
|
+
const service = new OAuth2AuthService();
|
|
257
|
+
const instance = createInstance(context.registry, coordinate);
|
|
258
|
+
(instance as any).login = service.login.bind(service);
|
|
259
|
+
(instance as any).logout = service.logout.bind(service);
|
|
260
|
+
(instance as any).validateToken = service.validateToken.bind(service);
|
|
261
|
+
return instance;
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// DATA REGISTRY - Data access with different backends
|
|
265
|
+
dataRegistry.createInstance(['user'], ['sql'], (coordinate, context) => {
|
|
266
|
+
const service = new PostgreSqlUserRepository();
|
|
267
|
+
const instance = createInstance(context.registry, coordinate);
|
|
268
|
+
(instance as any).findById = service.findById.bind(service);
|
|
269
|
+
(instance as any).save = service.save.bind(service);
|
|
270
|
+
(instance as any).delete = service.delete.bind(service);
|
|
271
|
+
return instance;
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
dataRegistry.createInstance(['user'], ['nosql'], (coordinate, context) => {
|
|
275
|
+
const service = new MongoUserRepository();
|
|
276
|
+
const instance = createInstance(context.registry, coordinate);
|
|
277
|
+
(instance as any).findById = service.findById.bind(service);
|
|
278
|
+
(instance as any).save = service.save.bind(service);
|
|
279
|
+
(instance as any).delete = service.delete.bind(service);
|
|
280
|
+
return instance;
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
// INFRASTRUCTURE REGISTRY - Infrastructure services
|
|
284
|
+
infraRegistry.createInstance(['cache'], ['prod'], (coordinate, context) => {
|
|
285
|
+
const service = new RedisCacheService();
|
|
286
|
+
const instance = createInstance(context.registry, coordinate);
|
|
287
|
+
(instance as any).get = service.get.bind(service);
|
|
288
|
+
(instance as any).set = service.set.bind(service);
|
|
289
|
+
(instance as any).invalidate = service.invalidate.bind(service);
|
|
290
|
+
return instance;
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
infraRegistry.createInstance(['cache'], ['dev'], (coordinate, context) => {
|
|
294
|
+
const service = new MemoryCacheService();
|
|
295
|
+
const instance = createInstance(context.registry, coordinate);
|
|
296
|
+
(instance as any).get = service.get.bind(service);
|
|
297
|
+
(instance as any).set = service.set.bind(service);
|
|
298
|
+
(instance as any).invalidate = service.invalidate.bind(service);
|
|
299
|
+
return instance;
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
infraRegistry.createInstance(['logger'], ['prod'], (coordinate, context) => {
|
|
303
|
+
const service = new FileLoggerService();
|
|
304
|
+
const instance = createInstance(context.registry, coordinate);
|
|
305
|
+
(instance as any).info = service.info.bind(service);
|
|
306
|
+
(instance as any).error = service.error.bind(service);
|
|
307
|
+
(instance as any).debug = service.debug.bind(service);
|
|
308
|
+
return instance;
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
infraRegistry.createInstance(['logger'], ['dev'], (coordinate, context) => {
|
|
312
|
+
const service = new ConsoleLoggerService();
|
|
313
|
+
const instance = createInstance(context.registry, coordinate);
|
|
314
|
+
(instance as any).info = service.info.bind(service);
|
|
315
|
+
(instance as any).error = service.error.bind(service);
|
|
316
|
+
(instance as any).debug = service.debug.bind(service);
|
|
317
|
+
return instance;
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// COMMUNICATION REGISTRY - External communication services
|
|
321
|
+
communicationRegistry.createInstance(['email'], ['prod'], (coordinate, context) => {
|
|
322
|
+
const service = new SendGridEmailService();
|
|
323
|
+
const instance = createInstance(context.registry, coordinate);
|
|
324
|
+
(instance as any).sendEmail = service.sendEmail.bind(service);
|
|
325
|
+
(instance as any).sendTemplate = service.sendTemplate.bind(service);
|
|
326
|
+
return instance;
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
communicationRegistry.createInstance(['email'], ['dev'], (coordinate, context) => {
|
|
330
|
+
const service = new SmtpEmailService();
|
|
331
|
+
const instance = createInstance(context.registry, coordinate);
|
|
332
|
+
(instance as any).sendEmail = service.sendEmail.bind(service);
|
|
333
|
+
(instance as any).sendTemplate = service.sendTemplate.bind(service);
|
|
334
|
+
return instance;
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
console.log('✅ All services registered successfully');
|
|
338
|
+
|
|
339
|
+
// ================================================================
|
|
340
|
+
// Step 4: Retrieve services by type and demonstrate usage
|
|
341
|
+
// ================================================================
|
|
342
|
+
console.log('\n4️⃣ Retrieving services by type...\n');
|
|
343
|
+
|
|
344
|
+
// Production environment setup
|
|
345
|
+
console.log('🏭 PRODUCTION ENVIRONMENT:');
|
|
346
|
+
console.log('─'.repeat(50));
|
|
347
|
+
|
|
348
|
+
const prodAuth = hub.get('services', ['auth'], { scopes: ['prod'] }) as any;
|
|
349
|
+
const sqlUserRepo = hub.get('data', ['user'], { scopes: ['sql'] }) as any;
|
|
350
|
+
const prodCache = hub.get('infrastructure', ['cache'], { scopes: ['prod'] }) as any;
|
|
351
|
+
const prodLogger = hub.get('infrastructure', ['logger'], { scopes: ['prod'] }) as any;
|
|
352
|
+
const prodEmail = hub.get('communication', ['email'], { scopes: ['prod'] }) as any;
|
|
353
|
+
|
|
354
|
+
// Demonstrate production workflow
|
|
355
|
+
const token = await prodAuth.login('john_doe', 'password123');
|
|
356
|
+
console.log(`✅ Production token received: ${token.substring(0, 20)}...`);
|
|
357
|
+
const user = await sqlUserRepo.findById('user123');
|
|
358
|
+
await prodCache.set('user:user123', user, 3600);
|
|
359
|
+
prodLogger.info('User logged in successfully');
|
|
360
|
+
await prodEmail.sendEmail('john@example.com', 'Welcome!', 'Welcome to our service!');
|
|
361
|
+
|
|
362
|
+
console.log('\n🧪 DEVELOPMENT ENVIRONMENT:');
|
|
363
|
+
console.log('─'.repeat(50));
|
|
364
|
+
|
|
365
|
+
const devAuth = hub.get('services', ['auth'], { scopes: ['dev'] }) as any;
|
|
366
|
+
const mongoUserRepo = hub.get('data', ['user'], { scopes: ['nosql'] }) as any;
|
|
367
|
+
const devCache = hub.get('infrastructure', ['cache'], { scopes: ['dev'] }) as any;
|
|
368
|
+
const devLogger = hub.get('infrastructure', ['logger'], { scopes: ['dev'] }) as any;
|
|
369
|
+
const devEmail = hub.get('communication', ['email'], { scopes: ['dev'] }) as any;
|
|
370
|
+
|
|
371
|
+
// Demonstrate development workflow
|
|
372
|
+
const devToken = await devAuth.login('jane_doe', 'devpassword');
|
|
373
|
+
console.log(`✅ Development token received: ${devToken.substring(0, 20)}...`);
|
|
374
|
+
const devUser = await mongoUserRepo.findById('user456');
|
|
375
|
+
await devCache.set('user:user456', devUser);
|
|
376
|
+
devLogger.debug('Development user logged in');
|
|
377
|
+
await devEmail.sendTemplate('welcome', { name: 'Jane', email: 'jane@example.com' });
|
|
378
|
+
|
|
379
|
+
// ================================================================
|
|
380
|
+
// Step 5: Demonstrate cross-registry service interactions
|
|
381
|
+
// ================================================================
|
|
382
|
+
console.log('\n5️⃣ Cross-registry service interactions...\n');
|
|
383
|
+
|
|
384
|
+
// Get services from different registries and show they can work together
|
|
385
|
+
const logger = hub.get('infrastructure', ['logger'], { scopes: ['dev'] }) as any;
|
|
386
|
+
const cache = hub.get('infrastructure', ['cache'], { scopes: ['dev'] }) as any;
|
|
387
|
+
const userRepo = hub.get('data', ['user'], { scopes: ['nosql'] }) as any;
|
|
388
|
+
const auth = hub.get('services', ['auth'], { scopes: ['dev'] }) as any;
|
|
389
|
+
|
|
390
|
+
logger.info('Starting complex workflow...');
|
|
391
|
+
|
|
392
|
+
// Simulate a complex workflow using services from different registries
|
|
393
|
+
const workflowToken = await auth.login('workflow_user', 'workflow_pass');
|
|
394
|
+
logger.info(`User authenticated for workflow with token: ${workflowToken.substring(0, 15)}...`);
|
|
395
|
+
|
|
396
|
+
const workflowUser = await userRepo.findById('workflow123');
|
|
397
|
+
logger.info('User data retrieved');
|
|
398
|
+
|
|
399
|
+
await cache.set('workflow:user123', workflowUser, 1800);
|
|
400
|
+
logger.info('User data cached');
|
|
401
|
+
|
|
402
|
+
const cachedUser = await cache.get('workflow:user123');
|
|
403
|
+
logger.info(`Retrieved user from cache: ${JSON.stringify(cachedUser).substring(0, 50)}...`);
|
|
404
|
+
|
|
405
|
+
// ================================================================
|
|
406
|
+
// Step 6: Show registry type management
|
|
407
|
+
// ================================================================
|
|
408
|
+
console.log('\n6️⃣ Registry management...\n');
|
|
409
|
+
|
|
410
|
+
console.log('📋 Available registry types:', hub.getRegisteredTypes());
|
|
411
|
+
|
|
412
|
+
// Get specific registries for direct access if needed
|
|
413
|
+
const servicesReg = hub.getRegistry('services');
|
|
414
|
+
const dataReg = hub.getRegistry('data');
|
|
415
|
+
|
|
416
|
+
console.log('🔧 Direct registry access available for:',
|
|
417
|
+
servicesReg ? 'services' : 'none',
|
|
418
|
+
dataReg ? 'data' : 'none'
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
console.log('\n✨ RegistryHub example completed successfully!');
|
|
422
|
+
console.log('\n📚 Key Benefits Demonstrated:');
|
|
423
|
+
console.log(' • Service organization by type/category');
|
|
424
|
+
console.log(' • Environment-specific implementations');
|
|
425
|
+
console.log(' • Clean separation of concerns');
|
|
426
|
+
console.log(' • Centralized service discovery');
|
|
427
|
+
console.log(' • Easy testing with different implementations');
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Run the example when executed directly
|
|
431
|
+
if (typeof process !== 'undefined' && process.argv?.[1]?.includes('registry-hub-types')) {
|
|
432
|
+
demonstrateRegistryHub().catch(console.error);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export default {
|
|
436
|
+
demonstrateRegistryHub
|
|
437
|
+
};
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple Registry Example - No RegistryHub, No Scopes
|
|
3
|
+
*
|
|
4
|
+
* This example demonstrates the simplest possible way to use the Registry library:
|
|
5
|
+
* - Create a registry without a RegistryHub
|
|
6
|
+
* - Register instances without any scopes
|
|
7
|
+
* - Retrieve instances without any scopes
|
|
8
|
+
* - Perfect for simple applications that just need basic dependency injection
|
|
9
|
+
*
|
|
10
|
+
* Run this example with: npx tsx examples/simple-example.ts
|
|
11
|
+
*
|
|
12
|
+
* Note: In a real application, import from the built package:
|
|
13
|
+
* import { createRegistry, createInstance } from '@fjell/registry';
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
// Import the actual registry functionality
|
|
17
|
+
import { createRegistry } from '../src/Registry';
|
|
18
|
+
import { createInstance } from '../src/Instance';
|
|
19
|
+
|
|
20
|
+
// ===== Simple Service Examples =====
|
|
21
|
+
|
|
22
|
+
// Example 1: A simple logger service
|
|
23
|
+
class SimpleLogger {
|
|
24
|
+
log(message: string) {
|
|
25
|
+
console.log(`[LOG] ${new Date().toISOString()}: ${message}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
error(message: string) {
|
|
29
|
+
console.error(`[ERROR] ${new Date().toISOString()}: ${message}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Example 2: A simple configuration service
|
|
34
|
+
class SimpleConfig {
|
|
35
|
+
private config = {
|
|
36
|
+
appName: 'My Simple App',
|
|
37
|
+
version: '1.0.0',
|
|
38
|
+
debugMode: true
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
get(key: string): any {
|
|
42
|
+
return this.config[key as keyof typeof this.config];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
set(key: string, value: any): void {
|
|
46
|
+
(this.config as any)[key] = value;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Example 3: A simple data service
|
|
51
|
+
class SimpleDataService {
|
|
52
|
+
private data: string[] = [];
|
|
53
|
+
|
|
54
|
+
add(item: string): void {
|
|
55
|
+
this.data.push(item);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
getAll(): string[] {
|
|
59
|
+
return [...this.data];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
count(): number {
|
|
63
|
+
return this.data.length;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ===== Main Example =====
|
|
68
|
+
|
|
69
|
+
async function runSimpleExample() {
|
|
70
|
+
console.log('🚀 Simple Registry Example - No RegistryHub, No Scopes\n');
|
|
71
|
+
|
|
72
|
+
// Step 1: Create a simple registry (no RegistryHub needed!)
|
|
73
|
+
console.log('1. Creating a simple registry...');
|
|
74
|
+
const registry = createRegistry('app');
|
|
75
|
+
console.log(' ✅ Registry created with type: "app"\n');
|
|
76
|
+
|
|
77
|
+
// Step 2: Create instances using factories (no scopes needed!)
|
|
78
|
+
console.log('2. Creating and registering instances...');
|
|
79
|
+
|
|
80
|
+
// Logger instance
|
|
81
|
+
registry.createInstance(
|
|
82
|
+
['logger'],
|
|
83
|
+
[], // Empty scopes array - we don't need scopes!
|
|
84
|
+
(coordinate, context) => {
|
|
85
|
+
// Create the actual service
|
|
86
|
+
const service = new SimpleLogger();
|
|
87
|
+
|
|
88
|
+
// Create and return an instance that wraps the service
|
|
89
|
+
const instance = createInstance(context.registry, coordinate);
|
|
90
|
+
// Attach service methods to the instance for easy access
|
|
91
|
+
(instance as any).log = service.log.bind(service);
|
|
92
|
+
(instance as any).error = service.error.bind(service);
|
|
93
|
+
return instance;
|
|
94
|
+
}
|
|
95
|
+
);
|
|
96
|
+
console.log(' ✅ Logger instance created and registered');
|
|
97
|
+
|
|
98
|
+
// Config instance
|
|
99
|
+
registry.createInstance(
|
|
100
|
+
['config'],
|
|
101
|
+
[], // No scopes needed
|
|
102
|
+
(coordinate, context) => {
|
|
103
|
+
const service = new SimpleConfig();
|
|
104
|
+
const instance = createInstance(context.registry, coordinate);
|
|
105
|
+
(instance as any).get = service.get.bind(service);
|
|
106
|
+
(instance as any).set = service.set.bind(service);
|
|
107
|
+
return instance;
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
console.log(' ✅ Config instance created and registered');
|
|
111
|
+
|
|
112
|
+
// Data service instance
|
|
113
|
+
registry.createInstance(
|
|
114
|
+
['data'],
|
|
115
|
+
[], // No scopes needed
|
|
116
|
+
(coordinate, context) => {
|
|
117
|
+
const service = new SimpleDataService();
|
|
118
|
+
const instance = createInstance(context.registry, coordinate);
|
|
119
|
+
(instance as any).add = service.add.bind(service);
|
|
120
|
+
(instance as any).getAll = service.getAll.bind(service);
|
|
121
|
+
(instance as any).count = service.count.bind(service);
|
|
122
|
+
return instance;
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
console.log(' ✅ Data service instance created and registered\n');
|
|
126
|
+
|
|
127
|
+
// Step 3: Retrieve instances (no scopes needed!)
|
|
128
|
+
console.log('3. Retrieving instances from registry...');
|
|
129
|
+
|
|
130
|
+
const retrievedLogger = registry.get(['logger']); // No scopes parameter needed!
|
|
131
|
+
const retrievedConfig = registry.get(['config']); // No scopes parameter needed!
|
|
132
|
+
const retrievedData = registry.get(['data']); // No scopes parameter needed!
|
|
133
|
+
|
|
134
|
+
console.log(' ✅ All instances retrieved successfully\n');
|
|
135
|
+
|
|
136
|
+
// Step 4: Verify everything works
|
|
137
|
+
console.log('4. Verifying the registry works...');
|
|
138
|
+
|
|
139
|
+
if (retrievedLogger && retrievedConfig && retrievedData) {
|
|
140
|
+
console.log(' ✅ All instances found in registry');
|
|
141
|
+
console.log(` 📋 Logger coordinate: ${JSON.stringify(retrievedLogger.coordinate)}`);
|
|
142
|
+
console.log(` 📋 Config coordinate: ${JSON.stringify(retrievedConfig.coordinate)}`);
|
|
143
|
+
console.log(` 📋 Data coordinate: ${JSON.stringify(retrievedData.coordinate)}\n`);
|
|
144
|
+
} else {
|
|
145
|
+
console.log(' ❌ Some instances were not found');
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Step 5: Show practical usage
|
|
150
|
+
console.log('5. Practical usage example...');
|
|
151
|
+
console.log(' In a real app, you would extend the instances with your actual services:');
|
|
152
|
+
console.log(' - Attach your service methods to the instance');
|
|
153
|
+
console.log(' - Use the registry to get dependencies between services');
|
|
154
|
+
console.log(' - Access instances from anywhere in your app\n');
|
|
155
|
+
|
|
156
|
+
// Step 6: Demonstrate the services actually work!
|
|
157
|
+
console.log('6. Testing the services...');
|
|
158
|
+
|
|
159
|
+
// Test the logger
|
|
160
|
+
const logger = registry.get(['logger']);
|
|
161
|
+
if (logger) {
|
|
162
|
+
(logger as any).log('This is a test log message from the registry!');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Test the config
|
|
166
|
+
const config = registry.get(['config']);
|
|
167
|
+
if (config) {
|
|
168
|
+
console.log(` 📋 App name from config: ${(config as any).get('appName')}`);
|
|
169
|
+
(config as any).set('newProperty', 'This was set dynamically!');
|
|
170
|
+
console.log(` 📋 New property: ${(config as any).get('newProperty')}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Test the data service
|
|
174
|
+
const dataService = registry.get(['data']);
|
|
175
|
+
if (dataService) {
|
|
176
|
+
(dataService as any).add('Item 1');
|
|
177
|
+
(dataService as any).add('Item 2');
|
|
178
|
+
(dataService as any).add('Item 3');
|
|
179
|
+
console.log(` 📋 Data service has ${(dataService as any).count()} items`);
|
|
180
|
+
console.log(` 📋 All items: ${JSON.stringify((dataService as any).getAll())}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
console.log(' ✅ All services working correctly!\n');
|
|
184
|
+
|
|
185
|
+
console.log('✨ Simple Registry Example Complete!');
|
|
186
|
+
console.log('\n📝 Key Takeaways:');
|
|
187
|
+
console.log(' • No RegistryHub required - just call createRegistry()');
|
|
188
|
+
console.log(' • No scopes required - pass empty array [] or omit entirely');
|
|
189
|
+
console.log(' • Perfect for simple dependency injection needs');
|
|
190
|
+
console.log(' • You can always add RegistryHub and scopes later as your app grows');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ===== Advanced Simple Example =====
|
|
194
|
+
|
|
195
|
+
async function runAdvancedSimpleExample() {
|
|
196
|
+
console.log('\n' + '='.repeat(60));
|
|
197
|
+
console.log('🔥 Advanced Simple Example - Service Dependencies');
|
|
198
|
+
console.log('='.repeat(60) + '\n');
|
|
199
|
+
|
|
200
|
+
const registry = createRegistry('advanced-app');
|
|
201
|
+
|
|
202
|
+
// Create a notification service that depends on the logger
|
|
203
|
+
class NotificationService {
|
|
204
|
+
constructor(private logger: any) { }
|
|
205
|
+
|
|
206
|
+
sendNotification(message: string) {
|
|
207
|
+
this.logger.log(`Sending notification: ${message}`);
|
|
208
|
+
// In a real app, this would send actual notifications
|
|
209
|
+
console.log(`📢 Notification sent: ${message}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Register logger first
|
|
214
|
+
registry.createInstance(['logger'], [], (coordinate, context) => {
|
|
215
|
+
const service = new SimpleLogger();
|
|
216
|
+
const instance = createInstance(context.registry, coordinate);
|
|
217
|
+
// Attach service methods to instance for easy access
|
|
218
|
+
(instance as any).log = service.log.bind(service);
|
|
219
|
+
(instance as any).error = service.error.bind(service);
|
|
220
|
+
return instance;
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Register notification service that uses the logger
|
|
224
|
+
registry.createInstance(['notification'], [], (coordinate, context) => {
|
|
225
|
+
// Get the logger dependency from the registry
|
|
226
|
+
const logger = context.registry.get(['logger']);
|
|
227
|
+
const service = new NotificationService(logger);
|
|
228
|
+
const instance = createInstance(context.registry, coordinate);
|
|
229
|
+
(instance as any).sendNotification = service.sendNotification.bind(service);
|
|
230
|
+
return instance;
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// Use the services
|
|
234
|
+
const notificationService = registry.get(['notification']);
|
|
235
|
+
if (notificationService) {
|
|
236
|
+
(notificationService as any).sendNotification('Welcome to the simple registry!');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
console.log('\n✨ This shows how services can depend on each other');
|
|
240
|
+
console.log(' even in the simple, no-scopes approach!');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Run the examples
|
|
244
|
+
// Note: In ES modules, we can't use require.main === module
|
|
245
|
+
// So we'll just run the examples directly when this file is executed
|
|
246
|
+
runSimpleExample()
|
|
247
|
+
.then(() => runAdvancedSimpleExample())
|
|
248
|
+
.catch(console.error);
|
|
249
|
+
|
|
250
|
+
export { runSimpleExample, runAdvancedSimpleExample };
|