npm package: @growcado/sdk
Description: Core SDK for Growcado services
Latest version: 0.0.25

# @growcado/sdk

The official JavaScript/TypeScript SDK for Growcado CMS. This SDK provides a simple and powerful way to fetch content from your Growcado CMS and automatically track customer interactions.

## Installation

```bash
npm install @growcado/sdk
# or
yarn add @growcado/sdk
# or  
pnpm add @growcado/sdk
```

## Quick Start

```typescript
import { GrowcadoSDK } from '@growcado/sdk';

// Configure the SDK
GrowcadoSDK.configure({
  tenantId: 'your-tenant-id',
  enableAutoUTM: true,
  enableReferrerTracking: true
});

// Fetch content
const response = await GrowcadoSDK.getContent({
  modelIdentifier: 'blog-post',
  contentIdentifier: 'my-first-post'
});

if (response.data) {
  console.log('Content:', response.data);
} else if (response.error) {
  console.error('Error:', response.error.message);
}
```

## Configuration

### SDKConfig

```typescript
interface SDKConfig {
  tenantId: string;                      // Your Growcado tenant ID (required)
  baseURL?: string;                      // API base URL (default: 'https://api.growcado.io/')
  enableAutoUTM?: boolean;               // Auto-track UTM parameters (default: true)
  enableReferrerTracking?: boolean;      // Auto-track referrer information (default: true)
  storage?: 'localStorage' | 'memory' | 'auto'; // Storage method (default: 'auto')
  ssrMode?: boolean;                     // Enable SSR mode (default: auto-detected)
  hydrateOnMount?: boolean;              // Auto-hydrate when client APIs become available (default: true)
  tracker?: TrackerConfig;               // Analytics tracker configuration (optional)
}
```

## Analytics Tracker

The Growcado SDK includes a built-in analytics tracker that works in both **browser AND Node.js (SSR)** environments.

**Key features:**
- **Isomorphic** - Works in browser AND Node.js for true SSR tracking
- **SSR-safe** - Automatic call queueing on server, flushed on client
- **Segment-compatible format** - Events use proper nested structure with `context`, `properties`, and `traits`
- **Compatible API** - Familiar analytics.js-style methods (page, track, identify, group, alias, reset)
- **Flexible backend** - Events are sent to your analytics server
- **Automatic tenant ID** - All events automatically include `tenantId` in the event context

### Configuration

Configure the tracker in your SDK setup:

```typescript
import { GrowcadoSDK } from '@growcado/sdk';

GrowcadoSDK.configure({
  tenantId: 'your-tenant-id',
  tracker: {
    writeKey: 'your-api-key',
    host: 'https://analytics.example.com',  // Your analytics server (required)
    debug: false,                            // Enable debug logging (optional)
    disabled: false,                         // Disable tracking entirely (optional)
  }
});
```

### TrackerConfig

```typescript
interface TrackerConfig {
  writeKey: string;        // Analytics API key (required)
  host: string;            // Analytics server host URL (required)
  debug?: boolean;         // Enable debug logging
  disabled?: boolean;      // Disable tracking entirely (useful for dev)
  cookieDomain?: string;   // Cookie domain for tracking (browser only)
  privacyPolicy?: string;  // Privacy policy URL
}
```

### Automatic Tenant ID

The `tenantId` from your SDK configuration is **automatically included in all tracker events**. You don't need to manually add it to each event - it's set as a persistent property when the tracker initializes.

```typescript
// When you configure the SDK with a tenantId...
GrowcadoSDK.configure({
  tenantId: 'my-tenant-123',  // This is automatically added to all events
  tracker: {
    writeKey: 'your-api-key',
    host: 'https://analytics.example.com'
  }
});

// All tracker calls automatically include tenant.id in context
GrowcadoSDK.tracker.track('Button Clicked');
// Event sent with Segment-compatible format:
// {
//   type: 'track',
//   event: 'Button Clicked',
//   properties: { ... },
//   context: { tenant: { id: 'my-tenant-123' }, page: { ... }, ... }
// }
```

### Usage

The tracker exposes standard analytics.js methods:

#### Page Views

Track page views with optional category, name, and properties:

```typescript
// Auto-detect page info
GrowcadoSDK.tracker.page();

// Track with page name
GrowcadoSDK.tracker.page('Home');

// Track with category and name
GrowcadoSDK.tracker.page('Products', 'Product List');

// Track with properties
GrowcadoSDK.tracker.page('Home', { title: 'Welcome Page', url: '/home' });

// Track with all parameters
GrowcadoSDK.tracker.page('Products', 'Product List', { 
  productCount: 25,
  category: 'Electronics' 
});
```

