mirror of
https://github.com/sourcebot-dev/sourcebot.git
synced 2025-12-11 20:05:25 +00:00
Add PostHog telemetry support
This commit is contained in:
parent
9a8d17b4c0
commit
3ef9be2b6b
18 changed files with 317 additions and 43 deletions
|
|
@ -8,3 +8,4 @@ README.md
|
|||
!.next/standalone
|
||||
.git
|
||||
.sourcebot
|
||||
.env.local
|
||||
4
.env
Normal file
4
.env
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
NEXT_PUBLIC_POSTHOG_KEY=phc_VFn4CkEGHRdlVyOOw8mfkoj1DKVoG6y1007EClvzAnS
|
||||
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com
|
||||
NEXT_PUBLIC_POSTHOG_ASSET_HOST=https://us-assets.i.posthog.com
|
||||
NEXT_PUBLIC_POSTHOG_UI_HOST=https://us.posthog.com
|
||||
|
|
@ -22,6 +22,8 @@ RUN yarn config set network-timeout 1200000
|
|||
RUN yarn --frozen-lockfile
|
||||
COPY . .
|
||||
ENV NEXT_TELEMETRY_DISABLED=1
|
||||
# @see: https://phase.dev/blog/nextjs-public-runtime-variables/
|
||||
ARG NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED=BAKED_NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED
|
||||
RUN yarn run build
|
||||
|
||||
# ------ Runner ------
|
||||
|
|
@ -33,6 +35,9 @@ ENV DATA_DIR=/data
|
|||
ENV CONFIG_PATH=$DATA_DIR/config.json
|
||||
ENV DATA_CACHE_DIR=$DATA_DIR/.sourcebot
|
||||
|
||||
# Sourcebot collects anonymous usage data using [PostHog](https://posthog.com/). Uncomment this line to disable.
|
||||
# ENV SOURCEBOT_TELEMETRY_DISABLED=1
|
||||
|
||||
# Configure dependencies
|
||||
RUN apk add --no-cache git ca-certificates bind-tools tini jansson wget supervisor
|
||||
|
||||
|
|
|
|||
16
README.md
16
README.md
|
|
@ -65,20 +65,26 @@ zoekt will now index your repositories (at `HEAD`). By default, it will re-index
|
|||
|
||||
4. Go to `http://localhost:3000` - once a index has been created, you should get results.
|
||||
|
||||
|
||||
|
||||
## Building Sourcebot
|
||||
|
||||
TODO
|
||||
|
||||
## GitLab
|
||||
## Disabling Telemetry
|
||||
|
||||
By default, Sourcebot collects anonymous usage data using [PostHog](https://posthog.com/). You can disable this by setting the environment variable `SOURCEBOT_TELEMETRY_DISABLED` to `1` in the docker run command. Example:
|
||||
```sh
|
||||
docker run -e SOURCEBOT_TELEMETRY_DISABLED=1 ...stuff... ghcr.io/taqlaai/sourcebot:main
|
||||
```
|
||||
|
||||
|
||||
# GitLab
|
||||
|
||||
TODO
|
||||
|
||||
## BitBucket
|
||||
# BitBucket
|
||||
|
||||
TODO
|
||||
|
||||
### Todos
|
||||
# Todos
|
||||
- Add instructions on using GitLab and BitBucket
|
||||
- Add instructions on building Sourcebot locally
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
|
||||
# Check if CONFIG_PATH is set
|
||||
if [ -z "$CONFIG_PATH" ]; then
|
||||
echo "\e[33mWarning: CONFIG_PATH environment variable is not set.\e[0m"
|
||||
|
|
@ -35,4 +34,17 @@ else
|
|||
echo -e "\e[33mWarning: GitLab repositories will not be indexed since GITLAB_TOKEN was not set. If you are not using GitLab, disregard.\e[0m"
|
||||
fi
|
||||
|
||||
# Update nextjs public env variables w/o requiring a rebuild.
|
||||
# @see: https://phase.dev/blog/nextjs-public-runtime-variables/
|
||||
|
||||
# Infer NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED if it is not set
|
||||
if [ -z "$NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED" ] && [ ! -z "$SOURCEBOT_TELEMETRY_DISABLED" ]; then
|
||||
export NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED="$SOURCEBOT_TELEMETRY_DISABLED"
|
||||
fi
|
||||
|
||||
find /app/public /app/.next -type f -name "*.js" |
|
||||
while read file; do
|
||||
sed -i "s|BAKED_NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED|${NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED}|g" "$file"
|
||||
done
|
||||
|
||||
exec supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
||||
|
|
@ -1,6 +1,26 @@
|
|||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
output: "standalone"
|
||||
output: "standalone",
|
||||
|
||||
// @see : https://posthog.com/docs/advanced/proxy/nextjs
|
||||
async rewrites() {
|
||||
return [
|
||||
{
|
||||
source: "/ingest/static/:path*",
|
||||
destination: `${process.env.NEXT_PUBLIC_POSTHOG_ASSET_HOST}/static/:path*`,
|
||||
},
|
||||
{
|
||||
source: "/ingest/:path*",
|
||||
destination: `${process.env.NEXT_PUBLIC_POSTHOG_HOST}/:path*`,
|
||||
},
|
||||
{
|
||||
source: "/ingest/decide",
|
||||
destination: `${process.env.NEXT_PUBLIC_POSTHOG_HOST}/decide`,
|
||||
},
|
||||
];
|
||||
},
|
||||
// This is required to support PostHog trailing slash API requests
|
||||
skipTrailingSlashRedirect: true,
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
|
|
|
|||
|
|
@ -27,17 +27,20 @@
|
|||
"@tanstack/react-table": "^8.20.5",
|
||||
"@uiw/react-codemirror": "^4.23.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"client-only": "^0.0.1",
|
||||
"clsx": "^2.1.1",
|
||||
"escape-string-regexp": "^5.0.0",
|
||||
"http-status-codes": "^2.3.0",
|
||||
"lucide-react": "^0.435.0",
|
||||
"next": "14.2.6",
|
||||
"next-themes": "^0.3.0",
|
||||
"posthog-js": "^1.161.5",
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"react-hook-form": "^7.53.0",
|
||||
"react-hotkeys-hook": "^4.5.1",
|
||||
"react-resizable-panels": "^2.1.1",
|
||||
"server-only": "^0.0.1",
|
||||
"sharp": "^0.33.5",
|
||||
"tailwind-merge": "^2.5.2",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
|
|
|
|||
|
|
@ -4,9 +4,15 @@ import "./globals.css";
|
|||
import { ThemeProvider } from "next-themes";
|
||||
import { Suspense } from "react";
|
||||
import { QueryClientProvider } from "./queryClientProvider";
|
||||
import { PHProvider } from "./posthogProvider";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
const PostHogPageView = dynamic(() => import('./posthogPageView'), {
|
||||
ssr: false,
|
||||
})
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Sourcebot",
|
||||
description: "Sourcebot",
|
||||
|
|
@ -24,6 +30,8 @@ export default function RootLayout({
|
|||
suppressHydrationWarning
|
||||
>
|
||||
<body className={inter.className}>
|
||||
<PHProvider>
|
||||
<PostHogPageView />
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="system"
|
||||
|
|
@ -40,6 +48,7 @@ export default function RootLayout({
|
|||
</Suspense>
|
||||
</QueryClientProvider>
|
||||
</ThemeProvider>
|
||||
</PHProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
|
|
|||
28
src/app/posthogPageView.tsx
Normal file
28
src/app/posthogPageView.tsx
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
'use client'
|
||||
|
||||
import { usePathname, useSearchParams } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
import { usePostHog } from 'posthog-js/react';
|
||||
|
||||
export default function PostHogPageView(): null {
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
const posthog = usePostHog();
|
||||
useEffect(() => {
|
||||
// Track pageviews
|
||||
if (pathname && posthog) {
|
||||
let url = window.origin + pathname
|
||||
if (searchParams.toString()) {
|
||||
url = url + `?${searchParams.toString()}`
|
||||
}
|
||||
posthog.capture(
|
||||
'$pageview',
|
||||
{
|
||||
'$current_url': url,
|
||||
}
|
||||
)
|
||||
}
|
||||
}, [pathname, searchParams, posthog])
|
||||
|
||||
return null
|
||||
}
|
||||
25
src/app/posthogProvider.tsx
Normal file
25
src/app/posthogProvider.tsx
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
'use client'
|
||||
import { NEXT_PUBLIC_POSTHOG_KEY, NEXT_PUBLIC_POSTHOG_UI_HOST, NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED } from '@/lib/environment.client'
|
||||
import posthog from 'posthog-js'
|
||||
import { PostHogProvider } from 'posthog-js/react'
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
if (!NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED) {
|
||||
posthog.init(NEXT_PUBLIC_POSTHOG_KEY!, {
|
||||
api_host: "/ingest",
|
||||
ui_host: NEXT_PUBLIC_POSTHOG_UI_HOST,
|
||||
person_profiles: 'identified_only',
|
||||
capture_pageview: false, // Disable automatic pageview capture, as we capture manually
|
||||
});
|
||||
} else {
|
||||
console.log("PostHog telemetry disabled");
|
||||
}
|
||||
}
|
||||
|
||||
export function PHProvider({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return <PostHogProvider client={posthog}>{children}</PostHogProvider>
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ import { SymbolIcon } from "@radix-ui/react-icons";
|
|||
import { useQuery } from "@tanstack/react-query";
|
||||
import Image from "next/image";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import logoDark from "../../../public/sb_logo_dark.png";
|
||||
import logoLight from "../../../public/sb_logo_light.png";
|
||||
import { fetchFileSource, search } from "../api/(client)/client";
|
||||
|
|
@ -21,6 +21,7 @@ import { SearchBar } from "../searchBar";
|
|||
import { SettingsDropdown } from "../settingsDropdown";
|
||||
import { CodePreviewFile, CodePreviewPanel } from "./codePreviewPanel";
|
||||
import { SearchResultsPanel } from "./searchResultsPanel";
|
||||
import useCaptureEvent from "@/hooks/useCaptureEvent";
|
||||
|
||||
const DEFAULT_NUM_RESULTS = 100;
|
||||
|
||||
|
|
@ -33,6 +34,8 @@ export default function SearchPage() {
|
|||
const [selectedMatchIndex, setSelectedMatchIndex] = useState(0);
|
||||
const [selectedFile, setSelectedFile] = useState<SearchResultFile | undefined>(undefined);
|
||||
|
||||
const captureEvent = useCaptureEvent();
|
||||
|
||||
const { data: searchResponse, isLoading } = useQuery({
|
||||
queryKey: ["search", searchQuery, numResults],
|
||||
queryFn: () => search({
|
||||
|
|
@ -40,8 +43,41 @@ export default function SearchPage() {
|
|||
numResults,
|
||||
}),
|
||||
enabled: searchQuery.length > 0,
|
||||
refetchOnWindowFocus: false,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!searchResponse) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fileLanguages = searchResponse.Result.Files?.map(file => file.Language) || [];
|
||||
|
||||
captureEvent("search_finished", {
|
||||
contentBytesLoaded: searchResponse.Result.ContentBytesLoaded,
|
||||
indexBytesLoaded: searchResponse.Result.IndexBytesLoaded,
|
||||
crashes: searchResponse.Result.Crashes,
|
||||
durationMs: searchResponse.Result.Duration / 1000000,
|
||||
fileCount: searchResponse.Result.FileCount,
|
||||
shardFilesConsidered: searchResponse.Result.ShardFilesConsidered,
|
||||
filesConsidered: searchResponse.Result.FilesConsidered,
|
||||
filesLoaded: searchResponse.Result.FilesLoaded,
|
||||
filesSkipped: searchResponse.Result.FilesSkipped,
|
||||
shardsScanned: searchResponse.Result.ShardsScanned,
|
||||
shardsSkipped: searchResponse.Result.ShardsSkipped,
|
||||
shardsSkippedFilter: searchResponse.Result.ShardsSkippedFilter,
|
||||
matchCount: searchResponse.Result.MatchCount,
|
||||
ngramMatches: searchResponse.Result.NgramMatches,
|
||||
ngramLookups: searchResponse.Result.NgramLookups,
|
||||
wait: searchResponse.Result.Wait,
|
||||
matchTreeConstruction: searchResponse.Result.MatchTreeConstruction,
|
||||
matchTreeSearch: searchResponse.Result.MatchTreeSearch,
|
||||
regexpsConsidered: searchResponse.Result.RegexpsConsidered,
|
||||
flushReason: searchResponse.Result.FlushReason,
|
||||
fileLanguages,
|
||||
});
|
||||
}, [captureEvent, searchResponse]);
|
||||
|
||||
const { fileMatches, searchDurationMs } = useMemo((): { fileMatches: SearchResultFile[], searchDurationMs: number } => {
|
||||
if (!searchResponse) {
|
||||
return {
|
||||
|
|
|
|||
26
src/hooks/useCaptureEvent.ts
Normal file
26
src/hooks/useCaptureEvent.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
'use client';
|
||||
|
||||
import { CaptureOptions } from "posthog-js";
|
||||
import posthog from "posthog-js";
|
||||
import { PosthogEvent, PosthogEventMap } from "../lib/posthogEvents";
|
||||
|
||||
export function captureEvent<E extends PosthogEvent>(event: E, properties: PosthogEventMap[E], options?: CaptureOptions) {
|
||||
if(!options) {
|
||||
options = {};
|
||||
}
|
||||
options.send_instantly = true;
|
||||
posthog.capture(event, properties, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures a distinct action as a event and forwards it to the event service
|
||||
* (i.e., PostHog).
|
||||
*
|
||||
* @returns A callback for capturing events.
|
||||
* @see: https://posthog.com/docs/libraries/js#capturing-events
|
||||
*/
|
||||
const useCaptureEvent = () => {
|
||||
return captureEvent;
|
||||
}
|
||||
|
||||
export default useCaptureEvent;
|
||||
9
src/lib/environment.client.ts
Normal file
9
src/lib/environment.client.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import 'client-only';
|
||||
|
||||
import { getEnv, getEnvBoolean } from "./utils";
|
||||
|
||||
export const NEXT_PUBLIC_POSTHOG_KEY = getEnv(process.env.NEXT_PUBLIC_POSTHOG_KEY);
|
||||
export const NEXT_PUBLIC_POSTHOG_HOST = getEnv(process.env.NEXT_PUBLIC_POSTHOG_HOST);
|
||||
export const NEXT_PUBLIC_POSTHOG_UI_HOST = getEnv(process.env.NEXT_PUBLIC_POSTHOG_UI_HOST);
|
||||
export const NEXT_PUBLIC_POSTHOG_ASSET_HOST = getEnv(process.env.NEXT_PUBLIC_POSTHOG_ASSET_HOST);
|
||||
export const NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED = getEnvBoolean(process.env.NEXT_PUBLIC_SOURCEBOT_TELEMETRY_DISABLED, false);
|
||||
|
|
@ -1,14 +1,8 @@
|
|||
import 'server-only';
|
||||
|
||||
const getEnv = (env: string | undefined, defaultValue = '') => {
|
||||
return env ?? defaultValue;
|
||||
}
|
||||
|
||||
const getEnvNumber = (env: string | undefined, defaultValue: number = 0) => {
|
||||
return Number(env) ?? defaultValue;
|
||||
}
|
||||
import { getEnv, getEnvNumber } from "./utils";
|
||||
|
||||
export const ZOEKT_WEBSERVER_URL = getEnv(process.env.ZOEKT_WEBSERVER_URL, "http://localhost:6070");
|
||||
export const SHARD_MAX_MATCH_COUNT = getEnvNumber(process.env.SHARD_MAX_MATCH_COUNT, 10000);
|
||||
export const TOTAL_MAX_MATCH_COUNT = getEnvNumber(process.env.TOTAL_MAX_MATCH_COUNT, 100000);
|
||||
|
||||
export const NODE_ENV = process.env.NODE_ENV;
|
||||
|
|
|
|||
29
src/lib/posthogEvents.ts
Normal file
29
src/lib/posthogEvents.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* eslint-disable @typescript-eslint/no-empty-object-type */
|
||||
|
||||
export type PosthogEventMap = {
|
||||
search_finished: {
|
||||
contentBytesLoaded: number,
|
||||
indexBytesLoaded: number,
|
||||
crashes: number,
|
||||
durationMs: number,
|
||||
fileCount: number,
|
||||
shardFilesConsidered: number,
|
||||
filesConsidered: number,
|
||||
filesLoaded: number,
|
||||
filesSkipped: number,
|
||||
shardsScanned: number,
|
||||
shardsSkipped: number,
|
||||
shardsSkippedFilter: number,
|
||||
matchCount: number,
|
||||
ngramMatches: number,
|
||||
ngramLookups: number,
|
||||
wait: number,
|
||||
matchTreeConstruction: number,
|
||||
matchTreeSearch: number,
|
||||
regexpsConsidered: number,
|
||||
flushReason: number,
|
||||
fileLanguages: string[]
|
||||
}
|
||||
}
|
||||
|
||||
export type PosthogEvent = keyof PosthogEventMap;
|
||||
|
|
@ -30,11 +30,34 @@ const rangeSchema = z.object({
|
|||
End: locationSchema,
|
||||
});
|
||||
|
||||
export const searchResponseSchema = z.object({
|
||||
Result: z.object({
|
||||
// @see : https://github.com/TaqlaAI/zoekt/blob/3780e68cdb537d5a7ed2c84d9b3784f80c7c5d04/api.go#L350
|
||||
export const searchResponseStats = {
|
||||
ContentBytesLoaded: z.number(),
|
||||
IndexBytesLoaded: z.number(),
|
||||
Crashes: z.number(),
|
||||
Duration: z.number(),
|
||||
FileCount: z.number(),
|
||||
ShardFilesConsidered: z.number(),
|
||||
FilesConsidered: z.number(),
|
||||
FilesLoaded: z.number(),
|
||||
FilesSkipped: z.number(),
|
||||
ShardsScanned: z.number(),
|
||||
ShardsSkipped: z.number(),
|
||||
ShardsSkippedFilter: z.number(),
|
||||
MatchCount: z.number(),
|
||||
NgramMatches: z.number(),
|
||||
NgramLookups: z.number(),
|
||||
Wait: z.number(),
|
||||
MatchTreeConstruction: z.number(),
|
||||
MatchTreeSearch: z.number(),
|
||||
RegexpsConsidered: z.number(),
|
||||
FlushReason: z.number(),
|
||||
}
|
||||
|
||||
// @see : https://github.com/TaqlaAI/zoekt/blob/3780e68cdb537d5a7ed2c84d9b3784f80c7c5d04/api.go#L497
|
||||
export const searchResponseSchema = z.object({
|
||||
Result: z.object({
|
||||
...searchResponseStats,
|
||||
Files: z.array(z.object({
|
||||
FileName: z.string(),
|
||||
Repository: z.string(),
|
||||
|
|
@ -72,7 +95,7 @@ export const fileSourceResponseSchema = z.object({
|
|||
export type ListRepositoriesResponse = z.infer<typeof listRepositoriesResponseSchema>;
|
||||
|
||||
// @see : https://github.com/TaqlaAI/zoekt/blob/3780e68cdb537d5a7ed2c84d9b3784f80c7c5d04/api.go#L728
|
||||
export const statsSchema = z.object({
|
||||
const repoStatsSchema = z.object({
|
||||
Repos: z.number(),
|
||||
Shards: z.number(),
|
||||
Documents: z.number(),
|
||||
|
|
@ -84,7 +107,7 @@ export const statsSchema = z.object({
|
|||
});
|
||||
|
||||
// @see : https://github.com/TaqlaAI/zoekt/blob/3780e68cdb537d5a7ed2c84d9b3784f80c7c5d04/api.go#L716
|
||||
export const indexMetadataSchema = z.object({
|
||||
const indexMetadataSchema = z.object({
|
||||
IndexFormatVersion: z.number(),
|
||||
IndexFeatureVersion: z.number(),
|
||||
IndexMinReaderVersion: z.number(),
|
||||
|
|
@ -96,7 +119,7 @@ export const indexMetadataSchema = z.object({
|
|||
});
|
||||
|
||||
// @see : https://github.com/TaqlaAI/zoekt/blob/3780e68cdb537d5a7ed2c84d9b3784f80c7c5d04/api.go#L555
|
||||
export const repositorySchema = z.object({
|
||||
const repositorySchema = z.object({
|
||||
Name: z.string(),
|
||||
URL: z.string(),
|
||||
Source: z.string(),
|
||||
|
|
@ -121,8 +144,8 @@ export const listRepositoriesResponseSchema = z.object({
|
|||
Repos: z.array(z.object({
|
||||
Repository: repositorySchema,
|
||||
IndexMetadata: indexMetadataSchema,
|
||||
Stats: statsSchema,
|
||||
Stats: repoStatsSchema,
|
||||
})),
|
||||
Stats: statsSchema,
|
||||
Stats: repoStatsSchema,
|
||||
})
|
||||
});
|
||||
|
|
|
|||
|
|
@ -81,3 +81,18 @@ export const isServiceError = (data: unknown): data is ServiceError => {
|
|||
'errorCode' in data &&
|
||||
'message' in data;
|
||||
}
|
||||
|
||||
export const getEnv = (env: string | undefined, defaultValue = '') => {
|
||||
return env ?? defaultValue;
|
||||
}
|
||||
|
||||
export const getEnvNumber = (env: string | undefined, defaultValue: number = 0) => {
|
||||
return Number(env) ?? defaultValue;
|
||||
}
|
||||
|
||||
export const getEnvBoolean = (env: string | undefined, defaultValue: boolean) => {
|
||||
if (!env) {
|
||||
return defaultValue;
|
||||
}
|
||||
return env === 'true' || env === '1';
|
||||
}
|
||||
31
yarn.lock
31
yarn.lock
|
|
@ -1268,7 +1268,7 @@ class-variance-authority@^0.7.0:
|
|||
dependencies:
|
||||
clsx "2.0.0"
|
||||
|
||||
client-only@0.0.1:
|
||||
client-only@0.0.1, client-only@^0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1"
|
||||
integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==
|
||||
|
|
@ -1897,6 +1897,11 @@ fastq@^1.6.0:
|
|||
dependencies:
|
||||
reusify "^1.0.4"
|
||||
|
||||
fflate@^0.4.8:
|
||||
version "0.4.8"
|
||||
resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae"
|
||||
integrity sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==
|
||||
|
||||
file-entry-cache@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
|
||||
|
|
@ -2901,6 +2906,20 @@ postcss@^8, postcss@^8.4.23:
|
|||
picocolors "^1.0.1"
|
||||
source-map-js "^1.2.0"
|
||||
|
||||
posthog-js@^1.161.5:
|
||||
version "1.161.5"
|
||||
resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.161.5.tgz#3c07acf622c0719cd8e0e78ab4b0f3e85914c7ef"
|
||||
integrity sha512-KGkb12grSQvGRauH6z+AUB83c4dgWqzmJFDjyMXarWRafaLN80HzjN1jk806x27HvdDXi21jtwiXekioWzEQ9g==
|
||||
dependencies:
|
||||
fflate "^0.4.8"
|
||||
preact "^10.19.3"
|
||||
web-vitals "^4.0.1"
|
||||
|
||||
preact@^10.19.3:
|
||||
version "10.24.0"
|
||||
resolved "https://registry.yarnpkg.com/preact/-/preact-10.24.0.tgz#bd8139bee35aafede3c6de96d2453982610dfeef"
|
||||
integrity sha512-aK8Cf+jkfyuZ0ZZRG9FbYqwmEiGQ4y/PUO4SuTWoyWL244nZZh7bd5h2APd4rSNDYTBNghg1L+5iJN3Skxtbsw==
|
||||
|
||||
prelude-ls@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||
|
|
@ -3113,6 +3132,11 @@ semver@^7.5.4, semver@^7.6.0, semver@^7.6.3:
|
|||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
|
||||
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
||||
|
||||
server-only@^0.0.1:
|
||||
version "0.0.1"
|
||||
resolved "https://registry.yarnpkg.com/server-only/-/server-only-0.0.1.tgz#0f366bb6afb618c37c9255a314535dc412cd1c9e"
|
||||
integrity sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==
|
||||
|
||||
set-function-length@^1.2.1:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
|
||||
|
|
@ -3572,6 +3596,11 @@ w3c-keyname@^2.2.4:
|
|||
resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz#7b17c8c6883d4e8b86ac8aba79d39e880f8869c5"
|
||||
integrity sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==
|
||||
|
||||
web-vitals@^4.0.1:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-4.2.3.tgz#270c4baecfbc6ec6fc15da1989e465e5f9b94fb7"
|
||||
integrity sha512-/CFAm1mNxSmOj6i0Co+iGFJ58OS4NRGVP+AWS/l509uIK5a1bSoIVaHz/ZumpHTfHSZBpgrJ+wjfpAOrTHok5Q==
|
||||
|
||||
which-boxed-primitive@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
|
||||
|
|
|
|||
Loading…
Reference in a new issue