@postrun/react 1.1.0 → 1.3.0

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/dist/index.d.cts CHANGED
@@ -2,7 +2,8 @@ import * as react from 'react';
2
2
  import { ReactNode, CSSProperties } from 'react';
3
3
  import * as _tanstack_react_query from '@tanstack/react-query';
4
4
  import { QueryClient, QueryKey } from '@tanstack/react-query';
5
- import { PostrunClient, ListProfilesQuery, DiscoverableAccountList, Connection, ConnectionKind, ConnectionStatus, ListMediaQuery, ListPostsQuery, ConnectablePlatform, MediaResource, MediaTarget, MediaKind, Metadata, ComposePostInput, XPostVariant, LinkedInPostVariant } from '@postrun/js';
5
+ import { PostrunClient, ListProfilesQuery, DiscoverableAccountList, Connection, ConnectionKind, ConnectionStatus, ListMediaQuery, ListPostsQuery, ConnectablePlatform, MediaResource, MediaTarget, MediaKind, Metadata, ComposePostInput, PostValidation, XPostVariant, LinkedInPostVariant } from '@postrun/js';
6
+ export { PostValidation, ValidationIssue } from '@postrun/js';
6
7
  import { TwitterComponents } from 'react-tweet';
7
8
 
8
9
  /**
@@ -341,6 +342,11 @@ interface UseConnectParams {
341
342
  /** Called once a connection is fully ACTIVE (an account is bound). The
342
343
  * connections list is auto-refetched too, so you rarely need to act here. */
343
344
  onConnected?: (connection: Connection) => void;
345
+ /** Called when the connect SUCCEEDS — `active` OR `connected_pending` (the
346
+ * grant landed but the account binds out-of-band / via a slow webhook). Use
347
+ * this to close your UI and let the auto-refetched list show the result;
348
+ * `onConnected` additionally hands you the bound connection on `active`. */
349
+ onSuccess?: () => void;
344
350
  /** Called when the attempt fails, with the typed reason. */
345
351
  onError?: (reason: ConnectErrorReason) => void;
346
352
  /** Called when the user closes the OAuth popup without finishing. The hook
@@ -419,7 +425,7 @@ interface UseConnectResult {
419
425
  * The hosted `/connect` page remains the fallback for callers NOT using this SDK
420
426
  * (a plain link to `hosted_connect_url`); this hook never redirects.
421
427
  */
422
- declare function useConnect({ profileId, platform, onConnected, onError, onCancelled, prepareOnMount, }: UseConnectParams): UseConnectResult;
428
+ declare function useConnect({ profileId, platform, onConnected, onSuccess, onError, onCancelled, prepareOnMount, }: UseConnectParams): UseConnectResult;
423
429
  /**
424
430
  * List a profile's connected accounts. Pass a `filter` to narrow by `kind`
425
431
  * (`posting` = social, `ads`) or `status` — e.g. a composer fetches
@@ -539,6 +545,8 @@ interface ConnectProps {
539
545
  platform: ConnectablePlatform;
540
546
  /** Called once a connection is fully ACTIVE (an account is bound). */
541
547
  onConnected?: (connection: Connection) => void;
548
+ /** Called when the connect succeeds — `active` OR `connected_pending`. */
549
+ onSuccess?: () => void;
542
550
  /** Called when the attempt fails, with the typed reason. */
543
551
  onError?: (reason: ConnectErrorReason) => void;
544
552
  /** Called when the user closes the OAuth popup without finishing. */
