javascript-solid-server 0.0.85 → 0.0.86
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/.claude/settings.local.json +2 -1
- package/bin/jss.js +2 -0
- package/package.json +1 -1
- package/src/notifications/websocket.js +6 -0
- package/test/live-reload.test.js +265 -0
package/bin/jss.js
CHANGED
package/package.json
CHANGED
|
@@ -40,6 +40,7 @@ export function handleWebSocket(socket, request, webId = null) {
|
|
|
40
40
|
// Store webId and server info on socket for ACL checks
|
|
41
41
|
socket.webId = webId;
|
|
42
42
|
socket.serverOrigin = `${request.protocol}://${request.hostname}`;
|
|
43
|
+
socket.publicMode = request.config?.public || false;
|
|
43
44
|
|
|
44
45
|
// Send protocol greeting
|
|
45
46
|
socket.send('protocol solid-0.1');
|
|
@@ -123,6 +124,11 @@ async function checkSubscriptionAccess(url, socket) {
|
|
|
123
124
|
const stats = await storage.stat(resourcePath);
|
|
124
125
|
const isContainer = stats?.isDirectory || resourcePath.endsWith('/');
|
|
125
126
|
|
|
127
|
+
// Skip WAC check in public mode
|
|
128
|
+
if (socket.publicMode) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
|
|
126
132
|
// Check WAC read permission
|
|
127
133
|
const { allowed } = await checkAccess({
|
|
128
134
|
resourceUrl: url,
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live Reload Test
|
|
3
|
+
*
|
|
4
|
+
* Tests the full chain: file change → WebSocket pub → client receives
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createServer } from '../src/server.js';
|
|
8
|
+
import { writeFileSync, mkdirSync, rmSync, existsSync } from 'fs';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import WebSocket from 'ws';
|
|
11
|
+
|
|
12
|
+
const TEST_PORT = 9876;
|
|
13
|
+
const TEST_DIR = '/tmp/live-reload-test-suite';
|
|
14
|
+
const BASE_URL = `http://localhost:${TEST_PORT}`;
|
|
15
|
+
|
|
16
|
+
// Setup and teardown
|
|
17
|
+
function setupTestDir() {
|
|
18
|
+
if (existsSync(TEST_DIR)) {
|
|
19
|
+
rmSync(TEST_DIR, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
mkdirSync(TEST_DIR, { recursive: true });
|
|
22
|
+
writeFileSync(join(TEST_DIR, 'index.html'), '<!DOCTYPE html><html><body>Hello</body></html>');
|
|
23
|
+
writeFileSync(join(TEST_DIR, 'test.txt'), 'initial content');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function cleanupTestDir() {
|
|
27
|
+
if (existsSync(TEST_DIR)) {
|
|
28
|
+
rmSync(TEST_DIR, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Test 1: Verify WebSocket notifications work for HTTP PUT
|
|
33
|
+
async function testHttpPutNotification() {
|
|
34
|
+
console.log('\n=== Test 1: HTTP PUT triggers WebSocket notification ===');
|
|
35
|
+
|
|
36
|
+
setupTestDir();
|
|
37
|
+
|
|
38
|
+
const server = createServer({
|
|
39
|
+
root: TEST_DIR,
|
|
40
|
+
port: TEST_PORT,
|
|
41
|
+
logger: false,
|
|
42
|
+
liveReload: true,
|
|
43
|
+
public: true,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
await server.listen({ port: TEST_PORT, host: '0.0.0.0' });
|
|
47
|
+
console.log('Server started on port', TEST_PORT);
|
|
48
|
+
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const timeout = setTimeout(() => {
|
|
51
|
+
ws.close();
|
|
52
|
+
server.close();
|
|
53
|
+
reject(new Error('Timeout: No WebSocket notification received'));
|
|
54
|
+
}, 5000);
|
|
55
|
+
|
|
56
|
+
const ws = new WebSocket(`ws://localhost:${TEST_PORT}/.notifications`);
|
|
57
|
+
|
|
58
|
+
ws.on('open', () => {
|
|
59
|
+
console.log('WebSocket connected');
|
|
60
|
+
// Subscribe to the test file
|
|
61
|
+
ws.send(`sub ${BASE_URL}/test.txt`);
|
|
62
|
+
console.log('Subscribed to', `${BASE_URL}/test.txt`);
|
|
63
|
+
|
|
64
|
+
// Wait a bit then do HTTP PUT
|
|
65
|
+
setTimeout(async () => {
|
|
66
|
+
console.log('Doing HTTP PUT...');
|
|
67
|
+
const res = await fetch(`${BASE_URL}/test.txt`, {
|
|
68
|
+
method: 'PUT',
|
|
69
|
+
body: 'updated via HTTP',
|
|
70
|
+
headers: { 'Content-Type': 'text/plain' }
|
|
71
|
+
});
|
|
72
|
+
console.log('PUT response:', res.status);
|
|
73
|
+
}, 500);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
ws.on('message', (data) => {
|
|
77
|
+
const msg = data.toString();
|
|
78
|
+
console.log('WebSocket received:', msg);
|
|
79
|
+
|
|
80
|
+
if (msg.startsWith('pub ')) {
|
|
81
|
+
clearTimeout(timeout);
|
|
82
|
+
console.log('SUCCESS: Received pub notification');
|
|
83
|
+
ws.close();
|
|
84
|
+
server.close().then(() => {
|
|
85
|
+
cleanupTestDir();
|
|
86
|
+
resolve(true);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
ws.on('error', (err) => {
|
|
92
|
+
clearTimeout(timeout);
|
|
93
|
+
server.close();
|
|
94
|
+
reject(err);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Test 2: Verify file watcher detects filesystem changes
|
|
100
|
+
async function testFileWatcherNotification() {
|
|
101
|
+
console.log('\n=== Test 2: Filesystem change triggers WebSocket notification ===');
|
|
102
|
+
|
|
103
|
+
setupTestDir();
|
|
104
|
+
|
|
105
|
+
const server = createServer({
|
|
106
|
+
root: TEST_DIR,
|
|
107
|
+
port: TEST_PORT,
|
|
108
|
+
logger: false,
|
|
109
|
+
liveReload: true,
|
|
110
|
+
public: true,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
await server.listen({ port: TEST_PORT, host: '0.0.0.0' });
|
|
114
|
+
console.log('Server started on port', TEST_PORT);
|
|
115
|
+
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
const timeout = setTimeout(() => {
|
|
118
|
+
ws.close();
|
|
119
|
+
server.close();
|
|
120
|
+
reject(new Error('Timeout: No WebSocket notification received for filesystem change'));
|
|
121
|
+
}, 5000);
|
|
122
|
+
|
|
123
|
+
const ws = new WebSocket(`ws://localhost:${TEST_PORT}/.notifications`);
|
|
124
|
+
|
|
125
|
+
ws.on('open', () => {
|
|
126
|
+
console.log('WebSocket connected');
|
|
127
|
+
// Subscribe to the test file
|
|
128
|
+
ws.send(`sub ${BASE_URL}/test.txt`);
|
|
129
|
+
console.log('Subscribed to', `${BASE_URL}/test.txt`);
|
|
130
|
+
|
|
131
|
+
// Wait a bit then modify file directly on filesystem
|
|
132
|
+
setTimeout(() => {
|
|
133
|
+
console.log('Modifying file via filesystem...');
|
|
134
|
+
writeFileSync(join(TEST_DIR, 'test.txt'), 'updated via filesystem ' + Date.now());
|
|
135
|
+
console.log('File written');
|
|
136
|
+
}, 1000);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
ws.on('message', (data) => {
|
|
140
|
+
const msg = data.toString();
|
|
141
|
+
console.log('WebSocket received:', msg);
|
|
142
|
+
|
|
143
|
+
if (msg.startsWith('pub ')) {
|
|
144
|
+
clearTimeout(timeout);
|
|
145
|
+
console.log('SUCCESS: Received pub notification for filesystem change');
|
|
146
|
+
ws.close();
|
|
147
|
+
server.close().then(() => {
|
|
148
|
+
cleanupTestDir();
|
|
149
|
+
resolve(true);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
ws.on('error', (err) => {
|
|
155
|
+
clearTimeout(timeout);
|
|
156
|
+
server.close();
|
|
157
|
+
reject(err);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Test 3: Verify fs.watch works on this platform
|
|
163
|
+
async function testFsWatch() {
|
|
164
|
+
console.log('\n=== Test 3: Basic fs.watch functionality ===');
|
|
165
|
+
|
|
166
|
+
const { watch } = await import('fs');
|
|
167
|
+
const testFile = '/tmp/fswatch-test.txt';
|
|
168
|
+
|
|
169
|
+
writeFileSync(testFile, 'initial');
|
|
170
|
+
|
|
171
|
+
return new Promise((resolve, reject) => {
|
|
172
|
+
const timeout = setTimeout(() => {
|
|
173
|
+
watcher.close();
|
|
174
|
+
reject(new Error('fs.watch did not detect file change'));
|
|
175
|
+
}, 3000);
|
|
176
|
+
|
|
177
|
+
const watcher = watch(testFile, (eventType, filename) => {
|
|
178
|
+
console.log('fs.watch detected:', eventType, filename);
|
|
179
|
+
clearTimeout(timeout);
|
|
180
|
+
watcher.close();
|
|
181
|
+
resolve(true);
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
// Modify file after short delay
|
|
185
|
+
setTimeout(() => {
|
|
186
|
+
console.log('Modifying file...');
|
|
187
|
+
writeFileSync(testFile, 'modified ' + Date.now());
|
|
188
|
+
}, 500);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Test 4: Verify fs.watch with recursive option
|
|
193
|
+
async function testFsWatchRecursive() {
|
|
194
|
+
console.log('\n=== Test 4: fs.watch with recursive option ===');
|
|
195
|
+
|
|
196
|
+
const { watch } = await import('fs');
|
|
197
|
+
const testDir = '/tmp/fswatch-recursive-test';
|
|
198
|
+
|
|
199
|
+
if (existsSync(testDir)) rmSync(testDir, { recursive: true });
|
|
200
|
+
mkdirSync(testDir, { recursive: true });
|
|
201
|
+
writeFileSync(join(testDir, 'file.txt'), 'initial');
|
|
202
|
+
|
|
203
|
+
return new Promise((resolve, reject) => {
|
|
204
|
+
const timeout = setTimeout(() => {
|
|
205
|
+
watcher.close();
|
|
206
|
+
console.log('FAIL: fs.watch recursive did not detect file change');
|
|
207
|
+
resolve(false); // Don't reject, just report failure
|
|
208
|
+
}, 3000);
|
|
209
|
+
|
|
210
|
+
let detected = false;
|
|
211
|
+
const watcher = watch(testDir, { recursive: true }, (eventType, filename) => {
|
|
212
|
+
if (!detected) {
|
|
213
|
+
detected = true;
|
|
214
|
+
console.log('fs.watch recursive detected:', eventType, filename);
|
|
215
|
+
clearTimeout(timeout);
|
|
216
|
+
watcher.close();
|
|
217
|
+
resolve(true);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
watcher.on('error', (err) => {
|
|
222
|
+
console.log('fs.watch error:', err.message);
|
|
223
|
+
clearTimeout(timeout);
|
|
224
|
+
resolve(false);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Modify file after short delay
|
|
228
|
+
setTimeout(() => {
|
|
229
|
+
console.log('Modifying file in watched directory...');
|
|
230
|
+
writeFileSync(join(testDir, 'file.txt'), 'modified ' + Date.now());
|
|
231
|
+
}, 500);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Run all tests
|
|
236
|
+
async function runTests() {
|
|
237
|
+
console.log('Live Reload Test Suite');
|
|
238
|
+
console.log('======================');
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
// Test basic fs.watch first
|
|
242
|
+
const fsWatchWorks = await testFsWatch();
|
|
243
|
+
console.log('Test 3 result: fs.watch works =', fsWatchWorks);
|
|
244
|
+
|
|
245
|
+
// Test recursive fs.watch
|
|
246
|
+
const fsWatchRecursiveWorks = await testFsWatchRecursive();
|
|
247
|
+
console.log('Test 4 result: fs.watch recursive works =', fsWatchRecursiveWorks);
|
|
248
|
+
|
|
249
|
+
// Test HTTP PUT notification
|
|
250
|
+
await testHttpPutNotification();
|
|
251
|
+
console.log('Test 1 result: PASSED');
|
|
252
|
+
|
|
253
|
+
// Test file watcher notification
|
|
254
|
+
await testFileWatcherNotification();
|
|
255
|
+
console.log('Test 2 result: PASSED');
|
|
256
|
+
|
|
257
|
+
console.log('\n=== All tests passed ===');
|
|
258
|
+
} catch (err) {
|
|
259
|
+
console.error('\n=== Test FAILED ===');
|
|
260
|
+
console.error(err.message);
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
runTests();
|