#### Event Tracking

Track custom events with properties:

```typescript
// Track simple event
GrowcadoSDK.tracker.track('Button Clicked');

// Track with properties
GrowcadoSDK.tracker.track('Purchase', {
  productId: 'SKU-123',
  price: 99.99,
  quantity: 2,
  currency: 'USD'
});

// Track complex events
GrowcadoSDK.tracker.track('Form Submitted', {
  formName: 'signup',
  fields: ['email', 'name', 'company'],
  duration: 45000
});
```

#### User Identification

Identify users with traits:

```typescript
// Identify with user ID only
GrowcadoSDK.tracker.identify('user123');

// Identify with user ID and traits
GrowcadoSDK.tracker.identify('user123', {
  email: 'user@example.com',
  name: 'John Doe',
  plan: 'premium'
});

// Identify anonymous user with traits
GrowcadoSDK.tracker.identify({
  email: 'user@example.com'
});

// Full user profile
GrowcadoSDK.tracker.identify('user123', {
  email: 'user@example.com',
  name: 'John Doe',
  firstName: 'John',
  lastName: 'Doe',
  phone: '+1-555-0123',
  company: {
    name: 'Acme Inc',
    industry: 'Technology',
    employee_count: 100,
    plan: 'enterprise'
  },
  address: {
    city: 'San Francisco',
    country: 'USA',
    postalCode: '94105'
  },
  createdAt: '2024-01-15T10:30:00Z'
});
```

#### Group Tracking

Associate users with groups (companies, organizations):

```typescript
// Group with ID only
GrowcadoSDK.tracker.group('org456');

// Group with traits
GrowcadoSDK.tracker.group('org456', {
  name: 'Acme Inc',
  industry: 'Technology',
  employees: 100,
  plan: 'enterprise',
  website: 'https://acme.com'
});
```

#### Alias

Create aliases for user IDs:

```typescript
// Link new ID to current anonymous ID
GrowcadoSDK.tracker.alias('new-user-id');

// Link two specific user IDs
GrowcadoSDK.tracker.alias('new-user-id', 'anonymous-123');
```

#### Reset

Clear user identity (typically on logout):

```typescript
GrowcadoSDK.tracker.reset();
```

#### Persistent Properties

Set properties that will be included in all subsequent events:

```typescript
// Set persistent properties
GrowcadoSDK.tracker.setProperties({
  appVersion: '2.0.0',
  environment: 'production',
  plan: 'premium'
});

// Unset a specific property
GrowcadoSDK.tracker.unsetProperty('plan');
```

### Waiting for Tracker Ready

The tracker initializes asynchronously. Use `ready()` to wait for initialization:

```typescript
// Wait for tracker to be ready
await GrowcadoSDK.tracker.ready();

// Check if ready synchronously
if (GrowcadoSDK.tracker.isReady()) {
  // Tracker is initialized
}
```

### SSR Support

The analytics tracker is **isomorphic** - it works in both browser AND Node.js environments! This means you can track events from your server-side rendering code.

- **Server-side**: Events are tracked directly from Node.js
- **Browser**: Events are tracked from the client
- **Queuing**: Calls made before initialization are queued and flushed when ready

```typescript
// Server-side (Next.js example) - WORKS!
GrowcadoSDK.configure({
  tenantId: 'your-tenant-id',
  tracker: {
    writeKey: 'your-api-key',
    host: 'https://analytics.example.com'
  }
});

// Track events on server (sent directly to your analytics server)
GrowcadoSDK.tracker.page('Home');
GrowcadoSDK.tracker.identify('user123');

// Client-side hydration
useEffect(() => {
  GrowcadoSDK.hydrate();
  // Tracker re-initializes for browser context
}, []);
```

### Server vs Browser Context

The tracker automatically adapts to the environment:

| Feature | Browser | Node.js (SSR) |
|---------|---------|---------------|
| Event tracking | ✅ Via HTTP | ✅ Via HTTP |
| Auto page context | ✅ window.location | ❌ Must provide |
| Cookies | ✅ Automatic | ❌ N/A |
| User agent | ✅ Automatic | ❌ Must provide |

```typescript
// On server, you may want to manually provide context
GrowcadoSDK.tracker.page({
  url: req.url,
  path: req.path,
  referrer: req.headers.referer
});
```

### Disabling Tracking

Disable tracking for development or testing:

