Skip to content
Open

Esm #1834

Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
6f6e708
mixed ESM
bourgeoa Nov 13, 2025
6e0b966
more mixed for app
bourgeoa Nov 13, 2025
78c0558
more mixed for app and test
bourgeoa Nov 13, 2025
f3f3968
index.mjs
bourgeoa Nov 13, 2025
d5d0e75
Fix line endings in bin/solid for Docker compatibility
Nov 13, 2025
13e5054
package.json
Nov 13, 2025
0357fea
.gitattributes
Nov 13, 2025
5b65f43
test/ migrated to esm in test-esm/
Nov 13, 2025
ac4eec7
ESM Migration: Convert main lib files and lib/api to ESM
Nov 13, 2025
73b2626
ESM Migration: Complete lib/api/ and lib/handlers/ conversion
Nov 13, 2025
277633e
Add ESM test command to CI workflow
bourgeoa Nov 14, 2025
562a91b
add missing test-esm/resources
Nov 20, 2025
d2ebe52
update esm and tests
Nov 20, 2025
86da28f
add missing .mjs files
Nov 22, 2025
04fef95
missing mjs file
Nov 25, 2025
f3d0a1b
add esm entry point
Nov 25, 2025
60e2f93
missing content in ldp.mjs
Nov 25, 2025
53c8278
miscellaneous updates
Nov 25, 2025
635f7b2
updates lib/api
Nov 25, 2025
ebf457f
updates to lib/srvices
Nov 25, 2025
7bd6cdd
updates to lib/models
Nov 25, 2025
aba64d9
updates to lib/payment-pointer-discovery.mjs
Nov 25, 2025
b935bdf
updates to lib/ldp-container.mjs
Nov 25, 2025
3f536ab
updates to lib/requests/auth-request.mjs
Nov 25, 2025
beb6dad
updates to lib/rdf-notification-template.mjs
Nov 25, 2025
c668ad3
updates to lib/handlers
Nov 25, 2025
5c258b2
lib/server-config.mjs
Nov 25, 2025
b370ff7
updates .mjs
Nov 30, 2025
1bf6b36
Revert "updates .mjs"
Nov 30, 2025
fd1a323
fix critical node-forge
Nov 30, 2025
f8ff940
esm account-manager and test
Dec 1, 2025
1706ce9
add default-templates/emails .mjs
Dec 2, 2025
ec340b2
update unit test to esm
Dec 2, 2025
32e473c
return to CJS utils.js
Dec 2, 2025
7afef98
update to esm only
Dec 6, 2025
21798f6
lint issues
Dec 6, 2025
128fc7c
add nyc-esm
Dec 6, 2025
be487db
some cleaning
Dec 6, 2025
89395b3
standard lint issues
Dec 8, 2025
924d4b8
Merge branch 'main' into nss2esm-hybrid
bourgeoa Dec 8, 2025
1e3025d
update require() and debug's
bourgeoa Dec 21, 2025
3f1fba3
delete patch.mjs.old
bourgeoa Dec 21, 2025
6db0f28
updates
bourgeoa Dec 22, 2025
7610c97
resources/config
bourgeoa Dec 22, 2025
23f2403
rename test-esm to test
bourgeoa Dec 23, 2025
94454d1
standard issues
bourgeoa Dec 23, 2025
951d712
remove test-esm from workflow
bourgeoa Dec 23, 2025
9424280
rm *.js to keep *.mjs
bourgeoa Dec 23, 2025
e1b12bb
update bin/solid and import index.mjs
bourgeoa Dec 23, 2025
3544e9b
c8 in CI workflow
bourgeoa Dec 23, 2025
40da84a
add missing test files
bourgeoa Dec 23, 2025
c65a248
some cleaning
bourgeoa Dec 23, 2025
bb700b5
mashlib was not loading and server OIDC listening issue
bourgeoa Dec 24, 2025
f1cbcc9
npm publish
bourgeoa Dec 24, 2025
4d031e4
Change test suite runner to ubuntu-latest
bourgeoa Dec 24, 2025
822b18b
Refactor CI workflow by removing test-suite job
bourgeoa Dec 24, 2025
9436591
oidc to include ES256/384/512
bourgeoa Dec 27, 2025
954a10d
update patch tests to rdflib@2.3.2
bourgeoa Dec 27, 2025
c012117
Merge branch 'esm' of https://github.com/nodeSolidServer/node-solid-s…
bourgeoa Dec 27, 2025
f7d9560
replace node-fetch with node:fetch and update for isIp and some lints
bourgeoa Dec 27, 2025
ad1867f
remove standard and use eslint@9
bourgeoa Dec 27, 2025
f18e73e
npm run lint in CI/CD
bourgeoa Dec 27, 2025
bf760f9
update more dependencies
bourgeoa Dec 27, 2025
1185df4
eslint.config.mjs
bourgeoa Dec 27, 2025
b387f8c
rimraf and get-folder-size at @latest
bourgeoa Dec 27, 2025
c7c332a
http-proxy-middleware@3
bourgeoa Dec 27, 2025
b278bff
http-proxy-middleware comments
bourgeoa Dec 27, 2025
7eb7ffb
rfc9207 iss == idp
bourgeoa Jan 1, 2026
95a4630
package-lock.json security issue
bourgeoa Jan 1, 2026
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
Prev Previous commit
Next Next commit
ESM Migration: Convert main lib files and lib/api to ESM
- Create ESM versions (.mjs) for 7 main lib files:
  - capability-discovery.mjs
  - ldp-container.mjs
  - ldp.mjs (large 632-line file)
  - payment-pointer-discovery.mjs
  - rdf-notification-template.mjs
  - resource-mapper.mjs (227 lines)
  - server-config.mjs
