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.mjs CHANGED
@@ -707,7 +707,7 @@ var AuthClient = class extends BaseClient {
707
707
 
708
708
  // src/utils/validators.ts
709
709
  var MAX_API_QUERY_LIMIT = 100;
710
- var ALLOWED_CONTENT_TYPES = ["post", "page"];
710
+ var ALLOWED_CONTENT_TYPES = ["post", "page", "block"];
711
711
  function validateLimit(limit, context) {
712
712
  if (typeof limit !== "number" || Number.isNaN(limit) || !Number.isFinite(limit)) {
713
713
  throw new Error(`[PerspectAPI] ${context} limit must be a finite number.`);
@@ -1461,6 +1461,64 @@ var ProductsClient = class extends BaseClient {
1461
1461
  }
1462
1462
  return void 0;
1463
1463
  }
1464
+ // ============================================================================
1465
+ // PRODUCT OPTIONS AND SKUS (for variants)
1466
+ // ============================================================================
1467
+ /**
1468
+ * Get all options (and their values) for a product
1469
+ */
1470
+ async getProductOptions(siteName, productId) {
1471
+ const endpoint = this.siteScopedEndpoint(
1472
+ siteName,
1473
+ `/products/${productId}/options`,
1474
+ { includeSitesSegment: false }
1475
+ );
1476
+ return this.getSingle(endpoint);
1477
+ }
1478
+ /**
1479
+ * Create a new product option (e.g., "Size", "Color")
1480
+ */
1481
+ async createProductOption(siteName, productId, data) {
1482
+ const endpoint = this.siteScopedEndpoint(
1483
+ siteName,
1484
+ `/products/${productId}/options`,
1485
+ { includeSitesSegment: false }
1486
+ );
1487
+ return this.create(endpoint, data);
1488
+ }
1489
+ /**
1490
+ * Create a new value for a product option (e.g., "Small", "Red")
1491
+ */
1492
+ async createProductOptionValue(siteName, productId, optionId, data) {
1493
+ const endpoint = this.siteScopedEndpoint(
1494
+ siteName,
1495
+ `/products/${productId}/options/${optionId}/values`,
1496
+ { includeSitesSegment: false }
1497
+ );
1498
+ return this.create(endpoint, data);
1499
+ }
1500
+ /**
1501
+ * Get all SKUs for a product (with their option value combinations)
1502
+ */
1503
+ async getProductSkus(siteName, productId) {
1504
+ const endpoint = this.siteScopedEndpoint(
1505
+ siteName,
1506
+ `/products/${productId}/skus`,
1507
+ { includeSitesSegment: false }
1508
+ );
1509
+ return this.getSingle(endpoint);
1510
+ }
1511
+ /**
1512
+ * Create or update a SKU for a product variant combination
1513
+ */
1514
+ async createProductSku(siteName, productId, data) {
1515
+ const endpoint = this.siteScopedEndpoint(
1516
+ siteName,
1517
+ `/products/${productId}/skus`,
1518
+ { includeSitesSegment: false }
1519
+ );
1520
+ return this.create(endpoint, data);
1521
+ }
1464
1522
  };
1465
1523
 
1466
1524
  // src/client/categories-client.ts
@@ -2175,6 +2233,271 @@ var NewsletterClient = class extends BaseClient {
2175
2233
  }
2176
2234
  };
2177
2235
 
