create-destination

Use when creating a new walkerOS destination. Example-driven workflow starting with research and examples before implementation.

$ Installieren

git clone https://github.com/elbwalker/walkerOS /tmp/walkerOS && cp -r /tmp/walkerOS/skills/create-destination ~/.claude/skills/walkerOS

// tip: Run this command in your terminal to install the skill


name: create-destination description: Use when creating a new walkerOS destination. Example-driven workflow starting with research and examples before implementation.

Create a New Destination

Prerequisites

Before starting, read these skills:

Choose Your Template

ComplexityTemplateWhen to Use
Simpleplausible/Single SDK call, minimal config
Complexgtag/Multiple services, sub-destinations
Servergcp/Server-side, batching, SDK init

Process Overview

1. Research     → Find SDK, understand vendor API
2. Examples     → Create dev entry with real usage patterns
3. Mapping      → Define walkerOS → vendor transformation
4. Scaffold     → Copy template and configure
5. Implement    → Build using examples as test fixtures
6. Test         → Verify against example variations
7. Document     → Write README

Phase 1: Research

Goal: Understand the vendor API before writing any code.

1.1 Find Official Resources

  • Vendor API Documentation - Endpoints, authentication, rate limits
  • Official TypeScript SDK - Check npm for @vendor/sdk or vendor-types
  • Event Schema - What fields are required/optional for each event type
# Search npm for official packages
npm search [vendor-name]
npm search @[vendor]

# Check for TypeScript types
npm info @types/[vendor]

1.2 Identify Event Types

List the vendor's event types and their required fields:

Vendor EventRequired FieldswalkerOS Equivalent
pageviewurl, titlepage view
trackevent, propertiesproduct view, etc.
identifyuserId, traitsUser identification

1.3 Check Existing Patterns

Review similar destinations in the codebase:

# List existing destinations
ls packages/web/destinations/

# Reference implementations
# - plausible: Simple, script-based
# - gtag: Complex, multiple services
# - meta: Pixel with custom events

Gate: Research Complete

Before proceeding, confirm:

  • API pattern identified (SDK function / HTTP / pixel)
  • Auth method documented (API key, token, none)
  • Event types mapped to walkerOS equivalents

Checkpoint: Research Review (Optional)

If working with human oversight, pause here to confirm:

  • API pattern and auth method correct?
  • Event mapping makes sense for the use case?
  • Any vendor quirks or rate limits to handle?

Continue only after approval.


Phase 2: Create Examples (BEFORE Implementation)

Goal: Define expected API calls in dev entry FIRST.

2.1 Scaffold Directory Structure

mkdir -p packages/web/destinations/[name]/src/{examples,schemas,types}

2.2 Create Output Examples

What the vendor API expects when we call it:

src/examples/outputs.ts:

/**
 * Examples of vendor API calls we will make.
 * These define the CONTRACT - implementation must produce these outputs.
 */

// Page view call
export const pageViewCall = {
  method: 'track',
  args: ['pageview', { url: '/home', title: 'Home Page' }],
};

// E-commerce event call
export const purchaseCall = {
  method: 'track',
  args: [
    'purchase',
    {
      transaction_id: 'T-123',
      value: 99.99,
      currency: 'USD',
      items: [{ item_id: 'P-1', item_name: 'Widget', price: 99.99 }],
    },
  ],
};

// Custom event call
export const customEventCall = {
  method: 'track',
  args: ['button_click', { button_id: 'cta', button_text: 'Sign Up' }],
};

2.3 Create Input Examples

walkerOS events that will trigger these outputs:

src/examples/events.ts:

import type { WalkerOS } from '@walkeros/core';

/**
 * walkerOS events that trigger destination calls.
 * Maps to outputs.ts examples.
 */
export const events: Record<string, WalkerOS.Event> = {
  // Maps to pageViewCall
  pageView: {
    event: 'page view',
    data: { title: 'Home Page', path: '/home' },
    context: {},
    globals: {},
    user: { device: 'device-123' },
    nested: [],
    consent: { analytics: true },
    id: '1-abc-1',
    trigger: 'load',
    entity: 'page',
    action: 'view',
    timestamp: 1700000000000,
    timing: 0,
    group: 'group-1',
    count: 1,
    version: { tagging: 1, config: 1 },
    source: { type: 'web', id: '', previous_id: '' },
  },

  // Maps to purchaseCall
  purchase: {
    event: 'order complete',
    data: { id: 'T-123', total: 99.99 },
    // ... full event structure
  },

  // Maps to customEventCall
  buttonClick: {
    event: 'button click',
    data: { id: 'cta', text: 'Sign Up' },
    // ... full event structure
  },
};

2.4 Create Environment Mock

src/examples/env.ts:

import type { DestinationWeb } from '@walkeros/web-core';

/**
 * Mock environment capturing vendor SDK calls.
 */
export const env: { push: DestinationWeb.Env } = {
  push: {
    window: {
      vendorSdk: jest.fn(), // Captures all calls for verification
    } as unknown as Window,
    document: {} as Document,
  },
};

2.5 Export via dev.ts

src/dev.ts:

export * as schemas from './schemas';
export * as examples from './examples';

Gate: Examples Valid

  • All example files compile (npm run build)
  • Can trace: input event → expected output for each example

Phase 3: Define Mapping

Goal: Document transformation from walkerOS events to vendor format.

3.1 Create Mapping Examples

src/examples/mapping.ts:

import type { Mapping } from '@walkeros/core';

/**
 * Default mapping: walkerOS events → vendor format.
 */
export const defaultMapping: Mapping.Rules = {
  page: {
    view: {
      name: 'pageview', // Vendor event name
      data: {
        map: {
          url: 'data.path',
          title: 'data.title',
        },
      },
    },
  },
  order: {
    complete: {
      name: 'purchase',
      data: {
        map: {
          transaction_id: 'data.id',
          value: 'data.total',
          currency: { value: 'USD' },
        },
      },
    },
  },
  button: {
    click: {
      name: 'button_click',
      data: {
        map: {
          button_id: 'data.id',
          button_text: 'data.text',
        },
      },
    },
  },
};

3.2 Verify Mapping Logic

Create a mental (or actual) trace:

Input: events.pageView
  ↓ Apply mapping
  ↓ page.view rule matches
  ↓ name: 'pageview'
  ↓ data.path → url, data.title → title
Output: Should match outputs.pageViewCall

Gate: Mapping Verified

  • Mapping covers: page view + at least one conversion event
  • Each mapping rule traces correctly to expected output

Phase 4: Scaffold

Template destination: packages/web/destinations/plausible/

cp -r packages/web/destinations/plausible packages/web/destinations/[name]
cd packages/web/destinations/[name]

# Update package.json: name, description, repository.directory

Directory structure:

packages/web/destinations/[name]/
├── src/
│   ├── index.ts           # Main destination (init + push)
│   ├── index.test.ts      # Tests against examples
│   ├── dev.ts             # Exports schemas and examples
│   ├── examples/
│   │   ├── index.ts       # Re-exports
│   │   ├── env.ts         # Mock environment
│   │   ├── events.ts      # Input events
│   │   ├── outputs.ts     # Expected outputs
│   │   └── mapping.ts     # Default mapping
│   ├── schemas/
│   │   └── index.ts       # Zod schemas
│   └── types/
│       └── index.ts       # Settings, Config types
├── package.json
├── tsconfig.json
├── tsup.config.ts
├── jest.config.mjs
└── README.md

Phase 5: Implement

Now write code to produce the outputs defined in Phase 2.

5.1 Define Types

src/types/index.ts:

import type { DestinationWeb } from '@walkeros/web-core';

export interface Settings {
  apiKey?: string;
  // Add vendor-specific settings
}

export interface Config extends DestinationWeb.Config<Settings> {}
export interface Destination extends DestinationWeb.Destination<Config> {}

5.2 Implement Destination

src/index.ts:

import type { Config, Destination } from './types';
import type { DestinationWeb } from '@walkeros/web-core';
import { isObject } from '@walkeros/core';
import { getEnv } from '@walkeros/web-core';

export * as DestinationVendor from './types';

export const destinationVendor: Destination = {
  type: 'vendor',
  config: {},

  init({ config, env }) {
    const { window } = getEnv(env);
    const settings = config.settings || {};

    if (config.loadScript) addScript(settings, env);

    // Initialize vendor SDK queue
    (window as Window).vendorSdk =
      (window as Window).vendorSdk ||
      function () {
        ((window as Window).vendorSdk.q =
          (window as Window).vendorSdk.q || []).push(arguments);
      };

    return config;
  },

  push(event, { config, data, env }) {
    const params = isObject(data) ? data : {};
    const { window } = getEnv(env);

    // Call vendor API - must match outputs.ts examples
    (window as Window).vendorSdk('track', event.name, params);
  },
};

function addScript(settings: Settings, env?: DestinationWeb.Env) {
  const { document } = getEnv(env);
  const script = document.createElement('script');
  script.src = `https://vendor.com/sdk.js?key=${settings.apiKey}`;
  document.head.appendChild(script);
}

export default destinationVendor;

Gate: Implementation Compiles

  • npm run build passes
  • npm run lint passes

Phase 6: Test Against Examples

Verify implementation produces expected outputs.

src/index.test.ts:

import { destinationVendor } from '.';
import { examples } from './dev';

describe('destinationVendor', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  test('page view produces correct output', () => {
    const mockSdk = jest.fn();
    const env = {
      ...examples.env.push,
      window: { vendorSdk: mockSdk } as unknown as Window,
    };

    destinationVendor.push(examples.events.pageView, {
      config: {},
      data: { url: '/home', title: 'Home Page' },
      env,
    });

    // Verify against expected output
    expect(mockSdk).toHaveBeenCalledWith(
      examples.outputs.pageViewCall.method,
      ...examples.outputs.pageViewCall.args,
    );
  });

  test('purchase produces correct output', () => {
    // Similar test against purchaseCall
  });

  test('custom event produces correct output', () => {
    // Similar test against customEventCall
  });
});

Gate: Tests Pass

  • npm run test passes
  • Tests verify against example outputs (not hardcoded values)

Phase 7: Document

Follow the writing-documentation skill for:

  • README structure and templates
  • Example validation against apps/quickstart/
  • Quality checklist before publishing

Key requirements for destination documentation:

  • Event mapping table (walkerOS → vendor format)
  • Configuration options table (use PropertyTable if schema exists)
  • Working code example with imports
  • Installation instructions

Destination-Specific Validation

Beyond understanding-development requirements (build, test, lint, no any):

  • Uses getEnv(env) pattern (never direct window/document access)
  • dev.ts exports schemas and examples
  • Examples match type signatures
  • Tests use examples for assertions (not hardcoded values)

Reference Files

WhatWhere
Simple templatepackages/web/destinations/plausible/
Complex examplepackages/web/destinations/gtag/
Typespackages/web/core/src/types/destination.ts

Related