Skip to content

Conversation

jverce
Copy link
Contributor

@jverce jverce commented Aug 26, 2025

Changelog

  • Configure Vite to abort if there are compile-time errors
  • Use explicit types from the Connect SDK (i.e. @pipedream/sdk) to make the v2 migration easier
  • Fix pre-existing compile-time errors
  • Remove unused utility functions

WHY

The purpose of this change is to prepare the connect-react package to be migrated to the new SDK by backfilling type annotations that relate to the current SDK. This change should not cause runtime behaviour changes, but it does include some minor refactors that helped me get to a point were there are no compile-time errors.

The very first step that I took was to make the build process fail in the case of compile-time errors. In order to speed up the development and release of this package, we made the decision to allow these errors as long as they did not affect the runtime behaviour. This PR intends to solve all these existing errors, while preventing new ones from showing up in the future.

The other important change here is to explicitly annotate variables and functions that relate to the current SDK, so as to let the compiler detect which parts of the code we must migrate, and check if we're doing it correctly. This is not infallible but it's a good step forward. For types that the SDK does not export, I redefined them in a single file called src/types.ts (it already existed) so that we don't forget about replacing those.

Last but not least, there was some Claude-generated code in src/utils/type-guards.ts that was either unused or unnecessarily complicated. I cleaned up as much as possible, and reutilized the sanitizeOption function defined in there in other parts of the code that did basically the same thing. This helped me check that we were processing the right type of data while making the code (c)leaner.

Summary by CodeRabbit

  • New Features

    • Select existing or connect a new account from the dropdown; newly connected account is refreshed and auto-selected.
    • Exposed select customization and consistent label/value option handling across controls.
    • Creatable and multi-select flows now produce normalized, sanitized options.
  • Bug Fixes

    • Improved SDK error parsing with clearer, prop-linked error alerts.
    • Safer account retrieval/selection and more reliable remote-options loading.
  • Chores

    • TypeScript build now fails on TS errors; package bumped and lint scripts added.
    • Removed Next.js example and related example files.

* Configure Vite to abort if there are compile-time errors
* Use explicit types from the Connect SDK (i.e. `@pipedream/sdk`) to
make the v2 migration easier
* Fix pre-existing compile-time errors
@jverce jverce self-assigned this Aug 26, 2025
@jverce jverce added enhancement New feature or request javascript Pull requests that update Javascript code tracked internally Issue is also tracked in our internal issue tracker prioritized Prioritized issue labels Aug 26, 2025
@jverce jverce requested a review from Copilot August 26, 2025 20:01
Copy link

vercel bot commented Aug 26, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
pipedream-docs Ignored Ignored Aug 27, 2025 9:32pm
pipedream-docs-redirect-do-not-edit Ignored Ignored Aug 27, 2025 9:32pm

Copy link
Contributor

coderabbitai bot commented Aug 26, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Standardizes select option shapes to LabelValueOption, broadens react-select customization generics, tightens form/field generics, migrates remote options to configureComponent with sanitized paging, introduces structured Observation/DynamicProps types, adds connect-account flow, fails build on TypeScript diagnostics, and removes the Next.js example.

Changes

