/**
* LDP (Linked Data Platform) header utilities
*/
import { getAcceptHeaders, getVaryHeader } from '../rdf/conneg.js';
const LDP = 'http://www.w3.org/ns/ldp#';
/**
* Get Link headers for a resource
* @param {boolean} isContainer
* @param {string} aclUrl - URL to the ACL resource
* @returns {string}
*/
export function getLinkHeader(isContainer, aclUrl = null) {
const links = [`; rel="type"`];
if (isContainer) {
links.push(`; rel="type"`);
links.push(`; rel="type"`);
}
// Add acl link for auxiliary resource discovery
if (aclUrl) {
links.push(`; rel="acl"`);
}
return links.join(', ');
}
/**
* Get the ACL URL for a resource
* @param {string} resourceUrl - Full URL of the resource
* @param {boolean} isContainer - Whether the resource is a container
* @returns {string} ACL URL
*/
export function getAclUrl(resourceUrl, isContainer) {
if (isContainer) {
// Container ACL: /path/.acl
const base = resourceUrl.endsWith('/') ? resourceUrl : resourceUrl + '/';
return base + '.acl';
}
// Resource ACL: /path/file.acl
return resourceUrl + '.acl';
}
/**
* Get standard LDP response headers
* @param {object} options
* @returns {object}
*/
export function getResponseHeaders({ isContainer = false, etag = null, contentType = null, resourceUrl = null, wacAllow = null, connegEnabled = false, mashlibEnabled = false, updatesVia = null }) {
// Calculate ACL URL if resource URL provided
const aclUrl = resourceUrl ? getAclUrl(resourceUrl, isContainer) : null;
const headers = {
'Link': getLinkHeader(isContainer, aclUrl),
'Accept-Patch': 'text/n3, application/sparql-update',
'Accept-Ranges': isContainer ? 'none' : 'bytes',
'Allow': 'GET, HEAD, PUT, DELETE, PATCH, OPTIONS' + (isContainer ? ', POST' : ''),
'Vary': getVaryHeader(connegEnabled, mashlibEnabled)
};
// Only set WAC-Allow if explicitly provided (otherwise the auth hook sets it)
if (wacAllow) {
headers['WAC-Allow'] = wacAllow;
}
// Add Accept-* headers (conneg-aware)
const acceptHeaders = getAcceptHeaders(connegEnabled, isContainer);
Object.assign(headers, acceptHeaders);
// Add Updates-Via header for WebSocket notifications discovery
if (updatesVia) {
headers['Updates-Via'] = updatesVia;
}
if (etag) {
headers['ETag'] = etag;
}
if (contentType) {
headers['Content-Type'] = contentType;
}
return headers;
}
/**
* Get CORS headers
* @param {string} origin
* @returns {object}
*/
export function getCorsHeaders(origin) {
return {
'Access-Control-Allow-Origin': origin || '*',
'Access-Control-Allow-Methods': 'GET, HEAD, POST, PUT, DELETE, PATCH, OPTIONS',
'Access-Control-Allow-Headers': 'Accept, Authorization, Content-Type, DPoP, If-Match, If-None-Match, Link, Range, Slug, Origin',
'Access-Control-Expose-Headers': 'Accept-Patch, Accept-Post, Accept-Ranges, Allow, Content-Length, Content-Range, Content-Type, ETag, Link, Location, Updates-Via, WAC-Allow, X-Cost, X-Balance, X-Pay-Currency',
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Max-Age': '86400'
};
}
/**
* Get all headers combined
* @param {object} options
* @returns {object}
*/
export function getAllHeaders({ isContainer = false, etag = null, contentType = null, origin = null, resourceUrl = null, wacAllow = null, connegEnabled = false, mashlibEnabled = false, updatesVia = null }) {
return {
...getResponseHeaders({ isContainer, etag, contentType, resourceUrl, wacAllow, connegEnabled, mashlibEnabled, updatesVia }),
...getCorsHeaders(origin)
};
}
/**
* Get headers for 404 responses (non-existent resources)
* These headers tell clients what methods are supported for creating the resource
* @param {object} options
* @returns {object}
*/
export function getNotFoundHeaders({ resourceUrl = null, origin = null, connegEnabled = false, mashlibEnabled = false }) {
// Determine if this would be a container based on URL ending with /
const isContainer = resourceUrl?.endsWith('/') || false;
const aclUrl = resourceUrl ? getAclUrl(resourceUrl, isContainer) : null;
// Get Accept-* headers
const acceptHeaders = getAcceptHeaders(connegEnabled, isContainer);
const headers = {
...getCorsHeaders(origin),
'Link': aclUrl ? `; rel="acl"` : '',
'Accept-Patch': 'text/n3, application/sparql-update',
'Accept-Put': acceptHeaders['Accept-Put'] || 'application/ld+json, */*',
'Allow': 'GET, HEAD, PUT, PATCH, OPTIONS' + (isContainer ? ', POST' : ''),
'Vary': getVaryHeader(connegEnabled, mashlibEnabled)
};
if (isContainer && acceptHeaders['Accept-Post']) {
headers['Accept-Post'] = acceptHeaders['Accept-Post'];
}
return headers;
}