roster-server 2.3.6 → 2.3.8
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 +5 -1
- package/docs/decisions/local-subdomain-localhost-url.md +12 -0
- package/index.js +17 -2
- package/package.json +1 -1
- package/test/roster-server.test.js +8 -1
package/README.md
CHANGED
|
@@ -309,12 +309,13 @@ await roster.start();
|
|
|
309
309
|
const url = roster.getUrl('example.com');
|
|
310
310
|
console.log(url);
|
|
311
311
|
// Local mode: http://localhost:9465
|
|
312
|
+
// Local subdomain: http://api.localhost:9465
|
|
312
313
|
// Production mode: https://example.com
|
|
313
314
|
```
|
|
314
315
|
|
|
315
316
|
This method:
|
|
316
317
|
- Returns the correct URL based on your environment (`local: true/false`)
|
|
317
|
-
- In **local mode**: Returns `http://localhost:{port}`
|
|
318
|
+
- In **local mode**: Returns `http://localhost:{port}` for apex domains and `http://{subdomain}.localhost:{port}` for subdomains
|
|
318
319
|
- In **production mode**: Returns `https://{domain}` (or with custom port if configured)
|
|
319
320
|
- Handles `www.` prefix automatically (returns same URL)
|
|
320
321
|
- Returns `null` for domains that aren't registered
|
|
@@ -327,9 +328,12 @@ import Roster from 'roster-server';
|
|
|
327
328
|
// Local development
|
|
328
329
|
const localRoster = new Roster({ local: true });
|
|
329
330
|
localRoster.register('example.com', handler);
|
|
331
|
+
localRoster.register('api.example.com', handler);
|
|
330
332
|
await localRoster.start();
|
|
331
333
|
console.log(localRoster.getUrl('example.com'));
|
|
332
334
|
// → http://localhost:9465
|
|
335
|
+
console.log(localRoster.getUrl('api.example.com'));
|
|
336
|
+
// → http://api.localhost:7342
|
|
333
337
|
|
|
334
338
|
// Production
|
|
335
339
|
const prodRoster = new Roster({ local: false });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
id: wrxv04lnq9
|
|
3
|
+
type: decision
|
|
4
|
+
title: 'Decision: Local subdomain URL format'
|
|
5
|
+
created: '2026-03-16 17:15:01'
|
|
6
|
+
---
|
|
7
|
+
# Decision: Local subdomain URL format
|
|
8
|
+
|
|
9
|
+
**What**: In local mode, `getUrl(domain)` now returns `http://<subdomain>.localhost:<port>` for subdomains, while apex domains keep `http://localhost:<port>`.
|
|
10
|
+
**Where**: Implemented in `index.js` (`getUrl` local branch), validated in `test/roster-server.test.js`, and documented in `README.md`.
|
|
11
|
+
**Why**: Local URLs should preserve subdomain semantics so multi-tenant/subdomain routes are explicit and easier to test.
|
|
12
|
+
**Alternatives rejected**: Keeping all local URLs as `http://localhost:<port>` was rejected because it hides host intent for subdomain-based apps.
|
package/index.js
CHANGED
|
@@ -606,7 +606,22 @@ class Roster {
|
|
|
606
606
|
if (this.local) {
|
|
607
607
|
const pattern = resolved.siteKey.split(':')[0];
|
|
608
608
|
if (this.domainPorts && this.domainPorts[pattern] !== undefined) {
|
|
609
|
-
|
|
609
|
+
const labels = cleanDomain.split('.').filter(Boolean);
|
|
610
|
+
let localHost = 'localhost';
|
|
611
|
+
|
|
612
|
+
// For wildcard matches, preserve the full left-side subdomain chain.
|
|
613
|
+
if (pattern.startsWith('*.')) {
|
|
614
|
+
const root = wildcardRoot(pattern);
|
|
615
|
+
if (root && hostMatchesWildcard(cleanDomain, pattern)) {
|
|
616
|
+
const subdomain = cleanDomain.slice(0, -(root.length + 1));
|
|
617
|
+
if (subdomain) localHost = `${subdomain}.localhost`;
|
|
618
|
+
}
|
|
619
|
+
} else if (labels.length > 2) {
|
|
620
|
+
// For exact subdomains (e.g. api.example.com), map to api.localhost.
|
|
621
|
+
localHost = `${labels.slice(0, -2).join('.')}.localhost`;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
return `http://${localHost}:${this.domainPorts[pattern]}`;
|
|
610
625
|
}
|
|
611
626
|
return null;
|
|
612
627
|
}
|
|
@@ -778,7 +793,7 @@ class Roster {
|
|
|
778
793
|
}
|
|
779
794
|
}
|
|
780
795
|
if (!msg || msg === 'undefined') msg = `[${event}] (no details)`;
|
|
781
|
-
if (eventDomain && !msg.includes(`[
|
|
796
|
+
if (eventDomain && !msg.includes(`[${eventDomain}]`)) {
|
|
782
797
|
msg = `[domain:${eventDomain}] ${msg}`;
|
|
783
798
|
}
|
|
784
799
|
// Suppress known benign warnings from ACME when using acme-dns-01-cli
|
package/package.json
CHANGED
|
@@ -218,7 +218,7 @@ describe('Roster', () => {
|
|
|
218
218
|
roster.register('*.example.com', () => {});
|
|
219
219
|
roster.domainPorts = { '*.example.com': 9999 };
|
|
220
220
|
roster.local = true;
|
|
221
|
-
assert.strictEqual(roster.getUrl('api.example.com'), 'http://localhost:9999');
|
|
221
|
+
assert.strictEqual(roster.getUrl('api.example.com'), 'http://api.localhost:9999');
|
|
222
222
|
});
|
|
223
223
|
it('returns https URL for wildcard-matched host in production', () => {
|
|
224
224
|
const roster = new Roster({ local: false });
|
|
@@ -359,6 +359,13 @@ describe('Roster', () => {
|
|
|
359
359
|
roster.local = true;
|
|
360
360
|
assert.strictEqual(roster.getUrl('exact.local'), 'http://localhost:4567');
|
|
361
361
|
});
|
|
362
|
+
it('returns http://subdomain.localhost:PORT in local mode for exact subdomain', () => {
|
|
363
|
+
const roster = new Roster({ local: true });
|
|
364
|
+
roster.register('api.example.com', () => {});
|
|
365
|
+
roster.domainPorts = { 'api.example.com': 5678 };
|
|
366
|
+
roster.local = true;
|
|
367
|
+
assert.strictEqual(roster.getUrl('api.example.com'), 'http://api.localhost:5678');
|
|
368
|
+
});
|
|
362
369
|
it('returns https URL in production for registered domain', () => {
|
|
363
370
|
const roster = new Roster({ local: false });
|
|
364
371
|
roster.register('example.com', () => {});
|