Cohort / File(s) Summary
Select options & guards
packages/connect-react/src/components/ControlSelect.tsx, packages/connect-react/src/components/Control.tsx, packages/connect-react/src/utils/type-guards.ts
Canonicalize option shapes to LabelValueOption<T>; replace ad-hoc normalization with sanitizeOption and isOptionWithLabel; update public props and internal flows to use LabelValueOption[].
React-select customization
packages/connect-react/src/hooks/customization-context.tsx
Make customization APIs fully generic (Option / IsMulti / Group); generalize BaseReactSelectProps and Customization helpers (getProps, getStyles, getComponents, getClassNames) to accept and return generics.
Account selection & hook typing
packages/connect-react/src/components/ControlApp.tsx, packages/connect-react/src/hooks/use-accounts.tsx
Add "Connect new X account" placeholder and startConnectAccount flow; switch to App/Account browser types; use GetAccountsResponse typing; use external_user_id and refetch accounts after connect.
Form/field generics and container
packages/connect-react/src/components/ComponentForm.tsx, packages/connect-react/src/components/ComponentFormContainer.tsx, packages/connect-react/src/components/ControlSubmit.tsx, packages/connect-react/src/components/Field.tsx, packages/connect-react/src/components/Errors.tsx, packages/connect-react/src/components/InternalField.tsx, packages/connect-react/src/hooks/form-field-context.tsx
Tighten generics across FormContext/FieldProps/ControlSubmit; enforce exclusive externalUserId vs userId at type level; replace some AppResponseApp in extras; include prop name in error alerts; add a ts-expect-error for suspense assignment.
DynamicProps, observations & types
packages/connect-react/src/hooks/form-context.tsx, packages/connect-react/src/types.ts
Replace local generic DynamicProps<T> with shared DynamicProps; introduce ObservationBase/ObservationError/ObservationLog and ObservationErrorDetails; add LabelValueOption/RawPropOption; extend configure/reload responses with observations; realign sdkResponse/error parsing to typed shapes.
Remote options / configureComponent migration
packages/connect-react/src/components/RemoteOptionsContainer.tsx
Migrate to configureComponent/ConfigureComponentOpts; type pageable state (page, prevContext, data, values); sanitize and dedupe RawPropOption responses via sanitizeOption and expose LabelValueOption[] to UI; preserve paging and error handling.
Utilities
packages/connect-react/src/utils/component.ts
Read __lv via a typed cast (NestedLabelValueOption<T>) to tighten TypeScript checks while preserving runtime behavior of valuesFromOptions.
Type guards overhaul
packages/connect-react/src/utils/type-guards.ts
Remove old OptionWithValue and helpers; add isOptionWithLabel and sanitizeOption<T>(option: RawPropOption<T>): LabelValueOption<T>; switch imports to new public types.
Control submit & field typing
packages/connect-react/src/components/ControlSubmit.tsx, packages/connect-react/src/components/Field.tsx
Propagate generics: ControlSubmit and Field components now accept generic T extends ConfigurableProps and use FormContext<T> / FormContext<T[]> respectively; ensure boolean coercion for disabled/styles.
Build / config
packages/connect-react/tsconfig.json, packages/connect-react/vite.config.mts, packages/connect-react/package.json
Change tsconfig module to preserve; add afterDiagnostic to vite-plugin-dts to fail build on TypeScript diagnostics; bump package to 1.6.0; add lint scripts.
Examples removed
packages/connect-react/examples/nextjs/* (many files deleted, e.g. package.json, src/app/*, tsconfig.json, .gitignore, Makefile, README.md, next.config.ts)
Remove the Next.js example and its artifacts (page, layout, actions, config, package manifest, docs, and supporting files).

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant ControlApp
  participant AccountsAPI as client.getAccounts
  participant Connector as client.connectAccount
  participant State as React State

  User->>ControlApp: open/select account
  ControlApp->>AccountsAPI: fetch accounts (external_user_id)
  AccountsAPI-->>ControlApp: accounts list
  ControlApp->>State: set options = [accounts..., "Connect new ..."]
  User->>ControlApp: select option
  alt selects existing account
    ControlApp->>State: set authProvisionId
  else selects "Connect new"
    ControlApp->>Connector: startConnectAccount()
    Connector-->>ControlApp: success (provision id)
    ControlApp->>AccountsAPI: refetch accounts
    AccountsAPI-->>ControlApp: updated accounts
    ControlApp->>State: set authProvisionId to new id
  else clears selection
    ControlApp->>State: set authProvisionId = undefined
  end
Loading
sequenceDiagram
  autonumber
  participant RemoteOptions as RemoteOptionsContainer
  participant SDK as client.configureComponent
  participant UI as ControlSelect

  RemoteOptions->>SDK: configureComponent(input, context)
  SDK-->>RemoteOptions: { data: RawPropOption<string>[], observations }
  RemoteOptions->>RemoteOptions: dedupe values (Set)
  RemoteOptions->>RemoteOptions: map sanitizeOption -> LabelValueOption[]
  RemoteOptions-->>UI: options (LabelValueOption[])
  opt load more
    UI->>RemoteOptions: request next page
    RemoteOptions->>SDK: configureComponent(next context)
    SDK-->>RemoteOptions: next data
    RemoteOptions->>UI: append sanitized options
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • dylburger

Poem

I hopped through options, labels in tow,
Wrapped values tidy so select boxes glow.
I nudged accounts to connect and play,
Observations now whisper what went astray.
Tiny paws fixed types — code ready to hop away. 🥕🐇

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes build issues and prepares the connect-react package for SDK v2 migration by adding type safety and error handling. It configures Vite to fail on TypeScript errors, uses explicit SDK types for easier migration, and cleans up unused utility functions.

  • Configure Vite to abort on TypeScript compilation errors
  • Add explicit type annotations using current SDK types to facilitate v2 migration
  • Fix pre-existing compile-time errors and clean up unused utilities

Reviewed Changes

Copilot reviewed 19 out of 20 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
vite.config.mts Configure build to fail on TypeScript errors and fix quote consistency
tsconfig.json Update module setting for better compatibility
src/types.ts Add comprehensive type definitions for observations, options, and SDK responses
src/utils/type-guards.ts Simplify and fix type guard functions, remove unused utilities
src/utils/component.ts Fix type assertion for __lv property access
src/hooks/*.tsx Update to use explicit SDK types and fix type errors
src/components/*.tsx Fix component typing issues and improve type safety
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
packages/connect-react/src/utils/component.ts (1)

170-187: Ensure validation covers both missing and falsy authProvisionId

The current check only runs validation when the authProvisionId property exists but is falsy, so any truly missing property slips through unchecked. We should normalize “absent or falsy” into one condition:

• File: packages/connect-react/src/utils/component.ts
Lines: 170–187

Apply this diff:

-  if ("authProvisionId" in _value && !_value.authProvisionId) {
+  const hasProvisionId = "authProvisionId" in _value && Boolean((_value as any).authProvisionId)
+  if (!hasProvisionId) {
     const errs = []
     if (app.auth_type === "oauth" && !(_value as OauthAppPropValue).oauth_access_token) {
       errs.push("missing oauth token")
     }
     if (app.auth_type === "oauth" || app.auth_type === "keys") {
       const customFields = getCustomFields(app)
       const _valueWithCustomFields = _value as AppPropValueWithCustomFields<typeof customFields>
       for (const cf of customFields) {
         if (!cf.optional && !_valueWithCustomFields[cf.name]) {
           errs.push(`missing custom field: ${cf.name}`)
         }
       }
     }

     return errs
   }

This change flags both omitted and empty authProvisionId consistently.

packages/connect-react/src/components/Errors.tsx (1)

3-5: Fix SDK import resolution and remove deprecated “/browser” imports

  • Dependency check
    packages/connect-react/package.json already declares @pipedream/sdk as a workspace dependency—no further changes needed there.

  • Type-only import in Errors.tsx
    In packages/connect-react/src/components/Errors.tsx, update the SDK imports to be type-only to prevent Vite from bundling runtime code:

    -import {
    -  ConfigurableProp, ConfigurablePropAlert, ConfigurableProps,
    -} from "@pipedream/sdk";
    +import type {
    +  ConfigurableProp, ConfigurablePropAlert, ConfigurableProps,
    +} from "@pipedream/sdk";
  • Remove or replace all @pipedream/sdk/browser imports
    The following files currently import from the internal /browser entrypoint, which breaks resolution. Replace each one with either:

    1. A type-only import from the public SDK package (import type { … } from "@pipedream/sdk"), or
    2. A proper runtime import from "@pipedream/sdk" if you need to pull in executable code.

    packages/connect-react/src/types.ts:4
    packages/connect-react/src/hooks/use-accounts.tsx:6
    packages/connect-react/src/hooks/form-field-context.tsx:7
    packages/connect-react/src/hooks/frontend-client-context.tsx:7
    packages/connect-react/src/components/ControlApp.tsx:15
    packages/connect-react/src/components/RemoteOptionsContainer.tsx:1
    packages/connect-react/src/components/ControlSubmit.tsx:4
    packages/connect-react/README.md:77, 119
    packages/connect-react/examples/nextjs/src/app/page.tsx:4

Ensure that all type-only imports use the import type { … } syntax so Vite/tree-shaking doesn’t bundle unintended code, and that any runtime APIs come from the correct public module.

packages/connect-react/src/components/Control.tsx (1)

4-6: Use type-only import for SDK types

Importing types from @pipedream/sdk as a value import pulls in the entire runtime SDK and increases bundle size. Please switch to a type-only import to ensure these are erased at compile time and tree-shaken from the final build. We’ve confirmed that @pipedream/sdk is already declared in packages/connect-react/package.json.

File to update:
packages/connect-react/src/components/Control.tsx

Verified dependency:
@pipedream/sdk appears on line 31 of packages/connect-react/package.json.

Suggested diff:

-import {
-  ConfigurableProp,
-  ConfigurableProps,
-} from "@pipedream/sdk";
+import type { ConfigurableProp, ConfigurableProps } from "@pipedream/sdk";
packages/connect-react/src/hooks/form-context.tsx (1)

433-441: Avoid mutating React state in place in registerField.

setFields mutates the existing object and returns the same reference, which can skip re-renders and cause subtle bugs.

Return a new object:

   const registerField = <T extends ConfigurableProp>(field: FormFieldContext<T>) => {
-    setFields((fields) => {
-      fields[field.prop.name] = field
-      return fields
-    });
+    setFields((prev) => ({
+      ...prev,
+      [field.prop.name]: field,
+    }));
     checkPropsNeedConfiguring()
   };
♻️ Duplicate comments (5)
packages/connect-react/src/components/InternalField.tsx (1)

31-33: Remove @ts-ignore and type ‘suspense’ correctly on useQueryOpts.

Suppressing with @ts-ignore undermines the “abort on compile errors” goal and can hide real regressions. Let’s assert the exact option shape (keeping runtime identical) and drop the ignore.

Option A — extract and validate with satisfies (preferred; TS ≥ 4.9):

// above the useApp() call
const queryOpts = {
  enabled: !!appSlug,
  ...(appSlug ? { suspense: true } : {}),
} satisfies (typeof formCtx)['useQueryOpts'] & { suspense?: boolean };

// ...
const { app } = useApp(appSlug || "", { useQueryOpts: queryOpts });

Option B — inline assertion (if you’d rather not introduce a local):

-    useQueryOpts: {
-      enabled: !!appSlug,
-      // this seems to work (this overrides enabled so don't just set to true)
-      // @ts-ignore
-      suspense: !!appSlug,
-    },
+    useQueryOpts: {
+      enabled: !!appSlug,
+      // enable suspense only when we actually have an appSlug
+      suspense: !!appSlug,
+    } as (typeof formCtx)['useQueryOpts'] & { suspense: boolean },

Note: “suspense overrides enabled” is not generally accurate with TanStack Query; enabled still gates fetching. If the intent is “only use suspense when enabled would be true,” Option A models that explicitly.

If TS complains about the indexed access type, confirm that formCtx exposes useQueryOpts at the type level or switch to the hook’s option type: NonNullable<Parameters<typeof useApp>[1]>['useQueryOpts'].

packages/connect-react/src/components/ControlApp.tsx (1)

96-99: Remove @ts-ignore by allowing suspense in use-accounts opts.

This duplicates an earlier review comment. After updating the opts type in use-accounts to include suspense, you can drop the ts-ignore here.

-        // this seems to work (this overrides enabled so don't just set to true)
-        // @ts-ignore
-        suspense: !!app,
+        // this seems to work (this overrides enabled so don't just set to true)
+        suspense: !!app,
packages/connect-react/src/components/Errors.tsx (1)

32-39: LGTM: carry prop.name into alerts for context

Including name: prop.name on each ConfigurablePropAlert helps downstream renderers/logging. Based on the SDK types, ConfigurablePropAlert extends the base prop with name, so this is type-safe.

packages/connect-react/src/hooks/form-context.tsx (1)

501-509: Type error details instead of using 'any'.

You can avoid the any by typing the error detail payload, aligning with your public types. This also addresses a previously raised concern.

-    const errorFromObservationErrorDetails = (item: ObservationErrorDetails, ret: SdkError[]) => {
+    const errorFromObservationErrorDetails = (item: ObservationErrorDetails, ret: SdkError[]) => {
       const err: SdkError = { name: item.name, message: item.message }
       if (err.name && err.message) ret.push(err)
     }
@@
-    const errorFromDetails = (data: any, ret: SdkError[]) => {
-      ret.push({ name: data.error, message: JSON.stringify(data.details) })
-    }
+    interface ErrorDetailsData { error: string; details: unknown }
+    const errorFromDetails = (data: ErrorDetailsData, ret: SdkError[]) => {
+      if (data?.error) {
+        ret.push({ name: data.error, message: JSON.stringify(data.details) })
+      }
+    }

Also applies to: 532-543

packages/connect-react/src/hooks/customization-context.tsx (1)

18-18: Fix TS2307: Cannot find '@pipedream/sdk' types (same root cause).

This file also imports ConfigurableProp from @pipedream/sdk. Apply the same workspace dependency or tsconfig “paths” mapping fix suggested in form-context.tsx.

#!/bin/bash
rg -nP --type=ts "\bfrom\s+['\"]@pipedream/sdk['\"]" -C1 packages/connect-react/src
🧹 Nitpick comments (39)
packages/connect-react/src/utils/component.ts (5)

59-67: Dead branch: valuesFromOptions never returns undefined

valuesFromOptions always returns an array, so typeof _values === "undefined" is unreachable. Either (a) adjust valuesFromOptions to return undefined for nullish inputs, or (b) simplify this check to rely on array length only. Given the PR’s “non-behavioral” intent, I recommend (b).

-  if (!opts.prop.default && typeof _values === "undefined") {
-    return [
-      "required",
-    ]
-  }
-  if (!opts.prop.default && Array.isArray(_values) && !_values.length) return [
+  if (!opts.prop.default && Array.isArray(_values) && _values.length === 0) return [
     "empty array",
   ]

70-74: Simplify null/undefined check

_value == null already covers null | undefined. The extra typeof _value === "undefined" is redundant.

-  if (_value == null || typeof _value === "undefined") return [
+  if (_value == null) return [
     "required",
   ]

86-97: Parse integers in base 10 and return undefined when no errors

  • Use radix 10 with parseInt to avoid legacy octal quirks.
  • Return undefined when there are no errors to match the function’s declared return type shape and be consistent with the other validators.
-  const _value: number = typeof value === "number"
-    ? value
-    : parseInt(String(value))
+  const _value: number = typeof value === "number"
+    ? value
+    : parseInt(String(value), 10)

   if (Number.isNaN(_value)) return [
     "not a number",
   ]
   const errs = []
   if (typeof prop.min === "number" && _value < prop.min) errs.push("number too small")
   if (typeof prop.max === "number" && _value > prop.max) errs.push("number too big")
-  return errs
+  return errs.length ? errs : undefined

99-107: Unwrap option values for consistency with other validators

For string props, use valueFromOption to keep behavior consistent with boolean/integer validators and to support wrapped values.

   const {
     prop, value: valueOpt,
   } = opts

-  if (!prop.default && (valueOpt == null || typeof valueOpt === "undefined")) return [
+  const _value = valueFromOption(valueOpt)
+  if (!prop.default && (_value == null)) return [
     "required",
   ]

126-144: Harden custom fields parsing

If custom_fields_json is invalid JSON, JSON.parse will throw and abort validation. Wrap in try/catch and fall back to an empty array.

-  const userDefinedCustomFields = JSON.parse(app.custom_fields_json || "[]")
+  let userDefinedCustomFields: AppCustomField[] = []
+  try {
+    userDefinedCustomFields = JSON.parse(app.custom_fields_json || "[]")
+  } catch {
+    userDefinedCustomFields = []
+  }
packages/connect-react/src/components/InternalField.tsx (1)

48-49: Ensure FormFieldContext.extra is typed to include ‘app’ (and allow undefined).

You’re now always setting extra: { app }. If appSlug is falsy, app will be undefined. Confirm FormFieldContext<T>['extra'] permits this shape; otherwise consumers may see type friction.

Two options:

  • Make app optional at the type level for app fields, or
  • Only add the key when available.

Minimal runtime-neutral tweak:

-    extra: {
-      app,
-    },
+    extra: app ? { app } : {},

If you prefer keeping the key present, update the type to extra: { app?: ReturnType<typeof useApp>['app'] } (or a narrower SDK type) so consumers don’t need non-null assertions.

packages/connect-react/vite.config.mts (2)

34-36: Consider deduping react and react-dom as well.

This avoids duplicate React copies in consumer bundles in some setups.

-    dedupe: [
-      "@emotion/react",
-    ],
+    dedupe: [
+      "react",
+      "react-dom",
+      "@emotion/react",
+    ],

21-26: UMD globals: react/jsx-runtime usually shouldn't be a global.

For UMD, react/jsx-runtime is typically not referenced directly at runtime. You can omit it from globals and external, or map it to React if you keep it externalized.

       output: {
         globals: {
-          "react": "React",
-          "react-dom": "ReactDOM",
-          "react/jsx-runtime": "react/jsx-runtime",
-          "@emotion/react": "@emotion/react",
+          react: "React",
+          "react-dom": "ReactDOM",
+          "@emotion/react": "emotionReact",
         },
       },
packages/connect-react/src/hooks/use-accounts.tsx (1)

21-28: Minor: stabilize the queryKey to reduce unnecessary refetches.

Using the raw input object as a key can cause cache misses on identity changes. Prefer a stable tuple of primitive fields.

-  const query = useQuery<GetAccountsResponse>({
+  const { external_user_id, app: appSlug, oauth_app_id } = input;
+  const query = useQuery<GetAccountsResponse>({
     ...opts?.useQueryOpts,
-    queryKey: [
-      "accounts",
-      input,
-    ],
-    queryFn: () => client.getAccounts(input),
+    queryKey: ["accounts", external_user_id, appSlug, oauth_app_id],
+    queryFn: () => client.getAccounts({ external_user_id, app: appSlug, oauth_app_id }),
   });
packages/connect-react/src/components/ControlApp.tsx (4)

28-33: Use a discriminant to prevent id collisions.

If an Account with id "_new" ever existed, it would conflict. Add a discriminant to the placeholder.

-type AccountPlaceholder = {
-  id: "_new";
-  name: string;
-}
+type AccountPlaceholder = {
+  kind: "placeholder";
+  id: "_new";
+  name: string;
+}

And adjust the usage checks to also narrow on kind where applicable.


66-79: Prop typing for react-select is on the right track; make Select generic to tighten onChange.

Without generics, react-select’s onChange param often widens to unknown/any. Set the component generic to SelectValue.

-          <Select
+          <Select<SelectValue, false>
             instanceId={id}
             value={selectValue}
             options={selectOptions}
             {...selectProps}
-            required={true}
             placeholder={`Select ${app.name} account...`}

Also, react-select doesn’t honor HTML required; prefer aria-required for a11y if needed:

+            aria-required="true"

124-128: Include newAccountPlaceholder in deps to keep label in sync.

If app.name changes, the memoized options would keep an old label.

-  ], [accounts]);
+  ], [accounts, newAccountPlaceholder]);

129-142: Defaulting to the “Connect new …” option is a product choice; just flagging UX.

Selecting a default “connect new” may surprise users who have existing accounts. Consider leaving value undefined so the placeholder text shows until a user picks an account.

-  const selectValue = useMemo<SelectValue>(() => {
+  const selectValue = useMemo<SelectValue | null>(() => {
     if (value?.authProvisionId) {
       for (const item of accounts) {
         if (value.authProvisionId === item.id) {
           return item;
         }
       }
     }
 
-    return newAccountPlaceholder;
+    return null;
   }, [
     accounts,
     value,
   ]);
packages/connect-react/src/types.ts (1)

17-21: Make stack optional for cross-environment compatibility.

Error.stack can be undefined in some runtimes; making it optional prevents needless narrowing or non-null assertions.

 export type ObservationErrorDetails = {
     name: string;
     message: string;
-    stack: string;
+    stack?: string;
 }
packages/connect-react/src/components/Errors.tsx (1)

46-48: Prefer a stable key over the array index

Using idx as the key may cause unnecessary re-renders. Use a deterministic key from the alert itself.

-    return <>{formattedErrors.map((fe, idx: number) => <Alert prop={fe} key={idx}/>)}</>
+    return <>{formattedErrors.map((fe) => (
+      <Alert prop={fe} key={`${fe.name}:${fe.content}`} />
+    ))}</>
packages/connect-react/src/hooks/form-field-context.tsx (1)

9-12: Use a stricter empty type for the non-App branch

{} is very permissive. If the intent is “no extra fields”, prefer Record<never, never> to maintain a true empty shape.

-export type FormFieldContextExtra<T extends ConfigurableProp> = T extends ConfigurablePropApp ? {
-  app?: App;
-} : {};
+export type FormFieldContextExtra<T extends ConfigurableProp> =
+  T extends ConfigurablePropApp
+    ? { app?: App }
+    : Record<never, never>;
packages/connect-react/src/utils/type-guards.ts (2)

71-76: Null/undefined handling: avoid fabricating a string value of type T

Returning value: "" as T lies to the type system when T isn’t string. Prefer undefined to reflect “no value”.

   if (option === null || option === undefined) {
     return {
       label: "",
-      value: "" as T,
+      value: undefined as unknown as T,
     };
   }

Please confirm all call sites tolerate value: undefined for the “empty” case. If not, we can feature-flag this change or keep the current string fallback for now.


101-104: Prefer JSON stringification for object labels

String(option) on objects yields “[object Object]”, which is not user-friendly.

-  return {
-    label: String(option),
-    value: option,
-  };
+  return {
+    label: (() => {
+      try { return JSON.stringify(option); } catch { return String(option); }
+    })(),
+    value: option,
+  };
packages/connect-react/src/components/RemoteOptionsContainer.tsx (3)

52-63: Type newOptions to avoid “any” and drop the cast later

Give newOptions an explicit type and remove the downstream as RawPropOption<string>[].

-  ] = useState<{
+  ] = useState<{
     page: number;
     prevContext: ConfigureComponentContext | undefined;
     data: RawPropOption<string>[];
     values: Set<string | number>;
   }>({
@@
-      const newOptions = []
+      const newOptions: RawPropOption<string>[] = []
@@
-        data = [
+        data = [
           ...pageable.data,
           ...newOptions,
-        ] as RawPropOption<string>[]
+        ]

Also applies to: 145-171


214-219: Avoid stale refetches on input change

Calling refetch() immediately after setQuery(v) can refetch with the previous query value and then again when the key updates. Either rely on the query key change alone or debounce. Minimal change: remove the manual refetch() and let the key drive fetching.

         onInputChange(v) {
           if (prop.useQuery) {
             setQuery(v);
-            refetch();
           }
         },

Alternatively, debounce:

// outside JSX
const [localQuery, setLocalQuery] = useState("");
useEffect(() => {
  const id = setTimeout(() => setQuery(localQuery), 250);
  return () => clearTimeout(id);
}, [localQuery]);

// in selectProps
onInputChange(v) {
  if (prop.useQuery) setLocalQuery(v);
}

139-143: Style: tighten stringOptions mapping

Minor readability tweak.

-      if (stringOptions?.length) {
-        const options = [];
-        for (const stringOption of stringOptions) {
-          options.push({
-            label: stringOption,
-            value: stringOption,
-          });
-        }
-        _options = options;
-      }
+      if (stringOptions?.length) {
+        _options = stringOptions.map((s) => ({ label: s, value: s }));
+      }
packages/connect-react/src/components/Control.tsx (1)

71-73: Guard the non-null assertion on app

app! assumes extra.app is always present when prop.type === "app". If upstream context fails to set it, this will throw later. Prefer an explicit guard.

-  case "app":
-    return <ControlApp app={app!} />;
+  case "app":
+    if (!app) {
+      throw new Error("Missing App in FormFieldContext.extra for 'app' prop");
+    }
+    return <ControlApp app={app} />;

Please confirm the provider guarantees extra.app for ConfigurablePropApp fields.

packages/connect-react/src/components/ComponentFormContainer.tsx (2)

12-16: DRY the XOR type across components

The same externalUserId | userId XOR also exists in ComponentFormProps. To avoid drift, export a shared type alias (e.g., UserIdentityProps) from a central file (src/types.ts) and reuse it here and in ComponentForm.

-} & (
-    // Either externalUserId or userId must be provided
-    | { externalUserId: string; userId?: never }
-    | { userId: string; externalUserId?: never }
-  );
+} & UserIdentityProps;

43-45: Avoid forwarding componentKey to ComponentForm

componentKey isn’t part of ComponentFormProps and gets forwarded via the spread. While harmless at runtime, it weakens the prop boundary and may confuse consumers or trigger excess-prop checks.

-  return <ComponentForm component={component} {...props} />;
+  const { componentKey, ...rest } = props;
+  return <ComponentForm component={component} {...rest} />;
packages/connect-react/src/components/Field.tsx (2)

1-1: Type-only import and tighten FormContext generic

Use a type-only import for FormContext to avoid emitting value imports, and align the generic to the readonly nature of ConfigurableProps to better match the SDK’s typing (Readonly<ConfigurableProp[]>).

-import { FormContext } from "../hooks/form-context";
+import type { FormContext } from "../hooks/form-context";
@@
-export type FieldProps<T extends ConfigurableProp> = {
-  form: FormContext<T[]>;
+export type FieldProps<T extends ConfigurableProp> = {
+  form: FormContext<Readonly<T[]>>;

Also applies to: 9-9


62-62: Downcast in getProps third arg is a smell

Casting to FieldProps drops the specific T and can hide type issues. Prefer passing a minimal, untyped bag for customization or widen the getProps third-arg type to unknown.

-    <div {...getProps("field", baseStyles, props as FieldProps<ConfigurableProp>)}>
+    <div {...getProps("field", baseStyles, { form, field })}>

If getProps relies on the full props shape, consider overloading it to accept unknown and let consumers narrow.

packages/connect-react/src/components/ComponentForm.tsx (1)

39-41: Share the XOR identity type with the container to prevent drift

Same suggestion as in ComponentFormContainer: factor the union into a single exported UserIdentityProps and reuse here.

-} & (
-    | { externalUserId: string; userId?: never }
-    | { userId: string; externalUserId?: never }
-  );
+} & UserIdentityProps;
packages/connect-react/src/components/ControlSubmit.tsx (1)

43-47: Casting to ControlSubmitProps is unnecessary

getProps doesn’t need a precise third-arg type; the cast adds noise without safety. Prefer passing a minimal object or keep props uncast.

-      baseStyles(!!propsNeedConfiguring.length || submitting),
-      props as ControlSubmitProps<ConfigurableProps>
+      baseStyles(!!propsNeedConfiguring.length || submitting),
+      { form }
packages/connect-react/src/components/ControlSelect.tsx (2)

147-169: Creation path typing: avoid forcing inputValue to T

inputValue is string; forcing it to T risks mis-typing when T is non-string. Let sanitizeOption infer string and widen only at the boundary.

-  const handleCreate = (inputValue: string) => {
-    const newOption = sanitizeOption(inputValue as T)
+  const handleCreate = (inputValue: string) => {
+    const newOption = sanitizeOption<string>(inputValue)
     let newRawValue: LabelValueOption<T> | LabelValueOption<T>[] = newOption

If strict typing is needed, consider a second generic constrained to string values for creatable flows.


79-115: selectValue normalization logic is sound but can be simplified

This block is correct but dense. Consider extracting to a small pure helper normalizeSelectValue(rawValue, selectOptions) to improve readability and testability.

packages/connect-react/src/hooks/form-context.tsx (5)

189-197: Narrow result type from reloadComponentProps and centralize error extraction.

Casting to ReloadComponentPropsResponse is fine, but you immediately branch on observations/errors. Consider early-returns to cut duplication and ensure only one error source wins.

-      const { dynamicProps, observations, errors: __errors } = result as ReloadComponentPropsResponse;
-      // Prioritize errors from observations over the errors array
-      if (observations && observations.filter((o) => o.k === "error").length > 0) {
-        handleSdkErrors(observations)
-      } else {
-        handleSdkErrors(__errors)
-      }
+      const { dynamicProps, observations, errors: rawErrors } = result as ReloadComponentPropsResponse;
+      const hasObsErrors = Array.isArray(observations) && observations.some((o) => o.k === "error");
+      handleSdkErrors(hasObsErrors ? observations : rawErrors);

Simplifies the branch and avoids traversing observations twice.


221-240: Minimize casts and keep prop filtering strictly typed.

The temporary cast to typeof props is unnecessary after the ConfigurableProps memo typing. Also, the slice branch doesn’t need Array.isArray since props is always an array.

-      props = _configurableProps as typeof props; // XXX
+      props = _configurableProps;
@@
-    if (reloadPropIdx != null) {
-      props = Array.isArray(props)
-        ? props.slice(0, reloadPropIdx + 1) // eslint-disable-line react/prop-types
-        : props; // XXX
-    }
+    if (reloadPropIdx != null) {
+      props = props.slice(0, reloadPropIdx + 1);
+    }

341-346: sdkResponse side-effect: ensure type stability.

handleSdkErrors accepts multiple shapes, but the Form props’ sdkResponse type likely isn’t ConfiguredProps. Passing a plain configured-props object here will clear errors (the final else branch). If sdkResponse actually represents SDK error envelopes, retype the function parameter accordingly.

-  const handleSdkErrors = (sdkResponse?: ConfiguredProps<T> | Observation[] | string[]) => {
+  type SdkEnvelope =
+    | { observations: Observation[] }
+    | { os: Observation[] }
+    | Observation[]
+    | string[]
+    | Error;
+  const handleSdkErrors = (sdkResponse?: SdkEnvelope) => {

This matches the branches you already support and avoids accidental “error reset” when an object without error shape is passed.


545-560: Harden HTTP error parsing and observation routing.

Parsing Error.message as JSON is brittle. You already guard with try/catch; consider also checking for payload instanceof Error and preserving message when JSON parsing fails. Additionally, both os and observations branches delegate to the same extractor—good consolidation.

-    const errorFromHttpError = (payload: Error, ret: SdkError[]) => {
+    const errorFromHttpError = (payload: unknown, ret: SdkError[]) => {
       try {
-        const data = JSON.parse(payload.message)?.data
+        const msg = payload instanceof Error ? payload.message : String(payload);
+        const data = JSON.parse(msg)?.data
         if (data && "os" in data) {
           errorFromObservations(data.os, ret)
         } else if (data && "observations" in data) {
           errorFromObservations(data.observations, ret)
         } else if (data && "error" in data && "details" in data) {
-          errorFromDetails(data, ret)
+          errorFromDetails(data as { error: string; details: unknown }, ret)
         }
       } catch {
-        // pass
+        // fallback: keep raw message
+        ret.push({ name: "SDKError", message: payload instanceof Error ? payload.message : String(payload) })
       }
     }

Also applies to: 562-579


178-210: React Query usage: consider returning meaningful data or switching to mutation.

queryFn returns [] and you rely on side effects to update state. Since you gate with enabled: reloadPropIdx != null, this is acceptable, but a mutation (useMutation) could better represent “reload props” semantics and avoid empty query data.

If staying on useQuery, return the dynamicProps id or a boolean to make cache/state transitions more explicit.

packages/connect-react/src/hooks/customization-context.tsx (4)

144-170: Generic select customization API looks correct; consider naming clarity for ReactSelectComponents.

The new generic signatures for getClassNames/getComponents/getStyles/getProps align with react-select types. However, you have both:

  • a value import: components as ReactSelectComponents
  • a type alias: export type ReactSelectComponents = { … }

While legal (type vs value namespaces), it’s easy to confuse. Consider renaming the type alias to SelectComponentKeys or ReactSelectControlComponents.

-export type ReactSelectComponents = {
+export type ReactSelectControlComponents = {
   controlAppSelect: typeof ControlApp;
   controlSelect: typeof ControlSelect;
 };
@@
-export type CustomComponents<Option, IsMulti extends boolean, Group extends GroupBase<Option>> = {
-  [K in keyof typeof defaultComponents]: typeof defaultComponents[K]
-} & {
-  [K in keyof ReactSelectComponents]: ReactSelectComponentsConfig<Option, IsMulti, Group>
-};
+export type CustomComponents<Option, IsMulti extends boolean, Group extends GroupBase<Option>> = {
+  [K in keyof typeof defaultComponents]: typeof defaultComponents[K]
+} & {
+  [K in keyof ReactSelectControlComponents]: ReactSelectComponentsConfig<Option, IsMulti, Group>
+};

And update keyed usages accordingly.


181-201: Minor: redundant condition inside getClassNames.

You wrap the re-assignment in if (typeof classNames?.container == "function") and then check the same condition again in the ternary. The outer check makes the inner redundant.

-    if (typeof classNames?.container == "function") {
-      classNames.container = typeof classNames?.container == "function"
-        ? (...args) => ([
-          classNames?.container?.(...args),
-          baseClassName,
-        ]).join(" ")
-        : () => baseClassName;
-    }
+    if (typeof classNames?.container == "function") {
+      classNames.container = (...args) => ([
+        classNames.container?.(...args),
+        baseClassName,
+      ]).filter(Boolean).join(" ");
+    }

282-287: Ensure context.components doesn’t leak custom select configs into non-select slots.

getComponents merges context?.components into defaultComponents, but context.components also holds select component configs keyed by ReactSelect… keys. Consider splitting the provider config into:

  • components: ComponentLibrary overrides
  • selectComponents: keyed overrides for select

This avoids accidental prop leakage and tightens typing on CustomizationContext. If keeping a single bucket, document the expected shape clearly.


324-330: Provider typing: consider narrowing generics to concrete defaults.

The provider currently takes CustomizationConfig<any, any, any>. If you don’t allow passing typed Option/IsMulti/Group at the top-level, replace with CustomizationConfig<unknown, boolean, GroupBase> for clearer intent.

-}: CustomizationConfig<any, any, any> & { children: ReactNode; }) => {
+}: CustomizationConfig<unknown, boolean, GroupBase<unknown>> & { children: ReactNode; }) => {

Import GroupBase to the scope if needed.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 7a12929 and c5335cb.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (19)
  • packages/connect-react/src/components/ComponentForm.tsx (3 hunks)
  • packages/connect-react/src/components/ComponentFormContainer.tsx (1 hunks)
  • packages/connect-react/src/components/Control.tsx (2 hunks)
  • packages/connect-react/src/components/ControlApp.tsx (9 hunks)
  • packages/connect-react/src/components/ControlSelect.tsx (6 hunks)
  • packages/connect-react/src/components/ControlSubmit.tsx (3 hunks)
  • packages/connect-react/src/components/Errors.tsx (2 hunks)
  • packages/connect-react/src/components/Field.tsx (2 hunks)
  • packages/connect-react/src/components/InternalField.tsx (3 hunks)
  • packages/connect-react/src/components/RemoteOptionsContainer.tsx (7 hunks)
  • packages/connect-react/src/hooks/customization-context.tsx (4 hunks)
  • packages/connect-react/src/hooks/form-context.tsx (13 hunks)
  • packages/connect-react/src/hooks/form-field-context.tsx (1 hunks)
  • packages/connect-react/src/hooks/use-accounts.tsx (2 hunks)
  • packages/connect-react/src/types.ts (1 hunks)
  • packages/connect-react/src/utils/component.ts (1 hunks)
  • packages/connect-react/src/utils/type-guards.ts (3 hunks)
  • packages/connect-react/tsconfig.json (1 hunks)
  • packages/connect-react/vite.config.mts (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (12)
packages/connect-react/src/components/ControlSubmit.tsx (2)
packages/sdk/src/shared/component.ts (1)
  • ConfigurableProps (203-203)
packages/connect-react/src/hooks/form-context.tsx (2)
  • FormContext (29-54)
  • FormContext (64-64)
packages/connect-react/src/components/Control.tsx (3)
packages/connect-react/src/types.ts (1)
  • LabelValueOption (36-39)
packages/connect-react/src/utils/type-guards.ts (1)
  • sanitizeOption (70-105)
packages/connect-react/src/components/ControlSelect.tsx (1)
  • ControlSelect (32-227)
packages/connect-react/src/hooks/form-field-context.tsx (2)
packages/sdk/src/shared/component.ts (2)
  • ConfigurableProp (181-201)
  • ConfigurablePropApp (79-82)
packages/sdk/src/shared/index.ts (1)
  • App (87-122)
packages/connect-react/src/components/Errors.tsx (1)
packages/sdk/src/shared/component.ts (1)
  • ConfigurableProp (181-201)
packages/connect-react/src/hooks/use-accounts.tsx (1)
packages/sdk/src/shared/index.ts (1)
  • GetAccountsResponse (546-548)
packages/connect-react/src/components/Field.tsx (2)
packages/connect-react/src/hooks/form-context.tsx (2)
  • FormContext (29-54)
  • FormContext (64-64)
packages/sdk/src/shared/component.ts (1)
  • ConfigurableProp (181-201)
packages/connect-react/src/components/ControlApp.tsx (2)
packages/sdk/src/shared/index.ts (1)
  • App (87-122)
packages/connect-react/src/hooks/customization-context.tsx (1)
  • BaseReactSelectProps (126-133)
packages/connect-react/src/components/ComponentForm.tsx (1)
packages/connect-react/src/types.ts (1)
  • DynamicProps (60-60)
packages/connect-react/src/utils/type-guards.ts (1)
packages/connect-react/src/types.ts (2)
  • LabelValueOption (36-39)
  • RawPropOption (45-48)
packages/connect-react/src/hooks/form-context.tsx (2)
packages/sdk/src/shared/component.ts (3)
  • ConfigurableProps (203-203)
  • V1Component (230-238)
  • ConfiguredProps (225-227)
packages/connect-react/src/types.ts (5)
  • DynamicProps (60-60)
  • ReloadComponentPropsResponse (56-58)
  • Observation (31-34)
  • ObservationErrorDetails (17-21)
  • SdkError (6-9)
packages/connect-react/src/components/RemoteOptionsContainer.tsx (3)
packages/connect-react/src/types.ts (2)
  • ConfigureComponentContext (50-50)
  • RawPropOption (45-48)
packages/sdk/src/shared/index.ts (1)
  • ConfigureComponentOpts (432-473)
packages/connect-react/src/utils/type-guards.ts (1)
  • sanitizeOption (70-105)
packages/connect-react/src/components/ControlSelect.tsx (3)
packages/connect-react/src/types.ts (1)
  • LabelValueOption (36-39)
packages/connect-react/src/hooks/customization-context.tsx (1)
  • BaseReactSelectProps (126-133)
packages/connect-react/src/utils/type-guards.ts (2)
  • isOptionWithLabel (23-33)
  • sanitizeOption (70-105)
🪛 GitHub Actions: .github/workflows/publish-packages.yaml
packages/connect-react/src/components/ControlSubmit.tsx

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.


[error] 4-4: TS2307: Cannot find module '@pipedream/sdk/browser' or its corresponding type declarations.

packages/connect-react/src/utils/component.ts

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.

packages/connect-react/src/components/Control.tsx

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.


[error] 6-6: TS2307: Cannot find module '@pipedream/sdk' or its corresponding type declarations.

packages/connect-react/vite.config.mts

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.

packages/connect-react/tsconfig.json

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.

packages/connect-react/src/hooks/form-field-context.tsx

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.


[error] 6-6: TS2307: Cannot find module '@pipedream/sdk' or its corresponding type declarations.


[error] 7-7: TS2307: Cannot find module '@pipedream/sdk/browser' or its corresponding type declarations.

packages/connect-react/src/components/Errors.tsx

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.


[error] 5-5: TS2307: Cannot find module '@pipedream/sdk' or its corresponding type declarations.

packages/connect-react/src/hooks/use-accounts.tsx

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.


[error] 4-4: TS2307: Cannot find module '@pipedream/sdk' or its corresponding type declarations.


[error] 6-6: TS2307: Cannot find module '@pipedream/sdk/browser' or its corresponding type declarations.

packages/connect-react/src/components/ComponentFormContainer.tsx

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.

packages/connect-react/src/components/Field.tsx

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.


[error] 3-3: TS2307: Cannot find module '@pipedream/sdk' or its corresponding type declarations.

packages/connect-react/src/types.ts

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.


[error] 4-4: TS2307: Cannot find module '@pipedream/sdk/browser' or its corresponding type declarations.

packages/connect-react/src/components/ControlApp.tsx

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.


[error] 15-15: TS2307: Cannot find module '@pipedream/sdk/browser' or its corresponding type declarations.

packages/connect-react/src/components/ComponentForm.tsx

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.


[error] 9-9: TS2307: Cannot find module '@pipedream/sdk' or its corresponding type declarations.

packages/connect-react/src/components/InternalField.tsx

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.


[error] 1-1: TS2307: Cannot find module '@pipedream/sdk' or its corresponding type declarations.

packages/connect-react/src/utils/type-guards.ts

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.

packages/connect-react/src/hooks/form-context.tsx

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.


[error] 12-12: TS2307: Cannot find module '@pipedream/sdk' or its corresponding type declarations.


[error] 349-349: TS2300: Type 'T' must have a 'Symbol.iterator' method that returns an iterator.


[error] 433-433: TS7006: Parameter 'p' implicitly has an 'any' type.


[error] 449-449: TS2300: Type 'T' must have a 'Symbol.iterator' method that returns an iterator.

packages/connect-react/src/hooks/customization-context.tsx

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.


[error] 4-4: TS2307: Cannot find module '@pipedream/sdk' or its corresponding type declarations.


[error] 4-4: TS2307: Cannot find module '@pipedream/sdk' or its corresponding type declarations.

packages/connect-react/src/components/RemoteOptionsContainer.tsx

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.

packages/connect-react/src/components/ControlSelect.tsx

[error] vite build failed during prepare step for packages/connect-react. Exit code 1. Build produced 41 TypeScript errors.


[error] 124-124: TS2304: Cannot find name 'MenuListProps'.

🪛 GitHub Actions: Pull Request Checks
packages/connect-react/src/components/Control.tsx

[error] 6-6: Cannot find module '@pipedream/sdk' or its corresponding type declarations.

packages/connect-react/src/types.ts

[error] 4-4: Cannot find module '@pipedream/sdk/browser' or its corresponding type declarations.

packages/connect-react/src/components/ControlApp.tsx

[error] 15-15: Cannot find module '@pipedream/sdk/browser' or its corresponding type declarations.

packages/connect-react/src/components/ComponentForm.tsx

[error] 9-9: Cannot find module '@pipedream/sdk' or its corresponding type declarations.

packages/connect-react/src/hooks/form-context.tsx

[error] 12-12: Cannot find module '@pipedream/sdk' or its corresponding type declarations.


[error] 349-349: Type 'T' must have a 'Symbol.iterator' method that returns an iterator.

packages/connect-react/src/components/RemoteOptionsContainer.tsx

[error] 1-1: TS2307: Cannot find module '@pipedream/sdk/browser' or its corresponding type declarations.

packages/connect-react/src/components/ControlSelect.tsx

[error] 124-124: Cannot find name 'MenuListProps'.

🔇 Additional comments (24)
packages/connect-react/src/components/InternalField.tsx (1)

1-1: No action needed: @pipedream/sdk is already declared
A fresh install (pnpm install) and TS server restart should resolve the import error. Since @pipedream/sdk is already listed as a workspace dependency in packages/connect-react/package.json, the interim import workaround (TODO patch) can be removed.

packages/connect-react/src/hooks/use-accounts.tsx (1)

88-91: API field names look correct.

external_user_id, app, and oauth_app_id match typical server expectations. No change needed.

packages/connect-react/src/components/ControlApp.tsx (2)

17-26: Typed Option component looks good.

Using OptionProps ensures safe access to name/id across Account and the placeholder. No changes.


182-187: Nice UX fallback.

Showing a direct “Connect {app.name}” button when no accounts are present is clear.

packages/connect-react/src/types.ts (3)

31-34: Observation union structure looks good.

The discriminated union via presence of err vs msg is simple and effective.


36-49: Option types are clean and pragmatic.

LabelValueOption, NestedLabelValueOption, and RawPropOption will simplify guard/sanitization logic. No change needed.


52-61: Extending SDK responses with observations is fine; just ensure it mirrors the SDK.

If the SDK starts including observations natively, revisit to avoid double wrapping.

You might want to add a quick comment referencing the SDK version this mirrors to aid future maintainers.

packages/connect-react/src/components/Errors.tsx (1)

21-23: Confirm intent: errors are hidden unless enableDebugging is true

This component returns null when enableDebugging is falsy. If these are developer-only diagnostics, fine. If these are user-facing validation errors, this will hide them outside debugging sessions.

Would you like these errors to render regardless of enableDebugging?

packages/connect-react/src/hooks/form-field-context.tsx (1)

13-22: LGTM: FormFieldContext shape stays consistent

value, onChange, errors, and enableDebugging are unchanged. The extra discriminated union by ConfigurablePropApp reads cleanly with the proposed import fix.

packages/connect-react/src/utils/type-guards.ts (1)

69-99: LGTM: sanitizeOption covers primitives, nested __lv, and value-bearing objects

The normalization path matches how Control/RemoteOptionsContainer consume options and centralizes previously duplicated logic.

packages/connect-react/src/components/Control.tsx (1)

41-46: LGTM: centralizing option normalization with sanitizeOption

Using sanitizeOption here keeps option shapes consistent with RemoteOptionsContainer and ControlSelect.

packages/connect-react/src/components/ComponentFormContainer.tsx (1)

12-16: Good XOR prop contract for user identity

The mutually exclusive union enforces exactly one of externalUserId or userId at compile time. This aligns with the PR objective to harden types without changing runtime behavior.

packages/connect-react/src/components/ComponentForm.tsx (3)

4-4: Centralizing DynamicProps import is a win

Sourcing DynamicProps from ../types creates a single point of truth and reduces circular typing concerns across hooks and components.


28-31: Props typing improvements (onUpdateDynamicProps, sdkResponse) look correct

  • onUpdateDynamicProps now uses the canonical DynamicProps.
  • sdkResponse typed to U (defaults to ConfiguredProps) improves safety and clarity.

5-9: No action required: @pipedream/sdk is already a workspace dependency

The connect-react package’s package.json already lists
"@pipedream/sdk": "workspace:^" (packages/connect-react/package.json, line 31),
so TypeScript will resolve the SDK’s declaration files from the workspace without needing any extra paths mappings.

packages/connect-react/src/components/ControlSubmit.tsx (1)

6-11: Generic props shape for ControlSubmit is consistent with FormContext

The generic constraint ties the button’s disabled logic to the form state correctly.

packages/connect-react/src/components/ControlSelect.tsx (1)

129-130: LoadMoreButton Prop Name Verified

I confirmed that LoadMoreButton is defined with an onChange prop in its ButtonProps (see LoadMoreButton.tsx:5-6). Using onChange here is intentional, so no prop rename is needed.

packages/connect-react/src/hooks/form-context.tsx (3)

247-286: Prop validation guardrails look good.

The short-circuit for optional/hidden/skippable props and the safe extraction of app via the "in" guard are solid improvements.


324-332: Initialize queryDisabledIdx from configured props only once per component load.

The effect correctly avoids forcing reconfiguration on reloads. No issues.


6-12: Please confirm the SDK package is wired into your workspace

I wasn’t able to locate a local package.json declaring “@pipedream/sdk” in your monorepo. Before applying the import‐fix below, verify that:

• The SDK package actually exists in the repo (e.g. under packages/sdk or similar) and its package.json has
json { "name": "@pipedream/sdk", … }
• Your root package.json (or pnpm-workspace.yaml/turbo.json) lists that SDK folder under workspaces, so it can be hoisted and referenced.

Once you’ve confirmed the SDK lives in your monorepo and is exposed as a workspace package, apply one of these fixes in packages/connect-react:

  1. Add a workspace dependency (ensures both types and runtime code resolve correctly):
--- packages/connect-react/package.json
+++ packages/connect-react/package.json
@@
   "version": "…",
+  "dependencies": {
+    "@pipedream/sdk": "workspace:*"
+  },
  1. Or use a TS‐only path alias (if you truly only need types at build time):
--- packages/connect-react/tsconfig.json
+++ packages/connect-react/tsconfig.json
@@
   "compilerOptions": {
+    "baseUrl": ".",
+    "paths": {
+      "@pipedream/sdk": ["../sdk/src"]
+    },
     …
   }

Afterward, re-run your TypeScript build/CI to confirm imports from “@pipedream/sdk” resolve without errors.

packages/connect-react/src/hooks/customization-context.tsx (4)

126-133: Solid: BaseReactSelectProps now correctly generic with defaults.

Defaulting IsMulti and Group and typing styles as StylesConfig<Option, IsMulti, Group> improves downstream inference. No issues.


203-214: Components merge order and typing are good.

You merge defaults, base overrides, then context overrides. Casting context to the generic config type is acceptable here.


216-223: Style merging: ensure defined objects before calling mergeStyles.

You already default to {} for both args—good. If you ever allow falsy, mergeReactSelectStyles expects objects; this implementation is safe.


226-239: getProps ties the generics together correctly.

Passing through baseProps?.components/styles and classNames from the generic overloads keeps inference intact. Looks good.

* Use the SDK version published in the NPM registry instead of the one
that lives in this repo (it'll be easier to switch)
* Fix compilation errors in the example app
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 902c174 and 4ee190b.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • packages/connect-react/examples/nextjs/package.json (1 hunks)
  • packages/connect-react/examples/nextjs/src/app/page.tsx (3 hunks)
  • packages/connect-react/package.json (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/connect-react/package.json
🧰 Additional context used
🧬 Code graph analysis (1)
packages/connect-react/examples/nextjs/src/app/page.tsx (1)
packages/sdk/src/shared/component.ts (2)
  • ConfiguredProps (225-227)
  • ConfigurableProp (181-201)
🪛 GitHub Actions: Pull Request Checks
packages/connect-react/examples/nextjs/src/app/page.tsx

[error] 5-5: TS2307: Cannot find module '@pipedream/connect-react' or its corresponding type declarations. Build failed during 'vite build' invoked by 'pnpm run build' (package: connect-react).

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: pnpm publish
🔇 Additional comments (3)
packages/connect-react/examples/nextjs/package.json (1)

10-10: SDK version and browser export verified

All checks pass:

  • Monorepo resolves to a single @pipedream/sdk version (1.8.0) across all packages.
  • The SDK’s package exports include the ./browser subpath.
  • The browser entry defines both ConfigurableProp and ConfiguredProps types.

No further action needed.

packages/connect-react/examples/nextjs/src/app/page.tsx (2)

23-23: Stronger typing for configuredProps is good.

Using useState<ConfiguredProps<ConfigurableProp[]>> matches the SDK’s mapped type and keeps the example non-prescriptive on prop names. LGTM.


35-35: I’ve added scripts to locate the component and confirm both the example’s state type and the SDK’s actual response shape. Once we see these outputs, we can verify whether to broaden from ConfiguredProps<…> to the full RunActionResponse.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (4)
packages/connect-react/src/components/ControlApp.tsx (1)

11-15: Fix module resolution error by importing from '@pipedream/sdk'

The import from '@pipedream/sdk/browser' is causing TS2307 errors. Import these types from the base SDK package instead.

-import type {
-  Account,
-  App,
-  ConfigurablePropApp,
-} from "@pipedream/sdk/browser";
+import type {
+  Account,
+  App,
+  ConfigurablePropApp,
+} from "@pipedream/sdk";
packages/connect-react/src/types.ts (1)

1-4: Fix module resolution error by importing from '@pipedream/sdk'

The import from '@pipedream/sdk/browser' is causing TS2307 build failures. Update to import from the base SDK package.

-import type {
-  ConfigureComponentResponse as SdkConfigureComponentResponse,
-  ReloadComponentPropsResponse as SdkReloadComponentPropsResponse,
-} from "@pipedream/sdk/browser";
+import type {
+  ConfigureComponentResponse as SdkConfigureComponentResponse,
+  ReloadComponentPropsResponse as SdkReloadComponentPropsResponse,
+} from "@pipedream/sdk";
packages/connect-react/src/components/ControlSelect.tsx (1)

138-145: Pass finalComponents to Select to enable LoadMore UI

The finalComponents object is built but never passed to the Select component, preventing the LoadMore UI from rendering.

   return (
     <MaybeCreatableSelect
       inputId={id}
       instanceId={id}
       options={cleanedOptions}
       value={selectValue}
       isMulti={prop.type.endsWith("[]")}
       isClearable={true}
       required={!prop.optional}
       getOptionLabel={(option) => sanitizeOption(option).label}
       getOptionValue={(option) => String(sanitizeOption(option).value)}
       onChange={handleChange}
+      components={finalComponents}
       {...props}
       {...selectProps}
       {...additionalProps}
     />
   );
packages/connect-react/src/hooks/form-context.tsx (1)

222-239: Fix TypeScript iterator and type inference issues

The configurableProps needs explicit typing to avoid iterator and implicit-any errors in loops and callbacks.

-  const configurableProps = useMemo(() => {
-    let props = dynamicProps?.configurableProps || formProps.component.configurable_props || [];
+  const configurableProps = useMemo<ConfigurableProps>(() => {
+    let props: ConfigurableProps = dynamicProps?.configurableProps || formProps.component.configurable_props || [];
     if (propNames?.length) {
       const _configurableProps = [];
       for (const prop of props) {
         // TODO decided propNames (and hideOptionalProps) should NOT filter dynamic props
         if (propNames.findIndex((name) => prop.name === name) >= 0) {
           _configurableProps.push(prop);
         }
       }
-      props = _configurableProps as typeof props; // XXX
+      props = _configurableProps;
     }
     if (reloadPropIdx != null) {
       props = Array.isArray(props)
         ? props.slice(0, reloadPropIdx + 1) // eslint-disable-line react/prop-types
         : props; // XXX
     }
-    return props as T;
+    return props;
   }, [
     dynamicProps?.configurableProps,
     formProps.component.configurable_props,
     propNames,
     reloadPropIdx,
   ]);

Then in the context value assembly (around line 596):

-    configurableProps,
+    configurableProps: configurableProps as T,
🧹 Nitpick comments (5)
packages/connect-react/src/components/ControlApp.tsx (3)

95-97: Consider using a more type-safe approach instead of @ts-expect-error

The @ts-expect-error directive suppresses TypeScript checking. Consider properly typing the options or using a type assertion.

-        // @ts-expect-error this seems to work (this overrides enabled so don't just set to true)
-        suspense: !!app,
+        // Override enabled with suspense when app is truthy
+        suspense: !!app,
+      } as const,

123-129: Consider memoizing newAccountPlaceholder to avoid recreations

The newAccountPlaceholder object is recreated on every render. Since it depends only on app.name, consider memoizing it.

-  const selectOptions = useMemo<SelectValue[]>(() => [
-    ...accounts,
-    newAccountPlaceholder,
-  ], [
-    accounts,
-  ]);
+  const selectOptions = useMemo<SelectValue[]>(() => [
+    ...accounts,
+    newAccountPlaceholder,
+  ], [
+    accounts,
+    newAccountPlaceholder,
+  ]);

Also update the newAccountPlaceholder definition:

-  const newAccountPlaceholder: AccountPlaceholder = {
-    id: "_new",
-    name: `Connect new ${app.name} account...`,
-  };
+  const newAccountPlaceholder: AccountPlaceholder = useMemo(() => ({
+    id: "_new",
+    name: `Connect new ${app.name} account...`,
+  }), [app.name]);

112-114: Implement error handling for account connection failure

The TODO comment indicates missing error handling. This should be implemented to provide proper user feedback.

Would you like me to generate the error handling implementation or open a new issue to track this task?

packages/connect-react/src/components/ControlSelect.tsx (1)

81-120: Consider simplifying the selectValue computation logic

The selectValue computation has complex nested conditions. Consider extracting helper functions for clarity.

const findMatchingOption = (value: T, options: LabelValueOption<T>[]): LabelValueOption<T> | undefined => {
  return options.find(item => item.value === value);
};

const createLabelValueOption = (value: T): LabelValueOption<T> => ({
  label: String(value),
  value,
});

const selectValue: LabelValueOption<T> | LabelValueOption<T>[] | null = useMemo(() => {
  if (rawValue == null) {
    return null;
  }

  if (Array.isArray(rawValue)) {
    return rawValue.map(o => 
      isOptionWithLabel(o) ? o : (findMatchingOption(o, selectOptions) || createLabelValueOption(o))
    );
  }

  let ret = rawValue;
  if (ret && typeof ret === "object" && "__lv" in ret) {
    ret = ret.__lv;
  }
  
  if (isOptionWithLabel(ret)) {
    return ret;
  }

  return findMatchingOption(ret, selectOptions) || createLabelValueOption(ret);
}, [rawValue, selectOptions]);
packages/connect-react/src/hooks/form-context.tsx (1)

535-535: Consider defining a proper type for error details

Using any type reduces type safety. Consider defining an interface for the error details structure.

-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    const errorFromDetails = (data: any, ret: SdkError[]) => {
+    interface ErrorDetailsData {
+      error: string;
+      details: unknown;
+    }
+    const errorFromDetails = (data: ErrorDetailsData, ret: SdkError[]) => {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ae44744 and e0e2187.

📒 Files selected for processing (11)
  • packages/connect-react/package.json (3 hunks)
  • packages/connect-react/src/components/Control.tsx (2 hunks)
  • packages/connect-react/src/components/ControlApp.tsx (9 hunks)
  • packages/connect-react/src/components/ControlSelect.tsx (6 hunks)
  • packages/connect-react/src/components/ControlSubmit.tsx (3 hunks)
  • packages/connect-react/src/components/InternalField.tsx (2 hunks)
  • packages/connect-react/src/components/RemoteOptionsContainer.tsx (7 hunks)
  • packages/connect-react/src/hooks/form-context.tsx (13 hunks)
  • packages/connect-react/src/hooks/form-field-context.tsx (1 hunks)
  • packages/connect-react/src/types.ts (1 hunks)
  • packages/connect-react/src/utils/component.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/connect-react/src/components/Control.tsx
  • packages/connect-react/src/utils/component.ts
  • packages/connect-react/src/hooks/form-field-context.tsx
  • packages/connect-react/package.json
  • packages/connect-react/src/components/InternalField.tsx
  • packages/connect-react/src/components/RemoteOptionsContainer.tsx
  • packages/connect-react/src/components/ControlSubmit.tsx
🧰 Additional context used
🧬 Code graph analysis (3)
packages/connect-react/src/components/ControlSelect.tsx (3)
packages/connect-react/src/types.ts (1)
  • LabelValueOption (36-39)
packages/connect-react/src/hooks/customization-context.tsx (1)
  • BaseReactSelectProps (126-133)
packages/connect-react/src/utils/type-guards.ts (2)
  • isOptionWithLabel (23-33)
  • sanitizeOption (70-105)
packages/connect-react/src/hooks/form-context.tsx (2)
packages/sdk/src/shared/component.ts (3)
  • ConfigurableProps (203-203)
  • V1Component (230-238)
  • ConfiguredProps (225-227)
packages/connect-react/src/types.ts (5)
  • DynamicProps (60-60)
  • ReloadComponentPropsResponse (56-58)
  • Observation (31-34)
  • ObservationErrorDetails (17-21)
  • SdkError (6-9)
packages/connect-react/src/components/ControlApp.tsx (2)
packages/sdk/src/shared/index.ts (1)
  • App (87-122)
packages/connect-react/src/hooks/customization-context.tsx (1)
  • BaseReactSelectProps (126-133)
🔇 Additional comments (7)
packages/connect-react/src/components/ControlApp.tsx (1)

28-32: LGTM! Clean type definition for placeholder account

The AccountPlaceholder type and SelectValue union type are well-structured for handling both existing accounts and the "connect new" option.

packages/connect-react/src/types.ts (2)

11-34: LGTM! Well-structured observation types

The discriminated union pattern for Observation with distinct ObservationError and ObservationLog types provides good type safety.


36-49: LGTM! Clean option type definitions

The LabelValueOption, NestedLabelValueOption, and RawPropOption types provide a clear hierarchy for handling different option formats.

packages/connect-react/src/components/ControlSelect.tsx (2)

59-63: LGTM! Clean option sanitization in useEffect

Good approach to sanitize options once on mount/update rather than in render.


147-174: LGTM! Well-handled creation flow with proper sanitization

The creation logic properly sanitizes new options and maintains clean option objects in the select state.

packages/connect-react/src/hooks/form-context.tsx (2)

503-523: LGTM! Clean error handling refactor

The refactored error handling functions properly work with the new Observation and ObservationErrorDetails types.


255-257: LGTM! Improved type safety for app extraction

Good defensive check using "app" in field.extra before accessing the app property.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/connect-react/src/types.ts (1)

1-4: Fix TS2307: import SDK types from the base entry (duplicate of prior review).

The subpath "@pipedream/sdk/browser" isn’t exported, causing CI failures. Import from the package base until a browser subpath export is added in the SDK.

Apply this diff:

-import type {
-  ConfigureComponentResponse as SdkConfigureComponentResponse,
-  ReloadComponentPropsResponse as SdkReloadComponentPropsResponse,
-} from "@pipedream/sdk/browser";
+import type {
+  ConfigureComponentResponse as SdkConfigureComponentResponse,
+  ReloadComponentPropsResponse as SdkReloadComponentPropsResponse,
+} from "@pipedream/sdk";

If you own the SDK and truly need a browser subpath, add an "exports" entry for "./browser" with matching .d.ts files in @pipedream/sdk, then revert to that import.

🧹 Nitpick comments (8)
packages/connect-react/src/types.ts (8)

11-16: Clarify abbreviated field names in ObservationBase.

Names like ts, k, h are terse. Add JSDoc to document semantics without breaking callers.

-export type ObservationBase = {
-    ts: number;
-    k: string;
-    h?: string;
-}
+/** Base metadata for an observation.
+ *  ts: epoch millis
+ *  k: short key / category
+ *  h: optional hash / correlation id
+ */
+export type ObservationBase = {
+  ts: number;
+  k: string;
+  h?: string;
+}

