samsar-js 0.48.23 → 0.48.25

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
@@ -131,13 +131,31 @@ await samsar.updateVideoOutroImage(
131
131
  { webhookUrl: 'https://example.com/webhook' },
132
132
  );
133
133
 
134
+ // Update the footer CTA on an existing video session
135
+ await samsar.updateVideoFooterImage(
136
+ {
137
+ videoSessionId: videoFromImages.data.session_id ?? videoFromImages.data.request_id!,
138
+ cta_text: 'Scan to book',
139
+ cta_logo: 'https://cdn.example.com/logo-white.png',
140
+ cta_url: 'https://example.com/book',
141
+ },
142
+ { webhookUrl: 'https://example.com/webhook' },
143
+ );
144
+
145
+ // Remove the footer from a cloned rerender
146
+ await samsar.updateVideoFooterImage({
147
+ videoSessionId: videoFromImages.data.session_id ?? videoFromImages.data.request_id!,
148
+ remove_footer: true,
149
+ });
150
+
134
151
  // Translate an existing video session into another language
135
152
  const translated = await samsar.translateVideo(
136
153
  {
137
154
  videoSessionId: videoFromImages.data.session_id ?? videoFromImages.data.request_id!,
138
155
  language: 'es',
139
- // Optional: override the outro image in the translated session
140
- outro_image_url: 'https://cdn.example.com/outro.png',
156
+ enable_subtitles: false,
157
+ translate_outro: true,
158
+ translate_footer: true,
141
159
  },
142
160
  { webhookUrl: 'https://example.com/webhook' },
143
161
  );
@@ -445,6 +463,15 @@ const externalOutroUpdate = await platform.updateExternalVideoOutroImage(externa
445
463
  });
446
464
  console.log(externalOutroUpdate.data.request_id);
447
465
 
466
+ // Update or remove an external user's existing footer CTA.
467
+ const externalFooterUpdate = await platform.updateExternalVideoFooterImage(externalUser, {
468
+ request_id: externalRender.data.request_id,
469
+ cta_text: 'Scan to shop',
470
+ cta_logo: 'https://cdn.example.com/logo-white.png',
471
+ cta_url: 'https://example.com/shop',
472
+ });
473
+ console.log(externalFooterUpdate.data.request_id);
474
+
448
475
  // Repeated video routes accept the returned extreq_ id or the normalized external id.
449
476
  // The API resolves ownership through external request mappings and GlobalSession records.
450
477
 
