From d9a5ac8e61df6476bb38a6f9359e6691760adf00 Mon Sep 17 00:00:00 2001 From: Steven Yau Date: Wed, 1 Jul 2026 18:08:36 +0100 Subject: [PATCH 1/8] first commit --- .gitignore | 1 + packages/steven-gh-in-out/.env.example | 16 ++ packages/steven-gh-in-out/package.json | 21 +++ packages/steven-gh-in-out/pnpm-lock.yaml | 35 ++++ packages/steven-gh-in-out/pnpm-workspace.yaml | 2 + .../skills/gather-bot/SKILL.md | 18 +++ .../gather-smart-object-webhooks/SKILL.md | 153 ++++++++++++++++++ packages/steven-gh-in-out/src/config.js | 57 +++++++ .../steven-gh-in-out/src/gather/sender.js | 39 +++++ .../steven-gh-in-out/src/github/client.js | 122 ++++++++++++++ packages/steven-gh-in-out/src/index.js | 33 ++++ packages/steven-gh-in-out/src/ping.js | 16 ++ .../src/polls/github-pr-review-count.js | 36 +++++ .../polls/github-review-requested-pr-count.js | 39 +++++ .../steven-gh-in-out/src/polls/registry.js | 33 ++++ packages/steven-gh-in-out/src/polls/types.js | 17 ++ .../src/publishers/gather-counter.js | 10 ++ packages/steven-gh-in-out/src/reset.js | 35 ++++ packages/steven-gh-in-out/src/runner.js | 18 +++ 19 files changed, 701 insertions(+) create mode 100644 packages/steven-gh-in-out/.env.example create mode 100644 packages/steven-gh-in-out/package.json create mode 100644 packages/steven-gh-in-out/pnpm-lock.yaml create mode 100644 packages/steven-gh-in-out/pnpm-workspace.yaml create mode 100644 packages/steven-gh-in-out/skills/gather-bot/SKILL.md create mode 100644 packages/steven-gh-in-out/skills/gather-smart-object-webhooks/SKILL.md create mode 100644 packages/steven-gh-in-out/src/config.js create mode 100644 packages/steven-gh-in-out/src/gather/sender.js create mode 100644 packages/steven-gh-in-out/src/github/client.js create mode 100644 packages/steven-gh-in-out/src/index.js create mode 100644 packages/steven-gh-in-out/src/ping.js create mode 100644 packages/steven-gh-in-out/src/polls/github-pr-review-count.js create mode 100644 packages/steven-gh-in-out/src/polls/github-review-requested-pr-count.js create mode 100644 packages/steven-gh-in-out/src/polls/registry.js create mode 100644 packages/steven-gh-in-out/src/polls/types.js create mode 100644 packages/steven-gh-in-out/src/publishers/gather-counter.js create mode 100644 packages/steven-gh-in-out/src/reset.js create mode 100644 packages/steven-gh-in-out/src/runner.js diff --git a/.gitignore b/.gitignore index 06c3eac..c938c74 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules/ coverage/ dist/ +.DS_Store diff --git a/packages/steven-gh-in-out/.env.example b/packages/steven-gh-in-out/.env.example new file mode 100644 index 0000000..73485c5 --- /dev/null +++ b/packages/steven-gh-in-out/.env.example @@ -0,0 +1,16 @@ +# GitHub — fine-grained or classic PAT with repo read access. +# Fine-grained tokens need Pull requests + Issues (read), and Contents (read) for private repo search. +GITHUB_TOKEN= +# Repository to watch (owner/repo) +GITHUB_REPO=your-org/your-repo + +# Gather Smart Object webhook — open PRs you authored or are assigned to +GATHER_WEBHOOK_URL=https://api.v2.staging.gather.town/api/v2/hooks/spaces/.../objects/... +GATHER_WEBHOOK_SECRET=whsec_... + +# Gather Smart Object webhook — open PRs that directly named you as reviewer, not yet approved (last 7 days) +GATHER_REVIEW_WEBHOOK_URL=https://api.v2.staging.gather.town/api/v2/hooks/spaces/.../objects/... +GATHER_REVIEW_WEBHOOK_SECRET=whsec_... + +# How often to poll all registered sources (default: 10 minutes) +POLL_INTERVAL_MS=600000 diff --git a/packages/steven-gh-in-out/package.json b/packages/steven-gh-in-out/package.json new file mode 100644 index 0000000..146cd84 --- /dev/null +++ b/packages/steven-gh-in-out/package.json @@ -0,0 +1,21 @@ +{ + "name": "gather-bot", + "version": "0.0.0", + "private": true, + "type": "module", + "description": "Gather bot", + "engines": { + "node": ">=20", + "pnpm": ">=10.16.0" + }, + "packageManager": "pnpm@10.16.0", + "scripts": { + "prepare": "corepack enable", + "start": "node --env-file=.env src/index.js", + "ping": "node --env-file=.env src/ping.js", + "reset": "node --env-file=.env src/reset.js" + }, + "dependencies": { + "standardwebhooks": "^1.0.0" + } +} diff --git a/packages/steven-gh-in-out/pnpm-lock.yaml b/packages/steven-gh-in-out/pnpm-lock.yaml new file mode 100644 index 0000000..97214d1 --- /dev/null +++ b/packages/steven-gh-in-out/pnpm-lock.yaml @@ -0,0 +1,35 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + standardwebhooks: + specifier: ^1.0.0 + version: 1.0.0 + +packages: + + '@stablelib/base64@1.0.1': + resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + + fast-sha256@1.3.0: + resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} + + standardwebhooks@1.0.0: + resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} + +snapshots: + + '@stablelib/base64@1.0.1': {} + + fast-sha256@1.3.0: {} + + standardwebhooks@1.0.0: + dependencies: + '@stablelib/base64': 1.0.1 + fast-sha256: 1.3.0 diff --git a/packages/steven-gh-in-out/pnpm-workspace.yaml b/packages/steven-gh-in-out/pnpm-workspace.yaml new file mode 100644 index 0000000..0ccb1b9 --- /dev/null +++ b/packages/steven-gh-in-out/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +minimumReleaseAge: 10080 +minimumReleaseAgeStrict: true diff --git a/packages/steven-gh-in-out/skills/gather-bot/SKILL.md b/packages/steven-gh-in-out/skills/gather-bot/SKILL.md new file mode 100644 index 0000000..f9de15b --- /dev/null +++ b/packages/steven-gh-in-out/skills/gather-bot/SKILL.md @@ -0,0 +1,18 @@ +--- +name: gather-bot +description: >- + Project context for gather-bot. Use when working in this repository to + understand tooling, dependency policy, and where to add project skills. +--- + +# Gather Bot + +## Package management + +- Uses **pnpm** via Corepack (`packageManager` in `package.json`). If `pnpm` is missing, run `corepack enable` once. +- **Minimum release age: 7 days.** Dependencies published within the last week are not installed. Settings live in `pnpm-workspace.yaml` (`minimumReleaseAge: 10080` minutes) and `.npmrc` (`min-release-age=7` for npm 11.10+). +- Do not bypass the release-age gate unless the user explicitly asks (e.g. `--min-release-age=0`). + +## Project skills + +Add reusable agent workflows under `skills//SKILL.md`. Each skill is a directory with YAML frontmatter (`name`, `description`) and markdown instructions. Cursor discovers them via the `.cursor/skills` symlink to this folder. Do not put project skills in `~/.cursor/skills-cursor/` (Cursor-managed only). diff --git a/packages/steven-gh-in-out/skills/gather-smart-object-webhooks/SKILL.md b/packages/steven-gh-in-out/skills/gather-smart-object-webhooks/SKILL.md new file mode 100644 index 0000000..be2a50d --- /dev/null +++ b/packages/steven-gh-in-out/skills/gather-smart-object-webhooks/SKILL.md @@ -0,0 +1,153 @@ +--- +name: gather-smart-object-webhooks +description: >- + Wire external systems to Gather Smart Objects via signed webhooks (Standard + Webhooks v1). Use when creating bot webhooks, connecting CI/monitoring/CRM to + an Inbox or other Smart Object, signing POST requests with whsec_ secrets, or + calling activity.add, info.set, and webhook.ping. +--- + +> **Source of truth: the live endpoint.** Before sending real events, run the `webhook.ping` self-check below — its `pong` response confirms the contract and the object's actual capabilities. If anything here contradicts the live response, trust the endpoint. + +# Wire up a Gather Smart Object (`Inbox` / `inbox` preset) + +You're connecting an external system (CI, monitoring, a CRM, a cron job…) to a Smart Object in a Gather space. Your system signs and POSTs events to one URL; the object updates live in the space. + +**Each Smart Object is independent, and so is its secret.** The URL and signing secret below belong to _this one object only_ — a key generated for one object will **not** authenticate any other (you'll get `404 not_found`), and there's no space-wide or account-wide key. To wire up several objects, reuse the `createSmartObjectSender` factory below — one instance per object, each with its own URL and its own secret env var. The factory is identical for every object, so define it once and import it; only the `(url, secret)` pair differs. Regenerating an object's key immediately invalidates its previous one. + +## Your configuration + +| Setting | Value | +| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Webhook URL | `https://api.v2.staging.gather.town/api/v2/hooks/spaces/eb638dc6-570a-43ce-822f-c2a3f0fd9f3b/objects/17554f00-b6f6-41a9-8a19-d8a71bb0b9df` | +| API key (signing secret) | `whsec_…` — **paste the key you copied** from the object's ⋮ menu → "Regenerate API key" (shown once). Store it in `GATHER_WEBHOOK_SECRET`; never commit or log it. | +| Signature scheme | Standard Webhooks v1 (`webhook-id` / `webhook-timestamp` / `webhook-signature`) | + +## Security & sanity checks — do these + +1. **Never store the secret in plain sight.** Read it from an env var / secret manager; never hardcode, commit, or log it (mask it if you must log a request). +2. **Validate the secret before use.** It must start with `whsec_`; fail fast if it's missing or truncated. +3. **Check the response status every time.** `200` = accepted. Do **not** blindly retry `4xx` (`400`/`404`/`410`/`415`) — fix the request. Retry only `5xx`/`503`, with backoff. +4. **Honor the rate-limit headers — don't DDoS Gather.** Responses carry `RateLimit-Limit` / `RateLimit-Remaining` / `RateLimit-Reset` (Unix seconds). Throttle proactively; on `429`, sleep until `RateLimit-Reset`; use exponential backoff with jitter. Limits: **60 req/min per space**, **100/min per IP** — coalesce bursts instead of one webhook per micro-event. +5. **Send a fresh timestamp.** The server rejects timestamps more than **±5 minutes** off. Stamp each request at send time; never replay an old signed body. +6. **Use a unique `webhook-id` per event, reused on retry.** The server dedupes the last 10 ids per object, so retrying the same id is idempotent; a new id is a new event. + +## Signing (Standard Webhooks — use a library) + +Auth follows the [Standard Webhooks](https://www.standardwebhooks.com/) spec, so **do not hand-roll the HMAC** — use the official `standardwebhooks` library for your language (JS/TS, Python, Go, Ruby, PHP, Rust, …). You give it the `whsec_…` secret + payload; it builds the headers and signature. + +- `POST` the webhook URL with `Content-Type: application/json`, body ≤ **4 KB**. +- `webhook-signature` is `v1,` over `` `${webhook-id}.${webhook-timestamp}.${rawBody}` `` (the library handles it). + +> **⚠️ Sign the exact bytes you send.** The HMAC covers the raw body bytes. Serialize the body once, sign that exact string, and send it unchanged — do **not** pretty-print/beautify, reorder keys, or re-serialize after signing, or you'll get the opaque `404 not_found`. + +Body shape: `{ "type": ".", "timestamp": "", "data": { … } }` (`timestamp` required except for `webhook.ping`). + +## Quick start (Node.js) + +```bash +npm install standardwebhooks +``` + +```javascript +import { Webhook } from "standardwebhooks" +import { randomUUID } from "node:crypto" + +// Define ONCE (identical for every object). Each Smart Object is just a (url, secret) pair — +// create one sender per object and never share a key between objects. +function createSmartObjectSender(url, secret) { + if (!/^whsec_.{20,}/.test(secret ?? "")) + throw new Error(`Smart Object secret for ${url} is missing or malformed`) + const webhook = new Webhook(secret) + return async function send(type, data = {}) { + const id = randomUUID() + const tsSeconds = Math.floor(Date.now() / 1000) + const body = JSON.stringify({ type, timestamp: new Date().toISOString(), data }) + const signature = webhook.sign(id, new Date(tsSeconds * 1000), body) + const res = await fetch(url, { + method: "POST", + headers: { + "content-type": "application/json", + "webhook-id": id, + "webhook-timestamp": String(tsSeconds), + "webhook-signature": signature, + }, + body, + }) + if (res.status === 429) { + const reset = Number(res.headers.get("RateLimit-Reset")) * 1000 + await new Promise((r) => setTimeout(r, Math.max(0, reset - Date.now()) + 250)) + return send(type, data) + } + if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`) + return res.json() + } +} + +// This object — paste the key you copied (e.g. whsec_PASTE_YOUR_API_KEY_HERE) into its own env var; never hardcode, commit, or log it: +const thisObject = createSmartObjectSender("https://api.v2.staging.gather.town/api/v2/hooks/spaces/eb638dc6-570a-43ce-822f-c2a3f0fd9f3b/objects/17554f00-b6f6-41a9-8a19-d8a71bb0b9df", process.env.GATHER_WEBHOOK_SECRET) + +// Each additional object reuses the SAME factory with its own URL + secret: +// const deployBoard = createSmartObjectSender(DEPLOY_BOARD_URL, process.env.GATHER_DEPLOY_BOARD_SECRET) +``` + +## What this object accepts + +This is an `inbox` Smart Object. Send `.` in `type`: + +### `info` +Base capability carrying a Smart Object's user-facing identity: a display `name` +and an optional `description`. Composed onto every preset (see `BASE_CAPABILITY_NAMES`) +so every webhook object has editable details regardless of integration type. + +- `info.set` — `name` (string, ≤120 chars, optional), `description` (string, ≤2000 chars, optional) + +### `activity` +`activity` capability: a bounded, newest-wins log of items for a Smart Object, rendered as a feed in +the details popover. Each entry is display-oriented, a sender-supplied `id` (stable identity), a +human-readable `text` line, and an optional `url` to link out to the source (a PR, an incident, etc). +The popover derives a favicon from the `url`'s hostname. + +Methods (wire-addressed as `activity.`): + - `add({ id, text, url? })`: add-or-update by `id`. + A newer event (by signed `at`) replaces the existing entry; a stale or out-of-order redelivery is ignored. + Entries are ordered by `at` and capped to the newest `ACTIVITY_BUFFER_SIZE`. `at` is + stamped from the signed dispatch context, never the args. + - `remove({ id })`: drop the entry with that id (e.g. an incident resolved). + +- `activity.add` — `id` (string, ≤128 chars, required), `text` (string, ≤500 chars, required), `url` (http/https URL, ≤2048 chars, optional) +- `activity.remove` — `id` (string, ≤128 chars, required) + +### `counter` +`counter` capability, a single non-negative integer state, or `null` when unset / cleared. + +Methods (addressed on the wire as `counter.`): + - `set({ count })`: overwrite the count. Negative / fractional values are rejected. + - `increment({ by? })`: add `by` (default `+1`) to the current count; a cleared count counts from 0. `by` must be a positive integer. + - `reset()`: clear the count back to unset (`null`). Presets that fall back when unset revert to their default. + +- `counter.set` — `count` (integer, ≥0, required) +- `counter.increment` — `by` (integer, >0, optional) +- `counter.reset` — no `data` + +**Reserved (every object):** +- `webhook.ping` — Protocol-level health check. Signed like any event but needs no `timestamp`/`data`; returns the object's current preset + capability state (`pong`). Use it to verify the secret and discover which capabilities the object accepts. + +## Verify setup first: `webhook.ping` + +```javascript +await thisObject("webhook.ping") // 200 { "status": "pong", preset, capabilities } +``` + +`pong` echoes the object's real capabilities — use it to confirm the secret works and what this object accepts. + +## Responses & errors + +- **200** `{"status":"dispatched"}` (space live) or `{"status":"space_idle"}` (space unloaded) — both mean accepted. +- **400** `invalid_request`/`invalid_args` — bad body/data; fix it. +- **404** `not_found` — uniform pre-auth failure: bad/missing signature, expired timestamp, wrong URL, oversized body, or a reformatted body. Check the secret, timestamp skew, and that you sent the exact signed bytes. +- **404** `capability_not_declared` — the object doesn't support that `type` (run `webhook.ping`). +- **410** `token_revoked` — key was regenerated; get the new one. +- **415** — set `Content-Type: application/json`. +- **429** — rate limited; honor `RateLimit-Reset`. +- **503** — transient; includes `Retry-After`. diff --git a/packages/steven-gh-in-out/src/config.js b/packages/steven-gh-in-out/src/config.js new file mode 100644 index 0000000..e952871 --- /dev/null +++ b/packages/steven-gh-in-out/src/config.js @@ -0,0 +1,57 @@ +const WHSEC_PATTERN = /^whsec_.{20,}/ +const WEBHOOK_URL_PATTERN = + /^https:\/\/api\.v2\.(?:staging\.)?gather\.town\/api\/v2\/hooks\/spaces\/[0-9a-f-]{36}\/objects\/[0-9a-f-]{36}$/i + +function required(name) { + const value = process.env[name]?.trim() + if (!value) throw new Error(`Missing required env var: ${name}`) + return value +} + +function parseRepo(value) { + const match = value.trim().match(/^([^/]+)\/([^/]+)$/) + if (!match) { + throw new Error(`GITHUB_REPO must be owner/repo (got ${JSON.stringify(value)})`) + } + return { owner: match[1], name: match[2] } +} + +function loadGatherWebhook(urlEnv, secretEnv) { + const secret = required(secretEnv) + if (!WHSEC_PATTERN.test(secret)) { + throw new Error(`${secretEnv} is missing or malformed (expected whsec_…)`) + } + + const url = required(urlEnv) + if (!WEBHOOK_URL_PATTERN.test(url)) { + throw new Error( + `${urlEnv} is invalid — copy the full URL from the Smart Object setup (spaces/{uuid}/objects/{uuid})`, + ) + } + + return { url, secret } +} + +export function loadConfig() { + const githubRepo = parseRepo(required("GITHUB_REPO")) + const pollIntervalMs = Number(process.env.POLL_INTERVAL_MS ?? "600000") + if (!Number.isFinite(pollIntervalMs) || pollIntervalMs < 10_000) { + throw new Error("POLL_INTERVAL_MS must be a number >= 10000") + } + + return { + gather: { + openPrs: loadGatherWebhook("GATHER_WEBHOOK_URL", "GATHER_WEBHOOK_SECRET"), + reviewRequested: loadGatherWebhook( + "GATHER_REVIEW_WEBHOOK_URL", + "GATHER_REVIEW_WEBHOOK_SECRET", + ), + }, + github: { + token: required("GITHUB_TOKEN"), + owner: githubRepo.owner, + repo: githubRepo.name, + }, + pollIntervalMs, + } +} diff --git a/packages/steven-gh-in-out/src/gather/sender.js b/packages/steven-gh-in-out/src/gather/sender.js new file mode 100644 index 0000000..d2a4130 --- /dev/null +++ b/packages/steven-gh-in-out/src/gather/sender.js @@ -0,0 +1,39 @@ +import { randomUUID } from "node:crypto" +import { Webhook } from "standardwebhooks" + +const WHSEC_PATTERN = /^whsec_.{20,}/ + +export function createSmartObjectSender(url, secret) { + if (!WHSEC_PATTERN.test(secret ?? "")) { + throw new Error(`Smart Object secret for ${url} is missing or malformed`) + } + + const webhook = new Webhook(secret) + + return async function send(type, data = {}) { + const id = randomUUID() + const tsSeconds = Math.floor(Date.now() / 1000) + const body = JSON.stringify({ type, timestamp: new Date().toISOString(), data }) + const signature = webhook.sign(id, new Date(tsSeconds * 1000), body) + + const res = await fetch(url, { + method: "POST", + headers: { + "content-type": "application/json", + "webhook-id": id, + "webhook-timestamp": String(tsSeconds), + "webhook-signature": signature, + }, + body, + }) + + if (res.status === 429) { + const reset = Number(res.headers.get("RateLimit-Reset")) * 1000 + await new Promise((r) => setTimeout(r, Math.max(0, reset - Date.now()) + 250)) + return send(type, data) + } + + if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`) + return res.json() + } +} diff --git a/packages/steven-gh-in-out/src/github/client.js b/packages/steven-gh-in-out/src/github/client.js new file mode 100644 index 0000000..fe5db71 --- /dev/null +++ b/packages/steven-gh-in-out/src/github/client.js @@ -0,0 +1,122 @@ +const GITHUB_API = "https://api.github.com" + +export const PR_CUTOFF_DAYS = 14 +export const REVIEW_REQUEST_CUTOFF_DAYS = 7 + +/** + * @typedef {{ id: number, number: number, title: string, html_url: string }} GitHubPullRequest + */ + +/** @param {number} days */ +export function cutoffSinceDate(days) { + const cutoff = new Date() + cutoff.setDate(cutoff.getDate() - days) + return cutoff.toISOString().slice(0, 10) +} + +/** @returns {string} YYYY-MM-DD for GitHub `created:>=` search qualifier */ +export function prCreatedSinceDate() { + return cutoffSinceDate(PR_CUTOFF_DAYS) +} + +/** @returns {string} YYYY-MM-DD — last week for review-requested PR search */ +export function reviewRequestSinceDate() { + return cutoffSinceDate(REVIEW_REQUEST_CUTOFF_DAYS) +} + +/** + * @param {string} token + */ +export function githubHeaders(token) { + return { + accept: "application/vnd.github+json", + authorization: `Bearer ${token}`, + "x-github-api-version": "2022-11-28", + } +} + +/** + * @param {string} token + * @returns {Promise} + */ +export async function fetchGitHubLogin(token) { + const res = await fetch(`${GITHUB_API}/user`, { + headers: githubHeaders(token), + }) + if (!res.ok) { + throw new Error(`GitHub /user failed (${res.status}): ${await res.text()}`) + } + const user = await res.json() + if (!user.login) { + throw new Error("GitHub /user response missing login") + } + return user.login +} + +/** + * @param {string} token + * @param {string} query + * @param {{ perPage?: number }} [options] + * @returns {Promise} + */ +export async function searchIssues(token, query, { perPage = 100 } = {}) { + const url = new URL("/search/issues", GITHUB_API) + url.searchParams.set("q", query) + url.searchParams.set("per_page", String(perPage)) + + const res = await fetch(url, { headers: githubHeaders(token) }) + if (!res.ok) { + throw new Error(`GitHub search failed (${res.status}): ${await res.text()}`) + } + const payload = await res.json() + return payload.items ?? [] +} + +/** + * Open, ready-for-review PRs you opened or are assigned to. Search API rejects + * OR on user qualifiers, so merge author + assignee results and dedupe. + * + * @param {string} token + * @param {{ owner: string, repo: string, login: string }} target + * @returns {Promise} + */ +export async function listOpenPrsForUser(token, { owner, repo, login }) { + const createdSince = prCreatedSinceDate() + const base = `repo:${owner}/${repo} is:pr is:open draft:false created:>=${createdSince}` + const [authored, assigned] = await Promise.all([ + searchIssues(token, `${base} author:${login}`), + searchIssues(token, `${base} assignee:${login}`), + ]) + + const byId = new Map() + for (const item of [...authored, ...assigned]) { + byId.set(item.id, item) + } + + return [...byId.values()].sort((a, b) => a.number - b.number) +} + +/** + * Open PRs that directly requested you as a reviewer (not via a team), not yet + * approved by anyone, created in the last week. + * + * @param {string} token + * @param {{ owner: string, repo: string, login: string }} target + * @returns {Promise} + */ +export async function listOpenReviewRequestedPrsForUser(token, { owner, repo, login }) { + const since = reviewRequestSinceDate() + const query = `repo:${owner}/${repo} is:pr is:open user-review-requested:${login} -review:approved created:>=${since}` + const items = await searchIssues(token, query) + return items.sort((a, b) => a.number - b.number) +} + +/** + * @param {string} token + * @param {{ owner: string, repo: string, login: string }} target + * @returns {Promise} + */ +export async function countOpenPrsForUser(token, target) { + const prs = await listOpenPrsForUser(token, target) + return prs.length +} diff --git a/packages/steven-gh-in-out/src/index.js b/packages/steven-gh-in-out/src/index.js new file mode 100644 index 0000000..7d8f4c8 --- /dev/null +++ b/packages/steven-gh-in-out/src/index.js @@ -0,0 +1,33 @@ +import { loadConfig } from "./config.js" +import { createSmartObjectSender } from "./gather/sender.js" +import { fetchGitHubLogin } from "./github/client.js" +import { createPollEntries } from "./polls/registry.js" +import { runPollCycle } from "./runner.js" + +const config = loadConfig() + +const pollEntries = createPollEntries({ + openPrs: createSmartObjectSender( + config.gather.openPrs.url, + config.gather.openPrs.secret, + ), + reviewRequested: createSmartObjectSender( + config.gather.reviewRequested.url, + config.gather.reviewRequested.secret, + ), +}) + +const login = await fetchGitHubLogin(config.github.token) +const ctx = { github: { ...config.github, login } } + +async function tick() { + try { + await runPollCycle(ctx, pollEntries) + } catch (err) { + console.error("Poll cycle failed:", err) + } +} + +console.log(`Starting gather-bot as @${login} (poll every ${config.pollIntervalMs / 1000}s)`) +await tick() +setInterval(tick, config.pollIntervalMs) diff --git a/packages/steven-gh-in-out/src/ping.js b/packages/steven-gh-in-out/src/ping.js new file mode 100644 index 0000000..5200232 --- /dev/null +++ b/packages/steven-gh-in-out/src/ping.js @@ -0,0 +1,16 @@ +import { loadConfig } from "./config.js" +import { createSmartObjectSender } from "./gather/sender.js" + +const config = loadConfig() + +const targets = [ + { name: "open PRs (authored/assigned)", ...config.gather.openPrs }, + { name: "unapproved direct review-requested PRs", ...config.gather.reviewRequested }, +] + +for (const { name, url, secret } of targets) { + const send = createSmartObjectSender(url, secret) + const result = await send("webhook.ping") + console.log(`\n${name}:`) + console.log(JSON.stringify(result, null, 2)) +} diff --git a/packages/steven-gh-in-out/src/polls/github-pr-review-count.js b/packages/steven-gh-in-out/src/polls/github-pr-review-count.js new file mode 100644 index 0000000..4591c60 --- /dev/null +++ b/packages/steven-gh-in-out/src/polls/github-pr-review-count.js @@ -0,0 +1,36 @@ +/** + * @typedef {import("./types.js").PollContext} PollContext + * @typedef {import("./types.js").PollResult} PollResult + */ + +import { listOpenPrsForUser, prCreatedSinceDate } from "../github/client.js" + +/** + * Count open, ready-for-review PRs you opened or are assigned to. + * + * @param {PollContext} ctx + * @returns {Promise} + */ +export async function githubPrReviewCountPoll(ctx) { + const { owner, repo, token, login } = ctx.github + + const prs = await listOpenPrsForUser(token, { owner, repo, login }) + + console.log( + `[github-pr-review-count] ${prs.length} ready-for-review PR(s) since ${prCreatedSinceDate()} authored by or assigned to @${login}:`, + ) + if (prs.length === 0) { + console.log(" (none)") + } else { + for (const pr of prs) { + console.log(` #${pr.number} ${pr.title}`) + console.log(` ${pr.html_url}`) + } + } + + return { + pollId: "github-pr-review-count", + label: "GitHub PR reviews", + value: prs.length, + } +} diff --git a/packages/steven-gh-in-out/src/polls/github-review-requested-pr-count.js b/packages/steven-gh-in-out/src/polls/github-review-requested-pr-count.js new file mode 100644 index 0000000..86ef878 --- /dev/null +++ b/packages/steven-gh-in-out/src/polls/github-review-requested-pr-count.js @@ -0,0 +1,39 @@ +/** + * @typedef {import("./types.js").PollContext} PollContext + * @typedef {import("./types.js").PollResult} PollResult + */ + +import { + listOpenReviewRequestedPrsForUser, + reviewRequestSinceDate, +} from "../github/client.js" + +/** + * Open PRs that directly named you as a reviewer, not yet approved by anyone, in the last week. + * + * @param {PollContext} ctx + * @returns {Promise} + */ +export async function githubReviewRequestedPrCountPoll(ctx) { + const { owner, repo, token, login } = ctx.github + + const prs = await listOpenReviewRequestedPrsForUser(token, { owner, repo, login }) + + console.log( + `[github-review-requested-count] ${prs.length} unapproved direct review request(s) since ${reviewRequestSinceDate()} for @${login}:`, + ) + if (prs.length === 0) { + console.log(" (none)") + } else { + for (const pr of prs) { + console.log(` #${pr.number} ${pr.title}`) + console.log(` ${pr.html_url}`) + } + } + + return { + pollId: "github-review-requested-count", + label: "GitHub unapproved review-requested PRs", + value: prs.length, + } +} diff --git a/packages/steven-gh-in-out/src/polls/registry.js b/packages/steven-gh-in-out/src/polls/registry.js new file mode 100644 index 0000000..190a21c --- /dev/null +++ b/packages/steven-gh-in-out/src/polls/registry.js @@ -0,0 +1,33 @@ +import { githubPrReviewCountPoll } from "./github-pr-review-count.js" +import { githubReviewRequestedPrCountPoll } from "./github-review-requested-pr-count.js" +import { publishCounter } from "../publishers/gather-counter.js" + +/** @typedef {import("./types.js").Poll} Poll */ +/** @typedef {import("./types.js").PollResult} PollResult */ +/** @typedef {ReturnType} GatherSend */ + +/** + * @typedef {object} PollEntry + * @property {Poll} poll + * @property {GatherSend} send + * @property {(send: GatherSend, result: PollResult) => Promise} publish + */ + +/** + * @param {{ openPrs: GatherSend, reviewRequested: GatherSend }} senders + * @returns {PollEntry[]} + */ +export function createPollEntries(senders) { + return [ + { + poll: githubPrReviewCountPoll, + send: senders.openPrs, + publish: publishCounter, + }, + { + poll: githubReviewRequestedPrCountPoll, + send: senders.reviewRequested, + publish: publishCounter, + }, + ] +} diff --git a/packages/steven-gh-in-out/src/polls/types.js b/packages/steven-gh-in-out/src/polls/types.js new file mode 100644 index 0000000..ce67c62 --- /dev/null +++ b/packages/steven-gh-in-out/src/polls/types.js @@ -0,0 +1,17 @@ +/** + * @typedef {object} PollContext + * @property {{ owner: string, repo: string, token: string, login: string }} github + */ + +/** + * @typedef {object} PollResult + * @property {string} pollId Stable id for logs and future publishers. + * @property {string} label Human-readable poll name for logs. + * @property {number} value Numeric metric driving publishers (e.g. slot count). + */ + +/** + * @typedef {(ctx: PollContext) => Promise} Poll + */ + +export {} diff --git a/packages/steven-gh-in-out/src/publishers/gather-counter.js b/packages/steven-gh-in-out/src/publishers/gather-counter.js new file mode 100644 index 0000000..658651d --- /dev/null +++ b/packages/steven-gh-in-out/src/publishers/gather-counter.js @@ -0,0 +1,10 @@ +/** + * Publish a poll count to a Smart Object via counter.set. + * + * @param {ReturnType} send + * @param {import("../polls/types.js").PollResult} result + */ +export async function publishCounter(send, result) { + const count = Math.max(0, Math.floor(result.value)) + await send("counter.set", { count }) +} diff --git a/packages/steven-gh-in-out/src/reset.js b/packages/steven-gh-in-out/src/reset.js new file mode 100644 index 0000000..daddbc1 --- /dev/null +++ b/packages/steven-gh-in-out/src/reset.js @@ -0,0 +1,35 @@ +import { loadConfig } from "./config.js" +import { createSmartObjectSender } from "./gather/sender.js" + +/** Activity slot ids from the pre-counter inbox publisher. */ +const LEGACY_ACTIVITY_SLOT_IDS = [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "github-pr-review-count", +] + +const config = loadConfig() + +const targets = [ + { name: "open PRs (authored/assigned)", ...config.gather.openPrs }, + { name: "unapproved direct review-requested PRs", ...config.gather.reviewRequested }, +] + +for (const { name, url, secret } of targets) { + const send = createSmartObjectSender(url, secret) + + for (const id of LEGACY_ACTIVITY_SLOT_IDS) { + await send("activity.remove", { id }) + } + + const result = await send("counter.reset") + console.log(`${name}: cleared ${LEGACY_ACTIVITY_SLOT_IDS.length} legacy slots + counter (${JSON.stringify(result)})`) +} diff --git a/packages/steven-gh-in-out/src/runner.js b/packages/steven-gh-in-out/src/runner.js new file mode 100644 index 0000000..2e7f01d --- /dev/null +++ b/packages/steven-gh-in-out/src/runner.js @@ -0,0 +1,18 @@ +/** + * @typedef {import("./polls/types.js").PollContext} PollContext + * @typedef {import("./polls/registry.js").PollEntry} PollEntry + */ + +/** + * Run every registered poll once and publish results to Gather. + * + * @param {PollContext} ctx + * @param {PollEntry[]} pollEntries + */ +export async function runPollCycle(ctx, pollEntries) { + for (const { poll, publish, send } of pollEntries) { + const result = await poll(ctx) + console.log(`[${result.pollId}] ${result.label}: ${result.value}`) + await publish(send, result) + } +} From 1a6a6dc555c9dd68b8ecb993f3ad65696e434794 Mon Sep 17 00:00:00 2001 From: Steven Yau Date: Wed, 1 Jul 2026 18:23:50 +0100 Subject: [PATCH 2/8] After AI refactor --- .gitignore | 1 + package-lock.json | 583 ++++++++++++++++++ packages/steven-gh-in-out/README.md | 43 ++ packages/steven-gh-in-out/package.json | 44 +- packages/steven-gh-in-out/pnpm-lock.yaml | 35 -- packages/steven-gh-in-out/pnpm-workspace.yaml | 2 - .../skills/gather-bot/SKILL.md | 18 - .../gather-smart-object-webhooks/SKILL.md | 153 ----- packages/steven-gh-in-out/src/config.js | 57 -- packages/steven-gh-in-out/src/config.spec.ts | 18 + packages/steven-gh-in-out/src/config.ts | 71 +++ .../steven-gh-in-out/src/gather/sender.js | 39 -- .../steven-gh-in-out/src/github/client.js | 122 ---- .../src/github/client.spec.ts | 30 + .../steven-gh-in-out/src/github/client.ts | 105 ++++ packages/steven-gh-in-out/src/index.js | 33 - packages/steven-gh-in-out/src/index.ts | 49 ++ packages/steven-gh-in-out/src/ping.js | 16 - packages/steven-gh-in-out/src/ping.ts | 19 + .../src/polls/github-pr-review-count.js | 36 -- .../src/polls/github-pr-review-count.ts | 32 + .../polls/github-review-requested-pr-count.js | 39 -- .../polls/github-review-requested-pr-count.ts | 39 ++ .../steven-gh-in-out/src/polls/registry.js | 33 - .../steven-gh-in-out/src/polls/registry.ts | 29 + packages/steven-gh-in-out/src/polls/types.js | 17 - packages/steven-gh-in-out/src/polls/types.ts | 11 + .../src/publishers/gather-counter.js | 10 - .../src/publishers/gather-counter.ts | 15 + packages/steven-gh-in-out/src/reset.js | 35 -- packages/steven-gh-in-out/src/reset.ts | 45 ++ packages/steven-gh-in-out/src/runner.js | 18 - packages/steven-gh-in-out/src/runner.ts | 14 + pnpm-lock.yaml | 6 + 34 files changed, 1135 insertions(+), 682 deletions(-) create mode 100644 package-lock.json create mode 100644 packages/steven-gh-in-out/README.md delete mode 100644 packages/steven-gh-in-out/pnpm-lock.yaml delete mode 100644 packages/steven-gh-in-out/pnpm-workspace.yaml delete mode 100644 packages/steven-gh-in-out/skills/gather-bot/SKILL.md delete mode 100644 packages/steven-gh-in-out/skills/gather-smart-object-webhooks/SKILL.md delete mode 100644 packages/steven-gh-in-out/src/config.js create mode 100644 packages/steven-gh-in-out/src/config.spec.ts create mode 100644 packages/steven-gh-in-out/src/config.ts delete mode 100644 packages/steven-gh-in-out/src/gather/sender.js delete mode 100644 packages/steven-gh-in-out/src/github/client.js create mode 100644 packages/steven-gh-in-out/src/github/client.spec.ts create mode 100644 packages/steven-gh-in-out/src/github/client.ts delete mode 100644 packages/steven-gh-in-out/src/index.js create mode 100644 packages/steven-gh-in-out/src/index.ts delete mode 100644 packages/steven-gh-in-out/src/ping.js create mode 100644 packages/steven-gh-in-out/src/ping.ts delete mode 100644 packages/steven-gh-in-out/src/polls/github-pr-review-count.js create mode 100644 packages/steven-gh-in-out/src/polls/github-pr-review-count.ts delete mode 100644 packages/steven-gh-in-out/src/polls/github-review-requested-pr-count.js create mode 100644 packages/steven-gh-in-out/src/polls/github-review-requested-pr-count.ts delete mode 100644 packages/steven-gh-in-out/src/polls/registry.js create mode 100644 packages/steven-gh-in-out/src/polls/registry.ts delete mode 100644 packages/steven-gh-in-out/src/polls/types.js create mode 100644 packages/steven-gh-in-out/src/polls/types.ts delete mode 100644 packages/steven-gh-in-out/src/publishers/gather-counter.js create mode 100644 packages/steven-gh-in-out/src/publishers/gather-counter.ts delete mode 100644 packages/steven-gh-in-out/src/reset.js create mode 100644 packages/steven-gh-in-out/src/reset.ts delete mode 100644 packages/steven-gh-in-out/src/runner.js create mode 100644 packages/steven-gh-in-out/src/runner.ts diff --git a/.gitignore b/.gitignore index c938c74..f43d240 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules/ coverage/ dist/ .DS_Store +.env diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2fa6234 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,583 @@ +{ + "name": "@webhook-objects/monorepo", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@webhook-objects/monorepo", + "version": "0.0.0", + "devDependencies": { + "@biomejs/biome": "^2.5", + "@types/node": "^26.0.0", + "@vitest/browser": "^4.1.9", + "@vitest/browser-playwright": "^4.1.9", + "@vitest/coverage-v8": "4.1.9", + "playwright": "^1.61.0", + "tsx": "^4.22", + "typescript": "^6.0", + "vite": "^8.1", + "vite-plugin-node": "^8.0", + "vitest": "^4.1" + } + }, + "../../Library/pnpm/store/v10/links/@/playwright/1.61.0/52c9183db18895ee1d73c2b9ad35a746a4f7d8ef9c8a4f262674f0e6179ad00e/node_modules/playwright": { + "version": "1.61.0", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.61.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "../../Library/pnpm/store/v10/links/@/tsx/4.22.4/52f31086cb74e31de36783d02540c96f2539c315359ab4cf9cefb5024738d8e1/node_modules/tsx": { + "version": "4.22.4", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "../../Library/pnpm/store/v10/links/@/typescript/6.0.3/770d5258c2d2fa59906f623596ec24e0d5e9e2c5d4f45cfc61592637402f6254/node_modules/typescript": { + "version": "6.0.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "devDependencies": { + "@dprint/formatter": "^0.4.1", + "@dprint/typescript": "0.93.4", + "@esfx/canceltoken": "^1.0.0", + "@eslint/js": "^10.0.1", + "@octokit/rest": "^22.0.1", + "@types/chai": "^4.3.20", + "@types/minimist": "^1.2.5", + "@types/mocha": "^10.0.10", + "@types/ms": "^2.1.0", + "@types/node": "latest", + "@types/source-map-support": "^0.5.10", + "@types/which": "^3.0.4", + "@typescript-eslint/rule-tester": "^8.56.1", + "@typescript-eslint/type-utils": "^8.56.1", + "@typescript-eslint/utils": "^8.56.1", + "azure-devops-node-api": "^15.1.3", + "c8": "^10.1.3", + "chai": "^4.5.0", + "chokidar": "^4.0.3", + "diff": "^8.0.3", + "dprint": "^0.49.1", + "esbuild": "^0.27.3", + "eslint": "^10.0.2", + "eslint-plugin-regexp": "^3.0.0", + "fast-xml-parser": "^5.4.1", + "glob": "^10.5.0", + "globals": "^17.4.0", + "hereby": "^1.12.0", + "jsonc-parser": "^3.3.1", + "knip": "^5.85.0", + "minimist": "^1.2.8", + "mocha": "^10.8.2", + "mocha-fivemat-progress-reporter": "^0.1.0", + "monocart-coverage-reports": "^2.12.9", + "ms": "^2.1.3", + "picocolors": "^1.1.1", + "playwright": "^1.58.2", + "source-map-support": "^0.5.21", + "tslib": "^2.8.1", + "typescript": "^5.9.3", + "typescript-eslint": "^8.56.1", + "which": "^3.0.1" + }, + "engines": { + "node": ">=14.17" + } + }, + "../../Library/pnpm/store/v10/links/@/vite-plugin-node/8.0.0/c519d02edbf477641abe28e3c039f3bd73cce68249af806c88cd3d864df006ee/node_modules/vite-plugin-node": { + "version": "8.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "debounce": "^2.2.0", + "debug": "^4.3.2" + }, + "devDependencies": { + "@marblejs/core": "^4.0.2", + "@marblejs/http": "^4.0.2", + "@nestjs/common": "^11.0.0", + "@nestjs/platform-fastify": "^11.1.3", + "@swc/core": "^1.7.26", + "@types/debug": "^4.1.7", + "@types/estree": "^0.0.50", + "@types/express": "^4.17.13", + "@types/koa": "^2.13.4", + "@types/node": "^20.0.0", + "@types/rx": "^4.1.2", + "express": "^4.17.1", + "fastify": "^3.22.0", + "fp-ts": "^2.8.2", + "koa": "^2.13.3", + "npm-run-all": "^4.1.5", + "reflect-metadata": "^0.1.13", + "rimraf": "^3.0.2", + "rxjs": "^7.5.5", + "vite": "^8.0.0" + }, + "peerDependencies": { + "@swc/core": "^1.7.26", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + } + } + }, + "../../Library/pnpm/store/v10/links/@/vite/8.1.0/e02a5fba08ebb43784e4c312971c5b7ab8df7f755144e6f92fbd5d0951a4cc32/node_modules/vite": { + "version": "8.1.0", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "~1.1.2", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "devDependencies": { + "@babel/parser": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "@jridgewell/trace-mapping": "^0.3.31", + "@polka/compression": "^1.0.0-next.25", + "@rollup/plugin-alias": "^6.0.0", + "@rollup/plugin-dynamic-import-vars": "2.1.4", + "@rollup/pluginutils": "^5.4.0", + "@types/escape-html": "^1.0.4", + "@types/pnpapi": "^0.0.5", + "@vercel/detect-agent": "^1.2.3", + "@vitejs/devtools": "^0.3.3", + "@vitest/utils": "4.1.8", + "@voidzero-dev/vite-task-client": "^0.2.0", + "artichokie": "^0.4.3", + "baseline-browser-mapping": "^2.10.37", + "cac": "^7.0.0", + "chokidar": "^3.6.0", + "connect": "^3.7.0", + "convert-source-map": "^2.0.0", + "cors": "^2.8.6", + "cross-spawn": "^7.0.6", + "dotenv-expand": "^13.0.0", + "es-module-lexer": "^2.1.0", + "esbuild": "^0.28.1", + "escape-html": "^1.0.3", + "estree-walker": "^3.0.3", + "etag": "^1.8.1", + "fresh-import": "^0.2.1", + "host-validation-middleware": "^0.1.4", + "http-proxy-3": "^1.23.3", + "launch-editor-middleware": "^2.14.1", + "magic-string": "^0.30.21", + "mlly": "^1.8.2", + "mrmime": "^2.0.1", + "nanoid": "^5.1.11", + "obug": "^1.0.2", + "open": "^10.2.0", + "parse5": "^8.0.1", + "pathe": "^2.0.3", + "periscopic": "^4.0.3", + "picocolors": "^1.1.1", + "postcss-import": "^16.1.1", + "postcss-load-config": "^6.0.1", + "postcss-modules": "^6.0.1", + "premove": "^4.0.0", + "resolve.exports": "^2.0.3", + "rolldown-plugin-dts": "^0.25.2", + "rollup": "^4.59.0", + "rollup-plugin-license": "^3.7.1", + "sass": "^1.101.0", + "sass-embedded": "^1.100.0", + "sirv": "^3.0.2", + "strip-literal": "^3.1.0", + "terser": "^5.48.0", + "ufo": "^1.6.4", + "ws": "^8.21.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "../../Library/pnpm/store/v10/links/@/vitest/4.1.9/37d2d3e691877a56d986077a8bb8123d06627a5b3ee74bdcfc1680c4f16d0fed/node_modules/vitest": { + "version": "4.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.9", + "@vitest/mocker": "4.1.9", + "@vitest/pretty-format": "4.1.9", + "@vitest/runner": "4.1.9", + "@vitest/snapshot": "4.1.9", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "devDependencies": { + "@antfu/install-pkg": "^1.1.0", + "@bomb.sh/tab": "^0.0.14", + "@edge-runtime/vm": "^5.0.0", + "@jridgewell/trace-mapping": "0.3.31", + "@opentelemetry/api": "^1.9.0", + "@sinonjs/fake-timers": "15.0.0", + "@types/estree": "^1.0.8", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/jsdom": "^27.0.0", + "@types/node": "^24.12.0", + "@types/picomatch": "^4.0.2", + "@types/prompts": "^2.4.9", + "@types/sinonjs__fake-timers": "^15.0.1", + "acorn": "8.11.3", + "acorn-walk": "^8.3.5", + "birpc": "^4.0.0", + "cac": "^6.7.14", + "empathic": "^2.0.0", + "flatted": "^3.4.2", + "happy-dom": "^20.8.3", + "jsdom": "^27.4.0", + "local-pkg": "^1.1.2", + "mime": "^4.1.0", + "prompts": "^2.4.2", + "strip-literal": "^3.1.0", + "tinyhighlight": "^0.3.2", + "ws": "^8.19.0" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.9", + "@vitest/browser-preview": "4.1.9", + "@vitest/browser-webdriverio": "4.1.9", + "@vitest/coverage-istanbul": "4.1.9", + "@vitest/coverage-v8": "4.1.9", + "@vitest/ui": "4.1.9", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "../../Library/pnpm/store/v10/links/@biomejs/biome/2.5.0/ca0221d6d89a7e38b65f409f69582fb3592f456769a49c5471c66e2c77c260cb/node_modules/@biomejs/biome": { + "version": "2.5.0", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.5.0", + "@biomejs/cli-darwin-x64": "2.5.0", + "@biomejs/cli-linux-arm64": "2.5.0", + "@biomejs/cli-linux-arm64-musl": "2.5.0", + "@biomejs/cli-linux-x64": "2.5.0", + "@biomejs/cli-linux-x64-musl": "2.5.0", + "@biomejs/cli-win32-arm64": "2.5.0", + "@biomejs/cli-win32-x64": "2.5.0" + } + }, + "../../Library/pnpm/store/v10/links/@types/node/26.0.0/8e42d3ed02c14e7ceb958c81803e89911c36583bd58376a044fdebdd859ad3ab/node_modules/@types/node": { + "version": "26.0.0", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "../../Library/pnpm/store/v10/links/@vitest/browser-playwright/4.1.9/709f0e645e38008637dd1c5ea513d8725132293f2e767a9479d129e9235a71a5/node_modules/@vitest/browser-playwright": { + "version": "4.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/browser": "4.1.9", + "@vitest/mocker": "4.1.9", + "tinyrainbow": "^3.1.0" + }, + "devDependencies": { + "playwright": "^1.59.0", + "vitest": "4.1.9" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "playwright": "*", + "vitest": "4.1.9" + }, + "peerDependenciesMeta": { + "playwright": { + "optional": false + } + } + }, + "../../Library/pnpm/store/v10/links/@vitest/browser/4.1.9/461811525d676a86a80b6b517b2c96595d0f07ca5ba0028f97fd1f58dbf96886/node_modules/@vitest/browser": { + "version": "4.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@blazediff/core": "1.9.1", + "@vitest/mocker": "4.1.9", + "@vitest/utils": "4.1.9", + "magic-string": "^0.30.21", + "pngjs": "^7.0.0", + "sirv": "^3.0.2", + "tinyrainbow": "^3.1.0", + "ws": "^8.19.0" + }, + "devDependencies": { + "@opentelemetry/api": "^1.9.0", + "@testing-library/user-event": "^14.6.1", + "@types/pngjs": "^6.0.5", + "@types/ws": "^8.18.1", + "@vitest/runner": "4.1.9", + "birpc": "^4.0.0", + "flatted": "^3.4.2", + "ivya": "^1.8.0", + "mime": "^4.1.0", + "pathe": "^2.0.3", + "vitest": "4.1.9" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "4.1.9" + } + }, + "../../Library/pnpm/store/v10/links/@vitest/coverage-v8/4.1.9/d0a4abad03b322e806455c40d4c9c8cb121591f062882c68be5b4177b8366aaf/node_modules/@vitest/coverage-v8": { + "version": "4.1.9", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.9", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" + }, + "devDependencies": { + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-lib-report": "^3.0.3", + "@types/istanbul-reports": "^3.0.4", + "@vitest/browser": "4.1.9", + "@vitest/browser-playwright": "4.1.9", + "pathe": "^2.0.3", + "vitest": "4.1.9" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.1.9", + "vitest": "4.1.9" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@biomejs/biome": { + "resolved": "../../Library/pnpm/store/v10/links/@biomejs/biome/2.5.0/ca0221d6d89a7e38b65f409f69582fb3592f456769a49c5471c66e2c77c260cb/node_modules/@biomejs/biome", + "link": true + }, + "node_modules/@types/node": { + "resolved": "../../Library/pnpm/store/v10/links/@types/node/26.0.0/8e42d3ed02c14e7ceb958c81803e89911c36583bd58376a044fdebdd859ad3ab/node_modules/@types/node", + "link": true + }, + "node_modules/@vitest/browser": { + "resolved": "../../Library/pnpm/store/v10/links/@vitest/browser/4.1.9/461811525d676a86a80b6b517b2c96595d0f07ca5ba0028f97fd1f58dbf96886/node_modules/@vitest/browser", + "link": true + }, + "node_modules/@vitest/browser-playwright": { + "resolved": "../../Library/pnpm/store/v10/links/@vitest/browser-playwright/4.1.9/709f0e645e38008637dd1c5ea513d8725132293f2e767a9479d129e9235a71a5/node_modules/@vitest/browser-playwright", + "link": true + }, + "node_modules/@vitest/coverage-v8": { + "resolved": "../../Library/pnpm/store/v10/links/@vitest/coverage-v8/4.1.9/d0a4abad03b322e806455c40d4c9c8cb121591f062882c68be5b4177b8366aaf/node_modules/@vitest/coverage-v8", + "link": true + }, + "node_modules/playwright": { + "resolved": "../../Library/pnpm/store/v10/links/@/playwright/1.61.0/52c9183db18895ee1d73c2b9ad35a746a4f7d8ef9c8a4f262674f0e6179ad00e/node_modules/playwright", + "link": true + }, + "node_modules/tsx": { + "resolved": "../../Library/pnpm/store/v10/links/@/tsx/4.22.4/52f31086cb74e31de36783d02540c96f2539c315359ab4cf9cefb5024738d8e1/node_modules/tsx", + "link": true + }, + "node_modules/typescript": { + "resolved": "../../Library/pnpm/store/v10/links/@/typescript/6.0.3/770d5258c2d2fa59906f623596ec24e0d5e9e2c5d4f45cfc61592637402f6254/node_modules/typescript", + "link": true + }, + "node_modules/vite": { + "resolved": "../../Library/pnpm/store/v10/links/@/vite/8.1.0/e02a5fba08ebb43784e4c312971c5b7ab8df7f755144e6f92fbd5d0951a4cc32/node_modules/vite", + "link": true + }, + "node_modules/vite-plugin-node": { + "resolved": "../../Library/pnpm/store/v10/links/@/vite-plugin-node/8.0.0/c519d02edbf477641abe28e3c039f3bd73cce68249af806c88cd3d864df006ee/node_modules/vite-plugin-node", + "link": true + }, + "node_modules/vitest": { + "resolved": "../../Library/pnpm/store/v10/links/@/vitest/4.1.9/37d2d3e691877a56d986077a8bb8123d06627a5b3ee74bdcfc1680c4f16d0fed/node_modules/vitest", + "link": true + } + } +} diff --git a/packages/steven-gh-in-out/README.md b/packages/steven-gh-in-out/README.md new file mode 100644 index 0000000..d3d057d --- /dev/null +++ b/packages/steven-gh-in-out/README.md @@ -0,0 +1,43 @@ +# @webhook-objects/steven-gh-in-out + +Polls GitHub for two PR metrics and mirrors each count to a Gather Smart Object +`counter`: + +1. Open, ready-for-review PRs you authored or are assigned to (last 14 days) +2. Open PRs that directly named you as reviewer, not yet approved (last 7 days) + +Uses the GitHub REST search API (not the `gh` CLI). Configure via `.env` — see +`.env.example`. + +## Setup + +```sh +cp packages/steven-gh-in-out/.env.example packages/steven-gh-in-out/.env +# fill in GITHUB_TOKEN, GITHUB_REPO, and both Gather webhook URL/secret pairs +pnpm install --merge-git-branch-lockfiles +``` + +## Usage + +```sh +# probe both Smart Objects (webhook.ping) +pnpm --filter @webhook-objects/steven-gh-in-out ping + +# clear legacy activity slots and reset counters (one-time migration) +pnpm --filter @webhook-objects/steven-gh-in-out reset + +# poll GitHub and push counter.set to both objects (runs until Ctrl+C) +pnpm --filter @webhook-objects/steven-gh-in-out start +``` + +| env var | required | default | meaning | +| --- | --- | --- | --- | +| `GITHUB_TOKEN` | yes | — | PAT with repo read access | +| `GITHUB_REPO` | yes | — | `owner/repo` to watch | +| `GATHER_WEBHOOK_URL` | yes | — | counter object for open PRs | +| `GATHER_WEBHOOK_SECRET` | yes | — | `whsec_…` for that object | +| `GATHER_REVIEW_WEBHOOK_URL` | yes | — | counter object for review requests | +| `GATHER_REVIEW_WEBHOOK_SECRET` | yes | — | `whsec_…` for that object | +| `POLL_INTERVAL_MS` | no | `600000` | poll interval (min 10000) | + +Events are signed and sent via `@webhook-objects/client`. diff --git a/packages/steven-gh-in-out/package.json b/packages/steven-gh-in-out/package.json index 146cd84..300cc5d 100644 --- a/packages/steven-gh-in-out/package.json +++ b/packages/steven-gh-in-out/package.json @@ -1,21 +1,27 @@ { - "name": "gather-bot", - "version": "0.0.0", - "private": true, - "type": "module", - "description": "Gather bot", - "engines": { - "node": ">=20", - "pnpm": ">=10.16.0" - }, - "packageManager": "pnpm@10.16.0", - "scripts": { - "prepare": "corepack enable", - "start": "node --env-file=.env src/index.js", - "ping": "node --env-file=.env src/ping.js", - "reset": "node --env-file=.env src/reset.js" - }, - "dependencies": { - "standardwebhooks": "^1.0.0" - } + "name": "@webhook-objects/steven-gh-in-out", + "version": "0.0.0", + "private": true, + "type": "module", + "description": "Poll GitHub PR metrics and mirror counts to Gather Smart Object counters", + "bin": { + "steven-gh-in-out": "src/index.ts" + }, + "scripts": { + "start": "tsx --env-file-if-exists=.env src/index.ts", + "ping": "tsx --env-file-if-exists=.env src/ping.ts", + "reset": "tsx --env-file-if-exists=.env src/reset.ts", + "test": "vitest run" + }, + "licenses": [ + { + "type": "Apache-2.0" + }, + { + "type": "MIT" + } + ], + "dependencies": { + "@webhook-objects/client": "workspace:*" + } } diff --git a/packages/steven-gh-in-out/pnpm-lock.yaml b/packages/steven-gh-in-out/pnpm-lock.yaml deleted file mode 100644 index 97214d1..0000000 --- a/packages/steven-gh-in-out/pnpm-lock.yaml +++ /dev/null @@ -1,35 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - standardwebhooks: - specifier: ^1.0.0 - version: 1.0.0 - -packages: - - '@stablelib/base64@1.0.1': - resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} - - fast-sha256@1.3.0: - resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} - - standardwebhooks@1.0.0: - resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} - -snapshots: - - '@stablelib/base64@1.0.1': {} - - fast-sha256@1.3.0: {} - - standardwebhooks@1.0.0: - dependencies: - '@stablelib/base64': 1.0.1 - fast-sha256: 1.3.0 diff --git a/packages/steven-gh-in-out/pnpm-workspace.yaml b/packages/steven-gh-in-out/pnpm-workspace.yaml deleted file mode 100644 index 0ccb1b9..0000000 --- a/packages/steven-gh-in-out/pnpm-workspace.yaml +++ /dev/null @@ -1,2 +0,0 @@ -minimumReleaseAge: 10080 -minimumReleaseAgeStrict: true diff --git a/packages/steven-gh-in-out/skills/gather-bot/SKILL.md b/packages/steven-gh-in-out/skills/gather-bot/SKILL.md deleted file mode 100644 index f9de15b..0000000 --- a/packages/steven-gh-in-out/skills/gather-bot/SKILL.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: gather-bot -description: >- - Project context for gather-bot. Use when working in this repository to - understand tooling, dependency policy, and where to add project skills. ---- - -# Gather Bot - -## Package management - -- Uses **pnpm** via Corepack (`packageManager` in `package.json`). If `pnpm` is missing, run `corepack enable` once. -- **Minimum release age: 7 days.** Dependencies published within the last week are not installed. Settings live in `pnpm-workspace.yaml` (`minimumReleaseAge: 10080` minutes) and `.npmrc` (`min-release-age=7` for npm 11.10+). -- Do not bypass the release-age gate unless the user explicitly asks (e.g. `--min-release-age=0`). - -## Project skills - -Add reusable agent workflows under `skills//SKILL.md`. Each skill is a directory with YAML frontmatter (`name`, `description`) and markdown instructions. Cursor discovers them via the `.cursor/skills` symlink to this folder. Do not put project skills in `~/.cursor/skills-cursor/` (Cursor-managed only). diff --git a/packages/steven-gh-in-out/skills/gather-smart-object-webhooks/SKILL.md b/packages/steven-gh-in-out/skills/gather-smart-object-webhooks/SKILL.md deleted file mode 100644 index be2a50d..0000000 --- a/packages/steven-gh-in-out/skills/gather-smart-object-webhooks/SKILL.md +++ /dev/null @@ -1,153 +0,0 @@ ---- -name: gather-smart-object-webhooks -description: >- - Wire external systems to Gather Smart Objects via signed webhooks (Standard - Webhooks v1). Use when creating bot webhooks, connecting CI/monitoring/CRM to - an Inbox or other Smart Object, signing POST requests with whsec_ secrets, or - calling activity.add, info.set, and webhook.ping. ---- - -> **Source of truth: the live endpoint.** Before sending real events, run the `webhook.ping` self-check below — its `pong` response confirms the contract and the object's actual capabilities. If anything here contradicts the live response, trust the endpoint. - -# Wire up a Gather Smart Object (`Inbox` / `inbox` preset) - -You're connecting an external system (CI, monitoring, a CRM, a cron job…) to a Smart Object in a Gather space. Your system signs and POSTs events to one URL; the object updates live in the space. - -**Each Smart Object is independent, and so is its secret.** The URL and signing secret below belong to _this one object only_ — a key generated for one object will **not** authenticate any other (you'll get `404 not_found`), and there's no space-wide or account-wide key. To wire up several objects, reuse the `createSmartObjectSender` factory below — one instance per object, each with its own URL and its own secret env var. The factory is identical for every object, so define it once and import it; only the `(url, secret)` pair differs. Regenerating an object's key immediately invalidates its previous one. - -## Your configuration - -| Setting | Value | -| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Webhook URL | `https://api.v2.staging.gather.town/api/v2/hooks/spaces/eb638dc6-570a-43ce-822f-c2a3f0fd9f3b/objects/17554f00-b6f6-41a9-8a19-d8a71bb0b9df` | -| API key (signing secret) | `whsec_…` — **paste the key you copied** from the object's ⋮ menu → "Regenerate API key" (shown once). Store it in `GATHER_WEBHOOK_SECRET`; never commit or log it. | -| Signature scheme | Standard Webhooks v1 (`webhook-id` / `webhook-timestamp` / `webhook-signature`) | - -## Security & sanity checks — do these - -1. **Never store the secret in plain sight.** Read it from an env var / secret manager; never hardcode, commit, or log it (mask it if you must log a request). -2. **Validate the secret before use.** It must start with `whsec_`; fail fast if it's missing or truncated. -3. **Check the response status every time.** `200` = accepted. Do **not** blindly retry `4xx` (`400`/`404`/`410`/`415`) — fix the request. Retry only `5xx`/`503`, with backoff. -4. **Honor the rate-limit headers — don't DDoS Gather.** Responses carry `RateLimit-Limit` / `RateLimit-Remaining` / `RateLimit-Reset` (Unix seconds). Throttle proactively; on `429`, sleep until `RateLimit-Reset`; use exponential backoff with jitter. Limits: **60 req/min per space**, **100/min per IP** — coalesce bursts instead of one webhook per micro-event. -5. **Send a fresh timestamp.** The server rejects timestamps more than **±5 minutes** off. Stamp each request at send time; never replay an old signed body. -6. **Use a unique `webhook-id` per event, reused on retry.** The server dedupes the last 10 ids per object, so retrying the same id is idempotent; a new id is a new event. - -## Signing (Standard Webhooks — use a library) - -Auth follows the [Standard Webhooks](https://www.standardwebhooks.com/) spec, so **do not hand-roll the HMAC** — use the official `standardwebhooks` library for your language (JS/TS, Python, Go, Ruby, PHP, Rust, …). You give it the `whsec_…` secret + payload; it builds the headers and signature. - -- `POST` the webhook URL with `Content-Type: application/json`, body ≤ **4 KB**. -- `webhook-signature` is `v1,` over `` `${webhook-id}.${webhook-timestamp}.${rawBody}` `` (the library handles it). - -> **⚠️ Sign the exact bytes you send.** The HMAC covers the raw body bytes. Serialize the body once, sign that exact string, and send it unchanged — do **not** pretty-print/beautify, reorder keys, or re-serialize after signing, or you'll get the opaque `404 not_found`. - -Body shape: `{ "type": ".", "timestamp": "", "data": { … } }` (`timestamp` required except for `webhook.ping`). - -## Quick start (Node.js) - -```bash -npm install standardwebhooks -``` - -```javascript -import { Webhook } from "standardwebhooks" -import { randomUUID } from "node:crypto" - -// Define ONCE (identical for every object). Each Smart Object is just a (url, secret) pair — -// create one sender per object and never share a key between objects. -function createSmartObjectSender(url, secret) { - if (!/^whsec_.{20,}/.test(secret ?? "")) - throw new Error(`Smart Object secret for ${url} is missing or malformed`) - const webhook = new Webhook(secret) - return async function send(type, data = {}) { - const id = randomUUID() - const tsSeconds = Math.floor(Date.now() / 1000) - const body = JSON.stringify({ type, timestamp: new Date().toISOString(), data }) - const signature = webhook.sign(id, new Date(tsSeconds * 1000), body) - const res = await fetch(url, { - method: "POST", - headers: { - "content-type": "application/json", - "webhook-id": id, - "webhook-timestamp": String(tsSeconds), - "webhook-signature": signature, - }, - body, - }) - if (res.status === 429) { - const reset = Number(res.headers.get("RateLimit-Reset")) * 1000 - await new Promise((r) => setTimeout(r, Math.max(0, reset - Date.now()) + 250)) - return send(type, data) - } - if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`) - return res.json() - } -} - -// This object — paste the key you copied (e.g. whsec_PASTE_YOUR_API_KEY_HERE) into its own env var; never hardcode, commit, or log it: -const thisObject = createSmartObjectSender("https://api.v2.staging.gather.town/api/v2/hooks/spaces/eb638dc6-570a-43ce-822f-c2a3f0fd9f3b/objects/17554f00-b6f6-41a9-8a19-d8a71bb0b9df", process.env.GATHER_WEBHOOK_SECRET) - -// Each additional object reuses the SAME factory with its own URL + secret: -// const deployBoard = createSmartObjectSender(DEPLOY_BOARD_URL, process.env.GATHER_DEPLOY_BOARD_SECRET) -``` - -## What this object accepts - -This is an `inbox` Smart Object. Send `.` in `type`: - -### `info` -Base capability carrying a Smart Object's user-facing identity: a display `name` -and an optional `description`. Composed onto every preset (see `BASE_CAPABILITY_NAMES`) -so every webhook object has editable details regardless of integration type. - -- `info.set` — `name` (string, ≤120 chars, optional), `description` (string, ≤2000 chars, optional) - -### `activity` -`activity` capability: a bounded, newest-wins log of items for a Smart Object, rendered as a feed in -the details popover. Each entry is display-oriented, a sender-supplied `id` (stable identity), a -human-readable `text` line, and an optional `url` to link out to the source (a PR, an incident, etc). -The popover derives a favicon from the `url`'s hostname. - -Methods (wire-addressed as `activity.`): - - `add({ id, text, url? })`: add-or-update by `id`. - A newer event (by signed `at`) replaces the existing entry; a stale or out-of-order redelivery is ignored. - Entries are ordered by `at` and capped to the newest `ACTIVITY_BUFFER_SIZE`. `at` is - stamped from the signed dispatch context, never the args. - - `remove({ id })`: drop the entry with that id (e.g. an incident resolved). - -- `activity.add` — `id` (string, ≤128 chars, required), `text` (string, ≤500 chars, required), `url` (http/https URL, ≤2048 chars, optional) -- `activity.remove` — `id` (string, ≤128 chars, required) - -### `counter` -`counter` capability, a single non-negative integer state, or `null` when unset / cleared. - -Methods (addressed on the wire as `counter.`): - - `set({ count })`: overwrite the count. Negative / fractional values are rejected. - - `increment({ by? })`: add `by` (default `+1`) to the current count; a cleared count counts from 0. `by` must be a positive integer. - - `reset()`: clear the count back to unset (`null`). Presets that fall back when unset revert to their default. - -- `counter.set` — `count` (integer, ≥0, required) -- `counter.increment` — `by` (integer, >0, optional) -- `counter.reset` — no `data` - -**Reserved (every object):** -- `webhook.ping` — Protocol-level health check. Signed like any event but needs no `timestamp`/`data`; returns the object's current preset + capability state (`pong`). Use it to verify the secret and discover which capabilities the object accepts. - -## Verify setup first: `webhook.ping` - -```javascript -await thisObject("webhook.ping") // 200 { "status": "pong", preset, capabilities } -``` - -`pong` echoes the object's real capabilities — use it to confirm the secret works and what this object accepts. - -## Responses & errors - -- **200** `{"status":"dispatched"}` (space live) or `{"status":"space_idle"}` (space unloaded) — both mean accepted. -- **400** `invalid_request`/`invalid_args` — bad body/data; fix it. -- **404** `not_found` — uniform pre-auth failure: bad/missing signature, expired timestamp, wrong URL, oversized body, or a reformatted body. Check the secret, timestamp skew, and that you sent the exact signed bytes. -- **404** `capability_not_declared` — the object doesn't support that `type` (run `webhook.ping`). -- **410** `token_revoked` — key was regenerated; get the new one. -- **415** — set `Content-Type: application/json`. -- **429** — rate limited; honor `RateLimit-Reset`. -- **503** — transient; includes `Retry-After`. diff --git a/packages/steven-gh-in-out/src/config.js b/packages/steven-gh-in-out/src/config.js deleted file mode 100644 index e952871..0000000 --- a/packages/steven-gh-in-out/src/config.js +++ /dev/null @@ -1,57 +0,0 @@ -const WHSEC_PATTERN = /^whsec_.{20,}/ -const WEBHOOK_URL_PATTERN = - /^https:\/\/api\.v2\.(?:staging\.)?gather\.town\/api\/v2\/hooks\/spaces\/[0-9a-f-]{36}\/objects\/[0-9a-f-]{36}$/i - -function required(name) { - const value = process.env[name]?.trim() - if (!value) throw new Error(`Missing required env var: ${name}`) - return value -} - -function parseRepo(value) { - const match = value.trim().match(/^([^/]+)\/([^/]+)$/) - if (!match) { - throw new Error(`GITHUB_REPO must be owner/repo (got ${JSON.stringify(value)})`) - } - return { owner: match[1], name: match[2] } -} - -function loadGatherWebhook(urlEnv, secretEnv) { - const secret = required(secretEnv) - if (!WHSEC_PATTERN.test(secret)) { - throw new Error(`${secretEnv} is missing or malformed (expected whsec_…)`) - } - - const url = required(urlEnv) - if (!WEBHOOK_URL_PATTERN.test(url)) { - throw new Error( - `${urlEnv} is invalid — copy the full URL from the Smart Object setup (spaces/{uuid}/objects/{uuid})`, - ) - } - - return { url, secret } -} - -export function loadConfig() { - const githubRepo = parseRepo(required("GITHUB_REPO")) - const pollIntervalMs = Number(process.env.POLL_INTERVAL_MS ?? "600000") - if (!Number.isFinite(pollIntervalMs) || pollIntervalMs < 10_000) { - throw new Error("POLL_INTERVAL_MS must be a number >= 10000") - } - - return { - gather: { - openPrs: loadGatherWebhook("GATHER_WEBHOOK_URL", "GATHER_WEBHOOK_SECRET"), - reviewRequested: loadGatherWebhook( - "GATHER_REVIEW_WEBHOOK_URL", - "GATHER_REVIEW_WEBHOOK_SECRET", - ), - }, - github: { - token: required("GITHUB_TOKEN"), - owner: githubRepo.owner, - repo: githubRepo.name, - }, - pollIntervalMs, - } -} diff --git a/packages/steven-gh-in-out/src/config.spec.ts b/packages/steven-gh-in-out/src/config.spec.ts new file mode 100644 index 0000000..02ce922 --- /dev/null +++ b/packages/steven-gh-in-out/src/config.spec.ts @@ -0,0 +1,18 @@ +import { expect, test } from "vitest"; +import { parseRepo } from "./config"; + +test("parseRepo accepts owner/repo", () => { + expect(parseRepo("acme/widget")).toEqual({ owner: "acme", name: "widget" }); +}); + +test("parseRepo rejects invalid shapes", () => { + expect(() => parseRepo("not-a-repo")).toThrow(/owner\/repo/); + expect(() => parseRepo("a/b/c")).toThrow(/owner\/repo/); +}); + +test("parseRepo trims whitespace", () => { + expect(parseRepo(" acme/widget ")).toEqual({ + owner: "acme", + name: "widget", + }); +}); diff --git a/packages/steven-gh-in-out/src/config.ts b/packages/steven-gh-in-out/src/config.ts new file mode 100644 index 0000000..90c903d --- /dev/null +++ b/packages/steven-gh-in-out/src/config.ts @@ -0,0 +1,71 @@ +const WHSEC_PATTERN = /^whsec_.{20,}/; +const WEBHOOK_URL_PATTERN = + /^https:\/\/api\.v2\.(?:staging\.)?gather\.town\/api\/v2\/hooks\/spaces\/[0-9a-f-]{36}\/objects\/[0-9a-f-]{36}$/i; + +function required(name: string): string { + const value = process.env[name]?.trim(); + if (!value) throw new Error(`Missing required env var: ${name}`); + return value; +} + +export function parseRepo(value: string): { owner: string; name: string } { + const match = value.trim().match(/^([^/]+)\/([^/]+)$/); + if (!match) { + throw new Error( + `GITHUB_REPO must be owner/repo (got ${JSON.stringify(value)})`, + ); + } + return { owner: match[1], name: match[2] }; +} + +function loadGatherWebhook(urlEnv: string, secretEnv: string) { + const secret = required(secretEnv); + if (!WHSEC_PATTERN.test(secret)) { + throw new Error(`${secretEnv} is missing or malformed (expected whsec_…)`); + } + + const url = required(urlEnv); + if (!WEBHOOK_URL_PATTERN.test(url)) { + throw new Error( + `${urlEnv} is invalid — copy the full URL from the Smart Object setup (spaces/{uuid}/objects/{uuid})`, + ); + } + + return { url, secret }; +} + +export type Config = { + gather: { + openPrs: { url: string; secret: string }; + reviewRequested: { url: string; secret: string }; + }; + github: { token: string; owner: string; repo: string }; + pollIntervalMs: number; +}; + +export function loadConfig(): Config { + const githubRepo = parseRepo(required("GITHUB_REPO")); + const pollIntervalMs = Number(process.env.POLL_INTERVAL_MS ?? "600000"); + if (!Number.isFinite(pollIntervalMs) || pollIntervalMs < 10_000) { + throw new Error("POLL_INTERVAL_MS must be a number >= 10000"); + } + + return { + gather: { + openPrs: loadGatherWebhook( + "GATHER_WEBHOOK_URL", + "GATHER_WEBHOOK_SECRET", + ), + reviewRequested: loadGatherWebhook( + "GATHER_REVIEW_WEBHOOK_URL", + "GATHER_REVIEW_WEBHOOK_SECRET", + ), + }, + github: { + token: required("GITHUB_TOKEN"), + owner: githubRepo.owner, + repo: githubRepo.name, + }, + pollIntervalMs, + }; +} diff --git a/packages/steven-gh-in-out/src/gather/sender.js b/packages/steven-gh-in-out/src/gather/sender.js deleted file mode 100644 index d2a4130..0000000 --- a/packages/steven-gh-in-out/src/gather/sender.js +++ /dev/null @@ -1,39 +0,0 @@ -import { randomUUID } from "node:crypto" -import { Webhook } from "standardwebhooks" - -const WHSEC_PATTERN = /^whsec_.{20,}/ - -export function createSmartObjectSender(url, secret) { - if (!WHSEC_PATTERN.test(secret ?? "")) { - throw new Error(`Smart Object secret for ${url} is missing or malformed`) - } - - const webhook = new Webhook(secret) - - return async function send(type, data = {}) { - const id = randomUUID() - const tsSeconds = Math.floor(Date.now() / 1000) - const body = JSON.stringify({ type, timestamp: new Date().toISOString(), data }) - const signature = webhook.sign(id, new Date(tsSeconds * 1000), body) - - const res = await fetch(url, { - method: "POST", - headers: { - "content-type": "application/json", - "webhook-id": id, - "webhook-timestamp": String(tsSeconds), - "webhook-signature": signature, - }, - body, - }) - - if (res.status === 429) { - const reset = Number(res.headers.get("RateLimit-Reset")) * 1000 - await new Promise((r) => setTimeout(r, Math.max(0, reset - Date.now()) + 250)) - return send(type, data) - } - - if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`) - return res.json() - } -} diff --git a/packages/steven-gh-in-out/src/github/client.js b/packages/steven-gh-in-out/src/github/client.js deleted file mode 100644 index fe5db71..0000000 --- a/packages/steven-gh-in-out/src/github/client.js +++ /dev/null @@ -1,122 +0,0 @@ -const GITHUB_API = "https://api.github.com" - -export const PR_CUTOFF_DAYS = 14 -export const REVIEW_REQUEST_CUTOFF_DAYS = 7 - -/** - * @typedef {{ id: number, number: number, title: string, html_url: string }} GitHubPullRequest - */ - -/** @param {number} days */ -export function cutoffSinceDate(days) { - const cutoff = new Date() - cutoff.setDate(cutoff.getDate() - days) - return cutoff.toISOString().slice(0, 10) -} - -/** @returns {string} YYYY-MM-DD for GitHub `created:>=` search qualifier */ -export function prCreatedSinceDate() { - return cutoffSinceDate(PR_CUTOFF_DAYS) -} - -/** @returns {string} YYYY-MM-DD — last week for review-requested PR search */ -export function reviewRequestSinceDate() { - return cutoffSinceDate(REVIEW_REQUEST_CUTOFF_DAYS) -} - -/** - * @param {string} token - */ -export function githubHeaders(token) { - return { - accept: "application/vnd.github+json", - authorization: `Bearer ${token}`, - "x-github-api-version": "2022-11-28", - } -} - -/** - * @param {string} token - * @returns {Promise} - */ -export async function fetchGitHubLogin(token) { - const res = await fetch(`${GITHUB_API}/user`, { - headers: githubHeaders(token), - }) - if (!res.ok) { - throw new Error(`GitHub /user failed (${res.status}): ${await res.text()}`) - } - const user = await res.json() - if (!user.login) { - throw new Error("GitHub /user response missing login") - } - return user.login -} - -/** - * @param {string} token - * @param {string} query - * @param {{ perPage?: number }} [options] - * @returns {Promise} - */ -export async function searchIssues(token, query, { perPage = 100 } = {}) { - const url = new URL("/search/issues", GITHUB_API) - url.searchParams.set("q", query) - url.searchParams.set("per_page", String(perPage)) - - const res = await fetch(url, { headers: githubHeaders(token) }) - if (!res.ok) { - throw new Error(`GitHub search failed (${res.status}): ${await res.text()}`) - } - const payload = await res.json() - return payload.items ?? [] -} - -/** - * Open, ready-for-review PRs you opened or are assigned to. Search API rejects - * OR on user qualifiers, so merge author + assignee results and dedupe. - * - * @param {string} token - * @param {{ owner: string, repo: string, login: string }} target - * @returns {Promise} - */ -export async function listOpenPrsForUser(token, { owner, repo, login }) { - const createdSince = prCreatedSinceDate() - const base = `repo:${owner}/${repo} is:pr is:open draft:false created:>=${createdSince}` - const [authored, assigned] = await Promise.all([ - searchIssues(token, `${base} author:${login}`), - searchIssues(token, `${base} assignee:${login}`), - ]) - - const byId = new Map() - for (const item of [...authored, ...assigned]) { - byId.set(item.id, item) - } - - return [...byId.values()].sort((a, b) => a.number - b.number) -} - -/** - * Open PRs that directly requested you as a reviewer (not via a team), not yet - * approved by anyone, created in the last week. - * - * @param {string} token - * @param {{ owner: string, repo: string, login: string }} target - * @returns {Promise} - */ -export async function listOpenReviewRequestedPrsForUser(token, { owner, repo, login }) { - const since = reviewRequestSinceDate() - const query = `repo:${owner}/${repo} is:pr is:open user-review-requested:${login} -review:approved created:>=${since}` - const items = await searchIssues(token, query) - return items.sort((a, b) => a.number - b.number) -} - -/** - * @param {string} token - * @param {{ owner: string, repo: string, login: string }} target - * @returns {Promise} - */ -export async function countOpenPrsForUser(token, target) { - const prs = await listOpenPrsForUser(token, target) - return prs.length -} diff --git a/packages/steven-gh-in-out/src/github/client.spec.ts b/packages/steven-gh-in-out/src/github/client.spec.ts new file mode 100644 index 0000000..b1e1519 --- /dev/null +++ b/packages/steven-gh-in-out/src/github/client.spec.ts @@ -0,0 +1,30 @@ +import { expect, test, vi } from "vitest"; +import { + cutoffSinceDate, + PR_CUTOFF_DAYS, + REVIEW_REQUEST_CUTOFF_DAYS, + prCreatedSinceDate, + reviewRequestSinceDate, +} from "./client"; + +test("cutoffSinceDate returns YYYY-MM-DD", () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date("2026-07-01T12:00:00Z")); + + expect(cutoffSinceDate(7)).toBe("2026-06-24"); + expect(cutoffSinceDate(14)).toBe("2026-06-17"); + + vi.useRealTimers(); +}); + +test("PR search cutoffs use expected day windows", () => { + vi.useFakeTimers(); + vi.setSystemTime(new Date("2026-07-01T12:00:00Z")); + + expect(prCreatedSinceDate()).toBe(cutoffSinceDate(PR_CUTOFF_DAYS)); + expect(reviewRequestSinceDate()).toBe( + cutoffSinceDate(REVIEW_REQUEST_CUTOFF_DAYS), + ); + + vi.useRealTimers(); +}); diff --git a/packages/steven-gh-in-out/src/github/client.ts b/packages/steven-gh-in-out/src/github/client.ts new file mode 100644 index 0000000..904ce03 --- /dev/null +++ b/packages/steven-gh-in-out/src/github/client.ts @@ -0,0 +1,105 @@ +const GITHUB_API = "https://api.github.com"; + +export const PR_CUTOFF_DAYS = 14; +export const REVIEW_REQUEST_CUTOFF_DAYS = 7; + +export type GitHubPullRequest = { + id: number; + number: number; + title: string; + html_url: string; +}; + +export function cutoffSinceDate(days: number): string { + const cutoff = new Date(); + cutoff.setDate(cutoff.getDate() - days); + return cutoff.toISOString().slice(0, 10); +} + +/** YYYY-MM-DD for GitHub `created:>=` search qualifier */ +export function prCreatedSinceDate(): string { + return cutoffSinceDate(PR_CUTOFF_DAYS); +} + +/** YYYY-MM-DD — last week for review-requested PR search */ +export function reviewRequestSinceDate(): string { + return cutoffSinceDate(REVIEW_REQUEST_CUTOFF_DAYS); +} + +export function githubHeaders(token: string): Record { + return { + accept: "application/vnd.github+json", + authorization: `Bearer ${token}`, + "x-github-api-version": "2022-11-28", + }; +} + +export async function fetchGitHubLogin(token: string): Promise { + const res = await fetch(`${GITHUB_API}/user`, { + headers: githubHeaders(token), + }); + if (!res.ok) { + throw new Error(`GitHub /user failed (${res.status}): ${await res.text()}`); + } + const user = (await res.json()) as { login?: string }; + if (!user.login) { + throw new Error("GitHub /user response missing login"); + } + return user.login; +} + +export async function searchIssues( + token: string, + query: string, + { perPage = 100 }: { perPage?: number } = {}, +): Promise { + const url = new URL("/search/issues", GITHUB_API); + url.searchParams.set("q", query); + url.searchParams.set("per_page", String(perPage)); + + const res = await fetch(url, { headers: githubHeaders(token) }); + if (!res.ok) { + throw new Error( + `GitHub search failed (${res.status}): ${await res.text()}`, + ); + } + const payload = (await res.json()) as { items?: GitHubPullRequest[] }; + return payload.items ?? []; +} + +/** + * Open, ready-for-review PRs you opened or are assigned to. Search API rejects + * OR on user qualifiers, so merge author + assignee results and dedupe. + */ +export async function listOpenPrsForUser( + token: string, + { owner, repo, login }: { owner: string; repo: string; login: string }, +): Promise { + const createdSince = prCreatedSinceDate(); + const base = `repo:${owner}/${repo} is:pr is:open draft:false created:>=${createdSince}`; + const [authored, assigned] = await Promise.all([ + searchIssues(token, `${base} author:${login}`), + searchIssues(token, `${base} assignee:${login}`), + ]); + + const byId = new Map(); + for (const item of [...authored, ...assigned]) { + byId.set(item.id, item); + } + + return [...byId.values()].sort((a, b) => a.number - b.number); +} + +/** + * Open PRs that directly requested you as a reviewer (not via a team), not yet + * approved by anyone, created in the last week. + */ +export async function listOpenReviewRequestedPrsForUser( + token: string, + { owner, repo, login }: { owner: string; repo: string; login: string }, +): Promise { + const since = reviewRequestSinceDate(); + const query = `repo:${owner}/${repo} is:pr is:open user-review-requested:${login} -review:approved created:>=${since}`; + const items = await searchIssues(token, query); + return items.sort((a, b) => a.number - b.number); +} diff --git a/packages/steven-gh-in-out/src/index.js b/packages/steven-gh-in-out/src/index.js deleted file mode 100644 index 7d8f4c8..0000000 --- a/packages/steven-gh-in-out/src/index.js +++ /dev/null @@ -1,33 +0,0 @@ -import { loadConfig } from "./config.js" -import { createSmartObjectSender } from "./gather/sender.js" -import { fetchGitHubLogin } from "./github/client.js" -import { createPollEntries } from "./polls/registry.js" -import { runPollCycle } from "./runner.js" - -const config = loadConfig() - -const pollEntries = createPollEntries({ - openPrs: createSmartObjectSender( - config.gather.openPrs.url, - config.gather.openPrs.secret, - ), - reviewRequested: createSmartObjectSender( - config.gather.reviewRequested.url, - config.gather.reviewRequested.secret, - ), -}) - -const login = await fetchGitHubLogin(config.github.token) -const ctx = { github: { ...config.github, login } } - -async function tick() { - try { - await runPollCycle(ctx, pollEntries) - } catch (err) { - console.error("Poll cycle failed:", err) - } -} - -console.log(`Starting gather-bot as @${login} (poll every ${config.pollIntervalMs / 1000}s)`) -await tick() -setInterval(tick, config.pollIntervalMs) diff --git a/packages/steven-gh-in-out/src/index.ts b/packages/steven-gh-in-out/src/index.ts new file mode 100644 index 0000000..c154ec5 --- /dev/null +++ b/packages/steven-gh-in-out/src/index.ts @@ -0,0 +1,49 @@ +#!/usr/bin/env -S npx tsx +/** + * Poll GitHub for PR metrics and mirror counts to two Gather Smart Object + * counters — open PRs you authored/are assigned to, and unapproved review + * requests naming you directly. + * + * @module + */ +import { Client } from "@webhook-objects/client/node"; +import { loadConfig } from "./config"; +import { fetchGitHubLogin } from "./github/client"; +import { createPollEntries } from "./polls/registry"; +import { runPollCycle } from "./runner"; + +const config = loadConfig(); + +const pollEntries = createPollEntries({ + openPrs: new Client(config.gather.openPrs), + reviewRequested: new Client(config.gather.reviewRequested), +}); + +const login = await fetchGitHubLogin(config.github.token); +const ctx = { github: { ...config.github, login } }; + +async function tick() { + try { + await runPollCycle(ctx, pollEntries); + } catch (err) { + console.error("Poll cycle failed:", err); + } +} + +console.log( + `Starting steven-gh-in-out as @${login} (poll every ${config.pollIntervalMs / 1000}s)`, +); + +let timer: ReturnType; +const loop = async () => { + await tick(); + timer = setTimeout(loop, config.pollIntervalMs); +}; + +process.on("SIGINT", () => { + clearTimeout(timer); + console.log("\nStopped."); + process.exit(0); +}); + +loop(); diff --git a/packages/steven-gh-in-out/src/ping.js b/packages/steven-gh-in-out/src/ping.js deleted file mode 100644 index 5200232..0000000 --- a/packages/steven-gh-in-out/src/ping.js +++ /dev/null @@ -1,16 +0,0 @@ -import { loadConfig } from "./config.js" -import { createSmartObjectSender } from "./gather/sender.js" - -const config = loadConfig() - -const targets = [ - { name: "open PRs (authored/assigned)", ...config.gather.openPrs }, - { name: "unapproved direct review-requested PRs", ...config.gather.reviewRequested }, -] - -for (const { name, url, secret } of targets) { - const send = createSmartObjectSender(url, secret) - const result = await send("webhook.ping") - console.log(`\n${name}:`) - console.log(JSON.stringify(result, null, 2)) -} diff --git a/packages/steven-gh-in-out/src/ping.ts b/packages/steven-gh-in-out/src/ping.ts new file mode 100644 index 0000000..63ba740 --- /dev/null +++ b/packages/steven-gh-in-out/src/ping.ts @@ -0,0 +1,19 @@ +import { Client } from "@webhook-objects/client/node"; +import { loadConfig } from "./config"; + +const config = loadConfig(); + +const targets = [ + { name: "open PRs (authored/assigned)", ...config.gather.openPrs }, + { + name: "unapproved direct review-requested PRs", + ...config.gather.reviewRequested, + }, +]; + +for (const { name, url, secret } of targets) { + const client = new Client({ url, secret }); + const result = await client.requestMetadata(); + console.log(`\n${name}:`); + console.log(JSON.stringify(result, null, 2)); +} diff --git a/packages/steven-gh-in-out/src/polls/github-pr-review-count.js b/packages/steven-gh-in-out/src/polls/github-pr-review-count.js deleted file mode 100644 index 4591c60..0000000 --- a/packages/steven-gh-in-out/src/polls/github-pr-review-count.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @typedef {import("./types.js").PollContext} PollContext - * @typedef {import("./types.js").PollResult} PollResult - */ - -import { listOpenPrsForUser, prCreatedSinceDate } from "../github/client.js" - -/** - * Count open, ready-for-review PRs you opened or are assigned to. - * - * @param {PollContext} ctx - * @returns {Promise} - */ -export async function githubPrReviewCountPoll(ctx) { - const { owner, repo, token, login } = ctx.github - - const prs = await listOpenPrsForUser(token, { owner, repo, login }) - - console.log( - `[github-pr-review-count] ${prs.length} ready-for-review PR(s) since ${prCreatedSinceDate()} authored by or assigned to @${login}:`, - ) - if (prs.length === 0) { - console.log(" (none)") - } else { - for (const pr of prs) { - console.log(` #${pr.number} ${pr.title}`) - console.log(` ${pr.html_url}`) - } - } - - return { - pollId: "github-pr-review-count", - label: "GitHub PR reviews", - value: prs.length, - } -} diff --git a/packages/steven-gh-in-out/src/polls/github-pr-review-count.ts b/packages/steven-gh-in-out/src/polls/github-pr-review-count.ts new file mode 100644 index 0000000..12d95ec --- /dev/null +++ b/packages/steven-gh-in-out/src/polls/github-pr-review-count.ts @@ -0,0 +1,32 @@ +import { + listOpenPrsForUser, + prCreatedSinceDate, +} from "../github/client"; +import type { Poll, PollContext, PollResult } from "./types"; + +/** Count open, ready-for-review PRs you opened or are assigned to. */ +export const githubPrReviewCountPoll: Poll = async ( + ctx: PollContext, +): Promise => { + const { owner, repo, token, login } = ctx.github; + + const prs = await listOpenPrsForUser(token, { owner, repo, login }); + + console.log( + `[github-pr-review-count] ${prs.length} ready-for-review PR(s) since ${prCreatedSinceDate()} authored by or assigned to @${login}:`, + ); + if (prs.length === 0) { + console.log(" (none)"); + } else { + for (const pr of prs) { + console.log(` #${pr.number} ${pr.title}`); + console.log(` ${pr.html_url}`); + } + } + + return { + pollId: "github-pr-review-count", + label: "GitHub PR reviews", + value: prs.length, + }; +}; diff --git a/packages/steven-gh-in-out/src/polls/github-review-requested-pr-count.js b/packages/steven-gh-in-out/src/polls/github-review-requested-pr-count.js deleted file mode 100644 index 86ef878..0000000 --- a/packages/steven-gh-in-out/src/polls/github-review-requested-pr-count.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * @typedef {import("./types.js").PollContext} PollContext - * @typedef {import("./types.js").PollResult} PollResult - */ - -import { - listOpenReviewRequestedPrsForUser, - reviewRequestSinceDate, -} from "../github/client.js" - -/** - * Open PRs that directly named you as a reviewer, not yet approved by anyone, in the last week. - * - * @param {PollContext} ctx - * @returns {Promise} - */ -export async function githubReviewRequestedPrCountPoll(ctx) { - const { owner, repo, token, login } = ctx.github - - const prs = await listOpenReviewRequestedPrsForUser(token, { owner, repo, login }) - - console.log( - `[github-review-requested-count] ${prs.length} unapproved direct review request(s) since ${reviewRequestSinceDate()} for @${login}:`, - ) - if (prs.length === 0) { - console.log(" (none)") - } else { - for (const pr of prs) { - console.log(` #${pr.number} ${pr.title}`) - console.log(` ${pr.html_url}`) - } - } - - return { - pollId: "github-review-requested-count", - label: "GitHub unapproved review-requested PRs", - value: prs.length, - } -} diff --git a/packages/steven-gh-in-out/src/polls/github-review-requested-pr-count.ts b/packages/steven-gh-in-out/src/polls/github-review-requested-pr-count.ts new file mode 100644 index 0000000..1717d1c --- /dev/null +++ b/packages/steven-gh-in-out/src/polls/github-review-requested-pr-count.ts @@ -0,0 +1,39 @@ +import { + listOpenReviewRequestedPrsForUser, + reviewRequestSinceDate, +} from "../github/client"; +import type { Poll, PollContext, PollResult } from "./types"; + +/** + * Open PRs that directly named you as a reviewer, not yet approved by anyone, + * in the last week. + */ +export const githubReviewRequestedPrCountPoll: Poll = async ( + ctx: PollContext, +): Promise => { + const { owner, repo, token, login } = ctx.github; + + const prs = await listOpenReviewRequestedPrsForUser(token, { + owner, + repo, + login, + }); + + console.log( + `[github-review-requested-count] ${prs.length} unapproved direct review request(s) since ${reviewRequestSinceDate()} for @${login}:`, + ); + if (prs.length === 0) { + console.log(" (none)"); + } else { + for (const pr of prs) { + console.log(` #${pr.number} ${pr.title}`); + console.log(` ${pr.html_url}`); + } + } + + return { + pollId: "github-review-requested-count", + label: "GitHub unapproved review-requested PRs", + value: prs.length, + }; +}; diff --git a/packages/steven-gh-in-out/src/polls/registry.js b/packages/steven-gh-in-out/src/polls/registry.js deleted file mode 100644 index 190a21c..0000000 --- a/packages/steven-gh-in-out/src/polls/registry.js +++ /dev/null @@ -1,33 +0,0 @@ -import { githubPrReviewCountPoll } from "./github-pr-review-count.js" -import { githubReviewRequestedPrCountPoll } from "./github-review-requested-pr-count.js" -import { publishCounter } from "../publishers/gather-counter.js" - -/** @typedef {import("./types.js").Poll} Poll */ -/** @typedef {import("./types.js").PollResult} PollResult */ -/** @typedef {ReturnType} GatherSend */ - -/** - * @typedef {object} PollEntry - * @property {Poll} poll - * @property {GatherSend} send - * @property {(send: GatherSend, result: PollResult) => Promise} publish - */ - -/** - * @param {{ openPrs: GatherSend, reviewRequested: GatherSend }} senders - * @returns {PollEntry[]} - */ -export function createPollEntries(senders) { - return [ - { - poll: githubPrReviewCountPoll, - send: senders.openPrs, - publish: publishCounter, - }, - { - poll: githubReviewRequestedPrCountPoll, - send: senders.reviewRequested, - publish: publishCounter, - }, - ] -} diff --git a/packages/steven-gh-in-out/src/polls/registry.ts b/packages/steven-gh-in-out/src/polls/registry.ts new file mode 100644 index 0000000..bbf987f --- /dev/null +++ b/packages/steven-gh-in-out/src/polls/registry.ts @@ -0,0 +1,29 @@ +import type { Client } from "@webhook-objects/client/node"; +import { publishCounter } from "../publishers/gather-counter"; +import { githubPrReviewCountPoll } from "./github-pr-review-count"; +import { githubReviewRequestedPrCountPoll } from "./github-review-requested-pr-count"; +import type { Poll, PollResult } from "./types"; + +export type PollEntry = { + poll: Poll; + client: Client; + publish: (client: Client, result: PollResult) => Promise; +}; + +export function createPollEntries(senders: { + openPrs: Client; + reviewRequested: Client; +}): PollEntry[] { + return [ + { + poll: githubPrReviewCountPoll, + client: senders.openPrs, + publish: publishCounter, + }, + { + poll: githubReviewRequestedPrCountPoll, + client: senders.reviewRequested, + publish: publishCounter, + }, + ]; +} diff --git a/packages/steven-gh-in-out/src/polls/types.js b/packages/steven-gh-in-out/src/polls/types.js deleted file mode 100644 index ce67c62..0000000 --- a/packages/steven-gh-in-out/src/polls/types.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @typedef {object} PollContext - * @property {{ owner: string, repo: string, token: string, login: string }} github - */ - -/** - * @typedef {object} PollResult - * @property {string} pollId Stable id for logs and future publishers. - * @property {string} label Human-readable poll name for logs. - * @property {number} value Numeric metric driving publishers (e.g. slot count). - */ - -/** - * @typedef {(ctx: PollContext) => Promise} Poll - */ - -export {} diff --git a/packages/steven-gh-in-out/src/polls/types.ts b/packages/steven-gh-in-out/src/polls/types.ts new file mode 100644 index 0000000..54c7714 --- /dev/null +++ b/packages/steven-gh-in-out/src/polls/types.ts @@ -0,0 +1,11 @@ +export type PollContext = { + github: { owner: string; repo: string; token: string; login: string }; +}; + +export type PollResult = { + pollId: string; + label: string; + value: number; +}; + +export type Poll = (ctx: PollContext) => Promise; diff --git a/packages/steven-gh-in-out/src/publishers/gather-counter.js b/packages/steven-gh-in-out/src/publishers/gather-counter.js deleted file mode 100644 index 658651d..0000000 --- a/packages/steven-gh-in-out/src/publishers/gather-counter.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Publish a poll count to a Smart Object via counter.set. - * - * @param {ReturnType} send - * @param {import("../polls/types.js").PollResult} result - */ -export async function publishCounter(send, result) { - const count = Math.max(0, Math.floor(result.value)) - await send("counter.set", { count }) -} diff --git a/packages/steven-gh-in-out/src/publishers/gather-counter.ts b/packages/steven-gh-in-out/src/publishers/gather-counter.ts new file mode 100644 index 0000000..c021c02 --- /dev/null +++ b/packages/steven-gh-in-out/src/publishers/gather-counter.ts @@ -0,0 +1,15 @@ +import type { Client } from "@webhook-objects/client/node"; +import type { PollResult } from "../polls/types"; + +/** Publish a poll count to a Smart Object via counter.set. */ +export async function publishCounter( + client: Client, + result: PollResult, +): Promise { + const count = Math.max(0, Math.floor(result.value)); + await client.send({ + type: "counter.set", + timestamp: new Date().toISOString(), + data: { count }, + }); +} diff --git a/packages/steven-gh-in-out/src/reset.js b/packages/steven-gh-in-out/src/reset.js deleted file mode 100644 index daddbc1..0000000 --- a/packages/steven-gh-in-out/src/reset.js +++ /dev/null @@ -1,35 +0,0 @@ -import { loadConfig } from "./config.js" -import { createSmartObjectSender } from "./gather/sender.js" - -/** Activity slot ids from the pre-counter inbox publisher. */ -const LEGACY_ACTIVITY_SLOT_IDS = [ - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "10", - "github-pr-review-count", -] - -const config = loadConfig() - -const targets = [ - { name: "open PRs (authored/assigned)", ...config.gather.openPrs }, - { name: "unapproved direct review-requested PRs", ...config.gather.reviewRequested }, -] - -for (const { name, url, secret } of targets) { - const send = createSmartObjectSender(url, secret) - - for (const id of LEGACY_ACTIVITY_SLOT_IDS) { - await send("activity.remove", { id }) - } - - const result = await send("counter.reset") - console.log(`${name}: cleared ${LEGACY_ACTIVITY_SLOT_IDS.length} legacy slots + counter (${JSON.stringify(result)})`) -} diff --git a/packages/steven-gh-in-out/src/reset.ts b/packages/steven-gh-in-out/src/reset.ts new file mode 100644 index 0000000..7e62912 --- /dev/null +++ b/packages/steven-gh-in-out/src/reset.ts @@ -0,0 +1,45 @@ +import { Client } from "@webhook-objects/client/node"; +import { loadConfig } from "./config"; + +/** Activity slot ids from the pre-counter inbox publisher. */ +const LEGACY_ACTIVITY_SLOT_IDS = [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "github-pr-review-count", +]; + +const config = loadConfig(); + +const targets = [ + { name: "open PRs (authored/assigned)", ...config.gather.openPrs }, + { + name: "unapproved direct review-requested PRs", + ...config.gather.reviewRequested, + }, +]; + +for (const { name, url, secret } of targets) { + const client = new Client({ url, secret }); + const timestamp = new Date().toISOString(); + + for (const id of LEGACY_ACTIVITY_SLOT_IDS) { + await client.send({ type: "activity.remove", timestamp, data: { id } }); + } + + const result = await client.send({ + type: "counter.reset", + timestamp, + data: {}, + }); + console.log( + `${name}: cleared ${LEGACY_ACTIVITY_SLOT_IDS.length} legacy slots + counter (${JSON.stringify(result)})`, + ); +} diff --git a/packages/steven-gh-in-out/src/runner.js b/packages/steven-gh-in-out/src/runner.js deleted file mode 100644 index 2e7f01d..0000000 --- a/packages/steven-gh-in-out/src/runner.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * @typedef {import("./polls/types.js").PollContext} PollContext - * @typedef {import("./polls/registry.js").PollEntry} PollEntry - */ - -/** - * Run every registered poll once and publish results to Gather. - * - * @param {PollContext} ctx - * @param {PollEntry[]} pollEntries - */ -export async function runPollCycle(ctx, pollEntries) { - for (const { poll, publish, send } of pollEntries) { - const result = await poll(ctx) - console.log(`[${result.pollId}] ${result.label}: ${result.value}`) - await publish(send, result) - } -} diff --git a/packages/steven-gh-in-out/src/runner.ts b/packages/steven-gh-in-out/src/runner.ts new file mode 100644 index 0000000..7a3a1b7 --- /dev/null +++ b/packages/steven-gh-in-out/src/runner.ts @@ -0,0 +1,14 @@ +import type { PollContext } from "./polls/types"; +import type { PollEntry } from "./polls/registry"; + +/** Run every registered poll once and publish results to Gather. */ +export async function runPollCycle( + ctx: PollContext, + pollEntries: PollEntry[], +): Promise { + for (const { poll, publish, client } of pollEntries) { + const result = await poll(ctx); + console.log(`[${result.pollId}] ${result.label}: ${result.value}`); + await publish(client, result); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a7bb14f..c87ed58 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,6 +83,12 @@ importers: specifier: workspace:* version: link:../client + packages/steven-gh-in-out: + dependencies: + '@webhook-objects/client': + specifier: workspace:* + version: link:../client + packages/z-build-config: {} packages: From 665ee48fe1314481f7bb998cd0c5c9bff5f18394 Mon Sep 17 00:00:00 2001 From: Steven Yau Date: Wed, 1 Jul 2026 18:26:55 +0100 Subject: [PATCH 3/8] chore: rename package to gh-prs-ready-need-review Co-authored-by: Cursor --- .../{steven-gh-in-out => gh-prs-ready-need-review}/.env.example | 0 packages/{steven-gh-in-out => gh-prs-ready-need-review}/README.md | 0 .../{steven-gh-in-out => gh-prs-ready-need-review}/package.json | 0 .../src/config.spec.ts | 0 .../{steven-gh-in-out => gh-prs-ready-need-review}/src/config.ts | 0 .../src/github/client.spec.ts | 0 .../src/github/client.ts | 0 .../{steven-gh-in-out => gh-prs-ready-need-review}/src/index.ts | 0 .../{steven-gh-in-out => gh-prs-ready-need-review}/src/ping.ts | 0 .../src/polls/github-pr-review-count.ts | 0 .../src/polls/github-review-requested-pr-count.ts | 0 .../src/polls/registry.ts | 0 .../src/polls/types.ts | 0 .../src/publishers/gather-counter.ts | 0 .../{steven-gh-in-out => gh-prs-ready-need-review}/src/reset.ts | 0 .../{steven-gh-in-out => gh-prs-ready-need-review}/src/runner.ts | 0 16 files changed, 0 insertions(+), 0 deletions(-) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/.env.example (100%) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/README.md (100%) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/package.json (100%) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/src/config.spec.ts (100%) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/src/config.ts (100%) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/src/github/client.spec.ts (100%) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/src/github/client.ts (100%) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/src/index.ts (100%) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/src/ping.ts (100%) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/src/polls/github-pr-review-count.ts (100%) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/src/polls/github-review-requested-pr-count.ts (100%) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/src/polls/registry.ts (100%) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/src/polls/types.ts (100%) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/src/publishers/gather-counter.ts (100%) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/src/reset.ts (100%) rename packages/{steven-gh-in-out => gh-prs-ready-need-review}/src/runner.ts (100%) diff --git a/packages/steven-gh-in-out/.env.example b/packages/gh-prs-ready-need-review/.env.example similarity index 100% rename from packages/steven-gh-in-out/.env.example rename to packages/gh-prs-ready-need-review/.env.example diff --git a/packages/steven-gh-in-out/README.md b/packages/gh-prs-ready-need-review/README.md similarity index 100% rename from packages/steven-gh-in-out/README.md rename to packages/gh-prs-ready-need-review/README.md diff --git a/packages/steven-gh-in-out/package.json b/packages/gh-prs-ready-need-review/package.json similarity index 100% rename from packages/steven-gh-in-out/package.json rename to packages/gh-prs-ready-need-review/package.json diff --git a/packages/steven-gh-in-out/src/config.spec.ts b/packages/gh-prs-ready-need-review/src/config.spec.ts similarity index 100% rename from packages/steven-gh-in-out/src/config.spec.ts rename to packages/gh-prs-ready-need-review/src/config.spec.ts diff --git a/packages/steven-gh-in-out/src/config.ts b/packages/gh-prs-ready-need-review/src/config.ts similarity index 100% rename from packages/steven-gh-in-out/src/config.ts rename to packages/gh-prs-ready-need-review/src/config.ts diff --git a/packages/steven-gh-in-out/src/github/client.spec.ts b/packages/gh-prs-ready-need-review/src/github/client.spec.ts similarity index 100% rename from packages/steven-gh-in-out/src/github/client.spec.ts rename to packages/gh-prs-ready-need-review/src/github/client.spec.ts diff --git a/packages/steven-gh-in-out/src/github/client.ts b/packages/gh-prs-ready-need-review/src/github/client.ts similarity index 100% rename from packages/steven-gh-in-out/src/github/client.ts rename to packages/gh-prs-ready-need-review/src/github/client.ts diff --git a/packages/steven-gh-in-out/src/index.ts b/packages/gh-prs-ready-need-review/src/index.ts similarity index 100% rename from packages/steven-gh-in-out/src/index.ts rename to packages/gh-prs-ready-need-review/src/index.ts diff --git a/packages/steven-gh-in-out/src/ping.ts b/packages/gh-prs-ready-need-review/src/ping.ts similarity index 100% rename from packages/steven-gh-in-out/src/ping.ts rename to packages/gh-prs-ready-need-review/src/ping.ts diff --git a/packages/steven-gh-in-out/src/polls/github-pr-review-count.ts b/packages/gh-prs-ready-need-review/src/polls/github-pr-review-count.ts similarity index 100% rename from packages/steven-gh-in-out/src/polls/github-pr-review-count.ts rename to packages/gh-prs-ready-need-review/src/polls/github-pr-review-count.ts diff --git a/packages/steven-gh-in-out/src/polls/github-review-requested-pr-count.ts b/packages/gh-prs-ready-need-review/src/polls/github-review-requested-pr-count.ts similarity index 100% rename from packages/steven-gh-in-out/src/polls/github-review-requested-pr-count.ts rename to packages/gh-prs-ready-need-review/src/polls/github-review-requested-pr-count.ts diff --git a/packages/steven-gh-in-out/src/polls/registry.ts b/packages/gh-prs-ready-need-review/src/polls/registry.ts similarity index 100% rename from packages/steven-gh-in-out/src/polls/registry.ts rename to packages/gh-prs-ready-need-review/src/polls/registry.ts diff --git a/packages/steven-gh-in-out/src/polls/types.ts b/packages/gh-prs-ready-need-review/src/polls/types.ts similarity index 100% rename from packages/steven-gh-in-out/src/polls/types.ts rename to packages/gh-prs-ready-need-review/src/polls/types.ts diff --git a/packages/steven-gh-in-out/src/publishers/gather-counter.ts b/packages/gh-prs-ready-need-review/src/publishers/gather-counter.ts similarity index 100% rename from packages/steven-gh-in-out/src/publishers/gather-counter.ts rename to packages/gh-prs-ready-need-review/src/publishers/gather-counter.ts diff --git a/packages/steven-gh-in-out/src/reset.ts b/packages/gh-prs-ready-need-review/src/reset.ts similarity index 100% rename from packages/steven-gh-in-out/src/reset.ts rename to packages/gh-prs-ready-need-review/src/reset.ts diff --git a/packages/steven-gh-in-out/src/runner.ts b/packages/gh-prs-ready-need-review/src/runner.ts similarity index 100% rename from packages/steven-gh-in-out/src/runner.ts rename to packages/gh-prs-ready-need-review/src/runner.ts From 664a83be81870ede925bc3393b6dd244db7a50f9 Mon Sep 17 00:00:00 2001 From: Steven Yau Date: Wed, 1 Jul 2026 18:27:39 +0100 Subject: [PATCH 4/8] commit --- packages/gh-prs-ready-need-review/README.md | 13 ++++++------- packages/gh-prs-ready-need-review/package.json | 4 ++-- packages/gh-prs-ready-need-review/src/index.ts | 2 +- pnpm-lock.yaml | 6 +++--- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/gh-prs-ready-need-review/README.md b/packages/gh-prs-ready-need-review/README.md index d3d057d..65dadc4 100644 --- a/packages/gh-prs-ready-need-review/README.md +++ b/packages/gh-prs-ready-need-review/README.md @@ -1,7 +1,6 @@ -# @webhook-objects/steven-gh-in-out +# @webhook-objects/gh-prs-ready-need-review -Polls GitHub for two PR metrics and mirrors each count to a Gather Smart Object -`counter`: +Polls GitHub for two PR metrics and mirrors each count to a Gather Smart Object `counter`: 1. Open, ready-for-review PRs you authored or are assigned to (last 14 days) 2. Open PRs that directly named you as reviewer, not yet approved (last 7 days) @@ -12,7 +11,7 @@ Uses the GitHub REST search API (not the `gh` CLI). Configure via `.env` — see ## Setup ```sh -cp packages/steven-gh-in-out/.env.example packages/steven-gh-in-out/.env +cp packages/gh-prs-ready-need-review/.env.example packages/gh-prs-ready-need-review/.env # fill in GITHUB_TOKEN, GITHUB_REPO, and both Gather webhook URL/secret pairs pnpm install --merge-git-branch-lockfiles ``` @@ -21,13 +20,13 @@ pnpm install --merge-git-branch-lockfiles ```sh # probe both Smart Objects (webhook.ping) -pnpm --filter @webhook-objects/steven-gh-in-out ping +pnpm --filter @webhook-objects/gh-prs-ready-need-review ping # clear legacy activity slots and reset counters (one-time migration) -pnpm --filter @webhook-objects/steven-gh-in-out reset +pnpm --filter @webhook-objects/gh-prs-ready-need-review reset # poll GitHub and push counter.set to both objects (runs until Ctrl+C) -pnpm --filter @webhook-objects/steven-gh-in-out start +pnpm --filter @webhook-objects/gh-prs-ready-need-review start ``` | env var | required | default | meaning | diff --git a/packages/gh-prs-ready-need-review/package.json b/packages/gh-prs-ready-need-review/package.json index 300cc5d..fe61747 100644 --- a/packages/gh-prs-ready-need-review/package.json +++ b/packages/gh-prs-ready-need-review/package.json @@ -1,11 +1,11 @@ { - "name": "@webhook-objects/steven-gh-in-out", + "name": "@webhook-objects/gh-prs-ready-need-review", "version": "0.0.0", "private": true, "type": "module", "description": "Poll GitHub PR metrics and mirror counts to Gather Smart Object counters", "bin": { - "steven-gh-in-out": "src/index.ts" + "gh-prs-ready-need-review": "src/index.ts" }, "scripts": { "start": "tsx --env-file-if-exists=.env src/index.ts", diff --git a/packages/gh-prs-ready-need-review/src/index.ts b/packages/gh-prs-ready-need-review/src/index.ts index c154ec5..7ae9e32 100644 --- a/packages/gh-prs-ready-need-review/src/index.ts +++ b/packages/gh-prs-ready-need-review/src/index.ts @@ -31,7 +31,7 @@ async function tick() { } console.log( - `Starting steven-gh-in-out as @${login} (poll every ${config.pollIntervalMs / 1000}s)`, + `Starting gh-prs-ready-need-review as @${login} (poll every ${config.pollIntervalMs / 1000}s)`, ); let timer: ReturnType; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c87ed58..95d7a69 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,19 +71,19 @@ importers: specifier: workspace:* version: link:../client - packages/low-battery-switch: + packages/gh-prs-ready-need-review: dependencies: '@webhook-objects/client': specifier: workspace:* version: link:../client - packages/now-playing-inbox: + packages/low-battery-switch: dependencies: '@webhook-objects/client': specifier: workspace:* version: link:../client - packages/steven-gh-in-out: + packages/now-playing-inbox: dependencies: '@webhook-objects/client': specifier: workspace:* From ab0ff5e3acfbdd288142e21af403363f6d354061 Mon Sep 17 00:00:00 2001 From: Steven Yau Date: Wed, 1 Jul 2026 18:35:17 +0100 Subject: [PATCH 5/8] lint pass --- packages/gh-prs-ready-need-review/src/config.ts | 5 +---- packages/gh-prs-ready-need-review/src/github/client.spec.ts | 2 +- .../src/polls/github-pr-review-count.ts | 5 +---- packages/gh-prs-ready-need-review/src/runner.ts | 2 +- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/packages/gh-prs-ready-need-review/src/config.ts b/packages/gh-prs-ready-need-review/src/config.ts index 90c903d..e88d21d 100644 --- a/packages/gh-prs-ready-need-review/src/config.ts +++ b/packages/gh-prs-ready-need-review/src/config.ts @@ -52,10 +52,7 @@ export function loadConfig(): Config { return { gather: { - openPrs: loadGatherWebhook( - "GATHER_WEBHOOK_URL", - "GATHER_WEBHOOK_SECRET", - ), + openPrs: loadGatherWebhook("GATHER_WEBHOOK_URL", "GATHER_WEBHOOK_SECRET"), reviewRequested: loadGatherWebhook( "GATHER_REVIEW_WEBHOOK_URL", "GATHER_REVIEW_WEBHOOK_SECRET", diff --git a/packages/gh-prs-ready-need-review/src/github/client.spec.ts b/packages/gh-prs-ready-need-review/src/github/client.spec.ts index b1e1519..01abc8a 100644 --- a/packages/gh-prs-ready-need-review/src/github/client.spec.ts +++ b/packages/gh-prs-ready-need-review/src/github/client.spec.ts @@ -2,8 +2,8 @@ import { expect, test, vi } from "vitest"; import { cutoffSinceDate, PR_CUTOFF_DAYS, - REVIEW_REQUEST_CUTOFF_DAYS, prCreatedSinceDate, + REVIEW_REQUEST_CUTOFF_DAYS, reviewRequestSinceDate, } from "./client"; diff --git a/packages/gh-prs-ready-need-review/src/polls/github-pr-review-count.ts b/packages/gh-prs-ready-need-review/src/polls/github-pr-review-count.ts index 12d95ec..5df4431 100644 --- a/packages/gh-prs-ready-need-review/src/polls/github-pr-review-count.ts +++ b/packages/gh-prs-ready-need-review/src/polls/github-pr-review-count.ts @@ -1,7 +1,4 @@ -import { - listOpenPrsForUser, - prCreatedSinceDate, -} from "../github/client"; +import { listOpenPrsForUser, prCreatedSinceDate } from "../github/client"; import type { Poll, PollContext, PollResult } from "./types"; /** Count open, ready-for-review PRs you opened or are assigned to. */ diff --git a/packages/gh-prs-ready-need-review/src/runner.ts b/packages/gh-prs-ready-need-review/src/runner.ts index 7a3a1b7..a6edb11 100644 --- a/packages/gh-prs-ready-need-review/src/runner.ts +++ b/packages/gh-prs-ready-need-review/src/runner.ts @@ -1,5 +1,5 @@ -import type { PollContext } from "./polls/types"; import type { PollEntry } from "./polls/registry"; +import type { PollContext } from "./polls/types"; /** Run every registered poll once and publish results to Gather. */ export async function runPollCycle( From f97bf5181db572c73486b6a80bbc0c0b469684f9 Mon Sep 17 00:00:00 2001 From: Steven Yau Date: Wed, 1 Jul 2026 18:41:04 +0100 Subject: [PATCH 6/8] refactor(gh-prs): use octokit for GitHub API Replace raw fetch calls with the octokit SDK and add --coverage to the package test script per review feedback. Co-authored-by: Cursor --- .../gh-prs-ready-need-review/package.json | 5 +- .../src/github/client.ts | 41 +- pnpm-lock.steven!github-in-out.yaml | 1920 +++++++++++++++++ 3 files changed, 1936 insertions(+), 30 deletions(-) create mode 100644 pnpm-lock.steven!github-in-out.yaml diff --git a/packages/gh-prs-ready-need-review/package.json b/packages/gh-prs-ready-need-review/package.json index fe61747..f277561 100644 --- a/packages/gh-prs-ready-need-review/package.json +++ b/packages/gh-prs-ready-need-review/package.json @@ -11,7 +11,7 @@ "start": "tsx --env-file-if-exists=.env src/index.ts", "ping": "tsx --env-file-if-exists=.env src/ping.ts", "reset": "tsx --env-file-if-exists=.env src/reset.ts", - "test": "vitest run" + "test": "vitest run --coverage" }, "licenses": [ { @@ -22,6 +22,7 @@ } ], "dependencies": { - "@webhook-objects/client": "workspace:*" + "@webhook-objects/client": "workspace:*", + "octokit": "^5.0.5" } } diff --git a/packages/gh-prs-ready-need-review/src/github/client.ts b/packages/gh-prs-ready-need-review/src/github/client.ts index 904ce03..ac3637c 100644 --- a/packages/gh-prs-ready-need-review/src/github/client.ts +++ b/packages/gh-prs-ready-need-review/src/github/client.ts @@ -1,4 +1,4 @@ -const GITHUB_API = "https://api.github.com"; +import { Octokit } from "octokit"; export const PR_CUTOFF_DAYS = 14; export const REVIEW_REQUEST_CUTOFF_DAYS = 7; @@ -10,6 +10,10 @@ export type GitHubPullRequest = { html_url: string; }; +function createOctokit(token: string): Octokit { + return new Octokit({ auth: token }); +} + export function cutoffSinceDate(days: number): string { const cutoff = new Date(); cutoff.setDate(cutoff.getDate() - days); @@ -26,22 +30,9 @@ export function reviewRequestSinceDate(): string { return cutoffSinceDate(REVIEW_REQUEST_CUTOFF_DAYS); } -export function githubHeaders(token: string): Record { - return { - accept: "application/vnd.github+json", - authorization: `Bearer ${token}`, - "x-github-api-version": "2022-11-28", - }; -} - export async function fetchGitHubLogin(token: string): Promise { - const res = await fetch(`${GITHUB_API}/user`, { - headers: githubHeaders(token), - }); - if (!res.ok) { - throw new Error(`GitHub /user failed (${res.status}): ${await res.text()}`); - } - const user = (await res.json()) as { login?: string }; + const octokit = createOctokit(token); + const { data: user } = await octokit.rest.users.getAuthenticated(); if (!user.login) { throw new Error("GitHub /user response missing login"); } @@ -53,18 +44,12 @@ export async function searchIssues( query: string, { perPage = 100 }: { perPage?: number } = {}, ): Promise { - const url = new URL("/search/issues", GITHUB_API); - url.searchParams.set("q", query); - url.searchParams.set("per_page", String(perPage)); - - const res = await fetch(url, { headers: githubHeaders(token) }); - if (!res.ok) { - throw new Error( - `GitHub search failed (${res.status}): ${await res.text()}`, - ); - } - const payload = (await res.json()) as { items?: GitHubPullRequest[] }; - return payload.items ?? []; + const octokit = createOctokit(token); + const { data } = await octokit.rest.search.issuesAndPullRequests({ + q: query, + per_page: perPage, + }); + return (data.items ?? []) as GitHubPullRequest[]; } /** diff --git a/pnpm-lock.steven!github-in-out.yaml b/pnpm-lock.steven!github-in-out.yaml new file mode 100644 index 0000000..b236138 --- /dev/null +++ b/pnpm-lock.steven!github-in-out.yaml @@ -0,0 +1,1920 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@biomejs/biome': + specifier: ^2.5 + version: 2.5.0 + '@types/node': + specifier: ^26.0.0 + version: 26.0.0 + '@vitest/browser': + specifier: ^4.1.9 + version: 4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))(vitest@4.1.9) + '@vitest/browser-playwright': + specifier: ^4.1.9 + version: 4.1.9(playwright@1.61.0)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))(vitest@4.1.9) + '@vitest/coverage-v8': + specifier: 4.1.9 + version: 4.1.9(@vitest/browser@4.1.9)(vitest@4.1.9) + playwright: + specifier: ^1.61.0 + version: 1.61.0 + tsx: + specifier: ^4.22 + version: 4.22.4 + typescript: + specifier: ^6.0 + version: 6.0.3 + vite: + specifier: ^8.1 + version: 8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4) + vite-plugin-node: + specifier: ^8.0 + version: 8.0.0(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) + vitest: + specifier: ^4.1 + version: 4.1.9(@types/node@26.0.0)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) + + packages/claude-status: + dependencies: + '@webhook-objects/client': + specifier: workspace:* + version: link:../client + devDependencies: + '@webhook-objects/z-build-config': + specifier: workspace:* + version: link:../z-build-config + + packages/client: + dependencies: + standardwebhooks: + specifier: ^1.0.0 + version: 1.0.0 + devDependencies: + '@webhook-objects/z-build-config': + specifier: workspace:* + version: link:../z-build-config + undici: + specifier: ^8.5.0 + version: 8.5.0 + + packages/gh-prs-inbox: + dependencies: + '@webhook-objects/client': + specifier: workspace:* + version: link:../client + + packages/gh-prs-ready-need-review: + dependencies: + '@webhook-objects/client': + specifier: workspace:* + version: link:../client + octokit: + specifier: ^5.0.5 + version: 5.0.5 + + packages/low-battery-switch: + dependencies: + '@webhook-objects/client': + specifier: workspace:* + version: link:../client + + packages/now-playing-inbox: + dependencies: + '@webhook-objects/client': + specifier: workspace:* + version: link:../client + + packages/z-build-config: {} + +packages: + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + + '@biomejs/biome@2.5.0': + resolution: {integrity: sha512-4kURkd9hAPrdDM3C9n82ycYgx8hvQcW6MjKTEejruj8rK0N8P3OPpdy8BvI8kt3KWY4ycF5XtDOrktetEfhfuw==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.5.0': + resolution: {integrity: sha512-Mn3Fwi3SA5fgmfCPqmzpWF2DLZnms3BVAhM088nTnGrTZmHS3wwIjcoZPqpXeNgd3DrrLH6xp8vTLIBuJoZiXw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.5.0': + resolution: {integrity: sha512-rg3VPL5P8mYro6pqlXYXuJWph21slVp3SZtAqWSrkZs40d2gTzYmHF8E/X1iTID25btmNKltNDJ926sqVBp7DQ==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.5.0': + resolution: {integrity: sha512-vQdM4oSGaf7ZNeGO9w5+Y8SBtyser9M6znxYbm7Ec8wInxJu1WiKxFYZW5Auj2d80bcVvefuGGRxoFOE0eee8g==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@biomejs/cli-linux-arm64@2.5.0': + resolution: {integrity: sha512-tl+LW8fdD96/xdeWtWwc82LIOc5CoY7N2AsogLTp5R4ECErYt+8Jl/N68ezN9vzSiqPTxw6vjcihoLPYKZHrlw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@biomejs/cli-linux-x64-musl@2.5.0': + resolution: {integrity: sha512-+9hIcMngJ+yGUahXqZuZ8CoWKJE9SAZsFsM3QDvXpNsLbXZ9lqVzgBhOk/jTSYkOA0GLP9eu3teukqpLUojHMg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@biomejs/cli-linux-x64@2.5.0': + resolution: {integrity: sha512-zpEGf4RQbFEh8Vt7OmavLyyOzRbtcE9osCqrS1kfvt8jDvxwhKXLSf7n0ebr/ov0RJ9ssP+lhs6C8a9WwFvrQA==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@biomejs/cli-win32-arm64@2.5.0': + resolution: {integrity: sha512-jB0wAvTLI4itx5VidqVUejPQFhRUxiZ9l9FvZ26D5fl6t3qme+ZB4PD3bTSeL1vZ8NI2Rx/zj6H9zcESuGHKGw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.5.0': + resolution: {integrity: sha512-VT/lF+GId+67j8aDfLkxdxNoVApsPSTbyAtB3jJq0IWTrY77WXfbPfpngxq0bA6JCEv/7k8C9qWjDRKRznDlyw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@blazediff/core@1.9.1': + resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==} + + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@napi-rs/wasm-runtime@1.1.5': + resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@octokit/app@16.1.2': + resolution: {integrity: sha512-8j7sEpUYVj18dxvh0KWj6W/l6uAiVRBl1JBDVRqH1VHKAO/G5eRVl4yEoYACjakWers1DjUkcCHyJNQK47JqyQ==} + engines: {node: '>= 20'} + + '@octokit/auth-app@8.2.0': + resolution: {integrity: sha512-vVjdtQQwomrZ4V46B9LaCsxsySxGoHsyw6IYBov/TqJVROrlYdyNgw5q6tQbB7KZt53v1l1W53RiqTvpzL907g==} + engines: {node: '>= 20'} + + '@octokit/auth-oauth-app@9.0.3': + resolution: {integrity: sha512-+yoFQquaF8OxJSxTb7rnytBIC2ZLbLqA/yb71I4ZXT9+Slw4TziV9j/kyGhUFRRTF2+7WlnIWsePZCWHs+OGjg==} + engines: {node: '>= 20'} + + '@octokit/auth-oauth-device@8.0.3': + resolution: {integrity: sha512-zh2W0mKKMh/VWZhSqlaCzY7qFyrgd9oTWmTmHaXnHNeQRCZr/CXy2jCgHo4e4dJVTiuxP5dLa0YM5p5QVhJHbw==} + engines: {node: '>= 20'} + + '@octokit/auth-oauth-user@6.0.2': + resolution: {integrity: sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A==} + engines: {node: '>= 20'} + + '@octokit/auth-token@6.0.0': + resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} + engines: {node: '>= 20'} + + '@octokit/auth-unauthenticated@7.0.3': + resolution: {integrity: sha512-8Jb1mtUdmBHL7lGmop9mU9ArMRUTRhg8vp0T1VtZ4yd9vEm3zcLwmjQkhNEduKawOOORie61xhtYIhTDN+ZQ3g==} + engines: {node: '>= 20'} + + '@octokit/core@7.0.6': + resolution: {integrity: sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==} + engines: {node: '>= 20'} + + '@octokit/endpoint@11.0.3': + resolution: {integrity: sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==} + engines: {node: '>= 20'} + + '@octokit/graphql@9.0.3': + resolution: {integrity: sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==} + engines: {node: '>= 20'} + + '@octokit/oauth-app@8.0.3': + resolution: {integrity: sha512-jnAjvTsPepyUaMu9e69hYBuozEPgYqP4Z3UnpmvoIzHDpf8EXDGvTY1l1jK0RsZ194oRd+k6Hm13oRU8EoDFwg==} + engines: {node: '>= 20'} + + '@octokit/oauth-authorization-url@8.0.0': + resolution: {integrity: sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ==} + engines: {node: '>= 20'} + + '@octokit/oauth-methods@6.0.2': + resolution: {integrity: sha512-HiNOO3MqLxlt5Da5bZbLV8Zarnphi4y9XehrbaFMkcoJ+FL7sMxH/UlUsCVxpddVu4qvNDrBdaTVE2o4ITK8ng==} + engines: {node: '>= 20'} + + '@octokit/openapi-types@27.0.0': + resolution: {integrity: sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==} + + '@octokit/openapi-webhooks-types@12.1.0': + resolution: {integrity: sha512-WiuzhOsiOvb7W3Pvmhf8d2C6qaLHXrWiLBP4nJ/4kydu+wpagV5Fkz9RfQwV2afYzv3PB+3xYgp4mAdNGjDprA==} + + '@octokit/plugin-paginate-graphql@6.0.0': + resolution: {integrity: sha512-crfpnIoFiBtRkvPqOyLOsw12XsveYuY2ieP6uYDosoUegBJpSVxGwut9sxUgFFcll3VTOTqpUf8yGd8x1OmAkQ==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-paginate-rest@14.0.0': + resolution: {integrity: sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-rest-endpoint-methods@17.0.0': + resolution: {integrity: sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-retry@8.1.0': + resolution: {integrity: sha512-O1FZgXeiGb2sowEr/hYTr6YunGdSAFWnr2fyW39Ah85H8O33ELASQxcvOFF5LE6Tjekcyu2ms4qAzJVhSaJxTw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=7' + + '@octokit/plugin-throttling@11.0.3': + resolution: {integrity: sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': ^7.0.0 + + '@octokit/request-error@7.1.0': + resolution: {integrity: sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==} + engines: {node: '>= 20'} + + '@octokit/request@10.0.10': + resolution: {integrity: sha512-KxNC2pTqqhszMNrf12ZRd4PonRgyJdsM4F/jySiddQK+DsRcfBtUvqn8t7UsyZhnRJHvX46OohDt5N3VqIWC2w==} + engines: {node: '>= 20'} + + '@octokit/types@16.0.0': + resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} + + '@octokit/webhooks-methods@6.0.0': + resolution: {integrity: sha512-MFlzzoDJVw/GcbfzVC1RLR36QqkTLUf79vLVO3D+xn7r0QgxnFoLZgtrzxiQErAjFUOdH6fas2KeQJ1yr/qaXQ==} + engines: {node: '>= 20'} + + '@octokit/webhooks@14.2.0': + resolution: {integrity: sha512-da6KbdNCV5sr1/txD896V+6W0iamFWrvVl8cHkBSPT+YlvmT3DwXa4jxZnQc+gnuTEqSWbBeoSZYTayXH9wXcw==} + engines: {node: '>= 20'} + + '@oxc-project/types@0.137.0': + resolution: {integrity: sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@rolldown/binding-android-arm64@1.1.2': + resolution: {integrity: sha512-2cZ+7xRS+DBcuJBJKnfzsbleumJhBqSlJVpuzHC0nTqfd3QQ7Vx2/x5YR/D7cBamKSeWplwo82Fn9lqYUDEMfA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.1.2': + resolution: {integrity: sha512-RkPMJnygxsgOYdkfqgpwY0/Fzm8d0VQe6HGU2/B00Xa9eqdLbrII+DOKAodbJAn3ZL1AJxGHkZRPYazgGY6Ljw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.1.2': + resolution: {integrity: sha512-Uiczh6vFhwyfd7WNe7Q7mCA4KxAiLdz7jPE/WGizfRpIieoyFuNVMmM8HqZ9HwudTkY6/AeMQwlNJ9NJijguWw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.1.2': + resolution: {integrity: sha512-+TpdtTRgHiJFjCVFbw311SuLk3KfytPOQQn+VlAEv+gBxYPtL7E6JS9e/tk+8CwxhIZvemJKo4rTKgfWNsKkkA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.1.2': + resolution: {integrity: sha512-4lv1/tkmi7ueIVHnyreaOeUpiZP26BH9rRy6hoYfR9310A2B9nUEVRDvBx69vx64Nr3eTPPRkyciqJJs+j9Jmw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.1.2': + resolution: {integrity: sha512-gBSUVO0eaWgw1JMjK3gB8BMlX2Mk148s2lTiVT3e9vjVxbl7UDfMWWY8CfIaaqiXuM9fVTMxIpUz6CAo/B6Vlw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.1.2': + resolution: {integrity: sha512-LjQP/iZLBu8o8PjIfk4x3At0/mT6h282pvz8Z5LAyhGbu/kDezyO7ea62rF5uoqmgnIYqbN/MqJ3Si3Aymi7xQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.1.2': + resolution: {integrity: sha512-X/7bVLWelEsbyWDUSXt7zVsTniLLPIY2n1rH58qr78l9i7MNbbxBWD8gI2vRfBWf4NUXJCUuQnfZDsp32LqsfQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.1.2': + resolution: {integrity: sha512-gb6dYKW/1KDorGXyy48glEBJs/sxVSC5pcVrox/pFGV4mvwSFeg2sK5L2tRkVsVlh7kueqOgg4GEcuipJcGuKg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.1.2': + resolution: {integrity: sha512-JY4w85pU3iAiJVMh5nuk4/Mh9GjMsupe8MrIN53rwxAZW64GKrWeJBuN6SxQg9QTU5uB1cxyhDzW8jqRn1EABw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.1.2': + resolution: {integrity: sha512-xvpA7o5KCYLB0Rwscmuylb1/zHHSUx4g4xilm4prC5jP76pEUlzBmMbgpbh7bVDbId4NcfT96gN5i6mE6UDaiw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.1.2': + resolution: {integrity: sha512-p/ts6KBLjuk49Bp21XH77poQGt02iNz7ChgHep7tudPOaLinR/De/RHdxF8w8Yj4r/bF/bqXwH6PZrB2sA+Nvw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.1.2': + resolution: {integrity: sha512-VMu/wmrZ9hJzYlRhbw7jK5PODlugyKZ5mOdX78+lS8OvuFkWNQdz1pFLrI2p3P0pjXOmUZ7B48o5VnMH9QOGtg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.1.2': + resolution: {integrity: sha512-xtUJqs8qEkuSviS0n1tsohaPuz3a1SPhZywOji4Oo+sgrJs8daEDMZ0QtqL0OS7dx8PoVpg2J/ZZycPY5I2+Zg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.1.2': + resolution: {integrity: sha512-85YiLQqjUKgSO/Zjnf9e0XIn5Ymrh1fLDWBeAkZqpuBR/3R8TpfoHXuyblqyQrftSSgWO9qpcHN8mkyKsLraoA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@stablelib/base64@1.0.1': + resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + + '@types/aws-lambda@8.10.162': + resolution: {integrity: sha512-Fn658grtLOci1oxi1391vvDWJRKNGWRSqfxRkmN/Iy3c0tQH1USMKEXcPYHLvope+ZgTFocx9FRQJx1muBL6qw==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/node@26.0.0': + resolution: {integrity: sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==} + + '@vitest/browser-playwright@4.1.9': + resolution: {integrity: sha512-Bq1rOGf9waevzG3EOkO/dene6bvKTUsZMVg8S1i+WH3JcMjuXEjiahP9rAqZRELUqjBySOJsvvSWqK/B3wjKQw==} + peerDependencies: + playwright: '*' + vitest: 4.1.9 + + '@vitest/browser@4.1.9': + resolution: {integrity: sha512-j1BKtWmPcqpMhmx/L9EPLgAJpCb0zKfwoWLmqBbxaogCXHjOwHFSEoHCBfnGtx93xKQwilZ26m+UOsHqHMkRNg==} + peerDependencies: + vitest: 4.1.9 + + '@vitest/coverage-v8@4.1.9': + resolution: {integrity: sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==} + peerDependencies: + '@vitest/browser': 4.1.9 + vitest: 4.1.9 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/expect@4.1.9': + resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} + + '@vitest/mocker@4.1.9': + resolution: {integrity: sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.9': + resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==} + + '@vitest/runner@4.1.9': + resolution: {integrity: sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==} + + '@vitest/snapshot@4.1.9': + resolution: {integrity: sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==} + + '@vitest/spy@4.1.9': + resolution: {integrity: sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==} + + '@vitest/utils@4.1.9': + resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-v8-to-istanbul@1.0.4: + resolution: {integrity: sha512-0bC0/4bTSrnwdhU3IsZDwEdojvuPrSg59OYZfKsLRtJZ0u8VBx9DebfqqG8bRdCC0I7vjgxmPi41P0lpkhJHtA==} + + before-after-hook@4.0.0: + resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} + + bottleneck@2.19.5: + resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + content-type@2.0.0: + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + engines: {node: '>=18'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + debounce@2.2.0: + resolution: {integrity: sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==} + engines: {node: '>=18'} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + es-module-lexer@2.1.0: + resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} + + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} + engines: {node: '>=18'} + hasBin: true + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + fast-sha256@1.3.0: + resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + + json-with-bigint@3.5.8: + resolution: {integrity: sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.5.3: + resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.15: + resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + obug@2.1.3: + resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} + engines: {node: '>=12.20.0'} + + octokit@5.0.5: + resolution: {integrity: sha512-4+/OFSqOjoyULo7eN7EA97DE0Xydj/PW5aIckxqQIoFjFwqXKuFCvXUJObyJfBF9Khu4RL/jlDRI9FPaMGfPnw==} + engines: {node: '>= 20'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + playwright-core@1.61.0: + resolution: {integrity: sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.61.0: + resolution: {integrity: sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==} + engines: {node: '>=18'} + hasBin: true + + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + engines: {node: ^10 || ^12 || >=14} + + rolldown@1.1.2: + resolution: {integrity: sha512-x0CrQQqCXWGeI8dTvFfN/Dnv3yMKT9hv5jFjlOreKAx9wqLq9wz7VvLLHyaAXC90/CpggTu9SisSbsJJTPSjNQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + standardwebhooks@1.0.0: + resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} + + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + + toad-cache@3.7.1: + resolution: {integrity: sha512-5DXWzE4Vz7xNHsv+xQ+MGfJYyC78Aok3tEr0MNwHoRf7vZnga1mQXZ4/Nsodld4VR6Wd+VhfmqnNrsRJyYPfrQ==} + engines: {node: '>=20'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tsx@4.22.4: + resolution: {integrity: sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==} + engines: {node: '>=18.0.0'} + hasBin: true + + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} + + undici@8.5.0: + resolution: {integrity: sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==} + engines: {node: '>=22.19.0'} + + universal-github-app-jwt@2.2.2: + resolution: {integrity: sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==} + + universal-user-agent@7.0.3: + resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} + + vite-plugin-node@8.0.0: + resolution: {integrity: sha512-/jz+hrOULqRfsOwSrA3xU0rm2ED/XLsUkQU3VhuJqaR/gvyGxb0ZRxsamh/U84DCpvIEhYkp2ZwgyDVQ+AmIRQ==} + peerDependencies: + '@swc/core': ^1.7.26 + vite: ^8.0.0 + peerDependenciesMeta: + '@swc/core': + optional: true + + vite@8.1.0: + resolution: {integrity: sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.3.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.9: + resolution: {integrity: sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.9 + '@vitest/browser-preview': 4.1.9 + '@vitest/browser-webdriverio': 4.1.9 + '@vitest/coverage-istanbul': 4.1.9 + '@vitest/coverage-v8': 4.1.9 + '@vitest/ui': 4.1.9 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + +snapshots: + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@bcoe/v8-coverage@1.0.2': {} + + '@biomejs/biome@2.5.0': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.5.0 + '@biomejs/cli-darwin-x64': 2.5.0 + '@biomejs/cli-linux-arm64': 2.5.0 + '@biomejs/cli-linux-arm64-musl': 2.5.0 + '@biomejs/cli-linux-x64': 2.5.0 + '@biomejs/cli-linux-x64-musl': 2.5.0 + '@biomejs/cli-win32-arm64': 2.5.0 + '@biomejs/cli-win32-x64': 2.5.0 + + '@biomejs/cli-darwin-arm64@2.5.0': + optional: true + + '@biomejs/cli-darwin-x64@2.5.0': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.5.0': + optional: true + + '@biomejs/cli-linux-arm64@2.5.0': + optional: true + + '@biomejs/cli-linux-x64-musl@2.5.0': + optional: true + + '@biomejs/cli-linux-x64@2.5.0': + optional: true + + '@biomejs/cli-win32-arm64@2.5.0': + optional: true + + '@biomejs/cli-win32-x64@2.5.0': + optional: true + + '@blazediff/core@1.9.1': {} + + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.28.1': + optional: true + + '@esbuild/android-arm64@0.28.1': + optional: true + + '@esbuild/android-arm@0.28.1': + optional: true + + '@esbuild/android-x64@0.28.1': + optional: true + + '@esbuild/darwin-arm64@0.28.1': + optional: true + + '@esbuild/darwin-x64@0.28.1': + optional: true + + '@esbuild/freebsd-arm64@0.28.1': + optional: true + + '@esbuild/freebsd-x64@0.28.1': + optional: true + + '@esbuild/linux-arm64@0.28.1': + optional: true + + '@esbuild/linux-arm@0.28.1': + optional: true + + '@esbuild/linux-ia32@0.28.1': + optional: true + + '@esbuild/linux-loong64@0.28.1': + optional: true + + '@esbuild/linux-mips64el@0.28.1': + optional: true + + '@esbuild/linux-ppc64@0.28.1': + optional: true + + '@esbuild/linux-riscv64@0.28.1': + optional: true + + '@esbuild/linux-s390x@0.28.1': + optional: true + + '@esbuild/linux-x64@0.28.1': + optional: true + + '@esbuild/netbsd-arm64@0.28.1': + optional: true + + '@esbuild/netbsd-x64@0.28.1': + optional: true + + '@esbuild/openbsd-arm64@0.28.1': + optional: true + + '@esbuild/openbsd-x64@0.28.1': + optional: true + + '@esbuild/openharmony-arm64@0.28.1': + optional: true + + '@esbuild/sunos-x64@0.28.1': + optional: true + + '@esbuild/win32-arm64@0.28.1': + optional: true + + '@esbuild/win32-ia32@0.28.1': + optional: true + + '@esbuild/win32-x64@0.28.1': + optional: true + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.2 + optional: true + + '@octokit/app@16.1.2': + dependencies: + '@octokit/auth-app': 8.2.0 + '@octokit/auth-unauthenticated': 7.0.3 + '@octokit/core': 7.0.6 + '@octokit/oauth-app': 8.0.3 + '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) + '@octokit/types': 16.0.0 + '@octokit/webhooks': 14.2.0 + + '@octokit/auth-app@8.2.0': + dependencies: + '@octokit/auth-oauth-app': 9.0.3 + '@octokit/auth-oauth-user': 6.0.2 + '@octokit/request': 10.0.10 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + toad-cache: 3.7.1 + universal-github-app-jwt: 2.2.2 + universal-user-agent: 7.0.3 + + '@octokit/auth-oauth-app@9.0.3': + dependencies: + '@octokit/auth-oauth-device': 8.0.3 + '@octokit/auth-oauth-user': 6.0.2 + '@octokit/request': 10.0.10 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/auth-oauth-device@8.0.3': + dependencies: + '@octokit/oauth-methods': 6.0.2 + '@octokit/request': 10.0.10 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/auth-oauth-user@6.0.2': + dependencies: + '@octokit/auth-oauth-device': 8.0.3 + '@octokit/oauth-methods': 6.0.2 + '@octokit/request': 10.0.10 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/auth-token@6.0.0': {} + + '@octokit/auth-unauthenticated@7.0.3': + dependencies: + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + + '@octokit/core@7.0.6': + dependencies: + '@octokit/auth-token': 6.0.0 + '@octokit/graphql': 9.0.3 + '@octokit/request': 10.0.10 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + before-after-hook: 4.0.0 + universal-user-agent: 7.0.3 + + '@octokit/endpoint@11.0.3': + dependencies: + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/graphql@9.0.3': + dependencies: + '@octokit/request': 10.0.10 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/oauth-app@8.0.3': + dependencies: + '@octokit/auth-oauth-app': 9.0.3 + '@octokit/auth-oauth-user': 6.0.2 + '@octokit/auth-unauthenticated': 7.0.3 + '@octokit/core': 7.0.6 + '@octokit/oauth-authorization-url': 8.0.0 + '@octokit/oauth-methods': 6.0.2 + '@types/aws-lambda': 8.10.162 + universal-user-agent: 7.0.3 + + '@octokit/oauth-authorization-url@8.0.0': {} + + '@octokit/oauth-methods@6.0.2': + dependencies: + '@octokit/oauth-authorization-url': 8.0.0 + '@octokit/request': 10.0.10 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + + '@octokit/openapi-types@27.0.0': {} + + '@octokit/openapi-webhooks-types@12.1.0': {} + + '@octokit/plugin-paginate-graphql@6.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + + '@octokit/plugin-paginate-rest@14.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 + + '@octokit/plugin-rest-endpoint-methods@17.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 + + '@octokit/plugin-retry@8.1.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + bottleneck: 2.19.5 + + '@octokit/plugin-throttling@11.0.3(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 + bottleneck: 2.19.5 + + '@octokit/request-error@7.1.0': + dependencies: + '@octokit/types': 16.0.0 + + '@octokit/request@10.0.10': + dependencies: + '@octokit/endpoint': 11.0.3 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + content-type: 2.0.0 + json-with-bigint: 3.5.8 + universal-user-agent: 7.0.3 + + '@octokit/types@16.0.0': + dependencies: + '@octokit/openapi-types': 27.0.0 + + '@octokit/webhooks-methods@6.0.0': {} + + '@octokit/webhooks@14.2.0': + dependencies: + '@octokit/openapi-webhooks-types': 12.1.0 + '@octokit/request-error': 7.1.0 + '@octokit/webhooks-methods': 6.0.0 + + '@oxc-project/types@0.137.0': {} + + '@polka/url@1.0.0-next.29': {} + + '@rolldown/binding-android-arm64@1.1.2': + optional: true + + '@rolldown/binding-darwin-arm64@1.1.2': + optional: true + + '@rolldown/binding-darwin-x64@1.1.2': + optional: true + + '@rolldown/binding-freebsd-x64@1.1.2': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.1.2': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.1.2': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.1.2': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.1.2': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.1.2': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.1.2': + optional: true + + '@rolldown/binding-linux-x64-musl@1.1.2': + optional: true + + '@rolldown/binding-openharmony-arm64@1.1.2': + optional: true + + '@rolldown/binding-wasm32-wasi@1.1.2': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.1.2': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.1.2': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@stablelib/base64@1.0.1': {} + + '@standard-schema/spec@1.1.0': {} + + '@tybys/wasm-util@0.10.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/aws-lambda@8.10.162': {} + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.9': {} + + '@types/node@26.0.0': + dependencies: + undici-types: 8.3.0 + + '@vitest/browser-playwright@4.1.9(playwright@1.61.0)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))(vitest@4.1.9)': + dependencies: + '@vitest/browser': 4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))(vitest@4.1.9) + '@vitest/mocker': 4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) + playwright: 1.61.0 + tinyrainbow: 3.1.0 + vitest: 4.1.9(@types/node@26.0.0)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/browser@4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))(vitest@4.1.9)': + dependencies: + '@blazediff/core': 1.9.1 + '@vitest/mocker': 4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) + '@vitest/utils': 4.1.9 + magic-string: 0.30.21 + pngjs: 7.0.0 + sirv: 3.0.2 + tinyrainbow: 3.1.0 + vitest: 4.1.9(@types/node@26.0.0)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) + ws: 8.21.0 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/coverage-v8@4.1.9(@vitest/browser@4.1.9)(vitest@4.1.9)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.1.9 + ast-v8-to-istanbul: 1.0.4 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.3 + obug: 2.1.3 + std-env: 4.1.0 + tinyrainbow: 3.1.0 + vitest: 4.1.9(@types/node@26.0.0)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) + optionalDependencies: + '@vitest/browser': 4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))(vitest@4.1.9) + + '@vitest/expect@4.1.9': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))': + dependencies: + '@vitest/spy': 4.1.9 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4) + + '@vitest/pretty-format@4.1.9': + dependencies: + tinyrainbow: 3.1.0 + + '@vitest/runner@4.1.9': + dependencies: + '@vitest/utils': 4.1.9 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.9': + dependencies: + '@vitest/pretty-format': 4.1.9 + '@vitest/utils': 4.1.9 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.9': {} + + '@vitest/utils@4.1.9': + dependencies: + '@vitest/pretty-format': 4.1.9 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + assertion-error@2.0.1: {} + + ast-v8-to-istanbul@1.0.4: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + + before-after-hook@4.0.0: {} + + bottleneck@2.19.5: {} + + chai@6.2.2: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + content-type@2.0.0: {} + + convert-source-map@2.0.0: {} + + debounce@2.2.0: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + detect-libc@2.1.2: {} + + es-module-lexer@2.1.0: {} + + esbuild@0.28.1: + optionalDependencies: + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + expect-type@1.3.0: {} + + fast-sha256@1.3.0: {} + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + has-flag@4.0.0: {} + + html-escaper@2.0.2: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + js-tokens@10.0.0: {} + + json-with-bigint@3.5.8: {} + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.3: + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.8.5 + + mrmime@2.0.1: {} + + ms@2.1.3: {} + + nanoid@3.3.15: {} + + obug@2.1.3: {} + + octokit@5.0.5: + dependencies: + '@octokit/app': 16.1.2 + '@octokit/core': 7.0.6 + '@octokit/oauth-app': 8.0.3 + '@octokit/plugin-paginate-graphql': 6.0.0(@octokit/core@7.0.6) + '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) + '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) + '@octokit/plugin-retry': 8.1.0(@octokit/core@7.0.6) + '@octokit/plugin-throttling': 11.0.3(@octokit/core@7.0.6) + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + '@octokit/webhooks': 14.2.0 + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + playwright-core@1.61.0: {} + + playwright@1.61.0: + dependencies: + playwright-core: 1.61.0 + optionalDependencies: + fsevents: 2.3.2 + + pngjs@7.0.0: {} + + postcss@8.5.15: + dependencies: + nanoid: 3.3.15 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + rolldown@1.1.2: + dependencies: + '@oxc-project/types': 0.137.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.1.2 + '@rolldown/binding-darwin-arm64': 1.1.2 + '@rolldown/binding-darwin-x64': 1.1.2 + '@rolldown/binding-freebsd-x64': 1.1.2 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.2 + '@rolldown/binding-linux-arm64-gnu': 1.1.2 + '@rolldown/binding-linux-arm64-musl': 1.1.2 + '@rolldown/binding-linux-ppc64-gnu': 1.1.2 + '@rolldown/binding-linux-s390x-gnu': 1.1.2 + '@rolldown/binding-linux-x64-gnu': 1.1.2 + '@rolldown/binding-linux-x64-musl': 1.1.2 + '@rolldown/binding-openharmony-arm64': 1.1.2 + '@rolldown/binding-wasm32-wasi': 1.1.2 + '@rolldown/binding-win32-arm64-msvc': 1.1.2 + '@rolldown/binding-win32-x64-msvc': 1.1.2 + + semver@7.8.5: {} + + siginfo@2.0.0: {} + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + source-map-js@1.2.1: {} + + stackback@0.0.2: {} + + standardwebhooks@1.0.0: + dependencies: + '@stablelib/base64': 1.0.1 + fast-sha256: 1.3.0 + + std-env@4.1.0: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + tinybench@2.9.0: {} + + tinyexec@1.2.4: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinyrainbow@3.1.0: {} + + toad-cache@3.7.1: {} + + totalist@3.0.1: {} + + tslib@2.8.1: + optional: true + + tsx@4.22.4: + dependencies: + esbuild: 0.28.1 + optionalDependencies: + fsevents: 2.3.3 + + typescript@6.0.3: {} + + undici-types@8.3.0: {} + + undici@8.5.0: {} + + universal-github-app-jwt@2.2.2: {} + + universal-user-agent@7.0.3: {} + + vite-plugin-node@8.0.0(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)): + dependencies: + chalk: 4.1.2 + debounce: 2.2.0 + debug: 4.4.3 + vite: 8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4) + transitivePeerDependencies: + - supports-color + + vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.15 + rolldown: 1.1.2 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 26.0.0 + esbuild: 0.28.1 + fsevents: 2.3.3 + tsx: 4.22.4 + + vitest@4.1.9(@types/node@26.0.0)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)): + dependencies: + '@vitest/expect': 4.1.9 + '@vitest/mocker': 4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) + '@vitest/pretty-format': 4.1.9 + '@vitest/runner': 4.1.9 + '@vitest/snapshot': 4.1.9 + '@vitest/spy': 4.1.9 + '@vitest/utils': 4.1.9 + es-module-lexer: 2.1.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.3 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.1.0 + tinybench: 2.9.0 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.0 + vite: 8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 26.0.0 + '@vitest/browser-playwright': 4.1.9(playwright@1.61.0)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))(vitest@4.1.9) + '@vitest/coverage-v8': 4.1.9(@vitest/browser@4.1.9)(vitest@4.1.9) + transitivePeerDependencies: + - msw + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + ws@8.21.0: {} From 0fd0edd791e58da2faa81ffdc5044f1b2331ce3b Mon Sep 17 00:00:00 2001 From: Steven Yau Date: Wed, 1 Jul 2026 19:37:34 +0100 Subject: [PATCH 7/8] fix: address PR review nits Remove explicit SIGINT handler, use production URLs in .env.example, and regenerate lockfiles (drop stray package-lock.json). Co-authored-by: Cursor --- package-lock.json | 583 ----- .../gh-prs-ready-need-review/.env.example | 4 +- .../gh-prs-ready-need-review/src/index.ts | 9 +- pnpm-lock.steven!github-in-out.yaml | 1920 ----------------- pnpm-lock.yaml | 318 +++ 5 files changed, 321 insertions(+), 2513 deletions(-) delete mode 100644 package-lock.json delete mode 100644 pnpm-lock.steven!github-in-out.yaml diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 2fa6234..0000000 --- a/package-lock.json +++ /dev/null @@ -1,583 +0,0 @@ -{ - "name": "@webhook-objects/monorepo", - "version": "0.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@webhook-objects/monorepo", - "version": "0.0.0", - "devDependencies": { - "@biomejs/biome": "^2.5", - "@types/node": "^26.0.0", - "@vitest/browser": "^4.1.9", - "@vitest/browser-playwright": "^4.1.9", - "@vitest/coverage-v8": "4.1.9", - "playwright": "^1.61.0", - "tsx": "^4.22", - "typescript": "^6.0", - "vite": "^8.1", - "vite-plugin-node": "^8.0", - "vitest": "^4.1" - } - }, - "../../Library/pnpm/store/v10/links/@/playwright/1.61.0/52c9183db18895ee1d73c2b9ad35a746a4f7d8ef9c8a4f262674f0e6179ad00e/node_modules/playwright": { - "version": "1.61.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "playwright-core": "1.61.0" - }, - "bin": { - "playwright": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "fsevents": "2.3.2" - } - }, - "../../Library/pnpm/store/v10/links/@/tsx/4.22.4/52f31086cb74e31de36783d02540c96f2539c315359ab4cf9cefb5024738d8e1/node_modules/tsx": { - "version": "4.22.4", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.28.0" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "../../Library/pnpm/store/v10/links/@/typescript/6.0.3/770d5258c2d2fa59906f623596ec24e0d5e9e2c5d4f45cfc61592637402f6254/node_modules/typescript": { - "version": "6.0.3", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "devDependencies": { - "@dprint/formatter": "^0.4.1", - "@dprint/typescript": "0.93.4", - "@esfx/canceltoken": "^1.0.0", - "@eslint/js": "^10.0.1", - "@octokit/rest": "^22.0.1", - "@types/chai": "^4.3.20", - "@types/minimist": "^1.2.5", - "@types/mocha": "^10.0.10", - "@types/ms": "^2.1.0", - "@types/node": "latest", - "@types/source-map-support": "^0.5.10", - "@types/which": "^3.0.4", - "@typescript-eslint/rule-tester": "^8.56.1", - "@typescript-eslint/type-utils": "^8.56.1", - "@typescript-eslint/utils": "^8.56.1", - "azure-devops-node-api": "^15.1.3", - "c8": "^10.1.3", - "chai": "^4.5.0", - "chokidar": "^4.0.3", - "diff": "^8.0.3", - "dprint": "^0.49.1", - "esbuild": "^0.27.3", - "eslint": "^10.0.2", - "eslint-plugin-regexp": "^3.0.0", - "fast-xml-parser": "^5.4.1", - "glob": "^10.5.0", - "globals": "^17.4.0", - "hereby": "^1.12.0", - "jsonc-parser": "^3.3.1", - "knip": "^5.85.0", - "minimist": "^1.2.8", - "mocha": "^10.8.2", - "mocha-fivemat-progress-reporter": "^0.1.0", - "monocart-coverage-reports": "^2.12.9", - "ms": "^2.1.3", - "picocolors": "^1.1.1", - "playwright": "^1.58.2", - "source-map-support": "^0.5.21", - "tslib": "^2.8.1", - "typescript": "^5.9.3", - "typescript-eslint": "^8.56.1", - "which": "^3.0.1" - }, - "engines": { - "node": ">=14.17" - } - }, - "../../Library/pnpm/store/v10/links/@/vite-plugin-node/8.0.0/c519d02edbf477641abe28e3c039f3bd73cce68249af806c88cd3d864df006ee/node_modules/vite-plugin-node": { - "version": "8.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "debounce": "^2.2.0", - "debug": "^4.3.2" - }, - "devDependencies": { - "@marblejs/core": "^4.0.2", - "@marblejs/http": "^4.0.2", - "@nestjs/common": "^11.0.0", - "@nestjs/platform-fastify": "^11.1.3", - "@swc/core": "^1.7.26", - "@types/debug": "^4.1.7", - "@types/estree": "^0.0.50", - "@types/express": "^4.17.13", - "@types/koa": "^2.13.4", - "@types/node": "^20.0.0", - "@types/rx": "^4.1.2", - "express": "^4.17.1", - "fastify": "^3.22.0", - "fp-ts": "^2.8.2", - "koa": "^2.13.3", - "npm-run-all": "^4.1.5", - "reflect-metadata": "^0.1.13", - "rimraf": "^3.0.2", - "rxjs": "^7.5.5", - "vite": "^8.0.0" - }, - "peerDependencies": { - "@swc/core": "^1.7.26", - "vite": "^8.0.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - } - } - }, - "../../Library/pnpm/store/v10/links/@/vite/8.1.0/e02a5fba08ebb43784e4c312971c5b7ab8df7f755144e6f92fbd5d0951a4cc32/node_modules/vite": { - "version": "8.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "~1.1.2", - "tinyglobby": "^0.2.17" - }, - "bin": { - "vite": "bin/vite.js" - }, - "devDependencies": { - "@babel/parser": "^7.29.7", - "@jridgewell/remapping": "^2.3.5", - "@jridgewell/trace-mapping": "^0.3.31", - "@polka/compression": "^1.0.0-next.25", - "@rollup/plugin-alias": "^6.0.0", - "@rollup/plugin-dynamic-import-vars": "2.1.4", - "@rollup/pluginutils": "^5.4.0", - "@types/escape-html": "^1.0.4", - "@types/pnpapi": "^0.0.5", - "@vercel/detect-agent": "^1.2.3", - "@vitejs/devtools": "^0.3.3", - "@vitest/utils": "4.1.8", - "@voidzero-dev/vite-task-client": "^0.2.0", - "artichokie": "^0.4.3", - "baseline-browser-mapping": "^2.10.37", - "cac": "^7.0.0", - "chokidar": "^3.6.0", - "connect": "^3.7.0", - "convert-source-map": "^2.0.0", - "cors": "^2.8.6", - "cross-spawn": "^7.0.6", - "dotenv-expand": "^13.0.0", - "es-module-lexer": "^2.1.0", - "esbuild": "^0.28.1", - "escape-html": "^1.0.3", - "estree-walker": "^3.0.3", - "etag": "^1.8.1", - "fresh-import": "^0.2.1", - "host-validation-middleware": "^0.1.4", - "http-proxy-3": "^1.23.3", - "launch-editor-middleware": "^2.14.1", - "magic-string": "^0.30.21", - "mlly": "^1.8.2", - "mrmime": "^2.0.1", - "nanoid": "^5.1.11", - "obug": "^1.0.2", - "open": "^10.2.0", - "parse5": "^8.0.1", - "pathe": "^2.0.3", - "periscopic": "^4.0.3", - "picocolors": "^1.1.1", - "postcss-import": "^16.1.1", - "postcss-load-config": "^6.0.1", - "postcss-modules": "^6.0.1", - "premove": "^4.0.0", - "resolve.exports": "^2.0.3", - "rolldown-plugin-dts": "^0.25.2", - "rollup": "^4.59.0", - "rollup-plugin-license": "^3.7.1", - "sass": "^1.101.0", - "sass-embedded": "^1.100.0", - "sirv": "^3.0.2", - "strip-literal": "^3.1.0", - "terser": "^5.48.0", - "ufo": "^1.6.4", - "ws": "^8.21.0" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.3.0", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "../../Library/pnpm/store/v10/links/@/vitest/4.1.9/37d2d3e691877a56d986077a8bb8123d06627a5b3ee74bdcfc1680c4f16d0fed/node_modules/vitest": { - "version": "4.1.9", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "4.1.9", - "@vitest/mocker": "4.1.9", - "@vitest/pretty-format": "4.1.9", - "@vitest/runner": "4.1.9", - "@vitest/snapshot": "4.1.9", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", - "es-module-lexer": "^2.0.0", - "expect-type": "^1.3.0", - "magic-string": "^0.30.21", - "obug": "^2.1.1", - "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "std-env": "^4.0.0-rc.1", - "tinybench": "^2.9.0", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.1.0", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "devDependencies": { - "@antfu/install-pkg": "^1.1.0", - "@bomb.sh/tab": "^0.0.14", - "@edge-runtime/vm": "^5.0.0", - "@jridgewell/trace-mapping": "0.3.31", - "@opentelemetry/api": "^1.9.0", - "@sinonjs/fake-timers": "15.0.0", - "@types/estree": "^1.0.8", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/jsdom": "^27.0.0", - "@types/node": "^24.12.0", - "@types/picomatch": "^4.0.2", - "@types/prompts": "^2.4.9", - "@types/sinonjs__fake-timers": "^15.0.1", - "acorn": "8.11.3", - "acorn-walk": "^8.3.5", - "birpc": "^4.0.0", - "cac": "^6.7.14", - "empathic": "^2.0.0", - "flatted": "^3.4.2", - "happy-dom": "^20.8.3", - "jsdom": "^27.4.0", - "local-pkg": "^1.1.2", - "mime": "^4.1.0", - "prompts": "^2.4.2", - "strip-literal": "^3.1.0", - "tinyhighlight": "^0.3.2", - "ws": "^8.19.0" - }, - "engines": { - "node": "^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@opentelemetry/api": "^1.9.0", - "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.9", - "@vitest/browser-preview": "4.1.9", - "@vitest/browser-webdriverio": "4.1.9", - "@vitest/coverage-istanbul": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "@vitest/ui": "4.1.9", - "happy-dom": "*", - "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@opentelemetry/api": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser-playwright": { - "optional": true - }, - "@vitest/browser-preview": { - "optional": true - }, - "@vitest/browser-webdriverio": { - "optional": true - }, - "@vitest/coverage-istanbul": { - "optional": true - }, - "@vitest/coverage-v8": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - }, - "vite": { - "optional": false - } - } - }, - "../../Library/pnpm/store/v10/links/@biomejs/biome/2.5.0/ca0221d6d89a7e38b65f409f69582fb3592f456769a49c5471c66e2c77c260cb/node_modules/@biomejs/biome": { - "version": "2.5.0", - "dev": true, - "license": "MIT OR Apache-2.0", - "bin": { - "biome": "bin/biome" - }, - "engines": { - "node": ">=14.21.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/biome" - }, - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "2.5.0", - "@biomejs/cli-darwin-x64": "2.5.0", - "@biomejs/cli-linux-arm64": "2.5.0", - "@biomejs/cli-linux-arm64-musl": "2.5.0", - "@biomejs/cli-linux-x64": "2.5.0", - "@biomejs/cli-linux-x64-musl": "2.5.0", - "@biomejs/cli-win32-arm64": "2.5.0", - "@biomejs/cli-win32-x64": "2.5.0" - } - }, - "../../Library/pnpm/store/v10/links/@types/node/26.0.0/8e42d3ed02c14e7ceb958c81803e89911c36583bd58376a044fdebdd859ad3ab/node_modules/@types/node": { - "version": "26.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~8.3.0" - } - }, - "../../Library/pnpm/store/v10/links/@vitest/browser-playwright/4.1.9/709f0e645e38008637dd1c5ea513d8725132293f2e767a9479d129e9235a71a5/node_modules/@vitest/browser-playwright": { - "version": "4.1.9", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/browser": "4.1.9", - "@vitest/mocker": "4.1.9", - "tinyrainbow": "^3.1.0" - }, - "devDependencies": { - "playwright": "^1.59.0", - "vitest": "4.1.9" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "playwright": "*", - "vitest": "4.1.9" - }, - "peerDependenciesMeta": { - "playwright": { - "optional": false - } - } - }, - "../../Library/pnpm/store/v10/links/@vitest/browser/4.1.9/461811525d676a86a80b6b517b2c96595d0f07ca5ba0028f97fd1f58dbf96886/node_modules/@vitest/browser": { - "version": "4.1.9", - "dev": true, - "license": "MIT", - "dependencies": { - "@blazediff/core": "1.9.1", - "@vitest/mocker": "4.1.9", - "@vitest/utils": "4.1.9", - "magic-string": "^0.30.21", - "pngjs": "^7.0.0", - "sirv": "^3.0.2", - "tinyrainbow": "^3.1.0", - "ws": "^8.19.0" - }, - "devDependencies": { - "@opentelemetry/api": "^1.9.0", - "@testing-library/user-event": "^14.6.1", - "@types/pngjs": "^6.0.5", - "@types/ws": "^8.18.1", - "@vitest/runner": "4.1.9", - "birpc": "^4.0.0", - "flatted": "^3.4.2", - "ivya": "^1.8.0", - "mime": "^4.1.0", - "pathe": "^2.0.3", - "vitest": "4.1.9" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "vitest": "4.1.9" - } - }, - "../../Library/pnpm/store/v10/links/@vitest/coverage-v8/4.1.9/d0a4abad03b322e806455c40d4c9c8cb121591f062882c68be5b4177b8366aaf/node_modules/@vitest/coverage-v8": { - "version": "4.1.9", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.9", - "ast-v8-to-istanbul": "^1.0.0", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-reports": "^3.2.0", - "magicast": "^0.5.2", - "obug": "^2.1.1", - "std-env": "^4.0.0-rc.1", - "tinyrainbow": "^3.1.0" - }, - "devDependencies": { - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-lib-report": "^3.0.3", - "@types/istanbul-reports": "^3.0.4", - "@vitest/browser": "4.1.9", - "@vitest/browser-playwright": "4.1.9", - "pathe": "^2.0.3", - "vitest": "4.1.9" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@vitest/browser": "4.1.9", - "vitest": "4.1.9" - }, - "peerDependenciesMeta": { - "@vitest/browser": { - "optional": true - } - } - }, - "node_modules/@biomejs/biome": { - "resolved": "../../Library/pnpm/store/v10/links/@biomejs/biome/2.5.0/ca0221d6d89a7e38b65f409f69582fb3592f456769a49c5471c66e2c77c260cb/node_modules/@biomejs/biome", - "link": true - }, - "node_modules/@types/node": { - "resolved": "../../Library/pnpm/store/v10/links/@types/node/26.0.0/8e42d3ed02c14e7ceb958c81803e89911c36583bd58376a044fdebdd859ad3ab/node_modules/@types/node", - "link": true - }, - "node_modules/@vitest/browser": { - "resolved": "../../Library/pnpm/store/v10/links/@vitest/browser/4.1.9/461811525d676a86a80b6b517b2c96595d0f07ca5ba0028f97fd1f58dbf96886/node_modules/@vitest/browser", - "link": true - }, - "node_modules/@vitest/browser-playwright": { - "resolved": "../../Library/pnpm/store/v10/links/@vitest/browser-playwright/4.1.9/709f0e645e38008637dd1c5ea513d8725132293f2e767a9479d129e9235a71a5/node_modules/@vitest/browser-playwright", - "link": true - }, - "node_modules/@vitest/coverage-v8": { - "resolved": "../../Library/pnpm/store/v10/links/@vitest/coverage-v8/4.1.9/d0a4abad03b322e806455c40d4c9c8cb121591f062882c68be5b4177b8366aaf/node_modules/@vitest/coverage-v8", - "link": true - }, - "node_modules/playwright": { - "resolved": "../../Library/pnpm/store/v10/links/@/playwright/1.61.0/52c9183db18895ee1d73c2b9ad35a746a4f7d8ef9c8a4f262674f0e6179ad00e/node_modules/playwright", - "link": true - }, - "node_modules/tsx": { - "resolved": "../../Library/pnpm/store/v10/links/@/tsx/4.22.4/52f31086cb74e31de36783d02540c96f2539c315359ab4cf9cefb5024738d8e1/node_modules/tsx", - "link": true - }, - "node_modules/typescript": { - "resolved": "../../Library/pnpm/store/v10/links/@/typescript/6.0.3/770d5258c2d2fa59906f623596ec24e0d5e9e2c5d4f45cfc61592637402f6254/node_modules/typescript", - "link": true - }, - "node_modules/vite": { - "resolved": "../../Library/pnpm/store/v10/links/@/vite/8.1.0/e02a5fba08ebb43784e4c312971c5b7ab8df7f755144e6f92fbd5d0951a4cc32/node_modules/vite", - "link": true - }, - "node_modules/vite-plugin-node": { - "resolved": "../../Library/pnpm/store/v10/links/@/vite-plugin-node/8.0.0/c519d02edbf477641abe28e3c039f3bd73cce68249af806c88cd3d864df006ee/node_modules/vite-plugin-node", - "link": true - }, - "node_modules/vitest": { - "resolved": "../../Library/pnpm/store/v10/links/@/vitest/4.1.9/37d2d3e691877a56d986077a8bb8123d06627a5b3ee74bdcfc1680c4f16d0fed/node_modules/vitest", - "link": true - } - } -} diff --git a/packages/gh-prs-ready-need-review/.env.example b/packages/gh-prs-ready-need-review/.env.example index 73485c5..3fe627e 100644 --- a/packages/gh-prs-ready-need-review/.env.example +++ b/packages/gh-prs-ready-need-review/.env.example @@ -5,11 +5,11 @@ GITHUB_TOKEN= GITHUB_REPO=your-org/your-repo # Gather Smart Object webhook — open PRs you authored or are assigned to -GATHER_WEBHOOK_URL=https://api.v2.staging.gather.town/api/v2/hooks/spaces/.../objects/... +GATHER_WEBHOOK_URL=https://api.v2.gather.town/api/v2/hooks/spaces/.../objects/... GATHER_WEBHOOK_SECRET=whsec_... # Gather Smart Object webhook — open PRs that directly named you as reviewer, not yet approved (last 7 days) -GATHER_REVIEW_WEBHOOK_URL=https://api.v2.staging.gather.town/api/v2/hooks/spaces/.../objects/... +GATHER_REVIEW_WEBHOOK_URL=https://api.v2.gather.town/api/v2/hooks/spaces/.../objects/... GATHER_REVIEW_WEBHOOK_SECRET=whsec_... # How often to poll all registered sources (default: 10 minutes) diff --git a/packages/gh-prs-ready-need-review/src/index.ts b/packages/gh-prs-ready-need-review/src/index.ts index 7ae9e32..feb6ff8 100644 --- a/packages/gh-prs-ready-need-review/src/index.ts +++ b/packages/gh-prs-ready-need-review/src/index.ts @@ -34,16 +34,9 @@ console.log( `Starting gh-prs-ready-need-review as @${login} (poll every ${config.pollIntervalMs / 1000}s)`, ); -let timer: ReturnType; const loop = async () => { await tick(); - timer = setTimeout(loop, config.pollIntervalMs); + setTimeout(loop, config.pollIntervalMs); }; -process.on("SIGINT", () => { - clearTimeout(timer); - console.log("\nStopped."); - process.exit(0); -}); - loop(); diff --git a/pnpm-lock.steven!github-in-out.yaml b/pnpm-lock.steven!github-in-out.yaml deleted file mode 100644 index b236138..0000000 --- a/pnpm-lock.steven!github-in-out.yaml +++ /dev/null @@ -1,1920 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - devDependencies: - '@biomejs/biome': - specifier: ^2.5 - version: 2.5.0 - '@types/node': - specifier: ^26.0.0 - version: 26.0.0 - '@vitest/browser': - specifier: ^4.1.9 - version: 4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))(vitest@4.1.9) - '@vitest/browser-playwright': - specifier: ^4.1.9 - version: 4.1.9(playwright@1.61.0)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))(vitest@4.1.9) - '@vitest/coverage-v8': - specifier: 4.1.9 - version: 4.1.9(@vitest/browser@4.1.9)(vitest@4.1.9) - playwright: - specifier: ^1.61.0 - version: 1.61.0 - tsx: - specifier: ^4.22 - version: 4.22.4 - typescript: - specifier: ^6.0 - version: 6.0.3 - vite: - specifier: ^8.1 - version: 8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4) - vite-plugin-node: - specifier: ^8.0 - version: 8.0.0(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) - vitest: - specifier: ^4.1 - version: 4.1.9(@types/node@26.0.0)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) - - packages/claude-status: - dependencies: - '@webhook-objects/client': - specifier: workspace:* - version: link:../client - devDependencies: - '@webhook-objects/z-build-config': - specifier: workspace:* - version: link:../z-build-config - - packages/client: - dependencies: - standardwebhooks: - specifier: ^1.0.0 - version: 1.0.0 - devDependencies: - '@webhook-objects/z-build-config': - specifier: workspace:* - version: link:../z-build-config - undici: - specifier: ^8.5.0 - version: 8.5.0 - - packages/gh-prs-inbox: - dependencies: - '@webhook-objects/client': - specifier: workspace:* - version: link:../client - - packages/gh-prs-ready-need-review: - dependencies: - '@webhook-objects/client': - specifier: workspace:* - version: link:../client - octokit: - specifier: ^5.0.5 - version: 5.0.5 - - packages/low-battery-switch: - dependencies: - '@webhook-objects/client': - specifier: workspace:* - version: link:../client - - packages/now-playing-inbox: - dependencies: - '@webhook-objects/client': - specifier: workspace:* - version: link:../client - - packages/z-build-config: {} - -packages: - - '@babel/helper-string-parser@7.29.7': - resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} - engines: {node: '>=6.9.0'} - - '@babel/helper-validator-identifier@7.29.7': - resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} - engines: {node: '>=6.9.0'} - - '@babel/parser@7.29.7': - resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} - engines: {node: '>=6.0.0'} - hasBin: true - - '@babel/types@7.29.7': - resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} - engines: {node: '>=6.9.0'} - - '@bcoe/v8-coverage@1.0.2': - resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} - engines: {node: '>=18'} - - '@biomejs/biome@2.5.0': - resolution: {integrity: sha512-4kURkd9hAPrdDM3C9n82ycYgx8hvQcW6MjKTEejruj8rK0N8P3OPpdy8BvI8kt3KWY4ycF5XtDOrktetEfhfuw==} - engines: {node: '>=14.21.3'} - hasBin: true - - '@biomejs/cli-darwin-arm64@2.5.0': - resolution: {integrity: sha512-Mn3Fwi3SA5fgmfCPqmzpWF2DLZnms3BVAhM088nTnGrTZmHS3wwIjcoZPqpXeNgd3DrrLH6xp8vTLIBuJoZiXw==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [darwin] - - '@biomejs/cli-darwin-x64@2.5.0': - resolution: {integrity: sha512-rg3VPL5P8mYro6pqlXYXuJWph21slVp3SZtAqWSrkZs40d2gTzYmHF8E/X1iTID25btmNKltNDJ926sqVBp7DQ==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [darwin] - - '@biomejs/cli-linux-arm64-musl@2.5.0': - resolution: {integrity: sha512-vQdM4oSGaf7ZNeGO9w5+Y8SBtyser9M6znxYbm7Ec8wInxJu1WiKxFYZW5Auj2d80bcVvefuGGRxoFOE0eee8g==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@biomejs/cli-linux-arm64@2.5.0': - resolution: {integrity: sha512-tl+LW8fdD96/xdeWtWwc82LIOc5CoY7N2AsogLTp5R4ECErYt+8Jl/N68ezN9vzSiqPTxw6vjcihoLPYKZHrlw==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@biomejs/cli-linux-x64-musl@2.5.0': - resolution: {integrity: sha512-+9hIcMngJ+yGUahXqZuZ8CoWKJE9SAZsFsM3QDvXpNsLbXZ9lqVzgBhOk/jTSYkOA0GLP9eu3teukqpLUojHMg==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - libc: [musl] - - '@biomejs/cli-linux-x64@2.5.0': - resolution: {integrity: sha512-zpEGf4RQbFEh8Vt7OmavLyyOzRbtcE9osCqrS1kfvt8jDvxwhKXLSf7n0ebr/ov0RJ9ssP+lhs6C8a9WwFvrQA==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@biomejs/cli-win32-arm64@2.5.0': - resolution: {integrity: sha512-jB0wAvTLI4itx5VidqVUejPQFhRUxiZ9l9FvZ26D5fl6t3qme+ZB4PD3bTSeL1vZ8NI2Rx/zj6H9zcESuGHKGw==} - engines: {node: '>=14.21.3'} - cpu: [arm64] - os: [win32] - - '@biomejs/cli-win32-x64@2.5.0': - resolution: {integrity: sha512-VT/lF+GId+67j8aDfLkxdxNoVApsPSTbyAtB3jJq0IWTrY77WXfbPfpngxq0bA6JCEv/7k8C9qWjDRKRznDlyw==} - engines: {node: '>=14.21.3'} - cpu: [x64] - os: [win32] - - '@blazediff/core@1.9.1': - resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==} - - '@emnapi/core@1.11.1': - resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} - - '@emnapi/runtime@1.11.1': - resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} - - '@emnapi/wasi-threads@1.2.2': - resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} - - '@esbuild/aix-ppc64@0.28.1': - resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.28.1': - resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.28.1': - resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.28.1': - resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.28.1': - resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.28.1': - resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.28.1': - resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.28.1': - resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.28.1': - resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.28.1': - resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.28.1': - resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.28.1': - resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.28.1': - resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.28.1': - resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.28.1': - resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.28.1': - resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.28.1': - resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.28.1': - resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.28.1': - resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.28.1': - resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.28.1': - resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.28.1': - resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.28.1': - resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.28.1': - resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.28.1': - resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.28.1': - resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - - '@jridgewell/trace-mapping@0.3.31': - resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - - '@napi-rs/wasm-runtime@1.1.5': - resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} - peerDependencies: - '@emnapi/core': ^1.7.1 - '@emnapi/runtime': ^1.7.1 - - '@octokit/app@16.1.2': - resolution: {integrity: sha512-8j7sEpUYVj18dxvh0KWj6W/l6uAiVRBl1JBDVRqH1VHKAO/G5eRVl4yEoYACjakWers1DjUkcCHyJNQK47JqyQ==} - engines: {node: '>= 20'} - - '@octokit/auth-app@8.2.0': - resolution: {integrity: sha512-vVjdtQQwomrZ4V46B9LaCsxsySxGoHsyw6IYBov/TqJVROrlYdyNgw5q6tQbB7KZt53v1l1W53RiqTvpzL907g==} - engines: {node: '>= 20'} - - '@octokit/auth-oauth-app@9.0.3': - resolution: {integrity: sha512-+yoFQquaF8OxJSxTb7rnytBIC2ZLbLqA/yb71I4ZXT9+Slw4TziV9j/kyGhUFRRTF2+7WlnIWsePZCWHs+OGjg==} - engines: {node: '>= 20'} - - '@octokit/auth-oauth-device@8.0.3': - resolution: {integrity: sha512-zh2W0mKKMh/VWZhSqlaCzY7qFyrgd9oTWmTmHaXnHNeQRCZr/CXy2jCgHo4e4dJVTiuxP5dLa0YM5p5QVhJHbw==} - engines: {node: '>= 20'} - - '@octokit/auth-oauth-user@6.0.2': - resolution: {integrity: sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A==} - engines: {node: '>= 20'} - - '@octokit/auth-token@6.0.0': - resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} - engines: {node: '>= 20'} - - '@octokit/auth-unauthenticated@7.0.3': - resolution: {integrity: sha512-8Jb1mtUdmBHL7lGmop9mU9ArMRUTRhg8vp0T1VtZ4yd9vEm3zcLwmjQkhNEduKawOOORie61xhtYIhTDN+ZQ3g==} - engines: {node: '>= 20'} - - '@octokit/core@7.0.6': - resolution: {integrity: sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==} - engines: {node: '>= 20'} - - '@octokit/endpoint@11.0.3': - resolution: {integrity: sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==} - engines: {node: '>= 20'} - - '@octokit/graphql@9.0.3': - resolution: {integrity: sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==} - engines: {node: '>= 20'} - - '@octokit/oauth-app@8.0.3': - resolution: {integrity: sha512-jnAjvTsPepyUaMu9e69hYBuozEPgYqP4Z3UnpmvoIzHDpf8EXDGvTY1l1jK0RsZ194oRd+k6Hm13oRU8EoDFwg==} - engines: {node: '>= 20'} - - '@octokit/oauth-authorization-url@8.0.0': - resolution: {integrity: sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ==} - engines: {node: '>= 20'} - - '@octokit/oauth-methods@6.0.2': - resolution: {integrity: sha512-HiNOO3MqLxlt5Da5bZbLV8Zarnphi4y9XehrbaFMkcoJ+FL7sMxH/UlUsCVxpddVu4qvNDrBdaTVE2o4ITK8ng==} - engines: {node: '>= 20'} - - '@octokit/openapi-types@27.0.0': - resolution: {integrity: sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==} - - '@octokit/openapi-webhooks-types@12.1.0': - resolution: {integrity: sha512-WiuzhOsiOvb7W3Pvmhf8d2C6qaLHXrWiLBP4nJ/4kydu+wpagV5Fkz9RfQwV2afYzv3PB+3xYgp4mAdNGjDprA==} - - '@octokit/plugin-paginate-graphql@6.0.0': - resolution: {integrity: sha512-crfpnIoFiBtRkvPqOyLOsw12XsveYuY2ieP6uYDosoUegBJpSVxGwut9sxUgFFcll3VTOTqpUf8yGd8x1OmAkQ==} - engines: {node: '>= 20'} - peerDependencies: - '@octokit/core': '>=6' - - '@octokit/plugin-paginate-rest@14.0.0': - resolution: {integrity: sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==} - engines: {node: '>= 20'} - peerDependencies: - '@octokit/core': '>=6' - - '@octokit/plugin-rest-endpoint-methods@17.0.0': - resolution: {integrity: sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==} - engines: {node: '>= 20'} - peerDependencies: - '@octokit/core': '>=6' - - '@octokit/plugin-retry@8.1.0': - resolution: {integrity: sha512-O1FZgXeiGb2sowEr/hYTr6YunGdSAFWnr2fyW39Ah85H8O33ELASQxcvOFF5LE6Tjekcyu2ms4qAzJVhSaJxTw==} - engines: {node: '>= 20'} - peerDependencies: - '@octokit/core': '>=7' - - '@octokit/plugin-throttling@11.0.3': - resolution: {integrity: sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg==} - engines: {node: '>= 20'} - peerDependencies: - '@octokit/core': ^7.0.0 - - '@octokit/request-error@7.1.0': - resolution: {integrity: sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==} - engines: {node: '>= 20'} - - '@octokit/request@10.0.10': - resolution: {integrity: sha512-KxNC2pTqqhszMNrf12ZRd4PonRgyJdsM4F/jySiddQK+DsRcfBtUvqn8t7UsyZhnRJHvX46OohDt5N3VqIWC2w==} - engines: {node: '>= 20'} - - '@octokit/types@16.0.0': - resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} - - '@octokit/webhooks-methods@6.0.0': - resolution: {integrity: sha512-MFlzzoDJVw/GcbfzVC1RLR36QqkTLUf79vLVO3D+xn7r0QgxnFoLZgtrzxiQErAjFUOdH6fas2KeQJ1yr/qaXQ==} - engines: {node: '>= 20'} - - '@octokit/webhooks@14.2.0': - resolution: {integrity: sha512-da6KbdNCV5sr1/txD896V+6W0iamFWrvVl8cHkBSPT+YlvmT3DwXa4jxZnQc+gnuTEqSWbBeoSZYTayXH9wXcw==} - engines: {node: '>= 20'} - - '@oxc-project/types@0.137.0': - resolution: {integrity: sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==} - - '@polka/url@1.0.0-next.29': - resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - - '@rolldown/binding-android-arm64@1.1.2': - resolution: {integrity: sha512-2cZ+7xRS+DBcuJBJKnfzsbleumJhBqSlJVpuzHC0nTqfd3QQ7Vx2/x5YR/D7cBamKSeWplwo82Fn9lqYUDEMfA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - - '@rolldown/binding-darwin-arm64@1.1.2': - resolution: {integrity: sha512-RkPMJnygxsgOYdkfqgpwY0/Fzm8d0VQe6HGU2/B00Xa9eqdLbrII+DOKAodbJAn3ZL1AJxGHkZRPYazgGY6Ljw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - - '@rolldown/binding-darwin-x64@1.1.2': - resolution: {integrity: sha512-Uiczh6vFhwyfd7WNe7Q7mCA4KxAiLdz7jPE/WGizfRpIieoyFuNVMmM8HqZ9HwudTkY6/AeMQwlNJ9NJijguWw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - - '@rolldown/binding-freebsd-x64@1.1.2': - resolution: {integrity: sha512-+TpdtTRgHiJFjCVFbw311SuLk3KfytPOQQn+VlAEv+gBxYPtL7E6JS9e/tk+8CwxhIZvemJKo4rTKgfWNsKkkA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - - '@rolldown/binding-linux-arm-gnueabihf@1.1.2': - resolution: {integrity: sha512-4lv1/tkmi7ueIVHnyreaOeUpiZP26BH9rRy6hoYfR9310A2B9nUEVRDvBx69vx64Nr3eTPPRkyciqJJs+j9Jmw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - - '@rolldown/binding-linux-arm64-gnu@1.1.2': - resolution: {integrity: sha512-gBSUVO0eaWgw1JMjK3gB8BMlX2Mk148s2lTiVT3e9vjVxbl7UDfMWWY8CfIaaqiXuM9fVTMxIpUz6CAo/B6Vlw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-arm64-musl@1.1.2': - resolution: {integrity: sha512-LjQP/iZLBu8o8PjIfk4x3At0/mT6h282pvz8Z5LAyhGbu/kDezyO7ea62rF5uoqmgnIYqbN/MqJ3Si3Aymi7xQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rolldown/binding-linux-ppc64-gnu@1.1.2': - resolution: {integrity: sha512-X/7bVLWelEsbyWDUSXt7zVsTniLLPIY2n1rH58qr78l9i7MNbbxBWD8gI2vRfBWf4NUXJCUuQnfZDsp32LqsfQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-s390x-gnu@1.1.2': - resolution: {integrity: sha512-gb6dYKW/1KDorGXyy48glEBJs/sxVSC5pcVrox/pFGV4mvwSFeg2sK5L2tRkVsVlh7kueqOgg4GEcuipJcGuKg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-x64-gnu@1.1.2': - resolution: {integrity: sha512-JY4w85pU3iAiJVMh5nuk4/Mh9GjMsupe8MrIN53rwxAZW64GKrWeJBuN6SxQg9QTU5uB1cxyhDzW8jqRn1EABw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-x64-musl@1.1.2': - resolution: {integrity: sha512-xvpA7o5KCYLB0Rwscmuylb1/zHHSUx4g4xilm4prC5jP76pEUlzBmMbgpbh7bVDbId4NcfT96gN5i6mE6UDaiw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rolldown/binding-openharmony-arm64@1.1.2': - resolution: {integrity: sha512-p/ts6KBLjuk49Bp21XH77poQGt02iNz7ChgHep7tudPOaLinR/De/RHdxF8w8Yj4r/bF/bqXwH6PZrB2sA+Nvw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - - '@rolldown/binding-wasm32-wasi@1.1.2': - resolution: {integrity: sha512-VMu/wmrZ9hJzYlRhbw7jK5PODlugyKZ5mOdX78+lS8OvuFkWNQdz1pFLrI2p3P0pjXOmUZ7B48o5VnMH9QOGtg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [wasm32] - - '@rolldown/binding-win32-arm64-msvc@1.1.2': - resolution: {integrity: sha512-xtUJqs8qEkuSviS0n1tsohaPuz3a1SPhZywOji4Oo+sgrJs8daEDMZ0QtqL0OS7dx8PoVpg2J/ZZycPY5I2+Zg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - - '@rolldown/binding-win32-x64-msvc@1.1.2': - resolution: {integrity: sha512-85YiLQqjUKgSO/Zjnf9e0XIn5Ymrh1fLDWBeAkZqpuBR/3R8TpfoHXuyblqyQrftSSgWO9qpcHN8mkyKsLraoA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - - '@rolldown/pluginutils@1.0.1': - resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - - '@stablelib/base64@1.0.1': - resolution: {integrity: sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==} - - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - - '@tybys/wasm-util@0.10.2': - resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} - - '@types/aws-lambda@8.10.162': - resolution: {integrity: sha512-Fn658grtLOci1oxi1391vvDWJRKNGWRSqfxRkmN/Iy3c0tQH1USMKEXcPYHLvope+ZgTFocx9FRQJx1muBL6qw==} - - '@types/chai@5.2.3': - resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - - '@types/deep-eql@4.0.2': - resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - - '@types/estree@1.0.9': - resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} - - '@types/node@26.0.0': - resolution: {integrity: sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==} - - '@vitest/browser-playwright@4.1.9': - resolution: {integrity: sha512-Bq1rOGf9waevzG3EOkO/dene6bvKTUsZMVg8S1i+WH3JcMjuXEjiahP9rAqZRELUqjBySOJsvvSWqK/B3wjKQw==} - peerDependencies: - playwright: '*' - vitest: 4.1.9 - - '@vitest/browser@4.1.9': - resolution: {integrity: sha512-j1BKtWmPcqpMhmx/L9EPLgAJpCb0zKfwoWLmqBbxaogCXHjOwHFSEoHCBfnGtx93xKQwilZ26m+UOsHqHMkRNg==} - peerDependencies: - vitest: 4.1.9 - - '@vitest/coverage-v8@4.1.9': - resolution: {integrity: sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==} - peerDependencies: - '@vitest/browser': 4.1.9 - vitest: 4.1.9 - peerDependenciesMeta: - '@vitest/browser': - optional: true - - '@vitest/expect@4.1.9': - resolution: {integrity: sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==} - - '@vitest/mocker@4.1.9': - resolution: {integrity: sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==} - peerDependencies: - msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - msw: - optional: true - vite: - optional: true - - '@vitest/pretty-format@4.1.9': - resolution: {integrity: sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==} - - '@vitest/runner@4.1.9': - resolution: {integrity: sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==} - - '@vitest/snapshot@4.1.9': - resolution: {integrity: sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==} - - '@vitest/spy@4.1.9': - resolution: {integrity: sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==} - - '@vitest/utils@4.1.9': - resolution: {integrity: sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - assertion-error@2.0.1: - resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} - engines: {node: '>=12'} - - ast-v8-to-istanbul@1.0.4: - resolution: {integrity: sha512-0bC0/4bTSrnwdhU3IsZDwEdojvuPrSg59OYZfKsLRtJZ0u8VBx9DebfqqG8bRdCC0I7vjgxmPi41P0lpkhJHtA==} - - before-after-hook@4.0.0: - resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} - - bottleneck@2.19.5: - resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} - - chai@6.2.2: - resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} - engines: {node: '>=18'} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - content-type@2.0.0: - resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} - engines: {node: '>=18'} - - convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - - debounce@2.2.0: - resolution: {integrity: sha512-Xks6RUDLZFdz8LIdR6q0MTH44k7FikOmnh5xkSjMig6ch45afc8sjTjRQf3P6ax8dMgcQrYO/AR2RGWURrruqw==} - engines: {node: '>=18'} - - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} - - es-module-lexer@2.1.0: - resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==} - - esbuild@0.28.1: - resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} - engines: {node: '>=18'} - hasBin: true - - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} - - expect-type@1.3.0: - resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} - engines: {node: '>=12.0.0'} - - fast-sha256@1.3.0: - resolution: {integrity: sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==} - - fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - - fsevents@2.3.2: - resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - html-escaper@2.0.2: - resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} - - istanbul-lib-coverage@3.2.2: - resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} - engines: {node: '>=8'} - - istanbul-lib-report@3.0.1: - resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} - engines: {node: '>=10'} - - istanbul-reports@3.2.0: - resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} - engines: {node: '>=8'} - - js-tokens@10.0.0: - resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} - - json-with-bigint@3.5.8: - resolution: {integrity: sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==} - - lightningcss-android-arm64@1.32.0: - resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [android] - - lightningcss-darwin-arm64@1.32.0: - resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [darwin] - - lightningcss-darwin-x64@1.32.0: - resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [darwin] - - lightningcss-freebsd-x64@1.32.0: - resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [freebsd] - - lightningcss-linux-arm-gnueabihf@1.32.0: - resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} - engines: {node: '>= 12.0.0'} - cpu: [arm] - os: [linux] - - lightningcss-linux-arm64-gnu@1.32.0: - resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - libc: [glibc] - - lightningcss-linux-arm64-musl@1.32.0: - resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [linux] - libc: [musl] - - lightningcss-linux-x64-gnu@1.32.0: - resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - libc: [glibc] - - lightningcss-linux-x64-musl@1.32.0: - resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [linux] - libc: [musl] - - lightningcss-win32-arm64-msvc@1.32.0: - resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} - engines: {node: '>= 12.0.0'} - cpu: [arm64] - os: [win32] - - lightningcss-win32-x64-msvc@1.32.0: - resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} - engines: {node: '>= 12.0.0'} - cpu: [x64] - os: [win32] - - lightningcss@1.32.0: - resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} - engines: {node: '>= 12.0.0'} - - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} - - magicast@0.5.3: - resolution: {integrity: sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==} - - make-dir@4.0.0: - resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} - engines: {node: '>=10'} - - mrmime@2.0.1: - resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} - engines: {node: '>=10'} - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - nanoid@3.3.15: - resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - - obug@2.1.3: - resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} - engines: {node: '>=12.20.0'} - - octokit@5.0.5: - resolution: {integrity: sha512-4+/OFSqOjoyULo7eN7EA97DE0Xydj/PW5aIckxqQIoFjFwqXKuFCvXUJObyJfBF9Khu4RL/jlDRI9FPaMGfPnw==} - engines: {node: '>= 20'} - - pathe@2.0.3: - resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - picomatch@4.0.4: - resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} - engines: {node: '>=12'} - - playwright-core@1.61.0: - resolution: {integrity: sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==} - engines: {node: '>=18'} - hasBin: true - - playwright@1.61.0: - resolution: {integrity: sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==} - engines: {node: '>=18'} - hasBin: true - - pngjs@7.0.0: - resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} - engines: {node: '>=14.19.0'} - - postcss@8.5.15: - resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} - engines: {node: ^10 || ^12 || >=14} - - rolldown@1.1.2: - resolution: {integrity: sha512-x0CrQQqCXWGeI8dTvFfN/Dnv3yMKT9hv5jFjlOreKAx9wqLq9wz7VvLLHyaAXC90/CpggTu9SisSbsJJTPSjNQ==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - - semver@7.8.5: - resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} - engines: {node: '>=10'} - hasBin: true - - siginfo@2.0.0: - resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - - sirv@3.0.2: - resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} - engines: {node: '>=18'} - - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - - stackback@0.0.2: - resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - - standardwebhooks@1.0.0: - resolution: {integrity: sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==} - - std-env@4.1.0: - resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - tinybench@2.9.0: - resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - - tinyexec@1.2.4: - resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} - engines: {node: '>=18'} - - tinyglobby@0.2.17: - resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} - engines: {node: '>=12.0.0'} - - tinyrainbow@3.1.0: - resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} - engines: {node: '>=14.0.0'} - - toad-cache@3.7.1: - resolution: {integrity: sha512-5DXWzE4Vz7xNHsv+xQ+MGfJYyC78Aok3tEr0MNwHoRf7vZnga1mQXZ4/Nsodld4VR6Wd+VhfmqnNrsRJyYPfrQ==} - engines: {node: '>=20'} - - totalist@3.0.1: - resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} - engines: {node: '>=6'} - - tslib@2.8.1: - resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - - tsx@4.22.4: - resolution: {integrity: sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==} - engines: {node: '>=18.0.0'} - hasBin: true - - typescript@6.0.3: - resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} - engines: {node: '>=14.17'} - hasBin: true - - undici-types@8.3.0: - resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} - - undici@8.5.0: - resolution: {integrity: sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==} - engines: {node: '>=22.19.0'} - - universal-github-app-jwt@2.2.2: - resolution: {integrity: sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==} - - universal-user-agent@7.0.3: - resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} - - vite-plugin-node@8.0.0: - resolution: {integrity: sha512-/jz+hrOULqRfsOwSrA3xU0rm2ED/XLsUkQU3VhuJqaR/gvyGxb0ZRxsamh/U84DCpvIEhYkp2ZwgyDVQ+AmIRQ==} - peerDependencies: - '@swc/core': ^1.7.26 - vite: ^8.0.0 - peerDependenciesMeta: - '@swc/core': - optional: true - - vite@8.1.0: - resolution: {integrity: sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - '@vitejs/devtools': ^0.3.0 - esbuild: ^0.27.0 || ^0.28.0 - jiti: '>=1.21.0' - less: ^4.0.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - '@vitejs/devtools': - optional: true - esbuild: - optional: true - jiti: - optional: true - less: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vitest@4.1.9: - resolution: {integrity: sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==} - engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@opentelemetry/api': ^1.9.0 - '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.9 - '@vitest/browser-preview': 4.1.9 - '@vitest/browser-webdriverio': 4.1.9 - '@vitest/coverage-istanbul': 4.1.9 - '@vitest/coverage-v8': 4.1.9 - '@vitest/ui': 4.1.9 - happy-dom: '*' - jsdom: '*' - vite: ^6.0.0 || ^7.0.0 || ^8.0.0 - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@opentelemetry/api': - optional: true - '@types/node': - optional: true - '@vitest/browser-playwright': - optional: true - '@vitest/browser-preview': - optional: true - '@vitest/browser-webdriverio': - optional: true - '@vitest/coverage-istanbul': - optional: true - '@vitest/coverage-v8': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - - why-is-node-running@2.3.0: - resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} - engines: {node: '>=8'} - hasBin: true - - ws@8.21.0: - resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - -snapshots: - - '@babel/helper-string-parser@7.29.7': {} - - '@babel/helper-validator-identifier@7.29.7': {} - - '@babel/parser@7.29.7': - dependencies: - '@babel/types': 7.29.7 - - '@babel/types@7.29.7': - dependencies: - '@babel/helper-string-parser': 7.29.7 - '@babel/helper-validator-identifier': 7.29.7 - - '@bcoe/v8-coverage@1.0.2': {} - - '@biomejs/biome@2.5.0': - optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.5.0 - '@biomejs/cli-darwin-x64': 2.5.0 - '@biomejs/cli-linux-arm64': 2.5.0 - '@biomejs/cli-linux-arm64-musl': 2.5.0 - '@biomejs/cli-linux-x64': 2.5.0 - '@biomejs/cli-linux-x64-musl': 2.5.0 - '@biomejs/cli-win32-arm64': 2.5.0 - '@biomejs/cli-win32-x64': 2.5.0 - - '@biomejs/cli-darwin-arm64@2.5.0': - optional: true - - '@biomejs/cli-darwin-x64@2.5.0': - optional: true - - '@biomejs/cli-linux-arm64-musl@2.5.0': - optional: true - - '@biomejs/cli-linux-arm64@2.5.0': - optional: true - - '@biomejs/cli-linux-x64-musl@2.5.0': - optional: true - - '@biomejs/cli-linux-x64@2.5.0': - optional: true - - '@biomejs/cli-win32-arm64@2.5.0': - optional: true - - '@biomejs/cli-win32-x64@2.5.0': - optional: true - - '@blazediff/core@1.9.1': {} - - '@emnapi/core@1.11.1': - dependencies: - '@emnapi/wasi-threads': 1.2.2 - tslib: 2.8.1 - optional: true - - '@emnapi/runtime@1.11.1': - dependencies: - tslib: 2.8.1 - optional: true - - '@emnapi/wasi-threads@1.2.2': - dependencies: - tslib: 2.8.1 - optional: true - - '@esbuild/aix-ppc64@0.28.1': - optional: true - - '@esbuild/android-arm64@0.28.1': - optional: true - - '@esbuild/android-arm@0.28.1': - optional: true - - '@esbuild/android-x64@0.28.1': - optional: true - - '@esbuild/darwin-arm64@0.28.1': - optional: true - - '@esbuild/darwin-x64@0.28.1': - optional: true - - '@esbuild/freebsd-arm64@0.28.1': - optional: true - - '@esbuild/freebsd-x64@0.28.1': - optional: true - - '@esbuild/linux-arm64@0.28.1': - optional: true - - '@esbuild/linux-arm@0.28.1': - optional: true - - '@esbuild/linux-ia32@0.28.1': - optional: true - - '@esbuild/linux-loong64@0.28.1': - optional: true - - '@esbuild/linux-mips64el@0.28.1': - optional: true - - '@esbuild/linux-ppc64@0.28.1': - optional: true - - '@esbuild/linux-riscv64@0.28.1': - optional: true - - '@esbuild/linux-s390x@0.28.1': - optional: true - - '@esbuild/linux-x64@0.28.1': - optional: true - - '@esbuild/netbsd-arm64@0.28.1': - optional: true - - '@esbuild/netbsd-x64@0.28.1': - optional: true - - '@esbuild/openbsd-arm64@0.28.1': - optional: true - - '@esbuild/openbsd-x64@0.28.1': - optional: true - - '@esbuild/openharmony-arm64@0.28.1': - optional: true - - '@esbuild/sunos-x64@0.28.1': - optional: true - - '@esbuild/win32-arm64@0.28.1': - optional: true - - '@esbuild/win32-ia32@0.28.1': - optional: true - - '@esbuild/win32-x64@0.28.1': - optional: true - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/sourcemap-codec@1.5.5': {} - - '@jridgewell/trace-mapping@0.3.31': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 - - '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': - dependencies: - '@emnapi/core': 1.11.1 - '@emnapi/runtime': 1.11.1 - '@tybys/wasm-util': 0.10.2 - optional: true - - '@octokit/app@16.1.2': - dependencies: - '@octokit/auth-app': 8.2.0 - '@octokit/auth-unauthenticated': 7.0.3 - '@octokit/core': 7.0.6 - '@octokit/oauth-app': 8.0.3 - '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) - '@octokit/types': 16.0.0 - '@octokit/webhooks': 14.2.0 - - '@octokit/auth-app@8.2.0': - dependencies: - '@octokit/auth-oauth-app': 9.0.3 - '@octokit/auth-oauth-user': 6.0.2 - '@octokit/request': 10.0.10 - '@octokit/request-error': 7.1.0 - '@octokit/types': 16.0.0 - toad-cache: 3.7.1 - universal-github-app-jwt: 2.2.2 - universal-user-agent: 7.0.3 - - '@octokit/auth-oauth-app@9.0.3': - dependencies: - '@octokit/auth-oauth-device': 8.0.3 - '@octokit/auth-oauth-user': 6.0.2 - '@octokit/request': 10.0.10 - '@octokit/types': 16.0.0 - universal-user-agent: 7.0.3 - - '@octokit/auth-oauth-device@8.0.3': - dependencies: - '@octokit/oauth-methods': 6.0.2 - '@octokit/request': 10.0.10 - '@octokit/types': 16.0.0 - universal-user-agent: 7.0.3 - - '@octokit/auth-oauth-user@6.0.2': - dependencies: - '@octokit/auth-oauth-device': 8.0.3 - '@octokit/oauth-methods': 6.0.2 - '@octokit/request': 10.0.10 - '@octokit/types': 16.0.0 - universal-user-agent: 7.0.3 - - '@octokit/auth-token@6.0.0': {} - - '@octokit/auth-unauthenticated@7.0.3': - dependencies: - '@octokit/request-error': 7.1.0 - '@octokit/types': 16.0.0 - - '@octokit/core@7.0.6': - dependencies: - '@octokit/auth-token': 6.0.0 - '@octokit/graphql': 9.0.3 - '@octokit/request': 10.0.10 - '@octokit/request-error': 7.1.0 - '@octokit/types': 16.0.0 - before-after-hook: 4.0.0 - universal-user-agent: 7.0.3 - - '@octokit/endpoint@11.0.3': - dependencies: - '@octokit/types': 16.0.0 - universal-user-agent: 7.0.3 - - '@octokit/graphql@9.0.3': - dependencies: - '@octokit/request': 10.0.10 - '@octokit/types': 16.0.0 - universal-user-agent: 7.0.3 - - '@octokit/oauth-app@8.0.3': - dependencies: - '@octokit/auth-oauth-app': 9.0.3 - '@octokit/auth-oauth-user': 6.0.2 - '@octokit/auth-unauthenticated': 7.0.3 - '@octokit/core': 7.0.6 - '@octokit/oauth-authorization-url': 8.0.0 - '@octokit/oauth-methods': 6.0.2 - '@types/aws-lambda': 8.10.162 - universal-user-agent: 7.0.3 - - '@octokit/oauth-authorization-url@8.0.0': {} - - '@octokit/oauth-methods@6.0.2': - dependencies: - '@octokit/oauth-authorization-url': 8.0.0 - '@octokit/request': 10.0.10 - '@octokit/request-error': 7.1.0 - '@octokit/types': 16.0.0 - - '@octokit/openapi-types@27.0.0': {} - - '@octokit/openapi-webhooks-types@12.1.0': {} - - '@octokit/plugin-paginate-graphql@6.0.0(@octokit/core@7.0.6)': - dependencies: - '@octokit/core': 7.0.6 - - '@octokit/plugin-paginate-rest@14.0.0(@octokit/core@7.0.6)': - dependencies: - '@octokit/core': 7.0.6 - '@octokit/types': 16.0.0 - - '@octokit/plugin-rest-endpoint-methods@17.0.0(@octokit/core@7.0.6)': - dependencies: - '@octokit/core': 7.0.6 - '@octokit/types': 16.0.0 - - '@octokit/plugin-retry@8.1.0(@octokit/core@7.0.6)': - dependencies: - '@octokit/core': 7.0.6 - '@octokit/request-error': 7.1.0 - '@octokit/types': 16.0.0 - bottleneck: 2.19.5 - - '@octokit/plugin-throttling@11.0.3(@octokit/core@7.0.6)': - dependencies: - '@octokit/core': 7.0.6 - '@octokit/types': 16.0.0 - bottleneck: 2.19.5 - - '@octokit/request-error@7.1.0': - dependencies: - '@octokit/types': 16.0.0 - - '@octokit/request@10.0.10': - dependencies: - '@octokit/endpoint': 11.0.3 - '@octokit/request-error': 7.1.0 - '@octokit/types': 16.0.0 - content-type: 2.0.0 - json-with-bigint: 3.5.8 - universal-user-agent: 7.0.3 - - '@octokit/types@16.0.0': - dependencies: - '@octokit/openapi-types': 27.0.0 - - '@octokit/webhooks-methods@6.0.0': {} - - '@octokit/webhooks@14.2.0': - dependencies: - '@octokit/openapi-webhooks-types': 12.1.0 - '@octokit/request-error': 7.1.0 - '@octokit/webhooks-methods': 6.0.0 - - '@oxc-project/types@0.137.0': {} - - '@polka/url@1.0.0-next.29': {} - - '@rolldown/binding-android-arm64@1.1.2': - optional: true - - '@rolldown/binding-darwin-arm64@1.1.2': - optional: true - - '@rolldown/binding-darwin-x64@1.1.2': - optional: true - - '@rolldown/binding-freebsd-x64@1.1.2': - optional: true - - '@rolldown/binding-linux-arm-gnueabihf@1.1.2': - optional: true - - '@rolldown/binding-linux-arm64-gnu@1.1.2': - optional: true - - '@rolldown/binding-linux-arm64-musl@1.1.2': - optional: true - - '@rolldown/binding-linux-ppc64-gnu@1.1.2': - optional: true - - '@rolldown/binding-linux-s390x-gnu@1.1.2': - optional: true - - '@rolldown/binding-linux-x64-gnu@1.1.2': - optional: true - - '@rolldown/binding-linux-x64-musl@1.1.2': - optional: true - - '@rolldown/binding-openharmony-arm64@1.1.2': - optional: true - - '@rolldown/binding-wasm32-wasi@1.1.2': - dependencies: - '@emnapi/core': 1.11.1 - '@emnapi/runtime': 1.11.1 - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) - optional: true - - '@rolldown/binding-win32-arm64-msvc@1.1.2': - optional: true - - '@rolldown/binding-win32-x64-msvc@1.1.2': - optional: true - - '@rolldown/pluginutils@1.0.1': {} - - '@stablelib/base64@1.0.1': {} - - '@standard-schema/spec@1.1.0': {} - - '@tybys/wasm-util@0.10.2': - dependencies: - tslib: 2.8.1 - optional: true - - '@types/aws-lambda@8.10.162': {} - - '@types/chai@5.2.3': - dependencies: - '@types/deep-eql': 4.0.2 - assertion-error: 2.0.1 - - '@types/deep-eql@4.0.2': {} - - '@types/estree@1.0.9': {} - - '@types/node@26.0.0': - dependencies: - undici-types: 8.3.0 - - '@vitest/browser-playwright@4.1.9(playwright@1.61.0)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))(vitest@4.1.9)': - dependencies: - '@vitest/browser': 4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))(vitest@4.1.9) - '@vitest/mocker': 4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) - playwright: 1.61.0 - tinyrainbow: 3.1.0 - vitest: 4.1.9(@types/node@26.0.0)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) - transitivePeerDependencies: - - bufferutil - - msw - - utf-8-validate - - vite - - '@vitest/browser@4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))(vitest@4.1.9)': - dependencies: - '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) - '@vitest/utils': 4.1.9 - magic-string: 0.30.21 - pngjs: 7.0.0 - sirv: 3.0.2 - tinyrainbow: 3.1.0 - vitest: 4.1.9(@types/node@26.0.0)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) - ws: 8.21.0 - transitivePeerDependencies: - - bufferutil - - msw - - utf-8-validate - - vite - - '@vitest/coverage-v8@4.1.9(@vitest/browser@4.1.9)(vitest@4.1.9)': - dependencies: - '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.1.9 - ast-v8-to-istanbul: 1.0.4 - istanbul-lib-coverage: 3.2.2 - istanbul-lib-report: 3.0.1 - istanbul-reports: 3.2.0 - magicast: 0.5.3 - obug: 2.1.3 - std-env: 4.1.0 - tinyrainbow: 3.1.0 - vitest: 4.1.9(@types/node@26.0.0)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) - optionalDependencies: - '@vitest/browser': 4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))(vitest@4.1.9) - - '@vitest/expect@4.1.9': - dependencies: - '@standard-schema/spec': 1.1.0 - '@types/chai': 5.2.3 - '@vitest/spy': 4.1.9 - '@vitest/utils': 4.1.9 - chai: 6.2.2 - tinyrainbow: 3.1.0 - - '@vitest/mocker@4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))': - dependencies: - '@vitest/spy': 4.1.9 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - vite: 8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4) - - '@vitest/pretty-format@4.1.9': - dependencies: - tinyrainbow: 3.1.0 - - '@vitest/runner@4.1.9': - dependencies: - '@vitest/utils': 4.1.9 - pathe: 2.0.3 - - '@vitest/snapshot@4.1.9': - dependencies: - '@vitest/pretty-format': 4.1.9 - '@vitest/utils': 4.1.9 - magic-string: 0.30.21 - pathe: 2.0.3 - - '@vitest/spy@4.1.9': {} - - '@vitest/utils@4.1.9': - dependencies: - '@vitest/pretty-format': 4.1.9 - convert-source-map: 2.0.0 - tinyrainbow: 3.1.0 - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - assertion-error@2.0.1: {} - - ast-v8-to-istanbul@1.0.4: - dependencies: - '@jridgewell/trace-mapping': 0.3.31 - estree-walker: 3.0.3 - js-tokens: 10.0.0 - - before-after-hook@4.0.0: {} - - bottleneck@2.19.5: {} - - chai@6.2.2: {} - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - content-type@2.0.0: {} - - convert-source-map@2.0.0: {} - - debounce@2.2.0: {} - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - detect-libc@2.1.2: {} - - es-module-lexer@2.1.0: {} - - esbuild@0.28.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.28.1 - '@esbuild/android-arm': 0.28.1 - '@esbuild/android-arm64': 0.28.1 - '@esbuild/android-x64': 0.28.1 - '@esbuild/darwin-arm64': 0.28.1 - '@esbuild/darwin-x64': 0.28.1 - '@esbuild/freebsd-arm64': 0.28.1 - '@esbuild/freebsd-x64': 0.28.1 - '@esbuild/linux-arm': 0.28.1 - '@esbuild/linux-arm64': 0.28.1 - '@esbuild/linux-ia32': 0.28.1 - '@esbuild/linux-loong64': 0.28.1 - '@esbuild/linux-mips64el': 0.28.1 - '@esbuild/linux-ppc64': 0.28.1 - '@esbuild/linux-riscv64': 0.28.1 - '@esbuild/linux-s390x': 0.28.1 - '@esbuild/linux-x64': 0.28.1 - '@esbuild/netbsd-arm64': 0.28.1 - '@esbuild/netbsd-x64': 0.28.1 - '@esbuild/openbsd-arm64': 0.28.1 - '@esbuild/openbsd-x64': 0.28.1 - '@esbuild/openharmony-arm64': 0.28.1 - '@esbuild/sunos-x64': 0.28.1 - '@esbuild/win32-arm64': 0.28.1 - '@esbuild/win32-ia32': 0.28.1 - '@esbuild/win32-x64': 0.28.1 - - estree-walker@3.0.3: - dependencies: - '@types/estree': 1.0.9 - - expect-type@1.3.0: {} - - fast-sha256@1.3.0: {} - - fdir@6.5.0(picomatch@4.0.4): - optionalDependencies: - picomatch: 4.0.4 - - fsevents@2.3.2: - optional: true - - fsevents@2.3.3: - optional: true - - has-flag@4.0.0: {} - - html-escaper@2.0.2: {} - - istanbul-lib-coverage@3.2.2: {} - - istanbul-lib-report@3.0.1: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 4.0.0 - supports-color: 7.2.0 - - istanbul-reports@3.2.0: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - - js-tokens@10.0.0: {} - - json-with-bigint@3.5.8: {} - - lightningcss-android-arm64@1.32.0: - optional: true - - lightningcss-darwin-arm64@1.32.0: - optional: true - - lightningcss-darwin-x64@1.32.0: - optional: true - - lightningcss-freebsd-x64@1.32.0: - optional: true - - lightningcss-linux-arm-gnueabihf@1.32.0: - optional: true - - lightningcss-linux-arm64-gnu@1.32.0: - optional: true - - lightningcss-linux-arm64-musl@1.32.0: - optional: true - - lightningcss-linux-x64-gnu@1.32.0: - optional: true - - lightningcss-linux-x64-musl@1.32.0: - optional: true - - lightningcss-win32-arm64-msvc@1.32.0: - optional: true - - lightningcss-win32-x64-msvc@1.32.0: - optional: true - - lightningcss@1.32.0: - dependencies: - detect-libc: 2.1.2 - optionalDependencies: - lightningcss-android-arm64: 1.32.0 - lightningcss-darwin-arm64: 1.32.0 - lightningcss-darwin-x64: 1.32.0 - lightningcss-freebsd-x64: 1.32.0 - lightningcss-linux-arm-gnueabihf: 1.32.0 - lightningcss-linux-arm64-gnu: 1.32.0 - lightningcss-linux-arm64-musl: 1.32.0 - lightningcss-linux-x64-gnu: 1.32.0 - lightningcss-linux-x64-musl: 1.32.0 - lightningcss-win32-arm64-msvc: 1.32.0 - lightningcss-win32-x64-msvc: 1.32.0 - - magic-string@0.30.21: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - - magicast@0.5.3: - dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 - source-map-js: 1.2.1 - - make-dir@4.0.0: - dependencies: - semver: 7.8.5 - - mrmime@2.0.1: {} - - ms@2.1.3: {} - - nanoid@3.3.15: {} - - obug@2.1.3: {} - - octokit@5.0.5: - dependencies: - '@octokit/app': 16.1.2 - '@octokit/core': 7.0.6 - '@octokit/oauth-app': 8.0.3 - '@octokit/plugin-paginate-graphql': 6.0.0(@octokit/core@7.0.6) - '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) - '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) - '@octokit/plugin-retry': 8.1.0(@octokit/core@7.0.6) - '@octokit/plugin-throttling': 11.0.3(@octokit/core@7.0.6) - '@octokit/request-error': 7.1.0 - '@octokit/types': 16.0.0 - '@octokit/webhooks': 14.2.0 - - pathe@2.0.3: {} - - picocolors@1.1.1: {} - - picomatch@4.0.4: {} - - playwright-core@1.61.0: {} - - playwright@1.61.0: - dependencies: - playwright-core: 1.61.0 - optionalDependencies: - fsevents: 2.3.2 - - pngjs@7.0.0: {} - - postcss@8.5.15: - dependencies: - nanoid: 3.3.15 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - rolldown@1.1.2: - dependencies: - '@oxc-project/types': 0.137.0 - '@rolldown/pluginutils': 1.0.1 - optionalDependencies: - '@rolldown/binding-android-arm64': 1.1.2 - '@rolldown/binding-darwin-arm64': 1.1.2 - '@rolldown/binding-darwin-x64': 1.1.2 - '@rolldown/binding-freebsd-x64': 1.1.2 - '@rolldown/binding-linux-arm-gnueabihf': 1.1.2 - '@rolldown/binding-linux-arm64-gnu': 1.1.2 - '@rolldown/binding-linux-arm64-musl': 1.1.2 - '@rolldown/binding-linux-ppc64-gnu': 1.1.2 - '@rolldown/binding-linux-s390x-gnu': 1.1.2 - '@rolldown/binding-linux-x64-gnu': 1.1.2 - '@rolldown/binding-linux-x64-musl': 1.1.2 - '@rolldown/binding-openharmony-arm64': 1.1.2 - '@rolldown/binding-wasm32-wasi': 1.1.2 - '@rolldown/binding-win32-arm64-msvc': 1.1.2 - '@rolldown/binding-win32-x64-msvc': 1.1.2 - - semver@7.8.5: {} - - siginfo@2.0.0: {} - - sirv@3.0.2: - dependencies: - '@polka/url': 1.0.0-next.29 - mrmime: 2.0.1 - totalist: 3.0.1 - - source-map-js@1.2.1: {} - - stackback@0.0.2: {} - - standardwebhooks@1.0.0: - dependencies: - '@stablelib/base64': 1.0.1 - fast-sha256: 1.3.0 - - std-env@4.1.0: {} - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - tinybench@2.9.0: {} - - tinyexec@1.2.4: {} - - tinyglobby@0.2.17: - dependencies: - fdir: 6.5.0(picomatch@4.0.4) - picomatch: 4.0.4 - - tinyrainbow@3.1.0: {} - - toad-cache@3.7.1: {} - - totalist@3.0.1: {} - - tslib@2.8.1: - optional: true - - tsx@4.22.4: - dependencies: - esbuild: 0.28.1 - optionalDependencies: - fsevents: 2.3.3 - - typescript@6.0.3: {} - - undici-types@8.3.0: {} - - undici@8.5.0: {} - - universal-github-app-jwt@2.2.2: {} - - universal-user-agent@7.0.3: {} - - vite-plugin-node@8.0.0(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)): - dependencies: - chalk: 4.1.2 - debounce: 2.2.0 - debug: 4.4.3 - vite: 8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4) - transitivePeerDependencies: - - supports-color - - vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4): - dependencies: - lightningcss: 1.32.0 - picomatch: 4.0.4 - postcss: 8.5.15 - rolldown: 1.1.2 - tinyglobby: 0.2.17 - optionalDependencies: - '@types/node': 26.0.0 - esbuild: 0.28.1 - fsevents: 2.3.3 - tsx: 4.22.4 - - vitest@4.1.9(@types/node@26.0.0)(@vitest/browser-playwright@4.1.9)(@vitest/coverage-v8@4.1.9)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)): - dependencies: - '@vitest/expect': 4.1.9 - '@vitest/mocker': 4.1.9(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)) - '@vitest/pretty-format': 4.1.9 - '@vitest/runner': 4.1.9 - '@vitest/snapshot': 4.1.9 - '@vitest/spy': 4.1.9 - '@vitest/utils': 4.1.9 - es-module-lexer: 2.1.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.3 - pathe: 2.0.3 - picomatch: 4.0.4 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.2.4 - tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4) - why-is-node-running: 2.3.0 - optionalDependencies: - '@types/node': 26.0.0 - '@vitest/browser-playwright': 4.1.9(playwright@1.61.0)(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4))(vitest@4.1.9) - '@vitest/coverage-v8': 4.1.9(@vitest/browser@4.1.9)(vitest@4.1.9) - transitivePeerDependencies: - - msw - - why-is-node-running@2.3.0: - dependencies: - siginfo: 2.0.0 - stackback: 0.0.2 - - ws@8.21.0: {} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 95d7a69..b236138 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -76,6 +76,9 @@ importers: '@webhook-objects/client': specifier: workspace:* version: link:../client + octokit: + specifier: ^5.0.5 + version: 5.0.5 packages/low-battery-switch: dependencies: @@ -355,6 +358,113 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 + '@octokit/app@16.1.2': + resolution: {integrity: sha512-8j7sEpUYVj18dxvh0KWj6W/l6uAiVRBl1JBDVRqH1VHKAO/G5eRVl4yEoYACjakWers1DjUkcCHyJNQK47JqyQ==} + engines: {node: '>= 20'} + + '@octokit/auth-app@8.2.0': + resolution: {integrity: sha512-vVjdtQQwomrZ4V46B9LaCsxsySxGoHsyw6IYBov/TqJVROrlYdyNgw5q6tQbB7KZt53v1l1W53RiqTvpzL907g==} + engines: {node: '>= 20'} + + '@octokit/auth-oauth-app@9.0.3': + resolution: {integrity: sha512-+yoFQquaF8OxJSxTb7rnytBIC2ZLbLqA/yb71I4ZXT9+Slw4TziV9j/kyGhUFRRTF2+7WlnIWsePZCWHs+OGjg==} + engines: {node: '>= 20'} + + '@octokit/auth-oauth-device@8.0.3': + resolution: {integrity: sha512-zh2W0mKKMh/VWZhSqlaCzY7qFyrgd9oTWmTmHaXnHNeQRCZr/CXy2jCgHo4e4dJVTiuxP5dLa0YM5p5QVhJHbw==} + engines: {node: '>= 20'} + + '@octokit/auth-oauth-user@6.0.2': + resolution: {integrity: sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A==} + engines: {node: '>= 20'} + + '@octokit/auth-token@6.0.0': + resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} + engines: {node: '>= 20'} + + '@octokit/auth-unauthenticated@7.0.3': + resolution: {integrity: sha512-8Jb1mtUdmBHL7lGmop9mU9ArMRUTRhg8vp0T1VtZ4yd9vEm3zcLwmjQkhNEduKawOOORie61xhtYIhTDN+ZQ3g==} + engines: {node: '>= 20'} + + '@octokit/core@7.0.6': + resolution: {integrity: sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==} + engines: {node: '>= 20'} + + '@octokit/endpoint@11.0.3': + resolution: {integrity: sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==} + engines: {node: '>= 20'} + + '@octokit/graphql@9.0.3': + resolution: {integrity: sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==} + engines: {node: '>= 20'} + + '@octokit/oauth-app@8.0.3': + resolution: {integrity: sha512-jnAjvTsPepyUaMu9e69hYBuozEPgYqP4Z3UnpmvoIzHDpf8EXDGvTY1l1jK0RsZ194oRd+k6Hm13oRU8EoDFwg==} + engines: {node: '>= 20'} + + '@octokit/oauth-authorization-url@8.0.0': + resolution: {integrity: sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ==} + engines: {node: '>= 20'} + + '@octokit/oauth-methods@6.0.2': + resolution: {integrity: sha512-HiNOO3MqLxlt5Da5bZbLV8Zarnphi4y9XehrbaFMkcoJ+FL7sMxH/UlUsCVxpddVu4qvNDrBdaTVE2o4ITK8ng==} + engines: {node: '>= 20'} + + '@octokit/openapi-types@27.0.0': + resolution: {integrity: sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==} + + '@octokit/openapi-webhooks-types@12.1.0': + resolution: {integrity: sha512-WiuzhOsiOvb7W3Pvmhf8d2C6qaLHXrWiLBP4nJ/4kydu+wpagV5Fkz9RfQwV2afYzv3PB+3xYgp4mAdNGjDprA==} + + '@octokit/plugin-paginate-graphql@6.0.0': + resolution: {integrity: sha512-crfpnIoFiBtRkvPqOyLOsw12XsveYuY2ieP6uYDosoUegBJpSVxGwut9sxUgFFcll3VTOTqpUf8yGd8x1OmAkQ==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-paginate-rest@14.0.0': + resolution: {integrity: sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-rest-endpoint-methods@17.0.0': + resolution: {integrity: sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-retry@8.1.0': + resolution: {integrity: sha512-O1FZgXeiGb2sowEr/hYTr6YunGdSAFWnr2fyW39Ah85H8O33ELASQxcvOFF5LE6Tjekcyu2ms4qAzJVhSaJxTw==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': '>=7' + + '@octokit/plugin-throttling@11.0.3': + resolution: {integrity: sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg==} + engines: {node: '>= 20'} + peerDependencies: + '@octokit/core': ^7.0.0 + + '@octokit/request-error@7.1.0': + resolution: {integrity: sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==} + engines: {node: '>= 20'} + + '@octokit/request@10.0.10': + resolution: {integrity: sha512-KxNC2pTqqhszMNrf12ZRd4PonRgyJdsM4F/jySiddQK+DsRcfBtUvqn8t7UsyZhnRJHvX46OohDt5N3VqIWC2w==} + engines: {node: '>= 20'} + + '@octokit/types@16.0.0': + resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} + + '@octokit/webhooks-methods@6.0.0': + resolution: {integrity: sha512-MFlzzoDJVw/GcbfzVC1RLR36QqkTLUf79vLVO3D+xn7r0QgxnFoLZgtrzxiQErAjFUOdH6fas2KeQJ1yr/qaXQ==} + engines: {node: '>= 20'} + + '@octokit/webhooks@14.2.0': + resolution: {integrity: sha512-da6KbdNCV5sr1/txD896V+6W0iamFWrvVl8cHkBSPT+YlvmT3DwXa4jxZnQc+gnuTEqSWbBeoSZYTayXH9wXcw==} + engines: {node: '>= 20'} + '@oxc-project/types@0.137.0': resolution: {integrity: sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==} @@ -468,6 +578,9 @@ packages: '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@types/aws-lambda@8.10.162': + resolution: {integrity: sha512-Fn658grtLOci1oxi1391vvDWJRKNGWRSqfxRkmN/Iy3c0tQH1USMKEXcPYHLvope+ZgTFocx9FRQJx1muBL6qw==} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -540,6 +653,12 @@ packages: ast-v8-to-istanbul@1.0.4: resolution: {integrity: sha512-0bC0/4bTSrnwdhU3IsZDwEdojvuPrSg59OYZfKsLRtJZ0u8VBx9DebfqqG8bRdCC0I7vjgxmPi41P0lpkhJHtA==} + before-after-hook@4.0.0: + resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} + + bottleneck@2.19.5: + resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} + chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} @@ -555,6 +674,10 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + content-type@2.0.0: + resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} + engines: {node: '>=18'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -634,6 +757,9 @@ packages: js-tokens@10.0.0: resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + json-with-bigint@3.5.8: + resolution: {integrity: sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==} + lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} @@ -734,6 +860,10 @@ packages: resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} engines: {node: '>=12.20.0'} + octokit@5.0.5: + resolution: {integrity: sha512-4+/OFSqOjoyULo7eN7EA97DE0Xydj/PW5aIckxqQIoFjFwqXKuFCvXUJObyJfBF9Khu4RL/jlDRI9FPaMGfPnw==} + engines: {node: '>= 20'} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -811,6 +941,10 @@ packages: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} + toad-cache@3.7.1: + resolution: {integrity: sha512-5DXWzE4Vz7xNHsv+xQ+MGfJYyC78Aok3tEr0MNwHoRf7vZnga1mQXZ4/Nsodld4VR6Wd+VhfmqnNrsRJyYPfrQ==} + engines: {node: '>=20'} + totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} @@ -835,6 +969,12 @@ packages: resolution: {integrity: sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==} engines: {node: '>=22.19.0'} + universal-github-app-jwt@2.2.2: + resolution: {integrity: sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==} + + universal-user-agent@7.0.3: + resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} + vite-plugin-node@8.0.0: resolution: {integrity: sha512-/jz+hrOULqRfsOwSrA3xU0rm2ED/XLsUkQU3VhuJqaR/gvyGxb0ZRxsamh/U84DCpvIEhYkp2ZwgyDVQ+AmIRQ==} peerDependencies: @@ -1109,6 +1249,154 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true + '@octokit/app@16.1.2': + dependencies: + '@octokit/auth-app': 8.2.0 + '@octokit/auth-unauthenticated': 7.0.3 + '@octokit/core': 7.0.6 + '@octokit/oauth-app': 8.0.3 + '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) + '@octokit/types': 16.0.0 + '@octokit/webhooks': 14.2.0 + + '@octokit/auth-app@8.2.0': + dependencies: + '@octokit/auth-oauth-app': 9.0.3 + '@octokit/auth-oauth-user': 6.0.2 + '@octokit/request': 10.0.10 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + toad-cache: 3.7.1 + universal-github-app-jwt: 2.2.2 + universal-user-agent: 7.0.3 + + '@octokit/auth-oauth-app@9.0.3': + dependencies: + '@octokit/auth-oauth-device': 8.0.3 + '@octokit/auth-oauth-user': 6.0.2 + '@octokit/request': 10.0.10 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/auth-oauth-device@8.0.3': + dependencies: + '@octokit/oauth-methods': 6.0.2 + '@octokit/request': 10.0.10 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/auth-oauth-user@6.0.2': + dependencies: + '@octokit/auth-oauth-device': 8.0.3 + '@octokit/oauth-methods': 6.0.2 + '@octokit/request': 10.0.10 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/auth-token@6.0.0': {} + + '@octokit/auth-unauthenticated@7.0.3': + dependencies: + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + + '@octokit/core@7.0.6': + dependencies: + '@octokit/auth-token': 6.0.0 + '@octokit/graphql': 9.0.3 + '@octokit/request': 10.0.10 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + before-after-hook: 4.0.0 + universal-user-agent: 7.0.3 + + '@octokit/endpoint@11.0.3': + dependencies: + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/graphql@9.0.3': + dependencies: + '@octokit/request': 10.0.10 + '@octokit/types': 16.0.0 + universal-user-agent: 7.0.3 + + '@octokit/oauth-app@8.0.3': + dependencies: + '@octokit/auth-oauth-app': 9.0.3 + '@octokit/auth-oauth-user': 6.0.2 + '@octokit/auth-unauthenticated': 7.0.3 + '@octokit/core': 7.0.6 + '@octokit/oauth-authorization-url': 8.0.0 + '@octokit/oauth-methods': 6.0.2 + '@types/aws-lambda': 8.10.162 + universal-user-agent: 7.0.3 + + '@octokit/oauth-authorization-url@8.0.0': {} + + '@octokit/oauth-methods@6.0.2': + dependencies: + '@octokit/oauth-authorization-url': 8.0.0 + '@octokit/request': 10.0.10 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + + '@octokit/openapi-types@27.0.0': {} + + '@octokit/openapi-webhooks-types@12.1.0': {} + + '@octokit/plugin-paginate-graphql@6.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + + '@octokit/plugin-paginate-rest@14.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 + + '@octokit/plugin-rest-endpoint-methods@17.0.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 + + '@octokit/plugin-retry@8.1.0(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + bottleneck: 2.19.5 + + '@octokit/plugin-throttling@11.0.3(@octokit/core@7.0.6)': + dependencies: + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 + bottleneck: 2.19.5 + + '@octokit/request-error@7.1.0': + dependencies: + '@octokit/types': 16.0.0 + + '@octokit/request@10.0.10': + dependencies: + '@octokit/endpoint': 11.0.3 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + content-type: 2.0.0 + json-with-bigint: 3.5.8 + universal-user-agent: 7.0.3 + + '@octokit/types@16.0.0': + dependencies: + '@octokit/openapi-types': 27.0.0 + + '@octokit/webhooks-methods@6.0.0': {} + + '@octokit/webhooks@14.2.0': + dependencies: + '@octokit/openapi-webhooks-types': 12.1.0 + '@octokit/request-error': 7.1.0 + '@octokit/webhooks-methods': 6.0.0 + '@oxc-project/types@0.137.0': {} '@polka/url@1.0.0-next.29': {} @@ -1173,6 +1461,8 @@ snapshots: tslib: 2.8.1 optional: true + '@types/aws-lambda@8.10.162': {} + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -1285,6 +1575,10 @@ snapshots: estree-walker: 3.0.3 js-tokens: 10.0.0 + before-after-hook@4.0.0: {} + + bottleneck@2.19.5: {} + chai@6.2.2: {} chalk@4.1.2: @@ -1298,6 +1592,8 @@ snapshots: color-name@1.1.4: {} + content-type@2.0.0: {} + convert-source-map@2.0.0: {} debounce@2.2.0: {} @@ -1376,6 +1672,8 @@ snapshots: js-tokens@10.0.0: {} + json-with-bigint@3.5.8: {} + lightningcss-android-arm64@1.32.0: optional: true @@ -1447,6 +1745,20 @@ snapshots: obug@2.1.3: {} + octokit@5.0.5: + dependencies: + '@octokit/app': 16.1.2 + '@octokit/core': 7.0.6 + '@octokit/oauth-app': 8.0.3 + '@octokit/plugin-paginate-graphql': 6.0.0(@octokit/core@7.0.6) + '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) + '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) + '@octokit/plugin-retry': 8.1.0(@octokit/core@7.0.6) + '@octokit/plugin-throttling': 11.0.3(@octokit/core@7.0.6) + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 + '@octokit/webhooks': 14.2.0 + pathe@2.0.3: {} picocolors@1.1.1: {} @@ -1526,6 +1838,8 @@ snapshots: tinyrainbow@3.1.0: {} + toad-cache@3.7.1: {} + totalist@3.0.1: {} tslib@2.8.1: @@ -1543,6 +1857,10 @@ snapshots: undici@8.5.0: {} + universal-github-app-jwt@2.2.2: {} + + universal-user-agent@7.0.3: {} + vite-plugin-node@8.0.0(vite@8.1.0(@types/node@26.0.0)(esbuild@0.28.1)(tsx@4.22.4)): dependencies: chalk: 4.1.2 From 8ec3e8fee18a9c3c3471ce10dc48266e4c90f1c3 Mon Sep 17 00:00:00 2001 From: Gianluca Esposito Date: Mon, 20 Jul 2026 16:52:55 +0200 Subject: [PATCH 8/8] refactor: migrate to the published @gathertown/webhook-object-sdk The workspace @webhook-objects/client is being removed in favor of the npm-published SDK (see #15): createWebhookObjectClient replaces new Client, send takes (type, data) and stamps the timestamp itself, ping() replaces requestMetadata(), and the client type is WebhookObjectClient. The @gathertown packages join the minimumReleaseAge exclusions since they are first-party. Co-authored-by: Cursor --- packages/gh-prs-ready-need-review/README.md | 2 +- .../gh-prs-ready-need-review/package.json | 2 +- .../gh-prs-ready-need-review/src/index.ts | 6 +++--- packages/gh-prs-ready-need-review/src/ping.ts | 6 +++--- .../src/polls/registry.ts | 10 +++++----- .../src/publishers/gather-counter.ts | 10 +++------- .../gh-prs-ready-need-review/src/reset.ts | 13 ++++-------- pnpm-lock.yaml | 20 ++++++++++++++++--- pnpm-workspace.yaml | 2 ++ 9 files changed, 39 insertions(+), 32 deletions(-) diff --git a/packages/gh-prs-ready-need-review/README.md b/packages/gh-prs-ready-need-review/README.md index 65dadc4..48e542f 100644 --- a/packages/gh-prs-ready-need-review/README.md +++ b/packages/gh-prs-ready-need-review/README.md @@ -39,4 +39,4 @@ pnpm --filter @webhook-objects/gh-prs-ready-need-review start | `GATHER_REVIEW_WEBHOOK_SECRET` | yes | — | `whsec_…` for that object | | `POLL_INTERVAL_MS` | no | `600000` | poll interval (min 10000) | -Events are signed and sent via `@webhook-objects/client`. +Events are signed and sent via [`@gathertown/webhook-object-sdk`](https://www.npmjs.com/package/@gathertown/webhook-object-sdk). diff --git a/packages/gh-prs-ready-need-review/package.json b/packages/gh-prs-ready-need-review/package.json index f277561..1e63599 100644 --- a/packages/gh-prs-ready-need-review/package.json +++ b/packages/gh-prs-ready-need-review/package.json @@ -22,7 +22,7 @@ } ], "dependencies": { - "@webhook-objects/client": "workspace:*", + "@gathertown/webhook-object-sdk": "^0.1.1", "octokit": "^5.0.5" } } diff --git a/packages/gh-prs-ready-need-review/src/index.ts b/packages/gh-prs-ready-need-review/src/index.ts index feb6ff8..bfb90a4 100644 --- a/packages/gh-prs-ready-need-review/src/index.ts +++ b/packages/gh-prs-ready-need-review/src/index.ts @@ -6,7 +6,7 @@ * * @module */ -import { Client } from "@webhook-objects/client/node"; +import { createWebhookObjectClient } from "@gathertown/webhook-object-sdk"; import { loadConfig } from "./config"; import { fetchGitHubLogin } from "./github/client"; import { createPollEntries } from "./polls/registry"; @@ -15,8 +15,8 @@ import { runPollCycle } from "./runner"; const config = loadConfig(); const pollEntries = createPollEntries({ - openPrs: new Client(config.gather.openPrs), - reviewRequested: new Client(config.gather.reviewRequested), + openPrs: createWebhookObjectClient(config.gather.openPrs), + reviewRequested: createWebhookObjectClient(config.gather.reviewRequested), }); const login = await fetchGitHubLogin(config.github.token); diff --git a/packages/gh-prs-ready-need-review/src/ping.ts b/packages/gh-prs-ready-need-review/src/ping.ts index 63ba740..0c30e83 100644 --- a/packages/gh-prs-ready-need-review/src/ping.ts +++ b/packages/gh-prs-ready-need-review/src/ping.ts @@ -1,4 +1,4 @@ -import { Client } from "@webhook-objects/client/node"; +import { createWebhookObjectClient } from "@gathertown/webhook-object-sdk"; import { loadConfig } from "./config"; const config = loadConfig(); @@ -12,8 +12,8 @@ const targets = [ ]; for (const { name, url, secret } of targets) { - const client = new Client({ url, secret }); - const result = await client.requestMetadata(); + const client = createWebhookObjectClient({ url, secret }); + const result = await client.ping(); console.log(`\n${name}:`); console.log(JSON.stringify(result, null, 2)); } diff --git a/packages/gh-prs-ready-need-review/src/polls/registry.ts b/packages/gh-prs-ready-need-review/src/polls/registry.ts index bbf987f..8b15c00 100644 --- a/packages/gh-prs-ready-need-review/src/polls/registry.ts +++ b/packages/gh-prs-ready-need-review/src/polls/registry.ts @@ -1,4 +1,4 @@ -import type { Client } from "@webhook-objects/client/node"; +import type { WebhookObjectClient } from "@gathertown/webhook-object-sdk"; import { publishCounter } from "../publishers/gather-counter"; import { githubPrReviewCountPoll } from "./github-pr-review-count"; import { githubReviewRequestedPrCountPoll } from "./github-review-requested-pr-count"; @@ -6,13 +6,13 @@ import type { Poll, PollResult } from "./types"; export type PollEntry = { poll: Poll; - client: Client; - publish: (client: Client, result: PollResult) => Promise; + client: WebhookObjectClient; + publish: (client: WebhookObjectClient, result: PollResult) => Promise; }; export function createPollEntries(senders: { - openPrs: Client; - reviewRequested: Client; + openPrs: WebhookObjectClient; + reviewRequested: WebhookObjectClient; }): PollEntry[] { return [ { diff --git a/packages/gh-prs-ready-need-review/src/publishers/gather-counter.ts b/packages/gh-prs-ready-need-review/src/publishers/gather-counter.ts index c021c02..978253f 100644 --- a/packages/gh-prs-ready-need-review/src/publishers/gather-counter.ts +++ b/packages/gh-prs-ready-need-review/src/publishers/gather-counter.ts @@ -1,15 +1,11 @@ -import type { Client } from "@webhook-objects/client/node"; +import type { WebhookObjectClient } from "@gathertown/webhook-object-sdk"; import type { PollResult } from "../polls/types"; /** Publish a poll count to a Smart Object via counter.set. */ export async function publishCounter( - client: Client, + client: WebhookObjectClient, result: PollResult, ): Promise { const count = Math.max(0, Math.floor(result.value)); - await client.send({ - type: "counter.set", - timestamp: new Date().toISOString(), - data: { count }, - }); + await client.send("counter.set", { count }); } diff --git a/packages/gh-prs-ready-need-review/src/reset.ts b/packages/gh-prs-ready-need-review/src/reset.ts index 7e62912..e1ff2fc 100644 --- a/packages/gh-prs-ready-need-review/src/reset.ts +++ b/packages/gh-prs-ready-need-review/src/reset.ts @@ -1,4 +1,4 @@ -import { Client } from "@webhook-objects/client/node"; +import { createWebhookObjectClient } from "@gathertown/webhook-object-sdk"; import { loadConfig } from "./config"; /** Activity slot ids from the pre-counter inbox publisher. */ @@ -27,18 +27,13 @@ const targets = [ ]; for (const { name, url, secret } of targets) { - const client = new Client({ url, secret }); - const timestamp = new Date().toISOString(); + const client = createWebhookObjectClient({ url, secret }); for (const id of LEGACY_ACTIVITY_SLOT_IDS) { - await client.send({ type: "activity.remove", timestamp, data: { id } }); + await client.send("activity.remove", { id }); } - const result = await client.send({ - type: "counter.reset", - timestamp, - data: {}, - }); + const result = await client.send("counter.reset"); console.log( `${name}: cleared ${LEGACY_ACTIVITY_SLOT_IDS.length} legacy slots + counter (${JSON.stringify(result)})`, ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b236138..c5b6294 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -73,9 +73,9 @@ importers: packages/gh-prs-ready-need-review: dependencies: - '@webhook-objects/client': - specifier: workspace:* - version: link:../client + '@gathertown/webhook-object-sdk': + specifier: ^0.1.1 + version: 0.1.1 octokit: specifier: ^5.0.5 version: 5.0.5 @@ -342,6 +342,13 @@ packages: cpu: [x64] os: [win32] + '@gathertown/webhook-object-sdk@0.1.1': + resolution: {integrity: sha512-lKy/S8bA1Ux0+RAlQAEn6K5FCmQrdIF3QwE+31OBPUAsOqdFeUbb9xvrSuyus2+nqL/JOIQU3QFcFXuHxDLoiA==} + engines: {node: '>=18'} + + '@gathertown/webhook-object-types@0.1.1': + resolution: {integrity: sha512-jJBooZyBW/lRHwY7njRWjr2y1oaZEWBW719sZElV3ipv2a0EymR/AZZtW8jJrkVWP+OhuVSb7EUHVEE0dMb9mA==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -1233,6 +1240,13 @@ snapshots: '@esbuild/win32-x64@0.28.1': optional: true + '@gathertown/webhook-object-sdk@0.1.1': + dependencies: + '@gathertown/webhook-object-types': 0.1.1 + standardwebhooks: 1.0.0 + + '@gathertown/webhook-object-types@0.1.1': {} + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.5': {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index fec54ef..bf128ae 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,8 @@ minimumReleaseAge: 10080 minimumReleaseAgeExclude: - vite + - "@gathertown/webhook-object-sdk" + - "@gathertown/webhook-object-types" gitBranchLockfile: true enableGlobalVirtualStore: true mergeGitBranchLockfilesBranchPattern: