diff --git a/.gitignore b/.gitignore index c3e292c..d73643f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,5 @@ node_modules storybook-static .next worker -wrangler.toml .devdbs dist diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..d7cb9ec --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +16.13.2 \ No newline at end of file diff --git a/packages/api/.gitignore b/packages/api/.gitignore index 1521c8b..5510818 100644 --- a/packages/api/.gitignore +++ b/packages/api/.gitignore @@ -1 +1,2 @@ dist +.mf diff --git a/packages/api/.nvmrc b/packages/api/.nvmrc new file mode 100644 index 0000000..8e2afd3 --- /dev/null +++ b/packages/api/.nvmrc @@ -0,0 +1 @@ +17 \ No newline at end of file diff --git a/packages/api/bindings.d.ts b/packages/api/bindings.d.ts deleted file mode 100644 index 32180fa..0000000 --- a/packages/api/bindings.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -export {}; - -declare global { - const BOT_CLIENT_ID: string; - const BOT_CLIENT_SECRET: string; - const UI_PUBLIC_URI: string; - const API_PUBLIC_URI: string; - const ROOT_USERS: string; - const ALLOWED_CALLBACK_HOSTS: string; - - const KV_SESSIONS: KVNamespace; - const KV_GUILDS: KVNamespace; - const KV_GUILD_DATA: KVNamespace; -} diff --git a/packages/api/handlers/bot-join.ts b/packages/api/old-src/handlers/bot-join.ts similarity index 100% rename from packages/api/handlers/bot-join.ts rename to packages/api/old-src/handlers/bot-join.ts diff --git a/packages/api/handlers/clear-guild-cache.ts b/packages/api/old-src/handlers/clear-guild-cache.ts similarity index 100% rename from packages/api/handlers/clear-guild-cache.ts rename to packages/api/old-src/handlers/clear-guild-cache.ts diff --git a/packages/api/handlers/get-picker-data.ts b/packages/api/old-src/handlers/get-picker-data.ts similarity index 100% rename from packages/api/handlers/get-picker-data.ts rename to packages/api/old-src/handlers/get-picker-data.ts diff --git a/packages/api/handlers/get-session.ts b/packages/api/old-src/handlers/get-session.ts similarity index 100% rename from packages/api/handlers/get-session.ts rename to packages/api/old-src/handlers/get-session.ts diff --git a/packages/api/handlers/get-slug.ts b/packages/api/old-src/handlers/get-slug.ts similarity index 100% rename from packages/api/handlers/get-slug.ts rename to packages/api/old-src/handlers/get-slug.ts diff --git a/packages/api/handlers/interactions-pick-role.ts b/packages/api/old-src/handlers/interactions-pick-role.ts similarity index 100% rename from packages/api/handlers/interactions-pick-role.ts rename to packages/api/old-src/handlers/interactions-pick-role.ts diff --git a/packages/api/handlers/interactions-pickable-roles.ts b/packages/api/old-src/handlers/interactions-pickable-roles.ts similarity index 100% rename from packages/api/handlers/interactions-pickable-roles.ts rename to packages/api/old-src/handlers/interactions-pickable-roles.ts diff --git a/packages/api/handlers/login-bounce.ts b/packages/api/old-src/handlers/login-bounce.ts similarity index 100% rename from packages/api/handlers/login-bounce.ts rename to packages/api/old-src/handlers/login-bounce.ts diff --git a/packages/api/handlers/login-callback.ts b/packages/api/old-src/handlers/login-callback.ts similarity index 100% rename from packages/api/handlers/login-callback.ts rename to packages/api/old-src/handlers/login-callback.ts diff --git a/packages/api/handlers/revoke-session.ts b/packages/api/old-src/handlers/revoke-session.ts similarity index 100% rename from packages/api/handlers/revoke-session.ts rename to packages/api/old-src/handlers/revoke-session.ts diff --git a/packages/api/handlers/sync-from-legacy.ts b/packages/api/old-src/handlers/sync-from-legacy.ts similarity index 100% rename from packages/api/handlers/sync-from-legacy.ts rename to packages/api/old-src/handlers/sync-from-legacy.ts diff --git a/packages/api/handlers/update-guild.ts b/packages/api/old-src/handlers/update-guild.ts similarity index 100% rename from packages/api/handlers/update-guild.ts rename to packages/api/old-src/handlers/update-guild.ts diff --git a/packages/api/handlers/update-roles.ts b/packages/api/old-src/handlers/update-roles.ts similarity index 100% rename from packages/api/handlers/update-roles.ts rename to packages/api/old-src/handlers/update-roles.ts diff --git a/packages/api/index.ts b/packages/api/old-src/index.ts similarity index 98% rename from packages/api/index.ts rename to packages/api/old-src/index.ts index 693acf8..b7fd599 100644 --- a/packages/api/index.ts +++ b/packages/api/old-src/index.ts @@ -66,5 +66,5 @@ router.addFallback('root', () => { }); addEventListener('fetch', (event: FetchEvent) => { - event.respondWith(router.handle(event)); + router.handle(event)); }); diff --git a/packages/api/utils/access-control.ts b/packages/api/old-src/utils/access-control.ts similarity index 100% rename from packages/api/utils/access-control.ts rename to packages/api/old-src/utils/access-control.ts diff --git a/packages/api/utils/api-tools.ts b/packages/api/old-src/utils/api-tools.ts similarity index 100% rename from packages/api/utils/api-tools.ts rename to packages/api/old-src/utils/api-tools.ts diff --git a/packages/api/utils/audit-log.ts b/packages/api/old-src/utils/audit-log.ts similarity index 100% rename from packages/api/utils/audit-log.ts rename to packages/api/old-src/utils/audit-log.ts diff --git a/packages/api/utils/bounce.ts b/packages/api/old-src/utils/bounce.ts similarity index 100% rename from packages/api/utils/bounce.ts rename to packages/api/old-src/utils/bounce.ts diff --git a/packages/api/utils/config.ts b/packages/api/old-src/utils/config.ts similarity index 100% rename from packages/api/utils/config.ts rename to packages/api/old-src/utils/config.ts diff --git a/packages/api/utils/feature-flags.ts b/packages/api/old-src/utils/feature-flags.ts similarity index 100% rename from packages/api/utils/feature-flags.ts rename to packages/api/old-src/utils/feature-flags.ts diff --git a/packages/api/utils/guild.ts b/packages/api/old-src/utils/guild.ts similarity index 100% rename from packages/api/utils/guild.ts rename to packages/api/old-src/utils/guild.ts diff --git a/packages/api/utils/import-from-legacy.ts b/packages/api/old-src/utils/import-from-legacy.ts similarity index 100% rename from packages/api/utils/import-from-legacy.ts rename to packages/api/old-src/utils/import-from-legacy.ts diff --git a/packages/api/utils/kv.ts b/packages/api/old-src/utils/kv.ts similarity index 100% rename from packages/api/utils/kv.ts rename to packages/api/old-src/utils/kv.ts diff --git a/packages/api/utils/rate-limiting.ts b/packages/api/old-src/utils/rate-limiting.ts similarity index 100% rename from packages/api/utils/rate-limiting.ts rename to packages/api/old-src/utils/rate-limiting.ts diff --git a/packages/api/utils/responses.ts b/packages/api/old-src/utils/responses.ts similarity index 100% rename from packages/api/utils/responses.ts rename to packages/api/old-src/utils/responses.ts diff --git a/packages/api/webpack.config.js b/packages/api/old-src/webpack.config.js similarity index 100% rename from packages/api/webpack.config.js rename to packages/api/old-src/webpack.config.js diff --git a/packages/api/worker.config.js b/packages/api/old-src/worker.config.js similarity index 100% rename from packages/api/worker.config.js rename to packages/api/old-src/worker.config.js diff --git a/packages/api/package.json b/packages/api/package.json index 6316916..74c55bd 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -1,11 +1,13 @@ { "name": "@roleypoly/api", "version": "0.1.0", + "main": "./src/index.ts", "scripts": { - "build": "yarn workspace @roleypoly/worker-emulator build --basePath `pwd`", - "lint:types": "tsc --noEmit", - "start": "cfw-emulator" + "build": "esbuild --bundle --sourcemap --platform=node --format=esm --outdir=dist --out-extension:.js=.mjs ./src/index.ts", + "dev": "miniflare --watch --debug", + "lint:types": "tsc --noEmit" }, + "dependencies": {}, "devDependencies": { "@cloudflare/workers-types": "^2.2.2", "@roleypoly/misc-utils": "*", @@ -14,8 +16,13 @@ "@roleypoly/worker-utils": "*", "@types/deep-equal": "^1.0.1", "deep-equal": "^2.0.5", + "esbuild": "^0.14.13", + "exponential-backoff": "^3.1.0", + "itty-router": "^2.4.10", "ksuid": "^2.0.0", "lodash": "^4.17.21", - "ts-loader": "^8.3.0" + "miniflare": "^2.2.0", + "ts-loader": "^8.3.0", + "ulid-workers": "^1.1.0" } } diff --git a/packages/api/src/config.ts b/packages/api/src/config.ts new file mode 100644 index 0000000..03e88c0 --- /dev/null +++ b/packages/api/src/config.ts @@ -0,0 +1,64 @@ +import { WrappedKVNamespace } from '@roleypoly/api/src/kv'; + +export type Environment = { + BOT_CLIENT_ID: string; + BOT_CLIENT_SECRET: string; + BOT_TOKEN: string; + UI_PUBLIC_URI: string; + API_PUBLIC_URI: string; + ROOT_USERS: string; + ALLOWED_CALLBACK_HOSTS: string; + BOT_IMPORT_TOKEN: string; + INTERACTIONS_SHARED_KEY: string; + RP_SERVER_ID: string; + RP_HELPER_ROLE_IDS: string; + + KV_SESSIONS: KVNamespace; + KV_GUILDS: KVNamespace; + KV_GUILD_DATA: KVNamespace; +}; + +export type Config = { + botClientID: string; + botClientSecret: string; + botToken: string; + uiPublicURI: string; + apiPublicURI: string; + rootUsers: string[]; + allowedCallbackHosts: string[]; + importSharedKey: string; + interactionsSharedKey: string; + roleypolyServerID: string; + helperRoleIDs: string[]; + kv: { + sessions: WrappedKVNamespace; + guilds: WrappedKVNamespace; + guildData: WrappedKVNamespace; + }; + _raw: Environment; +}; + +const toList = (x: string): string[] => x.split(','); +const safeURI = (x: string) => x.replace(/\/$/, ''); + +export const parseEnvironment = (env: Environment): Config => { + return { + _raw: env, + botClientID: env.BOT_CLIENT_ID, + botClientSecret: env.BOT_CLIENT_SECRET, + botToken: env.BOT_TOKEN, + uiPublicURI: safeURI(env.UI_PUBLIC_URI), + apiPublicURI: safeURI(env.API_PUBLIC_URI), + rootUsers: toList(env.ROOT_USERS), + allowedCallbackHosts: toList(env.ALLOWED_CALLBACK_HOSTS), + importSharedKey: env.BOT_IMPORT_TOKEN, + interactionsSharedKey: env.INTERACTIONS_SHARED_KEY, + roleypolyServerID: env.RP_SERVER_ID, + helperRoleIDs: toList(env.RP_HELPER_ROLE_IDS), + kv: { + sessions: new WrappedKVNamespace(env.KV_SESSIONS), + guilds: new WrappedKVNamespace(env.KV_GUILDS), + guildData: new WrappedKVNamespace(env.KV_GUILD_DATA), + }, + }; +}; diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts new file mode 100644 index 0000000..fa117c2 --- /dev/null +++ b/packages/api/src/index.ts @@ -0,0 +1,32 @@ +// @ts-ignore +import { authBounce } from '@roleypoly/api/src/routes/auth/bounce'; +import { json, notFound } from '@roleypoly/api/src/utils/response'; +import { Router } from 'itty-router'; +import { Config, Environment, parseEnvironment } from './config'; + +const router = Router(); + +router.get('/auth/bounce', authBounce); + +router.get('/', (request: Request, config: Config) => + json({ + __warning: '🦊', + this: 'is', + a: 'fox-based', + web: 'application', + please: 'be', + mindful: 'of', + your: 'surroundings', + warning__: '🦊', + meta: config.uiPublicURI, + }) +); + +router.get('*', () => notFound()); + +export default { + async fetch(request: Request, env: Environment, ctx: FetchEvent) { + const config = parseEnvironment(env); + return router.handle(request, config, ctx); + }, +}; diff --git a/packages/api/src/kv.ts b/packages/api/src/kv.ts new file mode 100644 index 0000000..df4e425 --- /dev/null +++ b/packages/api/src/kv.ts @@ -0,0 +1,23 @@ +export class WrappedKVNamespace { + constructor(private kvNamespace: KVNamespace) {} + + async get(key: string): Promise { + const data = await this.kvNamespace.get(key, 'text'); + if (!data) { + return null; + } + + return JSON.parse(data) as T; + } + + async put(key: string, value: T, ttlSeconds?: number) { + await this.kvNamespace.put(key, JSON.stringify(value), { + expirationTtl: ttlSeconds, + }); + } + + getRaw = this.kvNamespace.get; + list = this.kvNamespace.list; + getWithMetadata = this.kvNamespace.getWithMetadata; + delete = this.kvNamespace.delete; +} diff --git a/packages/api/src/routes/auth/bounce.ts b/packages/api/src/routes/auth/bounce.ts new file mode 100644 index 0000000..9257874 --- /dev/null +++ b/packages/api/src/routes/auth/bounce.ts @@ -0,0 +1,45 @@ +import { Config } from '@roleypoly/api/src/config'; +import { setupStateSession } from '@roleypoly/api/src/sessions/state'; +import { getQuery } from '@roleypoly/api/src/utils/request'; +import { seeOther } from '@roleypoly/api/src/utils/response'; +import { StateSession } from '@roleypoly/types'; + +type URLParams = { + clientID: string; + redirectURI: string; + state: string; +}; + +export const buildURL = (params: URLParams) => + `https://discord.com/api/oauth2/authorize?client_id=${ + params.clientID + }&response_type=code&scope=identify%20guilds&prompt=none&redirect_uri=${encodeURIComponent( + params.redirectURI + )}&state=${params.state}`; + +export const isAllowedCallbackHost = (config: Config, host: string): boolean => { + return ( + host === config.apiPublicURI || + config.allowedCallbackHosts.includes(host) || + config.allowedCallbackHosts + .filter((callbackHost) => callbackHost.includes('*')) + .find((wildcard) => new RegExp(wildcard.replace('*', '[a-z0-9-]+')).test(host)) !== + null + ); +}; + +export const authBounce = async (request: Request, config: Config) => { + const stateSessionData: StateSession = {}; + + const { cbh: callbackHost } = getQuery(request); + if (callbackHost && isAllowedCallbackHost(config, callbackHost)) { + stateSessionData.callbackHost = callbackHost; + } + + const state = await setupStateSession(config.kv.sessions, stateSessionData); + + const redirectURI = `${config.apiPublicURI}/login-callback`; + const clientID = config.botClientID; + + return seeOther(buildURL({ state, redirectURI, clientID })); +}; diff --git a/packages/api/src/routes/auth/callback.ts b/packages/api/src/routes/auth/callback.ts new file mode 100644 index 0000000..c5eae85 --- /dev/null +++ b/packages/api/src/routes/auth/callback.ts @@ -0,0 +1,75 @@ +import { Config } from '@roleypoly/api/src/config'; +import { isAllowedCallbackHost } from '@roleypoly/api/src/routes/auth/bounce'; +import { getStateSession } from '@roleypoly/api/src/sessions/state'; +import { getQuery, seeOther } from '@roleypoly/api/src/utils'; +import { AuthType, discordAPIBase, discordFetch } from '@roleypoly/api/src/utils/discord'; +import { formData } from '@roleypoly/api/src/utils/request'; +import { AuthTokenResponse, StateSession } from '@roleypoly/types'; +import { decodeTime, monotonicFactory } from 'ulid-workers'; +const ulid = monotonicFactory(); + +const authFailure = (uiPublicURI: string, extra?: string) => + seeOther( + uiPublicURI + + `/machinery/error?error_code=authFailure${extra ? `&extra=${extra}` : ''}` + ); + +export const authCallback = async (request: Request, config: Config) => { + let bounceBaseUrl = config.uiPublicURI; + + const { state: stateValue, code } = getQuery(request); + + if (stateValue === null) { + return authFailure('state missing'); + } + + try { + const stateTime = decodeTime(stateValue); + const stateExpiry = stateTime + 1000 * 60 * 5; + const currentTime = Date.now(); + + if (currentTime > stateExpiry) { + return authFailure('state expired'); + } + + const stateSession = await getStateSession( + config.kv.sessions, + stateValue + ); + if ( + stateSession?.callbackHost && + isAllowedCallbackHost(config, stateSession.callbackHost) + ) { + bounceBaseUrl = stateSession.callbackHost; + } + } catch (e) { + return authFailure('state invalid'); + } + + if (!code) { + return authFailure('code missing'); + } + + const response = await discordFetch( + `${discordAPIBase}/oauth2/token`, + '', + AuthType.None, + { + method: 'POST', + headers: { + 'content-type': 'application/x-www-form-urlencoded', + }, + body: formData({ + client_id: config.botClientID, + client_secret: config.botClientSecret, + grant_type: 'authorization_code', + code, + redirect_uri: config.apiPublicURI + '/auth/callback', + }), + } + ); + + if (!response) { + return authFailure('code auth failure'); + } +}; diff --git a/packages/api/src/sessions/create.ts b/packages/api/src/sessions/create.ts new file mode 100644 index 0000000..c8f6b43 --- /dev/null +++ b/packages/api/src/sessions/create.ts @@ -0,0 +1,8 @@ +import { Config } from "@roleypoly/api/src/config"; +import { AuthTokenResponse } from "@roleypoly/types"; + +export const createSession = async ( + config: Config, + sessionId: string, + tokens: AuthTokenResponse +) \ No newline at end of file diff --git a/packages/api/src/sessions/flags.ts b/packages/api/src/sessions/flags.ts new file mode 100644 index 0000000..892bd28 --- /dev/null +++ b/packages/api/src/sessions/flags.ts @@ -0,0 +1,5 @@ +import { SessionData, SessionFlags } from '@roleypoly/types'; + +export const getSessionFlags = async (session: SessionData): Promise => { + return SessionFlags.None; +}; diff --git a/packages/api/src/sessions/state.ts b/packages/api/src/sessions/state.ts new file mode 100644 index 0000000..ae7854c --- /dev/null +++ b/packages/api/src/sessions/state.ts @@ -0,0 +1,23 @@ +import { WrappedKVNamespace } from '@roleypoly/api/src/kv'; +import { monotonicFactory } from 'ulid-workers'; +const ulid = monotonicFactory(); + +export const setupStateSession = async ( + Sessions: WrappedKVNamespace, + data: T +): Promise => { + const stateID = ulid(); + + await Sessions.put(`state_${stateID}`, { data }, 60 * 5); + + return stateID; +}; + +export const getStateSession = async ( + Sessions: WrappedKVNamespace, + stateID: string +): Promise => { + const stateSession = await Sessions.get<{ data: T }>(`state_${stateID}`); + + return stateSession?.data; +}; diff --git a/packages/api/src/utils/discord.ts b/packages/api/src/utils/discord.ts new file mode 100644 index 0000000..a612d46 --- /dev/null +++ b/packages/api/src/utils/discord.ts @@ -0,0 +1,123 @@ +import { Config } from '@roleypoly/api/src/config'; +import { + evaluatePermission, + permissions as Permissions, +} from '@roleypoly/misc-utils/hasPermission'; +import { + AuthTokenResponse, + DiscordUser, + GuildSlug, + UserGuildPermissions, +} from '@roleypoly/types'; + +export const userAgent = + 'DiscordBot (https://github.com/roleypoly/roleypoly, git-main) (+https://roleypoly.com)'; + +export const discordAPIBase = 'https://discordapp.com/api/v9'; + +export enum AuthType { + Bearer = 'Bearer', + Bot = 'Bot', + None = 'None', +} + +export const discordFetch = async ( + url: string, + auth: string, + authType: AuthType = AuthType.Bearer, + init?: RequestInit +): Promise => { + const response = await fetch(discordAPIBase + url, { + ...(init || {}), + headers: { + ...(init?.headers || {}), + ...(authType !== AuthType.None + ? { + authorization: `${AuthType[authType]} ${auth}`, + } + : {}), + 'user-agent': userAgent, + }, + }); + + if (response.status >= 400) { + console.error('discordFetch failed', { + url, + authType, + payload: await response.text(), + }); + } + + if (response.ok) { + return (await response.json()) as T; + } else { + return null; + } +}; + +export const getTokenUser = async ( + accessToken: AuthTokenResponse['access_token'] +): Promise => { + const user = await discordFetch( + '/users/@me', + accessToken, + AuthType.Bearer + ); + + if (!user) { + return null; + } + + const { id, username, discriminator, bot, avatar } = user; + + return { id, username, discriminator, bot, avatar }; +}; + +type UserGuildsPayload = { + id: string; + name: string; + icon: string; + owner: boolean; + permissions: number; + features: string[]; +}[]; + +export const getTokenGuilds = async (accessToken: string, config: Config) => { + const guilds = await discordFetch( + '/users/@me/guilds', + accessToken, + AuthType.Bearer + ); + + if (!guilds) { + return []; + } + + const guildSlugs = guilds.map((guild) => ({ + id: guild.id, + name: guild.name, + icon: guild.icon, + permissionLevel: parsePermissions( + BigInt(guild.permissions), + guild.owner, + config.roleypolyServerID + ), + })); + + return guildSlugs; +}; + +export const parsePermissions = ( + permissions: bigint, + owner: boolean = false +): UserGuildPermissions => { + if (owner || evaluatePermission(permissions, Permissions.ADMINISTRATOR)) { + return UserGuildPermissions.Admin; + } + + if (evaluatePermission(permissions, Permissions.MANAGE_ROLES)) { + return UserGuildPermissions.Manager; + } + + return UserGuildPermissions.User; +}; diff --git a/packages/api/src/utils/request.ts b/packages/api/src/utils/request.ts new file mode 100644 index 0000000..ed643bf --- /dev/null +++ b/packages/api/src/utils/request.ts @@ -0,0 +1,15 @@ +export const getQuery = (request: Request): { [x: string]: string } => { + const output: { [x: string]: string } = {}; + + for (let [key, value] of new URL(request.url).searchParams.entries()) { + output[key] = value; + } + + return output; +}; + +export const formData = (obj: Record): string => { + return Object.keys(obj) + .map((key) => `${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`) + .join('&'); +}; diff --git a/packages/api/src/utils/response.ts b/packages/api/src/utils/response.ts new file mode 100644 index 0000000..74d8914 --- /dev/null +++ b/packages/api/src/utils/response.ts @@ -0,0 +1,24 @@ +export const json = (obj: any, init?: ResponseInit): Response => { + const body = JSON.stringify(obj); + return new Response(body, { + ...init, + headers: { + ...init?.headers, + 'content-type': 'application/json; charset=utf-8', + }, + }); +}; + +export const notFound = () => json({ error: 'not found' }, { status: 404 }); + +export const seeOther = (url: string) => + new Response( + `If you are not redirected soon, click here.`, + { + status: 303, + headers: { + location: url, + 'content-type': 'text/html; charset=utf-8', + }, + } + ); diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json index 1b989a0..d5dd82f 100644 --- a/packages/api/tsconfig.json +++ b/packages/api/tsconfig.json @@ -3,7 +3,9 @@ "outDir": "./dist", "lib": ["esnext", "webworker", "ES2020.BigInt", "ES2020.Promise"], "types": ["@cloudflare/workers-types"], - "target": "ES2019" + "target": "ES2019", + "esModuleInterop": true, + "module": "commonjs" }, "include": [ "./*.ts", diff --git a/packages/api/wrangler.toml b/packages/api/wrangler.toml new file mode 100644 index 0000000..8a8751e --- /dev/null +++ b/packages/api/wrangler.toml @@ -0,0 +1,29 @@ +# THIS DOES NOT WORK WITH WRANGLER BY DEFAULT. +# BE EXTREMELY AWARE OF THIS CAVEAT. + +name = "api" +type = "javascript" +account_id = "" +workers_dev = true +route = "" +zone_id = "" + +kv_namespaces = [ + { binding = "KV_SESSIONS", id = "", preview_id = "" }, + { binding = "KV_GUILDS", id = "", preview_id = "" }, + { binding = "KV_GUILD_DATA", id = "", preview_id = "" }, +] + +[build] +command = "yarn build" +[build.upload] +format = "modules" +dir = "dist" +main = "index.mjs" + +[miniflare] +host = "0.0.0.0" +port = 6609 +watch = true +env_path = "../../.env" +kv_persist = true diff --git a/packages/design-system/atoms/branding/DynamicBranding.tsx b/packages/design-system/atoms/branding/DynamicBranding.tsx index 834f928..73b8a3c 100644 --- a/packages/design-system/atoms/branding/DynamicBranding.tsx +++ b/packages/design-system/atoms/branding/DynamicBranding.tsx @@ -354,8 +354,8 @@ export const LunarNewYear: Variant = { // Feb 1, 2022 // Jan 22, 2023 activeIf: (currentDate?: Date) => - matchDay(new Date('2022-Jan-30'), new Date('2022-Feb-3'), currentDate, true) || - matchDay(new Date('2023-Jan-20'), new Date('2023-Jan-24'), currentDate, true), + matchDay(new Date('2022-Jan-27'), new Date('2022-Feb-10'), currentDate, true) || + matchDay(new Date('2023-Jan-17'), new Date('2023-Jan-29'), currentDate, true), sharedProps: { circleFill: palette.red200, circleOuterFill: palette.gold400, diff --git a/packages/types/Guild.ts b/packages/types/Guild.ts index fb2a6e4..bb970fa 100644 --- a/packages/types/Guild.ts +++ b/packages/types/Guild.ts @@ -50,6 +50,7 @@ export enum UserGuildPermissions { User, Manager = 1 << 1, Admin = 1 << 2, + RoleypolySupport = 1 << 3, } export type GuildSlug = { diff --git a/packages/types/Session.ts b/packages/types/Session.ts index 7a32f2c..6754716 100644 --- a/packages/types/Session.ts +++ b/packages/types/Session.ts @@ -9,12 +9,19 @@ export type AuthTokenResponse = { scope: string; }; +export enum SessionFlags { + None = 0, + IsRoot = 1 << 0, + IsSupport = 1 << 1, +} + export type SessionData = { /** sessionID is a KSUID */ sessionID: string; tokens: AuthTokenResponse; user: DiscordUser; guilds: GuildSlug[]; + flags: SessionFlags; }; export type StateSession = { diff --git a/packages/web/package.json b/packages/web/package.json index 632888f..0de0b63 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -32,6 +32,7 @@ "@types/react-helmet": "^6.1.2", "babel-loader": "8.1.0", "cross-env": "7.0.3", + "exponential-backoff": "^3.1.0", "ts-loader": "^8.3.0", "webpack": "4.44.2" }, diff --git a/packages/web/src/contexts/session/SessionContext.tsx b/packages/web/src/contexts/session/SessionContext.tsx index 94ed941..97fe55d 100644 --- a/packages/web/src/contexts/session/SessionContext.tsx +++ b/packages/web/src/contexts/session/SessionContext.tsx @@ -160,6 +160,7 @@ export const SessionContextProvider = (props: { children: React.ReactNode }) => setLock(false); } catch (e) { console.error('syncSession failed', e); + deleteSessionKey(); setLock(false); } }; @@ -179,7 +180,7 @@ const saveSessionKey = (key: string) => localStorage.setItem('rp_session_key', k const deleteSessionKey = () => localStorage.removeItem('rp_session_key'); const getSessionKey = () => localStorage.getItem('rp_session_key'); -type ServerSession = Omit; +type ServerSession = Omit, 'flags'>; const fetchSession = async ( authedFetch: SessionContextT['authedFetch'] ): Promise => { diff --git a/yarn.lock b/yarn.lock index ed0da4a..8ba085a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1813,6 +1813,11 @@ dependencies: "@hapi/hoek" "^8.3.0" +"@iarna/toml@^2.2.5": + version "2.2.5" + resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" + integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== + "@icons/material@^0.4.1": version "0.4.1" resolved "https://registry.yarnpkg.com/@icons/material/-/material-0.4.1.tgz#20ba0dff8d59a2b8749c7ad765faad52ae45b868" @@ -2137,6 +2142,144 @@ resolved "https://registry.yarnpkg.com/@mdx-js/util/-/util-1.6.22.tgz#219dfd89ae5b97a8801f015323ffa4b62f45718b" integrity sha512-H1rQc1ZOHANWBvPcW+JpGwr+juXSxM8Q8YCkm3GhZd8REu1fHR3z99CErO1p9pkcfcxZnMdIZdIsXkOHY0NilA== +"@miniflare/cache@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@miniflare/cache/-/cache-2.2.0.tgz#407f27fb007bd10f805bd1232e0daecaf18898df" + integrity sha512-N6b4V6fp4V0ljUHWs6+wVAvT94/SUCp6+pn3wnizjnYQhDE9ns8KJHu6zOTi+OkZUezjdUDJK0dqQXIKC/23XA== + dependencies: + "@miniflare/core" "2.2.0" + "@miniflare/shared" "2.2.0" + http-cache-semantics "^4.1.0" + undici "4.12.1" + +"@miniflare/cli-parser@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@miniflare/cli-parser/-/cli-parser-2.2.0.tgz#eea6b4f5e324266fb955f1653914e98801b2bc5b" + integrity sha512-AeP3hZOQ7/dBosCPh/iL7cwgdUi5EfHA0unGsFULaxuCwBb3RkQd5WAq4gzzgHywsFRIQ0+5/j9Mk1wHG2AkJA== + dependencies: + "@miniflare/shared" "2.2.0" + kleur "^4.1.4" + +"@miniflare/core@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@miniflare/core/-/core-2.2.0.tgz#ec45606ca2864612a455b91df74d57c2a279f866" + integrity sha512-CJwYhyXQ7si0ALZ4wB/vFAo0sHcyaMR7A8oQb6HaZ8XKhaKkzA8BmPutMvYVikiW+oRJP3YRcJGuVKibL3TSgA== + dependencies: + "@iarna/toml" "^2.2.5" + "@miniflare/shared" "2.2.0" + "@miniflare/watcher" "2.2.0" + busboy "^0.3.1" + dotenv "^10.0.0" + kleur "^4.1.4" + set-cookie-parser "^2.4.8" + undici "4.12.1" + +"@miniflare/durable-objects@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@miniflare/durable-objects/-/durable-objects-2.2.0.tgz#1c4633d0433bab3a8192761ea272b18e6affb596" + integrity sha512-iIRritqTBiTvX5dHPYswc3a6qzi3LPilbFE2HGjHMbqHi+/C4yX9SMfxCU4/6NcXc9xWjMGtJYBk+hgOW+Mvow== + dependencies: + "@miniflare/core" "2.2.0" + "@miniflare/shared" "2.2.0" + "@miniflare/storage-memory" "2.2.0" + undici "4.12.1" + +"@miniflare/html-rewriter@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@miniflare/html-rewriter/-/html-rewriter-2.2.0.tgz#83a3c0af3b4dee4824c8c422579f0a66d3e28522" + integrity sha512-20qic3swy5bXg63Q96oHBKxHWp5d5GSn2HCZYXlHtODHeVbI/1lYQoD12b9EmE2i+jUZfCJjlinmjonXGyyL+A== + dependencies: + "@miniflare/core" "2.2.0" + "@miniflare/shared" "2.2.0" + html-rewriter-wasm "^0.4.0" + undici "4.12.1" + +"@miniflare/http-server@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@miniflare/http-server/-/http-server-2.2.0.tgz#a6d899532a4c6ae0cd36e5ceac0da7635be44558" + integrity sha512-nx7xEXKYvPyUpu2HFigD+UEgclipMQzGdTKtm4t6JzsOGYys7Lj7kGjryYaQVtbbJvPKIxqSaViFQDk/QSdosg== + dependencies: + "@miniflare/core" "2.2.0" + "@miniflare/shared" "2.2.0" + "@miniflare/web-sockets" "2.2.0" + kleur "^4.1.4" + selfsigned "^2.0.0" + undici "4.12.1" + ws "^8.2.2" + youch "^2.2.2" + +"@miniflare/kv@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@miniflare/kv/-/kv-2.2.0.tgz#e9e03d3ba7a85a153a288d46b06f54cab4278f02" + integrity sha512-x2iUskcNEX74AfDhcZDKMTchNn3hUQyxDrzuDSHOv0vWRxDctmqYh5VxZVUGiaFvrbNN2+XgkgxXNY6TxrsfNQ== + dependencies: + "@miniflare/shared" "2.2.0" + +"@miniflare/runner-vm@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@miniflare/runner-vm/-/runner-vm-2.2.0.tgz#897042f6e08f2112ae054d9a9f1b07e34c87d721" + integrity sha512-x58BxIsAB6LcYgqTNVfxTsKFCu4G3Xiuj1k+3ZQ4b8v8TZjuZ3iOJcsrYU0PlbW5JpnRA8IH/mkiFRb/MrLtaQ== + dependencies: + "@miniflare/shared" "2.2.0" + +"@miniflare/scheduler@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@miniflare/scheduler/-/scheduler-2.2.0.tgz#3cc36a9cdfd03374fb880707cad4c9e7ca681622" + integrity sha512-OqfeSfxEfG3XA6D33C3dUxTUIy425n+BbZe15D2SdfvqUBeBobzOAAp0FbCQiZ5USsDY/zSIu98iSclqPxAn5w== + dependencies: + "@miniflare/core" "2.2.0" + "@miniflare/shared" "2.2.0" + cron-schedule "^3.0.4" + +"@miniflare/shared@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@miniflare/shared/-/shared-2.2.0.tgz#a4c79b0919a54c9c3da64c65f05ae813e56988ce" + integrity sha512-ikz3nkBexVQ16Ioi6naAzK6Spj5O7lUcqIihMLCQKTAAdC252EL0QrBRxHV9MlvkypVNj6MqkoL4E6HcyuCo6g== + dependencies: + ignore "^5.1.8" + kleur "^4.1.4" + +"@miniflare/sites@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@miniflare/sites/-/sites-2.2.0.tgz#ac984b26c5014846b6a8914c7e6bcdc390ab5a21" + integrity sha512-UpD5xGqEnwHZ0slvAp+8N60LX1WezXTZVLI599KGIXuthEVkRLgk8PwujabmHisicDoyC3nxITeDExq0DY9G/w== + dependencies: + "@miniflare/kv" "2.2.0" + "@miniflare/shared" "2.2.0" + "@miniflare/storage-file" "2.2.0" + +"@miniflare/storage-file@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@miniflare/storage-file/-/storage-file-2.2.0.tgz#22b6bca4896febe85223787877bd85117e223e00" + integrity sha512-DzBbUmPadDw+nAG31Yn60d1Jg6ksHmXasfnd1g7Whe/D0leFRAsvcEJyVaojzlnuxTabtZAsLMoUjsofvnThzA== + dependencies: + "@miniflare/shared" "2.2.0" + "@miniflare/storage-memory" "2.2.0" + +"@miniflare/storage-memory@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@miniflare/storage-memory/-/storage-memory-2.2.0.tgz#b89bfdd7a9368138e64e5e1018543da221937308" + integrity sha512-1Ze2ZGwRFzH+6lLu6hZMTwGYTNioIT07QXvz3ZS/ilpg++cWUtqy2yHnajdJgcnNDNNQ4iVGladC2GNezrLbHg== + dependencies: + "@miniflare/shared" "2.2.0" + +"@miniflare/watcher@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@miniflare/watcher/-/watcher-2.2.0.tgz#adfa5d93aa0df86ed8b83e44f71ebb1cbce4bb13" + integrity sha512-Myaztj1QaL1IaB04uBLKY8hE1kFtvB65cnKdJPOgjFxIgFzNyqybjlBjxx01Q6vymboFTPrl1Q5+g7fsW3lmfQ== + dependencies: + "@miniflare/shared" "2.2.0" + +"@miniflare/web-sockets@2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@miniflare/web-sockets/-/web-sockets-2.2.0.tgz#77c3c3a5c86dcbeee8b5ad8a31904a80dc737531" + integrity sha512-/GHYuTq6m28udequw4E8IpcFmcEheAcVQ3oZyAvY0ub0by8v4WzYuougiizqYylyQZ0KXyLg8SmPeBNChe82Zw== + dependencies: + "@miniflare/core" "2.2.0" + "@miniflare/shared" "2.2.0" + undici "4.12.1" + ws "^8.2.2" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -3747,6 +3890,11 @@ resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== +"@types/stack-trace@0.0.29": + version "0.0.29" + resolved "https://registry.yarnpkg.com/@types/stack-trace/-/stack-trace-0.0.29.tgz#eb7a7c60098edb35630ed900742a5ecb20cfcb4d" + integrity sha512-TgfOX+mGY/NyNxJLIbDWrO9DjGoVSW9+aB8H2yy1fy32jsvxijhmyJI9fDFgvz3YP4lvJaq9DzdR/M1bOgVc9g== + "@types/stack-utils@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" @@ -5353,6 +5501,13 @@ builtin-status-codes@^3.0.0: resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= +busboy@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.1.tgz#170899274c5bf38aae27d5c62b71268cd585fd1b" + integrity sha512-y7tTxhGKXcyBxRKAni+awqx8uqaJKrSFSNFSeRG5CsWNdmy2BIK+6VGWEW7TZnIO/533mtMEA4rOevQV815YJw== + dependencies: + dicer "0.3.0" + bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -6098,6 +6253,11 @@ cookie@0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== +cookie@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" + integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== + copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -6256,6 +6416,11 @@ create-react-context@0.3.0: gud "^1.0.0" warning "^4.0.3" +cron-schedule@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/cron-schedule/-/cron-schedule-3.0.4.tgz#9d3c3b6ed9069a77856b287f70ab47da271c14e3" + integrity sha512-wEspID2dNPfWyh7t2ZvE4Izunzk20QacZg8oZcqfTrN1j5kImj0CEYT3ZGZo+KqGQUgRc9tKlEJmY4uFVt4ccA== + cross-env@7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" @@ -6891,6 +7056,13 @@ detect-port@^1.3.0: address "^1.0.1" debug "^2.6.0" +dicer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.0.tgz#eacd98b3bfbf92e8ab5c2fdb71aaac44bb06b872" + integrity sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA== + dependencies: + streamsearch "0.1.2" + diff-sequences@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.6.2.tgz#48ba99157de1923412eed41db6b6d4aa9ca7c0b1" @@ -7529,6 +7701,120 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3: d "^1.0.1" ext "^1.1.2" +esbuild-android-arm64@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.13.tgz#a5c8cb275dc6dcaf2390abb639791ffc4aa6ead1" + integrity sha512-rhtwl+KJ3BzzXkK09N3/YbEF1i5WhriysJEStoeWNBzchx9hlmzyWmDGQQhu56HF78ua3JrVPyLOsdLGvtMvxQ== + +esbuild-darwin-64@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.13.tgz#51ad2fbf8c9d73c1d40e5f9ab6122ffa95f5b494" + integrity sha512-Fl47xIt5RMu50WIgMU93kwmUUJb+BPuL8R895n/aBNQqavS+KUMpLPoqKGABBV4myfx/fnAD/97X8Gt1C1YW6w== + +esbuild-darwin-arm64@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.13.tgz#7ff177193cffbba518a59dfa4666a2fbb3345330" + integrity sha512-UttqKRFXsWvuivcyAbFmo54vdkC9Me1ZYQNuoz/uBYDbkb2MgqKYG2+xoVKPBhLvhT0CKM5QGKD81flMH5BE6A== + +esbuild-freebsd-64@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.13.tgz#648357ff0ca399ce65f7e7ebfff701cf105c70f3" + integrity sha512-dlIhPFSp29Yq2TPh7Cm3/4M0uKjlfvOylHVNCRvRNiOvDbBol6/NZ3kLisczms+Yra0rxVapBPN1oMbSMuts9g== + +esbuild-freebsd-arm64@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.13.tgz#7cb0016dd64fd116624c56dcfe58b27fac8ceaee" + integrity sha512-bNOHLu7Oq6RwaAMnwPbJ40DVGPl9GlAOnfH/dFZ792f8hFEbopkbtVzo1SU1jjfY3TGLWOgqHNWxPxx1N7Au+g== + +esbuild-linux-32@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.13.tgz#50b197f6831e824660ab80d011b656505a0aae32" + integrity sha512-WzXyBx6zx16adGi7wPBvH2lRCBzYMcqnBRrJ8ciLIqYyruGvprZocX1nFWfiexjLcFxIElWnMNPX6LG7ULqyXA== + +esbuild-linux-64@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.13.tgz#d2ee4d780e3cadd129ab8988c7c75ca4bd636f3a" + integrity sha512-P6OFAfcoUvE7g9h/0UKm3qagvTovwqpCF1wbFLWe/BcCY8BS1bR/+SxUjCeKX2BcpIsg4/43ezHDE/ntg/iOpw== + +esbuild-linux-arm64@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.13.tgz#9151963c55cd42968c97f2fd354cd6ff9b3cb476" + integrity sha512-k/uIvmkm4mc7vyMvJVwILgGxi2F+FuvLdmESIIWoHrnxEfEekC5AWpI/R6GQ2OMfp8snebSQLs8KL05QPnt1zA== + +esbuild-linux-arm@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.13.tgz#80e94cd8920607dc0addb97bac7ff5988d0fc28f" + integrity sha512-4jmm0UySCg3Wi6FEBS7jpiPb1IyckI5um5kzYRwulHxPzkiokd6cgpcsTakR4/Y84UEicS8LnFAghHhXHZhbFg== + +esbuild-linux-mips64le@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.13.tgz#8e7eeef567b0895f2ecc4d66b1f1e5a542f1ce5b" + integrity sha512-vwYtgjQ1TRlUGL88km9wH9TjXsdZyZ/Xht1ASptg5XGRlqGquVjLGH11PfLLunoMdkQ0YTXR68b4l5gRfjVbyg== + +esbuild-linux-ppc64le@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.13.tgz#ffbe9915a1d8080f9810a9c5938f453003c6de98" + integrity sha512-0KqDSIkZaYugtcdpFCd3eQ38Fg6TzhxmOpkhDIKNTwD/W2RoXeiS+Z4y5yQ3oysb/ySDOxWkwNqTdXS4sz2LdQ== + +esbuild-linux-s390x@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.13.tgz#e27274d12f198580892432115fc0de432431d33d" + integrity sha512-bG20i7d0CN97fwPN9LaLe64E2IrI0fPZWEcoiff9hzzsvo/fQCx0YjMbPC2T3gqQ48QZRltdU9hQilTjHk3geQ== + +esbuild-netbsd-64@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.13.tgz#78199fa57d5794e6ac6c33993dd0588fba22b353" + integrity sha512-jz96PQb0ltqyqLggPpcRbWxzLvWHvrZBHZQyjcOzKRDqg1fR/R1y10b1Cuv84xoIbdAf+ceNUJkMN21FfR9G2g== + +esbuild-openbsd-64@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.13.tgz#5394336b602e1db15583bbef7d827a2b9648e373" + integrity sha512-bp6zSo3kDCXKPM5MmVUg6DEpt+yXDx37iDGzNTn3Kf9xh6d0cdITxUC4Bx6S3Di79GVYubWs+wNjSRVFIJpryw== + +esbuild-sunos-64@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.13.tgz#4afcddcece51f943149fa07274bd58f64e0913a8" + integrity sha512-08Fne1T9QHYxUnu55sV9V4i/yECADOaI1zMGET2YUa8SRkib10i80hc89U7U/G02DxpN/KUJMWEGq2wKTn0QFQ== + +esbuild-windows-32@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.13.tgz#cb06267f270bbf7ea6631cc78b8f6e3647975c44" + integrity sha512-MW3BMIi9+fzTyDdljH0ftfT/qlD3t+aVzle1O+zZ2MgHRMQD20JwWgyqoJXhe6uDVyunrAUbcjH3qTIEZN3isg== + +esbuild-windows-64@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.13.tgz#3bbfba58e0185121280bd47ccd073b6c1849fd27" + integrity sha512-d7+0N+EOgBKdi/nMxlQ8QA5xHBlpcLtSrYnHsA+Xp4yZk28dYfRw1+embsHf5uN5/1iPvrJwPrcpgDH1xyy4JA== + +esbuild-windows-arm64@0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.13.tgz#9203d40d915d809c50bc91af5a8373e5f5abfc4c" + integrity sha512-oX5hmgXk9yNKbb5AxThzRQm/E9kiHyDll7JJeyeT1fuGENTifv33f0INCpjBQ+Ty5ChKc84++ZQTEBwLCA12Kw== + +esbuild@^0.14.13: + version "0.14.13" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.13.tgz#ce3fc7c45a6b8a40caad43daa39cdb9c1e84930a" + integrity sha512-FIxvAdj3i2oHA6ex+E67bG7zlSTO+slt8kU2ogHDgGtrQLy2HNChv3PYjiFTYkt8hZbEAniZCXVeHn+FrHt7dA== + optionalDependencies: + esbuild-android-arm64 "0.14.13" + esbuild-darwin-64 "0.14.13" + esbuild-darwin-arm64 "0.14.13" + esbuild-freebsd-64 "0.14.13" + esbuild-freebsd-arm64 "0.14.13" + esbuild-linux-32 "0.14.13" + esbuild-linux-64 "0.14.13" + esbuild-linux-arm "0.14.13" + esbuild-linux-arm64 "0.14.13" + esbuild-linux-mips64le "0.14.13" + esbuild-linux-ppc64le "0.14.13" + esbuild-linux-s390x "0.14.13" + esbuild-netbsd-64 "0.14.13" + esbuild-openbsd-64 "0.14.13" + esbuild-sunos-64 "0.14.13" + esbuild-windows-32 "0.14.13" + esbuild-windows-64 "0.14.13" + esbuild-windows-arm64 "0.14.13" + escalade@^3.0.2, escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -7931,6 +8217,11 @@ expect@^26.6.0, expect@^26.6.2: jest-message-util "^26.6.2" jest-regex-util "^26.0.0" +exponential-backoff@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.0.tgz#9409c7e579131f8bd4b32d7d8094a911040f2e68" + integrity sha512-oBuz5SYz5zzyuHINoe9ooePwSu0xApKWgeNzok4hZ5YKXFh9zrQBEM15CXqoZkJJPuI2ArvqjPQd8UKJA753XA== + express@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" @@ -9094,6 +9385,11 @@ html-minifier-terser@^5.0.1: relateurl "^0.2.7" terser "^4.6.3" +html-rewriter-wasm@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/html-rewriter-wasm/-/html-rewriter-wasm-0.4.0.tgz#5113b0216e6f6c121f5ac37706f7995f9e80466e" + integrity sha512-x3keWzQCFIXTEIuBsF9oDcxg64ZuufuJRDimkTYGB9jwayLCB9KZ8ENsfDdTtUr+G47Gt07g0s10yCjZw2KtXg== + html-tags@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" @@ -9156,6 +9452,11 @@ htmlparser2@^6.0.0: domutils "^2.4.4" entities "^2.0.0" +http-cache-semantics@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + http-deceiver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" @@ -10071,6 +10372,11 @@ iterate-value@^1.0.0: es-get-iterator "^1.0.2" iterate-iterator "^1.0.1" +itty-router@^2.4.10: + version "2.4.10" + resolved "https://registry.yarnpkg.com/itty-router/-/itty-router-2.4.10.tgz#23c7e44e36784e106f71ae6b1ec0fd4b95c70978" + integrity sha512-6z8UO4MoJRCdgLRVU++L6QTHJwU2pCAYFM8+9HjjcneRM5DqYyF5g6tsgIw24xXLFJlpvVrIzVzhjn862ZQZcQ== + jest-changed-files@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" @@ -10854,6 +11160,11 @@ kleur@^3.0.3: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== +kleur@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.4.tgz#8c202987d7e577766d039a8cd461934c01cda04d" + integrity sha512-8QADVssbrFjivHWQU7KkMgptGTl6WAcSdlbBPY4uNF+mWr6DGcKrvY2w4FQJoXch7+fKMjj0dRrL75vk3k23OA== + klona@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" @@ -11680,6 +11991,30 @@ mini-css-extract-plugin@0.11.3: schema-utils "^1.0.0" webpack-sources "^1.1.0" +miniflare@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/miniflare/-/miniflare-2.2.0.tgz#a791d236fb4188477fb226d4f1a5316ae11d67eb" + integrity sha512-azOKDz9RfyWfDZY4u1k+GaaRuBApfgrRoz99sK/U7ngm0pxL0YRNuHPFSHwezAxw1Oz+KQpwtPClCE0zs7Rqgg== + dependencies: + "@miniflare/cache" "2.2.0" + "@miniflare/cli-parser" "2.2.0" + "@miniflare/core" "2.2.0" + "@miniflare/durable-objects" "2.2.0" + "@miniflare/html-rewriter" "2.2.0" + "@miniflare/http-server" "2.2.0" + "@miniflare/kv" "2.2.0" + "@miniflare/runner-vm" "2.2.0" + "@miniflare/scheduler" "2.2.0" + "@miniflare/shared" "2.2.0" + "@miniflare/sites" "2.2.0" + "@miniflare/storage-file" "2.2.0" + "@miniflare/storage-memory" "2.2.0" + "@miniflare/web-sockets" "2.2.0" + kleur "^4.1.4" + semiver "^1.1.0" + source-map-support "^0.5.20" + undici "4.12.1" + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -11828,6 +12163,11 @@ multicast-dns@^6.0.1: dns-packet "^1.3.1" thunky "^1.0.2" +mustache@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" + integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== + mvdan-sh@^0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/mvdan-sh/-/mvdan-sh-0.5.0.tgz#fa76f611a103595ad0f04f5d18e582892c46e87c" @@ -11937,6 +12277,11 @@ node-forge@^0.10.0: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== +node-forge@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.2.1.tgz#82794919071ef2eb5c509293325cec8afd0fd53c" + integrity sha512-Fcvtbb+zBcZXbTTVwqGA5W+MKBj56UjVRevvchv5XrcyXbmNdesfZL37nlcWOfpgHhgmxApw3tQbTr4CqNmX4w== + node-gyp-build@~4.2.1: version "4.2.3" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" @@ -15055,6 +15400,18 @@ selfsigned@^1.10.8: dependencies: node-forge "^0.10.0" +selfsigned@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-2.0.0.tgz#e927cd5377cbb0a1075302cff8df1042cc2bce5b" + integrity sha512-cUdFiCbKoa1mZ6osuJs2uDHrs0k0oprsKveFiiaBKCNq3SYyb5gs2HxhQyDNLCmL51ZZThqi4YNDpCK6GOP1iQ== + dependencies: + node-forge "^1.2.0" + +semiver@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/semiver/-/semiver-1.1.0.tgz#9c97fb02c21c7ce4fcf1b73e2c7a24324bdddd5f" + integrity sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg== + semver-compare@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" @@ -15168,6 +15525,11 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-cookie-parser@^2.4.8: + version "2.4.8" + resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.4.8.tgz#d0da0ed388bc8f24e706a391f9c9e252a13c58b2" + integrity sha512-edRH8mBKEWNVIVMKejNnuJxleqYE/ZSdcT8/Nem9/mmosx12pctd80s2Oy00KNZzrogMZS5mauK2/ymL1bvlvg== + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -15400,6 +15762,14 @@ source-map-support@^0.5.16, source-map-support@^0.5.6, source-map-support@~0.5.1 buffer-from "^1.0.0" source-map "^0.6.0" +source-map-support@^0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" @@ -15530,6 +15900,11 @@ stable@^0.1.8: resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== +stack-trace@0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" + integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= + stack-utils@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.4.tgz#4b600971dcfc6aed0cbdf2a8268177cc916c87c8" @@ -15620,6 +15995,11 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== +streamsearch@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" + integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= + strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -16588,6 +16968,11 @@ typescript@^4.3.5: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== +ulid-workers@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/ulid-workers/-/ulid-workers-1.1.0.tgz#f8812eac5b3c80353fad06b988215b65062a714c" + integrity sha512-noAmHMKQPfsfq1YRR7nje0TgzLTyQDZpvJ61FPEsSTQnHPr7BL3uIfR1nojF2tgDuCuOSFV6QnKJA1v1m3Auig== + unbox-primitive@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.0.tgz#eeacbc4affa28e9b3d36b5eaeccc50b3251b1d3f" @@ -16598,6 +16983,11 @@ unbox-primitive@^1.0.0: has-symbols "^1.0.0" which-boxed-primitive "^1.0.1" +undici@4.12.1: + version "4.12.1" + resolved "https://registry.yarnpkg.com/undici/-/undici-4.12.1.tgz#3a7b5fb12f835a96a65397dd94578464b08d1c27" + integrity sha512-MSfap7YiQJqTPP12C11PFRs9raZuVicDbwsZHTjB0a8+SsCqt7KdUis54f373yf7ZFhJzAkGJLaKm0202OIxHg== + unfetch@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" @@ -17608,7 +17998,7 @@ ws@^7.2.3: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.0.tgz#a5dd76a24197940d4a8bb9e0e152bb4503764da7" integrity sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ== -ws@^8.4.0: +ws@^8.2.2, ws@^8.4.0: version "8.4.2" resolved "https://registry.yarnpkg.com/ws/-/ws-8.4.2.tgz#18e749868d8439f2268368829042894b6907aa0b" integrity sha512-Kbk4Nxyq7/ZWqr/tarI9yIt/+iNNFOjBXEWgTb4ydaNHBNGgvf2QHbS9fdfsndfjFlFwEd4Al+mw83YkaD10ZA== @@ -17707,6 +18097,16 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== +youch@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/youch/-/youch-2.2.2.tgz#cb87a359a5c524ebd35eb07ca3a1521dbc7e1a3e" + integrity sha512-/FaCeG3GkuJwaMR34GHVg0l8jCbafZLHiFowSjqLlqhC6OMyf2tPJBu8UirF7/NI9X/R5ai4QfEKUCOxMAGxZQ== + dependencies: + "@types/stack-trace" "0.0.29" + cookie "^0.4.1" + mustache "^4.2.0" + stack-trace "0.0.10" + zod@^3.11.6: version "3.11.6" resolved "https://registry.yarnpkg.com/zod/-/zod-3.11.6.tgz#e43a5e0c213ae2e02aefe7cb2b1a6fa3d7f1f483"