Improve JSDoc types and add eslint-plugin-jsdoc

Standardized JSDoc type annotations across the codebase, replacing 'Object' with 'object' and refining function signatures for better type safety. Added and configured eslint-plugin-jsdoc for enhanced documentation linting, and updated ESLint settings to include adapters and new JSDoc rules. Updated dependencies to include eslint-plugin-jsdoc and related packages.
This commit is contained in:
xixu-me committed 2025-12-11 13:27:26 +08:00
1 parent bfec1d2d9e
commit 07f5f35972
18 files changed
+295 -102

No files matched your search

+2 -1
View File
@@ -2,7 +2,8 @@
root = true root = true
[*] [*]
indent_style = tab indent_style = space
indent_size = 2
end_of_line = lf end_of_line = lf
charset = utf-8 charset = utf-8
trim_trailing_whitespace = true trim_trailing_whitespace = true
+6 -9
View File
@@ -16,25 +16,22 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
/* eslint-disable no-undef, no-unused-vars */
import { handleRequest } from '../src/index.js'; import { handleRequest } from '../src/index.js';
/** /**
* Edge Function handler. * Edge Function handler.
*
* @param {Request} request - Standard Web API Request object * @param {Request} request - Standard Web API Request object
* @param {Object} [context] - Platform-specific context (Netlify only) * @param {object} [context] - Platform-specific context (Netlify only)
* @param {Object} [context.geo] - Geolocation data (Netlify) * @param {object} [context.geo] - Geolocation data (Netlify)
* @param {string} [context.ip] - Client IP address (Netlify) * @param {string} [context.ip] - Client IP address (Netlify)
* @param {Object} [context.env] - Environment variables (Netlify) * @param {object} [context.env] - Environment variables (Netlify)
* @param {Function} [context.waitUntil] - Background task extension (Netlify) * @param {(promise: Promise<unknown>) => void} [context.waitUntil] - Background task extension (Netlify)
* @returns {Promise<Response>} Standard Web API Response * @returns {Promise<Response>} Standard Web API Response
*
* @example * @example
* // Netlify invokes with context * // Netlify invokes with context
* handler(request, { geo: {...}, ip: '1.2.3.4', env: {...}, waitUntil: fn }) * handler(request, { geo: {...}, ip: '1.2.3.4', env: {...}, waitUntil: fn })
*
* @example * @example
* // Vercel invokes without context * // Vercel invokes without context
* handler(request) * handler(request)
@@ -70,7 +67,7 @@ export default async function handler(request, context) {
const ctx = { const ctx = {
waitUntil: isNetlify && context.waitUntil waitUntil: isNetlify && context.waitUntil
? (promise) => context.waitUntil(promise) ? (promise) => context.waitUntil(promise)
: (promise) => { : (_promise) => {
// No-op on Vercel: background tasks not supported // No-op on Vercel: background tasks not supported
// Cache writes will run synchronously instead // Cache writes will run synchronously instead
console.warn('waitUntil is not supported in Vercel Edge Runtime'); console.warn('waitUntil is not supported in Vercel Edge Runtime');
-2
View File
@@ -25,10 +25,8 @@ import { handleRequest } from './src/index.js';
* *
* This is the entry point for Deno Deploy deployments. It uses the * This is the entry point for Deno Deploy deployments. It uses the
* standard Deno.serve() API to handle incoming HTTP requests. * standard Deno.serve() API to handle incoming HTTP requests.
*
* @param {Request} request - Standard Web API Request object * @param {Request} request - Standard Web API Request object
* @returns {Promise<Response>} Standard Web API Response * @returns {Promise<Response>} Standard Web API Response
*
* @example * @example
* // Deno Deploy invokes automatically: * // Deno Deploy invokes automatically:
* // Deno.serve((request) => handler(request)) * // Deno.serve((request) => handler(request))
+8 -11
View File
@@ -16,7 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
/* eslint-disable no-undef */
import { handleRequest } from '../src/index.js'; import { handleRequest } from '../src/index.js';
@@ -31,21 +31,18 @@ import { handleRequest } from '../src/index.js';
* The [[path]] syntax in the filename creates a catch-all route that matches * The [[path]] syntax in the filename creates a catch-all route that matches
* any path, allowing this single function to handle all requests to the Pages * any path, allowing this single function to handle all requests to the Pages
* application. * application.
* * @param {object} context - Pages Function context
* @param {Object} context - Pages Function context
* @param {Request} context.request - The incoming HTTP request * @param {Request} context.request - The incoming HTTP request
* @param {Object} context.env - Environment variables and bindings (KV, secrets, etc.) * @param {object} context.env - Environment variables and bindings (KV, secrets, etc.)
* @param {Object} context.params - Route parameters (path segments from [[path]]) * @param {object} context.params - Route parameters (path segments from [[path]])
* @param {Function} context.waitUntil - Extend function execution for background tasks * @param {(promise: Promise<unknown>) => void} context.waitUntil - Extend function execution for background tasks
* @param {Function} context.next - Call next middleware in chain (not used here) * @param {() => Promise<Response>} context.next - Call next middleware in chain (not used here)
* @param {Object} context.data - Shared data between functions * @param {object} context.data - Shared data between functions
* @returns {Promise<Response>} The HTTP response to return to the client * @returns {Promise<Response>} The HTTP response to return to the client
*
* @example * @example
* // This is called automatically by Pages * // This is called automatically by Pages
* // Runtime invokes: onRequest(context) * // Runtime invokes: onRequest(context)
* // Returns: Response with package data * // Returns: Response with package data
*
* @example * @example
* // Environment variables usage * // Environment variables usage
* // wrangler.toml: [vars] TIMEOUT_SECONDS = "60" * // wrangler.toml: [vars] TIMEOUT_SECONDS = "60"
@@ -58,7 +55,7 @@ export async function onRequest(context) {
// Create a minimal ExecutionContext-like object for compatibility // Create a minimal ExecutionContext-like object for compatibility
const ctx = { const ctx = {
waitUntil: waitUntil, waitUntil,
passThroughOnException: () => { passThroughOnException: () => {
// Pages doesn't support passThroughOnException, so this is a no-op // Pages doesn't support passThroughOnException, so this is a no-op
console.warn('passThroughOnException is not supported in Pages Functions'); console.warn('passThroughOnException is not supported in Pages Functions');
+39 -1
View File
@@ -1,10 +1,12 @@
import js from '@eslint/js'; import js from '@eslint/js';
import prettierConfig from 'eslint-config-prettier'; import prettierConfig from 'eslint-config-prettier';
import jsdoc from 'eslint-plugin-jsdoc';
export default [ export default [
js.configs.recommended, js.configs.recommended,
jsdoc.configs['flat/recommended'],
{ {
files: ['src/**/*.js', 'test/**/*.js'], files: ['src/**/*.js', 'test/**/*.js', 'adapters/**/*.js'],
languageOptions: { languageOptions: {
ecmaVersion: 2022, ecmaVersion: 2022,
sourceType: 'module', sourceType: 'module',
@@ -33,6 +35,7 @@ export default [
TextDecoder: 'readonly', TextDecoder: 'readonly',
performance: 'readonly', performance: 'readonly',
globalThis: 'readonly', globalThis: 'readonly',
process: 'readonly',
// Vitest globals // Vitest globals
describe: 'readonly', describe: 'readonly',
@@ -45,7 +48,42 @@ export default [
vi: 'readonly' vi: 'readonly'
} }
}, },
settings: {
jsdoc: {
mode: 'typescript',
tagNamePreference: {
returns: 'returns'
}
}
},
rules: { rules: {
// JSDoc rules overrides
'jsdoc/require-description': 'warn',
'jsdoc/require-returns': 'off', // Often redundant if return type is void or obvious
'jsdoc/require-param-description': 'off', // Names are often self-explanatory
'jsdoc/no-undefined-types': [
'warn',
{
definedTypes: [
'ExecutionContext',
'Cache',
'RequestInit',
'Request',
'Response',
'Headers',
'URL',
'URLSearchParams',
'AbortController',
'AbortSignal',
'ReadableStream',
'WritableStream',
'TransformStream',
'TextEncoder',
'TextDecoder'
]
}
],
// Code quality rules // Code quality rules
'no-unused-vars': [ 'no-unused-vars': [
'error', 'error',
+213 -12
View File
@@ -16,6 +16,7 @@
"@cloudflare/workers-types": "^4.20251205.0", "@cloudflare/workers-types": "^4.20251205.0",
"eslint": "^9.39.1", "eslint": "^9.39.1",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-jsdoc": "^61.5.0",
"prettier": "^3.7.4", "prettier": "^3.7.4",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vitest": "^3.1.4", "vitest": "^3.1.4",
@@ -188,6 +189,33 @@
"tslib": "^2.4.0" "tslib": "^2.4.0"
} }
}, },
"node_modules/@es-joy/jsdoccomment": {
"version": "0.76.0",
"resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.76.0.tgz",
"integrity": "sha512-g+RihtzFgGTx2WYCuTHbdOXJeAlGnROws0TeALx9ow/ZmOROOZkVg5wp/B44n0WJgI4SQFP1eWM2iRPlU2Y14w==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.8",
"@typescript-eslint/types": "^8.46.0",
"comment-parser": "1.4.1",
"esquery": "^1.6.0",
"jsdoc-type-pratt-parser": "~6.10.0"
},
"engines": {
"node": ">=20.11.0"
}
},
"node_modules/@es-joy/resolve.exports": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@es-joy/resolve.exports/-/resolve.exports-1.2.0.tgz",
"integrity": "sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/@esbuild/aix-ppc64": { "node_modules/@esbuild/aix-ppc64": {
"version": "0.25.9", "version": "0.25.9",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
@@ -1584,6 +1612,19 @@
"win32" "win32"
] ]
}, },
"node_modules/@sindresorhus/base62": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/base62/-/base62-1.0.0.tgz",
"integrity": "sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@sindresorhus/is": { "node_modules/@sindresorhus/is": {
"version": "7.0.2", "version": "7.0.2",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.0.2.tgz", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.0.2.tgz",
@@ -1635,6 +1676,20 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@typescript-eslint/types": {
"version": "8.49.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.49.0.tgz",
"integrity": "sha512-e9k/fneezorUo6WShlQpMxXh8/8wfyc+biu6tnAqA81oWrEic0k21RHzP9uqqpyBBeBKu4T+Bsjy9/b8u7obXQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@vitest/expect": { "node_modules/@vitest/expect": {
"version": "3.2.4", "version": "3.2.4",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz",
@@ -1698,7 +1753,6 @@
"integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vitest/utils": "3.2.4", "@vitest/utils": "3.2.4",
"pathe": "^2.0.3", "pathe": "^2.0.3",
@@ -1714,7 +1768,6 @@
"integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@vitest/pretty-format": "3.2.4", "@vitest/pretty-format": "3.2.4",
"magic-string": "^0.30.17", "magic-string": "^0.30.17",
@@ -1771,7 +1824,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"bin": { "bin": {
"acorn": "bin/acorn" "acorn": "bin/acorn"
}, },
@@ -1832,6 +1884,16 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1" "url": "https://github.com/chalk/ansi-styles?sponsor=1"
} }
}, },
"node_modules/are-docs-informative": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz",
"integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14"
}
},
"node_modules/argparse": { "node_modules/argparse": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -2062,6 +2124,16 @@
"simple-swizzle": "^0.2.2" "simple-swizzle": "^0.2.2"
} }
}, },
"node_modules/comment-parser": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz",
"integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 12.0.0"
}
},
"node_modules/concat-map": { "node_modules/concat-map": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -2327,7 +2399,6 @@
"integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1", "@eslint-community/regexpp": "^4.12.1",
@@ -2398,6 +2469,35 @@
"eslint": ">=7.0.0" "eslint": ">=7.0.0"
} }
}, },
"node_modules/eslint-plugin-jsdoc": {
"version": "61.5.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-61.5.0.tgz",
"integrity": "sha512-PR81eOGq4S7diVnV9xzFSBE4CDENRQGP0Lckkek8AdHtbj+6Bm0cItwlFnxsLFriJHspiE3mpu8U20eODyToIg==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"@es-joy/jsdoccomment": "~0.76.0",
"@es-joy/resolve.exports": "1.2.0",
"are-docs-informative": "^0.0.2",
"comment-parser": "1.4.1",
"debug": "^4.4.3",
"escape-string-regexp": "^4.0.0",
"espree": "^10.4.0",
"esquery": "^1.6.0",
"html-entities": "^2.6.0",
"object-deep-merge": "^2.0.0",
"parse-imports-exports": "^0.2.4",
"semver": "^7.7.3",
"spdx-expression-parse": "^4.0.0",
"to-valid-identifier": "^1.0.0"
},
"engines": {
"node": ">=20.11.0"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0 || ^9.0.0"
}
},
"node_modules/eslint-scope": { "node_modules/eslint-scope": {
"version": "8.4.0", "version": "8.4.0",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
@@ -2846,6 +2946,23 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/html-entities": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz",
"integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/mdevils"
},
{
"type": "patreon",
"url": "https://patreon.com/mdevils"
}
],
"license": "MIT"
},
"node_modules/http-errors": { "node_modules/http-errors": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
@@ -2997,6 +3114,16 @@
"js-yaml": "bin/js-yaml.js" "js-yaml": "bin/js-yaml.js"
} }
}, },
"node_modules/jsdoc-type-pratt-parser": {
"version": "6.10.0",
"resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-6.10.0.tgz",
"integrity": "sha512-+LexoTRyYui5iOhJGn13N9ZazL23nAHGkXsa1p/C8yeq79WRfLBag6ZZ0FQG2aRoc9yfo59JT9EYCQonOkHKkQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=20.0.0"
}
},
"node_modules/json-buffer": { "node_modules/json-buffer": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -3264,6 +3391,13 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/object-deep-merge": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/object-deep-merge/-/object-deep-merge-2.0.0.tgz",
"integrity": "sha512-3DC3UMpeffLTHiuXSy/UG4NOIYTLlY9u3V82+djSCLYClWobZiS4ivYzpIUWrRY/nfsJ8cWsKyG3QfyLePmhvg==",
"dev": true,
"license": "MIT"
},
"node_modules/object-inspect": { "node_modules/object-inspect": {
"version": "1.13.4", "version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
@@ -3360,6 +3494,23 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/parse-imports-exports": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz",
"integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"parse-statements": "1.0.11"
}
},
"node_modules/parse-statements": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz",
"integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==",
"dev": true,
"license": "MIT"
},
"node_modules/parseurl": { "node_modules/parseurl": {
"version": "1.3.3", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -3429,7 +3580,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"engines": { "engines": {
"node": ">=12" "node": ">=12"
}, },
@@ -3554,6 +3704,19 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/reserved-identifiers": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/reserved-identifiers/-/reserved-identifiers-1.2.0.tgz",
"integrity": "sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/resolve-from": { "node_modules/resolve-from": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -3628,9 +3791,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/semver": { "node_modules/semver": {
"version": "7.7.2", "version": "7.7.3",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"dev": true, "dev": true,
"license": "ISC", "license": "ISC",
"bin": { "bin": {
@@ -3845,6 +4008,31 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/spdx-exceptions": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
"integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
"dev": true,
"license": "CC-BY-3.0"
},
"node_modules/spdx-expression-parse": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz",
"integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"spdx-exceptions": "^2.1.0",
"spdx-license-ids": "^3.0.0"
}
},
"node_modules/spdx-license-ids": {
"version": "3.0.22",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz",
"integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==",
"dev": true,
"license": "CC0-1.0"
},
"node_modules/stackback": { "node_modules/stackback": {
"version": "0.0.2", "version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
@@ -3979,6 +4167,23 @@
"node": ">=14.0.0" "node": ">=14.0.0"
} }
}, },
"node_modules/to-valid-identifier": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/to-valid-identifier/-/to-valid-identifier-1.0.0.tgz",
"integrity": "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@sindresorhus/base62": "^1.0.0",
"reserved-identifiers": "^1.0.0"
},
"engines": {
"node": ">=20"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/toidentifier": { "node_modules/toidentifier": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@@ -4053,7 +4258,6 @@
"integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"pathe": "^2.0.3" "pathe": "^2.0.3"
} }
@@ -4092,7 +4296,6 @@
"integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==", "integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"esbuild": "^0.25.0", "esbuild": "^0.25.0",
"fdir": "^6.5.0", "fdir": "^6.5.0",
@@ -4191,7 +4394,6 @@
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@types/chai": "^5.2.2", "@types/chai": "^5.2.2",
"@vitest/expect": "3.2.4", "@vitest/expect": "3.2.4",
@@ -4309,7 +4511,6 @@
"dev": true, "dev": true,
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"bin": { "bin": {
"workerd": "bin/workerd" "workerd": "bin/workerd"
}, },
+1
View File
@@ -8,6 +8,7 @@
"@cloudflare/workers-types": "^4.20251205.0", "@cloudflare/workers-types": "^4.20251205.0",
"eslint": "^9.39.1", "eslint": "^9.39.1",
"eslint-config-prettier": "^10.1.8", "eslint-config-prettier": "^10.1.8",
"eslint-plugin-jsdoc": "^61.5.0",
"prettier": "^3.7.4", "prettier": "^3.7.4",
"typescript": "^5.9.3", "typescript": "^5.9.3",
"vitest": "^3.1.4", "vitest": "^3.1.4",
+13 -25
View File
@@ -20,12 +20,10 @@ import { PLATFORMS } from './platforms.js';
/** /**
* Security-related configuration options for request validation and CORS. * Security-related configuration options for request validation and CORS.
* * @typedef {object} SecurityConfig
* @typedef {Object} SecurityConfig
* @property {string[]} ALLOWED_METHODS - List of allowed HTTP methods for incoming requests * @property {string[]} ALLOWED_METHODS - List of allowed HTTP methods for incoming requests
* @property {string[]} ALLOWED_ORIGINS - List of allowed CORS origins (use ['*'] for all origins) * @property {string[]} ALLOWED_ORIGINS - List of allowed CORS origins (use ['*'] for all origins)
* @property {number} MAX_PATH_LENGTH - Maximum allowed URL path length in characters * @property {number} MAX_PATH_LENGTH - Maximum allowed URL path length in characters
*
* @example * @example
* // Default security config * // Default security config
* const security = { * const security = {
@@ -33,7 +31,6 @@ import { PLATFORMS } from './platforms.js';
* ALLOWED_ORIGINS: ['*'], * ALLOWED_ORIGINS: ['*'],
* MAX_PATH_LENGTH: 2048 * MAX_PATH_LENGTH: 2048
* }; * };
*
* @example * @example
* // Custom security config with restricted origins * // Custom security config with restricted origins
* const security = { * const security = {
@@ -49,15 +46,13 @@ import { PLATFORMS } from './platforms.js';
* This configuration controls timeout behavior, retry logic, caching, security policies, * This configuration controls timeout behavior, retry logic, caching, security policies,
* and platform URL mappings. All values can be overridden via environment variables * and platform URL mappings. All values can be overridden via environment variables
* in Cloudflare Workers. * in Cloudflare Workers.
* * @typedef {object} ApplicationConfig
* @typedef {Object} ApplicationConfig
* @property {number} TIMEOUT_SECONDS - Request timeout in seconds (default: 30) * @property {number} TIMEOUT_SECONDS - Request timeout in seconds (default: 30)
* @property {number} MAX_RETRIES - Maximum number of retry attempts for failed requests (default: 3) * @property {number} MAX_RETRIES - Maximum number of retry attempts for failed requests (default: 3)
* @property {number} RETRY_DELAY_MS - Delay between retry attempts in milliseconds (default: 1000) * @property {number} RETRY_DELAY_MS - Delay between retry attempts in milliseconds (default: 1000)
* @property {number} CACHE_DURATION - Cache duration in seconds for successful responses (default: 1800) * @property {number} CACHE_DURATION - Cache duration in seconds for successful responses (default: 1800)
* @property {SecurityConfig} SECURITY - Security-related configurations * @property {SecurityConfig} SECURITY - Security-related configurations
* @property {Object.<string, string>} PLATFORMS - Platform-specific base URL mappings * @property {{ [key: string]: string }} PLATFORMS - Platform-specific base URL mappings
*
* @example * @example
* // Default configuration * // Default configuration
* const config = { * const config = {
@@ -72,7 +67,6 @@ import { PLATFORMS } from './platforms.js';
* }, * },
* PLATFORMS: { gh: 'https://github.com', ... } * PLATFORMS: { gh: 'https://github.com', ... }
* }; * };
*
* @example * @example
* // Configuration with environment overrides * // Configuration with environment overrides
* const env = { * const env = {
@@ -99,16 +93,13 @@ import { PLATFORMS } from './platforms.js';
* - `ALLOWED_METHODS` - Comma-separated HTTP methods (default: 'GET,HEAD') * - `ALLOWED_METHODS` - Comma-separated HTTP methods (default: 'GET,HEAD')
* - `ALLOWED_ORIGINS` - Comma-separated CORS origins (default: '*') * - `ALLOWED_ORIGINS` - Comma-separated CORS origins (default: '*')
* - `MAX_PATH_LENGTH` - Override max path length (default: 2048) * - `MAX_PATH_LENGTH` - Override max path length (default: 2048)
* * @param {Record<string, unknown>} env - Environment variables from Cloudflare Workers env object
* @param {Record<string, any>} env - Environment variables from Cloudflare Workers env object
* @returns {ApplicationConfig} Complete application configuration with applied overrides * @returns {ApplicationConfig} Complete application configuration with applied overrides
*
* @example * @example
* // Create config with defaults (no environment variables) * // Create config with defaults (no environment variables)
* const config = createConfig(); * const config = createConfig();
* console.log(config.TIMEOUT_SECONDS); // 30 * console.log(config.TIMEOUT_SECONDS); // 30
* console.log(config.CACHE_DURATION); // 1800 * console.log(config.CACHE_DURATION); // 1800
*
* @example * @example
* // Create config with environment overrides * // Create config with environment overrides
* const env = { * const env = {
@@ -122,7 +113,6 @@ import { PLATFORMS } from './platforms.js';
* console.log(config.MAX_RETRIES); // 5 * console.log(config.MAX_RETRIES); // 5
* console.log(config.CACHE_DURATION); // 3600 (1 hour) * console.log(config.CACHE_DURATION); // 3600 (1 hour)
* console.log(config.SECURITY.ALLOWED_METHODS); // ['GET', 'HEAD', 'POST', 'PUT'] * console.log(config.SECURITY.ALLOWED_METHODS); // ['GET', 'HEAD', 'POST', 'PUT']
*
* @example * @example
* // Invalid environment values fallback to defaults * // Invalid environment values fallback to defaults
* const env = { * const env = {
@@ -132,7 +122,6 @@ import { PLATFORMS } from './platforms.js';
* const config = createConfig(env); * const config = createConfig(env);
* console.log(config.TIMEOUT_SECONDS); // 30 (default) * console.log(config.TIMEOUT_SECONDS); // 30 (default)
* console.log(config.MAX_RETRIES); // 3 (default) * console.log(config.MAX_RETRIES); // 3 (default)
*
* @example * @example
* // Custom CORS origins * // Custom CORS origins
* const env = { * const env = {
@@ -144,14 +133,16 @@ import { PLATFORMS } from './platforms.js';
*/ */
export function createConfig(env = {}) { export function createConfig(env = {}) {
return { return {
TIMEOUT_SECONDS: parseInt(env.TIMEOUT_SECONDS, 10) || 30, TIMEOUT_SECONDS: parseInt(String(env.TIMEOUT_SECONDS), 10) || 30,
MAX_RETRIES: parseInt(env.MAX_RETRIES, 10) || 3, MAX_RETRIES: parseInt(String(env.MAX_RETRIES), 10) || 3,
RETRY_DELAY_MS: parseInt(env.RETRY_DELAY_MS, 10) || 1000, RETRY_DELAY_MS: parseInt(String(env.RETRY_DELAY_MS), 10) || 1000,
CACHE_DURATION: parseInt(env.CACHE_DURATION, 10) || 1800, // 30 minutes CACHE_DURATION: parseInt(String(env.CACHE_DURATION), 10) || 1800, // 30 minutes
SECURITY: { SECURITY: {
ALLOWED_METHODS: env.ALLOWED_METHODS ? env.ALLOWED_METHODS.split(',') : ['GET', 'HEAD'], ALLOWED_METHODS:
ALLOWED_ORIGINS: env.ALLOWED_ORIGINS ? env.ALLOWED_ORIGINS.split(',') : ['*'], typeof env.ALLOWED_METHODS === 'string' ? env.ALLOWED_METHODS.split(',') : ['GET', 'HEAD'],
MAX_PATH_LENGTH: parseInt(env.MAX_PATH_LENGTH, 10) || 2048 ALLOWED_ORIGINS:
typeof env.ALLOWED_ORIGINS === 'string' ? env.ALLOWED_ORIGINS.split(',') : ['*'],
MAX_PATH_LENGTH: parseInt(String(env.MAX_PATH_LENGTH), 10) || 2048
}, },
PLATFORMS PLATFORMS
}; };
@@ -163,14 +154,11 @@ export function createConfig(env = {}) {
* This is a pre-instantiated configuration object using default values with no * This is a pre-instantiated configuration object using default values with no
* environment overrides. In production (Cloudflare Workers), you should use * environment overrides. In production (Cloudflare Workers), you should use
* `createConfig(env)` instead to allow runtime configuration. * `createConfig(env)` instead to allow runtime configuration.
*
* @type {ApplicationConfig} * @type {ApplicationConfig}
*
* @example * @example
* // Import default config * // Import default config
* import { CONFIG } from './config/index.js'; * import { CONFIG } from './config/index.js';
* console.log(CONFIG.TIMEOUT_SECONDS); // 30 * console.log(CONFIG.TIMEOUT_SECONDS); // 30
*
* @example * @example
* // Check platform availability * // Check platform availability
* if (CONFIG.PLATFORMS.npm) { * if (CONFIG.PLATFORMS.npm) {
+1 -13
View File
@@ -115,17 +115,13 @@
* - `cr-suse` - SUSE Registry * - `cr-suse` - SUSE Registry
* - `cr-opensuse` - openSUSE Registry * - `cr-opensuse` - openSUSE Registry
* - `cr-gitpod` - Gitpod Registry * - `cr-gitpod` - Gitpod Registry
* * @type {{ [key: string]: string }}
* @type {Object.<string, string>}
*
* @example * @example
* // Access GitHub base URL * // Access GitHub base URL
* const githubUrl = PLATFORMS.gh; // 'https://github.com' * const githubUrl = PLATFORMS.gh; // 'https://github.com'
*
* @example * @example
* // Access OpenAI API base URL * // Access OpenAI API base URL
* const openaiUrl = PLATFORMS['ip-openai']; // 'https://api.openai.com' * const openaiUrl = PLATFORMS['ip-openai']; // 'https://api.openai.com'
*
* @example * @example
* // Check if platform exists * // Check if platform exists
* if (PLATFORMS.npm) { * if (PLATFORMS.npm) {
@@ -248,41 +244,33 @@ export const SORTED_PLATFORMS = Object.keys(PLATFORMS).sort((a, b) => {
* *
* The function handles special cases for platforms that require API path prefixes or * The function handles special cases for platforms that require API path prefixes or
* URL structure modifications to match their upstream API conventions. * URL structure modifications to match their upstream API conventions.
*
* @param {string} path - The original request path including platform prefix (e.g., '/gh/user/repo') * @param {string} path - The original request path including platform prefix (e.g., '/gh/user/repo')
* @param {string} platformKey - The platform key from PLATFORMS object (e.g., 'gh', 'crates', 'npm') * @param {string} platformKey - The platform key from PLATFORMS object (e.g., 'gh', 'crates', 'npm')
* @returns {string} The transformed path ready for upstream request * @returns {string} The transformed path ready for upstream request
*
* @example * @example
* // Basic transformation - strips platform prefix * // Basic transformation - strips platform prefix
* transformPath('/gh/torvalds/linux', 'gh') * transformPath('/gh/torvalds/linux', 'gh')
* // Returns: '/torvalds/linux' * // Returns: '/torvalds/linux'
*
* @example * @example
* // crates.io API transformation - adds API prefix * // crates.io API transformation - adds API prefix
* transformPath('/crates/serde/1.0.0/download', 'crates') * transformPath('/crates/serde/1.0.0/download', 'crates')
* // Returns: '/api/v1/crates/serde/1.0.0/download' * // Returns: '/api/v1/crates/serde/1.0.0/download'
*
* @example * @example
* // crates.io search endpoint * // crates.io search endpoint
* transformPath('/crates/?q=tokio', 'crates') * transformPath('/crates/?q=tokio', 'crates')
* // Returns: '/api/v1/crates?q=tokio' * // Returns: '/api/v1/crates?q=tokio'
*
* @example * @example
* // Jenkins update center transformation * // Jenkins update center transformation
* transformPath('/jenkins/update-center.json', 'jenkins') * transformPath('/jenkins/update-center.json', 'jenkins')
* // Returns: '/current/update-center.json' * // Returns: '/current/update-center.json'
*
* @example * @example
* // Homebrew API paths (pass-through) * // Homebrew API paths (pass-through)
* transformPath('/homebrew/api/formula/git.json', 'homebrew-api') * transformPath('/homebrew/api/formula/git.json', 'homebrew-api')
* // Returns: '/formula/git.json' * // Returns: '/formula/git.json'
*
* @example * @example
* // Unknown platform (no transformation) * // Unknown platform (no transformation)
* transformPath('/unknown/path', 'nonexistent') * transformPath('/unknown/path', 'nonexistent')
* // Returns: '/unknown/path' * // Returns: '/unknown/path'
*
* @example * @example
* // Multi-part platform key (hyphens converted to slashes) * // Multi-part platform key (hyphens converted to slashes)
* transformPath('/ip/openai/v1/chat/completions', 'ip-openai') * transformPath('/ip/openai/v1/chat/completions', 'ip-openai')
+5 -5
View File
@@ -24,9 +24,8 @@ import { isDockerRequest, validateRequest } from './utils/validation.js';
/** /**
* Main request handler with comprehensive caching, retry logic, and security measures. * Main request handler with comprehensive caching, retry logic, and security measures.
*
* @param {Request} request - The incoming HTTP request * @param {Request} request - The incoming HTTP request
* @param {Object} env - Cloudflare Workers environment variables for runtime config overrides * @param {object} env - Cloudflare Workers environment variables for runtime config overrides
* @param {ExecutionContext} ctx - Cloudflare Workers execution context for background tasks * @param {ExecutionContext} ctx - Cloudflare Workers execution context for background tasks
* @returns {Promise<Response>} The HTTP response with appropriate headers and body * @returns {Promise<Response>} The HTTP response with appropriate headers and body
*/ */
@@ -130,8 +129,8 @@ async function handleRequest(request, env, ctx) {
/** @type {Cache | null} */ /** @type {Cache | null} */
// @ts-ignore - Cloudflare Workers cache API // @ts-ignore - Cloudflare Workers cache API
const cache = const cache =
typeof caches !== 'undefined' && /** @type {any} */ (caches).default typeof caches !== 'undefined' && /** @type {any} */ (caches).default // eslint-disable-line jsdoc/reject-any-type
? /** @type {any} */ (caches).default ? /** @type {any} */ (caches).default // eslint-disable-line jsdoc/reject-any-type
: null; : null;
if (cache && !isGit && !isGitLFS && !isDocker && !isAI) { if (cache && !isGit && !isGitLFS && !isDocker && !isAI) {
@@ -602,8 +601,9 @@ async function handleRequest(request, env, ctx) {
export default { export default {
/** /**
* Main Worker entry point.
* @param {Request} request * @param {Request} request
* @param {Object} env * @param {object} env
* @param {ExecutionContext} ctx * @param {ExecutionContext} ctx
*/ */
fetch(request, env, ctx) { fetch(request, env, ctx) {
-2
View File
@@ -27,7 +27,6 @@
* - AI provider path prefix (/ip/{provider}/...) * - AI provider path prefix (/ip/{provider}/...)
* - Common AI API endpoints (chat, completions, embeddings, etc.) * - Common AI API endpoints (chat, completions, embeddings, etc.)
* - AI-specific URL patterns with JSON POST requests * - AI-specific URL patterns with JSON POST requests
*
* @param {Request} request - The incoming request object * @param {Request} request - The incoming request object
* @param {URL} url - Parsed URL object * @param {URL} url - Parsed URL object
* @returns {boolean} True if this is an AI inference request * @returns {boolean} True if this is an AI inference request
@@ -74,7 +73,6 @@ export function isAIInferenceRequest(request, url) {
* Configures headers for AI protocol requests. * Configures headers for AI protocol requests.
* *
* Sets Content-Type and User-Agent headers for AI inference requests. * Sets Content-Type and User-Agent headers for AI inference requests.
*
* @param {Headers} headers - The headers object to modify * @param {Headers} headers - The headers object to modify
* @param {Request} request - The original request * @param {Request} request - The original request
*/ */
-4
View File
@@ -28,7 +28,6 @@ import { createErrorResponse } from '../utils/security.js';
* *
* Extracts authentication realm and service information from the Bearer * Extracts authentication realm and service information from the Bearer
* authentication challenge header returned by container registries. * authentication challenge header returned by container registries.
*
* @param {string} authenticateStr - The WWW-Authenticate header value * @param {string} authenticateStr - The WWW-Authenticate header value
* @returns {{realm: string, service: string}} Parsed authentication info with realm URL and service name * @returns {{realm: string, service: string}} Parsed authentication info with realm URL and service name
* @throws {Error} If the header format is invalid or missing required fields * @throws {Error} If the header format is invalid or missing required fields
@@ -53,7 +52,6 @@ export function parseAuthenticate(authenticateStr) {
* *
* Requests a Bearer token from the registry's authentication service, * Requests a Bearer token from the registry's authentication service,
* optionally including scope (repository permissions) and authorization credentials. * optionally including scope (repository permissions) and authorization credentials.
*
* @param {{realm: string, service: string}} wwwAuthenticate - Authentication info from WWW-Authenticate header * @param {{realm: string, service: string}} wwwAuthenticate - Authentication info from WWW-Authenticate header
* @param {string} scope - The scope for the token (e.g., "repository:library/nginx:pull") * @param {string} scope - The scope for the token (e.g., "repository:library/nginx:pull")
* @param {string} authorization - Authorization header value (optional, for authenticated access) * @param {string} authorization - Authorization header value (optional, for authenticated access)
@@ -79,7 +77,6 @@ export async function fetchToken(wwwAuthenticate, scope, authorization) {
* *
* Generates a Docker/OCI registry-compliant 401 response with a WWW-Authenticate * Generates a Docker/OCI registry-compliant 401 response with a WWW-Authenticate
* header that directs clients to the token authentication endpoint. * header that directs clients to the token authentication endpoint.
*
* @param {URL} url - Request URL used to construct authentication realm * @param {URL} url - Request URL used to construct authentication realm
* @returns {Response} Unauthorized response with WWW-Authenticate header * @returns {Response} Unauthorized response with WWW-Authenticate header
*/ */
@@ -96,7 +93,6 @@ export function responseUnauthorized(url) {
* Handles the special /v2/auth endpoint for Docker authentication. * Handles the special /v2/auth endpoint for Docker authentication.
* *
* Proxies generation of auth tokens by negotiating with the upstream registry. * Proxies generation of auth tokens by negotiating with the upstream registry.
*
* @param {Request} request - The incoming request * @param {Request} request - The incoming request
* @param {URL} url - The parsed URL * @param {URL} url - The parsed URL
* @param {import('../config/index.js').ApplicationConfig} config - App configuration * @param {import('../config/index.js').ApplicationConfig} config - App configuration
-3
View File
@@ -28,7 +28,6 @@
* - Git User-Agent headers * - Git User-Agent headers
* - Git service query parameters * - Git service query parameters
* - Git-specific Content-Type headers * - Git-specific Content-Type headers
*
* @param {Request} request - The incoming request object * @param {Request} request - The incoming request object
* @param {URL} url - Parsed URL object * @param {URL} url - Parsed URL object
* @returns {boolean} True if this is a Git operation * @returns {boolean} True if this is a Git operation
@@ -72,7 +71,6 @@ export function isGitRequest(request, url) {
* - LFS object storage paths (SHA-256 hash patterns) * - LFS object storage paths (SHA-256 hash patterns)
* - Git LFS Accept/Content-Type headers * - Git LFS Accept/Content-Type headers
* - Git LFS User-Agent * - Git LFS User-Agent
*
* @param {Request} request - The incoming request object * @param {Request} request - The incoming request object
* @param {URL} url - Parsed URL object * @param {URL} url - Parsed URL object
* @returns {boolean} True if this is a Git LFS operation * @returns {boolean} True if this is a Git LFS operation
@@ -116,7 +114,6 @@ export function isGitLFSRequest(request, url) {
* Configures headers for Git protocol requests. * Configures headers for Git protocol requests.
* *
* Sets User-Agent and Content-Type headers required by Git and Git LFS protocols. * Sets User-Agent and Content-Type headers required by Git and Git LFS protocols.
*
* @param {Headers} headers - The headers object to modify * @param {Headers} headers - The headers object to modify
* @param {Request} request - The original request * @param {Request} request - The original request
* @param {URL} url - The parsed URL * @param {URL} url - The parsed URL
+1 -4
View File
@@ -45,7 +45,6 @@ export class PerformanceMonitor {
* *
* Records the elapsed time (in milliseconds) since the monitor was created. * Records the elapsed time (in milliseconds) since the monitor was created.
* If a mark with the same name already exists, logs a warning and overwrites it. * If a mark with the same name already exists, logs a warning and overwrites it.
*
* @param {string} name - The name of the timing mark (e.g., 'cache_hit', 'attempt_0', 'success') * @param {string} name - The name of the timing mark (e.g., 'cache_hit', 'attempt_0', 'success')
*/ */
mark(name) { mark(name) {
@@ -60,8 +59,7 @@ export class PerformanceMonitor {
* *
* Converts the internal Map of timing marks to a JavaScript object suitable for * Converts the internal Map of timing marks to a JavaScript object suitable for
* JSON serialization and inclusion in response headers. * JSON serialization and inclusion in response headers.
* * @returns {{ [key: string]: number }} Object containing name-timestamp pairs in milliseconds
* @returns {Object.<string, number>} Object containing name-timestamp pairs in milliseconds
*/ */
getMetrics() { getMetrics() {
return Object.fromEntries(this.marks.entries()); return Object.fromEntries(this.marks.entries());
@@ -76,7 +74,6 @@ export class PerformanceMonitor {
* headers are included. * headers are included.
* *
* **Note:** This header is only added to non-protocol responses (not Git/Docker/AI). * **Note:** This header is only added to non-protocol responses (not Git/Docker/AI).
*
* @param {Response} response - The original response object * @param {Response} response - The original response object
* @param {PerformanceMonitor} monitor - Performance monitor instance with collected metrics * @param {PerformanceMonitor} monitor - Performance monitor instance with collected metrics
* @returns {Response} New response with added performance and security headers * @returns {Response} New response with added performance and security headers
-2
View File
@@ -30,7 +30,6 @@
* - Referrer-Policy (referrer information control) * - Referrer-Policy (referrer information control)
* - Content-Security-Policy (resource loading restrictions) * - Content-Security-Policy (resource loading restrictions)
* - Permissions-Policy (privacy-invasive feature restrictions) * - Permissions-Policy (privacy-invasive feature restrictions)
*
* @param {Headers} headers - Headers object to modify (mutates in place) * @param {Headers} headers - Headers object to modify (mutates in place)
* @returns {Headers} Modified headers object (same reference) * @returns {Headers} Modified headers object (same reference)
*/ */
@@ -49,7 +48,6 @@ export function addSecurityHeaders(headers) {
* *
* Generates an HTTP error response with appropriate content type and security headers. * Generates an HTTP error response with appropriate content type and security headers.
* Can return either plain text or detailed JSON error format. * Can return either plain text or detailed JSON error format.
*
* @param {string} message - Error message to display * @param {string} message - Error message to display
* @param {number} status - HTTP status code (e.g., 400, 404, 500) * @param {number} status - HTTP status code (e.g., 400, 404, 500)
* @param {boolean} includeDetails - Whether to include detailed JSON error information * @param {boolean} includeDetails - Whether to include detailed JSON error information
-2
View File
@@ -33,7 +33,6 @@ import { isGitLFSRequest, isGitRequest } from '../protocols/git.js';
* - Registry API endpoints (/v2/...) * - Registry API endpoints (/v2/...)
* - Docker-specific User-Agent headers * - Docker-specific User-Agent headers
* - Docker/OCI manifest Accept headers * - Docker/OCI manifest Accept headers
*
* @param {Request} request - The incoming request object * @param {Request} request - The incoming request object
* @param {URL} url - Parsed URL object * @param {URL} url - Parsed URL object
* @returns {boolean} True if this is a container registry operation * @returns {boolean} True if this is a container registry operation
@@ -85,7 +84,6 @@ export { isAIInferenceRequest, isGitLFSRequest, isGitRequest };
* Different protocols have different allowed methods: * Different protocols have different allowed methods:
* - Regular requests: GET, HEAD (configurable via SECURITY.ALLOWED_METHODS) * - Regular requests: GET, HEAD (configurable via SECURITY.ALLOWED_METHODS)
* - Git/LFS/Docker/AI: GET, HEAD, POST, PUT, PATCH * - Git/LFS/Docker/AI: GET, HEAD, POST, PUT, PATCH
*
* @param {Request} request - The incoming request object * @param {Request} request - The incoming request object
* @param {URL} url - Parsed URL object * @param {URL} url - Parsed URL object
* @param {import('../config/index.js').ApplicationConfig} config - Configuration object * @param {import('../config/index.js').ApplicationConfig} config - Configuration object
+2 -2
View File
@@ -18,9 +18,9 @@ export class PerformanceTestHelper {
/** /**
* Measure execution time of an async function * Measure execution time of an async function
* @param {Function} fn - Async function to measure * @param {() => Promise<unknown>} fn - Async function to measure
* @param {string} name - Measurement name * @param {string} name - Measurement name
* @returns {Promise<any>} Function result * @returns {Promise<unknown>} Function result
*/ */
async measure(fn, name = 'operation') { async measure(fn, name = 'operation') {
const start = performance.now(); const start = performance.now();
+4 -4
View File
@@ -5,7 +5,7 @@
/** /**
* Create a mock request with default options * Create a mock request with default options
* @param {string} url - Request URL * @param {string} url - Request URL
* @param {Object} options - Request options * @param {object} options - Request options
* @returns {Request} Mock request object * @returns {Request} Mock request object
*/ */
export function createMockRequest(url, options = {}) { export function createMockRequest(url, options = {}) {
@@ -23,7 +23,7 @@ export function createMockRequest(url, options = {}) {
/** /**
* Create a mock response with default options * Create a mock response with default options
* @param {string} body - Response body * @param {string} body - Response body
* @param {Object} options - Response options * @param {object} options - Response options
* @returns {Response} Mock response object * @returns {Response} Mock response object
*/ */
export function createMockResponse(body = 'OK', options = {}) { export function createMockResponse(body = 'OK', options = {}) {
@@ -80,7 +80,7 @@ export function createDockerRequest(url, options = {}) {
/** /**
* Mock fetch function for testing * Mock fetch function for testing
* @param {string} url - Request URL * @param {string} url - Request URL
* @param {Object} _options - Fetch options * @param {object} _options - Fetch options
* @returns {Promise<Response>} Mock response * @returns {Promise<Response>} Mock response
*/ */
export function mockFetch(url, _options = {}) { export function mockFetch(url, _options = {}) {
@@ -101,7 +101,7 @@ export function mockFetch(url, _options = {}) {
* Create a mock npm registry response * Create a mock npm registry response
* @param {string} packageName - Package name * @param {string} packageName - Package name
* @param {string} version - Package version * @param {string} version - Package version
* @returns {Object} Mock npm registry response * @returns {object} Mock npm registry response
*/ */
export function createMockNpmRegistryResponse(packageName, version = '1.0.0') { export function createMockNpmRegistryResponse(packageName, version = '1.0.0') {
return { return {