diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index f60a31c..505772f 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -7,32 +7,32 @@ jobs: test: timeout-minutes: 60 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 + # 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@v4 - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 23 cache: pnpm -# - name: sql -# run: | -# sudo apt-get install -y postgresql-client -# PGPASSWORD=eighty4 psql -h localhost -U eighty4 -f v001-init-schema.sql eighty4 -# working-directory: sql + # - name: sql + # run: | + # sudo apt-get install -y postgresql-client + # PGPASSWORD=eighty4 psql -h localhost -U eighty4 -f v001-init-schema.sql eighty4 + # working-directory: sql - run: pnpm i - name: playwright env: diff --git a/.github/workflows/publish_frontend.yml b/.github/workflows/publish_frontend.yml index c7818d3..e55173b 100644 --- a/.github/workflows/publish_frontend.yml +++ b/.github/workflows/publish_frontend.yml @@ -1,4 +1,4 @@ -name: "Publish: frontend" +name: 'Publish: frontend' on: workflow_call: @@ -14,7 +14,6 @@ on: type: string jobs: - publish-frontend: runs-on: ubuntu-latest defaults: @@ -28,7 +27,7 @@ jobs: - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 23 cache: pnpm - run: pnpm i - run: pnpm build diff --git a/.github/workflows/publish_lambdas.yml b/.github/workflows/publish_lambdas.yml index 0e9846d..a0df77c 100644 --- a/.github/workflows/publish_lambdas.yml +++ b/.github/workflows/publish_lambdas.yml @@ -1,4 +1,4 @@ -name: "Publish: lambdas" +name: 'Publish: lambdas' on: workflow_call: @@ -14,7 +14,6 @@ on: type: string jobs: - publish-lambdas: runs-on: ubuntu-24.04 defaults: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6e20efb..ecafd40 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,5 +1,5 @@ name: Release -run-name: "Release: ${{ inputs.service }}" +run-name: 'Release: ${{ inputs.service }}' on: workflow_dispatch: @@ -13,17 +13,16 @@ on: - lambdas concurrency: - group: "release-${{ inputs.service }}" + group: 'release-${{ inputs.service }}' cancel-in-progress: false jobs: - verified: uses: ./.github/workflows/verify.yml create-git-tag: runs-on: ubuntu-latest - needs: [ verified ] + needs: [verified] steps: - if: github.ref != 'refs/heads/main' run: exit 1 @@ -50,7 +49,7 @@ jobs: create-gh-release: runs-on: ubuntu-latest - needs: [ create-git-tag ] + needs: [create-git-tag] steps: - name: create github release id: create diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index c514c97..648fc31 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -2,17 +2,17 @@ name: verify on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] workflow_call: jobs: - verified: runs-on: ubuntu-latest needs: -# - verify-backend + - format + # - verify-backend - verify-e2e - verify-frontend - verify-github @@ -21,41 +21,53 @@ jobs: - uses: actions/checkout@v4 - 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@v4 -# - uses: pnpm/action-setup@v4 -# - uses: actions/setup-node@v4 -# with: -# node-version: 22 -# cache: pnpm -# - name: sql -# run: | -# sudo apt-get update -y -# sudo apt-get install -y postgresql-client -# PGPASSWORD=eighty4 psql -h localhost -U eighty4 -f v001-init-schema.sql eighty4 -# working-directory: sql -# - run: pnpm i -# - name: verify -# run: | -# pnpm build -# pnpm test -# working-directory: backend + format: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version: 23 + cache: pnpm + - run: pnpm i + - run: pnpm exec prettier --check . + + # 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@v4 + # - uses: pnpm/action-setup@v4 + # - uses: actions/setup-node@v4 + # with: + # node-version: 22 + # cache: pnpm + # - name: sql + # run: | + # sudo apt-get update -y + # sudo apt-get install -y postgresql-client + # PGPASSWORD=eighty4 psql -h localhost -U eighty4 -f v001-init-schema.sql eighty4 + # working-directory: sql + # - run: pnpm i + # - name: verify + # run: | + # pnpm build + # pnpm test + # working-directory: backend verify-e2e: uses: ./.github/workflows/playwright.yml @@ -70,7 +82,7 @@ jobs: - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 23 cache: pnpm - run: pnpm i - run: VITE_GITHUB_CLIENT_ID=ci pnpm build @@ -82,7 +94,7 @@ jobs: - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 23 cache: pnpm - name: verify run: | @@ -97,7 +109,7 @@ jobs: - uses: pnpm/action-setup@v4 - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 23 cache: pnpm - name: verify shell: 'script -q -e -c "bash {0}"' diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..bd5535a --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +pnpm-lock.yaml diff --git a/.prettierrc.ts b/.prettierrc.ts new file mode 100644 index 0000000..7ef9937 --- /dev/null +++ b/.prettierrc.ts @@ -0,0 +1,19 @@ +import { type Config } from 'prettier' + +const config: Config = { + arrowParens: 'avoid', + semi: false, + singleQuote: true, + tabWidth: 4, + trailingComma: 'all', + overrides: [ + { + files: '*.{html,yaml,yml}', + options: { + tabWidth: 2, + }, + }, + ], +} + +export default config diff --git a/README.md b/README.md index d6f7f35..d2a496b 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,14 @@ See each package's .env file for environment variable names. Use these commands to run Install.sh locally: -| Package | Command | -|----------|------------------| -| lambdas | l3 sync | -| frontend | pnpm dev | +| Package | Command | +| -------- | -------- | +| lambdas | l3 sync | +| frontend | pnpm dev | APIs are deployed to AWS Lambdas with [eighty4/l3](https://github.com/eighty4/l3). For development, Vite will proxy API requests to AWS Lambda. -`l3 sync` must be run before starting the frontend Vite server. +`l3 sync` must be run before starting the frontend Vite server. Database features are currently disabled to migrate the backend to a serverless deployment. Unit tests, however, are still ran against Postgres. @@ -32,7 +32,7 @@ Data for offline mode is stubbed out in [//offline/src/data.ts](offline/src/data These commands will stub API dependencies for offline development of the frontend: | Package | Command | -|----------|------------------| +| -------- | ---------------- | | frontend | pnpm dev:offline | | offline | pnpm start | diff --git a/docker-compose.yml b/docker-compose.yml index 9eb4cc5..72d871c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,7 @@ services: container_name: binny-postgres image: postgres:17 ports: - - "5432:5432" + - '5432:5432' environment: POSTGRES_DB: eighty4 POSTGRES_USER: eighty4 diff --git a/e2e/package.json b/e2e/package.json index 08c0e48..ab75653 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -1,16 +1,16 @@ { - "name": "@eighty4/install-e2e", - "version": "0.0.1", - "private": true, - "author": "Adam McKee ", - "license": "BSD-3-Clause", - "type": "module", - "scripts": { - "test:ui": "playwright test --ui", - "test": "playwright test" - }, - "devDependencies": { - "@playwright/test": "^1.50.1", - "@types/node": "^22.13.5" - } + "name": "@eighty4/install-e2e", + "version": "0.0.1", + "private": true, + "author": "Adam McKee ", + "license": "BSD-3-Clause", + "type": "module", + "scripts": { + "test:ui": "playwright test --ui", + "test": "playwright test" + }, + "devDependencies": { + "@playwright/test": "^1.50.1", + "@types/node": "^22.13.5" + } } diff --git a/e2e/playwright.config.ts b/e2e/playwright.config.ts index 9fc5709..8784d7f 100644 --- a/e2e/playwright.config.ts +++ b/e2e/playwright.config.ts @@ -1,4 +1,8 @@ -import {defineConfig, devices, type PlaywrightTestConfig} from '@playwright/test' +import { + defineConfig, + devices, + type PlaywrightTestConfig, +} from '@playwright/test' // https://playwright.dev/docs/test-configuration @@ -16,24 +20,26 @@ export default defineConfig({ projects: [ { name: 'chromium', - use: {...devices['Desktop Chrome']}, + use: { ...devices['Desktop Chrome'] }, }, { name: 'firefox', - use: {...devices['Desktop Firefox']}, + use: { ...devices['Desktop Firefox'] }, }, { name: 'webkit', - use: {...devices['Desktop Safari']}, + use: { ...devices['Desktop Safari'] }, }, ], webServer: createWebServerConfig(), }) -function createWebServerConfig(): (PlaywrightTestConfig['webServer']) | undefined { +function createWebServerConfig(): + | PlaywrightTestConfig['webServer'] + | undefined { if (process.env.CI) { return { command: './start_app.sh', diff --git a/e2e/tests/configureAndDownload.spec.ts b/e2e/tests/configureAndDownload.spec.ts index 233c929..d8cfea0 100644 --- a/e2e/tests/configureAndDownload.spec.ts +++ b/e2e/tests/configureAndDownload.spec.ts @@ -1,7 +1,7 @@ -import {readFileSync} from 'node:fs' -import {type Download, expect, test} from '@playwright/test' +import { readFileSync } from 'node:fs' +import { type Download, expect, test } from '@playwright/test' -test('#configure/eighty4/maestro download script ', async ({page}) => { +test('#configure/eighty4/maestro download script ', async ({ page }) => { await page.goto('/') await page.click('#login') await page.waitForSelector('#login-redirect') @@ -14,7 +14,9 @@ test('#configure/eighty4/maestro download script ', async ({page}) => { await expect(page.getByText('MacOS & Linux')).toBeEnabled() await new Promise((res, rej) => { - page.on('download', (download) => verifyScript(download, 'install_maestro.sh').then(res).catch(rej)) + page.on('download', download => + verifyScript(download, 'install_maestro.sh').then(res).catch(rej), + ) page.getByText('MacOS & Linux').click().then().catch(rej) }) @@ -25,7 +27,11 @@ test('#configure/eighty4/maestro download script ', async ({page}) => { async function verifyScript(download: Download, filename: string) { expect(download.suggestedFilename()).toBe(filename) - expect(readFileSync(await download.path()).toString().startsWith(SCRIPT_HEADER)).toBe(true) + expect( + readFileSync(await download.path()) + .toString() + .startsWith(SCRIPT_HEADER), + ).toBe(true) } const SCRIPT_HEADER = `#!/usr/bin/env sh diff --git a/e2e/tests/loginAndLogout.spec.ts b/e2e/tests/loginAndLogout.spec.ts index 111efa0..565fb5c 100644 --- a/e2e/tests/loginAndLogout.spec.ts +++ b/e2e/tests/loginAndLogout.spec.ts @@ -1,6 +1,6 @@ -import {expect, test} from '@playwright/test' +import { expect, test } from '@playwright/test' -test('cancel login', async ({page}) => { +test('cancel login', async ({ page }) => { await page.goto('/') await page.click('#login') await page.waitForSelector('#login-cancel') @@ -8,7 +8,7 @@ test('cancel login', async ({page}) => { expect(await page.locator('html.out').count()).toBe(0) }) -test('login to #search route', async ({page}) => { +test('login to #search route', async ({ page }) => { await page.goto('/') await page.click('#login') await page.waitForSelector('#login-redirect') @@ -17,7 +17,7 @@ test('login to #search route', async ({page}) => { await page.waitForSelector('.repos') }) -test('logout navigates to /', async ({page}) => { +test('logout navigates to /', async ({ page }) => { await page.goto('/') await page.click('#login') await page.waitForSelector('#login-redirect') diff --git a/e2e/tests/searchToConfigure.spec.ts b/e2e/tests/searchToConfigure.spec.ts index e03f75f..f3a26d4 100644 --- a/e2e/tests/searchToConfigure.spec.ts +++ b/e2e/tests/searchToConfigure.spec.ts @@ -1,6 +1,6 @@ -import {expect, test} from '@playwright/test' +import { expect, test } from '@playwright/test' -test('#search to #configure/eighty4/maestro', async ({page}) => { +test('#search to #configure/eighty4/maestro', async ({ page }) => { await page.goto('/') await page.click('#login') await page.waitForSelector('#login-redirect') diff --git a/frontend/index.html b/frontend/index.html index fe45cc9..e5c458e 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1,37 +1,71 @@ - - - - - - - - - + + + + + + + + + Install.sh - - - -
-
-
-
-
- an app -
- +
+ + + diff --git a/frontend/launch/gitHubCallToAction.ts b/frontend/launch/gitHubCallToAction.ts index 19d5ec5..af5b631 100644 --- a/frontend/launch/gitHubCallToAction.ts +++ b/frontend/launch/gitHubCallToAction.ts @@ -4,16 +4,21 @@ const bugPaths = [ 'M40,200 C40,150 20,20 50,0', 'M20,200 C40,150 20,20 0,0', ] -const getRandomBugPath = () => `path("${bugPaths[Math.floor(Math.random() * bugPaths.length)]}")` +const getRandomBugPath = () => + `path("${bugPaths[Math.floor(Math.random() * bugPaths.length)]}")` export function initializeGitHubCallToAction() { const githubContainer = document.getElementById('github') as HTMLElement - let callToActions = githubContainer.querySelectorAll('.call-to-action') as NodeListOf + let callToActions = githubContainer.querySelectorAll( + '.call-to-action', + ) as NodeListOf setInterval(() => { // duped removal here bc an inactive tab may not fire onanimationend consistently // while setInterval is consistently called - const zombieBug = githubContainer.querySelector('#bug') as HTMLElement | null + const zombieBug = githubContainer.querySelector( + '#bug', + ) as HTMLElement | null if (zombieBug) { zombieBug.onanimationend = null zombieBug.remove() @@ -32,7 +37,7 @@ export function initializeGitHubCallToAction() { bugImg.ariaLabel = 'ah! a bug!' bugImg.style.offsetPath = getRandomBugPath() bugImg.style.right = `${document.body.clientWidth - callToAction.getBoundingClientRect().right + 25}px` - bugImg.onanimationend = (e) => (e.target as HTMLElement).remove() + bugImg.onanimationend = e => (e.target as HTMLElement).remove() githubContainer.appendChild(bugImg) } }, 4000) diff --git a/frontend/launch/init.css b/frontend/launch/init.css index 8901b19..411baba 100644 --- a/frontend/launch/init.css +++ b/frontend/launch/init.css @@ -4,7 +4,10 @@ font-display: swap; font-weight: 100 900; src: url('/inter.woff2') format('woff2-variations'); - unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; + unicode-range: + U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, + U+0304, U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, + U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } * { @@ -43,7 +46,8 @@ html { } } -html, body { +html, +body { min-height: 100vh; overflow-x: hidden; } @@ -78,13 +82,17 @@ html, body { z-index: var(--grid-z-index); pointer-events: none; - & .ish, #eighty4 *, #github * { + & .ish, + #eighty4 *, + #github * { opacity: 0; transition: 250ms opacity; } &.visible { - & .ish, #eighty4 *, #github * { + & .ish, + #eighty4 *, + #github * { opacity: 1; } } @@ -115,7 +123,7 @@ html, body { font-size: 2vh; color: var(--graph-paper-text-color); text-decoration: none; - gap: .5rem; + gap: 0.5rem; & img { height: 4vh; @@ -164,7 +172,7 @@ html, body { &.current { display: flex; align-items: center; - gap: .5rem; + gap: 0.5rem; } } diff --git a/frontend/launch/init.ts b/frontend/launch/init.ts index 62c22af..f3fda27 100644 --- a/frontend/launch/init.ts +++ b/frontend/launch/init.ts @@ -1,7 +1,7 @@ -import {initializeGitHubCallToAction} from './gitHubCallToAction.js' +import { initializeGitHubCallToAction } from './gitHubCallToAction.js' import './theme.js' -import {animateTriangleIntro} from './triangle.js' -import {getCookie} from '../src/parse.js' +import { animateTriangleIntro } from './triangle.js' +import { getCookie } from '../src/parse.js' const SKIP_ANIMATION = 'ish.skip_launch_animation' @@ -26,7 +26,7 @@ if (location.search === '?login') { async function playAnimationIntro() { if (document.hidden) { await new Promise(res => { - document.addEventListener('visibilitychange', res, {once: true}) + document.addEventListener('visibilitychange', res, { once: true }) }) } await animateTriangleIntro() diff --git a/frontend/launch/triangle.ts b/frontend/launch/triangle.ts index 312f59b..2f2d7d2 100644 --- a/frontend/launch/triangle.ts +++ b/frontend/launch/triangle.ts @@ -4,7 +4,7 @@ function lerp(min: number, max: number, ratio: number): number { } else if (ratio > 1) { return max } else { - return (ratio * (max - min)) + min + return ratio * (max - min) + min } } @@ -72,11 +72,11 @@ class BotRightAnimationCanvas extends AnimationCanvas { } boxPosX(cx: number, boxW: number): number { - return this.width - boxW - (cx * boxW) + return this.width - boxW - cx * boxW } boxPosY(ry: number, boxH: number): number { - return this.height - boxH - (ry * boxH) + return this.height - boxH - ry * boxH } clipPath(): Path2D { @@ -178,12 +178,28 @@ export function animateTriangleIntro(): Promise { const halfDuration = duration / 2 const botRightCanvas = new BotRightAnimationCanvas(createCanvasElement()) const topLeftCanvas = new TopLeftAnimationCanvas(createCanvasElement()) - const firstBotRightAnimation = new SizeAnimation(botRightCanvas, '#eee', '#111') - const secondBotRightAnimation = new SizeAnimation(botRightCanvas, '#111', '#eee') - const firstTopLeftAnimation = new SizeAnimation(topLeftCanvas, '#111', '#eee') - const secondTopLeftAnimation = new SizeAnimation(topLeftCanvas, '#eee', '#111') + const firstBotRightAnimation = new SizeAnimation( + botRightCanvas, + '#eee', + '#111', + ) + const secondBotRightAnimation = new SizeAnimation( + botRightCanvas, + '#111', + '#eee', + ) + const firstTopLeftAnimation = new SizeAnimation( + topLeftCanvas, + '#111', + '#eee', + ) + const secondTopLeftAnimation = new SizeAnimation( + topLeftCanvas, + '#eee', + '#111', + ) const start = Date.now() - return new Promise((res) => { + return new Promise(res => { const update = () => { const elapsed = Date.now() - start if (elapsed > halfDuration) { diff --git a/frontend/package.json b/frontend/package.json index 2df680e..ab6a5d6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,29 +1,27 @@ { - "name": "@eighty4/install-frontend", - "version": "0.0.1", - "private": true, - "author": "Adam McKee ", - "license": "BSD-3-Clause", - "type": "module", - "main": "lib/Domain.js", - "types": "lib/Domain.d.ts", - "scripts": { - "build": "pnpm build:dist", - "build:dist": "./build.sh", - "build:tsc": "tsc --build --clean && tsc --build", - "build:vite": "vite build", - "dev": "vite", - "dev:offline": "vite --mode offline", - "svg": "npx -y svgo@latest -f assets/svg -o public -r" - }, - "dependencies": { - "@eighty4/install-github": "workspace:^", - "@eighty4/install-template": "workspace:^" - }, - "devDependencies": { - "@minify-html/node": "^0.15.0", - "typescript": "^5.7.3", - "vite": "^6.1.1", - "vite-plugin-html-inline-sources": "^0.0.3" - } + "name": "@eighty4/install-frontend", + "version": "0.0.1", + "private": true, + "author": "Adam McKee ", + "license": "BSD-3-Clause", + "type": "module", + "scripts": { + "build": "pnpm build:dist", + "build:dist": "./build.sh", + "build:tsc": "tsc --build --clean && tsc --build", + "build:vite": "vite build", + "dev": "vite", + "dev:offline": "vite --mode offline", + "svg": "npx -y svgo@latest -f assets/svg -o public -r" + }, + "dependencies": { + "@eighty4/install-github": "workspace:^", + "@eighty4/install-template": "workspace:^" + }, + "devDependencies": { + "@minify-html/node": "^0.15.0", + "typescript": "^5.7.3", + "vite": "^6.1.1", + "vite-plugin-html-inline-sources": "^0.0.3" + } } diff --git a/frontend/src/api.ts b/frontend/src/api.ts index 20bcc74..1f4bcc4 100644 --- a/frontend/src/api.ts +++ b/frontend/src/api.ts @@ -1,15 +1,18 @@ -import {Unauthorized} from '@eighty4/install-github' -import type {GeneratedScript} from '@eighty4/install-template' -import {gitHubTokenCache} from './session/sessionCache.ts' +import { Unauthorized } from '@eighty4/install-github' +import type { GeneratedScript } from '@eighty4/install-template' +import { gitHubTokenCache } from './session/sessionCache.ts' -const GEN_SCRIPTS_URL = import.meta.env.VITE_INSTALL_API_BASE_URL + '/generated-scripts' +const GEN_SCRIPTS_URL = + import.meta.env.VITE_INSTALL_API_BASE_URL + '/generated-scripts' -export async function saveGeneratedScript(generatedScript: GeneratedScript): Promise { +export async function saveGeneratedScript( + generatedScript: GeneratedScript, +): Promise { const authToken = gitHubTokenCache.read() const response = await fetch(GEN_SCRIPTS_URL, { method: 'POST', headers: { - 'authorization': 'Bearer ' + authToken, + authorization: 'Bearer ' + authToken, 'content-type': 'application/json', }, body: JSON.stringify({ @@ -36,7 +39,7 @@ export async function fetchGeneratedScripts(): Promise> { const response = await fetch(GEN_SCRIPTS_URL, { method: 'GET', headers: { - 'authorization': 'Bearer ' + authToken, + authorization: 'Bearer ' + authToken, }, }) if (response.status !== 200) { @@ -50,11 +53,15 @@ export async function fetchGeneratedScripts(): Promise> { return response.json() } -export async function fetchGeneratedScriptsKeyedByRepo(): Promise> { +export async function fetchGeneratedScriptsKeyedByRepo(): Promise< + Record +> { const generatedScripts = await fetchGeneratedScripts() const result: Record = {} for (const generatedScript of generatedScripts) { - result[`${generatedScript.repository.owner}/${generatedScript.repository.name}`] = generatedScript + result[ + `${generatedScript.repository.owner}/${generatedScript.repository.name}` + ] = generatedScript } return result } diff --git a/frontend/src/app.css b/frontend/src/app.css index 026fac4..325eb99 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -10,7 +10,18 @@ left: 0; width: 100vw; height: 100vh; - clip-path: polygon(0 0, 0 90%, 10% 90%, 20% 80%, 20% 20%, 80% 20%, 90% 10%, 100% 10%, 100% 0, 0 0); + clip-path: polygon( + 0 0, + 0 90%, + 10% 90%, + 20% 80%, + 20% 20%, + 80% 20%, + 90% 10%, + 100% 10%, + 100% 0, + 0 0 + ); background: var(--triangle-bg-color); z-index: var(--triangle-z-index); } diff --git a/frontend/src/app.ts b/frontend/src/app.ts index 6154fcb..f589778 100644 --- a/frontend/src/app.ts +++ b/frontend/src/app.ts @@ -1,13 +1,13 @@ -import {Unauthorized, type User} from '@eighty4/install-github' +import { Unauthorized, type User } from '@eighty4/install-github' import './app.css' import createGitHubGraphApiClient from './createGitHubGraphApiClient.ts' -import {initializeCustomizationControls} from './customizations.ts' -import {getCookie} from './parse.ts' -import {handleCurrentRoute, subscribeRouterEvents} from './router.ts' +import { initializeCustomizationControls } from './customizations.ts' +import { getCookie } from './parse.ts' +import { handleCurrentRoute, subscribeRouterEvents } from './router.ts' import './components/define.ts' -import {showLoginButton} from './session/login.ts' -import {logout} from './session/logout.ts' -import {gitHubTokenCache, gitHubUserCache} from './session/sessionCache.ts' +import { showLoginButton } from './session/login.ts' +import { logout } from './session/logout.ts' +import { gitHubTokenCache, gitHubUserCache } from './session/sessionCache.ts' if (document.documentElement.classList.contains('app-ready')) { startApp() @@ -24,10 +24,11 @@ function startApp() { const ght = getCookie('ght') if (ght) { gitHubTokenCache.write(ght) - createGitHubGraphApiClient().queryUser() + createGitHubGraphApiClient() + .queryUser() .then(user => gitHubUserCache.write(user)) .then(startUserSession) - .catch((e) => { + .catch(e => { if (e instanceof Unauthorized) { logout() } else { @@ -53,6 +54,10 @@ export function initializeUserPanel() { } function createUserPanel(user: User) { - document.getElementById('page-grid')!.insertAdjacentHTML('beforeend', - ``) + document + .getElementById('page-grid')! + .insertAdjacentHTML( + 'beforeend', + ``, + ) } diff --git a/frontend/src/components/BackButton.css b/frontend/src/components/BackButton.css index 55d3a87..4dd8e92 100644 --- a/frontend/src/components/BackButton.css +++ b/frontend/src/components/BackButton.css @@ -2,14 +2,14 @@ div { cursor: pointer; display: none; opacity: 0; - transition: .3s opacity ease-in-out; + transition: 0.3s opacity ease-in-out; &.enabled { display: block; } &.hint { - opacity: .25; + opacity: 0.25; } &:hover { diff --git a/frontend/src/components/BackButton.html b/frontend/src/components/BackButton.html index fdacf56..c29f504 100644 --- a/frontend/src/components/BackButton.html +++ b/frontend/src/components/BackButton.html @@ -1,7 +1,17 @@
- - - - back to previous page + + + + back to previous page
diff --git a/frontend/src/components/BackButton.ts b/frontend/src/components/BackButton.ts index 696d701..8ce7a23 100644 --- a/frontend/src/components/BackButton.ts +++ b/frontend/src/components/BackButton.ts @@ -1,9 +1,8 @@ import css from './BackButton.css?inline' import html from './BackButton.html?raw' -import {cloneTemplate} from '../dom.ts' +import { cloneTemplate } from '../dom.ts' export default class BackButton extends HTMLElement { - private static readonly TEMPLATE_ID = 'tmpl-back-button' static templateHTML(): string { @@ -26,7 +25,7 @@ export default class BackButton extends HTMLElement { constructor() { super() - this.#shadow = this.attachShadow({mode: 'open'}) + this.#shadow = this.attachShadow({ mode: 'open' }) this.#shadow.appendChild(cloneTemplate(BackButton.TEMPLATE_ID)) this.#div = this.#shadow.querySelector('div')! } diff --git a/frontend/src/components/ProfilePicture.ts b/frontend/src/components/ProfilePicture.ts index 2665be3..7a4d6c6 100644 --- a/frontend/src/components/ProfilePicture.ts +++ b/frontend/src/components/ProfilePicture.ts @@ -1,7 +1,6 @@ -import {removeChildNodes} from '../dom.ts' +import { removeChildNodes } from '../dom.ts' export default class ProfilePicture extends HTMLElement { - static readonly SIZE = 30 static observedAttributes = ['owner'] @@ -26,7 +25,11 @@ export default class ProfilePicture extends HTMLElement { removeChildNodes(this) } - attributeChangedCallback(_name: string, _oldValue: string, newValue: string) { + attributeChangedCallback( + _name: string, + _oldValue: string, + newValue: string, + ) { this.setImgProps(newValue) } diff --git a/frontend/src/components/SpinIndicator.ts b/frontend/src/components/SpinIndicator.ts index fed7911..e6b3157 100644 --- a/frontend/src/components/SpinIndicator.ts +++ b/frontend/src/components/SpinIndicator.ts @@ -1,7 +1,6 @@ -import {cloneTemplate, removeChildNodes} from '../dom.ts' +import { cloneTemplate, removeChildNodes } from '../dom.ts' export default class SpinIndicator extends HTMLElement { - private static readonly TEMPLATE_ID = 'tmpl-spin-indicator' static templateHTML(): string { @@ -30,7 +29,7 @@ export default class SpinIndicator extends HTMLElement { constructor() { super() - this.attachShadow({mode: 'open'}) + this.attachShadow({ mode: 'open' }) } connectedCallback() { diff --git a/frontend/src/components/SystemLogo.ts b/frontend/src/components/SystemLogo.ts index 0ad63a7..2cec65b 100644 --- a/frontend/src/components/SystemLogo.ts +++ b/frontend/src/components/SystemLogo.ts @@ -1,4 +1,4 @@ -import type {OperatingSystem} from '@eighty4/install-template' +import type { OperatingSystem } from '@eighty4/install-template' interface SystemLogoOptions { color: string @@ -7,7 +7,6 @@ interface SystemLogoOptions { } export default class SystemLogo extends HTMLElement { - static observedAttributes = ['color', 'os', 'size'] constructor(opts?: Partial) { diff --git a/frontend/src/components/UserPanel.css b/frontend/src/components/UserPanel.css index f92655e..7da4646 100644 --- a/frontend/src/components/UserPanel.css +++ b/frontend/src/components/UserPanel.css @@ -16,8 +16,8 @@ border: 1px solid #ddd; font-family: monospace; text-align: center; - padding: .5rem 1rem; - margin: .5rem auto; + padding: 0.5rem 1rem; + margin: 0.5rem auto; width: max-content; max-width: 7vw; text-overflow: ellipsis; @@ -27,14 +27,14 @@ & #logout { margin: 0 auto; - padding: .5rem; + padding: 0.5rem; display: block; cursor: pointer; border: none; background: #eee; font-family: monospace; text-align: center; - transition: all .05s ease-in-out; + transition: all 0.05s ease-in-out; &:hover { background: #111; diff --git a/frontend/src/components/UserPanel.html b/frontend/src/components/UserPanel.html index 5d99c70..56abed1 100644 --- a/frontend/src/components/UserPanel.html +++ b/frontend/src/components/UserPanel.html @@ -1,5 +1,9 @@
- Your GitHub avatar -
-
logout
+ Your GitHub avatar +
+
logout
diff --git a/frontend/src/components/UserPanel.ts b/frontend/src/components/UserPanel.ts index bfec59b..7888ba8 100644 --- a/frontend/src/components/UserPanel.ts +++ b/frontend/src/components/UserPanel.ts @@ -1,10 +1,9 @@ import css from './UserPanel.css?inline' import html from './UserPanel.html?raw' -import {cloneTemplate} from '../dom.ts' -import {logout} from '../session/logout.ts' +import { cloneTemplate } from '../dom.ts' +import { logout } from '../session/logout.ts' export default class UserPanel extends HTMLElement { - private static readonly TEMPLATE_ID = 'tmpl-user-panel' static templateHTML(): string { @@ -15,7 +14,7 @@ export default class UserPanel extends HTMLElement { constructor() { super() - this.#shadow = this.attachShadow({mode: 'open'}) + this.#shadow = this.attachShadow({ mode: 'open' }) this.#shadow.appendChild(cloneTemplate(UserPanel.TEMPLATE_ID)) } diff --git a/frontend/src/components/configure/ArchitectureSelect.ts b/frontend/src/components/configure/ArchitectureSelect.ts index 55314d0..f7a3f64 100644 --- a/frontend/src/components/configure/ArchitectureSelect.ts +++ b/frontend/src/components/configure/ArchitectureSelect.ts @@ -1,10 +1,9 @@ -import {type Architecture, ARCHITECTURES} from '@eighty4/install-template' +import { type Architecture, ARCHITECTURES } from '@eighty4/install-template' import css from './ArchitectureSelect.css?inline' import html from './ArchitectureSelect.html?raw' -import {createArchitectureUpdate} from './ArchitectureUpdate.ts' +import { createArchitectureUpdate } from './ArchitectureUpdate.ts' export default class ArchitectureSelect extends HTMLElement { - private static readonly TEMPLATE_ID = 'tmpl-architecture-select' static templateHTML(): string { @@ -20,8 +19,9 @@ export default class ArchitectureSelect extends HTMLElement { } connectedCallback() { - this.#select.innerHTML = '' - + ARCHITECTURES.map(arch => ``).join('') + this.#select.innerHTML = + '' + + ARCHITECTURES.map(arch => ``).join('') this.#select.addEventListener('input', this.#onArchUpdate) this.appendChild(this.#select) } @@ -34,6 +34,8 @@ export default class ArchitectureSelect extends HTMLElement { #onArchUpdate = () => { this.#select.querySelector('option[value=""]')?.remove() const arch = this.#select.value as Architecture - this.dispatchEvent(createArchitectureUpdate(arch, this.getAttribute('data-filename')!)) + this.dispatchEvent( + createArchitectureUpdate(arch, this.getAttribute('data-filename')!), + ) } } diff --git a/frontend/src/components/configure/ArchitectureUpdate.ts b/frontend/src/components/configure/ArchitectureUpdate.ts index 43b84d9..1ef7c0f 100644 --- a/frontend/src/components/configure/ArchitectureUpdate.ts +++ b/frontend/src/components/configure/ArchitectureUpdate.ts @@ -1,4 +1,4 @@ -import type {Architecture} from '@eighty4/install-template' +import type { Architecture } from '@eighty4/install-template' export const ARCHITECTURE_UPDATE_EVENT_TYPE = 'architecture' @@ -9,6 +9,11 @@ export interface ArchitectureUpdate { filename: string } -export function createArchitectureUpdate(arch: Architecture, filename: string): CustomEvent { - return new CustomEvent(ARCHITECTURE_UPDATE_EVENT_TYPE, {detail: {arch, filename}}) +export function createArchitectureUpdate( + arch: Architecture, + filename: string, +): CustomEvent { + return new CustomEvent(ARCHITECTURE_UPDATE_EVENT_TYPE, { + detail: { arch, filename }, + }) } diff --git a/frontend/src/components/configure/ConfigureScript.css b/frontend/src/components/configure/ConfigureScript.css index bff221d..bed7d09 100644 --- a/frontend/src/components/configure/ConfigureScript.css +++ b/frontend/src/components/configure/ConfigureScript.css @@ -10,8 +10,9 @@ font-weight: 600; } -#version, #commit { - margin-top: .3rem; +#version, +#commit { + margin-top: 0.3rem; } table { @@ -19,7 +20,7 @@ table { text-align: left; & td { - padding: .5rem 0; + padding: 0.5rem 0; } &.file { diff --git a/frontend/src/components/configure/ConfigureScript.ts b/frontend/src/components/configure/ConfigureScript.ts index 64c3d20..c2e19dd 100644 --- a/frontend/src/components/configure/ConfigureScript.ts +++ b/frontend/src/components/configure/ConfigureScript.ts @@ -1,21 +1,30 @@ -import {type Asset, type Binary, Unauthorized} from '@eighty4/install-github' -import {generateScript, OPERATING_SYSTEMS, type OperatingSystem} from '@eighty4/install-template' -import {ARCHITECTURE_UPDATE_EVENT_TYPE, type ArchitectureUpdateEvent} from './ArchitectureUpdate.ts' +import { type Asset, type Binary, Unauthorized } from '@eighty4/install-github' +import { + generateScript, + OPERATING_SYSTEMS, + type OperatingSystem, +} from '@eighty4/install-template' +import { + ARCHITECTURE_UPDATE_EVENT_TYPE, + type ArchitectureUpdateEvent, +} from './ArchitectureUpdate.ts' import css from './ConfigureScript.css?inline' import html from './ConfigureScript.html?raw' -import {downloadScript} from './download.ts' -import DownloadPanel, {DOWNLOAD_SCRIPT_EVENT_TYPE, type DownloadScriptEvent} from './DownloadPanel.ts' +import { downloadScript } from './download.ts' +import DownloadPanel, { + DOWNLOAD_SCRIPT_EVENT_TYPE, + type DownloadScriptEvent, +} from './DownloadPanel.ts' import ScriptConfiguration from './ScriptConfiguration.ts' -import {saveGeneratedScript} from '../../api.ts' -import {cloneTemplate, removeChildNodes} from '../../dom.ts' -import type {RepositoryWithScript} from '../../routes/searchData.ts' -import {logout} from '../../session/logout.ts' +import { saveGeneratedScript } from '../../api.ts' +import { cloneTemplate, removeChildNodes } from '../../dom.ts' +import type { RepositoryWithScript } from '../../routes/searchData.ts' +import { logout } from '../../session/logout.ts' // todo links to gh commit, repo and release pages // todo release date // todo repo languages export default class ConfigureScript extends HTMLElement { - private static readonly TEMPLATE_ID = 'tmpl-configure-script' static templateHTML(): string { @@ -32,27 +41,42 @@ export default class ConfigureScript extends HTMLElement { constructor(repo: RepositoryWithScript) { super() - this.#configuration = new ScriptConfiguration(this.#repo = repo) - this.#shadow = this.attachShadow({mode: 'open'}) + this.#configuration = new ScriptConfiguration((this.#repo = repo)) + this.#shadow = this.attachShadow({ mode: 'open' }) this.#shadow.appendChild(cloneTemplate(ConfigureScript.TEMPLATE_ID)) - this.#shadow.append(this.#downloadPanel = new DownloadPanel()) + this.#shadow.append((this.#downloadPanel = new DownloadPanel())) } connectedCallback() { - this.#downloadPanel.addEventListener(DOWNLOAD_SCRIPT_EVENT_TYPE, this.#onDownloadButtonClick as EventListener) + this.#downloadPanel.addEventListener( + DOWNLOAD_SCRIPT_EVENT_TYPE, + this.#onDownloadButtonClick as EventListener, + ) this.#downloadPanel.update(this.#configuration) - this.#shadow.querySelector('#commit')!.textContent = this.#repo.latestRelease?.commitHash || '' - this.#shadow.querySelector('#name')!.textContent = `${this.#repo.owner}/${this.#repo.name}` - this.#shadow.querySelector('#version')!.textContent = this.#repo.latestRelease?.tag || '' - const {binaries, additionalAssets} = this.#configuration.buildAssetsView() + this.#shadow.querySelector('#commit')!.textContent = + this.#repo.latestRelease?.commitHash || '' + this.#shadow.querySelector('#name')!.textContent = + `${this.#repo.owner}/${this.#repo.name}` + this.#shadow.querySelector('#version')!.textContent = + this.#repo.latestRelease?.tag || '' + const { binaries, additionalAssets } = + this.#configuration.buildAssetsView() this.#renderBinariesTable(binaries) this.#renderAssetsTable(additionalAssets) } disconnectedCallback() { - this.#downloadPanel.removeEventListener('download-script', this.#onDownloadButtonClick as EventListener) - for (const configureBinaries of this.#shadow.querySelectorAll('architecture-select')) { - configureBinaries.removeEventListener(ARCHITECTURE_UPDATE_EVENT_TYPE, this.#onArchUpdate as EventListener) + this.#downloadPanel.removeEventListener( + 'download-script', + this.#onDownloadButtonClick as EventListener, + ) + for (const configureBinaries of this.#shadow.querySelectorAll( + 'architecture-select', + )) { + configureBinaries.removeEventListener( + ARCHITECTURE_UPDATE_EVENT_TYPE, + this.#onArchUpdate as EventListener, + ) } removeChildNodes(this.#shadow) } @@ -64,7 +88,9 @@ export default class ConfigureScript extends HTMLElement { tableHtml += `${os}` if (binaries[os].length) { for (const bin of binaries[os]) { - const arch = bin.arch ? bin.arch : `` + const arch = bin.arch + ? bin.arch + : `` tableHtml += `${bin.filename}${arch}` } } else { @@ -74,8 +100,13 @@ export default class ConfigureScript extends HTMLElement { const table = document.createElement('table') table.id = 'binaries' table.innerHTML = tableHtml - for (const archSelect of table.querySelectorAll('architecture-select')) { - archSelect.addEventListener(ARCHITECTURE_UPDATE_EVENT_TYPE, this.#onArchUpdate as EventListener) + for (const archSelect of table.querySelectorAll( + 'architecture-select', + )) { + archSelect.addEventListener( + ARCHITECTURE_UPDATE_EVENT_TYPE, + this.#onArchUpdate as EventListener, + ) } this.#shadow.appendChild(table) } @@ -93,7 +124,9 @@ export default class ConfigureScript extends HTMLElement { this.#shadow.appendChild(table) } - #onArchUpdate = ({detail: {arch, filename}}: ArchitectureUpdateEvent) => { + #onArchUpdate = ({ + detail: { arch, filename }, + }: ArchitectureUpdateEvent) => { if (this.#configuration.resolveArchitecture(filename, arch)) { this.#downloadPanel.update(this.#configuration) } @@ -105,16 +138,22 @@ export default class ConfigureScript extends HTMLElement { if (e.detail === 'Windows') { throw new Error() } - const generatedScript = generateScript(this.#configuration.buildGenerateScriptOptions()) + const generatedScript = generateScript( + this.#configuration.buildGenerateScriptOptions(), + ) downloadScript(`install_${this.#repo.name}.sh`, generatedScript.script) - saveGeneratedScript(generatedScript).then().catch((e) => { - if (e instanceof Unauthorized) { - logout() - } else { - console.error(e) - alert('The clouds are broken. We\'ll redirect you to the homepage.') - window.location.replace('/') - } - }) + saveGeneratedScript(generatedScript) + .then() + .catch(e => { + if (e instanceof Unauthorized) { + logout() + } else { + console.error(e) + alert( + "The clouds are broken. We'll redirect you to the homepage.", + ) + window.location.replace('/') + } + }) } } diff --git a/frontend/src/components/configure/DownloadPanel.css b/frontend/src/components/configure/DownloadPanel.css index 6318e99..2eee163 100644 --- a/frontend/src/components/configure/DownloadPanel.css +++ b/frontend/src/components/configure/DownloadPanel.css @@ -12,15 +12,15 @@ border: none; background: #80f390; color: #021; - font-size: .6rem; + font-size: 0.6rem; display: inline-flex; align-items: center; - padding: .5rem .75rem; - transition: background .1s; + padding: 0.5rem 0.75rem; + transition: background 0.1s; & svg { - margin-right: .5rem; - height: .9rem !important; + margin-right: 0.5rem; + height: 0.9rem !important; aspect-ratio: 1; } diff --git a/frontend/src/components/configure/DownloadPanel.html b/frontend/src/components/configure/DownloadPanel.html index 1237978..39acde2 100644 --- a/frontend/src/components/configure/DownloadPanel.html +++ b/frontend/src/components/configure/DownloadPanel.html @@ -1,28 +1,28 @@
-
- -
-
- -
+
+ +
+
+ +
diff --git a/frontend/src/components/configure/DownloadPanel.ts b/frontend/src/components/configure/DownloadPanel.ts index 2f117a2..b506c03 100644 --- a/frontend/src/components/configure/DownloadPanel.ts +++ b/frontend/src/components/configure/DownloadPanel.ts @@ -1,7 +1,7 @@ -import {type OperatingSystem} from '@eighty4/install-template' +import { type OperatingSystem } from '@eighty4/install-template' import css from './DownloadPanel.css?inline' import html from './DownloadPanel.html?raw' -import {cloneTemplate, removeChildNodes} from '../../dom.ts' +import { cloneTemplate, removeChildNodes } from '../../dom.ts' import type ScriptConfiguration from './ScriptConfiguration.ts' export const DOWNLOAD_SCRIPT_EVENT_TYPE = 'download-script' @@ -9,7 +9,6 @@ export const DOWNLOAD_SCRIPT_EVENT_TYPE = 'download-script' export type DownloadScriptEvent = CustomEvent export default class DownloadPanel extends HTMLElement { - private static readonly TEMPLATE_ID = 'tmpl-download-panel' static templateHTML(): string { @@ -24,27 +23,44 @@ export default class DownloadPanel extends HTMLElement { constructor() { super() - this.#shadow = this.attachShadow({mode: 'open'}) + this.#shadow = this.attachShadow({ mode: 'open' }) this.#shadow.appendChild(cloneTemplate(DownloadPanel.TEMPLATE_ID)) - this.#downloadLinuxButton = this.#shadow.querySelector('button[data-os="linux"]')! - this.#downloadWindowsButton = this.#shadow.querySelector('button[data-os="windows"]')! + this.#downloadLinuxButton = this.#shadow.querySelector( + 'button[data-os="linux"]', + )! + this.#downloadWindowsButton = this.#shadow.querySelector( + 'button[data-os="windows"]', + )! } connectedCallback() { this.#downloadLinuxButton.addEventListener('click', this.#downloadLinux) - this.#downloadWindowsButton.addEventListener('click', this.#downloadWindows) + this.#downloadWindowsButton.addEventListener( + 'click', + this.#downloadWindows, + ) } disconnectedCallback() { - this.#downloadLinuxButton.removeEventListener('click', this.#downloadLinux) - this.#downloadWindowsButton.removeEventListener('click', this.#downloadWindows) + this.#downloadLinuxButton.removeEventListener( + 'click', + this.#downloadLinux, + ) + this.#downloadWindowsButton.removeEventListener( + 'click', + this.#downloadWindows, + ) removeChildNodes(this.#shadow) } // todo change download button text if script only includes Linux or MacOS binaries update(configuration: ScriptConfiguration) { - this.#downloadLinuxButton.disabled = !(configuration.areOsBinariesResolved('Linux') && configuration.areOsBinariesResolved('MacOS')) - this.#downloadWindowsButton.disabled = !configuration.areOsBinariesResolved('Windows') + this.#downloadLinuxButton.disabled = !( + configuration.areOsBinariesResolved('Linux') && + configuration.areOsBinariesResolved('MacOS') + ) + this.#downloadWindowsButton.disabled = + !configuration.areOsBinariesResolved('Windows') } #downloadLinux = () => this.#download('Linux') @@ -52,6 +68,10 @@ export default class DownloadPanel extends HTMLElement { #downloadWindows = () => this.#download('Windows') #download(detail: OperatingSystem) { - this.dispatchEvent(new CustomEvent(DOWNLOAD_SCRIPT_EVENT_TYPE, {detail})) + this.dispatchEvent( + new CustomEvent(DOWNLOAD_SCRIPT_EVENT_TYPE, { + detail, + }), + ) } } diff --git a/frontend/src/components/configure/ScriptConfiguration.ts b/frontend/src/components/configure/ScriptConfiguration.ts index e3b0295..b9b976e 100644 --- a/frontend/src/components/configure/ScriptConfiguration.ts +++ b/frontend/src/components/configure/ScriptConfiguration.ts @@ -1,6 +1,11 @@ -import type {Architecture, Distribution, GenerateScriptOptions, OperatingSystem} from '@eighty4/install-template' -import type {Asset, Binary} from '@eighty4/install-github' -import type {RepositoryWithScript} from '../../routes/searchData.ts' +import type { + Architecture, + Distribution, + GenerateScriptOptions, + OperatingSystem, +} from '@eighty4/install-template' +import type { Asset, Binary } from '@eighty4/install-github' +import type { RepositoryWithScript } from '../../routes/searchData.ts' export default class ScriptConfiguration { readonly #repo: RepositoryWithScript @@ -17,8 +22,13 @@ export default class ScriptConfiguration { } } if (repo.script?.spec.explicitArchitectures) { - for (const filename of Object.keys(repo.script.spec.explicitArchitectures)) { - this.resolveArchitecture(filename, repo.script.spec.explicitArchitectures[filename]) + for (const filename of Object.keys( + repo.script.spec.explicitArchitectures, + )) { + this.resolveArchitecture( + filename, + repo.script.spec.explicitArchitectures[filename], + ) } } } @@ -45,7 +55,10 @@ export default class ScriptConfiguration { } } - buildAssetsView(): { binaries: Record>, additionalAssets: Array } { + buildAssetsView(): { + binaries: Record> + additionalAssets: Array + } { const binaries: Record> = { Linux: [], MacOS: [], @@ -64,10 +77,14 @@ export default class ScriptConfiguration { buildGenerateScriptOptions(): GenerateScriptOptions { return { - binaryInstalls: [{ - installAs: this.#repo.name, - binaries: this.#repo.latestRelease!.binaries.map(binary => binary.filename), - }], + binaryInstalls: [ + { + installAs: this.#repo.name, + binaries: this.#repo.latestRelease!.binaries.map( + binary => binary.filename, + ), + }, + ], explicitArchitectures: this.#resolved, repository: this.#repo, resolvedDistributions: this.#collectResolvedDistributions(), diff --git a/frontend/src/components/configure/download.ts b/frontend/src/components/configure/download.ts index 1deccef..228a8c1 100644 --- a/frontend/src/components/configure/download.ts +++ b/frontend/src/components/configure/download.ts @@ -9,7 +9,10 @@ export function downloadScript(filename: string, content: string) { } function downloadBlob(filename: string, content: string) { - downloadFile(filename, URL.createObjectURL(new Blob([content], {type: 'text/plain'}))) + downloadFile( + filename, + URL.createObjectURL(new Blob([content], { type: 'text/plain' })), + ) } // todo evaluate downloadBlob vs downloadText methods diff --git a/frontend/src/components/search/RepositoryLink.css b/frontend/src/components/search/RepositoryLink.css index c9a4842..dc9532f 100644 --- a/frontend/src/components/search/RepositoryLink.css +++ b/frontend/src/components/search/RepositoryLink.css @@ -4,32 +4,33 @@ color: var(--triangle-text-color); display: flex; flex-direction: column; - gap: .75rem; + gap: 0.75rem; text-decoration: none; padding: 1rem; box-sizing: border-box; cursor: pointer; transform-origin: center; - transition: all .3s ease-in-out; + transition: all 0.3s ease-in-out; &:hover { background: #282828; border-color: #555; border-radius: 15px; padding: 1rem 1.5rem; - transform: scale(.9); + transform: scale(0.9); } &:active { - transform: scale(.85) translateY(3px); - transition: all .075s ease-in-out; + transform: scale(0.85) translateY(3px); + transition: all 0.075s ease-in-out; } } -.deets, .header { +.deets, +.header { display: flex; align-items: center; - gap: .75rem; + gap: 0.75rem; } .name { @@ -38,7 +39,7 @@ } .version { - font-size: .9rem; + font-size: 0.9rem; } .deets { @@ -50,7 +51,7 @@ } .update { - font-size: .8rem; + font-size: 0.8rem; flex: 1; text-align: right; } diff --git a/frontend/src/components/search/RepositoryLink.html b/frontend/src/components/search/RepositoryLink.html index e1f73d0..b79956f 100644 --- a/frontend/src/components/search/RepositoryLink.html +++ b/frontend/src/components/search/RepositoryLink.html @@ -1,11 +1,11 @@ diff --git a/frontend/src/components/search/RepositoryLink.ts b/frontend/src/components/search/RepositoryLink.ts index b224172..d925592 100644 --- a/frontend/src/components/search/RepositoryLink.ts +++ b/frontend/src/components/search/RepositoryLink.ts @@ -1,15 +1,14 @@ -import type {OperatingSystem} from '@eighty4/install-template' -import {OPERATING_SYSTEMS} from '@eighty4/install-template' +import type { OperatingSystem } from '@eighty4/install-template' +import { OPERATING_SYSTEMS } from '@eighty4/install-template' import css from './RepositoryLink.css?inline' import html from './RepositoryLink.html?raw' import SystemLogo from '../SystemLogo.ts' -import {cloneTemplate} from '../../dom.ts' -import {pushConfigureRoute} from '../../router.ts' -import type {RepositoryWithScript} from '../../routes/searchData.ts' -import {configureRepoCache} from '../../session/sessionCache.ts' +import { cloneTemplate } from '../../dom.ts' +import { pushConfigureRoute } from '../../router.ts' +import type { RepositoryWithScript } from '../../routes/searchData.ts' +import { configureRepoCache } from '../../session/sessionCache.ts' export default class RepositoryLink extends HTMLElement { - private static readonly TEMPLATE_ID = 'tmpl-repo-link' static templateHTML(): string { @@ -23,7 +22,7 @@ export default class RepositoryLink extends HTMLElement { constructor(readonly repo: RepositoryWithScript) { super() this.#repo = repo - this.#shadow = this.attachShadow({mode: 'open'}) + this.#shadow = this.attachShadow({ mode: 'open' }) this.#shadow.appendChild(cloneTemplate(RepositoryLink.TEMPLATE_ID)) this.#render() } @@ -42,13 +41,24 @@ export default class RepositoryLink extends HTMLElement { } #render() { - this.#shadow.querySelector('profile-picture')!.setAttribute('owner', this.#repo.owner) + this.#shadow + .querySelector('profile-picture')! + .setAttribute('owner', this.#repo.owner) if (this.#repo.script) { - this.#shadow.querySelector('.link')!.appendChild(this.#createScriptVersion(this.#repo.script.templateVersion)) + this.#shadow + .querySelector('.link')! + .appendChild( + this.#createScriptVersion( + this.#repo.script.templateVersion, + ), + ) } - this.#shadow.querySelector('.name')!.textContent = `${this.#repo.owner}/${this.#repo.name}` - this.#shadow.querySelector('.update')!.textContent = this.#repo.latestRelease?.commitHash || '' - this.#shadow.querySelector('.version')!.textContent = this.#repo.latestRelease?.tag || '' + this.#shadow.querySelector('.name')!.textContent = + `${this.#repo.owner}/${this.#repo.name}` + this.#shadow.querySelector('.update')!.textContent = + this.#repo.latestRelease?.commitHash || '' + this.#shadow.querySelector('.version')!.textContent = + this.#repo.latestRelease?.tag || '' if (this.#repo.latestRelease) { const oses: Array = [] for (const os of OPERATING_SYSTEMS) { @@ -56,7 +66,9 @@ export default class RepositoryLink extends HTMLElement { oses.push(os) } } - this.#shadow!.querySelector('.os-logos')!.append(...oses.map(os => new SystemLogo({os}))) + this.#shadow!.querySelector('.os-logos')!.append( + ...oses.map(os => new SystemLogo({ os })), + ) } } @@ -66,9 +78,12 @@ export default class RepositoryLink extends HTMLElement { const checkmark = document.createElement('span') checkmark.style.color = 'lightgreen' checkmark.style.display = 'flex' - checkmark.innerHTML = '' + checkmark.innerHTML = + '' scriptVersion.appendChild(checkmark) - scriptVersion.appendChild(document.createTextNode(templateVersion + ' is up to date')) + scriptVersion.appendChild( + document.createTextNode(templateVersion + ' is up to date'), + ) return scriptVersion } } diff --git a/frontend/src/components/search/RepositorySection.ts b/frontend/src/components/search/RepositorySection.ts index aff7388..7cc723b 100644 --- a/frontend/src/components/search/RepositorySection.ts +++ b/frontend/src/components/search/RepositorySection.ts @@ -1,9 +1,13 @@ import RepositoryLink from './RepositoryLink.ts' import css from './RepositorySection.css?inline' -import {cloneTemplate, removeChildNodes} from '../../dom.ts' -import type {RepositoryWithScript} from '../../routes/searchData.ts' +import { cloneTemplate, removeChildNodes } from '../../dom.ts' +import type { RepositoryWithScript } from '../../routes/searchData.ts' -export type RepoSectionType = 'generated' | 'released' | 'compatible' | 'incompatible' +export type RepoSectionType = + | 'generated' + | 'released' + | 'compatible' + | 'incompatible' function repoSectionHeader(type: RepoSectionType): string { switch (type) { @@ -19,7 +23,6 @@ function repoSectionHeader(type: RepoSectionType): string { } export default class RepositorySection extends HTMLElement { - private static readonly TEMPLATE_ID = 'tmpl-repo-section' static templateHTML(): string { @@ -39,7 +42,7 @@ export default class RepositorySection extends HTMLElement { constructor(type: RepoSectionType, repos: Array) { super() - this.#shadow = this.attachShadow({mode: 'open'}) + this.#shadow = this.attachShadow({ mode: 'open' }) this.#shadow.appendChild(cloneTemplate(RepositorySection.TEMPLATE_ID)) this.#shadow.querySelector('h3')!.innerText = repoSectionHeader(type) this.#reposDiv = this.#shadow.querySelector('.repos')! diff --git a/frontend/src/createGitHubGraphApiClient.ts b/frontend/src/createGitHubGraphApiClient.ts index 0edafbe..323ada3 100644 --- a/frontend/src/createGitHubGraphApiClient.ts +++ b/frontend/src/createGitHubGraphApiClient.ts @@ -1,5 +1,5 @@ -import {GitHubApiClient} from '@eighty4/install-github' -import {gitHubTokenCache} from './session/sessionCache.ts' +import { GitHubApiClient } from '@eighty4/install-github' +import { gitHubTokenCache } from './session/sessionCache.ts' export default function (): GitHubApiClient { const accessToken = gitHubTokenCache.read() as string diff --git a/frontend/src/customizations.css b/frontend/src/customizations.css index 5758a6e..d00a569 100644 --- a/frontend/src/customizations.css +++ b/frontend/src/customizations.css @@ -14,7 +14,7 @@ flex-direction: column; align-items: center; justify-content: center; - gap: .25rem; + gap: 0.25rem; & button { background: transparent; diff --git a/frontend/src/customizations.html b/frontend/src/customizations.html index 59b3ae0..397155a 100644 --- a/frontend/src/customizations.html +++ b/frontend/src/customizations.html @@ -1,6 +1,8 @@ diff --git a/frontend/src/customizations.ts b/frontend/src/customizations.ts index 956fef4..603d127 100644 --- a/frontend/src/customizations.ts +++ b/frontend/src/customizations.ts @@ -8,7 +8,9 @@ export function initializeCustomizationControls() { } const pageGrid = document.querySelector('#page-grid')! pageGrid.insertAdjacentHTML('beforeend', html) - initializeStyleControl(pageGrid.querySelector('#flip-input') as HTMLInputElement) + initializeStyleControl( + pageGrid.querySelector('#flip-input') as HTMLInputElement, + ) } function initializeStyleControl(styleInput: HTMLInputElement) { diff --git a/frontend/src/dom.ts b/frontend/src/dom.ts index 0370cb2..2a86ca0 100644 --- a/frontend/src/dom.ts +++ b/frontend/src/dom.ts @@ -1,5 +1,7 @@ export function cloneTemplate(id: string): Node { - return (document.getElementById(id) as HTMLTemplateElement).content.cloneNode(true) + return ( + document.getElementById(id) as HTMLTemplateElement + ).content.cloneNode(true) } export function removeChildNodes(elem: Node): void { diff --git a/frontend/src/graphPaper.css b/frontend/src/graphPaper.css index af39c56..96a3cf0 100644 --- a/frontend/src/graphPaper.css +++ b/frontend/src/graphPaper.css @@ -3,8 +3,8 @@ html.out:not(.reader) #triangle { transform-origin: top left; transition-property: transform; transition-timing-function: ease; - transition-delay: .25s; - transition-duration: .6s; + transition-delay: 0.25s; + transition-duration: 0.6s; } #graph-paper-grid { diff --git a/frontend/src/graphPaper.ts b/frontend/src/graphPaper.ts index 5ff7614..5413815 100644 --- a/frontend/src/graphPaper.ts +++ b/frontend/src/graphPaper.ts @@ -1,6 +1,6 @@ -import {toggleLandingElements} from './ui.ts' +import { toggleLandingElements } from './ui.ts' import './graphPaper.css' -import {removeChildNodes} from './dom.ts' +import { removeChildNodes } from './dom.ts' let graphPaper: HTMLElement @@ -10,13 +10,22 @@ export function onClearGraphPaper(fn: () => void) { clearGraphPaperFn = fn } -export function showGraphPaper(readyFn?: (graphPaper: HTMLElement) => void): HTMLElement { +export function showGraphPaper( + readyFn?: (graphPaper: HTMLElement) => void, +): HTMLElement { if (!graphPaper) { graphPaper = createGraphPaper() toggleLandingElements(false) - document.body.insertAdjacentHTML('beforeend', `
`) - if (readyFn) document.getElementById('triangle')! - .addEventListener('transitionend', () => readyFn(graphPaper), {once: true}) + document.body.insertAdjacentHTML( + 'beforeend', + `
`, + ) + if (readyFn) + document + .getElementById('triangle')! + .addEventListener('transitionend', () => readyFn(graphPaper), { + once: true, + }) } else { clearGraphPaper(graphPaper) if (readyFn) readyFn(graphPaper) @@ -25,7 +34,10 @@ export function showGraphPaper(readyFn?: (graphPaper: HTMLElement) => void): HTM } function createGraphPaper(): HTMLElement { - document.body.insertAdjacentHTML('beforeend', `
`) + document.body.insertAdjacentHTML( + 'beforeend', + `
`, + ) return document.getElementById('graph-paper')! } diff --git a/frontend/src/router.ts b/frontend/src/router.ts index 4e221d4..456ae89 100644 --- a/frontend/src/router.ts +++ b/frontend/src/router.ts @@ -1,7 +1,7 @@ -import type {Repository} from '@eighty4/install-github' -import {openRepositoryConfig} from './routes/configure.ts' -import {findProgramRepository} from './routes/search.ts' -import {toggleReaderMode} from './ui.ts' +import type { Repository } from '@eighty4/install-github' +import { openRepositoryConfig } from './routes/configure.ts' +import { findProgramRepository } from './routes/search.ts' +import { toggleReaderMode } from './ui.ts' export function subscribeRouterEvents() { window.addEventListener('hashchange', handleCurrentRoute) diff --git a/frontend/src/routes/configure.ts b/frontend/src/routes/configure.ts index 6e770e1..ad0cc84 100644 --- a/frontend/src/routes/configure.ts +++ b/frontend/src/routes/configure.ts @@ -1,9 +1,9 @@ -import type {RepositoryWithScript} from './searchData.ts' +import type { RepositoryWithScript } from './searchData.ts' import createGitHubGraphApiClient from '../createGitHubGraphApiClient.ts' -import {showGraphPaper} from '../graphPaper.ts' -import {removeChildNodes} from '../dom.ts' +import { showGraphPaper } from '../graphPaper.ts' +import { removeChildNodes } from '../dom.ts' import ConfigureScript from '../components/configure/ConfigureScript.ts' -import {configureRepoCache} from '../session/sessionCache.ts' +import { configureRepoCache } from '../session/sessionCache.ts' // todo error handling export function openRepositoryConfig(repoOwner: string, repoName: string) { diff --git a/frontend/src/routes/search.empty.html b/frontend/src/routes/search.empty.html index 15134cb..f57eadc 100644 --- a/frontend/src/routes/search.empty.html +++ b/frontend/src/routes/search.empty.html @@ -1,15 +1,45 @@ -
-

Oh, this is awkward

-

You don't have any GitHub releases with artifacts for Linux, MacOS or Windows.

-

Try these docs and come back:

-

CLI

-

Create - a release

-

Create - a release asset

-

Rest API

-

Create a release

-

Create a release asset

+
+

Oh, this is awkward

+

+ You don't have any GitHub releases with artifacts for Linux, MacOS or + Windows. +

+

Try these docs and come back:

+

CLI

+

+ Create a release +

+

+ Create a release asset +

+

Rest API

+

+ Create a release +

+

+ Create a release asset +

diff --git a/frontend/src/routes/search.error.html b/frontend/src/routes/search.error.html index e9f4487..4134f93 100644 --- a/frontend/src/routes/search.error.html +++ b/frontend/src/routes/search.error.html @@ -1,4 +1,11 @@ -
-

Oh, this is awkward

-
An error occurred
+
+

Oh, this is awkward

+
An error occurred
diff --git a/frontend/src/routes/search.ts b/frontend/src/routes/search.ts index 38963d3..b0b0aef 100644 --- a/frontend/src/routes/search.ts +++ b/frontend/src/routes/search.ts @@ -1,16 +1,16 @@ import './search.css' import emptyHtml from './search.empty.html?raw' import errorHtml from './search.error.html?raw' -import {fetchSearchData, type SearchData} from './searchData.ts' -import {showGraphPaper} from '../graphPaper.ts' +import { fetchSearchData, type SearchData } from './searchData.ts' +import { showGraphPaper } from '../graphPaper.ts' import RepositorySection from '../components/search/RepositorySection.ts' -import {createSessionCache, gitHubUserCache} from '../session/sessionCache.ts' +import { createSessionCache, gitHubUserCache } from '../session/sessionCache.ts' const searchDataCache = createSessionCache('search.data') // todo move view oriented code of route fn to component export function findProgramRepository() { - showGraphPaper((graphPaper) => { + showGraphPaper(graphPaper => { graphPaper.classList.add('search-route') const searchData = searchDataCache.read() @@ -34,16 +34,36 @@ export function findProgramRepository() { } else { graphPaper.innerHTML = `

${gitHubUserCache.read()!.login}

` if (searchData.releasesWithGeneratedScripts.length) { - graphPaper.appendChild(new RepositorySection('generated', searchData.releasesWithGeneratedScripts)) + graphPaper.appendChild( + new RepositorySection( + 'generated', + searchData.releasesWithGeneratedScripts, + ), + ) } if (searchData.releasesWithBinaries.length) { - graphPaper.appendChild(new RepositorySection('released', searchData.releasesWithBinaries)) + graphPaper.appendChild( + new RepositorySection( + 'released', + searchData.releasesWithBinaries, + ), + ) } if (searchData.notReleasedWithCompatibleLanguage.length) { - graphPaper.appendChild(new RepositorySection('compatible', searchData.notReleasedWithCompatibleLanguage)) + graphPaper.appendChild( + new RepositorySection( + 'compatible', + searchData.notReleasedWithCompatibleLanguage, + ), + ) } if (searchData.everythingElse.length) { - graphPaper.appendChild(new RepositorySection('incompatible', searchData.everythingElse)) + graphPaper.appendChild( + new RepositorySection( + 'incompatible', + searchData.everythingElse, + ), + ) } } } @@ -60,7 +80,8 @@ export function findProgramRepository() { console.error(e) graphPaper.innerHTML = errorHtml if (import.meta.env.DEV) { - graphPaper.querySelector('code')!.innerText = e.stack?.toString() || e.message + graphPaper.querySelector('code')!.innerText = + e.stack?.toString() || e.message } } }) diff --git a/frontend/src/routes/searchData.ts b/frontend/src/routes/searchData.ts index f82515b..258354a 100644 --- a/frontend/src/routes/searchData.ts +++ b/frontend/src/routes/searchData.ts @@ -1,6 +1,6 @@ -import type {Repository} from '@eighty4/install-github' -import type {GeneratedScript} from '@eighty4/install-template' -import {fetchGeneratedScriptsKeyedByRepo} from '../api.ts' +import type { Repository } from '@eighty4/install-github' +import type { GeneratedScript } from '@eighty4/install-template' +import { fetchGeneratedScriptsKeyedByRepo } from '../api.ts' import createGitHubGraphApiClient from '../createGitHubGraphApiClient.ts' export type RepositoryWithScript = Repository & { script?: GeneratedScript } @@ -14,7 +14,8 @@ export interface SearchData { } export async function fetchSearchData(): Promise { - const repositories = await createGitHubGraphApiClient().collectUserRepositories() + const repositories = + await createGitHubGraphApiClient().collectUserRepositories() const generatedScripts = await fetchGeneratedScriptsKeyedByRepo() const releasesWithGeneratedScripts: Array = [] const releasesWithBinaries: Array = [] @@ -23,7 +24,7 @@ export async function fetchSearchData(): Promise { for (const repo of repositories) { const script = generatedScripts[`${repo.owner}/${repo.name}`] if (script) { - releasesWithGeneratedScripts.push({...repo, script}) + releasesWithGeneratedScripts.push({ ...repo, script }) } else if (repo.latestRelease?.binaries?.length) { releasesWithBinaries.push(repo) } else if (repo.languages.length) { diff --git a/frontend/src/session/login.css b/frontend/src/session/login.css index 9dc7358..213315c 100644 --- a/frontend/src/session/login.css +++ b/frontend/src/session/login.css @@ -14,7 +14,9 @@ --login-link-moving-ratio: 1; position: fixed; top: calc(var(--login-link-position-percent) - var(--login-link-center-y)); - right: calc(var(--login-link-position-percent) - var(--login-link-center-x)); + right: calc( + var(--login-link-position-percent) - var(--login-link-center-x) + ); z-index: calc(var(--grid-z-index) + 1); width: var(--login-link-width); height: var(--login-link-height); @@ -22,7 +24,7 @@ justify-content: center; align-items: center; font-size: 2vmin; - border: .5vmin solid hotpink; + border: 0.5vmin solid hotpink; background: var(--black); color: lawngreen; user-select: none; @@ -62,11 +64,11 @@ } 40% { - transform: rotate(var(--diagonal-rotate-rad)) scaleY(.7) scaleX(1.1); + transform: rotate(var(--diagonal-rotate-rad)) scaleY(0.7) scaleX(1.1); } 60% { - transform: rotate(var(--diagonal-rotate-rad)) scaleY(.7) scaleX(1.1); + transform: rotate(var(--diagonal-rotate-rad)) scaleY(0.7) scaleX(1.1); } 100% { @@ -76,13 +78,23 @@ @keyframes login-slide { 0% { - top: calc(var(--login-link-previous-position-percent) - var(--login-link-center-y)); - right: calc(var(--login-link-previous-position-percent) - var(--login-link-center-x)); + top: calc( + var(--login-link-previous-position-percent) - + var(--login-link-center-y) + ); + right: calc( + var(--login-link-previous-position-percent) - + var(--login-link-center-x) + ); } 100% { - top: calc(var(--login-link-position-percent) - var(--login-link-center-y)); - right: calc(var(--login-link-position-percent) - var(--login-link-center-x)); + top: calc( + var(--login-link-position-percent) - var(--login-link-center-y) + ); + right: calc( + var(--login-link-position-percent) - var(--login-link-center-x) + ); } } @@ -113,7 +125,7 @@ #login-cancel { border: 1px solid transparent; - opacity: .8; + opacity: 0.8; transition: all 100ms ease-in-out; &:hover { diff --git a/frontend/src/session/login.html b/frontend/src/session/login.html index 44c1bc1..8b42e00 100644 --- a/frontend/src/session/login.html +++ b/frontend/src/session/login.html @@ -1,8 +1,8 @@
-

Redirecting to GitHub for auth.

-

Hold on to your bits.

-
- - -
+

Redirecting to GitHub for auth.

+

Hold on to your bits.

+
+ + +
diff --git a/frontend/src/session/login.ts b/frontend/src/session/login.ts index abd6a76..469e9dd 100644 --- a/frontend/src/session/login.ts +++ b/frontend/src/session/login.ts @@ -1,8 +1,8 @@ import './login.css' -import html from './login.html?raw' -import {getPageGrid, toggleReaderMode} from '../ui.ts' +import html from './login.html?raw' +import { getPageGrid, toggleReaderMode } from '../ui.ts' -const LOGIN_LINK_MOVE_THRESHOLD = .075 +const LOGIN_LINK_MOVE_THRESHOLD = 0.075 interface Point { x: number @@ -16,23 +16,36 @@ export function showLoginButton() { let splitSlope = 0 let splitYIntercept = 0 let proximitySlope = 0 - let previousIntersectionPoint: Point = {x: document.body.clientWidth / 2, y: document.body.clientHeight / 2} - let previousPositionRatio = .15 + let previousIntersectionPoint: Point = { + x: document.body.clientWidth / 2, + y: document.body.clientHeight / 2, + } + let previousPositionRatio = 0.15 function onResize() { - const {clientHeight, clientWidth} = document.body - splitDistance = calculateDistanceBetweenPoints({ - x: 0, - y: document.body.clientHeight, - }, { - x: document.body.clientWidth, - y: 0, - }) - splitSlope = -1 * document.body.clientHeight / document.body.clientWidth + const { clientHeight, clientWidth } = document.body + splitDistance = calculateDistanceBetweenPoints( + { + x: 0, + y: document.body.clientHeight, + }, + { + x: document.body.clientWidth, + y: 0, + }, + ) + splitSlope = + (-1 * document.body.clientHeight) / document.body.clientWidth splitYIntercept = document.body.clientHeight proximitySlope = calculateInverseReciprocal(splitSlope) - document.documentElement.style.setProperty('--diagonal-length-px', `${splitDistance}px`) - document.documentElement.style.setProperty('--diagonal-rotate-rad', `${Math.atan(clientHeight / clientWidth) * -1}rad`) + document.documentElement.style.setProperty( + '--diagonal-length-px', + `${splitDistance}px`, + ) + document.documentElement.style.setProperty( + '--diagonal-rotate-rad', + `${Math.atan(clientHeight / clientWidth) * -1}rad`, + ) } onResize() @@ -42,25 +55,53 @@ export function showLoginButton() { if (loginLink.classList.contains('moving') || delaying) { return } - const proximityPoint = {x: e.clientX, y: e.clientY} - const proximityYIntercept = calculateYInterceptForLine(proximityPoint, proximitySlope) - const intersectionPoint = calculateIntersectionPointOfTwoLines(splitSlope, splitYIntercept, proximitySlope, proximityYIntercept) - const intersectionPointDistance = calculateDistanceBetweenPoints(intersectionPoint, { - x: document.body.clientWidth, - y: 0, - }) + const proximityPoint = { x: e.clientX, y: e.clientY } + const proximityYIntercept = calculateYInterceptForLine( + proximityPoint, + proximitySlope, + ) + const intersectionPoint = calculateIntersectionPointOfTwoLines( + splitSlope, + splitYIntercept, + proximitySlope, + proximityYIntercept, + ) + const intersectionPointDistance = calculateDistanceBetweenPoints( + intersectionPoint, + { + x: document.body.clientWidth, + y: 0, + }, + ) const positionRatio = intersectionPointDistance / splitDistance - const positionRatioDeltaWithPreviousRatio = Math.abs(positionRatio - previousPositionRatio) + const positionRatioDeltaWithPreviousRatio = Math.abs( + positionRatio - previousPositionRatio, + ) if (positionRatioDeltaWithPreviousRatio < LOGIN_LINK_MOVE_THRESHOLD) { return } - const pixelsMoved = calculateDistanceBetweenPoints(intersectionPoint, previousIntersectionPoint) - loginLink.style.setProperty('--login-link-previous-position-percent', loginLink.style.getPropertyValue('--login-link-position-percent')) + const pixelsMoved = calculateDistanceBetweenPoints( + intersectionPoint, + previousIntersectionPoint, + ) + loginLink.style.setProperty( + '--login-link-previous-position-percent', + loginLink.style.getPropertyValue('--login-link-position-percent'), + ) const positionPercent = positionRatio * 100 - const clampedPositionPercent = positionPercent < 50 ? Math.max(positionPercent, 12) : Math.min(positionPercent, 88) - loginLink.style.setProperty('--login-link-position-percent', `${clampedPositionPercent}%`) + const clampedPositionPercent = + positionPercent < 50 + ? Math.max(positionPercent, 12) + : Math.min(positionPercent, 88) + loginLink.style.setProperty( + '--login-link-position-percent', + `${clampedPositionPercent}%`, + ) console.log(positionPercent, clampedPositionPercent) - loginLink.style.setProperty('--login-link-moving-duration', `${(pixelsMoved / splitDistance) * 2}s`) + loginLink.style.setProperty( + '--login-link-moving-duration', + `${(pixelsMoved / splitDistance) * 2}s`, + ) previousIntersectionPoint = intersectionPoint previousPositionRatio = positionRatio loginLink.classList.add('moving') @@ -69,7 +110,7 @@ export function showLoginButton() { loginLink.addEventListener('animationend', () => { delaying = true loginLink.classList.remove('show', 'moving') - setTimeout(() => delaying = false, 100) + setTimeout(() => (delaying = false), 100) }) window.addEventListener('mousemove', debounce(onMouseEvent, 84)) @@ -89,8 +130,12 @@ function showLoginPrompt() { const grid = getPageGrid() grid.insertAdjacentHTML('beforeend', html) const loginPrompt = grid.querySelector('#login-prompt')! - const redirectButton = loginPrompt.querySelector('#login-redirect') as HTMLButtonElement - const cancelButton = loginPrompt.querySelector('#login-cancel') as HTMLButtonElement + const redirectButton = loginPrompt.querySelector( + '#login-redirect', + ) as HTMLButtonElement + const cancelButton = loginPrompt.querySelector( + '#login-cancel', + ) as HTMLButtonElement redirectButton.addEventListener('click', redirectToLogin) cancelButton.addEventListener('click', closeLoginPrompt) @@ -110,17 +155,22 @@ function showLoginPrompt() { function calculateDistanceBetweenPoints(p1: Point, p2: Point): number { const xd = p2.x - p1.x const yd = p2.y - p1.y - return Math.sqrt((xd * xd) + (yd * yd)) + return Math.sqrt(xd * xd + yd * yd) } function calculateYInterceptForLine(p: Point, m: number): number { - return p.y - (m * p.x) + return p.y - m * p.x } -function calculateIntersectionPointOfTwoLines(m1: number, yi1: number, m2: number, yi2: number): Point { +function calculateIntersectionPointOfTwoLines( + m1: number, + yi1: number, + m2: number, + yi2: number, +): Point { const x = (yi1 - yi2) / (m2 - m1) const y = m2 * x + yi2 - return {x, y} + return { x, y } } function calculateInverseReciprocal(m: number) { diff --git a/frontend/src/session/sessionCache.ts b/frontend/src/session/sessionCache.ts index c1b62ae..c110684 100644 --- a/frontend/src/session/sessionCache.ts +++ b/frontend/src/session/sessionCache.ts @@ -1,5 +1,5 @@ -import type {User} from '@eighty4/install-github' -import type {RepositoryWithScript} from '../routes/searchData.ts' +import type { User } from '@eighty4/install-github' +import type { RepositoryWithScript } from '../routes/searchData.ts' export interface SessionCache { clear(): void @@ -29,7 +29,8 @@ export function createSessionCache(key: string): SessionCache { } } -export const configureRepoCache = createSessionCache('configure.repo') +export const configureRepoCache = + createSessionCache('configure.repo') export const gitHubTokenCache = createSessionCache('ght') diff --git a/frontend/src/ui.css b/frontend/src/ui.css index 95c8a22..ce07e99 100644 --- a/frontend/src/ui.css +++ b/frontend/src/ui.css @@ -2,8 +2,11 @@ --out-transition-duration: 400ms; } -#controls, #login { - transition: transform var(--out-transition-duration) ease-in-out, opacity 100ms ease-in-out var(--out-transition-duration); +#controls, +#login { + transition: + transform var(--out-transition-duration) ease-in-out, + opacity 100ms ease-in-out var(--out-transition-duration); } #controls { @@ -28,7 +31,8 @@ html.out { * UI Reader mode */ -#eighty4, #github { +#eighty4, +#github { transition: transform var(--out-transition-duration) ease-in-out; transition-delay: 150ms; } @@ -52,7 +56,9 @@ html.out { } &.shifted { - transform: rotate(var(--triangle-shift-rotate-degs)) scale(var(--triangle-shift-scale-percent)) translateX(var(--triangle-shift-translate-x)); + transform: rotate(var(--triangle-shift-rotate-degs)) + scale(var(--triangle-shift-scale-percent)) + translateX(var(--triangle-shift-translate-x)); } } @@ -77,10 +83,14 @@ html.reader { } 50% { - transform: rotate(var(--triangle-shift-rotate-degs)) scale(var(--triangle-shift-scale-percent)) translateX(0vw) translateY(0vh); + transform: rotate(var(--triangle-shift-rotate-degs)) + scale(var(--triangle-shift-scale-percent)) translateX(0vw) + translateY(0vh); } 100% { - transform: rotate(var(--triangle-shift-rotate-degs)) scale(var(--triangle-shift-scale-percent)) translateX(var(--triangle-shift-translate-x)); + transform: rotate(var(--triangle-shift-rotate-degs)) + scale(var(--triangle-shift-scale-percent)) + translateX(var(--triangle-shift-translate-x)); } } diff --git a/frontend/src/ui.ts b/frontend/src/ui.ts index ce2d955..9168049 100644 --- a/frontend/src/ui.ts +++ b/frontend/src/ui.ts @@ -5,7 +5,12 @@ export function getPageGrid(): HTMLElement { } export function toggleLandingElements(showOrHide?: boolean) { - const fn = typeof showOrHide === 'undefined' ? 'toggle' : showOrHide ? 'remove' : 'add' + const fn = + typeof showOrHide === 'undefined' + ? 'toggle' + : showOrHide + ? 'remove' + : 'add' document.documentElement.classList[fn]('out') } @@ -14,11 +19,15 @@ export async function toggleReaderMode(openOrClose: boolean): Promise { toggleLandingElements(!openOrClose) document.documentElement.classList[openOrClose ? 'add' : 'remove']('reader') triangleDiv.classList.toggle(openOrClose ? 'shift' : 'unshift') - return new Promise((res) => { - triangleDiv.addEventListener('animationend', () => { - triangleDiv.classList.toggle('shifted') - triangleDiv.classList.remove('shift', 'unshift') - res() - }, {once: true}) + return new Promise(res => { + triangleDiv.addEventListener( + 'animationend', + () => { + triangleDiv.classList.toggle('shifted') + triangleDiv.classList.remove('shift', 'unshift') + res() + }, + { once: true }, + ) }) } diff --git a/frontend/templateVersion.js b/frontend/templateVersion.js index 57d47b1..fb1684a 100644 --- a/frontend/templateVersion.js +++ b/frontend/templateVersion.js @@ -1,12 +1,16 @@ -import {readFileSync} from 'node:fs' +import { readFileSync } from 'node:fs' try { - let packageJson = import.meta.resolve('./node_modules/@eighty4/install-template/package.json') + let packageJson = import.meta.resolve( + './node_modules/@eighty4/install-template/package.json', + ) if (packageJson.startsWith('file:///')) { packageJson = packageJson.substring(7) } const version = JSON.parse(readFileSync(packageJson).toString()).version console.log(version) } catch (e) { - throw new Error('failed resolving version of @eighty4/install-template: ' + e.message) + throw new Error( + 'failed resolving version of @eighty4/install-template: ' + e.message, + ) } diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index 8eb5f77..760bf3a 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -1,19 +1,19 @@ { - "extends": "../tsconfig.json", - "compilerOptions": { - "module": "ESNext", - "lib": ["ES2020", "DOM", "DOM.Iterable"], - "moduleResolution": "bundler", - "allowImportingTsExtensions": true, - "noEmit": true, - }, - "include": ["src/**/*.ts"], - "references": [ - { - "path": "../github" + "extends": "../tsconfig.json", + "compilerOptions": { + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "noEmit": true }, - { - "path": "../template" - } - ] + "include": ["src/**/*.ts"], + "references": [ + { + "path": "../github" + }, + { + "path": "../template" + } + ] } diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index fa369dd..2b86cbb 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -1,5 +1,5 @@ import * as fs from 'node:fs' -import {type ConfigEnv, defineConfig, loadEnv, type ProxyOptions} from 'vite' +import { type ConfigEnv, defineConfig, loadEnv, type ProxyOptions } from 'vite' import inlining from 'vite-plugin-html-inline-sources' export default defineConfig((env: ConfigEnv) => { @@ -27,15 +27,21 @@ function checkEnvVarsAreSet(mode: string): undefined | never { if (mode === 'offline') { return } - const {VITE_GITHUB_CLIENT_ID} = loadEnv(mode, process.cwd()) + const { VITE_GITHUB_CLIENT_ID } = loadEnv(mode, process.cwd()) if (!VITE_GITHUB_CLIENT_ID || !VITE_GITHUB_CLIENT_ID.length) { - console.error('Install.sh requires a `VITE_GITHUB_CLIENT_ID` environment variable for running Vite in dev or build modes.') - console.error('Try `VITE_GITHUB_CLIENT_ID=my_client_id pnpm build` or creating a `.env.development` file.') + console.error( + 'Install.sh requires a `VITE_GITHUB_CLIENT_ID` environment variable for running Vite in dev or build modes.', + ) + console.error( + 'Try `VITE_GITHUB_CLIENT_ID=my_client_id pnpm build` or creating a `.env.development` file.', + ) process.exit(1) } } -function buildProxyConfig(mode: string): Record | undefined { +function buildProxyConfig( + mode: string, +): Record | undefined { switch (mode) { case 'production': return @@ -54,9 +60,15 @@ function getApiGatewayApiId(): string | never { try { return fs.readFileSync('../lambdas/.l3/aws/api').toString().trim() } catch (e) { - console.error('Unable to read Amazon Gateway API id from `//lambdas/.l3/aws/api`.') - console.error(`Run \`l3 sync\` from \`//lambdas\` before running the Install.sh frontend development Vite server.`) - console.error('There is more information about local development in `//README.md`.') + console.error( + 'Unable to read Amazon Gateway API id from `//lambdas/.l3/aws/api`.', + ) + console.error( + `Run \`l3 sync\` from \`//lambdas\` before running the Install.sh frontend development Vite server.`, + ) + console.error( + 'There is more information about local development in `//README.md`.', + ) process.exit(1) } } diff --git a/github/package.json b/github/package.json index 447e23c..f7baf9f 100644 --- a/github/package.json +++ b/github/package.json @@ -1,24 +1,24 @@ { - "name": "@eighty4/install-github", - "version": "0.0.1", - "author": "Adam McKee ", - "license": "BSD-3-Clause", - "type": "module", - "main": "lib/GitHubApiClient.js", - "types": "lib/GitHubApiClient.d.ts", - "scripts": { - "build": "tsc --build --clean && tsc --build" - }, - "dependencies": { - "@eighty4/install-template": "workspace:^" - }, - "devDependencies": { - "typescript": "^5.7.3" - }, - "files": [ - "lib/**/*", - "src/**/*", - "package.json", - "tsconfig.json" - ] + "name": "@eighty4/install-github", + "version": "0.0.1", + "author": "Adam McKee ", + "license": "BSD-3-Clause", + "type": "module", + "main": "lib/GitHubApiClient.js", + "types": "lib/GitHubApiClient.d.ts", + "scripts": { + "build": "tsc --build --clean && tsc --build" + }, + "dependencies": { + "@eighty4/install-template": "workspace:^" + }, + "devDependencies": { + "typescript": "^5.7.3" + }, + "files": [ + "lib/**/*", + "src/**/*", + "package.json", + "tsconfig.json" + ] } diff --git a/github/src/GitHubApiClient.ts b/github/src/GitHubApiClient.ts index 416bc11..2116781 100644 --- a/github/src/GitHubApiClient.ts +++ b/github/src/GitHubApiClient.ts @@ -5,8 +5,12 @@ import { type ViewerRepositoriesWithLatestReleaseGraph, type ViewerUserGraph, } from './graphApiTypes.js' -import {fetchUserQuery, queryLatestReleaseQuery, queryUserRepositoriesQuery} from './graphApiQueries.js' -import type {Repository, User} from './Model.js' +import { + fetchUserQuery, + queryLatestReleaseQuery, + queryUserRepositoriesQuery, +} from './graphApiQueries.js' +import type { Repository, User } from './Model.js' export * from './Model.js' @@ -16,16 +20,17 @@ interface QueryUserRepositoriesResponse { hasNextPage: boolean } -export class Unauthorized { -} +export class Unauthorized {} export class GitHubApiClient { - constructor(private readonly ghAccessToken: string, - private readonly ghGraphApiUrl: string = 'https://api.github.com/graphql') { - } + constructor( + private readonly ghAccessToken: string, + private readonly ghGraphApiUrl: string = 'https://api.github.com/graphql', + ) {} async queryUser(): Promise { - const result = await this.internalDoGraphApiQuery(fetchUserQuery) + const result = + await this.internalDoGraphApiQuery(fetchUserQuery) return { login: result.data.viewer.login, // email: result.data.viewer.email.length ? result.data.viewer.email : undefined, @@ -39,7 +44,10 @@ export class GitHubApiClient { let hasNextPage = true let nextCursor: string | undefined = undefined do { - const response = await this.internalQueryUserRepositories(40, nextCursor) + const response = await this.internalQueryUserRepositories( + 40, + nextCursor, + ) hasNextPage = response.hasNextPage nextCursor = response.endCursor result.push(...response.repositories) @@ -47,8 +55,14 @@ export class GitHubApiClient { return result } - async queryLatestRelease(repoOwner: string, repoName: string): Promise { - const result = await this.internalDoGraphApiQuery(queryLatestReleaseQuery(repoOwner, repoName)) + async queryLatestRelease( + repoOwner: string, + repoName: string, + ): Promise { + const result = + await this.internalDoGraphApiQuery( + queryLatestReleaseQuery(repoOwner, repoName), + ) if (result.data.repository === null) { throw new Error(`${repoOwner}/${repoName} not found`) } @@ -64,41 +78,58 @@ export class GitHubApiClient { } } - private async internalQueryUserRepositories(reposPerPage: number, cursor?: string): Promise { - const result = await this.internalDoGraphApiQuery(queryUserRepositoriesQuery(reposPerPage, cursor)) + private async internalQueryUserRepositories( + reposPerPage: number, + cursor?: string, + ): Promise { + const result = + await this.internalDoGraphApiQuery( + queryUserRepositoriesQuery(reposPerPage, cursor), + ) const repositories: Array = [] for (const repo of result.data.viewer.repositories.nodes) { repositories.push({ owner: repo.owner.login, name: repo.name, languages: mapLanguageNodes(repo.languages.nodes), - latestRelease: repo.releases.nodes.length ? mapReleaseNode(repo.releases.nodes[0]) : undefined, + latestRelease: repo.releases.nodes.length + ? mapReleaseNode(repo.releases.nodes[0]) + : undefined, }) } - const {endCursor, hasNextPage} = result.data.viewer.repositories.pageInfo - return {repositories, hasNextPage, endCursor} + const { endCursor, hasNextPage } = + result.data.viewer.repositories.pageInfo + return { repositories, hasNextPage, endCursor } } - private async internalDoGraphApiQuery(query: string): Promise<{ data: T }> { + private async internalDoGraphApiQuery( + query: string, + ): Promise<{ data: T }> { const response = await fetch(this.ghGraphApiUrl, { method: 'POST', headers: { - 'Authorization': 'Bearer ' + this.ghAccessToken, + Authorization: 'Bearer ' + this.ghAccessToken, 'Content-Type': 'application/json', }, - body: JSON.stringify({query}), + body: JSON.stringify({ query }), }) if (response.status !== 200) { switch (response.status) { case 401: throw new Unauthorized() default: - throw new Error('internalDoGraphApiQuery bad gh graphql status code: ' + response.status) + throw new Error( + 'internalDoGraphApiQuery bad gh graphql status code: ' + + response.status, + ) } } const result = await response.json() if (!result.data) { - console.error('internalDoGraphApiQuery response', JSON.stringify(result, null, 4)) + console.error( + 'internalDoGraphApiQuery response', + JSON.stringify(result, null, 4), + ) throw new Error('internalDoGraphApiQuery bad gh graphql response') } console.log('internalDoGraphApiQuery', result) diff --git a/github/src/Model.ts b/github/src/Model.ts index 5d54200..0c5b83b 100644 --- a/github/src/Model.ts +++ b/github/src/Model.ts @@ -1,4 +1,4 @@ -import type {Architecture, OperatingSystem} from '@eighty4/install-template' +import type { Architecture, OperatingSystem } from '@eighty4/install-template' export type Language = 'C++' | 'C' | 'Go' | 'Rust' | 'Zig' diff --git a/github/src/graphApiQueries.ts b/github/src/graphApiQueries.ts index 25e9ac9..7c70ace 100644 --- a/github/src/graphApiQueries.ts +++ b/github/src/graphApiQueries.ts @@ -35,7 +35,10 @@ query { } }` -export const queryUserRepositoriesQuery = (reposPerPage: number, cursor?: string) => ` +export const queryUserRepositoriesQuery = ( + reposPerPage: number, + cursor?: string, +) => ` { viewer { repositories( diff --git a/github/src/graphApiTypes.ts b/github/src/graphApiTypes.ts index 178e512..19c640b 100644 --- a/github/src/graphApiTypes.ts +++ b/github/src/graphApiTypes.ts @@ -1,5 +1,5 @@ -import {resolveDistribution} from '@eighty4/install-template' -import type {Asset, Binary, Language, Release} from './Model.js' +import { resolveDistribution } from '@eighty4/install-template' +import type { Asset, Binary, Language, Release } from './Model.js' const compileBinaryLanguages = Object.freeze(['C', 'C++', 'Go', 'Rust', 'Zig']) @@ -76,16 +76,20 @@ export interface ReleaseAssetNode { export function mapLanguageNodes(nodes: Array): Array { return nodes .map(languageNode => languageNode.name) - .filter(language => compileBinaryLanguages.includes(language)) as Array + .filter(language => + compileBinaryLanguages.includes(language), + ) as Array } export function mapReleaseNode(release: ReleaseNode): Release { if (release.releaseAssets.pageInfo.hasNextPage) { - throw new Error(`release ${release.tagName} has more than 100 release assets and exceeding amount will not be processed bc paging is not yet supported`) + throw new Error( + `release ${release.tagName} has more than 100 release assets and exceeding amount will not be processed bc paging is not yet supported`, + ) } const binaries: Array = [] const otherAssets: Array = [] - for (const {name: filename, contentType} of release.releaseAssets.nodes) { + for (const { name: filename, contentType } of release.releaseAssets.nodes) { const distribution = resolveDistribution(filename, contentType) if (distribution) { binaries.push({ @@ -95,10 +99,10 @@ export function mapReleaseNode(release: ReleaseNode): Release { os: distribution.os, }) } else { - otherAssets.push({filename, contentType}) + otherAssets.push({ filename, contentType }) } } - const {createdAt, updatedAt, url} = release + const { createdAt, updatedAt, url } = release return { commitHash: release.tagCommit.abbreviatedOid, tag: release.tagName, diff --git a/github/tsconfig.json b/github/tsconfig.json index d5ce33e..a622ea5 100644 --- a/github/tsconfig.json +++ b/github/tsconfig.json @@ -1,15 +1,13 @@ { - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "lib", - "rootDir": "src" - }, - "include": [ - "src/**/*.ts" - ], - "references": [ - { - "path": "../template" - } - ] + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src" + }, + "include": ["src/**/*.ts"], + "references": [ + { + "path": "../template" + } + ] } diff --git a/lambdas/package.json b/lambdas/package.json index 248308b..6b62bbb 100644 --- a/lambdas/package.json +++ b/lambdas/package.json @@ -1,4 +1,4 @@ { - "name": "@eighty4/install-lambdas", - "type": "module" + "name": "@eighty4/install-lambdas", + "type": "module" } diff --git a/lambdas/routes/generated-scripts/lambda.js b/lambdas/routes/generated-scripts/lambda.js index 402eca7..db531d4 100644 --- a/lambdas/routes/generated-scripts/lambda.js +++ b/lambdas/routes/generated-scripts/lambda.js @@ -1,13 +1,16 @@ const generatedScripts = {} export async function GET(event) { - if (!event.headers['Authorization'] || !event.headers['Authorization'].startsWith('Bearer ')) { - return {statusCode: 401} + if ( + !event.headers['Authorization'] || + !event.headers['Authorization'].startsWith('Bearer ') + ) { + return { statusCode: 401 } } const accessToken = event.headers['Authorization'].substring(7) const userId = await fetchUserId(accessToken) if (!userId) { - return {statusCode: 401} + return { statusCode: 401 } } return { statusCode: 200, @@ -19,16 +22,22 @@ export async function GET(event) { } export async function POST(event) { - if (!event.headers['Authorization'] || !event.headers['Authorization'].startsWith('Bearer ')) { - return {statusCode: 401} + if ( + !event.headers['Authorization'] || + !event.headers['Authorization'].startsWith('Bearer ') + ) { + return { statusCode: 401 } } const accessToken = event.headers['Authorization'].substring(7) const userId = await fetchUserId(accessToken) if (!userId) { - return {statusCode: 401} + return { statusCode: 401 } } - if (!event.headers['Content-Type'] || !event.headers['Content-Type'].includes('application/json')) { - return {statusCode: 400} + if ( + !event.headers['Content-Type'] || + !event.headers['Content-Type'].includes('application/json') + ) { + return { statusCode: 400 } } if (!generatedScripts[userId]) { generatedScripts[userId] = [] @@ -36,14 +45,14 @@ export async function POST(event) { generatedScripts[userId].push(JSON.parse(event.body)) // todo validate // todo save to db - return {statusCode: 200} + return { statusCode: 200 } } // todo use client in @eighty4/install-github async function fetchUserId(accessToken) { const response = await fetch('https://api.github.com/user', { headers: { - 'Authorization': 'Bearer ' + accessToken, + Authorization: 'Bearer ' + accessToken, 'X-GitHub-Api-Version': '2022-11-28', }, }) @@ -53,7 +62,10 @@ async function fetchUserId(accessToken) { console.error('GET api.github.com/user unauthorized') return default: - throw new Error('GET api.github.com/user unexpected response status ' + response.status) + throw new Error( + 'GET api.github.com/user unexpected response status ' + + response.status, + ) } } return (await response.json()).id diff --git a/lambdas/routes/login/oauth/github/lambda.js b/lambdas/routes/login/oauth/github/lambda.js index be6f899..996b60e 100644 --- a/lambdas/routes/login/oauth/github/lambda.js +++ b/lambdas/routes/login/oauth/github/lambda.js @@ -3,7 +3,7 @@ export async function GET(event) { return { statusCode: 301, headers: { - 'Location': 'http://localhost:5711?login', + Location: 'http://localhost:5711?login', 'Set-Cookie': `ght=${accessToken}; Secure; SameSite=Strict; Path=/`, }, body: accessToken, @@ -11,19 +11,24 @@ export async function GET(event) { } async function fetchAccessToken(code) { - const response = await fetch('https://github.com/login/oauth/access_token', { - method: 'post', - headers: { - 'Content-Type': 'application/json', + const response = await fetch( + 'https://github.com/login/oauth/access_token', + { + method: 'post', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + client_id: process.env.GH_OAUTH_CLIENT_ID, + client_secret: process.env.GH_OAUTH_CLIENT_SECRET, + code, + }), }, - body: JSON.stringify({ - client_id: process.env.GH_OAUTH_CLIENT_ID, - client_secret: process.env.GH_OAUTH_CLIENT_SECRET, - code, - }), - }) + ) if (response.status !== 200) { - console.error(`github access token response ${response.status}: ${await response.text()}`) + console.error( + `github access token response ${response.status}: ${await response.text()}`, + ) throw new Error('github access token exchange failed') } const formData = await response.formData() @@ -35,7 +40,10 @@ async function fetchAccessToken(code) { for (const key in formData.keys()) { formDataStr += `${key}=${formData.get(key)}` } - console.error('github access token exchange form data missing access_token, form data has: ' + formDataStr) + console.error( + 'github access token exchange form data missing access_token, form data has: ' + + formDataStr, + ) throw new Error('github access token exchange failed') } } diff --git a/offline/package.json b/offline/package.json index ce9793a..9f0aede 100644 --- a/offline/package.json +++ b/offline/package.json @@ -1,21 +1,21 @@ { - "name": "@eighty4/install-offline", - "version": "0.0.1", - "private": true, - "author": "Adam McKee ", - "license": "BSD-3-Clause", - "type": "module", - "scripts": { - "start": "node --experimental-strip-types src/Offline.ts", - "start:watch": "node --experimental-strip-types --watch --watch-path ./ --watch-path ../github --watch-path ../template src/Offline.ts" - }, - "dependencies": { - "@eighty4/install-github": "workspace:*", - "@hono/node-server": "^1.13.8", - "hono": "^4.7.2" - }, - "devDependencies": { - "@types/node": "^22.13.5", - "typescript": "^5.7.3" - } + "name": "@eighty4/install-offline", + "version": "0.0.1", + "private": true, + "author": "Adam McKee ", + "license": "BSD-3-Clause", + "type": "module", + "scripts": { + "start": "node --experimental-strip-types src/Offline.ts", + "start:watch": "node --experimental-strip-types --watch --watch-path ./ --watch-path ../github --watch-path ../template src/Offline.ts" + }, + "dependencies": { + "@eighty4/install-github": "workspace:*", + "@hono/node-server": "^1.13.8", + "hono": "^4.7.2" + }, + "devDependencies": { + "@types/node": "^22.13.5", + "typescript": "^5.7.3" + } } diff --git a/offline/src/Offline.ts b/offline/src/Offline.ts index 116dcee..f961382 100644 --- a/offline/src/Offline.ts +++ b/offline/src/Offline.ts @@ -1,34 +1,37 @@ -import {serve} from '@hono/node-server' -import {Hono} from 'hono' -import {logger} from 'hono/logger' -import {handleGraphQuery} from './data.ts' +import { serve } from '@hono/node-server' +import { Hono } from 'hono' +import { logger } from 'hono/logger' +import { handleGraphQuery } from './data.ts' const HTTP_PORT = 7411 const app = new Hono() app.use(logger()) -app.post('/offline/github/graph', async (c) => { +app.post('/offline/github/graph', async c => { const json = await c.req.json() - return c.json({data: handleGraphQuery(json.query)}) + return c.json({ data: handleGraphQuery(json.query) }) }) -app.get('/offline/github/oauth', (c) => { +app.get('/offline/github/oauth', c => { const accessToken = '1234' // todo does Safari work with Secure and SameSite=Strict when the hostname isn't localhost? if (c.req.header('User-Agent')?.includes('Safari')) { c.res.headers.append('Set-Cookie', `ght=${accessToken}; Path=/`) } else { - c.res.headers.append('Set-Cookie', `ght=${accessToken}; Secure; SameSite=Strict; Path=/`) + c.res.headers.append( + 'Set-Cookie', + `ght=${accessToken}; Secure; SameSite=Strict; Path=/`, + ) } return c.redirect('http://localhost:5711?login', 302) }) const generatedScripts: Array = [] -app.get('/offline/api/generated-scripts', (c) => c.json(generatedScripts)) +app.get('/offline/api/generated-scripts', c => c.json(generatedScripts)) -app.post('/offline/api/generated-scripts', async (c) => { +app.post('/offline/api/generated-scripts', async c => { const generatedScript = await c.req.json() generatedScripts.push(generatedScript) console.log(JSON.stringify(generatedScript, null, 4)) diff --git a/offline/src/data.ts b/offline/src/data.ts index 128976a..4a8b794 100644 --- a/offline/src/data.ts +++ b/offline/src/data.ts @@ -1,4 +1,4 @@ -import type {Release, Repository} from '@eighty4/install-github' +import type { Release, Repository } from '@eighty4/install-github' import type { ReleaseAssetNode, ReleaseNode, @@ -30,17 +30,23 @@ function lookupViewerUserGraph(query: string): ViewerUserGraph | undefined { viewer: { login: 'eighty4', email: '', - avatarUrl: 'data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=', + avatarUrl: + 'data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=', id: '1234', }, } } } -function lookupRepositoryReleasesGraph(query: string): RepositoryReleasesGraph | undefined { - const matches = /repository\(owner:\s"(?[a-z0-9-]+)",\sname:\s"(?[a-z0-9-]+)"\)/.exec(query) +function lookupRepositoryReleasesGraph( + query: string, +): RepositoryReleasesGraph | undefined { + const matches = + /repository\(owner:\s"(?[a-z0-9-]+)",\sname:\s"(?[a-z0-9-]+)"\)/.exec( + query, + ) if (matches && matches.groups) { - const {owner, name} = matches.groups + const { owner, name } = matches.groups const repoKey = `${owner}/${name}` if (repositories[repoKey]) { return { @@ -50,12 +56,16 @@ function lookupRepositoryReleasesGraph(query: string): RepositoryReleasesGraph | } } -function lookupViewerRepositoriesWithLatestReleaseGraph(query: string): ViewerRepositoriesWithLatestReleaseGraph | undefined { +function lookupViewerRepositoriesWithLatestReleaseGraph( + query: string, +): ViewerRepositoriesWithLatestReleaseGraph | undefined { if (/^\s*{\s*viewer\s*{\s*repositories\(/.test(query)) { return { viewer: { repositories: { - nodes: Object.keys(repositories).map(repoName => repositories[repoName]).map(mapRepository), + nodes: Object.keys(repositories) + .map(repoName => repositories[repoName]) + .map(mapRepository), pageInfo: { endCursor: 'asdf', hasNextPage: false, @@ -73,7 +83,7 @@ function mapRepository(repository: Repository): RepositoryNode { login: repository.owner, }, languages: { - nodes: repository.languages.map(name => ({name})), + nodes: repository.languages.map(name => ({ name })), }, releases: { nodes: mapRelease(repository.latestRelease), @@ -98,19 +108,21 @@ function mapRelease(release?: Release): Array { name: asset.filename, }) } - return [{ - createdAt: release.createdAt, - updatedAt: release.updatedAt, - url: release.url, - tagCommit: {abbreviatedOid: 'bbb3b25'}, - tagName: release.tag, - releaseAssets: { - nodes: assets, - pageInfo: { - hasNextPage: false, + return [ + { + createdAt: release.createdAt, + updatedAt: release.updatedAt, + url: release.url, + tagCommit: { abbreviatedOid: 'bbb3b25' }, + tagName: release.tag, + releaseAssets: { + nodes: assets, + pageInfo: { + hasNextPage: false, + }, }, }, - }] + ] } } @@ -122,36 +134,44 @@ export const repositories: Record = { latestRelease: { commitHash: 'bbb3b25', createdAt: '2024-01-01', - binaries: [{ - arch: 'aarch64', - contentType: 'application/x-executable', - filename: 'maestro-linux-arm64', - os: 'Linux', - }, { - arch: 'x86_64', - contentType: 'application/x-executable', - filename: 'maestro-linux-amd64', - os: 'Linux', - }, { - arch: 'x86_64', - contentType: 'application/x-mach-binary', - filename: 'maestro-darwin-amd64', - os: 'MacOS', - }, { - arch: 'aarch64', - contentType: 'application/x-mach-binary', - filename: 'maestro-darwin-arm64', - os: 'MacOS', - }, { - arch: 'x86_64', - contentType: 'application/x-dosexec', - filename: 'maestro-windows-amd64.exe', - os: 'Windows', - }], - otherAssets: [{ - filename: 'README.md', - contentType: 'text/plain', - }], + binaries: [ + { + arch: 'aarch64', + contentType: 'application/x-executable', + filename: 'maestro-linux-arm64', + os: 'Linux', + }, + { + arch: 'x86_64', + contentType: 'application/x-executable', + filename: 'maestro-linux-amd64', + os: 'Linux', + }, + { + arch: 'x86_64', + contentType: 'application/x-mach-binary', + filename: 'maestro-darwin-amd64', + os: 'MacOS', + }, + { + arch: 'aarch64', + contentType: 'application/x-mach-binary', + filename: 'maestro-darwin-arm64', + os: 'MacOS', + }, + { + arch: 'x86_64', + contentType: 'application/x-dosexec', + filename: 'maestro-windows-amd64.exe', + os: 'Windows', + }, + ], + otherAssets: [ + { + filename: 'README.md', + contentType: 'text/plain', + }, + ], tag: '1.0.1', updatedAt: '2024-02-01', url: 'https://github.com/eighty4/maestro', @@ -164,11 +184,13 @@ export const repositories: Record = { latestRelease: { commitHash: 'bbb3b25', createdAt: '2024-01-01', - binaries: [{ - contentType: 'application/x-executable', - filename: 'maestro-linux', - os: 'Linux', - }], + binaries: [ + { + contentType: 'application/x-executable', + filename: 'maestro-linux', + os: 'Linux', + }, + ], otherAssets: [], tag: '1.0.1', updatedAt: '2024-02-01', diff --git a/offline/tsconfig.json b/offline/tsconfig.json index b256cad..8980d48 100644 --- a/offline/tsconfig.json +++ b/offline/tsconfig.json @@ -1,16 +1,14 @@ { - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "lib", - "rootDir": "src", - "allowImportingTsExtensions": true - }, - "include": [ - "src/**/*.ts" - ], - "references": [ - { - "path": "../github" - } - ] + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "lib", + "rootDir": "src", + "allowImportingTsExtensions": true + }, + "include": ["src/**/*.ts"], + "references": [ + { + "path": "../github" + } + ] } diff --git a/package.json b/package.json index 55ef087..c85c800 100644 --- a/package.json +++ b/package.json @@ -1,11 +1,15 @@ { - "name": "@eighty4/install-root", - "version": "0.0.1", - "private": true, - "author": "Adam McKee ", - "license": "BSD-3-Clause", - "packageManager": "pnpm@9.15.3", - "scripts": { - "clean": "./clean.sh" - } + "name": "@eighty4/install-root", + "version": "0.0.1", + "private": true, + "author": "Adam McKee ", + "license": "BSD-3-Clause", + "type": "module", + "packageManager": "pnpm@9.15.3", + "scripts": { + "clean": "./clean.sh" + }, + "devDependencies": { + "prettier": "^3.5.2" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b2945b..8e2d61d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,7 +6,11 @@ settings: importers: - .: {} + .: + devDependencies: + prettier: + specifier: ^3.5.2 + version: 3.5.2 e2e: devDependencies: @@ -499,6 +503,11 @@ packages: resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} engines: {node: ^10 || ^12 || >=14} + prettier@3.5.2: + resolution: {integrity: sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==} + engines: {node: '>=14'} + hasBin: true + rollup@4.34.8: resolution: {integrity: sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -929,6 +938,8 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + prettier@3.5.2: {} + rollup@4.34.8: dependencies: '@types/estree': 1.0.6 diff --git a/template/package.json b/template/package.json index e5ff154..0d55955 100644 --- a/template/package.json +++ b/template/package.json @@ -1,29 +1,29 @@ { - "name": "@eighty4/install-template", - "version": "0.0.1", - "author": "Adam McKee ", - "license": "BSD-3-Clause", - "type": "module", - "main": "lib/Template.js", - "types": "lib/Template.d.ts", - "scripts": { - "build": "tsc --build --clean && tsc --build", - "test": "pnpm test:unit && pnpm test:gold && pnpm test:integration", - "test:gold": "vitest run --mode=gold", - "test:gold:update": "UPDATE_GOLD=1 pnpm test:gold", - "test:integration": "./gold/integration_test.sh", - "test:unit": "vitest run" - }, - "devDependencies": { - "@types/node": "^22.13.5", - "typescript": "^5.7.3", - "vitest": "^3.0.6" - }, - "files": [ - "lib/**/*", - "src/**/*", - "gold/**/*.sh", - "package.json", - "tsconfig.json" - ] + "name": "@eighty4/install-template", + "version": "0.0.1", + "author": "Adam McKee ", + "license": "BSD-3-Clause", + "type": "module", + "main": "lib/Template.js", + "types": "lib/Template.d.ts", + "scripts": { + "build": "tsc --build --clean && tsc --build", + "test": "pnpm test:unit && pnpm test:gold && pnpm test:integration", + "test:gold": "vitest run --mode=gold", + "test:gold:update": "UPDATE_GOLD=1 pnpm test:gold", + "test:integration": "./gold/integration_test.sh", + "test:unit": "vitest run" + }, + "devDependencies": { + "@types/node": "^22.13.5", + "typescript": "^5.7.3", + "vitest": "^3.0.6" + }, + "files": [ + "lib/**/*", + "src/**/*", + "gold/**/*.sh", + "package.json", + "tsconfig.json" + ] } diff --git a/template/src/Distrubtions.ts b/template/src/Distrubtions.ts index ab3877a..e501cb7 100644 --- a/template/src/Distrubtions.ts +++ b/template/src/Distrubtions.ts @@ -1,11 +1,16 @@ // https://en.wikipedia.org/wiki/Uname#Examples export type Architecture = 'aarch64' | 'arm' | 'x86_64' -export const ARCHITECTURES: Readonly> = Object.freeze(['aarch64', 'arm', 'x86_64']) +export const ARCHITECTURES: Readonly> = Object.freeze([ + 'aarch64', + 'arm', + 'x86_64', +]) export type OperatingSystem = 'Linux' | 'MacOS' | 'Windows' -export const OPERATING_SYSTEMS: Readonly> = Object.freeze(['Linux', 'MacOS', 'Windows']) +export const OPERATING_SYSTEMS: Readonly> = + Object.freeze(['Linux', 'MacOS', 'Windows']) export function operatingSystemLabel(os: OperatingSystem): string { switch (os) { diff --git a/template/src/Generate.gold.ts b/template/src/Generate.gold.ts index f4da395..3a0ba32 100644 --- a/template/src/Generate.gold.ts +++ b/template/src/Generate.gold.ts @@ -1,7 +1,7 @@ -import {readFileSync, writeFileSync} from 'node:fs' -import {join} from 'node:path' -import {expect, test} from 'vitest' -import {generateScript, type GenerateScriptOptions} from './Generate.js' +import { readFileSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' +import { expect, test } from 'vitest' +import { generateScript, type GenerateScriptOptions } from './Generate.js' function generateScriptTest(goldFile: string, options: GenerateScriptOptions) { const goldPath = join('gold', 'scripts', goldFile) @@ -13,36 +13,45 @@ function generateScriptTest(goldFile: string, options: GenerateScriptOptions) { } } -test('generate maestro.sh', () => generateScriptTest('maestro.sh', { - binaryInstalls: [{ - installAs: 'maestro', - binaries: ['maestro-linux-amd64', 'maestro-linux-arm64', 'maestro-darwin-amd64', 'maestro-darwin-arm64', 'maestro-arm64.exe'], - }], - explicitArchitectures: {}, - repository: { - owner: 'eighty4', - name: 'maestro', - }, - resolvedDistributions: { - 'maestro-linux-amd64': { - arch: 'x86_64', - os: 'Linux', +test('generate maestro.sh', () => + generateScriptTest('maestro.sh', { + binaryInstalls: [ + { + installAs: 'maestro', + binaries: [ + 'maestro-linux-amd64', + 'maestro-linux-arm64', + 'maestro-darwin-amd64', + 'maestro-darwin-arm64', + 'maestro-arm64.exe', + ], + }, + ], + explicitArchitectures: {}, + repository: { + owner: 'eighty4', + name: 'maestro', }, - 'maestro-linux-arm64': { - arch: 'aarch64', - os: 'Linux', + resolvedDistributions: { + 'maestro-linux-amd64': { + arch: 'x86_64', + os: 'Linux', + }, + 'maestro-linux-arm64': { + arch: 'aarch64', + os: 'Linux', + }, + 'maestro-darwin-amd64': { + arch: 'x86_64', + os: 'MacOS', + }, + 'maestro-darwin-arm64': { + arch: 'aarch64', + os: 'MacOS', + }, + 'maestro-arm64.exe': { + arch: 'aarch64', + os: 'Windows', + }, }, - 'maestro-darwin-amd64': { - arch: 'x86_64', - os: 'MacOS', - }, - 'maestro-darwin-arm64': { - arch: 'aarch64', - os: 'MacOS', - }, - 'maestro-arm64.exe': { - arch: 'aarch64', - os: 'Windows', - }, - }, -})) + })) diff --git a/template/src/Generate.ts b/template/src/Generate.ts index b468059..bbf7ce2 100644 --- a/template/src/Generate.ts +++ b/template/src/Generate.ts @@ -1,5 +1,9 @@ -import type {Architecture, Distribution, OperatingSystem} from './Distrubtions.js' -import {getTemplateVersion} from './getTemplateVersion.js' +import type { + Architecture, + Distribution, + OperatingSystem, +} from './Distrubtions.js' +import { getTemplateVersion } from './getTemplateVersion.js' export interface Repository { owner: string @@ -49,13 +53,22 @@ function validateOptions(options: GenerateScriptOptions) { } else if (!options.binaryInstalls || !options.binaryInstalls.length) { throw new Error('options.binaryInstalls param is required') } else if (options.binaryInstalls.length !== 1) { - throw new Error('options.binaryInstalls only supports one binary install with an install script') + throw new Error( + 'options.binaryInstalls only supports one binary install with an install script', + ) } else { options.binaryInstalls.forEach((binaryInstall, i) => { if (!binaryInstall.installAs || !binaryInstall.installAs.length) { - throw new Error(`options.binaryInstallInstalls[${i}].installAs param is required`) - } else if (!binaryInstall.binaries || !Object.keys(binaryInstall.binaries).length) { - throw new Error(`options.binaryInstalls[${i}].binaries param is required`) + throw new Error( + `options.binaryInstallInstalls[${i}].installAs param is required`, + ) + } else if ( + !binaryInstall.binaries || + !Object.keys(binaryInstall.binaries).length + ) { + throw new Error( + `options.binaryInstalls[${i}].binaries param is required`, + ) } }) } @@ -66,7 +79,9 @@ interface DistributionSupport { oses: Array } -function collectDistributionSupport(options: GenerateScriptOptions): DistributionSupport { +function collectDistributionSupport( + options: GenerateScriptOptions, +): DistributionSupport { const resolvedDistributions = Object.values(options.resolvedDistributions) const explicitArchitectures = Object.values(options.explicitArchitectures) const architectures: Array = [] @@ -86,11 +101,14 @@ function collectDistributionSupport(options: GenerateScriptOptions): Distributio architectures.push(architecture) } } - return {architectures, oses} + return { architectures, oses } } -function collectBinaryDistributions(options: GenerateScriptOptions): Record { - const {binaryInstalls, explicitArchitectures, resolvedDistributions} = options +function collectBinaryDistributions( + options: GenerateScriptOptions, +): Record { + const { binaryInstalls, explicitArchitectures, resolvedDistributions } = + options const result: Record = {} for (const binaryInstall of binaryInstalls) { for (const binary of binaryInstall.binaries) { @@ -101,7 +119,9 @@ function collectBinaryDistributions(options: GenerateScriptOptions): Record) { chunks.push(` _filename="${filenames[0]}"`) if (filenames.length > 1) { for (const filename of filenames.splice(1)) { - chunks.push(` elif ${renderCheckDistribution(files[filename])}; then`) + chunks.push( + ` elif ${renderCheckDistribution(files[filename])}; then`, + ) chunks.push(` _filename="${filename}"`) } } @@ -297,6 +326,6 @@ ${chunks.join('\n')} }` } -function renderCheckDistribution({arch, os}: Distribution): string { +function renderCheckDistribution({ arch, os }: Distribution): string { return `test "$_cpu" = "${arch}" && test "$_os" = "${os}"` } diff --git a/template/src/Resolution.test.ts b/template/src/Resolution.test.ts index 4218ad0..dbfe495 100644 --- a/template/src/Resolution.test.ts +++ b/template/src/Resolution.test.ts @@ -1,29 +1,44 @@ -import {expect, test} from 'vitest' -import {resolveDistribution} from './Resolution.js' +import { expect, test } from 'vitest' +import { resolveDistribution } from './Resolution.js' test('resolveDistribution for Linux', () => { - expect(resolveDistribution('maestro-linux-386', 'application/x-executable')) - .toStrictEqual({arch: undefined, os: 'Linux'}) - expect(resolveDistribution('maestro-linux-amd64', 'application/x-executable')) - .toStrictEqual({arch: 'x86_64', os: 'Linux'}) - expect(resolveDistribution('maestro-linux-arm', 'application/x-executable')) - .toStrictEqual({arch: 'arm', os: 'Linux'}) - expect(resolveDistribution('maestro-linux-arm64', 'application/x-executable')) - .toStrictEqual({arch: 'aarch64', os: 'Linux'}) + expect( + resolveDistribution('maestro-linux-386', 'application/x-executable'), + ).toStrictEqual({ arch: undefined, os: 'Linux' }) + expect( + resolveDistribution('maestro-linux-amd64', 'application/x-executable'), + ).toStrictEqual({ arch: 'x86_64', os: 'Linux' }) + expect( + resolveDistribution('maestro-linux-arm', 'application/x-executable'), + ).toStrictEqual({ arch: 'arm', os: 'Linux' }) + expect( + resolveDistribution('maestro-linux-arm64', 'application/x-executable'), + ).toStrictEqual({ arch: 'aarch64', os: 'Linux' }) }) test('resolveDistribution for MacOS', () => { - expect(resolveDistribution('maestro-darwin-amd64', 'application/x-mach-binary')) - .toStrictEqual({arch: 'x86_64', os: 'MacOS'}) - expect(resolveDistribution('maestro-darwin-arm64', 'application/x-mach-binary')) - .toStrictEqual({arch: 'aarch64', os: 'MacOS'}) + expect( + resolveDistribution( + 'maestro-darwin-amd64', + 'application/x-mach-binary', + ), + ).toStrictEqual({ arch: 'x86_64', os: 'MacOS' }) + expect( + resolveDistribution( + 'maestro-darwin-arm64', + 'application/x-mach-binary', + ), + ).toStrictEqual({ arch: 'aarch64', os: 'MacOS' }) }) test('resolveDistribution for Windows', () => { - expect(resolveDistribution('maestro-windows-amd64', 'application/x-dosexec')) - .toStrictEqual({arch: 'x86_64', os: 'Windows'}) - expect(resolveDistribution('maestro-windows-arm', 'application/x-dosexec')) - .toStrictEqual({arch: 'arm', os: 'Windows'}) - expect(resolveDistribution('maestro-windows-arm64', 'application/x-dosexec')) - .toStrictEqual({arch: 'aarch64', os: 'Windows'}) + expect( + resolveDistribution('maestro-windows-amd64', 'application/x-dosexec'), + ).toStrictEqual({ arch: 'x86_64', os: 'Windows' }) + expect( + resolveDistribution('maestro-windows-arm', 'application/x-dosexec'), + ).toStrictEqual({ arch: 'arm', os: 'Windows' }) + expect( + resolveDistribution('maestro-windows-arm64', 'application/x-dosexec'), + ).toStrictEqual({ arch: 'aarch64', os: 'Windows' }) }) diff --git a/template/src/Resolution.ts b/template/src/Resolution.ts index 713eb26..a02d9f9 100644 --- a/template/src/Resolution.ts +++ b/template/src/Resolution.ts @@ -1,4 +1,8 @@ -import type {Architecture, Distribution, OperatingSystem} from './Distrubtions.js' +import type { + Architecture, + Distribution, + OperatingSystem, +} from './Distrubtions.js' const OS_BINARY_CONTENT_TYPES: Record = { 'application/x-executable': 'Linux', @@ -8,26 +12,32 @@ const OS_BINARY_CONTENT_TYPES: Record = { } const ARCH_LABELS: Record> = { - 'aarch64': ['arm64', 'aarch64'], - 'arm': ['arm'], - 'x86_64': ['amd64', 'x64', 'x86_64'], + aarch64: ['arm64', 'aarch64'], + arm: ['arm'], + x86_64: ['amd64', 'x64', 'x86_64'], } const OS_ARCHITECTURES: Record> = { - 'Linux': ['x86_64', 'aarch64', 'arm'], - 'MacOS': ['x86_64', 'aarch64'], - 'Windows': ['x86_64', 'aarch64', 'arm'], + Linux: ['x86_64', 'aarch64', 'arm'], + MacOS: ['x86_64', 'aarch64'], + Windows: ['x86_64', 'aarch64', 'arm'], } -export function resolveDistribution(filename: string, contentType: string): Distribution | undefined { +export function resolveDistribution( + filename: string, + contentType: string, +): Distribution | undefined { const os = resolveOperatingSystem(contentType) if (os) { const arch = resolveArchitecture(os, filename) - return {arch, os} + return { arch, os } } } -function resolveArchitecture(os: OperatingSystem, filename: string): Architecture | undefined { +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]) { @@ -38,6 +48,8 @@ function resolveArchitecture(os: OperatingSystem, filename: string): Architectur } } -function resolveOperatingSystem(contentType: string): OperatingSystem | undefined { +function resolveOperatingSystem( + contentType: string, +): OperatingSystem | undefined { return OS_BINARY_CONTENT_TYPES[contentType] } diff --git a/template/src/Template.ts b/template/src/Template.ts index 09912d2..118a69a 100644 --- a/template/src/Template.ts +++ b/template/src/Template.ts @@ -1,6 +1,18 @@ -export type {Architecture, Distribution, OperatingSystem} from './Distrubtions.js' -export {ARCHITECTURES, OPERATING_SYSTEMS, operatingSystemLabel} from './Distrubtions.js' -export {generateScript} from './Generate.js' -export type {GeneratedScript, GeneratedScriptSpec, GenerateScriptOptions} from './Generate.js' -export {getTemplateVersion} from './getTemplateVersion.js' -export {resolveDistribution} from './Resolution.js' +export type { + Architecture, + Distribution, + OperatingSystem, +} from './Distrubtions.js' +export { + ARCHITECTURES, + OPERATING_SYSTEMS, + operatingSystemLabel, +} from './Distrubtions.js' +export { generateScript } from './Generate.js' +export type { + GeneratedScript, + GeneratedScriptSpec, + GenerateScriptOptions, +} from './Generate.js' +export { getTemplateVersion } from './getTemplateVersion.js' +export { resolveDistribution } from './Resolution.js' diff --git a/template/src/getTemplateVersion.ts b/template/src/getTemplateVersion.ts index 0f1a53d..6857d21 100644 --- a/template/src/getTemplateVersion.ts +++ b/template/src/getTemplateVersion.ts @@ -1,4 +1,4 @@ -import packageJson from '../package.json' assert {type: 'json'} +import packageJson from '../package.json' assert { type: 'json' } export function getTemplateVersion(): string { return packageJson.version diff --git a/template/tsconfig.json b/template/tsconfig.json index b66eafa..d1fab9e 100644 --- a/template/tsconfig.json +++ b/template/tsconfig.json @@ -1,14 +1,10 @@ { - "extends": "../tsconfig.json", - "compilerOptions": { - "resolveJsonModule": true, - "outDir": "lib", - "rootDir": "src" - }, - "include": [ - "src/**/*.ts" - ], - "exclude": [ - "src/**/*.test.ts" - ] + "extends": "../tsconfig.json", + "compilerOptions": { + "resolveJsonModule": true, + "outDir": "lib", + "rootDir": "src" + }, + "include": ["src/**/*.ts"], + "exclude": ["src/**/*.test.ts"] } diff --git a/template/vitest.config.ts b/template/vitest.config.ts index 512acba..253e790 100644 --- a/template/vitest.config.ts +++ b/template/vitest.config.ts @@ -1,10 +1,10 @@ -import type {ConfigEnv} from 'vitest/config' +import type { ConfigEnv } from 'vitest/config' export default (configEnv: ConfigEnv) => { return { test: { include: resolveTestInclude(configEnv), - } + }, } } @@ -15,7 +15,9 @@ function resolveTestInclude(configEnv: ConfigEnv) { case 'gold': return 'src/**/*.gold.ts' default: - console.error('Run Vitest with an appropriate `--mode=test` or `--mode=gold` setting.') + console.error( + 'Run Vitest with an appropriate `--mode=test` or `--mode=gold` setting.', + ) process.exit(1) } } diff --git a/tsconfig.json b/tsconfig.json index e50072e..b952014 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,16 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "NodeNext", - "composite": true, - "forceConsistentCasingInFileNames": true, - "skipLibCheck": true, + "compilerOptions": { + "target": "ESNext", + "module": "NodeNext", + "composite": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, - /* Linting */ - "noFallthroughCasesInSwitch": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "strict": true, - "verbatimModuleSyntax": true, - }, + /* Linting */ + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strict": true, + "verbatimModuleSyntax": true + } }