@@ -580,7 +588,7 @@ interface ConnectProps {
580
588
  * The trigger MUST call `start()` directly in the click (it opens the popup
581
589
  * synchronously). Mount `<Connect>` inside a `<PostrunProvider>`.
582
590
  */
583
- declare function Connect({ profileId, platform, onConnected, onError, onCancelled, prepareOnMount, children, }: ConnectProps): ReactNode;
591
+ declare function Connect({ profileId, platform, onConnected, onSuccess, onError, onCancelled, prepareOnMount, children, }: ConnectProps): ReactNode;
584
592
 
585
593
  type MediaUploadStatus = 'idle' | 'uploading' | 'processing' | 'ready' | 'failed';
586
594
  interface MediaUploadOptions {
@@ -1275,6 +1283,36 @@ declare function useCreatePost(profileId: string): {
1275
1283
  isReady: boolean;
1276
1284
  connectedChannels: ("x" | "linkedin" | "facebook_page" | "instagram" | "tiktok")[];
1277
1285
  };
1286
+ /**
1287
+ * Validate a composed post WITHOUT saving or publishing it — the read-scoped
1288
+ * `POST /v1/posts/validate`. Builds the same best-effort variant set as
1289
+ * `useCreatePost` (via `buildCreatePost`), sends it, and relays the SERVER's
1290
+ * verdict: `publishable` (the single gate boolean) + `issues` (typed, per-variant
1291
+ * problems to render inline). The SDK makes NO validity judgment — the server is
1292
+ * the sole authority.
1293
+ *
1294
+ * It is a READ: no query-cache invalidation, no mutation handle. `validate` is a
1295
+ * stable callback (safe in deps); the caller debounces for live-as-you-type — the
1296
+ * hook never debounces internally. One call, no waterfall (connections are loaded
1297
+ * once by the shared `useConnections`).
1298
+ */
1299
+ declare function useValidatePost(profileId: string): {
1300
+ validate: (input: Omit<ComposePostInput, "profileId">) => Promise<PostValidation>;
1301
+ publishable: boolean | undefined;
1302
+ issues: {
1303
+ code: "unauthorized" | "forbidden" | "not_found" | "conflict" | "validation_failed" | "rate_limited" | "internal_error" | "idempotency_key_invalid" | "idempotency_key_reused" | "idempotency_request_in_progress" | "account_not_available" | "connection_reauth_required" | "connection_not_pending" | "connection_in_use" | "not_implemented" | "connection_discovery_failed" | "media_processing" | "not_publishable" | "invalid_connection" | "invalid_media" | "profile_scope_invalid" | "media_unprobeable" | "media_too_large" | "media_aspect_ratio_unsupported" | "media_resolution_too_low" | "media_gif_unsupported" | "media_format_recompressed" | "media_resolution_downscaled" | "video_container_unsupported" | "video_codec_unsupported" | "video_audio_codec_unsupported" | "video_too_large" | "video_too_small" | "video_dimensions_unsupported" | "video_dimensions_too_large" | "video_fps_unsupported" | "video_fps_too_low" | "video_aspect_unsupported" | "video_duration_too_short" | "video_duration_exceeds_max" | "video_transform_failed" | "media_fetch_failed" | "document_format_unsupported" | "document_too_large" | "document_too_many_pages" | "media_format_indeterminate" | "media_count_invalid" | "body_too_long" | "content_missing" | "content_conflict" | "content_incomplete" | "content_kind_mismatch" | "media_type_mismatch" | "tag_limit_exceeded" | "reel_field_on_non_reel" | "field_placement_invalid" | "media_not_ready" | "media_failed" | "media_unsupported" | "media_kind_mismatch" | "publishing_unavailable" | "x_duplicate_content" | "x_not_authorized" | "x_rate_limited" | "x_publish_failed" | "x_media_upload_failed" | "linkedin_duplicate_content" | "linkedin_auth_expired" | "linkedin_permission_denied" | "linkedin_media_processing" | "linkedin_media_upload_failed" | "linkedin_publish_failed" | "instagram_media_processing" | "instagram_container_expired" | "instagram_container_failed" | "instagram_rate_limited" | "instagram_not_authorized" | "instagram_publish_failed" | "facebook_reel_processing" | "facebook_reel_failed" | "facebook_rate_limited" | "facebook_not_authorized" | "facebook_publish_failed" | "tiktok_privacy_not_allowed" | "tiktok_duration_exceeds_max" | "tiktok_media_processing" | "tiktok_not_authorized" | "tiktok_rate_limited" | "tiktok_publish_failed" | "connection_platform_mismatch";
1304
+ message: string;
1305
+ hint?: string;
1306
+ allowed?: Array<string>;
1307
+ got?: string;
1308
+ variant_index: number;
1309
+ path: Array<string | number>;
1310
+ }[];
1311
+ isPending: boolean;
1312
+ error: Error | null;
1313
+ isReady: boolean;
1314
+ connectedChannels: ("x" | "linkedin" | "facebook_page" | "instagram" | "tiktok")[];
1315
+ };
1278
1316
  /**
1279
1317
  * Update a post by id. Pass a light edit directly (`{ schedule_at }`,
1280
1318
  * `{ tags }`, …) or a rebuilt body from `buildUpdatePost(input, connections)`
@@ -1627,4 +1665,4 @@ declare function LinkedInPostPreviewImpl({ variant, author, media, theme, time,
1627
1665
  * absorbs unstable media arrays). */
1628
1666
  declare const LinkedInPostPreview: react.MemoExoticComponent<typeof LinkedInPostPreviewImpl>;
1629
1667
 
1630
- export { type CalendarFilters, Connect, type ConnectErrorReason, type ConnectOutcome, type ConnectProps, type ConnectRenderApi, type ConnectState, type ConnectionsFilter, type DiscoverableAccount, type InfiniteList, LinkedInPostPreview, type LinkedInPostPreviewProps, type LinkedInPreviewAuthor, type LiveOptions, type MediaUploadItem, type MediaUploadOptions, type MediaUploadStatus, type PostrunContextValue, PostrunProvider, type PostrunProviderProps, type PreviewMedia, type PreviewMediaKind, UploadError, type UseConnectParams, type UseConnectResult, type UseMediaUploadOptions, type UseMediaUploadResult, XPostPreview, type XPostPreviewProps, type XPreviewAuthor, type XPreviewMedia, type XPreviewQuotedTweet, connectionKeys, mediaKeys, postKeys, profileKeys, useCalendar, useConnect, useConnection, useConnections, useCreatePost, useCreateProfile, useDeleteMedia, useDeletePost, useDeleteProfile, useDisconnect, useDiscoverableAccounts, useInfiniteList, useMedia, useMediaInfinite, useMediaList, useMediaUpload, usePost, usePostrun, usePosts, usePostsInfinite, useProfile, useProfiles, useProfilesInfinite, useSelectAccount, useUpdateMedia, useUpdatePost, useUpdateProfile };
1668
+ export { type CalendarFilters, Connect, type ConnectErrorReason, type ConnectOutcome, type ConnectProps, type ConnectRenderApi, type ConnectState, type ConnectionsFilter, type DiscoverableAccount, type InfiniteList, LinkedInPostPreview, type LinkedInPostPreviewProps, type LinkedInPreviewAuthor, type LiveOptions, type MediaUploadItem, type MediaUploadOptions, type MediaUploadStatus, type PostrunContextValue, PostrunProvider, type PostrunProviderProps, type PreviewMedia, type PreviewMediaKind, UploadError, type UseConnectParams, type UseConnectResult, type UseMediaUploadOptions, type UseMediaUploadResult, XPostPreview, type XPostPreviewProps, type XPreviewAuthor, type XPreviewMedia, type XPreviewQuotedTweet, connectionKeys, mediaKeys, postKeys, profileKeys, useCalendar, useConnect, useConnection, useConnections, useCreatePost, useCreateProfile, useDeleteMedia, useDeletePost, useDeleteProfile, useDisconnect, useDiscoverableAccounts, useInfiniteList, useMedia, useMediaInfinite, useMediaList, useMediaUpload, usePost, usePostrun, usePosts, usePostsInfinite, useProfile, useProfiles, useProfilesInfinite, useSelectAccount, useUpdateMedia, useUpdatePost, useUpdateProfile, useValidatePost };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,8 @@ import * as react from 'react';
2
2
  import { ReactNode, CSSProperties } from 'react';
3
3
  import * as _tanstack_react_query from '@tanstack/react-query';
4
4
  import { QueryClient, QueryKey } from '@tanstack/react-query';
5
- import { PostrunClient, ListProfilesQuery, DiscoverableAccountList, Connection, ConnectionKind, ConnectionStatus, ListMediaQuery, ListPostsQuery, ConnectablePlatform, MediaResource, MediaTarget, MediaKind, Metadata, ComposePostInput, XPostVariant, LinkedInPostVariant } from '@postrun/js';
5
+ import { PostrunClient, ListProfilesQuery, DiscoverableAccountList, Connection, ConnectionKind, ConnectionStatus, ListMediaQuery, ListPostsQuery, ConnectablePlatform, MediaResource, MediaTarget, MediaKind, Metadata, ComposePostInput, PostValidation, XPostVariant, LinkedInPostVariant } from '@postrun/js';
6
+ export { PostValidation, ValidationIssue } from '@postrun/js';
6
7
  import { TwitterComponents } from 'react-tweet';
7
8
 
8
9
  /**
@@ -341,6 +342,11 @@ interface UseConnectParams {
341
342
  /** Called once a connection is fully ACTIVE (an account is bound). The
342
343
  * connections list is auto-refetched too, so you rarely need to act here. */
343
344
  onConnected?: (connection: Connection) => void;
345
+ /** Called when the connect SUCCEEDS — `active` OR `connected_pending` (the
346
+ * grant landed but the account binds out-of-band / via a slow webhook). Use
347
+ * this to close your UI and let the auto-refetched list show the result;
348
+ * `onConnected` additionally hands you the bound connection on `active`. */
349
+ onSuccess?: () => void;
344
350
  /** Called when the attempt fails, with the typed reason. */
345
351
  onError?: (reason: ConnectErrorReason) => void;
346
352
  /** Called when the user closes the OAuth popup without finishing. The hook
@@ -419,7 +425,7 @@ interface UseConnectResult {
419
425
  * The hosted `/connect` page remains the fallback for callers NOT using this SDK
420
426
  * (a plain link to `hosted_connect_url`); this hook never redirects.
421
427
  */
422
- declare function useConnect({ profileId, platform, onConnected, onError, onCancelled, prepareOnMount, }: UseConnectParams): UseConnectResult;
428
+ declare function useConnect({ profileId, platform, onConnected, onSuccess, onError, onCancelled, prepareOnMount, }: UseConnectParams): UseConnectResult;
423
429
  /**
424
430
  * List a profile's connected accounts. Pass a `filter` to narrow by `kind`
425
431
  * (`posting` = social, `ads`) or `status` — e.g. a composer fetches
@@ -539,6 +545,8 @@ interface ConnectProps {
539
545
  platform: ConnectablePlatform;
540
546
  /** Called once a connection is fully ACTIVE (an account is bound). */
541
547
  onConnected?: (connection: Connection) => void;
548
+ /** Called when the connect succeeds — `active` OR `connected_pending`. */
549
+ onSuccess?: () => void;
542
550
  /** Called when the attempt fails, with the typed reason. */
543
551
  onError?: (reason: ConnectErrorReason) => void;
544
552
  /** Called when the user closes the OAuth popup without finishing. */
@@ -580,7 +588,7 @@ interface ConnectProps {
580
588
  * The trigger MUST call `start()` directly in the click (it opens the popup
581
589
  * synchronously). Mount `<Connect>` inside a `<PostrunProvider>`.
582
590
  */
583
- declare function Connect({ profileId, platform, onConnected, onError, onCancelled, prepareOnMount, children, }: ConnectProps): ReactNode;
591
+ declare function Connect({ profileId, platform, onConnected, onSuccess, onError, onCancelled, prepareOnMount, children, }: ConnectProps): ReactNode;
584
592
 
585
593
  type MediaUploadStatus = 'idle' | 'uploading' | 'processing' | 'ready' | 'failed';
586
594
  interface MediaUploadOptions {
@@ -1275,6 +1283,36 @@ declare function useCreatePost(profileId: string): {
1275
1283
  isReady: boolean;
1276
1284
  connectedChannels: ("x" | "linkedin" | "facebook_page" | "instagram" | "tiktok")[];
1277
1285
  };
1286
+ /**
1287
+ * Validate a composed post WITHOUT saving or publishing it — the read-scoped
1288
+ * `POST /v1/posts/validate`. Builds the same best-effort variant set as
1289
+ * `useCreatePost` (via `buildCreatePost`), sends it, and relays the SERVER's
1290
+ * verdict: `publishable` (the single gate boolean) + `issues` (typed, per-variant
1291
+ * problems to render inline). The SDK makes NO validity judgment — the server is
1292
+ * the sole authority.
1293
+ *
1294
+ * It is a READ: no query-cache invalidation, no mutation handle. `validate` is a
1295
+ * stable callback (safe in deps); the caller debounces for live-as-you-type — the
1296
+ * hook never debounces internally. One call, no waterfall (connections are loaded
1297
+ * once by the shared `useConnections`).
1298
+ */
1299
+ declare function useValidatePost(profileId: string): {
1300
+ validate: (input: Omit<ComposePostInput, "profileId">) => Promise<PostValidation>;
1301
+ publishable: boolean | undefined;
1302
+ issues: {
1303
+ code: "unauthorized" | "forbidden" | "not_found" | "conflict" | "validation_failed" | "rate_limited" | "internal_error" | "idempotency_key_invalid" | "idempotency_key_reused" | "idempotency_request_in_progress" | "account_not_available" | "connection_reauth_required" | "connection_not_pending" | "connection_in_use" | "not_implemented" | "connection_discovery_failed" | "media_processing" | "not_publishable" | "invalid_connection" | "invalid_media" | "profile_scope_invalid" | "media_unprobeable" | "media_too_large" | "media_aspect_ratio_unsupported" | "media_resolution_too_low" | "media_gif_unsupported" | "media_format_recompressed" | "media_resolution_downscaled" | "video_container_unsupported" | "video_codec_unsupported" | "video_audio_codec_unsupported" | "video_too_large" | "video_too_small" | "video_dimensions_unsupported" | "video_dimensions_too_large" | "video_fps_unsupported" | "video_fps_too_low" | "video_aspect_unsupported" | "video_duration_too_short" | "video_duration_exceeds_max" | "video_transform_failed" | "media_fetch_failed" | "document_format_unsupported" | "document_too_large" | "document_too_many_pages" | "media_format_indeterminate" | "media_count_invalid" | "body_too_long" | "content_missing" | "content_conflict" | "content_incomplete" | "content_kind_mismatch" | "media_type_mismatch" | "tag_limit_exceeded" | "reel_field_on_non_reel" | "field_placement_invalid" | "media_not_ready" | "media_failed" | "media_unsupported" | "media_kind_mismatch" | "publishing_unavailable" | "x_duplicate_content" | "x_not_authorized" | "x_rate_limited" | "x_publish_failed" | "x_media_upload_failed" | "linkedin_duplicate_content" | "linkedin_auth_expired" | "linkedin_permission_denied" | "linkedin_media_processing" | "linkedin_media_upload_failed" | "linkedin_publish_failed" | "instagram_media_processing" | "instagram_container_expired" | "instagram_container_failed" | "instagram_rate_limited" | "instagram_not_authorized" | "instagram_publish_failed" | "facebook_reel_processing" | "facebook_reel_failed" | "facebook_rate_limited" | "facebook_not_authorized" | "facebook_publish_failed" | "tiktok_privacy_not_allowed" | "tiktok_duration_exceeds_max" | "tiktok_media_processing" | "tiktok_not_authorized" | "tiktok_rate_limited" | "tiktok_publish_failed" | "connection_platform_mismatch";
1304
+ message: string;
1305
+ hint?: string;
1306
+ allowed?: Array<string>;
1307
+ got?: string;
1308
+ variant_index: number;
1309
+ path: Array<string | number>;
1310
+ }[];
1311
+ isPending: boolean;
1312
+ error: Error | null;
1313
+ isReady: boolean;
1314
+ connectedChannels: ("x" | "linkedin" | "facebook_page" | "instagram" | "tiktok")[];
1315
+ };
1278
1316
  /**
1279
1317
  * Update a post by id. Pass a light edit directly (`{ schedule_at }`,
1280
1318
  * `{ tags }`, …) or a rebuilt body from `buildUpdatePost(input, connections)`
@@ -1627,4 +1665,4 @@ declare function LinkedInPostPreviewImpl({ variant, author, media, theme, time,
1627
1665
  * absorbs unstable media arrays). */
1628
1666
  declare const LinkedInPostPreview: react.MemoExoticComponent<typeof LinkedInPostPreviewImpl>;
1629
1667
 
1630
- export { type CalendarFilters, Connect, type ConnectErrorReason, type ConnectOutcome, type ConnectProps, type ConnectRenderApi, type ConnectState, type ConnectionsFilter, type DiscoverableAccount, type InfiniteList, LinkedInPostPreview, type LinkedInPostPreviewProps, type LinkedInPreviewAuthor, type LiveOptions, type MediaUploadItem, type MediaUploadOptions, type MediaUploadStatus, type PostrunContextValue, PostrunProvider, type PostrunProviderProps, type PreviewMedia, type PreviewMediaKind, UploadError, type UseConnectParams, type UseConnectResult, type UseMediaUploadOptions, type UseMediaUploadResult, XPostPreview, type XPostPreviewProps, type XPreviewAuthor, type XPreviewMedia, type XPreviewQuotedTweet, connectionKeys, mediaKeys, postKeys, profileKeys, useCalendar, useConnect, useConnection, useConnections, useCreatePost, useCreateProfile, useDeleteMedia, useDeletePost, useDeleteProfile, useDisconnect, useDiscoverableAccounts, useInfiniteList, useMedia, useMediaInfinite, useMediaList, useMediaUpload, usePost, usePostrun, usePosts, usePostsInfinite, useProfile, useProfiles, useProfilesInfinite, useSelectAccount, useUpdateMedia, useUpdatePost, useUpdateProfile };
1668
+ export { type CalendarFilters, Connect, type ConnectErrorReason, type ConnectOutcome, type ConnectProps, type ConnectRenderApi, type ConnectState, type ConnectionsFilter, type DiscoverableAccount, type InfiniteList, LinkedInPostPreview, type LinkedInPostPreviewProps, type LinkedInPreviewAuthor, type LiveOptions, type MediaUploadItem, type MediaUploadOptions, type MediaUploadStatus, type PostrunContextValue, PostrunProvider, type PostrunProviderProps, type PreviewMedia, type PreviewMediaKind, UploadError, type UseConnectParams, type UseConnectResult, type UseMediaUploadOptions, type UseMediaUploadResult, XPostPreview, type XPostPreviewProps, type XPreviewAuthor, type XPreviewMedia, type XPreviewQuotedTweet, connectionKeys, mediaKeys, postKeys, profileKeys, useCalendar, useConnect, useConnection, useConnections, useCreatePost, useCreateProfile, useDeleteMedia, useDeletePost, useDeleteProfile, useDisconnect, useDiscoverableAccounts, useInfiniteList, useMedia, useMediaInfinite, useMediaList, useMediaUpload, usePost, usePostrun, usePosts, usePostsInfinite, useProfile, useProfiles, useProfilesInfinite, useSelectAccount, useUpdateMedia, useUpdatePost, useUpdateProfile, useValidatePost };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { createContext, memo, useMemo, useState, useEffect, Fragment as Fragment$1, useRef, createElement, useContext, useCallback } from 'react';
3
3
  import { useInfiniteQuery, useQuery, useMutation, QueryClient } from '@tanstack/react-query';
4
- import { createPostrunClient, profilesList, profilesGet, profilesCreate, profilesUpdate, profilesDelete, connectionsConnect, connectionsListByProfile, connectionsGet, connectionsListAccounts, connectionsSelect, connectionsDelete, mediaGet, mediaList, mediaUpdate, mediaDelete, postsList, postsGet, postsCreate, buildCreatePost, isPostPlatform, postsUpdate, postsDelete, mediaCreate, PostrunError } from '@postrun/js';
4
+ import { createPostrunClient, profilesList, profilesGet, profilesCreate, profilesUpdate, profilesDelete, connectionsConnect, connectionsListByProfile, connectionsGet, connectionsListAccounts, connectionsSelect, connectionsDelete, mediaGet, mediaList, mediaUpdate, mediaDelete, postsList, postsGet, postsCreate, buildCreatePost, isPostPlatform, postsValidate, postsUpdate, postsDelete, mediaCreate, PostrunError } from '@postrun/js';
5
5
  import Nango, { AuthError } from '@nangohq/frontend';
6
6
  import pWaitFor, { TimeoutError } from 'p-wait-for';
7
7
  import pLimit from 'p-limit';
@@ -304,6 +304,7 @@ function useConnect({
304
304
  profileId,
305
305
  platform,
306
306
  onConnected,
307
+ onSuccess,
307
308
  onError,
308
309
  onCancelled,
309
310
  prepareOnMount = true
@@ -318,10 +319,12 @@ function useConnect({
318
319
  const preparingRef = useRef(false);
319
320
  const prepareGenRef = useRef(0);
320
321
  const onConnectedRef = useRef(onConnected);
322
+ const onSuccessRef = useRef(onSuccess);
321
323
  const onErrorRef = useRef(onError);
322
324
  const onCancelledRef = useRef(onCancelled);
323
325
  useEffect(() => {
324
326
  onConnectedRef.current = onConnected;
327
+ onSuccessRef.current = onSuccess;
325
328
  onErrorRef.current = onError;
326
329
  onCancelledRef.current = onCancelled;
327
330
  });
@@ -444,10 +447,12 @@ function useConnect({
444
447
  setState({ phase: "active", connection: outcome.connection });
445
448
  void queryClient.invalidateQueries({ queryKey: connectionKeys.lists() });
446
449
  onConnectedRef.current?.(outcome.connection);
450
+ onSuccessRef.current?.();
447
451
  return;
448
452
  case "connected_pending":
449
453
  setState({ phase: "connected_pending" });
450
454
  void queryClient.invalidateQueries({ queryKey: connectionKeys.lists() });
455
+ onSuccessRef.current?.();
451
456
  return;
452
457
  case "cancelled":
453
458
  setState({ phase: "cancelled" });
@@ -542,6 +547,7 @@ function Connect({
542
547
  profileId,
543
548
  platform,
544
549
  onConnected,
550
+ onSuccess,
545
551
  onError,
546
552
  onCancelled,
547
553
  prepareOnMount,
@@ -551,6 +557,7 @@ function Connect({
551
557
  profileId,
552
558
  platform,
553
559
  onConnected,
560
+ onSuccess,
554
561
  onError,
555
562
  onCancelled,
556
563
  prepareOnMount
@@ -883,6 +890,37 @@ function useCreatePost(profileId) {
883
890
  connectedChannels: connected.map((connection) => connection.platform).filter(isPostPlatform)
884
891
  };
885
892
  }
893
+ function useValidatePost(profileId) {
894
+ const { client, queryClient } = usePostrun();
895
+ const connections = useConnections(profileId);
896
+ const connected = connections.data?.data ?? [];
897
+ const mutation = useMutation(
898
+ {
899
+ mutationFn: async (input) => (await postsValidate({
900
+ client,
901
+ body: buildCreatePost({ ...input, profileId }, connected)
902
+ })).data
903
+ },
904
+ queryClient
905
+ );
906
+ const { mutateAsync } = mutation;
907
+ const validate = useCallback(
908
+ (input) => mutateAsync(input),
909
+ [mutateAsync]
910
+ );
911
+ const issues = mutation.data?.issues ?? [];
912
+ return {
913
+ validate,
914
+ // The server's verdict (undefined until the first call resolves).
915
+ publishable: mutation.data?.publishable,
916
+ issues,
917
+ isPending: mutation.isPending,
918
+ error: mutation.error,
919
+ // Connections must load before `validate` can resolve a channel.
920
+ isReady: connections.isSuccess,
921
+ connectedChannels: connected.map((connection) => connection.platform).filter(isPostPlatform)
922
+ };
923
+ }
886
924
  function useUpdatePost(postId) {
887
925
  const { client, queryClient } = usePostrun();
888
926
  return useMutation(
@@ -1624,6 +1662,6 @@ function LinkedInPostPreviewImpl({
1624
1662
  }
1625
1663
  var LinkedInPostPreview = memo(LinkedInPostPreviewImpl);
1626
1664
 
1627
- export { Connect, LinkedInPostPreview, PostrunProvider, UploadError, XPostPreview, connectionKeys, mediaKeys, postKeys, profileKeys, useCalendar, useConnect, useConnection, useConnections, useCreatePost, useCreateProfile, useDeleteMedia, useDeletePost, useDeleteProfile, useDisconnect, useDiscoverableAccounts, useInfiniteList, useMedia, useMediaInfinite, useMediaList, useMediaUpload, usePost, usePostrun, usePosts, usePostsInfinite, useProfile, useProfiles, useProfilesInfinite, useSelectAccount, useUpdateMedia, useUpdatePost, useUpdateProfile };
1665
+ export { Connect, LinkedInPostPreview, PostrunProvider, UploadError, XPostPreview, connectionKeys, mediaKeys, postKeys, profileKeys, useCalendar, useConnect, useConnection, useConnections, useCreatePost, useCreateProfile, useDeleteMedia, useDeletePost, useDeleteProfile, useDisconnect, useDiscoverableAccounts, useInfiniteList, useMedia, useMediaInfinite, useMediaList, useMediaUpload, usePost, usePostrun, usePosts, usePostsInfinite, useProfile, useProfiles, useProfilesInfinite, useSelectAccount, useUpdateMedia, useUpdatePost, useUpdateProfile, useValidatePost };
1628
1666
  //# sourceMappingURL=index.js.map
1629
1667
  //# sourceMappingURL=index.js.map