@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.
Files changed (32) hide show
  1. package/bin/create-backend.js +3 -0
  2. package/lib/cli.d.ts +12 -0
  3. package/lib/cli.js +55 -0
  4. package/lib/cli.js.map +1 -0
  5. package/lib/commands/add-util.d.ts +9 -0
  6. package/lib/commands/add-util.js +119 -0
  7. package/lib/commands/add-util.js.map +1 -0
  8. package/lib/commands/init.d.ts +11 -0
  9. package/lib/commands/init.js +372 -0
  10. package/lib/commands/init.js.map +1 -0
  11. package/lib/commands/sync.d.ts +10 -0
  12. package/lib/commands/sync.js +161 -0
  13. package/lib/commands/sync.js.map +1 -0
  14. package/lib/utils-registry.d.ts +25 -0
  15. package/lib/utils-registry.js +187 -0
  16. package/lib/utils-registry.js.map +1 -0
  17. package/package.json +38 -0
  18. package/src/project-template/__examples__/README.md +559 -0
  19. package/src/project-template/__examples__/blog-platform.model.ts +528 -0
  20. package/src/project-template/__examples__/communications-usage.ts +175 -0
  21. package/src/project-template/__examples__/ecommerce-store.model.ts +1200 -0
  22. package/src/project-template/__examples__/saas-multi-tenant.model.ts +798 -0
  23. package/src/project-template/__examples__/user.model.ts +221 -0
  24. package/src/project-template/__scripts__/deploy.js +115 -0
  25. package/src/project-template/__scripts__/generate.js +122 -0
  26. package/src/project-template/__scripts__/setup.js +425 -0
  27. package/src/project-template/__scripts__/validate.js +325 -0
  28. package/src/project-template/firebase.json +32 -0
  29. package/src/project-template/firestore.rules +12 -0
  30. package/src/project-template/functions/jest.config.js +49 -0
  31. package/src/project-template/functions/src/index.ts +46 -0
  32. package/src/project-template/functions/tsconfig.json +38 -0
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Example Data Model: User Management
3
+ * This demonstrates how to define entities for code generation
4
+ */
5
+
6
+ import { DataModelSpecification } from '../functions/src/generator/types';
7
+
8
+ export const UserManagementModel: DataModelSpecification = {
9
+ entities: {
10
+ User: {
11
+ description: 'Application user with authentication and profile',
12
+
13
+ fields: {
14
+ userUID: {
15
+ type: 'uuid',
16
+ primaryKey: true,
17
+ generated: true,
18
+ description: 'Unique user identifier',
19
+ },
20
+ email: {
21
+ type: 'email',
22
+ unique: true,
23
+ required: true,
24
+ description: 'User email address',
25
+ },
26
+ displayName: {
27
+ type: 'string',
28
+ required: false,
29
+ maxLength: 100,
30
+ description: 'User display name',
31
+ },
32
+ role: {
33
+ type: 'enum',
34
+ values: ['admin', 'member', 'guest'],
35
+ default: 'member',
36
+ required: true,
37
+ description: 'User role for access control',
38
+ },
39
+ isActive: {
40
+ type: 'boolean',
41
+ default: true,
42
+ required: true,
43
+ description: 'Whether user account is active',
44
+ },
45
+ emailVerified: {
46
+ type: 'boolean',
47
+ default: false,
48
+ required: true,
49
+ description: 'Whether email has been verified',
50
+ },
51
+ lastLoginAt: {
52
+ type: 'timestamp',
53
+ nullable: true,
54
+ description: 'Last login timestamp',
55
+ },
56
+ profileImageUrl: {
57
+ type: 'url',
58
+ nullable: true,
59
+ description: 'URL to profile image',
60
+ },
61
+ },
62
+
63
+ relationships: {
64
+ organizations: {
65
+ type: 'many-to-many',
66
+ entity: 'Organization',
67
+ through: 'UserOrganization',
68
+ cascadeDelete: false,
69
+ description: 'Organizations the user belongs to',
70
+ },
71
+ createdLists: {
72
+ type: 'one-to-many',
73
+ entity: 'List',
74
+ foreignKey: 'ownerUID',
75
+ cascadeDelete: true,
76
+ description: 'Lists created by the user',
77
+ },
78
+ },
79
+
80
+ access: {
81
+ create: ['public'], // Anyone can register
82
+ read: ['self', 'admin', 'organization-member'],
83
+ update: ['self', 'admin'],
84
+ delete: ['admin'],
85
+ },
86
+
87
+ validation: {
88
+ email: 'Must be unique valid email address',
89
+ role: 'Members cannot promote themselves to admin',
90
+ displayName: 'Must be alphanumeric with spaces, max 100 characters',
91
+ },
92
+
93
+ indexes: [
94
+ { fields: ['email'], unique: true },
95
+ { fields: ['role', 'isActive'] },
96
+ { fields: ['createdAt'] },
97
+ ],
98
+
99
+ businessRules: [
100
+ 'Email must be verified before account activation',
101
+ 'Deleted users retain data for 30 days (soft delete)',
102
+ 'Admin role requires two-factor authentication',
103
+ 'Users must have at least one active organization',
104
+ ],
105
+ },
106
+
107
+ Organization: {
108
+ description: 'Organization or team entity',
109
+
110
+ fields: {
111
+ organizationUID: {
112
+ type: 'uuid',
113
+ primaryKey: true,
114
+ generated: true,
115
+ },
116
+ name: {
117
+ type: 'string',
118
+ required: true,
119
+ minLength: 2,
120
+ maxLength: 100,
121
+ },
122
+ slug: {
123
+ type: 'string',
124
+ unique: true,
125
+ required: true,
126
+ pattern: '^[a-z0-9-]+$',
127
+ },
128
+ description: {
129
+ type: 'string',
130
+ nullable: true,
131
+ maxLength: 500,
132
+ },
133
+ ownerUID: {
134
+ type: 'reference',
135
+ required: true,
136
+ description: 'Reference to User who owns this organization',
137
+ },
138
+ isActive: {
139
+ type: 'boolean',
140
+ default: true,
141
+ },
142
+ },
143
+
144
+ relationships: {
145
+ members: {
146
+ type: 'many-to-many',
147
+ entity: 'User',
148
+ through: 'UserOrganization',
149
+ cascadeDelete: false,
150
+ },
151
+ owner: {
152
+ type: 'many-to-one',
153
+ entity: 'User',
154
+ foreignKey: 'ownerUID',
155
+ },
156
+ },
157
+
158
+ access: {
159
+ create: ['authenticated'],
160
+ read: ['member', 'admin'],
161
+ update: ['owner', 'admin'],
162
+ delete: ['owner', 'admin'],
163
+ },
164
+
165
+ validation: {
166
+ slug: 'Must be unique URL-safe identifier',
167
+ name: 'Must be unique within user account',
168
+ },
169
+
170
+ indexes: [
171
+ { fields: ['slug'], unique: true },
172
+ { fields: ['ownerUID'] },
173
+ { fields: ['isActive'] },
174
+ ],
175
+
176
+ businessRules: [
177
+ 'Organization slug must be unique globally',
178
+ 'Owner cannot leave their own organization',
179
+ 'Deleting organization removes all member associations',
180
+ ],
181
+ },
182
+ },
183
+
184
+ workflows: {
185
+ userRegistration: {
186
+ trigger: 'USER_CREATED',
187
+ description: 'Handle new user registration',
188
+ steps: [
189
+ 'sendVerificationEmail',
190
+ 'createDefaultPreferences',
191
+ 'trackRegistrationEvent',
192
+ 'assignDefaultRole',
193
+ ],
194
+ conditions: {
195
+ sendVerificationEmail: 'email is not verified',
196
+ },
197
+ },
198
+
199
+ organizationCreation: {
200
+ trigger: 'ORGANIZATION_CREATED',
201
+ description: 'Handle new organization creation',
202
+ steps: [
203
+ 'addOwnerAsMember',
204
+ 'createDefaultSettings',
205
+ 'sendWelcomeEmail',
206
+ ],
207
+ },
208
+
209
+ accountDeletion: {
210
+ trigger: 'manual',
211
+ description: 'Handle user account deletion',
212
+ steps: [
213
+ 'validatePermissions',
214
+ 'exportUserData',
215
+ 'softDeleteAccount',
216
+ 'scheduleDataPurge',
217
+ 'notifyAdministrators',
218
+ ],
219
+ },
220
+ },
221
+ };
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Deployment Script
5
+ * Automates Firebase Functions deployment with safety checks
6
+ */
7
+
8
+ const { execSync } = require('child_process');
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+
12
+ const colors = {
13
+ reset: '\x1b[0m',
14
+ bright: '\x1b[1m',
15
+ green: '\x1b[32m',
16
+ blue: '\x1b[34m',
17
+ yellow: '\x1b[33m',
18
+ red: '\x1b[31m',
19
+ };
20
+
21
+ console.log(`
22
+ ${colors.bright}${colors.blue}╔════════════════════════════════════════════════════════════╗
23
+ ║ ║
24
+ ║ 🚀 Backend Boilerplate Deployment ║
25
+ ║ ║
26
+ ╚════════════════════════════════════════════════════════════╝${colors.reset}
27
+ `);
28
+
29
+ function runCommand(command, description) {
30
+ console.log(`${colors.blue}▶ ${description}...${colors.reset}`);
31
+ try {
32
+ execSync(command, {
33
+ cwd: path.join(__dirname, '../functions'),
34
+ stdio: 'inherit',
35
+ });
36
+ console.log(`${colors.green}✅ ${description} complete${colors.reset}\n`);
37
+ return true;
38
+ } catch (error) {
39
+ console.error(`${colors.red}❌ ${description} failed${colors.reset}\n`);
40
+ return false;
41
+ }
42
+ }
43
+
44
+ async function deploy() {
45
+ // Check environment
46
+ const envPath = path.join(__dirname, '../functions/.env');
47
+ if (!fs.existsSync(envPath)) {
48
+ console.error(`${colors.red}❌ .env file not found. Run 'npm run setup' first.${colors.reset}\n`);
49
+ process.exit(1);
50
+ }
51
+
52
+ // Run linter
53
+ if (!runCommand('npm run lint', 'Linting code')) {
54
+ console.error(`${colors.yellow}⚠️ Fix linting errors before deploying${colors.reset}\n`);
55
+ process.exit(1);
56
+ }
57
+
58
+ // Run build
59
+ if (!runCommand('npm run build', 'Building TypeScript')) {
60
+ console.error(`${colors.red}❌ Build failed. Cannot deploy.${colors.reset}\n`);
61
+ process.exit(1);
62
+ }
63
+
64
+ // Run tests (if they exist)
65
+ const testScript = path.join(__dirname, '../functions/package.json');
66
+ if (fs.existsSync(testScript)) {
67
+ const pkg = JSON.parse(fs.readFileSync(testScript, 'utf-8'));
68
+ if (pkg.scripts && pkg.scripts.test) {
69
+ console.log(`${colors.blue}▶ Running tests...${colors.reset}`);
70
+ try {
71
+ execSync('npm test', {
72
+ cwd: path.join(__dirname, '../functions'),
73
+ stdio: 'inherit',
74
+ });
75
+ console.log(`${colors.green}✅ Tests passed${colors.reset}\n`);
76
+ } catch (error) {
77
+ console.error(`${colors.red}❌ Tests failed${colors.reset}\n`);
78
+ const proceed = process.argv.includes('--force');
79
+ if (!proceed) {
80
+ console.log(`Use ${colors.yellow}--force${colors.reset} to deploy anyway\n`);
81
+ process.exit(1);
82
+ }
83
+ }
84
+ }
85
+ }
86
+
87
+ // Deploy to Firebase
88
+ console.log(`${colors.bright}${colors.blue}Deploying to Firebase...${colors.reset}\n`);
89
+
90
+ try {
91
+ execSync('firebase deploy --only functions', {
92
+ cwd: path.join(__dirname, '..'),
93
+ stdio: 'inherit',
94
+ });
95
+
96
+ console.log(`
97
+ ${colors.green}${colors.bright}✅ Deployment successful!${colors.reset}
98
+
99
+ ${colors.bright}Next steps:${colors.reset}
100
+ - Verify deployment in Firebase Console
101
+ - Test API endpoints
102
+ - Monitor logs with 'npm run logs'
103
+
104
+ ${colors.green}🎉 Your backend is live!${colors.reset}
105
+ `);
106
+ } catch (error) {
107
+ console.error(`\n${colors.red}❌ Deployment failed${colors.reset}\n`);
108
+ process.exit(1);
109
+ }
110
+ }
111
+
112
+ deploy().catch((error) => {
113
+ console.error(`\n${colors.red}❌ Error: ${error.message}${colors.reset}\n`);
114
+ process.exit(1);
115
+ });
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Code Generation Script
5
+ * Generates entities, repositories, services, and controllers from data model specifications
6
+ *
7
+ * Uses the code generator from @xbg/backend-core
8
+ */
9
+
10
+ const path = require('path');
11
+ const fs = require('fs');
12
+
13
+ // Colors for console output
14
+ const colors = {
15
+ reset: '\x1b[0m',
16
+ bright: '\x1b[1m',
17
+ green: '\x1b[32m',
18
+ blue: '\x1b[34m',
19
+ yellow: '\x1b[33m',
20
+ red: '\x1b[31m',
21
+ };
22
+
23
+ console.log(`
24
+ ${colors.bright}${colors.blue}╔════════════════════════════════════════════════════════════╗
25
+ ║ ║
26
+ ║ XBG Backend Code Generator ║
27
+ ║ ║
28
+ ╚════════════════════════════════════════════════════════════╝${colors.reset}
29
+ `);
30
+
31
+ // Import generator from @xbg/backend-core package
32
+ let createGenerator;
33
+ try {
34
+ ({ createGenerator } = require('@xbg/backend-core'));
35
+ } catch (error) {
36
+ console.error(`${colors.red}❌ @xbg/backend-core not installed. Run 'npm install' first.${colors.reset}`);
37
+ process.exit(1);
38
+ }
39
+
40
+ // Get model file from command line arguments
41
+ const modelFile = process.argv[2];
42
+
43
+ if (!modelFile) {
44
+ console.error(`${colors.red}❌ Usage: npm run generate <model-file>${colors.reset}`);
45
+ console.log(`\nExample: npm run generate __examples__/user.model.js\n`);
46
+ process.exit(1);
47
+ }
48
+
49
+ const modelPath = path.resolve(modelFile);
50
+
51
+ if (!fs.existsSync(modelPath)) {
52
+ console.error(`${colors.red}❌ Model file not found: ${modelPath}${colors.reset}\n`);
53
+ process.exit(1);
54
+ }
55
+
56
+ console.log(`${colors.blue}Loading model: ${modelPath}${colors.reset}\n`);
57
+
58
+ // Load the model
59
+ let model;
60
+ try {
61
+ if (modelPath.endsWith('.ts')) {
62
+ console.log(`${colors.yellow}TypeScript models need to be compiled first${colors.reset}`);
63
+ console.log(` Please create a .js version or use the compiled output\n`);
64
+ process.exit(1);
65
+ }
66
+
67
+ model = require(modelPath);
68
+
69
+ // Handle both default and named exports
70
+ const modelData = model.default || model.UserManagementModel || model;
71
+
72
+ if (!modelData || !modelData.entities) {
73
+ throw new Error('Invalid model format. Expected object with "entities" property');
74
+ }
75
+
76
+ console.log(`${colors.green}Model loaded successfully${colors.reset}\n`);
77
+ console.log(`${colors.bright}Entities found:${colors.reset}`);
78
+ Object.keys(modelData.entities).forEach((name) => {
79
+ console.log(` - ${name}`);
80
+ });
81
+ console.log('');
82
+
83
+ // Create generator - templates come from @xbg/backend-core package
84
+ const outputDir = path.join(__dirname, '../functions/src/generated');
85
+ const generator = createGenerator(outputDir);
86
+
87
+ // Generate code for each entity
88
+ console.log(`${colors.bright}${colors.blue}Generating code...${colors.reset}\n`);
89
+
90
+ for (const [entityName, spec] of Object.entries(modelData.entities)) {
91
+ console.log(`${colors.blue}Generating ${entityName}...${colors.reset}`);
92
+ generator.generateEntity(entityName, spec);
93
+ }
94
+
95
+ // Generate barrel exports
96
+ console.log('');
97
+ generator.generateBarrelExports();
98
+
99
+ console.log(`
100
+ ${colors.green}${colors.bright}Code generation complete!${colors.reset}
101
+
102
+ ${colors.bright}Generated files:${colors.reset}
103
+ - entities/ Entity classes with validation
104
+ - repositories/ Database access layer
105
+ - services/ Business logic layer
106
+ - controllers/ HTTP request handlers
107
+
108
+ ${colors.bright}Next steps:${colors.reset}
109
+ 1. Review generated code in functions/src/generated/
110
+ 2. Customize business logic in service classes
111
+ 3. Add custom routes in controller classes
112
+ 4. Update functions/src/index.ts to register controllers
113
+ 5. Run 'npm run build' to compile
114
+ 6. Run 'npm start' to test locally
115
+ `);
116
+ } catch (error) {
117
+ console.error(`\n${colors.red}Error: ${error.message}${colors.reset}\n`);
118
+ if (error.stack) {
119
+ console.error(error.stack);
120
+ }
121
+ process.exit(1);
122
+ }