feat: add repo ownership check for org in jsr validation algo

This commit is contained in:
Huakun Shen 2025-01-12 01:17:15 -05:00
parent 0af6ef2d0a
commit f6b70bade0
5 changed files with 368 additions and 216 deletions

View File

@ -43,6 +43,7 @@
}, },
"dependencies": { "dependencies": {
"@huakunshen/jsr-client": "^0.1.5", "@huakunshen/jsr-client": "^0.1.5",
"@octokit/rest": "^21.1.0",
"@tauri-apps/api": "^2.2.0", "@tauri-apps/api": "^2.2.0",
"@tauri-apps/cli": "^2.2.2", "@tauri-apps/cli": "^2.2.2",
"@tauri-apps/plugin-deep-link": "^2.2.0", "@tauri-apps/plugin-deep-link": "^2.2.0",

View File

@ -1,71 +1,75 @@
import { describe, expect, test } from "bun:test"; import { describe, expect, test } from "bun:test"
import { validateJsrPackageAsKunkunExtension } from "../index"; import { validateJsrPackageAsKunkunExtension } from "../index"
describe("Validate Jsr package as Kunkun extension", () => { describe("Validate Jsr package as Kunkun extension", () => {
test("Package not signed by GitHub Actions", async () => { test("Package not signed by GitHub Actions", async () => {
expect( expect(
(await validateJsrPackageAsKunkunExtension({ (
await validateJsrPackageAsKunkunExtension({
jsrPackage: { jsrPackage: {
scope: "kunkun", scope: "kunkun",
name: "api", name: "api",
version: "0.0.47", version: "0.0.47"
}, },
githubUsername: "kunkunsh", githubUsername: "kunkunsh"
})).error, })
).toBe("JSR package is not signed by GitHub Actions"); ).error
}); ).toBe("JSR package is not signed by GitHub Actions")
})
test("Non-existent package", async () => { test("Non-existent package", async () => {
expect( expect(
(await validateJsrPackageAsKunkunExtension({ (
await validateJsrPackageAsKunkunExtension({
jsrPackage: { jsrPackage: {
scope: "kunkun", scope: "kunkun",
name: "non-existent-package", name: "non-existent-package",
version: "0.0.47", version: "0.0.47"
}, },
githubUsername: "kunkunsh", githubUsername: "kunkunsh"
})).error, })
).toBe("JSR package does not exist"); ).error
}); ).toBe("JSR package does not exist")
})
test("Package not linked to a GitHub repository", async () => { test("Package not linked to a GitHub repository", async () => {
expect( expect(
(await validateJsrPackageAsKunkunExtension({ (
await validateJsrPackageAsKunkunExtension({
jsrPackage: { jsrPackage: {
scope: "hk", scope: "hk",
name: "tauri-plugin-network-api", name: "tauri-plugin-network-api",
version: "2.0.3-beta.1", version: "2.0.3-beta.1"
}, },
githubUsername: "kunkunsh", githubUsername: "kunkunsh"
})).error, })
).toBe("JSR package is not linked to a GitHub repository"); ).error
}); ).toBe("JSR package is not linked to a GitHub repository")
})
test("GitHub repository owner does not match JSR package owner", async () => { test("GitHub repository owner does not match JSR package owner", async () => {
expect( const res = await validateJsrPackageAsKunkunExtension({
(await validateJsrPackageAsKunkunExtension({
jsrPackage: { jsrPackage: {
scope: "kunkun", scope: "kunkun",
name: "ext-image-processing", name: "ext-image-processing",
version: "0.0.6", version: "0.0.6"
}, },
githubUsername: "kunkunsh", // should be HuakunShen githubUsername: "Huakun"
})).error, })
).toBe( expect(res.error).toBe(
"GitHub repository owner does not match JSR package owner: HuakunShen !== kunkunsh", "You (Huakun) are not authorized to publish this package. Only kunkunsh or its organization members can publish it."
); )
}); })
test("A valid extension package", async () => { test("A valid extension package", async () => {
expect( const res = await await validateJsrPackageAsKunkunExtension({
(await validateJsrPackageAsKunkunExtension({
jsrPackage: { jsrPackage: {
scope: "kunkun", scope: "kunkun",
name: "ext-image-processing", name: "ext-image-processing",
version: "0.0.6", version: "0.0.6"
}, },
githubUsername: "HuakunShen", githubUsername: "HuakunShen"
})).data, })
).toBeDefined(); expect(res.data).toBeDefined()
}); })
}); })

View File

@ -0,0 +1,34 @@
/**
* TODO: move this module to another folder
*/
import { Octokit } from "@octokit/rest"
/**
* Check if a user is a public member of a GitHub organization
* @param orgName - The name of the GitHub organization
* @param username - The username of the user
* @returns A promise that resolves to a boolean indicating if the user is a public member of the organization
*/
export function userIsPublicMemberOfGitHubOrg(orgName: string, username: string): Promise<boolean> {
const octokit = new Octokit()
return octokit.orgs
.checkPublicMembershipForUser({ org: orgName, username })
.then(() => true)
.catch(() => false)
}
/**
* Only works if user grants read:org scope with the org when login
* @param orgName
* @param username
* @param githubToken
*/
export function authenticatedUserIsMemberOfGitHubOrg(
orgName: string,
githubToken: string
): Promise<boolean> {
const octokit = new Octokit({ auth: githubToken })
return octokit.orgs.listForAuthenticatedUser().then((res) => {
return res.data.some((org) => org.login === orgName)
})
}

View File

@ -2,30 +2,31 @@ import {
client, client,
getPackage, getPackage,
getPackageVersion, getPackageVersion,
type GitHubRepository, type GitHubRepository
} from "@huakunshen/jsr-client/hey-api-client"; } from "@huakunshen/jsr-client/hey-api-client"
import * as v from "valibot"; import * as v from "valibot"
import { ExtPackageJson } from "../../models/manifest"; import { ExtPackageJson } from "../../models/manifest"
import type { JsrPackageMetadata, NpmPkgMetadata } from "./models"; import { authenticatedUserIsMemberOfGitHubOrg, userIsPublicMemberOfGitHubOrg } from "./github"
import type { JsrPackageMetadata, NpmPkgMetadata } from "./models"
export * from "./github"
client.setConfig({ client.setConfig({
baseUrl: "https://api.jsr.io", baseUrl: "https://api.jsr.io"
}); })
export function splitRawJsrPkgName( export function splitRawJsrPkgName(packageName: string): Promise<{ scope: string; name: string }> {
packageName: string,
): Promise<{ scope: string; name: string }> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// write a regex to match the scope and name // write a regex to match the scope and name
const regex = /^@([^@]+)\/([^@]+)$/; const regex = /^@([^@]+)\/([^@]+)$/
const match = packageName.match(regex); const match = packageName.match(regex)
if (!match) { if (!match) {
return reject(new Error("Invalid Jsr package name")); return reject(new Error("Invalid Jsr package name"))
} }
const [, rawScope, name] = match; const [, rawScope, name] = match
const scope = rawScope.startsWith("@") ? rawScope.slice(1) : rawScope; const scope = rawScope.startsWith("@") ? rawScope.slice(1) : rawScope
return resolve({ scope, name }); return resolve({ scope, name })
}); })
} }
/** /**
@ -35,8 +36,7 @@ export function splitRawJsrPkgName(
* @param name * @param name
* @returns * @returns
*/ */
export const translateJsrToNpmPkgName = (scope: string, name: string) => export const translateJsrToNpmPkgName = (scope: string, name: string) => `${scope}__${name}`
`${scope}__${name}`;
/** /**
/** /**
@ -46,19 +46,13 @@ export const translateJsrToNpmPkgName = (scope: string, name: string) =>
* @param version * @param version
* @returns * @returns
*/ */
export function getJsrPackageHtml( export function getJsrPackageHtml(scope: string, name: string, version?: string) {
scope: string, const url = `https://jsr.io/@${scope}/${name}${version ? `@${version}` : ""}`
name: string,
version?: string,
) {
const url = `https://jsr.io/@${scope}/${name}${
version ? `@${version}` : ""
}`;
return fetch(url, { return fetch(url, {
headers: { headers: {
"sec-fetch-dest": "document", "sec-fetch-dest": "document"
}, }
}).then((res) => res.text()); }).then((res) => res.text())
} }
/** /**
@ -68,29 +62,29 @@ export function getJsrPackageHtml(
export async function isSignedByGitHubAction( export async function isSignedByGitHubAction(
scope: string, scope: string,
name: string, name: string,
version: string, version: string
): Promise<boolean> { ): Promise<boolean> {
const pkgVersion = await getPackageVersion({ const pkgVersion = await getPackageVersion({
path: { path: {
scope, scope,
package: name, package: name,
version, version
}, }
}); })
return !!pkgVersion.data?.rekorLogId; return !!pkgVersion.data?.rekorLogId
} }
export async function getJsrPackageGitHubRepo( export async function getJsrPackageGitHubRepo(
scope: string, scope: string,
name: string, name: string
): Promise<GitHubRepository | null> { ): Promise<GitHubRepository | null> {
const pkg = await getPackage({ const pkg = await getPackage({
path: { path: {
scope, scope,
package: name, package: name
}, }
}); })
return pkg.data?.githubRepository ?? null; return pkg.data?.githubRepository ?? null
} }
/** /**
@ -102,12 +96,9 @@ export async function getJsrPackageGitHubRepo(
* @param name * @param name
* @returns * @returns
*/ */
export function getJsrPackageMetadata( export function getJsrPackageMetadata(scope: string, name: string): Promise<JsrPackageMetadata> {
scope: string, const url = `https://jsr.io/@${scope}/${name}/meta.json`
name: string, return fetch(url).then((res) => res.json())
): Promise<JsrPackageMetadata> {
const url = `https://jsr.io/@${scope}/${name}/meta.json`;
return fetch(url).then((res) => res.json());
} }
/** /**
@ -122,12 +113,12 @@ export function getJsrPackageSrcFile(
scope: string, scope: string,
name: string, name: string,
version: string, version: string,
file: string, file: string
): Promise<string | undefined> { ): Promise<string | undefined> {
const url = `https://jsr.io/@${scope}/${name}/${version}/${file}`; const url = `https://jsr.io/@${scope}/${name}/${version}/${file}`
return fetch(url) return fetch(url)
.then((res) => res.text()) .then((res) => res.text())
.catch(() => undefined); .catch(() => undefined)
} }
/** /**
@ -136,15 +127,10 @@ export function getJsrPackageSrcFile(
* @param name * @param name
* @returns * @returns
*/ */
export function getJsrNpmPkgMetadata( export function getJsrNpmPkgMetadata(scope: string, name: string): Promise<NpmPkgMetadata> {
scope: string,
name: string,
): Promise<NpmPkgMetadata> {
// Sample: https://npm.jsr.io/@jsr/kunkun__api // Sample: https://npm.jsr.io/@jsr/kunkun__api
const url = `https://npm.jsr.io/@jsr/${ const url = `https://npm.jsr.io/@jsr/${translateJsrToNpmPkgName(scope, name)}`
translateJsrToNpmPkgName(scope, name) return fetch(url).then((res) => res.json())
}`;
return fetch(url).then((res) => res.json());
} }
/** /**
@ -154,14 +140,10 @@ export function getJsrNpmPkgMetadata(
* @param version * @param version
* @returns * @returns
*/ */
export function getJsrNpmPackageVersionMetadata( export function getJsrNpmPackageVersionMetadata(scope: string, name: string, version: string) {
scope: string,
name: string,
version: string,
) {
return getJsrNpmPkgMetadata(scope, name).then((metadata) => { return getJsrNpmPkgMetadata(scope, name).then((metadata) => {
return metadata.versions[version]; return metadata.versions[version]
}); })
} }
/** /**
@ -174,16 +156,11 @@ export function getJsrNpmPackageVersionMetadata(
export async function getNpmPackageTarballUrl( export async function getNpmPackageTarballUrl(
scope: string, scope: string,
name: string, name: string,
version: string, version: string
): Promise<string | undefined> { ): Promise<string | undefined> {
const metadata = await getJsrNpmPackageVersionMetadata( const metadata = await getJsrNpmPackageVersionMetadata(scope, name, version)
scope, const tarballUrl: string | undefined = metadata?.dist.tarball
name, return tarballUrl
version,
);
const tarballUrl: string | undefined = metadata?.dist
.tarball;
return tarballUrl;
} }
/** /**
@ -192,12 +169,9 @@ export async function getNpmPackageTarballUrl(
* @param name * @param name
* @returns * @returns
*/ */
export async function getAllVersionsOfJsrPackage( export async function getAllVersionsOfJsrPackage(scope: string, name: string): Promise<string[]> {
scope: string, const metadata = await getJsrNpmPkgMetadata(scope, name)
name: string, return Object.keys(metadata.versions)
): Promise<string[]> {
const metadata = await getJsrNpmPkgMetadata(scope, name);
return Object.keys(metadata.versions);
} }
/** /**
@ -206,26 +180,22 @@ export async function getAllVersionsOfJsrPackage(
* @param name * @param name
* @returns * @returns
*/ */
export function jsrPackageExists( export function jsrPackageExists(scope: string, name: string, version?: string): Promise<boolean> {
scope: string,
name: string,
version?: string,
): Promise<boolean> {
if (version) { if (version) {
return getPackageVersion({ return getPackageVersion({
path: { path: {
scope, scope,
package: name, package: name,
version, version
}, }
}).then((res) => res.response.ok && res.response.status === 200); }).then((res) => res.response.ok && res.response.status === 200)
} }
return getPackage({ return getPackage({
path: { path: {
scope, scope,
package: name, package: name
}, }
}).then((res) => res.response.ok && res.response.status === 200); }).then((res) => res.response.ok && res.response.status === 200)
} }
/** /**
@ -236,10 +206,10 @@ export function jsrPackageExists(
export function getTarballSize(url: string): Promise<number> { export function getTarballSize(url: string): Promise<number> {
return fetch(url, { method: "HEAD" }).then((res) => { return fetch(url, { method: "HEAD" }).then((res) => {
if (!(res.ok && res.status === 200)) { if (!(res.ok && res.status === 200)) {
throw new Error("Failed to fetch tarball size"); throw new Error("Failed to fetch tarball size")
} }
return Number(res.headers.get("Content-Length")); return Number(res.headers.get("Content-Length"))
}); })
} }
/** /**
@ -254,40 +224,41 @@ export function getTarballSize(url: string): Promise<number> {
*/ */
export async function validateJsrPackageAsKunkunExtension(payload: { export async function validateJsrPackageAsKunkunExtension(payload: {
jsrPackage: { jsrPackage: {
scope: string; scope: string
name: string; name: string
version: string; version: string
}; }
githubUsername: string; githubUsername: string
tarballSizeLimit?: number; tarballSizeLimit?: number
githubToken?: string
}): Promise<{ }): Promise<{
error?: string; error?: string
data?: { data?: {
pkgJson: ExtPackageJson; pkgJson: ExtPackageJson
tarballUrl: string; tarballUrl: string
shasum: string; shasum: string
apiVersion: string; apiVersion: string
tarballSize: number; tarballSize: number
}; }
}> { }> {
// check if jsr package exists // check if jsr package exists
const jsrExists = await jsrPackageExists( const jsrExists = await jsrPackageExists(
payload.jsrPackage.scope, payload.jsrPackage.scope,
payload.jsrPackage.name, payload.jsrPackage.name,
payload.jsrPackage.version, payload.jsrPackage.version
); )
if (!jsrExists) { if (!jsrExists) {
return { error: "JSR package does not exist" }; return { error: "JSR package does not exist" }
} }
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* check if jsr pkg is linked to a github repo */ /* check if jsr pkg is linked to a github repo */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
const githubRepo = await getJsrPackageGitHubRepo( const githubRepo = await getJsrPackageGitHubRepo(
payload.jsrPackage.scope, payload.jsrPackage.scope,
payload.jsrPackage.name, payload.jsrPackage.name
); )
if (githubRepo === null) { if (githubRepo === null) {
return { error: "JSR package is not linked to a GitHub repository" }; return { error: "JSR package is not linked to a GitHub repository" }
} }
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* check if jsr pkg is signed with github action */ /* check if jsr pkg is signed with github action */
@ -295,21 +266,34 @@ export async function validateJsrPackageAsKunkunExtension(payload: {
const signed = await isSignedByGitHubAction( const signed = await isSignedByGitHubAction(
payload.jsrPackage.scope, payload.jsrPackage.scope,
payload.jsrPackage.name, payload.jsrPackage.name,
payload.jsrPackage.version, payload.jsrPackage.version
); )
if (!signed) { if (!signed) {
return { error: "JSR package is not signed by GitHub Actions" }; return { error: "JSR package is not signed by GitHub Actions" }
} }
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* check if user's github username is the same as repo's owner name */ /* check if user's github username is the same as repo's owner name */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
if ( if (!githubRepo.owner) {
githubRepo.owner?.toLowerCase() !== payload.githubUsername.toLowerCase() return { error: "Package's Linked GitHub repository owner is not found." }
) { }
if (githubRepo.owner.toLowerCase() !== payload.githubUsername.toLowerCase()) {
const isPublicMemeber = await userIsPublicMemberOfGitHubOrg(
githubRepo.owner,
payload.githubUsername
)
let isOrgMember = false
if (payload.githubToken) {
isOrgMember = await authenticatedUserIsMemberOfGitHubOrg(
githubRepo.owner,
payload.githubToken
)
}
if (!isPublicMemeber && !isOrgMember) {
return { return {
error: error: `You (${payload.githubUsername}) are not authorized to publish this package. Only ${githubRepo.owner} or its organization members can publish it.`
`GitHub repository owner does not match JSR package owner: ${githubRepo.owner} !== ${payload.githubUsername}`, }
}; }
} }
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* check if jsr.json or deno.json has the same version as package.json */ /* check if jsr.json or deno.json has the same version as package.json */
@ -318,60 +302,57 @@ export async function validateJsrPackageAsKunkunExtension(payload: {
payload.jsrPackage.scope, payload.jsrPackage.scope,
payload.jsrPackage.name, payload.jsrPackage.name,
payload.jsrPackage.version, payload.jsrPackage.version,
"package.json", "package.json"
); )
if (!packageJsonContent) { if (!packageJsonContent) {
return { error: "Could not find package.json in JSR package" }; return { error: "Could not find package.json in JSR package" }
} }
let packageJson: any; let packageJson: any
try { try {
packageJson = JSON.parse(packageJsonContent); packageJson = JSON.parse(packageJsonContent)
} catch (error) { } catch (error) {
return { error: "Failed to parse package.json" }; return { error: "Failed to parse package.json" }
} }
if (packageJson.version !== payload.jsrPackage.version) { if (packageJson.version !== payload.jsrPackage.version) {
// no need to fetch jsr.json or deno.json content, as we already know the version is valid with JSR API // no need to fetch jsr.json or deno.json content, as we already know the version is valid with JSR API
return { return {
error: error: "Package version in package.json does not match JSR package version"
"Package version in package.json does not match JSR package version", }
};
} }
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* validate package.json format against latest schema */ /* validate package.json format against latest schema */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
const parseResult = v.safeParse(ExtPackageJson, packageJson); const parseResult = v.safeParse(ExtPackageJson, packageJson)
if (!parseResult.success) { if (!parseResult.success) {
return { error: "package.json format not valid" }; return { error: "package.json format not valid" }
} }
const npmPkgVersionMetadata = await getJsrNpmPackageVersionMetadata( const npmPkgVersionMetadata = await getJsrNpmPackageVersionMetadata(
payload.jsrPackage.scope, payload.jsrPackage.scope,
payload.jsrPackage.name, payload.jsrPackage.name,
payload.jsrPackage.version, payload.jsrPackage.version
); )
const tarballUrl = npmPkgVersionMetadata.dist.tarball; const tarballUrl = npmPkgVersionMetadata.dist.tarball
const shasum = npmPkgVersionMetadata.dist.shasum; const shasum = npmPkgVersionMetadata.dist.shasum
if (!tarballUrl) { if (!tarballUrl) {
return { error: "Could not get tarball URL for JSR package" }; return { error: "Could not get tarball URL for JSR package" }
} }
const tarballSize = await getTarballSize(tarballUrl); const tarballSize = await getTarballSize(tarballUrl)
const sizeLimit = payload.tarballSizeLimit ?? 50 * 1024 * 1024; // default to 50MB const sizeLimit = payload.tarballSizeLimit ?? 50 * 1024 * 1024 // default to 50MB
if (tarballSize > sizeLimit) { if (tarballSize > sizeLimit) {
return { return {
error: error: `Package tarball size (${tarballSize} bytes) exceeds limit of ${sizeLimit} bytes`
`Package tarball size (${tarballSize} bytes) exceeds limit of ${sizeLimit} bytes`, }
};
} }
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* get @kksh/api dependency version */ /* get @kksh/api dependency version */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
const apiVersion = parseResult.output.dependencies?.["@kksh/api"]; const apiVersion = parseResult.output.dependencies?.["@kksh/api"]
if (!apiVersion) { if (!apiVersion) {
return { return {
error: error: `Extension ${packageJson.kunkun.identifier} doesn't not have @kksh/api as a dependency`
`Extension ${packageJson.kunkun.identifier} doesn't not have @kksh/api as a dependency`, }
};
} }
return { return {
@ -380,7 +361,7 @@ export async function validateJsrPackageAsKunkunExtension(payload: {
tarballUrl, tarballUrl,
shasum, shasum,
apiVersion, apiVersion,
tarballSize, tarballSize
}, }
}; }
} }