- Convert lib/api/ directory to ESM:
  - index.mjs
  - accounts/user-accounts.mjs
  - authn/index.mjs
  - authn/force-user.mjs
- All files maintain dual CommonJS/ESM compatibility
- Proper import/export conversions with createRequire for CommonJS deps
- No errors detected in new ESM modules
- Total: 18 .mjs files now available in lib/ directory
  • Loading branch information
CI Fix
CI Fix committed Nov 13, 2025
commit ac4eec70a6d94532a9c0d582847486377ca1bd16
88 changes: 88 additions & 0 deletions lib/api/accounts/user-accounts.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import express from 'express'
import { urlencoded } from 'body-parser'
const bodyParser = urlencoded({ extended: false })
import debug from '../../debug.mjs'
const debugAccounts = debug.accounts

import restrictToTopDomain from '../../handlers/restrict-to-top-domain.mjs'

import CreateAccountRequest from '../../requests/create-account-request.mjs'
import AddCertificateRequest from '../../requests/add-cert-request.mjs'
import DeleteAccountRequest from '../../requests/delete-account-request.mjs'
import DeleteAccountConfirmRequest from '../../requests/delete-account-confirm-request.mjs'

/**
* Returns an Express middleware handler for checking if a particular account
* exists (used by Signup apps).
*
* @param accountManager {AccountManager}
*
* @return {Function}
*/
export function checkAccountExists (accountManager) {
return (req, res, next) => {
const accountUri = req.hostname

accountManager.accountUriExists(accountUri)
.then(found => {
if (!found) {
debugAccounts(`Account ${accountUri} is available (for ${req.originalUrl})`)
return res.sendStatus(404)
}
debugAccounts(`Account ${accountUri} is not available (for ${req.originalUrl})`)
next()
})
.catch(next)
}
}

/**
* Returns an Express middleware handler for adding a new certificate to an
* existing account (POST to /api/accounts/cert).
*
* @param accountManager
*
* @return {Function}
*/
export function newCertificate (accountManager) {
return (req, res, next) => {
return AddCertificateRequest.handle(req, res, accountManager)
.catch(err => {
err.status = err.status || 400
next(err)
})
}
}

