@junctionjs/destination-amplitude 0.1.0 → 0.1.1

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.
package/dist/index.js CHANGED
@@ -8,7 +8,6 @@ function formatEventName(entity, action, format) {
8
8
  return `${capitalize(entity)} ${capitalize(action)}`;
9
9
  case "entity:action":
10
10
  return `${entity}:${action}`;
11
- case "snake_case":
12
11
  default:
13
12
  return `${entity}_${action}`;
14
13
  }
@@ -74,7 +73,7 @@ async function sendServer(payload, config) {
74
73
  "Content-Type": "application/json"
75
74
  };
76
75
  if (config.secretKey) {
77
- headers["Authorization"] = `Basic ${btoa(`${config.apiKey}:${config.secretKey}`)}`;
76
+ headers.Authorization = `Basic ${btoa(`${config.apiKey}:${config.secretKey}`)}`;
78
77
  }
79
78
  const response = await fetch(url, {
80
79
  method: "POST",
@@ -105,30 +104,12 @@ var amplitude = {
105
104
  );
106
105
  }
107
106
  },
108
- transform(event) {
107
+ transform(event, config) {
109
108
  if (event.entity === "_system") return null;
110
- return transformEvent(event, {});
109
+ return transformEvent(event, config);
111
110
  },
112
111
  async send(payload, config) {
113
112
  const ampEvent = payload;
114
- const fullPayload = transformEvent(
115
- // Reconstruct minimal event for re-transform
116
- {
117
- entity: "",
118
- action: "",
119
- properties: ampEvent.event_properties,
120
- context: {},
121
- user: {
122
- anonymousId: ampEvent.device_id,
123
- userId: ampEvent.user_id
124
- },
125
- timestamp: new Date(ampEvent.time).toISOString(),
126
- id: ampEvent.insert_id,
127
- version: "1.0.0",
128
- source: { type: "client", name: "browser", version: "0.1.0" }
129
- },
130
- config
131
- );
132
113
  if (config.mode === "server") {
133
114
  await sendServer(ampEvent, config);
134
115
  } else {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @junctionjs/destination-amplitude\n *\n * Amplitude Analytics destination for Junction.\n *\n * Supports both client-side (Amplitude Browser SDK) and server-side\n * (Amplitude HTTP V2 API) sending.\n *\n * Design notes:\n * - Uses Amplitude's official @amplitude/analytics-browser SDK on client\n * - Uses direct HTTP API on server (no SDK dependency needed)\n * - Maps Junction's entity:action events to Amplitude event names\n * - Forwards user identity (identify + traits → Amplitude user properties)\n */\n\nimport type { Destination, JctEvent, ConsentState } from \"@junctionjs/core\";\n\n// ─── Configuration ───────────────────────────────────────────────\n\nexport interface AmplitudeConfig {\n /** Amplitude API key */\n apiKey: string;\n\n /** Server URL override (for EU data residency or proxy) */\n serverUrl?: string;\n\n /** Whether to use the Amplitude Browser SDK (client) or HTTP API (server) */\n mode: \"client\" | \"server\";\n\n /** Server-side only: Amplitude HTTP API secret key */\n secretKey?: string;\n\n /**\n * Event name format. Controls how entity:action maps to Amplitude event names.\n * - \"snake_case\": \"product_added\" (default)\n * - \"Title Case\": \"Product Added\"\n * - \"entity:action\": \"product:added\" (raw)\n * - Custom function for full control\n */\n eventNameFormat?:\n | \"snake_case\"\n | \"Title Case\"\n | \"entity:action\"\n | ((entity: string, action: string) => string);\n\n /**\n * Default event properties to include on every event.\n */\n defaultProperties?: Record<string, unknown>;\n\n /**\n * Property mapping overrides. Keys are Junction property names,\n * values are Amplitude property names.\n *\n * Example: { \"product_id\": \"productId\", \"order_id\": \"orderId\" }\n */\n propertyMap?: Record<string, string>;\n}\n\n// ─── Event Name Formatting ───────────────────────────────────────\n\nfunction formatEventName(\n entity: string,\n action: string,\n format: AmplitudeConfig[\"eventNameFormat\"],\n): string {\n if (typeof format === \"function\") {\n return format(entity, action);\n }\n\n switch (format) {\n case \"Title Case\":\n return `${capitalize(entity)} ${capitalize(action)}`;\n case \"entity:action\":\n return `${entity}:${action}`;\n case \"snake_case\":\n default:\n return `${entity}_${action}`;\n }\n}\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\n// ─── Property Mapping ────────────────────────────────────────────\n\nfunction mapProperties(\n properties: Record<string, unknown>,\n propertyMap?: Record<string, string>,\n): Record<string, unknown> {\n if (!propertyMap) return properties;\n\n const mapped: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(properties)) {\n const mappedKey = propertyMap[key] ?? key;\n mapped[mappedKey] = value;\n }\n return mapped;\n}\n\n// ─── Amplitude Payload Types ─────────────────────────────────────\n\ninterface AmplitudeEvent {\n event_type: string;\n user_id?: string;\n device_id: string;\n event_properties: Record<string, unknown>;\n user_properties?: Record<string, unknown>;\n time: number;\n session_id?: number;\n platform?: string;\n os_name?: string;\n language?: string;\n ip?: string;\n insert_id: string;\n}\n\n// ─── Transform ───────────────────────────────────────────────────\n\nfunction transformEvent(event: JctEvent, config: AmplitudeConfig): AmplitudeEvent {\n const eventName = formatEventName(event.entity, event.action, config.eventNameFormat);\n\n const eventProperties = {\n ...config.defaultProperties,\n ...mapProperties(event.properties, config.propertyMap),\n // Include page context as properties\n ...(event.context.page\n ? {\n page_url: event.context.page.url,\n page_path: event.context.page.path,\n page_title: event.context.page.title,\n page_referrer: event.context.page.referrer,\n }\n : {}),\n };\n\n // Build user properties from traits (for $set operations)\n const userProperties = event.user.traits\n ? { $set: event.user.traits }\n : undefined;\n\n return {\n event_type: eventName,\n user_id: event.user.userId,\n device_id: event.user.anonymousId,\n event_properties: eventProperties,\n user_properties: userProperties,\n time: new Date(event.timestamp).getTime(),\n session_id: event.context.session\n ? new Date(event.timestamp).getTime() // Amplitude uses session start time\n : undefined,\n platform: event.context.device?.type === \"mobile\" ? \"Mobile\" : \"Web\",\n language: event.context.device?.language,\n insert_id: event.id, // deduplication\n };\n}\n\n// ─── Client-Side Sender ──────────────────────────────────────────\n\nasync function sendClient(payload: AmplitudeEvent, config: AmplitudeConfig): Promise<void> {\n // Use Amplitude's HTTP API directly (no SDK dependency)\n const url = config.serverUrl ?? \"https://api2.amplitude.com/2/httpapi\";\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n api_key: config.apiKey,\n events: [payload],\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Amplitude API error (${response.status}): ${body}`);\n }\n}\n\n// ─── Server-Side Sender ──────────────────────────────────────────\n\nasync function sendServer(payload: AmplitudeEvent, config: AmplitudeConfig): Promise<void> {\n const url = config.serverUrl ?? \"https://api2.amplitude.com/2/httpapi\";\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n // Server-side can use secret key for higher rate limits\n if (config.secretKey) {\n headers[\"Authorization\"] = `Basic ${btoa(`${config.apiKey}:${config.secretKey}`)}`;\n }\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n api_key: config.apiKey,\n events: [payload],\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Amplitude API error (${response.status}): ${body}`);\n }\n}\n\n// ─── Destination Export ──────────────────────────────────────────\n\n/**\n * Create an Amplitude destination.\n *\n * Usage:\n * import { amplitude } from \"@junctionjs/destination-amplitude\";\n *\n * const collector = createCollector({\n * config: {\n * destinations: [\n * {\n * destination: amplitude,\n * config: { apiKey: \"YOUR_API_KEY\", mode: \"client\" },\n * },\n * ],\n * },\n * });\n */\nexport const amplitude: Destination<AmplitudeConfig> = {\n name: \"amplitude\",\n description: \"Amplitude Analytics\",\n version: \"0.1.0\",\n consent: [\"analytics\"],\n runtime: \"both\",\n\n async init(config: AmplitudeConfig) {\n // Validate config\n if (!config.apiKey) {\n throw new Error(\"[Junction:Amplitude] apiKey is required\");\n }\n if (config.mode === \"server\" && !config.secretKey) {\n console.warn(\n \"[Junction:Amplitude] Running in server mode without secretKey. \" +\n \"Consider adding a secret key for higher rate limits.\",\n );\n }\n },\n\n transform(event: JctEvent) {\n // Skip internal/system events that Amplitude doesn't need\n if (event.entity === \"_system\") return null;\n\n return transformEvent(event, {} as AmplitudeConfig);\n },\n\n async send(payload: unknown, config: AmplitudeConfig) {\n const ampEvent = payload as AmplitudeEvent;\n\n // Re-transform with actual config (transform doesn't have config access by design)\n // In practice, you'd restructure this — here we just re-apply config\n const fullPayload = transformEvent(\n // Reconstruct minimal event for re-transform\n {\n entity: \"\",\n action: \"\",\n properties: ampEvent.event_properties,\n context: {},\n user: {\n anonymousId: ampEvent.device_id,\n userId: ampEvent.user_id,\n },\n timestamp: new Date(ampEvent.time).toISOString(),\n id: ampEvent.insert_id,\n version: \"1.0.0\",\n source: { type: \"client\", name: \"browser\", version: \"0.1.0\" },\n } as JctEvent,\n config,\n );\n\n if (config.mode === \"server\") {\n await sendServer(ampEvent, config);\n } else {\n await sendClient(ampEvent, config);\n }\n },\n\n onConsent(state: ConsentState) {\n // Amplitude doesn't have its own consent mode,\n // but we could opt-out of certain tracking here\n if (state.analytics === false) {\n console.log(\"[Junction:Amplitude] Analytics consent revoked\");\n }\n },\n};\n\nexport default amplitude;\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @junctionjs/destination-amplitude\n *\n * Amplitude Analytics destination for Junction.\n *\n * Supports both client-side (Amplitude Browser SDK) and server-side\n * (Amplitude HTTP V2 API) sending.\n *\n * Design notes:\n * - Uses Amplitude's official @amplitude/analytics-browser SDK on client\n * - Uses direct HTTP API on server (no SDK dependency needed)\n * - Maps Junction's entity:action events to Amplitude event names\n * - Forwards user identity (identify + traits → Amplitude user properties)\n */\n\nimport type { ConsentState, Destination, JctEvent } from \"@junctionjs/core\";\n\n// ─── Configuration ───────────────────────────────────────────────\n\nexport interface AmplitudeConfig {\n /** Amplitude API key */\n apiKey: string;\n\n /** Server URL override (for EU data residency or proxy) */\n serverUrl?: string;\n\n /** Whether to use the Amplitude Browser SDK (client) or HTTP API (server) */\n mode: \"client\" | \"server\";\n\n /** Server-side only: Amplitude HTTP API secret key */\n secretKey?: string;\n\n /**\n * Event name format. Controls how entity:action maps to Amplitude event names.\n * - \"snake_case\": \"product_added\" (default)\n * - \"Title Case\": \"Product Added\"\n * - \"entity:action\": \"product:added\" (raw)\n * - Custom function for full control\n */\n eventNameFormat?: \"snake_case\" | \"Title Case\" | \"entity:action\" | ((entity: string, action: string) => string);\n\n /**\n * Default event properties to include on every event.\n */\n defaultProperties?: Record<string, unknown>;\n\n /**\n * Property mapping overrides. Keys are Junction property names,\n * values are Amplitude property names.\n *\n * Example: { \"product_id\": \"productId\", \"order_id\": \"orderId\" }\n */\n propertyMap?: Record<string, string>;\n}\n\n// ─── Event Name Formatting ───────────────────────────────────────\n\nfunction formatEventName(entity: string, action: string, format: AmplitudeConfig[\"eventNameFormat\"]): string {\n if (typeof format === \"function\") {\n return format(entity, action);\n }\n\n switch (format) {\n case \"Title Case\":\n return `${capitalize(entity)} ${capitalize(action)}`;\n case \"entity:action\":\n return `${entity}:${action}`;\n default:\n return `${entity}_${action}`;\n }\n}\n\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\n// ─── Property Mapping ────────────────────────────────────────────\n\nfunction mapProperties(\n properties: Record<string, unknown>,\n propertyMap?: Record<string, string>,\n): Record<string, unknown> {\n if (!propertyMap) return properties;\n\n const mapped: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(properties)) {\n const mappedKey = propertyMap[key] ?? key;\n mapped[mappedKey] = value;\n }\n return mapped;\n}\n\n// ─── Amplitude Payload Types ─────────────────────────────────────\n\ninterface AmplitudeEvent {\n event_type: string;\n user_id?: string;\n device_id: string;\n event_properties: Record<string, unknown>;\n user_properties?: Record<string, unknown>;\n time: number;\n session_id?: number;\n platform?: string;\n os_name?: string;\n language?: string;\n ip?: string;\n insert_id: string;\n}\n\n// ─── Transform ───────────────────────────────────────────────────\n\nfunction transformEvent(event: JctEvent, config: AmplitudeConfig): AmplitudeEvent {\n const eventName = formatEventName(event.entity, event.action, config.eventNameFormat);\n\n const eventProperties = {\n ...config.defaultProperties,\n ...mapProperties(event.properties, config.propertyMap),\n // Include page context as properties\n ...(event.context.page\n ? {\n page_url: event.context.page.url,\n page_path: event.context.page.path,\n page_title: event.context.page.title,\n page_referrer: event.context.page.referrer,\n }\n : {}),\n };\n\n // Build user properties from traits (for $set operations)\n const userProperties = event.user.traits ? { $set: event.user.traits } : undefined;\n\n return {\n event_type: eventName,\n user_id: event.user.userId,\n device_id: event.user.anonymousId,\n event_properties: eventProperties,\n user_properties: userProperties,\n time: new Date(event.timestamp).getTime(),\n session_id: event.context.session\n ? new Date(event.timestamp).getTime() // Amplitude uses session start time\n : undefined,\n platform: event.context.device?.type === \"mobile\" ? \"Mobile\" : \"Web\",\n language: event.context.device?.language,\n insert_id: event.id, // deduplication\n };\n}\n\n// ─── Client-Side Sender ──────────────────────────────────────────\n\nasync function sendClient(payload: AmplitudeEvent, config: AmplitudeConfig): Promise<void> {\n // Use Amplitude's HTTP API directly (no SDK dependency)\n const url = config.serverUrl ?? \"https://api2.amplitude.com/2/httpapi\";\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n api_key: config.apiKey,\n events: [payload],\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Amplitude API error (${response.status}): ${body}`);\n }\n}\n\n// ─── Server-Side Sender ──────────────────────────────────────────\n\nasync function sendServer(payload: AmplitudeEvent, config: AmplitudeConfig): Promise<void> {\n const url = config.serverUrl ?? \"https://api2.amplitude.com/2/httpapi\";\n\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n };\n\n // Server-side can use secret key for higher rate limits\n if (config.secretKey) {\n headers.Authorization = `Basic ${btoa(`${config.apiKey}:${config.secretKey}`)}`;\n }\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n api_key: config.apiKey,\n events: [payload],\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Amplitude API error (${response.status}): ${body}`);\n }\n}\n\n// ─── Destination Export ──────────────────────────────────────────\n\n/**\n * Create an Amplitude destination.\n *\n * Usage:\n * import { amplitude } from \"@junctionjs/destination-amplitude\";\n *\n * const collector = createCollector({\n * config: {\n * destinations: [\n * {\n * destination: amplitude,\n * config: { apiKey: \"YOUR_API_KEY\", mode: \"client\" },\n * },\n * ],\n * },\n * });\n */\nexport const amplitude: Destination<AmplitudeConfig> = {\n name: \"amplitude\",\n description: \"Amplitude Analytics\",\n version: \"0.1.0\",\n consent: [\"analytics\"],\n runtime: \"both\",\n\n async init(config: AmplitudeConfig) {\n // Validate config\n if (!config.apiKey) {\n throw new Error(\"[Junction:Amplitude] apiKey is required\");\n }\n if (config.mode === \"server\" && !config.secretKey) {\n console.warn(\n \"[Junction:Amplitude] Running in server mode without secretKey. \" +\n \"Consider adding a secret key for higher rate limits.\",\n );\n }\n },\n\n transform(event: JctEvent, config: AmplitudeConfig) {\n // Skip internal/system events that Amplitude doesn't need\n if (event.entity === \"_system\") return null;\n\n return transformEvent(event, config);\n },\n\n async send(payload: unknown, config: AmplitudeConfig) {\n const ampEvent = payload as AmplitudeEvent;\n\n if (config.mode === \"server\") {\n await sendServer(ampEvent, config);\n } else {\n await sendClient(ampEvent, config);\n }\n },\n\n onConsent(state: ConsentState) {\n // Amplitude doesn't have its own consent mode,\n // but we could opt-out of certain tracking here\n if (state.analytics === false) {\n console.log(\"[Junction:Amplitude] Analytics consent revoked\");\n }\n },\n};\n\nexport default amplitude;\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@junctionjs/destination-amplitude",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Amplitude Analytics destination for Junction — HTTP API, client + server",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -19,7 +19,7 @@
19
19
  "typecheck": "tsc --noEmit"
20
20
  },
21
21
  "peerDependencies": {
22
- "@junctionjs/core": "*"
22
+ "@junctionjs/core": "^0.1.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@junctionjs/core": "*",
@@ -29,6 +29,11 @@
29
29
  "engines": {
30
30
  "node": ">=18.0.0"
31
31
  },
32
+ "publishConfig": {
33
+ "access": "public",
34
+ "provenance": true,
35
+ "registry": "https://registry.npmjs.org/"
36
+ },
32
37
  "license": "MIT",
33
38
  "repository": {
34
39
  "type": "git",