17-22: Avoid duplication with SdkError and make stack optional.

Error.stack is optional in some environments. You can also reuse SdkError to DRY the fields.

-export type ObservationErrorDetails = {
-    name: string;
-    message: string;
-    stack: string;
-}
+export type ObservationErrorDetails = SdkError & {
+  stack?: string;
+}

31-35: Optional: add an explicit discriminant for easier narrowing.

You currently discriminate by presence of err vs msg. Adding a type tag enables exhaustive switches with compiler help.

Example:

-export type Observation = ObservationBase & (
-  | ObservationError
-  | ObservationLog
-)
+export type Observation =
+  | (ObservationBase & { kind: "error" } & ObservationError)
+  | (ObservationBase & { kind: "log" } & ObservationLog)

36-40: Minor ergonomics: default generic and immutability for options.

Defaults reduce type noise; readonly prevents accidental mutation.

-export type LabelValueOption<T> = {
-  label: string;
-  value: T;
-}
+export type LabelValueOption<T = unknown> = {
+  readonly label: string;
+  readonly value: T;
+}

41-44: Document the nested option wrapper and prevent mutation.

The __lv sentinel is non-obvious; add a brief comment and make it readonly.

-export type NestedLabelValueOption<T> = {
-  __lv: LabelValueOption<T> | T;
-}
+/** Internal wrapper used by sanitizeOption to normalize raw option shapes. */
+export type NestedLabelValueOption<T> = {
+  readonly __lv: LabelValueOption<T> | T;
+}

