@@ -7,6 +7,7 @@ import vhost from 'vhost'
77import aclCheck from '@solid/acl-check'
88import path from 'path'
99import { createRequire } from 'module'
10+ import fs from 'fs'
1011import { fileURLToPath } from 'url'
1112import { dirname } from 'path'
1213import acceptEventsModule from 'express-accept-events'
@@ -18,36 +19,44 @@ import prepModule from 'express-prep'
1819const __filename = fileURLToPath ( import . meta. url )
1920const __dirname = dirname ( __filename )
2021
21- // Create require for accessing CommonJS modules and package.json
22- const require = createRequire ( import . meta. url )
23- const { version } = require ( '../package.json' )
22+ // Read package.json synchronously to avoid using require() for JSON
23+ const { version } = JSON . parse ( fs . readFileSync ( path . join ( __dirname , '../package.json' ) , 'utf8' ) )
2424
2525// Complex internal modules - keep as CommonJS for now except where ESM available
26- const LDP = require ( './ldp.js' )
26+ import LDP from './ldp.mjs'
2727import LdpMiddleware from './ldp-middleware.mjs'
28- const corsProxy = require ( './handlers/cors-proxy.js' )
29- const authProxy = require ( './handlers/auth-proxy.js' )
30- const SolidHost = require ( './models/solid-host.js' )
31- const AccountManager = require ( './models/account-manager.js' )
32- const EmailService = require ( './services/email-service.js' )
33- const TokenService = require ( './services/token-service.js' )
34- const capabilityDiscovery = require ( './capability-discovery.js' )
35- const paymentPointerDiscovery = require ( './payment-pointer-discovery.js' )
36- const API = require ( './api/index.js' )
37- const errorPages = require ( './handlers/error-pages.js' )
38- const config = require ( './server-config.js' )
28+ import corsProxy from './handlers/cors-proxy.mjs'
29+ import authProxy from './handlers/auth-proxy.mjs'
30+ import SolidHost from './models/solid-host.mjs'
31+ import AccountManager from './models/account-manager.mjs'
32+ import EmailService from './services/email-service.mjs'
33+ import TokenService from './services/token-service.mjs'
34+ import capabilityDiscovery from './capability-discovery.mjs'
35+ import paymentPointerDiscovery from './payment-pointer-discovery.mjs'
36+ import * as API from './api/index.mjs'
37+ import errorPages from './handlers/error-pages.mjs'
38+ import * as config from './server-config.mjs'
3939import defaults from '../config/defaults.mjs'
40- const options = require ( './handlers/options.js' )
40+ import options from './handlers/options.mjs'
4141import { handlers as debug } from './debug.mjs'
4242import { routeResolvedFile } from './utils.mjs'
43- const ResourceMapper = require ( './resource-mapper.js' )
43+ import ResourceMapper from './resource-mapper.mjs'
4444
4545// Extract default exports from ESM modules
4646const acceptEvents = acceptEventsModule . default
4747const events = negotiateEventsModule . default
4848const eventID = eventIDModule . default
4949const prep = prepModule . default
5050
51+ // Defensive fallbacks: if any of these weren't provided as functions (for example
52+ // when the optional packages are missing or export differently), replace them
53+ // with no-op middleware so `app.use(...)` doesn't receive `undefined`.
54+ function noopMiddleware ( req , res , next ) { return next ( ) }
55+ const safeEventID = typeof eventID === 'function' ? eventID : noopMiddleware
56+ const safeAcceptEvents = typeof acceptEvents === 'function' ? acceptEvents : noopMiddleware
57+ const safeEvents = typeof events === 'function' ? events : noopMiddleware
58+ const safePrep = typeof prep === 'function' ? prep : noopMiddleware
59+
5160const corsSettings = cors ( {
5261 methods : [
5362 'OPTIONS' , 'HEAD' , 'GET' , 'PATCH' , 'POST' , 'PUT' , 'DELETE'
@@ -81,10 +90,97 @@ function createApp (argv = {}) {
8190
8291 const app = express ( )
8392
93+ // Temporary instrumentation: wrap `app.route` and `app.use` to detect
94+ // registrations of undefined handlers which cause "Route.get() requires a
95+ // callback function but got a [object Undefined]" errors during tests.
96+ // This will log the path, method and the offending handler for diagnosis
97+ // and can be removed once the problematic registration is fixed.
98+ ; ( function instrumentApp ( ) {
99+ try {
100+ const origRoute = app . route . bind ( app )
101+ app . route = function ( path ) {
102+ const route = origRoute ( path )
103+ const methods = [ 'get' , 'post' , 'put' , 'patch' , 'delete' , 'head' , 'options' ]
104+ methods . forEach ( m => {
105+ const orig = route [ m ] . bind ( route )
106+ route [ m ] = function ( ...handlers ) {
107+ handlers . forEach ( ( h , i ) => {
108+ if ( typeof h !== 'function' ) {
109+ console . error ( '\n[diagnostic] Non-function handler detected for route' , m . toUpperCase ( ) , path )
110+ console . error ( '[diagnostic] handler index:' , i , 'value:' , h )
111+ console . error ( new Error ( ) . stack )
112+ }
113+ } )
114+ return orig ( ...handlers )
115+ }
116+ } )
117+ return route
118+ }
119+
120+ const origUse = app . use . bind ( app )
121+ app . use = function ( ...args ) {
122+ // app.use can be called as app.use(fn) or app.use(path, fn)
123+ const handlers = args . filter ( a => typeof a === 'function' || Array . isArray ( a ) ) . slice ( - 1 )
124+ handlers . forEach ( ( h , idx ) => {
125+ if ( Array . isArray ( h ) ) {
126+ h . forEach ( ( hh , ii ) => {
127+ if ( typeof hh !== 'function' ) {
128+ console . error ( '\n[diagnostic] Non-function middleware detected in app.use array at index' , ii , 'value:' , hh )
129+ console . error ( new Error ( ) . stack )
130+ }
131+ } )
132+ } else if ( typeof h !== 'function' ) {
133+ console . error ( '\n[diagnostic] Non-function middleware detected in app.use args at index' , idx , 'value:' , h )
134+ console . error ( new Error ( ) . stack )
135+ }
136+ } )
137+ return origUse ( ...args )
138+ }
139+ } catch ( e ) {
140+ console . error ( '[diagnostic] failed to instrument app.route/app.use' , e )
141+ }
142+ } ) ( )
143+
144+ // Also instrument Express Router methods (get/post/use/put/patch/delete/options/head)
145+ ; ( function instrumentRouter ( ) {
146+ try {
147+ const proto = express . Router && express . Router . prototype
148+ if ( ! proto ) { return }
149+ const methods = [ 'get' , 'post' , 'put' , 'patch' , 'delete' , 'head' , 'options' , 'use' ]
150+ methods . forEach ( m => {
151+ if ( ! proto [ m ] ) { return }
152+ const orig = proto [ m ]
153+ proto [ m ] = function ( ...args ) {
154+ // handlers can be provided as (path, fn...), or (fn...)
155+ const handlers = args . slice ( typeof args [ 0 ] === 'string' || args [ 0 ] instanceof RegExp ? 1 : 0 )
156+ handlers . forEach ( ( h , i ) => {
157+ if ( Array . isArray ( h ) ) {
158+ h . forEach ( ( hh , ii ) => {
159+ if ( typeof hh !== 'function' ) {
160+ console . error ( '\n[diagnostic] Non-function handler detected for router.%s at path %s' , m , args [ 0 ] )
161+ console . error ( '[diagnostic] handler index:' , i , 'array index:' , ii , 'value:' , hh )
162+ console . error ( new Error ( ) . stack )
163+ }
164+ } )
165+ } else if ( typeof h !== 'function' ) {
166+ console . error ( '\n[diagnostic] Non-function handler detected for router.%s at path %s' , m , args [ 0 ] )
167+ console . error ( '[diagnostic] handler index:' , i , 'value:' , h )
168+ console . error ( new Error ( ) . stack )
169+ }
170+ } )
171+ return orig . apply ( this , args )
172+ }
173+ } )
174+ } catch ( e ) {
175+ console . error ( '[diagnostic] failed to instrument express.Router methods' , e )
176+ }
177+ } ) ( )
178+
84179 // Add PREP support
85180 if ( argv . prep ) {
86- app . use ( eventID )
87- app . use ( acceptEvents , events , prep )
181+ // Use the safe fallbacks to avoid passing `undefined` to app.use()
182+ app . use ( safeEventID )
183+ app . use ( safeAcceptEvents , safeEvents , safePrep )
88184 }
89185
90186 initAppLocals ( app , argv , ldp )
@@ -94,7 +190,7 @@ function createApp (argv = {}) {
94190
95191 // Serve the public 'common' directory (for shared CSS files, etc)
96192 app . use ( '/common' , express . static ( path . join ( __dirname , '../common' ) ) )
97- app . use ( '/' , express . static ( path . dirname ( require . resolve ( 'mashlib/dist/databrowser.html' ) ) , { index : false } ) )
193+ app . use ( '/' , express . static ( path . dirname ( import . meta . resolve ( 'mashlib/dist/databrowser.html' ) ) , { index : false } ) )
98194 routeResolvedFile ( app , '/common/js/' , 'solid-auth-client/dist-lib/solid-auth-client.bundle.js' )
99195 routeResolvedFile ( app , '/common/js/' , 'solid-auth-client/dist-lib/solid-auth-client.bundle.js.map' )
100196 app . use ( '/.well-known' , express . static ( path . join ( __dirname , '../common/well-known' ) ) )
@@ -141,6 +237,26 @@ function createApp (argv = {}) {
141237 }
142238
143239 // Attach the LDP middleware
240+ // Server-side diagnostic middleware: logs incoming requests and when responses finish.
241+ // Helps detect requests that never complete (hang) during tests.
242+ app . use ( ( req , res , next ) => {
243+ try {
244+ const cookie = req . get ( 'Cookie' ) || req . get ( 'cookie' ) || ''
245+ console . error ( '[srv-debug] incoming request:' , req . method , req . path , 'cookie-present:' , ! ! cookie )
246+ } catch ( e ) {
247+ console . error ( '[srv-debug] incoming request: error reading headers' , e )
248+ }
249+ const start = Date . now ( )
250+ res . on ( 'finish' , ( ) => {
251+ try {
252+ console . error ( '[srv-debug] request finished:' , req . method , req . path , 'status:' , res . statusCode , 'durationMs:' , Date . now ( ) - start )
253+ } catch ( e ) {
254+ console . error ( '[srv-debug] request finished: error logging' , e )
255+ }
256+ } )
257+ next ( )
258+ } )
259+
144260 app . use ( '/' , LdpMiddleware ( corsSettings , argv . prep ) )
145261
146262 // https://stackoverflow.com/questions/51741383/nodejs-express-return-405-for-un-supported-method
0 commit comments