[READ-ONLY] a fast, modern browser for the npm registry
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor(cli): use h3/srvx for internal server

+598 -637
-13
cli/build.config.ts
··· 1 - import { defineBuildConfig } from 'unbuild' 2 - 3 - export default defineBuildConfig({ 4 - entries: [ 5 - { input: 'src/index', name: 'index' }, 6 - { input: 'src/cli', name: 'cli' }, 7 - ], 8 - declaration: true, 9 - clean: true, 10 - rollup: { 11 - emitCJS: false, 12 - }, 13 - })
+6 -6
cli/package.json
··· 10 10 "exports": { 11 11 ".": { 12 12 "import": "./dist/index.mjs", 13 - "types": "./dist/index.d.ts" 13 + "types": "./dist/index.d.mts" 14 14 } 15 15 }, 16 16 "files": [ 17 17 "dist" 18 18 ], 19 19 "scripts": { 20 - "build": "unbuild", 20 + "build": "tsdown", 21 21 "dev": "node --experimental-strip-types src/cli.ts", 22 22 "test:types": "tsc --noEmit" 23 23 }, ··· 26 26 "citty": "^0.2.0", 27 27 "consola": "^3.4.2", 28 28 "defu": "^6.1.4", 29 - "h3": "^1.15.5", 30 - "listhen": "^1.9.0", 29 + "h3": "^2.0.1-rc.11", 31 30 "ofetch": "^1.5.1", 32 31 "picocolors": "^1.1.1", 32 + "srvx": "^0.10.1", 33 33 "validate-npm-package-name": "^7.0.2" 34 34 }, 35 35 "devDependencies": { 36 36 "@types/node": "^24.10.9", 37 37 "@types/validate-npm-package-name": "^4.0.2", 38 - "typescript": "^5.9.3", 39 - "unbuild": "^3.6.1" 38 + "tsdown": "^0.20.1", 39 + "typescript": "^5.9.3" 40 40 } 41 41 }
+5 -4
cli/src/cli.ts
··· 4 4 import * as p from '@clack/prompts' 5 5 import pc from 'picocolors' 6 6 import { defineCommand, runMain } from 'citty' 7 - import { listen } from 'listhen' 8 - import { toNodeListener } from 'h3' 7 + import { serve } from 'srvx' 9 8 import { createConnectorApp, generateToken, CONNECTOR_VERSION } from './server.ts' 10 9 import { getNpmUser } from './npm-client.ts' 11 10 import { initLogger, showToken, logInfo, logWarning, logError } from './logger.ts' ··· 95 94 96 95 const app = createConnectorApp(token) 97 96 98 - await listen(toNodeListener(app), { 97 + const server = serve({ 99 98 port, 100 99 hostname: '127.0.0.1', 101 - showURL: false, 100 + fetch: app.fetch, 102 101 }) 102 + 103 + await server.ready() 103 104 104 105 logInfo('Waiting for connection... (Press Ctrl+C to stop)') 105 106 },
+375 -444
cli/src/server.ts
··· 2 2 import { readFileSync } from 'node:fs' 3 3 import { fileURLToPath } from 'node:url' 4 4 import { dirname, join } from 'node:path' 5 - import { 6 - createApp, 7 - createRouter, 8 - eventHandler, 9 - readBody, 10 - getQuery, 11 - createError, 12 - getHeader, 13 - getRequestHeader, 14 - setResponseHeaders, 15 - getRouterParam, 16 - } from 'h3' 5 + import { H3, HTTPError, handleCors, type H3Event } from 'h3' 6 + import type { CorsOptions } from 'h3' 17 7 18 - const ALLOWED_ORIGINS = new Set(['https://npmx.dev', 'http://localhost:3000']) 19 8 import type { ConnectorState, PendingOperation, OperationType, ApiResponse } from './types.ts' 20 9 import { 21 10 getNpmUser, ··· 59 48 return crypto.randomBytes(8).toString('hex') 60 49 } 61 50 51 + const corsOptions: CorsOptions = { 52 + origin: ['https://npmx.dev', 'http://localhost:3000'], 53 + methods: ['GET', 'POST', 'DELETE', 'OPTIONS'], 54 + allowHeaders: ['Content-Type', 'Authorization'], 55 + } 56 + 62 57 export function createConnectorApp(expectedToken: string) { 63 58 const state: ConnectorState = { 64 59 session: { ··· 69 64 operations: [], 70 65 } 71 66 72 - function setCorsHeaders(event: Parameters<typeof setResponseHeaders>[0]) { 73 - const origin = getRequestHeader(event, 'origin') 74 - if (origin && ALLOWED_ORIGINS.has(origin)) { 75 - setResponseHeaders(event, { 76 - 'Access-Control-Allow-Origin': origin, 77 - 'Access-Control-Allow-Methods': 'GET, POST, DELETE, OPTIONS', 78 - 'Access-Control-Allow-Headers': 'Content-Type, Authorization', 79 - }) 80 - } 81 - } 67 + const app = new H3() 82 68 83 - const app = createApp({ 84 - onRequest(event) { 85 - setCorsHeaders(event) 86 - }, 87 - onBeforeResponse(event) { 88 - setCorsHeaders(event) 89 - }, 69 + // Handle CORS for all requests (including preflight) 70 + app.use((event: H3Event) => { 71 + const corsResult = handleCors(event, corsOptions) 72 + if (corsResult !== false) { 73 + return corsResult 74 + } 90 75 }) 91 - const router = createRouter() 92 76 93 - // Handle CORS preflight requests 94 - router.options( 95 - '/**', 96 - eventHandler(() => ''), 97 - ) 98 - 99 - function validateToken(authHeader: string | null | undefined): boolean { 77 + function validateToken(authHeader: string | null): boolean { 100 78 if (!authHeader) return false 101 79 const token = authHeader.replace('Bearer ', '') 102 80 return token === expectedToken 103 81 } 104 82 105 - router.post( 106 - '/connect', 107 - eventHandler(async event => { 108 - const body = await readBody(event) 109 - if (body?.token !== expectedToken) { 110 - throw createError({ statusCode: 401, message: 'Invalid token' }) 111 - } 83 + app.post('/connect', async (event: H3Event) => { 84 + const body = (await event.req.json()) as { token?: string } 85 + if (body?.token !== expectedToken) { 86 + throw new HTTPError({ statusCode: 401, message: 'Invalid token' }) 87 + } 88 + 89 + const npmUser = await getNpmUser() 90 + state.session.connectedAt = Date.now() 91 + state.session.npmUser = npmUser 92 + 93 + return { 94 + success: true, 95 + data: { 96 + npmUser, 97 + connectedAt: state.session.connectedAt, 98 + }, 99 + } as ApiResponse 100 + }) 101 + 102 + app.get('/state', event => { 103 + const auth = event.req.headers.get('authorization') 104 + if (!validateToken(auth)) { 105 + throw new HTTPError({ statusCode: 401, message: 'Unauthorized' }) 106 + } 107 + 108 + return { 109 + success: true, 110 + data: { 111 + npmUser: state.session.npmUser, 112 + operations: state.operations, 113 + }, 114 + } as ApiResponse 115 + }) 116 + 117 + app.post('/operations', async event => { 118 + const auth = event.req.headers.get('authorization') 119 + if (!validateToken(auth)) { 120 + throw new HTTPError({ statusCode: 401, message: 'Unauthorized' }) 121 + } 112 122 113 - const npmUser = await getNpmUser() 114 - state.session.connectedAt = Date.now() 115 - state.session.npmUser = npmUser 123 + const body = (await event.req.json()) as { 124 + type: OperationType 125 + params: Record<string, string> 126 + description: string 127 + command: string 128 + } 129 + const { type, params, description, command } = body 116 130 117 - return { 118 - success: true, 119 - data: { 120 - npmUser, 121 - connectedAt: state.session.connectedAt, 122 - }, 123 - } as ApiResponse 124 - }), 125 - ) 131 + const operation: PendingOperation = { 132 + id: generateOperationId(), 133 + type, 134 + params, 135 + description, 136 + command, 137 + status: 'pending', 138 + createdAt: Date.now(), 139 + } 126 140 127 - router.get( 128 - '/state', 129 - eventHandler(event => { 130 - const auth = getHeader(event, 'authorization') 131 - if (!validateToken(auth)) { 132 - throw createError({ statusCode: 401, message: 'Unauthorized' }) 133 - } 141 + state.operations.push(operation) 134 142 135 - return { 136 - success: true, 137 - data: { 138 - npmUser: state.session.npmUser, 139 - operations: state.operations, 140 - }, 141 - } as ApiResponse 142 - }), 143 - ) 143 + return { 144 + success: true, 145 + data: operation, 146 + } as ApiResponse 147 + }) 144 148 145 - router.post( 146 - '/operations', 147 - eventHandler(async event => { 148 - const auth = getHeader(event, 'authorization') 149 - if (!validateToken(auth)) { 150 - throw createError({ statusCode: 401, message: 'Unauthorized' }) 151 - } 149 + app.post('/operations/batch', async event => { 150 + const auth = event.req.headers.get('authorization') 151 + if (!validateToken(auth)) { 152 + throw new HTTPError({ statusCode: 401, message: 'Unauthorized' }) 153 + } 152 154 153 - const body = await readBody(event) 154 - const { type, params, description, command } = body as { 155 - type: OperationType 156 - params: Record<string, string> 157 - description: string 158 - command: string 159 - } 155 + const operations = (await event.req.json()) as Array<{ 156 + type: OperationType 157 + params: Record<string, string> 158 + description: string 159 + command: string 160 + }> 160 161 162 + const created: PendingOperation[] = [] 163 + for (const op of operations) { 161 164 const operation: PendingOperation = { 162 165 id: generateOperationId(), 163 - type, 164 - params, 165 - description, 166 - command, 166 + type: op.type, 167 + params: op.params, 168 + description: op.description, 169 + command: op.command, 167 170 status: 'pending', 168 171 createdAt: Date.now(), 169 172 } 170 - 171 173 state.operations.push(operation) 174 + created.push(operation) 175 + } 172 176 173 - return { 174 - success: true, 175 - data: operation, 176 - } as ApiResponse 177 - }), 178 - ) 177 + return { 178 + success: true, 179 + data: created, 180 + } as ApiResponse 181 + }) 179 182 180 - router.post( 181 - '/operations/batch', 182 - eventHandler(async event => { 183 - const auth = getHeader(event, 'authorization') 184 - if (!validateToken(auth)) { 185 - throw createError({ statusCode: 401, message: 'Unauthorized' }) 186 - } 183 + app.post('/approve', event => { 184 + const auth = event.req.headers.get('authorization') 185 + if (!validateToken(auth)) { 186 + throw new HTTPError({ statusCode: 401, message: 'Unauthorized' }) 187 + } 187 188 188 - const body = await readBody(event) 189 - const operations = body as Array<{ 190 - type: OperationType 191 - params: Record<string, string> 192 - description: string 193 - command: string 194 - }> 189 + const url = new URL(event.req.url) 190 + const id = url.searchParams.get('id') 195 191 196 - const created: PendingOperation[] = [] 197 - for (const op of operations) { 198 - const operation: PendingOperation = { 199 - id: generateOperationId(), 200 - type: op.type, 201 - params: op.params, 202 - description: op.description, 203 - command: op.command, 204 - status: 'pending', 205 - createdAt: Date.now(), 206 - } 207 - state.operations.push(operation) 208 - created.push(operation) 209 - } 192 + const operation = state.operations.find(op => op.id === id) 193 + if (!operation) { 194 + throw new HTTPError({ statusCode: 404, message: 'Operation not found' }) 195 + } 210 196 211 - return { 212 - success: true, 213 - data: created, 214 - } as ApiResponse 215 - }), 216 - ) 197 + if (operation.status !== 'pending') { 198 + throw new HTTPError({ statusCode: 400, message: 'Operation is not pending' }) 199 + } 217 200 218 - router.post( 219 - '/approve', 220 - eventHandler(async event => { 221 - const auth = getHeader(event, 'authorization') 222 - if (!validateToken(auth)) { 223 - throw createError({ statusCode: 401, message: 'Unauthorized' }) 224 - } 201 + operation.status = 'approved' 202 + 203 + return { 204 + success: true, 205 + data: operation, 206 + } as ApiResponse 207 + }) 225 208 226 - const query = getQuery(event) 227 - const id = query.id as string 209 + app.post('/approve-all', event => { 210 + const auth = event.req.headers.get('authorization') 211 + if (!validateToken(auth)) { 212 + throw new HTTPError({ statusCode: 401, message: 'Unauthorized' }) 213 + } 228 214 229 - const operation = state.operations.find(op => op.id === id) 230 - if (!operation) { 231 - throw createError({ statusCode: 404, message: 'Operation not found' }) 232 - } 215 + const pendingOps = state.operations.filter(op => op.status === 'pending') 216 + for (const op of pendingOps) { 217 + op.status = 'approved' 218 + } 233 219 234 - if (operation.status !== 'pending') { 235 - throw createError({ statusCode: 400, message: 'Operation is not pending' }) 236 - } 220 + return { 221 + success: true, 222 + data: { approved: pendingOps.length }, 223 + } as ApiResponse 224 + }) 237 225 238 - operation.status = 'approved' 226 + app.post('/retry', event => { 227 + const auth = event.req.headers.get('authorization') 228 + if (!validateToken(auth)) { 229 + throw new HTTPError({ statusCode: 401, message: 'Unauthorized' }) 230 + } 239 231 240 - return { 241 - success: true, 242 - data: operation, 243 - } as ApiResponse 244 - }), 245 - ) 232 + const url = new URL(event.req.url) 233 + const id = url.searchParams.get('id') 246 234 247 - router.post( 248 - '/approve-all', 249 - eventHandler(async event => { 250 - const auth = getHeader(event, 'authorization') 251 - if (!validateToken(auth)) { 252 - throw createError({ statusCode: 401, message: 'Unauthorized' }) 253 - } 235 + const operation = state.operations.find(op => op.id === id) 236 + if (!operation) { 237 + throw new HTTPError({ statusCode: 404, message: 'Operation not found' }) 238 + } 254 239 255 - const pendingOps = state.operations.filter(op => op.status === 'pending') 256 - for (const op of pendingOps) { 257 - op.status = 'approved' 258 - } 240 + if (operation.status !== 'failed') { 241 + throw new HTTPError({ statusCode: 400, message: 'Only failed operations can be retried' }) 242 + } 259 243 260 - return { 261 - success: true, 262 - data: { approved: pendingOps.length }, 263 - } as ApiResponse 264 - }), 265 - ) 244 + // Reset the operation for retry 245 + operation.status = 'approved' 246 + operation.result = undefined 266 247 267 - router.post( 268 - '/retry', 269 - eventHandler(async event => { 270 - const auth = getHeader(event, 'authorization') 271 - if (!validateToken(auth)) { 272 - throw createError({ statusCode: 401, message: 'Unauthorized' }) 273 - } 248 + return { 249 + success: true, 250 + data: operation, 251 + } as ApiResponse 252 + }) 274 253 275 - const query = getQuery(event) 276 - const id = query.id as string 254 + app.post('/execute', async event => { 255 + const auth = event.req.headers.get('authorization') 256 + if (!validateToken(auth)) { 257 + throw new HTTPError({ statusCode: 401, message: 'Unauthorized' }) 258 + } 277 259 278 - const operation = state.operations.find(op => op.id === id) 279 - if (!operation) { 280 - throw createError({ statusCode: 404, message: 'Operation not found' }) 281 - } 260 + // OTP can be passed directly in the request body for this execution 261 + const body = (await event.req.json()) as { otp?: string } | null 262 + const otp = body?.otp 282 263 283 - if (operation.status !== 'failed') { 284 - throw createError({ statusCode: 400, message: 'Only failed operations can be retried' }) 285 - } 264 + const approvedOps = state.operations.filter(op => op.status === 'approved') 265 + const results: Array<{ id: string; result: NpmExecResult }> = [] 266 + let otpRequired = false 267 + const completedIds = new Set<string>() 268 + const failedIds = new Set<string>() 286 269 287 - // Reset the operation for retry 288 - operation.status = 'approved' 289 - operation.result = undefined 270 + // Execute operations in waves, respecting dependencies 271 + // Each wave contains operations whose dependencies are satisfied 272 + while (true) { 273 + // Find operations ready to run (no pending dependencies) 274 + const readyOps = approvedOps.filter(op => { 275 + // Already processed 276 + if (completedIds.has(op.id) || failedIds.has(op.id)) return false 277 + // No dependency - ready 278 + if (!op.dependsOn) return true 279 + // Dependency completed successfully - ready 280 + if (completedIds.has(op.dependsOn)) return true 281 + // Dependency failed - skip this one too 282 + if (failedIds.has(op.dependsOn)) { 283 + op.status = 'failed' 284 + op.result = { stdout: '', stderr: 'Skipped: dependency failed', exitCode: 1 } 285 + failedIds.add(op.id) 286 + results.push({ id: op.id, result: op.result }) 287 + return false 288 + } 289 + // Dependency still pending - not ready 290 + return false 291 + }) 290 292 291 - return { 292 - success: true, 293 - data: operation, 294 - } as ApiResponse 295 - }), 296 - ) 293 + // No more operations to run 294 + if (readyOps.length === 0) break 297 295 298 - router.post( 299 - '/execute', 300 - eventHandler(async event => { 301 - const auth = getHeader(event, 'authorization') 302 - if (!validateToken(auth)) { 303 - throw createError({ statusCode: 401, message: 'Unauthorized' }) 304 - } 296 + // If we've hit an OTP error and no OTP was provided, stop 297 + if (otpRequired && !otp) break 305 298 306 - // OTP can be passed directly in the request body for this execution 307 - const body = await readBody(event) 308 - const otp = body?.otp as string | undefined 299 + // Execute ready operations in parallel 300 + const runningOps = readyOps.map(async op => { 301 + op.status = 'running' 302 + const result = await executeOperation(op, otp) 303 + op.result = result 304 + op.status = result.exitCode === 0 ? 'completed' : 'failed' 309 305 310 - const approvedOps = state.operations.filter(op => op.status === 'approved') 311 - const results: Array<{ id: string; result: NpmExecResult }> = [] 312 - let otpRequired = false 313 - const completedIds = new Set<string>() 314 - const failedIds = new Set<string>() 306 + if (result.exitCode === 0) { 307 + completedIds.add(op.id) 308 + } else { 309 + failedIds.add(op.id) 310 + } 315 311 316 - // Execute operations in waves, respecting dependencies 317 - // Each wave contains operations whose dependencies are satisfied 318 - while (true) { 319 - // Find operations ready to run (no pending dependencies) 320 - const readyOps = approvedOps.filter(op => { 321 - // Already processed 322 - if (completedIds.has(op.id) || failedIds.has(op.id)) return false 323 - // No dependency - ready 324 - if (!op.dependsOn) return true 325 - // Dependency completed successfully - ready 326 - if (completedIds.has(op.dependsOn)) return true 327 - // Dependency failed - skip this one too 328 - if (failedIds.has(op.dependsOn)) { 329 - op.status = 'failed' 330 - op.result = { stdout: '', stderr: 'Skipped: dependency failed', exitCode: 1 } 331 - failedIds.add(op.id) 332 - results.push({ id: op.id, result: op.result }) 333 - return false 334 - } 335 - // Dependency still pending - not ready 336 - return false 337 - }) 312 + // Track if OTP is needed 313 + if (result.requiresOtp) { 314 + otpRequired = true 315 + } 338 316 339 - // No more operations to run 340 - if (readyOps.length === 0) break 317 + results.push({ id: op.id, result }) 318 + }) 341 319 342 - // If we've hit an OTP error and no OTP was provided, stop 343 - if (otpRequired && !otp) break 320 + await Promise.all(runningOps) 321 + } 344 322 345 - // Execute ready operations in parallel 346 - const runningOps = readyOps.map(async op => { 347 - op.status = 'running' 348 - const result = await executeOperation(op, otp) 349 - op.result = result 350 - op.status = result.exitCode === 0 ? 'completed' : 'failed' 323 + // Check if any operation had an auth failure 324 + const authFailure = results.some(r => r.result.authFailure) 351 325 352 - if (result.exitCode === 0) { 353 - completedIds.add(op.id) 354 - } else { 355 - failedIds.add(op.id) 356 - } 326 + return { 327 + success: true, 328 + data: { 329 + results, 330 + otpRequired, 331 + authFailure, 332 + }, 333 + } as ApiResponse 334 + }) 357 335 358 - // Track if OTP is needed 359 - if (result.requiresOtp) { 360 - otpRequired = true 361 - } 336 + app.delete('/operations', event => { 337 + const auth = event.req.headers.get('authorization') 338 + if (!validateToken(auth)) { 339 + throw new HTTPError({ statusCode: 401, message: 'Unauthorized' }) 340 + } 362 341 363 - results.push({ id: op.id, result }) 364 - }) 342 + const url = new URL(event.req.url) 343 + const id = url.searchParams.get('id') 365 344 366 - await Promise.all(runningOps) 367 - } 345 + const index = state.operations.findIndex(op => op.id === id) 346 + if (index === -1) { 347 + throw new HTTPError({ statusCode: 404, message: 'Operation not found' }) 348 + } 368 349 369 - // Check if any operation had an auth failure 370 - const authFailure = results.some(r => r.result.authFailure) 350 + const operation = state.operations[index] 351 + if (!operation || operation.status === 'running') { 352 + throw new HTTPError({ statusCode: 400, message: 'Cannot cancel running operation' }) 353 + } 371 354 372 - return { 373 - success: true, 374 - data: { 375 - results, 376 - otpRequired, 377 - authFailure, 378 - }, 379 - } as ApiResponse 380 - }), 381 - ) 355 + state.operations.splice(index, 1) 382 356 383 - router.delete( 384 - '/operations', 385 - eventHandler(async event => { 386 - const auth = getHeader(event, 'authorization') 387 - if (!validateToken(auth)) { 388 - throw createError({ statusCode: 401, message: 'Unauthorized' }) 389 - } 357 + return { success: true } as ApiResponse 358 + }) 390 359 391 - const query = getQuery(event) 392 - const id = query.id as string 360 + app.delete('/operations/all', event => { 361 + const auth = event.req.headers.get('authorization') 362 + if (!validateToken(auth)) { 363 + throw new HTTPError({ statusCode: 401, message: 'Unauthorized' }) 364 + } 393 365 394 - const index = state.operations.findIndex(op => op.id === id) 395 - if (index === -1) { 396 - throw createError({ statusCode: 404, message: 'Operation not found' }) 397 - } 366 + const removed = state.operations.filter(op => op.status !== 'running').length 367 + state.operations = state.operations.filter(op => op.status === 'running') 398 368 399 - const operation = state.operations[index] 400 - if (!operation || operation.status === 'running') { 401 - throw createError({ statusCode: 400, message: 'Cannot cancel running operation' }) 402 - } 369 + return { 370 + success: true, 371 + data: { removed }, 372 + } as ApiResponse 373 + }) 403 374 404 - state.operations.splice(index, 1) 375 + // List endpoints (read-only data fetching) 405 376 406 - return { success: true } as ApiResponse 407 - }), 408 - ) 377 + app.get('/org/:org/users', async event => { 378 + const auth = event.req.headers.get('authorization') 379 + if (!validateToken(auth)) { 380 + throw new HTTPError({ statusCode: 401, message: 'Unauthorized' }) 381 + } 409 382 410 - router.delete( 411 - '/operations/all', 412 - eventHandler(async event => { 413 - const auth = getHeader(event, 'authorization') 414 - if (!validateToken(auth)) { 415 - throw createError({ statusCode: 401, message: 'Unauthorized' }) 416 - } 383 + const org = event.context.params?.org 384 + if (!org) { 385 + throw new HTTPError({ statusCode: 400, message: 'Org name required' }) 386 + } 417 387 418 - const removed = state.operations.filter(op => op.status !== 'running').length 419 - state.operations = state.operations.filter(op => op.status === 'running') 388 + const result = await orgListUsers(org) 389 + if (result.exitCode !== 0) { 390 + return { 391 + success: false, 392 + error: result.stderr || 'Failed to list org users', 393 + } as ApiResponse 394 + } 420 395 396 + try { 397 + const users = JSON.parse(result.stdout) as Record<string, 'developer' | 'admin' | 'owner'> 421 398 return { 422 399 success: true, 423 - data: { removed }, 400 + data: users, 424 401 } as ApiResponse 425 - }), 426 - ) 402 + } catch { 403 + return { 404 + success: false, 405 + error: 'Failed to parse org users', 406 + } as ApiResponse 407 + } 408 + }) 427 409 428 - // List endpoints (read-only data fetching) 410 + app.get('/org/:org/teams', async event => { 411 + const auth = event.req.headers.get('authorization') 412 + if (!validateToken(auth)) { 413 + throw new HTTPError({ statusCode: 401, message: 'Unauthorized' }) 414 + } 429 415 430 - router.get( 431 - '/org/:org/users', 432 - eventHandler(async event => { 433 - const auth = getHeader(event, 'authorization') 434 - if (!validateToken(auth)) { 435 - throw createError({ statusCode: 401, message: 'Unauthorized' }) 436 - } 416 + const org = event.context.params?.org 417 + if (!org) { 418 + throw new HTTPError({ statusCode: 400, message: 'Org name required' }) 419 + } 437 420 438 - const org = getRouterParam(event, 'org') 439 - if (!org) { 440 - throw createError({ statusCode: 400, message: 'Org name required' }) 441 - } 421 + const result = await teamListTeams(org) 422 + if (result.exitCode !== 0) { 423 + return { 424 + success: false, 425 + error: result.stderr || 'Failed to list teams', 426 + } as ApiResponse 427 + } 442 428 443 - const result = await orgListUsers(org) 444 - if (result.exitCode !== 0) { 445 - return { 446 - success: false, 447 - error: result.stderr || 'Failed to list org users', 448 - } as ApiResponse 449 - } 450 - 451 - try { 452 - const users = JSON.parse(result.stdout) as Record<string, 'developer' | 'admin' | 'owner'> 453 - return { 454 - success: true, 455 - data: users, 456 - } as ApiResponse 457 - } catch { 458 - return { 459 - success: false, 460 - error: 'Failed to parse org users', 461 - } as ApiResponse 462 - } 463 - }), 464 - ) 465 - 466 - router.get( 467 - '/org/:org/teams', 468 - eventHandler(async event => { 469 - const auth = getHeader(event, 'authorization') 470 - if (!validateToken(auth)) { 471 - throw createError({ statusCode: 401, message: 'Unauthorized' }) 472 - } 473 - 474 - const org = getRouterParam(event, 'org') 475 - if (!org) { 476 - throw createError({ statusCode: 400, message: 'Org name required' }) 477 - } 478 - 479 - const result = await teamListTeams(org) 480 - if (result.exitCode !== 0) { 481 - return { 482 - success: false, 483 - error: result.stderr || 'Failed to list teams', 484 - } as ApiResponse 485 - } 486 - 487 - try { 488 - const teams = JSON.parse(result.stdout) as string[] 489 - return { 490 - success: true, 491 - data: teams, 492 - } as ApiResponse 493 - } catch { 494 - return { 495 - success: false, 496 - error: 'Failed to parse teams', 497 - } as ApiResponse 498 - } 499 - }), 500 - ) 429 + try { 430 + const teams = JSON.parse(result.stdout) as string[] 431 + return { 432 + success: true, 433 + data: teams, 434 + } as ApiResponse 435 + } catch { 436 + return { 437 + success: false, 438 + error: 'Failed to parse teams', 439 + } as ApiResponse 440 + } 441 + }) 501 442 502 - router.get( 503 - '/team/:scopeTeam/users', 504 - eventHandler(async event => { 505 - const auth = getHeader(event, 'authorization') 506 - if (!validateToken(auth)) { 507 - throw createError({ statusCode: 401, message: 'Unauthorized' }) 508 - } 443 + app.get('/team/:scopeTeam/users', async event => { 444 + const auth = event.req.headers.get('authorization') 445 + if (!validateToken(auth)) { 446 + throw new HTTPError({ statusCode: 401, message: 'Unauthorized' }) 447 + } 509 448 510 - const scopeTeamRaw = getRouterParam(event, 'scopeTeam') 511 - if (!scopeTeamRaw) { 512 - throw createError({ statusCode: 400, message: 'Team name required' }) 513 - } 449 + const scopeTeamRaw = event.context.params?.scopeTeam 450 + if (!scopeTeamRaw) { 451 + throw new HTTPError({ statusCode: 400, message: 'Team name required' }) 452 + } 514 453 515 - // Decode the team name (handles encoded colons like nuxt%3Adevelopers) 516 - const scopeTeam = decodeURIComponent(scopeTeamRaw) 454 + // Decode the team name (handles encoded colons like nuxt%3Adevelopers) 455 + const scopeTeam = decodeURIComponent(scopeTeamRaw) 517 456 518 - const result = await teamListUsers(scopeTeam) 519 - if (result.exitCode !== 0) { 520 - return { 521 - success: false, 522 - error: result.stderr || 'Failed to list team users', 523 - } as ApiResponse 524 - } 457 + const result = await teamListUsers(scopeTeam) 458 + if (result.exitCode !== 0) { 459 + return { 460 + success: false, 461 + error: result.stderr || 'Failed to list team users', 462 + } as ApiResponse 463 + } 525 464 526 - try { 527 - const users = JSON.parse(result.stdout) as string[] 528 - return { 529 - success: true, 530 - data: users, 531 - } as ApiResponse 532 - } catch { 533 - return { 534 - success: false, 535 - error: 'Failed to parse team users', 536 - } as ApiResponse 537 - } 538 - }), 539 - ) 465 + try { 466 + const users = JSON.parse(result.stdout) as string[] 467 + return { 468 + success: true, 469 + data: users, 470 + } as ApiResponse 471 + } catch { 472 + return { 473 + success: false, 474 + error: 'Failed to parse team users', 475 + } as ApiResponse 476 + } 477 + }) 540 478 541 - router.get( 542 - '/package/:pkg/collaborators', 543 - eventHandler(async event => { 544 - const auth = getHeader(event, 'authorization') 545 - if (!validateToken(auth)) { 546 - throw createError({ statusCode: 401, message: 'Unauthorized' }) 547 - } 479 + app.get('/package/:pkg/collaborators', async event => { 480 + const auth = event.req.headers.get('authorization') 481 + if (!validateToken(auth)) { 482 + throw new HTTPError({ statusCode: 401, message: 'Unauthorized' }) 483 + } 548 484 549 - const pkg = getRouterParam(event, 'pkg') 550 - if (!pkg) { 551 - throw createError({ statusCode: 400, message: 'Package name required' }) 552 - } 485 + const pkg = event.context.params?.pkg 486 + if (!pkg) { 487 + throw new HTTPError({ statusCode: 400, message: 'Package name required' }) 488 + } 553 489 554 - // Decode the package name (handles scoped packages like @nuxt%2Fkit) 555 - const decodedPkg = decodeURIComponent(pkg) 490 + // Decode the package name (handles scoped packages like @nuxt%2Fkit) 491 + const decodedPkg = decodeURIComponent(pkg) 556 492 557 - const result = await accessListCollaborators(decodedPkg) 558 - if (result.exitCode !== 0) { 559 - return { 560 - success: false, 561 - error: result.stderr || 'Failed to list collaborators', 562 - } as ApiResponse 563 - } 493 + const result = await accessListCollaborators(decodedPkg) 494 + if (result.exitCode !== 0) { 495 + return { 496 + success: false, 497 + error: result.stderr || 'Failed to list collaborators', 498 + } as ApiResponse 499 + } 564 500 565 - try { 566 - const collaborators = JSON.parse(result.stdout) as Record< 567 - string, 568 - 'read-only' | 'read-write' 569 - > 570 - return { 571 - success: true, 572 - data: collaborators, 573 - } as ApiResponse 574 - } catch { 575 - return { 576 - success: false, 577 - error: 'Failed to parse collaborators', 578 - } as ApiResponse 579 - } 580 - }), 581 - ) 501 + try { 502 + const collaborators = JSON.parse(result.stdout) as Record<string, 'read-only' | 'read-write'> 503 + return { 504 + success: true, 505 + data: collaborators, 506 + } as ApiResponse 507 + } catch { 508 + return { 509 + success: false, 510 + error: 'Failed to parse collaborators', 511 + } as ApiResponse 512 + } 513 + }) 582 514 583 - app.use(router) 584 515 return app 585 516 } 586 517
+9
cli/tsdown.config.ts
··· 1 + import { defineConfig } from 'tsdown' 2 + 3 + export default defineConfig({ 4 + entry: ['src/index.ts', 'src/cli.ts'], 5 + format: 'esm', 6 + clean: true, 7 + dts: true, 8 + outDir: 'dist', 9 + })
+203 -170
pnpm-lock.yaml
··· 162 162 specifier: ^6.1.4 163 163 version: 6.1.4 164 164 h3: 165 - specifier: ^1.15.5 166 - version: 1.15.5 167 - listhen: 168 - specifier: ^1.9.0 169 - version: 1.9.0 165 + specifier: ^2.0.1-rc.11 166 + version: 2.0.1-rc.11 170 167 ofetch: 171 168 specifier: ^1.5.1 172 169 version: 1.5.1 173 170 picocolors: 174 171 specifier: ^1.1.1 175 172 version: 1.1.1 173 + srvx: 174 + specifier: ^0.10.1 175 + version: 0.10.1 176 176 validate-npm-package-name: 177 177 specifier: ^7.0.2 178 178 version: 7.0.2 ··· 183 183 '@types/validate-npm-package-name': 184 184 specifier: ^4.0.2 185 185 version: 4.0.2 186 + tsdown: 187 + specifier: ^0.20.1 188 + version: 0.20.1(typescript@5.9.3)(vue-tsc@3.2.2(typescript@5.9.3)) 186 189 typescript: 187 190 specifier: ^5.9.3 188 191 version: 5.9.3 189 - unbuild: 190 - specifier: ^3.6.1 191 - version: 3.6.1(typescript@5.9.3)(vue-tsc@3.2.2(typescript@5.9.3))(vue@3.5.27(typescript@5.9.3)) 192 192 193 193 packages: 194 194 ··· 229 229 resolution: {integrity: sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==} 230 230 engines: {node: '>=6.9.0'} 231 231 232 + '@babel/generator@8.0.0-beta.4': 233 + resolution: {integrity: sha512-5xRfRZk6wx1BRu2XnTE8cTh2mx1ixrZ3/vpn7p/RCJpgctL6pexVVHE3eqtwlYvHhPAuOYCAlnsAyXpBdmfh5Q==} 234 + engines: {node: ^20.19.0 || >=22.12.0} 235 + 232 236 '@babel/helper-annotate-as-pure@7.27.3': 233 237 resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} 234 238 engines: {node: '>=6.9.0'} ··· 300 304 resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} 301 305 engines: {node: '>=6.9.0'} 302 306 307 + '@babel/helper-string-parser@8.0.0-beta.4': 308 + resolution: {integrity: sha512-FGwbdQ/I2nJXXfyxa7dT0Fr/zPWwgX7m+hNVj0HrIHYJtyLxSQeQY1Kd8QkAYviQJV3OWFlRLuGd5epF03bdQg==} 309 + engines: {node: ^20.19.0 || >=22.12.0} 310 + 303 311 '@babel/helper-validator-identifier@7.28.5': 304 312 resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} 305 313 engines: {node: '>=6.9.0'} 314 + 315 + '@babel/helper-validator-identifier@8.0.0-beta.4': 316 + resolution: {integrity: sha512-6t0IaUEzlinbLmsGIvBZIHEJGjuchx+cMj+FbS78zL17tucYervgbwO07V5/CgBenVraontpmyMCTVyqCfxhFQ==} 317 + engines: {node: ^20.19.0 || >=22.12.0} 306 318 307 319 '@babel/helper-validator-option@7.27.1': 308 320 resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} ··· 324 336 '@babel/parser@7.28.6': 325 337 resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==} 326 338 engines: {node: '>=6.0.0'} 339 + hasBin: true 340 + 341 + '@babel/parser@8.0.0-beta.4': 342 + resolution: {integrity: sha512-fBcUqUN3eenLyg25QFkOwY1lmV6L0RdG92g6gxyS2CVCY8kHdibkQz1+zV3bLzxcvNnfHoi3i9n5Dci+g93acg==} 343 + engines: {node: ^20.19.0 || >=22.12.0} 327 344 hasBin: true 328 345 329 346 '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': ··· 734 751 '@babel/types@7.28.6': 735 752 resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==} 736 753 engines: {node: '>=6.9.0'} 754 + 755 + '@babel/types@8.0.0-beta.4': 756 + resolution: {integrity: sha512-xjk2xqYp25ePzAs0I08hN2lrbUDDQFfCjwq6MIEa8HwHa0WK8NfNtdvtXod8Ku2CbE1iui7qwWojGvjQiyrQeA==} 757 + engines: {node: ^20.19.0 || >=22.12.0} 737 758 738 759 '@bcoe/v8-coverage@1.0.2': 739 760 resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} ··· 2412 2433 '@rolldown/pluginutils@1.0.0-rc.1': 2413 2434 resolution: {integrity: sha512-UTBjtTxVOhodhzFVp/ayITaTETRHPUPYZPXQe0WU0wOgxghMojXxYjOiPOauKIYNWJAWS2fd7gJgGQK8GU8vDA==} 2414 2435 2415 - '@rollup/plugin-alias@5.1.1': 2416 - resolution: {integrity: sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==} 2417 - engines: {node: '>=14.0.0'} 2418 - peerDependencies: 2419 - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 2420 - peerDependenciesMeta: 2421 - rollup: 2422 - optional: true 2423 - 2424 2436 '@rollup/plugin-alias@6.0.0': 2425 2437 resolution: {integrity: sha512-tPCzJOtS7uuVZd+xPhoy5W4vThe6KWXNmsFCNktaAh5RTqcLiSfT4huPQIXkgJ6YCOjJHvecOAzQxLFhPxKr+g==} 2426 2438 engines: {node: '>=20.19.0'} ··· 2441 2453 '@types/babel__core': 2442 2454 optional: true 2443 2455 2444 - '@rollup/plugin-commonjs@28.0.9': 2445 - resolution: {integrity: sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==} 2446 - engines: {node: '>=16.0.0 || 14 >= 14.17'} 2447 - peerDependencies: 2448 - rollup: ^2.68.0||^3.0.0||^4.0.0 2449 - peerDependenciesMeta: 2450 - rollup: 2451 - optional: true 2452 - 2453 2456 '@rollup/plugin-commonjs@29.0.0': 2454 2457 resolution: {integrity: sha512-U2YHaxR2cU/yAiwKJtJRhnyLk7cifnQw0zUpISsocBDoHDJn+HTV74ABqnwr5bEgWUwFZC9oFL6wLe21lHu5eQ==} 2455 2458 engines: {node: '>=16.0.0 || 14 >= 14.17'} ··· 2736 2739 2737 2740 '@types/hast@3.0.4': 2738 2741 resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} 2742 + 2743 + '@types/jsesc@2.5.1': 2744 + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} 2739 2745 2740 2746 '@types/json-schema@7.0.15': 2741 2747 resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} ··· 3230 3236 resolution: {integrity: sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==} 3231 3237 engines: {node: '>=20.19.0'} 3232 3238 3239 + ast-kit@3.0.0-beta.1: 3240 + resolution: {integrity: sha512-trmleAnZ2PxN/loHWVhhx1qeOHSRXq4TDsBBxq3GqeJitfk3+jTQ+v/C1km/KYq9M7wKqCewMh+/NAvVH7m+bw==} 3241 + engines: {node: '>=20.19.0'} 3242 + 3233 3243 ast-v8-to-istanbul@0.3.10: 3234 3244 resolution: {integrity: sha512-p4K7vMz2ZSk3wN8l5o3y2bJAoZXT3VuJI5OLTATY/01CYWumWvwkUw0SqDBnNq6IiTO3qDa1eSQDibAV8g7XOQ==} 3235 3245 ··· 3315 3325 3316 3326 birpc@2.9.0: 3317 3327 resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} 3328 + 3329 + birpc@4.0.0: 3330 + resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==} 3318 3331 3319 3332 boolbase@1.0.0: 3320 3333 resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} ··· 3792 3805 resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} 3793 3806 engines: {node: '>=12'} 3794 3807 3808 + dts-resolver@2.1.3: 3809 + resolution: {integrity: sha512-bihc7jPC90VrosXNzK0LTE2cuLP6jr0Ro8jk+kMugHReJVLIpHz/xadeq3MhuwyO4TD4OA3L1Q8pBBFRc08Tsw==} 3810 + engines: {node: '>=20.19.0'} 3811 + peerDependencies: 3812 + oxc-resolver: '>=11.0.0' 3813 + peerDependenciesMeta: 3814 + oxc-resolver: 3815 + optional: true 3816 + 3795 3817 dunder-proto@1.0.1: 3796 3818 resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} 3797 3819 engines: {node: '>= 0.4'} ··· 3830 3852 3831 3853 emoji-regex@9.2.2: 3832 3854 resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 3855 + 3856 + empathic@2.0.0: 3857 + resolution: {integrity: sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==} 3858 + engines: {node: '>=14'} 3833 3859 3834 3860 encodeurl@2.0.0: 3835 3861 resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} ··· 4070 4096 find-up@5.0.0: 4071 4097 resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 4072 4098 engines: {node: '>=10'} 4073 - 4074 - fix-dts-default-cjs-exports@1.0.1: 4075 - resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} 4076 4099 4077 4100 flat-cache@4.0.1: 4078 4101 resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} ··· 4413 4436 resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} 4414 4437 engines: {node: '>=6'} 4415 4438 4439 + import-without-cache@0.2.5: 4440 + resolution: {integrity: sha512-B6Lc2s6yApwnD2/pMzFh/d5AVjdsDXjgkeJ766FmFuJELIGHNycKRj+l3A39yZPM4CchqNCB4RITEAYB1KUM6A==} 4441 + engines: {node: '>=20.19.0'} 4442 + 4416 4443 impound@1.0.0: 4417 4444 resolution: {integrity: sha512-8lAJ+1Arw2sMaZ9HE2ZmL5zOcMnt18s6+7Xqgq2aUVy4P1nlzAyPtzCDxsk51KVFwHEEdc6OWvUyqwHwhRYaug==} 4418 4445 ··· 4675 4702 jest-worker@27.5.1: 4676 4703 resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} 4677 4704 engines: {node: '>= 10.13.0'} 4678 - 4679 - jiti@1.21.7: 4680 - resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} 4681 - hasBin: true 4682 4705 4683 4706 jiti@2.6.1: 4684 4707 resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} ··· 5052 5075 mitt@3.0.1: 5053 5076 resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} 5054 5077 5055 - mkdist@2.4.1: 5056 - resolution: {integrity: sha512-Ezk0gi04GJBkqMfsksICU5Rjoemc4biIekwgrONWVPor2EO/N9nBgN6MZXAf7Yw4mDDhrNyKbdETaHNevfumKg==} 5057 - hasBin: true 5058 - peerDependencies: 5059 - sass: ^1.92.1 5060 - typescript: '>=5.9.2' 5061 - vue: ^3.5.21 5062 - vue-sfc-transformer: ^0.1.1 5063 - vue-tsc: ^1.8.27 || ^2.0.21 || ^3.0.0 5064 - peerDependenciesMeta: 5065 - sass: 5066 - optional: true 5067 - typescript: 5068 - optional: true 5069 - vue: 5070 - optional: true 5071 - vue-sfc-transformer: 5072 - optional: true 5073 - vue-tsc: 5074 - optional: true 5075 - 5076 5078 mlly@1.8.0: 5077 5079 resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} 5078 5080 ··· 5500 5502 peerDependencies: 5501 5503 postcss: ^8.4.32 5502 5504 5503 - postcss-nested@7.0.2: 5504 - resolution: {integrity: sha512-5osppouFc0VR9/VYzYxO03VaDa3e8F23Kfd6/9qcZTUI8P58GIYlArOET2Wq0ywSl2o2PjELhYOFI4W7l5QHKw==} 5505 - engines: {node: '>=18.0'} 5506 - peerDependencies: 5507 - postcss: ^8.2.14 5508 - 5509 5505 postcss-normalize-charset@7.0.1: 5510 5506 resolution: {integrity: sha512-sn413ofhSQHlZFae//m9FTOfkmiZ+YQXsbosqOWRiVQncU2BA3daX3n0VF3cG6rGLSFVc5Di/yns0dFfh8NFgQ==} 5511 5507 engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} ··· 5775 5771 rfdc@1.4.1: 5776 5772 resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} 5777 5773 5774 + rolldown-plugin-dts@0.21.6: 5775 + resolution: {integrity: sha512-gePhzvZJRB0JIb/NyngEsMt3FPQtM4BXCLkxz7u1ggge2PmlZ7uOwmHjeBEsBiBRjOY12SdtEl7BmI3T1779ZA==} 5776 + engines: {node: '>=20.19.0'} 5777 + peerDependencies: 5778 + '@ts-macro/tsc': ^0.3.6 5779 + '@typescript/native-preview': '>=7.0.0-dev.20250601.1' 5780 + rolldown: ^1.0.0-beta.57 5781 + typescript: ^5.0.0 5782 + vue-tsc: ~3.2.0 5783 + peerDependenciesMeta: 5784 + '@ts-macro/tsc': 5785 + optional: true 5786 + '@typescript/native-preview': 5787 + optional: true 5788 + typescript: 5789 + optional: true 5790 + vue-tsc: 5791 + optional: true 5792 + 5778 5793 rolldown@1.0.0-rc.1: 5779 5794 resolution: {integrity: sha512-M3AeZjYE6UclblEf531Hch0WfVC/NOL43Cc+WdF3J50kk5/fvouHhDumSGTh0oRjbZ8C4faaVr5r6Nx1xMqDGg==} 5780 5795 engines: {node: ^20.19.0 || >=22.12.0} 5781 5796 hasBin: true 5782 5797 5783 - rollup-plugin-dts@6.3.0: 5784 - resolution: {integrity: sha512-d0UrqxYd8KyZ6i3M2Nx7WOMy708qsV/7fTHMHxCMCBOAe3V/U7OMPu5GkX8hC+cmkHhzGnfeYongl1IgiooddA==} 5785 - engines: {node: '>=16'} 5786 - peerDependencies: 5787 - rollup: ^3.29.4 || ^4 5788 - typescript: ^4.5 || ^5.0 5789 - 5790 5798 rollup-plugin-visualizer@6.0.5: 5791 5799 resolution: {integrity: sha512-9+HlNgKCVbJDs8tVtjQ43US12eqaiHyyiLMdBwQ7vSZPiHMysGNo2E88TAp1si5wx8NAoYriI2A5kuKfIakmJg==} 5792 5800 engines: {node: '>=18'} ··· 6274 6282 resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} 6275 6283 engines: {node: '>=20'} 6276 6284 6285 + tree-kill@1.2.2: 6286 + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} 6287 + hasBin: true 6288 + 6277 6289 trim-lines@3.0.1: 6278 6290 resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} 6291 + 6292 + tsdown@0.20.1: 6293 + resolution: {integrity: sha512-Wo1BzqNQVZ6SFQV8rjQBwMmNubO+yV3F+vp2WNTjEaS4S5CT1C1dHtUbeFMrCEasZpGy5w6TshpehNnfTe8QBQ==} 6294 + engines: {node: '>=20.19.0'} 6295 + hasBin: true 6296 + peerDependencies: 6297 + '@arethetypeswrong/core': ^0.18.1 6298 + '@vitejs/devtools': '*' 6299 + publint: ^0.3.0 6300 + typescript: ^5.0.0 6301 + unplugin-lightningcss: ^0.4.0 6302 + unplugin-unused: ^0.5.0 6303 + peerDependenciesMeta: 6304 + '@arethetypeswrong/core': 6305 + optional: true 6306 + '@vitejs/devtools': 6307 + optional: true 6308 + publint: 6309 + optional: true 6310 + typescript: 6311 + optional: true 6312 + unplugin-lightningcss: 6313 + optional: true 6314 + unplugin-unused: 6315 + optional: true 6279 6316 6280 6317 tslib@2.8.1: 6281 6318 resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} ··· 6330 6367 unbox-primitive@1.1.0: 6331 6368 resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} 6332 6369 engines: {node: '>= 0.4'} 6333 - 6334 - unbuild@3.6.1: 6335 - resolution: {integrity: sha512-+U5CdtrdjfWkZhuO4N9l5UhyiccoeMEXIc2Lbs30Haxb+tRwB3VwB8AoZRxlAzORXunenSo+j6lh45jx+xkKgg==} 6336 - hasBin: true 6337 - peerDependencies: 6338 - typescript: ^5.9.2 6339 - peerDependenciesMeta: 6340 - typescript: 6341 - optional: true 6342 6370 6343 6371 unconfig-core@7.4.2: 6344 6372 resolution: {integrity: sha512-VgPCvLWugINbXvMQDf8Jh0mlbvNjNC6eSUziHsBCMpxR05OPrNrvDnyatdMjRgcHaaNsCqz+wjNXxNw1kRLHUg==} ··· 6454 6482 resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} 6455 6483 engines: {node: '>=18.12.0'} 6456 6484 6485 + unrun@0.2.26: 6486 + resolution: {integrity: sha512-A3DQLBcDyTui4Hlaoojkldg+8x+CIR+tcSHY0wzW+CgB4X/DNyH58jJpXp1B/EkE+yG6tU8iH1mWsLtwFU3IQg==} 6487 + engines: {node: '>=20.19.0'} 6488 + hasBin: true 6489 + peerDependencies: 6490 + synckit: ^0.11.11 6491 + peerDependenciesMeta: 6492 + synckit: 6493 + optional: true 6494 + 6457 6495 unstorage@1.17.4: 6458 6496 resolution: {integrity: sha512-fHK0yNg38tBiJKp/Vgsq4j0JEsCmgqH58HAn707S7zGkArbZsVr/CwINoi+nh3h98BRCwKvx1K3Xg9u3VV83sw==} 6459 6497 peerDependencies: ··· 7063 7101 '@babel/types': 7.28.6 7064 7102 '@jridgewell/gen-mapping': 0.3.13 7065 7103 '@jridgewell/trace-mapping': 0.3.31 7104 + jsesc: 3.1.0 7105 + 7106 + '@babel/generator@8.0.0-beta.4': 7107 + dependencies: 7108 + '@babel/parser': 8.0.0-beta.4 7109 + '@babel/types': 8.0.0-beta.4 7110 + '@jridgewell/gen-mapping': 0.3.13 7111 + '@jridgewell/trace-mapping': 0.3.31 7112 + '@types/jsesc': 2.5.1 7066 7113 jsesc: 3.1.0 7067 7114 7068 7115 '@babel/helper-annotate-as-pure@7.27.3': ··· 7166 7213 7167 7214 '@babel/helper-string-parser@7.27.1': {} 7168 7215 7216 + '@babel/helper-string-parser@8.0.0-beta.4': {} 7217 + 7169 7218 '@babel/helper-validator-identifier@7.28.5': {} 7219 + 7220 + '@babel/helper-validator-identifier@8.0.0-beta.4': {} 7170 7221 7171 7222 '@babel/helper-validator-option@7.27.1': {} 7172 7223 ··· 7190 7241 '@babel/parser@7.28.6': 7191 7242 dependencies: 7192 7243 '@babel/types': 7.28.6 7244 + 7245 + '@babel/parser@8.0.0-beta.4': 7246 + dependencies: 7247 + '@babel/types': 8.0.0-beta.4 7193 7248 7194 7249 '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.28.6)': 7195 7250 dependencies: ··· 7720 7775 '@babel/helper-string-parser': 7.27.1 7721 7776 '@babel/helper-validator-identifier': 7.28.5 7722 7777 7778 + '@babel/types@8.0.0-beta.4': 7779 + dependencies: 7780 + '@babel/helper-string-parser': 8.0.0-beta.4 7781 + '@babel/helper-validator-identifier': 8.0.0-beta.4 7782 + 7723 7783 '@bcoe/v8-coverage@1.0.2': {} 7724 7784 7725 7785 '@bomb.sh/tab@0.0.11(cac@6.7.14)(citty@0.1.6)': ··· 9232 9292 9233 9293 '@rolldown/pluginutils@1.0.0-rc.1': {} 9234 9294 9235 - '@rollup/plugin-alias@5.1.1(rollup@4.56.0)': 9236 - optionalDependencies: 9237 - rollup: 4.56.0 9238 - 9239 9295 '@rollup/plugin-alias@6.0.0(rollup@4.56.0)': 9240 9296 optionalDependencies: 9241 9297 rollup: 4.56.0 ··· 9248 9304 rollup: 2.79.2 9249 9305 transitivePeerDependencies: 9250 9306 - supports-color 9251 - 9252 - '@rollup/plugin-commonjs@28.0.9(rollup@4.56.0)': 9253 - dependencies: 9254 - '@rollup/pluginutils': 5.3.0(rollup@4.56.0) 9255 - commondir: 1.0.1 9256 - estree-walker: 2.0.2 9257 - fdir: 6.5.0(picomatch@4.0.3) 9258 - is-reference: 1.2.1 9259 - magic-string: 0.30.21 9260 - picomatch: 4.0.3 9261 - optionalDependencies: 9262 - rollup: 4.56.0 9263 9307 9264 9308 '@rollup/plugin-commonjs@29.0.0(rollup@4.56.0)': 9265 9309 dependencies: ··· 9527 9571 '@types/hast@3.0.4': 9528 9572 dependencies: 9529 9573 '@types/unist': 3.0.3 9574 + 9575 + '@types/jsesc@2.5.1': {} 9530 9576 9531 9577 '@types/json-schema@7.0.15': {} 9532 9578 ··· 10281 10327 '@babel/parser': 7.28.6 10282 10328 pathe: 2.0.3 10283 10329 10330 + ast-kit@3.0.0-beta.1: 10331 + dependencies: 10332 + '@babel/parser': 8.0.0-beta.4 10333 + estree-walker: 3.0.3 10334 + pathe: 2.0.3 10335 + 10284 10336 ast-v8-to-istanbul@0.3.10: 10285 10337 dependencies: 10286 10338 '@jridgewell/trace-mapping': 0.3.31 ··· 10359 10411 file-uri-to-path: 1.0.0 10360 10412 10361 10413 birpc@2.9.0: {} 10414 + 10415 + birpc@4.0.0: {} 10362 10416 10363 10417 boolbase@1.0.0: {} 10364 10418 ··· 10833 10887 dotenv@16.6.1: {} 10834 10888 10835 10889 dotenv@17.2.3: {} 10890 + 10891 + dts-resolver@2.1.3: {} 10836 10892 10837 10893 dunder-proto@1.0.1: 10838 10894 dependencies: ··· 10867 10923 10868 10924 emoji-regex@9.2.2: {} 10869 10925 10926 + empathic@2.0.0: {} 10927 + 10870 10928 encodeurl@2.0.0: {} 10871 10929 10872 10930 enhanced-resolve@5.18.4: ··· 11227 11285 path-exists: 4.0.0 11228 11286 optional: true 11229 11287 11230 - fix-dts-default-cjs-exports@1.0.1: 11231 - dependencies: 11232 - magic-string: 0.30.21 11233 - mlly: 1.8.0 11234 - rollup: 4.56.0 11235 - 11236 11288 flat-cache@4.0.1: 11237 11289 dependencies: 11238 11290 flatted: 3.3.3 ··· 11403 11455 get-tsconfig@4.13.0: 11404 11456 dependencies: 11405 11457 resolve-pkg-maps: 1.0.0 11406 - optional: true 11407 11458 11408 11459 giget@2.0.0: 11409 11460 dependencies: ··· 11655 11706 resolve-from: 4.0.0 11656 11707 optional: true 11657 11708 11709 + import-without-cache@0.2.5: {} 11710 + 11658 11711 impound@1.0.0: 11659 11712 dependencies: 11660 11713 exsolve: 1.0.8 ··· 11911 11964 merge-stream: 2.0.0 11912 11965 supports-color: 8.1.1 11913 11966 11914 - jiti@1.21.7: {} 11915 - 11916 11967 jiti@2.6.1: {} 11917 11968 11918 11969 js-beautify@1.15.4: ··· 12298 12349 12299 12350 mitt@3.0.1: {} 12300 12351 12301 - mkdist@2.4.1(typescript@5.9.3)(vue-tsc@3.2.2(typescript@5.9.3))(vue@3.5.27(typescript@5.9.3)): 12302 - dependencies: 12303 - autoprefixer: 10.4.23(postcss@8.5.6) 12304 - citty: 0.1.6 12305 - cssnano: 7.1.2(postcss@8.5.6) 12306 - defu: 6.1.4 12307 - esbuild: 0.25.12 12308 - jiti: 1.21.7 12309 - mlly: 1.8.0 12310 - pathe: 2.0.3 12311 - pkg-types: 2.3.0 12312 - postcss: 8.5.6 12313 - postcss-nested: 7.0.2(postcss@8.5.6) 12314 - semver: 7.7.3 12315 - tinyglobby: 0.2.15 12316 - optionalDependencies: 12317 - typescript: 5.9.3 12318 - vue: 3.5.27(typescript@5.9.3) 12319 - vue-tsc: 3.2.2(typescript@5.9.3) 12320 - 12321 12352 mlly@1.8.0: 12322 12353 dependencies: 12323 12354 acorn: 8.15.0 ··· 13039 13070 postcss: 8.5.6 13040 13071 postcss-selector-parser: 7.1.1 13041 13072 13042 - postcss-nested@7.0.2(postcss@8.5.6): 13043 - dependencies: 13044 - postcss: 8.5.6 13045 - postcss-selector-parser: 7.1.1 13046 - 13047 13073 postcss-normalize-charset@7.0.1(postcss@8.5.6): 13048 13074 dependencies: 13049 13075 postcss: 8.5.6 ··· 13275 13301 13276 13302 resolve-from@5.0.0: {} 13277 13303 13278 - resolve-pkg-maps@1.0.0: 13279 - optional: true 13304 + resolve-pkg-maps@1.0.0: {} 13280 13305 13281 13306 resolve@1.22.11: 13282 13307 dependencies: ··· 13295 13320 13296 13321 rfdc@1.4.1: {} 13297 13322 13323 + rolldown-plugin-dts@0.21.6(rolldown@1.0.0-rc.1)(typescript@5.9.3)(vue-tsc@3.2.2(typescript@5.9.3)): 13324 + dependencies: 13325 + '@babel/generator': 8.0.0-beta.4 13326 + '@babel/parser': 8.0.0-beta.4 13327 + '@babel/types': 8.0.0-beta.4 13328 + ast-kit: 3.0.0-beta.1 13329 + birpc: 4.0.0 13330 + dts-resolver: 2.1.3 13331 + get-tsconfig: 4.13.0 13332 + obug: 2.1.1 13333 + rolldown: 1.0.0-rc.1 13334 + optionalDependencies: 13335 + typescript: 5.9.3 13336 + vue-tsc: 3.2.2(typescript@5.9.3) 13337 + transitivePeerDependencies: 13338 + - oxc-resolver 13339 + 13298 13340 rolldown@1.0.0-rc.1: 13299 13341 dependencies: 13300 13342 '@oxc-project/types': 0.110.0 ··· 13313 13355 '@rolldown/binding-wasm32-wasi': 1.0.0-rc.1 13314 13356 '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.1 13315 13357 '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.1 13316 - 13317 - rollup-plugin-dts@6.3.0(rollup@4.56.0)(typescript@5.9.3): 13318 - dependencies: 13319 - magic-string: 0.30.21 13320 - rollup: 4.56.0 13321 - typescript: 5.9.3 13322 - optionalDependencies: 13323 - '@babel/code-frame': 7.28.6 13324 13358 13325 13359 rollup-plugin-visualizer@6.0.5(rolldown@1.0.0-rc.1)(rollup@4.56.0): 13326 13360 dependencies: ··· 13951 13985 punycode: 2.3.1 13952 13986 optional: true 13953 13987 13988 + tree-kill@1.2.2: {} 13989 + 13954 13990 trim-lines@3.0.1: {} 13991 + 13992 + tsdown@0.20.1(typescript@5.9.3)(vue-tsc@3.2.2(typescript@5.9.3)): 13993 + dependencies: 13994 + ansis: 4.2.0 13995 + cac: 6.7.14 13996 + defu: 6.1.4 13997 + empathic: 2.0.0 13998 + hookable: 6.0.1 13999 + import-without-cache: 0.2.5 14000 + obug: 2.1.1 14001 + picomatch: 4.0.3 14002 + rolldown: 1.0.0-rc.1 14003 + rolldown-plugin-dts: 0.21.6(rolldown@1.0.0-rc.1)(typescript@5.9.3)(vue-tsc@3.2.2(typescript@5.9.3)) 14004 + semver: 7.7.3 14005 + tinyexec: 1.0.2 14006 + tinyglobby: 0.2.15 14007 + tree-kill: 1.2.2 14008 + unconfig-core: 7.4.2 14009 + unrun: 0.2.26 14010 + optionalDependencies: 14011 + typescript: 5.9.3 14012 + transitivePeerDependencies: 14013 + - '@ts-macro/tsc' 14014 + - '@typescript/native-preview' 14015 + - oxc-resolver 14016 + - synckit 14017 + - vue-tsc 13955 14018 13956 14019 tslib@2.8.1: {} 13957 14020 ··· 14021 14084 has-bigints: 1.1.0 14022 14085 has-symbols: 1.1.0 14023 14086 which-boxed-primitive: 1.1.1 14024 - 14025 - unbuild@3.6.1(typescript@5.9.3)(vue-tsc@3.2.2(typescript@5.9.3))(vue@3.5.27(typescript@5.9.3)): 14026 - dependencies: 14027 - '@rollup/plugin-alias': 5.1.1(rollup@4.56.0) 14028 - '@rollup/plugin-commonjs': 28.0.9(rollup@4.56.0) 14029 - '@rollup/plugin-json': 6.1.0(rollup@4.56.0) 14030 - '@rollup/plugin-node-resolve': 16.0.3(rollup@4.56.0) 14031 - '@rollup/plugin-replace': 6.0.3(rollup@4.56.0) 14032 - '@rollup/pluginutils': 5.3.0(rollup@4.56.0) 14033 - citty: 0.1.6 14034 - consola: 3.4.2 14035 - defu: 6.1.4 14036 - esbuild: 0.25.12 14037 - fix-dts-default-cjs-exports: 1.0.1 14038 - hookable: 5.5.3 14039 - jiti: 2.6.1 14040 - magic-string: 0.30.21 14041 - mkdist: 2.4.1(typescript@5.9.3)(vue-tsc@3.2.2(typescript@5.9.3))(vue@3.5.27(typescript@5.9.3)) 14042 - mlly: 1.8.0 14043 - pathe: 2.0.3 14044 - pkg-types: 2.3.0 14045 - pretty-bytes: 7.1.0 14046 - rollup: 4.56.0 14047 - rollup-plugin-dts: 6.3.0(rollup@4.56.0)(typescript@5.9.3) 14048 - scule: 1.3.0 14049 - tinyglobby: 0.2.15 14050 - untyped: 2.0.0 14051 - optionalDependencies: 14052 - typescript: 5.9.3 14053 - transitivePeerDependencies: 14054 - - sass 14055 - - vue 14056 - - vue-sfc-transformer 14057 - - vue-tsc 14058 14087 14059 14088 unconfig-core@7.4.2: 14060 14089 dependencies: ··· 14234 14263 acorn: 8.15.0 14235 14264 picomatch: 4.0.3 14236 14265 webpack-virtual-modules: 0.6.2 14266 + 14267 + unrun@0.2.26: 14268 + dependencies: 14269 + rolldown: 1.0.0-rc.1 14237 14270 14238 14271 unstorage@1.17.4(db0@0.3.4)(ioredis@5.9.2): 14239 14272 dependencies: