@vltpkg/vsr 0.0.0-26 → 0.0.0-27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +10 -114
- package/dist/README.md +1 -0
- package/dist/assets/public/favicon.ico +0 -0
- package/dist/assets/public/fonts/courier-bold-italic.ttf +0 -0
- package/dist/assets/public/fonts/courier-bold.ttf +0 -0
- package/dist/assets/public/fonts/courier-italic.ttf +0 -0
- package/dist/assets/public/fonts/courier-regular.ttf +0 -0
- package/dist/assets/public/fonts/geist-mono.ttf +0 -0
- package/dist/assets/public/fonts/inter.ttf +0 -0
- package/dist/assets/public/index.html +70 -0
- package/dist/assets/public/index.js +1300 -0
- package/dist/assets/public/index.js.map +7 -0
- package/dist/assets/public/main.css +1 -0
- package/dist/bin/vsr.js +771 -0
- package/dist/index.js +28283 -0
- package/dist/index.js.map +8 -0
- package/package.json +6 -49
- package/DEPLOY.md +0 -163
- package/config.ts +0 -221
- package/drizzle.config.js +0 -40
- package/info/COMPARISONS.md +0 -37
- package/info/CONFIGURATION.md +0 -143
- package/info/CONTRIBUTING.md +0 -32
- package/info/DATABASE_SETUP.md +0 -108
- package/info/GRANULAR_ACCESS_TOKENS.md +0 -160
- package/info/PROJECT_STRUCTURE.md +0 -291
- package/info/ROADMAP.md +0 -27
- package/info/SUPPORT.md +0 -39
- package/info/TESTING.md +0 -301
- package/info/USER_SUPPORT.md +0 -31
- package/scripts/build-assets.js +0 -31
- package/scripts/build-bin.js +0 -62
- package/scripts/prepack.js +0 -27
- package/src/bin/vsr.ts +0 -484
- package/src/db/client.ts +0 -590
- package/src/db/migrations/0000_faulty_ricochet.sql +0 -14
- package/src/db/migrations/0000_initial.sql +0 -29
- package/src/db/migrations/0001_uuid_validation.sql +0 -35
- package/src/db/migrations/0001_wealthy_magdalene.sql +0 -7
- package/src/db/migrations/drop.sql +0 -3
- package/src/db/migrations/meta/0000_snapshot.json +0 -104
- package/src/db/migrations/meta/0001_snapshot.json +0 -155
- package/src/db/migrations/meta/_journal.json +0 -20
- package/src/db/schema.ts +0 -43
- package/src/index.ts +0 -434
- package/src/middleware/config.ts +0 -79
- package/src/middleware/telemetry.ts +0 -43
- package/src/queue/index.ts +0 -97
- package/src/routes/access.ts +0 -852
- package/src/routes/docs.ts +0 -63
- package/src/routes/misc.ts +0 -469
- package/src/routes/packages.ts +0 -2823
- package/src/routes/ping.ts +0 -39
- package/src/routes/search.ts +0 -131
- package/src/routes/static.ts +0 -74
- package/src/routes/tokens.ts +0 -259
- package/src/routes/users.ts +0 -68
- package/src/utils/auth.ts +0 -202
- package/src/utils/cache.ts +0 -587
- package/src/utils/config.ts +0 -50
- package/src/utils/database.ts +0 -69
- package/src/utils/docs.ts +0 -146
- package/src/utils/packages.ts +0 -453
- package/src/utils/response.ts +0 -125
- package/src/utils/routes.ts +0 -64
- package/src/utils/spa.ts +0 -52
- package/src/utils/tracing.ts +0 -52
- package/src/utils/upstream.ts +0 -172
- package/test/access.test.ts +0 -705
- package/test/audit.test.ts +0 -828
- package/test/dashboard.test.ts +0 -693
- package/test/dist-tags.test.ts +0 -678
- package/test/manifest.test.ts +0 -436
- package/test/packument.test.ts +0 -530
- package/test/ping.test.ts +0 -41
- package/test/search.test.ts +0 -472
- package/test/setup.ts +0 -130
- package/test/static.test.ts +0 -646
- package/test/tokens.test.ts +0 -389
- package/test/utils/auth.test.ts +0 -214
- package/test/utils/packages.test.ts +0 -235
- package/test/utils/response.test.ts +0 -184
- package/test/whoami.test.ts +0 -119
- package/tsconfig.json +0 -16
- package/tsconfig.worker.json +0 -3
- package/typedoc.mjs +0 -2
- package/types.ts +0 -598
- package/vitest.config.ts +0 -25
- package/vlt.json.example +0 -56
- package/wrangler.json +0 -65
- /package/{src → dist}/assets/public/images/bg.png +0 -0
- /package/{src → dist}/assets/public/images/clients/logo-bun.png +0 -0
- /package/{src → dist}/assets/public/images/clients/logo-deno.png +0 -0
- /package/{src → dist}/assets/public/images/clients/logo-npm.png +0 -0
- /package/{src → dist}/assets/public/images/clients/logo-pnpm.png +0 -0
- /package/{src → dist}/assets/public/images/clients/logo-vlt.png +0 -0
- /package/{src → dist}/assets/public/images/clients/logo-yarn.png +0 -0
- /package/{src → dist}/assets/public/images/favicon/apple-touch-icon.png +0 -0
- /package/{src → dist}/assets/public/images/favicon/favicon-96x96.png +0 -0
- /package/{src → dist}/assets/public/images/favicon/favicon.ico +0 -0
- /package/{src → dist}/assets/public/images/favicon/favicon.svg +0 -0
- /package/{src → dist}/assets/public/images/favicon/site.webmanifest +0 -0
- /package/{src → dist}/assets/public/images/favicon/web-app-manifest-192x192.png +0 -0
- /package/{src → dist}/assets/public/images/favicon/web-app-manifest-512x512.png +0 -0
- /package/{src → dist}/assets/public/styles/styles.css +0 -0
- /package/{src → dist}/bin/demo/package.json +0 -0
- /package/{src → dist}/bin/demo/vlt.json +0 -0
package/src/utils/response.ts
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
import type { HonoContext, ApiError } from '../../types.ts'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* JSON response handler middleware that formats JSON based on Accept headers
|
|
5
|
-
* @returns {Function} Hono middleware function
|
|
6
|
-
*/
|
|
7
|
-
export function jsonResponseHandler() {
|
|
8
|
-
return async (c: any, next: any) => {
|
|
9
|
-
// Override the json method to handle formatting
|
|
10
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
11
|
-
const originalJson = c.json.bind(c)
|
|
12
|
-
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
14
|
-
c.json = (data: any, status?: number) => {
|
|
15
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
16
|
-
const acceptHeader = c.req.header('accept') || ''
|
|
17
|
-
|
|
18
|
-
// If the client accepts the npm install format, return minimal JSON
|
|
19
|
-
|
|
20
|
-
if (
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
22
|
-
acceptHeader.includes('application/vnd.npm.install-v1+json')
|
|
23
|
-
) {
|
|
24
|
-
// Use original json method for minimal output
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call
|
|
26
|
-
return originalJson(data, status)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// For other requests, return pretty-printed JSON
|
|
30
|
-
// Preserve existing headers that were set via c.header()
|
|
31
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-argument
|
|
32
|
-
const existingHeaders = new Headers(c.res?.headers || {})
|
|
33
|
-
existingHeaders.set(
|
|
34
|
-
'Content-Type',
|
|
35
|
-
'application/json; charset=UTF-8',
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
39
|
-
c.res = new Response(JSON.stringify(data, null, 2), {
|
|
40
|
-
status: status || 200,
|
|
41
|
-
headers: existingHeaders,
|
|
42
|
-
})
|
|
43
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-member-access
|
|
44
|
-
return c.res
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
48
|
-
await next()
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Creates a standardized JSON error response
|
|
54
|
-
* @param {HonoContext} c - The Hono context
|
|
55
|
-
* @param {string | ApiError} error - Error message or object
|
|
56
|
-
* @param {number} [status] - HTTP status code
|
|
57
|
-
* @returns {any} JSON error response
|
|
58
|
-
*/
|
|
59
|
-
export function jsonError(
|
|
60
|
-
c: HonoContext,
|
|
61
|
-
error: string | ApiError,
|
|
62
|
-
status = 400,
|
|
63
|
-
) {
|
|
64
|
-
const errorObj: ApiError =
|
|
65
|
-
typeof error === 'string' ? { error } : error
|
|
66
|
-
|
|
67
|
-
return c.json(errorObj, status as any)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Creates a standardized JSON success response
|
|
72
|
-
* @param {HonoContext} c - The Hono context
|
|
73
|
-
* @param {any} data - Response data
|
|
74
|
-
* @param {number} [status] - HTTP status code
|
|
75
|
-
* @returns {any} JSON success response
|
|
76
|
-
*/
|
|
77
|
-
export function jsonSuccess(c: HonoContext, data: any, status = 200) {
|
|
78
|
-
return c.json(data, status as any)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Creates a 404 Not Found response
|
|
83
|
-
* @param {HonoContext} c - The Hono context
|
|
84
|
-
* @param {string} [message] - Optional custom message
|
|
85
|
-
* @returns {any} 404 JSON response
|
|
86
|
-
*/
|
|
87
|
-
export function notFound(c: HonoContext, message = 'Not Found') {
|
|
88
|
-
return jsonError(c, { error: message }, 404)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Creates a 401 Unauthorized response
|
|
93
|
-
* @param {HonoContext} c - The Hono context
|
|
94
|
-
* @param {string} [message] - Optional custom message
|
|
95
|
-
* @returns {any} 401 JSON response
|
|
96
|
-
*/
|
|
97
|
-
export function unauthorized(
|
|
98
|
-
c: HonoContext,
|
|
99
|
-
message = 'Unauthorized',
|
|
100
|
-
) {
|
|
101
|
-
return jsonError(c, { error: message }, 401)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Creates a 403 Forbidden response
|
|
106
|
-
* @param {HonoContext} c - The Hono context
|
|
107
|
-
* @param {string} [message] - Optional custom message
|
|
108
|
-
* @returns {any} 403 JSON response
|
|
109
|
-
*/
|
|
110
|
-
export function forbidden(c: HonoContext, message = 'Forbidden') {
|
|
111
|
-
return jsonError(c, { error: message }, 403)
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Creates a 500 Internal Server Error response
|
|
116
|
-
* @param {HonoContext} c - The Hono context
|
|
117
|
-
* @param {string} [message] - Optional custom message
|
|
118
|
-
* @returns {any} 500 JSON response
|
|
119
|
-
*/
|
|
120
|
-
export function internalServerError(
|
|
121
|
-
c: HonoContext,
|
|
122
|
-
message = 'Internal Server Error',
|
|
123
|
-
) {
|
|
124
|
-
return jsonError(c, { error: message }, 500)
|
|
125
|
-
}
|
package/src/utils/routes.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import type { HonoContext } from '../../types.ts'
|
|
2
|
-
|
|
3
|
-
// Public / Static Assets
|
|
4
|
-
export function requiresToken(c: HonoContext): boolean {
|
|
5
|
-
const { path } = c.req
|
|
6
|
-
const publicRoutes = [
|
|
7
|
-
'/images',
|
|
8
|
-
'/styles',
|
|
9
|
-
'/-/auth/*',
|
|
10
|
-
'/-/ping',
|
|
11
|
-
'/-/docs',
|
|
12
|
-
'/',
|
|
13
|
-
]
|
|
14
|
-
|
|
15
|
-
// Check for upstream utility routes that are public (ping and docs only)
|
|
16
|
-
const upstreamPublicPattern = /^\/[^/]+\/-\/(ping|docs)$/
|
|
17
|
-
if (upstreamPublicPattern.test(path)) {
|
|
18
|
-
return false // These are public, no token required
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Check standard public routes
|
|
22
|
-
const isStandardPublicRoute = publicRoutes.some(route => {
|
|
23
|
-
if (route.endsWith('/*')) {
|
|
24
|
-
return path.startsWith(route.slice(0, -2))
|
|
25
|
-
}
|
|
26
|
-
return path === route || path.startsWith(route)
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
if (isStandardPublicRoute) {
|
|
30
|
-
return false // No token required
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Package routes should be public for downloads
|
|
34
|
-
// This includes hash-based routes, upstream routes, and legacy redirects
|
|
35
|
-
const isPackageRoute =
|
|
36
|
-
path.startsWith('/*/') || // Hash-based routes (literal asterisk)
|
|
37
|
-
/^\/[^-/][^/]*\/[^/]/.test(path) || // Upstream or package routes
|
|
38
|
-
/^\/[^-/][^/]*$/.test(path) || // Root package routes
|
|
39
|
-
/^\/@[^/]+\/[^/]+/.test(path) // Scoped packages
|
|
40
|
-
|
|
41
|
-
// Exclude PUT requests (publishing) from being public
|
|
42
|
-
const isPutRequest = c.req.method === 'PUT'
|
|
43
|
-
const isPublicPackageRoute = isPackageRoute && !isPutRequest
|
|
44
|
-
|
|
45
|
-
if (isPublicPackageRoute) {
|
|
46
|
-
return false // No token required
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// All other routes require authentication
|
|
50
|
-
return true
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Catch-all for non-GET methods
|
|
54
|
-
export function catchAll(c: HonoContext) {
|
|
55
|
-
return c.json({ error: 'Method not allowed' }, 405)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function notFound(c: HonoContext) {
|
|
59
|
-
return c.json({ error: 'Not found' }, 404)
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function isOK(c: HonoContext) {
|
|
63
|
-
return c.json({}, 200)
|
|
64
|
-
}
|
package/src/utils/spa.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
// This function now takes the ASSETS environment binding as a parameter
|
|
2
|
-
// to avoid the circular dependency issue
|
|
3
|
-
export const getApp = async (
|
|
4
|
-
assetsBinding?: any,
|
|
5
|
-
): Promise<string> => {
|
|
6
|
-
if (assetsBinding) {
|
|
7
|
-
try {
|
|
8
|
-
// Use the ASSETS binding to fetch the index.html file
|
|
9
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
|
|
10
|
-
const response = (await assetsBinding.fetch(
|
|
11
|
-
new Request('http://localhost/public/index.html'),
|
|
12
|
-
)) as Response
|
|
13
|
-
if (response.ok) {
|
|
14
|
-
const html = await response.text()
|
|
15
|
-
return changeSourceReferences(html)
|
|
16
|
-
}
|
|
17
|
-
} catch (error) {
|
|
18
|
-
// eslint-disable-next-line no-console
|
|
19
|
-
console.error('Failed to load index.html from assets:', error)
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Fallback: provide a simple HTML page
|
|
24
|
-
return `<!DOCTYPE html>
|
|
25
|
-
<html lang="en">
|
|
26
|
-
<head>
|
|
27
|
-
<meta charset="utf-8">
|
|
28
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
29
|
-
<title>vlt | Explorer</title>
|
|
30
|
-
<style>
|
|
31
|
-
body { font-family: system-ui, sans-serif; padding: 2rem; text-align: center; }
|
|
32
|
-
.error { color: #dc2626; margin: 1rem 0; }
|
|
33
|
-
</style>
|
|
34
|
-
</head>
|
|
35
|
-
<body>
|
|
36
|
-
<h1>vlt | Explorer</h1>
|
|
37
|
-
<div class="error">Unable to load the application assets.</div>
|
|
38
|
-
<p>Please check that the server is running correctly and assets are available.</p>
|
|
39
|
-
</body>
|
|
40
|
-
</html>`
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export const changeSourceReferences = (html: string): string => {
|
|
44
|
-
html = html.replace('href="/main.css', 'href="/public/main.css')
|
|
45
|
-
html = html.replace(
|
|
46
|
-
'href="/favicon.ico"',
|
|
47
|
-
'href="/public/favicon.ico"',
|
|
48
|
-
)
|
|
49
|
-
html = html.replace('href="/fonts/', 'href="/public/fonts/')
|
|
50
|
-
html = html.replace('src="/index.js"', 'src="/public/index.js"')
|
|
51
|
-
return html
|
|
52
|
-
}
|
package/src/utils/tracing.ts
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Simple tracing utility for debugging and monitoring
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Logs a trace message with timestamp
|
|
7
|
-
* @param {string} _message - The message to log
|
|
8
|
-
* @param {any} _data - Optional additional data to log
|
|
9
|
-
*/
|
|
10
|
-
export function trace(_message: string, _data?: any): void {
|
|
11
|
-
// Tracing disabled for production
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Measures execution time of a function
|
|
16
|
-
* @param {string} name - Name of the operation being measured
|
|
17
|
-
* @param {() => Promise<any>} fn - Function to measure
|
|
18
|
-
* @returns {Promise<any>} Result of the function
|
|
19
|
-
*/
|
|
20
|
-
export async function measureTime<T>(
|
|
21
|
-
name: string,
|
|
22
|
-
fn: () => Promise<T>,
|
|
23
|
-
): Promise<T> {
|
|
24
|
-
const start = performance.now()
|
|
25
|
-
try {
|
|
26
|
-
const result = await fn()
|
|
27
|
-
const duration = performance.now() - start
|
|
28
|
-
trace(`${name} completed in ${duration.toFixed(2)}ms`)
|
|
29
|
-
return result
|
|
30
|
-
} catch (error) {
|
|
31
|
-
const duration = performance.now() - start
|
|
32
|
-
trace(`${name} failed after ${duration.toFixed(2)}ms`, error)
|
|
33
|
-
throw error
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Session monitoring middleware for tracking requests
|
|
39
|
-
* @param {any} _c - The Hono context
|
|
40
|
-
* @param {() => Promise<void>} next - The next middleware function
|
|
41
|
-
* @returns {Promise<void>} Result of next middleware
|
|
42
|
-
*/
|
|
43
|
-
export function sessionMonitor(
|
|
44
|
-
_c: any,
|
|
45
|
-
next: () => Promise<void>,
|
|
46
|
-
): Promise<void> {
|
|
47
|
-
// Session monitoring is currently disabled
|
|
48
|
-
// This middleware can be extended to add session tracking functionality
|
|
49
|
-
trace('Session monitoring middleware called')
|
|
50
|
-
|
|
51
|
-
return next()
|
|
52
|
-
}
|
package/src/utils/upstream.ts
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { ORIGIN_CONFIG, RESERVED_ROUTES } from '../../config.ts'
|
|
2
|
-
import type {
|
|
3
|
-
UpstreamConfig,
|
|
4
|
-
ParsedPackageInfo,
|
|
5
|
-
} from '../../types.ts'
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Validates if an upstream name is allowed (not reserved)
|
|
9
|
-
* @param {string} upstreamName - The upstream name to validate
|
|
10
|
-
* @returns {boolean} True if valid, false if reserved
|
|
11
|
-
*/
|
|
12
|
-
export function isValidUpstreamName(upstreamName: string): boolean {
|
|
13
|
-
return !RESERVED_ROUTES.includes(upstreamName)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Gets the upstream configuration by name
|
|
18
|
-
* @param {string} upstreamName - The upstream name
|
|
19
|
-
* @returns {UpstreamConfig | null} The upstream config or null if not found
|
|
20
|
-
*/
|
|
21
|
-
export function getUpstreamConfig(
|
|
22
|
-
upstreamName: string,
|
|
23
|
-
): UpstreamConfig | null {
|
|
24
|
-
return ORIGIN_CONFIG.upstreams[upstreamName] ?? null
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Gets the default upstream name
|
|
29
|
-
* @returns {string} The default upstream name
|
|
30
|
-
*/
|
|
31
|
-
export function getDefaultUpstream(): string {
|
|
32
|
-
return ORIGIN_CONFIG.default
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Generates a cache key for upstream package data
|
|
37
|
-
* @param {string} upstreamName - The upstream name
|
|
38
|
-
* @param {string} packageName - The package name
|
|
39
|
-
* @param {string} [version] - The package version (optional)
|
|
40
|
-
* @returns {string} A deterministic hash ID
|
|
41
|
-
*/
|
|
42
|
-
export function generateCacheKey(
|
|
43
|
-
upstreamName: string,
|
|
44
|
-
packageName: string,
|
|
45
|
-
version?: string,
|
|
46
|
-
): string {
|
|
47
|
-
const key =
|
|
48
|
-
version ?
|
|
49
|
-
`${upstreamName}:${packageName}:${version}`
|
|
50
|
-
: `${upstreamName}:${packageName}`
|
|
51
|
-
|
|
52
|
-
// Use TextEncoder for cross-platform compatibility
|
|
53
|
-
const encoder = new TextEncoder()
|
|
54
|
-
const data = encoder.encode(key)
|
|
55
|
-
|
|
56
|
-
// Convert to base64 using btoa
|
|
57
|
-
const base64 = btoa(String.fromCharCode(...data))
|
|
58
|
-
|
|
59
|
-
// Convert base64 to base64url format (replace + with -, / with _, remove =)
|
|
60
|
-
return base64
|
|
61
|
-
.replace(/\+/g, '-')
|
|
62
|
-
.replace(/\//g, '_')
|
|
63
|
-
.replace(/=/g, '')
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Parses a request path to extract package information
|
|
68
|
-
* @param {string} path - The request path
|
|
69
|
-
* @returns {ParsedPackageInfo} Parsed package info
|
|
70
|
-
*/
|
|
71
|
-
export function parsePackageSpec(path: string): ParsedPackageInfo {
|
|
72
|
-
// Remove leading slash and split by '/'
|
|
73
|
-
const segments = path.replace(/^\/+/, '').split('/')
|
|
74
|
-
|
|
75
|
-
// Handle different path patterns
|
|
76
|
-
if (segments.length === 0) {
|
|
77
|
-
return { packageName: '', segments }
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// Check if first segment is an upstream name
|
|
81
|
-
const firstSegment = segments[0]
|
|
82
|
-
if (firstSegment && ORIGIN_CONFIG.upstreams[firstSegment]) {
|
|
83
|
-
// Path starts with upstream name: /upstream/package/version
|
|
84
|
-
const upstream = firstSegment
|
|
85
|
-
const packageSegments = segments.slice(1)
|
|
86
|
-
|
|
87
|
-
if (packageSegments.length === 0) {
|
|
88
|
-
return { upstream, packageName: '', segments: packageSegments }
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Handle scoped packages: @scope/package
|
|
92
|
-
if (
|
|
93
|
-
packageSegments[0]?.startsWith('@') &&
|
|
94
|
-
packageSegments.length > 1
|
|
95
|
-
) {
|
|
96
|
-
const packageName = `${packageSegments[0]}/${packageSegments[1]}`
|
|
97
|
-
const version = packageSegments[2]
|
|
98
|
-
const remainingSegments = packageSegments.slice(2)
|
|
99
|
-
return {
|
|
100
|
-
upstream,
|
|
101
|
-
packageName,
|
|
102
|
-
version,
|
|
103
|
-
segments: remainingSegments,
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Handle regular packages
|
|
108
|
-
const packageName = packageSegments[0] || ''
|
|
109
|
-
const version = packageSegments[1]
|
|
110
|
-
const remainingSegments = packageSegments.slice(1)
|
|
111
|
-
return {
|
|
112
|
-
upstream,
|
|
113
|
-
packageName,
|
|
114
|
-
version,
|
|
115
|
-
segments: remainingSegments,
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// No upstream in path, treat as package name
|
|
120
|
-
if (firstSegment?.startsWith('@') && segments.length > 1) {
|
|
121
|
-
// Scoped package: @scope/package/version
|
|
122
|
-
const packageName = `${segments[0]}/${segments[1] || ''}`
|
|
123
|
-
const version = segments[2]
|
|
124
|
-
const remainingSegments = segments.slice(2)
|
|
125
|
-
return { packageName, version, segments: remainingSegments }
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Regular package: package/version
|
|
129
|
-
const packageName = segments[0] || ''
|
|
130
|
-
const version = segments[1]
|
|
131
|
-
const remainingSegments = segments.slice(1)
|
|
132
|
-
return { packageName, version, segments: remainingSegments }
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Constructs the upstream URL for a package request
|
|
137
|
-
* @param {UpstreamConfig} upstreamConfig - The upstream configuration
|
|
138
|
-
* @param {string} packageName - The package name
|
|
139
|
-
* @param {string} [path] - Additional path segments
|
|
140
|
-
* @returns {string} The full upstream URL
|
|
141
|
-
*/
|
|
142
|
-
export function buildUpstreamUrl(
|
|
143
|
-
upstreamConfig: UpstreamConfig,
|
|
144
|
-
packageName: string,
|
|
145
|
-
path = '',
|
|
146
|
-
): string {
|
|
147
|
-
const baseUrl = upstreamConfig.url.replace(/\/$/, '')
|
|
148
|
-
const encodedPackage = encodeURIComponent(packageName)
|
|
149
|
-
|
|
150
|
-
switch (upstreamConfig.type) {
|
|
151
|
-
case 'npm':
|
|
152
|
-
case 'vsr':
|
|
153
|
-
return `${baseUrl}/${encodedPackage}${path ? `/${path}` : ''}`
|
|
154
|
-
case 'jsr':
|
|
155
|
-
// JSR has a different URL structure
|
|
156
|
-
return `${baseUrl}/${encodedPackage}${path ? `/${path}` : ''}`
|
|
157
|
-
case 'local':
|
|
158
|
-
return `${baseUrl}/${encodedPackage}${path ? `/${path}` : ''}`
|
|
159
|
-
default:
|
|
160
|
-
return `${baseUrl}/${encodedPackage}${path ? `/${path}` : ''}`
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Checks if proxying is enabled for an upstream
|
|
166
|
-
* @param {string} upstreamName - The upstream name
|
|
167
|
-
* @returns {boolean} True if proxying is enabled
|
|
168
|
-
*/
|
|
169
|
-
export function isProxyEnabled(upstreamName: string): boolean {
|
|
170
|
-
const config = getUpstreamConfig(upstreamName)
|
|
171
|
-
return config !== null && config.type !== 'local'
|
|
172
|
-
}
|