Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ jss --help # Show help
| `--base-domain <domain>` | Base domain for subdomains | - |
| `--mashlib` | Enable Mashlib (local mode) | false |
| `--mashlib-cdn` | Enable Mashlib (CDN mode) | false |
| `--mashlib-module <url>` | Enable ES module data browser from a URL | - |
| `--mashlib-version <ver>` | Mashlib CDN version | 2.0.0 |
| `--solidos-ui` | Enable modern SolidOS UI (requires --mashlib) | false |
| `--git` | Enable Git HTTP backend | false |
Expand Down Expand Up @@ -161,6 +162,7 @@ export JSS_CONNEG=true
export JSS_SUBDOMAINS=true
export JSS_BASE_DOMAIN=example.com
export JSS_MASHLIB=true
export JSS_MASHLIB_MODULE=https://example.com/mashlib.js
export JSS_NOSTR=true
export JSS_INVITE_ONLY=true
export JSS_WEBID_TLS=true
Expand Down Expand Up @@ -367,6 +369,12 @@ cd src/mashlib-local
npm install && npm run build
```

**ES Module Mode** (for custom or next-gen mashlib builds):
```bash
jss start --mashlib-module https://example.com/mashlib.js
```
Loads an ES module-based data browser from any URL. Uses `<script type="module">` and `<div id="mashlib">` (self-initializing). CSS is auto-derived by replacing `.js` with `.css`. Content negotiation is auto-enabled.

**How it works:**
1. Browser requests `/alice/public/data.ttl` with `Accept: text/html`
2. Server returns Mashlib HTML wrapper
Expand Down
3 changes: 3 additions & 0 deletions bin/jss.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ program
.option('--base-domain <domain>', 'Base domain for subdomain pods (e.g., "example.com")')
.option('--mashlib', 'Enable Mashlib data browser (local mode, requires mashlib in node_modules)')
.option('--mashlib-cdn', 'Enable Mashlib data browser (CDN mode, no local files needed)')
.option('--mashlib-module <url>', 'Enable ES module data browser from a URL')
.option('--no-mashlib', 'Disable Mashlib data browser')
.option('--mashlib-version <version>', 'Mashlib version for CDN mode (default: 2.0.0)')
.option('--solidos-ui', 'Enable modern Nextcloud-style UI (requires --mashlib)')
Expand Down Expand Up @@ -123,6 +124,7 @@ program
mashlib: config.mashlib || config.mashlibCdn,
mashlibCdn: config.mashlibCdn,
mashlibVersion: config.mashlibVersion,
mashlibModule: config.mashlibModule,
solidosUi: config.solidosUi,
git: config.git,
nostr: config.nostr,
Expand Down Expand Up @@ -158,6 +160,7 @@ program
} else if (config.mashlib) {
console.log(` Mashlib: local (data browser enabled)`);
}
if (config.mashlibModule) console.log(` Mashlib module: ${config.mashlibModule}`);
if (config.solidosUi) console.log(' SolidOS UI: enabled (modern interface)');
if (config.git) console.log(' Git: enabled (clone/push support)');
if (config.nostr) console.log(` Nostr: enabled (${config.nostrPath})`);
Expand Down
6 changes: 4 additions & 2 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const defaults = {
mashlib: false,
mashlibCdn: false,
mashlibVersion: '2.0.0',
mashlibModule: false,

// SolidOS UI (modern Nextcloud-style interface)
solidosUi: false,
Expand Down Expand Up @@ -113,6 +114,7 @@ const envMap = {
JSS_MASHLIB: 'mashlib',
JSS_MASHLIB_CDN: 'mashlibCdn',
JSS_MASHLIB_VERSION: 'mashlibVersion',
JSS_MASHLIB_MODULE: 'mashlibModule',
JSS_SOLIDOS_UI: 'solidosUi',
JSS_GIT: 'git',
JSS_NOSTR: 'nostr',
Expand Down Expand Up @@ -238,7 +240,7 @@ export async function loadConfig(cliOptions = {}, configFile = null) {
}

// Mashlib requires content negotiation for Turtle support
if (config.mashlib || config.mashlibCdn) {
if (config.mashlib || config.mashlibCdn || config.mashlibModule) {
config.conneg = true;
}

Expand Down Expand Up @@ -293,7 +295,7 @@ export function printConfig(config) {
console.log(` Notifications: ${config.notifications}`);
console.log(` IdP: ${config.idp ? (config.idpIssuer || 'enabled') : 'disabled'}`);
console.log(` Subdomains: ${config.subdomains ? (config.baseDomain || 'enabled') : 'disabled'}`);
console.log(` Mashlib: ${config.mashlibCdn ? `CDN v${config.mashlibVersion}` : config.mashlib ? 'local' : 'disabled'}`);
console.log(` Mashlib: ${config.mashlibModule ? `module (${config.mashlibModule})` : config.mashlibCdn ? `CDN v${config.mashlibVersion}` : config.mashlib ? 'local' : 'disabled'}`);
console.log(` SolidOS UI: ${config.solidosUi ? 'enabled' : 'disabled'}`);
console.log('─'.repeat(40));
}
14 changes: 9 additions & 5 deletions src/handlers/resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from '../rdf/conneg.js';
import { emitChange } from '../notifications/events.js';
import { checkIfMatch, checkIfNoneMatchForGet, checkIfNoneMatchForWrite } from '../utils/conditional.js';
import { generateDatabrowserHtml, generateSolidosUiHtml, shouldServeMashlib } from '../mashlib/index.js';
import { generateDatabrowserHtml, generateModuleDatabrowserHtml, generateSolidosUiHtml, shouldServeMashlib } from '../mashlib/index.js';

/**
* Live reload script - injected into HTML when --live-reload is enabled
Expand Down Expand Up @@ -230,10 +230,12 @@ export async function handleGet(request, reply) {

// Check if we should serve Mashlib data browser for containers
if (shouldServeMashlib(request, request.mashlibEnabled, 'application/ld+json')) {
// Use SolidOS UI if enabled, otherwise fallback to classic mashlib
// Use SolidOS UI if enabled, ES module if configured, otherwise classic mashlib
const html = request.solidosUiEnabled
? generateSolidosUiHtml()
: generateDatabrowserHtml(resourceUrl, request.mashlibCdn ? request.mashlibVersion : null);
: request.mashlibModule
? generateModuleDatabrowserHtml(request.mashlibModule)
: generateDatabrowserHtml(resourceUrl, request.mashlibCdn ? request.mashlibVersion : null);
const headers = getAllHeaders({
isContainer: true,
etag: stats.etag,
Expand Down Expand Up @@ -307,10 +309,12 @@ export async function handleGet(request, reply) {
// Check if we should serve Mashlib data browser
// Only for RDF resources when Accept: text/html is requested
if (shouldServeMashlib(request, request.mashlibEnabled, storedContentType)) {
// Use SolidOS UI if enabled, otherwise fallback to classic mashlib
// Use SolidOS UI if enabled, ES module if configured, otherwise classic mashlib
const html = request.solidosUiEnabled
? generateSolidosUiHtml()
: generateDatabrowserHtml(resourceUrl, request.mashlibCdn ? request.mashlibVersion : null);
: request.mashlibModule
? generateModuleDatabrowserHtml(request.mashlibModule)
: generateDatabrowserHtml(resourceUrl, request.mashlibCdn ? request.mashlibVersion : null);
const headers = getAllHeaders({
isContainer: false,
etag: stats.etag,
Expand Down
17 changes: 17 additions & 0 deletions src/mashlib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,23 @@ export function generateDatabrowserHtml(resourceUrl, cdnVersion = null) {
})</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>`;
}

