diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c5ee276 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +.idea +docker-compose.yml +node_modules +dist +*/tsconfig.tsbuildinfo +*/lib +backend/.env +backend/public diff --git a/.github/workflows/blah.yml b/.github/workflows/blah.yml new file mode 100644 index 0000000..48fe4fd --- /dev/null +++ b/.github/workflows/blah.yml @@ -0,0 +1,84 @@ +name: blah + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +concurrency: blah-ci + +jobs: + + verified: + runs-on: ubuntu-latest + needs: + - verify-backend + - verify-frontend + - verify-template + steps: + - uses: actions/checkout@v3 + - run: echo "44.481800,-88.054413" + + verify-backend: + runs-on: ubuntu-latest + services: + postgres: + image: postgres:16 + ports: + - 5432:5432 + env: + POSTGRES_DB: eighty4 + POSTGRES_USER: eighty4 + POSTGRES_PASSWORD: eighty4 + options: >- + --health-cmd pg_isready + --health-interval 5s + --health-timeout 5s + --health-retries 10 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 20 + - name: sql + run: | + sudo apt-get install -y postgresql-client + PGPASSWORD=eighty4 psql -U eighty4 -f backend/sql/v001-init-schema.sql eighty4 + - name: verify + run: | + corepack enable && corepack prepare pnpm@latest --activate + pnpm i + pnpm build + pnpm test + working-directory: backend + + verify-frontend: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 20 + - name: verify + run: | + corepack enable && corepack prepare pnpm@latest --activate + pnpm i + pnpm build + pnpm svg + working-directory: frontend + + verify-template: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 20 + - name: verify + run: | + corepack enable && corepack prepare pnpm@latest --activate + pnpm i + pnpm build + pnpm test + working-directory: template diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..088eeb1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +.DS_Store +.idea +node_modules +tsconfig.tsbuildinfo + +dist +backend/lib +backend/public +template/lib diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..034e6f1 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM node:20-alpine as build + +WORKDIR /install.sh +COPY . . + +RUN corepack enable && corepack prepare pnpm@latest --activate +RUN pnpm i +RUN pnpm --filter @eighty4/install-frontend build +RUN pnpm --filter @eighty4/install-backend build +RUN pnpm --filter @eighty4/install-backend --prod deploy dist + +FROM node:20-alpine + +WORKDIR /install.sh +COPY --from=build /install.sh/dist /install.sh + +EXPOSE 5741 + +CMD ["node", "lib/Api.js"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..35fc460 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# install.eighty4.tech (unfortunately not install.sh) + +## todos + +- preference mask-image css for color switches +- explain how screen +- flip theme toggle animation +- open graph paper animation +- #features animation +- graph paper loader animation +- lookup repository release ui +- configure binary distributions ui +- generate script +- cli auth sequence +- cli write script to local dir diff --git a/backend/.env b/backend/.env new file mode 100644 index 0000000..ff0d364 --- /dev/null +++ b/backend/.env @@ -0,0 +1,10 @@ +# https://github.com/settings/developers +GITHUB_CLIENT_ID="" +GITHUB_CLIENT_SECRET="" + +# https://node-postgres.com/features/connecting#environment-variables +PGHOST="localhost" +PGPORT="5432" +PGDATABASE="eighty4" +PGUSER="eighty4" +PGPASSWORD="eighty4" diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..ef29dcf --- /dev/null +++ b/backend/package.json @@ -0,0 +1,38 @@ +{ + "name": "@eighty4/install-backend", + "version": "0.0.1", + "private": true, + "author": "Adam McKee (Math.floor(Math.random() * 10000000)) + +describe('Database', () => { + + describe('saveUserLogin', () => { + it('saves new user', async () => { + const userId = randomUserId() + const newUser = await saveUserLogin(userId, 'adam@eighty4.tech', 'accessToken') + expect(newUser).toBe(true) + const result = await db.query('select * from install_sh.users where id = $1', [userId]) + expect(result.rows.length).toBe(1) + expect(result.rows[0].email).toBe('adam@eighty4.tech') + expect(result.rows[0].access_token).toBe('accessToken') + expect(result.rows[0].authed_when).toBeDefined() + expect(result.rows[0].authed_when).toStrictEqual(result.rows[0].created_when) + }) + it('saves login for existing user', async () => { + const userId = randomUserId() + let newUser = await saveUserLogin(userId, 'adam@eighty4.tech', 'accessToken0') + expect(newUser).toBe(true) + let result = await db.query('select * from install_sh.users where id = $1', [userId]) + expect(result.rows[0].email).toBe('adam@eighty4.tech') + expect(result.rows[0].access_token).toBe('accessToken0') + newUser = await saveUserLogin(userId, 'mckee@eighty4.tech', 'accessToken1') + expect(newUser).toBe(false) + result = await db.query('select * from install_sh.users where id = $1', [userId]) + expect(result.rows[0].email).toBe('mckee@eighty4.tech') + expect(result.rows[0].access_token).toBe('accessToken1') + expect(result.rows[0].authed_when).not.toStrictEqual(result.rows[0].created_when) + }) + }) +}) diff --git a/backend/src/Database.ts b/backend/src/Database.ts new file mode 100644 index 0000000..6322006 --- /dev/null +++ b/backend/src/Database.ts @@ -0,0 +1,42 @@ +import pg from 'pg' +import type {Repository} from '@eighty4/install-template' + +// https://node-postgres.com/features/connecting#environment-variables +const connectionPool = new pg.Pool({ + max: 20, + maxUses: 1000, +}) + +interface GeneratedScriptParams { + userId: number + repository: Repository + templateVersion: string +} + +export async function saveUserLogin(userId: number, email: string, accessToken: string): Promise { + const result = await connectionPool.query({ + name: 'save-user', + text: ` + insert into install_sh.users (id, email, access_token, created_when) + values ($1, $2, $3, now()) + on conflict (id) do update + set email = excluded.email, + access_token = excluded.access_token, + authed_when = now() + returning (created_when = authed_when) as new_user + `, + values: [userId, email, accessToken], + }) + return result.rows[0].new_user +} + +export async function saveGeneratedScript({userId, repository, templateVersion}: GeneratedScriptParams) { + await connectionPool.query({ + name: 'save-script', + text: ` + insert into install_sh.scripts (user_id, repo_owner, repo_name, template_version) + values ($1, $2, $3, $4) + `, + values: [userId, repository.owner, repository.name, templateVersion], + }) +} diff --git a/backend/src/Login.ts b/backend/src/Login.ts new file mode 100644 index 0000000..896fe4b --- /dev/null +++ b/backend/src/Login.ts @@ -0,0 +1,56 @@ +import {v4 as createUuid} from 'uuid' +import {saveUserLogin} from './Database.js' +import {fetchAccessToken, fetchEmail, fetchUserId} from './User.js' + +// todo https://blog.logrocket.com/complete-guide-abortcontroller-node-js +class PromiseMap { + private readonly resultById: Record }> = {} + private readonly resultByTimestamp: Array<{ id: string, timestamp: number }> = [] + + add(id: string, promise: Promise) { + const timestamp = Date.now() + this.resultByTimestamp.push({id, timestamp}) + this.resultById[id] = {timestamp, promise} + } + + remove(id: string) { + delete this.resultById[id] + this.resultByTimestamp.splice(this.resultByTimestamp.findIndex((obj: any) => obj.id === id), 1) + } + + resolve(id: string): Promise { + const result = this.resultById[id] + this.remove(id) + if (!result) { + throw new Error('aint shit here for you') + } + return result.promise + } +} + +export interface AuthedUser { + accessToken: string + newUser: boolean +} + +const map = new PromiseMap() + +async function loginSequence(code: string): Promise { + const accessToken = await fetchAccessToken(code) + const [email, userId] = await Promise.all([ + fetchEmail(accessToken), + fetchUserId(accessToken), + ]) + const newUser = await saveUserLogin(userId, email, accessToken) + return {accessToken, newUser} +} + +export async function initiateLogin(code: string): Promise { + const loginId = createUuid() + map.add(loginId, loginSequence(code)) + return loginId +} + +export async function resolveLogin(loginId: string): Promise { + return map.resolve(loginId) +} diff --git a/backend/src/Releases.ts b/backend/src/Releases.ts new file mode 100644 index 0000000..afe7822 --- /dev/null +++ b/backend/src/Releases.ts @@ -0,0 +1,82 @@ +import type {Architecture, OperatingSystem, Repository} from '@eighty4/install-template' +import {resolveDistribution} from './Resolution.js' + +export interface LatestReleaseRequest { + accessToken: string + repository: Repository +} + +export interface Asset { + contentType: string + filename: string +} + +export interface Binary extends Asset { + arch?: Architecture + os?: OperatingSystem +} + +export interface Release { + binaries: Array + otherAssets: Array + repository: Repository + tag: string + url: string +} + + +export async function queryLatestRelease({ + accessToken, + repository, + }: LatestReleaseRequest): Promise { + const query = `query { + repository(owner: "${repository.owner}", name: "${repository.name}") { + releases(first: 1, orderBy: {field: CREATED_AT, direction: DESC}) { + nodes { + releaseAssets(first: 100) { + nodes { + name + contentType + } + } + name + tagName + url + } + } + } +}` + const response = await fetch('https://api.github.com/graphql', { + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + accessToken, + }, + body: JSON.stringify({query}), + }) + if (response.status !== 200) { + throw new Error('queryLatestRelease status code ' + response.status) + } + const json = await response.json() + if (json.data.repository === null) { + return + } + console.log(json.data.repository.releases.nodes) + const release = json.data.repository.releases.nodes[0] + const {tagName: tag, url} = release + const binaries: Array = [] + const otherAssets: Array = [] + for (const {name: filename, contentType} of release.releaseAssets.nodes) { + const distribution = resolveDistribution(filename, contentType) + if (distribution) { + binaries.push({ + filename, + contentType, + arch: distribution.arch, + os: distribution.os, + }) + } else { + otherAssets.push({filename, contentType}) + } + } + return {tag, url, binaries, otherAssets, repository} +} diff --git a/backend/src/Repositories.ts b/backend/src/Repositories.ts new file mode 100644 index 0000000..e470ea6 --- /dev/null +++ b/backend/src/Repositories.ts @@ -0,0 +1,62 @@ +import type {Repository} from '@eighty4/install-template' + +export interface UserRepositoriesRequest { + accessToken: string +} + +export async function queryPinnedUserRepositories({accessToken}: UserRepositoriesRequest): Promise> { + const query = `viewer { + pinnedItems(first: 6, types: REPOSITORY) { + nodes { + ... on Repository { + name + owner { + login + } + } + } + } + } +}` + const response = await fetch('https://api.github.com/graphql', { + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + accessToken, + }, + body: JSON.stringify({query}), + }) + const json = await response.json() + const result = [] + for (const repo of json.data.viewer.pinnedItems.nodes) { + result.push({owner: repo.owner.login, name: repo.name}) + } + return result +} + +export async function queryUserRepositories({accessToken}: UserRepositoriesRequest): Promise> { + const query = `viewer { + repositories(first: 20, types: REPOSITORY, affiliations: [OWNER, COLLABORATOR, ORGANIZATION_MEMBER]) { + nodes { + ... on Repository { + name + owner { + login + } + } + } + } +}` + const response = await fetch('https://api.github.com/graphql', { + method: 'POST', + headers: { + 'Authorization': 'Bearer ' + accessToken, + }, + body: JSON.stringify({query}), + }) + const json = await response.json() + const result = [] + for (const repo of json.data.viewer.repositories.nodes) { + result.push({owner: repo.owner.login, name: repo.name}) + } + return result +} diff --git a/backend/src/Resolution.ts b/backend/src/Resolution.ts new file mode 100644 index 0000000..004dc64 --- /dev/null +++ b/backend/src/Resolution.ts @@ -0,0 +1,55 @@ +import {readFileSync} from 'fs' +import {Architecture, Distribution, OperatingSystem} from '@eighty4/install-template' + +const OS_BINARY_CONTENT_TYPES: Record = { + 'application/x-executable': 'Linux', + 'application/x-mach-binary': 'MacOS', + 'application/x-dosexec': 'Windows', +} + +const ARCH_LABELS: Record> = { + 'amd64': ['amd64', 'x64', 'x86_64'], + 'arm': ['arm'], + 'arm64': ['arm64', 'aarch64'], +} + +const OS_ARCHITECTURES: Record> = { + 'Linux': ['amd64', 'arm', 'arm64'], + 'MacOS': ['amd64', 'arm64'], + 'Windows': ['amd64', 'arm', 'arm64'], +} + +export function resolveDistribution(filename: string, contentType: string): Partial | undefined { + const os = resolveOperatingSystem(contentType) + if (os) { + const arch = resolveArchitecture(os, filename) + return {arch, os} + } +} + +function resolveArchitecture(os: OperatingSystem, filename: string): Architecture | undefined { + const lowercaseFilename = filename.toLowerCase() + for (const arch of OS_ARCHITECTURES[os]) { + for (const archLabel of ARCH_LABELS[arch]) { + if (lowercaseFilename.includes(archLabel)) { + return arch + } + } + } +} + +function resolveOperatingSystem(contentType: string): OperatingSystem | undefined { + return OS_BINARY_CONTENT_TYPES[contentType] +} + +export function resolveTemplateRuntimeVersion() { + try { + let packageJson = import.meta.resolve('../node_modules/@eighty4/install-template/package.json') + if (packageJson.startsWith('file:///')) { + packageJson = packageJson.substring(7) + } + return JSON.parse(readFileSync(packageJson).toString()).version + } catch (e) { + throw new Error('failed resolving version of @eighty4/install-template') + } +} diff --git a/backend/src/Server.ts b/backend/src/Server.ts new file mode 100644 index 0000000..75752ce --- /dev/null +++ b/backend/src/Server.ts @@ -0,0 +1,125 @@ +import net from 'node:net' +import express, {RequestHandler} from 'express' +import cookieParser from 'cookie-parser' +import {generateScript} from '@eighty4/install-template' +import {initiateLogin, resolveLogin} from './Login.js' +import {queryLatestRelease} from './Releases.js' +import {queryPinnedUserRepositories, queryUserRepositories} from './Repositories.js' +import {verifyAccessToken} from './User.js' + +const HTTP_PORT = parseInt(process.env.HTTP_PORT || '5741', 10) +const TCP_PORT = parseInt(process.env.TCP_PORT || '7411', 10) + +const app = express() +app.use(cookieParser()) +app.use(express.json()) +if (process.env['NODE_ENV'] === 'production') { + app.use(express.static('public')) +} + +app.use((req, res, next) => { + const start = Date.now() + res.on('finish', () => { + const uri = decodeURI(req.url) + const end = `${Date.now() - start}ms` + if (res.statusCode === 301) { + console.log(req.method, uri, res.statusCode, res.statusMessage, 'to', res.getHeaders().location, end) + } else { + console.log(req.method, uri, res.statusCode, res.statusMessage, end) + } + }) + next() +}) + +const authorize: RequestHandler = (req, res, next) => { + const accessToken = req.cookies.gat + verifyAccessToken(accessToken) + .then((user) => { + if (user === false) { + if (req.path.startsWith('/api/')) { + res.send(401) + } else { + res.redirect(301, 'http://localhost:5711') + } + } else { + req.user = user + next() + } + }) + .catch((e) => { + if (req.path.startsWith('/api/')) { + console.log('WTF', e.message) + res.send(500) + } else { + res.redirect(301, 'http://localhost:5711?error') + } + }) +} + +app.get('/login/oauth/github', async (req, res) => { + try { + const loginId = await initiateLogin(req.query.code as string) + res.redirect(301, 'http://localhost:5711?login=' + loginId) + } catch (e: any) { + console.log('auth redirect from github wtf', e.message) + res.redirect(301, 'http://localhost:5711?auth=failed') + } +}) + +app.get('/login/notify', async (req, res) => { + const loginId = req.query.login as string + if (!loginId || !loginId.length) { + res.status(400).send() + } else { + try { + const authData = await resolveLogin(loginId) + res.setHeader('Set-Cookie', `gat=${authData.accessToken}; Secure; HttpOnly; SameSite=Strict; Path=/api`) + res.json({newUser: authData.newUser}) + } catch (e: any) { + console.log('auth resolve wtf', e.message) + res.status(500).send() + } + } +}) + +app.get('/api/repositories', authorize, async (req, res) => { + res.json(await queryUserRepositories({accessToken: req.user!.accessToken})) +}) + +app.get('/api/repositories/pinned', authorize, async (req, res) => { + res.json(await queryPinnedUserRepositories({accessToken: req.user!.accessToken})) +}) + +app.get('/api/release/:repoOwner/:repoName', authorize, async (req, res) => { + const release = await queryLatestRelease({ + accessToken: req.user!.accessToken, repository: { + owner: req.params['repoOwner'], + name: req.params['repoName'], + }, + }) + if (release) { + res.json(release) + } else { + res.status(404).send() + } +}) + +app.post('/api/script', (req, res) => { + const script = generateScript(req.body) + res.set('Content-Type', 'text/plain') + res.set('Content-Disposition', 'inline; filename="draft.sh"') + res.send(script) +}) + +app.listen(HTTP_PORT, () => { + console.log('install.sh http listening on', HTTP_PORT) +}) + +const tcp = net.createServer((c) => { + console.log('#feelthezig') + c.end() +}) + +tcp.listen(TCP_PORT, () => { + console.log('install.sh tcp listening on', TCP_PORT) +}) diff --git a/backend/src/User.ts b/backend/src/User.ts new file mode 100644 index 0000000..240bd25 --- /dev/null +++ b/backend/src/User.ts @@ -0,0 +1,70 @@ +import {AuthedUser} from './Login.js' + +const client_id = process.env.GITHUB_CLIENT_ID +const client_secret = process.env.GITHUB_CLIENT_SECRET + +interface GitHubUser { + userId: number + login: string + accessToken: string +} + +export async function fetchAccessToken(code: string): Promise { + const response = await fetch('https://github.com/login/oauth/access_token', { + method: 'post', + body: JSON.stringify({client_id, client_secret, code}), + headers: { + 'Content-Type': 'application/json', + }, + }) + const formData = await response.formData() + const accessToken = formData.get('access_token') as string | null + if (accessToken) { + return accessToken + } else { + throw new Error('wtf happened?') + } +} + +export async function fetchUserId(accessToken: string): Promise { + const response = await fetch('https://api.github.com/user', { + headers: { + 'Authorization': 'Bearer ' + accessToken, + 'X-GitHub-Api-Version': '2022-11-28', + }, + }) + return (await response.json()).id +} + +export async function fetchEmail(accessToken: string): Promise { + const response = await fetch('https://api.github.com/user/public_emails', { + headers: { + 'Authorization': 'Bearer ' + accessToken, + 'X-GitHub-Api-Version': '2022-11-28', + }, + }) + return (await response.json()).find((email: any) => email.primary).email +} + +export async function verifyAccessToken(accessToken: string): Promise { + if (!accessToken || !accessToken.length) { + return false + } + const response = await fetch(`https://api.github.com/applications/${client_id}/token`, { + method: 'POST', + headers: { + 'Authorization': `Basic ${Buffer.from(`${client_id}:${client_secret}`).toString('base64')}`, + 'X-GitHub-Api-Version': '2022-11-28', + }, + body: JSON.stringify({access_token: accessToken}), + }) + if (response.status !== 200) { + return false + } + const payload = await response.json() + return { + userId: payload.user.id, + login: payload.user.login, + accessToken, + } +} diff --git a/backend/src/req.d.ts b/backend/src/req.d.ts new file mode 100644 index 0000000..dc6180d --- /dev/null +++ b/backend/src/req.d.ts @@ -0,0 +1,5 @@ +declare namespace Express { + export interface Request { + user?: { accessToken: string, login: string, userId: number }, + } +} diff --git a/backend/tsconfig.json b/backend/tsconfig.json new file mode 100644 index 0000000..f8bef3d --- /dev/null +++ b/backend/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "NodeNext", + "composite": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "outDir": "lib", + "rootDir": "src" + }, + "files": [ + "src/req.d.ts" + ], + "include": [ + "src/**/*.ts" + ], + "references": [ + { + "path": "../template" + } + ] +} diff --git a/backend/vitest.config.ts b/backend/vitest.config.ts new file mode 100644 index 0000000..b480e76 --- /dev/null +++ b/backend/vitest.config.ts @@ -0,0 +1,7 @@ +import {defineConfig} from 'vitest/config' + +export default defineConfig({ + test: { + setupFiles: ['dotenv/config'], + }, +}) diff --git a/cli/.gitignore b/cli/.gitignore new file mode 100644 index 0000000..2040c29 --- /dev/null +++ b/cli/.gitignore @@ -0,0 +1 @@ +zig-cache diff --git a/cli/build.zig b/cli/build.zig new file mode 100644 index 0000000..194972b --- /dev/null +++ b/cli/build.zig @@ -0,0 +1,38 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) void { + const target = b.standardTargetOptions(.{}); + const optimize = b.standardOptimizeOption(.{}); + + const exe = b.addExecutable(.{ + .name = "cli", + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = optimize, + }); + + const options = b.addOptions(); + options.addOption([4]u8, "install_sh_ip", .{ 127, 0, 0, 1 }); + options.addOption(u16, "install_sh_port", 2506); + exe.addOptions("build_options", options); + + b.installArtifact(exe); + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| { + run_cmd.addArgs(args); + } + + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + const unit_tests = b.addTest(.{ + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = optimize, + }); + + const run_unit_tests = b.addRunArtifact(unit_tests); + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&run_unit_tests.step); +} diff --git a/cli/src/main.zig b/cli/src/main.zig new file mode 100644 index 0000000..99bab98 --- /dev/null +++ b/cli/src/main.zig @@ -0,0 +1,21 @@ +const options = @import("build_options"); +const std = @import("std"); + +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + const allocator = gpa.allocator(); + const tcp_stream = makeTcpStream(allocator); + defer tcp_stream.close(); + const n = try tcp_stream.write("hello from cli"); + std.debug.print("tcp conn to install.sh sent {d} bytes\n", .{n}); +} + +fn makeTcpStream(allocator: std.mem.Allocator) std.net.Stream { + const address = std.net.Address.initIp4(options.install_sh_ip, options.install_sh_port); + if (std.net.tcpConnectToAddress(allocator, address)) |tcp_stream| { + return tcp_stream; + } else |err| { + std.debug.print("tcp conn to install.sh failed: {}\n", .{err}); + std.process.exit(1); + } +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..44a78ef --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,15 @@ +services: + postgres: + container_name: install-postgres + image: postgres:16 + ports: + - "5432:5432" + environment: + POSTGRES_DB: eighty4 + POSTGRES_USER: eighty4 + POSTGRES_PASSWORD: eighty4 + volumes: + - postgres_data:/var/lib/postgresql/data + +volumes: + postgres_data: diff --git a/frontend/.env b/frontend/.env new file mode 100644 index 0000000..de4d80a --- /dev/null +++ b/frontend/.env @@ -0,0 +1 @@ +VITE_GITHUB_CLIENT_ID="" diff --git a/frontend/assets/svg/eighty4.svg b/frontend/assets/svg/eighty4.svg new file mode 100644 index 0000000..659fd7d --- /dev/null +++ b/frontend/assets/svg/eighty4.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/assets/svg/favicon.svg b/frontend/assets/svg/favicon.svg new file mode 100644 index 0000000..0d5c0f5 --- /dev/null +++ b/frontend/assets/svg/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/assets/svg/github_b.svg b/frontend/assets/svg/github_b.svg new file mode 100644 index 0000000..37fa923 --- /dev/null +++ b/frontend/assets/svg/github_b.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/assets/svg/github_w.svg b/frontend/assets/svg/github_w.svg new file mode 100644 index 0000000..d5e6491 --- /dev/null +++ b/frontend/assets/svg/github_w.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/assets/svg/icons/bug_b.svg b/frontend/assets/svg/icons/bug_b.svg new file mode 100644 index 0000000..91bdea0 --- /dev/null +++ b/frontend/assets/svg/icons/bug_b.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/assets/svg/icons/bug_w.svg b/frontend/assets/svg/icons/bug_w.svg new file mode 100644 index 0000000..139932b --- /dev/null +++ b/frontend/assets/svg/icons/bug_w.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/assets/svg/languages/cpp.svg b/frontend/assets/svg/languages/cpp.svg new file mode 100644 index 0000000..583ab5e --- /dev/null +++ b/frontend/assets/svg/languages/cpp.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/assets/svg/languages/go.svg b/frontend/assets/svg/languages/go.svg new file mode 100644 index 0000000..683f0f8 --- /dev/null +++ b/frontend/assets/svg/languages/go.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/assets/svg/languages/rust_b.svg b/frontend/assets/svg/languages/rust_b.svg new file mode 100644 index 0000000..8975046 --- /dev/null +++ b/frontend/assets/svg/languages/rust_b.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/assets/svg/languages/rust_w.svg b/frontend/assets/svg/languages/rust_w.svg new file mode 100644 index 0000000..b9e285f --- /dev/null +++ b/frontend/assets/svg/languages/rust_w.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/assets/svg/languages/zig.svg b/frontend/assets/svg/languages/zig.svg new file mode 100644 index 0000000..cb056de --- /dev/null +++ b/frontend/assets/svg/languages/zig.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/assets/svg/logo_i.svg b/frontend/assets/svg/logo_i.svg new file mode 100644 index 0000000..943b0e1 --- /dev/null +++ b/frontend/assets/svg/logo_i.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/assets/svg/logo_sh_b.svg b/frontend/assets/svg/logo_sh_b.svg new file mode 100644 index 0000000..cce4f2f --- /dev/null +++ b/frontend/assets/svg/logo_sh_b.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/assets/svg/logo_sh_w.svg b/frontend/assets/svg/logo_sh_w.svg new file mode 100644 index 0000000..e45998a --- /dev/null +++ b/frontend/assets/svg/logo_sh_w.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..b73cd14 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,417 @@ + + + + + + + Install.sh + + + + + + + + + + + + + + + + + + +
+
+ an app +
+ +
+ +
+
+

Binary Installs,

+

Simplified

+
+ +
+
+
+

tested, cross-platform, reproducible scripts

+

supporting Linux, MacOS, aarch64 and x86_64 binaries

+

installed from your project's GitHub releases

+
+ + + + +
+
+
+ + + + + diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..3d6c2b2 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,21 @@ +{ + "name": "@eighty4/install-frontend", + "version": "0.0.1", + "private": true, + "author": "Adam McKee \ No newline at end of file diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg new file mode 100644 index 0000000..0d5c0f5 --- /dev/null +++ b/frontend/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/github_b.svg b/frontend/public/github_b.svg new file mode 100644 index 0000000..cc622a4 --- /dev/null +++ b/frontend/public/github_b.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/github_w.svg b/frontend/public/github_w.svg new file mode 100644 index 0000000..dd166bd --- /dev/null +++ b/frontend/public/github_w.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/icons/bug_b.svg b/frontend/public/icons/bug_b.svg new file mode 100644 index 0000000..6fbf0e4 --- /dev/null +++ b/frontend/public/icons/bug_b.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/icons/bug_w.svg b/frontend/public/icons/bug_w.svg new file mode 100644 index 0000000..3efbeff --- /dev/null +++ b/frontend/public/icons/bug_w.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/languages/cpp.svg b/frontend/public/languages/cpp.svg new file mode 100644 index 0000000..ece4361 --- /dev/null +++ b/frontend/public/languages/cpp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/languages/go.svg b/frontend/public/languages/go.svg new file mode 100644 index 0000000..f1739af --- /dev/null +++ b/frontend/public/languages/go.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/languages/rust_b.svg b/frontend/public/languages/rust_b.svg new file mode 100644 index 0000000..5284140 --- /dev/null +++ b/frontend/public/languages/rust_b.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/languages/rust_w.svg b/frontend/public/languages/rust_w.svg new file mode 100644 index 0000000..09b8984 --- /dev/null +++ b/frontend/public/languages/rust_w.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/languages/zig.svg b/frontend/public/languages/zig.svg new file mode 100644 index 0000000..e1e59e4 --- /dev/null +++ b/frontend/public/languages/zig.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/logo_i.svg b/frontend/public/logo_i.svg new file mode 100644 index 0000000..768f9be --- /dev/null +++ b/frontend/public/logo_i.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/logo_sh_b.svg b/frontend/public/logo_sh_b.svg new file mode 100644 index 0000000..20a8907 --- /dev/null +++ b/frontend/public/logo_sh_b.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/logo_sh_w.svg b/frontend/public/logo_sh_w.svg new file mode 100644 index 0000000..aff5d6f --- /dev/null +++ b/frontend/public/logo_sh_w.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/main.ts b/frontend/src/main.ts new file mode 100644 index 0000000..f2634ee --- /dev/null +++ b/frontend/src/main.ts @@ -0,0 +1,57 @@ +import './style.css' + +// class LoginCard extends HTMLElement { +// constructor() { +// super() +// const template = document.getElementById('tmpl-login-card') as HTMLTemplateElement +// this.attachShadow({mode: 'open'}).appendChild(template.content.cloneNode(true)) +// } +// } +// +// customElements.define('login-card', LoginCard) + +function parseQueryParams(): Record { + const result: Record = {} + if (location.search.length) { + const keyValuePairs = location.search.substring(1).split('&') + for (const keyValuePair of keyValuePairs) { + const [key, value] = keyValuePair.split('=') + result[key] = value + } + } + return result +} + +function requestLogin() { + // let main = document.body.querySelector('main') + // if (main) { + // main.remove() + // } + // main = document.createElement('main') + // main.appendChild(new LoginCard()) + // document.body.appendChild(main) +} + +async function awaitAuth(loginId: string): Promise { + const response = await fetch('/login/notify?login=' + loginId) + return await response.text() +} + +const params = parseQueryParams() +if (params['login']) { + awaitAuth(params['login']) + .then((accessToken) => { + console.log('logged in', accessToken) + }) + .catch(e => console.log('await login error', e)) + window.history.replaceState(null, '', '/') +} else if (params['auth'] === 'failed') { + console.log('auth=failed') + requestLogin() +} else { + requestLogin() +} + +document.addEventListener('DOMContentLoaded', () => { + +}) diff --git a/frontend/src/style.css b/frontend/src/style.css new file mode 100644 index 0000000..db5c9f3 --- /dev/null +++ b/frontend/src/style.css @@ -0,0 +1,64 @@ +/*#login-card {*/ +/* position: fixed;*/ +/* display: flex;*/ +/* flex-direction: column;*/ +/* justify-content: center;*/ +/* align-items: center;*/ +/* gap: 1rem;*/ +/*}*/ + +/*.card {*/ +/* background-color: var(--card-bg-color);*/ +/* background-size: 4vmin 4vmin;*/ +/* background-image: radial-gradient(circle, var(--card-graph-color) 1px, transparent 1px);*/ +/* border: 2vmin solid var(--card-bg-color);*/ +/* box-sizing: border-box;*/ +/* color: var(--card-text-color);*/ +/*}*/ + +/*.card > * {*/ +/* background: var(--card-bg-color);*/ +/* padding: 1rem 2rem;*/ +/*}*/ + +/*@media screen {*/ +/* @media (orientation:portrait) {*/ +/* #logo {*/ + +/* }*/ +/* .card {*/ +/* width: 80vw;*/ +/* height: 35vmax;*/ +/* left: 10vw;*/ +/* }*/ +/* }*/ +/* @media (orientation: landscape) {*/ +/* @media (max-width: 1799px) and (min-aspect-ratio: 2/3) {*/ +/* #logo {*/ +/* top: 5vh;*/ +/* left: 20vw;*/ +/* }*/ + +/* .card {*/ +/* top: 30vh;*/ +/* left: 20vw;*/ +/* min-height: 70vh;*/ +/* width: 60vw;*/ +/* }*/ + +/* }*/ +/* @media (min-width: 1800px) {*/ +/* #logo {*/ +/* top: 5vh;*/ +/* left: 20vw;*/ +/* }*/ + +/* .card {*/ +/* top: 20vh;*/ +/* left: 60vw;*/ +/* height: 60vh;*/ +/* width: 40vw;*/ +/* }*/ +/* }*/ +/* }*/ +/*}*/ diff --git a/frontend/src/vite-env.d.ts b/frontend/src/vite-env.d.ts new file mode 100644 index 0000000..6c30b62 --- /dev/null +++ b/frontend/src/vite-env.d.ts @@ -0,0 +1,9 @@ +/// + +interface ImportMetaEnv { + readonly VITE_GITHUB_CLIENT_ID: string +} + +interface ImportMeta { + readonly env: ImportMetaEnv +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000..75abdef --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/frontend/vite.config.js b/frontend/vite.config.js new file mode 100644 index 0000000..d57f7a4 --- /dev/null +++ b/frontend/vite.config.js @@ -0,0 +1,15 @@ +import {defineConfig} from 'vite' + +export default defineConfig({ + build: { + outDir: '../backend/public', + emptyOutDir: true, + }, + server: { + port: 5711, + proxy: { + '/login': 'http://localhost:5741', + '/api': 'http://localhost:5741', + }, + }, +}) diff --git a/package.json b/package.json new file mode 100644 index 0000000..f8dcc42 --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "@eighty4/install-root", + "version": "0.0.1", + "private": true, + "author": "Adam McKee =12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm64@0.19.9: + resolution: {integrity: sha512-q4cR+6ZD0938R19MyEW3jEsMzbb/1rulLXiNAJQADD/XYp7pT+rOS5JGxvpRW8dFDEfjW4wLgC/3FXIw4zYglQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.19.9: + resolution: {integrity: sha512-jkYjjq7SdsWuNI6b5quymW0oC83NN5FdRPuCbs9HZ02mfVdAP8B8eeqLSYU3gb6OJEaY5CQabtTFbqBf26H3GA==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.19.9: + resolution: {integrity: sha512-KOqoPntWAH6ZxDwx1D6mRntIgZh9KodzgNOy5Ebt9ghzffOk9X2c1sPwtM9P+0eXbefnDhqYfkh5PLP5ULtWFA==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.19.9: + resolution: {integrity: sha512-KBJ9S0AFyLVx2E5D8W0vExqRW01WqRtczUZ8NRu+Pi+87opZn5tL4Y0xT0mA4FtHctd0ZgwNoN639fUUGlNIWw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.19.9: + resolution: {integrity: sha512-vE0VotmNTQaTdX0Q9dOHmMTao6ObjyPm58CHZr1UK7qpNleQyxlFlNCaHsHx6Uqv86VgPmR4o2wdNq3dP1qyDQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.19.9: + resolution: {integrity: sha512-uFQyd/o1IjiEk3rUHSwUKkqZwqdvuD8GevWF065eqgYfexcVkxh+IJgwTaGZVu59XczZGcN/YMh9uF1fWD8j1g==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.19.9: + resolution: {integrity: sha512-WMLgWAtkdTbTu1AWacY7uoj/YtHthgqrqhf1OaEWnZb7PQgpt8eaA/F3LkV0E6K/Lc0cUr/uaVP/49iE4M4asA==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.19.9: + resolution: {integrity: sha512-PiPblfe1BjK7WDAKR1Cr9O7VVPqVNpwFcPWgfn4xu0eMemzRp442hXyzF/fSwgrufI66FpHOEJk0yYdPInsmyQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.19.9: + resolution: {integrity: sha512-C/ChPohUYoyUaqn1h17m/6yt6OB14hbXvT8EgM1ZWaiiTYz7nWZR0SYmMnB5BzQA4GXl3BgBO1l8MYqL/He3qw==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.19.9: + resolution: {integrity: sha512-f37i/0zE0MjDxijkPSQw1CO/7C27Eojqb+r3BbHVxMLkj8GCa78TrBZzvPyA/FNLUMzP3eyHCVkAopkKVja+6Q==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.19.9: + resolution: {integrity: sha512-t6mN147pUIf3t6wUt3FeumoOTPfmv9Cc6DQlsVBpB7eCpLOqQDyWBP1ymXn1lDw4fNUSb/gBcKAmvTP49oIkaA==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.19.9: + resolution: {integrity: sha512-jg9fujJTNTQBuDXdmAg1eeJUL4Jds7BklOTkkH80ZgQIoCTdQrDaHYgbFZyeTq8zbY+axgptncko3v9p5hLZtw==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.19.9: + resolution: {integrity: sha512-tkV0xUX0pUUgY4ha7z5BbDS85uI7ABw3V1d0RNTii7E9lbmV8Z37Pup2tsLV46SQWzjOeyDi1Q7Wx2+QM8WaCQ==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.19.9: + resolution: {integrity: sha512-DfLp8dj91cufgPZDXr9p3FoR++m3ZJ6uIXsXrIvJdOjXVREtXuQCjfMfvmc3LScAVmLjcfloyVtpn43D56JFHg==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.19.9: + resolution: {integrity: sha512-zHbglfEdC88KMgCWpOl/zc6dDYJvWGLiUtmPRsr1OgCViu3z5GncvNVdf+6/56O2Ca8jUU+t1BW261V6kp8qdw==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.19.9: + resolution: {integrity: sha512-JUjpystGFFmNrEHQnIVG8hKwvA2DN5o7RqiO1CVX8EN/F/gkCjkUMgVn6hzScpwnJtl2mPR6I9XV1oW8k9O+0A==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.19.9: + resolution: {integrity: sha512-GThgZPAwOBOsheA2RUlW5UeroRfESwMq/guy8uEe3wJlAOjpOXuSevLRd70NZ37ZrpO6RHGHgEHvPg1h3S1Jug==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.19.9: + resolution: {integrity: sha512-Ki6PlzppaFVbLnD8PtlVQfsYw4S9n3eQl87cqgeIw+O3sRr9IghpfSKY62mggdt1yCSZ8QWvTZ9jo9fjDSg9uw==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.19.9: + resolution: {integrity: sha512-MLHj7k9hWh4y1ddkBpvRj2b9NCBhfgBt3VpWbHQnXRedVun/hC7sIyTGDGTfsGuXo4ebik2+3ShjcPbhtFwWDw==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.19.9: + resolution: {integrity: sha512-GQoa6OrQ8G08guMFgeXPH7yE/8Dt0IfOGWJSfSH4uafwdC7rWwrfE6P9N8AtPGIjUzdo2+7bN8Xo3qC578olhg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.19.9: + resolution: {integrity: sha512-UOozV7Ntykvr5tSOlGCrqU3NBr3d8JqPes0QWN2WOXfvkWVGRajC+Ym0/Wj88fUgecUCLDdJPDF0Nna2UK3Qtg==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.19.9: + resolution: {integrity: sha512-oxoQgglOP7RH6iasDrhY+R/3cHrfwIDvRlT4CGChflq6twk8iENeVvMJjmvBb94Ik1Z+93iGO27err7w6l54GQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@fontsource-variable/inter@5.0.16: + resolution: {integrity: sha512-k+BUNqksTL+AN+o+OV7ILeiE9B5M5X+/jA7LWvCwjbV9ovXTqZyKRhA/x7uYv/ml8WQ0XNLBM7cRFIx4jW0/hg==} + dev: false + + /@jest/schemas@29.6.3: + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@sinclair/typebox': 0.27.8 + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@rollup/rollup-android-arm-eabi@4.8.0: + resolution: {integrity: sha512-zdTObFRoNENrdPpnTNnhOljYIcOX7aI7+7wyrSpPFFIOf/nRdedE6IYsjaBE7tjukphh1tMTojgJ7p3lKY8x6Q==} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-android-arm64@4.8.0: + resolution: {integrity: sha512-aiItwP48BiGpMFS9Znjo/xCNQVwTQVcRKkFKsO81m8exrGjHkCBDvm9PHay2kpa8RPnZzzKcD1iQ9KaLY4fPQQ==} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-arm64@4.8.0: + resolution: {integrity: sha512-zhNIS+L4ZYkYQUjIQUR6Zl0RXhbbA0huvNIWjmPc2SL0cB1h5Djkcy+RZ3/Bwszfb6vgwUvcVJYD6e6Zkpsi8g==} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-darwin-x64@4.8.0: + resolution: {integrity: sha512-A/FAHFRNQYrELrb/JHncRWzTTXB2ticiRFztP4ggIUAfa9Up1qfW8aG2w/mN9jNiZ+HB0t0u0jpJgFXG6BfRTA==} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm-gnueabihf@4.8.0: + resolution: {integrity: sha512-JsidBnh3p2IJJA4/2xOF2puAYqbaczB3elZDT0qHxn362EIoIkq7hrR43Xa8RisgI6/WPfvb2umbGsuvf7E37A==} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-gnu@4.8.0: + resolution: {integrity: sha512-hBNCnqw3EVCkaPB0Oqd24bv8SklETptQWcJz06kb9OtiShn9jK1VuTgi7o4zPSt6rNGWQOTDEAccbk0OqJmS+g==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-arm64-musl@4.8.0: + resolution: {integrity: sha512-Fw9ChYfJPdltvi9ALJ9wzdCdxGw4wtq4t1qY028b2O7GwB5qLNSGtqMsAel1lfWTZvf4b6/+4HKp0GlSYg0ahA==} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-riscv64-gnu@4.8.0: + resolution: {integrity: sha512-BH5xIh7tOzS9yBi8dFrCTG8Z6iNIGWGltd3IpTSKp6+pNWWO6qy8eKoRxOtwFbMrid5NZaidLYN6rHh9aB8bEw==} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-gnu@4.8.0: + resolution: {integrity: sha512-PmvAj8k6EuWiyLbkNpd6BLv5XeYFpqWuRvRNRl80xVfpGXK/z6KYXmAgbI4ogz7uFiJxCnYcqyvZVD0dgFog7Q==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-linux-x64-musl@4.8.0: + resolution: {integrity: sha512-mdxnlW2QUzXwY+95TuxZ+CurrhgrPAMveDWI97EQlA9bfhR8tw3Pt7SUlc/eSlCNxlWktpmT//EAA8UfCHOyXg==} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-arm64-msvc@4.8.0: + resolution: {integrity: sha512-ge7saUz38aesM4MA7Cad8CHo0Fyd1+qTaqoIo+Jtk+ipBi4ATSrHWov9/S4u5pbEQmLjgUjB7BJt+MiKG2kzmA==} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-ia32-msvc@4.8.0: + resolution: {integrity: sha512-p9E3PZlzurhlsN5h9g7zIP1DnqKXJe8ZUkFwAazqSvHuWfihlIISPxG9hCHCoA+dOOspL/c7ty1eeEVFTE0UTw==} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@rollup/rollup-win32-x64-msvc@4.8.0: + resolution: {integrity: sha512-kb4/auKXkYKqlUYTE8s40FcJIj5soOyRLHKd4ugR0dCq0G2EfcF54eYcfQiGkHzjidZ40daB4ulsFdtqNKZtBg==} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@sinclair/typebox@0.27.8: + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + dev: true + + /@types/body-parser@1.19.5: + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + dependencies: + '@types/connect': 3.4.38 + '@types/node': 20.10.4 + dev: true + + /@types/connect@3.4.38: + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + dependencies: + '@types/node': 20.10.4 + dev: true + + /@types/cookie-parser@1.4.6: + resolution: {integrity: sha512-KoooCrD56qlLskXPLGUiJxOMnv5l/8m7cQD2OxJ73NPMhuSz9PmvwRD6EpjDyKBVrdJDdQ4bQK7JFNHnNmax0w==} + dependencies: + '@types/express': 4.17.21 + dev: true + + /@types/express-serve-static-core@4.17.41: + resolution: {integrity: sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==} + dependencies: + '@types/node': 20.10.4 + '@types/qs': 6.9.10 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + dev: true + + /@types/express@4.17.21: + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + dependencies: + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 4.17.41 + '@types/qs': 6.9.10 + '@types/serve-static': 1.15.5 + dev: true + + /@types/http-errors@2.0.4: + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + dev: true + + /@types/mime@1.3.5: + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + dev: true + + /@types/mime@3.0.4: + resolution: {integrity: sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw==} + dev: true + + /@types/node@20.10.4: + resolution: {integrity: sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/pg@8.10.9: + resolution: {integrity: sha512-UksbANNE/f8w0wOMxVKKIrLCbEMV+oM1uKejmwXr39olg4xqcfBDbXxObJAt6XxHbDa4XTKOlUEcEltXDX+XLQ==} + dependencies: + '@types/node': 20.10.4 + pg-protocol: 1.6.0 + pg-types: 4.0.1 + dev: true + + /@types/qs@6.9.10: + resolution: {integrity: sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw==} + dev: true + + /@types/range-parser@1.2.7: + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + dev: true + + /@types/send@0.17.4: + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + dependencies: + '@types/mime': 1.3.5 + '@types/node': 20.10.4 + dev: true + + /@types/serve-static@1.15.5: + resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} + dependencies: + '@types/http-errors': 2.0.4 + '@types/mime': 3.0.4 + '@types/node': 20.10.4 + dev: true + + /@types/uuid@9.0.7: + resolution: {integrity: sha512-WUtIVRUZ9i5dYXefDEAI7sh9/O7jGvHg7Df/5O/gtH3Yabe5odI3UWopVR1qbPXQtvOxWu3mM4XxlYeZtMWF4g==} + dev: true + + /@vitest/expect@1.0.4: + resolution: {integrity: sha512-/NRN9N88qjg3dkhmFcCBwhn/Ie4h064pY3iv7WLRsDJW7dXnEgeoa8W9zy7gIPluhz6CkgqiB3HmpIXgmEY5dQ==} + dependencies: + '@vitest/spy': 1.0.4 + '@vitest/utils': 1.0.4 + chai: 4.3.10 + dev: true + + /@vitest/runner@1.0.4: + resolution: {integrity: sha512-rhOQ9FZTEkV41JWXozFM8YgOqaG9zA7QXbhg5gy6mFOVqh4PcupirIJ+wN7QjeJt8S8nJRYuZH1OjJjsbxAXTQ==} + dependencies: + '@vitest/utils': 1.0.4 + p-limit: 5.0.0 + pathe: 1.1.1 + dev: true + + /@vitest/snapshot@1.0.4: + resolution: {integrity: sha512-vkfXUrNyNRA/Gzsp2lpyJxh94vU2OHT1amoD6WuvUAA12n32xeVZQ0KjjQIf8F6u7bcq2A2k969fMVxEsxeKYA==} + dependencies: + magic-string: 0.30.5 + pathe: 1.1.1 + pretty-format: 29.7.0 + dev: true + + /@vitest/spy@1.0.4: + resolution: {integrity: sha512-9ojTFRL1AJVh0hvfzAQpm0QS6xIS+1HFIw94kl/1ucTfGCaj1LV/iuJU4Y6cdR03EzPDygxTHwE1JOm+5RCcvA==} + dependencies: + tinyspy: 2.2.0 + dev: true + + /@vitest/utils@1.0.4: + resolution: {integrity: sha512-gsswWDXxtt0QvtK/y/LWukN7sGMYmnCcv1qv05CsY6cU/Y1zpGX1QuvLs+GO1inczpE6Owixeel3ShkjhYtGfA==} + dependencies: + diff-sequences: 29.6.3 + loupe: 2.3.7 + pretty-format: 29.7.0 + dev: true + + /accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + dev: false + + /acorn-walk@8.3.1: + resolution: {integrity: sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==} + engines: {node: '>=0.4.0'} + dev: true + + /acorn@8.11.2: + resolution: {integrity: sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + + /array-flatten@3.0.0: + resolution: {integrity: sha512-zPMVc3ZYlGLNk4mpK1NzP2wg0ml9t7fUgDsayR5Y5rSzxQilzR9FGu/EH2jQOcKSAeAfWeylyW8juy3OkWRvNA==} + dev: false + + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + + /body-parser@2.0.0-beta.1: + resolution: {integrity: sha512-I1v2bt2OdYqtmk8nEFZuEf+9Opb30DphYwTPDbgg/OorSAoJOuTpWyDrZaSWQw7FdoevbBRCP2+9z/halXSWcA==} + engines: {node: '>= 0.10'} + dependencies: + bytes: 3.1.1 + content-type: 1.0.5 + debug: 2.6.9 + depd: 1.1.2 + http-errors: 1.8.1 + iconv-lite: 0.4.24 + on-finished: 2.3.0 + qs: 6.9.6 + raw-body: 2.4.2 + type-is: 1.6.18 + transitivePeerDependencies: + - supports-color + dev: false + + /buffer-writer@2.0.0: + resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==} + engines: {node: '>=4'} + dev: false + + /bytes@3.1.1: + resolution: {integrity: sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==} + engines: {node: '>= 0.8'} + dev: false + + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + + /chai@4.3.10: + resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.3 + deep-eql: 4.1.3 + get-func-name: 2.0.2 + loupe: 2.3.7 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + + /check-error@1.0.3: + resolution: {integrity: sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==} + dependencies: + get-func-name: 2.0.2 + dev: true + + /content-disposition@0.5.4: + resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} + engines: {node: '>= 0.6'} + dependencies: + safe-buffer: 5.2.1 + dev: false + + /content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + dev: false + + /cookie-parser@1.4.6: + resolution: {integrity: sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==} + engines: {node: '>= 0.8.0'} + dependencies: + cookie: 0.4.1 + cookie-signature: 1.0.6 + dev: false + + /cookie-signature@1.0.6: + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} + dev: false + + /cookie@0.4.1: + resolution: {integrity: sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==} + engines: {node: '>= 0.6'} + dev: false + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: false + + /debug@3.1.0: + resolution: {integrity: sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.0.0 + dev: false + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + dev: true + + /deep-eql@4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 + dev: true + + /depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + dev: false + + /destroy@1.0.4: + resolution: {integrity: sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==} + dev: false + + /diff-sequences@29.6.3: + resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dev: true + + /dotenv@16.3.1: + resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} + engines: {node: '>=12'} + dev: true + + /ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + dev: false + + /encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + dev: false + + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + + /esbuild@0.19.9: + resolution: {integrity: sha512-U9CHtKSy+EpPsEBa+/A2gMs/h3ylBC0H0KSqIg7tpztHerLi6nrrcoUJAkNCEPumx8yJ+Byic4BVwHgRbN0TBg==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.19.9 + '@esbuild/android-arm64': 0.19.9 + '@esbuild/android-x64': 0.19.9 + '@esbuild/darwin-arm64': 0.19.9 + '@esbuild/darwin-x64': 0.19.9 + '@esbuild/freebsd-arm64': 0.19.9 + '@esbuild/freebsd-x64': 0.19.9 + '@esbuild/linux-arm': 0.19.9 + '@esbuild/linux-arm64': 0.19.9 + '@esbuild/linux-ia32': 0.19.9 + '@esbuild/linux-loong64': 0.19.9 + '@esbuild/linux-mips64el': 0.19.9 + '@esbuild/linux-ppc64': 0.19.9 + '@esbuild/linux-riscv64': 0.19.9 + '@esbuild/linux-s390x': 0.19.9 + '@esbuild/linux-x64': 0.19.9 + '@esbuild/netbsd-x64': 0.19.9 + '@esbuild/openbsd-x64': 0.19.9 + '@esbuild/sunos-x64': 0.19.9 + '@esbuild/win32-arm64': 0.19.9 + '@esbuild/win32-ia32': 0.19.9 + '@esbuild/win32-x64': 0.19.9 + dev: true + + /escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + dev: false + + /etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + dev: false + + /execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + dependencies: + cross-spawn: 7.0.3 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.1.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + dev: true + + /express@5.0.0-beta.1: + resolution: {integrity: sha512-KPtBrlZoQu2Ps0Ce/Imqtq73AB0KBJ8Gx59yZQ3pmDJU2/LhcoZETo03oSgtTQufbcLXt/WBITk/jMjl/WMyrQ==} + engines: {node: '>= 4'} + dependencies: + accepts: 1.3.8 + array-flatten: 3.0.0 + body-parser: 2.0.0-beta.1 + content-disposition: 0.5.4 + content-type: 1.0.5 + cookie: 0.4.1 + cookie-signature: 1.0.6 + debug: 3.1.0 + depd: 1.1.2 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 1.1.2 + fresh: 0.5.2 + merge-descriptors: 1.0.1 + methods: 1.1.2 + mime-types: 2.1.35 + on-finished: 2.3.0 + parseurl: 1.3.3 + path-is-absolute: 1.0.1 + proxy-addr: 2.0.7 + qs: 6.9.6 + range-parser: 1.2.1 + router: 2.0.0-beta.1 + safe-buffer: 5.2.1 + send: 1.0.0-beta.1 + serve-static: 2.0.0-beta.1 + setprototypeof: 1.2.0 + statuses: 1.5.0 + type-is: 1.6.18 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + dev: false + + /finalhandler@1.1.2: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + dev: false + + /forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + dev: false + + /fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + dev: false + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /get-func-name@2.0.2: + resolution: {integrity: sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==} + dev: true + + /get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + dev: true + + /get-tsconfig@4.7.2: + resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + dependencies: + resolve-pkg-maps: 1.0.0 + dev: true + + /http-errors@1.8.1: + resolution: {integrity: sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==} + engines: {node: '>= 0.6'} + dependencies: + depd: 1.1.2 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 1.5.0 + toidentifier: 1.0.1 + dev: false + + /human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + dev: true + + /iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: false + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: false + + /ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + dev: false + + /is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + + /local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + dependencies: + mlly: 1.4.2 + pkg-types: 1.0.3 + dev: true + + /loupe@2.3.7: + resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==} + dependencies: + get-func-name: 2.0.2 + dev: true + + /magic-string@0.30.5: + resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + dev: false + + /merge-descriptors@1.0.1: + resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + dev: false + + /merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + dev: true + + /methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + dev: false + + /mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + dev: false + + /mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + dependencies: + mime-db: 1.52.0 + dev: false + + /mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + dev: true + + /mlly@1.4.2: + resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} + dependencies: + acorn: 8.11.2 + pathe: 1.1.1 + pkg-types: 1.0.3 + ufo: 1.3.2 + dev: true + + /ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + dev: false + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + dev: true + + /ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + dev: false + + /nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + dev: true + + /negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + dev: false + + /npm-run-path@5.1.0: + resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + path-key: 4.0.0 + dev: true + + /obuf@1.1.2: + resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + dev: true + + /on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + dependencies: + ee-first: 1.1.1 + dev: false + + /onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + dependencies: + mimic-fn: 4.0.0 + dev: true + + /p-limit@5.0.0: + resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} + engines: {node: '>=18'} + dependencies: + yocto-queue: 1.0.0 + dev: true + + /packet-reader@1.0.0: + resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} + dev: false + + /parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + dev: false + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: false + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + dev: true + + /path-to-regexp@3.2.0: + resolution: {integrity: sha512-jczvQbCUS7XmS7o+y1aEO9OBVFeZBQ1MDSEqmO7xSoPgOPoowY/SxLpZ6Vh97/8qHZOteiCKb7gkG9gA2ZUxJA==} + dev: false + + /pathe@1.1.1: + resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} + dev: true + + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + + /pg-cloudflare@1.1.1: + resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + requiresBuild: true + dev: false + optional: true + + /pg-connection-string@2.6.2: + resolution: {integrity: sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA==} + dev: false + + /pg-int8@1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} + + /pg-numeric@1.0.2: + resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} + engines: {node: '>=4'} + dev: true + + /pg-pool@3.6.1(pg@8.11.3): + resolution: {integrity: sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==} + peerDependencies: + pg: '>=8.0' + dependencies: + pg: 8.11.3 + dev: false + + /pg-protocol@1.6.0: + resolution: {integrity: sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q==} + + /pg-types@2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} + dependencies: + pg-int8: 1.0.1 + postgres-array: 2.0.0 + postgres-bytea: 1.0.0 + postgres-date: 1.0.7 + postgres-interval: 1.2.0 + dev: false + + /pg-types@4.0.1: + resolution: {integrity: sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==} + engines: {node: '>=10'} + dependencies: + pg-int8: 1.0.1 + pg-numeric: 1.0.2 + postgres-array: 3.0.2 + postgres-bytea: 3.0.0 + postgres-date: 2.0.1 + postgres-interval: 3.0.0 + postgres-range: 1.1.3 + dev: true + + /pg@8.11.3: + resolution: {integrity: sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=3.0.1' + peerDependenciesMeta: + pg-native: + optional: true + dependencies: + buffer-writer: 2.0.0 + packet-reader: 1.0.0 + pg-connection-string: 2.6.2 + pg-pool: 3.6.1(pg@8.11.3) + pg-protocol: 1.6.0 + pg-types: 2.2.0 + pgpass: 1.0.5 + optionalDependencies: + pg-cloudflare: 1.1.1 + dev: false + + /pgpass@1.0.5: + resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + dependencies: + split2: 4.2.0 + dev: false + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + + /pkg-types@1.0.3: + resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.4.2 + pathe: 1.1.1 + dev: true + + /postcss@8.4.32: + resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + + /postgres-array@2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} + dev: false + + /postgres-array@3.0.2: + resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==} + engines: {node: '>=12'} + dev: true + + /postgres-bytea@1.0.0: + resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + engines: {node: '>=0.10.0'} + dev: false + + /postgres-bytea@3.0.0: + resolution: {integrity: sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==} + engines: {node: '>= 6'} + dependencies: + obuf: 1.1.2 + dev: true + + /postgres-date@1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} + dev: false + + /postgres-date@2.0.1: + resolution: {integrity: sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==} + engines: {node: '>=12'} + dev: true + + /postgres-interval@1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} + dependencies: + xtend: 4.0.2 + dev: false + + /postgres-interval@3.0.0: + resolution: {integrity: sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==} + engines: {node: '>=12'} + dev: true + + /postgres-range@1.1.3: + resolution: {integrity: sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==} + dev: true + + /pretty-format@29.7.0: + resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + '@jest/schemas': 29.6.3 + ansi-styles: 5.2.0 + react-is: 18.2.0 + dev: true + + /proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + dev: false + + /qs@6.9.6: + resolution: {integrity: sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ==} + engines: {node: '>=0.6'} + dev: false + + /range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + dev: false + + /raw-body@2.4.2: + resolution: {integrity: sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==} + engines: {node: '>= 0.8'} + dependencies: + bytes: 3.1.1 + http-errors: 1.8.1 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + dev: false + + /react-is@18.2.0: + resolution: {integrity: sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==} + dev: true + + /resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + dev: true + + /rollup@4.8.0: + resolution: {integrity: sha512-NpsklK2fach5CdI+PScmlE5R4Ao/FSWtF7LkoIrHDxPACY/xshNasPsbpG0VVHxUTbf74tJbVT4PrP8JsJ6ZDA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.8.0 + '@rollup/rollup-android-arm64': 4.8.0 + '@rollup/rollup-darwin-arm64': 4.8.0 + '@rollup/rollup-darwin-x64': 4.8.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.8.0 + '@rollup/rollup-linux-arm64-gnu': 4.8.0 + '@rollup/rollup-linux-arm64-musl': 4.8.0 + '@rollup/rollup-linux-riscv64-gnu': 4.8.0 + '@rollup/rollup-linux-x64-gnu': 4.8.0 + '@rollup/rollup-linux-x64-musl': 4.8.0 + '@rollup/rollup-win32-arm64-msvc': 4.8.0 + '@rollup/rollup-win32-ia32-msvc': 4.8.0 + '@rollup/rollup-win32-x64-msvc': 4.8.0 + fsevents: 2.3.3 + dev: true + + /router@2.0.0-beta.1: + resolution: {integrity: sha512-GLoYgkhAGAiwVda5nt6Qd4+5RAPuQ4WIYLlZ+mxfYICI+22gnIB3eCfmhgV8+uJNPS1/39DOYi/vdrrz0/ouKA==} + engines: {node: '>= 0.10'} + dependencies: + array-flatten: 3.0.0 + methods: 1.1.2 + parseurl: 1.3.3 + path-to-regexp: 3.2.0 + setprototypeof: 1.2.0 + utils-merge: 1.0.1 + dev: false + + /safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + dev: false + + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: false + + /send@1.0.0-beta.1: + resolution: {integrity: sha512-OKTRokcl/oo34O8+6aUpj8Jf2Bjw2D0tZzmX0/RvyfVC9ZOZW+HPAWAlhS817IsRaCnzYX1z++h2kHFr2/KNRg==} + engines: {node: '>= 0.10'} + dependencies: + debug: 3.1.0 + destroy: 1.0.4 + encodeurl: 1.0.2 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 1.8.1 + mime-types: 2.1.35 + ms: 2.1.3 + on-finished: 2.3.0 + range-parser: 1.2.1 + statuses: 1.5.0 + transitivePeerDependencies: + - supports-color + dev: false + + /serve-static@2.0.0-beta.1: + resolution: {integrity: sha512-DEJ9on/tQeFO2Omj7ovT02lCp1YgP4Kb8W2lv2o/4keTFAbgc8HtH3yPd47++2wv9lvQeqiA7FHFDe5+8c4XpA==} + engines: {node: '>= 0.10'} + dependencies: + encodeurl: 1.0.2 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.0.0-beta.1 + transitivePeerDependencies: + - supports-color + dev: false + + /setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + dev: false + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + + /signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + dev: true + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + + /split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + dev: false + + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true + + /statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + dev: false + + /std-env@3.6.0: + resolution: {integrity: sha512-aFZ19IgVmhdB2uX599ve2kE6BIE3YMnQ6Gp6BURhW/oIzpXGKr878TQfAQZn1+i0Flcc/UKUy1gOlcfaUBCryg==} + dev: true + + /strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + dev: true + + /strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + dependencies: + acorn: 8.11.2 + dev: true + + /tinybench@2.5.1: + resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==} + dev: true + + /tinypool@0.8.1: + resolution: {integrity: sha512-zBTCK0cCgRROxvs9c0CGK838sPkeokNGdQVUUwHAbynHFlmyJYj825f/oRs528HaIJ97lo0pLIlDUzwN+IorWg==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy@2.2.0: + resolution: {integrity: sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==} + engines: {node: '>=14.0.0'} + dev: true + + /toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + dev: false + + /tsx@4.6.2: + resolution: {integrity: sha512-QPpBdJo+ZDtqZgAnq86iY/PD2KYCUPSUGIunHdGwyII99GKH+f3z3FZ8XNFLSGQIA4I365ui8wnQpl8OKLqcsg==} + engines: {node: '>=18.0.0'} + hasBin: true + dependencies: + esbuild: 0.18.20 + get-tsconfig: 4.7.2 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + + /type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + dev: false + + /typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /ufo@1.3.2: + resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==} + dev: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true + + /unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + dev: false + + /utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + dev: false + + /uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + dev: false + + /vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: false + + /vite-node@1.0.4(@types/node@20.10.4): + resolution: {integrity: sha512-9xQQtHdsz5Qn8hqbV7UKqkm8YkJhzT/zr41Dmt5N7AlD8hJXw/Z7y0QiD5I8lnTthV9Rvcvi0QW7PI0Fq83ZPg==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + pathe: 1.1.1 + picocolors: 1.0.0 + vite: 5.0.8(@types/node@20.10.4) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite@5.0.8(@types/node@20.10.4): + resolution: {integrity: sha512-jYMALd8aeqR3yS9xlHd0OzQJndS9fH5ylVgWdB+pxTwxLKdO1pgC5Dlb398BUxpfaBxa4M9oT7j1g503Gaj5IQ==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 20.10.4 + esbuild: 0.19.9 + postcss: 8.4.32 + rollup: 4.8.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vitest@1.0.4(@types/node@20.10.4): + resolution: {integrity: sha512-s1GQHp/UOeWEo4+aXDOeFBJwFzL6mjycbQwwKWX2QcYfh/7tIerS59hWQ20mxzupTJluA2SdwiBuWwQHH67ckg==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': ^1.0.0 + '@vitest/ui': ^1.0.0 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/node': 20.10.4 + '@vitest/expect': 1.0.4 + '@vitest/runner': 1.0.4 + '@vitest/snapshot': 1.0.4 + '@vitest/spy': 1.0.4 + '@vitest/utils': 1.0.4 + acorn-walk: 8.3.1 + cac: 6.7.14 + chai: 4.3.10 + debug: 4.3.4 + execa: 8.0.1 + local-pkg: 0.5.0 + magic-string: 0.30.5 + pathe: 1.1.1 + picocolors: 1.0.0 + std-env: 3.6.0 + strip-literal: 1.3.0 + tinybench: 2.5.1 + tinypool: 0.8.1 + vite: 5.0.8(@types/node@20.10.4) + vite-node: 1.0.4(@types/node@20.10.4) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /why-is-node-running@2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + + /xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + dev: false + + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..1568e20 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,4 @@ +packages: + - backend + - frontend + - template diff --git a/template/gold/debian.bash.Dockerfile b/template/gold/debian.bash.Dockerfile new file mode 100644 index 0000000..b390f6b --- /dev/null +++ b/template/gold/debian.bash.Dockerfile @@ -0,0 +1,13 @@ +FROM debian + +ARG INSTALL_DEPENDENCY +RUN test -n "$INSTALL_DEPENDENCY" && \ + apt-get update && \ + apt-get install -y $INSTALL_DEPENDENCY + +ENV SHELL /bin/bash + +WORKDIR /gold +COPY gold/scripts scripts +COPY gold/install_test.sh install_test.sh +RUN chmod +x /gold/scripts/*.sh /gold/install_test.sh diff --git a/template/gold/debian.fish.Dockerfile b/template/gold/debian.fish.Dockerfile new file mode 100644 index 0000000..df96e62 --- /dev/null +++ b/template/gold/debian.fish.Dockerfile @@ -0,0 +1,9 @@ +ARG INSTALL_DEPENDENCY +FROM install.template.test.debian.bash.${INSTALL_DEPENDENCY:-UNSET} + +RUN apt-get install -y fish +RUN mkdir -p ~/.config/fish && touch ~/.config/fish/config.fish + +ENV SHELL /bin/fish + +CMD ["/bin/fish"] diff --git a/template/gold/debian.zsh.Dockerfile b/template/gold/debian.zsh.Dockerfile new file mode 100644 index 0000000..8381322 --- /dev/null +++ b/template/gold/debian.zsh.Dockerfile @@ -0,0 +1,8 @@ +ARG INSTALL_DEPENDENCY +FROM install.template.test.debian.bash.${INSTALL_DEPENDENCY:-UNSET} + +RUN apt-get install -y zsh + +ENV SHELL /bin/zsh + +CMD ["/bin/zsh"] diff --git a/template/gold/install_test.sh b/template/gold/install_test.sh new file mode 100755 index 0000000..43cef1c --- /dev/null +++ b/template/gold/install_test.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env sh +set -e + +script="$1" +binary="$2" +profile="$3" + +/gold/scripts/"$script" +. ~/"$profile" +command -v "$binary" diff --git a/template/gold/integration_test.sh b/template/gold/integration_test.sh new file mode 100755 index 0000000..6741bfb --- /dev/null +++ b/template/gold/integration_test.sh @@ -0,0 +1,69 @@ +#!/usr/bin/env sh + +set -e +docker build --build-arg="INSTALL_DEPENDENCY=curl" -t install.template.test.debian.bash.curl -f gold/debian.bash.Dockerfile . +docker build --build-arg="INSTALL_DEPENDENCY=wget" -t install.template.test.debian.bash.wget -f gold/debian.bash.Dockerfile . +docker build --build-arg="INSTALL_DEPENDENCY=curl" -t install.template.test.debian.fish.curl -f gold/debian.fish.Dockerfile . +docker build --build-arg="INSTALL_DEPENDENCY=wget" -t install.template.test.debian.fish.wget -f gold/debian.fish.Dockerfile . +docker build --build-arg="INSTALL_DEPENDENCY=curl" -t install.template.test.debian.zsh.curl -f gold/debian.zsh.Dockerfile . +docker build --build-arg="INSTALL_DEPENDENCY=wget" -t install.template.test.debian.zsh.wget -f gold/debian.zsh.Dockerfile . +set +e + +run_test() { + image_suffix="$1" + script="$2" + binary="$3" + shell_profile="$4" + docker run -it --rm "install.template.test.$image_suffix" /gold/install_test.sh "$script" "$binary" "$shell_profile" +} + +pass="\033[1;38;5;41m\0342\0234\0224\033[m" +fail="\033[41mx\033[m" + +status_print() { + exit_code="$1" + label="$2" + output="$3" + if test "$exit_code" = 0; then + echo " $pass $label" + else + echo " $fail $label" + printf " ---OUTPUT---\n ------------\n%s\n ------------\n\n" "$output" + fi +} + +shell_profile() { + _shell="$1" + case $_shell in + bash) _profile=".bash_profile" ;; + fish) _profile=".config/fish/config.fish" ;; + zsh) _profile=".zprofile" ;; + *) exit 1 ;; + esac + echo "$_profile" +} + +binary_name() { + _script="$1" + case $_script in + maestro.sh) _binary="maestro" ;; + *) exit 1 ;; + esac + echo "$_binary" +} + +oses="debian" +shells="bash fish zsh" +downloaders="curl wget" +scripts="maestro.sh" + +for os in $oses; do + for shell in $shells; do + for downloader in $downloaders; do + for script in $scripts; do + output=$(run_test "$os.$shell.$downloader" "$script" $(binary_name $script) $(shell_profile $shell)) + status_print $? "$os $shell $downloader $script" "$output" + done + done + done +done diff --git a/template/gold/scripts/maestro.sh b/template/gold/scripts/maestro.sh new file mode 100644 index 0000000..c63e200 --- /dev/null +++ b/template/gold/scripts/maestro.sh @@ -0,0 +1,146 @@ +#!/usr/bin/env sh +set -e + +binary_name="maestro" +repository_name="eighty4/maestro" + +abandon_ship() { + if [ $# -gt 0 ]; then + echo "$1" >&2 + fi + echo "visit https://github.com/$repository_name for other setup methods." >&2 + exit 1 +} + +fetch_json() { + _json_url="$1" + _json="" + if command -v curl >/dev/null 2>&1; then + _json=$(curl -s "$_json_url") + elif command -v wget >/dev/null 2>&1; then + _json=$(wget -q -O - -o /dev/null "$_json_url") + else + abandon_ship "unable to download with curl or wget." + fi + echo "$_json" +} + +download_binary() { + _bin_url="$1" + _bin_path="$2" + if command -v curl >/dev/null 2>&1; then + curl -Ls "$_bin_url" -o "$_bin_path" + elif command -v wget >/dev/null 2>&1; then + wget -q -O "$_bin_path" -o /dev/null "$_bin_url" + else + abandon_ship "unable to download with curl or wget." + fi +} + +resolve_cpu() { + _cpu="" + case $(uname -m) in + armv6l | armv7l) abandon_ship "no prebuilt binary for 32-bit arm." ;; + x86_64 | amd64) _cpu="amd64" ;; + aarch64 | arm64) _cpu="arm64" ;; + *) abandon_ship "cpu architecture $_cpu is unsupported. visit https://github.com/eighty4/install/issues to submit a PR." ;; + esac + echo "$_cpu" +} + +resolve_os() { + _os="" + case $(uname -o) in + Darwin) _os="MacOS" ;; + GNU/Linux) _os="Linux" ;; + *) abandon_ship "operating system $_os is unsupported. visit https://github.com/eighty4/install/issues to submit a PR." ;; + esac + echo "$_os" +} + +resolve_shell_profile() { + _profile="" + case $SHELL in + */bash*) _profile=".bash_profile" ;; + */fish*) _profile=".config/fish/config.fish" ;; + */zsh*) _profile=".zprofile" ;; + *) _profile=".profile" ;; + esac + echo "$_profile" +} + +resolve_version() { + _version="" + _json=$(fetch_json "https://api.github.com/repos/$repository_name/releases/latest") + _version=$(echo "$_json" | grep \"tag_name\": | cut -d : -f 2 | cut -d \" -f 2) + if test -z "$_version"; then + _version="latest" + fi + echo "$_version" +} + +check_cmd() { + _cmd="$1" + if ! command -v "$_cmd" >/dev/null 2>&1; then + abandon_ship "unable to locate dependency program \`$_cmd\` on your machine." + fi +} + +resolve_filename() { + _cpu="$1" + _os="$2" + _filename="" + if test "$_cpu" = "amd64" && test "$_os" = "MacOS"; then + _filename="maestro-darwin-amd64" + elif test "$_cpu" = "arm64" && test "$_os" = "MacOS"; then + _filename="maestro-darwin-arm64" + elif test "$_cpu" = "amd64" && test "$_os" = "Linux"; then + _filename="maestro-linux-amd64" + elif test "$_cpu" = "arm64" && test "$_os" = "Linux"; then + _filename="maestro-linux-arm64" + else + abandon_ship "no prebuilt $_cpu binary for $_os" + fi + echo "$_filename" +} + +check_cmd chmod +check_cmd cut +check_cmd echo +check_cmd grep +check_cmd mkdir +check_cmd uname +shell_profile=$(resolve_shell_profile) +arch=$(resolve_cpu) +os=$(resolve_os) +filename=$(resolve_filename "$arch" "$os") + +latest_version=$(resolve_version) +echo "installing $binary_name@$latest_version" +echo "" + +install_path=".$binary_name/bin" +if [ "$os" = "linux" ]; then + install_path=".config/$install_path" +fi +install_dir="$HOME/$install_path" +mkdir -p "$install_dir" + +download_binary "https://github.com/$repository_name/releases/download/$latest_version/$filename" "$install_dir/$binary_name" +chmod +x "$install_dir/$binary_name" + +if ! grep .$binary_name/bin "$HOME/$shell_profile" >/dev/null 2>&1; then + { + echo ""; + echo "# added by https://install.eighty4.tech"; + echo "PATH=\"\$PATH:$install_dir"\" >> "$HOME/$shell_profile"; + } >> "$HOME/$shell_profile" +fi + +checkmark="\033[1;38;5;41m\0342\0234\0224\033[m" +echo "$checkmark binary installed at \033[1m~/$install_path\033[m" +echo "$checkmark \033[1m~/$shell_profile\033[m now adds $binary_name to PATH" +echo "" +echo "run these commands to verify install:" +echo " source ~/$shell_profile" +echo " $binary_name" diff --git a/template/package.json b/template/package.json new file mode 100644 index 0000000..0e92c74 --- /dev/null +++ b/template/package.json @@ -0,0 +1,29 @@ +{ + "name": "@eighty4/install-template", + "version": "0.0.1", + "author": "Adam McKee generateScriptTest('maestro.sh', { + binaryName: 'maestro', + filenames: { + 'maestro-darwin-amd64': { + arch: 'amd64', + os: 'MacOS', + }, + 'maestro-darwin-arm64': { + arch: 'arm64', + os: 'MacOS', + }, + 'maestro-linux-amd64': { + arch: 'amd64', + os: 'Linux', + }, + 'maestro-linux-arm64': { + arch: 'arm64', + os: 'Linux' + } + }, + repository: { + owner: 'eighty4', + name: 'maestro', + }, +})) diff --git a/template/src/Generate.ts b/template/src/Generate.ts new file mode 100644 index 0000000..5c4c1aa --- /dev/null +++ b/template/src/Generate.ts @@ -0,0 +1,218 @@ +import {Architecture, Distribution, OperatingSystem} from './Distrubtions.js' + +export {Architecture, Distribution, OperatingSystem} from './Distrubtions.js' + +export interface Repository { + owner: string + name: string +} + +export interface GenerateScriptOptions { + binaryName: string + filenames: Record + repository: Repository +} + +function validateOptions(options: GenerateScriptOptions) { + if (!options) { + throw new Error('options param is required') + } else if (!options.binaryName || !options.binaryName.length) { + throw new Error('options.binaryName param is required') + } else if (!options.repository) { + throw new Error('options.repository param is required') + } else if (!options.repository.owner || !options.repository.owner.length) { + throw new Error('options.repository.owner param is required') + } else if (!options.repository.name || !options.repository.name.length) { + throw new Error('options.repository.name param is required') + } else if (!options.filenames || !Object.keys(options.filenames).length) { + throw new Error('options.filenames is required') + } +} + +function distributionSupport(distributions: Array): { + architectures: Array, + oses: Array +} { + const architectures: Array = [] + const oses: Array = [] + for (const distribution of distributions) { + if (!architectures.includes(distribution.arch)) { + architectures.push(distribution.arch) + } + if (!oses.includes(distribution.os)) { + oses.push(distribution.os) + } + } + return {architectures, oses} +} + +export function generateScript(options: GenerateScriptOptions): string { + validateOptions(options) + const {architectures, oses} = distributionSupport(Object.values(options.filenames)) + return `#!/usr/bin/env sh +set -e + +binary_name="${options.binaryName}" +repository_name="${options.repository.owner}/${options.repository.name}" + +abandon_ship() { + if [ $# -gt 0 ]; then + echo "$1" >&2 + fi + echo "visit https://github.com/$repository_name for other setup methods." >&2 + exit 1 +} + +fetch_json() { + _json_url="$1" + _json="" + if command -v curl >/dev/null 2>&1; then + _json=$(curl -s "$_json_url") + elif command -v wget >/dev/null 2>&1; then + _json=$(wget -q -O - -o /dev/null "$_json_url") + else + abandon_ship "unable to download with curl or wget." + fi + echo "$_json" +} + +download_binary() { + _bin_url="$1" + _bin_path="$2" + if command -v curl >/dev/null 2>&1; then + curl -Ls "$_bin_url" -o "$_bin_path" + elif command -v wget >/dev/null 2>&1; then + wget -q -O "$_bin_path" -o /dev/null "$_bin_url" + else + abandon_ship "unable to download with curl or wget." + fi +} + +resolve_cpu() { + _cpu="" + case $(uname -m) in + armv6l | armv7l) ${assignOrAbandon(architectures.includes('arm'), '_cpu', 'arm', '32-bit arm')} ;; + x86_64 | amd64) ${assignOrAbandon(architectures.includes('amd64'), '_cpu', 'amd64')} ;; + aarch64 | arm64) ${assignOrAbandon(architectures.includes('arm64'), '_cpu', 'arm64')} ;; + *) abandon_ship "cpu architecture $_cpu is unsupported. visit https://github.com/eighty4/install/issues to submit a PR." ;; + esac + echo "$_cpu" +} + +resolve_os() { + _os="" + case $(uname -o) in + Darwin) ${assignOrAbandon(oses.includes('MacOS'), '_os', 'MacOS')} ;; + GNU/Linux) ${assignOrAbandon(oses.includes('Linux'), '_os', 'Linux')} ;; + *) abandon_ship "operating system $_os is unsupported. visit https://github.com/eighty4/install/issues to submit a PR." ;; + esac + echo "$_os" +} + +resolve_shell_profile() { + _profile="" + case $SHELL in + */bash*) _profile=".bash_profile" ;; + */fish*) _profile=".config/fish/config.fish" ;; + */zsh*) _profile=".zprofile" ;; + *) _profile=".profile" ;; + esac + echo "$_profile" +} + +resolve_version() { + _version="" + _json=$(fetch_json "https://api.github.com/repos/$repository_name/releases/latest") + _version=$(echo "$_json" | grep \\"tag_name\\": | cut -d : -f 2 | cut -d \\" -f 2) + if test -z "$_version"; then + _version="latest" + fi + echo "$_version" +} + +check_cmd() { + _cmd="$1" + if ! command -v "$_cmd" >/dev/null 2>&1; then + abandon_ship "unable to locate dependency program \\\`$_cmd\\\` on your machine." + fi +} + +${resolveFilenameFunction(options.filenames)} + +check_cmd chmod +check_cmd cut +check_cmd echo +check_cmd grep +check_cmd mkdir +check_cmd uname +shell_profile=$(resolve_shell_profile) +arch=$(resolve_cpu) +os=$(resolve_os) +filename=$(resolve_filename "$arch" "$os") + +latest_version=$(resolve_version) +echo "installing $binary_name@$latest_version" +echo "" + +install_path=".$binary_name/bin" +if [ "$os" = "linux" ]; then + install_path=".config/$install_path" +fi +install_dir="$HOME/$install_path" +mkdir -p "$install_dir" + +download_binary "https://github.com/$repository_name/releases/download/$latest_version/$filename" "$install_dir/$binary_name" +chmod +x "$install_dir/$binary_name" + +if ! grep .$binary_name/bin "$HOME/$shell_profile" >/dev/null 2>&1; then + { + echo ""; + echo "# added by https://install.eighty4.tech"; + echo "PATH=\\"\\$PATH:$install_dir"\\" >> "$HOME/$shell_profile"; + } >> "$HOME/$shell_profile" +fi + +checkmark="\\033[1;38;5;41m\\0342\\0234\\0224\\033[m" +echo "$checkmark binary installed at \\033[1m~/$install_path\\033[m" +echo "$checkmark \\033[1m~/$shell_profile\\033[m now adds $binary_name to PATH" +echo "" +echo "run these commands to verify install:" +echo " source ~/$shell_profile" +echo " $binary_name" +` +} + +function assignOrAbandon(check: boolean, variable: string, assignment: string, label?: string) { + if (check) { + return `${variable}="${assignment}"` + } else { + return `abandon_ship "no prebuilt binary for ${label || assignment}."` + } +} + +function resolveFilenameFunction(filenamesToDistribution: Record) { + const filenames = Object.keys(filenamesToDistribution) + const chunks: Array = [] + chunks.push(` if ${renderCheckDistribution(filenamesToDistribution[filenames[0]])}; then`) + chunks.push(` _filename="${filenames[0]}"`) + if (filenames.length > 1) { + for (const filename of filenames.splice(1)) { + chunks.push(` elif ${renderCheckDistribution(filenamesToDistribution[filename])}; then`) + chunks.push(` _filename="${filename}"`) + } + } + chunks.push(' else') + chunks.push(' abandon_ship "no prebuilt $_cpu binary for $_os"') + chunks.push(' fi') + return `resolve_filename() { + _cpu="$1" + _os="$2" + _filename="" +${chunks.join('\n')} + echo "$_filename" +}` +} + +function renderCheckDistribution({arch, os}: Distribution): string { + return `test "$_cpu" = "${arch}" && test "$_os" = "${os}"` +} diff --git a/template/tsconfig.json b/template/tsconfig.json new file mode 100644 index 0000000..e4bef24 --- /dev/null +++ b/template/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "NodeNext", + "composite": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "outDir": "lib", + "rootDir": "src" + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "src/**/*.test.ts" + ] +}