create-sprint 0.0.138 → 0.0.139
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/dist/index.js +110 -54
- package/dist/templates/configFiles.js +39 -49
- package/dist/templates/index.js +2 -0
- package/dist/templates/packageJson.js +22 -4
- package/dist/templates/routes.js +34 -25
- package/dist/templates/services.js +262 -0
- package/package.json +1 -1
- package/src/index.ts +142 -89
- package/src/templates/configFiles.ts +50 -51
- package/src/templates/index.ts +3 -0
- package/src/templates/packageJson.ts +21 -6
- package/src/templates/routes.ts +46 -23
- package/src/templates/services.ts +270 -0
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import color from "picocolors";
|
|
|
6
6
|
import * as p from "@clack/prompts";
|
|
7
7
|
import { validateProjectName } from "./validators.js";
|
|
8
8
|
import { getTypeScriptPackageJson, getJavaScriptPackageJson, getTsConfig, getMainFile, getHomeRoute, getAdminRoute, getUploadRoute, getHomeController, getAdminController, getUploadController, getEnvExample, getInternalAuthMiddleware, getUserAuthMiddleware, getHomeSchema, getAdminSchema, getUploadSchema, getDockerfile, getDockerCompose, getGitignore, getDockerIgnore, getSprintConfigFile, getEnvDevelopment, getEnvProduction, getExampleCronJob, getGraphQLFiles } from "./generators.js";
|
|
9
|
+
import { getQueueService, getCacheService, getTrpcRouter, getGrpcServer, getWebSocketScaffold } from "./templates/services.js";
|
|
9
10
|
export async function writeFile(path, content, options) {
|
|
10
11
|
if (typeof content === "string")
|
|
11
12
|
content = content.trim();
|
|
@@ -14,7 +15,6 @@ export async function writeFile(path, content, options) {
|
|
|
14
15
|
;
|
|
15
16
|
export async function runCLI(args) {
|
|
16
17
|
const options = parseArgs(args);
|
|
17
|
-
p.intro("Sprint — Quickly API Framework");
|
|
18
18
|
p.intro(`${color.bgCyan(color.black(' create-sprint-app '))}`);
|
|
19
19
|
let config;
|
|
20
20
|
if (options.skipPrompts) {
|
|
@@ -24,11 +24,18 @@ export async function runCLI(args) {
|
|
|
24
24
|
telemetry: options.telemetry ?? "none",
|
|
25
25
|
swagger: options.swagger ?? true,
|
|
26
26
|
graphql: options.graphql ?? false,
|
|
27
|
-
docker: options.docker || false
|
|
27
|
+
docker: options.docker || false,
|
|
28
|
+
queue: options.queue ?? "none",
|
|
29
|
+
cache: options.cache ?? "none",
|
|
30
|
+
websocket: options.websocket ?? false,
|
|
31
|
+
trpc: options.trpc ?? false,
|
|
32
|
+
grpc: options.grpc ?? false,
|
|
33
|
+
csrf: options.csrf ?? false,
|
|
34
|
+
cors: options.cors ?? ""
|
|
28
35
|
};
|
|
29
36
|
}
|
|
30
37
|
else {
|
|
31
|
-
|
|
38
|
+
const result = await p.group({
|
|
32
39
|
projectName: () => p.text({
|
|
33
40
|
message: "Project name:",
|
|
34
41
|
placeholder: "my-api",
|
|
@@ -41,6 +48,11 @@ export async function runCLI(args) {
|
|
|
41
48
|
{ value: "javascript", label: "JavaScript", hint: "not recommended" }
|
|
42
49
|
]
|
|
43
50
|
}),
|
|
51
|
+
cors: () => p.text({
|
|
52
|
+
message: "CORS allowed origins (comma-separated, blank = deny all, '*' = wildcard):",
|
|
53
|
+
placeholder: "https://app.example.com,http://localhost:3000",
|
|
54
|
+
defaultValue: ""
|
|
55
|
+
}),
|
|
44
56
|
telemetry: () => p.select({
|
|
45
57
|
message: "Error tracking:",
|
|
46
58
|
options: [
|
|
@@ -53,8 +65,28 @@ export async function runCLI(args) {
|
|
|
53
65
|
{ value: "nodemailer", label: "Nodemailer", hint: "sends emails", disabled: true }
|
|
54
66
|
]
|
|
55
67
|
}),
|
|
56
|
-
|
|
68
|
+
queue: () => p.select({
|
|
69
|
+
message: "Job queue:",
|
|
70
|
+
options: [
|
|
71
|
+
{ value: "none", label: "None" },
|
|
72
|
+
{ value: "memory", label: "In-memory", hint: "single process, retries + DLQ" },
|
|
73
|
+
{ value: "bullmq", label: "BullMQ + Redis", hint: "distributed, production-grade" }
|
|
74
|
+
]
|
|
75
|
+
}),
|
|
76
|
+
cache: () => p.select({
|
|
77
|
+
message: "Cache:",
|
|
78
|
+
options: [
|
|
79
|
+
{ value: "none", label: "None" },
|
|
80
|
+
{ value: "memory", label: "In-memory LRU", hint: "single process" },
|
|
81
|
+
{ value: "redis", label: "Redis", hint: "shared across instances" }
|
|
82
|
+
]
|
|
83
|
+
}),
|
|
84
|
+
websocket: () => p.confirm({ message: "Add WebSocket support?", initialValue: false }),
|
|
85
|
+
trpc: () => p.confirm({ message: "Add tRPC adapter?", initialValue: false }),
|
|
86
|
+
grpc: () => p.confirm({ message: "Add gRPC server?", initialValue: false }),
|
|
57
87
|
graphql: () => p.confirm({ message: "Add GraphQL support?", initialValue: false }),
|
|
88
|
+
swagger: () => p.confirm({ message: "Add Swagger UI & OpenAPI?", initialValue: true }),
|
|
89
|
+
csrf: () => p.confirm({ message: "Add CSRF middleware (double-submit cookie)?", initialValue: false }),
|
|
58
90
|
docker: () => p.confirm({ message: "Add Docker support?", initialValue: false })
|
|
59
91
|
}, {
|
|
60
92
|
onCancel: () => {
|
|
@@ -62,11 +94,12 @@ export async function runCLI(args) {
|
|
|
62
94
|
process.exit(0);
|
|
63
95
|
}
|
|
64
96
|
});
|
|
97
|
+
config = result;
|
|
65
98
|
}
|
|
66
99
|
const targetDir = config.projectName === "." ? process.cwd() : join(process.cwd(), config.projectName);
|
|
67
100
|
const s = p.spinner();
|
|
68
101
|
s.start("Creating project");
|
|
69
|
-
await createProject(config
|
|
102
|
+
await createProject(config);
|
|
70
103
|
s.stop("Project created");
|
|
71
104
|
let installDeps = true;
|
|
72
105
|
if (options.skipInstall)
|
|
@@ -112,6 +145,9 @@ function parseArgs(args) {
|
|
|
112
145
|
const hasJs = args.includes("--js") || args.includes("--javascript");
|
|
113
146
|
const hasName = args.indexOf("--name");
|
|
114
147
|
const telemetryArg = args.includes("--telemetry") ? args[args.indexOf("--telemetry") + 1] : null;
|
|
148
|
+
const queueArg = args.includes("--queue") ? args[args.indexOf("--queue") + 1] : null;
|
|
149
|
+
const cacheArg = args.includes("--cache") ? args[args.indexOf("--cache") + 1] : null;
|
|
150
|
+
const corsArg = args.includes("--cors") ? args[args.indexOf("--cors") + 1] : null;
|
|
115
151
|
if (args.includes("--yes") || args.includes("-y"))
|
|
116
152
|
options.skipPrompts = true;
|
|
117
153
|
if (!options.skipPrompts) {
|
|
@@ -139,36 +175,44 @@ function parseArgs(args) {
|
|
|
139
175
|
options.graphql = true;
|
|
140
176
|
else if (args.includes("--no-graphql"))
|
|
141
177
|
options.graphql = false;
|
|
178
|
+
if (args.includes("--websocket") || args.includes("--ws"))
|
|
179
|
+
options.websocket = true;
|
|
180
|
+
if (args.includes("--trpc"))
|
|
181
|
+
options.trpc = true;
|
|
182
|
+
if (args.includes("--grpc"))
|
|
183
|
+
options.grpc = true;
|
|
184
|
+
if (args.includes("--csrf"))
|
|
185
|
+
options.csrf = true;
|
|
142
186
|
if (args.includes("--no-install"))
|
|
143
187
|
options.skipInstall = true;
|
|
144
|
-
if (
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
188
|
+
if (queueArg && ["none", "memory", "bullmq"].includes(queueArg))
|
|
189
|
+
options.queue = queueArg;
|
|
190
|
+
if (cacheArg && ["none", "memory", "redis"].includes(cacheArg))
|
|
191
|
+
options.cache = cacheArg;
|
|
192
|
+
if (typeof corsArg === "string")
|
|
193
|
+
options.cors = corsArg;
|
|
194
|
+
if (telemetryArg && ["sentry", "glitchtip", "discord", "none"].includes(telemetryArg))
|
|
195
|
+
options.telemetry = telemetryArg;
|
|
148
196
|
return options;
|
|
149
197
|
}
|
|
150
198
|
;
|
|
151
|
-
async function createProject(
|
|
152
|
-
const isCurrentDir = projectName === ".";
|
|
153
|
-
const targetDir = isCurrentDir ? process.cwd() : join(process.cwd(), projectName);
|
|
199
|
+
async function createProject(features) {
|
|
200
|
+
const isCurrentDir = features.projectName === ".";
|
|
201
|
+
const targetDir = isCurrentDir ? process.cwd() : join(process.cwd(), features.projectName);
|
|
202
|
+
const ext = features.language === "typescript" ? "ts" : "js";
|
|
154
203
|
if (!isCurrentDir && existsSync(targetDir)) {
|
|
155
|
-
p.cancel(`Directory "${projectName}" already exists.`);
|
|
204
|
+
p.cancel(`Directory "${features.projectName}" already exists.`);
|
|
156
205
|
process.exit(1);
|
|
157
206
|
}
|
|
158
207
|
if (!isCurrentDir)
|
|
159
208
|
await mkdir(targetDir, { recursive: true });
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
else
|
|
164
|
-
pkgJson = getJavaScriptPackageJson(projectName, telemetry, swagger, graphql);
|
|
209
|
+
const pkgJson = features.language === "typescript"
|
|
210
|
+
? getTypeScriptPackageJson(features.projectName, features.telemetry, features.swagger, features.graphql, features)
|
|
211
|
+
: getJavaScriptPackageJson(features.projectName, features.telemetry, features.swagger, features.graphql, features);
|
|
165
212
|
await writeFile(join(targetDir, "package.json"), JSON.stringify(pkgJson, null, 2));
|
|
166
|
-
if (language === "typescript")
|
|
213
|
+
if (features.language === "typescript")
|
|
167
214
|
await writeFile(join(targetDir, "tsconfig.json"), getTsConfig());
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
else
|
|
171
|
-
await writeFile(join(targetDir, "sprint.config.js"), getSprintConfigFile(language, telemetry, swagger, graphql));
|
|
215
|
+
await writeFile(join(targetDir, `sprint.config.${ext}`), getSprintConfigFile(features.language, features.telemetry, features.swagger, features.graphql, features));
|
|
172
216
|
const srcDir = join(targetDir, "src");
|
|
173
217
|
await mkdir(srcDir, { recursive: true });
|
|
174
218
|
await mkdir(join(srcDir, "middlewares"), { recursive: true });
|
|
@@ -178,45 +222,57 @@ async function createProject(projectName, language, telemetry, swagger, graphql,
|
|
|
178
222
|
await mkdir(join(srcDir, "cronjobs"), { recursive: true });
|
|
179
223
|
await mkdir(join(srcDir, "config"), { recursive: true });
|
|
180
224
|
await mkdir(join(srcDir, "services"), { recursive: true });
|
|
181
|
-
if (graphql)
|
|
225
|
+
if (features.graphql)
|
|
182
226
|
await mkdir(join(srcDir, "graphql"), { recursive: true });
|
|
183
|
-
if (
|
|
184
|
-
await
|
|
185
|
-
|
|
227
|
+
if (features.trpc)
|
|
228
|
+
await mkdir(join(srcDir, "trpc"), { recursive: true });
|
|
229
|
+
if (features.grpc)
|
|
230
|
+
await mkdir(join(srcDir, "grpc"), { recursive: true });
|
|
231
|
+
if (features.websocket)
|
|
232
|
+
await mkdir(join(srcDir, "ws"), { recursive: true });
|
|
233
|
+
await writeFile(join(srcDir, "config", `index.${ext}`), "");
|
|
234
|
+
await writeFile(join(srcDir, "config", `clients.${ext}`), "");
|
|
235
|
+
await writeFile(join(srcDir, "services", ".gitkeep"), "");
|
|
236
|
+
await writeFile(join(srcDir, `app.${ext}`), getMainFile(features.language, features.graphql, features));
|
|
237
|
+
await writeFile(join(srcDir, "routes", `home.${ext}`), getHomeRoute(features.language));
|
|
238
|
+
await writeFile(join(srcDir, "routes", `admin.${ext}`), getAdminRoute(features.language));
|
|
239
|
+
await writeFile(join(srcDir, "routes", `upload.${ext}`), getUploadRoute(features.language));
|
|
240
|
+
await writeFile(join(srcDir, "controllers", `home.${ext}`), getHomeController(features.language));
|
|
241
|
+
await writeFile(join(srcDir, "controllers", `admin.${ext}`), getAdminController(features.language));
|
|
242
|
+
await writeFile(join(srcDir, "controllers", `upload.${ext}`), getUploadController(features.language));
|
|
243
|
+
await writeFile(join(srcDir, "middlewares", `auth.internal.${ext}`), getInternalAuthMiddleware(features.language));
|
|
244
|
+
await writeFile(join(srcDir, "middlewares", `auth.user.${ext}`), getUserAuthMiddleware(features.language));
|
|
245
|
+
await writeFile(join(srcDir, "schemas", `home.${ext}`), getHomeSchema(features.language));
|
|
246
|
+
await writeFile(join(srcDir, "schemas", `admin.${ext}`), getAdminSchema(features.language));
|
|
247
|
+
await writeFile(join(srcDir, "schemas", `upload.${ext}`), getUploadSchema(features.language));
|
|
248
|
+
await writeFile(join(srcDir, "cronjobs", `example.${ext}`), getExampleCronJob(features.language));
|
|
249
|
+
if (features.queue !== "none")
|
|
250
|
+
await writeFile(join(srcDir, "services", `queue.${ext}`), getQueueService(features.language, features.queue));
|
|
251
|
+
if (features.cache !== "none")
|
|
252
|
+
await writeFile(join(srcDir, "services", `cache.${ext}`), getCacheService(features.language, features.cache));
|
|
253
|
+
if (features.trpc) {
|
|
254
|
+
await writeFile(join(srcDir, "trpc", `router.${ext}`), getTrpcRouter(features.language));
|
|
186
255
|
}
|
|
187
|
-
|
|
188
|
-
await writeFile(join(srcDir, "
|
|
189
|
-
await writeFile(join(srcDir, "config", "clients.js"), "");
|
|
256
|
+
if (features.grpc) {
|
|
257
|
+
await writeFile(join(srcDir, "grpc", `server.${ext}`), getGrpcServer(features.language));
|
|
190
258
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
await writeFile(join(srcDir, "controllers", "home." + (language === "typescript" ? "ts" : "js")), getHomeController(language));
|
|
197
|
-
await writeFile(join(srcDir, "controllers", "admin." + (language === "typescript" ? "ts" : "js")), getAdminController(language));
|
|
198
|
-
await writeFile(join(srcDir, "controllers", "upload." + (language === "typescript" ? "ts" : "js")), getUploadController(language));
|
|
199
|
-
await writeFile(join(srcDir, "middlewares", "auth.internal." + (language === "typescript" ? "ts" : "js")), getInternalAuthMiddleware(language));
|
|
200
|
-
await writeFile(join(srcDir, "middlewares", "auth.user." + (language === "typescript" ? "ts" : "js")), getUserAuthMiddleware(language));
|
|
201
|
-
await writeFile(join(srcDir, "schemas", "home." + (language === "typescript" ? "ts" : "js")), getHomeSchema(language));
|
|
202
|
-
await writeFile(join(srcDir, "schemas", "admin." + (language === "typescript" ? "ts" : "js")), getAdminSchema(language));
|
|
203
|
-
await writeFile(join(srcDir, "schemas", "upload." + (language === "typescript" ? "ts" : "js")), getUploadSchema(language));
|
|
204
|
-
await writeFile(join(srcDir, "cronjobs", "example." + (language === "typescript" ? "ts" : "js")), getExampleCronJob(language));
|
|
205
|
-
if (graphql) {
|
|
206
|
-
const graphqlFiles = getGraphQLFiles(language);
|
|
207
|
-
const ext = language === "typescript" ? "ts" : "js";
|
|
259
|
+
if (features.websocket) {
|
|
260
|
+
await writeFile(join(srcDir, "ws", `chat.${ext}`), getWebSocketScaffold(features.language));
|
|
261
|
+
}
|
|
262
|
+
if (features.graphql) {
|
|
263
|
+
const graphqlFiles = getGraphQLFiles(features.language);
|
|
208
264
|
await writeFile(join(srcDir, "graphql", `types.${ext}`), graphqlFiles["types.ts"]);
|
|
209
265
|
await writeFile(join(srcDir, "graphql", `resolvers.${ext}`), graphqlFiles["resolvers.ts"]);
|
|
210
266
|
await writeFile(join(srcDir, "graphql", `schema.${ext}`), graphqlFiles["schema.ts"]);
|
|
211
267
|
}
|
|
212
|
-
await writeFile(join(targetDir, ".env.development.example"), getEnvExample(telemetry));
|
|
213
|
-
await writeFile(join(targetDir, ".env.production.example"), getEnvExample(telemetry));
|
|
214
|
-
await writeFile(join(targetDir, ".env.development"), getEnvDevelopment(telemetry));
|
|
215
|
-
await writeFile(join(targetDir, ".env.production"), getEnvProduction(telemetry));
|
|
268
|
+
await writeFile(join(targetDir, ".env.development.example"), getEnvExample(features.telemetry));
|
|
269
|
+
await writeFile(join(targetDir, ".env.production.example"), getEnvExample(features.telemetry));
|
|
270
|
+
await writeFile(join(targetDir, ".env.development"), getEnvDevelopment(features.telemetry));
|
|
271
|
+
await writeFile(join(targetDir, ".env.production"), getEnvProduction(features.telemetry));
|
|
216
272
|
await writeFile(join(targetDir, ".gitignore"), getGitignore());
|
|
217
|
-
if (
|
|
218
|
-
await writeFile(join(targetDir, "Dockerfile"), getDockerfile(language));
|
|
219
|
-
await writeFile(join(targetDir, "docker-compose.yml"), getDockerCompose(language));
|
|
273
|
+
if (features.docker) {
|
|
274
|
+
await writeFile(join(targetDir, "Dockerfile"), getDockerfile(features.language));
|
|
275
|
+
await writeFile(join(targetDir, "docker-compose.yml"), getDockerCompose(features.language));
|
|
220
276
|
await writeFile(join(targetDir, ".dockerignore"), getDockerIgnore());
|
|
221
277
|
}
|
|
222
278
|
}
|
|
@@ -28,56 +28,51 @@ export function getTsConfig() {
|
|
|
28
28
|
`;
|
|
29
29
|
}
|
|
30
30
|
;
|
|
31
|
-
|
|
31
|
+
function buildCorsBlock(corsInput) {
|
|
32
|
+
const trimmed = (corsInput ?? "").trim();
|
|
33
|
+
if (trimmed === "" || trimmed.toLowerCase() === "deny" || trimmed.toLowerCase() === "none") {
|
|
34
|
+
return ` cors: false,`;
|
|
35
|
+
}
|
|
36
|
+
if (trimmed === "*") {
|
|
37
|
+
return ` cors: { origin: "*" },`;
|
|
38
|
+
}
|
|
39
|
+
const origins = trimmed.split(",").map(o => o.trim()).filter(Boolean);
|
|
40
|
+
return ` cors: {
|
|
41
|
+
origin: ${JSON.stringify(origins)},
|
|
42
|
+
credentials: true,
|
|
43
|
+
methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
|
|
44
|
+
allowedHeaders: ["Content-Type", "Authorization"]
|
|
45
|
+
},`;
|
|
46
|
+
}
|
|
47
|
+
export function getSprintConfigFile(language, telemetry, swagger, graphql, features) {
|
|
48
|
+
const corsInput = features?.cors ?? "";
|
|
32
49
|
const swaggerEnabled = swagger ? "true" : "false";
|
|
33
50
|
const swaggerUiEnabled = swagger ? '["development"]' : "false";
|
|
34
51
|
const graphqlEnabled = graphql ? "true" : "false";
|
|
35
52
|
const graphiqlEnabled = graphql ? '["development"]' : "false";
|
|
36
|
-
|
|
37
|
-
|
|
53
|
+
const corsBlock = buildCorsBlock(corsInput);
|
|
54
|
+
const isTs = language === "typescript";
|
|
55
|
+
const importLine = isTs ? `import type { SprintOptions } from "sprint-es";\n\n` : "";
|
|
56
|
+
const exportLine = isTs ? `export const config: SprintOptions = {` : `export const config = {`;
|
|
57
|
+
let config = `${importLine}${exportLine}
|
|
58
|
+
${corsBlock}
|
|
38
59
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
enabled: ${swaggerEnabled},
|
|
42
|
-
generateOnBuild: ${swaggerEnabled},
|
|
43
|
-
swaggerUi: {
|
|
44
|
-
enabled: ${swaggerUiEnabled}
|
|
45
|
-
}
|
|
60
|
+
security: {
|
|
61
|
+
hsts: { maxAge: 63072000, includeSubDomains: true }
|
|
46
62
|
},
|
|
47
|
-
graphql: {
|
|
48
|
-
enabled: ${graphqlEnabled},
|
|
49
|
-
graphiql: {
|
|
50
|
-
enabled: ${graphiqlEnabled}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
};
|
|
54
63
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
`;
|
|
59
|
-
if (telemetry === "sentry" || telemetry === "glitchtip") {
|
|
60
|
-
config += `import { initTelemetry } from "sprint-es/telemetry";
|
|
64
|
+
context: {
|
|
65
|
+
trustIncomingRequestId: true
|
|
66
|
+
},
|
|
61
67
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
else if (telemetry === "discord") {
|
|
70
|
-
config += `import { initTelemetry } from "sprint-es/telemetry";
|
|
68
|
+
errorHandler: {
|
|
69
|
+
includeStack: process.env.NODE_ENV !== "production"
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
shutdown: {
|
|
73
|
+
timeoutMs: 30_000
|
|
74
|
+
},
|
|
71
75
|
|
|
72
|
-
initTelemetry({
|
|
73
|
-
provider: "discord",
|
|
74
|
-
webhookUrl: process.env.DISCORD_TELEMETRY_WEBHOOK_URL || ""
|
|
75
|
-
});
|
|
76
|
-
`;
|
|
77
|
-
}
|
|
78
|
-
return config;
|
|
79
|
-
}
|
|
80
|
-
let config = `export const config = {
|
|
81
76
|
openapi: {
|
|
82
77
|
enabled: ${swaggerEnabled},
|
|
83
78
|
generateOnBuild: ${swaggerEnabled},
|
|
@@ -85,6 +80,7 @@ initTelemetry({
|
|
|
85
80
|
enabled: ${swaggerUiEnabled}
|
|
86
81
|
}
|
|
87
82
|
},
|
|
83
|
+
|
|
88
84
|
graphql: {
|
|
89
85
|
enabled: ${graphqlEnabled},
|
|
90
86
|
graphiql: {
|
|
@@ -92,14 +88,9 @@ initTelemetry({
|
|
|
92
88
|
}
|
|
93
89
|
}
|
|
94
90
|
};
|
|
95
|
-
|
|
96
|
-
// To use GraphQL, create a schema at src/graphql/schema.js and import it here
|
|
97
|
-
// import { GraphQLSchema } from "graphql";
|
|
98
|
-
// export const graphqlSchema = new GraphQLSchema({ ... });
|
|
99
91
|
`;
|
|
100
92
|
if (telemetry === "sentry" || telemetry === "glitchtip") {
|
|
101
|
-
config +=
|
|
102
|
-
import { initTelemetry } from "sprint-es/telemetry";
|
|
93
|
+
config += `\nimport { initTelemetry } from "sprint-es/telemetry";
|
|
103
94
|
|
|
104
95
|
initTelemetry({
|
|
105
96
|
provider: "${telemetry}",
|
|
@@ -109,8 +100,7 @@ initTelemetry({
|
|
|
109
100
|
`;
|
|
110
101
|
}
|
|
111
102
|
else if (telemetry === "discord") {
|
|
112
|
-
config +=
|
|
113
|
-
import { initTelemetry } from "sprint-es/telemetry";
|
|
103
|
+
config += `\nimport { initTelemetry } from "sprint-es/telemetry";
|
|
114
104
|
|
|
115
105
|
initTelemetry({
|
|
116
106
|
provider: "discord",
|
package/dist/templates/index.js
CHANGED
|
@@ -18,5 +18,7 @@ export { getExampleCronJob } from "./cronjobs.js";
|
|
|
18
18
|
export { getDockerfile, getDockerCompose } from "./docker.js";
|
|
19
19
|
// GraphQL
|
|
20
20
|
export { getGraphQLFiles } from "./graphql.js";
|
|
21
|
+
// Services (queue, cache, trpc, grpc, websocket scaffolds)
|
|
22
|
+
export { getQueueService, getCacheService, getTrpcRouter, getGrpcServer, getWebSocketScaffold } from "./services.js";
|
|
21
23
|
// Misc
|
|
22
24
|
export { getGitignore, getDockerIgnore } from "./misc.js";
|
|
@@ -8,9 +8,23 @@ export function generateJWTKeys() {
|
|
|
8
8
|
return keys;
|
|
9
9
|
}
|
|
10
10
|
;
|
|
11
|
-
|
|
11
|
+
function applyFeatureDeps(deps, features) {
|
|
12
|
+
if (!features)
|
|
13
|
+
return;
|
|
14
|
+
if (features.queue === "bullmq")
|
|
15
|
+
deps["bullmq"] = "^5.0.0";
|
|
16
|
+
if (features.queue === "bullmq" || features.cache === "redis")
|
|
17
|
+
deps["ioredis"] = "^5.0.0";
|
|
18
|
+
if (features.websocket)
|
|
19
|
+
deps["ws"] = "^8.18.0";
|
|
20
|
+
if (features.trpc)
|
|
21
|
+
deps["@trpc/server"] = "^11.0.0";
|
|
22
|
+
if (features.grpc)
|
|
23
|
+
deps["@grpc/grpc-js"] = "^1.12.0";
|
|
24
|
+
}
|
|
25
|
+
export function getTypeScriptPackageJson(name, telemetry, swagger, graphql, features) {
|
|
12
26
|
const deps = {
|
|
13
|
-
"sprint-es": "^0.0
|
|
27
|
+
"sprint-es": "^1.0.0"
|
|
14
28
|
};
|
|
15
29
|
const devDeps = {
|
|
16
30
|
"@types/node": "^22.0.0",
|
|
@@ -32,6 +46,9 @@ export function getTypeScriptPackageJson(name, telemetry, swagger, graphql) {
|
|
|
32
46
|
deps["ruru"] = "^2.0.0-rc.6";
|
|
33
47
|
devDeps["@types/swagger-ui-express"] = "^4.1.8";
|
|
34
48
|
}
|
|
49
|
+
applyFeatureDeps(deps, features);
|
|
50
|
+
if (features?.websocket)
|
|
51
|
+
devDeps["@types/ws"] = "^8.5.10";
|
|
35
52
|
return {
|
|
36
53
|
name: name === "." ? "sprint-app" : name,
|
|
37
54
|
version: "0.0.1",
|
|
@@ -65,9 +82,9 @@ export function getTypeScriptPackageJson(name, telemetry, swagger, graphql) {
|
|
|
65
82
|
};
|
|
66
83
|
}
|
|
67
84
|
;
|
|
68
|
-
export function getJavaScriptPackageJson(name, telemetry, swagger, graphql) {
|
|
85
|
+
export function getJavaScriptPackageJson(name, telemetry, swagger, graphql, features) {
|
|
69
86
|
const deps = {
|
|
70
|
-
"sprint-es": "^0.0
|
|
87
|
+
"sprint-es": "^1.0.0"
|
|
71
88
|
};
|
|
72
89
|
if (telemetry === "sentry" || telemetry === "glitchtip")
|
|
73
90
|
deps["@sentry/node"] = "^8.0.0";
|
|
@@ -80,6 +97,7 @@ export function getJavaScriptPackageJson(name, telemetry, swagger, graphql) {
|
|
|
80
97
|
deps["graphql-http"] = "^1.22.4";
|
|
81
98
|
deps["ruru"] = "^2.0.0-rc.6";
|
|
82
99
|
}
|
|
100
|
+
applyFeatureDeps(deps, features);
|
|
83
101
|
return {
|
|
84
102
|
name: name === "." ? "sprint-app" : name,
|
|
85
103
|
version: "0.0.1",
|
package/dist/templates/routes.js
CHANGED
|
@@ -1,31 +1,40 @@
|
|
|
1
|
-
export function getMainFile(language, graphql = false) {
|
|
1
|
+
export function getMainFile(language, graphql = false, features) {
|
|
2
2
|
const isTs = language === "typescript";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { graphqlSchema } from "./graphql/schema";
|
|
7
|
-
|
|
8
|
-
const app = new Sprint();
|
|
9
|
-
app.setGraphQLSchema(graphqlSchema);
|
|
10
|
-
`;
|
|
11
|
-
}
|
|
12
|
-
return `import Sprint from "sprint-es";
|
|
13
|
-
|
|
14
|
-
const app = new Sprint();
|
|
15
|
-
`;
|
|
16
|
-
}
|
|
3
|
+
const ext = isTs ? "" : ".js";
|
|
4
|
+
const imports = [`import Sprint from "sprint-es";`];
|
|
5
|
+
const setup = [`const app = new Sprint();`];
|
|
17
6
|
if (graphql) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const app = new Sprint();
|
|
22
|
-
app.setGraphQLSchema(graphqlSchema);
|
|
23
|
-
`;
|
|
7
|
+
imports.push(`import { graphqlSchema } from "./graphql/schema${ext}";`);
|
|
8
|
+
setup.push(`app.setGraphQLSchema(graphqlSchema);`);
|
|
24
9
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
10
|
+
if (features?.queue && features.queue !== "none") {
|
|
11
|
+
imports.push(`import "./services/queue${ext}";`);
|
|
12
|
+
}
|
|
13
|
+
if (features?.cache && features.cache !== "none") {
|
|
14
|
+
imports.push(`import "./services/cache${ext}";`);
|
|
15
|
+
}
|
|
16
|
+
if (features?.csrf) {
|
|
17
|
+
// CSRF is registered via defineMiddleware in src/middlewares/csrf.ts; just hint here
|
|
18
|
+
}
|
|
19
|
+
if (features?.trpc) {
|
|
20
|
+
imports.push(`import { attachTrpc } from "sprint-es/trpc";`);
|
|
21
|
+
imports.push(`import { appRouter } from "./trpc/router${ext}";`);
|
|
22
|
+
setup.push(`await app.ready;`);
|
|
23
|
+
setup.push(`await attachTrpc({ app: app.app, router: appRouter });`);
|
|
24
|
+
}
|
|
25
|
+
if (features?.websocket) {
|
|
26
|
+
imports.push(`import { attachWebSocket } from "sprint-es/ws";`);
|
|
27
|
+
imports.push(`import { chatHandler } from "./ws/chat${ext}";`);
|
|
28
|
+
setup.push(`{
|
|
29
|
+
const server = await app.onListen();
|
|
30
|
+
await attachWebSocket({ server, handlers: { "/ws/chat": chatHandler } });
|
|
31
|
+
}`);
|
|
32
|
+
}
|
|
33
|
+
if (features?.grpc) {
|
|
34
|
+
imports.push(`import { startGrpcServer } from "./grpc/server${ext}";`);
|
|
35
|
+
setup.push(`startGrpcServer().catch(err => console.error("[grpc] failed to start", err));`);
|
|
36
|
+
}
|
|
37
|
+
return imports.join("\n") + "\n\n" + setup.join("\n") + "\n";
|
|
29
38
|
}
|
|
30
39
|
;
|
|
31
40
|
export function getHomeRoute(language) {
|