Skip to main content

Utilities

The SDK provides various utility functions for consent management, data transformation, browser context handling, and other common operations.

checkConsent()

Checks if all required consent types have been granted by the customer.
function checkConsent(
  requiredConsents: ConsentType[],
  canTrack: any,
  analytics: any
): boolean
Parameters:
  • requiredConsents - Array of consent types that must be granted
  • canTrack - Hydrogen’s canTrack function from analytics
  • analytics - Hydrogen’s analytics instance
Returns: true if all required consents are granted, false otherwise Usage:
import { checkConsent, CONSENT_TYPES } from '@glood/hydrogen';

// Check if analytics consent is granted
const canSendAnalytics = checkConsent(
  [CONSENT_TYPES.ANALYTICS],
  canTrack,
  analytics
);

// Check multiple consent types
const canSendMarketing = checkConsent(
  [CONSENT_TYPES.ANALYTICS, CONSENT_TYPES.MARKETING],
  canTrack,
  analytics
);

if (canSendAnalytics) {
  // Send analytics pixel
}
Implementation Details:
export function checkConsent(
  requiredConsents: ConsentType[],
  canTrack: any,
  analytics: any
): boolean {
  if (!requiredConsents || requiredConsents.length === 0) {
    return true; // No consent required
  }

  return requiredConsents.every(consentType => {
    switch (consentType) {
      case 'analytics':
        return analytics.customerPrivacy?.analyticsProcessingAllowed();
      case 'marketing':
        return analytics.customerPrivacy?.marketingAllowed();
      case 'preferences':
        return analytics.customerPrivacy?.preferencesProcessingAllowed();
      case 'sale_of_data':
        return analytics.customerPrivacy?.saleOfDataAllowed();
      default:
        return false;
    }
  });
}

Constants

DEFAULT_ENDPOINTS

Default API and pixel endpoints for all apps.
const DEFAULT_ENDPOINTS = {
  recommendations: {
    main: 'https://storefront.glood.ai',
    pixel: 'https://events.glood.ai',
  },
  search: {
    main: 'https://s-s.glood.ai',
    pixel: 'https://s-pixel.glood.ai',
  },
  wishlist: {
    main: 'https://w-s.glood.ai',
    pixel: 'https://w-pixel.glood.ai',
  },
} as const;
Usage:
import { DEFAULT_ENDPOINTS } from '@glood/hydrogen';

// Get recommendations endpoints
const recEndpoint = DEFAULT_ENDPOINTS.recommendations.main;
const recPixel = DEFAULT_ENDPOINTS.recommendations.pixel;

// Use in custom configuration
const customConfig = {
  endpoint: DEFAULT_ENDPOINTS.search.main,
  pixel: {
    enabled: true,
    endpoint: DEFAULT_ENDPOINTS.search.pixel,
    consent: ['analytics'],
  },
};

DEFAULT_PIXEL_CONFIG

Default pixel configuration settings.
const DEFAULT_PIXEL_CONFIG = {
  enabled: true,
} as const;

ALL_EVENTS

Array of all supported event types.
const ALL_EVENTS = [
  'page_viewed',
  'product_viewed',
  'collection_viewed',
  'cart_viewed',
  'search_viewed',
  'search_submitted',
  'custom_promotion_viewed',
  'product_added_to_cart',
  'product_removed_from_cart',
] as const;
Usage:
import { ALL_EVENTS } from '@glood/hydrogen';

// Check if event type is valid
function isValidEventType(eventType: string): boolean {
  return ALL_EVENTS.includes(eventType as any);
}

// Get all event types
const eventTypes = [...ALL_EVENTS];
Constants for all consent types.
const CONSENT_TYPES = {
  ANALYTICS: 'analytics',
  MARKETING: 'marketing',
  PREFERENCES: 'preferences',
  SALE_OF_DATA: 'sale_of_data',
} as const;
Usage:
import { CONSENT_TYPES } from '@glood/hydrogen';

// Use in configuration
const pixelConfig = {
  enabled: true,
  endpoint: 'https://events.glood.ai',
  consent: [CONSENT_TYPES.ANALYTICS, CONSENT_TYPES.MARKETING],
};

// Type-safe consent checking
const requiredConsents = [
  CONSENT_TYPES.ANALYTICS,
  CONSENT_TYPES.PREFERENCES,
];
Default consent requirements for each app.
const DEFAULT_PIXEL_CONSENT = {
  recommendations: [CONSENT_TYPES.ANALYTICS, CONSENT_TYPES.MARKETING],
  search: [CONSENT_TYPES.ANALYTICS],
  wishlist: [CONSENT_TYPES.ANALYTICS, CONSENT_TYPES.PREFERENCES],
} as const;
Usage:
import { DEFAULT_PIXEL_CONSENT } from '@glood/hydrogen';

// Get default consent for an app
const recConsent = DEFAULT_PIXEL_CONSENT.recommendations;
// ['analytics', 'marketing']

// Use in custom configuration
const customSearchConfig = {
  endpoint: 'https://custom-search.glood.ai',
  pixel: {
    enabled: true,
    endpoint: 'https://custom-pixel.glood.ai',
    consent: DEFAULT_PIXEL_CONSENT.search, // ['analytics']
  },
};

Data Transformers

transformCartData()

Transforms Shopify cart data to Glood pixel format.
function transformCartData(shopifyCart: any): any
Parameters:
  • shopifyCart - Shopify cart object from analytics events
Returns: Transformed cart data in Glood format, or null if cart is empty Usage:
import { transformCartData } from '@glood/hydrogen';

// Transform cart from Shopify analytics event
const transformedCart = transformCartData(eventData.cart);

if (transformedCart) {
  console.log('Cart total:', transformedCart.cost.totalAmount.amount);
  console.log('Item count:', transformedCart.totalQuantity);
}
Transformation Example:
// Input: Shopify cart
{
  id: 'gid://shopify/Cart/abc123?key=def456',
  cost: {
    totalAmount: { amount: '29.99', currencyCode: 'USD' }
  },
  lines: {
    nodes: [{
      cost: { totalAmount: { amount: '29.99', currencyCode: 'USD' } },
      merchandise: {
        id: 'gid://shopify/ProductVariant/123',
        title: 'Blue T-Shirt',
        price: { amount: '29.99', currencyCode: 'USD' },
        product: {
          id: 'gid://shopify/Product/456',
          title: 'Cool T-Shirt',
          handle: 'cool-t-shirt'
        }
      },
      quantity: 1
    }]
  },
  totalQuantity: 1
}

// Output: Glood format
{
  id: 'abc123',
  cost: {
    totalAmount: { amount: 29.99, currencyCode: 'USD' }
  },
  lines: [{
    cost: {
      totalAmount: { amount: 29.99, currencyCode: 'USD' }
    },
    merchandise: {
      id: '123',
      title: 'Blue T-Shirt',
      price: { amount: 29.99, currencyCode: 'USD' },
      product: {
        id: '456',
        title: 'Cool T-Shirt',
        url: '/products/cool-t-shirt',
        // ... other fields
      }
    },
    quantity: 1
  }],
  totalQuantity: 1
}

transformShopData()

Transforms shop data to Glood pixel format.
function transformShopData(shopData: any, myShopifyDomain: string): any
Parameters:
  • shopData - Shop data from analytics events
  • myShopifyDomain - Your Shopify domain
Returns: Transformed shop data in Glood format Usage:
import { transformShopData } from '@glood/hydrogen';

const transformedShop = transformShopData(
  eventData.shop,
  'my-store.myshopify.com'
);

console.log('Shop name:', transformedShop.name);
console.log('Currency:', transformedShop.paymentSettings.currencyCode);

createCustomerPrivacy()

Creates customer privacy object from analytics data.
function createCustomerPrivacy(analytics: any): any
Parameters:
  • analytics - Hydrogen analytics instance
Returns: Customer privacy object with consent status Usage:
import { createCustomerPrivacy } from '@glood/hydrogen';

const customerPrivacy = createCustomerPrivacy(analytics);

console.log('Analytics allowed:', customerPrivacy.analyticsProcessingAllowed);
console.log('Marketing allowed:', customerPrivacy.marketingAllowed);
Output Example:
{
  analyticsProcessingAllowed: true,
  marketingAllowed: false,
  preferencesProcessingAllowed: true,
  saleOfDataAllowed: false,
}

getPageTitle()

Gets the current page title with fallback.
function getPageTitle(fallback: string = ''): string
Parameters:
  • fallback - Fallback title if document.title is not available
Returns: Current page title or fallback Usage:
import { getPageTitle } from '@glood/hydrogen';

const title = getPageTitle('Default Title');
console.log('Page title:', title);

getCurrentUrl()

Gets the current URL from window or event data.
function getCurrentUrl(eventData?: any): string
Parameters:
  • eventData - Optional event data that might contain URL
Returns: Current URL string Usage:
import { getCurrentUrl } from '@glood/hydrogen';

const url = getCurrentUrl(eventData);
console.log('Current URL:', url);

ID Generators

generateEventId()

Generates a unique event ID.
function generateEventId(): string
Returns: Unique event ID string Usage:
import { generateEventId } from '@glood/hydrogen';