50-50: Consider constraining context to JSON-serializable values.

If this context crosses process boundaries or is logged, prefer JSONValue over unknown.

-export type ConfigureComponentContext = Record<string, unknown>
+export type JsonValue = string | number | boolean | null | { [k: string]: JsonValue } | JsonValue[];
+export type ConfigureComponentContext = Record<string, JsonValue>;

52-55: Nit: Enforce immutability on observations
I’ve confirmed via ripgrep that SdkConfigureComponentResponse (in packages/sdk/src/shared/index.ts) does not already define an observations field, so there’s no risk of shadowing or collision. As a small, optional improvement, converting your array to a read-only type helps guard against accidental mutations.

• File: packages/connect-react/src/types.ts (lines 52–55)
• Change:

-export type ConfigureComponentResponse = SdkConfigureComponentResponse & {
-    observations: Observation[];
-}
+export type ConfigureComponentResponse = SdkConfigureComponentResponse & {
+  readonly observations: ReadonlyArray<Observation>;
+}

56-59: Ensure observations is immutable and guard against future collisions

To align with our immutability conventions and prevent a potential name collision if the SDK ever adds an observations field, please update the type in packages/connect-react/src/types.ts:

• File: packages/connect-react/src/types.ts
Line: ~56

-export type ReloadComponentPropsResponse = SdkReloadComponentPropsResponse & {
-    observations: Observation[];
-}
+export type ReloadComponentPropsResponse =
+  Omit<SdkReloadComponentPropsResponse, "observations"> & {
+    readonly observations: ReadonlyArray<Observation>;
+  }

