diff --git a/.changeset/pre.json b/.changeset/pre.json index 758c09d55..507ab9b81 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -12,6 +12,7 @@ "@workflow/tsconfig": "4.0.0", "@workflow/typescript-plugin": "4.0.0", "@workflow/web": "4.0.0", + "@workflow/web-shared": "4.0.0", "workflow": "4.0.0", "@workflow/world": "4.0.0", "@workflow/world-local": "4.0.0", diff --git a/.changeset/warm-files-attack.md b/.changeset/warm-files-attack.md new file mode 100644 index 000000000..92c7466a7 --- /dev/null +++ b/.changeset/warm-files-attack.md @@ -0,0 +1,6 @@ +--- +"@workflow/web-shared": patch +"@workflow/web": patch +--- + +Extract reusable web UI code into shared package diff --git a/packages/nitro/src/builders.ts b/packages/nitro/src/builders.ts index be3d9120c..62a6f8c29 100644 --- a/packages/nitro/src/builders.ts +++ b/packages/nitro/src/builders.ts @@ -71,11 +71,13 @@ export class LocalBuilder extends BaseBuilder { } export function getWorkflowDirs(nitro: Nitro) { - return unique([ - ...(nitro.options.workflow?.dirs ?? []), - join(nitro.options.rootDir, 'workflows'), - ...nitro.options.scanDirs.map((dir) => join(dir, 'workflows')), - ].map((dir) => resolve(nitro.options.rootDir, dir))).sort(); + return unique( + [ + ...(nitro.options.workflow?.dirs ?? []), + join(nitro.options.rootDir, 'workflows'), + ...nitro.options.scanDirs.map((dir) => join(dir, 'workflows')), + ].map((dir) => resolve(nitro.options.rootDir, dir)) + ).sort(); } function unique(array: T[]): T[] { diff --git a/packages/nitro/src/types.ts b/packages/nitro/src/types.ts index 37e4d449e..7b9a22cbc 100644 --- a/packages/nitro/src/types.ts +++ b/packages/nitro/src/types.ts @@ -1,4 +1,4 @@ - export interface ModuleOptions { +export interface ModuleOptions { /** * Directories to scan for workflows and steps. * diff --git a/packages/nitro/test/dirs.test.ts b/packages/nitro/test/dirs.test.ts index e37b3ce97..233f86b8a 100644 --- a/packages/nitro/test/dirs.test.ts +++ b/packages/nitro/test/dirs.test.ts @@ -3,14 +3,14 @@ import { describe, expect, test } from 'vitest'; import { getWorkflowDirs } from '../src/builders.ts'; const nitroMock = (dirs: string[]) => { - return ({ - options: { - rootDir: '/root', - scanDirs: ['/root/server/'], - workflow: { dirs: dirs }, - }, - }) as unknown as Nitro; -} + return { + options: { + rootDir: '/root', + scanDirs: ['/root/server/'], + workflow: { dirs: dirs }, + }, + } as unknown as Nitro; +}; describe('nitro:getWorkflowDirs', () => { test('default dirs', () => { @@ -19,7 +19,9 @@ describe('nitro:getWorkflowDirs', () => { }); test('custom dirs', () => { - const result = getWorkflowDirs(nitroMock(['./relative/dir1', '/custom/dir2'])); + const result = getWorkflowDirs( + nitroMock(['./relative/dir1', '/custom/dir2']) + ); expect(result).toEqual([ '/custom/dir2', '/root/relative/dir1', diff --git a/packages/web-shared/.gitignore b/packages/web-shared/.gitignore new file mode 100644 index 000000000..e3a7542e0 --- /dev/null +++ b/packages/web-shared/.gitignore @@ -0,0 +1,42 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts +.env*.local diff --git a/packages/web-shared/LICENSE.md b/packages/web-shared/LICENSE.md new file mode 120000 index 000000000..f0608a63a --- /dev/null +++ b/packages/web-shared/LICENSE.md @@ -0,0 +1 @@ +../../LICENSE.md \ No newline at end of file diff --git a/packages/web-shared/README.md b/packages/web-shared/README.md new file mode 100644 index 000000000..0b1c9d3d3 --- /dev/null +++ b/packages/web-shared/README.md @@ -0,0 +1,74 @@ +# @workflow/web-shared + +Workflow Observability tools for NextJS. See [Workflow DevKit](https://useworkflow.dev/docs/observability) for more information. + +## Usage + +This package contains client and server code to interact with the Workflow API. +You can use it like so to display your own runs list: + +```tsx +import { useWorkflowRuns } from '@workflow/web-shared'; + +export default function MyRunsList() { + const { + data, + error, + nextPage, + previousPage, + hasNextPage, + hasPreviousPage, + reload, + pageInfo, + } = useWorkflowRuns(env, { + sortOrder, + workflowName: workflowNameFilter === 'all' ? undefined : workflowNameFilter, + status: status === 'all' ? undefined : status, + }); + + // Shows an interactive trace viewer for the given run + return
{runs.map((run) => ( +
+ {run.workflowName} + {run.status} + {run.startedAt} + {run.completedAt} +
+ ))}
; +} +``` + +It also comes with a pre-styled interactive trace viewer that you can use to display the trace for a given run: + +```tsx +import { RunTraceView } from '@workflow/web-shared'; + +export default function MyRunDetailView({ env, runId }: { env: EnvMap, runId: string }) { + // ... your other code + + // Shows an interactive trace viewer for the given run + return ; +} +``` + +## Environment Variables + +For API calls to work, you'll need to pass the same environment variables that are used by the Workflow CLI. +See `npx workflow inspect --help` for more information. + +If you're deploying this as part of your Vercel NextJS app, setting `WORKFLOW_TARGET_WORLD` to `vercel` is enough +to infer your other project details from the Vercel environment variables. + +## Styling + +In order for tailwind classes to be picked up correctly, you might need to configure your NextJS app +to use the correct CSS processor. E.g. if you're using PostCSS with TailwindCSS, you can do the following: + +```tsx +// postcss.config.mjs in your NextJS app +const config = { + plugins: ['@tailwindcss/postcss'], +}; + +export default config; +``` diff --git a/packages/web-shared/package.json b/packages/web-shared/package.json new file mode 100644 index 000000000..ccd9511c6 --- /dev/null +++ b/packages/web-shared/package.json @@ -0,0 +1,59 @@ +{ + "name": "@workflow/web-shared", + "description": "Shared components for Workflow Observability UI", + "version": "4.0.1-beta.4", + "private": false, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "license": "MIT", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + }, + "./server": { + "types": "./dist/api/workflow-server-actions.d.ts", + "default": "./dist/api/workflow-server-actions.js" + } + }, + "repository": { + "type": "git", + "url": "https://github.com/vercel/workflow.git", + "directory": "packages/web-shared" + }, + "scripts": { + "build": "tsc && cp -r src/trace-viewer/*.css dist/trace-viewer/", + "dev": "tsc --watch", + "clean": "tsc --build --clean && rm -r dist ||:", + "typecheck": "tsc --noEmit", + "lint": "biome check", + "format": "biome format --write" + }, + "dependencies": { + "@tailwindcss/postcss": "^4", + "@workflow/core": "workspace:*", + "@workflow/world": "workspace:*", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "date-fns": "^4.1.0", + "lucide-react": "^0.469.0", + "react": "19.1.0", + "react-dom": "19.1.0", + "sonner": "^2.0.7", + "swr": "^2.3.6", + "tailwind-merge": "^2.5.5", + "tailwindcss": "^4" + }, + "devDependencies": { + "@biomejs/biome": "catalog:", + "@types/node": "catalog:", + "@types/react": "^19", + "@types/react-dom": "^19", + "typescript": "catalog:", + "@workflow/tsconfig": "workspace:*" + } +} diff --git a/packages/web-shared/postcss.config.mjs b/packages/web-shared/postcss.config.mjs new file mode 100644 index 000000000..ba720fe55 --- /dev/null +++ b/packages/web-shared/postcss.config.mjs @@ -0,0 +1,5 @@ +const config = { + plugins: ['@tailwindcss/postcss'], +}; + +export default config; diff --git a/packages/web/src/workflow-trace-viewer/api/workflow-api-client.ts b/packages/web-shared/src/api/workflow-api-client.ts similarity index 99% rename from packages/web/src/workflow-trace-viewer/api/workflow-api-client.ts rename to packages/web-shared/src/api/workflow-api-client.ts index 93ed0a76a..73d2040a0 100644 --- a/packages/web/src/workflow-trace-viewer/api/workflow-api-client.ts +++ b/packages/web-shared/src/api/workflow-api-client.ts @@ -8,7 +8,7 @@ import type { WorkflowRunStatus, } from '@workflow/world'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { getPaginationDisplay } from '@/lib/utils'; +import { getPaginationDisplay } from '../lib/utils'; import type { EnvMap, ServerActionError } from './workflow-server-actions'; import { cancelRun as cancelRunServerAction, diff --git a/packages/web/src/workflow-trace-viewer/api/workflow-server-actions.ts b/packages/web-shared/src/api/workflow-server-actions.ts similarity index 97% rename from packages/web/src/workflow-trace-viewer/api/workflow-server-actions.ts rename to packages/web-shared/src/api/workflow-server-actions.ts index 6dc531321..40e075257 100644 --- a/packages/web/src/workflow-trace-viewer/api/workflow-server-actions.ts +++ b/packages/web-shared/src/api/workflow-server-actions.ts @@ -174,7 +174,9 @@ export async function fetchRun( try { const world = getWorldFromEnv(worldEnv); const run = await world.runs.get(runId, { resolveData }); - return createResponse(hydrate(run as WorkflowRun)); + const hydratedRun = hydrate(run as WorkflowRun); + console.log('hydratedRun', hydratedRun.input); + return createResponse(hydratedRun); } catch (error) { console.error('Failed to fetch run:', error); return { @@ -416,11 +418,15 @@ export async function recreateRun( try { const world = getWorldFromEnv({ ...worldEnv }); const run = await world.runs.get(runId); - const args = run.input; + const hydratedRun = hydrate(run as WorkflowRun); const deploymentId = run.deploymentId; - const newRun = await start({ workflowId: run.workflowName }, args, { - deploymentId, - }); + const newRun = await start( + { workflowId: run.workflowName }, + hydratedRun.input, + { + deploymentId, + } + ); return createResponse(newRun.runId); } catch (error) { console.error('Failed to start run:', error); diff --git a/packages/web-shared/src/components/ui/alert.tsx b/packages/web-shared/src/components/ui/alert.tsx new file mode 100644 index 000000000..b46e37cad --- /dev/null +++ b/packages/web-shared/src/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import { cva, type VariantProps } from 'class-variance-authority'; +import * as React from 'react'; + +import { cn } from '../../lib/utils'; + +const alertVariants = cva( + 'relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground', + { + variants: { + variant: { + default: 'bg-background text-foreground', + destructive: + 'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive', + }, + }, + defaultVariants: { + variant: 'default', + }, + } +); + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)); +Alert.displayName = 'Alert'; + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +AlertTitle.displayName = 'AlertTitle'; + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +AlertDescription.displayName = 'AlertDescription'; + +export { Alert, AlertTitle, AlertDescription }; diff --git a/packages/web/src/workflow-trace-viewer/index.ts b/packages/web-shared/src/index.ts similarity index 86% rename from packages/web/src/workflow-trace-viewer/index.ts rename to packages/web-shared/src/index.ts index 75789d6e2..60e9d2465 100644 --- a/packages/web/src/workflow-trace-viewer/index.ts +++ b/packages/web-shared/src/index.ts @@ -5,5 +5,6 @@ export { export type { Event, Hook, Step, WorkflowRun } from '@workflow/world'; export * from './api/workflow-api-client'; +export { RunTraceView } from './run-trace-view'; export type { Span, SpanEvent } from './trace-viewer/types'; export { WorkflowTraceViewer } from './workflow-trace-view'; diff --git a/packages/web-shared/src/lib/utils.ts b/packages/web-shared/src/lib/utils.ts new file mode 100644 index 000000000..d6b142277 --- /dev/null +++ b/packages/web-shared/src/lib/utils.ts @@ -0,0 +1,24 @@ +import { type ClassValue, clsx } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} + +/** + * Returns a formatted pagination display string + * @param currentPage - The current page number + * @param totalPages - The total number of pages visited so far + * @param hasMore - Whether there are more pages available + * @returns Formatted string like "Page 1 of 3+" or "Page 2 of 2" + */ +export function getPaginationDisplay( + currentPage: number, + totalPages: number, + hasMore: boolean +): string { + if (hasMore) { + return `Page ${currentPage} of ${totalPages}+`; + } + return `Page ${currentPage} of ${totalPages}`; +} diff --git a/packages/web-shared/src/run-trace-view.tsx b/packages/web-shared/src/run-trace-view.tsx new file mode 100644 index 000000000..03fc6cfc8 --- /dev/null +++ b/packages/web-shared/src/run-trace-view.tsx @@ -0,0 +1,56 @@ +'use client'; + +import type { WorkflowRun } from '@workflow/world'; +import { AlertCircle } from 'lucide-react'; +import { useWorkflowTraceViewerData } from './api/workflow-api-client'; +import type { EnvMap } from './api/workflow-server-actions'; +import { WorkflowTraceViewer } from './workflow-trace-view'; + +interface RunTraceViewProps { + env: EnvMap; + runId: string; +} + +export function RunTraceView({ env, runId }: RunTraceViewProps) { + // Fetch all run data with live updates + const { + run: runData, + steps: allSteps, + hooks: allHooks, + events: allEvents, + loading, + error, + } = useWorkflowTraceViewerData(env, runId, { live: true }); + const run = runData ?? ({} as WorkflowRun); + + if (error && !runData) { + return ( +
+ +

Error loading workflow run

+

{error.message}

+
+ ); + } + + return ( +
+
+ {loading && ( +
+
+
+ )} + +
+
+ ); +} diff --git a/packages/web/src/workflow-trace-viewer/sidebar/attribute-panel.tsx b/packages/web-shared/src/sidebar/attribute-panel.tsx similarity index 98% rename from packages/web/src/workflow-trace-viewer/sidebar/attribute-panel.tsx rename to packages/web-shared/src/sidebar/attribute-panel.tsx index b59fdaa28..b0fd62716 100644 --- a/packages/web/src/workflow-trace-viewer/sidebar/attribute-panel.tsx +++ b/packages/web-shared/src/sidebar/attribute-panel.tsx @@ -4,7 +4,7 @@ import { parseStepName, parseWorkflowName } from '@workflow/core/parse-name'; import type { Event, Hook, Step, WorkflowRun } from '@workflow/world'; import { AlertCircle } from 'lucide-react'; import type { ReactNode } from 'react'; -import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; +import { Alert, AlertDescription, AlertTitle } from '../components/ui/alert'; import { DetailCard } from './detail-card'; const JsonBlock = (value: unknown) => { diff --git a/packages/web/src/workflow-trace-viewer/sidebar/detail-card.tsx b/packages/web-shared/src/sidebar/detail-card.tsx similarity index 100% rename from packages/web/src/workflow-trace-viewer/sidebar/detail-card.tsx rename to packages/web-shared/src/sidebar/detail-card.tsx diff --git a/packages/web/src/workflow-trace-viewer/sidebar/events-list.tsx b/packages/web-shared/src/sidebar/events-list.tsx similarity index 55% rename from packages/web/src/workflow-trace-viewer/sidebar/events-list.tsx rename to packages/web-shared/src/sidebar/events-list.tsx index 0d71f6022..8ac480f11 100644 --- a/packages/web/src/workflow-trace-viewer/sidebar/events-list.tsx +++ b/packages/web-shared/src/sidebar/events-list.tsx @@ -3,11 +3,11 @@ import { AlertCircle } from 'lucide-react'; import { useCallback } from 'react'; import useSWR from 'swr'; -import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; import { type EnvMap, fetchEventsByCorrelationId, } from '../api/workflow-server-actions'; +import { Alert, AlertDescription, AlertTitle } from '../components/ui/alert'; import type { SpanEvent } from '../trace-viewer/types'; import { convertEventsToSpanEvents } from '../workflow-traces/trace-span-construction'; import { AttributeBlock } from './attribute-panel'; @@ -73,51 +73,49 @@ export function EventsList({
No events found
)} {displayData.length > 0 && !eventError ? ( - <> -
- {displayData.map((event, index) => ( - - - {event.name} - {' '} - -{' '} - - {new Date( - event.timestamp[0] * 1000 + event.timestamp[1] / 1e6 - ).toLocaleString()} - - - } - > -
- {Object.entries(event.attributes) - .filter(([key]) => key !== 'eventData') - .map(([key, value]) => ( - - ))} -
- {eventError &&
Error loading event data
} - {!eventError && - !eventsLoading && - event.attributes.eventData && ( - - )} -
+
+ {displayData.map((event, index) => ( + + + {event.name} + {' '} + -{' '} + + {new Date( + event.timestamp[0] * 1000 + event.timestamp[1] / 1e6 + ).toLocaleString()} + + + } + > +
+ {Object.entries(event.attributes) + .filter(([key]) => key !== 'eventData') + .map(([key, value]) => ( + + ))} +
+ {eventError &&
Error loading event data
} + {!eventError && + !eventsLoading && + event.attributes.eventData && ( + + )}
- - ))} -
- +
+ + ))} +
) : null}
); diff --git a/packages/web/src/workflow-trace-viewer/sidebar/workflow-detail-panel.tsx b/packages/web-shared/src/sidebar/workflow-detail-panel.tsx similarity index 100% rename from packages/web/src/workflow-trace-viewer/sidebar/workflow-detail-panel.tsx rename to packages/web-shared/src/sidebar/workflow-detail-panel.tsx diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/README.md b/packages/web-shared/src/trace-viewer/README.md similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/README.md rename to packages/web-shared/src/trace-viewer/README.md diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/components/map.tsx b/packages/web-shared/src/trace-viewer/components/map.tsx similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/components/map.tsx rename to packages/web-shared/src/trace-viewer/components/map.tsx diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/components/markers.tsx b/packages/web-shared/src/trace-viewer/components/markers.tsx similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/components/markers.tsx rename to packages/web-shared/src/trace-viewer/components/markers.tsx diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/components/node.tsx b/packages/web-shared/src/trace-viewer/components/node.tsx similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/components/node.tsx rename to packages/web-shared/src/trace-viewer/components/node.tsx diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/components/search-input.tsx b/packages/web-shared/src/trace-viewer/components/search-input.tsx similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/components/search-input.tsx rename to packages/web-shared/src/trace-viewer/components/search-input.tsx diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/components/search.tsx b/packages/web-shared/src/trace-viewer/components/search.tsx similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/components/search.tsx rename to packages/web-shared/src/trace-viewer/components/search.tsx diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/components/span-detail-panel.tsx b/packages/web-shared/src/trace-viewer/components/span-detail-panel.tsx similarity index 99% rename from packages/web/src/workflow-trace-viewer/trace-viewer/components/span-detail-panel.tsx rename to packages/web-shared/src/trace-viewer/components/span-detail-panel.tsx index 4506d1bba..4b43e6f38 100644 --- a/packages/web/src/workflow-trace-viewer/trace-viewer/components/span-detail-panel.tsx +++ b/packages/web-shared/src/trace-viewer/components/span-detail-panel.tsx @@ -97,7 +97,7 @@ const getGroupedAttributes = ( }; // biome-ignore lint/correctness/noUnusedFunctionParameters: ignored using `--suppress` -const getAncestors = (root: RootNode, start: SpanNode | null): SpanNode[] => { +const getAncestors = (_root: RootNode, start: SpanNode | null): SpanNode[] => { const result: SpanNode[] = []; if (!start) return result; diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/components/ui.tsx b/packages/web-shared/src/trace-viewer/components/ui.tsx similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/components/ui.tsx rename to packages/web-shared/src/trace-viewer/components/ui.tsx diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/components/zoom-button.tsx b/packages/web-shared/src/trace-viewer/components/zoom-button.tsx similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/components/zoom-button.tsx rename to packages/web-shared/src/trace-viewer/components/zoom-button.tsx diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/components/zoom-icons.tsx b/packages/web-shared/src/trace-viewer/components/zoom-icons.tsx similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/components/zoom-icons.tsx rename to packages/web-shared/src/trace-viewer/components/zoom-icons.tsx diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/context.tsx b/packages/web-shared/src/trace-viewer/context.tsx similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/context.tsx rename to packages/web-shared/src/trace-viewer/context.tsx diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/index.tsx b/packages/web-shared/src/trace-viewer/index.tsx similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/index.tsx rename to packages/web-shared/src/trace-viewer/index.tsx diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/modules.d.ts b/packages/web-shared/src/trace-viewer/modules.d.ts similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/modules.d.ts rename to packages/web-shared/src/trace-viewer/modules.d.ts diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/trace-viewer.module.css b/packages/web-shared/src/trace-viewer/trace-viewer.module.css similarity index 88% rename from packages/web/src/workflow-trace-viewer/trace-viewer/trace-viewer.module.css rename to packages/web-shared/src/trace-viewer/trace-viewer.module.css index 7023c4ce3..1ce4876c2 100644 --- a/packages/web/src/workflow-trace-viewer/trace-viewer/trace-viewer.module.css +++ b/packages/web-shared/src/trace-viewer/trace-viewer.module.css @@ -1164,3 +1164,112 @@ border-left: none; } } + +/* Workflow-specific styles */ + +/* Step statuses */ +.spanRunning, +.spanPending { + --span-background: var(--ds-blue-200); + --span-border: var(--ds-blue-500); + --span-line: var(--ds-blue-400); + --span-text: var(--ds-gray-1000); + --span-secondary: var(--ds-blue-900); +} + +.spanCompleted { + --span-background: var(--ds-green-200); + --span-border: var(--ds-green-500); + --span-line: var(--ds-green-400); + --span-text: var(--ds-gray-1000); + --span-secondary: var(--ds-green-900); +} + +.spanCancelled, +.spanPaused { + --span-background: var(--ds-amber-200); + --span-border: var(--ds-amber-500); + --span-line: var(--ds-amber-400); + --span-text: var(--ds-gray-1000); + --span-secondary: var(--ds-amber-900); +} + +.spanFailed { + --span-background: var(--ds-red-200); + --span-border: var(--ds-red-500); + --span-line: var(--ds-red-400); + --span-text: var(--ds-gray-1000); + --span-secondary: var(--ds-red-900); +} + +/* Striped background for pending status */ +.spanPendingStriped { + --span-background: repeating-linear-gradient( + 45deg, + var(--ds-blue-200), + var(--ds-blue-200) 10px, + color-mix(in srgb, var(--ds-blue-200) 70%, white) 10px, + color-mix(in srgb, var(--ds-blue-200) 70%, white) 20px + ); + --span-border: var(--ds-blue-500); + --span-line: var(--ds-blue-400); + --span-text: var(--ds-gray-1000); + --span-secondary: var(--ds-blue-900); +} + +/* Sleep step - light yellow/orange */ +.spanSleep { + --span-background: var(--ds-amber-200); + --span-border: var(--ds-amber-500); + --span-line: var(--ds-amber-400); + --span-text: var(--ds-gray-1000); + --span-secondary: var(--ds-amber-900); +} + +/* Hooks - gray color scheme */ +.spanHook { + --span-background: var(--ds-gray-200); + --span-border: var(--ds-gray-500); + --span-line: var(--ds-gray-400); + --span-text: var(--ds-gray-1000); + --span-secondary: var(--ds-gray-900); +} + +/* Event markers */ + +/* Failure events - Red */ +.eventFailed { + --event-color: var(--ds-red-600); + --span-background: var(--ds-red-100); + --span-border: var(--ds-red-500); + --span-text: var(--ds-red-900); + --span-secondary: var(--ds-red-700); +} + +/* Retry events - Orange/Yellow */ +.eventRetrying { + --event-color: var(--ds-amber-600); + --span-background: var(--ds-amber-100); + --span-border: var(--ds-amber-500); + --span-text: var(--ds-amber-900); + --span-secondary: var(--ds-amber-700); +} + +/* Webhook-related events - Purple */ +.eventHook { + --event-color: var(--ds-purple-600); + --span-background: var(--ds-purple-100); + --span-border: var(--ds-purple-500); + --span-text: var(--ds-purple-900); + --span-secondary: var(--ds-purple-700); +} + +/* Default event - Blue */ +.eventDefault { + --event-color: var(--ds-blue-600); + --span-background: var(--ds-blue-100); + --span-border: var(--ds-blue-500); + --span-text: var(--ds-blue-900); + --span-secondary: var(--ds-blue-700); +} + diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/trace-viewer.tsx b/packages/web-shared/src/trace-viewer/trace-viewer.tsx similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/trace-viewer.tsx rename to packages/web-shared/src/trace-viewer/trace-viewer.tsx diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/types.ts b/packages/web-shared/src/trace-viewer/types.ts similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/types.ts rename to packages/web-shared/src/trace-viewer/types.ts diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/util/constants.ts b/packages/web-shared/src/trace-viewer/util/constants.ts similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/util/constants.ts rename to packages/web-shared/src/trace-viewer/util/constants.ts diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/util/scrollbar-width.ts b/packages/web-shared/src/trace-viewer/util/scrollbar-width.ts similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/util/scrollbar-width.ts rename to packages/web-shared/src/trace-viewer/util/scrollbar-width.ts diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/util/timing.ts b/packages/web-shared/src/trace-viewer/util/timing.ts similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/util/timing.ts rename to packages/web-shared/src/trace-viewer/util/timing.ts diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/util/tree.ts b/packages/web-shared/src/trace-viewer/util/tree.ts similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/util/tree.ts rename to packages/web-shared/src/trace-viewer/util/tree.ts diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/util/use-immediate-style.ts b/packages/web-shared/src/trace-viewer/util/use-immediate-style.ts similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/util/use-immediate-style.ts rename to packages/web-shared/src/trace-viewer/util/use-immediate-style.ts diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/util/use-streaming-spans.ts b/packages/web-shared/src/trace-viewer/util/use-streaming-spans.ts similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/util/use-streaming-spans.ts rename to packages/web-shared/src/trace-viewer/util/use-streaming-spans.ts diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/util/use-trackpad-zoom.tsx b/packages/web-shared/src/trace-viewer/util/use-trackpad-zoom.tsx similarity index 100% rename from packages/web/src/workflow-trace-viewer/trace-viewer/util/use-trackpad-zoom.tsx rename to packages/web-shared/src/trace-viewer/util/use-trackpad-zoom.tsx diff --git a/packages/web/src/workflow-trace-viewer/trace-viewer/worker.ts b/packages/web-shared/src/trace-viewer/worker.ts similarity index 99% rename from packages/web/src/workflow-trace-viewer/trace-viewer/worker.ts rename to packages/web-shared/src/trace-viewer/worker.ts index 0374c7faa..777465175 100644 --- a/packages/web/src/workflow-trace-viewer/trace-viewer/worker.ts +++ b/packages/web-shared/src/trace-viewer/worker.ts @@ -82,7 +82,6 @@ const calculateSpanPositions = (root: RootNode): void => { } }; -// @ts-expect-error const ATTR_FILTER_REGEX = /(?<=^|\s)(?(?[\w.]+):(?\S*))/g; const filterSpans = (root: RootNode, filter: string): void => { diff --git a/packages/web/src/workflow-trace-viewer/workflow-trace-view.tsx b/packages/web-shared/src/workflow-trace-view.tsx similarity index 100% rename from packages/web/src/workflow-trace-viewer/workflow-trace-view.tsx rename to packages/web-shared/src/workflow-trace-view.tsx diff --git a/packages/web/src/workflow-trace-viewer/workflow-traces/event-colors.ts b/packages/web-shared/src/workflow-traces/event-colors.ts similarity index 100% rename from packages/web/src/workflow-trace-viewer/workflow-traces/event-colors.ts rename to packages/web-shared/src/workflow-traces/event-colors.ts diff --git a/packages/web/src/workflow-trace-viewer/workflow-traces/trace-colors.ts b/packages/web-shared/src/workflow-traces/trace-colors.ts similarity index 97% rename from packages/web/src/workflow-trace-viewer/workflow-traces/trace-colors.ts rename to packages/web-shared/src/workflow-traces/trace-colors.ts index a976ed56e..79bb3a611 100644 --- a/packages/web/src/workflow-trace-viewer/workflow-traces/trace-colors.ts +++ b/packages/web-shared/src/workflow-traces/trace-colors.ts @@ -3,8 +3,8 @@ */ import type { Step, WorkflowRun } from '@workflow/world'; +import styles from '../trace-viewer/trace-viewer.module.css'; import type { SpanNode, SpanNodeEvent } from '../trace-viewer/types'; -import styles from './workflow-trace.module.css'; /** * Get the CSS class name for a workflow entity based on its status diff --git a/packages/web/src/workflow-trace-viewer/workflow-traces/trace-span-construction.ts b/packages/web-shared/src/workflow-traces/trace-span-construction.ts similarity index 100% rename from packages/web/src/workflow-trace-viewer/workflow-traces/trace-span-construction.ts rename to packages/web-shared/src/workflow-traces/trace-span-construction.ts diff --git a/packages/web/src/workflow-trace-viewer/workflow-traces/trace-time-utils.ts b/packages/web-shared/src/workflow-traces/trace-time-utils.ts similarity index 100% rename from packages/web/src/workflow-trace-viewer/workflow-traces/trace-time-utils.ts rename to packages/web-shared/src/workflow-traces/trace-time-utils.ts diff --git a/packages/web-shared/tsconfig.json b/packages/web-shared/tsconfig.json new file mode 100644 index 000000000..fe8434ba9 --- /dev/null +++ b/packages/web-shared/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "@workflow/tsconfig/base.json", + "compilerOptions": { + "outDir": "dist", + "target": "es2022", + "lib": ["es2022", "dom", "dom.iterable", "webworker"], + "jsx": "preserve", + "module": "esnext", + "moduleResolution": "bundler", + "types": ["node", "react", "react-dom"] + }, + "include": ["src"], + "exclude": ["node_modules", "**/*.test.ts"] +} diff --git a/packages/web-shared/turbo.json b/packages/web-shared/turbo.json new file mode 100644 index 000000000..aa43d50d9 --- /dev/null +++ b/packages/web-shared/turbo.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://turborepo.org/schema.json", + "extends": ["//"], + "tasks": { + "build": { + "outputs": ["dist/**"] + } + } +} diff --git a/packages/web/package.json b/packages/web/package.json index 8737543ba..6210dd9af 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -42,16 +42,15 @@ "@types/react-dom": "^19", "@workflow/core": "workspace:*", "@workflow/world": "workspace:*", + "@workflow/web-shared": "workspace:*", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", - "json-colorizer": "^3.0.1", "lucide-react": "^0.469.0", "next-themes": "^0.4.6", "react": "19.1.0", "react-dom": "19.1.0", "sonner": "^2.0.7", - "swr": "^2.3.6", "tailwind-merge": "^2.5.5", "tailwindcss": "^4", "typescript": "catalog:" diff --git a/packages/web/src/app/globals.css b/packages/web/src/app/globals.css index e56921ca2..eba38b97a 100644 --- a/packages/web/src/app/globals.css +++ b/packages/web/src/app/globals.css @@ -1,5 +1,8 @@ @import "tailwindcss"; +/* Scan web-shared package for Tailwind classes */ +@source "../../../web-shared/src"; + :root { --background: 0 0% 100%; --foreground: 0 0% 3.9%; diff --git a/packages/web/src/components/display-utils/json-view.tsx b/packages/web/src/components/display-utils/json-view.tsx deleted file mode 100644 index 087de3054..000000000 --- a/packages/web/src/components/display-utils/json-view.tsx +++ /dev/null @@ -1,75 +0,0 @@ -'use client'; - -import * as jsonColorizer from 'json-colorizer'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; - -interface JsonViewProps { - title?: string; - data: unknown; - showCard?: boolean; -} - -const MAX_PROPERTY_LENGTH = 512; -const TRUNCATE_PROPERTIES = ['input', 'output', 'eventDetails']; - -function shouldTruncate(key: string, value: unknown): boolean { - if (!TRUNCATE_PROPERTIES.includes(key)) { - return false; - } - const stringified = JSON.stringify(value); - return stringified.length > MAX_PROPERTY_LENGTH; -} - -function truncateData(obj: unknown): unknown { - if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) { - return obj; - } - - const result: Record = {}; - - for (const [key, value] of Object.entries(obj)) { - result[key] = shouldTruncate(key, value) ? '' : value; - } - - return result; -} - -export function JsonView({ title, data, showCard = true }: JsonViewProps) { - const formatJson = (obj: unknown): string => { - const truncated = truncateData(obj); - return JSON.stringify(truncated, null, 2); - }; - - const highlightJson = (json: string): string => { - // Skip coloring if the JSON is too large - if (json.length > 10000) { - return json; - } - return jsonColorizer.colorize(json); - }; - - const content = ( -
-
-    
- ); - - if (!showCard) { - return content; - } - - return ( - - {title && ( - - {title} - - )} - {content} - - ); -} diff --git a/packages/web/src/components/hooks-table.tsx b/packages/web/src/components/hooks-table.tsx index 29435a567..90d48edae 100644 --- a/packages/web/src/components/hooks-table.tsx +++ b/packages/web/src/components/hooks-table.tsx @@ -1,5 +1,7 @@ 'use client'; +import { getErrorMessage, useWorkflowHooks } from '@workflow/web-shared'; +import { fetchEventsByCorrelationId } from '@workflow/web-shared/server'; import type { Event, Hook } from '@workflow/world'; import { AlertCircle, @@ -26,9 +28,6 @@ import { } from '@/components/ui/tooltip'; import { worldConfigToEnvMap } from '@/lib/config'; import type { WorldConfig } from '@/lib/config-world'; -import { useWorkflowHooks } from '@/workflow-trace-viewer'; -import { getErrorMessage } from '@/workflow-trace-viewer/api/workflow-api-client'; -import { fetchEventsByCorrelationId } from '@/workflow-trace-viewer/api/workflow-server-actions'; import { RelativeTime } from './display-utils/relative-time'; import { TableSkeleton } from './display-utils/table-skeleton'; diff --git a/packages/web/src/components/run-detail-view.tsx b/packages/web/src/components/run-detail-view.tsx index e421ee894..e6c798520 100644 --- a/packages/web/src/components/run-detail-view.tsx +++ b/packages/web/src/components/run-detail-view.tsx @@ -1,6 +1,13 @@ 'use client'; import { parseWorkflowName } from '@workflow/core/parse-name'; +import { + cancelRun, + recreateRun, + useWorkflowTraceViewerData, + type WorkflowRun, + WorkflowTraceViewer, +} from '@workflow/web-shared'; import { AlertCircle } from 'lucide-react'; import { useRouter } from 'next/navigation'; import { useMemo, useState } from 'react'; @@ -18,13 +25,6 @@ import { } from '@/components/ui/alert-dialog'; import { buildUrlWithConfig, worldConfigToEnvMap } from '@/lib/config'; import type { WorldConfig } from '@/lib/config-world'; -import { - cancelRun, - recreateRun, - useWorkflowTraceViewerData, - type WorkflowRun, - WorkflowTraceViewer, -} from '@/workflow-trace-viewer'; import { BackLink } from './display-utils/back-link'; import { CancelButton } from './display-utils/cancel-button'; import { CopyableText } from './display-utils/copyable-text'; diff --git a/packages/web/src/components/runs-table.tsx b/packages/web/src/components/runs-table.tsx index 04392cf67..e3ceae23b 100644 --- a/packages/web/src/components/runs-table.tsx +++ b/packages/web/src/components/runs-table.tsx @@ -1,6 +1,7 @@ 'use client'; import { parseWorkflowName } from '@workflow/core/parse-name'; +import { getErrorMessage, useWorkflowRuns } from '@workflow/web-shared'; import type { WorkflowRunStatus } from '@workflow/world'; import { AlertCircle, @@ -37,8 +38,6 @@ import { } from '@/components/ui/tooltip'; import { worldConfigToEnvMap } from '@/lib/config'; import type { WorldConfig } from '@/lib/config-world'; -import { useWorkflowRuns } from '@/workflow-trace-viewer'; -import { getErrorMessage } from '@/workflow-trace-viewer/api/workflow-api-client'; import { RelativeTime } from './display-utils/relative-time'; import { StatusBadge } from './display-utils/status-badge'; import { TableSkeleton } from './display-utils/table-skeleton'; diff --git a/packages/web/src/components/stream-detail-view.tsx b/packages/web/src/components/stream-detail-view.tsx index 8e5f14aac..4a4db95e0 100644 --- a/packages/web/src/components/stream-detail-view.tsx +++ b/packages/web/src/components/stream-detail-view.tsx @@ -1,11 +1,11 @@ 'use client'; +import { readStream } from '@workflow/web-shared'; +import type { EnvMap } from '@workflow/web-shared/server'; import { useParams } from 'next/navigation'; import { useEffect, useRef, useState } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { buildUrlWithConfig } from '@/lib/config'; -import { readStream } from '@/workflow-trace-viewer'; -import type { EnvMap } from '@/workflow-trace-viewer/api/workflow-server-actions'; import { BackLink } from './display-utils/back-link'; interface StreamDetailViewProps { diff --git a/packages/web/src/lib/config.ts b/packages/web/src/lib/config.ts index 80c4789fc..93ca795f0 100644 --- a/packages/web/src/lib/config.ts +++ b/packages/web/src/lib/config.ts @@ -1,9 +1,9 @@ 'use client'; +import type { EnvMap } from '@workflow/web-shared/server'; import { usePathname, useRouter, useSearchParams } from 'next/navigation'; import { useMemo } from 'react'; import type { WorldConfig } from '@/lib/config-world'; -import type { EnvMap } from '@/workflow-trace-viewer/api/workflow-server-actions'; // Default configuration const DEFAULT_CONFIG: WorldConfig = { diff --git a/packages/web/src/workflow-trace-viewer/workflow-traces/workflow-trace.module.css b/packages/web/src/workflow-trace-viewer/workflow-traces/workflow-trace.module.css deleted file mode 100644 index 9aee48d4b..000000000 --- a/packages/web/src/workflow-trace-viewer/workflow-traces/workflow-trace.module.css +++ /dev/null @@ -1,107 +0,0 @@ -/* Workflow-specific span colors */ - -/* Step statuses */ -.spanRunning, -.spanPending { - --span-background: var(--ds-blue-200); - --span-border: var(--ds-blue-500); - --span-line: var(--ds-blue-400); - --span-text: var(--ds-gray-1000); - --span-secondary: var(--ds-blue-900); -} - -.spanCompleted { - --span-background: var(--ds-green-200); - --span-border: var(--ds-green-500); - --span-line: var(--ds-green-400); - --span-text: var(--ds-gray-1000); - --span-secondary: var(--ds-green-900); -} - -.spanCancelled, -.spanPaused { - --span-background: var(--ds-amber-200); - --span-border: var(--ds-amber-500); - --span-line: var(--ds-amber-400); - --span-text: var(--ds-gray-1000); - --span-secondary: var(--ds-amber-900); -} - -.spanFailed { - --span-background: var(--ds-red-200); - --span-border: var(--ds-red-500); - --span-line: var(--ds-red-400); - --span-text: var(--ds-gray-1000); - --span-secondary: var(--ds-red-900); -} - -/* Striped background for pending status */ -.spanPendingStriped { - --span-background: repeating-linear-gradient( - 45deg, - var(--ds-blue-200), - var(--ds-blue-200) 10px, - color-mix(in srgb, var(--ds-blue-200) 70%, white) 10px, - color-mix(in srgb, var(--ds-blue-200) 70%, white) 20px - ); - --span-border: var(--ds-blue-500); - --span-line: var(--ds-blue-400); - --span-text: var(--ds-gray-1000); - --span-secondary: var(--ds-blue-900); -} - -/* Sleep step - light yellow/orange */ -.spanSleep { - --span-background: var(--ds-amber-200); - --span-border: var(--ds-amber-500); - --span-line: var(--ds-amber-400); - --span-text: var(--ds-gray-1000); - --span-secondary: var(--ds-amber-900); -} - -/* Hooks - gray color scheme */ -.spanHook { - --span-background: var(--ds-gray-200); - --span-border: var(--ds-gray-500); - --span-line: var(--ds-gray-400); - --span-text: var(--ds-gray-1000); - --span-secondary: var(--ds-gray-900); -} - -/* Event markers */ - -/* Failure events - Red */ -.eventFailed { - --event-color: var(--ds-red-600); - --span-background: var(--ds-red-100); - --span-border: var(--ds-red-500); - --span-text: var(--ds-red-900); - --span-secondary: var(--ds-red-700); -} - -/* Retry events - Orange/Yellow */ -.eventRetrying { - --event-color: var(--ds-amber-600); - --span-background: var(--ds-amber-100); - --span-border: var(--ds-amber-500); - --span-text: var(--ds-amber-900); - --span-secondary: var(--ds-amber-700); -} - -/* Webhook-related events - Purple */ -.eventHook { - --event-color: var(--ds-purple-600); - --span-background: var(--ds-purple-100); - --span-border: var(--ds-purple-500); - --span-text: var(--ds-purple-900); - --span-secondary: var(--ds-purple-700); -} - -/* Default event - Blue */ -.eventDefault { - --event-color: var(--ds-blue-600); - --span-background: var(--ds-blue-100); - --span-border: var(--ds-blue-500); - --span-text: var(--ds-blue-900); - --span-secondary: var(--ds-blue-700); -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5b155c251..4695c7fbf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -583,6 +583,9 @@ importers: '@workflow/core': specifier: workspace:* version: link:../core + '@workflow/web-shared': + specifier: workspace:* + version: link:../web-shared '@workflow/world': specifier: workspace:* version: link:../world @@ -595,9 +598,6 @@ importers: date-fns: specifier: ^4.1.0 version: 4.1.0 - json-colorizer: - specifier: ^3.0.1 - version: 3.0.1 lucide-react: specifier: ^0.469.0 version: 0.469.0(react@19.1.0) @@ -613,6 +613,48 @@ importers: sonner: specifier: ^2.0.7 version: 2.0.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + tailwind-merge: + specifier: ^2.5.5 + version: 2.6.0 + tailwindcss: + specifier: ^4 + version: 4.1.13 + typescript: + specifier: 'catalog:' + version: 5.9.3 + + packages/web-shared: + dependencies: + '@tailwindcss/postcss': + specifier: ^4 + version: 4.1.13 + '@workflow/core': + specifier: workspace:* + version: link:../core + '@workflow/world': + specifier: workspace:* + version: link:../world + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + date-fns: + specifier: ^4.1.0 + version: 4.1.0 + lucide-react: + specifier: ^0.469.0 + version: 0.469.0(react@19.1.0) + react: + specifier: 19.1.0 + version: 19.1.0 + react-dom: + specifier: 19.1.0 + version: 19.1.0(react@19.1.0) + sonner: + specifier: ^2.0.7 + version: 2.0.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0) swr: specifier: ^2.3.6 version: 2.3.6(react@19.1.0) @@ -622,6 +664,22 @@ importers: tailwindcss: specifier: ^4 version: 4.1.13 + devDependencies: + '@biomejs/biome': + specifier: 'catalog:' + version: 2.2.5 + '@types/node': + specifier: 'catalog:' + version: 24.6.2 + '@types/react': + specifier: ^19 + version: 19.1.13 + '@types/react-dom': + specifier: ^19 + version: 19.1.9(@types/react@19.1.13) + '@workflow/tsconfig': + specifier: workspace:* + version: link:../tsconfig typescript: specifier: 'catalog:' version: 5.9.3 @@ -6783,9 +6841,6 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-colorizer@3.0.1: - resolution: {integrity: sha512-4YyRAbD6eHeRnJD9vo0zjiU5fyY9QR6T+iYuH5DpO0XPThKWozpD4MaeY/8nLZIkHC3yEQMFLL+6P94E+JekDw==} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -16012,10 +16067,6 @@ snapshots: json-buffer@3.0.1: optional: true - json-colorizer@3.0.1: - dependencies: - colorette: 2.0.20 - json-schema-traverse@0.4.1: optional: true