roster-server 1.9.2 β†’ 1.9.4

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 CHANGED
@@ -166,6 +166,8 @@ When creating a new `RosterServer` instance, you can pass the following options:
166
166
  - `greenlockStorePath` (string): Directory for Greenlock configuration.
167
167
  - `staging` (boolean): Set to `true` to use Let's Encrypt's staging environment (for testing).
168
168
  - `local` (boolean): Set to `true` to run in local development mode.
169
+ - `minLocalPort` (number): Minimum port for local mode (default: 4000).
170
+ - `maxLocalPort` (number): Maximum port for local mode (default: 9999).
169
171
 
170
172
  ## 🏠 Local Development Mode
171
173
 
@@ -178,19 +180,84 @@ When `{ local: true }` is enabled, RosterServer **Skips SSL/HTTPS**: Runs pure H
178
180
  ```javascript
179
181
  const server = new Roster({
180
182
  wwwPath: '/srv/www',
181
- local: true // Enable local development mode
183
+ local: true, // Enable local development mode
184
+ minLocalPort: 4000, // Optional: minimum port (default: 4000)
185
+ maxLocalPort: 9999 // Optional: maximum port (default: 9999)
182
186
  });
183
187
  server.start();
184
188
  ```
185
189
 
186
190
  ### Port Assignment
187
191
 
188
- In local mode, domains are automatically assigned ports based on a CRC32 hash of the domain name (range 4000-9999):
192
+ In local mode, domains are automatically assigned ports based on a CRC32 hash of the domain name (default range 4000-9999, configurable via `minLocalPort` and `maxLocalPort`):
189
193
 
190
194
  - `example.com` β†’ `http://localhost:9465`
191
195
  - `api.example.com` β†’ `http://localhost:9388`
192
196
  - And so on...
193
197
 
198
+ You can customize the port range:
199
+
200
+ ```javascript
201
+ const roster = new Roster({
202
+ local: true,
203
+ minLocalPort: 5000, // Start from port 5000
204
+ maxLocalPort: 6000 // Up to port 6000
205
+ });
206
+ ```
207
+
208
+ ### Getting Local URLs
209
+
210
+ RosterServer provides two methods to get the local URL for a domain:
211
+
212
+ **1. Static Method (Predictable, No Instance Required):**
213
+
214
+ ```javascript
215
+ // Get the URL before starting the server (using default range 4000-9999)
216
+ const url = Roster.getLocalUrl('example.com');
217
+ console.log(url); // http://localhost:9465
218
+
219
+ // Or specify custom port range
220
+ const customUrl = Roster.getLocalUrl('example.com', {
221
+ minLocalPort: 5000,
222
+ maxLocalPort: 6000
223
+ });
224
+ ```
225
+
226
+ This static method calculates the port deterministically using CRC32, so you can predict the URL before even creating a Roster instance.
227
+
228
+ **2. Instance Method (After Registration):**
229
+
230
+ ```javascript
231
+ const roster = new Roster({ local: true });
232
+ roster.register('example.com', handler);
233
+
234
+ await roster.start();
235
+
236
+ // Get the actual URL assigned to the domain
237
+ const url = roster.getLocalUrl('example.com');
238
+ console.log(url); // http://localhost:9465
239
+ ```
240
+
241
+ This instance method returns the actual URL assigned to the domain after the server starts. It's useful when you need to confirm the URL or when there might be port collisions.
242
+
243
+ **Example Usage:**
244
+
245
+ ```javascript
246
+ const roster = new Roster({ local: true });
247
+
248
+ roster.register('example.com', (httpsServer) => {
249
+ return (req, res) => {
250
+ res.writeHead(200);
251
+ res.end('Hello World!');
252
+ };
253
+ });
254
+
255
+ roster.start().then(() => {
256
+ const url = roster.getLocalUrl('example.com');
257
+ console.log(`Server available at: ${url}`);
258
+ });
259
+ ```
260
+
194
261
  ## πŸ§‚ A Touch of Magic
195
262
 
196
263
  You might be thinking, "But setting up HTTPS and virtual hosts is supposed to be complicated and time-consuming!" Well, not anymore. With RosterServer, you can get back to writing code that matters, like defending Earth from alien invaders! πŸ‘ΎπŸ‘ΎπŸ‘Ύ