```typescript
GrowcadoSDK.configure({
  tenantId: 'your-tenant-id',
  tracker: {
    writeKey: 'your-write-key',
    disabled: process.env.NODE_ENV === 'development'
  }
});
```

## Server-Side Rendering (SSR) & Hydration

The Growcado SDK provides built-in support for Server-Side Rendering (SSR) environments like Next.js, Nuxt.js, and SvelteKit. The SDK automatically detects the environment and adapts its behavior accordingly.

### Quick SSR Setup

```typescript
import { GrowcadoSDK } from '@growcado/sdk';

// Configure for SSR with automatic environment detection
GrowcadoSDK.configure({
  tenantId: 'your-tenant-id',
  storage: 'auto',           // Automatically uses memory on server, localStorage on client
  enableAutoUTM: true,
  enableReferrerTracking: true
});
```

### SSR Configuration Options

- **`storage: 'auto'`** - Automatically uses memory storage on server and localStorage on client
- **`ssrMode: boolean`** - Explicitly enable/disable SSR mode (auto-detected by default)
- **`hydrateOnMount: boolean`** - Automatically migrate data from memory to localStorage on client (default: true)

### Next.js Example

#### Server Component (App Router)

```typescript
// app/blog/[slug]/page.tsx
import { GrowcadoSDK } from '@growcado/sdk';

// Configure once (can be in a separate config file)
GrowcadoSDK.configure({
  tenantId: 'your-tenant-id',
  storage: 'auto'
});

export default async function BlogPost({ params }: { params: { slug: string } }) {
  // This runs on the server - uses memory storage
  const response = await GrowcadoSDK.getContent({
    modelIdentifier: 'blog-post',
    contentIdentifier: params.slug
  });

  if (response.error) {
    return <div>Error loading content</div>;
  }

  return (
    <div>
      <h1>{response.data.title}</h1>
      <div dangerouslySetInnerHTML={{ __html: response.data.content }} />
    </div>
  );
}
```

#### Client Component with Hydration

```typescript
// components/ClientTracker.tsx
'use client';

import { useEffect } from 'react';
import { GrowcadoSDK } from '@growcado/sdk';

export default function ClientTracker() {
  useEffect(() => {
    // Hydrate on client - migrates any server data to localStorage
    GrowcadoSDK.hydrate();
    
    // Set customer identifiers (persists after hydration)
    GrowcadoSDK.setCustomerIdentifiers({
      userId: 'user123',
      email: 'user@example.com'
    });
  }, []);

  return null; // This component handles tracking only
}
```

#### Root Layout

```typescript
// app/layout.tsx
import ClientTracker from './components/ClientTracker';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        {children}
        <ClientTracker />
      </body>
    </html>
  );
}
```

### Manual Hydration

If you prefer manual control over hydration:

```typescript
GrowcadoSDK.configure({
  tenantId: 'your-tenant-id',
  storage: 'auto',
  hydrateOnMount: false  // Disable automatic hydration
});

// Later, manually hydrate when ready
if (typeof window !== 'undefined') {
  GrowcadoSDK.hydrate();
}
```

### SSR Best Practices

1. **Use `storage: 'auto'`** - This handles environment detection automatically
2. **Configure once** - Call `GrowcadoSDK.configure()` once in your app initialization
3. **Hydrate on client** - Call `GrowcadoSDK.hydrate()` in a client-side useEffect or component
4. **Set identifiers after hydration** - Customer identifiers set on server will persist after hydration

### Environment Detection

The SDK automatically detects the environment:

```typescript
// Server environment (Node.js)
// - Uses memory storage
// - Skips browser-specific APIs
// - Content fetching works normally

// Client environment (Browser)  
// - Uses localStorage (if available)
// - Enables full tracking features
// - Migrates server data during hydration
```

### Error Handling in SSR

```typescript
const response = await GrowcadoSDK.getContent({...});

if (response.error) {
  // Handle gracefully in SSR
  console.error('Content fetch failed:', response.error.message);
  
  // Return fallback content or error page
  return <div>Content temporarily unavailable</div>;
}
```

## API Reference

### `GrowcadoSDK.configure(config: SDKConfig)`

Configures the SDK with your tenant settings and tracking preferences.

### `GrowcadoSDK.getContent<T>(config: ContentConfig)`

Fetches content from your Growcado CMS.

```typescript
interface ContentConfig {
  modelIdentifier: string;      // The content model identifier
  contentIdentifier: string;    // The specific content identifier
  tenantId?: string;           // Override tenant ID for this request
  headers?: Record<string, string>; // Additional headers
  cxpParameters?: CXPParameters; // Dynamic context parameters (see CXP Parameters section)
}
```

