Conneg: recognise .acl and .meta as RDF (#294) — unblocks umai#296
Merged
melvincarvalho merged 1 commit intoApr 22, 2026
Conversation
…Server#294) getContentType() in src/utils/url.js is extension-based, but path.extname() returns '' for leading-dot names like '.acl' / '.meta' (node treats them as dotfiles, not extensions). Both fell through to application/octet-stream, which isRdfContentType() rejects, so handleGet's conneg branch never ran for them. Effect: Turtle-native clients (umai, Soukai-based apps, older Solid tooling) fetching <container>/.meta got JSON-LD back and errored with 'Malformed Turtle document — Expected entity but got { on line 2'. Hit today against ewingson's test.solidweb.app pod. Fix: one basename check in getContentType mapping '.acl' / '.meta' to application/ld+json — the format JSS already writes them in (via serializeAcl(), createPodStructure(), etc.). Conneg then handles translation for Turtle clients through handleGet's existing JSON-LD-to-Turtle path, and handlePut already converts incoming Turtle to JSON-LD before storing when --conneg is on. Zero migration: existing pods on disk stay as-is and become Turtle-servable immediately. Tests: - test/url.test.js: new 'getContentType' describe block — 6 cases (extension mapping, dotfile mapping, the 'acl-in-filename isn't an ACL file' guard). - test/conneg.test.js: new 'Solid convention dotfiles (JavaScriptSolidServer#294)' block — 3 integration cases against a running server (GET .meta default → ld+json, GET .meta with Accept: turtle → turtle, PUT turtle .meta → round-trips to JSON-LD). Full suite: 407/407 pass (was 398 + 9 new). The sibling tightening — reject non-JSON-LD PUTs with 415 when --conneg is off — is tracked separately as JavaScriptSolidServer#295 to avoid conflating a visible interop break with a design-ier purity fix. Fixes JavaScriptSolidServer#294
There was a problem hiding this comment.
Pull request overview
Fixes Solid conneg for convention dotfiles (.acl, .meta) by ensuring they’re recognized as RDF (JSON-LD) despite Node’s path.extname() returning '' for leading-dot filenames, which previously prevented the conneg branch from running for these resources.
Changes:
- Update
getContentType()to special-case basenames.acland.metaasapplication/ld+json. - Add unit tests for
getContentType()covering standard extension mapping and the dotfile regression. - Add conneg integration tests validating
.metaGET/PUT round-trips withAccept: text/turtleand JSON-LD defaults.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/utils/url.js |
Recognizes .acl/.meta via basename and returns JSON-LD content-type so conneg can apply. |
test/url.test.js |
Adds unit coverage for getContentType() including .acl/.meta regression cases. |
test/conneg.test.js |
Adds integration coverage ensuring .meta negotiates to Turtle and Turtle PUTs are stored as JSON-LD under conneg. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
melvincarvalho
merged commit Apr 22, 2026
a0a9ccd
into
JavaScriptSolidServer:gh-pages
4 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #294.
Summary
`getContentType()` in `src/utils/url.js` is extension-based, but `path.extname()` returns `''` for leading-dot names like `.acl` / `.meta` (node treats them as dotfiles). Both were falling through to `application/octet-stream`, which `isRdfContentType()` rejects, so `handleGet`'s conneg branch never ran for them.
Visible break: Turtle-native clients (umai, Soukai-based apps, older Solid tooling) fetching `/.meta` got JSON-LD back and errored with `MalformedSolidDocumentError: Malformed Turtle document — Expected entity but got { on line 2`. Hit today against ewingson's `test.solidweb.app` pod (JSS 0.0.144, `conneg: true`).
Latent twin: same bug applies to `.acl`. Hasn't shown up yet because (a) JSS's internal `parseAcl()` is forgiving (tries JSON then Turtle), and (b) most clients read `wac-allow` instead of fetching `.acl` directly. But any strict-Turtle ACL editor pointed at any existing JSS pod would hit the same parser error.
Change
One basename check in `getContentType`:
```js
const base = path.basename(filePath);
if (base === '.acl' || base === '.meta') return 'application/ld+json';
```
Mapped to `application/ld+json` (not `text/turtle`) because that's the format JSS already writes them in (`serializeAcl()`, `createPodStructure()`). Conneg handles translation for Turtle clients through `handleGet`'s existing JSON-LD-to-Turtle path, and `handlePut` already converts incoming Turtle to JSON-LD before storing when `--conneg` is on.
Zero migration: existing pods keep their JSON-LD-stored `.acl` / `.meta` files on disk. They become Turtle-servable immediately, no rewrite.
Round-trip table
Files
Test plan
Relation to #295
#295 proposes a sibling tightening: when `--conneg` is off, reject non-JSON-LD PUTs with 415 instead of silently storing corrupted bytes. That's a design-ier purity fix; deliberately kept separate from this PR so the real interop blocker (umai) ships first without getting stalled on 415-response bikeshed.