@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.
- package/docs/INTEGRATION_PATHS_ARCHITECTURE.md +1543 -0
- package/package.json +1 -1
- package/src/commands/spread.js +101 -6
- package/src/detectors/database-detector.js +245 -0
- package/src/detectors/index.js +17 -4
- package/src/generators/api-only/client.js +683 -0
- package/src/generators/api-only/index.js +96 -0
- package/src/generators/api-only/types.js +618 -0
- package/src/generators/api-only/webhooks.js +377 -0
- package/src/generators/index.js +88 -2
- package/src/generators/mcp-guide-generator.js +256 -0
- package/src/generators/quickstart/components/index.js +1699 -0
- package/src/generators/quickstart/database/convex.js +1257 -0
- package/src/generators/quickstart/database/index.js +34 -0
- package/src/generators/quickstart/database/supabase.js +1132 -0
- package/src/generators/quickstart/hooks/index.js +1047 -0
- package/src/generators/quickstart/index.js +151 -0
- package/src/generators/quickstart/pages/index.js +1466 -0
- package/src/mcp/registry/domains/benefits.js +798 -0
- package/src/mcp/registry/index.js +2 -0
- package/tests/database-detector.test.js +221 -0
- package/tests/generators-index.test.js +215 -3
|
@@ -16,6 +16,7 @@ const eventsDomain = require('./domains/events');
|
|
|
16
16
|
const formsDomain = require('./domains/forms');
|
|
17
17
|
const codegenDomain = require('./domains/codegen');
|
|
18
18
|
const applicationsDomain = require('./domains/applications');
|
|
19
|
+
const benefitsDomain = require('./domains/benefits');
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* @typedef {Object} ToolDefinition
|
|
@@ -44,6 +45,7 @@ const toolDomains = [
|
|
|
44
45
|
crmDomain,
|
|
45
46
|
eventsDomain,
|
|
46
47
|
formsDomain,
|
|
48
|
+
benefitsDomain,
|
|
47
49
|
codegenDomain,
|
|
48
50
|
];
|
|
49
51
|
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Database Detector
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
jest.mock('fs');
|
|
9
|
+
|
|
10
|
+
const databaseDetector = require('../src/detectors/database-detector');
|
|
11
|
+
|
|
12
|
+
describe('DatabaseDetector', () => {
|
|
13
|
+
const mockProjectPath = '/test/project';
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
jest.clearAllMocks();
|
|
17
|
+
fs.existsSync.mockReturnValue(false);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('detect', () => {
|
|
21
|
+
it('returns hasDatabase false when no database detected', () => {
|
|
22
|
+
fs.existsSync.mockReturnValue(false);
|
|
23
|
+
|
|
24
|
+
const result = databaseDetector.detect(mockProjectPath);
|
|
25
|
+
|
|
26
|
+
expect(result.hasDatabase).toBe(false);
|
|
27
|
+
expect(result.detections).toHaveLength(0);
|
|
28
|
+
expect(result.primary).toBeNull();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('detects Convex from directory', () => {
|
|
32
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
33
|
+
return filePath === path.join(mockProjectPath, 'convex') ||
|
|
34
|
+
filePath === path.join(mockProjectPath, 'convex', 'schema.ts');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const result = databaseDetector.detect(mockProjectPath);
|
|
38
|
+
|
|
39
|
+
expect(result.hasDatabase).toBe(true);
|
|
40
|
+
expect(result.primary.type).toBe('convex');
|
|
41
|
+
expect(result.primary.confidence).toBe('high');
|
|
42
|
+
expect(result.primary.hasSchema).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('detects Supabase from directory', () => {
|
|
46
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
47
|
+
return filePath === path.join(mockProjectPath, 'supabase') ||
|
|
48
|
+
filePath === path.join(mockProjectPath, 'supabase', 'migrations');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const result = databaseDetector.detect(mockProjectPath);
|
|
52
|
+
|
|
53
|
+
expect(result.hasDatabase).toBe(true);
|
|
54
|
+
expect(result.primary.type).toBe('supabase');
|
|
55
|
+
expect(result.primary.confidence).toBe('high');
|
|
56
|
+
expect(result.primary.hasMigrations).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('detects Prisma from directory', () => {
|
|
60
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
61
|
+
return filePath === path.join(mockProjectPath, 'prisma') ||
|
|
62
|
+
filePath === path.join(mockProjectPath, 'prisma', 'schema.prisma');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const result = databaseDetector.detect(mockProjectPath);
|
|
66
|
+
|
|
67
|
+
expect(result.hasDatabase).toBe(true);
|
|
68
|
+
expect(result.primary.type).toBe('prisma');
|
|
69
|
+
expect(result.primary.confidence).toBe('high');
|
|
70
|
+
expect(result.primary.hasSchema).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('detects Drizzle from config file', () => {
|
|
74
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
75
|
+
return filePath === path.join(mockProjectPath, 'drizzle.config.ts');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const result = databaseDetector.detect(mockProjectPath);
|
|
79
|
+
|
|
80
|
+
expect(result.hasDatabase).toBe(true);
|
|
81
|
+
expect(result.primary.type).toBe('drizzle');
|
|
82
|
+
expect(result.primary.confidence).toBe('high');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('detects database from package.json dependencies', () => {
|
|
86
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
87
|
+
return filePath === path.join(mockProjectPath, 'package.json');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
fs.readFileSync.mockReturnValue(JSON.stringify({
|
|
91
|
+
dependencies: {
|
|
92
|
+
'convex': '^1.0.0',
|
|
93
|
+
},
|
|
94
|
+
}));
|
|
95
|
+
|
|
96
|
+
const result = databaseDetector.detect(mockProjectPath);
|
|
97
|
+
|
|
98
|
+
expect(result.hasDatabase).toBe(true);
|
|
99
|
+
expect(result.primary.type).toBe('convex');
|
|
100
|
+
expect(result.primary.confidence).toBe('medium');
|
|
101
|
+
expect(result.primary.source).toBe('package.json');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('detects Supabase from package.json', () => {
|
|
105
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
106
|
+
return filePath === path.join(mockProjectPath, 'package.json');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
fs.readFileSync.mockReturnValue(JSON.stringify({
|
|
110
|
+
dependencies: {
|
|
111
|
+
'@supabase/supabase-js': '^2.0.0',
|
|
112
|
+
},
|
|
113
|
+
}));
|
|
114
|
+
|
|
115
|
+
const result = databaseDetector.detect(mockProjectPath);
|
|
116
|
+
|
|
117
|
+
expect(result.hasDatabase).toBe(true);
|
|
118
|
+
expect(result.primary.type).toBe('supabase');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('detects MongoDB/Mongoose from package.json', () => {
|
|
122
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
123
|
+
return filePath === path.join(mockProjectPath, 'package.json');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
fs.readFileSync.mockReturnValue(JSON.stringify({
|
|
127
|
+
dependencies: {
|
|
128
|
+
'mongoose': '^7.0.0',
|
|
129
|
+
},
|
|
130
|
+
}));
|
|
131
|
+
|
|
132
|
+
const result = databaseDetector.detect(mockProjectPath);
|
|
133
|
+
|
|
134
|
+
expect(result.hasDatabase).toBe(true);
|
|
135
|
+
expect(result.primary.type).toBe('mongodb');
|
|
136
|
+
expect(result.primary.details.client).toBe('mongoose');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('detects Firebase from package.json', () => {
|
|
140
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
141
|
+
return filePath === path.join(mockProjectPath, 'package.json');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
fs.readFileSync.mockReturnValue(JSON.stringify({
|
|
145
|
+
dependencies: {
|
|
146
|
+
'firebase': '^9.0.0',
|
|
147
|
+
},
|
|
148
|
+
}));
|
|
149
|
+
|
|
150
|
+
const result = databaseDetector.detect(mockProjectPath);
|
|
151
|
+
|
|
152
|
+
expect(result.hasDatabase).toBe(true);
|
|
153
|
+
expect(result.primary.type).toBe('firebase');
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('prioritizes high confidence detections', () => {
|
|
157
|
+
// Both directory and package.json detected
|
|
158
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
159
|
+
return filePath === path.join(mockProjectPath, 'convex') ||
|
|
160
|
+
filePath === path.join(mockProjectPath, 'package.json');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
fs.readFileSync.mockReturnValue(JSON.stringify({
|
|
164
|
+
dependencies: {
|
|
165
|
+
'mongoose': '^7.0.0',
|
|
166
|
+
},
|
|
167
|
+
}));
|
|
168
|
+
|
|
169
|
+
const result = databaseDetector.detect(mockProjectPath);
|
|
170
|
+
|
|
171
|
+
expect(result.hasDatabase).toBe(true);
|
|
172
|
+
// Convex directory detection should be primary (high confidence)
|
|
173
|
+
expect(result.primary.type).toBe('convex');
|
|
174
|
+
expect(result.primary.confidence).toBe('high');
|
|
175
|
+
// MongoDB should also be in detections
|
|
176
|
+
expect(result.detections.some(d => d.type === 'mongodb')).toBe(true);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('returns multiple detections when multiple databases found', () => {
|
|
180
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
181
|
+
return filePath === path.join(mockProjectPath, 'package.json');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
fs.readFileSync.mockReturnValue(JSON.stringify({
|
|
185
|
+
dependencies: {
|
|
186
|
+
'convex': '^1.0.0',
|
|
187
|
+
'mongoose': '^7.0.0',
|
|
188
|
+
},
|
|
189
|
+
}));
|
|
190
|
+
|
|
191
|
+
const result = databaseDetector.detect(mockProjectPath);
|
|
192
|
+
|
|
193
|
+
expect(result.hasDatabase).toBe(true);
|
|
194
|
+
expect(result.detections.length).toBeGreaterThan(1);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('handles malformed package.json gracefully', () => {
|
|
198
|
+
fs.existsSync.mockImplementation((filePath) => {
|
|
199
|
+
return filePath === path.join(mockProjectPath, 'package.json');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
fs.readFileSync.mockReturnValue('not valid json');
|
|
203
|
+
|
|
204
|
+
const result = databaseDetector.detect(mockProjectPath);
|
|
205
|
+
|
|
206
|
+
expect(result.hasDatabase).toBe(false);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('uses current directory as default', () => {
|
|
210
|
+
const originalCwd = process.cwd;
|
|
211
|
+
process.cwd = jest.fn().mockReturnValue(mockProjectPath);
|
|
212
|
+
|
|
213
|
+
fs.existsSync.mockReturnValue(false);
|
|
214
|
+
|
|
215
|
+
const result = databaseDetector.detect();
|
|
216
|
+
|
|
217
|
+
expect(result.hasDatabase).toBe(false);
|
|
218
|
+
process.cwd = originalCwd;
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
});
|
|
@@ -27,7 +27,7 @@ describe('FileGenerator', () => {
|
|
|
27
27
|
fs.readFileSync.mockReturnValue('');
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
describe('generate', () => {
|
|
30
|
+
describe('legacy generate (backward compatibility)', () => {
|
|
31
31
|
it('returns results object with expected structure', async () => {
|
|
32
32
|
const options = {
|
|
33
33
|
projectPath: mockProjectPath,
|
|
@@ -38,6 +38,7 @@ describe('FileGenerator', () => {
|
|
|
38
38
|
oauthProviders: [],
|
|
39
39
|
isTypeScript: false,
|
|
40
40
|
routerType: 'app',
|
|
41
|
+
integrationPath: 'legacy', // Use legacy path for backward compatibility tests
|
|
41
42
|
};
|
|
42
43
|
|
|
43
44
|
const result = await FileGenerator.generate(options);
|
|
@@ -58,6 +59,7 @@ describe('FileGenerator', () => {
|
|
|
58
59
|
features: ['crm'],
|
|
59
60
|
oauthProviders: [],
|
|
60
61
|
isTypeScript: false,
|
|
62
|
+
integrationPath: 'legacy',
|
|
61
63
|
};
|
|
62
64
|
|
|
63
65
|
const result = await FileGenerator.generate(options);
|
|
@@ -75,6 +77,7 @@ describe('FileGenerator', () => {
|
|
|
75
77
|
features: [],
|
|
76
78
|
oauthProviders: [],
|
|
77
79
|
isTypeScript: false,
|
|
80
|
+
integrationPath: 'legacy',
|
|
78
81
|
};
|
|
79
82
|
|
|
80
83
|
const result = await FileGenerator.generate(options);
|
|
@@ -90,6 +93,7 @@ describe('FileGenerator', () => {
|
|
|
90
93
|
organizationId: 'org-123',
|
|
91
94
|
features: [],
|
|
92
95
|
oauthProviders: [],
|
|
96
|
+
integrationPath: 'legacy',
|
|
93
97
|
};
|
|
94
98
|
|
|
95
99
|
const result = await FileGenerator.generate(options);
|
|
@@ -108,7 +112,8 @@ describe('FileGenerator', () => {
|
|
|
108
112
|
oauthProviders: ['google'],
|
|
109
113
|
isTypeScript: false,
|
|
110
114
|
routerType: 'app',
|
|
111
|
-
frameworkType: 'nextjs',
|
|
115
|
+
frameworkType: 'nextjs',
|
|
116
|
+
integrationPath: 'legacy',
|
|
112
117
|
};
|
|
113
118
|
|
|
114
119
|
const result = await FileGenerator.generate(options);
|
|
@@ -126,6 +131,7 @@ describe('FileGenerator', () => {
|
|
|
126
131
|
oauthProviders: ['google'],
|
|
127
132
|
isTypeScript: true,
|
|
128
133
|
frameworkType: 'expo',
|
|
134
|
+
integrationPath: 'legacy',
|
|
129
135
|
};
|
|
130
136
|
|
|
131
137
|
const result = await FileGenerator.generate(options);
|
|
@@ -143,6 +149,7 @@ describe('FileGenerator', () => {
|
|
|
143
149
|
oauthProviders: ['google'],
|
|
144
150
|
isTypeScript: false,
|
|
145
151
|
routerType: 'app',
|
|
152
|
+
integrationPath: 'legacy',
|
|
146
153
|
};
|
|
147
154
|
|
|
148
155
|
const result = await FileGenerator.generate(options);
|
|
@@ -160,6 +167,7 @@ describe('FileGenerator', () => {
|
|
|
160
167
|
oauthProviders: null,
|
|
161
168
|
isTypeScript: false,
|
|
162
169
|
routerType: 'app',
|
|
170
|
+
integrationPath: 'legacy',
|
|
163
171
|
};
|
|
164
172
|
|
|
165
173
|
const result = await FileGenerator.generate(options);
|
|
@@ -177,6 +185,7 @@ describe('FileGenerator', () => {
|
|
|
177
185
|
oauthProviders: ['google'],
|
|
178
186
|
productionDomain: 'example.com',
|
|
179
187
|
appName: 'Test App',
|
|
188
|
+
integrationPath: 'legacy',
|
|
180
189
|
};
|
|
181
190
|
|
|
182
191
|
const result = await FileGenerator.generate(options);
|
|
@@ -193,6 +202,7 @@ describe('FileGenerator', () => {
|
|
|
193
202
|
organizationId: 'org-123',
|
|
194
203
|
features: ['crm'],
|
|
195
204
|
oauthProviders: [],
|
|
205
|
+
integrationPath: 'legacy',
|
|
196
206
|
};
|
|
197
207
|
|
|
198
208
|
const result = await FileGenerator.generate(options);
|
|
@@ -208,11 +218,11 @@ describe('FileGenerator', () => {
|
|
|
208
218
|
organizationId: 'org-123',
|
|
209
219
|
features: [],
|
|
210
220
|
oauthProviders: [],
|
|
221
|
+
integrationPath: 'legacy',
|
|
211
222
|
};
|
|
212
223
|
|
|
213
224
|
const result = await FileGenerator.generate(options);
|
|
214
225
|
|
|
215
|
-
// gitignore generator returns path or null depending on if update needed
|
|
216
226
|
expect(result).toHaveProperty('gitignore');
|
|
217
227
|
});
|
|
218
228
|
|
|
@@ -229,6 +239,7 @@ describe('FileGenerator', () => {
|
|
|
229
239
|
productionDomain: 'example.com',
|
|
230
240
|
appName: 'Full App',
|
|
231
241
|
frameworkType: 'nextjs',
|
|
242
|
+
integrationPath: 'legacy',
|
|
232
243
|
};
|
|
233
244
|
|
|
234
245
|
const result = await FileGenerator.generate(options);
|
|
@@ -239,4 +250,205 @@ describe('FileGenerator', () => {
|
|
|
239
250
|
expect(result.oauthGuide).not.toBeNull();
|
|
240
251
|
});
|
|
241
252
|
});
|
|
253
|
+
|
|
254
|
+
describe('api-only integration path', () => {
|
|
255
|
+
it('returns results object with expected structure', async () => {
|
|
256
|
+
const options = {
|
|
257
|
+
projectPath: mockProjectPath,
|
|
258
|
+
apiKey: 'test-key',
|
|
259
|
+
backendUrl: 'https://backend.test.com',
|
|
260
|
+
organizationId: 'org-123',
|
|
261
|
+
features: ['crm'],
|
|
262
|
+
oauthProviders: [],
|
|
263
|
+
isTypeScript: true,
|
|
264
|
+
integrationPath: 'api-only',
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
const result = await FileGenerator.generate(options);
|
|
268
|
+
|
|
269
|
+
expect(result).toHaveProperty('apiClient');
|
|
270
|
+
expect(result).toHaveProperty('types');
|
|
271
|
+
expect(result).toHaveProperty('webhooks');
|
|
272
|
+
expect(result).toHaveProperty('index');
|
|
273
|
+
expect(result).toHaveProperty('envFile');
|
|
274
|
+
expect(result).toHaveProperty('gitignore');
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('generates typed client file', async () => {
|
|
278
|
+
const options = {
|
|
279
|
+
projectPath: mockProjectPath,
|
|
280
|
+
apiKey: 'test-key',
|
|
281
|
+
backendUrl: 'https://backend.test.com',
|
|
282
|
+
organizationId: 'org-123',
|
|
283
|
+
features: ['crm'],
|
|
284
|
+
oauthProviders: [],
|
|
285
|
+
isTypeScript: true,
|
|
286
|
+
integrationPath: 'api-only',
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const result = await FileGenerator.generate(options);
|
|
290
|
+
|
|
291
|
+
expect(result.apiClient).not.toBeNull();
|
|
292
|
+
expect(result.apiClient).toContain('client.ts');
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('generates types file for TypeScript projects', async () => {
|
|
296
|
+
const options = {
|
|
297
|
+
projectPath: mockProjectPath,
|
|
298
|
+
apiKey: 'test-key',
|
|
299
|
+
backendUrl: 'https://backend.test.com',
|
|
300
|
+
organizationId: 'org-123',
|
|
301
|
+
features: ['crm'],
|
|
302
|
+
oauthProviders: [],
|
|
303
|
+
isTypeScript: true,
|
|
304
|
+
integrationPath: 'api-only',
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const result = await FileGenerator.generate(options);
|
|
308
|
+
|
|
309
|
+
expect(result.types).not.toBeNull();
|
|
310
|
+
expect(result.types).toContain('types.ts');
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
it('does not generate types file for JavaScript projects', async () => {
|
|
314
|
+
const options = {
|
|
315
|
+
projectPath: mockProjectPath,
|
|
316
|
+
apiKey: 'test-key',
|
|
317
|
+
backendUrl: 'https://backend.test.com',
|
|
318
|
+
organizationId: 'org-123',
|
|
319
|
+
features: ['crm'],
|
|
320
|
+
oauthProviders: [],
|
|
321
|
+
isTypeScript: false,
|
|
322
|
+
integrationPath: 'api-only',
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const result = await FileGenerator.generate(options);
|
|
326
|
+
|
|
327
|
+
expect(result.types).toBeNull();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('generates webhooks utility file', async () => {
|
|
331
|
+
const options = {
|
|
332
|
+
projectPath: mockProjectPath,
|
|
333
|
+
apiKey: 'test-key',
|
|
334
|
+
backendUrl: 'https://backend.test.com',
|
|
335
|
+
organizationId: 'org-123',
|
|
336
|
+
features: ['crm'],
|
|
337
|
+
oauthProviders: [],
|
|
338
|
+
isTypeScript: true,
|
|
339
|
+
integrationPath: 'api-only',
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const result = await FileGenerator.generate(options);
|
|
343
|
+
|
|
344
|
+
expect(result.webhooks).not.toBeNull();
|
|
345
|
+
expect(result.webhooks).toContain('webhooks.ts');
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
describe('quickstart integration path', () => {
|
|
350
|
+
it('returns results object with expected structure', async () => {
|
|
351
|
+
const options = {
|
|
352
|
+
projectPath: mockProjectPath,
|
|
353
|
+
apiKey: 'test-key',
|
|
354
|
+
backendUrl: 'https://backend.test.com',
|
|
355
|
+
organizationId: 'org-123',
|
|
356
|
+
features: ['crm', 'oauth'],
|
|
357
|
+
oauthProviders: ['google'],
|
|
358
|
+
isTypeScript: true,
|
|
359
|
+
frameworkType: 'nextjs',
|
|
360
|
+
integrationPath: 'quickstart',
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const result = await FileGenerator.generate(options);
|
|
364
|
+
|
|
365
|
+
expect(result).toHaveProperty('apiClient');
|
|
366
|
+
expect(result).toHaveProperty('types');
|
|
367
|
+
expect(result).toHaveProperty('webhooks');
|
|
368
|
+
expect(result).toHaveProperty('envFile');
|
|
369
|
+
expect(result).toHaveProperty('nextauth');
|
|
370
|
+
expect(result).toHaveProperty('oauthGuide');
|
|
371
|
+
expect(result).toHaveProperty('gitignore');
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
it('generates NextAuth for Next.js with oauth feature', async () => {
|
|
375
|
+
const options = {
|
|
376
|
+
projectPath: mockProjectPath,
|
|
377
|
+
apiKey: 'test-key',
|
|
378
|
+
backendUrl: 'https://backend.test.com',
|
|
379
|
+
organizationId: 'org-123',
|
|
380
|
+
features: ['crm', 'oauth'],
|
|
381
|
+
oauthProviders: ['google'],
|
|
382
|
+
isTypeScript: true,
|
|
383
|
+
frameworkType: 'nextjs',
|
|
384
|
+
integrationPath: 'quickstart',
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
const result = await FileGenerator.generate(options);
|
|
388
|
+
|
|
389
|
+
expect(result.nextauth).not.toBeNull();
|
|
390
|
+
expect(result.oauthGuide).not.toBeNull();
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
describe('mcp-assisted integration path', () => {
|
|
395
|
+
it('returns results object with expected structure', async () => {
|
|
396
|
+
const options = {
|
|
397
|
+
projectPath: mockProjectPath,
|
|
398
|
+
apiKey: 'test-key',
|
|
399
|
+
backendUrl: 'https://backend.test.com',
|
|
400
|
+
organizationId: 'org-123',
|
|
401
|
+
organizationName: 'Test Org',
|
|
402
|
+
features: ['crm'],
|
|
403
|
+
oauthProviders: [],
|
|
404
|
+
isTypeScript: true,
|
|
405
|
+
integrationPath: 'mcp-assisted',
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
const result = await FileGenerator.generate(options);
|
|
409
|
+
|
|
410
|
+
expect(result).toHaveProperty('mcpConfig');
|
|
411
|
+
expect(result).toHaveProperty('mcpGuide');
|
|
412
|
+
expect(result).toHaveProperty('envFile');
|
|
413
|
+
expect(result).toHaveProperty('gitignore');
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
it('generates MCP config file', async () => {
|
|
417
|
+
const options = {
|
|
418
|
+
projectPath: mockProjectPath,
|
|
419
|
+
apiKey: 'test-key',
|
|
420
|
+
backendUrl: 'https://backend.test.com',
|
|
421
|
+
organizationId: 'org-123',
|
|
422
|
+
organizationName: 'Test Org',
|
|
423
|
+
features: ['crm'],
|
|
424
|
+
oauthProviders: [],
|
|
425
|
+
isTypeScript: true,
|
|
426
|
+
integrationPath: 'mcp-assisted',
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
const result = await FileGenerator.generate(options);
|
|
430
|
+
|
|
431
|
+
expect(result.mcpConfig).not.toBeNull();
|
|
432
|
+
expect(result.mcpConfig).toContain('mcp.json');
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('generates MCP guide file', async () => {
|
|
436
|
+
const options = {
|
|
437
|
+
projectPath: mockProjectPath,
|
|
438
|
+
apiKey: 'test-key',
|
|
439
|
+
backendUrl: 'https://backend.test.com',
|
|
440
|
+
organizationId: 'org-123',
|
|
441
|
+
organizationName: 'Test Org',
|
|
442
|
+
features: ['crm'],
|
|
443
|
+
oauthProviders: [],
|
|
444
|
+
isTypeScript: true,
|
|
445
|
+
integrationPath: 'mcp-assisted',
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
const result = await FileGenerator.generate(options);
|
|
449
|
+
|
|
450
|
+
expect(result.mcpGuide).not.toBeNull();
|
|
451
|
+
expect(result.mcpGuide).toContain('L4YERCAK3_MCP_GUIDE.md');
|
|
452
|
+
});
|
|
453
|
+
});
|
|
242
454
|
});
|