Returns a `GrowcadoResponse<T>` with either `data` or `error`.

### `GrowcadoSDK.setCustomerIdentifiers(identifiers: CustomerIdentifiers)`

Sets customer identification data for personalization and tracking.

```typescript
interface CustomerIdentifiers {
  email?: string;
  userId?: string;
  anonymousId?: string;
  [key: string]: string | undefined; // Custom identifiers
}
```

### `GrowcadoSDK.setUTMParameters(params: UTMParameters)`

Manually sets UTM parameters for tracking. This will override any existing UTM parameters.

```typescript
GrowcadoSDK.setUTMParameters({
  source: 'newsletter',
  medium: 'email',
  campaign: 'spring-sale',
  customParam: 'special-value'
});
```

### `GrowcadoSDK.getUTMParameters()`

Retrieves the current UTM parameters. Returns `UTMParameters | null`.

```typescript
const utmParams = GrowcadoSDK.getUTMParameters();
if (utmParams) {
  console.log('Current UTM source:', utmParams.source);
}
```

### `GrowcadoSDK.clearUTMParameters()`

Clears all stored UTM parameters.

```typescript
GrowcadoSDK.clearUTMParameters();
```

### `GrowcadoSDK.hydrate()`

Activates browser-specific features when transitioning from server-side to client-side rendering. This method automatically migrates data from memory storage to localStorage and enables full tracking capabilities.

```typescript
// Call during client-side hydration
GrowcadoSDK.hydrate();
```

### `GrowcadoSDK.setReferrer(referrer: string | ReferrerData)`

Manually sets referrer information for attribution tracking. This will override any existing referrer data.

```typescript
// Set as string
GrowcadoSDK.setReferrer('https://example.com/referring-page');

// Set as ReferrerData object
GrowcadoSDK.setReferrer({
  url: 'https://example.com/referring-page',
  domain: 'example.com'
});
```

### `GrowcadoSDK.getReferrer()`

Retrieves the current referrer information. Returns `string | null`.

```typescript
const referrer = GrowcadoSDK.getReferrer();
if (referrer) {
  console.log('Current referrer:', referrer);
}
```

### `GrowcadoSDK.clearReferrer()`

Clears all stored referrer information.

```typescript
GrowcadoSDK.clearReferrer();
```

## Features

### Automatic UTM Tracking
When `enableAutoUTM` is enabled, the SDK automatically captures and stores UTM parameters from the URL and includes them in API requests.

### Manual UTM Parameter Management
The SDK now provides methods for manually managing UTM parameters, giving you full control over UTM tracking:

```typescript
// Set UTM parameters manually
GrowcadoSDK.setUTMParameters({
  source: 'newsletter',
  medium: 'email',
  campaign: 'spring-sale',
  term: 'organic',
  content: 'header-cta'
});

// Get current UTM parameters
const currentUTM = GrowcadoSDK.getUTMParameters();
console.log(currentUTM); // { source: 'newsletter', medium: 'email', ... }

// Clear all UTM parameters
GrowcadoSDK.clearUTMParameters();

// Set custom UTM parameters
GrowcadoSDK.setUTMParameters({
  source: 'social',
  medium: 'facebook',
  customParam: 'special-offer'
});
```

#### UTM Parameters Interface
```typescript
interface UTMParameters {
  source?: string;      // Traffic source (e.g., 'google', 'newsletter')
  medium?: string;      // Marketing medium (e.g., 'cpc', 'email')
  campaign?: string;    // Campaign name (e.g., 'spring-sale')
  term?: string;        // Search term (e.g., 'organic')
  content?: string;     // Content identifier (e.g., 'header-cta')
  [key: string]: string | undefined; // Custom UTM parameters
}
```

### UTM Tracking in SSR Environments
The SDK handles UTM parameters intelligently during server-side rendering and hydration:

- **Server-side**: UTM parameters are preserved in memory storage
- **Client-side**: Parameters are automatically migrated to localStorage during hydration
- **Parameter Preservation**: Manually set UTM parameters are preserved during hydration and won't be overwritten by URL parameters unless new ones are present

```typescript
// Server-side (Next.js example)
GrowcadoSDK.configure({
  tenantId: 'your-tenant-id',
  enableAutoUTM: true
});

// Manually set UTM parameters on server
GrowcadoSDK.setUTMParameters({
  source: 'server',
  medium: 'ssr'
});

// Client-side hydration
useEffect(() => {
  GrowcadoSDK.hydrate(); // Preserves server-set parameters
  
  // Only overrides if new URL parameters are present
  // ?utm_source=google&utm_medium=cpc would update the parameters
}, []);
```