@@ -516,14 +543,15 @@ Video model support notes:
516
543
  - `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.
517
544
  - `createVideoFromImageList` can render per-scene footer QR cards by setting `add_footer_animation: true` and providing one `footer_metadata` item per image scene.
518
545
  - `updateVideoOutroImage` accepts either a replacement outro image URL (`outro_image_url`, `outroImageUrl`, `new_outro_image_url`) or a generated QR CTA outro (`generate_outro_image: true` with `cta_url`, or just `cta_url` when no outro image URL is supplied). Generated outro updates reuse the existing session image layers for tiling and only queue frame/video regeneration.
519
- - Main video methods and external-user methods accept the same generated outro and footer parameters. The API can resolve either internal session ids or external `extreq_...` ids on repeated video routes, so client code can keep using `translateVideo`, `joinVideos`, `addSubtitles`, `removeSubtitles`, `addVideoOutroImage`, and `updateVideoOutroImage`; the explicit external variants are available when you want to call `/external_users/*` directly. Do not strip the `extreq_` prefix.
546
+ - `updateVideoFooterImage` updates the footer CTA on a cloned session with `cta_text`, `cta_logo`, and/or `cta_url`, or removes all scene footers with `remove_footer: true`. Footer updates queue only frame/video regeneration.
547
+ - Main video methods and external-user methods accept the same generated outro and footer parameters. The API can resolve either internal session ids or external `extreq_...` ids on repeated video routes, so client code can keep using `translateVideo`, `joinVideos`, `addSubtitles`, `removeSubtitles`, `addVideoOutroImage`, `updateVideoOutroImage`, and `updateVideoFooterImage`; the explicit external variants are available when you want to call `/external_users/*` directly. Do not strip the `extreq_` prefix.
520
548
  - Completed video status, latest-version, and completed-session list responses expose `has_subtitles` and `result_language` when the session metadata is available.
521
549
  - `publishPublication`, `editPublication`, and `revokePublication` manage public feed publications for completed sessions through free `/publications/*` endpoints. They work with account API keys, customer sub-account API keys, and client auth tokens when the session belongs to the authenticated actor.
522
550
  - Image-list video pricing is per rendered second: `VEO3.1I2V` and `SEEDANCEI2V` are 75 credits/sec, `KLING3.0` is 50 credits/sec, and `RUNWAYML` is 25 credits/sec.
523
551
 
524
552
  Upcoming `/v2` omni route adapters:
525
553
  - `/v2` is additive; `/v1` is not deprecated.
526
- - `createV2VideoFromText`, `createV2VideoFromImageList`, `updateV2VideoOutroImage`, `addV2VideoOutroImage`, `getV2Status`, `getV2Credits`, `listV2Requests`, and `createV2Session` call the new omni route surface.
554
+ - `createV2VideoFromText`, `createV2VideoFromImageList`, `translateV2Video`, `updateV2VideoOutroImage`, `updateV2VideoFooterImage`, `addV2VideoOutroImage`, `getV2Status`, `getV2Credits`, `listV2Requests`, and `createV2Session` call the new omni route surface.
527
555
  - Programmatic user billing helpers include `createV2UserRechargeCredits`, `refreshV2UserToken`, `createV2UserAppKey`, `refreshV2UserAppKey`, `getV2UserCredits`, `getV2UserUsageLogs`, and `getV2UserPaymentStatus`.
528
556
  - 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.
529
557
 
@@ -544,6 +572,14 @@ const v2ExternalVideo = await platform.createV2VideoFromImageList(
544
572
  { externalUser },
545
573
  );
546
574
 
575
+ const v2Translated = await platform.translateV2Video({
576
+ videoSessionId: v2Video.data.request_id!,
577
+ language: 'es',
578
+ enable_subtitles: false,
579
+ translate_outro: true,
580
+ translate_footer: true,
581
+ });
582
+
547
583
  const v2Status = await platform.getV2Status(v2Video.data.request_id!);
548
584
  console.log(v2Status.data.status);
549
585
  ```
package/dist/index.d.ts CHANGED
@@ -95,8 +95,18 @@ export interface OutroFocusArea {
95
95
  height: number;
96
96
  }
97
97
  export interface FooterMetadataItem {
98
- url: string;
98
+ url?: string;
99
+ cta_url?: string;
100
+ ctaUrl?: string;
99
101
  title?: string;
102
+ text?: string;
103
+ cta_text?: string;
104
+ ctaText?: string;
105
+ cta_logo?: string;
106
+ ctaLogo?: string;
107
+ logoUrl?: string;
108
+ logoImagePath?: string;
109
+ footerLogoImagePath?: string;
100
110
  }
101
111
  export type ImageListToVideoAspectRatio = '16:9' | '9:16';
102
112
  export type ImageListToVideoModel = 'VEO3.1I2V' | 'SEEDANCEI2V' | 'KLING3.0' | 'KLINGIMGTOVID3PRO' | 'RUNWAYML';
@@ -209,10 +219,14 @@ export interface TranslateVideoInput {
209
219
  langauge?: string;
210
220
  langauge_code?: string;
211
221
  langaugeCode?: string;
212
- outro_image_url?: string;
213
- outroImageUrl?: string;
214
- new_outro_image_url?: string;
215
- newOutroImageUrl?: string;
222
+ enable_subtitles?: boolean;
223
+ enableSubtitles?: boolean;
224
+ add_subtitles?: boolean;
225
+ addSubtitles?: boolean;
226
+ translate_outro?: boolean;
227
+ translateOutro?: boolean;
228
+ translate_footer?: boolean;
229
+ translateFooter?: boolean;
216
230
  [key: string]: unknown;
217
231
  }
218
232
  export interface TranslateVideoResponse {
@@ -270,6 +284,39 @@ export interface UpdateVideoOutroImageResponse {
270
284
  remainingCredits?: number | null;
271
285
  [key: string]: unknown;
272
286
  }
287
+ export interface UpdateVideoFooterImageInput {
288
+ videoSessionId?: string;
289
+ video_session_id?: string;
290
+ videoSessionID?: string;
291
+ session_id?: string;
292
+ sessionId?: string;
293
+ sessionID?: string;
294
+ request_id?: string;
295
+ requestId?: string;
296
+ source_request_id?: string;
297
+ sourceRequestId?: string;
298
+ external_request_id?: string;
299
+ externalRequestId?: string;
300
+ external_session_id?: string;
301
+ externalSessionId?: string;
302
+ remove_footer?: boolean;
303
+ removeFooter?: boolean;
304
+ cta_text?: string;
305
+ ctaText?: string;
306
+ cta_logo?: string;
307
+ ctaLogo?: string;
308
+ cta_url?: string;
309
+ ctaUrl?: string;
310
+ [key: string]: unknown;
311
+ }
312
+ export interface UpdateVideoFooterImageResponse {
313
+ request_id?: string;
314
+ session_id?: string;
315
+ sessionID?: string;
316
+ creditsCharged?: number;
317
+ remainingCredits?: number | null;
318
+ [key: string]: unknown;
319
+ }
273
320
  export interface AddVideoOutroImageInput {
274
321
  videoSessionId?: string;
275
322
  video_session_id?: string;
@@ -1674,10 +1721,12 @@ export declare class SamsarClient {
1674
1721
  }): Promise<SamsarResult<CreateLoginTokenResponse | ExternalCreateLoginTokenResponse>>;
1675
1722
  createV2VideoFromText(input: CreateVideoFromTextInput, options?: V2RequestOptions): Promise<SamsarResult<CreateVideoResponse | ExternalRequestResponse>>;
1676
1723
  createV2VideoFromImageList(input: CreateVideoFromImageListInput, options?: V2RequestOptions): Promise<SamsarResult<CreateVideoFromImageListResponse | ExternalRequestResponse>>;
1724
+ translateV2Video(input: TranslateVideoInput, options?: V2RequestOptions): Promise<SamsarResult<TranslateVideoResponse | ExternalRequestResponse>>;
1677
1725
  uploadV2ImageData(imageData: string[], options?: V2RequestOptions): Promise<SamsarResult<{
1678
1726
  image_urls: string[];
1679
1727
  }>>;
1680
1728
  updateV2VideoOutroImage(input: UpdateVideoOutroImageInput, options?: V2RequestOptions): Promise<SamsarResult<UpdateVideoOutroImageResponse | ExternalRequestResponse>>;
1729
+ updateV2VideoFooterImage(input: UpdateVideoFooterImageInput, options?: V2RequestOptions): Promise<SamsarResult<UpdateVideoFooterImageResponse | ExternalRequestResponse>>;
1681
1730
  addV2VideoOutroImage(input: AddVideoOutroImageInput, options?: V2RequestOptions): Promise<SamsarResult<AddVideoOutroImageResponse | ExternalRequestResponse>>;
1682
1731
  getV2Status(requestId: string, options?: V2RequestOptions & {
1683
1732
  queryParams?: QueryParams;
@@ -1797,6 +1846,20 @@ export declare class SamsarClient {
1797
1846
  updateExternalVideoOutroImage(externalUser: ExternalUserIdentity, input: UpdateVideoOutroImageInput, options?: {
1798
1847
  webhookUrl?: string;
1799
1848
  } & SamsarRequestOptions): Promise<SamsarResult<ExternalRequestResponse>>;
1849
+ /**
1850
+ * Update or remove the footer for an existing video session by cloning it into a new session and
1851
+ * re-running frame/video generation.
1852
+ */
1853
+ updateVideoFooterImage(input: UpdateVideoFooterImageInput, options?: {
1854
+ webhookUrl?: string;
1855
+ } & SamsarRequestOptions): Promise<SamsarResult<UpdateVideoFooterImageResponse>>;
1856
+ /**
1857
+ * Update or remove the footer for an external-user video request through
1858
+ * /external_users/update_footer_image.
1859
+ */
1860
+ updateExternalVideoFooterImage(externalUser: ExternalUserIdentity, input: UpdateVideoFooterImageInput, options?: {
1861
+ webhookUrl?: string;
1862
+ } & SamsarRequestOptions): Promise<SamsarResult<ExternalRequestResponse>>;
1800
1863
  /**
1801
1864
  * Add or replace the outro image for an existing video session by cloning it into a new session and
1802
1865
  * re-running frame/video generation.
package/dist/index.js CHANGED
@@ -126,6 +126,50 @@ function normalizeCreateVideoFromImageListInput(input) {
126
126
  }
127
127
  return normalized;
128
128
  }
129
+ function normalizeTranslateVideoInput(input, context = 'translateVideo') {
130
+ const raw = input;
131
+ const videoSessionId = raw.videoSessionId ??
132
+ raw.video_session_id ??
133
+ raw.videoSessionID ??
134
+ raw.session_id ??
135
+ raw.sessionId ??
136
+ raw.sessionID ??
137
+ raw.request_id ??
138
+ raw.requestId;
139
+ const language = raw.language ??
140
+ raw.language_code ??
141
+ raw.languageCode ??
142
+ raw.langauge ??
143
+ raw.langauge_code ??
144
+ raw.langaugeCode ??
145
+ raw.languageString;
146
+ const enableSubtitles = resolveAliasedInputValue(raw, ['enable_subtitles', 'enableSubtitles', 'add_subtitles', 'addSubtitles'], 'enable_subtitles') ?? false;
147
+ const translateOutro = resolveAliasedInputValue(raw, ['translate_outro', 'translateOutro'], 'translate_outro') ?? true;
148
+ const translateFooter = resolveAliasedInputValue(raw, ['translate_footer', 'translateFooter'], 'translate_footer') ?? true;
149
+ if (!videoSessionId) {
150
+ throw new Error(`videoSessionId is required for ${context}`);
151
+ }
152
+ if (!language) {
153
+ throw new Error(`language is required for ${context}`);
154
+ }
155
+ assertOptionalBoolean(enableSubtitles, 'enable_subtitles', context);
156
+ assertOptionalBoolean(translateOutro, 'translate_outro', context);
157
+ assertOptionalBoolean(translateFooter, 'translate_footer', context);
158
+ const normalizedInput = { ...input };
159
+ delete normalizedInput.outro_image_url;
160
+ delete normalizedInput.outroImageUrl;
161
+ delete normalizedInput.outroImageURL;
162
+ delete normalizedInput.new_outro_image_url;
163
+ delete normalizedInput.newOutroImageUrl;
164
+ return {
165
+ ...normalizedInput,
166
+ videoSessionId: String(videoSessionId),
167
+ language: String(language),
168
+ enable_subtitles: enableSubtitles,
169
+ translate_outro: translateOutro,
170
+ translate_footer: translateFooter,
171
+ };
172
+ }
129
173
  function normalizeUpdateVideoOutroImageInput(input, context = 'updateVideoOutroImage') {
130
174
  const raw = input;
131
175
  const videoSessionId = raw.videoSessionId ??
@@ -216,6 +260,61 @@ function normalizeUpdateVideoOutroImageInput(input, context = 'updateVideoOutroI
216
260
  ...(rawOutroFocusArea !== undefined ? { outro_focust_area: rawOutroFocusArea } : {}),
217
261
  };
218
262
  }
263
+ function normalizeUpdateVideoFooterImageInput(input, context = 'updateVideoFooterImage') {
264
+ const raw = input;
265
+ const videoSessionId = raw.videoSessionId ??
266
+ raw.video_session_id ??
267
+ raw.videoSessionID ??
268
+ raw.session_id ??
269
+ raw.sessionId ??
270
+ raw.sessionID ??
271
+ raw.request_id ??
272
+ raw.requestId ??
273
+ raw.source_request_id ??
274
+ raw.sourceRequestId ??
275
+ raw.external_request_id ??
276
+ raw.externalRequestId ??
277
+ raw.external_session_id ??
278
+ raw.externalSessionId;
279
+ const rawRemoveFooter = raw.remove_footer ??
280
+ raw.removeFooter;
281
+ const ctaText = raw.cta_text ??
282
+ raw.ctaText;
283
+ const ctaLogo = raw.cta_logo ??
284
+ raw.ctaLogo;
285
+ const ctaUrl = raw.cta_url ??
286
+ raw.ctaUrl;
287
+ const removeFooter = rawRemoveFooter === true;
288
+ if (!videoSessionId) {
289
+ throw new Error(`videoSessionId is required for ${context}`);
290
+ }
291
+ if (rawRemoveFooter !== undefined && typeof rawRemoveFooter !== 'boolean') {
292
+ throw new Error(`remove_footer must be a boolean for ${context}`);
293
+ }
294
+ if (ctaText !== undefined && typeof ctaText !== 'string') {
295
+ throw new Error(`cta_text must be a string for ${context}`);
296
+ }
297
+ if (ctaLogo !== undefined && typeof ctaLogo !== 'string') {
298
+ throw new Error(`cta_logo must be a string for ${context}`);
299
+ }
300
+ if (ctaUrl !== undefined && typeof ctaUrl !== 'string') {
301
+ throw new Error(`cta_url must be a string for ${context}`);
302
+ }
303
+ const normalizedCtaText = typeof ctaText === 'string' ? ctaText.trim() : '';
304
+ const normalizedCtaLogo = typeof ctaLogo === 'string' ? ctaLogo.trim() : '';
305
+ const normalizedCtaUrl = typeof ctaUrl === 'string' ? ctaUrl.trim() : '';
306
+ if (!removeFooter && !normalizedCtaText && !normalizedCtaLogo && !normalizedCtaUrl) {
307
+ throw new Error(`At least one of cta_text, cta_logo, or cta_url is required for ${context} unless remove_footer is true`);
308
+ }
309
+ return {
310
+ ...input,
311
+ videoSessionId: String(videoSessionId),
312
+ remove_footer: removeFooter,
313
+ ...(normalizedCtaText ? { cta_text: normalizedCtaText } : {}),
314
+ ...(normalizedCtaLogo ? { cta_logo: normalizedCtaLogo } : {}),
315
+ ...(normalizedCtaUrl ? { cta_url: normalizedCtaUrl } : {}),
316
+ };
317
+ }
219
318
  function normalizeSessionPublicationInput(input, context) {
220
319
  if (typeof input !== 'string' && (!input || typeof input !== 'object' || Array.isArray(input))) {
221
320
  throw new Error(`input must be a session id or object for ${context}`);
@@ -435,6 +534,13 @@ export class SamsarClient {
435
534
  webhookUrl: options?.webhookUrl,
436
535
  }, options);
437
536
  }
537
+ async translateV2Video(input, options) {
538
+ const normalizedInput = normalizeTranslateVideoInput(input, 'translateV2Video');
539
+ return this.postV2('translate_video', {
540
+ input: normalizedInput,
541
+ webhookUrl: options?.webhookUrl,
542
+ }, options);
543
+ }
438
544
  async uploadV2ImageData(imageData, options) {
439
545
  if (!Array.isArray(imageData) || imageData.length === 0) {
440
546
  throw new Error('imageData must be a non-empty array of data URLs');
@@ -452,6 +558,13 @@ export class SamsarClient {
452
558
  webhookUrl: options?.webhookUrl,
453
559
  }, options);
454
560
  }
561
+ async updateV2VideoFooterImage(input, options) {
562
+ const normalizedInput = normalizeUpdateVideoFooterImageInput(input, 'updateV2VideoFooterImage');
563
+ return this.postV2('update_footer_image', {
564
+ input: normalizedInput,
565
+ webhookUrl: options?.webhookUrl,
566
+ }, options);
567
+ }
455
568
  async addV2VideoOutroImage(input, options) {
456
569
  return this.postV2('add_outro_image', {
457
570
  input,
@@ -641,33 +754,10 @@ export class SamsarClient {
641
754
  * Creates a new session_id and queues generation steps for lip sync + transcription + video render.
642
755
  */
