diff --git a/.claude/.pre-pr-sweep-state b/.claude/.pre-pr-sweep-state new file mode 100644 index 0000000000..d106f00346 --- /dev/null +++ b/.claude/.pre-pr-sweep-state @@ -0,0 +1 @@ +dialog-alert-type@9a715c4cda486d249c0a647d9606fdee51b6c9c9 diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000000..53998ac58c --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,9 @@ +{ + "permissions": { + "allow": [ + "Bash(yarn typecheck *)", + "Bash(yarn lint *)", + "Bash(yarn test *)" + ] + } +} diff --git a/.github/workflows/build-submit-ios.yml b/.github/workflows/build-submit-ios.yml index a6c74ac710..338ce132bb 100644 --- a/.github/workflows/build-submit-ios.yml +++ b/.github/workflows/build-submit-ios.yml @@ -109,23 +109,46 @@ jobs: run: | if [ -f "build.tar.gz" ]; then echo "Extracting build.tar.gz..." - mkdir ios-build + rm -rf ios-build + mkdir -p ios-build tar -xzf build.tar.gz -C ios-build echo "Extraction completed successfully" + + echo "" + echo "Top-level extracted files:" + find ios-build -maxdepth 3 -print + + echo "" + echo "Searching for IPA..." + IPA_PATH="$(find ios-build -type f -name '*.ipa' -print -quit)" + if [ -z "$IPA_PATH" ]; then + echo "ERROR: No .ipa found anywhere under ios-build." + echo "Archive contents:" + tar -tzf build.tar.gz | sed -n '1,200p' + exit 1 + fi + + BUILD_DIR="$(dirname "$IPA_PATH")" + echo "Found IPA at: $IPA_PATH" + echo "Build dir: $BUILD_DIR" + echo "" + echo "Build dir contents:" + ls -la "$BUILD_DIR" + echo "BUILD_DIR=$BUILD_DIR" >> $GITHUB_ENV else echo "Archive file not found!" exit 1 fi - name: 🚀 Deploy - run: eas submit -p ios --non-interactive --path ios-build/ios/build/Bluesky.ipa + run: eas submit -p ios --non-interactive --path "$BUILD_DIR/Bluesky.ipa" - name: 🪲 Upload dSYM to Sentry run: > SENTRY_ORG=blueskyweb SENTRY_PROJECT=app SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} - yarn sentry-cli debug-files upload ios-build/ios/build/Bluesky.app.dSYM.zip --include-sources + yarn sentry-cli debug-files upload "$BUILD_DIR/Bluesky.app.dSYM.zip" --include-sources - name: 📚 Get version from package.json id: get-build-info diff --git a/.github/workflows/bundle-deploy-eas-update.yml b/.github/workflows/bundle-deploy-eas-update.yml index 29271d7a6b..d7dba722c2 100644 --- a/.github/workflows/bundle-deploy-eas-update.yml +++ b/.github/workflows/bundle-deploy-eas-update.yml @@ -239,10 +239,52 @@ jobs: yarn use-build-number-with-bump eas build -p ios --profile testflight - --local --output build.ipa --non-interactive + --local --output build.tar.gz --non-interactive + + - name: 📂 Extract build artifact + run: | + if [ -f "build.tar.gz" ]; then + echo "Extracting build.tar.gz..." + rm -rf ios-build + mkdir -p ios-build + tar -xzf build.tar.gz -C ios-build + echo "Extraction completed successfully" + + echo "" + echo "Top-level extracted files:" + find ios-build -maxdepth 3 -print + + echo "" + echo "Searching for IPA..." + IPA_PATH="$(find ios-build -type f -name '*.ipa' -print -quit)" + if [ -z "$IPA_PATH" ]; then + echo "ERROR: No .ipa found anywhere under ios-build." + echo "Archive contents:" + tar -tzf build.tar.gz | sed -n '1,200p' + exit 1 + fi + + BUILD_DIR="$(dirname "$IPA_PATH")" + echo "Found IPA at: $IPA_PATH" + echo "Build dir: $BUILD_DIR" + echo "" + echo "Build dir contents:" + ls -la "$BUILD_DIR" + echo "BUILD_DIR=$BUILD_DIR" >> $GITHUB_ENV + else + echo "Archive file not found!" + exit 1 + fi - name: 🚀 Deploy - run: eas submit -p ios --non-interactive --path build.ipa + run: eas submit -p ios --non-interactive --path "$BUILD_DIR/Bluesky.ipa" + + - name: 🪲 Upload dSYM to Sentry + run: > + SENTRY_ORG=blueskyweb + SENTRY_PROJECT=app + SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} + yarn sentry-cli debug-files upload "$BUILD_DIR/Bluesky.app.dSYM.zip" --include-sources - name: ⬇️ Restore Cache id: get-base-commit diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 247217fe2d..cca7b91c52 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -51,4 +51,4 @@ jobs: # NOTE(sfn): we can add a custom system prompt here claude_args: | - --model claude-opus-4-5-20251101 + --model claude-opus-4-7 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6d6ee7eddd..60c5ccf9d7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -16,6 +16,31 @@ jobs: steps: - name: Check out Git repository uses: actions/checkout@v4 + - name: Verify Node version pins match .nvmrc + run: | + set -euo pipefail + expected=$(tr -d '[:space:]' < .nvmrc) + rc=0 + check() { + if [ "$2" != "$expected" ]; then + echo "::error file=$1::Node version mismatch: $1 pins '$2' but .nvmrc is '$expected'" + rc=1 + fi + } + # FROM node:X.Y.Z — service runtime images + for f in Dockerfile.bskylink Dockerfile.bskyogcard; do + v=$(grep -oE 'FROM node:[0-9]+\.[0-9]+\.[0-9]+' "$f" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | sort -u) + check "$f" "$v" + done + # ENV NODE_VERSION=X.Y.Z — Go images that nvm-install Node for the JS build stage + for f in Dockerfile Dockerfile.embedr; do + v=$(grep -oE 'NODE_VERSION=[0-9]+\.[0-9]+\.[0-9]+' "$f" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | sort -u) + check "$f" "$v" + done + # eas.json "node": "X.Y.Z" + v=$(grep -oE '"node":[[:space:]]*"[0-9]+\.[0-9]+\.[0-9]+"' eas.json | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | sort -u) + check "eas.json" "$v" + exit $rc - name: Install node uses: actions/setup-node@v6 with: diff --git a/.gitignore b/.gitignore index c0adc37228..6dc2fe592b 100644 --- a/.gitignore +++ b/.gitignore @@ -86,6 +86,9 @@ stats.json # VSCode .vscode +# Zed +.zed + # gitignore and github actions !.gitignore !.github @@ -127,3 +130,6 @@ bskyweb/static/media/*.webp bskyweb/static/media/*.jpg bskyweb/static/media/*.png bskyweb/static/media/*.svg + +# superpowers plugin plans/specs — local-only workspace +docs/superpowers/ diff --git a/.nvmrc b/.nvmrc index 209e3ef4b6..5bf4400f22 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20 +24.15.0 diff --git a/CLAUDE.md b/CLAUDE.md index a15b5bad88..4949a54d3f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,4 +1,4 @@ -# CLAUDE.md - Bluesky Social App Development Guide +# CLAUDE.md – Bluesky Social App Development Guide This document provides guidance for working effectively in the Bluesky Social app codebase. @@ -7,13 +7,17 @@ This document provides guidance for working effectively in the Bluesky Social ap Bluesky Social is a cross-platform social media application built with React Native and Expo. It runs on iOS, Android, and Web, connecting to the AT Protocol (atproto) decentralized social network. **Tech Stack:** + +- React 19.1 - React Native 0.81 with Expo 54 -- TypeScript -- React Navigation for routing +- TypeScript 6 +- React Navigation 7 for routing - TanStack Query (React Query) for data fetching -- Lingui for internationalization +- Lingui 5 for internationalization - Custom design system called ALF (Application Layout Framework) +Prefer using the latest features available for each of these libraries (exact versions are found in `package.json`). For example, prefer `@lingui/react/macro` over `@lingui/react`. Suggest refactoring legacy or deprecated uses. + ## Essential Commands ```bash @@ -162,6 +166,10 @@ its own looks more like a single component file. ### Documentation and Tests Within Features +Comment code when necessary to explain the “why” behind something; avoid +comments that simply describe the code. Avoid Unicode characters in comments, +e.g., use `-` not `—`. + For larger features or components, it's helpful to include a README.md file within the directory that explains the purpose of the feature, how it works, and any important implementation details. The `/Component/index.tsx` pattern lends @@ -182,6 +190,14 @@ ALF is the custom design system. It uses Tailwind-inspired naming with underscor ### Basic Usage +Generally, order atoms by: + +- Flexbox configuration, e.g., `a.flex_row` +- Spacing, e.g., `a.px_md` +- Text styles, e.g., `a.font_bold` +- Themes, e.g., `t.atoms.text`, +- Raw styles, e.g., `{backgroundColor: t.palette.primary_500}` + ```tsx import {atoms as a, useTheme} from '#/alf' @@ -190,9 +206,7 @@ function MyComponent() { return ( - - Hello - + Hello ) } @@ -200,20 +214,23 @@ function MyComponent() { ### Key Concepts -**Static Atoms** - Theme-independent styles imported from `atoms`: +**Static Atoms** – Theme-independent styles imported from `atoms`: + ```tsx import {atoms as a} from '#/alf' // a.flex_row, a.p_md, a.gap_sm, a.rounded_md, a.text_lg, etc. ``` -**Theme Atoms** - Theme-dependent colors from `useTheme()`: +**Theme Atoms** – Theme-dependent colors from `useTheme()`: + ```tsx const t = useTheme() // t.atoms.bg, t.atoms.text, t.atoms.border_contrast_low, etc. // t.palette.primary_500, t.palette.negative_400, etc. ``` -**Platform Utilities** - For platform-specific styles: +**Platform Utilities** – For platform-specific styles: + ```tsx import {web, native, ios, android, platform} from '#/alf' @@ -225,7 +242,8 @@ const styles = [ ] ``` -**Breakpoints** - Responsive design: +**Breakpoints** – Responsive design: + ```tsx import {useBreakpoints} from '#/alf' @@ -245,6 +263,38 @@ if (gtMobile) { ## Component Patterns +- Prefer fragment shorthand over `Fragment` unless a `key` is needed. +- Prefer functions over arrow functions for component declarations. +- Prefer prop destructuring via parameters over a const within the component. +- Prefer inline types over `Props` types or interfaces. +- Set reasonable defaults for optional props. + +```tsx +import {Fragment} from 'react' +import {View} from 'react-native' +import {Trans, useLingui} from '@lingui/react/macro' + +import {Text} from '#/components/Typography' + +function MyComponent({foo = []}: {foo?: string[]}) { + const {t: l} = useLingui() + + return ( + <> + Example + + {foo.map((foo, index) => ( + + {index} + {foo} + + ))} + + + ); +} +``` + ### Dialog Component Dialogs use a bottom sheet on native and a modal on web. Use `useDialogControl()` hook to manage state. @@ -263,23 +313,29 @@ function MyFeature() { {/* Typically the inner part is in its own component */} - {/* Native-only drag handle */} - - - Title - - - Dialog content here - - - {/* Web-only X button in top left */} - + ) } + +function DialogInner() { + return ( + <> + {/* Native-only drag handle */} + + + Title + + Dialog content here + + {/* Web-only X button in top left */} + + + ) +} ``` ### Menu Component @@ -345,6 +401,7 @@ import {Button, ButtonText, ButtonIcon} from '#/components/Button' ``` **Button Props:** + - `color`: `'primary'` | `'secondary'` | `'negative'` | `'primary_subtle'` | `'negative_subtle'` | `'secondary_inverted'` - `size`: `'tiny'` | `'small'` | `'large'` - `shape`: `'default'` (pill) | `'round'` | `'square'` | `'rectangular'` @@ -384,39 +441,66 @@ import * as TextField from '#/components/forms/TextField' ## Internationalization (i18n) -All user-facing strings must be wrapped for translation using Lingui. +All user-facing strings must be wrapped for translation using Lingui. Include `comment` and/or `context` props when necessary to avoid ambiguity, e.g., “Post” as a noun vs a verb. + +Prefer using `t` via `import {useLingui} '@lingui/react/macro'` vs `_` via `import {useLingui} from '@lingui/react'`. Alias `t` to `l` to avoid collisions with `const t = useTheme()`. Refactor existing uses of ``_(msg`foo`)`` to use `` l`foo` ``. + +Prefer Unicode punctuation over keyboard punctuation, e.g., `“quote”` over `"quote"`. Prefer en dashes preceded by a non-breaking space over em dashes, e.g., `one – two` over `one—two`. ```tsx -import {msg, plural} from '@lingui/core/macro' -import {Trans} from '@lingui/react/macro' -import {useLingui} from '@lingui/react' +import {plural} from '@lingui/core/macro' +import {Trans, useLingui} from '@lingui/react/macro' function MyComponent() { - const {_} = useLingui() + const {t: l} = useLingui() - // Simple strings - use msg() with _() function - const title = _(msg`Settings`) - const errorMessage = _(msg`Something went wrong`) + // Simple strings - use the l macro + const title = l`Settings` + const errorMessage = l({ + message: 'Something went wrong', + comment: 'Generic error message for unknown/unhandled errors.', + context: 'Toast', + }) // Strings with variables - const greeting = _(msg`Hello, ${name}!`) + const greeting = l`Hello, ${name}!` // Pluralization - const countLabel = _(plural(count, { + const countLabel = plural(count, { one: '# item', other: '# items', - })) + }) // JSX content - use Trans component return ( - Welcome to Bluesky + + Welcome to Bluesky, {name}! + ) } ``` +Prefer `i18n.date` for date and time formatting. This ensures formatting is re-applied when the language changes at runtime. Refactor existing uses of `Intl.DateTimeFormat` to use `i18n.date`. + +```tsx +import {useLingui} from '@lingui/react/macro' + +function MyComponent() { + const {i18n} = useLingui() + + const createdAt = new Date() + + return i18n.date(createdAt, { + dateStyle: 'medium', + timeStyle: 'medium', + }) +} +``` + **Commands:** + ```bash # DO NOT run these commands - extraction and compilation are handled by a nightly CI job yarn intl:extract # Extract new strings to locale files @@ -473,7 +557,7 @@ export function useProfileMutation() { const queryClient = useQueryClient() return useMutation({ - mutationFn: async (data) => { + mutationFn: async data => { // Update logic }, onSuccess: (_, variables) => { @@ -481,7 +565,7 @@ export function useProfileMutation() { queryKey: createProfileQueryKey({did: variables.did}), }) }, - onError: (error) => { + onError: error => { if (isNetworkError(error)) { // don't log, but inform user } else if (error instanceof AppBskyExampleProcedure.ExampleError) { @@ -490,7 +574,7 @@ export function useProfileMutation() { // Log unexpected errors to Sentry logger.error('Error updating profile', {safeMessage: error}) } - } + }, }) } @@ -505,21 +589,25 @@ export function useProfileCacheMutation() { const queryClient = useQueryClient() return (data: Partial) => { - queryClient.setQueryData(createProfileQueryKey({did: data.did}), oldData => { - if (!oldData) return oldData - return {...oldData, ...data} - }) + queryClient.setQueryData( + createProfileQueryKey({did: data.did}), + oldData => { + if (!oldData) return oldData + return {...oldData, ...data} + }, + ) } } ``` **Stale Time Constants** (from `src/state/queries/index.ts`): + ```tsx -STALE.SECONDS.FIFTEEN // 15 seconds -STALE.MINUTES.ONE // 1 minute -STALE.MINUTES.FIVE // 5 minutes -STALE.HOURS.ONE // 1 hour -STALE.INFINITY // Never stale +STALE.SECONDS.FIFTEEN // 15 seconds +STALE.MINUTES.ONE // 1 minute +STALE.MINUTES.FIVE // 5 minutes +STALE.HOURS.ONE // 1 hour +STALE.INFINITY // Never stale ``` **Paginated APIs:** Many atproto APIs return paginated results with a `cursor`. Use `useInfiniteQuery` for these: @@ -565,12 +653,7 @@ function SettingsScreen() { const autoplayDisabled = useAutoplayDisabled() const setAutoplayDisabled = useSetAutoplayDisabled() - return ( - - ) + return } ``` @@ -604,13 +687,9 @@ import {type CommonNavigatorParams} from '#/lib/routes/types' type Props = NativeStackScreenProps export function ProfileScreen({route, navigation}: Props) { - const {name} = route.params // Type-safe params + const {name} = route.params // Type-safe params - return ( - - {/* Screen content */} - - ) + return {/* Screen content */} } // Programmatic navigation @@ -637,8 +716,9 @@ Component.android.tsx # Android-only ``` Example from Dialog: -- `src/components/Dialog/index.tsx` - Native (uses BottomSheet) -- `src/components/Dialog/index.web.tsx` - Web (uses modal with Radix primitives) + +- `src/components/Dialog/index.tsx` – Native (uses BottomSheet) +- `src/components/Dialog/index.web.tsx` – Web (uses modal with Radix primitives) **Important:** The bundler automatically resolves platform-specific files. Just import normally: @@ -653,6 +733,7 @@ const storage = IS_NATIVE ``` Platform detection (for runtime logic, not imports): + ```tsx import {IS_WEB, IS_NATIVE, IS_IOS, IS_ANDROID} from '#/env' @@ -687,13 +768,13 @@ Common pitfalls to avoid in this codebase: // WRONG - causes bugs with state updates, navigation, opening other dialogs const onConfirm = () => { control.close() - navigation.navigate('Home') // May race with dialog animation + navigation.navigate('Home') // May race with dialog animation } // WRONG - same problem const onConfirm = () => { control.close() - otherDialogControl.open() // Will likely fail or cause visual glitches + otherDialogControl.open() // Will likely fail or cause visual glitches } // CORRECT - action runs after dialog fully closes @@ -720,12 +801,13 @@ const onConfirm = () => { ``` This applies to: + - Navigation (`navigation.navigate()`, `navigation.push()`) - Opening other dialogs or menus - State updates that affect UI (`setState`, `queryClient.invalidateQueries`) - Callbacks passed from parent components -The Menu component on iOS specifically uses this pattern - see `src/components/Menu/index.tsx:151`. +The Menu component on iOS specifically uses this pattern – see `src/components/Menu/index.tsx:151`. ### Controlled vs Uncontrolled Inputs @@ -748,10 +830,11 @@ Prefer `defaultValue` over `value` for TextInput on the old architecture: ### Platform-Specific Behavior Some components behave differently across platforms: -- `Dialog.Handle` - Only renders on native (drag handle for bottom sheet) -- `Dialog.Close` - Only renders on web (X button) -- `Menu.Divider` - Only renders on web -- `Menu.ContainerItem` - Only works on native + +- `Dialog.Handle` – Only renders on native (drag handle for bottom sheet) +- `Dialog.Close` – Only renders on web (X button) +- `Menu.Divider` – Only renders on web +- `Menu.ContainerItem` – Only works on native Always test on multiple platforms when using these components. @@ -772,6 +855,7 @@ const handlePress = () => { ``` Only use `useMemo`/`useCallback` when you have a specific reason, such as: + - The value is immediately used in an effect's dependency array - You're passing a callback to a non-React library that needs referential stability @@ -779,7 +863,7 @@ Only use `useMemo`/`useCallback` when you have a specific reason, such as: 1. **Accessibility**: Always provide `label` prop for interactive elements, use `accessibilityHint` where helpful -2. **Translations**: Wrap ALL user-facing strings with `msg()` or `` +2. **Translations**: Wrap ALL user-facing strings with ` `l` `` or `` 3. **Styling**: Combine static atoms with theme atoms, use platform utilities for platform-specific styles @@ -793,14 +877,14 @@ Only use `useMemo`/`useCallback` when you have a specific reason, such as: ## Key Files Reference -| Purpose | Location | -|---------|----------| -| Theme definitions | `src/alf/themes.ts` | -| Design tokens | `src/alf/tokens.ts` | -| Static atoms | `src/alf/atoms.ts` (extends `@bsky.app/alf`) | -| Navigation config | `src/Navigation.tsx` | -| Route definitions | `src/routes.ts` | -| Route types | `src/lib/routes/types.ts` | -| Query hooks | `src/state/queries/*.ts` | -| Session state | `src/state/session/index.tsx` | -| i18n setup | `src/locale/i18n.ts` | +| Purpose | Location | +| ----------------- | -------------------------------------------- | +| Theme definitions | `src/alf/themes.ts` | +| Design tokens | `src/alf/tokens.ts` | +| Static atoms | `src/alf/atoms.ts` (extends `@bsky.app/alf`) | +| Navigation config | `src/Navigation.tsx` | +| Route definitions | `src/routes.ts` | +| Route types | `src/lib/routes/types.ts` | +| Query hooks | `src/state/queries/*.ts` | +| Session state | `src/state/session/index.tsx` | +| i18n setup | `src/locale/i18n.ts` | diff --git a/Dockerfile b/Dockerfile index 32a45d2ca9..e7df4d7725 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,7 @@ ENV DEBIAN_FRONTEND=noninteractive # # Node # -ENV NODE_VERSION=20 +ENV NODE_VERSION=24.15.0 ENV NVM_DIR=/usr/share/nvm # diff --git a/Dockerfile.bskylink b/Dockerfile.bskylink index acde232256..934b40d1ad 100644 --- a/Dockerfile.bskylink +++ b/Dockerfile.bskylink @@ -1,4 +1,4 @@ -FROM node:20.11-alpine3.18 as build +FROM node:24.15.0-alpine3.22 AS build # Move files into the image and install WORKDIR /app @@ -14,7 +14,7 @@ RUN yarn build RUN yarn install --production --ignore-scripts --prefer-offline # Uses assets from build stage to reduce build size -FROM node:20.11-alpine3.18 +FROM node:24.15.0-alpine3.22 RUN apk add --update dumb-init @@ -29,8 +29,6 @@ VOLUME /app/data EXPOSE 3000 ENV LINK_PORT=3000 ENV NODE_ENV=production -# potential perf issues w/ io_uring on this version of node -ENV UV_USE_IO_URING=0 # https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md#non-root-user USER node diff --git a/Dockerfile.bskyogcard b/Dockerfile.bskyogcard index 10779a5661..540dcd526c 100644 --- a/Dockerfile.bskyogcard +++ b/Dockerfile.bskyogcard @@ -1,4 +1,4 @@ -FROM node:20.11-alpine3.18 as build +FROM node:24.15.0-alpine3.22 AS build # Move files into the image and install WORKDIR /app @@ -14,7 +14,7 @@ RUN yarn install-fonts && yarn build RUN yarn install --production --ignore-scripts --prefer-offline # Uses assets from build stage to reduce build size -FROM node:20.11-alpine3.18 +FROM node:24.15.0-alpine3.22 RUN apk add --update dumb-init @@ -30,8 +30,6 @@ EXPOSE 3000 3001 ENV CARD_PORT=3000 ENV CARD_METRICS_PORT=3001 ENV NODE_ENV=production -# potential perf issues w/ io_uring on this version of node -ENV UV_USE_IO_URING=0 # https://github.com/nodejs/docker-node/blob/master/docs/BestPractices.md#non-root-user USER node diff --git a/Dockerfile.embedr b/Dockerfile.embedr index 4f13b6d1a6..8410d8ef47 100644 --- a/Dockerfile.embedr +++ b/Dockerfile.embedr @@ -5,7 +5,7 @@ WORKDIR /usr/src/social-app ENV DEBIAN_FRONTEND=noninteractive # Node -ENV NODE_VERSION=20 +ENV NODE_VERSION=24.15.0 ENV NVM_DIR=/usr/share/nvm # Go diff --git a/__tests__/lib/string.test.ts b/__tests__/lib/string.test.ts index 7453b8d203..8a56423796 100644 --- a/__tests__/lib/string.test.ts +++ b/__tests__/lib/string.test.ts @@ -8,7 +8,7 @@ import { parseStarterPackUri, } from '#/lib/strings/starter-pack' import {messages} from '#/locale/locales/en/messages' -import {tenorUrlToBskyGifUrl} from '#/state/queries/tenor' +import {klipyUrlToBskyGifUrl} from '#/features/gifPicker/utils' import {cleanError} from '../../src/lib/strings/errors' import {createFullHandle, makeValidHandle} from '../../src/lib/strings/handles' import {enforceLen} from '../../src/lib/strings/helpers' @@ -450,6 +450,13 @@ describe('parseEmbedPlayerFromUrl', () => { 'https://sufjanstevens.bandcamp.com', 'https://bandcamp.com/', 'https://bandcamp.com', + + 'https://static.klipy.com/ii/abc123/73/ac/someFile.gif?hh=200&ww=300', + 'https://static.klipy.com/ii/abc123/73/ac/someFile.gif?hh=200&ww=300&mp4=videoSlugMp4&webm=videoSlugWebm', + 'https://static.klipy.com/ii/abc123/73/ac/someFile.gif?hh=200', + 'https://static.klipy.com/ii/abc123/73/ac/someFile.gif', + 'https://static.klipy.com/other/path.gif?hh=200&ww=300', + 'https://static.klipy.com', ] const outputs = [ @@ -845,6 +852,35 @@ describe('parseEmbedPlayerFromUrl', () => { undefined, undefined, undefined, + + { + type: 'klipy_gif', + source: 'klipy', + isGif: true, + hideDetails: true, + playerUri: 'https://k.gifs.bsky.app/ii/abc123/73/ac/someFile.gif', + dimensions: { + width: 300, + height: 200, + }, + }, + // With video slug params — on native (test env), keeps gif filename, + // strips mp4/webm params. On web, would swap to video filename. + { + type: 'klipy_gif', + source: 'klipy', + isGif: true, + hideDetails: true, + playerUri: 'https://k.gifs.bsky.app/ii/abc123/73/ac/someFile.gif', + dimensions: { + width: 300, + height: 200, + }, + }, + undefined, + undefined, + undefined, + undefined, ] it('correctly grabs the correct id from uri', () => { @@ -1035,17 +1071,30 @@ describe('createStarterPackGooglePlayUri', () => { }) }) -describe('tenorUrlToBskyGifUrl', () => { +describe('klipyUrlToBskyGifUrl', () => { const inputs = [ - 'https://media.tenor.com/someID_AAAAC/someName.gif', - 'https://media.tenor.com/someID/someName.gif', + 'https://static.klipy.com/ii/abc123/73/ac/someFile.gif', + 'https://static.klipy.com/ii/abc123/73/ac/someFile.gif?hh=200&ww=300', ] it.each(inputs)( - 'returns url with t.gifs.bsky.app as hostname for input url', + 'returns url with k.gifs.bsky.app as hostname for input url', input => { - const out = tenorUrlToBskyGifUrl(input) - expect(out.startsWith('https://t.gifs.bsky.app/')).toEqual(true) + const out = klipyUrlToBskyGifUrl(input) + expect(out.startsWith('https://k.gifs.bsky.app/')).toEqual(true) }, ) + + it('preserves the path and query params when rewriting', () => { + const out = klipyUrlToBskyGifUrl( + 'https://static.klipy.com/ii/abc123/73/ac/someFile.gif?hh=200&ww=300', + ) + expect(out).toEqual( + 'https://k.gifs.bsky.app/ii/abc123/73/ac/someFile.gif?hh=200&ww=300', + ) + }) + + it('returns empty string for invalid URLs', () => { + expect(klipyUrlToBskyGifUrl('not-a-url')).toEqual('') + }) }) diff --git a/app.config.js b/app.config.js index 5b32a2e296..1f8971209a 100644 --- a/app.config.js +++ b/app.config.js @@ -66,6 +66,7 @@ module.exports = function (_config) { infoPlist: { CADisableMinimumFrameDurationOnPhone: true, UIBackgroundModes: ['remote-notification'], + NSUserActivityTypes: ['INSendMessageIntent'], NSCameraUsageDescription: 'Used for profile pictures, posts, and other kinds of content.', NSMicrophoneUsageDescription: @@ -123,6 +124,7 @@ module.exports = function (_config) { 'com.apple.developer.kernel.increased-memory-limit': true, 'com.apple.developer.kernel.extended-virtual-addressing': true, 'com.apple.security.application-groups': 'group.app.bsky', + 'com.apple.developer.usernotifications.communication': true, // 'com.apple.developer.device-information.user-assigned-device-name': true, }, privacyManifests: { diff --git a/assets/icons/arrowBoxRight_stroke2_corner3_rounded.svg b/assets/icons/arrowBoxRight_stroke2_corner3_rounded.svg new file mode 100644 index 0000000000..7006406561 --- /dev/null +++ b/assets/icons/arrowBoxRight_stroke2_corner3_rounded.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/chainLinkBroken_stroke2_corner0_rounded.svg b/assets/icons/chainLinkBroken_stroke2_corner0_rounded.svg new file mode 100644 index 0000000000..c5197634c7 --- /dev/null +++ b/assets/icons/chainLinkBroken_stroke2_corner0_rounded.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/editBig_stroke2_corner2_rounded.svg b/assets/icons/editBig_stroke2_corner2_rounded.svg new file mode 100644 index 0000000000..7adbd1cfb1 --- /dev/null +++ b/assets/icons/editBig_stroke2_corner2_rounded.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/language_stroke2_corner2_rounded.svg b/assets/icons/language_stroke2_corner2_rounded.svg new file mode 100644 index 0000000000..9eac8d1e0d --- /dev/null +++ b/assets/icons/language_stroke2_corner2_rounded.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/messagePlus_stroke2_corner0_rounded.svg b/assets/icons/messagePlus_stroke2_corner0_rounded.svg new file mode 100644 index 0000000000..bf9e277fb8 --- /dev/null +++ b/assets/icons/messagePlus_stroke2_corner0_rounded.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/squareBehindSquare_stroke2_corner0_rounded.svg b/assets/icons/squareBehindSquare_stroke2_corner0_rounded.svg new file mode 100644 index 0000000000..69c48eead1 --- /dev/null +++ b/assets/icons/squareBehindSquare_stroke2_corner0_rounded.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/unlock_stroke2_corner2_rounded.svg b/assets/icons/unlock_stroke2_corner2_rounded.svg new file mode 100644 index 0000000000..941a6ef2e5 --- /dev/null +++ b/assets/icons/unlock_stroke2_corner2_rounded.svg @@ -0,0 +1 @@ + diff --git a/bskyembed/src/color-mode.ts b/bskyembed/src/color-mode.ts index b34624e312..3b9219cb3c 100644 --- a/bskyembed/src/color-mode.ts +++ b/bskyembed/src/color-mode.ts @@ -9,7 +9,11 @@ export function applyTheme(theme: 'light' | 'dark') { document.documentElement.classList.add(theme) } -export function initSystemColorMode() { +export function initSystemColorMode({additionalBodyClasses = ''} = {}) { + if (additionalBodyClasses) { + document.body.classList.add(additionalBodyClasses) + } + applyTheme( window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' diff --git a/bskyembed/src/screens/landing.tsx b/bskyembed/src/screens/landing.tsx index b4bb0f7e91..9c7cd68a40 100644 --- a/bskyembed/src/screens/landing.tsx +++ b/bskyembed/src/screens/landing.tsx @@ -28,7 +28,7 @@ export const EMBED_SCRIPT = `${EMBED_SERVICE}/static/embed.js` const root = document.getElementById('app') if (!root) throw new Error('No root element') -initSystemColorMode() +initSystemColorMode({additionalBodyClasses: 'dark:bg-dimmedBgDarken'}) const agent = new AtpAgent({ service: 'https://public.api.bsky.app', @@ -119,7 +119,7 @@ function LandingPage() { }, [uri]) return ( -
+
diff --git a/bskylink/package.json b/bskylink/package.json index d06247249d..23fb08aac2 100644 --- a/bskylink/package.json +++ b/bskylink/package.json @@ -4,7 +4,7 @@ "type": "module", "main": "index.ts", "scripts": { - "test": "npm run test:unit && npm run test:e2e", + "test": "yarn run test:unit && yarn run test:e2e", "test:e2e": "./tests/infra/with-test-db.sh node --loader ts-node/esm --test ./tests/index.ts", "test:unit": "node --loader ts-node/esm --test ./src/*.test.ts", "build": "tsc" diff --git a/bskyogcard/package.json b/bskyogcard/package.json index 5a2f02ca2a..e9fa78c8ee 100644 --- a/bskyogcard/package.json +++ b/bskyogcard/package.json @@ -3,11 +3,14 @@ "version": "0.0.0", "type": "module", "main": "src/index.ts", + "engines": { + "node": ">=24.15.0" + }, "scripts": { - "start": "node --loader ts-node/esm ./src/bin.ts", - "dev": "node --watch-path ./src --loader ts-node/esm ./src/bin.ts", + "start": "tsx ./src/bin.ts", + "dev": "tsx watch --clear-screen=false ./src/bin.ts", "build": "tsc && cp -r src/assets dist/", - "install-fonts": "node --loader ts-node/esm scripts/install-fonts.ts" + "install-fonts": "node scripts/install-fonts.ts" }, "dependencies": { "@atproto/api": "0.12.19-next.0", @@ -24,8 +27,9 @@ }, "devDependencies": { "@types/express": "^4.17.21", - "@types/node": "^20.14.3", - "ts-node": "^10.9.2", - "typescript": "^5.4.5" + "@types/node": "^24.12.2", + "@types/react": "^18.3.0", + "tsx": "^4.21.0", + "typescript": "^6.0.2" } } diff --git a/bskyogcard/src/context.ts b/bskyogcard/src/context.ts index 0c972c94de..4ec7c63d99 100644 --- a/bskyogcard/src/context.ts +++ b/bskyogcard/src/context.ts @@ -20,10 +20,13 @@ export class AppContext { fonts: {name: string; data: Buffer}[] abortController = new AbortController() - constructor(private opts: AppContextOptions) { - this.cfg = this.opts.cfg - this.appviewAgent = this.opts.appviewAgent - this.fonts = this.opts.fonts + private opts: AppContextOptions + + constructor(opts: AppContextOptions) { + this.opts = opts + this.cfg = opts.cfg + this.appviewAgent = opts.appviewAgent + this.fonts = opts.fonts } static async fromConfig(cfg: Config, overrides?: Partial) { diff --git a/bskyogcard/src/index.ts b/bskyogcard/src/index.ts index 53b5dbd6e1..c5bed89457 100644 --- a/bskyogcard/src/index.ts +++ b/bskyogcard/src/index.ts @@ -19,10 +19,13 @@ export class CardService { private terminator?: HttpTerminator private metricsTerminator?: HttpTerminator - constructor( - public app: express.Application, - public ctx: AppContext, - ) {} + public app: express.Application + public ctx: AppContext + + constructor(app: express.Application, ctx: AppContext) { + this.app = app + this.ctx = ctx + } static async create(cfg: Config): Promise { let app = express() diff --git a/bskyogcard/src/routes/starter-pack.tsx b/bskyogcard/src/routes/starter-pack.tsx index 99a3e73fe5..d895b744a2 100644 --- a/bskyogcard/src/routes/starter-pack.tsx +++ b/bskyogcard/src/routes/starter-pack.tsx @@ -38,11 +38,11 @@ export default function (ctx: AppContext, app: Express) { } const imageEntries = await Promise.all( [starterPack.creator] - .concat(starterPack.listItemsSample.map(li => li.subject)) + .concat((starterPack.listItemsSample ?? []).map(li => li.subject)) // has avatar .filter(p => p.avatar) // no sensitive labels - .filter(p => !p.labels.some(l => hideAvatarLabels.has(l.val))) + .filter(p => !p.labels?.some(l => hideAvatarLabels.has(l.val))) .map(async p => { try { assert(p.avatar) @@ -58,7 +58,12 @@ export default function (ctx: AppContext, app: Express) { }), ) const images = new Map( - imageEntries.filter(([_, image]) => image !== null).slice(0, 7), + imageEntries + .filter( + (entry): entry is readonly [string, Buffer] => + entry[1] !== null, + ) + .slice(0, 7), ) const svg = await satori( , @@ -68,8 +73,9 @@ export default function (ctx: AppContext, app: Express) { width: STARTERPACK_WIDTH, loadAdditionalAsset: async (code, text) => { if (code === 'emoji') { - return await loadEmojiAsSvg(text) + return (await loadEmojiAsSvg(text)) ?? '' } + return '' }, }, ) diff --git a/bskyogcard/tsconfig.json b/bskyogcard/tsconfig.json index a5c3beecb1..3b0cc15c95 100644 --- a/bskyogcard/tsconfig.json +++ b/bskyogcard/tsconfig.json @@ -3,9 +3,12 @@ "target": "ESNext", "module": "NodeNext", "esModuleInterop": true, + "erasableSyntaxOnly": true, "moduleResolution": "NodeNext", "jsx": "react-jsx", - "outDir": "dist" + "rootDir": "./src", + "outDir": "dist", + "skipLibCheck": true }, "include": ["./src/index.ts", "./src/bin.ts"] } diff --git a/bskyogcard/yarn.lock b/bskyogcard/yarn.lock index c2fb0b6d1c..3af9d5b7ca 100644 --- a/bskyogcard/yarn.lock +++ b/bskyogcard/yarn.lock @@ -90,12 +90,135 @@ resolved "https://registry.yarnpkg.com/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.2.0.tgz#4b3f07af047f984c082de34b116e765cb9af975f" integrity sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w== -"@cspotcode/source-map-support@^0.8.0": - version "0.8.1" - resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" - integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== - dependencies: - "@jridgewell/trace-mapping" "0.3.9" +"@esbuild/aix-ppc64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz#82b74f92aa78d720b714162939fb248c90addf53" + integrity sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg== + +"@esbuild/android-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz#f78cb8a3121fc205a53285adb24972db385d185d" + integrity sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ== + +"@esbuild/android-arm@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.27.7.tgz#593e10a1450bbfcac6cb321f61f468453bac209d" + integrity sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ== + +"@esbuild/android-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.27.7.tgz#453143d073326033d2d22caf9e48de4bae274b07" + integrity sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg== + +"@esbuild/darwin-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz#6f23000fb9b40b7e04b7d0606c0693bd0632f322" + integrity sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw== + +"@esbuild/darwin-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz#27393dd18bb1263c663979c5f1576e00c2d024be" + integrity sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ== + +"@esbuild/freebsd-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz#22e4638fa502d1c0027077324c97640e3adf3a62" + integrity sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w== + +"@esbuild/freebsd-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz#9224b8e4fea924ce2194e3efc3e9aebf822192d6" + integrity sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ== + +"@esbuild/linux-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz#4f5d1c27527d817b35684ae21419e57c2bda0966" + integrity sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A== + +"@esbuild/linux-arm@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz#b9e9d070c8c1c0449cf12b20eac37d70a4595921" + integrity sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA== + +"@esbuild/linux-ia32@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz#3f80fb696aa96051a94047f35c85b08b21c36f9e" + integrity sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg== + +"@esbuild/linux-loong64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz#9be1f2c28210b13ebb4156221bba356fe1675205" + integrity sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q== + +"@esbuild/linux-mips64el@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz#4ab5ee67a3dfcbcb5e8fd7883dae6e735b1163b8" + integrity sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw== + +"@esbuild/linux-ppc64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz#dac78c689f6499459c4321e5c15032c12307e7ea" + integrity sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ== + +"@esbuild/linux-riscv64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz#050f7d3b355c3a98308e935bc4d6325da91b0027" + integrity sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ== + +"@esbuild/linux-s390x@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz#d61f715ce61d43fe5844ad0d8f463f88cbe4fef6" + integrity sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw== + +"@esbuild/linux-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz#ca8e1aa478fc8209257bf3ac8f79c4dc2982f32a" + integrity sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA== + +"@esbuild/netbsd-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz#1650f2c1b948deeb3ef948f2fc30614723c09690" + integrity sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w== + +"@esbuild/netbsd-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz#65772ab342c4b3319bf0705a211050aac1b6e320" + integrity sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw== + +"@esbuild/openbsd-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz#37ed7cfa66549d7955852fce37d0c3de4e715ea1" + integrity sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A== + +"@esbuild/openbsd-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz#01bf3d385855ef50cb33db7c4b52f957c34cd179" + integrity sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg== + +"@esbuild/openharmony-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz#6c1f94b34086599aabda4eac8f638294b9877410" + integrity sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw== + +"@esbuild/sunos-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz#4b0dd17ae0a6941d2d0fd35a906392517071a90d" + integrity sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA== + +"@esbuild/win32-arm64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz#34193ab5565d6ff68ca928ac04be75102ccb2e77" + integrity sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA== + +"@esbuild/win32-ia32@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz#eb67f0e4482515d8c1894ede631c327a4da9fc4d" + integrity sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw== + +"@esbuild/win32-x64@0.27.7": + version "0.27.7" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz#8fe30b3088b89b4873c3a6cc87597ae3920c0a8b" + integrity sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg== "@ipld/dag-cbor@^7.0.3": version "7.0.3" @@ -105,24 +228,6 @@ cborg "^1.6.0" multiformats "^9.5.4" -"@jridgewell/resolve-uri@^3.0.3": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" - integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== - -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== - -"@jridgewell/trace-mapping@0.3.9": - version "0.3.9" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" - integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@opentelemetry/api@^1.4.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.9.0.tgz#d03eba68273dc0f7509e2a3d5cba21eae10379fe" @@ -214,26 +319,6 @@ fflate "^0.7.3" string.prototype.codepointat "^0.2.1" -"@tsconfig/node10@^1.0.7": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" - integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== - -"@tsconfig/node12@^1.0.7": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" - integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== - -"@tsconfig/node14@^1.0.0": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" - integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== - -"@tsconfig/node16@^1.0.2": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" - integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== - "@types/body-parser@*": version "1.19.6" resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.6.tgz#1859bebb8fd7dac9918a45d54c1971ab8b5af474" @@ -286,12 +371,17 @@ dependencies: undici-types "~7.12.0" -"@types/node@^20.14.3": - version "20.14.3" - resolved "https://registry.npmjs.org/@types/node/-/node-20.14.3.tgz" - integrity sha512-Nuzqa6WAxeGnve6SXqiPAM9rA++VQs+iLZ1DDd56y0gdvygSZlQvZuvdFPR3yLqkVxPu4WrO02iDEyH1g+wazw== +"@types/node@^24.12.2": + version "24.12.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-24.12.2.tgz#353cb161dbf1785ea25e8829ba7ec574c5c629ac" + integrity sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g== dependencies: - undici-types "~5.26.4" + undici-types "~7.16.0" + +"@types/prop-types@*": + version "15.7.15" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.15.tgz#e6e5a86d602beaca71ce5163fadf5f95d70931c7" + integrity sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw== "@types/qs@*": version "6.14.0" @@ -303,6 +393,14 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== +"@types/react@^18.3.0": + version "18.3.28" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.3.28.tgz#0a85b1a7243b4258d9f626f43797ba18eb5f8781" + integrity sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw== + dependencies: + "@types/prop-types" "*" + csstype "^3.2.2" + "@types/send@*": version "0.17.5" resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.5.tgz#d991d4f2b16f2b1ef497131f00a9114290791e74" @@ -335,23 +433,6 @@ accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" -acorn-walk@^8.1.1: - version "8.3.3" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.3.tgz#9caeac29eefaa0c41e3d4c65137de4d6f34df43e" - integrity sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw== - dependencies: - acorn "^8.11.0" - -acorn@^8.11.0, acorn@^8.4.1: - version "8.12.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.0.tgz#1627bfa2e058148036133b8d9b51a700663c294c" - integrity sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw== - -arg@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" - integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz" @@ -521,11 +602,6 @@ cookie@0.7.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.1.tgz#2f73c42142d5d5cf71310a74fc4ae61670e5dbc9" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== -create-require@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" - integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== - css-background-parser@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/css-background-parser/-/css-background-parser-0.1.0.tgz" @@ -550,6 +626,11 @@ css-to-react-native@^3.0.0: css-color-keywords "^1.0.0" postcss-value-parser "^4.0.2" +csstype@^3.2.2: + version "3.2.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.2.3.tgz#ec48c0f3e993e50648c86da559e2610995cf989a" + integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== + debug@2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" @@ -586,11 +667,6 @@ detect-libc@^2.0.1: resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz" integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw== -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - dunder-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" @@ -644,6 +720,38 @@ es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: dependencies: es-errors "^1.3.0" +esbuild@~0.27.0: + version "0.27.7" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.27.7.tgz#bcadce22b2f3fd76f257e3a64f83a64986fea11f" + integrity sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w== + optionalDependencies: + "@esbuild/aix-ppc64" "0.27.7" + "@esbuild/android-arm" "0.27.7" + "@esbuild/android-arm64" "0.27.7" + "@esbuild/android-x64" "0.27.7" + "@esbuild/darwin-arm64" "0.27.7" + "@esbuild/darwin-x64" "0.27.7" + "@esbuild/freebsd-arm64" "0.27.7" + "@esbuild/freebsd-x64" "0.27.7" + "@esbuild/linux-arm" "0.27.7" + "@esbuild/linux-arm64" "0.27.7" + "@esbuild/linux-ia32" "0.27.7" + "@esbuild/linux-loong64" "0.27.7" + "@esbuild/linux-mips64el" "0.27.7" + "@esbuild/linux-ppc64" "0.27.7" + "@esbuild/linux-riscv64" "0.27.7" + "@esbuild/linux-s390x" "0.27.7" + "@esbuild/linux-x64" "0.27.7" + "@esbuild/netbsd-arm64" "0.27.7" + "@esbuild/netbsd-x64" "0.27.7" + "@esbuild/openbsd-arm64" "0.27.7" + "@esbuild/openbsd-x64" "0.27.7" + "@esbuild/openharmony-arm64" "0.27.7" + "@esbuild/sunos-x64" "0.27.7" + "@esbuild/win32-arm64" "0.27.7" + "@esbuild/win32-ia32" "0.27.7" + "@esbuild/win32-x64" "0.27.7" + escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" @@ -810,6 +918,11 @@ fs-extra@^8.0.1: jsonfile "^4.0.0" universalify "^0.1.0" +fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" @@ -850,6 +963,13 @@ get-proto@^1.0.1: dunder-proto "^1.0.1" es-object-atoms "^1.0.0" +get-tsconfig@^4.7.5: + version "4.14.0" + resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.14.0.tgz#985d85c52a9903864280ccc2448d413fbf1efed8" + integrity sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA== + dependencies: + resolve-pkg-maps "^1.0.0" + gopd@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" @@ -990,11 +1110,6 @@ loose-envify@^1.1.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" -make-error@^1.1.1: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - math-intrinsics@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" @@ -1273,6 +1388,11 @@ real-require@^0.2.0: resolved "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz" integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg== +resolve-pkg-maps@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz#616b3dc2c57056b5588c31cdf4b3d64db133720f" + integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw== + roarr@^7.0.4: version "7.21.1" resolved "https://registry.npmjs.org/roarr/-/roarr-7.21.1.tgz" @@ -1515,24 +1635,15 @@ toidentifier@1.0.1: resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== -ts-node@^10.9.2: - version "10.9.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" - integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== +tsx@^4.21.0: + version "4.21.0" + resolved "https://registry.yarnpkg.com/tsx/-/tsx-4.21.0.tgz#32aa6cf17481e336f756195e6fe04dae3e6308b1" + integrity sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw== dependencies: - "@cspotcode/source-map-support" "^0.8.0" - "@tsconfig/node10" "^1.0.7" - "@tsconfig/node12" "^1.0.7" - "@tsconfig/node14" "^1.0.0" - "@tsconfig/node16" "^1.0.2" - acorn "^8.4.1" - acorn-walk "^8.1.1" - arg "^4.1.0" - create-require "^1.1.0" - diff "^4.0.1" - make-error "^1.1.1" - v8-compile-cache-lib "^3.0.1" - yn "3.1.1" + esbuild "~0.27.0" + get-tsconfig "^4.7.5" + optionalDependencies: + fsevents "~2.3.3" twemoji-parser@14.0.0: version "14.0.0" @@ -1562,10 +1673,10 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -typescript@^5.4.5: - version "5.4.5" - resolved "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz" - integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== +typescript@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-6.0.3.tgz#90251dc007916e972786cb94d74d15b185577d21" + integrity sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw== uint8arrays@3.0.0: version "3.0.0" @@ -1574,16 +1685,16 @@ uint8arrays@3.0.0: dependencies: multiformats "^9.4.2" -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== - undici-types@~7.12.0: version "7.12.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.12.0.tgz#15c5c7475c2a3ba30659529f5cdb4674b622fafb" integrity sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ== +undici-types@~7.16.0: + version "7.16.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46" + integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw== + unicode-trie@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz" @@ -1612,21 +1723,11 @@ utils-merge@1.0.1: resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -v8-compile-cache-lib@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" - integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== - vary@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== -yn@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" - integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== - yoga-wasm-web@^0.3.3: version "0.3.3" resolved "https://registry.npmjs.org/yoga-wasm-web/-/yoga-wasm-web-0.3.3.tgz" diff --git a/bskyweb/templates/base.html b/bskyweb/templates/base.html index e04047ced4..e33479e697 100644 --- a/bskyweb/templates/base.html +++ b/bskyweb/templates/base.html @@ -2,7 +2,7 @@ - +