mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-11 20:05:25 +00:00
Compare commits
16 commits
c95b4adac9
...
2be9217ba7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2be9217ba7 | ||
|
|
d63f3cf9d9 | ||
|
|
3d85a0595c | ||
|
|
84cf524d84 | ||
|
|
7c72578765 | ||
|
|
483b433aab | ||
|
|
bcca1d6d7d | ||
|
|
0e88eecc30 | ||
|
|
a4685e34ab | ||
|
|
76dc2f5a12 | ||
|
|
7fc068f8b2 | ||
|
|
91caf129ed | ||
|
|
92578881df | ||
|
|
28986f4355 | ||
|
|
41a6eb48a0 | ||
|
|
7341a49407 |
48 changed files with 886 additions and 616 deletions
|
|
@ -21,7 +21,7 @@ AUTH_URL="http://localhost:3000"
|
|||
|
||||
DATA_CACHE_DIR=${PWD}/.sourcebot # Path to the sourcebot cache dir (ex. ~/sourcebot/.sourcebot)
|
||||
SOURCEBOT_PUBLIC_KEY_PATH=${PWD}/public.pem
|
||||
# CONFIG_PATH=${PWD}/config.json # Path to the sourcebot config file (if one exists)
|
||||
CONFIG_PATH=${PWD}/config.json # Path to the sourcebot config file (if one exists)
|
||||
|
||||
# Email
|
||||
# EMAIL_FROM_ADDRESS="" # The from address for transactional emails.
|
||||
|
|
@ -29,7 +29,6 @@ SOURCEBOT_PUBLIC_KEY_PATH=${PWD}/public.pem
|
|||
|
||||
# PostHog
|
||||
# POSTHOG_PAPIK=""
|
||||
# NEXT_PUBLIC_POSTHOG_PAPIK=""
|
||||
|
||||
# Sentry
|
||||
# SENTRY_BACKEND_DSN=""
|
||||
|
|
|
|||
1
.github/workflows/_gcp-deploy.yml
vendored
1
.github/workflows/_gcp-deploy.yml
vendored
|
|
@ -55,7 +55,6 @@ jobs:
|
|||
${{ env.IMAGE_PATH }}:latest
|
||||
build-args: |
|
||||
NEXT_PUBLIC_SOURCEBOT_VERSION=${{ github.ref_name }}
|
||||
NEXT_PUBLIC_POSTHOG_PAPIK=${{ vars.NEXT_PUBLIC_POSTHOG_PAPIK }}
|
||||
NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT=${{ vars.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT }}
|
||||
NEXT_PUBLIC_SENTRY_ENVIRONMENT=${{ vars.NEXT_PUBLIC_SENTRY_ENVIRONMENT }}
|
||||
NEXT_PUBLIC_SENTRY_WEBAPP_DSN=${{ vars.NEXT_PUBLIC_SENTRY_WEBAPP_DSN }}
|
||||
|
|
|
|||
1
.github/workflows/ghcr-publish.yml
vendored
1
.github/workflows/ghcr-publish.yml
vendored
|
|
@ -77,7 +77,6 @@ jobs:
|
|||
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true,annotation.org.opencontainers.image.description=Blazingly fast code search
|
||||
build-args: |
|
||||
NEXT_PUBLIC_SOURCEBOT_VERSION=${{ github.ref_name }}
|
||||
NEXT_PUBLIC_POSTHOG_PAPIK=${{ vars.NEXT_PUBLIC_POSTHOG_PAPIK }}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
|
|
|
|||
22
CHANGELOG.md
22
CHANGELOG.md
|
|
@ -7,14 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
### Fixed
|
||||
- Fixed review agent so that it works with GHES instances [#611](https://github.com/sourcebot-dev/sourcebot/pull/611)
|
||||
|
||||
### Added
|
||||
- Added support for arbitrary user IDs required for OpenShift. [#658](https://github.com/sourcebot-dev/sourcebot/pull/658)
|
||||
|
||||
### Updated
|
||||
- Improved error messages in file source api. [#665](https://github.com/sourcebot-dev/sourcebot/pull/665)
|
||||
|
||||
## [4.10.2] - 2025-12-04
|
||||
|
||||
### Fixed
|
||||
- Fixed issue where the disable telemetry flag was not being respected for web server telemetry. [#657](https://github.com/sourcebot-dev/sourcebot/pull/657)
|
||||
|
||||
## [4.10.1] - 2025-12-03
|
||||
|
||||
### Added
|
||||
- Added `ALWAYS_INDEX_FILE_PATTERNS` environment variable to allow specifying a comma seperated list of glob patterns matching file paths that should always be indexed, regardless of size or # of trigrams. [#631](https://github.com/sourcebot-dev/sourcebot/pull/631)
|
||||
- Added button to explore menu to toggle cross-repository search. [#647](https://github.com/sourcebot-dev/sourcebot/pull/647)
|
||||
- Added server side telemetry for search metrics. [#652](https://github.com/sourcebot-dev/sourcebot/pull/652)
|
||||
|
||||
### Fixed
|
||||
- Fixed issue where single quotes could not be used in search queries. [#629](https://github.com/sourcebot-dev/sourcebot/pull/629)
|
||||
- Fixed issue where files with special characters would fail to load. [#636](https://github.com/sourcebot-dev/sourcebot/issues/636)
|
||||
- Fixed Ask performance issues. [#632](https://github.com/sourcebot-dev/sourcebot/pull/632)
|
||||
- Fixed regression where creating a new Ask thread when unauthenticated would result in a 404. [#641](https://github.com/sourcebot-dev/sourcebot/pull/641)
|
||||
- Updated react and next package versions to fix CVE 2025-55182. [#654](https://github.com/sourcebot-dev/sourcebot/pull/654)
|
||||
|
||||
### Changed
|
||||
- Changed the default behaviour for code nav to scope references & definitions search to the current repository. [#647](https://github.com/sourcebot-dev/sourcebot/pull/647)
|
||||
|
||||
## [4.10.0] - 2025-11-24
|
||||
|
||||
|
|
|
|||
48
Dockerfile
48
Dockerfile
|
|
@ -1,3 +1,4 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
# ------ Global scope variables ------
|
||||
|
||||
# Set of global build arguments.
|
||||
|
|
@ -8,11 +9,6 @@
|
|||
# @see: https://docs.docker.com/build/building/variables/#scoping
|
||||
|
||||
ARG NEXT_PUBLIC_SOURCEBOT_VERSION
|
||||
# PAPIK = Project API Key
|
||||
# Note that this key does not need to be kept secret, so it's not
|
||||
# necessary to use Docker build secrets here.
|
||||
# @see: https://posthog.com/tutorials/api-capture-events#authenticating-with-the-project-api-key
|
||||
ARG NEXT_PUBLIC_POSTHOG_PAPIK
|
||||
ARG NEXT_PUBLIC_SENTRY_ENVIRONMENT
|
||||
ARG NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT
|
||||
ARG NEXT_PUBLIC_SENTRY_WEBAPP_DSN
|
||||
|
|
@ -57,8 +53,6 @@ ENV SKIP_ENV_VALIDATION=1
|
|||
# -----------
|
||||
ARG NEXT_PUBLIC_SOURCEBOT_VERSION
|
||||
ENV NEXT_PUBLIC_SOURCEBOT_VERSION=$NEXT_PUBLIC_SOURCEBOT_VERSION
|
||||
ARG NEXT_PUBLIC_POSTHOG_PAPIK
|
||||
ENV NEXT_PUBLIC_POSTHOG_PAPIK=$NEXT_PUBLIC_POSTHOG_PAPIK
|
||||
ARG NEXT_PUBLIC_SENTRY_ENVIRONMENT
|
||||
ENV NEXT_PUBLIC_SENTRY_ENVIRONMENT=$NEXT_PUBLIC_SENTRY_ENVIRONMENT
|
||||
ARG NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT
|
||||
|
|
@ -148,14 +142,12 @@ fi
|
|||
|
||||
ENV SKIP_ENV_VALIDATION=0
|
||||
# ------------------------------
|
||||
|
||||
|
||||
# ------ Runner ------
|
||||
FROM node-alpine AS runner
|
||||
# -----------
|
||||
ARG NEXT_PUBLIC_SOURCEBOT_VERSION
|
||||
ENV NEXT_PUBLIC_SOURCEBOT_VERSION=$NEXT_PUBLIC_SOURCEBOT_VERSION
|
||||
ARG NEXT_PUBLIC_POSTHOG_PAPIK
|
||||
ENV NEXT_PUBLIC_POSTHOG_PAPIK=$NEXT_PUBLIC_POSTHOG_PAPIK
|
||||
ARG NEXT_PUBLIC_SENTRY_ENVIRONMENT
|
||||
ENV NEXT_PUBLIC_SENTRY_ENVIRONMENT=$NEXT_PUBLIC_SENTRY_ENVIRONMENT
|
||||
ARG NEXT_PUBLIC_SENTRY_WEBAPP_DSN
|
||||
|
|
@ -178,6 +170,12 @@ ENV DATA_CACHE_DIR=$DATA_DIR/.sourcebot
|
|||
ENV DATABASE_DATA_DIR=$DATA_CACHE_DIR/db
|
||||
ENV REDIS_DATA_DIR=$DATA_CACHE_DIR/redis
|
||||
ENV SOURCEBOT_PUBLIC_KEY_PATH=/app/public.pem
|
||||
# PAPIK = Project API Key
|
||||
# Note that this key does not need to be kept secret, so it's not
|
||||
# necessary to use Docker build secrets here.
|
||||
# @see: https://posthog.com/tutorials/api-capture-events#authenticating-with-the-project-api-key
|
||||
# @note: this is also declared in the shared env.server.ts file.
|
||||
ENV POSTHOG_PAPIK=phc_lLPuFFi5LH6c94eFJcqvYVFwiJffVcV6HD8U4a1OnRW
|
||||
|
||||
# Valid values are: debug, info, warn, error
|
||||
ENV SOURCEBOT_LOG_LEVEL=info
|
||||
|
|
@ -197,6 +195,7 @@ RUN addgroup -g $GID sourcebot && \
|
|||
adduser -D -u $UID -h /app -S sourcebot && \
|
||||
adduser sourcebot postgres && \
|
||||
adduser sourcebot redis && \
|
||||
chown -R sourcebot /app && \
|
||||
adduser sourcebot node && \
|
||||
mkdir /var/log/sourcebot && \
|
||||
chown sourcebot /var/log/sourcebot
|
||||
|
|
@ -220,22 +219,23 @@ COPY --from=zoekt-builder \
|
|||
/cmd/zoekt-index \
|
||||
/usr/local/bin/
|
||||
|
||||
RUN chown -R sourcebot:sourcebot /app
|
||||
|
||||
# Copy zoekt proto files (needed for gRPC client at runtime)
|
||||
COPY vendor/zoekt/grpc/protos /app/vendor/zoekt/grpc/protos
|
||||
COPY --chown=sourcebot:sourcebot vendor/zoekt/grpc/protos /app/vendor/zoekt/grpc/protos
|
||||
|
||||
# Copy all of the things
|
||||
COPY --from=web-builder /app/packages/web/public ./packages/web/public
|
||||
COPY --from=web-builder /app/packages/web/.next/standalone ./
|
||||
COPY --from=web-builder /app/packages/web/.next/static ./packages/web/.next/static
|
||||
COPY --chown=sourcebot:sourcebot --from=web-builder /app/packages/web/public ./packages/web/public
|
||||
COPY --chown=sourcebot:sourcebot --from=web-builder /app/packages/web/.next/standalone ./
|
||||
COPY --chown=sourcebot:sourcebot --from=web-builder /app/packages/web/.next/static ./packages/web/.next/static
|
||||
|
||||
COPY --from=backend-builder /app/node_modules ./node_modules
|
||||
COPY --from=backend-builder /app/packages/backend ./packages/backend
|
||||
COPY --chown=sourcebot:sourcebot --from=backend-builder /app/node_modules ./node_modules
|
||||
COPY --chown=sourcebot:sourcebot --from=backend-builder /app/packages/backend ./packages/backend
|
||||
|
||||
COPY --from=shared-libs-builder /app/node_modules ./node_modules
|
||||
COPY --from=shared-libs-builder /app/packages/db ./packages/db
|
||||
COPY --from=shared-libs-builder /app/packages/schemas ./packages/schemas
|
||||
COPY --from=shared-libs-builder /app/packages/shared ./packages/shared
|
||||
COPY --from=shared-libs-builder /app/packages/queryLanguage ./packages/queryLanguage
|
||||
COPY --chown=sourcebot:sourcebot --from=shared-libs-builder /app/packages/db ./packages/db
|
||||
COPY --chown=sourcebot:sourcebot --from=shared-libs-builder /app/packages/schemas ./packages/schemas
|
||||
COPY --chown=sourcebot:sourcebot --from=shared-libs-builder /app/packages/shared ./packages/shared
|
||||
COPY --chown=sourcebot:sourcebot --from=shared-libs-builder /app/packages/queryLanguage ./packages/queryLanguage
|
||||
|
||||
# Fixes git "dubious ownership" issues when the volume is mounted with different permissions to the container.
|
||||
RUN git config --global safe.directory "*"
|
||||
|
|
@ -246,9 +246,11 @@ RUN mkdir -p /run/postgresql && \
|
|||
chmod 775 /run/postgresql
|
||||
|
||||
# Make app directory accessible to both root and sourcebot user
|
||||
RUN chown -R sourcebot:sourcebot /app
|
||||
RUN chown -R sourcebot /app \
|
||||
&& chgrp -R 0 /app \
|
||||
&& chmod -R g=u /app
|
||||
# Make data directory accessible to both root and sourcebot user
|
||||
RUN chown -R sourcebot:sourcebot /data
|
||||
RUN chown -R sourcebot /data
|
||||
|
||||
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||
COPY prefix-output.sh ./prefix-output.sh
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import LicenseKeyRequired from '/snippets/license-key-required.mdx'
|
|||
| **Go to definition** | Clicking the "go to definition" button in the popover or clicking the symbol name navigates to the symbol's definition. |
|
||||
| **Find references** | Clicking the "find all references" button in the popover lists all references in the explore panel. |
|
||||
| **Explore panel** | Lists all references and definitions for the symbol selected in the popover. |
|
||||
| **Cross-repository navigation** | You can search across all repositories by clicking the globe icon in the explore panel. By default, references and definitions are scoped to the repository where the symbol is being resolved. |
|
||||
|
||||
## How does it work?
|
||||
|
||||
|
|
|
|||
|
|
@ -66,12 +66,6 @@ fi
|
|||
|
||||
echo -e "\e[34m[Info] Sourcebot version: $NEXT_PUBLIC_SOURCEBOT_VERSION\e[0m"
|
||||
|
||||
# If we don't have a PostHog key, then we need to disable telemetry.
|
||||
if [ -z "$NEXT_PUBLIC_POSTHOG_PAPIK" ]; then
|
||||
echo -e "\e[33m[Warning] NEXT_PUBLIC_POSTHOG_PAPIK was not set. Setting SOURCEBOT_TELEMETRY_DISABLED.\e[0m"
|
||||
export SOURCEBOT_TELEMETRY_DISABLED=true
|
||||
fi
|
||||
|
||||
if [ -n "$SOURCEBOT_TELEMETRY_DISABLED" ]; then
|
||||
# Validate that SOURCEBOT_TELEMETRY_DISABLED is either "true" or "false"
|
||||
if [ "$SOURCEBOT_TELEMETRY_DISABLED" != "true" ] && [ "$SOURCEBOT_TELEMETRY_DISABLED" != "false" ]; then
|
||||
|
|
@ -159,7 +153,7 @@ if [ ! -f "$FIRST_RUN_FILE" ]; then
|
|||
# (if telemetry is enabled)
|
||||
if [ "$SOURCEBOT_TELEMETRY_DISABLED" = "false" ]; then
|
||||
if ! ( curl -L --output /dev/null --silent --fail --header "Content-Type: application/json" -d '{
|
||||
"api_key": "'"$NEXT_PUBLIC_POSTHOG_PAPIK"'",
|
||||
"api_key": "'"$POSTHOG_PAPIK"'",
|
||||
"event": "install",
|
||||
"distinct_id": "'"$SOURCEBOT_INSTALL_ID"'",
|
||||
"properties": {
|
||||
|
|
@ -179,7 +173,7 @@ else
|
|||
|
||||
if [ "$SOURCEBOT_TELEMETRY_DISABLED" = "false" ]; then
|
||||
if ! ( curl -L --output /dev/null --silent --fail --header "Content-Type: application/json" -d '{
|
||||
"api_key": "'"$NEXT_PUBLIC_POSTHOG_PAPIK"'",
|
||||
"api_key": "'"$POSTHOG_PAPIK"'",
|
||||
"event": "upgrade",
|
||||
"distinct_id": "'"$SOURCEBOT_INSTALL_ID"'",
|
||||
"properties": {
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import { PosthogEvent, PosthogEventMap } from './posthogEvents.js';
|
|||
|
||||
let posthog: PostHog | undefined = undefined;
|
||||
|
||||
if (clientEnv.NEXT_PUBLIC_POSTHOG_PAPIK) {
|
||||
if (env.POSTHOG_PAPIK) {
|
||||
posthog = new PostHog(
|
||||
clientEnv.NEXT_PUBLIC_POSTHOG_PAPIK,
|
||||
env.POSTHOG_PAPIK,
|
||||
{
|
||||
host: "https://us.i.posthog.com",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.0.11] - 2025-12-03
|
||||
|
||||
### Changed
|
||||
- Updated API client to match the latest Sourcebot release. [#652](https://github.com/sourcebot-dev/sourcebot/pull/652)
|
||||
|
||||
## [1.0.10] - 2025-11-24
|
||||
|
||||
### Changed
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@sourcebot/mcp",
|
||||
"version": "1.0.10",
|
||||
"version": "1.0.11",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ export const search = async (request: SearchRequest): Promise<SearchResponse | S
|
|||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Org-Domain': '~',
|
||||
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
|
||||
},
|
||||
body: JSON.stringify(request)
|
||||
|
|
@ -26,7 +25,6 @@ export const listRepos = async (): Promise<ListRepositoriesResponse | ServiceErr
|
|||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Org-Domain': '~',
|
||||
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
|
||||
},
|
||||
}).then(response => response.json());
|
||||
|
|
@ -43,7 +41,6 @@ export const getFileSource = async (request: FileSourceRequest): Promise<FileSou
|
|||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Org-Domain': '~',
|
||||
...(env.SOURCEBOT_API_KEY ? { 'X-Sourcebot-Api-Key': env.SOURCEBOT_API_KEY } : {})
|
||||
},
|
||||
body: JSON.stringify(request)
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ server.tool(
|
|||
contextLines: env.DEFAULT_CONTEXT_LINES,
|
||||
isRegexEnabled: true,
|
||||
isCaseSensitivityEnabled: caseSensitive,
|
||||
source: 'mcp'
|
||||
});
|
||||
|
||||
if (isServiceError(response)) {
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ export const searchOptionsSchema = z.object({
|
|||
|
||||
export const searchRequestSchema = z.object({
|
||||
query: z.string(), // The zoekt query to execute.
|
||||
source: z.string().optional(), // The source of the search request.
|
||||
...searchOptionsSchema.shape,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ export const env = createEnv({
|
|||
client: {
|
||||
NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT: z.enum(SOURCEBOT_CLOUD_ENVIRONMENT).optional(),
|
||||
NEXT_PUBLIC_SOURCEBOT_VERSION: z.string().default("unknown"),
|
||||
NEXT_PUBLIC_POSTHOG_PAPIK: z.string().optional(),
|
||||
NEXT_PUBLIC_SENTRY_BACKEND_DSN: z.string().optional(),
|
||||
NEXT_PUBLIC_SENTRY_ENVIRONMENT: z.string().optional(),
|
||||
NEXT_PUBLIC_LANGFUSE_PUBLIC_KEY: z.string().optional(),
|
||||
|
|
@ -16,7 +15,6 @@ export const env = createEnv({
|
|||
runtimeEnvStrict: {
|
||||
NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT: process.env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT,
|
||||
NEXT_PUBLIC_SOURCEBOT_VERSION: process.env.NEXT_PUBLIC_SOURCEBOT_VERSION,
|
||||
NEXT_PUBLIC_POSTHOG_PAPIK: process.env.NEXT_PUBLIC_POSTHOG_PAPIK,
|
||||
NEXT_PUBLIC_SENTRY_BACKEND_DSN: process.env.NEXT_PUBLIC_SENTRY_BACKEND_DSN,
|
||||
NEXT_PUBLIC_SENTRY_ENVIRONMENT: process.env.NEXT_PUBLIC_SENTRY_ENVIRONMENT,
|
||||
NEXT_PUBLIC_LANGFUSE_PUBLIC_KEY: process.env.NEXT_PUBLIC_LANGFUSE_PUBLIC_KEY,
|
||||
|
|
|
|||
|
|
@ -120,6 +120,8 @@ export const env = createEnv({
|
|||
CONFIG_MAX_REPOS_NO_TOKEN: numberSchema.default(Number.MAX_SAFE_INTEGER),
|
||||
NODE_ENV: z.enum(["development", "test", "production"]),
|
||||
SOURCEBOT_TELEMETRY_DISABLED: booleanSchema.default('false'),
|
||||
// @note: this is also declared in the Dockerfile.
|
||||
POSTHOG_PAPIK: z.string().default("phc_lLPuFFi5LH6c94eFJcqvYVFwiJffVcV6HD8U4a1OnRW"),
|
||||
|
||||
// Database variables
|
||||
// Either DATABASE_URL or DATABASE_HOST, DATABASE_USERNAME, DATABASE_PASSWORD, and DATABASE_NAME must be set.
|
||||
|
|
|
|||
|
|
@ -147,7 +147,7 @@
|
|||
"langfuse-vercel": "^3.38.4",
|
||||
"lucide-react": "^0.517.0",
|
||||
"micromatch": "^4.0.8",
|
||||
"next": "15.5.0",
|
||||
"next": "^15.5.7",
|
||||
"next-auth": "^5.0.0-beta.30",
|
||||
"next-navigation-guard": "^0.2.0",
|
||||
"next-themes": "^0.3.0",
|
||||
|
|
@ -156,11 +156,12 @@
|
|||
"openai": "^4.98.0",
|
||||
"parse-diff": "^0.11.1",
|
||||
"posthog-js": "^1.161.5",
|
||||
"posthog-node": "^5.15.0",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
"psl": "^1.15.0",
|
||||
"react": "19.1.1",
|
||||
"react": "^19.2.1",
|
||||
"react-device-detect": "^2.2.3",
|
||||
"react-dom": "19.1.1",
|
||||
"react-dom": "^19.2.1",
|
||||
"react-hook-form": "^7.53.0",
|
||||
"react-hotkeys-hook": "^4.5.1",
|
||||
"react-icons": "^5.3.0",
|
||||
|
|
@ -196,8 +197,8 @@
|
|||
"@types/node": "^20",
|
||||
"@types/nodemailer": "^6.4.17",
|
||||
"@types/psl": "^1.1.3",
|
||||
"@types/react": "19.1.10",
|
||||
"@types/react-dom": "19.1.7",
|
||||
"@types/react": "19.2.1",
|
||||
"@types/react-dom": "19.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.40.0",
|
||||
"@typescript-eslint/parser": "^8.40.0",
|
||||
"cross-env": "^7.0.3",
|
||||
|
|
@ -217,7 +218,7 @@
|
|||
"vitest-mock-extended": "^3.1.0"
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "19.1.10",
|
||||
"@types/react-dom": "19.1.7"
|
||||
"@types/react": "19.2.1",
|
||||
"@types/react-dom": "19.2.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ const auditService = getAuditService();
|
|||
/**
|
||||
* "Service Error Wrapper".
|
||||
*
|
||||
* Captures any thrown exceptions and converts them to a unexpected
|
||||
* service error. Also logs them with Sentry.
|
||||
* Captures any thrown exceptions, logs them to the console and Sentry,
|
||||
* and returns a generic unexpected service error.
|
||||
*/
|
||||
export const sew = async <T>(fn: () => Promise<T>): Promise<T | ServiceError> => {
|
||||
try {
|
||||
|
|
@ -52,10 +52,6 @@ export const sew = async <T>(fn: () => Promise<T>): Promise<T | ServiceError> =>
|
|||
return e.serviceError;
|
||||
}
|
||||
|
||||
if (e instanceof Error) {
|
||||
return unexpectedError(e.message);
|
||||
}
|
||||
|
||||
return unexpectedError(`An unexpected error occurred. Please try again later.`);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,12 @@ export const CodePreviewPanel = async ({ path, repoName, revisionName }: CodePre
|
|||
getRepoInfoByName(repoName),
|
||||
]);
|
||||
|
||||
if (isServiceError(fileSourceResponse) || isServiceError(repoInfoResponse)) {
|
||||
return <div>Error loading file source</div>
|
||||
if (isServiceError(fileSourceResponse)) {
|
||||
return <div>Error loading file source: {fileSourceResponse.message}</div>
|
||||
}
|
||||
|
||||
if (isServiceError(repoInfoResponse)) {
|
||||
return <div>Error loading repo info: {repoInfoResponse.message}</div>
|
||||
}
|
||||
|
||||
const codeHostInfo = getCodeHostInfoForRepo({
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { SymbolHoverPopup } from "@/ee/features/codeNav/components/symbolHoverPopup";
|
||||
import { symbolHoverTargetsExtension } from "@/ee/features/codeNav/components/symbolHoverPopup/symbolHoverTargetsExtension";
|
||||
import { SymbolDefinition } from "@/ee/features/codeNav/components/symbolHoverPopup/useHoveredOverSymbolInfo";
|
||||
import { useHasEntitlement } from "@/features/entitlements/useHasEntitlement";
|
||||
import { useCodeMirrorLanguageExtension } from "@/hooks/useCodeMirrorLanguageExtension";
|
||||
import { useCodeMirrorTheme } from "@/hooks/useCodeMirrorTheme";
|
||||
|
|
@ -11,14 +10,10 @@ import { useKeymapExtension } from "@/hooks/useKeymapExtension";
|
|||
import { useNonEmptyQueryParam } from "@/hooks/useNonEmptyQueryParam";
|
||||
import { search } from "@codemirror/search";
|
||||
import CodeMirror, { EditorSelection, EditorView, ReactCodeMirrorRef, SelectionRange, ViewUpdate } from "@uiw/react-codemirror";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { EditorContextMenu } from "../../../components/editorContextMenu";
|
||||
import { useBrowseNavigation } from "../../hooks/useBrowseNavigation";
|
||||
import { BrowseHighlightRange, HIGHLIGHT_RANGE_QUERY_PARAM } from "../../hooks/utils";
|
||||
import { useBrowseState } from "../../hooks/useBrowseState";
|
||||
import { rangeHighlightingExtension } from "./rangeHighlightingExtension";
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
||||
import { createAuditAction } from "@/ee/features/audit/actions";
|
||||
|
||||
interface PureCodePreviewPanelProps {
|
||||
path: string;
|
||||
|
|
@ -40,9 +35,6 @@ export const PureCodePreviewPanel = ({
|
|||
const [currentSelection, setCurrentSelection] = useState<SelectionRange>();
|
||||
const keymapExtension = useKeymapExtension(editorRef?.view);
|
||||
const hasCodeNavEntitlement = useHasEntitlement("code-nav");
|
||||
const { updateBrowseState } = useBrowseState();
|
||||
const { navigateToPath } = useBrowseNavigation();
|
||||
const captureEvent = useCaptureEvent();
|
||||
|
||||
const highlightRangeQuery = useNonEmptyQueryParam(HIGHLIGHT_RANGE_QUERY_PARAM);
|
||||
const highlightRange = useMemo((): BrowseHighlightRange | undefined => {
|
||||
|
|
@ -88,7 +80,6 @@ export const PureCodePreviewPanel = ({
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}, [highlightRangeQuery]);
|
||||
|
||||
const extensions = useMemo(() => {
|
||||
|
|
@ -116,90 +107,31 @@ export const PureCodePreviewPanel = ({
|
|||
|
||||
// Scroll the highlighted range into view.
|
||||
useEffect(() => {
|
||||
if (!highlightRange || !editorRef || !editorRef.state) {
|
||||
if (!highlightRange || !editorRef || !editorRef.state || !editorRef.view) {
|
||||
return;
|
||||
}
|
||||
|
||||
const doc = editorRef.state.doc;
|
||||
const { start, end } = highlightRange;
|
||||
const selection = EditorSelection.range(
|
||||
doc.line(start.lineNumber).from,
|
||||
doc.line(end.lineNumber).from,
|
||||
);
|
||||
|
||||
const from = doc.line(start.lineNumber).from;
|
||||
const to = doc.line(end.lineNumber).to;
|
||||
const selection = EditorSelection.range(from, to);
|
||||
|
||||
// When the selection is in view, we don't want to perform any scrolling
|
||||
// as it could be jarring for the user. If it is not in view, scroll to the
|
||||
// center of the viewport.
|
||||
const viewport = editorRef.view.viewport;
|
||||
const isInView = from >= viewport.from && to <= viewport.to;
|
||||
const scrollStrategy = isInView ? "nearest" : "center";
|
||||
|
||||
editorRef.view?.dispatch({
|
||||
effects: [
|
||||
EditorView.scrollIntoView(selection, { y: "center" }),
|
||||
EditorView.scrollIntoView(selection, { y: scrollStrategy }),
|
||||
]
|
||||
});
|
||||
}, [editorRef, highlightRange]);
|
||||
|
||||
const onFindReferences = useCallback((symbolName: string) => {
|
||||
captureEvent('wa_find_references_pressed', {
|
||||
source: 'browse',
|
||||
});
|
||||
createAuditAction({
|
||||
action: "user.performed_find_references",
|
||||
metadata: {
|
||||
message: symbolName,
|
||||
},
|
||||
})
|
||||
|
||||
updateBrowseState({
|
||||
selectedSymbolInfo: {
|
||||
repoName,
|
||||
symbolName,
|
||||
revisionName,
|
||||
language,
|
||||
},
|
||||
isBottomPanelCollapsed: false,
|
||||
activeExploreMenuTab: "references",
|
||||
})
|
||||
}, [captureEvent, updateBrowseState, repoName, revisionName, language]);
|
||||
|
||||
|
||||
// If we resolve multiple matches, instead of navigating to the first match, we should
|
||||
// instead popup the bottom sheet with the list of matches.
|
||||
const onGotoDefinition = useCallback((symbolName: string, symbolDefinitions: SymbolDefinition[]) => {
|
||||
captureEvent('wa_goto_definition_pressed', {
|
||||
source: 'browse',
|
||||
});
|
||||
createAuditAction({
|
||||
action: "user.performed_goto_definition",
|
||||
metadata: {
|
||||
message: symbolName,
|
||||
},
|
||||
})
|
||||
|
||||
if (symbolDefinitions.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (symbolDefinitions.length === 1) {
|
||||
const symbolDefinition = symbolDefinitions[0];
|
||||
const { fileName, repoName } = symbolDefinition;
|
||||
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName,
|
||||
path: fileName,
|
||||
pathType: 'blob',
|
||||
highlightRange: symbolDefinition.range,
|
||||
})
|
||||
} else {
|
||||
updateBrowseState({
|
||||
selectedSymbolInfo: {
|
||||
symbolName,
|
||||
repoName,
|
||||
revisionName,
|
||||
language,
|
||||
},
|
||||
activeExploreMenuTab: "definitions",
|
||||
isBottomPanelCollapsed: false,
|
||||
})
|
||||
}
|
||||
}, [captureEvent, navigateToPath, revisionName, updateBrowseState, repoName, language]);
|
||||
|
||||
const theme = useCodeMirrorTheme();
|
||||
|
||||
return (
|
||||
|
|
@ -223,11 +155,12 @@ export const PureCodePreviewPanel = ({
|
|||
)}
|
||||
{editorRef && hasCodeNavEntitlement && (
|
||||
<SymbolHoverPopup
|
||||
source="preview"
|
||||
editorRef={editorRef}
|
||||
revisionName={revisionName}
|
||||
language={language}
|
||||
onFindReferences={onFindReferences}
|
||||
onGotoDefinition={onGotoDefinition}
|
||||
fileName={path}
|
||||
repoName={repoName}
|
||||
/>
|
||||
)}
|
||||
</CodeMirror>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { useBrowseParams } from "./hooks/useBrowseParams";
|
|||
import { FileSearchCommandDialog } from "./components/fileSearchCommandDialog";
|
||||
import { useDomain } from "@/hooks/useDomain";
|
||||
import { SearchBar } from "../components/searchBar";
|
||||
import escapeStringRegexp from "escape-string-regexp";
|
||||
|
||||
interface LayoutProps {
|
||||
children: React.ReactNode;
|
||||
|
|
@ -30,7 +31,7 @@ export default function Layout({
|
|||
<SearchBar
|
||||
size="sm"
|
||||
defaults={{
|
||||
query: `repo:${repoName}${revisionName ? ` rev:${revisionName}` : ''} `,
|
||||
query: `repo:^${escapeStringRegexp(repoName)}$${revisionName ? ` rev:${revisionName}` : ''} `,
|
||||
}}
|
||||
className="w-full"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ export const useSuggestionsData = ({
|
|||
query: `file:${suggestionQuery}`,
|
||||
matches: 15,
|
||||
contextLines: 1,
|
||||
source: 'search-bar-file-suggestions'
|
||||
}),
|
||||
select: (data): Suggestion[] => {
|
||||
if (isServiceError(data)) {
|
||||
|
|
@ -75,6 +76,7 @@ export const useSuggestionsData = ({
|
|||
query: `sym:${suggestionQuery.length > 0 ? suggestionQuery : ".*"}`,
|
||||
matches: 15,
|
||||
contextLines: 1,
|
||||
source: 'search-bar-symbol-suggestions'
|
||||
}),
|
||||
select: (data): Suggestion[] => {
|
||||
if (isServiceError(data)) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
'use client';
|
||||
|
||||
import { useBrowseNavigation } from "@/app/[domain]/browse/hooks/useBrowseNavigation";
|
||||
import { EditorContextMenu } from "@/app/[domain]/components/editorContextMenu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { SymbolHoverPopup } from "@/ee/features/codeNav/components/symbolHoverPopup";
|
||||
import { symbolHoverTargetsExtension } from "@/ee/features/codeNav/components/symbolHoverPopup/symbolHoverTargetsExtension";
|
||||
import { useHasEntitlement } from "@/features/entitlements/useHasEntitlement";
|
||||
import { SearchResultChunk } from "@/features/search";
|
||||
import { useCodeMirrorLanguageExtension } from "@/hooks/useCodeMirrorLanguageExtension";
|
||||
import { useCodeMirrorTheme } from "@/hooks/useCodeMirrorTheme";
|
||||
import { useKeymapExtension } from "@/hooks/useKeymapExtension";
|
||||
import { useCodeMirrorLanguageExtension } from "@/hooks/useCodeMirrorLanguageExtension";
|
||||
import { gutterWidthExtension } from "@/lib/extensions/gutterWidthExtension";
|
||||
import { highlightRanges, searchResultHighlightExtension } from "@/lib/extensions/searchResultHighlightExtension";
|
||||
import { search } from "@codemirror/search";
|
||||
|
|
@ -16,13 +20,6 @@ import { Scrollbar } from "@radix-ui/react-scroll-area";
|
|||
import CodeMirror, { ReactCodeMirrorRef, SelectionRange } from '@uiw/react-codemirror';
|
||||
import { ArrowDown, ArrowUp } from "lucide-react";
|
||||
import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useBrowseNavigation } from "@/app/[domain]/browse/hooks/useBrowseNavigation";
|
||||
import { SymbolHoverPopup } from "@/ee/features/codeNav/components/symbolHoverPopup";
|
||||
import { symbolHoverTargetsExtension } from "@/ee/features/codeNav/components/symbolHoverPopup/symbolHoverTargetsExtension";
|
||||
import { useHasEntitlement } from "@/features/entitlements/useHasEntitlement";
|
||||
import { SymbolDefinition } from "@/ee/features/codeNav/components/symbolHoverPopup/useHoveredOverSymbolInfo";
|
||||
import { createAuditAction } from "@/ee/features/audit/actions";
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
||||
|
||||
export interface CodePreviewFile {
|
||||
content: string;
|
||||
|
|
@ -59,8 +56,6 @@ export const CodePreview = ({
|
|||
const languageExtension = useCodeMirrorLanguageExtension(file?.language ?? '', editorRef?.view);
|
||||
const [currentSelection, setCurrentSelection] = useState<SelectionRange>();
|
||||
|
||||
const captureEvent = useCaptureEvent();
|
||||
|
||||
const extensions = useMemo(() => {
|
||||
return [
|
||||
keymapExtension,
|
||||
|
|
@ -115,81 +110,6 @@ export const CodePreview = ({
|
|||
onSelectedMatchIndexChange((prev) => prev + 1);
|
||||
}, [onSelectedMatchIndexChange]);
|
||||
|
||||
const onGotoDefinition = useCallback((symbolName: string, symbolDefinitions: SymbolDefinition[]) => {
|
||||
captureEvent('wa_goto_definition_pressed', {
|
||||
source: 'preview',
|
||||
});
|
||||
createAuditAction({
|
||||
action: "user.performed_goto_definition",
|
||||
metadata: {
|
||||
message: symbolName,
|
||||
},
|
||||
})
|
||||
|
||||
if (symbolDefinitions.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (symbolDefinitions.length === 1) {
|
||||
const symbolDefinition = symbolDefinitions[0];
|
||||
const { fileName, repoName } = symbolDefinition;
|
||||
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName: file.revision,
|
||||
path: fileName,
|
||||
pathType: 'blob',
|
||||
highlightRange: symbolDefinition.range,
|
||||
})
|
||||
} else {
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName: file.revision,
|
||||
path: file.filepath,
|
||||
pathType: 'blob',
|
||||
setBrowseState: {
|
||||
selectedSymbolInfo: {
|
||||
symbolName,
|
||||
repoName,
|
||||
revisionName: file.revision,
|
||||
language: file.language,
|
||||
},
|
||||
activeExploreMenuTab: "definitions",
|
||||
isBottomPanelCollapsed: false,
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [captureEvent, file.filepath, file.language, file.revision, navigateToPath, repoName]);
|
||||
|
||||
const onFindReferences = useCallback((symbolName: string) => {
|
||||
captureEvent('wa_find_references_pressed', {
|
||||
source: 'preview',
|
||||
});
|
||||
createAuditAction({
|
||||
action: "user.performed_find_references",
|
||||
metadata: {
|
||||
message: symbolName,
|
||||
},
|
||||
})
|
||||
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName: file.revision,
|
||||
path: file.filepath,
|
||||
pathType: 'blob',
|
||||
setBrowseState: {
|
||||
selectedSymbolInfo: {
|
||||
repoName,
|
||||
symbolName,
|
||||
revisionName: file.revision,
|
||||
language: file.language,
|
||||
},
|
||||
activeExploreMenuTab: "references",
|
||||
isBottomPanelCollapsed: false,
|
||||
}
|
||||
})
|
||||
}, [captureEvent, file.filepath, file.language, file.revision, navigateToPath, repoName]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="flex flex-row bg-accent items-center justify-between pr-3 py-0.5 mt-7">
|
||||
|
|
@ -218,7 +138,7 @@ export const CodePreview = ({
|
|||
}}
|
||||
title={file.filepath}
|
||||
>
|
||||
{file.filepath}
|
||||
<span>{file.filepath}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
|
@ -286,11 +206,12 @@ export const CodePreview = ({
|
|||
|
||||
{editorRef && hasCodeNavEntitlement && (
|
||||
<SymbolHoverPopup
|
||||
source="preview"
|
||||
editorRef={editorRef}
|
||||
language={file.language}
|
||||
revisionName={file.revision}
|
||||
onFindReferences={onFindReferences}
|
||||
onGotoDefinition={onGotoDefinition}
|
||||
fileName={file.filepath}
|
||||
repoName={repoName}
|
||||
/>
|
||||
)}
|
||||
</CodeMirror>
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ export const Entry = ({
|
|||
<div className="overflow-hidden flex-1 min-w-0">
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<p className="overflow-hidden text-ellipsis whitespace-nowrap truncate-start">{displayName}</p>
|
||||
<p className="overflow-hidden text-ellipsis whitespace-nowrap truncate-start"><span>{displayName}</span></p>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right" className="max-w-sm">
|
||||
<p className="font-mono text-sm break-all whitespace-pre-wrap">{displayName}</p>
|
||||
|
|
|
|||
|
|
@ -129,7 +129,8 @@ export const useStreamedSearch = ({ query, matches, contextLines, whole, isRegex
|
|||
whole,
|
||||
isRegexEnabled,
|
||||
isCaseSensitivityEnabled,
|
||||
}),
|
||||
source: 'sourcebot-web-client'
|
||||
} satisfies SearchRequest),
|
||||
signal: abortControllerRef.current.signal,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { search, searchRequestSchema } from "@/features/search";
|
|||
import { isServiceError } from "@/lib/utils";
|
||||
import { NextRequest } from "next/server";
|
||||
import { schemaValidationError, serviceErrorResponse } from "@/lib/serviceError";
|
||||
import { captureEvent } from "@/lib/posthog";
|
||||
|
||||
export const POST = async (request: NextRequest) => {
|
||||
const body = await request.json();
|
||||
|
|
@ -16,8 +17,14 @@ export const POST = async (request: NextRequest) => {
|
|||
|
||||
const {
|
||||
query,
|
||||
source,
|
||||
...options
|
||||
} = parsed.data;
|
||||
|
||||
await captureEvent('api_code_search_request', {
|
||||
source: source ?? 'unknown',
|
||||
type: 'blocking',
|
||||
});
|
||||
|
||||
const response = await search({
|
||||
queryType: 'string',
|
||||
|
|
@ -28,5 +35,6 @@ export const POST = async (request: NextRequest) => {
|
|||
if (isServiceError(response)) {
|
||||
return serviceErrorResponse(response);
|
||||
}
|
||||
|
||||
return Response.json(response);
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
'use server';
|
||||
|
||||
import { streamSearch, searchRequestSchema } from '@/features/search';
|
||||
import { captureEvent } from '@/lib/posthog';
|
||||
import { schemaValidationError, serviceErrorResponse } from '@/lib/serviceError';
|
||||
import { isServiceError } from '@/lib/utils';
|
||||
import { NextRequest } from 'next/server';
|
||||
|
|
@ -15,9 +16,15 @@ export const POST = async (request: NextRequest) => {
|
|||
|
||||
const {
|
||||
query,
|
||||
source,
|
||||
...options
|
||||
} = parsed.data;
|
||||
|
||||
await captureEvent('api_code_search_request', {
|
||||
source: source ?? 'unknown',
|
||||
type: 'streamed',
|
||||
});
|
||||
|
||||
const stream = await streamSearch({
|
||||
queryType: 'string',
|
||||
query,
|
||||
|
|
|
|||
|
|
@ -6,28 +6,32 @@ import { WebhookEventDefinition} from "@octokit/webhooks/types";
|
|||
import { EndpointDefaults } from "@octokit/types";
|
||||
import { env } from "@sourcebot/shared";
|
||||
import { processGitHubPullRequest } from "@/features/agents/review-agent/app";
|
||||
import { throttling } from "@octokit/plugin-throttling";
|
||||
import { throttling, type ThrottlingOptions } from "@octokit/plugin-throttling";
|
||||
import fs from "fs";
|
||||
import { GitHubPullRequest } from "@/features/agents/review-agent/types";
|
||||
import { createLogger } from "@sourcebot/shared";
|
||||
|
||||
const logger = createLogger('github-webhook');
|
||||
|
||||
let githubApp: App | undefined;
|
||||
const DEFAULT_GITHUB_API_BASE_URL = "https://api.github.com";
|
||||
type GitHubAppBaseOptions = Omit<ConstructorParameters<typeof App>[0], "Octokit"> & { throttle: ThrottlingOptions };
|
||||
|
||||
let githubAppBaseOptions: GitHubAppBaseOptions | undefined;
|
||||
const githubAppCache = new Map<string, App>();
|
||||
|
||||
if (env.GITHUB_REVIEW_AGENT_APP_ID && env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET && env.GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH) {
|
||||
try {
|
||||
const privateKey = fs.readFileSync(env.GITHUB_REVIEW_AGENT_APP_PRIVATE_KEY_PATH, "utf8");
|
||||
|
||||
const throttledOctokit = Octokit.plugin(throttling);
|
||||
githubApp = new App({
|
||||
githubAppBaseOptions = {
|
||||
appId: env.GITHUB_REVIEW_AGENT_APP_ID,
|
||||
privateKey: privateKey,
|
||||
privateKey,
|
||||
webhooks: {
|
||||
secret: env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET,
|
||||
},
|
||||
Octokit: throttledOctokit,
|
||||
throttle: {
|
||||
onRateLimit: (retryAfter: number, options: Required<EndpointDefaults>, octokit: Octokit, retryCount: number) => {
|
||||
enabled: true,
|
||||
onRateLimit: (retryAfter, _options, _octokit, retryCount) => {
|
||||
if (retryCount > 3) {
|
||||
logger.warn(`Rate limit exceeded: ${retryAfter} seconds`);
|
||||
return false;
|
||||
|
|
@ -35,13 +39,55 @@ if (env.GITHUB_REVIEW_AGENT_APP_ID && env.GITHUB_REVIEW_AGENT_APP_WEBHOOK_SECRET
|
|||
|
||||
return true;
|
||||
},
|
||||
}
|
||||
});
|
||||
onSecondaryRateLimit: (_retryAfter, options) => {
|
||||
// no retries on secondary rate limits
|
||||
logger.warn(`SecondaryRateLimit detected for ${options.method} ${options.url}`);
|
||||
}
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error(`Error initializing GitHub app: ${error}`);
|
||||
}
|
||||
}
|
||||
|
||||
const normalizeGithubApiBaseUrl = (baseUrl?: string) => {
|
||||
if (!baseUrl) {
|
||||
return DEFAULT_GITHUB_API_BASE_URL;
|
||||
}
|
||||
|
||||
return baseUrl.replace(/\/+$/, "");
|
||||
};
|
||||
|
||||
const resolveGithubApiBaseUrl = (headers: Record<string, string>) => {
|
||||
const enterpriseHost = headers["x-github-enterprise-host"];
|
||||
if (enterpriseHost) {
|
||||
return normalizeGithubApiBaseUrl(`https://${enterpriseHost}/api/v3`);
|
||||
}
|
||||
|
||||
return DEFAULT_GITHUB_API_BASE_URL;
|
||||
};
|
||||
|
||||
const getGithubAppForBaseUrl = (baseUrl: string) => {
|
||||
if (!githubAppBaseOptions) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const normalizedBaseUrl = normalizeGithubApiBaseUrl(baseUrl);
|
||||
const cachedApp = githubAppCache.get(normalizedBaseUrl);
|
||||
if (cachedApp) {
|
||||
return cachedApp;
|
||||
}
|
||||
|
||||
const OctokitWithBaseUrl = Octokit.plugin(throttling).defaults({ baseUrl: normalizedBaseUrl });
|
||||
const app = new App({
|
||||
...githubAppBaseOptions,
|
||||
Octokit: OctokitWithBaseUrl,
|
||||
});
|
||||
|
||||
githubAppCache.set(normalizedBaseUrl, app);
|
||||
return app;
|
||||
};
|
||||
|
||||
function isPullRequestEvent(eventHeader: string, payload: unknown): payload is WebhookEventDefinition<"pull-request-opened"> | WebhookEventDefinition<"pull-request-synchronize"> {
|
||||
return eventHeader === "pull_request" && typeof payload === "object" && payload !== null && "action" in payload && typeof payload.action === "string" && (payload.action === "opened" || payload.action === "synchronize");
|
||||
}
|
||||
|
|
@ -52,12 +98,16 @@ function isIssueCommentEvent(eventHeader: string, payload: unknown): payload is
|
|||
|
||||
export const POST = async (request: NextRequest) => {
|
||||
const body = await request.json();
|
||||
const headers = Object.fromEntries(request.headers.entries());
|
||||
const headers = Object.fromEntries(Array.from(request.headers.entries(), ([key, value]) => [key.toLowerCase(), value]));
|
||||
|
||||
const githubEvent = headers['x-github-event'] || headers['X-GitHub-Event'];
|
||||
const githubEvent = headers['x-github-event'];
|
||||
if (githubEvent) {
|
||||
logger.info('GitHub event received:', githubEvent);
|
||||
|
||||
const githubApiBaseUrl = resolveGithubApiBaseUrl(headers);
|
||||
logger.debug('Using GitHub API base URL for event', { githubApiBaseUrl });
|
||||
const githubApp = getGithubAppForBaseUrl(githubApiBaseUrl);
|
||||
|
||||
if (!githubApp) {
|
||||
logger.warn('Received GitHub webhook event but GitHub app env vars are not set');
|
||||
return Response.json({ status: 'ok' });
|
||||
|
|
@ -113,4 +163,4 @@ export const POST = async (request: NextRequest) => {
|
|||
}
|
||||
|
||||
return Response.json({ status: 'ok' });
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
import { cn } from '@/lib/utils'
|
||||
import React from 'react'
|
||||
|
||||
interface KeyboardShortcutHintProps {
|
||||
shortcut: string
|
||||
label?: string
|
||||
className?: string
|
||||
}
|
||||
|
||||
export function KeyboardShortcutHint({ shortcut, label }: KeyboardShortcutHintProps) {
|
||||
export function KeyboardShortcutHint({ shortcut, label, className }: KeyboardShortcutHintProps) {
|
||||
return (
|
||||
<div className="inline-flex items-center" aria-label={label || `Keyboard shortcut: ${shortcut}`}>
|
||||
<div className={cn("inline-flex items-center", className)} aria-label={label || `Keyboard shortcut: ${shortcut}`}>
|
||||
<kbd
|
||||
className="px-2 py-1 font-semibold font-sans border rounded-md"
|
||||
style={{
|
||||
|
|
|
|||
|
|
@ -313,6 +313,11 @@
|
|||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.truncate-start > * {
|
||||
direction: ltr;
|
||||
unicode-bidi: embed;
|
||||
}
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-border;
|
||||
|
|
|
|||
|
|
@ -37,7 +37,12 @@ export default function RootLayout({
|
|||
<Toaster />
|
||||
<SessionProvider>
|
||||
<PlanProvider entitlements={getEntitlements()}>
|
||||
<PostHogProvider disabled={env.SOURCEBOT_TELEMETRY_DISABLED === "true"}>
|
||||
<PostHogProvider
|
||||
isDisabled={env.SOURCEBOT_TELEMETRY_DISABLED === "true"}
|
||||
// @note: the posthog api key doesn't need to be kept secret,
|
||||
// so we are safe to send it to the client.
|
||||
posthogApiKey={env.POSTHOG_PAPIK}
|
||||
>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
|
|
|
|||
|
|
@ -32,16 +32,16 @@ function PostHogPageView() {
|
|||
|
||||
interface PostHogProviderProps {
|
||||
children: React.ReactNode
|
||||
disabled: boolean
|
||||
isDisabled: boolean
|
||||
posthogApiKey: string
|
||||
}
|
||||
|
||||
export function PostHogProvider({ children, disabled }: PostHogProviderProps) {
|
||||
export function PostHogProvider({ children, isDisabled, posthogApiKey }: PostHogProviderProps) {
|
||||
const { data: session } = useSession();
|
||||
|
||||
useEffect(() => {
|
||||
if (!disabled && env.NEXT_PUBLIC_POSTHOG_PAPIK) {
|
||||
console.debug(`PostHog telemetry enabled. Cloud environment: ${env.NEXT_PUBLIC_SOURCEBOT_CLOUD_ENVIRONMENT}`);
|
||||
posthog.init(env.NEXT_PUBLIC_POSTHOG_PAPIK, {
|
||||
if (!isDisabled) {
|
||||
posthog.init(posthogApiKey, {
|
||||
// @see next.config.mjs for path rewrites to the "/ingest" route.
|
||||
api_host: "/ingest",
|
||||
person_profiles: 'identified_only',
|
||||
|
|
@ -66,7 +66,7 @@ export function PostHogProvider({ children, disabled }: PostHogProviderProps) {
|
|||
} else {
|
||||
console.debug("PostHog telemetry disabled");
|
||||
}
|
||||
}, [disabled]);
|
||||
}, [isDisabled, posthogApiKey]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!session) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { cva, type VariantProps } from "class-variance-authority"
|
|||
import { cn } from "@/lib/utils"
|
||||
|
||||
const toggleVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 gap-2",
|
||||
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0 gap-2 cursor-pointer",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
|
|
@ -16,9 +16,7 @@ const toggleVariants = cva(
|
|||
"border border-input bg-transparent hover:bg-accent hover:text-accent-foreground",
|
||||
},
|
||||
size: {
|
||||
default: "h-10 px-3 min-w-10",
|
||||
sm: "h-9 px-2.5 min-w-9",
|
||||
lg: "h-11 px-5 min-w-11",
|
||||
default: "h-7 w-7 min-w-7 p-0",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,23 @@
|
|||
'use client';
|
||||
|
||||
import { useBrowseState } from "@/app/[domain]/browse/hooks/useBrowseState";
|
||||
import { findSearchBasedSymbolReferences, findSearchBasedSymbolDefinitions} from "@/app/api/(client)/client";
|
||||
import { findSearchBasedSymbolDefinitions, findSearchBasedSymbolReferences } from "@/app/api/(client)/client";
|
||||
import { AnimatedResizableHandle } from "@/components/ui/animatedResizableHandle";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable";
|
||||
import { Toggle } from "@/components/ui/toggle";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { useDomain } from "@/hooks/useDomain";
|
||||
import { unwrapServiceError } from "@/lib/utils";
|
||||
import { measure, unwrapServiceError } from "@/lib/utils";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import clsx from "clsx";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { useMemo } from "react";
|
||||
import { GlobeIcon, Loader2 } from "lucide-react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { VscSymbolMisc } from "react-icons/vsc";
|
||||
import { ReferenceList } from "./referenceList";
|
||||
import { KeyboardShortcutHint } from "@/app/components/keyboardShortcutHint";
|
||||
import { useHotkeys } from "react-hotkeys-hook";
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
||||
|
||||
interface ExploreMenuProps {
|
||||
selectedSymbolInfo: {
|
||||
|
|
@ -27,27 +31,39 @@ interface ExploreMenuProps {
|
|||
export const ExploreMenu = ({
|
||||
selectedSymbolInfo,
|
||||
}: ExploreMenuProps) => {
|
||||
|
||||
const domain = useDomain();
|
||||
const captureEvent = useCaptureEvent();
|
||||
const {
|
||||
state: { activeExploreMenuTab },
|
||||
updateBrowseState,
|
||||
} = useBrowseState();
|
||||
|
||||
const [isGlobalSearchEnabled, setIsGlobalSearchEnabled] = useState(false);
|
||||
|
||||
const {
|
||||
data: referencesResponse,
|
||||
isError: isReferencesResponseError,
|
||||
isPending: isReferencesResponsePending,
|
||||
isLoading: isReferencesResponseLoading,
|
||||
} = useQuery({
|
||||
queryKey: ["references", selectedSymbolInfo.symbolName, selectedSymbolInfo.repoName, selectedSymbolInfo.revisionName, selectedSymbolInfo.language, domain],
|
||||
queryFn: () => unwrapServiceError(
|
||||
findSearchBasedSymbolReferences({
|
||||
symbolName: selectedSymbolInfo.symbolName,
|
||||
language: selectedSymbolInfo.language,
|
||||
revisionName: selectedSymbolInfo.revisionName,
|
||||
queryKey: ["references", selectedSymbolInfo.symbolName, selectedSymbolInfo.repoName, selectedSymbolInfo.revisionName, selectedSymbolInfo.language, domain, isGlobalSearchEnabled],
|
||||
queryFn: async () => {
|
||||
const response = await measure(() => unwrapServiceError(
|
||||
findSearchBasedSymbolReferences({
|
||||
symbolName: selectedSymbolInfo.symbolName,
|
||||
language: selectedSymbolInfo.language,
|
||||
revisionName: selectedSymbolInfo.revisionName,
|
||||
repoName: isGlobalSearchEnabled ? undefined : selectedSymbolInfo.repoName
|
||||
})
|
||||
), 'findSearchBasedSymbolReferences', false);
|
||||
|
||||
captureEvent('wa_explore_menu_references_loaded', {
|
||||
durationMs: response.durationMs,
|
||||
isGlobalSearchEnabled,
|
||||
})
|
||||
),
|
||||
|
||||
return response.data;
|
||||
}
|
||||
});
|
||||
|
||||
const {
|
||||
|
|
@ -56,14 +72,32 @@ export const ExploreMenu = ({
|
|||
isPending: isDefinitionsResponsePending,
|
||||
isLoading: isDefinitionsResponseLoading,
|
||||
} = useQuery({
|
||||
queryKey: ["definitions", selectedSymbolInfo.symbolName, selectedSymbolInfo.repoName, selectedSymbolInfo.revisionName, selectedSymbolInfo.language, domain],
|
||||
queryFn: () => unwrapServiceError(
|
||||
findSearchBasedSymbolDefinitions({
|
||||
symbolName: selectedSymbolInfo.symbolName,
|
||||
language: selectedSymbolInfo.language,
|
||||
revisionName: selectedSymbolInfo.revisionName,
|
||||
queryKey: ["definitions", selectedSymbolInfo.symbolName, selectedSymbolInfo.repoName, selectedSymbolInfo.revisionName, selectedSymbolInfo.language, domain, isGlobalSearchEnabled],
|
||||
queryFn: async () => {
|
||||
const response = await measure(() => unwrapServiceError(
|
||||
findSearchBasedSymbolDefinitions({
|
||||
symbolName: selectedSymbolInfo.symbolName,
|
||||
language: selectedSymbolInfo.language,
|
||||
revisionName: selectedSymbolInfo.revisionName,
|
||||
repoName: isGlobalSearchEnabled ? undefined : selectedSymbolInfo.repoName
|
||||
})
|
||||
), 'findSearchBasedSymbolDefinitions', false);
|
||||
|
||||
captureEvent('wa_explore_menu_definitions_loaded', {
|
||||
durationMs: response.durationMs,
|
||||
isGlobalSearchEnabled,
|
||||
})
|
||||
),
|
||||
|
||||
return response.data;
|
||||
}
|
||||
});
|
||||
|
||||
useHotkeys('shift+a', () => {
|
||||
setIsGlobalSearchEnabled(prev => !prev);
|
||||
}, {
|
||||
enableOnFormTags: true,
|
||||
enableOnContentEditable: true,
|
||||
description: "Search all repositories",
|
||||
});
|
||||
|
||||
const isPending = isReferencesResponsePending || isDefinitionsResponsePending;
|
||||
|
|
@ -98,29 +132,52 @@ export const ExploreMenu = ({
|
|||
<ResizablePanel
|
||||
minSize={10}
|
||||
maxSize={20}
|
||||
className="flex flex-col h-full"
|
||||
>
|
||||
<div className="flex flex-col p-2">
|
||||
<Tooltip
|
||||
delayDuration={100}
|
||||
>
|
||||
<TooltipTrigger
|
||||
disabled={true}
|
||||
className="mr-auto"
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
|
||||
<Tooltip
|
||||
delayDuration={100}
|
||||
>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="w-fit h-fit flex-shrink-0 select-none"
|
||||
<TooltipTrigger
|
||||
disabled={true}
|
||||
className="mr-auto"
|
||||
>
|
||||
Search Based
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
side="top"
|
||||
align="start"
|
||||
>
|
||||
Symbol references and definitions found using a best-guess search heuristic.
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="w-fit h-fit flex-shrink-0 select-none"
|
||||
>
|
||||
Search Based
|
||||
</Badge>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
side="top"
|
||||
align="center"
|
||||
>
|
||||
Symbol references and definitions found using a best-guess search heuristic.
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span>
|
||||
<Toggle
|
||||
pressed={isGlobalSearchEnabled}
|
||||
onPressedChange={setIsGlobalSearchEnabled}
|
||||
>
|
||||
<GlobeIcon className="w-4 h-4" />
|
||||
</Toggle>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" align="center">
|
||||
Search all repositories
|
||||
<KeyboardShortcutHint
|
||||
shortcut="⇧ A"
|
||||
className="ml-2"
|
||||
/>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 mt-4">
|
||||
<Entry
|
||||
name="References"
|
||||
|
|
|
|||
|
|
@ -1,42 +1,50 @@
|
|||
import { useBrowseNavigation } from "@/app/[domain]/browse/hooks/useBrowseNavigation";
|
||||
import { KeyboardShortcutHint } from "@/app/components/keyboardShortcutHint";
|
||||
import { useToast } from "@/components/hooks/use-toast";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { LoadingButton } from "@/components/ui/loading-button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { createAuditAction } from "@/ee/features/audit/actions";
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
||||
import { computePosition, flip, offset, shift, VirtualElement } from "@floating-ui/react";
|
||||
import { ReactCodeMirrorRef } from "@uiw/react-codemirror";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { SymbolDefinition, useHoveredOverSymbolInfo } from "./useHoveredOverSymbolInfo";
|
||||
import { SymbolDefinitionPreview } from "./symbolDefinitionPreview";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import { useHotkeys } from "react-hotkeys-hook";
|
||||
import { useToast } from "@/components/hooks/use-toast";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
import { KeyboardShortcutHint } from "@/app/components/keyboardShortcutHint";
|
||||
import { SymbolDefinitionPreview } from "./symbolDefinitionPreview";
|
||||
import { useHoveredOverSymbolInfo } from "./useHoveredOverSymbolInfo";
|
||||
|
||||
interface SymbolHoverPopupProps {
|
||||
editorRef: ReactCodeMirrorRef;
|
||||
language: string;
|
||||
revisionName: string;
|
||||
onFindReferences: (symbolName: string) => void;
|
||||
onGotoDefinition: (symbolName: string, symbolDefinitions: SymbolDefinition[]) => void;
|
||||
repoName: string;
|
||||
fileName: string;
|
||||
source: 'browse' | 'preview' | 'chat';
|
||||
}
|
||||
|
||||
export const SymbolHoverPopup: React.FC<SymbolHoverPopupProps> = ({
|
||||
editorRef,
|
||||
revisionName,
|
||||
language,
|
||||
onFindReferences,
|
||||
onGotoDefinition: _onGotoDefinition,
|
||||
repoName,
|
||||
fileName,
|
||||
source,
|
||||
}) => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const [isSticky, setIsSticky] = useState(false);
|
||||
const { toast } = useToast();
|
||||
const { navigateToPath } = useBrowseNavigation();
|
||||
const captureEvent = useCaptureEvent();
|
||||
|
||||
const symbolInfo = useHoveredOverSymbolInfo({
|
||||
editorRef,
|
||||
isSticky,
|
||||
revisionName,
|
||||
language,
|
||||
repoName,
|
||||
});
|
||||
|
||||
// Positions the popup relative to the symbol
|
||||
|
|
@ -77,13 +85,118 @@ export const SymbolHoverPopup: React.FC<SymbolHoverPopupProps> = ({
|
|||
}
|
||||
}, [symbolInfo, editorRef]);
|
||||
|
||||
// Multiple symbol definitions can exist for the same symbol, but we can only navigate
|
||||
// and display a preview of one. If the symbol definition exists in the current file,
|
||||
// then we use that one, otherwise we fallback to the first definition in the list.
|
||||
const previewedSymbolDefinition = useMemo(() => {
|
||||
if (!symbolInfo?.symbolDefinitions || symbolInfo.symbolDefinitions.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const matchingDefinition = symbolInfo.symbolDefinitions.find(
|
||||
(definition) => (
|
||||
definition.fileName === fileName && definition.repoName === repoName
|
||||
)
|
||||
);
|
||||
|
||||
if (matchingDefinition) {
|
||||
return matchingDefinition;
|
||||
}
|
||||
|
||||
return symbolInfo.symbolDefinitions[0];
|
||||
}, [fileName, repoName, symbolInfo?.symbolDefinitions]);
|
||||
|
||||
const onGotoDefinition = useCallback(() => {
|
||||
if (!symbolInfo || !symbolInfo.symbolDefinitions) {
|
||||
if (
|
||||
!symbolInfo ||
|
||||
!symbolInfo.symbolDefinitions ||
|
||||
!previewedSymbolDefinition
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
_onGotoDefinition(symbolInfo.symbolName, symbolInfo.symbolDefinitions);
|
||||
}, [symbolInfo, _onGotoDefinition]);
|
||||
captureEvent('wa_goto_definition_pressed', {
|
||||
source,
|
||||
});
|
||||
|
||||
createAuditAction({
|
||||
action: "user.performed_goto_definition",
|
||||
metadata: {
|
||||
message: symbolInfo.symbolName,
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
fileName,
|
||||
repoName,
|
||||
revisionName,
|
||||
language,
|
||||
range: highlightRange,
|
||||
} = previewedSymbolDefinition;
|
||||
|
||||
navigateToPath({
|
||||
// Always navigate to the preview symbol definition.
|
||||
repoName,
|
||||
revisionName,
|
||||
path: fileName,
|
||||
pathType: 'blob',
|
||||
highlightRange,
|
||||
// If there are multiple definitions, we should open the Explore panel with the definitions.
|
||||
...(symbolInfo.symbolDefinitions.length > 1 ? {
|
||||
setBrowseState: {
|
||||
selectedSymbolInfo: {
|
||||
symbolName: symbolInfo.symbolName,
|
||||
repoName,
|
||||
revisionName,
|
||||
language,
|
||||
},
|
||||
activeExploreMenuTab: "definitions",
|
||||
isBottomPanelCollapsed: false,
|
||||
}
|
||||
} : {}),
|
||||
});
|
||||
}, [
|
||||
captureEvent,
|
||||
previewedSymbolDefinition,
|
||||
navigateToPath,
|
||||
source,
|
||||
symbolInfo
|
||||
]);
|
||||
|
||||
const onFindReferences = useCallback(() => {
|
||||
if (!symbolInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
captureEvent('wa_find_references_pressed', {
|
||||
source,
|
||||
});
|
||||
|
||||
createAuditAction({
|
||||
action: "user.performed_find_references",
|
||||
metadata: {
|
||||
message: symbolInfo.symbolName,
|
||||
},
|
||||
})
|
||||
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName,
|
||||
path: fileName,
|
||||
pathType: 'blob',
|
||||
highlightRange: symbolInfo.range,
|
||||
setBrowseState: {
|
||||
selectedSymbolInfo: {
|
||||
symbolName: symbolInfo.symbolName,
|
||||
repoName,
|
||||
revisionName,
|
||||
language,
|
||||
},
|
||||
activeExploreMenuTab: "references",
|
||||
isBottomPanelCollapsed: false,
|
||||
}
|
||||
})
|
||||
}, [captureEvent, fileName, language, navigateToPath, repoName, revisionName, source, symbolInfo]);
|
||||
|
||||
// @todo: We should probably make the behaviour s.t., the ctrl / cmd key needs to be held
|
||||
// down to navigate to the definition. We should also only show the underline when the key
|
||||
|
|
@ -100,9 +213,7 @@ export const SymbolHoverPopup: React.FC<SymbolHoverPopupProps> = ({
|
|||
}, [symbolInfo, onGotoDefinition]);
|
||||
|
||||
useHotkeys('alt+shift+f12', () => {
|
||||
if (symbolInfo?.symbolName) {
|
||||
onFindReferences(symbolInfo.symbolName);
|
||||
}
|
||||
onFindReferences();
|
||||
}, {
|
||||
enableOnFormTags: true,
|
||||
enableOnContentEditable: true,
|
||||
|
|
@ -147,9 +258,9 @@ export const SymbolHoverPopup: React.FC<SymbolHoverPopupProps> = ({
|
|||
<Loader2 className="w-4 h-4 animate-spin" />
|
||||
Loading...
|
||||
</div>
|
||||
) : symbolInfo.symbolDefinitions && symbolInfo.symbolDefinitions.length > 0 ? (
|
||||
) : previewedSymbolDefinition ? (
|
||||
<SymbolDefinitionPreview
|
||||
symbolDefinition={symbolInfo.symbolDefinitions[0]}
|
||||
symbolDefinition={previewedSymbolDefinition}
|
||||
/>
|
||||
) : (
|
||||
<p className="text-sm font-medium text-muted-foreground">No hover info found</p>
|
||||
|
|
@ -160,13 +271,13 @@ export const SymbolHoverPopup: React.FC<SymbolHoverPopupProps> = ({
|
|||
<TooltipTrigger asChild>
|
||||
<LoadingButton
|
||||
loading={symbolInfo.isSymbolDefinitionsLoading}
|
||||
disabled={!symbolInfo.symbolDefinitions || symbolInfo.symbolDefinitions.length === 0}
|
||||
disabled={!previewedSymbolDefinition}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={onGotoDefinition}
|
||||
>
|
||||
{
|
||||
!symbolInfo.isSymbolDefinitionsLoading && (!symbolInfo.symbolDefinitions || symbolInfo.symbolDefinitions.length === 0) ?
|
||||
!symbolInfo.isSymbolDefinitionsLoading && !previewedSymbolDefinition ?
|
||||
"No definition found" :
|
||||
`Go to ${symbolInfo.symbolDefinitions && symbolInfo.symbolDefinitions.length > 1 ? "definitions" : "definition"}`
|
||||
}
|
||||
|
|
@ -186,7 +297,7 @@ export const SymbolHoverPopup: React.FC<SymbolHoverPopupProps> = ({
|
|||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => onFindReferences(symbolInfo.symbolName)}
|
||||
onClick={onFindReferences}
|
||||
>
|
||||
Find references
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import { findSearchBasedSymbolDefinitions } from "@/app/api/(client)/client";
|
||||
import { SourceRange } from "@/features/search";
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
||||
import { useDomain } from "@/hooks/useDomain";
|
||||
import { unwrapServiceError } from "@/lib/utils";
|
||||
import { measure, unwrapServiceError } from "@/lib/utils";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { ReactCodeMirrorRef } from "@uiw/react-codemirror";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
|
|
@ -12,6 +13,7 @@ interface UseHoveredOverSymbolInfoProps {
|
|||
isSticky: boolean;
|
||||
revisionName: string;
|
||||
language: string;
|
||||
repoName: string;
|
||||
}
|
||||
|
||||
export type SymbolDefinition = {
|
||||
|
|
@ -19,12 +21,14 @@ export type SymbolDefinition = {
|
|||
language: string;
|
||||
fileName: string;
|
||||
repoName: string;
|
||||
revisionName: string;
|
||||
range: SourceRange;
|
||||
}
|
||||
|
||||
interface HoveredOverSymbolInfo {
|
||||
element: HTMLElement;
|
||||
symbolName: string;
|
||||
range: SourceRange;
|
||||
isSymbolDefinitionsLoading: boolean;
|
||||
symbolDefinitions?: SymbolDefinition[];
|
||||
}
|
||||
|
|
@ -37,6 +41,7 @@ export const useHoveredOverSymbolInfo = ({
|
|||
isSticky,
|
||||
revisionName,
|
||||
language,
|
||||
repoName,
|
||||
}: UseHoveredOverSymbolInfoProps): HoveredOverSymbolInfo | undefined => {
|
||||
const mouseOverTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
const mouseOutTimerRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
|
@ -49,15 +54,26 @@ export const useHoveredOverSymbolInfo = ({
|
|||
return (symbolElement && symbolElement.textContent) ?? undefined;
|
||||
}, [symbolElement]);
|
||||
|
||||
const captureEvent = useCaptureEvent();
|
||||
|
||||
const { data: symbolDefinitions, isLoading: isSymbolDefinitionsLoading } = useQuery({
|
||||
queryKey: ["definitions", symbolName, revisionName, language, domain],
|
||||
queryFn: () => unwrapServiceError(
|
||||
findSearchBasedSymbolDefinitions({
|
||||
symbolName: symbolName!,
|
||||
language,
|
||||
revisionName,
|
||||
})
|
||||
),
|
||||
queryKey: ["definitions", symbolName, revisionName, language, domain, repoName],
|
||||
queryFn: async () => {
|
||||
const response = await measure(() => unwrapServiceError(
|
||||
findSearchBasedSymbolDefinitions({
|
||||
symbolName: symbolName!,
|
||||
language,
|
||||
revisionName,
|
||||
repoName,
|
||||
})
|
||||
), 'findSearchBasedSymbolDefinitions', false);
|
||||
|
||||
captureEvent('wa_symbol_hover_popup_definitions_loaded', {
|
||||
durationMs: response.durationMs,
|
||||
});
|
||||
|
||||
return response.data;
|
||||
},
|
||||
select: ((data) => {
|
||||
return data.files.flatMap((file) => {
|
||||
return file.matches.map((match) => {
|
||||
|
|
@ -66,6 +82,7 @@ export const useHoveredOverSymbolInfo = ({
|
|||
language: file.language,
|
||||
fileName: file.fileName,
|
||||
repoName: file.repository,
|
||||
revisionName: revisionName,
|
||||
range: match.range,
|
||||
}
|
||||
})
|
||||
|
|
@ -107,7 +124,7 @@ export const useHoveredOverSymbolInfo = ({
|
|||
|
||||
const handleMouseOut = () => {
|
||||
clearTimers();
|
||||
|
||||
|
||||
mouseOutTimerRef.current = setTimeout(() => {
|
||||
setIsVisible(false);
|
||||
}, SYMBOL_HOVER_POPUP_MOUSE_OUT_TIMEOUT_MS);
|
||||
|
|
@ -122,17 +139,64 @@ export const useHoveredOverSymbolInfo = ({
|
|||
};
|
||||
}, [editorRef, domain, clearTimers]);
|
||||
|
||||
// Extract the highlight range of the symbolElement from the editor view.
|
||||
const highlightRange = useMemo((): SourceRange | undefined => {
|
||||
if (!symbolElement || !editorRef.view) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const view = editorRef.view;
|
||||
const rect = symbolElement.getBoundingClientRect();
|
||||
|
||||
// Get the start position (left edge, middle vertically)
|
||||
const startPos = view.posAtCoords({
|
||||
x: rect.left,
|
||||
y: rect.top + rect.height / 2,
|
||||
});
|
||||
|
||||
// Get the end position (right edge, middle vertically)
|
||||
const endPos = view.posAtCoords({
|
||||
x: rect.right,
|
||||
y: rect.top + rect.height / 2,
|
||||
});
|
||||
|
||||
if (startPos === null || endPos === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Convert CodeMirror positions to SourceRange format
|
||||
const startLine = view.state.doc.lineAt(startPos);
|
||||
const endLine = view.state.doc.lineAt(endPos);
|
||||
|
||||
const startColumn = startPos - startLine.from + 1; // 1-based column
|
||||
const endColumn = endPos - endLine.from + 1; // 1-based column
|
||||
|
||||
return {
|
||||
start: {
|
||||
byteOffset: startPos, // 0-based byte offset
|
||||
lineNumber: startLine.number, // 1-based line number
|
||||
column: startColumn, // 1-based column
|
||||
},
|
||||
end: {
|
||||
byteOffset: endPos, // 0-based byte offset
|
||||
lineNumber: endLine.number, // 1-based line number
|
||||
column: endColumn, // 1-based column
|
||||
},
|
||||
};
|
||||
}, [symbolElement, editorRef.view]);
|
||||
|
||||
if (!isVisible && !isSticky) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!symbolElement || !symbolName) {
|
||||
if (!symbolElement || !symbolName || !highlightRange) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
element: symbolElement,
|
||||
symbolName,
|
||||
range: highlightRange,
|
||||
isSymbolDefinitionsLoading: isSymbolDefinitionsLoading,
|
||||
symbolDefinitions,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ export const useSuggestionsData = ({
|
|||
query: query.join(' '),
|
||||
matches: 10,
|
||||
contextLines: 1,
|
||||
source: 'chat-file-suggestions'
|
||||
}))
|
||||
},
|
||||
select: (data): FileSuggestion[] => {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,8 @@
|
|||
'use client';
|
||||
|
||||
import { useBrowseNavigation } from "@/app/[domain]/browse/hooks/useBrowseNavigation";
|
||||
import { PathHeader } from "@/app/[domain]/components/pathHeader";
|
||||
import { SymbolHoverPopup } from '@/ee/features/codeNav/components/symbolHoverPopup';
|
||||
import { symbolHoverTargetsExtension } from "@/ee/features/codeNav/components/symbolHoverPopup/symbolHoverTargetsExtension";
|
||||
import { SymbolDefinition } from '@/ee/features/codeNav/components/symbolHoverPopup/useHoveredOverSymbolInfo';
|
||||
import { useHasEntitlement } from "@/features/entitlements/useHasEntitlement";
|
||||
import { useCodeMirrorLanguageExtension } from "@/hooks/useCodeMirrorLanguageExtension";
|
||||
import { useCodeMirrorTheme } from "@/hooks/useCodeMirrorTheme";
|
||||
|
|
@ -12,15 +10,13 @@ import { useKeymapExtension } from "@/hooks/useKeymapExtension";
|
|||
import { cn } from "@/lib/utils";
|
||||
import { Range } from "@codemirror/state";
|
||||
import { Decoration, DecorationSet, EditorView } from '@codemirror/view';
|
||||
import { CodeHostType } from "@sourcebot/db";
|
||||
import CodeMirror, { ReactCodeMirrorRef, StateField } from '@uiw/react-codemirror';
|
||||
import isEqual from "fast-deep-equal/react";
|
||||
import { ChevronDown, ChevronRight } from "lucide-react";
|
||||
import { forwardRef, memo, Ref, useCallback, useImperativeHandle, useMemo, useState } from "react";
|
||||
import { FileReference } from "../../types";
|
||||
import { createCodeFoldingExtension } from "./codeFoldingExtension";
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
||||
import { CodeHostType } from "@sourcebot/db";
|
||||
import { createAuditAction } from "@/ee/features/audit/actions";
|
||||
import isEqual from "fast-deep-equal/react";
|
||||
|
||||
const lineDecoration = Decoration.line({
|
||||
attributes: { class: "cm-range-border-radius chat-lineHighlight" },
|
||||
|
|
@ -74,7 +70,6 @@ const ReferencedFileSourceListItem = ({
|
|||
}: ReferencedFileSourceListItemProps, forwardedRef: Ref<ReactCodeMirrorRef>) => {
|
||||
const theme = useCodeMirrorTheme();
|
||||
const [editorRef, setEditorRef] = useState<ReactCodeMirrorRef | null>(null);
|
||||
const captureEvent = useCaptureEvent();
|
||||
|
||||
useImperativeHandle(
|
||||
forwardedRef,
|
||||
|
|
@ -84,7 +79,6 @@ const ReferencedFileSourceListItem = ({
|
|||
const hasCodeNavEntitlement = useHasEntitlement("code-nav");
|
||||
|
||||
const languageExtension = useCodeMirrorLanguageExtension(language, editorRef?.view);
|
||||
const { navigateToPath } = useBrowseNavigation();
|
||||
|
||||
const getReferenceAtPos = useCallback((x: number, y: number, view: EditorView): FileReference | undefined => {
|
||||
const pos = view.posAtCoords({ x, y });
|
||||
|
|
@ -217,83 +211,6 @@ const ReferencedFileSourceListItem = ({
|
|||
codeFoldingExtension,
|
||||
]);
|
||||
|
||||
const onGotoDefinition = useCallback((symbolName: string, symbolDefinitions: SymbolDefinition[]) => {
|
||||
if (symbolDefinitions.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
captureEvent('wa_goto_definition_pressed', {
|
||||
source: 'chat',
|
||||
});
|
||||
createAuditAction({
|
||||
action: "user.performed_goto_definition",
|
||||
metadata: {
|
||||
message: symbolName,
|
||||
},
|
||||
});
|
||||
|
||||
if (symbolDefinitions.length === 1) {
|
||||
const symbolDefinition = symbolDefinitions[0];
|
||||
const { fileName, repoName } = symbolDefinition;
|
||||
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName: revision,
|
||||
path: fileName,
|
||||
pathType: 'blob',
|
||||
highlightRange: symbolDefinition.range,
|
||||
})
|
||||
} else {
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName: revision,
|
||||
path: fileName,
|
||||
pathType: 'blob',
|
||||
setBrowseState: {
|
||||
selectedSymbolInfo: {
|
||||
symbolName,
|
||||
repoName,
|
||||
revisionName: revision,
|
||||
language: language,
|
||||
},
|
||||
activeExploreMenuTab: "definitions",
|
||||
isBottomPanelCollapsed: false,
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}, [captureEvent, navigateToPath, revision, repoName, fileName, language]);
|
||||
|
||||
const onFindReferences = useCallback((symbolName: string) => {
|
||||
captureEvent('wa_find_references_pressed', {
|
||||
source: 'chat',
|
||||
});
|
||||
createAuditAction({
|
||||
action: "user.performed_find_references",
|
||||
metadata: {
|
||||
message: symbolName,
|
||||
},
|
||||
});
|
||||
|
||||
navigateToPath({
|
||||
repoName,
|
||||
revisionName: revision,
|
||||
path: fileName,
|
||||
pathType: 'blob',
|
||||
setBrowseState: {
|
||||
selectedSymbolInfo: {
|
||||
symbolName,
|
||||
repoName,
|
||||
revisionName: revision,
|
||||
language: language,
|
||||
},
|
||||
activeExploreMenuTab: "references",
|
||||
isBottomPanelCollapsed: false,
|
||||
}
|
||||
})
|
||||
|
||||
}, [captureEvent, fileName, language, navigateToPath, repoName, revision]);
|
||||
|
||||
const ExpandCollapseIcon = useMemo(() => {
|
||||
return isExpanded ? ChevronDown : ChevronRight;
|
||||
}, [isExpanded]);
|
||||
|
|
@ -341,11 +258,12 @@ const ReferencedFileSourceListItem = ({
|
|||
>
|
||||
{editorRef && hasCodeNavEntitlement && (
|
||||
<SymbolHoverPopup
|
||||
source="chat"
|
||||
editorRef={editorRef}
|
||||
revisionName={revision}
|
||||
language={language}
|
||||
onFindReferences={onFindReferences}
|
||||
onGotoDefinition={onGotoDefinition}
|
||||
repoName={repoName}
|
||||
fileName={fileName}
|
||||
/>
|
||||
)}
|
||||
</CodeMirror>
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ export const FileListItem = ({
|
|||
pathType: 'blob',
|
||||
})}
|
||||
>
|
||||
{path}
|
||||
<span>{path}</span>
|
||||
</Link>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -26,8 +26,9 @@ export const findSymbolReferencesTool = tool({
|
|||
inputSchema: z.object({
|
||||
symbol: z.string().describe("The symbol to find references to"),
|
||||
language: z.string().describe("The programming language of the symbol"),
|
||||
repository: z.string().describe("The repository to scope the search to").optional(),
|
||||
}),
|
||||
execute: async ({ symbol, language }) => {
|
||||
execute: async ({ symbol, language, repository }) => {
|
||||
// @todo: make revision configurable.
|
||||
const revision = "HEAD";
|
||||
|
||||
|
|
@ -35,6 +36,7 @@ export const findSymbolReferencesTool = tool({
|
|||
symbolName: symbol,
|
||||
language,
|
||||
revisionName: "HEAD",
|
||||
repoName: repository,
|
||||
});
|
||||
|
||||
if (isServiceError(response)) {
|
||||
|
|
@ -63,8 +65,9 @@ export const findSymbolDefinitionsTool = tool({
|
|||
inputSchema: z.object({
|
||||
symbol: z.string().describe("The symbol to find definitions of"),
|
||||
language: z.string().describe("The programming language of the symbol"),
|
||||
repository: z.string().describe("The repository to scope the search to").optional(),
|
||||
}),
|
||||
execute: async ({ symbol, language }) => {
|
||||
execute: async ({ symbol, language, repository }) => {
|
||||
// @todo: make revision configurable.
|
||||
const revision = "HEAD";
|
||||
|
||||
|
|
@ -72,6 +75,7 @@ export const findSymbolDefinitionsTool = tool({
|
|||
symbolName: symbol,
|
||||
language,
|
||||
revisionName: revision,
|
||||
repoName: repository,
|
||||
});
|
||||
|
||||
if (isServiceError(response)) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { withOptionalAuthV2 } from "@/withAuthV2";
|
|||
import { SearchResponse } from "../search/types";
|
||||
import { FindRelatedSymbolsRequest, FindRelatedSymbolsResponse } from "./types";
|
||||
import { QueryIR } from '../search/ir';
|
||||
import escapeStringRegexp from "escape-string-regexp";
|
||||
|
||||
// The maximum number of matches to return from the search API.
|
||||
const MAX_REFERENCE_COUNT = 1000;
|
||||
|
|
@ -18,6 +19,7 @@ export const findSearchBasedSymbolReferences = async (props: FindRelatedSymbolsR
|
|||
symbolName,
|
||||
language,
|
||||
revisionName = "HEAD",
|
||||
repoName,
|
||||
} = props;
|
||||
|
||||
const languageFilter = getExpandedLanguageFilter(language);
|
||||
|
|
@ -40,6 +42,11 @@ export const findSearchBasedSymbolReferences = async (props: FindRelatedSymbolsR
|
|||
}
|
||||
},
|
||||
languageFilter,
|
||||
...(repoName ? [{
|
||||
repo: {
|
||||
regexp: `^${escapeStringRegexp(repoName)}$`,
|
||||
}
|
||||
}]: [])
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -67,6 +74,7 @@ export const findSearchBasedSymbolDefinitions = async (props: FindRelatedSymbols
|
|||
symbolName,
|
||||
language,
|
||||
revisionName = "HEAD",
|
||||
repoName
|
||||
} = props;
|
||||
|
||||
const languageFilter = getExpandedLanguageFilter(language);
|
||||
|
|
@ -93,6 +101,11 @@ export const findSearchBasedSymbolDefinitions = async (props: FindRelatedSymbols
|
|||
}
|
||||
},
|
||||
languageFilter,
|
||||
...(repoName ? [{
|
||||
repo: {
|
||||
regexp: `^${escapeStringRegexp(repoName)}$`,
|
||||
}
|
||||
}]: [])
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,16 @@ import { rangeSchema, repositoryInfoSchema } from "../search/types";
|
|||
export const findRelatedSymbolsRequestSchema = z.object({
|
||||
symbolName: z.string(),
|
||||
language: z.string(),
|
||||
/**
|
||||
* Optional revision name to scope search to.
|
||||
* If not provided, the search will be scoped to HEAD.
|
||||
*/
|
||||
revisionName: z.string().optional(),
|
||||
/**
|
||||
* Optional repository name to scope search to.
|
||||
* If not provided, the search will be across all repositories.
|
||||
*/
|
||||
repoName: z.string().optional(),
|
||||
});
|
||||
export type FindRelatedSymbolsRequest = z.infer<typeof findRelatedSymbolsRequestSchema>;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { search } from "./searchApi";
|
|||
import { sew } from "@/actions";
|
||||
import { withOptionalAuthV2 } from "@/withAuthV2";
|
||||
import { QueryIR } from './ir';
|
||||
import escapeStringRegexp from "escape-string-regexp";
|
||||
|
||||
// @todo (bkellam) #574 : We should really be using `git show <hash>:<path>` to fetch file contents here.
|
||||
// This will allow us to support permalinks to files at a specific revision that may not be indexed
|
||||
|
|
@ -18,7 +19,7 @@ export const getFileSource = async ({ fileName, repository, branch }: FileSource
|
|||
children: [
|
||||
{
|
||||
repo: {
|
||||
regexp: `^${repository}$`,
|
||||
regexp: `^${escapeStringRegexp(repository)}$`,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,13 +2,12 @@ import { sew } from "@/actions";
|
|||
import { getRepoPermissionFilterForUser } from "@/prisma";
|
||||
import { withOptionalAuthV2 } from "@/withAuthV2";
|
||||
import { PrismaClient, UserWithAccounts } from "@sourcebot/db";
|
||||
import { createLogger, env, hasEntitlement } from "@sourcebot/shared";
|
||||
import { env, hasEntitlement } from "@sourcebot/shared";
|
||||
import { QueryIR } from './ir';
|
||||
import { parseQuerySyntaxIntoIR } from './parser';
|
||||
import { SearchOptions } from "./types";
|
||||
import { createZoektSearchRequest, zoektSearch, zoektStreamSearch } from './zoektSearcher';
|
||||
|
||||
const logger = createLogger("searchApi");
|
||||
|
||||
type QueryStringSearchRequest = {
|
||||
queryType: 'string';
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ export type SearchOptions = z.infer<typeof searchOptionsSchema>;
|
|||
|
||||
export const searchRequestSchema = z.object({
|
||||
query: z.string(), // The zoekt query to execute.
|
||||
source: z.string().optional(), // The source of the search request.
|
||||
...searchOptionsSchema.shape,
|
||||
});
|
||||
export type SearchRequest = z.infer<typeof searchRequestSchema>;
|
||||
|
|
|
|||
69
packages/web/src/lib/posthog.ts
Normal file
69
packages/web/src/lib/posthog.ts
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
import { PostHog } from 'posthog-node'
|
||||
import { env } from '@sourcebot/shared'
|
||||
import { RequestCookies } from 'next/dist/compiled/@edge-runtime/cookies';
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import { PosthogEvent, PosthogEventMap } from './posthogEvents';
|
||||
import { cookies } from 'next/headers';
|
||||
|
||||
/**
|
||||
* @note: This is a subset of the properties stored in the
|
||||
* ph_phc_<id>_posthog cookie.
|
||||
*/
|
||||
export type PostHogCookie = {
|
||||
distinct_id: string;
|
||||
}
|
||||
|
||||
const isPostHogCookie = (cookie: unknown): cookie is PostHogCookie => {
|
||||
return typeof cookie === 'object' &&
|
||||
cookie !== null &&
|
||||
'distinct_id' in cookie;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to retrieve the PostHog cookie from the given cookie store, returning
|
||||
* undefined if the cookie is not found or is invalid.
|
||||
*/
|
||||
const getPostHogCookie = (cookieStore: Pick<RequestCookies, 'get'>): PostHogCookie | undefined => {
|
||||
const phCookieKey = `ph_${env.POSTHOG_PAPIK}_posthog`;
|
||||
const cookie = cookieStore.get(phCookieKey);
|
||||
|
||||
if (!cookie) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const parsedCookie = (() => {
|
||||
try {
|
||||
return JSON.parse(cookie.value);
|
||||
} catch (e) {
|
||||
Sentry.captureException(e);
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
if (isPostHogCookie(parsedCookie)) {
|
||||
return parsedCookie;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export async function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E]) {
|
||||
if (env.SOURCEBOT_TELEMETRY_DISABLED === 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
const cookieStore = await cookies();
|
||||
const cookie = getPostHogCookie(cookieStore);
|
||||
|
||||
const posthog = new PostHog(env.POSTHOG_PAPIK, {
|
||||
host: 'https://us.i.posthog.com',
|
||||
flushAt: 1,
|
||||
flushInterval: 0
|
||||
})
|
||||
|
||||
posthog.capture({
|
||||
event,
|
||||
properties,
|
||||
distinctId: cookie?.distinct_id ?? '',
|
||||
});
|
||||
}
|
||||
|
|
@ -273,15 +273,6 @@ export type PosthogEventMap = {
|
|||
wa_api_key_created: {},
|
||||
wa_api_key_creation_fail: {},
|
||||
//////////////////////////////////////////////////////////////////
|
||||
wa_goto_definition_pressed: {
|
||||
source: 'chat' | 'browse' | 'preview',
|
||||
},
|
||||
wa_find_references_pressed: {
|
||||
source: 'chat' | 'browse' | 'preview',
|
||||
},
|
||||
//////////////////////////////////////////////////////////////////
|
||||
wa_explore_menu_reference_clicked: {},
|
||||
//////////////////////////////////////////////////////////////////
|
||||
wa_chat_feedback_submitted: {
|
||||
feedback: 'like' | 'dislike',
|
||||
chatId: string,
|
||||
|
|
@ -302,5 +293,31 @@ export type PosthogEventMap = {
|
|||
//////////////////////////////////////////////////////////////////
|
||||
wa_github_star_toast_displayed: {},
|
||||
wa_github_star_toast_clicked: {},
|
||||
//////////////////////////////////////////////////////////////////
|
||||
wa_goto_definition_pressed: {
|
||||
source: 'chat' | 'browse' | 'preview',
|
||||
},
|
||||
wa_find_references_pressed: {
|
||||
source: 'chat' | 'browse' | 'preview',
|
||||
},
|
||||
wa_symbol_hover_popup_definitions_loaded: {
|
||||
durationMs: number,
|
||||
},
|
||||
wa_explore_menu_reference_clicked: {},
|
||||
wa_explore_menu_references_loaded: {
|
||||
durationMs: number,
|
||||
// Whether or not the user is searching all repositories.
|
||||
isGlobalSearchEnabled: boolean,
|
||||
},
|
||||
wa_explore_menu_definitions_loaded: {
|
||||
durationMs: number,
|
||||
// Whether or not the user is searching all repositories.
|
||||
isGlobalSearchEnabled: boolean,
|
||||
},
|
||||
//////////////////////////////////////////////////////////////////
|
||||
api_code_search_request: {
|
||||
source: string;
|
||||
type: 'streamed' | 'blocking';
|
||||
},
|
||||
}
|
||||
export type PosthogEvent = keyof PosthogEventMap;
|
||||
|
|
@ -35,7 +35,8 @@
|
|||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
".next/types/**/*.ts",
|
||||
".next/dev/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
|
|
|
|||
421
yarn.lock
421
yarn.lock
|
|
@ -1649,12 +1649,12 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@emnapi/runtime@npm:^1.4.4":
|
||||
version: 1.4.5
|
||||
resolution: "@emnapi/runtime@npm:1.4.5"
|
||||
"@emnapi/runtime@npm:^1.7.0":
|
||||
version: 1.7.1
|
||||
resolution: "@emnapi/runtime@npm:1.7.1"
|
||||
dependencies:
|
||||
tslib: "npm:^2.4.0"
|
||||
checksum: 10c0/37a0278be5ac81e918efe36f1449875cbafba947039c53c65a1f8fc238001b866446fc66041513b286baaff5d6f9bec667f5164b3ca481373a8d9cb65bfc984b
|
||||
checksum: 10c0/26b851cd3e93877d8732a985a2ebf5152325bbacc6204ef5336a47359dedcc23faeb08cdfcb8bb389b5401b3e894b882bc1a1e55b4b7c1ed1e67c991a760ddd5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -2426,6 +2426,13 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/colour@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "@img/colour@npm:1.0.0"
|
||||
checksum: 10c0/02261719c1e0d7aa5a2d585981954f2ac126f0c432400aa1a01b925aa2c41417b7695da8544ee04fd29eba7ecea8eaf9b8bef06f19dc8faba78f94eeac40667d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-darwin-arm64@npm:0.33.5":
|
||||
version: 0.33.5
|
||||
resolution: "@img/sharp-darwin-arm64@npm:0.33.5"
|
||||
|
|
@ -2438,11 +2445,11 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-darwin-arm64@npm:0.34.3":
|
||||
version: 0.34.3
|
||||
resolution: "@img/sharp-darwin-arm64@npm:0.34.3"
|
||||
"@img/sharp-darwin-arm64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-darwin-arm64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-darwin-arm64": "npm:1.2.0"
|
||||
"@img/sharp-libvips-darwin-arm64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-darwin-arm64":
|
||||
optional: true
|
||||
|
|
@ -2462,11 +2469,11 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-darwin-x64@npm:0.34.3":
|
||||
version: 0.34.3
|
||||
resolution: "@img/sharp-darwin-x64@npm:0.34.3"
|
||||
"@img/sharp-darwin-x64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-darwin-x64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-darwin-x64": "npm:1.2.0"
|
||||
"@img/sharp-libvips-darwin-x64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-darwin-x64":
|
||||
optional: true
|
||||
|
|
@ -2481,9 +2488,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-darwin-arm64@npm:1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "@img/sharp-libvips-darwin-arm64@npm:1.2.0"
|
||||
"@img/sharp-libvips-darwin-arm64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-darwin-arm64@npm:1.2.4"
|
||||
conditions: os=darwin & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -2495,9 +2502,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-darwin-x64@npm:1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "@img/sharp-libvips-darwin-x64@npm:1.2.0"
|
||||
"@img/sharp-libvips-darwin-x64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-darwin-x64@npm:1.2.4"
|
||||
conditions: os=darwin & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -2509,9 +2516,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linux-arm64@npm:1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "@img/sharp-libvips-linux-arm64@npm:1.2.0"
|
||||
"@img/sharp-libvips-linux-arm64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linux-arm64@npm:1.2.4"
|
||||
conditions: os=linux & cpu=arm64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -2523,20 +2530,27 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linux-arm@npm:1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "@img/sharp-libvips-linux-arm@npm:1.2.0"
|
||||
"@img/sharp-libvips-linux-arm@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linux-arm@npm:1.2.4"
|
||||
conditions: os=linux & cpu=arm & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linux-ppc64@npm:1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "@img/sharp-libvips-linux-ppc64@npm:1.2.0"
|
||||
"@img/sharp-libvips-linux-ppc64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linux-ppc64@npm:1.2.4"
|
||||
conditions: os=linux & cpu=ppc64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linux-riscv64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linux-riscv64@npm:1.2.4"
|
||||
conditions: os=linux & cpu=riscv64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linux-s390x@npm:1.0.4":
|
||||
version: 1.0.4
|
||||
resolution: "@img/sharp-libvips-linux-s390x@npm:1.0.4"
|
||||
|
|
@ -2544,9 +2558,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linux-s390x@npm:1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "@img/sharp-libvips-linux-s390x@npm:1.2.0"
|
||||
"@img/sharp-libvips-linux-s390x@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linux-s390x@npm:1.2.4"
|
||||
conditions: os=linux & cpu=s390x & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -2558,9 +2572,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linux-x64@npm:1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "@img/sharp-libvips-linux-x64@npm:1.2.0"
|
||||
"@img/sharp-libvips-linux-x64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linux-x64@npm:1.2.4"
|
||||
conditions: os=linux & cpu=x64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -2572,9 +2586,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-arm64@npm:1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.2.0"
|
||||
"@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.2.4"
|
||||
conditions: os=linux & cpu=arm64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -2586,9 +2600,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-libvips-linuxmusl-x64@npm:1.2.0":
|
||||
version: 1.2.0
|
||||
resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.2.0"
|
||||
"@img/sharp-libvips-linuxmusl-x64@npm:1.2.4":
|
||||
version: 1.2.4
|
||||
resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.2.4"
|
||||
conditions: os=linux & cpu=x64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -2605,11 +2619,11 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linux-arm64@npm:0.34.3":
|
||||
version: 0.34.3
|
||||
resolution: "@img/sharp-linux-arm64@npm:0.34.3"
|
||||
"@img/sharp-linux-arm64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linux-arm64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linux-arm64": "npm:1.2.0"
|
||||
"@img/sharp-libvips-linux-arm64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linux-arm64":
|
||||
optional: true
|
||||
|
|
@ -2629,11 +2643,11 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linux-arm@npm:0.34.3":
|
||||
version: 0.34.3
|
||||
resolution: "@img/sharp-linux-arm@npm:0.34.3"
|
||||
"@img/sharp-linux-arm@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linux-arm@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linux-arm": "npm:1.2.0"
|
||||
"@img/sharp-libvips-linux-arm": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linux-arm":
|
||||
optional: true
|
||||
|
|
@ -2641,11 +2655,11 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linux-ppc64@npm:0.34.3":
|
||||
version: 0.34.3
|
||||
resolution: "@img/sharp-linux-ppc64@npm:0.34.3"
|
||||
"@img/sharp-linux-ppc64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linux-ppc64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linux-ppc64": "npm:1.2.0"
|
||||
"@img/sharp-libvips-linux-ppc64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linux-ppc64":
|
||||
optional: true
|
||||
|
|
@ -2653,6 +2667,18 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linux-riscv64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linux-riscv64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linux-riscv64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linux-riscv64":
|
||||
optional: true
|
||||
conditions: os=linux & cpu=riscv64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linux-s390x@npm:0.33.5":
|
||||
version: 0.33.5
|
||||
resolution: "@img/sharp-linux-s390x@npm:0.33.5"
|
||||
|
|
@ -2665,11 +2691,11 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linux-s390x@npm:0.34.3":
|
||||
version: 0.34.3
|
||||
resolution: "@img/sharp-linux-s390x@npm:0.34.3"
|
||||
"@img/sharp-linux-s390x@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linux-s390x@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linux-s390x": "npm:1.2.0"
|
||||
"@img/sharp-libvips-linux-s390x": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linux-s390x":
|
||||
optional: true
|
||||
|
|
@ -2689,11 +2715,11 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linux-x64@npm:0.34.3":
|
||||
version: 0.34.3
|
||||
resolution: "@img/sharp-linux-x64@npm:0.34.3"
|
||||
"@img/sharp-linux-x64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linux-x64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linux-x64": "npm:1.2.0"
|
||||
"@img/sharp-libvips-linux-x64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linux-x64":
|
||||
optional: true
|
||||
|
|
@ -2713,11 +2739,11 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linuxmusl-arm64@npm:0.34.3":
|
||||
version: 0.34.3
|
||||
resolution: "@img/sharp-linuxmusl-arm64@npm:0.34.3"
|
||||
"@img/sharp-linuxmusl-arm64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linuxmusl-arm64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.0"
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linuxmusl-arm64":
|
||||
optional: true
|
||||
|
|
@ -2737,11 +2763,11 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-linuxmusl-x64@npm:0.34.3":
|
||||
version: 0.34.3
|
||||
resolution: "@img/sharp-linuxmusl-x64@npm:0.34.3"
|
||||
"@img/sharp-linuxmusl-x64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-linuxmusl-x64@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-libvips-linuxmusl-x64": "npm:1.2.0"
|
||||
"@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-libvips-linuxmusl-x64":
|
||||
optional: true
|
||||
|
|
@ -2758,18 +2784,18 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-wasm32@npm:0.34.3":
|
||||
version: 0.34.3
|
||||
resolution: "@img/sharp-wasm32@npm:0.34.3"
|
||||
"@img/sharp-wasm32@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-wasm32@npm:0.34.5"
|
||||
dependencies:
|
||||
"@emnapi/runtime": "npm:^1.4.4"
|
||||
"@emnapi/runtime": "npm:^1.7.0"
|
||||
conditions: cpu=wasm32
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-win32-arm64@npm:0.34.3":
|
||||
version: 0.34.3
|
||||
resolution: "@img/sharp-win32-arm64@npm:0.34.3"
|
||||
"@img/sharp-win32-arm64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-win32-arm64@npm:0.34.5"
|
||||
conditions: os=win32 & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -2781,9 +2807,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-win32-ia32@npm:0.34.3":
|
||||
version: 0.34.3
|
||||
resolution: "@img/sharp-win32-ia32@npm:0.34.3"
|
||||
"@img/sharp-win32-ia32@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-win32-ia32@npm:0.34.5"
|
||||
conditions: os=win32 & cpu=ia32
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -2795,9 +2821,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@img/sharp-win32-x64@npm:0.34.3":
|
||||
version: 0.34.3
|
||||
resolution: "@img/sharp-win32-x64@npm:0.34.3"
|
||||
"@img/sharp-win32-x64@npm:0.34.5":
|
||||
version: 0.34.5
|
||||
resolution: "@img/sharp-win32-x64@npm:0.34.5"
|
||||
conditions: os=win32 & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -3268,10 +3294,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/env@npm:15.5.0":
|
||||
version: 15.5.0
|
||||
resolution: "@next/env@npm:15.5.0"
|
||||
checksum: 10c0/13ed931688c26b764499840022c9855763fb583e728cf30933b2ff71d3ca681eb71611ec4d7580ecb45197c18a1a4498ba5a61cb60edc6e18966103620d11bad
|
||||
"@next/env@npm:15.5.7":
|
||||
version: 15.5.7
|
||||
resolution: "@next/env@npm:15.5.7"
|
||||
checksum: 10c0/f92d99e5fa3516c6b7699abafd9bd813f5c1889dd257ab098f1b71f93137f5e4f49792e22f6dddf8a59efcb134e8e84277c983ff88607b2a42aac651bfde78ea
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -3291,9 +3317,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-darwin-arm64@npm:15.5.0":
|
||||
version: 15.5.0
|
||||
resolution: "@next/swc-darwin-arm64@npm:15.5.0"
|
||||
"@next/swc-darwin-arm64@npm:15.5.7":
|
||||
version: 15.5.7
|
||||
resolution: "@next/swc-darwin-arm64@npm:15.5.7"
|
||||
conditions: os=darwin & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -3305,9 +3331,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-darwin-x64@npm:15.5.0":
|
||||
version: 15.5.0
|
||||
resolution: "@next/swc-darwin-x64@npm:15.5.0"
|
||||
"@next/swc-darwin-x64@npm:15.5.7":
|
||||
version: 15.5.7
|
||||
resolution: "@next/swc-darwin-x64@npm:15.5.7"
|
||||
conditions: os=darwin & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -3319,9 +3345,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-arm64-gnu@npm:15.5.0":
|
||||
version: 15.5.0
|
||||
resolution: "@next/swc-linux-arm64-gnu@npm:15.5.0"
|
||||
"@next/swc-linux-arm64-gnu@npm:15.5.7":
|
||||
version: 15.5.7
|
||||
resolution: "@next/swc-linux-arm64-gnu@npm:15.5.7"
|
||||
conditions: os=linux & cpu=arm64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -3333,9 +3359,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-arm64-musl@npm:15.5.0":
|
||||
version: 15.5.0
|
||||
resolution: "@next/swc-linux-arm64-musl@npm:15.5.0"
|
||||
"@next/swc-linux-arm64-musl@npm:15.5.7":
|
||||
version: 15.5.7
|
||||
resolution: "@next/swc-linux-arm64-musl@npm:15.5.7"
|
||||
conditions: os=linux & cpu=arm64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -3347,9 +3373,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-x64-gnu@npm:15.5.0":
|
||||
version: 15.5.0
|
||||
resolution: "@next/swc-linux-x64-gnu@npm:15.5.0"
|
||||
"@next/swc-linux-x64-gnu@npm:15.5.7":
|
||||
version: 15.5.7
|
||||
resolution: "@next/swc-linux-x64-gnu@npm:15.5.7"
|
||||
conditions: os=linux & cpu=x64 & libc=glibc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -3361,9 +3387,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-linux-x64-musl@npm:15.5.0":
|
||||
version: 15.5.0
|
||||
resolution: "@next/swc-linux-x64-musl@npm:15.5.0"
|
||||
"@next/swc-linux-x64-musl@npm:15.5.7":
|
||||
version: 15.5.7
|
||||
resolution: "@next/swc-linux-x64-musl@npm:15.5.7"
|
||||
conditions: os=linux & cpu=x64 & libc=musl
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -3375,9 +3401,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-win32-arm64-msvc@npm:15.5.0":
|
||||
version: 15.5.0
|
||||
resolution: "@next/swc-win32-arm64-msvc@npm:15.5.0"
|
||||
"@next/swc-win32-arm64-msvc@npm:15.5.7":
|
||||
version: 15.5.7
|
||||
resolution: "@next/swc-win32-arm64-msvc@npm:15.5.7"
|
||||
conditions: os=win32 & cpu=arm64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -3396,9 +3422,9 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@next/swc-win32-x64-msvc@npm:15.5.0":
|
||||
version: 15.5.0
|
||||
resolution: "@next/swc-win32-x64-msvc@npm:15.5.0"
|
||||
"@next/swc-win32-x64-msvc@npm:15.5.7":
|
||||
version: 15.5.7
|
||||
resolution: "@next/swc-win32-x64-msvc@npm:15.5.7"
|
||||
conditions: os=win32 & cpu=x64
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
|
@ -4573,6 +4599,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@posthog/core@npm:1.6.0":
|
||||
version: 1.6.0
|
||||
resolution: "@posthog/core@npm:1.6.0"
|
||||
dependencies:
|
||||
cross-spawn: "npm:^7.0.6"
|
||||
checksum: 10c0/28aa907bb21b18587bc5f47c44349ebc834b37d9a4cedb1a18d7b673d4d7cdad2120dda80deceaee707b2f52333e9a08a8e591e1fc4066521ce05e820b76309f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@prisma/client@npm:6.2.1":
|
||||
version: 6.2.1
|
||||
resolution: "@prisma/client@npm:6.2.1"
|
||||
|
|
@ -8155,8 +8190,8 @@ __metadata:
|
|||
"@types/node": "npm:^20"
|
||||
"@types/nodemailer": "npm:^6.4.17"
|
||||
"@types/psl": "npm:^1.1.3"
|
||||
"@types/react": "npm:19.1.10"
|
||||
"@types/react-dom": "npm:19.1.7"
|
||||
"@types/react": "npm:19.2.1"
|
||||
"@types/react-dom": "npm:19.2.1"
|
||||
"@typescript-eslint/eslint-plugin": "npm:^8.40.0"
|
||||
"@typescript-eslint/parser": "npm:^8.40.0"
|
||||
"@uidotdev/usehooks": "npm:^2.4.1"
|
||||
|
|
@ -8208,7 +8243,7 @@ __metadata:
|
|||
langfuse-vercel: "npm:^3.38.4"
|
||||
lucide-react: "npm:^0.517.0"
|
||||
micromatch: "npm:^4.0.8"
|
||||
next: "npm:15.5.0"
|
||||
next: "npm:^15.5.7"
|
||||
next-auth: "npm:^5.0.0-beta.30"
|
||||
next-navigation-guard: "npm:^0.2.0"
|
||||
next-themes: "npm:^0.3.0"
|
||||
|
|
@ -8219,11 +8254,12 @@ __metadata:
|
|||
parse-diff: "npm:^0.11.1"
|
||||
postcss: "npm:^8"
|
||||
posthog-js: "npm:^1.161.5"
|
||||
posthog-node: "npm:^5.15.0"
|
||||
pretty-bytes: "npm:^6.1.1"
|
||||
psl: "npm:^1.15.0"
|
||||
react: "npm:19.1.1"
|
||||
react: "npm:^19.2.1"
|
||||
react-device-detect: "npm:^2.2.3"
|
||||
react-dom: "npm:19.1.1"
|
||||
react-dom: "npm:^19.2.1"
|
||||
react-email: "npm:3.0.3"
|
||||
react-hook-form: "npm:^7.53.0"
|
||||
react-hotkeys-hook: "npm:^4.5.1"
|
||||
|
|
@ -8874,21 +8910,21 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/react-dom@npm:19.1.7":
|
||||
version: 19.1.7
|
||||
resolution: "@types/react-dom@npm:19.1.7"
|
||||
"@types/react-dom@npm:19.2.1":
|
||||
version: 19.2.1
|
||||
resolution: "@types/react-dom@npm:19.2.1"
|
||||
peerDependencies:
|
||||
"@types/react": ^19.0.0
|
||||
checksum: 10c0/8db5751c1567552fe4e1ece9f5823b682f2994ec8d30ed34ba0ef984e3c8ace1435f8be93d02f55c350147e78ac8c4dbcd8ed2c3b6a60f575bc5374f588c51c9
|
||||
"@types/react": ^19.2.0
|
||||
checksum: 10c0/0dbbc5b7ecd74681bfac95a413133b26118a70b8840748277abafa47e5c7a037beae6a660e6a21fb53f5cbdb0b2d33e117ea7bbd976a888c298392a8a96bc68f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/react@npm:19.1.10":
|
||||
version: 19.1.10
|
||||
resolution: "@types/react@npm:19.1.10"
|
||||
"@types/react@npm:19.2.1":
|
||||
version: 19.2.1
|
||||
resolution: "@types/react@npm:19.2.1"
|
||||
dependencies:
|
||||
csstype: "npm:^3.0.2"
|
||||
checksum: 10c0/fb583deacd0a815e2775dc1b9f764532d8cacb748ddd2c2914805a46c257ce6c237b4078f44009692074db212ab61a390301c6470f07f5aa5bfdeb78a2acfda1
|
||||
checksum: 10c0/c44881c275da91156ce02986ab1f59c9724db256f4850d3937c9acea561a6ab1fe1028f7a1fc4da3a2c1bcb00de29e238922e8c6d42a727ef2e6e0cd40b3db9f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -11310,10 +11346,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"detect-libc@npm:^2.0.4":
|
||||
version: 2.0.4
|
||||
resolution: "detect-libc@npm:2.0.4"
|
||||
checksum: 10c0/c15541f836eba4b1f521e4eecc28eefefdbc10a94d3b8cb4c507689f332cc111babb95deda66f2de050b22122113189986d5190be97d51b5a2b23b938415e67c
|
||||
"detect-libc@npm:^2.1.2":
|
||||
version: 2.1.2
|
||||
resolution: "detect-libc@npm:2.1.2"
|
||||
checksum: 10c0/acc675c29a5649fa1fb6e255f993b8ee829e510b6b56b0910666949c80c364738833417d0edb5f90e4e46be17228b0f2b66a010513984e18b15deeeac49369c4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -16142,19 +16178,19 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"next@npm:15.5.0":
|
||||
version: 15.5.0
|
||||
resolution: "next@npm:15.5.0"
|
||||
"next@npm:^15.5.7":
|
||||
version: 15.5.7
|
||||
resolution: "next@npm:15.5.7"
|
||||
dependencies:
|
||||
"@next/env": "npm:15.5.0"
|
||||
"@next/swc-darwin-arm64": "npm:15.5.0"
|
||||
"@next/swc-darwin-x64": "npm:15.5.0"
|
||||
"@next/swc-linux-arm64-gnu": "npm:15.5.0"
|
||||
"@next/swc-linux-arm64-musl": "npm:15.5.0"
|
||||
"@next/swc-linux-x64-gnu": "npm:15.5.0"
|
||||
"@next/swc-linux-x64-musl": "npm:15.5.0"
|
||||
"@next/swc-win32-arm64-msvc": "npm:15.5.0"
|
||||
"@next/swc-win32-x64-msvc": "npm:15.5.0"
|
||||
"@next/env": "npm:15.5.7"
|
||||
"@next/swc-darwin-arm64": "npm:15.5.7"
|
||||
"@next/swc-darwin-x64": "npm:15.5.7"
|
||||
"@next/swc-linux-arm64-gnu": "npm:15.5.7"
|
||||
"@next/swc-linux-arm64-musl": "npm:15.5.7"
|
||||
"@next/swc-linux-x64-gnu": "npm:15.5.7"
|
||||
"@next/swc-linux-x64-musl": "npm:15.5.7"
|
||||
"@next/swc-win32-arm64-msvc": "npm:15.5.7"
|
||||
"@next/swc-win32-x64-msvc": "npm:15.5.7"
|
||||
"@swc/helpers": "npm:0.5.15"
|
||||
caniuse-lite: "npm:^1.0.30001579"
|
||||
postcss: "npm:8.4.31"
|
||||
|
|
@ -16197,7 +16233,7 @@ __metadata:
|
|||
optional: true
|
||||
bin:
|
||||
next: dist/bin/next
|
||||
checksum: 10c0/8691787562666713e9ee8bc3edad646328d9cea85d5c4c10bd05a978afa25bc9927bbfd50acc69ceb268296741b5f71e78fa0213fe65b731a55e73b0f5807c24
|
||||
checksum: 10c0/baf5b9f42416c478702b3894479b3d7862bc4abf18afe0e43b7fc7ed35567b8dc6cb76cd94906505bab9013cb8d0f3370cdc0451c01ec15ae5a638d37b5ba7c7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -17209,6 +17245,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"posthog-node@npm:^5.15.0":
|
||||
version: 5.15.0
|
||||
resolution: "posthog-node@npm:5.15.0"
|
||||
dependencies:
|
||||
"@posthog/core": "npm:1.6.0"
|
||||
checksum: 10c0/7db929453cefc9b2d0017e1f7c9ffe7e6ecd2a495ee9861bd5e8f3873f72fa29a318dd7bccf10eb15639e1860aab396d4be502af88afba96ed15ac8b46d57e9d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"preact-render-to-string@npm:6.5.11":
|
||||
version: 6.5.11
|
||||
resolution: "preact-render-to-string@npm:6.5.11"
|
||||
|
|
@ -17539,14 +17584,14 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react-dom@npm:19.1.1":
|
||||
version: 19.1.1
|
||||
resolution: "react-dom@npm:19.1.1"
|
||||
"react-dom@npm:^19.2.1":
|
||||
version: 19.2.1
|
||||
resolution: "react-dom@npm:19.2.1"
|
||||
dependencies:
|
||||
scheduler: "npm:^0.26.0"
|
||||
scheduler: "npm:^0.27.0"
|
||||
peerDependencies:
|
||||
react: ^19.1.1
|
||||
checksum: 10c0/8c91198510521299c56e4e8d5e3a4508b2734fb5e52f29eeac33811de64e76fe586ad32c32182e2e84e070d98df67125da346c3360013357228172dbcd20bcdd
|
||||
react: ^19.2.1
|
||||
checksum: 10c0/e56b6b3d72314df580ca800b70a69a21c6372703c8f45d9b5451ca6519faefb2496d76ffa9c5adb94136d2bbf2fd303d0dfc208a2cd77ede3132877471af9470
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -17763,10 +17808,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"react@npm:19.1.1":
|
||||
version: 19.1.1
|
||||
resolution: "react@npm:19.1.1"
|
||||
checksum: 10c0/8c9769a2dfd02e603af6445058325e6c8a24b47b185d0e461f66a6454765ddcaecb3f0a90184836c68bb509f3c38248359edbc42f0d07c23eb500a5c30c87b4e
|
||||
"react@npm:^19.2.1":
|
||||
version: 19.2.1
|
||||
resolution: "react@npm:19.2.1"
|
||||
checksum: 10c0/2b5eaf407abb3db84090434c20d6c5a8e447ab7abcd8fe9eaf1ddc299babcf31284ee9db7ea5671d21c85ac5298bd632fa1a7da1ed78d5b368a537f5e1cd5d62
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -18490,10 +18535,10 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"scheduler@npm:^0.26.0":
|
||||
version: 0.26.0
|
||||
resolution: "scheduler@npm:0.26.0"
|
||||
checksum: 10c0/5b8d5bfddaae3513410eda54f2268e98a376a429931921a81b5c3a2873aab7ca4d775a8caac5498f8cbc7d0daeab947cf923dbd8e215d61671f9f4e392d34356
|
||||
"scheduler@npm:^0.27.0":
|
||||
version: 0.27.0
|
||||
resolution: "scheduler@npm:0.27.0"
|
||||
checksum: 10c0/4f03048cb05a3c8fddc45813052251eca00688f413a3cee236d984a161da28db28ba71bd11e7a3dd02f7af84ab28d39fb311431d3b3772fed557945beb00c452
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -18542,12 +18587,12 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"semver@npm:^7.7.2":
|
||||
version: 7.7.2
|
||||
resolution: "semver@npm:7.7.2"
|
||||
"semver@npm:^7.7.3":
|
||||
version: 7.7.3
|
||||
resolution: "semver@npm:7.7.3"
|
||||
bin:
|
||||
semver: bin/semver.js
|
||||
checksum: 10c0/aca305edfbf2383c22571cb7714f48cadc7ac95371b4b52362fb8eeffdfbc0de0669368b82b2b15978f8848f01d7114da65697e56cd8c37b0dab8c58e543f9ea
|
||||
checksum: 10c0/4afe5c986567db82f44c8c6faef8fe9df2a9b1d98098fc1721f57c696c4c21cebd572f297fc21002f81889492345b8470473bc6f4aff5fb032a6ea59ea2bc45e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
@ -18745,34 +18790,36 @@ __metadata:
|
|||
linkType: hard
|
||||
|
||||
"sharp@npm:^0.34.3":
|
||||
version: 0.34.3
|
||||
resolution: "sharp@npm:0.34.3"
|
||||
version: 0.34.5
|
||||
resolution: "sharp@npm:0.34.5"
|
||||
dependencies:
|
||||
"@img/sharp-darwin-arm64": "npm:0.34.3"
|
||||
"@img/sharp-darwin-x64": "npm:0.34.3"
|
||||
"@img/sharp-libvips-darwin-arm64": "npm:1.2.0"
|
||||
"@img/sharp-libvips-darwin-x64": "npm:1.2.0"
|
||||
"@img/sharp-libvips-linux-arm": "npm:1.2.0"
|
||||
"@img/sharp-libvips-linux-arm64": "npm:1.2.0"
|
||||
"@img/sharp-libvips-linux-ppc64": "npm:1.2.0"
|
||||
"@img/sharp-libvips-linux-s390x": "npm:1.2.0"
|
||||
"@img/sharp-libvips-linux-x64": "npm:1.2.0"
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.0"
|
||||
"@img/sharp-libvips-linuxmusl-x64": "npm:1.2.0"
|
||||
"@img/sharp-linux-arm": "npm:0.34.3"
|
||||
"@img/sharp-linux-arm64": "npm:0.34.3"
|
||||
"@img/sharp-linux-ppc64": "npm:0.34.3"
|
||||
"@img/sharp-linux-s390x": "npm:0.34.3"
|
||||
"@img/sharp-linux-x64": "npm:0.34.3"
|
||||
"@img/sharp-linuxmusl-arm64": "npm:0.34.3"
|
||||
"@img/sharp-linuxmusl-x64": "npm:0.34.3"
|
||||
"@img/sharp-wasm32": "npm:0.34.3"
|
||||
"@img/sharp-win32-arm64": "npm:0.34.3"
|
||||
"@img/sharp-win32-ia32": "npm:0.34.3"
|
||||
"@img/sharp-win32-x64": "npm:0.34.3"
|
||||
color: "npm:^4.2.3"
|
||||
detect-libc: "npm:^2.0.4"
|
||||
semver: "npm:^7.7.2"
|
||||
"@img/colour": "npm:^1.0.0"
|
||||
"@img/sharp-darwin-arm64": "npm:0.34.5"
|
||||
"@img/sharp-darwin-x64": "npm:0.34.5"
|
||||
"@img/sharp-libvips-darwin-arm64": "npm:1.2.4"
|
||||
"@img/sharp-libvips-darwin-x64": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linux-arm": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linux-arm64": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linux-ppc64": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linux-riscv64": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linux-s390x": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linux-x64": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "npm:1.2.4"
|
||||
"@img/sharp-libvips-linuxmusl-x64": "npm:1.2.4"
|
||||
"@img/sharp-linux-arm": "npm:0.34.5"
|
||||
"@img/sharp-linux-arm64": "npm:0.34.5"
|
||||
"@img/sharp-linux-ppc64": "npm:0.34.5"
|
||||
"@img/sharp-linux-riscv64": "npm:0.34.5"
|
||||
"@img/sharp-linux-s390x": "npm:0.34.5"
|
||||
"@img/sharp-linux-x64": "npm:0.34.5"
|
||||
"@img/sharp-linuxmusl-arm64": "npm:0.34.5"
|
||||
"@img/sharp-linuxmusl-x64": "npm:0.34.5"
|
||||
"@img/sharp-wasm32": "npm:0.34.5"
|
||||
"@img/sharp-win32-arm64": "npm:0.34.5"
|
||||
"@img/sharp-win32-ia32": "npm:0.34.5"
|
||||
"@img/sharp-win32-x64": "npm:0.34.5"
|
||||
detect-libc: "npm:^2.1.2"
|
||||
semver: "npm:^7.7.3"
|
||||
dependenciesMeta:
|
||||
"@img/sharp-darwin-arm64":
|
||||
optional: true
|
||||
|
|
@ -18788,6 +18835,8 @@ __metadata:
|
|||
optional: true
|
||||
"@img/sharp-libvips-linux-ppc64":
|
||||
optional: true
|
||||
"@img/sharp-libvips-linux-riscv64":
|
||||
optional: true
|
||||
"@img/sharp-libvips-linux-s390x":
|
||||
optional: true
|
||||
"@img/sharp-libvips-linux-x64":
|
||||
|
|
@ -18802,6 +18851,8 @@ __metadata:
|
|||
optional: true
|
||||
"@img/sharp-linux-ppc64":
|
||||
optional: true
|
||||
"@img/sharp-linux-riscv64":
|
||||
optional: true
|
||||
"@img/sharp-linux-s390x":
|
||||
optional: true
|
||||
"@img/sharp-linux-x64":
|
||||
|
|
@ -18818,7 +18869,7 @@ __metadata:
|
|||
optional: true
|
||||
"@img/sharp-win32-x64":
|
||||
optional: true
|
||||
checksum: 10c0/df9e6645e3db6ed298a0ac956ba74e468c367fc038b547936fbdddc6a29fce9af40413acbef73b3716291530760f311a20e45c8983f20ee5ea69dd2f21464a2b
|
||||
checksum: 10c0/fd79e29df0597a7d5704b8461c51f944ead91a5243691697be6e8243b966402beda53ddc6f0a53b96ea3cb8221f0b244aa588114d3ebf8734fb4aefd41ab802f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue