@l4yercak3/cli 1.2.16 → 1.2.18

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,683 @@
1
+ /**
2
+ * Typed API Client Generator
3
+ * Generates a fully-typed API client for L4YERCAK3
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const { ensureDir, writeFileWithBackup, checkFileOverwrite } = require('../../utils/file-utils');
9
+
10
+ class ClientGenerator {
11
+ /**
12
+ * Generate the typed API client
13
+ * @param {Object} options - Generation options
14
+ * @returns {Promise<string|null>} - Path to generated file or null if skipped
15
+ */
16
+ async generate(options) {
17
+ const { projectPath, isTypeScript, organizationId, backendUrl } = options;
18
+
19
+ // Determine output directory
20
+ let outputDir;
21
+ if (fs.existsSync(path.join(projectPath, 'src'))) {
22
+ outputDir = path.join(projectPath, 'src', 'lib', 'l4yercak3');
23
+ } else {
24
+ outputDir = path.join(projectPath, 'lib', 'l4yercak3');
25
+ }
26
+
27
+ ensureDir(outputDir);
28
+
29
+ const extension = isTypeScript ? 'ts' : 'js';
30
+ const outputPath = path.join(outputDir, `client.${extension}`);
31
+
32
+ const action = await checkFileOverwrite(outputPath);
33
+ if (action === 'skip') {
34
+ return null;
35
+ }
36
+
37
+ const content = isTypeScript
38
+ ? this.generateTypeScriptClient({ organizationId, backendUrl })
39
+ : this.generateJavaScriptClient({ organizationId, backendUrl });
40
+
41
+ return writeFileWithBackup(outputPath, content, action);
42
+ }
43
+
44
+ generateTypeScriptClient({ organizationId, backendUrl }) {
45
+ return `/**
46
+ * L4YERCAK3 API Client
47
+ * Auto-generated by @l4yercak3/cli
48
+ *
49
+ * A fully-typed API client for the L4YERCAK3 platform.
50
+ * Organization ID: ${organizationId}
51
+ */
52
+
53
+ import type {
54
+ Contact, ContactCreateInput, ContactUpdateInput,
55
+ Organization, OrganizationCreateInput,
56
+ Event, EventCreateInput,
57
+ Attendee,
58
+ Form, FormSubmission,
59
+ Product, Order, OrderItem,
60
+ Invoice, InvoiceCreateInput,
61
+ BenefitClaim, BenefitClaimInput,
62
+ CommissionPayout,
63
+ PaginatedResponse,
64
+ } from './types';
65
+
66
+ export class L4yercak3Client {
67
+ private apiKey: string;
68
+ private baseUrl: string;
69
+ private organizationId: string;
70
+
71
+ constructor(config: { apiKey: string; baseUrl?: string; organizationId?: string }) {
72
+ this.apiKey = config.apiKey;
73
+ this.baseUrl = config.baseUrl || '${backendUrl}';
74
+ this.organizationId = config.organizationId || '${organizationId}';
75
+ }
76
+
77
+ // ============ CRM: Contacts ============
78
+
79
+ async listContacts(params?: {
80
+ limit?: number;
81
+ offset?: number;
82
+ status?: 'active' | 'inactive' | 'archived';
83
+ subtype?: 'customer' | 'lead' | 'prospect' | 'partner';
84
+ search?: string;
85
+ tags?: string[];
86
+ }): Promise<PaginatedResponse<Contact>> {
87
+ return this.request('GET', '/api/v1/crm/contacts', { params });
88
+ }
89
+
90
+ async getContact(id: string, options?: {
91
+ includeActivities?: boolean;
92
+ includeNotes?: boolean;
93
+ includeOrganization?: boolean;
94
+ }): Promise<Contact> {
95
+ return this.request('GET', \`/api/v1/crm/contacts/\${id}\`, { params: options });
96
+ }
97
+
98
+ async createContact(data: ContactCreateInput): Promise<Contact> {
99
+ return this.request('POST', '/api/v1/crm/contacts', { body: data });
100
+ }
101
+
102
+ async updateContact(id: string, data: ContactUpdateInput): Promise<Contact> {
103
+ return this.request('PATCH', \`/api/v1/crm/contacts/\${id}\`, { body: data });
104
+ }
105
+
106
+ async deleteContact(id: string): Promise<void> {
107
+ return this.request('DELETE', \`/api/v1/crm/contacts/\${id}\`);
108
+ }
109
+
110
+ async addTagsToContact(id: string, tags: string[]): Promise<Contact> {
111
+ return this.request('POST', \`/api/v1/crm/contacts/\${id}/tags\`, { body: { tags } });
112
+ }
113
+
114
+ async removeTagsFromContact(id: string, tags: string[]): Promise<Contact> {
115
+ return this.request('DELETE', \`/api/v1/crm/contacts/\${id}/tags\`, { body: { tags } });
116
+ }
117
+
118
+ // ============ CRM: Organizations ============
119
+
120
+ async listOrganizations(params?: {
121
+ limit?: number;
122
+ offset?: number;
123
+ subtype?: 'customer' | 'prospect' | 'partner' | 'vendor';
124
+ search?: string;
125
+ }): Promise<PaginatedResponse<Organization>> {
126
+ return this.request('GET', '/api/v1/crm/organizations', { params });
127
+ }
128
+
129
+ async getOrganization(id: string, options?: {
130
+ includeContacts?: boolean;
131
+ }): Promise<Organization> {
132
+ return this.request('GET', \`/api/v1/crm/organizations/\${id}\`, { params: options });
133
+ }
134
+
135
+ async createOrganization(data: OrganizationCreateInput): Promise<Organization> {
136
+ return this.request('POST', '/api/v1/crm/organizations', { body: data });
137
+ }
138
+
139
+ // ============ Events ============
140
+
141
+ async listEvents(params?: {
142
+ status?: 'draft' | 'published' | 'cancelled' | 'completed';
143
+ fromDate?: string;
144
+ toDate?: string;
145
+ limit?: number;
146
+ offset?: number;
147
+ }): Promise<PaginatedResponse<Event>> {
148
+ return this.request('GET', '/api/v1/events', { params });
149
+ }
150
+
151
+ async getEvent(id: string, options?: {
152
+ includeProducts?: boolean;
153
+ includeSponsors?: boolean;
154
+ includeForms?: boolean;
155
+ }): Promise<Event> {
156
+ return this.request('GET', \`/api/v1/events/\${id}\`, { params: options });
157
+ }
158
+
159
+ async createEvent(data: EventCreateInput): Promise<Event> {
160
+ return this.request('POST', '/api/v1/events', { body: data });
161
+ }
162
+
163
+ async updateEvent(id: string, data: Partial<EventCreateInput>): Promise<Event> {
164
+ return this.request('PATCH', \`/api/v1/events/\${id}\`, { body: data });
165
+ }
166
+
167
+ async getEventAttendees(eventId: string, params?: {
168
+ status?: 'registered' | 'checked_in' | 'cancelled' | 'no_show';
169
+ limit?: number;
170
+ offset?: number;
171
+ }): Promise<PaginatedResponse<Attendee>> {
172
+ return this.request('GET', \`/api/v1/events/\${eventId}/attendees\`, { params });
173
+ }
174
+
175
+ async checkInAttendee(eventId: string, attendeeId: string): Promise<Attendee> {
176
+ return this.request('POST', \`/api/v1/events/\${eventId}/attendees/\${attendeeId}/check-in\`);
177
+ }
178
+
179
+ // ============ Forms ============
180
+
181
+ async listForms(params?: {
182
+ status?: 'draft' | 'published' | 'closed';
183
+ eventId?: string;
184
+ subtype?: 'registration' | 'survey' | 'application' | 'feedback' | 'contact';
185
+ limit?: number;
186
+ }): Promise<PaginatedResponse<Form>> {
187
+ return this.request('GET', '/api/v1/forms', { params });
188
+ }
189
+
190
+ async getForm(id: string): Promise<Form> {
191
+ return this.request('GET', \`/api/v1/forms/\${id}\`);
192
+ }
193
+
194
+ async submitForm(formId: string, data: Record<string, unknown>): Promise<FormSubmission> {
195
+ return this.request('POST', \`/api/v1/forms/\${formId}/submit\`, { body: data });
196
+ }
197
+
198
+ async getFormResponses(formId: string, params?: {
199
+ status?: 'submitted' | 'reviewed' | 'approved' | 'rejected';
200
+ limit?: number;
201
+ offset?: number;
202
+ }): Promise<PaginatedResponse<FormSubmission>> {
203
+ return this.request('GET', \`/api/v1/forms/\${formId}/responses\`, { params });
204
+ }
205
+
206
+ // ============ Products ============
207
+
208
+ async listProducts(params?: {
209
+ eventId?: string;
210
+ status?: 'active' | 'sold_out' | 'hidden';
211
+ category?: string;
212
+ limit?: number;
213
+ }): Promise<PaginatedResponse<Product>> {
214
+ return this.request('GET', '/api/v1/products', { params });
215
+ }
216
+
217
+ async getProduct(id: string): Promise<Product> {
218
+ return this.request('GET', \`/api/v1/products/\${id}\`);
219
+ }
220
+
221
+ // ============ Checkout ============
222
+
223
+ async createCheckoutSession(data: {
224
+ items: Array<{ productId: string; quantity: number }>;
225
+ contactId?: string;
226
+ email?: string;
227
+ successUrl: string;
228
+ cancelUrl: string;
229
+ metadata?: Record<string, string>;
230
+ }): Promise<{ sessionId: string; checkoutUrl: string }> {
231
+ return this.request('POST', '/api/v1/checkout/sessions', { body: data });
232
+ }
233
+
234
+ async getCheckoutSession(sessionId: string): Promise<{
235
+ sessionId: string;
236
+ status: 'open' | 'complete' | 'expired';
237
+ order?: Order;
238
+ }> {
239
+ return this.request('GET', \`/api/v1/checkout/sessions/\${sessionId}\`);
240
+ }
241
+
242
+ // ============ Orders ============
243
+
244
+ async listOrders(params?: {
245
+ contactId?: string;
246
+ status?: 'pending' | 'paid' | 'refunded' | 'cancelled';
247
+ fromDate?: string;
248
+ toDate?: string;
249
+ limit?: number;
250
+ }): Promise<PaginatedResponse<Order>> {
251
+ return this.request('GET', '/api/v1/orders', { params });
252
+ }
253
+
254
+ async getOrder(id: string): Promise<Order> {
255
+ return this.request('GET', \`/api/v1/orders/\${id}\`);
256
+ }
257
+
258
+ // ============ Invoicing ============
259
+
260
+ async listInvoices(params?: {
261
+ contactId?: string;
262
+ organizationId?: string;
263
+ status?: 'draft' | 'sent' | 'paid' | 'overdue' | 'cancelled';
264
+ type?: 'b2b' | 'b2c';
265
+ limit?: number;
266
+ }): Promise<PaginatedResponse<Invoice>> {
267
+ return this.request('GET', '/api/v1/invoices', { params });
268
+ }
269
+
270
+ async getInvoice(id: string): Promise<Invoice> {
271
+ return this.request('GET', \`/api/v1/invoices/\${id}\`);
272
+ }
273
+
274
+ async createInvoice(data: InvoiceCreateInput): Promise<Invoice> {
275
+ return this.request('POST', '/api/v1/invoices', { body: data });
276
+ }
277
+
278
+ async sendInvoice(id: string, options?: {
279
+ emailTo?: string;
280
+ message?: string;
281
+ }): Promise<void> {
282
+ return this.request('POST', \`/api/v1/invoices/\${id}/send\`, { body: options });
283
+ }
284
+
285
+ async markInvoicePaid(id: string, data?: {
286
+ paidAt?: string;
287
+ paymentMethod?: string;
288
+ paymentReference?: string;
289
+ }): Promise<Invoice> {
290
+ return this.request('POST', \`/api/v1/invoices/\${id}/mark-paid\`, { body: data });
291
+ }
292
+
293
+ async getInvoicePdf(id: string): Promise<{ pdfUrl: string }> {
294
+ return this.request('GET', \`/api/v1/invoices/\${id}/pdf\`);
295
+ }
296
+
297
+ // ============ Benefits ============
298
+
299
+ async listBenefitClaims(params?: {
300
+ memberId?: string;
301
+ status?: 'pending' | 'approved' | 'rejected' | 'paid' | 'cancelled';
302
+ benefitType?: string;
303
+ limit?: number;
304
+ }): Promise<PaginatedResponse<BenefitClaim>> {
305
+ return this.request('GET', '/api/v1/benefits/claims', { params });
306
+ }
307
+
308
+ async getBenefitClaim(id: string): Promise<BenefitClaim> {
309
+ return this.request('GET', \`/api/v1/benefits/claims/\${id}\`);
310
+ }
311
+
312
+ async createBenefitClaim(data: BenefitClaimInput): Promise<BenefitClaim> {
313
+ return this.request('POST', '/api/v1/benefits/claims', { body: data });
314
+ }
315
+
316
+ async approveBenefitClaim(id: string, notes?: string): Promise<BenefitClaim> {
317
+ return this.request('POST', \`/api/v1/benefits/claims/\${id}/approve\`, { body: { notes } });
318
+ }
319
+
320
+ async rejectBenefitClaim(id: string, reason: string): Promise<BenefitClaim> {
321
+ return this.request('POST', \`/api/v1/benefits/claims/\${id}/reject\`, { body: { reason } });
322
+ }
323
+
324
+ async listCommissionPayouts(params?: {
325
+ memberId?: string;
326
+ status?: 'pending' | 'processing' | 'completed' | 'failed';
327
+ limit?: number;
328
+ }): Promise<PaginatedResponse<CommissionPayout>> {
329
+ return this.request('GET', '/api/v1/benefits/commissions', { params });
330
+ }
331
+
332
+ // ============ Projects ============
333
+
334
+ async listProjects(params?: {
335
+ status?: 'active' | 'completed' | 'archived';
336
+ limit?: number;
337
+ }): Promise<PaginatedResponse<{ id: string; name: string; status: string }>> {
338
+ return this.request('GET', '/api/v1/projects', { params });
339
+ }
340
+
341
+ async getProject(id: string): Promise<{ id: string; name: string; status: string; tasks: unknown[] }> {
342
+ return this.request('GET', \`/api/v1/projects/\${id}\`);
343
+ }
344
+
345
+ // ============ Internal Request Handler ============
346
+
347
+ private async request<T>(
348
+ method: string,
349
+ path: string,
350
+ options?: {
351
+ params?: Record<string, unknown>;
352
+ body?: unknown;
353
+ }
354
+ ): Promise<T> {
355
+ const url = new URL(path, this.baseUrl);
356
+
357
+ if (options?.params) {
358
+ Object.entries(options.params).forEach(([key, value]) => {
359
+ if (value !== undefined && value !== null) {
360
+ if (Array.isArray(value)) {
361
+ value.forEach(v => url.searchParams.append(key, String(v)));
362
+ } else {
363
+ url.searchParams.set(key, String(value));
364
+ }
365
+ }
366
+ });
367
+ }
368
+
369
+ const response = await fetch(url.toString(), {
370
+ method,
371
+ headers: {
372
+ 'Authorization': \`Bearer \${this.apiKey}\`,
373
+ 'Content-Type': 'application/json',
374
+ 'X-Organization-Id': this.organizationId,
375
+ },
376
+ body: options?.body ? JSON.stringify(options.body) : undefined,
377
+ });
378
+
379
+ if (!response.ok) {
380
+ const error = await response.json().catch(() => ({}));
381
+ throw new L4yercak3Error(
382
+ response.status,
383
+ error.message || \`Request failed: \${response.status}\`,
384
+ error.code,
385
+ error
386
+ );
387
+ }
388
+
389
+ // Handle 204 No Content
390
+ if (response.status === 204) {
391
+ return undefined as T;
392
+ }
393
+
394
+ return response.json();
395
+ }
396
+ }
397
+
398
+ export class L4yercak3Error extends Error {
399
+ constructor(
400
+ public status: number,
401
+ message: string,
402
+ public code?: string,
403
+ public details?: unknown
404
+ ) {
405
+ super(message);
406
+ this.name = 'L4yercak3Error';
407
+ }
408
+ }
409
+
410
+ // Singleton instance
411
+ let client: L4yercak3Client | null = null;
412
+
413
+ export function getL4yercak3Client(): L4yercak3Client {
414
+ if (!client) {
415
+ const apiKey = process.env.L4YERCAK3_API_KEY;
416
+ if (!apiKey) {
417
+ throw new Error('L4YERCAK3_API_KEY environment variable is required');
418
+ }
419
+ client = new L4yercak3Client({ apiKey });
420
+ }
421
+ return client;
422
+ }
423
+ `;
424
+ }
425
+
426
+ generateJavaScriptClient({ organizationId, backendUrl }) {
427
+ return `/**
428
+ * L4YERCAK3 API Client
429
+ * Auto-generated by @l4yercak3/cli
430
+ *
431
+ * A typed API client for the L4YERCAK3 platform.
432
+ * Organization ID: ${organizationId}
433
+ */
434
+
435
+ class L4yercak3Client {
436
+ /**
437
+ * @param {Object} config
438
+ * @param {string} config.apiKey
439
+ * @param {string} [config.baseUrl]
440
+ * @param {string} [config.organizationId]
441
+ */
442
+ constructor(config) {
443
+ this.apiKey = config.apiKey;
444
+ this.baseUrl = config.baseUrl || '${backendUrl}';
445
+ this.organizationId = config.organizationId || '${organizationId}';
446
+ }
447
+
448
+ // ============ CRM: Contacts ============
449
+
450
+ /** @returns {Promise<{items: Array, total: number, hasMore: boolean}>} */
451
+ async listContacts(params) {
452
+ return this.request('GET', '/api/v1/crm/contacts', { params });
453
+ }
454
+
455
+ async getContact(id, options) {
456
+ return this.request('GET', \`/api/v1/crm/contacts/\${id}\`, { params: options });
457
+ }
458
+
459
+ async createContact(data) {
460
+ return this.request('POST', '/api/v1/crm/contacts', { body: data });
461
+ }
462
+
463
+ async updateContact(id, data) {
464
+ return this.request('PATCH', \`/api/v1/crm/contacts/\${id}\`, { body: data });
465
+ }
466
+
467
+ async deleteContact(id) {
468
+ return this.request('DELETE', \`/api/v1/crm/contacts/\${id}\`);
469
+ }
470
+
471
+ // ============ CRM: Organizations ============
472
+
473
+ async listOrganizations(params) {
474
+ return this.request('GET', '/api/v1/crm/organizations', { params });
475
+ }
476
+
477
+ async getOrganization(id, options) {
478
+ return this.request('GET', \`/api/v1/crm/organizations/\${id}\`, { params: options });
479
+ }
480
+
481
+ async createOrganization(data) {
482
+ return this.request('POST', '/api/v1/crm/organizations', { body: data });
483
+ }
484
+
485
+ // ============ Events ============
486
+
487
+ async listEvents(params) {
488
+ return this.request('GET', '/api/v1/events', { params });
489
+ }
490
+
491
+ async getEvent(id, options) {
492
+ return this.request('GET', \`/api/v1/events/\${id}\`, { params: options });
493
+ }
494
+
495
+ async createEvent(data) {
496
+ return this.request('POST', '/api/v1/events', { body: data });
497
+ }
498
+
499
+ async getEventAttendees(eventId, params) {
500
+ return this.request('GET', \`/api/v1/events/\${eventId}/attendees\`, { params });
501
+ }
502
+
503
+ async checkInAttendee(eventId, attendeeId) {
504
+ return this.request('POST', \`/api/v1/events/\${eventId}/attendees/\${attendeeId}/check-in\`);
505
+ }
506
+
507
+ // ============ Forms ============
508
+
509
+ async listForms(params) {
510
+ return this.request('GET', '/api/v1/forms', { params });
511
+ }
512
+
513
+ async getForm(id) {
514
+ return this.request('GET', \`/api/v1/forms/\${id}\`);
515
+ }
516
+
517
+ async submitForm(formId, data) {
518
+ return this.request('POST', \`/api/v1/forms/\${formId}/submit\`, { body: data });
519
+ }
520
+
521
+ async getFormResponses(formId, params) {
522
+ return this.request('GET', \`/api/v1/forms/\${formId}/responses\`, { params });
523
+ }
524
+
525
+ // ============ Products ============
526
+
527
+ async listProducts(params) {
528
+ return this.request('GET', '/api/v1/products', { params });
529
+ }
530
+
531
+ async getProduct(id) {
532
+ return this.request('GET', \`/api/v1/products/\${id}\`);
533
+ }
534
+
535
+ // ============ Checkout ============
536
+
537
+ async createCheckoutSession(data) {
538
+ return this.request('POST', '/api/v1/checkout/sessions', { body: data });
539
+ }
540
+
541
+ async getCheckoutSession(sessionId) {
542
+ return this.request('GET', \`/api/v1/checkout/sessions/\${sessionId}\`);
543
+ }
544
+
545
+ // ============ Orders ============
546
+
547
+ async listOrders(params) {
548
+ return this.request('GET', '/api/v1/orders', { params });
549
+ }
550
+
551
+ async getOrder(id) {
552
+ return this.request('GET', \`/api/v1/orders/\${id}\`);
553
+ }
554
+
555
+ // ============ Invoicing ============
556
+
557
+ async listInvoices(params) {
558
+ return this.request('GET', '/api/v1/invoices', { params });
559
+ }
560
+
561
+ async getInvoice(id) {
562
+ return this.request('GET', \`/api/v1/invoices/\${id}\`);
563
+ }
564
+
565
+ async createInvoice(data) {
566
+ return this.request('POST', '/api/v1/invoices', { body: data });
567
+ }
568
+
569
+ async sendInvoice(id, options) {
570
+ return this.request('POST', \`/api/v1/invoices/\${id}/send\`, { body: options });
571
+ }
572
+
573
+ async markInvoicePaid(id, data) {
574
+ return this.request('POST', \`/api/v1/invoices/\${id}/mark-paid\`, { body: data });
575
+ }
576
+
577
+ // ============ Benefits ============
578
+
579
+ async listBenefitClaims(params) {
580
+ return this.request('GET', '/api/v1/benefits/claims', { params });
581
+ }
582
+
583
+ async createBenefitClaim(data) {
584
+ return this.request('POST', '/api/v1/benefits/claims', { body: data });
585
+ }
586
+
587
+ async approveBenefitClaim(id, notes) {
588
+ return this.request('POST', \`/api/v1/benefits/claims/\${id}/approve\`, { body: { notes } });
589
+ }
590
+
591
+ async rejectBenefitClaim(id, reason) {
592
+ return this.request('POST', \`/api/v1/benefits/claims/\${id}/reject\`, { body: { reason } });
593
+ }
594
+
595
+ // ============ Projects ============
596
+
597
+ async listProjects(params) {
598
+ return this.request('GET', '/api/v1/projects', { params });
599
+ }
600
+
601
+ async getProject(id) {
602
+ return this.request('GET', \`/api/v1/projects/\${id}\`);
603
+ }
604
+
605
+ // ============ Internal Request Handler ============
606
+
607
+ async request(method, path, options = {}) {
608
+ const url = new URL(path, this.baseUrl);
609
+
610
+ if (options.params) {
611
+ Object.entries(options.params).forEach(([key, value]) => {
612
+ if (value !== undefined && value !== null) {
613
+ if (Array.isArray(value)) {
614
+ value.forEach(v => url.searchParams.append(key, String(v)));
615
+ } else {
616
+ url.searchParams.set(key, String(value));
617
+ }
618
+ }
619
+ });
620
+ }
621
+
622
+ const response = await fetch(url.toString(), {
623
+ method,
624
+ headers: {
625
+ 'Authorization': \`Bearer \${this.apiKey}\`,
626
+ 'Content-Type': 'application/json',
627
+ 'X-Organization-Id': this.organizationId,
628
+ },
629
+ body: options.body ? JSON.stringify(options.body) : undefined,
630
+ });
631
+
632
+ if (!response.ok) {
633
+ const error = await response.json().catch(() => ({}));
634
+ throw new L4yercak3Error(
635
+ response.status,
636
+ error.message || \`Request failed: \${response.status}\`,
637
+ error.code,
638
+ error
639
+ );
640
+ }
641
+
642
+ if (response.status === 204) {
643
+ return undefined;
644
+ }
645
+
646
+ return response.json();
647
+ }
648
+ }
649
+
650
+ class L4yercak3Error extends Error {
651
+ constructor(status, message, code, details) {
652
+ super(message);
653
+ this.name = 'L4yercak3Error';
654
+ this.status = status;
655
+ this.code = code;
656
+ this.details = details;
657
+ }
658
+ }
659
+
660
+ // Singleton instance
661
+ let client = null;
662
+
663
+ function getL4yercak3Client() {
664
+ if (!client) {
665
+ const apiKey = process.env.L4YERCAK3_API_KEY;
666
+ if (!apiKey) {
667
+ throw new Error('L4YERCAK3_API_KEY environment variable is required');
668
+ }
669
+ client = new L4yercak3Client({ apiKey });
670
+ }
671
+ return client;
672
+ }
673
+
674
+ module.exports = {
675
+ L4yercak3Client,
676
+ L4yercak3Error,
677
+ getL4yercak3Client,
678
+ };
679
+ `;
680
+ }
681
+ }
682
+
683
+ module.exports = new ClientGenerator();