From ead1cb26b98b526c69b0225ce4fb9c79ab0060af Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Wed, 20 Nov 2024 13:03:43 -0300 Subject: [PATCH 01/17] getFinalResult works for workflows that didn't produce any code --- packages/ai/src/workflows/Workflow.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/ai/src/workflows/Workflow.ts b/packages/ai/src/workflows/Workflow.ts index a08adbbb09..6df7c3e841 100644 --- a/packages/ai/src/workflows/Workflow.ts +++ b/packages/ai/src/workflows/Workflow.ts @@ -316,32 +316,32 @@ export class Workflow { getFinalResult(): ClientWorkflowResult { const finalStep = this.getRecentStepWithCode(); - if (!finalStep) { - throw new Error("No steps found"); - } - - const isValid = finalStep.step.getState().kind === "DONE"; + const isValid = finalStep?.step.getState().kind === "DONE"; + // compute run time + let runTimeMs: number; const lastStep = this.steps.at(-1); if (!lastStep) { throw new Error("No steps found"); } - const lastStepState = finalStep.step.getState(); - if (lastStepState.kind === "PENDING") { - throw new Error("Last step is still pending"); - } + { + const lastStepState = lastStep.getState(); + if (lastStepState.kind === "PENDING") { + throw new Error("Last step is still pending"); + } - const startTime = this.steps[0].startTime; - const endTime = lastStep.startTime + lastStepState.durationMs; - const runTimeMs = endTime - startTime; + const startTime = this.steps[0].startTime; + const endTime = lastStep.startTime + lastStepState.durationMs; + runTimeMs = endTime - startTime; + } const { totalPrice, llmRunCount } = this.getLlmMetrics(); const logSummary = generateSummary(this); return { - code: finalStep.code, + code: finalStep?.code ?? "", isValid, totalPrice, runTimeMs, From 92ec2448fbc6f6857beb342c91dae07049264987 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Wed, 20 Nov 2024 13:42:39 -0300 Subject: [PATCH 02/17] detailed step states in streaming protocol --- packages/ai/src/types.ts | 18 +++++-- packages/ai/src/workflows/streaming.ts | 33 ++++++------ packages/hub/src/app/ai/AiDashboard.tsx | 3 +- packages/hub/src/app/ai/Sidebar.tsx | 43 ++++++++++++---- packages/hub/src/app/ai/StepStatusIcon.tsx | 16 ++++-- ...tedNodeSideView.tsx => ClientStepView.tsx} | 47 ++++++++++------- .../src/app/ai/WorkflowViewer/StepNode.tsx | 2 +- .../app/ai/WorkflowViewer/WorkflowActions.tsx | 50 ++++++++----------- .../hub/src/app/ai/WorkflowViewer/index.tsx | 6 +-- packages/hub/src/app/ai/api/create/route.ts | 5 +- packages/hub/src/app/ai/utils.ts | 1 + packages/hub/src/server/ai/v1_0.ts | 20 ++++++-- 12 files changed, 153 insertions(+), 91 deletions(-) rename packages/hub/src/app/ai/WorkflowViewer/{SelectedNodeSideView.tsx => ClientStepView.tsx} (71%) diff --git a/packages/ai/src/types.ts b/packages/ai/src/types.ts index e92ad454b7..d5d0a84d6a 100644 --- a/packages/ai/src/types.ts +++ b/packages/ai/src/types.ts @@ -32,7 +32,20 @@ export type ClientArtifact = z.infer; // ClientStep type -const stepStateSchema = z.enum(["PENDING", "DONE", "FAILED"]); +export const stepStateSchema = z.discriminatedUnion("kind", [ + z.object({ + kind: z.literal("PENDING"), + }), + z.object({ + kind: z.literal("DONE"), + outputs: z.record(z.string(), artifactSchema), + }), + z.object({ + kind: z.literal("FAILED"), + message: z.string(), + errorType: z.enum(["CRITICAL", "MINOR"]), + }), +]); const messageSchema = z.object({ role: z.enum(["system", "user", "assistant"]), @@ -46,7 +59,6 @@ const stepSchema = z.object({ name: z.string(), state: stepStateSchema, inputs: z.record(z.string(), artifactSchema), - outputs: z.record(z.string(), artifactSchema), messages: z.array(messageSchema), }); @@ -65,14 +77,12 @@ const workflowStartedSchema = z.object({ const stepAddedSchema = stepSchema.omit({ state: true, - outputs: true, messages: true, }); const stepUpdatedSchema = stepSchema.partial().required({ id: true, messages: true, - outputs: true, }); // WorkflowResult type diff --git a/packages/ai/src/workflows/streaming.ts b/packages/ai/src/workflows/streaming.ts index 1cf69ba8dd..056264e213 100644 --- a/packages/ai/src/workflows/streaming.ts +++ b/packages/ai/src/workflows/streaming.ts @@ -5,7 +5,7 @@ import { import { Artifact } from "../Artifact.js"; import { type LLMStepInstance } from "../LLMStepInstance.js"; -import { IOShape } from "../LLMStepTemplate.js"; +import { IOShape, StepState } from "../LLMStepTemplate.js"; import { ClientArtifact, ClientStep, @@ -45,34 +45,37 @@ export function artifactToClientArtifact(value: Artifact): ClientArtifact { } } -function getClientOutputs( - step: LLMStepInstance -): Record { - const stepState = step.getState(); - return stepState.kind === "DONE" - ? Object.fromEntries( - Object.entries(stepState.outputs) +function getClientState( + state: StepState +): ClientStep["state"] { + if (state.kind === "DONE") { + return { + kind: "DONE", + outputs: Object.fromEntries( + Object.entries(state.outputs) .filter( (pair): pair is [string, NonNullable<(typeof pair)[1]>] => pair[1] !== undefined ) .map(([key, value]) => [key, artifactToClientArtifact(value)]) - ) - : {}; + ), + }; + } else { + return state; + } } export function stepToClientStep(step: LLMStepInstance): ClientStep { return { id: step.id, name: step.template.name ?? "unknown", - state: step.getState().kind, + state: getClientState(step.getState()), inputs: Object.fromEntries( Object.entries(step.getInputs()).map(([key, value]) => [ key, artifactToClientArtifact(value), ]) ), - outputs: getClientOutputs(step), messages: step.getConversationMessages(), }; } @@ -129,8 +132,7 @@ export function addStreamingListeners( kind: "stepUpdated", content: { id: event.data.step.id, - state: event.data.step.getState().kind, - outputs: getClientOutputs(event.data.step), + state: getClientState(event.data.step.getState()), messages: event.data.step.getConversationMessages(), }, }); @@ -207,9 +209,8 @@ export async function decodeWorkflowFromReader({ steps: [ ...workflow.steps, { - outputs: {}, messages: [], - state: "PENDING", + state: { kind: "PENDING" }, ...event.content, }, ], diff --git a/packages/hub/src/app/ai/AiDashboard.tsx b/packages/hub/src/app/ai/AiDashboard.tsx index d759b703cf..26fbd029be 100644 --- a/packages/hub/src/app/ai/AiDashboard.tsx +++ b/packages/hub/src/app/ai/AiDashboard.tsx @@ -1,6 +1,5 @@ "use client"; -import clsx from "clsx"; import { FC, useRef } from "react"; import { ClientWorkflow, ClientWorkflowResult } from "@quri/squiggle-ai"; @@ -31,7 +30,7 @@ export const AiDashboard: FC = ({ return (
{/* Left column: Mode Toggle, Chat, Form, and list of Workflows */} -
+
(function Sidebar( @@ -71,6 +73,7 @@ Outputs: model: "Claude-Sonnet", numericSteps: 1, styleGuideSteps: 1, + anthropicApiKey: "", }, }); @@ -95,24 +98,35 @@ Outputs: })); const handleSubmit = form.handleSubmit( - async ({ prompt, squiggleCode, model, numericSteps, styleGuideSteps }) => { - const numericStepsNumber = _.toNumber(numericSteps) || 0; - const styleGuideStepsNumber = _.toNumber(styleGuideSteps) || 0; + async ({ + prompt, + squiggleCode, + model, + numericSteps, + styleGuideSteps, + anthropicApiKey, + }) => { + const commonRequestFields: Pick< + AiRequestBody, + "model" | "numericSteps" | "styleGuideSteps" | "anthropicApiKey" + > = { + model: model as LlmId, + numericSteps, + styleGuideSteps, + anthropicApiKey, + }; + const requestBody: AiRequestBody = mode === "create" ? { kind: "create", prompt, - model: model as LlmId, - numericSteps: numericStepsNumber, - styleGuideSteps: styleGuideStepsNumber, + ...commonRequestFields, } : { kind: "edit", squiggleCode, - model: model as LlmId, - numericSteps: numericStepsNumber, - styleGuideSteps: styleGuideStepsNumber, + ...commonRequestFields, }; submitWorkflow(requestBody); @@ -180,7 +194,7 @@ Outputs: inputWidth="w-16" size="small" layout="row" - rules={{ min: 0 }} + rules={{ min: 0, required: true }} /> name="styleGuideSteps" @@ -189,7 +203,14 @@ Outputs: inputWidth="w-16" size="small" layout="row" - rules={{ min: 0 }} + rules={{ min: 0, required: true }} + /> + + name="anthropicApiKey" + label="Anthropic API Key" + tooltip="Anthropic API key for using Claude." + size="small" + layout="row" />
diff --git a/packages/hub/src/app/ai/StepStatusIcon.tsx b/packages/hub/src/app/ai/StepStatusIcon.tsx index 866a7b113e..09da1f4bc4 100644 --- a/packages/hub/src/app/ai/StepStatusIcon.tsx +++ b/packages/hub/src/app/ai/StepStatusIcon.tsx @@ -1,16 +1,24 @@ import { FC } from "react"; import { ClientStep } from "@quri/squiggle-ai"; -import { CheckCircleIcon, ErrorIcon, RefreshIcon } from "@quri/ui"; +import { CheckCircleIcon, ErrorIcon, RefreshIcon, TextTooltip } from "@quri/ui"; export const StepStatusIcon: FC<{ step: ClientStep }> = ({ step }) => { - switch (step.state) { + switch (step.state.kind) { case "PENDING": return ; case "DONE": return ; - case "FAILED": - return ; + case "FAILED": { + const { message } = step.state; + return ( + +
+ +
+
+ ); + } default: return null; } diff --git a/packages/hub/src/app/ai/WorkflowViewer/SelectedNodeSideView.tsx b/packages/hub/src/app/ai/WorkflowViewer/ClientStepView.tsx similarity index 71% rename from packages/hub/src/app/ai/WorkflowViewer/SelectedNodeSideView.tsx rename to packages/hub/src/app/ai/WorkflowViewer/ClientStepView.tsx index 6b9f60bff6..4988961395 100644 --- a/packages/hub/src/app/ai/WorkflowViewer/SelectedNodeSideView.tsx +++ b/packages/hub/src/app/ai/WorkflowViewer/ClientStepView.tsx @@ -32,40 +32,43 @@ const NavButton: FC<{ ); }; -export const SelectedNodeSideView: FC<{ - selectedNode: ClientStep; - onSelectPreviousNode?: () => void; - onSelectNextNode?: () => void; -}> = ({ selectedNode, onSelectPreviousNode, onSelectNextNode }) => { +export const ClientStepView: FC<{ + step: ClientStep; + onSelectPreviousStep?: () => void; + onSelectNextStep?: () => void; +}> = ({ step, onSelectPreviousStep, onSelectNextStep }) => { const { ref, height } = useAvailableHeight(); const selectedNodeCodeOutput = useMemo(() => { - return Object.values(selectedNode.outputs).find( + if (step.state.kind !== "DONE") { + return undefined; + } + return Object.values(step.state.outputs).find( (output): output is ClientArtifact & { kind: "code" } => output.kind === "code" ); - }, [selectedNode]); + }, [step]); const maxPrimaryHeight = height ? height - 180 : 400; return (

- {stepNames[selectedNode.name] || selectedNode.name} + {stepNames[step.name] || step.name}

@@ -74,12 +77,22 @@ export const SelectedNodeSideView: FC<{
- -
-
- +
+ {step.state.kind === "DONE" && ( +
+ +
+ )}
+ {step.state.kind === "FAILED" && ( +
+

Error

+
+              {step.state.message}
+            
+
+ )}
{selectedNodeCodeOutput && (
@@ -94,7 +107,7 @@ export const SelectedNodeSideView: FC<{
)} - {selectedNode.messages.length > 0 && ( + {step.messages.length > 0 && (

Messages: @@ -103,7 +116,7 @@ export const SelectedNodeSideView: FC<{ className="flex-grow overflow-y-auto" style={{ maxHeight: maxPrimaryHeight }} > - +

)} diff --git a/packages/hub/src/app/ai/WorkflowViewer/StepNode.tsx b/packages/hub/src/app/ai/WorkflowViewer/StepNode.tsx index 992ce44d99..bf970a0008 100644 --- a/packages/hub/src/app/ai/WorkflowViewer/StepNode.tsx +++ b/packages/hub/src/app/ai/WorkflowViewer/StepNode.tsx @@ -32,7 +32,7 @@ export const StepNode: FC = ({ {stepNumber}. {stepNames[data.name] || data.name}
- {data.state !== "DONE" && ( + {data.state.kind !== "DONE" && (
diff --git a/packages/hub/src/app/ai/WorkflowViewer/WorkflowActions.tsx b/packages/hub/src/app/ai/WorkflowViewer/WorkflowActions.tsx index 5ed66baac8..9b3099a093 100644 --- a/packages/hub/src/app/ai/WorkflowViewer/WorkflowActions.tsx +++ b/packages/hub/src/app/ai/WorkflowViewer/WorkflowActions.tsx @@ -1,16 +1,15 @@ import { FC, useEffect, useRef, useState } from "react"; -import { ClientStep, ClientWorkflow } from "@quri/squiggle-ai"; +import { ClientWorkflow } from "@quri/squiggle-ai"; -import { SelectedNodeSideView } from "./SelectedNodeSideView"; +import { ClientStepView } from "./ClientStepView"; import { StepNode } from "./StepNode"; -export const WorkflowActions: FC<{ +export const WorkflowSteps: FC<{ workflow: ClientWorkflow; height: number; - onNodeClick?: (node: ClientStep) => void; -}> = ({ workflow, height, onNodeClick }) => { - const [selectedNodeIndex, setSelectedNodeIndex] = useState( +}> = ({ workflow, height }) => { + const [selectedStepIndex, setSelectedStepIndex] = useState( workflow.steps.length - 1 ); const prevStepsLengthRef = useRef(workflow.steps.length); @@ -22,8 +21,8 @@ export const WorkflowActions: FC<{ // select the last non-pending step for (let i = workflow.steps.length - 1; i >= 0; i--) { - if (workflow.steps[i].state !== "PENDING") { - setSelectedNodeIndex(i); + if (workflow.steps[i].state.kind !== "PENDING") { + setSelectedStepIndex(i); break; } } @@ -31,24 +30,22 @@ export const WorkflowActions: FC<{ prevStepsLengthRef.current = workflow.steps.length; }, [workflow.steps]); - const selectPreviousNode = - selectedNodeIndex !== null && selectedNodeIndex > 0 + const selectPreviousStep = + selectedStepIndex !== null && selectedStepIndex > 0 ? () => { - setSelectedNodeIndex(selectedNodeIndex - 1); - onNodeClick?.(workflow.steps[selectedNodeIndex - 1]); + setSelectedStepIndex(selectedStepIndex - 1); } : undefined; - const selectNextNode = - selectedNodeIndex !== null && selectedNodeIndex < workflow.steps.length - 1 + const selectNextStep = + selectedStepIndex !== null && selectedStepIndex < workflow.steps.length - 1 ? () => { - setSelectedNodeIndex(selectedNodeIndex + 1); - onNodeClick?.(workflow.steps[selectedNodeIndex + 1]); + setSelectedStepIndex(selectedStepIndex + 1); } : undefined; - const hasSelectedNode = !( - selectedNodeIndex === null || !workflow.steps[selectedNodeIndex] + const hasSelectedStep = !( + selectedStepIndex === null || !workflow.steps[selectedStepIndex] ); return ( @@ -58,22 +55,19 @@ export const WorkflowActions: FC<{ {workflow.steps.map((step, index) => ( { - setSelectedNodeIndex(index); - onNodeClick?.(step); - }} - isSelected={selectedNodeIndex === index} + onClick={() => setSelectedStepIndex(index)} + isSelected={selectedStepIndex === index} stepNumber={index + 1} key={step.id} /> ))}
- {hasSelectedNode && ( - )}
diff --git a/packages/hub/src/app/ai/WorkflowViewer/index.tsx b/packages/hub/src/app/ai/WorkflowViewer/index.tsx index 5422f47962..e4f1ec9e7a 100644 --- a/packages/hub/src/app/ai/WorkflowViewer/index.tsx +++ b/packages/hub/src/app/ai/WorkflowViewer/index.tsx @@ -9,7 +9,7 @@ import { useAvailableHeight } from "@/hooks/useAvailableHeight"; import { LogsView } from "../LogsView"; import { SquigglePlaygroundForWorkflow } from "../SquigglePlaygroundForWorkflow"; import { Header } from "./Header"; -import { WorkflowActions } from "./WorkflowActions"; +import { WorkflowSteps } from "./WorkflowActions"; type WorkflowViewerProps< T extends ClientWorkflow["status"] = ClientWorkflow["status"], @@ -74,7 +74,7 @@ const FinishedWorkflowViewer: FC> = ({ /> - + @@ -100,7 +100,7 @@ const LoadingWorkflowViewer: FC> = ({ renderRight={() => null} />
- +
); diff --git a/packages/hub/src/app/ai/api/create/route.ts b/packages/hub/src/app/ai/api/create/route.ts index 21f7188c1d..8829880390 100644 --- a/packages/hub/src/app/ai/api/create/route.ts +++ b/packages/hub/src/app/ai/api/create/route.ts @@ -85,7 +85,10 @@ export async function POST(req: Request) { }; const openaiApiKey = process.env["OPENAI_API_KEY"]; - const anthropicApiKey = process.env["ANTHROPIC_API_KEY"]; + const anthropicApiKey = + request.anthropicApiKey || process.env["ANTHROPIC_API_KEY"]; + + console.log({ anthropicApiKey }); const squiggleWorkflow = request.kind === "create" diff --git a/packages/hub/src/app/ai/utils.ts b/packages/hub/src/app/ai/utils.ts index f4e70400c7..4d36ad8ae2 100644 --- a/packages/hub/src/app/ai/utils.ts +++ b/packages/hub/src/app/ai/utils.ts @@ -24,6 +24,7 @@ const commonRequestFields = { model: z.enum(MODEL_CONFIGS.map((model) => model.id) as ModelKeys).optional(), numericSteps: z.number(), styleGuideSteps: z.number(), + anthropicApiKey: z.string().optional(), }; export const aiRequestBodySchema = z.discriminatedUnion("kind", [ diff --git a/packages/hub/src/server/ai/v1_0.ts b/packages/hub/src/server/ai/v1_0.ts index 159d2078f9..39ac5e59b9 100644 --- a/packages/hub/src/server/ai/v1_0.ts +++ b/packages/hub/src/server/ai/v1_0.ts @@ -98,22 +98,34 @@ export const v1WorkflowSchema = z.discriminatedUnion("status", [ export function decodeV1_0JsonToClientWorkflow( json: Prisma.JsonValue ): ClientWorkflow { - const v1Workflow = v1WorkflowSchema.parse(json); + // `input` doesn't exist in the new ClientWorkflow shape, so we need to pull it out + const { input, ...v1Workflow } = v1WorkflowSchema.parse(json); + // upgrading legacy workflow to new client workflow shape return { ...v1Workflow, + steps: v1Workflow.steps.map(({ outputs, ...step }) => ({ + ...step, + // modern steps in ClientWorkflow store state as an object + state: + step.state === "DONE" + ? ({ kind: "DONE", outputs } as const) + : step.state === "FAILED" + ? { kind: "FAILED", errorType: "CRITICAL", message: "Unknown" } + : { kind: "PENDING" }, + })), inputs: - v1Workflow.input.type === "Create" + input.type === "Create" ? { prompt: { - value: v1Workflow.input.prompt, + value: input.prompt, kind: "prompt", id: `${v1Workflow.id}-prompt`, }, } : { source: { - value: v1Workflow.input.source, + value: input.source, kind: "source", id: `${v1Workflow.id}-source`, }, From 5e4bd2631c507465f6ad19a33143b38768e2ab99 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Wed, 20 Nov 2024 13:53:40 -0300 Subject: [PATCH 03/17] rename Actions -> Steps --- .../WorkflowViewer/{WorkflowActions.tsx => WorkflowSteps.tsx} | 0 packages/hub/src/app/ai/WorkflowViewer/index.tsx | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename packages/hub/src/app/ai/WorkflowViewer/{WorkflowActions.tsx => WorkflowSteps.tsx} (100%) diff --git a/packages/hub/src/app/ai/WorkflowViewer/WorkflowActions.tsx b/packages/hub/src/app/ai/WorkflowViewer/WorkflowSteps.tsx similarity index 100% rename from packages/hub/src/app/ai/WorkflowViewer/WorkflowActions.tsx rename to packages/hub/src/app/ai/WorkflowViewer/WorkflowSteps.tsx diff --git a/packages/hub/src/app/ai/WorkflowViewer/index.tsx b/packages/hub/src/app/ai/WorkflowViewer/index.tsx index e4f1ec9e7a..d48ac9949b 100644 --- a/packages/hub/src/app/ai/WorkflowViewer/index.tsx +++ b/packages/hub/src/app/ai/WorkflowViewer/index.tsx @@ -9,7 +9,7 @@ import { useAvailableHeight } from "@/hooks/useAvailableHeight"; import { LogsView } from "../LogsView"; import { SquigglePlaygroundForWorkflow } from "../SquigglePlaygroundForWorkflow"; import { Header } from "./Header"; -import { WorkflowSteps } from "./WorkflowActions"; +import { WorkflowSteps } from "./WorkflowSteps"; type WorkflowViewerProps< T extends ClientWorkflow["status"] = ClientWorkflow["status"], @@ -59,7 +59,7 @@ const FinishedWorkflowViewer: FC> = ({
- +
From b8ddaa6f32c70cb9306bfe78d0d8cb6a7e725802 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Thu, 21 Nov 2024 13:19:25 -0300 Subject: [PATCH 04/17] /admin/dev page; control prisma logs in dev --- packages/hub/src/app/admin/dev/page.tsx | 43 ++++++++++++++++ packages/hub/src/prisma.ts | 65 ++++++++++++++++--------- 2 files changed, 86 insertions(+), 22 deletions(-) create mode 100644 packages/hub/src/app/admin/dev/page.tsx diff --git a/packages/hub/src/app/admin/dev/page.tsx b/packages/hub/src/app/admin/dev/page.tsx new file mode 100644 index 0000000000..ca944ae964 --- /dev/null +++ b/packages/hub/src/app/admin/dev/page.tsx @@ -0,0 +1,43 @@ +import { notFound } from "next/navigation"; + +import { Button } from "@quri/ui"; + +import { NarrowPageLayout } from "@/components/layout/NarrowPageLayout"; +import { H2 } from "@/components/ui/Headers"; +import { resetPrisma } from "@/prisma"; + +export default async function () { + if (process.env.NODE_ENV !== "development") { + notFound(); + } + + async function disablePrismaLogs() { + "use server"; + await resetPrisma({ logs: "none" }); + } + + async function enablePrismaLogs() { + "use server"; + await resetPrisma({ logs: "query" }); + } + + return ( + +
+

Prisma logs

+
+ These buttons will enable/disable console logs from Prisma. +
+ Logs are on by default in dev mode, but you might want to disable them + if you have other logs. +
+
+ +
+
+ +
+
+
+ ); +} diff --git a/packages/hub/src/prisma.ts b/packages/hub/src/prisma.ts index d2e0956117..7b8dc7d477 100644 --- a/packages/hub/src/prisma.ts +++ b/packages/hub/src/prisma.ts @@ -4,35 +4,56 @@ */ import { PrismaClient } from "@prisma/client"; -// This config helps with connection leaks during hot reload -// (which we don't have in server.ts yet, but might in the future.) - declare global { // allow global `var` declarations // eslint-disable-next-line no-var + var _prismaConfig: PrismaConfig; var _prisma: PrismaClient | undefined; } -export const prisma = - global._prisma || - new PrismaClient({ - log: process.env.NODE_ENV === "test" ? [] : ["query"], - // Uncomment the following and `prisma.$on` code below if you need to log query params for debugging. - // Enabling it causes duplicate log lines on code reloads, so it's not enabled by default. - // log: [ - // { - // emit: "event", - // level: "query", - // }, - // ], +type PrismaConfig = { + logs: "none" | "query" | "query-with-params"; +}; + +global._prismaConfig = { + logs: process.env.NODE_ENV === "test" ? "none" : "query", +}; + +function makePrisma() { + const config = global._prismaConfig; + const prisma = new PrismaClient({ + log: + config.logs === "none" + ? [] + : config.logs === "query" + ? ["query"] + : [ + { + emit: "event", + level: "query", + }, + ], }); -// // Prisma types are weird, using `any` -// (prisma as any).$on("query", async (e: any) => { -// if (process.env.NODE_ENV === "test") { -// return; // logs are too verbose for jest -// } -// console.log(`${e.query} ${e.params}`); -// }); + // FIXME - query-with-params mode causes duplicate log lines on code reloads. + (prisma as any).$on("query", async (e: any) => { + console.log(`${e.query} ${e.params}`); + }); + return prisma; +} + +export let prisma = global._prisma || makePrisma(); + +// Single prisma instance for the entire app, in dev mode. +// This helps with connection leaks during hot reloads. if (process.env.NODE_ENV !== "production") global._prisma = prisma; + +// This will work only in dev mode, and will be invoked only in dev mode. +export async function resetPrisma(config: PrismaConfig) { + if (process.env.NODE_ENV === "production") return; + global._prismaConfig = config; + await prisma.$disconnect(); + prisma = makePrisma(); + global._prisma = prisma; +} From 4302a41171d51b647f9ebf9172c239620efdf215 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Thu, 21 Nov 2024 14:19:17 -0300 Subject: [PATCH 05/17] use getServerSession wrapper --- packages/hub/src/app/admin/layout.tsx | 6 ++---- packages/hub/src/app/ai/api/create/route.ts | 8 ++------ packages/hub/src/app/api/graphql/route.ts | 6 ++---- packages/hub/src/app/layout.tsx | 6 +++--- packages/hub/src/server/helpers.ts | 1 + 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/packages/hub/src/app/admin/layout.tsx b/packages/hub/src/app/admin/layout.tsx index c038a2b828..f300da8ad0 100644 --- a/packages/hub/src/app/admin/layout.tsx +++ b/packages/hub/src/app/admin/layout.tsx @@ -1,4 +1,3 @@ -import { getServerSession } from "next-auth"; import { PropsWithChildren } from "react"; import { LockIcon } from "@quri/ui"; @@ -7,11 +6,10 @@ import { FullLayoutWithPadding } from "@/components/layout/FullLayoutWithPadding import { NarrowPageLayout } from "@/components/layout/NarrowPageLayout"; import { H1 } from "@/components/ui/Headers"; import { isRootEmail } from "@/graphql/helpers/userHelpers"; - -import { authOptions } from "../api/auth/[...nextauth]/authOptions"; +import { getServerSession } from "@/server/helpers"; export default async function AdminLayout({ children }: PropsWithChildren) { - const session = await getServerSession(authOptions); + const session = await getServerSession(); const email = session?.user.email; diff --git a/packages/hub/src/app/ai/api/create/route.ts b/packages/hub/src/app/ai/api/create/route.ts index 8829880390..ae799c9a03 100644 --- a/packages/hub/src/app/ai/api/create/route.ts +++ b/packages/hub/src/app/ai/api/create/route.ts @@ -1,5 +1,3 @@ -import { getServerSession } from "next-auth"; - import { LlmConfig } from "@quri/squiggle-ai"; import { createSquiggleWorkflowTemplate, @@ -9,11 +7,11 @@ import { Workflow, } from "@quri/squiggle-ai/server"; -import { authOptions } from "@/app/api/auth/[...nextauth]/authOptions"; import { getSelf, isSignedIn } from "@/graphql/helpers/userHelpers"; import { prisma } from "@/prisma"; import { getAiCodec } from "@/server/ai/utils"; import { V2WorkflowData } from "@/server/ai/v2_0"; +import { getServerSession } from "@/server/helpers"; import { aiRequestBodySchema } from "../../utils"; @@ -62,7 +60,7 @@ async function updateWorkflowLog(workflow: Workflow) { } export async function POST(req: Request) { - const session = await getServerSession(authOptions); + const session = await getServerSession(); if (!isSignedIn(session)) { return new Response("Unauthorized", { status: 401 }); @@ -88,8 +86,6 @@ export async function POST(req: Request) { const anthropicApiKey = request.anthropicApiKey || process.env["ANTHROPIC_API_KEY"]; - console.log({ anthropicApiKey }); - const squiggleWorkflow = request.kind === "create" ? createSquiggleWorkflowTemplate.instantiate({ diff --git a/packages/hub/src/app/api/graphql/route.ts b/packages/hub/src/app/api/graphql/route.ts index b5fdefa329..0497a20cf7 100644 --- a/packages/hub/src/app/api/graphql/route.ts +++ b/packages/hub/src/app/api/graphql/route.ts @@ -1,10 +1,8 @@ import { createYoga } from "graphql-yoga"; -import { getServerSession } from "next-auth"; import { NextRequest, NextResponse } from "next/server"; import { schema } from "@/graphql/schema"; - -import { authOptions } from "../auth/[...nextauth]/authOptions"; +import { getServerSession } from "@/server/helpers"; const yoga = createYoga({ graphqlEndpoint: "/api/graphql", @@ -13,7 +11,7 @@ const yoga = createYoga({ // There's some magic involved here; // getServerSession() obtains request data through Next.js cookies() and headers() functions // See also: https://github.com/nextauthjs/next-auth/issues/7355 - const session = await getServerSession(authOptions); + const session = await getServerSession(); return { session }; }, }); diff --git a/packages/hub/src/app/layout.tsx b/packages/hub/src/app/layout.tsx index 85530300fe..488ac0b3bb 100644 --- a/packages/hub/src/app/layout.tsx +++ b/packages/hub/src/app/layout.tsx @@ -4,16 +4,16 @@ import "@/styles/main.css"; import { Analytics } from "@vercel/analytics/react"; import { Metadata } from "next"; -import { getServerSession } from "next-auth"; import { PropsWithChildren } from "react"; -import { authOptions } from "./api/auth/[...nextauth]/authOptions"; +import { getServerSession } from "@/server/helpers"; + import { RootLayout } from "./RootLayout"; export default async function ServerRootLayout({ children, }: PropsWithChildren) { - const session = await getServerSession(authOptions); + const session = await getServerSession(); return ( diff --git a/packages/hub/src/server/helpers.ts b/packages/hub/src/server/helpers.ts index 01f7018680..21ebf182c8 100644 --- a/packages/hub/src/server/helpers.ts +++ b/packages/hub/src/server/helpers.ts @@ -3,6 +3,7 @@ * TODO: unify these with `graphql/helpers/*` * (see https://github.com/quantified-uncertainty/squiggle/issues/3154, we plan to migrate away from GraphQL) */ +import "server-only"; import { getServerSession as getNextAuthServerSession } from "next-auth"; import { redirect } from "next/navigation"; From fbce9a9436152f03b37ed32bc82f75305bdcd88e Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Thu, 21 Nov 2024 15:09:08 -0300 Subject: [PATCH 06/17] refactor LLMClient --- packages/ai/src/LLMClient.ts | 253 ------------------ .../ai/src/LLMClient/AnthropicProvider.ts | 97 +++++++ packages/ai/src/LLMClient/OpenAIProvider.ts | 68 +++++ packages/ai/src/LLMClient/index.ts | 93 +++++++ packages/ai/src/LLMClient/types.ts | 28 ++ packages/ai/src/LLMStepInstance.ts | 7 +- packages/ai/src/Logger.ts | 2 +- packages/ai/src/generateSummary.ts | 2 +- packages/ai/src/modelConfigs.ts | 2 +- packages/ai/src/workflows/Workflow.ts | 8 +- 10 files changed, 293 insertions(+), 267 deletions(-) delete mode 100644 packages/ai/src/LLMClient.ts create mode 100644 packages/ai/src/LLMClient/AnthropicProvider.ts create mode 100644 packages/ai/src/LLMClient/OpenAIProvider.ts create mode 100644 packages/ai/src/LLMClient/index.ts create mode 100644 packages/ai/src/LLMClient/types.ts diff --git a/packages/ai/src/LLMClient.ts b/packages/ai/src/LLMClient.ts deleted file mode 100644 index c3aedeab3c..0000000000 --- a/packages/ai/src/LLMClient.ts +++ /dev/null @@ -1,253 +0,0 @@ -import Anthropic from "@anthropic-ai/sdk"; -import OpenAI from "openai"; - -import { LlmId, MODEL_CONFIGS } from "./modelConfigs.js"; -import { squiggleSystemPrompt } from "./prompts.js"; - -const TIMEOUT_MS = 60000; - -export type Message = { - role: "system" | "user" | "assistant"; - content: string; -}; - -function convertToClaudeMessages(history: Message[]): Anthropic.MessageParam[] { - return history - .filter((msg) => msg.role !== "system") - .map((msg) => ({ - role: msg.role as Exclude, - content: msg.content, - })); -} - -function extractTextContent(content: Anthropic.ContentBlock[]): string { - return content - .filter( - (block): block is Extract => - block.type === "text" - ) - .map((block) => block.text) - .join("\n"); -} - -interface StandardizedChatCompletion { - id: string; - object: string; - created: number; - model: string; - content: string; - role: "assistant"; - finish_reason: string | null; - usage: { - prompt_tokens: number; - completion_tokens: number; - total_tokens: number; - }; -} - -function convertClaudeToStandardFormat( - claudeResponse: Anthropic.Message -): StandardizedChatCompletion { - return { - id: claudeResponse.id, - object: "chat.completion", - created: Date.now(), - model: claudeResponse.model, - content: extractTextContent(claudeResponse.content), - role: "assistant", - finish_reason: claudeResponse.stop_reason || null, - usage: { - prompt_tokens: claudeResponse.usage.input_tokens, - completion_tokens: claudeResponse.usage.output_tokens, - total_tokens: - claudeResponse.usage.input_tokens + claudeResponse.usage.output_tokens, - }, - }; -} - -function convertOpenAIToStandardFormat( - openAIResponse: OpenAI.Chat.Completions.ChatCompletion -): StandardizedChatCompletion { - const choice = openAIResponse.choices[0]; - return { - id: openAIResponse.id, - object: openAIResponse.object, - created: openAIResponse.created, - model: openAIResponse.model, - content: choice.message.content || "", - role: "assistant", - finish_reason: choice.finish_reason || null, - usage: openAIResponse.usage || { - prompt_tokens: 0, - completion_tokens: 0, - total_tokens: 0, - }, - }; -} - -export type LlmMetrics = { - apiCalls: number; - inputTokens: number; - outputTokens: number; - llmId: LlmId; -}; - -export function calculatePriceMultipleCalls( - metrics: Partial> -): number { - let totalCost = 0; - - for (const [llmId, { inputTokens, outputTokens }] of Object.entries( - metrics - )) { - const modelConfig = MODEL_CONFIGS.find((model) => model.id === llmId); - - if (!modelConfig) { - console.warn(`No pricing information found for LLM: ${llmId}`); - continue; - } - - const inputCost = (inputTokens * modelConfig.inputRate) / 1_000_000; - const outputCost = (outputTokens * modelConfig.outputRate) / 1_000_000; - totalCost += inputCost + outputCost; - } - - return totalCost; -} - -function compressAssistantMessages(messages: Message[]): Message[] { - return messages.reduce((acc, current, index, array) => { - if (current.role !== "assistant") { - acc.push(current); - } else if (index === 0 || array[index - 1].role !== "assistant") { - acc.push(current); - } else { - acc[acc.length - 1].content += "\n\n" + current.content; - } - return acc; - }, [] as Message[]); -} - -// Wrapper around OpenAI and Anthropic clients; injects Squiggle context into the conversation. -export class LLMClient { - private openaiClient?: OpenAI; - private anthropicClient?: Anthropic; - - constructor( - public llmId: LlmId, - openaiApiKey?: string, - anthropicApiKey?: string - ) { - if (openaiApiKey) { - this.openaiClient = new OpenAI({ - apiKey: openaiApiKey, - }); - } - - if (anthropicApiKey) { - this.anthropicClient = new Anthropic({ - apiKey: anthropicApiKey, - }); - } - } - - getOpenAIClient(): OpenAI { - if (!this.openaiClient) { - throw new Error("OpenAI client is not initialized"); - } - return this.openaiClient; - } - - getAnthropicClient(): Anthropic { - if (!this.anthropicClient) { - throw new Error("Anthropic client is not initialized"); - } - return this.anthropicClient; - } - - async run( - conversationHistory: Message[] - ): Promise { - const selectedModelConfig = MODEL_CONFIGS.find( - (model) => model.id === this.llmId - ); - - if (!selectedModelConfig) { - throw new Error(`No model config found for LLM: ${this.llmId}`); - } - - try { - const timeoutPromise = new Promise((_, reject) => - setTimeout( - () => - reject(new Error(`API call timed out after ${TIMEOUT_MS / 1000}s`)), - TIMEOUT_MS - ) - ); - - if (selectedModelConfig.provider === "anthropic") { - const anthropicClient = this.getAnthropicClient(); - const compressedMessages = - compressAssistantMessages(conversationHistory); - const claudeMessages = convertToClaudeMessages(compressedMessages); - - if (claudeMessages.length === 0) { - throw new Error("At least one message is required"); - } - - const completionPromise = - anthropicClient.beta.promptCaching.messages.create({ - max_tokens: selectedModelConfig.maxTokens, - messages: claudeMessages, - model: selectedModelConfig.model, - system: [ - { - text: squiggleSystemPrompt, - type: "text", - cache_control: { type: "ephemeral" }, - }, - ], - }); - - const completion = await Promise.race([ - completionPromise, - timeoutPromise, - ]); - return convertClaudeToStandardFormat(completion as Anthropic.Message); - } else { - const openaiClient = this.getOpenAIClient(); - const messages = selectedModelConfig.allowsSystemPrompt - ? [ - { role: "system", content: squiggleSystemPrompt }, - ...conversationHistory, - ] - : [ - { - role: "user", - content: `Here are the basics of Squiggle. Read them, and then say 'Okay'. ${squiggleSystemPrompt}`, - }, - { role: "assistant", content: "Okay." }, - ...conversationHistory, - ]; - const completionPromise = openaiClient.chat.completions.create({ - model: selectedModelConfig.model, - messages: messages.map((msg) => ({ - role: msg.role as "system" | "user" | "assistant", - content: msg.content, - })), - }); - - const completion = await Promise.race([ - completionPromise, - timeoutPromise, - ]); - return convertOpenAIToStandardFormat( - completion as OpenAI.Chat.Completions.ChatCompletion - ); - } - } catch (error) { - console.error("Error in API call:", error); - throw error; - } - } -} diff --git a/packages/ai/src/LLMClient/AnthropicProvider.ts b/packages/ai/src/LLMClient/AnthropicProvider.ts new file mode 100644 index 0000000000..5727e2b768 --- /dev/null +++ b/packages/ai/src/LLMClient/AnthropicProvider.ts @@ -0,0 +1,97 @@ +import Anthropic from "@anthropic-ai/sdk"; + +import { MODEL_CONFIGS } from "../modelConfigs.js"; +import { squiggleSystemPrompt } from "../prompts.js"; +import { Message, StandardizedChatCompletion } from "./types.js"; + +function extractTextContent(content: Anthropic.ContentBlock[]): string { + return content + .filter( + (block): block is Extract => + block.type === "text" + ) + .map((block) => block.text) + .join("\n"); +} + +function convertToClaudeMessages(history: Message[]): Anthropic.MessageParam[] { + return history + .filter((msg) => msg.role !== "system") + .map((msg) => ({ + role: msg.role as Exclude, + content: msg.content, + })); +} + +function compressAssistantMessages(messages: Message[]): Message[] { + return messages.reduce((acc, current, index, array) => { + if (current.role !== "assistant") { + acc.push(current); + } else if (index === 0 || array[index - 1].role !== "assistant") { + acc.push(current); + } else { + acc[acc.length - 1].content += "\n\n" + current.content; + } + return acc; + }, [] as Message[]); +} + +function convertClaudeToStandardFormat( + claudeResponse: Anthropic.Message +): StandardizedChatCompletion { + return { + id: claudeResponse.id, + object: "chat.completion", + created: Date.now(), + model: claudeResponse.model, + content: extractTextContent(claudeResponse.content), + role: "assistant", + finish_reason: claudeResponse.stop_reason || null, + usage: { + prompt_tokens: claudeResponse.usage.input_tokens, + completion_tokens: claudeResponse.usage.output_tokens, + total_tokens: + claudeResponse.usage.input_tokens + claudeResponse.usage.output_tokens, + }, + }; +} + +export class AnthropicProvider { + client: Anthropic; + + constructor( + apiKey: string, + private modelConfig: Extract< + (typeof MODEL_CONFIGS)[number], + { provider: "anthropic" } + > + ) { + this.client = new Anthropic({ + apiKey, + }); + } + + async run(conversationHistory: Message[]) { + const compressedMessages = compressAssistantMessages(conversationHistory); + const claudeMessages = convertToClaudeMessages(compressedMessages); + + if (claudeMessages.length === 0) { + throw new Error("At least one message is required"); + } + + const completion = await this.client.beta.promptCaching.messages.create({ + max_tokens: this.modelConfig.maxTokens, + messages: claudeMessages, + model: this.modelConfig.model, + system: [ + { + text: squiggleSystemPrompt, + type: "text", + cache_control: { type: "ephemeral" }, + }, + ], + }); + + return convertClaudeToStandardFormat(completion as Anthropic.Message); + } +} diff --git a/packages/ai/src/LLMClient/OpenAIProvider.ts b/packages/ai/src/LLMClient/OpenAIProvider.ts new file mode 100644 index 0000000000..5db360f4b6 --- /dev/null +++ b/packages/ai/src/LLMClient/OpenAIProvider.ts @@ -0,0 +1,68 @@ +import OpenAI from "openai"; + +import { MODEL_CONFIGS } from "../modelConfigs.js"; +import { squiggleSystemPrompt } from "../prompts.js"; +import { Message, StandardizedChatCompletion } from "./types.js"; + +function convertOpenAIToStandardFormat( + openAIResponse: OpenAI.Chat.Completions.ChatCompletion +): StandardizedChatCompletion { + const choice = openAIResponse.choices[0]; + return { + id: openAIResponse.id, + object: openAIResponse.object, + created: openAIResponse.created, + model: openAIResponse.model, + content: choice.message.content || "", + role: "assistant", + finish_reason: choice.finish_reason || null, + usage: openAIResponse.usage || { + prompt_tokens: 0, + completion_tokens: 0, + total_tokens: 0, + }, + }; +} + +export class OpenAIProvider { + private client: OpenAI; + + constructor( + apiKey: string, + private modelConfig: Extract< + (typeof MODEL_CONFIGS)[number], + { provider: "openai" } + > + ) { + this.client = new OpenAI({ + apiKey: apiKey, + }); + } + + async run(conversationHistory: Message[]) { + const messages = this.modelConfig.allowsSystemPrompt + ? [ + { role: "system", content: squiggleSystemPrompt }, + ...conversationHistory, + ] + : [ + { + role: "user", + content: `Here are the basics of Squiggle. Read them, and then say 'Okay'. ${squiggleSystemPrompt}`, + }, + { role: "assistant", content: "Okay." }, + ...conversationHistory, + ]; + const completion = await this.client.chat.completions.create({ + model: this.modelConfig.model, + messages: messages.map((msg) => ({ + role: msg.role as "system" | "user" | "assistant", + content: msg.content, + })), + }); + + return convertOpenAIToStandardFormat( + completion as OpenAI.Chat.Completions.ChatCompletion + ); + } +} diff --git a/packages/ai/src/LLMClient/index.ts b/packages/ai/src/LLMClient/index.ts new file mode 100644 index 0000000000..5578b1b4ed --- /dev/null +++ b/packages/ai/src/LLMClient/index.ts @@ -0,0 +1,93 @@ +import { LlmId, MODEL_CONFIGS } from "../modelConfigs.js"; +import { AnthropicProvider } from "./AnthropicProvider.js"; +import { OpenAIProvider } from "./OpenAIProvider.js"; +import { LlmMetrics, Message, StandardizedChatCompletion } from "./types.js"; + +const TIMEOUT_MS = 60000; + +export function calculatePriceMultipleCalls( + metrics: Partial> +): number { + let totalCost = 0; + + for (const [llmId, { inputTokens, outputTokens }] of Object.entries( + metrics + )) { + const modelConfig = MODEL_CONFIGS.find((model) => model.id === llmId); + + if (!modelConfig) { + console.warn(`No pricing information found for LLM: ${llmId}`); + continue; + } + + const inputCost = (inputTokens * modelConfig.inputRate) / 1_000_000; + const outputCost = (outputTokens * modelConfig.outputRate) / 1_000_000; + totalCost += inputCost + outputCost; + } + + return totalCost; +} + +// Wrapper around OpenAI and Anthropic clients; injects Squiggle context into the conversation. +export class LLMClient { + private provider: AnthropicProvider | OpenAIProvider; + + constructor( + public llmId: LlmId, + openaiApiKey?: string, + anthropicApiKey?: string + ) { + const selectedModelConfig = MODEL_CONFIGS.find( + (model) => model.id === this.llmId + ); + + if (!selectedModelConfig) { + throw new Error(`No model config found for LLM: ${this.llmId}`); + } + + if (selectedModelConfig.provider === "openai") { + if (!openaiApiKey) { + throw new Error("No OpenAI API key provided"); + } + this.provider = new OpenAIProvider(openaiApiKey, selectedModelConfig); + } else if (selectedModelConfig.provider === "anthropic") { + if (!anthropicApiKey) { + throw new Error("No Anthropic API key provided"); + } + this.provider = new AnthropicProvider( + anthropicApiKey, + selectedModelConfig + ); + } else { + throw new Error( + `Unsupported model config: ${selectedModelConfig satisfies never}` + ); + } + } + + async run( + conversationHistory: Message[] + ): Promise { + try { + const timeoutPromise = new Promise((_, reject) => + setTimeout( + () => + reject(new Error(`API call timed out after ${TIMEOUT_MS / 1000}s`)), + TIMEOUT_MS + ) + ); + + const completionPromise = this.provider.run(conversationHistory); + + const completion = await Promise.race([ + completionPromise, + timeoutPromise, + ]); + + return completion; + } catch (error) { + console.error("Error in API call:", error); + throw error; + } + } +} diff --git a/packages/ai/src/LLMClient/types.ts b/packages/ai/src/LLMClient/types.ts new file mode 100644 index 0000000000..5fc9cb2f36 --- /dev/null +++ b/packages/ai/src/LLMClient/types.ts @@ -0,0 +1,28 @@ +import { LlmId } from "../modelConfigs.js"; + +export type Message = { + role: "system" | "user" | "assistant"; + content: string; +}; + +export type LlmMetrics = { + apiCalls: number; + inputTokens: number; + outputTokens: number; + llmId: LlmId; +}; + +export type StandardizedChatCompletion = { + id: string; + object: string; + created: number; + model: string; + content: string; + role: "assistant"; + finish_reason: string | null; + usage: { + prompt_tokens: number; + completion_tokens: number; + total_tokens: number; + }; +}; diff --git a/packages/ai/src/LLMStepInstance.ts b/packages/ai/src/LLMStepInstance.ts index 064e7f7145..4896cffbee 100644 --- a/packages/ai/src/LLMStepInstance.ts +++ b/packages/ai/src/LLMStepInstance.ts @@ -1,9 +1,6 @@ import { ArtifactKind, BaseArtifact, makeArtifact } from "./Artifact.js"; -import { - calculatePriceMultipleCalls, - LlmMetrics, - Message, -} from "./LLMClient.js"; +import { calculatePriceMultipleCalls } from "./LLMClient/index.js"; +import { LlmMetrics, Message } from "./LLMClient/types.js"; import { ErrorType, ExecuteContext, diff --git a/packages/ai/src/Logger.ts b/packages/ai/src/Logger.ts index f593d5ecb3..2b2502d4e5 100644 --- a/packages/ai/src/Logger.ts +++ b/packages/ai/src/Logger.ts @@ -1,6 +1,6 @@ import chalk from "chalk"; -import { Message } from "./LLMClient.js"; +import { Message } from "./LLMClient/types.js"; export type LogEntry = | InfoLogEntry diff --git a/packages/ai/src/generateSummary.ts b/packages/ai/src/generateSummary.ts index 99cd91e3e6..447bfd2e42 100644 --- a/packages/ai/src/generateSummary.ts +++ b/packages/ai/src/generateSummary.ts @@ -1,6 +1,6 @@ import { Artifact, ArtifactKind } from "./Artifact.js"; import { Code } from "./Code.js"; -import { calculatePriceMultipleCalls } from "./LLMClient.js"; +import { calculatePriceMultipleCalls } from "./LLMClient/index.js"; import { IOShape } from "./LLMStepTemplate.js"; import { getLogEntryFullName, TimestampedLogEntry } from "./Logger.js"; import { Workflow } from "./workflows/Workflow.js"; diff --git a/packages/ai/src/modelConfigs.ts b/packages/ai/src/modelConfigs.ts index 24ee5735c4..6b9f96ec8f 100644 --- a/packages/ai/src/modelConfigs.ts +++ b/packages/ai/src/modelConfigs.ts @@ -1,5 +1,5 @@ // Model selection and pricing -type ModelConfig = { +export type ModelConfig = { provider: "anthropic" | "openai"; model: string; inputRate: number; diff --git a/packages/ai/src/workflows/Workflow.ts b/packages/ai/src/workflows/Workflow.ts index 6df7c3e841..56de906d64 100644 --- a/packages/ai/src/workflows/Workflow.ts +++ b/packages/ai/src/workflows/Workflow.ts @@ -1,12 +1,8 @@ import { ReadableStream } from "stream/web"; import { generateSummary } from "../generateSummary.js"; -import { - calculatePriceMultipleCalls, - LLMClient, - LlmMetrics, - Message, -} from "../LLMClient.js"; +import { calculatePriceMultipleCalls, LLMClient } from "../LLMClient/index.js"; +import { LlmMetrics, Message } from "../LLMClient/types.js"; import { LLMStepInstance } from "../LLMStepInstance.js"; import { Inputs, IOShape, PreparedStep } from "../LLMStepTemplate.js"; import { TimestampedLogEntry } from "../Logger.js"; From 078b0f26e8c01a0f172156e8c07f63caf07da892 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Thu, 21 Nov 2024 15:33:46 -0300 Subject: [PATCH 07/17] queryLLM always returns a completion; update some errors to critical --- packages/ai/src/LLMClient/LLMError.ts | 9 +++++ packages/ai/src/LLMClient/index.ts | 8 ++++- packages/ai/src/LLMStepInstance.ts | 36 ++++++++++++------- packages/ai/src/LLMStepTemplate.ts | 4 ++- packages/ai/src/steps/adjustToFeedbackStep.ts | 5 --- .../ai/src/steps/fixCodeUntilItRunsStep.ts | 4 --- packages/ai/src/steps/generateCodeStep.ts | 4 --- packages/ai/src/steps/matchStyleGuideStep.ts | 5 --- 8 files changed, 43 insertions(+), 32 deletions(-) create mode 100644 packages/ai/src/LLMClient/LLMError.ts diff --git a/packages/ai/src/LLMClient/LLMError.ts b/packages/ai/src/LLMClient/LLMError.ts new file mode 100644 index 0000000000..b9d7eecdba --- /dev/null +++ b/packages/ai/src/LLMClient/LLMError.ts @@ -0,0 +1,9 @@ +// Common error class for OpenAI and Anthropic errors. +export class LLMError extends Error { + constructor( + message: string, + public kind: "timeout" | "balance" | "other" + ) { + super(message); + } +} diff --git a/packages/ai/src/LLMClient/index.ts b/packages/ai/src/LLMClient/index.ts index 5578b1b4ed..d2fe449a3f 100644 --- a/packages/ai/src/LLMClient/index.ts +++ b/packages/ai/src/LLMClient/index.ts @@ -1,5 +1,6 @@ import { LlmId, MODEL_CONFIGS } from "../modelConfigs.js"; import { AnthropicProvider } from "./AnthropicProvider.js"; +import { LLMError } from "./LLMError.js"; import { OpenAIProvider } from "./OpenAIProvider.js"; import { LlmMetrics, Message, StandardizedChatCompletion } from "./types.js"; @@ -72,7 +73,12 @@ export class LLMClient { const timeoutPromise = new Promise((_, reject) => setTimeout( () => - reject(new Error(`API call timed out after ${TIMEOUT_MS / 1000}s`)), + reject( + new LLMError( + `API call timed out after ${TIMEOUT_MS / 1000}s`, + "timeout" + ) + ), TIMEOUT_MS ) ); diff --git a/packages/ai/src/LLMStepInstance.ts b/packages/ai/src/LLMStepInstance.ts index 4896cffbee..ffdcd57da5 100644 --- a/packages/ai/src/LLMStepInstance.ts +++ b/packages/ai/src/LLMStepInstance.ts @@ -1,5 +1,6 @@ import { ArtifactKind, BaseArtifact, makeArtifact } from "./Artifact.js"; import { calculatePriceMultipleCalls } from "./LLMClient/index.js"; +import { LLMError } from "./LLMClient/LLMError.js"; import { LlmMetrics, Message } from "./LLMClient/types.js"; import { ErrorType, @@ -180,7 +181,9 @@ export class LLMStepInstance< this.fail(error.type, error.message); } else { this.fail( - "MINOR", // TODO - critical? + // Steps are responsible for handling their own errors. + // If the step throws anything other than a FailError, we assume it's an error in step implementation. + "CRITICAL", error instanceof Error ? error.message : String(error) ); } @@ -275,7 +278,7 @@ export class LLMStepInstance< this.conversationMessages.push(message); } - private async queryLLM(promptPair: PromptPair): Promise { + private async queryLLM(promptPair: PromptPair): Promise { try { const messagesToSend: Message[] = [ ...this.workflow.getRelevantPreviousConversationMessages( @@ -304,11 +307,10 @@ export class LLMStepInstance< }); if (!completion?.content) { - this.log({ - type: "error", - message: "Received an empty response from the API", - }); - return null; + throw new FailError( + "CRITICAL", + "Received an empty response from the API" + ); } else { this.addConversationMessage({ role: "user", @@ -323,11 +325,21 @@ export class LLMStepInstance< return completion.content; } catch (error) { - this.fail( - "MINOR", - `Error in queryLLM: ${error instanceof Error ? error.message : error}` - ); - return null; + if (error instanceof LLMError) { + if (error.kind === "timeout") { + // retry timeouts + throw new FailError("MINOR", error.message); + } else { + // fail the workflow on all other LLM errors + throw new FailError("CRITICAL", error.message); + } + } else { + // LLMClient shouldn't throw anything other than LLMError. + throw new FailError( + "CRITICAL", + error instanceof Error ? error.message : String(error) + ); + } } } diff --git a/packages/ai/src/LLMStepTemplate.ts b/packages/ai/src/LLMStepTemplate.ts index ab8a2e4d76..574df4173e 100644 --- a/packages/ai/src/LLMStepTemplate.ts +++ b/packages/ai/src/LLMStepTemplate.ts @@ -2,6 +2,8 @@ import { Artifact, ArtifactKind } from "./Artifact.js"; import { LogEntry } from "./Logger.js"; import { PromptPair } from "./prompts.js"; +// "CRITICAL" is fatal and will stop the workflow. +// "MINOR" is non-fatal and will usually cause step retries (depending on the controller implementation). export type ErrorType = "CRITICAL" | "MINOR"; export type StepState = @@ -58,7 +60,7 @@ type StepExecuteResult = { // ExecuteContext is the context that's available to the step implementation. // We intentionally don't pass the reference to the step implementation, so that steps won't mess with their internal state. export type ExecuteContext = { - queryLLM(promptPair: PromptPair): Promise; + queryLLM(promptPair: PromptPair): Promise; log(log: LogEntry): void; fail(errorType: ErrorType, message: string): never; }; diff --git a/packages/ai/src/steps/adjustToFeedbackStep.ts b/packages/ai/src/steps/adjustToFeedbackStep.ts index 0c53a170d6..a1faa0377c 100644 --- a/packages/ai/src/steps/adjustToFeedbackStep.ts +++ b/packages/ai/src/steps/adjustToFeedbackStep.ts @@ -71,11 +71,6 @@ export const adjustToFeedbackStep = new LLMStepTemplate( adjustToFeedbackPrompt(prompt.value, code.value) ); - if (!completion) { - // failed - return context.fail("CRITICAL", "LLM failed to provide a response"); - } - // handle adjustment response const trimmedResponse = completion.trim(); const noAdjustmentRegex = diff --git a/packages/ai/src/steps/fixCodeUntilItRunsStep.ts b/packages/ai/src/steps/fixCodeUntilItRunsStep.ts index b70705afe6..710efcc092 100644 --- a/packages/ai/src/steps/fixCodeUntilItRunsStep.ts +++ b/packages/ai/src/steps/fixCodeUntilItRunsStep.ts @@ -86,10 +86,6 @@ export const fixCodeUntilItRunsStep = new LLMStepTemplate( const promptPair = editExistingSquiggleCodePrompt(code.value); const completion = await context.queryLLM(promptPair); - if (!completion) { - // failed - return context.fail("CRITICAL", "LLM failed to provide a response"); - } const newCodeResult = await diffCompletionContentToCode( completion, diff --git a/packages/ai/src/steps/generateCodeStep.ts b/packages/ai/src/steps/generateCodeStep.ts index d57d1e2f63..20051dbbdd 100644 --- a/packages/ai/src/steps/generateCodeStep.ts +++ b/packages/ai/src/steps/generateCodeStep.ts @@ -138,10 +138,6 @@ export const generateCodeStep = new LLMStepTemplate( const promptPair = generateNewSquiggleCodePrompt(prompt.value); const completion = await context.queryLLM(promptPair); - if (!completion) { - return context.fail("MINOR", "No completion"); - } - const state = await generationCompletionContentToCode(completion); if (state.ok) { return { code: state.value }; diff --git a/packages/ai/src/steps/matchStyleGuideStep.ts b/packages/ai/src/steps/matchStyleGuideStep.ts index d6f515c840..d57bf9ccb8 100644 --- a/packages/ai/src/steps/matchStyleGuideStep.ts +++ b/packages/ai/src/steps/matchStyleGuideStep.ts @@ -79,11 +79,6 @@ export const matchStyleGuideStep = new LLMStepTemplate( matchStyleGuidePrompt(prompt.value, code.value) ); - if (!completion) { - // failed - return context.fail("MINOR", "No completion"); - } - // handle adjustment response const trimmedResponse = completion.trim(); const noAdjustmentRegex = From 91a0e06919f72bd1a0e7bc120fd825a8f280c09d Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Thu, 21 Nov 2024 15:39:43 -0300 Subject: [PATCH 08/17] refactor ai route --- .../ai/src/LLMClient/AnthropicProvider.ts | 20 +-- packages/ai/src/LLMClient/LLMError.ts | 1 + packages/hub/src/app/ai/api/create/route.ts | 119 ++++++++++-------- packages/hub/src/server/ai/data.ts | 6 +- packages/hub/src/server/helpers.ts | 4 +- 5 files changed, 82 insertions(+), 68 deletions(-) diff --git a/packages/ai/src/LLMClient/AnthropicProvider.ts b/packages/ai/src/LLMClient/AnthropicProvider.ts index 5727e2b768..21c0356648 100644 --- a/packages/ai/src/LLMClient/AnthropicProvider.ts +++ b/packages/ai/src/LLMClient/AnthropicProvider.ts @@ -4,16 +4,6 @@ import { MODEL_CONFIGS } from "../modelConfigs.js"; import { squiggleSystemPrompt } from "../prompts.js"; import { Message, StandardizedChatCompletion } from "./types.js"; -function extractTextContent(content: Anthropic.ContentBlock[]): string { - return content - .filter( - (block): block is Extract => - block.type === "text" - ) - .map((block) => block.text) - .join("\n"); -} - function convertToClaudeMessages(history: Message[]): Anthropic.MessageParam[] { return history .filter((msg) => msg.role !== "system") @@ -36,6 +26,16 @@ function compressAssistantMessages(messages: Message[]): Message[] { }, [] as Message[]); } +function extractTextContent(content: Anthropic.ContentBlock[]): string { + return content + .filter( + (block): block is Extract => + block.type === "text" + ) + .map((block) => block.text) + .join("\n"); +} + function convertClaudeToStandardFormat( claudeResponse: Anthropic.Message ): StandardizedChatCompletion { diff --git a/packages/ai/src/LLMClient/LLMError.ts b/packages/ai/src/LLMClient/LLMError.ts index b9d7eecdba..404db8fcee 100644 --- a/packages/ai/src/LLMClient/LLMError.ts +++ b/packages/ai/src/LLMClient/LLMError.ts @@ -2,6 +2,7 @@ export class LLMError extends Error { constructor( message: string, + // LLMStepInstance will use `kind` to decide whether the errors was critical or minor. public kind: "timeout" | "balance" | "other" ) { super(message); diff --git a/packages/hub/src/app/ai/api/create/route.ts b/packages/hub/src/app/ai/api/create/route.ts index ae799c9a03..974ce364df 100644 --- a/packages/hub/src/app/ai/api/create/route.ts +++ b/packages/hub/src/app/ai/api/create/route.ts @@ -13,7 +13,7 @@ import { getAiCodec } from "@/server/ai/utils"; import { V2WorkflowData } from "@/server/ai/v2_0"; import { getServerSession } from "@/server/helpers"; -import { aiRequestBodySchema } from "../../utils"; +import { AiRequestBody, aiRequestBodySchema } from "../../utils"; // https://nextjs.org/docs/app/api-reference/file-conventions/route-segment-config#maxduration export const maxDuration = 300; @@ -59,6 +59,68 @@ async function updateWorkflowLog(workflow: Workflow) { }); } +function aiRequestToWorkflow(request: AiRequestBody) { + // Create a SquiggleWorkflow instance + const llmConfig: LlmConfig = { + llmId: request.model ?? "Claude-Sonnet", + priceLimit: 0.3, + durationLimitMinutes: 2, + messagesInHistoryToKeep: 4, + numericSteps: request.numericSteps, + styleGuideSteps: request.styleGuideSteps, + }; + + const openaiApiKey = process.env["OPENAI_API_KEY"]; + const anthropicApiKey = + request.anthropicApiKey || process.env["ANTHROPIC_API_KEY"]; + + const workflow = + request.kind === "create" + ? createSquiggleWorkflowTemplate.instantiate({ + llmConfig, + inputs: { + prompt: new PromptArtifact(request.prompt), + }, + // abortSignal: req.signal, + openaiApiKey, + anthropicApiKey, + }) + : fixSquiggleWorkflowTemplate.instantiate({ + llmConfig, + inputs: { + source: new SourceArtifact(request.squiggleCode), + }, + // abortSignal: req.signal, + openaiApiKey, + anthropicApiKey, + }); + + return workflow; +} + +function saveWorkflowToDbOnUpdates( + workflow: Workflow, + user: Awaited> +) { + // Save workflow to the database on each update. + workflow.addEventListener("stepFinished", () => { + upsertWorkflow(user, workflow); + }); + + /* + * We save the markdown log after all steps are finished. This means that if + * the workflow fails or this route dies, there'd be no log summary. Should + * we save the log summary after each step? It'd be more expensive but more + * robust. + * (this important only in case we decide to roll back our fully + * deserializable workflows; if deserialization works well then this doesn't + * matter, the log is redundant) + */ + workflow.addEventListener("allStepsFinished", () => { + updateWorkflowLog(workflow); + }); +} + export async function POST(req: Request) { const session = await getServerSession(); @@ -72,60 +134,11 @@ export async function POST(req: Request) { const body = await req.json(); const request = aiRequestBodySchema.parse(body); - // Create a SquiggleWorkflow instance - const llmConfig: LlmConfig = { - llmId: request.model ?? "Claude-Sonnet", - priceLimit: 0.3, - durationLimitMinutes: 2, - messagesInHistoryToKeep: 4, - numericSteps: request.numericSteps, - styleGuideSteps: request.styleGuideSteps, - }; - - const openaiApiKey = process.env["OPENAI_API_KEY"]; - const anthropicApiKey = - request.anthropicApiKey || process.env["ANTHROPIC_API_KEY"]; - - const squiggleWorkflow = - request.kind === "create" - ? createSquiggleWorkflowTemplate.instantiate({ - llmConfig, - inputs: { - prompt: new PromptArtifact(request.prompt), - }, - abortSignal: req.signal, - openaiApiKey, - anthropicApiKey, - }) - : fixSquiggleWorkflowTemplate.instantiate({ - llmConfig, - inputs: { - source: new SourceArtifact(request.squiggleCode), - }, - abortSignal: req.signal, - openaiApiKey, - anthropicApiKey, - }); - - // Save workflow to the database on each update. - squiggleWorkflow.addEventListener("stepFinished", ({ workflow }) => - upsertWorkflow(user, workflow) - ); + const workflow = aiRequestToWorkflow(request); - /* - * We save the markdown log after all steps are finished. This means that if - * the workflow fails or this route dies, there'd be no log summary. Should - * we save the log summary after each step? It'd be more expensive but more - * robust. - * (this important only in case we decide to roll back our fully - * deserializable workflows; if deserialization works well then this doesn't - * matter, the log is redundant) - */ - squiggleWorkflow.addEventListener("allStepsFinished", ({ workflow }) => - updateWorkflowLog(workflow) - ); + saveWorkflowToDbOnUpdates(workflow, user); - const stream = squiggleWorkflow.runAsStream(); + const stream = workflow.runAsStream(); return new Response(stream as ReadableStream, { headers: { diff --git a/packages/hub/src/server/ai/data.ts b/packages/hub/src/server/ai/data.ts index fb8ccce084..e8d400c750 100644 --- a/packages/hub/src/server/ai/data.ts +++ b/packages/hub/src/server/ai/data.ts @@ -2,7 +2,7 @@ import "server-only"; import { prisma } from "@/prisma"; -import { getUserOrRedirect } from "../helpers"; +import { getSessionUserOrRedirect } from "../helpers"; import { decodeDbWorkflowToClientWorkflow } from "./storage"; export async function loadWorkflows({ @@ -10,12 +10,12 @@ export async function loadWorkflows({ }: { limit?: number; } = {}) { - const user = await getUserOrRedirect(); + const sessionUser = await getSessionUserOrRedirect(); const rows = await prisma.aiWorkflow.findMany({ orderBy: { createdAt: "desc" }, where: { - user: { email: user.email }, + user: { email: sessionUser.email }, }, take: limit + 1, }); diff --git a/packages/hub/src/server/helpers.ts b/packages/hub/src/server/helpers.ts index 21ebf182c8..35930df247 100644 --- a/packages/hub/src/server/helpers.ts +++ b/packages/hub/src/server/helpers.ts @@ -17,7 +17,7 @@ export async function getServerSession() { return getNextAuthServerSession(authOptions); } -export async function getUserOrRedirect() { +export async function getSessionUserOrRedirect() { const session = await getServerSession(); if (!isSignedIn(session)) { redirect("/api/auth/signin"); // TODO - callbackUrl @@ -28,7 +28,7 @@ export async function getUserOrRedirect() { export async function checkRootUser() { // TODO - unify with src/graphql/helpers - const sessionUser = await getUserOrRedirect(); + const sessionUser = await getSessionUserOrRedirect(); const user = await prisma.user.findUniqueOrThrow({ where: { email: sessionUser.email }, }); From d4ee0b96ec312978dc67a8f5cf0afc50659ec2d2 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Thu, 21 Nov 2024 16:41:51 -0300 Subject: [PATCH 09/17] remove currentStep; remove stepStarted events --- packages/ai/src/types.ts | 1 - packages/ai/src/workflows/Workflow.ts | 20 ++++--------------- packages/ai/src/workflows/streaming.ts | 16 ++++++++++++--- packages/hub/src/app/admin/dev/page.tsx | 17 ++++++++++++---- packages/hub/src/app/ai/AiDashboard.tsx | 7 +------ .../hub/src/app/ai/WorkflowSummaryItem.tsx | 4 +--- packages/hub/src/app/ai/api/create/route.ts | 2 +- packages/hub/src/server/ai/v1_0.ts | 1 - 8 files changed, 33 insertions(+), 35 deletions(-) diff --git a/packages/ai/src/types.ts b/packages/ai/src/types.ts index d5d0a84d6a..2be96c7785 100644 --- a/packages/ai/src/types.ts +++ b/packages/ai/src/types.ts @@ -126,7 +126,6 @@ const commonClientWorkflowFields = { timestamp: z.number(), // milliseconds since epoch inputs: z.record(z.string(), artifactSchema), steps: z.array(stepSchema), - currentStep: z.string().optional(), }; export const clientWorkflowSchema = z.discriminatedUnion("status", [ diff --git a/packages/ai/src/workflows/Workflow.ts b/packages/ai/src/workflows/Workflow.ts index 56de906d64..b49d28d707 100644 --- a/packages/ai/src/workflows/Workflow.ts +++ b/packages/ai/src/workflows/Workflow.ts @@ -55,12 +55,6 @@ export type WorkflowEventShape = step: LLMStepInstance; }; } - | { - type: "stepStarted"; - payload: { - step: LLMStepInstance; - }; - } | { type: "stepFinished"; payload: { @@ -177,13 +171,13 @@ export class Workflow { } private addStep( - prepatedStep: PreparedStep + preparedStep: PreparedStep ): LLMStepInstance { - // `any` is necessary because of countervariance issues. + // `any` is necessary because of contravariance issues. // But that's not important because `PreparedStep` was already strictly typed. const step: LLMStepInstance = LLMStepInstance.create({ - template: prepatedStep.template, - inputs: prepatedStep.inputs, + template: preparedStep.template, + inputs: preparedStep.inputs, workflow: this, }); @@ -202,11 +196,6 @@ export class Workflow { return; } - this.dispatchEvent({ - // should we fire this after `run()` is called? - type: "stepStarted", - payload: { step }, - }); await step.run(); this.dispatchEvent({ @@ -496,7 +485,6 @@ export class Workflow { steps: this.steps.map((step) => stepToClientStep(step as LLMStepInstance) ), - currentStep: this.getCurrentStep()?.id, ...(this.isProcessComplete() ? { status: "finished", diff --git a/packages/ai/src/workflows/streaming.ts b/packages/ai/src/workflows/streaming.ts index 056264e213..ffcff8de9a 100644 --- a/packages/ai/src/workflows/streaming.ts +++ b/packages/ai/src/workflows/streaming.ts @@ -93,7 +93,14 @@ export function addStreamingListeners( controller: ReadableStreamController ) { const send = (message: StreamingMessage) => { - controller.enqueue(JSON.stringify(message) + "\n"); + try { + controller.enqueue(JSON.stringify(message) + "\n"); + } catch (error) { + // If the connection to the client is lost, we don't want to throw errors, + // just to make sure it doesn't interfere with the workflow. + // (I'm not sure if this is important; these are happening in event handlers which shouldn't affect the main process) + console.error("Error sending message to stream", error); + } }; workflow.addEventListener("workflowStarted", (event) => { @@ -142,7 +149,11 @@ export function addStreamingListeners( kind: "finalResult", content: event.workflow.getFinalResult(), }); - controller.close(); + try { + controller.close(); + } catch (error) { + console.error("Error closing stream", error); + } }); } @@ -205,7 +216,6 @@ export async function decodeWorkflowFromReader({ case "stepAdded": await setWorkflow((workflow) => ({ ...workflow, - currentStep: event.content.name, steps: [ ...workflow.steps, { diff --git a/packages/hub/src/app/admin/dev/page.tsx b/packages/hub/src/app/admin/dev/page.tsx index ca944ae964..fb07951d34 100644 --- a/packages/hub/src/app/admin/dev/page.tsx +++ b/packages/hub/src/app/admin/dev/page.tsx @@ -26,10 +26,19 @@ export default async function () {

Prisma logs

- These buttons will enable/disable console logs from Prisma. -
- Logs are on by default in dev mode, but you might want to disable them - if you have other logs. +

These buttons will enable/disable console logs from Prisma.

+

+ Logs are on by default in dev mode, but you might want to disable + them if you have other logs. +

+

+ { + "These buttons don't work immediately, seemingly because Next.js can have multiple processes or threads? But they do work eventually." + } +

+

+ The state is not persisted between next dev runs. +

diff --git a/packages/hub/src/app/ai/AiDashboard.tsx b/packages/hub/src/app/ai/AiDashboard.tsx index 26fbd029be..178ae28d6b 100644 --- a/packages/hub/src/app/ai/AiDashboard.tsx +++ b/packages/hub/src/app/ai/AiDashboard.tsx @@ -2,17 +2,12 @@ import { FC, useRef } from "react"; -import { ClientWorkflow, ClientWorkflowResult } from "@quri/squiggle-ai"; +import { ClientWorkflow } from "@quri/squiggle-ai"; import { Sidebar } from "./Sidebar"; import { useSquiggleWorkflows } from "./useSquiggleWorkflows"; import { WorkflowViewer } from "./WorkflowViewer"; -export type SquiggleResponse = { - result?: ClientWorkflowResult; - currentStep?: string; -}; - type Props = { initialWorkflows: ClientWorkflow[]; hasMoreWorkflows: boolean; diff --git a/packages/hub/src/app/ai/WorkflowSummaryItem.tsx b/packages/hub/src/app/ai/WorkflowSummaryItem.tsx index 0541f6395c..9648aeea98 100644 --- a/packages/hub/src/app/ai/WorkflowSummaryItem.tsx +++ b/packages/hub/src/app/ai/WorkflowSummaryItem.tsx @@ -1,5 +1,3 @@ -"use client"; - import clsx from "clsx"; import { FC } from "react"; @@ -31,7 +29,7 @@ export const WorkflowSummaryItem: FC<{
{workflow.status === "loading" && (
-

{workflow.currentStep}

+

{workflow.steps.at(-1)?.name}

)} diff --git a/packages/hub/src/app/ai/api/create/route.ts b/packages/hub/src/app/ai/api/create/route.ts index 974ce364df..e6f8dd6fc4 100644 --- a/packages/hub/src/app/ai/api/create/route.ts +++ b/packages/hub/src/app/ai/api/create/route.ts @@ -103,7 +103,7 @@ function saveWorkflowToDbOnUpdates( user: Awaited> ) { // Save workflow to the database on each update. - workflow.addEventListener("stepFinished", () => { + workflow.addEventListener("stepAdded", () => { upsertWorkflow(user, workflow); }); diff --git a/packages/hub/src/server/ai/v1_0.ts b/packages/hub/src/server/ai/v1_0.ts index 39ac5e59b9..c4ba32595c 100644 --- a/packages/hub/src/server/ai/v1_0.ts +++ b/packages/hub/src/server/ai/v1_0.ts @@ -65,7 +65,6 @@ const commonV1WorkflowFields = { timestamp: z.number(), // milliseconds since epoch input: v1InputSchema, steps: z.array(v1StepSchema), - currentStep: z.string().optional(), }; const v1WorkflowResultSchema = z.object({ From 51264f0cabbf39c310a4efc50a5e310b18576db6 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Thu, 21 Nov 2024 17:00:34 -0300 Subject: [PATCH 10/17] tooltip and placeholder for api key field --- packages/hub/src/app/ai/Sidebar.tsx | 1 + packages/ui/src/forms/common/FormField.tsx | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/hub/src/app/ai/Sidebar.tsx b/packages/hub/src/app/ai/Sidebar.tsx index 5a834676f0..c3793dd8b5 100644 --- a/packages/hub/src/app/ai/Sidebar.tsx +++ b/packages/hub/src/app/ai/Sidebar.tsx @@ -209,6 +209,7 @@ Outputs: name="anthropicApiKey" label="Anthropic API Key" tooltip="Anthropic API key for using Claude." + placeholder="Optional" size="small" layout="row" /> diff --git a/packages/ui/src/forms/common/FormField.tsx b/packages/ui/src/forms/common/FormField.tsx index 57593ed515..4c855774da 100644 --- a/packages/ui/src/forms/common/FormField.tsx +++ b/packages/ui/src/forms/common/FormField.tsx @@ -22,20 +22,24 @@ export function FormField< TValues extends FieldValues, TName extends FieldPath = FieldPath, >({ - name, - rules, + // FieldLayout props label, description, - layout, standaloneLabel, + layout, + tooltip, + // FormField props + name, + rules, children, }: FormFieldProps) { return ( {children} From 56c88f2eab94aa7c680827769b27b29df0edc93d Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Thu, 14 Nov 2024 15:02:52 -0300 Subject: [PATCH 11/17] upgrade hub to next v15; patch react-select --- package.json | 3 + packages/hub/package.json | 2 +- patches/react-select.patch | 16 ++ pnpm-lock.yaml | 399 +++++++++++++++++++++---------------- 4 files changed, 252 insertions(+), 168 deletions(-) create mode 100644 patches/react-select.patch diff --git a/package.json b/package.json index 075779f19d..f07d947e23 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,9 @@ "pnpm": { "overrides": { "@headlessui/react": "^2.2.0" + }, + "patchedDependencies": { + "react-select": "patches/react-select.patch" } }, "version": "0.0.1-0", diff --git a/packages/hub/package.json b/packages/hub/package.json index 92b82a8ba3..14b34ee708 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -51,7 +51,7 @@ "immutable": "^4.3.6", "invariant": "^2.2.4", "lodash": "^4.17.21", - "next": "^14.2.15", + "next": "^15.0.3", "next-auth": "^4.24.7", "nodemailer": "^6.9.13", "pako": "^2.1.0", diff --git a/patches/react-select.patch b/patches/react-select.patch new file mode 100644 index 0000000000..b31f9caa06 --- /dev/null +++ b/patches/react-select.patch @@ -0,0 +1,16 @@ +diff --git a/dist/Select-49a62830.esm.js b/dist/Select-49a62830.esm.js +index bed08498732b023f350d24a62728371af6a4dace..ea7d4fbd21d2189fa48cd5dba17f086ee61f1e17 100644 +--- a/dist/Select-49a62830.esm.js ++++ b/dist/Select-49a62830.esm.js +@@ -2125,7 +2125,10 @@ var Select = /*#__PURE__*/function (_Component) { + 'aria-labelledby': this.props['aria-labelledby'], + 'aria-required': required, + role: 'combobox', +- 'aria-activedescendant': this.isAppleDevice ? undefined : this.state.focusedOptionId || '' ++ 'aria-activedescendant': ++ this.state.componentHasMounted && this.isAppleDevice ++ ? undefined ++ : this.state.focusedOptionId || '', + }, menuIsOpen && { + 'aria-controls': this.getElementId('listbox') + }), !isSearchable && { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7738b55c34..f2eeb6c950 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,11 @@ settings: overrides: '@headlessui/react': ^2.2.0 +patchedDependencies: + react-select: + hash: vyab3ly3abwpwnnlilsrsbv2pq + path: patches/react-select.patch + importers: .: @@ -406,7 +411,7 @@ importers: dependencies: '@next-auth/prisma-adapter': specifier: ^1.0.7 - version: 1.0.7(@prisma/client@5.22.0(prisma@5.22.0))(next-auth@4.24.7(next@14.2.17(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + version: 1.0.7(@prisma/client@5.22.0(prisma@5.22.0))(next-auth@4.24.7(next@15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@pothos/core': specifier: ^3.41.1 version: 3.41.1(graphql@16.8.1) @@ -451,7 +456,7 @@ importers: version: link:../versioned-components '@vercel/analytics': specifier: ^1.3.1 - version: 1.3.1(next@14.2.17(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 1.3.1(next@15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) base64-js: specifier: ^1.5.1 version: 1.5.1 @@ -480,11 +485,11 @@ importers: specifier: ^4.17.21 version: 4.17.21 next: - specifier: ^14.2.15 - version: 14.2.17(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^15.0.3 + version: 15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-auth: specifier: ^4.24.7 - version: 4.24.7(next@14.2.17(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 4.24.7(next@15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) nodemailer: specifier: ^6.9.13 version: 6.9.13 @@ -514,7 +519,7 @@ importers: version: 16.2.0(react@18.3.1) react-select: specifier: ^5.8.0 - version: 5.8.0(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) relay-runtime: specifier: ^16.2.0 version: 16.2.0 @@ -694,7 +699,7 @@ importers: version: 29.5.14 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + version: 29.7.0(babel-plugin-macros@3.1.0) typescript: specifier: ^5.6.3 version: 5.6.3 @@ -881,7 +886,7 @@ importers: version: 7.50.0(react@18.3.1) react-select: specifier: ^5.8.0 - version: 5.8.0(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-textarea-autosize: specifier: 8.5.4 version: 8.5.4(@types/react@18.3.3)(react@18.3.1) @@ -4033,24 +4038,18 @@ packages: '@prisma/client': '>=2.26.0 || >=3' next-auth: ^4 - '@next/env@14.2.17': - resolution: {integrity: sha512-MCgO7VHxXo8sYR/0z+sk9fGyJJU636JyRmkjc7ZJY8Hurl8df35qG5hoAh5KMs75FLjhlEo9bb2LGe89Y/scDA==} - '@next/env@15.0.0': resolution: {integrity: sha512-Mcv8ZVmEgTO3bePiH/eJ7zHqQEs2gCqZ0UId2RxHmDDc7Pw6ngfSrOFlxG8XDpaex+n2G+TKPsQAf28MO+88Gw==} '@next/env@15.0.2': resolution: {integrity: sha512-c0Zr0ModK5OX7D4ZV8Jt/wqoXtitLNPwUfG9zElCZztdaZyNVnN40rDXVZ/+FGuR4CcNV5AEfM6N8f+Ener7Dg==} + '@next/env@15.0.3': + resolution: {integrity: sha512-t9Xy32pjNOvVn2AS+Utt6VmyrshbpfUMhIjFO60gI58deSo/KgLOp31XZ4O+kY/Is8WAGYwA5gR7kOb1eORDBA==} + '@next/eslint-plugin-next@14.2.3': resolution: {integrity: sha512-L3oDricIIjgj1AVnRdRor21gI7mShlSwU/1ZGHmqM3LzHhXXhdkrfeNY5zif25Bi5Dd7fiJHsbhoZCHfXYvlAw==} - '@next/swc-darwin-arm64@14.2.17': - resolution: {integrity: sha512-WiOf5nElPknrhRMTipXYTJcUz7+8IAjOYw3vXzj3BYRcVY0hRHKWgTgQ5439EvzQyHEko77XK+yN9x9OJ0oOog==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - '@next/swc-darwin-arm64@15.0.0': resolution: {integrity: sha512-Gjgs3N7cFa40a9QT9AEHnuGKq69/bvIOn0SLGDV+ordq07QOP4k1GDOVedMHEjVeqy1HBLkL8rXnNTuMZIv79A==} engines: {node: '>= 10'} @@ -4063,10 +4062,10 @@ packages: cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@14.2.17': - resolution: {integrity: sha512-29y425wYnL17cvtxrDQWC3CkXe/oRrdt8ie61S03VrpwpPRI0XsnTvtKO06XCisK4alaMnZlf8riwZIbJTaSHQ==} + '@next/swc-darwin-arm64@15.0.3': + resolution: {integrity: sha512-s3Q/NOorCsLYdCKvQlWU+a+GeAd3C8Rb3L1YnetsgwXzhc3UTWrtQpB/3eCjFOdGUj5QmXfRak12uocd1ZiiQw==} engines: {node: '>= 10'} - cpu: [x64] + cpu: [arm64] os: [darwin] '@next/swc-darwin-x64@15.0.0': @@ -4081,11 +4080,11 @@ packages: cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@14.2.17': - resolution: {integrity: sha512-SSHLZls3ZwNEHsc+d0ynKS+7Af0Nr8+KTUBAy9pm6xz9SHkJ/TeuEg6W3cbbcMSh6j4ITvrjv3Oi8n27VR+IPw==} + '@next/swc-darwin-x64@15.0.3': + resolution: {integrity: sha512-Zxl/TwyXVZPCFSf0u2BNj5sE0F2uR6iSKxWpq4Wlk/Sv9Ob6YCKByQTkV2y6BCic+fkabp9190hyrDdPA/dNrw==} engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] + cpu: [x64] + os: [darwin] '@next/swc-linux-arm64-gnu@15.0.0': resolution: {integrity: sha512-sbCoEpuWUBpYoLSgYrk0CkBv8RFv4ZlPxbwqRHr/BWDBJppTBtF53EvsntlfzQJ9fosYX12xnS6ltxYYwsMBjg==} @@ -4099,8 +4098,8 @@ packages: cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@14.2.17': - resolution: {integrity: sha512-VFge37us5LNPatB4F7iYeuGs9Dprqe4ZkW7lOEJM91r+Wf8EIdViWHLpIwfdDXinvCdLl6b4VyLpEBwpkctJHA==} + '@next/swc-linux-arm64-gnu@15.0.3': + resolution: {integrity: sha512-T5+gg2EwpsY3OoaLxUIofmMb7ohAUlcNZW0fPQ6YAutaWJaxt1Z1h+8zdl4FRIOr5ABAAhXtBcpkZNwUcKI2fw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] @@ -4117,10 +4116,10 @@ packages: cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@14.2.17': - resolution: {integrity: sha512-aaQlpxUVb9RZ41adlTYVQ3xvYEfBPUC8+6rDgmQ/0l7SvK8S1YNJzPmDPX6a4t0jLtIoNk7j+nroS/pB4nx7vQ==} + '@next/swc-linux-arm64-musl@15.0.3': + resolution: {integrity: sha512-WkAk6R60mwDjH4lG/JBpb2xHl2/0Vj0ZRu1TIzWuOYfQ9tt9NFsIinI1Epma77JVgy81F32X/AeD+B2cBu/YQA==} engines: {node: '>= 10'} - cpu: [x64] + cpu: [arm64] os: [linux] '@next/swc-linux-x64-gnu@15.0.0': @@ -4135,8 +4134,8 @@ packages: cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@14.2.17': - resolution: {integrity: sha512-HSyEiFaEY3ay5iATDqEup5WAfrhMATNJm8dYx3ZxL+e9eKv10XKZCwtZByDoLST7CyBmyDz+OFJL1wigyXeaoA==} + '@next/swc-linux-x64-gnu@15.0.3': + resolution: {integrity: sha512-gWL/Cta1aPVqIGgDb6nxkqy06DkwJ9gAnKORdHWX1QBbSZZB+biFYPFti8aKIQL7otCE1pjyPaXpFzGeG2OS2w==} engines: {node: '>= 10'} cpu: [x64] os: [linux] @@ -4153,11 +4152,11 @@ packages: cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@14.2.17': - resolution: {integrity: sha512-h5qM9Btqv87eYH8ArrnLoAHLyi79oPTP2vlGNSg4CDvUiXgi7l0+5KuEGp5pJoMhjuv9ChRdm7mRlUUACeBt4w==} + '@next/swc-linux-x64-musl@15.0.3': + resolution: {integrity: sha512-QQEMwFd8r7C0GxQS62Zcdy6GKx999I/rTO2ubdXEe+MlZk9ZiinsrjwoiBL5/57tfyjikgh6GOU2WRQVUej3UA==} engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] + cpu: [x64] + os: [linux] '@next/swc-win32-arm64-msvc@15.0.0': resolution: {integrity: sha512-ZOd7c/Lz1lv7qP/KzR513XEa7QzW5/P0AH3A5eR1+Z/KmDOvMucht0AozccPc0TqhdV1xaXmC0Fdx0hoNzk6ng==} @@ -4171,16 +4170,10 @@ packages: cpu: [arm64] os: [win32] - '@next/swc-win32-ia32-msvc@14.2.17': - resolution: {integrity: sha512-BD/G++GKSLexQjdyoEUgyo5nClU7er5rK0sE+HlEqnldJSm96CIr/+YOTT063LVTT/dUOeQsNgp5DXr86/K7/A==} + '@next/swc-win32-arm64-msvc@15.0.3': + resolution: {integrity: sha512-9TEp47AAd/ms9fPNgtgnT7F3M1Hf7koIYYWCMQ9neOwjbVWJsHZxrFbI3iEDJ8rf1TDGpmHbKxXf2IFpAvheIQ==} engines: {node: '>= 10'} - cpu: [ia32] - os: [win32] - - '@next/swc-win32-x64-msvc@14.2.17': - resolution: {integrity: sha512-vkQfN1+4V4KqDibkW2q0sJ6CxQuXq5l2ma3z0BRcfIqkAMZiiW67T9yCpwqJKP68QghBtPEFjPAlaqe38O6frw==} - engines: {node: '>= 10'} - cpu: [x64] + cpu: [arm64] os: [win32] '@next/swc-win32-x64-msvc@15.0.0': @@ -4195,6 +4188,12 @@ packages: cpu: [x64] os: [win32] + '@next/swc-win32-x64-msvc@15.0.3': + resolution: {integrity: sha512-VNAz+HN4OGgvZs6MOoVfnn41kBzT+M+tB+OK4cww6DNyWS6wKaDpaAm/qLeOUbnMh0oVx1+mg0uoYARF69dJyA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -5614,8 +5613,8 @@ packages: '@swc/helpers@0.5.13': resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==} - '@swc/helpers@0.5.5': - resolution: {integrity: sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==} + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} '@tailwindcss/forms@0.5.7': resolution: {integrity: sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==} @@ -6820,6 +6819,9 @@ packages: caniuse-lite@1.0.30001679: resolution: {integrity: sha512-j2YqID/YwpLnKzCmBOS4tlZdWprXm3ZmQLBH9ZBXFOhoxLA46fwyBvx6toCBWBmnuwUY/qB3kEU6gFx8qgCroA==} + caniuse-lite@1.0.30001680: + resolution: {integrity: sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==} + capital-case@1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} @@ -10256,34 +10258,37 @@ packages: react: ^16.8 || ^17 || ^18 react-dom: ^16.8 || ^17 || ^18 - next@14.2.17: - resolution: {integrity: sha512-hNo/Zy701DDO3nzKkPmsLRlDfNCtb1OJxFUvjGEl04u7SFa3zwC6hqsOUzMajcaEOEV8ey1GjvByvrg0Qr5AiQ==} - engines: {node: '>=18.17.0'} + next@15.0.0: + resolution: {integrity: sha512-/ivqF6gCShXpKwY9hfrIQYh8YMge8L3W+w1oRLv/POmK4MOQnh+FscZ8a0fRFTSQWE+2z9ctNYvELD9vP2FV+A==} + engines: {node: '>=18.18.0'} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 '@playwright/test': ^1.41.2 - react: ^18.2.0 - react-dom: ^18.2.0 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-65a56d0e-20241020 + react-dom: ^18.2.0 || 19.0.0-rc-65a56d0e-20241020 sass: ^1.3.0 peerDependenciesMeta: '@opentelemetry/api': optional: true '@playwright/test': optional: true + babel-plugin-react-compiler: + optional: true sass: optional: true - next@15.0.0: - resolution: {integrity: sha512-/ivqF6gCShXpKwY9hfrIQYh8YMge8L3W+w1oRLv/POmK4MOQnh+FscZ8a0fRFTSQWE+2z9ctNYvELD9vP2FV+A==} + next@15.0.2: + resolution: {integrity: sha512-rxIWHcAu4gGSDmwsELXacqAPUk+j8dV/A9cDF5fsiCMpkBDYkO2AEaL1dfD+nNmDiU6QMCFN8Q30VEKapT9UHQ==} engines: {node: '>=18.18.0'} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 '@playwright/test': ^1.41.2 babel-plugin-react-compiler: '*' - react: ^18.2.0 || 19.0.0-rc-65a56d0e-20241020 - react-dom: ^18.2.0 || 19.0.0-rc-65a56d0e-20241020 + react: ^18.2.0 || 19.0.0-rc-02c0e824-20241028 + react-dom: ^18.2.0 || 19.0.0-rc-02c0e824-20241028 sass: ^1.3.0 peerDependenciesMeta: '@opentelemetry/api': @@ -10295,16 +10300,16 @@ packages: sass: optional: true - next@15.0.2: - resolution: {integrity: sha512-rxIWHcAu4gGSDmwsELXacqAPUk+j8dV/A9cDF5fsiCMpkBDYkO2AEaL1dfD+nNmDiU6QMCFN8Q30VEKapT9UHQ==} - engines: {node: '>=18.18.0'} + next@15.0.3: + resolution: {integrity: sha512-ontCbCRKJUIoivAdGB34yCaOcPgYXr9AAkV/IwqFfWWTXEPUgLYkSkqBhIk9KK7gGmgjc64B+RdoeIDM13Irnw==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 '@playwright/test': ^1.41.2 babel-plugin-react-compiler: '*' - react: ^18.2.0 || 19.0.0-rc-02c0e824-20241028 - react-dom: ^18.2.0 || 19.0.0-rc-02c0e824-20241028 + react: ^18.2.0 || 19.0.0-rc-66855b96-20241106 + react-dom: ^18.2.0 || 19.0.0-rc-66855b96-20241106 sass: ^1.3.0 peerDependenciesMeta: '@opentelemetry/api': @@ -12045,19 +12050,6 @@ packages: style-to-object@0.4.4: resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==} - styled-jsx@5.1.1: - resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==} - engines: {node: '>= 12.0.0'} - peerDependencies: - '@babel/core': '*' - babel-plugin-macros: '*' - react: '>= 16.8.0 || 17.x.x || ^18.0.0-0' - peerDependenciesMeta: - '@babel/core': - optional: true - babel-plugin-macros: - optional: true - styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -13326,7 +13318,7 @@ snapshots: '@babel/core': 7.26.0 '@babel/generator': 7.24.1 '@babel/parser': 7.26.2 - '@babel/runtime': 7.24.5 + '@babel/runtime': 7.26.0 '@babel/traverse': 7.24.1 '@babel/types': 7.24.0 babel-preset-fbjs: 3.4.0(@babel/core@7.26.0) @@ -16266,6 +16258,41 @@ snapshots: jest-util: 29.7.0 slash: 3.0.0 + '@jest/core@29.7.0(babel-plugin-macros@3.1.0)': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.9.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.8.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.6.3))': dependencies: '@jest/console': 29.7.0 @@ -16614,32 +16641,29 @@ snapshots: pump: 3.0.0 tar-fs: 2.1.1 - '@next-auth/prisma-adapter@1.0.7(@prisma/client@5.22.0(prisma@5.22.0))(next-auth@4.24.7(next@14.2.17(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': + '@next-auth/prisma-adapter@1.0.7(@prisma/client@5.22.0(prisma@5.22.0))(next-auth@4.24.7(next@15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: '@prisma/client': 5.22.0(prisma@5.22.0) - next-auth: 4.24.7(next@14.2.17(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - - '@next/env@14.2.17': {} + next-auth: 4.24.7(next@15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@next/env@15.0.0': optional: true '@next/env@15.0.2': {} + '@next/env@15.0.3': {} + '@next/eslint-plugin-next@14.2.3': dependencies: glob: 10.3.10 - '@next/swc-darwin-arm64@14.2.17': - optional: true - '@next/swc-darwin-arm64@15.0.0': optional: true '@next/swc-darwin-arm64@15.0.2': optional: true - '@next/swc-darwin-x64@14.2.17': + '@next/swc-darwin-arm64@15.0.3': optional: true '@next/swc-darwin-x64@15.0.0': @@ -16648,7 +16672,7 @@ snapshots: '@next/swc-darwin-x64@15.0.2': optional: true - '@next/swc-linux-arm64-gnu@14.2.17': + '@next/swc-darwin-x64@15.0.3': optional: true '@next/swc-linux-arm64-gnu@15.0.0': @@ -16657,7 +16681,7 @@ snapshots: '@next/swc-linux-arm64-gnu@15.0.2': optional: true - '@next/swc-linux-arm64-musl@14.2.17': + '@next/swc-linux-arm64-gnu@15.0.3': optional: true '@next/swc-linux-arm64-musl@15.0.0': @@ -16666,7 +16690,7 @@ snapshots: '@next/swc-linux-arm64-musl@15.0.2': optional: true - '@next/swc-linux-x64-gnu@14.2.17': + '@next/swc-linux-arm64-musl@15.0.3': optional: true '@next/swc-linux-x64-gnu@15.0.0': @@ -16675,7 +16699,7 @@ snapshots: '@next/swc-linux-x64-gnu@15.0.2': optional: true - '@next/swc-linux-x64-musl@14.2.17': + '@next/swc-linux-x64-gnu@15.0.3': optional: true '@next/swc-linux-x64-musl@15.0.0': @@ -16684,7 +16708,7 @@ snapshots: '@next/swc-linux-x64-musl@15.0.2': optional: true - '@next/swc-win32-arm64-msvc@14.2.17': + '@next/swc-linux-x64-musl@15.0.3': optional: true '@next/swc-win32-arm64-msvc@15.0.0': @@ -16693,10 +16717,7 @@ snapshots: '@next/swc-win32-arm64-msvc@15.0.2': optional: true - '@next/swc-win32-ia32-msvc@14.2.17': - optional: true - - '@next/swc-win32-x64-msvc@14.2.17': + '@next/swc-win32-arm64-msvc@15.0.3': optional: true '@next/swc-win32-x64-msvc@15.0.0': @@ -16705,6 +16726,9 @@ snapshots: '@next/swc-win32-x64-msvc@15.0.2': optional: true + '@next/swc-win32-x64-msvc@15.0.3': + optional: true + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -17336,7 +17360,7 @@ snapshots: react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: 18.3.1(react@18.3.1) react-hook-form: 7.50.0(react@18.3.1) - react-select: 5.8.0(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-select: 5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-textarea-autosize: 8.5.2(@types/react@18.3.3)(react@18.3.1) react-use: 17.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: @@ -17352,7 +17376,7 @@ snapshots: react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: 18.3.1(react@18.3.1) react-hook-form: 7.50.0(react@18.3.1) - react-select: 5.8.0(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-select: 5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-textarea-autosize: 8.5.3(@types/react@18.3.3)(react@18.3.1) react-use: 17.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: @@ -17368,7 +17392,7 @@ snapshots: react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: 18.3.1(react@18.3.1) react-hook-form: 7.50.0(react@18.3.1) - react-select: 5.8.0(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-select: 5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-textarea-autosize: 8.5.3(@types/react@18.3.3)(react@18.3.1) react-use: 17.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: @@ -17384,7 +17408,7 @@ snapshots: react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: 18.3.1(react@18.3.1) react-hook-form: 7.50.0(react@18.3.1) - react-select: 5.8.0(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-select: 5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-textarea-autosize: 8.5.3(@types/react@18.3.3)(react@18.3.1) react-use: 17.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: @@ -17400,7 +17424,7 @@ snapshots: react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: 18.3.1(react@18.3.1) react-hook-form: 7.50.0(react@18.3.1) - react-select: 5.8.0(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-select: 5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-textarea-autosize: 8.5.3(@types/react@18.3.3)(react@18.3.1) react-use: 17.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: @@ -17410,7 +17434,7 @@ snapshots: '@radix-ui/primitive@1.0.1': dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 '@radix-ui/primitive@1.1.0': {} @@ -17470,7 +17494,7 @@ snapshots: '@radix-ui/react-compose-refs@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 react: 18.3.1 optionalDependencies: '@types/react': 18.3.3 @@ -17483,7 +17507,7 @@ snapshots: '@radix-ui/react-context@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 react: 18.3.1 optionalDependencies: '@types/react': 18.3.3 @@ -17553,7 +17577,7 @@ snapshots: '@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 '@radix-ui/primitive': 1.0.1 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -17580,7 +17604,7 @@ snapshots: '@radix-ui/react-focus-guards@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 react: 18.3.1 optionalDependencies: '@types/react': 18.3.3 @@ -17593,7 +17617,7 @@ snapshots: '@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) @@ -17616,7 +17640,7 @@ snapshots: '@radix-ui/react-id@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) react: 18.3.1 optionalDependencies: @@ -17694,7 +17718,7 @@ snapshots: '@radix-ui/react-portal@1.0.4(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -17714,7 +17738,7 @@ snapshots: '@radix-ui/react-presence@1.0.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.3.3)(react@18.3.1) '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.3.3)(react@18.3.1) react: 18.3.1 @@ -17735,7 +17759,7 @@ snapshots: '@radix-ui/react-primitive@1.0.3(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 '@radix-ui/react-slot': 1.0.2(@types/react@18.3.3)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -17819,7 +17843,7 @@ snapshots: '@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 react: 18.3.1 optionalDependencies: '@types/react': 18.3.3 @@ -17832,7 +17856,7 @@ snapshots: '@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) react: 18.3.1 optionalDependencies: @@ -17847,7 +17871,7 @@ snapshots: '@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.3.3)(react@18.3.1) react: 18.3.1 optionalDependencies: @@ -17862,7 +17886,7 @@ snapshots: '@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.3.3)(react@18.3.1)': dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 react: 18.3.1 optionalDependencies: '@types/react': 18.3.3 @@ -17909,7 +17933,7 @@ snapshots: '@react-aria/interactions': 3.21.3(react@18.3.1) '@react-aria/utils': 3.24.1(react@18.3.1) '@react-types/shared': 3.23.1(react@18.3.1) - '@swc/helpers': 0.5.13 + '@swc/helpers': 0.5.15 clsx: 2.1.1 react: 18.3.1 @@ -17918,12 +17942,12 @@ snapshots: '@react-aria/ssr': 3.9.4(react@18.3.1) '@react-aria/utils': 3.24.1(react@18.3.1) '@react-types/shared': 3.23.1(react@18.3.1) - '@swc/helpers': 0.5.13 + '@swc/helpers': 0.5.15 react: 18.3.1 '@react-aria/ssr@3.9.4(react@18.3.1)': dependencies: - '@swc/helpers': 0.5.13 + '@swc/helpers': 0.5.15 react: 18.3.1 '@react-aria/utils@3.24.1(react@18.3.1)': @@ -17931,7 +17955,7 @@ snapshots: '@react-aria/ssr': 3.9.4(react@18.3.1) '@react-stately/utils': 3.10.1(react@18.3.1) '@react-types/shared': 3.23.1(react@18.3.1) - '@swc/helpers': 0.5.13 + '@swc/helpers': 0.5.15 clsx: 2.1.1 react: 18.3.1 @@ -17958,7 +17982,7 @@ snapshots: '@react-stately/utils@3.10.1(react@18.3.1)': dependencies: - '@swc/helpers': 0.5.13 + '@swc/helpers': 0.5.15 react: 18.3.1 '@react-types/shared@3.23.1(react@18.3.1)': @@ -19091,9 +19115,8 @@ snapshots: dependencies: tslib: 2.8.1 - '@swc/helpers@0.5.5': + '@swc/helpers@0.5.15': dependencies: - '@swc/counter': 0.1.3 tslib: 2.8.1 '@tailwindcss/forms@0.5.7(tailwindcss@3.4.14(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)))': @@ -19776,19 +19799,19 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vercel/analytics@1.3.1(next@14.2.17(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@vercel/analytics@1.3.1(next@15.0.2(react-dom@19.0.0-rc-cae764ce-20241025(react@19.0.0-rc-cae764ce-20241025))(react@19.0.0-rc-cae764ce-20241025))(react@19.0.0-rc-cae764ce-20241025)': dependencies: server-only: 0.0.1 optionalDependencies: - next: 14.2.17(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - react: 18.3.1 + next: 15.0.2(react-dom@19.0.0-rc-cae764ce-20241025(react@19.0.0-rc-cae764ce-20241025))(react@19.0.0-rc-cae764ce-20241025) + react: 19.0.0-rc-cae764ce-20241025 - '@vercel/analytics@1.3.1(next@15.0.2(react-dom@19.0.0-rc-cae764ce-20241025(react@19.0.0-rc-cae764ce-20241025))(react@19.0.0-rc-cae764ce-20241025))(react@19.0.0-rc-cae764ce-20241025)': + '@vercel/analytics@1.3.1(next@15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: server-only: 0.0.1 optionalDependencies: - next: 15.0.2(react-dom@19.0.0-rc-cae764ce-20241025(react@19.0.0-rc-cae764ce-20241025))(react@19.0.0-rc-cae764ce-20241025) - react: 19.0.0-rc-cae764ce-20241025 + next: 15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 '@vitest/expect@1.3.1': dependencies: @@ -20624,6 +20647,8 @@ snapshots: caniuse-lite@1.0.30001679: {} + caniuse-lite@1.0.30001680: {} + capital-case@1.0.4: dependencies: no-case: 3.0.4 @@ -21080,6 +21105,21 @@ snapshots: - supports-color - ts-node + create-jest@29.7.0(babel-plugin-macros@3.1.0): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@20.12.7)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.6.3)) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + create-require@1.1.1: {} crelt@1.0.5: {} @@ -21559,7 +21599,7 @@ snapshots: didyoumean2@6.0.1: dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 fastest-levenshtein: 1.0.16 lodash.deburr: 4.1.0 @@ -22077,7 +22117,7 @@ snapshots: eslint-plugin-jsx-a11y@6.7.1(eslint@8.57.0): dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 aria-query: 5.3.0 array-includes: 3.1.7 array.prototype.flatmap: 1.3.2 @@ -23784,6 +23824,25 @@ snapshots: - supports-color - ts-node + jest-cli@29.7.0(babel-plugin-macros@3.1.0): + dependencies: + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(babel-plugin-macros@3.1.0) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@20.12.7)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.6.3)) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest-config@29.7.0(@types/node@20.12.7)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.6.3)): dependencies: '@babel/core': 7.26.0 @@ -24131,6 +24190,18 @@ snapshots: - supports-color - ts-node + jest@29.7.0(babel-plugin-macros@3.1.0): + dependencies: + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0) + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(babel-plugin-macros@3.1.0) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jiti@1.20.0: {} jiti@1.21.0: {} @@ -24296,7 +24367,7 @@ snapshots: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.6.0 + semver: 7.6.3 jstat@1.9.6: {} @@ -25473,13 +25544,13 @@ snapshots: neo-async@2.6.2: {} - next-auth@4.24.7(next@14.2.17(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next-auth@4.24.7(next@15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.23.9 '@panva/hkdf': 1.1.1 cookie: 0.5.0 jose: 4.15.5 - next: 14.2.17(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) oauth: 0.9.15 openid-client: 5.6.1 preact: 10.18.1 @@ -25495,42 +25566,17 @@ snapshots: react: 19.0.0-rc-cae764ce-20241025 react-dom: 19.0.0-rc-cae764ce-20241025(react@19.0.0-rc-cae764ce-20241025) - next@14.2.17(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@next/env': 14.2.17 - '@swc/helpers': 0.5.5 - busboy: 1.6.0 - caniuse-lite: 1.0.30001679 - graceful-fs: 4.2.11 - postcss: 8.4.31 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.1(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react@18.3.1) - optionalDependencies: - '@next/swc-darwin-arm64': 14.2.17 - '@next/swc-darwin-x64': 14.2.17 - '@next/swc-linux-arm64-gnu': 14.2.17 - '@next/swc-linux-arm64-musl': 14.2.17 - '@next/swc-linux-x64-gnu': 14.2.17 - '@next/swc-linux-x64-musl': 14.2.17 - '@next/swc-win32-arm64-msvc': 14.2.17 - '@next/swc-win32-ia32-msvc': 14.2.17 - '@next/swc-win32-x64-msvc': 14.2.17 - transitivePeerDependencies: - - '@babel/core' - - babel-plugin-macros - next@15.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@next/env': 15.0.0 '@swc/counter': 0.1.3 '@swc/helpers': 0.5.13 busboy: 1.6.0 - caniuse-lite: 1.0.30001679 + caniuse-lite: 1.0.30001680 postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.6(react@18.3.1) + styled-jsx: 5.1.6(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react@18.3.1) optionalDependencies: '@next/swc-darwin-arm64': 15.0.0 '@next/swc-darwin-x64': 15.0.0 @@ -25571,6 +25617,31 @@ snapshots: - '@babel/core' - babel-plugin-macros + next@15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@next/env': 15.0.3 + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.13 + busboy: 1.6.0 + caniuse-lite: 1.0.30001680 + postcss: 8.4.31 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + styled-jsx: 5.1.6(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react@18.3.1) + optionalDependencies: + '@next/swc-darwin-arm64': 15.0.3 + '@next/swc-darwin-x64': 15.0.3 + '@next/swc-linux-arm64-gnu': 15.0.3 + '@next/swc-linux-arm64-musl': 15.0.3 + '@next/swc-linux-x64-gnu': 15.0.3 + '@next/swc-linux-x64-musl': 15.0.3 + '@next/swc-win32-arm64-msvc': 15.0.3 + '@next/swc-win32-x64-msvc': 15.0.3 + sharp: 0.33.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + no-case@3.0.4: dependencies: lower-case: 2.0.2 @@ -25578,7 +25649,7 @@ snapshots: node-abi@3.47.0: dependencies: - semver: 7.6.0 + semver: 7.6.3 optional: true node-addon-api@4.3.0: @@ -26552,7 +26623,7 @@ snapshots: transitivePeerDependencies: - react-dom - react-select@5.8.0(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-select@5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.23.9 '@emotion/cache': 11.11.0 @@ -26588,7 +26659,7 @@ snapshots: react-textarea-autosize@8.5.2(@types/react@18.3.3)(react@18.3.1): dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 react: 18.3.1 use-composed-ref: 1.3.0(react@18.3.1) use-latest: 1.2.1(@types/react@18.3.3)(react@18.3.1) @@ -26597,7 +26668,7 @@ snapshots: react-textarea-autosize@8.5.3(@types/react@18.3.3)(react@18.3.1): dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 react: 18.3.1 use-composed-ref: 1.3.0(react@18.3.1) use-latest: 1.2.1(@types/react@18.3.3)(react@18.3.1) @@ -26892,7 +26963,7 @@ snapshots: relay-runtime@12.0.0: dependencies: - '@babel/runtime': 7.25.6 + '@babel/runtime': 7.26.0 fbjs: 3.0.5 invariant: 2.2.4 transitivePeerDependencies: @@ -27640,7 +27711,7 @@ snapshots: dependencies: inline-style-parser: 0.1.1 - styled-jsx@5.1.1(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react@18.3.1): + styled-jsx@5.1.6(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react@18.3.1): dependencies: client-only: 0.0.1 react: 18.3.1 @@ -27648,12 +27719,6 @@ snapshots: '@babel/core': 7.26.0 babel-plugin-macros: 3.1.0 - styled-jsx@5.1.6(react@18.3.1): - dependencies: - client-only: 0.0.1 - react: 18.3.1 - optional: true - styled-jsx@5.1.6(react@19.0.0-rc-cae764ce-20241025): dependencies: client-only: 0.0.1 From 9c910a6cb42b32954bc8b4e5b92d0ee313b154f1 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Thu, 14 Nov 2024 15:09:32 -0300 Subject: [PATCH 12/17] more react-select patches --- patches/react-select.patch | 32 ++++++++++++++++++++++++++++++++ pnpm-lock.yaml | 18 +++++++++--------- 2 files changed, 41 insertions(+), 9 deletions(-) diff --git a/patches/react-select.patch b/patches/react-select.patch index b31f9caa06..e1ced9e764 100644 --- a/patches/react-select.patch +++ b/patches/react-select.patch @@ -14,3 +14,35 @@ index bed08498732b023f350d24a62728371af6a4dace..ea7d4fbd21d2189fa48cd5dba17f086e }, menuIsOpen && { 'aria-controls': this.getElementId('listbox') }), !isSearchable && { +diff --git a/dist/Select-5dacb5ba.cjs.prod.js b/dist/Select-5dacb5ba.cjs.prod.js +index ea9203fb889d23825493d63964aad8229e758517..90f700cfa1d07fdbf169b9ca3b25af367622c37c 100644 +--- a/dist/Select-5dacb5ba.cjs.prod.js ++++ b/dist/Select-5dacb5ba.cjs.prod.js +@@ -2130,7 +2130,10 @@ var Select = /*#__PURE__*/function (_Component) { + 'aria-labelledby': this.props['aria-labelledby'], + 'aria-required': required, + role: 'combobox', +- 'aria-activedescendant': this.isAppleDevice ? undefined : this.state.focusedOptionId || '' ++ 'aria-activedescendant': ++ this.state.componentHasMounted && this.isAppleDevice ++ ? undefined ++ : this.state.focusedOptionId || '', + }, menuIsOpen && { + 'aria-controls': this.getElementId('listbox') + }), !isSearchable && { +diff --git a/dist/Select-d63eed7b.cjs.dev.js b/dist/Select-d63eed7b.cjs.dev.js +index b3858a3b953490f3089fd60e97e1e7555b22ec15..fc39df89b770aaa97df264a60c085adeb80f697c 100644 +--- a/dist/Select-d63eed7b.cjs.dev.js ++++ b/dist/Select-d63eed7b.cjs.dev.js +@@ -2149,7 +2149,10 @@ var Select = /*#__PURE__*/function (_Component) { + 'aria-labelledby': this.props['aria-labelledby'], + 'aria-required': required, + role: 'combobox', +- 'aria-activedescendant': this.isAppleDevice ? undefined : this.state.focusedOptionId || '' ++ 'aria-activedescendant': ++ this.state.componentHasMounted && this.isAppleDevice ++ ? undefined ++ : this.state.focusedOptionId || '', + }, menuIsOpen && { + 'aria-controls': this.getElementId('listbox') + }), !isSearchable && { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f2eeb6c950..ace56783a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,7 +9,7 @@ overrides: patchedDependencies: react-select: - hash: vyab3ly3abwpwnnlilsrsbv2pq + hash: pok3nxq32ihaf3qpdecuz4j5ea path: patches/react-select.patch importers: @@ -519,7 +519,7 @@ importers: version: 16.2.0(react@18.3.1) react-select: specifier: ^5.8.0 - version: 5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 5.8.0(patch_hash=pok3nxq32ihaf3qpdecuz4j5ea)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) relay-runtime: specifier: ^16.2.0 version: 16.2.0 @@ -886,7 +886,7 @@ importers: version: 7.50.0(react@18.3.1) react-select: specifier: ^5.8.0 - version: 5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 5.8.0(patch_hash=pok3nxq32ihaf3qpdecuz4j5ea)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-textarea-autosize: specifier: 8.5.4 version: 8.5.4(@types/react@18.3.3)(react@18.3.1) @@ -17360,7 +17360,7 @@ snapshots: react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: 18.3.1(react@18.3.1) react-hook-form: 7.50.0(react@18.3.1) - react-select: 5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-select: 5.8.0(patch_hash=pok3nxq32ihaf3qpdecuz4j5ea)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-textarea-autosize: 8.5.2(@types/react@18.3.3)(react@18.3.1) react-use: 17.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: @@ -17376,7 +17376,7 @@ snapshots: react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: 18.3.1(react@18.3.1) react-hook-form: 7.50.0(react@18.3.1) - react-select: 5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-select: 5.8.0(patch_hash=pok3nxq32ihaf3qpdecuz4j5ea)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-textarea-autosize: 8.5.3(@types/react@18.3.3)(react@18.3.1) react-use: 17.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: @@ -17392,7 +17392,7 @@ snapshots: react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: 18.3.1(react@18.3.1) react-hook-form: 7.50.0(react@18.3.1) - react-select: 5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-select: 5.8.0(patch_hash=pok3nxq32ihaf3qpdecuz4j5ea)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-textarea-autosize: 8.5.3(@types/react@18.3.3)(react@18.3.1) react-use: 17.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: @@ -17408,7 +17408,7 @@ snapshots: react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: 18.3.1(react@18.3.1) react-hook-form: 7.50.0(react@18.3.1) - react-select: 5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-select: 5.8.0(patch_hash=pok3nxq32ihaf3qpdecuz4j5ea)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-textarea-autosize: 8.5.3(@types/react@18.3.3)(react@18.3.1) react-use: 17.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: @@ -17424,7 +17424,7 @@ snapshots: react-colorful: 5.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-dom: 18.3.1(react@18.3.1) react-hook-form: 7.50.0(react@18.3.1) - react-select: 5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-select: 5.8.0(patch_hash=pok3nxq32ihaf3qpdecuz4j5ea)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-textarea-autosize: 8.5.3(@types/react@18.3.3)(react@18.3.1) react-use: 17.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: @@ -26623,7 +26623,7 @@ snapshots: transitivePeerDependencies: - react-dom - react-select@5.8.0(patch_hash=vyab3ly3abwpwnnlilsrsbv2pq)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + react-select@5.8.0(patch_hash=pok3nxq32ihaf3qpdecuz4j5ea)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.23.9 '@emotion/cache': 11.11.0 From b1ba9edd7e500a36a8c99a9659b27f94b71b2ec1 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Fri, 22 Nov 2024 01:43:23 -0300 Subject: [PATCH 13/17] next-auth v5 --- packages/hub/package.json | 4 +- packages/hub/src/app/admin/layout.tsx | 4 +- .../src/app/ai/analytics/code-errors/page.tsx | 6 +- packages/hub/src/app/ai/api/create/route.ts | 4 +- packages/hub/src/app/ai/page.tsx | 4 +- .../src/app/api/auth/[...nextauth]/route.ts | 8 +- packages/hub/src/app/api/graphql/route.ts | 4 +- packages/hub/src/app/layout.tsx | 4 +- .../[...nextauth]/authOptions.ts => auth.ts} | 18 +- packages/hub/src/server/helpers.ts | 9 +- pnpm-lock.yaml | 286 ++++++++---------- 11 files changed, 156 insertions(+), 195 deletions(-) rename packages/hub/src/{app/api/auth/[...nextauth]/authOptions.ts => auth.ts} (78%) diff --git a/packages/hub/package.json b/packages/hub/package.json index 14b34ee708..33f37907f8 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -26,7 +26,7 @@ "build-last-revision": "pnpm build:esbuild && node ./dist/scripts/buildRecentModelRevision/main.mjs" }, "dependencies": { - "@next-auth/prisma-adapter": "^1.0.7", + "@auth/prisma-adapter": "^2.7.4", "@pothos/core": "^3.41.1", "@pothos/plugin-errors": "^3.11.1", "@pothos/plugin-prisma": "^3.65.2", @@ -52,7 +52,7 @@ "invariant": "^2.2.4", "lodash": "^4.17.21", "next": "^15.0.3", - "next-auth": "^4.24.7", + "next-auth": "5.0.0-beta.25", "nodemailer": "^6.9.13", "pako": "^2.1.0", "react": "^18.2.0", diff --git a/packages/hub/src/app/admin/layout.tsx b/packages/hub/src/app/admin/layout.tsx index f300da8ad0..c20592c791 100644 --- a/packages/hub/src/app/admin/layout.tsx +++ b/packages/hub/src/app/admin/layout.tsx @@ -2,14 +2,14 @@ import { PropsWithChildren } from "react"; import { LockIcon } from "@quri/ui"; +import { auth } from "@/auth"; import { FullLayoutWithPadding } from "@/components/layout/FullLayoutWithPadding"; import { NarrowPageLayout } from "@/components/layout/NarrowPageLayout"; import { H1 } from "@/components/ui/Headers"; import { isRootEmail } from "@/graphql/helpers/userHelpers"; -import { getServerSession } from "@/server/helpers"; export default async function AdminLayout({ children }: PropsWithChildren) { - const session = await getServerSession(); + const session = await auth(); const email = session?.user.email; diff --git a/packages/hub/src/app/ai/analytics/code-errors/page.tsx b/packages/hub/src/app/ai/analytics/code-errors/page.tsx index 3abdd1d0a4..8ffbe112f2 100644 --- a/packages/hub/src/app/ai/analytics/code-errors/page.tsx +++ b/packages/hub/src/app/ai/analytics/code-errors/page.tsx @@ -13,11 +13,7 @@ const SIGNATURE_ERROR = "Signature errors (total)"; const LONG_ERROR = "Long errors"; const NOT_DEFINED_ERROR = "Undefined variable"; -export default async function ({ - searchParams, -}: { - searchParams: { [key: string]: string | string[] | undefined }; -}) { +export default async function () { const errors = await getCodeErrors(); // pre-initialized to follow the right key order diff --git a/packages/hub/src/app/ai/api/create/route.ts b/packages/hub/src/app/ai/api/create/route.ts index e6f8dd6fc4..18f2a72bf2 100644 --- a/packages/hub/src/app/ai/api/create/route.ts +++ b/packages/hub/src/app/ai/api/create/route.ts @@ -7,11 +7,11 @@ import { Workflow, } from "@quri/squiggle-ai/server"; +import { auth } from "@/auth"; import { getSelf, isSignedIn } from "@/graphql/helpers/userHelpers"; import { prisma } from "@/prisma"; import { getAiCodec } from "@/server/ai/utils"; import { V2WorkflowData } from "@/server/ai/v2_0"; -import { getServerSession } from "@/server/helpers"; import { AiRequestBody, aiRequestBodySchema } from "../../utils"; @@ -122,7 +122,7 @@ function saveWorkflowToDbOnUpdates( } export async function POST(req: Request) { - const session = await getServerSession(); + const session = await auth(); if (!isSignedIn(session)) { return new Response("Unauthorized", { status: 401 }); diff --git a/packages/hub/src/app/ai/page.tsx b/packages/hub/src/app/ai/page.tsx index cb71e1cfb5..2f7ea6331a 100644 --- a/packages/hub/src/app/ai/page.tsx +++ b/packages/hub/src/app/ai/page.tsx @@ -8,13 +8,13 @@ import { AiDashboard } from "./AiDashboard"; export default async function SessionsPage({ searchParams, }: { - searchParams: { [key: string]: string | string[] | undefined }; + searchParams: Promise<{ [key: string]: string | string[] | undefined }>; }) { const { limit } = z .object({ limit: numberInString.optional(), }) - .parse(searchParams); + .parse(await searchParams); const { workflows, hasMore } = await loadWorkflows({ limit }); diff --git a/packages/hub/src/app/api/auth/[...nextauth]/route.ts b/packages/hub/src/app/api/auth/[...nextauth]/route.ts index f84923cd8d..a49631a69f 100644 --- a/packages/hub/src/app/api/auth/[...nextauth]/route.ts +++ b/packages/hub/src/app/api/auth/[...nextauth]/route.ts @@ -1,7 +1,3 @@ -import NextAuth from "next-auth"; +import { handlers } from "../../../../auth"; -import { authOptions } from "./authOptions"; - -const handler = NextAuth(authOptions); - -export { handler as GET, handler as POST }; +export const { GET, POST } = handlers; diff --git a/packages/hub/src/app/api/graphql/route.ts b/packages/hub/src/app/api/graphql/route.ts index 0497a20cf7..faad42fde6 100644 --- a/packages/hub/src/app/api/graphql/route.ts +++ b/packages/hub/src/app/api/graphql/route.ts @@ -1,8 +1,8 @@ import { createYoga } from "graphql-yoga"; import { NextRequest, NextResponse } from "next/server"; +import { auth } from "@/auth"; import { schema } from "@/graphql/schema"; -import { getServerSession } from "@/server/helpers"; const yoga = createYoga({ graphqlEndpoint: "/api/graphql", @@ -11,7 +11,7 @@ const yoga = createYoga({ // There's some magic involved here; // getServerSession() obtains request data through Next.js cookies() and headers() functions // See also: https://github.com/nextauthjs/next-auth/issues/7355 - const session = await getServerSession(); + const session = await auth(); return { session }; }, }); diff --git a/packages/hub/src/app/layout.tsx b/packages/hub/src/app/layout.tsx index 488ac0b3bb..aa416e4fab 100644 --- a/packages/hub/src/app/layout.tsx +++ b/packages/hub/src/app/layout.tsx @@ -6,14 +6,14 @@ import { Analytics } from "@vercel/analytics/react"; import { Metadata } from "next"; import { PropsWithChildren } from "react"; -import { getServerSession } from "@/server/helpers"; +import { auth } from "@/auth"; import { RootLayout } from "./RootLayout"; export default async function ServerRootLayout({ children, }: PropsWithChildren) { - const session = await getServerSession(); + const session = await auth(); return ( diff --git a/packages/hub/src/app/api/auth/[...nextauth]/authOptions.ts b/packages/hub/src/auth.ts similarity index 78% rename from packages/hub/src/app/api/auth/[...nextauth]/authOptions.ts rename to packages/hub/src/auth.ts index 6e9aed404b..13c85ae3e6 100644 --- a/packages/hub/src/app/api/auth/[...nextauth]/authOptions.ts +++ b/packages/hub/src/auth.ts @@ -1,5 +1,7 @@ -import { PrismaAdapter } from "@next-auth/prisma-adapter"; -import { AuthOptions } from "next-auth"; +import "server-only"; + +import { PrismaAdapter } from "@auth/prisma-adapter"; +import NextAuth, { NextAuthConfig } from "next-auth"; import EmailProvider from "next-auth/providers/email"; import GithubProvider from "next-auth/providers/github"; import { Provider } from "next-auth/providers/index"; @@ -7,7 +9,7 @@ import { Provider } from "next-auth/providers/index"; import { indexUserId } from "@/graphql/helpers/searchHelpers"; import { prisma } from "@/prisma"; -function buildAuthOptions() { +function buildAuthConfig(): NextAuthConfig { const providers: Provider[] = []; const { SENDGRID_KEY, EMAIL_FROM } = process.env; @@ -31,7 +33,7 @@ function buildAuthOptions() { ); } - const authOptions: AuthOptions = { + const config: NextAuthConfig = { adapter: PrismaAdapter(prisma), providers, callbacks: { @@ -50,12 +52,14 @@ function buildAuthOptions() { }, events: { async createUser({ user }) { - await indexUserId(user.id); + if (user.id) { + await indexUserId(user.id); + } }, }, }; - return authOptions; + return config; } -export const authOptions = buildAuthOptions(); +export const { auth, handlers, signIn, signOut } = NextAuth(buildAuthConfig()); diff --git a/packages/hub/src/server/helpers.ts b/packages/hub/src/server/helpers.ts index 35930df247..812ee52334 100644 --- a/packages/hub/src/server/helpers.ts +++ b/packages/hub/src/server/helpers.ts @@ -5,20 +5,15 @@ */ import "server-only"; -import { getServerSession as getNextAuthServerSession } from "next-auth"; import { redirect } from "next/navigation"; import { isRootEmail, isSignedIn } from "@/graphql/helpers/userHelpers"; import { prisma } from "@/prisma"; -import { authOptions } from "../app/api/auth/[...nextauth]/authOptions"; - -export async function getServerSession() { - return getNextAuthServerSession(authOptions); -} +import { auth } from "../auth"; export async function getSessionUserOrRedirect() { - const session = await getServerSession(); + const session = await auth(); if (!isSignedIn(session)) { redirect("/api/auth/signin"); // TODO - callbackUrl } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ace56783a1..4b7b87463c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -409,9 +409,9 @@ importers: packages/hub: dependencies: - '@next-auth/prisma-adapter': - specifier: ^1.0.7 - version: 1.0.7(@prisma/client@5.22.0(prisma@5.22.0))(next-auth@4.24.7(next@15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@auth/prisma-adapter': + specifier: ^2.7.4 + version: 2.7.4(@prisma/client@5.22.0(prisma@5.22.0))(nodemailer@6.9.13) '@pothos/core': specifier: ^3.41.1 version: 3.41.1(graphql@16.8.1) @@ -488,8 +488,8 @@ importers: specifier: ^15.0.3 version: 15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-auth: - specifier: ^4.24.7 - version: 4.24.7(next@15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: 5.0.0-beta.25 + version: 5.0.0-beta.25(next@15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react@18.3.1) nodemailer: specifier: ^6.9.13 version: 6.9.13 @@ -699,7 +699,7 @@ importers: version: 29.5.14 jest: specifier: ^29.7.0 - version: 29.7.0(babel-plugin-macros@3.1.0) + version: 29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) typescript: specifier: ^5.6.3 version: 5.6.3 @@ -1349,6 +1349,39 @@ packages: resolution: {integrity: sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA==} engines: {node: '>=14'} + '@auth/core@0.37.2': + resolution: {integrity: sha512-kUvzyvkcd6h1vpeMAojK2y7+PAV5H+0Cc9+ZlKYDFhDY31AlvsB+GW5vNO4qE3Y07KeQgvNO9U0QUx/fN62kBw==} + peerDependencies: + '@simplewebauthn/browser': ^9.0.1 + '@simplewebauthn/server': ^9.0.2 + nodemailer: ^6.8.0 + peerDependenciesMeta: + '@simplewebauthn/browser': + optional: true + '@simplewebauthn/server': + optional: true + nodemailer: + optional: true + + '@auth/core@0.37.4': + resolution: {integrity: sha512-HOXJwXWXQRhbBDHlMU0K/6FT1v+wjtzdKhsNg0ZN7/gne6XPsIrjZ4daMcFnbq0Z/vsAbYBinQhhua0d77v7qw==} + peerDependencies: + '@simplewebauthn/browser': ^9.0.1 + '@simplewebauthn/server': ^9.0.2 + nodemailer: ^6.8.0 + peerDependenciesMeta: + '@simplewebauthn/browser': + optional: true + '@simplewebauthn/server': + optional: true + nodemailer: + optional: true + + '@auth/prisma-adapter@2.7.4': + resolution: {integrity: sha512-3T/X94R9J1sxOLQtsD3ijIZ0JGHPXlZQxRr/8NpnZBJ3KGxun/mNsZ1MwMRhTxy0mmn9JWXk7u9+xCcVn0pu3A==} + peerDependencies: + '@prisma/client': '>=2.26.0 || >=3 || >=4 || >=5' + '@aw-web-design/x-default-browser@1.4.126': resolution: {integrity: sha512-Xk1sIhyNC/esHGGVjL/niHLowM0csl/kFO5uawBy4IrWwy0o1G8LGt3jP6nmWGz+USxeeqbihAmp/oVZju6wug==} hasBin: true @@ -4032,12 +4065,6 @@ packages: '@ndelangen/get-tarball@3.0.9': resolution: {integrity: sha512-9JKTEik4vq+yGosHYhZ1tiH/3WpUS0Nh0kej4Agndhox8pAdWhEx5knFVRcb/ya9knCRCs1rPxNrSXTDdfVqpA==} - '@next-auth/prisma-adapter@1.0.7': - resolution: {integrity: sha512-Cdko4KfcmKjsyHFrWwZ//lfLUbcLqlyFqjd/nYE2m3aZ7tjMNUjpks47iw7NTCnXf+5UWz5Ypyt1dSs1EP5QJw==} - peerDependencies: - '@prisma/client': '>=2.26.0 || >=3' - next-auth: ^4 - '@next/env@15.0.0': resolution: {integrity: sha512-Mcv8ZVmEgTO3bePiH/eJ7zHqQEs2gCqZ0UId2RxHmDDc7Pw6ngfSrOFlxG8XDpaex+n2G+TKPsQAf28MO+88Gw==} @@ -4234,8 +4261,8 @@ packages: resolution: {integrity: sha512-18hl0MiCLmumODHjrLzSdTb1Ny3Dh8tn44jwgx0LksCdvVAsr3jQvfr+hwrE7bVkap0wPELb/dnuJjvupKxheQ==} engines: {node: '>= 16.0.0'} - '@panva/hkdf@1.1.1': - resolution: {integrity: sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==} + '@panva/hkdf@1.2.1': + resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==} '@parcel/watcher-android-arm64@2.4.1': resolution: {integrity: sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==} @@ -5734,6 +5761,9 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/cross-spawn@6.0.6': resolution: {integrity: sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==} @@ -7133,14 +7163,14 @@ packages: cookie-signature@1.0.6: resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} - cookie@0.5.0: - resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} - engines: {node: '>= 0.6'} - cookie@0.6.0: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} + engines: {node: '>= 0.6'} + copy-to-clipboard@3.3.3: resolution: {integrity: sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==} @@ -9395,6 +9425,9 @@ packages: jose@4.15.5: resolution: {integrity: sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg==} + jose@5.9.6: + resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==} + js-cookie@2.2.1: resolution: {integrity: sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==} @@ -10241,14 +10274,19 @@ packages: neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - next-auth@4.24.7: - resolution: {integrity: sha512-iChjE8ov/1K/z98gdKbn2Jw+2vLgJtVV39X+rCP5SGnVQuco7QOr19FRNGMIrD8d3LYhHWV9j9sKLzq1aDWWQQ==} + next-auth@5.0.0-beta.25: + resolution: {integrity: sha512-2dJJw1sHQl2qxCrRk+KTQbeH+izFbGFPuJj5eGgBZFYyiYYtvlrBeUw1E/OJJxTRjuxbSYGnCTkUIRsIIW0bog==} peerDependencies: - next: ^12.2.5 || ^13 || ^14 + '@simplewebauthn/browser': ^9.0.1 + '@simplewebauthn/server': ^9.0.2 + next: ^14.0.0-0 || ^15.0.0-0 nodemailer: ^6.6.5 - react: ^17.0.2 || ^18 - react-dom: ^17.0.2 || ^18 + react: ^18.2.0 || ^19.0.0-0 peerDependenciesMeta: + '@simplewebauthn/browser': + optional: true + '@simplewebauthn/server': + optional: true nodemailer: optional: true @@ -10444,17 +10482,13 @@ packages: engines: {node: ^14.16.0 || >=16.10.0} hasBin: true - oauth@0.9.15: - resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==} + oauth4webapi@3.1.3: + resolution: {integrity: sha512-dik5wEMdFL5p3JlijYvM7wMNCgaPhblLIDCZtdXcaZp5wgu5Iwmsu7lMzgFhIDTi5d0BJo03LVoOoFQvXMeOeQ==} object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - object-hash@2.2.0: - resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==} - engines: {node: '>= 6'} - object-hash@3.0.0: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} @@ -10512,10 +10546,6 @@ packages: ohash@1.1.3: resolution: {integrity: sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==} - oidc-token-hash@5.0.3: - resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==} - engines: {node: ^10.13.0 || >=12.0.0} - on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -10559,9 +10589,6 @@ packages: zod: optional: true - openid-client@5.6.1: - resolution: {integrity: sha512-PtrWsY+dXg6y8mtMPyL/namZSYVz8pjXz3yJiBNZsEdCnu9miHLB4ELVC85WvneMKo2Rg62Ay7NkuCpM0bgiLQ==} - optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -10874,13 +10901,21 @@ packages: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} - preact-render-to-string@5.2.6: - resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==} + preact-render-to-string@5.2.3: + resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==} peerDependencies: preact: '>=10' - preact@10.18.1: - resolution: {integrity: sha512-mKUD7RRkQQM6s7Rkmi7IFkoEHjuFqRQUaXamO61E6Nn7vqF/bo7EZCmSyrUnp2UWHw0O7XjZ2eeXis+m7tf4lg==} + preact-render-to-string@6.5.11: + resolution: {integrity: sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==} + peerDependencies: + preact: '>=10' + + preact@10.11.3: + resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==} + + preact@10.24.3: + resolution: {integrity: sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==} prebuild-install@7.1.1: resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} @@ -13343,6 +13378,37 @@ snapshots: transitivePeerDependencies: - encoding + '@auth/core@0.37.2(nodemailer@6.9.13)': + dependencies: + '@panva/hkdf': 1.2.1 + '@types/cookie': 0.6.0 + cookie: 0.7.1 + jose: 5.9.6 + oauth4webapi: 3.1.3 + preact: 10.11.3 + preact-render-to-string: 5.2.3(preact@10.11.3) + optionalDependencies: + nodemailer: 6.9.13 + + '@auth/core@0.37.4(nodemailer@6.9.13)': + dependencies: + '@panva/hkdf': 1.2.1 + jose: 5.9.6 + oauth4webapi: 3.1.3 + preact: 10.24.3 + preact-render-to-string: 6.5.11(preact@10.24.3) + optionalDependencies: + nodemailer: 6.9.13 + + '@auth/prisma-adapter@2.7.4(@prisma/client@5.22.0(prisma@5.22.0))(nodemailer@6.9.13)': + dependencies: + '@auth/core': 0.37.4(nodemailer@6.9.13) + '@prisma/client': 5.22.0(prisma@5.22.0) + transitivePeerDependencies: + - '@simplewebauthn/browser' + - '@simplewebauthn/server' + - nodemailer + '@aw-web-design/x-default-browser@1.4.126': dependencies: default-browser-id: 3.0.0 @@ -16258,41 +16324,6 @@ snapshots: jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(babel-plugin-macros@3.1.0)': - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 22.9.0 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.8.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.5 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.6.3))': dependencies: '@jest/console': 29.7.0 @@ -16641,11 +16672,6 @@ snapshots: pump: 3.0.0 tar-fs: 2.1.1 - '@next-auth/prisma-adapter@1.0.7(@prisma/client@5.22.0(prisma@5.22.0))(next-auth@4.24.7(next@15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': - dependencies: - '@prisma/client': 5.22.0(prisma@5.22.0) - next-auth: 4.24.7(next@15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@next/env@15.0.0': optional: true @@ -16795,7 +16821,7 @@ snapshots: '@orama/orama@3.0.1': {} - '@panva/hkdf@1.1.1': {} + '@panva/hkdf@1.2.1': {} '@parcel/watcher-android-arm64@2.4.1': optional: true @@ -19274,6 +19300,8 @@ snapshots: dependencies: '@types/node': 22.9.0 + '@types/cookie@0.6.0': {} + '@types/cross-spawn@6.0.6': dependencies: '@types/node': 22.9.0 @@ -21003,10 +21031,10 @@ snapshots: cookie-signature@1.0.6: {} - cookie@0.5.0: {} - cookie@0.6.0: {} + cookie@0.7.1: {} + copy-to-clipboard@3.3.3: dependencies: toggle-selection: 1.0.6 @@ -21105,21 +21133,6 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(babel-plugin-macros@3.1.0): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.12.7)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.6.3)) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - create-require@1.1.1: {} crelt@1.0.5: {} @@ -22045,7 +22058,7 @@ snapshots: eslint: 8.57.0 eslint-import-resolver-node: 0.3.7 eslint-import-resolver-typescript: 3.5.3(eslint-plugin-import@2.28.1(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.5.3(eslint-plugin-import@2.28.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.5.3)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.7.1(eslint@8.57.0) eslint-plugin-react: 7.33.2(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) @@ -22068,7 +22081,7 @@ snapshots: debug: 4.3.6 enhanced-resolve: 5.15.0 eslint: 8.57.0 - eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.5.3(eslint-plugin-import@2.28.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.5.3)(eslint@8.57.0) get-tsconfig: 4.7.5 globby: 13.1.3 is-core-module: 2.13.1 @@ -22088,7 +22101,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.5.3(eslint-plugin-import@2.28.1(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.20.0(eslint@8.57.0)(typescript@5.6.3))(eslint-import-resolver-typescript@3.5.3)(eslint@8.57.0): dependencies: array-includes: 3.1.7 array.prototype.findlastindex: 1.2.3 @@ -23824,25 +23837,6 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(babel-plugin-macros@3.1.0): - dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(babel-plugin-macros@3.1.0) - exit: 0.1.2 - import-local: 3.1.0 - jest-config: 29.7.0(@types/node@20.12.7)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.6.3)) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - jest-config@29.7.0(@types/node@20.12.7)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.6.3)): dependencies: '@babel/core': 7.26.0 @@ -24190,24 +24184,14 @@ snapshots: - supports-color - ts-node - jest@29.7.0(babel-plugin-macros@3.1.0): - dependencies: - '@jest/core': 29.7.0(babel-plugin-macros@3.1.0) - '@jest/types': 29.6.3 - import-local: 3.1.0 - jest-cli: 29.7.0(babel-plugin-macros@3.1.0) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - jiti@1.20.0: {} jiti@1.21.0: {} jose@4.15.5: {} + jose@5.9.6: {} + js-cookie@2.2.1: {} js-tokens@4.0.0: {} @@ -25544,20 +25528,11 @@ snapshots: neo-async@2.6.2: {} - next-auth@4.24.7(next@15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next-auth@5.0.0-beta.25(next@15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nodemailer@6.9.13)(react@18.3.1): dependencies: - '@babel/runtime': 7.23.9 - '@panva/hkdf': 1.1.1 - cookie: 0.5.0 - jose: 4.15.5 + '@auth/core': 0.37.2(nodemailer@6.9.13) next: 15.0.3(@babel/core@7.26.0)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - oauth: 0.9.15 - openid-client: 5.6.1 - preact: 10.18.1 - preact-render-to-string: 5.2.6(preact@10.18.1) react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - uuid: 8.3.2 optionalDependencies: nodemailer: 6.9.13 @@ -25766,12 +25741,10 @@ snapshots: pathe: 1.1.2 ufo: 1.3.2 - oauth@0.9.15: {} + oauth4webapi@3.1.3: {} object-assign@4.1.1: {} - object-hash@2.2.0: {} - object-hash@3.0.0: {} object-inspect@1.12.3: {} @@ -25839,8 +25812,6 @@ snapshots: ohash@1.1.3: {} - oidc-token-hash@5.0.3: {} - on-finished@2.4.1: dependencies: ee-first: 1.1.1 @@ -25897,13 +25868,6 @@ snapshots: transitivePeerDependencies: - encoding - openid-client@5.6.1: - dependencies: - jose: 4.15.5 - lru-cache: 6.0.0 - object-hash: 2.2.0 - oidc-token-hash: 5.0.3 - optionator@0.9.3: dependencies: '@aashutoshrathi/word-wrap': 1.2.6 @@ -26232,12 +26196,18 @@ snapshots: picocolors: 1.0.0 source-map-js: 1.2.0 - preact-render-to-string@5.2.6(preact@10.18.1): + preact-render-to-string@5.2.3(preact@10.11.3): dependencies: - preact: 10.18.1 + preact: 10.11.3 pretty-format: 3.8.0 - preact@10.18.1: {} + preact-render-to-string@6.5.11(preact@10.24.3): + dependencies: + preact: 10.24.3 + + preact@10.11.3: {} + + preact@10.24.3: {} prebuild-install@7.1.1: dependencies: From 61fe0dde2d488f9abe97b034671025e458ee6614 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Sun, 24 Nov 2024 21:08:02 -0300 Subject: [PATCH 14/17] esbuild for worker --- packages/squiggle-lang/package.json | 4 +- .../src/runners/WebWorkerRunner.ts | 20 +- pnpm-lock.yaml | 326 +++++------------- 3 files changed, 106 insertions(+), 244 deletions(-) diff --git a/packages/squiggle-lang/package.json b/packages/squiggle-lang/package.json index 8de47ee071..92a3b6a282 100644 --- a/packages/squiggle-lang/package.json +++ b/packages/squiggle-lang/package.json @@ -10,9 +10,10 @@ "directory": "packages/squiggle-lang" }, "scripts": { - "build": "pnpm run build:peggy && pnpm run build:ts", + "build": "pnpm run build:peggy && pnpm run build:ts && pnpm run build:worker", "build:peggy": "peggy --cache --format es ./src/ast/peggyParser.peggy", "build:ts": "tsc -b", + "build:worker": "esbuild src/runners/worker.ts --bundle --minify --platform=browser --outfile=dist/runners/esbuild-worker.cjs", "dev": "tsc -b -w", "clean": "rm -rf dist && rm -f src/ast/peggyParser.js && rm *.tsbuildinfo", "jest": "NODE_OPTIONS=--experimental-vm-modules jest", @@ -59,6 +60,7 @@ "@typescript-eslint/parser": "^7.11.0", "codecov": "^3.8.3", "date-fns": "^4.1.0", + "esbuild": "^0.24.0", "eslint": "^8.57.1", "fast-check": "^3.19.0", "jest": "^29.7.0", diff --git a/packages/squiggle-lang/src/runners/WebWorkerRunner.ts b/packages/squiggle-lang/src/runners/WebWorkerRunner.ts index 3774c5c4a7..e7d15421b3 100644 --- a/packages/squiggle-lang/src/runners/WebWorkerRunner.ts +++ b/packages/squiggle-lang/src/runners/WebWorkerRunner.ts @@ -9,9 +9,23 @@ export class WebWorkerRunner extends AnyWorkerRunner { throw new Error("WebWorkerRunner is only available in browser"); } super(); - this.worker = new Worker(new URL("./worker.js", import.meta.url), { - type: "module", - }); + + /** + * If you need to touch this code, make sure to check carefully both the components (storybook) and the hub editor. + * This configuration and the esbuild configuration in `package.json` are extremely fragile. + * + * In particular: + * 1. ESM is problematic:`{ type: "module" }` (with `.mjs` or `.js`)will seemingly work, but cause lots of Turbopack CLI errors; Turbopack fails to analyze `(url, { type: "module" })` parameters correctly, as of Next.js 15.0.3. + * 2. `.js` extension will collide with the default `"type": "module"` in `package.json`, which will lead to Next.js warnings. + * + * So, instead, we bundle the worker to the basic IIFE; see `package.json` for details. + * + * This might cause issues in the future, if we decide to depend on some library that's ESM-only; I'm not sure how good esbuild is for bundling ESM dependencies. + * + * But, for now, it seems like it's working. + */ + const url = new URL("./esbuild-worker.cjs", import.meta.url); + this.worker = new Worker(url); } async getWorker(): Promise { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4b7b87463c..1275e5f8e2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -699,7 +699,7 @@ importers: version: 29.5.14 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + version: 29.7.0(babel-plugin-macros@3.1.0) typescript: specifier: ^5.6.3 version: 5.6.3 @@ -830,6 +830,9 @@ importers: date-fns: specifier: ^4.1.0 version: 4.1.0 + esbuild: + specifier: ^0.24.0 + version: 0.24.0 eslint: specifier: ^8.57.1 version: 8.57.1 @@ -2768,12 +2771,6 @@ packages: peerDependencies: esbuild: '*' - '@esbuild/aix-ppc64@0.20.1': - resolution: {integrity: sha512-m55cpeupQ2DbuRGQMMZDzbv9J9PgVelPjlcmM5kxHnrBdBx6REaEd7LamYV7Dm8N7rCyR/XwU6rVP8ploKtIkA==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.20.2': resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} engines: {node: '>=12'} @@ -2798,12 +2795,6 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.20.1': - resolution: {integrity: sha512-hCnXNF0HM6AjowP+Zou0ZJMWWa1VkD77BXe959zERgGJBBxB+sV+J9f/rcjeg2c5bsukD/n17RKWXGFCO5dD5A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.20.2': resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} engines: {node: '>=12'} @@ -2828,12 +2819,6 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm@0.20.1': - resolution: {integrity: sha512-4j0+G27/2ZXGWR5okcJi7pQYhmkVgb4D7UKwxcqrjhvp5TKWx3cUjgB1CGj1mfdmJBQ9VnUGgUhign+FPF2Zgw==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.20.2': resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} engines: {node: '>=12'} @@ -2858,12 +2843,6 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-x64@0.20.1': - resolution: {integrity: sha512-MSfZMBoAsnhpS+2yMFYIQUPs8Z19ajwfuaSZx+tSl09xrHZCjbeXXMsUF/0oq7ojxYEpsSo4c0SfjxOYXRbpaA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.20.2': resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} engines: {node: '>=12'} @@ -2888,12 +2867,6 @@ packages: cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.20.1': - resolution: {integrity: sha512-Ylk6rzgMD8klUklGPzS414UQLa5NPXZD5tf8JmQU8GQrj6BrFA/Ic9tb2zRe1kOZyCbGl+e8VMbDRazCEBqPvA==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.20.2': resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} engines: {node: '>=12'} @@ -2918,12 +2891,6 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.20.1': - resolution: {integrity: sha512-pFIfj7U2w5sMp52wTY1XVOdoxw+GDwy9FsK3OFz4BpMAjvZVs0dT1VXs8aQm22nhwoIWUmIRaE+4xow8xfIDZA==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.20.2': resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} engines: {node: '>=12'} @@ -2948,12 +2915,6 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.20.1': - resolution: {integrity: sha512-UyW1WZvHDuM4xDz0jWun4qtQFauNdXjXOtIy7SYdf7pbxSWWVlqhnR/T2TpX6LX5NI62spt0a3ldIIEkPM6RHw==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.20.2': resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} engines: {node: '>=12'} @@ -2978,12 +2939,6 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.20.1': - resolution: {integrity: sha512-itPwCw5C+Jh/c624vcDd9kRCCZVpzpQn8dtwoYIt2TJF3S9xJLiRohnnNrKwREvcZYx0n8sCSbvGH349XkcQeg==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.20.2': resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} engines: {node: '>=12'} @@ -3008,12 +2963,6 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.20.1': - resolution: {integrity: sha512-cX8WdlF6Cnvw/DO9/X7XLH2J6CkBnz7Twjpk56cshk9sjYVcuh4sXQBy5bmTwzBjNVZze2yaV1vtcJS04LbN8w==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.20.2': resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} engines: {node: '>=12'} @@ -3038,12 +2987,6 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.20.1': - resolution: {integrity: sha512-LojC28v3+IhIbfQ+Vu4Ut5n3wKcgTu6POKIHN9Wpt0HnfgUGlBuyDDQR4jWZUZFyYLiz4RBBBmfU6sNfn6RhLw==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.20.2': resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} engines: {node: '>=12'} @@ -3068,12 +3011,6 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.20.1': - resolution: {integrity: sha512-4H/sQCy1mnnGkUt/xszaLlYJVTz3W9ep52xEefGtd6yXDQbz/5fZE5dFLUgsPdbUOQANcVUa5iO6g3nyy5BJiw==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.20.2': resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} engines: {node: '>=12'} @@ -3098,12 +3035,6 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.20.1': - resolution: {integrity: sha512-c0jgtB+sRHCciVXlyjDcWb2FUuzlGVRwGXgI+3WqKOIuoo8AmZAddzeOHeYLtD+dmtHw3B4Xo9wAUdjlfW5yYA==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.20.2': resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} engines: {node: '>=12'} @@ -3128,12 +3059,6 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.20.1': - resolution: {integrity: sha512-TgFyCfIxSujyuqdZKDZ3yTwWiGv+KnlOeXXitCQ+trDODJ+ZtGOzLkSWngynP0HZnTsDyBbPy7GWVXWaEl6lhA==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.20.2': resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} engines: {node: '>=12'} @@ -3158,12 +3083,6 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.20.1': - resolution: {integrity: sha512-b+yuD1IUeL+Y93PmFZDZFIElwbmFfIKLKlYI8M6tRyzE6u7oEP7onGk0vZRh8wfVGC2dZoy0EqX1V8qok4qHaw==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.20.2': resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} engines: {node: '>=12'} @@ -3188,12 +3107,6 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.20.1': - resolution: {integrity: sha512-wpDlpE0oRKZwX+GfomcALcouqjjV8MIX8DyTrxfyCfXxoKQSDm45CZr9fanJ4F6ckD4yDEPT98SrjvLwIqUCgg==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.20.2': resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} engines: {node: '>=12'} @@ -3218,12 +3131,6 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.20.1': - resolution: {integrity: sha512-5BepC2Au80EohQ2dBpyTquqGCES7++p7G+7lXe1bAIvMdXm4YYcEfZtQrP4gaoZ96Wv1Ute61CEHFU7h4FMueQ==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.20.2': resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} engines: {node: '>=12'} @@ -3248,12 +3155,6 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.20.1': - resolution: {integrity: sha512-5gRPk7pKuaIB+tmH+yKd2aQTRpqlf1E4f/mC+tawIm/CGJemZcHZpp2ic8oD83nKgUPMEd0fNanrnFljiruuyA==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.20.2': resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} engines: {node: '>=12'} @@ -3278,12 +3179,6 @@ packages: cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.20.1': - resolution: {integrity: sha512-4fL68JdrLV2nVW2AaWZBv3XEm3Ae3NZn/7qy2KGAt3dexAgSVT+Hc97JKSZnqezgMlv9x6KV0ZkZY7UO5cNLCg==} - engines: {node: '>=12'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.20.2': resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} engines: {node: '>=12'} @@ -3320,12 +3215,6 @@ packages: cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.20.1': - resolution: {integrity: sha512-GhRuXlvRE+twf2ES+8REbeCb/zeikNqwD3+6S5y5/x+DYbAQUNl0HNBs4RQJqrechS4v4MruEr8ZtAin/hK5iw==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.20.2': resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} engines: {node: '>=12'} @@ -3350,12 +3239,6 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.20.1': - resolution: {integrity: sha512-ZnWEyCM0G1Ex6JtsygvC3KUUrlDXqOihw8RicRuQAzw+c4f1D66YlPNNV3rkjVW90zXVsHwZYWbJh3v+oQFM9Q==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.20.2': resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} engines: {node: '>=12'} @@ -3380,12 +3263,6 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.20.1': - resolution: {integrity: sha512-QZ6gXue0vVQY2Oon9WyLFCdSuYbXSoxaZrPuJ4c20j6ICedfsDilNPYfHLlMH7vGfU5DQR0czHLmJvH4Nzis/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.20.2': resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} engines: {node: '>=12'} @@ -3410,12 +3287,6 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.20.1': - resolution: {integrity: sha512-HzcJa1NcSWTAU0MJIxOho8JftNp9YALui3o+Ny7hCh0v5f90nprly1U3Sj1Ldj/CvKKdvvFsCRvDkpsEMp4DNw==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.20.2': resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} engines: {node: '>=12'} @@ -3440,12 +3311,6 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.20.1': - resolution: {integrity: sha512-0MBh53o6XtI6ctDnRMeQ+xoCN8kD2qI1rY1KgF/xdWQwoFeKou7puvDfV8/Wv4Ctx2rRpET/gGdz3YlNtNACSA==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.20.2': resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} engines: {node: '>=12'} @@ -7888,11 +7753,6 @@ packages: peerDependencies: esbuild: '>=0.12 <1' - esbuild@0.20.1: - resolution: {integrity: sha512-OJwEgrpWm/PCMsLVWXKqvcjme3bHNpOgN7Tb6cQnR5n0TPbQx1/Xrn7rqM+wn17bYeT6MGB5sn1Bh5YiGi70nA==} - engines: {node: '>=12'} - hasBin: true - esbuild@0.20.2: resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} engines: {node: '>=12'} @@ -15299,9 +15159,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@esbuild/aix-ppc64@0.20.1': - optional: true - '@esbuild/aix-ppc64@0.20.2': optional: true @@ -15314,9 +15171,6 @@ snapshots: '@esbuild/aix-ppc64@0.24.0': optional: true - '@esbuild/android-arm64@0.20.1': - optional: true - '@esbuild/android-arm64@0.20.2': optional: true @@ -15329,9 +15183,6 @@ snapshots: '@esbuild/android-arm64@0.24.0': optional: true - '@esbuild/android-arm@0.20.1': - optional: true - '@esbuild/android-arm@0.20.2': optional: true @@ -15344,9 +15195,6 @@ snapshots: '@esbuild/android-arm@0.24.0': optional: true - '@esbuild/android-x64@0.20.1': - optional: true - '@esbuild/android-x64@0.20.2': optional: true @@ -15359,9 +15207,6 @@ snapshots: '@esbuild/android-x64@0.24.0': optional: true - '@esbuild/darwin-arm64@0.20.1': - optional: true - '@esbuild/darwin-arm64@0.20.2': optional: true @@ -15374,9 +15219,6 @@ snapshots: '@esbuild/darwin-arm64@0.24.0': optional: true - '@esbuild/darwin-x64@0.20.1': - optional: true - '@esbuild/darwin-x64@0.20.2': optional: true @@ -15389,9 +15231,6 @@ snapshots: '@esbuild/darwin-x64@0.24.0': optional: true - '@esbuild/freebsd-arm64@0.20.1': - optional: true - '@esbuild/freebsd-arm64@0.20.2': optional: true @@ -15404,9 +15243,6 @@ snapshots: '@esbuild/freebsd-arm64@0.24.0': optional: true - '@esbuild/freebsd-x64@0.20.1': - optional: true - '@esbuild/freebsd-x64@0.20.2': optional: true @@ -15419,9 +15255,6 @@ snapshots: '@esbuild/freebsd-x64@0.24.0': optional: true - '@esbuild/linux-arm64@0.20.1': - optional: true - '@esbuild/linux-arm64@0.20.2': optional: true @@ -15434,9 +15267,6 @@ snapshots: '@esbuild/linux-arm64@0.24.0': optional: true - '@esbuild/linux-arm@0.20.1': - optional: true - '@esbuild/linux-arm@0.20.2': optional: true @@ -15449,9 +15279,6 @@ snapshots: '@esbuild/linux-arm@0.24.0': optional: true - '@esbuild/linux-ia32@0.20.1': - optional: true - '@esbuild/linux-ia32@0.20.2': optional: true @@ -15464,9 +15291,6 @@ snapshots: '@esbuild/linux-ia32@0.24.0': optional: true - '@esbuild/linux-loong64@0.20.1': - optional: true - '@esbuild/linux-loong64@0.20.2': optional: true @@ -15479,9 +15303,6 @@ snapshots: '@esbuild/linux-loong64@0.24.0': optional: true - '@esbuild/linux-mips64el@0.20.1': - optional: true - '@esbuild/linux-mips64el@0.20.2': optional: true @@ -15494,9 +15315,6 @@ snapshots: '@esbuild/linux-mips64el@0.24.0': optional: true - '@esbuild/linux-ppc64@0.20.1': - optional: true - '@esbuild/linux-ppc64@0.20.2': optional: true @@ -15509,9 +15327,6 @@ snapshots: '@esbuild/linux-ppc64@0.24.0': optional: true - '@esbuild/linux-riscv64@0.20.1': - optional: true - '@esbuild/linux-riscv64@0.20.2': optional: true @@ -15524,9 +15339,6 @@ snapshots: '@esbuild/linux-riscv64@0.24.0': optional: true - '@esbuild/linux-s390x@0.20.1': - optional: true - '@esbuild/linux-s390x@0.20.2': optional: true @@ -15539,9 +15351,6 @@ snapshots: '@esbuild/linux-s390x@0.24.0': optional: true - '@esbuild/linux-x64@0.20.1': - optional: true - '@esbuild/linux-x64@0.20.2': optional: true @@ -15554,9 +15363,6 @@ snapshots: '@esbuild/linux-x64@0.24.0': optional: true - '@esbuild/netbsd-x64@0.20.1': - optional: true - '@esbuild/netbsd-x64@0.20.2': optional: true @@ -15575,9 +15381,6 @@ snapshots: '@esbuild/openbsd-arm64@0.24.0': optional: true - '@esbuild/openbsd-x64@0.20.1': - optional: true - '@esbuild/openbsd-x64@0.20.2': optional: true @@ -15590,9 +15393,6 @@ snapshots: '@esbuild/openbsd-x64@0.24.0': optional: true - '@esbuild/sunos-x64@0.20.1': - optional: true - '@esbuild/sunos-x64@0.20.2': optional: true @@ -15605,9 +15405,6 @@ snapshots: '@esbuild/sunos-x64@0.24.0': optional: true - '@esbuild/win32-arm64@0.20.1': - optional: true - '@esbuild/win32-arm64@0.20.2': optional: true @@ -15620,9 +15417,6 @@ snapshots: '@esbuild/win32-arm64@0.24.0': optional: true - '@esbuild/win32-ia32@0.20.1': - optional: true - '@esbuild/win32-ia32@0.20.2': optional: true @@ -15635,9 +15429,6 @@ snapshots: '@esbuild/win32-ia32@0.24.0': optional: true - '@esbuild/win32-x64@0.20.1': - optional: true - '@esbuild/win32-x64@0.20.2': optional: true @@ -16324,6 +16115,41 @@ snapshots: jest-util: 29.7.0 slash: 3.0.0 + '@jest/core@29.7.0(babel-plugin-macros@3.1.0)': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 22.9.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.8.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.5 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + '@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.6.3))': dependencies: '@jest/console': 29.7.0 @@ -21133,6 +20959,21 @@ snapshots: - supports-color - ts-node + create-jest@29.7.0(babel-plugin-macros@3.1.0): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + create-require@1.1.1: {} crelt@1.0.5: {} @@ -21896,32 +21737,6 @@ snapshots: transitivePeerDependencies: - supports-color - esbuild@0.20.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.20.1 - '@esbuild/android-arm': 0.20.1 - '@esbuild/android-arm64': 0.20.1 - '@esbuild/android-x64': 0.20.1 - '@esbuild/darwin-arm64': 0.20.1 - '@esbuild/darwin-x64': 0.20.1 - '@esbuild/freebsd-arm64': 0.20.1 - '@esbuild/freebsd-x64': 0.20.1 - '@esbuild/linux-arm': 0.20.1 - '@esbuild/linux-arm64': 0.20.1 - '@esbuild/linux-ia32': 0.20.1 - '@esbuild/linux-loong64': 0.20.1 - '@esbuild/linux-mips64el': 0.20.1 - '@esbuild/linux-ppc64': 0.20.1 - '@esbuild/linux-riscv64': 0.20.1 - '@esbuild/linux-s390x': 0.20.1 - '@esbuild/linux-x64': 0.20.1 - '@esbuild/netbsd-x64': 0.20.1 - '@esbuild/openbsd-x64': 0.20.1 - '@esbuild/sunos-x64': 0.20.1 - '@esbuild/win32-arm64': 0.20.1 - '@esbuild/win32-ia32': 0.20.1 - '@esbuild/win32-x64': 0.20.1 - esbuild@0.20.2: optionalDependencies: '@esbuild/aix-ppc64': 0.20.2 @@ -23837,6 +23652,25 @@ snapshots: - supports-color - ts-node + jest-cli@29.7.0(babel-plugin-macros@3.1.0): + dependencies: + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(babel-plugin-macros@3.1.0) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@22.9.0)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.9.0)(typescript@5.6.3)) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest-config@29.7.0(@types/node@20.12.7)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.12.7)(typescript@5.6.3)): dependencies: '@babel/core': 7.26.0 @@ -24184,6 +24018,18 @@ snapshots: - supports-color - ts-node + jest@29.7.0(babel-plugin-macros@3.1.0): + dependencies: + '@jest/core': 29.7.0(babel-plugin-macros@3.1.0) + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(babel-plugin-macros@3.1.0) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jiti@1.20.0: {} jiti@1.21.0: {} @@ -28528,7 +28374,7 @@ snapshots: vite@5.2.10(@types/node@20.12.7): dependencies: - esbuild: 0.20.1 + esbuild: 0.20.2 postcss: 8.4.38 rollup: 4.17.2 optionalDependencies: From 88bd7358fdb99f7afc5c7f9cff0ac710ff44446f Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Sun, 24 Nov 2024 21:22:46 -0300 Subject: [PATCH 15/17] async params and searchParams --- .../app/groups/[slug]/invite-link/page.tsx | 5 ++-- packages/hub/src/app/groups/[slug]/layout.tsx | 10 ++++--- .../src/app/groups/[slug]/members/page.tsx | 5 ++-- packages/hub/src/app/groups/[slug]/page.tsx | 5 ++-- .../src/app/models/[owner]/[slug]/layout.tsx | 26 +++++++++---------- .../src/app/models/[owner]/[slug]/page.tsx | 8 +++--- .../relative-values/[variableName]/layout.tsx | 11 ++++---- .../[slug]/revisions/[revisionId]/page.tsx | 7 ++--- .../models/[owner]/[slug]/revisions/page.tsx | 5 ++-- .../[slug]/variables/[variableName]/page.tsx | 11 ++++---- .../app/models/[owner]/[slug]/view/page.tsx | 5 ++-- .../[owner]/[slug]/edit/page.tsx | 5 ++-- .../relative-values/[owner]/[slug]/layout.tsx | 10 ++++--- .../relative-values/[owner]/[slug]/page.tsx | 5 ++-- .../app/users/[username]/definitions/page.tsx | 10 ++++--- .../src/app/users/[username]/groups/page.tsx | 10 ++++--- .../hub/src/app/users/[username]/layout.tsx | 10 ++++--- .../hub/src/app/users/[username]/page.tsx | 10 ++++--- .../app/users/[username]/variables/page.tsx | 10 ++++--- 19 files changed, 94 insertions(+), 74 deletions(-) diff --git a/packages/hub/src/app/groups/[slug]/invite-link/page.tsx b/packages/hub/src/app/groups/[slug]/invite-link/page.tsx index ffc5cb7975..dcb4ac3a3e 100644 --- a/packages/hub/src/app/groups/[slug]/invite-link/page.tsx +++ b/packages/hub/src/app/groups/[slug]/invite-link/page.tsx @@ -7,12 +7,13 @@ import QueryNode, { } from "@/__generated__/AcceptGroupInvitePageQuery.graphql"; type Props = { - params: { slug: string }; + params: Promise<{ slug: string }>; }; export default async function OuterAcceptGroupInvitePage({ params }: Props) { + const { slug } = await params; const query = await loadPageQuery(QueryNode, { - slug: params.slug, + slug, }); return ; diff --git a/packages/hub/src/app/groups/[slug]/layout.tsx b/packages/hub/src/app/groups/[slug]/layout.tsx index fd487c41f4..a08fef4a4f 100644 --- a/packages/hub/src/app/groups/[slug]/layout.tsx +++ b/packages/hub/src/app/groups/[slug]/layout.tsx @@ -11,12 +11,13 @@ import QueryNode, { } from "@/__generated__/GroupLayoutQuery.graphql"; type Props = PropsWithChildren<{ - params: { slug: string }; + params: Promise<{ slug: string }>; }>; export default async function OuterGroupLayout({ params, children }: Props) { + const { slug } = await params; const query = await loadPageQuery(QueryNode, { - slug: params.slug, + slug, }); return ( @@ -26,6 +27,7 @@ export default async function OuterGroupLayout({ params, children }: Props) { ); } -export function generateMetadata({ params }: Props): Metadata { - return { title: params.slug }; +export async function generateMetadata({ params }: Props): Promise { + const { slug } = await params; + return { title: slug }; } diff --git a/packages/hub/src/app/groups/[slug]/members/page.tsx b/packages/hub/src/app/groups/[slug]/members/page.tsx index d01ba731b0..cb180889c2 100644 --- a/packages/hub/src/app/groups/[slug]/members/page.tsx +++ b/packages/hub/src/app/groups/[slug]/members/page.tsx @@ -7,12 +7,13 @@ import QueryNode, { } from "@/__generated__/GroupMembersPageQuery.graphql"; type Props = { - params: { slug: string }; + params: Promise<{ slug: string }>; }; export default async function OuterGroupMembersPage({ params }: Props) { + const { slug } = await params; const query = await loadPageQuery(QueryNode, { - slug: params.slug, + slug, }); return ; diff --git a/packages/hub/src/app/groups/[slug]/page.tsx b/packages/hub/src/app/groups/[slug]/page.tsx index eb8bd8d640..abfe56f6c1 100644 --- a/packages/hub/src/app/groups/[slug]/page.tsx +++ b/packages/hub/src/app/groups/[slug]/page.tsx @@ -7,12 +7,13 @@ import QueryNode, { } from "@/__generated__/GroupPageQuery.graphql"; type Props = { - params: { slug: string }; + params: Promise<{ slug: string }>; }; export default async function OuterGroupPage({ params }: Props) { + const { slug } = await params; const query = await loadPageQuery(QueryNode, { - slug: params.slug, + slug, }); return ; diff --git a/packages/hub/src/app/models/[owner]/[slug]/layout.tsx b/packages/hub/src/app/models/[owner]/[slug]/layout.tsx index f22bbe1213..d327cf1680 100644 --- a/packages/hub/src/app/models/[owner]/[slug]/layout.tsx +++ b/packages/hub/src/app/models/[owner]/[slug]/layout.tsx @@ -1,5 +1,5 @@ import { Metadata } from "next"; -import { ReactNode, Suspense } from "react"; +import { PropsWithChildren, Suspense } from "react"; import { loadPageQuery } from "@/relay/loadPageQuery"; @@ -10,31 +10,29 @@ import ModelLayoutQueryNode, { ModelLayoutQuery, } from "@/__generated__/ModelLayoutQuery.graphql"; -type Props = { - params: { owner: string; slug: string }; - children: ReactNode; -}; +type Props = PropsWithChildren<{ + params: Promise<{ owner: string; slug: string }>; +}>; async function LoadedLayout({ params, children }: Props) { + const { owner, slug } = await params; const query = await loadPageQuery(ModelLayoutQueryNode, { - input: { owner: params.owner, slug: params.slug }, + input: { owner, slug }, }); return {children}; } -export default function Layout({ params, children }: Props) { +export default async function Layout({ params, children }: Props) { + const { owner, slug } = await params; return ( - - } - > + }> {children} ); } -export function generateMetadata({ params }: Props): Metadata { - return { title: `${params.owner}/${params.slug}` }; +export async function generateMetadata({ params }: Props): Promise { + const { owner, slug } = await params; + return { title: `${owner}/${slug}` }; } diff --git a/packages/hub/src/app/models/[owner]/[slug]/page.tsx b/packages/hub/src/app/models/[owner]/[slug]/page.tsx index 3deb9b0643..a72ec311e0 100644 --- a/packages/hub/src/app/models/[owner]/[slug]/page.tsx +++ b/packages/hub/src/app/models/[owner]/[slug]/page.tsx @@ -7,15 +7,13 @@ import QueryNode, { } from "@/__generated__/EditModelPageQuery.graphql"; type Props = { - params: { owner: string; slug: string }; + params: Promise<{ owner: string; slug: string }>; }; export default async function Page({ params }: Props) { + const { owner, slug } = await params; const query = await loadPageQuery(QueryNode, { - input: { - owner: params.owner, - slug: params.slug, - }, + input: { owner, slug }, }); return ( diff --git a/packages/hub/src/app/models/[owner]/[slug]/relative-values/[variableName]/layout.tsx b/packages/hub/src/app/models/[owner]/[slug]/relative-values/[variableName]/layout.tsx index a16e278fab..9a888646db 100644 --- a/packages/hub/src/app/models/[owner]/[slug]/relative-values/[variableName]/layout.tsx +++ b/packages/hub/src/app/models/[owner]/[slug]/relative-values/[variableName]/layout.tsx @@ -12,20 +12,21 @@ export default async function Layout({ params, children, }: PropsWithChildren<{ - params: { owner: string; slug: string; variableName: string }; + params: Promise<{ owner: string; slug: string; variableName: string }>; }>) { + const { owner, slug, variableName } = await params; const query = await loadPageQuery(QueryNode, { input: { - owner: params.owner, - slug: params.slug, + owner, + slug, }, forRelativeValues: { - variableName: params.variableName, + variableName, }, }); return ( - + {children} ); diff --git a/packages/hub/src/app/models/[owner]/[slug]/revisions/[revisionId]/page.tsx b/packages/hub/src/app/models/[owner]/[slug]/revisions/[revisionId]/page.tsx index 6b1ba6c703..439962781b 100644 --- a/packages/hub/src/app/models/[owner]/[slug]/revisions/[revisionId]/page.tsx +++ b/packages/hub/src/app/models/[owner]/[slug]/revisions/[revisionId]/page.tsx @@ -9,11 +9,12 @@ import QueryNode, { export default async function ModelPage({ params, }: { - params: { owner: string; slug: string; revisionId: string }; + params: Promise<{ owner: string; slug: string; revisionId: string }>; }) { + const { owner, slug, revisionId } = await params; const query = await loadPageQuery(QueryNode, { - input: { owner: params.owner, slug: params.slug }, - revisionId: params.revisionId, + input: { owner, slug }, + revisionId, }); return ; diff --git a/packages/hub/src/app/models/[owner]/[slug]/revisions/page.tsx b/packages/hub/src/app/models/[owner]/[slug]/revisions/page.tsx index 854c54ab4a..1e9c63f9d7 100644 --- a/packages/hub/src/app/models/[owner]/[slug]/revisions/page.tsx +++ b/packages/hub/src/app/models/[owner]/[slug]/revisions/page.tsx @@ -10,10 +10,11 @@ import QueryNode, { export default async function ModelPage({ params, }: { - params: { owner: string; slug: string }; + params: Promise<{ owner: string; slug: string }>; }) { + const { owner, slug } = await params; const query = await loadPageQuery(QueryNode, { - input: { owner: params.owner, slug: params.slug }, + input: { owner, slug }, }); return ( diff --git a/packages/hub/src/app/models/[owner]/[slug]/variables/[variableName]/page.tsx b/packages/hub/src/app/models/[owner]/[slug]/variables/[variableName]/page.tsx index 7d984f3bf9..59b7ee0586 100644 --- a/packages/hub/src/app/models/[owner]/[slug]/variables/[variableName]/page.tsx +++ b/packages/hub/src/app/models/[owner]/[slug]/variables/[variableName]/page.tsx @@ -7,21 +7,22 @@ import QueryNode, { } from "@/__generated__/VariablePageQuery.graphql"; type Props = { - params: { owner: string; slug: string; variableName: string }; + params: Promise<{ owner: string; slug: string; variableName: string }>; }; export default async function OuterVariablePage({ params }: Props) { + const { owner, slug, variableName } = await params; const query = await loadPageQuery(QueryNode, { input: { - owner: params.owner, - slug: params.slug, - variableName: params.variableName, + owner, + slug, + variableName, }, }); return (
- +
); } diff --git a/packages/hub/src/app/models/[owner]/[slug]/view/page.tsx b/packages/hub/src/app/models/[owner]/[slug]/view/page.tsx index e2d1427452..5ea3b4bbfa 100644 --- a/packages/hub/src/app/models/[owner]/[slug]/view/page.tsx +++ b/packages/hub/src/app/models/[owner]/[slug]/view/page.tsx @@ -7,12 +7,13 @@ import QueryNode, { } from "@/__generated__/ViewModelPageQuery.graphql"; type Props = { - params: { owner: string; slug: string }; + params: Promise<{ owner: string; slug: string }>; }; export default async function OuterModelPage({ params }: Props) { + const { owner, slug } = await params; const query = await loadPageQuery(QueryNode, { - input: { owner: params.owner, slug: params.slug }, + input: { owner, slug }, }); return ( diff --git a/packages/hub/src/app/relative-values/[owner]/[slug]/edit/page.tsx b/packages/hub/src/app/relative-values/[owner]/[slug]/edit/page.tsx index 19dede0f3c..d2345b1ca4 100644 --- a/packages/hub/src/app/relative-values/[owner]/[slug]/edit/page.tsx +++ b/packages/hub/src/app/relative-values/[owner]/[slug]/edit/page.tsx @@ -10,12 +10,13 @@ import { EditRelativeValuesDefinition } from "./EditRelativeValuesDefinition"; export default async function Page({ params, }: { - params: { owner: string; slug: string }; + params: Promise<{ owner: string; slug: string }>; }) { + const { owner, slug } = await params; const query = await loadPageQuery( QueryNode, { - input: { owner: params.owner, slug: params.slug }, + input: { owner, slug }, } ); diff --git a/packages/hub/src/app/relative-values/[owner]/[slug]/layout.tsx b/packages/hub/src/app/relative-values/[owner]/[slug]/layout.tsx index 104980da6d..5b7685db70 100644 --- a/packages/hub/src/app/relative-values/[owner]/[slug]/layout.tsx +++ b/packages/hub/src/app/relative-values/[owner]/[slug]/layout.tsx @@ -10,16 +10,18 @@ import QueryNode, { } from "@/__generated__/DefinitionLayoutQuery.graphql"; type Props = PropsWithChildren<{ - params: { owner: string; slug: string }; + params: Promise<{ owner: string; slug: string }>; }>; export default async function Layout({ params, children }: Props) { + const { owner, slug } = await params; const query = await loadPageQuery(QueryNode, { - input: { owner: params.owner, slug: params.slug }, + input: { owner, slug }, }); return {children}; } -export function generateMetadata({ params }: Props): Metadata { - return { title: `${params.owner}/${params.slug}` }; +export async function generateMetadata({ params }: Props): Promise { + const { owner, slug } = await params; + return { title: `${owner}/${slug}` }; } diff --git a/packages/hub/src/app/relative-values/[owner]/[slug]/page.tsx b/packages/hub/src/app/relative-values/[owner]/[slug]/page.tsx index 1c0b1f666e..c7daff7641 100644 --- a/packages/hub/src/app/relative-values/[owner]/[slug]/page.tsx +++ b/packages/hub/src/app/relative-values/[owner]/[slug]/page.tsx @@ -7,14 +7,15 @@ import { loadPageQuery } from "@/relay/loadPageQuery"; import { RelativeValuesDefinitionPage } from "./RelativeValuesDefinitionPage"; type Props = { - params: { owner: string; slug: string }; + params: Promise<{ owner: string; slug: string }>; }; export default async function OuterDefinitionPage({ params }: Props) { + const { owner, slug } = await params; const query = await loadPageQuery( QueryNode, { - input: { owner: params.owner, slug: params.slug }, + input: { owner, slug }, } ); diff --git a/packages/hub/src/app/users/[username]/definitions/page.tsx b/packages/hub/src/app/users/[username]/definitions/page.tsx index 1316d15607..372d767814 100644 --- a/packages/hub/src/app/users/[username]/definitions/page.tsx +++ b/packages/hub/src/app/users/[username]/definitions/page.tsx @@ -9,17 +9,19 @@ import QueryNode, { } from "@/__generated__/UserDefinitionsPageQuery.graphql"; type Props = { - params: { username: string }; + params: Promise<{ username: string }>; }; export default async function OuterUserDefinitionsPage({ params }: Props) { + const { username } = await params; const query = await loadPageQuery(QueryNode, { - username: params.username, + username, }); return ; } -export function generateMetadata({ params }: Props): Metadata { - return { title: params.username }; +export async function generateMetadata({ params }: Props): Promise { + const { username } = await params; + return { title: username }; } diff --git a/packages/hub/src/app/users/[username]/groups/page.tsx b/packages/hub/src/app/users/[username]/groups/page.tsx index 489abec4b5..7246da8a27 100644 --- a/packages/hub/src/app/users/[username]/groups/page.tsx +++ b/packages/hub/src/app/users/[username]/groups/page.tsx @@ -9,17 +9,19 @@ import QueryNode, { } from "@/__generated__/UserGroupsPageQuery.graphql"; type Props = { - params: { username: string }; + params: Promise<{ username: string }>; }; export default async function OuterUserGroupsPage({ params }: Props) { + const { username } = await params; const query = await loadPageQuery(QueryNode, { - username: params.username, + username, }); return ; } -export function generateMetadata({ params }: Props): Metadata { - return { title: params.username }; +export async function generateMetadata({ params }: Props): Promise { + const { username } = await params; + return { title: username }; } diff --git a/packages/hub/src/app/users/[username]/layout.tsx b/packages/hub/src/app/users/[username]/layout.tsx index a1b179afd4..657eb9dbf4 100644 --- a/packages/hub/src/app/users/[username]/layout.tsx +++ b/packages/hub/src/app/users/[username]/layout.tsx @@ -11,15 +11,16 @@ import QueryNode, { } from "@/__generated__/UserLayoutQuery.graphql"; type Props = { - params: { username: string }; + params: Promise<{ username: string }>; }; export default async function OuterUserLayout({ params, children, }: PropsWithChildren) { + const { username } = await params; const query = await loadPageQuery(QueryNode, { - username: params.username, + username, }); return ( @@ -29,6 +30,7 @@ export default async function OuterUserLayout({ ); } -export function generateMetadata({ params }: Props): Metadata { - return { title: params.username }; +export async function generateMetadata({ params }: Props): Promise { + const { username } = await params; + return { title: username }; } diff --git a/packages/hub/src/app/users/[username]/page.tsx b/packages/hub/src/app/users/[username]/page.tsx index b39a7140f0..c3c5f53180 100644 --- a/packages/hub/src/app/users/[username]/page.tsx +++ b/packages/hub/src/app/users/[username]/page.tsx @@ -9,17 +9,19 @@ import QueryNode, { } from "@/__generated__/UserPageQuery.graphql"; type Props = { - params: { username: string }; + params: Promise<{ username: string }>; }; export default async function OuterUserPage({ params }: Props) { + const { username } = await params; const query = await loadPageQuery(QueryNode, { - username: params.username, + username, }); return ; } -export function generateMetadata({ params }: Props): Metadata { - return { title: params.username }; +export async function generateMetadata({ params }: Props): Promise { + const { username } = await params; + return { title: username }; } diff --git a/packages/hub/src/app/users/[username]/variables/page.tsx b/packages/hub/src/app/users/[username]/variables/page.tsx index defcd69102..005bd8c015 100644 --- a/packages/hub/src/app/users/[username]/variables/page.tsx +++ b/packages/hub/src/app/users/[username]/variables/page.tsx @@ -9,17 +9,19 @@ import QueryNode, { } from "@/__generated__/UserVariablesPageQuery.graphql"; type Props = { - params: { username: string }; + params: Promise<{ username: string }>; }; export default async function OuterUserVariablesPage({ params }: Props) { + const { username } = await params; const query = await loadPageQuery(QueryNode, { - username: params.username, + username, }); return ; } -export function generateMetadata({ params }: Props): Metadata { - return { title: params.username }; +export async function generateMetadata({ params }: Props): Promise { + const { username } = await params; + return { title: username }; } From a53b6fbbbcb09cb1090e839f8d17d942a783817c Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Sun, 24 Nov 2024 21:24:55 -0300 Subject: [PATCH 16/17] turbopack in dev --- packages/hub/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/hub/package.json b/packages/hub/package.json index 33f37907f8..b79183f33d 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -9,7 +9,7 @@ "db:migrate:dev": "PRISMA_HIDE_UPDATE_MESSAGE=1 prisma migrate dev", "db:migrate": "PRISMA_HIDE_UPDATE_MESSAGE=1 prisma migrate deploy", "db:reset": "PRISMA_HIDE_UPDATE_MESSAGE=1 prisma migrate reset", - "dev": "next dev -p 3001", + "dev": "next dev -p 3001 --turbo", "start": "__NEXT_PRIVATE_PREBUNDLED_REACT=next next start", "gen:prisma": "PRISMA_HIDE_UPDATE_MESSAGE=1 prisma generate --no-hints", "gen:relay": "relay-compiler", From f84baa10170e5a82dcbe0d36ac66fd6a67aef1b5 Mon Sep 17 00:00:00 2001 From: Vyacheslav Matyukhin Date: Sun, 24 Nov 2024 21:39:28 -0300 Subject: [PATCH 17/17] pin node to v20 for hub and website --- packages/hub/package.json | 2 +- packages/website/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/hub/package.json b/packages/hub/package.json index b79183f33d..b8a03074fd 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "engines": { - "node": ">=20.x" + "node": "20.x" }, "scripts": { "db:migrate:dev": "PRISMA_HIDE_UPDATE_MESSAGE=1 prisma migrate dev", diff --git a/packages/website/package.json b/packages/website/package.json index 75725ed7e1..91f5bae01b 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -4,7 +4,7 @@ "private": true, "license": "MIT", "engines": { - "node": ">=20.x" + "node": "20.x" }, "scripts": { "dev": "next dev",