Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { dirname, join, delimiter } from 'path';
import chalk from 'chalk';
import { existsSync, mkdirSync, readFileSync, writeFileSync, chmodSync, statSync, copyFileSync } from 'fs';
import { randomBytes } from 'crypto';
import { createServer } from 'net';

const __dirname = dirname(fileURLToPath(import.meta.url));
const pkg = JSON.parse(readFileSync(join(__dirname, 'package.json'), 'utf8'));
Expand Down Expand Up @@ -161,6 +162,35 @@ if (!existsSync(options.root)) {
mkdirSync(options.root, { recursive: true });
}

// Find a free port starting at the requested one. Mirrors Vite's
// behaviour: shift up by one and try again, up to 10 attempts. We probe
// by binding a throwaway server on the same host the spawned JSS will
// use, so the result reflects the actual interface we'll claim.
async function findFreePort(startPort, host, maxTries = 10) {
for (let p = startPort; p < startPort + maxTries; p++) {
const free = await new Promise((resolve) => {
const srv = createServer();
srv.once('error', () => resolve(false));
srv.once('listening', () => srv.close(() => resolve(true)));
srv.listen(p, host);
});
if (free) return p;
}
return null;
}

const requestedPort = options.port;
const freePort = await findFreePort(requestedPort, options.host);
if (freePort === null) {
console.error(chalk.red(`✗ No free port in range ${requestedPort}-${requestedPort + 9} on ${options.host}.`));
console.error(chalk.dim('Pass --port <number> to pick a different starting port.'));
process.exit(1);
}
if (freePort !== requestedPort) {
console.log(chalk.yellow(`Port ${requestedPort} is in use, using ${freePort} instead.`));
}
options.port = freePort;

// Resolve the JWT signing secret. Priority:
// 1. TOKEN_SECRET env var (operator-controlled)
// 2. Persisted random secret at <root>/.token-secret (generated on
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jspod",
"version": "0.0.15",
"version": "0.0.16",
"description": "JavaScript Solid Pod - Just works, batteries included",
"type": "module",
"main": "index.js",
Expand Down