@codemation/next-host 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/app/(shell)/credentials/page.tsx +5 -0
- package/app/(shell)/dashboard/page.tsx +14 -0
- package/app/(shell)/layout.tsx +11 -0
- package/app/(shell)/page.tsx +5 -0
- package/app/(shell)/users/page.tsx +5 -0
- package/app/(shell)/workflows/[workflowId]/page.tsx +19 -0
- package/app/(shell)/workflows/page.tsx +5 -0
- package/app/api/[[...path]]/route.ts +40 -0
- package/app/api/auth/[...nextauth]/route.ts +3 -0
- package/app/globals.css +997 -0
- package/app/invite/[token]/page.tsx +10 -0
- package/app/layout.tsx +65 -0
- package/app/login/layout.tsx +25 -0
- package/app/login/page.tsx +22 -0
- package/components.json +21 -0
- package/docs/FORMS.md +46 -0
- package/docs/TAILWIND_SHADCN_MIGRATION.md +89 -0
- package/eslint.config.mjs +56 -0
- package/middleware.ts +29 -0
- package/next-env.d.ts +6 -0
- package/next.config.ts +34 -0
- package/package.json +76 -0
- package/postcss.config.mjs +7 -0
- package/public/canvas-icons/builtin/openai.svg +5 -0
- package/src/api/CodemationApiClient.ts +107 -0
- package/src/api/CodemationApiHttpError.ts +17 -0
- package/src/auth/CodemationNextAuthConfigResolver.ts +14 -0
- package/src/auth/CodemationNextAuthOAuthProviderDescriptorMapper.ts +30 -0
- package/src/auth/CodemationNextAuthOAuthProviderSnapshotResolver.ts +17 -0
- package/src/auth/CodemationNextAuthProviderCatalog.ts +107 -0
- package/src/auth/codemationEdgeAuth.ts +25 -0
- package/src/auth/codemationNextAuth.ts +32 -0
- package/src/components/Codemation.tsx +6 -0
- package/src/components/CodemationDataTable.tsx +37 -0
- package/src/components/CodemationDialog.tsx +137 -0
- package/src/components/CodemationFormattedDateTime.tsx +46 -0
- package/src/components/GoogleColorGIcon.tsx +39 -0
- package/src/components/OauthProviderIcon.tsx +33 -0
- package/src/components/PasswordStrengthMeter.tsx +59 -0
- package/src/components/forms/index.ts +28 -0
- package/src/components/json/JsonMonacoEditor.tsx +75 -0
- package/src/components/oauthProviderIconData.ts +17 -0
- package/src/components/ui/alert.tsx +56 -0
- package/src/components/ui/badge.tsx +40 -0
- package/src/components/ui/button.tsx +64 -0
- package/src/components/ui/card.tsx +70 -0
- package/src/components/ui/collapsible.tsx +26 -0
- package/src/components/ui/dialog.tsx +137 -0
- package/src/components/ui/dropdown-menu.tsx +238 -0
- package/src/components/ui/form.tsx +147 -0
- package/src/components/ui/input.tsx +19 -0
- package/src/components/ui/label.tsx +26 -0
- package/src/components/ui/scroll-area.tsx +47 -0
- package/src/components/ui/select.tsx +169 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/switch.tsx +28 -0
- package/src/components/ui/table.tsx +72 -0
- package/src/components/ui/tabs.tsx +76 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/toggle.tsx +41 -0
- package/src/features/credentials/components/CredentialConfirmDialog.tsx +58 -0
- package/src/features/credentials/components/CredentialDialog.tsx +252 -0
- package/src/features/credentials/components/CredentialDialogFeedback.tsx +36 -0
- package/src/features/credentials/components/CredentialDialogFieldRows.tsx +257 -0
- package/src/features/credentials/components/CredentialDialogFormSections.tsx +230 -0
- package/src/features/credentials/components/CredentialEnvFieldStatusRow.tsx +64 -0
- package/src/features/credentials/components/CredentialFieldCopyButton.tsx +48 -0
- package/src/features/credentials/components/CredentialsScreenHealthBadge.tsx +21 -0
- package/src/features/credentials/components/CredentialsScreenInstancesTable.tsx +108 -0
- package/src/features/credentials/components/CredentialsScreenTestFailureAlert.tsx +33 -0
- package/src/features/credentials/hooks/useCredentialCreateDialog.ts +33 -0
- package/src/features/credentials/hooks/useCredentialDialogSession.ts +616 -0
- package/src/features/credentials/hooks/useCredentialsScreen.ts +213 -0
- package/src/features/credentials/lib/credentialFieldHelpers.ts +35 -0
- package/src/features/credentials/lib/credentialFormTypes.ts +1 -0
- package/src/features/credentials/lib/credentialInstanceTestPayloadParser.ts +10 -0
- package/src/features/credentials/screens/CredentialsScreen.tsx +187 -0
- package/src/features/invite/screens/InviteAcceptScreen.tsx +190 -0
- package/src/features/users/components/UsersInviteDialog.tsx +121 -0
- package/src/features/users/components/UsersRegenerateDialog.tsx +81 -0
- package/src/features/users/components/UsersScreenUserStatusBadge.tsx +19 -0
- package/src/features/users/schemas/usersInviteFormSchema.ts +7 -0
- package/src/features/users/screens/UsersScreen.tsx +240 -0
- package/src/features/workflows/components/WorkflowListFolderSection.tsx +91 -0
- package/src/features/workflows/components/WorkflowListItemCard.tsx +67 -0
- package/src/features/workflows/components/WorkflowListRoot.tsx +39 -0
- package/src/features/workflows/components/WorkflowsListTree.tsx +28 -0
- package/src/features/workflows/components/canvas/CanvasNodeChromeTooltip.tsx +96 -0
- package/src/features/workflows/components/canvas/CanvasNodeIconSlot.tsx +25 -0
- package/src/features/workflows/components/canvas/VisibleNodeStatusResolver.tsx +84 -0
- package/src/features/workflows/components/canvas/WorkflowCanvas.tsx +248 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNode.tsx +182 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeAccents.tsx +73 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeAgentBottomSourceHandles.tsx +43 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeAgentLabels.tsx +47 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeCard.tsx +202 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeHandles.tsx +77 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeLabelBelow.tsx +51 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeMainGlyph.tsx +64 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasCodemationNodeToolbar.tsx +95 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasLoadingPlaceholder.tsx +69 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasNodeIcon.tsx +102 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasSimpleIconGlyph.tsx +21 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasStraightCountEdge.tsx +33 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasStructureSignature.tsx +7 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasSymmetricForkEdge.tsx +32 -0
- package/src/features/workflows/components/canvas/WorkflowCanvasToolbarIconButton.tsx +95 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasBuiltinIconRegistry.ts +26 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasEdgeCountResolver.ts +51 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasEdgeStyleResolver.ts +35 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasLabelLayoutEstimator.ts +42 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasOverlapResolver.ts +78 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasPortOrderResolver.ts +25 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasRoundedOrthogonalPathPlanner.ts +56 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasSiIconRegistry.ts +18 -0
- package/src/features/workflows/components/canvas/lib/WorkflowCanvasSymmetricForkPathPlanner.ts +43 -0
- package/src/features/workflows/components/canvas/lib/layoutWorkflow.ts +315 -0
- package/src/features/workflows/components/canvas/lib/workflowCanvasEdgeGeometry.ts +3 -0
- package/src/features/workflows/components/canvas/lib/workflowCanvasEmbeddedStyles.ts +62 -0
- package/src/features/workflows/components/canvas/lib/workflowCanvasFlowTypes.ts +10 -0
- package/src/features/workflows/components/canvas/lib/workflowCanvasNodeData.ts +41 -0
- package/src/features/workflows/components/canvas/lib/workflowCanvasNodeGeometry.ts +99 -0
- package/src/features/workflows/components/canvas/workflowCanvasNodeChrome.tsx +46 -0
- package/src/features/workflows/components/realtime/RealtimeContext.tsx +14 -0
- package/src/features/workflows/components/realtime/WorkflowRealtimeProvider.tsx +15 -0
- package/src/features/workflows/components/workflowDetail/NodeCredentialBindingRow.tsx +209 -0
- package/src/features/workflows/components/workflowDetail/NodeCredentialBindingsSection.tsx +227 -0
- package/src/features/workflows/components/workflowDetail/NodePropertiesConfigSection.tsx +51 -0
- package/src/features/workflows/components/workflowDetail/NodePropertiesPanelHeader.tsx +50 -0
- package/src/features/workflows/components/workflowDetail/NodePropertiesSlidePanel.tsx +134 -0
- package/src/features/workflows/components/workflowDetail/WorkflowActivationErrorDialog.tsx +71 -0
- package/src/features/workflows/components/workflowDetail/WorkflowActivationHeaderControl.tsx +64 -0
- package/src/features/workflows/components/workflowDetail/WorkflowDetailIcons.tsx +52 -0
- package/src/features/workflows/components/workflowDetail/WorkflowExecutionInspector.tsx +110 -0
- package/src/features/workflows/components/workflowDetail/WorkflowExecutionInspectorDetailBody.tsx +213 -0
- package/src/features/workflows/components/workflowDetail/WorkflowExecutionInspectorPanes.tsx +239 -0
- package/src/features/workflows/components/workflowDetail/WorkflowExecutionInspectorSidebarResizer.tsx +31 -0
- package/src/features/workflows/components/workflowDetail/WorkflowExecutionInspectorTreePanel.tsx +133 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorAttachmentGroupingPresenter.tsx +31 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorAttachmentList.tsx +118 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorBinaryView.tsx +15 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorErrorView.tsx +107 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorJsonView.tsx +114 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorPrettyTreePresenter.tsx +132 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorPrettyTreeViewRenderer.tsx +147 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorPrettyView.tsx +65 -0
- package/src/features/workflows/components/workflowDetail/WorkflowInspectorViews.tsx +5 -0
- package/src/features/workflows/components/workflowDetail/WorkflowJsonEditorBinaryAttachmentRow.tsx +74 -0
- package/src/features/workflows/components/workflowDetail/WorkflowJsonEditorBinaryUploadRow.tsx +69 -0
- package/src/features/workflows/components/workflowDetail/WorkflowJsonEditorDialog.tsx +254 -0
- package/src/features/workflows/components/workflowDetail/WorkflowRunsList.tsx +89 -0
- package/src/features/workflows/components/workflowDetail/WorkflowRunsSidebar.tsx +50 -0
- package/src/features/workflows/hooks/canvas/useWorkflowCanvasVisibleNodeStatuses.ts +14 -0
- package/src/features/workflows/hooks/realtime/realtime.tsx +271 -0
- package/src/features/workflows/hooks/realtime/runQueryPolling.ts +34 -0
- package/src/features/workflows/hooks/realtime/useWorkflowRealtimeInfrastructure.ts +541 -0
- package/src/features/workflows/hooks/realtime/useWorkflowRealtimeShowDisconnectedBadge.ts +9 -0
- package/src/features/workflows/hooks/workflowDetail/useWorkflowDetailController.tsx +1300 -0
- package/src/features/workflows/lib/realtime/realtimeApi.ts +78 -0
- package/src/features/workflows/lib/realtime/realtimeClientBridge.ts +52 -0
- package/src/features/workflows/lib/realtime/realtimeDomainTypes.ts +191 -0
- package/src/features/workflows/lib/realtime/realtimeQueryKeys.ts +15 -0
- package/src/features/workflows/lib/realtime/realtimeRunMutations.ts +167 -0
- package/src/features/workflows/lib/realtime/workflowTypes.ts +5 -0
- package/src/features/workflows/lib/workflowDetail/PersistedWorkflowSnapshotMapper.ts +205 -0
- package/src/features/workflows/lib/workflowDetail/WorkflowActivationHttpErrorFormat.ts +32 -0
- package/src/features/workflows/lib/workflowDetail/WorkflowDetailPresenter.ts +1017 -0
- package/src/features/workflows/lib/workflowDetail/WorkflowDetailUrlCodec.ts +70 -0
- package/src/features/workflows/lib/workflowDetail/workflowDetailTypes.ts +152 -0
- package/src/features/workflows/lib/workflowDetailTreeStyles.ts +65 -0
- package/src/features/workflows/screens/WorkflowDetailScreen.tsx +236 -0
- package/src/features/workflows/screens/WorkflowDetailScreenInspectorPanel.tsx +55 -0
- package/src/features/workflows/screens/WorkflowsList.tsx +35 -0
- package/src/features/workflows/screens/WorkflowsScreen.tsx +31 -0
- package/src/index.ts +1 -0
- package/src/lib/utils.ts +6 -0
- package/src/middleware/CodemationNextHostMiddlewarePathRules.ts +31 -0
- package/src/providers/CodemationSessionProvider.tsx +23 -0
- package/src/providers/Providers.tsx +36 -0
- package/src/providers/RealtimeBoundary.tsx +17 -0
- package/src/providers/WhitelabelProvider.tsx +22 -0
- package/src/server/CodemationAuthPrismaClient.ts +21 -0
- package/src/server/CodemationNextHost.ts +379 -0
- package/src/shell/AppLayout.tsx +141 -0
- package/src/shell/AppLayoutNavItems.tsx +129 -0
- package/src/shell/AppLayoutPageHeader.tsx +79 -0
- package/src/shell/AppLayoutSidebarBrand.tsx +33 -0
- package/src/shell/AppMainContent.tsx +17 -0
- package/src/shell/AppShellHeaderActions.tsx +12 -0
- package/src/shell/AppShellHeaderActionsAuthenticated.tsx +51 -0
- package/src/shell/CodemationNextClientShell.tsx +17 -0
- package/src/shell/CredentialsSignInRedirectResolver.ts +21 -0
- package/src/shell/LoginPageClient.tsx +231 -0
- package/src/shell/WorkflowDetailChromeContext.tsx +42 -0
- package/src/shell/WorkflowFolderTreeBuilder.ts +62 -0
- package/src/shell/WorkflowFolderUi.ts +42 -0
- package/src/shell/WorkflowSidebarNavFolder.tsx +112 -0
- package/src/shell/WorkflowSidebarNavTree.tsx +68 -0
- package/src/shell/appLayoutPageTitle.ts +16 -0
- package/src/shell/appLayoutSidebarIcons.tsx +108 -0
- package/src/whitelabel/CodemationWhitelabelSnapshot.ts +4 -0
- package/src/whitelabel/CodemationWhitelabelSnapshotFactory.ts +18 -0
- package/tsconfig.json +40 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +34 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { InviteAcceptScreen } from "../../../src/features/invite/screens/InviteAcceptScreen";
|
|
2
|
+
|
|
3
|
+
export default async function InviteTokenPage(args: Readonly<{ params: Promise<{ token: string }> }>) {
|
|
4
|
+
const params = await args.params;
|
|
5
|
+
return (
|
|
6
|
+
<div className="min-h-0 flex-1 bg-background">
|
|
7
|
+
<InviteAcceptScreen inviteToken={params.token} />
|
|
8
|
+
</div>
|
|
9
|
+
);
|
|
10
|
+
}
|
package/app/layout.tsx
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import "@xyflow/react/dist/style.css";
|
|
2
|
+
import { League_Spartan } from "next/font/google";
|
|
3
|
+
import "rc-tree/assets/index.css";
|
|
4
|
+
import type { Metadata } from "next";
|
|
5
|
+
import type { ReactNode } from "react";
|
|
6
|
+
import { auth } from "../src/auth/codemationNextAuth";
|
|
7
|
+
import { WhitelabelProvider } from "../src/providers/WhitelabelProvider";
|
|
8
|
+
import { CodemationNextHost } from "../src/server/CodemationNextHost";
|
|
9
|
+
import { CodemationNextClientShell } from "../src/shell/CodemationNextClientShell";
|
|
10
|
+
import { CodemationSessionRoot } from "../src/providers/CodemationSessionProvider";
|
|
11
|
+
import type { CodemationWhitelabelSnapshot } from "../src/whitelabel/CodemationWhitelabelSnapshot";
|
|
12
|
+
import "./globals.css";
|
|
13
|
+
|
|
14
|
+
/** Consumer whitelabel and auth must be evaluated per request; do not statically cache the root shell. */
|
|
15
|
+
export const dynamic = "force-dynamic";
|
|
16
|
+
|
|
17
|
+
const leagueSpartan = League_Spartan({
|
|
18
|
+
subsets: ["latin"],
|
|
19
|
+
display: "swap",
|
|
20
|
+
variable: "--font-league-spartan",
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const defaultWhitelabel: CodemationWhitelabelSnapshot = {
|
|
24
|
+
productName: "Codemation",
|
|
25
|
+
logoUrl: null,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export async function generateMetadata(): Promise<Metadata> {
|
|
29
|
+
try {
|
|
30
|
+
const whitelabel = await CodemationNextHost.shared.getWhitelabelSnapshot();
|
|
31
|
+
return {
|
|
32
|
+
title: whitelabel.productName,
|
|
33
|
+
description: "Framework-managed workflows running inside the Next.js host.",
|
|
34
|
+
};
|
|
35
|
+
} catch {
|
|
36
|
+
return {
|
|
37
|
+
title: defaultWhitelabel.productName,
|
|
38
|
+
description: "Framework-managed workflows running inside the Next.js host.",
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export default async function RootLayout(args: Readonly<{ children: ReactNode }>) {
|
|
44
|
+
const skipUiAuth = process.env.CODEMATION_SKIP_UI_AUTH === "true";
|
|
45
|
+
const session = skipUiAuth ? null : await auth();
|
|
46
|
+
let whitelabel: CodemationWhitelabelSnapshot;
|
|
47
|
+
try {
|
|
48
|
+
whitelabel = await CodemationNextHost.shared.getWhitelabelSnapshot();
|
|
49
|
+
} catch {
|
|
50
|
+
whitelabel = defaultWhitelabel;
|
|
51
|
+
}
|
|
52
|
+
return (
|
|
53
|
+
<html lang="en" className={leagueSpartan.variable} suppressHydrationWarning>
|
|
54
|
+
<body className={leagueSpartan.className} suppressHydrationWarning>
|
|
55
|
+
<WhitelabelProvider value={whitelabel}>
|
|
56
|
+
<CodemationNextClientShell>
|
|
57
|
+
<CodemationSessionRoot enabled={!skipUiAuth} session={session}>
|
|
58
|
+
{args.children}
|
|
59
|
+
</CodemationSessionRoot>
|
|
60
|
+
</CodemationNextClientShell>
|
|
61
|
+
</WhitelabelProvider>
|
|
62
|
+
</body>
|
|
63
|
+
</html>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
import { CodemationNextHost } from "../../src/server/CodemationNextHost";
|
|
5
|
+
|
|
6
|
+
export async function generateMetadata(): Promise<Metadata> {
|
|
7
|
+
try {
|
|
8
|
+
const whitelabel = await CodemationNextHost.shared.getWhitelabelSnapshot();
|
|
9
|
+
return {
|
|
10
|
+
title: `Sign in — ${whitelabel.productName}`,
|
|
11
|
+
};
|
|
12
|
+
} catch {
|
|
13
|
+
return {
|
|
14
|
+
title: "Sign in — Codemation",
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default function LoginLayout(args: Readonly<{ children: ReactNode }>) {
|
|
20
|
+
return (
|
|
21
|
+
<div className="codemation-login-root" data-testid="login-layout-root">
|
|
22
|
+
{args.children}
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { CodemationNextAuthOAuthProviderSnapshotResolver } from "../../src/auth/CodemationNextAuthOAuthProviderSnapshotResolver";
|
|
2
|
+
import { CodemationNextHost } from "../../src/server/CodemationNextHost";
|
|
3
|
+
import { LoginPageClient } from "../../src/shell/LoginPageClient";
|
|
4
|
+
|
|
5
|
+
export default async function LoginPage(args: Readonly<{ searchParams: Promise<{ callbackUrl?: string }> }>) {
|
|
6
|
+
const searchParams = await args.searchParams;
|
|
7
|
+
const callbackUrl =
|
|
8
|
+
typeof searchParams.callbackUrl === "string" && searchParams.callbackUrl.length > 0
|
|
9
|
+
? searchParams.callbackUrl
|
|
10
|
+
: "/";
|
|
11
|
+
const fallbackWhitelabel = { productName: "Codemation", logoUrl: null } as const;
|
|
12
|
+
const whitelabel = await CodemationNextHost.shared.getWhitelabelSnapshot().catch(() => fallbackWhitelabel);
|
|
13
|
+
const oauthProviders = await new CodemationNextAuthOAuthProviderSnapshotResolver().resolve().catch(() => []);
|
|
14
|
+
return (
|
|
15
|
+
<LoginPageClient
|
|
16
|
+
callbackUrl={callbackUrl}
|
|
17
|
+
productName={whitelabel.productName}
|
|
18
|
+
logoUrl={whitelabel.logoUrl}
|
|
19
|
+
oauthProviders={oauthProviders}
|
|
20
|
+
/>
|
|
21
|
+
);
|
|
22
|
+
}
|
package/components.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "radix-nova",
|
|
4
|
+
"rsc": true,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "app/globals.css",
|
|
9
|
+
"baseColor": "neutral",
|
|
10
|
+
"cssVariables": true,
|
|
11
|
+
"prefix": ""
|
|
12
|
+
},
|
|
13
|
+
"aliases": {
|
|
14
|
+
"components": "@/components",
|
|
15
|
+
"utils": "@/lib/utils",
|
|
16
|
+
"ui": "@/components/ui",
|
|
17
|
+
"lib": "@/lib",
|
|
18
|
+
"hooks": "@/hooks"
|
|
19
|
+
},
|
|
20
|
+
"iconLibrary": "lucide"
|
|
21
|
+
}
|
package/docs/FORMS.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Forms (next-host)
|
|
2
|
+
|
|
3
|
+
## Stack
|
|
4
|
+
|
|
5
|
+
- **React Hook Form** — performant, accessible field registration and submission.
|
|
6
|
+
- **Zod** (v3 in this package) — schema validation at the boundary; infer TypeScript types from schemas. Kept on **Zod 3** for stable `@hookform/resolvers/zod` typings.
|
|
7
|
+
- **`@hookform/resolvers/zod`** — connect Zod to RHF via `zodResolver`.
|
|
8
|
+
- **UI** — `Form`, `FormField`, `FormItem`, `FormLabel`, `FormControl`, `FormDescription`, `FormMessage` from `@/components/ui/form`, wrapping `@/components/ui/input`, `Label`, etc.
|
|
9
|
+
|
|
10
|
+
## Pattern
|
|
11
|
+
|
|
12
|
+
1. Define a Zod schema (co-locate under the feature or a small `*Schema.ts` module).
|
|
13
|
+
2. `useForm` with `resolver: zodResolver(schema)` and `defaultValues`.
|
|
14
|
+
3. Wrap the form in `<Form {...form}>` (alias of `FormProvider`).
|
|
15
|
+
4. For each field, use `FormField` + `FormItem` + `FormLabel` + `FormControl` + your `Input` / `Select` / `Textarea`.
|
|
16
|
+
5. Surface field errors with `FormMessage`; optional help with `FormDescription`.
|
|
17
|
+
6. Submit with `form.handleSubmit(onValid)`.
|
|
18
|
+
|
|
19
|
+
## Imports
|
|
20
|
+
|
|
21
|
+
Prefer the barrel so agents don’t mix ad-hoc stacks:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import {
|
|
25
|
+
z,
|
|
26
|
+
zodResolver,
|
|
27
|
+
useForm,
|
|
28
|
+
Form,
|
|
29
|
+
FormField,
|
|
30
|
+
FormItem,
|
|
31
|
+
FormLabel,
|
|
32
|
+
FormControl,
|
|
33
|
+
FormMessage,
|
|
34
|
+
} from "@/components/forms";
|
|
35
|
+
import { Input } from "@/components/ui/input";
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## ESLint
|
|
39
|
+
|
|
40
|
+
`packages/next-host/eslint.config.mjs` forbids raw `<input>` and `<textarea>` outside `src/components/ui/**` (primitives live there). Use `Input` / `Textarea` + the form components above.
|
|
41
|
+
|
|
42
|
+
## References
|
|
43
|
+
|
|
44
|
+
- [React Hook Form](https://react-hook-form.com/)
|
|
45
|
+
- [Zod](https://zod.dev/)
|
|
46
|
+
- [shadcn/ui Form](https://ui.shadcn.com/docs/components/form)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Tailwind v4 + shadcn/ui migration (next-host)
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Replace ad-hoc global CSS with **Tailwind CSS v4**, **design tokens** (CSS variables + `@theme inline`), and **shadcn/ui** primitives (Radix + copy-paste components). Prepare for **dashboards** later (Recharts + TanStack Table + shadcn chart patterns).
|
|
6
|
+
|
|
7
|
+
## Inventory (refactor surface)
|
|
8
|
+
|
|
9
|
+
| Area | Notes |
|
|
10
|
+
| ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| **`app/globals.css`** | ~1.5k lines after bootstrap: Tailwind imports + shadcn theme + **legacy** BEM-style rules (to delete incrementally). |
|
|
12
|
+
| **`app/`** | `layout.tsx`, `(shell)/layout.tsx`, `login/layout.tsx`, route `page.tsx` files — shell chrome and page wrappers. |
|
|
13
|
+
| **`src/shell/`** | App layout, sidebar, header, login client — high-value for first Tailwind passes. |
|
|
14
|
+
| **`src/features/credentials/`** (`screens/`, `components/`, `hooks/`, `lib/`) | Tables, dialogs, forms — migrate to `Button`, `Input`, `Dialog`, etc. |
|
|
15
|
+
| **`src/features/users/`**, **`invite/`** | Same layout (`screens/` + `components/` where needed). |
|
|
16
|
+
| **`src/features/workflows/`** | Largest surface: `screens/`, `components/{workflowDetail,canvas,realtime}/`, `hooks/`, `lib/{workflowDetail,realtime}/` — canvas helpers live under **`components/canvas/lib/`** (layout, edge resolvers, embedded styles). Many **inline `style={{}}`** (~200+ in package). |
|
|
17
|
+
| **CSS-in-TS** | `components/canvas/lib/workflowCanvasEmbeddedStyles.ts`, `lib/workflowDetailTreeStyles.ts` — keep minimal (keyframes, third-party overrides); rest → utilities/tokens. |
|
|
18
|
+
| **Vendor CSS** | `@xyflow/react/dist/style.css`, `rc-tree/assets/index.css` — keep; override with scoped classes or tokens. |
|
|
19
|
+
| **`src/components/`** | Shared widgets (`CodemationDataTable`, etc.) — adopt primitives + `cn()`. |
|
|
20
|
+
|
|
21
|
+
**Approximate counts (pre-migration):** ~230 `className=` in `.tsx`; ~238 `style={{` in `.tsx` (workflows-heavy).
|
|
22
|
+
|
|
23
|
+
## What’s done (bootstrap)
|
|
24
|
+
|
|
25
|
+
- **Tailwind v4** via `@tailwindcss/postcss` + `postcss.config.mjs`.
|
|
26
|
+
- **`shadcn` CLI package** + **`tw-animate-css`**, **`class-variance-authority`**, **`clsx`**, **`tailwind-merge`**.
|
|
27
|
+
- **`radix-ui`** (unified Radix primitives; required by **radix-nova** generated components such as `Button`).
|
|
28
|
+
- **`components.json`** (`style: radix-nova`, `css: app/globals.css`, Lucide).
|
|
29
|
+
- **`src/lib/utils.ts`** — `cn()` helper for class merging.
|
|
30
|
+
- **`tsconfig`**: `baseUrl: "."`, `@/*` → `./src/*`, workspace aliases as **paths relative to `packages/next-host`** (required for Next + `@codemation/*` resolution).
|
|
31
|
+
- **`app/globals.css`**: `@import "tailwindcss"`, `tw-animate-css`, `shadcn/tailwind.css`; `@theme inline`; shadcn `:root` / `.dark`; `@layer base`; **legacy bridge** (`--color-*`, `--sidebar-*`, spacing, buttons) → tokens; **legacy class rules** retained until screens migrate.
|
|
32
|
+
|
|
33
|
+
## Phased rollout
|
|
34
|
+
|
|
35
|
+
1. **Primitives** — `pnpm dlx shadcn@latest add button input label dialog select tabs table ...` into `src/components/ui/`. Use `@/components/ui/*` imports.
|
|
36
|
+
2. **Shell** — Replace `.app-*` classes in `AppLayout` / nav with Tailwind + tokens; shrink `globals.css` sections.
|
|
37
|
+
3. **Feature screens** — Credentials → Users → Invite → Workflows list → Workflow detail (inspector before canvas if easier).
|
|
38
|
+
4. **Canvas** — Last: XYFlow needs pixel layout; prefer tokens + minimal inline where unavoidable.
|
|
39
|
+
5. **Dashboards (later)** — Add **Recharts** + shadcn chart recipe; **TanStack Table** for dense grids; optional **Tremor** blocks only if needed.
|
|
40
|
+
|
|
41
|
+
## Dark mode
|
|
42
|
+
|
|
43
|
+
- Tokens already include `.dark` in `globals.css`. Add a **theme toggle** that sets `class="dark"` on `document.documentElement` (or `next-themes` when you add it).
|
|
44
|
+
|
|
45
|
+
## ESLint / consistency
|
|
46
|
+
|
|
47
|
+
- Keep **no Server Actions** rule in `packages/next-host`.
|
|
48
|
+
- Root config enables **`no-alert`** (blocks `alert()`, `confirm()`, `prompt()` — use in-app UI). In **next-host** we extend the same idea for markup: **`no-restricted-syntax`** flags native **`<select>`** so agents use **`@/components/ui/select`** (Radix) instead of inconsistent browser styling and `change`-event tests.
|
|
49
|
+
- Prefer **semantic utilities** (`bg-background`, `text-muted-foreground`) over raw palette classes in new code.
|
|
50
|
+
- Optionally add **lint for raw `gray-*`** in a follow-up (team decision).
|
|
51
|
+
|
|
52
|
+
## Buttons vs badges
|
|
53
|
+
|
|
54
|
+
- **`Button`** (`src/components/ui/button.tsx`): **squarer corners** (`rounded-md`, smaller sizes `rounded-sm`), light **`shadow-sm`** on filled variants — reads as an actionable control.
|
|
55
|
+
- **`Badge`** (`src/components/ui/badge.tsx`): **pill** shape (`rounded-full`), **`shadow-none`** — reads as status/metadata, not a primary action.
|
|
56
|
+
|
|
57
|
+
## Composed dialogs (`CodemationDialog`)
|
|
58
|
+
|
|
59
|
+
Use [`src/components/CodemationDialog.tsx`](src/components/CodemationDialog.tsx) for modal shells instead of hand-rolled `fixed inset-0` overlays. It wraps Radix **`Dialog`** + **`DialogContent`** (focus trap, escape, overlay) with a consistent layout:
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
<CodemationDialog onClose={...} testId="..." size="wide" role="dialog">
|
|
63
|
+
<CodemationDialog.Title>Title</CodemationDialog.Title>
|
|
64
|
+
<CodemationDialog.Content>{/* scrollable body */}</CodemationDialog.Content>
|
|
65
|
+
<CodemationDialog.Actions position="bottom">{/* buttons */}</CodemationDialog.Actions>
|
|
66
|
+
</CodemationDialog>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- **`CodemationDialog.Actions`** — `position="top" | "bottom"` (default `bottom`), `align="start" | "end" | "between"`.
|
|
70
|
+
- **`size`** — `narrow` (~`lg`), `wide` (~`2xl`), `full`.
|
|
71
|
+
- **Do not pass `id` to `CodemationDialog.Title`** — Radix assigns `titleId` in context; overriding `id` on `DialogTitle` breaks `aria-labelledby` and triggers dev warnings.
|
|
72
|
+
- Optional **`role="alertdialog"`** for confirmations (e.g. delete confirm).
|
|
73
|
+
|
|
74
|
+
## Verification (avoid heavy full-repo gates)
|
|
75
|
+
|
|
76
|
+
Iterating on UI only:
|
|
77
|
+
|
|
78
|
+
1. `pnpm --filter @codemation/next-host run lint`
|
|
79
|
+
2. `pnpm run test:ui` (from repo root; host UI tests import next-host with `@/` alias)
|
|
80
|
+
|
|
81
|
+
Use **`pnpm --filter @codemation/next-host run build`** when you need a Next production compile without building all workspace packages.
|
|
82
|
+
|
|
83
|
+
Reserve **root `pnpm test`** / **`pnpm check`** / wide **`turbo run build`** for **CI or pre-merge** — they rebuild many packages and run all suites (high CPU/time).
|
|
84
|
+
|
|
85
|
+
## References
|
|
86
|
+
|
|
87
|
+
- [Tailwind + Next.js](https://tailwindcss.com/docs/installation/framework-guides/nextjs)
|
|
88
|
+
- [shadcn manual install](https://ui.shadcn.com/docs/installation/manual)
|
|
89
|
+
- [Tailwind v4 + shadcn](https://ui.shadcn.com/docs/tailwind-v4)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import base from "@codemation/eslint-config";
|
|
2
|
+
|
|
3
|
+
/** @type {import("eslint").Linter.FlatConfig[]} */
|
|
4
|
+
export default [
|
|
5
|
+
...base,
|
|
6
|
+
{
|
|
7
|
+
files: ["src/components/ui/**/*.tsx", "src/components/CodemationDialog.tsx"],
|
|
8
|
+
rules: {
|
|
9
|
+
"max-lines": "off",
|
|
10
|
+
"codemation/single-react-component-per-file": "off",
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
files: ["src/**/*.{ts,tsx}"],
|
|
15
|
+
ignores: ["src/api/CodemationApiClient.ts"],
|
|
16
|
+
rules: {
|
|
17
|
+
"no-restricted-syntax": [
|
|
18
|
+
"error",
|
|
19
|
+
{
|
|
20
|
+
selector: "ExpressionStatement[expression.type='Literal'][expression.value='use server']",
|
|
21
|
+
message:
|
|
22
|
+
'Use the HTTP API (/api/*) and @codemation/host handlers only; do not add Server Actions ("use server").',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
selector: "CallExpression[callee.type='Identifier'][callee.name='fetch']",
|
|
26
|
+
message:
|
|
27
|
+
"Use codemationApiClient from src/api/CodemationApiClient.ts for /api/* calls (same-origin session cookies, JSON, consistent errors). Global fetch() is reserved for that wrapper only.",
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
files: ["src/**/*.{ts,tsx}"],
|
|
34
|
+
ignores: ["src/api/CodemationApiClient.ts", "src/components/ui/**"],
|
|
35
|
+
rules: {
|
|
36
|
+
"no-restricted-syntax": [
|
|
37
|
+
"error",
|
|
38
|
+
{
|
|
39
|
+
selector: "JSXOpeningElement[name.name='select']",
|
|
40
|
+
message:
|
|
41
|
+
"Use @/components/ui/select (Radix/shadcn) instead of native <select> for consistent styling, keyboard behavior, and testability.",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
selector: "JSXOpeningElement[name.name='input']",
|
|
45
|
+
message:
|
|
46
|
+
"Use @/components/ui/input with FormField/FormControl from @/components/ui/form (see packages/next-host/docs/FORMS.md) instead of raw <input>.",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
selector: "JSXOpeningElement[name.name='textarea']",
|
|
50
|
+
message:
|
|
51
|
+
"Use @/components/ui/textarea with FormField/FormControl from @/components/ui/form (see packages/next-host/docs/FORMS.md) instead of raw <textarea>.",
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
];
|
package/middleware.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { NextRequest } from "next/server";
|
|
2
|
+
import { NextResponse } from "next/server";
|
|
3
|
+
import { auth } from "./src/auth/codemationEdgeAuth";
|
|
4
|
+
import { CodemationNextHostMiddlewarePathRules } from "./src/middleware/CodemationNextHostMiddlewarePathRules";
|
|
5
|
+
|
|
6
|
+
export default auth((request: NextRequest) => {
|
|
7
|
+
if (process.env.CODEMATION_SKIP_UI_AUTH === "true") {
|
|
8
|
+
return NextResponse.next();
|
|
9
|
+
}
|
|
10
|
+
const pathname = request.nextUrl.pathname;
|
|
11
|
+
if (
|
|
12
|
+
CodemationNextHostMiddlewarePathRules.isFrameworkAuthRoute(pathname) ||
|
|
13
|
+
CodemationNextHostMiddlewarePathRules.isAnonymousApiRoute(pathname) ||
|
|
14
|
+
CodemationNextHostMiddlewarePathRules.isPublicUiRoute(pathname) ||
|
|
15
|
+
CodemationNextHostMiddlewarePathRules.isNextStaticAsset(pathname)
|
|
16
|
+
) {
|
|
17
|
+
return NextResponse.next();
|
|
18
|
+
}
|
|
19
|
+
if (!request.auth) {
|
|
20
|
+
const loginUrl = new URL("/login", request.nextUrl.origin);
|
|
21
|
+
loginUrl.searchParams.set("callbackUrl", request.nextUrl.pathname + request.nextUrl.search);
|
|
22
|
+
return NextResponse.redirect(loginUrl);
|
|
23
|
+
}
|
|
24
|
+
return NextResponse.next();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export const config = {
|
|
28
|
+
matcher: ["/((?!_next/static|_next/image|.*\\.(?:ico|png|jpg|svg|webp)$).*)"],
|
|
29
|
+
};
|
package/next-env.d.ts
ADDED
package/next.config.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { NextConfig } from "next";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const nextHostDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
6
|
+
|
|
7
|
+
const nextConfig: NextConfig = {
|
|
8
|
+
reactStrictMode: true,
|
|
9
|
+
/** Playwright and some browsers hit the dev server via 127.0.0.1 while Next prints localhost — silence Turbopack HMR cross-origin warnings. */
|
|
10
|
+
allowedDevOrigins: ["127.0.0.1", "localhost"],
|
|
11
|
+
serverExternalPackages: [
|
|
12
|
+
"@electric-sql/pglite",
|
|
13
|
+
"@electric-sql/pglite-socket",
|
|
14
|
+
"pglite-prisma-adapter",
|
|
15
|
+
"ws",
|
|
16
|
+
"bufferutil",
|
|
17
|
+
"utf-8-validate",
|
|
18
|
+
],
|
|
19
|
+
transpilePackages: [
|
|
20
|
+
"@codemation/core",
|
|
21
|
+
"@codemation/core-nodes",
|
|
22
|
+
"@codemation/core-nodes-gmail",
|
|
23
|
+
"@codemation/eventbus-redis",
|
|
24
|
+
"@codemation/host",
|
|
25
|
+
"@codemation/node-example",
|
|
26
|
+
"@codemation/queue-bullmq",
|
|
27
|
+
],
|
|
28
|
+
experimental: {
|
|
29
|
+
externalDir: true,
|
|
30
|
+
},
|
|
31
|
+
outputFileTracingRoot: path.resolve(nextHostDirectory, "../.."),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default nextConfig;
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codemation/next-host",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"author": "Made Relevant B.V.",
|
|
8
|
+
"homepage": "https://www.maderelevant.com",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./src/index.ts",
|
|
13
|
+
"import": "./src/index.ts"
|
|
14
|
+
},
|
|
15
|
+
"./package.json": "./package.json"
|
|
16
|
+
},
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"@auth/prisma-adapter": "^2.11.1",
|
|
19
|
+
"@hookform/resolvers": "^5.2.2",
|
|
20
|
+
"@monaco-editor/react": "^4.7.0",
|
|
21
|
+
"@tanstack/react-query": "^5.90.21",
|
|
22
|
+
"@uiw/react-json-view": "2.0.0-alpha.41",
|
|
23
|
+
"@xyflow/react": "^12.10.1",
|
|
24
|
+
"bcryptjs": "^3.0.3",
|
|
25
|
+
"class-variance-authority": "^0.7.1",
|
|
26
|
+
"clsx": "^2.1.1",
|
|
27
|
+
"dagre": "^0.8.5",
|
|
28
|
+
"date-fns": "^4.1.0",
|
|
29
|
+
"hono": "^4.12.8",
|
|
30
|
+
"jose": "^6.2.2",
|
|
31
|
+
"lucide-react": "^0.577.0",
|
|
32
|
+
"monaco-editor": "^0.55.1",
|
|
33
|
+
"next": "^16.2.0",
|
|
34
|
+
"next-auth": "5.0.0-beta.30",
|
|
35
|
+
"pretty-ms": "^9.3.0",
|
|
36
|
+
"prisma": "^7.5.0",
|
|
37
|
+
"radix-ui": "^1.4.3",
|
|
38
|
+
"rc-tree": "^5.13.1",
|
|
39
|
+
"react": "^19.2.4",
|
|
40
|
+
"react-dom": "^19.2.4",
|
|
41
|
+
"react-hook-form": "^7.71.2",
|
|
42
|
+
"shadcn": "^4.1.0",
|
|
43
|
+
"simple-icons": "^16.12.0",
|
|
44
|
+
"tailwind-merge": "^3.5.0",
|
|
45
|
+
"tw-animate-css": "^1.4.0",
|
|
46
|
+
"zod": "^3.25.76",
|
|
47
|
+
"zxcvbn": "^4.4.2",
|
|
48
|
+
"@codemation/core": "0.0.1",
|
|
49
|
+
"@codemation/host": "0.0.1"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
53
|
+
"@testing-library/react": "^16.3.2",
|
|
54
|
+
"@tailwindcss/postcss": "^4.2.2",
|
|
55
|
+
"@types/dagre": "^0.7.54",
|
|
56
|
+
"@types/node": "^25.3.5",
|
|
57
|
+
"@types/react": "^19.2.14",
|
|
58
|
+
"@types/react-dom": "^19.2.3",
|
|
59
|
+
"@types/zxcvbn": "^4.4.5",
|
|
60
|
+
"eslint": "^10.0.3",
|
|
61
|
+
"jsdom": "^25.0.1",
|
|
62
|
+
"postcss": "^8.5.8",
|
|
63
|
+
"tailwindcss": "^4.2.2",
|
|
64
|
+
"typescript": "^5.9.3",
|
|
65
|
+
"vitest": "4.0.18",
|
|
66
|
+
"@codemation/eslint-config": "0.0.0"
|
|
67
|
+
},
|
|
68
|
+
"scripts": {
|
|
69
|
+
"dev:next": "next dev",
|
|
70
|
+
"build": "next build",
|
|
71
|
+
"start": "next start",
|
|
72
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
73
|
+
"lint": "eslint .",
|
|
74
|
+
"test": "vitest run"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 320" width="24" height="24" fill="#0f172a" aria-hidden="true">
|
|
2
|
+
<title>OpenAI</title>
|
|
3
|
+
<!-- Symbol only (wordmark omitted). Square canvas slot: use object-fit contain in the host. Source: Wikimedia OpenAI_Logo.svg (mark path). -->
|
|
4
|
+
<path d="m297.06 130.97c7.26-21.79 4.76-45.66-6.85-65.48-17.46-30.4-52.56-46.04-86.84-38.68-15.25-17.18-37.16-26.95-60.13-26.81-35.04-.08-66.13 22.48-76.91 55.82-22.51 4.61-41.94 18.7-53.31 38.67-17.59 30.32-13.58 68.54 9.92 94.54-7.26 21.79-4.76 45.66 6.85 65.48 17.46 30.4 52.56 46.04 86.84 38.68 15.24 17.18 37.16 26.95 60.13 26.8 35.06.09 66.16-22.49 76.94-55.86 22.51-4.61 41.94-18.7 53.31-38.67 17.57-30.32 13.55-68.51-9.94-94.51zm-120.28 168.11c-14.03.02-27.62-4.89-38.39-13.88.49-.26 1.34-.73 1.89-1.07l63.72-36.8c3.26-1.85 5.26-5.32 5.24-9.07v-89.83l26.93 15.55c.29.14.48.42.52.74v74.39c-.04 33.08-26.83 59.9-59.91 59.97zm-128.84-55.03c-7.03-12.14-9.56-26.37-7.15-40.18.47.28 1.3.79 1.89 1.13l63.72 36.8c3.23 1.89 7.23 1.89 10.47 0l77.79-44.92v31.1c.02.32-.13.63-.38.83l-64.41 37.19c-28.69 16.52-65.33 6.7-81.92-21.95zm-16.77-139.09c7-12.16 18.05-21.46 31.21-26.29 0 .55-.03 1.52-.03 2.2v73.61c-.02 3.74 1.98 7.21 5.23 9.06l77.79 44.91-26.93 15.55c-.27.18-.61.21-.91.08l-64.42-37.22c-28.63-16.58-38.45-53.21-21.95-81.89zm221.26 51.49-77.79-44.92 26.93-15.54c.27-.18.61-.21.91-.08l64.42 37.19c28.68 16.57 38.51 53.26 21.94 81.94-7.01 12.14-18.05 21.44-31.2 26.28v-75.81c.03-3.74-1.96-7.2-5.2-9.06zm26.8-40.34c-.47-.29-1.3-.79-1.89-1.13l-63.72-36.8c-3.23-1.89-7.23-1.89-10.47 0l-77.79 44.92v-31.1c-.02-.32.13-.63.38-.83l64.41-37.16c28.69-16.55 65.37-6.7 81.91 22 6.99 12.12 9.52 26.31 7.15 40.1zm-168.51 55.43-26.94-15.55c-.29-.14-.48-.42-.52-.74v-74.39c.02-33.12 26.89-59.96 60.01-59.94 14.01 0 27.57 4.92 38.34 13.88-.49.26-1.33.73-1.89 1.07l-63.72 36.8c-3.26 1.85-5.26 5.31-5.24 9.06l-.04 89.79zm14.63-31.54 34.65-20.01 34.65 20v40.01l-34.65 20-34.65-20z"/>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { CodemationApiHttpError } from "./CodemationApiHttpError";
|
|
2
|
+
|
|
3
|
+
const defaultInit: Readonly<RequestInit> = {
|
|
4
|
+
cache: "no-store",
|
|
5
|
+
credentials: "same-origin",
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
function mergeHeaders(base: HeadersInit | undefined, extra: HeadersInit | undefined): Headers {
|
|
9
|
+
const headers = new Headers(base);
|
|
10
|
+
if (extra) {
|
|
11
|
+
const next = new Headers(extra);
|
|
12
|
+
next.forEach((value, key) => {
|
|
13
|
+
headers.set(key, value);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
return headers;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Single place for browser calls to the Codemation App Router `/api/*` surface.
|
|
21
|
+
* Uses same-origin cookies (session) and consistent JSON + error handling.
|
|
22
|
+
*/
|
|
23
|
+
export class CodemationApiClient {
|
|
24
|
+
private async requestOrThrow(url: string, init: RequestInit): Promise<Response> {
|
|
25
|
+
const response = await fetch(url, { ...defaultInit, ...init });
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
const bodyText = typeof response.text === "function" ? await response.text() : "";
|
|
28
|
+
throw new CodemationApiHttpError(response.status, bodyText);
|
|
29
|
+
}
|
|
30
|
+
return response;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private async parseJsonBody<T>(response: Response): Promise<T> {
|
|
34
|
+
if (typeof response.json === "function") {
|
|
35
|
+
try {
|
|
36
|
+
const data = await response.json();
|
|
37
|
+
return data as T;
|
|
38
|
+
} catch {
|
|
39
|
+
// Empty or invalid JSON body — try text fallback when available.
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (typeof response.text === "function") {
|
|
43
|
+
const text = await response.text();
|
|
44
|
+
if (!text.trim()) {
|
|
45
|
+
return undefined as T;
|
|
46
|
+
}
|
|
47
|
+
return JSON.parse(text) as T;
|
|
48
|
+
}
|
|
49
|
+
return undefined as T;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async getJson<T>(url: string, init?: RequestInit): Promise<T> {
|
|
53
|
+
const response = await this.requestOrThrow(url, { method: "GET", ...init });
|
|
54
|
+
return this.parseJsonBody<T>(response);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async postJson<TResponse>(url: string, body?: unknown, init?: RequestInit): Promise<TResponse> {
|
|
58
|
+
const headers = mergeHeaders(init?.headers, { "content-type": "application/json" });
|
|
59
|
+
const response = await this.requestOrThrow(url, {
|
|
60
|
+
...init,
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers,
|
|
63
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
64
|
+
});
|
|
65
|
+
return this.parseJsonBody<TResponse>(response);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async putJson<TResponse>(url: string, body: unknown, init?: RequestInit): Promise<TResponse> {
|
|
69
|
+
const headers = mergeHeaders(init?.headers, { "content-type": "application/json" });
|
|
70
|
+
const response = await this.requestOrThrow(url, {
|
|
71
|
+
...init,
|
|
72
|
+
method: "PUT",
|
|
73
|
+
headers,
|
|
74
|
+
body: JSON.stringify(body),
|
|
75
|
+
});
|
|
76
|
+
return this.parseJsonBody<TResponse>(response);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async patchJson<TResponse>(url: string, body: unknown, init?: RequestInit): Promise<TResponse> {
|
|
80
|
+
const headers = mergeHeaders(init?.headers, { "content-type": "application/json" });
|
|
81
|
+
const response = await this.requestOrThrow(url, {
|
|
82
|
+
...init,
|
|
83
|
+
method: "PATCH",
|
|
84
|
+
headers,
|
|
85
|
+
body: JSON.stringify(body),
|
|
86
|
+
});
|
|
87
|
+
return this.parseJsonBody<TResponse>(response);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async delete(url: string, init?: RequestInit): Promise<void> {
|
|
91
|
+
await this.requestOrThrow(url, { method: "DELETE", ...init });
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Multipart upload (browser sets Content-Type with boundary). */
|
|
95
|
+
async postFormData<T>(url: string, formData: FormData, init?: RequestInit): Promise<T> {
|
|
96
|
+
const response = await this.requestOrThrow(url, {
|
|
97
|
+
...init,
|
|
98
|
+
method: "POST",
|
|
99
|
+
body: formData,
|
|
100
|
+
headers: init?.headers,
|
|
101
|
+
});
|
|
102
|
+
return this.parseJsonBody<T>(response);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Shared client for next-host UI modules (same-origin `/api/*`). */
|
|
107
|
+
export const codemationApiClient = new CodemationApiClient();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown when a Codemation `/api/*` HTTP response has a non-OK status.
|
|
3
|
+
* Use {@link CodemationApiHttpError.bodyText} for user-facing or diagnostic messages.
|
|
4
|
+
*/
|
|
5
|
+
export class CodemationApiHttpError extends Error {
|
|
6
|
+
readonly status: number;
|
|
7
|
+
|
|
8
|
+
readonly bodyText: string;
|
|
9
|
+
|
|
10
|
+
constructor(status: number, bodyText: string) {
|
|
11
|
+
const trimmed = bodyText.trim();
|
|
12
|
+
super(trimmed.length > 0 ? `HTTP ${status}: ${trimmed}` : `HTTP ${status}`);
|
|
13
|
+
this.name = "CodemationApiHttpError";
|
|
14
|
+
this.status = status;
|
|
15
|
+
this.bodyText = bodyText;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CodemationAuthConfig } from "@codemation/host";
|
|
2
|
+
|
|
3
|
+
export class CodemationNextAuthConfigResolver {
|
|
4
|
+
async resolve(): Promise<CodemationAuthConfig | undefined> {
|
|
5
|
+
const serialized = process.env.CODEMATION_AUTH_CONFIG_JSON;
|
|
6
|
+
if (serialized && serialized.trim().length > 0) {
|
|
7
|
+
const parsed = JSON.parse(serialized) as CodemationAuthConfig | null;
|
|
8
|
+
return parsed ?? undefined;
|
|
9
|
+
}
|
|
10
|
+
const { CodemationNextHost } = await import("../server/CodemationNextHost");
|
|
11
|
+
const context = await CodemationNextHost.shared.prepare();
|
|
12
|
+
return context.authConfig;
|
|
13
|
+
}
|
|
14
|
+
}
|