@powerhousedao/registry 6.0.0-dev.182 → 6.0.0-dev.183
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/README.md +17 -15
- package/dist/cli.mjs +9 -2
- package/dist/cli.mjs.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -28,6 +28,8 @@ Returns info for a single package (supports scoped names like `@powerhousedao/ve
|
|
|
28
28
|
|
|
29
29
|
Serves files from the CDN cache. On first request, fetches and extracts the tarball from Verdaccio. Looks for files in the package root, then `cdn/`, `dist/cdn/`, and `dist/` subdirectories.
|
|
30
30
|
|
|
31
|
+
If the package is not published locally, Verdaccio transparently proxies metadata and tarballs from the configured upstream (`--uplink`, default `https://registry.npmjs.org/`). The CDN then extracts the upstream tarball into `cdn-cache/<pkg>/<version>/` exactly as it does for locally-published packages, so subsequent requests are served from the local cache without re-hitting the upstream. The same fallback applies to `@powerhousedao/*` packages: the registry prefers its local copy, and falls back to the upstream when the package isn't published locally.
|
|
32
|
+
|
|
31
33
|
### Publish Notifications
|
|
32
34
|
|
|
33
35
|
When a package is published, the registry can notify subscribers in real time via Server-Sent Events (SSE) and webhooks.
|
|
@@ -107,21 +109,21 @@ ph-registry --port 8080 --storage-dir ./storage --cdn-cache-dir ./cdn-cache
|
|
|
107
109
|
|
|
108
110
|
Options:
|
|
109
111
|
|
|
110
|
-
| Option | Env Variable | Default
|
|
111
|
-
| ------------------------ | ---------------------- |
|
|
112
|
-
| `--port` | `PORT` | `8080`
|
|
113
|
-
| `--storage-dir` | `REGISTRY_STORAGE` | `./storage`
|
|
114
|
-
| `--cdn-cache-dir` | `REGISTRY_CDN_CACHE` | `./cdn-cache`
|
|
115
|
-
| `--uplink` | `REGISTRY_UPLINK` |
|
|
116
|
-
| `--web-enabled` | `REGISTRY_WEB` | `true`
|
|
117
|
-
| `--webhook` | `REGISTRY_WEBHOOKS` | —
|
|
118
|
-
| `--s3-bucket` | `S3_BUCKET` | —
|
|
119
|
-
| `--s3-endpoint` | `S3_ENDPOINT` | —
|
|
120
|
-
| `--s3-region` | `S3_REGION` | —
|
|
121
|
-
| `--s3-access-key-id` | `S3_ACCESS_KEY_ID` | —
|
|
122
|
-
| `--s3-secret-access-key` | `S3_SECRET_ACCESS_KEY` | —
|
|
123
|
-
| `--s3-key-prefix` | `S3_KEY_PREFIX` | —
|
|
124
|
-
| `--s3-force-path-style` | `S3_FORCE_PATH_STYLE` | `true`
|
|
112
|
+
| Option | Env Variable | Default | Description |
|
|
113
|
+
| ------------------------ | ---------------------- | ----------------------------- | ------------------------------------------------- |
|
|
114
|
+
| `--port` | `PORT` | `8080` | Port to listen on |
|
|
115
|
+
| `--storage-dir` | `REGISTRY_STORAGE` | `./storage` | Verdaccio storage directory |
|
|
116
|
+
| `--cdn-cache-dir` | `REGISTRY_CDN_CACHE` | `./cdn-cache` | CDN cache directory |
|
|
117
|
+
| `--uplink` | `REGISTRY_UPLINK` | `https://registry.npmjs.org/` | Upstream npm registry URL used for fallback proxy |
|
|
118
|
+
| `--web-enabled` | `REGISTRY_WEB` | `true` | Enable Verdaccio web UI |
|
|
119
|
+
| `--webhook` | `REGISTRY_WEBHOOKS` | — | Comma-separated webhook URLs to notify on publish |
|
|
120
|
+
| `--s3-bucket` | `S3_BUCKET` | — | S3 bucket for storage |
|
|
121
|
+
| `--s3-endpoint` | `S3_ENDPOINT` | — | S3 endpoint URL |
|
|
122
|
+
| `--s3-region` | `S3_REGION` | — | S3 region |
|
|
123
|
+
| `--s3-access-key-id` | `S3_ACCESS_KEY_ID` | — | S3 access key |
|
|
124
|
+
| `--s3-secret-access-key` | `S3_SECRET_ACCESS_KEY` | — | S3 secret key |
|
|
125
|
+
| `--s3-key-prefix` | `S3_KEY_PREFIX` | — | S3 key prefix |
|
|
126
|
+
| `--s3-force-path-style` | `S3_FORCE_PATH_STYLE` | `true` | Force S3 path-style URLs |
|
|
125
127
|
|
|
126
128
|
## Deployment
|
|
127
129
|
|
package/dist/cli.mjs
CHANGED
|
@@ -551,16 +551,23 @@ var WebhookChannel = class {
|
|
|
551
551
|
//#region src/verdaccio-config.ts
|
|
552
552
|
function buildVerdaccioConfig(config) {
|
|
553
553
|
const htpasswdPath = path.join(config.storagePath, "htpasswd");
|
|
554
|
+
const uplinkUrl = config.uplink ?? "https://registry.npmjs.org/";
|
|
554
555
|
const base = {
|
|
555
556
|
storage: config.storagePath,
|
|
556
557
|
self_path: "./",
|
|
557
558
|
auth: { htpasswd: { file: htpasswdPath } },
|
|
558
|
-
uplinks:
|
|
559
|
+
uplinks: { npmjs: {
|
|
560
|
+
url: uplinkUrl,
|
|
561
|
+
maxage: "15m",
|
|
562
|
+
timeout: "30s",
|
|
563
|
+
cache: true
|
|
564
|
+
} },
|
|
559
565
|
packages: {
|
|
560
566
|
"@powerhousedao/*": {
|
|
561
567
|
access: "$all",
|
|
562
568
|
publish: "$authenticated",
|
|
563
|
-
unpublish: "$authenticated"
|
|
569
|
+
unpublish: "$authenticated",
|
|
570
|
+
proxy: "npmjs"
|
|
564
571
|
},
|
|
565
572
|
"**": {
|
|
566
573
|
access: "$all",
|
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":["#resolveFile","#extractWithLock","#extractionLocks","#channels","#clients","#storagePath","#predefined","#dynamic","#load","#save","#filePath"],"sources":["../src/constants.ts","../src/semver.ts","../src/cdn.ts","../src/packages.ts","../src/middleware.ts","../src/notifications/manager.ts","../src/notifications/sse.ts","../src/notifications/webhook.ts","../src/verdaccio-config.ts","../src/run.ts","../cli.ts"],"sourcesContent":["export const DEFAULT_PORT = 8080;\nexport const DEFAULT_STORAGE_DIR_NAME = \"./storage\" as const;\nexport const DEFAULT_REGISTRY_CDN_CACHE_DIR_NAME = \"./cdn-cache\" as const;\n","/**\n * Compare two semver version strings for sorting.\n * Returns negative if a < b, positive if a > b, 0 if equal.\n *\n * Handles numeric component comparison (so \"1.0.10\" > \"1.0.9\")\n * and prerelease ordering (release > prerelease).\n */\nexport function compareSemver(a: string, b: string): number {\n const [coreA, preA] = a.split(\"-\", 2);\n const [coreB, preB] = b.split(\"-\", 2);\n\n const partsA = coreA.split(\".\").map(Number);\n const partsB = coreB.split(\".\").map(Number);\n\n for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {\n const na = partsA[i] ?? 0;\n const nb = partsB[i] ?? 0;\n if (na !== nb) return na - nb;\n }\n\n // Equal core versions — release (no prerelease) sorts after prerelease\n if (!preA && preB) return 1;\n if (preA && !preB) return -1;\n if (preA && preB) return preA < preB ? -1 : preA > preB ? 1 : 0;\n\n return 0;\n}\n","import crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { extract } from \"tar\";\nimport { compareSemver } from \"./semver.js\";\n\n/**\n * Parse a package specifier into name and version/tag.\n * Supports:\n * \"@scope/pkg\" -> { name: \"@scope/pkg\", tag: undefined }\n * \"@scope/pkg@dev\" -> { name: \"@scope/pkg\", tag: \"dev\" }\n * \"@scope/pkg@1.0.0\" -> { name: \"@scope/pkg\", tag: \"1.0.0\" }\n * \"pkg@latest\" -> { name: \"pkg\", tag: \"latest\" }\n */\nexport function parsePackageSpec(spec: string): {\n name: string;\n tag: string | undefined;\n} {\n // For scoped packages (@scope/name@tag), split on the last @\n // For unscoped packages (name@tag), split on the first @\n if (spec.startsWith(\"@\")) {\n // Scoped: find the @ after the scope/name portion\n const lastAt = spec.lastIndexOf(\"@\");\n if (lastAt > 0 && lastAt !== spec.indexOf(\"@\")) {\n return { name: spec.slice(0, lastAt), tag: spec.slice(lastAt + 1) };\n }\n return { name: spec, tag: undefined };\n }\n const atIndex = spec.indexOf(\"@\");\n if (atIndex > 0) {\n return { name: spec.slice(0, atIndex), tag: spec.slice(atIndex + 1) };\n }\n return { name: spec, tag: undefined };\n}\n\nexport class CdnCache {\n #extractionLocks = new Map<string, Promise<void>>();\n\n constructor(\n private registryUrl: string,\n private cdnCachePath: string,\n ) {}\n\n async getFileByVersion(\n packageName: string,\n version: string,\n filePath: string,\n ): Promise<string | null> {\n const versionDir = path.join(this.cdnCachePath, packageName, version);\n\n // Check all possible paths before attempting extraction\n const resolved = this.#resolveFile(versionDir, filePath);\n if (resolved) return resolved;\n\n // File not found in any location — extract tarball and try again\n await this.#extractWithLock(packageName, version);\n\n return this.#resolveFile(versionDir, filePath);\n }\n\n #resolveFile(versionDir: string, filePath: string): string | null {\n // Check direct path first, then fall back to cdn/ and dist/cdn/ subdirectories\n // (npm tarballs contain files under dist/, bun bundles go to cdn/)\n const candidates = [\n path.join(versionDir, filePath),\n path.join(versionDir, \"cdn\", filePath),\n path.join(versionDir, \"dist\", \"cdn\", filePath),\n path.join(versionDir, \"dist\", filePath),\n ];\n\n for (const candidate of candidates) {\n if (this.isSafePath(candidate) && fs.existsSync(candidate))\n return candidate;\n }\n\n return null;\n }\n\n async #extractWithLock(packageName: string, version: string): Promise<void> {\n const key = `${packageName}@${version}`;\n const existing = this.#extractionLocks.get(key);\n if (existing) return existing;\n\n const promise = this.extractTarball(packageName, version).finally(() => {\n this.#extractionLocks.delete(key);\n });\n this.#extractionLocks.set(key, promise);\n return promise;\n }\n\n getLatestCachedVersion(packageName: string): string | null {\n const pkgDir = path.join(this.cdnCachePath, packageName);\n try {\n const entries = fs.readdirSync(pkgDir, { withFileTypes: true });\n const versions = entries\n .filter((e) => e.isDirectory())\n .map((e) => e.name);\n if (versions.length === 0) return null;\n versions.sort(compareSemver);\n return versions[versions.length - 1];\n } catch {\n return null;\n }\n }\n\n /**\n * Resolve a version for a package. If tag is a semver version that exists\n * in the registry, return it directly. If tag is a dist-tag name (e.g.\n * \"dev\", \"latest\"), resolve it to the concrete version. If no tag is\n * provided, prefer \"latest\", then fall back to any available dist-tag.\n */\n async resolveVersion(\n packageName: string,\n tag?: string,\n ): Promise<string | null> {\n try {\n const url = `${this.registryUrl}/${encodeURIComponent(packageName)}`;\n const res = await fetch(url, {\n headers: { Accept: \"application/json\" },\n });\n if (!res.ok) return null;\n const metadata = (await res.json()) as Record<string, unknown>;\n const distTags = metadata[\"dist-tags\"] as\n | Record<string, string>\n | undefined;\n const versions = metadata[\"versions\"] as\n | Record<string, unknown>\n | undefined;\n\n if (tag) {\n // If the tag matches an exact version in the registry, use it directly\n if (versions && tag in versions) return tag;\n // Otherwise treat it as a dist-tag name\n if (distTags && tag in distTags) return distTags[tag];\n // Tag not found\n return null;\n }\n\n if (!distTags) return null;\n // No tag specified: prefer \"latest\", fall back to any available tag\n return distTags.latest ?? Object.values(distTags)[0] ?? null;\n } catch {\n return null;\n }\n }\n\n async extractTarball(packageName: string, version: string): Promise<void> {\n const shortName = packageName.startsWith(\"@\")\n ? packageName.split(\"/\")[1]\n : packageName;\n const tarballUrl = `${this.registryUrl}/${encodeURIComponent(packageName)}/-/${shortName}-${version}.tgz`;\n\n let res: Response;\n try {\n res = await fetch(tarballUrl);\n if (!res.ok || !res.body) return;\n } catch {\n return;\n }\n\n const destDir = path.join(this.cdnCachePath, packageName, version);\n fs.mkdirSync(destDir, { recursive: true });\n\n const tmpFile = path.join(\n destDir,\n `.tmp-tarball-${crypto.randomUUID()}.tgz`,\n );\n try {\n const fileStream = fs.createWriteStream(tmpFile);\n await pipeline(Readable.fromWeb(res.body as never), fileStream);\n await extract({ file: tmpFile, cwd: destDir, strip: 1 });\n } finally {\n fs.rmSync(tmpFile, { force: true });\n }\n }\n\n invalidate(packageName: string): void {\n const cacheDir = path.join(this.cdnCachePath, packageName);\n if (!this.isSafePath(cacheDir)) return;\n fs.rmSync(cacheDir, { recursive: true, force: true });\n }\n\n /** Remove all cached version directories except the specified one. */\n pruneOldVersions(packageName: string, keepVersion: string): void {\n const pkgDir = path.join(this.cdnCachePath, packageName);\n try {\n const entries = fs.readdirSync(pkgDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name !== keepVersion) {\n const dir = path.join(pkgDir, entry.name);\n if (this.isSafePath(dir)) {\n fs.rmSync(dir, { recursive: true, force: true });\n }\n }\n }\n } catch {\n // ignore — directory may not exist yet\n }\n }\n\n private isSafePath(filePath: string): boolean {\n const resolved = path.resolve(filePath);\n const cacheRoot = path.resolve(this.cdnCachePath);\n return resolved.startsWith(cacheRoot + path.sep) || resolved === cacheRoot;\n }\n}\n","import type { Manifest } from \"@powerhousedao/shared\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { compareSemver } from \"./semver.js\";\nimport type { PackageInfo } from \"./types.js\";\n\nfunction readManifest(dir: string): Manifest | null {\n const candidates = [\n path.join(dir, \"powerhouse.manifest.json\"),\n path.join(dir, \"cdn\", \"powerhouse.manifest.json\"),\n path.join(dir, \"dist\", \"powerhouse.manifest.json\"),\n ];\n for (const manifestPath of candidates) {\n try {\n const raw = fs.readFileSync(manifestPath, \"utf-8\");\n return JSON.parse(raw) as Manifest;\n } catch {\n // try next candidate\n }\n }\n return null;\n}\n\nfunction readPackageJsonVersion(dir: string): string | undefined {\n try {\n const raw = fs.readFileSync(path.join(dir, \"package.json\"), \"utf-8\");\n const pkg = JSON.parse(raw) as { version?: unknown };\n return typeof pkg.version === \"string\" ? pkg.version : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction getLatestVersionDir(pkgDir: string): string | null {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(pkgDir, { withFileTypes: true });\n } catch {\n return null;\n }\n const versions = entries.filter((e) => e.isDirectory()).map((e) => e.name);\n if (versions.length === 0) return null;\n versions.sort(compareSemver);\n return path.join(pkgDir, versions[versions.length - 1]);\n}\n\nexport function loadPackage(\n cdnCachePath: string,\n name: string,\n version?: string,\n): PackageInfo | null {\n const pkgDir = path.join(cdnCachePath, name);\n const versionDir = version\n ? path.join(pkgDir, version)\n : getLatestVersionDir(pkgDir);\n const manifestDir = versionDir ?? pkgDir;\n const manifest = readManifest(manifestDir);\n\n if (!manifest) {\n return null;\n }\n return {\n name: manifest.name ?? name,\n path: `/-/cdn/${name}`,\n manifest,\n documentTypes: getDocumentTypesFromManifest(manifest),\n version: readPackageJsonVersion(manifestDir),\n };\n}\n\nfunction getDocumentTypesFromManifest(manifest: Manifest | undefined | null) {\n if (!manifest) return [];\n\n const documentTypes: string[] = [];\n const { apps, documentModels, editors, subgraphs } = manifest;\n\n if (apps?.length) {\n documentTypes.push(\"powerhouse/document-drive\");\n }\n documentTypes.push(\n ...(documentModels ?? []).map((dm) => dm.id),\n ...(editors ?? [])\n .flatMap((e) => e.documentTypes)\n .filter((dt) => dt !== undefined),\n ...(subgraphs ?? [])\n .flatMap((e) => e.documentTypes)\n .filter((dt) => dt !== undefined),\n );\n\n return documentTypes;\n}\n\nexport function scanPackages(cdnCachePath: string): PackageInfo[] {\n const absDir = path.resolve(cdnCachePath);\n const packages: PackageInfo[] = [];\n\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(absDir, { withFileTypes: true });\n } catch {\n return packages;\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n if (entry.name.startsWith(\"@\")) {\n const scopeDir = path.join(absDir, entry.name);\n let scopedEntries: fs.Dirent[];\n try {\n scopedEntries = fs.readdirSync(scopeDir, { withFileTypes: true });\n } catch (error) {\n console.log(error);\n continue;\n }\n for (const scopedEntry of scopedEntries) {\n if (!scopedEntry.isDirectory()) continue;\n const dirName = `${entry.name}/${scopedEntry.name}`;\n const pkgDir = path.join(scopeDir, scopedEntry.name);\n const versionDir = getLatestVersionDir(pkgDir);\n const manifestDir = versionDir ?? pkgDir;\n const manifest = readManifest(manifestDir);\n const name = manifest?.name ?? dirName;\n packages.push({\n name,\n path: `/-/cdn/${dirName}`,\n manifest,\n documentTypes: getDocumentTypesFromManifest(manifest),\n version: readPackageJsonVersion(manifestDir),\n });\n }\n } else {\n const pkgDir = path.join(absDir, entry.name);\n const versionDir = getLatestVersionDir(pkgDir);\n const manifestDir = versionDir ?? pkgDir;\n const manifest = readManifest(manifestDir);\n const name = manifest?.name ?? entry.name;\n packages.push({\n name,\n path: `/-/cdn/${entry.name}`,\n manifest,\n documentTypes: getDocumentTypesFromManifest(manifest),\n version: readPackageJsonVersion(manifestDir),\n });\n }\n }\n\n return packages;\n}\n\nexport function findPackagesByDocumentType(\n packagesDir: string,\n documentType: string,\n): PackageInfo[] {\n const allPackages = scanPackages(packagesDir);\n\n return allPackages.filter((pkg) => {\n if (!pkg.manifest?.documentModels) {\n return false;\n }\n return pkg.manifest.documentModels.some((dm) => dm.id === documentType);\n });\n}\n","import express, {\n Router,\n type NextFunction,\n type Request,\n type Response,\n} from \"express\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { CdnCache, parsePackageSpec } from \"./cdn.js\";\nimport type { SSEChannel } from \"./notifications/sse.js\";\nimport type { NotificationChannel } from \"./notifications/types.js\";\nimport type { WebhookChannel } from \"./notifications/webhook.js\";\nimport {\n findPackagesByDocumentType,\n loadPackage,\n scanPackages,\n} from \"./packages.js\";\nimport type { RegistryConfig } from \"./types.js\";\n\nconst MIME_TYPES: Record<string, string> = {\n \".js\": \"application/javascript\",\n \".mjs\": \"application/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".wasm\": \"application/wasm\",\n \".map\": \"application/json\",\n \".html\": \"text/html\",\n \".svg\": \"image/svg+xml\",\n};\n\nfunction getContentType(filePath: string): string {\n const ext = path.extname(filePath).toLowerCase();\n return MIME_TYPES[ext] ?? \"application/octet-stream\";\n}\n\nexport function createPowerhouseRouter(\n config: RegistryConfig,\n sse: SSEChannel,\n webhooks: WebhookChannel,\n): Router {\n const cdn = new CdnCache(\n `http://localhost:${config.port}`,\n config.cdnCachePath,\n );\n const router = Router();\n\n // CORS on every response\n router.use((_req: Request, res: Response, next: NextFunction) => {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n next();\n });\n\n // SSE endpoint for publish notifications\n router.get(\"/-/events\", (_req: Request, res: Response) => {\n sse.addClient(res);\n });\n\n // Webhook management\n router.get(\"/-/webhooks\", (_req: Request, res: Response) => {\n res.json(webhooks.getWebhooks());\n });\n\n router.post(\"/-/webhooks\", express.json(), (req: Request, res: Response) => {\n const { endpoint, headers } = req.body as {\n endpoint?: string;\n headers?: Record<string, string>;\n };\n if (!endpoint) {\n res.status(400).json({ error: \"Missing required field: endpoint\" });\n return;\n }\n webhooks.addWebhook({ endpoint, headers });\n res.status(201).json({ endpoint, headers });\n });\n\n router.delete(\n \"/-/webhooks\",\n express.json(),\n (req: Request, res: Response) => {\n const { endpoint } = req.body as { endpoint?: string };\n if (!endpoint) {\n res.status(400).json({ error: \"Missing required field: endpoint\" });\n return;\n }\n const removed = webhooks.removeWebhook(endpoint);\n if (!removed) {\n res.status(404).json({ error: \"Webhook not found\" });\n return;\n }\n res.status(204).end();\n },\n );\n\n // Package listing API\n router.get(\"/packages\", (req: Request, res: Response) => {\n const packages = scanPackages(config.cdnCachePath);\n const documentType = req.query.documentType as string | undefined;\n if (documentType) {\n const filtered = packages.filter((pkg) =>\n pkg.manifest?.documentModels?.some((m) => m.id === documentType),\n );\n res.json(filtered);\n return;\n }\n res.json(packages);\n });\n\n // Find packages by document type - returns array of package names\n router.get(\"/packages/by-document-type\", (req: Request, res: Response) => {\n const documentType = req.query.type;\n\n if (typeof documentType !== \"string\" || !documentType) {\n res.status(400).json({ error: \"Missing required query parameter: type\" });\n return;\n }\n\n const packages = findPackagesByDocumentType(\n config.cdnCachePath,\n documentType,\n );\n const packageNames = packages.map((pkg) => pkg.name);\n res.json(packageNames);\n });\n\n // Single package info\n router.get(\"/packages/*name\", async (req: Request, res: Response) => {\n const raw = (req.params as Record<string, string[]>).name.join(\"/\");\n const { name, tag } = parsePackageSpec(raw);\n const version =\n (await cdn.resolveVersion(name, tag)) ?? cdn.getLatestCachedVersion(name);\n const pkg = loadPackage(config.cdnCachePath, name, version ?? undefined);\n if (!pkg) {\n res.status(404).send(\"Package not found\");\n return;\n }\n res.json(pkg);\n });\n\n // CDN file serving\n router.get(\"/-/cdn/*path\", async (req: Request, res: Response) => {\n const parts = (req.params as Record<string, string[]>).path;\n const fullPath = parts.join(\"/\");\n\n // Parse scoped or unscoped package specifier from the path\n let packageSpec: string;\n let filePath: string;\n\n if (fullPath.startsWith(\"@\")) {\n // Scoped: @scope/pkg@1.0.0/file.js -> packageSpec = @scope/pkg@1.0.0, filePath = file.js\n const segments = fullPath.split(\"/\");\n if (segments.length < 2) {\n res.status(400).send(\"Invalid package path\");\n return;\n }\n packageSpec = `${segments[0]}/${segments[1]}`;\n filePath = segments.slice(2).join(\"/\") || \"index.js\";\n } else {\n // Unscoped: pkg@1.0.0/file.js -> packageSpec = pkg@1.0.0, filePath = file.js\n const segments = fullPath.split(\"/\");\n packageSpec = segments[0];\n filePath = segments.slice(1).join(\"/\") || \"index.js\";\n }\n\n const { name: packageName, tag } = parsePackageSpec(packageSpec);\n const version =\n (await cdn.resolveVersion(packageName, tag)) ??\n cdn.getLatestCachedVersion(packageName);\n if (!version) {\n res.status(404).send(\"File not found\");\n return;\n }\n\n const resolved = await cdn.getFileByVersion(packageName, version, filePath);\n if (!resolved) {\n res.status(404).send(\"File not found\");\n return;\n }\n\n res.setHeader(\"Content-Type\", getContentType(filePath));\n const content = fs.readFileSync(resolved);\n res.send(content);\n });\n\n return router;\n}\n\nexport function createPublishHook(\n config: RegistryConfig,\n notifications: NotificationChannel,\n) {\n const cdn = new CdnCache(\n `http://localhost:${config.port}`,\n config.cdnCachePath,\n );\n\n return (req: Request, res: Response, next: NextFunction) => {\n // Only intercept PUT requests to npm publish endpoints\n if (req.method !== \"PUT\") {\n next();\n return;\n }\n\n const originalEnd = res.end.bind(res);\n res.end = function (\n this: Response,\n chunk?: unknown,\n encoding?: unknown,\n cb?: () => void,\n ) {\n const urlPath = req.path.replace(/^\\//, \"\");\n if (\n res.statusCode < 200 ||\n res.statusCode >= 300 ||\n !urlPath ||\n urlPath.startsWith(\"-\")\n ) {\n return originalEnd(chunk, encoding as BufferEncoding, cb);\n }\n const packageName = decodeURIComponent(urlPath);\n const versionsObj = (req.body as { versions: Record<string, unknown> })\n .versions;\n const versions = Object.keys(versionsObj);\n const version = versions.at(0);\n if (!version) {\n console.error(`[registry] No version found for ${packageName}`);\n return originalEnd(chunk, encoding as BufferEncoding, cb);\n }\n if (versions.length > 1) {\n console.warn(\n `[registry] Multiple versions published for ${packageName}: ${JSON.stringify(versions)}`,\n );\n }\n\n cdn\n .extractTarball(packageName, version)\n .then(() => {\n notifications.notifyPublish({ packageName, version });\n })\n .catch((err) => {\n console.error(\n `[registry] Failed to extract ${packageName} to CDN cache:`,\n err,\n );\n });\n\n return originalEnd(chunk, encoding as BufferEncoding, cb);\n };\n\n next();\n };\n}\n","import type { NotificationChannel, PublishEvent } from \"./types.js\";\n\nexport class NotificationManager implements NotificationChannel {\n #channels: NotificationChannel[];\n\n constructor(channels: NotificationChannel[]) {\n this.#channels = channels;\n }\n\n notifyPublish(event: PublishEvent): void {\n for (const channel of this.#channels) {\n channel.notifyPublish(event);\n }\n }\n}\n","import type { Response } from \"express\";\nimport type { NotificationChannel, PublishEvent } from \"./types.js\";\n\nexport class SSEChannel implements NotificationChannel {\n #clients = new Set<Response>();\n\n addClient(res: Response): void {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n res.write(\"event: connected\\ndata: {}\\n\\n\");\n\n this.#clients.add(res);\n res.on(\"close\", () => {\n this.#clients.delete(res);\n });\n }\n\n notifyPublish(event: PublishEvent): void {\n const payload = `event: publish\\ndata: ${JSON.stringify(event)}\\n\\n`;\n for (const client of this.#clients) {\n try {\n client.write(payload);\n } catch (err) {\n console.error(\"[registry] SSE client write failed:\", err);\n this.#clients.delete(client);\n }\n }\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { NotifyConfig, WebhookConfig } from \"../types.js\";\nimport type { NotificationChannel, PublishEvent } from \"./types.js\";\n\nconst WEBHOOKS_FILE = \"webhooks.json\";\n\nexport class WebhookChannel implements NotificationChannel {\n #predefined: WebhookConfig[];\n #dynamic: WebhookConfig[];\n #storagePath: string;\n\n constructor(storagePath: string, config?: NotifyConfig) {\n this.#storagePath = storagePath;\n this.#predefined = config?.webhooks ?? [];\n this.#dynamic = this.#load();\n }\n\n getWebhooks(): WebhookConfig[] {\n return [...this.#predefined, ...this.#dynamic];\n }\n\n addWebhook(webhook: WebhookConfig): void {\n const exists = this.getWebhooks().some(\n (w) => w.endpoint === webhook.endpoint,\n );\n if (exists) return;\n this.#dynamic.push(webhook);\n this.#save();\n }\n\n removeWebhook(endpoint: string): boolean {\n const before = this.#dynamic.length;\n this.#dynamic = this.#dynamic.filter((w) => w.endpoint !== endpoint);\n if (this.#dynamic.length === before) return false;\n this.#save();\n return true;\n }\n\n notifyPublish(event: PublishEvent): void {\n for (const webhook of this.getWebhooks()) {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...webhook.headers,\n };\n\n fetch(webhook.endpoint, {\n method: \"POST\",\n headers,\n body: JSON.stringify(event),\n }).catch((err: unknown) => {\n console.error(`[registry] Webhook to ${webhook.endpoint} failed:`, err);\n });\n }\n }\n\n #filePath(): string {\n return path.join(this.#storagePath, WEBHOOKS_FILE);\n }\n\n #load(): WebhookConfig[] {\n try {\n const raw = fs.readFileSync(this.#filePath(), \"utf-8\");\n return JSON.parse(raw) as WebhookConfig[];\n } catch {\n return [];\n }\n }\n\n #save(): void {\n fs.mkdirSync(this.#storagePath, { recursive: true });\n fs.writeFileSync(this.#filePath(), JSON.stringify(this.#dynamic, null, 2));\n }\n}\n","import path from \"node:path\";\nimport type { RegistryConfig } from \"./types.js\";\n\nexport function buildVerdaccioConfig(config: RegistryConfig) {\n const htpasswdPath = path.join(config.storagePath, \"htpasswd\");\n\n const base: Record<string, unknown> = {\n storage: config.storagePath,\n self_path: \"./\",\n auth: {\n htpasswd: {\n file: htpasswdPath,\n },\n },\n uplinks: undefined,\n packages: {\n \"@powerhousedao/*\": {\n access: \"$all\",\n publish: \"$authenticated\",\n unpublish: \"$authenticated\",\n },\n \"**\": {\n access: \"$all\",\n publish: \"$authenticated\",\n unpublish: \"$authenticated\",\n proxy: \"npmjs\",\n },\n },\n web: {\n enable: config.webEnabled !== false,\n title: \"Powerhouse Registry\",\n logo: \"https://raw.githubusercontent.com/powerhouse-inc/powerhouse/main/packages/registry/static/logo.svg\",\n favicon: \"/-/static/favicon.ico\",\n primary_color: \"#38C780\",\n darkMode: true,\n },\n server: {\n keepAliveTimeout: 60,\n },\n log: {\n type: \"stdout\",\n format: \"pretty\",\n level: \"warn\",\n },\n max_body_size: config.maxBodySize ?? \"300mb\",\n };\n\n if (config.s3) {\n base.store = {\n \"aws-s3-storage\": {\n bucket: config.s3.bucket,\n endpoint: config.s3.endpoint,\n region: config.s3.region,\n s3ForcePathStyle: config.s3.s3ForcePathStyle ?? true,\n ...(config.s3.keyPrefix && { keyPrefix: config.s3.keyPrefix }),\n ...(config.s3.accessKeyId && { accessKeyId: config.s3.accessKeyId }),\n ...(config.s3.secretAccessKey && {\n secretAccessKey: config.s3.secretAccessKey,\n }),\n },\n };\n }\n\n return base;\n}\n","import express from \"express\";\nimport { findUp } from \"find-up\";\nimport { mkdir } from \"node:fs/promises\";\nimport type { Server } from \"node:http\";\nimport path from \"node:path\";\nimport { runServer } from \"verdaccio\";\nimport { createPowerhouseRouter, createPublishHook } from \"./middleware.js\";\nimport { NotificationManager } from \"./notifications/manager.js\";\nimport { SSEChannel } from \"./notifications/sse.js\";\nimport { WebhookChannel } from \"./notifications/webhook.js\";\nimport type { RegistryCommandArgs, RegistryConfig } from \"./types.js\";\nimport { buildVerdaccioConfig } from \"./verdaccio-config.js\";\n\nasync function resolveDir(dir: string): Promise<string> {\n if (path.isAbsolute(dir)) {\n await mkdir(dir, { recursive: true });\n return dir;\n }\n const found = await findUp(dir, { type: \"directory\" });\n if (!found) {\n await mkdir(dir, { recursive: true });\n return dir;\n }\n return found;\n}\n\nexport async function runRegistry(args: RegistryCommandArgs) {\n const {\n port,\n storageDir,\n cdnCacheDir,\n uplink,\n webEnabled,\n webhooks,\n s3AccessKeyId,\n s3Bucket,\n s3Endpoint,\n s3ForcePathStyle,\n s3KeyPrefix,\n s3Region,\n s3SecretAccessKey,\n } = args;\n const storagePath = await resolveDir(storageDir);\n const cdnCachePath = await resolveDir(cdnCacheDir);\n\n console.log({\n storagePath,\n cdnCachePath,\n });\n\n const webhookConfigs = webhooks\n ?.split(\",\")\n .map((url) => url.trim())\n .filter(Boolean)\n .map((endpoint) => ({ endpoint }));\n\n const config: RegistryConfig = {\n port,\n storagePath,\n cdnCachePath,\n uplink,\n webEnabled,\n ...(webhookConfigs?.length && {\n notify: { webhooks: webhookConfigs },\n }),\n ...(s3Bucket &&\n s3Endpoint &&\n s3Region && {\n s3: {\n bucket: s3Bucket,\n endpoint: s3Endpoint,\n region: s3Region,\n accessKeyId: s3AccessKeyId,\n secretAccessKey: s3SecretAccessKey,\n keyPrefix: s3KeyPrefix,\n s3ForcePathStyle,\n },\n }),\n };\n // Ensure directories exist (for relative paths resolved via findUp)\n await mkdir(storagePath, { recursive: true });\n await mkdir(cdnCachePath, { recursive: true });\n\n const verdaccioConfig = buildVerdaccioConfig(config);\n\n // verdaccio's runServer returns Promise<any> (upstream type limitation)\n const verdaccioServer = (await runServer(verdaccioConfig)) as Server;\n const verdaccioHandler = verdaccioServer.listeners(\"request\")[0] as (\n ...args: unknown[]\n ) => void;\n\n const app = express();\n\n const sseChannel = new SSEChannel();\n const webhookChannel = new WebhookChannel(config.storagePath, config.notify);\n const notifications = new NotificationManager([sseChannel, webhookChannel]);\n\n // Serve static assets (logo, etc.)\n const staticDir = await findUp(\"static\", { type: \"directory\" });\n if (staticDir) {\n app.use(\"/-/static\", express.static(staticDir));\n }\n\n // Our routes take priority over Verdaccio\n app.use(createPowerhouseRouter(config, sseChannel, webhookChannel));\n app.use(createPublishHook(config, notifications));\n\n // Verdaccio handles everything else (npm protocol, web UI, auth)\n app.use((req, res) => verdaccioHandler(req, res));\n\n const server = app.listen(port, () => {\n console.log(`Powerhouse Registry running on http://localhost:${port}`);\n console.log(` CDN: http://localhost:${port}/-/cdn/`);\n console.log(` Packages: http://localhost:${port}/packages`);\n console.log(` npm: http://localhost:${port}/`);\n console.log(` Storage: ${storagePath}`);\n console.log(` CDN cache: ${cdnCachePath}`);\n if (config.s3) {\n console.log(` S3: ${config.s3.endpoint}/${config.s3.bucket}`);\n }\n });\n\n return server;\n}\n","import {\n binary,\n command,\n flag,\n number,\n option,\n optional,\n run,\n string,\n} from \"cmd-ts\";\nimport {\n DEFAULT_PORT,\n DEFAULT_REGISTRY_CDN_CACHE_DIR_NAME,\n DEFAULT_STORAGE_DIR_NAME,\n} from \"./src/constants.js\";\nimport { runRegistry } from \"./src/run.js\";\n\nexport const registryCommand = command({\n name: \"Package registry\",\n args: {\n port: option({\n long: \"port\",\n type: number,\n defaultValue: () => Number(process.env.PORT) || DEFAULT_PORT,\n defaultValueIsSerializable: true,\n }),\n storageDir: option({\n long: \"storage-dir\",\n type: string,\n defaultValue: () =>\n process.env.REGISTRY_STORAGE || DEFAULT_STORAGE_DIR_NAME,\n defaultValueIsSerializable: true,\n }),\n cdnCacheDir: option({\n long: \"cdn-cache-dir\",\n type: string,\n defaultValue: () =>\n process.env.REGISTRY_CDN_CACHE || DEFAULT_REGISTRY_CDN_CACHE_DIR_NAME,\n defaultValueIsSerializable: true,\n }),\n uplink: option({\n long: \"uplink\",\n type: optional(string),\n defaultValue: () => process.env.REGISTRY_UPLINK,\n defaultValueIsSerializable: true,\n }),\n s3Bucket: option({\n long: \"s3-bucket\",\n type: optional(string),\n defaultValue: () => process.env.S3_BUCKET,\n defaultValueIsSerializable: true,\n }),\n s3Endpoint: option({\n long: \"s3-endpoint\",\n type: optional(string),\n defaultValue: () => process.env.S3_ENDPOINT,\n defaultValueIsSerializable: true,\n }),\n s3Region: option({\n long: \"s3-region\",\n type: optional(string),\n defaultValue: () => process.env.S3_REGION,\n defaultValueIsSerializable: true,\n }),\n s3AccessKeyId: option({\n long: \"s3-access-key-id\",\n type: optional(string),\n defaultValue: () => process.env.S3_ACCESS_KEY_ID,\n defaultValueIsSerializable: true,\n }),\n s3SecretAccessKey: option({\n long: \"s3-secret-access-key\",\n type: optional(string),\n defaultValue: () => process.env.S3_SECRET_ACCESS_KEY,\n defaultValueIsSerializable: true,\n }),\n s3KeyPrefix: option({\n long: \"s3-key-prefix\",\n type: optional(string),\n defaultValue: () => process.env.S3_KEY_PREFIX,\n defaultValueIsSerializable: true,\n }),\n s3ForcePathStyle: flag({\n long: \"s3-force-path-style\",\n defaultValue: () => process.env.S3_FORCE_PATH_STYLE !== \"false\",\n defaultValueIsSerializable: true,\n }),\n webEnabled: flag({\n long: \"web-enabled\",\n defaultValue: () => process.env.REGISTRY_WEB !== \"false\",\n defaultValueIsSerializable: true,\n }),\n webhooks: option({\n long: \"webhook\",\n type: optional(string),\n description: \"Comma-separated webhook URLs to notify on publish\",\n defaultValue: () => process.env.REGISTRY_WEBHOOKS,\n defaultValueIsSerializable: true,\n }),\n },\n handler: async (args) => {\n console.log(args);\n\n try {\n await runRegistry(args);\n } catch (error) {\n console.error(\"Failed to start registry:\");\n console.error(error);\n process.exit(1);\n }\n },\n});\n\nconst registryCli = binary(registryCommand);\n\nawait run(registryCli, process.argv);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["#resolveFile","#extractWithLock","#extractionLocks","#channels","#clients","#storagePath","#predefined","#dynamic","#load","#save","#filePath"],"sources":["../src/constants.ts","../src/semver.ts","../src/cdn.ts","../src/packages.ts","../src/middleware.ts","../src/notifications/manager.ts","../src/notifications/sse.ts","../src/notifications/webhook.ts","../src/verdaccio-config.ts","../src/run.ts","../cli.ts"],"sourcesContent":["export const DEFAULT_PORT = 8080;\nexport const DEFAULT_STORAGE_DIR_NAME = \"./storage\" as const;\nexport const DEFAULT_REGISTRY_CDN_CACHE_DIR_NAME = \"./cdn-cache\" as const;\n","/**\n * Compare two semver version strings for sorting.\n * Returns negative if a < b, positive if a > b, 0 if equal.\n *\n * Handles numeric component comparison (so \"1.0.10\" > \"1.0.9\")\n * and prerelease ordering (release > prerelease).\n */\nexport function compareSemver(a: string, b: string): number {\n const [coreA, preA] = a.split(\"-\", 2);\n const [coreB, preB] = b.split(\"-\", 2);\n\n const partsA = coreA.split(\".\").map(Number);\n const partsB = coreB.split(\".\").map(Number);\n\n for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {\n const na = partsA[i] ?? 0;\n const nb = partsB[i] ?? 0;\n if (na !== nb) return na - nb;\n }\n\n // Equal core versions — release (no prerelease) sorts after prerelease\n if (!preA && preB) return 1;\n if (preA && !preB) return -1;\n if (preA && preB) return preA < preB ? -1 : preA > preB ? 1 : 0;\n\n return 0;\n}\n","import crypto from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport { pipeline } from \"node:stream/promises\";\nimport { extract } from \"tar\";\nimport { compareSemver } from \"./semver.js\";\n\n/**\n * Parse a package specifier into name and version/tag.\n * Supports:\n * \"@scope/pkg\" -> { name: \"@scope/pkg\", tag: undefined }\n * \"@scope/pkg@dev\" -> { name: \"@scope/pkg\", tag: \"dev\" }\n * \"@scope/pkg@1.0.0\" -> { name: \"@scope/pkg\", tag: \"1.0.0\" }\n * \"pkg@latest\" -> { name: \"pkg\", tag: \"latest\" }\n */\nexport function parsePackageSpec(spec: string): {\n name: string;\n tag: string | undefined;\n} {\n // For scoped packages (@scope/name@tag), split on the last @\n // For unscoped packages (name@tag), split on the first @\n if (spec.startsWith(\"@\")) {\n // Scoped: find the @ after the scope/name portion\n const lastAt = spec.lastIndexOf(\"@\");\n if (lastAt > 0 && lastAt !== spec.indexOf(\"@\")) {\n return { name: spec.slice(0, lastAt), tag: spec.slice(lastAt + 1) };\n }\n return { name: spec, tag: undefined };\n }\n const atIndex = spec.indexOf(\"@\");\n if (atIndex > 0) {\n return { name: spec.slice(0, atIndex), tag: spec.slice(atIndex + 1) };\n }\n return { name: spec, tag: undefined };\n}\n\nexport class CdnCache {\n #extractionLocks = new Map<string, Promise<void>>();\n\n constructor(\n private registryUrl: string,\n private cdnCachePath: string,\n ) {}\n\n async getFileByVersion(\n packageName: string,\n version: string,\n filePath: string,\n ): Promise<string | null> {\n const versionDir = path.join(this.cdnCachePath, packageName, version);\n\n // Check all possible paths before attempting extraction\n const resolved = this.#resolveFile(versionDir, filePath);\n if (resolved) return resolved;\n\n // File not found in any location — extract tarball and try again\n await this.#extractWithLock(packageName, version);\n\n return this.#resolveFile(versionDir, filePath);\n }\n\n #resolveFile(versionDir: string, filePath: string): string | null {\n // Check direct path first, then fall back to cdn/ and dist/cdn/ subdirectories\n // (npm tarballs contain files under dist/, bun bundles go to cdn/)\n const candidates = [\n path.join(versionDir, filePath),\n path.join(versionDir, \"cdn\", filePath),\n path.join(versionDir, \"dist\", \"cdn\", filePath),\n path.join(versionDir, \"dist\", filePath),\n ];\n\n for (const candidate of candidates) {\n if (this.isSafePath(candidate) && fs.existsSync(candidate))\n return candidate;\n }\n\n return null;\n }\n\n async #extractWithLock(packageName: string, version: string): Promise<void> {\n const key = `${packageName}@${version}`;\n const existing = this.#extractionLocks.get(key);\n if (existing) return existing;\n\n const promise = this.extractTarball(packageName, version).finally(() => {\n this.#extractionLocks.delete(key);\n });\n this.#extractionLocks.set(key, promise);\n return promise;\n }\n\n getLatestCachedVersion(packageName: string): string | null {\n const pkgDir = path.join(this.cdnCachePath, packageName);\n try {\n const entries = fs.readdirSync(pkgDir, { withFileTypes: true });\n const versions = entries\n .filter((e) => e.isDirectory())\n .map((e) => e.name);\n if (versions.length === 0) return null;\n versions.sort(compareSemver);\n return versions[versions.length - 1];\n } catch {\n return null;\n }\n }\n\n /**\n * Resolve a version for a package. If tag is a semver version that exists\n * in the registry, return it directly. If tag is a dist-tag name (e.g.\n * \"dev\", \"latest\"), resolve it to the concrete version. If no tag is\n * provided, prefer \"latest\", then fall back to any available dist-tag.\n */\n async resolveVersion(\n packageName: string,\n tag?: string,\n ): Promise<string | null> {\n try {\n const url = `${this.registryUrl}/${encodeURIComponent(packageName)}`;\n const res = await fetch(url, {\n headers: { Accept: \"application/json\" },\n });\n if (!res.ok) return null;\n const metadata = (await res.json()) as Record<string, unknown>;\n const distTags = metadata[\"dist-tags\"] as\n | Record<string, string>\n | undefined;\n const versions = metadata[\"versions\"] as\n | Record<string, unknown>\n | undefined;\n\n if (tag) {\n // If the tag matches an exact version in the registry, use it directly\n if (versions && tag in versions) return tag;\n // Otherwise treat it as a dist-tag name\n if (distTags && tag in distTags) return distTags[tag];\n // Tag not found\n return null;\n }\n\n if (!distTags) return null;\n // No tag specified: prefer \"latest\", fall back to any available tag\n return distTags.latest ?? Object.values(distTags)[0] ?? null;\n } catch {\n return null;\n }\n }\n\n async extractTarball(packageName: string, version: string): Promise<void> {\n const shortName = packageName.startsWith(\"@\")\n ? packageName.split(\"/\")[1]\n : packageName;\n const tarballUrl = `${this.registryUrl}/${encodeURIComponent(packageName)}/-/${shortName}-${version}.tgz`;\n\n let res: Response;\n try {\n res = await fetch(tarballUrl);\n if (!res.ok || !res.body) return;\n } catch {\n return;\n }\n\n const destDir = path.join(this.cdnCachePath, packageName, version);\n fs.mkdirSync(destDir, { recursive: true });\n\n const tmpFile = path.join(\n destDir,\n `.tmp-tarball-${crypto.randomUUID()}.tgz`,\n );\n try {\n const fileStream = fs.createWriteStream(tmpFile);\n await pipeline(Readable.fromWeb(res.body as never), fileStream);\n await extract({ file: tmpFile, cwd: destDir, strip: 1 });\n } finally {\n fs.rmSync(tmpFile, { force: true });\n }\n }\n\n invalidate(packageName: string): void {\n const cacheDir = path.join(this.cdnCachePath, packageName);\n if (!this.isSafePath(cacheDir)) return;\n fs.rmSync(cacheDir, { recursive: true, force: true });\n }\n\n /** Remove all cached version directories except the specified one. */\n pruneOldVersions(packageName: string, keepVersion: string): void {\n const pkgDir = path.join(this.cdnCachePath, packageName);\n try {\n const entries = fs.readdirSync(pkgDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.isDirectory() && entry.name !== keepVersion) {\n const dir = path.join(pkgDir, entry.name);\n if (this.isSafePath(dir)) {\n fs.rmSync(dir, { recursive: true, force: true });\n }\n }\n }\n } catch {\n // ignore — directory may not exist yet\n }\n }\n\n private isSafePath(filePath: string): boolean {\n const resolved = path.resolve(filePath);\n const cacheRoot = path.resolve(this.cdnCachePath);\n return resolved.startsWith(cacheRoot + path.sep) || resolved === cacheRoot;\n }\n}\n","import type { Manifest } from \"@powerhousedao/shared\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { compareSemver } from \"./semver.js\";\nimport type { PackageInfo } from \"./types.js\";\n\nfunction readManifest(dir: string): Manifest | null {\n const candidates = [\n path.join(dir, \"powerhouse.manifest.json\"),\n path.join(dir, \"cdn\", \"powerhouse.manifest.json\"),\n path.join(dir, \"dist\", \"powerhouse.manifest.json\"),\n ];\n for (const manifestPath of candidates) {\n try {\n const raw = fs.readFileSync(manifestPath, \"utf-8\");\n return JSON.parse(raw) as Manifest;\n } catch {\n // try next candidate\n }\n }\n return null;\n}\n\nfunction readPackageJsonVersion(dir: string): string | undefined {\n try {\n const raw = fs.readFileSync(path.join(dir, \"package.json\"), \"utf-8\");\n const pkg = JSON.parse(raw) as { version?: unknown };\n return typeof pkg.version === \"string\" ? pkg.version : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction getLatestVersionDir(pkgDir: string): string | null {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(pkgDir, { withFileTypes: true });\n } catch {\n return null;\n }\n const versions = entries.filter((e) => e.isDirectory()).map((e) => e.name);\n if (versions.length === 0) return null;\n versions.sort(compareSemver);\n return path.join(pkgDir, versions[versions.length - 1]);\n}\n\nexport function loadPackage(\n cdnCachePath: string,\n name: string,\n version?: string,\n): PackageInfo | null {\n const pkgDir = path.join(cdnCachePath, name);\n const versionDir = version\n ? path.join(pkgDir, version)\n : getLatestVersionDir(pkgDir);\n const manifestDir = versionDir ?? pkgDir;\n const manifest = readManifest(manifestDir);\n\n if (!manifest) {\n return null;\n }\n return {\n name: manifest.name ?? name,\n path: `/-/cdn/${name}`,\n manifest,\n documentTypes: getDocumentTypesFromManifest(manifest),\n version: readPackageJsonVersion(manifestDir),\n };\n}\n\nfunction getDocumentTypesFromManifest(manifest: Manifest | undefined | null) {\n if (!manifest) return [];\n\n const documentTypes: string[] = [];\n const { apps, documentModels, editors, subgraphs } = manifest;\n\n if (apps?.length) {\n documentTypes.push(\"powerhouse/document-drive\");\n }\n documentTypes.push(\n ...(documentModels ?? []).map((dm) => dm.id),\n ...(editors ?? [])\n .flatMap((e) => e.documentTypes)\n .filter((dt) => dt !== undefined),\n ...(subgraphs ?? [])\n .flatMap((e) => e.documentTypes)\n .filter((dt) => dt !== undefined),\n );\n\n return documentTypes;\n}\n\nexport function scanPackages(cdnCachePath: string): PackageInfo[] {\n const absDir = path.resolve(cdnCachePath);\n const packages: PackageInfo[] = [];\n\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(absDir, { withFileTypes: true });\n } catch {\n return packages;\n }\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n if (entry.name.startsWith(\"@\")) {\n const scopeDir = path.join(absDir, entry.name);\n let scopedEntries: fs.Dirent[];\n try {\n scopedEntries = fs.readdirSync(scopeDir, { withFileTypes: true });\n } catch (error) {\n console.log(error);\n continue;\n }\n for (const scopedEntry of scopedEntries) {\n if (!scopedEntry.isDirectory()) continue;\n const dirName = `${entry.name}/${scopedEntry.name}`;\n const pkgDir = path.join(scopeDir, scopedEntry.name);\n const versionDir = getLatestVersionDir(pkgDir);\n const manifestDir = versionDir ?? pkgDir;\n const manifest = readManifest(manifestDir);\n const name = manifest?.name ?? dirName;\n packages.push({\n name,\n path: `/-/cdn/${dirName}`,\n manifest,\n documentTypes: getDocumentTypesFromManifest(manifest),\n version: readPackageJsonVersion(manifestDir),\n });\n }\n } else {\n const pkgDir = path.join(absDir, entry.name);\n const versionDir = getLatestVersionDir(pkgDir);\n const manifestDir = versionDir ?? pkgDir;\n const manifest = readManifest(manifestDir);\n const name = manifest?.name ?? entry.name;\n packages.push({\n name,\n path: `/-/cdn/${entry.name}`,\n manifest,\n documentTypes: getDocumentTypesFromManifest(manifest),\n version: readPackageJsonVersion(manifestDir),\n });\n }\n }\n\n return packages;\n}\n\nexport function findPackagesByDocumentType(\n packagesDir: string,\n documentType: string,\n): PackageInfo[] {\n const allPackages = scanPackages(packagesDir);\n\n return allPackages.filter((pkg) => {\n if (!pkg.manifest?.documentModels) {\n return false;\n }\n return pkg.manifest.documentModels.some((dm) => dm.id === documentType);\n });\n}\n","import express, {\n Router,\n type NextFunction,\n type Request,\n type Response,\n} from \"express\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { CdnCache, parsePackageSpec } from \"./cdn.js\";\nimport type { SSEChannel } from \"./notifications/sse.js\";\nimport type { NotificationChannel } from \"./notifications/types.js\";\nimport type { WebhookChannel } from \"./notifications/webhook.js\";\nimport {\n findPackagesByDocumentType,\n loadPackage,\n scanPackages,\n} from \"./packages.js\";\nimport type { RegistryConfig } from \"./types.js\";\n\nconst MIME_TYPES: Record<string, string> = {\n \".js\": \"application/javascript\",\n \".mjs\": \"application/javascript\",\n \".css\": \"text/css\",\n \".json\": \"application/json\",\n \".wasm\": \"application/wasm\",\n \".map\": \"application/json\",\n \".html\": \"text/html\",\n \".svg\": \"image/svg+xml\",\n};\n\nfunction getContentType(filePath: string): string {\n const ext = path.extname(filePath).toLowerCase();\n return MIME_TYPES[ext] ?? \"application/octet-stream\";\n}\n\nexport function createPowerhouseRouter(\n config: RegistryConfig,\n sse: SSEChannel,\n webhooks: WebhookChannel,\n): Router {\n const cdn = new CdnCache(\n `http://localhost:${config.port}`,\n config.cdnCachePath,\n );\n const router = Router();\n\n // CORS on every response\n router.use((_req: Request, res: Response, next: NextFunction) => {\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\");\n next();\n });\n\n // SSE endpoint for publish notifications\n router.get(\"/-/events\", (_req: Request, res: Response) => {\n sse.addClient(res);\n });\n\n // Webhook management\n router.get(\"/-/webhooks\", (_req: Request, res: Response) => {\n res.json(webhooks.getWebhooks());\n });\n\n router.post(\"/-/webhooks\", express.json(), (req: Request, res: Response) => {\n const { endpoint, headers } = req.body as {\n endpoint?: string;\n headers?: Record<string, string>;\n };\n if (!endpoint) {\n res.status(400).json({ error: \"Missing required field: endpoint\" });\n return;\n }\n webhooks.addWebhook({ endpoint, headers });\n res.status(201).json({ endpoint, headers });\n });\n\n router.delete(\n \"/-/webhooks\",\n express.json(),\n (req: Request, res: Response) => {\n const { endpoint } = req.body as { endpoint?: string };\n if (!endpoint) {\n res.status(400).json({ error: \"Missing required field: endpoint\" });\n return;\n }\n const removed = webhooks.removeWebhook(endpoint);\n if (!removed) {\n res.status(404).json({ error: \"Webhook not found\" });\n return;\n }\n res.status(204).end();\n },\n );\n\n // Package listing API\n router.get(\"/packages\", (req: Request, res: Response) => {\n const packages = scanPackages(config.cdnCachePath);\n const documentType = req.query.documentType as string | undefined;\n if (documentType) {\n const filtered = packages.filter((pkg) =>\n pkg.manifest?.documentModels?.some((m) => m.id === documentType),\n );\n res.json(filtered);\n return;\n }\n res.json(packages);\n });\n\n // Find packages by document type - returns array of package names\n router.get(\"/packages/by-document-type\", (req: Request, res: Response) => {\n const documentType = req.query.type;\n\n if (typeof documentType !== \"string\" || !documentType) {\n res.status(400).json({ error: \"Missing required query parameter: type\" });\n return;\n }\n\n const packages = findPackagesByDocumentType(\n config.cdnCachePath,\n documentType,\n );\n const packageNames = packages.map((pkg) => pkg.name);\n res.json(packageNames);\n });\n\n // Single package info\n router.get(\"/packages/*name\", async (req: Request, res: Response) => {\n const raw = (req.params as Record<string, string[]>).name.join(\"/\");\n const { name, tag } = parsePackageSpec(raw);\n const version =\n (await cdn.resolveVersion(name, tag)) ?? cdn.getLatestCachedVersion(name);\n const pkg = loadPackage(config.cdnCachePath, name, version ?? undefined);\n if (!pkg) {\n res.status(404).send(\"Package not found\");\n return;\n }\n res.json(pkg);\n });\n\n // CDN file serving\n router.get(\"/-/cdn/*path\", async (req: Request, res: Response) => {\n const parts = (req.params as Record<string, string[]>).path;\n const fullPath = parts.join(\"/\");\n\n // Parse scoped or unscoped package specifier from the path\n let packageSpec: string;\n let filePath: string;\n\n if (fullPath.startsWith(\"@\")) {\n // Scoped: @scope/pkg@1.0.0/file.js -> packageSpec = @scope/pkg@1.0.0, filePath = file.js\n const segments = fullPath.split(\"/\");\n if (segments.length < 2) {\n res.status(400).send(\"Invalid package path\");\n return;\n }\n packageSpec = `${segments[0]}/${segments[1]}`;\n filePath = segments.slice(2).join(\"/\") || \"index.js\";\n } else {\n // Unscoped: pkg@1.0.0/file.js -> packageSpec = pkg@1.0.0, filePath = file.js\n const segments = fullPath.split(\"/\");\n packageSpec = segments[0];\n filePath = segments.slice(1).join(\"/\") || \"index.js\";\n }\n\n const { name: packageName, tag } = parsePackageSpec(packageSpec);\n const version =\n (await cdn.resolveVersion(packageName, tag)) ??\n cdn.getLatestCachedVersion(packageName);\n if (!version) {\n res.status(404).send(\"File not found\");\n return;\n }\n\n const resolved = await cdn.getFileByVersion(packageName, version, filePath);\n if (!resolved) {\n res.status(404).send(\"File not found\");\n return;\n }\n\n res.setHeader(\"Content-Type\", getContentType(filePath));\n const content = fs.readFileSync(resolved);\n res.send(content);\n });\n\n return router;\n}\n\nexport function createPublishHook(\n config: RegistryConfig,\n notifications: NotificationChannel,\n) {\n const cdn = new CdnCache(\n `http://localhost:${config.port}`,\n config.cdnCachePath,\n );\n\n return (req: Request, res: Response, next: NextFunction) => {\n // Only intercept PUT requests to npm publish endpoints\n if (req.method !== \"PUT\") {\n next();\n return;\n }\n\n const originalEnd = res.end.bind(res);\n res.end = function (\n this: Response,\n chunk?: unknown,\n encoding?: unknown,\n cb?: () => void,\n ) {\n const urlPath = req.path.replace(/^\\//, \"\");\n if (\n res.statusCode < 200 ||\n res.statusCode >= 300 ||\n !urlPath ||\n urlPath.startsWith(\"-\")\n ) {\n return originalEnd(chunk, encoding as BufferEncoding, cb);\n }\n const packageName = decodeURIComponent(urlPath);\n const versionsObj = (req.body as { versions: Record<string, unknown> })\n .versions;\n const versions = Object.keys(versionsObj);\n const version = versions.at(0);\n if (!version) {\n console.error(`[registry] No version found for ${packageName}`);\n return originalEnd(chunk, encoding as BufferEncoding, cb);\n }\n if (versions.length > 1) {\n console.warn(\n `[registry] Multiple versions published for ${packageName}: ${JSON.stringify(versions)}`,\n );\n }\n\n cdn\n .extractTarball(packageName, version)\n .then(() => {\n notifications.notifyPublish({ packageName, version });\n })\n .catch((err) => {\n console.error(\n `[registry] Failed to extract ${packageName} to CDN cache:`,\n err,\n );\n });\n\n return originalEnd(chunk, encoding as BufferEncoding, cb);\n };\n\n next();\n };\n}\n","import type { NotificationChannel, PublishEvent } from \"./types.js\";\n\nexport class NotificationManager implements NotificationChannel {\n #channels: NotificationChannel[];\n\n constructor(channels: NotificationChannel[]) {\n this.#channels = channels;\n }\n\n notifyPublish(event: PublishEvent): void {\n for (const channel of this.#channels) {\n channel.notifyPublish(event);\n }\n }\n}\n","import type { Response } from \"express\";\nimport type { NotificationChannel, PublishEvent } from \"./types.js\";\n\nexport class SSEChannel implements NotificationChannel {\n #clients = new Set<Response>();\n\n addClient(res: Response): void {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n Connection: \"keep-alive\",\n \"Access-Control-Allow-Origin\": \"*\",\n });\n res.write(\"event: connected\\ndata: {}\\n\\n\");\n\n this.#clients.add(res);\n res.on(\"close\", () => {\n this.#clients.delete(res);\n });\n }\n\n notifyPublish(event: PublishEvent): void {\n const payload = `event: publish\\ndata: ${JSON.stringify(event)}\\n\\n`;\n for (const client of this.#clients) {\n try {\n client.write(payload);\n } catch (err) {\n console.error(\"[registry] SSE client write failed:\", err);\n this.#clients.delete(client);\n }\n }\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { NotifyConfig, WebhookConfig } from \"../types.js\";\nimport type { NotificationChannel, PublishEvent } from \"./types.js\";\n\nconst WEBHOOKS_FILE = \"webhooks.json\";\n\nexport class WebhookChannel implements NotificationChannel {\n #predefined: WebhookConfig[];\n #dynamic: WebhookConfig[];\n #storagePath: string;\n\n constructor(storagePath: string, config?: NotifyConfig) {\n this.#storagePath = storagePath;\n this.#predefined = config?.webhooks ?? [];\n this.#dynamic = this.#load();\n }\n\n getWebhooks(): WebhookConfig[] {\n return [...this.#predefined, ...this.#dynamic];\n }\n\n addWebhook(webhook: WebhookConfig): void {\n const exists = this.getWebhooks().some(\n (w) => w.endpoint === webhook.endpoint,\n );\n if (exists) return;\n this.#dynamic.push(webhook);\n this.#save();\n }\n\n removeWebhook(endpoint: string): boolean {\n const before = this.#dynamic.length;\n this.#dynamic = this.#dynamic.filter((w) => w.endpoint !== endpoint);\n if (this.#dynamic.length === before) return false;\n this.#save();\n return true;\n }\n\n notifyPublish(event: PublishEvent): void {\n for (const webhook of this.getWebhooks()) {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...webhook.headers,\n };\n\n fetch(webhook.endpoint, {\n method: \"POST\",\n headers,\n body: JSON.stringify(event),\n }).catch((err: unknown) => {\n console.error(`[registry] Webhook to ${webhook.endpoint} failed:`, err);\n });\n }\n }\n\n #filePath(): string {\n return path.join(this.#storagePath, WEBHOOKS_FILE);\n }\n\n #load(): WebhookConfig[] {\n try {\n const raw = fs.readFileSync(this.#filePath(), \"utf-8\");\n return JSON.parse(raw) as WebhookConfig[];\n } catch {\n return [];\n }\n }\n\n #save(): void {\n fs.mkdirSync(this.#storagePath, { recursive: true });\n fs.writeFileSync(this.#filePath(), JSON.stringify(this.#dynamic, null, 2));\n }\n}\n","import path from \"node:path\";\nimport type { RegistryConfig } from \"./types.js\";\n\nexport function buildVerdaccioConfig(config: RegistryConfig) {\n const htpasswdPath = path.join(config.storagePath, \"htpasswd\");\n\n const uplinkUrl = config.uplink ?? \"https://registry.npmjs.org/\";\n\n const base: Record<string, unknown> = {\n storage: config.storagePath,\n self_path: \"./\",\n auth: {\n htpasswd: {\n file: htpasswdPath,\n },\n },\n uplinks: {\n npmjs: {\n url: uplinkUrl,\n maxage: \"15m\",\n timeout: \"30s\",\n cache: true,\n },\n },\n packages: {\n \"@powerhousedao/*\": {\n access: \"$all\",\n publish: \"$authenticated\",\n unpublish: \"$authenticated\",\n proxy: \"npmjs\",\n },\n \"**\": {\n access: \"$all\",\n publish: \"$authenticated\",\n unpublish: \"$authenticated\",\n proxy: \"npmjs\",\n },\n },\n web: {\n enable: config.webEnabled !== false,\n title: \"Powerhouse Registry\",\n logo: \"https://raw.githubusercontent.com/powerhouse-inc/powerhouse/main/packages/registry/static/logo.svg\",\n favicon: \"/-/static/favicon.ico\",\n primary_color: \"#38C780\",\n darkMode: true,\n },\n server: {\n keepAliveTimeout: 60,\n },\n log: {\n type: \"stdout\",\n format: \"pretty\",\n level: \"warn\",\n },\n max_body_size: config.maxBodySize ?? \"300mb\",\n };\n\n if (config.s3) {\n base.store = {\n \"aws-s3-storage\": {\n bucket: config.s3.bucket,\n endpoint: config.s3.endpoint,\n region: config.s3.region,\n s3ForcePathStyle: config.s3.s3ForcePathStyle ?? true,\n ...(config.s3.keyPrefix && { keyPrefix: config.s3.keyPrefix }),\n ...(config.s3.accessKeyId && { accessKeyId: config.s3.accessKeyId }),\n ...(config.s3.secretAccessKey && {\n secretAccessKey: config.s3.secretAccessKey,\n }),\n },\n };\n }\n\n return base;\n}\n","import express from \"express\";\nimport { findUp } from \"find-up\";\nimport { mkdir } from \"node:fs/promises\";\nimport type { Server } from \"node:http\";\nimport path from \"node:path\";\nimport { runServer } from \"verdaccio\";\nimport { createPowerhouseRouter, createPublishHook } from \"./middleware.js\";\nimport { NotificationManager } from \"./notifications/manager.js\";\nimport { SSEChannel } from \"./notifications/sse.js\";\nimport { WebhookChannel } from \"./notifications/webhook.js\";\nimport type { RegistryCommandArgs, RegistryConfig } from \"./types.js\";\nimport { buildVerdaccioConfig } from \"./verdaccio-config.js\";\n\nasync function resolveDir(dir: string): Promise<string> {\n if (path.isAbsolute(dir)) {\n await mkdir(dir, { recursive: true });\n return dir;\n }\n const found = await findUp(dir, { type: \"directory\" });\n if (!found) {\n await mkdir(dir, { recursive: true });\n return dir;\n }\n return found;\n}\n\nexport async function runRegistry(args: RegistryCommandArgs) {\n const {\n port,\n storageDir,\n cdnCacheDir,\n uplink,\n webEnabled,\n webhooks,\n s3AccessKeyId,\n s3Bucket,\n s3Endpoint,\n s3ForcePathStyle,\n s3KeyPrefix,\n s3Region,\n s3SecretAccessKey,\n } = args;\n const storagePath = await resolveDir(storageDir);\n const cdnCachePath = await resolveDir(cdnCacheDir);\n\n console.log({\n storagePath,\n cdnCachePath,\n });\n\n const webhookConfigs = webhooks\n ?.split(\",\")\n .map((url) => url.trim())\n .filter(Boolean)\n .map((endpoint) => ({ endpoint }));\n\n const config: RegistryConfig = {\n port,\n storagePath,\n cdnCachePath,\n uplink,\n webEnabled,\n ...(webhookConfigs?.length && {\n notify: { webhooks: webhookConfigs },\n }),\n ...(s3Bucket &&\n s3Endpoint &&\n s3Region && {\n s3: {\n bucket: s3Bucket,\n endpoint: s3Endpoint,\n region: s3Region,\n accessKeyId: s3AccessKeyId,\n secretAccessKey: s3SecretAccessKey,\n keyPrefix: s3KeyPrefix,\n s3ForcePathStyle,\n },\n }),\n };\n // Ensure directories exist (for relative paths resolved via findUp)\n await mkdir(storagePath, { recursive: true });\n await mkdir(cdnCachePath, { recursive: true });\n\n const verdaccioConfig = buildVerdaccioConfig(config);\n\n // verdaccio's runServer returns Promise<any> (upstream type limitation)\n const verdaccioServer = (await runServer(verdaccioConfig)) as Server;\n const verdaccioHandler = verdaccioServer.listeners(\"request\")[0] as (\n ...args: unknown[]\n ) => void;\n\n const app = express();\n\n const sseChannel = new SSEChannel();\n const webhookChannel = new WebhookChannel(config.storagePath, config.notify);\n const notifications = new NotificationManager([sseChannel, webhookChannel]);\n\n // Serve static assets (logo, etc.)\n const staticDir = await findUp(\"static\", { type: \"directory\" });\n if (staticDir) {\n app.use(\"/-/static\", express.static(staticDir));\n }\n\n // Our routes take priority over Verdaccio\n app.use(createPowerhouseRouter(config, sseChannel, webhookChannel));\n app.use(createPublishHook(config, notifications));\n\n // Verdaccio handles everything else (npm protocol, web UI, auth)\n app.use((req, res) => verdaccioHandler(req, res));\n\n const server = app.listen(port, () => {\n console.log(`Powerhouse Registry running on http://localhost:${port}`);\n console.log(` CDN: http://localhost:${port}/-/cdn/`);\n console.log(` Packages: http://localhost:${port}/packages`);\n console.log(` npm: http://localhost:${port}/`);\n console.log(` Storage: ${storagePath}`);\n console.log(` CDN cache: ${cdnCachePath}`);\n if (config.s3) {\n console.log(` S3: ${config.s3.endpoint}/${config.s3.bucket}`);\n }\n });\n\n return server;\n}\n","import {\n binary,\n command,\n flag,\n number,\n option,\n optional,\n run,\n string,\n} from \"cmd-ts\";\nimport {\n DEFAULT_PORT,\n DEFAULT_REGISTRY_CDN_CACHE_DIR_NAME,\n DEFAULT_STORAGE_DIR_NAME,\n} from \"./src/constants.js\";\nimport { runRegistry } from \"./src/run.js\";\n\nexport const registryCommand = command({\n name: \"Package registry\",\n args: {\n port: option({\n long: \"port\",\n type: number,\n defaultValue: () => Number(process.env.PORT) || DEFAULT_PORT,\n defaultValueIsSerializable: true,\n }),\n storageDir: option({\n long: \"storage-dir\",\n type: string,\n defaultValue: () =>\n process.env.REGISTRY_STORAGE || DEFAULT_STORAGE_DIR_NAME,\n defaultValueIsSerializable: true,\n }),\n cdnCacheDir: option({\n long: \"cdn-cache-dir\",\n type: string,\n defaultValue: () =>\n process.env.REGISTRY_CDN_CACHE || DEFAULT_REGISTRY_CDN_CACHE_DIR_NAME,\n defaultValueIsSerializable: true,\n }),\n uplink: option({\n long: \"uplink\",\n type: optional(string),\n defaultValue: () => process.env.REGISTRY_UPLINK,\n defaultValueIsSerializable: true,\n }),\n s3Bucket: option({\n long: \"s3-bucket\",\n type: optional(string),\n defaultValue: () => process.env.S3_BUCKET,\n defaultValueIsSerializable: true,\n }),\n s3Endpoint: option({\n long: \"s3-endpoint\",\n type: optional(string),\n defaultValue: () => process.env.S3_ENDPOINT,\n defaultValueIsSerializable: true,\n }),\n s3Region: option({\n long: \"s3-region\",\n type: optional(string),\n defaultValue: () => process.env.S3_REGION,\n defaultValueIsSerializable: true,\n }),\n s3AccessKeyId: option({\n long: \"s3-access-key-id\",\n type: optional(string),\n defaultValue: () => process.env.S3_ACCESS_KEY_ID,\n defaultValueIsSerializable: true,\n }),\n s3SecretAccessKey: option({\n long: \"s3-secret-access-key\",\n type: optional(string),\n defaultValue: () => process.env.S3_SECRET_ACCESS_KEY,\n defaultValueIsSerializable: true,\n }),\n s3KeyPrefix: option({\n long: \"s3-key-prefix\",\n type: optional(string),\n defaultValue: () => process.env.S3_KEY_PREFIX,\n defaultValueIsSerializable: true,\n }),\n s3ForcePathStyle: flag({\n long: \"s3-force-path-style\",\n defaultValue: () => process.env.S3_FORCE_PATH_STYLE !== \"false\",\n defaultValueIsSerializable: true,\n }),\n webEnabled: flag({\n long: \"web-enabled\",\n defaultValue: () => process.env.REGISTRY_WEB !== \"false\",\n defaultValueIsSerializable: true,\n }),\n webhooks: option({\n long: \"webhook\",\n type: optional(string),\n description: \"Comma-separated webhook URLs to notify on publish\",\n defaultValue: () => process.env.REGISTRY_WEBHOOKS,\n defaultValueIsSerializable: true,\n }),\n },\n handler: async (args) => {\n console.log(args);\n\n try {\n await runRegistry(args);\n } catch (error) {\n console.error(\"Failed to start registry:\");\n console.error(error);\n process.exit(1);\n }\n },\n});\n\nconst registryCli = binary(registryCommand);\n\nawait run(registryCli, process.argv);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;ACOA,SAAgB,cAAc,GAAW,GAAmB;CAC1D,MAAM,CAAC,OAAO,QAAQ,EAAE,MAAM,KAAK,EAAE;CACrC,MAAM,CAAC,OAAO,QAAQ,EAAE,MAAM,KAAK,EAAE;CAErC,MAAM,SAAS,MAAM,MAAM,IAAI,CAAC,IAAI,OAAO;CAC3C,MAAM,SAAS,MAAM,MAAM,IAAI,CAAC,IAAI,OAAO;AAE3C,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,IAAI,OAAO,QAAQ,OAAO,OAAO,EAAE,KAAK;EAC/D,MAAM,KAAK,OAAO,MAAM;EACxB,MAAM,KAAK,OAAO,MAAM;AACxB,MAAI,OAAO,GAAI,QAAO,KAAK;;AAI7B,KAAI,CAAC,QAAQ,KAAM,QAAO;AAC1B,KAAI,QAAQ,CAAC,KAAM,QAAO;AAC1B,KAAI,QAAQ,KAAM,QAAO,OAAO,OAAO,KAAK,OAAO,OAAO,IAAI;AAE9D,QAAO;;;;;;;;;;;;ACTT,SAAgB,iBAAiB,MAG/B;AAGA,KAAI,KAAK,WAAW,IAAI,EAAE;EAExB,MAAM,SAAS,KAAK,YAAY,IAAI;AACpC,MAAI,SAAS,KAAK,WAAW,KAAK,QAAQ,IAAI,CAC5C,QAAO;GAAE,MAAM,KAAK,MAAM,GAAG,OAAO;GAAE,KAAK,KAAK,MAAM,SAAS,EAAE;GAAE;AAErE,SAAO;GAAE,MAAM;GAAM,KAAK,KAAA;GAAW;;CAEvC,MAAM,UAAU,KAAK,QAAQ,IAAI;AACjC,KAAI,UAAU,EACZ,QAAO;EAAE,MAAM,KAAK,MAAM,GAAG,QAAQ;EAAE,KAAK,KAAK,MAAM,UAAU,EAAE;EAAE;AAEvE,QAAO;EAAE,MAAM;EAAM,KAAK,KAAA;EAAW;;AAGvC,IAAa,WAAb,MAAsB;CACpB,mCAAmB,IAAI,KAA4B;CAEnD,YACE,aACA,cACA;AAFQ,OAAA,cAAA;AACA,OAAA,eAAA;;CAGV,MAAM,iBACJ,aACA,SACA,UACwB;EACxB,MAAM,aAAa,KAAK,KAAK,KAAK,cAAc,aAAa,QAAQ;EAGrE,MAAM,WAAW,MAAA,YAAkB,YAAY,SAAS;AACxD,MAAI,SAAU,QAAO;AAGrB,QAAM,MAAA,gBAAsB,aAAa,QAAQ;AAEjD,SAAO,MAAA,YAAkB,YAAY,SAAS;;CAGhD,aAAa,YAAoB,UAAiC;EAGhE,MAAM,aAAa;GACjB,KAAK,KAAK,YAAY,SAAS;GAC/B,KAAK,KAAK,YAAY,OAAO,SAAS;GACtC,KAAK,KAAK,YAAY,QAAQ,OAAO,SAAS;GAC9C,KAAK,KAAK,YAAY,QAAQ,SAAS;GACxC;AAED,OAAK,MAAM,aAAa,WACtB,KAAI,KAAK,WAAW,UAAU,IAAI,GAAG,WAAW,UAAU,CACxD,QAAO;AAGX,SAAO;;CAGT,OAAA,gBAAuB,aAAqB,SAAgC;EAC1E,MAAM,MAAM,GAAG,YAAY,GAAG;EAC9B,MAAM,WAAW,MAAA,gBAAsB,IAAI,IAAI;AAC/C,MAAI,SAAU,QAAO;EAErB,MAAM,UAAU,KAAK,eAAe,aAAa,QAAQ,CAAC,cAAc;AACtE,SAAA,gBAAsB,OAAO,IAAI;IACjC;AACF,QAAA,gBAAsB,IAAI,KAAK,QAAQ;AACvC,SAAO;;CAGT,uBAAuB,aAAoC;EACzD,MAAM,SAAS,KAAK,KAAK,KAAK,cAAc,YAAY;AACxD,MAAI;GAEF,MAAM,WADU,GAAG,YAAY,QAAQ,EAAE,eAAe,MAAM,CAAC,CAE5D,QAAQ,MAAM,EAAE,aAAa,CAAC,CAC9B,KAAK,MAAM,EAAE,KAAK;AACrB,OAAI,SAAS,WAAW,EAAG,QAAO;AAClC,YAAS,KAAK,cAAc;AAC5B,UAAO,SAAS,SAAS,SAAS;UAC5B;AACN,UAAO;;;;;;;;;CAUX,MAAM,eACJ,aACA,KACwB;AACxB,MAAI;GACF,MAAM,MAAM,GAAG,KAAK,YAAY,GAAG,mBAAmB,YAAY;GAClE,MAAM,MAAM,MAAM,MAAM,KAAK,EAC3B,SAAS,EAAE,QAAQ,oBAAoB,EACxC,CAAC;AACF,OAAI,CAAC,IAAI,GAAI,QAAO;GACpB,MAAM,WAAY,MAAM,IAAI,MAAM;GAClC,MAAM,WAAW,SAAS;GAG1B,MAAM,WAAW,SAAS;AAI1B,OAAI,KAAK;AAEP,QAAI,YAAY,OAAO,SAAU,QAAO;AAExC,QAAI,YAAY,OAAO,SAAU,QAAO,SAAS;AAEjD,WAAO;;AAGT,OAAI,CAAC,SAAU,QAAO;AAEtB,UAAO,SAAS,UAAU,OAAO,OAAO,SAAS,CAAC,MAAM;UAClD;AACN,UAAO;;;CAIX,MAAM,eAAe,aAAqB,SAAgC;EACxE,MAAM,YAAY,YAAY,WAAW,IAAI,GACzC,YAAY,MAAM,IAAI,CAAC,KACvB;EACJ,MAAM,aAAa,GAAG,KAAK,YAAY,GAAG,mBAAmB,YAAY,CAAC,KAAK,UAAU,GAAG,QAAQ;EAEpG,IAAI;AACJ,MAAI;AACF,SAAM,MAAM,MAAM,WAAW;AAC7B,OAAI,CAAC,IAAI,MAAM,CAAC,IAAI,KAAM;UACpB;AACN;;EAGF,MAAM,UAAU,KAAK,KAAK,KAAK,cAAc,aAAa,QAAQ;AAClE,KAAG,UAAU,SAAS,EAAE,WAAW,MAAM,CAAC;EAE1C,MAAM,UAAU,KAAK,KACnB,SACA,gBAAgB,OAAO,YAAY,CAAC,MACrC;AACD,MAAI;GACF,MAAM,aAAa,GAAG,kBAAkB,QAAQ;AAChD,SAAM,SAAS,SAAS,QAAQ,IAAI,KAAc,EAAE,WAAW;AAC/D,SAAM,QAAQ;IAAE,MAAM;IAAS,KAAK;IAAS,OAAO;IAAG,CAAC;YAChD;AACR,MAAG,OAAO,SAAS,EAAE,OAAO,MAAM,CAAC;;;CAIvC,WAAW,aAA2B;EACpC,MAAM,WAAW,KAAK,KAAK,KAAK,cAAc,YAAY;AAC1D,MAAI,CAAC,KAAK,WAAW,SAAS,CAAE;AAChC,KAAG,OAAO,UAAU;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC;;;CAIvD,iBAAiB,aAAqB,aAA2B;EAC/D,MAAM,SAAS,KAAK,KAAK,KAAK,cAAc,YAAY;AACxD,MAAI;GACF,MAAM,UAAU,GAAG,YAAY,QAAQ,EAAE,eAAe,MAAM,CAAC;AAC/D,QAAK,MAAM,SAAS,QAClB,KAAI,MAAM,aAAa,IAAI,MAAM,SAAS,aAAa;IACrD,MAAM,MAAM,KAAK,KAAK,QAAQ,MAAM,KAAK;AACzC,QAAI,KAAK,WAAW,IAAI,CACtB,IAAG,OAAO,KAAK;KAAE,WAAW;KAAM,OAAO;KAAM,CAAC;;UAIhD;;CAKV,WAAmB,UAA2B;EAC5C,MAAM,WAAW,KAAK,QAAQ,SAAS;EACvC,MAAM,YAAY,KAAK,QAAQ,KAAK,aAAa;AACjD,SAAO,SAAS,WAAW,YAAY,KAAK,IAAI,IAAI,aAAa;;;;;ACvMrE,SAAS,aAAa,KAA8B;CAClD,MAAM,aAAa;EACjB,KAAK,KAAK,KAAK,2BAA2B;EAC1C,KAAK,KAAK,KAAK,OAAO,2BAA2B;EACjD,KAAK,KAAK,KAAK,QAAQ,2BAA2B;EACnD;AACD,MAAK,MAAM,gBAAgB,WACzB,KAAI;EACF,MAAM,MAAM,GAAG,aAAa,cAAc,QAAQ;AAClD,SAAO,KAAK,MAAM,IAAI;SAChB;AAIV,QAAO;;AAGT,SAAS,uBAAuB,KAAiC;AAC/D,KAAI;EACF,MAAM,MAAM,GAAG,aAAa,KAAK,KAAK,KAAK,eAAe,EAAE,QAAQ;EACpE,MAAM,MAAM,KAAK,MAAM,IAAI;AAC3B,SAAO,OAAO,IAAI,YAAY,WAAW,IAAI,UAAU,KAAA;SACjD;AACN;;;AAIJ,SAAS,oBAAoB,QAA+B;CAC1D,IAAI;AACJ,KAAI;AACF,YAAU,GAAG,YAAY,QAAQ,EAAE,eAAe,MAAM,CAAC;SACnD;AACN,SAAO;;CAET,MAAM,WAAW,QAAQ,QAAQ,MAAM,EAAE,aAAa,CAAC,CAAC,KAAK,MAAM,EAAE,KAAK;AAC1E,KAAI,SAAS,WAAW,EAAG,QAAO;AAClC,UAAS,KAAK,cAAc;AAC5B,QAAO,KAAK,KAAK,QAAQ,SAAS,SAAS,SAAS,GAAG;;AAGzD,SAAgB,YACd,cACA,MACA,SACoB;CACpB,MAAM,SAAS,KAAK,KAAK,cAAc,KAAK;CAI5C,MAAM,eAHa,UACf,KAAK,KAAK,QAAQ,QAAQ,GAC1B,oBAAoB,OAAO,KACG;CAClC,MAAM,WAAW,aAAa,YAAY;AAE1C,KAAI,CAAC,SACH,QAAO;AAET,QAAO;EACL,MAAM,SAAS,QAAQ;EACvB,MAAM,UAAU;EAChB;EACA,eAAe,6BAA6B,SAAS;EACrD,SAAS,uBAAuB,YAAY;EAC7C;;AAGH,SAAS,6BAA6B,UAAuC;AAC3E,KAAI,CAAC,SAAU,QAAO,EAAE;CAExB,MAAM,gBAA0B,EAAE;CAClC,MAAM,EAAE,MAAM,gBAAgB,SAAS,cAAc;AAErD,KAAI,MAAM,OACR,eAAc,KAAK,4BAA4B;AAEjD,eAAc,KACZ,IAAI,kBAAkB,EAAE,EAAE,KAAK,OAAO,GAAG,GAAG,EAC5C,IAAI,WAAW,EAAE,EACd,SAAS,MAAM,EAAE,cAAc,CAC/B,QAAQ,OAAO,OAAO,KAAA,EAAU,EACnC,IAAI,aAAa,EAAE,EAChB,SAAS,MAAM,EAAE,cAAc,CAC/B,QAAQ,OAAO,OAAO,KAAA,EAAU,CACpC;AAED,QAAO;;AAGT,SAAgB,aAAa,cAAqC;CAChE,MAAM,SAAS,KAAK,QAAQ,aAAa;CACzC,MAAM,WAA0B,EAAE;CAElC,IAAI;AACJ,KAAI;AACF,YAAU,GAAG,YAAY,QAAQ,EAAE,eAAe,MAAM,CAAC;SACnD;AACN,SAAO;;AAGT,MAAK,MAAM,SAAS,SAAS;AAC3B,MAAI,CAAC,MAAM,aAAa,CAAE;AAE1B,MAAI,MAAM,KAAK,WAAW,IAAI,EAAE;GAC9B,MAAM,WAAW,KAAK,KAAK,QAAQ,MAAM,KAAK;GAC9C,IAAI;AACJ,OAAI;AACF,oBAAgB,GAAG,YAAY,UAAU,EAAE,eAAe,MAAM,CAAC;YAC1D,OAAO;AACd,YAAQ,IAAI,MAAM;AAClB;;AAEF,QAAK,MAAM,eAAe,eAAe;AACvC,QAAI,CAAC,YAAY,aAAa,CAAE;IAChC,MAAM,UAAU,GAAG,MAAM,KAAK,GAAG,YAAY;IAC7C,MAAM,SAAS,KAAK,KAAK,UAAU,YAAY,KAAK;IAEpD,MAAM,cADa,oBAAoB,OAAO,IACZ;IAClC,MAAM,WAAW,aAAa,YAAY;IAC1C,MAAM,OAAO,UAAU,QAAQ;AAC/B,aAAS,KAAK;KACZ;KACA,MAAM,UAAU;KAChB;KACA,eAAe,6BAA6B,SAAS;KACrD,SAAS,uBAAuB,YAAY;KAC7C,CAAC;;SAEC;GACL,MAAM,SAAS,KAAK,KAAK,QAAQ,MAAM,KAAK;GAE5C,MAAM,cADa,oBAAoB,OAAO,IACZ;GAClC,MAAM,WAAW,aAAa,YAAY;GAC1C,MAAM,OAAO,UAAU,QAAQ,MAAM;AACrC,YAAS,KAAK;IACZ;IACA,MAAM,UAAU,MAAM;IACtB;IACA,eAAe,6BAA6B,SAAS;IACrD,SAAS,uBAAuB,YAAY;IAC7C,CAAC;;;AAIN,QAAO;;AAGT,SAAgB,2BACd,aACA,cACe;AAGf,QAFoB,aAAa,YAAY,CAE1B,QAAQ,QAAQ;AACjC,MAAI,CAAC,IAAI,UAAU,eACjB,QAAO;AAET,SAAO,IAAI,SAAS,eAAe,MAAM,OAAO,GAAG,OAAO,aAAa;GACvE;;;;AC9IJ,MAAM,aAAqC;CACzC,OAAO;CACP,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,SAAS;CACT,QAAQ;CACR,SAAS;CACT,QAAQ;CACT;AAED,SAAS,eAAe,UAA0B;AAEhD,QAAO,WADK,KAAK,QAAQ,SAAS,CAAC,aAAa,KACtB;;AAG5B,SAAgB,uBACd,QACA,KACA,UACQ;CACR,MAAM,MAAM,IAAI,SACd,oBAAoB,OAAO,QAC3B,OAAO,aACR;CACD,MAAM,SAAS,QAAQ;AAGvB,QAAO,KAAK,MAAe,KAAe,SAAuB;AAC/D,MAAI,UAAU,+BAA+B,IAAI;AACjD,QAAM;GACN;AAGF,QAAO,IAAI,cAAc,MAAe,QAAkB;AACxD,MAAI,UAAU,IAAI;GAClB;AAGF,QAAO,IAAI,gBAAgB,MAAe,QAAkB;AAC1D,MAAI,KAAK,SAAS,aAAa,CAAC;GAChC;AAEF,QAAO,KAAK,eAAe,QAAQ,MAAM,GAAG,KAAc,QAAkB;EAC1E,MAAM,EAAE,UAAU,YAAY,IAAI;AAIlC,MAAI,CAAC,UAAU;AACb,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,oCAAoC,CAAC;AACnE;;AAEF,WAAS,WAAW;GAAE;GAAU;GAAS,CAAC;AAC1C,MAAI,OAAO,IAAI,CAAC,KAAK;GAAE;GAAU;GAAS,CAAC;GAC3C;AAEF,QAAO,OACL,eACA,QAAQ,MAAM,GACb,KAAc,QAAkB;EAC/B,MAAM,EAAE,aAAa,IAAI;AACzB,MAAI,CAAC,UAAU;AACb,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,oCAAoC,CAAC;AACnE;;AAGF,MAAI,CADY,SAAS,cAAc,SAAS,EAClC;AACZ,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,qBAAqB,CAAC;AACpD;;AAEF,MAAI,OAAO,IAAI,CAAC,KAAK;GAExB;AAGD,QAAO,IAAI,cAAc,KAAc,QAAkB;EACvD,MAAM,WAAW,aAAa,OAAO,aAAa;EAClD,MAAM,eAAe,IAAI,MAAM;AAC/B,MAAI,cAAc;GAChB,MAAM,WAAW,SAAS,QAAQ,QAChC,IAAI,UAAU,gBAAgB,MAAM,MAAM,EAAE,OAAO,aAAa,CACjE;AACD,OAAI,KAAK,SAAS;AAClB;;AAEF,MAAI,KAAK,SAAS;GAClB;AAGF,QAAO,IAAI,+BAA+B,KAAc,QAAkB;EACxE,MAAM,eAAe,IAAI,MAAM;AAE/B,MAAI,OAAO,iBAAiB,YAAY,CAAC,cAAc;AACrD,OAAI,OAAO,IAAI,CAAC,KAAK,EAAE,OAAO,0CAA0C,CAAC;AACzE;;EAOF,MAAM,eAJW,2BACf,OAAO,cACP,aACD,CAC6B,KAAK,QAAQ,IAAI,KAAK;AACpD,MAAI,KAAK,aAAa;GACtB;AAGF,QAAO,IAAI,mBAAmB,OAAO,KAAc,QAAkB;EAEnE,MAAM,EAAE,MAAM,QAAQ,iBADT,IAAI,OAAoC,KAAK,KAAK,IAAI,CACxB;EAC3C,MAAM,UACH,MAAM,IAAI,eAAe,MAAM,IAAI,IAAK,IAAI,uBAAuB,KAAK;EAC3E,MAAM,MAAM,YAAY,OAAO,cAAc,MAAM,WAAW,KAAA,EAAU;AACxE,MAAI,CAAC,KAAK;AACR,OAAI,OAAO,IAAI,CAAC,KAAK,oBAAoB;AACzC;;AAEF,MAAI,KAAK,IAAI;GACb;AAGF,QAAO,IAAI,gBAAgB,OAAO,KAAc,QAAkB;EAEhE,MAAM,WADS,IAAI,OAAoC,KAChC,KAAK,IAAI;EAGhC,IAAI;EACJ,IAAI;AAEJ,MAAI,SAAS,WAAW,IAAI,EAAE;GAE5B,MAAM,WAAW,SAAS,MAAM,IAAI;AACpC,OAAI,SAAS,SAAS,GAAG;AACvB,QAAI,OAAO,IAAI,CAAC,KAAK,uBAAuB;AAC5C;;AAEF,iBAAc,GAAG,SAAS,GAAG,GAAG,SAAS;AACzC,cAAW,SAAS,MAAM,EAAE,CAAC,KAAK,IAAI,IAAI;SACrC;GAEL,MAAM,WAAW,SAAS,MAAM,IAAI;AACpC,iBAAc,SAAS;AACvB,cAAW,SAAS,MAAM,EAAE,CAAC,KAAK,IAAI,IAAI;;EAG5C,MAAM,EAAE,MAAM,aAAa,QAAQ,iBAAiB,YAAY;EAChE,MAAM,UACH,MAAM,IAAI,eAAe,aAAa,IAAI,IAC3C,IAAI,uBAAuB,YAAY;AACzC,MAAI,CAAC,SAAS;AACZ,OAAI,OAAO,IAAI,CAAC,KAAK,iBAAiB;AACtC;;EAGF,MAAM,WAAW,MAAM,IAAI,iBAAiB,aAAa,SAAS,SAAS;AAC3E,MAAI,CAAC,UAAU;AACb,OAAI,OAAO,IAAI,CAAC,KAAK,iBAAiB;AACtC;;AAGF,MAAI,UAAU,gBAAgB,eAAe,SAAS,CAAC;EACvD,MAAM,UAAU,GAAG,aAAa,SAAS;AACzC,MAAI,KAAK,QAAQ;GACjB;AAEF,QAAO;;AAGT,SAAgB,kBACd,QACA,eACA;CACA,MAAM,MAAM,IAAI,SACd,oBAAoB,OAAO,QAC3B,OAAO,aACR;AAED,SAAQ,KAAc,KAAe,SAAuB;AAE1D,MAAI,IAAI,WAAW,OAAO;AACxB,SAAM;AACN;;EAGF,MAAM,cAAc,IAAI,IAAI,KAAK,IAAI;AACrC,MAAI,MAAM,SAER,OACA,UACA,IACA;GACA,MAAM,UAAU,IAAI,KAAK,QAAQ,OAAO,GAAG;AAC3C,OACE,IAAI,aAAa,OACjB,IAAI,cAAc,OAClB,CAAC,WACD,QAAQ,WAAW,IAAI,CAEvB,QAAO,YAAY,OAAO,UAA4B,GAAG;GAE3D,MAAM,cAAc,mBAAmB,QAAQ;GAC/C,MAAM,cAAe,IAAI,KACtB;GACH,MAAM,WAAW,OAAO,KAAK,YAAY;GACzC,MAAM,UAAU,SAAS,GAAG,EAAE;AAC9B,OAAI,CAAC,SAAS;AACZ,YAAQ,MAAM,mCAAmC,cAAc;AAC/D,WAAO,YAAY,OAAO,UAA4B,GAAG;;AAE3D,OAAI,SAAS,SAAS,EACpB,SAAQ,KACN,8CAA8C,YAAY,IAAI,KAAK,UAAU,SAAS,GACvF;AAGH,OACG,eAAe,aAAa,QAAQ,CACpC,WAAW;AACV,kBAAc,cAAc;KAAE;KAAa;KAAS,CAAC;KACrD,CACD,OAAO,QAAQ;AACd,YAAQ,MACN,gCAAgC,YAAY,iBAC5C,IACD;KACD;AAEJ,UAAO,YAAY,OAAO,UAA4B,GAAG;;AAG3D,QAAM;;;;;ACtPV,IAAa,sBAAb,MAAgE;CAC9D;CAEA,YAAY,UAAiC;AAC3C,QAAA,WAAiB;;CAGnB,cAAc,OAA2B;AACvC,OAAK,MAAM,WAAW,MAAA,SACpB,SAAQ,cAAc,MAAM;;;;;ACRlC,IAAa,aAAb,MAAuD;CACrD,2BAAW,IAAI,KAAe;CAE9B,UAAU,KAAqB;AAC7B,MAAI,UAAU,KAAK;GACjB,gBAAgB;GAChB,iBAAiB;GACjB,YAAY;GACZ,+BAA+B;GAChC,CAAC;AACF,MAAI,MAAM,iCAAiC;AAE3C,QAAA,QAAc,IAAI,IAAI;AACtB,MAAI,GAAG,eAAe;AACpB,SAAA,QAAc,OAAO,IAAI;IACzB;;CAGJ,cAAc,OAA2B;EACvC,MAAM,UAAU,yBAAyB,KAAK,UAAU,MAAM,CAAC;AAC/D,OAAK,MAAM,UAAU,MAAA,QACnB,KAAI;AACF,UAAO,MAAM,QAAQ;WACd,KAAK;AACZ,WAAQ,MAAM,uCAAuC,IAAI;AACzD,SAAA,QAAc,OAAO,OAAO;;;;;;ACvBpC,MAAM,gBAAgB;AAEtB,IAAa,iBAAb,MAA2D;CACzD;CACA;CACA;CAEA,YAAY,aAAqB,QAAuB;AACtD,QAAA,cAAoB;AACpB,QAAA,aAAmB,QAAQ,YAAY,EAAE;AACzC,QAAA,UAAgB,MAAA,MAAY;;CAG9B,cAA+B;AAC7B,SAAO,CAAC,GAAG,MAAA,YAAkB,GAAG,MAAA,QAAc;;CAGhD,WAAW,SAA8B;AAIvC,MAHe,KAAK,aAAa,CAAC,MAC/B,MAAM,EAAE,aAAa,QAAQ,SAC/B,CACW;AACZ,QAAA,QAAc,KAAK,QAAQ;AAC3B,QAAA,MAAY;;CAGd,cAAc,UAA2B;EACvC,MAAM,SAAS,MAAA,QAAc;AAC7B,QAAA,UAAgB,MAAA,QAAc,QAAQ,MAAM,EAAE,aAAa,SAAS;AACpE,MAAI,MAAA,QAAc,WAAW,OAAQ,QAAO;AAC5C,QAAA,MAAY;AACZ,SAAO;;CAGT,cAAc,OAA2B;AACvC,OAAK,MAAM,WAAW,KAAK,aAAa,EAAE;GACxC,MAAM,UAAkC;IACtC,gBAAgB;IAChB,GAAG,QAAQ;IACZ;AAED,SAAM,QAAQ,UAAU;IACtB,QAAQ;IACR;IACA,MAAM,KAAK,UAAU,MAAM;IAC5B,CAAC,CAAC,OAAO,QAAiB;AACzB,YAAQ,MAAM,yBAAyB,QAAQ,SAAS,WAAW,IAAI;KACvE;;;CAIN,YAAoB;AAClB,SAAO,KAAK,KAAK,MAAA,aAAmB,cAAc;;CAGpD,QAAyB;AACvB,MAAI;GACF,MAAM,MAAM,GAAG,aAAa,MAAA,UAAgB,EAAE,QAAQ;AACtD,UAAO,KAAK,MAAM,IAAI;UAChB;AACN,UAAO,EAAE;;;CAIb,QAAc;AACZ,KAAG,UAAU,MAAA,aAAmB,EAAE,WAAW,MAAM,CAAC;AACpD,KAAG,cAAc,MAAA,UAAgB,EAAE,KAAK,UAAU,MAAA,SAAe,MAAM,EAAE,CAAC;;;;;ACpE9E,SAAgB,qBAAqB,QAAwB;CAC3D,MAAM,eAAe,KAAK,KAAK,OAAO,aAAa,WAAW;CAE9D,MAAM,YAAY,OAAO,UAAU;CAEnC,MAAM,OAAgC;EACpC,SAAS,OAAO;EAChB,WAAW;EACX,MAAM,EACJ,UAAU,EACR,MAAM,cACP,EACF;EACD,SAAS,EACP,OAAO;GACL,KAAK;GACL,QAAQ;GACR,SAAS;GACT,OAAO;GACR,EACF;EACD,UAAU;GACR,oBAAoB;IAClB,QAAQ;IACR,SAAS;IACT,WAAW;IACX,OAAO;IACR;GACD,MAAM;IACJ,QAAQ;IACR,SAAS;IACT,WAAW;IACX,OAAO;IACR;GACF;EACD,KAAK;GACH,QAAQ,OAAO,eAAe;GAC9B,OAAO;GACP,MAAM;GACN,SAAS;GACT,eAAe;GACf,UAAU;GACX;EACD,QAAQ,EACN,kBAAkB,IACnB;EACD,KAAK;GACH,MAAM;GACN,QAAQ;GACR,OAAO;GACR;EACD,eAAe,OAAO,eAAe;EACtC;AAED,KAAI,OAAO,GACT,MAAK,QAAQ,EACX,kBAAkB;EAChB,QAAQ,OAAO,GAAG;EAClB,UAAU,OAAO,GAAG;EACpB,QAAQ,OAAO,GAAG;EAClB,kBAAkB,OAAO,GAAG,oBAAoB;EAChD,GAAI,OAAO,GAAG,aAAa,EAAE,WAAW,OAAO,GAAG,WAAW;EAC7D,GAAI,OAAO,GAAG,eAAe,EAAE,aAAa,OAAO,GAAG,aAAa;EACnE,GAAI,OAAO,GAAG,mBAAmB,EAC/B,iBAAiB,OAAO,GAAG,iBAC5B;EACF,EACF;AAGH,QAAO;;;;AC5DT,eAAe,WAAW,KAA8B;AACtD,KAAI,KAAK,WAAW,IAAI,EAAE;AACxB,QAAM,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;AACrC,SAAO;;CAET,MAAM,QAAQ,MAAM,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AACtD,KAAI,CAAC,OAAO;AACV,QAAM,MAAM,KAAK,EAAE,WAAW,MAAM,CAAC;AACrC,SAAO;;AAET,QAAO;;AAGT,eAAsB,YAAY,MAA2B;CAC3D,MAAM,EACJ,MACA,YACA,aACA,QACA,YACA,UACA,eACA,UACA,YACA,kBACA,aACA,UACA,sBACE;CACJ,MAAM,cAAc,MAAM,WAAW,WAAW;CAChD,MAAM,eAAe,MAAM,WAAW,YAAY;AAElD,SAAQ,IAAI;EACV;EACA;EACD,CAAC;CAEF,MAAM,iBAAiB,UACnB,MAAM,IAAI,CACX,KAAK,QAAQ,IAAI,MAAM,CAAC,CACxB,OAAO,QAAQ,CACf,KAAK,cAAc,EAAE,UAAU,EAAE;CAEpC,MAAM,SAAyB;EAC7B;EACA;EACA;EACA;EACA;EACA,GAAI,gBAAgB,UAAU,EAC5B,QAAQ,EAAE,UAAU,gBAAgB,EACrC;EACD,GAAI,YACF,cACA,YAAY,EACV,IAAI;GACF,QAAQ;GACR,UAAU;GACV,QAAQ;GACR,aAAa;GACb,iBAAiB;GACjB,WAAW;GACX;GACD,EACF;EACJ;AAED,OAAM,MAAM,aAAa,EAAE,WAAW,MAAM,CAAC;AAC7C,OAAM,MAAM,cAAc,EAAE,WAAW,MAAM,CAAC;CAM9C,MAAM,oBADmB,MAAM,UAHP,qBAAqB,OAAO,CAGK,EAChB,UAAU,UAAU,CAAC;CAI9D,MAAM,MAAM,SAAS;CAErB,MAAM,aAAa,IAAI,YAAY;CACnC,MAAM,iBAAiB,IAAI,eAAe,OAAO,aAAa,OAAO,OAAO;CAC5E,MAAM,gBAAgB,IAAI,oBAAoB,CAAC,YAAY,eAAe,CAAC;CAG3E,MAAM,YAAY,MAAM,OAAO,UAAU,EAAE,MAAM,aAAa,CAAC;AAC/D,KAAI,UACF,KAAI,IAAI,aAAa,QAAQ,OAAO,UAAU,CAAC;AAIjD,KAAI,IAAI,uBAAuB,QAAQ,YAAY,eAAe,CAAC;AACnE,KAAI,IAAI,kBAAkB,QAAQ,cAAc,CAAC;AAGjD,KAAI,KAAK,KAAK,QAAQ,iBAAiB,KAAK,IAAI,CAAC;AAcjD,QAZe,IAAI,OAAO,YAAY;AACpC,UAAQ,IAAI,mDAAmD,OAAO;AACtE,UAAQ,IAAI,gCAAgC,KAAK,SAAS;AAC1D,UAAQ,IAAI,gCAAgC,KAAK,WAAW;AAC5D,UAAQ,IAAI,gCAAgC,KAAK,GAAG;AACpD,UAAQ,IAAI,eAAe,cAAc;AACzC,UAAQ,IAAI,gBAAgB,eAAe;AAC3C,MAAI,OAAO,GACT,SAAQ,IAAI,eAAe,OAAO,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS;GAEtE;;;;ACvGJ,MAAa,kBAAkB,QAAQ;CACrC,MAAM;CACN,MAAM;EACJ,MAAM,OAAO;GACX,MAAM;GACN,MAAM;GACN,oBAAoB,OAAO,QAAQ,IAAI,KAAK,IAAA;GAC5C,4BAA4B;GAC7B,CAAC;EACF,YAAY,OAAO;GACjB,MAAM;GACN,MAAM;GACN,oBACE,QAAQ,IAAI,oBAAA;GACd,4BAA4B;GAC7B,CAAC;EACF,aAAa,OAAO;GAClB,MAAM;GACN,MAAM;GACN,oBACE,QAAQ,IAAI,sBAAA;GACd,4BAA4B;GAC7B,CAAC;EACF,QAAQ,OAAO;GACb,MAAM;GACN,MAAM,SAAS,OAAO;GACtB,oBAAoB,QAAQ,IAAI;GAChC,4BAA4B;GAC7B,CAAC;EACF,UAAU,OAAO;GACf,MAAM;GACN,MAAM,SAAS,OAAO;GACtB,oBAAoB,QAAQ,IAAI;GAChC,4BAA4B;GAC7B,CAAC;EACF,YAAY,OAAO;GACjB,MAAM;GACN,MAAM,SAAS,OAAO;GACtB,oBAAoB,QAAQ,IAAI;GAChC,4BAA4B;GAC7B,CAAC;EACF,UAAU,OAAO;GACf,MAAM;GACN,MAAM,SAAS,OAAO;GACtB,oBAAoB,QAAQ,IAAI;GAChC,4BAA4B;GAC7B,CAAC;EACF,eAAe,OAAO;GACpB,MAAM;GACN,MAAM,SAAS,OAAO;GACtB,oBAAoB,QAAQ,IAAI;GAChC,4BAA4B;GAC7B,CAAC;EACF,mBAAmB,OAAO;GACxB,MAAM;GACN,MAAM,SAAS,OAAO;GACtB,oBAAoB,QAAQ,IAAI;GAChC,4BAA4B;GAC7B,CAAC;EACF,aAAa,OAAO;GAClB,MAAM;GACN,MAAM,SAAS,OAAO;GACtB,oBAAoB,QAAQ,IAAI;GAChC,4BAA4B;GAC7B,CAAC;EACF,kBAAkB,KAAK;GACrB,MAAM;GACN,oBAAoB,QAAQ,IAAI,wBAAwB;GACxD,4BAA4B;GAC7B,CAAC;EACF,YAAY,KAAK;GACf,MAAM;GACN,oBAAoB,QAAQ,IAAI,iBAAiB;GACjD,4BAA4B;GAC7B,CAAC;EACF,UAAU,OAAO;GACf,MAAM;GACN,MAAM,SAAS,OAAO;GACtB,aAAa;GACb,oBAAoB,QAAQ,IAAI;GAChC,4BAA4B;GAC7B,CAAC;EACH;CACD,SAAS,OAAO,SAAS;AACvB,UAAQ,IAAI,KAAK;AAEjB,MAAI;AACF,SAAM,YAAY,KAAK;WAChB,OAAO;AACd,WAAQ,MAAM,4BAA4B;AAC1C,WAAQ,MAAM,MAAM;AACpB,WAAQ,KAAK,EAAE;;;CAGpB,CAAC;AAIF,MAAM,IAFc,OAAO,gBAAgB,EAEpB,QAAQ,KAAK"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@powerhousedao/registry",
|
|
3
|
-
"version": "6.0.0-dev.
|
|
3
|
+
"version": "6.0.0-dev.183",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"tar": "^7.5.11",
|
|
26
26
|
"verdaccio": "^6.5.0",
|
|
27
27
|
"verdaccio-aws-s3-storage": "^10.4.0",
|
|
28
|
-
"@powerhousedao/shared": "6.0.0-dev.
|
|
28
|
+
"@powerhousedao/shared": "6.0.0-dev.183"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"tsdown": "0.21.
|
|
31
|
+
"tsdown": "0.21.1",
|
|
32
32
|
"@types/express": "^5.0.6",
|
|
33
33
|
"@types/node": "25.2.3",
|
|
34
34
|
"@types/supertest": "^6.0.2",
|