miragedev-sdk 0.1.0 → 0.2.0
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/README.md +30 -44
- package/dist/chunk-OIWPIRJY.cjs +591 -0
- package/dist/chunk-PDNLOOI2.js +581 -0
- package/dist/cli/commands/create.cjs +11 -0
- package/dist/cli/commands/create.d.cts +3 -0
- package/dist/cli/commands/create.d.ts +3 -0
- package/dist/cli/commands/create.js +2 -0
- package/dist/cli/index.cjs +3 -1
- package/dist/cli/index.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,75 +16,61 @@ AI-first SDK for building SAAS applications with Next.js. Build production-ready
|
|
|
16
16
|
- 📦 **Modular** - Import only what you need
|
|
17
17
|
- 🔒 **Type-Safe** - Full TypeScript support with Zod validation
|
|
18
18
|
|
|
19
|
-
## Quick Start (
|
|
19
|
+
## Quick Start (2 minutes)
|
|
20
20
|
|
|
21
|
-
### 1
|
|
21
|
+
### Option 1: Create New Project (Recommended)
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
npx miragedev create my-saas
|
|
25
|
+
cd my-saas
|
|
26
|
+
npm run dev
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
That's it! You now have a full-featured SAAS with:
|
|
30
|
+
- ✅ Next.js 14+ with App Router
|
|
31
|
+
- ✅ Authentication (login page included)
|
|
32
|
+
- ✅ Protected dashboard
|
|
33
|
+
- ✅ Stripe billing setup
|
|
34
|
+
- ✅ Email templates
|
|
35
|
+
- ✅ Pricing page
|
|
36
|
+
- ✅ PWA ready
|
|
37
|
+
|
|
38
|
+
### Option 2: Add to Existing Project
|
|
30
39
|
|
|
31
40
|
```bash
|
|
41
|
+
# In your existing Next.js project
|
|
42
|
+
npm install miragedev-sdk
|
|
32
43
|
npx miragedev init
|
|
33
44
|
```
|
|
34
45
|
|
|
35
|
-
The CLI will guide you through selecting providers and generating configuration files.
|
|
36
|
-
|
|
37
46
|
**What gets created:**
|
|
38
|
-
- `miragedev.config.ts` - SDK configuration
|
|
47
|
+
- `lib/miragedev.config.ts` - SDK configuration
|
|
39
48
|
- `.env.local.example` - Environment variables template
|
|
40
|
-
- `miragedev.init.ts` - Initialization file
|
|
49
|
+
- `lib/miragedev.init.ts` - Initialization file
|
|
41
50
|
- `AGENTS.md` - Instructions for AI assistants (Copilot, Cursor, etc.)
|
|
42
51
|
|
|
43
|
-
###
|
|
52
|
+
### Configure Environment
|
|
44
53
|
|
|
45
|
-
|
|
54
|
+
Copy `.env.local.example` to `.env.local` and fill in your API keys:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
cp .env.local.example .env.local
|
|
58
|
+
```
|
|
46
59
|
|
|
47
60
|
```env
|
|
48
61
|
# Auth
|
|
49
|
-
AUTH_SECRET=your-secret-key-here
|
|
62
|
+
AUTH_SECRET=your-secret-key-here # Generate with: openssl rand -base64 32
|
|
50
63
|
|
|
51
64
|
# Billing (Stripe)
|
|
52
|
-
STRIPE_SECRET_KEY=sk_test_xxx
|
|
53
|
-
STRIPE_WEBHOOK_SECRET=whsec_xxx
|
|
65
|
+
STRIPE_SECRET_KEY=sk_test_xxx # From https://dashboard.stripe.com/test/apikeys
|
|
66
|
+
STRIPE_WEBHOOK_SECRET=whsec_xxx # From https://dashboard.stripe.com/test/webhooks
|
|
54
67
|
|
|
55
68
|
# Email (Resend)
|
|
56
|
-
RESEND_API_KEY=re_xxx
|
|
69
|
+
RESEND_API_KEY=re_xxx # From https://resend.com/api-keys
|
|
57
70
|
EMAIL_FROM=noreply@yourapp.com
|
|
58
71
|
```
|
|
59
72
|
|
|
60
|
-
###
|
|
61
|
-
|
|
62
|
-
Create `lib/miragedev.ts`:
|
|
63
|
-
|
|
64
|
-
```typescript
|
|
65
|
-
import { initMirageDev } from 'miragedev-sdk'
|
|
66
|
-
|
|
67
|
-
initMirageDev({
|
|
68
|
-
auth: {
|
|
69
|
-
provider: 'nextauth',
|
|
70
|
-
sessionSecret: process.env.AUTH_SECRET!,
|
|
71
|
-
},
|
|
72
|
-
billing: {
|
|
73
|
-
provider: 'stripe',
|
|
74
|
-
stripeSecretKey: process.env.STRIPE_SECRET_KEY!,
|
|
75
|
-
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET!,
|
|
76
|
-
},
|
|
77
|
-
email: {
|
|
78
|
-
provider: 'resend',
|
|
79
|
-
apiKey: process.env.RESEND_API_KEY!,
|
|
80
|
-
from: process.env.EMAIL_FROM!,
|
|
81
|
-
},
|
|
82
|
-
})
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
Import this in your root layout (`app/layout.tsx`).
|
|
86
|
-
|
|
87
|
-
### 5. Start Building
|
|
73
|
+
### Start Building
|
|
88
74
|
|
|
89
75
|
```typescript
|
|
90
76
|
// Server Component - Protected Page
|
|
@@ -0,0 +1,591 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var inquirer = require('inquirer');
|
|
4
|
+
var chalk = require('chalk');
|
|
5
|
+
var ora = require('ora');
|
|
6
|
+
var child_process = require('child_process');
|
|
7
|
+
var fs = require('fs/promises');
|
|
8
|
+
var path = require('path');
|
|
9
|
+
|
|
10
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
|
|
12
|
+
var inquirer__default = /*#__PURE__*/_interopDefault(inquirer);
|
|
13
|
+
var chalk__default = /*#__PURE__*/_interopDefault(chalk);
|
|
14
|
+
var ora__default = /*#__PURE__*/_interopDefault(ora);
|
|
15
|
+
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
16
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
17
|
+
|
|
18
|
+
// cli/commands/create.ts
|
|
19
|
+
async function createCommand(projectName) {
|
|
20
|
+
console.log(chalk__default.default.blue.bold("\n\u{1F680} Create New SAAS with MirageDev\n"));
|
|
21
|
+
const answers = await inquirer__default.default.prompt([
|
|
22
|
+
{
|
|
23
|
+
type: "input",
|
|
24
|
+
name: "projectName",
|
|
25
|
+
message: "What is your project name?",
|
|
26
|
+
default: projectName || "my-saas",
|
|
27
|
+
when: !projectName,
|
|
28
|
+
validate: (input) => {
|
|
29
|
+
if (!input.trim()) return "Project name is required";
|
|
30
|
+
if (!/^[a-z0-9-]+$/.test(input)) {
|
|
31
|
+
return "Project name can only contain lowercase letters, numbers, and hyphens";
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
type: "list",
|
|
38
|
+
name: "authProvider",
|
|
39
|
+
message: "Choose your auth provider:",
|
|
40
|
+
choices: [
|
|
41
|
+
{ name: "NextAuth.js (Recommended)", value: "nextauth" },
|
|
42
|
+
{ name: "Clerk", value: "clerk" },
|
|
43
|
+
{ name: "Supabase Auth", value: "supabase" }
|
|
44
|
+
],
|
|
45
|
+
default: "nextauth"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
type: "list",
|
|
49
|
+
name: "billingProvider",
|
|
50
|
+
message: "Choose your billing provider:",
|
|
51
|
+
choices: [
|
|
52
|
+
{ name: "Stripe (Recommended)", value: "stripe" },
|
|
53
|
+
{ name: "Paddle (Coming soon)", value: "paddle", disabled: true },
|
|
54
|
+
{ name: "LemonSqueezy (Coming soon)", value: "lemonsqueezy", disabled: true }
|
|
55
|
+
],
|
|
56
|
+
default: "stripe"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
type: "list",
|
|
60
|
+
name: "emailProvider",
|
|
61
|
+
message: "Choose your email provider:",
|
|
62
|
+
choices: [
|
|
63
|
+
{ name: "Resend (Recommended)", value: "resend" },
|
|
64
|
+
{ name: "SendGrid", value: "sendgrid" },
|
|
65
|
+
{ name: "Postmark (Coming soon)", value: "postmark", disabled: true }
|
|
66
|
+
],
|
|
67
|
+
default: "resend"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
type: "checkbox",
|
|
71
|
+
name: "features",
|
|
72
|
+
message: "Select additional features:",
|
|
73
|
+
choices: [
|
|
74
|
+
{ name: "PWA Support (Progressive Web App)", value: "pwa", checked: true },
|
|
75
|
+
{ name: "Biometric Authentication", value: "biometric", checked: false },
|
|
76
|
+
{ name: "Mobile Optimizations", value: "mobile", checked: true }
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
type: "confirm",
|
|
81
|
+
name: "includeExamples",
|
|
82
|
+
message: "Include example pages? (Login, Dashboard, Pricing)",
|
|
83
|
+
default: true
|
|
84
|
+
}
|
|
85
|
+
]);
|
|
86
|
+
const finalProjectName = projectName || answers.projectName;
|
|
87
|
+
const projectPath = path__default.default.resolve(process.cwd(), finalProjectName);
|
|
88
|
+
console.log(chalk__default.default.cyan(`
|
|
89
|
+
\u{1F4E6} Creating project at: ${projectPath}
|
|
90
|
+
`));
|
|
91
|
+
try {
|
|
92
|
+
await createNextjsProject(finalProjectName);
|
|
93
|
+
await installSDK(finalProjectName);
|
|
94
|
+
await generateProjectFiles(finalProjectName, answers);
|
|
95
|
+
if (answers.includeExamples) {
|
|
96
|
+
await createExamplePages(finalProjectName, answers);
|
|
97
|
+
}
|
|
98
|
+
console.log(chalk__default.default.green.bold("\n\u2705 Project created successfully!\n"));
|
|
99
|
+
console.log(chalk__default.default.cyan("\u{1F4DD} Next steps:\n"));
|
|
100
|
+
console.log(chalk__default.default.white(` 1. cd ${finalProjectName}`));
|
|
101
|
+
console.log(chalk__default.default.white(" 2. Copy .env.local.example to .env.local"));
|
|
102
|
+
console.log(chalk__default.default.white(" 3. Fill in your API keys in .env.local"));
|
|
103
|
+
console.log(chalk__default.default.white(" 4. npm run dev\n"));
|
|
104
|
+
console.log(chalk__default.default.gray("Documentation: https://github.com/yourusername/miragedev-sdk\n"));
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.error(chalk__default.default.red("\n\u274C Error creating project:"), error);
|
|
107
|
+
console.log(chalk__default.default.yellow("\nTrying to clean up..."));
|
|
108
|
+
try {
|
|
109
|
+
await fs__default.default.rm(projectPath, { recursive: true, force: true });
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function createNextjsProject(projectName) {
|
|
116
|
+
const spinner = ora__default.default("Creating Next.js project...").start();
|
|
117
|
+
try {
|
|
118
|
+
child_process.execSync(
|
|
119
|
+
`npx create-next-app@latest ${projectName} --typescript --tailwind --app --no-git --eslint --src-dir --import-alias "@/*"`,
|
|
120
|
+
{
|
|
121
|
+
stdio: "pipe",
|
|
122
|
+
cwd: process.cwd()
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
spinner.succeed(chalk__default.default.green("Next.js project created"));
|
|
126
|
+
} catch (error) {
|
|
127
|
+
spinner.fail(chalk__default.default.red("Failed to create Next.js project"));
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
async function installSDK(projectName) {
|
|
132
|
+
const spinner = ora__default.default("Installing MirageDev SDK...").start();
|
|
133
|
+
const projectPath = path__default.default.resolve(process.cwd(), projectName);
|
|
134
|
+
try {
|
|
135
|
+
child_process.execSync("npm install miragedev-sdk", {
|
|
136
|
+
stdio: "pipe",
|
|
137
|
+
cwd: projectPath
|
|
138
|
+
});
|
|
139
|
+
spinner.succeed(chalk__default.default.green("MirageDev SDK installed"));
|
|
140
|
+
} catch (error) {
|
|
141
|
+
spinner.fail(chalk__default.default.red("Failed to install SDK"));
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
async function generateProjectFiles(projectName, answers) {
|
|
146
|
+
const spinner = ora__default.default("Generating configuration files...").start();
|
|
147
|
+
const projectPath = path__default.default.resolve(process.cwd(), projectName);
|
|
148
|
+
try {
|
|
149
|
+
await generateConfigFile(projectPath, answers);
|
|
150
|
+
await generateEnvTemplate(projectPath, answers);
|
|
151
|
+
await generateInitFile(projectPath, answers);
|
|
152
|
+
await generateAgentRules(projectPath, answers);
|
|
153
|
+
await updateRootLayout(projectPath);
|
|
154
|
+
spinner.succeed(chalk__default.default.green("Configuration files generated"));
|
|
155
|
+
} catch (error) {
|
|
156
|
+
spinner.fail(chalk__default.default.red("Failed to generate configuration"));
|
|
157
|
+
throw error;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async function generateConfigFile(projectPath, answers) {
|
|
161
|
+
const config = `// MirageDev SDK Configuration
|
|
162
|
+
import { initMirageDev } from 'miragedev-sdk'
|
|
163
|
+
|
|
164
|
+
export function initializeMirageDev() {
|
|
165
|
+
initMirageDev({
|
|
166
|
+
auth: {
|
|
167
|
+
provider: '${answers.authProvider}',
|
|
168
|
+
sessionSecret: process.env.AUTH_SECRET!,
|
|
169
|
+
},
|
|
170
|
+
billing: {
|
|
171
|
+
provider: '${answers.billingProvider}',
|
|
172
|
+
${answers.billingProvider === "stripe" ? "stripeSecretKey: process.env.STRIPE_SECRET_KEY!," : ""}
|
|
173
|
+
webhookSecret: process.env.${answers.billingProvider.toUpperCase()}_WEBHOOK_SECRET!,
|
|
174
|
+
},
|
|
175
|
+
email: {
|
|
176
|
+
provider: '${answers.emailProvider}',
|
|
177
|
+
apiKey: process.env.${answers.emailProvider.toUpperCase()}_API_KEY!,
|
|
178
|
+
from: process.env.EMAIL_FROM!,
|
|
179
|
+
},
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
`;
|
|
183
|
+
await fs__default.default.writeFile(path__default.default.join(projectPath, "lib", "miragedev.config.ts"), config, "utf-8");
|
|
184
|
+
}
|
|
185
|
+
async function generateEnvTemplate(projectPath, answers) {
|
|
186
|
+
const env = `# MirageDev SDK Environment Variables
|
|
187
|
+
# Copy this file to .env.local and fill in your actual values
|
|
188
|
+
|
|
189
|
+
# Auth
|
|
190
|
+
AUTH_SECRET=your-secret-key-here-generate-with-openssl-rand-base64-32
|
|
191
|
+
|
|
192
|
+
# Billing (${answers.billingProvider})
|
|
193
|
+
${answers.billingProvider === "stripe" ? "STRIPE_SECRET_KEY=sk_test_xxx" : ""}
|
|
194
|
+
${answers.billingProvider.toUpperCase()}_WEBHOOK_SECRET=whsec_xxx
|
|
195
|
+
|
|
196
|
+
# Email (${answers.emailProvider})
|
|
197
|
+
${answers.emailProvider.toUpperCase()}_API_KEY=your-api-key-here
|
|
198
|
+
EMAIL_FROM=noreply@yourapp.com
|
|
199
|
+
|
|
200
|
+
# Next.js
|
|
201
|
+
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
|
202
|
+
`;
|
|
203
|
+
await fs__default.default.writeFile(path__default.default.join(projectPath, ".env.local.example"), env, "utf-8");
|
|
204
|
+
}
|
|
205
|
+
async function generateInitFile(projectPath, answers) {
|
|
206
|
+
const hasFeature = (feature) => answers.features.includes(feature);
|
|
207
|
+
const imports = [];
|
|
208
|
+
const setup = [];
|
|
209
|
+
if (hasFeature("pwa")) {
|
|
210
|
+
imports.push("import { configurePWA } from 'miragedev-sdk/pwa'");
|
|
211
|
+
setup.push(`
|
|
212
|
+
// PWA Configuration
|
|
213
|
+
export const pwaConfig = configurePWA({
|
|
214
|
+
name: 'Your SAAS App',
|
|
215
|
+
shortName: 'SAAS',
|
|
216
|
+
theme: '#000000',
|
|
217
|
+
backgroundColor: '#ffffff',
|
|
218
|
+
offline: { enabled: true, pages: ['/'] }
|
|
219
|
+
})`);
|
|
220
|
+
}
|
|
221
|
+
const content = `// MirageDev SDK Initialization
|
|
222
|
+
import { initializeMirageDev } from './miragedev.config'
|
|
223
|
+
${imports.join("\n")}
|
|
224
|
+
|
|
225
|
+
// Initialize SDK
|
|
226
|
+
initializeMirageDev()
|
|
227
|
+
|
|
228
|
+
${setup.join("\n\n")}
|
|
229
|
+
`;
|
|
230
|
+
await fs__default.default.writeFile(path__default.default.join(projectPath, "lib", "miragedev.init.ts"), content, "utf-8");
|
|
231
|
+
}
|
|
232
|
+
async function generateAgentRules(projectPath, answers) {
|
|
233
|
+
const modules = ["auth", "billing", "email"];
|
|
234
|
+
if (answers.features.includes("pwa")) modules.push("pwa");
|
|
235
|
+
if (answers.features.includes("mobile")) modules.push("mobile");
|
|
236
|
+
if (answers.features.includes("biometric")) modules.push("auth/biometric");
|
|
237
|
+
const content = `# AI Agent Instructions
|
|
238
|
+
|
|
239
|
+
> **For AI Assistants (GitHub Copilot, OpenCode, Cursor, etc.):**
|
|
240
|
+
> This project uses **MirageDev SDK**. Always prefer SDK functions over manual implementations.
|
|
241
|
+
|
|
242
|
+
## Installed SDK Modules
|
|
243
|
+
|
|
244
|
+
${modules.map((mod) => `- \`miragedev-sdk/${mod}\` - ${getModuleDescription(mod)}`).join("\n")}
|
|
245
|
+
|
|
246
|
+
## Configuration
|
|
247
|
+
|
|
248
|
+
- **Auth Provider:** ${answers.authProvider}
|
|
249
|
+
- **Billing Provider:** ${answers.billingProvider}
|
|
250
|
+
- **Email Provider:** ${answers.emailProvider}
|
|
251
|
+
${answers.features.includes("pwa") ? "- **PWA:** Enabled" : ""}
|
|
252
|
+
${answers.features.includes("biometric") ? "- **Biometric Auth:** Enabled" : ""}
|
|
253
|
+
${answers.features.includes("mobile") ? "- **Mobile Optimizations:** Enabled" : ""}
|
|
254
|
+
|
|
255
|
+
## Code Generation Rules
|
|
256
|
+
|
|
257
|
+
### \u2705 DO: Use MirageDev SDK
|
|
258
|
+
|
|
259
|
+
When developer needs authentication:
|
|
260
|
+
\`\`\`typescript
|
|
261
|
+
// \u2705 CORRECT
|
|
262
|
+
import { requireAuth } from 'miragedev-sdk/auth'
|
|
263
|
+
|
|
264
|
+
export default async function Page() {
|
|
265
|
+
const session = await requireAuth()
|
|
266
|
+
return <div>Hello {session.user.name}</div>
|
|
267
|
+
}
|
|
268
|
+
\`\`\`
|
|
269
|
+
|
|
270
|
+
### \u274C DON'T: Manual Implementation
|
|
271
|
+
|
|
272
|
+
\`\`\`typescript
|
|
273
|
+
// \u274C WRONG - Don't do this when SDK exists
|
|
274
|
+
import Stripe from 'stripe'
|
|
275
|
+
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
|
|
276
|
+
\`\`\`
|
|
277
|
+
|
|
278
|
+
For full documentation, see: https://github.com/yourusername/miragedev-sdk
|
|
279
|
+
`;
|
|
280
|
+
await fs__default.default.writeFile(path__default.default.join(projectPath, "AGENTS.md"), content, "utf-8");
|
|
281
|
+
}
|
|
282
|
+
async function updateRootLayout(projectPath) {
|
|
283
|
+
const layoutPath = path__default.default.join(projectPath, "src", "app", "layout.tsx");
|
|
284
|
+
let layout = await fs__default.default.readFile(layoutPath, "utf-8");
|
|
285
|
+
const importStatement = "import '@/lib/miragedev.init'\n";
|
|
286
|
+
if (!layout.includes("miragedev.init")) {
|
|
287
|
+
const lines = layout.split("\n");
|
|
288
|
+
const firstImportIndex = lines.findIndex((line) => line.startsWith("import"));
|
|
289
|
+
if (firstImportIndex !== -1) {
|
|
290
|
+
lines.splice(firstImportIndex, 0, importStatement);
|
|
291
|
+
layout = lines.join("\n");
|
|
292
|
+
await fs__default.default.writeFile(layoutPath, layout, "utf-8");
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async function createExamplePages(projectPath, answers) {
|
|
297
|
+
const spinner = ora__default.default("Creating example pages...").start();
|
|
298
|
+
try {
|
|
299
|
+
await createLoginPage(projectPath, answers);
|
|
300
|
+
await createDashboardPage(projectPath);
|
|
301
|
+
await createPricingPage(projectPath);
|
|
302
|
+
await createMiddleware(projectPath);
|
|
303
|
+
await createWebhookRoute(projectPath, answers);
|
|
304
|
+
spinner.succeed(chalk__default.default.green("Example pages created"));
|
|
305
|
+
} catch (error) {
|
|
306
|
+
spinner.fail(chalk__default.default.red("Failed to create example pages"));
|
|
307
|
+
throw error;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
async function createLoginPage(projectPath, _answers) {
|
|
311
|
+
const authDir = path__default.default.join(projectPath, "src", "app", "(auth)", "login");
|
|
312
|
+
await fs__default.default.mkdir(authDir, { recursive: true });
|
|
313
|
+
const content = `'use client'
|
|
314
|
+
|
|
315
|
+
import { signIn } from 'miragedev-sdk/auth/client'
|
|
316
|
+
import { useState } from 'react'
|
|
317
|
+
|
|
318
|
+
export default function LoginPage() {
|
|
319
|
+
const [isLoading, setIsLoading] = useState(false)
|
|
320
|
+
|
|
321
|
+
const handleSignIn = async (provider: string) => {
|
|
322
|
+
setIsLoading(true)
|
|
323
|
+
try {
|
|
324
|
+
await signIn(provider)
|
|
325
|
+
} catch (error) {
|
|
326
|
+
console.error('Sign in error:', error)
|
|
327
|
+
setIsLoading(false)
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return (
|
|
332
|
+
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100">
|
|
333
|
+
<div className="w-full max-w-md space-y-8 rounded-2xl bg-white p-8 shadow-xl">
|
|
334
|
+
<div className="text-center">
|
|
335
|
+
<h1 className="text-3xl font-bold text-gray-900">Welcome Back</h1>
|
|
336
|
+
<p className="mt-2 text-gray-600">Sign in to your account</p>
|
|
337
|
+
</div>
|
|
338
|
+
|
|
339
|
+
<div className="space-y-4">
|
|
340
|
+
<button
|
|
341
|
+
onClick={() => handleSignIn('google')}
|
|
342
|
+
disabled={isLoading}
|
|
343
|
+
className="w-full rounded-lg bg-blue-600 px-4 py-3 font-medium text-white hover:bg-blue-700 disabled:opacity-50"
|
|
344
|
+
>
|
|
345
|
+
{isLoading ? 'Signing in...' : 'Sign in with Google'}
|
|
346
|
+
</button>
|
|
347
|
+
|
|
348
|
+
<button
|
|
349
|
+
onClick={() => handleSignIn('github')}
|
|
350
|
+
disabled={isLoading}
|
|
351
|
+
className="w-full rounded-lg bg-gray-900 px-4 py-3 font-medium text-white hover:bg-gray-800 disabled:opacity-50"
|
|
352
|
+
>
|
|
353
|
+
{isLoading ? 'Signing in...' : 'Sign in with GitHub'}
|
|
354
|
+
</button>
|
|
355
|
+
</div>
|
|
356
|
+
|
|
357
|
+
<p className="text-center text-sm text-gray-500">
|
|
358
|
+
By signing in, you agree to our Terms of Service
|
|
359
|
+
</p>
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
)
|
|
363
|
+
}
|
|
364
|
+
`;
|
|
365
|
+
await fs__default.default.writeFile(path__default.default.join(authDir, "page.tsx"), content, "utf-8");
|
|
366
|
+
}
|
|
367
|
+
async function createDashboardPage(projectPath) {
|
|
368
|
+
const dashboardDir = path__default.default.join(projectPath, "src", "app", "(protected)", "dashboard");
|
|
369
|
+
await fs__default.default.mkdir(dashboardDir, { recursive: true });
|
|
370
|
+
const content = `import { requireAuth } from 'miragedev-sdk/auth'
|
|
371
|
+
import { UpgradeButton } from '@/components/UpgradeButton'
|
|
372
|
+
|
|
373
|
+
export default async function DashboardPage() {
|
|
374
|
+
const session = await requireAuth()
|
|
375
|
+
|
|
376
|
+
return (
|
|
377
|
+
<div className="min-h-screen bg-gray-50 p-8">
|
|
378
|
+
<div className="mx-auto max-w-4xl">
|
|
379
|
+
<h1 className="text-3xl font-bold text-gray-900">
|
|
380
|
+
Welcome back, {session.user.name || 'User'}!
|
|
381
|
+
</h1>
|
|
382
|
+
|
|
383
|
+
<div className="mt-6 grid gap-6 md:grid-cols-2">
|
|
384
|
+
<div className="rounded-lg bg-white p-6 shadow">
|
|
385
|
+
<h2 className="text-xl font-semibold">Your Account</h2>
|
|
386
|
+
<p className="mt-2 text-gray-600">Email: {session.user.email}</p>
|
|
387
|
+
</div>
|
|
388
|
+
|
|
389
|
+
<div className="rounded-lg bg-white p-6 shadow">
|
|
390
|
+
<h2 className="text-xl font-semibold">Subscription</h2>
|
|
391
|
+
<div className="mt-4">
|
|
392
|
+
<UpgradeButton />
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
</div>
|
|
396
|
+
</div>
|
|
397
|
+
</div>
|
|
398
|
+
)
|
|
399
|
+
}
|
|
400
|
+
`;
|
|
401
|
+
await fs__default.default.writeFile(path__default.default.join(dashboardDir, "page.tsx"), content, "utf-8");
|
|
402
|
+
const componentsDir = path__default.default.join(projectPath, "src", "components");
|
|
403
|
+
await fs__default.default.mkdir(componentsDir, { recursive: true });
|
|
404
|
+
const buttonContent = `'use client'
|
|
405
|
+
|
|
406
|
+
import { useSubscription } from 'miragedev-sdk/billing/client'
|
|
407
|
+
|
|
408
|
+
export function UpgradeButton() {
|
|
409
|
+
const { subscription, isLoading } = useSubscription()
|
|
410
|
+
|
|
411
|
+
if (isLoading) {
|
|
412
|
+
return <div className="text-gray-500">Loading subscription...</div>
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (subscription?.status === 'active') {
|
|
416
|
+
return (
|
|
417
|
+
<div className="rounded-lg bg-green-50 p-4 text-green-800">
|
|
418
|
+
\u2705 You're a Pro subscriber!
|
|
419
|
+
</div>
|
|
420
|
+
)
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return (
|
|
424
|
+
<button className="w-full rounded-lg bg-purple-600 px-6 py-3 font-medium text-white hover:bg-purple-700">
|
|
425
|
+
Upgrade to Pro
|
|
426
|
+
</button>
|
|
427
|
+
)
|
|
428
|
+
}
|
|
429
|
+
`;
|
|
430
|
+
await fs__default.default.writeFile(path__default.default.join(componentsDir, "UpgradeButton.tsx"), buttonContent, "utf-8");
|
|
431
|
+
}
|
|
432
|
+
async function createPricingPage(projectPath) {
|
|
433
|
+
const pricingDir = path__default.default.join(projectPath, "src", "app", "pricing");
|
|
434
|
+
await fs__default.default.mkdir(pricingDir, { recursive: true });
|
|
435
|
+
const content = `export default function PricingPage() {
|
|
436
|
+
return (
|
|
437
|
+
<div className="min-h-screen bg-gradient-to-br from-purple-50 to-blue-50 py-16">
|
|
438
|
+
<div className="mx-auto max-w-6xl px-4">
|
|
439
|
+
<h1 className="text-center text-4xl font-bold text-gray-900">
|
|
440
|
+
Simple, Transparent Pricing
|
|
441
|
+
</h1>
|
|
442
|
+
<p className="mt-4 text-center text-xl text-gray-600">
|
|
443
|
+
Choose the plan that's right for you
|
|
444
|
+
</p>
|
|
445
|
+
|
|
446
|
+
<div className="mt-12 grid gap-8 md:grid-cols-3">
|
|
447
|
+
{/* Free Plan */}
|
|
448
|
+
<div className="rounded-2xl bg-white p-8 shadow-lg">
|
|
449
|
+
<h3 className="text-2xl font-bold">Free</h3>
|
|
450
|
+
<p className="mt-2 text-gray-600">Perfect for getting started</p>
|
|
451
|
+
<div className="mt-4 text-4xl font-bold">$0</div>
|
|
452
|
+
<p className="text-gray-500">per month</p>
|
|
453
|
+
|
|
454
|
+
<ul className="mt-6 space-y-3">
|
|
455
|
+
<li className="flex items-center">
|
|
456
|
+
<span className="mr-2">\u2713</span> Basic features
|
|
457
|
+
</li>
|
|
458
|
+
<li className="flex items-center">
|
|
459
|
+
<span className="mr-2">\u2713</span> 1 user
|
|
460
|
+
</li>
|
|
461
|
+
</ul>
|
|
462
|
+
|
|
463
|
+
<button className="mt-8 w-full rounded-lg border-2 border-gray-300 px-6 py-3 font-medium hover:bg-gray-50">
|
|
464
|
+
Get Started
|
|
465
|
+
</button>
|
|
466
|
+
</div>
|
|
467
|
+
|
|
468
|
+
{/* Pro Plan */}
|
|
469
|
+
<div className="rounded-2xl bg-gradient-to-br from-purple-600 to-blue-600 p-8 text-white shadow-2xl ring-4 ring-purple-300">
|
|
470
|
+
<div className="text-sm font-semibold uppercase tracking-wide">
|
|
471
|
+
Most Popular
|
|
472
|
+
</div>
|
|
473
|
+
<h3 className="mt-2 text-2xl font-bold">Pro</h3>
|
|
474
|
+
<p className="mt-2 opacity-90">For growing businesses</p>
|
|
475
|
+
<div className="mt-4 text-4xl font-bold">$29</div>
|
|
476
|
+
<p className="opacity-80">per month</p>
|
|
477
|
+
|
|
478
|
+
<ul className="mt-6 space-y-3">
|
|
479
|
+
<li className="flex items-center">
|
|
480
|
+
<span className="mr-2">\u2713</span> All Free features
|
|
481
|
+
</li>
|
|
482
|
+
<li className="flex items-center">
|
|
483
|
+
<span className="mr-2">\u2713</span> Unlimited users
|
|
484
|
+
</li>
|
|
485
|
+
<li className="flex items-center">
|
|
486
|
+
<span className="mr-2">\u2713</span> Priority support
|
|
487
|
+
</li>
|
|
488
|
+
</ul>
|
|
489
|
+
|
|
490
|
+
<button className="mt-8 w-full rounded-lg bg-white px-6 py-3 font-medium text-purple-600 hover:bg-gray-50">
|
|
491
|
+
Subscribe Now
|
|
492
|
+
</button>
|
|
493
|
+
</div>
|
|
494
|
+
|
|
495
|
+
{/* Enterprise Plan */}
|
|
496
|
+
<div className="rounded-2xl bg-white p-8 shadow-lg">
|
|
497
|
+
<h3 className="text-2xl font-bold">Enterprise</h3>
|
|
498
|
+
<p className="mt-2 text-gray-600">For large organizations</p>
|
|
499
|
+
<div className="mt-4 text-4xl font-bold">Custom</div>
|
|
500
|
+
<p className="text-gray-500">contact us</p>
|
|
501
|
+
|
|
502
|
+
<ul className="mt-6 space-y-3">
|
|
503
|
+
<li className="flex items-center">
|
|
504
|
+
<span className="mr-2">\u2713</span> All Pro features
|
|
505
|
+
</li>
|
|
506
|
+
<li className="flex items-center">
|
|
507
|
+
<span className="mr-2">\u2713</span> Custom integrations
|
|
508
|
+
</li>
|
|
509
|
+
<li className="flex items-center">
|
|
510
|
+
<span className="mr-2">\u2713</span> Dedicated support
|
|
511
|
+
</li>
|
|
512
|
+
</ul>
|
|
513
|
+
|
|
514
|
+
<button className="mt-8 w-full rounded-lg border-2 border-gray-300 px-6 py-3 font-medium hover:bg-gray-50">
|
|
515
|
+
Contact Sales
|
|
516
|
+
</button>
|
|
517
|
+
</div>
|
|
518
|
+
</div>
|
|
519
|
+
</div>
|
|
520
|
+
</div>
|
|
521
|
+
)
|
|
522
|
+
}
|
|
523
|
+
`;
|
|
524
|
+
await fs__default.default.writeFile(path__default.default.join(pricingDir, "page.tsx"), content, "utf-8");
|
|
525
|
+
}
|
|
526
|
+
async function createMiddleware(projectPath) {
|
|
527
|
+
const content = `import { authMiddleware } from 'miragedev-sdk/auth/middleware'
|
|
528
|
+
|
|
529
|
+
export default authMiddleware({
|
|
530
|
+
publicRoutes: ['/', '/login', '/pricing'],
|
|
531
|
+
redirectTo: '/login'
|
|
532
|
+
})
|
|
533
|
+
|
|
534
|
+
export const config = {
|
|
535
|
+
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)']
|
|
536
|
+
}
|
|
537
|
+
`;
|
|
538
|
+
await fs__default.default.writeFile(path__default.default.join(projectPath, "src", "middleware.ts"), content, "utf-8");
|
|
539
|
+
}
|
|
540
|
+
async function createWebhookRoute(projectPath, _answers) {
|
|
541
|
+
const webhookDir = path__default.default.join(projectPath, "src", "app", "api", "webhooks", "stripe");
|
|
542
|
+
await fs__default.default.mkdir(webhookDir, { recursive: true });
|
|
543
|
+
const content = `import { handleWebhook } from 'miragedev-sdk/billing/webhook'
|
|
544
|
+
|
|
545
|
+
export async function POST(req: Request) {
|
|
546
|
+
return handleWebhook(req, {
|
|
547
|
+
onSubscriptionCreated: async (data) => {
|
|
548
|
+
console.log('\u2705 Subscription created:', data.subscriptionId)
|
|
549
|
+
// TODO: Update your database
|
|
550
|
+
// await db.user.update({
|
|
551
|
+
// where: { id: data.userId },
|
|
552
|
+
// data: { subscriptionId: data.subscriptionId, isPro: true }
|
|
553
|
+
// })
|
|
554
|
+
},
|
|
555
|
+
|
|
556
|
+
onSubscriptionUpdated: async (data) => {
|
|
557
|
+
console.log('\u{1F504} Subscription updated:', data.subscriptionId)
|
|
558
|
+
// TODO: Update your database
|
|
559
|
+
},
|
|
560
|
+
|
|
561
|
+
onSubscriptionCanceled: async (data) => {
|
|
562
|
+
console.log('\u274C Subscription canceled:', data.subscriptionId)
|
|
563
|
+
// TODO: Update your database
|
|
564
|
+
// await db.user.update({
|
|
565
|
+
// where: { id: data.userId },
|
|
566
|
+
// data: { isPro: false }
|
|
567
|
+
// })
|
|
568
|
+
},
|
|
569
|
+
|
|
570
|
+
onPaymentFailed: async (data) => {
|
|
571
|
+
console.log('\u{1F4B3} Payment failed:', data.subscriptionId)
|
|
572
|
+
// TODO: Send notification to user
|
|
573
|
+
},
|
|
574
|
+
})
|
|
575
|
+
}
|
|
576
|
+
`;
|
|
577
|
+
await fs__default.default.writeFile(path__default.default.join(webhookDir, "route.ts"), content, "utf-8");
|
|
578
|
+
}
|
|
579
|
+
function getModuleDescription(module) {
|
|
580
|
+
const descriptions = {
|
|
581
|
+
"auth": "requireAuth(), getSession(), middleware",
|
|
582
|
+
"auth/biometric": "enableBiometric(), signInWithBiometric()",
|
|
583
|
+
"billing": "createCheckout(), createPortal(), webhooks",
|
|
584
|
+
"email": "sendEmail(), sendTemplateEmail() with 5 templates",
|
|
585
|
+
"pwa": "configurePWA(), manifest + service worker",
|
|
586
|
+
"mobile": "useNetworkStatus(), useInstallPrompt(), etc"
|
|
587
|
+
};
|
|
588
|
+
return descriptions[module] || "Available functions";
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
exports.createCommand = createCommand;
|
|
@@ -0,0 +1,581 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import fs from 'fs/promises';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
|
|
8
|
+
// cli/commands/create.ts
|
|
9
|
+
async function createCommand(projectName) {
|
|
10
|
+
console.log(chalk.blue.bold("\n\u{1F680} Create New SAAS with MirageDev\n"));
|
|
11
|
+
const answers = await inquirer.prompt([
|
|
12
|
+
{
|
|
13
|
+
type: "input",
|
|
14
|
+
name: "projectName",
|
|
15
|
+
message: "What is your project name?",
|
|
16
|
+
default: projectName || "my-saas",
|
|
17
|
+
when: !projectName,
|
|
18
|
+
validate: (input) => {
|
|
19
|
+
if (!input.trim()) return "Project name is required";
|
|
20
|
+
if (!/^[a-z0-9-]+$/.test(input)) {
|
|
21
|
+
return "Project name can only contain lowercase letters, numbers, and hyphens";
|
|
22
|
+
}
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: "list",
|
|
28
|
+
name: "authProvider",
|
|
29
|
+
message: "Choose your auth provider:",
|
|
30
|
+
choices: [
|
|
31
|
+
{ name: "NextAuth.js (Recommended)", value: "nextauth" },
|
|
32
|
+
{ name: "Clerk", value: "clerk" },
|
|
33
|
+
{ name: "Supabase Auth", value: "supabase" }
|
|
34
|
+
],
|
|
35
|
+
default: "nextauth"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: "list",
|
|
39
|
+
name: "billingProvider",
|
|
40
|
+
message: "Choose your billing provider:",
|
|
41
|
+
choices: [
|
|
42
|
+
{ name: "Stripe (Recommended)", value: "stripe" },
|
|
43
|
+
{ name: "Paddle (Coming soon)", value: "paddle", disabled: true },
|
|
44
|
+
{ name: "LemonSqueezy (Coming soon)", value: "lemonsqueezy", disabled: true }
|
|
45
|
+
],
|
|
46
|
+
default: "stripe"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
type: "list",
|
|
50
|
+
name: "emailProvider",
|
|
51
|
+
message: "Choose your email provider:",
|
|
52
|
+
choices: [
|
|
53
|
+
{ name: "Resend (Recommended)", value: "resend" },
|
|
54
|
+
{ name: "SendGrid", value: "sendgrid" },
|
|
55
|
+
{ name: "Postmark (Coming soon)", value: "postmark", disabled: true }
|
|
56
|
+
],
|
|
57
|
+
default: "resend"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
type: "checkbox",
|
|
61
|
+
name: "features",
|
|
62
|
+
message: "Select additional features:",
|
|
63
|
+
choices: [
|
|
64
|
+
{ name: "PWA Support (Progressive Web App)", value: "pwa", checked: true },
|
|
65
|
+
{ name: "Biometric Authentication", value: "biometric", checked: false },
|
|
66
|
+
{ name: "Mobile Optimizations", value: "mobile", checked: true }
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
type: "confirm",
|
|
71
|
+
name: "includeExamples",
|
|
72
|
+
message: "Include example pages? (Login, Dashboard, Pricing)",
|
|
73
|
+
default: true
|
|
74
|
+
}
|
|
75
|
+
]);
|
|
76
|
+
const finalProjectName = projectName || answers.projectName;
|
|
77
|
+
const projectPath = path.resolve(process.cwd(), finalProjectName);
|
|
78
|
+
console.log(chalk.cyan(`
|
|
79
|
+
\u{1F4E6} Creating project at: ${projectPath}
|
|
80
|
+
`));
|
|
81
|
+
try {
|
|
82
|
+
await createNextjsProject(finalProjectName);
|
|
83
|
+
await installSDK(finalProjectName);
|
|
84
|
+
await generateProjectFiles(finalProjectName, answers);
|
|
85
|
+
if (answers.includeExamples) {
|
|
86
|
+
await createExamplePages(finalProjectName, answers);
|
|
87
|
+
}
|
|
88
|
+
console.log(chalk.green.bold("\n\u2705 Project created successfully!\n"));
|
|
89
|
+
console.log(chalk.cyan("\u{1F4DD} Next steps:\n"));
|
|
90
|
+
console.log(chalk.white(` 1. cd ${finalProjectName}`));
|
|
91
|
+
console.log(chalk.white(" 2. Copy .env.local.example to .env.local"));
|
|
92
|
+
console.log(chalk.white(" 3. Fill in your API keys in .env.local"));
|
|
93
|
+
console.log(chalk.white(" 4. npm run dev\n"));
|
|
94
|
+
console.log(chalk.gray("Documentation: https://github.com/yourusername/miragedev-sdk\n"));
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.error(chalk.red("\n\u274C Error creating project:"), error);
|
|
97
|
+
console.log(chalk.yellow("\nTrying to clean up..."));
|
|
98
|
+
try {
|
|
99
|
+
await fs.rm(projectPath, { recursive: true, force: true });
|
|
100
|
+
} catch {
|
|
101
|
+
}
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function createNextjsProject(projectName) {
|
|
106
|
+
const spinner = ora("Creating Next.js project...").start();
|
|
107
|
+
try {
|
|
108
|
+
execSync(
|
|
109
|
+
`npx create-next-app@latest ${projectName} --typescript --tailwind --app --no-git --eslint --src-dir --import-alias "@/*"`,
|
|
110
|
+
{
|
|
111
|
+
stdio: "pipe",
|
|
112
|
+
cwd: process.cwd()
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
spinner.succeed(chalk.green("Next.js project created"));
|
|
116
|
+
} catch (error) {
|
|
117
|
+
spinner.fail(chalk.red("Failed to create Next.js project"));
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async function installSDK(projectName) {
|
|
122
|
+
const spinner = ora("Installing MirageDev SDK...").start();
|
|
123
|
+
const projectPath = path.resolve(process.cwd(), projectName);
|
|
124
|
+
try {
|
|
125
|
+
execSync("npm install miragedev-sdk", {
|
|
126
|
+
stdio: "pipe",
|
|
127
|
+
cwd: projectPath
|
|
128
|
+
});
|
|
129
|
+
spinner.succeed(chalk.green("MirageDev SDK installed"));
|
|
130
|
+
} catch (error) {
|
|
131
|
+
spinner.fail(chalk.red("Failed to install SDK"));
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
async function generateProjectFiles(projectName, answers) {
|
|
136
|
+
const spinner = ora("Generating configuration files...").start();
|
|
137
|
+
const projectPath = path.resolve(process.cwd(), projectName);
|
|
138
|
+
try {
|
|
139
|
+
await generateConfigFile(projectPath, answers);
|
|
140
|
+
await generateEnvTemplate(projectPath, answers);
|
|
141
|
+
await generateInitFile(projectPath, answers);
|
|
142
|
+
await generateAgentRules(projectPath, answers);
|
|
143
|
+
await updateRootLayout(projectPath);
|
|
144
|
+
spinner.succeed(chalk.green("Configuration files generated"));
|
|
145
|
+
} catch (error) {
|
|
146
|
+
spinner.fail(chalk.red("Failed to generate configuration"));
|
|
147
|
+
throw error;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async function generateConfigFile(projectPath, answers) {
|
|
151
|
+
const config = `// MirageDev SDK Configuration
|
|
152
|
+
import { initMirageDev } from 'miragedev-sdk'
|
|
153
|
+
|
|
154
|
+
export function initializeMirageDev() {
|
|
155
|
+
initMirageDev({
|
|
156
|
+
auth: {
|
|
157
|
+
provider: '${answers.authProvider}',
|
|
158
|
+
sessionSecret: process.env.AUTH_SECRET!,
|
|
159
|
+
},
|
|
160
|
+
billing: {
|
|
161
|
+
provider: '${answers.billingProvider}',
|
|
162
|
+
${answers.billingProvider === "stripe" ? "stripeSecretKey: process.env.STRIPE_SECRET_KEY!," : ""}
|
|
163
|
+
webhookSecret: process.env.${answers.billingProvider.toUpperCase()}_WEBHOOK_SECRET!,
|
|
164
|
+
},
|
|
165
|
+
email: {
|
|
166
|
+
provider: '${answers.emailProvider}',
|
|
167
|
+
apiKey: process.env.${answers.emailProvider.toUpperCase()}_API_KEY!,
|
|
168
|
+
from: process.env.EMAIL_FROM!,
|
|
169
|
+
},
|
|
170
|
+
})
|
|
171
|
+
}
|
|
172
|
+
`;
|
|
173
|
+
await fs.writeFile(path.join(projectPath, "lib", "miragedev.config.ts"), config, "utf-8");
|
|
174
|
+
}
|
|
175
|
+
async function generateEnvTemplate(projectPath, answers) {
|
|
176
|
+
const env = `# MirageDev SDK Environment Variables
|
|
177
|
+
# Copy this file to .env.local and fill in your actual values
|
|
178
|
+
|
|
179
|
+
# Auth
|
|
180
|
+
AUTH_SECRET=your-secret-key-here-generate-with-openssl-rand-base64-32
|
|
181
|
+
|
|
182
|
+
# Billing (${answers.billingProvider})
|
|
183
|
+
${answers.billingProvider === "stripe" ? "STRIPE_SECRET_KEY=sk_test_xxx" : ""}
|
|
184
|
+
${answers.billingProvider.toUpperCase()}_WEBHOOK_SECRET=whsec_xxx
|
|
185
|
+
|
|
186
|
+
# Email (${answers.emailProvider})
|
|
187
|
+
${answers.emailProvider.toUpperCase()}_API_KEY=your-api-key-here
|
|
188
|
+
EMAIL_FROM=noreply@yourapp.com
|
|
189
|
+
|
|
190
|
+
# Next.js
|
|
191
|
+
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
|
192
|
+
`;
|
|
193
|
+
await fs.writeFile(path.join(projectPath, ".env.local.example"), env, "utf-8");
|
|
194
|
+
}
|
|
195
|
+
async function generateInitFile(projectPath, answers) {
|
|
196
|
+
const hasFeature = (feature) => answers.features.includes(feature);
|
|
197
|
+
const imports = [];
|
|
198
|
+
const setup = [];
|
|
199
|
+
if (hasFeature("pwa")) {
|
|
200
|
+
imports.push("import { configurePWA } from 'miragedev-sdk/pwa'");
|
|
201
|
+
setup.push(`
|
|
202
|
+
// PWA Configuration
|
|
203
|
+
export const pwaConfig = configurePWA({
|
|
204
|
+
name: 'Your SAAS App',
|
|
205
|
+
shortName: 'SAAS',
|
|
206
|
+
theme: '#000000',
|
|
207
|
+
backgroundColor: '#ffffff',
|
|
208
|
+
offline: { enabled: true, pages: ['/'] }
|
|
209
|
+
})`);
|
|
210
|
+
}
|
|
211
|
+
const content = `// MirageDev SDK Initialization
|
|
212
|
+
import { initializeMirageDev } from './miragedev.config'
|
|
213
|
+
${imports.join("\n")}
|
|
214
|
+
|
|
215
|
+
// Initialize SDK
|
|
216
|
+
initializeMirageDev()
|
|
217
|
+
|
|
218
|
+
${setup.join("\n\n")}
|
|
219
|
+
`;
|
|
220
|
+
await fs.writeFile(path.join(projectPath, "lib", "miragedev.init.ts"), content, "utf-8");
|
|
221
|
+
}
|
|
222
|
+
async function generateAgentRules(projectPath, answers) {
|
|
223
|
+
const modules = ["auth", "billing", "email"];
|
|
224
|
+
if (answers.features.includes("pwa")) modules.push("pwa");
|
|
225
|
+
if (answers.features.includes("mobile")) modules.push("mobile");
|
|
226
|
+
if (answers.features.includes("biometric")) modules.push("auth/biometric");
|
|
227
|
+
const content = `# AI Agent Instructions
|
|
228
|
+
|
|
229
|
+
> **For AI Assistants (GitHub Copilot, OpenCode, Cursor, etc.):**
|
|
230
|
+
> This project uses **MirageDev SDK**. Always prefer SDK functions over manual implementations.
|
|
231
|
+
|
|
232
|
+
## Installed SDK Modules
|
|
233
|
+
|
|
234
|
+
${modules.map((mod) => `- \`miragedev-sdk/${mod}\` - ${getModuleDescription(mod)}`).join("\n")}
|
|
235
|
+
|
|
236
|
+
## Configuration
|
|
237
|
+
|
|
238
|
+
- **Auth Provider:** ${answers.authProvider}
|
|
239
|
+
- **Billing Provider:** ${answers.billingProvider}
|
|
240
|
+
- **Email Provider:** ${answers.emailProvider}
|
|
241
|
+
${answers.features.includes("pwa") ? "- **PWA:** Enabled" : ""}
|
|
242
|
+
${answers.features.includes("biometric") ? "- **Biometric Auth:** Enabled" : ""}
|
|
243
|
+
${answers.features.includes("mobile") ? "- **Mobile Optimizations:** Enabled" : ""}
|
|
244
|
+
|
|
245
|
+
## Code Generation Rules
|
|
246
|
+
|
|
247
|
+
### \u2705 DO: Use MirageDev SDK
|
|
248
|
+
|
|
249
|
+
When developer needs authentication:
|
|
250
|
+
\`\`\`typescript
|
|
251
|
+
// \u2705 CORRECT
|
|
252
|
+
import { requireAuth } from 'miragedev-sdk/auth'
|
|
253
|
+
|
|
254
|
+
export default async function Page() {
|
|
255
|
+
const session = await requireAuth()
|
|
256
|
+
return <div>Hello {session.user.name}</div>
|
|
257
|
+
}
|
|
258
|
+
\`\`\`
|
|
259
|
+
|
|
260
|
+
### \u274C DON'T: Manual Implementation
|
|
261
|
+
|
|
262
|
+
\`\`\`typescript
|
|
263
|
+
// \u274C WRONG - Don't do this when SDK exists
|
|
264
|
+
import Stripe from 'stripe'
|
|
265
|
+
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!)
|
|
266
|
+
\`\`\`
|
|
267
|
+
|
|
268
|
+
For full documentation, see: https://github.com/yourusername/miragedev-sdk
|
|
269
|
+
`;
|
|
270
|
+
await fs.writeFile(path.join(projectPath, "AGENTS.md"), content, "utf-8");
|
|
271
|
+
}
|
|
272
|
+
async function updateRootLayout(projectPath) {
|
|
273
|
+
const layoutPath = path.join(projectPath, "src", "app", "layout.tsx");
|
|
274
|
+
let layout = await fs.readFile(layoutPath, "utf-8");
|
|
275
|
+
const importStatement = "import '@/lib/miragedev.init'\n";
|
|
276
|
+
if (!layout.includes("miragedev.init")) {
|
|
277
|
+
const lines = layout.split("\n");
|
|
278
|
+
const firstImportIndex = lines.findIndex((line) => line.startsWith("import"));
|
|
279
|
+
if (firstImportIndex !== -1) {
|
|
280
|
+
lines.splice(firstImportIndex, 0, importStatement);
|
|
281
|
+
layout = lines.join("\n");
|
|
282
|
+
await fs.writeFile(layoutPath, layout, "utf-8");
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async function createExamplePages(projectPath, answers) {
|
|
287
|
+
const spinner = ora("Creating example pages...").start();
|
|
288
|
+
try {
|
|
289
|
+
await createLoginPage(projectPath, answers);
|
|
290
|
+
await createDashboardPage(projectPath);
|
|
291
|
+
await createPricingPage(projectPath);
|
|
292
|
+
await createMiddleware(projectPath);
|
|
293
|
+
await createWebhookRoute(projectPath, answers);
|
|
294
|
+
spinner.succeed(chalk.green("Example pages created"));
|
|
295
|
+
} catch (error) {
|
|
296
|
+
spinner.fail(chalk.red("Failed to create example pages"));
|
|
297
|
+
throw error;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
async function createLoginPage(projectPath, _answers) {
|
|
301
|
+
const authDir = path.join(projectPath, "src", "app", "(auth)", "login");
|
|
302
|
+
await fs.mkdir(authDir, { recursive: true });
|
|
303
|
+
const content = `'use client'
|
|
304
|
+
|
|
305
|
+
import { signIn } from 'miragedev-sdk/auth/client'
|
|
306
|
+
import { useState } from 'react'
|
|
307
|
+
|
|
308
|
+
export default function LoginPage() {
|
|
309
|
+
const [isLoading, setIsLoading] = useState(false)
|
|
310
|
+
|
|
311
|
+
const handleSignIn = async (provider: string) => {
|
|
312
|
+
setIsLoading(true)
|
|
313
|
+
try {
|
|
314
|
+
await signIn(provider)
|
|
315
|
+
} catch (error) {
|
|
316
|
+
console.error('Sign in error:', error)
|
|
317
|
+
setIsLoading(false)
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return (
|
|
322
|
+
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-blue-50 to-indigo-100">
|
|
323
|
+
<div className="w-full max-w-md space-y-8 rounded-2xl bg-white p-8 shadow-xl">
|
|
324
|
+
<div className="text-center">
|
|
325
|
+
<h1 className="text-3xl font-bold text-gray-900">Welcome Back</h1>
|
|
326
|
+
<p className="mt-2 text-gray-600">Sign in to your account</p>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
<div className="space-y-4">
|
|
330
|
+
<button
|
|
331
|
+
onClick={() => handleSignIn('google')}
|
|
332
|
+
disabled={isLoading}
|
|
333
|
+
className="w-full rounded-lg bg-blue-600 px-4 py-3 font-medium text-white hover:bg-blue-700 disabled:opacity-50"
|
|
334
|
+
>
|
|
335
|
+
{isLoading ? 'Signing in...' : 'Sign in with Google'}
|
|
336
|
+
</button>
|
|
337
|
+
|
|
338
|
+
<button
|
|
339
|
+
onClick={() => handleSignIn('github')}
|
|
340
|
+
disabled={isLoading}
|
|
341
|
+
className="w-full rounded-lg bg-gray-900 px-4 py-3 font-medium text-white hover:bg-gray-800 disabled:opacity-50"
|
|
342
|
+
>
|
|
343
|
+
{isLoading ? 'Signing in...' : 'Sign in with GitHub'}
|
|
344
|
+
</button>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
<p className="text-center text-sm text-gray-500">
|
|
348
|
+
By signing in, you agree to our Terms of Service
|
|
349
|
+
</p>
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
352
|
+
)
|
|
353
|
+
}
|
|
354
|
+
`;
|
|
355
|
+
await fs.writeFile(path.join(authDir, "page.tsx"), content, "utf-8");
|
|
356
|
+
}
|
|
357
|
+
async function createDashboardPage(projectPath) {
|
|
358
|
+
const dashboardDir = path.join(projectPath, "src", "app", "(protected)", "dashboard");
|
|
359
|
+
await fs.mkdir(dashboardDir, { recursive: true });
|
|
360
|
+
const content = `import { requireAuth } from 'miragedev-sdk/auth'
|
|
361
|
+
import { UpgradeButton } from '@/components/UpgradeButton'
|
|
362
|
+
|
|
363
|
+
export default async function DashboardPage() {
|
|
364
|
+
const session = await requireAuth()
|
|
365
|
+
|
|
366
|
+
return (
|
|
367
|
+
<div className="min-h-screen bg-gray-50 p-8">
|
|
368
|
+
<div className="mx-auto max-w-4xl">
|
|
369
|
+
<h1 className="text-3xl font-bold text-gray-900">
|
|
370
|
+
Welcome back, {session.user.name || 'User'}!
|
|
371
|
+
</h1>
|
|
372
|
+
|
|
373
|
+
<div className="mt-6 grid gap-6 md:grid-cols-2">
|
|
374
|
+
<div className="rounded-lg bg-white p-6 shadow">
|
|
375
|
+
<h2 className="text-xl font-semibold">Your Account</h2>
|
|
376
|
+
<p className="mt-2 text-gray-600">Email: {session.user.email}</p>
|
|
377
|
+
</div>
|
|
378
|
+
|
|
379
|
+
<div className="rounded-lg bg-white p-6 shadow">
|
|
380
|
+
<h2 className="text-xl font-semibold">Subscription</h2>
|
|
381
|
+
<div className="mt-4">
|
|
382
|
+
<UpgradeButton />
|
|
383
|
+
</div>
|
|
384
|
+
</div>
|
|
385
|
+
</div>
|
|
386
|
+
</div>
|
|
387
|
+
</div>
|
|
388
|
+
)
|
|
389
|
+
}
|
|
390
|
+
`;
|
|
391
|
+
await fs.writeFile(path.join(dashboardDir, "page.tsx"), content, "utf-8");
|
|
392
|
+
const componentsDir = path.join(projectPath, "src", "components");
|
|
393
|
+
await fs.mkdir(componentsDir, { recursive: true });
|
|
394
|
+
const buttonContent = `'use client'
|
|
395
|
+
|
|
396
|
+
import { useSubscription } from 'miragedev-sdk/billing/client'
|
|
397
|
+
|
|
398
|
+
export function UpgradeButton() {
|
|
399
|
+
const { subscription, isLoading } = useSubscription()
|
|
400
|
+
|
|
401
|
+
if (isLoading) {
|
|
402
|
+
return <div className="text-gray-500">Loading subscription...</div>
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (subscription?.status === 'active') {
|
|
406
|
+
return (
|
|
407
|
+
<div className="rounded-lg bg-green-50 p-4 text-green-800">
|
|
408
|
+
\u2705 You're a Pro subscriber!
|
|
409
|
+
</div>
|
|
410
|
+
)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return (
|
|
414
|
+
<button className="w-full rounded-lg bg-purple-600 px-6 py-3 font-medium text-white hover:bg-purple-700">
|
|
415
|
+
Upgrade to Pro
|
|
416
|
+
</button>
|
|
417
|
+
)
|
|
418
|
+
}
|
|
419
|
+
`;
|
|
420
|
+
await fs.writeFile(path.join(componentsDir, "UpgradeButton.tsx"), buttonContent, "utf-8");
|
|
421
|
+
}
|
|
422
|
+
async function createPricingPage(projectPath) {
|
|
423
|
+
const pricingDir = path.join(projectPath, "src", "app", "pricing");
|
|
424
|
+
await fs.mkdir(pricingDir, { recursive: true });
|
|
425
|
+
const content = `export default function PricingPage() {
|
|
426
|
+
return (
|
|
427
|
+
<div className="min-h-screen bg-gradient-to-br from-purple-50 to-blue-50 py-16">
|
|
428
|
+
<div className="mx-auto max-w-6xl px-4">
|
|
429
|
+
<h1 className="text-center text-4xl font-bold text-gray-900">
|
|
430
|
+
Simple, Transparent Pricing
|
|
431
|
+
</h1>
|
|
432
|
+
<p className="mt-4 text-center text-xl text-gray-600">
|
|
433
|
+
Choose the plan that's right for you
|
|
434
|
+
</p>
|
|
435
|
+
|
|
436
|
+
<div className="mt-12 grid gap-8 md:grid-cols-3">
|
|
437
|
+
{/* Free Plan */}
|
|
438
|
+
<div className="rounded-2xl bg-white p-8 shadow-lg">
|
|
439
|
+
<h3 className="text-2xl font-bold">Free</h3>
|
|
440
|
+
<p className="mt-2 text-gray-600">Perfect for getting started</p>
|
|
441
|
+
<div className="mt-4 text-4xl font-bold">$0</div>
|
|
442
|
+
<p className="text-gray-500">per month</p>
|
|
443
|
+
|
|
444
|
+
<ul className="mt-6 space-y-3">
|
|
445
|
+
<li className="flex items-center">
|
|
446
|
+
<span className="mr-2">\u2713</span> Basic features
|
|
447
|
+
</li>
|
|
448
|
+
<li className="flex items-center">
|
|
449
|
+
<span className="mr-2">\u2713</span> 1 user
|
|
450
|
+
</li>
|
|
451
|
+
</ul>
|
|
452
|
+
|
|
453
|
+
<button className="mt-8 w-full rounded-lg border-2 border-gray-300 px-6 py-3 font-medium hover:bg-gray-50">
|
|
454
|
+
Get Started
|
|
455
|
+
</button>
|
|
456
|
+
</div>
|
|
457
|
+
|
|
458
|
+
{/* Pro Plan */}
|
|
459
|
+
<div className="rounded-2xl bg-gradient-to-br from-purple-600 to-blue-600 p-8 text-white shadow-2xl ring-4 ring-purple-300">
|
|
460
|
+
<div className="text-sm font-semibold uppercase tracking-wide">
|
|
461
|
+
Most Popular
|
|
462
|
+
</div>
|
|
463
|
+
<h3 className="mt-2 text-2xl font-bold">Pro</h3>
|
|
464
|
+
<p className="mt-2 opacity-90">For growing businesses</p>
|
|
465
|
+
<div className="mt-4 text-4xl font-bold">$29</div>
|
|
466
|
+
<p className="opacity-80">per month</p>
|
|
467
|
+
|
|
468
|
+
<ul className="mt-6 space-y-3">
|
|
469
|
+
<li className="flex items-center">
|
|
470
|
+
<span className="mr-2">\u2713</span> All Free features
|
|
471
|
+
</li>
|
|
472
|
+
<li className="flex items-center">
|
|
473
|
+
<span className="mr-2">\u2713</span> Unlimited users
|
|
474
|
+
</li>
|
|
475
|
+
<li className="flex items-center">
|
|
476
|
+
<span className="mr-2">\u2713</span> Priority support
|
|
477
|
+
</li>
|
|
478
|
+
</ul>
|
|
479
|
+
|
|
480
|
+
<button className="mt-8 w-full rounded-lg bg-white px-6 py-3 font-medium text-purple-600 hover:bg-gray-50">
|
|
481
|
+
Subscribe Now
|
|
482
|
+
</button>
|
|
483
|
+
</div>
|
|
484
|
+
|
|
485
|
+
{/* Enterprise Plan */}
|
|
486
|
+
<div className="rounded-2xl bg-white p-8 shadow-lg">
|
|
487
|
+
<h3 className="text-2xl font-bold">Enterprise</h3>
|
|
488
|
+
<p className="mt-2 text-gray-600">For large organizations</p>
|
|
489
|
+
<div className="mt-4 text-4xl font-bold">Custom</div>
|
|
490
|
+
<p className="text-gray-500">contact us</p>
|
|
491
|
+
|
|
492
|
+
<ul className="mt-6 space-y-3">
|
|
493
|
+
<li className="flex items-center">
|
|
494
|
+
<span className="mr-2">\u2713</span> All Pro features
|
|
495
|
+
</li>
|
|
496
|
+
<li className="flex items-center">
|
|
497
|
+
<span className="mr-2">\u2713</span> Custom integrations
|
|
498
|
+
</li>
|
|
499
|
+
<li className="flex items-center">
|
|
500
|
+
<span className="mr-2">\u2713</span> Dedicated support
|
|
501
|
+
</li>
|
|
502
|
+
</ul>
|
|
503
|
+
|
|
504
|
+
<button className="mt-8 w-full rounded-lg border-2 border-gray-300 px-6 py-3 font-medium hover:bg-gray-50">
|
|
505
|
+
Contact Sales
|
|
506
|
+
</button>
|
|
507
|
+
</div>
|
|
508
|
+
</div>
|
|
509
|
+
</div>
|
|
510
|
+
</div>
|
|
511
|
+
)
|
|
512
|
+
}
|
|
513
|
+
`;
|
|
514
|
+
await fs.writeFile(path.join(pricingDir, "page.tsx"), content, "utf-8");
|
|
515
|
+
}
|
|
516
|
+
async function createMiddleware(projectPath) {
|
|
517
|
+
const content = `import { authMiddleware } from 'miragedev-sdk/auth/middleware'
|
|
518
|
+
|
|
519
|
+
export default authMiddleware({
|
|
520
|
+
publicRoutes: ['/', '/login', '/pricing'],
|
|
521
|
+
redirectTo: '/login'
|
|
522
|
+
})
|
|
523
|
+
|
|
524
|
+
export const config = {
|
|
525
|
+
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)']
|
|
526
|
+
}
|
|
527
|
+
`;
|
|
528
|
+
await fs.writeFile(path.join(projectPath, "src", "middleware.ts"), content, "utf-8");
|
|
529
|
+
}
|
|
530
|
+
async function createWebhookRoute(projectPath, _answers) {
|
|
531
|
+
const webhookDir = path.join(projectPath, "src", "app", "api", "webhooks", "stripe");
|
|
532
|
+
await fs.mkdir(webhookDir, { recursive: true });
|
|
533
|
+
const content = `import { handleWebhook } from 'miragedev-sdk/billing/webhook'
|
|
534
|
+
|
|
535
|
+
export async function POST(req: Request) {
|
|
536
|
+
return handleWebhook(req, {
|
|
537
|
+
onSubscriptionCreated: async (data) => {
|
|
538
|
+
console.log('\u2705 Subscription created:', data.subscriptionId)
|
|
539
|
+
// TODO: Update your database
|
|
540
|
+
// await db.user.update({
|
|
541
|
+
// where: { id: data.userId },
|
|
542
|
+
// data: { subscriptionId: data.subscriptionId, isPro: true }
|
|
543
|
+
// })
|
|
544
|
+
},
|
|
545
|
+
|
|
546
|
+
onSubscriptionUpdated: async (data) => {
|
|
547
|
+
console.log('\u{1F504} Subscription updated:', data.subscriptionId)
|
|
548
|
+
// TODO: Update your database
|
|
549
|
+
},
|
|
550
|
+
|
|
551
|
+
onSubscriptionCanceled: async (data) => {
|
|
552
|
+
console.log('\u274C Subscription canceled:', data.subscriptionId)
|
|
553
|
+
// TODO: Update your database
|
|
554
|
+
// await db.user.update({
|
|
555
|
+
// where: { id: data.userId },
|
|
556
|
+
// data: { isPro: false }
|
|
557
|
+
// })
|
|
558
|
+
},
|
|
559
|
+
|
|
560
|
+
onPaymentFailed: async (data) => {
|
|
561
|
+
console.log('\u{1F4B3} Payment failed:', data.subscriptionId)
|
|
562
|
+
// TODO: Send notification to user
|
|
563
|
+
},
|
|
564
|
+
})
|
|
565
|
+
}
|
|
566
|
+
`;
|
|
567
|
+
await fs.writeFile(path.join(webhookDir, "route.ts"), content, "utf-8");
|
|
568
|
+
}
|
|
569
|
+
function getModuleDescription(module) {
|
|
570
|
+
const descriptions = {
|
|
571
|
+
"auth": "requireAuth(), getSession(), middleware",
|
|
572
|
+
"auth/biometric": "enableBiometric(), signInWithBiometric()",
|
|
573
|
+
"billing": "createCheckout(), createPortal(), webhooks",
|
|
574
|
+
"email": "sendEmail(), sendTemplateEmail() with 5 templates",
|
|
575
|
+
"pwa": "configurePWA(), manifest + service worker",
|
|
576
|
+
"mobile": "useNetworkStatus(), useInstallPrompt(), etc"
|
|
577
|
+
};
|
|
578
|
+
return descriptions[module] || "Available functions";
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
export { createCommand };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkOIWPIRJY_cjs = require('../../chunk-OIWPIRJY.cjs');
|
|
4
|
+
require('../../chunk-75ZPJI57.cjs');
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Object.defineProperty(exports, "createCommand", {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: function () { return chunkOIWPIRJY_cjs.createCommand; }
|
|
11
|
+
});
|
package/dist/cli/index.cjs
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var chunkM26EDKMY_cjs = require('../chunk-M26EDKMY.cjs');
|
|
5
|
+
var chunkOIWPIRJY_cjs = require('../chunk-OIWPIRJY.cjs');
|
|
5
6
|
require('../chunk-75ZPJI57.cjs');
|
|
6
7
|
var commander = require('commander');
|
|
7
8
|
|
|
8
9
|
var program = new commander.Command();
|
|
9
10
|
program.name("miragedev").description("MirageDev SDK CLI - Build SAAS apps faster").version("0.1.0");
|
|
10
|
-
program.command("
|
|
11
|
+
program.command("create [project-name]").description("Create a new SAAS project with MirageDev SDK").action(chunkOIWPIRJY_cjs.createCommand);
|
|
12
|
+
program.command("init").description("Initialize MirageDev SDK in existing project").action(chunkM26EDKMY_cjs.initCommand);
|
|
11
13
|
program.parse();
|
package/dist/cli/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { initCommand } from '../chunk-DZDDLA4G.js';
|
|
3
|
+
import { createCommand } from '../chunk-PDNLOOI2.js';
|
|
3
4
|
import '../chunk-MLKGABMK.js';
|
|
4
5
|
import { Command } from 'commander';
|
|
5
6
|
|
|
6
7
|
var program = new Command();
|
|
7
8
|
program.name("miragedev").description("MirageDev SDK CLI - Build SAAS apps faster").version("0.1.0");
|
|
8
|
-
program.command("
|
|
9
|
+
program.command("create [project-name]").description("Create a new SAAS project with MirageDev SDK").action(createCommand);
|
|
10
|
+
program.command("init").description("Initialize MirageDev SDK in existing project").action(initCommand);
|
|
9
11
|
program.parse();
|