bffgen 1.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/LICENSE +21 -0
- package/README.md +253 -0
- package/bin/bffgen.js +59 -0
- package/lib/index.js +142 -0
- package/package.json +63 -0
- package/scripts/install.js +211 -0
- package/scripts/platform.js +93 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 RichGod Usen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# bffgen
|
|
2
|
+
|
|
3
|
+
**Backend-for-Frontend (BFF) generator** - Scaffold secure, production-ready BFF services in **Go**, **Node.js (Express)**, or **Node.js (Fastify)** with JWT auth, rate limiting, CORS, and comprehensive logging.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/bffgen)
|
|
6
|
+
[](https://www.npmjs.com/package/bffgen)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## ā” Quick Start
|
|
12
|
+
|
|
13
|
+
### Using npx (No Installation)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Create Express BFF
|
|
17
|
+
npx bffgen init my-express-bff --lang nodejs-express
|
|
18
|
+
cd my-express-bff
|
|
19
|
+
npm install && npm run dev
|
|
20
|
+
|
|
21
|
+
# Create Fastify BFF
|
|
22
|
+
npx bffgen init my-fastify-bff --lang nodejs-fastify
|
|
23
|
+
|
|
24
|
+
# Create Go BFF
|
|
25
|
+
npx bffgen init my-go-bff --lang go --framework chi
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Global Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Install globally
|
|
32
|
+
npm install -g bffgen
|
|
33
|
+
|
|
34
|
+
# Use anywhere
|
|
35
|
+
bffgen init my-project --lang nodejs-express
|
|
36
|
+
cd my-project && npm run dev
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## ⨠Features
|
|
42
|
+
|
|
43
|
+
### š **Multi-Runtime Support**
|
|
44
|
+
|
|
45
|
+
- **Node.js Express** - Popular, flexible web framework
|
|
46
|
+
- **Node.js Fastify** - Fast, schema-based framework
|
|
47
|
+
- **Go (Chi/Echo/Fiber)** - High-performance, compiled servers
|
|
48
|
+
|
|
49
|
+
### š **Production-Ready Aggregation (v1.2.0)**
|
|
50
|
+
|
|
51
|
+
- **Parallel Service Calls** - Fetch from multiple backends simultaneously
|
|
52
|
+
- **Redis Caching** - Built-in caching with automatic fallback
|
|
53
|
+
- **Circuit Breakers** - Prevent cascade failures
|
|
54
|
+
- **Request Batching** - Avoid N+1 queries
|
|
55
|
+
- **Response Transformation** - Filter and optimize API responses
|
|
56
|
+
- **Field Selection** - GraphQL-like field filtering for REST
|
|
57
|
+
|
|
58
|
+
### š **Security Features**
|
|
59
|
+
|
|
60
|
+
- **JWT Authentication** - Token validation with user context
|
|
61
|
+
- **Rate Limiting** - Built-in for all runtimes
|
|
62
|
+
- **Security Headers** - Helmet, CSP, HSTS, XSS protection
|
|
63
|
+
- **CORS Configuration** - Restrictive origins, credentials support
|
|
64
|
+
|
|
65
|
+
### šØ **Developer Experience**
|
|
66
|
+
|
|
67
|
+
- **Interactive CLI** - Guided project setup
|
|
68
|
+
- **Template System** - Pre-built templates (auth, ecommerce, content)
|
|
69
|
+
- **Code Generation** - Auto-generate routes, controllers, services
|
|
70
|
+
- **Hot Reload** - Development mode with auto-restart
|
|
71
|
+
- **Comprehensive Tests** - Jest setup with sample tests
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## š ļø Commands
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
# Initialize new BFF project
|
|
79
|
+
bffgen init my-bff --lang nodejs-express
|
|
80
|
+
|
|
81
|
+
# Add route interactively
|
|
82
|
+
bffgen add-route
|
|
83
|
+
|
|
84
|
+
# Add template (auth, ecommerce, content)
|
|
85
|
+
bffgen add-template auth
|
|
86
|
+
|
|
87
|
+
# Generate routes, controllers, and services
|
|
88
|
+
bffgen generate
|
|
89
|
+
|
|
90
|
+
# Generate API documentation
|
|
91
|
+
bffgen generate-docs
|
|
92
|
+
|
|
93
|
+
# Create Postman collection
|
|
94
|
+
bffgen postman
|
|
95
|
+
|
|
96
|
+
# Health check
|
|
97
|
+
bffgen doctor
|
|
98
|
+
|
|
99
|
+
# Run development server (Go only)
|
|
100
|
+
bffgen dev
|
|
101
|
+
|
|
102
|
+
# Show version
|
|
103
|
+
bffgen version
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## š Examples
|
|
109
|
+
|
|
110
|
+
### Node.js Express Example
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Create project
|
|
114
|
+
npx bffgen init my-express-bff --lang nodejs-express
|
|
115
|
+
|
|
116
|
+
# Project structure:
|
|
117
|
+
my-express-bff/
|
|
118
|
+
āāā src/
|
|
119
|
+
ā āāā index.js # Express server
|
|
120
|
+
ā āāā routes/ # Route handlers
|
|
121
|
+
ā āāā controllers/ # Business logic with aggregation
|
|
122
|
+
ā āāā services/ # HTTP clients
|
|
123
|
+
ā āāā middleware/ # Auth, validation, logging
|
|
124
|
+
ā āāā utils/ # Aggregation utilities (NEW v1.2.0)
|
|
125
|
+
ā ā āāā aggregator.js # Parallel requests
|
|
126
|
+
ā ā āāā cache-manager.js # Redis caching
|
|
127
|
+
ā ā āāā circuit-breaker.js # Fault tolerance
|
|
128
|
+
ā ā āāā ...
|
|
129
|
+
ā āāā examples/ # Working aggregation examples
|
|
130
|
+
āāā tests/ # Jest tests
|
|
131
|
+
āāā docker-compose.yml # Redis setup
|
|
132
|
+
āāā package.json
|
|
133
|
+
āāā bffgen.config.json # BFF configuration
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Aggregation Example (v1.2.0)
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
const ParallelAggregator = require("./utils/aggregator");
|
|
140
|
+
const CacheManager = require("./utils/cache-manager");
|
|
141
|
+
|
|
142
|
+
const aggregator = new ParallelAggregator({ timeout: 5000 });
|
|
143
|
+
const cache = new CacheManager({ ttl: 300 });
|
|
144
|
+
|
|
145
|
+
// Fetch from multiple services in parallel
|
|
146
|
+
const results = await aggregator.fetchParallel([
|
|
147
|
+
{ name: "user", fetch: () => UserService.getProfile(userId) },
|
|
148
|
+
{ name: "orders", fetch: () => OrdersService.getOrders(userId) },
|
|
149
|
+
{ name: "preferences", fetch: () => PreferencesService.get(userId) },
|
|
150
|
+
]);
|
|
151
|
+
|
|
152
|
+
// Combine results with graceful degradation
|
|
153
|
+
const dashboard = {
|
|
154
|
+
user: results.find((r) => r.service === "user" && r.success)?.data,
|
|
155
|
+
orders: results.find((r) => r.service === "orders" && r.success)?.data || [],
|
|
156
|
+
preferences:
|
|
157
|
+
results.find((r) => r.service === "preferences" && r.success)?.data || {},
|
|
158
|
+
};
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## š§ Programmatic API
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
const bffgen = require("bffgen");
|
|
167
|
+
|
|
168
|
+
// Initialize project programmatically
|
|
169
|
+
await bffgen.init({
|
|
170
|
+
name: "my-project",
|
|
171
|
+
lang: "nodejs-express",
|
|
172
|
+
framework: "express",
|
|
173
|
+
skipTests: false,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Generate code
|
|
177
|
+
await bffgen.generate();
|
|
178
|
+
|
|
179
|
+
// Get version
|
|
180
|
+
const version = bffgen.getVersion();
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## š Documentation
|
|
186
|
+
|
|
187
|
+
- [Full Documentation](https://github.com/RichGod93/bffgen)
|
|
188
|
+
- [Node.js Aggregation Guide](https://github.com/RichGod93/bffgen/blob/main/docs/NODEJS_AGGREGATION.md)
|
|
189
|
+
- [Quick Reference](https://github.com/RichGod93/bffgen/blob/main/docs/QUICK_REFERENCE.md)
|
|
190
|
+
- [Examples](https://github.com/RichGod93/bffgen/tree/main/examples)
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## š Platform Support
|
|
195
|
+
|
|
196
|
+
Supported platforms:
|
|
197
|
+
|
|
198
|
+
- ā
macOS (Intel & Apple Silicon)
|
|
199
|
+
- ā
Linux (x64 & ARM64)
|
|
200
|
+
- ā
Windows (x64)
|
|
201
|
+
|
|
202
|
+
The appropriate binary for your platform is automatically downloaded during installation.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## š Troubleshooting
|
|
207
|
+
|
|
208
|
+
### Installation Issues
|
|
209
|
+
|
|
210
|
+
If installation fails:
|
|
211
|
+
|
|
212
|
+
1. **Check your internet connection**
|
|
213
|
+
2. **Clear npm cache:**
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
npm cache clean --force
|
|
217
|
+
npm install -g bffgen
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
3. **Manual installation:**
|
|
221
|
+
Download from [GitHub Releases](https://github.com/RichGod93/bffgen/releases)
|
|
222
|
+
|
|
223
|
+
### Platform Not Supported
|
|
224
|
+
|
|
225
|
+
If your platform isn't supported, you can:
|
|
226
|
+
|
|
227
|
+
- Install via Go: `go install github.com/RichGod93/bffgen/cmd/bffgen@latest`
|
|
228
|
+
- Build from source: Clone the repo and run `make build`
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## š¤ Contributing
|
|
233
|
+
|
|
234
|
+
Contributions are welcome! Please see the [Contributing Guide](https://github.com/RichGod93/bffgen/blob/main/CONTRIBUTING.md).
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## š License
|
|
239
|
+
|
|
240
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## š Links
|
|
245
|
+
|
|
246
|
+
- [GitHub Repository](https://github.com/RichGod93/bffgen)
|
|
247
|
+
- [Documentation](https://github.com/RichGod93/bffgen#readme)
|
|
248
|
+
- [Issue Tracker](https://github.com/RichGod93/bffgen/issues)
|
|
249
|
+
- [npm Package](https://www.npmjs.com/package/bffgen)
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
**Made with ā¤ļø for the Backend-for-Frontend pattern**
|
package/bin/bffgen.js
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* bffgen npm binary wrapper
|
|
5
|
+
* Executes the platform-specific downloaded binary
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { spawn } = require("child_process");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
const fs = require("fs");
|
|
11
|
+
const { getBinaryName } = require("../scripts/platform");
|
|
12
|
+
|
|
13
|
+
// Find binary
|
|
14
|
+
const binaryName = getBinaryName();
|
|
15
|
+
const binaryPath = path.join(__dirname, binaryName);
|
|
16
|
+
|
|
17
|
+
// Check if binary exists
|
|
18
|
+
if (!fs.existsSync(binaryPath)) {
|
|
19
|
+
console.error("ā bffgen binary not found!");
|
|
20
|
+
console.error(
|
|
21
|
+
"\nThe binary should have been downloaded during installation."
|
|
22
|
+
);
|
|
23
|
+
console.error("Try reinstalling:");
|
|
24
|
+
console.error(" npm install -g bffgen");
|
|
25
|
+
console.error("\nOr install via Go:");
|
|
26
|
+
console.error(" go install github.com/RichGod93/bffgen/cmd/bffgen@latest");
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Execute binary with all arguments
|
|
31
|
+
const args = process.argv.slice(2);
|
|
32
|
+
const child = spawn(binaryPath, args, {
|
|
33
|
+
stdio: "inherit",
|
|
34
|
+
cwd: process.cwd(),
|
|
35
|
+
env: process.env,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Handle exit
|
|
39
|
+
child.on("error", (error) => {
|
|
40
|
+
console.error("Failed to execute bffgen:", error.message);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
child.on("exit", (code, signal) => {
|
|
45
|
+
if (signal) {
|
|
46
|
+
process.kill(process.pid, signal);
|
|
47
|
+
} else {
|
|
48
|
+
process.exit(code || 0);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Handle signals
|
|
53
|
+
process.on("SIGINT", () => {
|
|
54
|
+
child.kill("SIGINT");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
process.on("SIGTERM", () => {
|
|
58
|
+
child.kill("SIGTERM");
|
|
59
|
+
});
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bffgen Programmatic API
|
|
3
|
+
* Use bffgen from JavaScript/TypeScript code
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const { execSync: nodeExecSync, spawn } = require("child_process");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const { getBinaryName } = require("../scripts/platform");
|
|
9
|
+
|
|
10
|
+
const BINARY_PATH = path.join(__dirname, "..", "bin", getBinaryName());
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Execute bffgen command synchronously
|
|
14
|
+
* @param {string[]} args - Command arguments
|
|
15
|
+
* @param {Object} options - Execution options
|
|
16
|
+
* @returns {Buffer} - Command output
|
|
17
|
+
*/
|
|
18
|
+
function execBffgenSync(args, options = {}) {
|
|
19
|
+
const command = `"${BINARY_PATH}" ${args.join(" ")}`;
|
|
20
|
+
return nodeExecSync(command, {
|
|
21
|
+
encoding: "utf8",
|
|
22
|
+
...options,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Execute bffgen command asynchronously
|
|
28
|
+
* @param {string[]} args - Command arguments
|
|
29
|
+
* @param {Object} options - Execution options
|
|
30
|
+
* @returns {Promise<string>} - Command output
|
|
31
|
+
*/
|
|
32
|
+
function exec(args, options = {}) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const child = spawn(BINARY_PATH, args, {
|
|
35
|
+
...options,
|
|
36
|
+
cwd: options.cwd || process.cwd(),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
let stdout = "";
|
|
40
|
+
let stderr = "";
|
|
41
|
+
|
|
42
|
+
if (child.stdout) {
|
|
43
|
+
child.stdout.on("data", (data) => {
|
|
44
|
+
stdout += data.toString();
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (child.stderr) {
|
|
49
|
+
child.stderr.on("data", (data) => {
|
|
50
|
+
stderr += data.toString();
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
child.on("error", reject);
|
|
55
|
+
|
|
56
|
+
child.on("exit", (code) => {
|
|
57
|
+
if (code !== 0) {
|
|
58
|
+
const error = new Error(`bffgen exited with code ${code}`);
|
|
59
|
+
error.code = code;
|
|
60
|
+
error.stderr = stderr;
|
|
61
|
+
error.stdout = stdout;
|
|
62
|
+
reject(error);
|
|
63
|
+
} else {
|
|
64
|
+
resolve(stdout);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Initialize a new BFF project
|
|
72
|
+
* @param {Object} options - Project options
|
|
73
|
+
* @param {string} options.name - Project name
|
|
74
|
+
* @param {string} options.lang - Language/runtime (go, nodejs-express, nodejs-fastify)
|
|
75
|
+
* @param {string} options.framework - Framework (chi, echo, fiber, express, fastify)
|
|
76
|
+
* @param {boolean} options.skipTests - Skip test generation
|
|
77
|
+
* @param {boolean} options.skipDocs - Skip documentation generation
|
|
78
|
+
* @returns {Promise<string>}
|
|
79
|
+
*/
|
|
80
|
+
async function init(options) {
|
|
81
|
+
const args = ["init", options.name];
|
|
82
|
+
|
|
83
|
+
if (options.lang) {
|
|
84
|
+
args.push("--lang", options.lang);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (options.framework) {
|
|
88
|
+
args.push("--framework", options.framework);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (options.skipTests) {
|
|
92
|
+
args.push("--skip-tests");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (options.skipDocs) {
|
|
96
|
+
args.push("--skip-docs");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return exec(args, { stdio: "inherit" });
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Generate code from configuration
|
|
104
|
+
* @param {Object} options - Generation options
|
|
105
|
+
* @returns {Promise<string>}
|
|
106
|
+
*/
|
|
107
|
+
async function generate(options = {}) {
|
|
108
|
+
const args = ["generate"];
|
|
109
|
+
|
|
110
|
+
if (options.check) {
|
|
111
|
+
args.push("--check");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (options.dryRun) {
|
|
115
|
+
args.push("--dry-run");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return exec(args, { stdio: "inherit" });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get bffgen version
|
|
123
|
+
* @returns {string}
|
|
124
|
+
*/
|
|
125
|
+
function getVersion() {
|
|
126
|
+
try {
|
|
127
|
+
const output = execBffgenSync(["version"]);
|
|
128
|
+
const match = output.match(/bffgen version (v?[\d.]+)/);
|
|
129
|
+
return match ? match[1] : require("../package.json").version;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
return require("../package.json").version;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = {
|
|
136
|
+
exec,
|
|
137
|
+
execSync: execBffgenSync,
|
|
138
|
+
init,
|
|
139
|
+
generate,
|
|
140
|
+
getVersion,
|
|
141
|
+
version: require("../package.json").version,
|
|
142
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bffgen",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Backend-for-Frontend (BFF) generator - Scaffold secure, production-ready BFF services in Go, Express, or Fastify",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"bffgen": "bin/bffgen.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"postinstall": "node scripts/install.js",
|
|
11
|
+
"test": "node bin/bffgen.js version"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"bff",
|
|
15
|
+
"backend-for-frontend",
|
|
16
|
+
"generator",
|
|
17
|
+
"cli",
|
|
18
|
+
"scaffold",
|
|
19
|
+
"scaffolding",
|
|
20
|
+
"express",
|
|
21
|
+
"expressjs",
|
|
22
|
+
"fastify",
|
|
23
|
+
"go",
|
|
24
|
+
"golang",
|
|
25
|
+
"microservices",
|
|
26
|
+
"api",
|
|
27
|
+
"rest",
|
|
28
|
+
"graphql",
|
|
29
|
+
"aggregation",
|
|
30
|
+
"proxy",
|
|
31
|
+
"gateway"
|
|
32
|
+
],
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/RichGod93/bffgen.git",
|
|
36
|
+
"directory": "npm"
|
|
37
|
+
},
|
|
38
|
+
"homepage": "https://github.com/RichGod93/bffgen#readme",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/RichGod93/bffgen/issues"
|
|
41
|
+
},
|
|
42
|
+
"author": "RichGod93",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=14.0.0"
|
|
46
|
+
},
|
|
47
|
+
"files": [
|
|
48
|
+
"bin",
|
|
49
|
+
"scripts",
|
|
50
|
+
"lib",
|
|
51
|
+
"README.md",
|
|
52
|
+
"LICENSE"
|
|
53
|
+
],
|
|
54
|
+
"os": [
|
|
55
|
+
"darwin",
|
|
56
|
+
"linux",
|
|
57
|
+
"win32"
|
|
58
|
+
],
|
|
59
|
+
"cpu": [
|
|
60
|
+
"x64",
|
|
61
|
+
"arm64"
|
|
62
|
+
]
|
|
63
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Post-install script for bffgen npm package
|
|
5
|
+
* Downloads the appropriate binary from GitHub Releases
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const https = require("https");
|
|
9
|
+
const fs = require("fs");
|
|
10
|
+
const path = require("path");
|
|
11
|
+
const crypto = require("crypto");
|
|
12
|
+
const {
|
|
13
|
+
getBinaryName,
|
|
14
|
+
getDownloadUrl,
|
|
15
|
+
getChecksumsUrl,
|
|
16
|
+
isSupported,
|
|
17
|
+
SUPPORTED_PLATFORMS,
|
|
18
|
+
} = require("./platform");
|
|
19
|
+
|
|
20
|
+
const PACKAGE_VERSION = require("../package.json").version;
|
|
21
|
+
const BIN_DIR = path.join(__dirname, "..", "bin");
|
|
22
|
+
const BINARY_PATH = path.join(BIN_DIR, getBinaryName());
|
|
23
|
+
|
|
24
|
+
// Colors for output
|
|
25
|
+
const colors = {
|
|
26
|
+
reset: "\x1b[0m",
|
|
27
|
+
blue: "\x1b[34m",
|
|
28
|
+
green: "\x1b[32m",
|
|
29
|
+
red: "\x1b[31m",
|
|
30
|
+
yellow: "\x1b[33m",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
function log(message, color = "reset") {
|
|
34
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Check if platform is supported
|
|
39
|
+
*/
|
|
40
|
+
function checkPlatform() {
|
|
41
|
+
if (!isSupported()) {
|
|
42
|
+
log("ā Platform not supported", "red");
|
|
43
|
+
log(`\nSupported platforms: ${SUPPORTED_PLATFORMS.join(", ")}`, "yellow");
|
|
44
|
+
log("\nš¦ Manual Installation:", "blue");
|
|
45
|
+
log(
|
|
46
|
+
` Download from: https://github.com/RichGod93/bffgen/releases/v${PACKAGE_VERSION}`
|
|
47
|
+
);
|
|
48
|
+
log(" Extract and add to PATH");
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Download file from URL
|
|
55
|
+
*/
|
|
56
|
+
function downloadFile(url) {
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
log(`š„ Downloading from: ${url}`, "blue");
|
|
59
|
+
|
|
60
|
+
https
|
|
61
|
+
.get(url, { timeout: 30000 }, (response) => {
|
|
62
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
63
|
+
// Follow redirect
|
|
64
|
+
https
|
|
65
|
+
.get(
|
|
66
|
+
response.headers.location,
|
|
67
|
+
{ timeout: 30000 },
|
|
68
|
+
(redirectResponse) => {
|
|
69
|
+
if (redirectResponse.statusCode !== 200) {
|
|
70
|
+
reject(
|
|
71
|
+
new Error(
|
|
72
|
+
`HTTP ${redirectResponse.statusCode}: ${redirectResponse.statusMessage}`
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const chunks = [];
|
|
79
|
+
redirectResponse.on("data", (chunk) => chunks.push(chunk));
|
|
80
|
+
redirectResponse.on("end", () =>
|
|
81
|
+
resolve(Buffer.concat(chunks))
|
|
82
|
+
);
|
|
83
|
+
redirectResponse.on("error", reject);
|
|
84
|
+
}
|
|
85
|
+
)
|
|
86
|
+
.on("error", reject);
|
|
87
|
+
} else if (response.statusCode === 200) {
|
|
88
|
+
const chunks = [];
|
|
89
|
+
response.on("data", (chunk) => chunks.push(chunk));
|
|
90
|
+
response.on("end", () => resolve(Buffer.concat(chunks)));
|
|
91
|
+
response.on("error", reject);
|
|
92
|
+
} else {
|
|
93
|
+
reject(
|
|
94
|
+
new Error(
|
|
95
|
+
`HTTP ${response.statusCode}: ${response.statusMessage}\nURL: ${url}`
|
|
96
|
+
)
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
})
|
|
100
|
+
.on("error", reject);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Verify binary checksum
|
|
106
|
+
*/
|
|
107
|
+
async function verifyChecksum(binaryBuffer, binaryName) {
|
|
108
|
+
try {
|
|
109
|
+
log("š Verifying checksum...", "blue");
|
|
110
|
+
|
|
111
|
+
const checksumsUrl = getChecksumsUrl(PACKAGE_VERSION);
|
|
112
|
+
const checksumsData = await downloadFile(checksumsUrl);
|
|
113
|
+
const checksumsText = checksumsData.toString("utf8");
|
|
114
|
+
|
|
115
|
+
// Find checksum for our binary
|
|
116
|
+
const lines = checksumsText.split("\n");
|
|
117
|
+
const checksumLine = lines.find((line) => line.includes(binaryName));
|
|
118
|
+
|
|
119
|
+
if (!checksumLine) {
|
|
120
|
+
log("ā ļø Warning: Checksum not found in checksums.txt", "yellow");
|
|
121
|
+
return true; // Continue anyway
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const expectedChecksum = checksumLine.split(/\s+/)[0];
|
|
125
|
+
|
|
126
|
+
// Calculate actual checksum
|
|
127
|
+
const hash = crypto.createHash("sha256");
|
|
128
|
+
hash.update(binaryBuffer);
|
|
129
|
+
const actualChecksum = hash.digest("hex");
|
|
130
|
+
|
|
131
|
+
if (expectedChecksum !== actualChecksum) {
|
|
132
|
+
log("ā Checksum verification failed!", "red");
|
|
133
|
+
log(` Expected: ${expectedChecksum}`, "red");
|
|
134
|
+
log(` Actual: ${actualChecksum}`, "red");
|
|
135
|
+
log("\nā ļø Binary may be corrupted. Try reinstalling:", "yellow");
|
|
136
|
+
log(" npm cache clean --force", "yellow");
|
|
137
|
+
log(" npm install -g bffgen", "yellow");
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
log("ā
Checksum verified", "green");
|
|
142
|
+
return true;
|
|
143
|
+
} catch (error) {
|
|
144
|
+
log(`ā ļø Warning: Could not verify checksum: ${error.message}`, "yellow");
|
|
145
|
+
return true; // Continue anyway
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Main installation process
|
|
151
|
+
*/
|
|
152
|
+
async function install() {
|
|
153
|
+
try {
|
|
154
|
+
log(`\nš¦ Installing bffgen v${PACKAGE_VERSION}...`, "blue");
|
|
155
|
+
|
|
156
|
+
// Check platform support
|
|
157
|
+
checkPlatform();
|
|
158
|
+
|
|
159
|
+
const binaryName = getBinaryName();
|
|
160
|
+
log(` Platform: ${binaryName}`, "blue");
|
|
161
|
+
|
|
162
|
+
// Create bin directory
|
|
163
|
+
if (!fs.existsSync(BIN_DIR)) {
|
|
164
|
+
fs.mkdirSync(BIN_DIR, { recursive: true });
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Download binary
|
|
168
|
+
const downloadUrl = getDownloadUrl(PACKAGE_VERSION);
|
|
169
|
+
const binaryBuffer = await downloadFile(downloadUrl);
|
|
170
|
+
|
|
171
|
+
// Verify checksum
|
|
172
|
+
const checksumValid = await verifyChecksum(binaryBuffer, binaryName);
|
|
173
|
+
if (!checksumValid) {
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Write binary file
|
|
178
|
+
fs.writeFileSync(BINARY_PATH, binaryBuffer);
|
|
179
|
+
|
|
180
|
+
// Make executable (Unix only)
|
|
181
|
+
if (process.platform !== "win32") {
|
|
182
|
+
fs.chmodSync(BINARY_PATH, 0o755);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
log("ā
bffgen installed successfully!", "green");
|
|
186
|
+
log("\nš Quick Start:", "blue");
|
|
187
|
+
log(" npx bffgen init my-project --lang nodejs-express", "blue");
|
|
188
|
+
log(" npx bffgen --help", "blue");
|
|
189
|
+
log("\nš Documentation: https://github.com/RichGod93/bffgen", "blue");
|
|
190
|
+
} catch (error) {
|
|
191
|
+
log("\nā Installation failed!", "red");
|
|
192
|
+
log(` Error: ${error.message}`, "red");
|
|
193
|
+
log("\nš¦ Manual Installation:", "yellow");
|
|
194
|
+
log(
|
|
195
|
+
` 1. Download from: https://github.com/RichGod93/bffgen/releases/v${PACKAGE_VERSION}`,
|
|
196
|
+
"yellow"
|
|
197
|
+
);
|
|
198
|
+
log(` 2. Extract the binary for your platform`, "yellow");
|
|
199
|
+
log(` 3. Add to your PATH`, "yellow");
|
|
200
|
+
log("\nš” Or install via Go:", "yellow");
|
|
201
|
+
log(
|
|
202
|
+
` go install github.com/RichGod93/bffgen/cmd/bffgen@v${PACKAGE_VERSION}`,
|
|
203
|
+
"yellow"
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Run installation
|
|
211
|
+
install();
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform Detection Utilities
|
|
3
|
+
* Maps Node.js platform/arch to Go binary names
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const os = require("os");
|
|
7
|
+
|
|
8
|
+
const PLATFORM_MAP = {
|
|
9
|
+
darwin: {
|
|
10
|
+
x64: "darwin-amd64",
|
|
11
|
+
arm64: "darwin-arm64",
|
|
12
|
+
},
|
|
13
|
+
linux: {
|
|
14
|
+
x64: "linux-amd64",
|
|
15
|
+
arm64: "linux-arm64",
|
|
16
|
+
},
|
|
17
|
+
win32: {
|
|
18
|
+
x64: "windows-amd64",
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const SUPPORTED_PLATFORMS = [
|
|
23
|
+
"darwin-x64",
|
|
24
|
+
"darwin-arm64",
|
|
25
|
+
"linux-x64",
|
|
26
|
+
"linux-arm64",
|
|
27
|
+
"win32-x64",
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Get current platform and architecture
|
|
32
|
+
*/
|
|
33
|
+
function getPlatform() {
|
|
34
|
+
const platform = os.platform();
|
|
35
|
+
const arch = os.arch();
|
|
36
|
+
return { platform, arch };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Map Node.js platform/arch to Go binary name
|
|
41
|
+
*/
|
|
42
|
+
function getBinaryName() {
|
|
43
|
+
const { platform, arch } = getPlatform();
|
|
44
|
+
|
|
45
|
+
if (!PLATFORM_MAP[platform] || !PLATFORM_MAP[platform][arch]) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Unsupported platform: ${platform}-${arch}\n` +
|
|
48
|
+
`Supported platforms: ${SUPPORTED_PLATFORMS.join(", ")}\n` +
|
|
49
|
+
`Please install manually from: https://github.com/RichGod93/bffgen/releases`
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const goPlatform = PLATFORM_MAP[platform][arch];
|
|
54
|
+
const ext = platform === "win32" ? ".exe" : "";
|
|
55
|
+
|
|
56
|
+
return `bffgen-${goPlatform}${ext}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get download URL for current platform
|
|
61
|
+
*/
|
|
62
|
+
function getDownloadUrl(version) {
|
|
63
|
+
const binaryName = getBinaryName();
|
|
64
|
+
return `https://github.com/RichGod93/bffgen/releases/download/v${version}/${binaryName}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get checksums URL for version
|
|
69
|
+
*/
|
|
70
|
+
function getChecksumsUrl(version) {
|
|
71
|
+
return `https://github.com/RichGod93/bffgen/releases/download/v${version}/checksums.txt`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Check if platform is supported
|
|
76
|
+
*/
|
|
77
|
+
function isSupported() {
|
|
78
|
+
try {
|
|
79
|
+
getBinaryName();
|
|
80
|
+
return true;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
module.exports = {
|
|
87
|
+
getPlatform,
|
|
88
|
+
getBinaryName,
|
|
89
|
+
getDownloadUrl,
|
|
90
|
+
getChecksumsUrl,
|
|
91
|
+
isSupported,
|
|
92
|
+
SUPPORTED_PLATFORMS,
|
|
93
|
+
};
|