diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 6eabff0b5f..9fdd5e0277 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -49,6 +49,8 @@ jobs:
run: npm run test:unit -- --ci --runInBand
- name: Integration Tests
run: npm run test:integration -- --ci --runInBand
+ - name: Consumption Tests
+ run: npm run test:consume-types
test-playwright:
runs-on: ubuntu-latest
diff --git a/.gitignore b/.gitignore
index 2fcd3279a2..462c5abfa2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,8 +4,6 @@
.vercel
_playwright-report
_playwright-results
-dist
-lib
node_modules
# Files
@@ -14,3 +12,10 @@ node_modules
# Exceptions
!.gitkeep
+
+# Output folder for the global build only
+dist
+
+# TypeScript declaration files for standard ESM consumption
+src/**/*.d.ts
+src/**/*.d.ts.map
diff --git a/.prettierignore b/.prettierignore
index 6906e9d65e..2983d21759 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -8,4 +8,4 @@ _vars.css
_vars-advanced.css
CHANGELOG.md
emoji-data.*
-HISTORY.md
\ No newline at end of file
+HISTORY.md
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 39497bbfc1..45d2e18043 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,4 +1,5 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
- "cSpell.words": ["coverpage"]
+ "cSpell.words": ["coverpage"],
+ "typescript.tsdk": "node_modules/typescript/lib"
}
diff --git a/docs/configuration.md b/docs/configuration.md
index 3901501898..d9cdfd05b1 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -456,7 +456,7 @@ window.$docsify = {
## name
-- Type: `String`
+- Type: `Boolean | String`
Website name as it appears in the sidebar.
@@ -474,6 +474,22 @@ window.$docsify = {
};
```
+If `true`, the website name will be inferred from the document's `
` tag.
+
+```js
+window.$docsify = {
+ name: true,
+};
+```
+
+If `false` or empty, no name will be displayed.
+
+```js
+window.$docsify = {
+ name: false,
+};
+```
+
## nameLink
- Type: `String`
@@ -652,6 +668,10 @@ window.$docsify = {
};
```
+## plugins
+
+See [Plugins](./plugins.md).
+
## relativePath
- Type: `Boolean`
@@ -706,6 +726,8 @@ window.$docsify = {
};
```
+If undefined or empty, no GitHub corner will be displayed.
+
## requestHeaders
- Type: `Object`
@@ -887,9 +909,9 @@ window.$docsify = {
Determines if/how the site's [skip navigation link](https://webaim.org/techniques/skipnav/) will be rendered.
```js
-// Render skip link for all routes (default)
+// Render skip link for all routes
window.$docsify = {
- skipLink: 'Skip to main content',
+ skipLink: 'Skip to content',
};
```
@@ -912,6 +934,13 @@ window.$docsify = {
};
```
+```js
+// Use default
+window.$docsify = {
+ skipLink: true, // "Skip to main content"
+};
+```
+
## subMaxLevel
- Type: `Number`
diff --git a/docs/plugins.md b/docs/plugins.md
index 2cbdff0f82..a656bc1fd8 100644
--- a/docs/plugins.md
+++ b/docs/plugins.md
@@ -1,5 +1,9 @@
# List of Plugins
+These are built-in and external plugins for Docsify.
+
+See also how to [Write a Plugin](./write-a-plugin.md).
+
## Full text search
By default, the hyperlink on the current page is recognized and the content is saved in `IndexedDB`. You can also specify the path to the files.
@@ -25,7 +29,7 @@ By default, the hyperlink on the current page is recognized and the content is s
insertBefore: '.sidebar-nav', // CSS selector in .sidebar scope
maxAge: 86400000, // Expiration time, the default one day
- paths: [], // or 'auto'
+ paths: [], // string[] of files to search in, or 'auto' for discovery based on your sidebar
placeholder: 'Type to search',
// Localization
diff --git a/package-lock.json b/package-lock.json
index 35516aa18e..4e287cda5e 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -26,7 +26,9 @@
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-replace": "^6.0.1",
"@rollup/plugin-terser": "^0.4.3",
+ "@types/common-tags": "^1.8.4",
"@types/eslint": "^8.40.2",
+ "@types/prismjs": "^1.26.5",
"axios": "^1.5.0",
"browser-sync": "^3.0.2",
"common-tags": "^1.8.0",
@@ -53,6 +55,7 @@
"rimraf": "^6.1.0",
"rollup": "^4.17.2",
"rollup-plugin-import-css": "^4.0.1",
+ "typescript": "^5.9.3",
"vue": "^3.4.27",
"xhr-mock": "^2.5.1"
},
@@ -4576,6 +4579,13 @@
"@babel/types": "^7.20.7"
}
},
+ "node_modules/@types/common-tags": {
+ "version": "1.8.4",
+ "resolved": "https://registry.npmjs.org/@types/common-tags/-/common-tags-1.8.4.tgz",
+ "integrity": "sha512-S+1hLDJPjWNDhcGxsxEbepzaxWqURP/o+3cP4aa2w7yBXgdcmKGQtZzP8JbyfOd0m+33nh+8+kvxYE2UJtBDkg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/cookie": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
@@ -4670,6 +4680,13 @@
"integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
"dev": true
},
+ "node_modules/@types/prismjs": {
+ "version": "1.26.5",
+ "resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.5.tgz",
+ "integrity": "sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/resolve": {
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
@@ -18207,11 +18224,11 @@
}
},
"node_modules/typescript": {
- "version": "5.5.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz",
- "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==",
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
- "peer": true,
+ "license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
diff --git a/package.json b/package.json
index 48c6cf2526..87750ec766 100644
--- a/package.json
+++ b/package.json
@@ -24,18 +24,22 @@
},
"type": "module",
"main": "dist/docsify.js",
+ "types": "src/core/Docsify.d.ts",
"exports": {
".": "./src/core/Docsify.js",
"./*": "./*"
},
"files": [
- "dist"
+ "dist",
+ "src"
],
"lint-staged": {
"*.js": "eslint --fix"
},
"dependencies": {
+ "common-tags": "^1.8.0",
"dexie": "^4.0.11",
+ "marked": "^16.0.0",
"medium-zoom": "^1.1.0",
"opencollective-postinstall": "^2.0.2",
"prismjs": "^1.29.0",
@@ -51,10 +55,11 @@
"@rollup/plugin-node-resolve": "^16.0.0",
"@rollup/plugin-replace": "^6.0.1",
"@rollup/plugin-terser": "^0.4.3",
+ "@types/common-tags": "^1.8.4",
"@types/eslint": "^8.40.2",
+ "@types/prismjs": "^1.26.5",
"axios": "^1.5.0",
"browser-sync": "^3.0.2",
- "common-tags": "^1.8.0",
"conventional-changelog-cli": "^3.0.0",
"cross-env": "^10.0.0",
"cssnano": "^7.0.1",
@@ -69,7 +74,6 @@
"jest": "^30.0.4",
"jest-environment-jsdom": "^30.0.5",
"lint-staged": "^16.1.0",
- "marked": "^16.0.0",
"npm-run-all": "^4.1.5",
"postcss-cli": "^11.0.0",
"postcss-import": "^16.1.0",
@@ -78,6 +82,7 @@
"rimraf": "^6.1.0",
"rollup": "^4.17.2",
"rollup-plugin-import-css": "^4.0.1",
+ "typescript": "^5.9.3",
"vue": "^3.4.27",
"xhr-mock": "^2.5.1"
},
@@ -87,8 +92,9 @@
"build:css:min": "cross-env NODE_ENV='production' npm run build:css -- --ext .min.css",
"build:emoji": "node ./build/emoji.js",
"build:js": "rollup -c",
- "build": "run-s clean build:js build:css build:css:min build:cover",
- "clean": "rimraf --glob dist/** themes/** _playwright*/**",
+ "build:types": "tsc",
+ "build": "run-s clean build:types build:js build:css build:css:min build:cover",
+ "clean": "rimraf --glob \"dist/**\" \"themes/**\" \"_playwright*/**\" \"src/**/*.d.ts\" \"src/**/*.d.ts.map\"",
"dev": "run-p serve:dev watch:*",
"docker:build:test": "npm run docker:cli -- build:test",
"docker:build": "docker build -f Dockerfile -t docsify-test:local .",
@@ -111,11 +117,15 @@
"test:e2e": "playwright test",
"test:e2e:chromium": "playwright test --project='chromium'",
"test:e2e:ui": "playwright test --ui",
+ "test:e2e:consume-types": "echo TODO: test the consume-types example with ESM modules",
"test:integration": "npm run test:jest -- --selectProjects integration",
"test:jest": "cross-env NODE_OPTIONS=--experimental-vm-modules jest",
"test:unit": "npm run test:jest -- --selectProjects unit",
"test:update:snapshot": "npm run test:jest -- --updateSnapshot",
- "test": "run-s test:jest test:e2e",
+ "test:consume-types": "cd test/consume-types && npm clean-install --install-links && npm run typecheck",
+ "test": "run-s test:jest test:e2e test:consume-types",
+ "typecheck": "tsc --noEmit",
+ "typecheck:watch": "tsc --noEmit --watch",
"watch:css": "run-p 'build:css -- --watch' 'build:css:min -- --watch'",
"watch:js": "npm run build:js -- --watch"
}
diff --git a/src/core/Docsify.js b/src/core/Docsify.js
index dbf7755ce9..f00268a763 100644
--- a/src/core/Docsify.js
+++ b/src/core/Docsify.js
@@ -1,3 +1,4 @@
+import prism from 'prismjs';
import { Router } from './router/index.js';
import { Render } from './render/index.js';
import { Fetch } from './fetch/index.js';
@@ -8,16 +9,29 @@ import config from './config.js';
import { isFn } from './util/core.js';
import { Lifecycle } from './init/lifecycle.js';
+export { prism };
+export { marked } from 'marked';
+export * as util from './util/index.js';
+export * as dom from './util/dom.js';
+export { Compiler } from './render/compiler.js';
+export { slugify } from './render/slugify.js';
+export { get } from './util/ajax.js';
+
/** @typedef {new (...args: any[]) => any} Constructor */
+/** @typedef {import('./config.js').DocsifyConfig} DocsifyConfig */
export class Docsify extends Fetch(
Events(Render(VirtualRoutes(Router(Lifecycle(Object))))),
) {
- config = config(this);
+ /** @type {DocsifyConfig} */
+ config;
- constructor() {
+ /** @param {Partial} conf */
+ constructor(conf = {}) {
super();
+ this.config = config(this, conf);
+
this.initLifecycle(); // Init hooks
this.initPlugin(); // Install plugins
this.callHook('init');
@@ -45,3 +59,5 @@ export class Docsify extends Fetch(
});
}
}
+
+export const version = '__VERSION__';
diff --git a/src/core/config.js b/src/core/config.js
index 8f165518aa..4fd85c2e29 100644
--- a/src/core/config.js
+++ b/src/core/config.js
@@ -1,82 +1,142 @@
import { stripIndent } from 'common-tags';
import { hyphenate, isPrimitive } from './util/core.js';
+/** @import { Docsify } from './Docsify.js' */
+/** @import { Hooks } from './init/lifecycle.js' */
const currentScript = document.currentScript;
-/** @param {import('./Docsify.js').Docsify} vm */
-export default function (vm) {
- const config = Object.assign(
- {
- alias: {},
- auto2top: false,
- autoHeader: false,
- basePath: '',
- catchPluginErrors: true,
- cornerExternalLinkTarget: '_blank',
- coverpage: '',
- el: '#app',
- executeScript: null,
- ext: '.md',
- externalLinkRel: 'noopener',
- externalLinkTarget: '_blank',
- fallbackLanguages: null,
- fallbackDefaultLanguage: '',
- formatUpdated: '',
- hideSidebar: false,
- homepage: 'README.md',
- keyBindings: {},
- loadNavbar: null,
- loadSidebar: null,
- logo: false,
- markdown: null,
- maxLevel: 6,
- mergeNavbar: false,
- name: '',
- nameLink: window.location.pathname,
- nativeEmoji: false,
- noCompileLinks: [],
- noEmoji: false,
- notFoundPage: false,
- onlyCover: false,
- plugins: [],
- relativePath: false,
- repo: '',
- requestHeaders: {},
- routerMode: 'hash',
- routes: {},
- skipLink: 'Skip to main content',
- subMaxLevel: 0,
- // themeColor: '',
- topMargin: 0,
-
- // Deprecations //////////////////
-
- __themeColor: '',
- get themeColor() {
- return this.__themeColor;
- },
- set themeColor(value) {
- if (value) {
- this.__themeColor = value;
-
- // eslint-disable-next-line no-console
- console.warn(
- stripIndent(`
- $docsify.themeColor is deprecated. Use the "--theme-color" theme property to set your theme color.
-
- `).trim(),
- );
- }
- },
- },
+const defaultDocsifyConfig = () => ({
+ alias: /** @type {Record} */ ({}),
+ auto2top: false,
+ autoHeader: false,
+ basePath: '',
+ catchPluginErrors: true,
+ cornerExternalLinkTarget:
+ /** @type {'_blank' | '_self' | '_parent' | '_top' | '_unfencedTop'} */ (
+ '_blank'
+ ),
+ coverpage: /** @type {boolean | string} */ (''),
+ el: '#app',
+ executeScript: /** @type {null | boolean} */ (null),
+ ext: '.md',
+ externalLinkRel: /** @type {'noopener' | string} */ ('noopener'), // TODO string union type based on spec
+ externalLinkTarget:
+ /** @type {'_blank' | '_self' | '_parent' | '_top' | '_unfencedTop'} */ (
+ '_blank'
+ ),
+ fallbackLanguages: /** @type {null | string[]} */ (null),
+ fallbackDefaultLanguage: '',
+ formatUpdated: /** @type {string | ((updatedAt: string) => string)} */ (''),
+ /** For the frontmatter plugin. */
+ frontMatter: /** @type {Record | null} */ (null),
+ hideSidebar: false,
+ homepage: 'README.md',
+ keyBindings:
+ /** @type {false | { [commandName: string]: { bindings: string[]; callback: Function } }} */ ({}),
+ loadNavbar: /** @type {null | boolean | string} */ (null),
+ loadSidebar: /** @type {null | boolean | string} */ (null),
+ logo: false,
+ markdown: null,
+ maxLevel: 6,
+ mergeNavbar: false,
+ name: /** @type {boolean | string} */ (''),
+ nameLink: window.location.pathname,
+ nativeEmoji: false,
+ noCompileLinks: /** @type {string[]} */ ([]),
+ noEmoji: false,
+ notFoundPage: /** @type {boolean | string | Record} */ (
+ false
+ ),
+ onlyCover: false,
+ plugins: /** @type {Plugin[]} */ ([]),
+ relativePath: false,
+ repo: /** @type {string} */ (''),
+ requestHeaders: /** @type {Record} */ ({}),
+ routerMode: /** @type {'hash' | 'history'} */ 'hash',
+ routes: /** @type {Record} */ ({}),
+ skipLink: /** @type {false | string | Record} */ (
+ 'Skip to main content'
+ ),
+ subMaxLevel: 0,
+ vueComponents: /** @type {Record} */ ({}),
+ vueGlobalOptions: /** @type {Record} */ ({}),
+ vueMounts: /** @type {Record} */ ({}),
+
+ // Deprecations //////////////////
+
+ /** @deprecated */
+ get themeColor() {
+ return this.__themeColor;
+ },
+ set themeColor(value) {
+ if (value) {
+ this.__themeColor = value;
+
+ // eslint-disable-next-line no-console
+ console.warn(
+ stripIndent(`
+ $docsify.themeColor is deprecated as of v5. Use the "--theme-color" CSS property to customize your theme color.
+
+ `).trim(),
+ );
+ }
+ },
+ __themeColor: '',
+
+ /** @deprecated */
+ get topMargin() {
+ return this.__topMargin;
+ },
+ set topMargin(value) {
+ if (value) {
+ this.__topMargin = value;
+
+ // eslint-disable-next-line no-console
+ console.warn(
+ stripIndent(`
+ $docsify.topMargin is deprecated as of v5. Use the "--scroll-padding-top" CSS property to specify a scroll margin when using a sticky navbar.
+
+ `).trim(),
+ );
+ }
+ },
+ __topMargin: 0,
+});
+
+/** @typedef {ReturnType} DocsifyConfig */
+
+/**
+ * @param {import('./Docsify.js').Docsify} vm
+ * @param {Partial} config
+ * @returns {DocsifyConfig}
+ */
+export default function (vm, config = {}) {
+ if (window.$docsify) {
+ // eslint-disable-next-line no-console
+ console.warn(
+ 'DEPRECATION: The global $docsify config variable is deprecated. See the latest getting started docs. https://docsify.js.org/#/quickstart',
+ );
+ }
+
+ config = Object.assign(
+ defaultDocsifyConfig(),
+
+ // Handle non-function configs no matter what (f.e. plugins assign options onto it)
+ window.$docsify,
- typeof window.$docsify === 'function'
- ? window.$docsify(vm)
- : window.$docsify,
+ // Also handle function config (the app can specificy a function, and plugins will assign options onto it)
+ typeof window.$docsify === 'function' ? window.$docsify(vm) : undefined,
+
+ // Finally, user config passed directly to the instance has priority.
+ config,
);
// Merge default and user-specified key bindings
@@ -86,8 +146,10 @@ export default function (vm) {
{
toggleSidebar: {
bindings: ['\\'],
- callback(e) {
- const toggleElm = document.querySelector('.sidebar-toggle-button');
+ callback(/** @type {KeyboardEvent} */ e) {
+ const toggleElm = /** @type {HTMLElement} */ (
+ document.querySelector('.sidebar-toggle-button')
+ );
if (toggleElm) {
toggleElm.click();
@@ -107,10 +169,20 @@ export default function (vm) {
)[0];
if (script) {
- for (const prop of Object.keys(config)) {
+ for (const prop of /** @type {(keyof DocsifyConfig)[]} */ (
+ Object.keys(config)
+ )) {
const val = script.getAttribute('data-' + hyphenate(prop));
if (isPrimitive(val)) {
+ // eslint-disable-next-line no-console
+ console.warn(
+ `DEPRECATION: data-* attributes on the docsify global script (f.e. ${
+ 'data-' + hyphenate(prop)
+ }) are deprecated.`,
+ );
+
+ // @ts-expect-error too dynamic for TS
config[prop] = val === '' ? true : val;
}
}
@@ -128,15 +200,30 @@ export default function (vm) {
config.coverpage = '_coverpage' + config.ext;
}
- if (config.repo === true) {
- config.repo = '';
- }
-
if (config.name === true) {
config.name = '';
}
- window.$docsify = config;
-
- return config;
+ return /** @type {DocsifyConfig} */ (config);
}
+
+/** @typedef {any} TODO */
+
+/** @typedef {(hooks: Hooks, vm: Docsify) => void} Plugin */
+
+/**
+ @typedef {(
+ ((route: string, matched: RegExpMatchArray) => string) |
+ ((route: string, matched: RegExpMatchArray, next: (markdown?: string) => void) => void)
+ )} RouteHandler - Given a route, provides the markdown to render for that route.
+ */
+
+/**
+@typedef {
+ {
+ subMaxLevel: number,
+ themeColor: string,
+ topMargin: number,
+ }
+} DocsifyConfigOld
+*/
diff --git a/src/core/event/index.js b/src/core/event/index.js
index 12fa3c78c7..ed51481652 100644
--- a/src/core/event/index.js
+++ b/src/core/event/index.js
@@ -10,8 +10,8 @@ import { stripUrlExceptId } from '../router/util.js';
*/
export function Events(Base) {
return class Events extends Base {
- #intersectionObserver;
- #isScrolling;
+ #intersectionObserver = new IntersectionObserver(() => {});
+ #isScrolling = false;
#title = dom.$.title;
// Initialization
@@ -53,7 +53,7 @@ export function Events(Base) {
const coverElm = dom.find('section.cover');
if (!coverElm) {
- dom.toggleClass(dom.body, 'add', 'sticky');
+ dom.body.classList.add('sticky');
return;
}
@@ -61,7 +61,7 @@ export function Events(Base) {
const isIntersecting = entries[0].isIntersecting;
const op = isIntersecting ? 'remove' : 'add';
- dom.toggleClass(dom.body, op, 'sticky');
+ dom.body.classList[op]('sticky');
});
observer.observe(coverElm);
@@ -165,32 +165,35 @@ export function Events(Base) {
// Convert key sequences to sorted arrays (modifiers first)
// Ex: ['alt+t', 't+ctrl'] => [['alt', 't'], ['ctrl', 't']]
- bindingConfig.bindings = bindingConfig.bindings.map(keys => {
- const sortedKeys = [[], []]; // Modifier keys, non-modifier keys
+ bindingConfig.bindings = bindingConfig.bindings.map(
+ (/** @type {string | string[]} */ keys) => {
+ /** @type {string[][]} */
+ const sortedKeys = [[], []]; // Modifier keys, non-modifier keys
- if (typeof keys === 'string') {
- keys = keys.split('+');
- }
+ if (typeof keys === 'string') {
+ keys = keys.split('+');
+ }
- keys.forEach(key => {
- const isModifierKey = modifierKeys.includes(key);
- const targetArray = sortedKeys[isModifierKey ? 0 : 1];
- const newKeyValue = key.trim().toLowerCase();
+ keys.forEach(key => {
+ const isModifierKey = modifierKeys.includes(key);
+ const targetArray = sortedKeys[isModifierKey ? 0 : 1];
+ const newKeyValue = key.trim().toLowerCase();
- targetArray.push(newKeyValue);
- });
+ targetArray.push(newKeyValue);
+ });
- sortedKeys.forEach(arr => arr.sort());
+ sortedKeys.forEach(arr => arr.sort());
- return sortedKeys.flat();
- });
+ return sortedKeys.flat();
+ },
+ );
});
// Handle keyboard events
- dom.on('keydown', e => {
- const isTextEntry = document.activeElement.matches(
- 'input, select, textarea',
- );
+ dom.on('keydown', (/** @type {KeyboardEvent} */ e) => {
+ const isTextEntry = /** @type {HTMLElement} */ (
+ document.activeElement
+ ).matches('input, select, textarea');
if (isTextEntry) {
return;
@@ -198,15 +201,16 @@ export function Events(Base) {
const bindingConfigs = Object.values(keyBindings || []);
const matchingConfigs = bindingConfigs.filter(
- ({ bindings }) =>
+ (/** @type {{ bindings: string[][] }} */ { bindings }) =>
bindings &&
// bindings: [['alt', 't'], ['ctrl', 't']]
- bindings.some(keys =>
+ bindings.some((/** @type {string[]} */ keys) =>
// keys: ['alt', 't']
keys.every(
// k: 'alt'
k =>
- (modifierKeys.includes(k) && e[k + 'Key']) ||
+ (modifierKeys.includes(k) &&
+ e[/** @type {keyof KeyboardEvent} */ (k + 'Key')]) ||
e.key === k || // Ex: " ", "a"
e.code.toLowerCase() === k || // "space"
e.code.toLowerCase() === `key${k}`, // "keya"
@@ -242,13 +246,15 @@ export function Events(Base) {
});
// Collapse toggle
- dom.on(sidebarElm, 'click', ({ target }) => {
- const linkElm = target.closest('a');
- const linkParent = linkElm?.closest('li');
+ dom.on(sidebarElm, 'click', (/** @type {MouseEvent} */ { target }) => {
+ const linkElm = /** @type {HTMLElement} */ (target).closest('a');
+ const linkParent = /** @type {HTMLLIElement} */ (
+ linkElm?.closest('li')
+ );
const hasSubSidebar = linkParent?.querySelector('.app-sub-sidebar');
if (hasSubSidebar) {
- dom.toggleClass(linkParent, 'collapse');
+ linkParent.classList.toggle('collapse');
}
});
}
@@ -266,19 +272,20 @@ export function Events(Base) {
return;
}
+ /** @type {HTMLElement | null} */
let lastContentFocusElm;
// Store last focused content element (restored via #toggleSidebar)
- dom.on(contentElm, 'focusin', e => {
+ dom.on(contentElm, 'focusin', (/** @type {FocusEvent} */ e) => {
const focusAttr = 'data-restore-focus';
lastContentFocusElm?.removeAttribute(focusAttr);
- lastContentFocusElm = e.target;
+ lastContentFocusElm = /** @type {HTMLElement} */ (e.target);
lastContentFocusElm.setAttribute(focusAttr, '');
});
// Toggle sidebar
- dom.on(toggleElm, 'click', e => {
+ dom.on(toggleElm, 'click', (/** @type {MouseEvent} */ e) => {
e.stopPropagation();
this.#toggleSidebar();
});
@@ -366,7 +373,8 @@ export function Events(Base) {
else if (source === 'navigate') {
// Scroll to top
if (auto2top) {
- document.scrollingElement.scrollTop = topMargin ?? 0;
+ /** @type {Element} */ (document.scrollingElement).scrollTop =
+ topMargin ?? 0;
}
}
}
@@ -406,13 +414,15 @@ export function Events(Base) {
...options,
};
const { query } = this.route;
- const focusEl = query.id
- ? // Heading ID
- dom.find(`#${query.id}`)
- : // First heading
- dom.find('#main :where(h1, h2, h3, h4, h5, h6)') ||
- // Content container
- dom.find('#main');
+ const focusEl = /** @type {HTMLElement|null} */ (
+ query.id
+ ? // Heading ID
+ dom.find(`#${query.id}`)
+ : // First heading
+ dom.find('#main :where(h1, h2, h3, h4, h5, h6)') ||
+ // Content container
+ dom.find('#main')
+ );
// Move focus to content area
if (focusEl) {
@@ -433,10 +443,6 @@ export function Events(Base) {
/**
* Marks the active app nav item
- *
- * @param {string} [href] Matching element HREF value. If unspecified,
- * defaults to the current path (without query params)
- * @void
*/
#markAppNavActiveElm() {
const href = decodeURIComponent(this.router.toURL(this.route.path));
@@ -448,13 +454,16 @@ export function Events(Base) {
return;
}
- const newActive = dom
- .findAll(navElm, 'a')
+ const newActive = /** @type {HTMLAnchorElement[]} */ (
+ dom.findAll(navElm, 'a')
+ )
.sort((a, b) => b.href.length - a.href.length)
.find(
a =>
- href.includes(a.getAttribute('href')) ||
- href.includes(decodeURI(a.getAttribute('href'))),
+ href.includes(/** @type {string} */ (a.getAttribute('href'))) ||
+ href.includes(
+ decodeURI(/** @type {string} */ (a.getAttribute('href'))),
+ ),
)
?.closest('li');
const oldActive = dom.find(navElm, 'li.active');
@@ -488,7 +497,7 @@ export function Events(Base) {
const newActive = dom
.find(
sidebar,
- `a[href="${href}"], a[href="${decodeURIComponent(href)}"]`,
+ `a[href="${href}"], a[href="${decodeURIComponent(/** @type {string} */ (href))}"]`,
)
?.closest('li');
@@ -521,7 +530,7 @@ export function Events(Base) {
const newPage = dom
.find(
sidebar,
- `a[href="${path}"], a[href="${decodeURIComponent(path)}"]`,
+ `a[href="${path}"], a[href="${decodeURIComponent(/** @type {string} */ (path))}"]`,
)
?.closest('li');
@@ -533,8 +542,11 @@ export function Events(Base) {
return newPage;
}
+ /**
+ * @param {boolean} [force]
+ */
#toggleSidebar(force) {
- const sidebarElm = dom.find('.sidebar');
+ const sidebarElm = /** @type {HTMLElement|null} */ (dom.find('.sidebar'));
if (!sidebarElm) {
return;
@@ -549,7 +561,7 @@ export function Events(Base) {
// Set aria-expanded attribute
ariaElms.forEach(toggleElm => {
const expanded = force ?? sidebarElm.classList.contains('show');
- toggleElm.setAttribute('aria-expanded', expanded);
+ toggleElm.setAttribute('aria-expanded', String(expanded));
toggleElm.setAttribute(
'aria-label',
expanded ? 'Hide primary navigation' : 'Show primary navigation',
@@ -570,8 +582,8 @@ export function Events(Base) {
}
// Restore focus
else {
- const restoreElm = document.querySelector(
- 'main > .content [data-restore-focus]',
+ const restoreElm = /** @type {HTMLElement|null} */ (
+ document.querySelector('main > .content [data-restore-focus]')
);
if (restoreElm) {
@@ -604,6 +616,9 @@ export function Events(Base) {
}
// Browsers w/o native scrollend event support (Safari)
else {
+ /** @type {any} */
+ let scrollTimer;
+
const callback = () => {
clearTimeout(scrollTimer);
@@ -613,8 +628,6 @@ export function Events(Base) {
}, 100);
};
- let scrollTimer;
-
document.addEventListener('scroll', callback, false);
}
},
diff --git a/src/core/fetch/index.js b/src/core/fetch/index.js
index eb625fb02d..3dcc0caf9d 100644
--- a/src/core/fetch/index.js
+++ b/src/core/fetch/index.js
@@ -10,6 +10,14 @@ import { get } from '../util/ajax.js';
*/
export function Fetch(Base) {
return class Fetch extends Base {
+ /**
+ * @param {any} path
+ * @param {any} qs
+ * @param {any} file
+ * @param {any} next
+ * @param {any} vm
+ * @param {any} [first]
+ */
#loadNested(path, qs, file, next, vm, first) {
path = first ? path : path.replace(/\/$/, '');
path = getParentPath(path);
@@ -25,16 +33,25 @@ export function Fetch(Base) {
).then(next, _error => this.#loadNested(path, qs, file, next, vm));
}
+ /** @type {any} */
#last;
#abort = () => this.#last && this.#last.abort && this.#last.abort();
+ /**
+ * @param {any} url
+ * @param {any} requestHeaders
+ */
#request = (url, requestHeaders) => {
this.#abort();
this.#last = get(url, true, requestHeaders);
return this.#last;
};
+ /**
+ * @param {any} path
+ * @param {any} config
+ */
#get404Path = (path, config) => {
const { notFoundPage, ext } = config;
const defaultPath = '_404' + (ext || '.md');
@@ -64,8 +81,17 @@ export function Fetch(Base) {
return path404;
};
+ /**
+ * @param {any} path
+ * @param {any} qs
+ * @param {any} loadSidebar
+ * @param {any} cb
+ */
_loadSideAndNav(path, qs, loadSidebar, cb) {
return () => {
+ /**
+ * @param {any} result
+ */
const renderSidebar = result => {
this._renderSidebar(result);
cb();
@@ -73,7 +99,7 @@ export function Fetch(Base) {
if (!loadSidebar) {
// Although, we don't load sidebar from sidebar file, we still need call the render to auto generate sidebar from headings toc
- renderSidebar();
+ renderSidebar(null);
return;
}
@@ -103,6 +129,11 @@ export function Fetch(Base) {
this.isHTML = /\.html$/g.test(file);
// create a handler that should be called if content was fetched successfully
+ /**
+ * @param {any} text
+ * @param {any} [opt]
+ * @param {any} [response]
+ */
const contentFetched = (text, opt, response) => {
this.route.response = response;
this._renderMain(
@@ -113,6 +144,10 @@ export function Fetch(Base) {
};
// and a handler that is called if content failed to fetch
+ /**
+ * @param {any} _error
+ * @param {any} [response]
+ */
const contentFailedToFetch = (_error, response) => {
this.route.response = response;
this._fetchFallbackPage(path, qs, cb) || this._fetch404(file, qs, cb);
@@ -120,7 +155,7 @@ export function Fetch(Base) {
// attempt to fetch content from a virtual route, and fallback to fetching the actual file
if (!this.isRemoteUrl) {
- this.matchVirtualRoute(path).then(contents => {
+ this.matchVirtualRoute(path).then((/** @type {any} */ contents) => {
if (typeof contents === 'string') {
contentFetched(contents);
} else {
@@ -144,7 +179,7 @@ export function Fetch(Base) {
path,
qs,
loadNavbar,
- text => this._renderNav(text),
+ (/** @type {string} */ text) => this._renderNav(text),
this,
true,
);
@@ -203,6 +238,11 @@ export function Fetch(Base) {
}
}
+ /**
+ * @param {any} path
+ * @param {any} qs
+ * @param {any} [cb]
+ */
_fetchFallbackPage(path, qs, cb = noop) {
const {
requestHeaders,
@@ -227,12 +267,17 @@ export function Fetch(Base) {
const req = this.#request(newPath + qs, requestHeaders);
req.then(
+ /**
+ * @param {any} text
+ * @param {any} [opt]
+ */
(text, opt) =>
this._renderMain(
text,
opt,
this._loadSideAndNav(path, qs, loadSidebar, cb),
),
+ /** @param {any} _error */
_error => this._fetch404(path, qs, cb),
);
@@ -245,7 +290,6 @@ export function Fetch(Base) {
* @param {*} qs TODO: define
* @param {Function} cb Callback
* @returns {Boolean} True if the requested page is not found
- * @private
*/
_fetch404(path, qs, cb = noop) {
const { loadSidebar, requestHeaders, notFoundPage } = this.config;
@@ -255,7 +299,12 @@ export function Fetch(Base) {
const path404 = this.#get404Path(path, this.config);
this.#request(this.router.getFile(path404), requestHeaders).then(
+ /**
+ * @param {any} text
+ * @param {any} [opt]
+ */
(text, opt) => this._renderMain(text, opt, fnLoadSideAndNav),
+ /** @param {any} _error */
_error => this._renderMain(null, {}, fnLoadSideAndNav),
);
return true;
@@ -266,7 +315,7 @@ export function Fetch(Base) {
}
initFetch() {
- this.$fetch(_ => this.callHook('ready'));
+ this.$fetch(() => this.callHook('ready'));
}
};
}
diff --git a/src/core/global-api.js b/src/core/global-api.js
index 8a571d84ac..1b48cb43b1 100644
--- a/src/core/global-api.js
+++ b/src/core/global-api.js
@@ -1,4 +1,4 @@
-import prism from 'prismjs';
+import * as prism from 'prismjs';
import { marked } from 'marked';
import * as util from './util/index.js';
import * as dom from './util/dom.js';
@@ -6,10 +6,6 @@ import { Compiler } from './render/compiler.js';
import { slugify } from './render/slugify.js';
import { get } from './util/ajax.js';
-// TODO This is deprecated, kept for backwards compatibility. Remove in a
-// major release. We'll tell people to get everything from the DOCSIFY global
-// when using the global build, but we'll highly recommend for them to import
-// from the ESM build (f.e. dist/docsify.esm.js and dist/docsify.min.esm.js).
export default function initGlobalAPI() {
window.Docsify = {
util,
diff --git a/src/core/globals.ts b/src/core/globals.ts
new file mode 100644
index 0000000000..cf0f795211
--- /dev/null
+++ b/src/core/globals.ts
@@ -0,0 +1,42 @@
+import prism from 'prismjs';
+import { marked as _marked } from 'marked';
+import * as util from './util/index.js';
+import * as dom from './util/dom.js';
+import { Compiler } from './render/compiler.js';
+import { slugify } from './render/slugify.js';
+import { get } from './util/ajax.js';
+import { DocsifyConfig } from './config.js';
+import { Docsify } from './Docsify.js';
+
+type DocsifyConfigOrFn =
+ | Partial
+ | (Partial & ((config: Docsify) => Partial));
+
+declare global {
+ interface Window {
+ $docsify?: DocsifyConfigOrFn;
+
+ Docsify: {
+ util: typeof util;
+ dom: typeof dom;
+ get: typeof get;
+ slugify: typeof slugify;
+ version: string;
+ };
+ DocsifyCompiler: typeof Compiler;
+ marked: typeof _marked;
+ Prism: typeof prism;
+ Vue: any; // TODO Get Vue types and apply them here
+
+ __current_docsify_compiler__?: Compiler;
+ }
+
+ const $docsify: Window['$docsify'];
+
+ const Docsify: Window['Docsify'];
+ const DocsifyCompiler: Window['DocsifyCompiler'];
+ const marked: Window['marked'];
+ // @ts-expect-error Prism types are wonky
+ const Prism: Window['Prism'];
+ const Vue: Window['Vue'];
+}
diff --git a/src/core/index.js b/src/core/index.js
index 897ac66b3a..43fb5b9c7e 100644
--- a/src/core/index.js
+++ b/src/core/index.js
@@ -2,10 +2,6 @@ import { documentReady } from './util/dom.js';
import { Docsify } from './Docsify.js';
import initGlobalAPI from './global-api.js';
-// TODO This global API and auto-running Docsify will be deprecated, and removed
-// in a major release. Instead we'll tell users to use `new Docsify()` to create
-// and manage their instance(s).
-
/**
* Global API
*/
diff --git a/src/core/init/lifecycle.js b/src/core/init/lifecycle.js
index 7174079afb..de11f84c70 100644
--- a/src/core/init/lifecycle.js
+++ b/src/core/init/lifecycle.js
@@ -8,8 +8,10 @@ import { noop } from '../util/core.js';
*/
export function Lifecycle(Base) {
return class Lifecycle extends Base {
+ /** @type {Record} */
_hooks = {};
- _lifecycle = {};
+
+ _lifecycle = /** @type {Hooks} */ ({});
initLifecycle() {
const hooks = [
@@ -21,16 +23,25 @@ export function Lifecycle(Base) {
'ready',
];
- hooks.forEach(hook => {
+ hooks.forEach((/** @type {string} */ hook) => {
+ /** @type {Function[]} */
const arr = (this._hooks[hook] = []);
- this._lifecycle[hook] = fn => arr.push(fn);
+ this._lifecycle[hook] = (/** @type {Function} */ fn) => arr.push(fn);
});
}
+ /**
+ * @param {string} hookName
+ * @param {any} [data]
+ * @param {Function} [next]
+ */
callHook(hookName, data, next = noop) {
const queue = this._hooks[hookName];
const catchPluginErrors = this.config.catchPluginErrors;
+ /**
+ * @param {number} index
+ */
const step = function (index) {
const hookFn = queue[index];
@@ -40,9 +51,11 @@ export function Lifecycle(Base) {
const errTitle = 'Docsify plugin error';
if (hookFn.length === 2) {
+ // FIXME this does not catch async errors. We can support async
+ // functions for this, or add a second arg to next() functions.
try {
- hookFn(data, result => {
- data = result;
+ hookFn(data, (/** @type {string} */ result) => {
+ data = result === undefined ? data : result;
step(index + 1);
});
} catch (err) {
@@ -81,3 +94,20 @@ export function Lifecycle(Base) {
}
};
}
+
+/**
+@typedef {{
+ init(): void
+ mounted(): void
+ beforeEach: (
+ ((markdown: string) => string) |
+ ((markdown: string, next: (markdown?: string) => void) => void)
+ )
+ afterEach: (
+ ((html: string) => string) |
+ ((html: string, next: (html?: string) => void) => void)
+ )
+ doneEach(): void
+ ready(): void
+}} Hooks
+*/
diff --git a/src/core/module.js b/src/core/module.js
new file mode 100644
index 0000000000..4206ebc246
--- /dev/null
+++ b/src/core/module.js
@@ -0,0 +1 @@
+export * from './Docsify.js';
diff --git a/src/core/module.min.js b/src/core/module.min.js
new file mode 100644
index 0000000000..c08b05d104
--- /dev/null
+++ b/src/core/module.min.js
@@ -0,0 +1,2 @@
+// This file exists for type declaration output.
+export * from './Docsify.js';
diff --git a/src/core/modules.ts b/src/core/modules.ts
new file mode 100644
index 0000000000..55ba6a0820
--- /dev/null
+++ b/src/core/modules.ts
@@ -0,0 +1,4 @@
+declare module '*.css' {
+ const cssText: string;
+ export default cssText;
+}
diff --git a/src/core/render/compiler.js b/src/core/render/compiler.js
index 5eb32fbce9..1d5dd54d66 100644
--- a/src/core/render/compiler.js
+++ b/src/core/render/compiler.js
@@ -96,7 +96,7 @@ export class Compiler {
* @param {string} href The href to the file to embed in the page.
* @param {string} title Title of the link used to make the embed.
*
- * @return {type} Return value description.
+ * @return {any} Return value description.
*/
compileEmbed(href, title) {
const { str, config } = getAndRemoveConfig(title);
@@ -179,6 +179,7 @@ export class Compiler {
origin.listitem = taskListItemCompiler({ renderer });
origin.tablecell = tableCellCompiler({ renderer });
+ // @ts-expect-error
renderer.origin = origin;
return renderer;
@@ -265,6 +266,7 @@ export class Compiler {
text: text,
tokens: [{ type: 'text', raw: text, text: text }],
};
+ // @ts-expect-error
return this.renderer.heading(tokenHeading);
}
diff --git a/src/core/render/compiler/code.js b/src/core/render/compiler/code.js
index b63b85aa1b..bfe57413b9 100644
--- a/src/core/render/compiler/code.js
+++ b/src/core/render/compiler/code.js
@@ -1,4 +1,4 @@
-import Prism from 'prismjs';
+import * as Prism from 'prismjs';
// See https://github.com/PrismJS/prism/pull/1367
import 'prismjs/components/prism-markup-templating.js';
import checkLangDependenciesAllLoaded from '../../util/prism.js';
diff --git a/src/core/render/compiler/image.js b/src/core/render/compiler/image.js
index 6e9b20f897..d113516b61 100644
--- a/src/core/render/compiler/image.js
+++ b/src/core/render/compiler/image.js
@@ -18,7 +18,7 @@ export const imageCompiler = ({ renderer, contentBase, router }) =>
}
if (config.size) {
- const [width, height] = config.size.split('x');
+ const [width, height] = /** @type {string} */ (config.size).split('x');
if (height) {
attrs.push(`width="${width}" height="${height}"`);
} else {
diff --git a/src/core/render/embed.js b/src/core/render/embed.js
index 95055e9fa8..73fe7de19f 100644
--- a/src/core/render/embed.js
+++ b/src/core/render/embed.js
@@ -61,10 +61,9 @@ function walkFetchEmbed({ embedTokens, compile, fetch }, cb) {
});
// This may contain YAML front matter and will need to be stripped.
- const frontMatterInstalled =
- ($docsify.frontMatter || {}).installed || false;
- if (frontMatterInstalled === true) {
- text = $docsify.frontMatter.parseMarkdown(text);
+ const frontMatterInstalled = $docsify?.frontMatter?.installed;
+ if (frontMatterInstalled) {
+ text = $docsify.frontMatter?.parseMarkdown(text);
}
if (currentToken.embed.fragment) {
@@ -91,10 +90,10 @@ function walkFetchEmbed({ embedTokens, compile, fetch }, cb) {
text: /* html */ `\n${text}\n
`,
},
];
- embedToken.links = {};
+ /** @type {any} */ (embedToken).links = {};
} else {
embedToken = [{ type: 'html', text }];
- embedToken.links = {};
+ /** @type {any} */ (embedToken).links = {};
}
}
diff --git a/src/core/render/index.js b/src/core/render/index.js
index 85b6e7200f..9e546a3d01 100644
--- a/src/core/render/index.js
+++ b/src/core/render/index.js
@@ -9,18 +9,28 @@ import { prerenderEmbed } from './embed.js';
/** @typedef {import('../Docsify.js').Constructor} Constructor */
+// TODO replace with Vue types if available
+/** @typedef {{ _isVue?: boolean, $destroy?: () => void }} Vue2Instance */
+/** @typedef {{ __vue__?: Vue2Instance }} WithVue2 */
+/** @typedef {{ __v_skip?: boolean }} VNode3 */
+/** @typedef {{ _vnode?: VNode3, __vue_app__?: { unmount: () => void } }} WithVue3 */
+/** @typedef {Element & WithVue2 & WithVue3} VueMountElement */
+
/**
* @template {!Constructor} T
* @param {T} Base - The class to extend
*/
export function Render(Base) {
return class Render extends Base {
+ /** @type {Compiler | undefined} */
+ compiler;
#vueGlobalData;
#addTextAsTitleAttribute(cssSelector) {
dom.findAll(cssSelector).forEach(elm => {
- if (!elm.title && elm.innerText) {
- elm.title = elm.innerText;
+ const e = /** @type {HTMLElement} */ (elm);
+ if (!e.title && e.innerText) {
+ e.title = e.innerText;
}
});
}
@@ -28,12 +38,14 @@ export function Render(Base) {
#executeScript() {
const script = dom
.findAll('.markdown-section>script')
- .filter(s => !/template/.test(s.type))[0];
+ .filter(
+ s => !/template/.test(/** @type {HTMLScriptElement} */ (s).type),
+ )[0];
if (!script) {
return false;
}
- const code = script.innerText.trim();
+ const code = /** @type {HTMLElement} */ (script).innerText.trim();
if (!code) {
return false;
}
@@ -60,6 +72,9 @@ export function Render(Base) {
window.Vue.version &&
Number(window.Vue.version.charAt(0));
+ /**
+ * @param {VueMountElement} elm
+ */
const isMountedVue = elm => {
const isVue2 = Boolean(elm.__vue__ && elm.__vue__._isVue);
const isVue3 = Boolean(elm._vnode && elm._vnode.__v_skip);
@@ -75,9 +90,9 @@ export function Render(Base) {
// Destroy/unmount existing Vue instances
for (const mountedElm of mountedElms) {
if (vueVersion === 2) {
- mountedElm.__vue__.$destroy();
+ /** @type {VueMountElement} */ (mountedElm).__vue__?.$destroy?.();
} else if (vueVersion === 3) {
- mountedElm.__vue_app__.unmount();
+ /** @type {VueMountElement} */ (mountedElm).__vue_app__?.unmount();
}
}
}
@@ -159,12 +174,16 @@ export function Render(Base) {
.filter(elm => !vueMountData.some(([e, c]) => e === elm))
// Detect Vue content
.filter(elm => {
+ const selector = vueComponentNames.join(',');
+ const hasComponents = selector
+ ? Boolean(elm.querySelector(selector))
+ : false;
const isVueMount =
// is a component
elm.tagName.toLowerCase() in
(docsifyConfig.vueComponents || {}) ||
// has a component(s)
- elm.querySelector(vueComponentNames.join(',') || null) ||
+ hasComponents ||
// has curly braces
reHasBraces.test(elm.outerHTML) ||
// has content directive
@@ -281,24 +300,30 @@ export function Render(Base) {
const sidebarToggleEl = dom.getNode('button.sidebar-toggle');
if (hideSidebar) {
- sidebarEl?.remove(sidebarEl);
- sidebarToggleEl?.remove(sidebarToggleEl);
+ sidebarEl?.remove();
+ sidebarToggleEl?.remove();
return null;
}
+ if (!this.compiler) {
+ throw new Error('Compiler is not initialized');
+ }
+
dom.setHTML('.sidebar-nav', this.compiler.sidebar(text, maxLevel));
- sidebarToggleEl.setAttribute('aria-expanded', !isMobile());
+ sidebarToggleEl.setAttribute('aria-expanded', String(!isMobile()));
const activeElmHref = this.router.toURL(this.route.path);
- const activeEl = dom.find(`.sidebar-nav a[href="${activeElmHref}"]`);
+ const activeEl = /** @type {HTMLElement | null} */ (
+ dom.find(`.sidebar-nav a[href="${activeElmHref}"]`)
+ );
this.#addTextAsTitleAttribute('.sidebar-nav a');
if (loadSidebar && activeEl) {
- activeEl.parentNode.innerHTML +=
- this.compiler.subSidebar(subMaxLevel) || '';
+ const parent = /** @type {HTMLElement} */ (activeEl.parentElement);
+ parent.innerHTML += this.compiler.subSidebar(subMaxLevel) || '';
} else {
this.compiler.resetToc();
}
@@ -335,6 +360,9 @@ export function Render(Base) {
});
}
+ /**
+ * @param {HTMLElement | null} activeEl
+ */
_bindEventOnRendered(activeEl) {
const { autoHeader } = this.config;
@@ -345,7 +373,10 @@ export function Render(Base) {
const hasH1 = main.querySelector('h1');
if (!hasH1) {
- const h1HTML = this.compiler.header(activeEl.innerText, 1);
+ const h1HTML = /** @type {Compiler} */ (this.compiler).header(
+ activeEl.innerText,
+ 1,
+ );
const h1Node = dom.create('div', h1HTML).firstElementChild;
if (h1Node) {
@@ -360,7 +391,7 @@ export function Render(Base) {
return;
}
- const html = this.compiler.compile(text);
+ const html = /** @type {Compiler} */ (this.compiler).compile(text);
['.app-nav', '.app-nav-merged'].forEach(selector => {
dom.setHTML(selector, html);
@@ -400,11 +431,12 @@ export function Render(Base) {
} else {
prerenderEmbed(
{
- compiler: this.compiler,
+ compiler: /** @type {Compiler} */ (this.compiler),
raw: result,
+ fetch: undefined,
},
tokens => {
- html = this.compiler.compile(tokens);
+ html = /** @type {Compiler} */ (this.compiler).compile(tokens);
callback();
},
);
@@ -417,20 +449,18 @@ export function Render(Base) {
const rootElm = document.documentElement;
const coverBg = getComputedStyle(rootElm).getPropertyValue('--cover-bg');
- dom.toggleClass(
- dom.getNode('main'),
- coverOnly ? 'add' : 'remove',
- 'hidden',
- );
+ dom.getNode('main').classList[coverOnly ? 'add' : 'remove']('hidden');
if (!text) {
- dom.toggleClass(el, 'remove', 'show');
+ el.classList.remove('show');
return;
}
- dom.toggleClass(el, 'add', 'show');
+ el.classList.add('show');
- let html = this.coverIsHTML ? text : this.compiler.cover(text);
+ let html = this.coverIsHTML
+ ? text
+ : /** @type {Compiler} */ (this.compiler).cover(text);
if (!coverBg) {
const mdBgMatch = html
@@ -585,13 +615,17 @@ export function Render(Base) {
}
if (config.themeColor) {
- dom.$.head.appendChild(
- dom.create('div', tpl.theme(config.themeColor)).firstElementChild,
- );
+ const themeNode = dom.create(
+ 'div',
+ tpl.theme(config.themeColor),
+ ).firstElementChild;
+ if (themeNode) {
+ dom.$.head.appendChild(themeNode);
+ }
}
this._updateRender();
- dom.toggleClass(dom.body, 'ready');
+ dom.body.classList.add('ready');
}
};
}
diff --git a/src/core/render/utils.js b/src/core/render/utils.js
index 66796de1e0..f402118043 100644
--- a/src/core/render/utils.js
+++ b/src/core/render/utils.js
@@ -16,9 +16,10 @@
*
* @param {string} str The string to parse.
*
- * @return {{str: string, config: object}} The original string formatted, and parsed object, { str, config }.
+ * @return {{str: string, config: Record}} The original string formatted, and parsed object, { str, config }.
*/
export function getAndRemoveConfig(str = '') {
+ /** @type {Record} */
const config = {};
if (str) {
@@ -36,7 +37,8 @@ export function getAndRemoveConfig(str = '') {
if (!Array.isArray(config[key]) && value !== config[key]) {
config[key] = [config[key]];
}
- config[key].includes(value) || config[key].push(value);
+ config[key].includes(value) ||
+ /** @type {string[]} */ (config[key]).push(value);
} else {
config[key] = value;
}
@@ -86,5 +88,9 @@ export function getAndRemoveDocsifyIgnoreConfig(content = '') {
ignoreAllSubs = true;
}
- return { content, ignoreAllSubs, ignoreSubHeading };
+ return /** @type {{content: string, ignoreAllSubs: boolean, ignoreSubHeading: boolean}} */ ({
+ content,
+ ignoreAllSubs,
+ ignoreSubHeading,
+ });
}
diff --git a/src/core/router/history/base.js b/src/core/router/history/base.js
index 352aa0f03c..22f2276f49 100644
--- a/src/core/router/history/base.js
+++ b/src/core/router/history/base.js
@@ -51,7 +51,11 @@ export class History {
return this.config.basePath;
}
- getFile(path = this.getCurrentPath(), isRelative) {
+ /**
+ * @param {string} path
+ * @param {boolean} isRelative
+ */
+ getFile(path = this.getCurrentPath(), isRelative = false) {
const { config } = this;
const base = this.getBasePath();
const ext = typeof config.ext === 'string' ? config.ext : '.md';
@@ -72,11 +76,24 @@ export class History {
cb();
}
- getCurrentPath() {}
+ /**
+ * @return {string}
+ */
+ getCurrentPath() {
+ throw new Error('Subclass should implement');
+ }
- normalize() {}
+ normalize() {
+ throw new Error('Subclass should implement');
+ }
- parse() {}
+ /**
+ * @param {string} path
+ * @return {import('../index.js').Route} { path, query, file, response }
+ */
+ parse(path) {
+ throw new Error('Subclass should implement');
+ }
toURL(path, params, currentRoute) {
const local = currentRoute && path[0] === '#';
diff --git a/src/core/router/history/hash.js b/src/core/router/history/hash.js
index 4285ea4c3d..8bd091d53d 100644
--- a/src/core/router/history/hash.js
+++ b/src/core/router/history/hash.js
@@ -34,7 +34,7 @@ export class HashHistory extends History {
return index === -1 ? '' : href.slice(index + 1);
}
- /** @param {((params: {source: TODO}) => void)} [cb] */
+ /** @param {(params: {source: any, event?: any}) => void} [cb] */
onchange(cb = noop) {
// The hashchange event does not tell us if it originated from
// a clicked link or by moving back/forward in the history;
@@ -80,8 +80,8 @@ export class HashHistory extends History {
/**
* Parse the url
- * @param {string} [path=location.href] URL to be parsed
- * @return {object} { path, query }
+ * @param {string} path URL to be parsed
+ * @return {import('../index.js').Route} { path, query, file, response }
*/
parse(path = location.href) {
let query = '';
diff --git a/src/core/router/history/html5.js b/src/core/router/history/html5.js
index 49c1aac5eb..9903e46986 100644
--- a/src/core/router/history/html5.js
+++ b/src/core/router/history/html5.js
@@ -17,6 +17,7 @@ export class HTML5History extends History {
return (path || '/') + window.location.search + window.location.hash;
}
+ /** @param {(params: any) => void} [cb] */
onchange(cb = noop) {
on('click', e => {
const el = e.target.tagName === 'A' ? e.target : e.target.parentNode;
@@ -37,7 +38,7 @@ export class HTML5History extends History {
/**
* Parse the url
* @param {string} [path=location.href] URL to be parsed
- * @return {object} { path, query }
+ * @return {import('../index.js').Route} { path, query, file, response }
*/
parse(path = location.href) {
let query = '';
diff --git a/src/core/router/index.js b/src/core/router/index.js
index cb4f8674cd..f7b985314c 100644
--- a/src/core/router/index.js
+++ b/src/core/router/index.js
@@ -5,11 +5,14 @@ import { HTML5History } from './history/html5.js';
/**
* @typedef {{
- * path?: string
+ * path: string
+ * query: Record;
+ * file: string;
+ * response: {}
* }} Route
*/
-/** @type {Route} */
+/** @type {Partial} */
let lastRoute = {};
/** @typedef {import('../Docsify.js').Constructor} Constructor */
@@ -20,12 +23,13 @@ let lastRoute = {};
*/
export function Router(Base) {
return class Router extends Base {
+ /** @type {Partial} */
route = {};
updateRender() {
- this.router.normalize();
- this.route = this.router.parse();
- dom.body.setAttribute('data-page', this.route.file);
+ this.router?.normalize();
+ this.route = this.router?.parse() ?? {};
+ dom.body.setAttribute('data-page', this.route.file ?? '');
}
initRouter() {
diff --git a/src/core/router/util.js b/src/core/router/util.js
index f795063bcf..54118b82df 100644
--- a/src/core/router/util.js
+++ b/src/core/router/util.js
@@ -3,7 +3,12 @@ import { cached } from '../util/core.js';
const decode = decodeURIComponent;
const encode = encodeURIComponent;
+/**
+ * @param {string} query
+ * @return {Record}
+ */
export function parseQuery(query) {
+ /** @type {Record} */
const res = {};
query = query.trim().replace(/^(\?|#|&)/, '');
diff --git a/src/core/util/ajax.js b/src/core/util/ajax.js
index 1a0edd107e..37afe1401c 100644
--- a/src/core/util/ajax.js
+++ b/src/core/util/ajax.js
@@ -20,6 +20,11 @@ const cache = {};
// TODO update to using fetch() + Streams API instead of XMLHttpRequest. See an
// example of download progress calculation using fetch() here:
// https://streams.spec.whatwg.org/demos/
+/**
+ * @param {string} url
+ * @param {boolean} [hasBar]
+ * @param {Record} [headers]
+ */
export function get(url, hasBar = false, headers = {}) {
const xhr = new XMLHttpRequest();
const cached = cache[url];
diff --git a/src/core/util/core.js b/src/core/util/core.js
index fc61809422..04f3fbdd12 100644
--- a/src/core/util/core.js
+++ b/src/core/util/core.js
@@ -48,10 +48,11 @@ export function isFn(obj) {
/**
* Check if url is external
- * @param {String} string url
+ * @param {String} url url
* @returns {Boolean} True if the passed-in url is external
*/
export function isExternal(url) {
+ /** @type {any} */
const match = url.match(
/^([^:/?#]+:)?(?:\/{2,}([^/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/,
);
diff --git a/src/core/util/dom.js b/src/core/util/dom.js
index 3bd794ee5b..84f1bb5299 100644
--- a/src/core/util/dom.js
+++ b/src/core/util/dom.js
@@ -1,12 +1,13 @@
import { isFn } from '../util/core.js';
+/** @type {Record} */
const cacheNode = {};
/**
* Get Node
* @param {String|Element} el A DOM element
* @param {Boolean} noCache Flag to use or not use the cache
- * @return {HTMLElement | SVGElement} The found node element
+ * @return {Element} The found node element
*/
export function getNode(el, noCache = false) {
if (typeof el === 'string') {
@@ -26,6 +27,11 @@ export function getNode(el, noCache = false) {
* @param {*} content the content to be rendered as HTML
* @param {*} replace To replace the content (true) or insert instead (false) , default is false
*/
+/**
+ * @param {string|Element} el
+ * @param {string} content
+ * @param {boolean} [replace]
+ */
export function setHTML(el, content, replace) {
const node = getNode(el);
if (node) {
@@ -40,54 +46,87 @@ export const body = $.body;
export const head = $.head;
/**
- * Find elements
- * @param {String|Element} el The root element where to perform the search from
- * @param {Element} node The query
+ * Find the first matching element
+ * @param {string|Element} el The root element on which to perform the query
+ * from, or a query string to query from `document`.
+ * @param {string} [query] The query string to use on `el` if `el` is an
+ * element.
* @returns {Element} The found DOM element
* @example
* find('nav') => document.querySelector('nav')
* find(nav, 'a') => nav.querySelector('a')
*/
-export function find(el, node) {
- return node ? el.querySelector(node) : $.querySelector(el);
+export function find(el, query = ':is()') {
+ return /** @type {Element} */ (
+ typeof el !== 'string' ? el.querySelector(query) : $.querySelector(el)
+ );
}
/**
- * Find all elements
- * @param {String|Element} el The root element where to perform the search from
- * @param {Element} node The query
+ * Find all matching elements
+ * @param {string|Element} el The root element on which to perform the query
+ * from, or a query string to query from `document`.
+ * @param {string} [query] The query string to use on `el` if `el` is an
+ * element.
* @returns {Array} An array of DOM elements
* @example
* findAll('a') => Array.from(document.querySelectorAll('a'))
* findAll(nav, 'a') => Array.from(nav.querySelectorAll('a'))
*/
-export function findAll(el, node) {
- return Array.from(node ? el.querySelectorAll(node) : $.querySelectorAll(el));
+export function findAll(el, query = ':is()') {
+ return Array.from(
+ typeof el !== 'string'
+ ? el.querySelectorAll(query)
+ : $.querySelectorAll(el),
+ );
}
+/**
+ * @param {string} node
+ * @param {string} [tpl]
+ * @returns {HTMLElement}
+ */
export function create(node, tpl) {
- node = $.createElement(node);
+ const element = $.createElement(node);
if (tpl) {
- node.innerHTML = tpl;
+ element.innerHTML = tpl;
}
- return node;
+ return element;
}
+/**
+ * @param {Element} target
+ * @param {Element} el
+ */
export function appendTo(target, el) {
return target.appendChild(el);
}
+/**
+ * @param {Element} target
+ * @param {Element} el
+ */
export function before(target, el) {
return target.insertBefore(el, target.children[0]);
}
+/**
+ * @param {any} el
+ * @param {any} type
+ * @param {any} [handler]
+ */
export function on(el, type, handler) {
isFn(type)
? window.addEventListener(el, type)
: el.addEventListener(type, handler);
}
+/**
+ * @param {any} el
+ * @param {any} type
+ * @param {any} [handler]
+ */
export function off(el, type, handler) {
isFn(type)
? window.removeEventListener(el, type)
@@ -95,27 +134,16 @@ export function off(el, type, handler) {
}
/**
- * Toggle class
- * @param {Element|null} el The element that needs the class to be toggled
- * @param {string} type The type of action to be performed on the classList (toggle by default)
- * @param {String} val Name of the class to be toggled
- * @void
- * @example
- * toggleClass(el, 'active') => el.classList.toggle('active')
- * toggleClass(el, 'add', 'active') => el.classList.add('active')
+ * @param {string} content
*/
-export function toggleClass(el, type, val) {
- el && el.classList[val ? type : 'toggle'](val || type);
-}
-
export function style(content) {
- appendTo(head, create('style', content));
+ appendTo(head, /** @type {Element} */ (create('style', content)));
}
/**
* Fork https://github.com/bendrucker/document-ready/blob/master/index.js
- * @param {Function} callback The callbacack to be called when the page is loaded
- * @returns {Number|void} If the page is already loaded returns the result of the setTimeout callback,
+ * @param {(event: Event) => void} callback The callbacack to be called when the page is loaded
+ * @returns {number|void} If the page is already loaded returns the result of the setTimeout callback,
* otherwise it only attaches the callback to the DOMContentLoaded event
*/
export function documentReady(callback, doc = document) {
diff --git a/src/core/util/prism.js b/src/core/util/prism.js
index 86ae9ab585..3f381701ee 100644
--- a/src/core/util/prism.js
+++ b/src/core/util/prism.js
@@ -1,4 +1,4 @@
-import Prism from 'prismjs';
+import * as Prism from 'prismjs';
/**
*
* The dependencies map which syncs from
@@ -292,7 +292,7 @@ const prettryPrint = (depTree, level) => {
let cur = `${' '.repeat(level * 3)} ${depTree.cur} ${depTree.loaded ? '(+)' : '(-)'}`;
if (depTree.dependencies.length) {
depTree.dependencies.forEach(dep => {
- cur += prettryPrint(dep, level + 1, cur);
+ cur += prettryPrint(dep, level + 1);
});
}
return '\n' + cur;
diff --git a/src/core/virtual-routes/index.js b/src/core/virtual-routes/index.js
index faea43b9c0..a164d57a39 100644
--- a/src/core/virtual-routes/index.js
+++ b/src/core/virtual-routes/index.js
@@ -4,7 +4,7 @@ import { createNextFunction } from './next.js';
/** @typedef {import('../Docsify.js').Constructor} Constructor */
/** @typedef {Record} VirtualRoutesMap */
-/** @typedef {(route: string, match: RegExpMatchArray | null) => string | void | Promise } VirtualRouteHandler */
+/** @typedef {(route: string, match: RegExpMatchArray | null, next?: (content: string | void | Promise) => void) => string | void | Promise } VirtualRouteHandler */
/**
* Allows users/plugins to introduce dynamically created content into their docsify
@@ -25,7 +25,7 @@ import { createNextFunction } from './next.js';
* }
* ```
*
- * @template {!Constructor} T
+ * @template {Constructor} T
* @param {T} Base - The class to extend
*/
export function VirtualRoutes(Base) {
@@ -41,12 +41,13 @@ export function VirtualRoutes(Base) {
/**
* Attempts to match the given path with a virtual route.
* @param {string} path the path of the route to match
- * @returns {Promise} resolves to string if route was matched, otherwise null
+ * @returns {PromiseLike} resolves to string if route was matched, otherwise null
*/
matchVirtualRoute(path) {
const virtualRoutes = this.routes();
const virtualRoutePaths = Object.keys(virtualRoutes);
+ /** @type {(value: string | null) => any} */
let done = () => null;
/**
@@ -101,7 +102,9 @@ export function VirtualRoutes(Base) {
}
return {
+ // @ts-expect-error types are screwed here
then(cb) {
+ // @ts-expect-error types are screwed here
done = cb;
asyncMatchNextRoute();
},
diff --git a/src/core/virtual-routes/next.js b/src/core/virtual-routes/next.js
index ec53ece46f..21d5c57b82 100644
--- a/src/core/virtual-routes/next.js
+++ b/src/core/virtual-routes/next.js
@@ -9,7 +9,7 @@
*/
export function createNextFunction() {
/** @type {CB} */
- let storedCb = () => null;
+ let storedCb = () => {};
function next(value) {
storedCb(value);
diff --git a/src/plugins/disqus.js b/src/plugins/disqus.js
index dd19d5c6b4..b6b38681b2 100644
--- a/src/plugins/disqus.js
+++ b/src/plugins/disqus.js
@@ -3,6 +3,8 @@ if (fixedPath !== location.href) {
location.href = fixedPath;
}
+const window = /** @type {any} */ (globalThis);
+
function install(hook, vm) {
const dom = Docsify.dom;
const disqus = vm.config.disqus;
@@ -11,11 +13,11 @@ function install(hook, vm) {
}
hook.init(_ => {
- const script = dom.create('script');
+ const script = /** @type {HTMLScriptElement} */ (dom.create('script'));
script.async = true;
script.src = `https://${disqus}.disqus.com/embed.js`;
- script.setAttribute('data-timestamp', Number(new Date()));
+ script.setAttribute('data-timestamp', String(Number(new Date())));
dom.appendTo(dom.body, script);
});
@@ -48,4 +50,6 @@ function install(hook, vm) {
}
window.$docsify = window.$docsify || {};
-$docsify.plugins = [install, ...($docsify.plugins || [])];
+window.$docsify.plugins = [install, ...(window.$docsify.plugins || [])];
+
+export {};
diff --git a/src/plugins/emoji.js b/src/plugins/emoji.js
index 7d71a9980d..10f9fa0eb7 100644
--- a/src/plugins/emoji.js
+++ b/src/plugins/emoji.js
@@ -1,5 +1,7 @@
import emojiData from '../core/render/emoji-data.js';
+const window = /** @type {any} */ (globalThis);
+
// Deprecation notice
if (window && window.console) {
// eslint-disable-next-line no-console
diff --git a/src/plugins/external-script.js b/src/plugins/external-script.js
index fcdeca7069..992790fc8a 100644
--- a/src/plugins/external-script.js
+++ b/src/plugins/external-script.js
@@ -1,6 +1,10 @@
+const window = /** @type {any} */ (globalThis);
+
function handleExternalScript() {
const container = Docsify.dom.getNode('#main');
- const scripts = Docsify.dom.findAll(container, 'script');
+ const scripts = /** @type {HTMLScriptElement[]} */ (
+ Docsify.dom.findAll(container, 'script')
+ );
for (const script of scripts) {
if (script.src) {
@@ -10,8 +14,8 @@ function handleExternalScript() {
newScript[attribute.name] = attribute.value;
});
- script.parentNode.insertBefore(newScript, script);
- script.parentNode.removeChild(script);
+ script.before(newScript);
+ script.remove();
}
}
}
@@ -21,4 +25,6 @@ const install = function (hook) {
};
window.$docsify = window.$docsify || {};
-$docsify.plugins = [install, ...($docsify.plugins || [])];
+window.$docsify.plugins = [install, ...(window.$docsify.plugins || [])];
+
+export {};
diff --git a/src/plugins/front-matter/index.js b/src/plugins/front-matter/index.js
index ffe7986fcb..1764698117 100644
--- a/src/plugins/front-matter/index.js
+++ b/src/plugins/front-matter/index.js
@@ -19,4 +19,4 @@ const install = function (hook, vm) {
};
window.$docsify = window.$docsify || {};
-$docsify.plugins = [install, ...($docsify.plugins || [])];
+window.$docsify.plugins = [install, ...(window.$docsify?.plugins || [])];
diff --git a/src/plugins/front-matter/yaml.js b/src/plugins/front-matter/yaml.js
index 3ac8523b57..83c33d657d 100644
--- a/src/plugins/front-matter/yaml.js
+++ b/src/plugins/front-matter/yaml.js
@@ -1,3 +1,4 @@
+// @ts-nocheck
/**
* Forked from https://github.com/egoist/docute/blob/master/src/utils/yaml.js
*/
@@ -54,7 +55,6 @@ var errors = [],
/**
* @class A block of lines of a given level.
* @param {int} lvl The block's level.
- * @private
*/
function Block(lvl) {
return {
diff --git a/src/plugins/ga.js b/src/plugins/ga.js
index c1c130c110..4716aebdab 100644
--- a/src/plugins/ga.js
+++ b/src/plugins/ga.js
@@ -6,6 +6,8 @@ function appendScript() {
document.body.appendChild(script);
}
+const window = /** @type {any} */ (globalThis);
+
function init(id) {
appendScript();
window.ga =
@@ -20,15 +22,14 @@ function init(id) {
function collect() {
if (!window.ga) {
- init($docsify.ga);
+ init(window.$docsify.ga);
}
-
window.ga('set', 'page', location.hash);
window.ga('send', 'pageview');
}
const install = function (hook) {
- if (!$docsify.ga) {
+ if (!window.$docsify.ga) {
// eslint-disable-next-line no-console
console.error('[Docsify] ga is required.');
return;
@@ -38,4 +39,6 @@ const install = function (hook) {
};
window.$docsify = window.$docsify || {};
-$docsify.plugins = [install, ...($docsify.plugins || [])];
+window.$docsify.plugins = [install, ...(window.$docsify?.plugins || [])];
+
+export {};
diff --git a/src/plugins/gitalk.js b/src/plugins/gitalk.js
index 627dcef3b0..abf357442c 100644
--- a/src/plugins/gitalk.js
+++ b/src/plugins/gitalk.js
@@ -1,4 +1,5 @@
-/* global gitalk */
+const window = /** @type {any} */ (globalThis);
+
function install(hook) {
const dom = Docsify.dom;
@@ -11,14 +12,18 @@ function install(hook) {
});
hook.doneEach(_ => {
- const el = document.getElementById('gitalk-container');
+ const el = /** @type {HTMLElement} */ (
+ document.getElementById('gitalk-container')
+ );
while (el.hasChildNodes()) {
- el.removeChild(el.firstChild);
+ el.removeChild(/** @type {Node} */ (el.firstChild));
}
- gitalk.render('gitalk-container');
+ window.gitalk.render('gitalk-container');
});
}
window.$docsify = window.$docsify || {};
-$docsify.plugins = [install, ...($docsify.plugins || [])];
+window.$docsify.plugins = [install, ...(window.$docsify.plugins || [])];
+
+export {};
diff --git a/src/plugins/gtag.js b/src/plugins/gtag.js
index aae78b6fa9..2a4298af11 100644
--- a/src/plugins/gtag.js
+++ b/src/plugins/gtag.js
@@ -7,6 +7,8 @@ function appendScript(id) {
document.body.appendChild(script);
}
+const window = /** @type {any} */ (globalThis);
+
// global site tag instance initialized
function initGlobalSiteTag(id) {
appendScript(id);
@@ -46,7 +48,7 @@ function init(ids) {
function collect() {
if (!window.gtag) {
- init($docsify.gtag);
+ init(window.$docsify.gtag);
}
// usage: https://developers.google.com/analytics/devguides/collection/gtagjs/pages
@@ -58,7 +60,7 @@ function collect() {
}
const install = function (hook) {
- if (!$docsify.gtag) {
+ if (!window.$docsify.gtag) {
// eslint-disable-next-line no-console
console.error('[Docsify] gtag is required.');
return;
@@ -68,4 +70,6 @@ const install = function (hook) {
};
window.$docsify = window.$docsify || {};
-$docsify.plugins = [install, ...($docsify.plugins || [])];
+window.$docsify.plugins = [install, ...(window.$docsify.plugins || [])];
+
+export {};
diff --git a/src/plugins/matomo.js b/src/plugins/matomo.js
index e4ed911ce5..478e31dc5d 100644
--- a/src/plugins/matomo.js
+++ b/src/plugins/matomo.js
@@ -5,6 +5,8 @@ function appendScript(options) {
document.body.appendChild(script);
}
+const window = /** @type {any} */ (globalThis);
+
function init(options) {
window._paq = window._paq || [];
window._paq.push(['trackPageView']);
@@ -18,16 +20,15 @@ function init(options) {
function collect() {
if (!window._paq) {
- init($docsify.matomo);
+ init(window.$docsify.matomo);
}
-
window._paq.push(['setCustomUrl', window.location.hash.substr(1)]);
window._paq.push(['setDocumentTitle', document.title]);
window._paq.push(['trackPageView']);
}
const install = function (hook) {
- if (!$docsify.matomo) {
+ if (!window.$docsify.matomo) {
// eslint-disable-next-line no-console
console.error('[Docsify] matomo is required.');
return;
@@ -37,4 +38,6 @@ const install = function (hook) {
};
window.$docsify = window.$docsify || {};
-$docsify.plugins = [install, ...($docsify.plugins || [])];
+window.$docsify.plugins = [install, ...(window.$docsify.plugins || [])];
+
+export {};
diff --git a/src/plugins/search/component.js b/src/plugins/search/component.js
index a382f8e7c8..8d447c9cb1 100644
--- a/src/plugins/search/component.js
+++ b/src/plugins/search/component.js
@@ -21,8 +21,10 @@ function tpl(vm, defaultValue = '') {
`;
const sidebarElm = Docsify.dom.find('.sidebar');
const searchElm = Docsify.dom.create('section', html);
- const insertElm = sidebarElm.querySelector(
- `:scope ${insertAfter || insertBefore || '> :first-child'}`,
+ const insertElm = /** @type {HTMLElement} */ (
+ sidebarElm.querySelector(
+ `:scope ${insertAfter || insertBefore || '> :first-child'}`,
+ )
);
searchElm.classList.add('search');
@@ -69,7 +71,9 @@ function doSearch(value) {
function bindEvents() {
const $search = Docsify.dom.find('.search');
- const $input = Docsify.dom.find($search, 'input');
+ const $input = /** @type {HTMLInputElement} */ (
+ Docsify.dom.find($search, 'input')
+ );
const $clear = Docsify.dom.find($search, '.clear-button');
let timeId;
@@ -90,7 +94,10 @@ function bindEvents() {
);
Docsify.dom.on($input, 'input', e => {
clearTimeout(timeId);
- timeId = setTimeout(_ => doSearch(e.target.value.trim()), 100);
+ timeId = setTimeout(
+ _ => doSearch(/** @type {HTMLInputElement} */ (e.target).value.trim()),
+ 100,
+ );
});
Docsify.dom.on($clear, 'click', e => {
$input.value = '';
@@ -99,7 +106,9 @@ function bindEvents() {
}
function updatePlaceholder(text, path) {
- const $input = Docsify.dom.getNode('.search input[type="search"]');
+ const $input = /** @type {HTMLInputElement | null} */ (
+ Docsify.dom.getNode('.search input[type="search"]')
+ );
if (!$input) {
return;
diff --git a/src/plugins/search/index.js b/src/plugins/search/index.js
index 157fa43c44..f6ef34df01 100644
--- a/src/plugins/search/index.js
+++ b/src/plugins/search/index.js
@@ -4,6 +4,19 @@ import {
} from './component.js';
import { init as initSearch } from './search.js';
+/**
+ * @type {{
+ * placeholder: string;
+ * noData: string;
+ * paths: string[] | 'auto';
+ * depth: number;
+ * maxAge: number;
+ * namespace?: string;
+ * pathNamespaces?: RegExp | string[];
+ * keyBindings: string[];
+ * insertAfter?: string;
+ * insertBefore?: string;
+ * }} */
const CONFIG = {
placeholder: 'Type to search',
noData: 'No Results!',
@@ -45,9 +58,14 @@ const install = function (hook, vm) {
bindings: CONFIG.keyBindings,
callback(e) {
const sidebarElm = document.querySelector('.sidebar');
- const sidebarToggleElm = document.querySelector('.sidebar-toggle');
- const searchElm = sidebarElm?.querySelector('input[type="search"]');
- const isSidebarHidden = sidebarElm?.getBoundingClientRect().x < 0;
+ const sidebarToggleElm = /** @type {HTMLElement} */ (
+ document.querySelector('.sidebar-toggle')
+ );
+ const searchElm = /** @type {HTMLInputElement | null} */ (
+ sidebarElm?.querySelector('input[type="search"]')
+ );
+ const isSidebarHidden =
+ (sidebarElm?.getBoundingClientRect().x ?? 0) < 0;
isSidebarHidden && sidebarToggleElm?.click();
@@ -67,4 +85,4 @@ const install = function (hook, vm) {
};
window.$docsify = window.$docsify || {};
-$docsify.plugins = [install, ...($docsify.plugins || [])];
+window.$docsify.plugins = [install, ...(window.$docsify.plugins || [])];
diff --git a/src/plugins/search/markdown-to-txt.js b/src/plugins/search/markdown-to-txt.js
index c98459f2f8..4068c071b9 100644
--- a/src/plugins/search/markdown-to-txt.js
+++ b/src/plugins/search/markdown-to-txt.js
@@ -79,7 +79,9 @@ const markdownToTxtRenderer = {
listitem(item) {
let itemBody = '';
if (item.task) {
- const checkbox = this.checkbox?.({ checked: !!item.checked });
+ const checkbox = this.checkbox?.({
+ checked: !!item.checked,
+ });
if (item.loose) {
if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') {
item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
@@ -108,7 +110,11 @@ const markdownToTxtRenderer = {
return `${itemBody || ''}`;
},
- checkbox() {
+ /**
+ * @param {{checked: boolean}} options
+ * @return {string}
+ */
+ checkbox(options) {
return '';
},
@@ -185,7 +191,10 @@ const markdownToTxtRenderer = {
: token.text || '';
},
};
-const _marked = marked.setOptions({ renderer: markdownToTxtRenderer });
+const _marked = marked.setOptions({
+ // @ts-expect-error missing properties
+ renderer: markdownToTxtRenderer,
+});
export function markdownToTxt(markdown) {
const unmarked = _marked.parse(markdown);
diff --git a/src/plugins/search/search.js b/src/plugins/search/search.js
index bb0dfb55d6..6e3b2e0704 100644
--- a/src/plugins/search/search.js
+++ b/src/plugins/search/search.js
@@ -18,17 +18,22 @@ async function saveData(maxAge, expireKey) {
INDEXES = Object.values(INDEXES).flatMap(innerData =>
Object.values(innerData),
);
- await db.search.bulkPut(INDEXES);
- await db.expires.put({ key: expireKey, value: Date.now() + maxAge });
+ await /** @type {any} */ (db).search.bulkPut(INDEXES);
+ await /** @type {any} */ (db).expires.put({
+ key: expireKey,
+ value: Date.now() + maxAge,
+ });
}
async function getData(key, isExpireKey = false) {
if (isExpireKey) {
- const item = await db.expires.get(key);
+ const item = await /** @type {any} */ (db).expires.get(key);
return item ? item.value : 0;
}
- const item = await db.search.where({ indexKey: key }).toArray();
+ const item = await /** @type {any} */ (db).search
+ .where({ indexKey: key })
+ .toArray();
return item ? item : null;
}
@@ -67,8 +72,10 @@ function getAllPaths(router) {
Docsify.dom
.findAll('.sidebar-nav a:not(.section-link):not([data-nosearch])')
.forEach(node => {
- const href = node.href;
- const originHref = node.getAttribute('href');
+ const href = /** @type {HTMLAnchorElement} */ (node).href;
+ const originHref = /** @type {HTMLAnchorElement} */ (node).getAttribute(
+ 'href',
+ );
const path = router.parse(href).path;
if (
@@ -103,7 +110,8 @@ function getListData(token) {
export function genIndex(path, content = '', router, depth, indexKey) {
const tokens = window.marked.lexer(content);
const slugify = window.Docsify.slugify;
- const index = [];
+ /** @type {Record} */
+ const index = {};
let slug;
let title = '';
@@ -131,7 +139,7 @@ export function genIndex(path, content = '', router, depth, indexKey) {
index[slug] = {
slug,
title: path !== '/' ? path.slice(1) : 'Home Page',
- body: markdownToTxt(token.text || ''),
+ body: markdownToTxt(/** @type {any} */ (token).text || ''),
path: path,
indexKey: indexKey,
};
@@ -144,14 +152,20 @@ export function genIndex(path, content = '', router, depth, indexKey) {
if (!index[slug]) {
index[slug] = { slug, title: '', body: '' };
} else if (index[slug].body) {
+ // @ts-expect-error
token.text = getTableData(token);
+ // @ts-expect-error
token.text = getListData(token);
+ // @ts-expect-error
index[slug].body += '\n' + markdownToTxt(token.text || '');
} else {
+ // @ts-expect-error
token.text = getTableData(token);
+ // @ts-expect-error
token.text = getListData(token);
+ // @ts-expect-error
index[slug].body = markdownToTxt(token.text || '');
}
diff --git a/src/plugins/zoom-image.js b/src/plugins/zoom-image.js
index 52e1ebef7e..6b4d0e8f5b 100644
--- a/src/plugins/zoom-image.js
+++ b/src/plugins/zoom-image.js
@@ -4,10 +4,12 @@ function install(hook) {
let zoom;
hook.doneEach(_ => {
- let elms = Array.from(
- document.querySelectorAll(
- '.markdown-section img:not(.emoji):not([data-no-zoom])',
- ),
+ let elms = /** @type {HTMLElement[]} */ (
+ Array.from(
+ document.querySelectorAll(
+ '.markdown-section img:not(.emoji):not([data-no-zoom])',
+ ),
+ )
);
Docsify.dom.style(
@@ -25,4 +27,4 @@ function install(hook) {
}
window.$docsify = window.$docsify || {};
-$docsify.plugins = [install, ...($docsify.plugins || [])];
+window.$docsify.plugins = [install, ...(window.$docsify.plugins || [])];
diff --git a/test/consume-types/README.md b/test/consume-types/README.md
new file mode 100644
index 0000000000..2fd30ae1ba
--- /dev/null
+++ b/test/consume-types/README.md
@@ -0,0 +1,32 @@
+# Vanilla ESM TypeScript Example
+
+This is an example of a project that imports Docsify via plain ESM in browser,
+with type checking with TypeScript to prove that type definitions are working.
+
+This ensures that downstream projects that consume Docsify via ESM get
+intellisense in their IDE (f.e. `Go To Definition` in VS Code will take the user
+to Docsify source code).
+
+This example is not using a build tool, but relying on native ESM in the
+browser, using an `importmap` script (`
+
+
+
+
+
+
+
+
+
diff --git a/test/consume-types/package-lock.json b/test/consume-types/package-lock.json
new file mode 100644
index 0000000000..01e333b6ec
--- /dev/null
+++ b/test/consume-types/package-lock.json
@@ -0,0 +1,2276 @@
+{
+ "name": "consume-types",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "dependencies": {
+ "docsify": "file:../../"
+ },
+ "devDependencies": {
+ "five-server": "^0.4.5",
+ "typescript": "^5.9.3"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@html-validate/stylish": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/@html-validate/stylish/-/stylish-4.3.0.tgz",
+ "integrity": "sha512-eUfvKpRJg5TvzSfTf2EovrQoTKjkRnPUOUnXVJ2cQ4GbC/bQw98oxN+DdSf+HxOBK00YOhsP52xWdJPV1o4n5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "kleur": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "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",
+ "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"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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/@sidvind/better-ajv-errors": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-2.1.3.tgz",
+ "integrity": "sha512-lWuod/rh7Xz5uXiEGSfm2Sd5PG7K/6yJfoAZVqzsEswjPJhUz15R7Gn/o8RczA041QS15hBd/BCSeu9vwPArkA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@babel/code-frame": "^7.16.0",
+ "chalk": "^4.1.0"
+ },
+ "engines": {
+ "node": ">= 16.14"
+ },
+ "peerDependencies": {
+ "ajv": "4.11.8 - 8"
+ }
+ },
+ "node_modules/@types/node": {
+ "version": "24.10.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
+ "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
+ "node_modules/@types/node-forge": {
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.14.tgz",
+ "integrity": "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@yandeu/open-cjs": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@yandeu/open-cjs/-/open-cjs-0.0.3.tgz",
+ "integrity": "sha512-TbwJoKM8vybqT/8PjISQO+uS2Bjkn9kQm2Sd/eGmiacZe5DBO7teSejwq45TxjMSbFf64yP06nvjqefmpk+gdA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "^3.0.0",
+ "negotiator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/accepts/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/accepts/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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "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",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "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",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.3.4",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
+ "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "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/body-parser": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
+ "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "^3.1.2",
+ "content-type": "^1.0.5",
+ "debug": "^4.4.0",
+ "http-errors": "^2.0.0",
+ "iconv-lite": "^0.6.3",
+ "on-finished": "^2.4.1",
+ "qs": "^6.14.0",
+ "raw-body": "^3.0.0",
+ "type-is": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "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/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/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "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/common-tags": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz",
+ "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.6.0"
+ }
+ },
+ "node_modules/cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "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",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/css-select": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+ "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.0.1",
+ "domhandler": "^4.3.1",
+ "domutils": "^2.8.0",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
+ "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/dexie": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/dexie/-/dexie-4.2.1.tgz",
+ "integrity": "sha512-Ckej0NS6jxQ4Po3OrSQBFddayRhTCic2DoCAG5zacOfOVB9P2Q5Xc5uL/nVa7ZVs+HdMnvUPzLFCB/JwpB6Csg==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/docsify": {
+ "version": "5.0.0-rc.3",
+ "resolved": "file:../..",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "common-tags": "^1.8.0",
+ "dexie": "^4.0.11",
+ "marked": "^16.0.0",
+ "medium-zoom": "^1.1.0",
+ "opencollective-postinstall": "^2.0.2",
+ "prismjs": "^1.29.0",
+ "tinydate": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=20.11.0"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+ "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.2.0",
+ "entities": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/domhandler": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+ "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^2.2.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+ "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^1.0.1",
+ "domelementtype": "^2.2.0",
+ "domhandler": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "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/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/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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,
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "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/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/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/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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "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": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "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": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
+ "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/finalhandler": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
+ "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "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": ">= 0.8"
+ }
+ },
+ "node_modules/five-server": {
+ "version": "0.4.5",
+ "resolved": "https://registry.npmjs.org/five-server/-/five-server-0.4.5.tgz",
+ "integrity": "sha512-EVAuwMdXHuRsHXG/jUthQtLP6G0j7MbkgaU1AXJInkJUOuNVz0vEVGe87YgdrA+mZE1ZVHFUmdi+B78kRFNQqw==",
+ "dev": true,
+ "license": "SEE LICENSE IN LICENSE",
+ "dependencies": {
+ "@yandeu/open-cjs": "^0.0.3",
+ "chokidar": "^4.0.3",
+ "cors": "^2.8.5",
+ "debug": "^4.3.1",
+ "express": "^5.1.0",
+ "html-validate": "^7.1.1",
+ "mime-types": "~2.1.24",
+ "node-html-parser": "~5.4.1",
+ "parseurl": "~1.3.3",
+ "selfsigned": "^2.0.0",
+ "ws": "^8.2.0"
+ },
+ "bin": {
+ "five-server": "lib/bin.js",
+ "live-server": "lib/bin.js"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/yandeu"
+ }
+ },
+ "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,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "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/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/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/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",
+ "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/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/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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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/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/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_modules/html-validate": {
+ "version": "7.18.1",
+ "resolved": "https://registry.npmjs.org/html-validate/-/html-validate-7.18.1.tgz",
+ "integrity": "sha512-K5jb0h/xAoeR8sJqyR0n/QaKL7rdT88sPCtN+Pvtyn5JUU+nidQe2gBB09WRzPTcQtPXBj4QxBUH5IA2tt8JQg==",
+ "dev": true,
+ "license": "MIT",
+ "workspaces": [
+ "docs"
+ ],
+ "dependencies": {
+ "@babel/code-frame": "^7.10.0",
+ "@html-validate/stylish": "^4.0.1",
+ "@sidvind/better-ajv-errors": "^2.0.0",
+ "acorn-walk": "^8.0.0",
+ "ajv": "^8.0.0",
+ "deepmerge": "^4.2.0",
+ "espree": "^9.0.0",
+ "glob": "^10.0.0",
+ "ignore": "^5.0.0",
+ "kleur": "^4.1.0",
+ "minimist": "^1.2.0",
+ "prompts": "^2.0.0",
+ "semver": "^7.0.0"
+ },
+ "bin": {
+ "html-validate": "bin/html-validate.js"
+ },
+ "engines": {
+ "node": ">= 14.0"
+ },
+ "peerDependencies": {
+ "jest": "^25.1 || ^26 || ^27.1 || ^28.1.3 || ^29.0.3",
+ "jest-diff": "^25.1 || ^26 || ^27.1 || ^28.1.3 || ^29.0.3",
+ "jest-snapshot": "^25.1 || ^26 || ^27.1 || ^28.1.3 || ^29.0.3"
+ },
+ "peerDependenciesMeta": {
+ "jest": {
+ "optional": true
+ },
+ "jest-diff": {
+ "optional": true
+ },
+ "jest-snapshot": {
+ "optional": true
+ }
+ }
+ },
+ "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==",
+ "dev": true,
+ "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/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "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/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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"
+ },
+ "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/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/kleur": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
+ "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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/marked": {
+ "version": "16.4.2",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz",
+ "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==",
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 20"
+ }
+ },
+ "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/media-typer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+ "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/medium-zoom": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/medium-zoom/-/medium-zoom-1.1.0.tgz",
+ "integrity": "sha512-ewyDsp7k4InCUp3jRmwHBRFGyjBimKps/AJLjRSox+2q/2H4p/PNpQf+pwONWlJiOudkBXtbdmVbFjqyybfTmQ==",
+ "license": "MIT"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "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",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "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/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "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/negotiator": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/node-forge": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
+ "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
+ "dev": true,
+ "license": "(BSD-3-Clause OR GPL-2.0)",
+ "engines": {
+ "node": ">= 6.13.0"
+ }
+ },
+ "node_modules/node-html-parser": {
+ "version": "5.4.2",
+ "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-5.4.2.tgz",
+ "integrity": "sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "css-select": "^4.2.1",
+ "he": "1.2.0"
+ }
+ },
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
+ "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/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/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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "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/opencollective-postinstall": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
+ "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==",
+ "license": "MIT",
+ "bin": {
+ "opencollective-postinstall": "index.js"
+ }
+ },
+ "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/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "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": ">=8"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "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/path-to-regexp": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz",
+ "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "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/prismjs": {
+ "version": "1.30.0",
+ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz",
+ "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/prompts": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
+ "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "kleur": "^3.0.3",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/prompts/node_modules/kleur": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
+ "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "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/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz",
+ "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.7.0",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/raw-body/node_modules/iconv-lite": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz",
+ "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.18.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/router": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
+ "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+ "dev": true,
+ "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": ">= 18"
+ }
+ },
+ "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/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/selfsigned": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz",
+ "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node-forge": "^1.3.0",
+ "node-forge": "^1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/send": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
+ "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
+ "dev": true,
+ "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": ">= 18"
+ }
+ },
+ "node_modules/send/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/send/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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "parseurl": "^1.3.3",
+ "send": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "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": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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": ">=8"
+ }
+ },
+ "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/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/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/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/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",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/sisteransi": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
+ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "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,
+ "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_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,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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,
+ "license": "MIT"
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tinydate": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/tinydate/-/tinydate-1.3.0.tgz",
+ "integrity": "sha512-7cR8rLy2QhYHpsBDBVYnnWXm8uRTr38RoZakFSW7Bs7PzfMPNZthuMLkwqZv7MTu8lhQ91cOFYS5a7iFj2oR3w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^1.0.5",
+ "media-typer": "^1.1.0",
+ "mime-types": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/type-is/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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/type-is/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==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "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==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "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": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "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/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",
+ "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/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_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/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,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "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/ws": {
+ "version": "8.18.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
+ "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
+ "dev": true,
+ "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
+ }
+ }
+ }
+ }
+}
diff --git a/test/consume-types/package.json b/test/consume-types/package.json
new file mode 100644
index 0000000000..83ab9314f4
--- /dev/null
+++ b/test/consume-types/package.json
@@ -0,0 +1,13 @@
+{
+ "scripts": {
+ "typecheck": "tsc --noEmit",
+ "serve": "five-server . --open=false --ignorePattern=node_modules"
+ },
+ "dependencies": {
+ "docsify": "file:../../"
+ },
+ "devDependencies": {
+ "five-server": "^0.4.5",
+ "typescript": "^5.9.3"
+ }
+}
diff --git a/test/consume-types/prism.js b/test/consume-types/prism.js
new file mode 100644
index 0000000000..9fa1050498
--- /dev/null
+++ b/test/consume-types/prism.js
@@ -0,0 +1,27 @@
+// Small ESM wrapper, used in the importmap for 'prismjs', to import PrismJS by
+// tricking its CommonJS format to see module.exports.
+
+const __prismCjs = await fetch('/node_modules/prismjs/prism.js').then(res =>
+ res.text(),
+);
+
+// Emulate CommonJS environment
+const module = { exports: {} };
+// eslint-disable-next-line no-unused-vars
+const exports = module.exports;
+
+// Evaluate the CommonJS code with module.exports in scope
+eval(__prismCjs);
+
+// Export the Prism object
+const _Prism = module.exports;
+export default _Prism;
+
+// Also make Prism global because Docsify expects it by importing from
+// 'prismjs/components/prism-markup-templating.js' in the compiler.
+// @ts-expect-error FIXME get rid of this ugly global dependency hack in Docsify.
+window.Prism = _Prism;
+
+// @ts-expect-error
+// eslint-disable-next-line no-undef
+console.log('Prism loaded:', Prism);
diff --git a/test/consume-types/register-sw.js b/test/consume-types/register-sw.js
new file mode 100644
index 0000000000..566c14d0bf
--- /dev/null
+++ b/test/consume-types/register-sw.js
@@ -0,0 +1,86 @@
+// This whole thing is ugly. It is only so that we can fix improper import
+// statements in libraries from node_modules. See sw.js for how we re-map the
+// import URLs.
+// The convoluted code here is so that we will force the app to use a new
+// service worker if in dev mode we update the service worker code in sw.js
+
+let reloadQueued = false;
+function queueReload() {
+ if (reloadQueued) {
+ return;
+ }
+ reloadQueued = true;
+ // Use location.reload() after a so any late controllerchange still settles.
+ window.location.reload();
+}
+
+// Register first.
+const registration = await navigator.serviceWorker.register('/sw.js', {
+ scope: '/',
+ type: 'module',
+ updateViaCache: 'none',
+});
+
+// If there is already a waiting worker, wait for it to claim this client.
+if (registration.waiting) {
+ const sw = registration.waiting;
+
+ await new Promise(resolve => {
+ sw.addEventListener('statechange', () => {
+ // Wait until activated.
+ if (sw.state === 'activated') {
+ if (sw === navigator.serviceWorker.controller) {
+ resolve(void 0);
+ } else {
+ // If the new SW activated but is not controlling yet, it changed? Not sure if this can actuall happen.
+ queueReload();
+ }
+ }
+ });
+ });
+}
+
+if (navigator.serviceWorker.controller) {
+ navigator.serviceWorker.addEventListener('controllerchange', () => {
+ queueReload();
+ });
+} else {
+ // First-ever load: wait for controllerchange
+ await new Promise(resolve => {
+ navigator.serviceWorker.addEventListener('controllerchange', resolve, {
+ once: true,
+ });
+ });
+}
+
+// Track new installs.
+registration.addEventListener('updatefound', () => {
+ const sw = registration.installing;
+ if (!sw) {
+ return;
+ }
+ sw.addEventListener('statechange', () => {
+ // When installed AND there is already a controller, a reload will let new SW control.
+ if (sw.state === 'installed' && navigator.serviceWorker.controller) {
+ // If skipWaiting ran inside new SW, controllerchange may fire soon; still queue.
+ queueReload();
+ }
+ });
+});
+
+// Force update check after listeners.
+// For purposes of testing the example, force update on page load so we
+// always test the latest service worker.
+await registration.update();
+
+// Wait until there is an active worker (first load).
+await navigator.serviceWorker.ready;
+
+if (reloadQueued) {
+ await new Promise(() => {});
+}
+console.log(
+ 'Service worker ready and controlling. Continue app bootstrap here.',
+);
+
+export {};
diff --git a/test/consume-types/sw.js b/test/consume-types/sw.js
new file mode 100644
index 0000000000..ea14cb925c
--- /dev/null
+++ b/test/consume-types/sw.js
@@ -0,0 +1,99 @@
+// The purpose of this service worker is to help with loading
+// node_modules modules when using ES modules in the browser.
+//
+// Specifically, this service worker helps with non-standard module paths
+// that do not include file extensions, such as:
+//
+// /node_modules/some-lib/foo/bar
+// /node_modules/some-lib/foo/bar/
+//
+// In these cases, the service worker will try to resolve them to actual files
+// by appending ".js" or "/index.js" as needed.
+//
+// This service worker only handles requests under /node_modules/.
+// All other requests are passed through unmodified.
+
+export {};
+
+const scope = /** @type {ServiceWorkerGlobalScope} */ (
+ /** @type {any} */ (self)
+);
+
+scope.addEventListener('install', event => {
+ // Always activate worker immediately, for purposes of testing.
+ event.waitUntil(scope.skipWaiting());
+});
+
+scope.addEventListener('activate', event => {
+ // Always activate worker immediately, for purposes of testing.
+ event.waitUntil(scope.clients.claim());
+});
+
+scope.addEventListener('fetch', event => {
+ const url = new URL(event.request.url);
+
+ // Don't handle non-node_modules paths
+ if (!url.pathname.startsWith('/node_modules/')) {
+ event.respondWith(fetch(url.href));
+ return;
+ }
+
+ // 6
+ // Special handling for non-standard module paths in node_modules
+
+ const parts = url.pathname.split('/');
+ const fileName = /** @type {string} */ (parts.pop());
+ const ext = fileName.includes('.') ? fileName.split('.').pop() : '';
+
+ // Handle imports like 'some-lib/foo/bar' without an extension.
+ if (fileName !== '' && ext === '') {
+ event.respondWith(
+ // eslint-disable-next-line no-async-promise-executor
+ new Promise(async resolve => {
+ try {
+ // First try adding .js
+ const response = await tryJs();
+ const mimeType = response.headers.get('Content-Type') || '';
+ if (response.ok && mimeType.includes('javascript')) {
+ resolve(response);
+ } else {
+ throw new Error('Not JS');
+ }
+ } catch {
+ // If that fails, try adding /index.js
+ resolve(await tryIndexJs());
+ }
+
+ async function tryJs() {
+ const tryJs = new URL(url);
+ tryJs.href += '.js';
+ const response = await fetch(tryJs);
+ return response;
+ }
+
+ async function tryIndexJs() {
+ const tryIndexJs = new URL(url);
+ tryIndexJs.href += '/index.js';
+ const response = await fetch(tryIndexJs);
+ return response;
+ }
+ }),
+ );
+
+ return;
+ }
+
+ // Handle imports like 'some-lib/foo/bar/' (ending with a slash).
+ if (fileName === '') {
+ // adding index.js
+ const tryIndexJs = new URL(url);
+ tryIndexJs.href += 'index.js';
+
+ event.respondWith(fetch(tryIndexJs));
+
+ return;
+ }
+
+ // For all other cases, just fetch normally.
+ event.respondWith(fetch(url));
+});
diff --git a/test/consume-types/tsconfig.json b/test/consume-types/tsconfig.json
new file mode 100644
index 0000000000..d05ee2182a
--- /dev/null
+++ b/test/consume-types/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": true,
+ "module": "esnext",
+ "moduleResolution": "node",
+ "target": "esnext",
+ "strict": true,
+ "noEmit": true,
+ "lib": ["DOM", "ESNext", "WebWorker"],
+ "skipLibCheck": true,
+ "skipDefaultLibCheck": true
+ }
+}
diff --git a/test/e2e/configuration.test.js b/test/e2e/configuration.test.js
index d081d95eda..10af07faf0 100644
--- a/test/e2e/configuration.test.js
+++ b/test/e2e/configuration.test.js
@@ -41,7 +41,13 @@ test.describe('Configuration options', () => {
test('false (throws uncaught errors)', async ({ page }) => {
let consoleMsg, errorMsg;
- page.on('console', msg => (consoleMsg = msg.text()));
+ page.on('console', msg => {
+ const text = msg.text();
+ if (text.startsWith('DEPRECATION:')) {
+ return;
+ } // ignore expected deprecation warnings
+ consoleMsg = text;
+ });
page.on('pageerror', err => (errorMsg = err.message));
await docsifyInit({
diff --git a/test/e2e/plugins.test.js b/test/e2e/plugins.test.js
index 68534830df..b1e53d729a 100644
--- a/test/e2e/plugins.test.js
+++ b/test/e2e/plugins.test.js
@@ -16,7 +16,13 @@ test.describe('Plugins', () => {
'ready',
];
- page.on('console', msg => consoleMsgs.push(msg.text()));
+ page.on('console', msg => {
+ const text = msg.text();
+ if (text.startsWith('DEPRECATION:')) {
+ return;
+ } // ignore expected deprecation warnings
+ consoleMsgs.push(text);
+ });
await docsifyInit({
config: {
@@ -166,8 +172,15 @@ test.describe('Plugins', () => {
page.on('console', async msg => {
for (const arg of msg.args()) {
const val = await arg.jsonValue();
- const obj = JSON.parse(val);
- obj.response && (routeData = obj);
+ if (typeof val === 'string' && val.startsWith('DEPRECATION:')) {
+ continue;
+ }
+ try {
+ const obj = typeof val === 'string' ? JSON.parse(val) : val;
+ obj && obj.response && (routeData = obj);
+ } catch {
+ // ignore non-JSON console messages
+ }
}
});
});
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000000..ef671d3627
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,34 @@
+{
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": true,
+
+ "module": "esnext",
+ "moduleResolution": "node",
+ "target": "es2023",
+ "lib": ["ES2023", "DOM", "DOM.AsyncIterable", "DOM.Iterable"],
+ "declaration": true,
+ "declarationMap": true, // This (along with also shipping src/, not only dist/), makes "Go To Definition" go to the source files in IDEs for a better DX.
+ "emitDeclarationOnly": true,
+ "resolveJsonModule": true,
+
+ // Output declarations next to source files (the default when outDir is not
+ // defined), rather than in dist/, to avoid conflicting with the global
+ // build in dist/.
+ // "outDir": "dist/",
+
+ "strict": true,
+ "skipLibCheck": true,
+ "skipDefaultLibCheck": true,
+
+ // TODO: Remove once tinydate import is refactored to non-default, or replace/delete non-standards-aligned libs.
+ // Enables default import interop for CJS modules like tinydate
+ "allowSyntheticDefaultImports": true,
+
+ // TODO: Remove this once all implicit any errors are fixed with proper JSDoc types
+ // Currently suppressing ~600 implicit any errors across the codebase
+ // See: https://github.com/docsifyjs/docsify/pull/2392
+ "noImplicitAny": false
+ },
+ "include": ["src/**/*.js", "src/core/modules.ts", "src/core/globals.ts"]
+}