150
pnpm-lock.yaml generated
View File

@ -342,6 +342,9 @@ importers:
'@huakunshen/jsr-client': '@huakunshen/jsr-client':
specifier: ^0.1.5 specifier: ^0.1.5
version: 0.1.5(axios@1.7.9)(react@18.3.1)(typescript@5.6.3) version: 0.1.5(axios@1.7.9)(react@18.3.1)(typescript@5.6.3)
'@octokit/rest':
specifier: ^21.1.0
version: 21.1.0
'@tauri-apps/api': '@tauri-apps/api':
specifier: ^2.2.0 specifier: ^2.2.0
version: 2.2.0 version: 2.2.0
@ -2756,6 +2759,58 @@ packages:
'@nuxtjs/tailwindcss@6.12.2': '@nuxtjs/tailwindcss@6.12.2':
resolution: {integrity: sha512-qPJiFH67CkTj/2kBGBzqXihOD1rQXMsbVS4vdQvfBxOBLPfGhU1yw7AATdhPl2BBjO2krjJLuZj39t7dnDYOwg==} resolution: {integrity: sha512-qPJiFH67CkTj/2kBGBzqXihOD1rQXMsbVS4vdQvfBxOBLPfGhU1yw7AATdhPl2BBjO2krjJLuZj39t7dnDYOwg==}
'@octokit/auth-token@5.1.1':
resolution: {integrity: sha512-rh3G3wDO8J9wSjfI436JUKzHIxq8NaiL0tVeB2aXmG6p/9859aUOAjA9pmSPNGGZxfwmaJ9ozOJImuNVJdpvbA==}
engines: {node: '>= 18'}
'@octokit/core@6.1.3':
resolution: {integrity: sha512-z+j7DixNnfpdToYsOutStDgeRzJSMnbj8T1C/oQjB6Aa+kRfNjs/Fn7W6c8bmlt6mfy3FkgeKBRnDjxQow5dow==}
engines: {node: '>= 18'}
'@octokit/endpoint@10.1.2':
resolution: {integrity: sha512-XybpFv9Ms4hX5OCHMZqyODYqGTZ3H6K6Vva+M9LR7ib/xr1y1ZnlChYv9H680y77Vd/i/k+thXApeRASBQkzhA==}
engines: {node: '>= 18'}
'@octokit/graphql@8.1.2':
resolution: {integrity: sha512-bdlj/CJVjpaz06NBpfHhp4kGJaRZfz7AzC+6EwUImRtrwIw8dIgJ63Xg0OzV9pRn3rIzrt5c2sa++BL0JJ8GLw==}
engines: {node: '>= 18'}
'@octokit/openapi-types@23.0.1':
resolution: {integrity: sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==}
'@octokit/plugin-paginate-rest@11.4.0':
resolution: {integrity: sha512-ttpGck5AYWkwMkMazNCZMqxKqIq1fJBNxBfsFwwfyYKTf914jKkLF0POMS3YkPBwp5g1c2Y4L79gDz01GhSr1g==}
engines: {node: '>= 18'}
peerDependencies:
'@octokit/core': '>=6'
'@octokit/plugin-request-log@5.3.1':
resolution: {integrity: sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==}
engines: {node: '>= 18'}
peerDependencies:
'@octokit/core': '>=6'
'@octokit/plugin-rest-endpoint-methods@13.3.0':
resolution: {integrity: sha512-LUm44shlmkp/6VC+qQgHl3W5vzUP99ZM54zH6BuqkJK4DqfFLhegANd+fM4YRLapTvPm4049iG7F3haANKMYvQ==}
engines: {node: '>= 18'}
peerDependencies:
'@octokit/core': '>=6'
'@octokit/request-error@6.1.6':
resolution: {integrity: sha512-pqnVKYo/at0NuOjinrgcQYpEbv4snvP3bKMRqHaD9kIsk9u1LCpb2smHZi8/qJfgeNqLo5hNW4Z7FezNdEo0xg==}
engines: {node: '>= 18'}
'@octokit/request@9.1.4':
resolution: {integrity: sha512-tMbOwGm6wDII6vygP3wUVqFTw3Aoo0FnVQyhihh8vVq12uO3P+vQZeo2CKMpWtPSogpACD0yyZAlVlQnjW71DA==}
engines: {node: '>= 18'}
'@octokit/rest@21.1.0':
resolution: {integrity: sha512-93iLxcKDJboUpmnUyeJ6cRIi7z7cqTZT1K7kRK4LobGxwTwpsa+2tQQbRQNGy7IFDEAmrtkf4F4wBj3D5rVlJQ==}
engines: {node: '>= 18'}
'@octokit/types@13.7.0':
resolution: {integrity: sha512-BXfRP+3P3IN6fd4uF3SniaHKOO4UXWBfkdR3vA8mIvaoO/wLjGN5qivUtW0QRitBHHMcfC41SLhNVYIZZE+wkA==}
'@parcel/watcher-android-arm64@2.5.0': '@parcel/watcher-android-arm64@2.5.0':
resolution: {integrity: sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==} resolution: {integrity: sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
@ -5551,6 +5606,9 @@ packages:
bcrypt-pbkdf@1.0.2: bcrypt-pbkdf@1.0.2:
resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
before-after-hook@3.0.2:
resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==}
better-path-resolve@1.0.0: better-path-resolve@1.0.0:
resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==}
engines: {node: '>=4'} engines: {node: '>=4'}
@ -6890,6 +6948,9 @@ packages:
resolution: {integrity: sha512-TmpgSeJ2jUV+FNWnSy9iIE9bOV9nCNQ4it+K9BpCNT9JsQOfZYznWGSbMw+Wa4uusEss0IcL/trFVoRxS6IuAA==} resolution: {integrity: sha512-TmpgSeJ2jUV+FNWnSy9iIE9bOV9nCNQ4it+K9BpCNT9JsQOfZYznWGSbMw+Wa4uusEss0IcL/trFVoRxS6IuAA==}
engines: {node: '>=8.0.0'} engines: {node: '>=8.0.0'}
fast-content-type-parse@2.0.1:
resolution: {integrity: sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==}
fast-deep-equal@3.1.3: fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
@ -10296,6 +10357,9 @@ packages:
unist-util-visit@5.0.0: unist-util-visit@5.0.0:
resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
universal-user-agent@7.0.2:
resolution: {integrity: sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==}
universalify@0.1.2: universalify@0.1.2:
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
engines: {node: '>= 4.0.0'} engines: {node: '>= 4.0.0'}
@ -13098,6 +13162,68 @@ snapshots:
- supports-color - supports-color
- ts-node - ts-node
'@octokit/auth-token@5.1.1': {}
'@octokit/core@6.1.3':
dependencies:
'@octokit/auth-token': 5.1.1
'@octokit/graphql': 8.1.2
'@octokit/request': 9.1.4
'@octokit/request-error': 6.1.6
'@octokit/types': 13.7.0
before-after-hook: 3.0.2
universal-user-agent: 7.0.2
'@octokit/endpoint@10.1.2':
dependencies:
'@octokit/types': 13.7.0
universal-user-agent: 7.0.2
'@octokit/graphql@8.1.2':
dependencies:
'@octokit/request': 9.1.4
'@octokit/types': 13.7.0
universal-user-agent: 7.0.2
'@octokit/openapi-types@23.0.1': {}
'@octokit/plugin-paginate-rest@11.4.0(@octokit/core@6.1.3)':
dependencies:
'@octokit/core': 6.1.3
'@octokit/types': 13.7.0
'@octokit/plugin-request-log@5.3.1(@octokit/core@6.1.3)':
dependencies:
'@octokit/core': 6.1.3
'@octokit/plugin-rest-endpoint-methods@13.3.0(@octokit/core@6.1.3)':
dependencies:
'@octokit/core': 6.1.3
'@octokit/types': 13.7.0
'@octokit/request-error@6.1.6':
dependencies:
'@octokit/types': 13.7.0
'@octokit/request@9.1.4':
dependencies:
'@octokit/endpoint': 10.1.2
'@octokit/request-error': 6.1.6
'@octokit/types': 13.7.0
fast-content-type-parse: 2.0.1
universal-user-agent: 7.0.2
'@octokit/rest@21.1.0':
dependencies:
'@octokit/core': 6.1.3
'@octokit/plugin-paginate-rest': 11.4.0(@octokit/core@6.1.3)
'@octokit/plugin-request-log': 5.3.1(@octokit/core@6.1.3)
'@octokit/plugin-rest-endpoint-methods': 13.3.0(@octokit/core@6.1.3)
'@octokit/types@13.7.0':
dependencies:
'@octokit/openapi-types': 23.0.1
'@parcel/watcher-android-arm64@2.5.0': '@parcel/watcher-android-arm64@2.5.0':
optional: true optional: true
@ -16393,6 +16519,8 @@ snapshots:
dependencies: dependencies:
tweetnacl: 0.14.5 tweetnacl: 0.14.5
before-after-hook@3.0.2: {}
better-path-resolve@1.0.0: better-path-resolve@1.0.0:
dependencies: dependencies:
is-windows: 1.0.2 is-windows: 1.0.2
@ -17601,8 +17729,8 @@ snapshots:
'@typescript-eslint/parser': 8.15.0(eslint@8.57.1)(typescript@5.6.3) '@typescript-eslint/parser': 8.15.0(eslint@8.57.1)(typescript@5.6.3)
eslint: 8.57.1 eslint: 8.57.1
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1) eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1)
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1)
eslint-plugin-react: 7.37.2(eslint@8.57.1) eslint-plugin-react: 7.37.2(eslint@8.57.1)
eslint-plugin-react-hooks: 5.0.0(eslint@8.57.1) eslint-plugin-react-hooks: 5.0.0(eslint@8.57.1)
@ -17630,37 +17758,37 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1): eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1):
dependencies: dependencies:
'@nolyfill/is-core-module': 1.0.39 '@nolyfill/is-core-module': 1.0.39
debug: 4.4.0(supports-color@9.4.0) debug: 4.4.0(supports-color@9.4.0)
enhanced-resolve: 5.17.1 enhanced-resolve: 5.17.1
eslint: 8.57.1 eslint: 8.57.1
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
fast-glob: 3.3.2 fast-glob: 3.3.2
get-tsconfig: 4.8.1 get-tsconfig: 4.8.1
is-bun-module: 1.2.1 is-bun-module: 1.2.1
is-glob: 4.0.3 is-glob: 4.0.3
optionalDependencies: optionalDependencies:
eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
transitivePeerDependencies: transitivePeerDependencies:
- '@typescript-eslint/parser' - '@typescript-eslint/parser'
- eslint-import-resolver-node - eslint-import-resolver-node
- eslint-import-resolver-webpack - eslint-import-resolver-webpack
- supports-color - supports-color
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): eslint-module-utils@2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
dependencies: dependencies:
debug: 3.2.7 debug: 3.2.7
optionalDependencies: optionalDependencies:
'@typescript-eslint/parser': 8.15.0(eslint@8.57.1)(typescript@5.6.3) '@typescript-eslint/parser': 8.15.0(eslint@8.57.1)(typescript@5.6.3)
eslint: 8.57.1 eslint: 8.57.1
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1) eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0)(eslint@8.57.1)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1):
dependencies: dependencies:
'@rtsao/scc': 1.1.0 '@rtsao/scc': 1.1.0
array-includes: 3.1.8 array-includes: 3.1.8
@ -17671,7 +17799,7 @@ snapshots:
doctrine: 2.1.0 doctrine: 2.1.0
eslint: 8.57.1 eslint: 8.57.1
eslint-import-resolver-node: 0.3.9 eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.15.0(eslint@8.57.1)(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.1)
hasown: 2.0.2 hasown: 2.0.2
is-core-module: 2.15.1 is-core-module: 2.15.1
is-glob: 4.0.3 is-glob: 4.0.3
@ -17981,6 +18109,8 @@ snapshots:
pure-rand: 6.1.0 pure-rand: 6.1.0
optional: true optional: true
fast-content-type-parse@2.0.1: {}
fast-deep-equal@3.1.3: {} fast-deep-equal@3.1.3: {}
fast-equals@5.0.1: {} fast-equals@5.0.1: {}
@ -21902,6 +22032,8 @@ snapshots:
unist-util-is: 6.0.0 unist-util-is: 6.0.0
unist-util-visit-parents: 6.0.1 unist-util-visit-parents: 6.0.1
universal-user-agent@7.0.2: {}
universalify@0.1.2: {} universalify@0.1.2: {}
universalify@2.0.1: {} universalify@2.0.1: {}