@xbg.solutions/create-backend 1.0.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/bin/create-backend.js +3 -0
- package/lib/cli.d.ts +12 -0
- package/lib/cli.js +55 -0
- package/lib/cli.js.map +1 -0
- package/lib/commands/add-util.d.ts +9 -0
- package/lib/commands/add-util.js +119 -0
- package/lib/commands/add-util.js.map +1 -0
- package/lib/commands/init.d.ts +11 -0
- package/lib/commands/init.js +372 -0
- package/lib/commands/init.js.map +1 -0
- package/lib/commands/sync.d.ts +10 -0
- package/lib/commands/sync.js +161 -0
- package/lib/commands/sync.js.map +1 -0
- package/lib/utils-registry.d.ts +25 -0
- package/lib/utils-registry.js +187 -0
- package/lib/utils-registry.js.map +1 -0
- package/package.json +38 -0
- package/src/project-template/__examples__/README.md +559 -0
- package/src/project-template/__examples__/blog-platform.model.ts +528 -0
- package/src/project-template/__examples__/communications-usage.ts +175 -0
- package/src/project-template/__examples__/ecommerce-store.model.ts +1200 -0
- package/src/project-template/__examples__/saas-multi-tenant.model.ts +798 -0
- package/src/project-template/__examples__/user.model.ts +221 -0
- package/src/project-template/__scripts__/deploy.js +115 -0
- package/src/project-template/__scripts__/generate.js +122 -0
- package/src/project-template/__scripts__/setup.js +425 -0
- package/src/project-template/__scripts__/validate.js +325 -0
- package/src/project-template/firebase.json +32 -0
- package/src/project-template/firestore.rules +12 -0
- package/src/project-template/functions/jest.config.js +49 -0
- package/src/project-template/functions/src/index.ts +46 -0
- package/src/project-template/functions/tsconfig.json +38 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Interactive Setup Wizard
|
|
5
|
+
*
|
|
6
|
+
* Configures the backend boilerplate project for either:
|
|
7
|
+
* - Backend-only deployment (standalone Firebase Functions)
|
|
8
|
+
* - Mono-repo deployment (e.g. SvelteKit frontend + Firebase backend)
|
|
9
|
+
*
|
|
10
|
+
* Handles:
|
|
11
|
+
* - Project naming and Firebase project ID
|
|
12
|
+
* - Database mode (multi-DB recommended, single-DB supported)
|
|
13
|
+
* - Environment configuration (.env generation)
|
|
14
|
+
* - Feature flag selection
|
|
15
|
+
* - .firebaserc update
|
|
16
|
+
* - Dependency installation
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const readline = require('readline');
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
const { execSync } = require('child_process');
|
|
23
|
+
|
|
24
|
+
const colors = {
|
|
25
|
+
reset: '\x1b[0m',
|
|
26
|
+
bright: '\x1b[1m',
|
|
27
|
+
dim: '\x1b[2m',
|
|
28
|
+
green: '\x1b[32m',
|
|
29
|
+
blue: '\x1b[34m',
|
|
30
|
+
yellow: '\x1b[33m',
|
|
31
|
+
red: '\x1b[31m',
|
|
32
|
+
cyan: '\x1b[36m',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const rl = readline.createInterface({
|
|
36
|
+
input: process.stdin,
|
|
37
|
+
output: process.stdout,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
function question(prompt) {
|
|
41
|
+
return new Promise((resolve) => {
|
|
42
|
+
rl.question(prompt, resolve);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function printBanner() {
|
|
47
|
+
console.log(`
|
|
48
|
+
${colors.bright}${colors.blue}╔════════════════════════════════════════════════════════════╗
|
|
49
|
+
║ ║
|
|
50
|
+
║ Backend Boilerplate Setup Wizard ║
|
|
51
|
+
║ ║
|
|
52
|
+
╚════════════════════════════════════════════════════════════╝${colors.reset}
|
|
53
|
+
|
|
54
|
+
${colors.bright}This wizard configures your backend project.${colors.reset}
|
|
55
|
+
${colors.dim}Press Enter to accept default values shown in [brackets].${colors.reset}
|
|
56
|
+
`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function printSection(title) {
|
|
60
|
+
console.log(`\n${colors.bright}${colors.cyan}── ${title} ${'─'.repeat(Math.max(0, 54 - title.length))}${colors.reset}\n`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ──────────────────────────────────────────────────────────
|
|
64
|
+
// Resolve paths relative to __scripts__/ location
|
|
65
|
+
// ──────────────────────────────────────────────────────────
|
|
66
|
+
const SCRIPTS_DIR = __dirname;
|
|
67
|
+
const REPO_ROOT = path.resolve(SCRIPTS_DIR, '..');
|
|
68
|
+
|
|
69
|
+
async function setup() {
|
|
70
|
+
printBanner();
|
|
71
|
+
const config = {};
|
|
72
|
+
|
|
73
|
+
// ── Project basics ──────────────────────────────────────
|
|
74
|
+
printSection('Project');
|
|
75
|
+
|
|
76
|
+
config.projectName = (await question(
|
|
77
|
+
`${colors.blue}Project name ${colors.dim}[my-backend-api]${colors.reset}: `
|
|
78
|
+
)).trim() || 'my-backend-api';
|
|
79
|
+
|
|
80
|
+
config.firebaseProject = (await question(
|
|
81
|
+
`${colors.blue}Firebase project ID ${colors.dim}(required)${colors.reset}: `
|
|
82
|
+
)).trim();
|
|
83
|
+
|
|
84
|
+
if (!config.firebaseProject) {
|
|
85
|
+
console.log(`\n${colors.red}Firebase project ID is required.${colors.reset}`);
|
|
86
|
+
console.log(`${colors.dim}Create one at https://console.firebase.google.com${colors.reset}\n`);
|
|
87
|
+
rl.close();
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ── Deployment mode ─────────────────────────────────────
|
|
92
|
+
printSection('Deployment Mode');
|
|
93
|
+
|
|
94
|
+
console.log(` ${colors.bright}1)${colors.reset} Backend-only ${colors.dim}(standalone Firebase Functions project)${colors.reset}`);
|
|
95
|
+
console.log(` ${colors.bright}2)${colors.reset} Mono-repo ${colors.dim}(frontend + backend in one repo, backend in functions/)${colors.reset}`);
|
|
96
|
+
|
|
97
|
+
const modeChoice = (await question(
|
|
98
|
+
`\n${colors.blue}Choose mode ${colors.dim}[1]${colors.reset}: `
|
|
99
|
+
)).trim() || '1';
|
|
100
|
+
config.isMonoRepo = modeChoice === '2';
|
|
101
|
+
|
|
102
|
+
if (config.isMonoRepo) {
|
|
103
|
+
console.log(`\n${colors.yellow}Mono-repo mode selected.${colors.reset}`);
|
|
104
|
+
console.log(`${colors.dim}The backend source lives in functions/src/ and deploys as Firebase Functions.`);
|
|
105
|
+
console.log(`In a mono-repo the project root has its own package.json and the frontend`);
|
|
106
|
+
console.log(`typically lives in frontend/ (or a similar directory).${colors.reset}`);
|
|
107
|
+
console.log(`\n${colors.dim}Note: .claude/skills/ should live at the mono-repo root, not inside functions/.${colors.reset}`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ── Environment ─────────────────────────────────────────
|
|
111
|
+
printSection('Environment');
|
|
112
|
+
|
|
113
|
+
const envChoice = (await question(
|
|
114
|
+
`${colors.blue}Environment ${colors.dim}[development]${colors.reset} (development/staging/production): `
|
|
115
|
+
)).trim() || 'development';
|
|
116
|
+
config.environment = envChoice;
|
|
117
|
+
|
|
118
|
+
config.port = (await question(
|
|
119
|
+
`${colors.blue}Port ${colors.dim}[5001]${colors.reset}: `
|
|
120
|
+
)).trim() || '5001';
|
|
121
|
+
|
|
122
|
+
// ── Database ────────────────────────────────────────────
|
|
123
|
+
printSection('Database');
|
|
124
|
+
|
|
125
|
+
console.log(` ${colors.bright}Multi-database${colors.reset} ${colors.dim}(recommended)${colors.reset}: Separate Firestore databases for`);
|
|
126
|
+
console.log(` different concerns (e.g. main data vs analytics). Each database`);
|
|
127
|
+
console.log(` has independent scaling and can be in different regions.`);
|
|
128
|
+
console.log('');
|
|
129
|
+
console.log(` ${colors.bright}Single-database${colors.reset}: Everything in Firestore's (default) database.`);
|
|
130
|
+
console.log(` Simpler to start but harder to separate later.\n`);
|
|
131
|
+
|
|
132
|
+
const dbChoice = (await question(
|
|
133
|
+
`${colors.blue}Use multi-database? ${colors.dim}[Y/n]${colors.reset}: `
|
|
134
|
+
)).trim().toLowerCase();
|
|
135
|
+
config.multiDB = dbChoice !== 'n';
|
|
136
|
+
|
|
137
|
+
if (config.multiDB) {
|
|
138
|
+
config.mainDatabaseId = (await question(
|
|
139
|
+
`${colors.blue}Main database ID ${colors.dim}[main]${colors.reset}: `
|
|
140
|
+
)).trim() || 'main';
|
|
141
|
+
|
|
142
|
+
config.analyticsDatabaseId = (await question(
|
|
143
|
+
`${colors.blue}Analytics database ID ${colors.dim}[analytics]${colors.reset}: `
|
|
144
|
+
)).trim() || 'analytics';
|
|
145
|
+
} else {
|
|
146
|
+
config.mainDatabaseId = '(default)';
|
|
147
|
+
config.analyticsDatabaseId = '(default)';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ── API ─────────────────────────────────────────────────
|
|
151
|
+
printSection('API');
|
|
152
|
+
|
|
153
|
+
config.corsOrigins = (await question(
|
|
154
|
+
`${colors.blue}CORS origins ${colors.dim}[http://localhost:5173,http://localhost:3000]${colors.reset}: `
|
|
155
|
+
)).trim() || 'http://localhost:5173,http://localhost:3000';
|
|
156
|
+
|
|
157
|
+
config.apiBasePath = (await question(
|
|
158
|
+
`${colors.blue}API base path ${colors.dim}[/api/v1]${colors.reset}: `
|
|
159
|
+
)).trim() || '/api/v1';
|
|
160
|
+
|
|
161
|
+
// ── Features ────────────────────────────────────────────
|
|
162
|
+
printSection('Features');
|
|
163
|
+
|
|
164
|
+
config.enableAuth = (await question(` Authentication ${colors.dim}[Y/n]${colors.reset}: `)).trim().toLowerCase() !== 'n';
|
|
165
|
+
config.enableMultiTenant = (await question(` Multi-tenant ${colors.dim}[y/N]${colors.reset}: `)).trim().toLowerCase() === 'y';
|
|
166
|
+
config.enableFileUploads = (await question(` File uploads ${colors.dim}[Y/n]${colors.reset}: `)).trim().toLowerCase() !== 'n';
|
|
167
|
+
config.enableNotifications = (await question(` Notifications ${colors.dim}[Y/n]${colors.reset}: `)).trim().toLowerCase() !== 'n';
|
|
168
|
+
config.enableAnalytics = (await question(` Analytics ${colors.dim}[y/N]${colors.reset}: `)).trim().toLowerCase() === 'y';
|
|
169
|
+
config.enableRealtime = (await question(` Realtime (SSE/WebSocket) ${colors.dim}[Y/n]${colors.reset}: `)).trim().toLowerCase() !== 'n';
|
|
170
|
+
|
|
171
|
+
// ── Write files ─────────────────────────────────────────
|
|
172
|
+
printSection('Writing configuration');
|
|
173
|
+
|
|
174
|
+
writeEnvFile(config);
|
|
175
|
+
writeFirebaseRc(config);
|
|
176
|
+
updateFirebaseJson(config);
|
|
177
|
+
|
|
178
|
+
// ── Install dependencies ────────────────────────────────
|
|
179
|
+
const installDeps = (await question(
|
|
180
|
+
`\n${colors.blue}Install dependencies? ${colors.dim}[Y/n]${colors.reset}: `
|
|
181
|
+
)).trim().toLowerCase();
|
|
182
|
+
|
|
183
|
+
if (installDeps !== 'n') {
|
|
184
|
+
console.log(`\n${colors.dim}Installing dependencies...${colors.reset}`);
|
|
185
|
+
try {
|
|
186
|
+
execSync('npm install', {
|
|
187
|
+
cwd: path.join(REPO_ROOT, 'functions'),
|
|
188
|
+
stdio: 'inherit',
|
|
189
|
+
});
|
|
190
|
+
console.log(`${colors.green}Dependencies installed.${colors.reset}`);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.error(`${colors.red}Failed to install dependencies. Run 'npm install' in functions/ manually.${colors.reset}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ── Summary ─────────────────────────────────────────────
|
|
197
|
+
printSummary(config);
|
|
198
|
+
|
|
199
|
+
rl.close();
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ──────────────────────────────────────────────────────────
|
|
203
|
+
// File writers
|
|
204
|
+
// ──────────────────────────────────────────────────────────
|
|
205
|
+
|
|
206
|
+
function writeEnvFile(config) {
|
|
207
|
+
const envContent = `# ──────────────────────────────────────────────────────────
|
|
208
|
+
# Application
|
|
209
|
+
# ──────────────────────────────────────────────────────────
|
|
210
|
+
APP_NAME=${config.projectName}
|
|
211
|
+
APP_VERSION=1.0.0
|
|
212
|
+
NODE_ENV=${config.environment}
|
|
213
|
+
PORT=${config.port}
|
|
214
|
+
|
|
215
|
+
# ──────────────────────────────────────────────────────────
|
|
216
|
+
# Firebase
|
|
217
|
+
# ──────────────────────────────────────────────────────────
|
|
218
|
+
FIREBASE_PROJECT_ID=${config.firebaseProject}
|
|
219
|
+
|
|
220
|
+
# ──────────────────────────────────────────────────────────
|
|
221
|
+
# Database
|
|
222
|
+
# ──────────────────────────────────────────────────────────
|
|
223
|
+
MAIN_DATABASE_ID=${config.mainDatabaseId}
|
|
224
|
+
ANALYTICS_DATABASE_ID=${config.analyticsDatabaseId}
|
|
225
|
+
# DB_RETRY_ATTEMPTS=3
|
|
226
|
+
# DB_RETRY_DELAY=1000
|
|
227
|
+
# DB_TIMEOUT=10000
|
|
228
|
+
# DB_ENABLE_CACHE=true
|
|
229
|
+
|
|
230
|
+
# Uncomment for local development with emulator:
|
|
231
|
+
# FIRESTORE_EMULATOR_HOST=localhost:8080
|
|
232
|
+
|
|
233
|
+
# ──────────────────────────────────────────────────────────
|
|
234
|
+
# API
|
|
235
|
+
# ──────────────────────────────────────────────────────────
|
|
236
|
+
API_BASE_PATH=${config.apiBasePath}
|
|
237
|
+
CORS_ORIGINS=${config.corsOrigins}
|
|
238
|
+
REQUEST_SIZE_LIMIT=10mb
|
|
239
|
+
# ENABLE_SWAGGER=true
|
|
240
|
+
|
|
241
|
+
# ──────────────────────────────────────────────────────────
|
|
242
|
+
# Features
|
|
243
|
+
# ──────────────────────────────────────────────────────────
|
|
244
|
+
FEATURE_AUTHENTICATION=${config.enableAuth}
|
|
245
|
+
FEATURE_MULTI_TENANT=${config.enableMultiTenant}
|
|
246
|
+
FEATURE_FILE_UPLOADS=${config.enableFileUploads}
|
|
247
|
+
FEATURE_NOTIFICATIONS=${config.enableNotifications}
|
|
248
|
+
FEATURE_ANALYTICS=${config.enableAnalytics}
|
|
249
|
+
FEATURE_REALTIME=${config.enableRealtime}
|
|
250
|
+
|
|
251
|
+
# ──────────────────────────────────────────────────────────
|
|
252
|
+
# Rate Limiting
|
|
253
|
+
# ──────────────────────────────────────────────────────────
|
|
254
|
+
RATE_LIMIT_ENABLED=true
|
|
255
|
+
RATE_LIMIT_WINDOW_MS=900000
|
|
256
|
+
RATE_LIMIT_MAX=100
|
|
257
|
+
|
|
258
|
+
# ──────────────────────────────────────────────────────────
|
|
259
|
+
# Logging
|
|
260
|
+
# ──────────────────────────────────────────────────────────
|
|
261
|
+
LOG_LEVEL=info
|
|
262
|
+
|
|
263
|
+
# ──────────────────────────────────────────────────────────
|
|
264
|
+
# PII Encryption
|
|
265
|
+
# Required for AES-256-GCM PII encryption (hashValue/hashFields).
|
|
266
|
+
# Generate with: openssl rand -hex 32
|
|
267
|
+
# ──────────────────────────────────────────────────────────
|
|
268
|
+
PII_ENCRYPTION_KEY=
|
|
269
|
+
|
|
270
|
+
# ──────────────────────────────────────────────────────────
|
|
271
|
+
# JWT / Authentication
|
|
272
|
+
# ──────────────────────────────────────────────────────────
|
|
273
|
+
JWT_ISSUER=${config.projectName}
|
|
274
|
+
JWT_AUDIENCE=${config.projectName}-api
|
|
275
|
+
JWT_EXPIRES_IN=1h
|
|
276
|
+
JWT_REFRESH_EXPIRES_IN=7d
|
|
277
|
+
TOKEN_BLACKLIST_ENABLED=true
|
|
278
|
+
TOKEN_BLACKLIST_CLEANUP_INTERVAL=3600000
|
|
279
|
+
TOKEN_BLACKLIST_RETENTION_DAYS=30
|
|
280
|
+
|
|
281
|
+
# ──────────────────────────────────────────────────────────
|
|
282
|
+
# Integrations (uncomment and configure as needed)
|
|
283
|
+
# ──────────────────────────────────────────────────────────
|
|
284
|
+
# EMAIL_ENABLED=true
|
|
285
|
+
# EMAIL_PROVIDER=mailjet
|
|
286
|
+
# MAILJET_API_KEY=
|
|
287
|
+
# MAILJET_SECRET_KEY=
|
|
288
|
+
|
|
289
|
+
# SMS_ENABLED=true
|
|
290
|
+
# SMS_PROVIDER=twilio
|
|
291
|
+
# TWILIO_ACCOUNT_SID=
|
|
292
|
+
# TWILIO_AUTH_TOKEN=
|
|
293
|
+
# TWILIO_FROM_NUMBER=
|
|
294
|
+
|
|
295
|
+
# CRM_ENABLED=true
|
|
296
|
+
# CRM_PROVIDER=hubspot
|
|
297
|
+
# HUBSPOT_API_KEY=
|
|
298
|
+
|
|
299
|
+
# STRIPE_SECRET_KEY=
|
|
300
|
+
# STRIPE_WEBHOOK_SECRET=
|
|
301
|
+
|
|
302
|
+
# ──────────────────────────────────────────────────────────
|
|
303
|
+
# Cache (disabled by default)
|
|
304
|
+
# ──────────────────────────────────────────────────────────
|
|
305
|
+
# CACHE_ENABLED=false
|
|
306
|
+
# CACHE_DEFAULT_PROVIDER=memory
|
|
307
|
+
# CACHE_DEFAULT_TTL=300
|
|
308
|
+
# CACHE_NAMESPACE=v1
|
|
309
|
+
`;
|
|
310
|
+
|
|
311
|
+
const envPath = path.join(REPO_ROOT, 'functions', '.env');
|
|
312
|
+
fs.writeFileSync(envPath, envContent);
|
|
313
|
+
console.log(` ${colors.green}Created${colors.reset} functions/.env`);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function writeFirebaseRc(config) {
|
|
317
|
+
const firebaseRc = {
|
|
318
|
+
projects: {
|
|
319
|
+
default: config.firebaseProject,
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const rcPath = path.join(REPO_ROOT, '.firebaserc');
|
|
324
|
+
fs.writeFileSync(rcPath, JSON.stringify(firebaseRc, null, 2) + '\n');
|
|
325
|
+
console.log(` ${colors.green}Updated${colors.reset} .firebaserc`);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function updateFirebaseJson(config) {
|
|
329
|
+
const firebaseJsonPath = path.join(REPO_ROOT, 'firebase.json');
|
|
330
|
+
|
|
331
|
+
let firebaseJson;
|
|
332
|
+
try {
|
|
333
|
+
firebaseJson = JSON.parse(fs.readFileSync(firebaseJsonPath, 'utf8'));
|
|
334
|
+
} catch {
|
|
335
|
+
// Create a fresh firebase.json if it doesn't exist or is invalid
|
|
336
|
+
firebaseJson = {};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
firebaseJson.firestore = {
|
|
340
|
+
rules: 'firestore.rules',
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
firebaseJson.functions = {
|
|
344
|
+
source: 'functions',
|
|
345
|
+
runtime: 'nodejs22',
|
|
346
|
+
ignore: [
|
|
347
|
+
'node_modules',
|
|
348
|
+
'.git',
|
|
349
|
+
'firebase-debug.log',
|
|
350
|
+
'firebase-debug.*.log',
|
|
351
|
+
],
|
|
352
|
+
predeploy: [
|
|
353
|
+
'npm --prefix "$RESOURCE_DIR" run lint',
|
|
354
|
+
'npm --prefix "$RESOURCE_DIR" run build',
|
|
355
|
+
],
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
firebaseJson.emulators = {
|
|
359
|
+
functions: { port: parseInt(config.port, 10) },
|
|
360
|
+
firestore: { port: 8080 },
|
|
361
|
+
ui: { enabled: true, port: 4000 },
|
|
362
|
+
singleProjectMode: true,
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
fs.writeFileSync(firebaseJsonPath, JSON.stringify(firebaseJson, null, 2) + '\n');
|
|
366
|
+
console.log(` ${colors.green}Updated${colors.reset} firebase.json`);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// ──────────────────────────────────────────────────────────
|
|
370
|
+
// Summary
|
|
371
|
+
// ──────────────────────────────────────────────────────────
|
|
372
|
+
|
|
373
|
+
function printSummary(config) {
|
|
374
|
+
const features = [
|
|
375
|
+
config.enableAuth && 'Authentication',
|
|
376
|
+
config.enableMultiTenant && 'Multi-tenant',
|
|
377
|
+
config.enableFileUploads && 'File uploads',
|
|
378
|
+
config.enableNotifications && 'Notifications',
|
|
379
|
+
config.enableAnalytics && 'Analytics',
|
|
380
|
+
config.enableRealtime && 'Realtime',
|
|
381
|
+
].filter(Boolean);
|
|
382
|
+
|
|
383
|
+
console.log(`
|
|
384
|
+
${colors.green}${colors.bright}Setup complete!${colors.reset}
|
|
385
|
+
|
|
386
|
+
${colors.bright}Configuration:${colors.reset}
|
|
387
|
+
Project: ${config.projectName}
|
|
388
|
+
Firebase project: ${config.firebaseProject}
|
|
389
|
+
Environment: ${config.environment}
|
|
390
|
+
Mode: ${config.isMonoRepo ? 'Mono-repo' : 'Backend-only'}
|
|
391
|
+
Database: ${config.multiDB ? `Multi-DB (main: ${config.mainDatabaseId}, analytics: ${config.analyticsDatabaseId})` : 'Single (default)'}
|
|
392
|
+
Features: ${features.join(', ') || 'None'}
|
|
393
|
+
|
|
394
|
+
${colors.bright}Next steps:${colors.reset}
|
|
395
|
+
1. Review functions/.env and add integration keys as needed
|
|
396
|
+
2. Define your data model in __examples__/ (see existing examples)
|
|
397
|
+
3. Run ${colors.cyan}npm run generate __examples__/your-model.ts${colors.reset} to generate code
|
|
398
|
+
4. Register generated controllers in functions/src/index.ts
|
|
399
|
+
5. Run ${colors.cyan}npm run build${colors.reset} to compile TypeScript
|
|
400
|
+
6. Run ${colors.cyan}npm start${colors.reset} for local development`);
|
|
401
|
+
|
|
402
|
+
if (config.isMonoRepo) {
|
|
403
|
+
console.log(`
|
|
404
|
+
${colors.yellow}Mono-repo notes:${colors.reset}
|
|
405
|
+
- Ensure .claude/skills/ live at the mono-repo root (not inside functions/)
|
|
406
|
+
- The project root should have its own package.json
|
|
407
|
+
- The frontend typically lives in frontend/ (or similar)
|
|
408
|
+
- Backend source stays in functions/src/`);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
console.log(`
|
|
412
|
+
${colors.dim}Documentation: See __docs__/ for guides${colors.reset}
|
|
413
|
+
${colors.dim}Examples: See __examples__/ for sample data models${colors.reset}
|
|
414
|
+
`);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ──────────────────────────────────────────────────────────
|
|
418
|
+
// Run
|
|
419
|
+
// ──────────────────────────────────────────────────────────
|
|
420
|
+
|
|
421
|
+
setup().catch((error) => {
|
|
422
|
+
console.error(`\n${colors.red}Setup failed: ${error.message}${colors.reset}\n`);
|
|
423
|
+
rl.close();
|
|
424
|
+
process.exit(1);
|
|
425
|
+
});
|