javascript-solid-server 0.0.164 → 0.0.165

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.
@@ -1,329 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * servejss - Static file server with REST write support
5
- * A drop-in replacement for `npx serve` with PUT/DELETE capabilities
6
- */
7
-
8
- import { program } from 'commander';
9
- import { resolve } from 'path';
10
- import { spawn } from 'child_process';
11
- import { createServer } from 'net';
12
- import { networkInterfaces } from 'os';
13
- import { fileURLToPath } from 'url';
14
- import { dirname, join } from 'path';
15
- import chalk from 'chalk';
16
- import fs from 'fs';
17
-
18
- const __filename = fileURLToPath(import.meta.url);
19
- const __dirname = dirname(__filename);
20
- const pkg = JSON.parse(fs.readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
21
-
22
- /**
23
- * Check if a port is available
24
- * @param {number} port
25
- * @returns {Promise<boolean>}
26
- */
27
- function isPortAvailable(port) {
28
- return new Promise((resolve) => {
29
- const server = createServer();
30
- server.once('error', () => resolve(false));
31
- server.once('listening', () => {
32
- server.close();
33
- resolve(true);
34
- });
35
- server.listen(port, '0.0.0.0');
36
- });
37
- }
38
-
39
- /**
40
- * Find an available port starting from the given port
41
- * @param {number} startPort
42
- * @param {number} maxAttempts
43
- * @returns {Promise<number>}
44
- */
45
- async function findAvailablePort(startPort, maxAttempts = 100) {
46
- for (let i = 0; i < maxAttempts; i++) {
47
- const port = startPort + i;
48
- if (await isPortAvailable(port)) {
49
- return port;
50
- }
51
- }
52
- throw new Error(`Could not find available port after ${maxAttempts} attempts`);
53
- }
54
-
55
- /**
56
- * Get local network IP address
57
- * @returns {string|null}
58
- */
59
- function getNetworkAddress() {
60
- const nets = networkInterfaces();
61
- for (const name of Object.keys(nets)) {
62
- for (const net of nets[name]) {
63
- if (net.family === 'IPv4' && !net.internal) {
64
- return net.address;
65
- }
66
- }
67
- }
68
- return null;
69
- }
70
-
71
- /**
72
- * Print the startup banner
73
- */
74
- function printBanner(options) {
75
- const { port, host, directory, readOnly, live, networkAddress } = options;
76
-
77
- const local = `http://${host === '0.0.0.0' ? 'localhost' : host}:${port}`;
78
- const network = networkAddress ? `http://${networkAddress}:${port}` : null;
79
-
80
- const mode = readOnly ? 'GET only (read-only)' : 'GET/PUT/DELETE enabled';
81
-
82
- console.log();
83
- console.log(chalk.bgCyan.black(' servejss '));
84
- console.log();
85
- console.log(` ${chalk.gray('Directory:')} ${directory}`);
86
- console.log();
87
- console.log(` ${chalk.gray('Local:')} ${chalk.cyan(local)}`);
88
- if (network) {
89
- console.log(` ${chalk.gray('Network:')} ${chalk.cyan(network)}`);
90
- }
91
- console.log();
92
- console.log(` ${chalk.gray('Mode:')} ${chalk.yellow(mode)}`);
93
- if (live) {
94
- console.log(` ${chalk.gray('Live:')} ${chalk.green('Watching for changes')}`);
95
- }
96
- console.log();
97
- console.log(chalk.gray(' Press Ctrl+C to stop'));
98
- console.log();
99
- }
100
-
101
- /**
102
- * Print an error message
103
- */
104
- function printError(message) {
105
- console.error();
106
- console.error(chalk.red(' ERROR:'), message);
107
- console.error();
108
- }
109
-
110
- // CLI definition - matching `serve` interface
111
- program
112
- .name('servejss')
113
- .description('Static file server with REST write support')
114
- .version(pkg.version, '-v, --version')
115
- .argument('[directory]', 'Directory to serve', '.')
116
- .option('-l, --listen <uri>', 'Specify a URI endpoint on which to listen')
117
- .option('-p, --port <port>', 'Specify custom port', '3000')
118
- .option('-H, --host <host>', 'Host to bind to', '0.0.0.0')
119
- .option('-s, --single', 'Rewrite all not-found requests to index.html (SPA mode)')
120
- .option('-d, --debug', 'Show debugging information')
121
- .option('-C, --cors', 'Enable CORS (enabled by default)')
122
- .option('-L, --no-request-logging', 'Do not log any request information')
123
- .option('--no-etag', 'Disable ETag generation')
124
- .option('-S, --symlinks', 'Resolve symlinks instead of showing 404')
125
- .option('--ssl-cert <path>', 'Path to SSL certificate')
126
- .option('--ssl-key <path>', 'Path to SSL private key')
127
- .option('--no-port-switching', 'Do not open a different port if specified one is taken')
128
- // servejss-specific options
129
- .option('-r, --read-only', 'Disable PUT/DELETE methods (like npx serve)')
130
- .option('--write', 'Enable PUT/DELETE methods (default)')
131
- .option('--auth <credentials>', 'Enable basic auth (user:pass)')
132
- .option('--solid', 'Enable full Solid protocol features')
133
- .option('--live', 'Enable live reload (default: true)')
134
- .option('--no-live', 'Disable live reload')
135
- .option('--git', 'Enable git HTTP backend (default: true)')
136
- .option('--no-git', 'Disable git HTTP backend')
137
- .option('-q, --quiet', 'Suppress all output')
138
- .addHelpText('after', `
139
- Examples:
140
- $ servejss Serve current directory with read/write
141
- $ servejss ./public Serve specific directory
142
- $ servejss -p 8080 Use custom port
143
- $ servejss --read-only Read-only mode (like npx serve)
144
- $ servejss -l 3000 ./dist Listen on port 3000, serve ./dist
145
-
146
- REST API:
147
- GET /file.txt Read file
148
- PUT /file.txt Create/update file
149
- DELETE /file.txt Delete file
150
- HEAD /file.txt Get file metadata
151
-
152
- Differences from 'serve':
153
- - PUT and DELETE enabled by default (use --read-only to disable)
154
- - ETag support for caching and conditional requests
155
- - Optional upgrade to full Solid protocol with --solid
156
- `)
157
- .action(async (directory, options) => {
158
- try {
159
- await run(directory, options);
160
- } catch (err) {
161
- printError(err.message);
162
- process.exit(1);
163
- }
164
- });
165
-
166
- program.parse();
167
-
168
- /**
169
- * Main run function
170
- */
171
- async function run(directory, options) {
172
- // Resolve directory
173
- const dir = resolve(directory || '.');
174
-
175
- // Check directory exists
176
- if (!fs.existsSync(dir)) {
177
- throw new Error(`Directory not found: ${dir}`);
178
- }
179
-
180
- if (!fs.statSync(dir).isDirectory()) {
181
- throw new Error(`Not a directory: ${dir}`);
182
- }
183
-
184
- // Parse port from --listen or --port
185
- let port = parseInt(options.port, 10);
186
- if (options.listen) {
187
- // Parse listen URI (e.g., "3000", "tcp://localhost:3000")
188
- const listenMatch = options.listen.match(/:?(\d+)$/);
189
- if (listenMatch) {
190
- port = parseInt(listenMatch[1], 10);
191
- }
192
- }
193
-
194
- // Find available port if needed
195
- const originalPort = port;
196
- if (options.portSwitching !== false) {
197
- const available = await isPortAvailable(port);
198
- if (!available) {
199
- port = await findAvailablePort(port + 1);
200
- if (!options.quiet) {
201
- console.log(chalk.yellow(` Port ${originalPort} is in use, using ${port} instead`));
202
- }
203
- }
204
- } else {
205
- const available = await isPortAvailable(port);
206
- if (!available) {
207
- throw new Error(`Port ${port} is already in use`);
208
- }
209
- }
210
-
211
- // Build JSS arguments
212
- const jssArgs = [
213
- 'start',
214
- '--root', dir,
215
- '--port', String(port),
216
- '--host', options.host,
217
- '--public', // Enable open access (requires #107)
218
- ];
219
-
220
- // Read-only mode
221
- if (options.readOnly) {
222
- jssArgs.push('--read-only');
223
- }
224
-
225
- // Quiet mode
226
- if (options.quiet || !options.requestLogging) {
227
- jssArgs.push('--quiet');
228
- }
229
-
230
- // SSL
231
- if (options.sslCert && options.sslKey) {
232
- jssArgs.push('--ssl-cert', options.sslCert);
233
- jssArgs.push('--ssl-key', options.sslKey);
234
- }
235
-
236
- // Solid mode (full features)
237
- if (options.solid) {
238
- // Remove --public, use full Solid auth
239
- const publicIndex = jssArgs.indexOf('--public');
240
- if (publicIndex > -1) {
241
- jssArgs.splice(publicIndex, 1);
242
- }
243
- jssArgs.push('--idp');
244
- jssArgs.push('--conneg');
245
- }
246
-
247
- // Live reload mode (default: enabled)
248
- if (options.live !== false) {
249
- jssArgs.push('--live-reload');
250
- }
251
-
252
- // Git HTTP backend (default: enabled)
253
- if (options.git !== false) {
254
- jssArgs.push('--git');
255
- }
256
-
257
- // Debug mode
258
- if (options.debug) {
259
- console.log(chalk.gray(' JSS args:'), jssArgs.join(' '));
260
- }
261
-
262
- // Print banner
263
- if (!options.quiet) {
264
- printBanner({
265
- port,
266
- host: options.host,
267
- directory: dir,
268
- readOnly: options.readOnly,
269
- live: options.live !== false,
270
- networkAddress: getNetworkAddress(),
271
- });
272
- }
273
-
274
- // Find jss binary
275
- let jssPath;
276
- try {
277
- // Try to find jss in node_modules
278
- const jssModule = await import.meta.resolve('javascript-solid-server/bin/jss.js');
279
- jssPath = fileURLToPath(jssModule);
280
- } catch {
281
- // Fallback to global jss
282
- jssPath = 'jss';
283
- }
284
-
285
- // Spawn JSS
286
- const child = spawn('node', [jssPath, ...jssArgs], {
287
- stdio: options.quiet ? 'ignore' : ['inherit', 'pipe', 'inherit'],
288
- env: { ...process.env, FORCE_COLOR: '1' },
289
- });
290
-
291
- // Filter JSS output to remove its banner (we have our own)
292
- if (!options.quiet && child.stdout) {
293
- let skipBanner = true;
294
- child.stdout.on('data', (data) => {
295
- const str = data.toString();
296
- // Skip JSS startup banner, show rest
297
- if (skipBanner && (str.includes('Listening') || str.includes('Server'))) {
298
- skipBanner = false;
299
- return;
300
- }
301
- if (!skipBanner || options.debug) {
302
- process.stdout.write(data);
303
- }
304
- });
305
- }
306
-
307
- // Handle child process errors
308
- child.on('error', (err) => {
309
- printError(`Failed to start server: ${err.message}`);
310
- process.exit(1);
311
- });
312
-
313
- child.on('exit', (code) => {
314
- if (code !== 0 && code !== null) {
315
- process.exit(code);
316
- }
317
- });
318
-
319
- // Handle graceful shutdown
320
- const shutdown = () => {
321
- if (!options.quiet) {
322
- console.log(chalk.gray('\n Shutting down...'));
323
- }
324
- child.kill('SIGTERM');
325
- };
326
-
327
- process.on('SIGINT', shutdown);
328
- process.on('SIGTERM', shutdown);
329
- }