From f7a0aa17b9a198dacd40d65644a3cdd7231d85cc Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Thu, 17 Apr 2025 15:24:25 -0500 Subject: [PATCH 01/18] testing --- .gitignore | 3 +- jsrepo-package.tar.gz | Bin 0 -> 18893 bytes package.json | 17 ++++++++++-- pnpm-lock.yaml | 51 ++++++++++++++++++++++++++++++++++ src/cli.ts | 1 + src/commands/index.ts | 17 ++++++------ src/commands/publish.ts | 60 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 138 insertions(+), 11 deletions(-) create mode 100644 jsrepo-package.tar.gz create mode 100644 src/commands/publish.ts diff --git a/.gitignore b/.gitignore index 436a245c..10d98241 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ node_modules dist # testing -temp-test \ No newline at end of file +temp-test +temp-registry \ No newline at end of file diff --git a/jsrepo-package.tar.gz b/jsrepo-package.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..15b204003c932c441ef4ed7533a4a9e33c59a5b9 GIT binary patch literal 18893 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 literal 0 HcmV?d00001 diff --git a/package.json b/package.json index 9baec19e..e29eea0b 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,16 @@ "bugs": { "url": "https://github.com/jsrepojs/jsrepo/issues" }, - "keywords": ["repo", "cli", "svelte", "vue", "typescript", "javascript", "shadcn", "registry"], + "keywords": [ + "repo", + "cli", + "svelte", + "vue", + "typescript", + "javascript", + "shadcn", + "registry" + ], "type": "module", "packageManager": "pnpm@10.8.1", "exports": { @@ -26,7 +35,10 @@ }, "bin": "./dist/index.js", "main": "./dist/index.js", - "files": ["./schemas/**/*", "dist/**/*"], + "files": [ + "./schemas/**/*", + "dist/**/*" + ], "scripts": { "start": "tsup --silent && node ./dist/index.js", "build": "tsup", @@ -79,6 +91,7 @@ "semver": "^7.7.1", "sisteransi": "^1.0.5", "svelte": "^5.25.9", + "tar": "^7.4.3", "valibot": "1.0.0", "validate-npm-package-name": "^6.0.0", "vue": "^3.5.13" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 392a1115..5d5928c4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -95,6 +95,9 @@ importers: svelte: specifier: ^5.25.9 version: 5.27.0 + tar: + specifier: ^7.4.3 + version: 7.4.3 valibot: specifier: 1.0.0 version: 1.0.0(typescript@5.8.3) @@ -460,6 +463,10 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} @@ -1074,6 +1081,10 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} @@ -1549,6 +1560,15 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} + minizlib@3.0.2: + resolution: {integrity: sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==} + engines: {node: '>= 18'} + + mkdirp@3.0.1: + resolution: {integrity: sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==} + engines: {node: '>=10'} + hasBin: true + mlly@1.7.4: resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} @@ -1910,6 +1930,10 @@ packages: resolution: {integrity: sha512-Uai13Ydt1ZE+bUHme6b9U38PCYVNCqBRoBMkUKbFbKiD7kHWjdUUrklYAQZJxyKK81qII4mrBwe/YmvEMSlC9w==} engines: {node: '>=18'} + tar@7.4.3: + resolution: {integrity: sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==} + engines: {node: '>=18'} + term-size@2.2.1: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} @@ -2193,6 +2217,10 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + yoctocolors@2.1.1: resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} engines: {node: '>=18'} @@ -2537,6 +2565,10 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 @@ -3167,6 +3199,8 @@ snapshots: dependencies: readdirp: 4.1.2 + chownr@3.0.0: {} + ci-info@3.9.0: {} cli-boxes@3.0.0: {} @@ -3617,6 +3651,12 @@ snapshots: minipass@7.1.2: {} + minizlib@3.0.2: + dependencies: + minipass: 7.1.2 + + mkdirp@3.0.1: {} + mlly@1.7.4: dependencies: acorn: 8.14.1 @@ -3977,6 +4017,15 @@ snapshots: magic-string: 0.30.17 zimmerframe: 1.1.2 + tar@7.4.3: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.0.2 + mkdirp: 3.0.1 + yallist: 5.0.0 + term-size@2.2.1: {} thenify-all@1.6.0: @@ -4225,6 +4274,8 @@ snapshots: wrappy@1.0.2: {} + yallist@5.0.0: {} + yoctocolors@2.1.1: {} zimmerframe@1.1.2: {} diff --git a/src/cli.ts b/src/cli.ts index d1c2428b..77a86c3d 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -11,6 +11,7 @@ const cli = program .addCommand(commands.build) .addCommand(commands.exec) .addCommand(commands.init) + .addCommand(commands.publish) .addCommand(commands.test) .addCommand(commands.update); diff --git a/src/commands/index.ts b/src/commands/index.ts index 7c4ec01c..03736c50 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1,9 +1,10 @@ -import { add } from './add'; -import { auth } from './auth'; -import { build } from './build'; -import { exec } from './exec'; -import { init } from './init'; -import { test } from './test'; -import { update } from './update'; +import { add } from "./add"; +import { auth } from "./auth"; +import { build } from "./build"; +import { exec } from "./exec"; +import { init } from "./init"; +import { publish } from "./publish"; +import { test } from "./test"; +import { update } from "./update"; -export { add, auth, build, exec, init, test, update }; +export { add, auth, build, exec, init, publish, test, update }; diff --git a/src/commands/publish.ts b/src/commands/publish.ts new file mode 100644 index 00000000..15f1ce2b --- /dev/null +++ b/src/commands/publish.ts @@ -0,0 +1,60 @@ +import { cancel, confirm, isCancel, log, outro, password, select, text } from "@clack/prompts"; +import color from "chalk"; +import { Command } from "commander"; +import * as v from "valibot"; +import { intro, spinner } from "../utils/prompts"; +import path from "pathe"; +import * as tar from "tar"; +import fs from "node:fs"; +import fetch from "node-fetch"; + +const schema = v.object({ + cwd: v.string(), +}); + +type Options = v.InferInput; + +export const publish = new Command("publish") + .description("Publish a registry to jsrepo.com.") + .option("--cwd ", "The current working directory.", process.cwd()) + .action(async (opts) => { + const options = v.parse(schema, opts); + + await intro(); + + await _publish(options); + + outro(color.green("All done!")); + }); + +async function _publish(options: Options) { + const dest = "jsrepo-package.tar.gz"; + + const files = fs.readdirSync(path.resolve(options.cwd, 'temp-registry')); + + await tar.create( + { + z: true, + cwd: path.resolve(options.cwd, 'temp-registry'), + file: dest, + }, + files + ); + + const tarBuffer = fs.readFileSync(path.resolve(options.cwd, dest)); + + const response = await fetch("http://localhost:5173/api/publish", { + body: tarBuffer, + headers: { + "content-type": "application/gzip", + "content-encoding": "gzip", + }, + method: "POST", + }); + + if (!response.ok) { + console.error(`${response.status} ${response.statusText}`); + } + + console.log('Completed publish!'); +} From 755c64aa82ef4c9b41477104db6e45bd1d5fd00c Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Fri, 18 Apr 2025 14:10:42 -0500 Subject: [PATCH 02/18] jsrepo provider --- package.json | 16 +--- schemas/registry-config.json | 16 ++++ src/commands/add.ts | 6 +- src/commands/build.ts | 4 + src/commands/exec.ts | 6 +- src/commands/index.ts | 16 ++-- src/commands/publish.ts | 51 ++++++------ src/commands/test.ts | 8 +- src/types.ts | 3 + src/utils/config.ts | 3 + src/utils/manifest.ts | 1 + src/utils/registry-providers/index.ts | 5 +- src/utils/registry-providers/internal.ts | 12 ++- src/utils/registry-providers/jsrepo.ts | 101 +++++++++++++++++++++++ tests/providers.test.ts | 85 +++++++++++++++++++ 15 files changed, 277 insertions(+), 56 deletions(-) create mode 100644 src/utils/registry-providers/jsrepo.ts diff --git a/package.json b/package.json index e29eea0b..cffdba7a 100644 --- a/package.json +++ b/package.json @@ -15,16 +15,7 @@ "bugs": { "url": "https://github.com/jsrepojs/jsrepo/issues" }, - "keywords": [ - "repo", - "cli", - "svelte", - "vue", - "typescript", - "javascript", - "shadcn", - "registry" - ], + "keywords": ["repo", "cli", "svelte", "vue", "typescript", "javascript", "shadcn", "registry"], "type": "module", "packageManager": "pnpm@10.8.1", "exports": { @@ -35,10 +26,7 @@ }, "bin": "./dist/index.js", "main": "./dist/index.js", - "files": [ - "./schemas/**/*", - "dist/**/*" - ], + "files": ["./schemas/**/*", "dist/**/*"], "scripts": { "start": "tsup --silent && node ./dist/index.js", "build": "tsup", diff --git a/schemas/registry-config.json b/schemas/registry-config.json index d7f40788..9928d1cc 100644 --- a/schemas/registry-config.json +++ b/schemas/registry-config.json @@ -2,6 +2,22 @@ "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { + "name": { + "type": "string", + "description": "The name of the registry on jsrepo.com.", + "examples": ["@ieedan/std", "@shadcn/ui"] + }, + "version": { + "type": "string", + "description": "A semver compatible version of the registry on jsrepo.com", + "format": "semver", + "examples": ["1.0.0"] + }, + "private": { + "type": "boolean", + "description": "Should this registry be kept private when publishing on jsrepo.com for the first time", + "default": false + }, "meta": { "description": "Optional metadata to include in the manifest file.", "type": "object", diff --git a/src/commands/add.ts b/src/commands/add.ts index e0d8b2a4..4c3a00c1 100644 --- a/src/commands/add.ts +++ b/src/commands/add.ts @@ -266,7 +266,11 @@ async function _add(blockNames: string[], options: Options) { verbose(`Fetching blocks from ${color.cyan(repoPaths.join(', '))}`); - const manifests = (await registry.fetchManifests(...resolvedRepos)).match( + const manifests = ( + await registry.fetchManifests(resolvedRepos, { + verbose: options.verbose ? verbose : undefined, + }) + ).match( (v) => v, ({ repo, message }) => { loading.stop(`Failed fetching blocks from ${color.cyan(repo)}`); diff --git a/src/commands/build.ts b/src/commands/build.ts index c06a715c..071ab3dd 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -94,6 +94,7 @@ async function _build(options: Options) { if (val === null) { return { $schema: '', + private: false, dirs: options.dirs ?? [], outputDir: options.outputDir, doNotListBlocks: options.doNotListBlocks ?? [], @@ -316,6 +317,9 @@ export function createManifest( config: RegistryConfig ) { const manifest: Manifest = { + name: config.name, + version: config.version, + private: config.private, meta: config.meta, peerDependencies: config.peerDependencies, configFiles, diff --git a/src/commands/exec.ts b/src/commands/exec.ts index 33c38861..333e87e2 100644 --- a/src/commands/exec.ts +++ b/src/commands/exec.ts @@ -164,7 +164,11 @@ export async function _exec(s: string | undefined, options: Options, command: an } ); - const blocksMap = (await registry.fetchBlocks(...resolvedRepos)).match( + const blocksMap = ( + await registry.fetchBlocks(resolvedRepos, { + verbose: options.verbose ? verbose : undefined, + }) + ).match( (val) => val, ({ repo, message }) => { loading.stop(`Failed fetching scripts from ${color.cyan(repo)}`); diff --git a/src/commands/index.ts b/src/commands/index.ts index 03736c50..c6e50395 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1,10 +1,10 @@ -import { add } from "./add"; -import { auth } from "./auth"; -import { build } from "./build"; -import { exec } from "./exec"; -import { init } from "./init"; -import { publish } from "./publish"; -import { test } from "./test"; -import { update } from "./update"; +import { add } from './add'; +import { auth } from './auth'; +import { build } from './build'; +import { exec } from './exec'; +import { init } from './init'; +import { publish } from './publish'; +import { test } from './test'; +import { update } from './update'; export { add, auth, build, exec, init, publish, test, update }; diff --git a/src/commands/publish.ts b/src/commands/publish.ts index 15f1ce2b..fd19fea1 100644 --- a/src/commands/publish.ts +++ b/src/commands/publish.ts @@ -1,12 +1,12 @@ -import { cancel, confirm, isCancel, log, outro, password, select, text } from "@clack/prompts"; -import color from "chalk"; -import { Command } from "commander"; -import * as v from "valibot"; -import { intro, spinner } from "../utils/prompts"; -import path from "pathe"; -import * as tar from "tar"; -import fs from "node:fs"; -import fetch from "node-fetch"; +import { cancel, confirm, isCancel, log, outro, password, select, text } from '@clack/prompts'; +import color from 'chalk'; +import { Command } from 'commander'; +import * as v from 'valibot'; +import { intro, spinner } from '../utils/prompts'; +import path from 'pathe'; +import * as tar from 'tar'; +import fs from 'node:fs'; +import fetch from 'node-fetch'; const schema = v.object({ cwd: v.string(), @@ -14,9 +14,9 @@ const schema = v.object({ type Options = v.InferInput; -export const publish = new Command("publish") - .description("Publish a registry to jsrepo.com.") - .option("--cwd ", "The current working directory.", process.cwd()) +export const publish = new Command('publish') + .description('Publish a registry to jsrepo.com.') + .option('--cwd ', 'The current working directory.', process.cwd()) .action(async (opts) => { const options = v.parse(schema, opts); @@ -24,13 +24,13 @@ export const publish = new Command("publish") await _publish(options); - outro(color.green("All done!")); + outro(color.green('All done!')); }); async function _publish(options: Options) { - const dest = "jsrepo-package.tar.gz"; - - const files = fs.readdirSync(path.resolve(options.cwd, 'temp-registry')); + const dest = 'jsrepo-package.tar.gz'; + + const files = fs.readdirSync(path.resolve(options.cwd, 'temp-registry')); await tar.create( { @@ -43,18 +43,21 @@ async function _publish(options: Options) { const tarBuffer = fs.readFileSync(path.resolve(options.cwd, dest)); - const response = await fetch("http://localhost:5173/api/publish", { + const response = await fetch('http://localhost:5173/api/registries/publish', { body: tarBuffer, headers: { - "content-type": "application/gzip", - "content-encoding": "gzip", + 'content-type': 'application/gzip', + 'content-encoding': 'gzip', + 'x-api-key': 'PLSvimGZbGqeHpbahhUDKgGQkYFpBpyiHHcKkEjZDxeDOqkxKvcHyFSnOYwpJaya', }, - method: "POST", + method: 'POST', }); - if (!response.ok) { - console.error(`${response.status} ${response.statusText}`); - } + if (!response.ok) { + const res = (await response.json()) as { message: string }; - console.log('Completed publish!'); + console.error(`${response.status} ${res.message}`); + } else { + console.log('Completed publish!'); + } } diff --git a/src/commands/test.ts b/src/commands/test.ts index 096c6aca..ec684d04 100644 --- a/src/commands/test.ts +++ b/src/commands/test.ts @@ -96,8 +96,10 @@ async function _test(blockNames: string[], options: Options) { verbose(`Fetching blocks from ${color.cyan(repoPaths.join(', '))}`); - const blocksMap: Map = ( - await registry.fetchBlocks(...resolvedRepos) + const blocksMap = ( + await registry.fetchBlocks(resolvedRepos, { + verbose: options.verbose ? verbose : undefined, + }) ).match( (val) => val, ({ repo, message }) => { @@ -176,7 +178,7 @@ async function _test(blockNames: string[], options: Options) { (err) => program.error(color.red(err)) ); - const map = (await registry.fetchBlocks(providerState)).match( + const map = (await registry.fetchBlocks([providerState])).match( (val) => val, (err) => program.error(color.red(err)) ); diff --git a/src/types.ts b/src/types.ts index 08f57044..041e95aa 100644 --- a/src/types.ts +++ b/src/types.ts @@ -58,6 +58,9 @@ export const manifestConfigFileSchema = v.object({ }); export const manifestSchema = v.object({ + name: v.optional(v.string()), + version: v.optional(v.string()), + private: v.optional(v.boolean(), false), meta: v.optional(manifestMeta), peerDependencies: v.optional(peerDependencySchema), configFiles: v.optional(v.array(manifestConfigFileSchema)), diff --git a/src/utils/config.ts b/src/utils/config.ts index 0604f63f..7a44b509 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -55,6 +55,9 @@ export type Formatter = v.InferOutput; export const registryConfigSchema = v.object({ $schema: v.string(), + name: v.optional(v.string()), + version: v.optional(v.string()), + private: v.optional(v.boolean(), false), meta: v.optional(manifestMeta), peerDependencies: v.optional(peerDependencySchema), configFiles: v.optional(v.array(configFileSchema)), diff --git a/src/utils/manifest.ts b/src/utils/manifest.ts index dbcb1e2a..ba86f7b7 100644 --- a/src/utils/manifest.ts +++ b/src/utils/manifest.ts @@ -26,6 +26,7 @@ export function parseManifest(json: string): Result { } return Ok({ + private: false, categories: validated.output, }); } diff --git a/src/utils/registry-providers/index.ts b/src/utils/registry-providers/index.ts index ae3f779f..e6b8d033 100644 --- a/src/utils/registry-providers/index.ts +++ b/src/utils/registry-providers/index.ts @@ -7,9 +7,10 @@ import { type BitBucketProviderState, bitbucket } from './bitbucket'; import { type GitHubProviderState, github } from './github'; import { type GitLabProviderState, gitlab } from './gitlab'; import { http } from './http'; +import { type JsrepoProviderState, jsrepo } from './jsrepo'; import type { RegistryProvider, RegistryProviderState } from './types'; -export const providers = [github, gitlab, bitbucket, azure, http]; +export const providers = [jsrepo, github, gitlab, bitbucket, azure, http]; export function selectProvider(url: string): RegistryProvider | undefined { const provider = providers.find((p) => p.matches(url)); @@ -76,11 +77,13 @@ export async function fetchManifest( export * from './types'; export { + jsrepo, github, gitlab, bitbucket, azure, http, + type JsrepoProviderState, type AzureProviderState, type GitHubProviderState, type GitLabProviderState, diff --git a/src/utils/registry-providers/internal.ts b/src/utils/registry-providers/internal.ts index 00d98c0e..836e314d 100644 --- a/src/utils/registry-providers/internal.ts +++ b/src/utils/registry-providers/internal.ts @@ -8,6 +8,7 @@ import { fetchRaw, github, gitlab, + jsrepo, providers, selectProvider, } from '.'; @@ -146,13 +147,14 @@ export async function forEachPathGetProviderState( * @returns */ export async function fetchBlocks( - ...repos: RegistryProviderState[] + repos: RegistryProviderState[], + { verbose }: { verbose?: (msg: string) => void } = {} ): Promise, { message: string; repo: string }>> { const blocksMap = new Map(); const errors = await Promise.all( repos.map(async (state) => { - const getManifestResult = await internalFetchManifest(state); + const getManifestResult = await internalFetchManifest(state, { verbose }); if (getManifestResult.isErr()) { return Err({ message: getManifestResult.unwrapErr(), repo: state.url }); @@ -210,13 +212,14 @@ export type FetchManifestResult = { * @returns */ export async function fetchManifests( - ...repos: RegistryProviderState[] + repos: RegistryProviderState[], + { verbose }: { verbose?: (msg: string) => void } = {} ): Promise> { const manifests: FetchManifestResult[] = []; const errors = await Promise.all( repos.map(async (state) => { - const getManifestResult = await internalFetchManifest(state); + const getManifestResult = await internalFetchManifest(state, { verbose }); if (getManifestResult.isErr()) { return Err({ message: getManifestResult.unwrapErr(), repo: state.url }); @@ -243,6 +246,7 @@ export { github, gitlab, http, + jsrepo, providers, internalFetchManifest as fetchManifest, internalFetchRaw as fetchRaw, diff --git a/src/utils/registry-providers/jsrepo.ts b/src/utils/registry-providers/jsrepo.ts new file mode 100644 index 00000000..e1f66d08 --- /dev/null +++ b/src/utils/registry-providers/jsrepo.ts @@ -0,0 +1,101 @@ +import color from 'chalk'; +import type { ParseOptions, RegistryProvider, RegistryProviderState } from './types'; + +export const BASE_URL = 'http://localhost:5173'; + +export interface JsrepoProviderState extends RegistryProviderState { + scope: string; + registryName: string; + version: string; +} + +/** Valid paths + * + * `@ieedan/std` + * `@ieedan/std@latest` + * `@ieedan/std@1.0.0` + * `@ieedan/std@1.0.0/ts/math` + */ +export const jsrepo: RegistryProvider = { + name: 'jsrepo', + + matches: (url) => url.startsWith('@'), + + parse: (url, opts) => { + const parsed = parseUrl(url, opts); + + return { + url: parsed.url, + specifier: parsed.specifier, + }; + }, + + baseUrl: (url) => { + const { scope, registryName, version } = parseUrl(url, { fullyQualified: false }); + + return `${BASE_URL}/${scope}/${registryName}/v/${version}`; + }, + + state: async (url) => { + const parsed = parseUrl(url, { fullyQualified: false }); + + return { + ...parsed, + provider: jsrepo, + } satisfies JsrepoProviderState; + }, + + resolveRaw: async (state, resourcePath) => { + // essentially assert that we are using the correct state + if (state.provider.name !== jsrepo.name) { + throw new Error( + `You passed the incorrect state object (${state.provider.name}) to the ${jsrepo.name} provider.` + ); + } + + const { scope, registryName, version } = state as JsrepoProviderState; + + return new URL( + `${BASE_URL}/api/registries/${scope}/${registryName}/v/${version}/files/${resourcePath}` + ); + }, + + authHeader: (token) => ['x-api-key', token], + + formatFetchError: (state, filePath, error) => { + return `There was an error fetching ${color.bold(new URL(filePath, state.url).toString())} + +${color.bold(error)}`; + }, +}; + +function parseUrl( + url: string, + { fullyQualified }: ParseOptions +): { + url: string; + specifier?: string; + scope: string; + registryName: string; + version: string; +} { + const [scope, name, ...rest] = url.split('/'); + + const [registryName, version] = name.split('@'); + + let specifier: string | undefined = undefined; + + if (fullyQualified) { + specifier = rest.slice(rest.length - 2).join('/'); + } + + const parsedUrl = `${scope}/${name}`; + + return { + url: parsedUrl, + specifier, + scope, + registryName, + version: version ?? 'latest', + }; +} diff --git a/tests/providers.test.ts b/tests/providers.test.ts index ff29bf38..b4fd7565 100644 --- a/tests/providers.test.ts +++ b/tests/providers.test.ts @@ -1575,3 +1575,88 @@ describe('http', () => { ]`); }); }); + +describe('jsrepo', () => { + it('correctly parses urls', () => { + const cases: ParseTestCase[] = [ + { + url: '@ieedan/std', + opts: { fullyQualified: false }, + expected: { + url: '@ieedan/std', + specifier: undefined, + }, + }, + { + url: '@ieedan/std@1.0.0', + opts: { fullyQualified: false }, + expected: { + url: '@ieedan/std@1.0.0', + specifier: undefined, + }, + }, + { + url: '@ieedan/std/utils/math', + opts: { fullyQualified: true }, + expected: { + url: '@ieedan/std', + specifier: 'utils/math', + }, + }, + { + url: '@ieedan/std@1.0.0/utils/math', + opts: { fullyQualified: true }, + expected: { + url: '@ieedan/std@1.0.0', + specifier: 'utils/math', + }, + }, + ]; + + for (const c of cases) { + expect(registry.jsrepo.parse(c.url, c.opts)).toStrictEqual(c.expected); + } + }); + + it('correctly resolves url', async () => { + const cases: StringTestCase[] = [ + { + url: '@ieedan/std', + expected: + 'https://jsrepo.com/api/registries/@ieedan/std/v/latest/files/jsrepo-manifest.json', + }, + { + url: '@ieedan/std@1.0.0', + expected: + 'https://jsrepo.com/api/registries/@ieedan/std/v/1.0.0/files/jsrepo-manifest.json', + }, + ]; + + for (const c of cases) { + const state = await registry.getProviderState(c.url); + + assert(!state.isErr()); + + expect( + await state.unwrap().provider.resolveRaw(state.unwrap(), MANIFEST_FILE) + ).toStrictEqual(new URL(c.expected)); + } + }); + + it('correctly parses base url', () => { + const cases: StringTestCase[] = [ + { + url: '@ieedan/std', + expected: 'https://jsrepo.com/@ieedan/std/v/latest', + }, + { + url: '@ieedan/std@1.0.0', + expected: 'https://jsrepo.com/@ieedan/std/v/1.0.0', + }, + ]; + + for (const c of cases) { + expect(registry.jsrepo.baseUrl(c.url)).toBe(c.expected); + } + }); +}); From 0a1d1e55a8d1fb27bda8e770e01b07a4510cc4ba Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Tue, 22 Apr 2025 07:35:37 -0500 Subject: [PATCH 03/18] publish command --- src/commands/build.ts | 25 +-- src/commands/init.ts | 1 + src/commands/publish.ts | 265 +++++++++++++++++++++++-- src/commands/update.ts | 2 +- src/utils/ascii.ts | 1 + src/utils/config.ts | 3 + src/utils/manifest.ts | 21 +- src/utils/registry-providers/jsrepo.ts | 23 ++- 8 files changed, 303 insertions(+), 38 deletions(-) diff --git a/src/commands/build.ts b/src/commands/build.ts index 071ab3dd..402c782f 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -10,13 +10,10 @@ import type { Category, Manifest } from '../types'; import * as ascii from '../utils/ascii'; import { buildBlocksDirectory, buildConfigFiles, pruneUnused } from '../utils/build'; import { DEFAULT_CONFIG, runRules } from '../utils/build/check'; -import { type RegistryConfig, getRegistryConfig } from '../utils/config'; -import { parseManifest } from '../utils/manifest'; +import { IGNORED_DIRS, type RegistryConfig, getRegistryConfig } from '../utils/config'; +import { createManifest, parseManifest } from '../utils/manifest'; import { intro, spinner } from '../utils/prompts'; -// sensible defaults for ignored directories -const IGNORED_DIRS = ['.git', 'node_modules'] as const; - const schema = v.object({ dirs: v.optional(v.array(v.string())), outputDir: v.optional(v.string()), @@ -311,22 +308,4 @@ async function _build(options: Options) { } } -export function createManifest( - categories: Category[], - configFiles: Manifest['configFiles'], - config: RegistryConfig -) { - const manifest: Manifest = { - name: config.name, - version: config.version, - private: config.private, - meta: config.meta, - peerDependencies: config.peerDependencies, - configFiles, - categories, - }; - - return manifest; -} - export { build }; diff --git a/src/commands/init.ts b/src/commands/init.ts index 4a65ce49..778ff0d1 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -694,6 +694,7 @@ const _initRegistry = async (options: Options) => { if (!config) { config = { $schema: '', + private: false, dirs: [], doNotListBlocks: [], doNotListCategories: [], diff --git a/src/commands/publish.ts b/src/commands/publish.ts index fd19fea1..e0965cda 100644 --- a/src/commands/publish.ts +++ b/src/commands/publish.ts @@ -1,14 +1,25 @@ -import { cancel, confirm, isCancel, log, outro, password, select, text } from '@clack/prompts'; +import fs from 'node:fs'; +import { log, outro } from '@clack/prompts'; import color from 'chalk'; -import { Command } from 'commander'; -import * as v from 'valibot'; -import { intro, spinner } from '../utils/prompts'; +import { Command, program } from 'commander'; +import ignore from 'ignore'; +import fetch from 'node-fetch'; import path from 'pathe'; +import semver from 'semver'; import * as tar from 'tar'; -import fs from 'node:fs'; -import fetch from 'node-fetch'; +import * as v from 'valibot'; +import type { Category } from '../types'; +import * as ascii from '../utils/ascii'; +import { buildBlocksDirectory, buildConfigFiles, pruneUnused } from '../utils/build'; +import { runRules } from '../utils/build/check'; +import { IGNORED_DIRS, getRegistryConfig } from '../utils/config'; +import { createManifest } from '../utils/manifest'; +import { intro, spinner } from '../utils/prompts'; +import * as jsrepo from '../utils/registry-providers/jsrepo'; const schema = v.object({ + dryRun: v.boolean(), + verbose: v.boolean(), cwd: v.string(), }); @@ -16,6 +27,8 @@ type Options = v.InferInput; export const publish = new Command('publish') .description('Publish a registry to jsrepo.com.') + .option('--dry-run', "Test the publish but don't list on jsrepo.com", false) + .option('--verbose', 'Include debug logs.', false) .option('--cwd ', 'The current working directory.', process.cwd()) .action(async (opts) => { const options = v.parse(schema, opts); @@ -28,20 +41,223 @@ export const publish = new Command('publish') }); async function _publish(options: Options) { - const dest = 'jsrepo-package.tar.gz'; + const verbose = (msg: string) => { + if (options.verbose) { + console.info(`${ascii.INFO} ${msg}`); + } + }; + + const loading = spinner({ verbose: options.verbose ? verbose : undefined }); + + const config = getRegistryConfig(options.cwd).match( + (val) => { + if (val === null) { + program.error( + color.red(`Publishing to ${color.bold('jsrepo.com')} requires a config.`) + ); + } + + return val; + }, + (err) => program.error(color.red(err)) + ); + + // check name + if (config.name !== undefined) { + try { + const [scope, registryName, ...rest] = config.name.split('/'); + + if (rest.length > 0) { + throw new Error(); + } + + if (!scope.startsWith('@')) { + throw new Error(); + } + + if (!scope.slice(1).match(jsrepo.NAME_REGEX)) { + throw new Error(); + } + + if (!registryName.match(jsrepo.NAME_REGEX)) { + throw new Error(); + } + } catch { + program.error( + color.red( + `\`${config.name}\` is not a valid name. The name should be provided as \`@/\`` + ) + ); + } + } else { + program.error( + color.red( + `To publish to ${color.bold('jsrepo.com')} you need to provide the \`name\` field in the \`jsrepo-build-config.json\`` + ) + ); + } + + // check version + if (config.version !== undefined) { + const valid = semver.valid(config.version); + + if (!valid) { + program.error(`\`${config.version}\` is not a valid semver version.`); + } + } else { + program.error( + color.red( + `To publish to ${color.bold('jsrepo.com')} you need to provide the \`version\` field in the \`jsrepo-build-config.json\`` + ) + ); + } + + // build into temp dir + const categories: Category[] = []; + + const ig = ignore(); + + try { + const ignoreFile = fs.readFileSync(path.join(options.cwd, '.gitignore')).toString(); + + ig.add(ignoreFile); + } catch { + // just continue on + } + + ig.add(IGNORED_DIRS); + + for (const dir of config.dirs) { + const dirPath = path.join(options.cwd, dir); + + loading.start(`Building ${color.cyan(dirPath)}`); + + const builtCategories = buildBlocksDirectory(dirPath, { + cwd: options.cwd, + ignore: ig, + config, + }); + + for (const category of builtCategories) { + if (categories.find((cat) => cat.name === category.name) !== undefined) { + console.warn( + `${ascii.VERTICAL_LINE} ${ascii.WARN} Skipped adding \`${color.cyan(`${dir}/${category.name}`)}\` because a category with the same name already exists!` + ); + continue; + } + + categories.push(category); + } + + loading.stop(`Built ${color.cyan(dirPath)}`); + } + + const configFiles = buildConfigFiles(config, { cwd: options.cwd }); - const files = fs.readdirSync(path.resolve(options.cwd, 'temp-registry')); + const manifest = createManifest(categories, configFiles, config); + + loading.start('Checking manifest'); + + const { warnings, errors } = runRules(manifest, config, options.cwd, config.rules); + + loading.stop('Completed checking manifest.'); + + // add gap for errors + if (warnings.length > 0 || errors.length > 0) { + console.log(ascii.VERTICAL_LINE); + } + + for (const warning of warnings) { + console.log(warning); + } + + if (errors.length > 0) { + for (const error of errors) { + console.log(error); + } + + program.error( + color.red( + `Completed checking manifest with ${color.bold(`${errors.length} error(s)`)} and ${color.bold(`${warnings.length} warning(s)`)}` + ) + ); + } + + // removes any unused blocks or categories + const [prunedCategories, count] = pruneUnused(manifest.categories); + + manifest.categories = prunedCategories; + + if (count > 0) { + log.step(`Removed ${count} unused block${count > 1 ? 's' : ''}.`); + } + + loading.start(`Packaging ${color.cyan(manifest.name)}...`); + + const tempOutDir = path.resolve(options.cwd, `jsrepo-publish-temp-${Date.now()}`); + + fs.mkdirSync(tempOutDir, { recursive: true }); + + fs.writeFileSync(path.resolve(tempOutDir, 'jsrepo-manifest.json'), JSON.stringify(manifest)); + + // copy config files to output directory + if (manifest.configFiles) { + for (const file of manifest.configFiles) { + const originalPath = path.join(options.cwd, file.path); + const destPath = path.join(tempOutDir, file.path); + + const containing = path.join(destPath, '../'); + + if (!fs.existsSync(containing)) { + fs.mkdirSync(containing, { recursive: true }); + } + + fs.copyFileSync(originalPath, destPath); + } + } + + // copy the files for each block in each category + for (const category of manifest.categories) { + for (const block of category.blocks) { + const originalPath = path.join(options.cwd, block.directory); + const newDirPath = path.join(tempOutDir, block.directory); + + for (const file of block.files) { + const containing = path.join(newDirPath, file, '../'); + + if (!fs.existsSync(containing)) { + fs.mkdirSync(containing, { recursive: true }); + } + + fs.copyFileSync(path.join(originalPath, file), path.join(newDirPath, file)); + } + } + } + + const dest = path.resolve(options.cwd, `${config.name.replace('/', '_')}-package.tar.gz`); + + const files = fs.readdirSync(tempOutDir); await tar.create( { z: true, - cwd: path.resolve(options.cwd, 'temp-registry'), + cwd: tempOutDir, file: dest, }, files ); - const tarBuffer = fs.readFileSync(path.resolve(options.cwd, dest)); + // remove temp directory + fs.rmSync(tempOutDir, { force: true, recursive: true }); + + loading.stop(`Created package ${color.cyan(dest)}...`); + + loading.start(`Publishing to ${ascii.JSREPO_DOT_COM}...`); + + const tarBuffer = fs.readFileSync(dest); + + // remove archive file + fs.rmSync(dest, { force: true, recursive: true }); const response = await fetch('http://localhost:5173/api/registries/publish', { body: tarBuffer, @@ -49,15 +265,40 @@ async function _publish(options: Options) { 'content-type': 'application/gzip', 'content-encoding': 'gzip', 'x-api-key': 'PLSvimGZbGqeHpbahhUDKgGQkYFpBpyiHHcKkEjZDxeDOqkxKvcHyFSnOYwpJaya', + 'x-dry-run': options.dryRun ? '1' : '0', }, method: 'POST', }); + loading.stop(`Got response from ${ascii.JSREPO_DOT_COM}.`); + if (!response.ok) { const res = (await response.json()) as { message: string }; - console.error(`${response.status} ${res.message}`); + program.error( + color.red(`${color.bold('[jsrepo.com]')} ${color.bold(response.status)} ${res.message}`) + ); } else { - console.log('Completed publish!'); + const res = (await response.json()) as PublishResponse; + + if (res.status === 'dry-run') { + log.success(`${color.hex('#f7df1e').bold('[jsrepo.com]')} Completed dry run!`); + } else { + log.success( + `${color.hex('#f7df1e').bold('[jsrepo.com]')} published ${color.greenBright(`@${res.scope}`)}/${res.registry}${color.greenBright(`@${res.version}`)}!` + ); + } } } + +type PublishResponse = + | { + status: 'published'; + scope: string; + registry: string; + version: string; + tag: string | null; + } + | { + status: 'dry-run'; + }; diff --git a/src/commands/update.ts b/src/commands/update.ts index 0daa8d7a..e2db9a8f 100644 --- a/src/commands/update.ts +++ b/src/commands/update.ts @@ -127,7 +127,7 @@ async function _update(blockNames: string[], options: Options) { verbose(`Fetching blocks from ${color.cyan(repoPaths.join(', '))}`); - const manifests = (await registry.fetchManifests(...resolvedRepos)).match( + const manifests = (await registry.fetchManifests(resolvedRepos)).match( (v) => v, ({ repo, message }) => { loading.stop(`Failed fetching blocks from ${color.cyan(repo)}`); diff --git a/src/utils/ascii.ts b/src/utils/ascii.ts index 41f2ba81..5da3bc24 100644 --- a/src/utils/ascii.ts +++ b/src/utils/ascii.ts @@ -28,3 +28,4 @@ export const INFO = color.bgBlueBright.white(' INFO '); export const ERROR = color.bgRedBright.white(' ERROR '); export const JSREPO = color.hex('#f7df1e')('jsrepo'); +export const JSREPO_DOT_COM = color.hex('#f7df1e').bold('jsrepo.com'); diff --git a/src/utils/config.ts b/src/utils/config.ts index 7a44b509..fdd02961 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -8,6 +8,9 @@ import { Err, Ok, type Result } from './blocks/ts/result'; import { ruleConfigSchema } from './build/check'; import { tryGetTsconfig } from './files'; +/** sensible defaults for ignored directories */ +export const IGNORED_DIRS = ['.git', 'node_modules'] as const; + export const PROJECT_CONFIG_NAME = 'jsrepo.json'; export const REGISTRY_CONFIG_NAME = 'jsrepo-build-config.json'; diff --git a/src/utils/manifest.ts b/src/utils/manifest.ts index ba86f7b7..29500df5 100644 --- a/src/utils/manifest.ts +++ b/src/utils/manifest.ts @@ -1,6 +1,7 @@ import * as v from 'valibot'; -import { type Manifest, categorySchema, manifestSchema } from '../types'; +import { type Category, type Manifest, categorySchema, manifestSchema } from '../types'; import { Err, Ok, type Result } from './blocks/ts/result'; +import type { RegistryConfig } from './config'; /** Parses the json string (if it can be) into a manifest. * @@ -39,3 +40,21 @@ export function parseManifest(json: string): Result { return Ok(validated.output); } + +export function createManifest( + categories: Category[], + configFiles: Manifest['configFiles'], + config: RegistryConfig +) { + const manifest: Manifest = { + name: config.name, + version: config.version, + private: config.private, + meta: config.meta, + peerDependencies: config.peerDependencies, + configFiles, + categories, + }; + + return manifest; +} diff --git a/src/utils/registry-providers/jsrepo.ts b/src/utils/registry-providers/jsrepo.ts index e1f66d08..0b57d419 100644 --- a/src/utils/registry-providers/jsrepo.ts +++ b/src/utils/registry-providers/jsrepo.ts @@ -1,6 +1,27 @@ import color from 'chalk'; import type { ParseOptions, RegistryProvider, RegistryProviderState } from './types'; +/** Regex for scopes and registry names. + * Names that don't match this regex will be rejected. + * + * ### Valid + * ```txt + * console + * console0 + * console-0 + * ``` + * + * ### Invalid + * ```txt + * Console + * 0console + * -console + * console- + * console--0 + * ``` + */ +export const NAME_REGEX = /^(?![-0-9])(?!.*--)[a-z0-9]*(?:-[a-z0-9]+)*$/gi; + export const BASE_URL = 'http://localhost:5173'; export interface JsrepoProviderState extends RegistryProviderState { @@ -69,7 +90,7 @@ ${color.bold(error)}`; }, }; -function parseUrl( +export function parseUrl( url: string, { fullyQualified }: ParseOptions ): { From 7b9143237c04b466470596f8bd82a075dc53e7b0 Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Tue, 22 Apr 2025 07:46:12 -0500 Subject: [PATCH 04/18] move endpoints --- src/commands/publish.ts | 2 +- src/utils/registry-providers/jsrepo.ts | 2 +- tests/providers.test.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/commands/publish.ts b/src/commands/publish.ts index e0965cda..c29d3505 100644 --- a/src/commands/publish.ts +++ b/src/commands/publish.ts @@ -259,7 +259,7 @@ async function _publish(options: Options) { // remove archive file fs.rmSync(dest, { force: true, recursive: true }); - const response = await fetch('http://localhost:5173/api/registries/publish', { + const response = await fetch('http://localhost:5173/api/publish', { body: tarBuffer, headers: { 'content-type': 'application/gzip', diff --git a/src/utils/registry-providers/jsrepo.ts b/src/utils/registry-providers/jsrepo.ts index 0b57d419..8558d7d7 100644 --- a/src/utils/registry-providers/jsrepo.ts +++ b/src/utils/registry-providers/jsrepo.ts @@ -77,7 +77,7 @@ export const jsrepo: RegistryProvider = { const { scope, registryName, version } = state as JsrepoProviderState; return new URL( - `${BASE_URL}/api/registries/${scope}/${registryName}/v/${version}/files/${resourcePath}` + `${BASE_URL}/api/scopes/${scope}/${registryName}/v/${version}/files/${resourcePath}` ); }, diff --git a/tests/providers.test.ts b/tests/providers.test.ts index b4fd7565..1b9ea64a 100644 --- a/tests/providers.test.ts +++ b/tests/providers.test.ts @@ -1623,12 +1623,12 @@ describe('jsrepo', () => { { url: '@ieedan/std', expected: - 'https://jsrepo.com/api/registries/@ieedan/std/v/latest/files/jsrepo-manifest.json', + 'https://jsrepo.com/api/scopes/@ieedan/std/v/latest/files/jsrepo-manifest.json', }, { url: '@ieedan/std@1.0.0', expected: - 'https://jsrepo.com/api/registries/@ieedan/std/v/1.0.0/files/jsrepo-manifest.json', + 'https://jsrepo.com/api/scopes/@ieedan/std/v/1.0.0/files/jsrepo-manifest.json', }, ]; From 13a570b3f99d61197f6074fa7d0b0e6a7c094768 Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Tue, 22 Apr 2025 08:50:24 -0500 Subject: [PATCH 05/18] improvements --- .gitignore | 3 +-- jsrepo-package.tar.gz | Bin 18893 -> 0 bytes jsrepo.json | 4 ++-- schemas/registry-config.json | 9 ++++---- src/commands/auth.ts | 11 +++++++++- src/commands/build.ts | 4 ++-- src/commands/init.ts | 2 +- src/commands/publish.ts | 39 ++++++++++++++++++++++++++++++++--- src/types.ts | 1 - src/utils/config.ts | 2 +- src/utils/manifest.ts | 1 - 11 files changed, 58 insertions(+), 18 deletions(-) delete mode 100644 jsrepo-package.tar.gz diff --git a/.gitignore b/.gitignore index 10d98241..436a245c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,4 @@ node_modules dist # testing -temp-test -temp-registry \ No newline at end of file +temp-test \ No newline at end of file diff --git a/jsrepo-package.tar.gz b/jsrepo-package.tar.gz deleted file mode 100644 index 15b204003c932c441ef4ed7533a4a9e33c59a5b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18893 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 diff --git a/jsrepo.json b/jsrepo.json index 2e7ce1e7..1ce74cd9 100644 --- a/jsrepo.json +++ b/jsrepo.json @@ -1,6 +1,6 @@ { - "$schema": "https://unpkg.com/jsrepo@1.43.0/schemas/project-config.json", - "repos": ["github/ieedan/std"], + "$schema": "https://unpkg.com/jsrepo@1.47.1/schemas/project-config.json", + "repos": ["@ieedan/std"], "includeTests": false, "watermark": true, "formatter": "biome", diff --git a/schemas/registry-config.json b/schemas/registry-config.json index 9928d1cc..0fcd8579 100644 --- a/schemas/registry-config.json +++ b/schemas/registry-config.json @@ -13,10 +13,11 @@ "format": "semver", "examples": ["1.0.0"] }, - "private": { - "type": "boolean", - "description": "Should this registry be kept private when publishing on jsrepo.com for the first time", - "default": false + "readme": { + "type": "string", + "description": "Path to the README file that will be packaged with the registry.", + "default": "README.md", + "examples": ["README.md"] }, "meta": { "description": "Optional metadata to include in the manifest file.", diff --git a/src/commands/auth.ts b/src/commands/auth.ts index 2550e0c5..d73a308d 100644 --- a/src/commands/auth.ts +++ b/src/commands/auth.ts @@ -15,7 +15,16 @@ const schema = v.object({ type Options = v.InferInput; -const services = ['Anthropic', 'Azure', 'BitBucket', 'GitHub', 'GitLab', 'OpenAI', 'http'].sort(); +const services = [ + 'Anthropic', + 'Azure', + 'BitBucket', + 'GitHub', + 'GitLab', + 'jsrepo', + 'OpenAI', + 'http', +].sort(); export const auth = new Command('auth') .description('Provide a token for access to private repositories.') diff --git a/src/commands/build.ts b/src/commands/build.ts index 402c782f..f3da40ef 100644 --- a/src/commands/build.ts +++ b/src/commands/build.ts @@ -6,7 +6,7 @@ import ignore from 'ignore'; import path from 'pathe'; import * as v from 'valibot'; import { MANIFEST_FILE } from '../constants'; -import type { Category, Manifest } from '../types'; +import type { Category } from '../types'; import * as ascii from '../utils/ascii'; import { buildBlocksDirectory, buildConfigFiles, pruneUnused } from '../utils/build'; import { DEFAULT_CONFIG, runRules } from '../utils/build/check'; @@ -91,7 +91,7 @@ async function _build(options: Options) { if (val === null) { return { $schema: '', - private: false, + readme: 'README.md', dirs: options.dirs ?? [], outputDir: options.outputDir, doNotListBlocks: options.doNotListBlocks ?? [], diff --git a/src/commands/init.ts b/src/commands/init.ts index 778ff0d1..a54e96cf 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -694,7 +694,7 @@ const _initRegistry = async (options: Options) => { if (!config) { config = { $schema: '', - private: false, + readme: 'README.md', dirs: [], doNotListBlocks: [], doNotListCategories: [], diff --git a/src/commands/publish.ts b/src/commands/publish.ts index c29d3505..5c98ba14 100644 --- a/src/commands/publish.ts +++ b/src/commands/publish.ts @@ -16,8 +16,10 @@ import { IGNORED_DIRS, getRegistryConfig } from '../utils/config'; import { createManifest } from '../utils/manifest'; import { intro, spinner } from '../utils/prompts'; import * as jsrepo from '../utils/registry-providers/jsrepo'; +import { TokenManager } from '../utils/token-manager'; const schema = v.object({ + private: v.boolean(), dryRun: v.boolean(), verbose: v.boolean(), cwd: v.string(), @@ -27,7 +29,12 @@ type Options = v.InferInput; export const publish = new Command('publish') .description('Publish a registry to jsrepo.com.') - .option('--dry-run', "Test the publish but don't list on jsrepo.com", false) + .option( + '--private', + 'When publishing the first version of the registry make it private.', + false + ) + .option('--dry-run', "Test the publish but don't list on jsrepo.com.", false) .option('--verbose', 'Include debug logs.', false) .option('--cwd ', 'The current working directory.', process.cwd()) .action(async (opts) => { @@ -62,6 +69,12 @@ async function _publish(options: Options) { (err) => program.error(color.red(err)) ); + if (options.dryRun) { + log.warn(color.bgYellow.black(' DRY RUN ')); + } + + // -- pre-flights -- + // check name if (config.name !== undefined) { try { @@ -112,6 +125,14 @@ async function _publish(options: Options) { ); } + const apiKey = new TokenManager().get('jsrepo'); + + if (apiKey === undefined) { + program.error( + color.red(`To publish to ${color.bold('jsrepo.com')} you need an access token.`) + ); + } + // build into temp dir const categories: Category[] = []; @@ -198,8 +219,18 @@ async function _publish(options: Options) { fs.mkdirSync(tempOutDir, { recursive: true }); + // write manifest fs.writeFileSync(path.resolve(tempOutDir, 'jsrepo-manifest.json'), JSON.stringify(manifest)); + // try copy readme + const readmePath = path.resolve(options.cwd, config.readme); + + try { + fs.copyFileSync(readmePath, path.join(tempOutDir, 'README.md')); + } catch { + // do nothing it's okay + } + // copy config files to output directory if (manifest.configFiles) { for (const file of manifest.configFiles) { @@ -252,7 +283,7 @@ async function _publish(options: Options) { loading.stop(`Created package ${color.cyan(dest)}...`); - loading.start(`Publishing to ${ascii.JSREPO_DOT_COM}...`); + loading.start(`Publishing ${color.bold(manifest.name)} to ${ascii.JSREPO_DOT_COM}...`); const tarBuffer = fs.readFileSync(dest); @@ -264,8 +295,9 @@ async function _publish(options: Options) { headers: { 'content-type': 'application/gzip', 'content-encoding': 'gzip', - 'x-api-key': 'PLSvimGZbGqeHpbahhUDKgGQkYFpBpyiHHcKkEjZDxeDOqkxKvcHyFSnOYwpJaya', + 'x-api-key': apiKey, 'x-dry-run': options.dryRun ? '1' : '0', + 'x-private': options.private ? '1' : '0', }, method: 'POST', }); @@ -298,6 +330,7 @@ type PublishResponse = registry: string; version: string; tag: string | null; + private: boolean; } | { status: 'dry-run'; diff --git a/src/types.ts b/src/types.ts index 041e95aa..e79b3406 100644 --- a/src/types.ts +++ b/src/types.ts @@ -60,7 +60,6 @@ export const manifestConfigFileSchema = v.object({ export const manifestSchema = v.object({ name: v.optional(v.string()), version: v.optional(v.string()), - private: v.optional(v.boolean(), false), meta: v.optional(manifestMeta), peerDependencies: v.optional(peerDependencySchema), configFiles: v.optional(v.array(manifestConfigFileSchema)), diff --git a/src/utils/config.ts b/src/utils/config.ts index fdd02961..9dcd3134 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -60,7 +60,7 @@ export const registryConfigSchema = v.object({ $schema: v.string(), name: v.optional(v.string()), version: v.optional(v.string()), - private: v.optional(v.boolean(), false), + readme: v.optional(v.string(), 'README.md'), meta: v.optional(manifestMeta), peerDependencies: v.optional(peerDependencySchema), configFiles: v.optional(v.array(configFileSchema)), diff --git a/src/utils/manifest.ts b/src/utils/manifest.ts index 29500df5..a35a3527 100644 --- a/src/utils/manifest.ts +++ b/src/utils/manifest.ts @@ -49,7 +49,6 @@ export function createManifest( const manifest: Manifest = { name: config.name, version: config.version, - private: config.private, meta: config.meta, peerDependencies: config.peerDependencies, configFiles, From 326302b2a5481b32847abbfc50a83b5f2245cf8a Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Tue, 22 Apr 2025 09:30:01 -0500 Subject: [PATCH 06/18] init and some changesets --- .changeset/lucky-regions-poke.md | 6 + .changeset/red-flowers-unite.md | 6 + .changeset/tasty-taxis-retire.md | 6 + src/commands/init.ts | 215 ++++++++++++++++++------------- 4 files changed, 143 insertions(+), 90 deletions(-) create mode 100644 .changeset/lucky-regions-poke.md create mode 100644 .changeset/red-flowers-unite.md create mode 100644 .changeset/tasty-taxis-retire.md diff --git a/.changeset/lucky-regions-poke.md b/.changeset/lucky-regions-poke.md new file mode 100644 index 00000000..11fd8502 --- /dev/null +++ b/.changeset/lucky-regions-poke.md @@ -0,0 +1,6 @@ +--- +"jsrepo": major +--- + +feat: Support jsrepo as a registry provider 🎉 + \ No newline at end of file diff --git a/.changeset/red-flowers-unite.md b/.changeset/red-flowers-unite.md new file mode 100644 index 00000000..aa601d8b --- /dev/null +++ b/.changeset/red-flowers-unite.md @@ -0,0 +1,6 @@ +--- +"jsrepo": minor +--- + +feat: Setup registry for publish on jsrepo.com during init + \ No newline at end of file diff --git a/.changeset/tasty-taxis-retire.md b/.changeset/tasty-taxis-retire.md new file mode 100644 index 00000000..bd94ed50 --- /dev/null +++ b/.changeset/tasty-taxis-retire.md @@ -0,0 +1,6 @@ +--- +"jsrepo": minor +--- + +feat: Always install jsrepo as a dev dependency when initializing a new registry + \ No newline at end of file diff --git a/src/commands/init.ts b/src/commands/init.ts index a54e96cf..5ee5e225 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -33,6 +33,7 @@ import { packageJson } from '../utils/context'; import { formatFile, matchJSDescendant, tryGetTsconfig } from '../utils/files'; import { loadFormatterConfig } from '../utils/format'; import { json } from '../utils/language-support'; +import type { PackageJson } from '../utils/package'; import { checkPreconditions } from '../utils/preconditions'; import { type Task, @@ -52,7 +53,8 @@ const schema = v.object({ formatter: v.optional(formatterSchema), project: v.optional(v.boolean()), registry: v.optional(v.boolean()), - script: v.string(), + buildScript: v.string(), + publishScript: v.string(), expand: v.boolean(), maxUnchanged: v.number(), yes: v.boolean(), @@ -80,10 +82,15 @@ const init = new Command('init') .option('-P, --project', 'Takes you through the steps to initialize a project.') .option('-R, --registry', 'Takes you through the steps to initialize a registry.') .option( - '--script ', + '--build-script ', 'The name of the build script. (For Registry setup)', 'build:registry' ) + .option( + '--publish-script ', + 'The name of the publish script. (For Registry setup)', + 'release:registry' + ) .option('-E, --expand', 'Expands the diff so you see the entire file.', false) .option( '--max-unchanged ', @@ -689,11 +696,11 @@ const _initRegistry = async (options: Options) => { (err) => program.error(color.red(err)) ); - const noConfig = config === null; - if (!config) { config = { $schema: '', + name: undefined, + version: undefined, readme: 'README.md', dirs: [], doNotListBlocks: [], @@ -746,103 +753,82 @@ const _initRegistry = async (options: Options) => { const pkg = JSON.parse(fs.readFileSync(packagePath).toString()); - // continue asking until the user either chooses to overwrite or inputs a script that doesn't exist yet - while (!options.yes && pkg.scripts && pkg.scripts[options.script]) { - const response = await confirm({ - message: `The \`${color.cyan(options.script)}\` already exists overwrite?`, - initialValue: false, + let configurePublish = !options.yes; + + if (!options.yes) { + const confirmResult = await confirm({ + message: `Configure to publish to ${ascii.JSREPO_DOT_COM}?`, + initialValue: true, }); - if (isCancel(response)) { + if (isCancel(confirmResult)) { cancel('Canceled!'); process.exit(0); } - if (!response) { - const response = await text({ - message: 'What would you like to call the script?', - placeholder: 'build:registry', + configurePublish = confirmResult; + } + + if (configurePublish) { + if (!config.name) { + const nameResponse = await text({ + message: "What's the name of your registry?", + placeholder: '@ieedan/std', validate: (val) => { if (val.trim().length === 0) return 'Please provide a value!'; }, }); - if (isCancel(response)) { + if (isCancel(nameResponse)) { cancel('Canceled!'); process.exit(0); } - options.script = response; - } else { - break; + config.name = nameResponse; } - } - - const alreadyInstalled = pkg.devDependencies && pkg.devDependencies.jsrepo !== undefined; - - let installAsDevDependency = options.yes || alreadyInstalled; - - if (!options.yes && !alreadyInstalled) { - const response = await confirm({ - message: `Add ${ascii.JSREPO} as a dev dependency?`, - initialValue: true, - }); - if (isCancel(response)) { - cancel('Canceled!'); - process.exit(0); + if (!config.version) { + config.version = '0.0.1'; } - installAsDevDependency = response; + options.publishScript = await promptForScriptKey( + options.publishScript, + pkg, + options, + 'release:registry' + ); + } else { + options.buildScript = await promptForScriptKey( + options.buildScript, + pkg, + options, + 'build:registry' + ); } - let jsonConfig = !noConfig; - - if (!options.yes && noConfig) { - const response = await confirm({ - message: `Create a \`${color.cyan(REGISTRY_CONFIG_NAME)}\` file?`, - initialValue: true, - }); - - if (isCancel(response)) { - cancel('Canceled!'); - process.exit(0); - } - - jsonConfig = response; - } + const alreadyInstalled = pkg.devDependencies && pkg.devDependencies.jsrepo !== undefined; const pm = (await detect({ cwd: 'cwd' }))?.agent ?? 'npm'; - let buildScript = ''; - - if (installAsDevDependency) { - buildScript += 'jsrepo build'; - } else { - const command = resolveCommand(pm, 'execute', ['jsrepo', 'build']); - - if (!command) program.error(color.red(`Error resolving execute command for ${pm}`)); - - buildScript += `${command.command} ${command.args.join(' ')} `; - } - - // if we aren't using a config file configure the command with the correct flags - if (!jsonConfig) { - buildScript += ` --dirs ${config.dirs.join(' ')} `; - } + const buildScript = 'jsrepo build'; + const publishScript = 'jsrepo publish'; // ensure we are adding to an object that exists if (pkg.scripts === undefined) { pkg.scripts = {}; } - pkg.scripts[options.script] = buildScript; + if (configurePublish) { + pkg.scripts[options.publishScript] = publishScript; + } else { + pkg.scripts[options.buildScript] = buildScript; + } const tasks: Task[] = []; tasks.push({ - loadingMessage: `Adding \`${color.cyan(options.script)}\` to scripts in package.json`, - completedMessage: `Added \`${color.cyan(options.script)}\` to scripts in package.json`, + loadingMessage: 'Adding script to package.json', + completedMessage: 'Added script to package.json', run: async () => { try { fs.writeFileSync(packagePath, JSON.stringify(pkg, null, '\t')); @@ -854,29 +840,27 @@ const _initRegistry = async (options: Options) => { }, }); - if (jsonConfig) { - tasks.push({ - loadingMessage: `Writing config to \`${color.cyan(REGISTRY_CONFIG_NAME)}\``, - completedMessage: `Wrote config to \`${color.cyan(REGISTRY_CONFIG_NAME)}\``, - run: async () => { - const configPath = path.join(options.cwd, REGISTRY_CONFIG_NAME); - - try { - fs.writeFileSync(path.join(configPath), JSON.stringify(config, null, '\t')); - } catch (err) { - program.error( - color.red(`Error writing to \`${color.bold(configPath)}\`. Error: ${err}`) - ); - } - }, - }); - } + tasks.push({ + loadingMessage: `Writing config to \`${color.cyan(REGISTRY_CONFIG_NAME)}\``, + completedMessage: `Wrote config to \`${color.cyan(REGISTRY_CONFIG_NAME)}\``, + run: async () => { + const configPath = path.join(options.cwd, REGISTRY_CONFIG_NAME); + + try { + fs.writeFileSync(path.join(configPath), JSON.stringify(config, null, '\t')); + } catch (err) { + program.error( + color.red(`Error writing to \`${color.bold(configPath)}\`. Error: ${err}`) + ); + } + }, + }); await runTasks(tasks, { loading }); let installed = alreadyInstalled; - if (installAsDevDependency && !alreadyInstalled) { + if (!alreadyInstalled) { const installedResult = await promptInstallDependencies(new Set(), new Set(['jsrepo']), { cwd: options.cwd, pm, @@ -888,7 +872,7 @@ const _initRegistry = async (options: Options) => { let steps: string[] = []; - if (!installed && installAsDevDependency) { + if (!installed) { const cmd = resolveCommand(pm, 'add', ['jsrepo', '-D']); steps.push( @@ -898,11 +882,19 @@ const _initRegistry = async (options: Options) => { steps.push(`Add categories to \`${color.cyan(config.dirs.join(', '))}\`.`); - const runScript = resolveCommand(pm, 'run', [options.script]); + if (configurePublish) { + const runScript = resolveCommand(pm, 'run', [options.publishScript]); - steps.push( - `Run \`${color.cyan(`${runScript?.command} ${runScript?.args.join(' ')}`)}\` to build the registry.` - ); + steps.push( + `Run \`${color.cyan(`${runScript?.command} ${runScript?.args.join(' ')}`)}\` to publish the registry.` + ); + } else { + const runScript = resolveCommand(pm, 'run', [options.buildScript]); + + steps.push( + `Run \`${color.cyan(`${runScript?.command} ${runScript?.args.join(' ')}`)}\` to build the registry.` + ); + } // put steps with numbers above here steps = steps.map((step, i) => `${i + 1}. ${step}`); @@ -912,4 +904,47 @@ const _initRegistry = async (options: Options) => { process.stdout.write(next); }; +async function promptForScriptKey( + scriptKey: string, + pkg: PackageJson, + options: Options, + placeholder: string +) { + let key = scriptKey; + + // continue asking until the user either chooses to overwrite or inputs a script that doesn't exist yet + while (!options.yes && pkg.scripts && pkg.scripts[key]) { + const response = await confirm({ + message: `The \`${color.cyan(key)}\` already exists overwrite?`, + initialValue: false, + }); + + if (isCancel(response)) { + cancel('Canceled!'); + process.exit(0); + } + + if (!response) { + const response = await text({ + message: 'What would you like to call the script?', + placeholder, + validate: (val) => { + if (val.trim().length === 0) return 'Please provide a value!'; + }, + }); + + if (isCancel(response)) { + cancel('Canceled!'); + process.exit(0); + } + + key = response; + } else { + break; + } + } + + return key; +} + export { init }; From dd7c79f360eca2f44d9fc288fa1ceed01a0ea53c Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Mon, 28 Apr 2025 08:48:11 -0500 Subject: [PATCH 07/18] a few things --- package.json | 16 ++++++++++++++-- src/utils/token-manager.ts | 8 +++++++- tests/pre-release.test.ts | 8 ++++++++ tests/providers.test.ts | 22 ++++++++++++++++++---- 4 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 tests/pre-release.test.ts diff --git a/package.json b/package.json index cffdba7a..e29eea0b 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,16 @@ "bugs": { "url": "https://github.com/jsrepojs/jsrepo/issues" }, - "keywords": ["repo", "cli", "svelte", "vue", "typescript", "javascript", "shadcn", "registry"], + "keywords": [ + "repo", + "cli", + "svelte", + "vue", + "typescript", + "javascript", + "shadcn", + "registry" + ], "type": "module", "packageManager": "pnpm@10.8.1", "exports": { @@ -26,7 +35,10 @@ }, "bin": "./dist/index.js", "main": "./dist/index.js", - "files": ["./schemas/**/*", "dist/**/*"], + "files": [ + "./schemas/**/*", + "dist/**/*" + ], "scripts": { "start": "tsup --silent && node ./dist/index.js", "build": "tsup", diff --git a/src/utils/token-manager.ts b/src/utils/token-manager.ts index 7b23a50e..74b39cba 100644 --- a/src/utils/token-manager.ts +++ b/src/utils/token-manager.ts @@ -17,7 +17,13 @@ export class TokenManager { get(name: string): string | undefined { const key = this.getKey(name); - return this.#storage.get(key, undefined) as string | undefined; + const token = this.#storage.get(key, undefined) as string | undefined; + + if (name === 'jsrepo') { + return token ?? process.env.JSREPO_TOKEN; + } + + return token; } set(name: string, secret: string) { diff --git a/tests/pre-release.test.ts b/tests/pre-release.test.ts new file mode 100644 index 00000000..da9f6ee2 --- /dev/null +++ b/tests/pre-release.test.ts @@ -0,0 +1,8 @@ +import { describe, it, expect } from "vitest"; +import { BASE_URL as JSREPO_BASE_URL } from "../src/utils/registry-providers/jsrepo"; + +describe('JSREPO_BASE_URL', () => { + it('is the correct url', () => { + expect(JSREPO_BASE_URL).toBe('https://jsrepo.com') + }) +}) \ No newline at end of file diff --git a/tests/providers.test.ts b/tests/providers.test.ts index 1b9ea64a..22b297aa 100644 --- a/tests/providers.test.ts +++ b/tests/providers.test.ts @@ -2,6 +2,7 @@ import { assert, describe, expect, it } from 'vitest'; import { MANIFEST_FILE } from '../src/constants'; import * as registry from '../src/utils/registry-providers/internal'; import type { ParseOptions, ParseResult } from '../src/utils/registry-providers/types'; +import { BASE_URL as JSREPO_BASE_URL } from '../src/utils/registry-providers/jsrepo'; type ParseTestCase = { url: string; @@ -1623,12 +1624,12 @@ describe('jsrepo', () => { { url: '@ieedan/std', expected: - 'https://jsrepo.com/api/scopes/@ieedan/std/v/latest/files/jsrepo-manifest.json', + `${JSREPO_BASE_URL}/api/scopes/@ieedan/std/v/latest/files/jsrepo-manifest.json`, }, { url: '@ieedan/std@1.0.0', expected: - 'https://jsrepo.com/api/scopes/@ieedan/std/v/1.0.0/files/jsrepo-manifest.json', + `${JSREPO_BASE_URL}/api/scopes/@ieedan/std/v/1.0.0/files/jsrepo-manifest.json`, }, ]; @@ -1647,11 +1648,11 @@ describe('jsrepo', () => { const cases: StringTestCase[] = [ { url: '@ieedan/std', - expected: 'https://jsrepo.com/@ieedan/std/v/latest', + expected: `${JSREPO_BASE_URL}/@ieedan/std/v/latest`, }, { url: '@ieedan/std@1.0.0', - expected: 'https://jsrepo.com/@ieedan/std/v/1.0.0', + expected: `${JSREPO_BASE_URL}/@ieedan/std/v/1.0.0`, }, ]; @@ -1659,4 +1660,17 @@ describe('jsrepo', () => { expect(registry.jsrepo.baseUrl(c.url)).toBe(c.expected); } }); + + it('Fetches the manifest', async () => { + const repoURL = '@ieedan/std'; + + const providerState = await registry.getProviderState(repoURL); + + assert(providerState.isOk()); + + // this way we just get the text and skip the schema validation + const content = await registry.fetchRaw(providerState.unwrap(), 'jsrepo-manifest.json'); + + expect(content.isOk()).toBe(true) + }); }); From ef7be88ba7c2fa9fe8cfc5c179878dd4847231ef Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Mon, 28 Apr 2025 13:23:18 -0500 Subject: [PATCH 08/18] allow for caching --- .changeset/nine-dingos-chew.md | 6 + .gitignore | 4 +- jsrepo.json | 2 +- package.json | 20 +- pnpm-lock.yaml | 375 ++++++++++++++++++++--- src/commands/publish.ts | 4 +- src/utils/fetch.ts | 4 + src/utils/get-latest-version.ts | 16 +- src/utils/registry-providers/index.ts | 2 +- src/utils/registry-providers/internal.ts | 16 +- src/utils/registry-providers/jsrepo.ts | 4 +- src/utils/registry-providers/types.ts | 2 +- tests/pre-release.test.ts | 12 +- tests/providers.test.ts | 10 +- 14 files changed, 373 insertions(+), 104 deletions(-) create mode 100644 .changeset/nine-dingos-chew.md create mode 100644 src/utils/fetch.ts diff --git a/.changeset/nine-dingos-chew.md b/.changeset/nine-dingos-chew.md new file mode 100644 index 00000000..9c923ce5 --- /dev/null +++ b/.changeset/nine-dingos-chew.md @@ -0,0 +1,6 @@ +--- +"jsrepo": minor +--- + +feat: Allow for caching using `make-fetch-happen` + \ No newline at end of file diff --git a/.gitignore b/.gitignore index 436a245c..709b3702 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ node_modules dist # testing -temp-test \ No newline at end of file +temp-test + +cache \ No newline at end of file diff --git a/jsrepo.json b/jsrepo.json index 1ce74cd9..f0f3fde7 100644 --- a/jsrepo.json +++ b/jsrepo.json @@ -1,6 +1,6 @@ { "$schema": "https://unpkg.com/jsrepo@1.47.1/schemas/project-config.json", - "repos": ["@ieedan/std"], + "repos": ["@ieedan/std2"], "includeTests": false, "watermark": true, "formatter": "biome", diff --git a/package.json b/package.json index e29eea0b..1cff515f 100644 --- a/package.json +++ b/package.json @@ -15,16 +15,7 @@ "bugs": { "url": "https://github.com/jsrepojs/jsrepo/issues" }, - "keywords": [ - "repo", - "cli", - "svelte", - "vue", - "typescript", - "javascript", - "shadcn", - "registry" - ], + "keywords": ["repo", "cli", "svelte", "vue", "typescript", "javascript", "shadcn", "registry"], "type": "module", "packageManager": "pnpm@10.8.1", "exports": { @@ -35,13 +26,11 @@ }, "bin": "./dist/index.js", "main": "./dist/index.js", - "files": [ - "./schemas/**/*", - "dist/**/*" - ], + "files": ["./schemas/**/*", "dist/**/*"], "scripts": { "start": "tsup --silent && node ./dist/index.js", "build": "tsup", + "run:dev": "node ./dist/index.js", "format": "biome format --write", "lint": "biome lint --write", "check": "biome check && pnpm check:types", @@ -53,6 +42,7 @@ "@biomejs/biome": "1.9.4", "@changesets/cli": "^2.29.2", "@types/diff": "^7.0.2", + "@types/make-fetch-happen": "^10.0.4", "@types/node": "^22.14.0", "@types/semver": "^7.7.0", "@types/validate-npm-package-name": "^4.0.2", @@ -78,7 +68,7 @@ "get-tsconfig": "^4.10.0", "ignore": "^7.0.3", "is-unicode-supported": "^2.1.0", - "node-fetch": "^3.3.2", + "make-fetch-happen": "^14.0.3", "octokit": "^4.1.2", "ollama": "^0.5.14", "openai": "^4.93.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5d5928c4..95938878 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@anthropic-ai/sdk': specifier: ^0.39.0 - version: 0.39.0 + version: 0.39.0(encoding@0.1.13) '@biomejs/js-api': specifier: ^0.7.1 version: 0.7.1(@biomejs/wasm-nodejs@1.9.4) @@ -56,9 +56,9 @@ importers: is-unicode-supported: specifier: ^2.1.0 version: 2.1.0 - node-fetch: - specifier: ^3.3.2 - version: 3.3.2 + make-fetch-happen: + specifier: ^14.0.3 + version: 14.0.3 octokit: specifier: ^4.1.2 version: 4.1.3 @@ -67,7 +67,7 @@ importers: version: 0.5.15 openai: specifier: ^4.93.0 - version: 4.95.0(zod@3.24.3) + version: 4.95.0(encoding@0.1.13)(zod@3.24.3) oxc-parser: specifier: ^0.64.0 version: 0.64.0 @@ -117,6 +117,9 @@ importers: '@types/diff': specifier: ^7.0.2 version: 7.0.2 + '@types/make-fetch-happen': + specifier: ^10.0.4 + version: 10.0.4 '@types/node': specifier: ^22.14.0 version: 22.14.1 @@ -510,6 +513,14 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@npmcli/agent@3.0.0': + resolution: {integrity: sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@npmcli/fs@4.0.0': + resolution: {integrity: sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==} + engines: {node: ^18.17.0 || >=20.5.0} + '@octokit/action@6.1.0': resolution: {integrity: sha512-lo+nHx8kAV86bxvOVOI3vFjX3gXPd/L7guAUbvs3pUvnR2KC+R7yjBkA1uACt4gYhs4LcWP3AXSGQzsbeN2XXw==} engines: {node: '>= 18'} @@ -863,6 +874,9 @@ packages: '@types/estree@1.0.7': resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + '@types/make-fetch-happen@10.0.4': + resolution: {integrity: sha512-jKzweQaEMMAi55ehvR1z0JF6aSVQm/h1BXBhPLOJriaeQBctjw5YbpIGs7zAx9dN0Sa2OO5bcXwCkrlgenoPEA==} + '@types/node-fetch@2.6.12': resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} @@ -875,9 +889,15 @@ packages: '@types/node@22.14.1': resolution: {integrity: sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==} + '@types/retry@0.12.5': + resolution: {integrity: sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw==} + '@types/semver@7.7.0': resolution: {integrity: sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==} + '@types/ssri@7.1.5': + resolution: {integrity: sha512-odD/56S3B51liILSk5aXJlnYt99S6Rt9EFDDqGtJM26rKHApHcwyU/UoYHrzKkdkHMAIquGWCuHtQTbes+FRQw==} + '@types/validate-npm-package-name@4.0.2': resolution: {integrity: sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw==} @@ -948,6 +968,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + agentkeepalive@4.6.0: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} @@ -1051,6 +1075,10 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} + cacache@19.0.1: + resolution: {integrity: sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==} + engines: {node: ^18.17.0 || >=20.5.0} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -1137,10 +1165,6 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} - data-uri-to-buffer@4.0.1: - resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} - engines: {node: '>= 12'} - debounce-fn@6.0.0: resolution: {integrity: sha512-rBMW+F2TXryBwB54Q0d8drNEI+TfoS9JpNTAoVpukbWEhjXQq4rySFYLaqXMFXwdv61Zb2OHtj5bviSoimqxRQ==} engines: {node: '>=18'} @@ -1201,6 +1225,9 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + encoding@0.1.13: + resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + enquirer@2.4.1: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} @@ -1213,6 +1240,9 @@ packages: resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + err-code@2.0.3: + resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -1301,10 +1331,6 @@ packages: picomatch: optional: true - fetch-blob@3.2.0: - resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} - engines: {node: ^12.20 || >= 14.13} - figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} @@ -1336,10 +1362,6 @@ packages: resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} engines: {node: '>= 12.20'} - formdata-polyfill@4.0.10: - resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} - engines: {node: '>=12.20.0'} - fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -1348,6 +1370,10 @@ packages: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} + fs-minipass@3.0.3: + resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -1406,6 +1432,17 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + human-id@4.1.1: resolution: {integrity: sha512-3gKm/gCSUipeLsRYZbbdA1BD83lBoWUkZ7G9VFrhWPAU76KwYo5KR8V28bpoPm/ygy0x5/GCbpRQdY7VLYCoIg==} hasBin: true @@ -1421,6 +1458,10 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -1429,6 +1470,14 @@ packages: resolution: {integrity: sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==} engines: {node: '>= 4'} + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -1486,6 +1535,9 @@ packages: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true + jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -1528,6 +1580,10 @@ packages: magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + make-fetch-happen@14.0.3: + resolution: {integrity: sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==} + engines: {node: ^18.17.0 || >=20.5.0} + math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} @@ -1556,6 +1612,30 @@ packages: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + minipass-collect@2.0.1: + resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass-fetch@4.0.1: + resolution: {integrity: sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass-sized@1.0.3: + resolution: {integrity: sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + minipass@7.1.2: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} @@ -1587,6 +1667,10 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} @@ -1600,10 +1684,6 @@ packages: encoding: optional: true - node-fetch@3.3.2: - resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - npm-run-path@6.0.0: resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} engines: {node: '>=18'} @@ -1661,6 +1741,10 @@ packages: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} + p-map@7.0.3: + resolution: {integrity: sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==} + engines: {node: '>=18'} + p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -1776,6 +1860,14 @@ packages: resolution: {integrity: sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==} engines: {node: '>=18'} + proc-log@5.0.0: + resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + promise-retry@2.0.1: + resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} + engines: {node: '>=10'} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -1820,6 +1912,10 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} @@ -1862,6 +1958,18 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + smart-buffer@4.2.0: + resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} + engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + + socks-proxy-agent@8.0.5: + resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} + engines: {node: '>= 14'} + + socks@2.8.4: + resolution: {integrity: sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1880,6 +1988,13 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + + ssri@12.0.0: + resolution: {integrity: sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==} + engines: {node: ^18.17.0 || >=20.5.0} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -2048,6 +2163,14 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} + unique-filename@4.0.0: + resolution: {integrity: sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==} + engines: {node: ^18.17.0 || >=20.5.0} + + unique-slug@5.0.0: + resolution: {integrity: sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==} + engines: {node: ^18.17.0 || >=20.5.0} + universal-github-app-jwt@2.2.2: resolution: {integrity: sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==} @@ -2162,10 +2285,6 @@ packages: typescript: optional: true - web-streams-polyfill@3.3.3: - resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} - engines: {node: '>= 8'} - web-streams-polyfill@4.0.0-beta.3: resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} engines: {node: '>= 14'} @@ -2217,6 +2336,9 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} @@ -2242,7 +2364,7 @@ snapshots: '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 - '@anthropic-ai/sdk@0.39.0': + '@anthropic-ai/sdk@0.39.0(encoding@0.1.13)': dependencies: '@types/node': 18.19.86 '@types/node-fetch': 2.6.12 @@ -2250,7 +2372,7 @@ snapshots: agentkeepalive: 4.6.0 form-data-encoder: 1.7.2 formdata-node: 4.4.1 - node-fetch: 2.7.0 + node-fetch: 2.7.0(encoding@0.1.13) transitivePeerDependencies: - encoding @@ -2628,6 +2750,20 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 + '@npmcli/agent@3.0.0': + dependencies: + agent-base: 7.1.3 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + lru-cache: 10.4.3 + socks-proxy-agent: 8.0.5 + transitivePeerDependencies: + - supports-color + + '@npmcli/fs@4.0.0': + dependencies: + semver: 7.7.1 + '@octokit/action@6.1.0': dependencies: '@octokit/auth-action': 4.1.0 @@ -2963,6 +3099,12 @@ snapshots: '@types/estree@1.0.7': {} + '@types/make-fetch-happen@10.0.4': + dependencies: + '@types/node-fetch': 2.6.12 + '@types/retry': 0.12.5 + '@types/ssri': 7.1.5 + '@types/node-fetch@2.6.12': dependencies: '@types/node': 22.14.1 @@ -2978,8 +3120,14 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/retry@0.12.5': {} + '@types/semver@7.7.0': {} + '@types/ssri@7.1.5': + dependencies: + '@types/node': 22.14.1 + '@types/validate-npm-package-name@4.0.2': {} '@vitest/expect@3.1.1': @@ -3082,6 +3230,8 @@ snapshots: acorn@8.14.1: {} + agent-base@7.1.3: {} + agentkeepalive@4.6.0: dependencies: humanize-ms: 1.2.1 @@ -3172,6 +3322,21 @@ snapshots: cac@6.7.14: {} + cacache@19.0.1: + dependencies: + '@npmcli/fs': 4.0.0 + fs-minipass: 3.0.3 + glob: 10.4.5 + lru-cache: 10.4.3 + minipass: 7.1.2 + minipass-collect: 2.0.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + p-map: 7.0.3 + ssri: 12.0.0 + tar: 7.4.3 + unique-filename: 4.0.0 + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -3249,8 +3414,6 @@ snapshots: csstype@3.1.3: {} - data-uri-to-buffer@4.0.1: {} - debounce-fn@6.0.0: dependencies: mimic-function: 5.0.1 @@ -3293,6 +3456,11 @@ snapshots: emoji-regex@9.2.2: {} + encoding@0.1.13: + dependencies: + iconv-lite: 0.6.3 + optional: true + enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 @@ -3302,6 +3470,8 @@ snapshots: env-paths@3.0.0: {} + err-code@2.0.3: {} + es-define-property@1.0.1: {} es-errors@1.3.0: {} @@ -3412,11 +3582,6 @@ snapshots: optionalDependencies: picomatch: 4.0.2 - fetch-blob@3.2.0: - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 3.3.3 - figures@6.1.0: dependencies: is-unicode-supported: 2.1.0 @@ -3451,10 +3616,6 @@ snapshots: node-domexception: 1.0.0 web-streams-polyfill: 4.0.0-beta.3 - formdata-polyfill@4.0.10: - dependencies: - fetch-blob: 3.2.0 - fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 @@ -3467,6 +3628,10 @@ snapshots: jsonfile: 4.0.0 universalify: 0.1.2 + fs-minipass@3.0.3: + dependencies: + minipass: 7.1.2 + fsevents@2.3.3: optional: true @@ -3537,6 +3702,22 @@ snapshots: dependencies: function-bind: 1.1.2 + http-cache-semantics@4.1.1: {} + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + transitivePeerDependencies: + - supports-color + human-id@4.1.1: {} human-signals@8.0.1: {} @@ -3549,10 +3730,22 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + optional: true + ignore@5.3.2: {} ignore@7.0.3: {} + imurmurhash@0.1.4: {} + + ip-address@9.0.5: + dependencies: + jsbn: 1.1.0 + sprintf-js: 1.1.3 + is-extglob@2.1.1: {} is-fullwidth-code-point@3.0.0: {} @@ -3596,6 +3789,8 @@ snapshots: argparse: 1.0.10 esprima: 4.0.1 + jsbn@1.1.0: {} + json-schema-traverse@1.0.0: {} json-schema-typed@8.0.1: {} @@ -3628,6 +3823,22 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 + make-fetch-happen@14.0.3: + dependencies: + '@npmcli/agent': 3.0.0 + cacache: 19.0.1 + http-cache-semantics: 4.1.1 + minipass: 7.1.2 + minipass-fetch: 4.0.1 + minipass-flush: 1.0.5 + minipass-pipeline: 1.2.4 + negotiator: 1.0.0 + proc-log: 5.0.0 + promise-retry: 2.0.1 + ssri: 12.0.0 + transitivePeerDependencies: + - supports-color + math-intrinsics@1.1.0: {} merge2@1.4.1: {} @@ -3649,6 +3860,34 @@ snapshots: dependencies: brace-expansion: 2.0.1 + minipass-collect@2.0.1: + dependencies: + minipass: 7.1.2 + + minipass-fetch@4.0.1: + dependencies: + minipass: 7.1.2 + minipass-sized: 1.0.3 + minizlib: 3.0.2 + optionalDependencies: + encoding: 0.1.13 + + minipass-flush@1.0.5: + dependencies: + minipass: 3.3.6 + + minipass-pipeline@1.2.4: + dependencies: + minipass: 3.3.6 + + minipass-sized@1.0.3: + dependencies: + minipass: 3.3.6 + + minipass@3.3.6: + dependencies: + yallist: 4.0.0 + minipass@7.1.2: {} minizlib@3.0.2: @@ -3676,17 +3915,15 @@ snapshots: nanoid@3.3.11: {} + negotiator@1.0.0: {} + node-domexception@1.0.0: {} - node-fetch@2.7.0: + node-fetch@2.7.0(encoding@0.1.13): dependencies: whatwg-url: 5.0.0 - - node-fetch@3.3.2: - dependencies: - data-uri-to-buffer: 4.0.1 - fetch-blob: 3.2.0 - formdata-polyfill: 4.0.10 + optionalDependencies: + encoding: 0.1.13 npm-run-path@6.0.0: dependencies: @@ -3716,7 +3953,7 @@ snapshots: dependencies: wrappy: 1.0.2 - openai@4.95.0(zod@3.24.3): + openai@4.95.0(encoding@0.1.13)(zod@3.24.3): dependencies: '@types/node': 18.19.86 '@types/node-fetch': 2.6.12 @@ -3724,7 +3961,7 @@ snapshots: agentkeepalive: 4.6.0 form-data-encoder: 1.7.2 formdata-node: 4.4.1 - node-fetch: 2.7.0 + node-fetch: 2.7.0(encoding@0.1.13) optionalDependencies: zod: 3.24.3 transitivePeerDependencies: @@ -3763,6 +4000,8 @@ snapshots: p-map@2.1.0: {} + p-map@7.0.3: {} + p-try@2.2.0: {} package-json-from-dist@1.0.1: {} @@ -3847,6 +4086,13 @@ snapshots: dependencies: parse-ms: 4.0.0 + proc-log@5.0.0: {} + + promise-retry@2.0.1: + dependencies: + err-code: 2.0.3 + retry: 0.12.0 + punycode@2.3.1: {} quansync@0.2.10: {} @@ -3887,6 +4133,8 @@ snapshots: resolve-pkg-maps@1.0.0: {} + retry@0.12.0: {} + reusify@1.1.0: {} rollup@4.40.0: @@ -3937,6 +4185,21 @@ snapshots: slash@3.0.0: {} + smart-buffer@4.2.0: {} + + socks-proxy-agent@8.0.5: + dependencies: + agent-base: 7.1.3 + debug: 4.4.0 + socks: 2.8.4 + transitivePeerDependencies: + - supports-color + + socks@2.8.4: + dependencies: + ip-address: 9.0.5 + smart-buffer: 4.2.0 + source-map-js@1.2.1: {} source-map@0.8.0-beta.0: @@ -3952,6 +4215,12 @@ snapshots: sprintf-js@1.0.3: {} + sprintf-js@1.1.3: {} + + ssri@12.0.0: + dependencies: + minipass: 7.1.2 + stackback@0.0.2: {} std-env@3.9.0: {} @@ -4119,6 +4388,14 @@ snapshots: unicorn-magic@0.3.0: {} + unique-filename@4.0.0: + dependencies: + unique-slug: 5.0.0 + + unique-slug@5.0.0: + dependencies: + imurmurhash: 0.1.4 + universal-github-app-jwt@2.2.2: {} universal-user-agent@6.0.1: {} @@ -4218,8 +4495,6 @@ snapshots: optionalDependencies: typescript: 5.8.3 - web-streams-polyfill@3.3.3: {} - web-streams-polyfill@4.0.0-beta.3: {} webidl-conversions@3.0.1: {} @@ -4274,6 +4549,8 @@ snapshots: wrappy@1.0.2: {} + yallist@4.0.0: {} + yallist@5.0.0: {} yoctocolors@2.1.1: {} diff --git a/src/commands/publish.ts b/src/commands/publish.ts index 5c98ba14..acfe26cb 100644 --- a/src/commands/publish.ts +++ b/src/commands/publish.ts @@ -3,7 +3,6 @@ import { log, outro } from '@clack/prompts'; import color from 'chalk'; import { Command, program } from 'commander'; import ignore from 'ignore'; -import fetch from 'node-fetch'; import path from 'pathe'; import semver from 'semver'; import * as tar from 'tar'; @@ -13,6 +12,7 @@ import * as ascii from '../utils/ascii'; import { buildBlocksDirectory, buildConfigFiles, pruneUnused } from '../utils/build'; import { runRules } from '../utils/build/check'; import { IGNORED_DIRS, getRegistryConfig } from '../utils/config'; +import { iFetch } from '../utils/fetch'; import { createManifest } from '../utils/manifest'; import { intro, spinner } from '../utils/prompts'; import * as jsrepo from '../utils/registry-providers/jsrepo'; @@ -290,7 +290,7 @@ async function _publish(options: Options) { // remove archive file fs.rmSync(dest, { force: true, recursive: true }); - const response = await fetch('http://localhost:5173/api/publish', { + const response = await iFetch('http://localhost:5173/api/publish', { body: tarBuffer, headers: { 'content-type': 'application/gzip', diff --git a/src/utils/fetch.ts b/src/utils/fetch.ts new file mode 100644 index 00000000..bf9d5fc3 --- /dev/null +++ b/src/utils/fetch.ts @@ -0,0 +1,4 @@ +import mfFetch from 'make-fetch-happen'; + +/** Fetch method used for (i)nternal consumption */ +export const iFetch = mfFetch.defaults({ cachePath: './cache' }); diff --git a/src/utils/get-latest-version.ts b/src/utils/get-latest-version.ts index 40da2820..dab61cff 100644 --- a/src/utils/get-latest-version.ts +++ b/src/utils/get-latest-version.ts @@ -1,5 +1,5 @@ -import nodeFetch from 'node-fetch'; import { Err, Ok, type Result } from './blocks/ts/result'; +import { iFetch } from './fetch'; import type { Package } from './parse-package-name'; import * as persisted from './persisted'; @@ -35,23 +35,13 @@ export async function getLatestVersion({ } } - // we abort the request after a second - // because it really just isn't worth it if it's going to take that long - const controller = new AbortController(); - - const timeout = setTimeout(() => { - controller.abort(); - }, 1000); - - const response = await nodeFetch( + const response = await iFetch( 'https://raw.githubusercontent.com/jsrepojs/jsrepo/refs/heads/main/packages/cli/package.json', { - signal: controller.signal, + timeout: 1000, } ); - clearTimeout(timeout); - if (!response.ok) { return Err('Error getting version'); } diff --git a/src/utils/registry-providers/index.ts b/src/utils/registry-providers/index.ts index e6b8d033..63f9c667 100644 --- a/src/utils/registry-providers/index.ts +++ b/src/utils/registry-providers/index.ts @@ -20,7 +20,7 @@ export function selectProvider(url: string): RegistryProvider | undefined { export type FetchOptions = { token: string; - /** Override the fetch method. If you are using this in a node environment you will want to pass `node-fetch` */ + /** Override the fetch method. */ fetch?: typeof fetch; verbose: (str: string) => void; }; diff --git a/src/utils/registry-providers/internal.ts b/src/utils/registry-providers/internal.ts index 836e314d..361b7dc7 100644 --- a/src/utils/registry-providers/internal.ts +++ b/src/utils/registry-providers/internal.ts @@ -1,5 +1,4 @@ import color from 'chalk'; -import nodeFetch from 'node-fetch'; import { http, azure, @@ -15,13 +14,14 @@ import { import type { Block, Manifest } from '../../types'; import { Err, Ok, type Result } from '../blocks/ts/result'; import * as u from '../blocks/ts/url'; +import { iFetch } from '../fetch'; import * as persisted from '../persisted'; import { TokenManager } from '../token-manager'; import type { RegistryProvider, RegistryProviderState } from './types'; export type RemoteBlock = Block & { sourceRepo: RegistryProviderState }; -/** Wraps the basic implementation to inject `node-fetch` and the correct token. */ +/** Wraps the basic implementation to inject our internal fetch method and the correct token. */ export async function internalFetchRaw( state: RegistryProviderState, resourcePath: string, @@ -29,21 +29,21 @@ export async function internalFetchRaw( ) { return await fetchRaw(state, resourcePath, { verbose, - // @ts-expect-error but it does work - fetch: nodeFetch, + // @ts-expect-error it's fine + fetch: iFetch, token: getProviderToken(state.provider, state.url), }); } -/** Wraps the basic implementation to inject `node-fetch` and the correct token. */ +/** Wraps the basic implementation to inject internal fetch method and the correct token. */ export async function internalFetchManifest( state: RegistryProviderState, { verbose }: { verbose?: (msg: string) => void } = {} ) { return await fetchManifest(state, { verbose, - // @ts-expect-error but it does work - fetch: nodeFetch, + // @ts-expect-error it's fine + fetch: iFetch, token: getProviderToken(state.provider, state.url), }); } @@ -91,7 +91,7 @@ export async function getProviderState( const state = await provider.state(repo, { token: getProviderToken(provider, parsed.url), // @ts-expect-error but it does work - fetch: nodeFetch, + fetch: iFetch, }); // only cache git providers diff --git a/src/utils/registry-providers/jsrepo.ts b/src/utils/registry-providers/jsrepo.ts index 8558d7d7..236a213d 100644 --- a/src/utils/registry-providers/jsrepo.ts +++ b/src/utils/registry-providers/jsrepo.ts @@ -84,7 +84,9 @@ export const jsrepo: RegistryProvider = { authHeader: (token) => ['x-api-key', token], formatFetchError: (state, filePath, error) => { - return `There was an error fetching ${color.bold(new URL(filePath, state.url).toString())} + const { scope, registryName, version } = state as JsrepoProviderState; + + return `There was an error fetching ${filePath} from ${scope}/${registryName}@${version} ${color.bold(error)}`; }, diff --git a/src/utils/registry-providers/types.ts b/src/utils/registry-providers/types.ts index b4793695..61b182e2 100644 --- a/src/utils/registry-providers/types.ts +++ b/src/utils/registry-providers/types.ts @@ -62,7 +62,7 @@ export type ParseResult = { export type StateOptions = { token?: string; - /** Override the fetch method. If you are using this in a node environment you will want to pass `node-fetch` */ + /** Override the fetch method. */ fetch?: typeof fetch; }; diff --git a/tests/pre-release.test.ts b/tests/pre-release.test.ts index da9f6ee2..e7797c40 100644 --- a/tests/pre-release.test.ts +++ b/tests/pre-release.test.ts @@ -1,8 +1,8 @@ -import { describe, it, expect } from "vitest"; -import { BASE_URL as JSREPO_BASE_URL } from "../src/utils/registry-providers/jsrepo"; +import { describe, expect, it } from 'vitest'; +import { BASE_URL as JSREPO_BASE_URL } from '../src/utils/registry-providers/jsrepo'; describe('JSREPO_BASE_URL', () => { - it('is the correct url', () => { - expect(JSREPO_BASE_URL).toBe('https://jsrepo.com') - }) -}) \ No newline at end of file + it('is the correct url', () => { + expect(JSREPO_BASE_URL).toBe('https://jsrepo.com'); + }); +}); diff --git a/tests/providers.test.ts b/tests/providers.test.ts index 22b297aa..ae1d01d7 100644 --- a/tests/providers.test.ts +++ b/tests/providers.test.ts @@ -1,8 +1,8 @@ import { assert, describe, expect, it } from 'vitest'; import { MANIFEST_FILE } from '../src/constants'; import * as registry from '../src/utils/registry-providers/internal'; -import type { ParseOptions, ParseResult } from '../src/utils/registry-providers/types'; import { BASE_URL as JSREPO_BASE_URL } from '../src/utils/registry-providers/jsrepo'; +import type { ParseOptions, ParseResult } from '../src/utils/registry-providers/types'; type ParseTestCase = { url: string; @@ -1623,13 +1623,11 @@ describe('jsrepo', () => { const cases: StringTestCase[] = [ { url: '@ieedan/std', - expected: - `${JSREPO_BASE_URL}/api/scopes/@ieedan/std/v/latest/files/jsrepo-manifest.json`, + expected: `${JSREPO_BASE_URL}/api/scopes/@ieedan/std/v/latest/files/jsrepo-manifest.json`, }, { url: '@ieedan/std@1.0.0', - expected: - `${JSREPO_BASE_URL}/api/scopes/@ieedan/std/v/1.0.0/files/jsrepo-manifest.json`, + expected: `${JSREPO_BASE_URL}/api/scopes/@ieedan/std/v/1.0.0/files/jsrepo-manifest.json`, }, ]; @@ -1671,6 +1669,6 @@ describe('jsrepo', () => { // this way we just get the text and skip the schema validation const content = await registry.fetchRaw(providerState.unwrap(), 'jsrepo-manifest.json'); - expect(content.isOk()).toBe(true) + expect(content.isOk()).toBe(true); }); }); From de9f8675ae23bff44a9cd526abefa328cd104629 Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Mon, 28 Apr 2025 14:06:27 -0500 Subject: [PATCH 09/18] add build options to publish command --- src/commands/publish.ts | 90 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 83 insertions(+), 7 deletions(-) diff --git a/src/commands/publish.ts b/src/commands/publish.ts index acfe26cb..a8a0b66f 100644 --- a/src/commands/publish.ts +++ b/src/commands/publish.ts @@ -10,8 +10,8 @@ import * as v from 'valibot'; import type { Category } from '../types'; import * as ascii from '../utils/ascii'; import { buildBlocksDirectory, buildConfigFiles, pruneUnused } from '../utils/build'; -import { runRules } from '../utils/build/check'; -import { IGNORED_DIRS, getRegistryConfig } from '../utils/config'; +import { DEFAULT_CONFIG, runRules } from '../utils/build/check'; +import { IGNORED_DIRS, type RegistryConfig, getRegistryConfig } from '../utils/config'; import { iFetch } from '../utils/fetch'; import { createManifest } from '../utils/manifest'; import { intro, spinner } from '../utils/prompts'; @@ -21,6 +21,20 @@ import { TokenManager } from '../utils/token-manager'; const schema = v.object({ private: v.boolean(), dryRun: v.boolean(), + name: v.optional(v.string()), + ver: v.optional(v.string()), + dirs: v.optional(v.array(v.string())), + outputDir: v.optional(v.string()), + includeBlocks: v.optional(v.array(v.string())), + includeCategories: v.optional(v.array(v.string())), + excludeBlocks: v.optional(v.array(v.string())), + excludeCategories: v.optional(v.array(v.string())), + excludeDeps: v.optional(v.array(v.string())), + listBlocks: v.optional(v.array(v.string())), + listCategories: v.optional(v.array(v.string())), + doNotListBlocks: v.optional(v.array(v.string())), + doNotListCategories: v.optional(v.array(v.string())), + allowSubdirectories: v.optional(v.boolean()), verbose: v.boolean(), cwd: v.string(), }); @@ -35,6 +49,32 @@ export const publish = new Command('publish') false ) .option('--dry-run', "Test the publish but don't list on jsrepo.com.", false) + .option('--name ', 'The name of the registry. i.e. @ieedan/std') + .option('--ver ', 'The version of the registry. i.e. 0.0.1') + .option('--dirs [dirs...]', 'The directories containing the blocks.') + .option( + '--output-dir ', + 'The directory to output the registry to. (Copies jsrepo-manifest.json + all required files)' + ) + .option('--include-blocks [blockNames...]', 'Include only the blocks with these names.') + .option( + '--include-categories [categoryNames...]', + 'Include only the categories with these names.' + ) + .option('--exclude-blocks [blockNames...]', 'Do not include the blocks with these names.') + .option( + '--exclude-categories [categoryNames...]', + 'Do not include the categories with these names.' + ) + .option('--list-blocks [blockNames...]', 'List only the blocks with these names.') + .option('--list-categories [categoryNames...]', 'List only the categories with these names.') + .option('--do-not-list-blocks [blockNames...]', 'Do not list the blocks with these names.') + .option( + '--do-not-list-categories [categoryNames...]', + 'Do not list the categories with these names.' + ) + .option('--exclude-deps [deps...]', 'Dependencies that should not be added.') + .option('--allow-subdirectories', 'Allow subdirectories to be built.') .option('--verbose', 'Include debug logs.', false) .option('--cwd ', 'The current working directory.', process.cwd()) .action(async (opts) => { @@ -56,15 +96,51 @@ async function _publish(options: Options) { const loading = spinner({ verbose: options.verbose ? verbose : undefined }); - const config = getRegistryConfig(options.cwd).match( + const config: RegistryConfig = getRegistryConfig(options.cwd).match( (val) => { if (val === null) { - program.error( - color.red(`Publishing to ${color.bold('jsrepo.com')} requires a config.`) - ); + return { + $schema: '', + readme: 'README.md', + dirs: options.dirs ?? [], + outputDir: options.outputDir, + doNotListBlocks: options.doNotListBlocks ?? [], + doNotListCategories: options.doNotListCategories ?? [], + listBlocks: options.listBlocks ?? [], + listCategories: options.listCategories ?? [], + excludeDeps: options.excludeDeps ?? [], + includeBlocks: options.includeBlocks ?? [], + includeCategories: options.includeCategories ?? [], + excludeBlocks: options.excludeBlocks ?? [], + excludeCategories: options.excludeCategories ?? [], + allowSubdirectories: options.allowSubdirectories, + } satisfies RegistryConfig; } - return val; + const mergedVal = val; + + // overwrites config with flag values + + if (options.name) mergedVal.name = options.name; + if (options.ver) mergedVal.version = options.ver; + if (options.dirs) mergedVal.dirs = options.dirs; + if (options.outputDir) mergedVal.outputDir = options.outputDir; + if (options.doNotListBlocks) mergedVal.doNotListBlocks = options.doNotListBlocks; + if (options.doNotListCategories) + mergedVal.doNotListCategories = options.doNotListCategories; + if (options.listBlocks) mergedVal.listBlocks = options.listBlocks; + if (options.listCategories) mergedVal.listCategories = options.listCategories; + if (options.includeBlocks) mergedVal.includeBlocks = options.includeBlocks; + if (options.includeCategories) mergedVal.includeCategories = options.includeCategories; + if (options.excludeBlocks) mergedVal.excludeBlocks = options.excludeBlocks; + if (options.excludeCategories) mergedVal.excludeCategories = options.excludeCategories; + if (options.excludeDeps) mergedVal.excludeDeps = options.excludeDeps; + if (options.allowSubdirectories !== undefined) + mergedVal.allowSubdirectories = options.allowSubdirectories; + + mergedVal.rules = { ...DEFAULT_CONFIG, ...mergedVal.rules }; + + return mergedVal; }, (err) => program.error(color.red(err)) ); From 32464a3838f2d3de2c6d860a44e8fc183e5f49b8 Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Mon, 28 Apr 2025 14:12:03 -0500 Subject: [PATCH 10/18] Update publish.ts --- src/commands/publish.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/commands/publish.ts b/src/commands/publish.ts index a8a0b66f..8c8b071d 100644 --- a/src/commands/publish.ts +++ b/src/commands/publish.ts @@ -24,7 +24,6 @@ const schema = v.object({ name: v.optional(v.string()), ver: v.optional(v.string()), dirs: v.optional(v.array(v.string())), - outputDir: v.optional(v.string()), includeBlocks: v.optional(v.array(v.string())), includeCategories: v.optional(v.array(v.string())), excludeBlocks: v.optional(v.array(v.string())), @@ -52,10 +51,6 @@ export const publish = new Command('publish') .option('--name ', 'The name of the registry. i.e. @ieedan/std') .option('--ver ', 'The version of the registry. i.e. 0.0.1') .option('--dirs [dirs...]', 'The directories containing the blocks.') - .option( - '--output-dir ', - 'The directory to output the registry to. (Copies jsrepo-manifest.json + all required files)' - ) .option('--include-blocks [blockNames...]', 'Include only the blocks with these names.') .option( '--include-categories [categoryNames...]', @@ -103,7 +98,6 @@ async function _publish(options: Options) { $schema: '', readme: 'README.md', dirs: options.dirs ?? [], - outputDir: options.outputDir, doNotListBlocks: options.doNotListBlocks ?? [], doNotListCategories: options.doNotListCategories ?? [], listBlocks: options.listBlocks ?? [], @@ -124,7 +118,6 @@ async function _publish(options: Options) { if (options.name) mergedVal.name = options.name; if (options.ver) mergedVal.version = options.ver; if (options.dirs) mergedVal.dirs = options.dirs; - if (options.outputDir) mergedVal.outputDir = options.outputDir; if (options.doNotListBlocks) mergedVal.doNotListBlocks = options.doNotListBlocks; if (options.doNotListCategories) mergedVal.doNotListCategories = options.doNotListCategories; From 1db27775bd2a9f13f0562b50d7ed948f06604251 Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Mon, 28 Apr 2025 14:52:24 -0500 Subject: [PATCH 11/18] allow users to specify package instead of a version to pull the version from package.json --- schemas/registry-config.json | 4 ++-- src/commands/publish.ts | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/schemas/registry-config.json b/schemas/registry-config.json index 0fcd8579..1a25d9ac 100644 --- a/schemas/registry-config.json +++ b/schemas/registry-config.json @@ -9,9 +9,9 @@ }, "version": { "type": "string", - "description": "A semver compatible version of the registry on jsrepo.com", + "description": "A semver compatible version of the registry on jsrepo.com or `package` to use the version from the `package.json`", "format": "semver", - "examples": ["1.0.0"] + "examples": ["1.0.0", "package"] }, "readme": { "type": "string", diff --git a/src/commands/publish.ts b/src/commands/publish.ts index 8c8b071d..6986056a 100644 --- a/src/commands/publish.ts +++ b/src/commands/publish.ts @@ -14,6 +14,7 @@ import { DEFAULT_CONFIG, runRules } from '../utils/build/check'; import { IGNORED_DIRS, type RegistryConfig, getRegistryConfig } from '../utils/config'; import { iFetch } from '../utils/fetch'; import { createManifest } from '../utils/manifest'; +import type { PackageJson } from '../utils/package'; import { intro, spinner } from '../utils/prompts'; import * as jsrepo from '../utils/registry-providers/jsrepo'; import { TokenManager } from '../utils/token-manager'; @@ -181,6 +182,19 @@ async function _publish(options: Options) { // check version if (config.version !== undefined) { + // use version from package.json + if (config.version === 'package') { + const packagePath = path.join(options.cwd, 'package.json'); + + if (!fs.existsSync(packagePath)) { + program.error(color.red(`Couldn't find your ${color.bold('package.json')}!`)); + } + + const { version } = JSON.parse(fs.readFileSync(packagePath).toString()) as PackageJson; + + config.version = version; + } + const valid = semver.valid(config.version); if (!valid) { From 5b1febc3d2ab0ba0e55d1dadac9f9a66e9eb1287 Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Mon, 28 Apr 2025 14:55:38 -0500 Subject: [PATCH 12/18] Update jsrepo.ts --- src/utils/registry-providers/jsrepo.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/registry-providers/jsrepo.ts b/src/utils/registry-providers/jsrepo.ts index 236a213d..09f4e7d3 100644 --- a/src/utils/registry-providers/jsrepo.ts +++ b/src/utils/registry-providers/jsrepo.ts @@ -22,7 +22,7 @@ import type { ParseOptions, RegistryProvider, RegistryProviderState } from './ty */ export const NAME_REGEX = /^(?![-0-9])(?!.*--)[a-z0-9]*(?:-[a-z0-9]+)*$/gi; -export const BASE_URL = 'http://localhost:5173'; +export const BASE_URL = 'https://platform-jsrepo.vercel.app'; export interface JsrepoProviderState extends RegistryProviderState { scope: string; From c75e50015e1a62610f0313950f4dcaf50f85520d Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Mon, 28 Apr 2025 15:23:32 -0500 Subject: [PATCH 13/18] better logging and fix error --- src/commands/publish.ts | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/commands/publish.ts b/src/commands/publish.ts index 6986056a..a232b0f7 100644 --- a/src/commands/publish.ts +++ b/src/commands/publish.ts @@ -300,8 +300,12 @@ async function _publish(options: Options) { const tempOutDir = path.resolve(options.cwd, `jsrepo-publish-temp-${Date.now()}`); + verbose(`Creating temp dir: ${tempOutDir}`); + fs.mkdirSync(tempOutDir, { recursive: true }); + verbose('Writing manifest to temp dir'); + // write manifest fs.writeFileSync(path.resolve(tempOutDir, 'jsrepo-manifest.json'), JSON.stringify(manifest)); @@ -309,13 +313,20 @@ async function _publish(options: Options) { const readmePath = path.resolve(options.cwd, config.readme); try { + verbose('Attempting to copy readme'); + fs.copyFileSync(readmePath, path.join(tempOutDir, 'README.md')); + + verbose('Copied readme'); } catch { // do nothing it's okay + verbose('No readme found.'); } // copy config files to output directory if (manifest.configFiles) { + verbose('Copying config files'); + for (const file of manifest.configFiles) { const originalPath = path.join(options.cwd, file.path); const destPath = path.join(tempOutDir, file.path); @@ -328,8 +339,12 @@ async function _publish(options: Options) { fs.copyFileSync(originalPath, destPath); } + + verbose('Copied config files'); } + verbose('Copying registry files'); + // copy the files for each block in each category for (const category of manifest.categories) { for (const block of category.blocks) { @@ -348,10 +363,14 @@ async function _publish(options: Options) { } } + verbose('Copied registry files'); + const dest = path.resolve(options.cwd, `${config.name.replace('/', '_')}-package.tar.gz`); const files = fs.readdirSync(tempOutDir); + verbose('Creating archive file'); + await tar.create( { z: true, @@ -361,19 +380,31 @@ async function _publish(options: Options) { files ); + verbose('Created archive file'); + + verbose('Removing temp directory'); + // remove temp directory fs.rmSync(tempOutDir, { force: true, recursive: true }); + verbose('Removed temp directory'); + loading.stop(`Created package ${color.cyan(dest)}...`); loading.start(`Publishing ${color.bold(manifest.name)} to ${ascii.JSREPO_DOT_COM}...`); const tarBuffer = fs.readFileSync(dest); + verbose('Removing archive file'); + // remove archive file fs.rmSync(dest, { force: true, recursive: true }); - const response = await iFetch('http://localhost:5173/api/publish', { + verbose('Removed archive file'); + + verbose(`Publishing to ${`${jsrepo.BASE_URL}/api/publish`}`); + + const response = await iFetch(`${jsrepo.BASE_URL}/api/publish`, { body: tarBuffer, headers: { 'content-type': 'application/gzip', From 09455471fc380d8772897a6aa70fb7ab2c3d30ea Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Thu, 1 May 2025 18:14:54 -0500 Subject: [PATCH 14/18] sign into jsrepo.com the nice way --- .changeset/clear-rooms-start.md | 6 + jsrepo.json | 2 +- package.json | 1 + pnpm-lock.yaml | 8 + src/cli.ts | 1 + src/commands/auth.ts | 244 +++++++------------------ src/commands/index.ts | 3 +- src/commands/tokens.ts | 222 ++++++++++++++++++++++ src/utils/blocks/ts/sleep.ts | 21 +++ src/utils/blocks/ts/time.ts | 39 ++++ src/utils/registry-providers/index.ts | 7 +- src/utils/registry-providers/jsrepo.ts | 2 +- 12 files changed, 376 insertions(+), 180 deletions(-) create mode 100644 .changeset/clear-rooms-start.md create mode 100644 src/commands/tokens.ts create mode 100644 src/utils/blocks/ts/sleep.ts create mode 100644 src/utils/blocks/ts/time.ts diff --git a/.changeset/clear-rooms-start.md b/.changeset/clear-rooms-start.md new file mode 100644 index 00000000..9d089a99 --- /dev/null +++ b/.changeset/clear-rooms-start.md @@ -0,0 +1,6 @@ +--- +"jsrepo": major +--- + +breaking: original `auth` command is now `tokens` and `auth` command is now just for jsrepo.com. + \ No newline at end of file diff --git a/jsrepo.json b/jsrepo.json index f0f3fde7..1ce74cd9 100644 --- a/jsrepo.json +++ b/jsrepo.json @@ -1,6 +1,6 @@ { "$schema": "https://unpkg.com/jsrepo@1.47.1/schemas/project-config.json", - "repos": ["@ieedan/std2"], + "repos": ["@ieedan/std"], "includeTests": false, "watermark": true, "formatter": "biome", diff --git a/package.json b/package.json index 1cff515f..4ab52969 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "ignore": "^7.0.3", "is-unicode-supported": "^2.1.0", "make-fetch-happen": "^14.0.3", + "node-machine-id": "^1.1.12", "octokit": "^4.1.2", "ollama": "^0.5.14", "openai": "^4.93.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 95938878..33ce23d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -129,6 +129,9 @@ importers: '@types/validate-npm-package-name': specifier: ^4.0.2 version: 4.0.2 + node-machine-id: + specifier: ^1.1.12 + version: 1.1.12 pkg-pr-new: specifier: ^0.0.42 version: 0.0.42 @@ -1684,6 +1687,9 @@ packages: encoding: optional: true + node-machine-id@1.1.12: + resolution: {integrity: sha512-QNABxbrPa3qEIfrE6GOJ7BYIuignnJw7iQ2YPbc3Nla1HzRJjXzZOiikfF8m7eAMfichLt3M4VgLOetqgDmgGQ==} + npm-run-path@6.0.0: resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} engines: {node: '>=18'} @@ -3925,6 +3931,8 @@ snapshots: optionalDependencies: encoding: 0.1.13 + node-machine-id@1.1.12: {} + npm-run-path@6.0.0: dependencies: path-key: 4.0.0 diff --git a/src/cli.ts b/src/cli.ts index 77a86c3d..7b688720 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -13,6 +13,7 @@ const cli = program .addCommand(commands.init) .addCommand(commands.publish) .addCommand(commands.test) + .addCommand(commands.tokens) .addCommand(commands.update); export { cli }; diff --git a/src/commands/auth.ts b/src/commands/auth.ts index d73a308d..d2344f98 100644 --- a/src/commands/auth.ts +++ b/src/commands/auth.ts @@ -1,10 +1,13 @@ -import { cancel, confirm, isCancel, log, outro, password, select, text } from '@clack/prompts'; +import { cancel, confirm, isCancel, log, outro } from '@clack/prompts'; import color from 'chalk'; -import { Argument, Command } from 'commander'; +import { Command, program } from 'commander'; +import nodeMachineId from 'node-machine-id'; import * as v from 'valibot'; -import { getProjectConfig } from '../utils/config'; -import { intro } from '../utils/prompts'; -import { http } from '../utils/registry-providers'; +import * as ASCII from '../utils/ascii'; +import { sleep } from '../utils/blocks/ts/sleep'; +import { iFetch } from '../utils/fetch'; +import { intro, spinner } from '../utils/prompts'; +import * as jsrepo from '../utils/registry-providers/jsrepo'; import { TokenManager } from '../utils/token-manager'; const schema = v.object({ @@ -15,217 +18,110 @@ const schema = v.object({ type Options = v.InferInput; -const services = [ - 'Anthropic', - 'Azure', - 'BitBucket', - 'GitHub', - 'GitLab', - 'jsrepo', - 'OpenAI', - 'http', -].sort(); - export const auth = new Command('auth') - .description('Provide a token for access to private repositories.') - .addArgument( - new Argument('service', 'The service you want to authenticate to.') - .choices(services.map((s) => s.toLowerCase())) - .argOptional() - ) + .description('Authenticate to jsrepo.com') .option('--logout', 'Execute the logout flow.', false) .option('--token ', 'The token to use for authenticating to this service.') .option('--cwd ', 'The current working directory.', process.cwd()) - .action(async (service, opts) => { + .action(async (opts) => { const options = v.parse(schema, opts); await intro(); - await _auth(service, options); + await _auth(options); outro(color.green('All done!')); }); -async function _auth(service: string | undefined, options: Options) { - const configuredRegistries: string[] = getProjectConfig(options.cwd).match( - (v) => v.repos.filter(http.matches), - () => [] - ); - - let selectedService = services.find((s) => s.toLowerCase() === service?.toLowerCase()); - - const storage = new TokenManager(); +async function _auth(options: Options) { + const tokenManager = new TokenManager(); - // logout flow if (options.logout) { - if (selectedService !== undefined) { - if (selectedService === 'http') { - await promptHttpLogout(storage); - - return; - } - - storage.delete(selectedService); - log.success(`Logged out of ${selectedService}.`); - return; - } - - for (const serviceName of services) { - if (serviceName === 'http') { - await promptHttpLogout(storage); - continue; - } - - if (storage.get(serviceName) === undefined) { - log.step(color.gray(`Already logged out of ${color.bold(serviceName)}.`)); - continue; - } - - const response = await confirm({ - message: `Logout of ${color.bold(serviceName)}?`, - initialValue: true, - }); - - if (isCancel(response)) { - cancel('Canceled!'); - process.exit(0); - } - - if (!response) continue; - - storage.delete(serviceName); - } + tokenManager.delete('jsrepo'); + log.success(`Logged out of ${ASCII.JSREPO_DOT_COM}!`); + return; + } + if (options.token !== undefined) { + tokenManager.set('jsrepo', options.token); + log.success(`Logged into ${ASCII.JSREPO_DOT_COM}!`); return; } - // login flow - if (selectedService === undefined) { - const response = await select({ - message: 'Which service do you want to authenticate to?', - options: services.map((serviceName) => ({ - label: serviceName, - value: serviceName, - })), - initialValue: services[0], + if (tokenManager.get('jsrepo') !== undefined) { + const result = await confirm({ + message: 'You are currently signed into jsrepo do you want to sign out?', + initialValue: false, }); - if (isCancel(response)) { + if (isCancel(result) || !result) { cancel('Canceled!'); process.exit(0); } - - selectedService = response; - - if (selectedService === 'http') { - let selectedRegistry = 'Other'; - - if (configuredRegistries.length > 0) { - configuredRegistries.push('Other'); - - const response = await select({ - message: 'Which registry do you want to authenticate to?', - options: configuredRegistries.map((serviceName) => ({ - label: serviceName, - value: serviceName, - })), - initialValue: services[0], - }); - - if (isCancel(response)) { - cancel('Canceled!'); - process.exit(0); - } - - selectedRegistry = new URL(response).origin; - } - - // prompt for registry - if (selectedRegistry === 'Other') { - const response = await text({ - message: 'Please enter the registry url you want to authenticate to:', - placeholder: 'https://example.com', - validate(value) { - if (value.trim() === '') return 'Please provide a value'; - - try { - // try to parse the url - new URL(value); - } catch { - // if parsing fails return the error - return 'Please provide a valid url'; - } - }, - }); - - if (isCancel(response)) { - cancel('Canceled!'); - process.exit(0); - } - - selectedRegistry = new URL(response).origin; - } - - selectedService = `http-${selectedRegistry}`; - } } - let serviceName = selectedService; + const hardwareId = nodeMachineId.machineIdSync(); - if (serviceName.startsWith('http')) { - serviceName = serviceName.slice(5); - } + let anonSessionId: string; - if (options.token === undefined) { - const response = await password({ - message: `Paste your token for ${color.bold(serviceName)}:`, - validate(value) { - if (value.trim() === '') return 'Please provide a value'; - }, + try { + const response = await iFetch(`${jsrepo.BASE_URL}/api/login/device`, { + method: 'POST', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ hardwareId }), }); - if (isCancel(response) || !response) { - cancel('Canceled!'); - process.exit(0); + if (!response.ok) { + throw new Error('There was an error creating the session'); } - options.token = response; + const res = await response.json(); + + anonSessionId = res.id; + } catch (err) { + program.error(color.red(err)); } - storage.set(selectedService, options.token); + log.step(`Sign in at ${color.cyan(`${jsrepo.BASE_URL}/login/device/${anonSessionId}`)}`); - log.success(`Logged into ${color.bold(serviceName)}.`); -} + const timeout = 1000 * 60 * 60 * 15; // 15 minutes -async function promptHttpLogout(storage: TokenManager) { - // list all providers for logout - const registries = storage.getHttpRegistriesWithTokens(); + const loading = spinner(); - if (registries.length === 0) { - log.step(color.gray(`Already logged out of ${color.bold('http')}.`)); - } + const pollingTimeout = setTimeout(() => { + loading.stop('You never signed in.'); - for (const registry of registries) { - let registryUrl: URL; + program.error(color.red('Session timed out try again!')); + }, timeout); + + loading.start('Waiting for you to sign in...'); + + while (true) { + // wait initially cause c'mon ain't no way + await sleep(5000); // wait 5 seconds + + const endpoint = `${jsrepo.BASE_URL}/api/login/device/${anonSessionId}`; try { - registryUrl = new URL(registry); - } catch { - continue; - } + const response = await iFetch(endpoint, { + method: 'PATCH', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ hardwareId }), + }); - const response = await confirm({ - message: `Logout of ${color.bold(registryUrl.origin)}?`, - initialValue: true, - }); + if (!response.ok) continue; - if (isCancel(response)) { - cancel('Canceled!'); - process.exit(0); - } + clearTimeout(pollingTimeout); - if (!response) continue; + const key = await response.text(); - storage.delete(`http-${registryUrl.origin}`); + tokenManager.set('jsrepo', key); + + loading.stop(`Logged into ${ASCII.JSREPO_DOT_COM}!`); + + break; + } catch { + // continue + } } } diff --git a/src/commands/index.ts b/src/commands/index.ts index c6e50395..8df67036 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -5,6 +5,7 @@ import { exec } from './exec'; import { init } from './init'; import { publish } from './publish'; import { test } from './test'; +import { tokens } from './tokens'; import { update } from './update'; -export { add, auth, build, exec, init, publish, test, update }; +export { add, auth, tokens, build, exec, init, publish, test, update }; diff --git a/src/commands/tokens.ts b/src/commands/tokens.ts new file mode 100644 index 00000000..a5ea0566 --- /dev/null +++ b/src/commands/tokens.ts @@ -0,0 +1,222 @@ +import { cancel, confirm, isCancel, log, outro, password, select, text } from '@clack/prompts'; +import color from 'chalk'; +import { Argument, Command } from 'commander'; +import * as v from 'valibot'; +import { getProjectConfig } from '../utils/config'; +import { intro } from '../utils/prompts'; +import { http } from '../utils/registry-providers'; +import { TokenManager } from '../utils/token-manager'; + +const schema = v.object({ + token: v.optional(v.string()), + logout: v.boolean(), + cwd: v.string(), +}); + +type Options = v.InferInput; + +const services = ['Anthropic', 'Azure', 'BitBucket', 'GitHub', 'GitLab', 'OpenAI', 'http'].sort(); + +export const tokens = new Command('tokens') + .description('Provide a token for access to private repositories.') + .addArgument( + new Argument('service', 'The service you want to authenticate to.') + .choices(services.map((s) => s.toLowerCase())) + .argOptional() + ) + .option('--logout', 'Execute the logout flow.', false) + .option('--token ', 'The token to use for authenticating to this service.') + .option('--cwd ', 'The current working directory.', process.cwd()) + .action(async (service, opts) => { + const options = v.parse(schema, opts); + + await intro(); + + await _tokens(service, options); + + outro(color.green('All done!')); + }); + +async function _tokens(service: string | undefined, options: Options) { + const configuredRegistries: string[] = getProjectConfig(options.cwd).match( + (v) => v.repos.filter(http.matches), + () => [] + ); + + let selectedService = services.find((s) => s.toLowerCase() === service?.toLowerCase()); + + const storage = new TokenManager(); + + // logout flow + if (options.logout) { + if (selectedService !== undefined) { + if (selectedService === 'http') { + await promptHttpLogout(storage); + + return; + } + + storage.delete(selectedService); + log.success(`Logged out of ${selectedService}.`); + return; + } + + for (const serviceName of services) { + if (serviceName === 'http') { + await promptHttpLogout(storage); + continue; + } + + if (storage.get(serviceName) === undefined) { + log.step(color.gray(`Already logged out of ${color.bold(serviceName)}.`)); + continue; + } + + const response = await confirm({ + message: `Logout of ${color.bold(serviceName)}?`, + initialValue: true, + }); + + if (isCancel(response)) { + cancel('Canceled!'); + process.exit(0); + } + + if (!response) continue; + + storage.delete(serviceName); + } + + return; + } + + // login flow + if (selectedService === undefined) { + const response = await select({ + message: 'Which service do you want to authenticate to?', + options: services.map((serviceName) => ({ + label: serviceName, + value: serviceName, + })), + initialValue: services[0], + }); + + if (isCancel(response)) { + cancel('Canceled!'); + process.exit(0); + } + + selectedService = response; + + if (selectedService === 'http') { + let selectedRegistry = 'Other'; + + if (configuredRegistries.length > 0) { + configuredRegistries.push('Other'); + + const response = await select({ + message: 'Which registry do you want to authenticate to?', + options: configuredRegistries.map((serviceName) => ({ + label: serviceName, + value: serviceName, + })), + initialValue: services[0], + }); + + if (isCancel(response)) { + cancel('Canceled!'); + process.exit(0); + } + + selectedRegistry = new URL(response).origin; + } + + // prompt for registry + if (selectedRegistry === 'Other') { + const response = await text({ + message: 'Please enter the registry url you want to authenticate to:', + placeholder: 'https://example.com', + validate(value) { + if (value.trim() === '') return 'Please provide a value'; + + try { + // try to parse the url + new URL(value); + } catch { + // if parsing fails return the error + return 'Please provide a valid url'; + } + }, + }); + + if (isCancel(response)) { + cancel('Canceled!'); + process.exit(0); + } + + selectedRegistry = new URL(response).origin; + } + + selectedService = `http-${selectedRegistry}`; + } + } + + let serviceName = selectedService; + + if (serviceName.startsWith('http')) { + serviceName = serviceName.slice(5); + } + + if (options.token === undefined) { + const response = await password({ + message: `Paste your token for ${color.bold(serviceName)}:`, + validate(value) { + if (value.trim() === '') return 'Please provide a value'; + }, + }); + + if (isCancel(response) || !response) { + cancel('Canceled!'); + process.exit(0); + } + + options.token = response; + } + + storage.set(selectedService, options.token); + + log.success(`Logged into ${color.bold(serviceName)}.`); +} + +async function promptHttpLogout(storage: TokenManager) { + // list all providers for logout + const registries = storage.getHttpRegistriesWithTokens(); + + if (registries.length === 0) { + log.step(color.gray(`Already logged out of ${color.bold('http')}.`)); + } + + for (const registry of registries) { + let registryUrl: URL; + + try { + registryUrl = new URL(registry); + } catch { + continue; + } + + const response = await confirm({ + message: `Logout of ${color.bold(registryUrl.origin)}?`, + initialValue: true, + }); + + if (isCancel(response)) { + cancel('Canceled!'); + process.exit(0); + } + + if (!response) continue; + + storage.delete(`http-${registryUrl.origin}`); + } +} diff --git a/src/utils/blocks/ts/sleep.ts b/src/utils/blocks/ts/sleep.ts new file mode 100644 index 00000000..0c6b4c61 --- /dev/null +++ b/src/utils/blocks/ts/sleep.ts @@ -0,0 +1,21 @@ +/* + Installed from @ieedan/std +*/ + +/** Await this to pause execution until the duration has passed. + * + * @param durationMs The duration in ms until the sleep in over + * @returns + * + * ## Usage + * ```ts + * console.log(Date.now()) // 1725739228744 + * + * await sleep(1000); + * + * console.log(Date.now()) // 1725739229744 + * ``` + */ +export function sleep(durationMs: number): Promise { + return new Promise((res) => setTimeout(res, durationMs)); +} diff --git a/src/utils/blocks/ts/time.ts b/src/utils/blocks/ts/time.ts new file mode 100644 index 00000000..b5b81652 --- /dev/null +++ b/src/utils/blocks/ts/time.ts @@ -0,0 +1,39 @@ +/* + Installed from @ieedan/std +*/ + +/** Milliseconds in a second */ +export const SECOND = 1000; +/** Milliseconds in a minute */ +export const MINUTE = SECOND * 60; +/** Milliseconds in an hour */ +export const HOUR = MINUTE * 60; +/** Milliseconds in a day */ +export const DAY = HOUR * 24; + +/** Formats a time given in milliseconds with units. + * + * @param durationMs Time to be formatted in milliseconds + * @param transform Runs before the num is formatted perfect place to put a `.toFixed()` + * @returns + * + * ## Usage + * ```ts + * formatDuration(500); // 500ms + * formatDuration(SECOND); // 1s + * formatDuration(MINUTE); // 1min + * formatDuration(HOUR); // 1h + * ``` + */ +export function formatDuration( + durationMs: number, + transform: (num: number) => string = (num) => num.toString() +): string { + if (durationMs < SECOND) return `${transform(durationMs)}ms`; + + if (durationMs < MINUTE) return `${transform(durationMs / SECOND)}s`; + + if (durationMs < HOUR) return `${transform(durationMs / MINUTE)}min`; + + return `${durationMs / HOUR}h`; +} diff --git a/src/utils/registry-providers/index.ts b/src/utils/registry-providers/index.ts index 63f9c667..ed045554 100644 --- a/src/utils/registry-providers/index.ts +++ b/src/utils/registry-providers/index.ts @@ -35,15 +35,16 @@ export async function fetchRaw( verbose?.(`Trying to fetch from ${url}`); try { - const headers = new Headers(); + // having headers as a record covers more fetch implementations + const headers: Record = {}; if (token !== undefined && state.provider.authHeader) { const [key, value] = state.provider.authHeader(token); - headers.append(key, value); + headers[key] = value; } - const response = await f(url, { headers }); + const response = await f(url.toString(), { headers }); verbose?.(`Got a response from ${url} ${response.status} ${response.statusText}`); diff --git a/src/utils/registry-providers/jsrepo.ts b/src/utils/registry-providers/jsrepo.ts index 09f4e7d3..236a213d 100644 --- a/src/utils/registry-providers/jsrepo.ts +++ b/src/utils/registry-providers/jsrepo.ts @@ -22,7 +22,7 @@ import type { ParseOptions, RegistryProvider, RegistryProviderState } from './ty */ export const NAME_REGEX = /^(?![-0-9])(?!.*--)[a-z0-9]*(?:-[a-z0-9]+)*$/gi; -export const BASE_URL = 'https://platform-jsrepo.vercel.app'; +export const BASE_URL = 'http://localhost:5173'; export interface JsrepoProviderState extends RegistryProviderState { scope: string; From 0da0f0786a59b045a01f3bf548683646c7bf2e95 Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Fri, 2 May 2025 14:21:22 -0500 Subject: [PATCH 15/18] Update auth.ts --- src/commands/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/auth.ts b/src/commands/auth.ts index d2344f98..94d4b02d 100644 --- a/src/commands/auth.ts +++ b/src/commands/auth.ts @@ -60,7 +60,7 @@ async function _auth(options: Options) { } } - const hardwareId = nodeMachineId.machineIdSync(); + const hardwareId = nodeMachineId.machineIdSync(true); let anonSessionId: string; From a70d69dbd1827301445d1eaf94da4e35204ca246 Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Fri, 2 May 2025 15:12:25 -0500 Subject: [PATCH 16/18] Update jsrepo.ts --- src/utils/registry-providers/jsrepo.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/registry-providers/jsrepo.ts b/src/utils/registry-providers/jsrepo.ts index 236a213d..09f4e7d3 100644 --- a/src/utils/registry-providers/jsrepo.ts +++ b/src/utils/registry-providers/jsrepo.ts @@ -22,7 +22,7 @@ import type { ParseOptions, RegistryProvider, RegistryProviderState } from './ty */ export const NAME_REGEX = /^(?![-0-9])(?!.*--)[a-z0-9]*(?:-[a-z0-9]+)*$/gi; -export const BASE_URL = 'http://localhost:5173'; +export const BASE_URL = 'https://platform-jsrepo.vercel.app'; export interface JsrepoProviderState extends RegistryProviderState { scope: string; From b3ef51e5a3cdbcb5f5c58aefba19a69086350e8e Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Fri, 2 May 2025 15:14:21 -0500 Subject: [PATCH 17/18] bump deps --- package.json | 34 ++++-- pnpm-lock.yaml | 319 +++++++++++++++++++++++++++---------------------- 2 files changed, 197 insertions(+), 156 deletions(-) diff --git a/package.json b/package.json index 4ab52969..1a611667 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,16 @@ "bugs": { "url": "https://github.com/jsrepojs/jsrepo/issues" }, - "keywords": ["repo", "cli", "svelte", "vue", "typescript", "javascript", "shadcn", "registry"], + "keywords": [ + "repo", + "cli", + "svelte", + "vue", + "typescript", + "javascript", + "shadcn", + "registry" + ], "type": "module", "packageManager": "pnpm@10.8.1", "exports": { @@ -26,7 +35,10 @@ }, "bin": "./dist/index.js", "main": "./dist/index.js", - "files": ["./schemas/**/*", "dist/**/*"], + "files": [ + "./schemas/**/*", + "dist/**/*" + ], "scripts": { "start": "tsup --silent && node ./dist/index.js", "build": "tsup", @@ -43,16 +55,16 @@ "@changesets/cli": "^2.29.2", "@types/diff": "^7.0.2", "@types/make-fetch-happen": "^10.0.4", - "@types/node": "^22.14.0", + "@types/node": "^22.15.3", "@types/semver": "^7.7.0", "@types/validate-npm-package-name": "^4.0.2", - "pkg-pr-new": "^0.0.42", + "pkg-pr-new": "^0.0.43", "tsup": "^8.4.0", "typescript": "^5.8.3", - "vitest": "^3.1.1" + "vitest": "^3.1.2" }, "dependencies": { - "@anthropic-ai/sdk": "^0.39.0", + "@anthropic-ai/sdk": "^0.40.1", "@biomejs/js-api": "^0.7.1", "@biomejs/wasm-nodejs": "^1.9.4", "@clack/prompts": "^0.10.1", @@ -66,22 +78,22 @@ "estree-walker": "^3.0.3", "execa": "^9.5.2", "get-tsconfig": "^4.10.0", - "ignore": "^7.0.3", + "ignore": "^7.0.4", "is-unicode-supported": "^2.1.0", "make-fetch-happen": "^14.0.3", "node-machine-id": "^1.1.12", "octokit": "^4.1.2", "ollama": "^0.5.14", - "openai": "^4.93.0", - "oxc-parser": "^0.64.0", + "openai": "^4.97.0", + "oxc-parser": "^0.67.0", "package-manager-detector": "^1.1.0", - "parse5": "^7.2.1", + "parse5": "^7.3.0", "pathe": "^2.0.3", "prettier": "^3.5.3", "prettier-plugin-svelte": "^3.3.3", "semver": "^7.7.1", "sisteransi": "^1.0.5", - "svelte": "^5.25.9", + "svelte": "^5.28.2", "tar": "^7.4.3", "valibot": "1.0.0", "validate-npm-package-name": "^6.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 33ce23d9..55c8fc0b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: dependencies: '@anthropic-ai/sdk': - specifier: ^0.39.0 - version: 0.39.0(encoding@0.1.13) + specifier: ^0.40.1 + version: 0.40.1(encoding@0.1.13) '@biomejs/js-api': specifier: ^0.7.1 version: 0.7.1(@biomejs/wasm-nodejs@1.9.4) @@ -51,14 +51,17 @@ importers: specifier: ^4.10.0 version: 4.10.0 ignore: - specifier: ^7.0.3 - version: 7.0.3 + specifier: ^7.0.4 + version: 7.0.4 is-unicode-supported: specifier: ^2.1.0 version: 2.1.0 make-fetch-happen: specifier: ^14.0.3 version: 14.0.3 + node-machine-id: + specifier: ^1.1.12 + version: 1.1.12 octokit: specifier: ^4.1.2 version: 4.1.3 @@ -66,17 +69,17 @@ importers: specifier: ^0.5.14 version: 0.5.15 openai: - specifier: ^4.93.0 - version: 4.95.0(encoding@0.1.13)(zod@3.24.3) + specifier: ^4.97.0 + version: 4.97.0(encoding@0.1.13)(zod@3.24.3) oxc-parser: - specifier: ^0.64.0 - version: 0.64.0 + specifier: ^0.67.0 + version: 0.67.0 package-manager-detector: specifier: ^1.1.0 version: 1.2.0 parse5: - specifier: ^7.2.1 - version: 7.2.1 + specifier: ^7.3.0 + version: 7.3.0 pathe: specifier: ^2.0.3 version: 2.0.3 @@ -85,7 +88,7 @@ importers: version: 3.5.3 prettier-plugin-svelte: specifier: ^3.3.3 - version: 3.3.3(prettier@3.5.3)(svelte@5.27.0) + version: 3.3.3(prettier@3.5.3)(svelte@5.28.2) semver: specifier: ^7.7.1 version: 7.7.1 @@ -93,8 +96,8 @@ importers: specifier: ^1.0.5 version: 1.0.5 svelte: - specifier: ^5.25.9 - version: 5.27.0 + specifier: ^5.28.2 + version: 5.28.2 tar: specifier: ^7.4.3 version: 7.4.3 @@ -121,20 +124,17 @@ importers: specifier: ^10.0.4 version: 10.0.4 '@types/node': - specifier: ^22.14.0 - version: 22.14.1 + specifier: ^22.15.3 + version: 22.15.3 '@types/semver': specifier: ^7.7.0 version: 7.7.0 '@types/validate-npm-package-name': specifier: ^4.0.2 version: 4.0.2 - node-machine-id: - specifier: ^1.1.12 - version: 1.1.12 pkg-pr-new: - specifier: ^0.0.42 - version: 0.0.42 + specifier: ^0.0.43 + version: 0.0.43 tsup: specifier: ^8.4.0 version: 8.4.0(postcss@8.5.3)(typescript@5.8.3) @@ -142,8 +142,8 @@ importers: specifier: ^5.8.3 version: 5.8.3 vitest: - specifier: ^3.1.1 - version: 3.1.1(@types/node@22.14.1) + specifier: ^3.1.2 + version: 3.1.2(@types/node@22.15.3) packages: @@ -151,8 +151,8 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@anthropic-ai/sdk@0.39.0': - resolution: {integrity: sha512-eMyDIPRZbt1CCLErRCi3exlAvNkBtRe+kW5vvJyef93PmNr/clstYgHhtvmkxN82nlKgzyGPCyGxrm0JQ1ZIdg==} + '@anthropic-ai/sdk@0.40.1': + resolution: {integrity: sha512-DJMWm8lTEM9Lk/MSFL+V+ugF7jKOn0M2Ujvb5fN8r2nY14aHbGPZ1k6sgjL+tpJ3VuOGJNG+4R83jEpOuYPv8w==} '@babel/helper-string-parser@7.25.9': resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} @@ -687,67 +687,67 @@ packages: resolution: {integrity: sha512-3PCWyFBNbW2+Ox36VAkSqlPoIb96NZiPcICRYySHZrDTM2NuNxvrjPeaQDj2egqILs9EZFObRTHVMe4XxXJV7w==} engines: {node: '>= 18'} - '@oxc-parser/binding-darwin-arm64@0.64.0': - resolution: {integrity: sha512-FfmLZWrt5rsG+wzruv0xfYci1fE/GQ/HnUCmB+j3keU4SfDxkxSIGUTphxdcE8S4ISoLelgeVZiE8QDGRhmSoQ==} + '@oxc-parser/binding-darwin-arm64@0.67.0': + resolution: {integrity: sha512-AWLaNH7emKLCpFzHjcYr0wqE8HRpK/5vDtIAUz0BEZKsYxM/Nd8UpgRg2ZlNlEiPDMgAhpRLBHqjf9Xiv/IMhw==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.64.0': - resolution: {integrity: sha512-FFbtYNdlRw6d/KcfSxqOAJAI4evijC+i+PHQkpB8JJGr+mPzQEPKwVa8vh2Qe/lcspaQs6IrR2GRpJ+5UvciRw==} + '@oxc-parser/binding-darwin-x64@0.67.0': + resolution: {integrity: sha512-1wYbIWpXZ8V9jUx309LyCyK5cVqbTC3qawZ293qfGSmRTsn0F7OenRK7ERbRx7HuvWw2zgWIbWieUpO487nloQ==} engines: {node: '>=14.0.0'} cpu: [x64] os: [darwin] - '@oxc-parser/binding-linux-arm-gnueabihf@0.64.0': - resolution: {integrity: sha512-u113yYpeTW0rQBp6Lld2PvdEMzVQmTq8n2T4WDb7UNGQFCMzoURCKgahkIZCStph4+zHAFU5uKwG5waQaswCyw==} + '@oxc-parser/binding-linux-arm-gnueabihf@0.67.0': + resolution: {integrity: sha512-Dry9zRk/LOvPvb/GDNkgtQZ2cJKBIc6alQOwjvpji/OdJFjqawTPJoHB0F7nd6NfRYle0tVXCFYHtGUxv2WNxQ==} engines: {node: '>=14.0.0'} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm64-gnu@0.64.0': - resolution: {integrity: sha512-cqWgdJcXJ2u2Rcjd/+4mY10DPISZtKosgyL7eMZwZdCNJD8q2ohS57pk6IbCmopF55QAh9/Py8rajblKbFCJBg==} + '@oxc-parser/binding-linux-arm64-gnu@0.67.0': + resolution: {integrity: sha512-Bk+Fqe2J9OvIPs+FK/avTA1YL0tAQD3FgiHe0gq7HLaiuwjw8FAzi2KldyataNmIekfiSH6e+xrt2FugwDXFlw==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] - '@oxc-parser/binding-linux-arm64-musl@0.64.0': - resolution: {integrity: sha512-b7Ma+CDlkK+UIU/Zr8Ydo+q3A9ouWUhV8PzWcnfOxiOwK+JEaoz5N02ixAPK8qvO+IKqzP00HzxPD8tUto8GcA==} + '@oxc-parser/binding-linux-arm64-musl@0.67.0': + resolution: {integrity: sha512-zBMJOkxgcR7Fgmx6hFJQycgWCl9fhS/oW5n1Qix+cbKFe2HfgtOhI+pESEqHc642WX/93BJ1m4OMmZJl35VYgg==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [linux] - '@oxc-parser/binding-linux-x64-gnu@0.64.0': - resolution: {integrity: sha512-7o/qfZNZ0kt1o5vtqUz6nQkV6tuCGor4+gOmqtrb2TtnAo3qxYwPXZVjd9LKv39Z+Nfpqz/2cnR+GIqUNqv34A==} + '@oxc-parser/binding-linux-x64-gnu@0.67.0': + resolution: {integrity: sha512-/zHUMrL24fGMTEr1iHE63f8NYa2IvxfIeNo24H1ofxhtr0A2KmcgOCcEUIypFjMxD5EY5kpQ2t0Nf42o+d4LOA==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] - '@oxc-parser/binding-linux-x64-musl@0.64.0': - resolution: {integrity: sha512-nuL0rqoWgvO11pP7g5FYdTDsjX93mt8ZFtUaOL4HMVkvRAx3XiKltJBYXXWiI2kySbHRC/XHJftAKWEgGhcXgg==} + '@oxc-parser/binding-linux-x64-musl@0.67.0': + resolution: {integrity: sha512-+JsqPXn2Op35lPEMbTyHonPHzTyvCpfaD522M5nziDt41DAOe3BMMcGgRfJXl6Dv/r8f4iZuHL3YSU8wF+elcQ==} engines: {node: '>=14.0.0'} cpu: [x64] os: [linux] - '@oxc-parser/binding-wasm32-wasi@0.64.0': - resolution: {integrity: sha512-iZ5LeOPDo0gCISzcq1JKo3HGqXwuQDTgHVPBUs+UFdCL9WJ9DmNkXXQPLVYEyyI/YFXg15y7Rv2L+FEvpvYa+w==} + '@oxc-parser/binding-wasm32-wasi@0.67.0': + resolution: {integrity: sha512-jAugJhwJvCSurHEoicL0Gp9k1XVEnrTpQ3l1YBro/jfJ5uKSpfMBPPpNZBW04gumD08RDk32nqcPbk+BezvTaw==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@oxc-parser/binding-win32-arm64-msvc@0.64.0': - resolution: {integrity: sha512-9kWLwYOT9sCVrFL3Egpt4+viAYtYOwmstGoy/CPikC0fxEpB760qln8u+MfZpbrH0Df2XgEdAUTqiwnRwcp+uA==} + '@oxc-parser/binding-win32-arm64-msvc@0.67.0': + resolution: {integrity: sha512-JZparqb773ahTQoC3/e6LazRqOLhlyzNhllK73xvz/wixkYueivHxJrdYtFy4ss2VDns4Dg0MZ/zRhkBJy1enA==} engines: {node: '>=14.0.0'} cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.64.0': - resolution: {integrity: sha512-EHQglaBx4LpNw9BMA65aM36isTpuAdWxGbAUH7w55GYIGjVG7hIsMx/MuOrJXsmOBVmRokoYNYLN7X5aTd5TmQ==} + '@oxc-parser/binding-win32-x64-msvc@0.67.0': + resolution: {integrity: sha512-jP5BkhItHRg0J/JfTp1L8D57VMyFgKrXeqH8h8CS04WL1V/ZbBUSr6FnK8gfy7eeKQZg7K7ZeJRTEEcW6i3nwQ==} engines: {node: '>=14.0.0'} cpu: [x64] os: [win32] - '@oxc-project/types@0.64.0': - resolution: {integrity: sha512-B0dxuEZFV6M4tXjPFwDSaED5/J55YUhODBaF09xNFNRrEyzQLKZuhKXAw1xYK8bO4K8Jn1d21TZfei3kAIE8dA==} + '@oxc-project/types@0.67.0': + resolution: {integrity: sha512-AI7inoYvnVro7b8S2Z+Fxi295xQvNKLP1CM/xzx5il4R3aiGgnFt9qiXaRo9vIutataX8AjHcaPnOsjdcItU0w==} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} @@ -889,8 +889,8 @@ packages: '@types/node@18.19.86': resolution: {integrity: sha512-fifKayi175wLyKyc5qUfyENhQ1dCNI1UNjp653d8kuYcPQN5JhX3dGuP/XmvPTg/xRBn1VTLpbmi+H/Mr7tLfQ==} - '@types/node@22.14.1': - resolution: {integrity: sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==} + '@types/node@22.15.3': + resolution: {integrity: sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==} '@types/retry@0.12.5': resolution: {integrity: sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw==} @@ -904,11 +904,11 @@ packages: '@types/validate-npm-package-name@4.0.2': resolution: {integrity: sha512-lrpDziQipxCEeK5kWxvljWYhUvOiB2A9izZd9B2AFarYAkqZshb4lPbRs7zKEic6eGtH8V/2qJW+dPp9OtF6bw==} - '@vitest/expect@3.1.1': - resolution: {integrity: sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==} + '@vitest/expect@3.1.2': + resolution: {integrity: sha512-O8hJgr+zREopCAqWl3uCVaOdqJwZ9qaDwUP7vy3Xigad0phZe9APxKhPcDNqYYi0rX5oMvwJMSCAXY2afqeTSA==} - '@vitest/mocker@3.1.1': - resolution: {integrity: sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==} + '@vitest/mocker@3.1.2': + resolution: {integrity: sha512-kOtd6K2lc7SQ0mBqYv/wdGedlqPdM/B38paPY+OwJ1XiNi44w3Fpog82UfOibmHaV9Wod18A09I9SCKLyDMqgw==} peerDependencies: msw: ^2.4.9 vite: ^5.0.0 || ^6.0.0 @@ -918,20 +918,20 @@ packages: vite: optional: true - '@vitest/pretty-format@3.1.1': - resolution: {integrity: sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==} + '@vitest/pretty-format@3.1.2': + resolution: {integrity: sha512-R0xAiHuWeDjTSB3kQ3OQpT8Rx3yhdOAIm/JM4axXxnG7Q/fS8XUwggv/A4xzbQA+drYRjzkMnpYnOGAc4oeq8w==} - '@vitest/runner@3.1.1': - resolution: {integrity: sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==} + '@vitest/runner@3.1.2': + resolution: {integrity: sha512-bhLib9l4xb4sUMPXnThbnhX2Yi8OutBMA8Yahxa7yavQsFDtwY/jrUZwpKp2XH9DhRFJIeytlyGpXCqZ65nR+g==} - '@vitest/snapshot@3.1.1': - resolution: {integrity: sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==} + '@vitest/snapshot@3.1.2': + resolution: {integrity: sha512-Q1qkpazSF/p4ApZg1vfZSQ5Yw6OCQxVMVrLjslbLFA1hMDrT2uxtqMaw8Tc/jy5DLka1sNs1Y7rBcftMiaSH/Q==} - '@vitest/spy@3.1.1': - resolution: {integrity: sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==} + '@vitest/spy@3.1.2': + resolution: {integrity: sha512-OEc5fSXMws6sHVe4kOFyDSj/+4MSwst0ib4un0DlcYgQvRuYQ0+M2HyqGaauUMnjq87tmUaMNDxKQx7wNfVqPA==} - '@vitest/utils@3.1.1': - resolution: {integrity: sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==} + '@vitest/utils@3.1.2': + resolution: {integrity: sha512-5GGd0ytZ7BH3H6JTj9Kw7Prn1Nbg0wZVrIvou+UWxm54d+WoXXgAgjFJ8wn3LdagWLFSEfpPeyYrByZaGEZHLg==} '@vue/compiler-core@3.5.13': resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} @@ -1239,6 +1239,10 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + entities@6.0.0: + resolution: {integrity: sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==} + engines: {node: '>=0.12'} + env-paths@3.0.0: resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1334,6 +1338,14 @@ packages: picomatch: optional: true + fdir@6.4.4: + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} @@ -1469,8 +1481,8 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - ignore@7.0.3: - resolution: {integrity: sha512-bAH5jbK/F3T3Jls4I0SO1hmPR0dKU0a7+SY6n1yzRtG54FLO8d6w/nxLFX2Nb7dBu6cCWXPaAME6cYqFUMmuCA==} + ignore@7.0.4: + resolution: {integrity: sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==} engines: {node: '>= 4'} imurmurhash@0.1.4: @@ -1677,6 +1689,7 @@ packages: node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} @@ -1708,8 +1721,8 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - openai@4.95.0: - resolution: {integrity: sha512-tWHLTA+/HHyWlP8qg0mQLDSpI2NQLhk6zHLJL8yb59qn2pEI8rbEiAGSDPViLvi3BRDoQZIX5scaJ3xYGr2nhw==} + openai@4.97.0: + resolution: {integrity: sha512-LRoiy0zvEf819ZUEJhgfV8PfsE8G5WpQi4AwA1uCV8SKvvtXQkoWUFkepD6plqyJQRghy2+AEPQ07FrJFKHZ9Q==} hasBin: true peerDependencies: ws: ^8.18.0 @@ -1727,8 +1740,8 @@ packages: outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - oxc-parser@0.64.0: - resolution: {integrity: sha512-T5/h7Iv3kwUwTaOwOLz2yTwz2LsUfdu5IXTmZuMEDYL2Bp/dxGdxQZHaz8lc4bUBU9Swnb+caioKk4FLBT7prg==} + oxc-parser@0.67.0: + resolution: {integrity: sha512-07arJoEJQopwEQ3gDu220l9J7i4XIyOWUGhfRalOX6gKEEYZIaqts5zJvFNtwNSjCc2yHMYscAdHNAB8nRazjA==} engines: {node: '>=14.0.0'} p-filter@2.1.0: @@ -1768,8 +1781,8 @@ packages: resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} engines: {node: '>=18'} - parse5@7.2.1: - resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==} + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} @@ -1817,8 +1830,8 @@ packages: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} - pkg-pr-new@0.0.42: - resolution: {integrity: sha512-Fgx4PRJyjEaT5iicEHi08ENNhejOi4Fq2buord/ubGbFoo86VZRZuKzmZ2AKxdFV+zk5e79VgRjCIyGDOXnwYg==} + pkg-pr-new@0.0.43: + resolution: {integrity: sha512-BxadQyJbbt7BtInyg82I73ztPOftPmDF1ttpTiJ1FpCp4p3zt5sXvDLm3V1GA8ukxa8MSvsI1+BJmsR54FLBpg==} hasBin: true pkg-types@1.3.1: @@ -2047,8 +2060,8 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true - svelte@5.27.0: - resolution: {integrity: sha512-Uai13Ydt1ZE+bUHme6b9U38PCYVNCqBRoBMkUKbFbKiD7kHWjdUUrklYAQZJxyKK81qII4mrBwe/YmvEMSlC9w==} + svelte@5.28.2: + resolution: {integrity: sha512-FbWBxgWOpQfhKvoGJv/TFwzqb4EhJbwCD17dB0tEpQiw1XyUEKZJtgm4nA4xq3LLsMo7hu5UY/BOFmroAxKTMg==} engines: {node: '>=18'} tar@7.4.3: @@ -2076,6 +2089,10 @@ packages: resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} engines: {node: '>=12.0.0'} + tinyglobby@0.2.13: + resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + engines: {node: '>=12.0.0'} + tinypool@1.0.2: resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} engines: {node: ^18.0.0 || >=20.0.0} @@ -2210,8 +2227,8 @@ packages: resolution: {integrity: sha512-d7KLgL1LD3U3fgnvWEY1cQXoO/q6EQ1BSz48Sa149V/5zVTAbgmZIpyI8TRi6U9/JNyeYLlTKsEMPtLC27RFUg==} engines: {node: ^18.17.0 || >=20.5.0} - vite-node@3.1.1: - resolution: {integrity: sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==} + vite-node@3.1.2: + resolution: {integrity: sha512-/8iMryv46J3aK13iUXsei5G/A3CUlW4665THCPS+K8xAaqrVWiGB4RfXMQXCLjpK9P2eK//BczrVkn5JLAk6DA==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true @@ -2255,16 +2272,16 @@ packages: yaml: optional: true - vitest@3.1.1: - resolution: {integrity: sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==} + vitest@3.1.2: + resolution: {integrity: sha512-WaxpJe092ID1C0mr+LH9MmNrhfzi8I65EX/NRU/Ld016KqQNRgxSOlGNP1hHN+a/F8L15Mh8klwaF77zR3GeDQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/debug': ^4.1.12 '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 3.1.1 - '@vitest/ui': 3.1.1 + '@vitest/browser': 3.1.2 + '@vitest/ui': 3.1.2 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -2370,7 +2387,7 @@ snapshots: '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 - '@anthropic-ai/sdk@0.39.0(encoding@0.1.13)': + '@anthropic-ai/sdk@0.40.1(encoding@0.1.13)': dependencies: '@types/node': 18.19.86 '@types/node-fetch': 2.6.12 @@ -2989,39 +3006,39 @@ snapshots: '@octokit/request-error': 6.1.8 '@octokit/webhooks-methods': 5.1.1 - '@oxc-parser/binding-darwin-arm64@0.64.0': + '@oxc-parser/binding-darwin-arm64@0.67.0': optional: true - '@oxc-parser/binding-darwin-x64@0.64.0': + '@oxc-parser/binding-darwin-x64@0.67.0': optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.64.0': + '@oxc-parser/binding-linux-arm-gnueabihf@0.67.0': optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.64.0': + '@oxc-parser/binding-linux-arm64-gnu@0.67.0': optional: true - '@oxc-parser/binding-linux-arm64-musl@0.64.0': + '@oxc-parser/binding-linux-arm64-musl@0.67.0': optional: true - '@oxc-parser/binding-linux-x64-gnu@0.64.0': + '@oxc-parser/binding-linux-x64-gnu@0.67.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.64.0': + '@oxc-parser/binding-linux-x64-musl@0.67.0': optional: true - '@oxc-parser/binding-wasm32-wasi@0.64.0': + '@oxc-parser/binding-wasm32-wasi@0.67.0': dependencies: '@napi-rs/wasm-runtime': 0.2.9 optional: true - '@oxc-parser/binding-win32-arm64-msvc@0.64.0': + '@oxc-parser/binding-win32-arm64-msvc@0.67.0': optional: true - '@oxc-parser/binding-win32-x64-msvc@0.64.0': + '@oxc-parser/binding-win32-x64-msvc@0.67.0': optional: true - '@oxc-project/types@0.64.0': {} + '@oxc-project/types@0.67.0': {} '@pkgjs/parseargs@0.11.0': optional: true @@ -3113,7 +3130,7 @@ snapshots: '@types/node-fetch@2.6.12': dependencies: - '@types/node': 22.14.1 + '@types/node': 22.15.3 form-data: 4.0.2 '@types/node@12.20.55': {} @@ -3122,7 +3139,7 @@ snapshots: dependencies: undici-types: 5.26.5 - '@types/node@22.14.1': + '@types/node@22.15.3': dependencies: undici-types: 6.21.0 @@ -3132,47 +3149,47 @@ snapshots: '@types/ssri@7.1.5': dependencies: - '@types/node': 22.14.1 + '@types/node': 22.15.3 '@types/validate-npm-package-name@4.0.2': {} - '@vitest/expect@3.1.1': + '@vitest/expect@3.1.2': dependencies: - '@vitest/spy': 3.1.1 - '@vitest/utils': 3.1.1 + '@vitest/spy': 3.1.2 + '@vitest/utils': 3.1.2 chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.1.1(vite@6.3.1(@types/node@22.14.1))': + '@vitest/mocker@3.1.2(vite@6.3.1(@types/node@22.15.3))': dependencies: - '@vitest/spy': 3.1.1 + '@vitest/spy': 3.1.2 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 6.3.1(@types/node@22.14.1) + vite: 6.3.1(@types/node@22.15.3) - '@vitest/pretty-format@3.1.1': + '@vitest/pretty-format@3.1.2': dependencies: tinyrainbow: 2.0.0 - '@vitest/runner@3.1.1': + '@vitest/runner@3.1.2': dependencies: - '@vitest/utils': 3.1.1 + '@vitest/utils': 3.1.2 pathe: 2.0.3 - '@vitest/snapshot@3.1.1': + '@vitest/snapshot@3.1.2': dependencies: - '@vitest/pretty-format': 3.1.1 + '@vitest/pretty-format': 3.1.2 magic-string: 0.30.17 pathe: 2.0.3 - '@vitest/spy@3.1.1': + '@vitest/spy@3.1.2': dependencies: tinyspy: 3.0.2 - '@vitest/utils@3.1.1': + '@vitest/utils@3.1.2': dependencies: - '@vitest/pretty-format': 3.1.1 + '@vitest/pretty-format': 3.1.2 loupe: 3.1.3 tinyrainbow: 2.0.0 @@ -3474,6 +3491,8 @@ snapshots: entities@4.5.0: {} + entities@6.0.0: {} + env-paths@3.0.0: {} err-code@2.0.3: {} @@ -3588,6 +3607,10 @@ snapshots: optionalDependencies: picomatch: 4.0.2 + fdir@6.4.4(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + figures@6.1.0: dependencies: is-unicode-supported: 2.1.0 @@ -3743,7 +3766,7 @@ snapshots: ignore@5.3.2: {} - ignore@7.0.3: {} + ignore@7.0.4: {} imurmurhash@0.1.4: {} @@ -3961,7 +3984,7 @@ snapshots: dependencies: wrappy: 1.0.2 - openai@4.95.0(encoding@0.1.13)(zod@3.24.3): + openai@4.97.0(encoding@0.1.13)(zod@3.24.3): dependencies: '@types/node': 18.19.86 '@types/node-fetch': 2.6.12 @@ -3979,20 +4002,20 @@ snapshots: outdent@0.5.0: {} - oxc-parser@0.64.0: + oxc-parser@0.67.0: dependencies: - '@oxc-project/types': 0.64.0 + '@oxc-project/types': 0.67.0 optionalDependencies: - '@oxc-parser/binding-darwin-arm64': 0.64.0 - '@oxc-parser/binding-darwin-x64': 0.64.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.64.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.64.0 - '@oxc-parser/binding-linux-arm64-musl': 0.64.0 - '@oxc-parser/binding-linux-x64-gnu': 0.64.0 - '@oxc-parser/binding-linux-x64-musl': 0.64.0 - '@oxc-parser/binding-wasm32-wasi': 0.64.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.64.0 - '@oxc-parser/binding-win32-x64-msvc': 0.64.0 + '@oxc-parser/binding-darwin-arm64': 0.67.0 + '@oxc-parser/binding-darwin-x64': 0.67.0 + '@oxc-parser/binding-linux-arm-gnueabihf': 0.67.0 + '@oxc-parser/binding-linux-arm64-gnu': 0.67.0 + '@oxc-parser/binding-linux-arm64-musl': 0.67.0 + '@oxc-parser/binding-linux-x64-gnu': 0.67.0 + '@oxc-parser/binding-linux-x64-musl': 0.67.0 + '@oxc-parser/binding-wasm32-wasi': 0.67.0 + '@oxc-parser/binding-win32-arm64-msvc': 0.67.0 + '@oxc-parser/binding-win32-x64-msvc': 0.67.0 p-filter@2.1.0: dependencies: @@ -4022,9 +4045,9 @@ snapshots: parse-ms@4.0.0: {} - parse5@7.2.1: + parse5@7.3.0: dependencies: - entities: 4.5.0 + entities: 6.0.0 path-exists@4.0.0: {} @@ -4053,7 +4076,7 @@ snapshots: pirates@4.0.7: {} - pkg-pr-new@0.0.42: + pkg-pr-new@0.0.43: dependencies: '@jsdevtools/ez-spawn': 3.0.4 '@octokit/action': 6.1.0 @@ -4081,10 +4104,10 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.27.0): + prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.28.2): dependencies: prettier: 3.5.3 - svelte: 5.27.0 + svelte: 5.28.2 prettier@2.8.8: {} @@ -4277,7 +4300,7 @@ snapshots: pirates: 4.0.7 ts-interface-checker: 0.1.13 - svelte@5.27.0: + svelte@5.28.2: dependencies: '@ampproject/remapping': 2.3.0 '@jridgewell/sourcemap-codec': 1.5.0 @@ -4322,6 +4345,11 @@ snapshots: fdir: 6.4.3(picomatch@4.0.2) picomatch: 4.0.2 + tinyglobby@0.2.13: + dependencies: + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + tinypool@1.0.2: {} tinyrainbow@2.0.0: {} @@ -4422,13 +4450,13 @@ snapshots: validate-npm-package-name@6.0.0: {} - vite-node@3.1.1(@types/node@22.14.1): + vite-node@3.1.2(@types/node@22.15.3): dependencies: cac: 6.7.14 debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 2.0.3 - vite: 6.3.1(@types/node@22.14.1) + vite: 6.3.1(@types/node@22.15.3) transitivePeerDependencies: - '@types/node' - jiti @@ -4443,27 +4471,27 @@ snapshots: - tsx - yaml - vite@6.3.1(@types/node@22.14.1): + vite@6.3.1(@types/node@22.15.3): dependencies: esbuild: 0.25.2 fdir: 6.4.3(picomatch@4.0.2) picomatch: 4.0.2 postcss: 8.5.3 rollup: 4.40.0 - tinyglobby: 0.2.12 + tinyglobby: 0.2.13 optionalDependencies: - '@types/node': 22.14.1 + '@types/node': 22.15.3 fsevents: 2.3.3 - vitest@3.1.1(@types/node@22.14.1): + vitest@3.1.2(@types/node@22.15.3): dependencies: - '@vitest/expect': 3.1.1 - '@vitest/mocker': 3.1.1(vite@6.3.1(@types/node@22.14.1)) - '@vitest/pretty-format': 3.1.1 - '@vitest/runner': 3.1.1 - '@vitest/snapshot': 3.1.1 - '@vitest/spy': 3.1.1 - '@vitest/utils': 3.1.1 + '@vitest/expect': 3.1.2 + '@vitest/mocker': 3.1.2(vite@6.3.1(@types/node@22.15.3)) + '@vitest/pretty-format': 3.1.2 + '@vitest/runner': 3.1.2 + '@vitest/snapshot': 3.1.2 + '@vitest/spy': 3.1.2 + '@vitest/utils': 3.1.2 chai: 5.2.0 debug: 4.4.0 expect-type: 1.2.1 @@ -4472,13 +4500,14 @@ snapshots: std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 + tinyglobby: 0.2.13 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 6.3.1(@types/node@22.14.1) - vite-node: 3.1.1(@types/node@22.14.1) + vite: 6.3.1(@types/node@22.15.3) + vite-node: 3.1.2(@types/node@22.15.3) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.14.1 + '@types/node': 22.15.3 transitivePeerDependencies: - jiti - less From e5cf32354e37c500662117475c3be78d5026b2af Mon Sep 17 00:00:00 2001 From: Aidan Bleser Date: Fri, 2 May 2025 15:15:37 -0500 Subject: [PATCH 18/18] Update package.json --- package.json | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 1a611667..6c1e0a51 100644 --- a/package.json +++ b/package.json @@ -15,16 +15,7 @@ "bugs": { "url": "https://github.com/jsrepojs/jsrepo/issues" }, - "keywords": [ - "repo", - "cli", - "svelte", - "vue", - "typescript", - "javascript", - "shadcn", - "registry" - ], + "keywords": ["repo", "cli", "svelte", "vue", "typescript", "javascript", "shadcn", "registry"], "type": "module", "packageManager": "pnpm@10.8.1", "exports": { @@ -35,10 +26,7 @@ }, "bin": "./dist/index.js", "main": "./dist/index.js", - "files": [ - "./schemas/**/*", - "dist/**/*" - ], + "files": ["./schemas/**/*", "dist/**/*"], "scripts": { "start": "tsup --silent && node ./dist/index.js", "build": "tsup",