javascript-solid-server 0.0.145 → 0.0.147

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.
@@ -350,7 +350,8 @@
350
350
  "Bash(\\\\\"git config:*)",
351
351
  "Read(//usr/local/lib/node_modules/gitmark-test/**)",
352
352
  "WebFetch(domain:nip98.com)",
353
- "WebFetch(domain:htmlpreview.github.io)"
353
+ "WebFetch(domain:htmlpreview.github.io)",
354
+ "WebFetch(domain:timbl.solidcommunity.net)"
354
355
  ]
355
356
  }
356
357
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "javascript-solid-server",
3
- "version": "0.0.145",
3
+ "version": "0.0.147",
4
4
  "description": "A minimal, fast Solid server",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
@@ -197,10 +197,14 @@ export async function createPodStructure(name, webId, podUri, issuer, defaultQuo
197
197
  const privateAcl = generatePrivateAcl(`${podUri}private/`, webId);
198
198
  await storage.write(`${podPath}private/.acl`, serializeAcl(privateAcl));
199
199
 
200
- // settings folder: owner only
200
+ // settings folder: owner only (contains private preferences)
201
201
  const settingsAcl = generatePrivateAcl(`${podUri}settings/`, webId);
202
202
  await storage.write(`${podPath}settings/.acl`, serializeAcl(settingsAcl));
203
203
 
204
+ // publicTypeIndex: public read, overrides the private default inherited from /settings/
205
+ const publicTypeIndexAcl = generateOwnerAcl(`${podUri}settings/publicTypeIndex.jsonld`, webId, false);
206
+ await storage.write(`${podPath}settings/publicTypeIndex.jsonld.acl`, serializeAcl(publicTypeIndexAcl));
207
+
204
208
  // Inbox: owner full, public append
205
209
  const inboxAcl = generateInboxAcl(`${podUri}inbox/`, webId);
206
210
  await storage.write(`${podPath}inbox/.acl`, serializeAcl(inboxAcl));
package/src/server.js CHANGED
@@ -622,6 +622,10 @@ export function createServer(options = {}) {
622
622
  const settingsAcl = generatePrivateAcl(`${podUri}settings/`, webId);
623
623
  await storage.write('/settings/.acl', serializeAcl(settingsAcl));
624
624
 
625
+ // publicTypeIndex: public read, overrides the private default inherited from /settings/
626
+ const publicTypeIndexAcl = generateOwnerAcl(`${podUri}settings/publicTypeIndex.jsonld`, webId, false);
627
+ await storage.write('/settings/publicTypeIndex.jsonld.acl', serializeAcl(publicTypeIndexAcl));
628
+
625
629
  const inboxAcl = generateInboxAcl(`${podUri}inbox/`, webId);
626
630
  await storage.write('/inbox/.acl', serializeAcl(inboxAcl));
627
631
 
package/src/utils/url.js CHANGED
@@ -237,7 +237,10 @@ export function getContentType(filePath) {
237
237
  '.md': 'text/markdown',
238
238
  '.m3u': 'audio/mpegurl',
239
239
  '.m3u8': 'application/vnd.apple.mpegurl',
240
- '.pls': 'audio/x-scpls'
240
+ '.pls': 'audio/x-scpls',
241
+ // Solid ACL/meta as extensions (e.g. publicTypeIndex.jsonld.acl)
242
+ '.acl': 'application/ld+json',
243
+ '.meta': 'application/ld+json'
241
244
  };
242
245
 
243
246
  // Solid convention dotfiles (.acl, .meta) are RDF resources. path.extname
@@ -24,7 +24,6 @@ const PIM = 'http://www.w3.org/ns/pim/space#';
24
24
  */
25
25
  export function generateProfileJsonLd({ webId, name, podUri, issuer }) {
26
26
  const pod = podUri.endsWith('/') ? podUri : podUri + '/';
27
- const profileDoc = webId.split('#')[0];
28
27
 
29
28
  return {
30
29
  '@context': {
@@ -39,12 +38,14 @@ export function generateProfileJsonLd({ webId, name, podUri, issuer }) {
39
38
  'preferencesFile': { '@id': 'pim:preferencesFile', '@type': '@id' },
40
39
  'publicTypeIndex': { '@id': 'solid:publicTypeIndex', '@type': '@id' },
41
40
  'privateTypeIndex': { '@id': 'solid:privateTypeIndex', '@type': '@id' },
41
+ 'isPrimaryTopicOf': { '@id': 'foaf:isPrimaryTopicOf', '@type': '@id' },
42
42
  'mainEntityOfPage': { '@id': 'schema:mainEntityOfPage', '@type': '@id' }
43
43
  },
44
44
  '@id': webId,
45
45
  '@type': ['foaf:Person', 'schema:Person'],
46
46
  'foaf:name': name,
47
- 'mainEntityOfPage': profileDoc,
47
+ 'isPrimaryTopicOf': '',
48
+ 'mainEntityOfPage': '',
48
49
  'inbox': `${pod}inbox/`,
49
50
  'storage': pod,
50
51
  'oidcIssuer': issuer,
package/test/idp.test.js CHANGED
@@ -409,6 +409,55 @@ describe('Identity Provider - Single-user mode landing', () => {
409
409
  });
410
410
  });
411
411
 
412
+ // Root-level pod (singleUserName: '/') — verifies createRootPodStructure wires
413
+ // publicTypeIndex as public-read and privateTypeIndex as owner-only.
414
+ // Regression coverage for #297.
415
+ describe('Identity Provider - Root pod type index ACLs', () => {
416
+ let server;
417
+ let baseUrl;
418
+ const ROOT_POD_DATA_DIR = './test-data-idp-root-pod';
419
+
420
+ before(async () => {
421
+ await fs.remove(ROOT_POD_DATA_DIR);
422
+ await fs.ensureDir(ROOT_POD_DATA_DIR);
423
+
424
+ const port = await getAvailablePort();
425
+ baseUrl = `http://${TEST_HOST}:${port}`;
426
+
427
+ server = createServer({
428
+ logger: false,
429
+ root: ROOT_POD_DATA_DIR,
430
+ idp: true,
431
+ idpIssuer: baseUrl,
432
+ singleUser: true,
433
+ singleUserName: '/',
434
+ forceCloseConnections: true,
435
+ });
436
+
437
+ await server.listen({ port, host: TEST_HOST });
438
+ });
439
+
440
+ after(async () => {
441
+ await server.close();
442
+ await fs.remove(ROOT_POD_DATA_DIR);
443
+ });
444
+
445
+ it('publicTypeIndex is readable without auth', async () => {
446
+ const res = await fetch(`${baseUrl}/settings/publicTypeIndex.jsonld`);
447
+ assert.strictEqual(res.status, 200);
448
+ });
449
+
450
+ it('privateTypeIndex requires auth', async () => {
451
+ const res = await fetch(`${baseUrl}/settings/privateTypeIndex.jsonld`);
452
+ assert.strictEqual(res.status, 401);
453
+ });
454
+
455
+ it('prefs requires auth', async () => {
456
+ const res = await fetch(`${baseUrl}/settings/prefs.jsonld`);
457
+ assert.strictEqual(res.status, 401);
458
+ });
459
+ });
460
+
412
461
  describe('Identity Provider - Accounts', () => {
413
462
  let server;
414
463
  let accountsUrl;
package/test/pod.test.js CHANGED
@@ -115,5 +115,21 @@ describe('Pod Lifecycle', () => {
115
115
  const privIndex = await request('/dan/settings/privateTypeIndex.jsonld', { auth: 'dan' });
116
116
  assertStatus(privIndex, 200);
117
117
  });
118
+
119
+ it('should make publicTypeIndex publicly readable but keep privateTypeIndex private', async () => {
120
+ await createTestPod('elsa');
121
+
122
+ // publicTypeIndex: no auth required (per Solid Type Indexes spec)
123
+ const pubIndex = await request('/elsa/settings/publicTypeIndex.jsonld');
124
+ assertStatus(pubIndex, 200);
125
+
126
+ // privateTypeIndex: auth required
127
+ const privIndex = await request('/elsa/settings/privateTypeIndex.jsonld');
128
+ assertStatus(privIndex, 401);
129
+
130
+ // prefs: auth required (private by inheritance from /settings/)
131
+ const prefs = await request('/elsa/settings/prefs.jsonld');
132
+ assertStatus(prefs, 401);
133
+ });
118
134
  });
119
135
  });
package/test/url.test.js CHANGED
@@ -106,4 +106,15 @@ describe('getContentType', () => {
106
106
  assert.strictEqual(getContentType('/alice/notes/my-acl-plan.md'), 'text/markdown');
107
107
  });
108
108
  });
