create-sprint 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/bin/cli.js +650 -0
  2. package/bin/cli.ts +639 -221
  3. package/package.json +35 -32
package/bin/cli.ts CHANGED
@@ -1,281 +1,699 @@
1
- #!/usr/bin/env node
2
-
3
- import { select, input } from '@inquirer/prompts';
4
- import { mkdir, writeFile } from 'fs/promises';
5
- import { existsSync } from 'fs';
6
- import { join } from 'path';
1
+ import { select, input, confirm } from "@inquirer/prompts";
2
+ import { mkdir, writeFile } from "fs/promises";
3
+ import { existsSync } from "fs";
4
+ import { join } from "path";
5
+ import { execSync } from "child_process";
7
6
 
8
7
  const args = process.argv.slice(2);
9
- const initIndex = args.indexOf('init');
10
- const isInitCommand = initIndex !== -1;
11
-
12
- if (!isInitCommand) {
13
- console.log('\nšŸš€ Sprint - Quickly API Framework\n');
14
- console.log('Usage: sprint init [options]');
15
- console.log('\nOptions:');
16
- console.log(' --ts, --typescript Create TypeScript project');
17
- console.log(' --js, --javascript Create JavaScript project');
18
- console.log(' --name <name> Project name');
19
- console.log(' --current Create in current directory');
20
- console.log('\nExamples:');
21
- console.log(' sprint init Interactive mode');
22
- console.log(' sprint init --ts Create TypeScript project');
23
- console.log(' sprint init --ts --name my-api');
24
- console.log(' sprint init --js --current\n');
25
- process.exit(0);
26
- }
27
8
 
28
- const hasTs = args.includes('--ts') || args.includes('--typescript');
29
- const hasJs = args.includes('--js') || args.includes('--javascript');
30
- const hasName = args.indexOf('--name');
9
+ const hasHelp = args.includes("--help") || args.includes("-h");
10
+ const hasTs = args.includes("--ts") || args.includes("--typescript");
11
+ const hasJs = args.includes("--js") || args.includes("--javascript");
12
+ const hasName = args.indexOf("--name");
31
13
  const projectNameArg = hasName !== -1 ? args[hasName + 1] : null;
32
- const useCurrentDirArg = args.includes('--current');
14
+ const useCurrentDirArg = args.includes("--current");
15
+ const skipInstallArg = args.includes("--no-install");
16
+ const telemetryArg = args.includes("--telemetry") ? args[args.indexOf("--telemetry") + 1] : null;
17
+ const useDockerArg = args.includes("--docker");
18
+
19
+ if (hasHelp) {
20
+ console.log("\nšŸš€ Sprint - Quickly API Framework\n");
21
+ console.log("Usage: sprint [options]");
22
+ console.log("\nOptions:");
23
+ console.log(" --ts, --typescript Create TypeScript project");
24
+ console.log(" --js, --javascript Create JavaScript project");
25
+ console.log(" --name <name> Project name (use '.' for current directory)");
26
+ console.log(" --no-install Skip automatic dependency installation");
27
+ console.log(" --telemetry <type> Telemetry: none, sentry, glitchtip, discord");
28
+ console.log(" --docker Add Docker support");
29
+ console.log(" --help, -h Show this help message");
30
+ console.log("\nExamples:");
31
+ console.log(" sprint Interactive mode");
32
+ console.log(" sprint --ts Create TypeScript project");
33
+ console.log(" sprint --ts --name my-api");
34
+ console.log(" sprint --js --name . --telemetry sentry --docker\n");
35
+ process.exit(0);
36
+ }
33
37
 
