samsar-js 0.48.30 → 0.48.32

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/README.md CHANGED
@@ -330,14 +330,9 @@ await samsar.deleteEmbedding({
330
330
  source_id: '2',
331
331
  });
332
332
 
333
- // Remove branding/watermark
333
+ // Remove visible text
334
334
  const cleaned = await samsar.removeBrandingFromImage({ image_url: 'https://example.com/photo.png' });
335
335
 
336
- // Replace branding/watermark (original image, replacement logo/image)
337
- const replaced = await samsar.replaceBrandingFromImage({
338
- image_urls: ['https://example.com/photo.png', 'https://example.com/new-logo.png'],
339
- });
340
-
341
336
  // Extend image set
342
337
  const images = await samsar.extendImageList({
343
338
  image_urls: ['https://example.com/extra.jpg'],
@@ -346,6 +341,24 @@ const images = await samsar.extendImageList({
346
341
  aspect_ratio: '16:9',
347
342
  });
348
343
 
344
+ // Assign a short SEO-friendly image title
345
+ const imageTitle = await samsar.assignImageTitle({
346
+ image_url: 'https://example.com/product-photo.png',
347
+ metadata: {
348
+ product: 'linen travel shirt',
349
+ collection: 'spring essentials',
350
+ },
351
+ });
352
+ console.log(imageTitle.data.content); // "Linen Travel Shirt"
353
+
354
+ // Binary uploads are also supported in browser/Node 18+ runtimes
355
+ const fileTitle = await samsar.assignImageTitle({
356
+ image: fileOrBlob,
357
+ fileName: 'product-photo.png',
358
+ mimeType: 'image/png',
359
+ metadata: { product: 'linen travel shirt' },
360
+ });
361
+
349
362
  // Create a reusable receipt template from one sample receipt image (free endpoint)
350
363
  const receiptTemplate = await samsar.createReceiptTemplate({
351
364
  image_url: 'https://example.com/receipt-template.png',
@@ -560,10 +573,10 @@ console.log(externalLibrary.data.requests.map((request) => request.request_id));
560
573
  ```
561
574
 
562
575
  Video model support notes:
563
- - `createVideoFromText` image model keys include: `GPTIMAGE2`, `IMAGEN4`, `SEEDREAM`, `NANOBANANA2`, `CUSTOM_TEXT_TO_IMAGE`.
576
+ - `createVideoFromText` image model keys include: `GPTIMAGE2`, `IMAGEN4`, `SEEDREAM`, `NANOBANANA2`, `NANOBANANAPRO`, `CUSTOM_TEXT_TO_IMAGE`.
564
577
  - `createVideoFromText` supports these video models: `RUNWAYML`, `VEO3.1I2V`, `VEO3.1I2VFAST`, `SEEDANCEI2V` (Seedance 1.5), `KLINGIMGTOVID3PRO`, and `HAPPYHORSEI2V`.
565
578
  - `createVideoFromText` accepts either a provided outro (`outro_image_url`) or server-generated QR outro (`generate_outro_image: true` with `cta_url`). It can also render bottom CTA footer QR cards with `add_footer_animation` and `footer_metadata`; one footer item applies to every generated scene, while multiple items map by scene index.
566
- - `createVideoFromImageList` supports `RUNWAYML`, `VEO3.1I2V`, `VEO3.1I2VFAST`, `SEEDANCEI2V`, `KLINGIMGTOVID3PRO`, and `HAPPYHORSEI2V` via `video_model`; if omitted, it defaults to `RUNWAYML`. Use `aspect_ratio: '16:9'` or `'9:16'`; omitted or invalid values fall back to `16:9`.
579
+ - `createVideoFromImageList` supports `RUNWAYML`, `VEO3.1I2V`, `VEO3.1I2VFAST`, `SEEDANCEI2V`, `KLINGIMGTOVID3PRO`, and `HAPPYHORSEI2V` via `video_model`; if omitted, it defaults to `RUNWAYML`. It also accepts the same `image_model` keys as text-to-video. Use `aspect_ratio: '16:9'` or `'9:16'`; omitted or invalid values fall back to `16:9`.
567
580
  - `createVideoFromImageList` accepts either a provided outro (`outro_image_url`) or server-generated QR outro (`generate_outro_image: true` with `cta_url`). Do not combine the two modes in a single request.
568
581
  - `createVideoFromImageList` can render per-scene footer QR cards by setting `add_footer_animation: true` and providing one `footer_metadata` item per image scene.
569
582
  - `createVideoFromImageList` can also generate QR outro CTA text and each scene footer CTA from a single link by setting `express_cta_generation: true` with `cta_url`. CamelCase `expressCtaGeneration` and compatibility aliases `auto_generate_cta_text` / `generate_cta_texts` are normalized to the same API field.
@@ -579,7 +592,7 @@ Video model support notes:
579
592
 
580
593
  Upcoming `/v2` omni route adapters:
581
594
  - `/v2` is additive; `/v1` is not deprecated.
582
- - `createV2VideoFromText`, `createV2VideoFromImageList`, `translateV2Video`, `cloneV2Video`, `regenerateV2VideoAvatar`, `updateV2VideoOutroImage`, `updateV2VideoFooterImage`, `addV2VideoOutroImage`, `getV2Status`, `getV2StatusDetailed`, `getV2Credits`, `listV2Requests`, and `createV2Session` call the new omni route surface.
595
+ - `createV2VideoFromText`, `createV2VideoFromImageList`, `assignV2ImageTitle`, `translateV2Video`, `cloneV2Video`, `regenerateV2VideoAvatar`, `updateV2VideoOutroImage`, `updateV2VideoFooterImage`, `addV2VideoOutroImage`, `getV2Status`, `getV2StatusDetailed`, `getV2Credits`, `listV2Requests`, and `createV2Session` call the new omni route surface.
583
596
  - Step-controlled video helpers include `createV2StepVideoFromText`, `createV2StepTextToVideo`, `createV2StepVideoFromImage`, `createV2StepImageToVideo`, `getV2StepVideoStatus`, `getV2StepVideoStatusDetailed`, and `processNextV2StepVideo`. They default to 1-step express rendering by sending `auto_render_full_video: true` and `manual_step_stages: []`; pass `{ stepMode: 'two_step' }` or `manual_step_stages: ['ai_video_generation']` to require an explicit second-step approval before image-to-video generation.
584
597
  - Programmatic user helpers include `createV2ExternalUser`, `createV2UserRechargeCredits`, `refreshV2UserToken`, `createV2UserAppKey`, `refreshV2UserAppKey`, `getV2UserCredits`, `getV2UserUsageLogs`, and `getV2UserPaymentStatus`.
585
598
  - Omit `externalUser` for internal account billing, pass `externalUser` to scope an external user with the account API key, or authenticate the client directly with an external-user auth token/API key. V2 external users can be referenced by `unique_key`; if `unique_key` is omitted during creation, the server uses `external_user_id` as the key.
@@ -601,6 +614,12 @@ const v2ExternalVideo = await platform.createV2VideoFromImageList(
601
614
  { externalUser },
602
615
  );
603
616
 
617
+ const v2ImageTitle = await platform.assignV2ImageTitle({
618
+ image_url: 'https://cdn.example.com/product-frame.png',
619
+ metadata: { product: 'travel shirt', channel: 'marketplace' },
620
+ });
621
+ console.log(v2ImageTitle.data.content);
622
+
604
623
  const v2Translated = await platform.translateV2Video({
605
624
  videoSessionId: v2Video.data.request_id!,
606
625
  language: 'es',
package/dist/index.d.ts CHANGED
@@ -939,22 +939,6 @@ export interface RemoveBrandingFromImageResponse {
939
939
  remainingCredits?: number;
940
940
  [key: string]: unknown;
941
941
  }
942
- export interface ReplaceBrandingFromImageRequest {
943
- image_urls: string[];
944
- }
945
- export interface ReplaceBrandingFromImageResponse {
946
- status?: string;
947
- message?: string;
948
- request_id?: string;
949
- session_id?: string;
950
- global_status_id?: string;
951
- case_type?: string;
952
- image_urls: string[];
953
- userId?: string;
954
- creditsCharged?: number;
955
- remainingCredits?: number;
956
- [key: string]: unknown;
957
- }
958
942
  export interface EnhanceImageRequest {
959
943
  image_url: string;
960
944
  resolution?: '1k' | '2k' | '3k' | '4k';
@@ -975,6 +959,33 @@ export interface EnhanceImageResponse {
975
959
  remainingCredits?: number;
976
960
  [key: string]: unknown;
977
961
  }
962
+ export type AssignImageTitleMimeType = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/gif';
963
+ export type AssignImageTitleBinaryInput = Blob | ArrayBuffer | Uint8Array;
964
+ export interface AssignImageTitleRequest {
965
+ image?: string | AssignImageTitleBinaryInput;
966
+ file?: AssignImageTitleBinaryInput;
967
+ image_file?: AssignImageTitleBinaryInput;
968
+ imageFile?: AssignImageTitleBinaryInput;
969
+ image_url?: string;
970
+ imageUrl?: string;
971
+ url?: string;
972
+ image_data?: string;
973
+ imageData?: string;
974
+ image_data_url?: string;
975
+ imageDataUrl?: string;
976
+ metadata?: Record<string, unknown> | string;
977
+ metadata_json?: Record<string, unknown> | string;
978
+ metadataJson?: Record<string, unknown> | string;
979
+ fileName?: string;
980
+ filename?: string;
981
+ mimeType?: AssignImageTitleMimeType | string;
982
+ [key: string]: unknown;
983
+ }
984
+ export interface AssignImageTitleResponse {
985
+ content: string;
986
+ title?: string;
987
+ [key: string]: unknown;
988
+ }
978
989
  export interface ExtendImageListRequest {
979
990
  image_urls: string[];
980
991
  num_images: number;
@@ -1839,6 +1850,18 @@ export interface CreditsRechargeResponse {
1839
1850
  currency: string;
1840
1851
  [key: string]: unknown;
1841
1852
  }
1853
+ export interface V2CreditsRechargeRequest {
1854
+ credits?: number;
1855
+ credits_to_recharge?: number;
1856
+ creditsToRecharge?: number;
1857
+ [key: string]: unknown;
1858
+ }
1859
+ export interface V2CreditsGrantRequest {
1860
+ credits?: number;
1861
+ credits_to_grant?: number;
1862
+ creditsToGrant?: number;
1863
+ [key: string]: unknown;
1864
+ }
1842
1865
  export interface EnableAutoRechargeRequest {
1843
1866
  thresholdCredits?: number;
1844
1867
  amountUsd?: number;
@@ -1943,6 +1966,8 @@ export declare class SamsarClient {
1943
1966
  getV2<T = Record<string, unknown>>(path: string, options?: V2RequestOptions): Promise<SamsarResult<T>>;
1944
1967
  createV2Session(options?: V2RequestOptions): Promise<SamsarResult<V2SessionResponse>>;
1945
1968
  getV2Credits(options?: V2RequestOptions): Promise<SamsarResult<CreditsBalanceResponse | ExternalCreditsBalanceResponse>>;
1969
+ createV2CreditsRecharge(payload: number | V2CreditsRechargeRequest, options?: V2RequestOptions): Promise<SamsarResult<CreditsRechargeResponse | ExternalCreditsRechargeResponse>>;
1970
+ grantV2Credits(payload: number | V2CreditsGrantRequest, options?: V2RequestOptions): Promise<SamsarResult<ExternalCreditsGrantResponse>>;
1946
1971
  getV2UserCredits(options?: V2RequestOptions): Promise<SamsarResult<CreditsBalanceResponse>>;
1947
1972
  getV2UserUsageLogs(options?: V2RequestOptions & {
1948
1973
  page?: number;
@@ -1959,6 +1984,7 @@ export declare class SamsarClient {
1959
1984
  refreshV2UserAppKey(payload?: V2UserAppKeyRefreshRequest, options?: V2RequestOptions): Promise<SamsarResult<V2UserAppKeyResponse>>;
1960
1985
  revokeV2UserAppKey(options?: V2RequestOptions): Promise<SamsarResult<V2UserAppKeyResponse>>;
1961
1986
  getV2UserPaymentStatus(payload: PaymentStatusRequest, options?: V2RequestOptions): Promise<SamsarResult<PaymentStatusResponse>>;
1987
+ getV2PaymentStatus(payload: PaymentStatusRequest, options?: V2RequestOptions): Promise<SamsarResult<PaymentStatusResponse | ExternalPaymentStatusResponse>>;
1962
1988
  createV2LoginToken(options?: V2RequestOptions & {
1963
1989
  redirect?: string;
1964
1990
  }): Promise<SamsarResult<CreateLoginTokenResponse | ExternalCreateLoginTokenResponse>>;
@@ -1978,9 +2004,16 @@ export declare class SamsarClient {
1978
2004
  uploadV2ImageData(imageData: string[], options?: V2RequestOptions): Promise<SamsarResult<{
1979
2005
  image_urls: string[];
1980
2006
  }>>;
2007
+ /**
2008
+ * Assign a short SEO-friendly title to an image using the /v2 image route.
2009
+ * Supports image_url/image_data JSON payloads or binary image/FormData uploads.
2010
+ */
2011
+ assignV2ImageTitle(payload: AssignImageTitleRequest | FormData, options?: V2RequestOptions): Promise<SamsarResult<AssignImageTitleResponse>>;
2012
+ assignV2TitleToImage(payload: AssignImageTitleRequest | FormData, options?: V2RequestOptions): Promise<SamsarResult<AssignImageTitleResponse>>;
1981
2013
  updateV2VideoOutroImage(input: UpdateVideoOutroImageInput, options?: V2RequestOptions): Promise<SamsarResult<UpdateVideoOutroImageResponse | ExternalRequestResponse>>;
1982
2014
  updateV2VideoFooterImage(input: UpdateVideoFooterImageInput, options?: V2RequestOptions): Promise<SamsarResult<UpdateVideoFooterImageResponse | ExternalRequestResponse>>;
1983
2015
  addV2VideoOutroImage(input: AddVideoOutroImageInput, options?: V2RequestOptions): Promise<SamsarResult<AddVideoOutroImageResponse | ExternalRequestResponse>>;
2016
+ joinV2Videos(input: JoinVideosInput, options?: V2RequestOptions): Promise<SamsarResult<JoinVideosResponse | ExternalRequestResponse>>;
1984
2017
  getV2Status(requestId: string, options?: V2RequestOptions & {
1985
2018
  queryParams?: QueryParams;
1986
2019
  }): Promise<SamsarResult<GlobalStatusResponse | ExternalStatusResponse>>;
@@ -1990,6 +2023,10 @@ export declare class SamsarClient {
1990
2023
  getV2DetailedStatus(requestId: string, options?: V2RequestOptions & {
1991
2024
  queryParams?: QueryParams;
1992
2025
  }): Promise<SamsarResult<GlobalStatusDetailedResponse | ExternalStatusDetailedResponse>>;
2026
+ /**
2027
+ * Cancel an in-progress v2 render for an existing video session.
2028
+ */
2029
+ cancelV2Render(input: CancelRenderInput, options?: V2RequestOptions): Promise<SamsarResult<CancelRenderResponse>>;
1993
2030
  /**
1994
2031
  * Create a new video generation job from text.
1995
2032
  */
@@ -2252,17 +2289,19 @@ export declare class SamsarClient {
2252
2289
  */
2253
2290
  getEmbeddingStatus(templateId: string, options?: SamsarRequestOptions): Promise<SamsarResult<EmbeddingStatusResponse>>;
2254
2291
  /**
2255
- * Remove branding/watermark from an image by URL.
2292
+ * Remove visible text from an image by URL.
2256
2293
  */
2257
2294
  removeBrandingFromImage(payload: RemoveBrandingFromImageRequest, options?: SamsarRequestOptions): Promise<SamsarResult<RemoveBrandingFromImageResponse>>;
2258
- /**
2259
- * Replace branding/watermark on an image by providing the original and replacement image URLs.
2260
- */
2261
- replaceBrandingFromImage(payload: ReplaceBrandingFromImageRequest, options?: SamsarRequestOptions): Promise<SamsarResult<ReplaceBrandingFromImageResponse>>;
2262
2295
  /**
2263
2296
  * Enhance an image by upscaling it with the specified resolution.
2264
2297
  */
2265
2298
  enhanceImage(payload: EnhanceImageRequest, options?: SamsarRequestOptions): Promise<SamsarResult<EnhanceImageResponse>>;
2299
+ /**
2300
+ * Assign a short SEO-friendly title to an image.
2301
+ * Supports image_url/image_data JSON payloads or binary image/FormData uploads.
2302
+ */
2303
+ assignImageTitle(payload: AssignImageTitleRequest | FormData, options?: SamsarRequestOptions): Promise<SamsarResult<AssignImageTitleResponse>>;
2304
+ assignTitleToImage(payload: AssignImageTitleRequest | FormData, options?: SamsarRequestOptions): Promise<SamsarResult<AssignImageTitleResponse>>;
2266
2305
  /**
2267
2306
  * Add or extend a saved image list for the authenticated API key.
2268
2307
  */
@@ -2379,6 +2418,7 @@ export declare class SamsarClient {
2379
2418
  }): Promise<SamsarResult<GlobalStatusResponse>>;
2380
2419
  private get;
2381
2420
  private post;
2421
+ private postForm;
2382
2422
  private request;
2383
2423
  private buildHeaders;
2384
2424
  private buildUrl;
package/dist/index.js CHANGED
@@ -557,6 +557,32 @@ export class SamsarClient {
557
557
  async getV2Credits(options) {
558
558
  return this.getV2('credits', options);
559
559
  }
560
+ async createV2CreditsRecharge(payload, options) {
561
+ const input = typeof payload === 'number' ? { credits: payload } : (payload ?? {});
562
+ const credits = Number(input.credits ?? input.credits_to_recharge ?? input.creditsToRecharge);
563
+ if (!Number.isFinite(credits) || credits <= 0 || !Number.isInteger(credits)) {
564
+ throw new Error('credits must be a positive integer');
565
+ }
566
+ return this.postV2('credits/recharge', {
567
+ input: {
568
+ ...input,
569
+ credits,
570
+ },
571
+ }, options);
572
+ }
573
+ async grantV2Credits(payload, options) {
574
+ const input = typeof payload === 'number' ? { credits: payload } : (payload ?? {});
575
+ const credits = Number(input.credits ?? input.credits_to_grant ?? input.creditsToGrant);
576
+ if (!Number.isFinite(credits) || credits <= 0 || !Number.isInteger(credits)) {
577
+ throw new Error('credits must be a positive integer');
578
+ }
579
+ return this.postV2('credits/grant', {
580
+ input: {
581
+ ...input,
582
+ credits,
583
+ },
584
+ }, options);
585
+ }
560
586
  async getV2UserCredits(options) {
561
587
  return this.getV2('user/credits', options);
562
588
  }
@@ -689,6 +715,24 @@ export class SamsarClient {
689
715
  query,
690
716
  });
691
717
  }
718
+ async getV2PaymentStatus(payload, options) {
719
+ const query = {
720
+ ...(options?.query ?? {}),
721
+ };
722
+ if (payload?.checkoutSessionId) {
723
+ query.checkoutSessionId = payload.checkoutSessionId;
724
+ }
725
+ if (payload?.paymentIntentId) {
726
+ query.paymentIntentId = payload.paymentIntentId;
727
+ }
728
+ if (payload?.setupIntentId) {
729
+ query.setupIntentId = payload.setupIntentId;
730
+ }
731
+ return this.getV2('payment_status', {
732
+ ...(options ?? {}),
733
+ query,
734
+ });
735
+ }
692
736
  async createV2LoginToken(options) {
693
737
  const body = options?.redirect ? { redirect: options.redirect } : {};
694
738
  return this.postV2('create_login_token', body, options);
@@ -792,6 +836,22 @@ export class SamsarClient {
792
836
  },
793
837
  }, options);
794
838
  }
839
+ /**
840
+ * Assign a short SEO-friendly title to an image using the /v2 image route.
841
+ * Supports image_url/image_data JSON payloads or binary image/FormData uploads.
842
+ */
843
+ async assignV2ImageTitle(payload, options) {
844
+ const body = buildAssignImageTitleBody(payload);
845
+ if (isFormDataBody(body)) {
846
+ return this.postForm(this.buildV2Url('image/assign_title'), body, options);
847
+ }
848
+ return this.postV2('image/assign_title', {
849
+ input: body,
850
+ }, options);
851
+ }
852
+ async assignV2TitleToImage(payload, options) {
853
+ return this.assignV2ImageTitle(payload, options);
854
+ }
795
855
  async updateV2VideoOutroImage(input, options) {
796
856
  const normalizedInput = normalizeUpdateVideoOutroImageInput(input, 'updateV2VideoOutroImage');
797
857
  return this.postV2('update_outro_image', {
@@ -812,6 +872,22 @@ export class SamsarClient {
812
872
  webhookUrl: options?.webhookUrl,
813
873
  }, options);
814
874
  }
875
+ async joinV2Videos(input, options) {
876
+ const rawIds = input.session_ids ?? input.sessionIds ?? input.video_session_ids ?? input.videoSessionIds;
877
+ const sessionIds = Array.isArray(rawIds)
878
+ ? rawIds.map((value) => (typeof value === 'string' ? value.trim() : '')).filter(Boolean)
879
+ : [];
880
+ if (sessionIds.length < 2) {
881
+ throw new Error('at least two session IDs are required for joinV2Videos');
882
+ }
883
+ return this.postV2('join_videos', {
884
+ input: {
885
+ ...input,
886
+ session_ids: sessionIds,
887
+ },
888
+ webhookUrl: options?.webhookUrl,
889
+ }, options);
890
+ }
815
891
  async getV2Status(requestId, options) {
816
892
  const queryParams = {
817
893
  ...(options?.externalUser
@@ -841,6 +917,48 @@ export class SamsarClient {
841
917
  async getV2DetailedStatus(requestId, options) {
842
918
  return this.getV2StatusDetailed(requestId, options);
843
919
  }
920
+ /**
921
+ * Cancel an in-progress v2 render for an existing video session.
922
+ */
923
+ async cancelV2Render(input, options) {
924
+ const raw = input;
925
+ const videoSessionId = raw.videoSessionId ??
926
+ raw.video_session_id ??
927
+ raw.videoSessionID ??
928
+ raw.session_id ??
929
+ raw.sessionId ??
930
+ raw.sessionID ??
931
+ raw.request_id ??
932
+ raw.requestId;
933
+ if (!videoSessionId) {
934
+ throw new Error('videoSessionId is required for cancelV2Render');
935
+ }
936
+ const response = await this.postV2('cancel_render', {
937
+ input: {
938
+ ...input,
939
+ videoSessionId: String(videoSessionId),
940
+ },
941
+ }, options);
942
+ const data = response.data;
943
+ if (data && typeof data === 'object') {
944
+ const sessionId = typeof data.sessionID === 'string'
945
+ ? data.sessionID
946
+ : typeof data.session_id === 'string'
947
+ ? data.session_id
948
+ : typeof data.request_id === 'string'
949
+ ? data.request_id
950
+ : undefined;
951
+ const normalizedSessionId = sessionId ? String(sessionId) : undefined;
952
+ const normalizedData = {
953
+ ...data,
954
+ sessionID: data.sessionID ?? normalizedSessionId ?? '',
955
+ session_id: data.session_id ?? normalizedSessionId,
956
+ request_id: data.request_id ?? normalizedSessionId,
957
+ };
958
+ return { ...response, data: normalizedData };
959
+ }
960
+ return response;
961
+ }
844
962
  /**
845
963
  * Create a new video generation job from text.
846
964
  */
@@ -1741,23 +1859,31 @@ export class SamsarClient {
1741
1859
  });
1742
1860
  }
1743
1861
  /**
1744
- * Remove branding/watermark from an image by URL.
1862
+ * Remove visible text from an image by URL.
1745
1863
  */
1746
1864
  async removeBrandingFromImage(payload, options) {
1747
1865
  return this.post('image/remove_branding', payload, options);
1748
1866
  }
1749
- /**
1750
- * Replace branding/watermark on an image by providing the original and replacement image URLs.
1751
- */
1752
- async replaceBrandingFromImage(payload, options) {
1753
- return this.post('image/replace_branding', payload, options);
1754
- }
1755
1867
  /**
1756
1868
  * Enhance an image by upscaling it with the specified resolution.
1757
1869
  */
1758
1870
  async enhanceImage(payload, options) {
1759
1871
  return this.post('image/enhance', payload, options);
1760
1872
  }
1873
+ /**
1874
+ * Assign a short SEO-friendly title to an image.
1875
+ * Supports image_url/image_data JSON payloads or binary image/FormData uploads.
1876
+ */
1877
+ async assignImageTitle(payload, options) {
1878
+ const body = buildAssignImageTitleBody(payload);
1879
+ if (isFormDataBody(body)) {
1880
+ return this.postForm('image/assign_title', body, options);
1881
+ }
1882
+ return this.post('image/assign_title', body, options);
1883
+ }
1884
+ async assignTitleToImage(payload, options) {
1885
+ return this.assignImageTitle(payload, options);
1886
+ }
1761
1887
  /**
1762
1888
  * Add or extend a saved image list for the authenticated API key.
1763
1889
  */
@@ -2132,6 +2258,13 @@ export class SamsarClient {
2132
2258
  body: JSON.stringify(body),
2133
2259
  });
2134
2260
  }
2261
+ async postForm(path, body, options) {
2262
+ return this.request(path, {
2263
+ ...(options ?? {}),
2264
+ method: 'POST',
2265
+ body,
2266
+ });
2267
+ }
2135
2268
  async request(path, options) {
2136
2269
  const url = this.buildUrl(path, options.query);
2137
2270
  const controller = new AbortController();
@@ -2221,7 +2354,7 @@ export class SamsarClient {
2221
2354
  : this.apiKey
2222
2355
  ? `Bearer ${this.apiKey}`
2223
2356
  : undefined,
2224
- 'Content-Type': options.body ? 'application/json' : undefined,
2357
+ 'Content-Type': options.body && !isFormDataBody(options.body) ? 'application/json' : undefined,
2225
2358
  'x-external-user-api-key': options.externalUserApiKey ?? this.externalUserApiKey,
2226
2359
  'x-app-secret': resolvedAppKey ? resolvedAppSecret : undefined,
2227
2360
  ...this.defaultHeaders,
@@ -2288,6 +2421,104 @@ export class SamsarClient {
2288
2421
  function trimTrailingSlash(url) {
2289
2422
  return url.replace(/\/+$/, '');
2290
2423
  }
2424
+ function buildAssignImageTitleBody(payload) {
2425
+ if (isFormDataBody(payload)) {
2426
+ return payload;
2427
+ }
2428
+ const input = (payload ?? {});
2429
+ const image = input.image ??
2430
+ input.file ??
2431
+ input.image_file ??
2432
+ input.imageFile;
2433
+ if (isBinaryAssignImageTitleInput(image)) {
2434
+ return buildAssignImageTitleFormData(input, image);
2435
+ }
2436
+ const stringImage = getTrimmedString(image);
2437
+ const imageData = stringImage?.startsWith('data:image/')
2438
+ ? stringImage
2439
+ : getTrimmedString(input.image_data) ??
2440
+ getTrimmedString(input.imageData) ??
2441
+ getTrimmedString(input.image_data_url) ??
2442
+ getTrimmedString(input.imageDataUrl);
2443
+ const imageUrl = stringImage && !stringImage.startsWith('data:image/')
2444
+ ? stringImage
2445
+ : getTrimmedString(input.image_url) ??
2446
+ getTrimmedString(input.imageUrl) ??
2447
+ getTrimmedString(input.url);
2448
+ const metadata = input.metadata ?? input.metadata_json ?? input.metadataJson;
2449
+ if (!imageData && !imageUrl) {
2450
+ throw new Error('image, image_data, or image_url is required for assignImageTitle');
2451
+ }
2452
+ return {
2453
+ ...(imageData ? { image_data: imageData } : {}),
2454
+ ...(imageUrl ? { image_url: imageUrl } : {}),
2455
+ ...(metadata !== undefined ? { metadata } : {}),
2456
+ };
2457
+ }
2458
+ function buildAssignImageTitleFormData(input, image) {
2459
+ if (typeof FormData === 'undefined') {
2460
+ throw new Error('FormData is required for binary assignImageTitle uploads');
2461
+ }
2462
+ const metadata = input.metadata ?? input.metadata_json ?? input.metadataJson;
2463
+ const mimeType = getTrimmedString(input.mimeType) ?? getBlobType(image);
2464
+ if (!mimeType || !isSupportedAssignImageTitleMimeType(mimeType)) {
2465
+ throw new Error('mimeType must be image/png, image/jpeg, image/webp, or image/gif for binary assignImageTitle uploads');
2466
+ }
2467
+ const blob = toAssignImageTitleBlob(image, mimeType);
2468
+ const fileName = getTrimmedString(input.fileName) ??
2469
+ getTrimmedString(input.filename) ??
2470
+ `image.${mimeTypeToExtension(mimeType)}`;
2471
+ const formData = new FormData();
2472
+ formData.append('image', blob, fileName);
2473
+ if (metadata !== undefined) {
2474
+ formData.append('metadata', typeof metadata === 'string' ? metadata : JSON.stringify(metadata));
2475
+ }
2476
+ return formData;
2477
+ }
2478
+ function isBinaryAssignImageTitleInput(value) {
2479
+ return (isBlobValue(value) ||
2480
+ value instanceof ArrayBuffer ||
2481
+ value instanceof Uint8Array);
2482
+ }
2483
+ function toAssignImageTitleBlob(value, mimeType) {
2484
+ if (isBlobValue(value)) {
2485
+ if (value.type === mimeType) {
2486
+ return value;
2487
+ }
2488
+ return new Blob([value], { type: mimeType });
2489
+ }
2490
+ if (value instanceof Uint8Array) {
2491
+ const arrayBuffer = value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength);
2492
+ return new Blob([arrayBuffer], { type: mimeType });
2493
+ }
2494
+ return new Blob([value], { type: mimeType });
2495
+ }
2496
+ function getBlobType(value) {
2497
+ return isBlobValue(value) ? getTrimmedString(value.type) : undefined;
2498
+ }
2499
+ function isBlobValue(value) {
2500
+ return typeof Blob !== 'undefined' && value instanceof Blob;
2501
+ }
2502
+ function isFormDataBody(body) {
2503
+ return typeof FormData !== 'undefined' && body instanceof FormData;
2504
+ }
2505
+ function isSupportedAssignImageTitleMimeType(value) {
2506
+ const normalized = value.trim().toLowerCase();
2507
+ return ['image/png', 'image/jpeg', 'image/jpg', 'image/webp', 'image/gif'].includes(normalized);
2508
+ }
2509
+ function mimeTypeToExtension(value) {
2510
+ const normalized = value.trim().toLowerCase();
2511
+ if (normalized === 'image/jpeg' || normalized === 'image/jpg') {
2512
+ return 'jpg';
2513
+ }
2514
+ if (normalized === 'image/webp') {
2515
+ return 'webp';
2516
+ }
2517
+ if (normalized === 'image/gif') {
2518
+ return 'gif';
2519
+ }
2520
+ return 'png';
2521
+ }
2291
2522
  function parseMaybeJson(value) {
2292
2523
  try {
2293
2524
  return JSON.parse(value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "samsar-js",
3
- "version": "0.48.30",
3
+ "version": "0.48.32",
4
4
  "description": "TypeScript client for the Samsar Processor API routes.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",