/**
* Generate ES module-based databrowser HTML
*
* @param {string} moduleUrl - URL to the ES module entry point
* @returns {string} HTML content
*/
export function generateModuleDatabrowserHtml(moduleUrl) {
const cssUrl = moduleUrl.replace(/\.js$/, '.css');
return `<!doctype html><html lang="en"><head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Solid Data Browser</title>
<link rel="stylesheet" href="${cssUrl}"></head>
<body><div id="mashlib"></div>
<script type="module" src="${moduleUrl}"></script>
</body></html>`;
}

/**
* Check if request wants HTML and mashlib should handle it
* @param {object} request - Fastify request
Expand Down
6 changes: 5 additions & 1 deletion src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ export function createServer(options = {}) {
const baseDomain = options.baseDomain || null;
// Mashlib data browser is OFF by default
// mashlibCdn: if true, load from CDN; if false, serve locally
const mashlibEnabled = options.mashlib ?? false;
// mashlibModule: URL to ES module entry point (alternative to classic mashlib)
const mashlibModule = options.mashlibModule ?? false;
const mashlibEnabled = options.mashlib || !!mashlibModule;
const mashlibCdn = options.mashlibCdn ?? false;
const mashlibVersion = options.mashlibVersion ?? '2.0.0';
// SolidOS UI (modern Nextcloud-style interface) - requires mashlib
Expand Down Expand Up @@ -147,6 +149,7 @@ export function createServer(options = {}) {
fastify.decorateRequest('mashlibEnabled', null);
fastify.decorateRequest('mashlibCdn', null);
fastify.decorateRequest('mashlibVersion', null);
fastify.decorateRequest('mashlibModule', null);
fastify.decorateRequest('solidosUiEnabled', null);
fastify.decorateRequest('defaultQuota', null);
fastify.decorateRequest('config', null);
Expand All @@ -160,6 +163,7 @@ export function createServer(options = {}) {
request.mashlibEnabled = mashlibEnabled;
request.mashlibCdn = mashlibCdn;
request.mashlibVersion = mashlibVersion;
request.mashlibModule = mashlibModule;
request.solidosUiEnabled = solidosUiEnabled;
request.defaultQuota = defaultQuota;
request.config = { public: options.public, readOnly: options.readOnly };
Expand Down