github-up 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +183 -0
- package/dist/bin/cli.mjs +119 -0
- package/dist/browser/browser.d.ts +130 -0
- package/dist/browser/browser.js +1 -0
- package/dist/index.d.mts +190 -0
- package/dist/index.mjs +112 -0
- package/dist/site/index.html +133 -0
- package/package.json +114 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kaj Kowalski
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# github-up
|
|
2
|
+
|
|
3
|
+
[](https://npm.im/github-up)
|
|
4
|
+
[](https://pkg.pr.new/~/kjanat/github-up)
|
|
5
|
+
|
|
6
|
+
is github down (again) (maybe). booga check, you no doomscroll.
|
|
7
|
+
|
|
8
|
+
GitHub no work? maybe you. maybe GitHub. booga look two places same time:
|
|
9
|
+
|
|
10
|
+
- **www.githubstatus.com** - what GitHub admit to.
|
|
11
|
+
- **Downdetector** - what everyone else screaming about, usually first.
|
|
12
|
+
|
|
13
|
+
## or just watch the page
|
|
14
|
+
|
|
15
|
+
no install, no terminal. page poll itself, you stare:
|
|
16
|
+
|
|
17
|
+
[https://github-up.kjanat.dev/][site]
|
|
18
|
+
|
|
19
|
+
or pop it from the cli:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
github-up web
|
|
23
|
+
# or
|
|
24
|
+
github-up site
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## get booga
|
|
28
|
+
|
|
29
|
+
run direct, no install:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
bunx github-up status
|
|
33
|
+
# or
|
|
34
|
+
npx -y github-up status
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
or keep forever:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
bun install -g github-up
|
|
41
|
+
# or
|
|
42
|
+
npm install -g github-up
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
<details>
|
|
46
|
+
<summary>fresh builds (every commit)</summary>
|
|
47
|
+
|
|
48
|
+
every push + PR get published to [pkg.pr.new]. bot drop the url in the PR. run
|
|
49
|
+
any sha:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
bunx https://pkg.pr.new/kjanat/github-up@<sha> status # or npx / pnpx
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
</details>
|
|
56
|
+
|
|
57
|
+
## how use
|
|
58
|
+
|
|
59
|
+
### words for human
|
|
60
|
+
|
|
61
|
+
`status` give indicator, lil description, details from both place.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
github-up status
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### words for robot
|
|
68
|
+
|
|
69
|
+
json for scripts and little monitoring guys.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
github-up status --json
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### no words, just number
|
|
76
|
+
|
|
77
|
+
exit code always tell truth (see [the numbers](#the-numbers)), output or not.
|
|
78
|
+
slap `-q`/`--quiet` in CI when you only care about number.
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
github-up status -q
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### pick your place
|
|
85
|
+
|
|
86
|
+
default check uses GitHub Status and Downdetector. use subcommand or `--source`;
|
|
87
|
+
`--source` eat commas and repeats. Downdetector may report unavailable if
|
|
88
|
+
Cloudflare challenge automated checks.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
# subcommand
|
|
92
|
+
github-up github
|
|
93
|
+
github-up downdetector
|
|
94
|
+
|
|
95
|
+
# flag
|
|
96
|
+
github-up status --source github
|
|
97
|
+
github-up status -s downdetector
|
|
98
|
+
|
|
99
|
+
# many
|
|
100
|
+
github-up status --source github,downdetector
|
|
101
|
+
github-up status -s github -s downdetector
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### pick your part
|
|
105
|
+
|
|
106
|
+
only care about one thing? every GitHub component got shorthand flag.
|
|
107
|
+
`--component` eat commas and repeats too, same as `--source`.
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# is Actions cooked?
|
|
111
|
+
github-up status --actions
|
|
112
|
+
|
|
113
|
+
# many worry
|
|
114
|
+
github-up status --pr --pages
|
|
115
|
+
github-up github --component git,api
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
full set: `--actions`, `--api`, `--codespaces`, `--copilot`, `--git`,
|
|
119
|
+
`--issue`/`--issues`, `--packages`, `--pages`, `--pr`/`--prs`, `--webhooks`.
|
|
120
|
+
|
|
121
|
+
filter look at incident + component names, and severity come from what
|
|
122
|
+
actually matched: degraded = exit `1`, real outage = exit `2`, nothing
|
|
123
|
+
mention your thing = exit `0`. broad incident like "multiple GitHub services"
|
|
124
|
+
count for whatever you asked. downdetector no know components; its row pass
|
|
125
|
+
through whole.
|
|
126
|
+
|
|
127
|
+
## use in browser
|
|
128
|
+
|
|
129
|
+
browser-safe door, GitHub Status only (downdetector need real chromium, no work
|
|
130
|
+
in browser).
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import { checkGitHub } from "github-up/browser";
|
|
134
|
+
|
|
135
|
+
const result = await checkGitHub();
|
|
136
|
+
|
|
137
|
+
if (result.kind === "ok") {
|
|
138
|
+
console.log(result.summary.status.description);
|
|
139
|
+
console.log(result.summary.status.indicator);
|
|
140
|
+
console.log(result.summary.incidents);
|
|
141
|
+
console.log(result.summary.components);
|
|
142
|
+
} else {
|
|
143
|
+
console.error(result.reason);
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## the numbers
|
|
148
|
+
|
|
149
|
+
exit code = how bad. set every run (not just `--quiet`), worst source win.
|
|
150
|
+
|
|
151
|
+
| Code | Vibe | what happen |
|
|
152
|
+
| -----: | :-------- | :------------------------------------------------------ |
|
|
153
|
+
| **0** | all good | everything work. go back to your life. |
|
|
154
|
+
| **1** | meh | minor thing, or GitHub got live incident. |
|
|
155
|
+
| **2** | cooked | major/critical outage, or downdetector say GitHub down. |
|
|
156
|
+
| **21** | who knows | every source booga try was unreachable. |
|
|
157
|
+
|
|
158
|
+
source booga no reach = unknown, NOT down. `21` only when EVERY source dead, so
|
|
159
|
+
one flaky downdetector scrape no ruin your day.
|
|
160
|
+
|
|
161
|
+
## who do all this
|
|
162
|
+
|
|
163
|
+
booga no write flag parser. booga no write `--help`, tab-complete, json mode,
|
|
164
|
+
exit codes. all that [dreamcli]. booga just point at GitHub and shitpost.
|
|
165
|
+
|
|
166
|
+
you want make own CLI look this clean? -> [dreamcli]
|
|
167
|
+
|
|
168
|
+
## hack on it
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
bun install # setup
|
|
172
|
+
bun run build # build
|
|
173
|
+
bun test # test
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## license
|
|
177
|
+
|
|
178
|
+
[MIT][LICENSE] © 2026 Kaj Kowalski
|
|
179
|
+
|
|
180
|
+
[LICENSE]: https://github.com/kjanat/github-up/blob/master/LICENSE
|
|
181
|
+
[dreamcli]: https://github.com/kjanat/dreamcli
|
|
182
|
+
[pkg.pr.new]: https://pkg.pr.new
|
|
183
|
+
[site]: https://github-up.kjanat.dev
|
package/dist/bin/cli.mjs
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{ParseError as e,cli as t,command as n,flag as r,osc8 as i}from"@kjanat/dreamcli";import{createColors as a}from"ansispeck";import{spawn as o,spawnSync as s}from"node:child_process";import{existsSync as c,mkdtempSync as ee,readFileSync as te,rmSync as ne}from"node:fs";import{tmpdir as re}from"node:os";import{join as l,win32 as ie}from"node:path";import{setTimeout as u}from"node:timers/promises";import{argv as ae,platform as oe,stdout as se}from"node:process";import{createNodeAdapter as ce}from"@kjanat/dreamcli/runtime";var le=`github-up`,d=`0.1.0`,ue=`Is GitHub down? Probably...`,f=`https://github-up.kjanat.dev/`,de={type:`git`,url:`git+https://github.com/kjanat/github-up.git`};const p=[`github`,`downdetector`],m={github:`GitHub`,downdetector:`Downdetector`},h=[`actions`,`api`,`codespaces`,`copilot`,`git`,`issues`,`packages`,`pages`,`pr`,`webhooks`],fe={actions:/\bactions\b/i,api:/\bapi\b/i,codespaces:/\bcodespaces\b/i,copilot:/\bcopilot\b/i,git:/\bgit\b/i,issues:/\bissues\b/i,packages:/\bpackages\b/i,pages:/\bpages\b/i,pr:/\bpull requests?\b/i,webhooks:/\bwebhooks?\b/i},pe=/\b(?:all|many|most|multiple|several|some|various)\s+(?:github\s+)?(?:components|services|systems)\b/i;function g(e){return pe.test(e)}function _(e,t){let n=[];for(let r of t)fe[r].test(e)&&n.push(r);return n}function v(e,t){return t.size>0&&(_(e,t).length>0||g(e))}const y={none:0,minor:1,major:2,critical:2,unavailable:21},b=`https://www.githubstatus.com`,x=`https://downdetector.com/status/github/`,S=[`google-chrome-stable`,`google-chrome`,`chromium`,`brave`,`microsoft-edge-stable`,`microsoft-edge`],me=[`chrome.exe`,`msedge.exe`,`brave.exe`],he=[[`Google`,`Chrome`,`Application`,`chrome.exe`],[`Microsoft`,`Edge`,`Application`,`msedge.exe`],[`BraveSoftware`,`Brave-Browser`,`Application`,`brave.exe`],[`Chromium`,`Application`,`chrome.exe`]],ge=[`ProgramFiles`,`ProgramFiles(x86)`,`LOCALAPPDATA`],_e=[`/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`,`/Applications/Brave Browser.app/Contents/MacOS/Brave Browser`,`/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge`,`/Applications/Chromium.app/Contents/MacOS/Chromium`];function C(t,n){return r=>{let i=[];for(let a of String(r).split(`,`)){let r=a.trim();if(r.length===0)continue;let o=t.find(e=>e===r);if(o===void 0)throw new e(`Invalid value '${r}' for flag --${n}. Allowed: ${t.join(`, `)}`,{code:`INVALID_VALUE`,details:{flag:n,input:`--${n}`,value:r,allowed:t}});i.push(o)}return i}}const ve=C(p,`source`),ye=C(h,`component`),w=r.boolean().alias(`q`).describe(`Silent; exit code only`),T=r.custom(e=>new URL(String(e))).alias(`github-status-base`,{hidden:!0}).alias(`base`).alias(`b`).default(new URL(b)).env(`GITHUB_UP_GITHUB_STATUS_BASE`).describe(`Override GitHub status page base URL`),be=r.array(r.custom(ve)).alias(`s`).default([[...p]]).env(`GITHUB_UP_SOURCE`).env(`GITHUB_UP_SOURCES`).describe(`Data source(s) to check`),E=r.string().env(`GITHUB_UP_CHROME`).describe(`Path to a Chrome/Chromium binary`),xe=r.array(r.custom(ye)).alias(`c`).describe(`Only report incidents/components mentioning these component(s)`),D={actions:r.boolean().describe(`Shortcut for --component actions`),api:r.boolean().describe(`Shortcut for --component api`),codespaces:r.boolean().describe(`Shortcut for --component codespaces`),copilot:r.boolean().describe(`Shortcut for --component copilot`),git:r.boolean().describe(`Shortcut for --component git`),issues:r.boolean().alias(`issue`).describe(`Shortcut for --component issues`),packages:r.boolean().describe(`Shortcut for --component packages`),pages:r.boolean().describe(`Shortcut for --component pages`),pr:r.boolean().alias(`prs`).describe(`Shortcut for --component pr`),webhooks:r.boolean().describe(`Shortcut for --component webhooks`)};function O(e){let t=new Set(e.component.flat());for(let n of h)e[n]&&t.add(n);return t}function Se(e){return[...new Set(e.flat())]}function Ce(e){return{none:e.green,minor:e.yellow,major:e.red,critical:e.red}}function we(e,t){return e.indicator===`unavailable`?t.dim:Ce(t)[e.indicator]}function Te(e){return e.source===`github`?b:x}function k(e,t,n){if(n.length!==0){e.push(` ${t}:`);for(let t of n)e.push(` - ${t}`)}}function A(e,t){let n=a(t),r=we(e,n),i=[n.link(Te(e),n.underline(n.bold(r(m[e.source]))))];if(e.indicator===`unavailable`){let t=e.summaryText??`unknown error`;return i.push(` ${r(`Unavailable: ${t}`)}`),i.join(`
|
|
3
|
+
`)}if(e.source===`downdetector`){let t=e.summaryText??`No user-reported issues`;return i.push(` ${r(t)}`),i.join(`
|
|
4
|
+
`)}let o=e.summaryText??`All systems operational`;i.push(` ${r(o)}`);let s=e.incidents?.map(e=>`${e.name} (${e.status})`)??[];return k(i,s.length===1?`Active incident`:`Active incidents`,s),k(i,`Affected components`,e.affectedComponents?.map(e=>e.name)??[]),i.join(`
|
|
5
|
+
`)}function Ee(e){return e.source===`downdetector`?{source:`downdetector`,status:e.indicator===`unavailable`?`unavailable`:e.reportsOutage?`down`:`up`,details:e.summaryText}:{source:`github`,status:e.indicator===`none`?`up`:e.indicator,details:e.summaryText,incidents:e.incidents,affected:e.affectedComponents}}function De(e){return e.map(e=>Ee(e))}function Oe(e,t){if(t.jsonMode||!t.isTTY){t.json(De(e));return}t.log(e.map(e=>A(e,t.isTTY)).join(`
|
|
6
|
+
|
|
7
|
+
`))}function ke(e,t,n={}){let r=n.leadingBlank===!0?`
|
|
8
|
+
`:``;t.log(`${r}${A(e,t.isTTY)}`)}function Ae(e){if(e.jsonMode||!e.isTTY)return;let t=a(!0),n=t.link(f,t.underline(f));e.log(`\n${t.dim(`Watch the live status page: ${n}`)}`)}function je(e){return typeof e==`object`&&!!e&&`webSocketDebuggerUrl`in e&&typeof e.webSocketDebuggerUrl==`string`}function Me(e){return typeof e==`object`&&!!e&&`id`in e&&typeof e.id==`number`}function Ne(e){let t=new Map,n=0;return e.onmessage=e=>{let n=typeof e.data==`string`?e.data:e.data instanceof ArrayBuffer?new TextDecoder().decode(e.data):null;if(n===null)return;let r;try{r=JSON.parse(n)}catch{return}if(!Me(r))return;let i=t.get(r.id);typeof i==`function`&&(t.delete(r.id),i(r))},(r,i={})=>new Promise((a,o)=>{let s=++n,c=setTimeout(()=>{t.delete(s),o(Error(`CDP command '${r}' timed out`))},5e3);t.set(s,e=>{clearTimeout(c),a(e)}),e.send(JSON.stringify({id:s,method:r,params:i}))})}async function Pe(e,t){let n=await(await fetch(`${e}/json/new?about%3Ablank`,{method:`PUT`})).json();if(!je(n))return{ok:!1,error:`unexpected CDP target shape`};let r=new WebSocket(n.webSocketDebuggerUrl);await new Promise((e,t)=>{r.onopen=()=>e(),r.onerror=()=>t(Error(`WebSocket connection failed`)),r.onclose=()=>t(Error(`WebSocket closed before opening`))});let i=Ne(r);return await i(`Page.enable`),await i(`Page.navigate`,{url:t}),{ok:!0,send:i,close:()=>r.close()}}function j(e,t){for(let n of t){let t=s(e,[n]);if(t.status===0&&t.stdout){let e=t.stdout.toString().split(/\r?\n/)[0]?.trim();if(e)return e}}return null}function M(e){for(let t of e)if(t&&c(t))return t;return null}function Fe(){let e=[];for(let t of ge){let n=process.env[t];if(n)for(let t of he)e.push(ie.join(n,...t))}return e}function Ie(e){let t=e??process.env.GITHUB_UP_CHROME;return t?c(t)?t:null:process.platform===`win32`?j(`where.exe`,me)??M(Fe()):process.platform===`darwin`?j(`which`,S)??M(_e):j(`which`,S)}async function Le(e,t){let n=Date.now()+t;for(;Date.now()<n;){try{if((await fetch(`${e}/json/version`)).ok)return!0}catch{}await u(100)}return!1}async function Re(e,t){let n=l(e,`DevToolsActivePort`),r=Date.now()+t;for(;Date.now()<r;){try{let e=te(n,`utf8`).split(`
|
|
9
|
+
`)[0]?.trim(),t=Number(e);if(Number.isInteger(t)&&t>0)return t}catch{}await u(100)}return null}async function ze(e){let t;try{t=ee(l(re(),`github-up-`))}catch(e){return{ok:!1,error:`mkdtemp failed: ${e instanceof Error?e.message:String(e)}`}}let n=o(e,[`--headless=new`,`--disable-gpu`,`--no-sandbox`,`--disable-blink-features=AutomationControlled`,`--window-size=1920,1080`,`--user-data-dir=${t}`,`--remote-debugging-port=0`,`--user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36`,`about:blank`],{stdio:`ignore`}),r=new Promise(e=>{n.once(`error`,t=>e({failed:t.message})),n.once(`exit`,(t,n)=>e({failed:`Chrome exited before CDP came up (${n??t??`unknown`})`}))}),i=await Promise.race([Re(t,5e3),r]);if(typeof i!=`number`)return N(n,t),{ok:!1,error:i===null?`CDP endpoint never came up`:`Chrome launch failed: ${i.failed}`};let a=`http://localhost:${i}`;return await Le(a,5e3)?{ok:!0,browser:{proc:n,userDataDir:t,base:a}}:(N(n,t),{ok:!1,error:`CDP endpoint never came up`})}function N(e,t){process.platform===`win32`&&e.pid!==void 0?s(`taskkill`,[`/pid`,String(e.pid),`/t`,`/f`]):e.kill();try{ne(t,{recursive:!0,force:!0,maxRetries:5,retryDelay:100})}catch{}}const P=`({
|
|
10
|
+
title: document.title,
|
|
11
|
+
pogo: window.PogoConfig ?? null,
|
|
12
|
+
h1: document.querySelector('h1')?.innerText ?? null,
|
|
13
|
+
cfChallenge: document.title === 'Just a moment...'
|
|
14
|
+
|| document.querySelector('script[src*="/cdn-cgi/challenge-platform/"]') !== null
|
|
15
|
+
|| document.body?.innerText?.includes('Enable JavaScript and cookies to continue') === true,
|
|
16
|
+
})`;function Be(e){return typeof e==`object`&&!!e&&`result`in e&&typeof e.result==`object`&&e.result!==null&&`result`in e.result&&typeof e.result.result==`object`&&e.result.result!==null&&`value`in e.result.result&&typeof e.result.result.value==`string`}function F(e){if(typeof e!=`object`||!e||!(`title`in e)||typeof e.title!=`string`||`h1`in e&&e.h1!==null&&typeof e.h1!=`string`||`cfChallenge`in e&&e.cfChallenge!==void 0&&typeof e.cfChallenge!=`boolean`||!(`pogo`in e))return!1;let{pogo:t}=e;return t===null||!(typeof t!=`object`||`outage`in t&&t.outage!==void 0&&typeof t.outage!=`boolean`)}async function Ve(e,t){return await e(`Runtime.enable`),He(async t=>{let n=await e(`Runtime.evaluate`,{expression:`JSON.stringify(${t})`,returnByValue:!0});if(!Be(n))return null;try{return JSON.parse(n.result.result.value)}catch{return null}},t)}async function He(e,t){let n=Date.now()+t;for(;Date.now()<n;){let t=await e(P);if(F(t)&&t.cfChallenge===!0)return{kind:`cloudflare-challenge`};if(F(t)&&t.pogo!==null)return{kind:`status`,pogo:t.pogo,heading:t.h1};await u(700)}return null}const I=/possible problems/i;function L(e){return e!==null&&I.test(e)?e:void 0}const Ue=`
|
|
17
|
+
const WEBVIEW_WIDTH = 1920;
|
|
18
|
+
const WEBVIEW_HEIGHT = 1080;
|
|
19
|
+
const SNAPSHOT_TIMEOUT_MS = 20000;
|
|
20
|
+
const POLL_INTERVAL_MS = 700;
|
|
21
|
+
const CHROME_ARGV = ${JSON.stringify([`--headless=new`,`--disable-gpu`,`--no-sandbox`,`--disable-blink-features=AutomationControlled`,`--window-size=1920,1080`,`--user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36`])};
|
|
22
|
+
const POSSIBLE_PROBLEMS_PATTERN = ${I};
|
|
23
|
+
${L.toString()}
|
|
24
|
+
|
|
25
|
+
function webViewOptions(chromePath) {
|
|
26
|
+
if (chromePath || process.platform !== 'darwin') {
|
|
27
|
+
return {
|
|
28
|
+
width: WEBVIEW_WIDTH,
|
|
29
|
+
height: WEBVIEW_HEIGHT,
|
|
30
|
+
backend: {
|
|
31
|
+
type: 'chrome',
|
|
32
|
+
url: false,
|
|
33
|
+
argv: CHROME_ARGV,
|
|
34
|
+
...(chromePath ? { path: chromePath } : {}),
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return { width: WEBVIEW_WIDTH, height: WEBVIEW_HEIGHT };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isPogoSnapshot(value) {
|
|
43
|
+
if (typeof value !== 'object' || value === null) return false;
|
|
44
|
+
if (!('title' in value) || typeof value.title !== 'string') return false;
|
|
45
|
+
if ('h1' in value && value.h1 !== null && typeof value.h1 !== 'string') return false;
|
|
46
|
+
if ('cfChallenge' in value && value.cfChallenge !== undefined && typeof value.cfChallenge !== 'boolean') return false;
|
|
47
|
+
if (!('pogo' in value)) return false;
|
|
48
|
+
const { pogo } = value;
|
|
49
|
+
if (pogo === null) return true;
|
|
50
|
+
if (typeof pogo !== 'object') return false;
|
|
51
|
+
return !('outage' in pogo) || pogo.outage === undefined || typeof pogo.outage === 'boolean';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function pollPogoSnapshot(view) {
|
|
55
|
+
const deadline = Date.now() + SNAPSHOT_TIMEOUT_MS;
|
|
56
|
+
while (Date.now() < deadline) {
|
|
57
|
+
const snapshot = await view.evaluate(${JSON.stringify(P)}).catch(() => null);
|
|
58
|
+
if (isPogoSnapshot(snapshot) && snapshot.cfChallenge === true) {
|
|
59
|
+
return { kind: 'cloudflare-challenge' };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (
|
|
63
|
+
isPogoSnapshot(snapshot)
|
|
64
|
+
&& snapshot.pogo !== null
|
|
65
|
+
) {
|
|
66
|
+
return { kind: 'status', pogo: snapshot.pogo, heading: snapshot.h1 };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
await Bun.sleep(POLL_INTERVAL_MS);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function check(url, chromePath) {
|
|
76
|
+
const WebView = globalThis.Bun?.WebView;
|
|
77
|
+
if (typeof WebView !== 'function') {
|
|
78
|
+
return { ok: false, error: 'Bun.WebView is unavailable; upgrade Bun or run with Node' };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let view;
|
|
82
|
+
try {
|
|
83
|
+
view = new WebView(webViewOptions(chromePath));
|
|
84
|
+
} catch (error) {
|
|
85
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
await view.navigate(url);
|
|
90
|
+
const result = await pollPogoSnapshot(view);
|
|
91
|
+
if (result === null) return { ok: false, error: 'CF challenge not cleared in time' };
|
|
92
|
+
if (result.kind === 'cloudflare-challenge') return { ok: false, error: 'Cloudflare challenge page' };
|
|
93
|
+
if (result.pogo.outage === true) {
|
|
94
|
+
return { ok: true, down: true, reason: result.heading ?? 'outage reported' };
|
|
95
|
+
}
|
|
96
|
+
const note = detectPossibleProblemsNote(result.heading);
|
|
97
|
+
if (note !== undefined) {
|
|
98
|
+
return { ok: true, down: false, note };
|
|
99
|
+
}
|
|
100
|
+
return { ok: true, down: false };
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
103
|
+
} finally {
|
|
104
|
+
view?.close();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
self.onmessage = async (event) => {
|
|
109
|
+
try {
|
|
110
|
+
const { url, chromePath } = event.data;
|
|
111
|
+
self.postMessage({ type: 'result', result: await check(url, chromePath) });
|
|
112
|
+
} catch (error) {
|
|
113
|
+
self.postMessage({
|
|
114
|
+
type: 'error',
|
|
115
|
+
error: error instanceof Error ? error.message : String(error),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
`;function We(){let e=globalThis.Bun;return e===void 0?null:e}function R(){let e=We()?.WebView;return typeof e==`function`?e:null}function Ge(){return R()!==null}function Ke(){let e=URL.createObjectURL(new Blob([Ue],{type:`text/javascript`}));return new Worker(e,{type:`module`})}async function qe(e,t,n=45e3){let r;try{r=Ke()}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}try{return await new Promise(i=>{let a=setTimeout(()=>{r.terminate(),i({ok:!1,error:`Bun.WebView timed out`})},n);r.onmessage=e=>{clearTimeout(a),r.terminate();let t=e.data;if(t.type===`result`){i(t.result);return}i({ok:!1,error:t.error})},r.onerror=e=>{clearTimeout(a),r.terminate(),i({ok:!1,error:e.message})},r.postMessage({url:e,chromePath:t})})}finally{await u(0)}}async function Je(e,t){return R()===null?{ok:!1,error:`Bun.WebView is unavailable; upgrade Bun or run with Node`}:qe(e,t??process.env.GITHUB_UP_CHROME)}async function Ye(e){if(Ge())return Je(x,e);let t=Ie(e);if(t===null)return{ok:!1,error:`no Chrome/Chromium found; set GITHUB_UP_CHROME or pass --chrome <path>`};let n=await ze(t);if(!n.ok)return n;let{browser:{proc:r,userDataDir:i,base:a}}=n;try{let e=await Pe(a,x);if(!e.ok)return e;let t=await Ve(e.send,2e4);if(e.close(),t===null)return{ok:!1,error:`CF challenge not cleared in time`};if(t.kind===`cloudflare-challenge`)return{ok:!1,error:`Cloudflare challenge page`};if(t.pogo.outage===!0)return{ok:!0,down:!0,reason:t.heading??`outage reported`};let n=L(t.heading);return n===void 0?{ok:!0,down:!1}:{ok:!0,down:!1,note:n}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}finally{N(r,i)}}const z=`/api/v2`,Xe={components(){return`${z}/components.json`},Incidents:{all(){return`${z}/incidents.json`},unresolved(){return`${z}/incidents/unresolved.json`}},ScheduledMaintenances:{active(){return`${z}/scheduled-maintenances/active.json`},all(){return`${z}/scheduled-maintenances.json`},upcoming(){return`${z}/scheduled-maintenances/upcoming.json`}},status(){return`${z}/status.json`},subscriber(e){return`${z}/subscribers/${encodeURIComponent(e)}.json`},subscribers(){return`${z}/subscribers.json`},summary(){return`${z}/summary.json`}};function B(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function V(e){return B(e)&&typeof e.name==`string`&&typeof e.status==`string`}function Ze(e){return B(e)&&typeof e.indicator==`string`&&typeof e.description==`string`}function Qe(e){return V(e)&&B(e)&&typeof e.impact==`string`}function $e(e){return B(e)&&Ze(e.status)&&Array.isArray(e.components)&&e.components.every(V)&&Array.isArray(e.incidents)&&e.incidents.every(Qe)}async function et(e){try{let t=await e.text(),n=t.length>0?`: ${t}`:``;return`Request failed with status code ${e.status}${n}`}catch{let{status:t,statusText:n}=e;return`Request failed with status code ${t}: ${n}`}}async function tt(e=b){try{let t=await fetch(new URL(Xe.summary(),e));if(!t.ok){let{headers:e}=t;return{headers:e,kind:`unknown`,reason:await et(t)}}let n=await t.json();return $e(n)?{headers:t.headers,kind:`ok`,summary:n}:{headers:t.headers,kind:`unknown`,reason:`unexpected response shape from status API`}}catch(e){return{kind:`unknown`,reason:e instanceof Error?e.message:String(e)}}}const H={none:0,minor:1,major:2,critical:3},nt={none:`All Systems Operational`,minor:`Minor Service Outage`,major:`Major Service Outage`,critical:`Critical Service Outage`};function rt(e){return typeof e==`string`&&e in H}function it(e){return rt(e)?e:`critical`}function U(e,t){return H[t]>H[e]?t:e}function at(e){switch(e){case`operational`:return`none`;case`degraded_performance`:case`under_maintenance`:return`minor`;case`partial_outage`:case`major_outage`:return`major`;default:return`major`}}function ot(e){switch(e){case`degraded_performance`:case`partial_outage`:case`major_outage`:return!0;default:return!1}}function W(e){let t=`none`,n=0;for(let r of e)ot(r.status)&&(n+=1),t=U(t,at(r.status));return n>=3?U(t,`major`):t}function st(e,t=[]){return U(it(e),W(t))}function ct(e){return nt[e]}function lt(e){return e===`operational`||e===`degraded_performance`||e===`partial_outage`||e===`major_outage`||e===`under_maintenance`?e:`major_outage`}async function G(e){let t=await tt(e);if(t.kind===`unknown`)return{source:`github`,indicator:`unavailable`,summaryText:t.reason,incidents:null,affectedComponents:null};let n=t.summary.status.indicator,r=st(n,t.summary.components),i=r===n?t.summary.status.description:`${ct(r)} (reported ${String(n)})`,a=t.summary.components.filter(e=>e.status!==`operational`);return{source:`github`,indicator:r,summaryText:i,incidents:t.summary.incidents.length>0?t.summary.incidents.map(e=>({name:e.name,status:e.status})):null,affectedComponents:a.length>0?a.map(e=>({name:e.name,status:lt(e.status)})):null}}async function K(e){let t=await Ye(e);return t.ok?t.down?{source:`downdetector`,indicator:`major`,summaryText:t.reason,reportsOutage:!0}:{source:`downdetector`,indicator:t.note===void 0?`none`:`minor`,summaryText:t.note??null,reportsOutage:!1}:{source:`downdetector`,indicator:`unavailable`,summaryText:t.error,reportsOutage:!1}}async function ut(e,t,n){switch(e){case`github`:return G(t);case`downdetector`:return K(n)}}function q(e,t,n){let r=!1;for(let i of t){g(i.name)&&(r=!0);for(let t of _(i.name,n))e.add(t)}return r}function dt(e,t,n){let r=new Set,i=q(r,e,n),a=q(r,t,n);if(i||a)for(let e of n)r.add(e);return[...n].filter(e=>r.has(e))}function J(e,t){if(e.source!==`github`||t.size===0||e.indicator===`unavailable`)return e;let n=e.incidents?.filter(e=>v(e.name,t))??[],r=e.affectedComponents?.filter(e=>v(e.name,t))??[],i=n.length+r.length,a=dt(n,r,t),o=W(r);return{source:`github`,indicator:n.length>0?U(o,`minor`):o,summaryText:i>0?`${i} report${i===1?``:`s`} affecting ${a.join(`, `)}`:`No incidents reported for ${[...t].join(`, `)}`,incidents:n.length>0?n:null,affectedComponents:r.length>0?r:null}}function ft(e){let t=y[e.indicator];return e.source===`github`&&e.incidents&&e.incidents.length>0?Math.max(t,y.minor):t}function Y(e){let t=e.filter(e=>e.indicator!==`unavailable`);return t.length===0?e.length===0?y.none:y.unavailable:t.reduce((e,t)=>Math.max(e,ft(t)),y.none)}function pt(e){return[...e].sort((e,t)=>e.source.localeCompare(t.source))}function mt(e,t=oe){switch(t){case`darwin`:return{command:`open`,args:[e]};case`win32`:return{command:`cmd`,args:[`/c`,`start`,``,e]};default:return{command:`xdg-open`,args:[e]}}}function ht(e){let{args:t,command:n}=mt(e);return new Promise((e,r)=>{let i=o(n,[...t],{detached:!0,stdio:`ignore`});i.once(`error`,r),i.once(`spawn`,()=>{i.unref(),e()})})}function X(e){return e.flag(`component`,xe).flag(`actions`,D.actions).flag(`api`,D.api).flag(`codespaces`,D.codespaces).flag(`copilot`,D.copilot).flag(`git`,D.git).flag(`issues`,D.issues).flag(`packages`,D.packages).flag(`pages`,D.pages).flag(`pr`,D.pr).flag(`webhooks`,D.webhooks)}async function Z(e,t,n,r){if(r.isTTY&&!r.jsonMode&&!n){let n=await gt(e,t,r);r.setExitCode(Y(n));return}_t(vt(await Promise.all(e.map(e=>e.run())),t),n,r)}function Q(e){return`Checking ${[...e].map(e=>m[e]).join(`, `)}…`}async function gt(e,t,n){let r=new Set(e.map(e=>e.source)),i=[],a=new Map(e.map((e,t)=>[t,e.run().then(t=>({source:e.source,row:t}))])),o=n.spinner(Q(r)),s=0;try{for(;a.size>0;){let e=await Promise.race([...a].map(([e,t])=>t.then(t=>({index:e,...t}))));a.delete(e.index),r.delete(e.source);let c=J(e.row,t);i.push(c),o.stop(),ke(c,n,{leadingBlank:s>0}),s+=1,r.size>0&&(o=n.spinner(Q(r)))}}catch(e){throw o.stop(),e}return Ae(n),i}function _t(e,t,n){t||Oe(pt(e),n),n.setExitCode(Y(e))}function vt(e,t){return t.size===0?e:e.map(e=>J(e,t))}const yt=X(n(`status`).description(`Check GitHub status across GitHub and Downdetector`).example(`status`,`Check all sources`).example(`status --source github`,`Check only GitHub`).example(`status --actions`,`Only report trouble mentioning Actions`).example(`status --json`,`Emit machine-readable source rows`)).flag(`githubStatusBase`,T).flag(`chrome`,E).flag(`quiet`,w).flag(`source`,be).action(async({flags:e,out:t})=>{let{source:n,githubStatusBase:r,chrome:i,quiet:a}=e;await Z(Se(n).map(e=>({source:e,run:()=>ut(e,r,i)})),O(e),a,t)}),bt=X(n(`github`).description(`Check only ${m.github}`).example(`github`,`Check only ${m.github}`).example(`github --component actions`,`Only report trouble mentioning Actions`)).flag(`githubStatusBase`,T).flag(`quiet`,w).action(async({flags:e,out:t})=>{await Z([{source:`github`,run:()=>G(e.githubStatusBase)}],O(e),e.quiet,t)}),xt=n(`downdetector`).description(`Check only ${m.downdetector}`).example(`downdetector`,`Check only ${m.downdetector}`).flag(`chrome`,E).flag(`quiet`,w).action(async({flags:e,out:t})=>{await Z([{source:`downdetector`,run:()=>K(e.chrome)}],new Set,e.quiet,t)});function St(e=ht){return n(`web`).alias(`site`).description(`Open the live status page`).example(`web`,`Open the live status page in your browser`).action(async({out:t})=>{await e(f),t.log(`Opening ${f}`)})}const Ct=St(),$=de.url.replace(/^git\+/,``).replace(/\.git$/,``),wt=t(le).version(d).description(ue).links({name:$,version:`${$}/releases/tag/v${d}`}).default(yt,{route:!0}).command(bt).command(xt).command(Ct).completions(),Tt=new Set([`--help`,`-h`]);function Et(e){return e.some(e=>Tt.has(e))}function Dt(e,t=!1){return`\nNo terminal? Watch the live status page: ${t?i(e,e):e}\n`}if(import.meta.main){let e=ce(),t=Et(ae.slice(2));wt.run({help:{width:se.columns},adapter:{...e,exit:n=>(t&&e.stdout(Dt(f,e.isTTY)),e.exit(n))}})}export{};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
//#region node_modules/statuspage.io/dist/interfaces/Result.d.ts
|
|
2
|
+
declare enum IncidentImpact {
|
|
3
|
+
Critical = "critical",
|
|
4
|
+
Major = "major",
|
|
5
|
+
Minor = "minor",
|
|
6
|
+
None = "none"
|
|
7
|
+
}
|
|
8
|
+
declare enum IncidentStatus {
|
|
9
|
+
Identified = "identified",
|
|
10
|
+
Investigating = "investigating",
|
|
11
|
+
Monitoring = "monitoring",
|
|
12
|
+
Postmortem = "postmortem",
|
|
13
|
+
Resolved = "resolved"
|
|
14
|
+
}
|
|
15
|
+
declare enum MaintenanceStatus {
|
|
16
|
+
Completed = "completed",
|
|
17
|
+
InProgress = "in_progress",
|
|
18
|
+
Scheduled = "scheduled",
|
|
19
|
+
Verifying = "verifying"
|
|
20
|
+
}
|
|
21
|
+
interface Component {
|
|
22
|
+
created_at: string;
|
|
23
|
+
description: null | string;
|
|
24
|
+
group: boolean;
|
|
25
|
+
group_id: null | string;
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
only_show_if_degraded: boolean;
|
|
29
|
+
page_id: string;
|
|
30
|
+
position: number;
|
|
31
|
+
showcase: boolean;
|
|
32
|
+
start_date: null | string;
|
|
33
|
+
status: string;
|
|
34
|
+
updated_at: string;
|
|
35
|
+
}
|
|
36
|
+
interface Components extends Page {
|
|
37
|
+
components: Component[];
|
|
38
|
+
}
|
|
39
|
+
interface Incident {
|
|
40
|
+
created_at: string;
|
|
41
|
+
id: string;
|
|
42
|
+
impact: IncidentImpact;
|
|
43
|
+
incident_updates: IncidentUpdate[];
|
|
44
|
+
monitoring_at: null | string;
|
|
45
|
+
name: string;
|
|
46
|
+
page_id: string;
|
|
47
|
+
resolved_at: null | string;
|
|
48
|
+
shortlink: string;
|
|
49
|
+
status: IncidentStatus;
|
|
50
|
+
updated_at: string;
|
|
51
|
+
}
|
|
52
|
+
interface Incidents extends Page {
|
|
53
|
+
incidents: Incident[];
|
|
54
|
+
}
|
|
55
|
+
interface IncidentUpdate {
|
|
56
|
+
body: string;
|
|
57
|
+
created_at: string;
|
|
58
|
+
display_at: string;
|
|
59
|
+
id: string;
|
|
60
|
+
incident_id: string;
|
|
61
|
+
status: IncidentStatus;
|
|
62
|
+
updated_at: string;
|
|
63
|
+
}
|
|
64
|
+
interface Page {
|
|
65
|
+
page: {
|
|
66
|
+
id: string;
|
|
67
|
+
name: string;
|
|
68
|
+
time_zone: string;
|
|
69
|
+
updated_at: string;
|
|
70
|
+
url: string;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
type ScheduledMaintenance = {
|
|
74
|
+
scheduled_for: string;
|
|
75
|
+
scheduled_until: string;
|
|
76
|
+
status: MaintenanceStatus;
|
|
77
|
+
} & Omit<Incident, 'status'>;
|
|
78
|
+
interface ScheduledMaintenances extends Page {
|
|
79
|
+
scheduled_maintenances: ScheduledMaintenance[];
|
|
80
|
+
}
|
|
81
|
+
interface Status extends Page {
|
|
82
|
+
status: {
|
|
83
|
+
description: string;
|
|
84
|
+
indicator: string;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
type Summary = Components & Incidents & ScheduledMaintenances & Status;
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/lib/types.d.ts
|
|
90
|
+
/** String values of `statuspage.io`'s {@linkcode IncidentImpact} enum. */
|
|
91
|
+
type IncidentImpactValue = `${IncidentImpact}`;
|
|
92
|
+
/** String values of `statuspage.io`'s {@linkcode IncidentStatus} enum. */
|
|
93
|
+
type IncidentStatusValue = `${IncidentStatus}`;
|
|
94
|
+
/** The closed set of component statuses `statuspage.io` emits.
|
|
95
|
+
*
|
|
96
|
+
* Hand-rolled because upstream types {@linkcode Component["status"]} as `string`. */
|
|
97
|
+
type ComponentStatus = 'operational' | 'degraded_performance' | 'partial_outage' | 'major_outage' | 'under_maintenance' | Component['status'];
|
|
98
|
+
/** The result of a status check. */
|
|
99
|
+
type Result = {
|
|
100
|
+
/** Headers from the response, included when available for debugging purposes. */
|
|
101
|
+
headers: Headers;
|
|
102
|
+
/** A discriminant property that indicates the type of result.
|
|
103
|
+
* - `'ok'` for a successful summary. */
|
|
104
|
+
kind: 'ok';
|
|
105
|
+
/** The summary of the status page, included when the result is 'ok'. @see {@linkcode Summary} */
|
|
106
|
+
summary: Summary;
|
|
107
|
+
} | {
|
|
108
|
+
/** Headers from the response, included when available for debugging purposes. */
|
|
109
|
+
headers?: Headers;
|
|
110
|
+
/** A discriminant property that indicates the type of result.
|
|
111
|
+
* - `'unknown'` for an unknown state. */
|
|
112
|
+
kind: 'unknown';
|
|
113
|
+
/** The reason for the unknown state. */
|
|
114
|
+
reason: string;
|
|
115
|
+
};
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/lib/github.d.ts
|
|
118
|
+
/** Checks the status of GitHub's services by querying their Statuspage API.
|
|
119
|
+
*
|
|
120
|
+
* @param baseUrl - Optional base URL for the GitHub Statuspage API.
|
|
121
|
+
* @returns A promise that resolves to a Result object containing either the summary of the status or an error reason.
|
|
122
|
+
*/
|
|
123
|
+
declare function check(baseUrl?: string | URL): Promise<Result>;
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/browser.d.ts
|
|
126
|
+
declare const _default: {
|
|
127
|
+
checkGitHub: typeof check;
|
|
128
|
+
};
|
|
129
|
+
//#endregion
|
|
130
|
+
export { type Component, type ComponentStatus, type Incident, type IncidentImpactValue, type IncidentStatusValue, type Result, type Summary, check as checkGitHub, _default as default };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=`/api/v2`,t={components(){return`${e}/components.json`},Incidents:{all(){return`${e}/incidents.json`},unresolved(){return`${e}/incidents/unresolved.json`}},ScheduledMaintenances:{active(){return`${e}/scheduled-maintenances/active.json`},all(){return`${e}/scheduled-maintenances.json`},upcoming(){return`${e}/scheduled-maintenances/upcoming.json`}},status(){return`${e}/status.json`},subscriber(t){return`${e}/subscribers/${encodeURIComponent(t)}.json`},subscribers(){return`${e}/subscribers.json`},summary(){return`${e}/summary.json`}};function n(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function r(e){return n(e)&&typeof e.name==`string`&&typeof e.status==`string`}function i(e){return n(e)&&typeof e.indicator==`string`&&typeof e.description==`string`}function a(e){return r(e)&&n(e)&&typeof e.impact==`string`}function o(e){return n(e)&&i(e.status)&&Array.isArray(e.components)&&e.components.every(r)&&Array.isArray(e.incidents)&&e.incidents.every(a)}async function s(e){try{let t=await e.text(),n=t.length>0?`: ${t}`:``;return`Request failed with status code ${e.status}${n}`}catch{let{status:t,statusText:n}=e;return`Request failed with status code ${t}: ${n}`}}async function c(e=`https://www.githubstatus.com`){try{let n=await fetch(new URL(t.summary(),e));if(!n.ok){let{headers:e}=n;return{headers:e,kind:`unknown`,reason:await s(n)}}let r=await n.json();return o(r)?{headers:n.headers,kind:`ok`,summary:r}:{headers:n.headers,kind:`unknown`,reason:`unexpected response shape from status API`}}catch(e){return{kind:`unknown`,reason:e instanceof Error?e.message:String(e)}}}var l={checkGitHub:c};export{c as checkGitHub,l as default};
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
//#region src/lib/constants.d.ts
|
|
2
|
+
/** Mapping of status levels to their corresponding exit codes. */
|
|
3
|
+
declare const EXIT_CODES: {
|
|
4
|
+
readonly none: 0;
|
|
5
|
+
readonly minor: 1;
|
|
6
|
+
readonly major: 2;
|
|
7
|
+
readonly critical: 2;
|
|
8
|
+
readonly unavailable: 21;
|
|
9
|
+
};
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region node_modules/statuspage.io/dist/interfaces/Result.d.ts
|
|
12
|
+
declare enum IncidentImpact {
|
|
13
|
+
Critical = "critical",
|
|
14
|
+
Major = "major",
|
|
15
|
+
Minor = "minor",
|
|
16
|
+
None = "none"
|
|
17
|
+
}
|
|
18
|
+
declare enum IncidentStatus {
|
|
19
|
+
Identified = "identified",
|
|
20
|
+
Investigating = "investigating",
|
|
21
|
+
Monitoring = "monitoring",
|
|
22
|
+
Postmortem = "postmortem",
|
|
23
|
+
Resolved = "resolved"
|
|
24
|
+
}
|
|
25
|
+
declare enum MaintenanceStatus {
|
|
26
|
+
Completed = "completed",
|
|
27
|
+
InProgress = "in_progress",
|
|
28
|
+
Scheduled = "scheduled",
|
|
29
|
+
Verifying = "verifying"
|
|
30
|
+
}
|
|
31
|
+
interface Component {
|
|
32
|
+
created_at: string;
|
|
33
|
+
description: null | string;
|
|
34
|
+
group: boolean;
|
|
35
|
+
group_id: null | string;
|
|
36
|
+
id: string;
|
|
37
|
+
name: string;
|
|
38
|
+
only_show_if_degraded: boolean;
|
|
39
|
+
page_id: string;
|
|
40
|
+
position: number;
|
|
41
|
+
showcase: boolean;
|
|
42
|
+
start_date: null | string;
|
|
43
|
+
status: string;
|
|
44
|
+
updated_at: string;
|
|
45
|
+
}
|
|
46
|
+
interface Components extends Page {
|
|
47
|
+
components: Component[];
|
|
48
|
+
}
|
|
49
|
+
interface Incident {
|
|
50
|
+
created_at: string;
|
|
51
|
+
id: string;
|
|
52
|
+
impact: IncidentImpact;
|
|
53
|
+
incident_updates: IncidentUpdate[];
|
|
54
|
+
monitoring_at: null | string;
|
|
55
|
+
name: string;
|
|
56
|
+
page_id: string;
|
|
57
|
+
resolved_at: null | string;
|
|
58
|
+
shortlink: string;
|
|
59
|
+
status: IncidentStatus;
|
|
60
|
+
updated_at: string;
|
|
61
|
+
}
|
|
62
|
+
interface Incidents extends Page {
|
|
63
|
+
incidents: Incident[];
|
|
64
|
+
}
|
|
65
|
+
interface IncidentUpdate {
|
|
66
|
+
body: string;
|
|
67
|
+
created_at: string;
|
|
68
|
+
display_at: string;
|
|
69
|
+
id: string;
|
|
70
|
+
incident_id: string;
|
|
71
|
+
status: IncidentStatus;
|
|
72
|
+
updated_at: string;
|
|
73
|
+
}
|
|
74
|
+
interface Page {
|
|
75
|
+
page: {
|
|
76
|
+
id: string;
|
|
77
|
+
name: string;
|
|
78
|
+
time_zone: string;
|
|
79
|
+
updated_at: string;
|
|
80
|
+
url: string;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
type ScheduledMaintenance = {
|
|
84
|
+
scheduled_for: string;
|
|
85
|
+
scheduled_until: string;
|
|
86
|
+
status: MaintenanceStatus;
|
|
87
|
+
} & Omit<Incident, 'status'>;
|
|
88
|
+
interface ScheduledMaintenances extends Page {
|
|
89
|
+
scheduled_maintenances: ScheduledMaintenance[];
|
|
90
|
+
}
|
|
91
|
+
interface Status extends Page {
|
|
92
|
+
status: {
|
|
93
|
+
description: string;
|
|
94
|
+
indicator: string;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
type Summary = Components & Incidents & ScheduledMaintenances & Status;
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/lib/types.d.ts
|
|
100
|
+
/** String values of `statuspage.io`'s {@linkcode IncidentImpact} enum. */
|
|
101
|
+
type IncidentImpactValue = `${IncidentImpact}`;
|
|
102
|
+
/** String values of `statuspage.io`'s {@linkcode IncidentStatus} enum. */
|
|
103
|
+
type IncidentStatusValue = `${IncidentStatus}`;
|
|
104
|
+
/** Status for the state of a service.
|
|
105
|
+
* - `'unavailable'` when a source can't be reached. */
|
|
106
|
+
type Indicator = IncidentImpactValue | 'unavailable';
|
|
107
|
+
/** The closed set of component statuses `statuspage.io` emits.
|
|
108
|
+
*
|
|
109
|
+
* Hand-rolled because upstream types {@linkcode Component["status"]} as `string`. */
|
|
110
|
+
type ComponentStatus = 'operational' | 'degraded_performance' | 'partial_outage' | 'major_outage' | 'under_maintenance' | Component['status'];
|
|
111
|
+
/** A signal represents the outcome of a status check. */
|
|
112
|
+
type Signal = {
|
|
113
|
+
/** The check was successful. */
|
|
114
|
+
ok: true;
|
|
115
|
+
/** The service is down. */
|
|
116
|
+
down: true;
|
|
117
|
+
/** The reason for the downtime. */
|
|
118
|
+
reason: string;
|
|
119
|
+
} | {
|
|
120
|
+
/** The check was successful. */
|
|
121
|
+
ok: true;
|
|
122
|
+
/** The service is up. */
|
|
123
|
+
down: false;
|
|
124
|
+
/** Downdetector's own heading text, surfaced when it reports elevated
|
|
125
|
+
* user reports that fall short of its `outage` flag (e.g. "User reports
|
|
126
|
+
* show possible problems with X"). Omitted when the heading carries no
|
|
127
|
+
* such signal. */
|
|
128
|
+
note?: string;
|
|
129
|
+
} | {
|
|
130
|
+
/** The check failed. */
|
|
131
|
+
ok: false;
|
|
132
|
+
/** The reason for the failure. */
|
|
133
|
+
error: string;
|
|
134
|
+
};
|
|
135
|
+
/** The result of a status check. */
|
|
136
|
+
type Result = {
|
|
137
|
+
/** Headers from the response, included when available for debugging purposes. */
|
|
138
|
+
headers: Headers;
|
|
139
|
+
/** A discriminant property that indicates the type of result.
|
|
140
|
+
* - `'ok'` for a successful summary. */
|
|
141
|
+
kind: 'ok';
|
|
142
|
+
/** The summary of the status page, included when the result is 'ok'. @see {@linkcode Summary} */
|
|
143
|
+
summary: Summary;
|
|
144
|
+
} | {
|
|
145
|
+
/** Headers from the response, included when available for debugging purposes. */
|
|
146
|
+
headers?: Headers;
|
|
147
|
+
/** A discriminant property that indicates the type of result.
|
|
148
|
+
* - `'unknown'` for an unknown state. */
|
|
149
|
+
kind: 'unknown';
|
|
150
|
+
/** The reason for the unknown state. */
|
|
151
|
+
reason: string;
|
|
152
|
+
};
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/lib/downdetector.d.ts
|
|
155
|
+
/** Checks the status of GitHub on Downdetector.
|
|
156
|
+
*
|
|
157
|
+
* Launches a headless Chromium browser, navigates to the Downdetector status
|
|
158
|
+
* page for GitHub, and polls for the presence of a "Pogo Snapshot" element
|
|
159
|
+
* that indicates whether there is an outage.
|
|
160
|
+
*
|
|
161
|
+
* If an outage is detected, it extracts the reason from the page.
|
|
162
|
+
*
|
|
163
|
+
* @param chromePath Optional explicit Chrome/Chromium binary to use.
|
|
164
|
+
* @returns A promise of {@linkcode Signal}.
|
|
165
|
+
* @see {@link https://downdetector.com/status/github/} for the target page.
|
|
166
|
+
*/
|
|
167
|
+
declare function check(chromePath?: string): Promise<Signal>;
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/lib/github.d.ts
|
|
170
|
+
/** Checks the status of GitHub's services by querying their Statuspage API.
|
|
171
|
+
*
|
|
172
|
+
* @param baseUrl - Optional base URL for the GitHub Statuspage API.
|
|
173
|
+
* @returns A promise that resolves to a Result object containing either the summary of the status or an error reason.
|
|
174
|
+
*/
|
|
175
|
+
declare function check$1(baseUrl?: string | URL): Promise<Result>;
|
|
176
|
+
//#endregion
|
|
177
|
+
//#region src/index.d.ts
|
|
178
|
+
declare const githubUp: {
|
|
179
|
+
checkGitHub: typeof check$1;
|
|
180
|
+
checkDownDetector: typeof check;
|
|
181
|
+
EXIT_CODES: {
|
|
182
|
+
readonly none: 0;
|
|
183
|
+
readonly minor: 1;
|
|
184
|
+
readonly major: 2;
|
|
185
|
+
readonly critical: 2;
|
|
186
|
+
readonly unavailable: 21;
|
|
187
|
+
};
|
|
188
|
+
};
|
|
189
|
+
//#endregion
|
|
190
|
+
export { type Component, type ComponentStatus, EXIT_CODES, type Incident, type IncidentImpactValue, type IncidentStatusValue, type Indicator, type Result, type Signal, type Summary, check as checkDownDetector, check$1 as checkGitHub, githubUp as default };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import{spawn as e,spawnSync as t}from"node:child_process";import{existsSync as n,mkdtempSync as r,readFileSync as i,rmSync as a}from"node:fs";import{tmpdir as o}from"node:os";import{join as s,win32 as c}from"node:path";import{setTimeout as l}from"node:timers/promises";const u={none:0,minor:1,major:2,critical:2,unavailable:21},d=`https://downdetector.com/status/github/`,f=[`google-chrome-stable`,`google-chrome`,`chromium`,`brave`,`microsoft-edge-stable`,`microsoft-edge`],p=[`chrome.exe`,`msedge.exe`,`brave.exe`],m=[[`Google`,`Chrome`,`Application`,`chrome.exe`],[`Microsoft`,`Edge`,`Application`,`msedge.exe`],[`BraveSoftware`,`Brave-Browser`,`Application`,`brave.exe`],[`Chromium`,`Application`,`chrome.exe`]],h=[`ProgramFiles`,`ProgramFiles(x86)`,`LOCALAPPDATA`],g=[`/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`,`/Applications/Brave Browser.app/Contents/MacOS/Brave Browser`,`/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge`,`/Applications/Chromium.app/Contents/MacOS/Chromium`];function _(e){return typeof e==`object`&&!!e&&`webSocketDebuggerUrl`in e&&typeof e.webSocketDebuggerUrl==`string`}function v(e){return typeof e==`object`&&!!e&&`id`in e&&typeof e.id==`number`}function y(e){let t=new Map,n=0;return e.onmessage=e=>{let n=typeof e.data==`string`?e.data:e.data instanceof ArrayBuffer?new TextDecoder().decode(e.data):null;if(n===null)return;let r;try{r=JSON.parse(n)}catch{return}if(!v(r))return;let i=t.get(r.id);typeof i==`function`&&(t.delete(r.id),i(r))},(r,i={})=>new Promise((a,o)=>{let s=++n,c=setTimeout(()=>{t.delete(s),o(Error(`CDP command '${r}' timed out`))},5e3);t.set(s,e=>{clearTimeout(c),a(e)}),e.send(JSON.stringify({id:s,method:r,params:i}))})}async function b(e,t){let n=await(await fetch(`${e}/json/new?about%3Ablank`,{method:`PUT`})).json();if(!_(n))return{ok:!1,error:`unexpected CDP target shape`};let r=new WebSocket(n.webSocketDebuggerUrl);await new Promise((e,t)=>{r.onopen=()=>e(),r.onerror=()=>t(Error(`WebSocket connection failed`)),r.onclose=()=>t(Error(`WebSocket closed before opening`))});let i=y(r);return await i(`Page.enable`),await i(`Page.navigate`,{url:t}),{ok:!0,send:i,close:()=>r.close()}}function x(e,n){for(let r of n){let n=t(e,[r]);if(n.status===0&&n.stdout){let e=n.stdout.toString().split(/\r?\n/)[0]?.trim();if(e)return e}}return null}function S(e){for(let t of e)if(t&&n(t))return t;return null}function C(){let e=[];for(let t of h){let n=process.env[t];if(n)for(let t of m)e.push(c.join(n,...t))}return e}function w(e){let t=e??process.env.GITHUB_UP_CHROME;return t?n(t)?t:null:process.platform===`win32`?x(`where.exe`,p)??S(C()):process.platform===`darwin`?x(`which`,f)??S(g):x(`which`,f)}async function T(e,t){let n=Date.now()+t;for(;Date.now()<n;){try{if((await fetch(`${e}/json/version`)).ok)return!0}catch{}await l(100)}return!1}async function E(e,t){let n=s(e,`DevToolsActivePort`),r=Date.now()+t;for(;Date.now()<r;){try{let e=i(n,`utf8`).split(`
|
|
2
|
+
`)[0]?.trim(),t=Number(e);if(Number.isInteger(t)&&t>0)return t}catch{}await l(100)}return null}async function D(t){let n;try{n=r(s(o(),`github-up-`))}catch(e){return{ok:!1,error:`mkdtemp failed: ${e instanceof Error?e.message:String(e)}`}}let i=e(t,[`--headless=new`,`--disable-gpu`,`--no-sandbox`,`--disable-blink-features=AutomationControlled`,`--window-size=1920,1080`,`--user-data-dir=${n}`,`--remote-debugging-port=0`,`--user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36`,`about:blank`],{stdio:`ignore`}),a=new Promise(e=>{i.once(`error`,t=>e({failed:t.message})),i.once(`exit`,(t,n)=>e({failed:`Chrome exited before CDP came up (${n??t??`unknown`})`}))}),c=await Promise.race([E(n,5e3),a]);if(typeof c!=`number`)return O(i,n),{ok:!1,error:c===null?`CDP endpoint never came up`:`Chrome launch failed: ${c.failed}`};let l=`http://localhost:${c}`;return await T(l,5e3)?{ok:!0,browser:{proc:i,userDataDir:n,base:l}}:(O(i,n),{ok:!1,error:`CDP endpoint never came up`})}function O(e,n){process.platform===`win32`&&e.pid!==void 0?t(`taskkill`,[`/pid`,String(e.pid),`/t`,`/f`]):e.kill();try{a(n,{recursive:!0,force:!0,maxRetries:5,retryDelay:100})}catch{}}const k=`({
|
|
3
|
+
title: document.title,
|
|
4
|
+
pogo: window.PogoConfig ?? null,
|
|
5
|
+
h1: document.querySelector('h1')?.innerText ?? null,
|
|
6
|
+
cfChallenge: document.title === 'Just a moment...'
|
|
7
|
+
|| document.querySelector('script[src*="/cdn-cgi/challenge-platform/"]') !== null
|
|
8
|
+
|| document.body?.innerText?.includes('Enable JavaScript and cookies to continue') === true,
|
|
9
|
+
})`;function A(e){return typeof e==`object`&&!!e&&`result`in e&&typeof e.result==`object`&&e.result!==null&&`result`in e.result&&typeof e.result.result==`object`&&e.result.result!==null&&`value`in e.result.result&&typeof e.result.result.value==`string`}function j(e){if(typeof e!=`object`||!e||!(`title`in e)||typeof e.title!=`string`||`h1`in e&&e.h1!==null&&typeof e.h1!=`string`||`cfChallenge`in e&&e.cfChallenge!==void 0&&typeof e.cfChallenge!=`boolean`||!(`pogo`in e))return!1;let{pogo:t}=e;return t===null||!(typeof t!=`object`||`outage`in t&&t.outage!==void 0&&typeof t.outage!=`boolean`)}async function M(e,t){return await e(`Runtime.enable`),N(async t=>{let n=await e(`Runtime.evaluate`,{expression:`JSON.stringify(${t})`,returnByValue:!0});if(!A(n))return null;try{return JSON.parse(n.result.result.value)}catch{return null}},t)}async function N(e,t){let n=Date.now()+t;for(;Date.now()<n;){let t=await e(k);if(j(t)&&t.cfChallenge===!0)return{kind:`cloudflare-challenge`};if(j(t)&&t.pogo!==null)return{kind:`status`,pogo:t.pogo,heading:t.h1};await l(700)}return null}const P=/possible problems/i;function F(e){return e!==null&&P.test(e)?e:void 0}const I=`
|
|
10
|
+
const WEBVIEW_WIDTH = 1920;
|
|
11
|
+
const WEBVIEW_HEIGHT = 1080;
|
|
12
|
+
const SNAPSHOT_TIMEOUT_MS = 20000;
|
|
13
|
+
const POLL_INTERVAL_MS = 700;
|
|
14
|
+
const CHROME_ARGV = ${JSON.stringify([`--headless=new`,`--disable-gpu`,`--no-sandbox`,`--disable-blink-features=AutomationControlled`,`--window-size=1920,1080`,`--user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36`])};
|
|
15
|
+
const POSSIBLE_PROBLEMS_PATTERN = ${P};
|
|
16
|
+
${F.toString()}
|
|
17
|
+
|
|
18
|
+
function webViewOptions(chromePath) {
|
|
19
|
+
if (chromePath || process.platform !== 'darwin') {
|
|
20
|
+
return {
|
|
21
|
+
width: WEBVIEW_WIDTH,
|
|
22
|
+
height: WEBVIEW_HEIGHT,
|
|
23
|
+
backend: {
|
|
24
|
+
type: 'chrome',
|
|
25
|
+
url: false,
|
|
26
|
+
argv: CHROME_ARGV,
|
|
27
|
+
...(chromePath ? { path: chromePath } : {}),
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return { width: WEBVIEW_WIDTH, height: WEBVIEW_HEIGHT };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isPogoSnapshot(value) {
|
|
36
|
+
if (typeof value !== 'object' || value === null) return false;
|
|
37
|
+
if (!('title' in value) || typeof value.title !== 'string') return false;
|
|
38
|
+
if ('h1' in value && value.h1 !== null && typeof value.h1 !== 'string') return false;
|
|
39
|
+
if ('cfChallenge' in value && value.cfChallenge !== undefined && typeof value.cfChallenge !== 'boolean') return false;
|
|
40
|
+
if (!('pogo' in value)) return false;
|
|
41
|
+
const { pogo } = value;
|
|
42
|
+
if (pogo === null) return true;
|
|
43
|
+
if (typeof pogo !== 'object') return false;
|
|
44
|
+
return !('outage' in pogo) || pogo.outage === undefined || typeof pogo.outage === 'boolean';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function pollPogoSnapshot(view) {
|
|
48
|
+
const deadline = Date.now() + SNAPSHOT_TIMEOUT_MS;
|
|
49
|
+
while (Date.now() < deadline) {
|
|
50
|
+
const snapshot = await view.evaluate(${JSON.stringify(k)}).catch(() => null);
|
|
51
|
+
if (isPogoSnapshot(snapshot) && snapshot.cfChallenge === true) {
|
|
52
|
+
return { kind: 'cloudflare-challenge' };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (
|
|
56
|
+
isPogoSnapshot(snapshot)
|
|
57
|
+
&& snapshot.pogo !== null
|
|
58
|
+
) {
|
|
59
|
+
return { kind: 'status', pogo: snapshot.pogo, heading: snapshot.h1 };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
await Bun.sleep(POLL_INTERVAL_MS);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function check(url, chromePath) {
|
|
69
|
+
const WebView = globalThis.Bun?.WebView;
|
|
70
|
+
if (typeof WebView !== 'function') {
|
|
71
|
+
return { ok: false, error: 'Bun.WebView is unavailable; upgrade Bun or run with Node' };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let view;
|
|
75
|
+
try {
|
|
76
|
+
view = new WebView(webViewOptions(chromePath));
|
|
77
|
+
} catch (error) {
|
|
78
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
await view.navigate(url);
|
|
83
|
+
const result = await pollPogoSnapshot(view);
|
|
84
|
+
if (result === null) return { ok: false, error: 'CF challenge not cleared in time' };
|
|
85
|
+
if (result.kind === 'cloudflare-challenge') return { ok: false, error: 'Cloudflare challenge page' };
|
|
86
|
+
if (result.pogo.outage === true) {
|
|
87
|
+
return { ok: true, down: true, reason: result.heading ?? 'outage reported' };
|
|
88
|
+
}
|
|
89
|
+
const note = detectPossibleProblemsNote(result.heading);
|
|
90
|
+
if (note !== undefined) {
|
|
91
|
+
return { ok: true, down: false, note };
|
|
92
|
+
}
|
|
93
|
+
return { ok: true, down: false };
|
|
94
|
+
} catch (error) {
|
|
95
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error) };
|
|
96
|
+
} finally {
|
|
97
|
+
view?.close();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
self.onmessage = async (event) => {
|
|
102
|
+
try {
|
|
103
|
+
const { url, chromePath } = event.data;
|
|
104
|
+
self.postMessage({ type: 'result', result: await check(url, chromePath) });
|
|
105
|
+
} catch (error) {
|
|
106
|
+
self.postMessage({
|
|
107
|
+
type: 'error',
|
|
108
|
+
error: error instanceof Error ? error.message : String(error),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
`;function L(){let e=globalThis.Bun;return e===void 0?null:e}function R(){let e=L()?.WebView;return typeof e==`function`?e:null}function z(){return R()!==null}function B(){let e=URL.createObjectURL(new Blob([I],{type:`text/javascript`}));return new Worker(e,{type:`module`})}async function V(e,t,n=45e3){let r;try{r=B()}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}try{return await new Promise(i=>{let a=setTimeout(()=>{r.terminate(),i({ok:!1,error:`Bun.WebView timed out`})},n);r.onmessage=e=>{clearTimeout(a),r.terminate();let t=e.data;if(t.type===`result`){i(t.result);return}i({ok:!1,error:t.error})},r.onerror=e=>{clearTimeout(a),r.terminate(),i({ok:!1,error:e.message})},r.postMessage({url:e,chromePath:t})})}finally{await l(0)}}async function H(e,t){return R()===null?{ok:!1,error:`Bun.WebView is unavailable; upgrade Bun or run with Node`}:V(e,t??process.env.GITHUB_UP_CHROME)}async function U(e){if(z())return H(d,e);let t=w(e);if(t===null)return{ok:!1,error:`no Chrome/Chromium found; set GITHUB_UP_CHROME or pass --chrome <path>`};let n=await D(t);if(!n.ok)return n;let{browser:{proc:r,userDataDir:i,base:a}}=n;try{let e=await b(a,d);if(!e.ok)return e;let t=await M(e.send,2e4);if(e.close(),t===null)return{ok:!1,error:`CF challenge not cleared in time`};if(t.kind===`cloudflare-challenge`)return{ok:!1,error:`Cloudflare challenge page`};if(t.pogo.outage===!0)return{ok:!0,down:!0,reason:t.heading??`outage reported`};let n=F(t.heading);return n===void 0?{ok:!0,down:!1}:{ok:!0,down:!1,note:n}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e)}}finally{O(r,i)}}const W=`/api/v2`,G={components(){return`${W}/components.json`},Incidents:{all(){return`${W}/incidents.json`},unresolved(){return`${W}/incidents/unresolved.json`}},ScheduledMaintenances:{active(){return`${W}/scheduled-maintenances/active.json`},all(){return`${W}/scheduled-maintenances.json`},upcoming(){return`${W}/scheduled-maintenances/upcoming.json`}},status(){return`${W}/status.json`},subscriber(e){return`${W}/subscribers/${encodeURIComponent(e)}.json`},subscribers(){return`${W}/subscribers.json`},summary(){return`${W}/summary.json`}};function K(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function q(e){return K(e)&&typeof e.name==`string`&&typeof e.status==`string`}function J(e){return K(e)&&typeof e.indicator==`string`&&typeof e.description==`string`}function Y(e){return q(e)&&K(e)&&typeof e.impact==`string`}function X(e){return K(e)&&J(e.status)&&Array.isArray(e.components)&&e.components.every(q)&&Array.isArray(e.incidents)&&e.incidents.every(Y)}async function Z(e){try{let t=await e.text(),n=t.length>0?`: ${t}`:``;return`Request failed with status code ${e.status}${n}`}catch{let{status:t,statusText:n}=e;return`Request failed with status code ${t}: ${n}`}}async function Q(e=`https://www.githubstatus.com`){try{let t=await fetch(new URL(G.summary(),e));if(!t.ok){let{headers:e}=t;return{headers:e,kind:`unknown`,reason:await Z(t)}}let n=await t.json();return X(n)?{headers:t.headers,kind:`ok`,summary:n}:{headers:t.headers,kind:`unknown`,reason:`unexpected response shape from status API`}}catch(e){return{kind:`unknown`,reason:e instanceof Error?e.message:String(e)}}}const $={checkGitHub:Q,checkDownDetector:U,EXIT_CODES:u};export{u as EXIT_CODES,U as checkDownDetector,Q as checkGitHub,$ as default};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" class="dark">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<link rel="canonical" href="https://github-up.kjanat.dev/">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
7
|
+
<meta name="robots" content="all, follow, index">
|
|
8
|
+
|
|
9
|
+
<title>github-up</title>
|
|
10
|
+
<meta
|
|
11
|
+
name="description"
|
|
12
|
+
content="Live GitHub service status dashboard using github-up"
|
|
13
|
+
>
|
|
14
|
+
<meta name="theme-color" content="#e2e8f0">
|
|
15
|
+
|
|
16
|
+
<link
|
|
17
|
+
rel="preload"
|
|
18
|
+
href="https://www.githubstatus.com/api/v2/summary.json"
|
|
19
|
+
as="fetch"
|
|
20
|
+
type="application/json"
|
|
21
|
+
crossorigin
|
|
22
|
+
>
|
|
23
|
+
|
|
24
|
+
<link id="favicon" rel="icon" href="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjggMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+Cgk8Y2lyY2xlIGN4PSIxNCIgY3k9IjE0IiByPSIxNCIgZmlsbD0iIzBEMTExNyIgLz4KCTxwYXRoIGQ9Ik0xMi4wMDcgMTguNjE0CgkgICAgICAgICBjLTMuMDE0LS4zNjUtNS4xMzYtMi41MzQtNS4xMzYtNS4zNDMgMC0xLjE0MS40MTEtMi4zNzUgMS4wOTYtMy4xOTctLjI5Ni0uNzU0LS4yNTEtMi4zNTMuMDkxLTMuMDE0LjkxNC0uMTE0IDIuMTQ3LjM2NSAyLjg3OCAxLjAyOC44NjctLjI3NCAxLjc4MS0uNDExIDIuOS0uNDExCgkgICAgICAgICBzMi4wMzMuMTM3IDIuODU1LjM4OAoJICAgICAgICAgYy43MDgtLjY0IDEuOTYzLTEuMTE5IDIuODc4LTEuMDA1LjMxOS42MTcuMzY1IDIuMjE2LjA2OCAyLjk5Mi43MzEuODY4IDEuMTE5IDIuMDMzIDEuMTE5IDMuMjIgMCAyLjgwOS0yLjEyNCA0LjkzMi01LjE4MyA1LjMyLjc3Ni41MDMgMS4zMDIgMS41OTkgMS4zMDIgMi44NTUKCSAgICAgICAgIHYyLjM3NQoJICAgICAgICAgYzAgLjY4NS41NzEgMS4wNzQgMS4yNTYuNzk5IDQuMTMzLTEuNTc2IDcuMzc2LTUuNzA4IDcuMzc2LTEwLjgyMSAwLTYuNDYyLTUuMjUxLTExLjczNS0xMS43MTMtMTEuNzM1CgkgICAgICAgICBTMi4xMjIgNy4zMzggMi4xMjIgMTMuOCBhMTEuNTE3IDExLjUxNyAwIDAgMCA3LjU1OCAxMC44NDQKCSAgICAgICAgIGMuNjE3LjIyOCAxLjIxMS0uMTgzIDEuMjExLS43OTkgdi0xLjgyNyBhMi45NDEgMi45NDEgMCAwIDEtMS4wOTYuMjI4CgkgICAgICAgICBjLTEuNTA3IDAtMi4zOTgtLjgyMi0zLjAzNy0yLjM1Mi0uMjUyLS42MTYtLjUyNi0uOTgxLTEuMDUxLTEuMDUtLjI3NC0uMDIzLS4zNjUtLjEzNy0uMzY1LS4yNzQgMC0uMjc0LjQ1Ni0uNDguOTEzLS40OC42NjMgMCAxLjIzNC40MTEgMS44MjcgMS4yNTYuNDU3LjY2Mi45MzYuOTU5IDEuNTA3Ljk1OQoJICAgICAgICAgcy45MzYtLjIwNSAxLjQ2MS0uNzMgYy4zODgtLjM4OS42ODUtLjczMS45NTktLjk2MSBaIgoJICAgICAgZmlsbD0iI0YwRjZGQyIgLz4KCTxjaXJjbGUgY3g9IjIxIiBjeT0iNyIgcj0iNC43NSIKCSAgICAgICAgZmlsbD0iI0VGNDQ0NCIgc3Ryb2tlPSIjMEQxMTE3IiBzdHJva2Utd2lkdGg9IjEuNSIgLz4KCTxwYXRoIGQ9Ik0yMSA0LjV2My4yIgoJICAgICAgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIxLjM1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIC8+Cgk8Y2lyY2xlIGN4PSIyMSIgY3k9IjkuNTUiIHI9IjAuNyIgZmlsbD0id2hpdGUiIC8+Cjwvc3ZnPgo=" type="image/svg+xml" sizes="any">
|
|
25
|
+
|
|
26
|
+
<style>@import "https://cdn.jsdelivr.net/npm/tailwindcss@4/preflight.min.css";:root{--color-bg:#0f1117;--color-text:#e2e8f0;--color-text-strong:#f1f5f9;--color-text-muted:#cbd5e1;--color-muted:#94a3b8;--color-surface:#1e2535;--color-border:#2d3748;--color-border-hover:#3d4f6b;--color-success:#22c55e;--color-caution:#f59e0b;--color-warning:#f97316;--color-error:#ef4444;--color-critical:#dc2626;--color-info:#6366f1;--color-success-soft:color-mix(in srgb,var(--color-success)20%,transparent);--color-caution-soft:color-mix(in srgb,var(--color-caution)20%,transparent);--color-error-soft:color-mix(in srgb,var(--color-error)20%,transparent);--color-critical-soft:color-mix(in srgb,var(--color-critical)20%,transparent);--icon-github:url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+Cgk8cGF0aCBkPSJNMTAuMzAzIDE2LjY1MgoJICAgICAgICAgYy0yLjgzNy0uMzQ0LTQuODM1LTIuMzg1LTQuODM1LTUuMDI4IDAtMS4wNzQuMzg3LTIuMjM1IDEuMDMxLTMuMDA4LS4yNzktLjcwOS0uMjM2LTIuMjE0LjA4Ni0yLjgzNy44Ni0uMTA3IDIuMDIuMzQ0IDIuNzA4Ljk2Ny44MTYtLjI1OCAxLjY3Ni0uMzg2IDIuNzI4LS4zODYgMS4wNTMgMCAxLjkxMy4xMjggMi42ODYuMzY1LjY2Ni0uNjAyIDEuODQ4LTEuMDUzIDIuNzA4LS45NDYuMy41ODEuMzQ0IDIuMDg1LjA2NCAyLjgxNS42ODguODE3IDEuMDUzIDEuOTEzIDEuMDUzIDMuMDMgMCAyLjY0My0xLjk5OCA0LjY0MS00Ljg3NyA1LjAwNi43My40NzMgMS4yMjQgMS41MDQgMS4yMjQgMi42ODYKCSAgICAgICAgIHYyLjIzNQoJICAgICAgICAgYzAgLjY0NC41MzcgMS4wMSAxLjE4Mi43NTIgMy44ODktMS40ODMgNi45NC01LjM3MiA2Ljk0LTEwLjE4NSAwLTYuMDgxLTQuOTQyLTExLjA0NC0xMS4wMjItMTEuMDQ0LTYuMDgxIDAtMTAuOTggNC45NjMtMTAuOTggMTEuMDQ0CgkgICAgICAgICBhMTAuODQgMTAuODQgMCAwIDAgNy4xMTIgMTAuMjA2IGMuNTguMjE1IDEuMTM5LS4xNzIgMS4xMzktLjc1MiB2LTEuNzE5IGEyLjc2OCAyLjc2OCAwIDAgMS0xLjAzMi4yMTUKCSAgICAgICAgIGMtMS40MTggMC0yLjI1Ni0uNzczLTIuODU3LTIuMjEzLS4yMzctLjU4LS40OTUtLjkyNC0uOTg5LS45ODgtLjI1OC0uMDIyLS4zNDQtLjEyOS0uMzQ0LS4yNTggMC0uMjU4LjQzLS40NTEuODYtLjQ1MS42MjMgMCAxLjE2LjM4NiAxLjcxOSAxLjE4MS40My42MjMuODgxLjkwMyAxLjQxOC45MDMuNTM3IDAgLjg4MS0uMTk0IDEuMzc1LS42ODguMzY1LS4zNjUuNjQ1LS42ODcuOTAzLS45MDIKCSAgICAgICAgIFoiIC8+Cjwvc3ZnPgo=);--icon-loading:url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiCiAgICAgZmlsbD0ibm9uZSIgc3Ryb2tlPSJjdXJyZW50Q29sb3IiIHN0cm9rZS13aWR0aD0iMiIKICAgICB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgoJPHRpdGxlPgoJCUxvYWRpbmcKCTwvdGl0bGU+Cgk8Y2lyY2xlIGN4PSIxMiIgY3k9IjEyIiByPSIxMCIgLz4KCTxwYXRoIGQ9Ik0xMiA4djRsMyAzIiAvPgo8L3N2Zz4K);--logo:url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjggMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+Cgk8Y2lyY2xlIGN4PSIxNCIgY3k9IjE0IiByPSIxNCIgZmlsbD0iIzBEMTExNyIgLz4KCTxwYXRoIGQ9Ik0xMi4wMDcgMTguNjE0CgkgICAgICAgICBjLTMuMDE0LS4zNjUtNS4xMzYtMi41MzQtNS4xMzYtNS4zNDMgMC0xLjE0MS40MTEtMi4zNzUgMS4wOTYtMy4xOTctLjI5Ni0uNzU0LS4yNTEtMi4zNTMuMDkxLTMuMDE0LjkxNC0uMTE0IDIuMTQ3LjM2NSAyLjg3OCAxLjAyOC44NjctLjI3NCAxLjc4MS0uNDExIDIuOS0uNDExCgkgICAgICAgICBzMi4wMzMuMTM3IDIuODU1LjM4OAoJICAgICAgICAgYy43MDgtLjY0IDEuOTYzLTEuMTE5IDIuODc4LTEuMDA1LjMxOS42MTcuMzY1IDIuMjE2LjA2OCAyLjk5Mi43MzEuODY4IDEuMTE5IDIuMDMzIDEuMTE5IDMuMjIgMCAyLjgwOS0yLjEyNCA0LjkzMi01LjE4MyA1LjMyLjc3Ni41MDMgMS4zMDIgMS41OTkgMS4zMDIgMi44NTUKCSAgICAgICAgIHYyLjM3NQoJICAgICAgICAgYzAgLjY4NS41NzEgMS4wNzQgMS4yNTYuNzk5IDQuMTMzLTEuNTc2IDcuMzc2LTUuNzA4IDcuMzc2LTEwLjgyMSAwLTYuNDYyLTUuMjUxLTExLjczNS0xMS43MTMtMTEuNzM1CgkgICAgICAgICBTMi4xMjIgNy4zMzggMi4xMjIgMTMuOCBhMTEuNTE3IDExLjUxNyAwIDAgMCA3LjU1OCAxMC44NDQKCSAgICAgICAgIGMuNjE3LjIyOCAxLjIxMS0uMTgzIDEuMjExLS43OTkgdi0xLjgyNyBhMi45NDEgMi45NDEgMCAwIDEtMS4wOTYuMjI4CgkgICAgICAgICBjLTEuNTA3IDAtMi4zOTgtLjgyMi0zLjAzNy0yLjM1Mi0uMjUyLS42MTYtLjUyNi0uOTgxLTEuMDUxLTEuMDUtLjI3NC0uMDIzLS4zNjUtLjEzNy0uMzY1LS4yNzQgMC0uMjc0LjQ1Ni0uNDguOTEzLS40OC42NjMgMCAxLjIzNC40MTEgMS44MjcgMS4yNTYuNDU3LjY2Mi45MzYuOTU5IDEuNTA3Ljk1OQoJICAgICAgICAgcy45MzYtLjIwNSAxLjQ2MS0uNzMgYy4zODgtLjM4OS42ODUtLjczMS45NTktLjk2MSBaIgoJICAgICAgZmlsbD0iI0YwRjZGQyIgLz4KCTxjaXJjbGUgY3g9IjIxIiBjeT0iNyIgcj0iNC43NSIKCSAgICAgICAgZmlsbD0iI0VGNDQ0NCIgc3Ryb2tlPSIjMEQxMTE3IiBzdHJva2Utd2lkdGg9IjEuNSIgLz4KCTxwYXRoIGQ9Ik0yMSA0LjV2My4yIgoJICAgICAgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIxLjM1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIC8+Cgk8Y2lyY2xlIGN4PSIyMSIgY3k9IjkuNTUiIHI9IjAuNyIgZmlsbD0id2hpdGUiIC8+Cjwvc3ZnPgo=);--icon-npm:url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjcuMjMgMjcuMjMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+Cgk8cGF0aCBkPSJNMCAyIGEyIDIgMCAwIDEgMi0yIGgyMy4yMyBhMiAyIDAgMCAxIDIgMiB2MjMuMjMgYTIgMiAwIDAgMS0yIDIgSDIgYTIgMiAwIDAgMS0yLTIgVjIgWgoJICAgICAgICAgbTUuOCAxOS43NSBoNy44NiBsLjAxLTExLjc3IGgzLjkyIGwtLjAxIDExLjc4IGgzLjkzIGwuMDEtMTUuNyAtMTUuNy0uMDIgLS4wMiAxNS43MSBaIiAvPgo8L3N2Zz4K)}body{background:var(--color-bg);color:var(--color-text);min-height:100vh;padding:0 0 32px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif}.dashboard{max-width:840px;margin:0 auto;padding:0 16px}header{display:flex;border-bottom:1px solid var(--color-surface);justify-content:space-between;align-items: center;margin-bottom:24px;padding:20px 0 16px}#name,.status,.comp-domain,.comp-name{-webkit-user-select:all;user-select:all}.logo{display:flex;letter-spacing:-.3px;color:var(--color-text-strong);align-items: center;gap:10px;font-size:17px;font-weight:700}.logo:before{content:"";display:block;background:var(--logo)center/contain no-repeat;width:28px;height:28px}.hero{display:flex;border-radius:16px;align-items: center;margin-bottom:28px;padding:28px;transition:background .4s}.hero.loading{background:var(--color-surface)}.hero.ok{background:linear-gradient(135deg,#052e16 0%,#14532d 100%);border:1px solid #166534}.hero.minor{background:linear-gradient(135deg,#451a03 0%,#78350f 100%);border:1px solid #b45309}.hero.major,.hero.critical{background:linear-gradient(135deg,#450a0a 0%,#7f1d1d 100%);border:1px solid #b91c1c}.hero.error{background:var(--color-surface);border:1px solid #334155}.hero-inner{display:flex;align-items: center;gap:20px}.hero-icon{display:flex;border-radius:50%;flex-shrink:0;justify-content:center;align-items: center;width:64px;height:64px}.hero-icon.loading{background:var(--color-border);color:var(--color-muted)}.hero-icon.loading:before{content:"";display:block;mask:var(--icon-loading)center/contain no-repeat;background-color:currentColor;width:32px;height:32px}.hero-icon.ok{background:var(--color-success-soft);color:var(--color-success)}.hero-icon.minor{background:var(--color-caution-soft);color:var(--color-caution)}.hero-icon.major,.hero-icon.critical{background:var(--color-error-soft);color:var(--color-error)}.hero-icon.error{background:var(--color-border);color:var(--color-muted)}.hero-icon>svg{display:block;width:32px;height:32px}.hero-title{color:var(--color-text-strong);letter-spacing:-.3px;font-size:22px;font-weight:700}.hero-sub{color:var(--color-muted);margin-top:4px;font-size:14px}.section{margin-bottom:28px}.section h2{text-transform:uppercase;letter-spacing:.08em;color:var(--color-muted);margin-bottom:12px;font-size:13px;font-weight:600}.incidents-list{display:flex;flex-direction:column;gap:10px}.incident-card{background:var(--color-surface);border:1px solid var(--color-border);border-radius:12px;padding:16px 18px}.inc-header{display:flex;justify-content:space-between;align-items: center;gap:12px;margin-bottom:6px}.inc-name{color:var(--color-text-strong);font-size:15px;font-weight:600}.inc-badge{text-transform:uppercase;letter-spacing:.05em;white-space:nowrap;border-radius:99px;padding:3px 8px;font-size:11px;font-weight:700}.inc-badge.minor{background:var(--color-caution-soft);color:var(--color-caution)}.inc-badge.major{background:var(--color-error-soft);color:var(--color-error)}.inc-badge.critical{background:var(--color-critical-soft);color:var(--color-critical)}.inc-badge.none{background:var(--color-success-soft);color:var(--color-success)}.inc-meta{color:var(--color-muted);font-size:12px}.empty-row{display:flex;background:var(--color-surface);border:1px solid var(--color-border);color:var(--color-success);border-radius:12px;align-items: center;gap:8px;padding:16px 18px;font-size:14px}.loading-row{background:var(--color-surface);border:1px solid var(--color-border);color:var(--color-muted);border-radius:12px;padding:16px 18px;font-size:14px}.noscript-card{background:var(--color-surface);border:1px solid var(--color-caution);color:var(--color-text-muted);border-radius:12px;margin-bottom:24px;padding:16px 18px;font-size:14px}.noscript-card code{color:var(--color-text-strong);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}.components-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:10px}.comp-card{background:var(--color-surface);border:1px solid var(--color-border);display:flex;border-radius:12px;align-items: center;gap:12px;padding:14px 16px;transition:border-color .2s}.comp-card:hover{border-color:var(--color-border-hover)}.comp-dot{background:currentColor;border-radius:50%;flex-shrink:0;width:10px;height:10px;box-shadow:0 0 6px}.comp-block{color:var(--color-text-muted);font-size:13px;font-weight:500;line-height:1.3}.comp-domain{display:block;color:var(--color-muted);user-select:all;padding-bottom:4px;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:10px;font-weight:400}.comp-status{text-transform:uppercase;letter-spacing:.05em;margin-top:2px;font-size:11px;font-weight:600}.comp-dot.operational,.comp-status.operational{color:var(--color-success)}.comp-dot.degraded_performance,.comp-status.degraded_performance{color:var(--color-caution)}.comp-dot.partial_outage,.comp-status.partial_outage{color:var(--color-warning)}.comp-dot.major_outage,.comp-status.major_outage{color:var(--color-error)}.comp-dot.under_maintenance,.comp-status.under_maintenance{color:var(--color-info)}.comp-dot.unknown,.comp-status.unknown{color:var(--color-muted)}.footer{display:flex;justify-content:center;gap:10px;padding-top:6px}.footer-link{display:inline-flex;border:1px solid var(--color-border);color:var(--color-muted);background:radial-gradient(circle at 50% 100%,color-mix(in srgb,var(--color-muted)14%,transparent),transparent 58%),var(--color-surface);border-radius:50%;justify-content:center;align-items: center;width:40px;height:40px;transition:background .2s,border-color .2s,box-shadow .2s,color .2s,transform .2s}.footer-link:before{content:"";display:block;transform-origin:center;background-color:currentColor;width:20px;height:20px;transition:transform .2s}.footer-link-github:before{mask:var(--icon-github)center/contain no-repeat}.footer-link-npm:before{mask:var(--icon-npm)center/contain no-repeat}.footer-link:hover{border-color:var(--color-border-hover);transform:translateY(-2px);box-shadow:0 10px 26px #0000003d}.footer-link:focus-visible{border-color:var(--color-border-hover);transform:translateY(-2px);box-shadow:0 10px 26px #0000003d}.footer-link:hover:before{transform:rotate(-6deg)scale(1.08)}.footer-link:focus-visible:before{transform:rotate(-6deg)scale(1.08)}.footer-link-github:hover{color:var(--color-text-strong);background:radial-gradient(circle at 50% 100%,color-mix(in srgb,var(--color-text-strong)20%,transparent),transparent 58%),var(--color-surface)}.footer-link-github:focus-visible{color:var(--color-text-strong);background:radial-gradient(circle at 50% 100%,color-mix(in srgb,var(--color-text-strong)20%,transparent),transparent 58%),var(--color-surface)}.footer-link-npm:hover{color:var(--color-warning);background:radial-gradient(circle at 50% 100%,color-mix(in srgb,var(--color-warning)22%,transparent),transparent 58%),var(--color-surface)}.footer-link-npm:focus-visible{color:var(--color-warning);background:radial-gradient(circle at 50% 100%,color-mix(in srgb,var(--color-warning)22%,transparent),transparent 58%),var(--color-surface)}.footer-link:focus-visible{outline:2px solid var(--color-muted);outline-offset:3px}.status{color:var(--color-muted);font-size:11px}.status:before{content:"Updated: "}@media (prefers-reduced-motion:reduce){.hero,.comp-card,.footer-link,.footer-link:before{transition:none}.footer-link:hover,.footer-link:hover:before{transform:none}.footer-link:focus-visible{transform:none}.footer-link:focus-visible:before{transform:none}}@media (max-width:480px){body{padding-bottom:20px}.dashboard{padding:0 14px}header{margin-bottom:18px;padding:16px 0 13px}.logo{font-size:16px}.hero{margin-bottom:22px;padding:22px}.hero-inner{gap:16px}.hero-icon{width:56px;height:56px}.hero-title{font-size:20px;line-height:1.15}.hero-sub{font-size:13px}.section{margin-bottom:20px}.section h2{margin-bottom:10px;font-size:12px}.empty-row,.loading-row,.noscript-card{padding:13px 16px}.components-grid{gap:8px}.comp-card{min-height:50px;padding:10px 14px}.footer{padding-top:2px}.footer-link{width:38px;height:38px}}
|
|
27
|
+
</style></head>
|
|
28
|
+
|
|
29
|
+
<body translate="no">
|
|
30
|
+
<main class="dashboard">
|
|
31
|
+
<header>
|
|
32
|
+
<div class="logo">
|
|
33
|
+
<span id="name">github-up</span>
|
|
34
|
+
</div>
|
|
35
|
+
<div id="last-updated" class="status"></div>
|
|
36
|
+
</header>
|
|
37
|
+
|
|
38
|
+
<noscript>
|
|
39
|
+
<div class="noscript-card">
|
|
40
|
+
JavaScript is disabled. For terminal status, run
|
|
41
|
+
<code>npx github-up</code>.
|
|
42
|
+
</div>
|
|
43
|
+
</noscript>
|
|
44
|
+
|
|
45
|
+
<section class="hero" id="hero">
|
|
46
|
+
<div class="hero-inner">
|
|
47
|
+
<div id="hero-icon" class="hero-icon loading"></div>
|
|
48
|
+
<div>
|
|
49
|
+
<div id="hero-title" class="hero-title">Checking status…</div>
|
|
50
|
+
<div id="hero-sub" class="hero-sub"></div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</section>
|
|
54
|
+
|
|
55
|
+
<section class="section">
|
|
56
|
+
<h2>Incidents</h2>
|
|
57
|
+
<div id="incidents-list" class="incidents-list">
|
|
58
|
+
<div class="loading-row">Loading…</div>
|
|
59
|
+
</div>
|
|
60
|
+
</section>
|
|
61
|
+
|
|
62
|
+
<section class="section">
|
|
63
|
+
<h2>Components</h2>
|
|
64
|
+
<div id="components-grid" class="components-grid">
|
|
65
|
+
<div class="loading-row">Loading…</div>
|
|
66
|
+
</div>
|
|
67
|
+
</section>
|
|
68
|
+
|
|
69
|
+
<footer class="footer" aria-label="Project links">
|
|
70
|
+
<a
|
|
71
|
+
class="footer-link footer-link-github"
|
|
72
|
+
href="https://github.com/kjanat/github-up"
|
|
73
|
+
aria-label="github-up on GitHub"
|
|
74
|
+
target="_blank"
|
|
75
|
+
rel="noreferrer"
|
|
76
|
+
></a>
|
|
77
|
+
<a
|
|
78
|
+
class="footer-link footer-link-npm"
|
|
79
|
+
href="https://npm.im/github-up"
|
|
80
|
+
aria-label="github-up on npm"
|
|
81
|
+
target="_blank"
|
|
82
|
+
rel="noreferrer"
|
|
83
|
+
></a>
|
|
84
|
+
</footer>
|
|
85
|
+
</main>
|
|
86
|
+
<script type="module">var G="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjggMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+Cgk8Y2lyY2xlIGN4PSIxNCIgY3k9IjE0IiByPSIxNCIgZmlsbD0iIzBEMTExNyIgLz4KCTxwYXRoIGQ9Ik0xMi4wMDcgMTguNjE0CgkgICAgICAgICBjLTMuMDE0LS4zNjUtNS4xMzYtMi41MzQtNS4xMzYtNS4zNDMgMC0xLjE0MS40MTEtMi4zNzUgMS4wOTYtMy4xOTctLjI5Ni0uNzU0LS4yNTEtMi4zNTMuMDkxLTMuMDE0LjkxNC0uMTE0IDIuMTQ3LjM2NSAyLjg3OCAxLjAyOC44NjctLjI3NCAxLjc4MS0uNDExIDIuOS0uNDExCgkgICAgICAgICBzMi4wMzMuMTM3IDIuODU1LjM4OAoJICAgICAgICAgYy43MDgtLjY0IDEuOTYzLTEuMTE5IDIuODc4LTEuMDA1LjMxOS42MTcuMzY1IDIuMjE2LjA2OCAyLjk5Mi43MzEuODY4IDEuMTE5IDIuMDMzIDEuMTE5IDMuMjIgMCAyLjgwOS0yLjEyNCA0LjkzMi01LjE4MyA1LjMyLjc3Ni41MDMgMS4zMDIgMS41OTkgMS4zMDIgMi44NTUKCSAgICAgICAgIHYyLjM3NQoJICAgICAgICAgYzAgLjY4NS41NzEgMS4wNzQgMS4yNTYuNzk5IDQuMTMzLTEuNTc2IDcuMzc2LTUuNzA4IDcuMzc2LTEwLjgyMSAwLTYuNDYyLTUuMjUxLTExLjczNS0xMS43MTMtMTEuNzM1CgkgICAgICAgICBTMi4xMjIgNy4zMzggMi4xMjIgMTMuOCBhMTEuNTE3IDExLjUxNyAwIDAgMCA3LjU1OCAxMC44NDQKCSAgICAgICAgIGMuNjE3LjIyOCAxLjIxMS0uMTgzIDEuMjExLS43OTkgdi0xLjgyNyBhMi45NDEgMi45NDEgMCAwIDEtMS4wOTYuMjI4CgkgICAgICAgICBjLTEuNTA3IDAtMi4zOTgtLjgyMi0zLjAzNy0yLjM1Mi0uMjUyLS42MTYtLjUyNi0uOTgxLTEuMDUxLTEuMDUtLjI3NC0uMDIzLS4zNjUtLjEzNy0uMzY1LS4yNzQgMC0uMjc0LjQ1Ni0uNDguOTEzLS40OC42NjMgMCAxLjIzNC40MTEgMS44MjcgMS4yNTYuNDU3LjY2Mi45MzYuOTU5IDEuNTA3Ljk1OQoJICAgICAgICAgcy45MzYtLjIwNSAxLjQ2MS0uNzMgYy4zODgtLjM4OS42ODUtLjczMS45NTktLjk2MSBaIgoJICAgICAgZmlsbD0iI0YwRjZGQyIgLz4KCTxjaXJjbGUgY3g9IjIxIiBjeT0iNyIgcj0iNC43NSIKCSAgICAgICAgZmlsbD0iI0VGNDQ0NCIgc3Ryb2tlPSIjMEQxMTE3IiBzdHJva2Utd2lkdGg9IjEuNSIgLz4KCTxwYXRoIGQ9Ik0yMSA0LjV2My4yIgoJICAgICAgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIxLjM1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIC8+Cgk8Y2lyY2xlIGN4PSIyMSIgY3k9IjkuNTUiIHI9IjAuNyIgZmlsbD0id2hpdGUiIC8+Cjwvc3ZnPgo=";function a(o){return typeof o==="object"&&o!==null&&!Array.isArray(o)}function d(o){return String(o).replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}function L(o){return String(o).replaceAll(/[^\w-]/gu,"")}function z(o,r=""){return typeof o==="string"?o:r}function U(o){return o instanceof Error?o.message:String(o)}function P(o){return new Date(o).toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"})}var H="github-up:last-summary";function D(){try{let o=sessionStorage.getItem(H);if(o===null)return null;let r=JSON.parse(o);if(!a(r))return null;let{savedAt:i,intervalMs:n,result:c}=r;if(typeof i!=="number"||typeof n!=="number")return null;return{savedAt:i,intervalMs:n,result:c}}catch{return null}}function S(o,r){try{sessionStorage.setItem(H,JSON.stringify({savedAt:Date.now(),intervalMs:r,result:o}))}catch{}}var R="https://www.githubstatus.com";var W={components(){return"/api/v2/components.json"},Incidents:{all(){return"/api/v2/incidents.json"},unresolved(){return"/api/v2/incidents/unresolved.json"}},ScheduledMaintenances:{active(){return"/api/v2/scheduled-maintenances/active.json"},all(){return"/api/v2/scheduled-maintenances.json"},upcoming(){return"/api/v2/scheduled-maintenances/upcoming.json"}},status(){return"/api/v2/status.json"},subscriber(o){return`/api/v2/subscribers/${encodeURIComponent(o)}.json`},subscribers(){return"/api/v2/subscribers.json"},summary(){return"/api/v2/summary.json"}};function x(o){return typeof o==="object"&&o!==null&&!Array.isArray(o)}function A(o){return x(o)&&typeof o.name==="string"&&typeof o.status==="string"}function ho(o){return x(o)&&typeof o.indicator==="string"&&typeof o.description==="string"}function mo(o){return A(o)&&x(o)&&typeof o.impact==="string"}function wo(o){return x(o)&&ho(o.status)&&Array.isArray(o.components)&&o.components.every(A)&&Array.isArray(o.incidents)&&o.incidents.every(mo)}async function ko(o){try{let r=await o.text(),i=r.length>0?`: ${r}`:"";return`Request failed with status code ${o.status}${i}`}catch{let{status:r,statusText:i}=o;return`Request failed with status code ${r}: ${i}`}}async function j(o=R){try{let r=await fetch(new URL(W.summary(),o));if(!r.ok){let{headers:n}=r;return{headers:n,kind:"unknown",reason:await ko(r)}}let i=await r.json();if(!wo(i))return{headers:r.headers,kind:"unknown",reason:"unexpected response shape from status API"};return{headers:r.headers,kind:"ok",summary:i}}catch(r){return{kind:"unknown",reason:r instanceof Error?r.message:String(r)}}}var C="data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjggMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+Cgk8Y2lyY2xlIGN4PSIxNCIgY3k9IjE0IiByPSIxNCIgZmlsbD0iIzMwMzYzRCIgLz4KCTxwYXRoIGQ9Ik0xMi4wMDcgMTguNjE0CgkgICAgICAgICBjLTMuMDE0LS4zNjUtNS4xMzYtMi41MzQtNS4xMzYtNS4zNDMgMC0xLjE0MS40MTEtMi4zNzUgMS4wOTYtMy4xOTctLjI5Ni0uNzU0LS4yNTEtMi4zNTMuMDkxLTMuMDE0LjkxNC0uMTE0IDIuMTQ3LjM2NSAyLjg3OCAxLjAyOC44NjctLjI3NCAxLjc4MS0uNDExIDIuOS0uNDExCgkgICAgICAgICBzMi4wMzMuMTM3IDIuODU1LjM4OAoJICAgICAgICAgYy43MDgtLjY0IDEuOTYzLTEuMTE5IDIuODc4LTEuMDA1LjMxOS42MTcuMzY1IDIuMjE2LjA2OCAyLjk5Mi43MzEuODY4IDEuMTE5IDIuMDMzIDEuMTE5IDMuMjIgMCAyLjgwOS0yLjEyNCA0LjkzMi01LjE4MyA1LjMyLjc3Ni41MDMgMS4zMDIgMS41OTkgMS4zMDIgMi44NTUKCSAgICAgICAgIHYyLjM3NQoJICAgICAgICAgYzAgLjY4NS41NzEgMS4wNzQgMS4yNTYuNzk5IDQuMTMzLTEuNTc2IDcuMzc2LTUuNzA4IDcuMzc2LTEwLjgyMSAwLTYuNDYyLTUuMjUxLTExLjczNS0xMS43MTMtMTEuNzM1CgkgICAgICAgICBTMi4xMjIgNy4zMzggMi4xMjIgMTMuOCBhMTEuNTE3IDExLjUxNyAwIDAgMCA3LjU1OCAxMC44NDQKCSAgICAgICAgIGMuNjE3LjIyOCAxLjIxMS0uMTgzIDEuMjExLS43OTkgdi0xLjgyNyBhMi45NDEgMi45NDEgMCAwIDEtMS4wOTYuMjI4CgkgICAgICAgICBjLTEuNTA3IDAtMi4zOTgtLjgyMi0zLjAzNy0yLjM1Mi0uMjUyLS42MTYtLjUyNi0uOTgxLTEuMDUxLTEuMDUtLjI3NC0uMDIzLS4zNjUtLjEzNy0uMzY1LS4yNzQgMC0uMjc0LjQ1Ni0uNDguOTEzLS40OC42NjMgMCAxLjIzNC40MTEgMS44MjcgMS4yNTYuNDU3LjY2Mi45MzYuOTU5IDEuNTA3Ljk1OQoJICAgICAgICAgcy45MzYtLjIwNSAxLjQ2MS0uNzMgYy4zODgtLjM4OS42ODUtLjczMS45NTktLjk2MSBaIgoJICAgICAgZmlsbD0iI0YwRjZGQyIgLz4KCTxjaXJjbGUgY3g9IjIxIiBjeT0iNyIgcj0iNC43NSIKCSAgICAgICAgZmlsbD0iIzhCOTQ5RSIgc3Ryb2tlPSIjMzAzNjNEIiBzdHJva2Utd2lkdGg9IjEuNSIgLz4KCTxwYXRoIGQ9Ik0xOC45IDdoNC4yIgoJICAgICAgc3Ryb2tlPSJ3aGl0ZSIgc3Ryb2tlLXdpZHRoPSIxLjM1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIC8+Cjwvc3ZnPgo=";function l(o){let r=document.querySelector(`#${o}`);if(r instanceof HTMLElement)return r;throw Error(`Missing #${o}`)}function yo(){let o=document.querySelector('link[rel~="icon"]');if(o instanceof HTMLLinkElement)return o;let r=document.createElement("link");return r.rel="icon",r.type="image/svg+xml",r.sizes.value="any",document.head.append(r),r}var t={lastUpdated:l("last-updated"),hero:l("hero"),heroIcon:l("hero-icon"),heroSub:l("hero-sub"),heroTitle:l("hero-title"),incidentsList:l("incidents-list"),componentsGrid:l("components-grid"),favicon:yo()},E={polling:G,idle:C};function p(o){let r=o?E.polling:E.idle;if(t.favicon.getAttribute("href")===r)return;let i=document.createElement("link");i.id="favicon",i.rel="icon",i.type="image/svg+xml",i.sizes.value="any",i.href=r,t.favicon.replaceWith(i),t.favicon=i}var N=1e4;function Mo(o){return Math.min(Math.max(o,3000),60000)}function O(o,r){if(o===null)return null;for(let i of o.split(",")){let[n,c]=i.trim().split("=");if(n===void 0||c===void 0)continue;if(n.toLowerCase()!==r)continue;let e=Number(c.trim().replace(/^"|"$/g,""));if(Number.isFinite(e)&&e>0)return e}return null}function T(o){let r=o.get("cache-control"),i=O(r,"s-maxage")??O(r,"max-age");if(i===null)return 1e4;return Mo(i*1000)}var K=`<svg width="18" height="18" viewBox="0 0 24 24"
|
|
87
|
+
fill="none" stroke="currentColor" stroke-width="2"
|
|
88
|
+
xmlns="http://www.w3.org/2000/svg">
|
|
89
|
+
<path d="M20 6L9 17l-5-5" />
|
|
90
|
+
</svg>
|
|
91
|
+
`;var I=`<svg width="32" height="32" viewBox="0 0 24 24"
|
|
92
|
+
fill="none" stroke="currentColor" stroke-width="2.5"
|
|
93
|
+
xmlns="http://www.w3.org/2000/svg">
|
|
94
|
+
<path d="M20 6L9 17l-5-5" />
|
|
95
|
+
</svg>
|
|
96
|
+
`;var oo=`<svg width="32" height="32" viewBox="0 0 24 24"
|
|
97
|
+
fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round"
|
|
98
|
+
xmlns="http://www.w3.org/2000/svg">
|
|
99
|
+
<path d="M3.75 12h3.5l2.25-5.5 4 11 2.25-5.5h4.5" />
|
|
100
|
+
<path d="M18 5.75v3.5" />
|
|
101
|
+
<circle cx="18" cy="13.75" r="1" fill="currentColor" stroke="none" />
|
|
102
|
+
</svg>
|
|
103
|
+
`;var jo={operational:"Operational",degraded_performance:"Degraded",partial_outage:"Partial Outage",major_outage:"Major Outage",under_maintenance:"Maintenance"},ro="idle",F,_,V,B,io;function u(o){if(ro===o)return;F=void 0,_=void 0,V=void 0,B=void 0,ro=o}function Fo(o){return o==="operational"||o==="degraded_performance"||o==="partial_outage"||o==="major_outage"||o==="under_maintenance"}function _o(o){if(Fo(o))return{label:jo[o]??o,className:o};return{label:o,className:"unknown"}}function Vo(o){let i=/^(.+?)\s+\(([^)]+)\)\s*$/.exec(o);if(i===null||i[1]===void 0||i[2]===void 0)return{main:o,domain:null};return{main:i[1],domain:i[2]}}function no(o){let r=o.toLocaleTimeString();if(r===io)return;io=r,t.lastUpdated.textContent=`${r}`}function Z(o,r){u("error");let i=`${o}\x00${r}`;if(i===B)return;B=i,t.heroTitle.textContent=o,t.heroSub.textContent=r,t.hero.className="hero error",t.heroIcon.className="hero-icon error",t.incidentsList.innerHTML="",t.componentsGrid.innerHTML=""}function Bo(o,r){let{indicator:i,reportedIndicator:n}=o;if(i==="none")return r===0?"No active incidents":`${r} active incident${r===1?"":"s"}`;return n!==void 0&&n!==i?`Indicator: ${i} (reported ${n})`:`Indicator: ${i}`}function to(o,r){u("success");let i=`${JSON.stringify(o)}\x00${r}`;if(i===F)return;F=i;let{indicator:n,description:c}=o,e=n==="none"?"ok":L(n);t.hero.className=`hero ${e}`,t.heroIcon.className=`hero-icon ${e}`,t.heroIcon.innerHTML=n==="none"?`${I}`:`${oo}`,t.heroTitle.textContent=c,t.heroSub.textContent=Bo(o,r)}function co(o){u("success");let r=JSON.stringify(o);if(r===_)return;if(_=r,o.length===0){t.incidentsList.innerHTML=`
|
|
104
|
+
<div class="empty-row">
|
|
105
|
+
${K}
|
|
106
|
+
No active incidents
|
|
107
|
+
</div>
|
|
108
|
+
`;return}t.incidentsList.innerHTML=o.map((i)=>{let{name:n,impact:c,created_at:e,updated_at:w}=i,k=d(n),g=d(c),v=L(c),y=d(P(e)),M=d(P(w));return`
|
|
109
|
+
<div class="incident-card">
|
|
110
|
+
<div class="inc-header">
|
|
111
|
+
<span class="inc-name">${k}</span>
|
|
112
|
+
<span class="inc-badge ${v}">${g}</span>
|
|
113
|
+
</div>
|
|
114
|
+
<div class="inc-meta">
|
|
115
|
+
Created ${y} · Updated ${M}
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
`}).join("")}function eo(o){u("success");let r=JSON.stringify(o);if(r===V)return;V=r,t.componentsGrid.innerHTML=o.map((i)=>{let n=_o(i.status),c=Vo(i.name),{className:e,label:w}=n,{main:k,domain:g}=c,v=d(k),y=g===null?"":`<span class="comp-domain">${d(g)}</span>`,M=d(w);return`
|
|
119
|
+
<div class="comp-card">
|
|
120
|
+
<div class="comp-dot ${e}"></div>
|
|
121
|
+
<div class="comp-info">
|
|
122
|
+
<div class="comp-block">
|
|
123
|
+
<span class="comp-name">${v}</span>
|
|
124
|
+
${y}
|
|
125
|
+
</div>
|
|
126
|
+
<div class="comp-status ${e}">
|
|
127
|
+
${M}
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
`}).join("")}var $={none:0,minor:1,major:2,critical:3},Zo={none:"All Systems Operational",minor:"Minor Service Outage",major:"Major Service Outage",critical:"Critical Service Outage"};function $o(o){return typeof o==="string"&&o in $}function Jo(o){return $o(o)?o:"critical"}function J(o,r){return $[r]>$[o]?r:o}function Qo(o){switch(o){case"operational":return"none";case"degraded_performance":case"under_maintenance":return"minor";case"partial_outage":case"major_outage":return"major";default:return"major"}}function Xo(o){switch(o){case"degraded_performance":case"partial_outage":case"major_outage":return!0;default:return!1}}function Yo(o){let r="none",i=0;for(let n of o){if(Xo(n.status))i+=1;r=J(r,Qo(n.status))}return i>=3?J(r,"major"):r}function ao(o,r=[]){return J(Jo(o),Yo(r))}function lo(o){return Zo[o]}function so(o){if(!a(o))return o instanceof Error?o.message:String(o);let r=o.reason??o.error??o.message;if(r instanceof Error)return r.message;if(typeof r==="string")return r;return"Unknown error"}function po(o){if(!a(o))return;return o.headers instanceof Headers?o.headers:void 0}function fo(o){if(!a(o))return!1;if(o.kind!==void 0)return o.kind!=="ok";if(o.ok!==void 0)return o.ok!==!0;if(o.success!==void 0)return o.success!==!0;return!1}function qo(o){if(!a(o))return o;return o.summary??o.data??o.value??o.result??o}function Go(o){if(a(o)){let r=z(o.indicator,"none");return{description:z(o.description,r==="none"?"All Systems Operational":`Indicator: ${r}`),indicator:r}}if(typeof o==="string")return{description:o,indicator:o==="operational"?"none":o};return{description:"Unknown status",indicator:"major"}}function s(o,r){return typeof o[r]==="string"}function Uo(o){return a(o)&&s(o,"name")&&s(o,"impact")&&s(o,"status")&&s(o,"created_at")&&s(o,"updated_at")}function Ho(o){return a(o)&&s(o,"name")&&s(o,"status")}function go(o){let r=qo(o);if(!a(r))throw Error("Invalid status response");let i=Go(r.status),n=Array.isArray(r.incidents)?r.incidents.filter(Uo):[],c=Array.isArray(r.components)?r.components.filter(Ho):[],e=ao(i.indicator,c);return{status:{description:e===i.indicator?i.description:lo(e),indicator:e,reportedIndicator:i.indicator},incidents:n,components:c}}var b,h=!1,m=N;function xo(o){m=o}function Q(o,r){if(no(r),fo(o)){Z("Error",so(o));return}let{status:i,incidents:n,components:c}=go(o);to(i,n.length),co(n),eo(c)}async function Do(){let o=await j();Q(o,new Date);let r=po(o),i=r===void 0?m:T(r);return{result:o,intervalMs:i}}function X(){if(b===void 0)return;window.clearTimeout(b),b=void 0}function f(){X(),p(!1)}function Y(o=m){if(X(),document.visibilityState!=="visible"){f();return}p(!0),b=window.setTimeout(()=>{uo()},Math.max(0,o))}function q(){if(document.visibilityState!=="visible"){f();return}if(p(!0),h)return;uo()}async function uo(){if(X(),document.visibilityState!=="visible"){f();return}if(h)return;h=!0,p(!0);try{let{result:o,intervalMs:r}=await Do();m=r,S(o,r)}catch(o){Z("Exception",U(o))}finally{h=!1}Y()}function So(){let o=D();if(o!==null){let r=Date.now()-o.savedAt;if(r>=0&&r<o.intervalMs){Q(o.result,new Date(o.savedAt)),xo(o.intervalMs),Y(o.intervalMs-r);return}}q()}document.addEventListener("visibilitychange",()=>{if(document.visibilityState==="visible")q();else f()});So();
|
|
132
|
+
</script></body>
|
|
133
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "github-up",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Is GitHub down? Probably...",
|
|
5
|
+
"homepage": "https://github-up.kjanat.dev/",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://github.com/kjanat/github-up/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/kjanat/github-up.git"
|
|
12
|
+
},
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"author": "Kaj Kowalski <info@kajkowalski.nl> (https://github.com/kjanat)",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"github",
|
|
17
|
+
"cli",
|
|
18
|
+
"downdetector",
|
|
19
|
+
"dreamcli",
|
|
20
|
+
"statuspage",
|
|
21
|
+
"ansispeck"
|
|
22
|
+
],
|
|
23
|
+
"type": "module",
|
|
24
|
+
"jsdelivr": "./dist/browser/browser.js",
|
|
25
|
+
"imports": {
|
|
26
|
+
"#github-up": "./src/index.ts",
|
|
27
|
+
"#github-up/cli": "./src/cli/index.ts",
|
|
28
|
+
"#github-up/browser": "./src/browser.ts",
|
|
29
|
+
"#github-up/*": "./src/*.ts",
|
|
30
|
+
"#demo/*": "./demo/*.ts",
|
|
31
|
+
"#pkg": "./package.json",
|
|
32
|
+
"#test/*": "./test/*"
|
|
33
|
+
},
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"types": "./dist/index.d.mts",
|
|
37
|
+
"default": "./dist/index.mjs"
|
|
38
|
+
},
|
|
39
|
+
"./browser": {
|
|
40
|
+
"types": "./dist/browser/browser.d.ts",
|
|
41
|
+
"default": "./dist/browser/browser.js"
|
|
42
|
+
},
|
|
43
|
+
"./package.json": "./package.json"
|
|
44
|
+
},
|
|
45
|
+
"bin": {
|
|
46
|
+
"github-up": "dist/bin/cli.mjs"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"dist",
|
|
50
|
+
"README.md",
|
|
51
|
+
"LICENSE",
|
|
52
|
+
"package.json"
|
|
53
|
+
],
|
|
54
|
+
"scripts": {
|
|
55
|
+
"bd": "runner build",
|
|
56
|
+
"build": "bun --bun build.ts",
|
|
57
|
+
"build:watch": "runner build --watch",
|
|
58
|
+
"check": "biome check",
|
|
59
|
+
"fmt": "bunx dprint fmt",
|
|
60
|
+
"lint": "biome lint",
|
|
61
|
+
"local": "bun run build -l warn && bun link",
|
|
62
|
+
"prepublishOnly": "bun run build -l warn",
|
|
63
|
+
"publishChecks": "e18e-cli analyze; attw; bunx publint --pack bun --strict",
|
|
64
|
+
"test": "bun test",
|
|
65
|
+
"typecheck": "tsc --noEmit",
|
|
66
|
+
"dev": "runner build:watch",
|
|
67
|
+
"preview": "bun start",
|
|
68
|
+
"site:build": "runner build",
|
|
69
|
+
"prestart": "runner build",
|
|
70
|
+
"start": "bunx serve dist/site",
|
|
71
|
+
"publish:npm": "npm view \"$(jq -r '\"\\(.name)@\\(.version)\"' package.json)\" version >/dev/null 2>&1 && echo already published, skipping || bunx npm@11 publish",
|
|
72
|
+
"publish:pkg-pr-new": "bunx pkg-pr-new publish --bun --bin --packageManager=npm,bun,pnpm"
|
|
73
|
+
},
|
|
74
|
+
"dependencies": {
|
|
75
|
+
"@kjanat/dreamcli": "^3.0.0-rc.6",
|
|
76
|
+
"ansispeck": "^0.1.2"
|
|
77
|
+
},
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@biomejs/biome": ">=2.5,<2.5.3",
|
|
80
|
+
"@types/bun": "^1.3",
|
|
81
|
+
"@types/node": "^26",
|
|
82
|
+
"runner-run": "^0.19.1",
|
|
83
|
+
"statuspage.io": "^3.5",
|
|
84
|
+
"tsdown": "^0.22",
|
|
85
|
+
"typescript": "^7"
|
|
86
|
+
},
|
|
87
|
+
"packageManager": "bun@1.3.14",
|
|
88
|
+
"engines": {
|
|
89
|
+
"bun": ">=1.3",
|
|
90
|
+
"node": ">=22.18"
|
|
91
|
+
},
|
|
92
|
+
"devEngines": {
|
|
93
|
+
"packageManager": [
|
|
94
|
+
{
|
|
95
|
+
"name": "bun",
|
|
96
|
+
"onFail": "warn"
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
"runtime": [
|
|
100
|
+
{
|
|
101
|
+
"name": "bun",
|
|
102
|
+
"onFail": "warn"
|
|
103
|
+
}
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
"volta": {
|
|
107
|
+
"node": "26.5.0",
|
|
108
|
+
"npm": "11.18.0"
|
|
109
|
+
},
|
|
110
|
+
"publishConfig": {
|
|
111
|
+
"access": "public",
|
|
112
|
+
"tag": "latest"
|
|
113
|
+
}
|
|
114
|
+
}
|