This change makes the observations array read-only and uses Omit<…> to avoid a future property collision.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e0e2187 and e05bbe6.

📒 Files selected for processing (1)
  • packages/connect-react/src/types.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: pnpm publish
  • GitHub Check: Lint Code Base
🔇 Additional comments (3)
packages/connect-react/src/types.ts (3)

23-26: LGTM: clear error wrapper shape.

Nesting error details under err avoids ambiguous unions and simplifies narrowing.


27-30: LGTM: concise log variant.


60-60: dynamicProps is non-nullable; no change required

I’ve confirmed that in the SDK’s ReloadComponentPropsResponse (packages/sdk/src/shared/index.ts), dynamicProps is defined as a required object (no ? or | null), so SdkReloadComponentPropsResponse["dynamicProps"] is already non‐nullable. Applying NonNullable<> here would be a no‐op.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (3)
packages/connect-react/src/types.ts (2)

1-4: Good fix: import SDK types from the base entry.

This resolves the TS2307 error from the former "/browser" subpath and aligns imports across the package.


45-49: Centralize normalization for RawPropOption<T> before accessing .label / .value.

Reiterate: consumers must call a single sanitizeOption (or equivalent) helper. This avoids fragile ad-hoc unwrapping when options are primitives, nested wrappers, or legacy shapes.

