perspectapi-ts-sdk 2.7.0 → 2.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -36,6 +36,7 @@ __export(index_exports, {
36
36
  OrganizationsClient: () => OrganizationsClient,
37
37
  PerspectApiClient: () => PerspectApiClient,
38
38
  ProductsClient: () => ProductsClient,
39
+ SiteUsersClient: () => SiteUsersClient,
39
40
  SitesClient: () => SitesClient,
40
41
  WebhooksClient: () => WebhooksClient,
41
42
  buildImageUrl: () => buildImageUrl,
@@ -768,7 +769,7 @@ var AuthClient = class extends BaseClient {
768
769
 
769
770
  // src/utils/validators.ts
770
771
  var MAX_API_QUERY_LIMIT = 100;
771
- var ALLOWED_CONTENT_TYPES = ["post", "page"];
772
+ var ALLOWED_CONTENT_TYPES = ["post", "page", "block"];
772
773
  function validateLimit(limit, context) {
773
774
  if (typeof limit !== "number" || Number.isNaN(limit) || !Number.isFinite(limit)) {
774
775
  throw new Error(`[PerspectAPI] ${context} limit must be a finite number.`);
@@ -1522,6 +1523,64 @@ var ProductsClient = class extends BaseClient {
1522
1523
  }
1523
1524
  return void 0;
1524
1525
  }
1526
+ // ============================================================================
1527
+ // PRODUCT OPTIONS AND SKUS (for variants)
1528
+ // ============================================================================
1529
+ /**
1530
+ * Get all options (and their values) for a product
1531
+ */
1532
+ async getProductOptions(siteName, productId) {
1533
+ const endpoint = this.siteScopedEndpoint(
1534
+ siteName,
1535
+ `/products/${productId}/options`,
1536
+ { includeSitesSegment: false }
1537
+ );
1538
+ return this.getSingle(endpoint);
1539
+ }
1540
+ /**
1541
+ * Create a new product option (e.g., "Size", "Color")
1542
+ */
1543
+ async createProductOption(siteName, productId, data) {
1544
+ const endpoint = this.siteScopedEndpoint(
1545
+ siteName,
1546
+ `/products/${productId}/options`,
1547
+ { includeSitesSegment: false }
1548
+ );
1549
+ return this.create(endpoint, data);
1550
+ }
1551
+ /**
1552
+ * Create a new value for a product option (e.g., "Small", "Red")
1553
+ */
1554
+ async createProductOptionValue(siteName, productId, optionId, data) {
1555
+ const endpoint = this.siteScopedEndpoint(
1556
+ siteName,
1557
+ `/products/${productId}/options/${optionId}/values`,
1558
+ { includeSitesSegment: false }
1559
+ );
1560
+ return this.create(endpoint, data);
1561
+ }
1562
+ /**
1563
+ * Get all SKUs for a product (with their option value combinations)
1564
+ */
1565
+ async getProductSkus(siteName, productId) {
1566
+ const endpoint = this.siteScopedEndpoint(
1567
+ siteName,
1568
+ `/products/${productId}/skus`,
1569
+ { includeSitesSegment: false }
1570
+ );
1571
+ return this.getSingle(endpoint);
1572
+ }
1573
+ /**
1574
+ * Create or update a SKU for a product variant combination
1575
+ */
1576
+ async createProductSku(siteName, productId, data) {
1577
+ const endpoint = this.siteScopedEndpoint(
1578
+ siteName,
1579
+ `/products/${productId}/skus`,
1580
+ { includeSitesSegment: false }
1581
+ );
1582
+ return this.create(endpoint, data);
1583
+ }
1525
1584
  };
1526
1585
 
1527
1586
  // src/client/categories-client.ts
@@ -2236,6 +2295,271 @@ var NewsletterClient = class extends BaseClient {
2236
2295
  }
2237
2296
  };
2238
2297
 
2298
+ // src/client/site-users-client.ts
2299
+ var SiteUsersClient = class extends BaseClient {
2300
+ constructor(http, cache) {
2301
+ super(http, "/api/v1", cache);
2302
+ }
2303
+ /**
2304
+ * Build a site user endpoint scoped to a site (without /sites prefix)
2305
+ */
2306
+ siteUserEndpoint(siteName, endpoint) {
2307
+ return this.siteScopedEndpoint(siteName, endpoint, { includeSitesSegment: false });
2308
+ }
2309
+ // ============================================================================
2310
+ // PUBLIC ENDPOINTS (OTP authentication)
2311
+ // ============================================================================
2312
+ /**
2313
+ * Request OTP for login/signup
2314
+ * @param siteName - The site name
2315
+ * @param data - Email address
2316
+ * @param csrfToken - CSRF token (required for browser-based submissions)
2317
+ */
2318
+ async requestOtp(siteName, data, csrfToken) {
2319
+ if (typeof window !== "undefined" && !csrfToken) {
2320
+ console.warn("CSRF token recommended for browser-based OTP requests");
2321
+ }
2322
+ return this.create(
2323
+ this.siteUserEndpoint(siteName, "/users/request-otp"),
2324
+ data,
2325
+ csrfToken
2326
+ );
2327
+ }
2328
+ /**
2329
+ * Verify OTP and get JWT token
2330
+ *
2331
+ * For cross-domain authentication, you must manually set the token after verification:
2332
+ * ```typescript
2333
+ * const response = await client.siteUsers.verifyOtp('mysite', { email, code });
2334
+ * const { token, user } = response.data;
2335
+ *
2336
+ * // Store token securely (choose one):
2337
+ * // Option 1: Memory (lost on refresh, most secure)
2338
+ * client.setAuth(token);
2339
+ *
2340
+ * // Option 2: httpOnly cookie on YOUR domain (recommended for production)
2341
+ * await fetch('/your-api/set-auth-cookie', {
2342
+ * method: 'POST',
2343
+ * body: JSON.stringify({ token })
2344
+ * });
2345
+ * client.setAuth(token);
2346
+ *
2347
+ * // Option 3: localStorage (vulnerable to XSS, not recommended)
2348
+ * localStorage.setItem('site_user_token', token);
2349
+ * client.setAuth(token);
2350
+ * ```
2351
+ *
2352
+ * For convenience, use `verifyOtpAndSetAuth()` to automatically set the token in memory.
2353
+ *
2354
+ * @param siteName - The site name
2355
+ * @param data - Email and code
2356
+ * @param csrfToken - CSRF token (required for browser-based submissions)
2357
+ */
2358
+ async verifyOtp(siteName, data, csrfToken) {
2359
+ if (typeof window !== "undefined" && !csrfToken) {
2360
+ console.warn("CSRF token recommended for browser-based OTP verification");
2361
+ }
2362
+ return this.create(
2363
+ this.siteUserEndpoint(siteName, "/users/verify-otp"),
2364
+ data,
2365
+ csrfToken
2366
+ );
2367
+ }
2368
+ /**
2369
+ * Verify OTP and automatically set the token for subsequent requests
2370
+ *
2371
+ * Convenience method that:
2372
+ * 1. Verifies the OTP
2373
+ * 2. Automatically calls setAuth() with the returned token
2374
+ *
2375
+ * Note: Token is stored in memory only and will be lost on page refresh.
2376
+ * For persistent auth, use verifyOtp() and store the token yourself.
2377
+ *
2378
+ * @param siteName - The site name
2379
+ * @param data - Email and code
2380
+ * @param csrfToken - CSRF token (required for browser-based submissions)
2381
+ */
2382
+ async verifyOtpAndSetAuth(siteName, data, csrfToken) {
2383
+ const response = await this.verifyOtp(siteName, data, csrfToken);
2384
+ if (response.data?.token) {
2385
+ this.http.setAuth(response.data.token);
2386
+ }
2387
+ return response;
2388
+ }
2389
+ /**
2390
+ * Logout (clear session cookie)
2391
+ * @param siteName - The site name
2392
+ */
2393
+ async logout(siteName) {
2394
+ return this.create(
2395
+ this.siteUserEndpoint(siteName, "/users/logout"),
2396
+ {}
2397
+ );
2398
+ }
2399
+ // ============================================================================
2400
+ // AUTHENTICATED ENDPOINTS (site user JWT required)
2401
+ // ============================================================================
2402
+ /**
2403
+ * Get current user profile
2404
+ * @param siteName - The site name
2405
+ */
2406
+ async getMe(siteName) {
2407
+ return this.getSingle(
2408
+ this.siteUserEndpoint(siteName, "/users/me")
2409
+ );
2410
+ }
2411
+ /**
2412
+ * Update current user profile
2413
+ * @param siteName - The site name
2414
+ * @param data - Fields to update
2415
+ * @param csrfToken - CSRF token (required)
2416
+ */
2417
+ async updateMe(siteName, data, csrfToken) {
2418
+ return this.patch(
2419
+ this.siteUserEndpoint(siteName, "/users/me"),
2420
+ data,
2421
+ csrfToken
2422
+ );
2423
+ }
2424
+ /**
2425
+ * Get all profile key-values
2426
+ * @param siteName - The site name
2427
+ */
2428
+ async getProfile(siteName) {
2429
+ return this.getSingle(
2430
+ this.siteUserEndpoint(siteName, "/users/me/profile")
2431
+ );
2432
+ }
2433
+ /**
2434
+ * Set a profile key-value
2435
+ * @param siteName - The site name
2436
+ * @param key - Profile key (e.g., 'phone', 'whatsapp', 'address_shipping')
2437
+ * @param value - Profile value (string or JSON string)
2438
+ * @param csrfToken - CSRF token (required)
2439
+ */
2440
+ async setProfileValue(siteName, key, value, csrfToken) {
2441
+ return this.update(
2442
+ this.siteUserEndpoint(siteName, `/users/me/profile/${encodeURIComponent(key)}`),
2443
+ { value },
2444
+ csrfToken
2445
+ );
2446
+ }
2447
+ /**
2448
+ * Delete a profile key-value
2449
+ * @param siteName - The site name
2450
+ * @param key - Profile key to delete
2451
+ * @param csrfToken - CSRF token (required)
2452
+ */
2453
+ async deleteProfileValue(siteName, key, csrfToken) {
2454
+ return this.delete(
2455
+ this.siteUserEndpoint(siteName, `/users/me/profile/${encodeURIComponent(key)}`),
2456
+ csrfToken
2457
+ );
2458
+ }
2459
+ /**
2460
+ * Get transaction/order history
2461
+ * @param siteName - The site name
2462
+ * @param params - Pagination params
2463
+ */
2464
+ async getOrders(siteName, params) {
2465
+ return this.http.get(
2466
+ this.buildPath(this.siteUserEndpoint(siteName, "/users/me/orders")),
2467
+ params
2468
+ );
2469
+ }
2470
+ /**
2471
+ * Get single order detail
2472
+ * @param siteName - The site name
2473
+ * @param orderId - Order ID or session ID
2474
+ */
2475
+ async getOrder(siteName, orderId) {
2476
+ return this.getSingle(
2477
+ this.siteUserEndpoint(siteName, `/users/me/orders/${encodeURIComponent(orderId)}`)
2478
+ );
2479
+ }
2480
+ /**
2481
+ * Get payment subscriptions
2482
+ * @param siteName - The site name
2483
+ * @param params - Pagination params
2484
+ */
2485
+ async getSubscriptions(siteName, params) {
2486
+ return this.http.get(
2487
+ this.buildPath(this.siteUserEndpoint(siteName, "/users/me/subscriptions")),
2488
+ params
2489
+ );
2490
+ }
2491
+ /**
2492
+ * Get single subscription detail
2493
+ * @param siteName - The site name
2494
+ * @param id - Subscription ID
2495
+ */
2496
+ async getSubscription(siteName, id) {
2497
+ return this.getSingle(
2498
+ this.siteUserEndpoint(siteName, `/users/me/subscriptions/${encodeURIComponent(id)}`)
2499
+ );
2500
+ }
2501
+ /**
2502
+ * Cancel a subscription (marks for cancellation at period end)
2503
+ * @param siteName - The site name
2504
+ * @param id - Subscription ID
2505
+ * @param csrfToken - CSRF token (required)
2506
+ */
2507
+ async cancelSubscription(siteName, id, csrfToken) {
2508
+ return this.create(
2509
+ this.siteUserEndpoint(siteName, `/users/me/subscriptions/${encodeURIComponent(id)}/cancel`),
2510
+ {},
2511
+ csrfToken
2512
+ );
2513
+ }
2514
+ /**
2515
+ * Get linked newsletter subscriptions
2516
+ * @param siteName - The site name
2517
+ */
2518
+ async getNewsletterSubscriptions(siteName) {
2519
+ return this.getSingle(
2520
+ this.siteUserEndpoint(siteName, "/users/me/newsletters")
2521
+ );
2522
+ }
2523
+ // ============================================================================
2524
+ // ADMIN ENDPOINTS (API key auth required)
2525
+ // ============================================================================
2526
+ /**
2527
+ * List all site users (admin only)
2528
+ * @param siteName - The site name
2529
+ * @param params - Query params (limit, offset, status)
2530
+ */
2531
+ async listUsers(siteName, params) {
2532
+ return this.http.get(
2533
+ this.buildPath(this.siteUserEndpoint(siteName, "/users")),
2534
+ params
2535
+ );
2536
+ }
2537
+ /**
2538
+ * Get user detail (admin only)
2539
+ * @param siteName - The site name
2540
+ * @param userId - User ID
2541
+ */
2542
+ async getUser(siteName, userId) {
2543
+ return this.getSingle(
2544
+ this.siteUserEndpoint(siteName, `/users/${encodeURIComponent(userId)}`)
2545
+ );
2546
+ }
2547
+ /**
2548
+ * Update user status (admin only)
2549
+ * @param siteName - The site name
2550
+ * @param userId - User ID
2551
+ * @param status - New status
2552
+ * @param csrfToken - CSRF token (required)
2553
+ */
2554
+ async updateUserStatus(siteName, userId, status, csrfToken) {
2555
+ return this.patch(
2556
+ this.siteUserEndpoint(siteName, `/users/${encodeURIComponent(userId)}/status`),
2557
+ { status },
2558
+ csrfToken
2559
+ );
2560
+ }
2561
+ };
2562
+
2239
2563
  // src/perspect-api-client.ts
2240
2564
  var PerspectApiClient = class {
2241
2565
  http;
@@ -2252,6 +2576,7 @@ var PerspectApiClient = class {
2252
2576
  checkout;
2253
2577
  contact;
2254
2578
  newsletter;
2579
+ siteUsers;
2255
2580
  constructor(config) {
2256
2581
  if (!config.baseUrl) {
2257
2582
  throw new Error("baseUrl is required in PerspectApiConfig");
@@ -2269,6 +2594,7 @@ var PerspectApiClient = class {
2269
2594
  this.checkout = new CheckoutClient(this.http, this.cache);
2270
2595
  this.contact = new ContactClient(this.http, this.cache);
2271
2596
  this.newsletter = new NewsletterClient(this.http, this.cache);
2597
+ this.siteUsers = new SiteUsersClient(this.http, this.cache);
2272
2598
  }
2273
2599
  /**
2274
2600
  * Update authentication token
@@ -2958,6 +3284,7 @@ async function createCheckoutSession(options) {
2958
3284
  OrganizationsClient,
2959
3285
  PerspectApiClient,
2960
3286
  ProductsClient,
3287
+ SiteUsersClient,
2961
3288
  SitesClient,
2962
3289
  WebhooksClient,
2963
3290
  buildImageUrl,