/**
* Returns an Express router for providing user account related middleware
* handlers.
*
* @param accountManager {AccountManager}
*
* @return {Router}
*/
export function middleware (accountManager) {
const router = express.Router('/')

router.get('/', checkAccountExists(accountManager))

router.post('/api/accounts/new', restrictToTopDomain, bodyParser, CreateAccountRequest.post)
router.get(['/register', '/api/accounts/new'], restrictToTopDomain, CreateAccountRequest.get)

router.post('/api/accounts/cert', restrictToTopDomain, bodyParser, newCertificate(accountManager))

router.get('/account/delete', restrictToTopDomain, DeleteAccountRequest.get)
router.post('/account/delete', restrictToTopDomain, bodyParser, DeleteAccountRequest.post)

router.get('/account/delete/confirm', restrictToTopDomain, DeleteAccountConfirmRequest.get)
router.post('/account/delete/confirm', restrictToTopDomain, bodyParser, DeleteAccountConfirmRequest.post)

return router
}

export default {
middleware,
checkAccountExists,
newCertificate
}
22 changes: 22 additions & 0 deletions lib/api/authn/force-user.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import debug from '../../debug.mjs'
const debugAuth = debug.authentication

/**
* Enforces the `--force-user` server flag, hardcoding a webid for all requests,
* for testing purposes.
*/
export function initialize (app, argv) {
const forceUserId = argv.forceUser
app.use('/', (req, res, next) => {
debugAuth(`Identified user (override): ${forceUserId}`)
req.session.userId = forceUserId
if (argv.auth === 'tls') {
res.set('User', forceUserId)
}
next()
})
}

export default {
initialize
}
9 changes: 9 additions & 0 deletions lib/api/authn/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export { default as oidc } from './webid-oidc.mjs'
export { default as tls } from './webid-tls.mjs'
export { default as forceUser } from './force-user.mjs'

export default {
oidc: (await import('./webid-oidc.mjs')).default,
tls: (await import('./webid-tls.mjs')).default,
forceUser: (await import('./force-user.mjs')).default
}
2 changes: 2 additions & 0 deletions lib/api/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { default as authn } from './authn/index.mjs'
export { default as accounts } from './accounts/user-accounts.mjs'
51 changes: 51 additions & 0 deletions lib/capability-discovery.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* @module capability-discovery
*/
import express from 'express'
import { URL } from 'url'

/**
* Returns a set of routes to deal with server capability discovery
* @method capabilityDiscovery
* @return {Router} Express router
*/
export default function capabilityDiscovery () {
const router = express.Router('/')

// Advertise the server capability discover endpoint
router.get('/.well-known/solid', serviceCapabilityDocument())
return router
}

/**
* Serves the service capability document (containing server root URL, including
* any base path the user specified in config, server API endpoints, etc).
* @method serviceCapabilityDocument
* @param req
* @param res
* @param next
*/
function serviceCapabilityDocument () {
return (req, res) => {
const ldp = req.app.locals.ldp
res.json({
// Add the server root url
root: ldp.resourceMapper.resolveUrl(req.hostname, req.path),
// Add the 'apps' urls section
apps: req.app.locals.appUrls,
api: {
accounts: {
// 'changePassword': '/api/account/changePassword',
// 'delete': '/api/accounts/delete',

// Create new user (see IdentityProvider.post() in identity-provider.js)
new: new URL('/api/accounts/new', ldp.serverUri),
recover: new URL('/api/accounts/recover', ldp.serverUri),
signin: ldp.resourceMapper.resolveUrl(req.hostname, '/login'),
signout: ldp.resourceMapper.resolveUrl(req.hostname, '/logout'),
validateToken: new URL('/api/accounts/validateToken', ldp.serverUri)
}
}
})
}
}
169 changes: 169 additions & 0 deletions lib/ldp-container.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
import { createRequire } from 'module'
const require = createRequire(import.meta.url)

const $rdf = require('rdflib')
import debug from './debug.mjs'
import error from './http-error.mjs'
import fs from 'fs'
const ns = require('solid-namespace')($rdf)
const mime = require('mime-types')
import path from 'path'

export { addContainerStats, addFile, addStats, readdir }