const eventId = generateEventId();
console.log('Event ID:', eventId); // "evt_abc123def456"

CookieStorage

Utility class for managing client IDs and session data.

getOrGenerateClientId()

Gets or generates a persistent client ID.
static getOrGenerateClientId(): string
Returns: Client ID string (persistent across browser sessions)

getOrGenerateSessionId()

Gets or generates a session ID.
static getOrGenerateSessionId(): string
Returns: Session ID string (valid for current browser session)

getOrGenerateRkUid()

Gets or generates a user ID for analytics.
static getOrGenerateRkUid(analytics: any): string
Parameters:
  • analytics - Hydrogen analytics instance
Returns: User ID string (prefers analytics customer ID, falls back to client ID) Usage:
import { CookieStorage } from '@glood/hydrogen';

// Get persistent client ID
const clientId = CookieStorage.getOrGenerateClientId();

// Get session ID
const sessionId = CookieStorage.getOrGenerateSessionId();

// Get user ID for analytics
const userId = CookieStorage.getOrGenerateRkUid(analytics);

extractCartIdentifier()

Extracts cart identifier from Shopify cart data.
function extractCartIdentifier(cart: any): string | null
Parameters:
  • cart - Shopify cart object
Returns: Cart identifier string or null if not available Usage:
import { extractCartIdentifier } from '@glood/hydrogen';

const cartId = extractCartIdentifier(eventData.cart);
if (cartId) {
  console.log('Cart identifier:', cartId);
}

Browser Context

browserContext

Utility object for getting browser environment information.

getContext()

Gets comprehensive browser context information.
getContext(): BrowserContext
Returns: Browser context object with page, user agent, screen, and locale information Usage:
import { browserContext } from '@glood/hydrogen';

const context = browserContext.getContext();

console.log('Page URL:', context.page.url);
console.log('Screen size:', context.screen.width, 'x', context.screen.height);
console.log('Language:', context.language);
console.log('Timezone:', context.timezone);
Output Example:
{
  page: {
    url: 'https://store.myshopify.com/products/shirt',
    title: 'Cool Shirt - My Store',
    referrer: 'https://google.com'
  },
  userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
  screen: {
    width: 1920,
    height: 1080
  },
  language: 'en-US',
  timezone: 'America/New_York'
}

Pixel Queue

getPixelQueue()

Gets the global pixel queue instance for sending events.
function getPixelQueue(debug?: boolean): PixelQueue
Parameters:
  • debug - Enable debug logging for pixel transmission
Returns: PixelQueue instance Usage:
import { getPixelQueue } from '@glood/hydrogen';

const pixelQueue = getPixelQueue(true); // Enable debug mode

// Send a pixel event
pixelQueue.add({
  endpoint: 'https://events.glood.ai',
  data: {
    event: {
      id: 'evt_123',
      name: 'product_viewed',
      // ... event data
    }
  }
});

resetPixelQueue()

Resets the pixel queue (mainly for testing).
function resetPixelQueue(): void
Usage:
import { resetPixelQueue } from '@glood/hydrogen';

// Reset queue (typically used in tests)
resetPixelQueue();

Advanced Usage Examples

import { checkConsent, CONSENT_TYPES } from '@glood/hydrogen';

function createCustomConsentChecker(analytics: any) {
  return {
    canSendAnalytics: () => checkConsent([CONSENT_TYPES.ANALYTICS], null, analytics),
    canSendMarketing: () => checkConsent([CONSENT_TYPES.MARKETING], null, analytics),
    canPersonalize: () => checkConsent([CONSENT_TYPES.PREFERENCES], null, analytics),
    canSellData: () => checkConsent([CONSENT_TYPES.SALE_OF_DATA], null, analytics),

    // Custom combination checks
    canSendRecommendations: () => checkConsent(
      [CONSENT_TYPES.ANALYTICS, CONSENT_TYPES.MARKETING],
      null,
      analytics
    ),

    canTrackWishlist: () => checkConsent(
      [CONSENT_TYPES.ANALYTICS, CONSENT_TYPES.PREFERENCES],
      null,
      analytics
    ),
  };
}

// Usage
const consentChecker = createCustomConsentChecker(analytics);

if (consentChecker.canSendRecommendations()) {
  // Send recommendations pixel
}

Custom Event Transformer

import {
  transformCartData,
  transformShopData,
  generateEventId,
  CookieStorage,
  browserContext
} from '@glood/hydrogen';

