navis.js 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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Syed Iman Ali
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.
22
+
package/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # Navis.js
2
+
3
+ A lightweight, serverless-first, microservice API framework designed for AWS Lambda and Node.js.
4
+
5
+ ## Philosophy
6
+
7
+ Navis.js is "Express for serverless microservices — but simpler."
8
+
9
+ - **Extremely lightweight** - Zero or minimal dependencies
10
+ - **Serverless-first** - Built for AWS Lambda
11
+ - **Microservice-friendly** - API-to-API communication made easy
12
+ - **Simple & readable** - No magic abstractions
13
+
14
+ ## Installation
15
+
16
+ ### Via npm
17
+
18
+ ```bash
19
+ npm install navis.js
20
+ ```
21
+
22
+ ### From GitHub
23
+
24
+ ```bash
25
+ # Clone the repository
26
+ git clone https://github.com/mafhh14/navis.js.git
27
+ cd navis.js
28
+
29
+ # Install dependencies (if any)
30
+ npm install
31
+
32
+ # Link CLI locally for development
33
+ npm link
34
+ ```
35
+
36
+ ## Quick Start
37
+
38
+ ### Node.js HTTP Server
39
+
40
+ ```javascript
41
+ const { NavisApp, response } = require('navis.js');
42
+
43
+ const app = new NavisApp();
44
+
45
+ app.get('/', (req, res) => {
46
+ response.success(res, { message: 'Hello Navis.js!' });
47
+ });
48
+
49
+ app.listen(3000);
50
+ ```
51
+
52
+ ### AWS Lambda
53
+
54
+ ```javascript
55
+ const { NavisApp } = require('navis.js');
56
+
57
+ const app = new NavisApp();
58
+
59
+ app.get('/hello', (req, res) => {
60
+ res.statusCode = 200;
61
+ res.body = { message: 'Hello from Lambda!' };
62
+ });
63
+
64
+ exports.handler = async (event) => {
65
+ return await app.handleLambda(event);
66
+ };
67
+ ```
68
+
69
+ ## CLI
70
+
71
+ ```bash
72
+ # Start example server
73
+ navis start
74
+ ```
75
+
76
+ ## Features
77
+
78
+ ### v1 (Current)
79
+
80
+ - ✅ HTTP routing (GET, POST, PUT, DELETE)
81
+ - ✅ Middleware support (`app.use()`)
82
+ - ✅ Unified handler for Node.js and AWS Lambda
83
+ - ✅ Simple path-based routing
84
+ - ✅ ServiceClient for service-to-service calls
85
+ - ✅ Timeout support
86
+
87
+ ### v2 (Planned)
88
+
89
+ - 🔄 Retry logic
90
+ - 🔄 Circuit breaker
91
+ - 🔄 Config-based services
92
+ - 🔄 Service discovery
93
+ - 🔄 Async messaging (SQS / Kafka / NATS)
94
+ - 🔄 CLI generators (`navis generate service`)
95
+
96
+ ## API Reference
97
+
98
+ ### NavisApp
99
+
100
+ #### Methods
101
+
102
+ - `app.use(fn)` - Register middleware
103
+ - `app.get(path, handler)` - Register GET route
104
+ - `app.post(path, handler)` - Register POST route
105
+ - `app.put(path, handler)` - Register PUT route
106
+ - `app.delete(path, handler)` - Register DELETE route
107
+ - `app.listen(port, callback)` - Start HTTP server (Node.js)
108
+ - `app.handleLambda(event)` - Handle AWS Lambda event
109
+
110
+ ### ServiceClient
111
+
112
+ const { ServiceClient } = require('navis.js');
113
+
114
+ const client = new ServiceClient('http://api.example.com', {
115
+ timeout: 5000, // milliseconds
116
+ });
117
+
118
+ // GET request
119
+ const response = await client.get('/users');
120
+
121
+ // POST request
122
+ const result = await client.post('/users', { name: 'John' });
123
+ ```
124
+
125
+ ### Response Helpers
126
+
127
+ ```javascript
128
+ const { response } = require('navis.js');
129
+
130
+ // Success response
131
+ response.success(res, { data: 'value' }, 200);
132
+
133
+ // Error response
134
+ response.error(res, 'Error message', 500);
135
+ ```
136
+
137
+ ## Examples
138
+
139
+ See the `examples/` directory:
140
+
141
+ - `server.js` - Node.js HTTP server example
142
+ - `lambda.js` - AWS Lambda handler example
143
+ - `service-client-demo.js` - ServiceClient usage example
144
+
145
+ ## Roadmap
146
+
147
+ ### v1 (Current)
148
+ Core functionality: routing, middleware, Lambda support, ServiceClient
149
+
150
+ ### v2 (Next)
151
+ Resilience patterns: retry, circuit breaker, service discovery
152
+
153
+ ### v3 (Future)
154
+ Advanced features: async messaging, observability, advanced CLI
155
+
156
+ ## License
157
+
158
+ MIT
package/bin/navis.js ADDED
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Navis.js CLI
3
+ * v1: Basic start command
4
+ */
5
+
6
+ const { spawn } = require('child_process');
7
+ const path = require('path');
8
+ const fs = require('fs');
9
+
10
+ const command = process.argv[2];
11
+
12
+ if (command === 'start') {
13
+ // Check if examples/server.js exists
14
+ const serverPath = path.join(__dirname, '..', 'examples', 'server.js');
15
+
16
+ if (!fs.existsSync(serverPath)) {
17
+ console.error('Error: examples/server.js not found');
18
+ process.exit(1);
19
+ }
20
+
21
+ // Run the server
22
+ const server = spawn('node', [serverPath], {
23
+ stdio: 'inherit',
24
+ cwd: path.join(__dirname, '..'),
25
+ });
26
+
27
+ server.on('error', (err) => {
28
+ console.error('Error starting server:', err);
29
+ process.exit(1);
30
+ });
31
+
32
+ server.on('exit', (code) => {
33
+ process.exit(code);
34
+ });
35
+ } else if (command === 'generate') {
36
+ // TODO v2: Implement navis generate service
37
+ console.log('Generator commands coming in v2');
38
+ } else {
39
+ console.log('Navis.js CLI');
40
+ console.log('');
41
+ console.log('Usage:');
42
+ console.log(' navis start Start the example server');
43
+ console.log('');
44
+ console.log('v2 commands:');
45
+ console.log(' navis generate Generate service boilerplate');
46
+ }
@@ -0,0 +1,30 @@
1
+ const { NavisApp } = require('../src/index');
2
+
3
+ const app = new NavisApp();
4
+
5
+ // Middleware example
6
+ app.use((req, res, next) => {
7
+ console.log(`Lambda: ${req.method} ${req.path}`);
8
+ next();
9
+ });
10
+
11
+ // Routes
12
+ app.get('/', (req, res) => {
13
+ res.statusCode = 200;
14
+ res.body = { message: 'Welcome to Navis.js Lambda!' };
15
+ });
16
+
17
+ app.get('/health', (req, res) => {
18
+ res.statusCode = 200;
19
+ res.body = { status: 'ok' };
20
+ });
21
+
22
+ app.post('/echo', (req, res) => {
23
+ res.statusCode = 200;
24
+ res.body = { echo: req.body };
25
+ });
26
+
27
+ // Lambda handler
28
+ exports.handler = async (event) => {
29
+ return await app.handleLambda(event);
30
+ };
@@ -0,0 +1,36 @@
1
+ const { NavisApp, response } = require('../src/index');
2
+
3
+ const app = new NavisApp();
4
+
5
+ // Middleware example
6
+ app.use((req, res, next) => {
7
+ console.log(`${req.method} ${req.url}`);
8
+ next();
9
+ });
10
+
11
+ // Routes
12
+ app.get('/', (req, res) => {
13
+ response.success(res, { message: 'Welcome to Navis.js!' });
14
+ });
15
+
16
+ app.get('/health', (req, res) => {
17
+ response.success(res, { status: 'ok' });
18
+ });
19
+
20
+ app.post('/echo', (req, res) => {
21
+ // In Node.js, we need to parse body manually (v1 simplicity)
22
+ let body = '';
23
+ req.on('data', (chunk) => {
24
+ body += chunk.toString();
25
+ });
26
+ req.on('end', () => {
27
+ const data = body ? JSON.parse(body) : {};
28
+ response.success(res, { echo: data });
29
+ });
30
+ });
31
+
32
+ // Start server
33
+ const PORT = process.env.PORT || 3000;
34
+ app.listen(PORT, () => {
35
+ console.log(`Example server running on http://localhost:${PORT}`);
36
+ });
@@ -0,0 +1,27 @@
1
+ const { ServiceClient } = require('../src/index');
2
+
3
+ // Example: Call another microservice
4
+ async function demo() {
5
+ const client = new ServiceClient('http://localhost:3000', {
6
+ timeout: 3000,
7
+ });
8
+
9
+ try {
10
+ // GET request
11
+ const healthCheck = await client.get('/health');
12
+ console.log('Health check:', healthCheck);
13
+
14
+ // POST request
15
+ const echo = await client.post('/echo', { message: 'Hello from ServiceClient!' });
16
+ console.log('Echo:', echo);
17
+ } catch (err) {
18
+ console.error('Service call failed:', err.message);
19
+ }
20
+ }
21
+
22
+ // Run demo if called directly
23
+ if (require.main === module) {
24
+ demo();
25
+ }
26
+
27
+ module.exports = { demo };
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "navis.js",
3
+ "version": "1.0.0",
4
+ "description": "A lightweight, serverless-first, microservice API framework designed for AWS Lambda and Node.js",
5
+ "main": "src/index.js",
6
+ "bin": {
7
+ "navis": "bin/navis.js"
8
+ },
9
+ "files": [
10
+ "src/",
11
+ "bin/",
12
+ "examples/",
13
+ "README.md",
14
+ "LICENSE"
15
+ ],
16
+ "scripts": {
17
+ "test": "echo \"Error: no test specified\" && exit 1",
18
+ "start": "node examples/server.js"
19
+ },
20
+ "keywords": [
21
+ "serverless",
22
+ "lambda",
23
+ "aws-lambda",
24
+ "microservice",
25
+ "api",
26
+ "framework",
27
+ "express",
28
+ "http",
29
+ "routing",
30
+ "middleware",
31
+ "nodejs"
32
+ ],
33
+ "author": "Syed Iman Ali",
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/mafhh14/navis.js.git"
38
+ },
39
+ "bugs": {
40
+ "url": "https://github.com/mafhh14/navis.js/issues"
41
+ },
42
+ "homepage": "https://github.com/mafhh14/navis.js#readme",
43
+ "engines": {
44
+ "node": ">=14.0.0"
45
+ }
46
+ }
@@ -0,0 +1,177 @@
1
+ const http = require('http');
2
+ const Router = require('./router');
3
+ const { executeMiddleware } = require('./middleware');
4
+ const { error: errorResponse } = require('../utils/response');
5
+
6
+ /**
7
+ * NavisApp - Main application class
8
+ */
9
+ class NavisApp {
10
+ constructor() {
11
+ this.router = new Router();
12
+ this.middlewares = [];
13
+ this.server = null;
14
+ }
15
+
16
+ /**
17
+ * Register middleware
18
+ * @param {Function} fn - Middleware function (req, res, next)
19
+ */
20
+ use(fn) {
21
+ this.middlewares.push(fn);
22
+ }
23
+
24
+ /**
25
+ * Register GET route
26
+ */
27
+ get(path, handler) {
28
+ this.router.get(path, handler);
29
+ }
30
+
31
+ /**
32
+ * Register POST route
33
+ */
34
+ post(path, handler) {
35
+ this.router.post(path, handler);
36
+ }
37
+
38
+ /**
39
+ * Register PUT route
40
+ */
41
+ put(path, handler) {
42
+ this.router.put(path, handler);
43
+ }
44
+
45
+ /**
46
+ * Register DELETE route
47
+ */
48
+ delete(path, handler) {
49
+ this.router.delete(path, handler);
50
+ }
51
+
52
+ /**
53
+ * Handle HTTP request (Node.js)
54
+ * @param {Object} req - Node.js HTTP request
55
+ * @param {Object} res - Node.js HTTP response
56
+ */
57
+ async handleRequest(req, res) {
58
+ const method = req.method;
59
+ const url = new URL(req.url, `http://${req.headers.host}`);
60
+ const path = url.pathname;
61
+
62
+ // Find route handler
63
+ const handler = this.router.find(method, path);
64
+
65
+ if (!handler) {
66
+ errorResponse(res, 'Not Found', 404);
67
+ return;
68
+ }
69
+
70
+ // Execute middleware chain, then route handler
71
+ try {
72
+ await executeMiddleware(
73
+ this.middlewares,
74
+ req,
75
+ res,
76
+ handler,
77
+ false
78
+ );
79
+ } catch (err) {
80
+ errorResponse(res, err.message || 'Internal Server Error', 500);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Handle AWS Lambda event
86
+ * @param {Object} event - Lambda event
87
+ * @returns {Object} - Lambda response
88
+ */
89
+ async handleLambda(event) {
90
+ const method = event.httpMethod || event.requestContext?.http?.method || 'GET';
91
+ const path = event.path || event.rawPath || '/';
92
+
93
+ // Find route handler
94
+ const handler = this.router.find(method, path);
95
+
96
+ if (!handler) {
97
+ return {
98
+ statusCode: 404,
99
+ headers: {
100
+ 'Content-Type': 'application/json',
101
+ },
102
+ body: JSON.stringify({ error: 'Not Found' }),
103
+ };
104
+ }
105
+
106
+ // Create Lambda-compatible req/res objects
107
+ const req = {
108
+ method,
109
+ path,
110
+ headers: event.headers || {},
111
+ body: event.body ? JSON.parse(event.body) : {},
112
+ query: event.queryStringParameters || {},
113
+ // Store original event for advanced use cases
114
+ event,
115
+ };
116
+
117
+ const res = {
118
+ statusCode: 200,
119
+ headers: {},
120
+ body: null,
121
+ };
122
+
123
+ // Execute middleware chain, then route handler
124
+ try {
125
+ const result = await executeMiddleware(
126
+ this.middlewares,
127
+ req,
128
+ res,
129
+ handler,
130
+ true
131
+ );
132
+
133
+ // If handler returned a Lambda response directly, use it
134
+ if (result && result.statusCode) {
135
+ return result;
136
+ }
137
+
138
+ // Otherwise, construct response from res object
139
+ return {
140
+ statusCode: res.statusCode || 200,
141
+ headers: {
142
+ 'Content-Type': 'application/json',
143
+ ...res.headers,
144
+ },
145
+ body: res.body ? JSON.stringify(res.body) : JSON.stringify(result || {}),
146
+ };
147
+ } catch (err) {
148
+ return {
149
+ statusCode: 500,
150
+ headers: {
151
+ 'Content-Type': 'application/json',
152
+ },
153
+ body: JSON.stringify({ error: err.message || 'Internal Server Error' }),
154
+ };
155
+ }
156
+ }
157
+
158
+ /**
159
+ * Start HTTP server (Node.js)
160
+ * @param {number} port - Port number
161
+ * @param {Function} callback - Optional callback
162
+ */
163
+ listen(port = 3000, callback) {
164
+ this.server = http.createServer((req, res) => {
165
+ this.handleRequest(req, res);
166
+ });
167
+
168
+ this.server.listen(port, () => {
169
+ if (callback) callback();
170
+ console.log(`Navis.js server listening on port ${port}`);
171
+ });
172
+
173
+ return this.server;
174
+ }
175
+ }
176
+
177
+ module.exports = NavisApp;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Middleware engine for sequential execution
3
+ */
4
+
5
+ /**
6
+ * Execute middleware chain sequentially
7
+ * @param {Array} middlewares - Array of middleware functions
8
+ * @param {Object} req - Request object
9
+ * @param {Object} res - Response object
10
+ * @param {Function} finalHandler - Final route handler
11
+ * @param {boolean} isLambda - Whether this is a Lambda invocation
12
+ */
13
+ async function executeMiddleware(middlewares, req, res, finalHandler, isLambda = false) {
14
+ let index = 0;
15
+
16
+ const next = async () => {
17
+ if (index >= middlewares.length) {
18
+ // All middleware executed, run final handler
19
+ if (finalHandler) {
20
+ return await finalHandler(req, res);
21
+ }
22
+ return;
23
+ }
24
+
25
+ const middleware = middlewares[index++];
26
+ try {
27
+ await middleware(req, res, next);
28
+ } catch (err) {
29
+ // Error in middleware - stop chain
30
+ throw err;
31
+ }
32
+ };
33
+
34
+ return await next();
35
+ }
36
+
37
+ module.exports = {
38
+ executeMiddleware,
39
+ };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Simple path-based router
3
+ * v1: No regex, no params (keep it simple)
4
+ */
5
+
6
+ class Router {
7
+ constructor() {
8
+ this.routes = {
9
+ GET: {},
10
+ POST: {},
11
+ PUT: {},
12
+ DELETE: {},
13
+ };
14
+ }
15
+
16
+ /**
17
+ * Register a route handler
18
+ * @param {string} method - HTTP method
19
+ * @param {string} path - Route path (exact match only in v1)
20
+ * @param {Function} handler - Route handler function
21
+ */
22
+ register(method, path, handler) {
23
+ const normalizedMethod = method.toUpperCase();
24
+ if (!this.routes[normalizedMethod]) {
25
+ throw new Error(`Unsupported HTTP method: ${method}`);
26
+ }
27
+ this.routes[normalizedMethod][path] = handler;
28
+ }
29
+
30
+ /**
31
+ * Get route handler for a method and path
32
+ * @param {string} method - HTTP method
33
+ * @param {string} path - Route path
34
+ * @returns {Function|null} - Route handler or null if not found
35
+ */
36
+ find(method, path) {
37
+ const normalizedMethod = method.toUpperCase();
38
+ const methodRoutes = this.routes[normalizedMethod] || {};
39
+ return methodRoutes[path] || null;
40
+ }
41
+
42
+ /**
43
+ * Register GET route
44
+ */
45
+ get(path, handler) {
46
+ this.register('GET', path, handler);
47
+ }
48
+
49
+ /**
50
+ * Register POST route
51
+ */
52
+ post(path, handler) {
53
+ this.register('POST', path, handler);
54
+ }
55
+
56
+ /**
57
+ * Register PUT route
58
+ */
59
+ put(path, handler) {
60
+ this.register('PUT', path, handler);
61
+ }
62
+
63
+ /**
64
+ * Register DELETE route
65
+ */
66
+ delete(path, handler) {
67
+ this.register('DELETE', path, handler);
68
+ }
69
+ }
70
+
71
+ module.exports = Router;
package/src/index.js ADDED
@@ -0,0 +1,12 @@
1
+ const NavisApp = require('./core/app');
2
+ const ServiceClient = require('./utils/service-client');
3
+ const { success, error } = require('./utils/response');
4
+
5
+ module.exports = {
6
+ NavisApp,
7
+ ServiceClient,
8
+ response: {
9
+ success,
10
+ error,
11
+ },
12
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Unified response helper for Node.js HTTP and AWS Lambda
3
+ */
4
+
5
+ /**
6
+ * Send a unified response
7
+ * @param {Object} context - Response context (res for Node.js, or Lambda context)
8
+ * @param {number} statusCode - HTTP status code
9
+ * @param {Object|string} data - Response data
10
+ * @param {boolean} isLambda - Whether this is a Lambda invocation
11
+ */
12
+ function sendResponse(context, statusCode, data, isLambda = false) {
13
+ const body = typeof data === 'string' ? data : JSON.stringify(data);
14
+
15
+ if (isLambda) {
16
+ // Lambda response format
17
+ return {
18
+ statusCode,
19
+ headers: {
20
+ 'Content-Type': 'application/json',
21
+ },
22
+ body,
23
+ };
24
+ } else {
25
+ // Node.js HTTP response
26
+ context.writeHead(statusCode, {
27
+ 'Content-Type': 'application/json',
28
+ });
29
+ context.end(body);
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Success response helper
35
+ */
36
+ function success(context, data, statusCode = 200, isLambda = false) {
37
+ return sendResponse(context, statusCode, data, isLambda);
38
+ }
39
+
40
+ /**
41
+ * Error response helper
42
+ */
43
+ function error(context, message, statusCode = 500, isLambda = false) {
44
+ return sendResponse(
45
+ context,
46
+ statusCode,
47
+ { error: message },
48
+ isLambda
49
+ );
50
+ }
51
+
52
+ module.exports = {
53
+ sendResponse,
54
+ success,
55
+ error,
56
+ };
@@ -0,0 +1,105 @@
1
+ const http = require('http');
2
+ const https = require('https');
3
+
4
+ /**
5
+ * ServiceClient - Lightweight HTTP client for service-to-service calls
6
+ * v1: Basic get() and post() with timeout support
7
+ */
8
+
9
+ class ServiceClient {
10
+ constructor(baseUrl, options = {}) {
11
+ this.baseUrl = baseUrl;
12
+ this.timeout = options.timeout || 5000; // Default 5s timeout
13
+ // TODO v2: Add retry logic
14
+ // TODO v2: Add circuit breaker
15
+ // TODO v2: Add config-based services
16
+ }
17
+
18
+ /**
19
+ * Make HTTP request
20
+ * @private
21
+ */
22
+ _request(method, path, data = null, options = {}) {
23
+ return new Promise((resolve, reject) => {
24
+ const url = new URL(path, this.baseUrl);
25
+ const isHttps = url.protocol === 'https:';
26
+ const client = isHttps ? https : http;
27
+
28
+ const requestOptions = {
29
+ hostname: url.hostname,
30
+ port: url.port || (isHttps ? 443 : 80),
31
+ path: url.pathname + url.search,
32
+ method,
33
+ headers: {
34
+ 'Content-Type': 'application/json',
35
+ ...options.headers,
36
+ },
37
+ timeout: options.timeout || this.timeout,
38
+ };
39
+
40
+ const req = client.request(requestOptions, (res) => {
41
+ let body = '';
42
+
43
+ res.on('data', (chunk) => {
44
+ body += chunk;
45
+ });
46
+
47
+ res.on('end', () => {
48
+ try {
49
+ const parsedBody = body ? JSON.parse(body) : {};
50
+ resolve({
51
+ statusCode: res.statusCode,
52
+ headers: res.headers,
53
+ data: parsedBody,
54
+ });
55
+ } catch (err) {
56
+ resolve({
57
+ statusCode: res.statusCode,
58
+ headers: res.headers,
59
+ data: body,
60
+ });
61
+ }
62
+ });
63
+ });
64
+
65
+ req.on('error', (err) => {
66
+ reject(err);
67
+ });
68
+
69
+ req.on('timeout', () => {
70
+ req.destroy();
71
+ reject(new Error('Request timeout'));
72
+ });
73
+
74
+ if (data) {
75
+ req.write(JSON.stringify(data));
76
+ }
77
+
78
+ req.end();
79
+ });
80
+ }
81
+
82
+ /**
83
+ * GET request
84
+ * @param {string} path - Request path
85
+ * @param {Object} options - Request options
86
+ */
87
+ async get(path, options = {}) {
88
+ return this._request('GET', path, null, options);
89
+ }
90
+
91
+ /**
92
+ * POST request
93
+ * @param {string} path - Request path
94
+ * @param {Object} data - Request body data
95
+ * @param {Object} options - Request options
96
+ */
97
+ async post(path, data, options = {}) {
98
+ return this._request('POST', path, data, options);
99
+ }
100
+
101
+ // TODO v2: Add PUT, DELETE, PATCH methods
102
+ // TODO v2: Add service discovery integration
103
+ }
104
+
105
+ module.exports = ServiceClient;