diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 00000000..13013b7f --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,34 @@ +name: Prettier Check + +on: [pull_request] + +jobs: + prettier: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: npm ci + + - name: Run Prettier check + run: npm run prettier:check + + # - name: Annotate Pull Request with Results + # if: failure() + # uses: actions/github-script@v7 + # with: + # script: | + # github.rest.issues.createComment({ + # issue_number: context.issue.number, + # owner: context.repo.owner, + # repo: context.repo.repo, + # body: '⚠️ Prettier found formatting issues. Please fix them.' + # }) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml deleted file mode 100644 index 093a9544..00000000 --- a/.github/workflows/node.js.yml +++ /dev/null @@ -1,31 +0,0 @@ -# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs - -name: Node.js CI - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [18.x, 20.x, 22.x] - # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ - - steps: - - uses: actions/checkout@v4 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - cache: 'npm' - - run: npm ci - - run: npm run build --if-present - - run: npm test diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..776a3260 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,28 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + # Releases https://github.com/nodejs/release#release-schedule + node-version: + - 20.x # Maintenance + - 22.x # LTS + - 24.x # Current + + steps: + - uses: actions/checkout@v4 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci + - run: npm test diff --git a/.gitignore b/.gitignore index 3908c983..34faae4d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,13 +7,8 @@ config.json /data /spec/data -/client/client.js -/client/test/testclient.js -/client/plugins/*.js -/client/plugins/**/*.js -/client/plugins/pushpin/**/*.js -/client/chart/ -/client/garden/ /default-data/status/sitemap.json node_modules npm-debug.log +temp +coverage diff --git a/.npmignore b/.npmignore index fc4d4fd3..fbd220b3 100644 --- a/.npmignore +++ b/.npmignore @@ -1,3 +1,11 @@ /data config.json - +.github/ +.mailmap +.prettier* +.vscode/ +.zed/ +eslint.config.js +scripts/ +test/ +coverage diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..4ebc8aea --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +coverage diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..4aa98acb --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "semi": false, + "singleQuote": true, + "bracketSpacing": true, + "bracketSameLine": true, + "arrowParens": "avoid", + "printWidth": 120 +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..d7df89c9 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..02db64b7 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true +} diff --git a/.zed/settings.json b/.zed/settings.json new file mode 100644 index 00000000..8cbcf3cc --- /dev/null +++ b/.zed/settings.json @@ -0,0 +1,7 @@ +// Folder-specific settings +// +// For a full list of overridable settings, and general information on folder-specific settings, +// see the documentation: https://zed.dev/docs/configuring-zed#settings-files +{ + "format_on_save": "on" +} diff --git a/AUTHORS.txt b/AUTHORS.txt index d39df252..651a637b 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -19,3 +19,4 @@ Eric Dobbs Joshua Benuck Tom Lieber Andrew Shell +jon r diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index 6671c27e..00000000 --- a/Gruntfile.js +++ /dev/null @@ -1,55 +0,0 @@ -module.exports = function( grunt ) { - - "use strict"; - - grunt.loadNpmTasks('grunt-contrib-watch'); - grunt.loadNpmTasks('grunt-mocha-test'); - grunt.loadNpmTasks('grunt-git-authors'); - - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - - mochaTest: { - test: { - options: { - reporter: 'spec', - require: [ - 'coffeescript/register', - 'should' - ] - }, - src: [ - 'test/defaultargs.coffee', - 'test/page.coffee', - 'test/random.coffee', - 'test/server.coffee', - 'test/sitemap.coffee' - ] - } - }, - - authors: { - prior: [ - "Ward Cunningham ", - "Nick Niemeir ", - "Patrick Mueller ", - "Erkan Yilmaz ", - "Tom Lee ", - "Nicholas Hallahan ", - "Paul Rodwell ", - "Austin King " - ] - }, - - watch: { - all: { - files: ['lib/*.coffee', 'test/*.coffee'], - tasks: ['mochaTest'] - } - } - }); - - grunt.registerTask('default', ['mochaTest']); - grunt.registerTask('check', ['retire']); - -} diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 00000000..0877defc --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,24 @@ +import globals from 'globals' +import pluginJs from '@eslint/js' + +/** @type {import('eslint').Linter.Config[]} */ +export default [ + pluginJs.configs.recommended, + { + rules: { + 'no-unused-vars': 'warn', + }, + }, + { ignores: ['client/*'] }, + { + languageOptions: { + sourceType: 'commonjs', + globals: { + wiki: 'readonly', + ...globals.node, + ...globals.nodeBuiltin, + ...globals.mocha, + }, + }, + }, +] diff --git a/index.js b/index.js index 8f4480d0..2c472d71 100644 --- a/index.js +++ b/index.js @@ -2,11 +2,7 @@ // Simple file so that if you require this directory // in node it instead requires ./lib/server.coffee // with coffee-script already loaded. -require('coffeescript'); -require('coffeescript/register'); +require('coffeescript') +require('coffeescript/register') -// set a default process exitCode, so we can diferentiate between exiting as -// part of a reload, and an exit after an uncaught error -process.exitCode = 1 - -module.exports = require('./lib/server'); +module.exports = require('./lib/server') diff --git a/lib/defaultargs.coffee b/lib/defaultargs.coffee deleted file mode 100644 index 3fac264f..00000000 --- a/lib/defaultargs.coffee +++ /dev/null @@ -1,77 +0,0 @@ -### - * Federated Wiki : Node Server - * - * Copyright Ward Cunningham and other contributors - * Licensed under the MIT license. - * https://github.com/fedwiki/wiki-server/blob/master/LICENSE.txt -### - - -# **defaultargs.coffee** when called on the argv object this -# module will create reasonable defaults for options not supplied, -# based on what information is provided. -path = require 'path' - -getUserHome = -> - process.env.HOME or process.env.HOMEPATH or process.env.USERPROFILE - -module.exports = (argv) -> - argv or= {} - argv.root or= __dirname - # the directory that contains all the packages that makeup the wiki - argv.packageDir or= path.join(argv.root, "..") - argv.port or= 3000 - argv.home or= 'welcome-visitors' - argv.data or= path.join(getUserHome(), '.wiki') # see also cli - argv.client or= path.join(argv.packageDir, 'wiki-client', 'client') - argv.db or= path.join(argv.data, 'pages') - argv.status or= path.join(argv.data, 'status') - argv.assets or= path.join(argv.data, 'assets') - argv.recycler or= path.join(argv.data, 'recycle') - argv.commons or= path.join(argv.data, 'commons') - argv.url or= 'http://localhost' + (if argv.port is 80 then '' else ':' + argv.port) - argv.id or= path.join(argv.status, 'owner.json') - argv.uploadLimit or= '5mb' - argv.cookieSecret or= require('crypto').randomBytes(64).toString('hex') - argv.secure_cookie or= false - argv.session_duration or= 7 - argv.neighbors or= '' - argv.debug or= false - - if typeof(argv.database) is 'string' - argv.database = JSON.parse(argv.database) - argv.database or= {} - argv.database.type or= './page' - if argv.database.type.charAt(0) is '.' - if argv.database.type != './page' - console.log "\n\nWARNING: This storage option is depeciated." - console.log " See ReadMe for details of the changes required.\n\n" - else - argv.database.type = 'wiki-storage-' + argv.database.type - - argv.security_type or= './security' - if argv.security_type is './security' - console.log "\n\nINFORMATION: Using default security module." - else - argv.security_type = 'wiki-security-' + argv.security_type - argv.security_legacy or= false - - #resolve all relative paths - argv.root = path.resolve(argv.root) - argv.packageDir = path.resolve(argv.packageDir) - argv.data = path.resolve(argv.data) - argv.client = path.resolve(argv.client) - argv.db = path.resolve(argv.db) - argv.status = path.resolve(argv.status) - argv.assets = path.resolve(argv.assets) - argv.recycler = path.resolve(argv.recycler) - argv.commons = path.resolve(argv.commons) - argv.id = path.resolve(argv.id) - - if /node_modules/.test(argv.data) - console.log "\n\nWARNING : The dafault data path is not a safe place." - console.log " : by using ", argv.data, " your pages will be lost when packages are updated." - console.log " : You are strongly advised to use an alternative directory." - console.log " : See the wiki package ReadMe for how to do this.\n\n" - - argv diff --git a/lib/defaultargs.js b/lib/defaultargs.js new file mode 100644 index 00000000..1f050009 --- /dev/null +++ b/lib/defaultargs.js @@ -0,0 +1,83 @@ +/* + * Federated Wiki : Node Server + * + * Copyright Ward Cunningham and other contributors + * Licensed under the MIT license. + * https://github.com/fedwiki/wiki-server/blob/master/LICENSE.txt + */ + +// **defaultargs.coffee** when called on the argv object this +// module will create reasonable defaults for options not supplied, +// based on what information is provided. +const path = require('node:path') + +const getUserHome = () => { + return process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE +} + +module.exports = argv => { + argv = argv || {} + argv.root ||= __dirname + // the directory that contains all the packages that makeup the wiki + argv.packageDir ||= path.join(argv.root, '..') + argv.port ||= 3000 + argv.home ||= 'welcome-visitors' + argv.data ||= path.join(getUserHome(), '.wiki') // see also cli + argv.client ||= path.join(argv.packageDir, 'wiki-client', 'client') + argv.db ||= path.join(argv.data, 'pages') + argv.status ||= path.join(argv.data, 'status') + argv.assets ||= path.join(argv.data, 'assets') + argv.recycler ||= path.join(argv.data, 'recycle') + argv.commons ||= path.join(argv.data, 'commons') + argv.url ||= `http://localhost${argv.port === 80 ? '' : ':' + argv.port}` + argv.id ||= path.join(argv.status, 'owner.json') + argv.uploadLimit ||= '5mb' + argv.cookieSecret ||= require('crypto').randomBytes(64).toString('hex') + argv.secure_cookie ||= false + argv.session_duration ||= 7 + argv.neighbors ||= '' + argv.debug ||= false + argv.test ||= false + + if (typeof argv.database === 'string') { + argv.database = JSON.parse(argv.database) + } + argv.database ||= {} + argv.database.type ||= './page' + if (argv.database.type.charAt(0) === '.') { + if (argv.database.type != './page') { + console.log('\n\nWARNING: This storage option is depeciated.') + console.log(' See ReadMe for details of the changes required.\n\n') + } + } else { + argv.database.type = 'wiki-storage-' + argv.database.type + } + + argv.security_type ||= './security' + if (argv.security_type === './security') { + console.log('\n\nINFORMATION: Using default security module.') + } else { + argv.security_type = 'wiki-security-' + argv.security_type + } + argv.security_legacy ||= false + + // resolve all relative paths + argv.root = path.resolve(argv.root) + argv.packageDir = path.resolve(argv.packageDir) + argv.data = path.resolve(argv.data) + argv.client = path.resolve(argv.client) + argv.db = path.resolve(argv.db) + argv.status = path.resolve(argv.status) + argv.assets = path.resolve(argv.assets) + argv.recycler = path.resolve(argv.recycler) + argv.commons = path.resolve(argv.commons) + argv.id = path.resolve(argv.id) + + if (/node_modules/.test(argv.data)) { + console.log('\n\nWARNING : The dafault data path is not a safe place.') + console.log(' : by using ', argv.data, ' your pages will be lost when packages are updated.') + console.log(' : You are strongly advised to use an alternative directory.') + console.log(' : See the wiki package ReadMe for how to do this.\n\n') + } + return argv +} diff --git a/lib/forward.js b/lib/forward.js index d5ddd30e..8c608364 100644 --- a/lib/forward.js +++ b/lib/forward.js @@ -1,12 +1,12 @@ const init = (app, emitter) => { let sockets = [] - app.io.on('connection', (socket) => { + app.io.on('connection', socket => { let listeners = [] sockets.push(socket) console.log('client connected:', socket.handshake.address) - socket.on('disconnect', (reason) => { + socket.on('disconnect', reason => { console.log('client disconnected:', socket.handshake.address, reason) - for (let {sProducer, listener} of listeners) { + for (let { sProducer, listener } of listeners) { console.log('removing listener:', sProducer) emitter.removeListener(sProducer, listener) } @@ -14,9 +14,9 @@ const init = (app, emitter) => { let i = sockets.indexOf(socket) sockets.splice(i, 1) }) - socket.on('unsubscribe', (sProducer) => { + socket.on('unsubscribe', sProducer => { console.log('unsubscribing listener:', socket.handshake.address, sProducer) - for (let [i, {slugItem, listener}] of listeners.entries()) { + for (let [i, { slugItem, listener }] of listeners.entries()) { if (slugItem == sProducer) { console.log('removing listener:', sProducer) emitter.removeListener(sProducer, listener) @@ -24,15 +24,15 @@ const init = (app, emitter) => { } } }) - socket.on('subscribe', (sProducer) => { - let listener = (result) => { + socket.on('subscribe', sProducer => { + let listener = result => { console.log('forwarding:', socket.handshake.address, result) - socket.emit(sProducer, {slugItem: sProducer, result}) + socket.emit(sProducer, { slugItem: sProducer, result }) } console.log(`registering listener:`, socket.handshake.address, sProducer) emitter.on(sProducer, listener) - listeners.push({sProducer, listener}) + listeners.push({ sProducer, listener }) }) }) } -module.exports = {init} +module.exports = { init } diff --git a/lib/page.coffee b/lib/page.coffee deleted file mode 100644 index 44f138f5..00000000 --- a/lib/page.coffee +++ /dev/null @@ -1,371 +0,0 @@ -### - * Federated Wiki : Node Server - * - * Copyright Ward Cunningham and other contributors - * Licensed under the MIT license. - * https://github.com/fedwiki/wiki-server/blob/master/LICENSE.txt -### -# **page.coffee** -# Module for interacting with pages persisted on the server. -# Everything is stored using json flat files. - -#### Requires #### -fs = require 'fs' -path = require 'path' -events = require 'events' -glob = require 'glob' - -async = require 'async' - -random_id = require './random_id' -synopsis = require 'wiki-client/lib/synopsis' - -asSlug = (name) -> - name.replace(/\s/g, '-').replace(/[^A-Za-z0-9-]/g, '').toLowerCase() - - -# Export a function that generates a page handler -# when called with options object. -module.exports = exports = (argv) -> - - wikiName = new URL(argv.url).hostname - - fs.mkdir argv.db, { recursive: true }, (e) -> - if e then throw e - - #### Private utility methods. #### - load_parse = (loc, cb, annotations={}) -> - fs.readFile(loc, (err, data) -> - return cb(err) if err - try - page = JSON.parse(data) - catch e - errorPage = path.basename(loc) - errorPagePath = path.dirname(loc) - recyclePage = path.resolve(errorPagePath, '..', 'recycle', errorPage) - fs.exists(path.dirname(recyclePage), (exists) -> - if exists - fs.rename(loc, recyclePage, (err) -> - if err - console.log "ERROR: moving problem page #{loc} to recycler", err - else - console.log "ERROR: problem page #{loc} moved to recycler" - ) - else - fs.mkdir(path.dirname(recyclePage), { recursive: true }, (err) -> - if err - console.log "ERROR: creating recycler", err - else - fs.rename(loc, recyclePage, (err) -> - if err - console.log "ERROR: moving problem page #{loc} to recycler", err - else - console.log "ERROR: problem page #{loc} moved to recycler" - ) - ) - ) - return cb(null, 'Error Parsing Page', 404) - for key, val of annotations - page[key] = val - cb(null, page) - ) - - load_parse_copy = (defloc, file, cb) -> - fs.readFile(defloc, (err, data) -> - if err then cb(err) - try - page = JSON.parse(data) - catch e - return cb(e) - cb(null, page) - itself.put(file, page, (err) -> - if err then cb(err) - ) - ) - - # Reads and writes are async, but serially queued to avoid race conditions. - queue = [] - - tryDefaults = (file, cb) -> - lastDefault = (cb) -> - defloc = path.join(argv.root, 'default-data', 'pages', file) - fs.exists defloc, (exists) -> - if exists - cb(defloc) - else - cb(null) - if argv.defaults - defloc = path.join(argv.data, '..', argv.defaults, 'pages', file) - console.log 'firstDefault', defloc - fs.exists defloc, (exists) -> - if exists - cb(defloc) - else - lastDefault(cb) - else - lastDefault(cb) - - # Main file io function, when called without page it reads, - # when called with page it writes. - fileio = (action, file, page, cb) -> - if file.startsWith 'recycler/' - loc = path.join(argv.recycler, file.split('/')[1]) - else - loc = path.join(argv.db, file) - switch action - when 'delete' - if file.startsWith 'recycler/' - # delete from recycler - fs.exists(loc, (exists) -> - if exists - fs.unlink(loc, (err) -> - cb(err) - ) - ) - else - # move page to recycler - fs.exists(loc, (exists) -> - if exists - recycleLoc = path.join(argv.recycler, file) - fs.exists(path.dirname(recycleLoc), (exists) -> - if exists - fs.rename(loc, recycleLoc, (err) -> - cb(err) - ) - else - fs.mkdir(path.dirname(recycleLoc), { recursive: true }, (err) -> - if err then cb(err) - fs.rename(loc, recycleLoc, (err) -> - cb(err) - ) - ) - ) - else - cb('page does not exist') - ) - when 'recycle' - copyFile = (source, target, cb) -> - - done = (err) -> - if !cbCalled - cb err - cbCalled = true - return - - cbCalled = false - - rd = fs.createReadStream(source) - rd.on 'error', (err) -> - done err - return - - wr = fs.createWriteStream(target) - wr.on 'error', (err) -> - done err - return - wr.on 'close', (ex) -> - done() - return - rd.pipe wr - return - - fs.exists(loc, (exists) -> - if exists - recycleLoc = path.join(argv.recycler, file) - fs.exists(path.dirname(recycleLoc), (exists) -> - if exists - copyFile(loc, recycleLoc, (err) -> - cb(err) - ) - else - fs.mkdir(path.dirname(recycleLoc), { recursive: true }, (err) -> - if err then cb(err) - copyFile(loc, recycleLoc, (err) -> - cb(err) - ) - ) - ) - else - cb('page does not exist') - ) - when 'get' - fs.exists(loc, (exists) -> - if exists - load_parse(loc, cb, {plugin: undefined}) - else - tryDefaults(file, (defloc) -> - if defloc - load_parse(defloc, cb) - else - glob "wiki-plugin-*/pages", {cwd: argv.packageDir}, (e, plugins) -> - if e then return cb(e) - - # if no plugins found - if plugins.length is 0 - cb(null, 'Page not found', 404) - - giveUp = do -> - count = plugins.length - return -> - count -= 1 - if count is 0 - cb(null, 'Page not found', 404) - - for plugin in plugins - do -> - pluginName = plugin.slice(12, -6) - pluginloc = path.join(argv.packageDir, plugin, file) - fs.exists(pluginloc, (exists) -> - if exists - load_parse(pluginloc, cb, {plugin: pluginName}) - else - giveUp() - ) - ) - ) - when 'put' - page = JSON.stringify(page, null, 2) - fs.exists(path.dirname(loc), (exists) -> - if exists - fs.writeFile(loc, page, (err) -> - if err - console.log "ERROR: write file #{loc} ", err - cb(err) - ) - else - fs.mkdir(path.dirname(loc), { recursive: true }, (err) -> - if err then cb(err) - fs.writeFile(loc, page, (err) -> - if err - console.log "ERROR: write file #{loc} ", err - cb(err) - ) - ) - ) - else - console.log "pagehandler: unrecognized action #{action}" - - # Control variable that tells if the serial queue is currently working. - # Set back to false when all jobs are complete. - working = false - - # Keep file io working on queued jobs, but don't block the main thread. - serial = (item) -> - if item - itself.start() - fileio(item.action, item.file, item.page, (err, data, status) -> - process.nextTick( -> - serial(queue.shift()) - ) - item.cb(err, data, status) - ) - else - itself.stop() - - #### Public stuff #### - # Make the exported object an instance of EventEmitter - # so other modules can tell if it is working or not. - itself = new events.EventEmitter - itself.start = -> - working = true - @emit 'working' - itself.stop = -> - working = false - @emit 'finished' - - itself.isWorking = -> - working - - # get method takes a slug and a callback, adding them to the queue, - # starting serial if it isn't already working. - itself.get = (file, cb) -> - queue.push({action: 'get', file, page: null, cb}) - serial(queue.shift()) unless working - - # put takes a slugged name, the page as a json object, and a callback. - # adds them to the queue, and starts it unless it is working. - itself.put = (file, page, cb) -> - queue.push({action: 'put', file, page, cb}) - serial(queue.shift()) unless working - - itself.delete = (file, cb) -> - queue.push({action: 'delete', file, page: null, cb}) - serial(queue.shift()) unless working - - itself.saveToRecycler = (file, cb) -> - queue.push({action: 'recycle', file, page: null, cb}) - serial(queue.shift()) unless working - - editDate = (journal) -> - for action in (journal || []) by -1 - return action.date if action.date and action.type != 'fork' - undefined - - itself.pages = (cb) -> - - extractPageLinks = (collaborativeLinks, currentItem, currentIndex, array) -> - # extract collaborative links - # - this will need extending if we also extract the id of the item containing the link - try - linkRe = /\[\[([^\]]+)\]\]/g - match = undefined - while (match = linkRe.exec(currentItem.text)) != null - if not collaborativeLinks.has(asSlug(match[1])) - collaborativeLinks.set(asSlug(match[1]), currentItem.id) - if 'reference' == currentItem.type - if not collaborativeLinks.has(currentItem.slug) - collaborativeLinks.set(currentItem.slug, currentItem.id) - catch err - console.log "METADATA *** #{wikiName} Error extracting links from #{currentIndex} of #{JSON.stringify(array)}", err.message - collaborativeLinks - - fs.readdir argv.db, (e, files) -> - return cb(e) if e - # used to make sure all of the files are read - # and processesed in the site map before responding - doSitemap = (file, cb) -> - itself.get file, (e, page, status) -> - return cb() if file.match /^\./ - if e or status is 404 - console.log 'Problem building sitemap:', file, 'e: ', e, 'status:', status - return cb() # Ignore errors in the pagehandler get. - - try - pageLinksMap = page.story.reduce( extractPageLinks, new Map()) - catch err - console.log "METADATA *** #{wikiName} reduce to extract links on #{file} failed", err.message - pageLinksMap = [] - # - if pageLinksMap.size > 0 - pageLinks = Object.fromEntries(pageLinksMap) - else - pageLinks = undefined - - cb null, { - slug : file - title : page.title - date : editDate(page.journal) - synopsis : synopsis(page) - links : pageLinks - } - - async.map files, doSitemap, (e, sitemap) -> - return cb(e) if e - cb null, sitemap.filter (item) -> if item? then true - - itself.slugs = (cb) -> - fs.readdir argv.db, {withFileTypes: true}, (e, files) -> - if e - console.log 'Problem reading pages directory', e - cb(e) - else - onlyFiles = files.map((i) -> - if i.isFile() - return i.name - else - return null - ).filter((i) -> - i != null && !i?.startsWith('.')) - cb(null, onlyFiles) - - itself diff --git a/lib/page.js b/lib/page.js new file mode 100644 index 00000000..da44f7eb --- /dev/null +++ b/lib/page.js @@ -0,0 +1,436 @@ +/* + * Federated Wiki : Node Server + * + * Copyright Ward Cunningham and other contributors + * Licensed under the MIT license. + * https://github.com/fedwiki/wiki-server/blob/master/LICENSE.txt + */ +// **page.coffee** +// Module for interacting with pages persisted on the server. +// Everything is stored using json flat files. + +// #### Requires #### +const fs = require('fs') +const path = require('path') +const events = require('events') + +const random_id = require('./random_id') +const synopsis = require('wiki-client/lib/synopsis') + +const asSlug = name => + name + .replace(/\s/g, '-') + .replace(/[^A-Za-z0-9-]/g, '') + .toLowerCase() + +// Export a function that generates a page handler +// when called with options object. +module.exports = exports = argv => { + const wikiName = new URL(argv.url).hostname + + fs.mkdir(argv.db, { recursive: true }, e => { + if (e) throw e + }) + + // create a list of plugin pages. + const pluginPages = new Map() + Object.keys(require.main.require('./package').dependencies) + .filter(depend => depend.startsWith('wiki-plugin')) + .forEach(plugin => { + const pagesPath = path.join( + path.dirname(require.resolve(`${plugin}/package`, { paths: require.main.paths })), + 'pages', + ) + fs.readdir(pagesPath, { withFileTypes: true }, (err, entries) => { + if (err) return + entries.forEach(entry => { + if (entry.isFile() && !pluginPages.has(entry.name)) { + pluginPages.set(entry.name, { pluginName: plugin, pluginPath: entry.parentPath }) + } + }) + }) + }) + + // #### Private utility methods. #### + const load_parse = (loc, cb, annotations = {}) => { + let page + fs.readFile(loc, (err, data) => { + if (err) return cb(err) + try { + page = JSON.parse(data) + } catch { + const errorPage = path.basename(loc) + const errorPagePath = path.dirname(loc) + const recyclePage = path.resolve(errorPagePath, '..', 'recycle', errorPage) + fs.access(path.dirname(recyclePage), fs.constants.F_OK, err => { + if (!err) { + fs.rename(loc, recyclePage, err => { + if (err) { + console.log(`ERROR: moving problem page ${loc} to recycler`, err) + } else { + console.log(`ERROR: problem page ${loc} moved to recycler`) + } + }) + } else { + fs.mkdir(path.dirname(recyclePage), { recursive: true }, err => { + if (err) { + console.log('ERROR: creating recycler', err) + } else { + fs.rename(loc, recyclePage, err => { + if (err) { + console.log(`ERROR: moving problem page ${loc} to recycler`, err) + } else { + console.log(`ERROR: problem page ${loc} moved to recycler`) + } + }) + } + }) + } + }) + + return cb(null, 'Error Parsing Page', 404) + } + for (const [key, val] of Object.entries(annotations)) { + page[key] = val + } + cb(null, page) + }) + } + + const load_parse_copy = (defloc, file, cb) => { + fs.readFile(defloc, (err, data) => { + if (err) cb(err) + let page + try { + page = JSON.parse(data) + } catch (e) { + return cb(e) + } + cb(null, page) + // TODO: what is happening here?! put will never be reached??? + itself.put(file, page, err => { + if (err) cb(err) + }) + }) + } + // Reads and writes are async, but serially queued to avoid race conditions. + const queue = [] + + const tryDefaults = (file, cb) => { + const lastDefault = cb => { + const defloc = path.join(argv.root, 'default-data', 'pages', file) + fs.access(defloc, fs.constants.F_OK, err => { + if (!err) { + cb(defloc) + } else { + cb(null) + } + }) + } + if (argv.defaults) { + const defloc = path.join(argv.data, '..', argv.defaults, 'pages', file) + fs.access(defloc, fs.constants.F_OK, err => { + if (!err) { + cb(defloc) + } else { + lastDefault(cb) + } + }) + } else { + lastDefault(cb) + } + } + + // Main file io function, when called without page it reads, + // when called with page it writes. + const fileio = (action, file, page, cb) => { + const loc = file.startsWith('recycler/') ? path.join(argv.recycler, file.split('/')[1]) : path.join(argv.db, file) + + switch (action) { + case 'delete': + if (file.startsWith('recycler/')) { + // delete from recycler + fs.access(loc, fs.constants.F_OK, err => { + if (!err) + fs.unlink(loc, err => { + cb(err) + }) + }) + } else { + // move page to recycler + fs.access(loc, fs.constants.F_OK, err => { + if (!err) { + const recycleLoc = path.join(argv.recycler, file) + fs.access(path.dirname(recycleLoc), fs.constants.F_OK, err => { + if (!err) { + fs.rename(loc, recycleLoc, err => { + cb(err) + }) + } else { + fs.mkdir(path.dirname(recycleLoc), { recursive: true }, err => { + if (err) cb(err) + fs.rename(loc, recycleLoc, err => { + cb(err) + }) + }) + } + }) + } else { + cb('page does not exist') + } + }) + } + break + case 'recycle': { + const copyFile = (source, target, cb) => { + const done = err => { + if (!cbCalled) { + cb(err) + cbCalled = true + } + return + } + + let cbCalled = false + + const rd = fs.createReadStream(source) + rd.on('error', err => { + done(err) + return + }) + + const wr = fs.createWriteStream(target) + wr.on('error', err => { + done(err) + return + }) + wr.on('close', () => { + done() + return + }) + rd.pipe(wr) + return + } + + fs.access(loc, fs.constants.F_OK, err => { + if (!err) { + const recycleLoc = path.join(argv.recycler, file) + fs.access(path.dirname(recycleLoc), fs.constants.F_OK, err => { + if (!err) { + copyFile(loc, recycleLoc, err => { + cb(err) + }) + } else { + fs.mkdir(path.dirname(recycleLoc), { recursive: true }, err => { + if (err) cb(err) + copyFile(loc, recycleLoc, err => { + cb(err) + }) + }) + } + }) + } else { + cb('page does not exist') + } + }) + break + } + case 'get': + fs.access(loc, fs.constants.F_OK, err => { + if (!err) { + load_parse(loc, cb, { plugin: undefined }) + } else { + tryDefaults(file, defloc => { + if (defloc) { + load_parse(defloc, cb) + } else { + if (pluginPages.has(file)) { + const { pluginName, pluginPath } = pluginPages.get(file) + load_parse(path.join(pluginPath, file), cb, { plugin: pluginName.slice(12) }) + } else { + cb(null, 'Page not found', 404) + } + } + }) + } + }) + break + case 'put': + page = JSON.stringify(page, null, 2) + fs.access(path.dirname(loc), fs.constants.F_OK, err => { + if (!err) { + fs.writeFile(loc, page, err => { + if (err) { + console.log(`ERROR: write file ${loc} `, err) + } + cb(err) + }) + } else { + fs.mkdir(path.dirname(loc), { recursive: true }, err => { + if (err) cb(err) + fs.writeFile(loc, page, err => { + if (err) { + console.log(`ERROR: write file ${loc} `, err) + } + cb(err) + }) + }) + } + }) + break + default: + console.log(`pagehandler: unrecognized action ${action}`) + } + } + + // Control variable that tells if the serial queue is currently working. + // Set back to false when all jobs are complete. + let working = false + + // Keep file io working on queued jobs, but don't block the main thread. + const serial = item => { + if (item) { + itself.start() + fileio(item.action, item.file, item.page, (err, data, status) => { + process.nextTick(() => { + serial(queue.shift()) + }) + item.cb(err, data, status) + }) + } else { + itself.stop() + } + } + + // #### Public stuff #### + // Make the exported object an instance of EventEmitter + // so other modules can tell if it is working or not. + const itself = new events.EventEmitter() + + itself.start = () => { + working = true + itself.emit('working') + } + + itself.stop = () => { + working = false + itself.emit('finished') + } + + itself.isWorking = () => working + + // get method takes a slug and a callback, adding them to the queue, + // starting serial if it isn't already working. + itself.get = (file, cb) => { + queue.push({ action: 'get', file, page: null, cb }) + if (!working) serial(queue.shift()) + } + + // put takes a slugged name, the page as a json object, and a callback. + // adds them to the queue, and starts it unless it is working. + itself.put = (file, page, cb) => { + queue.push({ action: 'put', file, page, cb }) + if (!working) serial(queue.shift()) + } + + itself.delete = (file, cb) => { + queue.push({ action: 'delete', file, page: null, cb }) + if (!working) serial(queue.shift()) + } + + itself.saveToRecycler = (file, cb) => { + queue.push({ action: 'recycle', file, page: null, cb }) + if (!working) serial(queue.shift()) + } + + const editDate = journal => { + if (!journal) return undefined + // find the last journal entry, that is not a fork, with a date. + const last = journal.findLast(action => { + return action.date && action.type != 'fork' + }) + return last ? last.date : undefined + } + + itself.pages = cb => { + const extractPageLinks = (collaborativeLinks, currentItem, currentIndex, array) => { + // extract collaborative links + // - this will need extending if we also extract the id of the item containing the link + try { + const linkRe = /\[\[([^\]]+)\]\]/g + let match = undefined + while ((match = linkRe.exec(currentItem.text)) != null) { + if (!collaborativeLinks.has(asSlug(match[1]))) { + collaborativeLinks.set(asSlug(match[1]), currentItem.id) + } + } + if ('reference' == currentItem.type) { + if (!collaborativeLinks.has(currentItem.slug)) { + collaborativeLinks.set(currentItem.slug, currentItem.id) + } + } + } catch (err) { + console.log( + `METADATA *** ${wikiName} Error extracting links from ${currentIndex} of ${JSON.stringify(array)}`, + err.message, + ) + } + return collaborativeLinks + } + + fs.readdir(argv.db, (e, files) => { + if (e) return cb(e) + const doSitemap = async file => { + return new Promise(resolve => { + itself.get(file, (e, page, status) => { + if (file.match(/^\./)) return resolve(null) + if (e || status === 404) { + console.log('Problem building sitemap:', file, 'e: ', e, 'status:', status) + return resolve(null) // Ignore errors in the pagehandler get. + } + let pageLinksMap + try { + pageLinksMap = page.story.reduce(extractPageLinks, new Map()) + } catch (err) { + console.log(`METADATA *** ${wikiName} reduce to extract links on ${file} failed`, err.message) + pageLinksMap = [] + } + // + const pageLinks = pageLinksMap.size > 0 ? Object.fromEntries(pageLinksMap) : undefined + + resolve({ + slug: file, + title: page.title, + date: editDate(page.journal), + synopsis: synopsis(page), + links: pageLinks, + }) + }) + }) + } + + Promise.all(files.map(doSitemap)) + .then(sitemap => { + cb( + null, + sitemap.filter(item => item != null), + ) + }) + .catch(e => cb(e)) + }) + } + + itself.slugs = cb => { + fs.readdir(argv.db, { withFileTypes: true }, (e, files) => { + if (e) { + console.log('Problem reading pages directory', e) + return cb(e) + } + + const onlyFiles = files.map(i => (i.isFile() ? i.name : null)).filter(i => i != null && !i?.startsWith('.')) + cb(null, onlyFiles) + }) + } + + return itself +} diff --git a/lib/plugins.coffee b/lib/plugins.coffee deleted file mode 100644 index 3b646dfb..00000000 --- a/lib/plugins.coffee +++ /dev/null @@ -1,48 +0,0 @@ -### - * Federated Wiki : Node Server - * - * Copyright Ward Cunningham and other contributors - * Licensed under the MIT license. - * https://github.com/fedwiki/wiki-server/blob/master/LICENSE.txt -### - -# support server-side plugins - -fs = require 'fs' -path = require 'path' -glob = require 'glob' -events = require 'events' -{ pathToFileURL } = require 'node:url' -# forward = require './forward' - -module.exports = exports = (argv) -> - -# NOTE: plugins are now in their own package directories alongside this one... -# Plugins are in directories of the form wiki-package-* -# those with a server component will have a server directory - - plugins = {} - - # http://stackoverflow.com/questions/10914751/loading-node-js-modules-dynamically-based-on-route - - startServer = (params, plugin) -> - server = "#{argv.packageDir}/#{plugin}/server/server.js" - fs.exists server, (exists) -> - if exists - console.log 'starting plugin', plugin - import(pathToFileURL(server)).then((exported) -> - plugins[plugin] = exported - plugins[plugin].startServer?(params) - ).catch((e) -> - console.log 'failed to start plugin', plugin, e?.stack or e - ) - - startServers = (params) -> - # emitter = new events.EventEmitter() - # forward.init params.app, emitter - # params.emitter = emitter - glob "wiki-plugin-*", {cwd: argv.packageDir}, (e, plugins) -> - startServer params, plugin for plugin in plugins - - - {startServers} diff --git a/lib/plugins.js b/lib/plugins.js new file mode 100644 index 00000000..4bb48a6e --- /dev/null +++ b/lib/plugins.js @@ -0,0 +1,54 @@ +/* + * Federated Wiki : Node Server + * + * Copyright Ward Cunningham and other contributors + * Licensed under the MIT license. + * https://github.com/fedwiki/wiki-server/blob/master/LICENSE.txt + */ + +// support server-side plugins + +const fs = require('node:fs') +const { pathToFileURL } = require('node:url') +// forward = require './forward' + +module.exports = exports = argv => { + // NOTE: plugins are now in their own package directories alongside this one... + // Plugins are in directories of the form wiki-package-* + // those with a server component will have a server directory + + const plugins = {} + + // http://stackoverflow.com/questions/10914751/loading-node-js-modules-dynamically-based-on-route + + const startServer = (params, plugin) => { + const server = `${argv.packageDir}/${plugin}/server/server.js` + fs.access(server, fs.constants.F_OK, err => { + if (!err) { + console.log('starting plugin', plugin) + import(pathToFileURL(server)) + .then(exported => { + plugins[plugin] = exported + plugins[plugin].startServer?.(params) + }) + .catch(e => { + console.log('failed to start plugin', plugin, e?.stack || e) + }) + } + }) + } + + const startServers = params => { + // emitter = new events.EventEmitter() + // forward.init params.app, emitter + // params.emitter = emitter + + Object.keys(require.main.require('./package').dependencies) + .filter(depend => depend.startsWith('wiki-plugin')) + .forEach(plugin => { + startServer(params, plugin) + }) + } + + return { startServers } +} diff --git a/lib/random_id.coffee b/lib/random_id.coffee deleted file mode 100644 index 3ad9fd02..00000000 --- a/lib/random_id.coffee +++ /dev/null @@ -1,18 +0,0 @@ -### - * Federated Wiki : Node Server - * - * Copyright Ward Cunningham and other contributors - * Licensed under the MIT license. - * https://github.com/fedwiki/wiki-server/blob/master/LICENSE.txt -### - -# **random_id.coffee** -# Simple random hex generator, takes an optional number of -# chars that defaults to 16 and returns a random id. - -random_id = (chars = 16) -> - [0...chars].map( -> - Math.floor(Math.random() * 16).toString(16) - ).join('') - -module.exports = random_id.random_id = random_id diff --git a/lib/random_id.js b/lib/random_id.js new file mode 100644 index 00000000..52476aab --- /dev/null +++ b/lib/random_id.js @@ -0,0 +1,15 @@ +/* + * Federated Wiki : Node Server + * + * Copyright Ward Cunningham and other contributors + * Licensed under the MIT license. + * https://github.com/fedwiki/wiki-server/blob/master/LICENSE.txt + */ + +// **random_id.coffee** +// Simple random hex generator, takes an optional number of +// chars that defaults to 16 and returns a random id. + +const random_id = (chars = 16) => [...Array(chars)].map(() => Math.floor(Math.random() * 16).toString(16)).join('') + +module.exports = random_id.random_id = random_id diff --git a/lib/search.coffee b/lib/search.coffee deleted file mode 100644 index 86eee63b..00000000 --- a/lib/search.coffee +++ /dev/null @@ -1,304 +0,0 @@ -### - * Federated Wiki : Node Server - * - * Copyright Ward Cunningham and other contributors - * Licensed under the MIT license. - * https://github.com/fedwiki/wiki-server/blob/master/LICENSE.txt -### - -# **search.coffee** - -fs = require 'fs' -path = require 'path' -events = require 'events' -url = require 'node:url' -writeFileAtomic = require 'write-file-atomic' - -miniSearch = require 'minisearch' - -module.exports = exports = (argv) -> - - wikiName = new URL(argv.url).hostname - - siteIndex = [] - - queue = [] - - searchPageHandler = null - - # ms since last update we will remove index from memory - # orig - searchTimeoutMs = 1200000 - searchTimeoutMs = 120000 # temp reduce to 2 minutes - searchTimeoutHandler = null - - siteIndexLoc = path.join(argv.status, 'site-index.json') - indexUpdateFlag = path.join(argv.status, 'index-updated') - - working = false - - touch = (file, cb) -> - fs.stat file, (err, stats) -> - return cb() if err is null - fs.open file, 'w', (err,fd) -> - cb(err) if err - fs.close fd, (err) -> - cb(err) - - searchPageUpdate = (slug, page, cb) -> - # to update we have to remove the page first, and then readd it - try - pageText = page.story.reduce( extractPageText, '') - catch err - console.log "SITE INDEX *** #{wikiName} reduce to extract the text on #{slug} failed", err.message - pageText = "" - if siteIndex.has slug - siteIndex.replace { - 'id': slug - 'title': page.title - 'content': pageText - } - else - siteIndex.add { - 'id': slug - 'title': page.title - 'content': pageText - } - cb() - - searchPageRemove = (slug, cb) -> - # remove page from index - timeLabel = "SITE INDEX page remove #{slug} - #{wikiName}" - try - siteIndex.discard slug - catch err - # swallow error, if the page was not in index - console.log "removing #{slug} from index #{wikiName} failed", err unless err.message.includes('not in the index') - cb() - - searchSave = (siteIndex, cb) -> - # save index to file - fs.exists argv.status, (exists) -> - if exists - writeFileAtomic siteIndexLoc, JSON.stringify(siteIndex), (e) -> - return cb(e) if e - touch indexUpdateFlag, (err) -> - cb() - else - fs.mkdir argv.status, { recursive: true }, -> - writeFileAtomic siteIndexLoc, JSON.stringify(siteIndex), (e) -> - return cb(e) if e - touch indexUpdateFlag, (err) -> - cb() - - - searchRestore = (cb) -> - # restore index, or create if it doesn't already exist - fs.exists siteIndexLoc, (exists) -> - if exists - fs.readFile(siteIndexLoc, (err, data) -> - return cb(err) if err - try - siteIndex = miniSearch.loadJSON data, - fields: ['title', 'content'] - catch e - return cb(e) - process.nextTick( -> - serial(queue.shift()))) - - serial = (item) -> - if item - switch item.action - when "update" - itself.start() - searchPageUpdate(item.slug, item.page, (e) -> - process.nextTick( -> - serial(queue.shift()) - ) - ) - when "remove" - itself.start() - searchPageRemove(item.slug, (e) -> - process.nextTick( -> - serial(queue.shift()) - ) - ) - else - console.log "SITE INDEX *** unexpected action #{item.action} for #{item.page}" - process.nextTick( -> - serial(queue.shift)) - else - searchSave siteIndex, (e) -> - console.log "SITE INDEX *** save failed: " + e if e - itself.stop() - - extractItemText = (text) -> - return text.replace(/\[([^\]]*?)\][\[\(].*?[\]\)]/g, " $1 ") - .replace(/\[{2}|\[(?:[\S]+)|\]{1,2}/g, ' ') - .replace(/\n/g, ' ') - .replace(//g, ' ') - .replace(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g, ' ') - .replace(/<(?:[^>])+>/g, ' ') - .replace(/(https?:.*?)(?=\p{White_Space}|\p{Quotation_Mark}|$)/gu, (match) -> - myUrl = url.parse(match) - return myUrl.hostname + ' ' + myUrl.pathname) - .replace(/[\p{P}\p{Emoji}\p{Symbol}}]+/gu, ' ') - .replace /[\p{White_Space}\n\t]+/gu, ' ' - - - extractPageText = (pageText, currentItem, currentIndex, array) -> - # console.log('extractPageText', pageText, currentItem, currentIndex, array) - try - if currentItem.text? - switch currentItem.type - when 'paragraph', 'markdown', 'html', 'reference', 'image', 'pagefold', 'math', 'mathjax', 'code' - pageText += ' ' + extractItemText currentItem.text - when 'audio', 'video', 'frame' - pageText += ' ' + extractItemText(currentItem.text.split(/\r\n?|\n/) - .map((line) -> - firstWord = line.split(/\p{White_Space}/u)[0] - if firstWord.startsWith('http') or firstWord.toUpperCase() is firstWord or firstWord.startsWith('//') - # line is markup - return '' - else - return line - ).join(' ')) - catch err - throw new Error("Error extracting text from #{currentIndex}, #{JSON.stringify(currentItem)} #{err}, #{err.stack}") - pageText - - - #### Public stuff #### - - itself = new events.EventEmitter - itself.start = -> - clearTimeout(searchTimeoutHandler) - working = true - @emit 'indexing' - itself.stop = -> - clearsearch = -> - console.log "SITE INDEX #{wikiName} : removed from memory" - siteIndex = [] - clearTimeout(searchTimeoutHandler) - searchTimeoutHandler = setTimeout clearsearch, searchTimeoutMs - working = false - @emit 'indexed' - - itself.isWorking = -> - working - - itself.createIndex = (pagehandler) -> - - itself.start() - - # we save the pagehandler, so we can recreate the site index if it is removed - searchPageHandler = pagehandler if !searchPageHandler? - - #timeLabel = "SITE INDEX #{wikiName} : Created" - #console.time timeLabel - - pagehandler.slugs (e, slugs) -> - if e - console.log "SITE INDEX *** createIndex #{wikiName} error:", e - itself.stop() - return e - - siteIndex = new miniSearch({ - fields: ['title', 'content'] - }) - - indexPromises = slugs.map (slug) -> - return new Promise (resolve) -> - pagehandler.get slug, (err, page) -> - if err - console.log "SITE INDEX *** #{wikiName}: error reading page", slug - return - # page - try - pageText = page.story.reduce( extractPageText, '') - catch err - console.log "SITE INDEX *** #{wikiName} reduce to extract text on #{slug} failed", err.message - # console.log "page", page - pageText = "" - siteIndex.add { - 'id': slug - 'title': page.title - 'content': pageText - } - resolve() - - Promise.all(indexPromises) - .then () -> - # console.timeEnd timeLabel - process.nextTick ( -> - serial(queue.shift())) - - itself.removePage = (slug) -> - action = "remove" - queue.push({action, slug }) - if Array.isArray(siteIndex) and !working - itself.start() - searchRestore (e) -> - console.log "SITE INDEX *** Problems restoring search index #{wikiName}:" + e if e - itself.createIndex(searchPageHandler) - else - serial(queue.shift()) unless working - - itself.update = (slug, page) -> - action = "update" - queue.push({action, slug, page}) - if Array.isArray(siteIndex) and !working - itself.start() - searchRestore( (e) -> - console.log "SITE INDEX *** Problems restoring search index #{wikiName}:" + e if e - itself.createIndex(searchPageHandler)) - else - serial(queue.shift()) unless working - - itself.startUp = (pagehandler) -> - # called on server startup, here we check if wiki already is index - # we only create an index if there is either no index or there have been updates since last startup - console.log "SITE INDEX #{wikiName} : StartUp" - fs.stat siteIndexLoc, (err, stats) -> - if err is null - # site index exists, but has it been updated? - fs.stat indexUpdateFlag, (err, stats) -> - if !err - # index has been updated, so recreate it. - itself.createIndex pagehandler - # remove the update flag once the index has been created - itself.once 'indexed', -> - fs.unlink indexUpdateFlag, (err) -> - console.log "+++ SITE INDEX #{wikiName} : unable to delete update flag" if err - else - # not been updated, but is it the correct version? - fs.readFile siteIndexLoc, (err, data) -> - if !err - try - testIndex = JSON.parse(data) - catch err - testIndex = {} - if testIndex.serializationVersion != 2 - console.log "+++ SITE INDEX #{wikiName} : updating to latest version." - itself.createIndex pagehandler - # remove the update flag once the index has been created - itself.once 'indexed', -> - fs.unlink indexUpdateFlag, (err) -> - console.log "+++ SITE INDEX #{wikiName} : unable to delete update flag" if err - else - console.log "+++ SITE INDEX #{wikiName} : error reading index - attempting creating" - itself.createIndex pagehandler - # remove the update flag once the index has been created - itself.once 'indexed', -> - fs.unlink indexUpdateFlag, (err) -> - console.log "+++ SITE INDEX #{wikiName} : unable to delete update flag" if err - else - # index does not exist, so create it - itself.createIndex pagehandler - # remove the update flag once the index has been created - itself.once 'indexed', -> - fs.unlink indexUpdateFlag, (err) -> - console.log "+++ SITE INDEX #{wikiName} : unable to delete update flag" if err - - - - itself diff --git a/lib/search.js b/lib/search.js new file mode 100644 index 00000000..f318f74a --- /dev/null +++ b/lib/search.js @@ -0,0 +1,394 @@ +/* + * Federated Wiki : Node Server + * + * Copyright Ward Cunningham and other contributors + * Licensed under the MIT license. + * https://github.com/fedwiki/wiki-server/blob/master/LICENSE.txt + */ + +// **search.js** + +const fs = require('node:fs') +const path = require('node:path') +const events = require('node:events') +const url = require('node:url') +const writeFileAtomic = require('write-file-atomic') + +const miniSearch = require('minisearch') + +module.exports = exports = argv => { + const wikiName = new URL(argv.url).hostname + let siteIndex = [] + const queue = [] + + let searchPageHandler = null + + // ms since last update we will remove index from memory + // orig - searchTimeoutMs = 1200000 + const searchTimeoutMs = 120000 // temp reduce to 2 minutes + let searchTimeoutHandler = null + + const siteIndexLoc = path.join(argv.status, 'site-index.json') + const indexUpdateFlag = path.join(argv.status, 'index-updated') + + let working = false + + const touch = (file, cb) => { + fs.stat(file, (err, stats) => { + if (err === null) return cb() + fs.open(file, 'w', (err, fd) => { + if (err) cb(err) + fs.close(fd, err => { + cb(err) + }) + }) + }) + } + + const searchPageUpdate = (slug, page, cb) => { + // to update we have to remove the page first, and then readd it + let pageText + try { + pageText = page.story.reduce(extractPageText, '') + } catch (err) { + console.log(`SITE INDEX *** ${wikiName} reduce to extract the text on ${slug} failed`, err.message) + pageText = '' + } + if (siteIndex.has(slug)) { + siteIndex.replace({ + id: slug, + title: page.title, + content: pageText, + }) + } else { + siteIndex.add({ + id: slug, + title: page.title, + content: pageText, + }) + } + cb() + } + + const searchPageRemove = (slug, cb) => { + // remove page from index + try { + siteIndex.discard(slug) + } catch (err) { + // swallow error, if the page was not in index + if (!err.message.includes('not in the index')) { + console.log(`removing ${slug} from index ${wikiName} failed`, err) + } + } + cb() + } + + const searchSave = (siteIndex, cb) => { + // save index to file + fs.access(argv.status, fs.constants.F_OK, err => { + if (!err) { + writeFileAtomic(siteIndexLoc, JSON.stringify(siteIndex), e => { + if (e) return cb(e) + touch(indexUpdateFlag, () => { + cb() + }) + }) + } else { + fs.mkdir(argv.status, { recursive: true }, () => { + writeFileAtomic(siteIndexLoc, JSON.stringify(siteIndex), e => { + if (e) return cb(e) + touch(indexUpdateFlag, () => { + cb() + }) + }) + }) + } + }) + } + + const searchRestore = cb => { + // restore index, or create if it doesn't already exist + fs.access(siteIndexLoc, fs.constants.F_OK, err => { + if (!err) { + fs.readFile(siteIndexLoc, (err, data) => { + if (err) return cb(err) + try { + siteIndex = miniSearch.loadJSON(data, { + fields: ['title', 'content'], + }) + } catch (e) { + return cb(e) + } + process.nextTick(() => { + serial(queue.shift()) + }) + }) + } + }) + } + + const serial = item => { + if (item) { + switch (item.action) { + case 'update': + itself.start() + searchPageUpdate(item.slug, item.page, () => { + process.nextTick(() => { + serial(queue.shift()) + }) + }) + break + case 'remove': + itself.start() + searchPageRemove(item.slug, () => { + process.nextTick(() => { + serial(queue.shift()) + }) + }) + break + default: + console.log(`SITE INDEX *** unexpected action ${item.action} for ${item.page}`) + process.nextTick(() => { + serial(queue.shift) + }) + } + } else { + searchSave(siteIndex, e => { + if (e) console.log('SITE INDEX *** save failed: ' + e) + itself.stop() + }) + } + } + + const extractItemText = text => { + return text + .replace(/\[([^\]]*?)\][[(].*?[\])]/g, ' $1 ') + .replace(/\[{2}|\[(?:[\S]+)|\]{1,2}/g, ' ') + .replace(/\n/g, ' ') + .replace(//g, ' ') + .replace(/<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g, ' ') + .replace(/<(?:[^>])+>/g, ' ') + .replace(/(https?:.*?)(?=\p{White_Space}|\p{Quotation_Mark}|$)/gu, match => { + try { + const myUrl = new URL(match) + return myUrl.hostname + } catch { + return ' ' + } + }) + .replace(/[\p{P}\p{Emoji}\p{Symbol}}]+/gu, ' ') + .replace(/[\p{White_Space}\n\t]+/gu, ' ') + } + + const extractPageText = (pageText, currentItem, currentIndex, array) => { + // console.log('extractPageText', pageText, currentItem, currentIndex, array) + try { + if (currentItem.text) { + switch (currentItem.type) { + case 'paragraph': + case 'markdown': + case 'html': + case 'reference': + case 'image': + case 'pagefold': + case 'math': + case 'mathjax': + case 'code': + pageText += ' ' + extractItemText(currentItem.text) + break + case 'audio': + case 'video': + case 'frame': + pageText += + ' ' + + extractItemText( + currentItem.text + .split(/\r\n?|\n/) + .map(line => { + const firstWord = line.split(/\p{White_Space}/u)[0] + if ( + firstWord.startsWith('http') || + firstWord.toUpperCase() === firstWord || + firstWord.startsWith('//') + ) { + // line is markup + return '' + } else { + return line + } + }) + .join(' '), + ) + } + } + } catch (err) { + throw new Error(`Error extracting text from ${currentIndex}, ${JSON.stringify(currentItem)} ${err}, ${err.stack}`) + } + return pageText + } + + // #### Public stuff #### + + var itself = new events.EventEmitter() + itself.start = () => { + clearTimeout(searchTimeoutHandler) + working = true + return itself.emit('indexing') + } + itself.stop = () => { + const clearsearch = () => { + console.log(`SITE INDEX ${wikiName} : removed from memory`) + siteIndex = [] + clearTimeout(searchTimeoutHandler) + } + searchTimeoutHandler = setTimeout(clearsearch, searchTimeoutMs) + working = false + return itself.emit('indexed') + } + itself.isWorking = () => { + return working + } + itself.createIndex = pagehandler => { + itself.start() + + // we save the pagehandler, so we can recreate the site index if it is removed + searchPageHandler = searchPageHandler ?? pagehandler + + //timeLabel = `SITE INDEX ${wikiName} : Created` + //console.time timeLabel + + pagehandler.slugs((e, slugs) => { + if (e) { + console.log(`SITE INDEX *** createIndex ${wikiName} error:`, e) + itself.stop() + return e + } + siteIndex = new miniSearch({ + fields: ['title', 'content'], + }) + + const indexPromises = slugs.map(slug => { + return new Promise(resolve => { + pagehandler.get(slug, (err, page) => { + if (err) { + console.log(`SITE INDEX *** ${wikiName}: error reading page`, slug) + return + } + // page + let pageText + try { + pageText = page.story.reduce(extractPageText, '') + } catch (err) { + console.log(`SITE INDEX *** ${wikiName} reduce to extract text on ${slug} failed`, err.message) + // console.log "page", page + pageText = '' + } + siteIndex.add({ + id: slug, + title: page.title, + content: pageText, + }) + resolve() + }) + }) + }) + Promise.all(indexPromises).then(() => { + // console.timeEnd timeLabel + process.nextTick(() => { + serial(queue.shift()) + }) + }) + }) + } + + itself.removePage = slug => { + const action = 'remove' + queue.push({ action, slug }) + if (Array.isArray(siteIndex) && !working) { + itself.start() + searchRestore(e => { + if (e) console.log(`SITE INDEX *** Problems restoring search index ${wikiName}:` + e) + itself.createIndex(searchPageHandler) + }) + } else { + if (!working) serial(queue.shift()) + } + } + + itself.update = (slug, page) => { + const action = 'update' + queue.push({ action, slug, page }) + if (Array.isArray(siteIndex) && !working) { + itself.start() + searchRestore(e => { + if (e) console.log(`SITE INDEX *** Problems restoring search index ${wikiName}:` + e) + itself.createIndex(searchPageHandler) + }) + } else { + if (!working) serial(queue.shift()) + } + } + itself.startUp = pagehandler => { + // called on server startup, here we check if wiki already is index + // we only create an index if there is either no index or there have been updates since last startup + console.log(`SITE INDEX ${wikiName} : StartUp`) + fs.stat(siteIndexLoc, (err, stats) => { + if (err === null) { + // site index exists, but has it been updated? + fs.stat(indexUpdateFlag, (err, stats) => { + if (!err) { + // index has been updated, so recreate it. + itself.createIndex(pagehandler) + // remove the update flag once the index has been created + itself.once('indexed', () => { + fs.unlink(indexUpdateFlag, err => { + if (err) console.log(`+++ SITE INDEX ${wikiName} : unable to delete update flag`) + }) + }) + } else { + // not been updated, but is it the correct version? + fs.readFile(siteIndexLoc, (err, data) => { + if (!err) { + let testIndex + try { + testIndex = JSON.parse(data) + } catch (err) { + testIndex = {} + } + if (testIndex.serializationVersion != 2) + console.log(`+++ SITE INDEX ${wikiName} : updating to latest version.`) + itself.createIndex(pagehandler) + // remove the update flag once the index has been created + itself.once('indexed', () => { + fs.unlink(indexUpdateFlag, err => { + if (err) console.log(`+++ SITE INDEX ${wikiName} : unable to delete update flag`) + }) + }) + } else { + console.log(`+++ SITE INDEX ${wikiName} : error reading index - attempting creating`) + itself.createIndex(pagehandler) + // remove the update flag once the index has been created + itself.once('indexed', () => { + fs.unlink(indexUpdateFlag, err => { + if (err) console.log(`+++ SITE INDEX ${wikiName} : unable to delete update flag`) + }) + }) + } + }) + } + }) + } else { + // index does not exist, so create it + itself.createIndex(pagehandler) + // remove the update flag once the index has been created + itself.once('indexed', () => { + fs.unlink(indexUpdateFlag, err => { + if (err) console.log(`+++ SITE INDEX ${wikiName} : unable to delete update flag`) + }) + }) + } + }) + } + + return itself +} diff --git a/lib/security.coffee b/lib/security.coffee deleted file mode 100644 index 6b7fc8e3..00000000 --- a/lib/security.coffee +++ /dev/null @@ -1,86 +0,0 @@ -### - * Federated Wiki : Node Server - * - * Copyright Ward Cunningham and other contributors - * Licensed under the MIT license. - * https://github.com/fedwiki/wiki-node-server/blob/master/LICENSE.txt -### -# **security.coffee** -# Module for default site security. -# -# This module is not intented for use, but is here to catch a problem with -# configuration of security. It does not provide any authentication, but will -# allow the server to run read-only. - -#### Requires #### -fs = require 'fs' - - -# Export a function that generates security handler -# when called with options object. -module.exports = exports = (log, loga, argv) -> - security={} - - #### Private utility methods. #### - - user = '' - - owner = '' - - admin = argv.admin - - # save the location of the identity file - idFile = argv.id - - #### Public stuff #### - - security.authenticate_session = -> - (req, res, next) -> - # not possible to login, so always false - req.isAuthenticated = -> - return false - next() - - # Retrieve owner infomation from identity file in status directory - security.retrieveOwner = (cb) -> - fs.exists idFile, (exists) -> - if exists - fs.readFile(idFile, (err, data) -> - if err then return cb err - owner += data - cb()) - else - owner = '' - cb() - - # Return the owners name - security.getOwner = -> - if !owner.name? - ownerName = '' - else - ownerName = owner.name - ownerName - - security.getUser = (req) -> - return '' - - security.isAuthorized = (req) -> - # nobody is authorized - everything is read-only - # unless legacy support, when unclaimed sites can be editted. - if owner == '' - if argv.security_legacy - return true - else - return false - else - return false - - # Wiki server admin - security.isAdmin = -> - return false - - security.defineRoutes = (app, cors, updateOwner) -> - # default security does not have any routes - - - security diff --git a/lib/security.js b/lib/security.js new file mode 100644 index 00000000..e79228aa --- /dev/null +++ b/lib/security.js @@ -0,0 +1,94 @@ +/* + * Federated Wiki : Node Server + * + * Copyright Ward Cunningham and other contributors + * Licensed under the MIT license. + * https://github.com/fedwiki/wiki-node-server/blob/master/LICENSE.txt + */ +// **security.js** +// Module for default site security. +// +// This module is not intented for use, but is here to catch a problem with +// configuration of security. It does not provide any authentication, but will +// allow the server to run read-only. + +// #### Requires #### +const fs = require('node:fs') + +// Export a function that generates security handler +// when called with options object. +module.exports = exports = (log, loga, argv) => { + const security = {} + + // #### Private utility methods. #### + + const user = '' + + let owner = '' + + // save the admin user, and location of the identity file + const { admin, id: idFile } = argv + + // #### Public stuff #### + + security.authenticate_session = () => { + ;(req, res, next) => { + // not possible to login, so always false + req.isAuthenticated = () => false + return next() + } + } + + // Retrieve owner infomation from identity file in status directory + security.retrieveOwner = cb => { + fs.access(idFile, fs.constants.F_OK, err => { + if (!err) { + fs.readFile(idFile, (err, data) => { + if (err) return cb(err) + owner += data + cb() + }) + } else { + owner = '' + cb() + } + }) + } + + // Return the owners name + security.getOwner = () => { + let ownerName + if (!owner.name) { + ownerName = '' + } else { + ownerName = owner.name + } + return ownerName + } + security.getUser = req => { + return '' + } + + security.isAuthorized = req => { + // nobody is authorized - everything is read-only + // unless legacy support, when unclaimed sites can be editted. + if (owner == '') { + if (argv.security_legacy) { + return true + } else { + return false + } + } else { + return false + } + } + // Wiki server admin + security.isAdmin = () => { + return false + } + security.defineRoutes = (app, cors, updateOwner) => { + // default security does not have any routes + } + + return security +} diff --git a/lib/server.coffee b/lib/server.coffee deleted file mode 100644 index 4e82cd3a..00000000 --- a/lib/server.coffee +++ /dev/null @@ -1,782 +0,0 @@ -### - * Federated Wiki : Node Server - * - * Copyright Ward Cunningham and other contributors - * Licensed under the MIT license. - * https://github.com/fedwiki/wiki-server/blob/master/LICENSE.txt -### - -# **server.coffee** is the main guts of the express version -# of (Smallest Federated Wiki)[https://github.com/WardCunningham/Smallest-Federated-Wiki]. -# The CLI and Farm are just front ends -# for setting arguments, and spawning servers. In a complex system -# you would probably want to replace the CLI/Farm with your own code, -# and use server.coffee directly. -# -#### Dependencies #### -# anything not in the standard library is included in the repo, or -# can be installed with an: -# npm install - -# Standard lib -fs = require 'fs' -path = require 'path' -http = require 'http' -url = require 'url' -{ pipeline } = require 'node:stream/promises' - -# From npm -express = require 'express' -hbs = require 'express-hbs' -glob = require 'glob' -async = require 'async' -f = require('flates') - -createDOMPurify = require('dompurify') -{ JSDOM } = require('jsdom') - -window = new JSDOM('').window -DOMPurify = createDOMPurify(window) - -# node-fetch is now ESM only -fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args)); - -# Express 4 middleware -logger = require 'morgan' -cookieParser = require 'cookie-parser' -methodOverride = require 'method-override' -## session = require 'express-session' -sessions = require 'client-sessions' -bodyParser = require 'body-parser' -errorHandler = require 'errorhandler' - - - -# Local files -random = require './random_id' -defargs = require './defaultargs' -resolveClient = require 'wiki-client/lib/resolve' -pluginsFactory = require './plugins' -sitemapFactory = require './sitemap' -searchFactory = require './search' - -render = (page) -> - return f.div({class: "twins"}, f.p('')) + '\n' + - f.div({class: "header"}, f.h1( - f.a({href: '/', style: 'text-decoration: none'}, - f.img({height: '32px', src: '/favicon.png'})) + - ' ' + (page.title))) + '\n' + - f.div {class: "story"}, - page.story.map((story) -> - return '' unless story - if story.type is 'paragraph' - f.div {class: "item paragraph"}, f.p(resolveClient.resolveLinks(story.text)) - else if story.type is 'image' - f.div {class: "item image"}, - f.img({class: "thumbnail", src: story.url}), - f.p(resolveClient.resolveLinks(story.text or story.caption or 'uploaded image')) - else if story.type is 'html' - f.div {class: "item html"}, - f.p(resolveClient.resolveLinks(story.text or '', DOMPurify.sanitize)) - else f.div {class: "item"}, f.p(resolveClient.resolveLinks(story.text or '')) - ).join('\n') - -# Set export objects for node and coffee to a function that generates a sfw server. -module.exports = exports = (argv) -> - # Create the main application object, app. - app = express() - - # remove x-powered-by header - app.disable('x-powered-by') - - # defaultargs.coffee exports a function that takes the argv object - # that is passed in and then does its - # best to supply sane defaults for any arguments that are missing. - argv = defargs(argv) - - app.startOpts = argv - - log = (stuff...) -> - console.log stuff if argv.debug - - loga = (stuff...) -> - console.log stuff - - - ourErrorHandler = (req, res, next) -> - fired = false - res.e = (error, status) -> - if !fired - fired = true - res.statusCode = status or 500 - res.end 'Server ' + error - log "Res sent:", res.statusCode, error - else - log "Already fired", error - next() - - # Require the database adapter and initialize it with options. - app.pagehandler = pagehandler = require(argv.database.type)(argv) - - # Require the sitemap adapter and initialize it with options. - app.sitemaphandler = sitemaphandler = sitemapFactory(argv) - - # Require the site indexer and initialize it with options - app.searchhandler = searchhandler = searchFactory(argv) - - # Require the security adapter and initialize it with options. - app.securityhandler = securityhandler = require(argv.security_type)(log, loga, argv) - - # If the site is owned, owner will contain the name of the owner - owner = '' - - # If the user is logged in, user will contain their identity - user = '' - - # Called from authentication when the site is claimed, - # to update the name of the owner held here. - updateOwner = (id) -> - owner = id - - - #### Middleware #### - # - # Allow json to be got cross origin. - cors = (req, res, next) -> - res.header 'Access-Control-Allow-Origin', req.get('origin')||'*' - next() - - - remoteGet = (remote, slug, cb) -> - # assume http, as we know no better at this point and we need to specify a protocol. - remoteURL = new URL("http://#{remote}/#{slug}.json").toString() - # set a two second timeout - fetch(remoteURL, {signal: AbortSignal.timeout(2000)}) - .then (res) -> - if res.ok - return res - throw new Error(res.statusText) - .then (res) -> - return res.json() - .then (json) -> - cb(null, json, 200) - .catch (err) -> - console.error('Unable to fetch remote resource', remote, slug, err) - cb(err, 'Page not found', 404) - - - - #### Express configuration #### - # Set up all the standard express server options, - # including hbs to use handlebars/mustache templates - # saved with a .html extension, and no layout. - - # - staticPathOptions = { - dotfiles: 'ignore' - etag: true - immutable: false - lastModified: false - maxAge: '1h' - } - - app.set('views', - path.join(require.resolve('wiki-client/package.json'), '..', 'views')) - app.set('view engine', 'html') - app.engine('html', hbs.express4()) - app.set('view options', layout: false) - - # return deterministically colored strings - colorString = (str) -> - colorReset = '\x1b[0m' - hash = 0; - str.split('').forEach (char) -> - hash = char.charCodeAt(0) + ((hash << 5) - hash) - color = '\x1b[38;2' - for i in [0..2] - do (i) -> - value = (hash >> (i * 8)) & 0xff - color += ';' + value.toString() - color += 'm' - return color + str + colorReset - - # use logger, at least in development, probably needs a param to configure (or turn off). - # use stream to direct to somewhere other than stdout. - logger.token('vhost', (req, res) -> - return colorString(req.hostname)) - app.use(logger(':vhost :method :url :status :res[content-length] - :response-time ms')) - app.use(cookieParser()) - app.use(bodyParser.json({ limit: argv.uploadLimit})) - app.use(bodyParser.urlencoded({ extended: true, limit: argv.uploadLimit})) - app.use(methodOverride()) - cookieValue = { - httpOnly: true - sameSite: 'lax' - } - if argv.wiki_domain - if !argv.wiki_domain.endsWith('localhost') - cookieValue['domain'] = argv.wiki_domain - # use secureProxy as TLS is terminated in outside the node process - if argv.secure_cookie - cookieName = 'wikiTlsSession' - cookieValue['secureProxy'] = true - else - cookieName = "wikiSession" - app.use(sessions({ - cookieName: cookieName, - requestKey: 'session', - secret: argv.cookieSecret, - # make the session session_duration days long - duration: argv.session_duration * 24 * 60 * 60 * 1000, - # add 12 hours to session if less than 12 hours to expiry - activeDuration: 24 * 60 * 60 * 1000, - cookie: cookieValue - })) - - app.use(ourErrorHandler) - - # Add static route to the client - app.use(express.static(argv.client, staticPathOptions)) - - ##### Define security routes ##### - securityhandler.defineRoutes app, cors, updateOwner - - # Add static route to assets - app.use('/assets', cors, express.static(argv.assets)) - - # Add static routes to the plugins client. - glob "wiki-plugin-*/client", {cwd: argv.packageDir}, (e, plugins) -> - plugins.map (plugin) -> - pluginName = plugin.slice(12, -7) - pluginPath = '/plugins/' + pluginName - app.use(pluginPath, cors, express.static(path.join(argv.packageDir, plugin), staticPathOptions)) - - # Add static routes to the security client. - if argv.security != './security' - app.use('/security', express.static(path.join(argv.packageDir, argv.security_type, 'client'), staticPathOptions)) - - - ##### Set up standard environments. ##### - # In dev mode turn on console.log debugging as well as showing the stack on err. - if 'development' == app.get('env') - app.use(errorHandler()) - argv.debug = console? and true - - # Show all of the options a server is using. - log argv - - #### Routes #### - # Routes currently make up the bulk of the Express port of - # Smallest Federated Wiki. Most routes use literal names, - # or regexes to match, and then access req.params directly. - - ##### Redirects ##### - # Common redirects that may get used throughout the routes. - index = argv.home + '.html' - - oops = '/oops' - - ##### Get routes ##### - # Routes have mostly been kept together by http verb, with the exception - # of the openID related routes which are at the end together. - - # Main route for initial contact. Allows us to - # link into a specific set of pages, local and remote. - # Can also be handled by the client, but it also sets up - # the login status, and related footer html, which the client - # relies on to know if it is logged in or not. - app.get ///^((/[a-zA-Z0-9:.-]+/[a-z0-9-]+(_rev\d+)?)+)/?$///, cors, (req, res, next) -> - urlPages = (i for i in req.params[0].split('/') by 2)[1..] - urlLocs = (j for j in req.params[0].split('/')[1..] by 2) - if ['plugin', 'auth'].indexOf(urlLocs[0]) > -1 - return next() - title = urlPages[..].pop().replace(/-+/g,' ') - user = securityhandler.getUser(req) - info = { - title - pages: [] - authenticated: if user - true - else - false - user: user - seedNeighbors: argv.neighbors - owned: if owner - true - else - false - isOwner: if securityhandler.isAuthorized(req) - true - else - false - ownedBy: if owner - owner - else - '' - } - for page, idx in urlPages - if urlLocs[idx] is 'view' - pageDiv = {page} - else - pageDiv = {page, origin: """data-site=#{urlLocs[idx]}"""} - info.pages.push(pageDiv) - res.render('static.html', info) - - app.get ///^\/([a-z0-9-]+)\.html$///, cors, (req, res, next) -> - slug = req.params[0] - log(slug) - if slug is 'runtests' - return next() - pagehandler.get slug, (e, page, status) -> - if e then return res.e e - if status is 404 - return res.status(status).send(page) - page.title ||= slug.replace(/-+/g,' ') - page.story ||= [] - user = securityhandler.getUser(req) - - info = { - title: page.title - pages: [ - page: slug - generated: """data-server-generated=true""" - story: render(page) - ] - authenticated: if user - true - else - false - user: user - seedNeighbors: argv.neighbors - owned: if owner - true - else - false - isOwner: if securityhandler.isAuthorized(req) - true - else - false - ownedBy: if owner - owner - else - '' - } - res.render('static.html', info) - - app.get ///system/factories.json///, (req, res) -> - res.status(200) - res.header('Content-Type', 'application/json') -# Plugins are located in packages in argv.packageDir, with package names of the form wiki-plugin-* - glob path.join(argv.packageDir, 'wiki-plugin-*', 'factory.json'), (e, files) -> - if e then return res.e(e) - - doFactories = (file, cb) -> - fs.readFile file, (err, data) -> - return cb() if err - try - factory = JSON.parse data - cb null, factory - catch err - return cb() - - async.map files, doFactories, (e, factories) -> - res.e(e) if e - res.end(JSON.stringify factories) - - - ###### Json Routes ###### - # Handle fetching local and remote json pages. - # Local pages are handled by the pagehandler module. - app.get ///^/([a-z0-9-]+)\.json$///, cors, (req, res) -> - file = req.params[0] - pagehandler.get file, (e, page, status) -> - if e then return res.e e - res.status(status or 200).send(page) - - # Remote pages use the http client to retrieve the page - # and sends it to the client. TODO: consider caching remote pages locally. - app.get ///^/remote/([a-zA-Z0-9:\.-]+)/([a-z0-9-]+)\.json$///, (req, res) -> - remoteGet req.params[0], req.params[1], (e, page, status) -> - if e - log "remoteGet error:", e - return res.e e - res.status(status or 200).send(page) - - - ###### Theme Routes ###### - # If themes doesn't exist send 404 and let the client - # deal with it. - app.get /^\/theme\/(\w+\.\w+)$/, cors, (req,res) -> - res.sendFile(path.join(argv.status, 'theme', req.params[0]), (e) -> - if (e) - # swallow the error if the theme does not exist... - if req.path is '/theme/style.css' - res.set('Content-Type', 'text/css') - res.send('') - else - res.sendStatus(404) - ) - - ###### Favicon Routes ###### - # If favLoc doesn't exist send the default favicon. - favLoc = path.join(argv.status, 'favicon.png') - defaultFavLoc = path.join(argv.root, 'default-data', 'status', 'favicon.png') - app.get '/favicon.png', cors, (req,res) -> - fs.exists favLoc, (exists) -> - if exists - res.sendFile(favLoc) - else - res.sendFile(defaultFavLoc) - - authorized = (req, res, next) -> - if securityhandler.isAuthorized(req) - next() - else - console.log 'rejecting', req.path - res.sendStatus(403) - - # Accept favicon image posted to the server, and if it does not already exist - # save it. - app.post '/favicon.png', authorized, (req, res) -> - favicon = req.body.image.replace(///^data:image/png;base64,///, "") - buf = new Buffer(favicon, 'base64') - fs.exists argv.status, (exists) -> - if exists - fs.writeFile favLoc, buf, (e) -> - if e then return res.e e - res.send('Favicon Saved') - - else - fs.mkdir argv.status, { recursive: true }, -> - fs.writeFile favLoc, buf, (e) -> - if e then return res.e e - res.send('Favicon Saved') - - # Redirect remote favicons to the server they are needed from. - app.get ///^/remote/([a-zA-Z0-9:\.-]+/favicon.png)$///, (req, res) -> - remotefav = "http://#{req.params[0]}" - res.redirect(remotefav) - - ###### Recycler Routes ###### - # These routes are only available to the site's owner - - # Give the recycler a standard flag - use the Taiwan symbol as the use of - # negative space outward pointing arrows nicely indicates that items can be removed - recyclerFavLoc = path.join(argv.root, 'default-data', 'status', 'recycler.png') - app.get '/recycler/favicon.png', authorized, (req, res) -> - res.sendFile(recyclerFavLoc) - - # Send an array of pages currently in the recycler via json - app.get '/recycler/system/slugs.json', authorized, (req, res) -> - fs.readdir argv.recycler, (e, files) -> - - doRecyclermap = (file, cb) -> - recycleFile = 'recycler/' + file - pagehandler.get recycleFile, (e, page, status) -> - if e or status is 404 - console.log 'Problem building recycler map:', file, 'e: ',e - # this will leave an undefined/empty item in the array, which we will filter out later - return cb() - cb null, { - slug: file - title: page.title - } - - if e then return res.e e - async.map files, doRecyclermap, (e, recyclermap) -> - return cb(e) if e - # remove any empty items - recyclermap = recyclermap.filter( (el) -> return !!el ) - res.send(recyclermap) - - # Fetching page from the recycler - #///^/([a-z0-9-]+)\.json$/// - app.get ///^/recycler/([a-z0-9-]+)\.json$///, authorized, (req, res) -> - file = 'recycler/' + req.params[0] - pagehandler.get file, (e, page, status) -> - if e then return res.e e - res.status(status or 200).send(page) - - # Delete page from the recycler - app.delete ///^/recycler/([a-z0-9-]+)\.json$///, authorized, (req, res) -> - file = 'recycler/' + req.params[0] - pagehandler.delete file, (err) -> - if err then res.status(500).send(err) - res.status(200).send('') - - - ###### Meta Routes ###### - # Send an array of pages in the database via json - app.get '/system/slugs.json', cors, (req, res) -> - pagehandler.slugs (err, files) -> - if err then res.status(500).send(err) - res.send(files) - -# Returns a list of installed plugins. (does this get called anymore!) - app.get '/system/plugins.json', cors, (req, res) -> - glob "wiki-plugin-*", {cwd: argv.packageDir}, (e, files) -> - if e then return res.e e - # extract the plugin name from the name of the directory it's installed in - files = files.map (file) -> file.slice(12) - res.send(files) - -# - sitemapLoc = path.join(argv.status, 'sitemap.json') - app.get '/system/sitemap.json', cors, (req, res) -> - fs.exists sitemapLoc, (exists) -> - if exists - res.sendFile(sitemapLoc) - else - # only createSitemap if we are not already creating one - sitemaphandler.createSitemap (pagehandler) if !sitemaphandler.isWorking() - # wait for the sitemap file to be written, before sending - sitemaphandler.once 'finished', -> - res.sendFile(sitemapLoc) - - xmlSitemapLoc = path.join(argv.status, 'sitemap.xml') - app.get '/sitemap.xml', cors, (req, res) -> - fs.exists sitemapLoc, (exists) -> - if exists - res.sendFile(xmlSitemapLoc) - else - sitemaphandler.createSitemap (pagehandler) if !sitemaphandler.isWorking() - sitemaphandler.once 'finished', -> - res.sendFile(xmlSitemapLoc) - - searchIndexLoc = path.join(argv.status, 'site-index.json') - app.get '/system/site-index.json', cors, (req, res) -> - fs.exists searchIndexLoc, (exists) -> - if exists - res.sendFile(searchIndexLoc) - else - # only create index if we are not already creating one - searchhandler.createIndex(pagehandler) if !searchhandler.isWorking() - searchhandler.once 'indexed', -> - res.sendFile(searchIndexLoc) - - app.get '/system/export.json', cors, (req, res) -> - pagehandler.pages (e, sitemap) -> - return res.e(e) if e - async.map( - sitemap, - (stub, done) -> - pagehandler.get(stub.slug, (error, page) -> - return done(e) if e - done(null, {slug: stub.slug, page}) - ) - , - (e, pages) -> - return res.e(e) if e - res.json(pages.reduce( (dict, combined) -> - dict[combined.slug] = combined.page - dict - , {})) - ) - - admin = (req, res, next) -> - if securityhandler.isAdmin(req) - next() - else - console.log 'rejecting', req.path - res.sendStatus(403) - - app.get '/system/version.json', admin, (req, res) -> - versions = {} - wikiModule = module.parent.parent.parent - versions[wikiModule.require('./package').name] = wikiModule.require('./package').version - versions[wikiModule.require('wiki-server/package').name] = wikiModule.require('wiki-server/package').version - versions[wikiModule.require('wiki-client/package').name] = wikiModule.require('wiki-client/package').version - versions['security'] = {} - versions['plugins'] = {} - - glob '+(wiki-security-*|wiki-plugin-*)', {cwd: argv.packageDir}, (e, plugins) -> - plugins.map (plugin) -> - if plugin.includes 'wiki-security' - versions.security[wikiModule.require(plugin + "/package").name] = wikiModule.require(plugin + "/package").version - else - versions.plugins[wikiModule.require(plugin + "/package").name] = wikiModule.require(plugin + "/package").version - res.json(versions) - - ##### Proxy routes ##### - - app.get '/proxy/*', authorized, (req, res) -> - pathParts = req.originalUrl.split('/') - remoteHost = pathParts[2] - pathParts.splice(0,3) - remoteResource = pathParts.join('/') - requestURL = 'http://' + remoteHost + '/' + remoteResource - console.log("PROXY Request: ", requestURL) - if requestURL.endsWith('.json') or requestURL.endsWith('.png') or requestURL.endsWith('.jpg') or pathParts[0] is "plugin" - fetch(requestURL, {timeout: 2000}) - .then (fetchRes) -> - if fetchRes.ok - res.set('content-type', fetchRes.headers.get('content-type')) - res.set('last-modified', fetchRes.headers.get('last-modified')) - await pipeline(fetchRes.body, res) - else - res.status(fetchRes.status).end() - .catch (err) -> - console.log("ERROR: Proxy Request ", requestURL, err) - res.status(500).end() - else - res.status(400).end() - - - ##### Put routes ##### - - app.put /^\/page\/([a-z0-9-]+)\/action$/i, authorized, (req, res) -> - action = JSON.parse(req.body.action) - # Handle all of the possible actions to be taken on a page, - actionCB = (e, page, status) -> - #if e then return res.e e - if status is 404 - # res.status(status).send(page) - return res.e page,status - # Using Coffee-Scripts implicit returns we assign page.story to the - # result of a list comprehension by way of a switch expression. - try - page.story = switch action.type - when 'move' - action.order.map (id) -> - page.story.filter((para) -> - id == para.id - )[0] or throw('Ignoring move. Try reload.') - - when 'add' - idx = page.story.map((para) -> para.id).indexOf(action.after) + 1 - page.story.splice(idx, 0, action.item) - page.story - - when 'remove' - page.story.filter (para) -> - para?.id != action.id - - when 'edit' - page.story.map (para) -> - if para.id is action.id - action.item - else - para - - - when 'create', 'fork' - page.story or [] - - else - log "Unfamiliar action:", action - #page.story - throw('Unfamiliar action ignored') - catch e - return res.e e - - # Add a blank journal if it does not exist. - # And add what happened to the journal. - if not page.journal - page.journal = [] - if action.fork - page.journal.push({type: "fork", site: action.fork, date: action.date - 1}) - delete action.fork - page.journal.push(action) - pagehandler.put req.params[0], page, (e) -> - if e then return res.e e - res.send('ok') - # log 'saved' - - # update sitemap - sitemaphandler.update(req.params[0], page) - - # update site index - searchhandler.update(req.params[0], page) - - # log action - - # If the action is a fork, get the page from the remote server, - # otherwise ask pagehandler for it. - if action.fork - pagehandler.saveToRecycler req.params[0], (err) -> - if err and err isnt 'page does not exist' - console.log "Error saving #{req.params[0]} before fork: #{err}" - if action.forkPage - forkPageCopy = JSON.parse(JSON.stringify(action.forkPage)) - delete action.forkPage - actionCB(null, forkPageCopy) - else - # Legacy path, new clients will provide forkPage on implicit forks. - remoteGet(action.fork, req.params[0], actionCB) - else if action.type is 'create' - # Prevent attempt to write circular structure - itemCopy = JSON.parse(JSON.stringify(action.item)) - pagehandler.get req.params[0], (e, page, status) -> - if e then return actionCB(e) - unless status is 404 - res.status(409).send('Page already exists.') - else - actionCB(null, itemCopy) - - else if action.type == 'fork' - pagehandler.saveToRecycler req.params[0], (err) -> - if err then console.log "Error saving #{req.params[0]} before fork: #{err}" - if action.forkPage # push - forkPageCopy = JSON.parse(JSON.stringify(action.forkPage)) - delete action.forkPage - actionCB(null, forkPageCopy) - else # pull - remoteGet(action.site, req.params[0], actionCB) - else - pagehandler.get(req.params[0], actionCB) - - # Return the oops page when login fails. - app.get '/oops', (req, res) -> - res.statusCode = 403 - res.render('oops.html', {msg:'This is not your wiki!'}) - - # Traditional request to / redirects to index :) - app.get '/', cors, (req, res) -> - home = path.join argv.assets, 'home', 'index.html' - fs.stat home, (err, stats) -> - if err || !stats.isFile() - res.redirect(index) - else - res.redirect("/assets/home/index.html") - - ##### Delete Routes ##### - - app.delete ///^/([a-z0-9-]+)\.json$///, authorized, (req, res) -> - pageFile = req.params[0] - # we need the original page text to remove it from the index, so get the original text before deleting it - pagehandler.get pageFile, (e, page, status) -> - title = page.title - pagehandler.delete pageFile, (err) -> - if err - res.status(500).send(err) - else - sitemaphandler.removePage pageFile - res.status(200).send('') - # update site index - searchhandler.removePage(req.params[0]) - - - - #### Start the server #### - # Wait to make sure owner is known before listening. - securityhandler.retrieveOwner (e) -> - # Throw if you can't find the initial owner - if e then throw e - owner = securityhandler.getOwner() - console.log "owner: " + owner - app.emit 'owner-set' - - app.on 'running-serv', (server) -> - ### Plugins ### - # Should replace most WebSocketServers below. - plugins = pluginsFactory(argv) - plugins.startServers({argv, app}) - ### Sitemap ### - # create sitemap at start-up - sitemaphandler.createSitemap(pagehandler) - # create site index at start-up - searchhandler.startUp(pagehandler) - - - # Return app when called, so that it can be watched for events and shutdown with .close() externally. - app diff --git a/lib/server.js b/lib/server.js new file mode 100644 index 00000000..3ea16e98 --- /dev/null +++ b/lib/server.js @@ -0,0 +1,929 @@ +/* + * Federated Wiki : Node Server + * + * Copyright Ward Cunningham and other contributors + * Licensed under the MIT license. + * https://github.com/fedwiki/wiki-server/blob/master/LICENSE.txt + */ + +// **server.coffee** is the main guts of the express version +// of (Smallest Federated Wiki)[https://github.com/WardCunningham/Smallest-Federated-Wiki]. +// The CLI and Farm are just front ends +// for setting arguments, and spawning servers. In a complex system +// you would probably want to replace the CLI/Farm with your own code, +// and use server.coffee directly. +// +// #### Dependencies #### +// anything not in the standard library is included in the repo, or +// can be installed with an: +// npm install + +// Standard lib +const fs = require('fs') +const path = require('path') +const http = require('http') +const url = require('url') +const { pipeline } = require('node:stream/promises') + +// From npm +const express = require('express') +const hbs = require('express-hbs') +const f = require('flates') + +const createDOMPurify = require('dompurify') +const { JSDOM } = require('jsdom') + +const window = new JSDOM('').window +const DOMPurify = createDOMPurify(window) + +// Using native fetch API (available in Node.js 18+) + +// Express 4 middleware +const logger = require('morgan') +const cookieParser = require('cookie-parser') +const methodOverride = require('method-override') +// session = require 'express-session' +const sessions = require('client-sessions') +const bodyParser = require('body-parser') +const errorHandler = require('errorhandler') + +// Local files +const random = require('./random_id') +const defargs = require('./defaultargs') +const resolveClient = require('wiki-client/lib/resolve') +const pluginsFactory = require('./plugins') +const sitemapFactory = require('./sitemap') +const searchFactory = require('./search') + +const render = page => { + return ( + f.div({ class: 'twins' }, f.p('')) + + '\n' + + f.div( + { class: 'header' }, + f.h1( + f.a({ href: '/', style: 'text-decoration: none' }, f.img({ height: '32px', src: '/favicon.png' })) + + ' ' + + page.title, + ), + ) + + '\n' + + f.div( + { class: 'story' }, + page.story + .map(story => { + if (!story) return '' + if (story.type === 'paragraph') { + f.div({ class: 'item paragraph' }, f.p(resolveClient.resolveLinks(story.text))) + } else if (story.type === 'image') { + f.div( + { class: 'item image' }, + f.img({ class: 'thumbnail', src: story.url }), + f.p(resolveClient.resolveLinks(story.text || story.caption || 'uploaded image')), + ) + } else if (story.type === 'html') { + f.div({ class: 'item html' }, f.p(resolveClient.resolveLinks(story.text || '', DOMPurify.sanitize))) + } else f.div({ class: 'item' }, f.p(resolveClient.resolveLinks(story.text || ''))) + }) + .join('\n'), + ) + ) +} +// Set export objects for node and coffee to a function that generates a sfw server. +module.exports = exports = argv => { + // Create the main application object, app. + const app = express() + + // remove x-powered-by header + app.disable('x-powered-by') + + // defaultargs.coffee exports a function that takes the argv object + // that is passed in and then does its + // best to supply sane defaults for any arguments that are missing. + argv = defargs(argv) + + app.startOpts = argv + + const log = (...stuff) => { + if (argv.debug) console.log(stuff) + } + const loga = (...stuff) => { + console.log(stuff) + } + + const ourErrorHandler = (req, res, next) => { + let fired = false + res.e = (error, status) => { + if (!fired) { + fired = true + res.statusCode = status || 500 + res.end('Server ' + error) + log('Res sent:', res.statusCode, error) + } else { + log('Already fired', error) + } + } + next() + } + let pagehandler, sitemaphandler, searchhandler, securityhandler + // Require the database adapter and initialize it with options. + app.pagehandler = pagehandler = require(argv.database.type)(argv) + + // Require the sitemap adapter and initialize it with options. + app.sitemaphandler = sitemaphandler = sitemapFactory(argv) + + // Require the site indexer and initialize it with options + app.searchhandler = searchhandler = searchFactory(argv) + + // Require the security adapter and initialize it with options. + app.securityhandler = securityhandler = require(argv.security_type)(log, loga, argv) + + // If the site is owned, owner will contain the name of the owner + let owner = '' + + // If the user is logged in, user will contain their identity + let user = '' + + // Called from authentication when the site is claimed, + // to update the name of the owner held here. + const updateOwner = id => { + owner = id + } + + // #### Middleware #### + // + // Allow json to be got cross origin. + const cors = (req, res, next) => { + res.header('Access-Control-Allow-Origin', req.get('origin') || '*') + next() + } + + const remoteGet = (remote, slug, cb) => { + // assume http, as we know no better at this point and we need to specify a protocol. + const remoteURL = new URL(`http://${remote}/${slug}.json`).toString() + // set a two second timeout + fetch(remoteURL, { signal: AbortSignal.timeout(2000) }) + .then(res => { + if (res.ok) { + return res + } + throw new Error(res.statusText) + }) + .then(res => { + return res.json() + }) + .then(json => { + cb(null, json, 200) + }) + .catch(err => { + console.error('Unable to fetch remote resource', remote, slug, err) + cb(err, 'Page not found', 404) + }) + } + + // #### Express configuration #### + // Set up all the standard express server options, + // including hbs to use handlebars/mustache templates + // saved with a .html extension, and no layout. + + // + const staticPathOptions = { + dotfiles: 'ignore', + etag: true, + immutable: false, + lastModified: false, + maxAge: '1h', + } + + app.set('views', path.join(require.resolve('wiki-client/package.json'), '..', 'views')) + app.set('view engine', 'html') + app.engine('html', hbs.express4()) + app.set('view options', { layout: false }) + + // return deterministically colored strings + const colorString = str => { + const colorReset = '\x1b[0m' + let hash = 0 + str.split('').forEach(char => { + hash = char.charCodeAt(0) + ((hash << 5) - hash) + }) + let color = '\x1b[38;2' + ;[...Array(3).keys()].forEach(i => { + const value = (hash >> (i * 8)) & 0xff + color += ':' + value.toString() + }) + color += 'm' + return color + str + colorReset + } + + // use logger, at least in development, probably needs a param to configure (or turn off). + // use stream to direct to somewhere other than stdout. + logger.token('vhost', (req, res) => { + return colorString(req.hostname) + }) + app.use(logger(':vhost :method :url :status :res[content-length] - :response-time ms')) + app.use(cookieParser()) + app.use(bodyParser.json({ limit: argv.uploadLimit })) + app.use(bodyParser.urlencoded({ extended: true, limit: argv.uploadLimit })) + app.use(methodOverride()) + const cookieValue = { + httpOnly: true, + sameSite: 'lax', + } + if (argv.wiki_domain) { + if (!argv.wiki_domain.endsWith('localhost')) { + cookieValue['domain'] = argv.wiki_domain + } + } + // use secureProxy as TLS is terminated in outside the node process + let cookieName + if (argv.secure_cookie) { + cookieName = 'wikiTlsSession' + cookieValue['secureProxy'] = true + } else { + cookieName = 'wikiSession' + } + app.use( + sessions({ + cookieName: cookieName, + requestKey: 'session', + secret: argv.cookieSecret, + // make the session session_duration days long + duration: argv.session_duration * 24 * 60 * 60 * 1000, + // add 12 hours to session if less than 12 hours to expiry + activeDuration: 24 * 60 * 60 * 1000, + cookie: cookieValue, + }), + ) + + app.use(ourErrorHandler) + + // Add static route to the client + app.use(express.static(argv.client, staticPathOptions)) + + // ##### Define security routes ##### + securityhandler.defineRoutes(app, cors, updateOwner) + + // Add static route to assets + app.use('/assets', cors, express.static(argv.assets)) + + // Add static routes to the plugins client. + Object.keys(require.main.require('./package').dependencies) + .filter(depend => depend.startsWith('wiki-plugin')) + .forEach(plugin => { + const clientPath = path.join( + path.dirname(require.resolve(`${plugin}/package`, { paths: require.main.paths })), + 'client', + ) + const pluginPath = '/plugins/' + plugin.slice(12) + app.use(pluginPath, cors, express.static(clientPath, staticPathOptions)) + }) + + // Add static routes to the security client. + if (argv.security != './security') { + app.use('/security', express.static(path.join(argv.packageDir, argv.security_type, 'client'), staticPathOptions)) + } + + // ##### Set up standard environments. ##### + // In dev mode turn on console.log debugging as well as showing the stack on err. + if ('development' == app.get('env')) { + app.use(errorHandler()) + argv.debug = true + } + + // Show all of the options a server is using. + log(argv) + + // #### Routes #### + // Routes currently make up the bulk of the Express port of + // Smallest Federated Wiki. Most routes use literal names, + // or regexes to match, and then access req.params directly. + + // ##### Redirects ##### + // Common redirects that may get used throughout the routes. + const index = argv.home + '.html' + const oops = '/oops' + + // ##### Get routes ##### + // Routes have mostly been kept together by http verb, with the exception + // of the openID related routes which are at the end together. + + // Main route for initial contact. Allows us to + // link into a specific set of pages, local and remote. + // Can also be handled by the client, but it also sets up + // the login status, and related footer html, which the client + // relies on to know if it is logged in or not. + app.get(/^((\/[a-zA-Z0-9:.-]+\/[a-z0-9-]+(_rev\d+)?)+)\/?$/, cors, (req, res, next) => { + const urlPages = req.params[0] + .split('/') + .filter((_, index) => index % 2 === 0) + .slice(1) + const urlLocs = req.params[0] + .split('/') + .slice(1) + .filter((_, index) => index % 2 === 0) + if (['plugin', 'auth'].indexOf(urlLocs[0]) > -1) { + return next() + } + const title = urlPages.slice().pop().replace(/-+/g, ' ') + user = securityhandler.getUser(req) + const info = { + title, + pages: [], + authenticated: user ? true : false, + user: user, + seedNeighbors: argv.neighbors, + owned: owner ? true : false, + isOwner: securityhandler.isAuthorized(req) ? true : false, + ownedBy: owner ? owner : '', + } + for (const [idx, page] of urlPages.entries()) { + let pageDiv + if (urlLocs[idx] === 'view') { + pageDiv = { page } + } else { + pageDiv = { page, origin: `data-site=${urlLocs[idx]}` } + } + info.pages.push(pageDiv) + } + res.render('static.html', info) + }) + + app.get(/^\/([a-z0-9-]+)\.html$/, cors, (req, res, next) => { + const slug = req.params[0] + log(slug) + if (slug === 'runtests') return next() + pagehandler.get(slug, (e, page, status) => { + if (e) { + return res.e(e) + } + if (status === 404) { + return res.status(status).send(page) + } + page.title ||= slug.replace(/-+/g, ' ') + page.story ||= [] + user = securityhandler.getUser(req) + + const info = { + title: page.title, + pages: [ + { + page: slug, + generated: 'data-server-generated=true', + story: render(page), + }, + ], + authenticated: user ? true : false, + user: user, + seedNeighbors: argv.neighbors, + owned: owner ? true : false, + isOwner: securityhandler.isAuthorized(req) ? true : false, + ownedBy: owner ? owner : '', + } + res.render('static.html', info) + }) + }) + + app.get('/system/factories.json', (req, res) => { + res.status(200) + res.header('Content-Type', 'application/json') + const factories = [] + Object.keys(require.main.require('./package').dependencies) + .filter(depend => depend.startsWith('wiki-plugin')) + .forEach(plugin => { + try { + factories.push(require.main.require(`${plugin}/factory`)) + } catch { + // do nothing if plugin doesn't have a factory category. + } + }) + res.end(JSON.stringify(factories)) + }) + + // ###### Json Routes ###### + // Handle fetching local and remote json pages. + // Local pages are handled by the pagehandler module. + app.get(/^\/([a-z0-9-]+)\.json$/, cors, (req, res) => { + const file = req.params[0] + pagehandler.get(file, (e, page, status) => { + if (e) { + return res.e(e) + } + res.status(status || 200).send(page) + }) + }) + + // Remote pages use the http client to retrieve the page + // and sends it to the client. TODO: consider caching remote pages locally. + app.get(/^\/remote\/([a-zA-Z0-9:.-]+)\/([a-z0-9-]+)\.json$/, (req, res) => { + remoteGet(req.params[0], req.params[1], (e, page, status) => { + if (e) { + log('remoteGet error:', e) + return res.e(e) + } + res.status(status || 200).send(page) + }) + }) + + // ###### Theme Routes ###### + // If themes doesn't exist send 404 and let the client + // deal with it. + app.get(/^\/theme\/(\w+\.\w+)$/, cors, (req, res) => { + res.sendFile(path.join(argv.status, 'theme', req.params[0]), { dotfiles: 'allow' }, e => { + if (e) { + // swallow the error if the theme does not exist... + if (req.path === '/theme/style.css') { + res.set('Content-Type', 'text/css') + res.send('') + } else { + res.sendStatus(404) + } + } + }) + }) + + // ###### Favicon Routes ###### + // If favLoc doesn't exist send the default favicon. + const favLoc = path.join(argv.status, 'favicon.png') + const defaultFavLoc = path.join(argv.root, 'default-data', 'status', 'favicon.png') + app.get('/favicon.png', cors, (req, res) => { + fs.access(favLoc, fs.constants.F_OK, err => { + if (!err) { + res.sendFile(favLoc, { dotfiles: 'allow' }) + } else { + res.sendFile(defaultFavLoc) + } + }) + }) + + const authorized = (req, res, next) => { + if (securityhandler.isAuthorized(req)) { + next() + } else { + console.log('rejecting', req.path) + res.sendStatus(403) + } + } + + // Accept favicon image posted to the server, and if it does not already exist + // save it. + app.post('/favicon.png', authorized, (req, res) => { + const favicon = req.body.image.replace(/^data:image\/png;base64,/, '') + const buf = Buffer.from(favicon, 'base64') + fs.access(argv.status, fs.constants.F_OK, err => { + if (!err) { + fs.writeFile(favLoc, buf, e => { + if (e) { + return res.e(e) + } + res.send('Favicon Saved') + }) + } else { + fs.mkdir(argv.status, { recursive: true }, () => { + fs.writeFile(favLoc, buf, e => { + if (e) { + return res.e(e) + } + res.send('Favicon Saved') + }) + }) + } + }) + }) + + // Redirect remote favicons to the server they are needed from. + app.get(/^\/remote\/([a-zA-Z0-9:.-]+\/favicon.png)$/, (req, res) => { + const remotefav = `http://${req.params[0]}` + res.redirect(remotefav) + }) + + // ###### Recycler Routes ###### + // These routes are only available to the site's owner + + // Give the recycler a standard flag - use the Taiwan symbol as the use of + // negative space outward pointing arrows nicely indicates that items can be removed + const recyclerFavLoc = path.join(argv.root, 'default-data', 'status', 'recycler.png') + app.get('/recycler/favicon.png', authorized, (req, res) => { + res.sendFile(recyclerFavLoc, { dotfiles: 'allow' }) + }) + + // Send an array of pages currently in the recycler via json + app.get('/recycler/system/slugs.json', authorized, (req, res) => { + fs.readdir(argv.recycler, (e, files) => { + if (e) { + return res.e(e) + } + const doRecyclermap = async file => { + return new Promise(resolve => { + const recycleFile = 'recycler/' + file + pagehandler.get(recycleFile, (e, page, status) => { + if (e || status === 404) { + console.log('Problem building recycler map:', file, 'e: ', e) + // this will leave an undefined/empty item in the array, which we will filter out later + return resolve(null) + } + resolve({ + slug: file, + title: page.title, + }) + }) + }) + } + + Promise.all(files.map(doRecyclermap)) + .then(recyclermap => { + recyclermap = recyclermap.filter(el => !!el) + res.send(recyclermap) + }) + .catch(error => { + res.e(error) + }) + }) + }) + + // Fetching page from the recycler + /////^/([a-z0-9-]+)\.json$/// + app.get(/^\/recycler\/([a-z0-9-]+)\.json$/, authorized, (req, res) => { + const file = 'recycler/' + req.params[0] + pagehandler.get(file, (e, page, status) => { + if (e) { + return res.e(e) + } + res.status(status || 200).send(page) + }) + }) + + // Delete page from the recycler + app.delete(/^\/recycler\/([a-z0-9-]+)\.json$/, authorized, (req, res) => { + const file = 'recycler/' + req.params[0] + pagehandler.delete(file, err => { + if (err) { + res.status(500).send(err) + } + res.status(200).send('') + }) + }) + + // ###### Meta Routes ###### + // Send an array of pages in the database via json + app.get('/system/slugs.json', cors, (req, res) => { + pagehandler.slugs((err, files) => { + if (err) { + res.status(500).send(err) + } + res.send(files) + }) + }) + + // Returns a list of installed plugins. (does this get called anymore!) + app.get('/system/plugins.json', cors, (req, res) => { + try { + const pluginNames = Object.keys(require.main.require('./package').dependencies) + .filter(depend => depend.startsWith('wiki-plugin')) + .map(name => name.slice(12)) + res.send(pluginNames) + } catch (e) { + return res.e(e) + } + }) + //{ + const sitemapLoc = path.join(argv.status, 'sitemap.json') + app.get('/system/sitemap.json', cors, (req, res) => { + fs.access(sitemapLoc, fs.constants.F_OK, err => { + if (!err) { + res.sendFile(sitemapLoc, { dotfiles: 'allow' }) + } else { + // only createSitemap if we are not already creating one + if (!sitemaphandler.isWorking()) { + sitemaphandler.createSitemap(pagehandler) + } + // wait for the sitemap file to be written, before sending + sitemaphandler.once('finished', () => { + res.sendFile(sitemapLoc, { dotfiles: 'allow' }) + }) + } + }) + }) + + const xmlSitemapLoc = path.join(argv.status, 'sitemap.xml') + app.get('/sitemap.xml', cors, (req, res) => { + fs.access(sitemapLoc, fs.constants.F_OK, err => { + if (!err) { + res.sendFile(xmlSitemapLoc, { dotfiles: 'allow' }) + } else { + if (!sitemaphandler.isWorking()) { + sitemaphandler.createSitemap(pagehandler) + } + sitemaphandler.once('finished', () => { + res.sendFile(xmlSitemapLoc, { dotfiles: 'allow' }) + }) + } + }) + }) + + const searchIndexLoc = path.join(argv.status, 'site-index.json') + app.get('/system/site-index.json', cors, (req, res) => { + fs.access(searchIndexLoc, fs.constants.F_OK, err => { + if (!err) { + res.sendFile(searchIndexLoc, { dotfiles: 'allow' }) + } else { + // only create index if we are not already creating one + if (!searchhandler.isWorking()) { + searchhandler.createIndex(pagehandler) + } + searchhandler.once('indexed', () => { + res.sendFile(searchIndexLoc, { dotfiles: 'allow' }) + }) + } + }) + }) + + app.get('/system/export.json', cors, (req, res) => { + pagehandler.pages((e, sitemap) => { + if (e) { + return res.e(e) + } + const pagePromises = sitemap.map(stub => { + return new Promise((resolve, reject) => { + pagehandler.get(stub.slug, (error, page) => { + if (error) { + return reject(error) + } + resolve({ slug: stub.slug, page }) + }) + }) + }) + + Promise.all(pagePromises) + .then(pages => { + const pageExport = pages.reduce((dict, combined) => { + dict[combined.slug] = combined.page + return dict + }, {}) + // TODO: this fails for a very large site + res.json(pageExport) + }) + .catch(error => { + res.e(error) + }) + }) + }) + + const admin = (req, res, next) => { + if (securityhandler.isAdmin(req)) { + next() + } else { + console.log('rejecting', req.path) + res.sendStatus(403) + } + } + + app.get('/system/version.json', admin, (req, res) => { + const versions = {} + const wikiModule = require.main + versions[wikiModule.require('./package').name] = wikiModule.require('./package').version + versions[wikiModule.require('wiki-server/package').name] = wikiModule.require('wiki-server/package').version + versions[wikiModule.require('wiki-client/package').name] = wikiModule.require('wiki-client/package').version + + versions['security'] = {} + Object.keys(require.main.require('./package').dependencies) + .filter(depend => depend.startsWith('wiki-security')) + .forEach(key => { + versions.security[key] = wikiModule.require(`${key}/package`).version + }) + + versions['plugins'] = {} + Object.keys(require.main.require('./package').dependencies) + .filter(depend => depend.startsWith('wiki-plugin')) + .forEach(key => { + versions.plugins[key] = wikiModule.require(`${key}/package`).version + }) + + res.json(versions) + }) + + // ##### Proxy routes ##### + + app.get('/proxy/*splat', authorized, (req, res) => { + const pathParts = req.originalUrl.split('/') + const remoteHost = pathParts[2] + pathParts.splice(0, 3) + const remoteResource = pathParts.join('/') + // this will fail if remote is TLS only! + const requestURL = 'http://' + remoteHost + '/' + remoteResource + console.log('PROXY Request: ', requestURL) + if ( + requestURL.endsWith('.json') || + requestURL.endsWith('.png') || + requestURL.endsWith('.jpg') || + pathParts[0] === 'plugin' + ) { + fetch(requestURL, { signal: AbortSignal.timeout(2000) }) + .then(async fetchRes => { + if (fetchRes.ok) { + res.set('content-type', fetchRes.headers.get('content-type')) + res.set('last-modified', fetchRes.headers.get('last-modified')) + await pipeline(fetchRes.body, res) + } else { + res.status(fetchRes.status).end() + } + }) + .catch(err => { + console.log('ERROR: Proxy Request ', requestURL, err) + res.status(500).end() + }) + } else { + res.status(400).end() + } + }) + + // ##### Put routes ##### + + app.put(/^\/page\/([a-z0-9-]+)\/action$/i, authorized, (req, res) => { + const action = JSON.parse(req.body.action) + // Handle all of the possible actions to be taken on a page, + const actionCB = (e, page, status) => { + //if e then return res.e e + if (status === 404) { + // res.status(status).send(page) + return res.e(page, status) + } + // Using Coffee-Scripts implicit returns we assign page.story to the + // result of a list comprehension by way of a switch expression. + try { + page.story = (() => { + switch (action.type) { + case 'move': + return action.order.map(id => { + const match = page.story.filter(para => id === para.id)[0] + if (!match) throw 'Ignoring move. Try reload.' + return match + }) + case 'add': { + const idx = page.story.map(para => para.id).indexOf(action.after) + 1 + page.story.splice(idx, 0, action.item) + return page.story + } + + case 'remove': + return page.story.filter(para => para?.id !== action.id) + + case 'edit': + return page.story.map(para => { + if (para.id === action.id) { + return action.item + } else { + return para + } + }) + + case 'create': + case 'fork': + return page.story || [] + + default: + log('Unfamiliar action:', action) + //page.story + throw 'Unfamiliar action ignored' + } + })() + } catch (e) { + return res.e(e) + } + // Add a blank journal if it does not exist. + // And add what happened to the journal. + if (!page.journal) { + page.journal = [] + } + if (action.fork) { + page.journal.push({ type: 'fork', site: action.fork, date: action.date - 1 }) + delete action.fork + } + page.journal.push(action) + pagehandler.put(req.params[0], page, e => { + if (e) return res.e(e) + res.send('ok') + // log 'saved' + }) + // update sitemap + sitemaphandler.update(req.params[0], page) + + // update site index + searchhandler.update(req.params[0], page) + } + // log action + + // If the action is a fork, get the page from the remote server, + // otherwise ask pagehandler for it. + if (action.fork) { + pagehandler.saveToRecycler(req.params[0], err => { + if (err && err !== 'page does not exist') { + console.log(`Error saving ${req.params[0]} before fork: ${err}`) + } + if (action.forkPage) { + const forkPageCopy = JSON.parse(JSON.stringify(action.forkPage)) + delete action.forkPage + actionCB(null, forkPageCopy) + } else { + // Legacy path, new clients will provide forkPage on implicit forks. + remoteGet(action.fork, req.params[0], actionCB) + } + }) + } else if (action.type === 'create') { + // Prevent attempt to write circular structure + const itemCopy = JSON.parse(JSON.stringify(action.item)) + pagehandler.get(req.params[0], (e, page, status) => { + if (e) return actionCB(e) + if (status !== 404) { + res.status(409).send('Page already exists.') + } else { + actionCB(null, itemCopy) + } + }) + } else if (action.type === 'fork') { + pagehandler.saveToRecycler(req.params[0], err => { + if (err) console.log(`Error saving ${req.params[0]} before fork: ${err}`) + if (action.forkPage) { + // push + const forkPageCopy = JSON.parse(JSON.stringify(action.forkPage)) + delete action.forkPage + actionCB(null, forkPageCopy) + } else { + // pull + remoteGet(action.site, req.params[0], actionCB) + } + }) + } else { + pagehandler.get(req.params[0], actionCB) + } + }) + + // Return the oops page when login fails. + app.get('/oops', (req, res) => { + res.statusCode = 403 + res.render('oops.html', { msg: 'This is not your wiki!' }) + }) + + // Traditional request to / redirects to index :) + app.get('/', cors, (req, res) => { + const home = path.join(argv.assets, 'home', 'index.html') + fs.stat(home, (err, stats) => { + if (err || !stats.isFile()) { + res.redirect(index) + } else { + res.redirect('/assets/home/index.html') + } + }) + }) + + // ##### Delete Routes ##### + + app.delete(/^\/([a-z0-9-]+)\.json$/, authorized, (req, res) => { + const pageFile = req.params[0] + // we need the original page text to remove it from the index, so get the original text before deleting it + pagehandler.get(pageFile, (e, page, status) => { + const title = page.title + pagehandler.delete(pageFile, err => { + if (err) { + res.status(500).send(err) + } else { + sitemaphandler.removePage(pageFile) + res.status(200).send('') + // update site index + searchhandler.removePage(req.params[0]) + } + }) + }) + }) + + // #### Start the server #### + // + // set a default process exitCode, so we can diferentiate between exiting as part of a reload, + // and an exit after an uncaught error. + // except when test is set, so the tests don't report a fail when closing the server process. + process.exitCode = argv.test ? 0 : 1 + + // Wait to make sure owner is known before listening. + securityhandler.retrieveOwner(e => { + // Throw if you can't find the initial owner + if (e) throw e + owner = securityhandler.getOwner() + console.log('owner: ' + owner) + app.emit('owner-set') + }) + + app.on('running-serv', server => { + // ### Plugins ### + // Should replace most WebSocketServers below. + const plugins = pluginsFactory(argv) + plugins.startServers({ argv, app }) + // ### Sitemap ### + // create sitemap at start-up + sitemaphandler.createSitemap(pagehandler) + // create site index at start-up + searchhandler.startUp(pagehandler) + }) + + // Return app when called, so that it can be watched for events and shutdown with .close() externally. + return app +} diff --git a/lib/sitemap.coffee b/lib/sitemap.coffee deleted file mode 100644 index 234841c3..00000000 --- a/lib/sitemap.coffee +++ /dev/null @@ -1,246 +0,0 @@ -### - * Federated Wiki : Node Server - * - * Copyright Ward Cunningham and other contributors - * Licensed under the MIT license. - * https://github.com/fedwiki/wiki-server/blob/master/LICENSE.txt -### - -# **sitemap.coffee** - -fs = require 'fs' -path = require 'path' -events = require 'events' -writeFileAtomic = require 'write-file-atomic' -xml2js = require 'xml2js' - -synopsis = require 'wiki-client/lib/synopsis' - -asSlug = (name) -> - name.replace(/\s/g, '-').replace(/[^A-Za-z0-9-]/g, '').toLowerCase() - - -module.exports = exports = (argv) -> - - wikiName = new URL(argv.url).hostname - - sitemap = [] - - queue = [] - - sitemapPageHandler = null - - # ms since last update we will remove sitemap from memory - sitemapTimeoutMs = 120000 - sitemapTimeoutHandler = null - - sitemapLoc = path.join(argv.status, 'sitemap.json') - xmlSitemapLoc = path.join(argv.status, 'sitemap.xml') - - working = false - - lastEdit = (journal) -> - for action in (journal || []) by -1 - return action.date if action.date and action.type != 'fork' - undefined - - sitemapUpdate = (file, page, cb) -> - - extractPageLinks = (collaborativeLinks, currentItem, currentIndex, array) -> - # extract collaborative links - # - this will need extending if we also extract the id of the item containing the link - try - linkRe = /\[\[([^\]]+)\]\]/g - match = undefined - while (match = linkRe.exec(currentItem.text)) != null - if not collaborativeLinks.has(asSlug(match[1])) - collaborativeLinks.set(asSlug(match[1]), currentItem.id) - if 'reference' == currentItem.type - if not collaborativeLinks.has(currentItem.slug) - collaborativeLinks.set(currentItem.slug, currentItem.id) - catch err - console.log "METADATA *** #{wikiName} Error extracting links from #{currentIndex} of #{JSON.stringify(array)}", err.message - collaborativeLinks - - try - pageLinksMap = page.story.reduce( extractPageLinks, new Map()) - catch err - console.log "METADATA *** #{wikiName} reduce to extract links on #{file} failed", err.message - pageLinksMap = [] - # - if pageLinksMap.size > 0 - pageLinks = Object.fromEntries(pageLinksMap) - else - pageLinks = undefined - - entry = { - 'slug': file - 'title': page.title - 'date': lastEdit(page.journal) - 'synopsis': synopsis(page) - 'links': pageLinks - } - - slugs = sitemap.map (page) -> page.slug - - idx = slugs.indexOf(file) - - if ~idx - sitemap[idx] = entry - else - sitemap.push entry - - cb() - - sitemapRemovePage = (file, cb) -> - slugs = sitemap.map (page) -> page.slug - idx = slugs.indexOf(file) - - if ~idx - sitemap.splice(idx,1) - - cb() - - sitemapSave = (sitemap, cb) -> - fs.exists argv.status, (exists) -> - if exists - writeFileAtomic sitemapLoc, JSON.stringify(sitemap), (e) -> - return cb(e) if e - cb() - else - fs.mkdir argv.status, { recursive: true }, -> - writeFileAtomic sitemapLoc, JSON.stringify(sitemap), (e) -> - return cb(e) if e - cb() - - sitemapRestore = (cb) -> - fs.exists sitemapLoc, (exists) -> - if exists - fs.readFile(sitemapLoc, (err, data) -> - return cb(err) if err - try - sitemap = JSON.parse(data) - catch e - return cb(e) - process.nextTick( -> - serial(queue.shift())) - ) - else - # sitemap file does not exist, so needs creating - itself.createSitemap(sitemapPageHandler) - - xmlSitemapSave = (sitemap, cb) -> - xmlmap = [] - sitemap.forEach (page) -> - result = {} - result["loc"] = argv.url + "/" + page.slug + ".html" - if page.date? - date = new Date(page.date) - if !(isNaN(date.valueOf())) - result["lastmod"] = date.toISOString().substring(0,10) - xmlmap.push result - xmlmap = {'urlset': {"$": {"xmlns": "http://www.sitemaps.org/schemas/sitemap/0.9"},'url': xmlmap}} - builder = new xml2js.Builder() - xml = builder.buildObject(xmlmap) - fs.exists argv.status, (exists) -> - if exists - writeFileAtomic xmlSitemapLoc, xml, (e) -> - return cb(e) if e - cb() - else - fs.mkdir argv.status, { recursive: true }, -> - writeFileAtomic xmlSitemapLoc, xml, (e) -> - return cb(e) if e - cb() - - serial = (item) -> - if item - switch item.action - when "update" - itself.start() - sitemapUpdate(item.file, item.page, (e) -> - process.nextTick( -> - serial(queue.shift()) - ) - ) - when "remove" - itself.start() - sitemapRemovePage(item.file, (e) -> - process.nextTick( -> - serial(queue.shift()) - ) - ) - else - console.log "Sitemap unexpected action #{item.action} for #{item.page} in #{wikiName}" - process.nextTick( -> - serial(queue.shift)) - else - sitemapSave sitemap, (e) -> - console.log "Problems saving sitemap #{wikiName}: "+ e if e - itself.stop() - xmlSitemapSave sitemap, (e) -> - console.log "Problems saving sitemap(xml) #{wikiName}"+ e if e - - - #### Public stuff #### - - itself = new events.EventEmitter - itself.start = -> - clearTimeout(sitemapTimeoutHandler) - working = true - @emit 'working' - itself.stop = -> - clearsitemap = -> - console.log "removing sitemap #{wikiName} from memory" - sitemap = [] - clearTimeout(sitemapTimeoutHandler) - sitemapTimeoutHandler = setTimeout clearsitemap, sitemapTimeoutMs - working = false - @emit 'finished' - - itself.isWorking = -> - working - - itself.createSitemap = (pagehandler) -> - - itself.start() - - # we save the pagehandler, so we can recreate the sitemap if it is removed - sitemapPageHandler = pagehandler if !sitemapPageHandler? - - pagehandler.pages (e, newsitemap) -> - if e - console.log "createSitemap #{wikiName} : error " + e - itself.stop() - return e - sitemap = newsitemap - - process.nextTick ( -> - serial(queue.shift())) - - itself.removePage = (file) -> - action = "remove" - queue.push({action, file, ""}) - if sitemap.length is 0 and !working - itself.start() - sitemapRestore (e) -> - console.log "Problems restoring sitemap #{wikiName} : " + e if e - itself.createSitemap(sitemapPageHandler) - else - serial(queue.shift()) unless working - - - itself.update = (file, page) -> - action = "update" - queue.push({action, file, page}) - if sitemap.length is 0 and !working - itself.start() - sitemapRestore (e) -> - console.log "Problems restoring sitemap #{wikiName} : " + e if e - itself.createSitemap(sitemapPageHandler) - else - serial(queue.shift()) unless working - - - - itself diff --git a/lib/sitemap.js b/lib/sitemap.js new file mode 100644 index 00000000..feb758e6 --- /dev/null +++ b/lib/sitemap.js @@ -0,0 +1,288 @@ +/* + * Federated Wiki : Node Server + * + * Copyright Ward Cunningham and other contributors + * Licensed under the MIT license. + * https://github.com/fedwiki/wiki-server/blob/master/LICENSE.txt + */ + +// **sitemap.coffee** + +const fs = require('fs') +const path = require('path') +const events = require('events') +const writeFileAtomic = require('write-file-atomic') +const xml2js = require('xml2js') + +const synopsis = require('wiki-client/lib/synopsis') + +const asSlug = name => + name + .replace(/\s/g, '-') + .replace(/[^A-Za-z0-9-]/g, '') + .toLowerCase() + +module.exports = exports = argv => { + const wikiName = new URL(argv.url).hostname + + let sitemap = [] + + const queue = [] + + let sitemapPageHandler = null + + // ms since last update we will remove sitemap from memory + const sitemapTimeoutMs = 120000 + let sitemapTimeoutHandler = null + + const sitemapLoc = path.join(argv.status, 'sitemap.json') + const xmlSitemapLoc = path.join(argv.status, 'sitemap.xml') + + let working = false + + const lastEdit = journal => { + if (!journal) return undefined + // find the last journal entry, that is not a fork, with a date. + const last = journal.findLast(action => { + return action.date && action.type != 'fork' + }) + return last ? last.date : undefined + } + + const sitemapUpdate = (file, page, cb) => { + let pageLinks, pageLinksMap + const extractPageLinks = (collaborativeLinks, currentItem, currentIndex, array) => { + // extract collaborative links + // - this will need extending if we also extract the id of the item containing the link + try { + const linkRe = /\[\[([^\]]+)\]\]/g + let match = undefined + while ((match = linkRe.exec(currentItem.text)) != null) { + if (!collaborativeLinks.has(asSlug(match[1]))) { + collaborativeLinks.set(asSlug(match[1]), currentItem.id) + } + } + if ('reference' == currentItem.type) { + if (!collaborativeLinks.has(currentItem.slug)) { + collaborativeLinks.set(currentItem.slug, currentItem.id) + } + } + } catch (err) { + console.log( + `METADATA *** ${wikiName} Error extracting links from ${currentIndex} of ${JSON.stringify(array)}`, + err.message, + ) + } + return collaborativeLinks + } + try { + pageLinksMap = page.story.reduce(extractPageLinks, new Map()) + } catch (err) { + console.log(`METADATA *** ${wikiName} reduce to extract links on ${file} failed`, err.message) + pageLinksMap = [] + } + // + if (pageLinksMap.size > 0) { + pageLinks = Object.fromEntries(pageLinksMap) + } else { + pageLinks = undefined + } + + const entry = { + slug: file, + title: page.title, + date: lastEdit(page.journal), + synopsis: synopsis(page), + links: pageLinks, + } + + const slugs = sitemap.map(page => page.slug) + + const idx = slugs.indexOf(file) + + if (~idx) { + sitemap[idx] = entry + } else { + sitemap.push(entry) + } + cb() + } + + const sitemapRemovePage = (file, cb) => { + const slugs = sitemap.map(page => page.slug) + const idx = slugs.indexOf(file) + + if (~idx) { + sitemap.splice(idx, 1) + } + cb() + } + + const sitemapSave = (sitemap, cb) => { + fs.access(argv.status, fs.constants.F_OK, err => { + if (!err) { + writeFileAtomic(sitemapLoc, JSON.stringify(sitemap), e => { + if (e) return cb(e) + cb() + }) + } else + fs.mkdir(argv.status, { recursive: true }, () => { + writeFileAtomic(sitemapLoc, JSON.stringify(sitemap), e => { + if (e) return cb(e) + cb() + }) + }) + }) + } + + const sitemapRestore = cb => { + fs.access(sitemapLoc, fs.constants.F_OK, err => { + if (!err) { + fs.readFile(sitemapLoc, (err, data) => { + if (err) return cb(err) + try { + sitemap = JSON.parse(data) + } catch (e) { + return cb(e) + } + process.nextTick(() => { + serial(queue.shift()) + }) + }) + } else { + // sitemap file does not exist, so needs creating + itself.createSitemap(sitemapPageHandler) + } + }) + } + + const xmlSitemapSave = (sitemap, cb) => { + const xmlmapPages = [] + sitemap.forEach(page => { + const result = {} + result['loc'] = argv.url + '/' + page.slug + '.html' + if (page.date) { + const date = new Date(page.date) + if (!isNaN(date.valueOf())) { + result['lastmod'] = date.toISOString().substring(0, 10) + } + } + xmlmapPages.push(result) + }) + const xmlmap = { urlset: { $: { xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9' }, url: xmlmapPages } } + const builder = new xml2js.Builder() + const xml = builder.buildObject(xmlmap) + fs.access(argv.status, fs.constants.F_OK, err => { + if (!err) { + writeFileAtomic(xmlSitemapLoc, xml, e => { + if (e) return cb(e) + cb() + }) + } else { + fs.mkdir(argv.status, { recursive: true }, () => { + writeFileAtomic(xmlSitemapLoc, xml, e => { + if (e) return cb(e) + cb() + }) + }) + } + }) + } + + const serial = item => { + if (item) { + switch (item.action) { + case 'update': + itself.start() + sitemapUpdate(item.file, item.page, e => process.nextTick(() => serial(queue.shift()))) + break + case 'remove': + itself.start() + sitemapRemovePage(item.file, e => process.nextTick(() => serial(queue.shift()))) + break + default: + console.log(`Sitemap unexpected action ${item.action} for ${item.page} in ${wikiName}`) + process.nextTick(() => serial(queue.shift)) + } + } else + sitemapSave(sitemap, e => { + if (e) console.log(`Problems saving sitemap ${wikiName}: ` + e) + itself.stop() + }) + xmlSitemapSave(sitemap, e => { + if (e) console.log(`Problems saving sitemap(xml) ${wikiName}`) + e + }) + } + + // #### Public stuff #### + + const itself = new events.EventEmitter() + itself.start = () => { + clearTimeout(sitemapTimeoutHandler) + working = true + itself.emit('working') + } + itself.stop = () => { + const clearsitemap = () => { + console.log(`removing sitemap ${wikiName} from memory`) + sitemap.length = 0 + clearTimeout(sitemapTimeoutHandler) + } + // don't clear sitemap when in test environment. It just delays the tests completing. + if (!argv.test) sitemapTimeoutHandler = setTimeout(clearsitemap, sitemapTimeoutMs) + working = false + itself.emit('finished') + } + itself.isWorking = () => { + working + } + + itself.createSitemap = pagehandler => { + itself.start() + // we save the pagehandler, so we can recreate the sitemap if it is removed + if (!sitemapPageHandler) sitemapPageHandler = pagehandler + + pagehandler.pages((e, newsitemap) => { + if (e) { + console.log(`createSitemap ${wikiName} : error ` + e) + itself.stop() + return e + } + sitemap = newsitemap + + process.nextTick(() => { + serial(queue.shift()) + }) + }) + } + + itself.removePage = file => { + const action = 'remove' + queue.push({ action, file }) + if (sitemap.length === 0 && !working) { + itself.start() + sitemapRestore(e => { + if (e) console.log(`Problems restoring sitemap ${wikiName} : ` + e) + itself.createSitemap(sitemapPageHandler) + }) + } else { + if (!working) serial(queue.shift()) + } + } + + itself.update = (file, page) => { + const action = 'update' + queue.push({ action, file, page }) + if (sitemap.length === 0 && !working) { + itself.start() + sitemapRestore(e => { + if (e) console.log(`Problems restoring sitemap ${wikiName} : ` + e) + itself.createSitemap(sitemapPageHandler) + }) + } else { + if (!working) serial(queue.shift()) + } + } + + return itself +} diff --git a/package-lock.json b/package-lock.json index d758cca9..a05b5f92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,45 +9,55 @@ "version": "0.25.10", "license": "MIT", "dependencies": { - "async": "^3.2.4", "body-parser": "^2.2.0", "client-sessions": "^0.8.0", "coffeescript": "^2.5.0", "cookie-parser": "^1.4.4", "dompurify": "^3.1.0", "errorhandler": "^1.5.1", - "express": "^4.17.1", + "express": "^5.1.0", "express-hbs": "^2.5.0", "flates": "0.0.5", - "glob": "^7.2.3", "jsdom": "^26.0.0", "method-override": "^3.0.0", "minisearch": "^7.1.0", "morgan": "^1.9.1", - "node-fetch": "^3.3.2", "write-file-atomic": "^6.0.0", "xml2js": "^0.6.2" }, "devDependencies": { - "grunt": "^1.6.1", - "grunt-contrib-watch": "^1.1.0", + "@eslint/js": "^9.27.0", + "eslint": "^9.27.0", + "globals": "^16.1.0", "grunt-git-authors": "^3.2.0", - "grunt-mocha-test": "^0.13.3", - "mocha": "^11.1.0", - "should": "^13.2.3", + "prettier": "^3.5.3", "supertest": "^7.0.0", - "wiki-client": "^0.31", + "wiki-client": "^0.31.1", "wiki-plugin-activity": "0.7", "wiki-plugin-video": "^0.4" }, "engines": { - "node": ">=18.x" + "node": ">=20.x" + } + }, + "node_modules/@eslint/js": { + "version": "9.29.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.29.0.tgz", + "integrity": "sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, "license": "MIT" }, "node_modules/body-parser": { @@ -118,9 +128,9 @@ } }, "node_modules/body-parser/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -618,9 +628,9 @@ "license": "MIT" }, "node_modules/dompurify": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.5.tgz", - "integrity": "sha512-mLPd29uoRe9HpvwP2TxClGQBzGXeEC/we/q+bFlmPPmj2p2Ugl3r6ATu/UU1v77DXNcehiBg9zsr1dREyA/dJQ==", + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz", + "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -695,2438 +705,329 @@ "node": ">= 0.6" } }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" + "node_modules/eslint": { + "version": "9.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.29.0.tgz", + "integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.1", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.29.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">= 0.10.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/express-hbs": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/express-hbs/-/express-hbs-2.5.0.tgz", - "integrity": "sha512-i2O1ZBwKO32KF0MePnkgYHsAAILr9H9Sp5GoGp9JWz/qhsBfTMSq9VF1pN109DHysPX6YO88y7B+f6xnEEF/mg==", + "node_modules/eslint/node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, "license": "MIT", "dependencies": { - "handlebars": "^4.7.7", - "lodash": "^4.17.21", - "readdirp": "^3.6.0" + "eslint-visitor-keys": "^3.4.3" }, - "optionalDependencies": { - "js-beautify": "^1.13.11" + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/express-hbs/node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "optional": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, + "node_modules/eslint/node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/express-hbs/node_modules/@one-ini/wasm": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", - "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", - "license": "MIT", - "optional": true - }, - "node_modules/express-hbs/node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "node_modules/eslint/node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, "license": "MIT", - "optional": true, "engines": { - "node": ">=14" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/express-hbs/node_modules/abbrev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", - "license": "ISC", - "optional": true, + "node_modules/eslint/node_modules/@eslint/config-array": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz", + "integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/express-hbs/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", - "optional": true, + "node_modules/eslint/node_modules/@eslint/config-helpers": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.3.tgz", + "integrity": "sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/express-hbs/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "node_modules/eslint/node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, "license": "MIT", - "optional": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/express-hbs/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT", - "optional": true + "node_modules/eslint/node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } }, - "node_modules/express-hbs/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "license": "MIT", - "optional": true, + "node_modules/eslint/node_modules/@eslint/plugin-kit": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz", + "integrity": "sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0" + "@eslint/core": "^0.15.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/express-hbs/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "optional": true, + "node_modules/eslint/node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", + "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "color-name": "~1.1.4" + "@types/json-schema": "^7.0.15" }, "engines": { - "node": ">=7.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/express-hbs/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT", - "optional": true - }, - "node_modules/express-hbs/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "license": "MIT", - "optional": true, + "node_modules/eslint/node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=14" + "node": ">=18.18.0" } }, - "node_modules/express-hbs/node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "license": "MIT", - "optional": true, + "node_modules/eslint/node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" } }, - "node_modules/express-hbs/node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "optional": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "node_modules/eslint/node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/eslint/node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">= 8" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/express-hbs/node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT", - "optional": true + "node_modules/eslint/node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, - "node_modules/express-hbs/node_modules/editorconfig": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", - "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "node_modules/eslint/node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, "license": "MIT", - "optional": true, - "dependencies": { - "@one-ini/wasm": "0.1.1", - "commander": "^10.0.0", - "minimatch": "9.0.1", - "semver": "^7.5.3" - }, "bin": { - "editorconfig": "bin/editorconfig" + "acorn": "bin/acorn" }, "engines": { - "node": ">=14" + "node": ">=0.4.0" } }, - "node_modules/express-hbs/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "node_modules/eslint/node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, "license": "MIT", - "optional": true + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } }, - "node_modules/express-hbs/node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "optional": true, - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/express-hbs/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "license": "ISC", - "optional": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/express-hbs/node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "optional": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/express-hbs/node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/express-hbs/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC", - "optional": true - }, - "node_modules/express-hbs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/express-hbs/node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC", - "optional": true - }, - "node_modules/express-hbs/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", - "optional": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/express-hbs/node_modules/js-beautify": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", - "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", - "license": "MIT", - "optional": true, - "dependencies": { - "config-chain": "^1.1.13", - "editorconfig": "^1.0.4", - "glob": "^10.4.2", - "js-cookie": "^3.0.5", - "nopt": "^7.2.1" - }, - "bin": { - "css-beautify": "js/bin/css-beautify.js", - "html-beautify": "js/bin/html-beautify.js", - "js-beautify": "js/bin/js-beautify.js" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/express-hbs/node_modules/js-cookie": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", - "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/express-hbs/node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/express-hbs/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC", - "optional": true - }, - "node_modules/express-hbs/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", - "license": "ISC", - "optional": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/express-hbs/node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express-hbs/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "optional": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/express-hbs/node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "license": "MIT" - }, - "node_modules/express-hbs/node_modules/nopt": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", - "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", - "license": "ISC", - "optional": true, - "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/express-hbs/node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0", - "optional": true - }, - "node_modules/express-hbs/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/express-hbs/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", - "optional": true, - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/express-hbs/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/express-hbs/node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "license": "ISC", - "optional": true - }, - "node_modules/express-hbs/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/express-hbs/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/express-hbs/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "optional": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/express-hbs/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/express-hbs/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "optional": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/express-hbs/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/express-hbs/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "optional": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/express-hbs/node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "optional": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/express-hbs/node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/express-hbs/node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT", - "optional": true - }, - "node_modules/express-hbs/node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/express-hbs/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/express-hbs/node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/express-hbs/node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/express-hbs/node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/express-hbs/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "optional": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/express-hbs/node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "license": "MIT" - }, - "node_modules/express-hbs/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/express-hbs/node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/express-hbs/node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/express-hbs/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "optional": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/express-hbs/node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT", - "optional": true - }, - "node_modules/express-hbs/node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "optional": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/express-hbs/node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/express/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/express/node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/express/node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/express/node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express/node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/express/node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/express/node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/express/node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/express/node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/express/node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/express/node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/express/node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express/node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express/node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/express/node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express/node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express/node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/express/node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/express/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/express/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/express/node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/express/node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/express/node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/express/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/express/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express/node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/express/node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/express/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express/node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/express/node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/express/node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/express/node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/express/node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/express/node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/express/node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express/node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express/node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express/node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/express/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/express/node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/express/node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/express/node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/express/node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/flates": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/flates/-/flates-0.0.5.tgz", - "integrity": "sha512-KSD99qlu4M92d3EAwqY9k8HiLu7gOdo+Zk5vWrY1kzo05o1sacAGJ5E7rwomTMuuSGko5K6cXHfF/sHb2IGSxw==", - "engines": { - "node": ">0.6" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/glob/node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, - "node_modules/glob/node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/glob/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/glob/node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/glob/node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob/node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/grunt": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/grunt/-/grunt-1.6.1.tgz", - "integrity": "sha512-/ABUy3gYWu5iBmrUSRBP97JLpQUm0GgVveDCp6t3yRNIoltIYw7rEj3g5y1o2PGPR2vfTRGa7WC/LZHLTXnEzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "dateformat": "~4.6.2", - "eventemitter2": "~0.4.13", - "exit": "~0.1.2", - "findup-sync": "~5.0.0", - "glob": "~7.1.6", - "grunt-cli": "~1.4.3", - "grunt-known-options": "~2.0.0", - "grunt-legacy-log": "~3.0.0", - "grunt-legacy-util": "~2.0.1", - "iconv-lite": "~0.6.3", - "js-yaml": "~3.14.0", - "minimatch": "~3.0.4", - "nopt": "~3.0.6" - }, - "bin": { - "grunt": "bin/grunt" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/grunt-contrib-watch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.1.0.tgz", - "integrity": "sha512-yGweN+0DW5yM+oo58fRu/XIRrPcn3r4tQx+nL7eMRwjpvk+rQY6R8o94BPK0i2UhTg9FN21hS+m8vR8v9vXfeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "async": "^2.6.0", - "gaze": "^1.1.0", - "lodash": "^4.17.10", - "tiny-lr": "^1.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/grunt-contrib-watch/node_modules/async": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", - "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.14" - } - }, - "node_modules/grunt-contrib-watch/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/grunt-contrib-watch/node_modules/body": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/body/-/body-5.1.0.tgz", - "integrity": "sha512-chUsBxGRtuElD6fmw1gHLpvnKdVLK302peeFa9ZqAEk8TyzZ3fygLyUEDDPTJvL9+Bor0dIwn6ePOsRM2y0zQQ==", - "dev": true, - "dependencies": { - "continuable-cache": "^0.3.1", - "error": "^7.0.0", - "raw-body": "~1.1.0", - "safe-json-parse": "~1.0.1" - } - }, - "node_modules/grunt-contrib-watch/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/grunt-contrib-watch/node_modules/bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz", - "integrity": "sha512-/x68VkHLeTl3/Ll8IvxdwzhrT+IyKc52e/oyHhA2RwqPqswSnjVbSddfPRwAsJtbilMAPSRWwAlpxdYsSWOTKQ==", - "dev": true - }, - "node_modules/grunt-contrib-watch/node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/grunt-contrib-watch/node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/grunt-contrib-watch/node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/grunt-contrib-watch/node_modules/continuable-cache": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz", - "integrity": "sha512-TF30kpKhTH8AGCG3dut0rdd/19B7Z+qCnrMoBLpyQu/2drZdNrrpcjPEoJeSVsQM+8KmWG5O56oPDjSSUsuTyA==", - "dev": true - }, - "node_modules/grunt-contrib-watch/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/grunt-contrib-watch/node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/grunt-contrib-watch/node_modules/error": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", - "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", - "dev": true, - "dependencies": { - "string-template": "~0.2.1" - } - }, - "node_modules/grunt-contrib-watch/node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/grunt-contrib-watch/node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/grunt-contrib-watch/node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/grunt-contrib-watch/node_modules/faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha512-Xhj93RXbMSq8urNCUq4p9l0P6hnySJ/7YNRhYNug0bLOuii7pKO7xQFb5mx9xZXWCar88pLPb805PvUkwrLZpQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/grunt-contrib-watch/node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/grunt-contrib-watch/node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/grunt-contrib-watch/node_modules/gaze": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz", - "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "globule": "^1.0.0" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/grunt-contrib-watch/node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/grunt-contrib-watch/node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/grunt-contrib-watch/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/grunt-contrib-watch/node_modules/globule": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.4.tgz", - "integrity": "sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob": "~7.1.1", - "lodash": "^4.17.21", - "minimatch": "~3.0.2" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/grunt-contrib-watch/node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/grunt-contrib-watch/node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/grunt-contrib-watch/node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/grunt-contrib-watch/node_modules/http-parser-js": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", - "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/grunt-contrib-watch/node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/grunt-contrib-watch/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/grunt-contrib-watch/node_modules/livereload-js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz", - "integrity": "sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw==", - "dev": true, - "license": "MIT" - }, - "node_modules/grunt-contrib-watch/node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, - "license": "MIT" - }, - "node_modules/grunt-contrib-watch/node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/grunt-contrib-watch/node_modules/minimatch": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", - "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/grunt-contrib-watch/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/grunt-contrib-watch/node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/grunt-contrib-watch/node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/grunt-contrib-watch/node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/grunt-contrib-watch/node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/grunt-contrib-watch/node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/grunt-contrib-watch/node_modules/raw-body": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz", - "integrity": "sha512-WmJJU2e9Y6M5UzTOkHaM7xJGAPQD8PNzx3bAd2+uhZAim6wDk6dAZxPVYLF67XhbR4hmKGh33Lpmh4XWrCH5Mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "1", - "string_decoder": "0.10" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/grunt-contrib-watch/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/grunt-contrib-watch/node_modules/safe-json-parse": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz", - "integrity": "sha512-o0JmTu17WGUaUOHa1l0FPGXKBfijbxK6qoHzlkihsDXxzBHvJcA7zgviKR92Xs841rX9pK16unfphLq0/KqX7A==", - "dev": true - }, - "node_modules/grunt-contrib-watch/node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/grunt-contrib-watch/node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/grunt-contrib-watch/node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/grunt-contrib-watch/node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/grunt-contrib-watch/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/grunt-contrib-watch/node_modules/string-template": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", - "integrity": "sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw==", - "dev": true - }, - "node_modules/grunt-contrib-watch/node_modules/tiny-lr": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz", - "integrity": "sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "body": "^5.1.0", - "debug": "^3.1.0", - "faye-websocket": "~0.10.0", - "livereload-js": "^2.3.0", - "object-assign": "^4.1.0", - "qs": "^6.4.0" - } - }, - "node_modules/grunt-contrib-watch/node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/grunt-contrib-watch/node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/grunt-contrib-watch/node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/grunt-git-authors": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/grunt-git-authors/-/grunt-git-authors-3.2.0.tgz", - "integrity": "sha512-uDJGPKoO7H93wUnnDPsNIdIrfhifyhQln2tG1ulH25ceFeKuKTrTKYytzTUuxeaspss2t66amQlgaqUS9yo+2w==", - "dev": true, - "dependencies": { - "spawnback": "~1.0.0" - } - }, - "node_modules/grunt-git-authors/node_modules/spawnback": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/spawnback/-/spawnback-1.0.1.tgz", - "integrity": "sha512-340ZqtqJzWAZtHwaCC2gx4mdQOnkUWAWNDp7y0bCEatdjmgQ4j7b0qQ7qO5WIJWx/luNrKcrYzpKbH3NTR030A==", - "dev": true - }, - "node_modules/grunt-mocha-test": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/grunt-mocha-test/-/grunt-mocha-test-0.13.3.tgz", - "integrity": "sha512-zQGEsi3d+ViPPi7/4jcj78afKKAKiAA5n61pknQYi25Ugik+aNOuRmiOkmb8mN2CeG8YxT+YdT1H1Q7B/eNkoQ==", + "node_modules/eslint/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", "dependencies": { - "hooker": "^0.2.3", - "mkdirp": "^0.5.0" - }, - "engines": { - "node": ">= 0.10.4" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "peerDependencies": { - "mocha": ">=1.20.0" - } - }, - "node_modules/grunt-mocha-test/node_modules/hooker": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", - "integrity": "sha512-t+UerCsQviSymAInD01Pw+Dn/usmz1sRO+3Zk1+lx8eg+WKpD2ulcwWqHHL0+aseRBr+3+vIhiG1K1JTwaIcTA==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/grunt-mocha-test/node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "license": "MIT", "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/grunt-mocha-test/node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/grunt/node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true, - "license": "ISC" - }, - "node_modules/grunt/node_modules/ansi-styles": { + "node_modules/eslint/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", @@ -3142,47 +1043,24 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/grunt/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/grunt/node_modules/array-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/grunt/node_modules/array-slice": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "license": "Python-2.0" }, - "node_modules/grunt/node_modules/balanced-match": { + "node_modules/eslint/node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, "license": "MIT" }, - "node_modules/grunt/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -3190,20 +1068,17 @@ "concat-map": "0.0.1" } }, - "node_modules/grunt/node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/eslint/node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/grunt/node_modules/chalk": { + "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", @@ -3220,7 +1095,7 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/grunt/node_modules/color-convert": { + "node_modules/eslint/node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", @@ -3233,366 +1108,266 @@ "node": ">=7.0.0" } }, - "node_modules/grunt/node_modules/color-name": { + "node_modules/eslint/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, "license": "MIT" }, - "node_modules/grunt/node_modules/colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/grunt/node_modules/concat-map": { + "node_modules/eslint/node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, "license": "MIT" }, - "node_modules/grunt/node_modules/dateformat": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", - "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/grunt/node_modules/detect-file": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "node_modules/eslint/node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/grunt/node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=4" - } - }, - "node_modules/grunt/node_modules/eventemitter2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", - "integrity": "sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/grunt/node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" + "node": ">= 8" } }, - "node_modules/grunt/node_modules/expand-tilde": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "node_modules/eslint/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { - "homedir-polyfill": "^1.0.1" + "ms": "^2.1.3" }, "engines": { - "node": ">=0.10.0" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/grunt/node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "node_modules/eslint/node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, - "node_modules/grunt/node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, "engines": { - "node": ">=8" - } - }, - "node_modules/grunt/node_modules/findup-sync": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", - "integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.3", - "micromatch": "^4.0.4", - "resolve-dir": "^1.0.1" + "node": ">=10" }, - "engines": { - "node": ">= 10.13.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/grunt/node_modules/fined": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "node_modules/eslint/node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", - "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">= 0.10" - } - }, - "node_modules/grunt/node_modules/flagged-respawn": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/grunt/node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "engines": { - "node": ">=0.10.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/grunt/node_modules/for-own": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", + "node_modules/eslint/node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, - "license": "MIT", + "license": "BSD-2-Clause", "dependencies": { - "for-in": "^1.0.1" + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/grunt/node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/grunt/node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "license": "MIT", + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://opencollective.com/eslint" } }, - "node_modules/grunt/node_modules/getobject": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/getobject/-/getobject-1.0.2.tgz", - "integrity": "sha512-2zblDBaFcb3rB4rF77XVnuINOE2h2k/OnqXAiy0IrTxUfV1iFp3la33oAQVY9pCpWU268WFYVt2t71hlMuLsOg==", + "node_modules/eslint/node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, "engines": { - "node": ">=10" + "node": ">=0.10" } }, - "node_modules/grunt/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/eslint/node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "ISC", + "license": "BSD-2-Clause", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "estraverse": "^5.2.0" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=4.0" } }, - "node_modules/grunt/node_modules/global-modules": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "license": "MIT", - "dependencies": { - "global-prefix": "^1.0.1", - "is-windows": "^1.0.1", - "resolve-dir": "^1.0.0" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">=0.10.0" + "node": ">=4.0" } }, - "node_modules/grunt/node_modules/global-prefix": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "node_modules/eslint/node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, - "license": "MIT", - "dependencies": { - "expand-tilde": "^2.0.2", - "homedir-polyfill": "^1.0.1", - "ini": "^1.3.4", - "is-windows": "^1.0.1", - "which": "^1.2.14" - }, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, - "node_modules/grunt/node_modules/global-prefix/node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/eslint/node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } + "license": "MIT" }, - "node_modules/grunt/node_modules/grunt-cli": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.4.3.tgz", - "integrity": "sha512-9Dtx/AhVeB4LYzsViCjUQkd0Kw0McN2gYpdmGYKtE2a5Yt7v1Q+HYZVWhqXc/kGnxlMtqKDxSwotiGeFmkrCoQ==", + "node_modules/eslint/node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, - "license": "MIT", - "dependencies": { - "grunt-known-options": "~2.0.0", - "interpret": "~1.1.0", - "liftup": "~3.0.1", - "nopt": "~4.0.1", - "v8flags": "~3.2.0" - }, - "bin": { - "grunt": "bin/grunt" - }, - "engines": { - "node": ">=10" - } + "license": "MIT" }, - "node_modules/grunt/node_modules/grunt-cli/node_modules/nopt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz", - "integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==", + "node_modules/eslint/node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, - "license": "ISC", + "license": "MIT" + }, + "node_modules/eslint/node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", "dependencies": { - "abbrev": "1", - "osenv": "^0.1.4" + "flat-cache": "^4.0.0" }, - "bin": { - "nopt": "bin/nopt.js" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/grunt/node_modules/grunt-known-options": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz", - "integrity": "sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA==", + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/grunt/node_modules/grunt-legacy-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-3.0.0.tgz", - "integrity": "sha512-GHZQzZmhyq0u3hr7aHW4qUH0xDzwp2YXldLPZTCjlOeGscAOWWPftZG3XioW8MasGp+OBRIu39LFx14SLjXRcA==", + "node_modules/eslint/node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { - "colors": "~1.1.2", - "grunt-legacy-log-utils": "~2.1.0", - "hooker": "~0.2.3", - "lodash": "~4.17.19" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": ">= 0.10.0" + "node": ">=16" } }, - "node_modules/grunt/node_modules/grunt-legacy-log-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.1.0.tgz", - "integrity": "sha512-lwquaPXJtKQk0rUM1IQAop5noEpwFqOXasVoedLeNzaibf/OPWjKYvvdqnEHNmU+0T0CaReAXIbGo747ZD+Aaw==", + "node_modules/eslint/node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true, - "license": "MIT", + "license": "ISC" + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", "dependencies": { - "chalk": "~4.1.0", - "lodash": "~4.17.19" + "is-glob": "^4.0.3" }, "engines": { - "node": ">=10" + "node": ">=10.13.0" } }, - "node_modules/grunt/node_modules/grunt-legacy-util": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-2.0.1.tgz", - "integrity": "sha512-2bQiD4fzXqX8rhNdXkAywCadeqiPiay0oQny77wA2F3WF4grPJXCvAcyoWUJV+po/b15glGkxuSiQCK299UC2w==", + "node_modules/eslint/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", - "dependencies": { - "async": "~3.2.0", - "exit": "~0.1.2", - "getobject": "~1.0.0", - "hooker": "~0.2.3", - "lodash": "~4.17.21", - "underscore.string": "~3.3.5", - "which": "~2.0.2" - }, "engines": { - "node": ">=10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/grunt/node_modules/has-flag": { + "node_modules/eslint/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", @@ -3602,2410 +1377,2500 @@ "node": ">=8" } }, - "node_modules/grunt/node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, "engines": { - "node": ">= 0.4" + "node": ">= 4" } }, - "node_modules/grunt/node_modules/homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "node_modules/eslint/node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { - "parse-passwd": "^1.0.0" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/grunt/node_modules/hooker": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", - "integrity": "sha512-t+UerCsQviSymAInD01Pw+Dn/usmz1sRO+3Zk1+lx8eg+WKpD2ulcwWqHHL0+aseRBr+3+vIhiG1K1JTwaIcTA==", + "node_modules/eslint/node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { - "node": "*" + "node": ">=0.8.19" } }, - "node_modules/grunt/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "node_modules/eslint/node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint/node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/grunt/node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "node_modules/eslint/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, - "license": "ISC", + "license": "ISC" + }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", "dependencies": { - "once": "^1.3.0", - "wrappy": "1" + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/grunt/node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "node_modules/eslint/node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/grunt/node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, - "license": "ISC" + "license": "MIT" }, - "node_modules/grunt/node_modules/interpret": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz", - "integrity": "sha512-CLM8SNMDu7C5psFCn6Wg/tgpj/bKAg7hc2gWqcuR9OD5Ft9PhBpIu8PLicPeis+xDd6YX2ncI8MCA64I9tftIA==", + "node_modules/eslint/node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, "license": "MIT" }, - "node_modules/grunt/node_modules/is-absolute": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", + "node_modules/eslint/node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/eslint/node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { - "is-relative": "^1.0.0", - "is-windows": "^1.0.1" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/grunt/node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "p-locate": "^5.0.0" }, "engines": { - "node": ">= 0.4" + "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/grunt/node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "node_modules/eslint/node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, - "license": "MIT", + "license": "MIT" + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/grunt/node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "node_modules/eslint/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { - "is-extglob": "^2.1.1" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/grunt/node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, "engines": { - "node": ">=0.12.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/grunt/node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { - "isobject": "^3.0.1" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/grunt/node_modules/is-relative": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", + "node_modules/eslint/node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { - "is-unc-path": "^1.0.0" + "callsites": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/grunt/node_modules/is-unc-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", + "node_modules/eslint/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", - "dependencies": { - "unc-path-regex": "^0.1.2" - }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/grunt/node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "node_modules/eslint/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/grunt/node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "node_modules/eslint/node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "license": "ISC" + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } }, - "node_modules/grunt/node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "node_modules/eslint/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" + } + }, + "node_modules/eslint/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/grunt/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "node_modules/eslint/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "shebang-regex": "^3.0.0" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">=8" } }, - "node_modules/grunt/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "node_modules/eslint/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/grunt/node_modules/liftup": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/liftup/-/liftup-3.0.1.tgz", - "integrity": "sha512-yRHaiQDizWSzoXk3APcA71eOI/UuhEkNN9DiW2Tt44mhYzX4joFoCZlxsSOF7RyeLlfqzFLQI1ngFq3ggMPhOw==", + "node_modules/eslint/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "license": "MIT", "dependencies": { - "extend": "^3.0.2", - "findup-sync": "^4.0.0", - "fined": "^1.2.0", - "flagged-respawn": "^1.0.1", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.1", - "rechoir": "^0.7.0", - "resolve": "^1.19.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/grunt/node_modules/liftup/node_modules/findup-sync": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz", - "integrity": "sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ==", + "node_modules/eslint/node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { - "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^4.0.2", - "resolve-dir": "^1.0.1" + "prelude-ls": "^1.2.1" }, "engines": { - "node": ">= 8" + "node": ">= 0.8.0" } }, - "node_modules/grunt/node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "node_modules/eslint/node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "license": "MIT" + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } }, - "node_modules/grunt/node_modules/make-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", + "node_modules/eslint/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "kind-of": "^6.0.2" + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" }, "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, - "node_modules/grunt/node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", + "node_modules/eslint/node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/grunt/node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "node_modules/eslint/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, "engines": { - "node": ">=8.6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/grunt/node_modules/minimatch": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", - "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", - "dev": true, - "license": "ISC", + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", "dependencies": { - "brace-expansion": "^1.1.7" + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" }, "engines": { - "node": "*" - } - }, - "node_modules/grunt/node_modules/nopt": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", - "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "1" + "node": ">= 18" }, - "bin": { - "nopt": "bin/nopt.js" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/grunt/node_modules/object.defaults": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", - "dev": true, + "node_modules/express-hbs": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/express-hbs/-/express-hbs-2.5.0.tgz", + "integrity": "sha512-i2O1ZBwKO32KF0MePnkgYHsAAILr9H9Sp5GoGp9JWz/qhsBfTMSq9VF1pN109DHysPX6YO88y7B+f6xnEEF/mg==", "license": "MIT", "dependencies": { - "array-each": "^1.0.1", - "array-slice": "^1.0.0", - "for-own": "^1.0.0", - "isobject": "^3.0.0" + "handlebars": "^4.7.7", + "lodash": "^4.17.21", + "readdirp": "^3.6.0" }, - "engines": { - "node": ">=0.10.0" + "optionalDependencies": { + "js-beautify": "^1.13.11" } }, - "node_modules/grunt/node_modules/object.map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==", - "dev": true, - "license": "MIT", + "node_modules/express-hbs/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "optional": true, "dependencies": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=12" } }, - "node_modules/grunt/node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", - "dev": true, + "node_modules/express-hbs/node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, + "optional": true + }, + "node_modules/express-hbs/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "license": "MIT", + "optional": true, "engines": { - "node": ">=0.10.0" + "node": ">=14" } }, - "node_modules/grunt/node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, + "node_modules/express-hbs/node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", "license": "ISC", - "dependencies": { - "wrappy": "1" + "optional": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/grunt/node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", - "dev": true, + "node_modules/express-hbs/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "license": "MIT", + "optional": true, "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/grunt/node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, + "node_modules/express-hbs/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "license": "MIT", + "optional": true, "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/grunt/node_modules/osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "deprecated": "This package is no longer supported.", - "dev": true, - "license": "ISC", + "node_modules/express-hbs/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT", + "optional": true + }, + "node_modules/express-hbs/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "optional": true, "dependencies": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" + "balanced-match": "^1.0.0" } }, - "node_modules/grunt/node_modules/parse-filepath": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", - "dev": true, + "node_modules/express-hbs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", + "optional": true, "dependencies": { - "is-absolute": "^1.0.0", - "map-cache": "^0.2.0", - "path-root": "^0.1.1" + "color-name": "~1.1.4" }, "engines": { - "node": ">=0.8" + "node": ">=7.0.0" } }, - "node_modules/grunt/node_modules/parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", - "dev": true, + "node_modules/express-hbs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT", - "engines": { - "node": ">=0.10.0" - } + "optional": true }, - "node_modules/grunt/node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, + "node_modules/express-hbs/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "license": "MIT", + "optional": true, "engines": { - "node": ">=0.10.0" + "node": ">=14" } }, - "node_modules/grunt/node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" + "node_modules/express-hbs/node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } }, - "node_modules/grunt/node_modules/path-root": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", - "dev": true, + "node_modules/express-hbs/node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", + "optional": true, "dependencies": { - "path-root-regex": "^0.1.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, - "node_modules/grunt/node_modules/path-root-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", - "dev": true, + "node_modules/express-hbs/node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT", + "optional": true + }, + "node_modules/express-hbs/node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", "license": "MIT", + "optional": true, + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, "engines": { - "node": ">=0.10.0" + "node": ">=14" } }, - "node_modules/grunt/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, + "node_modules/express-hbs/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "license": "MIT", + "optional": true + }, + "node_modules/express-hbs/node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "optional": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, "engines": { - "node": ">=8.6" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/express-hbs/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "license": "ISC", + "optional": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/grunt/node_modules/rechoir": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", - "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", - "dev": true, - "license": "MIT", + "node_modules/express-hbs/node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "license": "ISC", + "optional": true, "dependencies": { - "resolve": "^1.9.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">= 0.10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/grunt/node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, + "node_modules/express-hbs/node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", "license": "MIT", "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" }, "bin": { - "resolve": "bin/resolve" + "handlebars": "bin/handlebars" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/grunt/node_modules/resolve-dir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "expand-tilde": "^2.0.0", - "global-modules": "^1.0.0" + "node": ">=0.4.7" }, - "engines": { - "node": ">=0.10.0" + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "node_modules/grunt/node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/grunt/node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" + "node_modules/express-hbs/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC", + "optional": true }, - "node_modules/grunt/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/express-hbs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, + "optional": true, "engines": { "node": ">=8" } }, - "node_modules/grunt/node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" + "node_modules/express-hbs/node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC", + "optional": true + }, + "node_modules/express-hbs/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "license": "BlueOak-1.0.0", + "optional": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/grunt/node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, + "node_modules/express-hbs/node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", "license": "MIT", + "optional": true, "dependencies": { - "is-number": "^7.0.0" + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" }, "engines": { - "node": ">=8.0" + "node": ">=14" } }, - "node_modules/grunt/node_modules/unc-path-regex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", - "integrity": "sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg==", - "dev": true, + "node_modules/express-hbs/node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", "license": "MIT", + "optional": true, "engines": { - "node": ">=0.10.0" + "node": ">=14" } }, - "node_modules/grunt/node_modules/underscore.string": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.6.tgz", - "integrity": "sha512-VoC83HWXmCrF6rgkyxS9GHv8W9Q5nhMKho+OadDJGzL2oDYbYEppBaCMH6pFlwLeqj2QS+hhkw2kpXkSdD1JxQ==", - "dev": true, - "license": "MIT", + "node_modules/express-hbs/node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/express-hbs/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC", + "optional": true + }, + "node_modules/express-hbs/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "license": "ISC", + "optional": true, "dependencies": { - "sprintf-js": "^1.1.1", - "util-deprecate": "^1.0.2" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/grunt/node_modules/underscore.string/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/grunt/node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/grunt/node_modules/v8flags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", - "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", - "dev": true, + "node_modules/express-hbs/node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "license": "MIT", - "dependencies": { - "homedir-polyfill": "^1.0.1" - }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/express-hbs/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "optional": true, "engines": { - "node": ">= 0.10" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/grunt/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, + "node_modules/express-hbs/node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "license": "MIT" + }, + "node_modules/express-hbs/node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", "license": "ISC", + "optional": true, "dependencies": { - "isexe": "^2.0.0" + "abbrev": "^2.0.0" }, "bin": { - "node-which": "bin/node-which" + "nopt": "bin/nopt.js" }, "engines": { - "node": ">= 8" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/grunt/node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" + "node_modules/express-hbs/node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0", + "optional": true }, - "node_modules/jsdom": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", - "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", + "node_modules/express-hbs/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/express-hbs/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "license": "BlueOak-1.0.0", + "optional": true, "dependencies": { - "cssstyle": "^4.2.1", - "data-urls": "^5.0.0", - "decimal.js": "^10.5.0", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.6", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.16", - "parse5": "^7.2.1", - "rrweb-cssom": "^0.8.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^5.1.1", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.1.1", - "ws": "^8.18.0", - "xml-name-validator": "^5.0.0" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=18" + "node": ">=16 || 14 >=14.18" }, - "peerDependencies": { - "canvas": "^3.0.0" + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/express-hbs/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/jsdom/node_modules/@asamuzakjp/css-color": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.1.7.tgz", - "integrity": "sha512-Ok5fYhtwdyJQmU1PpEv6Si7Y+A4cYb8yNM9oiIJC9TzXPMuN9fvdonKJqcnz9TbFqV6bQ8z0giRq0iaOpGZV2g==", + "node_modules/express-hbs/node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "license": "ISC", + "optional": true + }, + "node_modules/express-hbs/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "license": "MIT", "dependencies": { - "@csstools/css-calc": "^2.1.3", - "@csstools/css-color-parser": "^3.0.9", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "lru-cache": "^10.4.3" - } - }, - "node_modules/jsdom/node_modules/@csstools/color-helpers": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", - "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", + "picomatch": "^2.2.1" + }, "engines": { - "node": ">=18" + "node": ">=8.10.0" } }, - "node_modules/jsdom/node_modules/@csstools/css-calc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.3.tgz", - "integrity": "sha512-XBG3talrhid44BY1x3MHzUx/aTG8+x/Zi57M4aTKK9RFB4aLlF3TTSzfzn8nWVHWL3FgAXAxmupmDd6VWww+pw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" + "node_modules/express-hbs/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "engines": { + "node": ">=10" } }, - "node_modules/jsdom/node_modules/@csstools/css-color-parser": { - "version": "3.0.9", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.9.tgz", - "integrity": "sha512-wILs5Zk7BU86UArYBJTPy/FMPPKVKHMj1ycCEyf3VUptol0JNRLFU/BZsJ4aiIHJEbSLiizzRrw8Pc1uAEDrXw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], + "node_modules/express-hbs/node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", + "optional": true, "dependencies": { - "@csstools/color-helpers": "^5.0.2", - "@csstools/css-calc": "^2.1.3" + "shebang-regex": "^3.0.0" }, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3" + "node": ">=8" } }, - "node_modules/jsdom/node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", - "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], + "node_modules/express-hbs/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "license": "MIT", + "optional": true, "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.3" + "node": ">=8" } }, - "node_modules/jsdom/node_modules/@csstools/css-tokenizer": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", - "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", + "node_modules/express-hbs/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "optional": true, "engines": { - "node": ">=18" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/jsdom/node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "license": "MIT", + "node_modules/express-hbs/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", "engines": { - "node": ">= 14" + "node": ">=0.10.0" } }, - "node_modules/jsdom/node_modules/cssstyle": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.3.1.tgz", - "integrity": "sha512-ZgW+Jgdd7i52AaLYCriF8Mxqft0gD/R9i9wi6RWBhs1pqdPEzPjym7rvRKi397WmQFf3SlyUsszhw+VVCbx79Q==", + "node_modules/express-hbs/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "license": "MIT", + "optional": true, "dependencies": { - "@asamuzakjp/css-color": "^3.1.2", - "rrweb-cssom": "^0.8.0" + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" }, "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/jsdom/node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "node_modules/express-hbs/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", + "optional": true, "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/jsdom/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/express-hbs/node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, + "optional": true, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=8" } }, - "node_modules/jsdom/node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", - "license": "MIT" + "node_modules/express-hbs/node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT", + "optional": true }, - "node_modules/jsdom/node_modules/entities": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", - "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" + "node_modules/express-hbs/node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "engines": { + "node": ">=8" } }, - "node_modules/jsdom/node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "node_modules/express-hbs/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "license": "MIT", + "optional": true, "dependencies": { - "whatwg-encoding": "^3.1.1" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/jsdom/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "node_modules/express-hbs/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", + "optional": true, "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">= 14" + "node": ">=8" } }, - "node_modules/jsdom/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "node_modules/express-hbs/node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/express-hbs/node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" }, "engines": { - "node": ">= 14" + "node": ">=0.8.0" } }, - "node_modules/jsdom/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", + "node_modules/express-hbs/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "optional": true, "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" }, "engines": { - "node": ">=0.10.0" + "node": ">= 8" } }, - "node_modules/jsdom/node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "license": "MIT" - }, - "node_modules/jsdom/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/jsdom/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/jsdom/node_modules/nwsapi": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", - "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", + "node_modules/express-hbs/node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", "license": "MIT" }, - "node_modules/jsdom/node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "node_modules/express-hbs/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "license": "MIT", + "optional": true, "dependencies": { - "entities": "^6.0.0" + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" }, "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/jsdom/node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "node_modules/express-hbs/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/jsdom/node_modules/rrweb-cssom": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", - "license": "MIT" - }, - "node_modules/jsdom/node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/jsdom/node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "license": "ISC", + "optional": true, "dependencies": { - "xmlchars": "^2.2.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=v12.22.7" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/jsdom/node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "license": "MIT" + "node_modules/express-hbs/node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } }, - "node_modules/jsdom/node_modules/tldts": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", - "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "node_modules/express-hbs/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", + "optional": true, "dependencies": { - "tldts-core": "^6.1.86" + "color-convert": "^2.0.1" }, - "bin": { - "tldts": "bin/cli.js" + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/jsdom/node_modules/tldts-core": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", - "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", - "license": "MIT" + "node_modules/express-hbs/node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT", + "optional": true }, - "node_modules/jsdom/node_modules/tough-cookie": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", - "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", - "license": "BSD-3-Clause", + "node_modules/express-hbs/node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "optional": true, "dependencies": { - "tldts": "^6.1.32" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=16" + "node": ">=8" } }, - "node_modules/jsdom/node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "node_modules/express-hbs/node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", + "optional": true, "dependencies": { - "punycode": "^2.3.1" + "ansi-regex": "^5.0.1" }, "engines": { - "node": ">=18" + "node": ">=8" } }, - "node_modules/jsdom/node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "node_modules/express/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "dependencies": { - "xml-name-validator": "^5.0.0" + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" }, "engines": { - "node": ">=18" - } - }, - "node_modules/jsdom/node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" + "node": ">= 0.6" } }, - "node_modules/jsdom/node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "node_modules/express/node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { - "iconv-lite": "0.6.3" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" }, "engines": { - "node": ">=18" + "node": ">= 0.4" } }, - "node_modules/jsdom/node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "node_modules/express/node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, "engines": { - "node": ">=18" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jsdom/node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "node_modules/express/node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", "license": "MIT", "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" + "safe-buffer": "5.2.1" }, "engines": { - "node": ">=18" + "node": ">= 0.6" } }, - "node_modules/jsdom/node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", + "node_modules/express/node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "node": ">= 0.6" } }, - "node_modules/jsdom/node_modules/xml-name-validator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "license": "Apache-2.0", + "node_modules/express/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", "engines": { - "node": ">=18" + "node": ">= 0.6" } }, - "node_modules/jsdom/node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "license": "MIT" + "node_modules/express/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } }, - "node_modules/method-override": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", - "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", + "node_modules/express/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { - "debug": "3.1.0", - "methods": "~1.1.2", - "parseurl": "~1.3.2", - "vary": "~1.1.2" + "ms": "^2.1.3" }, "engines": { - "node": ">= 0.10" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/method-override/node_modules/debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "node_modules/express/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", - "dependencies": { - "ms": "2.0.0" + "engines": { + "node": ">= 0.8" } }, - "node_modules/method-override/node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "node_modules/express/node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, "engines": { - "node": ">= 0.6" + "node": ">= 0.4" } }, - "node_modules/method-override/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "node_modules/express/node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, - "node_modules/method-override/node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "node_modules/express/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/method-override/node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "node_modules/express/node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { - "node": ">= 0.8" + "node": ">= 0.4" } }, - "node_modules/minisearch": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.1.2.tgz", - "integrity": "sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA==", - "license": "MIT" - }, - "node_modules/mocha": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.2.2.tgz", - "integrity": "sha512-VlSBxrPYHK4YNOEbFdkCxHQbZMoNzBkoPprqtZRW6311EUF/DlSxoycE2e/2NtRk4WKkIXzyrXDTrlikJMWgbw==", - "dev": true, + "node_modules/express/node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", - "dependencies": { - "browser-stdout": "^1.3.1", - "chokidar": "^4.0.1", - "debug": "^4.3.5", - "diff": "^5.2.0", - "escape-string-regexp": "^4.0.0", - "find-up": "^5.0.0", - "glob": "^10.4.5", - "he": "^1.2.0", - "js-yaml": "^4.1.0", - "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "serialize-javascript": "^6.0.2", - "strip-json-comments": "^3.1.1", - "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^17.7.2", - "yargs-parser": "^21.1.1", - "yargs-unparser": "^2.0.0" - }, - "bin": { - "_mocha": "bin/_mocha", - "mocha": "bin/mocha.js" - }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": ">= 0.4" } }, - "node_modules/mocha/node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", + "node_modules/express/node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "es-errors": "^1.3.0" }, "engines": { - "node": ">=12" + "node": ">= 0.4" } }, - "node_modules/mocha/node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } + "node_modules/express/node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" }, - "node_modules/mocha/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, + "node_modules/express/node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">= 0.6" } }, - "node_modules/mocha/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, + "node_modules/express/node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": ">= 0.8" } }, - "node_modules/mocha/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/mocha/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, + "node_modules/express/node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" + "engines": { + "node": ">= 0.6" } }, - "node_modules/mocha/node_modules/browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true, - "license": "ISC" - }, - "node_modules/mocha/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, + "node_modules/express/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", "engines": { - "node": ">=10" - }, + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, + "node_modules/express/node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, + "node_modules/express/node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/mocha/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, + "node_modules/express/node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, "engines": { - "node": ">= 14.16.0" + "node": ">= 0.4" }, "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/mocha/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, + "node_modules/express/node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/mocha/node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "node_modules/express/node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "function-bind": "^1.1.2" }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, - "node_modules/mocha/node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/express/node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/mocha/node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, + "node_modules/express/node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">= 0.8" } }, - "node_modules/mocha/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "node_modules/express/node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/express/node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, "engines": { - "node": ">=7.0.0" + "node": ">= 0.10" } }, - "node_modules/mocha/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, + "node_modules/express/node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, - "node_modules/mocha/node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, + "node_modules/express/node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, "engines": { - "node": ">= 8" + "node": ">= 0.4" } }, - "node_modules/mocha/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, + "node_modules/express/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 0.8" } }, - "node_modules/mocha/node_modules/decamelize": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", - "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true, + "node_modules/express/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/express/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", "engines": { - "node": ">=0.3.1" + "node": ">= 0.6" } }, - "node_modules/mocha/node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" + "node_modules/express/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } }, - "node_modules/mocha/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, + "node_modules/express/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, - "node_modules/mocha/node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, + "node_modules/express/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", "engines": { - "node": ">=6" + "node": ">= 0.6" } }, - "node_modules/mocha/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, + "node_modules/express/node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, + "node_modules/express/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "ee-first": "1.1.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mocha/node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" + "node": ">= 0.8" } }, - "node_modules/mocha/node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, + "node_modules/express/node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, + "wrappy": "1" + } + }, + "node_modules/express/node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 0.8" } }, - "node_modules/mocha/node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", + "node_modules/express/node_modules/path-to-regexp": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", + "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "license": "MIT", "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=16" } }, - "node_modules/mocha/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", + "node_modules/express/node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">= 0.10" } }, - "node_modules/mocha/node_modules/glob/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", + "node_modules/express/node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", "dependencies": { - "brace-expansion": "^2.0.1" + "side-channel": "^1.1.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=0.6" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, + "node_modules/express/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", "engines": { - "node": ">=8" - } - }, - "node_modules/mocha/node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "license": "MIT", - "bin": { - "he": "bin/he" + "node": ">= 0.6" } }, - "node_modules/mocha/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, + "node_modules/express/node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, "engines": { - "node": ">=8" + "node": ">= 18" } }, - "node_modules/mocha/node_modules/is-plain-obj": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", - "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", - "dev": true, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/express/node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, "engines": { - "node": ">=8" + "node": ">= 18" } }, - "node_modules/mocha/node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, + "node_modules/express/node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", "license": "MIT", - "engines": { - "node": ">=10" + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">= 18" } }, - "node_modules/mocha/node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, + "node_modules/express/node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, - "node_modules/mocha/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, + "node_modules/express/node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", "dependencies": { - "argparse": "^2.0.1" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, + "node_modules/express/node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, + "node_modules/express/node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" }, "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", + "node_modules/express/node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.1" + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { - "node": ">=10" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mocha/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", + "node_modules/express/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">= 0.8" } }, - "node_modules/mocha/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/mocha/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, + "node_modules/express/node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=0.6" } }, - "node_modules/mocha/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, + "node_modules/express/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.6" } }, - "node_modules/mocha/node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/mocha/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, + "node_modules/express/node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.8" } }, - "node_modules/mocha/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", + "node_modules/express/node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/flates": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/flates/-/flates-0.0.5.tgz", + "integrity": "sha512-KSD99qlu4M92d3EAwqY9k8HiLu7gOdo+Zk5vWrY1kzo05o1sacAGJ5E7rwomTMuuSGko5K6cXHfF/sHb2IGSxw==", "engines": { - "node": ">=8" + "node": ">0.6" } }, - "node_modules/mocha/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "node_modules/globals": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.2.0.tgz", + "integrity": "sha512-O+7l9tPdHCU320IigZZPj5zmRCFG9xHmx9cU8FqU2Rp+JN714seHV+2S9+JslCpY4gJwU2vOGox0wzgae/MCEg==", "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, + "license": "MIT", "engines": { - "node": ">=16 || 14 >=14.18" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mocha/node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/mocha/node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "node_modules/grunt-git-authors": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/grunt-git-authors/-/grunt-git-authors-3.2.0.tgz", + "integrity": "sha512-uDJGPKoO7H93wUnnDPsNIdIrfhifyhQln2tG1ulH25ceFeKuKTrTKYytzTUuxeaspss2t66amQlgaqUS9yo+2w==", "dev": true, - "license": "MIT", "dependencies": { - "safe-buffer": "^5.1.0" + "spawnback": "~1.0.0" } }, - "node_modules/mocha/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, + "node_modules/grunt-git-authors/node_modules/spawnback": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/spawnback/-/spawnback-1.0.1.tgz", + "integrity": "sha512-340ZqtqJzWAZtHwaCC2gx4mdQOnkUWAWNDp7y0bCEatdjmgQ4j7b0qQ7qO5WIJWx/luNrKcrYzpKbH3NTR030A==", + "dev": true + }, + "node_modules/jsdom": { + "version": "26.1.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", + "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", "license": "MIT", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.5.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.1.1", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.1", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, "engines": { - "node": ">= 14.18.0" + "node": ">=18" }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, - "node_modules/mocha/node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, + "node_modules/jsdom/node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", "license": "MIT", - "engines": { - "node": ">=0.10.0" + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" } }, - "node_modules/mocha/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, + "node_modules/jsdom/node_modules/@csstools/color-helpers": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz", + "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==", "funding": [ { "type": "github", - "url": "https://github.com/sponsors/feross" + "url": "https://github.com/sponsors/csstools" }, { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" }, { - "type": "consulting", - "url": "https://feross.org/support" + "type": "opencollective", + "url": "https://opencollective.com/csstools" } ], - "license": "MIT" - }, - "node_modules/mocha/node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, - "node_modules/mocha/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, + "node_modules/jsdom/node_modules/@csstools/css-color-parser": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz", + "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "@csstools/color-helpers": "^5.0.2", + "@csstools/css-calc": "^2.1.4" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" } }, - "node_modules/mocha/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, + "node_modules/jsdom/node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], "license": "MIT", "engines": { - "node": ">=8" + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" } }, - "node_modules/mocha/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", + "node_modules/jsdom/node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18" } }, - "node_modules/mocha/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, + "node_modules/jsdom/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 14" } }, - "node_modules/mocha/node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "node_modules/jsdom/node_modules/cssstyle": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.4.0.tgz", + "integrity": "sha512-W0Y2HOXlPkb2yaKrCVRjinYKciu/qSLEmK0K9mcfDei3zwlnHFEHAs/Du3cIRwPqY+J4JsiBzUjoHyc8RsJ03A==", + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/mocha/node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, + "node_modules/jsdom/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, "engines": { - "node": ">=8" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/mocha/node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, + "node_modules/jsdom/node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", "license": "MIT" }, - "node_modules/mocha/node_modules/string-width-cjs/node_modules/strip-ansi": { + "node_modules/jsdom/node_modules/entities": { "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/jsdom/node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "whatwg-encoding": "^3.1.1" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/mocha/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, + "node_modules/jsdom/node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "license": "MIT", "dependencies": { - "ansi-regex": "^6.0.1" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "node": ">= 14" } }, - "node_modules/mocha/node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/jsdom/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "agent-base": "^7.1.2", + "debug": "4" }, "engines": { - "node": ">=8" + "node": ">= 14" } }, - "node_modules/mocha/node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, + "node_modules/jsdom/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/mocha/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, + "node_modules/jsdom/node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "license": "MIT" + }, + "node_modules/jsdom/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/jsdom/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/jsdom/node_modules/nwsapi": { + "version": "2.2.20", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", + "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", + "license": "MIT" + }, + "node_modules/jsdom/node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", "license": "MIT", - "engines": { - "node": ">=8" + "dependencies": { + "entities": "^6.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, + "node_modules/jsdom/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">=6" } }, - "node_modules/mocha/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, + "node_modules/jsdom/node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "license": "MIT" + }, + "node_modules/jsdom/node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/jsdom/node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "license": "ISC", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "xmlchars": "^2.2.0" }, "engines": { - "node": ">= 8" + "node": ">=v12.22.7" } }, - "node_modules/mocha/node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", - "dev": true, - "license": "Apache-2.0" + "node_modules/jsdom/node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "license": "MIT" }, - "node_modules/mocha/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, + "node_modules/jsdom/node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", "license": "MIT", "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" + "tldts-core": "^6.1.86" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "bin": { + "tldts": "bin/cli.js" } }, - "node_modules/mocha/node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", + "node_modules/jsdom/node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "license": "MIT" + }, + "node_modules/jsdom/node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "license": "BSD-3-Clause", "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" + "tldts": "^6.1.32" }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/mocha/node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", "engines": { - "node": ">=8" + "node": ">=16" } }, - "node_modules/mocha/node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/mocha/node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "node_modules/jsdom/node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "dependencies": { + "punycode": "^2.3.1" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/mocha/node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/jsdom/node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" + "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/mocha/node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", "engines": { "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/mocha/node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" } }, - "node_modules/mocha/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, + "node_modules/jsdom/node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", "license": "MIT", "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" + "iconv-lite": "0.6.3" }, "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", + "node_modules/jsdom/node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/mocha/node_modules/yargs-unparser": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", - "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", - "dev": true, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "license": "MIT", "dependencies": { - "camelcase": "^6.0.0", - "decamelize": "^4.0.0", - "flat": "^5.0.2", - "is-plain-obj": "^2.1.0" + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/mocha/node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, + "node_modules/jsdom/node_modules/ws": { + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } } }, - "node_modules/mocha/node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, + "node_modules/jsdom/node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "license": "MIT" }, - "node_modules/mocha/node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, + "node_modules/method-override": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", + "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", "license": "MIT", "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "debug": "3.1.0", + "methods": "~1.1.2", + "parseurl": "~1.3.2", + "vary": "~1.1.2" }, "engines": { - "node": ">=8" + "node": ">= 0.10" } }, - "node_modules/mocha/node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, + "node_modules/method-override/node_modules/debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "license": "MIT", "dependencies": { - "ansi-regex": "^5.0.1" - }, + "ms": "2.0.0" + } + }, + "node_modules/method-override/node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">= 0.6" } }, - "node_modules/mocha/node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, + "node_modules/method-override/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/method-override/node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.8" + } + }, + "node_modules/method-override/node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" } }, + "node_modules/minisearch": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.1.2.tgz", + "integrity": "sha512-R1Pd9eF+MD5JYDDSPAp/q1ougKglm14uEkPMvQ/05RGmx6G9wvmLTrTI/Q5iPNJLYqNdsDQ7qTGIcNWR+FrHmA==", + "license": "MIT" + }, "node_modules/morgan": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", @@ -6091,166 +3956,31 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT" }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, "license": "MIT", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" + "bin": { + "prettier": "bin/prettier.cjs" }, "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + "node": ">=14" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/node-fetch/node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/node-fetch/node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/node-fetch/node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "license": "MIT", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/node-fetch/node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch/node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/should": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/should/-/should-13.2.3.tgz", - "integrity": "sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "should-equal": "^2.0.0", - "should-format": "^3.0.3", - "should-type": "^1.4.0", - "should-type-adaptors": "^1.0.1", - "should-util": "^1.0.0" - } - }, - "node_modules/should/node_modules/should-equal": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz", - "integrity": "sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "should-type": "^1.4.0" - } - }, - "node_modules/should/node_modules/should-format": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz", - "integrity": "sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "should-type": "^1.3.0", - "should-type-adaptors": "^1.0.1" - } - }, - "node_modules/should/node_modules/should-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz", - "integrity": "sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/should/node_modules/should-type-adaptors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz", - "integrity": "sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "should-type": "^1.3.0", - "should-util": "^1.0.0" + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/should/node_modules/should-util": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz", - "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", - "dev": true, - "license": "MIT" - }, "node_modules/supertest": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.0.tgz", - "integrity": "sha512-5QeSO8hSrKghtcWEoPiO036fxH0Ii2wVQfFZSP0oqQhmjk8bOLhDFXr4JrvaFmPuEWUoq4znY3uSi8UzLKxGqw==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.1.tgz", + "integrity": "sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==", "dev": true, "license": "MIT", "dependencies": { "methods": "^1.1.2", - "superagent": "^9.0.1" + "superagent": "^10.2.1" }, "engines": { "node": ">=14.18.0" @@ -6355,9 +4085,9 @@ "license": "MIT" }, "node_modules/supertest/node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6465,15 +4195,16 @@ "license": "MIT" }, "node_modules/supertest/node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -6781,9 +4512,9 @@ } }, "node_modules/supertest/node_modules/superagent": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-9.0.2.tgz", - "integrity": "sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==", + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.1.tgz", + "integrity": "sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==", "dev": true, "license": "MIT", "dependencies": { @@ -6792,7 +4523,7 @@ "debug": "^4.3.4", "fast-safe-stringify": "^2.1.1", "form-data": "^4.0.0", - "formidable": "^3.5.1", + "formidable": "^3.5.4", "methods": "^1.1.2", "mime": "2.6.0", "qs": "^6.11.0" @@ -6809,9 +4540,9 @@ "license": "ISC" }, "node_modules/wiki-client": { - "version": "0.31.0", - "resolved": "https://registry.npmjs.org/wiki-client/-/wiki-client-0.31.0.tgz", - "integrity": "sha512-zwRcS5rLyVypiGfrHPnKhh0ngcmetaqNlySLt9nYNtRnMkMAKD3M/j4jteBVHNooqK5kJBmCRGPKWkBgcFuzlA==", + "version": "0.31.2", + "resolved": "https://registry.npmjs.org/wiki-client/-/wiki-client-0.31.2.tgz", + "integrity": "sha512-dwHpfarpn25xlK86jNrXMqZJ5GrdGR4T6xL+6PPagx0+Wynf7P51ZXvJolvx4oA5y0JLpiXI7SBnX6iVEztKtg==", "dev": true, "license": "MIT", "dependencies": { diff --git a/package.json b/package.json index 1b64c07e..24b995b6 100644 --- a/package.json +++ b/package.json @@ -8,57 +8,63 @@ "url": "http://ward.fed.wiki.org" }, "contributors": [ - { - "name": "Nick Niemeir", - "email": "nick.niemeir@gmail.com", - "url": "http://nrn.io" - }, - { - "name": "Paul Rodwell", - "email": "paul.rodwell@btinternet.com", - "url": "http://wiki-paul90.rhcloud.com" - } + "Austin King ", + "Paul Rodwell ", + "Nicholas Hallahan ", + "Tom Lee ", + "Erkan Yilmaz ", + "Patrick Mueller ", + "Nick Niemeir ", + "Ward Cunningham ", + "Christian Smith ", + "Gui13 ", + "Merlyn Albery-Speyer ", + "Marcin Cieslak ", + "enyst ", + "Peter deHaan ", + "winckell benjamin ", + "Eric Dobbs ", + "Joshua Benuck ", + "Tom Lieber ", + "Andrew Shell ", + "jon r " ], "dependencies": { - "async": "^3.2.4", "body-parser": "^2.2.0", "client-sessions": "^0.8.0", "coffeescript": "^2.5.0", "cookie-parser": "^1.4.4", "dompurify": "^3.1.0", "errorhandler": "^1.5.1", - "express": "^4.17.1", + "express": "^5.1.0", "express-hbs": "^2.5.0", "flates": "0.0.5", - "glob": "^7.2.3", "jsdom": "^26.0.0", "method-override": "^3.0.0", "minisearch": "^7.1.0", "morgan": "^1.9.1", - "node-fetch": "^3.3.2", "write-file-atomic": "^6.0.0", "xml2js": "^0.6.2" }, "scripts": { - "test": "grunt mochaTest" + "prettier:format": "prettier --write './**/*.js'", + "prettier:check": "prettier --check ./**/*.js", + "test": "node --test", + "update-authors": "node scripts/update-authors.js" }, "devDependencies": { - "grunt": "^1.6.1", - "grunt-contrib-watch": "^1.1.0", + "@eslint/js": "^9.27.0", + "eslint": "^9.27.0", + "globals": "^16.1.0", "grunt-git-authors": "^3.2.0", - "grunt-mocha-test": "^0.13.3", - "mocha": "^11.1.0", - "should": "^13.2.3", + "prettier": "^3.5.3", "supertest": "^7.0.0", - "wiki-client": "^0.31", + "wiki-client": "^0.31.1", "wiki-plugin-activity": "0.7", "wiki-plugin-video": "^0.4" }, "engines": { - "node": ">=18.x" - }, - "testling": { - "harness": "mocha" + "node": ">=20.x" }, "license": "MIT", "repository": { diff --git a/scripts/update-authors.js b/scripts/update-authors.js new file mode 100644 index 00000000..f1e8663c --- /dev/null +++ b/scripts/update-authors.js @@ -0,0 +1,32 @@ +const gitAuthors = require('grunt-git-authors') + +// list of contributers from prior the split out of Smallest Federated Wiki repo. +const priorAuthors = [ + 'Ward Cunningham ', + 'Nick Niemeir ', + 'Patrick Mueller ', + 'Erkan Yilmaz ', + 'Tom Lee ', + 'Nicholas Hallahan ', + 'Paul Rodwell ', + 'Austin King ', +] + +gitAuthors.updatePackageJson({ priorAuthors: priorAuthors, order: 'date' }, error => { + if (error) { + console.log('Error: ', error) + } +}) + +gitAuthors.updateAuthors( + { + priorAuthors: priorAuthors.reverse(), + }, + (error, filename) => { + if (error) { + console.log('Error: ', error) + } else { + console.log(filename, 'updated') + } + }, +) diff --git a/test/defaultargs.coffee b/test/defaultargs.coffee deleted file mode 100644 index d975e051..00000000 --- a/test/defaultargs.coffee +++ /dev/null @@ -1,10 +0,0 @@ -defaultargs = require '../lib/defaultargs' - -describe 'defaultargs', -> - describe '#defaultargs()', -> - it 'should not write over give args', -> - defaultargs({port: 1234}).port.should.equal(1234) - it 'should write non give args', -> - defaultargs().port.should.equal(3000) - it 'should modify dependant args', -> - defaultargs({data: '/tmp/asdf/'}).db.should.equal('/tmp/asdf/pages') diff --git a/test/defaultargs.js b/test/defaultargs.js new file mode 100644 index 00000000..00ae570c --- /dev/null +++ b/test/defaultargs.js @@ -0,0 +1,18 @@ +const { describe, it } = require('node:test') +const assert = require('node:assert/strict') + +const defaultargs = require('../lib/defaultargs') + +describe('defaultargs', () => { + describe('#defaultargs()', () => { + it('should not write over give args', () => { + assert.equal(defaultargs({ port: 1234 }).port, 1234) + }) + it('should write non give args', () => { + assert.equal(defaultargs().port, 3000) + }) + it('should modify dependant args', () => { + assert.equal(defaultargs({ data: '/tmp/asdf/' }).db, '/tmp/asdf/pages') + }) + }) +}) diff --git a/test/mocha.opts b/test/mocha.opts deleted file mode 100644 index 4e3e2af1..00000000 --- a/test/mocha.opts +++ /dev/null @@ -1,2 +0,0 @@ ---require should ---compilers coffee:coffeescript/register diff --git a/test/package.json b/test/package.json new file mode 100644 index 00000000..c6dcf84e --- /dev/null +++ b/test/package.json @@ -0,0 +1,8 @@ +{ + "comment": "This exists so that the tests have a package to read", + "name": "wiki-server", + "dependencies": { + "wiki-plugin-activity": "0.1", + "wiki-plugin-video": "0.1" + } +} diff --git a/test/page.coffee b/test/page.coffee deleted file mode 100644 index c9ffc68f..00000000 --- a/test/page.coffee +++ /dev/null @@ -1,61 +0,0 @@ -path = require('path') -random = require('../lib/random_id') -testid = random() -argv = require('../lib/defaultargs.coffee')({data: path.join('/tmp', 'sfwtests', testid), root: path.join(__dirname, '..'), packageDir: path.join(__dirname, '..', 'node_modules'), security_legacy: true}) -page = require('../lib/page.coffee')(argv) -fs = require('fs') - -testpage = {title: 'Asdf'} - -describe 'page', -> - describe '#page.put()', -> - it 'should save a page', (done) -> - page.put('asdf', testpage, (e) -> - done(e) - ) - describe '#page.get()', -> - it 'should get a page if it exists', (done) -> - page.get('asdf', (e, got) -> - if e then throw e - got.title.should.equal 'Asdf' - done() - ) - it 'should copy a page from default if nonexistant in db', (done) -> - page.get('welcome-visitors', (e, got) -> - if e then throw e - got.title.should.equal 'Welcome Visitors' - done() - ) - # note: here we assume the wiki-plugin-activity repo has been cloned into an adjacent directory - it 'should copy a page from plugins if nonexistant in db', (done) -> - page.get('recent-changes', (e, got) -> - if e then throw e - got.title.should.equal 'Recent Changes' - done() - ) - # note: here we assume the wiki-plugin-activity repo has been cloned into an adjacent directory - it 'should mark a page from plugins with the plugin name', (done) -> - page.get('recent-changes', (e, got) -> - if e then throw e - got.plugin.should.equal 'activity' - done() - ) - it 'should create a page if it exists nowhere', (done) -> - page.get(random(), (e, got) -> - if e then throw e - got.should.equal('Page not found') - done() - ) - it 'should eventually write the page to disk', (done) -> - test = -> - fs.readFile(path.join(argv.db, 'asdf'), (err, data) -> - if err then throw err - readPage = JSON.parse(data) - page.get('asdf', (e, got) -> - readPage.title.should.equal got.title - done() - ) - ) - if page.isWorking() - page.on('finished', -> test()) - else test() diff --git a/test/page.js b/test/page.js new file mode 100644 index 00000000..ebca772b --- /dev/null +++ b/test/page.js @@ -0,0 +1,90 @@ +const { describe, it } = require('node:test') +const assert = require('node:assert/strict') + +const path = require('node:path') +const random = require('../lib/random_id') +const testid = random() +const argv = require('../lib/defaultargs')({ + data: path.join('/tmp', 'sfwtests', testid), + root: path.join(__dirname, '..'), + packageDir: path.join(__dirname, '..', 'node_modules'), + security_legacy: true, +}) +const page = require('../lib/page')(argv) +const fs = require('node:fs') + +const testpage = { title: 'Asdf' } + +console.log('testid', testid) + +describe('page', () => { + describe('#page.put()', () => { + it('should save a page', async () => { + return new Promise(resolve => { + page.put('asdf', testpage, e => { + if (e) throw e + resolve() + }) + }) + }) + }) + describe('#page.get()', () => { + it('should get a page if it exists', async () => { + return new Promise(resolve => { + page.get('asdf', (e, got) => { + if (e) throw e + assert.equal(got.title, 'Asdf') + resolve() + }) + }) + }) + it('should copy a page from default if nonexistant in db', async () => { + return new Promise(resolve => { + page.get('welcome-visitors', (e, got) => { + if (e) throw e + assert.equal(got.title, 'Welcome Visitors') + resolve() + }) + }) + }) + // note: here we assume the wiki-plugin-activity repo has been cloned into an adjacent directory + it('should copy a page from plugins if nonexistant in db', async () => { + return new Promise(resolve => { + page.get('recent-changes', (e, got) => { + if (e) throw e + assert.equal(got.title, 'Recent Changes') + resolve() + }) + }) + }) + // note: here we assume the wiki-plugin-activity repo has been cloned into an adjacent directory + it('should mark a page from plugins with the plugin name', async () => { + return new Promise(resolve => { + page.get('recent-changes', (e, got) => { + if (e) throw e + assert.equal(got.plugin, 'activity') + resolve() + }) + }) + }) + it('should create a page if it exists nowhere', async () => { + return new Promise(resolve => { + page.get(random(), (e, got) => { + if (e) throw e + assert.equal(got, 'Page not found') + resolve() + }) + }) + }) + it('should eventually write the page to disk', async () => { + return new Promise(resolve => { + page.get('asdf', (e, got) => { + if (e) throw e + const page = JSON.parse(fs.readFileSync(path.join(path.sep, 'tmp', 'sfwtests', testid, 'pages', 'asdf'))) + assert.equal(got.title, page.title) + resolve() + }) + }) + }) + }) +}) diff --git a/test/random.coffee b/test/random.coffee deleted file mode 100644 index a7dc5700..00000000 --- a/test/random.coffee +++ /dev/null @@ -1,8 +0,0 @@ -random = require '../lib/random_id' - -describe 'random', -> - describe '#random_id', -> - it 'should not be the same twice', -> - random().should.not.equal random() - it 'should be 16 digits', -> - random().length.should.equal 16 diff --git a/test/random.js b/test/random.js new file mode 100644 index 00000000..b2b88d9f --- /dev/null +++ b/test/random.js @@ -0,0 +1,15 @@ +const { describe, it } = require('node:test') +const assert = require('node:assert/strict') + +const random = require('../lib/random_id') + +describe('random', () => { + describe('#random_id', () => { + it('should not be the same twice', () => { + assert.notEqual(random(), random()) + }) + it('should be 16 digits', () => { + assert.equal(random().length, 16) + }) + }) +}) diff --git a/test/server.coffee b/test/server.coffee deleted file mode 100644 index 485b0165..00000000 --- a/test/server.coffee +++ /dev/null @@ -1,228 +0,0 @@ -request = require 'supertest' -fs = require 'fs' -server = require '..' -path = require 'path' -random = require '../lib/random_id' -testid = random() -argv = require('../lib/defaultargs.coffee')({data: path.join('/tmp', 'sfwtests', testid), packageDir: path.join(__dirname, '..', 'node_modules'), port: 55557, security_legacy: true}) - -describe 'server', -> - app = {} - before((done) -> - # as starting the server this was does not create a sitemap file, create an empty one - sitemapLoc = path.join('/tmp', 'sfwtests', testid, 'status', 'sitemap.json') - fs.mkdirSync path.join('/tmp', 'sfwtests', testid) - fs.mkdirSync path.join('/tmp', 'sfwtests', testid, 'status') - fs.writeFileSync sitemapLoc, JSON.stringify([]) - - app = server(argv) - app.once("owner-set", -> - app.listen app.startOpts.port, app.startOpts.host, done - )) - - - request = request('http://localhost:55557') - - # location of the test page - loc = path.join('/tmp', 'sfwtests', testid, 'pages', 'adsf-test-page') - - it 'factories should return a list of plugin', () -> - await request - .get('/system/factories.json') - .expect(200) - .expect('Content-Type', /json/) - .then (res) -> - res.body[1].name.should.equal('Video') - res.body[1].category.should.equal('format') - - it 'new site should have an empty list of pages', () -> - await request - .get('/system/slugs.json') - .expect(200) - .expect('Content-Type', /json/) - .then (res) -> - res.body.should.be.empty - , (error) -> - throw error - .catch (error) -> - throw error - - it 'should create a page', () -> - body = JSON.stringify({ - type: 'create' - item: { - title: "Asdf Test Page" - story: [ - {id: "a1", type: "paragraph", text: "this is the first paragraph"} - {id: "a2", type: "paragraph", text: "this is the second paragraph"} - {id: "a3", type: "paragraph", text: "this is the third paragraph"} - {id: "a4", type: "paragraph", text: "this is the fourth paragraph"} - ] - } - date: 1234567890123 - }) - - request - .put('/page/adsf-test-page/action') - .send("action=" + body) - .expect(200) - .catch (err) -> - throw err - - it 'should move the paragraphs to the order given ', () -> - body = '{ "type": "move", "order": [ "a1", "a3", "a2", "a4"] }' - - request - .put('/page/adsf-test-page/action') - .send("action=" + body) - .expect(200) - .then (res) -> - if err - throw err - try - page = JSON.parse(fs.readFileSync(loc)) - catch err - throw err - page.story[1].id.should.equal('a3') - page.story[2].id.should.equal('a2') - page.journal[1].type.should.equal('move') - , (err) -> - throw err - .catch (err) -> - throw err - - it 'should add a paragraph', () -> - body = JSON.stringify({ - type: 'add' - after: 'a2' - item: {id: 'a5', type: 'paragraph', text: 'this is the NEW paragrpah'} - }) - - request - .put('/page/adsf-test-page/action') - .send("action=" + body) - .expect(200) - .then (res) -> - try - page = JSON.parse(fs.readFileSync(loc)) - catch err - throw err - page.story.length.should.equal(5) - page.story[3].id.should.equal('a5') - page.journal[2].type.should.equal('add') - , (err) -> - throw err - .catch (err) -> - throw err - - it 'should remove a paragraph with given id', () -> - body = JSON.stringify({ - type: 'remove' - id: 'a2' - }) - - request - .put('/page/adsf-test-page/action') - .send("action=" + body) - .expect(200) - .then (res) -> - try - page = JSON.parse(fs.readFileSync(loc)) - catch err - throw err - page.story.length.should.equal(4) - page.story[1].id.should.equal('a3') - page.story[2].id.should.not.equal('a2') - page.story[2].id.should.equal('a5') - page.journal[3].type.should.equal('remove') - , (err) -> - throw err - .catch (err) -> - throw err - - it 'should edit a paragraph in place', () -> - body = JSON.stringify({ - type: 'edit' - item: {id: 'a3', type: 'paragraph', text: 'edited'} - id: 'a3' - }) - - request - .put('/page/adsf-test-page/action') - .send("action=" + body) - .expect(200) - .then (res) -> - try - page = JSON.parse(fs.readFileSync(loc)) - catch err - throw err - page.story[1].text.should.equal('edited') - page.journal[4].type.should.equal('edit') - , (err) -> - throw err - .catch (err) -> - throw err - - it 'should default to no change', () -> - body = JSON.stringify({ - type: 'asdf' - }) - - request - .put('/page/adsf-test-page/action') - .send("action=" + body) - .expect(500) - .then (res) -> - try - page = JSON.parse(fs.readFileSync(loc)) - catch err - throw err - page.story.length.should.equal(4) - page.journal.length.should.equal(5) - page.story[0].id.should.equal('a1') - page.story[3].text.should.equal('this is the fourth paragraph') - page.journal[4].type.should.equal('edit') - , (err) -> - throw err - .catch (err) -> - throw err - - it 'should refuse to create over a page', () -> - body = JSON.stringify({ - type: 'create' - item: { title: 'Doh'} - id: 'c1' - }) - - request - .put('/page/adsf-test-page/action') - .send("action=" + body) - .expect(409) - .then (res) -> - try - page = JSON.parse(fs.readFileSync(loc)) - catch err - throw err - page.title.should.not.equal('Doh') - , (err) -> - throw err - .catch (err) -> - throw err - - it 'site should now have one page', () -> - request - .get('/system/slugs.json') - .expect(200) - .expect('Content-Type', /json/) - .then (res) -> - res.body.length.should.equal[1] - res.body[0].should.equal['adsf-test-page'] - , (err) -> - throw err - .catch (err) -> - throw err - - - - after( -> - app.close() if app.close) diff --git a/test/server.js b/test/server.js new file mode 100644 index 00000000..8f132f32 --- /dev/null +++ b/test/server.js @@ -0,0 +1,227 @@ +const { describe, it, before, after } = require('node:test') +const assert = require('node:assert/strict') + +const supertest = require('supertest') +const fs = require('node:fs') +const server = require('..') +const path = require('node:path') +const random = require('../lib/random_id') +const testid = random() +const argv = require('../lib/defaultargs')({ + data: path.join('/tmp', 'sfwtests', testid), + packageDir: path.join(__dirname, '..', 'node_modules'), + port: 55557, + security_legacy: true, + test: true, +}) + +describe('server', () => { + var app = {} + let runningServer = null + before(done => { + // as starting the server this was does not create a sitemap file, create an empty one + const sitemapLoc = path.join('/tmp', 'sfwtests', testid, 'status', 'sitemap.json') + fs.mkdirSync(path.join('/tmp', 'sfwtests', testid)) + fs.mkdirSync(path.join('/tmp', 'sfwtests', testid, 'status')) + fs.writeFileSync(sitemapLoc, JSON.stringify([])) + + app = server(argv) + app.once('owner-set', () => { + runningServer = app.listen(app.startOpts.port, app.startOpts.host, done) + }) + }) + + after(() => { + runningServer.close() + }) + + const request = supertest('http://localhost:55557') + + // location of the test page + const loc = path.join('/tmp', 'sfwtests', testid, 'pages', 'adsf-test-page') + + it('factories should return a list of plugin', async () => { + await request + .get('/system/factories.json') + .expect(200) + .expect('Content-Type', /json/) + .then(res => { + assert.equal(res.body[1].name, 'Video') + assert.equal(res.body[1].category, 'format') + }) + }) + + it('new site should have an empty list of pages', async () => { + await request + .get('/system/slugs.json') + .expect(200) + .expect('Content-Type', /json/) + .then(res => assert.deepEqual(res.body, [])) + }) + + it('should create a page', async () => { + const body = JSON.stringify({ + type: 'create', + item: { + title: 'Asdf Test Page', + story: [ + { id: 'a1', type: 'paragraph', text: 'this is the first paragraph' }, + { id: 'a2', type: 'paragraph', text: 'this is the second paragraph' }, + { id: 'a3', type: 'paragraph', text: 'this is the third paragraph' }, + { id: 'a4', type: 'paragraph', text: 'this is the fourth paragraph' }, + ], + }, + date: 1234567890123, + }) + + await request + .put('/page/adsf-test-page/action') + .send('action=' + body) + .expect(200) + }) + + it('should move the paragraphs to the order given ', async () => { + const body = '{ "type": "move", "order": [ "a1", "a3", "a2", "a4"] }' + + await request + .put('/page/adsf-test-page/action') + .send('action=' + body) + .expect(200) + .then( + () => { + const page = JSON.parse(fs.readFileSync(loc)) + assert.equal(page.story[1].id, 'a3') + assert.equal(page.story[2].id, 'a2') + assert.equal(page.journal[1].type, 'move') + }, + err => { + throw err + }, + ) + .catch(err => { + throw err + }) + }) + + it('should add a paragraph', async () => { + const body = JSON.stringify({ + type: 'add', + after: 'a2', + item: { id: 'a5', type: 'paragraph', text: 'this is the NEW paragrpah' }, + }) + + await request + .put('/page/adsf-test-page/action') + .send('action=' + body) + .expect(200) + .then(() => { + const page = JSON.parse(fs.readFileSync(loc)) + assert.equal(page.story.length, 5) + assert.equal(page.story[3].id, 'a5') + assert.equal(page.journal[2].type, 'add') + }) + .catch(err => { + throw err + }) + }) + + it('should remove a paragraph with given id', async () => { + const body = JSON.stringify({ + type: 'remove', + id: 'a2', + }) + + await request + .put('/page/adsf-test-page/action') + .send('action=' + body) + .expect(200) + .then(() => { + const page = JSON.parse(fs.readFileSync(loc)) + assert.equal(page.story.length, 4) + assert.equal(page.story[1].id, 'a3') + assert.notEqual(page.story[2].id, 'a2') + assert.equal(page.story[2].id, 'a5') + assert.equal(page.journal[3].type, 'remove') + }) + .catch(err => { + throw err + }) + }) + + it('should edit a paragraph in place', async () => { + const body = JSON.stringify({ + type: 'edit', + item: { id: 'a3', type: 'paragraph', text: 'edited' }, + id: 'a3', + }) + + await request + .put('/page/adsf-test-page/action') + .send('action=' + body) + .expect(200) + .then(() => { + const page = JSON.parse(fs.readFileSync(loc)) + assert.equal(page.story[1].text, 'edited') + assert.equal(page.journal[4].type, 'edit') + }) + .catch(err => { + throw err + }) + }) + + it('should default to no change', async () => { + const body = JSON.stringify({ + type: 'asdf', + }) + + await request + .put('/page/adsf-test-page/action') + .send('action=' + body) + .expect(500) + .then(() => { + const page = JSON.parse(fs.readFileSync(loc)) + assert.equal(page.story.length, 4) + assert.equal(page.journal.length, 5) + assert.equal(page.story[0].id, 'a1') + assert.equal(page.story[3].text, 'this is the fourth paragraph') + assert.equal(page.journal[4].type, 'edit') + }) + .catch(err => { + throw err + }) + }) + + it('should refuse to create over a page', async () => { + const body = JSON.stringify({ + type: 'create', + item: { title: 'Doh' }, + id: 'c1', + }) + + await request + .put('/page/adsf-test-page/action') + .send('action=' + body) + .expect(409) + .then(() => { + const page = JSON.parse(fs.readFileSync(loc)) + assert.notEqual(page.title, 'Doh') + }) + .catch(err => { + throw err + }) + }) + + it('site should now have one page', async () => { + await request + .get('/system/slugs.json') + .expect(200) + .expect('Content-Type', /json/) + .then(res => { + assert.equal(res.body.length, 1) + assert.equal(res.body[0], 'adsf-test-page') + }) + .catch(err => { + throw err + }) + }) +}) diff --git a/test/sitemap.coffee b/test/sitemap.coffee deleted file mode 100644 index cfc69449..00000000 --- a/test/sitemap.coffee +++ /dev/null @@ -1,110 +0,0 @@ -request = require 'supertest' -fs = require 'fs' -server = require '..' -path = require 'path' -random = require '../lib/random_id' -testid = random() -argv = require('../lib/defaultargs.coffee')({data: path.join('/tmp', 'sfwtests', testid), port: 55556, security_legacy: true}) - -describe 'sitemap', -> - app = {} - runningServer = null - beforeEach((done) -> - app = server(argv) - app.once("owner-set", -> - runningServer = app.listen app.startOpts.port, app.startOpts.host, done - )) - afterEach () -> - runningServer.close() - - - request = request('http://localhost:55556') - fs.mkdirSync path.join('/tmp', 'sfwtests', testid, 'pages'), {recursive: true} - - # location of the sitemap - sitemapLoc = path.join('/tmp', 'sfwtests', testid, 'status', 'sitemap.json') - - it 'new site should have an empty sitemap', () -> - request - .get('/system/sitemap.json') - .expect(200) - .expect('Content-Type', /json/) - .then (res) -> - res.body.should.be.empty - , (err) -> - throw err - - - it 'creating a page should add it to the sitemap', () -> - body = JSON.stringify({ - type: 'create' - item: { - title: "Asdf Test Page" - story: [ - {id: "a1", type: "paragraph", text: "this is the first paragraph"} - {id: "a2", type: "paragraph", text: "this is the second paragraph"} - {id: "a3", type: "paragraph", text: "this is the third paragraph"} - {id: "a4", type: "paragraph", text: "this is the fourth paragraph"} - ] - } - date: 1234567890123 - }) - - request - .put('/page/adsf-test-page/action') - .send("action=" + body) - .expect(200) - .then (res) -> - # sitemap update does not happen until after the put has returned, so wait for it to finish - app.sitemaphandler.once 'finished', -> - try - sitemap = JSON.parse(fs.readFileSync(sitemapLoc)) - catch err - throw err - sitemap[0].slug.should.equal['adsf-test-page'] - sitemap[0].synopsis.should.equal['this is the first paragraph'] - , (err) -> - throw err - - it 'synopsis should reflect edit to first paragraph', () -> - body = JSON.stringify({ - type: 'edit' - item: {id: 'a1', type: 'paragraph', text: 'edited'} - id: 'a1' - }) - - request - .put('/page/adsf-test-page/action') - .send("action=" + body) - .expect(200) - .then (res) -> - app.sitemaphandler.once 'finished', -> - try - sitemap = JSON.parse(fs.readFileSync(sitemapLoc)) - catch err - throw err - sitemap[0].slug.should.equal['adsf-test-page'] - sitemap[0].synopsis.should.equal['edited'] - , (err) -> - throw err - - it 'deleting a page should remove it from the sitemap', () -> - - request - .delete('/adsf-test-page.json') - .send() - .expect(200) - .then (res) -> - app.sitemaphandler.once 'finished', -> - try - sitemap = JSON.parse(fs.readFileSync(sitemapLoc)) - catch error - throw err - sitemap.should.be.empty - , (err) -> - throw err - - - - after( -> - app.close() if app.close) diff --git a/test/sitemap.js b/test/sitemap.js new file mode 100644 index 00000000..b4d60c45 --- /dev/null +++ b/test/sitemap.js @@ -0,0 +1,112 @@ +const { describe, it, before, after } = require('node:test') +const assert = require('node:assert/strict') + +const supertest = require('supertest') +const fs = require('node:fs') +const server = require('..') +const path = require('node:path') +const random = require('../lib/random_id') +const testid = random() +const argv = require('../lib/defaultargs')({ + data: path.join('/tmp', 'sfwtests', testid), + port: 55556, + security_legacy: true, + test: true, +}) + +describe('sitemap', () => { + let app = {} + let runningServer = null + + before(done => { + app = server(argv) + app.once('owner-set', () => { + runningServer = app.listen(app.startOpts.port, app.startOpts.host, done) + }) + }) + + after(() => { + runningServer.close() + }) + + const request = supertest('http://localhost:55556') + fs.mkdirSync(path.join('/tmp', 'sfwtests', testid, 'pages'), { recursive: true }) + + // location of the sitemap + const sitemapLoc = path.join('/tmp', 'sfwtests', testid, 'status', 'sitemap.json') + + it('new site should have an empty sitemap', async () => { + await request + .get('/system/sitemap.json') + .expect(200) + .expect('Content-Type', /json/) + .then(res => { + assert.equal(res.body.length, 0) + }) + }) + + it('creating a page should add it to the sitemap', async () => { + const body = JSON.stringify({ + type: 'create', + item: { + title: 'Asdf Test Page', + story: [ + { id: 'a1', type: 'paragraph', text: 'this is the first paragraph' }, + { id: 'a2', type: 'paragraph', text: 'this is the second paragraph' }, + { id: 'a3', type: 'paragraph', text: 'this is the [[third]] paragraph' }, + { id: 'a4', type: 'paragraph', text: 'this is the fourth paragraph' }, + ], + }, + date: 1234567890123, + }) + + await request + .put('/page/adsf-test-page/action') + .send('action=' + body) + .expect(200) + // sitemap update does not happen until after the put has returned, so wait for it to finish + .then(() => new Promise(resolve => app.sitemaphandler.once('finished', () => resolve()))) + .then( + () => { + const sitemap = JSON.parse(fs.readFileSync(sitemapLoc)) + assert.equal(sitemap[0].slug, 'adsf-test-page') + assert.equal(sitemap[0].synopsis, 'this is the first paragraph') + assert.deepEqual(sitemap[0].links, { third: 'a3' }) + }, + err => { + throw err + }, + ) + }) + + it('synopsis should reflect edit to first paragraph', async () => { + const body = JSON.stringify({ + type: 'edit', + item: { id: 'a1', type: 'paragraph', text: 'edited' }, + id: 'a1', + }) + + await request + .put('/page/adsf-test-page/action') + .send('action=' + body) + .expect(200) + .then(() => new Promise(resolve => app.sitemaphandler.once('finished', () => resolve()))) + .then(() => { + const sitemap = JSON.parse(fs.readFileSync(sitemapLoc)) + assert.equal(sitemap[0].slug, 'adsf-test-page') + assert.equal(sitemap[0].synopsis, 'edited') + }) + }) + + it('deleting a page should remove it from the sitemap', async () => { + await request + .delete('/adsf-test-page.json') + .send() + .expect(200) + .then(() => new Promise(resolve => app.sitemaphandler.once('finished', () => resolve()))) + .then(() => { + const sitemap = JSON.parse(fs.readFileSync(sitemapLoc)) + assert.deepEqual(sitemap, []) + }) + }) +})