wiki-server 0.26.0-rc.1 → 0.26.0-rc.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -2,7 +2,11 @@
2
2
  // Simple file so that if you require this directory
3
3
  // in node it instead requires ./lib/server.coffee
4
4
  // with coffee-script already loaded.
5
- require('coffeescript')
6
- require('coffeescript/register')
5
+ // import('coffeescript')
6
+ // import('coffeescript/register.js')
7
7
 
8
- module.exports = require('./lib/server')
8
+ // const { default: server } = await import('./lib/server.js')
9
+ // export default server
10
+
11
+ import { default as server } from './lib/server.js'
12
+ export default server
@@ -9,13 +9,19 @@
9
9
  // **defaultargs.coffee** when called on the argv object this
10
10
  // module will create reasonable defaults for options not supplied,
11
11
  // based on what information is provided.
12
- const path = require('node:path')
12
+ import path from 'node:path'
13
+ import { randomBytes } from 'node:crypto'
14
+ import { fileURLToPath } from 'node:url'
15
+ import { dirname } from 'node:path'
16
+
17
+ const __filename = fileURLToPath(import.meta.url)
18
+ const __dirname = dirname(__filename)
13
19
 
14
20
  const getUserHome = () => {
15
21
  return process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE
16
22
  }
17
23
 
