diff --git a/.vscode/settings.json b/.vscode/settings.json index 1e76b8a0..d9aacae8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,7 +4,6 @@ }, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, - "eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }], "eslint.workingDirectories": [ { "pattern": "apps/*/" }, { "pattern": "packages/*/" }, diff --git a/README.md b/README.md index d32ca5ad..d922995f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# openpanel +# Openpanel Openpanel is a simple analytics tool for logging events on web and react-native. My goal is to make a minimal mixpanel copy with the most basic features (for now). diff --git a/apps/api/scripts/test-events.ts b/apps/api/scripts/test-events.ts deleted file mode 100644 index fe456aaf..00000000 --- a/apps/api/scripts/test-events.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { omit, prop, uniqBy } from 'ramda'; - -import { generateProfileId, getTime, toISOString } from '@openpanel/common'; -import type { Event, IServiceCreateEventPayload } from '@openpanel/db'; -import { - createEvent as createClickhouseEvent, - db, - formatClickhouseDate, - getSalts, -} from '@openpanel/db'; - -import { parseIp } from '../src/utils/parseIp'; -import { parseUserAgent } from '../src/utils/parseUserAgent'; - -const clean = omit([ - 'ip', - 'os', - 'ua', - 'url', - 'hash', - 'host', - 'path', - 'device', - 'screen', - 'hostname', - 'language', - 'referrer', - 'timezone', -]); -async function main() { - const events = await db.event.findMany({ - where: { - project_id: '4e2798cb-e255-4e9d-960d-c9ad095aabd7', - name: 'screen_view', - createdAt: { - gte: new Date('2024-01-01'), - lt: new Date('2024-02-04'), - }, - }, - orderBy: { - createdAt: 'asc', - }, - }); - - const grouped: Record = {}; - let index = 0; - for (const event of events.slice()) { - // console.log(index, event.name, event.createdAt.toISOString()); - index++; - - const properties = event.properties as Record; - - if (properties.ua?.includes('bot')) { - // console.log('IGNORE', event.id); - continue; - } - - if (!event.profile_id) { - // console.log('IGNORE', event.id); - continue; - } - const hej = grouped[event.profile_id]; - if (hej) { - hej.push(event); - } else { - grouped[event.profile_id] = [event]; - } - } - - console.log('Total users', Object.keys(grouped).length); - - let uidx = -1; - for (const profile_id of Object.keys(grouped)) { - uidx++; - console.log(`User index ${uidx}`); - - const events = uniqBy(prop('createdAt'), grouped[profile_id] || []); - - if (events) { - let lastSessionStart = null; - let screenViews = 0; - let totalDuration = 0; - console.log('new user...'); - let eidx = -1; - for (const event of events) { - eidx++; - const prevEvent = events[eidx - 1]; - const prevEventAt = prevEvent?.createdAt; - - const nextEvent = events[eidx + 1]; - - const properties = event.properties as Record; - const projectId = event.project_id; - const path = properties.path!; - const ip = properties.ip!; - const origin = 'https://openpanel.kiddo.se'; - const ua = properties.ua!; - const uaInfo = parseUserAgent(ua); - const salts = await getSalts(); - const profileId = generateProfileId({ - salt: salts.current, - origin, - ip, - ua, - }); - - const geo = parseIp(ip); - - const isNextEventNewSession = - nextEvent && - nextEvent.createdAt.getTime() - event.createdAt.getTime() > - 1000 * 60 * 30; - - const payload: IServiceCreateEventPayload = { - name: event.name, - profileId, - projectId, - properties: clean(properties), - createdAt: event.createdAt.toISOString(), - country: geo.country, - city: geo.city, - region: geo.region, - continent: geo.continent, - os: uaInfo.os, - osVersion: uaInfo.osVersion, - browser: uaInfo.browser, - browserVersion: uaInfo.browserVersion, - device: uaInfo.device, - brand: uaInfo.brand, - model: uaInfo.model, - duration: - nextEvent && !isNextEventNewSession - ? nextEvent.createdAt.getTime() - event.createdAt.getTime() - : 0, - path, - referrer: properties?.referrer?.host ?? '', // TODO - referrerName: properties?.referrer?.host ?? '', // TODO - }; - - if (!prevEventAt) { - lastSessionStart = await createSessionStart(payload); - } else if ( - event.createdAt.getTime() - prevEventAt.getTime() > - 1000 * 60 * 30 - ) { - if (eidx > 0 && prevEventAt && lastSessionStart) { - await createSessionEnd(prevEventAt, lastSessionStart, { - screenViews, - totalDuration, - }); - totalDuration = 0; - screenViews = 0; - lastSessionStart = await createSessionStart(payload); - } - } - - screenViews++; - totalDuration += payload.duration; - await createEvent(payload); - } // for each user event - - const prevEventAt = events[events.length - 1]?.createdAt; - if (prevEventAt && lastSessionStart) { - await createSessionEnd(prevEventAt, lastSessionStart, { - screenViews, - totalDuration, - }); - } - } - } -} - -async function createEvent(event: IServiceCreateEventPayload) { - console.log( - `Create ${event.name} - ${event.path} - ${formatClickhouseDate( - event.createdAt - )} - ${event.duration / 1000} sec` - ); - await createClickhouseEvent(event); -} - -async function createSessionStart(event: IServiceCreateEventPayload) { - const session: IServiceCreateEventPayload = { - ...event, - duration: 0, - name: 'session_start', - createdAt: toISOString(getTime(event.createdAt) - 100), - }; - - await createEvent(session); - return session; -} - -async function createSessionEnd( - prevEventAt: Date, - sessionStart: IServiceCreateEventPayload, - options: { - screenViews: number; - totalDuration: number; - } -) { - const properties: Record = {}; - if (options.screenViews === 1) { - properties.__bounce = true; - } else { - properties.__bounce = false; - } - - const session: IServiceCreateEventPayload = { - ...sessionStart, - properties: { - ...properties, - ...sessionStart.properties, - }, - duration: options.totalDuration, - name: 'session_end', - createdAt: toISOString(prevEventAt.getTime() + 10), - }; - - await createEvent(session); - return session; -} - -main(); diff --git a/apps/api/scripts/test-ua.ts b/apps/api/scripts/test-ua.ts deleted file mode 100644 index 00547799..00000000 --- a/apps/api/scripts/test-ua.ts +++ /dev/null @@ -1,18 +0,0 @@ -//@ts-nocheck - -async function main() { - const crypto = require('crypto'); - - function createHash(data, len) { - return crypto - .createHash('shake256', { outputLength: len }) - .update(data) - .digest('hex'); - } - - console.log(createHash('foo', 2)); - // 1af9 - console.log(createHash('foo', 32)); - // 1af97f7818a28edf} -} -main(); diff --git a/apps/api/src/controllers/event.controller.ts b/apps/api/src/controllers/event.controller.ts index 6d50f128..afefb11e 100644 --- a/apps/api/src/controllers/event.controller.ts +++ b/apps/api/src/controllers/event.controller.ts @@ -18,7 +18,7 @@ import type { PostEventPayload } from '@openpanel/sdk'; const SESSION_TIMEOUT = 1000 * 60 * 30; const SESSION_END_TIMEOUT = SESSION_TIMEOUT + 1000; -async function withTiming(name: string, promise: T) { +async function withTiming(name: string, promise: Promise) { try { const start = Date.now(); const res = await promise; diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json index a291eef2..754acf08 100644 --- a/apps/api/tsconfig.json +++ b/apps/api/tsconfig.json @@ -8,5 +8,5 @@ "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" }, "include": ["."], - "exclude": ["node_modules"] + "exclude": ["node_modules", "dist"] } diff --git a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/dashboards/list-dashboards.tsx b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/dashboards/list-dashboards.tsx index 2b37693d..0494a50b 100644 --- a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/dashboards/list-dashboards.tsx +++ b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/dashboards/list-dashboards.tsx @@ -1,11 +1,11 @@ 'use client'; -import { api, handleErrorToastOptions } from '@/app/_trpc/client'; import { Card, CardActions, CardActionsItem } from '@/components/card'; import { FullPageEmptyState } from '@/components/full-page-empty-state'; import { Button } from '@/components/ui/button'; import { useAppParams } from '@/hooks/useAppParams'; import { pushModal } from '@/modals'; +import { api, handleErrorToastOptions } from '@/trpc/client'; import { LayoutPanelTopIcon, Pencil, PlusIcon, Trash } from 'lucide-react'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; diff --git a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/events/event-details.tsx b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/events/event-details.tsx index b54af6cc..89299eae 100644 --- a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/events/event-details.tsx +++ b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/events/event-details.tsx @@ -184,7 +184,7 @@ export function EventDetails({ event, open, setOpen }: Props) { key={item.name} name={item.name} value={item.value} - onClick={item.onClick} + onClick={() => item.onClick?.()} /> ))} @@ -225,7 +225,7 @@ export function EventDetails({ event, open, setOpen }: Props) { className="w-full" onClick={() => setIsEditOpen(true)} > - Customize "{name}" + Customize "{name}" diff --git a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/events/event-edit.tsx b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/events/event-edit.tsx index 6b158649..decbb11b 100644 --- a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/events/event-edit.tsx +++ b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/events/event-edit.tsx @@ -1,6 +1,5 @@ import type { Dispatch, SetStateAction } from 'react'; import { useEffect, useState } from 'react'; -import { api } from '@/app/_trpc/client'; import { Button } from '@/components/ui/button'; import { Checkbox } from '@/components/ui/checkbox'; import { Label } from '@/components/ui/label'; @@ -11,6 +10,7 @@ import { SheetHeader, SheetTitle, } from '@/components/ui/sheet'; +import { api } from '@/trpc/client'; import { cn } from '@/utils/cn'; import { useRouter } from 'next/navigation'; import { toast } from 'sonner'; @@ -78,7 +78,7 @@ export function EventEdit({ event, open, setOpen }: Props) { - Edit "{name}" + Edit "{name}"
diff --git a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/events/event-icon.tsx b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/events/event-icon.tsx index efd5c96e..03004579 100644 --- a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/events/event-icon.tsx +++ b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/events/event-icon.tsx @@ -1,5 +1,4 @@ import { useEffect, useState } from 'react'; -import { api } from '@/app/_trpc/client'; import { Button } from '@/components/ui/button'; import { Checkbox } from '@/components/ui/checkbox'; import { Label } from '@/components/ui/label'; @@ -12,6 +11,7 @@ import { SheetTrigger, } from '@/components/ui/sheet'; import { Tooltip, TooltipContent } from '@/components/ui/tooltip'; +import { api } from '@/trpc/client'; import { cn } from '@/utils/cn'; import { TooltipTrigger } from '@radix-ui/react-tooltip'; import type { VariantProps } from 'class-variance-authority'; diff --git a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/layout-organization-selector.tsx b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/layout-organization-selector.tsx index 692ff0f7..e540c025 100644 --- a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/layout-organization-selector.tsx +++ b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/layout-organization-selector.tsx @@ -36,7 +36,7 @@ export default function LayoutOrganizationSelector({ .filter((item) => item.slug) .map((item) => ({ label: item.name, - value: item.slug!, + value: item.slug, })) ?? [] } onChange={(value) => { diff --git a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/organization/edit-organization.tsx b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/organization/edit-organization.tsx index 518be8e5..7a8febba 100644 --- a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/organization/edit-organization.tsx +++ b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/organization/edit-organization.tsx @@ -1,9 +1,9 @@ 'use client'; -import { api, handleError } from '@/app/_trpc/client'; import { InputWithLabel } from '@/components/forms/input-with-label'; import { Button } from '@/components/ui/button'; import { Widget, WidgetBody, WidgetHead } from '@/components/widget'; +import { api, handleError } from '@/trpc/client'; import { useRouter } from 'next/navigation'; import { useForm } from 'react-hook-form'; import { toast } from 'sonner'; diff --git a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/organization/invites/create-invite.tsx b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/organization/invites/create-invite.tsx index 701c0f04..6b292339 100644 --- a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/organization/invites/create-invite.tsx +++ b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/organization/invites/create-invite.tsx @@ -1,4 +1,3 @@ -import { api } from '@/app/_trpc/client'; import { InputWithLabel } from '@/components/forms/input-with-label'; import { Button } from '@/components/ui/button'; import { ComboboxAdvanced } from '@/components/ui/combobox-advanced'; @@ -15,6 +14,7 @@ import { SheetTrigger, } from '@/components/ui/sheet'; import { useAppParams } from '@/hooks/useAppParams'; +import { api } from '@/trpc/client'; import { zodResolver } from '@hookform/resolvers/zod'; import { PlusIcon, SendIcon } from 'lucide-react'; import { useRouter } from 'next/navigation'; diff --git a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/organization/members/members.tsx b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/organization/members/members.tsx index 4fd1d6b5..e3c2e49b 100644 --- a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/organization/members/members.tsx +++ b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/organization/members/members.tsx @@ -1,7 +1,6 @@ 'use client'; import { useState } from 'react'; -import { api } from '@/app/_trpc/client'; import { ComboboxAdvanced } from '@/components/ui/combobox-advanced'; import { Table, @@ -12,6 +11,7 @@ import { TableRow, } from '@/components/ui/table'; import { Widget, WidgetHead } from '@/components/widget'; +import { api } from '@/trpc/client'; import type { IServiceMember, IServiceProject } from '@openpanel/db'; diff --git a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/profile/edit-profile.tsx b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/profile/edit-profile.tsx index 72072a56..5d6af336 100644 --- a/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/profile/edit-profile.tsx +++ b/apps/dashboard/src/app/(app)/[organizationId]/[projectId]/settings/profile/edit-profile.tsx @@ -1,9 +1,9 @@ 'use client'; -import { api, handleError } from '@/app/_trpc/client'; import { InputWithLabel } from '@/components/forms/input-with-label'; import { Button } from '@/components/ui/button'; import { Widget, WidgetBody, WidgetHead } from '@/components/widget'; +import { api, handleError } from '@/trpc/client'; import { zodResolver } from '@hookform/resolvers/zod'; import { useRouter } from 'next/navigation'; import { useForm } from 'react-hook-form'; diff --git a/apps/dashboard/src/app/(app)/[organizationId]/create-project.tsx b/apps/dashboard/src/app/(app)/[organizationId]/create-project.tsx index 4990d30e..df131a3d 100644 --- a/apps/dashboard/src/app/(app)/[organizationId]/create-project.tsx +++ b/apps/dashboard/src/app/(app)/[organizationId]/create-project.tsx @@ -5,6 +5,7 @@ import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { useAppParams } from '@/hooks/useAppParams'; +import { api, handleError } from '@/trpc/client'; import { zodResolver } from '@hookform/resolvers/zod'; import { SaveIcon } from 'lucide-react'; import { useRouter } from 'next/navigation'; @@ -13,8 +14,6 @@ import { useForm } from 'react-hook-form'; import { toast } from 'sonner'; import { z } from 'zod'; -import { api, handleError } from '../../_trpc/client'; - const validation = z.object({ name: z.string().min(1), }); diff --git a/apps/dashboard/src/app/(app)/[organizationId]/page.tsx b/apps/dashboard/src/app/(app)/[organizationId]/page.tsx index 655fd66b..deaeb033 100644 --- a/apps/dashboard/src/app/(app)/[organizationId]/page.tsx +++ b/apps/dashboard/src/app/(app)/[organizationId]/page.tsx @@ -5,7 +5,6 @@ import { notFound, redirect } from 'next/navigation'; import { getCurrentProjects, getOrganizationBySlug, - getProjectsByOrganizationSlug, isWaitlistUserAccepted, } from '@openpanel/db'; @@ -36,8 +35,8 @@ export default async function Page({ params: { organizationId } }: PageProps) {

Not quite there yet

- We're still working on Openpanel, but we're not quite there yet. - We'll let you know when we're ready to go! + We're still working on Openpanel, but we're not quite + there yet. We'll let you know when we're ready to go!
diff --git a/apps/dashboard/src/app/(app)/create-organization.tsx b/apps/dashboard/src/app/(app)/create-organization.tsx index c2559edf..9d4d5d59 100644 --- a/apps/dashboard/src/app/(app)/create-organization.tsx +++ b/apps/dashboard/src/app/(app)/create-organization.tsx @@ -6,6 +6,7 @@ import { Button, buttonVariants } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { api, handleError } from '@/trpc/client'; import { cn } from '@/utils/cn'; import { zodResolver } from '@hookform/resolvers/zod'; import { SaveIcon, WallpaperIcon } from 'lucide-react'; @@ -14,8 +15,6 @@ import type { SubmitHandler } from 'react-hook-form'; import { useForm } from 'react-hook-form'; import { z } from 'zod'; -import { api, handleError } from '../_trpc/client'; - const validation = z .object({ organization: z.string().min(3), @@ -61,8 +60,8 @@ export function CreateOrganization() {

Nice job!

- You're ready to start using our SDK. Save the client ID and secret (if - you have any) + You're ready to start using our SDK. Save the client ID and + secret (if you have any)
diff --git a/apps/dashboard/src/app/(app)/page.tsx b/apps/dashboard/src/app/(app)/page.tsx index 46c3948a..bab92454 100644 --- a/apps/dashboard/src/app/(app)/page.tsx +++ b/apps/dashboard/src/app/(app)/page.tsx @@ -18,8 +18,8 @@ export default async function Page() {

Not quite there yet

- We're still working on Openpanel, but we're not quite there yet. - We'll let you know when we're ready to go! + We're still working on Openpanel, but we're not quite + there yet. We'll let you know when we're ready to go!
diff --git a/apps/dashboard/src/app/api/clerk/webhook/route.ts b/apps/dashboard/src/app/api/clerk/webhook/route.ts index 7f4ab6ad..0aa9adc3 100644 --- a/apps/dashboard/src/app/api/clerk/webhook/route.ts +++ b/apps/dashboard/src/app/api/clerk/webhook/route.ts @@ -32,6 +32,6 @@ export async function POST(request: Request) { return Response.json({ message: 'Webhook received!' }); } -export async function GET() { +export function GET() { return Response.json({ message: 'Hello World!' }); } diff --git a/apps/dashboard/src/app/api/trpc/[trpc]/route.ts b/apps/dashboard/src/app/api/trpc/[trpc]/route.ts index ca979c6e..d6c26d34 100644 --- a/apps/dashboard/src/app/api/trpc/[trpc]/route.ts +++ b/apps/dashboard/src/app/api/trpc/[trpc]/route.ts @@ -1,4 +1,4 @@ -import { appRouter } from '@/server/api/root'; +import { appRouter } from '@/trpc/api/root'; import { getAuth } from '@clerk/nextjs/server'; import { fetchRequestHandler } from '@trpc/server/adapters/fetch'; @@ -7,7 +7,7 @@ const handler = (req: Request) => endpoint: '/api/trpc', req, router: appRouter, - async createContext({ req }) { + createContext({ req }) { const session = getAuth(req as any); return { session, diff --git a/apps/dashboard/src/app/global-error.jsx b/apps/dashboard/src/app/global-error.jsx deleted file mode 100644 index 2e6130a1..00000000 --- a/apps/dashboard/src/app/global-error.jsx +++ /dev/null @@ -1,19 +0,0 @@ -"use client"; - -import * as Sentry from "@sentry/nextjs"; -import Error from "next/error"; -import { useEffect } from "react"; - -export default function GlobalError({ error }) { - useEffect(() => { - Sentry.captureException(error); - }, [error]); - - return ( - - - - - - ); -} diff --git a/apps/dashboard/src/app/global-error.tsx b/apps/dashboard/src/app/global-error.tsx new file mode 100644 index 00000000..8aa74d7b --- /dev/null +++ b/apps/dashboard/src/app/global-error.tsx @@ -0,0 +1,23 @@ +'use client'; + +import { useEffect } from 'react'; +import * as Sentry from '@sentry/nextjs'; + +export default function GlobalError({ + error, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useEffect(() => { + Sentry.captureException(error); + }, [error]); + + return ( + + +

Something went wrong

+ + + ); +} diff --git a/apps/dashboard/src/app/providers.tsx b/apps/dashboard/src/app/providers.tsx index 9c7868af..0fb0149f 100644 --- a/apps/dashboard/src/app/providers.tsx +++ b/apps/dashboard/src/app/providers.tsx @@ -1,11 +1,11 @@ 'use client'; import React, { useRef, useState } from 'react'; -import { api } from '@/app/_trpc/client'; import { TooltipProvider } from '@/components/ui/tooltip'; import { ModalProvider } from '@/modals'; import type { AppStore } from '@/redux'; import makeStore from '@/redux'; +import { api } from '@/trpc/client'; import { ClerkProvider, useAuth } from '@clerk/nextjs'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { httpLink } from '@trpc/client'; diff --git a/apps/dashboard/src/components/clients/client-actions.tsx b/apps/dashboard/src/components/clients/client-actions.tsx index cf163685..f4bdc151 100644 --- a/apps/dashboard/src/components/clients/client-actions.tsx +++ b/apps/dashboard/src/components/clients/client-actions.tsx @@ -1,7 +1,7 @@ 'use client'; -import { api } from '@/app/_trpc/client'; import { pushModal, showConfirm } from '@/modals'; +import { api } from '@/trpc/client'; import { clipboard } from '@/utils/clipboard'; import { MoreHorizontal } from 'lucide-react'; import { useRouter } from 'next/navigation'; diff --git a/apps/dashboard/src/components/dark-mode-toggle.tsx b/apps/dashboard/src/components/dark-mode-toggle.tsx index 9368d229..dff96bc0 100644 --- a/apps/dashboard/src/components/dark-mode-toggle.tsx +++ b/apps/dashboard/src/components/dark-mode-toggle.tsx @@ -11,13 +11,17 @@ import { import { MoonIcon, SunIcon } from 'lucide-react'; import { useTheme } from 'next-themes'; -export default function DarkModeToggle() { +interface Props { + className?: string; +} + +export default function DarkModeToggle({ className }: Props) { const { setTheme } = useTheme(); return ( - - - - ); -} diff --git a/apps/test/src/app/app-dir/page.tsx b/apps/test/src/app/app-dir/page.tsx deleted file mode 100644 index 5bb787c8..00000000 --- a/apps/test/src/app/app-dir/page.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import { getClientIp, parseIp } from '@/utils/parseIp'; -import { isUserAgentSet, parseUserAgent } from '@/utils/parseUserAgent'; -import type { FastifyReply, FastifyRequest } from 'fastify'; -import { assocPath, pathOr } from 'ramda'; - -import { getProfileById, upsertProfile } from '@openpanel/db'; -import type { - IncrementProfilePayload, - UpdateProfilePayload, -} from '@openpanel/sdk'; - -export async function updateProfile( - request: FastifyRequest<{ - Body: UpdateProfilePayload; - }>, - reply: FastifyReply -) { - const { profileId, properties, ...rest } = request.body; - const projectId = request.projectId; - const ip = getClientIp(request)!; - const ua = request.headers['user-agent']!; - const uaInfo = parseUserAgent(ua); - const geo = await parseIp(ip); - - await upsertProfile({ - id: profileId, - projectId, - properties: { - ...(properties ?? {}), - ...(ip ? geo : {}), - ...(isUserAgentSet(ua) ? uaInfo : {}), - }, - ...rest, - }); - - reply.status(202).send(profileId); -} - -export async function incrementProfileProperty( - request: FastifyRequest<{ - Body: IncrementProfilePayload; - }>, - reply: FastifyReply -) { - const { profileId, property, value } = request.body; - const projectId = request.projectId; - - const profile = await getProfileById(profileId); - if (!profile) { - return reply.status(404).send('Not found'); - } - - const parsed = parseInt( - pathOr('0', property.split('.'), profile.properties), - 10 - ); - - if (isNaN(parsed)) { - return reply.status(400).send('Not number'); - } - - profile.properties = assocPath( - property.split('.'), - parsed + value, - profile.properties - ); - - await upsertProfile({ - id: profile.id, - projectId, - properties: profile.properties, - }); - - reply.status(202).send(profile.id); -} - -export async function decrementProfileProperty( - request: FastifyRequest<{ - Body: IncrementProfilePayload; - }>, - reply: FastifyReply -) { - const { profileId, property, value } = request.body; - const projectId = request.projectId; - - const profile = await getProfileById(profileId); - if (!profile) { - return reply.status(404).send('Not found'); - } - - const parsed = parseInt( - pathOr('0', property.split('.'), profile.properties), - 10 - ); - - if (isNaN(parsed)) { - return reply.status(400).send('Not number'); - } - - profile.properties = assocPath( - property.split('.'), - parsed - value, - profile.properties - ); - - await upsertProfile({ - id: profile.id, - projectId, - properties: profile.properties, - }); - - reply.status(202).send(profile.id); -} diff --git a/apps/test/src/app/layout.tsx b/apps/test/src/app/layout.tsx deleted file mode 100644 index f15c9cd8..00000000 --- a/apps/test/src/app/layout.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { OpenpanelProvider } from '@openpanel-test/nextjs'; - -export default function Layout({ children }: { children: React.ReactNode }) { - return ( - - - - - - - - {children} - - - ); -} diff --git a/apps/test/src/pages/_app.tsx b/apps/test/src/pages/_app.tsx deleted file mode 100644 index 22c38e90..00000000 --- a/apps/test/src/pages/_app.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { OpenpanelProvider } from '@openpanel-test/nextjs'; -import type { AppProps } from 'next/app'; - -export default function MyApp({ Component, pageProps }: AppProps) { - return ( - <> - - - - ); -} diff --git a/apps/test/src/pages/index.tsx b/apps/test/src/pages/index.tsx deleted file mode 100644 index 396a6ece..00000000 --- a/apps/test/src/pages/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import Link from 'next/link'; - -export default function Home() { - return ( - - ); -} diff --git a/apps/test/src/pages/test.tsx b/apps/test/src/pages/test.tsx deleted file mode 100644 index d93d4241..00000000 --- a/apps/test/src/pages/test.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { useEffect, useState } from 'react'; -import { - clear, - decrement, - increment, - setProfile, - trackEvent, -} from '@openpanel-test/nextjs'; -import Link from 'next/link'; - -export default function Test() { - const [id, setId] = useState(''); - const [auth, setAuth] = useState(null); - - function handleLogin() { - if (id) { - localStorage.setItem('auth', id); - setAuth(id); - } - } - - function handleLogout() { - localStorage.removeItem('auth'); - setAuth(null); - } - - useEffect(() => { - setAuth(localStorage.getItem('auth') ?? null); - }, []); - - useEffect(() => { - console.log('auth', auth); - - if (auth) { - console.log('set profile?', auth); - - setProfile({ - profileId: auth, - }); - } else { - clear(); - } - }, [auth]); - - if (auth === null) { - return ( -
- setId(e.target.value)} - /> - -
- ); - } - return ( -
- Home - - - - - - - -
- {['a', 'b', 'c', 'd', 'f'].map((letter) => ( - - ))} -
-
- ); -} diff --git a/apps/test/tailwind.config.js b/apps/test/tailwind.config.js deleted file mode 100644 index 931a5665..00000000 --- a/apps/test/tailwind.config.js +++ /dev/null @@ -1,102 +0,0 @@ -const colors = [ - '#7856ff', - '#ff7557', - '#7fe1d8', - '#f8bc3c', - '#b3596e', - '#72bef4', - '#ffb27a', - '#0f7ea0', - '#3ba974', - '#febbb2', - '#cb80dc', - '#5cb7af', -]; - -/** @type {import('tailwindcss').Config} */ -const config = { - safelist: [...colors.map((color) => `chart-${color}`)], - content: [ - './pages/**/*.{ts,tsx}', - './components/**/*.{ts,tsx}', - './app/**/*.{ts,tsx}', - './src/**/*.{ts,tsx}', - ], - theme: { - container: { - center: true, - padding: '2rem', - screens: { - '2xl': '1400px', - }, - }, - extend: { - colors: { - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', - primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))', - }, - secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))', - }, - destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))', - }, - muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))', - }, - accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))', - }, - popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))', - }, - card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))', - }, - ...colors.reduce((acc, color, index) => { - return { - ...acc, - [`chart-${index}`]: color, - }; - }, {}), - }, - borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)', - }, - boxShadow: { - DEFAULT: '0 5px 10px rgb(0 0 0 / 5%)', - }, - keyframes: { - 'accordion-down': { - from: { height: '0px' }, - to: { height: 'var(--radix-accordion-content-height)' }, - }, - 'accordion-up': { - from: { height: 'var(--radix-accordion-content-height)' }, - to: { height: '0px' }, - }, - }, - animation: { - 'accordion-down': 'accordion-down 0.2s ease-out', - 'accordion-up': 'accordion-up 0.2s ease-out', - }, - }, - }, - plugins: [require('tailwindcss-animate')], -}; - -export default config; diff --git a/apps/test/tsconfig.json b/apps/test/tsconfig.json deleted file mode 100644 index d17f2ec2..00000000 --- a/apps/test/tsconfig.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "@openpanel/tsconfig/base.json", - "compilerOptions": { - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - }, - "plugins": [ - { - "name": "next" - } - ], - "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json", - "strictNullChecks": true - }, - "include": [".", ".next/types/**/*.ts"], - "exclude": ["node_modules"] -} diff --git a/apps/worker/tsconfig.json b/apps/worker/tsconfig.json index d17f2ec2..6f9c5b17 100644 --- a/apps/worker/tsconfig.json +++ b/apps/worker/tsconfig.json @@ -13,6 +13,6 @@ "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json", "strictNullChecks": true }, - "include": [".", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": ["."], + "exclude": ["node_modules", "dist"] } diff --git a/packages/db/scripts/insert.ts b/packages/db/scripts/insert.ts deleted file mode 100644 index 5f270b23..00000000 --- a/packages/db/scripts/insert.ts +++ /dev/null @@ -1,162 +0,0 @@ -// @ts-nocheck - -import { createEvent } from '@/services/event.service'; -import { last, omit } from 'ramda'; - -import type { Event } from '../src/prisma-client'; -import { db } from '../src/prisma-client'; - -async function push(event: Event) { - if (event.properties.ip && Number.isNaN(parseInt(event.properties.ip[0]))) { - return console.log('IGNORE', event.id); - } - await fetch('http://localhost:3030/api/event', { - method: 'POST', - body: JSON.stringify({ - name: event.name, - timestamp: event.createdAt.toISOString(), - path: event.properties.path, - properties: omit( - [ - 'ip', - 'os', - 'ua', - 'url', - 'hash', - 'host', - 'path', - 'device', - 'screen', - 'hostname', - 'language', - 'referrer', - 'timezone', - ], - event.properties - ), - referrer: event.properties?.referrer?.host ?? undefined, - }), - headers: { - 'Content-Type': 'application/json', - 'User-Agent': event.properties.ua, - 'X-Forwarded-For': event.properties.ip, - 'openpanel-client-id': 'c8b4962e-bc3d-4b23-8ea4-505c8fbdf09e', - origin: 'https://openpanel.kiddo.se', - }, - }).catch(() => {}); -} - -async function main() { - const events = await db.event.findMany({ - where: { - project_id: '4e2798cb-e255-4e9d-960d-c9ad095aabd7', - name: 'screen_view', - createdAt: { - gte: new Date('2024-01-14'), - lt: new Date('2024-01-18'), - }, - }, - orderBy: { - createdAt: 'asc', - }, - }); - - const grouped: Record = {}; - let index = 0; - for (const event of events.slice()) { - console.log(index, event.name, event.createdAt.toISOString()); - index++; - - if (event.properties.ua?.includes('bot')) { - console.log('IGNORE', event.id); - continue; - } - - if (grouped[event.profile_id]) { - grouped[event.profile_id].push(event); - } else { - grouped[event.profile_id] = [event]; - } - } - - for (const profile_id of Object.keys(grouped).slice(0, 10)) { - const events = grouped[profile_id]; - - if (events) { - console.log('new user...'); - let eidx = -1; - for (const event of events) { - eidx++; - const lastEventAt = events[eidx - 1]?.createdAt; - - const profileId: string | null = null; - const projectId = event.project_id; - const path = event.properties.path as string; - const ip = event.properties.ip as string; - const origin = 'https://openpanel.kiddo.se'; - const ua = event.properties.ua as string; - const uaInfo = parseUserAgent(ua); - const salts = await getSalts(); - const currentProfileId = generateProfileId({ - salt: salts.current, - origin, - ip, - ua, - }); - const previousProfileId = generateProfileId({ - salt: salts.previous, - origin, - ip, - ua, - }); - - const [geo, eventsJobs] = Promise.all([ - parseIp(ip), - eventsQueue.getJobs(['delayed']), - ]); - const payload: IServiceCreateEventPayload = { - name: body.name, - profileId, - projectId, - properties: body.properties, - createdAt: body.timestamp, - country: geo.country, - city: geo.city, - region: geo.region, - continent: geo.continent, - os: uaInfo.os, - osVersion: uaInfo.osVersion, - browser: uaInfo.browser, - browserVersion: uaInfo.browserVersion, - device: uaInfo.device, - brand: uaInfo.brand, - model: uaInfo.model, - duration: 0, - path, - referrer: body.referrer, // TODO - referrerName: body.referrer, // TODO - }; - - if (!lastEventAt) { - createEvent({}); - continue; - } - - if ( - event.createdAt.getTime() - lastEventAt.getTime() > - 1000 * 60 * 30 - ) { - console.log( - 'new Session?', - event.createdAt.toISOString(), - event.properties.path - ); - } else { - console.log('Same session?'); - } - } - } - } -} - -main(); diff --git a/packages/db/scripts/test-funnel.ts b/packages/db/scripts/test-funnel.ts deleted file mode 100644 index 9bc50ba8..00000000 --- a/packages/db/scripts/test-funnel.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { createEvent } from '../src/services/event.service'; - -function c(name: string, createdAt: Date, session_id: string) { - return createEvent({ - name, - deviceId: '', - profileId: '', - projectId: '', - sessionId: session_id, - properties: {}, - createdAt, - country: '', - city: '', - region: '', - continent: '', - os: '', - osVersion: '', - browser: '', - browserVersion: '', - device: '', - brand: '', - model: '', - duration: 0, - path: '/', - referrer: '', - referrerName: '', - referrerType: '', - profile: undefined, - meta: undefined, - }); -} - -async function main() { - // Level 5 - const s = Math.random().toString(36).substring(7); - await c('session_start', new Date('2024-02-24 10:10:00'), s); - - // // Level 2 - // s = 's2'; - // await c('session_start', new Date('2024-02-24 10:10:00'), ''); - // await c('a', new Date('2024-02-24 10:10:00'), s); - // await c('b', new Date('2024-02-24 10:10:02'), s); - - // // Level 5 - // s = 's3'; - // await c('session_start', new Date('2024-02-24 10:10:00'), ''); - // await c('a', new Date('2024-02-24 10:10:00'), s); - // await c('b', new Date('2024-02-24 10:10:02'), s); - // await c('c', new Date('2024-02-24 10:10:03'), s); - // await c('d', new Date('2024-02-24 10:10:04'), s); - // await c('f', new Date('2024-02-24 10:10:10'), s); - - // // Level 4 - // s = 's4'; - // await c('session_start', new Date('2024-02-24 10:10:00'), ''); - // await c('a', new Date('2024-02-24 10:10:00'), s); - // await c('b', new Date('2024-02-24 10:10:02'), s); - // await c('c', new Date('2024-02-24 10:10:03'), s); - // await c('d', new Date('2024-02-24 10:10:04'), s); - - // // Level 3 - // s = 's5'; - // await c('session_start', new Date('2024-02-24 10:10:00'), ''); - // await c('a', new Date('2024-02-24 10:10:00'), s); - // await c('b', new Date('2024-02-24 10:10:02'), s); - // await c('c', new Date('2024-02-24 10:10:03'), s); - - // // Level 3 - // s = 's6'; - // await c('session_start', new Date('2024-02-24 10:10:00'), ''); - // await c('a', new Date('2024-02-24 10:10:00'), s); - // await c('b', new Date('2024-02-24 10:10:02'), s); - // await c('c', new Date('2024-02-24 10:10:03'), s); - - // // Level 2 - // s = 's7'; - // await c('session_start', new Date('2024-02-24 10:10:00'), ''); - // await c('a', new Date('2024-02-24 10:10:00'), s); - // await c('b', new Date('2024-02-24 10:10:02'), s); - - // // Level 5 - // s = 's8'; - // await c('session_start', new Date('2024-02-24 10:10:00'), ''); - // await c('a', new Date('2024-02-24 10:10:00'), s); - // await c('b', new Date('2024-02-24 10:10:02'), s); - // await c('c', new Date('2024-02-24 10:10:03'), s); - // await c('d', new Date('2024-02-24 10:10:04'), s); - // await c('f', new Date('2024-02-24 10:10:10'), s); - - // // Level 4 - // s = 's9'; - // await c('session_start', new Date('2024-02-24 10:10:00'), ''); - // await c('a', new Date('2024-02-24 10:10:00'), s); - // await c('b', new Date('2024-02-24 10:10:02'), s); - // await c('c', new Date('2024-02-24 10:10:03'), s); - // await c('d', new Date('2024-02-24 10:10:04'), s); - - process.exit(); -} - -main(); diff --git a/packages/sdks/nextjs/tsconfig.json b/packages/sdks/nextjs/tsconfig.json index 145f03d2..fa4341f1 100644 --- a/packages/sdks/nextjs/tsconfig.json +++ b/packages/sdks/nextjs/tsconfig.json @@ -3,5 +3,6 @@ "compilerOptions": { "incremental": false, "outDir": "dist" - } + }, + "exclude": ["dist"] } diff --git a/packages/sdks/react-native/tsconfig.json b/packages/sdks/react-native/tsconfig.json index 145f03d2..fa4341f1 100644 --- a/packages/sdks/react-native/tsconfig.json +++ b/packages/sdks/react-native/tsconfig.json @@ -3,5 +3,6 @@ "compilerOptions": { "incremental": false, "outDir": "dist" - } + }, + "exclude": ["dist"] } diff --git a/packages/sdks/sdk/tsconfig.json b/packages/sdks/sdk/tsconfig.json index 145f03d2..fa4341f1 100644 --- a/packages/sdks/sdk/tsconfig.json +++ b/packages/sdks/sdk/tsconfig.json @@ -3,5 +3,6 @@ "compilerOptions": { "incremental": false, "outDir": "dist" - } + }, + "exclude": ["dist"] } diff --git a/packages/sdks/web/cdn.ts b/packages/sdks/web/cdn.ts index 2f456717..b9ce999e 100644 --- a/packages/sdks/web/cdn.ts +++ b/packages/sdks/web/cdn.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-unsafe-call */ + import { Openpanel } from './index'; declare global { diff --git a/packages/sdks/web/index.ts b/packages/sdks/web/index.ts index 7a32ca4a..cf2cb08f 100644 --- a/packages/sdks/web/index.ts +++ b/packages/sdks/web/index.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/unbound-method */ + import type { OpenpanelSdkOptions, PostEventPayload } from '@openpanel/sdk'; import { OpenpanelSdk } from '@openpanel/sdk'; diff --git a/packages/sdks/web/tsconfig.json b/packages/sdks/web/tsconfig.json index 145f03d2..fa4341f1 100644 --- a/packages/sdks/web/tsconfig.json +++ b/packages/sdks/web/tsconfig.json @@ -3,5 +3,6 @@ "compilerOptions": { "incremental": false, "outDir": "dist" - } + }, + "exclude": ["dist"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 63822359..36e8af58 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -628,73 +628,6 @@ importers: specifier: ^5.2.2 version: 5.3.3 - apps/test: - dependencies: - '@openpanel-test/nextjs': - specifier: workspace:@openpanel/nextjs@* - version: link:../../packages/sdks/nextjs - '@openpanel-test/sdk': - specifier: workspace:@openpanel/sdk@* - version: link:../../packages/sdks/sdk - '@openpanel-test/web': - specifier: workspace:@openpanel/web@* - version: link:../../packages/sdks/web - next: - specifier: ~14.1.0 - version: 14.1.0(react-dom@18.2.0)(react@18.2.0) - react: - specifier: 18.2.0 - version: 18.2.0 - react-dom: - specifier: 18.2.0 - version: 18.2.0(react@18.2.0) - devDependencies: - '@openpanel/eslint-config': - specifier: workspace:* - version: link:../../tooling/eslint - '@openpanel/prettier-config': - specifier: workspace:* - version: link:../../tooling/prettier - '@openpanel/tsconfig': - specifier: workspace:* - version: link:../../tooling/typescript - '@types/react': - specifier: ^18.2.20 - version: 18.2.56 - '@types/react-dom': - specifier: ^18.2.7 - version: 18.2.19 - '@types/react-syntax-highlighter': - specifier: ^15.5.9 - version: 15.5.11 - '@typescript-eslint/eslint-plugin': - specifier: ^6.6.0 - version: 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/parser': - specifier: ^6.6.0 - version: 6.21.0(eslint@8.56.0)(typescript@5.3.3) - autoprefixer: - specifier: ^10.4.14 - version: 10.4.17(postcss@8.4.35) - eslint: - specifier: ^8.48.0 - version: 8.56.0 - postcss: - specifier: ^8.4.27 - version: 8.4.35 - prettier: - specifier: ^3.0.3 - version: 3.2.5 - prettier-plugin-tailwindcss: - specifier: ^0.5.1 - version: 0.5.11(@ianvs/prettier-plugin-sort-imports@4.1.1)(prettier@3.2.5) - tailwindcss: - specifier: ^3.3.3 - version: 3.4.1 - typescript: - specifier: ^5.2.2 - version: 5.3.3 - apps/worker: dependencies: '@bull-board/api': @@ -821,7 +754,7 @@ importers: dependencies: '@clerk/nextjs': specifier: ^4.29.7 - version: 4.29.7(next@14.1.0)(react-dom@18.2.0)(react@18.2.0) + version: 4.29.7(next@14.0.4)(react-dom@18.2.0)(react@18.2.0) '@clickhouse/client': specifier: ^0.2.9 version: 0.2.9 @@ -2811,26 +2744,6 @@ packages: tslib: 2.4.1 dev: false - /@clerk/nextjs@4.29.7(next@14.1.0)(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-tPvIp4GXCsjcKankLRpPPQGDWmpmlB2tm+p656/OUUmzPMeDnk5Euc86HjSk+5C9BAHVatrveRth6fHa4yzNhQ==} - engines: {node: '>=14'} - peerDependencies: - next: '>=10' - react: ^17.0.2 || ^18.0.0-0 - react-dom: ^17.0.2 || ^18.0.0-0 - dependencies: - '@clerk/backend': 0.38.1(react@18.2.0) - '@clerk/clerk-react': 4.30.5(react@18.2.0) - '@clerk/clerk-sdk-node': 4.13.9(react@18.2.0) - '@clerk/shared': 1.3.1(react@18.2.0) - '@clerk/types': 3.62.0 - next: 14.1.0(react-dom@18.2.0)(react@18.2.0) - path-to-regexp: 6.2.1 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - tslib: 2.4.1 - dev: false - /@clerk/shared@1.3.1(react@18.2.0): resolution: {integrity: sha512-nzv4+uA90I/eQp55zfK9a1Po9VgCYlzlNhuZnKqyRsPyJ38l4gpIf3B3qSHHdN0+MTx9cWGFrik1CnpftdOBXQ==} peerDependencies: diff --git a/tooling/eslint/base.js b/tooling/eslint/base.js index 5954b0a1..7ad6389d 100644 --- a/tooling/eslint/base.js +++ b/tooling/eslint/base.js @@ -47,6 +47,7 @@ const config = { '**/*.config.cjs', '.next', 'dist', + 'public', 'pnpm-lock.yaml', ], reportUnusedDisableDirectives: true, diff --git a/tooling/eslint/react.js b/tooling/eslint/react.js index 5b657e14..3324e646 100644 --- a/tooling/eslint/react.js +++ b/tooling/eslint/react.js @@ -8,7 +8,8 @@ const config = { rules: { 'react/prop-types': 'off', 'react/no-unknown-property': 'off', - 'jsx-a11y/click-events-have-key-events': 'off' + 'jsx-a11y/click-events-have-key-events': 'off', + 'jsx-a11y/label-has-associated-control': 'off', }, globals: { React: 'writable', diff --git a/tooling/publish/publish.ts b/tooling/publish/publish.ts index 9206e9af..9889093b 100644 --- a/tooling/publish/publish.ts +++ b/tooling/publish/publish.ts @@ -81,6 +81,7 @@ function main() { delete properties.type; } + // eslint-disable-next-line @typescript-eslint/no-var-requires const pkgJson = require( workspacePath(`./packages/sdks/${name}/package.json`) );