@xbg.solutions/create-frontend 1.1.1

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.
@@ -0,0 +1,1306 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Service Generator
5
+ *
6
+ * Generates a new service with TypeScript support, error handling, and tests.
7
+ *
8
+ * Usage:
9
+ * node __scripts__/generate-service.js <ServiceName> [options]
10
+ * npm run generate:service <ServiceName> [options]
11
+ *
12
+ * Options:
13
+ * --type <type> Service type: api|store|util|auth|data (default: api)
14
+ * --with-test Generate test file
15
+ * --with-types Generate TypeScript types file
16
+ * --with-cache Add caching functionality
17
+ * --with-events Add event publishing
18
+ *
19
+ * Examples:
20
+ * node __scripts__/generate-service.js UserService --type=api --with-test --with-cache
21
+ * node __scripts__/generate-service.js NotificationService --with-events --with-test
22
+ * node __scripts__/generate-service.js DataProcessor --type=util --with-types
23
+ */
24
+
25
+ const fs = require('fs');
26
+ const path = require('path');
27
+
28
+ // Parse command line arguments
29
+ const args = process.argv.slice(2);
30
+ const serviceName = args[0];
31
+
32
+ if (!serviceName) {
33
+ console.error('โŒ Service name is required!');
34
+ console.log('Usage: node __scripts__/generate-service.js <ServiceName> [options]');
35
+ console.log('Examples:');
36
+ console.log(' node __scripts__/generate-service.js UserService --type=api');
37
+ console.log(' node __scripts__/generate-service.js NotificationStore --type=store');
38
+ process.exit(1);
39
+ }
40
+
41
+ // Parse options
42
+ const options = {
43
+ type: 'api',
44
+ withTest: false,
45
+ withTypes: false,
46
+ withCache: false,
47
+ withEvents: false
48
+ };
49
+
50
+ args.slice(1).forEach(arg => {
51
+ if (arg.startsWith('--type=')) {
52
+ options.type = arg.split('=')[1];
53
+ } else if (arg === '--with-test') {
54
+ options.withTest = true;
55
+ } else if (arg === '--with-types') {
56
+ options.withTypes = true;
57
+ } else if (arg === '--with-cache') {
58
+ options.withCache = true;
59
+ } else if (arg === '--with-events') {
60
+ options.withEvents = true;
61
+ }
62
+ });
63
+
64
+ // Validate service name
65
+ if (!/^[A-Z][a-zA-Z0-9]*$/.test(serviceName)) {
66
+ console.error('โŒ Service name must be PascalCase (e.g., UserService, DataProcessor)');
67
+ process.exit(1);
68
+ }
69
+
70
+ // Validate service type
71
+ if (!['api', 'store', 'util', 'auth', 'data'].includes(options.type)) {
72
+ console.error('โŒ Invalid service type. Must be: api, store, util, auth, or data');
73
+ process.exit(1);
74
+ }
75
+
76
+ // Generate file paths
77
+ const serviceTypePath = {
78
+ api: 'src/lib/services/api',
79
+ store: 'src/lib/stores',
80
+ util: 'src/lib/utils',
81
+ auth: 'src/lib/services/auth',
82
+ data: 'src/lib/services/data'
83
+ };
84
+
85
+ const basePath = serviceTypePath[options.type];
86
+ const kebabName = serviceName.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
87
+ const fileName = kebabName.replace('-service', '') + (options.type === 'store' ? '.store' : options.type === 'util' ? '' : '.service');
88
+ const serviceFile = path.join(basePath, `${fileName}.ts`);
89
+ const testFile = path.join(basePath, `${fileName}.test.ts`);
90
+ const typesFile = path.join(basePath, `${fileName}.types.ts`);
91
+
92
+ // Ensure directory exists
93
+ const dir = path.dirname(serviceFile);
94
+ if (!fs.existsSync(dir)) {
95
+ fs.mkdirSync(dir, { recursive: true });
96
+ }
97
+
98
+ // Generate service template based on type
99
+ function generateServiceTemplate() {
100
+ const templates = {
101
+ api: generateApiServiceTemplate(),
102
+ store: generateStoreServiceTemplate(),
103
+ util: generateUtilServiceTemplate(),
104
+ auth: generateAuthServiceTemplate(),
105
+ data: generateDataServiceTemplate()
106
+ };
107
+
108
+ return templates[options.type];
109
+ }
110
+
111
+ function generateApiServiceTemplate() {
112
+ const cacheImport = options.withCache ? `import { cacheService } from '$lib/services/cache/cache.service';` : '';
113
+ const eventsImport = options.withEvents ? `import { publish } from '$lib/services/events';` : '';
114
+ const typesImport = options.withTypes ? `import type { ${serviceName}Types } from './${fileName}.types';` : '';
115
+
116
+ const cacheLogic = options.withCache ? `
117
+ // Cache configuration
118
+ private cacheKey = '${kebabName}';
119
+ private cacheTTL = 5 * 60 * 1000; // 5 minutes` : '';
120
+
121
+ const eventLogic = options.withEvents ? `
122
+ // Event publishing
123
+ private publishEvent(eventType: string, data: any) {
124
+ publish(\`${kebabName}:\${eventType}\`, {
125
+ timestamp: Date.now(),
126
+ source: '${serviceName}',
127
+ data
128
+ });
129
+ }` : '';
130
+
131
+ return `/**
132
+ * ${serviceName}
133
+ *
134
+ * API service for managing ${serviceName.replace('Service', '').toLowerCase()} operations.
135
+ * Handles CRUD operations, error handling, and data transformation.
136
+ */
137
+
138
+ import { apiService } from '$lib/services/api/api.service';
139
+ import { handleError } from '$lib/utils/error-handler';
140
+ import { loggerService } from '$lib/services/logging/logging.service';${cacheImport}${eventsImport}${typesImport}
141
+
142
+ // Types
143
+ export interface ${serviceName.replace('Service', '')}Item {
144
+ id: string;
145
+ name: string;
146
+ description?: string;
147
+ createdAt: Date;
148
+ updatedAt: Date;
149
+ }
150
+
151
+ export interface Create${serviceName.replace('Service', '')}Request {
152
+ name: string;
153
+ description?: string;
154
+ }
155
+
156
+ export interface Update${serviceName.replace('Service', '')}Request {
157
+ name?: string;
158
+ description?: string;
159
+ }
160
+
161
+ export interface ${serviceName.replace('Service', '')}ListOptions {
162
+ limit?: number;
163
+ offset?: number;
164
+ search?: string;
165
+ sortBy?: string;
166
+ sortOrder?: 'asc' | 'desc';
167
+ }
168
+
169
+ export interface ${serviceName.replace('Service', '')}Response {
170
+ data: ${serviceName.replace('Service', '')}Item[];
171
+ meta: {
172
+ total: number;
173
+ limit: number;
174
+ offset: number;
175
+ hasMore: boolean;
176
+ };
177
+ }
178
+
179
+ class ${serviceName} {
180
+ private baseUrl = '/api/${kebabName}';${cacheLogic}${eventLogic}
181
+
182
+ /**
183
+ * Get all items with optional filtering and pagination
184
+ */
185
+ async getAll(options: ${serviceName.replace('Service', '')}ListOptions = {}): Promise<${serviceName.replace('Service', '')}Response> {
186
+ try {
187
+ const params = new URLSearchParams();
188
+
189
+ if (options.limit) params.set('limit', options.limit.toString());
190
+ if (options.offset) params.set('offset', options.offset.toString());
191
+ if (options.search) params.set('search', options.search);
192
+ if (options.sortBy) params.set('sortBy', options.sortBy);
193
+ if (options.sortOrder) params.set('sortOrder', options.sortOrder);
194
+
195
+ const url = \`\${this.baseUrl}?\${params.toString()}\`;${options.withCache ? `
196
+
197
+ // Check cache first
198
+ const cached = await cacheService.get<${serviceName.replace('Service', '')}Response>(url);
199
+ if (cached) {
200
+ loggerService.debug('${serviceName}: Returning cached data');
201
+ return cached;
202
+ }` : ''}
203
+
204
+ const response = await apiService.get<${serviceName.replace('Service', '')}Response>(url);${options.withCache ? `
205
+
206
+ // Cache the response
207
+ await cacheService.set(url, response.data, this.cacheTTL);` : ''}${options.withEvents ? `
208
+
209
+ this.publishEvent('list', { count: response.data.data.length, options });` : ''}
210
+
211
+ return response.data;
212
+
213
+ } catch (error) {
214
+ loggerService.error('${serviceName}: Failed to get all items', { error, options });
215
+ throw handleError(error, 'Failed to load ${serviceName.replace('Service', '').toLowerCase()}s');
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Get a single item by ID
221
+ */
222
+ async getById(id: string): Promise<${serviceName.replace('Service', '')}Item> {
223
+ if (!id) {
224
+ throw new Error('ID is required');
225
+ }
226
+
227
+ try {
228
+ const url = \`\${this.baseUrl}/\${id}\`;${options.withCache ? `
229
+
230
+ // Check cache first
231
+ const cached = await cacheService.get<${serviceName.replace('Service', '')}Item>(url);
232
+ if (cached) {
233
+ return cached;
234
+ }` : ''}
235
+
236
+ const response = await apiService.get<${serviceName.replace('Service', '')}Item>(url);${options.withCache ? `
237
+
238
+ // Cache the response
239
+ await cacheService.set(url, response.data, this.cacheTTL);` : ''}${options.withEvents ? `
240
+
241
+ this.publishEvent('get', { id });` : ''}
242
+
243
+ return response.data;
244
+
245
+ } catch (error) {
246
+ loggerService.error('${serviceName}: Failed to get item by ID', { error, id });
247
+ throw handleError(error, \`Failed to load \${serviceName.replace('Service', '').toLowerCase()}\`);
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Create a new item
253
+ */
254
+ async create(data: Create${serviceName.replace('Service', '')}Request): Promise<${serviceName.replace('Service', '')}Item> {
255
+ try {
256
+ // Validate input
257
+ if (!data.name?.trim()) {
258
+ throw new Error('Name is required');
259
+ }
260
+
261
+ const response = await apiService.post<${serviceName.replace('Service', '')}Item>(this.baseUrl, data);${options.withCache ? `
262
+
263
+ // Invalidate related cache entries
264
+ await this.invalidateCache();` : ''}${options.withEvents ? `
265
+
266
+ this.publishEvent('create', { item: response.data });` : ''}
267
+
268
+ loggerService.info('${serviceName}: Item created successfully', { id: response.data.id });
269
+ return response.data;
270
+
271
+ } catch (error) {
272
+ loggerService.error('${serviceName}: Failed to create item', { error, data });
273
+ throw handleError(error, 'Failed to create ${serviceName.replace('Service', '').toLowerCase()}');
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Update an existing item
279
+ */
280
+ async update(id: string, data: Update${serviceName.replace('Service', '')}Request): Promise<${serviceName.replace('Service', '')}Item> {
281
+ if (!id) {
282
+ throw new Error('ID is required');
283
+ }
284
+
285
+ try {
286
+ const response = await apiService.put<${serviceName.replace('Service', '')}Item>(\`\${this.baseUrl}/\${id}\`, data);${options.withCache ? `
287
+
288
+ // Invalidate cache
289
+ await this.invalidateCache();
290
+ await cacheService.delete(\`\${this.baseUrl}/\${id}\`);` : ''}${options.withEvents ? `
291
+
292
+ this.publishEvent('update', { id, item: response.data });` : ''}
293
+
294
+ loggerService.info('${serviceName}: Item updated successfully', { id });
295
+ return response.data;
296
+
297
+ } catch (error) {
298
+ loggerService.error('${serviceName}: Failed to update item', { error, id, data });
299
+ throw handleError(error, 'Failed to update ${serviceName.replace('Service', '').toLowerCase()}');
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Delete an item
305
+ */
306
+ async delete(id: string): Promise<void> {
307
+ if (!id) {
308
+ throw new Error('ID is required');
309
+ }
310
+
311
+ try {
312
+ await apiService.delete(\`\${this.baseUrl}/\${id}\`);${options.withCache ? `
313
+
314
+ // Invalidate cache
315
+ await this.invalidateCache();
316
+ await cacheService.delete(\`\${this.baseUrl}/\${id}\`);` : ''}${options.withEvents ? `
317
+
318
+ this.publishEvent('delete', { id });` : ''}
319
+
320
+ loggerService.info('${serviceName}: Item deleted successfully', { id });
321
+
322
+ } catch (error) {
323
+ loggerService.error('${serviceName}: Failed to delete item', { error, id });
324
+ throw handleError(error, 'Failed to delete ${serviceName.replace('Service', '').toLowerCase()}');
325
+ }
326
+ }${options.withCache ? `
327
+
328
+ /**
329
+ * Clear all cached data for this service
330
+ */
331
+ async clearCache(): Promise<void> {
332
+ await this.invalidateCache();
333
+ loggerService.debug('${serviceName}: Cache cleared');
334
+ }
335
+
336
+ /**
337
+ * Invalidate cache entries
338
+ */
339
+ private async invalidateCache(): Promise<void> {
340
+ await cacheService.deleteByPattern(\`\${this.baseUrl}*\`);
341
+ }` : ''}${options.withEvents ? `
342
+
343
+ /**
344
+ * Subscribe to service events
345
+ */
346
+ onEvent(eventType: string, callback: (data: any) => void): () => void {
347
+ const fullEventType = \`${kebabName}:\${eventType}\`;
348
+ // Implementation depends on your event system
349
+ // Return unsubscribe function
350
+ return () => {};
351
+ }` : ''}
352
+ }
353
+
354
+ // Export singleton instance
355
+ export const ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)} = new ${serviceName}();
356
+ export default ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)};`;
357
+ }
358
+
359
+ function generateStoreServiceTemplate() {
360
+ return `/**
361
+ * ${serviceName}
362
+ *
363
+ * Svelte store for managing ${serviceName.replace('Service', '').replace('Store', '').toLowerCase()} state.
364
+ */
365
+
366
+ import { writable, derived, type Writable, type Readable } from 'svelte/store';
367
+ import { browser } from '$app/environment';${options.withEvents ? `
368
+ import { publish } from '$lib/services/events';` : ''}
369
+
370
+ // Types
371
+ export interface ${serviceName.replace('Service', '').replace('Store', '')}State {
372
+ data: any[];
373
+ loading: boolean;
374
+ error: string | null;
375
+ lastUpdated: Date | null;
376
+ }
377
+
378
+ export interface ${serviceName.replace('Service', '').replace('Store', '')}Item {
379
+ id: string;
380
+ // Add your item properties here
381
+ }
382
+
383
+ // Initial state
384
+ const initialState: ${serviceName.replace('Service', '').replace('Store', '')}State = {
385
+ data: [],
386
+ loading: false,
387
+ error: null,
388
+ lastUpdated: null
389
+ };
390
+
391
+ // Create the store
392
+ function create${serviceName}() {
393
+ const { subscribe, set, update }: Writable<${serviceName.replace('Service', '').replace('Store', '')}State> = writable(initialState);
394
+
395
+ return {
396
+ subscribe,
397
+
398
+ // Actions
399
+ setLoading(loading: boolean) {
400
+ update(state => ({ ...state, loading }));
401
+ },
402
+
403
+ setError(error: string | null) {
404
+ update(state => ({ ...state, error, loading: false }));
405
+ },
406
+
407
+ setData(data: any[]) {
408
+ update(state => ({
409
+ ...state,
410
+ data,
411
+ loading: false,
412
+ error: null,
413
+ lastUpdated: new Date()
414
+ }));${options.withEvents ? `
415
+
416
+ publish('${kebabName}:dataChanged', { count: data.length });` : ''}
417
+ },
418
+
419
+ addItem(item: ${serviceName.replace('Service', '').replace('Store', '')}Item) {
420
+ update(state => ({
421
+ ...state,
422
+ data: [...state.data, item],
423
+ lastUpdated: new Date()
424
+ }));${options.withEvents ? `
425
+
426
+ publish('${kebabName}:itemAdded', item);` : ''}
427
+ },
428
+
429
+ updateItem(id: string, updates: Partial<${serviceName.replace('Service', '').replace('Store', '')}Item>) {
430
+ update(state => ({
431
+ ...state,
432
+ data: state.data.map(item =>
433
+ item.id === id ? { ...item, ...updates } : item
434
+ ),
435
+ lastUpdated: new Date()
436
+ }));${options.withEvents ? `
437
+
438
+ publish('${kebabName}:itemUpdated', { id, updates });` : ''}
439
+ },
440
+
441
+ removeItem(id: string) {
442
+ update(state => ({
443
+ ...state,
444
+ data: state.data.filter(item => item.id !== id),
445
+ lastUpdated: new Date()
446
+ }));${options.withEvents ? `
447
+
448
+ publish('${kebabName}:itemRemoved', { id });` : ''}
449
+ },
450
+
451
+ reset() {
452
+ set(initialState);
453
+ },
454
+
455
+ // Load data from API
456
+ async load() {
457
+ this.setLoading(true);
458
+
459
+ try {
460
+ // Replace with your actual API call
461
+ // const response = await apiService.get('/api/${kebabName}');
462
+ // this.setData(response.data);
463
+
464
+ // Mock data for now
465
+ this.setData([]);
466
+
467
+ } catch (error) {
468
+ this.setError(error instanceof Error ? error.message : 'Failed to load data');
469
+ }
470
+ }
471
+ };
472
+ }
473
+
474
+ // Create store instance
475
+ export const ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)} = create${serviceName}();
476
+
477
+ // Derived stores
478
+ export const ${serviceName.replace('Service', '').replace('Store', '').toLowerCase()}Items: Readable<${serviceName.replace('Service', '').replace('Store', '')}Item[]> = derived(
479
+ ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)},
480
+ ($store) => $store.data
481
+ );
482
+
483
+ export const ${serviceName.replace('Service', '').replace('Store', '').toLowerCase()}Loading: Readable<boolean> = derived(
484
+ ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)},
485
+ ($store) => $store.loading
486
+ );
487
+
488
+ export const ${serviceName.replace('Service', '').replace('Store', '').toLowerCase()}Error: Readable<string | null> = derived(
489
+ ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)},
490
+ ($store) => $store.error
491
+ );
492
+
493
+ export const ${serviceName.replace('Service', '').replace('Store', '').toLowerCase()}Count: Readable<number> = derived(
494
+ ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)},
495
+ ($store) => $store.data.length
496
+ );
497
+
498
+ // Auto-load data in browser
499
+ if (browser) {
500
+ ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)}.load();
501
+ }`;
502
+ }
503
+
504
+ function generateUtilServiceTemplate() {
505
+ return `/**
506
+ * ${serviceName}
507
+ *
508
+ * Utility functions for ${serviceName.replace('Service', '').toLowerCase()} operations.
509
+ */
510
+
511
+ import { loggerService } from '$lib/services/logging/logging.service';${options.withEvents ? `
512
+ import { publish } from '$lib/services/events';` : ''}${options.withTypes ? `
513
+ import type { ${serviceName}Types } from './${fileName}.types';` : ''}
514
+
515
+ // Types
516
+ export interface ${serviceName.replace('Service', '')}Options {
517
+ // Define your options here
518
+ }
519
+
520
+ export interface ${serviceName.replace('Service', '')}Result {
521
+ success: boolean;
522
+ data?: any;
523
+ error?: string;
524
+ }
525
+
526
+ /**
527
+ * Main ${serviceName.replace('Service', '').toLowerCase()} function
528
+ */
529
+ export function ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1).replace('Service', '')}(
530
+ input: any,
531
+ options: ${serviceName.replace('Service', '')}Options = {}
532
+ ): ${serviceName.replace('Service', '')}Result {
533
+ try {
534
+ loggerService.debug('${serviceName}: Processing input', { input, options });
535
+
536
+ // Your utility logic here
537
+ const result = processInput(input, options);${options.withEvents ? `
538
+
539
+ publish('${kebabName}:processed', { input, result, options });` : ''}
540
+
541
+ return {
542
+ success: true,
543
+ data: result
544
+ };
545
+
546
+ } catch (error) {
547
+ loggerService.error('${serviceName}: Processing failed', { error, input, options });
548
+
549
+ return {
550
+ success: false,
551
+ error: error instanceof Error ? error.message : 'Processing failed'
552
+ };
553
+ }
554
+ }
555
+
556
+ /**
557
+ * Async version of ${serviceName.replace('Service', '').toLowerCase()}
558
+ */
559
+ export async function ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1).replace('Service', '')}Async(
560
+ input: any,
561
+ options: ${serviceName.replace('Service', '')}Options = {}
562
+ ): Promise<${serviceName.replace('Service', '')}Result> {
563
+ try {
564
+ loggerService.debug('${serviceName}: Processing input async', { input, options });
565
+
566
+ // Your async utility logic here
567
+ const result = await processInputAsync(input, options);${options.withEvents ? `
568
+
569
+ publish('${kebabName}:processedAsync', { input, result, options });` : ''}
570
+
571
+ return {
572
+ success: true,
573
+ data: result
574
+ };
575
+
576
+ } catch (error) {
577
+ loggerService.error('${serviceName}: Async processing failed', { error, input, options });
578
+
579
+ return {
580
+ success: false,
581
+ error: error instanceof Error ? error.message : 'Async processing failed'
582
+ };
583
+ }
584
+ }
585
+
586
+ /**
587
+ * Validate input data
588
+ */
589
+ export function validate${serviceName.replace('Service', '')}Input(input: any): boolean {
590
+ if (!input) {
591
+ return false;
592
+ }
593
+
594
+ // Add your validation logic here
595
+ return true;
596
+ }
597
+
598
+ /**
599
+ * Transform input data
600
+ */
601
+ export function transform${serviceName.replace('Service', '')}Input(input: any): any {
602
+ if (!input) {
603
+ throw new Error('Input is required');
604
+ }
605
+
606
+ // Add your transformation logic here
607
+ return input;
608
+ }
609
+
610
+ // Helper functions
611
+ function processInput(input: any, options: ${serviceName.replace('Service', '')}Options): any {
612
+ // Validate input
613
+ if (!validate${serviceName.replace('Service', '')}Input(input)) {
614
+ throw new Error('Invalid input data');
615
+ }
616
+
617
+ // Transform input
618
+ const transformed = transform${serviceName.replace('Service', '')}Input(input);
619
+
620
+ // Process the data
621
+ // Add your processing logic here
622
+
623
+ return transformed;
624
+ }
625
+
626
+ async function processInputAsync(input: any, options: ${serviceName.replace('Service', '')}Options): Promise<any> {
627
+ // Async processing logic
628
+ return new Promise((resolve, reject) => {
629
+ try {
630
+ const result = processInput(input, options);
631
+ // Simulate async operation
632
+ setTimeout(() => resolve(result), 100);
633
+ } catch (error) {
634
+ reject(error);
635
+ }
636
+ });
637
+ }
638
+
639
+ // Export utility object
640
+ export const ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)} = {
641
+ process: ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1).replace('Service', '')},
642
+ processAsync: ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1).replace('Service', '')}Async,
643
+ validate: validate${serviceName.replace('Service', '')}Input,
644
+ transform: transform${serviceName.replace('Service', '')}Input
645
+ };
646
+
647
+ export default ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)};`;
648
+ }
649
+
650
+ function generateAuthServiceTemplate() {
651
+ return `/**
652
+ * ${serviceName}
653
+ *
654
+ * Authentication service for ${serviceName.replace('Service', '').replace('Auth', '').toLowerCase()} operations.
655
+ */
656
+
657
+ import { authService } from '$lib/services/auth/auth.service';
658
+ import { tokenService } from '$lib/services/token/token.service';
659
+ import { handleError } from '$lib/utils/error-handler';
660
+ import { loggerService } from '$lib/services/logging/logging.service';${options.withEvents ? `
661
+ import { publish } from '$lib/services/events';` : ''}
662
+
663
+ // Types
664
+ export interface ${serviceName.replace('Service', '')}Credentials {
665
+ // Define your credential structure
666
+ }
667
+
668
+ export interface ${serviceName.replace('Service', '')}Result {
669
+ success: boolean;
670
+ user?: any;
671
+ token?: string;
672
+ error?: string;
673
+ }
674
+
675
+ class ${serviceName} {
676
+ /**
677
+ * Authenticate user
678
+ */
679
+ async authenticate(credentials: ${serviceName.replace('Service', '')}Credentials): Promise<${serviceName.replace('Service', '')}Result> {
680
+ try {
681
+ loggerService.info('${serviceName}: Starting authentication');
682
+
683
+ // Validate credentials
684
+ if (!this.validateCredentials(credentials)) {
685
+ throw new Error('Invalid credentials');
686
+ }
687
+
688
+ // Perform authentication logic
689
+ const authResult = await this.performAuth(credentials);
690
+
691
+ if (!authResult.success) {
692
+ throw new Error(authResult.error || 'Authentication failed');
693
+ }${options.withEvents ? `
694
+
695
+ publish('${kebabName}:authenticated', {
696
+ userId: authResult.user?.id,
697
+ timestamp: Date.now()
698
+ });` : ''}
699
+
700
+ loggerService.info('${serviceName}: Authentication successful');
701
+ return authResult;
702
+
703
+ } catch (error) {
704
+ loggerService.error('${serviceName}: Authentication failed', { error });
705
+ return {
706
+ success: false,
707
+ error: error instanceof Error ? error.message : 'Authentication failed'
708
+ };
709
+ }
710
+ }
711
+
712
+ /**
713
+ * Validate user session
714
+ */
715
+ async validateSession(): Promise<boolean> {
716
+ try {
717
+ // Check if user is authenticated
718
+ const isAuthenticated = await authService.isAuthenticated();
719
+ if (!isAuthenticated) {
720
+ return false;
721
+ }
722
+
723
+ // Validate token
724
+ const token = await tokenService.getToken();
725
+ if (!token) {
726
+ return false;
727
+ }
728
+
729
+ // Additional validation logic here
730
+ return true;
731
+
732
+ } catch (error) {
733
+ loggerService.error('${serviceName}: Session validation failed', { error });
734
+ return false;
735
+ }
736
+ }
737
+
738
+ /**
739
+ * Refresh authentication
740
+ */
741
+ async refresh(): Promise<${serviceName.replace('Service', '')}Result> {
742
+ try {
743
+ loggerService.info('${serviceName}: Refreshing authentication');
744
+
745
+ // Get current token
746
+ const currentToken = await tokenService.getToken();
747
+ if (!currentToken) {
748
+ throw new Error('No token to refresh');
749
+ }
750
+
751
+ // Refresh token
752
+ await tokenService.refreshToken();
753
+
754
+ return {
755
+ success: true
756
+ };
757
+
758
+ } catch (error) {
759
+ loggerService.error('${serviceName}: Refresh failed', { error });
760
+ return {
761
+ success: false,
762
+ error: error instanceof Error ? error.message : 'Refresh failed'
763
+ };
764
+ }
765
+ }
766
+
767
+ /**
768
+ * Sign out user
769
+ */
770
+ async signOut(): Promise<void> {
771
+ try {
772
+ loggerService.info('${serviceName}: Signing out user');
773
+
774
+ // Sign out from main auth service
775
+ await authService.logout();${options.withEvents ? `
776
+
777
+ publish('${kebabName}:signedOut', {
778
+ timestamp: Date.now()
779
+ });` : ''}
780
+
781
+ } catch (error) {
782
+ loggerService.error('${serviceName}: Sign out failed', { error });
783
+ throw handleError(error, 'Sign out failed');
784
+ }
785
+ }
786
+
787
+ // Helper methods
788
+ private validateCredentials(credentials: ${serviceName.replace('Service', '')}Credentials): boolean {
789
+ // Add your credential validation logic
790
+ return !!credentials;
791
+ }
792
+
793
+ private async performAuth(credentials: ${serviceName.replace('Service', '')}Credentials): Promise<${serviceName.replace('Service', '')}Result> {
794
+ // Add your authentication logic here
795
+ // This might involve API calls, token exchange, etc.
796
+
797
+ return {
798
+ success: true,
799
+ user: {
800
+ id: 'user-id',
801
+ // Add user properties
802
+ },
803
+ token: 'auth-token'
804
+ };
805
+ }
806
+ }
807
+
808
+ // Export singleton instance
809
+ export const ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)} = new ${serviceName}();
810
+ export default ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)};`;
811
+ }
812
+
813
+ function generateDataServiceTemplate() {
814
+ return `/**
815
+ * ${serviceName}
816
+ *
817
+ * Data service for managing ${serviceName.replace('Service', '').replace('Data', '').toLowerCase()} data operations.
818
+ */
819
+
820
+ import { apiService } from '$lib/services/api/api.service';
821
+ import { handleError } from '$lib/utils/error-handler';
822
+ import { loggerService } from '$lib/services/logging/logging.service';${options.withCache ? `
823
+ import { cacheService } from '$lib/services/cache/cache.service';` : ''}${options.withEvents ? `
824
+ import { publish } from '$lib/services/events';` : ''}
825
+
826
+ // Types
827
+ export interface ${serviceName.replace('Service', '').replace('Data', '')}DataItem {
828
+ id: string;
829
+ // Define your data structure
830
+ }
831
+
832
+ export interface ${serviceName.replace('Service', '').replace('Data', '')}Query {
833
+ filter?: Record<string, any>;
834
+ sort?: Record<string, 'asc' | 'desc'>;
835
+ limit?: number;
836
+ offset?: number;
837
+ }
838
+
839
+ export interface ${serviceName.replace('Service', '').replace('Data', '')}QueryResult {
840
+ data: ${serviceName.replace('Service', '').replace('Data', '')}DataItem[];
841
+ total: number;
842
+ hasMore: boolean;
843
+ }
844
+
845
+ class ${serviceName} {
846
+ private baseUrl = '/api/data/${kebabName}';${options.withCache ? `
847
+ private cachePrefix = '${kebabName}:';
848
+ private cacheTTL = 10 * 60 * 1000; // 10 minutes` : ''}
849
+
850
+ /**
851
+ * Query data with filtering and pagination
852
+ */
853
+ async query(query: ${serviceName.replace('Service', '').replace('Data', '')}Query = {}): Promise<${serviceName.replace('Service', '').replace('Data', '')}QueryResult> {
854
+ try {
855
+ loggerService.debug('${serviceName}: Querying data', { query });${options.withCache ? `
856
+
857
+ // Check cache
858
+ const cacheKey = this.cachePrefix + JSON.stringify(query);
859
+ const cached = await cacheService.get<${serviceName.replace('Service', '').replace('Data', '')}QueryResult>(cacheKey);
860
+ if (cached) {
861
+ loggerService.debug('${serviceName}: Returning cached query result');
862
+ return cached;
863
+ }` : ''}
864
+
865
+ // Build query parameters
866
+ const params = new URLSearchParams();
867
+ if (query.filter) params.set('filter', JSON.stringify(query.filter));
868
+ if (query.sort) params.set('sort', JSON.stringify(query.sort));
869
+ if (query.limit) params.set('limit', query.limit.toString());
870
+ if (query.offset) params.set('offset', query.offset.toString());
871
+
872
+ const response = await apiService.get<${serviceName.replace('Service', '').replace('Data', '')}QueryResult>(
873
+ \`\${this.baseUrl}?\${params.toString()}\`
874
+ );${options.withCache ? `
875
+
876
+ // Cache the result
877
+ await cacheService.set(cacheKey, response.data, this.cacheTTL);` : ''}${options.withEvents ? `
878
+
879
+ publish('${kebabName}:queried', { query, resultCount: response.data.data.length });` : ''}
880
+
881
+ return response.data;
882
+
883
+ } catch (error) {
884
+ loggerService.error('${serviceName}: Query failed', { error, query });
885
+ throw handleError(error, 'Failed to query data');
886
+ }
887
+ }
888
+
889
+ /**
890
+ * Get data by ID
891
+ */
892
+ async getById(id: string): Promise<${serviceName.replace('Service', '').replace('Data', '')}DataItem | null> {
893
+ if (!id) {
894
+ throw new Error('ID is required');
895
+ }
896
+
897
+ try {${options.withCache ? `
898
+ // Check cache
899
+ const cacheKey = \`\${this.cachePrefix}item:\${id}\`;
900
+ const cached = await cacheService.get<${serviceName.replace('Service', '').replace('Data', '')}DataItem>(cacheKey);
901
+ if (cached) {
902
+ return cached;
903
+ }` : ''}
904
+
905
+ const response = await apiService.get<${serviceName.replace('Service', '').replace('Data', '')}DataItem>(\`\${this.baseUrl}/\${id}\`);${options.withCache ? `
906
+
907
+ // Cache the item
908
+ await cacheService.set(cacheKey, response.data, this.cacheTTL);` : ''}
909
+
910
+ return response.data;
911
+
912
+ } catch (error) {
913
+ if (error.status === 404) {
914
+ return null;
915
+ }
916
+ loggerService.error('${serviceName}: Get by ID failed', { error, id });
917
+ throw handleError(error, 'Failed to get data item');
918
+ }
919
+ }
920
+
921
+ /**
922
+ * Save data item
923
+ */
924
+ async save(item: Omit<${serviceName.replace('Service', '').replace('Data', '')}DataItem, 'id'> | ${serviceName.replace('Service', '').replace('Data', '')}DataItem): Promise<${serviceName.replace('Service', '').replace('Data', '')}DataItem> {
925
+ try {
926
+ const isUpdate = 'id' in item && item.id;
927
+
928
+ const response = isUpdate
929
+ ? await apiService.put<${serviceName.replace('Service', '').replace('Data', '')}DataItem>(\`\${this.baseUrl}/\${item.id}\`, item)
930
+ : await apiService.post<${serviceName.replace('Service', '').replace('Data', '')}DataItem>(this.baseUrl, item);${options.withCache ? `
931
+
932
+ // Invalidate cache
933
+ await this.invalidateCache();` : ''}${options.withEvents ? `
934
+
935
+ publish(\`${kebabName}:\${isUpdate ? 'updated' : 'created'}\`, { item: response.data });` : ''}
936
+
937
+ loggerService.info(\`${serviceName}: Item \${isUpdate ? 'updated' : 'created'}\`, { id: response.data.id });
938
+ return response.data;
939
+
940
+ } catch (error) {
941
+ loggerService.error('${serviceName}: Save failed', { error, item });
942
+ throw handleError(error, 'Failed to save data item');
943
+ }
944
+ }
945
+
946
+ /**
947
+ * Delete data item
948
+ */
949
+ async delete(id: string): Promise<void> {
950
+ if (!id) {
951
+ throw new Error('ID is required');
952
+ }
953
+
954
+ try {
955
+ await apiService.delete(\`\${this.baseUrl}/\${id}\`);${options.withCache ? `
956
+
957
+ // Invalidate cache
958
+ await this.invalidateCache();
959
+ await cacheService.delete(\`\${this.cachePrefix}item:\${id}\`);` : ''}${options.withEvents ? `
960
+
961
+ publish('${kebabName}:deleted', { id });` : ''}
962
+
963
+ loggerService.info('${serviceName}: Item deleted', { id });
964
+
965
+ } catch (error) {
966
+ loggerService.error('${serviceName}: Delete failed', { error, id });
967
+ throw handleError(error, 'Failed to delete data item');
968
+ }
969
+ }
970
+
971
+ /**
972
+ * Bulk operations
973
+ */
974
+ async bulkSave(items: Array<Omit<${serviceName.replace('Service', '').replace('Data', '')}DataItem, 'id'> | ${serviceName.replace('Service', '').replace('Data', '')}DataItem>): Promise<${serviceName.replace('Service', '').replace('Data', '')}DataItem[]> {
975
+ try {
976
+ const response = await apiService.post<${serviceName.replace('Service', '').replace('Data', '')}DataItem[]>(\`\${this.baseUrl}/bulk\`, { items });${options.withCache ? `
977
+
978
+ // Invalidate cache
979
+ await this.invalidateCache();` : ''}${options.withEvents ? `
980
+
981
+ publish('${kebabName}:bulkSaved', { count: response.data.length });` : ''}
982
+
983
+ return response.data;
984
+
985
+ } catch (error) {
986
+ loggerService.error('${serviceName}: Bulk save failed', { error, itemCount: items.length });
987
+ throw handleError(error, 'Failed to bulk save items');
988
+ }
989
+ }
990
+
991
+ async bulkDelete(ids: string[]): Promise<void> {
992
+ try {
993
+ await apiService.post(\`\${this.baseUrl}/bulk-delete\`, { ids });${options.withCache ? `
994
+
995
+ // Invalidate cache
996
+ await this.invalidateCache();` : ''}${options.withEvents ? `
997
+
998
+ publish('${kebabName}:bulkDeleted', { count: ids.length });` : ''}
999
+
1000
+ } catch (error) {
1001
+ loggerService.error('${serviceName}: Bulk delete failed', { error, ids });
1002
+ throw handleError(error, 'Failed to bulk delete items');
1003
+ }
1004
+ }${options.withCache ? `
1005
+
1006
+ /**
1007
+ * Cache management
1008
+ */
1009
+ async clearCache(): Promise<void> {
1010
+ await this.invalidateCache();
1011
+ loggerService.debug('${serviceName}: Cache cleared');
1012
+ }
1013
+
1014
+ private async invalidateCache(): Promise<void> {
1015
+ await cacheService.deleteByPattern(\`\${this.cachePrefix}*\`);
1016
+ }` : ''}
1017
+ }
1018
+
1019
+ // Export singleton instance
1020
+ export const ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)} = new ${serviceName}();
1021
+ export default ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)};`;
1022
+ }
1023
+
1024
+ // Generate types file
1025
+ function generateTypesTemplate() {
1026
+ return `/**
1027
+ * ${serviceName} Types
1028
+ *
1029
+ * TypeScript type definitions for ${serviceName}.
1030
+ */
1031
+
1032
+ // Core types
1033
+ export interface ${serviceName}Config {
1034
+ // Configuration options
1035
+ }
1036
+
1037
+ export interface ${serviceName}Options {
1038
+ // Service options
1039
+ }
1040
+
1041
+ export interface ${serviceName}Result<T = any> {
1042
+ success: boolean;
1043
+ data?: T;
1044
+ error?: string;
1045
+ meta?: Record<string, any>;
1046
+ }
1047
+
1048
+ // Event types
1049
+ export interface ${serviceName}Events {
1050
+ // Define event types
1051
+ }
1052
+
1053
+ // API types
1054
+ export interface ${serviceName}ApiResponse<T = any> {
1055
+ data: T;
1056
+ status: number;
1057
+ message?: string;
1058
+ }
1059
+
1060
+ // Error types
1061
+ export interface ${serviceName}Error {
1062
+ code: string;
1063
+ message: string;
1064
+ details?: any;
1065
+ }
1066
+
1067
+ // Export namespace
1068
+ export namespace ${serviceName}Types {
1069
+ export type Config = ${serviceName}Config;
1070
+ export type Options = ${serviceName}Options;
1071
+ export type Result<T = any> = ${serviceName}Result<T>;
1072
+ export type Events = ${serviceName}Events;
1073
+ export type ApiResponse<T = any> = ${serviceName}ApiResponse<T>;
1074
+ export type Error = ${serviceName}Error;
1075
+ }`;
1076
+ }
1077
+
1078
+ // Generate test file
1079
+ function generateTestTemplate() {
1080
+ const serviceInstance = serviceName.charAt(0).toLowerCase() + serviceName.slice(1);
1081
+
1082
+ return `import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
1083
+ import { ${serviceInstance} } from './${fileName}';${options.type === 'api' ? `
1084
+ import { apiService } from '$lib/services/api/api.service';` : ''}${options.withCache ? `
1085
+ import { cacheService } from '$lib/services/cache/cache.service';` : ''}
1086
+
1087
+ // Mock dependencies
1088
+ ${options.type === 'api' ? `vi.mock('$lib/services/api/api.service');` : ''}${options.withCache ? `
1089
+ vi.mock('$lib/services/cache/cache.service');` : ''}
1090
+ vi.mock('$lib/services/logging/logging.service');
1091
+
1092
+ describe('${serviceName}', () => {
1093
+ beforeEach(() => {
1094
+ // Reset all mocks before each test
1095
+ vi.clearAllMocks();
1096
+ });
1097
+
1098
+ afterEach(() => {
1099
+ // Clean up after each test
1100
+ });
1101
+
1102
+ describe('Basic functionality', () => {
1103
+ it('should be defined', () => {
1104
+ expect(${serviceInstance}).toBeDefined();
1105
+ });${options.type === 'api' ? `
1106
+
1107
+ it('should get all items', async () => {
1108
+ const mockData = [{ id: '1', name: 'Test Item' }];
1109
+ const mockResponse = { data: { data: mockData, meta: { total: 1 } } };
1110
+
1111
+ vi.mocked(apiService.get).mockResolvedValue(mockResponse);
1112
+
1113
+ const result = await ${serviceInstance}.getAll();
1114
+
1115
+ expect(apiService.get).toHaveBeenCalledWith(expect.stringContaining('/api/${kebabName}'));
1116
+ expect(result.data).toEqual(mockData);
1117
+ });
1118
+
1119
+ it('should get item by ID', async () => {
1120
+ const mockItem = { id: '1', name: 'Test Item' };
1121
+ const mockResponse = { data: mockItem };
1122
+
1123
+ vi.mocked(apiService.get).mockResolvedValue(mockResponse);
1124
+
1125
+ const result = await ${serviceInstance}.getById('1');
1126
+
1127
+ expect(apiService.get).toHaveBeenCalledWith('/api/${kebabName}/1');
1128
+ expect(result).toEqual(mockItem);
1129
+ });
1130
+
1131
+ it('should create new item', async () => {
1132
+ const newItem = { name: 'New Item' };
1133
+ const createdItem = { id: '1', ...newItem };
1134
+ const mockResponse = { data: createdItem };
1135
+
1136
+ vi.mocked(apiService.post).mockResolvedValue(mockResponse);
1137
+
1138
+ const result = await ${serviceInstance}.create(newItem);
1139
+
1140
+ expect(apiService.post).toHaveBeenCalledWith('/api/${kebabName}', newItem);
1141
+ expect(result).toEqual(createdItem);
1142
+ });
1143
+
1144
+ it('should update existing item', async () => {
1145
+ const updates = { name: 'Updated Item' };
1146
+ const updatedItem = { id: '1', ...updates };
1147
+ const mockResponse = { data: updatedItem };
1148
+
1149
+ vi.mocked(apiService.put).mockResolvedValue(mockResponse);
1150
+
1151
+ const result = await ${serviceInstance}.update('1', updates);
1152
+
1153
+ expect(apiService.put).toHaveBeenCalledWith('/api/${kebabName}/1', updates);
1154
+ expect(result).toEqual(updatedItem);
1155
+ });
1156
+
1157
+ it('should delete item', async () => {
1158
+ vi.mocked(apiService.delete).mockResolvedValue({ data: null });
1159
+
1160
+ await ${serviceInstance}.delete('1');
1161
+
1162
+ expect(apiService.delete).toHaveBeenCalledWith('/api/${kebabName}/1');
1163
+ });` : ''}${options.type === 'store' ? `
1164
+
1165
+ it('should set loading state', () => {
1166
+ ${serviceInstance}.setLoading(true);
1167
+
1168
+ // Test loading state change
1169
+ // Note: You'll need to implement proper store testing
1170
+ });
1171
+
1172
+ it('should set data', () => {
1173
+ const testData = [{ id: '1', name: 'Test' }];
1174
+
1175
+ ${serviceInstance}.setData(testData);
1176
+
1177
+ // Test data state change
1178
+ });
1179
+
1180
+ it('should add item', () => {
1181
+ const newItem = { id: '1', name: 'New Item' };
1182
+
1183
+ ${serviceInstance}.addItem(newItem);
1184
+
1185
+ // Test item addition
1186
+ });` : ''}${options.type === 'util' ? `
1187
+
1188
+ it('should process input correctly', () => {
1189
+ const input = { test: 'data' };
1190
+ const result = ${serviceInstance}.process(input);
1191
+
1192
+ expect(result.success).toBe(true);
1193
+ expect(result.data).toBeDefined();
1194
+ });
1195
+
1196
+ it('should handle invalid input', () => {
1197
+ const result = ${serviceInstance}.process(null);
1198
+
1199
+ expect(result.success).toBe(false);
1200
+ expect(result.error).toBeDefined();
1201
+ });
1202
+
1203
+ it('should validate input', () => {
1204
+ expect(${serviceInstance}.validate({ test: 'data' })).toBe(true);
1205
+ expect(${serviceInstance}.validate(null)).toBe(false);
1206
+ });` : ''}
1207
+ });
1208
+
1209
+ describe('Error handling', () => {
1210
+ it('should handle API errors gracefully', async () => {
1211
+ const errorMessage = 'API Error';
1212
+ ${options.type === 'api' ? `vi.mocked(apiService.get).mockRejectedValue(new Error(errorMessage));
1213
+
1214
+ await expect(${serviceInstance}.getAll()).rejects.toThrow();` : `
1215
+ // Add error handling tests for your service type`}
1216
+ });
1217
+ });${options.withCache ? `
1218
+
1219
+ describe('Caching', () => {
1220
+ it('should return cached data when available', async () => {
1221
+ const cachedData = { data: [{ id: '1' }], meta: { total: 1 } };
1222
+ vi.mocked(cacheService.get).mockResolvedValue(cachedData);
1223
+
1224
+ const result = await ${serviceInstance}.getAll();
1225
+
1226
+ expect(cacheService.get).toHaveBeenCalled();
1227
+ expect(result).toEqual(cachedData);
1228
+ });
1229
+
1230
+ it('should cache API responses', async () => {
1231
+ const mockResponse = { data: { data: [], meta: { total: 0 } } };
1232
+ vi.mocked(apiService.get).mockResolvedValue(mockResponse);
1233
+ vi.mocked(cacheService.get).mockResolvedValue(null);
1234
+
1235
+ await ${serviceInstance}.getAll();
1236
+
1237
+ expect(cacheService.set).toHaveBeenCalled();
1238
+ });
1239
+
1240
+ it('should clear cache', async () => {
1241
+ await ${serviceInstance}.clearCache();
1242
+
1243
+ expect(cacheService.deleteByPattern).toHaveBeenCalled();
1244
+ });
1245
+ });` : ''}${options.withEvents ? `
1246
+
1247
+ describe('Events', () => {
1248
+ it('should publish events for operations', async () => {
1249
+ // Mock publish function
1250
+ const mockPublish = vi.fn();
1251
+ vi.doMock('$lib/services/events', () => ({ publish: mockPublish }));
1252
+
1253
+ // Test event publishing
1254
+ // Implementation depends on your specific service
1255
+ });
1256
+ });` : ''}
1257
+ });`;
1258
+ }
1259
+
1260
+ // Write files
1261
+ try {
1262
+ console.log(`๐Ÿš€ Generating ${serviceName} (${options.type} service)...`);
1263
+
1264
+ let filesToCreate = [];
1265
+
1266
+ // Write main service file
1267
+ fs.writeFileSync(serviceFile, generateServiceTemplate());
1268
+ filesToCreate.push(serviceFile);
1269
+
1270
+ // Write test file if requested
1271
+ if (options.withTest) {
1272
+ fs.writeFileSync(testFile, generateTestTemplate());
1273
+ filesToCreate.push(testFile);
1274
+ }
1275
+
1276
+ // Write types file if requested
1277
+ if (options.withTypes) {
1278
+ fs.writeFileSync(typesFile, generateTypesTemplate());
1279
+ filesToCreate.push(typesFile);
1280
+ }
1281
+
1282
+ console.log(`โœ… Service files created:`);
1283
+ filesToCreate.forEach(file => {
1284
+ console.log(` ${file}`);
1285
+ });
1286
+
1287
+ console.log(`\n๐ŸŽ‰ ${serviceName} generated successfully!`);
1288
+ console.log(`\n๐Ÿ“– Import your service:`);
1289
+ console.log(` import { ${serviceName.charAt(0).toLowerCase() + serviceName.slice(1)} } from '${serviceFile.replace(/^src\//, '$').replace(/\.ts$/, '')}';`);
1290
+
1291
+ if (options.withTest) {
1292
+ console.log(`\n๐Ÿงช Run tests:`);
1293
+ console.log(` npm test ${testFile}`);
1294
+ }
1295
+
1296
+ console.log(`\n๐Ÿ”ง Features enabled:`);
1297
+ console.log(` - Type: ${options.type}`);
1298
+ if (options.withCache) console.log(` - Caching: โœ…`);
1299
+ if (options.withEvents) console.log(` - Events: โœ…`);
1300
+ if (options.withTest) console.log(` - Tests: โœ…`);
1301
+ if (options.withTypes) console.log(` - Types: โœ…`);
1302
+
1303
+ } catch (error) {
1304
+ console.error('โŒ Error generating service:', error.message);
1305
+ process.exit(1);
1306
+ }