@@ -0,0 +1,40 @@
1
+ const Roster = require('../index.js');
2
+
3
+ // Example with custom port range
4
+ const roster = new Roster({
5
+ local: true,
6
+ minLocalPort: 5000, // Custom minimum port
7
+ maxLocalPort: 5100 // Custom maximum port
8
+ });
9
+
10
+ console.log('\nπŸ“ Static URL Prediction with custom range (5000-5100):');
11
+ console.log('example.com β†’', Roster.getLocalUrl('example.com', {
12
+ minLocalPort: 5000,
13
+ maxLocalPort: 5100
14
+ }));
15
+ console.log('api.example.com β†’', Roster.getLocalUrl('api.example.com', {
16
+ minLocalPort: 5000,
17
+ maxLocalPort: 5100
18
+ }));
19
+
20
+ roster.register('example.com', (httpsServer) => {
21
+ return (req, res) => {
22
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
23
+ res.end('Hello from example.com with custom port range!');
24
+ };
25
+ });
26
+
27
+ roster.register('api.example.com', (httpsServer) => {
28
+ return (req, res) => {
29
+ res.writeHead(200, { 'Content-Type': 'application/json' });
30
+ res.end(JSON.stringify({ message: 'API with custom port range' }));
31
+ };
32
+ });
33
+
34
+ roster.start().then(() => {
35
+ console.log('\nπŸš€ Server Started with custom port range:');
36
+ console.log('example.com β†’', roster.getLocalUrl('example.com'));
37
+ console.log('api.example.com β†’', roster.getLocalUrl('api.example.com'));
38
+
39
+ console.log('\nβœ… Both domains running in custom port range (5000-5100)!');
40
+ });
@@ -0,0 +1,40 @@
1
+ const Roster = require('../index.js');
2
+
3
+ // Example 1: Get URL before creating instance (static method)
4
+ console.log('\nπŸ“ Static URL Prediction (before server starts):');
5
+ console.log('example.com β†’', Roster.getLocalUrl('example.com'));
6
+ console.log('api.example.com β†’', Roster.getLocalUrl('api.example.com'));
7
+ console.log('test.example.com β†’', Roster.getLocalUrl('test.example.com'));
8
+
9
+ // Example 2: Get URL after registration (instance method)
10
+ const roster = new Roster({ local: true });
11
+
12
+ roster.register('example.com', (httpsServer) => {
13
+ return (req, res) => {
14
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
15
+ res.end('Hello from example.com!');
16
+ };
17
+ });
18
+
19
+ roster.register('api.example.com', (httpsServer) => {
20
+ return (req, res) => {
21
+ res.writeHead(200, { 'Content-Type': 'application/json' });
22
+ res.end(JSON.stringify({ message: 'API endpoint' }));
23
+ };
24
+ });
25
+
26
+ roster.start().then(() => {
27
+ console.log('\nπŸš€ Server Started - Actual URLs:');
28
+ console.log('example.com β†’', roster.getLocalUrl('example.com'));
29
+ console.log('api.example.com β†’', roster.getLocalUrl('api.example.com'));
30
+
31
+ // Test with www prefix (should return same URL)
32
+ console.log('\nπŸ”„ Testing www prefix handling:');
33
+ console.log('www.example.com β†’', roster.getLocalUrl('www.example.com'));
34
+
35
+ // Test non-existent domain
36
+ console.log('\n❌ Testing non-existent domain:');
37
+ console.log('nonexistent.com β†’', roster.getLocalUrl('nonexistent.com') || 'null (domain not registered)');
38
+
39
+ console.log('\nβœ… All domains running!');
40
+ });
package/demo/socketio.js CHANGED
@@ -26,4 +26,12 @@ roster.register('example.com', (httpsServer) => {
26
26
  };
27
27
  });
28
28
 