34
38
  async function main() {
35
- console.log('\nšŸš€ Welcome to Sprint - Quickly API Framework\n');
36
-
37
- let projectName: string;
38
- let language: 'typescript' | 'javascript';
39
-
40
- if (projectNameArg) {
41
- projectName = projectNameArg;
42
- } else if (useCurrentDirArg) {
43
- projectName = '.';
44
- } else {
45
- projectName = await getProjectName();
46
- }
47
-
48
- if (hasTs) {
49
- language = 'typescript';
50
- } else if (hasJs) {
51
- language = 'javascript';
52
- } else {
53
- language = await selectLanguage();
54
- }
55
-
56
- console.log(`\nāœ… Creating Sprint project: ${projectName === '.' ? 'current directory' : projectName} with ${language === 'typescript' ? 'TypeScript' : 'JavaScript'}\n`);
57
-
58
- await createProject(projectName, language);
59
-
60
- console.log('\nāœ… Project created successfully!');
61
- console.log(`\nšŸ“¦ Next steps:`);
62
- console.log(` cd ${projectName === '.' ? '' : projectName}${projectName === '.' ? '' : ' '}(if created new directory)`);
63
- console.log(` npm install`);
64
- console.log(` npm run dev`);
65
- console.log('\n');
39
+ console.log("\nšŸš€ Welcome to Sprint - Quickly API Framework\n");
40
+
41
+ let projectName;
42
+ let language;
43
+
44
+ if (projectNameArg) {
45
+ projectName = projectNameArg;
46
+ } else if (useCurrentDirArg) {
47
+ projectName = ".";
48
+ } else {
49
+ projectName = await getProjectName();
50
+ }
51
+
52
+ if (hasTs) {
53
+ language = "typescript";
54
+ } else if (hasJs) {
55
+ language = "javascript";
56
+ } else {
57
+ language = await selectLanguage();
58
+ }
59
+
60
+ console.log(`\nāœ… Creating Sprint project: ${projectName === "." ? "current directory" : projectName} with ${language === "typescript" ? "TypeScript" : "JavaScript"}\n`);
61
+
62
+ await createProject(projectName, language);
63
+
64
+ console.log("\nāœ… Project created successfully!");
65
+
66
+ let installDeps = true;
67
+ if (!skipInstallArg) {
68
+ installDeps = await confirm({
69
+ message: "Do you want to install dependencies now?",
70
+ default: true,
71
+ });
72
+ }
73
+
74
+ if (installDeps) {
75
+ console.log("\nšŸ“¦ Installing dependencies...\n");
76
+ const targetDir = projectName === "." ? process.cwd() : join(process.cwd(), projectName);
77
+ try {
78
+ execSync("npm install", { cwd: targetDir, stdio: "inherit" });
79
+ console.log("\nāœ… Dependencies installed successfully!");
80
+ } catch (error) {
81
+ console.error("\nāŒ Error installing dependencies. Please run 'npm install' manually.");
82
+ }
83
+ }
84
+
85
+ console.log("\nšŸ“¦ Next steps:");
86
+ const cdCmd = projectName === "." ? "" : `cd ${projectName} && `;
87
+ if (!installDeps) {
88
+ console.log(` ${cdCmd}npm install`);
89
+ }
90
+ console.log(` ${cdCmd}npm run dev`);
91
+ console.log("\n");
66
92
  }
67
93
 
