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 @@
-
+