From 4f84975af1451339cfe76d54370fade5f54e6a85 Mon Sep 17 00:00:00 2001 From: Danny Roosevelt Date: Wed, 1 Oct 2025 14:22:12 -0700 Subject: [PATCH 1/8] Minor, mostly no-op tweaks to the basic managed-auth demo --- managed-auth-basic-next-app/.env.example | 2 +- managed-auth-basic-next-app/app/globals.css | 20 ++++++++++++++------ managed-auth-basic-next-app/app/layout.tsx | 8 +++++++- managed-auth-basic-next-app/app/page.tsx | 6 ++++-- managed-auth-basic-next-app/app/server.ts | 6 +++--- 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/managed-auth-basic-next-app/.env.example b/managed-auth-basic-next-app/.env.example index 0a1513c..cd5fca6 100644 --- a/managed-auth-basic-next-app/.env.example +++ b/managed-auth-basic-next-app/.env.example @@ -1,5 +1,5 @@ PIPEDREAM_CLIENT_ID= PIPEDREAM_CLIENT_SECRET= -PIPEDREAM_PROJECT_ID= +NEXT_PUBLIC_PIPEDREAM_PROJECT_ID= # does not typically need to be accessible by the frontend, but the demo app uses it PIPEDREAM_PROJECT_ENVIRONMENT=development PIPEDREAM_ALLOWED_ORIGINS='["https://example.com/", "http://localhost:3000/", "http://localhost:3001/", "http://localhost:3002/"]' \ No newline at end of file diff --git a/managed-auth-basic-next-app/app/globals.css b/managed-auth-basic-next-app/app/globals.css index b67d5c0..1d55421 100644 --- a/managed-auth-basic-next-app/app/globals.css +++ b/managed-auth-basic-next-app/app/globals.css @@ -2,31 +2,39 @@ @tailwind components; @tailwind utilities; +@layer base { + html, body { + @apply overflow-x-hidden; + width: 100%; + position: relative; + } +} + @layer components { .text-title { @apply text-2xl font-bold text-gray-800; } - + .text-subtitle { @apply text-lg font-semibold text-gray-800; } - + .text-body { @apply font-normal text-base text-gray-600; } - + .text-small { @apply font-normal text-sm text-gray-600; } - + .text-code { @apply font-mono text-sm; } - + .text-error { @apply text-red-500; } - + .text-link { @apply text-blue-600 hover:underline; } diff --git a/managed-auth-basic-next-app/app/layout.tsx b/managed-auth-basic-next-app/app/layout.tsx index 679471b..7e8e074 100644 --- a/managed-auth-basic-next-app/app/layout.tsx +++ b/managed-auth-basic-next-app/app/layout.tsx @@ -1,4 +1,4 @@ -import type { Metadata } from "next"; +import type { Metadata, Viewport } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; @@ -9,6 +9,12 @@ export const metadata: Metadata = { description: "Generated by create next app", }; +export const viewport: Viewport = { + width: "device-width", + initialScale: 1, + maximumScale: 1, +}; + export default function RootLayout({ children, }: Readonly<{ diff --git a/managed-auth-basic-next-app/app/page.tsx b/managed-auth-basic-next-app/app/page.tsx index eafe839..7741922 100644 --- a/managed-auth-basic-next-app/app/page.tsx +++ b/managed-auth-basic-next-app/app/page.tsx @@ -8,7 +8,8 @@ import { useSearchParams } from "next/navigation"; import { serverConnectTokenCreate, getAccountById } from "./server" import type { GetAppResponse, App, PipedreamClient as FrontendClient } from "@pipedream/sdk/browser"; -const frontendHost = process.env.PIPEDREAM_FRONTEND_HOST || "pipedream.com" +const frontendHost = process.env.NEXT_PUBLIC_PIPEDREAM_FRONTEND_HOST || "pipedream.com" +const apiHost = process.env.NEXT_PUBLIC_PIPEDREAM_API_HOST || "api.pipedream.com" export default function Home() { // Core Pipedream Connect state @@ -62,6 +63,7 @@ export default function Home() { const { createFrontendClient } = await import('@pipedream/sdk/browser'); const client = createFrontendClient({ frontendHost, + apiHost, externalUserId, token, tokenCallback: async () => { @@ -477,7 +479,7 @@ export default function Home() { }, [searchTimeout]); return ( -
+
{externalUserId && (

Pipedream Connect: Managed Auth Demo App

diff --git a/managed-auth-basic-next-app/app/server.ts b/managed-auth-basic-next-app/app/server.ts index 96fce58..674039d 100644 --- a/managed-auth-basic-next-app/app/server.ts +++ b/managed-auth-basic-next-app/app/server.ts @@ -10,7 +10,7 @@ import { } from "@pipedream/sdk/server"; const { - PIPEDREAM_PROJECT_ID, + NEXT_PUBLIC_PIPEDREAM_PROJECT_ID, PIPEDREAM_CLIENT_ID, PIPEDREAM_CLIENT_SECRET, PIPEDREAM_PROJECT_ENVIRONMENT, @@ -21,13 +21,13 @@ if (!PIPEDREAM_CLIENT_ID) throw new Error("PIPEDREAM_CLIENT_ID not set in environment"); if (!PIPEDREAM_CLIENT_SECRET) throw new Error("PIPEDREAM_CLIENT_SECRET not set in environment"); -if (!PIPEDREAM_PROJECT_ID) +if (!NEXT_PUBLIC_PIPEDREAM_PROJECT_ID) throw new Error("PIPEDREAM_PROJECT_ID not set in environment"); if (!PIPEDREAM_PROJECT_ENVIRONMENT || !["development", "production"].includes(PIPEDREAM_PROJECT_ENVIRONMENT)) throw new Error("PIPEDREAM_PROJECT_ENVIRONMENT not set in environment"); const pd = new PipedreamClient({ - projectId: PIPEDREAM_PROJECT_ID, + projectId: NEXT_PUBLIC_PIPEDREAM_PROJECT_ID, projectEnvironment: PIPEDREAM_PROJECT_ENVIRONMENT as "development" | "production", clientId: PIPEDREAM_CLIENT_ID, clientSecret: PIPEDREAM_CLIENT_SECRET, From 3d4e692b7c40f5759aca82053766d33faa742776 Mon Sep 17 00:00:00 2001 From: Jay Vercellone Date: Fri, 10 Oct 2025 12:35:21 -0300 Subject: [PATCH 2/8] Upgrade SDK and `connect-react` (#64) This change upgrades the major versions of these packages * `@pipedream/sdk` * `@pipedream/connect-react` It also fixes a few pre-existing type issues since they were related to the new exported types by both packages. The pre-existing functionality should not be affected. --- connect-react-demo/.env.example | 3 + connect-react-demo/.gitignore | 1 + .../app/actions/backendClient.ts | 8 +- .../app/components/ClientWrapper.tsx | 29 +- .../app/components/CodeExample.tsx | 48 +- .../app/components/ComponentTypeSelector.tsx | 21 +- .../app/components/ConfigPanel.tsx | 68 +- .../app/components/DemoPanel.tsx | 55 +- .../config/CustomizationSection.tsx | 2 - .../components/config/FormSettingsSection.tsx | 2 - .../customization-select/CustomLabel.tsx | 9 +- .../customization-select/blue-theme.ts | 2 +- .../customization-select/dark-theme.ts | 4 +- .../customization-select/default-unstyled.ts | 4 +- connect-react-demo/lib/app-state.tsx | 54 +- connect-react-demo/lib/backend-client.ts | 31 +- connect-react-demo/lib/code-templates.ts | 54 +- connect-react-demo/lib/env.ts | 2 + connect-react-demo/lib/sdk-logger.tsx | 89 +- connect-react-demo/lib/types/pipedream.ts | 14 +- .../lib/utils/type-descriptions.ts | 4 +- connect-react-demo/next.config.mjs | 2 +- connect-react-demo/package-lock.json | 5699 ----------------- connect-react-demo/package.json | 10 +- connect-react-demo/pnpm-lock.yaml | 269 +- connect-react-demo/tsconfig.tsbuildinfo | 1 - 26 files changed, 397 insertions(+), 6088 deletions(-) delete mode 100644 connect-react-demo/package-lock.json delete mode 100644 connect-react-demo/tsconfig.tsbuildinfo diff --git a/connect-react-demo/.env.example b/connect-react-demo/.env.example index 0a0dfb8..c843618 100644 --- a/connect-react-demo/.env.example +++ b/connect-react-demo/.env.example @@ -3,4 +3,7 @@ PIPEDREAM_CLIENT_SECRET= PIPEDREAM_PROJECT_ID= # Starts with 'proj_', available in your project settings PIPEDREAM_PROJECT_ENVIRONMENT=development PIPEDREAM_ALLOWED_ORIGINS='["https://example.com/", "http://localhost:3000/"]' + +NEXT_PUBLIC_PIPEDREAM_PROJECT_ENVIRONMENT=development # Same as PIPEDREAM_PROJECT_ENVIRONMENT + #NEXT_PUBLIC_EXTERNAL_USER_ID= # Set a static external-user-id here for easier debugging diff --git a/connect-react-demo/.gitignore b/connect-react-demo/.gitignore index 9d2d443..f8ad7d8 100644 --- a/connect-react-demo/.gitignore +++ b/connect-react-demo/.gitignore @@ -2,3 +2,4 @@ node_modules .env* !.env.example +tsconfig.tsbuildinfo diff --git a/connect-react-demo/app/actions/backendClient.ts b/connect-react-demo/app/actions/backendClient.ts index 794169a..fa92751 100644 --- a/connect-react-demo/app/actions/backendClient.ts +++ b/connect-react-demo/app/actions/backendClient.ts @@ -22,10 +22,10 @@ const allowedOrigins = ([ const _fetchToken = async (opts: FetchTokenOpts) => { const serverClient = backendClient() - const resp = await serverClient.createConnectToken({ - external_user_id: opts.externalUserId, - allowed_origins: allowedOrigins, // TODO set this to the correct origin - webhook_uri: process.env.PIPEDREAM_CONNECT_WEBHOOK_URI, + const resp = await serverClient.tokens.create({ + externalUserId: opts.externalUserId, + allowedOrigins: allowedOrigins, // TODO set this to the correct origin + webhookUri: process.env.PIPEDREAM_CONNECT_WEBHOOK_URI, }); return resp } diff --git a/connect-react-demo/app/components/ClientWrapper.tsx b/connect-react-demo/app/components/ClientWrapper.tsx index ea32490..8091691 100644 --- a/connect-react-demo/app/components/ClientWrapper.tsx +++ b/connect-react-demo/app/components/ClientWrapper.tsx @@ -3,7 +3,11 @@ import { AppStateProvider } from "@/lib/app-state" import { useStableUuid } from "@/lib/stable-uuid" import { FrontendClientProvider } from "@pipedream/connect-react" -import { createFrontendClient } from "@pipedream/sdk/browser" +import { + createFrontendClient, + type PipedreamEnvironment, + type ProjectEnvironment +} from "@pipedream/sdk/browser" import { fetchToken } from "../actions/backendClient" import { SDKLoggerProvider, useSDKLogger, createLoggedFrontendClient } from "@/lib/sdk-logger" import Demo from "./Demo" @@ -15,25 +19,30 @@ const ClientProviderWithLogger = () => { const [externalUserId] = useStableUuid() const logger = useSDKLogger() + const frontendHost = process.env.NEXT_PUBLIC_PIPEDREAM_FRONTEND_HOST + const environment = process.env.NEXT_PUBLIC_PIPEDREAM_ENVIRONMENT as PipedreamEnvironment + const projectEnvironment = process.env.NEXT_PUBLIC_PIPEDREAM_PROJECT_ENVIRONMENT as ProjectEnvironment + const client = externalUserId ? createLoggedFrontendClient( createFrontendClient({ - ...(process.env.NEXT_PUBLIC_PIPEDREAM_API_HOST && { apiHost: process.env.NEXT_PUBLIC_PIPEDREAM_API_HOST }), - environment: process.env.PIPEDREAM_PROJECT_ENVIRONMENT, + frontendHost, + environment, + projectEnvironment, tokenCallback: fetchToken, externalUserId, }), logger ) : null + if (!client) { + return + } + return ( - {client ? ( - - - - ) : ( - - )} + + + ); } diff --git a/connect-react-demo/app/components/CodeExample.tsx b/connect-react-demo/app/components/CodeExample.tsx index 4da8d50..da6e4d2 100644 --- a/connect-react-demo/app/components/CodeExample.tsx +++ b/connect-react-demo/app/components/CodeExample.tsx @@ -2,7 +2,7 @@ import { useState } from "react" import { Button } from "@/components/ui/button" -import { +import { Collapsible, CollapsibleContent, CollapsibleTrigger, @@ -54,8 +54,8 @@ export function CodeExample({ title, description, children, className }: CodeExa return ( -
- - +
{children} @@ -84,12 +84,12 @@ export function CodeExample({ title, description, children, className }: CodeExa } export function LiveCodeExamples() { - const { - selectedComponentKey, - selectedComponentType, + const { + selectedComponentKey, + selectedComponentType, externalUserId, configuredProps, - webhookUrl + webhookUrl } = useAppState() const [activeFileTab, setActiveFileTab] = useState("app") @@ -122,7 +122,7 @@ import { createFrontendClient } from "@pipedream/sdk/browser" export function ClientProvider({ children }: { children: React.ReactNode }) { const client = createFrontendClient({ - environment: process.env.NEXT_PUBLIC_PIPEDREAM_ENVIRONMENT, + projectEnvironment: process.env.NEXT_PUBLIC_PIPEDREAM_PROJECT_ENVIRONMENT, tokenCallback: async () => { const response = await fetch('https://github.com/api/connect/token') const { token } = await response.json() @@ -152,19 +152,19 @@ export default function IntegrationsPage() { const handleComponentSubmit = async (ctx) => { try { - ${selectedComponentType === "action" ? - `const result = await frontendClient.actionRun({ + ${selectedComponentType === "action" ? + `const result = await frontendClient.actions.run({ externalUserId: "user-${externalUserId}", - actionId: "${selectedComponentKey}", + id: "${selectedComponentKey}", configuredProps: ctx.configuredProps, - })` : - `const result = await frontendClient.deployTrigger({ + })` : + `const result = await frontendClient.triggers.deploy({ externalUserId: "user-${externalUserId}", - triggerId: "${selectedComponentKey}", + id: "${selectedComponentKey}", configuredProps: ctx.configuredProps, webhookUrl: "https://your-app.com/api/webhook", })`} - + console.log('Success:', result) // Handle success (show toast, redirect, etc.) } catch (error) { @@ -176,7 +176,7 @@ export default function IntegrationsPage() { return (

Connect Your Apps

- +
- + {/* File tabs */}
- + {getFileContent(activeFileTab)}
) -} \ No newline at end of file +} diff --git a/connect-react-demo/app/components/ComponentTypeSelector.tsx b/connect-react-demo/app/components/ComponentTypeSelector.tsx index 4930592..1021343 100644 --- a/connect-react-demo/app/components/ComponentTypeSelector.tsx +++ b/connect-react-demo/app/components/ComponentTypeSelector.tsx @@ -1,22 +1,23 @@ import { cn } from "@/lib/utils" import { IoCubeSharp, IoFlashOutline } from "react-icons/io5" import { TOGGLE_STYLES } from "@/lib/constants/ui" +import { ComponentType } from "@pipedream/sdk" interface ComponentTypeSelectorProps { - selectedType: "action" | "trigger" - onTypeChange: (type: "action" | "trigger") => void + selectedType: ComponentType + onTypeChange: (type: ComponentType) => void } const COMPONENT_TYPES = [ - { - value: "action", - label: "Action", + { + value: "action", + label: "Action", icon: IoCubeSharp, description: "Perform read and write API operations" }, - { - value: "trigger", - label: "Trigger", + { + value: "trigger", + label: "Trigger", icon: IoFlashOutline, description: "React to events and webhooks" }, @@ -46,10 +47,10 @@ export function ComponentTypeSelector({ selectedType, onTypeChange }: ComponentT ))} - +
{COMPONENT_TYPES.find(type => type.value === selectedType)?.description}
) -} \ No newline at end of file +} diff --git a/connect-react-demo/app/components/ConfigPanel.tsx b/connect-react-demo/app/components/ConfigPanel.tsx index fc12b43..f37b084 100644 --- a/connect-react-demo/app/components/ConfigPanel.tsx +++ b/connect-react-demo/app/components/ConfigPanel.tsx @@ -19,9 +19,9 @@ import { useAppState } from "@/lib/app-state" import { cn } from "@/lib/utils" import Select from "react-select" import { IoChevronDown, IoSettingsOutline } from "react-icons/io5" -import type { ConfigurableProp } from "../../lib/types/pipedream" import type { CSSObjectWithLabel } from "react-select" import { getTypeDescription } from "../../lib/utils/type-descriptions" +import { ComponentType, ConfigurablePropType } from "@pipedream/sdk" const typeBadgeStyles = { string: @@ -36,7 +36,7 @@ const typeBadgeStyles = { interface PropertyItemProps { name: string - type: string + type: ConfigurablePropType description: string required?: boolean children: React.ReactNode @@ -186,15 +186,15 @@ export const ConfigPanel = () => { const id1 = useId(); const id2 = useId(); const [showAdvanced, setShowAdvanced] = useState(false); - + // Local state for immediate UI updates, separate from router state const [localPropNames, setLocalPropNames] = useState(propNames) - + // Sync router state to local state useEffect(() => { setLocalPropNames(propNames) }, [propNames]) - + // Debounced sync from local state to router useEffect(() => { const timer = setTimeout(() => { @@ -209,8 +209,9 @@ export const ConfigPanel = () => { }, [localPropNames, propNames, setPropNames]) // Fetch component details to get configurable_props for the propNames dropdown - const { component, isLoading, error } = useComponent({ + const { component, isLoading, error } = useComponent({ key: selectedComponent?.key || '', + }, { useQueryOpts: { enabled: !!selectedComponent?.key } @@ -259,8 +260,8 @@ export const ConfigPanel = () => { zIndex: 99999, position: 'fixed', }), - menuPortal: (base: CSSObjectWithLabel) => ({ - ...base, + menuPortal: (base: CSSObjectWithLabel) => ({ + ...base, zIndex: 99999, position: 'fixed', }), @@ -328,7 +329,7 @@ export const ConfigPanel = () => {
- @@ -345,16 +346,16 @@ export const ConfigPanel = () => { value={selectedApp} onChange={(app) => { app - ? setSelectedAppSlug(app.name_slug) + ? setSelectedAppSlug(app.nameSlug) : removeSelectedAppSlug() }} /> @@ -394,18 +395,17 @@ export const ConfigPanel = () => { } }} placeholder="https://example.com/webhook" - className={`w-full px-3 py-1.5 text-sm font-mono border-2 ${ - shouldShowWebhookUrlError() - ? "border-red-500" - : webhookUrl - ? "border-gray-200" + className={`w-full px-3 py-1.5 text-sm font-mono border-2 ${shouldShowWebhookUrlError() + ? "border-red-500" + : webhookUrl + ? "border-gray-200" : "border-blue-500" - } rounded bg-zinc-50/50 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500`} + } rounded bg-zinc-50/50 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500`} /> {shouldShowWebhookUrlError() && (

- {!webhookUrl - ? "Webhook URL is required for triggers" + {!webhookUrl + ? "Webhook URL is required for triggers" : "Please enter a valid URL" }

@@ -449,11 +449,11 @@ export const ConfigPanel = () => { />
{