643
756
  async translateVideo(input, options) {
644
- const raw = input;
645
- const videoSessionId = raw.videoSessionId ??
646
- raw.video_session_id ??
647
- raw.videoSessionID ??
648
- raw.session_id ??
649
- raw.sessionId ??
650
- raw.sessionID ??
651
- raw.request_id ??
652
- raw.requestId;
653
- const language = raw.language ??
654
- raw.language_code ??
655
- raw.languageCode ??
656
- raw.langauge ??
657
- raw.langauge_code ??
658
- raw.langaugeCode ??
659
- raw.languageString;
660
- if (!videoSessionId) {
661
- throw new Error('videoSessionId is required for translateVideo');
662
- }
663
- if (!language) {
664
- throw new Error('language is required for translateVideo');
665
- }
757
+ const normalizedInput = normalizeTranslateVideoInput(input, 'translateVideo');
666
758
  const body = {
667
759
  input: {
668
- ...input,
669
- videoSessionId: String(videoSessionId),
670
- language: String(language),
760
+ ...normalizedInput,
671
761
  },
672
762
  webhookUrl: options?.webhookUrl,
673
763
  };
@@ -923,6 +1013,50 @@ export class SamsarClient {
923
1013
  };
924
1014
  return this.post('external_users/update_outro_image', body, options);
925
1015
  }
