@fjell/registry 4.4.5 โ†’ 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.
Files changed (52) hide show
  1. package/README.md +546 -0
  2. package/dist/Coordinate.cjs +8 -5
  3. package/dist/Coordinate.d.ts +1 -1
  4. package/dist/Coordinate.js +8 -5
  5. package/dist/Instance.cjs +1 -1
  6. package/dist/Instance.d.ts +1 -1
  7. package/dist/Instance.js +1 -1
  8. package/dist/Registry.cjs +99 -90
  9. package/dist/Registry.d.ts +3 -42
  10. package/dist/Registry.js +99 -90
  11. package/dist/RegistryHub.cjs +78 -0
  12. package/dist/RegistryHub.d.ts +3 -0
  13. package/dist/RegistryHub.js +74 -0
  14. package/dist/errors/CoordinateError.cjs +70 -0
  15. package/dist/errors/CoordinateError.d.ts +28 -0
  16. package/dist/errors/CoordinateError.js +63 -0
  17. package/dist/errors/InstanceError.cjs +101 -0
  18. package/dist/errors/InstanceError.d.ts +42 -0
  19. package/dist/errors/InstanceError.js +92 -0
  20. package/dist/errors/RegistryError.cjs +82 -0
  21. package/dist/errors/RegistryError.d.ts +31 -0
  22. package/dist/errors/RegistryError.js +75 -0
  23. package/dist/errors/RegistryHubError.cjs +92 -0
  24. package/dist/errors/RegistryHubError.d.ts +39 -0
  25. package/dist/errors/RegistryHubError.js +84 -0
  26. package/dist/errors/index.d.ts +4 -0
  27. package/dist/index.cjs +501 -101
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.d.ts +3 -0
  30. package/dist/index.js +6 -1
  31. package/dist/types.d.ts +90 -0
  32. package/docs/TIMING_NODE_OPTIMIZATION.md +207 -0
  33. package/docs/TIMING_README.md +170 -0
  34. package/docs/memory-data/scaling-10-instances.json +526 -0
  35. package/docs/memory-data/scaling-100-instances.json +526 -0
  36. package/docs/memory-data/scaling-1000-instances.json +276 -0
  37. package/docs/memory-data/scaling-10000-instances.json +126 -0
  38. package/docs/memory-data/scaling-20-instances.json +526 -0
  39. package/docs/memory-data/scaling-200-instances.json +526 -0
  40. package/docs/memory-data/scaling-2000-instances.json +276 -0
  41. package/docs/memory-data/scaling-50-instances.json +526 -0
  42. package/docs/memory-data/scaling-500-instances.json +276 -0
  43. package/docs/memory-data/scaling-5000-instances.json +126 -0
  44. package/docs/memory-overhead.svg +120 -0
  45. package/docs/memory.md +430 -0
  46. package/docs/timing-range.svg +174 -0
  47. package/docs/timing.md +483 -0
  48. package/examples/README.md +187 -0
  49. package/examples/multi-level-keys.ts +374 -0
  50. package/examples/registry-hub-types.ts +437 -0
  51. package/examples/simple-example.ts +250 -0
  52. package/package.json +5 -3
