@automattic/jetpack-connection 1.4.38 → 1.4.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ### This is a list detailing changes for the Jetpack RNA Connection Component releases.
4
4
 
5
+ ## [1.4.39] - 2026-03-09
6
+ ### Changed
7
+ - Switch to Native TypeScript compiler based on Go. [#47375]
8
+
9
+ ### Fixed
10
+ - Fix TS errors detected by tsgo. [#47409]
11
+
5
12
  ## [1.4.38] - 2026-03-02
6
13
  ### Changed
7
14
  - Convert `useConnection` hook to TypeScript. [#47380]
@@ -1285,6 +1292,7 @@
1285
1292
  - `Main` and `ConnectUser` components added.
1286
1293
  - `JetpackRestApiClient` API client added.
1287
1294
 
1295
+ [1.4.39]: https://github.com/Automattic/jetpack-connection-js/compare/v1.4.38...v1.4.39
1288
1296
  [1.4.38]: https://github.com/Automattic/jetpack-connection-js/compare/v1.4.37...v1.4.38
1289
1297
  [1.4.37]: https://github.com/Automattic/jetpack-connection-js/compare/v1.4.36...v1.4.37
1290
1298
  [1.4.36]: https://github.com/Automattic/jetpack-connection-js/compare/v1.4.35...v1.4.36
@@ -3,22 +3,15 @@ import { Icon, Notice, Path, SVG } from '@wordpress/components';
3
3
  import { __, sprintf } from '@wordpress/i18n';
4
4
  import PropTypes from 'prop-types';
5
5
  import styles from './styles.module.scss';
6
+ import type { ConnectionErrorNoticeProps } from './types';
6
7
 
7
- /**
8
- * The RNA Connection Error Notice component.
9
- *
10
- * @param {object} props -- The properties.
11
- * @return {import('react').Component} The `ConnectionErrorNotice` component.
12
- */
13
- const ConnectionErrorNotice = props => {
14
- const {
15
- message,
16
- isRestoringConnection,
17
- restoreConnectionCallback,
18
- restoreConnectionError,
19
- actions = [], // New prop for custom actions
20
- } = props;
21
-
8
+ const ConnectionErrorNotice = ( {
9
+ message,
10
+ isRestoringConnection,
11
+ restoreConnectionCallback,
12
+ restoreConnectionError,
13
+ actions = [],
14
+ }: ConnectionErrorNoticeProps ) => {
22
15
  const wrapperClassName = styles.notice;
23
16
 
24
17
  const icon = (
@@ -0,0 +1,17 @@
1
+ import type { ReactElement } from 'react';
2
+
3
+ export interface ActionItem {
4
+ label: string;
5
+ onClick: () => void;
6
+ isLoading?: boolean;
7
+ loadingText?: string;
8
+ variant?: 'primary' | 'secondary';
9
+ }
10
+
11
+ export interface ConnectionErrorNoticeProps {
12
+ message: string | ReactElement;
13
+ restoreConnectionCallback?: ( () => void ) | null;
14
+ isRestoringConnection?: boolean;
15
+ restoreConnectionError?: string | null;
16
+ actions?: ActionItem[];
17
+ }
@@ -15,7 +15,7 @@ import { useCallback, useEffect, useState, useMemo } from 'react';
15
15
  /**
16
16
  * Internal dependencies
17
17
  */
18
- import ConnectionErrorNotice from '../connection-error-notice/index.jsx';
18
+ import ConnectionErrorNotice from '../connection-error-notice';
19
19
  import DisconnectDialog from '../disconnect-dialog';
20
20
  import OwnerDisconnectDialog from '../owner-disconnect-dialog';
21
21
  import './style.scss';
@@ -1,7 +1,17 @@
1
1
  import { __ } from '@wordpress/i18n';
2
2
  import ConnectionErrorNotice from '../../components/connection-error-notice';
3
3
  import useConnection from '../../components/use-connection';
4
- import useRestoreConnection from '../../hooks/use-restore-connection/index.jsx';
4
+ import useRestoreConnection from '../../hooks/use-restore-connection';
5
+ import type {
6
+ Action,
7
+ ConnectionErrorData,
8
+ ConnectionErrorMap,
9
+ ConnectionErrorObject,
10
+ ConnectionErrorProps,
11
+ } from './types';
12
+ import type { ReactElement } from 'react';
13
+
14
+ export type { ConnectionErrorData, ConnectionErrorMap, ConnectionErrorObject } from './types';
5
15
 
6
16
  /**
7
17
  * Connection error notice hook.
@@ -12,13 +22,15 @@ import useRestoreConnection from '../../hooks/use-restore-connection/index.jsx';
12
22
  */
13
23
  export default function useConnectionErrorNotice() {
14
24
  const { connectionErrors } = useConnection( {} );
15
- const connectionErrorList = Object.values( connectionErrors ).shift();
16
- const firstError =
17
- connectionErrorList &&
18
- Object.values( connectionErrorList ).length &&
19
- Object.values( connectionErrorList ).shift();
25
+ // connectionErrors is typed as Array<string|object> but is actually a nested object at runtime.
26
+ const errorMap = connectionErrors as unknown as ConnectionErrorMap;
27
+ const connectionErrorList = Object.values( errorMap ).shift();
28
+ const firstError: ConnectionErrorObject | undefined =
29
+ connectionErrorList && Object.values( connectionErrorList ).length
30
+ ? Object.values( connectionErrorList ).shift()
31
+ : undefined;
20
32
 
21
- const connectionErrorMessage = firstError && firstError.error_message;
33
+ const connectionErrorMessage = firstError?.error_message;
22
34
 
23
35
  // Return all connection errors
24
36
  const hasConnectionError = Boolean( connectionErrorMessage );
@@ -27,15 +39,15 @@ export default function useConnectionErrorNotice() {
27
39
  hasConnectionError,
28
40
  connectionErrorMessage,
29
41
  connectionError: firstError, // Full error object with error_type, etc.
30
- connectionErrors, // All errors for advanced use cases
42
+ connectionErrors: errorMap, // All errors for advanced use cases
31
43
  };
32
44
  }
33
45
 
34
46
  export const ConnectionError = ( {
35
- actionHandlers = {}, // Handlers for specific actions like { create_missing_account: () => {}, custom_action: () => {} }
36
- trackingCallback = null, // Custom tracking function
37
- customActions = null, // Function that returns custom actions based on error (takes precedence)
38
- } = {} ) => {
47
+ actionHandlers = {},
48
+ trackingCallback = null,
49
+ customActions = null,
50
+ }: ConnectionErrorProps = {} ): ReactElement | null => {
39
51
  const { hasConnectionError, connectionErrorMessage, connectionError } =
40
52
  useConnectionErrorNotice();
41
53
  const { restoreConnection, isRestoringConnection, restoreConnectionError } =
@@ -46,21 +58,24 @@ export const ConnectionError = ( {
46
58
  }
47
59
 
48
60
  // Build actions array based on error data
49
- let actions = [];
61
+ let actions: Action[] = [];
50
62
 
51
63
  if ( customActions ) {
52
64
  // Use provided custom actions function
53
65
  try {
54
- actions = customActions( connectionError, { restoreConnection, isRestoringConnection } );
66
+ actions = customActions( connectionError as ConnectionErrorObject, {
67
+ restoreConnection,
68
+ isRestoringConnection,
69
+ } );
55
70
  } catch {
56
71
  // Silently fall back to default behavior if customActions fails
57
72
  actions = [];
58
73
  }
59
74
  } else {
60
75
  // Get action info from error data
61
- const errorData = connectionError?.error_data || {};
76
+ const errorData = connectionError?.error_data || ( {} as ConnectionErrorData );
62
77
  const suggestedAction = errorData.action;
63
- const actionHandler = actionHandlers[ suggestedAction ];
78
+ const actionHandler = suggestedAction ? actionHandlers[ suggestedAction ] : undefined;
64
79
 
65
80
  if ( suggestedAction && actionHandler ) {
66
81
  // Use action data from the error
@@ -76,7 +91,7 @@ export const ConnectionError = ( {
76
91
  if ( trackingCallback && trackingEvent ) {
77
92
  trackingCallback( trackingEvent, {} );
78
93
  }
79
- actionHandler( connectionError );
94
+ actionHandler( connectionError as ConnectionErrorObject );
80
95
  } catch {
81
96
  // Silently fail if action handler throws
82
97
  }
@@ -98,7 +113,7 @@ export const ConnectionError = ( {
98
113
  if ( trackingCallback && trackingEvent ) {
99
114
  trackingCallback( trackingEvent, {} );
100
115
  }
101
- window.location.href = errorData.action_url;
116
+ window.location.href = errorData.action_url as string;
102
117
  } catch {
103
118
  // Silently fail if navigation throws
104
119
  }
@@ -130,7 +145,9 @@ export const ConnectionError = ( {
130
145
  // Add secondary action if available (only for custom errors, not default restore)
131
146
  if ( actions.length > 0 && ( suggestedAction || errorData.action_url ) ) {
132
147
  const secondaryAction = errorData.secondary_action;
133
- const secondaryActionHandler = actionHandlers[ secondaryAction ];
148
+ const secondaryActionHandler = secondaryAction
149
+ ? actionHandlers[ secondaryAction ]
150
+ : undefined;
134
151
  const secondaryActionUrl = errorData.secondary_action_url;
135
152
  const secondaryActionLabel = errorData.secondary_action_label;
136
153
 
@@ -146,7 +163,7 @@ export const ConnectionError = ( {
146
163
  if ( trackingCallback && secondaryTrackingEvent ) {
147
164
  trackingCallback( secondaryTrackingEvent, {} );
148
165
  }
149
- secondaryActionHandler( connectionError );
166
+ secondaryActionHandler( connectionError as ConnectionErrorObject );
150
167
  } catch {
151
168
  // Silently fail if secondary action handler throws
152
169
  }
@@ -186,7 +203,7 @@ export const ConnectionError = ( {
186
203
  <ConnectionErrorNotice
187
204
  isRestoringConnection={ isRestoringConnection }
188
205
  restoreConnectionError={ restoreConnectionError }
189
- restoreConnectionCallback={ actions.length === 0 ? restoreConnection : null } // Fallback for backward compatibility
206
+ restoreConnectionCallback={ actions.length === 0 ? restoreConnection : null }
190
207
  message={ connectionErrorMessage }
191
208
  actions={ actions }
192
209
  />
@@ -0,0 +1,49 @@
1
+ export interface ConnectionErrorData {
2
+ action?: string;
3
+ action_label?: string;
4
+ action_variant?: 'primary' | 'secondary';
5
+ action_url?: string;
6
+ tracking_event?: string;
7
+ secondary_action?: string;
8
+ secondary_action_url?: string;
9
+ secondary_action_label?: string;
10
+ secondary_action_variant?: 'primary' | 'secondary';
11
+ secondary_tracking_event?: string;
12
+ [ key: string ]: unknown;
13
+ }
14
+
15
+ export interface ConnectionErrorObject {
16
+ error_message: string;
17
+ error_code?: string;
18
+ user_id?: string;
19
+ error_type?: string;
20
+ error_data?: ConnectionErrorData;
21
+ [ key: string ]: unknown;
22
+ }
23
+
24
+ /**
25
+ * The shape of connectionErrors from the store: a nested object keyed by error code, then user ID.
26
+ */
27
+ export type ConnectionErrorMap = Record< string, Record< string, ConnectionErrorObject > >;
28
+
29
+ export interface Action {
30
+ label: string;
31
+ onClick: () => void;
32
+ isLoading?: boolean;
33
+ loadingText?: string;
34
+ variant?: 'primary' | 'secondary';
35
+ }
36
+
37
+ export interface ConnectionErrorProps {
38
+ actionHandlers?: Record< string, ( error: ConnectionErrorObject ) => void >;
39
+ trackingCallback?: ( ( event: string, data: object ) => void ) | null;
40
+ customActions?:
41
+ | ( (
42
+ error: ConnectionErrorObject,
43
+ helpers: {
44
+ restoreConnection: () => void;
45
+ isRestoringConnection: boolean;
46
+ }
47
+ ) => Action[] )
48
+ | null;
49
+ }
@@ -6,6 +6,7 @@ import { useEffect, useState, useMemo } from 'react';
6
6
  import { getCalypsoOrigin } from '@automattic/jetpack-connection';
7
7
  import useConnection from '../../components/use-connection';
8
8
  import { STORE_ID } from '../../state/store.jsx';
9
+ import type { UseProductCheckoutWorkflowProps } from './types';
9
10
 
10
11
  const debug = debugFactory( 'jetpack:connection:useProductCheckoutWorkflow' );
11
12
 
@@ -16,35 +17,31 @@ const {
16
17
  siteSuffix: defaultSiteSuffix,
17
18
  } = window?.JP_CONNECTION_INITIAL_STATE || getScriptData()?.connection || {};
18
19
  const defaultAdminUrl = () =>
19
- typeof window !== 'undefined' ? window?.myJetpackInitialState?.adminUrl : null;
20
+ typeof window !== 'undefined'
21
+ ? ( window as Window & { myJetpackInitialState?: { adminUrl?: string } } )
22
+ ?.myJetpackInitialState?.adminUrl
23
+ : null;
20
24
 
21
25
  /**
22
26
  * Custom hook that performs the needed steps
23
27
  * to concrete the checkout workflow.
24
28
  *
25
- * @param {object} props - The props passed to the hook.
26
- * @param {string} props.productSlug - The WordPress product slug.
27
- * @param {string} props.redirectUrl - The URI to redirect to after checkout.
28
- * @param {string} [props.siteSuffix] - The site suffix.
29
- * @param {string} [props.adminUrl] - The site wp-admin url.
30
- * @param {boolean} [props.connectAfterCheckout] - Whether or not to conect after checkout if not connected (default false - connect before).
31
- * @param {Function} [props.siteProductAvailabilityHandler] - The function used to check whether the site already has the requested product. This will be checked after registration and the checkout page will be skipped if the promise returned resloves true.
32
- * @param {string} props.from - The plugin slug initiated the flow.
33
- * @param {number} [props.quantity] - The quantity of the product to purchase.
34
- * @param {boolean} [props.useBlogIdSuffix] - Use blog ID instead of site suffix in the checkout URL.
35
- * @return {object} The useEffect hook.
29
+ * @param {UseProductCheckoutWorkflowProps} props - The checkout workflow properties.
30
+ * @return The checkout workflow hook data.
36
31
  */
37
- export default function useProductCheckoutWorkflow( {
38
- productSlug,
39
- redirectUrl,
40
- siteSuffix = defaultSiteSuffix,
41
- adminUrl = defaultAdminUrl(),
42
- connectAfterCheckout = false,
43
- siteProductAvailabilityHandler = null,
44
- quantity = null,
45
- from,
46
- useBlogIdSuffix = false,
47
- } = {} ) {
32
+ export default function useProductCheckoutWorkflow(
33
+ {
34
+ productSlug,
35
+ redirectUrl,
36
+ siteSuffix = defaultSiteSuffix,
37
+ adminUrl = defaultAdminUrl(),
38
+ connectAfterCheckout = false,
39
+ siteProductAvailabilityHandler = null,
40
+ quantity = null,
41
+ from,
42
+ useBlogIdSuffix = false,
43
+ }: UseProductCheckoutWorkflowProps = {} as UseProductCheckoutWorkflowProps
44
+ ) {
48
45
  debug( 'productSlug is %s', productSlug );
49
46
  debug( 'redirectUrl is %s', redirectUrl );
50
47
  debug( 'siteSuffix is %s', siteSuffix );
@@ -52,7 +49,10 @@ export default function useProductCheckoutWorkflow( {
52
49
  const [ hasCheckoutStarted, setCheckoutStarted ] = useState( false );
53
50
  const { registerSite } = useDispatch( STORE_ID );
54
51
 
55
- const blogID = useSelect( select => select( STORE_ID ).getBlogId(), [] );
52
+ const blogID = useSelect(
53
+ select => ( select( STORE_ID ) as { getBlogId: () => string } ).getBlogId(),
54
+ []
55
+ );
56
56
  debug( 'blogID is %s', blogID ?? 'undefined' );
57
57
 
58
58
  useBlogIdSuffix = useBlogIdSuffix && !! blogID;
@@ -78,7 +78,7 @@ export default function useProductCheckoutWorkflow( {
78
78
  );
79
79
 
80
80
  if ( shouldConnectAfterCheckout ) {
81
- productCheckoutUrl.searchParams.set( 'connect_after_checkout', true );
81
+ productCheckoutUrl.searchParams.set( 'connect_after_checkout', 'true' );
82
82
  productCheckoutUrl.searchParams.set( 'admin_url', adminUrl );
83
83
  /**
84
84
  * `from_site_slug` is the Jetpack site slug (siteSuffix) passed 'from the site' via url
@@ -135,7 +135,7 @@ export default function useProductCheckoutWorkflow( {
135
135
  'handleAfterRegistration: Site does not have a product associated. Redirecting to checkout %s',
136
136
  checkoutUrl
137
137
  );
138
- window.location.href = checkoutUrl;
138
+ window.location.href = checkoutUrl.toString();
139
139
  } );
140
140
  };
141
141
 
@@ -146,17 +146,18 @@ export default function useProductCheckoutWorkflow( {
146
146
 
147
147
  debug( 'Redirecting to connectAfterCheckout flow: %s', checkoutUrl );
148
148
 
149
- window.location.href = checkoutUrl;
149
+ window.location.href = checkoutUrl.toString();
150
150
  };
151
151
 
152
152
  /**
153
153
  * Handler to run the checkout workflow.
154
154
  *
155
- * @param {Event} [event] - Event that dispatched run
156
- * @param {string} redirect - A possible redirect URL to go to after the checkout
157
- * @return {void} Nothing.
155
+ * @param {object} [event] - Event that dispatched run.
156
+ * @param {Function} event.preventDefault - Prevents the default event behavior.
157
+ * @param {string} redirect - A possible redirect URL to go to after the checkout.
158
+ * @return {void} Nothing.
158
159
  */
159
- const run = ( event, redirect = null ) => {
160
+ const run = ( event?: { preventDefault: () => void }, redirect: string | null = null ) => {
160
161
  event && event.preventDefault();
161
162
  setCheckoutStarted( true );
162
163
  // By default we will connect first prior to checkout unless `props.connectAfterCheckout`
@@ -0,0 +1,20 @@
1
+ export interface UseProductCheckoutWorkflowProps {
2
+ /** The WordPress product slug. */
3
+ productSlug: string;
4
+ /** The URI to redirect to after checkout. */
5
+ redirectUrl: string;
6
+ /** The site suffix. */
7
+ siteSuffix?: string;
8
+ /** The site wp-admin url. */
9
+ adminUrl?: string;
10
+ /** Whether or not to connect after checkout if not connected (default false - connect before). */
11
+ connectAfterCheckout?: boolean;
12
+ /** The function used to check whether the site already has the requested product. This will be checked after registration and the checkout page will be skipped if the promise returned resolves true. */
13
+ siteProductAvailabilityHandler?: ( () => Promise< boolean > | boolean ) | null;
14
+ /** The plugin slug that initiated the flow. */
15
+ from: string;
16
+ /** The quantity of the product to purchase. */
17
+ quantity?: number | null;
18
+ /** Use blog ID instead of site suffix in the checkout URL. */
19
+ useBlogIdSuffix?: boolean;
20
+ }
@@ -8,6 +8,11 @@ import { STORE_ID } from '../../state/store';
8
8
  const { apiRoot, apiNonce } =
9
9
  window?.JP_CONNECTION_INITIAL_STATE || getScriptData()?.connection || {};
10
10
 
11
+ interface ConnectionStoreDispatch {
12
+ disconnectUserSuccess: () => void;
13
+ setConnectionErrors: ( errors: Record< string, unknown > ) => void;
14
+ }
15
+
11
16
  /**
12
17
  * Restore connection hook.
13
18
  * It will initiate an API request attempting to restore the connection, or reconnect if it cannot be restored.
@@ -16,9 +21,11 @@ const { apiRoot, apiNonce } =
16
21
  */
17
22
  export default function useRestoreConnection() {
18
23
  const [ isRestoringConnection, setIsRestoringConnection ] = useState( false );
19
- const [ restoreConnectionError, setRestoreConnectionError ] = useState( null );
24
+ const [ restoreConnectionError, setRestoreConnectionError ] = useState< string | null >( null );
20
25
 
21
- const { disconnectUserSuccess, setConnectionErrors } = useDispatch( STORE_ID );
26
+ const { disconnectUserSuccess, setConnectionErrors } = useDispatch(
27
+ STORE_ID
28
+ ) as ConnectionStoreDispatch;
22
29
 
23
30
  const USER_CONNECTION_URL = getUserConnectionUrl();
24
31
 
@@ -34,7 +41,7 @@ export default function useRestoreConnection() {
34
41
 
35
42
  return restApi
36
43
  .reconnect()
37
- .then( connectionStatusData => {
44
+ .then( ( connectionStatusData: { status: string } ) => {
38
45
  // status 'in_progress' means the user needs to re-connect their WP.com account.
39
46
  if ( 'in_progress' === connectionStatusData.status ) {
40
47
  disconnectUserSuccess();
@@ -48,7 +55,7 @@ export default function useRestoreConnection() {
48
55
 
49
56
  return connectionStatusData;
50
57
  } )
51
- .catch( error => {
58
+ .catch( ( error: string ) => {
52
59
  setRestoreConnectionError( error );
53
60
  setIsRestoringConnection( false );
54
61
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automattic/jetpack-connection",
3
- "version": "1.4.38",
3
+ "version": "1.4.39",
4
4
  "description": "Jetpack Connection Component",
5
5
  "homepage": "https://github.com/Automattic/jetpack/tree/HEAD/projects/js-packages/connection/#readme",
6
6
  "bugs": {
@@ -25,14 +25,14 @@
25
25
  "scripts": {
26
26
  "test": "NODE_OPTIONS=--experimental-vm-modules jest",
27
27
  "test-coverage": "pnpm run test --coverage",
28
- "typecheck": "tsc --noEmit"
28
+ "typecheck": "tsgo --noEmit"
29
29
  },
30
30
  "dependencies": {
31
31
  "@automattic/jetpack-analytics": "^1.0.8",
32
32
  "@automattic/jetpack-api": "^1.0.18",
33
- "@automattic/jetpack-components": "^1.4.16",
33
+ "@automattic/jetpack-components": "^1.5.0",
34
34
  "@automattic/jetpack-config": "^1.0.7",
35
- "@automattic/jetpack-script-data": "^0.6.0",
35
+ "@automattic/jetpack-script-data": "^0.6.1",
36
36
  "@wordpress/base-styles": "6.16.0",
37
37
  "@wordpress/browserslist-config": "6.40.0",
38
38
  "@wordpress/components": "32.2.0",
@@ -53,11 +53,11 @@
53
53
  "@testing-library/react": "16.3.0",
54
54
  "@testing-library/user-event": "14.6.1",
55
55
  "@types/react": "18.3.28",
56
+ "@typescript/native-preview": "7.0.0-dev.20260225.1",
56
57
  "jest": "30.2.0",
57
58
  "react": "18.3.1",
58
59
  "react-dom": "18.3.1",
59
- "storybook": "10.2.11",
60
- "typescript": "5.9.3"
60
+ "storybook": "10.2.11"
61
61
  },
62
62
  "peerDependencies": {
63
63
  "react": "^18.0.0",