18
- module.exports = argv => {
24
+ export default argv => {
19
25
  argv = argv || {}
20
26
  argv.root ||= __dirname
21
27
  // the directory that contains all the packages that makeup the wiki
@@ -32,7 +38,7 @@ module.exports = argv => {
32
38
  argv.url ||= `http://localhost${argv.port === 80 ? '' : ':' + argv.port}`
33
39
  argv.id ||= path.join(argv.status, 'owner.json')
34
40
  argv.uploadLimit ||= '5mb'
35
- argv.cookieSecret ||= require('crypto').randomBytes(64).toString('hex')
41
+ argv.cookieSecret ||= randomBytes(64).toString('hex')
36
42
  argv.secure_cookie ||= false
37
43
  argv.session_duration ||= 7
38
44
  argv.neighbors ||= ''
@@ -43,9 +49,9 @@ module.exports = argv => {
43
49
  argv.database = JSON.parse(argv.database)
44
50
  }
45
51
  argv.database ||= {}
46
- argv.database.type ||= './page'
52
+ argv.database.type ||= './page.js'
47
53
  if (argv.database.type.charAt(0) === '.') {
48
- if (argv.database.type != './page') {
54
+ if (argv.database.type != './page.js') {
49
55
  console.log('\n\nWARNING: This storage option is depeciated.')
50
56
  console.log(' See ReadMe for details of the changes required.\n\n')
51
57
  }
@@ -53,8 +59,8 @@ module.exports = argv => {
53
59
  argv.database.type = 'wiki-storage-' + argv.database.type
54
60
  }
55
61
 
56
- argv.security_type ||= './security'
57
- if (argv.security_type === './security') {
62
+ argv.security_type ||= './security.js'
63
+ if (argv.security_type === './security.js') {
58
64
  console.log('\n\nINFORMATION: Using default security module.')
59
65
  } else {
60
66
  argv.security_type = 'wiki-security-' + argv.security_type
package/lib/forward.js CHANGED
@@ -1,4 +1,4 @@
1
- const init = (app, emitter) => {
1
+ export const init = (app, emitter) => {
2
2
  let sockets = []
3
3
  app.io.on('connection', socket => {
4
4
  let listeners = []
@@ -35,4 +35,3 @@ const init = (app, emitter) => {
35
35
  })
36
36
  })
37
37
  }
38
- module.exports = { init }
package/lib/page.js CHANGED
@@ -10,14 +10,53 @@
10
10
  // Everything is stored using json flat files.
11
11
 
12
12
  // #### Requires ####
13
- const fs = require('fs')
14
- const path = require('path')
15
- const events = require('events')
13
+ // const fs = require('fs')
14
+ // const path = require('path')
15
+ // const events = require('events')
16
+ // const glob = require('glob')
17
+ //
18
+ // const async = require('async')
19
+ //
20
+ // // const random_id = require('./random_id.cjs')
21
+ // const synopsis = require('wiki-client/lib/synopsis')
22
+
23
+ import fs from 'node:fs'
24
+ import path from 'node:path'
25
+ import url from 'node:url'
26
+ import events from 'node:events'
27
+
28
+ import { createRequire } from 'node:module'
29
+ const require = createRequire(import.meta.url)
30
+
31
+ // Use dynamic import to load package.json from the main application's working directory
32
+ const wikiPackageImport = async () => {
33
+ let done = false
34
+ return new Promise(resolve => {
35
+ import('wiki/package.json', { with: { type: 'json' } })
36
+ .then(imported => {
37
+ done = true
38
+ resolve(imported.default)
39
+ })
40
+ .catch(e => {
41
+ return e
42
+ })
43
+ .then(async () => {
44
+ if (done) return
45
+ const packageJsonPath = path.join(process.cwd(), 'package.json')
46
+ const packageJsonUrl = url.pathToFileURL(packageJsonPath).href
47
+ import(packageJsonUrl, { with: { type: 'json' } })
48
+ .then(imported => {
49
+ resolve(imported.default)
50
+ })
51
+ .catch(e => console.error('problems importing package', e))
52
+ })
53
+ })
54
+ }
16
55
 
17
- const async = require('async')
56
+ const packageJson = await wikiPackageImport()
18
57
 
19
- const random_id = require('./random_id')
20
- const synopsis = require('wiki-client/lib/synopsis')
58
+ // import random_id from './random_id.cjs'; // Uncomment if needed
59
+ import synopsis from 'wiki-client/lib/synopsis.js' // Add correct extension if necessary
21
60
 
22
61
  const asSlug = name =>
23
62
  name
@@ -27,7 +66,7 @@ const asSlug = name =>
27
66
 
28
67
  // Export a function that generates a page handler
29
68
  // when called with options object.
30
- module.exports = exports = argv => {
69
+ export default argv => {
31
70
  const wikiName = new URL(argv.url).hostname
32
71
 
33
72
  fs.mkdir(argv.db, { recursive: true }, e => {
@@ -36,13 +75,11 @@ module.exports = exports = argv => {
36
75
 
37
76
  // create a list of plugin pages.
38
77
  const pluginPages = new Map()
39
- Object.keys(require.main.require('./package').dependencies)
78
+
79
+ Object.keys(packageJson.dependencies)
40
80
  .filter(depend => depend.startsWith('wiki-plugin'))
41
81
  .forEach(plugin => {
42
- const pagesPath = path.join(
43
- path.dirname(require.resolve(`${plugin}/package`, { paths: require.main.paths })),
44
- 'pages',
45
- )
82
+ const pagesPath = path.join(path.dirname(require.resolve(`${plugin}/package`)), 'pages')
46
83
  fs.readdir(pagesPath, { withFileTypes: true }, (err, entries) => {
47
84
  if (err) return
48
85
  entries.forEach(entry => {
@@ -379,43 +416,46 @@ module.exports = exports = argv => {
379
416
  }
380
417
  return collaborativeLinks
381
418
  }
419
+
382
420
  fs.readdir(argv.db, (e, files) => {
383
421
  if (e) return cb(e)
384
- // used to make sure all of the files are read
385
- // and processesed in the site map before responding
386
- const doSitemap = (file, cb) => {
387
- itself.get(file, (e, page, status) => {
388
- if (file.match(/^\./)) return cb()
389
- if (e || status === 404) {
390
- console.log('Problem building sitemap:', file, 'e: ', e, 'status:', status)
391
- return cb() // Ignore errors in the pagehandler get.
392
- }
393
- let pageLinksMap
394
- try {
395
- pageLinksMap = page.story.reduce(extractPageLinks, new Map())
396
- } catch (err) {
397
- console.log(`METADATA *** ${wikiName} reduce to extract links on ${file} failed`, err.message)
398
- pageLinksMap = []
399
- }
400
- //
401
- const pageLinks = pageLinksMap.size > 0 ? Object.fromEntries(pageLinksMap) : undefined
402
-
403
- cb(null, {
404
- slug: file,
405
- title: page.title,
406
- date: editDate(page.journal),
407
- synopsis: synopsis(page),
408
- links: pageLinks,
422
+ const doSitemap = async file => {
423
+ return new Promise(resolve => {
424
+ itself.get(file, (e, page, status) => {
425
+ if (file.match(/^\./)) return resolve(null)
426
+ if (e || status === 404) {
427
+ console.log('Problem building sitemap:', file, 'e: ', e, 'status:', status)
428
+ return resolve(null) // Ignore errors in the pagehandler get.
429
+ }
430
+ let pageLinksMap
431
+ try {
432
+ pageLinksMap = page.story.reduce(extractPageLinks, new Map())
433
+ } catch (err) {
434
+ console.log(`METADATA *** ${wikiName} reduce to extract links on ${file} failed`, err.message)
435
+ pageLinksMap = []
436
+ }
437
+ //
438
+ const pageLinks = pageLinksMap.size > 0 ? Object.fromEntries(pageLinksMap) : undefined
439
+
440
+ resolve({
441
+ slug: file,
442
+ title: page.title,
443
+ date: editDate(page.journal),
444
+ synopsis: synopsis(page),
445
+ links: pageLinks,
446
+ })
409
447
  })
410
448
  })
411
449
  }
412
- async.map(files, doSitemap, (e, sitemap) => {
413
- if (e) return cb(e)
414
- cb(
415
- null,
416
- sitemap.filter(item => item != null),
417
- )
418
- })
450
+
451
+ Promise.all(files.map(doSitemap))
452
+ .then(sitemap => {
453
+ cb(
454
+ null,
455
+ sitemap.filter(item => item != null),
456
+ )
457
+ })
458
+ .catch(e => cb(e))
419
459
  })
420
460
  }
421
461
 
package/lib/plugins.js CHANGED
@@ -8,11 +8,11 @@
8
8
 
9
9
  // support server-side plugins
10
10
 
11
- const fs = require('node:fs')
12
- const { pathToFileURL } = require('node:url')
13
- // forward = require './forward'
11
+ import fs from 'node:fs'
12
+ import { pathToFileURL } from 'node:url'
13
+ // import forward from './forward.cjs'; // Uncomment if needed and adjust import style if it's not a default export
14
14
 
15
- module.exports = exports = argv => {
15
+ export default argv => {
16
16
  // NOTE: plugins are now in their own package directories alongside this one...
17
17
  // Plugins are in directories of the form wiki-package-*
18
18
  // those with a server component will have a server directory
@@ -39,11 +39,8 @@ module.exports = exports = argv => {
39
39
  }
40
40
 
41
41
  const startServers = params => {
42
- // emitter = new events.EventEmitter()
43
- // forward.init params.app, emitter
44
- // params.emitter = emitter
45
-
46
- Object.keys(require.main.require('./package').dependencies)
42
+ const dependencies = params.packageJson.dependencies
43
+ Object.keys(dependencies)
47
44
  .filter(depend => depend.startsWith('wiki-plugin'))
48
45
  .forEach(plugin => {
49
46
  startServer(params, plugin)
package/lib/random_id.js CHANGED
@@ -10,6 +10,4 @@
10
10
  // Simple random hex generator, takes an optional number of
11
11
  // chars that defaults to 16 and returns a random id.
12
12
 
13
- const random_id = (chars = 16) => [...Array(chars)].map(() => Math.floor(Math.random() * 16).toString(16)).join('')
14
-
15
- module.exports = random_id.random_id = random_id
13
+ export default (chars = 16) => [...Array(chars)].map(() => Math.floor(Math.random() * 16).toString(16)).join('')
package/lib/search.js CHANGED
@@ -8,15 +8,15 @@
8
8
 
9
9
  // **search.js**
10
10
 
11
- const fs = require('node:fs')
12
- const path = require('node:path')
13
- const events = require('node:events')
14
- const url = require('node:url')
15
- const writeFileAtomic = require('write-file-atomic')
11
+ import fs from 'node:fs'
12
+ import path from 'node:path'
13
+ import events from 'node:events'
14
+ import url from 'node:url'
15
+ import writeFileAtomic from 'write-file-atomic'
16
16
 
17
- const miniSearch = require('minisearch')
17
+ import miniSearch from 'minisearch'
18
18
 
19
- module.exports = exports = argv => {
19
+ export default argv => {
20
20
  const wikiName = new URL(argv.url).hostname
21
21
  let siteIndex = []
22
22
  const queue = []
@@ -169,8 +169,12 @@ module.exports = exports = argv => {
169
169
  .replace(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g, ' ')
170
170
  .replace(/<(?:[^>])+>/g, ' ')
171
171
  .replace(/(https?:.*?)(?=\p{White_Space}|\p{Quotation_Mark}|$)/gu, match => {
172
- const myUrl = url.parse(match)
173
- return myUrl.hostname + ' ' + myUrl.pathname
172
+ try {
173
+ const myUrl = new URL(match)
174
+ return myUrl.hostname
175
+ } catch {
176
+ return ' '
177
+ }
174
178
  })
175
179
  .replace(/[\p{P}\p{Emoji}\p{Symbol}}]+/gu, ' ')
176
180
  .replace(/[\p{White_Space}\n\t]+/gu, ' ')
package/lib/security.js CHANGED
@@ -13,11 +13,11 @@
13
13
  // allow the server to run read-only.
14
14
 
15
15
  // #### Requires ####
16
- const fs = require('node:fs')
16
+ import fs from 'node:fs'
17
17
 
18
18
  // Export a function that generates security handler
19
19
  // when called with options object.
20
- module.exports = exports = (log, loga, argv) => {
20
+ export default (log, loga, argv) => {
21
21
  const security = {}
22
22
 
23
23
  // #### Private utility methods. ####
@@ -84,7 +84,16 @@ module.exports = exports = (log, loga, argv) => {
84
84
  }
85
85
  // Wiki server admin
86
86
  security.isAdmin = () => {
87
- return false
87
+ // nobody is admin - unless legacy support, and test
88
+ if (argv.security_legacy) {
89
+ if (argv.test) {
90
+ return true
91
+ } else {
92
+ return false
93
+ }
94
+ } else {
95
+ return false
96
+ }
88
97
  }
89
98
  security.defineRoutes = (app, cors, updateOwner) => {
90
99
  // default security does not have any routes
package/lib/server.js CHANGED
@@ -19,20 +19,18 @@
19
19
  // npm install
20
20
 
21
21
  // Standard lib
22
- const fs = require('fs')
23
- const path = require('path')
24
- const http = require('http')
25
- const url = require('url')
26
- const { pipeline } = require('node:stream/promises')
22
+ import fs from 'fs'
23
+ import path from 'path'
24
+ import url from 'url'
25
+ import { pipeline } from 'node:stream/promises'
27
26
 
28
27
  // From npm
29
- const express = require('express')
30
- const hbs = require('express-hbs')
31
- const async = require('async')
32
- const f = require('flates')
28
+ import express from 'express'
29
+ import hbs from 'express-hbs'
30
+ import f from 'flates'
33
31
 
34
- const createDOMPurify = require('dompurify')
35
- const { JSDOM } = require('jsdom')
32
+ import createDOMPurify from 'dompurify'
33
+ import { JSDOM } from 'jsdom'
36
34
 
37
35
  const window = new JSDOM('').window
38
36
  const DOMPurify = createDOMPurify(window)
@@ -40,21 +38,53 @@ const DOMPurify = createDOMPurify(window)
40
38
  // Using native fetch API (available in Node.js 18+)
41
39
 
42
40
  // Express 4 middleware
43
- const logger = require('morgan')
44
- const cookieParser = require('cookie-parser')
45
- const methodOverride = require('method-override')
46
- // session = require 'express-session'
47
- const sessions = require('client-sessions')
48
- const bodyParser = require('body-parser')
49
- const errorHandler = require('errorhandler')
41
+ import logger from 'morgan'
42
+ import cookieParser from 'cookie-parser'
43
+ import methodOverride from 'method-override'
44
+ // session = require('express-session') // This one was commented out — uncomment if used
45
+ import sessions from 'client-sessions'
46
+ import bodyParser from 'body-parser'
47
+ import errorHandler from 'errorhandler'
50
48
 
51
49
  // Local files
52
- const random = require('./random_id')
53
- const defargs = require('./defaultargs')
54
- const resolveClient = require('wiki-client/lib/resolve')
55
- const pluginsFactory = require('./plugins')
56
- const sitemapFactory = require('./sitemap')
57
- const searchFactory = require('./search')
50
+ // Make sure these files are ESM modules or compatible
51
+ // If they are CommonJS, you will need to dynamically import them (see below)
52
+ import defargs from './defaultargs.js'
53
+ import resolveClient from 'wiki-client/lib/resolve.js'
54
+ import pluginsFactory from './plugins.js'
55
+ import sitemapFactory from './sitemap.js'
56
+ import searchFactory from './search.js'
57
+ import { warn } from 'node:console'
58
+ import { createRequire } from 'module'
59
+ const require = createRequire(import.meta.url)
60
+
61
+ // Use import to load package.json from the main application's working directory
62
+ //const { default: packageJson } = await import('wiki/package.json', { with: { type: 'json' } })
63
+ const wikiPackageImport = async () => {
64
+ let done = false
65
+ return new Promise(resolve => {
66
+ import('wiki/package.json', { with: { type: 'json' } })
67
+ .then(imported => {
68
+ done = true
69
+ resolve(imported.default)
70
+ })
71
+ .catch(e => {
72
+ return e
73
+ })
74
+ .then(async () => {
75
+ if (done) return
76
+ const packageJsonPath = path.join(process.cwd(), 'package.json')
77
+ const packageJsonUrl = url.pathToFileURL(packageJsonPath).href
78
+ import(packageJsonUrl, { with: { type: 'json' } })
79
+ .then(imported => {
80
+ resolve(imported.default)
81
+ })
82
+ .catch(e => console.error('problems importing package', e))
83
+ })
84
+ })
85
+ }
86
+
87
+ const packageJson = await wikiPackageImport()
58
88
 
59
89
  const render = page => {
60
90
  return (
@@ -91,7 +121,7 @@ const render = page => {
91
121
  )
92
122
  }
93
123
  // Set export objects for node and coffee to a function that generates a sfw server.
94
- module.exports = exports = argv => {
124
+ export default async argv => {
95
125
  // Create the main application object, app.
96
126
  const app = express()
97
127
 
@@ -105,6 +135,8 @@ module.exports = exports = argv => {
105
135
 
106
136
  app.startOpts = argv
107
137
 
138
+ const wikiName = new URL(argv.url).hostname
139
+
108
140
  const log = (...stuff) => {
109
141
  if (argv.debug) console.log(stuff)
110
142
  }
@@ -126,18 +158,23 @@ module.exports = exports = argv => {
126
158
  }
127
159
  next()
128
160
  }
161
+
129
162
  let pagehandler, sitemaphandler, searchhandler, securityhandler
130
- // Require the database adapter and initialize it with options.
131
- app.pagehandler = pagehandler = require(argv.database.type)(argv)
132
163
 
133
- // Require the sitemap adapter and initialize it with options.
164
+ // Dynamically import database adapter (since the module name is dynamic)
165
+ const dbModule = await import(argv.database.type)
166
+ app.pagehandler = pagehandler = dbModule.default(argv)
167
+
168
+ // Initialize sitemap handler
134
169
  app.sitemaphandler = sitemaphandler = sitemapFactory(argv)
135
170
 
136
- // Require the site indexer and initialize it with options
171
+ // Initialize search handler
137
172
  app.searchhandler = searchhandler = searchFactory(argv)
138
173
 
139
- // Require the security adapter and initialize it with options.
140
- app.securityhandler = securityhandler = require(argv.security_type)(log, loga, argv)
174
+ // Dynamically import security adapter (also dynamic)
175
+ console.log('security_type', argv.security_type)
176
+ const securityModule = await import(argv.security_type)
177
+ app.securityhandler = securityhandler = securityModule.default(log, loga, argv)
141
178
 
142
179
  // If the site is owned, owner will contain the name of the owner
143
180
  let owner = ''
@@ -220,7 +257,7 @@ module.exports = exports = argv => {
220
257
  // use logger, at least in development, probably needs a param to configure (or turn off).
221
258
  // use stream to direct to somewhere other than stdout.
222
259
  logger.token('vhost', (req, res) => {
223
- return colorString(req.hostname)
260
+ return colorString(wikiName)
224
261
  })
225
262
  app.use(logger(':vhost :method :url :status :res[content-length] - :response-time ms'))
226
263
  app.use(cookieParser())
@@ -269,13 +306,10 @@ module.exports = exports = argv => {
269
306
  app.use('/assets', cors, express.static(argv.assets))
270
307
 
271
308
  // Add static routes to the plugins client.
272
- Object.keys(require.main.require('./package').dependencies)
309
+ Object.keys(packageJson.dependencies)
273
310
  .filter(depend => depend.startsWith('wiki-plugin'))
274
311
  .forEach(plugin => {
275
- const clientPath = path.join(
276
- path.dirname(require.resolve(`${plugin}/package`, { paths: require.main.paths })),
277
- 'client',
278
- )
312
+ const clientPath = path.join(path.dirname(require.resolve(`${plugin}/package`)), 'client')
279
313
  const pluginPath = '/plugins/' + plugin.slice(12)
280
314
  app.use(pluginPath, cors, express.static(clientPath, staticPathOptions))
281
315
  })
@@ -388,17 +422,28 @@ module.exports = exports = argv => {
388
422
  app.get('/system/factories.json', (req, res) => {
389
423
  res.status(200)
390
424
  res.header('Content-Type', 'application/json')
425
+
391
426
  const factories = []
392
- Object.keys(require.main.require('./package').dependencies)
393
- .filter(depend => depend.startsWith('wiki-plugin'))
394
- .forEach(plugin => {
395
- try {
396
- factories.push(require.main.require(`${plugin}/factory`))
397
- } catch {
398
- // do nothing if plugin doesn't have a factory category.
399
- }
427
+
428
+ const getPackageFactory = plugin => {
429
+ return new Promise(resolve => {
430
+ import(`${plugin}/factory.json`, { with: { type: 'json' } })
431
+ .then(({ default: factory }) => {
432
+ resolve(factories.push(factory))
433
+ })
434
+ .catch(() => {
435
+ resolve()
436
+ })
400
437
  })
401
- res.end(JSON.stringify(factories))
438
+ }
439
+
440
+ Promise.all(
441
+ Object.keys(packageJson.dependencies)
442
+ .filter(depend => depend.startsWith('wiki-plugin'))
443
+ .map(plugin => {
444
+ return getPackageFactory(plugin)
445
+ }),
446
+ ).then(() => res.end(JSON.stringify(factories)))
402
447
  })
403
448
 
404
449
  // ###### Json Routes ######
@@ -511,32 +556,34 @@ module.exports = exports = argv => {
511
556
  // Send an array of pages currently in the recycler via json
512
557
  app.get('/recycler/system/slugs.json', authorized, (req, res) => {
513
558
  fs.readdir(argv.recycler, (e, files) => {
514
- const doRecyclermap = (file, cb) => {
515
- const recycleFile = 'recycler/' + file
516
- pagehandler.get(recycleFile, (e, page, status) => {
517
- if (e || status === 404) {
518
- console.log('Problem building recycler map:', file, 'e: ', e)
519
- // this will leave an undefined/empty item in the array, which we will filter out later
520
- return cb()
521
- }
522
- cb(null, {
523
- slug: file,
524
- title: page.title,
559
+ if (e) {
560
+ return res.e(e)
561
+ }
562
+ const doRecyclermap = async file => {
563
+ return new Promise(resolve => {
564
+ const recycleFile = 'recycler/' + file
565
+ pagehandler.get(recycleFile, (e, page, status) => {
566
+ if (e || status === 404) {
567
+ console.log('Problem building recycler map:', file, 'e: ', e)
568
+ // this will leave an undefined/empty item in the array, which we will filter out later
569
+ return resolve(null)
570
+ }
571
+ resolve({
572
+ slug: file,
573
+ title: page.title,
574
+ })
525
575
  })
526
576
  })
527
577
  }
528
578
 
529
- if (e) {
530
- return res.e(e)
531
- }
532
- async.map(files, doRecyclermap, (e, recyclermap) => {
533
- if (e) {
534
- return res.e(e)
535
- }
536
- // remove any empty items
537
- recyclermap = recyclermap.filter(el => !!el)
538
- res.send(recyclermap)
539
- })
579
+ Promise.all(files.map(doRecyclermap))
580
+ .then(recyclermap => {
581
+ recyclermap = recyclermap.filter(el => !!el)
582
+ res.send(recyclermap)
583
+ })
584
+ .catch(error => {
585
+ res.e(error)
586
+ })
540
587
  })
541
588
  })
542
589
 
@@ -642,28 +689,29 @@ module.exports = exports = argv => {
642
689
  if (e) {
643
690
  return res.e(e)
644
691
  }
645
- async.map(
646
- sitemap,
647
- (stub, done) => {
692
+ const pagePromises = sitemap.map(stub => {
693
+ return new Promise((resolve, reject) => {
648
694
  pagehandler.get(stub.slug, (error, page) => {
649
- if (e) {
650
- return done(e)
695
+ if (error) {
696
+ return reject(error)
651
697
  }
652
- done(null, { slug: stub.slug, page })
698
+ resolve({ slug: stub.slug, page })
653
699
  })
654
- },
655
- (e, pages) => {
656
- if (e) {
657
- return res.e(e)
658
- }
659
- res.json(
660
- pages.reduce((dict, combined) => {
661
- dict[combined.slug] = combined.page
662
- return dict
663
- }, {}),
664
- )
665
- },
666
- )
700
+ })
701
+ })
702
+
703
+ Promise.all(pagePromises)
704
+ .then(pages => {
705
+ const pageExport = pages.reduce((dict, combined) => {
706
+ dict[combined.slug] = combined.page
707
+ return dict
708
+ }, {})
709
+ // TODO: this fails for a very large site
710
+ res.json(pageExport)
711
+ })
712
+ .catch(error => {
713
+ res.e(error)
714
+ })
667
715
  })
668
716
  })
669
717
 
@@ -677,27 +725,54 @@ module.exports = exports = argv => {
677
725
  }
678
726
 
679
727
  app.get('/system/version.json', admin, (req, res) => {
728
+ const getPackageVersion = packageName => {
729
+ return new Promise(resolve => {
730
+ try {
731
+ // Use import to load package.json from the main application's working directory
732
+ import(`${packageName}/package.json`, { with: { type: 'json' } }).then(({ default: packageJson }) => {
733
+ resolve({ [packageName]: packageJson.version })
734
+ })
735
+ } catch (error) {
736
+ console.error(`Error reading package for ${packageName}:`, error)
737
+ resolve({ [packageName]: 'unknown' })
738
+ }
739
+ })
740
+ }
741
+
680
742
  const versions = {}
681
- const wikiModule = require.main
682
- versions[wikiModule.require('./package').name] = wikiModule.require('./package').version
683
- versions[wikiModule.require('wiki-server/package').name] = wikiModule.require('wiki-server/package').version
684
- versions[wikiModule.require('wiki-client/package').name] = wikiModule.require('wiki-client/package').version
685
-
686
- versions['security'] = {}
687
- Object.keys(require.main.require('./package').dependencies)
688
- .filter(depend => depend.startsWith('wiki-security'))
689
- .forEach(key => {
690
- versions.security[key] = wikiModule.require(`${key}/package`).version
743
+
744
+ const security = () => {
745
+ return new Promise(resolve => {
746
+ Promise.all(
747
+ Object.keys(packageJson.dependencies)
748
+ .filter(depend => depend.startsWith('wiki-security'))
749
+ .map(key => {
750
+ return getPackageVersion(key)
751
+ }),
752
+ ).then(values => {
753
+ resolve({ security: values.reduce((acc, cV) => Object.assign(acc, cV), {}) })
754
+ })
691
755
  })
756
+ }
692
757
 
693
- versions['plugins'] = {}
694
- Object.keys(require.main.require('./package').dependencies)
695
- .filter(depend => depend.startsWith('wiki-plugin'))
696
- .forEach(key => {
697
- versions.plugins[key] = wikiModule.require(`${key}/package`).version
758
+ const plugins = () => {
759
+ return new Promise(resolve => {
760
+ Promise.all(
761
+ Object.keys(packageJson.dependencies)
762
+ .filter(depend => depend.startsWith('wiki-plugin'))
763
+ .map(key => {
764
+ return getPackageVersion(key)
765
+ }),
766
+ ).then(values => {
767
+ resolve({ plugins: values.reduce((acc, cV) => Object.assign(acc, cV), {}) })
768
+ })
698
769
  })
770
+ }
699
771
 
700
- res.json(versions)
772
+ Promise.all([getPackageVersion('wiki-server'), getPackageVersion('wiki-client'), security(), plugins()]).then(v => {
773
+ Object.assign(versions, { [packageJson.name]: packageJson.version }, ...v)
774
+ res.json(versions)
775
+ })
701
776
  })
702
777
 
703
778
  // ##### Proxy routes #####
@@ -914,7 +989,7 @@ module.exports = exports = argv => {
914
989
  // ### Plugins ###
915
990
  // Should replace most WebSocketServers below.
916
991
  const plugins = pluginsFactory(argv)
917
- plugins.startServers({ argv, app })
992
+ plugins.startServers({ argv, app, packageJson })
918
993
  // ### Sitemap ###
919
994
  // create sitemap at start-up
920
995
  sitemaphandler.createSitemap(pagehandler)
package/lib/sitemap.js CHANGED
@@ -7,14 +7,13 @@
7
7
  */
8
8
 
9
9
  // **sitemap.coffee**
10
+ import fs from 'node:fs'
11
+ import path from 'node:path'
12
+ import events from 'node:events'
13
+ import writeFileAtomic from 'write-file-atomic'
14
+ import xml2js from 'xml2js'
10
15
 
11
- const fs = require('fs')
12
- const path = require('path')
13
- const events = require('events')
14
- const writeFileAtomic = require('write-file-atomic')
15
- const xml2js = require('xml2js')
16
-
17
- const synopsis = require('wiki-client/lib/synopsis')
16
+ import synopsis from 'wiki-client/lib/synopsis.js' // Add .js if needed
18
17
 
19
18
  const asSlug = name =>
20
19
  name
@@ -22,7 +21,7 @@ const asSlug = name =>
22
21
  .replace(/[^A-Za-z0-9-]/g, '')
23
22
  .toLowerCase()
24
23
 
25
- module.exports = exports = argv => {
24
+ export default argv => {
26
25
  const wikiName = new URL(argv.url).hostname
27
26
 
28
27
  let sitemap = []
package/package.json CHANGED
@@ -1,7 +1,8 @@
1
1
  {
2
2
  "name": "wiki-server",
3
3
  "description": "A Federated Wiki Server",
4
- "version": "0.26.0-rc.1",
4
+ "version": "0.26.0-rc.10",
5
+ "type": "module",
5
6
  "author": {
6
7
  "name": "Ward Cunningham",
7
8
  "email": "ward@c2.com",
@@ -29,38 +30,39 @@
29
30
  "Andrew Shell <andrew@andrewshell.org>",
30
31
  "jon r <jon@allmende.io>"
31
32
  ],
33
+ "main": "index.js",
32
34
  "dependencies": {
33
- "async": "^3.2.4",
34
- "body-parser": "^2.2.0",
35
+ "body-parser": "^2.2.1",
35
36
  "client-sessions": "^0.8.0",
36
37
  "coffeescript": "^2.5.0",
37
38
  "cookie-parser": "^1.4.4",
38
- "dompurify": "^3.1.0",
39
+ "dompurify": "^3.3.0",
39
40
  "errorhandler": "^1.5.1",
40
41
  "express": "^5.1.0",
41
42
  "express-hbs": "^2.5.0",
42
43
  "flates": "0.0.5",
43
- "jsdom": "^26.0.0",
44
+ "jsdom": "^27.2.0",
44
45
  "method-override": "^3.0.0",
45
- "minisearch": "^7.1.0",
46
- "morgan": "^1.9.1",
47
- "write-file-atomic": "^6.0.0",
46
+ "minisearch": "^7.2.0",
47
+ "morgan": "^1.10.1",
48
+ "write-file-atomic": "^7.0.0",
48
49
  "xml2js": "^0.6.2"
49
50
  },
50
51
  "scripts": {
51
52
  "prettier:format": "prettier --write './**/*.js'",
52
53
  "prettier:check": "prettier --check ./**/*.js",
53
- "test": "node --test",
54
+ "test": "cd test; node --test",
55
+ "watch": "cd test; node --test --watch",
54
56
  "update-authors": "node scripts/update-authors.js"
55
57
  },
56
58
  "devDependencies": {
57
- "@eslint/js": "^9.29.0",
58
- "eslint": "^9.29.0",
59
- "globals": "^16.1.0",
59
+ "@eslint/js": "^9.39.1",
60
+ "eslint": "^9.39.1",
61
+ "globals": "^16.5.0",
60
62
  "grunt-git-authors": "^3.2.0",
61
- "prettier": "^3.5.3",
62
- "supertest": "^7.0.0",
63
- "wiki-client": "^0.31.2",
63
+ "prettier": "^3.7.3",
64
+ "supertest": "^7.1.4",
65
+ "wiki-client": "^0.31.6",
64
66
  "wiki-plugin-activity": "0.7",
65
67
  "wiki-plugin-video": "^0.4"
66
68
  },