stackkit 0.1.4 → 0.1.6

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.
@@ -40,23 +40,26 @@ const fs = __importStar(require("fs-extra"));
40
40
  const path = __importStar(require("path"));
41
41
  const package_root_1 = require("../utils/package-root");
42
42
  async function mergeModuleIntoGeneratorConfig(config, modulePath) {
43
- const modulePathJson = path.join(modulePath, 'module.json');
43
+ const modulePathJson = path.join(modulePath, "module.json");
44
44
  if (await fs.pathExists(modulePathJson)) {
45
45
  try {
46
46
  const moduleConfig = await fs.readJson(modulePathJson);
47
47
  if (moduleConfig.postInstall && Array.isArray(moduleConfig.postInstall)) {
48
48
  config.postInstall = moduleConfig.postInstall;
49
49
  }
50
- if (moduleConfig.dependencies && typeof moduleConfig.dependencies === 'object') {
50
+ if (moduleConfig.dependencies && typeof moduleConfig.dependencies === "object") {
51
51
  config.dependencies = { ...(config.dependencies || {}), ...moduleConfig.dependencies };
52
52
  }
53
- if (moduleConfig.devDependencies && typeof moduleConfig.devDependencies === 'object') {
54
- config.devDependencies = { ...(config.devDependencies || {}), ...moduleConfig.devDependencies };
53
+ if (moduleConfig.devDependencies && typeof moduleConfig.devDependencies === "object") {
54
+ config.devDependencies = {
55
+ ...(config.devDependencies || {}),
56
+ ...moduleConfig.devDependencies,
57
+ };
55
58
  }
56
- if (moduleConfig.scripts && typeof moduleConfig.scripts === 'object') {
59
+ if (moduleConfig.scripts && typeof moduleConfig.scripts === "object") {
57
60
  config.scripts = { ...(config.scripts || {}), ...moduleConfig.scripts };
58
61
  }
59
- if (moduleConfig.envVars && typeof moduleConfig.envVars === 'object') {
62
+ if (moduleConfig.envVars && typeof moduleConfig.envVars === "object") {
60
63
  config.envVars = { ...(config.envVars || {}), ...moduleConfig.envVars };
61
64
  }
62
65
  }
@@ -67,14 +70,24 @@ async function mergeModuleIntoGeneratorConfig(config, modulePath) {
67
70
  return config;
68
71
  }
69
72
  async function mergeGeneratorIntoModuleMetadata(metadata, modulePath) {
70
- const generatorPath = path.join(modulePath, 'generator.json');
73
+ const generatorPath = path.join(modulePath, "generator.json");
71
74
  if (await fs.pathExists(generatorPath)) {
72
75
  try {
73
76
  const generator = await fs.readJson(generatorPath);
74
- if (generator.envVars) {
75
- metadata.envVars = metadata.envVars || [];
76
- for (const [key, value] of Object.entries(generator.envVars)) {
77
- metadata.envVars.push({ key, value: value, description: `Environment variable for ${key}`, required: true });
77
+ // Process add-env operations to extract envVars
78
+ if (generator.operations && Array.isArray(generator.operations)) {
79
+ for (const operation of generator.operations) {
80
+ if (operation.type === "add-env" && operation.envVars) {
81
+ metadata.envVars = metadata.envVars || [];
82
+ for (const [key, value] of Object.entries(operation.envVars)) {
83
+ metadata.envVars.push({
84
+ key,
85
+ value: value,
86
+ description: `Environment variable for ${key}`,
87
+ required: true,
88
+ });
89
+ }
90
+ }
78
91
  }
79
92
  }
80
93
  if (generator.dependencies) {
@@ -96,12 +109,12 @@ async function mergeGeneratorIntoModuleMetadata(metadata, modulePath) {
96
109
  }
97
110
  function locateOperationSource(generatorType, generatorName, sourceRel) {
98
111
  const packageRoot = (0, package_root_1.getPackageRoot)();
99
- const modulesPath = path.join(packageRoot, 'modules');
100
- const templatesPath = path.join(packageRoot, 'templates');
101
- const moduleBasePath = generatorType === 'framework'
112
+ const modulesPath = path.join(packageRoot, "modules");
113
+ const templatesPath = path.join(packageRoot, "templates");
114
+ const moduleBasePath = generatorType === "framework"
102
115
  ? path.join(templatesPath, generatorName)
103
116
  : path.join(modulesPath, generatorType, generatorName);
104
- const sourcePath = path.join(moduleBasePath, 'files', sourceRel);
117
+ const sourcePath = path.join(moduleBasePath, "files", sourceRel);
105
118
  try {
106
119
  return sourcePath;
107
120
  }
@@ -37,9 +37,9 @@ exports.getPackageRoot = getPackageRoot;
37
37
  const path = __importStar(require("path"));
38
38
  function getPackageRoot() {
39
39
  try {
40
- return path.dirname(require.resolve('stackkit/package.json'));
40
+ return path.dirname(require.resolve("stackkit/package.json"));
41
41
  }
42
42
  catch {
43
- return path.resolve(__dirname, '..', '..', '..');
43
+ return path.resolve(__dirname, "..", "..", "..");
44
44
  }
45
45
  }
@@ -5,6 +5,5 @@
5
5
  "operations": [],
6
6
  "dependencies": {},
7
7
  "devDependencies": {},
8
- "scripts": {},
9
- "envVars": {}
8
+ "scripts": {}
10
9
  }
@@ -1,24 +1,32 @@
1
1
  import { betterAuth } from "better-auth";
2
2
  import { sendEmail } from "./email/email-service";
3
3
  import { getVerificationEmailTemplate, getPasswordResetEmailTemplate } from "./email/email-templates";
4
- {{#if database == 'prisma'}}
4
+ {{#switch database}}
5
+ {{#case prisma}}
5
6
  import { prisma } from "{{framework == 'nextjs' ? '@/lib' : '.'}}/prisma";
6
7
  import { prismaAdapter } from "better-auth/adapters/prisma";
7
- {{/if}}
8
- {{#if database == 'mongoose'}}
9
- import { mongoClient, db } from "{{framework == 'nextjs' ? '@/lib' : '.'}}/db";
8
+ {{/case}}
9
+ {{#case mongoose}}
10
+ import { mongoose } from "{{framework == 'nextjs' ? '@/lib' : '.'}}/mongoose";
10
11
  import { mongodbAdapter } from "better-auth/adapters/mongodb";
11
- {{/if}}
12
+ {{/case}}
13
+ {{/switch}}
12
14
 
13
- export const auth = betterAuth({
14
- {{#if database == 'prisma'}}
15
+ export async function initAuth() {
16
+ return betterAuth({
17
+ {{#switch database}}
18
+ {{#case prisma}}
15
19
  database: prismaAdapter(prisma, {
16
- provider: "{{prismaProvider}}",
20
+ provider: "{{prismaProvider}}",
17
21
  }),
18
- {{/if}}
19
- {{#if database == 'mongoose'}}
20
- database: mongodbAdapter(db),
21
- {{/if}}
22
+ {{/case}}
23
+ {{#case mongoose}}
24
+ const mongooseInstance = await mongoose();
25
+ const client = mongooseInstance.connection.getClient();
26
+ const db = client.db();
27
+ database: mongodbAdapter(db, { client }),
28
+ {{/case}}
29
+ {{/switch}}
22
30
  user: {
23
31
  additionalFields: {
24
32
  role: {
@@ -31,6 +39,15 @@ export const auth = betterAuth({
31
39
  emailAndPassword: {
32
40
  enabled: true,
33
41
  requireEmailVerification: true,
42
+ sendResetPassword: async ({ user, url }) => {
43
+ const { html, text } = getPasswordResetEmailTemplate(user, url);
44
+ await sendEmail({
45
+ to: user.email,
46
+ subject: "Reset Your Password",
47
+ text,
48
+ html,
49
+ });
50
+ },
34
51
  },
35
52
  socialProviders: {
36
53
  google: {
@@ -50,22 +67,9 @@ export const auth = betterAuth({
50
67
  },
51
68
  sendOnSignIn: true,
52
69
  },
53
- password: {
54
- reset: {
55
- sendResetEmail: async ({ user, url }) => {
56
- const { html, text } = getPasswordResetEmailTemplate(user, url);
57
- await sendEmail({
58
- to: user.email,
59
- subject: "Reset Your Password",
60
- text,
61
- html,
62
- });
63
- },
64
- },
65
- },
66
70
  rateLimit: {
67
- window: 10, // 10 seconds
68
- max: 100, // max requests per window
71
+ window: 10,
72
+ max: 100,
69
73
  },
70
74
  account: {
71
75
  accountLinking: {
@@ -77,7 +81,10 @@ export const auth = betterAuth({
77
81
  cookieCache: {
78
82
  enabled: true,
79
83
  },
80
- expiresIn: 60 * 60 * 24 * 7, // 7 days
81
- updateAge: 60 * 60 * 24, // 1 day
84
+ expiresIn: 60 * 60 * 24 * 7,
85
+ updateAge: 60 * 60 * 24,
82
86
  }
83
- });
87
+ })
88
+ };
89
+
90
+ export const auth = await initAuth();
@@ -1,4 +1,4 @@
1
- {{#var defaultId = {{#if prismaProvider == postgresql}}@default(cuid()){{/if}}{{#if prismaProvider == mysql}}@default(uuid()){{/if}}{{#if prismaProvider == sqlite}}@default(uuid()){{/if}}}}
1
+ {{#var defaultId = {{#if prismaProvider == mongodb}}@default(auto()) @map("_id") @db.ObjectId{{else}}@default(cuid()){{/if}}}}
2
2
  model User {
3
3
  id String @id {{defaultId}}
4
4
  name String
@@ -23,7 +23,7 @@ model Session {
23
23
  updatedAt DateTime @updatedAt
24
24
  ipAddress String?
25
25
  userAgent String?
26
- userId String
26
+ userId String {{#if prismaProvider == mongodb}} @db.ObjectId{{/if}}
27
27
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
28
28
 
29
29
  @@index([userId])
@@ -34,7 +34,7 @@ model Account {
34
34
  id String @id {{defaultId}}
35
35
  accountId String
36
36
  providerId String
37
- userId String
37
+ userId String {{#if prismaProvider == mongodb}} @db.ObjectId{{/if}}
38
38
  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
39
39
  accessToken String?
40
40
  refreshToken String?
@@ -33,12 +33,6 @@
33
33
  "destination": "lib/auth-client.ts",
34
34
  "condition": { "framework": ["nextjs", "react"] }
35
35
  },
36
- {
37
- "type": "create-file",
38
- "destination": "proxy.ts",
39
- "condition": { "framework": "nextjs" },
40
- "content": "import { auth } from \"@/lib/auth\";\n\nexport default auth((req) => {\n console.log('middleware', req.auth)\n})\n\nexport const config = {\n matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],\n}"
41
- },
42
36
  {
43
37
  "type": "patch-file",
44
38
  "destination": "prisma/schema.prisma",
@@ -50,6 +44,39 @@
50
44
  }
51
45
  ]
52
46
  },
47
+ {
48
+ "type": "patch-file",
49
+ "destination": "src/server.ts",
50
+ "condition": { "framework": "express" },
51
+ "operations": [
52
+ {
53
+ "type": "add-import",
54
+ "imports": ["import { initAuth } from \"@/lib/auth\";"]
55
+ },
56
+ {
57
+ "type": "add-code",
58
+ "before": "const port = env.port;",
59
+ "code": "await initAuth();"
60
+ }
61
+ ]
62
+ },
63
+ {
64
+ "type": "patch-file",
65
+ "destination": "src/app.ts",
66
+ "condition": { "framework": "express" },
67
+ "operations": [
68
+ {
69
+ "type": "add-import",
70
+ "imports": ["import { auth } from \"@/lib/auth\";",
71
+ "import { toNodeHandler } from \"better-auth/node\";"]
72
+ },
73
+ {
74
+ "type": "add-code",
75
+ "after": "// routes",
76
+ "code": "app.all(\"/api/auth/*splat\", toNodeHandler(auth));"
77
+ }
78
+ ]
79
+ },
53
80
  {
54
81
  "type": "add-dependency",
55
82
  "condition": { "framework": ["nextjs", "express"] },
@@ -59,20 +86,23 @@
59
86
  "devDependencies": {
60
87
  "@types/nodemailer": "^7.0.5"
61
88
  }
89
+ },
90
+ {
91
+ "type": "add-env",
92
+ "envVars": {
93
+ "BETTER_AUTH_SECRET": "",
94
+ "BETTER_AUTH_URL": "http://localhost:3000",
95
+ "EMAIL_HOST": "smtp.gmail.com",
96
+ "EMAIL_PORT": "587",
97
+ "EMAIL_USER": "",
98
+ "EMAIL_PASS": "",
99
+ "EMAIL_FROM": "noreply@yourapp.com"
100
+ }
62
101
  }
63
102
  ],
64
103
  "dependencies": {
65
104
  "better-auth": "^1.4.12"
66
105
  },
67
106
  "devDependencies": {},
68
- "scripts": {},
69
- "envVars": {
70
- "BETTER_AUTH_SECRET": "",
71
- "BETTER_AUTH_URL": "http://localhost:3000",
72
- "EMAIL_HOST": "smtp.gmail.com",
73
- "EMAIL_PORT": "587",
74
- "EMAIL_USER": "",
75
- "EMAIL_PASS": "",
76
- "EMAIL_FROM": "noreply@yourapp.com"
77
- }
107
+ "scripts": {}
78
108
  }
@@ -12,13 +12,32 @@
12
12
  "type": "create-file",
13
13
  "source": "models/health.ts",
14
14
  "destination": "models/health.ts"
15
+ },
16
+ {
17
+ "type": "patch-file",
18
+ "destination": "src/server.ts",
19
+ "condition": { "framework": "express" },
20
+ "operations": [
21
+ {
22
+ "type": "add-import",
23
+ "imports": ["import { mongoose } from \"@/lib/mongoose\";"]
24
+ },
25
+ {
26
+ "type": "add-code",
27
+ "after": "async function startServer() {",
28
+ "code": "await mongoose();"
29
+ }
30
+ ]
31
+ },
32
+ {
33
+ "type": "add-env",
34
+ "envVars": {
35
+ "MONGODB_URI": "mongodb://localhost:27017/database_name"
36
+ }
15
37
  }
16
38
  ],
17
39
  "dependencies": {
18
40
  "mongoose": "^8.8.0"
19
41
  },
20
- "devDependencies": {},
21
- "envVars": {
22
- "MONGODB_URI": "mongodb://localhost:27017/database_name"
23
- }
42
+ "devDependencies": {}
24
43
  }
@@ -1,27 +1,28 @@
1
- import 'dotenv/config'
1
+ import 'dotenv/config';
2
2
  import { PrismaClient } from './generated/prisma/client'
3
3
 
4
4
  const globalForPrisma = globalThis as unknown as {
5
5
  prisma: PrismaClient | undefined
6
6
  }
7
7
 
8
- {{#if prismaProvider == "postgresql"}}
9
- import { PrismaPg } from "@prisma/adapter-pg";
8
+ {{#switch prismaProvider}}
9
+ {{#case postgresql}}
10
+ import { PrismaPg } from '@prisma/adapter-pg'
10
11
 
11
- const adapter = new PrismaPg({
12
- connectionString: process.env.DATABASE_URL!,
13
- });
12
+ const connectionString = `${process.env.DATABASE_URL}`
14
13
 
15
- export const prisma = globalForPrisma.prisma ?? new PrismaClient({
16
- adapter,
17
- });
18
- {{/if}}
14
+ const adapter = new PrismaPg({ connectionString })
15
+ const prisma = new PrismaClient({ adapter })
16
+
17
+ export { prisma }
18
+ {{/case}}
19
+ {{#case mongodb}}
19
20
 
20
- {{#if prismaProvider == "mongodb"}}
21
- export const prisma = globalForPrisma.prisma ?? new PrismaClient();
22
- {{/if}}
21
+ const prisma = new PrismaClient()
23
22
 
24
- {{#if prismaProvider == "mysql"}}
23
+ export { prisma }
24
+ {{/case}}
25
+ {{#case mysql}}
25
26
  import { PrismaMariaDb } from '@prisma/adapter-mariadb';
26
27
 
27
28
  const adapter = new PrismaMariaDb({
@@ -31,16 +32,20 @@ const adapter = new PrismaMariaDb({
31
32
  database: process.env.DATABASE_NAME,
32
33
  connectionLimit: 5
33
34
  });
35
+ const prisma = new PrismaClient({ adapter });
34
36
 
35
- export const prisma = globalForPrisma.prisma ?? new PrismaClient({ adapter });
36
- {{/if}}
37
-
38
- {{#if prismaProvider == "sqlite"}}
37
+ export { prisma }
38
+ {{/case}}
39
+ {{#case sqlite}}
39
40
  import { PrismaBetterSqlite3 } from "@prisma/adapter-better-sqlite3";
40
41
 
41
- const adapter = new PrismaBetterSqlite3({ url: process.env.DATABASE_URL });
42
+ const connectionString = `${process.env.DATABASE_URL}`;
43
+
44
+ const adapter = new PrismaBetterSqlite3({ url: connectionString });
45
+ const prisma = new PrismaClient({ adapter });
42
46
 
43
- export const prisma = globalForPrisma.prisma ?? new PrismaClient({ adapter });
44
- {{/if}}
47
+ export { prisma };
48
+ {{/case}}
49
+ {{/switch}}
45
50
 
46
51
  if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
@@ -1,12 +1,29 @@
1
1
  import "dotenv/config";
2
+ {{#switch prismaProvider}}
3
+ {{#case mongodb}}
4
+ import { defineConfig, env } from "prisma/config";
5
+ {{/case}}
6
+ {{#case default}}
2
7
  import { defineConfig } from "prisma/config";
8
+ {{/case}}
9
+ {{/switch}}
3
10
 
4
11
  export default defineConfig({
5
12
  schema: "prisma/schema.prisma",
6
13
  migrations: {
7
14
  path: "prisma/migrations",
8
15
  },
16
+ {{#switch prismaProvider}}
17
+ {{#case mongodb}}
18
+ engine: "classic",
19
+ datasource: {
20
+ url: env('DATABASE_URL'),
21
+ },
22
+ {{/case}}
23
+ {{#case default}}
9
24
  datasource: {
10
25
  url: process.env["DATABASE_URL"],
11
26
  },
27
+ {{/case}}
28
+ {{/switch}}
12
29
  });
@@ -22,47 +22,86 @@
22
22
  "type": "add-dependency",
23
23
  "condition": { "prismaProvider": "postgresql" },
24
24
  "dependencies": {
25
- "@prisma/adapter-pg": "^5.0.0",
25
+ "@prisma/adapter-pg": "^7.0.0",
26
+ "@prisma/client": "^7.2.0",
26
27
  "pg": "^8.0.0"
28
+ },
29
+ "devDependencies": {
30
+ "prisma": "^7.2.0"
27
31
  }
28
32
  },
29
33
  {
30
34
  "type": "add-dependency",
31
35
  "condition": { "prismaProvider": "mysql" },
32
36
  "dependencies": {
33
- "@prisma/adapter-mariadb": "^5.0.0",
37
+ "@prisma/adapter-mariadb": "^7.0.0",
38
+ "@prisma/client": "^7.2.0",
34
39
  "mysql2": "^3.0.0"
40
+ },
41
+ "devDependencies": {
42
+ "prisma": "^7.2.0"
35
43
  }
36
44
  },
37
45
  {
38
46
  "type": "add-dependency",
39
47
  "condition": { "prismaProvider": "sqlite" },
40
48
  "dependencies": {
41
- "@prisma/adapter-better-sqlite3": "^5.0.0",
49
+ "@prisma/adapter-better-sqlite3": "^7.0.0",
50
+ "@prisma/client": "^7.2.0",
42
51
  "better-sqlite3": "^9.0.0"
52
+ },
53
+ "devDependencies": {
54
+ "prisma": "^7.2.0"
43
55
  }
44
56
  },
45
57
  {
46
58
  "type": "add-dependency",
47
59
  "condition": { "prismaProvider": "mongodb" },
48
- "dependencies": {}
60
+ "dependencies": {
61
+ "@prisma/client": "^6.19.0"
62
+ },
63
+ "devDependencies": {
64
+ "prisma": "^6.19.0"
65
+ }
66
+ },
67
+ {
68
+ "type": "add-env",
69
+ "condition": { "prismaProvider": "postgresql" },
70
+ "envVars": {
71
+ "DATABASE_URL": "postgresql://username:password@localhost:5432/database_name"
72
+ }
73
+ },
74
+ {
75
+ "type": "add-env",
76
+ "condition": { "prismaProvider": "mysql" },
77
+ "envVars": {
78
+ "DATABASE_URL": "mysql://username:password@localhost:3306/database_name"
79
+ }
80
+ },
81
+ {
82
+ "type": "add-env",
83
+ "condition": { "prismaProvider": "sqlite" },
84
+ "envVars": {
85
+ "DATABASE_URL": "file:./dev.db"
86
+ }
87
+ },
88
+ {
89
+ "type": "add-env",
90
+ "condition": { "prismaProvider": "mongodb" },
91
+ "envVars": {
92
+ "DATABASE_URL": "mongodb://localhost:27017/database_name"
93
+ }
49
94
  }
50
95
  ],
51
96
  "dependencies": {
52
- "@prisma/client": "^7.2.0",
53
97
  "dotenv": "^17.2.3"
54
98
  },
55
- "devDependencies": {
56
- "prisma": "^7.2.0"
57
- },
99
+ "devDependencies": {},
58
100
  "scripts": {
59
101
  "db:generate": "npx prisma generate",
60
102
  "db:push": "npx prisma db push",
61
103
  "db:seed": "tsx prisma/seed.ts",
62
104
  "db:migrate": "npx prisma migrate dev",
63
105
  "db:studio": "npx prisma studio"
64
- },
65
- "envVars": {
66
- "DATABASE_URL": "postgresql://username:password@localhost:5432/database_name"
67
106
  }
68
107
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stackkit",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Production-ready CLI to create and extend JavaScript or TypeScript apps with modular stacks.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -28,6 +28,8 @@
28
28
  "copy-assets": "cp -r ../../templates . && cp -r ../../modules .",
29
29
  "clean": "rm -rf dist templates modules",
30
30
  "typecheck": "tsc --noEmit",
31
+ "lint": "eslint src --ext .ts",
32
+ "lint:fix": "eslint src --ext .ts --fix",
31
33
  "prepublishOnly": "npm run build"
32
34
  },
33
35
  "keywords": [
@@ -1,8 +1,14 @@
1
1
  import app from "./app";
2
2
  import { env } from "./config/env";
3
3
 
4
- const port = env.port;
4
+ async function startServer() {
5
+ const port = env.port;
6
+ app.listen(port, () => {
7
+ console.log(`Server is running on http://localhost:${port}`);
8
+ });
9
+ }
5
10
 
6
- app.listen(port, () => {
7
- console.log(`Server is running on http://localhost:${port}`);
11
+ startServer().catch((err) => {
12
+ console.error("Failed to start server:", err);
13
+ process.exit(1);
8
14
  });
@@ -4,6 +4,6 @@
4
4
  "moduleResolution": "node",
5
5
  "target": "ES2023",
6
6
  "strict": true,
7
- "esModuleInterop": true,
7
+ "esModuleInterop": true
8
8
  }
9
- }
9
+ }
@@ -5,4 +5,4 @@ export const env = {
5
5
 
6
6
  isDev: process.env.NODE_ENV === "development",
7
7
  isProd: process.env.NODE_ENV === "production",
8
- } as const;
8
+ } as const;