From 50e36356897075037cd1be52f01589fad9325812 Mon Sep 17 00:00:00 2001 From: Kevin Foley Date: Wed, 8 Apr 2026 11:40:27 -0400 Subject: [PATCH] chore: migrate to @assembly-js/node-sdk and @assembly-js/app-bridge - Replace copilot-node-sdk with @assembly-js/node-sdk (v4.0.2) - Implement lazy init pattern for async assemblyApi() in CopilotAPI - Migrate dispatchWebhook to SDK's sendWebhook method - Replace manual postMessage hooks with @assembly-js/app-bridge - Wire up AssemblyBridge token refresh in ClientSideStateUpdate - Remove portalUrl prop drilling (handled by AssemblyBridge.configure) - Make NEXT_PUBLIC_ASSEMBLY_API_DOMAIN optional (token baseUrl takes priority) - Fix serve-embedded: DASHBOARD_DOMAIN array bug, localhost support Co-Authored-By: Claude Opus 4.6 (1M context) --- jest.config.ts | 2 +- package.json | 3 +- src/app/client/page.tsx | 2 +- src/app/detail/[task_id]/[user_type]/page.tsx | 8 +- src/app/detail/ui/ClientDetailAppBridge.tsx | 6 +- src/app/detail/ui/DetailAppBridge.tsx | 8 +- src/app/detail/ui/Sidebar.tsx | 3 - .../manage-templates/[template_id]/page.tsx | 2 +- src/app/manage-templates/page.tsx | 2 +- .../ui/ManageTemplatesAppBridge.tsx | 27 ++-- .../ui/ManageTemplatesDetailsAppBridge.tsx | 7 +- src/app/ui/TaskBoard.tsx | 2 +- src/app/ui/TaskBoardAppBridge.tsx | 9 +- src/cmd/serve-embedded/index.ts | 12 +- src/components/layouts/HeaderBreadcrumbs.tsx | 3 - src/config/index.ts | 4 +- src/hoc/ClientSideStateUpdate.tsx | 9 ++ src/hooks/app-bridge/types.ts | 36 ----- src/hooks/app-bridge/useActionsMenu.tsx | 70 ++------- src/hooks/app-bridge/useBreadcrumbs.tsx | 51 ++----- src/hooks/app-bridge/usePrimaryCta.tsx | 55 ++----- src/hooks/app-bridge/useSecondaryCta.tsx | 55 ++----- src/hooks/app-bridge/utils.ts | 7 - src/utils/CopilotAPI.ts | 89 ++++++----- yarn.lock | 140 ++---------------- 25 files changed, 160 insertions(+), 452 deletions(-) delete mode 100644 src/hooks/app-bridge/utils.ts diff --git a/jest.config.ts b/jest.config.ts index a1d73c021..02599aa7f 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -187,7 +187,7 @@ const config: Config = { // transform: undefined, // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation - // transformIgnorePatterns: ['/node_modules/copilot-node-sdk/(.*)'], + // transformIgnorePatterns: ['/node_modules/@assembly-js/node-sdk/(.*)'], // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them // unmockedModulePathPatterns: undefined, diff --git a/package.json b/package.json index 05e3c500b..c022a1e1c 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "bottleneck": "^2.19.5", "clsx": "^2.1.1", "copilot-design-system": "^2.3.4", - "copilot-node-sdk": "^3.16.0", + "@assembly-js/app-bridge": "^1.1.0", + "@assembly-js/node-sdk": "^4.0.2", "dayjs": "^1.11.10", "deep-equal": "^2.2.3", "fetch-retry": "^6.0.0", diff --git a/src/app/client/page.tsx b/src/app/client/page.tsx index 51ba540a7..efd9f0c87 100644 --- a/src/app/client/page.tsx +++ b/src/app/client/page.tsx @@ -104,7 +104,7 @@ export default async function ClientPage(props: { searchParams: Promise<{ token: - + diff --git a/src/app/detail/[task_id]/[user_type]/page.tsx b/src/app/detail/[task_id]/[user_type]/page.tsx index aff41864b..7fbd41e0b 100644 --- a/src/app/detail/[task_id]/[user_type]/page.tsx +++ b/src/app/detail/[task_id]/[user_type]/page.tsx @@ -160,12 +160,7 @@ export default async function TaskDetailPage(props: { ) : ( <> - + )} @@ -249,7 +244,6 @@ export default async function TaskDetailPage(props: { task_id={task_id} selectedAssigneeId={task?.assigneeId} userType={user_type} - portalUrl={workspace.portalUrl} selectedWorkflowState={task?.workflowState} updateWorkflowState={async (workflowState) => { 'use server' diff --git a/src/app/detail/ui/ClientDetailAppBridge.tsx b/src/app/detail/ui/ClientDetailAppBridge.tsx index a0e2152c4..7cbca2786 100644 --- a/src/app/detail/ui/ClientDetailAppBridge.tsx +++ b/src/app/detail/ui/ClientDetailAppBridge.tsx @@ -9,10 +9,9 @@ import { useCallback, useEffect, useState } from 'react' interface DetailAppBridgeProps { handleTaskComplete: () => void isTaskCompleted: boolean - portalUrl?: string } -export const ClientDetailAppBridge = ({ handleTaskComplete, isTaskCompleted, portalUrl }: DetailAppBridgeProps) => { +export const ClientDetailAppBridge = ({ handleTaskComplete, isTaskCompleted }: DetailAppBridgeProps) => { const awake = useAwake() const handleMarkAsDone = useCallback(() => { @@ -29,9 +28,8 @@ export const ClientDetailAppBridge = ({ handleTaskComplete, isTaskCompleted, por icon: Icons.CHECK, onClick: handleMarkAsDone, }, - { portalUrl }, ) - useSecondaryCta(null, { portalUrl }) + useSecondaryCta(null) return <> } diff --git a/src/app/detail/ui/DetailAppBridge.tsx b/src/app/detail/ui/DetailAppBridge.tsx index 37e867f47..b705b52a1 100644 --- a/src/app/detail/ui/DetailAppBridge.tsx +++ b/src/app/detail/ui/DetailAppBridge.tsx @@ -10,13 +10,12 @@ import store from '@/redux/store' interface DetailAppBridgeProps { isArchived: boolean handleToggleArchive: () => void - portalUrl?: string } -export const DetailAppBridge = ({ isArchived, handleToggleArchive, portalUrl }: DetailAppBridgeProps) => { +export const DetailAppBridge = ({ isArchived, handleToggleArchive }: DetailAppBridgeProps) => { const handleDelete = () => store.dispatch(setShowConfirmDeleteModal()) - usePrimaryCta(null, { portalUrl }) + usePrimaryCta(null) useSecondaryCta( isArchived ? { @@ -25,7 +24,6 @@ export const DetailAppBridge = ({ isArchived, handleToggleArchive, portalUrl }: onClick: handleToggleArchive, } : null, - { portalUrl }, ) const items: Clickable[] = [ @@ -44,7 +42,7 @@ export const DetailAppBridge = ({ isArchived, handleToggleArchive, portalUrl }: }) } - useActionsMenu(items, { portalUrl }) + useActionsMenu(items) return <> } diff --git a/src/app/detail/ui/Sidebar.tsx b/src/app/detail/ui/Sidebar.tsx index ccdc621d7..c8efc86f7 100644 --- a/src/app/detail/ui/Sidebar.tsx +++ b/src/app/detail/ui/Sidebar.tsx @@ -67,7 +67,6 @@ export const Sidebar = ({ disabled, workflowDisabled = false, userType, - portalUrl, }: { task_id: string selectedWorkflowState: WorkflowStateResponse @@ -78,7 +77,6 @@ export const Sidebar = ({ disabled: boolean workflowDisabled?: boolean userType: UserType - portalUrl?: string }) => { const { activeTask, workflowStates, assignee, previewMode } = useSelector(selectTaskBoard) const { showSidebar, showConfirmAssignModal, fromNotificationCenter } = useSelector(selectTaskDetails) @@ -617,7 +615,6 @@ export const Sidebar = ({ completedWorkflowState && updateStatusValue(completedWorkflowState) completedWorkflowState && updateWorkflowState(completedWorkflowState) }} - portalUrl={portalUrl} /> )} diff --git a/src/app/manage-templates/[template_id]/page.tsx b/src/app/manage-templates/[template_id]/page.tsx index e5426a3e7..d1314b85a 100644 --- a/src/app/manage-templates/[template_id]/page.tsx +++ b/src/app/manage-templates/[template_id]/page.tsx @@ -80,7 +80,7 @@ export default async function TaskDetailPage(props: { )} - + - + { diff --git a/src/app/manage-templates/ui/ManageTemplatesAppBridge.tsx b/src/app/manage-templates/ui/ManageTemplatesAppBridge.tsx index 24fd20ca5..e89432793 100644 --- a/src/app/manage-templates/ui/ManageTemplatesAppBridge.tsx +++ b/src/app/manage-templates/ui/ManageTemplatesAppBridge.tsx @@ -13,31 +13,24 @@ import { useRouter } from 'next/navigation' interface TaskBoardAppBridgeProps { token: string role: UserRole - portalUrl?: string } -export const ManageTemplatesAppBridge = ({ token, role, portalUrl }: TaskBoardAppBridgeProps) => { +export const ManageTemplatesAppBridge = ({ token, role }: TaskBoardAppBridgeProps) => { const handleTemplateCreate = () => { store.dispatch(setShowTemplateModal({ targetMethod: TargetMethod.POST })) } - usePrimaryCta( + usePrimaryCta({ + label: 'Create template', + icon: Icons.PLUS, + onClick: handleTemplateCreate, + }) + useActionsMenu([]) + useBreadcrumbs([ { - label: 'Create template', - icon: Icons.PLUS, - onClick: handleTemplateCreate, + label: 'Manage templates', }, - { portalUrl }, - ) - useActionsMenu([], { portalUrl }) - useBreadcrumbs( - [ - { - label: 'Manage templates', - }, - ], - { portalUrl }, - ) + ]) return <> } diff --git a/src/app/manage-templates/ui/ManageTemplatesDetailsAppBridge.tsx b/src/app/manage-templates/ui/ManageTemplatesDetailsAppBridge.tsx index c31bf8785..b93faa444 100644 --- a/src/app/manage-templates/ui/ManageTemplatesDetailsAppBridge.tsx +++ b/src/app/manage-templates/ui/ManageTemplatesDetailsAppBridge.tsx @@ -12,11 +12,10 @@ import { ITemplate } from '@/types/interfaces' import { useCallback, useEffect, useState } from 'react' interface ManageTemplateDetailsAppBridgeProps { - portalUrl?: string template: ITemplate } -export const ManageTemplateDetailsAppBridge = ({ portalUrl, template }: ManageTemplateDetailsAppBridgeProps) => { +export const ManageTemplateDetailsAppBridge = ({ template }: ManageTemplateDetailsAppBridgeProps) => { const awake = useAwake() const handleDeleteTemplate = useCallback(() => { @@ -34,8 +33,8 @@ export const ManageTemplateDetailsAppBridge = ({ portalUrl, template }: ManageTe onClick: handleDeleteTemplate, }, ] - usePrimaryCta(null, { portalUrl }) - useActionsMenu(items, { portalUrl }) + usePrimaryCta(null) + useActionsMenu(items) return <> } diff --git a/src/app/ui/TaskBoard.tsx b/src/app/ui/TaskBoard.tsx index d8c90d9b3..4b3453586 100644 --- a/src/app/ui/TaskBoard.tsx +++ b/src/app/ui/TaskBoard.tsx @@ -134,7 +134,7 @@ export const TaskBoard = ({ mode, workspace, token }: TaskBoardProps) => { <> - {mode == UserRole.IU && } + {mode == UserRole.IU && } {/* Filterbars */} diff --git a/src/app/ui/TaskBoardAppBridge.tsx b/src/app/ui/TaskBoardAppBridge.tsx index 70f53bb33..227f912f6 100644 --- a/src/app/ui/TaskBoardAppBridge.tsx +++ b/src/app/ui/TaskBoardAppBridge.tsx @@ -15,11 +15,10 @@ import { useCallback, useEffect, useState } from 'react' interface TaskBoardAppBridgeProps { token: string role: UserRole - portalUrl?: string isTaskBoardEmpty?: boolean } -export const TaskBoardAppBridge = ({ token, role, portalUrl, isTaskBoardEmpty = false }: TaskBoardAppBridgeProps) => { +export const TaskBoardAppBridge = ({ token, role, isTaskBoardEmpty = false }: TaskBoardAppBridgeProps) => { const router = useRouter() const awake = useAwake() @@ -41,11 +40,10 @@ export const TaskBoardAppBridge = ({ token, role, portalUrl, isTaskBoardEmpty = icon: Icons.PLUS, onClick: handleTaskCreate, }, - { portalUrl }, ) // Unset "Unarchive" button from tasks details if redirected to board from an archived task - useSecondaryCta(null, { portalUrl }) + useSecondaryCta(null) useActionsMenu( role == UserRole.Client @@ -57,9 +55,8 @@ export const TaskBoardAppBridge = ({ token, role, portalUrl, isTaskBoardEmpty = onClick: handleManageTemplatesClick, }, ], - { portalUrl }, ) - useBreadcrumbs([], { portalUrl }) + useBreadcrumbs([]) return <> } diff --git a/src/cmd/serve-embedded/index.ts b/src/cmd/serve-embedded/index.ts index 1a1ae41e6..ca4b5ea39 100644 --- a/src/cmd/serve-embedded/index.ts +++ b/src/cmd/serve-embedded/index.ts @@ -29,8 +29,8 @@ const createNgrokTunnel = async (port: number): Promise => { return ngrokUrl } -const openDashboard = async (ngrokUrl: string) => { - const url = `${DASHBOARD_DOMAIN}/dev-mode?url=${encodeURIComponent(ngrokUrl)}` +const openDashboard = async (appUrl: string) => { + const url = `${DASHBOARD_DOMAIN[0]}/dev-mode?url=${encodeURIComponent(appUrl)}` console.info(`> Opening browser at ${url}`) const browserApp = Array.isArray(apps.browser) ? apps.browser[0] : apps.browser @@ -51,6 +51,9 @@ const handleProcessEvents = () => { async function startServer() { await app.prepare() + const useNgrok = !!process.env.NGROK_AUTHTOKEN + const appUrl = useNgrok ? await createNgrokTunnel(port) : `http://localhost:${port}` + const server = createServer((req: IncomingMessage, res: ServerResponse) => { if (!req.url) { res.statusCode = 400 @@ -59,7 +62,7 @@ async function startServer() { } const parsedUrl = parse(req.url, true) - res.setHeader('Set-Cookie', `ngrokUrl=${ngrokUrl}`) + res.setHeader('Set-Cookie', `appUrl=${appUrl}`) res.setHeader('X-Frame-Options', '') handle(req, res, parsedUrl) }) @@ -68,8 +71,7 @@ async function startServer() { console.info(`> Server listening at http://localhost:${port}`) }) - const ngrokUrl = await createNgrokTunnel(port) - await openDashboard(ngrokUrl) + await openDashboard(appUrl) handleProcessEvents() } diff --git a/src/components/layouts/HeaderBreadcrumbs.tsx b/src/components/layouts/HeaderBreadcrumbs.tsx index 00ab5364c..abf66f52a 100644 --- a/src/components/layouts/HeaderBreadcrumbs.tsx +++ b/src/components/layouts/HeaderBreadcrumbs.tsx @@ -17,12 +17,10 @@ export const HeaderBreadcrumbs = ({ token, items, userType, - portalUrl, }: { token: string | undefined items: { label: string; href?: string }[] userType: UserType - portalUrl?: string }) => { const { previewMode } = useSelector(selectTaskBoard) const router = useRouter() @@ -41,7 +39,6 @@ export const HeaderBreadcrumbs = ({ label, onClick: index === items.length - 1 ? undefined : href ? () => router.push(href) : undefined, })), - { portalUrl }, ) if (!previewMode) { diff --git a/src/config/index.ts b/src/config/index.ts index d4b27e017..a99459792 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -1,5 +1,3 @@ -import z from 'zod' - export const copilotAPIKey = process.env.COPILOT_API_KEY || '' export const tasksAppId = process.env.COPILOT_TASKS_APP_ID || '' @@ -30,4 +28,4 @@ export const showQueries = (() => { return true })() -export const assemblyApiDomain = z.string().url().parse(process.env.NEXT_PUBLIC_ASSEMBLY_API_DOMAIN) +export const assemblyApiDomain = process.env.NEXT_PUBLIC_ASSEMBLY_API_DOMAIN ?? '' diff --git a/src/hoc/ClientSideStateUpdate.tsx b/src/hoc/ClientSideStateUpdate.tsx index d159c4a25..58ca04d45 100644 --- a/src/hoc/ClientSideStateUpdate.tsx +++ b/src/hoc/ClientSideStateUpdate.tsx @@ -1,5 +1,6 @@ 'use client' +import { AssemblyBridge } from '@assembly-js/app-bridge' import { setTokenPayload, setWorkspace } from '@/redux/features/authDetailsSlice' import { selectTaskBoard, @@ -87,6 +88,10 @@ export const ClientSideStateUpdate = ({ store.dispatch(setToken(token)) } + const unsubscribeTokenRefresh = AssemblyBridge.sessionToken.onTokenUpdate((data) => { + store.dispatch(setToken(data.token)) + }) + if (assignee) { store.dispatch(setAssigneeList(assignee)) } @@ -152,6 +157,9 @@ export const ClientSideStateUpdate = ({ if (workspace) { store.dispatch(setWorkspace(workspace)) + if (workspace.portalUrl) { + AssemblyBridge.configure({ additionalOrigins: [workspace.portalUrl] }) + } } if (template) { store.dispatch(setActiveTemplate(template)) @@ -159,6 +167,7 @@ export const ClientSideStateUpdate = ({ return () => { store.dispatch(setActiveTask(undefined)) store.dispatch(setActiveTemplate(null)) + unsubscribeTokenRefresh() } //when component is unmounted, we need to clear the active task. }, [ workflowStates, diff --git a/src/hooks/app-bridge/types.ts b/src/hooks/app-bridge/types.ts index 6890a6ef0..a425dae26 100644 --- a/src/hooks/app-bridge/types.ts +++ b/src/hooks/app-bridge/types.ts @@ -7,45 +7,9 @@ export enum Icons { CHECK = 'Check', } -export interface BreadcrumbsPayload { - items: { - label: string - onClick: string - }[] - type: 'header.breadcrumbs' -} - -export interface PrimaryCtaPayload { - icon?: Icons - label: string - onClick?: string - type: 'header.primaryCta' -} - -export interface SecondaryCtaPayload { - icon?: Icons - label: string - onClick?: string - type: 'header.secondaryCta' -} - -export interface ActionsMenuPayload { - items: { - label: string - onClick: string - icon?: Icons - color?: string - }[] - type: 'header.actionsMenu' -} - export interface Clickable { label: string onClick?: () => void icon?: Icons color?: string } - -export interface Configurable { - portalUrl?: string -} diff --git a/src/hooks/app-bridge/useActionsMenu.tsx b/src/hooks/app-bridge/useActionsMenu.tsx index d8f461b09..2c6028a64 100644 --- a/src/hooks/app-bridge/useActionsMenu.tsx +++ b/src/hooks/app-bridge/useActionsMenu.tsx @@ -1,58 +1,18 @@ -import { ActionsMenuPayload, Clickable, Configurable } from '@/hooks/app-bridge/types' -import { ensureHttps } from '@/utils/https' -import { useEffect, useMemo } from 'react' -import { DASHBOARD_DOMAIN } from '@/constants/domains' -import { postMessageParentDashboard } from './utils' -const getActionMenuItemId = (idx: number) => `header.actionsMenu.${idx}` - -export function useActionsMenu(actions: Clickable[], config?: Configurable) { - const callbackRefs = useMemo(() => { - return actions.reduce void>>((acc, { onClick }, idx) => { - if (onClick) acc[getActionMenuItemId(idx)] = onClick - return acc - }, {}) - }, [actions]) +import { AssemblyBridge } from '@assembly-js/app-bridge' +import { Clickable } from '@/hooks/app-bridge/types' +import { useEffect } from 'react' +export function useActionsMenu(actions: Clickable[]) { useEffect(() => { - const payload: ActionsMenuPayload = { - type: 'header.actionsMenu', - items: actions.map(({ label, onClick, icon, color }, idx) => ({ - onClick: onClick ? getActionMenuItemId(idx) : '', - label, - icon, - color, - })), - } - - postMessageParentDashboard(payload) - if (config?.portalUrl) { - window.parent.postMessage(payload, ensureHttps(config.portalUrl)) - } - - const handleMessage = (event: MessageEvent) => { - if ( - event.data.type === 'header.actionsMenu.onClick' && - typeof event.data.id === 'string' && - callbackRefs[event.data.id] - ) { - callbackRefs[event.data.id]() - } - } - - addEventListener('message', handleMessage) - - return () => { - removeEventListener('message', handleMessage) - } - }, [actions, callbackRefs, config?.portalUrl]) - - useEffect(() => { - const handleUnload = () => { - postMessageParentDashboard({ type: 'header.actionsMenu', items: [] }) - } - addEventListener('beforeunload', handleUnload) - return () => { - removeEventListener('beforeunload', handleUnload) - } - }, []) + AssemblyBridge.header.setActionsMenu( + actions + .filter((action) => action.onClick) + .map(({ label, onClick, icon, color }) => ({ + label, + onClick: onClick!, + icon, + ...(color ? { color: color as 'red' } : {}), + })), + ) + }, [actions]) } diff --git a/src/hooks/app-bridge/useBreadcrumbs.tsx b/src/hooks/app-bridge/useBreadcrumbs.tsx index 755701ccb..6eca0470e 100644 --- a/src/hooks/app-bridge/useBreadcrumbs.tsx +++ b/src/hooks/app-bridge/useBreadcrumbs.tsx @@ -1,47 +1,14 @@ -import { BreadcrumbsPayload, Clickable, Configurable } from '@/hooks/app-bridge/types' -import { ensureHttps } from '@/utils/https' -import { useEffect, useMemo } from 'react' -import { DASHBOARD_DOMAIN } from '@/constants/domains' -import { postMessageParentDashboard } from './utils' - -const getBreadcrumbId = (idx: number) => `header.breadcrumbs.${idx}` - -export const useBreadcrumbs = (breadcrumbs: Clickable[], config?: Configurable) => { - const callbackRefs = useMemo(() => { - return breadcrumbs.reduce void>>((acc, { onClick }, idx) => { - if (onClick) acc[getBreadcrumbId(idx)] = onClick - return acc - }, {}) - }, [breadcrumbs]) +import { AssemblyBridge } from '@assembly-js/app-bridge' +import { Clickable } from '@/hooks/app-bridge/types' +import { useEffect } from 'react' +export const useBreadcrumbs = (breadcrumbs: Clickable[]) => { useEffect(() => { - const payload: BreadcrumbsPayload = { - type: 'header.breadcrumbs', - items: breadcrumbs.map(({ label, onClick }, idx) => ({ - onClick: onClick ? getBreadcrumbId(idx) : '', + AssemblyBridge.header.setBreadcrumbs( + breadcrumbs.map(({ label, onClick }) => ({ label, + onClick, })), - } - - postMessageParentDashboard(payload) - if (config?.portalUrl) { - window.parent.postMessage(payload, ensureHttps(config.portalUrl)) - } - - const handleMessage = (event: MessageEvent) => { - if ( - event.data.type === 'header.breadcrumbs.onClick' && - typeof event.data.id === 'string' && - callbackRefs[event.data.id] - ) { - callbackRefs[event.data.id]() - } - } - - addEventListener('message', handleMessage) - - return () => { - removeEventListener('message', handleMessage) - } - }, [breadcrumbs, callbackRefs, config?.portalUrl]) + ) + }, [breadcrumbs]) } diff --git a/src/hooks/app-bridge/usePrimaryCta.tsx b/src/hooks/app-bridge/usePrimaryCta.tsx index 947f12df3..efd4aeb2a 100644 --- a/src/hooks/app-bridge/usePrimaryCta.tsx +++ b/src/hooks/app-bridge/usePrimaryCta.tsx @@ -1,48 +1,17 @@ -import { Clickable, Configurable, PrimaryCtaPayload } from '@/hooks/app-bridge/types' -import { ensureHttps } from '@/utils/https' +import { AssemblyBridge } from '@assembly-js/app-bridge' +import { Clickable } from '@/hooks/app-bridge/types' import { useEffect } from 'react' -import { DASHBOARD_DOMAIN } from '@/constants/domains' -import { postMessageParentDashboard } from './utils' -export const usePrimaryCta = (primaryCta: Clickable | null, config?: Configurable) => { +export const usePrimaryCta = (primaryCta: Clickable | null) => { useEffect(() => { - const payload: PrimaryCtaPayload | Pick = !primaryCta - ? { type: 'header.primaryCta' } - : { - icon: primaryCta.icon, - label: primaryCta.label, - onClick: 'header.primaryCta.onClick', - type: 'header.primaryCta', - } - - postMessageParentDashboard(payload) - if (config?.portalUrl) { - window.parent.postMessage(payload, ensureHttps(config.portalUrl)) - } - - const handleMessage = (event: MessageEvent) => { - if (event.data.type === 'header.primaryCta.onClick' && typeof event.data.id === 'string' && primaryCta?.onClick) { - primaryCta.onClick() - } - } - - addEventListener('message', handleMessage) - - return () => { - removeEventListener('message', handleMessage) - } - }, [primaryCta, config?.portalUrl]) - - useEffect(() => { - const handleUnload = () => { - postMessageParentDashboard({ type: 'header.primaryCta' }) - if (config?.portalUrl) { - window.parent.postMessage({ type: 'header.primaryCta' }, ensureHttps(config.portalUrl)) - } - } - addEventListener('beforeunload', handleUnload) - return () => { - removeEventListener('beforeunload', handleUnload) + if (primaryCta?.onClick) { + AssemblyBridge.header.setPrimaryCta({ + label: primaryCta.label, + icon: primaryCta.icon, + onClick: primaryCta.onClick, + }) + } else { + AssemblyBridge.header.setPrimaryCta(null) } - }, [config?.portalUrl]) + }, [primaryCta]) } diff --git a/src/hooks/app-bridge/useSecondaryCta.tsx b/src/hooks/app-bridge/useSecondaryCta.tsx index 62f4ac491..821a36a10 100644 --- a/src/hooks/app-bridge/useSecondaryCta.tsx +++ b/src/hooks/app-bridge/useSecondaryCta.tsx @@ -1,48 +1,17 @@ -import { Clickable, Configurable, SecondaryCtaPayload } from '@/hooks/app-bridge/types' -import { ensureHttps } from '@/utils/https' +import { AssemblyBridge } from '@assembly-js/app-bridge' +import { Clickable } from '@/hooks/app-bridge/types' import { useEffect } from 'react' -import { DASHBOARD_DOMAIN } from '@/constants/domains' -import { postMessageParentDashboard } from './utils' -export const useSecondaryCta = (secondaryCta: Clickable | null, config?: Configurable) => { +export const useSecondaryCta = (secondaryCta: Clickable | null) => { useEffect(() => { - const payload: SecondaryCtaPayload | Pick = !secondaryCta - ? { type: 'header.secondaryCta' } - : { - type: 'header.secondaryCta', - label: secondaryCta.label, - icon: secondaryCta.icon, - onClick: 'header.secondaryCta.onClick', - } - - postMessageParentDashboard(payload) - if (config?.portalUrl) { - window.parent.postMessage(payload, ensureHttps(config.portalUrl)) - } - - const handleMessage = (event: MessageEvent) => { - if (event.data.type === 'header.secondaryCta.onClick' && typeof event.data.id === 'string' && secondaryCta?.onClick) { - secondaryCta.onClick() - } - } - - addEventListener('message', handleMessage) - - return () => { - removeEventListener('message', handleMessage) - } - }, [secondaryCta, config?.portalUrl]) - - useEffect(() => { - const handleUnload = () => { - postMessageParentDashboard({ type: 'header.secondaryCta' }) - if (config?.portalUrl) { - window.parent.postMessage({ type: 'header.secondaryCta' }, ensureHttps(config.portalUrl)) - } - } - addEventListener('beforeunload', handleUnload) - return () => { - removeEventListener('beforeunload', handleUnload) + if (secondaryCta?.onClick) { + AssemblyBridge.header.setSecondaryCta({ + label: secondaryCta.label, + icon: secondaryCta.icon, + onClick: secondaryCta.onClick, + }) + } else { + AssemblyBridge.header.setSecondaryCta(null) } - }, [config?.portalUrl]) + }, [secondaryCta]) } diff --git a/src/hooks/app-bridge/utils.ts b/src/hooks/app-bridge/utils.ts deleted file mode 100644 index dd201e717..000000000 --- a/src/hooks/app-bridge/utils.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { DASHBOARD_DOMAIN } from '@/constants/domains' - -export const postMessageParentDashboard = (payload: object) => { - for (const domain of DASHBOARD_DOMAIN) { - window.parent.postMessage(payload, domain) - } -} diff --git a/src/utils/CopilotAPI.ts b/src/utils/CopilotAPI.ts index d734d02df..5b03adc5e 100644 --- a/src/utils/CopilotAPI.ts +++ b/src/utils/CopilotAPI.ts @@ -37,22 +37,33 @@ import { } from '@/types/common' import { DISPATCHABLE_EVENT } from '@/types/webhook' import Bottleneck from 'bottleneck' -import type { CopilotAPI as SDK } from 'copilot-node-sdk' -import { copilotApi } from 'copilot-node-sdk' +import type { AssemblyAPI as SDK } from '@assembly-js/node-sdk' +import { assemblyApi } from '@assembly-js/node-sdk' import { z } from 'zod' export class CopilotAPI { - copilot: SDK + private assemblyPromise: Promise constructor( private token: string, customApiKey?: string, ) { - this.copilot = copilotApi({ apiKey: customApiKey ?? apiKey, token }) + this.assemblyPromise = assemblyApi({ apiKey: customApiKey ?? apiKey, token }) + } + + private async _getBaseUrl(): Promise { + const assembly = await this.assemblyPromise + const tokenPayload = assembly.getTokenPayload ? await assembly.getTokenPayload() : null + const baseUrl = tokenPayload?.baseUrl || assemblyApiDomain + if (!baseUrl) { + throw new Error('No API base URL available: set NEXT_PUBLIC_ASSEMBLY_API_DOMAIN or provide a token with baseUrl') + } + return baseUrl } private async _manualFetch(route: string, query?: Record, workspaceId?: string) { - const url = new URL(`${assemblyApiDomain}/v1/${route}`) + const baseUrl = await this._getBaseUrl() + const url = new URL(`${baseUrl}/v1/${route}`) if (query) { for (const key of Object.keys(query)) { url.searchParams.set(key, query[key]) @@ -78,7 +89,8 @@ export class CopilotAPI { // Get Token Payload from copilot request token async _getTokenPayload(): Promise { - const getTokenPayload = this.copilot.getTokenPayload + const assembly = await this.assemblyPromise + const getTokenPayload = assembly.getTokenPayload if (!getTokenPayload) { console.error(`CopilotAPI#getTokenPayload | Could not parse token payload for token ${this.token}`) return null @@ -93,9 +105,8 @@ export class CopilotAPI { const id = tokenPayload?.internalUserId || tokenPayload?.clientId if (!tokenPayload || !id) return null - const retrieveCurrentUserInfo = tokenPayload.internalUserId - ? this.copilot.retrieveInternalUser - : this.copilot.retrieveClient + const assembly = await this.assemblyPromise + const retrieveCurrentUserInfo = tokenPayload.internalUserId ? assembly.retrieveInternalUser : assembly.retrieveClient const currentUserInfo = await retrieveCurrentUserInfo({ id }) return MeResponseSchema.parse(currentUserInfo) @@ -103,7 +114,8 @@ export class CopilotAPI { async _getWorkspace(): Promise { console.info('CopilotAPI#_getWorkspace', this.token) - return WorkspaceResponseSchema.parse(await this.copilot.retrieveWorkspace()) + const assembly = await this.assemblyPromise + return WorkspaceResponseSchema.parse(await assembly.retrieveWorkspace()) } async _getClientTokenPayload(): Promise { @@ -124,30 +136,33 @@ export class CopilotAPI { async _createClient(requestBody: ClientRequest, sendInvite: boolean = false): Promise { console.info('CopilotAPI#_createClient', this.token) - return ClientResponseSchema.parse(await this.copilot.createClient({ sendInvite, requestBody })) + const assembly = await this.assemblyPromise + return ClientResponseSchema.parse(await assembly.createClient({ sendInvite, requestBody })) } async _getClient(id: string): Promise { console.info('CopilotAPI#_getClient', this.token) - return ClientResponseSchema.parse(await this.copilot.retrieveClient({ id })) + const assembly = await this.assemblyPromise + return ClientResponseSchema.parse(await assembly.retrieveClient({ id })) } async _getClients(args: CopilotListArgs & { companyId?: string } = {}) { console.info('CopilotAPI#_getClients', this.token) + const assembly = await this.assemblyPromise const maxLimit = MAX_LIMIT_CLIENT_COUNT const requestedLimit = args.limit || maxLimit let clients: ClientResponse[] = [] let nextToken: string | undefined = undefined if (requestedLimit <= maxLimit) { - return ClientsResponseSchema.parse(await this.copilot.listClients(args)) + return ClientsResponseSchema.parse(await assembly.listClients(args)) } //fetching client data in batches of MAX_LIMIT_CLIENT_COUNT instead of fetching it as a whole. do { const remaining = requestedLimit - clients.length const fetchLimit = Math.min(maxLimit, remaining) - const response = await this.copilot.listClients({ + const response = await assembly.listClients({ ...args, limit: fetchLimit, nextToken, @@ -162,28 +177,33 @@ export class CopilotAPI { async _updateClient(id: string, requestBody: ClientRequest): Promise { console.info('CopilotAPI#_updateClient', this.token) + const assembly = await this.assemblyPromise // @ts-ignore - return ClientResponseSchema.parse(await this.copilot.updateClient({ id, requestBody })) + return ClientResponseSchema.parse(await assembly.updateClient({ id, requestBody })) } async _deleteClient(id: string) { console.info('CopilotAPI#_deleteClient', this.token) - return await this.copilot.deleteClient({ id }) + const assembly = await this.assemblyPromise + return await assembly.deleteClient({ id }) } async _createCompany(requestBody: CompanyCreateRequest) { console.info('CopilotAPI#_createCompany', this.token) - return CompanyResponseSchema.parse(await this.copilot.createCompany({ requestBody })) + const assembly = await this.assemblyPromise + return CompanyResponseSchema.parse(await assembly.createCompany({ requestBody })) } async _getCompany(id: string): Promise { console.info('CopilotAPI#_getCompany', this.token) - return CompanyResponseSchema.parse(await this.copilot.retrieveCompany({ id })) + const assembly = await this.assemblyPromise + return CompanyResponseSchema.parse(await assembly.retrieveCompany({ id })) } async _getCompanies(args: CopilotListArgs & { isPlaceholder?: boolean } = {}): Promise { console.info('CopilotAPI#_getCompanies', this.token) - return CompaniesResponseSchema.parse(await this.copilot.listCompanies(args)) + const assembly = await this.assemblyPromise + return CompaniesResponseSchema.parse(await assembly.listCompanies(args)) } async _getCompanyClients(companyId: string): Promise { @@ -193,28 +213,33 @@ export class CopilotAPI { async _getCustomFields(): Promise { console.info('CopilotAPI#_getCustomFields', this.token) - return CustomFieldResponseSchema.parse(await this.copilot.listCustomFields()) + const assembly = await this.assemblyPromise + return CustomFieldResponseSchema.parse(await assembly.listCustomFields({})) } async _getInternalUsers(args: CopilotListArgs = {}): Promise { console.info('CopilotAPI#_getInternalUsers', this.token) - return InternalUsersResponseSchema.parse(await this.copilot.listInternalUsers(args)) + const assembly = await this.assemblyPromise + return InternalUsersResponseSchema.parse(await assembly.listInternalUsers(args)) } async _getInternalUser(id: string): Promise { console.info('CopilotAPI#_getInternalUser', this.token) - return InternalUsersSchema.parse(await this.copilot.retrieveInternalUser({ id })) + const assembly = await this.assemblyPromise + return InternalUsersSchema.parse(await assembly.retrieveInternalUser({ id })) } async _createNotification(requestBody: NotificationRequestBody): Promise { console.info('CopilotAPI#_createNotification', this.token) - const notification = await this.copilot.createNotification({ requestBody }) + const assembly = await this.assemblyPromise + const notification = await assembly.createNotification({ requestBody }) return NotificationCreatedResponseSchema.parse(notification) } async _markNotificationAsRead(id: string): Promise { console.info('CopilotAPI#_markNotificationAsRead', this.token) - await this.copilot.markNotificationRead({ id }) + const assembly = await this.assemblyPromise + await assembly.markNotificationRead({ id }) } async _bulkMarkNotificationsAsRead(notificationIds: string[], shouldThrowError: boolean = true): Promise { @@ -247,7 +272,8 @@ export class CopilotAPI { async _deleteNotification(id: string): Promise { console.info('CopilotAPI#_deleteNotification', this.token) - await this.copilot.deleteNotification({ id }) + const assembly = await this.assemblyPromise + await assembly.deleteNotification({ id }) } async _bulkDeleteNotifications(notificationIds: string[]): Promise { @@ -313,18 +339,11 @@ export class CopilotAPI { async dispatchWebhook(eventName: DISPATCHABLE_EVENT, { workspaceId, payload }: { workspaceId: string; payload?: object }) { console.info('CopilotAPI#dispatchWebhook', this.token) - const url = `${assemblyApiDomain}/v1/webhooks/${eventName}` - console.info('CopilotAPI#dispatchWebhook | Dispatching webhook to ', url, 'with payload', payload ?? null) + console.info('CopilotAPI#dispatchWebhook | Dispatching webhook', eventName, 'with payload', payload ?? null) try { - await fetch(url, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'x-api-key': `${workspaceId}/${apiKey}`, - }, - body: payload ? JSON.stringify(payload) : null, - }) + const assembly = await this.assemblyPromise + await assembly.sendWebhook(eventName, payload) } catch (e) { console.error(`Failed to dispatch webhook for event ${eventName}`, e) } diff --git a/yarn.lock b/yarn.lock index 599e62c91..b1d9d206a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18,6 +18,16 @@ "@csstools/css-tokenizer" "^3.0.3" lru-cache "^10.4.3" +"@assembly-js/app-bridge@^1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@assembly-js/app-bridge/-/app-bridge-1.1.1.tgz#671b49991bd89538dd8bda2444b0e5ac8e81c1bd" + integrity sha512-NWD8FuRBmLb8+5bMHX8NtepSnIVShmmYoPPD6rHfcFrkN9SKm61/vdCASJnMViWVOjItuKku9QSpn6sOy4L6Og== + +"@assembly-js/node-sdk@^4.0.2": + version "4.0.2" + resolved "https://registry.yarnpkg.com/@assembly-js/node-sdk/-/node-sdk-4.0.2.tgz#d745fedcb1247479757916f6170057909a99cf98" + integrity sha512-mZo+bFTKGYO9YR7m95OlGJpGkF4tYNtxsCABvmNbCTBVXnTfpPjkQMDCYIoE0N3ljN8Iht0+AKBdF+kjTep7fQ== + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.27.1": version "7.27.1" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.27.1.tgz#200f715e66d52a23b221a9435534a91cc13ad5be" @@ -2081,11 +2091,6 @@ dependencies: "@types/pg" "8.6.6" -"@next/env@14.2.32": - version "14.2.32" - resolved "https://registry.yarnpkg.com/@next/env/-/env-14.2.32.tgz#6d1107e2b7cc8649ff3730b8b46deb4e8a6d38fa" - integrity sha512-n9mQdigI6iZ/DF6pCTwMKeWgF2e8lg7qgt5M7HXMLtyhZYMnf/u905M18sSpPmHL9MKp9JHo56C6jrD2EvWxng== - "@next/env@16.1.1": version "16.1.1" resolved "https://registry.yarnpkg.com/@next/env/-/env-16.1.1.tgz#3d06a470efff135746ef609cc02a4996512bd9ab" @@ -2098,86 +2103,41 @@ dependencies: fast-glob "3.3.1" -"@next/swc-darwin-arm64@14.2.32": - version "14.2.32" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.32.tgz#83482a7282df899b73d916e02b02a189771e706c" - integrity sha512-osHXveM70zC+ilfuFa/2W6a1XQxJTvEhzEycnjUaVE8kpUS09lDpiDDX2YLdyFCzoUbvbo5r0X1Kp4MllIOShw== - "@next/swc-darwin-arm64@16.1.1": version "16.1.1" resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.1.tgz#1e7e87fd21fcabce546dfb04fb946ecd9f866917" integrity sha512-JS3m42ifsVSJjSTzh27nW+Igfha3NdBOFScr9C80hHGrWx55pTrVL23RJbqir7k7/15SKlrLHhh/MQzqBBYrQA== -"@next/swc-darwin-x64@14.2.32": - version "14.2.32" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.32.tgz#1a9eb676a014e1fc999251f10288c25a0f81d6d1" - integrity sha512-P9NpCAJuOiaHHpqtrCNncjqtSBi1f6QUdHK/+dNabBIXB2RUFWL19TY1Hkhu74OvyNQEYEzzMJCMQk5agjw1Qg== - "@next/swc-darwin-x64@16.1.1": version "16.1.1" resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.1.tgz#6617d03b96bdffad7bf4df50d4a699faea0d04c3" integrity sha512-hbyKtrDGUkgkyQi1m1IyD3q4I/3m9ngr+V93z4oKHrPcmxwNL5iMWORvLSGAf2YujL+6HxgVvZuCYZfLfb4bGw== -"@next/swc-linux-arm64-gnu@14.2.32": - version "14.2.32" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.32.tgz#7713a49abd555d6f698e766b1631b67d881b4ee4" - integrity sha512-v7JaO0oXXt6d+cFjrrKqYnR2ubrD+JYP7nQVRZgeo5uNE5hkCpWnHmXm9vy3g6foMO8SPwL0P3MPw1c+BjbAzA== - "@next/swc-linux-arm64-gnu@16.1.1": version "16.1.1" resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.1.tgz#d8337e2f4881a80221a1f56ac3f979b665b7e574" integrity sha512-/fvHet+EYckFvRLQ0jPHJCUI5/B56+2DpI1xDSvi80r/3Ez+Eaa2Yq4tJcRTaB1kqj/HrYKn8Yplm9bNoMJpwQ== -"@next/swc-linux-arm64-musl@14.2.32": - version "14.2.32" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.32.tgz#327efdffe97e56f5389a7889cdedbd676fdbb519" - integrity sha512-tA6sIKShXtSJBTH88i0DRd6I9n3ZTirmwpwAqH5zdJoQF7/wlJXR8DkPmKwYl5mFWhEKr5IIa3LfpMW9RRwKmQ== - "@next/swc-linux-arm64-musl@16.1.1": version "16.1.1" resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.1.tgz#6c24392824406a50f27fb9cf4e49362d4666db1c" integrity sha512-MFHrgL4TXNQbBPzkKKur4Fb5ICEJa87HM7fczFs2+HWblM7mMLdco3dvyTI+QmLBU9xgns/EeeINSZD6Ar+oLg== -"@next/swc-linux-x64-gnu@14.2.32": - version "14.2.32" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.32.tgz#a3e7444613d0fe5c8ea4ead08d6a9c818246758c" - integrity sha512-7S1GY4TdnlGVIdeXXKQdDkfDysoIVFMD0lJuVVMeb3eoVjrknQ0JNN7wFlhCvea0hEk0Sd4D1hedVChDKfV2jw== - "@next/swc-linux-x64-gnu@16.1.1": version "16.1.1" resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.1.tgz#f1bd19fc7d119f27c4cf7f51915aef0f119c943f" integrity sha512-20bYDfgOQAPUkkKBnyP9PTuHiJGM7HzNBbuqmD0jiFVZ0aOldz+VnJhbxzjcSabYsnNjMPsE0cyzEudpYxsrUQ== -"@next/swc-linux-x64-musl@14.2.32": - version "14.2.32" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.32.tgz#a2ec5b0a06c740d6740c938b1d4a614f1a13f018" - integrity sha512-OHHC81P4tirVa6Awk6eCQ6RBfWl8HpFsZtfEkMpJ5GjPsJ3nhPe6wKAJUZ/piC8sszUkAgv3fLflgzPStIwfWg== - "@next/swc-linux-x64-musl@16.1.1": version "16.1.1" resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.1.tgz#20c1ea0c98988c337614ce6fda01b82300ff00fd" integrity sha512-9pRbK3M4asAHQRkwaXwu601oPZHghuSC8IXNENgbBSyImHv/zY4K5udBusgdHkvJ/Tcr96jJwQYOll0qU8+fPA== -"@next/swc-win32-arm64-msvc@14.2.32": - version "14.2.32" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.32.tgz#b4d3e47c6b276fc4711deb978d04015d029d198d" - integrity sha512-rORQjXsAFeX6TLYJrCG5yoIDj+NKq31Rqwn8Wpn/bkPNy5rTHvOXkW8mLFonItS7QC6M+1JIIcLe+vOCTOYpvg== - "@next/swc-win32-arm64-msvc@16.1.1": version "16.1.1" resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.1.tgz#9d66be9dc4ebc458d445a7f6ee804f416d5c2daf" integrity sha512-bdfQkggaLgnmYrFkSQfsHfOhk/mCYmjnrbRCGgkMcoOBZ4n+TRRSLmT/CU5SATzlBJ9TpioUyBW/vWFXTqQRiA== -"@next/swc-win32-ia32-msvc@14.2.32": - version "14.2.32" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.32.tgz#d1f1f854a1fbbaeefa8f81271437448653f33494" - integrity sha512-jHUeDPVHrgFltqoAqDB6g6OStNnFxnc7Aks3p0KE0FbwAvRg6qWKYF5mSTdCTxA3axoSAUwxYdILzXJfUwlHhA== - -"@next/swc-win32-x64-msvc@14.2.32": - version "14.2.32" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.32.tgz#8212d681cf6858a9e3204728f8f2b161000683ed" - integrity sha512-2N0lSoU4GjfLSO50wvKpMQgKd4HdI2UHEhQPPPnlgfBJlOgJxkjpkYBqzk08f1gItBB6xF/n+ykso2hgxuydsA== - "@next/swc-win32-x64-msvc@16.1.1": version "16.1.1" resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.1.tgz#96e9e335e2577481dab6fc7a2af48f3e4a28fbdb" @@ -3495,11 +3455,6 @@ "@svgr/plugin-jsx" "8.1.0" "@svgr/plugin-svgo" "8.1.0" -"@swc/counter@^0.1.3": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@swc/counter/-/counter-0.1.3.tgz#cc7463bd02949611c6329596fccd2b0ec782b0e9" - integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ== - "@swc/helpers@0.5.15": version "0.5.15" resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.15.tgz#79efab344c5819ecf83a43f3f9f811fc84b516d7" @@ -3507,14 +3462,6 @@ dependencies: tslib "^2.8.0" -"@swc/helpers@0.5.5": - version "0.5.5" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.5.5.tgz#12689df71bfc9b21c4f4ca00ae55f2f16c8b77c0" - integrity sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A== - dependencies: - "@swc/counter" "^0.1.3" - tslib "^2.4.0" - "@tanstack/react-virtual@^3.13.12": version "3.13.12" resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz#d372dc2783739cc04ec1a728ca8203937687a819" @@ -4608,13 +4555,6 @@ bundle-name@^4.1.0: dependencies: run-applescript "^7.0.0" -busboy@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" - integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== - dependencies: - streamsearch "^1.1.0" - bytes@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" @@ -4893,14 +4833,6 @@ copilot-design-system@^2.3.4: tailwind-merge "^2.3.0" uuid "^9.0.1" -copilot-node-sdk@^3.16.0: - version "3.16.0" - resolved "https://registry.yarnpkg.com/copilot-node-sdk/-/copilot-node-sdk-3.16.0.tgz#c1335c768bd3f9d091c154f930fce9a2633ade8d" - integrity sha512-wqqeJuW+uge39nC1/+cqMWSSgCCNThtCR+JykPYFB2PTj0d0tfROCXofwtqZZccUUzk/xXXkJF53QfweVxmMLg== - dependencies: - isomorphic-fetch "^3.0.0" - next "^14.0.2" - copy-anything@^3.0.2: version "3.0.5" resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-3.0.5.tgz#2d92dce8c498f790fa7ad16b01a1ae5a45b020a0" @@ -6304,7 +6236,7 @@ gopd@^1.0.1, gopd@^1.2.0: resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== -graceful-fs@^4.2.11, graceful-fs@^4.2.9: +graceful-fs@^4.2.9: version "4.2.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== @@ -6827,14 +6759,6 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -isomorphic-fetch@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz#0267b005049046d2421207215d45d6a262b8b8b4" - integrity sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA== - dependencies: - node-fetch "^2.6.1" - whatwg-fetch "^3.4.1" - istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz#2d166c4b0644d43a39f04bf6c2edd1e585f31756" @@ -7771,29 +7695,6 @@ next@16.1.1: "@next/swc-win32-x64-msvc" "16.1.1" sharp "^0.34.4" -next@^14.0.2: - version "14.2.32" - resolved "https://registry.yarnpkg.com/next/-/next-14.2.32.tgz#279b544f0c8ed023c33454ce4d563d3e05c2f3fb" - integrity sha512-fg5g0GZ7/nFc09X8wLe6pNSU8cLWbLRG3TZzPJ1BJvi2s9m7eF991se67wliM9kR5yLHRkyGKU49MMx58s3LJg== - dependencies: - "@next/env" "14.2.32" - "@swc/helpers" "0.5.5" - busboy "1.6.0" - caniuse-lite "^1.0.30001579" - graceful-fs "^4.2.11" - postcss "8.4.31" - styled-jsx "5.1.1" - optionalDependencies: - "@next/swc-darwin-arm64" "14.2.32" - "@next/swc-darwin-x64" "14.2.32" - "@next/swc-linux-arm64-gnu" "14.2.32" - "@next/swc-linux-arm64-musl" "14.2.32" - "@next/swc-linux-x64-gnu" "14.2.32" - "@next/swc-linux-x64-musl" "14.2.32" - "@next/swc-win32-arm64-msvc" "14.2.32" - "@next/swc-win32-ia32-msvc" "14.2.32" - "@next/swc-win32-x64-msvc" "14.2.32" - nextjs-progressloader@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/nextjs-progressloader/-/nextjs-progressloader-1.3.0.tgz#5290df9585d63f09443a17939c232d70cd9bc79f" @@ -7831,7 +7732,7 @@ node-fetch-native@^1.6.6: resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.7.tgz#9d09ca63066cc48423211ed4caf5d70075d76a71" integrity sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q== -node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@^2.6.7: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== @@ -9223,11 +9124,6 @@ stop-iteration-iterator@^1.0.0, stop-iteration-iterator@^1.1.0: es-errors "^1.3.0" internal-slot "^1.1.0" -streamsearch@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" - integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== - string-length@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" @@ -9404,13 +9300,6 @@ styled-components@^6.1.11: stylis "4.3.2" tslib "2.6.2" -styled-jsx@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.1.tgz#839a1c3aaacc4e735fed0781b8619ea5d0009d1f" - integrity sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw== - dependencies: - client-only "0.0.1" - styled-jsx@5.1.6: version "5.1.6" resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.6.tgz#83b90c077e6c6a80f7f5e8781d0f311b2fe41499" @@ -10067,11 +9956,6 @@ whatwg-encoding@^3.1.1: dependencies: iconv-lite "0.6.3" -whatwg-fetch@^3.4.1: - version "3.6.20" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz#580ce6d791facec91d37c72890995a0b48d31c70" - integrity sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg== - whatwg-mimetype@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz#bc1bf94a985dc50388d54a9258ac405c3ca2fc0a"