109
+
110
+ describe('.acl / .meta as extensions (#297)', () => {
111
+ it('treats *.acl (extension) as application/ld+json', () => {
112
+ assert.strictEqual(getContentType('/settings/publicTypeIndex.jsonld.acl'), 'application/ld+json');
113
+ assert.strictEqual(getContentType('/alice/private/secret.json.acl'), 'application/ld+json');
114
+ });
115
+
116
+ it('treats *.meta (extension) as application/ld+json', () => {
117
+ assert.strictEqual(getContentType('/alice/resource.meta'), 'application/ld+json');
118
+ });
119
+ });
109
120
  });
@@ -83,11 +83,20 @@ describe('WebID Profile', () => {
83
83
  assert.ok(jsonLd['inbox'].endsWith('/webidtest/inbox/'), 'Should have inbox');
84
84
  });
85
85
 
86
- it('should have mainEntityOfPage', async () => {
86
+ it('should have mainEntityOfPage pointing to the document', async () => {
87
87
  const res = await request(profilePath);
88
88
  const jsonLd = await res.json();
89
89
 
90
- assert.ok(jsonLd['mainEntityOfPage'], 'Should have mainEntityOfPage');
90
+ // Empty string is a relative URI reference to the document itself (JSON-LD)
91
+ assert.strictEqual(jsonLd['mainEntityOfPage'], '', 'mainEntityOfPage should be "" (self)');
92
+ });
93
+
94
+ it('should have isPrimaryTopicOf pointing to the document', async () => {
95
+ const res = await request(profilePath);
96
+ const jsonLd = await res.json();
97
+
98
+ // Empty string is a relative URI reference to the document itself (JSON-LD)
99
+ assert.strictEqual(jsonLd['isPrimaryTopicOf'], '', 'isPrimaryTopicOf should be "" (self)');
91
100
  });
92
101
  });
93
102