68
- async function getProjectName(): Promise<string> {
69
- const useCurrentDir = await select({
70
- message: 'Where would you like to create your project?',
71
- choices: [
72
- {
73
- name: 'Current directory',
74
- value: 'current',
75
- },
76
- {
77
- name: 'New directory',
78
- value: 'new',
79
- },
80
- ],
81
- });
82
-
83
- if (useCurrentDir === 'current') {
84
- return '.';
85
- }
94
+ async function getProjectName() {
95
+ const name = await input({
96
+ message: "Enter project name:",
97
+ validate: (value) => {
98
+ if (!value.trim()) return "Please enter a project name";
99
+ return true;
100
+ },
101
+ });
86
102
 
87
- const name = await input({
88
- message: 'Enter project name:',
89
- validate: (value: string) => {
90
- if (!value.trim()) return 'Please enter a project name';
91
- return true;
92
- },
93
- });
94
-
95
- return name;
103
+ return name;
96
104
  }
97
105
 
98
- async function selectLanguage(): Promise<'typescript' | 'javascript'> {
99
- const language: 'typescript' | 'javascript' = await select({
100
- message: 'Select your preferred language:',
101
- choices: [
102
- {
103
- name: 'TypeScript',
104
- value: 'typescript',
105
- description: 'Recommended - Type safety and better developer experience',
106
- },
107
- {
108
- name: 'JavaScript',
109
- value: 'javascript',
110
- description: 'Vanilla JavaScript for simpler projects',
111
- },
112
- ],
113
- });
106
+ async function selectLanguage() {
107
+ const language = await select({
108
+ message: "Select your preferred language:",
109
+ choices: [
110
+ {
111
+ name: "TypeScript",
112
+ value: "typescript",
113
+ description: "Recommended - Type safety and better developer experience",
114
+ },
115
+ {
116
+ name: "JavaScript",
117
+ value: "javascript",
118
+ description: "Vanilla JavaScript for simpler projects",
119
+ },
120
+ ],
121
+ });
122
+
123
+ return language;
124
+ }
114
125
 
115
- return language;
126
+ async function selectTelemetry() {
127
+ const telemetry = await select({
128
+ message: "Select error tracking/telemetry solution:",
129
+ choices: [
130
+ {
131
+ name: "None",
132
+ value: "none",
133
+ description: "No error tracking integration",
134
+ },
135
+ {
136
+ name: "Sentry",
137
+ value: "sentry",
138
+ description: "Full-featured error tracking (free tier available)",
139
+ },
140
+ {
141
+ name: "GlitchTip",
142
+ value: "glitchtip",
143
+ description: "Simple error tracking, can be self-hosted",
144
+ },
145
+ {
146
+ name: "Discord Webhook",
147
+ value: "discord",
148
+ description: "Send error notifications to Discord channel",
149
+ },
150
+ ],
151
+ });
152
+
153
+ return telemetry;
116
154
  }
117
155
 
118
- async function createProject(projectName: string, language: 'typescript' | 'javascript') {
119
- const isCurrentDir = projectName === '.';
120
- const targetDir = isCurrentDir ? process.cwd() : join(process.cwd(), projectName);
156
+ async function createProject(projectName, language) {
157
+ const isCurrentDir = projectName === ".";
158
+ const targetDir = isCurrentDir ? process.cwd() : join(process.cwd(), projectName);
159
+
160
+ if (!isCurrentDir && existsSync(targetDir)) {
161
+ console.error(`Error: Directory ${projectName} already exists`);
162
+ process.exit(1);
163
+ }
164
+
165
+ if (!isCurrentDir) {
166
+ await mkdir(targetDir, { recursive: true });
167
+ }
168
+
169
+ let telemetry = "none";
170
+ if (telemetryArg && ["sentry", "glitchtip", "discord", "none"].includes(telemetryArg)) {
171
+ telemetry = telemetryArg;
172
+ } else if (!hasTs && !hasJs) {
173
+ telemetry = await selectTelemetry();
174
+ }
175
+
176
+ let useDocker = useDockerArg;
177
+ if (!useDocker && !hasTs && !hasJs) {
178
+ useDocker = await confirm({
179
+ message: "Do you want to add Docker support?",
180
+ default: false,
181
+ });
182
+ }
121
183
 
122
- if (!isCurrentDir && existsSync(targetDir)) {
123
- console.error(`Error: Directory ${projectName} already exists`);
124
- process.exit(1);
125
- }
184
+ let pkgJson;
185
+ if (language === "typescript") {
186
+ pkgJson = getTypeScriptPackageJson(projectName, telemetry);
187
+ } else {
188
+ pkgJson = getJavaScriptPackageJson(projectName, telemetry);
189
+ }
126
190
 
127
- if (!isCurrentDir) {
128
- await mkdir(targetDir, { recursive: true });
129
- }
191
+ await writeFile(join(targetDir, "package.json"), JSON.stringify(pkgJson, null, 2));
130
192
 
131
- const pkgJson = language === 'typescript'
132
- ? getTypeScriptPackageJson(projectName)
133
- : getJavaScriptPackageJson(projectName);
193
+ if (language === "typescript") {
194
+ await writeFile(join(targetDir, "tsconfig.json"), getTsConfig());
195
+ await writeFile(join(targetDir, "vite.config.ts"), getViteConfig());
196
+ }
134
197
 
135
- await writeFile(join(targetDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
198
+ const srcDir = join(targetDir, "src");
199
+ await mkdir(srcDir, { recursive: true });
136
200
 
137
- if (language === 'typescript') {
138
- await writeFile(join(targetDir, 'tsconfig.json'), getTsConfig());
139
- }
201
+ await mkdir(join(srcDir, "middlewares"), { recursive: true });
202
+ await mkdir(join(srcDir, "routes"), { recursive: true });
203
+ await mkdir(join(srcDir, "controllers"), { recursive: true });
140
204
 
141
- const srcDir = join(targetDir, 'src');
142
- await mkdir(srcDir, { recursive: true });
143
-
144
- await writeFile(join(srcDir, 'index.' + (language === 'typescript' ? 'ts' : 'js')), getMainFile(language));
145
-
146
- if (language === 'typescript') {
147
- await writeFile(join(srcDir, 'app.' + (language === 'typescript' ? 'ts' : 'js')), getAppFile(language));
148
- }
205
+ await writeFile(join(srcDir, "middlewares", ".gitkeep"), "");
206
+ await writeFile(join(srcDir, "controllers", ".gitkeep"), "");
149
207
 
150
- await writeFile(join(targetDir, '.env.example'), getEnvExample());
208
+ await writeFile(join(srcDir, "index." + (language === "typescript" ? "ts" : "js")), getMainFile(language, telemetry));
209
+
210
+ await writeFile(join(srcDir, "routes", "home." + (language === "typescript" ? "ts" : "js")), getHomeRoute(language));
211
+
212
+ if (language === "typescript") {
213
+ await writeFile(join(srcDir, "app." + (language === "typescript" ? "ts" : "js")), getAppFile(language, telemetry));
214
+ }
215
+
216
+ await writeFile(join(targetDir, ".env.example"), getEnvExample(telemetry));
217
+ await writeFile(join(targetDir, ".env"), "");
218
+
219
+ if (telemetry !== "none") {
220
+ await writeFile(join(targetDir, "sprint.config.js"), getSprintConfig(telemetry));
221
+ }
222
+
223
+ await writeFile(join(targetDir, ".gitignore"), getGitignore(language));
224
+
225
+ if (useDocker) {
226
+ await writeFile(join(targetDir, "Dockerfile"), getDockerfile(language));
227
+ await writeFile(join(targetDir, "docker-compose.yml"), getDockerCompose(language));
228
+ await writeFile(join(targetDir, ".dockerignore"), getDockerIgnore());
229
+ }
151
230
  }
152
231
 
153
- function getTypeScriptPackageJson(name: string) {
154
- return {
155
- name: name === '.' ? 'sprint-app' : name,
156
- version: '0.0.1',
157
- description: 'Sprint API',
158
- main: 'dist/index.js',
159
- scripts: {
160
- build: 'tsc',
161
- start: 'node dist/index.js',
162
- dev: 'tsx watch src/index.ts',
163
- },
164
- dependencies: {
165
- 'sprint-es': '^0.0.23',
166
- dotenv: '^17.0.0',
167
- },
168
- devDependencies: {
169
- '@types/node': '^22.0.0',
170
- 'tsx': '^4.19.0',
171
- typescript: '^5.6.0',
172
- },
173
- };
232
+ function getTypeScriptPackageJson(name, telemetry) {
233
+ const deps = {
234
+ "sprint-es": "^0.0.24",
235
+ dotenv: "^17.0.0",
236
+ };
237
+
238
+ const devDeps = {
239
+ "@types/node": "^22.0.0",
240
+ "tsx": "^4.19.0",
241
+ typescript: "^5.6.0",
242
+ vite: "^6.0.0",
243
+ };
244
+
245
+ if (telemetry === "sentry") {
246
+ deps["@sentry/node"] = "^8.0.0";
247
+ } else if (telemetry === "glitchtip") {
248
+ deps["@sentry/node"] = "^8.0.0";
249
+ } else if (telemetry === "discord") {
250
+ deps["axios"] = "^1.6.0";
251
+ }
252
+
253
+ return {
254
+ name: name === "." ? "sprint-app" : name,
255
+ version: "0.0.1",
256
+ description: "Sprint API",
257
+ main: "dist/index.js",
258
+ scripts: {
259
+ build: "vite build",
260
+ start: "node dist/index.js --prod",
261
+ dev: "vite --dev",
262
+ },
263
+ dependencies: deps,
264
+ devDependencies: devDeps,
265
+ };
174
266
  }
175
267
 
176
- function getJavaScriptPackageJson(name: string) {
177
- return {
178
- name: name === '.' ? 'sprint-app' : name,
179
- version: '0.0.1',
180
- description: 'Sprint API',
181
- main: 'src/index.js',
182
- type: 'module',
183
- scripts: {
184
- start: 'node src/index.js',
185
- dev: 'node --watch src/index.js',
186
- },
187
- dependencies: {
188
- 'sprint-es': '^0.0.23',
189
- dotenv: '^17.0.0',
190
- },
191
- };
268
+ function getJavaScriptPackageJson(name, telemetry) {
269
+ const deps = {
270
+ "sprint-es": "^0.0.24",
271
+ dotenv: "^17.0.0",
272
+ };
273
+
274
+ if (telemetry === "sentry") {
275
+ deps["@sentry/node"] = "^8.0.0";
276
+ } else if (telemetry === "glitchtip") {
277
+ deps["@sentry/node"] = "^8.0.0";
278
+ } else if (telemetry === "discord") {
279
+ deps["axios"] = "^1.6.0";
280
+ }
281
+
282
+ return {
283
+ name: name === "." ? "sprint-app" : name,
284
+ version: "0.0.1",
285
+ description: "Sprint API",
286
+ main: "src/index.js",
287
+ type: "module",
288
+ scripts: {
289
+ start: "node src/index.js --prod",
290
+ dev: "node --watch src/index.js --dev",
291
+ },
292
+ dependencies: deps,
293
+ };
192
294
  }
193
295
 
194
296
  function getTsConfig() {
195
- return JSON.stringify({
196
- compilerOptions: {
197
- target: 'ES2020',
198
- module: 'ESNext',
199
- moduleResolution: 'bundler',
200
- lib: ['ES2020'],
201
- outDir: './dist',
202
- rootDir: './src',
203
- strict: true,
204
- esModuleInterop: true,
205
- skipLibCheck: true,
206
- forceConsistentCasingInFileNames: true,
207
- resolveJsonModule: true,
208
- declaration: true,
209
- declarationMap: true,
210
- sourceMap: true,
297
+ return JSON.stringify({
298
+ compilerOptions: {
299
+ target: "ES2020",
300
+ module: "ESNext",
301
+ moduleResolution: "bundler",
302
+ lib: ["ES2020"],
303
+ outDir: "./dist",
304
+ rootDir: "./src",
305
+ strict: true,
306
+ esModuleInterop: true,
307
+ skipLibCheck: true,
308
+ forceConsistentCasingInFileNames: true,
309
+ resolveJsonModule: true,
310
+ declaration: true,
311
+ declarationMap: true,
312
+ sourceMap: true,
313
+ tabWidth: 4,
314
+ },
315
+ include: ["src/**/*"],
316
+ exclude: ["node_modules", "dist"],
317
+ }, null, 2);
318
+ }
319
+
320
+ function getViteConfig() {
321
+ return `import { defineConfig } from "vite";
322
+ import { resolve } from "path";
323
+
324
+ export default defineConfig({
325
+ build: {
326
+ lib: {
327
+ entry: resolve(__dirname, "src/index.ts"),
328
+ formats: ["es"],
329
+ fileName: "index",
330
+ },
331
+ outDir: "dist",
332
+ rollupOptions: {
333
+ external: ["sprint-es", "express", "cors", "morgan", "serve-favicon", "dotenv"],
334
+ },
335
+ target: "ES2020",
336
+ },
337
+ resolve: {
338
+ alias: {
339
+ "@": resolve(__dirname, "src"),
340
+ },
211
341
  },
212
- include: ['src/**/*'],
213
- exclude: ['node_modules', 'dist'],
214
- }, null, 2);
342
+ });
343
+ `;
215
344
  }
216
345
 
217
- function getMainFile(language: 'typescript' | 'javascript') {
218
- if (language === 'typescript') {
219
- return `import Sprint from 'sprint-es';
220
- import dotenv from 'dotenv';
346
+ function getMainFile(language, telemetry) {
347
+ const hasTelemetry = telemetry !== "none";
348
+
349
+ if (language === "typescript") {
350
+ let code = `import Sprint from "sprint-es";
351
+ import dotenv from "dotenv";
352
+ import homeRouter from "./routes/home";
221
353
 
222
354
  dotenv.config();
223
355
 
224
- const app = new Sprint({
225
- port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
226
- favicon: process.env.FAVICON || undefined,
227
- bodyParser: {
228
- limit: '10mb'
229
- }
230
- });
356
+ `;
357
+
358
+ if (hasTelemetry) {
359
+ code += getTelemetryImport(language, telemetry);
360
+ }
231
361
 
232
- app.get('/', (req, res) => {
233
- res.send('Hello from Sprint!');
362
+ code += `const app = new Sprint({
363
+ port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
364
+ favicon: process.env.FAVICON || undefined,
365
+ bodyParser: {
366
+ limit: "10mb"
367
+ }
234
368
  });
235
369
 
370
+ app.use(homeRouter);
371
+
236
372
  app.listen();
237
373
  `;
238
- }
239
- return `import Sprint from 'sprint-es';
240
- import dotenv from 'dotenv';
374
+ return code;
375
+ }
376
+
377
+ let code = `import Sprint from "sprint-es";
378
+ import dotenv from "dotenv";
379
+ import homeRouter from "./routes/home";
241
380
 
242
381
  dotenv.config();
243
382
 
244
- const app = new Sprint({
245
- port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
246
- favicon: process.env.FAVICON || undefined,
247
- bodyParser: {
248
- limit: '10mb'
249
- }
250
- });
383
+ `;
251
384
 
252
- app.get('/', (req, res) => {
253
- res.send('Hello from Sprint!');
385
+ if (hasTelemetry) {
386
+ code += getTelemetryImport(language, telemetry);
387
+ }
388
+
389
+ code += `const app = new Sprint({
390
+ port: process.env.PORT ? parseInt(process.env.PORT) : 3000,
391
+ favicon: process.env.FAVICON || undefined,
392
+ bodyParser: {
393
+ limit: "10mb"
394
+ }
254
395
  });
255
396
 
397
+ app.use(homeRouter);
398
+
256
399
  app.listen();
257
400
  `;
401
+ return code;
402
+ }
403
+
404
+ function getTelemetryImport(language, telemetry) {
405
+ if (telemetry === "sentry" || telemetry === "glitchtip") {
406
+ return `import * as Sentry from "@sentry/node";
407
+
408
+ Sentry.init({
409
+ dsn: process.env.SENTRY_DSN,
410
+ integrations: [
411
+ Sentry.httpIntegration(),
412
+ ],
413
+ });
414
+
415
+ `;
416
+ } else if (telemetry === "discord") {
417
+ return `import axios from "axios";
418
+
419
+ const discordWebhook = process.env.DISCORD_WEBHOOK_URL;
420
+
421
+ async function sendDiscordError(error, req) {
422
+ if (!discordWebhook) return;
423
+
424
+ const embed = {
425
+ title: "🚨 Error in Sprint API",
426
+ description: \`\${error.message}\`,
427
+ color: 16711680,
428
+ fields: [
429
+ {
430
+ name: "Method",
431
+ value: req.method,
432
+ inline: true,
433
+ },
434
+ {
435
+ name: "URL",
436
+ value: req.url,
437
+ inline: true,
438
+ },
439
+ {
440
+ name: "Stack",
441
+ value: error.stack?.split("\\n").slice(0, 5).join("\\n") || "No stack trace",
442
+ },
443
+ ],
444
+ timestamp: new Date().toISOString(),
445
+ };
446
+
447
+ try {
448
+ await axios.post(discordWebhook, {
449
+ embeds: [embed],
450
+ });
451
+ } catch (e) {
452
+ console.error("Failed to send Discord webhook:", e.message);
453
+ }
454
+ }
455
+
456
+ `;
457
+ }
458
+ return "";
258
459
  }
259
460
 
260
- function getAppFile(language: 'typescript' | 'javascript') {
261
- if (language === 'typescript') {
262
- return `import type { SprintOptions } from 'sprint-es';
461
+ function getAppFile(language, telemetry) {
462
+ if (language === "typescript") {
463
+ return `import type { SprintOptions } from "sprint-es";
263
464
 
264
465
  export const options: SprintOptions = {
265
- port: 3000,
266
- bodyParser: {
267
- limit: '10mb'
268
- }
466
+ port: 3000,
467
+ bodyParser: {
468
+ limit: "10mb"
469
+ }
269
470
  };
270
471
  `;
271
- }
272
- return '';
472
+ }
473
+ return "";
273
474
  }
274
475
 
275
- function getEnvExample() {
276
- return `PORT=3000
476
+ function getEnvExample(telemetry) {
477
+ let env = `PORT=3000
277
478
  FAVICON=./public/favicon.ico
278
479
  NODE_ENV=development
480
+ `;
481
+
482
+ if (telemetry === "sentry" || telemetry === "glitchtip") {
483
+ env += `
484
+ # Sentry / GlitchTip (use GlitchTip DSN for self-hosted)
485
+ SENTRY_DSN=
486
+ `;
487
+ } else if (telemetry === "discord") {
488
+ env += `
489
+ # Discord Webhook URL for error notifications
490
+ DISCORD_WEBHOOK_URL=
491
+ `;
492
+ }
493
+
494
+ return env;
495
+ }
496
+
497
+ function getSprintConfig(telemetry) {
498
+ if (telemetry === "discord") {
499
+ return `export default {
500
+ errorHandler: async (error, req, res) => {
501
+ console.error(error);
502
+
503
+ if (process.env.DISCORD_WEBHOOK_URL && res.socket.server) {
504
+ const axios = await import("axios");
505
+ const embed = {
506
+ title: "🚨 Error in Sprint API",
507
+ description: error.message,
508
+ color: 16711680,
509
+ fields: [
510
+ {
511
+ name: "Method",
512
+ value: req.method,
513
+ inline: true,
514
+ },
515
+ {
516
+ name: "URL",
517
+ value: req.url,
518
+ inline: true,
519
+ },
520
+ {
521
+ name: "Stack",
522
+ value: error.stack?.split("\\n").slice(0, 5).join("\\n") || "No stack",
523
+ },
524
+ ],
525
+ timestamp: new Date().toISOString(),
526
+ };
527
+
528
+ try {
529
+ await axios.default.post(process.env.DISCORD_WEBHOOK_URL, {
530
+ embeds: [embed],
531
+ });
532
+ } catch (e) {
533
+ console.error("Discord webhook error:", e.message);
534
+ }
535
+ }
536
+
537
+ res.status(500).json({ error: "Internal server error" });
538
+ }
539
+ };
540
+ `;
541
+ }
542
+ return "";
543
+ }
544
+
545
+ function getHomeRoute(language) {
546
+ if (language === "typescript") {
547
+ return `import { Router } from "sprint-es";
548
+
549
+ const router = Router();
550
+
551
+ router.get("/", (req, res) => {
552
+ res.json({
553
+ message: "Hello World",
554
+ status: "ok"
555
+ });
556
+ });
557
+
558
+ export default router;
559
+ `;
560
+ }
561
+ return `import { Router } from "sprint-es";
562
+
563
+ const router = Router();
564
+
565
+ router.get("/", (req, res) => {
566
+ res.json({
567
+ message: "Hello World",
568
+ status: "ok"
569
+ });
570
+ });
571
+
572
+ export default router;
573
+ `;
574
+ }
575
+
576
+ function getDockerfile(language) {
577
+ if (language === "typescript") {
578
+ return `FROM node:20-alpine
579
+
580
+ WORKDIR /app
581
+
582
+ COPY package*.json ./
583
+
584
+ RUN npm install
585
+
586
+ COPY . .
587
+
588
+ RUN npm run build
589
+
590
+ EXPOSE 3000
591
+
592
+ CMD ["npm", "start"]
593
+ `;
594
+ }
595
+ return `FROM node:20-alpine
596
+
597
+ WORKDIR /app
598
+
599
+ COPY package*.json ./
600
+
601
+ RUN npm install
602
+
603
+ COPY . .
604
+
605
+ EXPOSE 3000
606
+
607
+ CMD ["npm", "start"]
608
+ `;
609
+ }
610
+
611
+ function getDockerCompose(language) {
612
+ if (language === "typescript") {
613
+ return `version: "3.8"
614
+
615
+ services:
616
+ app:
617
+ build: .
618
+ ports:
619
+ - "3000:3000"
620
+ environment:
621
+ - NODE_ENV=production
622
+ - PORT=3000
623
+ restart: unless-stopped
624
+ `;
625
+ }
626
+ return `version: "3.8"
627
+
628
+ services:
629
+ app:
630
+ build: .
631
+ ports:
632
+ - "3000:3000"
633
+ environment:
634
+ - NODE_ENV=production
635
+ - PORT=3000
636
+ restart: unless-stopped
637
+ `;
638
+ }
639
+
640
+ function getGitignore(language) {
641
+ return `# Dependencies
642
+ node_modules/
643
+ npm-debug.log*
644
+ yarn-debug.log*
645
+ yarn-error.log*
646
+
647
+ # Build
648
+ dist/
649
+ build/
650
+ *.tsbuildinfo
651
+
652
+ # Environment
653
+ .env
654
+ .env.local
655
+ .env.*.local
656
+
657
+ # IDE
658
+ .vscode/
659
+ .idea/
660
+ *.swp
661
+ *.swo
662
+ *~
663
+
664
+ # OS
665
+ .DS_Store
666
+ Thumbs.db
667
+
668
+ # Logs
669
+ logs/
670
+ *.log
671
+
672
+ # Test
673
+ coverage/
674
+
675
+ # Temporary
676
+ tmp/
677
+ temp/
678
+ `;
679
+ }
680
+
681
+ function getDockerIgnore() {
682
+ return `node_modules
683
+ npm-debug.log
684
+ .env
685
+ .env.local
686
+ .git
687
+ .gitignore
688
+ README.md
689
+ dist
690
+ build
691
+ coverage
692
+ .vscode
693
+ .idea
694
+ *.log
695
+ tmp
696
+ temp
279
697
  `;
280
698
  }
281
699