Run to find potential unsafe direct property access:

#!/bin/bash
# Find direct .label/.value access that likely bypasses normalization
rg -n --type=ts -C2 '(\.\s*(label|value)\b)' packages/connect-react \
  | rg -v 'LabelValueOption|type-guards|sanitize'

If matches are found, refactor to use sanitizeOption(...).

packages/connect-react/src/components/ControlApp.tsx (1)

12-15: Good fix: unify imports to '@pipedream/sdk'.

Resolves the missing module errors from the '/browser' subpath and keeps imports consistent.

🧹 Nitpick comments (7)
packages/connect-react/src/types.ts (1)

6-9: Unify error type shapes; make stack optional to match common Error semantics.

Avoid duplicating error field sets and reduce friction when mapping native Errors or SDK errors into observations.

Apply:

-export type SdkError = {
-    name: string;
-    message: string;
-}
+export type SdkError = {
+  name: string;
+  message: string;
+  stack?: string;
+}
@@
-export type ObservationErrorDetails = {
-    name: string;
-    message: string;
-    stack: string;
-}
+export type ObservationErrorDetails = SdkError

Also applies to: 17-21

packages/connect-react/src/components/ControlApp.tsx (6)

96-98: Replace @ts-expect-error with a typed path for suspense.

Current suppression hides a type mismatch between your wrapper’s useQueryOpts and React Query options. Prefer exposing suspense?: boolean in the hook’s option type so callers remain type-safe.

