@sparkvault/sdk 1.21.6 → 1.21.7

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.
@@ -265,6 +265,7 @@ class HttpClient {
265
265
  }
266
266
  async parseResponse(response) {
267
267
  // Handle empty responses (204 No Content, etc.)
268
+ // Callers expecting no body should use <void> as the generic type parameter.
268
269
  const contentLength = response.headers.get('content-length');
269
270
  if (response.status === 204 || contentLength === '0') {
270
271
  return null;
@@ -539,6 +540,57 @@ function base64urlToString(base64url) {
539
540
  * Consolidates duplicated logic from renderer, api, and index.
540
541
  */
541
542
  // Re-export timer utilities
543
+ /**
544
+ * Common multi-part public suffixes where the last two labels form the TLD.
545
+ * Domains under these suffixes need three labels for a valid registrable domain
546
+ * (e.g., "app.client.co.uk" → registrable domain is "client.co.uk").
547
+ */
548
+ const MULTI_PART_TLDS = new Set([
549
+ 'co.uk', 'co.jp', 'co.kr', 'co.nz', 'co.za', 'co.in', 'co.id', 'co.th',
550
+ 'com.au', 'com.br', 'com.cn', 'com.mx', 'com.sg', 'com.tw', 'com.hk',
551
+ 'com.ar', 'com.co', 'com.my', 'com.ph', 'com.pk', 'com.tr', 'com.ua',
552
+ 'com.vn', 'com.ng', 'com.eg', 'com.sa', 'com.pe', 'com.ec',
553
+ 'net.au', 'net.br', 'net.cn', 'net.nz',
554
+ 'org.au', 'org.br', 'org.cn', 'org.nz', 'org.uk',
555
+ 'ac.uk', 'gov.uk', 'ac.jp', 'ne.jp', 'or.jp',
556
+ 'ac.kr', 'go.kr', 'or.kr',
557
+ 'ac.nz', 'govt.nz',
558
+ 'ac.za', 'gov.za',
559
+ 'ac.in', 'gov.in', 'net.in', 'org.in',
560
+ ]);
561
+ /**
562
+ * Extract the root (registrable) domain from a hostname for cross-subdomain cookies.
563
+ * Returns the domain prefixed with a dot (e.g., ".client.com") or null when
564
+ * the domain attribute should not be set (localhost, IP addresses).
565
+ *
566
+ * @param hostname - The hostname to extract the root domain from
567
+ * @returns Root domain with leading dot, or null
568
+ */
569
+ function getRootDomain(hostname) {
570
+ // Localhost — browsers reject domain attribute for localhost
571
+ if (hostname === 'localhost' || hostname === '127.0.0.1')
572
+ return null;
573
+ // IPv4 addresses
574
+ if (/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname))
575
+ return null;
576
+ // IPv6 addresses
577
+ if (hostname.includes(':'))
578
+ return null;
579
+ const parts = hostname.split('.');
580
+ // Single-label hostnames (no dots)
581
+ if (parts.length <= 1)
582
+ return null;
583
+ // Two-part domain (e.g., "client.com") — already the root
584
+ if (parts.length === 2)
585
+ return `.${hostname}`;
586
+ // Check for multi-part TLD (e.g., "co.uk" in "app.client.co.uk")
587
+ const lastTwo = parts.slice(-2).join('.');
588
+ if (MULTI_PART_TLDS.has(lastTwo) && parts.length >= 3) {
589
+ return `.${parts.slice(-3).join('.')}`;
590
+ }
591
+ // Standard TLD — root domain is last two parts (e.g., "app.client.com" → ".client.com")
592
+ return `.${lastTwo}`;
593
+ }
542
594
  /**
543
595
  * Get cookie value by name
544
596
  * @param name - Cookie name
@@ -549,7 +601,10 @@ function getCookie(name) {
549
601
  return match ? match[2] : null;
550
602
  }
551
603
  /**
552
- * Set cookie with expiration
604
+ * Set cookie with expiration on the root domain for cross-subdomain access.
605
+ * The domain is automatically extracted from the current hostname and prefixed
606
+ * with a dot (e.g., ".client.com") so the cookie is shared across all subdomains.
607
+ *
553
608
  * @param name - Cookie name
554
609
  * @param value - Cookie value
555
610
  * @param days - Days until expiration
@@ -558,7 +613,9 @@ function setCookie(name, value, days) {
558
613
  const date = new Date();
559
614
  date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
560
615
  const expires = `expires=${date.toUTCString()}`;
561
- document.cookie = `${name}=${value}; ${expires}; path=/; SameSite=Lax`;
616
+ const domain = getRootDomain(window.location.hostname);
617
+ const domainAttr = domain ? `; domain=${domain}` : '';
618
+ document.cookie = `${name}=${value}; ${expires}; path=/${domainAttr}; SameSite=Lax`;
562
619
  }
563
620
  /**
564
621
  * Generate cryptographically secure random state string
@@ -610,9 +667,11 @@ class IdentityApi {
610
667
  }
611
668
  const url = `${this.baseUrl}${endpoint}`;
612
669
  const headers = {
613
- 'Content-Type': 'application/json',
614
670
  'Accept': 'application/json',
615
671
  };
672
+ if (body) {
673
+ headers['Content-Type'] = 'application/json';
674
+ }
616
675
  // Create abort controller for request timeout
617
676
  const timeoutController = new AbortController();
618
677
  const timeoutId = setTimeout(() => timeoutController.abort(), this.timeoutMs);