2236
+ // src/client/site-users-client.ts
2237
+ var SiteUsersClient = class extends BaseClient {
2238
+ constructor(http, cache) {
2239
+ super(http, "/api/v1", cache);
2240
+ }
2241
+ /**
2242
+ * Build a site user endpoint scoped to a site (without /sites prefix)
2243
+ */
2244
+ siteUserEndpoint(siteName, endpoint) {
2245
+ return this.siteScopedEndpoint(siteName, endpoint, { includeSitesSegment: false });
2246
+ }
2247
+ // ============================================================================
2248
+ // PUBLIC ENDPOINTS (OTP authentication)
2249
+ // ============================================================================
2250
+ /**
2251
+ * Request OTP for login/signup
2252
+ * @param siteName - The site name
2253
+ * @param data - Email address
2254
+ * @param csrfToken - CSRF token (required for browser-based submissions)
2255
+ */
2256
+ async requestOtp(siteName, data, csrfToken) {
2257
+ if (typeof window !== "undefined" && !csrfToken) {
2258
+ console.warn("CSRF token recommended for browser-based OTP requests");
2259
+ }
2260
+ return this.create(
2261
+ this.siteUserEndpoint(siteName, "/users/request-otp"),
2262
+ data,
2263
+ csrfToken
2264
+ );
2265
+ }
2266
+ /**
2267
+ * Verify OTP and get JWT token
2268
+ *
2269
+ * For cross-domain authentication, you must manually set the token after verification:
2270
+ * ```typescript
2271
+ * const response = await client.siteUsers.verifyOtp('mysite', { email, code });
2272
+ * const { token, user } = response.data;
2273
+ *
2274
+ * // Store token securely (choose one):
2275
+ * // Option 1: Memory (lost on refresh, most secure)
2276
+ * client.setAuth(token);
2277
+ *
2278
+ * // Option 2: httpOnly cookie on YOUR domain (recommended for production)
2279
+ * await fetch('/your-api/set-auth-cookie', {
2280
+ * method: 'POST',
2281
+ * body: JSON.stringify({ token })
2282
+ * });
2283
+ * client.setAuth(token);
2284
+ *
2285
+ * // Option 3: localStorage (vulnerable to XSS, not recommended)
2286
+ * localStorage.setItem('site_user_token', token);
2287
+ * client.setAuth(token);
2288
+ * ```
2289
+ *
2290
+ * For convenience, use `verifyOtpAndSetAuth()` to automatically set the token in memory.
2291
+ *
2292
+ * @param siteName - The site name
2293
+ * @param data - Email and code
2294
+ * @param csrfToken - CSRF token (required for browser-based submissions)
2295
+ */
2296
+ async verifyOtp(siteName, data, csrfToken) {
2297
+ if (typeof window !== "undefined" && !csrfToken) {
2298
+ console.warn("CSRF token recommended for browser-based OTP verification");
2299
+ }
2300
+ return this.create(
2301
+ this.siteUserEndpoint(siteName, "/users/verify-otp"),
2302
+ data,
2303
+ csrfToken
2304
+ );
2305
+ }
2306
+ /**
2307
+ * Verify OTP and automatically set the token for subsequent requests
2308
+ *
2309
+ * Convenience method that:
2310
+ * 1. Verifies the OTP
2311
+ * 2. Automatically calls setAuth() with the returned token
2312
+ *
2313
+ * Note: Token is stored in memory only and will be lost on page refresh.
2314
+ * For persistent auth, use verifyOtp() and store the token yourself.
2315
+ *
2316
+ * @param siteName - The site name
2317
+ * @param data - Email and code
2318
+ * @param csrfToken - CSRF token (required for browser-based submissions)
2319
+ */
2320
+ async verifyOtpAndSetAuth(siteName, data, csrfToken) {
2321
+ const response = await this.verifyOtp(siteName, data, csrfToken);
2322
+ if (response.data?.token) {
2323
+ this.http.setAuth(response.data.token);
2324
+ }
2325
+ return response;
2326
+ }
2327
+ /**
2328
+ * Logout (clear session cookie)
2329
+ * @param siteName - The site name
2330
+ */
2331
+ async logout(siteName) {
2332
+ return this.create(
2333
+ this.siteUserEndpoint(siteName, "/users/logout"),
2334
+ {}
2335
+ );
2336
+ }
2337
+ // ============================================================================
2338
+ // AUTHENTICATED ENDPOINTS (site user JWT required)
2339
+ // ============================================================================
2340
+ /**
2341
+ * Get current user profile
2342
+ * @param siteName - The site name
2343
+ */
2344
+ async getMe(siteName) {
2345
+ return this.getSingle(
2346
+ this.siteUserEndpoint(siteName, "/users/me")
2347
+ );
2348
+ }
2349
+ /**
2350
+ * Update current user profile
2351
+ * @param siteName - The site name
2352
+ * @param data - Fields to update
2353
+ * @param csrfToken - CSRF token (required)
2354
+ */
2355
+ async updateMe(siteName, data, csrfToken) {
2356
+ return this.patch(
2357
+ this.siteUserEndpoint(siteName, "/users/me"),
2358
+ data,
2359
+ csrfToken
2360
+ );
2361
+ }
2362
+ /**
2363
+ * Get all profile key-values
2364
+ * @param siteName - The site name
2365
+ */
2366
+ async getProfile(siteName) {
2367
+ return this.getSingle(
2368
+ this.siteUserEndpoint(siteName, "/users/me/profile")
2369
+ );
2370
+ }
2371
+ /**
2372
+ * Set a profile key-value
2373
+ * @param siteName - The site name
2374
+ * @param key - Profile key (e.g., 'phone', 'whatsapp', 'address_shipping')
2375
+ * @param value - Profile value (string or JSON string)
2376
+ * @param csrfToken - CSRF token (required)
2377
+ */
2378
+ async setProfileValue(siteName, key, value, csrfToken) {
2379
+ return this.update(
2380
+ this.siteUserEndpoint(siteName, `/users/me/profile/${encodeURIComponent(key)}`),
2381
+ { value },
2382
+ csrfToken
2383
+ );
2384
+ }
2385
+ /**
2386
+ * Delete a profile key-value
2387
+ * @param siteName - The site name
2388
+ * @param key - Profile key to delete
2389
+ * @param csrfToken - CSRF token (required)
2390
+ */
2391
+ async deleteProfileValue(siteName, key, csrfToken) {
2392
+ return this.delete(
2393
+ this.siteUserEndpoint(siteName, `/users/me/profile/${encodeURIComponent(key)}`),
2394
+ csrfToken
2395
+ );
2396
+ }
2397
+ /**
2398
+ * Get transaction/order history
2399
+ * @param siteName - The site name
2400
+ * @param params - Pagination params
2401
+ */
2402
+ async getOrders(siteName, params) {
2403
+ return this.http.get(
2404
+ this.buildPath(this.siteUserEndpoint(siteName, "/users/me/orders")),
2405
+ params
2406
+ );
2407
+ }
2408
+ /**
2409
+ * Get single order detail
2410
+ * @param siteName - The site name
2411
+ * @param orderId - Order ID or session ID
2412
+ */
2413
+ async getOrder(siteName, orderId) {
2414
+ return this.getSingle(
2415
+ this.siteUserEndpoint(siteName, `/users/me/orders/${encodeURIComponent(orderId)}`)
2416
+ );
2417
+ }
2418
+ /**
2419
+ * Get payment subscriptions
2420
+ * @param siteName - The site name
2421
+ * @param params - Pagination params
2422
+ */
2423
+ async getSubscriptions(siteName, params) {
2424
+ return this.http.get(
2425
+ this.buildPath(this.siteUserEndpoint(siteName, "/users/me/subscriptions")),
2426
+ params
2427
+ );
2428
+ }
2429
+ /**
2430
+ * Get single subscription detail
2431
+ * @param siteName - The site name
2432
+ * @param id - Subscription ID
2433
+ */
2434
+ async getSubscription(siteName, id) {
2435
+ return this.getSingle(
2436
+ this.siteUserEndpoint(siteName, `/users/me/subscriptions/${encodeURIComponent(id)}`)
2437
+ );
2438
+ }
2439
+ /**
2440
+ * Cancel a subscription (marks for cancellation at period end)
2441
+ * @param siteName - The site name
2442
+ * @param id - Subscription ID
2443
+ * @param csrfToken - CSRF token (required)
2444
+ */
2445
+ async cancelSubscription(siteName, id, csrfToken) {
2446
+ return this.create(
2447
+ this.siteUserEndpoint(siteName, `/users/me/subscriptions/${encodeURIComponent(id)}/cancel`),
2448
+ {},
2449
+ csrfToken
2450
+ );
2451
+ }
2452
+ /**
2453
+ * Get linked newsletter subscriptions
2454
+ * @param siteName - The site name
2455
+ */
2456
+ async getNewsletterSubscriptions(siteName) {
2457
+ return this.getSingle(
2458
+ this.siteUserEndpoint(siteName, "/users/me/newsletters")
2459
+ );
2460
+ }
2461
+ // ============================================================================
2462
+ // ADMIN ENDPOINTS (API key auth required)
2463
+ // ============================================================================
2464
+ /**
2465
+ * List all site users (admin only)
2466
+ * @param siteName - The site name
2467
+ * @param params - Query params (limit, offset, status)
2468
+ */
2469
+ async listUsers(siteName, params) {
2470
+ return this.http.get(
2471
+ this.buildPath(this.siteUserEndpoint(siteName, "/users")),
2472
+ params
2473
+ );
2474
+ }
2475
+ /**
2476
+ * Get user detail (admin only)
2477
+ * @param siteName - The site name
2478
+ * @param userId - User ID
2479
+ */
2480
+ async getUser(siteName, userId) {
2481
+ return this.getSingle(
2482
+ this.siteUserEndpoint(siteName, `/users/${encodeURIComponent(userId)}`)
2483
+ );
2484
+ }
2485
+ /**
2486
+ * Update user status (admin only)
2487
+ * @param siteName - The site name
2488
+ * @param userId - User ID
2489
+ * @param status - New status
2490
+ * @param csrfToken - CSRF token (required)
2491
+ */
2492
+ async updateUserStatus(siteName, userId, status, csrfToken) {
2493
+ return this.patch(
2494
+ this.siteUserEndpoint(siteName, `/users/${encodeURIComponent(userId)}/status`),
2495
+ { status },
2496
+ csrfToken
2497
+ );
2498
+ }
2499
+ };
2500
+
2178
2501
  // src/perspect-api-client.ts
2179
2502
  var PerspectApiClient = class {
2180
2503
  http;
@@ -2191,6 +2514,7 @@ var PerspectApiClient = class {
2191
2514
  checkout;
2192
2515
  contact;
2193
2516
  newsletter;
2517
+ siteUsers;
2194
2518
  constructor(config) {
2195
2519
  if (!config.baseUrl) {
2196
2520
  throw new Error("baseUrl is required in PerspectApiConfig");
@@ -2208,6 +2532,7 @@ var PerspectApiClient = class {
2208
2532
  this.checkout = new CheckoutClient(this.http, this.cache);
2209
2533
  this.contact = new ContactClient(this.http, this.cache);
2210
2534
  this.newsletter = new NewsletterClient(this.http, this.cache);
2535
+ this.siteUsers = new SiteUsersClient(this.http, this.cache);
2211
2536
  }
2212
2537
  /**
2213
2538
  * Update authentication token
@@ -2896,6 +3221,7 @@ export {
2896
3221
  OrganizationsClient,
2897
3222
  PerspectApiClient,
2898
3223
  ProductsClient,
3224
+ SiteUsersClient,
2899
3225
  SitesClient,
2900
3226
  WebhooksClient,
2901
3227
  buildImageUrl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perspectapi-ts-sdk",
3
- "version": "2.7.0",
3
+ "version": "2.8.2",
4
4
  "description": "TypeScript SDK for PerspectAPI - Cloudflare Workers compatible",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -371,4 +371,127 @@ export class ProductsClient extends BaseClient {
371
371
  }
372
372
  return undefined;
373
373
  }
374
+
375
+ // ============================================================================
376
+ // PRODUCT OPTIONS AND SKUS (for variants)
377
+ // ============================================================================
378
+
379
+ /**
380
+ * Get all options (and their values) for a product
381
+ */
382
+ async getProductOptions(
383
+ siteName: string,
384
+ productId: number
385
+ ): Promise<ApiResponse<Array<{
386
+ option_id: number;
387
+ option_name: string;
388
+ display_order: number;
389
+ values: Array<{
390
+ value_id: number;
391
+ value: string;
392
+ display_order: number;
393
+ }>;
394
+ }>>> {
395
+ const endpoint = this.siteScopedEndpoint(
396
+ siteName,
397
+ `/products/${productId}/options`,
398
+ { includeSitesSegment: false }
399
+ );
400
+ return this.getSingle(endpoint);
401
+ }
402
+
403
+ /**
404
+ * Create a new product option (e.g., "Size", "Color")
405
+ */
406
+ async createProductOption(
407
+ siteName: string,
408
+ productId: number,
409
+ data: { option_name: string; display_order?: number }
410
+ ): Promise<ApiResponse<{
411
+ option_id: number;
412
+ option_name: string;
413
+ display_order: number;
414
+ }>> {
415
+ const endpoint = this.siteScopedEndpoint(
416
+ siteName,
417
+ `/products/${productId}/options`,
418
+ { includeSitesSegment: false }
419
+ );
420
+ return this.create(endpoint, data);
421
+ }
422
+
423
+ /**
424
+ * Create a new value for a product option (e.g., "Small", "Red")
425
+ */
426
+ async createProductOptionValue(
427
+ siteName: string,
428
+ productId: number,
429
+ optionId: number,
430
+ data: { value: string; display_order?: number }
431
+ ): Promise<ApiResponse<{
432
+ value_id: number;
433
+ value: string;
434
+ display_order: number;
435
+ }>> {
436
+ const endpoint = this.siteScopedEndpoint(
437
+ siteName,
438
+ `/products/${productId}/options/${optionId}/values`,
439
+ { includeSitesSegment: false }
440
+ );
441
+ return this.create(endpoint, data);
442
+ }
443
+
444
+ /**
445
+ * Get all SKUs for a product (with their option value combinations)
446
+ */
447
+ async getProductSkus(
448
+ siteName: string,
449
+ productId: number
450
+ ): Promise<ApiResponse<Array<{
451
+ sku_id: number;
452
+ sku: string;
453
+ price?: number;
454
+ sale_price?: number;
455
+ stock_quantity?: number;
456
+ combination_key: string;
457
+ value_ids: number[];
458
+ created_at: string;
459
+ updated_at: string;
460
+ }>>> {
461
+ const endpoint = this.siteScopedEndpoint(
462
+ siteName,
463
+ `/products/${productId}/skus`,
464
+ { includeSitesSegment: false }
465
+ );
466
+ return this.getSingle(endpoint);
467
+ }
468
+
469
+ /**
470
+ * Create or update a SKU for a product variant combination
471
+ */
472
+ async createProductSku(
473
+ siteName: string,
474
+ productId: number,
475
+ data: {
476
+ sku: string;
477
+ price?: number | null;
478
+ sale_price?: number | null;
479
+ stock_quantity?: number | null;
480
+ value_ids: number[];
481
+ }
482
+ ): Promise<ApiResponse<{
483
+ sku_id: number;
484
+ sku: string;
485
+ price?: number;
486
+ sale_price?: number;
487
+ stock_quantity?: number;
488
+ combination_key: string;
489
+ }>> {
490
+ const endpoint = this.siteScopedEndpoint(
491
+ siteName,
492
+ `/products/${productId}/skus`,
493
+ { includeSitesSegment: false }
494
+ );
495
+ return this.create(endpoint, data);
496
+ }
374
497
  }