@l4yercak3/cli 1.0.6 → 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.
- package/bin/cli.js +6 -0
- package/docs/microsass_production_machine/CLI_API_REFERENCE.md +1197 -0
- package/docs/microsass_production_machine/CLI_PRODUCT_VISION.md +676 -0
- package/docs/microsass_production_machine/CLI_REQUIREMENTS.md +606 -0
- package/docs/microsass_production_machine/CONNECTED_APPLICATIONS_SPEC.md +390 -0
- package/docs/microsass_production_machine/IMPLEMENTATION_ROADMAP.md +725 -0
- package/docs/microsass_production_machine/OBJECT_MAPPINGS.md +808 -0
- package/docs/microsass_production_machine/REFERENCE_IMPLEMENTATION.md +532 -0
- package/package.json +1 -1
- package/src/api/backend-client.js +62 -0
- package/src/commands/spread.js +137 -12
- package/src/generators/api-client-generator.js +13 -6
- package/src/generators/env-generator.js +14 -1
- package/src/generators/index.js +4 -4
- package/src/generators/nextauth-generator.js +14 -9
- package/src/utils/file-utils.js +117 -0
- package/tests/api-client-generator.test.js +20 -13
- package/tests/backend-client.test.js +167 -0
- package/tests/file-utils.test.js +194 -0
- package/tests/generators-index.test.js +8 -0
- package/tests/nextauth-generator.test.js +38 -14
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
# L4YERCAK3 CLI - Reference Implementation Analysis
|
|
2
|
+
|
|
3
|
+
**Source:** HaffNet L4YerCak3 Frontend (`/.kiro/haffnet-l4yercak3/`)
|
|
4
|
+
|
|
5
|
+
This document analyzes the real-world HaffNet integration to inform CLI code generation.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
HaffNet is a **medical education platform** (CME courses) built on:
|
|
12
|
+
- **Next.js 16** + TypeScript
|
|
13
|
+
- **Convex** (frontend real-time database + auth)
|
|
14
|
+
- **L4YERCAK3 Backend** (CRM, events, checkout, invoicing)
|
|
15
|
+
|
|
16
|
+
This is the **gold standard** for how external apps should connect to L4YERCAK3.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Architecture Pattern: Dual Database
|
|
21
|
+
|
|
22
|
+
### The Key Insight
|
|
23
|
+
|
|
24
|
+
HaffNet uses **TWO databases**:
|
|
25
|
+
|
|
26
|
+
1. **Convex (Frontend)** - Fast, real-time, local to the app
|
|
27
|
+
- User authentication (sessions, passwords)
|
|
28
|
+
- CMS content (page configs, checkout instances)
|
|
29
|
+
- Local user profiles
|
|
30
|
+
|
|
31
|
+
2. **L4YERCAK3 Backend** - Business logic, shared data
|
|
32
|
+
- CRM contacts
|
|
33
|
+
- Events & products
|
|
34
|
+
- Checkout & transactions
|
|
35
|
+
- Invoicing
|
|
36
|
+
- Workflows
|
|
37
|
+
|
|
38
|
+
### Why This Pattern?
|
|
39
|
+
|
|
40
|
+
| Concern | Convex (Frontend) | L4YERCAK3 (Backend) |
|
|
41
|
+
|---------|-------------------|---------------------|
|
|
42
|
+
| **Auth speed** | ✅ < 50ms | ❌ ~200ms |
|
|
43
|
+
| **Real-time** | ✅ Native | ❌ Polling |
|
|
44
|
+
| **CRM data** | ❌ Duplication | ✅ Source of truth |
|
|
45
|
+
| **Business logic** | ❌ None | ✅ Workflows |
|
|
46
|
+
| **Invoicing** | ❌ None | ✅ Full system |
|
|
47
|
+
| **Multi-app sharing** | ❌ Isolated | ✅ Central |
|
|
48
|
+
|
|
49
|
+
### User Sync Pattern
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// STEP 1: Create Convex auth user (fast)
|
|
53
|
+
const convexUser = await convexAuth.register({
|
|
54
|
+
email, password, firstName, lastName
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// STEP 2: Create Backend user (links to CRM)
|
|
58
|
+
const backendUser = await userApi.registerUser({
|
|
59
|
+
email, firstName, lastName,
|
|
60
|
+
convexUserId: convexUser._id // Link for sync
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Result: User exists in both systems, linked by IDs
|
|
64
|
+
// convexUser._id ↔ backendUser.frontendUserId
|
|
65
|
+
// backendUser.crmContactId → CRM contact
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## API Client Structure
|
|
71
|
+
|
|
72
|
+
The CLI should generate an API client similar to HaffNet's `src/lib/api-client.ts`:
|
|
73
|
+
|
|
74
|
+
### Core Pattern
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// Environment variables
|
|
78
|
+
const API_URL = process.env.NEXT_PUBLIC_API_URL;
|
|
79
|
+
const API_KEY = process.env.NEXT_PUBLIC_API_KEY;
|
|
80
|
+
const ORG_ID = process.env.NEXT_PUBLIC_ORG_ID;
|
|
81
|
+
|
|
82
|
+
// Base fetch wrapper
|
|
83
|
+
async function apiFetch<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
|
|
84
|
+
const response = await fetch(`${API_URL}${endpoint}`, {
|
|
85
|
+
...options,
|
|
86
|
+
headers: {
|
|
87
|
+
'Content-Type': 'application/json',
|
|
88
|
+
'Authorization': `Bearer ${API_KEY}`,
|
|
89
|
+
...options.headers,
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
const error = await response.json();
|
|
95
|
+
throw new Error(error.message || `API Error: ${response.status}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return response.json();
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Module Organization
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
// Event API
|
|
106
|
+
export const eventApi = {
|
|
107
|
+
getEvents(params?: { status?, upcoming?, limit? }),
|
|
108
|
+
getEvent(eventId: string),
|
|
109
|
+
getEventProducts(eventId: string),
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// Form API
|
|
113
|
+
export const formApi = {
|
|
114
|
+
getPublicForm(formId: string),
|
|
115
|
+
submitForm({ formId, responses, metadata }),
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Checkout API (main registration flow)
|
|
119
|
+
export const checkoutApi = {
|
|
120
|
+
submitRegistration(data: RegistrationInput, checkoutInstanceId: string),
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Ticket API
|
|
124
|
+
export const ticketApi = {
|
|
125
|
+
getTicket(ticketId: string),
|
|
126
|
+
verifyTicket(ticketId: string),
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Transaction API
|
|
130
|
+
export const transactionApi = {
|
|
131
|
+
getTransaction(transactionId: string),
|
|
132
|
+
getTicketByTransaction(transactionId: string),
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// User API (sync with Backend)
|
|
136
|
+
export const userApi = {
|
|
137
|
+
registerUser(userData: {
|
|
138
|
+
email, firstName, lastName, convexUserId
|
|
139
|
+
}),
|
|
140
|
+
};
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Universal Event Pattern
|
|
146
|
+
|
|
147
|
+
The most important pattern from HaffNet is the **workflow trigger API**:
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
POST /api/v1/workflows/trigger
|
|
151
|
+
Authorization: Bearer {API_KEY}
|
|
152
|
+
|
|
153
|
+
{
|
|
154
|
+
"trigger": "registration_complete",
|
|
155
|
+
"inputData": {
|
|
156
|
+
"eventType": "seminar_registration",
|
|
157
|
+
"source": "haffnet_website",
|
|
158
|
+
"eventId": "event_xxx",
|
|
159
|
+
|
|
160
|
+
"customerData": {
|
|
161
|
+
"email": "user@example.com",
|
|
162
|
+
"firstName": "John",
|
|
163
|
+
"lastName": "Doe",
|
|
164
|
+
"phone": "+49123456789",
|
|
165
|
+
"organization": "Hospital Name"
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
"formResponses": {
|
|
169
|
+
"specialty": "Cardiology",
|
|
170
|
+
"dietary_requirements": "vegetarian"
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
"transactionData": {
|
|
174
|
+
"productId": "product_xxx",
|
|
175
|
+
"price": 0,
|
|
176
|
+
"currency": "EUR"
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
**Response:**
|
|
183
|
+
```typescript
|
|
184
|
+
{
|
|
185
|
+
"success": true,
|
|
186
|
+
"ticketId": "ticket_xxx",
|
|
187
|
+
"invoiceId": "invoice_xxx",
|
|
188
|
+
"crmContactId": "contact_xxx",
|
|
189
|
+
"frontendUserId": "user_xxx",
|
|
190
|
+
"isGuestRegistration": true,
|
|
191
|
+
"downloadUrls": {
|
|
192
|
+
"tickets": "https://...",
|
|
193
|
+
"invoice": "https://..."
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## Type Definitions to Generate
|
|
201
|
+
|
|
202
|
+
### Event Type
|
|
203
|
+
```typescript
|
|
204
|
+
interface Event {
|
|
205
|
+
id: string;
|
|
206
|
+
name: string;
|
|
207
|
+
description?: string;
|
|
208
|
+
subtype: string; // "symposium", "workshop", etc.
|
|
209
|
+
status: string; // "draft", "published", "completed"
|
|
210
|
+
|
|
211
|
+
// Flattened from customProperties
|
|
212
|
+
startDate?: number;
|
|
213
|
+
endDate?: number;
|
|
214
|
+
location?: string;
|
|
215
|
+
capacity?: number;
|
|
216
|
+
registrations?: number;
|
|
217
|
+
registrationFormId?: string;
|
|
218
|
+
checkoutInstanceId?: string;
|
|
219
|
+
agenda?: AgendaItem[];
|
|
220
|
+
|
|
221
|
+
// Legacy nested format
|
|
222
|
+
customProperties?: {
|
|
223
|
+
startDate?: number;
|
|
224
|
+
endDate?: number;
|
|
225
|
+
location?: string;
|
|
226
|
+
venue?: string;
|
|
227
|
+
address?: Address;
|
|
228
|
+
registration?: {
|
|
229
|
+
enabled: boolean;
|
|
230
|
+
openDate: number;
|
|
231
|
+
closeDate: number;
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Product Type
|
|
238
|
+
```typescript
|
|
239
|
+
interface Product {
|
|
240
|
+
id: string;
|
|
241
|
+
name: string;
|
|
242
|
+
description: string;
|
|
243
|
+
type: string;
|
|
244
|
+
subtype: string;
|
|
245
|
+
status: string;
|
|
246
|
+
customProperties: {
|
|
247
|
+
price: number;
|
|
248
|
+
currency: string;
|
|
249
|
+
sold: number;
|
|
250
|
+
categoryCode: string;
|
|
251
|
+
categoryLabel: string;
|
|
252
|
+
invoiceConfig?: {
|
|
253
|
+
employerSourceField: string;
|
|
254
|
+
employerMapping: Record<string, string>;
|
|
255
|
+
defaultPaymentTerms: string;
|
|
256
|
+
};
|
|
257
|
+
addons: ProductAddon[];
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Registration Input
|
|
263
|
+
```typescript
|
|
264
|
+
interface RegistrationInput {
|
|
265
|
+
eventId: string;
|
|
266
|
+
formId: string;
|
|
267
|
+
|
|
268
|
+
products: Array<{
|
|
269
|
+
productId: string;
|
|
270
|
+
quantity: number;
|
|
271
|
+
}>;
|
|
272
|
+
|
|
273
|
+
customerData: {
|
|
274
|
+
email: string;
|
|
275
|
+
firstName: string;
|
|
276
|
+
lastName: string;
|
|
277
|
+
phone?: string;
|
|
278
|
+
salutation?: string;
|
|
279
|
+
title?: string;
|
|
280
|
+
organization?: string;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
formResponses: Record<string, unknown>;
|
|
284
|
+
|
|
285
|
+
transactionData: {
|
|
286
|
+
currency: string;
|
|
287
|
+
breakdown: {
|
|
288
|
+
basePrice: number;
|
|
289
|
+
addons?: Array<{
|
|
290
|
+
id: string;
|
|
291
|
+
name: string;
|
|
292
|
+
quantity: number;
|
|
293
|
+
pricePerUnit: number;
|
|
294
|
+
total: number;
|
|
295
|
+
}>;
|
|
296
|
+
subtotal: number;
|
|
297
|
+
tax?: number;
|
|
298
|
+
total: number;
|
|
299
|
+
};
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
metadata?: {
|
|
303
|
+
source: string;
|
|
304
|
+
ipAddress?: string;
|
|
305
|
+
userAgent?: string;
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Files CLI Should Generate
|
|
313
|
+
|
|
314
|
+
Based on HaffNet, the CLI `spread` command should generate:
|
|
315
|
+
|
|
316
|
+
### 1. API Client (`src/lib/layercake.ts`)
|
|
317
|
+
```
|
|
318
|
+
- Base fetch wrapper with auth
|
|
319
|
+
- Event API module
|
|
320
|
+
- Form API module
|
|
321
|
+
- Checkout API module
|
|
322
|
+
- Ticket API module
|
|
323
|
+
- Transaction API module
|
|
324
|
+
- User API module (if auth feature selected)
|
|
325
|
+
- Full TypeScript types
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### 2. Environment File (`.env.local`)
|
|
329
|
+
```bash
|
|
330
|
+
# L4YERCAK3 Backend
|
|
331
|
+
NEXT_PUBLIC_API_URL=https://agreeable-lion-828.convex.site/api/v1
|
|
332
|
+
NEXT_PUBLIC_API_KEY=org_xxx_yyy
|
|
333
|
+
NEXT_PUBLIC_ORG_ID=xxx
|
|
334
|
+
|
|
335
|
+
# Optional: Convex (if dual-db pattern)
|
|
336
|
+
NEXT_PUBLIC_CONVEX_URL=https://xxx.convex.cloud
|
|
337
|
+
CONVEX_DEPLOYMENT=dev:xxx
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### 3. Auth Integration (`src/lib/layercake-auth.ts`)
|
|
341
|
+
```typescript
|
|
342
|
+
// Only if OAuth feature selected
|
|
343
|
+
// User sync between frontend auth and Backend CRM
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### 4. Hooks (`src/hooks/useLayercake.ts`)
|
|
347
|
+
```typescript
|
|
348
|
+
// React hooks for common operations
|
|
349
|
+
useEvents()
|
|
350
|
+
useEvent(id)
|
|
351
|
+
useCheckout()
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### 5. Types (`src/types/layercake.ts`)
|
|
355
|
+
```typescript
|
|
356
|
+
// All type definitions
|
|
357
|
+
Event, Product, Ticket, Transaction, Form, etc.
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## Feature Flags from HaffNet
|
|
363
|
+
|
|
364
|
+
Features that can be enabled/disabled:
|
|
365
|
+
|
|
366
|
+
| Feature | Files Generated |
|
|
367
|
+
|---------|-----------------|
|
|
368
|
+
| **events** | Event API, Event types |
|
|
369
|
+
| **forms** | Form API, Form types |
|
|
370
|
+
| **checkout** | Checkout API, Registration types |
|
|
371
|
+
| **tickets** | Ticket API, Ticket types |
|
|
372
|
+
| **invoicing** | Invoice types (accessed via checkout) |
|
|
373
|
+
| **user-sync** | User API, Auth sync hooks |
|
|
374
|
+
| **crm** | Contact API, CRM types |
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Convex CMS Pattern
|
|
379
|
+
|
|
380
|
+
HaffNet uses Convex for CMS content. The CLI could generate:
|
|
381
|
+
|
|
382
|
+
### convex-client.ts
|
|
383
|
+
```typescript
|
|
384
|
+
import { useQuery } from 'convex/react';
|
|
385
|
+
import { api } from '@convex/_generated/api';
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Get published page content from CMS
|
|
389
|
+
*/
|
|
390
|
+
export async function getPageContent(orgSlug: string, pagePath: string) {
|
|
391
|
+
// Fetch from Convex CMS
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Extract checkout instance ID from page content
|
|
396
|
+
*/
|
|
397
|
+
export function getCheckoutInstanceId(pageContent: PageContent): string | null {
|
|
398
|
+
return pageContent?.content?.checkout?.checkoutInstanceId || null;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Check if page has checkout configured
|
|
403
|
+
*/
|
|
404
|
+
export function hasCheckoutConfigured(pageContent: PageContent): boolean {
|
|
405
|
+
return !!getCheckoutInstanceId(pageContent);
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
411
|
+
## Error Handling Patterns
|
|
412
|
+
|
|
413
|
+
From HaffNet:
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
// API Error class
|
|
417
|
+
export class ApiError extends Error {
|
|
418
|
+
constructor(
|
|
419
|
+
message: string,
|
|
420
|
+
public code?: string,
|
|
421
|
+
public errors?: Array<{ field: string; message: string }>
|
|
422
|
+
) {
|
|
423
|
+
super(message);
|
|
424
|
+
this.name = 'ApiError';
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Response handling
|
|
429
|
+
async function apiFetch<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
|
|
430
|
+
// Check content type before parsing
|
|
431
|
+
const contentType = response.headers.get('content-type');
|
|
432
|
+
const isJson = contentType?.includes('application/json');
|
|
433
|
+
|
|
434
|
+
if (!response.ok) {
|
|
435
|
+
if (isJson) {
|
|
436
|
+
const errorData = await response.json();
|
|
437
|
+
throw new Error(errorData.message || `API Error: ${response.status}`);
|
|
438
|
+
} else {
|
|
439
|
+
const errorText = await response.text();
|
|
440
|
+
console.error('API Error Response (non-JSON):', errorText.substring(0, 500));
|
|
441
|
+
throw new Error(`API Error: ${response.status}`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (!isJson) {
|
|
446
|
+
throw new Error('API returned non-JSON response');
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return response.json();
|
|
450
|
+
}
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## Security Practices
|
|
456
|
+
|
|
457
|
+
From HaffNet:
|
|
458
|
+
|
|
459
|
+
1. **API key in server-side only**
|
|
460
|
+
- Use `NEXT_PUBLIC_` prefix carefully
|
|
461
|
+
- Server actions for sensitive operations
|
|
462
|
+
|
|
463
|
+
2. **Honeypot spam protection**
|
|
464
|
+
```typescript
|
|
465
|
+
body: JSON.stringify({
|
|
466
|
+
responses: data.responses,
|
|
467
|
+
bot_trap: '', // Must be empty for legitimate submissions
|
|
468
|
+
})
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
3. **Rate limiting awareness**
|
|
472
|
+
- Document: 5 submissions/hour per IP for public forms
|
|
473
|
+
|
|
474
|
+
4. **Input validation**
|
|
475
|
+
- Zod schemas before API calls
|
|
476
|
+
- Field-level error messages
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
480
|
+
## Documentation Structure
|
|
481
|
+
|
|
482
|
+
HaffNet has 25+ documentation files. Key ones:
|
|
483
|
+
|
|
484
|
+
| Document | Purpose |
|
|
485
|
+
|----------|---------|
|
|
486
|
+
| `README.md` | Overview & navigation |
|
|
487
|
+
| `QUICK_REFERENCE.md` | One-page cheat sheet |
|
|
488
|
+
| `API_SPECIFICATION.md` | Complete API reference |
|
|
489
|
+
| `UNIVERSAL_EVENT_PAYLOAD.md` | Event structure |
|
|
490
|
+
| `FRONTEND_INTEGRATION.md` | Code examples |
|
|
491
|
+
| `USER_SYNC_ARCHITECTURE.md` | Dual-DB pattern |
|
|
492
|
+
|
|
493
|
+
**CLI should generate similar docs for each project.**
|
|
494
|
+
|
|
495
|
+
---
|
|
496
|
+
|
|
497
|
+
## CLI Generation Templates
|
|
498
|
+
|
|
499
|
+
Based on HaffNet, create templates:
|
|
500
|
+
|
|
501
|
+
### Template: `api-client.template.ts`
|
|
502
|
+
Full API client with all modules
|
|
503
|
+
|
|
504
|
+
### Template: `types.template.ts`
|
|
505
|
+
All TypeScript type definitions
|
|
506
|
+
|
|
507
|
+
### Template: `hooks.template.ts`
|
|
508
|
+
React hooks for data fetching
|
|
509
|
+
|
|
510
|
+
### Template: `auth-sync.template.ts`
|
|
511
|
+
User sync between auth systems
|
|
512
|
+
|
|
513
|
+
### Template: `README.template.md`
|
|
514
|
+
Project-specific documentation
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## Summary: What CLI Should Do
|
|
519
|
+
|
|
520
|
+
1. **Detect** existing project structure (Next.js, Convex, etc.)
|
|
521
|
+
2. **Ask** which features to enable (events, forms, checkout, etc.)
|
|
522
|
+
3. **Generate** API client with only needed modules
|
|
523
|
+
4. **Generate** TypeScript types for enabled features
|
|
524
|
+
5. **Generate** hooks for common operations
|
|
525
|
+
6. **Generate** auth sync if user-sync enabled
|
|
526
|
+
7. **Create** environment template
|
|
527
|
+
8. **Create** documentation for the integration
|
|
528
|
+
9. **Register** the connection in L4YERCAK3 backend
|
|
529
|
+
|
|
530
|
+
---
|
|
531
|
+
|
|
532
|
+
*This analysis based on production code from HaffNet L4YerCak3 Frontend*
|
package/package.json
CHANGED
|
@@ -167,6 +167,68 @@ class BackendClient {
|
|
|
167
167
|
name,
|
|
168
168
|
});
|
|
169
169
|
}
|
|
170
|
+
|
|
171
|
+
// ============================================
|
|
172
|
+
// Connected Applications API
|
|
173
|
+
// ============================================
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Check if an application already exists for this project path
|
|
177
|
+
* @param {string} organizationId - The organization ID
|
|
178
|
+
* @param {string} projectPathHash - SHA256 hash of the project path
|
|
179
|
+
* @returns {Promise<{found: boolean, application?: object}>}
|
|
180
|
+
*/
|
|
181
|
+
async checkExistingApplication(organizationId, projectPathHash) {
|
|
182
|
+
try {
|
|
183
|
+
return await this.request(
|
|
184
|
+
'GET',
|
|
185
|
+
`/api/v1/cli/applications/by-path?organizationId=${organizationId}&hash=${projectPathHash}`
|
|
186
|
+
);
|
|
187
|
+
} catch (error) {
|
|
188
|
+
// If 404, no existing app found
|
|
189
|
+
if (error.status === 404) {
|
|
190
|
+
return { found: false };
|
|
191
|
+
}
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Register a new connected application
|
|
198
|
+
* @param {object} data - Application registration data
|
|
199
|
+
* @returns {Promise<{applicationId: string, apiKey?: object, backendUrl: string}>}
|
|
200
|
+
*/
|
|
201
|
+
async registerApplication(data) {
|
|
202
|
+
return await this.request('POST', '/api/v1/cli/applications', data);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Update an existing connected application
|
|
207
|
+
* @param {string} applicationId - The application ID
|
|
208
|
+
* @param {object} updates - Fields to update
|
|
209
|
+
* @returns {Promise<object>}
|
|
210
|
+
*/
|
|
211
|
+
async updateApplication(applicationId, updates) {
|
|
212
|
+
return await this.request('PATCH', `/api/v1/cli/applications/${applicationId}`, updates);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Get application details
|
|
217
|
+
* @param {string} applicationId - The application ID
|
|
218
|
+
* @returns {Promise<object>}
|
|
219
|
+
*/
|
|
220
|
+
async getApplication(applicationId) {
|
|
221
|
+
return await this.request('GET', `/api/v1/cli/applications/${applicationId}`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* List all connected applications for an organization
|
|
226
|
+
* @param {string} organizationId - The organization ID
|
|
227
|
+
* @returns {Promise<{applications: object[]}>}
|
|
228
|
+
*/
|
|
229
|
+
async listApplications(organizationId) {
|
|
230
|
+
return await this.request('GET', `/api/v1/cli/applications?organizationId=${organizationId}`);
|
|
231
|
+
}
|
|
170
232
|
}
|
|
171
233
|
|
|
172
234
|
module.exports = new BackendClient();
|