-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathindex.js
More file actions
106 lines (96 loc) · 4.09 KB
/
index.js
File metadata and controls
106 lines (96 loc) · 4.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/**
* Mashlib Data Browser Integration
*
* Generates HTML wrapper that loads SolidOS Mashlib from CDN.
* When a browser requests an RDF resource with Accept: text/html,
* we return this wrapper which then fetches and renders the data.
*/
/**
* Generate Mashlib databrowser HTML
*
* @param {string} resourceUrl - The URL of the resource being viewed (unused, kept for API compatibility)
* @param {string} cdnVersion - If provided, load mashlib from unpkg CDN (e.g., "2.0.0")
* @returns {string} HTML content
*/
export function generateDatabrowserHtml(resourceUrl, cdnVersion = null) {
if (cdnVersion) {
// CDN mode - use script.onload to ensure mashlib is fully loaded before init
// This avoids race conditions with defer + DOMContentLoaded
const cdnBase = `https://unpkg.com/mashlib@${cdnVersion}/dist`;
return `<!doctype html><html><head><meta charset="utf-8"/><title>SolidOS Web App</title>
<link href="${cdnBase}/mash.css" rel="stylesheet"></head>
<body id="PageBody"><header id="PageHeader"></header>
<div class="TabulatorOutline" id="DummyUUID" role="main"><table id="outline"></table><div id="GlobalDashboard"></div></div>
<footer id="PageFooter"></footer>
<script>
(function() {
var s = document.createElement('script');
s.src = '${cdnBase}/mashlib.min.js';
s.onload = function() { panes.runDataBrowser(); };
s.onerror = function() { document.body.innerHTML = '<p>Failed to load Mashlib from CDN</p>'; };
document.head.appendChild(s);
})();
</script></body></html>`;
}
// Local mode - use defer (reliable when served locally)
return `<!doctype html><html><head><meta charset="utf-8"/><title>SolidOS Web App</title><script>document.addEventListener('DOMContentLoaded', function() {
panes.runDataBrowser()
})</script><script defer="defer" src="/mashlib.min.js"></script><link href="/mash.css" rel="stylesheet"></head><body id="PageBody"><header id="PageHeader"></header><div class="TabulatorOutline" id="DummyUUID" role="main"><table id="outline"></table><div id="GlobalDashboard"></div></div><footer id="PageFooter"></footer></body></html>`;
}
/**
* Check if request wants HTML and mashlib should handle it
* @param {object} request - Fastify request
* @param {boolean} mashlibEnabled - Whether mashlib is enabled
* @param {string} contentType - Content type of the resource
* @returns {boolean}
*/
export function shouldServeMashlib(request, mashlibEnabled, contentType) {
const accept = request.headers.accept || '';
const secFetchDest = request.headers['sec-fetch-dest'] || '';
if (!mashlibEnabled) {
return false;
}
// Only serve mashlib for top-level document navigation
// sec-fetch-dest: 'document' = browser navigation (serve mashlib)
// sec-fetch-dest: 'empty' = JavaScript fetch/XHR (serve RDF data)
if (secFetchDest && secFetchDest !== 'document') {
return false;
}
// Must explicitly accept HTML as a primary type (not via */*)
// Browser navigation: "text/html,application/xhtml+xml,..."
// Mashlib fetch: "application/rdf+xml;q=0.9, */*;q=0.1,..."
if (!accept.includes('text/html')) {
return false;
}
// Don't serve mashlib if RDF types appear BEFORE text/html in Accept header
// This handles cases like "application/rdf+xml, text/html" where RDF is preferred
const htmlPos = accept.indexOf('text/html');
const acceptRdfTypes = ['application/rdf+xml', 'text/turtle', 'application/ld+json', 'text/n3', 'application/n-triples'];
for (const rdfType of acceptRdfTypes) {
const rdfPos = accept.indexOf(rdfType);
if (rdfPos !== -1 && rdfPos < htmlPos) {
return false; // RDF type is preferred over HTML
}
}
// Only serve mashlib for RDF content types
const rdfTypes = [
'text/turtle',
'application/ld+json',
'application/json',
'text/n3',
'application/n-triples',
'application/rdf+xml'
];
const baseType = contentType.split(';')[0].trim().toLowerCase();
return rdfTypes.includes(baseType);
}
/**
* Escape HTML special characters
*/
function escapeHtml(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}