@postrun/js 2.24.0 → 3.0.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/{chunk-RBHVSFEN.js → chunk-QXMLASQ6.js} +76 -130
- package/dist/chunk-QXMLASQ6.js.map +1 -0
- package/dist/{chunk-4WBAULYQ.js → chunk-YFAPR4TH.js} +17 -4
- package/dist/chunk-YFAPR4TH.js.map +1 -0
- package/dist/index.cjs +93 -179
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -30
- package/dist/index.d.ts +50 -30
- package/dist/index.js +8 -54
- package/dist/index.js.map +1 -1
- package/dist/query.cjs +52 -0
- package/dist/query.cjs.map +1 -1
- package/dist/query.d.cts +1220 -7760
- package/dist/query.d.ts +1220 -7760
- package/dist/query.js +49 -1
- package/dist/query.js.map +1 -1
- package/dist/schemas/index.cjs +102 -127
- package/dist/schemas/index.cjs.map +1 -1
- package/dist/schemas/index.d.cts +443 -531
- package/dist/schemas/index.d.ts +443 -531
- package/dist/schemas/index.js +27 -1
- package/dist/schemas/index.js.map +1 -1
- package/dist/server.cjs +74 -128
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +2 -2
- package/dist/{types.gen-CzfWmp33.d.cts → types.gen-zIKtx009.d.cts} +1106 -7738
- package/dist/{types.gen-CzfWmp33.d.ts → types.gen-zIKtx009.d.ts} +1106 -7738
- package/package.json +1 -1
- package/dist/chunk-4WBAULYQ.js.map +0 -1
- package/dist/chunk-RBHVSFEN.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { mediaAbort, mediaComplete, mediaListParts, mediaSignPart } from './chunk-
|
|
2
|
-
export { PostrunError, connectionsConnect, connectionsDelete, connectionsGet, connectionsListAccounts, connectionsListByProfile, connectionsSelect, createPostrunClient, googleCreateAd, googleCreateAdGroup, googleCreateAdGroupCriterion, googleCreateBudget, googleCreateCampaign, googleCreateCampaignCriterion, googleCreateConversionAction, googleCreateDisplayAd, googleCreateKeyword, googleEditAccount, googleEditAdGroup, googleEditBudget, googleEditCampaign, googleEnableAd, googleEnableAdGroup, googleEnableCampaign, googleEnableKeyword, googleGetAccount, googleGetAd, googleGetAdGroup, googleGetAdTree, googleGetCampaign, googleGetConversionAction, googleGetConversionStatus, googleGetInsights, googleGetKeyword, googleListAdGroupCriteria, googleListAdGroups, googleListAds, googleListAudiences, googleListCampaignCriteria, googleListCampaigns, googleListConversionActions, googleListConversionGoals, googleListKeywords, googlePauseAd, googlePauseAdGroup, googlePauseCampaign, googlePauseKeyword, googleRemoveAd, googleRemoveAdGroup, googleRemoveAdGroupCriterion, googleRemoveBudget, googleRemoveCampaign, googleRemoveCampaignCriterion, googleRemoveKeyword, googleRunGaql, googleSearchGeoTargets, googleSendConversions, googleSetAdGroupBids, googleSetCampaignGeoSettings, googleSetCampaignTargetingSetting, googleSetConversionGoal, googleSetKeywordBid, googleUploadImageAsset, logsGet, logsList, mediaAbort, mediaComplete, mediaCreate, mediaDelete, mediaGet, mediaList, mediaListParts, mediaSignPart, mediaUpdate, metaAccount, metaAd, metaAds, metaAdset, metaAdsets, metaCampaign, metaCampaigns, metaInsights, postsCreate, postsDelete, postsGet, postsList, postsUpdate, postsValidate, profilesCreate, profilesDelete, profilesGet, profilesList, profilesUpdate, tiktokCreatorInfo, tokensMint, webhooksCreateEndpoint, webhooksCreatePortal, webhooksDeleteEndpoint, webhooksGetEndpoint, webhooksListEndpoints, webhooksListEventTypes, webhooksPing, webhooksUpdateEndpoint } from './chunk-
|
|
3
|
-
import './chunk-
|
|
1
|
+
import { mediaAbort, mediaComplete, mediaListParts, mediaSignPart } from './chunk-YFAPR4TH.js';
|
|
2
|
+
export { PostrunError, connectionsConnect, connectionsDelete, connectionsGet, connectionsListAccounts, connectionsListByProfile, connectionsSelect, createPostrunClient, googleCreateAd, googleCreateAdGroup, googleCreateAdGroupCriterion, googleCreateBudget, googleCreateCampaign, googleCreateCampaignCriterion, googleCreateConversionAction, googleCreateDisplayAd, googleCreateKeyword, googleEditAccount, googleEditAdGroup, googleEditBudget, googleEditCampaign, googleEnableAd, googleEnableAdGroup, googleEnableCampaign, googleEnableKeyword, googleGetAccount, googleGetAd, googleGetAdGroup, googleGetAdTree, googleGetCampaign, googleGetConversionAction, googleGetConversionStatus, googleGetInsights, googleGetKeyword, googleListAdGroupCriteria, googleListAdGroups, googleListAds, googleListAudiences, googleListCampaignCriteria, googleListCampaigns, googleListConversionActions, googleListConversionGoals, googleListConversionRequests, googleListKeywords, googlePauseAd, googlePauseAdGroup, googlePauseCampaign, googlePauseKeyword, googleRemoveAd, googleRemoveAdGroup, googleRemoveAdGroupCriterion, googleRemoveBudget, googleRemoveCampaign, googleRemoveCampaignCriterion, googleRemoveKeyword, googleRunGaql, googleSearchGeoTargets, googleSendConversions, googleSetAdGroupBids, googleSetCampaignGeoSettings, googleSetCampaignTargetingSetting, googleSetConversionGoal, googleSetKeywordBid, googleUploadImageAsset, logsGet, logsList, mediaAbort, mediaComplete, mediaCreate, mediaDelete, mediaGet, mediaList, mediaListParts, mediaSignPart, mediaUpdate, metaAccount, metaAd, metaAds, metaAdset, metaAdsets, metaCampaign, metaCampaigns, metaInsights, postsCreate, postsDelete, postsGet, postsList, postsUpdate, postsValidate, profilesCreate, profilesDelete, profilesGet, profilesList, profilesUpdate, tiktokCreatorInfo, tokensMint, webhooksCreateEndpoint, webhooksCreatePortal, webhooksDeleteEndpoint, webhooksGetEndpoint, webhooksListEndpoints, webhooksListEventTypes, webhooksPing, webhooksUpdateEndpoint } from './chunk-YFAPR4TH.js';
|
|
3
|
+
import './chunk-QXMLASQ6.js';
|
|
4
4
|
import Uppy from '@uppy/core';
|
|
5
5
|
import AwsS3 from '@uppy/aws-s3';
|
|
6
6
|
|
|
@@ -305,30 +305,9 @@ var ComposeError = class extends Error {
|
|
|
305
305
|
this.name = "ComposeError";
|
|
306
306
|
}
|
|
307
307
|
};
|
|
308
|
-
function countKinds(media) {
|
|
309
|
-
let images = 0;
|
|
310
|
-
let videos = 0;
|
|
311
|
-
let documents = 0;
|
|
312
|
-
for (const item of media) {
|
|
313
|
-
if (item.kind === "video") videos += 1;
|
|
314
|
-
else if (item.kind === "document") documents += 1;
|
|
315
|
-
else images += 1;
|
|
316
|
-
}
|
|
317
|
-
return { images, videos, documents };
|
|
318
|
-
}
|
|
319
|
-
function deriveSinglePlacement(media, videoType) {
|
|
320
|
-
const { images, videos } = countKinds(media);
|
|
321
|
-
if (videos >= 1) return videoType;
|
|
322
|
-
return images >= 2 ? "multi_image" : "single_image";
|
|
323
|
-
}
|
|
324
308
|
var xHandler = {
|
|
325
|
-
|
|
326
|
-
if (media.length === 0) return "text";
|
|
327
|
-
return deriveSinglePlacement(media, "video");
|
|
328
|
-
},
|
|
329
|
-
buildVariant: ({ settings, postType, connectionId, body, media }) => ({
|
|
309
|
+
buildVariant: ({ settings, connectionId, body, media }) => ({
|
|
330
310
|
platform: "x",
|
|
331
|
-
post_type: postType,
|
|
332
311
|
connection_id: connectionId,
|
|
333
312
|
body,
|
|
334
313
|
media: [...media],
|
|
@@ -336,16 +315,8 @@ var xHandler = {
|
|
|
336
315
|
})
|
|
337
316
|
};
|
|
338
317
|
var linkedInHandler = {
|
|
339
|
-
|
|
340
|
-
if (media.length === 0) return "text";
|
|
341
|
-
if (countKinds(media).documents > 0) {
|
|
342
|
-
return "single_image";
|
|
343
|
-
}
|
|
344
|
-
return deriveSinglePlacement(media, "video");
|
|
345
|
-
},
|
|
346
|
-
buildVariant: ({ settings, postType, connectionId, body, media }) => ({
|
|
318
|
+
buildVariant: ({ settings, connectionId, body, media }) => ({
|
|
347
319
|
platform: "linkedin",
|
|
348
|
-
post_type: postType,
|
|
349
320
|
connection_id: connectionId,
|
|
350
321
|
body,
|
|
351
322
|
media: [...media],
|
|
@@ -353,13 +324,8 @@ var linkedInHandler = {
|
|
|
353
324
|
})
|
|
354
325
|
};
|
|
355
326
|
var facebookHandler = {
|
|
356
|
-
|
|
357
|
-
if (media.length === 0) return "text";
|
|
358
|
-
return deriveSinglePlacement(media, "reel");
|
|
359
|
-
},
|
|
360
|
-
buildVariant: ({ settings, postType, connectionId, body, media }) => ({
|
|
327
|
+
buildVariant: ({ settings, connectionId, body, media }) => ({
|
|
361
328
|
platform: "facebook_page",
|
|
362
|
-
post_type: postType,
|
|
363
329
|
connection_id: connectionId,
|
|
364
330
|
body,
|
|
365
331
|
media: [...media],
|
|
@@ -367,13 +333,8 @@ var facebookHandler = {
|
|
|
367
333
|
})
|
|
368
334
|
};
|
|
369
335
|
var instagramHandler = {
|
|
370
|
-
|
|
371
|
-
if (media.length >= 2) return "carousel";
|
|
372
|
-
return countKinds(media).videos === 1 ? "reel" : "single_image";
|
|
373
|
-
},
|
|
374
|
-
buildVariant: ({ settings, postType, connectionId, body, media }) => ({
|
|
336
|
+
buildVariant: ({ settings, connectionId, body, media }) => ({
|
|
375
337
|
platform: "instagram",
|
|
376
|
-
post_type: postType,
|
|
377
338
|
connection_id: connectionId,
|
|
378
339
|
body,
|
|
379
340
|
media: [...media],
|
|
@@ -381,14 +342,8 @@ var instagramHandler = {
|
|
|
381
342
|
})
|
|
382
343
|
};
|
|
383
344
|
var tiktokHandler = {
|
|
384
|
-
|
|
385
|
-
const { images, videos } = countKinds(media);
|
|
386
|
-
if (videos >= 1) return "video";
|
|
387
|
-
return images >= 2 ? "carousel" : "single_image";
|
|
388
|
-
},
|
|
389
|
-
buildVariant: ({ settings, postType, connectionId, body, media }) => ({
|
|
345
|
+
buildVariant: ({ settings, connectionId, body, media }) => ({
|
|
390
346
|
platform: "tiktok",
|
|
391
|
-
post_type: postType,
|
|
392
347
|
connection_id: connectionId,
|
|
393
348
|
body,
|
|
394
349
|
media: [...media],
|
|
@@ -420,7 +375,6 @@ function buildChannel(handler, platform, config, content, connections) {
|
|
|
420
375
|
const media = config.media ?? content.media ?? [];
|
|
421
376
|
return handler.buildVariant({
|
|
422
377
|
settings: config.settings,
|
|
423
|
-
postType: config.postType ?? handler.derivePostType(media),
|
|
424
378
|
connectionId: resolveConnectionId(platform, config.connectionId, connections),
|
|
425
379
|
body: config.body ?? content.body,
|
|
426
380
|
media: media.map((item) => ({ media_id: item.id }))
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/resources.ts","../src/upload/casing.ts","../src/upload/upload-error.ts","../src/upload/upload-file.ts","../src/tiktok.ts","../src/linkedin.ts","../src/x.ts","../src/ads-tree.ts","../src/tiktok-options.ts","../src/compose.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type {\n ConnectionsConnectData,\n ConnectionsConnectResponse,\n ConnectionsGetResponse,\n ConnectionsListAccountsResponse,\n ConnectionsListByProfileData,\n ConnectionsListByProfileResponse,\n ConnectionsSelectData,\n MediaCreateData,\n MediaCreateResponse,\n MediaGetResponse,\n MediaListData,\n MediaListResponse,\n MediaUpdateData,\n PostsCreateData,\n PostsGetResponse,\n PostsListData,\n PostsListResponse,\n PostsUpdateData,\n PostsValidateData,\n PostsValidateResponse,\n ProfilesCreateData,\n ProfilesGetResponse,\n ProfilesListData,\n ProfilesListResponse,\n ProfilesUpdateData,\n TiktokCreatorInfoResponse,\n} from './client/types.gen';\n\n/**\n * Resource and input shapes, DERIVED from the generated Hey API operation types\n * (`*Data` = request `{ body, path, query }`, `*Response` = success body). Never\n * hand-declared — there is exactly one definition of each, sourced from the\n * contract, so `pnpm generate` keeps them in lockstep with the live API.\n */\n\n/** A profile — a client/brand workspace. */\nexport type Profile = ProfilesGetResponse;\n\n/** A page of profiles (the list envelope). */\nexport type ProfileList = ProfilesListResponse;\n\n/** Query parameters for listing profiles (filters + pagination). */\nexport type ListProfilesQuery = NonNullable<ProfilesListData['query']>;\n\n/** Request body to create a profile. */\nexport type CreateProfileInput = NonNullable<ProfilesCreateData['body']>;\n\n/** Request body to update a profile (send only what changes). */\nexport type UpdateProfileInput = NonNullable<ProfilesUpdateData['body']>;\n\n/** A connected social/ads account on a profile. */\nexport type Connection = ConnectionsGetResponse;\n\n/** A page of a profile's connections. */\nexport type ConnectionList = ConnectionsListByProfileResponse;\n\n/** Query filters for listing a profile's connections (kind / status / pagination). */\nexport type ListConnectionsQuery = NonNullable<\n ConnectionsListByProfileData['query']\n>;\n\n/** A connection's kind — `posting` (social) or `ads`. From the contract. */\nexport type ConnectionKind = NonNullable<ListConnectionsQuery['kind']>;\n\n/** A connection's lifecycle status — `pending` / `active` / `needs_reauth`. */\nexport type ConnectionStatus = NonNullable<ListConnectionsQuery['status']>;\n\n/** The session returned when starting a connect (OAuth) flow. */\nexport type ConnectSession = ConnectionsConnectResponse;\n\n/** A platform a connection can start an OAuth connect flow for. */\nexport type ConnectablePlatform =\n NonNullable<ConnectionsConnectData['body']>['platform'];\n\n/** Accounts discoverable on a pending connection, for the selection step. */\nexport type DiscoverableAccountList = ConnectionsListAccountsResponse;\n\n/**\n * A TikTok creator's live publish options for a connection — the data TikTok's\n * Content Posting policy REQUIRES the publishing UI to render: `creator`\n * (nickname + avatar), the `privacy_options` to offer (with NO default), the\n * positive `interaction` (allowed) flags for Comment/Duet/Stitch toggles, and\n * `max_video_duration_sec`. Derived from the contract, never hand-declared.\n */\nexport type TikTokCreatorInfo = TiktokCreatorInfoResponse;\n\n/**\n * One TikTok privacy level — a member of the closed audience union\n * (`PUBLIC_TO_EVERYONE` / `MUTUAL_FOLLOW_FRIENDS` / `FOLLOWER_OF_CREATOR` /\n * `SELF_ONLY`). The per-account ALLOWED subset is `TikTokCreatorInfo.privacy_options`;\n * this is one value from it (what you bind a dropdown option to). Derived from\n * the contract, never hand-declared; render it with {@link tiktokPrivacyLabel}.\n */\nexport type TikTokPrivacyLevel = TikTokCreatorInfo['privacy_options'][number];\n\n/** Request body to select an account on a pending connection. */\nexport type SelectAccountInput = NonNullable<ConnectionsSelectData['body']>;\n\n/** The create/update post body, derived from the contract. */\ntype CreatePostBody = NonNullable<PostsCreateData['body']>;\n\n/**\n * One channel's variant on WRITE — the per-platform discriminated-union member\n * with fully typed native `settings`. Preferred over the read resource's variant\n * for anything that inspects settings (on read, `settings` is an opaque record);\n * it is also the compose-time shape, which is exactly what a live preview renders.\n */\nexport type PostVariantInput = CreatePostBody['variants'][number];\n\n/** The X (Twitter) member of the write variant union — typed native settings\n * (`quote_tweet_id`, `reply`, `poll`, …). Narrowed from the contract, never\n * hand-declared. */\nexport type XPostVariant = Extract<PostVariantInput, { platform: 'x' }>;\n\n/** The LinkedIn member of the write variant union — typed native settings\n * (`content_kind`, `visibility`, `article`, `poll`, `document`, `mentions`).\n * Narrowed from the contract, never hand-declared. */\nexport type LinkedInPostVariant = Extract<\n PostVariantInput,\n { platform: 'linkedin' }\n>;\n\n/** The TikTok member of the write variant union — typed native settings\n * (`privacy_level`, `disable_comment`/`disable_duet`/`disable_stitch`,\n * `brand_content_toggle`/`brand_organic_toggle`, `is_aigc`, photo/video cover).\n * Narrowed from the contract, never hand-declared — it is the compose-time shape a\n * live TikTok preview renders. */\nexport type TikTokPostVariant = Extract<\n PostVariantInput,\n { platform: 'tiktok' }\n>;\n\n/** The Instagram member of the write variant union — typed native settings\n * (`media_type`, `collaborators`, `user_tags`, `location_id`, and the reel-only\n * `share_to_feed`/`audio_name`/`cover_url`/`thumb_offset`). Narrowed from the\n * contract, never hand-declared — the compose-time shape an IG preview renders. */\nexport type InstagramPostVariant = Extract<\n PostVariantInput,\n { platform: 'instagram' }\n>;\n\n/** A post — its variants, schedule, and derived status (the read resource). */\nexport type Post = PostsGetResponse;\n\n/**\n * One per-platform variant on a RETURNED post (the read resource): its own\n * `status`, a typed `error` ({@link PostVariantError}) when it failed, and a\n * `result` (platform id + permalink) when it published. Derived from the post\n * resource, never hand-declared. (For inspecting compose-time native `settings`,\n * prefer {@link PostVariantInput}; on read `settings` is opaque.)\n */\nexport type PostVariant = Post['variants'][number];\n\n/**\n * A variant's typed publish failure — `{ code, message }`. The `code` is the\n * adapter's own minted reason (`x_access_not_permitted`, `linkedin_auth_expired`,\n * …); it stays a `string` because adapter codes are minted outside the API's\n * closed registry. Derived from the variant, never hand-declared.\n */\nexport type PostVariantError = NonNullable<PostVariant['error']>;\n\n/**\n * Did the post fully publish? `true` ONLY when the rollup `status` is\n * `'published'` — `partially_published` and `failed` are NOT success. The single\n * pure predicate every surface (SDK, React hook, composer) shares, so \"success\"\n * has exactly one definition.\n */\nexport const isPublished = (post: Post): boolean =>\n post.status === 'published';\n\n/**\n * The variants that FAILED to publish, each carrying its typed {@link\n * PostVariantError}. Empty when none failed. The pure helper behind\n * `useCreatePost().failedVariants` — usable directly from a non-React caller so\n * the \"which platforms failed, and why\" projection has one definition.\n */\nexport const failedVariants = (post: Post): PostVariant[] =>\n post.variants.filter((variant) => variant.status === 'failed');\n\n/** A page of posts (the calendar/queue list). */\nexport type PostList = PostsListResponse;\n\n/** Query parameters for listing posts (filters + pagination). */\nexport type ListPostsQuery = NonNullable<PostsListData['query']>;\n\n/**\n * A post's derived rollup status — the values accepted by the list `status`\n * filter (draft / scheduled / publishing / partially_published / published /\n * failed). Sourced from the generated query contract, never hand-listed.\n */\nexport type PostStatus = NonNullable<ListPostsQuery['status']>[number];\n\n/** The full create-post request body — what `buildCreatePost` produces. */\nexport type CreatePostInput = CreatePostBody;\n\n/** The update-post request body — what `buildUpdatePost` produces. */\nexport type UpdatePostInput = NonNullable<PostsUpdateData['body']>;\n\n/**\n * The validate-post request body — the composition-only subset of the create\n * body (`profile_id` + `variants`). `buildCreatePost` produces a superset, so\n * its output is structurally assignable here. Derived from the contract.\n */\nexport type ValidatePostInput = NonNullable<PostsValidateData['body']>;\n\n/**\n * The server's publish-readiness verdict for a composed post — `{ object,\n * publishable, issues }`. The SERVER is the sole authority on validity; the SDK\n * only builds a best-effort payload and relays this result. Derived from the\n * contract, never hand-declared.\n */\nexport type PostValidation = PostsValidateResponse;\n\n/**\n * One typed, per-variant problem in a `PostValidation` — a closed-registry\n * `code`, a human `message`, the `variant_index` + `path` it applies to, and the\n * optional `hint`/`allowed`/`got` the rule produced. Narrowed from the contract.\n */\nexport type ValidationIssue = PostValidation['issues'][number];\n\n/** A posting platform — the variant discriminator (x / linkedin / …). */\nexport type PostPlatform = PostVariantInput['platform'];\n\n/** The allowed `post_type` union for a given platform. */\nexport type PostTypeFor<P extends PostPlatform> = Extract<\n PostVariantInput,\n { platform: P }\n>['post_type'];\n\n/** The native `settings` shape for a given platform (typed per channel). */\nexport type SettingsFor<P extends PostPlatform> = Extract<\n PostVariantInput,\n { platform: P }\n>['settings'];\n\n/** Publish mode: `now` | `schedule` | `draft`. */\nexport type PublishMode = NonNullable<CreatePostBody['publish']>;\n\n/** Customer metadata on a post (the shared scalar map). */\nexport type PostMetadata = NonNullable<CreatePostBody['metadata']>;\n\n/** A media asset (image/video/gif/document) with its per-platform renditions. */\nexport type MediaResource = MediaGetResponse;\n\n/**\n * A media asset's kind (image / video / gif / document), from the contract. The\n * resource's `kind` is nullable only in the transient pre-detection `uploading`\n * window; this is the concrete family, so it strips that null — what you pass to\n * create/upload and switch over on a settled asset.\n */\nexport type MediaKind = NonNullable<MediaResource['kind']>;\n\n/** A media render target — a post platform or `google_ads`. */\nexport type MediaTarget = NonNullable<\n NonNullable<MediaCreateData['body']>['targets']\n>[number];\n\n/** The create-media response: the resource plus a multipart upload session. */\nexport type CreateMediaResult = MediaCreateResponse;\n\n/** Request body to create a media asset. */\nexport type CreateMediaInput = NonNullable<MediaCreateData['body']>;\n\n/** Request body to update a media asset (alt text / metadata / extend targets). */\nexport type UpdateMediaInput = NonNullable<MediaUpdateData['body']>;\n\n/** A page of media assets (the list envelope). */\nexport type MediaList = MediaListResponse;\n\n/** Query parameters for listing media (filters + pagination). */\nexport type ListMediaQuery = NonNullable<MediaListData['query']>;\n\n/**\n * A media asset's lifecycle state — the values the list `status` filter accepts\n * (uploading / processing / ready / failed). From the contract, never hand-listed.\n */\nexport type MediaStatus = NonNullable<ListMediaQuery['status']>;\n\n/**\n * Customer metadata — a flat scalar map (`string | number | boolean`; ≤50 keys,\n * keys ≤40 chars, ≤500-char strings, 16 KiB serialized). The contract uses this\n * SAME shape on every stored resource (profiles / posts / media); this alias is\n * derived from the media write body as a stable representative, so it's the type\n * to annotate metadata you send — never `Record<string, unknown>`. (The three\n * resources share one structural shape in the spec; were the API ever to diverge\n * one, prefer that resource's own `*['metadata']` for it.)\n */\nexport type Metadata = NonNullable<CreateMediaInput['metadata']>;\n\n/**\n * A metadata filter for list endpoints — the same scalar map, matched by exact\n * containment (multiple keys ANDed; omit for no filter). Pass the object directly\n * from the SDK/React hooks; the client URL-encodes it for the REST query.\n */\nexport type MetadataFilter = NonNullable<ListMediaQuery['metadata']>;\n","import type { AwsS3Part } from '@uppy/aws-s3';\n\nimport type {\n MediaCompleteData,\n MediaListPartsResponse,\n} from '../client/types.gen';\n\n/**\n * The casing seam between Uppy's `@uppy/aws-s3` callbacks (which speak S3\n * PascalCase — `{ PartNumber, ETag, Size }`) and our endpoints (which speak the\n * API's snake_case — `{ part_number, etag, size }`). Uppy reads the parts we hand\n * it back from `listParts` and hands us the parts to send to `complete`, so a\n * silent mismatch here would break resume/complete with no error. Both maps are\n * pure and tested; the shapes are DERIVED from the generated client + Uppy types,\n * never hand-declared.\n */\n\n/** One part as our `GET /media/{id}/multipart/parts` endpoint returns it. */\ntype ApiListedPart = MediaListPartsResponse['parts'][number];\n\n/** One part as our `POST /media/{id}/multipart/complete` body carries it. */\ntype ApiCompletePart = NonNullable<MediaCompleteData['body']>['parts'][number];\n\n/**\n * Map our snake_case listed parts → Uppy's S3 `AwsS3Part[]` so a resume after a\n * dropped connection skips the parts R2 already holds. A 1:1 field map — the\n * endpoint's `parts` contract guarantees `part_number`/`etag`/`size` on every\n * entry, so there's nothing to drop.\n */\nexport function listedPartsToS3(\n parts: readonly ApiListedPart[],\n): AwsS3Part[] {\n return parts.map((part) => ({\n PartNumber: part.part_number,\n ETag: part.etag,\n Size: part.size,\n }));\n}\n\n/**\n * Map Uppy's S3 `AwsS3Part[]` (handed to `completeMultipartUpload`) → our\n * snake_case `complete` body parts. `AwsS3Part` types `PartNumber`/`ETag` as\n * optional; a part Uppy completes with always has both, but we assert the\n * invariant explicitly rather than cast — a missing field is a hard error, not a\n * silently-dropped part.\n */\nexport function s3PartsToComplete(\n parts: readonly AwsS3Part[],\n): ApiCompletePart[] {\n return parts.map((part) => {\n if (part.PartNumber === undefined || part.ETag === undefined) {\n throw new Error(\n 'Multipart complete received a part without PartNumber/ETag.',\n );\n }\n return { part_number: part.PartNumber, etag: part.ETag };\n });\n}\n","/**\n * A failed direct-to-storage (multipart) upload. `cause` carries the underlying\n * Uppy / network / API error so callers can inspect the real reason; `message` is\n * a short human summary. Thrown by {@link uploadFile} on a hard upload failure\n * (one or more parts could not be uploaded, or `complete` failed).\n */\nexport class UploadError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = 'UploadError';\n }\n}\n","import Uppy from '@uppy/core';\nimport AwsS3 from '@uppy/aws-s3';\n\nimport type { PostrunClient } from '../client';\nimport {\n mediaAbort,\n mediaComplete,\n mediaListParts,\n mediaSignPart,\n} from '../client/sdk.gen';\nimport type { MediaCreateResponse } from '../client/types.gen';\nimport { listedPartsToS3, s3PartsToComplete } from './casing';\nimport { UploadError } from './upload-error';\n\n/**\n * The multipart session the create response carries on its `upload` field — the\n * cached `{ upload_id, key, part_size, expires_at }` Uppy's `createMultipartUpload`\n * returns without a second HTTP call. Derived from the contract, never declared.\n */\nexport type MultipartSession = NonNullable<MediaCreateResponse['upload']>;\n\nexport interface UploadFileOptions {\n /** The media id the create call returned — binds the multipart sub-endpoints. */\n mediaId: string;\n /** The cached `upload` session from the create response (`upload_id`/`key`/…). */\n session: MultipartSession;\n /** The typed client the sub-endpoint calls go through (auth + base URL). */\n client: PostrunClient;\n /** Fraction uploaded, 0..1, as bytes flow; called once with 1 on success. */\n onProgress?: (fraction: number) => void;\n /** Abort the in-flight upload (cancels Uppy and aborts the R2 session). */\n signal?: AbortSignal;\n}\n\n/**\n * Upload ONE file's bytes to R2 with a resilient, resumable multipart upload,\n * driving `@uppy/aws-s3` headless (no DOM). Uppy owns the bug-prone mechanics —\n * fixed-size chunking, parallel parts, per-part retry, resume via `listParts`,\n * and progress — wired to OUR signing endpoints (`createMultipartUpload` returns\n * the cached create-response session, so there's no extra round-trip to start).\n *\n * Resolves when the upload is fully assembled on R2 (the object then settles\n * through the same probe/transform pipeline a `source_url` import does). Throws\n * {@link UploadError} on a hard failure, or a `DOMException('AbortError')` when\n * `signal` fires.\n */\nexport async function uploadFile(\n file: Blob,\n options: UploadFileOptions,\n): Promise<void> {\n const { mediaId, session, client, onProgress, signal } = options;\n\n if (signal?.aborted) {\n throw new DOMException('Upload aborted', 'AbortError');\n }\n\n const uppy = new Uppy({ autoProceed: false }).use(AwsS3, {\n // Every file is one resumable multipart upload — no size-threshold split, so\n // the path is uniform and a tiny file is just a 1-part upload (PLAN §2). The\n // literal `true` (not a `() => true`) selects Uppy's always-multipart variant.\n shouldUseMultipart: true,\n // Pin Uppy's chunk size to the server's part size — every part except the\n // last MUST be exactly this (an R2 requirement the session encodes).\n getChunkSize: () => session.part_size,\n\n // Start: hand back the cached session from the create response — NO HTTP.\n createMultipartUpload: () => ({\n uploadId: session.upload_id,\n key: session.key,\n }),\n\n // Sign one part → POST /media/{id}/multipart/sign. The sub-endpoint reads the\n // upload id from the asset row, so the body carries only the part number.\n signPart: async (_file, { partNumber }) => {\n const { data } = await mediaSignPart({\n client,\n path: { id: mediaId },\n body: { part_number: partNumber },\n });\n return { url: data.url };\n },\n\n // Resume: GET /media/{id}/multipart/parts → map snake_case → S3 PascalCase so\n // Uppy skips the parts R2 already holds after a dropped connection.\n listParts: async () => {\n const { data } = await mediaListParts({\n client,\n path: { id: mediaId },\n });\n return listedPartsToS3(data.parts);\n },\n\n // Finish: map Uppy's S3 parts → snake_case → POST /media/{id}/multipart/complete.\n completeMultipartUpload: async (_file, { parts }) => {\n await mediaComplete({\n client,\n path: { id: mediaId },\n body: { parts: s3PartsToComplete(parts) },\n });\n return {};\n },\n\n // Cancel: POST /media/{id}/multipart/abort (idempotent server-side).\n abortMultipartUpload: async () => {\n await mediaAbort({ client, path: { id: mediaId } });\n },\n });\n\n // Uppy's `progress` event is the overall 0–100 across all parts of the queue\n // (one file here) — forward it as a 0..1 fraction.\n uppy.on('progress', (percent) => onProgress?.(percent / 100));\n\n const onAbort = () => {\n uppy.cancelAll();\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n try {\n uppy.addFile({ name: fileName(file), type: file.type, data: file });\n const result = await uppy.upload();\n\n if (signal?.aborted) {\n throw new DOMException('Upload aborted', 'AbortError');\n }\n\n const failed = result?.failed ?? [];\n if (failed.length > 0) {\n throw new UploadError('Multipart upload failed.', {\n cause: failed[0]?.error,\n });\n }\n\n onProgress?.(1);\n } finally {\n signal?.removeEventListener('abort', onAbort);\n uppy.destroy();\n }\n}\n\n/** Uppy needs a file name; a bare `Blob` has none, so fall back to a stable one. */\nfunction fileName(file: Blob): string {\n return file instanceof File ? file.name : 'upload';\n}\n","import type { TikTokPrivacyLevel } from './resources';\n\n/**\n * Map a TikTok privacy level to a short, human label for the composer's privacy\n * dropdown — the presentation the API deliberately keeps OFF the wire (the\n * `creator-info` response returns the typed `privacy_options`; the SDK owns how\n * they read).\n *\n * Exhaustive over the closed `TikTokPrivacyLevel` union with no `default` branch,\n * so adding a TikTok privacy level upstream surfaces as a compile error here\n * rather than silently rendering a raw enum string.\n */\nexport function tiktokPrivacyLabel(value: TikTokPrivacyLevel): string {\n switch (value) {\n case 'PUBLIC_TO_EVERYONE':\n return 'Everyone';\n case 'MUTUAL_FOLLOW_FRIENDS':\n return 'Friends';\n case 'FOLLOWER_OF_CREATOR':\n return 'Followers';\n case 'SELF_ONLY':\n return 'Only you';\n }\n}\n","import type { LinkedInPostVariant } from './resources';\n\n/**\n * One LinkedIn poll duration — DERIVED from the contract (the poll `duration`\n * field), never hand-listed, so it can't drift from the API shape.\n */\nexport type LinkedInPollDuration = NonNullable<\n NonNullable<LinkedInPostVariant['settings']>['poll']\n>['duration'];\n\n/**\n * Map a poll duration to the short \"time left\" label LinkedIn shows on the poll\n * card — the presentation the wire keeps enum-only. Exhaustive over the closed\n * union with no `default` branch, so adding a duration upstream surfaces as a\n * compile error here rather than silently rendering a raw enum string.\n */\nexport function linkedinPollDurationLabel(value: LinkedInPollDuration): string {\n switch (value) {\n case 'ONE_DAY':\n return '1 day left';\n case 'THREE_DAYS':\n return '3 days left';\n case 'SEVEN_DAYS':\n return '1 week left';\n case 'FOURTEEN_DAYS':\n return '2 weeks left';\n }\n}\n","import type { XPostVariant } from './resources';\n\n/** An X poll's settings shape — DERIVED from the contract, never re-declared. */\nexport type XPoll = NonNullable<\n NonNullable<XPostVariant['settings']>['poll']\n>;\n\n/** `n unit left`, pluralizing the unit. */\nfunction plural(n: number, unit: string): string {\n return `${n} ${unit}${n === 1 ? '' : 's'} left`;\n}\n\n/**\n * Turn an X poll's `duration_minutes` (5–10080, i.e. 5 min – 7 days) into the\n * \"time left\" label the poll card shows, floored to the coarsest sensible unit\n * (days ≥ 1 day, else hours ≥ 1 hour, else minutes). Pure — pin it in tests.\n */\nexport function xPollDurationLabel(durationMinutes: number): string {\n if (durationMinutes >= 1440) {\n return plural(Math.floor(durationMinutes / 1440), 'day');\n }\n if (durationMinutes >= 60) {\n return plural(Math.floor(durationMinutes / 60), 'hour');\n }\n return plural(durationMinutes, 'minute');\n}\n","import type { GoogleAdTreeNode } from './client/types.gen';\n\n/**\n * The Google Ads tree, assembled. `GET /v1/google/{conn}/ads/tree` returns FLAT,\n * native `GoogleAdTreeNode`s with Google's authoritative per-level metrics (no\n * client roll-up) keyed by `parent_id`; turning that into a nested tree is generic,\n * so it lives here rather than in every consumer.\n */\n\n/** A flat tree node with its children attached. Extends the generated node — never\n * a redeclared shape, so it can't drift from the API. */\nexport interface AdTreeNode extends GoogleAdTreeNode {\n children: AdTreeNode[];\n}\n\n/**\n * Group flat tree nodes into a nested tree by `parent_id`. A node whose parent is\n * NOT in the set is a root — so a campaign-level page (campaigns have\n * `parent_id: null`) and a per-campaign subtree page (ad groups whose campaign is\n * absent) each group correctly on their own, and concatenating a campaign page with\n * an expanded campaign's subtree page nests the subtree under that campaign. Input\n * order is preserved within every level. Pure — the input is not mutated.\n */\nexport function groupAdTree(nodes: GoogleAdTreeNode[]): AdTreeNode[] {\n const byId = new Map<string, AdTreeNode>();\n\n for (const node of nodes) {\n byId.set(node.id, { ...node, children: [] });\n }\n\n const roots: AdTreeNode[] = [];\n\n for (const node of nodes) {\n const treeNode = byId.get(node.id);\n\n // Unreachable — every id was just inserted — but keeps the access total\n // without a non-null assertion.\n if (treeNode === undefined) {\n continue;\n }\n\n const parent =\n node.parent_id === null ? undefined : byId.get(node.parent_id);\n\n if (parent === undefined) {\n roots.push(treeNode);\n } else {\n parent.children.push(treeNode);\n }\n }\n\n return roots;\n}\n","import type {\n SettingsFor,\n TikTokCreatorInfo,\n TikTokPostVariant,\n} from './resources';\nimport { type TikTokPrivacyLevel } from './resources';\nimport { tiktokPrivacyLabel } from './tiktok';\n\n/**\n * TikTok's mandatory publish-options model — the framework-agnostic glue between\n * the live `creator-info` payload and a TikTok post's settings. This is the\n * SINGLE source for the no-default / creator-gated / commercial-disclosure rules\n * TikTok's Required UX mandates; the React panel and the dashboard composer both\n * consume it (never re-implement it).\n *\n * The creator-info shape, the privacy union, and the privacy label come from the\n * contract (`./resources`, `./tiktok`) — never re-declared. This module owns the\n * controlled FORM value, the selection rules, and the mapping into the SDK's\n * per-platform `tiktok` settings.\n */\n\n/** TikTok caption caps, mirrored from the publish contract (`BODY_MAX.tiktok_*`).\n * A video post caps the title (2200), a photo post the description (4000). Lives\n * here (not in React) so non-React consumers (MCP, API client, scripts) can use\n * it. The server enforces these; the editor surfaces them. */\nexport const TIKTOK_CAPTION_MAX = { video: 2200, photo: 4000 } as const;\n\n/** The caption cap for a given post_type (video → title cap, photo → description). */\nexport function captionMaxFor(postType: TikTokPostVariant['post_type']): number {\n return postType === 'video'\n ? TIKTOK_CAPTION_MAX.video\n : TIKTOK_CAPTION_MAX.photo;\n}\n\n/** The creator-gated interactions TikTok requires us to surface — DERIVED from\n * the creator-info contract (`interaction` keys), never hand-listed, so it can\n * never drift from the API shape. */\nexport type TikTokInteractionKey = keyof TikTokCreatorInfo['interaction'];\n\n/** Which commercial-disclosure brand option a control maps to. */\nexport type TikTokBrandKind = 'your_brand' | 'branded_content';\n\n/**\n * The controlled value a TikTok composer/preview owns. `privacy_level` is\n * optional because TikTok requires the user to pick — there is NO default (an\n * absent value is the \"must choose\" state the publish gate keys off). Interaction\n * flags are POSITIVE (\"allow\"); `tiktokSettings` inverts them to the API's\n * `disable_*` shape.\n */\nexport interface TikTokOptionsValue {\n privacy_level?: TikTokPrivacyLevel;\n allow_comment: boolean;\n allow_duet: boolean;\n allow_stitch: boolean;\n /** Commercial Content Disclosure master toggle (off by default). When on, at\n * least one of `your_brand` / `branded_content` must be chosen. */\n commercial_disclosure: boolean;\n your_brand: boolean;\n branded_content: boolean;\n /** AI-generated content disclosure → the API's `is_aigc`. Video-only, off by\n * default. */\n aigc: boolean;\n}\n\nexport interface TikTokPrivacyChoice {\n level: TikTokPrivacyLevel;\n label: string;\n}\n\nexport interface TikTokInteractionRow {\n key: TikTokInteractionKey;\n label: string;\n /** Disabled (and forced off) when the creator forbids it — TikTok mandate. */\n disabled: boolean;\n}\n\n/* --------------------------- required wording --------------------------- */\n\n/** TikTok's Music Usage Confirmation — the legal URL the declaration links to. */\nexport const TIKTOK_MUSIC_CONFIRMATION_URL =\n 'https://www.tiktok.com/legal/page/global/music-usage-confirmation/en';\n\n/** TikTok's Branded Content Policy — linked when a paid partnership is disclosed. */\nexport const TIKTOK_BRANDED_CONTENT_POLICY_URL =\n 'https://www.tiktok.com/legal/page/global/bc-policy/en';\n\n/** TikTok's exact required prompt when disclosure is on but nothing is chosen. */\nexport const TIKTOK_DISCLOSURE_REQUIRED_HINT =\n 'You need to indicate if your content promotes yourself, a third party, or both.';\n\n/** TikTok's required prompt when no audience has been picked yet. */\nexport const TIKTOK_AUDIENCE_REQUIRED_HINT =\n 'Choose who can view this video before publishing.';\n\n/** TikTok Required UX: clients must notify users a published post may take a few\n * minutes to process. No exact string is mandated; this conveys it. */\nexport const TIKTOK_PROCESSING_NOTICE =\n 'After you publish, it may take a few minutes for your post to process and appear on your TikTok profile.';\n\n/* ------------------------------ defaults ------------------------------ */\n\n/**\n * The initial value for a TikTok post: nothing pre-selected. TikTok's Required UX\n * mandates the user actively choose — no default privacy, every interaction OFF,\n * no disclosure. The panel still greys the interactions the creator forbids.\n */\nexport function defaultTikTokOptions(): TikTokOptionsValue {\n return {\n privacy_level: undefined,\n allow_comment: false,\n allow_duet: false,\n allow_stitch: false,\n commercial_disclosure: false,\n your_brand: false,\n branded_content: false,\n aigc: false,\n };\n}\n\n/* ------------------------------ audience ------------------------------ */\n\n/** The audience choices for the dropdown — one per level the creator allows. */\nexport function privacyChoices(\n options: readonly TikTokPrivacyLevel[],\n): TikTokPrivacyChoice[] {\n return options.map((level) => ({ level, label: tiktokPrivacyLabel(level) }));\n}\n\n/** Narrow a raw dropdown value to a `TikTokPrivacyLevel` against the creator's\n * allowed options — returns `undefined` for anything not offered (no cast). */\nexport function parsePrivacyLevel(\n value: string,\n options: readonly TikTokPrivacyLevel[],\n): TikTokPrivacyLevel | undefined {\n return options.find((option) => option === value);\n}\n\n/** Effective brand flags (only meaningful while disclosure is on). */\nfunction discloses(value: TikTokOptionsValue, kind: TikTokBrandKind): boolean {\n return value.commercial_disclosure && value[kind];\n}\n\n/**\n * The audience levels to offer. Branded content can ONLY be public/friends —\n * TikTok forbids posting it SELF_ONLY (private) — so that option is dropped when\n * branded content is disclosed. Otherwise every creator-allowed level is offered.\n */\nexport function audiencePrivacyOptions(\n options: readonly TikTokPrivacyLevel[],\n value: TikTokOptionsValue,\n): TikTokPrivacyLevel[] {\n return discloses(value, 'branded_content')\n ? options.filter((option) => option !== 'SELF_ONLY')\n : [...options];\n}\n\n/** No audience picked yet — must BLOCK publish (TikTok mandates an explicit\n * choice with no default; the API rejects a missing `privacy_level` too). */\nexport function audienceUnselected(value: TikTokOptionsValue): boolean {\n return value.privacy_level === undefined;\n}\n\n/* --------------------------- commercial disclosure --------------------------- */\n\n/** Disclosure is on but neither brand option is chosen — TikTok requires ≥1. */\nexport function commercialDisclosureIncomplete(\n value: TikTokOptionsValue,\n): boolean {\n return (\n value.commercial_disclosure && !value.your_brand && !value.branded_content\n );\n}\n\n/** True when the post is disclosed as branded content (drives the BC declaration). */\nexport function brandedContentDeclared(value: TikTokOptionsValue): boolean {\n return discloses(value, 'branded_content');\n}\n\n/**\n * TikTok's required label notice for the disclosed content — branded content\n * (alone OR with your-brand) is \"Paid partnership\"; your-brand alone is\n * \"Promotional content\". `null` when nothing is disclosed. Wording is verbatim.\n */\nexport function commercialLabelNotice(\n value: TikTokOptionsValue,\n): string | null {\n if (discloses(value, 'branded_content')) {\n return \"Your photo/video will be labeled as 'Paid partnership'\";\n }\n if (discloses(value, 'your_brand')) {\n return \"Your photo/video will be labeled as 'Promotional content'\";\n }\n return null;\n}\n\n/** Toggle the master disclosure switch. Turning it OFF clears both brand\n * selections (a hidden control must not keep sending a stale selection). */\nexport function setCommercialDisclosure(\n value: TikTokOptionsValue,\n on: boolean,\n): TikTokOptionsValue {\n return on\n ? { ...value, commercial_disclosure: true }\n : {\n ...value,\n commercial_disclosure: false,\n your_brand: false,\n branded_content: false,\n };\n}\n\n/**\n * Set one brand option. Enabling branded content while a private audience is\n * picked clears the audience (branded content can't be SELF_ONLY; no default — the\n * user re-picks from the now-narrowed options).\n */\nexport function setBrandKind(\n value: TikTokOptionsValue,\n kind: TikTokBrandKind,\n on: boolean,\n): TikTokOptionsValue {\n const clearPrivacy =\n kind === 'branded_content' && on && value.privacy_level === 'SELF_ONLY';\n return {\n ...value,\n [kind]: on,\n ...(clearPrivacy ? { privacy_level: undefined } : {}),\n };\n}\n\n/* ------------------------------ interactions ------------------------------ */\n\n/** True when the creator does NOT permit this interaction (so it's locked off). */\nexport function isInteractionForbidden(\n info: TikTokCreatorInfo,\n key: TikTokInteractionKey,\n): boolean {\n return !info.interaction[key];\n}\n\nconst INTERACTION_LABELS: Record<TikTokInteractionKey, string> = {\n comment: 'Comment',\n duet: 'Duet',\n stitch: 'Stitch',\n};\n\n/** Duet & Stitch are video-remix features — TikTok states they are NOT applicable\n * to photo posts (\"only 'Allow Comment' can be displayed\"), and the API rejects\n * `disable_duet`/`disable_stitch` on a non-video post. So a photo post shows only\n * Comment. */\nconst VIDEO_INTERACTIONS: readonly TikTokInteractionKey[] = [\n 'comment',\n 'duet',\n 'stitch',\n];\nconst PHOTO_INTERACTIONS: readonly TikTokInteractionKey[] = ['comment'];\n\nconst INTERACTION_VALUE_KEY = {\n comment: 'allow_comment',\n duet: 'allow_duet',\n stitch: 'allow_stitch',\n} as const satisfies Record<TikTokInteractionKey, keyof TikTokOptionsValue>;\n\n/** The interaction toggles to render — video shows Comment/Duet/Stitch (each\n * disabled iff the creator forbids it); photo shows only Comment. */\nexport function interactionRows(\n info: TikTokCreatorInfo,\n isVideo: boolean,\n): TikTokInteractionRow[] {\n const order = isVideo ? VIDEO_INTERACTIONS : PHOTO_INTERACTIONS;\n return order.map((key) => ({\n key,\n label: INTERACTION_LABELS[key],\n disabled: isInteractionForbidden(info, key),\n }));\n}\n\n/** The `TikTokOptionsValue` flag an interaction key maps to. */\nexport function interactionValueKey(\n key: TikTokInteractionKey,\n): keyof TikTokOptionsValue {\n return INTERACTION_VALUE_KEY[key];\n}\n\n/** Immutably set an interaction flag — returns a new value, never mutates. */\nexport function toggleInteraction(\n value: TikTokOptionsValue,\n key: TikTokInteractionKey,\n on: boolean,\n): TikTokOptionsValue {\n return { ...value, [INTERACTION_VALUE_KEY[key]]: on };\n}\n\n/* -------------------------------- mapping -------------------------------- */\n\n/**\n * Map the composer's positive value into the SDK's `tiktok` settings — INVERTING\n * `allow_*` into TikTok's `disable_*`. `disable_duet`/`disable_stitch`/`is_aigc`\n * are video-only (the API rejects them on a photo post), so a photo post sends\n * just `privacy_level` + `disable_comment` + the brand toggles.\n */\nexport function tiktokSettings(\n value: TikTokOptionsValue,\n isVideo: boolean,\n): NonNullable<SettingsFor<'tiktok'>> {\n const base = {\n privacy_level: value.privacy_level,\n disable_comment: !value.allow_comment,\n brand_organic_toggle: discloses(value, 'your_brand'),\n brand_content_toggle: discloses(value, 'branded_content'),\n };\n\n return isVideo\n ? {\n ...base,\n disable_duet: !value.allow_duet,\n disable_stitch: !value.allow_stitch,\n is_aigc: value.aigc,\n }\n : base;\n}\n\n/* ------------------------------ readiness ------------------------------ */\n\n/**\n * Is the TikTok post ready to publish (client-side gate mirroring the API)?\n * Audience must be chosen and, if disclosure is on, a brand kind must be picked.\n * The caption-cap + media-count checks live with their own components.\n */\nexport function tiktokOptionsReady(value: TikTokOptionsValue): boolean {\n // Branded content cannot be SELF_ONLY (the API rejects it) — mirror that here so\n // the Post button never enables on a state the wire will refuse, even if a host\n // constructs the options object directly (bypassing setBrandKind's clearing).\n const brandedPrivate =\n discloses(value, 'branded_content') && value.privacy_level === 'SELF_ONLY';\n return (\n !audienceUnselected(value) &&\n !commercialDisclosureIncomplete(value) &&\n !brandedPrivate\n );\n}\n","import type {\n Connection,\n CreatePostInput,\n MediaResource,\n PostMetadata,\n PostPlatform,\n PostTypeFor,\n PostVariantInput,\n PublishMode,\n SettingsFor,\n UpdatePostInput,\n} from './resources';\n\n/* --------------------------------- types --------------------------------- */\n\n/** A media attachment — the uploaded asset (or just its id + kind). */\nexport type MediaInput = Pick<MediaResource, 'id' | 'kind'>;\n\n/** Base content, shared across channels unless a channel overrides it. */\nexport interface PostContent {\n body?: string;\n media?: readonly MediaInput[];\n}\n\n/**\n * One channel's config. The SDK owns the plumbing (connection, media, assembly)\n * and derives `post_type` from the media; the CUSTOMER owns `settings` — the\n * platform's native, fully-typed config. An Instagram setting on an X channel is\n * a compile error. `postType` is an optional override of the derived value.\n */\nexport interface ChannelConfig<P extends PostPlatform> {\n settings: SettingsFor<P>;\n postType?: PostTypeFor<P>;\n body?: string;\n media?: readonly MediaInput[];\n connectionId?: string;\n}\n\n/** Channels keyed by platform — each value typed to that platform. */\nexport type Channels = { [P in PostPlatform]?: ChannelConfig<P> };\n\nexport interface ComposePostInput {\n profileId: string;\n content?: PostContent;\n channels: Channels;\n publish?: PublishMode;\n scheduleAt?: string;\n externalId?: string;\n metadata?: PostMetadata;\n tags?: readonly string[];\n notes?: string;\n dryRun?: boolean;\n}\n\n/** A post edit. Omit `channels` for a light edit; include it to rebuild variants. */\nexport interface ComposeUpdateInput {\n content?: PostContent;\n channels?: Channels;\n publish?: PublishMode;\n scheduleAt?: string;\n externalId?: string;\n metadata?: PostMetadata;\n tags?: readonly string[];\n notes?: string;\n dryRun?: boolean;\n}\n\n/** The connections a build resolves against (accepts a full `Connection[]`). */\nexport type ConnectionRef = Pick<Connection, 'id' | 'platform'>;\n\n/**\n * Thrown only for a genuine USAGE error the SDK can't paper over — an\n * unresolvable connection, or a build with zero channels. It is NEVER thrown for\n * a media/`post_type` combination: validity (which media pair with which\n * post_type, document support, count limits, …) is the SERVER's job, surfaced as\n * typed `ValidationIssue`s by `POST /v1/posts/validate`. `derivePostType` is\n * best-effort SUGAR, so the SDK can always build an in-progress composition.\n */\nexport class ComposeError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ComposeError';\n }\n}\n\ntype VariantFor<P extends PostPlatform> = Extract<PostVariantInput, { platform: P }>;\n\n/* ----------------------------- platform handlers -------------------------- */\n\n/** What a handler receives once `post_type` is resolved (derived or explicit). */\ninterface ResolvedChannel<P extends PostPlatform> {\n settings: SettingsFor<P>;\n postType: PostTypeFor<P>;\n connectionId: string;\n body: string | undefined;\n media: readonly { media_id: string }[];\n}\n\n/**\n * Each platform implements two layers:\n * - `derivePostType` — the SUGAR: a BEST-EFFORT default `post_type` guessed from\n * the media. Pure and TOTAL — it NEVER throws and never decides validity; it\n * only runs when the caller doesn't pass `postType`. The SERVER\n * (`POST /v1/posts/validate`) is the sole authority on whether the\n * composition is publishable, returning typed `ValidationIssue`s.\n * - `buildVariant` — the explicit CORE: assemble the typed variant from a\n * resolved channel. Passes `settings` straight through (the customer owns it).\n * Splitting them keeps the core stable and the derivation purely additive.\n */\ninterface PlatformHandler<P extends PostPlatform> {\n derivePostType(media: readonly MediaInput[]): PostTypeFor<P>;\n buildVariant(resolved: ResolvedChannel<P>): VariantFor<P>;\n}\n\nfunction countKinds(media: readonly MediaInput[]) {\n let images = 0;\n let videos = 0;\n let documents = 0;\n for (const item of media) {\n if (item.kind === 'video') videos += 1;\n else if (item.kind === 'document') documents += 1;\n else images += 1; // image | gif\n }\n return { images, videos, documents };\n}\n\n/**\n * X / LinkedIn / Facebook have no mixed-media placement: a post is text, images,\n * OR a single video. This is a BEST-EFFORT guess — it never throws. When a video\n * is present it wins the placement (`video`/`reel`), else 2+ images is\n * `multi_image` and a lone item is `single_image`. The SERVER rules on whether\n * the actual combination (a blend, >1 video, a document) is publishable.\n */\nfunction deriveSinglePlacement<V extends 'video' | 'reel'>(\n media: readonly MediaInput[],\n videoType: V,\n): 'single_image' | 'multi_image' | V {\n const { images, videos } = countKinds(media);\n if (videos >= 1) return videoType;\n return images >= 2 ? 'multi_image' : 'single_image';\n}\n\nconst xHandler: PlatformHandler<'x'> = {\n derivePostType: (media) => {\n if (media.length === 0) return 'text';\n return deriveSinglePlacement(media, 'video');\n },\n buildVariant: ({ settings, postType, connectionId, body, media }) => ({\n platform: 'x',\n post_type: postType,\n connection_id: connectionId,\n body,\n media: [...media],\n settings,\n }),\n};\n\nconst linkedInHandler: PlatformHandler<'linkedin'> = {\n derivePostType: (media) => {\n if (media.length === 0) return 'text';\n if (countKinds(media).documents > 0) {\n // A document rides as a `single_image` post_type; the customer sets\n // `content_kind: 'document'` in settings — we don't infer it. Best-effort:\n // multi-document is a server concern, not a local throw.\n return 'single_image';\n }\n return deriveSinglePlacement(media, 'video');\n },\n buildVariant: ({ settings, postType, connectionId, body, media }) => ({\n platform: 'linkedin',\n post_type: postType,\n connection_id: connectionId,\n body,\n media: [...media],\n settings,\n }),\n};\n\nconst facebookHandler: PlatformHandler<'facebook_page'> = {\n derivePostType: (media) => {\n if (media.length === 0) return 'text';\n return deriveSinglePlacement(media, 'reel');\n },\n buildVariant: ({ settings, postType, connectionId, body, media }) => ({\n platform: 'facebook_page',\n post_type: postType,\n connection_id: connectionId,\n body,\n media: [...media],\n settings,\n }),\n};\n\nconst instagramHandler: PlatformHandler<'instagram'> = {\n derivePostType: (media) => {\n // Best-effort: empty media falls back to `single_image` (the server rules on\n // whether Instagram needs media). 2+ items is a carousel (the API allows it\n // to mix image/gif/video); a lone video is a reel, else a single image.\n if (media.length >= 2) return 'carousel';\n return countKinds(media).videos === 1 ? 'reel' : 'single_image';\n },\n buildVariant: ({ settings, postType, connectionId, body, media }) => ({\n platform: 'instagram',\n post_type: postType,\n connection_id: connectionId,\n body,\n media: [...media],\n settings,\n }),\n};\n\nconst tiktokHandler: PlatformHandler<'tiktok'> = {\n derivePostType: (media) => {\n // Best-effort: TikTok keeps video and photo posts separate (a video post is\n // one standalone video; photos form a single image or a multi-photo\n // carousel). A video present wins `video`; else 2+ images is a carousel and a\n // lone item is `single_image`. The SERVER rules on a blend / >1 video / docs /\n // empty media.\n const { images, videos } = countKinds(media);\n if (videos >= 1) return 'video';\n return images >= 2 ? 'carousel' : 'single_image';\n },\n buildVariant: ({ settings, postType, connectionId, body, media }) => ({\n platform: 'tiktok',\n post_type: postType,\n connection_id: connectionId,\n body,\n media: [...media],\n settings,\n }),\n};\n\n/**\n * The handler registry. A `Record<PostPlatform, …>` (no optional keys), so a\n * platform the contract defines without a handler here is a COMPILE ERROR.\n */\nconst PLATFORM_HANDLERS: { [P in PostPlatform]: PlatformHandler<P> } = {\n x: xHandler,\n linkedin: linkedInHandler,\n facebook_page: facebookHandler,\n instagram: instagramHandler,\n tiktok: tiktokHandler,\n};\n\n/** Narrow any platform string to a posting platform. */\nexport function isPostPlatform(value: string): value is PostPlatform {\n return Object.prototype.hasOwnProperty.call(PLATFORM_HANDLERS, value);\n}\n\n/** Posting platforms — the single source, derived from the handler registry\n * (the `.filter` type-guard narrows `string[]` → `PostPlatform[]`, cast-free). */\nexport const POST_PLATFORMS: PostPlatform[] =\n Object.keys(PLATFORM_HANDLERS).filter(isPostPlatform);\n\n/* -------------------------------- assembly -------------------------------- */\n\nfunction resolveConnectionId(\n platform: PostPlatform,\n connectionId: string | undefined,\n connections: readonly ConnectionRef[],\n): string {\n if (connectionId) return connectionId;\n const match = connections.find((connection) => connection.platform === platform);\n if (!match) {\n throw new ComposeError(\n `No connection for \"${platform}\" on this profile. Connect the account or pass connectionId.`,\n );\n }\n return match.id;\n}\n\n/** Resolve one channel into its typed variant. Generic over the concrete `P`, so\n * the handler / config / result stay correlated with no cast. */\nfunction buildChannel<P extends PostPlatform>(\n handler: PlatformHandler<P>,\n platform: P,\n config: ChannelConfig<P>,\n content: PostContent,\n connections: readonly ConnectionRef[],\n): VariantFor<P> {\n const media = config.media ?? content.media ?? [];\n return handler.buildVariant({\n settings: config.settings,\n postType: config.postType ?? handler.derivePostType(media),\n connectionId: resolveConnectionId(platform, config.connectionId, connections),\n body: config.body ?? content.body,\n media: media.map((item) => ({ media_id: item.id })),\n });\n}\n\n/**\n * Resolve ONE platform's channel, or `undefined` when the caller didn't include\n * it. Generic over the concrete `P`, so `channels[platform]` and\n * `PLATFORM_HANDLERS[platform]` stay correlated to the same platform with no cast.\n * Wrapping the registry lookup in this generic is what lets `buildVariants`\n * iterate `POST_PLATFORMS` (a union) without the per-member variance error.\n */\nfunction collectChannel<P extends PostPlatform>(\n platform: P,\n channels: Channels,\n content: PostContent,\n connections: readonly ConnectionRef[],\n): VariantFor<P> | undefined {\n const config = channels[platform];\n if (!config) return undefined;\n return buildChannel(\n PLATFORM_HANDLERS[platform],\n platform,\n config,\n content,\n connections,\n );\n}\n\nfunction buildVariants(\n content: PostContent,\n channels: Channels,\n connections: readonly ConnectionRef[],\n): PostVariantInput[] {\n // Driven by POST_PLATFORMS (derived from the exhaustive PLATFORM_HANDLERS), so a\n // new platform added to the registry is dispatched here automatically — no\n // hand-maintained per-platform branch to forget (the gap that once dropped TikTok).\n const variants = POST_PLATFORMS.flatMap((platform) => {\n const variant = collectChannel(platform, channels, content, connections);\n return variant ? [variant] : [];\n });\n\n if (variants.length === 0) {\n // The ONE retained composition-level guard: \"you called build with no\n // channels\" is a programmer USAGE error, not a per-end-user validity judgment.\n // It is NOT a verdict on whether any media/post_type combination is\n // publishable — that authority lives entirely on the server.\n throw new ComposeError('At least one channel is required.');\n }\n return variants;\n}\n\n/**\n * Turn an ergonomic `{ content, channels }` input into the exact\n * `CreatePostInput` the API expects — resolving each channel's connection,\n * attaching the shared/overridden media, and deriving a best-effort `post_type`\n * from that media (sugar — the server validates the real composition). The build\n * is TOTAL: it never throws for a media/post_type combination, so an in-progress\n * post can always be built to validate. The customer never assembles `variants[]`\n * or sees a `connection_id`, and owns each channel's typed `settings`.\n */\nexport function buildCreatePost(\n input: ComposePostInput,\n connections: readonly ConnectionRef[],\n): CreatePostInput {\n return {\n profile_id: input.profileId,\n publish: input.publish,\n schedule_at: input.scheduleAt,\n external_id: input.externalId,\n metadata: input.metadata,\n tags: input.tags ? [...input.tags] : undefined,\n notes: input.notes,\n dry_run: input.dryRun,\n variants: buildVariants(input.content ?? {}, input.channels, connections),\n };\n}\n\n/** The envelope fields the API counts as a real edit (`dry_run` deliberately not). */\nconst MUTABLE_ENVELOPE_KEYS = [\n 'publish',\n 'scheduleAt',\n 'externalId',\n 'metadata',\n 'tags',\n 'notes',\n] as const satisfies readonly (keyof ComposeUpdateInput)[];\n\n/**\n * Build an `UpdatePostInput`. With `channels`, the full variant set is rebuilt\n * (the API's PATCH replaces it); without it, only the envelope changes.\n */\nexport function buildUpdatePost(\n input: ComposeUpdateInput,\n connections: readonly ConnectionRef[] = [],\n): UpdatePostInput {\n const changesEnvelope = MUTABLE_ENVELOPE_KEYS.some((key) => input[key] !== undefined);\n if (!input.channels && !changesEnvelope) {\n throw new ComposeError(\n 'A post update must change at least one field — pass content/channels or an ' +\n 'envelope field (publish, scheduleAt, tags, …).',\n );\n }\n\n return {\n publish: input.publish,\n schedule_at: input.scheduleAt,\n external_id: input.externalId,\n metadata: input.metadata,\n tags: input.tags ? [...input.tags] : undefined,\n notes: input.notes,\n dry_run: input.dryRun,\n variants: input.channels\n ? buildVariants(input.content ?? {}, input.channels, connections)\n : undefined,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/resources.ts","../src/upload/casing.ts","../src/upload/upload-error.ts","../src/upload/upload-file.ts","../src/tiktok.ts","../src/linkedin.ts","../src/x.ts","../src/ads-tree.ts","../src/tiktok-options.ts","../src/compose.ts"],"names":[],"mappings":";;;;;;;AA0KO,IAAM,WAAA,GAAc,CAAC,IAAA,KAC1B,IAAA,CAAK,MAAA,KAAW;AAQX,IAAM,cAAA,GAAiB,CAAC,IAAA,KAC7B,IAAA,CAAK,QAAA,CAAS,OAAO,CAAC,OAAA,KAAY,OAAA,CAAQ,MAAA,KAAW,QAAQ;;;ACvJxD,SAAS,gBACd,KAAA,EACa;AACb,EAAA,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,MAAU;AAAA,IAC1B,YAAY,IAAA,CAAK,WAAA;AAAA,IACjB,MAAM,IAAA,CAAK,IAAA;AAAA,IACX,MAAM,IAAA,CAAK;AAAA,GACb,CAAE,CAAA;AACJ;AASO,SAAS,kBACd,KAAA,EACmB;AACnB,EAAA,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACzB,IAAA,IAAI,IAAA,CAAK,UAAA,KAAe,MAAA,IAAa,IAAA,CAAK,SAAS,MAAA,EAAW;AAC5D,MAAA,MAAM,IAAI,KAAA;AAAA,QACR;AAAA,OACF;AAAA,IACF;AACA,IAAA,OAAO,EAAE,WAAA,EAAa,IAAA,CAAK,UAAA,EAAY,IAAA,EAAM,KAAK,IAAA,EAAK;AAAA,EACzD,CAAC,CAAA;AACH;;;ACnDO,IAAM,WAAA,GAAN,cAA0B,KAAA,CAAM;AAAA,EACrC,WAAA,CAAY,SAAiB,OAAA,EAA+B;AAC1D,IAAA,KAAA,CAAM,SAAS,OAAO,CAAA;AACtB,IAAA,IAAA,CAAK,IAAA,GAAO,aAAA;AAAA,EACd;AACF;;;ACmCA,eAAsB,UAAA,CACpB,MACA,OAAA,EACe;AACf,EAAA,MAAM,EAAE,OAAA,EAAS,OAAA,EAAS,MAAA,EAAQ,UAAA,EAAY,QAAO,GAAI,OAAA;AAEzD,EAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,IAAA,MAAM,IAAI,YAAA,CAAa,gBAAA,EAAkB,YAAY,CAAA;AAAA,EACvD;AAEA,EAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,EAAE,aAAa,KAAA,EAAO,CAAA,CAAE,GAAA,CAAI,KAAA,EAAO;AAAA;AAAA;AAAA;AAAA,IAIvD,kBAAA,EAAoB,IAAA;AAAA;AAAA;AAAA,IAGpB,YAAA,EAAc,MAAM,OAAA,CAAQ,SAAA;AAAA;AAAA,IAG5B,uBAAuB,OAAO;AAAA,MAC5B,UAAU,OAAA,CAAQ,SAAA;AAAA,MAClB,KAAK,OAAA,CAAQ;AAAA,KACf,CAAA;AAAA;AAAA;AAAA,IAIA,QAAA,EAAU,OAAO,KAAA,EAAO,EAAE,YAAW,KAAM;AACzC,MAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,aAAA,CAAc;AAAA,QACnC,MAAA;AAAA,QACA,IAAA,EAAM,EAAE,EAAA,EAAI,OAAA,EAAQ;AAAA,QACpB,IAAA,EAAM,EAAE,WAAA,EAAa,UAAA;AAAW,OACjC,CAAA;AACD,MAAA,OAAO,EAAE,GAAA,EAAK,IAAA,CAAK,GAAA,EAAI;AAAA,IACzB,CAAA;AAAA;AAAA;AAAA,IAIA,WAAW,YAAY;AACrB,MAAA,MAAM,EAAE,IAAA,EAAK,GAAI,MAAM,cAAA,CAAe;AAAA,QACpC,MAAA;AAAA,QACA,IAAA,EAAM,EAAE,EAAA,EAAI,OAAA;AAAQ,OACrB,CAAA;AACD,MAAA,OAAO,eAAA,CAAgB,KAAK,KAAK,CAAA;AAAA,IACnC,CAAA;AAAA;AAAA,IAGA,uBAAA,EAAyB,OAAO,KAAA,EAAO,EAAE,OAAM,KAAM;AACnD,MAAA,MAAM,aAAA,CAAc;AAAA,QAClB,MAAA;AAAA,QACA,IAAA,EAAM,EAAE,EAAA,EAAI,OAAA,EAAQ;AAAA,QACpB,IAAA,EAAM,EAAE,KAAA,EAAO,iBAAA,CAAkB,KAAK,CAAA;AAAE,OACzC,CAAA;AACD,MAAA,OAAO,EAAC;AAAA,IACV,CAAA;AAAA;AAAA,IAGA,sBAAsB,YAAY;AAChC,MAAA,MAAM,UAAA,CAAW,EAAE,MAAA,EAAQ,IAAA,EAAM,EAAE,EAAA,EAAI,OAAA,IAAW,CAAA;AAAA,IACpD;AAAA,GACD,CAAA;AAID,EAAA,IAAA,CAAK,GAAG,UAAA,EAAY,CAAC,YAAY,UAAA,GAAa,OAAA,GAAU,GAAG,CAAC,CAAA;AAE5D,EAAA,MAAM,UAAU,MAAM;AACpB,IAAA,IAAA,CAAK,SAAA,EAAU;AAAA,EACjB,CAAA;AACA,EAAA,MAAA,EAAQ,iBAAiB,OAAA,EAAS,OAAA,EAAS,EAAE,IAAA,EAAM,MAAM,CAAA;AAEzD,EAAA,IAAI;AACF,IAAA,IAAA,CAAK,OAAA,CAAQ,EAAE,IAAA,EAAM,QAAA,CAAS,IAAI,CAAA,EAAG,IAAA,EAAM,IAAA,CAAK,IAAA,EAAM,IAAA,EAAM,IAAA,EAAM,CAAA;AAClE,IAAA,MAAM,MAAA,GAAS,MAAM,IAAA,CAAK,MAAA,EAAO;AAEjC,IAAA,IAAI,QAAQ,OAAA,EAAS;AACnB,MAAA,MAAM,IAAI,YAAA,CAAa,gBAAA,EAAkB,YAAY,CAAA;AAAA,IACvD;AAEA,IAAA,MAAM,MAAA,GAAS,MAAA,EAAQ,MAAA,IAAU,EAAC;AAClC,IAAA,IAAI,MAAA,CAAO,SAAS,CAAA,EAAG;AACrB,MAAA,MAAM,IAAI,YAAY,0BAAA,EAA4B;AAAA,QAChD,KAAA,EAAO,MAAA,CAAO,CAAC,CAAA,EAAG;AAAA,OACnB,CAAA;AAAA,IACH;AAEA,IAAA,UAAA,GAAa,CAAC,CAAA;AAAA,EAChB,CAAA,SAAE;AACA,IAAA,MAAA,EAAQ,mBAAA,CAAoB,SAAS,OAAO,CAAA;AAC5C,IAAA,IAAA,CAAK,OAAA,EAAQ;AAAA,EACf;AACF;AAGA,SAAS,SAAS,IAAA,EAAoB;AACpC,EAAA,OAAO,IAAA,YAAgB,IAAA,GAAO,IAAA,CAAK,IAAA,GAAO,QAAA;AAC5C;;;AClIO,SAAS,mBAAmB,KAAA,EAAmC;AACpE,EAAA,QAAQ,KAAA;AAAO,IACb,KAAK,oBAAA;AACH,MAAA,OAAO,UAAA;AAAA,IACT,KAAK,uBAAA;AACH,MAAA,OAAO,SAAA;AAAA,IACT,KAAK,qBAAA;AACH,MAAA,OAAO,WAAA;AAAA,IACT,KAAK,WAAA;AACH,MAAA,OAAO,UAAA;AAAA;AAEb;;;ACPO,SAAS,0BAA0B,KAAA,EAAqC;AAC7E,EAAA,QAAQ,KAAA;AAAO,IACb,KAAK,SAAA;AACH,MAAA,OAAO,YAAA;AAAA,IACT,KAAK,YAAA;AACH,MAAA,OAAO,aAAA;AAAA,IACT,KAAK,YAAA;AACH,MAAA,OAAO,aAAA;AAAA,IACT,KAAK,eAAA;AACH,MAAA,OAAO,cAAA;AAAA;AAEb;;;ACnBA,SAAS,MAAA,CAAO,GAAW,IAAA,EAAsB;AAC/C,EAAA,OAAO,CAAA,EAAG,CAAC,CAAA,CAAA,EAAI,IAAI,GAAG,CAAA,KAAM,CAAA,GAAI,KAAK,GAAG,CAAA,KAAA,CAAA;AAC1C;AAOO,SAAS,mBAAmB,eAAA,EAAiC;AAClE,EAAA,IAAI,mBAAmB,IAAA,EAAM;AAC3B,IAAA,OAAO,OAAO,IAAA,CAAK,KAAA,CAAM,eAAA,GAAkB,IAAI,GAAG,KAAK,CAAA;AAAA,EACzD;AACA,EAAA,IAAI,mBAAmB,EAAA,EAAI;AACzB,IAAA,OAAO,OAAO,IAAA,CAAK,KAAA,CAAM,eAAA,GAAkB,EAAE,GAAG,MAAM,CAAA;AAAA,EACxD;AACA,EAAA,OAAO,MAAA,CAAO,iBAAiB,QAAQ,CAAA;AACzC;;;ACFO,SAAS,YAAY,KAAA,EAAyC;AACnE,EAAA,MAAM,IAAA,uBAAW,GAAA,EAAwB;AAEzC,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,IAAA,CAAK,GAAA,CAAI,KAAK,EAAA,EAAI,EAAE,GAAG,IAAA,EAAM,QAAA,EAAU,EAAC,EAAG,CAAA;AAAA,EAC7C;AAEA,EAAA,MAAM,QAAsB,EAAC;AAE7B,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA;AAIjC,IAAA,IAAI,aAAa,MAAA,EAAW;AAC1B,MAAA;AAAA,IACF;AAEA,IAAA,MAAM,MAAA,GACJ,KAAK,SAAA,KAAc,IAAA,GAAO,SAAY,IAAA,CAAK,GAAA,CAAI,KAAK,SAAS,CAAA;AAE/D,IAAA,IAAI,WAAW,MAAA,EAAW;AACxB,MAAA,KAAA,CAAM,KAAK,QAAQ,CAAA;AAAA,IACrB,CAAA,MAAO;AACL,MAAA,MAAA,CAAO,QAAA,CAAS,KAAK,QAAQ,CAAA;AAAA,IAC/B;AAAA,EACF;AAEA,EAAA,OAAO,KAAA;AACT;;;AC3BO,IAAM,kBAAA,GAAqB,EAAE,KAAA,EAAO,IAAA,EAAM,OAAO,GAAA;AAKjD,SAAS,cAAc,QAAA,EAA4B;AACxD,EAAA,OAAO,QAAA,KAAa,OAAA,GAChB,kBAAA,CAAmB,KAAA,GACnB,kBAAA,CAAmB,KAAA;AACzB;AA+CO,IAAM,6BAAA,GACX;AAGK,IAAM,iCAAA,GACX;AAGK,IAAM,+BAAA,GACX;AAGK,IAAM,6BAAA,GACX;AAIK,IAAM,wBAAA,GACX;AASK,SAAS,oBAAA,GAA2C;AACzD,EAAA,OAAO;AAAA,IACL,aAAA,EAAe,MAAA;AAAA,IACf,aAAA,EAAe,KAAA;AAAA,IACf,UAAA,EAAY,KAAA;AAAA,IACZ,YAAA,EAAc,KAAA;AAAA,IACd,qBAAA,EAAuB,KAAA;AAAA,IACvB,UAAA,EAAY,KAAA;AAAA,IACZ,eAAA,EAAiB,KAAA;AAAA,IACjB,IAAA,EAAM;AAAA,GACR;AACF;AAKO,SAAS,eACd,OAAA,EACuB;AACvB,EAAA,OAAO,OAAA,CAAQ,GAAA,CAAI,CAAC,KAAA,MAAW,EAAE,OAAO,KAAA,EAAO,kBAAA,CAAmB,KAAK,CAAA,EAAE,CAAE,CAAA;AAC7E;AAIO,SAAS,iBAAA,CACd,OACA,OAAA,EACgC;AAChC,EAAA,OAAO,OAAA,CAAQ,IAAA,CAAK,CAAC,MAAA,KAAW,WAAW,KAAK,CAAA;AAClD;AAGA,SAAS,SAAA,CAAU,OAA2B,IAAA,EAAgC;AAC5E,EAAA,OAAO,KAAA,CAAM,qBAAA,IAAyB,KAAA,CAAM,IAAI,CAAA;AAClD;AAOO,SAAS,sBAAA,CACd,SACA,KAAA,EACsB;AACtB,EAAA,OAAO,SAAA,CAAU,KAAA,EAAO,iBAAiB,CAAA,GACrC,OAAA,CAAQ,MAAA,CAAO,CAAC,MAAA,KAAW,MAAA,KAAW,WAAW,CAAA,GACjD,CAAC,GAAG,OAAO,CAAA;AACjB;AAIO,SAAS,mBAAmB,KAAA,EAAoC;AACrE,EAAA,OAAO,MAAM,aAAA,KAAkB,MAAA;AACjC;AAKO,SAAS,+BACd,KAAA,EACS;AACT,EAAA,OACE,MAAM,qBAAA,IAAyB,CAAC,KAAA,CAAM,UAAA,IAAc,CAAC,KAAA,CAAM,eAAA;AAE/D;AAGO,SAAS,uBAAuB,KAAA,EAAoC;AACzE,EAAA,OAAO,SAAA,CAAU,OAAO,iBAAiB,CAAA;AAC3C;AAOO,SAAS,sBACd,KAAA,EACe;AACf,EAAA,IAAI,SAAA,CAAU,KAAA,EAAO,iBAAiB,CAAA,EAAG;AACvC,IAAA,OAAO,wDAAA;AAAA,EACT;AACA,EAAA,IAAI,SAAA,CAAU,KAAA,EAAO,YAAY,CAAA,EAAG;AAClC,IAAA,OAAO,2DAAA;AAAA,EACT;AACA,EAAA,OAAO,IAAA;AACT;AAIO,SAAS,uBAAA,CACd,OACA,EAAA,EACoB;AACpB,EAAA,OAAO,KACH,EAAE,GAAG,KAAA,EAAO,qBAAA,EAAuB,MAAK,GACxC;AAAA,IACE,GAAG,KAAA;AAAA,IACH,qBAAA,EAAuB,KAAA;AAAA,IACvB,UAAA,EAAY,KAAA;AAAA,IACZ,eAAA,EAAiB;AAAA,GACnB;AACN;AAOO,SAAS,YAAA,CACd,KAAA,EACA,IAAA,EACA,EAAA,EACoB;AACpB,EAAA,MAAM,YAAA,GACJ,IAAA,KAAS,iBAAA,IAAqB,EAAA,IAAM,MAAM,aAAA,KAAkB,WAAA;AAC9D,EAAA,OAAO;AAAA,IACL,GAAG,KAAA;AAAA,IACH,CAAC,IAAI,GAAG,EAAA;AAAA,IACR,GAAI,YAAA,GAAe,EAAE,aAAA,EAAe,MAAA,KAAc;AAAC,GACrD;AACF;AAKO,SAAS,sBAAA,CACd,MACA,GAAA,EACS;AACT,EAAA,OAAO,CAAC,IAAA,CAAK,WAAA,CAAY,GAAG,CAAA;AAC9B;AAEA,IAAM,kBAAA,GAA2D;AAAA,EAC/D,OAAA,EAAS,SAAA;AAAA,EACT,IAAA,EAAM,MAAA;AAAA,EACN,MAAA,EAAQ;AACV,CAAA;AAMA,IAAM,kBAAA,GAAsD;AAAA,EAC1D,SAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAA;AACA,IAAM,kBAAA,GAAsD,CAAC,SAAS,CAAA;AAEtE,IAAM,qBAAA,GAAwB;AAAA,EAC5B,OAAA,EAAS,eAAA;AAAA,EACT,IAAA,EAAM,YAAA;AAAA,EACN,MAAA,EAAQ;AACV,CAAA;AAIO,SAAS,eAAA,CACd,MACA,OAAA,EACwB;AACxB,EAAA,MAAM,KAAA,GAAQ,UAAU,kBAAA,GAAqB,kBAAA;AAC7C,EAAA,OAAO,KAAA,CAAM,GAAA,CAAI,CAAC,GAAA,MAAS;AAAA,IACzB,GAAA;AAAA,IACA,KAAA,EAAO,mBAAmB,GAAG,CAAA;AAAA,IAC7B,QAAA,EAAU,sBAAA,CAAuB,IAAA,EAAM,GAAG;AAAA,GAC5C,CAAE,CAAA;AACJ;AAGO,SAAS,oBACd,GAAA,EAC0B;AAC1B,EAAA,OAAO,sBAAsB,GAAG,CAAA;AAClC;AAGO,SAAS,iBAAA,CACd,KAAA,EACA,GAAA,EACA,EAAA,EACoB;AACpB,EAAA,OAAO,EAAE,GAAG,KAAA,EAAO,CAAC,sBAAsB,GAAG,CAAC,GAAG,EAAA,EAAG;AACtD;AAUO,SAAS,cAAA,CACd,OACA,OAAA,EACoC;AACpC,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,eAAe,KAAA,CAAM,aAAA;AAAA,IACrB,eAAA,EAAiB,CAAC,KAAA,CAAM,aAAA;AAAA,IACxB,oBAAA,EAAsB,SAAA,CAAU,KAAA,EAAO,YAAY,CAAA;AAAA,IACnD,oBAAA,EAAsB,SAAA,CAAU,KAAA,EAAO,iBAAiB;AAAA,GAC1D;AAEA,EAAA,OAAO,OAAA,GACH;AAAA,IACE,GAAG,IAAA;AAAA,IACH,YAAA,EAAc,CAAC,KAAA,CAAM,UAAA;AAAA,IACrB,cAAA,EAAgB,CAAC,KAAA,CAAM,YAAA;AAAA,IACvB,SAAS,KAAA,CAAM;AAAA,GACjB,GACA,IAAA;AACN;AASO,SAAS,mBAAmB,KAAA,EAAoC;AAIrE,EAAA,MAAM,iBACJ,SAAA,CAAU,KAAA,EAAO,iBAAiB,CAAA,IAAK,MAAM,aAAA,KAAkB,WAAA;AACjE,EAAA,OACE,CAAC,mBAAmB,KAAK,CAAA,IACzB,CAAC,8BAAA,CAA+B,KAAK,KACrC,CAAC,cAAA;AAEL;;;ACrQO,IAAM,YAAA,GAAN,cAA2B,KAAA,CAAM;AAAA,EACtC,YAAY,OAAA,EAAiB;AAC3B,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AAAA,EACd;AACF;AA0BA,IAAM,QAAA,GAAiC;AAAA,EACrC,cAAc,CAAC,EAAE,UAAU,YAAA,EAAc,IAAA,EAAM,OAAM,MAAO;AAAA,IAC1D,QAAA,EAAU,GAAA;AAAA,IACV,aAAA,EAAe,YAAA;AAAA,IACf,IAAA;AAAA,IACA,KAAA,EAAO,CAAC,GAAG,KAAK,CAAA;AAAA,IAChB;AAAA,GACF;AACF,CAAA;AAEA,IAAM,eAAA,GAA+C;AAAA,EACnD,cAAc,CAAC,EAAE,UAAU,YAAA,EAAc,IAAA,EAAM,OAAM,MAAO;AAAA,IAC1D,QAAA,EAAU,UAAA;AAAA,IACV,aAAA,EAAe,YAAA;AAAA,IACf,IAAA;AAAA,IACA,KAAA,EAAO,CAAC,GAAG,KAAK,CAAA;AAAA,IAChB;AAAA,GACF;AACF,CAAA;AAEA,IAAM,eAAA,GAAoD;AAAA,EACxD,cAAc,CAAC,EAAE,UAAU,YAAA,EAAc,IAAA,EAAM,OAAM,MAAO;AAAA,IAC1D,QAAA,EAAU,eAAA;AAAA,IACV,aAAA,EAAe,YAAA;AAAA,IACf,IAAA;AAAA,IACA,KAAA,EAAO,CAAC,GAAG,KAAK,CAAA;AAAA,IAChB;AAAA,GACF;AACF,CAAA;AAEA,IAAM,gBAAA,GAAiD;AAAA,EACrD,cAAc,CAAC,EAAE,UAAU,YAAA,EAAc,IAAA,EAAM,OAAM,MAAO;AAAA,IAC1D,QAAA,EAAU,WAAA;AAAA,IACV,aAAA,EAAe,YAAA;AAAA,IACf,IAAA;AAAA,IACA,KAAA,EAAO,CAAC,GAAG,KAAK,CAAA;AAAA,IAChB;AAAA,GACF;AACF,CAAA;AAEA,IAAM,aAAA,GAA2C;AAAA,EAC/C,cAAc,CAAC,EAAE,UAAU,YAAA,EAAc,IAAA,EAAM,OAAM,MAAO;AAAA,IAC1D,QAAA,EAAU,QAAA;AAAA,IACV,aAAA,EAAe,YAAA;AAAA,IACf,IAAA;AAAA,IACA,KAAA,EAAO,CAAC,GAAG,KAAK,CAAA;AAAA,IAChB;AAAA,GACF;AACF,CAAA;AAMA,IAAM,iBAAA,GAAiE;AAAA,EACrE,CAAA,EAAG,QAAA;AAAA,EACH,QAAA,EAAU,eAAA;AAAA,EACV,aAAA,EAAe,eAAA;AAAA,EACf,SAAA,EAAW,gBAAA;AAAA,EACX,MAAA,EAAQ;AACV,CAAA;AAGO,SAAS,eAAe,KAAA,EAAsC;AACnE,EAAA,OAAO,MAAA,CAAO,SAAA,CAAU,cAAA,CAAe,IAAA,CAAK,mBAAmB,KAAK,CAAA;AACtE;AAIO,IAAM,iBACX,MAAA,CAAO,IAAA,CAAK,iBAAiB,CAAA,CAAE,OAAO,cAAc;AAItD,SAAS,mBAAA,CACP,QAAA,EACA,YAAA,EACA,WAAA,EACQ;AACR,EAAA,IAAI,cAAc,OAAO,YAAA;AACzB,EAAA,MAAM,QAAQ,WAAA,CAAY,IAAA,CAAK,CAAC,UAAA,KAAe,UAAA,CAAW,aAAa,QAAQ,CAAA;AAC/E,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,IAAI,YAAA;AAAA,MACR,sBAAsB,QAAQ,CAAA,4DAAA;AAAA,KAChC;AAAA,EACF;AACA,EAAA,OAAO,KAAA,CAAM,EAAA;AACf;AAIA,SAAS,YAAA,CACP,OAAA,EACA,QAAA,EACA,MAAA,EACA,SACA,WAAA,EACe;AACf,EAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,KAAA,IAAS,OAAA,CAAQ,SAAS,EAAC;AAChD,EAAA,OAAO,QAAQ,YAAA,CAAa;AAAA,IAC1B,UAAU,MAAA,CAAO,QAAA;AAAA,IACjB,YAAA,EAAc,mBAAA,CAAoB,QAAA,EAAU,MAAA,CAAO,cAAc,WAAW,CAAA;AAAA,IAC5E,IAAA,EAAM,MAAA,CAAO,IAAA,IAAQ,OAAA,CAAQ,IAAA;AAAA,IAC7B,KAAA,EAAO,MAAM,GAAA,CAAI,CAAC,UAAU,EAAE,QAAA,EAAU,IAAA,CAAK,EAAA,EAAG,CAAE;AAAA,GACnD,CAAA;AACH;AASA,SAAS,cAAA,CACP,QAAA,EACA,QAAA,EACA,OAAA,EACA,WAAA,EAC2B;AAC3B,EAAA,MAAM,MAAA,GAAS,SAAS,QAAQ,CAAA;AAChC,EAAA,IAAI,CAAC,QAAQ,OAAO,MAAA;AACpB,EAAA,OAAO,YAAA;AAAA,IACL,kBAAkB,QAAQ,CAAA;AAAA,IAC1B,QAAA;AAAA,IACA,MAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACF;AACF;AAEA,SAAS,aAAA,CACP,OAAA,EACA,QAAA,EACA,WAAA,EACoB;AAIpB,EAAA,MAAM,QAAA,GAAW,cAAA,CAAe,OAAA,CAAQ,CAAC,QAAA,KAAa;AACpD,IAAA,MAAM,OAAA,GAAU,cAAA,CAAe,QAAA,EAAU,QAAA,EAAU,SAAS,WAAW,CAAA;AACvE,IAAA,OAAO,OAAA,GAAU,CAAC,OAAO,CAAA,GAAI,EAAC;AAAA,EAChC,CAAC,CAAA;AAED,EAAA,IAAI,QAAA,CAAS,WAAW,CAAA,EAAG;AAKzB,IAAA,MAAM,IAAI,aAAa,mCAAmC,CAAA;AAAA,EAC5D;AACA,EAAA,OAAO,QAAA;AACT;AAWO,SAAS,eAAA,CACd,OACA,WAAA,EACiB;AACjB,EAAA,OAAO;AAAA,IACL,YAAY,KAAA,CAAM,SAAA;AAAA,IAClB,SAAS,KAAA,CAAM,OAAA;AAAA,IACf,aAAa,KAAA,CAAM,UAAA;AAAA,IACnB,aAAa,KAAA,CAAM,UAAA;AAAA,IACnB,UAAU,KAAA,CAAM,QAAA;AAAA,IAChB,MAAM,KAAA,CAAM,IAAA,GAAO,CAAC,GAAG,KAAA,CAAM,IAAI,CAAA,GAAI,MAAA;AAAA,IACrC,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,SAAS,KAAA,CAAM,MAAA;AAAA,IACf,QAAA,EAAU,cAAc,KAAA,CAAM,OAAA,IAAW,EAAC,EAAG,KAAA,CAAM,UAAU,WAAW;AAAA,GAC1E;AACF;AAGA,IAAM,qBAAA,GAAwB;AAAA,EAC5B,SAAA;AAAA,EACA,YAAA;AAAA,EACA,YAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AAAA,EACA;AACF,CAAA;AAMO,SAAS,eAAA,CACd,KAAA,EACA,WAAA,GAAwC,EAAC,EACxB;AACjB,EAAA,MAAM,eAAA,GAAkB,sBAAsB,IAAA,CAAK,CAAC,QAAQ,KAAA,CAAM,GAAG,MAAM,MAAS,CAAA;AACpF,EAAA,IAAI,CAAC,KAAA,CAAM,QAAA,IAAY,CAAC,eAAA,EAAiB;AACvC,IAAA,MAAM,IAAI,YAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,SAAS,KAAA,CAAM,OAAA;AAAA,IACf,aAAa,KAAA,CAAM,UAAA;AAAA,IACnB,aAAa,KAAA,CAAM,UAAA;AAAA,IACnB,UAAU,KAAA,CAAM,QAAA;AAAA,IAChB,MAAM,KAAA,CAAM,IAAA,GAAO,CAAC,GAAG,KAAA,CAAM,IAAI,CAAA,GAAI,MAAA;AAAA,IACrC,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,SAAS,KAAA,CAAM,MAAA;AAAA,IACf,QAAA,EAAU,KAAA,CAAM,QAAA,GACZ,aAAA,CAAc,KAAA,CAAM,OAAA,IAAW,EAAC,EAAG,KAAA,CAAM,QAAA,EAAU,WAAW,CAAA,GAC9D;AAAA,GACN;AACF","file":"index.js","sourcesContent":["import type {\n ConnectionsConnectData,\n ConnectionsConnectResponse,\n ConnectionsGetResponse,\n ConnectionsListAccountsResponse,\n ConnectionsListByProfileData,\n ConnectionsListByProfileResponse,\n ConnectionsSelectData,\n MediaCreateData,\n MediaCreateResponse,\n MediaGetResponse,\n MediaListData,\n MediaListResponse,\n MediaUpdateData,\n PostsCreateData,\n PostsGetResponse,\n PostsListData,\n PostsListResponse,\n PostsUpdateData,\n PostsValidateData,\n PostsValidateResponse,\n ProfilesCreateData,\n ProfilesGetResponse,\n ProfilesListData,\n ProfilesListResponse,\n ProfilesUpdateData,\n TiktokCreatorInfoResponse,\n} from './client/types.gen';\n\n/**\n * Resource and input shapes, DERIVED from the generated Hey API operation types\n * (`*Data` = request `{ body, path, query }`, `*Response` = success body). Never\n * hand-declared — there is exactly one definition of each, sourced from the\n * contract, so `pnpm generate` keeps them in lockstep with the live API.\n */\n\n/** A profile — a client/brand workspace. */\nexport type Profile = ProfilesGetResponse;\n\n/** A page of profiles (the list envelope). */\nexport type ProfileList = ProfilesListResponse;\n\n/** Query parameters for listing profiles (filters + pagination). */\nexport type ListProfilesQuery = NonNullable<ProfilesListData['query']>;\n\n/** Request body to create a profile. */\nexport type CreateProfileInput = NonNullable<ProfilesCreateData['body']>;\n\n/** Request body to update a profile (send only what changes). */\nexport type UpdateProfileInput = NonNullable<ProfilesUpdateData['body']>;\n\n/** A connected social/ads account on a profile. */\nexport type Connection = ConnectionsGetResponse;\n\n/** A page of a profile's connections. */\nexport type ConnectionList = ConnectionsListByProfileResponse;\n\n/** Query filters for listing a profile's connections (kind / status / pagination). */\nexport type ListConnectionsQuery = NonNullable<\n ConnectionsListByProfileData['query']\n>;\n\n/** A connection's kind — `posting` (social) or `ads`. From the contract. */\nexport type ConnectionKind = NonNullable<ListConnectionsQuery['kind']>;\n\n/** A connection's lifecycle status — `pending` / `active` / `needs_reauth`. */\nexport type ConnectionStatus = NonNullable<ListConnectionsQuery['status']>;\n\n/** The session returned when starting a connect (OAuth) flow. */\nexport type ConnectSession = ConnectionsConnectResponse;\n\n/** A platform a connection can start an OAuth connect flow for. */\nexport type ConnectablePlatform =\n NonNullable<ConnectionsConnectData['body']>['platform'];\n\n/** Accounts discoverable on a pending connection, for the selection step. */\nexport type DiscoverableAccountList = ConnectionsListAccountsResponse;\n\n/**\n * A TikTok creator's live publish options for a connection — the data TikTok's\n * Content Posting policy REQUIRES the publishing UI to render: `creator`\n * (nickname + avatar), the `privacy_options` to offer (with NO default), the\n * positive `interaction` (allowed) flags for Comment/Duet/Stitch toggles, and\n * `max_video_duration_sec`. Derived from the contract, never hand-declared.\n */\nexport type TikTokCreatorInfo = TiktokCreatorInfoResponse;\n\n/**\n * One TikTok privacy level — a member of the closed audience union\n * (`PUBLIC_TO_EVERYONE` / `MUTUAL_FOLLOW_FRIENDS` / `FOLLOWER_OF_CREATOR` /\n * `SELF_ONLY`). The per-account ALLOWED subset is `TikTokCreatorInfo.privacy_options`;\n * this is one value from it (what you bind a dropdown option to). Derived from\n * the contract, never hand-declared; render it with {@link tiktokPrivacyLabel}.\n */\nexport type TikTokPrivacyLevel = TikTokCreatorInfo['privacy_options'][number];\n\n/** Request body to select an account on a pending connection. */\nexport type SelectAccountInput = NonNullable<ConnectionsSelectData['body']>;\n\n/** The create/update post body, derived from the contract. */\ntype CreatePostBody = NonNullable<PostsCreateData['body']>;\n\n/**\n * One channel's variant on WRITE — the per-platform discriminated-union member\n * with fully typed native `settings`. Preferred over the read resource's variant\n * for anything that inspects settings (on read, `settings` is an opaque record);\n * it is also the compose-time shape, which is exactly what a live preview renders.\n */\nexport type PostVariantInput = CreatePostBody['variants'][number];\n\n/** The X (Twitter) member of the write variant union — typed native settings\n * (`quote_tweet_id`, `reply`, `poll`, …). Narrowed from the contract, never\n * hand-declared. */\nexport type XPostVariant = Extract<PostVariantInput, { platform: 'x' }>;\n\n/** The LinkedIn member of the write variant union — typed native settings\n * (`visibility`, `article`, `poll`, `document`, `mentions`). The `content_kind`\n * shape is server-DERIVED and read-only (see {@link PostVariant}). Narrowed from\n * the contract, never hand-declared. */\nexport type LinkedInPostVariant = Extract<\n PostVariantInput,\n { platform: 'linkedin' }\n>;\n\n/** The TikTok member of the write variant union — typed native settings\n * (`privacy_level`, `disable_comment`/`disable_duet`/`disable_stitch`,\n * `brand_content_toggle`/`brand_organic_toggle`, `is_aigc`, photo/video cover).\n * Narrowed from the contract, never hand-declared — it is the compose-time shape a\n * live TikTok preview renders. */\nexport type TikTokPostVariant = Extract<\n PostVariantInput,\n { platform: 'tiktok' }\n>;\n\n/** The Instagram member of the write variant union — typed native settings\n * (`collaborators`, `user_tags`, `location_id`, and the reel-only\n * `share_to_feed`/`audio_name`/`cover_url`/`thumb_offset`). The `media_type` shape\n * is server-DERIVED and read-only (see {@link PostVariant}). Narrowed from the\n * contract, never hand-declared — the compose-time shape an IG preview renders. */\nexport type InstagramPostVariant = Extract<\n PostVariantInput,\n { platform: 'instagram' }\n>;\n\n/** A post — its variants, schedule, and derived status (the read resource). */\nexport type Post = PostsGetResponse;\n\n/**\n * One per-platform variant on a RETURNED post (the read resource): its own\n * `status`, a typed `error` ({@link PostVariantError}) when it failed, and a\n * `result` (platform id + permalink) when it published. Derived from the post\n * resource, never hand-declared. (For inspecting compose-time native `settings`,\n * prefer {@link PostVariantInput}; on read `settings` is opaque.)\n */\nexport type PostVariant = Post['variants'][number];\n\n/**\n * A variant's typed publish failure — `{ code, message }`. The `code` is the\n * adapter's own minted reason (`x_access_not_permitted`, `linkedin_auth_expired`,\n * …); it stays a `string` because adapter codes are minted outside the API's\n * closed registry. Derived from the variant, never hand-declared.\n */\nexport type PostVariantError = NonNullable<PostVariant['error']>;\n\n/**\n * Did the post fully publish? `true` ONLY when the rollup `status` is\n * `'published'` — `partially_published` and `failed` are NOT success. The single\n * pure predicate every surface (SDK, React hook, composer) shares, so \"success\"\n * has exactly one definition.\n */\nexport const isPublished = (post: Post): boolean =>\n post.status === 'published';\n\n/**\n * The variants that FAILED to publish, each carrying its typed {@link\n * PostVariantError}. Empty when none failed. The pure helper behind\n * `useCreatePost().failedVariants` — usable directly from a non-React caller so\n * the \"which platforms failed, and why\" projection has one definition.\n */\nexport const failedVariants = (post: Post): PostVariant[] =>\n post.variants.filter((variant) => variant.status === 'failed');\n\n/** A page of posts (the calendar/queue list). */\nexport type PostList = PostsListResponse;\n\n/** Query parameters for listing posts (filters + pagination). */\nexport type ListPostsQuery = NonNullable<PostsListData['query']>;\n\n/**\n * A post's derived rollup status — the values accepted by the list `status`\n * filter (draft / scheduled / publishing / partially_published / published /\n * failed). Sourced from the generated query contract, never hand-listed.\n */\nexport type PostStatus = NonNullable<ListPostsQuery['status']>[number];\n\n/** The full create-post request body — what `buildCreatePost` produces. */\nexport type CreatePostInput = CreatePostBody;\n\n/** The update-post request body — what `buildUpdatePost` produces. */\nexport type UpdatePostInput = NonNullable<PostsUpdateData['body']>;\n\n/**\n * The validate-post request body — the composition-only subset of the create\n * body (`profile_id` + `variants`). `buildCreatePost` produces a superset, so\n * its output is structurally assignable here. Derived from the contract.\n */\nexport type ValidatePostInput = NonNullable<PostsValidateData['body']>;\n\n/**\n * The server's publish-readiness verdict for a composed post — `{ object,\n * publishable, issues }`. The SERVER is the sole authority on validity; the SDK\n * only builds a best-effort payload and relays this result. Derived from the\n * contract, never hand-declared.\n */\nexport type PostValidation = PostsValidateResponse;\n\n/**\n * One typed, per-variant problem in a `PostValidation` — a closed-registry\n * `code`, a human `message`, the `variant_index` + `path` it applies to, and the\n * optional `hint`/`allowed`/`got` the rule produced. Narrowed from the contract.\n */\nexport type ValidationIssue = PostValidation['issues'][number];\n\n/** A posting platform — the variant discriminator (x / linkedin / …). */\nexport type PostPlatform = PostVariantInput['platform'];\n\n/**\n * The post shape (`text` / `single_image` / `multi_image` / `video` / `reel` /\n * `carousel`) — DERIVED server-side from the attached media, never sent on write.\n * It surfaces on the READ variant (`PostVariant.post_type`, null while\n * undetermined); this strips that null to the concrete union. Derived from the\n * contract, never hand-listed.\n */\nexport type PostType = NonNullable<PostVariant['post_type']>;\n\n/** The native `settings` shape for a given platform (typed per channel). */\nexport type SettingsFor<P extends PostPlatform> = Extract<\n PostVariantInput,\n { platform: P }\n>['settings'];\n\n/** Publish mode: `now` | `schedule` | `draft`. */\nexport type PublishMode = NonNullable<CreatePostBody['publish']>;\n\n/** Customer metadata on a post (the shared scalar map). */\nexport type PostMetadata = NonNullable<CreatePostBody['metadata']>;\n\n/** A media asset (image/video/gif/document) with its per-platform renditions. */\nexport type MediaResource = MediaGetResponse;\n\n/**\n * A media asset's kind (image / video / gif / document), from the contract. The\n * resource's `kind` is nullable only in the transient pre-detection `uploading`\n * window; this is the concrete family, so it strips that null — what you pass to\n * create/upload and switch over on a settled asset.\n */\nexport type MediaKind = NonNullable<MediaResource['kind']>;\n\n/** A media render target — a post platform or `google_ads`. */\nexport type MediaTarget = NonNullable<\n NonNullable<MediaCreateData['body']>['targets']\n>[number];\n\n/** The create-media response: the resource plus a multipart upload session. */\nexport type CreateMediaResult = MediaCreateResponse;\n\n/** Request body to create a media asset. */\nexport type CreateMediaInput = NonNullable<MediaCreateData['body']>;\n\n/** Request body to update a media asset (alt text / metadata / extend targets). */\nexport type UpdateMediaInput = NonNullable<MediaUpdateData['body']>;\n\n/** A page of media assets (the list envelope). */\nexport type MediaList = MediaListResponse;\n\n/** Query parameters for listing media (filters + pagination). */\nexport type ListMediaQuery = NonNullable<MediaListData['query']>;\n\n/**\n * A media asset's lifecycle state — the values the list `status` filter accepts\n * (uploading / processing / ready / failed). From the contract, never hand-listed.\n */\nexport type MediaStatus = NonNullable<ListMediaQuery['status']>;\n\n/**\n * Customer metadata — a flat scalar map (`string | number | boolean`; ≤50 keys,\n * keys ≤40 chars, ≤500-char strings, 16 KiB serialized). The contract uses this\n * SAME shape on every stored resource (profiles / posts / media); this alias is\n * derived from the media write body as a stable representative, so it's the type\n * to annotate metadata you send — never `Record<string, unknown>`. (The three\n * resources share one structural shape in the spec; were the API ever to diverge\n * one, prefer that resource's own `*['metadata']` for it.)\n */\nexport type Metadata = NonNullable<CreateMediaInput['metadata']>;\n\n/**\n * A metadata filter for list endpoints — the same scalar map, matched by exact\n * containment (multiple keys ANDed; omit for no filter). Pass the object directly\n * from the SDK/React hooks; the client URL-encodes it for the REST query.\n */\nexport type MetadataFilter = NonNullable<ListMediaQuery['metadata']>;\n","import type { AwsS3Part } from '@uppy/aws-s3';\n\nimport type {\n MediaCompleteData,\n MediaListPartsResponse,\n} from '../client/types.gen';\n\n/**\n * The casing seam between Uppy's `@uppy/aws-s3` callbacks (which speak S3\n * PascalCase — `{ PartNumber, ETag, Size }`) and our endpoints (which speak the\n * API's snake_case — `{ part_number, etag, size }`). Uppy reads the parts we hand\n * it back from `listParts` and hands us the parts to send to `complete`, so a\n * silent mismatch here would break resume/complete with no error. Both maps are\n * pure and tested; the shapes are DERIVED from the generated client + Uppy types,\n * never hand-declared.\n */\n\n/** One part as our `GET /media/{id}/multipart/parts` endpoint returns it. */\ntype ApiListedPart = MediaListPartsResponse['parts'][number];\n\n/** One part as our `POST /media/{id}/multipart/complete` body carries it. */\ntype ApiCompletePart = NonNullable<MediaCompleteData['body']>['parts'][number];\n\n/**\n * Map our snake_case listed parts → Uppy's S3 `AwsS3Part[]` so a resume after a\n * dropped connection skips the parts R2 already holds. A 1:1 field map — the\n * endpoint's `parts` contract guarantees `part_number`/`etag`/`size` on every\n * entry, so there's nothing to drop.\n */\nexport function listedPartsToS3(\n parts: readonly ApiListedPart[],\n): AwsS3Part[] {\n return parts.map((part) => ({\n PartNumber: part.part_number,\n ETag: part.etag,\n Size: part.size,\n }));\n}\n\n/**\n * Map Uppy's S3 `AwsS3Part[]` (handed to `completeMultipartUpload`) → our\n * snake_case `complete` body parts. `AwsS3Part` types `PartNumber`/`ETag` as\n * optional; a part Uppy completes with always has both, but we assert the\n * invariant explicitly rather than cast — a missing field is a hard error, not a\n * silently-dropped part.\n */\nexport function s3PartsToComplete(\n parts: readonly AwsS3Part[],\n): ApiCompletePart[] {\n return parts.map((part) => {\n if (part.PartNumber === undefined || part.ETag === undefined) {\n throw new Error(\n 'Multipart complete received a part without PartNumber/ETag.',\n );\n }\n return { part_number: part.PartNumber, etag: part.ETag };\n });\n}\n","/**\n * A failed direct-to-storage (multipart) upload. `cause` carries the underlying\n * Uppy / network / API error so callers can inspect the real reason; `message` is\n * a short human summary. Thrown by {@link uploadFile} on a hard upload failure\n * (one or more parts could not be uploaded, or `complete` failed).\n */\nexport class UploadError extends Error {\n constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = 'UploadError';\n }\n}\n","import Uppy from '@uppy/core';\nimport AwsS3 from '@uppy/aws-s3';\n\nimport type { PostrunClient } from '../client';\nimport {\n mediaAbort,\n mediaComplete,\n mediaListParts,\n mediaSignPart,\n} from '../client/sdk.gen';\nimport type { MediaCreateResponse } from '../client/types.gen';\nimport { listedPartsToS3, s3PartsToComplete } from './casing';\nimport { UploadError } from './upload-error';\n\n/**\n * The multipart session the create response carries on its `upload` field — the\n * cached `{ upload_id, key, part_size, expires_at }` Uppy's `createMultipartUpload`\n * returns without a second HTTP call. Derived from the contract, never declared.\n */\nexport type MultipartSession = NonNullable<MediaCreateResponse['upload']>;\n\nexport interface UploadFileOptions {\n /** The media id the create call returned — binds the multipart sub-endpoints. */\n mediaId: string;\n /** The cached `upload` session from the create response (`upload_id`/`key`/…). */\n session: MultipartSession;\n /** The typed client the sub-endpoint calls go through (auth + base URL). */\n client: PostrunClient;\n /** Fraction uploaded, 0..1, as bytes flow; called once with 1 on success. */\n onProgress?: (fraction: number) => void;\n /** Abort the in-flight upload (cancels Uppy and aborts the R2 session). */\n signal?: AbortSignal;\n}\n\n/**\n * Upload ONE file's bytes to R2 with a resilient, resumable multipart upload,\n * driving `@uppy/aws-s3` headless (no DOM). Uppy owns the bug-prone mechanics —\n * fixed-size chunking, parallel parts, per-part retry, resume via `listParts`,\n * and progress — wired to OUR signing endpoints (`createMultipartUpload` returns\n * the cached create-response session, so there's no extra round-trip to start).\n *\n * Resolves when the upload is fully assembled on R2 (the object then settles\n * through the same probe/transform pipeline a `source_url` import does). Throws\n * {@link UploadError} on a hard failure, or a `DOMException('AbortError')` when\n * `signal` fires.\n */\nexport async function uploadFile(\n file: Blob,\n options: UploadFileOptions,\n): Promise<void> {\n const { mediaId, session, client, onProgress, signal } = options;\n\n if (signal?.aborted) {\n throw new DOMException('Upload aborted', 'AbortError');\n }\n\n const uppy = new Uppy({ autoProceed: false }).use(AwsS3, {\n // Every file is one resumable multipart upload — no size-threshold split, so\n // the path is uniform and a tiny file is just a 1-part upload (PLAN §2). The\n // literal `true` (not a `() => true`) selects Uppy's always-multipart variant.\n shouldUseMultipart: true,\n // Pin Uppy's chunk size to the server's part size — every part except the\n // last MUST be exactly this (an R2 requirement the session encodes).\n getChunkSize: () => session.part_size,\n\n // Start: hand back the cached session from the create response — NO HTTP.\n createMultipartUpload: () => ({\n uploadId: session.upload_id,\n key: session.key,\n }),\n\n // Sign one part → POST /media/{id}/multipart/sign. The sub-endpoint reads the\n // upload id from the asset row, so the body carries only the part number.\n signPart: async (_file, { partNumber }) => {\n const { data } = await mediaSignPart({\n client,\n path: { id: mediaId },\n body: { part_number: partNumber },\n });\n return { url: data.url };\n },\n\n // Resume: GET /media/{id}/multipart/parts → map snake_case → S3 PascalCase so\n // Uppy skips the parts R2 already holds after a dropped connection.\n listParts: async () => {\n const { data } = await mediaListParts({\n client,\n path: { id: mediaId },\n });\n return listedPartsToS3(data.parts);\n },\n\n // Finish: map Uppy's S3 parts → snake_case → POST /media/{id}/multipart/complete.\n completeMultipartUpload: async (_file, { parts }) => {\n await mediaComplete({\n client,\n path: { id: mediaId },\n body: { parts: s3PartsToComplete(parts) },\n });\n return {};\n },\n\n // Cancel: POST /media/{id}/multipart/abort (idempotent server-side).\n abortMultipartUpload: async () => {\n await mediaAbort({ client, path: { id: mediaId } });\n },\n });\n\n // Uppy's `progress` event is the overall 0–100 across all parts of the queue\n // (one file here) — forward it as a 0..1 fraction.\n uppy.on('progress', (percent) => onProgress?.(percent / 100));\n\n const onAbort = () => {\n uppy.cancelAll();\n };\n signal?.addEventListener('abort', onAbort, { once: true });\n\n try {\n uppy.addFile({ name: fileName(file), type: file.type, data: file });\n const result = await uppy.upload();\n\n if (signal?.aborted) {\n throw new DOMException('Upload aborted', 'AbortError');\n }\n\n const failed = result?.failed ?? [];\n if (failed.length > 0) {\n throw new UploadError('Multipart upload failed.', {\n cause: failed[0]?.error,\n });\n }\n\n onProgress?.(1);\n } finally {\n signal?.removeEventListener('abort', onAbort);\n uppy.destroy();\n }\n}\n\n/** Uppy needs a file name; a bare `Blob` has none, so fall back to a stable one. */\nfunction fileName(file: Blob): string {\n return file instanceof File ? file.name : 'upload';\n}\n","import type { TikTokPrivacyLevel } from './resources';\n\n/**\n * Map a TikTok privacy level to a short, human label for the composer's privacy\n * dropdown — the presentation the API deliberately keeps OFF the wire (the\n * `creator-info` response returns the typed `privacy_options`; the SDK owns how\n * they read).\n *\n * Exhaustive over the closed `TikTokPrivacyLevel` union with no `default` branch,\n * so adding a TikTok privacy level upstream surfaces as a compile error here\n * rather than silently rendering a raw enum string.\n */\nexport function tiktokPrivacyLabel(value: TikTokPrivacyLevel): string {\n switch (value) {\n case 'PUBLIC_TO_EVERYONE':\n return 'Everyone';\n case 'MUTUAL_FOLLOW_FRIENDS':\n return 'Friends';\n case 'FOLLOWER_OF_CREATOR':\n return 'Followers';\n case 'SELF_ONLY':\n return 'Only you';\n }\n}\n","import type { LinkedInPostVariant } from './resources';\n\n/**\n * One LinkedIn poll duration — DERIVED from the contract (the poll `duration`\n * field), never hand-listed, so it can't drift from the API shape.\n */\nexport type LinkedInPollDuration = NonNullable<\n NonNullable<LinkedInPostVariant['settings']>['poll']\n>['duration'];\n\n/**\n * Map a poll duration to the short \"time left\" label LinkedIn shows on the poll\n * card — the presentation the wire keeps enum-only. Exhaustive over the closed\n * union with no `default` branch, so adding a duration upstream surfaces as a\n * compile error here rather than silently rendering a raw enum string.\n */\nexport function linkedinPollDurationLabel(value: LinkedInPollDuration): string {\n switch (value) {\n case 'ONE_DAY':\n return '1 day left';\n case 'THREE_DAYS':\n return '3 days left';\n case 'SEVEN_DAYS':\n return '1 week left';\n case 'FOURTEEN_DAYS':\n return '2 weeks left';\n }\n}\n","import type { XPostVariant } from './resources';\n\n/** An X poll's settings shape — DERIVED from the contract, never re-declared. */\nexport type XPoll = NonNullable<\n NonNullable<XPostVariant['settings']>['poll']\n>;\n\n/** `n unit left`, pluralizing the unit. */\nfunction plural(n: number, unit: string): string {\n return `${n} ${unit}${n === 1 ? '' : 's'} left`;\n}\n\n/**\n * Turn an X poll's `duration_minutes` (5–10080, i.e. 5 min – 7 days) into the\n * \"time left\" label the poll card shows, floored to the coarsest sensible unit\n * (days ≥ 1 day, else hours ≥ 1 hour, else minutes). Pure — pin it in tests.\n */\nexport function xPollDurationLabel(durationMinutes: number): string {\n if (durationMinutes >= 1440) {\n return plural(Math.floor(durationMinutes / 1440), 'day');\n }\n if (durationMinutes >= 60) {\n return plural(Math.floor(durationMinutes / 60), 'hour');\n }\n return plural(durationMinutes, 'minute');\n}\n","import type { GoogleAdTreeNode } from './client/types.gen';\n\n/**\n * The Google Ads tree, assembled. `GET /v1/google/{conn}/ads/tree` returns FLAT,\n * native `GoogleAdTreeNode`s with Google's authoritative per-level metrics (no\n * client roll-up) keyed by `parent_id`; turning that into a nested tree is generic,\n * so it lives here rather than in every consumer.\n */\n\n/** A flat tree node with its children attached. Extends the generated node — never\n * a redeclared shape, so it can't drift from the API. */\nexport interface AdTreeNode extends GoogleAdTreeNode {\n children: AdTreeNode[];\n}\n\n/**\n * Group flat tree nodes into a nested tree by `parent_id`. A node whose parent is\n * NOT in the set is a root — so a campaign-level page (campaigns have\n * `parent_id: null`) and a per-campaign subtree page (ad groups whose campaign is\n * absent) each group correctly on their own, and concatenating a campaign page with\n * an expanded campaign's subtree page nests the subtree under that campaign. Input\n * order is preserved within every level. Pure — the input is not mutated.\n */\nexport function groupAdTree(nodes: GoogleAdTreeNode[]): AdTreeNode[] {\n const byId = new Map<string, AdTreeNode>();\n\n for (const node of nodes) {\n byId.set(node.id, { ...node, children: [] });\n }\n\n const roots: AdTreeNode[] = [];\n\n for (const node of nodes) {\n const treeNode = byId.get(node.id);\n\n // Unreachable — every id was just inserted — but keeps the access total\n // without a non-null assertion.\n if (treeNode === undefined) {\n continue;\n }\n\n const parent =\n node.parent_id === null ? undefined : byId.get(node.parent_id);\n\n if (parent === undefined) {\n roots.push(treeNode);\n } else {\n parent.children.push(treeNode);\n }\n }\n\n return roots;\n}\n","import type {\n PostType,\n SettingsFor,\n TikTokCreatorInfo,\n} from './resources';\nimport { type TikTokPrivacyLevel } from './resources';\nimport { tiktokPrivacyLabel } from './tiktok';\n\n/**\n * TikTok's mandatory publish-options model — the framework-agnostic glue between\n * the live `creator-info` payload and a TikTok post's settings. This is the\n * SINGLE source for the no-default / creator-gated / commercial-disclosure rules\n * TikTok's Required UX mandates; the React panel and the dashboard composer both\n * consume it (never re-implement it).\n *\n * The creator-info shape, the privacy union, and the privacy label come from the\n * contract (`./resources`, `./tiktok`) — never re-declared. This module owns the\n * controlled FORM value, the selection rules, and the mapping into the SDK's\n * per-platform `tiktok` settings.\n */\n\n/** TikTok caption caps, mirrored from the publish contract (`BODY_MAX.tiktok_*`).\n * A video post caps the title (2200), a photo post the description (4000). Lives\n * here (not in React) so non-React consumers (MCP, API client, scripts) can use\n * it. The server enforces these; the editor surfaces them. */\nexport const TIKTOK_CAPTION_MAX = { video: 2200, photo: 4000 } as const;\n\n/** The caption cap for a given post_type (video → title cap, photo → description).\n * `post_type` is server-derived from the media (read-only) — the composer passes\n * the shape it knows it's building (a video post vs a photo post). */\nexport function captionMaxFor(postType: PostType): number {\n return postType === 'video'\n ? TIKTOK_CAPTION_MAX.video\n : TIKTOK_CAPTION_MAX.photo;\n}\n\n/** The creator-gated interactions TikTok requires us to surface — DERIVED from\n * the creator-info contract (`interaction` keys), never hand-listed, so it can\n * never drift from the API shape. */\nexport type TikTokInteractionKey = keyof TikTokCreatorInfo['interaction'];\n\n/** Which commercial-disclosure brand option a control maps to. */\nexport type TikTokBrandKind = 'your_brand' | 'branded_content';\n\n/**\n * The controlled value a TikTok composer/preview owns. `privacy_level` is\n * optional because TikTok requires the user to pick — there is NO default (an\n * absent value is the \"must choose\" state the publish gate keys off). Interaction\n * flags are POSITIVE (\"allow\"); `tiktokSettings` inverts them to the API's\n * `disable_*` shape.\n */\nexport interface TikTokOptionsValue {\n privacy_level?: TikTokPrivacyLevel;\n allow_comment: boolean;\n allow_duet: boolean;\n allow_stitch: boolean;\n /** Commercial Content Disclosure master toggle (off by default). When on, at\n * least one of `your_brand` / `branded_content` must be chosen. */\n commercial_disclosure: boolean;\n your_brand: boolean;\n branded_content: boolean;\n /** AI-generated content disclosure → the API's `is_aigc`. Video-only, off by\n * default. */\n aigc: boolean;\n}\n\nexport interface TikTokPrivacyChoice {\n level: TikTokPrivacyLevel;\n label: string;\n}\n\nexport interface TikTokInteractionRow {\n key: TikTokInteractionKey;\n label: string;\n /** Disabled (and forced off) when the creator forbids it — TikTok mandate. */\n disabled: boolean;\n}\n\n/* --------------------------- required wording --------------------------- */\n\n/** TikTok's Music Usage Confirmation — the legal URL the declaration links to. */\nexport const TIKTOK_MUSIC_CONFIRMATION_URL =\n 'https://www.tiktok.com/legal/page/global/music-usage-confirmation/en';\n\n/** TikTok's Branded Content Policy — linked when a paid partnership is disclosed. */\nexport const TIKTOK_BRANDED_CONTENT_POLICY_URL =\n 'https://www.tiktok.com/legal/page/global/bc-policy/en';\n\n/** TikTok's exact required prompt when disclosure is on but nothing is chosen. */\nexport const TIKTOK_DISCLOSURE_REQUIRED_HINT =\n 'You need to indicate if your content promotes yourself, a third party, or both.';\n\n/** TikTok's required prompt when no audience has been picked yet. */\nexport const TIKTOK_AUDIENCE_REQUIRED_HINT =\n 'Choose who can view this video before publishing.';\n\n/** TikTok Required UX: clients must notify users a published post may take a few\n * minutes to process. No exact string is mandated; this conveys it. */\nexport const TIKTOK_PROCESSING_NOTICE =\n 'After you publish, it may take a few minutes for your post to process and appear on your TikTok profile.';\n\n/* ------------------------------ defaults ------------------------------ */\n\n/**\n * The initial value for a TikTok post: nothing pre-selected. TikTok's Required UX\n * mandates the user actively choose — no default privacy, every interaction OFF,\n * no disclosure. The panel still greys the interactions the creator forbids.\n */\nexport function defaultTikTokOptions(): TikTokOptionsValue {\n return {\n privacy_level: undefined,\n allow_comment: false,\n allow_duet: false,\n allow_stitch: false,\n commercial_disclosure: false,\n your_brand: false,\n branded_content: false,\n aigc: false,\n };\n}\n\n/* ------------------------------ audience ------------------------------ */\n\n/** The audience choices for the dropdown — one per level the creator allows. */\nexport function privacyChoices(\n options: readonly TikTokPrivacyLevel[],\n): TikTokPrivacyChoice[] {\n return options.map((level) => ({ level, label: tiktokPrivacyLabel(level) }));\n}\n\n/** Narrow a raw dropdown value to a `TikTokPrivacyLevel` against the creator's\n * allowed options — returns `undefined` for anything not offered (no cast). */\nexport function parsePrivacyLevel(\n value: string,\n options: readonly TikTokPrivacyLevel[],\n): TikTokPrivacyLevel | undefined {\n return options.find((option) => option === value);\n}\n\n/** Effective brand flags (only meaningful while disclosure is on). */\nfunction discloses(value: TikTokOptionsValue, kind: TikTokBrandKind): boolean {\n return value.commercial_disclosure && value[kind];\n}\n\n/**\n * The audience levels to offer. Branded content can ONLY be public/friends —\n * TikTok forbids posting it SELF_ONLY (private) — so that option is dropped when\n * branded content is disclosed. Otherwise every creator-allowed level is offered.\n */\nexport function audiencePrivacyOptions(\n options: readonly TikTokPrivacyLevel[],\n value: TikTokOptionsValue,\n): TikTokPrivacyLevel[] {\n return discloses(value, 'branded_content')\n ? options.filter((option) => option !== 'SELF_ONLY')\n : [...options];\n}\n\n/** No audience picked yet — must BLOCK publish (TikTok mandates an explicit\n * choice with no default; the API rejects a missing `privacy_level` too). */\nexport function audienceUnselected(value: TikTokOptionsValue): boolean {\n return value.privacy_level === undefined;\n}\n\n/* --------------------------- commercial disclosure --------------------------- */\n\n/** Disclosure is on but neither brand option is chosen — TikTok requires ≥1. */\nexport function commercialDisclosureIncomplete(\n value: TikTokOptionsValue,\n): boolean {\n return (\n value.commercial_disclosure && !value.your_brand && !value.branded_content\n );\n}\n\n/** True when the post is disclosed as branded content (drives the BC declaration). */\nexport function brandedContentDeclared(value: TikTokOptionsValue): boolean {\n return discloses(value, 'branded_content');\n}\n\n/**\n * TikTok's required label notice for the disclosed content — branded content\n * (alone OR with your-brand) is \"Paid partnership\"; your-brand alone is\n * \"Promotional content\". `null` when nothing is disclosed. Wording is verbatim.\n */\nexport function commercialLabelNotice(\n value: TikTokOptionsValue,\n): string | null {\n if (discloses(value, 'branded_content')) {\n return \"Your photo/video will be labeled as 'Paid partnership'\";\n }\n if (discloses(value, 'your_brand')) {\n return \"Your photo/video will be labeled as 'Promotional content'\";\n }\n return null;\n}\n\n/** Toggle the master disclosure switch. Turning it OFF clears both brand\n * selections (a hidden control must not keep sending a stale selection). */\nexport function setCommercialDisclosure(\n value: TikTokOptionsValue,\n on: boolean,\n): TikTokOptionsValue {\n return on\n ? { ...value, commercial_disclosure: true }\n : {\n ...value,\n commercial_disclosure: false,\n your_brand: false,\n branded_content: false,\n };\n}\n\n/**\n * Set one brand option. Enabling branded content while a private audience is\n * picked clears the audience (branded content can't be SELF_ONLY; no default — the\n * user re-picks from the now-narrowed options).\n */\nexport function setBrandKind(\n value: TikTokOptionsValue,\n kind: TikTokBrandKind,\n on: boolean,\n): TikTokOptionsValue {\n const clearPrivacy =\n kind === 'branded_content' && on && value.privacy_level === 'SELF_ONLY';\n return {\n ...value,\n [kind]: on,\n ...(clearPrivacy ? { privacy_level: undefined } : {}),\n };\n}\n\n/* ------------------------------ interactions ------------------------------ */\n\n/** True when the creator does NOT permit this interaction (so it's locked off). */\nexport function isInteractionForbidden(\n info: TikTokCreatorInfo,\n key: TikTokInteractionKey,\n): boolean {\n return !info.interaction[key];\n}\n\nconst INTERACTION_LABELS: Record<TikTokInteractionKey, string> = {\n comment: 'Comment',\n duet: 'Duet',\n stitch: 'Stitch',\n};\n\n/** Duet & Stitch are video-remix features — TikTok states they are NOT applicable\n * to photo posts (\"only 'Allow Comment' can be displayed\"), and the API rejects\n * `disable_duet`/`disable_stitch` on a non-video post. So a photo post shows only\n * Comment. */\nconst VIDEO_INTERACTIONS: readonly TikTokInteractionKey[] = [\n 'comment',\n 'duet',\n 'stitch',\n];\nconst PHOTO_INTERACTIONS: readonly TikTokInteractionKey[] = ['comment'];\n\nconst INTERACTION_VALUE_KEY = {\n comment: 'allow_comment',\n duet: 'allow_duet',\n stitch: 'allow_stitch',\n} as const satisfies Record<TikTokInteractionKey, keyof TikTokOptionsValue>;\n\n/** The interaction toggles to render — video shows Comment/Duet/Stitch (each\n * disabled iff the creator forbids it); photo shows only Comment. */\nexport function interactionRows(\n info: TikTokCreatorInfo,\n isVideo: boolean,\n): TikTokInteractionRow[] {\n const order = isVideo ? VIDEO_INTERACTIONS : PHOTO_INTERACTIONS;\n return order.map((key) => ({\n key,\n label: INTERACTION_LABELS[key],\n disabled: isInteractionForbidden(info, key),\n }));\n}\n\n/** The `TikTokOptionsValue` flag an interaction key maps to. */\nexport function interactionValueKey(\n key: TikTokInteractionKey,\n): keyof TikTokOptionsValue {\n return INTERACTION_VALUE_KEY[key];\n}\n\n/** Immutably set an interaction flag — returns a new value, never mutates. */\nexport function toggleInteraction(\n value: TikTokOptionsValue,\n key: TikTokInteractionKey,\n on: boolean,\n): TikTokOptionsValue {\n return { ...value, [INTERACTION_VALUE_KEY[key]]: on };\n}\n\n/* -------------------------------- mapping -------------------------------- */\n\n/**\n * Map the composer's positive value into the SDK's `tiktok` settings — INVERTING\n * `allow_*` into TikTok's `disable_*`. `disable_duet`/`disable_stitch`/`is_aigc`\n * are video-only (the API rejects them on a photo post), so a photo post sends\n * just `privacy_level` + `disable_comment` + the brand toggles.\n */\nexport function tiktokSettings(\n value: TikTokOptionsValue,\n isVideo: boolean,\n): NonNullable<SettingsFor<'tiktok'>> {\n const base = {\n privacy_level: value.privacy_level,\n disable_comment: !value.allow_comment,\n brand_organic_toggle: discloses(value, 'your_brand'),\n brand_content_toggle: discloses(value, 'branded_content'),\n };\n\n return isVideo\n ? {\n ...base,\n disable_duet: !value.allow_duet,\n disable_stitch: !value.allow_stitch,\n is_aigc: value.aigc,\n }\n : base;\n}\n\n/* ------------------------------ readiness ------------------------------ */\n\n/**\n * Is the TikTok post ready to publish (client-side gate mirroring the API)?\n * Audience must be chosen and, if disclosure is on, a brand kind must be picked.\n * The caption-cap + media-count checks live with their own components.\n */\nexport function tiktokOptionsReady(value: TikTokOptionsValue): boolean {\n // Branded content cannot be SELF_ONLY (the API rejects it) — mirror that here so\n // the Post button never enables on a state the wire will refuse, even if a host\n // constructs the options object directly (bypassing setBrandKind's clearing).\n const brandedPrivate =\n discloses(value, 'branded_content') && value.privacy_level === 'SELF_ONLY';\n return (\n !audienceUnselected(value) &&\n !commercialDisclosureIncomplete(value) &&\n !brandedPrivate\n );\n}\n","import type {\n Connection,\n CreatePostInput,\n MediaResource,\n PostMetadata,\n PostPlatform,\n PostVariantInput,\n PublishMode,\n SettingsFor,\n UpdatePostInput,\n} from './resources';\n\n/* --------------------------------- types --------------------------------- */\n\n/**\n * A media attachment — just the uploaded asset's id. The post SHAPE (`post_type`\n * and the LinkedIn `content_kind` / Instagram `media_type`) is DERIVED server-side\n * from the media's byte-detected kind, so the customer never declares a media\n * `kind` here — they attach the asset and we figure out the rest.\n */\nexport type MediaInput = Pick<MediaResource, 'id'>;\n\n/** Base content, shared across channels unless a channel overrides it. */\nexport interface PostContent {\n body?: string;\n media?: readonly MediaInput[];\n}\n\n/**\n * One channel's config. The SDK owns the plumbing (connection, media, assembly);\n * the CUSTOMER owns `settings` — the platform's native, fully-typed config. An\n * Instagram setting on an X channel is a compile error. The post shape is never\n * specified here — the server derives it from the attached media.\n */\nexport interface ChannelConfig<P extends PostPlatform> {\n settings: SettingsFor<P>;\n body?: string;\n media?: readonly MediaInput[];\n connectionId?: string;\n}\n\n/** Channels keyed by platform — each value typed to that platform. */\nexport type Channels = { [P in PostPlatform]?: ChannelConfig<P> };\n\nexport interface ComposePostInput {\n profileId: string;\n content?: PostContent;\n channels: Channels;\n publish?: PublishMode;\n scheduleAt?: string;\n externalId?: string;\n metadata?: PostMetadata;\n tags?: readonly string[];\n notes?: string;\n dryRun?: boolean;\n}\n\n/** A post edit. Omit `channels` for a light edit; include it to rebuild variants. */\nexport interface ComposeUpdateInput {\n content?: PostContent;\n channels?: Channels;\n publish?: PublishMode;\n scheduleAt?: string;\n externalId?: string;\n metadata?: PostMetadata;\n tags?: readonly string[];\n notes?: string;\n dryRun?: boolean;\n}\n\n/** The connections a build resolves against (accepts a full `Connection[]`). */\nexport type ConnectionRef = Pick<Connection, 'id' | 'platform'>;\n\n/**\n * Thrown only for a genuine USAGE error the SDK can't paper over — an\n * unresolvable connection, or a build with zero channels. It is NEVER thrown for\n * a media combination: validity (which media a platform accepts, document\n * support, count limits, …) is the SERVER's job — it derives the post shape from\n * the media and surfaces any problem as a typed `ValidationIssue` from\n * `POST /v1/posts/validate`. So the SDK can always build an in-progress composition.\n */\nexport class ComposeError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ComposeError';\n }\n}\n\ntype VariantFor<P extends PostPlatform> = Extract<PostVariantInput, { platform: P }>;\n\n/* ----------------------------- platform handlers -------------------------- */\n\n/** What a handler receives to assemble one variant. */\ninterface ResolvedChannel<P extends PostPlatform> {\n settings: SettingsFor<P>;\n connectionId: string;\n body: string | undefined;\n media: readonly { media_id: string }[];\n}\n\n/**\n * A per-platform variant builder: assemble the typed variant from a resolved\n * channel, passing `settings` straight through (the customer owns it). Each\n * platform gets its own builder so the literal `platform` discriminant ties the\n * result to the right union member with no cast — and so the registry is\n * exhaustive over `PostPlatform`. The post shape is NOT set here: the server\n * derives it from the media.\n */\ninterface PlatformHandler<P extends PostPlatform> {\n buildVariant(resolved: ResolvedChannel<P>): VariantFor<P>;\n}\n\nconst xHandler: PlatformHandler<'x'> = {\n buildVariant: ({ settings, connectionId, body, media }) => ({\n platform: 'x',\n connection_id: connectionId,\n body,\n media: [...media],\n settings,\n }),\n};\n\nconst linkedInHandler: PlatformHandler<'linkedin'> = {\n buildVariant: ({ settings, connectionId, body, media }) => ({\n platform: 'linkedin',\n connection_id: connectionId,\n body,\n media: [...media],\n settings,\n }),\n};\n\nconst facebookHandler: PlatformHandler<'facebook_page'> = {\n buildVariant: ({ settings, connectionId, body, media }) => ({\n platform: 'facebook_page',\n connection_id: connectionId,\n body,\n media: [...media],\n settings,\n }),\n};\n\nconst instagramHandler: PlatformHandler<'instagram'> = {\n buildVariant: ({ settings, connectionId, body, media }) => ({\n platform: 'instagram',\n connection_id: connectionId,\n body,\n media: [...media],\n settings,\n }),\n};\n\nconst tiktokHandler: PlatformHandler<'tiktok'> = {\n buildVariant: ({ settings, connectionId, body, media }) => ({\n platform: 'tiktok',\n connection_id: connectionId,\n body,\n media: [...media],\n settings,\n }),\n};\n\n/**\n * The handler registry. A `Record<PostPlatform, …>` (no optional keys), so a\n * platform the contract defines without a handler here is a COMPILE ERROR.\n */\nconst PLATFORM_HANDLERS: { [P in PostPlatform]: PlatformHandler<P> } = {\n x: xHandler,\n linkedin: linkedInHandler,\n facebook_page: facebookHandler,\n instagram: instagramHandler,\n tiktok: tiktokHandler,\n};\n\n/** Narrow any platform string to a posting platform. */\nexport function isPostPlatform(value: string): value is PostPlatform {\n return Object.prototype.hasOwnProperty.call(PLATFORM_HANDLERS, value);\n}\n\n/** Posting platforms — the single source, derived from the handler registry\n * (the `.filter` type-guard narrows `string[]` → `PostPlatform[]`, cast-free). */\nexport const POST_PLATFORMS: PostPlatform[] =\n Object.keys(PLATFORM_HANDLERS).filter(isPostPlatform);\n\n/* -------------------------------- assembly -------------------------------- */\n\nfunction resolveConnectionId(\n platform: PostPlatform,\n connectionId: string | undefined,\n connections: readonly ConnectionRef[],\n): string {\n if (connectionId) return connectionId;\n const match = connections.find((connection) => connection.platform === platform);\n if (!match) {\n throw new ComposeError(\n `No connection for \"${platform}\" on this profile. Connect the account or pass connectionId.`,\n );\n }\n return match.id;\n}\n\n/** Resolve one channel into its typed variant. Generic over the concrete `P`, so\n * the handler / config / result stay correlated with no cast. */\nfunction buildChannel<P extends PostPlatform>(\n handler: PlatformHandler<P>,\n platform: P,\n config: ChannelConfig<P>,\n content: PostContent,\n connections: readonly ConnectionRef[],\n): VariantFor<P> {\n const media = config.media ?? content.media ?? [];\n return handler.buildVariant({\n settings: config.settings,\n connectionId: resolveConnectionId(platform, config.connectionId, connections),\n body: config.body ?? content.body,\n media: media.map((item) => ({ media_id: item.id })),\n });\n}\n\n/**\n * Resolve ONE platform's channel, or `undefined` when the caller didn't include\n * it. Generic over the concrete `P`, so `channels[platform]` and\n * `PLATFORM_HANDLERS[platform]` stay correlated to the same platform with no cast.\n * Wrapping the registry lookup in this generic is what lets `buildVariants`\n * iterate `POST_PLATFORMS` (a union) without the per-member variance error.\n */\nfunction collectChannel<P extends PostPlatform>(\n platform: P,\n channels: Channels,\n content: PostContent,\n connections: readonly ConnectionRef[],\n): VariantFor<P> | undefined {\n const config = channels[platform];\n if (!config) return undefined;\n return buildChannel(\n PLATFORM_HANDLERS[platform],\n platform,\n config,\n content,\n connections,\n );\n}\n\nfunction buildVariants(\n content: PostContent,\n channels: Channels,\n connections: readonly ConnectionRef[],\n): PostVariantInput[] {\n // Driven by POST_PLATFORMS (derived from the exhaustive PLATFORM_HANDLERS), so a\n // new platform added to the registry is dispatched here automatically — no\n // hand-maintained per-platform branch to forget (the gap that once dropped TikTok).\n const variants = POST_PLATFORMS.flatMap((platform) => {\n const variant = collectChannel(platform, channels, content, connections);\n return variant ? [variant] : [];\n });\n\n if (variants.length === 0) {\n // The ONE retained composition-level guard: \"you called build with no\n // channels\" is a programmer USAGE error, not a per-end-user validity judgment.\n // It is NOT a verdict on whether the media is publishable — that authority\n // (and the post-shape derivation) lives entirely on the server.\n throw new ComposeError('At least one channel is required.');\n }\n return variants;\n}\n\n/**\n * Turn an ergonomic `{ content, channels }` input into the exact\n * `CreatePostInput` the API expects — resolving each channel's connection and\n * attaching the shared/overridden media. The server derives the post shape from\n * that media, so the build is TOTAL: it never throws for a media combination and\n * an in-progress post can always be built to validate. The customer never\n * assembles `variants[]` or sees a `connection_id`, and owns each channel's typed\n * `settings`.\n */\nexport function buildCreatePost(\n input: ComposePostInput,\n connections: readonly ConnectionRef[],\n): CreatePostInput {\n return {\n profile_id: input.profileId,\n publish: input.publish,\n schedule_at: input.scheduleAt,\n external_id: input.externalId,\n metadata: input.metadata,\n tags: input.tags ? [...input.tags] : undefined,\n notes: input.notes,\n dry_run: input.dryRun,\n variants: buildVariants(input.content ?? {}, input.channels, connections),\n };\n}\n\n/** The envelope fields the API counts as a real edit (`dry_run` deliberately not). */\nconst MUTABLE_ENVELOPE_KEYS = [\n 'publish',\n 'scheduleAt',\n 'externalId',\n 'metadata',\n 'tags',\n 'notes',\n] as const satisfies readonly (keyof ComposeUpdateInput)[];\n\n/**\n * Build an `UpdatePostInput`. With `channels`, the full variant set is rebuilt\n * (the API's PATCH replaces it); without it, only the envelope changes.\n */\nexport function buildUpdatePost(\n input: ComposeUpdateInput,\n connections: readonly ConnectionRef[] = [],\n): UpdatePostInput {\n const changesEnvelope = MUTABLE_ENVELOPE_KEYS.some((key) => input[key] !== undefined);\n if (!input.channels && !changesEnvelope) {\n throw new ComposeError(\n 'A post update must change at least one field — pass content/channels or an ' +\n 'envelope field (publish, scheduleAt, tags, …).',\n );\n }\n\n return {\n publish: input.publish,\n schedule_at: input.scheduleAt,\n external_id: input.externalId,\n metadata: input.metadata,\n tags: input.tags ? [...input.tags] : undefined,\n notes: input.notes,\n dry_run: input.dryRun,\n variants: input.channels\n ? buildVariants(input.content ?? {}, input.channels, connections)\n : undefined,\n };\n}\n"]}
|
package/dist/query.cjs
CHANGED
|
@@ -2070,6 +2070,19 @@ var googleGetConversionStatus = (options) => (options.client ?? client).get({
|
|
|
2070
2070
|
url: "/google/{connection_id}/conversions/status",
|
|
2071
2071
|
...options
|
|
2072
2072
|
});
|
|
2073
|
+
var googleListConversionRequests = (options) => (options.client ?? client).get({
|
|
2074
|
+
security: [{
|
|
2075
|
+
key: "secretKey",
|
|
2076
|
+
scheme: "bearer",
|
|
2077
|
+
type: "http"
|
|
2078
|
+
}, {
|
|
2079
|
+
key: "frontendToken",
|
|
2080
|
+
scheme: "bearer",
|
|
2081
|
+
type: "http"
|
|
2082
|
+
}],
|
|
2083
|
+
url: "/google/{connection_id}/conversions",
|
|
2084
|
+
...options
|
|
2085
|
+
});
|
|
2073
2086
|
var googleListConversionGoals = (options) => (options.client ?? client).get({
|
|
2074
2087
|
security: [{
|
|
2075
2088
|
key: "secretKey",
|
|
@@ -3500,6 +3513,41 @@ var googleGetConversionStatusOptions = (options) => reactQuery.queryOptions({
|
|
|
3500
3513
|
},
|
|
3501
3514
|
queryKey: googleGetConversionStatusQueryKey(options)
|
|
3502
3515
|
});
|
|
3516
|
+
var googleListConversionRequestsQueryKey = (options) => createQueryKey("googleListConversionRequests", options);
|
|
3517
|
+
var googleListConversionRequestsOptions = (options) => reactQuery.queryOptions({
|
|
3518
|
+
queryFn: async ({ queryKey, signal }) => {
|
|
3519
|
+
const { data } = await googleListConversionRequests({
|
|
3520
|
+
...options,
|
|
3521
|
+
...queryKey[0],
|
|
3522
|
+
signal,
|
|
3523
|
+
throwOnError: true
|
|
3524
|
+
});
|
|
3525
|
+
return data;
|
|
3526
|
+
},
|
|
3527
|
+
queryKey: googleListConversionRequestsQueryKey(options)
|
|
3528
|
+
});
|
|
3529
|
+
var googleListConversionRequestsInfiniteQueryKey = (options) => createQueryKey("googleListConversionRequests", options, true);
|
|
3530
|
+
var googleListConversionRequestsInfiniteOptions = (options) => reactQuery.infiniteQueryOptions(
|
|
3531
|
+
// @ts-ignore
|
|
3532
|
+
{
|
|
3533
|
+
queryFn: async ({ pageParam, queryKey, signal }) => {
|
|
3534
|
+
const page = typeof pageParam === "object" ? pageParam : {
|
|
3535
|
+
query: {
|
|
3536
|
+
offset: pageParam
|
|
3537
|
+
}
|
|
3538
|
+
};
|
|
3539
|
+
const params = createInfiniteParams(queryKey, page);
|
|
3540
|
+
const { data } = await googleListConversionRequests({
|
|
3541
|
+
...options,
|
|
3542
|
+
...params,
|
|
3543
|
+
signal,
|
|
3544
|
+
throwOnError: true
|
|
3545
|
+
});
|
|
3546
|
+
return data;
|
|
3547
|
+
},
|
|
3548
|
+
queryKey: googleListConversionRequestsInfiniteQueryKey(options)
|
|
3549
|
+
}
|
|
3550
|
+
);
|
|
3503
3551
|
var googleListConversionGoalsQueryKey = (options) => createQueryKey("googleListConversionGoals", options);
|
|
3504
3552
|
var googleListConversionGoalsOptions = (options) => reactQuery.queryOptions({
|
|
3505
3553
|
queryFn: async ({ queryKey, signal }) => {
|
|
@@ -3801,6 +3849,10 @@ exports.googleListConversionActionsOptions = googleListConversionActionsOptions;
|
|
|
3801
3849
|
exports.googleListConversionActionsQueryKey = googleListConversionActionsQueryKey;
|
|
3802
3850
|
exports.googleListConversionGoalsOptions = googleListConversionGoalsOptions;
|
|
3803
3851
|
exports.googleListConversionGoalsQueryKey = googleListConversionGoalsQueryKey;
|
|
3852
|
+
exports.googleListConversionRequestsInfiniteOptions = googleListConversionRequestsInfiniteOptions;
|
|
3853
|
+
exports.googleListConversionRequestsInfiniteQueryKey = googleListConversionRequestsInfiniteQueryKey;
|
|
3854
|
+
exports.googleListConversionRequestsOptions = googleListConversionRequestsOptions;
|
|
3855
|
+
exports.googleListConversionRequestsQueryKey = googleListConversionRequestsQueryKey;
|
|
3804
3856
|
exports.googleListKeywordsOptions = googleListKeywordsOptions;
|
|
3805
3857
|
exports.googleListKeywordsQueryKey = googleListKeywordsQueryKey;
|
|
3806
3858
|
exports.googlePauseAdGroupMutation = googlePauseAdGroupMutation;
|