### Referrer Tracking  
When `enableReferrerTracking` is enabled, the SDK captures the initial referrer and includes it in API requests for attribution tracking.

### Manual Referrer Tracking
The SDK now provides methods for manually managing referrer information, giving you full control over referrer attribution:

```typescript
// Set referrer manually (string)
GrowcadoSDK.setReferrer('https://example.com/referring-page');

// Set referrer with additional data (ReferrerData object)
GrowcadoSDK.setReferrer({
  url: 'https://example.com/referring-page',
  domain: 'example.com'
});

// Get current referrer
const currentReferrer = GrowcadoSDK.getReferrer();
console.log(currentReferrer); // 'https://example.com/referring-page'

// Clear referrer
GrowcadoSDK.clearReferrer();
```

#### ReferrerData Interface
```typescript
interface ReferrerData {
  url: string;          // Referrer URL (required)
  domain?: string;      // Referrer domain (optional)
  [key: string]: string | undefined; // Custom referrer properties
}
```

### Referrer Tracking in SSR Environments
Similar to UTM tracking, the SDK handles referrer information intelligently during server-side rendering and hydration:

- **Server-side**: Referrer information is preserved in memory storage
- **Client-side**: Referrer data is automatically migrated to localStorage during hydration
- **Parameter Preservation**: Manually set referrer information is preserved during hydration and won't be overwritten by document.referrer unless no referrer was previously stored

```typescript
// Server-side (Next.js example)
GrowcadoSDK.configure({
  tenantId: 'your-tenant-id',
  enableReferrerTracking: true
});

// Manually set referrer on server
GrowcadoSDK.setReferrer('https://newsletter.example.com');

// Client-side hydration
useEffect(() => {
  GrowcadoSDK.hydrate(); // Preserves server-set referrer
  
  // Only captures document.referrer if no referrer was previously stored
}, []);
```

### Customer Identification
Set customer identifiers to enable personalized content delivery and customer journey tracking.

```typescript
GrowcadoSDK.setCustomerIdentifiers({
  email: 'user@example.com',
  userId: 'user123',
  customId: 'custom-value'
});
```

### CXP Parameters

CXP Parameters allow you to pass dynamic, context-specific data with each content request. This is ideal for scenarios where the context changes frequently, such as:

- Product pages (product title, price, SKU)
- Shopping cart context (cart total, item count)
- User session data (current page, interaction state)

Unlike customer identifiers which are persisted, CXP parameters are passed per-request and are not stored, making them perfect for highly dynamic data.

```typescript
// Fetch content with dynamic context
const response = await GrowcadoSDK.getContent({
  modelIdentifier: 'product-banner',
  contentIdentifier: 'hero',
  cxpParameters: {
    productTitle: 'iPhone 15 Pro',
    productPrice: '999',
    cartTotal: '1250.00',
    cartItems: '3'
  }
});
```

The parameters are sent via the `X-CXP-PARAMETERS` header in the same key-value format as other tracking headers.

#### CXPParameters Interface

```typescript
interface CXPParameters {
  [key: string]: string | undefined; // Any key-value pairs
}
```

#### Use Cases

**Product Page Personalization:**
```typescript
const response = await GrowcadoSDK.getContent({
  modelIdentifier: 'product-recommendation',
  contentIdentifier: 'similar-items',
  cxpParameters: {
    productId: 'SKU-12345',
    productCategory: 'electronics',
    productPrice: '599.99'
  }
});
```

**Cart-Based Content:**
```typescript
const response = await GrowcadoSDK.getContent({
  modelIdentifier: 'checkout-banner',
  contentIdentifier: 'upsell',
  cxpParameters: {
    cartTotal: cart.total.toString(),
    cartItemCount: cart.items.length.toString(),
    hasPromoCode: cart.promoCode ? 'true' : 'false'
  }
});
```

### TypeScript Support
The SDK is written in TypeScript and provides full type safety for all APIs.

## Error Handling

The SDK returns a standardized response format:

```typescript
interface GrowcadoResponse<T> {
  data?: T;              // Successful response data
  error?: {              // Error information
    message: string;
    code?: string | number;
    details?: any;
  };
}
```

Always check for errors in your response:

```typescript
const response = await GrowcadoSDK.getContent({...});

if (response.error) {
  // Handle error
  console.error('Failed to fetch content:', response.error.message);
  return;
}

// Use response.data safely
console.log(response.data);
```

## License

MIT