diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3d6c1d1..27050d8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,7 +2,7 @@ name: CI & CD on: push: branches: - - '*' + - '**' jobs: Build-and-Deploy: env: @@ -10,13 +10,12 @@ jobs: VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} runs-on: ubuntu-latest - permissions: - contents: write steps: - uses: actions/checkout@v4 if: ${{ env.VERCEL_TOKEN && env.VERCEL_ORG_ID && env.VERCEL_PROJECT_ID }} - name: Deploy to Vercel + id: vercel-deployment uses: amondnet/vercel-action@v25 if: ${{ env.VERCEL_TOKEN && env.VERCEL_ORG_ID && env.VERCEL_PROJECT_ID }} with: @@ -25,4 +24,35 @@ jobs: vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} working-directory: ./ - vercel-args: ${{ github.ref == 'refs/heads/main' && ' --prod' || '' }} + vercel-args: ${{ github.ref == 'refs/heads/main' && ' --prod' || ''}} + + - name: Lark notification + uses: foxundermoon/feishu-action@v2 + with: + url: ${{ secrets.LARK_CHATBOT_HOOK_URL }} + msg_type: post + content: | + post: + zh_cn: + title: Vercel 预览环境 + content: + - - tag: text + text: Git 仓库: + - tag: a + text: ${{ github.server_url }}/${{ github.repository }} + href: ${{ github.server_url }}/${{ github.repository }} + - - tag: text + text: 代码分支: + - tag: a + text: ${{ github.ref }} + href: ${{ github.server_url }}/${{ github.repository }}/tree/${{ github.ref_name }} + - - tag: text + text: 提交作者: + - tag: a + text: ${{ github.actor }} + href: ${{ github.server_url }}/${{ github.actor }} + - - tag: text + text: 预览链接: + - tag: a + text: ${{ steps.vercel-deployment.outputs.preview-url }} + href: ${{ steps.vercel-deployment.outputs.preview-url }} diff --git a/components/SocialIconBar.tsx b/components/SocialIconBar.tsx index bf1b1f3..8f9fc1a 100644 --- a/components/SocialIconBar.tsx +++ b/components/SocialIconBar.tsx @@ -1,6 +1,8 @@ import { faBilibili, + faFreeCodeCamp, faGithub, + faLinkedin, faTwitter, faWeibo, faWeixin, @@ -22,6 +24,8 @@ const IconMap = { youtube: { icon: faYoutube, color: 'danger' }, bilibili: { icon: faBilibili, color: 'primary' }, podcast: { icon: faPodcast, color: 'warning' }, + fcc: { icon: faFreeCodeCamp, color: 'success' }, + linkedin: { icon: faLinkedin, color: 'primary' }, }; export type SocialIconBarProps = Pick & diff --git a/models/Base.ts b/models/Base.ts index 7c26369..fe912fa 100644 --- a/models/Base.ts +++ b/models/Base.ts @@ -1,4 +1,5 @@ import { HTTPClient } from 'koajax'; +import { githubClient } from 'mobx-github'; export const isServer = () => typeof window === 'undefined'; @@ -14,3 +15,25 @@ export const ownClient = new HTTPClient({ baseURI: `${API_Host}/api/`, responseType: 'json', }); + +const GithubToken = process.env.GITHUB_TOKEN; + +if (!isServer()) githubClient.baseURI = `${API_Host}/api/GitHub/`; + +githubClient.use(({ request }, next) => { + if (GithubToken) + request.headers = { + authorization: `Bearer ${GithubToken}`, + ...request.headers, + }; + + return next(); +}); + +export { githubClient }; + +export interface Base { + id: number; + createdAt: string; + updatedAt: string; +} diff --git a/models/Repository.ts b/models/Repository.ts index 7ced930..2942d72 100644 --- a/models/Repository.ts +++ b/models/Repository.ts @@ -1,26 +1,10 @@ import { - githubClient, GitRepository, RepositoryFilter, RepositoryModel as GitRepoModel, } from 'mobx-github'; import { Stream } from 'mobx-restful'; -import { API_Host, isServer } from './Base'; - -const GithubToken = process.env.GITHUB_TOKEN; - -if (!isServer()) githubClient.baseURI = `${API_Host}/api/GitHub/`; - -githubClient.use(({ request }, next) => { - if (GithubToken) - request.headers = { - authorization: `Bearer ${GithubToken}`, - ...request.headers, - }; - return next(); -}); - const MainOrganization = 'freeCodeCamp-China'; export class RepositoryModel extends Stream( @@ -50,7 +34,7 @@ export class RepositoryModel extends Stream( } if (hasAdditional) - // @ts-ignore + // @ts-expect-error Mixin type issue yield await this.getOne('freeCodeCamp/chinese', filter.relation); } } diff --git a/models/User.ts b/models/User.ts new file mode 100644 index 0000000..577ca70 --- /dev/null +++ b/models/User.ts @@ -0,0 +1,21 @@ +import { HTTPClient } from 'koajax'; +import { SupabaseListModel } from 'mobx-supabase'; + +import { Base } from './Base'; + +export enum Gender { + Female = 0, + Male = 1, + Other = 2, +} + +export interface User extends Base { + email: string; + gender?: Gender; + birthday?: string; +} + +export class UserModel extends SupabaseListModel { + baseURI = 'user'; + client = new HTTPClient(); +} diff --git a/next.config.ts b/next.config.ts index 72dfff4..0e1b359 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,3 +1,4 @@ +import { NextConfig } from 'next'; import setPWA from 'next-pwa'; // @ts-expect-error No types available import withLess from 'next-with-less'; @@ -10,4 +11,21 @@ const { NODE_ENV, CI } = process.env, disable: NODE_ENV === 'development', }); -export default withLess(withPWA({ output: CI ? 'standalone' : undefined })); +const rewrites: NextConfig['rewrites'] = async () => ({ + beforeFiles: [ + { + source: '/proxy/github.com/:path*', + destination: 'https://github.com/:path*', + }, + { + source: '/proxy/raw.githubusercontent.com/:path*', + destination: 'https://raw.githubusercontent.com/:path*', + }, + ], + afterFiles: [], + fallback: [], +}); + +export default withLess( + withPWA({ output: CI ? 'standalone' : undefined, rewrites }), +); diff --git a/package.json b/package.json index db78a7a..510382d 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@fortawesome/react-fontawesome": "^0.2.6", "@giscus/react": "^3.1.0", "idea-react": "^2.0.0-rc.13", + "koa": "^3.1.1", "koajax": "^3.1.2", "less": "^4.4.2", "less-loader": "^12.3.0", @@ -22,8 +23,10 @@ "mobx-github": "^0.6.2", "mobx-i18n": "^0.7.2", "mobx-react": "^9.2.1", + "mobx-react-helper": "^0.5.1", "mobx-restful": "^2.1.4", "mobx-restful-table": "^2.5.5", + "mobx-supabase": "^0.1.0", "next": "^16.0.1", "next-pwa": "~5.6.0", "next-ssr-middleware": "^1.0.3", @@ -44,6 +47,7 @@ "@softonus/prettier-plugin-duplicate-remover": "^1.1.2", "@stylistic/eslint-plugin": "^5.5.0", "@types/eslint-config-prettier": "^6.11.3", + "@types/koa": "^3.0.1", "@types/lodash": "^4.17.20", "@types/next-pwa": "^5.6.9", "@types/node": "^22.19.0", @@ -52,6 +56,7 @@ "eslint-config-prettier": "^10.1.8", "eslint-plugin-react": "^7.37.5", "eslint-plugin-simple-import-sort": "^12.1.1", + "git-utility": "^0.2.1", "globals": "^16.5.0", "husky": "^9.1.7", "jiti": "^2.6.1", @@ -86,6 +91,7 @@ "scripts": { "prepare": "husky || true", "install": "next typegen", + "postinstall": "xgit download https://github.com/freeCodeCamp-China/key-vault main Web-site || true", "dev": "next dev --webpack", "build": "next build --webpack", "start": "next start", diff --git a/pages/api/GitHub/[...slug].ts b/pages/api/GitHub/[...slug].ts index b4f24b4..df28ca6 100644 --- a/pages/api/GitHub/[...slug].ts +++ b/pages/api/GitHub/[...slug].ts @@ -1,3 +1,12 @@ -import { proxyGithub } from './core'; +import { createKoaRouter, withKoaRouter } from 'next-ssr-middleware'; -export default proxyGithub(); +import { safeAPI } from '../core'; +import { proxyGitHubAll } from './core'; + +export const config = { api: { bodyParser: false } }; + +const router = createKoaRouter(import.meta.url); + +router.get('/(.*)', safeAPI, proxyGitHubAll); + +export default withKoaRouter(router); diff --git a/pages/api/GitHub/core.ts b/pages/api/GitHub/core.ts index e99a69e..8d339aa 100644 --- a/pages/api/GitHub/core.ts +++ b/pages/api/GitHub/core.ts @@ -1,22 +1,36 @@ -import { githubClient } from 'mobx-github'; +import { Context, Middleware } from 'koa'; +import { githubOAuth2 } from 'next-ssr-middleware'; -import { safeAPI } from '../core'; +import { githubClient } from '../../../models/Base'; -export const proxyGithub = (dataFilter?: (path: string, data: T) => T) => - safeAPI(async ({ method, url, headers, body }, response) => { - delete headers.host; +export const proxyGithub = async ({ + method, + url, + headers: { host, ...headers }, + request, +}: Context) => { + const path = url!.slice(`/api/GitHub/`.length), + body = Reflect.get(request, 'body'); - const path = url!.slice(`/api/GitHub/`.length); + // @ts-expect-error KoAJAX type compatibility + return githubClient.request({ method, path, headers, body }); +}; - const { status, body: data } = await githubClient.request({ - // @ts-expect-error Upstream Type compatibility issue - method, - path, - // @ts-expect-error Upstream Type compatibility issue - headers, - body: body || undefined, - }); +export const proxyGitHubAll: Middleware = async context => { + const { status, body } = await proxyGithub(context); - response.status(status); - response.send(dataFilter?.(path, data as T) || data); - }); + context.status = status; + context.body = body; +}; + +const client_id = process.env.GITHUB_OAUTH_CLIENT_ID!, + client_secret = process.env.GITHUB_OAUTH_CLIENT_SECRET!; + +export const ProxyBaseURL = 'https://chinese.fcc-cd.dev/proxy'; + +export const githubSigner = githubOAuth2({ + rootBaseURL: process.env.VERCEL ? undefined : `${ProxyBaseURL}/github.com/`, + client_id, + client_secret, + scopes: ['user:email', 'read:user', 'public_repo', 'read:project'], +}); diff --git a/pages/api/core.ts b/pages/api/core.ts index 222dfb0..7df62a4 100644 --- a/pages/api/core.ts +++ b/pages/api/core.ts @@ -1,44 +1,38 @@ +import { Context, Middleware } from 'koa'; import { HTTPError } from 'koajax'; -import { NextApiRequest, NextApiResponse } from 'next'; import { ProxyAgent, setGlobalDispatcher } from 'undici'; const { HTTP_PROXY } = process.env; if (HTTP_PROXY) setGlobalDispatcher(new ProxyAgent(HTTP_PROXY)); -export type NextAPI = ( - req: NextApiRequest, - res: NextApiResponse, -) => Promise; - -export function safeAPI(handler: NextAPI): NextAPI { - return async (req, res) => { - try { - return await handler(req, res); - } catch (error) { - if (!(error instanceof HTTPError)) { - console.error(error); - - res.status(400); - return res.send({ message: (error as Error).message }); - } - const { message, response } = error; - let { body } = response; - - res.status(response.status); - res.statusMessage = message; - - if (body instanceof ArrayBuffer) - try { - body = new TextDecoder().decode(new Uint8Array(body)); - console.error(body); - - body = JSON.parse(body); - console.error(body); - } catch { - // - } - res.send(body); +export const safeAPI: Middleware = async (context: Context, next) => { + try { + return await next(); + } catch (error) { + if (!(error instanceof HTTPError)) { + console.error(error); + + context.status = 400; + + return (context.body = { message: (error as Error).message }); } - }; -} + const { message, response } = error; + let { body } = response; + + context.status = response.status; + context.statusMessage = message; + + if (body instanceof ArrayBuffer) + try { + body = new TextDecoder().decode(new Uint8Array(body)); + + body = JSON.parse(body); + } catch { + // + } + console.error(JSON.stringify(body, null, 2)); + + context.body = body; + } +}; diff --git a/pages/api/user.ts b/pages/api/user.ts new file mode 100644 index 0000000..f8b5ab8 --- /dev/null +++ b/pages/api/user.ts @@ -0,0 +1,89 @@ +import { HTTPClient } from 'koajax'; + +export type FCCUserBase = Record< + | 'name' + | `username${'' | 'Display'}` + | 'picture' + | 'about' + | 'location' + | 'website' + | 'linkedIn' + | 'githubProfile', + string +>; + +export type FCCUserStatus = Record< + 'isHonest' | 'isCheater' | 'isDonating', + boolean +>; + +export type FCCCertificationUserStatus = Record< + | 'is2018DataVisCert' + | 'is2018FullStackCert' + | 'isA2EnglishCert' + | 'isApisMicroservicesCert' + | 'isBackEndCert' + | 'isCollegeAlgebraPyCertV8' + | 'isDataAnalysisPyCertV7' + | 'isDataVisCert' + | 'isFoundationalCSharpCertV8' + | 'isFrontEndCert' + | 'isFrontEndLibsCert' + | 'isFullStackCert' + | 'isJavascriptCertV9' + | 'isInfosecCertV7' + | 'isInfosecQaCert' + | 'isJsAlgoDataStructCert' + | 'isJsAlgoDataStructCertV8' + | 'isMachineLearningPyCertV7' + | 'isQaCertV7' + | 'isRelationalDatabaseCertV8' + | 'isRespWebDesignCert' + | 'isRespWebDesignCertV9' + | 'isSciCompPyCertV7', + boolean +>; + +export interface CompletedChallenge { + id: string; + completedDate: number; + files: any[]; +} + +export type FCCUserPrivacySettings = Record< + | 'isLocked' + | `show${'About' | 'Certs' | 'Donation' | 'HeatMap' | 'Location' | 'Name' | 'Points' | 'Portfolio' | 'TimeLine'}`, + boolean +>; + +export interface FCCUserProfile + extends FCCUserBase, + FCCUserStatus, + FCCCertificationUserStatus { + joinDate: Date; + calendar: Record; + completedChallenges: CompletedChallenge[]; + completedExams: any[]; + points: number; + portfolio: any[]; + profileUI: FCCUserPrivacySettings; + yearsTopContributor: string[]; +} + +const fccClient = new HTTPClient({ + baseURI: 'https://api.freecodecamp.org/', + responseType: 'json', +}); + +interface FCCUserQuery { + entities: { user: Record }; + result: string; +} + +export async function getUserProfile(username: string) { + const { body } = await fccClient.get( + `users/get-public-profile?${new URLSearchParams({ username })}`, + ); + + return body!.entities.user[body!.result]; +} diff --git a/pages/user/[username].tsx b/pages/user/[username].tsx new file mode 100644 index 0000000..d56ae87 --- /dev/null +++ b/pages/user/[username].tsx @@ -0,0 +1,94 @@ +import { cache, compose, errorLogger } from 'next-ssr-middleware'; +import { FC } from 'react'; +import { Card, Col, Container, Image, Row } from 'react-bootstrap'; + +import { CommentBox } from '../../components/CommentBox'; +import { PageHead } from '../../components/PageHead'; +import { SocialIconBar } from '../../components/SocialIconBar'; +import { FCCUserProfile, getUserProfile } from '../api/user'; + +export const getServerSideProps = compose<{ username: string }>( + cache(), + errorLogger, + async ({ params }) => { + const userProfile = await getUserProfile(params!.username); + + return { props: JSON.parse(JSON.stringify(userProfile)) }; + }, +); + +const UserProfilePage: FC = ({ + username, + usernameDisplay, + name, + picture, + about, + location, + website, + linkedIn, + githubProfile, + joinDate, + points, + completedChallenges, +}) => ( + + + + + + {name + + +
+

{name || usernameDisplay || username}

+

@{usernameDisplay || username}

+
+ {location && ( +
+ 位置: + {location} +
+ )} + + {about &&
{about}
} + + + +
+ + + + + {points} +
积分
+ + + {completedChallenges?.length || 0} +
完成的挑战
+ +
+
+ + +
+); + +export default UserProfilePage; diff --git a/pages/user/session.tsx b/pages/user/session.tsx new file mode 100644 index 0000000..7897494 --- /dev/null +++ b/pages/user/session.tsx @@ -0,0 +1,48 @@ +import { observer } from 'mobx-react'; +import { ObservedComponent } from 'mobx-react-helper'; +import { RestForm } from 'mobx-restful-table'; +import { compose } from 'next-ssr-middleware'; +import { Container } from 'react-bootstrap'; + +import { PageHead } from '../../components/PageHead'; +import { i18n, I18nContext } from '../../models/Translation'; +import { UserModel } from '../../models/User'; +import { githubSigner } from '../api/GitHub/core'; + +export const getServerSideProps = compose(githubSigner); + +@observer +export default class UserSessionPage extends ObservedComponent< + {}, + typeof i18n +> { + static contextType = I18nContext; + + store = new UserModel(); + + render() { + return ( + + + +

登录用户信息

+ + +
+ ); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4bff892..c9a210a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: idea-react: specifier: ^2.0.0-rc.13 version: 2.0.0-rc.13(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react-is@16.13.1)(react@19.2.0)(typescript@5.9.3) + koa: + specifier: ^3.1.1 + version: 3.1.1 koajax: specifier: ^3.1.2 version: 3.1.2(typescript@5.9.3) @@ -56,12 +59,18 @@ importers: mobx-react: specifier: ^9.2.1 version: 9.2.1(mobx@6.15.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + mobx-react-helper: + specifier: ^0.5.1 + version: 0.5.1(mobx@6.15.0)(react@19.2.0)(typescript@5.9.3) mobx-restful: specifier: ^2.1.4 version: 2.1.4(mobx@6.15.0)(typescript@5.9.3) mobx-restful-table: specifier: ^2.5.5 version: 2.5.5(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3) + mobx-supabase: + specifier: ^0.1.0 + version: 0.1.0(mobx@6.15.0)(typescript@5.9.3) next: specifier: ^16.0.1 version: 16.0.1(@babel/core@7.28.5)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -117,6 +126,9 @@ importers: '@types/eslint-config-prettier': specifier: ^6.11.3 version: 6.11.3 + '@types/koa': + specifier: ^3.0.1 + version: 3.0.1 '@types/lodash': specifier: ^4.17.20 version: 4.17.20 @@ -141,6 +153,9 @@ importers: eslint-plugin-simple-import-sort: specifier: ^12.1.1 version: 12.1.1(eslint@9.39.1(jiti@2.6.1)) + git-utility: + specifier: ^0.2.1 + version: 0.2.1(typescript@5.9.3) globals: specifier: ^16.5.0 version: 16.5.0 @@ -1406,6 +1421,13 @@ packages: '@swc/helpers@0.5.17': resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} + '@tech_query/node-toolkit@2.0.0-beta.3': + resolution: {integrity: sha512-ShhOWszgwwq19UlbtI8BCbPz7H4XLVk41fydKbUxwnVK1Fa24CE8imkPbddpOG3e6NEdOujKkCHKtAY84VVtqg==} + engines: {node: '>=20'} + + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + '@types/accepts@1.3.7': resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} @@ -1587,6 +1609,10 @@ packages: resolution: {integrity: sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -1726,6 +1752,9 @@ packages: resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} engines: {node: '>= 0.6.0'} + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + baseline-browser-mapping@2.8.25: resolution: {integrity: sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==} hasBin: true @@ -1757,6 +1786,9 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + builtin-modules@3.3.0: resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} engines: {node: '>=6'} @@ -1836,6 +1868,9 @@ packages: colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + commander-jsx@0.7.2: + resolution: {integrity: sha512-7hNzASXVkyDfbdAMYJ6lb4ZuQ8/PNqqogpHOSMGHKfIqxxeW8wDrdQKK1rlPKdikOuF427jXtXSF8AWRKHqp5g==} + commander@14.0.2: resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} engines: {node: '>=20'} @@ -2164,9 +2199,17 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2198,6 +2241,10 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + file-type@16.5.4: + resolution: {integrity: sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==} + engines: {node: '>=10'} + filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} @@ -2232,6 +2279,10 @@ packages: resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} engines: {node: '>= 0.6'} + fs-extra@11.3.2: + resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} + engines: {node: '>=14.14'} + fs-extra@9.1.0: resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} engines: {node: '>=10'} @@ -2288,6 +2339,10 @@ packages: giscus@1.6.0: resolution: {integrity: sha512-Zrsi8r4t1LVW950keaWcsURuZUQwUaMKjvJgTCY125vkW6OiEBkatE7ScJDbpqKHdZwb///7FVC21SE3iFK3PQ==} + git-utility@0.2.1: + resolution: {integrity: sha512-SWA62OzFey2EgJfj1ZGBHgV0zY5ZTL8Qdug8aKgpPeJ2Ajy2/imZbNpZiP0t6wdAgfm6S5jrvKN83LX3GlV3yQ==} + hasBin: true + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -2402,6 +2457,9 @@ packages: react: '>=16' react-dom: '>=16' + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -2870,6 +2928,11 @@ packages: engines: {node: '>=4'} hasBin: true + mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + mimic-function@5.0.1: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} @@ -2945,6 +3008,9 @@ packages: peerDependencies: mobx: '>=6.11' + mobx-supabase@0.1.0: + resolution: {integrity: sha512-i/RJePbEmjRmZh1iT20SemJ0KQSqQYBIwp1JSDitbNmhfHEilVWWlsc7vynx+DehfXDqxFOMJXVs4nM195pj7A==} + mobx@6.15.0: resolution: {integrity: sha512-UczzB+0nnwGotYSgllfARAqWCJ5e/skuV2K/l+Zyck/H6pJIhLXuBnz+6vn2i211o7DtbE78HQtsYEKICHGI+g==} @@ -3126,6 +3192,10 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + peek-readable@4.1.0: + resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==} + engines: {node: '>=8'} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -3205,6 +3275,10 @@ packages: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + prop-types-extra@1.1.1: resolution: {integrity: sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==} peerDependencies: @@ -3281,6 +3355,14 @@ packages: resolution: {integrity: sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==} engines: {node: '>=0.10.0'} + readable-stream@4.7.0: + resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + readable-web-to-node-stream@3.0.4: + resolution: {integrity: sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==} + engines: {node: '>=8'} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -3545,6 +3627,9 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + stringify-object@3.3.0: resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==} engines: {node: '>=4'} @@ -3561,6 +3646,10 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strtok3@6.3.0: + resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==} + engines: {node: '>=10'} + styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -3630,6 +3719,10 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + token-types@4.2.1: + resolution: {integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==} + engines: {node: '>=10'} + tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} @@ -3903,6 +3996,11 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zx@8.8.5: + resolution: {integrity: sha512-SNgDF5L0gfN7FwVOdEFguY3orU5AkfFZm9B5YSHog/UDHv+lvmd82ZAsOenOkQixigwH2+yyH198AwNdKhj+RA==} + engines: {node: '>= 12.17.0'} + hasBin: true + snapshots: '@apideck/better-ajv-errors@0.3.6(ajv@8.17.1)': @@ -5278,6 +5376,15 @@ snapshots: dependencies: tslib: 2.8.1 + '@tech_query/node-toolkit@2.0.0-beta.3': + dependencies: + file-type: 16.5.4 + fs-extra: 11.3.2 + mime: 3.0.0 + yaml: 2.8.1 + + '@tokenizer/token@0.3.0': {} + '@types/accepts@1.3.7': dependencies: '@types/node': 22.19.0 @@ -5530,6 +5637,10 @@ snapshots: '@typescript-eslint/types': 8.46.3 eslint-visitor-keys: 4.2.1 + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + accepts@1.3.8: dependencies: mime-types: 2.1.35 @@ -5695,6 +5806,8 @@ snapshots: base64-arraybuffer@1.0.2: {} + base64-js@1.5.1: {} + baseline-browser-mapping@2.8.25: {} big.js@5.2.2: {} @@ -5726,6 +5839,11 @@ snapshots: buffer-from@1.1.2: {} + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + builtin-modules@3.3.0: {} bytes@3.1.2: {} @@ -5804,6 +5922,15 @@ snapshots: colorette@2.0.20: {} + commander-jsx@0.7.2(typescript@5.9.3): + dependencies: + '@tech_query/node-toolkit': 2.0.0-beta.3 + tslib: 2.8.1 + web-utility: 4.6.4(typescript@5.9.3) + transitivePeerDependencies: + - element-internals-polyfill + - typescript + commander@14.0.2: {} commander@2.20.3: {} @@ -6255,8 +6382,12 @@ snapshots: esutils@2.0.3: {} + event-target-shim@5.0.1: {} + eventemitter3@5.0.1: {} + events@3.3.0: {} + fast-deep-equal@3.1.3: {} fast-equals@5.3.2: {} @@ -6291,6 +6422,12 @@ snapshots: dependencies: flat-cache: 4.0.1 + file-type@16.5.4: + dependencies: + readable-web-to-node-stream: 3.0.4 + strtok3: 6.3.0 + token-types: 4.2.1 + filelist@1.0.4: dependencies: minimatch: 5.1.6 @@ -6328,6 +6465,12 @@ snapshots: fresh@0.5.2: {} + fs-extra@11.3.2: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + fs-extra@9.1.0: dependencies: at-least-node: 1.0.0 @@ -6391,6 +6534,14 @@ snapshots: dependencies: lit: 3.3.1 + git-utility@0.2.1(typescript@5.9.3): + dependencies: + commander-jsx: 0.7.2(typescript@5.9.3) + zx: 8.8.5 + transitivePeerDependencies: + - element-internals-polyfill + - typescript + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -6535,6 +6686,8 @@ snapshots: - react-native - typescript + ieee754@1.2.1: {} + ignore@5.3.2: {} ignore@7.0.5: {} @@ -7013,6 +7166,8 @@ snapshots: mime@1.6.0: optional: true + mime@3.0.0: {} + mimic-function@5.0.1: {} minimatch@10.1.1: @@ -7134,6 +7289,19 @@ snapshots: - jsdom - typescript + mobx-supabase@0.1.0(mobx@6.15.0)(typescript@5.9.3): + dependencies: + '@swc/helpers': 0.5.17 + koajax: 3.1.2(typescript@5.9.3) + mobx-restful: 2.1.4(mobx@6.15.0)(typescript@5.9.3) + web-utility: 4.6.4(typescript@5.9.3) + transitivePeerDependencies: + - core-js + - element-internals-polyfill + - jsdom + - mobx + - typescript + mobx@6.15.0: {} ms@2.1.3: {} @@ -7330,6 +7498,8 @@ snapshots: path-type@4.0.0: {} + peek-readable@4.1.0: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -7385,6 +7555,8 @@ snapshots: prismjs@1.30.0: {} + process@0.11.10: {} + prop-types-extra@1.1.1(react@19.2.0): dependencies: react: 19.2.0 @@ -7492,6 +7664,18 @@ snapshots: react@19.2.0: {} + readable-stream@4.7.0: + dependencies: + abort-controller: 3.0.0 + buffer: 6.0.3 + events: 3.3.0 + process: 0.11.10 + string_decoder: 1.3.0 + + readable-web-to-node-stream@3.0.4: + dependencies: + readable-stream: 4.7.0 + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -7828,6 +8012,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.1.1 + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + stringify-object@3.3.0: dependencies: get-own-enumerable-property-symbols: 3.0.2 @@ -7842,6 +8030,11 @@ snapshots: strip-json-comments@3.1.1: {} + strtok3@6.3.0: + dependencies: + '@tokenizer/token': 0.3.0 + peek-readable: 4.1.0 + styled-jsx@5.1.6(@babel/core@7.28.5)(react@19.2.0): dependencies: client-only: 0.0.1 @@ -7897,6 +8090,11 @@ snapshots: toidentifier@1.0.1: {} + token-types@4.2.1: + dependencies: + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + tr46@1.0.1: dependencies: punycode: 2.3.1 @@ -8257,3 +8455,5 @@ snapshots: yaml@2.8.1: {} yocto-queue@0.1.0: {} + + zx@8.8.5: {}