samsar-js 0.48.19 → 0.48.21

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
@@ -512,6 +512,7 @@ Video model support notes:
512
512
  Upcoming `/v2` omni route adapters:
513
513
  - `/v2` is additive; `/v1` is not deprecated.
514
514
  - `createV2VideoFromText`, `createV2VideoFromImageList`, `updateV2VideoOutroImage`, `addV2VideoOutroImage`, `getV2Status`, `getV2Credits`, `listV2Requests`, and `createV2Session` call the new omni route surface.
515
+ - Programmatic user billing helpers include `createV2UserRechargeCredits`, `refreshV2UserToken`, `createV2UserAppKey`, `refreshV2UserAppKey`, `getV2UserCredits`, `getV2UserUsageLogs`, and `getV2UserPaymentStatus`.
515
516
  - 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.
516
517
 
517
518
  ```ts
@@ -535,6 +536,41 @@ const v2Status = await platform.getV2Status(v2Video.data.request_id!);
535
536
  console.log(v2Status.data.status);
536
537
  ```
537
538
 
539
+ Programmatic user recharge and OAuth-style refresh token rotation:
540
+
541
+ ```ts
542
+ const publicClient = new SamsarClient({});
543
+
544
+ const checkout = await publicClient.createV2UserRechargeCredits({
545
+ amount: 25,
546
+ email: 'customer@example.com',
547
+ redirect_url: 'https://example.com/samsar/callback',
548
+ });
549
+
550
+ // After the Stripe webhook calls your redirect_url with authToken, refreshToken, and expiryDate:
551
+ const userClient = new SamsarClient({ apiKey: authToken });
552
+ const refreshed = await userClient.refreshV2UserToken(refreshToken);
553
+
554
+ localStorage.setItem('authToken', refreshed.data.authToken);
555
+ localStorage.setItem('refreshToken', refreshed.data.refreshToken);
556
+ localStorage.setItem('expiryDate', refreshed.data.expiryDate);
557
+ ```
558
+
559
+ Long-running app credentials:
560
+
561
+ ```ts
562
+ const secret = crypto.randomUUID() + crypto.randomUUID();
563
+ const created = await userClient.createV2UserAppKey({ secret });
564
+
565
+ const appClient = new SamsarClient({
566
+ appKey: created.data.appKey ?? created.data.app_key,
567
+ appSecret: secret,
568
+ });
569
+
570
+ const credits = await appClient.getV2UserCredits();
571
+ const rotated = await appClient.refreshV2UserAppKey();
572
+ ```
573
+
538
574
  Each method returns `{ data, status, headers, creditsCharged, creditsRemaining, raw }`. Non-2xx responses throw `SamsarRequestError` containing status, body, and credit headers (if present).
539
575
 
540
576
  ## Billing notes
package/dist/index.d.ts CHANGED
@@ -2,7 +2,9 @@ type FetchLike = (input: RequestInfo | URL, init?: RequestInit) => Promise<Respo
2
2
  type QueryValue = string | number | boolean | null | undefined;
3
3
  type QueryParams = Record<string, QueryValue>;