@@ -0,0 +1,374 @@
1
+ /**
2
+ * Multi-Level Key Type Arrays - Working Example
3
+ *
4
+ * This example demonstrates how Registry supports hierarchical key paths with actual implementations:
5
+ * - ['user'] - Single level (SQL vs NoSQL implementations)
6
+ * - ['user', 'profile'] - Two levels (S3 vs Local storage implementations)
7
+ * - ['user', 'profile', 'preference'] - Three levels (Redis implementation)
8
+ * - ['task'] - Another single level (PostgreSQL vs MongoDB implementations)
9
+ *
10
+ * Run this example with: npx tsx examples/multi-level-keys.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
+ import type { Registry } from '../src/types';
20
+
21
+ // Helper function to require a service (throw if not found)
22
+ function requireService(registry: Registry, keyPath: string[], options?: { scopes?: string[] }): any {
23
+ const result = registry.get(keyPath, options);
24
+ if (!result) {
25
+ throw new Error(`Required service '${keyPath.join('.')}' not found`);
26
+ }
27
+ return result;
28
+ }
29
+
30
+ // ===== Service Interface Definitions =====
31
+
32
+ interface UserService {
33
+ id: string;
34
+ name: string;
35
+ email: string;
36
+ save(): Promise<void>;
37
+ findById(id: string): Promise<UserService | null>;
38
+ }
39
+
40
+ interface UserProfileService {
41
+ userId: string;
42
+ avatar: string;
43
+ bio: string;
44
+ updateProfile(data: Partial<UserProfileService>): Promise<void>;
45
+ }
46
+
47
+ interface UserPreferenceService {
48
+ userId: string;
49
+ theme: 'light' | 'dark';
50
+ notifications: boolean;
51
+ updatePreferences(prefs: Partial<UserPreferenceService>): Promise<void>;
52
+ }
53
+
54
+ interface TaskService {
55
+ id: string;
56
+ title: string;
57
+ completed: boolean;
58
+ markComplete(): Promise<void>;
59
+ createTask(data: { title: string; completed?: boolean }): Promise<TaskService>;
60
+ }
61
+
62
+ // ===== Service Interface Map (for documentation) =====
63
+ // In a real app, these would map key paths to their interface types:
64
+ // ['user'] -> UserService
65
+ // ['user', 'profile'] -> UserProfileService
66
+ // ['user', 'profile', 'preference'] -> UserPreferenceService
67
+ // ['task'] -> TaskService
68
+
69
+ // ===== Service Implementations =====
70
+
71
+ class SqlUserService implements UserService {
72
+ constructor(public id: string, public name: string, public email: string) { }
73
+
74
+ async save(): Promise<void> {
75
+ console.log(`[SQL] Saving user ${this.name} to database`);
76
+ }
77
+
78
+ async findById(id: string): Promise<UserService | null> {
79
+ console.log(`[SQL] Finding user by ID: ${id}`);
80
+ if (id === this.id) return this;
81
+ return null;
82
+ }
83
+ }
84
+
85
+ class NoSqlUserService implements UserService {
86
+ constructor(public id: string, public name: string, public email: string) { }
87
+
88
+ async save(): Promise<void> {
89
+ console.log(`[NoSQL] Saving user ${this.name} to document store`);
90
+ }
91
+
92
+ async findById(id: string): Promise<UserService | null> {
93
+ console.log(`[NoSQL] Finding user by ID: ${id}`);
94
+ if (id === this.id) return this;
95
+ return null;
96
+ }
97
+ }
98
+
99
+ class S3ProfileService implements UserProfileService {
100
+ constructor(public userId: string, public avatar: string, public bio: string) { }
101
+
102
+ async updateProfile(data: Partial<UserProfileService>): Promise<void> {
103
+ console.log(`[S3] Updating profile for user ${this.userId}:`, data);
104
+ Object.assign(this, data);
105
+ }
106
+ }
107
+
108
+ class LocalProfileService implements UserProfileService {
109
+ constructor(public userId: string, public avatar: string, public bio: string) { }
110
+
111
+ async updateProfile(data: Partial<UserProfileService>): Promise<void> {
112
+ console.log(`[Local] Updating profile for user ${this.userId}:`, data);
113
+ Object.assign(this, data);
114
+ }
115
+ }
116
+
117
+ class RedisPreferenceService implements UserPreferenceService {
118
+ constructor(
119
+ public userId: string,
120
+ public theme: 'light' | 'dark' = 'light',
121
+ public notifications: boolean = true
122
+ ) { }
123
+
124
+ async updatePreferences(prefs: Partial<UserPreferenceService>): Promise<void> {
125
+ console.log(`[Redis] Updating preferences for user ${this.userId}:`, prefs);
126
+ Object.assign(this, prefs);
127
+ }
128
+ }
129
+
130
+ class PostgreSqlTaskService implements TaskService {
131
+ constructor(public id: string, public title: string, public completed: boolean = false) { }
132
+
133
+ async markComplete(): Promise<void> {
134
+ console.log(`[PostgreSQL] Marking task "${this.title}" as complete`);
135
+ this.completed = true;
136
+ }
137
+
138
+ async createTask(data: { title: string; completed?: boolean }): Promise<TaskService> {
139
+ console.log(`[PostgreSQL] Creating new task: ${data.title}`);
140
+ const newId = Math.random().toString(36).substr(2, 9);
141
+ return new PostgreSqlTaskService(newId, data.title, data.completed ?? false);
142
+ }
143
+ }
144
+
145
+ class MongoTaskService implements TaskService {
146
+ constructor(public id: string, public title: string, public completed: boolean = false) { }
147
+
148
+ async markComplete(): Promise<void> {
149
+ console.log(`[MongoDB] Marking task "${this.title}" as complete`);
150
+ this.completed = true;
151
+ }
152
+
153
+ async createTask(data: { title: string; completed?: boolean }): Promise<TaskService> {
154
+ console.log(`[MongoDB] Creating new task: ${data.title}`);
155
+ const newId = Math.random().toString(36).substr(2, 9);
156
+ return new MongoTaskService(newId, data.title, data.completed ?? false);
157
+ }
158
+ }
159
+
160
+ // ===== Working Example =====
161
+
162
+ export async function runMultiLevelKeysExample(): Promise<void> {
163
+ console.log('๐Ÿš€ Registry Multi-Level Keys - Working Example');
164
+ console.log('');
165
+
166
+ // Create real registry for demonstration
167
+ const registry = createRegistry('multi-level-demo');
168
+
169
+ // ===== Register implementations with different scopes =====
170
+
171
+ console.log('๐Ÿ“ Registering services...');
172
+ console.log('');
173
+
174
+ // Single level - User service with different database implementations
175
+ registry.createInstance(['user'], ['sql', 'prod'], (coordinate, context) => {
176
+ const service = new SqlUserService('1', 'John Doe', 'john@example.com');
177
+ const instance = createInstance(context.registry, coordinate);
178
+ (instance as any).save = service.save.bind(service);
179
+ (instance as any).findById = service.findById.bind(service);
180
+ (instance as any).id = service.id;
181
+ (instance as any).name = service.name;
182
+ (instance as any).email = service.email;
183
+ return instance;
184
+ });
185
+
186
+ registry.createInstance(['user'], ['nosql', 'dev'], (coordinate, context) => {
187
+ const service = new NoSqlUserService('1', 'John Doe', 'john@example.com');
188
+ const instance = createInstance(context.registry, coordinate);
189
+ (instance as any).save = service.save.bind(service);
190
+ (instance as any).findById = service.findById.bind(service);
191
+ (instance as any).id = service.id;
192
+ (instance as any).name = service.name;
193
+ (instance as any).email = service.email;
194
+ return instance;
195
+ });
196
+
197
+ // Two levels - Profile service with different storage implementations
198
+ registry.createInstance(['user', 'profile'], ['s3', 'prod'], (coordinate, context) => {
199
+ const service = new S3ProfileService('1', 'avatar.jpg', 'Software developer');
200
+ const instance = createInstance(context.registry, coordinate);
201
+ (instance as any).updateProfile = service.updateProfile.bind(service);
202
+ (instance as any).userId = service.userId;
203
+ (instance as any).avatar = service.avatar;
204
+ (instance as any).bio = service.bio;
205
+ return instance;
206
+ });
207
+
208
+ registry.createInstance(['user', 'profile'], ['local', 'dev'], (coordinate, context) => {
209
+ const service = new LocalProfileService('1', 'avatar.jpg', 'Software developer');
210
+ const instance = createInstance(context.registry, coordinate);
211
+ (instance as any).updateProfile = service.updateProfile.bind(service);
212
+ (instance as any).userId = service.userId;
213
+ (instance as any).avatar = service.avatar;
214
+ (instance as any).bio = service.bio;
215
+ return instance;
216
+ });
217
+
218
+ // Three levels - Preferences (single implementation)
219
+ registry.createInstance(['user', 'profile', 'preference'], [], (coordinate, context) => {
220
+ const service = new RedisPreferenceService('1', 'dark', true);
221
+ const instance = createInstance(context.registry, coordinate);
222
+ (instance as any).updatePreferences = service.updatePreferences.bind(service);
223
+ (instance as any).userId = service.userId;
224
+ (instance as any).theme = service.theme;
225
+ (instance as any).notifications = service.notifications;
226
+ return instance;
227
+ });
228
+
229
+ // Single level - Task service with different database implementations
230
+ registry.createInstance(['task'], ['postgresql'], (coordinate, context) => {
231
+ const service = new PostgreSqlTaskService('task-1', 'Write documentation', false);
232
+ const instance = createInstance(context.registry, coordinate);
233
+ (instance as any).markComplete = service.markComplete.bind(service);
234
+ (instance as any).createTask = service.createTask.bind(service);
235
+ (instance as any).id = service.id;
236
+ (instance as any).title = service.title;
237
+ (instance as any).completed = service.completed;
238
+ return instance;
239
+ });
240
+
241
+ registry.createInstance(['task'], ['mongodb'], (coordinate, context) => {
242
+ const service = new MongoTaskService('task-1', 'Write documentation', false);
243
+ const instance = createInstance(context.registry, coordinate);
244
+ (instance as any).markComplete = service.markComplete.bind(service);
245
+ (instance as any).createTask = service.createTask.bind(service);
246
+ (instance as any).id = service.id;
247
+ (instance as any).title = service.title;
248
+ (instance as any).completed = service.completed;
249
+ return instance;
250
+ });
251
+
252
+ console.log('โœ… All services registered successfully!');
253
+ console.log('');
254
+
255
+ // ===== Demonstrate environment-based selection =====
256
+
257
+ console.log('๐Ÿ”„ Environment-based service selection:');
258
+ console.log('');
259
+
260
+ // Production environment
261
+ const prodUser = registry.get(['user'], { scopes: ['prod'] });
262
+ const prodProfile = registry.get(['user', 'profile'], { scopes: ['prod'] });
263
+
264
+ if (prodUser && prodProfile) {
265
+ console.log('๐Ÿญ Production Environment:');
266
+ await (prodUser as any).save();
267
+ await (prodProfile as any).updateProfile({ bio: 'Senior developer' });
268
+ console.log('');
269
+ }
270
+
271
+ // Development environment
272
+ const devUser = registry.get(['user'], { scopes: ['dev'] });
273
+ const devProfile = registry.get(['user', 'profile'], { scopes: ['dev'] });
274
+
275
+ if (devUser && devProfile) {
276
+ console.log('๐Ÿงช Development Environment:');
277
+ await (devUser as any).save();
278
+ await (devProfile as any).updateProfile({ bio: 'Junior developer' });
279
+ console.log('');
280
+ }
281
+
282
+ // ===== Demonstrate multi-level access =====
283
+
284
+ console.log('๐Ÿ—๏ธ Multi-level service access:');
285
+ console.log('');
286
+
287
+ // Three-level access - preferences (no scope needed, single implementation)
288
+ const preferences = requireService(registry, ['user', 'profile', 'preference']);
289
+ console.log('๐Ÿ“ฑ User preferences:');
290
+ console.log(` Theme: ${(preferences as any).theme}, Notifications: ${(preferences as any).notifications}`);
291
+ await (preferences as any).updatePreferences({ theme: 'light' });
292
+ console.log('');
293
+
294
+ // ===== Demonstrate different implementations for same interface =====
295
+
296
+ console.log('๐Ÿ’พ Database implementation selection:');
297
+ console.log('');
298
+
299
+ const pgTask = registry.get(['task'], { scopes: ['postgresql'] });
300
+ const mongoTask = registry.get(['task'], { scopes: ['mongodb'] });
301
+
302
+ if (pgTask) {
303
+ console.log('๐Ÿ˜ PostgreSQL Task Implementation:');
304
+ await (pgTask as any).markComplete();
305
+ const newTask = await (pgTask as any).createTask({ title: 'Add tests', completed: false });
306
+ console.log(` Created task: ${(newTask as any).title} (ID: ${(newTask as any).id})`);
307
+ console.log('');
308
+ }
309
+
310
+ if (mongoTask) {
311
+ console.log('๐Ÿƒ MongoDB Task Implementation:');
312
+ await (mongoTask as any).markComplete();
313
+ const newTask = await (mongoTask as any).createTask({ title: 'Deploy app', completed: false });
314
+ console.log(` Created task: ${(newTask as any).title} (ID: ${(newTask as any).id})`);
315
+ console.log('');
316
+ }
317
+
318
+ // ===== Demonstrate type safety =====
319
+
320
+ console.log('๐Ÿ”’ Type Safety in Action:');
321
+ console.log('');
322
+
323
+ // In a real app, TypeScript would know the exact return types
324
+ const typedUser = registry.get(['user'], { scopes: ['sql'] });
325
+ const typedProfile = registry.get(['user', 'profile'], { scopes: ['s3'] });
326
+ const typedPrefs = requireService(registry, ['user', 'profile', 'preference']);
327
+ const typedTask = registry.get(['task'], { scopes: ['postgresql'] });
328
+
329
+ console.log('โœ… All services retrieved successfully!');
330
+ console.log(` User service type: ${typedUser?.constructor.name}`);
331
+ console.log(` Profile service type: ${typedProfile?.constructor.name}`);
332
+ console.log(` Preference service type: ${typedPrefs.constructor.name}`);
333
+ console.log(` Task service type: ${typedTask?.constructor.name}`);
334
+ console.log('');
335
+
336
+ // ===== Demonstrate error handling =====
337
+
338
+ console.log('โš ๏ธ Error handling:');
339
+ console.log('');
340
+
341
+ // This will throw an error (not found)
342
+ try {
343
+ const missingService = registry.get(['user'], { scopes: ['nonexistent'] });
344
+ console.log(`Missing service with wrong scope: ${missingService}`);
345
+ } catch (error: any) {
346
+ console.log(`โŒ Service with wrong scope throws error: ${error.message}`);
347
+ }
348
+
349
+ // This will throw an error
350
+ try {
351
+ requireService(registry, ['nonexistent', 'service']);
352
+ } catch (error: any) {
353
+ console.log(`โŒ Caught expected error: ${error.message}`);
354
+ }
355
+
356
+ console.log('');
357
+ console.log('๐ŸŽ‰ Multi-level keys example completed successfully!');
358
+ console.log('');
359
+ console.log('Key takeaways:');
360
+ console.log('โ€ข Each key path has its own consistent interface type');
361
+ console.log('โ€ข Scopes allow multiple implementations of the same interface');
362
+ console.log('โ€ข TypeScript provides full type safety for all operations');
363
+ console.log('โ€ข Services can be organized hierarchically while remaining independent');
364
+ console.log('โ€ข Perfect for microservices, A/B testing, and environment-specific implementations');
365
+ }
366
+
367
+ // Run the example when executed directly
368
+ if (typeof process !== 'undefined' && process.argv?.[1]?.includes('multi-level-keys')) {
369
+ runMultiLevelKeysExample().catch(console.error);
370
+ }
371
+
372
+ export default {
373
+ runMultiLevelKeysExample
374
+ };