Proposed (outside this file) in use-accounts.tsx:

// Extend the options type the hook accepts:
export type UseAccountsOpts = {
  useQueryOpts?: Omit<UseQueryOptions<AccountsResponse, Error>, "queryKey" | "queryFn"> & {
    suspense?: boolean;
  };
};

Then this file can drop the suppression and keep:

-        // @ts-expect-error this seems to work (this overrides enabled so don't just set to true)
-        suspense: !!app,
+        suspense: !!app,

Want me to open a follow-up PR to adjust the hook types?


123-129: Memo dependency nit: include newAccountPlaceholder.

selectOptions depends on both accounts and newAccountPlaceholder. Add it to avoid stale options when app.name changes.

-  const selectOptions = useMemo<SelectValue[]>(() => [
-    ...accounts,
-    newAccountPlaceholder,
-  ], [
-    accounts,
-  ]);
+  const selectOptions = useMemo<SelectValue[]>(
+    () => [...accounts, newAccountPlaceholder],
+    [accounts, newAccountPlaceholder],
+  );

130-140: Avoid auto-selecting “Connect new …” when no account is chosen.

Selecting the placeholder by default changes UX: the field appears filled instead of showing the placeholder text. Prefer null to show the prompt until the user picks something.

-type SelectValue = Account | AccountPlaceholder;
+type SelectValue = Account | AccountPlaceholder;