4
4
  export interface SamsarClientOptions {
5
- apiKey: string;
5
+ apiKey?: string;
6
+ appKey?: string;
7
+ appSecret?: string;
6
8
  baseUrl?: string;
7
9
  timeoutMs?: number;
8
10
  fetch?: FetchLike;
@@ -13,6 +15,8 @@ export interface SamsarRequestOptions {
13
15
  idempotencyKey?: string;
14
16
  headers?: Record<string, string>;
15
17
  externalUserApiKey?: string;
18
+ appKey?: string;
19
+ appSecret?: string;
16
20
  signal?: AbortSignal;
17
21
  query?: QueryParams;
18
22
  }
@@ -1334,6 +1338,105 @@ export interface V2RequestsListResponse {
1334
1338
  externalUser?: ExternalUserSummary | null;
1335
1339
  [key: string]: unknown;
1336
1340
  }
1341
+ export interface V2UserRechargeCreditsRequest {
1342
+ amount: number;
1343
+ email: string;
1344
+ redirect_url?: string;
1345
+ redirectUrl?: string;
1346
+ [key: string]: unknown;
1347
+ }
1348
+ export interface V2UserRechargeCreditsResponse {
1349
+ url: string;
1350
+ checkoutSessionId?: string | null;
1351
+ paymentStatusEndpoint?: string;
1352
+ amount: number;
1353
+ amountCents: number;
1354
+ credits: number;
1355
+ currency: string;
1356
+ redirectUrl?: string;
1357
+ [key: string]: unknown;
1358
+ }
1359
+ export interface V2UserTokenRefreshRequest {
1360
+ refreshToken?: string;
1361
+ refresh_token?: string;
1362
+ }
1363
+ export interface V2UserTokenResponse {
1364
+ tokenType?: 'Bearer' | string;
1365
+ authToken: string;
1366
+ refreshToken: string;
1367
+ expiryDate: string;
1368
+ expiresInSeconds?: number;
1369
+ refreshTokenExpiresAt?: string;
1370
+ [key: string]: unknown;
1371
+ }
1372
+ export interface V2UserAppKeyRequest {
1373
+ secret?: string;
1374
+ appSecret?: string;
1375
+ app_secret?: string;
1376
+ metadata?: Record<string, unknown>;
1377
+ [key: string]: unknown;
1378
+ }
1379
+ export interface V2UserAppKeyRefreshRequest {
1380
+ appKey?: string;
1381
+ app_key?: string;
1382
+ secret?: string;
1383
+ appSecret?: string;
1384
+ app_secret?: string;
1385
+ [key: string]: unknown;
1386
+ }
1387
+ export interface V2UserAppKeyRecord {
1388
+ id?: string;
1389
+ userId?: string;
1390
+ appKeyPrefix?: string | null;
1391
+ appKeyLast4?: string | null;
1392
+ status?: 'active' | 'revoked' | string;
1393
+ expiresAt?: string | null;
1394
+ lastUsedAt?: string | null;
1395
+ refreshedAt?: string | null;
1396
+ revokedAt?: string | null;
1397
+ rotationCount?: number;
1398
+ createdAt?: string | null;
1399
+ updatedAt?: string | null;
1400
+ authScheme?: string;
1401
+ authHeader?: string;
1402
+ secretHeader?: string;
1403
+ [key: string]: unknown;
1404
+ }
1405
+ export interface V2UserAppKeyResponse {
1406
+ app_key?: string;
1407
+ appKey?: string;
1408
+ token_type?: 'AppKey' | string;
1409
+ tokenType?: 'AppKey' | string;
1410
+ expires_at?: string;
1411
+ expiresAt?: string;
1412
+ app_key_record?: V2UserAppKeyRecord;
1413
+ appKeyRecord?: V2UserAppKeyRecord;
1414
+ [key: string]: unknown;
1415
+ }
1416
+ export interface UsageLogItem {
1417
+ id?: string;
1418
+ source?: string;
1419
+ credits?: number;
1420
+ balanceAfter?: number | null;
1421
+ metadata?: Record<string, unknown>;
1422
+ direction?: string;
1423
+ createdAt?: string;
1424
+ updatedAt?: string;
1425
+ [key: string]: unknown;
1426
+ }
1427
+ export interface UsageLogsResponse {
1428
+ items: UsageLogItem[];
1429
+ pagination?: {
1430
+ page?: number;
1431
+ pageSize?: number;
1432
+ totalItems?: number;
1433
+ totalPages?: number;
1434
+ hasNextPage?: boolean;
1435
+ hasPreviousPage?: boolean;
1436
+ [key: string]: unknown;
1437
+ };
1438
+ [key: string]: unknown;
1439
+ }
1337
1440
  export interface ExternalArchiveResponse {
1338
1441
  request?: ExternalRequestSummary | null;
1339
1442
  external_user?: ExternalUserSummary | null;
@@ -1516,7 +1619,9 @@ export declare class SamsarRequestError extends Error {
1516
1619
  constructor(message: string, init: SamsarErrorInit);
1517
1620
  }
1518
1621
  export declare class SamsarClient {
1519
- private readonly apiKey;
1622
+ private readonly apiKey?;
1623
+ private readonly appKey?;
1624
+ private readonly appSecret?;
1520
1625
  private readonly baseUrl;
1521
1626
  private readonly timeoutMs;
1522
1627
  private readonly fetchFn;
@@ -1535,7 +1640,21 @@ export declare class SamsarClient {
1535
1640
  getV2<T = Record<string, unknown>>(path: string, options?: V2RequestOptions): Promise<SamsarResult<T>>;
1536
1641
  createV2Session(options?: V2RequestOptions): Promise<SamsarResult<V2SessionResponse>>;
1537
1642
  getV2Credits(options?: V2RequestOptions): Promise<SamsarResult<CreditsBalanceResponse | ExternalCreditsBalanceResponse>>;
1643
+ getV2UserCredits(options?: V2RequestOptions): Promise<SamsarResult<CreditsBalanceResponse>>;
1644
+ getV2UserUsageLogs(options?: V2RequestOptions & {
1645
+ page?: number;
1646
+ pageSize?: number;
1647
+ limit?: number;
1648
+ }): Promise<SamsarResult<UsageLogsResponse>>;
1538
1649
  listV2Requests(options?: V2RequestOptions): Promise<SamsarResult<V2RequestsListResponse>>;
1650
+ createV2UserRechargeCredits(payload: V2UserRechargeCreditsRequest, options?: V2RequestOptions): Promise<SamsarResult<V2UserRechargeCreditsResponse>>;
1651
+ refreshV2UserToken(payload: string | V2UserTokenRefreshRequest, options?: V2RequestOptions): Promise<SamsarResult<V2UserTokenResponse>>;
1652
+ refreshV2UserAuthToken(payload: string | V2UserTokenRefreshRequest, options?: V2RequestOptions): Promise<SamsarResult<V2UserTokenResponse>>;
1653
+ createV2UserAppKey(payload: string | V2UserAppKeyRequest, options?: V2RequestOptions): Promise<SamsarResult<V2UserAppKeyResponse>>;
1654
+ getV2UserAppKey(options?: V2RequestOptions): Promise<SamsarResult<V2UserAppKeyResponse>>;
1655
+ refreshV2UserAppKey(payload?: V2UserAppKeyRefreshRequest, options?: V2RequestOptions): Promise<SamsarResult<V2UserAppKeyResponse>>;
1656
+ revokeV2UserAppKey(options?: V2RequestOptions): Promise<SamsarResult<V2UserAppKeyResponse>>;
1657
+ getV2UserPaymentStatus(payload: PaymentStatusRequest, options?: V2RequestOptions): Promise<SamsarResult<PaymentStatusResponse>>;
1539
1658
  createV2LoginToken(options?: V2RequestOptions & {
1540
1659
  redirect?: string;
1541
1660
  }): Promise<SamsarResult<CreateLoginTokenResponse | ExternalCreateLoginTokenResponse>>;
package/dist/index.js CHANGED
@@ -270,10 +270,9 @@ function normalizeSessionPublicationInput(input, context) {
270
270
  }
271
271
  export class SamsarClient {
272
272
  constructor(options) {
273
- if (!options?.apiKey) {
274
- throw new Error('apiKey is required to initialize SamsarClient');
275
- }
276
- this.apiKey = options.apiKey;
273
+ this.apiKey = options?.apiKey?.trim() || undefined;
274
+ this.appKey = options?.appKey?.trim() || undefined;
275
+ this.appSecret = options?.appSecret?.trim() || undefined;
277
276
  this.baseUrl = trimTrailingSlash(options.baseUrl ?? DEFAULT_BASE_URL);
278
277
  this.timeoutMs = options.timeoutMs ?? 30000;
279
278
  this.fetchFn = options.fetch ?? globalThis.fetch;
@@ -307,9 +306,117 @@ export class SamsarClient {
307
306
  async getV2Credits(options) {
308
307
  return this.getV2('credits', options);
309
308
  }
309
+ async getV2UserCredits(options) {
310
+ return this.getV2('user/credits', options);
311
+ }
312
+ async getV2UserUsageLogs(options) {
313
+ const query = {
314
+ ...(options?.query ?? {}),
315
+ };
316
+ if (options?.page !== undefined) {
317
+ query.page = options.page;
318
+ }
319
+ if (options?.pageSize !== undefined) {
320
+ query.pageSize = options.pageSize;
321
+ }
322
+ if (options?.limit !== undefined) {
323
+ query.limit = options.limit;
324
+ }
325
+ return this.getV2('user/usage/logs', {
326
+ ...(options ?? {}),
327
+ query,
328
+ });
329
+ }
310
330
  async listV2Requests(options) {
311
331
  return this.getV2('requests', options);
312
332
  }
333
+ async createV2UserRechargeCredits(payload, options) {
334
+ const amount = Number(payload?.amount);
335
+ if (!Number.isFinite(amount) || amount <= 0) {
336
+ throw new Error('amount must be a positive dollar amount');
337
+ }
338
+ if (!payload?.email || typeof payload.email !== 'string') {
339
+ throw new Error('email is required');
340
+ }
341
+ const redirectUrl = payload.redirect_url ?? payload.redirectUrl;
342
+ if (!redirectUrl || typeof redirectUrl !== 'string') {
343
+ throw new Error('redirect_url is required');
344
+ }
345
+ return this.postV2('user/recharge_credits', {
346
+ ...payload,
347
+ amount,
348
+ email: payload.email.trim(),
349
+ redirect_url: redirectUrl.trim(),
350
+ }, options);
351
+ }
352
+ async refreshV2UserToken(payload, options) {
353
+ const refreshToken = typeof payload === 'string'
354
+ ? payload
355
+ : (payload?.refreshToken ?? payload?.refresh_token);
356
+ if (!refreshToken || typeof refreshToken !== 'string') {
357
+ throw new Error('refreshToken is required');
358
+ }
359
+ return this.postV2('user/refresh_token', { refreshToken: refreshToken.trim() }, options);
360
+ }
361
+ async refreshV2UserAuthToken(payload, options) {
362
+ return this.refreshV2UserToken(payload, options);
363
+ }
364
+ async createV2UserAppKey(payload, options) {
365
+ const input = typeof payload === 'string' ? { secret: payload } : (payload ?? {});
366
+ const secret = input.secret ?? input.appSecret ?? input.app_secret;
367
+ if (!secret || typeof secret !== 'string') {
368
+ throw new Error('secret is required');
369
+ }
370
+ if (secret.trim().length < 32) {
371
+ throw new Error('secret must be at least 32 characters');
372
+ }
373
+ return this.postV2('users/app_key', {
374
+ ...input,
375
+ secret: secret.trim(),
376
+ }, options);
377
+ }
378
+ async getV2UserAppKey(options) {
379
+ return this.getV2('users/app_key', options);
380
+ }
381
+ async refreshV2UserAppKey(payload, options) {
382
+ const input = payload ?? {};
383
+ const appKey = input.appKey ?? input.app_key ?? options?.appKey ?? this.appKey;
384
+ const secret = input.secret ?? input.appSecret ?? input.app_secret ?? options?.appSecret ?? this.appSecret;
385
+ if (!appKey || typeof appKey !== 'string') {
386
+ throw new Error('appKey is required');
387
+ }
388
+ if (!secret || typeof secret !== 'string') {
389
+ throw new Error('secret is required');
390
+ }
391
+ if (secret.trim().length < 32) {
392
+ throw new Error('secret must be at least 32 characters');
393
+ }
394
+ return this.postV2('users/app_key/refresh', {
395
+ app_key: appKey.trim(),
396
+ secret: secret.trim(),
397
+ }, options);
398
+ }
399
+ async revokeV2UserAppKey(options) {
400
+ return this.request(this.buildV2Url('users/app_key'), { ...(options ?? {}), method: 'DELETE' });
401
+ }
402
+ async getV2UserPaymentStatus(payload, options) {
403
+ const query = {
404
+ ...(options?.query ?? {}),
405
+ };
406
+ if (payload?.checkoutSessionId) {
407
+ query.checkoutSessionId = payload.checkoutSessionId;
408
+ }
409
+ if (payload?.paymentIntentId) {
410
+ query.paymentIntentId = payload.paymentIntentId;
411
+ }
412
+ if (payload?.setupIntentId) {
413
+ query.setupIntentId = payload.setupIntentId;
414
+ }
415
+ return this.getV2('user/payment_status', {
416
+ ...(options ?? {}),
417
+ query,
418
+ });
419
+ }
313
420
  async createV2LoginToken(options) {
314
421
  const body = options?.redirect ? { redirect: options.redirect } : {};
315
422
  return this.postV2('create_login_token', body, options);
@@ -1619,10 +1726,17 @@ export class SamsarClient {
1619
1726
  };
1620
1727
  }
1621
1728
  buildHeaders(options) {
1729
+ const resolvedAppKey = options.appKey?.trim() || (!this.apiKey ? this.appKey : undefined);
1730
+ const resolvedAppSecret = options.appSecret?.trim() || this.appSecret;
1622
1731
  const headers = {
1623
- Authorization: `Bearer ${this.apiKey}`,
1732
+ Authorization: resolvedAppKey
1733
+ ? `AppKey ${resolvedAppKey}`
1734
+ : this.apiKey
1735
+ ? `Bearer ${this.apiKey}`
1736
+ : undefined,
1624
1737
  'Content-Type': options.body ? 'application/json' : undefined,
1625
1738
  'x-external-user-api-key': options.externalUserApiKey ?? this.externalUserApiKey,
1739
+ 'x-app-secret': resolvedAppKey ? resolvedAppSecret : undefined,
1626
1740
  ...this.defaultHeaders,
1627
1741
  ...(options.headers ?? {}),
1628
1742
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "samsar-js",
3
- "version": "0.48.19",
3
+ "version": "0.48.21",
4
4
  "description": "TypeScript client for the Samsar Processor API routes.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",