function createCustomEventTransformer(client: GloodClient) {
  return {
    transformEvent(eventType: string, eventData: any) {
      const baseEvent = {
        id: generateEventId(),
        name: eventType,
        type: 'custom',
        timestamp: new Date().toISOString(),
        clientId: CookieStorage.getOrGenerateClientId(),
        context: browserContext.getContext(),
      };

      const transformedData = {
        customer: eventData.customer || null,
        cart: eventData.cart ? transformCartData(eventData.cart) : null,
        shop: transformShopData(eventData.shop, client.config.myShopifyDomain),
      };

      return {
        event: baseEvent,
        data: transformedData,
        sessionId: CookieStorage.getOrGenerateSessionId(),
        // Add custom fields
        customMetadata: {
          source: 'custom-transformer',
          version: '1.0.0',
        },
      };
    }
  };
}

Environment-Specific Constants

import { DEFAULT_ENDPOINTS, CONSENT_TYPES } from '@glood/hydrogen';

function getEnvironmentConfig(env: string) {
  const baseConfig = {
    consent: {
      minimal: [CONSENT_TYPES.ANALYTICS],
      standard: [CONSENT_TYPES.ANALYTICS, CONSENT_TYPES.MARKETING],
      full: [CONSENT_TYPES.ANALYTICS, CONSENT_TYPES.MARKETING, CONSENT_TYPES.PREFERENCES],
    },
  };

  switch (env) {
    case 'development':
      return {
        ...baseConfig,
        endpoints: {
          recommendations: {
            main: 'https://dev-storefront.glood.ai',
            pixel: 'https://dev-events.glood.ai',
          },
          // ... other dev endpoints
        },
        pixelEnabled: false, // Disable pixels in dev
      };

    case 'staging':
      return {
        ...baseConfig,
        endpoints: {
          recommendations: {
            main: 'https://staging-storefront.glood.ai',
            pixel: 'https://staging-events.glood.ai',
          },
          // ... other staging endpoints
        },
        pixelEnabled: true,
      };

    case 'production':
    default:
      return {
        ...baseConfig,
        endpoints: DEFAULT_ENDPOINTS,
        pixelEnabled: true,
      };
  }
}

Error Handling Utilities

Safe Transformation

import { transformCartData, transformShopData } from '@glood/hydrogen';

function safeTransform<T>(
  transformer: (data: any) => T,
  data: any,
  fallback: T
): T {
  try {
    return transformer(data);
  } catch (error) {
    console.warn('Transform error:', error);
    return fallback;
  }
}

// Usage
const safeCart = safeTransform(transformCartData, eventData.cart, null);
const safeShop = safeTransform(
  (shop) => transformShopData(shop, 'my-store.myshopify.com'),
  eventData.shop,
  { name: 'Unknown Store' }
);
import { checkConsent, CONSENT_TYPES } from '@glood/hydrogen';

function validateConsent(
  requiredConsents: ConsentType[],
  analytics: any
): { valid: boolean; missing: ConsentType[] } {
  const missing: ConsentType[] = [];

  for (const consentType of requiredConsents) {
    if (!checkConsent([consentType], null, analytics)) {
      missing.push(consentType);
    }
  }

  return {
    valid: missing.length === 0,
    missing,
  };
}

// Usage
const validation = validateConsent(
  [CONSENT_TYPES.ANALYTICS, CONSENT_TYPES.MARKETING],
  analytics
);

if (!validation.valid) {
  console.log('Missing consents:', validation.missing);
}

Performance Utilities

Debounced Event Sending

import { getPixelQueue } from '@glood/hydrogen';

function createDebouncedPixelSender(delay: number = 100) {
  let timeoutId: NodeJS.Timeout;
  const queue: Array<{ endpoint: string; data: any }> = [];

  return {
    add(endpoint: string, data: any) {
      queue.push({ endpoint, data });

      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => {
        const pixelQueue = getPixelQueue();
        queue.forEach(item => pixelQueue.add(item));
        queue.length = 0; // Clear queue
      }, delay);
    }
  };
}

Testing Utilities

Mock Analytics

function createMockAnalytics(consents: Partial<Record<ConsentType, boolean>> = {}) {
  return {
    customerPrivacy: {
      analyticsProcessingAllowed: () => consents.analytics ?? true,
      marketingAllowed: () => consents.marketing ?? false,
      preferencesProcessingAllowed: () => consents.preferences ?? false,
      saleOfDataAllowed: () => consents.sale_of_data ?? false,
    },
    subscribe: jest.fn(),
    canTrack: jest.fn(() => true),
  };
}

// Usage in tests
const mockAnalytics = createMockAnalytics({
  analytics: true,
  marketing: false,
});

const canSend = checkConsent([CONSENT_TYPES.ANALYTICS], null, mockAnalytics);
expect(canSend).toBe(true);

See Also