@@
-  const selectValue = useMemo<SelectValue>(() => {
+  const selectValue = useMemo<SelectValue | null>(() => {
     if (value?.authProvisionId) {
       for (const item of accounts) {
         if (value.authProvisionId === item.id) {
           return item;
         }
       }
     }
-
-    return newAccountPlaceholder;
+    return null;
   }, [
     accounts,
     value,
   ]);
@@
-          <Select
+          <Select<SelectValue, false>
             instanceId={id}
             value={selectValue}
             options={selectOptions}

If the intent is to promote the connect flow by default, keep current behavior; otherwise the above restores the typical empty-select UX.

Also applies to: 154-158


164-176: Type the onChange handler and handle the “new” case tersely.

Minor cleanup and stronger typing when paired with the generic Select change.

-            onChange={(a) => {
-              if (a) {
-                if (a.id === "_new") {
-                  // start connect account and then select it, etc.
-                  // TODO unset / put in loading state
-                  startConnectAccount();
-                } else {
-                  onChange({
-                    authProvisionId: a.id,
-                  });
-                }
-              } else {
-                onChange(undefined);
-              }
-            }}
+            onChange={(a) => {
+              if (!a) return onChange(undefined);
+              if (a.id === "_new") {
+                // TODO: unset / loading state while connecting
+                startConnectAccount();
+                return;
+              }
+              onChange({ authProvisionId: a.id });
+            }}

183-188: Consider basic error reporting in onError.

Even a minimal toast/log will help users diagnose failed account connects.

-      onError: () => {
-        // TODO
-      },
+      onError: (err) => {
+        // TODO: replace with app-wide toast/notification
+        console.error("Failed to connect account", err);
+      },

154-166: Note: required has no effect on react-select.

Browsers ignore required on custom components. Keep if your form handler checks it manually; otherwise safe to remove.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e05bbe6 and 2816bd0.

📒 Files selected for processing (6)
  • packages/connect-react/src/components/ControlApp.tsx (9 hunks)
  • packages/connect-react/src/components/ControlSubmit.tsx (3 hunks)
  • packages/connect-react/src/components/RemoteOptionsContainer.tsx (7 hunks)
  • packages/connect-react/src/hooks/form-field-context.tsx (1 hunks)
  • packages/connect-react/src/hooks/use-accounts.tsx (2 hunks)
  • packages/connect-react/src/types.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/connect-react/src/hooks/form-field-context.tsx
  • packages/connect-react/src/components/ControlSubmit.tsx
  • packages/connect-react/src/components/RemoteOptionsContainer.tsx
  • packages/connect-react/src/hooks/use-accounts.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
packages/connect-react/src/components/ControlApp.tsx (2)
packages/sdk/src/shared/index.ts (1)
  • App (87-122)
packages/connect-react/src/hooks/customization-context.tsx (1)
  • BaseReactSelectProps (126-133)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Lint Code Base
  • GitHub Check: pnpm publish
🔇 Additional comments (2)
packages/connect-react/src/types.ts (1)

52-58: Confirm server contract: should observations be required?

You’re forcing observations to be present. If the API omits it (e.g., no logs/errors), downstream code will compile against a value that may be undefined at runtime. If it’s not guaranteed, mark it optional.

Option if not guaranteed:

-export type ConfigureComponentResponse = SdkConfigureComponentResponse & {
-    observations: Observation[];
-}
+export type ConfigureComponentResponse = SdkConfigureComponentResponse & {
+  observations?: Observation[];
+}
@@
-export type ReloadComponentPropsResponse = SdkReloadComponentPropsResponse & {
-    observations: Observation[];
-}
+export type ReloadComponentPropsResponse = SdkReloadComponentPropsResponse & {
+  observations?: Observation[];
+}

Also applies to: 60-60

packages/connect-react/src/components/ControlApp.tsx (1)

88-91: Correct param names for useAccounts query.

Switching to external_user_id (snake_case) and oauth_app_id looks consistent with the API surface.

@jverce jverce requested a review from tjk August 27, 2025 21:32
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We deleted this whole directory since we're using this repo instead:
https://github.com/PipedreamHQ/pipedream-connect-examples

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request javascript Pull requests that update Javascript code prioritized Prioritized issue tracked internally Issue is also tracked in our internal issue tracker
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant