npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@solapi/crm-browser

v0.1.2

Published

SOLAPI CRM browser SDK for event tracking and record creation

Downloads

347

Readme

SOLAPI CRM Browser SDK

English | 한국어

A lightweight, production-ready JavaScript SDK for tracking user events and creating records in SOLAPI CRM from web browsers.

Features

  • Event Tracking: Track custom user events with flexible properties
  • Automatic Page View Tracking: Monitor user navigation in single-page applications (SPAs)
  • Record Creation: Create CRM records directly from your frontend
  • Session Identification: Associate anonymous sessions with known records
  • Super Properties: Register global properties sent with every event
  • Automatic Batching: Queue and batch events for efficient network usage
  • Offline Resilience: Queue events locally when network is unavailable
  • GDPR Compliant: Full opt-in/opt-out tracking control
  • TypeScript Support: Full type definitions included
  • Zero Dependencies: Minimal bundle size (~2KB minified+gzipped)
  • Security: HTTPS-only, prototype pollution prevention, cryptographic ID generation

Installation

npm

npm install @solapi/crm-browser

Yarn

yarn add @solapi/crm-browser

pnpm

pnpm add @solapi/crm-browser

CDN (Script Tag)

<script src="https://cdn.solapi.com/mfe/sdk/crm-sdk/solapi-crm.min.js"></script>
<script>
  SolapiCRM.init({ trackingKey: 'tk_live_...' });
</script>

Quick Start

1. Initialize the SDK

Always call init() first with your tracking key:

import SolapiCRM from '@solapi/crm-browser';

SolapiCRM.init({
  trackingKey: 'tk_live_your_tracking_key_here',
  entityId: 'entity_abc123',  // Optional: default entity for events
  autoPageView: true,          // Optional: auto-track page views
  debug: true,                 // Optional: enable debug logging
});

Required: trackingKeySOLAPI Console > CRM > Settings > SDK에서 발급

2. Track Events

// Simple event
SolapiCRM.track('signup');

// Event with properties
SolapiCRM.track('purchase', {
  product: 'Premium Plan',
  amount: 79000,
  currency: 'KRW',
});

// Event with analytics options
SolapiCRM.track('button_click', {}, {
  category: 'engagement',
  label: 'subscribe_button',
  value: 1,
});

3. Identify Users

Once a user logs in, associate the session with their CRM record:

// After user login
const record = await SolapiCRM.createRecord({
  name: 'John Doe',
  data: { email: '[email protected]' },
  tags: ['premium'],
});

// Associate session with the record
await SolapiCRM.identify(record.recordId, {
  email: '[email protected]',
  plan: 'premium',
});

4. Register Super Properties

Send the same properties with every event:

SolapiCRM.register({
  plan: 'premium',
  region: 'KR',
  appVersion: '1.2.0',
});

Configuration

The init() method accepts a configuration object:

interface SolapiCRMConfig {
  // Required
  trackingKey: string;

  // Optional
  entityId?: string;                // Default entity ID for events/records
  endpoint?: string;                // API endpoint (default: https://api.solapi.com/crm-core/v1/sdk)
  autoPageView?: boolean;           // Auto track page views on init (default: false)
  batchSize?: number;               // Events to queue before auto-flush (default: 10)
  flushInterval?: number;           // Auto-flush interval in milliseconds (default: 5000)
  maxQueueSize?: number;            // Max queued events before dropping oldest (default: 1000)
  debug?: boolean;                  // Enable debug logging (default: false)
}

Configuration Examples

Production Setup:

SolapiCRM.init({
  trackingKey: 'tk_live_xxxxxxxx',
  entityId: 'entity_main_app',
  batchSize: 20,        // Larger batches for efficiency
  flushInterval: 10000, // Less frequent flushes
});

Development with Auto-Tracking:

SolapiCRM.init({
  trackingKey: 'tk_live_xxxxxxxx',
  autoPageView: true,
  debug: true,          // See all events logged
});

API Reference

init(config)

Initialize the SDK with a configuration object.

SolapiCRM.init({
  trackingKey: 'tk_live_xxxxxxxx',
  entityId: 'entity_abc123',
  autoPageView: true,
});

Throws: Error if trackingKey is missing or invalid.

Note: Calling init() again will clean up and restart the SDK instance.


track(eventType, properties?, options?)

Track a custom event.

SolapiCRM.track('button_click', {
  buttonId: 'submit_btn',
  pageSection: 'checkout',
}, {
  category: 'engagement',
  label: 'checkout_submit',
  value: 1,
});

Parameters:

  • eventType (string, required): Event name (e.g., 'purchase', 'login')
  • properties (object, optional): Custom event data
  • options (object, optional):
    • category (string): Event category for analytics
    • label (string): Event label
    • value (number): Numeric value for the event
    • recordId (string): Override the current record ID for this event only

Throws: Error if SDK not initialized or eventType is empty.

Behavior: If opt-out is active, event is silently discarded.


trackPageView()

Manually track a page view.

SolapiCRM.trackPageView();

Behavior:

  • Captures current URL, path, page title, and referrer
  • Sent as an event with eventType: 'PAGE_VIEW'
  • Useful for manual tracking in non-SPA apps

Throws: Error if SDK not initialized.


enableAutoPageView()

Install automatic SPA route change tracking.

SolapiCRM.init({ trackingKey: 'tk_live_...' });
SolapiCRM.enableAutoPageView();

Behavior:

  • Patches history.pushState() and history.replaceState()
  • Listens for popstate events (browser back/forward)
  • Tracks a new page view when routes change
  • Safe to call multiple times (installs only once)

createRecord(data)

Create a new CRM record.

const record = await SolapiCRM.createRecord({
  entityId: 'entity_customers',
  name: 'John Doe',
  data: {
    email: '[email protected]',
    phone: '010-1234-5678',
    source: 'web_signup',
  },
  tags: ['vip', 'early_adopter'],
});

console.log(record.recordId); // 'record_xyz789'
console.log(record.entityId); // 'entity_customers'
console.log(record.name);     // 'John Doe'

Parameters:

  • entityId (string, optional): Entity type for the record (defaults to config entityId)
  • name (string, required): Record name (e.g., user name, company name)
  • data (object, optional): Custom record fields
  • tags (string[], optional): Labels for organizing records

Returns: Promise<CreateRecordResponse>

interface CreateRecordResponse {
  recordId: string;  // Unique record ID
  entityId: string;  // Entity type
  name: string;      // Record name
  [key: string]: any; // Additional response fields
}

Throws: Error if SDK not initialized or name is missing.


identify(recordId, traits?)

Associate the current session with a known CRM record.

// Basic identification
await SolapiCRM.identify('record_abc123');

// With trait updates
await SolapiCRM.identify('record_abc123', {
  email: '[email protected]',
  plan: 'premium',
  lastLogin: new Date().toISOString(),
});

Parameters:

  • recordId (string, required): The CRM record ID to identify as
  • traits (object, optional): User properties to update on the record

Behavior:

  • Merges all previous anonymous events to this record (server-side)
  • Persists the record ID to localStorage for session continuity
  • Network failures are logged but never throw (safe for host app)
  • If switching from one known ID to another, sends identify request with traits

Throws: Error if SDK not initialized or recordId is empty.

Best Practice: Call this after user login or when record is known.


register(properties)

Register super properties sent with every subsequent event.

SolapiCRM.register({
  plan: 'premium',
  region: 'KR',
  userAgent: navigator.userAgent,
});

SolapiCRM.track('purchase', { amount: 79000 });
// Sent event includes: plan, region, userAgent, amount

Parameters:

  • properties (object): Key-value pairs to attach to all future events

Behavior:

  • Properties persist in memory only (cleared on page reload or reset())
  • Later calls overwrite earlier values for duplicate keys
  • Prototype pollution safe (blocks __proto__, constructor, prototype)

registerOnce(properties)

Register super properties, skipping keys that are already set.

SolapiCRM.registerOnce({
  firstVisit: new Date().toISOString(),
  source: 'google_ads',
});

// Later calls don't overwrite these
SolapiCRM.registerOnce({
  firstVisit: '2099-01-01', // Skipped (already set)
  source: 'facebook_ads',   // Skipped (already set)
});

Parameters:

  • properties (object): Key-value pairs to set if not already present

unregister(propertyName)

Remove a super property.

SolapiCRM.unregister('tempFlag');

Parameters:

  • propertyName (string): The key to remove

optOutTracking()

Opt out of all event tracking.

SolapiCRM.optOutTracking();
// No events are sent until optInTracking() is called

Behavior:

  • All track() calls silently return
  • Event queue timer stops
  • Persists in memory (not stored; resets on page reload)

Use Case: Respect user privacy preferences or GDPR consent.


optInTracking()

Resume event tracking after opting out.

SolapiCRM.optInTracking();

Behavior:

  • Resumes accepting and sending events
  • Restarts the event queue timer

hasOptedOut()

Check the current opt-out status.

if (SolapiCRM.hasOptedOut()) {
  console.log('Tracking is disabled');
}

Returns: booleantrue if opted out, false if tracking is active.


reset()

Reset the user session identity.

// On user logout
SolapiCRM.reset();

Behavior:

  • Generates a new anonymous ID
  • Clears all super properties
  • Clears persisted record ID from localStorage
  • Future events sent with the new anonymous ID

Use Case: Handle user logout or session reset.


flush()

Immediately send all queued events to the server.

await SolapiCRM.flush();

Behavior:

  • Bypasses the automatic flush interval
  • Retries up to 3 times with exponential backoff (1s, 2s, 4s)
  • Batches events in groups of up to 100
  • Dropped on persistent network failure after all retries

Use Case: Ensure events are sent before page unload (handled automatically).


destroy()

Clean up and destroy the SDK instance.

await SolapiCRM.destroy();

Behavior:

  • Flushes all remaining queued events
  • Restores original history.pushState/replaceState if patched
  • Removes all event listeners
  • Stops timers
  • Clears internal state

Use Case: Cleanup in single-page app route changes or app unmount.


Common Patterns

React Integration

// App.tsx
import { useEffect } from 'react';
import SolapiCRM from '@solapi/crm-browser';

export function App() {
  useEffect(() => {
    // Initialize on mount
    SolapiCRM.init({
      trackingKey: import.meta.env.VITE_TRACKING_KEY,
      entityId: 'entity_web_app',
      autoPageView: true,
    });

    // Cleanup on unmount
    return () => {
      SolapiCRM.destroy();
    };
  }, []);

  return <div>Your app</div>;
}

User Authentication Flow

// After successful login
const user = await loginUser(credentials);

const record = await SolapiCRM.createRecord({
  name: user.fullName,
  data: {
    email: user.email,
    role: user.role,
  },
});

await SolapiCRM.identify(record.recordId, {
  email: user.email,
  role: user.role,
});

// Now all events are tied to this user
SolapiCRM.track('login_success');

SPA Route Navigation

// In your router (e.g., React Router)
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import SolapiCRM from '@solapi/crm-browser';

export function RouteTracker() {
  const location = useLocation();

  useEffect(() => {
    // Manual page view on each route change
    SolapiCRM.trackPageView();
  }, [location]);

  return null;
}

// Or use enableAutoPageView() for automatic tracking
SolapiCRM.enableAutoPageView();

GDPR Opt-Out Consent Banner

function ConsentBanner() {
  const handleOptOut = () => {
    localStorage.setItem('tracking_consent', 'denied');
    SolapiCRM.optOutTracking();
  };

  const handleOptIn = () => {
    localStorage.setItem('tracking_consent', 'accepted');
    SolapiCRM.optInTracking();
  };

  return (
    <div className="consent-banner">
      <p>We use analytics to improve your experience.</p>
      <button onClick={handleOptOut}>Opt Out</button>
      <button onClick={handleOptIn}>Accept</button>
    </div>
  );
}

// On app load, respect stored consent
const consent = localStorage.getItem('tracking_consent');
if (consent === 'denied') {
  SolapiCRM.optOutTracking();
}

Vue.js Integration

// main.ts
import { createApp } from 'vue';
import SolapiCRM from '@solapi/crm-browser';
import App from './App.vue';

const app = createApp(App);

SolapiCRM.init({
  trackingKey: import.meta.env.VITE_TRACKING_KEY,
  entityId: 'entity_vue_app',
});

app.config.globalProperties.$crm = SolapiCRM;
app.mount('#app');

Event Properties Best Practices

// Good: Clear, lowercase keys, minimal nesting
SolapiCRM.track('add_to_cart', {
  product_id: '123',
  product_name: 'Laptop',
  price: 1500000,
  currency: 'KRW',
  quantity: 1,
});

// Avoid: Nested objects, inconsistent naming, excessive data
SolapiCRM.track('add_to_cart', {
  product: {
    id: '123',
    name: 'Laptop',
    pricing: {
      amount: 1500000,
    },
  },
  ProductQuantity: 1,  // Inconsistent casing
});

Browser Compatibility

The SDK requires modern browser APIs. Minimum support:

| Browser | Minimum Version | |---------|-----------------| | Chrome | 57+ | | Firefox | 52+ | | Safari | 11+ | | Edge | 79+ | | iOS Safari | 11+ | | Android Chrome | 57+ |

Required APIs:

  • fetch()
  • crypto.getRandomValues()
  • localStorage (optional; falls back gracefully)
  • history.pushState/replaceState (for SPA tracking)

Graceful Fallbacks:

  • If localStorage is unavailable (private mode, iframe sandbox), IDs are generated per-session
  • If historical API patching fails, manual trackPageView() calls still work
  • Network failures are caught and logged; never crash the host app

TypeScript Support

Full TypeScript support with exported types:

import type { SolapiCRMConfig, TrackEvent } from '@solapi/crm-browser';
import SolapiCRM from '@solapi/crm-browser';

const config: SolapiCRMConfig = {
  trackingKey: 'tk_live_...',
  entityId: 'entity_main',
  debug: true,
};

SolapiCRM.init(config);

interface CustomEvent extends TrackEvent {
  customProperty: string;
}

Debug Mode

Enable detailed logging to troubleshoot issues:

SolapiCRM.init({
  trackingKey: 'tk_live_...',
  debug: true,
});

// Console output:
// [SolapiCRM] Event queued: purchase
// [SolapiCRM] Flushing 5 events...
// [SolapiCRM] Events sent successfully

Security Considerations

  • HTTPS Only: SDK enforces HTTPS transport (localhost exempted for development)
  • Tracking Key Security: Never expose trackingKey on public repositories. Use environment variables.
  • Prototype Pollution: Property keys like __proto__, constructor, prototype are automatically blocked
  • ID Generation: Uses crypto.getRandomValues() with rejection sampling for cryptographically secure IDs
  • No Sensitive Data: Avoid sending passwords, credit card numbers, or PII in event properties

Best Practice:

// Store tracking key in environment variables
const config = {
  trackingKey: process.env.REACT_APP_TRACKING_KEY,
};

// Don't do this:
const config = {
  trackingKey: 'tk_live_hardcoded_key', // Visible in source code
};

Performance Tips

  1. Batch Events: Queue events and flush periodically instead of individual requests
  2. Limit Super Properties: Register only essential global properties
  3. Use Appropriate Batch Size: Balance between network overhead and queue memory
SolapiCRM.init({
  trackingKey: 'tk_live_...',
  batchSize: 20,        // Flush after 20 events (default: 10)
  flushInterval: 10000, // Flush every 10 seconds (default: 5s)
});
  1. Clean Up: Call destroy() when SDK is no longer needed

Storage

The SDK uses browser localStorage to persist:

| Key | Value | Purpose | |-----|-------|---------| | _solapi_anon_id | Randomly generated ID | Persistent anonymous user ID | | _solapi_record_id | Record ID from identify() | Persistent identified user |

Note: Storage is not used if localStorage is unavailable (e.g., private mode, iframe with sandbox).

Error Handling

The SDK is designed to never break the host application:

try {
  // Always safe — errors are caught and logged
  SolapiCRM.track('event_name', { customData: true });
} catch (error) {
  // This rarely happens (only on init)
  console.error('SolapiCRM error:', error);
}

Network Failures: Events are retried up to 3 times with exponential backoff. On persistent failure, events are dropped and logged (if debug is enabled).

Initialization: Only init() throws errors if required config is missing.

Changelog

0.1.0 (Initial Release)

  • Event tracking with custom properties
  • Page view tracking for SPAs
  • Record creation and identification
  • Super properties support
  • Opt-in/opt-out GDPR compliance
  • Automatic batching and retry logic
  • TypeScript support
  • Browser SDK distribution (IIFE + ESM)

License

MIT



한국어

SOLAPI CRM 브라우저 SDK

웹 브라우저에서 SOLAPI CRM의 사용자 이벤트를 추적하고 레코드를 생성하기 위한 가볍고 본격적인 JavaScript SDK입니다.

주요 기능

  • 이벤트 추적: 유연한 속성을 가진 사용자 정의 이벤트 추적
  • 자동 페이지 뷰 추적: 단일 페이지 애플리케이션(SPA)의 사용자 네비게이션 모니터링
  • 레코드 생성: 프론트엔드에서 직접 CRM 레코드 생성
  • 세션 식별: 익명 세션을 알려진 레코드와 연결
  • 슈퍼 속성: 모든 이벤트와 함께 전송되는 전역 속성 등록
  • 자동 배칭: 효율적인 네트워크 사용을 위한 이벤트 큐잉 및 배칭
  • 오프라인 복원력: 네트워크를 사용할 수 없을 때 로컬에서 이벤트 대기
  • GDPR 준수: 추적의 전체 옵트인/옵트아웃 제어
  • TypeScript 지원: 완전한 타입 정의 포함
  • 의존성 없음: 최소 번들 크기 (~2KB minified+gzipped)
  • 보안: HTTPS만 지원, 프로토타입 오염 방지, 암호학적 ID 생성

설치

npm

npm install @solapi/crm-browser

Yarn

yarn add @solapi/crm-browser

pnpm

pnpm add @solapi/crm-browser

CDN (Script 태그)

<script src="https://cdn.solapi.com/mfe/sdk/crm-sdk/solapi-crm.min.js"></script>
<script>
  SolapiCRM.init({ trackingKey: 'tk_live_...' });
</script>

빠른 시작

1. SDK 초기화

항상 추적 키를 사용하여 먼저 init()을 호출합니다:

import SolapiCRM from '@solapi/crm-browser';

SolapiCRM.init({
  trackingKey: 'tk_live_your_tracking_key_here',
  entityId: 'entity_abc123',  // 선택사항: 이벤트 기본 개체
  autoPageView: true,          // 선택사항: 페이지 뷰 자동 추적
  debug: true,                 // 선택사항: 디버그 로깅 활성화
});

필수: trackingKeySOLAPI 콘솔 > CRM > 설정 > SDK에서 발급

2. 이벤트 추적

// 간단한 이벤트
SolapiCRM.track('signup');

// 속성이 있는 이벤트
SolapiCRM.track('purchase', {
  product: 'Premium Plan',
  amount: 79000,
  currency: 'KRW',
});

// 분석 옵션이 있는 이벤트
SolapiCRM.track('button_click', {}, {
  category: 'engagement',
  label: 'subscribe_button',
  value: 1,
});

3. 사용자 식별

사용자 로그인 후 세션을 CRM 레코드와 연결합니다:

// 사용자 로그인 후
const record = await SolapiCRM.createRecord({
  name: 'John Doe',
  data: { email: '[email protected]' },
  tags: ['premium'],
});

// 세션을 레코드와 연결
await SolapiCRM.identify(record.recordId, {
  email: '[email protected]',
  plan: 'premium',
});

4. 슈퍼 속성 등록

모든 이벤트와 함께 동일한 속성을 전송합니다:

SolapiCRM.register({
  plan: 'premium',
  region: 'KR',
  appVersion: '1.2.0',
});

설정

init() 메서드는 설정 객체를 허용합니다:

interface SolapiCRMConfig {
  // 필수
  trackingKey: string;

  // 선택사항
  entityId?: string;                // 이벤트/레코드의 기본 개체 ID
  endpoint?: string;                // API 엔드포인트 (기본값: https://api.solapi.com/crm-core/v1/sdk)
  autoPageView?: boolean;           // init 시 자동으로 페이지 뷰 추적 (기본값: false)
  batchSize?: number;               // 자동 플러시 전 큐잉할 이벤트 수 (기본값: 10)
  flushInterval?: number;           // 자동 플러시 간격(밀리초) (기본값: 5000)
  maxQueueSize?: number;            // 최대 큐 크기, 초과 시 가장 오래된 항목 삭제 (기본값: 1000)
  debug?: boolean;                  // 디버그 로깅 활성화 (기본값: false)
}

설정 예제

프로덕션 설정:

SolapiCRM.init({
  trackingKey: 'tk_live_xxxxxxxx',
  entityId: 'entity_main_app',
  batchSize: 20,        // 효율성을 위한 더 큰 배치
  flushInterval: 10000, // 덜 빈번한 플러시
});

자동 추적을 사용한 개발:

SolapiCRM.init({
  trackingKey: 'tk_live_xxxxxxxx',
  autoPageView: true,
  debug: true,          // 모든 이벤트 로깅 확인
});

API 참조

init(config)

설정 객체로 SDK를 초기화합니다.

SolapiCRM.init({
  trackingKey: 'tk_live_xxxxxxxx',
  entityId: 'entity_abc123',
  autoPageView: true,
});

예외: trackingKey가 누락되거나 유효하지 않으면 Error 발생.

참고: init()을 다시 호출하면 이전 인스턴스를 정리하고 다시 시작합니다.


track(eventType, properties?, options?)

사용자 정의 이벤트를 추적합니다.

SolapiCRM.track('button_click', {
  buttonId: 'submit_btn',
  pageSection: 'checkout',
}, {
  category: 'engagement',
  label: 'checkout_submit',
  value: 1,
});

매개변수:

  • eventType (문자열, 필수): 이벤트 이름 (예: 'purchase', 'login')
  • properties (객체, 선택): 사용자 정의 이벤트 데이터
  • options (객체, 선택):
    • category (문자열): 분석을 위한 이벤트 카테고리
    • label (문자열): 이벤트 레이블
    • value (숫자): 이벤트의 수치 값
    • recordId (문자열): 이 이벤트만 현재 레코드 ID 무시

예외: SDK가 초기화되지 않았거나 eventType이 비어있으면 Error 발생.

동작: 옵트아웃이 활성화되면 이벤트가 자동으로 삭제됩니다.


trackPageView()

페이지 뷰를 수동으로 추적합니다.

SolapiCRM.trackPageView();

동작:

  • 현재 URL, 경로, 페이지 제목 및 리퍼러를 캡처합니다
  • eventType: 'PAGE_VIEW'인 이벤트로 전송됩니다
  • 비 SPA 앱의 수동 추적에 유용합니다

예외: SDK가 초기화되지 않으면 Error 발생.


enableAutoPageView()

자동 SPA 라우트 변경 추적을 설치합니다.

SolapiCRM.init({ trackingKey: 'tk_live_...' });
SolapiCRM.enableAutoPageView();

동작:

  • history.pushState()history.replaceState() 패치
  • popstate 이벤트 리스너 (브라우저 뒤로/앞으로)
  • 라우트 변경 시 새로운 페이지 뷰 추적
  • 여러 번 호출해도 안전 (한 번만 설치)

createRecord(data)

새로운 CRM 레코드를 생성합니다.

const record = await SolapiCRM.createRecord({
  entityId: 'entity_customers',
  name: 'John Doe',
  data: {
    email: '[email protected]',
    phone: '010-1234-5678',
    source: 'web_signup',
  },
  tags: ['vip', 'early_adopter'],
});

console.log(record.recordId); // 'record_xyz789'
console.log(record.entityId); // 'entity_customers'
console.log(record.name);     // 'John Doe'

매개변수:

  • entityId (문자열, 선택): 레코드의 개체 유형 (설정 entityId 기본값)
  • name (문자열, 필수): 레코드 이름 (예: 사용자 이름, 회사명)
  • data (객체, 선택): 사용자 정의 레코드 필드
  • tags (문자열[], 선택): 레코드 구성을 위한 레이블

반환: Promise<CreateRecordResponse>

interface CreateRecordResponse {
  recordId: string;  // 고유 레코드 ID
  entityId: string;  // 개체 유형
  name: string;      // 레코드 이름
  [key: string]: any; // 추가 응답 필드
}

예외: SDK가 초기화되지 않았거나 name이 누락되면 Error 발생.


identify(recordId, traits?)

현재 세션을 알려진 CRM 레코드와 연결합니다.

// 기본 식별
await SolapiCRM.identify('record_abc123');

// 특성 업데이트 포함
await SolapiCRM.identify('record_abc123', {
  email: '[email protected]',
  plan: 'premium',
  lastLogin: new Date().toISOString(),
});

매개변수:

  • recordId (문자열, 필수): 식별할 CRM 레코드 ID
  • traits (객체, 선택): 레코드에 업데이트할 사용자 속성

동작:

  • 모든 이전 익명 이벤트를 이 레코드로 병합 (서버 쪽)
  • localStorage에 레코드 ID를 저장하여 세션 연속성 유지
  • 네트워크 실패는 로깅되지만 절대 발생하지 않음 (호스트 앱 안전)
  • 알려진 ID에서 다른 ID로 전환 시 특성과 함께 식별 요청 전송

예외: SDK가 초기화되지 않았거나 recordId가 비어있으면 Error 발생.

모범 사례: 사용자 로그인 후 또는 레코드가 알려져 있을 때 호출합니다.


register(properties)

모든 후속 이벤트와 함께 전송되는 슈퍼 속성을 등록합니다.

SolapiCRM.register({
  plan: 'premium',
  region: 'KR',
  userAgent: navigator.userAgent,
});

SolapiCRM.track('purchase', { amount: 79000 });
// 전송된 이벤트는 다음을 포함: plan, region, userAgent, amount

매개변수:

  • properties (객체): 모든 향후 이벤트에 첨부할 키-값 쌍

동작:

  • 속성은 메모리에만 저장됨 (페이지 새로고침 또는 reset() 시 삭제)
  • 이후 호출이 중복 키에 대한 이전 값을 덮어씁니다
  • 프로토타입 오염 안전 (__proto__, constructor, prototype 차단)

registerOnce(properties)

슈퍼 속성을 등록하되, 이미 설정된 키를 건너뜁니다.

SolapiCRM.registerOnce({
  firstVisit: new Date().toISOString(),
  source: 'google_ads',
});

// 이후 호출은 이를 덮어쓰지 않음
SolapiCRM.registerOnce({
  firstVisit: '2099-01-01', // 건너뜀 (이미 설정됨)
  source: 'facebook_ads',   // 건너뜀 (이미 설정됨)
});

매개변수:

  • properties (객체): 아직 존재하지 않으면 설정할 키-값 쌍

unregister(propertyName)

슈퍼 속성을 제거합니다.

SolapiCRM.unregister('tempFlag');

매개변수:

  • propertyName (문자열): 제거할 키

optOutTracking()

모든 이벤트 추적을 옵트아웃합니다.

SolapiCRM.optOutTracking();
// optInTracking()을 호출할 때까지 이벤트가 전송되지 않음

동작:

  • 모든 track() 호출이 자동으로 반환됨
  • 이벤트 큐 타이머 중지
  • 메모리에 저장됨 (저장 안 함; 페이지 새로고침 시 재설정)

사용 사례: 사용자 개인정보 보호 설정 또는 GDPR 동의 존중.


optInTracking()

옵트아웃 후 이벤트 추적을 재개합니다.

SolapiCRM.optInTracking();

동작:

  • 이벤트 수락 및 전송 재개
  • 이벤트 큐 타이머 재시작

hasOptedOut()

현재 옵트아웃 상태를 확인합니다.

if (SolapiCRM.hasOptedOut()) {
  console.log('추적이 비활성화됨');
}

반환: boolean — 옵트아웃된 경우 true, 추적이 활성인 경우 false.


reset()

사용자 세션 식별을 재설정합니다.

// 사용자 로그아웃 시
SolapiCRM.reset();

동작:

  • 새로운 익명 ID 생성
  • 모든 슈퍼 속성 삭제
  • localStorage에서 지속된 레코드 ID 삭제
  • 향후 이벤트는 새 익명 ID로 전송됨

사용 사례: 사용자 로그아웃 또는 세션 재설정 처리.


flush()

모든 큐 이벤트를 즉시 서버로 전송합니다.

await SolapiCRM.flush();

동작:

  • 자동 플러시 간격 무시
  • 모든 재시도 후 지수 백오프 최대 3회 재시도 (1초, 2초, 4초)
  • 이벤트를 최대 100개 그룹으로 배치
  • 모든 재시도 후 지속적 네트워크 오류 시 삭제

사용 사례: 페이지 언로드 전에 이벤트가 전송되는지 확인 (자동 처리됨).


destroy()

SDK 인스턴스를 정리하고 삭제합니다.

await SolapiCRM.destroy();

동작:

  • 모든 남은 큐 이벤트 플러시
  • 패치된 경우 원본 history.pushState/replaceState 복원
  • 모든 이벤트 리스너 제거
  • 타이머 중지
  • 내부 상태 삭제

사용 사례: 단일 페이지 앱 라우트 변경 또는 앱 언마운트 시 정리.


일반적인 패턴

React 통합

// App.tsx
import { useEffect } from 'react';
import SolapiCRM from '@solapi/crm-browser';

export function App() {
  useEffect(() => {
    // 마운트 시 초기화
    SolapiCRM.init({
      trackingKey: import.meta.env.VITE_TRACKING_KEY,
      entityId: 'entity_web_app',
      autoPageView: true,
    });

    // 언마운트 시 정리
    return () => {
      SolapiCRM.destroy();
    };
  }, []);

  return <div>Your app</div>;
}

사용자 인증 흐름

// 성공적인 로그인 후
const user = await loginUser(credentials);

const record = await SolapiCRM.createRecord({
  name: user.fullName,
  data: {
    email: user.email,
    role: user.role,
  },
});

await SolapiCRM.identify(record.recordId, {
  email: user.email,
  role: user.role,
});

// 이제 모든 이벤트가 이 사용자와 연결됨
SolapiCRM.track('login_success');

SPA 라우트 네비게이션

// 라우터에서 (예: React Router)
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import SolapiCRM from '@solapi/crm-browser';

export function RouteTracker() {
  const location = useLocation();

  useEffect(() => {
    // 각 라우트 변경 시 수동 페이지 뷰
    SolapiCRM.trackPageView();
  }, [location]);

  return null;
}

// 또는 자동 추적을 위해 enableAutoPageView() 사용
SolapiCRM.enableAutoPageView();

GDPR 옵트아웃 동의 배너

function ConsentBanner() {
  const handleOptOut = () => {
    localStorage.setItem('tracking_consent', 'denied');
    SolapiCRM.optOutTracking();
  };

  const handleOptIn = () => {
    localStorage.setItem('tracking_consent', 'accepted');
    SolapiCRM.optInTracking();
  };

  return (
    <div className="consent-banner">
      <p>분석을 통해 환경을 개선하기 위해 사용합니다.</p>
      <button onClick={handleOptOut}>거부</button>
      <button onClick={handleOptIn}>승인</button>
    </div>
  );
}

// 앱 로드 시 저장된 동의 존중
const consent = localStorage.getItem('tracking_consent');
if (consent === 'denied') {
  SolapiCRM.optOutTracking();
}

Vue.js 통합

// main.ts
import { createApp } from 'vue';
import SolapiCRM from '@solapi/crm-browser';
import App from './App.vue';

const app = createApp(App);

SolapiCRM.init({
  trackingKey: import.meta.env.VITE_TRACKING_KEY,
  entityId: 'entity_vue_app',
});

app.config.globalProperties.$crm = SolapiCRM;
app.mount('#app');

이벤트 속성 모범 사례

// 좋은 예: 명확한 소문자 키, 최소 중첩
SolapiCRM.track('add_to_cart', {
  product_id: '123',
  product_name: 'Laptop',
  price: 1500000,
  currency: 'KRW',
  quantity: 1,
});

// 피해야 할 것: 중첩 객체, 일관성 없는 이름, 과도한 데이터
SolapiCRM.track('add_to_cart', {
  product: {
    id: '123',
    name: 'Laptop',
    pricing: {
      amount: 1500000,
    },
  },
  ProductQuantity: 1,  // 일관성 없는 대소문자
});

브라우저 호환성

SDK는 최신 브라우저 API가 필요합니다. 최소 지원:

| 브라우저 | 최소 버전 | |---------|----------| | Chrome | 57+ | | Firefox | 52+ | | Safari | 11+ | | Edge | 79+ | | iOS Safari | 11+ | | Android Chrome | 57+ |

필수 API:

  • fetch()
  • crypto.getRandomValues()
  • localStorage (선택; 우아하게 폴백)
  • history.pushState/replaceState (SPA 추적용)

우아한 폴백:

  • localStorage를 사용할 수 없으면 (개인 모드, iframe 샌드박스) ID는 세션별로 생성됨
  • 기록 API 패칭이 실패하면 수동 trackPageView() 호출은 여전히 작동
  • 네트워크 오류는 포착되고 로깅됨; 호스트 앱이 절대 충돌하지 않음

TypeScript 지원

내보낸 타입의 완전한 TypeScript 지원:

import type { SolapiCRMConfig, TrackEvent } from '@solapi/crm-browser';
import SolapiCRM from '@solapi/crm-browser';

const config: SolapiCRMConfig = {
  trackingKey: 'tk_live_...',
  entityId: 'entity_main',
  debug: true,
};

SolapiCRM.init(config);

interface CustomEvent extends TrackEvent {
  customProperty: string;
}

디버그 모드

문제를 해결하기 위해 자세한 로깅을 활성화합니다:

SolapiCRM.init({
  trackingKey: 'tk_live_...',
  debug: true,
});

// 콘솔 출력:
// [SolapiCRM] Event queued: purchase
// [SolapiCRM] Flushing 5 events...
// [SolapiCRM] Events sent successfully

보안 고려사항

  • HTTPS만: SDK는 HTTPS 전송을 강제함 (개발용 localhost 제외)
  • 추적 키 보안: 공개 저장소에 trackingKey를 노출하지 마세요. 환경 변수를 사용하세요.
  • 프로토타입 오염: __proto__, constructor, prototype 같은 속성 키는 자동으로 차단됨
  • ID 생성: 암호학적으로 안전한 ID를 위해 crypto.getRandomValues()를 거부 샘플링과 함께 사용
  • 민감한 데이터 없음: 이벤트 속성에 비밀번호, 신용카드 번호 또는 PII를 보내지 마세요

모범 사례:

// 환경 변수에 추적 키 저장
const config = {
  trackingKey: process.env.REACT_APP_TRACKING_KEY,
};

// 이렇게 하지 마세요:
const config = {
  trackingKey: 'tk_live_hardcoded_key', // 소스 코드에 표시됨
};

성능 팁

  1. 배치 이벤트: 개별 요청 대신 이벤트를 큐잉하고 주기적으로 플러시합니다
  2. 슈퍼 속성 제한: 필수 전역 속성만 등록합니다
  3. 적절한 배치 크기 사용: 네트워크 오버헤드와 큐 메모리의 균형
SolapiCRM.init({
  trackingKey: 'tk_live_...',
  batchSize: 20,        // 20개 이벤트 후 플러시 (기본값: 10)
  flushInterval: 10000, // 10초마다 플러시 (기본값: 5초)
});
  1. 정리: SDK가 더 이상 필요하지 않을 때 destroy() 호출

저장소

SDK는 브라우저 localStorage를 사용하여 다음을 저장합니다:

| 키 | 값 | 목적 | |----|------|------| | _solapi_anon_id | 무작위로 생성된 ID | 지속적 익명 사용자 ID | | _solapi_record_id | identify()의 레코드 ID | 지속적 식별 사용자 |

참고: localStorage를 사용할 수 없으면 저장소가 사용되지 않음 (예: 개인 모드, 샌드박스가 있는 iframe).

오류 처리

SDK는 호스트 애플리케이션을 절대 손상시키지 않도록 설계되었습니다:

try {
  // 항상 안전 — 오류는 포착되고 로깅됨
  SolapiCRM.track('event_name', { customData: true });
} catch (error) {
  // 이것은 드물게 발생 (init에서만)
  console.error('SolapiCRM error:', error);
}

네트워크 실패: 이벤트는 지수 백오프로 최대 3회 재시도됩니다. 지속적 실패 시 이벤트가 삭제되고 로깅됨 (디버그가 활성화된 경우).

초기화: trackingKey가 누락된 경우에만 init()이 오류를 발생시킵니다.

변경 로그

0.1.0 (초기 릴리스)

  • 사용자 정의 속성을 사용한 이벤트 추적
  • SPA를 위한 페이지 뷰 추적
  • 레코드 생성 및 식별
  • 슈퍼 속성 지원
  • GDPR 준수 옵트인/옵트아웃
  • 자동 배칭 및 재시도 로직
  • TypeScript 지원
  • 브라우저 SDK 배포 (IIFE + ESM)

라이선스

MIT