javascript-solid-server 0.0.84 → 0.0.85

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "javascript-solid-server",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "description": "A minimal, fast Solid server",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -6,6 +6,8 @@
6
6
  */
7
7
 
8
8
  import { EventEmitter } from 'events';
9
+ import { watch } from 'fs';
10
+ import { join, relative } from 'path';
9
11
 
10
12
  // Singleton event emitter for resource changes
11
13
  export const resourceEvents = new EventEmitter();
@@ -20,3 +22,56 @@ resourceEvents.setMaxListeners(1000);
20
22
  export function emitChange(resourceUrl) {
21
23
  resourceEvents.emit('change', resourceUrl);
22
24
  }
25
+
26
+ /**
27
+ * Start watching filesystem for changes and emit notifications
28
+ * @param {string} rootDir - Directory to watch
29
+ * @param {string} baseUrl - Base URL for constructing resource URLs (e.g., http://localhost:3000)
30
+ */
31
+ export function startFileWatcher(rootDir, baseUrl) {
32
+ // Debounce map to avoid duplicate events (editors often save multiple times)
33
+ const debounceMap = new Map();
34
+ const DEBOUNCE_MS = 100;
35
+
36
+ try {
37
+ const watcher = watch(rootDir, { recursive: true }, (eventType, filename) => {
38
+ if (!filename) return;
39
+
40
+ // Skip hidden files and common temp files
41
+ if (filename.startsWith('.') || filename.endsWith('~') || filename.endsWith('.swp')) {
42
+ return;
43
+ }
44
+
45
+ // Debounce: skip if we just emitted for this file
46
+ const now = Date.now();
47
+ const lastEmit = debounceMap.get(filename);
48
+ if (lastEmit && now - lastEmit < DEBOUNCE_MS) {
49
+ return;
50
+ }
51
+ debounceMap.set(filename, now);
52
+
53
+ // Clean up old debounce entries periodically
54
+ if (debounceMap.size > 1000) {
55
+ for (const [key, time] of debounceMap) {
56
+ if (now - time > 5000) debounceMap.delete(key);
57
+ }
58
+ }
59
+
60
+ // Construct resource URL
61
+ const resourcePath = '/' + filename.replace(/\\/g, '/');
62
+ const resourceUrl = baseUrl.replace(/\/$/, '') + resourcePath;
63
+
64
+ emitChange(resourceUrl);
65
+ });
66
+
67
+ // Handle watcher errors gracefully
68
+ watcher.on('error', (err) => {
69
+ console.error('File watcher error:', err.message);
70
+ });
71
+
72
+ return watcher;
73
+ } catch (err) {
74
+ console.error('Failed to start file watcher:', err.message);
75
+ return null;
76
+ }
77
+ }
package/src/server.js CHANGED
@@ -9,6 +9,7 @@ import * as storage from './storage/filesystem.js';
9
9
  import { getCorsHeaders } from './ldp/headers.js';
10
10
  import { authorize, handleUnauthorized } from './auth/middleware.js';
11
11
  import { notificationsPlugin } from './notifications/index.js';
12
+ import { startFileWatcher } from './notifications/events.js';
12
13
  import { idpPlugin } from './idp/index.js';
13
14
  import { isGitRequest, isGitWriteOperation, handleGit } from './handlers/git.js';
14
15
  import { AccessMode } from './wac/parser.js';
@@ -542,6 +543,16 @@ export function createServer(options = {}) {
542
543
  // Note: Quota not initialized for root-level pods (no user directory)
543
544
  }
544
545
 
546
+ // Start file watcher for live reload (watches filesystem for external changes)
547
+ if (liveReloadEnabled) {
548
+ const dataRoot = options.root || process.env.DATA_ROOT || './data';
549
+ const protocol = options.ssl ? 'https' : 'http';
550
+ // Use configured port, or default; actual URL will be localhost
551
+ const port = options.port || 3000;
552
+ const baseUrl = `${protocol}://localhost:${port}`;
553
+ startFileWatcher(dataRoot, baseUrl);
554
+ }
555
+
545
556
  return fastify;
546
557
  }
547
558