1016
+ /**
1017
+ * Update or remove the footer for an existing video session by cloning it into a new session and
1018
+ * re-running frame/video generation.
1019
+ */
1020
+ async updateVideoFooterImage(input, options) {
1021
+ const normalizedInput = normalizeUpdateVideoFooterImageInput(input, 'updateVideoFooterImage');
1022
+ const body = {
1023
+ input: normalizedInput,
1024
+ webhookUrl: options?.webhookUrl,
1025
+ };
1026
+ const response = await this.post('video/update_footer_image', body, options);
1027
+ const data = response.data;
1028
+ if (data && typeof data === 'object') {
1029
+ const sessionId = typeof data.sessionID === 'string'
1030
+ ? data.sessionID
1031
+ : typeof data.session_id === 'string'
1032
+ ? data.session_id
1033
+ : typeof data.request_id === 'string'
1034
+ ? data.request_id
1035
+ : undefined;
1036
+ const normalizedSessionId = sessionId ? String(sessionId) : undefined;
1037
+ const normalizedData = {
1038
+ ...data,
1039
+ sessionID: data.sessionID ?? normalizedSessionId ?? '',
1040
+ session_id: data.session_id ?? normalizedSessionId,
1041
+ request_id: data.request_id ?? normalizedSessionId,
1042
+ };
1043
+ return { ...response, data: normalizedData };
1044
+ }
1045
+ return response;
1046
+ }
1047
+ /**
1048
+ * Update or remove the footer for an external-user video request through
1049
+ * /external_users/update_footer_image.
1050
+ */
1051
+ async updateExternalVideoFooterImage(externalUser, input, options) {
1052
+ const normalizedInput = normalizeUpdateVideoFooterImageInput(input, 'updateExternalVideoFooterImage');
1053
+ const body = {
1054
+ external_user: normalizeExternalUserIdentity(externalUser),
1055
+ input: normalizedInput,
1056
+ webhookUrl: options?.webhookUrl,
1057
+ };
1058
+ return this.post('external_users/update_footer_image', body, options);
1059
+ }
926
1060
  /**
927
1061
  * Add or replace the outro image for an existing video session by cloning it into a new session and
928
1062
  * re-running frame/video generation.
@@ -1755,7 +1889,10 @@ export class SamsarClient {
1755
1889
  body: parsedBody,
1756
1890
  });
1757
1891
  }
1758
- throw new SamsarRequestError(`Request to ${url} failed with status ${response.status}`, {
1892
+ const responseMessage = getResponseErrorMessage(parsedBody);
1893
+ throw new SamsarRequestError(responseMessage
1894
+ ? `Request to ${url} failed with status ${response.status}: ${responseMessage}`
1895
+ : `Request to ${url} failed with status ${response.status}`, {
1759
1896
  status: response.status,
1760
1897
  body: parsedBody,
1761
1898
  headers: headerRecord,
@@ -1837,6 +1974,25 @@ function parseMaybeJson(value) {
1837
1974
  return value;
1838
1975
  }
1839
1976
  }
1977
+ function getResponseErrorMessage(body) {
1978
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
1979
+ return typeof body === 'string' && body.trim() ? body.trim() : null;
1980
+ }
1981
+ const record = body;
1982
+ for (const key of ['message', 'error', 'detail']) {
1983
+ const value = record[key];
1984
+ if (typeof value === 'string' && value.trim()) {
1985
+ return value.trim();
1986
+ }
1987
+ }
1988
+ if (record.error && typeof record.error === 'object' && !Array.isArray(record.error)) {
1989
+ const nestedMessage = record.error.message;
1990
+ if (typeof nestedMessage === 'string' && nestedMessage.trim()) {
1991
+ return nestedMessage.trim();
1992
+ }
1993
+ }
1994
+ return null;
1995
+ }
1840
1996
  function headersToObject(headers) {
1841
1997
  const result = {};
1842
1998
  headers.forEach((value, key) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "samsar-js",
3
- "version": "0.48.23",
3
+ "version": "0.48.25",
4
4
  "description": "TypeScript client for the Samsar Processor API routes.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",