29
- roster.start();
29
+ roster.start().then(() => {
30
+ // Get local URL for registered domain (requires instance)
31
+ const url = roster.getLocalUrl('example.com');
32
+ console.log(`βœ… Socket.IO server available at: ${url}`);
33
+
34
+ // Get local URL without instance (static method - predictable port)
35
+ const staticUrl = Roster.getLocalUrl('example.com');
36
+ console.log(`ℹ️ Static prediction: ${staticUrl}`);
37
+ });
package/index.js CHANGED
@@ -160,9 +160,12 @@ class Roster {
160
160
  this.sites = {};
161
161
  this.domainServers = {}; // Store separate servers for each domain
162
162
  this.portServers = {}; // Store servers by port
163
+ this.domainPorts = {}; // Store domain β†’ port mapping for local mode
163
164
  this.assignedPorts = new Set(); // Track ports assigned to domains (not OS availability)
164
165
  this.hostname = options.hostname || '0.0.0.0';
165
166
  this.filename = options.filename || 'index';
167
+ this.minLocalPort = options.minLocalPort || 4000;
168
+ this.maxLocalPort = options.maxLocalPort || 9999;
166
169
 
167
170
  const port = options.port === undefined ? 443 : options.port;
168
171
  if (port === 80 && !this.local) {
@@ -363,19 +366,57 @@ class Roster {
363
366
  return { domain: domainString, port: this.defaultPort };
364
367
  }
365
368
 
369
+ /**
370
+ * Get the local URL for a domain when running in local mode
371
+ * @param {string} domain - The domain name (e.g., 'example.com')
372
+ * @param {Object} options - Optional configuration
373
+ * @param {number} options.minLocalPort - Minimum port range (default: 4000)
374
+ * @param {number} options.maxLocalPort - Maximum port range (default: 9999)
375
+ * @returns {string} The local URL (e.g., 'http://localhost:4321')
376
+ */
377
+ static getLocalUrl(domain, options = {}) {
378
+ const minPort = options.minLocalPort || 4000;
379
+ const maxPort = options.maxLocalPort || 9999;
380
+
381
+ // Remove www prefix if present
382
+ const cleanDomain = domain.startsWith('www.') ? domain.slice(4) : domain;
383
+
384
+ // Calculate deterministic port
385
+ const port = domainToPort(cleanDomain, minPort, maxPort);
386
+
387
+ return `http://localhost:${port}`;
388
+ }
389
+
390
+ /**
391
+ * Get the local URL for a domain that was registered on this instance
392
+ * @param {string} domain - The domain name
393
+ * @returns {string|null} The local URL if found, null otherwise
394
+ */
395
+ getLocalUrl(domain) {
396
+ // Remove www prefix if present
397
+ const cleanDomain = domain.startsWith('www.') ? domain.slice(4) : domain;
398
+
399
+ // Check if domain has a port assigned
400
+ if (this.domainPorts && this.domainPorts[cleanDomain]) {
401
+ return `http://localhost:${this.domainPorts[cleanDomain]}`;
402
+ }
403
+
404
+ return null;
405
+ }
406
+
366
407
  createVirtualServer(domain) {
367
408
  return new VirtualServer(domain);
368
409
  }
369
410
 
370
411
  // Assign port to domain, detecting collisions with already assigned ports
371
- assignPortToDomain(domain, minPort = 4000, maxPort = 9999) {
372
- let port = domainToPort(domain, minPort, maxPort);
412
+ assignPortToDomain(domain) {
413
+ let port = domainToPort(domain, this.minLocalPort, this.maxLocalPort);
373
414
 
374
415
  // If port is already assigned to another domain, increment until we find a free one
375
416
  while (this.assignedPorts.has(port)) {
376
417
  port++;
377
- if (port > maxPort) {
378
- port = minPort; // Wrap around if we exceed max port
418
+ if (port > this.maxLocalPort) {
419
+ port = this.minLocalPort; // Wrap around if we exceed max port
379
420
  }
380
421
  }
381
422
 
@@ -403,6 +444,9 @@ class Roster {
403
444
 
404
445
  // Start server in local mode with HTTP - simplified version
405
446
  startLocalMode() {
447
+ // Store mapping of domain to port for later retrieval
448
+ this.domainPorts = {};
449
+
406
450
  // Create a simple HTTP server for each domain with CRC32-based ports
407
451
  for (const [hostKey, siteApp] of Object.entries(this.sites)) {
408
452
  const domain = hostKey.split(':')[0]; // Remove port if present
@@ -413,7 +457,10 @@ class Roster {
413
457
  }
414
458
 
415
459
  // Calculate deterministic port based on domain CRC32, with collision detection
416
- const port = this.assignPortToDomain(domain, 4000, 9999);
460
+ const port = this.assignPortToDomain(domain);
461
+
462
+ // Store domain β†’ port mapping
463
+ this.domainPorts[domain] = port;
417
464
 
418
465
  // Create virtual server for the domain
419
466
  const virtualServer = this.createVirtualServer(domain);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roster-server",
3
- "version": "1.9.2",
3
+ "version": "1.9.4",
4
4
  "description": "πŸ‘Ύ RosterServer - A domain host router to host multiple HTTPS.",
5
5
  "main": "index.js",
6
6
  "scripts": {