async function addContainerStats (ldp, reqUri, filename, resourceGraph) {
const containerStats = await ldp.stat(filename)
addStats(resourceGraph, reqUri, containerStats, filename)
const storage = new URL(reqUri)
if (reqUri === storage.origin + '/') {
resourceGraph.add(
resourceGraph.sym(reqUri),
ns.rdf('type'),
ns.space('Storage')
)
}
resourceGraph.add(
resourceGraph.sym(reqUri),
ns.rdf('type'),
ns.ldp('BasicContainer'))
resourceGraph.add(
resourceGraph.sym(reqUri),
ns.rdf('type'),
ns.ldp('Container'))
}

async function addFile (ldp, resourceGraph, containerUri, reqUri, container, file) {
// Skip .meta and .acl
if (file.endsWith(ldp.suffixMeta) || file.endsWith(ldp.suffixAcl)) {
return null
}

const filePath = path.join(container, file)

// Get file stats
let stats
try {
stats = await ldp.stat(filePath)
} catch (e) {
return null
}
const memberUri = reqUri + (stats.isDirectory() ? '/' : '')

// Add fileStats to resource Graph
addStats(resourceGraph, memberUri, stats, file)

// Add to `contains` list
resourceGraph.add(
resourceGraph.sym(containerUri),
ns.ldp('contains'),
resourceGraph.sym(memberUri))

// Set up a metaFile path
// Earlier code used a .ttl file as its own meta file, which
// caused massive data files to parsed as part of deirectory listings just looking for type triples
const metaFile = containerUri + file + ldp.suffixMeta

let metadataGraph
try {
metadataGraph = await getMetadataGraph(ldp, metaFile, memberUri)
} catch (err) {
metadataGraph = $rdf.graph()
}

// Add Container or BasicContainer types
if (stats.isDirectory()) {
resourceGraph.add(
metadataGraph.sym(memberUri),
ns.rdf('type'),
ns.ldp('BasicContainer'))

resourceGraph.add(
metadataGraph.sym(memberUri),
ns.rdf('type'),
ns.ldp('Container'))
}
// Add generic LDP type
resourceGraph.add(
metadataGraph.sym(memberUri),
ns.rdf('type'),
ns.ldp('Resource'))

// Add type from metadataGraph
metadataGraph
.statementsMatching(
metadataGraph.sym(memberUri),
ns.rdf('type'),
undefined)
.forEach(function (typeStatement) {
// If the current is a file and its type is BasicContainer,
// This is not possible, so do not infer its type!
if (
(
typeStatement.object.uri !== ns.ldp('BasicContainer').uri &&
typeStatement.object.uri !== ns.ldp('Container').uri
) ||
!stats.isFile()
) {
resourceGraph.add(
resourceGraph.sym(reqUri),
typeStatement.predicate,
typeStatement.object)
}
})

return null
}

function addStats (resourceGraph, reqUri, stats, filename) {
resourceGraph.add(
resourceGraph.sym(reqUri),
ns.stat('mtime'), // Deprecate?
stats.mtime.getTime() / 1000)

resourceGraph.add(
resourceGraph.sym(reqUri),
ns.dct('modified'),
stats.mtime) // An actual datetime value from a Date object

resourceGraph.add(
resourceGraph.sym(reqUri),
ns.stat('size'),
stats.size)

if (!reqUri.endsWith('/') && mime.lookup(filename)) { // Is the file has a well-known type,
const type = 'http://www.w3.org/ns/iana/media-types/' + mime.lookup(filename) + '#Resource'
resourceGraph.add(
resourceGraph.sym(reqUri),
ns.rdf('type'), // convert MIME type to RDF
resourceGraph.sym(type)
)
}
}

function readdir (filename) {
debug.handlers('GET -- Reading directory')
return new Promise((resolve, reject) => {
fs.readdir(filename, function (err, files) {
if (err) {
debug.handlers('GET -- Error reading files: ' + err)
return reject(error(err, 'Can\'t read container'))
}

debug.handlers('Files in directory: ' + files.toString().slice(0, 100))
return resolve(files)
})
})
}

async function getMetadataGraph (ldp, metaFile) {
const metaStats = await ldp.stat(metaFile)
if (metaStats && metaStats.isFile()) {
try {
return await ldp.getGraph(metaFile)
} catch (err) {
throw error(err, 'Can\'t parse container metadata')
}
} else {
return $rdf.graph()
}
}
Loading