deploy-bbc 0.0.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/CLAUDE.md +329 -0
- package/README.md +0 -0
- package/cli/README.md +0 -0
- package/cli/package.json +43 -0
- package/cli/src/cli/index.ts +449 -0
- package/cli/src/helpers/create-project.ts +66 -0
- package/cli/src/helpers/generate-docker-compose.ts +133 -0
- package/cli/src/helpers/generate-dockerfile.ts +45 -0
- package/cli/src/helpers/init-git.ts +33 -0
- package/cli/src/helpers/install-dependencies.ts +28 -0
- package/cli/src/helpers/log-next-steps.ts +84 -0
- package/cli/src/helpers/scaffold-project.ts +62 -0
- package/cli/src/index.ts +18 -0
- package/cli/src/installers/ai.ts +123 -0
- package/cli/src/installers/auth.ts +132 -0
- package/cli/src/installers/base.ts +16 -0
- package/cli/src/installers/cloud.ts +127 -0
- package/cli/src/installers/database.ts +212 -0
- package/cli/src/installers/docs.ts +93 -0
- package/cli/src/installers/email.ts +119 -0
- package/cli/src/installers/env-variables.ts +27 -0
- package/cli/src/installers/index.ts +145 -0
- package/cli/src/installers/observability.ts +103 -0
- package/cli/src/installers/queue.ts +103 -0
- package/cli/src/installers/ratelimit.ts +98 -0
- package/cli/src/installers/realtime.ts +79 -0
- package/cli/src/installers/testing.ts +88 -0
- package/cli/src/installers/validation.ts +85 -0
- package/cli/src/templates/base/.env.example +3 -0
- package/cli/src/templates/base/README.md +31 -0
- package/cli/src/templates/base/package.json +20 -0
- package/cli/src/templates/base/src/config/index.ts +8 -0
- package/cli/src/templates/base/src/index.ts +26 -0
- package/cli/src/templates/base/src/middleware/error.ts +13 -0
- package/cli/src/templates/base/src/middleware/logger.ts +8 -0
- package/cli/src/templates/base/src/routes/index.ts +12 -0
- package/cli/src/templates/base/src/types/index.ts +2 -0
- package/cli/src/templates/base/src/utils/env.ts +5 -0
- package/cli/src/templates/base/tsconfig.json +20 -0
- package/cli/src/templates/base-bun-native/.env.example +3 -0
- package/cli/src/templates/base-bun-native/README.md +31 -0
- package/cli/src/templates/base-bun-native/package.json +19 -0
- package/cli/src/templates/base-bun-native/src/config/index.ts +8 -0
- package/cli/src/templates/base-bun-native/src/index.ts +50 -0
- package/cli/src/templates/base-bun-native/src/middleware/error.ts +20 -0
- package/cli/src/templates/base-bun-native/src/middleware/logger.ts +6 -0
- package/cli/src/templates/base-bun-native/src/routes/index.ts +21 -0
- package/cli/src/templates/base-bun-native/src/types/index.ts +2 -0
- package/cli/src/templates/base-bun-native/src/utils/env.ts +5 -0
- package/cli/src/templates/base-bun-native/tsconfig.json +20 -0
- package/cli/src/templates/base-express/.env.example +3 -0
- package/cli/src/templates/base-express/README.md +31 -0
- package/cli/src/templates/base-express/package.json +21 -0
- package/cli/src/templates/base-express/src/config/index.ts +8 -0
- package/cli/src/templates/base-express/src/index.ts +27 -0
- package/cli/src/templates/base-express/src/middleware/error.ts +15 -0
- package/cli/src/templates/base-express/src/middleware/logger.ts +12 -0
- package/cli/src/templates/base-express/src/routes/index.ts +12 -0
- package/cli/src/templates/base-express/src/types/index.ts +2 -0
- package/cli/src/templates/base-express/src/utils/env.ts +5 -0
- package/cli/src/templates/base-express/tsconfig.json +20 -0
- package/cli/src/templates/extras/ai/anthropic/src/routes/ai/claude.ts +0 -0
- package/cli/src/templates/extras/ai/anthropic/src/services/ai/anthropic.ts +0 -0
- package/cli/src/templates/extras/ai/gemini/src/services/ai/gemini.ts +0 -0
- package/cli/src/templates/extras/ai/openai/src/routes/ai/chat.ts +0 -0
- package/cli/src/templates/extras/ai/openai/src/services/ai/openai.ts +0 -0
- package/cli/src/templates/extras/ai/vercel-ai/src/routes/ai/generate.ts +0 -0
- package/cli/src/templates/extras/ai/vercel-ai/src/routes/ai/stream.ts +0 -0
- package/cli/src/templates/extras/ai/vercel-ai/src/services/ai/index.ts +0 -0
- package/cli/src/templates/extras/auth/jwt/src/middleware/auth.ts +0 -0
- package/cli/src/templates/extras/auth/jwt/src/routes/auth.ts +0 -0
- package/cli/src/templates/extras/auth/jwt/src/utils/jwt.ts +0 -0
- package/cli/src/templates/extras/auth/oauth/src/config/oauth.ts +0 -0
- package/cli/src/templates/extras/auth/oauth/src/routes/auth.ts +0 -0
- package/cli/src/templates/extras/auth/session/src/config/session.ts +0 -0
- package/cli/src/templates/extras/auth/session/src/middleware/session.ts +0 -0
- package/cli/src/templates/extras/cloud/aws/src/services/aws/s3.ts +0 -0
- package/cli/src/templates/extras/cloud/aws/src/services/aws/ses.ts +0 -0
- package/cli/src/templates/extras/cloud/azure/src/services/azure/blob.ts +0 -0
- package/cli/src/templates/extras/cloud/cloudflare-r2/src/services/cloudflare/r2.ts +0 -0
- package/cli/src/templates/extras/cloud/gcp/src/services/gcp/storage.ts +0 -0
- package/cli/src/templates/extras/database/mongodb/src/db/index.ts +0 -0
- package/cli/src/templates/extras/database/mongodb/src/db/models/user.model.ts +0 -0
- package/cli/src/templates/extras/database/mysql/drizzle.config.ts +0 -0
- package/cli/src/templates/extras/database/postgres/src/db/index.ts +0 -0
- package/cli/src/templates/extras/database/redis/src/db/redis.ts +0 -0
- package/cli/src/templates/extras/docs/scalar/src/openapi/index.ts +0 -0
- package/cli/src/templates/extras/docs/scalar/src/routes/docs.ts +0 -0
- package/cli/src/templates/extras/docs/swagger/src/openapi/index.ts +0 -0
- package/cli/src/templates/extras/docs/swagger/src/routes/docs.ts +0 -0
- package/cli/src/templates/extras/email/nodemailer/src/services/email/nodemailer.ts +0 -0
- package/cli/src/templates/extras/email/resend/src/services/email/resend.ts +0 -0
- package/cli/src/templates/extras/email/resend/src/templates/email/welcome.ts +0 -0
- package/cli/src/templates/extras/email/sendgrid/src/services/email/sendgrid.ts +0 -0
- package/cli/src/templates/extras/observability/logtail/src/config/logger.ts +0 -0
- package/cli/src/templates/extras/observability/sentry/src/config/sentry.ts +0 -0
- package/cli/src/templates/extras/observability/sentry/src/middleware/sentry.ts +0 -0
- package/cli/src/templates/extras/queue/bullmq/src/queue/index.ts +0 -0
- package/cli/src/templates/extras/queue/bullmq/src/queue/jobs/email.job.ts +0 -0
- package/cli/src/templates/extras/queue/bullmq/src/queue/processors/email.processor.ts +0 -0
- package/cli/src/templates/extras/queue/bullmq/src/routes/queue.ts +0 -0
- package/cli/src/templates/extras/queue/inngest/src/inngest/client.ts +0 -0
- package/cli/src/templates/extras/queue/inngest/src/inngest/functions/email.ts +0 -0
- package/cli/src/templates/extras/queue/inngest/src/routes/inngest.ts +0 -0
- package/cli/src/templates/extras/realtime/socketio/src/socket/handlers.ts +0 -0
- package/cli/src/templates/extras/realtime/socketio/src/socket/index.ts +0 -0
- package/cli/src/templates/extras/realtime/sse/src/routes/sse.ts +0 -0
- package/cli/src/templates/extras/testing/vitest/src/__tests__/example.test.ts +0 -0
- package/cli/src/templates/extras/testing/vitest/src/__tests__/setup.ts +0 -0
- package/cli/src/templates/extras/testing/vitest/vitest.config.ts +0 -0
- package/cli/src/templates/extras/validation/yup/src/middleware/index.ts +1 -0
- package/cli/src/templates/extras/validation/yup/src/middleware/validate.ts +83 -0
- package/cli/src/templates/extras/validation/yup/src/routes/users.ts +132 -0
- package/cli/src/templates/extras/validation/zod/src/middleware/index.ts +1 -0
- package/cli/src/templates/extras/validation/zod/src/middleware/validate.ts +80 -0
- package/cli/src/templates/extras/validation/zod/src/routes/users.ts +128 -0
- package/cli/src/types/index.ts +126 -0
- package/cli/src/utils/add-package-dependency.ts +56 -0
- package/cli/src/utils/dependency-version-map.ts +85 -0
- package/cli/src/utils/logger.ts +19 -0
- package/cli/src/utils/parse-name-and-path.ts +55 -0
- package/cli/src/utils/render-title.ts +11 -0
- package/cli/tsconfig.json +35 -0
- package/package.json +20 -0
- package/prettier.config.mjs +0 -0
- package/test-cli.sh +56 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Context, Next } from "hono";
|
|
2
|
+
import { z, ZodSchema } from "zod";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Validation middleware for Hono using Zod schemas
|
|
6
|
+
* Validates request body, query parameters, or route params
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type ValidateTarget = "body" | "query" | "param";
|
|
10
|
+
|
|
11
|
+
interface ValidationOptions {
|
|
12
|
+
target: ValidateTarget;
|
|
13
|
+
schema: ZodSchema;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Create a validation middleware
|
|
18
|
+
* @param options - Validation configuration
|
|
19
|
+
* @returns Hono middleware function
|
|
20
|
+
*/
|
|
21
|
+
export function validate(options: ValidationOptions) {
|
|
22
|
+
return async (c: Context, next: Next) => {
|
|
23
|
+
try {
|
|
24
|
+
let data: unknown;
|
|
25
|
+
|
|
26
|
+
switch (options.target) {
|
|
27
|
+
case "body":
|
|
28
|
+
data = await c.req.json();
|
|
29
|
+
break;
|
|
30
|
+
case "query":
|
|
31
|
+
data = c.req.query();
|
|
32
|
+
break;
|
|
33
|
+
case "param":
|
|
34
|
+
data = c.req.param();
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
return c.json(
|
|
38
|
+
{ error: "Invalid validation target" },
|
|
39
|
+
400
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Validate data against schema
|
|
44
|
+
const validated = options.schema.parse(data);
|
|
45
|
+
|
|
46
|
+
// Store validated data in context for route handlers
|
|
47
|
+
c.set(`validated_${options.target}`, validated);
|
|
48
|
+
|
|
49
|
+
await next();
|
|
50
|
+
} catch (error) {
|
|
51
|
+
if (error instanceof z.ZodError) {
|
|
52
|
+
return c.json(
|
|
53
|
+
{
|
|
54
|
+
error: "Validation failed",
|
|
55
|
+
details: error.errors.map((err) => ({
|
|
56
|
+
path: err.path.join("."),
|
|
57
|
+
message: err.message,
|
|
58
|
+
})),
|
|
59
|
+
},
|
|
60
|
+
400
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return c.json(
|
|
65
|
+
{ error: "Invalid request data" },
|
|
66
|
+
400
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Helper to get validated data from context
|
|
74
|
+
* @param c - Hono context
|
|
75
|
+
* @param target - Validation target (body/query/param)
|
|
76
|
+
* @returns Validated data
|
|
77
|
+
*/
|
|
78
|
+
export function get_validated<T>(c: Context, target: ValidateTarget): T {
|
|
79
|
+
return c.get(`validated_${target}`) as T;
|
|
80
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { validate, get_validated } from "../middleware/validate.js";
|
|
4
|
+
|
|
5
|
+
const users = new Hono();
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Zod Schemas for validation
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Schema for creating a new user
|
|
12
|
+
const create_user_schema = z.object({
|
|
13
|
+
name: z.string().min(2, "Name must be at least 2 characters"),
|
|
14
|
+
email: z.string().email("Invalid email format"),
|
|
15
|
+
age: z.number().int().min(18, "Must be at least 18 years old").optional(),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// Schema for updating a user
|
|
19
|
+
const update_user_schema = z.object({
|
|
20
|
+
name: z.string().min(2).optional(),
|
|
21
|
+
email: z.string().email().optional(),
|
|
22
|
+
age: z.number().int().min(18).optional(),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// Schema for query parameters
|
|
26
|
+
const list_users_query_schema = z.object({
|
|
27
|
+
page: z.string().regex(/^\d+$/).transform(Number).optional(),
|
|
28
|
+
limit: z.string().regex(/^\d+$/).transform(Number).optional(),
|
|
29
|
+
search: z.string().optional(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Schema for route params
|
|
33
|
+
const user_id_param_schema = z.object({
|
|
34
|
+
id: z.string().uuid("Invalid user ID format"),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Routes with validation
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
// GET /users - List users with query validation
|
|
42
|
+
users.get(
|
|
43
|
+
"/",
|
|
44
|
+
validate({ target: "query", schema: list_users_query_schema }),
|
|
45
|
+
(c) => {
|
|
46
|
+
const query = get_validated<z.infer<typeof list_users_query_schema>>(c, "query");
|
|
47
|
+
|
|
48
|
+
return c.json({
|
|
49
|
+
users: [],
|
|
50
|
+
pagination: {
|
|
51
|
+
page: query.page || 1,
|
|
52
|
+
limit: query.limit || 10,
|
|
53
|
+
search: query.search,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
// POST /users - Create user with body validation
|
|
60
|
+
users.post(
|
|
61
|
+
"/",
|
|
62
|
+
validate({ target: "body", schema: create_user_schema }),
|
|
63
|
+
(c) => {
|
|
64
|
+
const body = get_validated<z.infer<typeof create_user_schema>>(c, "body");
|
|
65
|
+
|
|
66
|
+
// Your database logic here
|
|
67
|
+
const new_user = {
|
|
68
|
+
id: crypto.randomUUID(),
|
|
69
|
+
...body,
|
|
70
|
+
created_at: new Date().toISOString(),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return c.json(new_user, 201);
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// GET /users/:id - Get user by ID with param validation
|
|
78
|
+
users.get(
|
|
79
|
+
"/:id",
|
|
80
|
+
validate({ target: "param", schema: user_id_param_schema }),
|
|
81
|
+
(c) => {
|
|
82
|
+
const params = get_validated<z.infer<typeof user_id_param_schema>>(c, "param");
|
|
83
|
+
|
|
84
|
+
// Your database logic here
|
|
85
|
+
const user = {
|
|
86
|
+
id: params.id,
|
|
87
|
+
name: "John Doe",
|
|
88
|
+
email: "john@example.com",
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return c.json(user);
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
// PATCH /users/:id - Update user with param + body validation
|
|
96
|
+
users.patch(
|
|
97
|
+
"/:id",
|
|
98
|
+
validate({ target: "param", schema: user_id_param_schema }),
|
|
99
|
+
validate({ target: "body", schema: update_user_schema }),
|
|
100
|
+
(c) => {
|
|
101
|
+
const params = get_validated<z.infer<typeof user_id_param_schema>>(c, "param");
|
|
102
|
+
const body = get_validated<z.infer<typeof update_user_schema>>(c, "body");
|
|
103
|
+
|
|
104
|
+
// Your database logic here
|
|
105
|
+
const updated_user = {
|
|
106
|
+
id: params.id,
|
|
107
|
+
...body,
|
|
108
|
+
updated_at: new Date().toISOString(),
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return c.json(updated_user);
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
// DELETE /users/:id
|
|
116
|
+
users.delete(
|
|
117
|
+
"/:id",
|
|
118
|
+
validate({ target: "param", schema: user_id_param_schema }),
|
|
119
|
+
(c) => {
|
|
120
|
+
const params = get_validated<z.infer<typeof user_id_param_schema>>(c, "param");
|
|
121
|
+
|
|
122
|
+
return c.json({
|
|
123
|
+
message: `User ${params.id} deleted successfully`,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
export default users;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
export interface CliFlags {
|
|
2
|
+
noGit: boolean;
|
|
3
|
+
noInstall: boolean;
|
|
4
|
+
default: boolean;
|
|
5
|
+
CI: boolean;
|
|
6
|
+
|
|
7
|
+
// Database flags
|
|
8
|
+
postgres?: boolean;
|
|
9
|
+
mysql?: boolean;
|
|
10
|
+
mongodb?: boolean;
|
|
11
|
+
redis?: boolean;
|
|
12
|
+
|
|
13
|
+
// Auth flags
|
|
14
|
+
jwt?: boolean;
|
|
15
|
+
oauth?: boolean;
|
|
16
|
+
session?: boolean;
|
|
17
|
+
|
|
18
|
+
// AI flags
|
|
19
|
+
openai?: boolean;
|
|
20
|
+
anthropic?: boolean;
|
|
21
|
+
gemini?: boolean;
|
|
22
|
+
vercelAI?: boolean;
|
|
23
|
+
|
|
24
|
+
// Cloud flags
|
|
25
|
+
aws?: boolean;
|
|
26
|
+
gcp?: boolean;
|
|
27
|
+
azure?: boolean;
|
|
28
|
+
cloudflareR2?: boolean;
|
|
29
|
+
|
|
30
|
+
// Communication flags
|
|
31
|
+
resend?: boolean;
|
|
32
|
+
sendgrid?: boolean;
|
|
33
|
+
nodemailer?: boolean;
|
|
34
|
+
socketio?: boolean;
|
|
35
|
+
sse?: boolean;
|
|
36
|
+
|
|
37
|
+
// Infrastructure flags
|
|
38
|
+
bullmq?: boolean;
|
|
39
|
+
inngest?: boolean;
|
|
40
|
+
upstashRateLimit?: boolean;
|
|
41
|
+
customRateLimit?: boolean;
|
|
42
|
+
sentry?: boolean;
|
|
43
|
+
logtail?: boolean;
|
|
44
|
+
|
|
45
|
+
// DevX flags
|
|
46
|
+
swagger?: boolean;
|
|
47
|
+
scalar?: boolean;
|
|
48
|
+
vitest?: boolean;
|
|
49
|
+
|
|
50
|
+
// Validation flags
|
|
51
|
+
zod?: boolean;
|
|
52
|
+
yup?: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export type Framework = "hono" | "express" | "bun-native";
|
|
56
|
+
|
|
57
|
+
export interface CliResults {
|
|
58
|
+
appName: string;
|
|
59
|
+
framework: Framework;
|
|
60
|
+
packages: AvailablePackages[];
|
|
61
|
+
flags: CliFlags;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export enum AvailablePackages {
|
|
65
|
+
// Core
|
|
66
|
+
postgres = "postgres",
|
|
67
|
+
mysql = "mysql",
|
|
68
|
+
mongodb = "mongodb",
|
|
69
|
+
redis = "redis",
|
|
70
|
+
jwt = "jwt",
|
|
71
|
+
oauth = "oauth",
|
|
72
|
+
session = "session",
|
|
73
|
+
|
|
74
|
+
// AI & ML
|
|
75
|
+
openai = "openai",
|
|
76
|
+
anthropic = "anthropic",
|
|
77
|
+
gemini = "gemini",
|
|
78
|
+
vercelAI = "vercel-ai",
|
|
79
|
+
|
|
80
|
+
// Cloud & Storage
|
|
81
|
+
aws = "aws",
|
|
82
|
+
gcp = "gcp",
|
|
83
|
+
azure = "azure",
|
|
84
|
+
cloudflareR2 = "cloudflare-r2",
|
|
85
|
+
|
|
86
|
+
// Communications
|
|
87
|
+
resend = "resend",
|
|
88
|
+
sendgrid = "sendgrid",
|
|
89
|
+
nodemailer = "nodemailer",
|
|
90
|
+
socketio = "socketio",
|
|
91
|
+
sse = "sse",
|
|
92
|
+
|
|
93
|
+
// Infrastructure
|
|
94
|
+
bullmq = "bullmq",
|
|
95
|
+
inngest = "inngest",
|
|
96
|
+
upstashRateLimit = "upstash-ratelimit",
|
|
97
|
+
customRateLimit = "custom-ratelimit",
|
|
98
|
+
sentry = "sentry",
|
|
99
|
+
logtail = "logtail",
|
|
100
|
+
|
|
101
|
+
// Developer Experience
|
|
102
|
+
swagger = "swagger",
|
|
103
|
+
scalar = "scalar",
|
|
104
|
+
vitest = "vitest",
|
|
105
|
+
|
|
106
|
+
// Validation
|
|
107
|
+
zod = "zod",
|
|
108
|
+
yup = "yup",
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface InstallerOptions {
|
|
112
|
+
projectDir: string;
|
|
113
|
+
packages: AvailablePackages[];
|
|
114
|
+
appName: string;
|
|
115
|
+
framework: Framework;
|
|
116
|
+
noInstall: boolean;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export type Installer = (opts: InstallerOptions) => Promise<void>;
|
|
120
|
+
|
|
121
|
+
export interface PkgInstallerMap {
|
|
122
|
+
[key: string]: {
|
|
123
|
+
inUse: boolean;
|
|
124
|
+
installer: Installer;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Adds dependencies to a project's package.json file.
|
|
6
|
+
* Reads the file, merges dependencies, sorts keys alphabetically, and writes back.
|
|
7
|
+
*
|
|
8
|
+
* @param projectDir - Absolute path to the project directory
|
|
9
|
+
* @param dependencies - Dependencies to add to the "dependencies" section
|
|
10
|
+
* @param devDependencies - Optional dev dependencies to add to "devDependencies" section
|
|
11
|
+
*/
|
|
12
|
+
export async function add_package_dependency(
|
|
13
|
+
projectDir: string,
|
|
14
|
+
dependencies: Record<string, string>,
|
|
15
|
+
devDependencies?: Record<string, string>
|
|
16
|
+
): Promise<void> {
|
|
17
|
+
const packageJsonPath = path.join(projectDir, "package.json");
|
|
18
|
+
|
|
19
|
+
// Read existing package.json
|
|
20
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
21
|
+
|
|
22
|
+
// Merge dependencies
|
|
23
|
+
if (dependencies && Object.keys(dependencies).length > 0) {
|
|
24
|
+
packageJson.dependencies = {
|
|
25
|
+
...packageJson.dependencies,
|
|
26
|
+
...dependencies,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Sort dependencies alphabetically
|
|
30
|
+
packageJson.dependencies = Object.keys(packageJson.dependencies)
|
|
31
|
+
.sort()
|
|
32
|
+
.reduce((acc: Record<string, string>, key: string) => {
|
|
33
|
+
acc[key] = packageJson.dependencies[key];
|
|
34
|
+
return acc;
|
|
35
|
+
}, {});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Merge devDependencies
|
|
39
|
+
if (devDependencies && Object.keys(devDependencies).length > 0) {
|
|
40
|
+
packageJson.devDependencies = {
|
|
41
|
+
...packageJson.devDependencies,
|
|
42
|
+
...devDependencies,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Sort devDependencies alphabetically
|
|
46
|
+
packageJson.devDependencies = Object.keys(packageJson.devDependencies)
|
|
47
|
+
.sort()
|
|
48
|
+
.reduce((acc: Record<string, string>, key: string) => {
|
|
49
|
+
acc[key] = packageJson.devDependencies[key];
|
|
50
|
+
return acc;
|
|
51
|
+
}, {});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Write back to package.json with proper formatting
|
|
55
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
56
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Central registry mapping package names to their versions.
|
|
3
|
+
* Single source of truth for all dependency versions used in generated projects.
|
|
4
|
+
*/
|
|
5
|
+
export const DEPENDENCY_VERSION_MAP: Record<string, string> = {
|
|
6
|
+
// Core Framework
|
|
7
|
+
"hono": "^4.0.0",
|
|
8
|
+
"express": "^4.18.2",
|
|
9
|
+
"@types/express": "^4.17.21",
|
|
10
|
+
|
|
11
|
+
// Database Drivers
|
|
12
|
+
"postgres": "^3.4.4",
|
|
13
|
+
"mysql2": "^3.9.0",
|
|
14
|
+
"mongoose": "^8.1.0",
|
|
15
|
+
"redis": "^4.6.12",
|
|
16
|
+
|
|
17
|
+
// ORM & Query Builders
|
|
18
|
+
"drizzle-orm": "^0.29.3",
|
|
19
|
+
"drizzle-kit": "^0.20.10",
|
|
20
|
+
"kysely": "^0.27.2",
|
|
21
|
+
|
|
22
|
+
// Authentication
|
|
23
|
+
"jsonwebtoken": "^9.0.2",
|
|
24
|
+
"@types/jsonwebtoken": "^9.0.5",
|
|
25
|
+
"bcryptjs": "^2.4.3",
|
|
26
|
+
"@types/bcryptjs": "^2.4.6",
|
|
27
|
+
"passport": "^0.7.0",
|
|
28
|
+
"passport-oauth2": "^1.8.0",
|
|
29
|
+
"@types/passport": "^1.0.16",
|
|
30
|
+
|
|
31
|
+
// AI Providers
|
|
32
|
+
"openai": "^4.26.0",
|
|
33
|
+
"@anthropic-ai/sdk": "^0.12.0",
|
|
34
|
+
"@google/generative-ai": "^0.1.3",
|
|
35
|
+
"ai": "^3.0.0",
|
|
36
|
+
|
|
37
|
+
// Cloud Storage
|
|
38
|
+
"@aws-sdk/client-s3": "^3.490.0",
|
|
39
|
+
"@aws-sdk/s3-request-presigner": "^3.490.0",
|
|
40
|
+
"@google-cloud/storage": "^7.7.0",
|
|
41
|
+
"@azure/storage-blob": "^12.17.0",
|
|
42
|
+
"@cloudflare/workers-types": "^4.20240117.0",
|
|
43
|
+
|
|
44
|
+
// Email Services
|
|
45
|
+
"resend": "^3.0.0",
|
|
46
|
+
"@sendgrid/mail": "^8.1.0",
|
|
47
|
+
"nodemailer": "^6.9.8",
|
|
48
|
+
"@types/nodemailer": "^6.4.14",
|
|
49
|
+
|
|
50
|
+
// Real-time Communication
|
|
51
|
+
"socket.io": "^4.6.1",
|
|
52
|
+
"@types/socket.io": "^3.0.0",
|
|
53
|
+
|
|
54
|
+
// Queue Systems
|
|
55
|
+
"bullmq": "^5.1.5",
|
|
56
|
+
"inngest": "^3.12.0",
|
|
57
|
+
|
|
58
|
+
// Rate Limiting
|
|
59
|
+
"@upstash/ratelimit": "^1.0.0",
|
|
60
|
+
"@upstash/redis": "^1.27.1",
|
|
61
|
+
|
|
62
|
+
// Observability
|
|
63
|
+
"@sentry/node": "^7.99.0",
|
|
64
|
+
"@logtail/node": "^0.4.13",
|
|
65
|
+
|
|
66
|
+
// API Documentation
|
|
67
|
+
"@hono/swagger": "^0.1.0",
|
|
68
|
+
"@scalar/hono-api-reference": "^0.5.0",
|
|
69
|
+
|
|
70
|
+
// Testing
|
|
71
|
+
"vitest": "^1.2.0",
|
|
72
|
+
"@vitest/ui": "^1.2.0",
|
|
73
|
+
"supertest": "^6.3.4",
|
|
74
|
+
"@types/supertest": "^6.0.2",
|
|
75
|
+
|
|
76
|
+
// Utilities
|
|
77
|
+
"zod": "^3.22.4",
|
|
78
|
+
"yup": "^1.3.3",
|
|
79
|
+
"dotenv": "^16.3.1",
|
|
80
|
+
"cors": "^2.8.5",
|
|
81
|
+
"@types/cors": "^2.8.17",
|
|
82
|
+
"helmet": "^7.1.0",
|
|
83
|
+
"compression": "^1.7.4",
|
|
84
|
+
"@types/compression": "^1.7.5",
|
|
85
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
|
|
3
|
+
export const logger = {
|
|
4
|
+
error: (...args: unknown[]) => {
|
|
5
|
+
console.log(chalk.red(...args));
|
|
6
|
+
},
|
|
7
|
+
warn: (...args: unknown[]) => {
|
|
8
|
+
console.log(chalk.yellow(...args));
|
|
9
|
+
},
|
|
10
|
+
info: (...args: unknown[]) => {
|
|
11
|
+
console.log(chalk.cyan(...args));
|
|
12
|
+
},
|
|
13
|
+
success: (...args: unknown[]) => {
|
|
14
|
+
console.log(chalk.green(...args));
|
|
15
|
+
},
|
|
16
|
+
debug: (...args: unknown[]) => {
|
|
17
|
+
console.log(chalk.gray(...args));
|
|
18
|
+
},
|
|
19
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
|
|
3
|
+
export interface ParsedNameAndPath {
|
|
4
|
+
projectName: string;
|
|
5
|
+
projectDir: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parses the app name input to extract project name and directory path.
|
|
10
|
+
* Handles relative paths (./my-app), absolute paths, and simple names.
|
|
11
|
+
*
|
|
12
|
+
* @param appName - The app name or path provided by the user
|
|
13
|
+
* @returns Object containing projectName (kebab-case) and projectDir (absolute path)
|
|
14
|
+
* @throws Error if the name contains invalid characters (spaces, special chars except - and _)
|
|
15
|
+
*/
|
|
16
|
+
export function parse_name_and_path(appName: string): ParsedNameAndPath {
|
|
17
|
+
// Validate app name - no spaces or invalid characters
|
|
18
|
+
// Allow only alphanumeric, hyphens, underscores, slashes, and dots (for paths)
|
|
19
|
+
const pathRegex = /^[a-zA-Z0-9\-_/.]+$/;
|
|
20
|
+
if (!pathRegex.test(appName)) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Invalid app name: "${appName}". Use only letters, numbers, hyphens, and underscores.`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let projectDir: string;
|
|
27
|
+
let projectName: string;
|
|
28
|
+
|
|
29
|
+
// Check if it's a path (contains / or starts with . or ~)
|
|
30
|
+
if (appName.includes("/") || appName.startsWith(".") || appName.startsWith("~")) {
|
|
31
|
+
// It's a path - resolve it to absolute
|
|
32
|
+
projectDir = path.resolve(process.cwd(), appName);
|
|
33
|
+
// Extract the last segment as the project name
|
|
34
|
+
projectName = path.basename(projectDir);
|
|
35
|
+
} else {
|
|
36
|
+
// It's just a name - create in current directory
|
|
37
|
+
projectName = appName;
|
|
38
|
+
projectDir = path.resolve(process.cwd(), appName);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Ensure project name is kebab-case (convert underscores to hyphens)
|
|
42
|
+
projectName = projectName.replace(/_/g, "-").toLowerCase();
|
|
43
|
+
|
|
44
|
+
// Additional validation - ensure name doesn't start/end with hyphen
|
|
45
|
+
if (projectName.startsWith("-") || projectName.endsWith("-")) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Invalid app name: "${projectName}". Name cannot start or end with a hyphen.`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
projectName,
|
|
53
|
+
projectDir,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Displays a styled section header in the terminal.
|
|
5
|
+
* Used to visually separate different stages of the CLI execution.
|
|
6
|
+
*
|
|
7
|
+
* @param title - The title text to display
|
|
8
|
+
*/
|
|
9
|
+
export function render_title(title: string): void {
|
|
10
|
+
console.log("\n" + chalk.bold.cyan(`▸ ${title}`));
|
|
11
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"lib": [
|
|
6
|
+
"ES2022"
|
|
7
|
+
],
|
|
8
|
+
"moduleResolution": "bundler",
|
|
9
|
+
"allowImportingTsExtensions": true,
|
|
10
|
+
"noEmit": true,
|
|
11
|
+
"composite": false,
|
|
12
|
+
"strict": true,
|
|
13
|
+
"downlevelIteration": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"jsx": "preserve",
|
|
16
|
+
"allowSyntheticDefaultImports": true,
|
|
17
|
+
"forceConsistentCasingInFileNames": true,
|
|
18
|
+
"allowJs": true,
|
|
19
|
+
"types": [
|
|
20
|
+
"bun-types"
|
|
21
|
+
],
|
|
22
|
+
"esModuleInterop": true,
|
|
23
|
+
"resolveJsonModule": true,
|
|
24
|
+
"isolatedModules": true,
|
|
25
|
+
"outDir": "dist",
|
|
26
|
+
"rootDir": "src"
|
|
27
|
+
},
|
|
28
|
+
"include": [
|
|
29
|
+
"src/**/*"
|
|
30
|
+
],
|
|
31
|
+
"exclude": [
|
|
32
|
+
"node_modules",
|
|
33
|
+
"dist"
|
|
34
|
+
]
|
|
35
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "deploy-bbc",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CLI to bootstrap production-ready backends with Bun (Best Backend Code)",
|
|
5
|
+
"workspaces": [
|
|
6
|
+
"cli"
|
|
7
|
+
],
|
|
8
|
+
"scripts": {
|
|
9
|
+
"dev": "bun --watch cli/src/index.ts",
|
|
10
|
+
"build": "cd cli && bun run build",
|
|
11
|
+
"lint": "eslint . --ext .ts",
|
|
12
|
+
"format": "prettier --write \"**/*.{ts,json,md}\""
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@types/bun": "latest",
|
|
16
|
+
"bun-types": "^1.3.7",
|
|
17
|
+
"prettier": "^3.1.1",
|
|
18
|
+
"typescript": "^5.3.3"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
File without changes
|
package/test-cli.sh
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Test script for deploy-bbc CLI
|
|
4
|
+
|
|
5
|
+
echo "🧪 Testing deploy-bbc CLI..."
|
|
6
|
+
echo ""
|
|
7
|
+
|
|
8
|
+
# Test 1: Minimal setup
|
|
9
|
+
echo "Test 1: Minimal setup (postgres + jwt)"
|
|
10
|
+
cd /tmp
|
|
11
|
+
bun ~/Documents/cli/cli/src/index.ts test-minimal --CI --postgres --jwt --noInstall --noGit
|
|
12
|
+
echo "✅ Test 1 passed"
|
|
13
|
+
echo ""
|
|
14
|
+
|
|
15
|
+
# Test 2: Full stack with AI
|
|
16
|
+
echo "Test 2: Full stack (postgres, redis, jwt, openai, vercel-ai)"
|
|
17
|
+
cd /tmp
|
|
18
|
+
bun ~/Documents/cli/cli/src/index.ts test-fullstack --CI \
|
|
19
|
+
--postgres --redis \
|
|
20
|
+
--jwt --session \
|
|
21
|
+
--openai --anthropic --vercelAI \
|
|
22
|
+
--resend \
|
|
23
|
+
--bullmq \
|
|
24
|
+
--swagger --vitest \
|
|
25
|
+
--noInstall --noGit
|
|
26
|
+
echo "✅ Test 2 passed"
|
|
27
|
+
echo ""
|
|
28
|
+
|
|
29
|
+
# Test 3: MongoDB + OAuth
|
|
30
|
+
echo "Test 3: MongoDB + OAuth setup"
|
|
31
|
+
cd /tmp
|
|
32
|
+
bun ~/Documents/cli/cli/src/index.ts test-mongo --CI \
|
|
33
|
+
--mongodb \
|
|
34
|
+
--oauth \
|
|
35
|
+
--socketio \
|
|
36
|
+
--scalar \
|
|
37
|
+
--noInstall --noGit
|
|
38
|
+
echo "✅ Test 3 passed"
|
|
39
|
+
echo ""
|
|
40
|
+
|
|
41
|
+
# Test 4: Cloud services
|
|
42
|
+
echo "Test 4: Cloud services (AWS, GCP, Azure)"
|
|
43
|
+
cd /tmp
|
|
44
|
+
bun ~/Documents/cli/cli/src/index.ts test-cloud --CI \
|
|
45
|
+
--postgres \
|
|
46
|
+
--jwt \
|
|
47
|
+
--aws --gcp --azure --cloudflareR2 \
|
|
48
|
+
--sentry \
|
|
49
|
+
--noInstall --noGit
|
|
50
|
+
echo "✅ Test 4 passed"
|
|
51
|
+
echo ""
|
|
52
|
+
|
|
53
|
+
echo "✨ All tests completed successfully!"
|
|
54
|
+
echo ""
|
|
55
|
+
echo "To test interactively, run:"
|
|
56
|
+
echo " cd /tmp && bun ~/Documents/cli/cli/src/index.ts my-project"
|