@codaco/analytics 9.0.0 → 11.0.0
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/CHANGELOG.md +30 -0
- package/LICENSE +674 -0
- package/README.md +174 -305
- package/dist/{chunk-3NEQVIC4.js → chunk-TPZBZWEN.js} +19 -18
- package/dist/chunk-TPZBZWEN.js.map +1 -0
- package/dist/index.d.ts +11 -5
- package/dist/index.js +20 -9
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +2 -1
- package/dist/server.js.map +1 -1
- package/dist/{types-Ymgjicqi.d.ts → types-BxfgCtu7.d.ts} +20 -8
- package/package.json +8 -8
- package/src/__tests__/client.test.ts +3 -1
- package/src/__tests__/index.test.ts +37 -1
- package/src/client.ts +2 -1
- package/src/config.ts +1 -0
- package/src/index.ts +5 -2
- package/src/provider.tsx +2 -2
- package/src/server.ts +1 -0
- package/src/types.ts +31 -7
- package/tsconfig.json +1 -1
- package/dist/chunk-3NEQVIC4.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts","../src/config.ts"],"sourcesContent":["// Helper function that ensures that a value is an Error\nexport function ensureError(value: unknown): Error {\n\tif (!value) return new Error(\"No value was thrown\");\n\n\tif (value instanceof Error) return value;\n\n\t// Test if value inherits from Error\n\tif (Object.prototype.isPrototypeOf.call(value, Error)) return value as Error & typeof value;\n\n\tlet stringified = \"[Unable to stringify the thrown value]\";\n\ttry {\n\t\tstringified = JSON.stringify(value);\n\t} catch (e) {\n\t\t// biome-ignore lint/suspicious/noConsole: logging\n\t\tconsole.error(e);\n\t}\n\n\tconst error = new Error(`This value was thrown as is, not through an Error: ${stringified}`);\n\treturn error;\n}\n","import type { AnalyticsConfig } from \"./types\";\n\n/**\n * Hardcoded PostHog API host - always uses the Cloudflare Worker reverse proxy\n * Authentication is handled by the worker at this endpoint\n */\nconst POSTHOG_PROXY_HOST = \"https://ph-relay.networkcanvas.com\";\n\n/**\n * Dummy API key used for proxy mode\n * PostHog JS library requires an API key for initialization, but when using\n * the reverse proxy, authentication is handled by the Cloudflare Worker.\n * This placeholder key is used for client-side initialization only.\n */\nconst PROXY_MODE_DUMMY_KEY = \"phc_proxy_mode_placeholder\";\n\n/**\n * Check if analytics is disabled via environment variables\n */\nexport function isDisabledByEnv(): boolean {\n\tif (typeof process === \"undefined\") {\n\t\treturn false;\n\t}\n\n\treturn process.env.DISABLE_ANALYTICS === \"true\" || process.env.NEXT_PUBLIC_DISABLE_ANALYTICS === \"true\";\n}\n\n/**\n * Default configuration for analytics\n * API host and key are hardcoded, but disabled flag can be set via environment variables\n */\nexport const defaultConfig: Partial<AnalyticsConfig> = {\n\t// Always use the Cloudflare Worker reverse proxy\n\tapiHost: POSTHOG_PROXY_HOST,\n\n\t// Analytics enabled by default (can be disabled via env var or config option)\n\tdisabled: false,\n\n\t// Debug mode disabled by default\n\tdebug: false,\n\n\t// Default PostHog options\n\tposthogOptions: {\n\t\t// Disable session recording by default (can be enabled per-app)\n\t\tdisable_session_recording: true,\n\n\t\t// Disable autocapture to keep events clean and intentional\n\t\tautocapture: false,\n\n\t\t// Disable automatic pageview capture (apps can enable if needed)\n\t\tcapture_pageview: false,\n\n\t\t// Disable pageleave events\n\t\tcapture_pageleave: false,\n\n\t\t// Don't use cross-subdomain cookies\n\t\tcross_subdomain_cookie: false,\n\n\t\t// Enable feature flags by default\n\t\tadvanced_disable_feature_flags: false,\n\n\t\t// Send feature flag events\n\t\tadvanced_disable_feature_flags_on_first_load: false,\n\n\t\t// Enable persistence for feature flags\n\t\tpersistence: \"localStorage+cookie\",\n\t},\n};\n\n/**\n * Merge user config with defaults\n *\n * Note: This package is designed to work exclusively with the Cloudflare Worker\n * reverse proxy (ph-relay.networkcanvas.com). Authentication is handled by the\n * worker, so the API key is optional and defaults to a placeholder value.\n *\n * The only environment variable checked is DISABLE_ANALYTICS / NEXT_PUBLIC_DISABLE_ANALYTICS\n * for disabling tracking. All other configuration is hardcoded or passed explicitly.\n */\nexport function mergeConfig(userConfig: AnalyticsConfig): Required<AnalyticsConfig> {\n\treturn {\n\t\tapp: userConfig.app,\n\t\tapiHost: userConfig.apiHost ?? defaultConfig.apiHost ?? POSTHOG_PROXY_HOST,\n\t\tapiKey: userConfig.apiKey ?? PROXY_MODE_DUMMY_KEY,\n\t\tinstallationId: userConfig.installationId,\n\t\tdisabled: userConfig.disabled ?? isDisabledByEnv() ?? defaultConfig.disabled ?? false,\n\t\tdebug: userConfig.debug ?? defaultConfig.debug ?? false,\n\t\tposthogOptions: {\n\t\t\t...defaultConfig.posthogOptions,\n\t\t\t...userConfig.posthogOptions,\n\t\t},\n\t};\n}\n"],"mappings":";AACO,SAAS,YAAY,OAAuB;AAClD,MAAI,CAAC,MAAO,QAAO,IAAI,MAAM,qBAAqB;AAElD,MAAI,iBAAiB,MAAO,QAAO;AAGnC,MAAI,OAAO,UAAU,cAAc,KAAK,OAAO,KAAK,EAAG,QAAO;AAE9D,MAAI,cAAc;AAClB,MAAI;AACH,kBAAc,KAAK,UAAU,KAAK;AAAA,EACnC,SAAS,GAAG;AAEX,YAAQ,MAAM,CAAC;AAAA,EAChB;AAEA,QAAM,QAAQ,IAAI,MAAM,sDAAsD,WAAW,EAAE;AAC3F,SAAO;AACR;;;ACbA,IAAM,qBAAqB;AAQ3B,IAAM,uBAAuB;AAKtB,SAAS,kBAA2B;AAC1C,MAAI,OAAO,YAAY,aAAa;AACnC,WAAO;AAAA,EACR;AAEA,SAAO,QAAQ,IAAI,sBAAsB,UAAU,QAAQ,IAAI,kCAAkC;AAClG;AAMO,IAAM,gBAA0C;AAAA;AAAA,EAEtD,SAAS;AAAA;AAAA,EAGT,UAAU;AAAA;AAAA,EAGV,OAAO;AAAA;AAAA,EAGP,gBAAgB;AAAA;AAAA,IAEf,2BAA2B;AAAA;AAAA,IAG3B,aAAa;AAAA;AAAA,IAGb,kBAAkB;AAAA;AAAA,IAGlB,mBAAmB;AAAA;AAAA,IAGnB,wBAAwB;AAAA;AAAA,IAGxB,gCAAgC;AAAA;AAAA,IAGhC,8CAA8C;AAAA;AAAA,IAG9C,aAAa;AAAA,EACd;AACD;AAYO,SAAS,YAAY,YAAwD;AACnF,SAAO;AAAA,IACN,KAAK,WAAW;AAAA,IAChB,SAAS,WAAW,WAAW,cAAc,WAAW;AAAA,IACxD,QAAQ,WAAW,UAAU;AAAA,IAC7B,gBAAgB,WAAW;AAAA,IAC3B,UAAU,WAAW,YAAY,gBAAgB,KAAK,cAAc,YAAY;AAAA,IAChF,OAAO,WAAW,SAAS,cAAc,SAAS;AAAA,IAClD,gBAAgB;AAAA,MACf,GAAG,cAAc;AAAA,MACjB,GAAG,WAAW;AAAA,IACf;AAAA,EACD;AACD;","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { A as AnalyticsConfig, a as Analytics } from './types-
|
|
2
|
-
export { E as ErrorProperties,
|
|
1
|
+
import { A as AnalyticsConfig, a as Analytics } from './types-BxfgCtu7.js';
|
|
2
|
+
export { b as AppName, E as ErrorProperties, c as EventProperties, d as EventType, e as appNames, f as eventTypes, l as legacyEventTypeMap } from './types-BxfgCtu7.js';
|
|
3
3
|
import * as react from 'react';
|
|
4
4
|
import { ReactNode } from 'react';
|
|
5
5
|
import 'zod';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Create a client-side analytics instance
|
|
9
|
+
* This wraps PostHog with Network Canvas-specific functionality
|
|
10
|
+
*/
|
|
11
|
+
declare function createAnalytics(config: Required<AnalyticsConfig>): Analytics;
|
|
12
|
+
|
|
7
13
|
/**
|
|
8
14
|
* Check if analytics is disabled via environment variables
|
|
9
15
|
*/
|
|
@@ -86,10 +92,10 @@ declare function useFeatureFlagValue(flagKey: string): string | boolean | undefi
|
|
|
86
92
|
/**
|
|
87
93
|
* Props for the AnalyticsProvider
|
|
88
94
|
*/
|
|
89
|
-
|
|
95
|
+
type AnalyticsProviderProps = {
|
|
90
96
|
children: ReactNode;
|
|
91
97
|
config: AnalyticsConfig;
|
|
92
|
-
}
|
|
98
|
+
};
|
|
93
99
|
/**
|
|
94
100
|
* Provider component that initializes PostHog and provides analytics context
|
|
95
101
|
*
|
|
@@ -116,4 +122,4 @@ declare function AnalyticsProvider({ children, config }: AnalyticsProviderProps)
|
|
|
116
122
|
|
|
117
123
|
declare function ensureError(value: unknown): Error;
|
|
118
124
|
|
|
119
|
-
export { Analytics, AnalyticsConfig, AnalyticsProvider, type AnalyticsProviderProps, defaultConfig, ensureError, isDisabledByEnv, mergeConfig, useAnalytics, useFeatureFlag, useFeatureFlagValue };
|
|
125
|
+
export { Analytics, AnalyticsConfig, AnalyticsProvider, type AnalyticsProviderProps, createAnalytics, defaultConfig, ensureError, isDisabledByEnv, mergeConfig, useAnalytics, useFeatureFlag, useFeatureFlagValue };
|
package/dist/index.js
CHANGED
|
@@ -3,18 +3,12 @@ import {
|
|
|
3
3
|
ensureError,
|
|
4
4
|
isDisabledByEnv,
|
|
5
5
|
mergeConfig
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
|
|
8
|
-
// src/hooks.ts
|
|
9
|
-
import { useContext } from "react";
|
|
10
|
-
|
|
11
|
-
// src/provider.tsx
|
|
12
|
-
import { createContext, useEffect, useRef } from "react";
|
|
6
|
+
} from "./chunk-TPZBZWEN.js";
|
|
13
7
|
|
|
14
8
|
// src/client.ts
|
|
15
9
|
import posthog from "posthog-js";
|
|
16
10
|
function createAnalytics(config) {
|
|
17
|
-
const { apiHost, apiKey, installationId, disabled, debug, posthogOptions } = config;
|
|
11
|
+
const { app, apiHost, apiKey, installationId, disabled, debug, posthogOptions } = config;
|
|
18
12
|
if (disabled) {
|
|
19
13
|
return createNoOpAnalytics(installationId);
|
|
20
14
|
}
|
|
@@ -22,6 +16,7 @@ function createAnalytics(config) {
|
|
|
22
16
|
api_host: apiHost,
|
|
23
17
|
loaded: (posthogInstance) => {
|
|
24
18
|
posthogInstance.register({
|
|
19
|
+
app,
|
|
25
20
|
installation_id: installationId
|
|
26
21
|
});
|
|
27
22
|
if (debug) {
|
|
@@ -135,7 +130,11 @@ function createNoOpAnalytics(installationId) {
|
|
|
135
130
|
};
|
|
136
131
|
}
|
|
137
132
|
|
|
133
|
+
// src/hooks.ts
|
|
134
|
+
import { useContext } from "react";
|
|
135
|
+
|
|
138
136
|
// src/provider.tsx
|
|
137
|
+
import { createContext, useEffect, useRef } from "react";
|
|
139
138
|
var AnalyticsContext = createContext(null);
|
|
140
139
|
function AnalyticsProvider({ children, config }) {
|
|
141
140
|
const analyticsRef = useRef(null);
|
|
@@ -169,7 +168,7 @@ function useFeatureFlagValue(flagKey) {
|
|
|
169
168
|
}
|
|
170
169
|
|
|
171
170
|
// src/types.ts
|
|
172
|
-
import z from "zod";
|
|
171
|
+
import { z } from "zod";
|
|
173
172
|
var eventTypes = [
|
|
174
173
|
"app_setup",
|
|
175
174
|
"protocol_installed",
|
|
@@ -186,6 +185,16 @@ var legacyEventTypeMap = {
|
|
|
186
185
|
DataExported: "data_exported",
|
|
187
186
|
Error: "error"
|
|
188
187
|
};
|
|
188
|
+
var appNames = [
|
|
189
|
+
"Fresco",
|
|
190
|
+
"Studio",
|
|
191
|
+
"Architect",
|
|
192
|
+
"Interviewer",
|
|
193
|
+
"ArchitectWeb",
|
|
194
|
+
"CommunityForum",
|
|
195
|
+
"ProjectWebsite",
|
|
196
|
+
"Documentation"
|
|
197
|
+
];
|
|
189
198
|
var EventPropertiesSchema = z.object({
|
|
190
199
|
metadata: z.record(z.string(), z.unknown()).optional()
|
|
191
200
|
});
|
|
@@ -197,6 +206,8 @@ var ErrorPropertiesSchema = EventPropertiesSchema.extend({
|
|
|
197
206
|
});
|
|
198
207
|
export {
|
|
199
208
|
AnalyticsProvider,
|
|
209
|
+
appNames,
|
|
210
|
+
createAnalytics,
|
|
200
211
|
defaultConfig,
|
|
201
212
|
ensureError,
|
|
202
213
|
eventTypes,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks.ts","../src/provider.tsx","../src/client.ts","../src/types.ts"],"sourcesContent":["\"use client\";\n\nimport { useContext } from \"react\";\nimport { AnalyticsContext } from \"./provider\";\nimport type { Analytics } from \"./types\";\n\n/**\n * Hook to access analytics functionality in React components\n *\n * @example\n * ```tsx\n * import { useAnalytics } from '@codaco/analytics';\n *\n * function MyComponent() {\n * const { trackEvent, trackError } = useAnalytics();\n *\n * const handleAction = () => {\n * trackEvent('protocol_installed', {\n * metadata: { protocolName: 'My Protocol' }\n * });\n * };\n *\n * return <button onClick={handleAction}>Install Protocol</button>;\n * }\n * ```\n */\nexport function useAnalytics(): Analytics {\n\tconst analytics = useContext(AnalyticsContext);\n\n\tif (!analytics) {\n\t\tthrow new Error(\"useAnalytics must be used within an AnalyticsProvider\");\n\t}\n\n\treturn analytics;\n}\n\n/**\n * Hook to access feature flags\n *\n * @example\n * ```tsx\n * import { useFeatureFlag } from '@codaco/analytics';\n *\n * function MyComponent() {\n * const isNewFeatureEnabled = useFeatureFlag('new-feature');\n *\n * if (isNewFeatureEnabled) {\n * return <NewFeature />;\n * }\n *\n * return <OldFeature />;\n * }\n * ```\n */\nexport function useFeatureFlag(flagKey: string): boolean {\n\tconst analytics = useAnalytics();\n\treturn analytics.isFeatureEnabled(flagKey) ?? false;\n}\n\n/**\n * Hook to access feature flag values (for multivariate flags)\n *\n * @example\n * ```tsx\n * import { useFeatureFlagValue } from '@codaco/analytics';\n *\n * function MyComponent() {\n * const theme = useFeatureFlagValue('theme-variant');\n *\n * return <div className={theme === 'dark' ? 'dark-theme' : 'light-theme'}>\n * Content\n * </div>;\n * }\n * ```\n */\nexport function useFeatureFlagValue(flagKey: string): string | boolean | undefined {\n\tconst analytics = useAnalytics();\n\treturn analytics.getFeatureFlag(flagKey);\n}\n","\"use client\";\n\nimport { createContext, type ReactNode, useEffect, useRef } from \"react\";\nimport { createAnalytics } from \"./client\";\nimport { mergeConfig } from \"./config\";\nimport type { Analytics, AnalyticsConfig } from \"./types\";\n\n/**\n * React Context for analytics\n */\nexport const AnalyticsContext = createContext<Analytics | null>(null);\n\n/**\n * Props for the AnalyticsProvider\n */\nexport interface AnalyticsProviderProps {\n\tchildren: ReactNode;\n\tconfig: AnalyticsConfig;\n}\n\n/**\n * Provider component that initializes PostHog and provides analytics context\n *\n * @example\n * ```tsx\n * import { AnalyticsProvider } from '@codaco/analytics';\n *\n * function App({ children }) {\n * return (\n * <AnalyticsProvider\n * config={{\n * installationId: 'your-installation-id',\n * apiKey: 'phc_your_api_key', // optional if set via env\n * apiHost: 'https://ph-relay.networkcanvas.com', // optional\n * }}\n * >\n * {children}\n * </AnalyticsProvider>\n * );\n * }\n * ```\n */\nexport function AnalyticsProvider({ children, config }: AnalyticsProviderProps) {\n\tconst analyticsRef = useRef<Analytics | null>(null);\n\n\t// Initialize analytics only once\n\tuseEffect(() => {\n\t\tif (!analyticsRef.current) {\n\t\t\tconst mergedConfig = mergeConfig(config);\n\t\t\tanalyticsRef.current = createAnalytics(mergedConfig);\n\t\t}\n\t}, []); // Empty deps - only initialize once\n\n\t// Don't render children until analytics is initialized\n\tif (!analyticsRef.current) {\n\t\treturn null;\n\t}\n\n\treturn <AnalyticsContext.Provider value={analyticsRef.current}>{children}</AnalyticsContext.Provider>;\n}\n","import posthog from \"posthog-js\";\nimport type { Analytics, AnalyticsConfig, ErrorProperties, EventProperties, EventType } from \"./types\";\nimport { ensureError } from \"./utils\";\n\n/**\n * Create a client-side analytics instance\n * This wraps PostHog with Network Canvas-specific functionality\n */\nexport function createAnalytics(config: Required<AnalyticsConfig>): Analytics {\n\tconst { apiHost, apiKey, installationId, disabled, debug, posthogOptions } = config;\n\n\t// If analytics is disabled, return a no-op implementation\n\tif (disabled) {\n\t\treturn createNoOpAnalytics(installationId);\n\t}\n\n\t// Initialize PostHog\n\tposthog.init(apiKey, {\n\t\tapi_host: apiHost,\n\t\tloaded: (posthogInstance) => {\n\t\t\t// Set installation ID as a super property (included with every event)\n\t\t\tposthogInstance.register({\n\t\t\t\tinstallation_id: installationId,\n\t\t\t});\n\n\t\t\tif (debug) {\n\t\t\t\tposthogInstance.debug();\n\t\t\t}\n\t\t},\n\t\t...posthogOptions,\n\t});\n\n\treturn {\n\t\ttrackEvent: (eventType: EventType | string, properties?: EventProperties) => {\n\t\t\tif (disabled) return;\n\n\t\t\ttry {\n\t\t\t\tposthog.capture(eventType, {\n\t\t\t\t\t...properties,\n\t\t\t\t\t// Flatten metadata into properties for better PostHog integration\n\t\t\t\t\t...(properties?.metadata ?? {}),\n\t\t\t\t});\n\t\t\t} catch (_e) {\n\t\t\t\tif (debug) {\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\ttrackError: (error: Error, additionalProperties?: EventProperties) => {\n\t\t\tif (disabled) return;\n\n\t\t\ttry {\n\t\t\t\tconst errorObj = ensureError(error);\n\t\t\t\tconst errorProperties: ErrorProperties = {\n\t\t\t\t\tmessage: errorObj.message,\n\t\t\t\t\tname: errorObj.name,\n\t\t\t\t\tstack: errorObj.stack,\n\t\t\t\t\tcause: errorObj.cause ? String(errorObj.cause) : undefined,\n\t\t\t\t\t...additionalProperties,\n\t\t\t\t};\n\n\t\t\t\tposthog.capture(\"error\", {\n\t\t\t\t\t...errorProperties,\n\t\t\t\t\t// Flatten metadata\n\t\t\t\t\t...(additionalProperties?.metadata ?? {}),\n\t\t\t\t});\n\t\t\t} catch (_e) {\n\t\t\t\tif (debug) {\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tisFeatureEnabled: (flagKey: string) => {\n\t\t\tif (disabled) return false;\n\n\t\t\ttry {\n\t\t\t\treturn posthog.isFeatureEnabled(flagKey);\n\t\t\t} catch (_e) {\n\t\t\t\tif (debug) {\n\t\t\t\t}\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t},\n\n\t\tgetFeatureFlag: (flagKey: string) => {\n\t\t\tif (disabled) return undefined;\n\n\t\t\ttry {\n\t\t\t\treturn posthog.getFeatureFlag(flagKey);\n\t\t\t} catch (_e) {\n\t\t\t\tif (debug) {\n\t\t\t\t}\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t},\n\n\t\treloadFeatureFlags: () => {\n\t\t\tif (disabled) return;\n\n\t\t\ttry {\n\t\t\t\tposthog.reloadFeatureFlags();\n\t\t\t} catch (_e) {\n\t\t\t\tif (debug) {\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tidentify: (distinctId: string, properties?: Record<string, unknown>) => {\n\t\t\tif (disabled) return;\n\n\t\t\ttry {\n\t\t\t\tposthog.identify(distinctId, properties);\n\t\t\t} catch (_e) {\n\t\t\t\tif (debug) {\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\treset: () => {\n\t\t\tif (disabled) return;\n\n\t\t\ttry {\n\t\t\t\tposthog.reset();\n\t\t\t} catch (_e) {\n\t\t\t\tif (debug) {\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tisEnabled: () => !disabled,\n\n\t\tgetInstallationId: () => installationId,\n\t};\n}\n\n/**\n * Create a no-op analytics instance when analytics is disabled\n */\nfunction createNoOpAnalytics(installationId: string): Analytics {\n\treturn {\n\t\ttrackEvent: () => {},\n\t\ttrackError: () => {},\n\t\tisFeatureEnabled: () => false,\n\t\tgetFeatureFlag: () => undefined,\n\t\treloadFeatureFlags: () => {},\n\t\tidentify: () => {},\n\t\treset: () => {},\n\t\tisEnabled: () => false,\n\t\tgetInstallationId: () => installationId,\n\t};\n}\n","import z from \"zod\";\n\n/**\n * Event types supported by the analytics system.\n * These are converted to snake_case for PostHog.\n */\nexport const eventTypes = [\n\t\"app_setup\",\n\t\"protocol_installed\",\n\t\"interview_started\",\n\t\"interview_completed\",\n\t\"data_exported\",\n\t\"error\",\n] as const;\n\nexport type EventType = (typeof eventTypes)[number];\n\n/**\n * Legacy event type mapping for backward compatibility\n */\nexport const legacyEventTypeMap: Record<string, EventType> = {\n\tAppSetup: \"app_setup\",\n\tProtocolInstalled: \"protocol_installed\",\n\tInterviewStarted: \"interview_started\",\n\tInterviewCompleted: \"interview_completed\",\n\tDataExported: \"data_exported\",\n\tError: \"error\",\n};\n\n/**\n * Standard event properties that can be sent with any event\n */\nexport const EventPropertiesSchema = z.object({\n\tmetadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nexport type EventProperties = z.infer<typeof EventPropertiesSchema>;\n\n/**\n * Error-specific properties for error tracking\n */\nexport const ErrorPropertiesSchema = EventPropertiesSchema.extend({\n\tmessage: z.string(),\n\tname: z.string(),\n\tstack: z.string().optional(),\n\tcause: z.string().optional(),\n});\n\nexport type ErrorProperties = z.infer<typeof ErrorPropertiesSchema>;\n\n/**\n * Analytics configuration options\n *\n * This package is designed to work exclusively with the Cloudflare Worker\n * reverse proxy at ph-relay.networkcanvas.com. All authentication is handled\n * by the worker, so the API key is optional.\n */\nexport interface AnalyticsConfig {\n\t/**\n\t * PostHog API host - should point to the Cloudflare Worker reverse proxy\n\t * Defaults to \"https://ph-relay.networkcanvas.com\"\n\t */\n\tapiHost?: string;\n\n\t/**\n\t * PostHog project API key (optional)\n\t *\n\t * When using the reverse proxy (default), authentication is handled by the\n\t * Cloudflare Worker. A placeholder key will be used for client-side PostHog\n\t * initialization if not provided.\n\t *\n\t * Only set this if you need to override the default behavior.\n\t */\n\tapiKey?: string;\n\n\t/**\n\t * Unique identifier for this installation/deployment\n\t * This is included with every event as a super property\n\t */\n\tinstallationId: string;\n\n\t/**\n\t * Disable all analytics tracking\n\t * Can be set via DISABLE_ANALYTICS or NEXT_PUBLIC_DISABLE_ANALYTICS env var\n\t */\n\tdisabled?: boolean;\n\n\t/**\n\t * Enable debug mode for PostHog\n\t */\n\tdebug?: boolean;\n\n\t/**\n\t * Additional options to pass to PostHog initialization\n\t */\n\tposthogOptions?: {\n\t\t/**\n\t\t * Disable session recording\n\t\t */\n\t\tdisable_session_recording?: boolean;\n\n\t\t/**\n\t\t * Autocapture settings\n\t\t */\n\t\tautocapture?: boolean;\n\n\t\t/**\n\t\t * Capture pageviews automatically\n\t\t */\n\t\tcapture_pageview?: boolean;\n\n\t\t/**\n\t\t * Capture pageleave events\n\t\t */\n\t\tcapture_pageleave?: boolean;\n\n\t\t/**\n\t\t * Cross-subdomain cookie\n\t\t */\n\t\tcross_subdomain_cookie?: boolean;\n\n\t\t/**\n\t\t * Advanced feature flags support\n\t\t */\n\t\tadvanced_disable_feature_flags?: boolean;\n\n\t\t/**\n\t\t * Other PostHog options\n\t\t */\n\t\t[key: string]: unknown;\n\t};\n}\n\n/**\n * Analytics instance interface\n */\nexport interface Analytics {\n\t/**\n\t * Track a custom event\n\t */\n\ttrackEvent: (eventType: EventType | string, properties?: EventProperties) => void;\n\n\t/**\n\t * Track an error with full stack trace\n\t */\n\ttrackError: (error: Error, additionalProperties?: EventProperties) => void;\n\n\t/**\n\t * Check if a feature flag is enabled\n\t */\n\tisFeatureEnabled: (flagKey: string) => boolean | undefined;\n\n\t/**\n\t * Get the value of a feature flag\n\t */\n\tgetFeatureFlag: (flagKey: string) => string | boolean | undefined;\n\n\t/**\n\t * Reload feature flags from PostHog\n\t */\n\treloadFeatureFlags: () => void;\n\n\t/**\n\t * Identify a user (optional - for advanced use cases)\n\t * Note: By default we only track installations, not users\n\t */\n\tidentify: (distinctId: string, properties?: Record<string, unknown>) => void;\n\n\t/**\n\t * Reset the user identity\n\t */\n\treset: () => void;\n\n\t/**\n\t * Check if analytics is enabled\n\t */\n\tisEnabled: () => boolean;\n\n\t/**\n\t * Get the installation ID\n\t */\n\tgetInstallationId: () => string;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts","../src/hooks.ts","../src/provider.tsx","../src/types.ts"],"sourcesContent":["import posthog from \"posthog-js\";\nimport type { Analytics, AnalyticsConfig, ErrorProperties, EventProperties, EventType } from \"./types\";\nimport { ensureError } from \"./utils\";\n\n/**\n * Create a client-side analytics instance\n * This wraps PostHog with Network Canvas-specific functionality\n */\nexport function createAnalytics(config: Required<AnalyticsConfig>): Analytics {\n\tconst { app, apiHost, apiKey, installationId, disabled, debug, posthogOptions } = config;\n\n\t// If analytics is disabled, return a no-op implementation\n\tif (disabled) {\n\t\treturn createNoOpAnalytics(installationId);\n\t}\n\n\t// Initialize PostHog\n\tposthog.init(apiKey, {\n\t\tapi_host: apiHost,\n\t\tloaded: (posthogInstance) => {\n\t\t\t// Set installation ID as a super property (included with every event)\n\t\t\tposthogInstance.register({\n\t\t\t\tapp,\n\t\t\t\tinstallation_id: installationId,\n\t\t\t});\n\n\t\t\tif (debug) {\n\t\t\t\tposthogInstance.debug();\n\t\t\t}\n\t\t},\n\t\t...posthogOptions,\n\t});\n\n\treturn {\n\t\ttrackEvent: (eventType: EventType | string, properties?: EventProperties) => {\n\t\t\tif (disabled) return;\n\n\t\t\ttry {\n\t\t\t\tposthog.capture(eventType, {\n\t\t\t\t\t...properties,\n\t\t\t\t\t// Flatten metadata into properties for better PostHog integration\n\t\t\t\t\t...(properties?.metadata ?? {}),\n\t\t\t\t});\n\t\t\t} catch (_e) {\n\t\t\t\tif (debug) {\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\ttrackError: (error: Error, additionalProperties?: EventProperties) => {\n\t\t\tif (disabled) return;\n\n\t\t\ttry {\n\t\t\t\tconst errorObj = ensureError(error);\n\t\t\t\tconst errorProperties: ErrorProperties = {\n\t\t\t\t\tmessage: errorObj.message,\n\t\t\t\t\tname: errorObj.name,\n\t\t\t\t\tstack: errorObj.stack,\n\t\t\t\t\tcause: errorObj.cause ? String(errorObj.cause) : undefined,\n\t\t\t\t\t...additionalProperties,\n\t\t\t\t};\n\n\t\t\t\tposthog.capture(\"error\", {\n\t\t\t\t\t...errorProperties,\n\t\t\t\t\t// Flatten metadata\n\t\t\t\t\t...(additionalProperties?.metadata ?? {}),\n\t\t\t\t});\n\t\t\t} catch (_e) {\n\t\t\t\tif (debug) {\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tisFeatureEnabled: (flagKey: string) => {\n\t\t\tif (disabled) return false;\n\n\t\t\ttry {\n\t\t\t\treturn posthog.isFeatureEnabled(flagKey);\n\t\t\t} catch (_e) {\n\t\t\t\tif (debug) {\n\t\t\t\t}\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t},\n\n\t\tgetFeatureFlag: (flagKey: string) => {\n\t\t\tif (disabled) return undefined;\n\n\t\t\ttry {\n\t\t\t\treturn posthog.getFeatureFlag(flagKey);\n\t\t\t} catch (_e) {\n\t\t\t\tif (debug) {\n\t\t\t\t}\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t},\n\n\t\treloadFeatureFlags: () => {\n\t\t\tif (disabled) return;\n\n\t\t\ttry {\n\t\t\t\tposthog.reloadFeatureFlags();\n\t\t\t} catch (_e) {\n\t\t\t\tif (debug) {\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tidentify: (distinctId: string, properties?: Record<string, unknown>) => {\n\t\t\tif (disabled) return;\n\n\t\t\ttry {\n\t\t\t\tposthog.identify(distinctId, properties);\n\t\t\t} catch (_e) {\n\t\t\t\tif (debug) {\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\treset: () => {\n\t\t\tif (disabled) return;\n\n\t\t\ttry {\n\t\t\t\tposthog.reset();\n\t\t\t} catch (_e) {\n\t\t\t\tif (debug) {\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tisEnabled: () => !disabled,\n\n\t\tgetInstallationId: () => installationId,\n\t};\n}\n\n/**\n * Create a no-op analytics instance when analytics is disabled\n */\nfunction createNoOpAnalytics(installationId: string): Analytics {\n\treturn {\n\t\ttrackEvent: () => {},\n\t\ttrackError: () => {},\n\t\tisFeatureEnabled: () => false,\n\t\tgetFeatureFlag: () => undefined,\n\t\treloadFeatureFlags: () => {},\n\t\tidentify: () => {},\n\t\treset: () => {},\n\t\tisEnabled: () => false,\n\t\tgetInstallationId: () => installationId,\n\t};\n}\n","\"use client\";\n\nimport { useContext } from \"react\";\nimport { AnalyticsContext } from \"./provider\";\nimport type { Analytics } from \"./types\";\n\n/**\n * Hook to access analytics functionality in React components\n *\n * @example\n * ```tsx\n * import { useAnalytics } from '@codaco/analytics';\n *\n * function MyComponent() {\n * const { trackEvent, trackError } = useAnalytics();\n *\n * const handleAction = () => {\n * trackEvent('protocol_installed', {\n * metadata: { protocolName: 'My Protocol' }\n * });\n * };\n *\n * return <button onClick={handleAction}>Install Protocol</button>;\n * }\n * ```\n */\nexport function useAnalytics(): Analytics {\n\tconst analytics = useContext(AnalyticsContext);\n\n\tif (!analytics) {\n\t\tthrow new Error(\"useAnalytics must be used within an AnalyticsProvider\");\n\t}\n\n\treturn analytics;\n}\n\n/**\n * Hook to access feature flags\n *\n * @example\n * ```tsx\n * import { useFeatureFlag } from '@codaco/analytics';\n *\n * function MyComponent() {\n * const isNewFeatureEnabled = useFeatureFlag('new-feature');\n *\n * if (isNewFeatureEnabled) {\n * return <NewFeature />;\n * }\n *\n * return <OldFeature />;\n * }\n * ```\n */\nexport function useFeatureFlag(flagKey: string): boolean {\n\tconst analytics = useAnalytics();\n\treturn analytics.isFeatureEnabled(flagKey) ?? false;\n}\n\n/**\n * Hook to access feature flag values (for multivariate flags)\n *\n * @example\n * ```tsx\n * import { useFeatureFlagValue } from '@codaco/analytics';\n *\n * function MyComponent() {\n * const theme = useFeatureFlagValue('theme-variant');\n *\n * return <div className={theme === 'dark' ? 'dark-theme' : 'light-theme'}>\n * Content\n * </div>;\n * }\n * ```\n */\nexport function useFeatureFlagValue(flagKey: string): string | boolean | undefined {\n\tconst analytics = useAnalytics();\n\treturn analytics.getFeatureFlag(flagKey);\n}\n","\"use client\";\n\nimport { createContext, type ReactNode, useEffect, useRef } from \"react\";\nimport { createAnalytics } from \"./client\";\nimport { mergeConfig } from \"./config\";\nimport type { Analytics, AnalyticsConfig } from \"./types\";\n\n/**\n * React Context for analytics\n */\nexport const AnalyticsContext = createContext<Analytics | null>(null);\n\n/**\n * Props for the AnalyticsProvider\n */\nexport type AnalyticsProviderProps = {\n\tchildren: ReactNode;\n\tconfig: AnalyticsConfig;\n};\n\n/**\n * Provider component that initializes PostHog and provides analytics context\n *\n * @example\n * ```tsx\n * import { AnalyticsProvider } from '@codaco/analytics';\n *\n * function App({ children }) {\n * return (\n * <AnalyticsProvider\n * config={{\n * installationId: 'your-installation-id',\n * apiKey: 'phc_your_api_key', // optional if set via env\n * apiHost: 'https://ph-relay.networkcanvas.com', // optional\n * }}\n * >\n * {children}\n * </AnalyticsProvider>\n * );\n * }\n * ```\n */\nexport function AnalyticsProvider({ children, config }: AnalyticsProviderProps) {\n\tconst analyticsRef = useRef<Analytics | null>(null);\n\n\t// Initialize analytics only once\n\tuseEffect(() => {\n\t\tif (!analyticsRef.current) {\n\t\t\tconst mergedConfig = mergeConfig(config);\n\t\t\tanalyticsRef.current = createAnalytics(mergedConfig);\n\t\t}\n\t}, []); // Empty deps - only initialize once\n\n\t// Don't render children until analytics is initialized\n\tif (!analyticsRef.current) {\n\t\treturn null;\n\t}\n\n\treturn <AnalyticsContext.Provider value={analyticsRef.current}>{children}</AnalyticsContext.Provider>;\n}\n","import { z } from \"zod\";\n\n/**\n * Common event types supported by the analytics system.\n * This list is non-exhaustive — trackEvent accepts any string.\n * These are provided for discoverability and autocomplete.\n */\nexport const eventTypes = [\n\t\"app_setup\",\n\t\"protocol_installed\",\n\t\"interview_started\",\n\t\"interview_completed\",\n\t\"data_exported\",\n\t\"error\",\n] as const;\n\nexport type EventType = (typeof eventTypes)[number];\n\n/**\n * Legacy event type mapping for backward compatibility\n */\nexport const legacyEventTypeMap: Record<string, EventType> = {\n\tAppSetup: \"app_setup\",\n\tProtocolInstalled: \"protocol_installed\",\n\tInterviewStarted: \"interview_started\",\n\tInterviewCompleted: \"interview_completed\",\n\tDataExported: \"data_exported\",\n\tError: \"error\",\n};\n\n/**\n * Supported application identifiers.\n * Used to segment events within a single PostHog project.\n */\nexport const appNames = [\n\t\"Fresco\",\n\t\"Studio\",\n\t\"Architect\",\n\t\"Interviewer\",\n\t\"ArchitectWeb\",\n\t\"CommunityForum\",\n\t\"ProjectWebsite\",\n\t\"Documentation\",\n] as const;\n\nexport type AppName = (typeof appNames)[number];\n\n/**\n * Standard event properties that can be sent with any event\n */\nexport const EventPropertiesSchema = z.object({\n\tmetadata: z.record(z.string(), z.unknown()).optional(),\n});\n\nexport type EventProperties = z.infer<typeof EventPropertiesSchema>;\n\n/**\n * Error-specific properties for error tracking\n */\nexport const ErrorPropertiesSchema = EventPropertiesSchema.extend({\n\tmessage: z.string(),\n\tname: z.string(),\n\tstack: z.string().optional(),\n\tcause: z.string().optional(),\n});\n\nexport type ErrorProperties = z.infer<typeof ErrorPropertiesSchema>;\n\n/**\n * Analytics configuration options\n *\n * This package is designed to work exclusively with the Cloudflare Worker\n * reverse proxy at ph-relay.networkcanvas.com. All authentication is handled\n * by the worker, so the API key is optional.\n */\nexport type AnalyticsConfig = {\n\t/**\n\t * Application identifier. Included with every event as a super property\n\t * to segment analytics within a single PostHog project.\n\t */\n\tapp: AppName;\n\n\t/**\n\t * PostHog API host - should point to the Cloudflare Worker reverse proxy\n\t * Defaults to \"https://ph-relay.networkcanvas.com\"\n\t */\n\tapiHost?: string;\n\n\t/**\n\t * PostHog project API key (optional)\n\t *\n\t * When using the reverse proxy (default), authentication is handled by the\n\t * Cloudflare Worker. A placeholder key will be used for client-side PostHog\n\t * initialization if not provided.\n\t *\n\t * Only set this if you need to override the default behavior.\n\t */\n\tapiKey?: string;\n\n\t/**\n\t * Unique identifier for this installation/deployment\n\t * This is included with every event as a super property\n\t */\n\tinstallationId: string;\n\n\t/**\n\t * Disable all analytics tracking\n\t * Can be set via DISABLE_ANALYTICS or NEXT_PUBLIC_DISABLE_ANALYTICS env var\n\t */\n\tdisabled?: boolean;\n\n\t/**\n\t * Enable debug mode for PostHog\n\t */\n\tdebug?: boolean;\n\n\t/**\n\t * Additional options to pass to PostHog initialization\n\t */\n\tposthogOptions?: {\n\t\t/**\n\t\t * Disable session recording\n\t\t */\n\t\tdisable_session_recording?: boolean;\n\n\t\t/**\n\t\t * Autocapture settings\n\t\t */\n\t\tautocapture?: boolean;\n\n\t\t/**\n\t\t * Capture pageviews automatically\n\t\t */\n\t\tcapture_pageview?: boolean;\n\n\t\t/**\n\t\t * Capture pageleave events\n\t\t */\n\t\tcapture_pageleave?: boolean;\n\n\t\t/**\n\t\t * Cross-subdomain cookie\n\t\t */\n\t\tcross_subdomain_cookie?: boolean;\n\n\t\t/**\n\t\t * Advanced feature flags support\n\t\t */\n\t\tadvanced_disable_feature_flags?: boolean;\n\n\t\t/**\n\t\t * Other PostHog options\n\t\t */\n\t\t[key: string]: unknown;\n\t};\n};\n\n/**\n * Analytics instance interface\n */\nexport type Analytics = {\n\t/**\n\t * Track a custom event\n\t */\n\ttrackEvent: (eventType: EventType | string, properties?: EventProperties) => void;\n\n\t/**\n\t * Track an error with full stack trace\n\t */\n\ttrackError: (error: Error, additionalProperties?: EventProperties) => void;\n\n\t/**\n\t * Check if a feature flag is enabled\n\t */\n\tisFeatureEnabled: (flagKey: string) => boolean | undefined;\n\n\t/**\n\t * Get the value of a feature flag\n\t */\n\tgetFeatureFlag: (flagKey: string) => string | boolean | undefined;\n\n\t/**\n\t * Reload feature flags from PostHog\n\t */\n\treloadFeatureFlags: () => void;\n\n\t/**\n\t * Identify a user (optional - for advanced use cases)\n\t * Note: By default we only track installations, not users\n\t */\n\tidentify: (distinctId: string, properties?: Record<string, unknown>) => void;\n\n\t/**\n\t * Reset the user identity\n\t */\n\treset: () => void;\n\n\t/**\n\t * Check if analytics is enabled\n\t */\n\tisEnabled: () => boolean;\n\n\t/**\n\t * Get the installation ID\n\t */\n\tgetInstallationId: () => string;\n};\n"],"mappings":"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","names":[]}
|
package/dist/server.d.ts
CHANGED
package/dist/server.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ensureError,
|
|
3
3
|
mergeConfig
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-TPZBZWEN.js";
|
|
5
5
|
|
|
6
6
|
// src/server.ts
|
|
7
7
|
var ServerAnalytics = class {
|
|
@@ -98,6 +98,7 @@ var ServerAnalytics = class {
|
|
|
98
98
|
event,
|
|
99
99
|
properties: {
|
|
100
100
|
...properties,
|
|
101
|
+
app: this.config.app,
|
|
101
102
|
installation_id: this.config.installationId
|
|
102
103
|
},
|
|
103
104
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import { mergeConfig } from \"./config\";\nimport type { Analytics, AnalyticsConfig, ErrorProperties, EventProperties, EventType } from \"./types\";\nimport { ensureError } from \"./utils\";\n\n/**\n * Server-side analytics implementation\n * This uses PostHog's API directly for server-side tracking\n */\nclass ServerAnalytics implements Analytics {\n\tprivate config: Required<AnalyticsConfig>;\n\tprivate disabled: boolean;\n\n\tconstructor(config: AnalyticsConfig) {\n\t\tthis.config = mergeConfig(config);\n\t\tthis.disabled = this.config.disabled;\n\t}\n\n\t/**\n\t * Track an event on the server-side\n\t */\n\ttrackEvent(eventType: EventType | string, properties?: EventProperties): void {\n\t\tif (this.disabled) return;\n\n\t\t// Send event to PostHog using fetch\n\t\tthis.sendToPostHog(eventType, {\n\t\t\t...properties,\n\t\t\t...(properties?.metadata ?? {}),\n\t\t}).catch((_error) => {\n\t\t\tif (this.config.debug) {\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Track an error on the server-side\n\t */\n\ttrackError(error: Error, additionalProperties?: EventProperties): void {\n\t\tif (this.disabled) return;\n\n\t\tconst errorObj = ensureError(error);\n\t\tconst errorProperties: ErrorProperties = {\n\t\t\tmessage: errorObj.message,\n\t\t\tname: errorObj.name,\n\t\t\tstack: errorObj.stack,\n\t\t\tcause: errorObj.cause ? String(errorObj.cause) : undefined,\n\t\t\t...additionalProperties,\n\t\t};\n\n\t\tthis.sendToPostHog(\"error\", {\n\t\t\t...errorProperties,\n\t\t\t...(additionalProperties?.metadata ?? {}),\n\t\t}).catch((_error) => {\n\t\t\tif (this.config.debug) {\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Feature flags are not supported in server-side mode\n\t * Use client-side hooks or PostHog API directly for feature flags\n\t */\n\tisFeatureEnabled(_flagKey: string): boolean {\n\t\treturn false;\n\t}\n\n\t/**\n\t * Feature flags are not supported in server-side mode\n\t */\n\tgetFeatureFlag(_flagKey: string): string | boolean | undefined {\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Feature flags are not supported in server-side mode\n\t */\n\treloadFeatureFlags(): void {}\n\n\t/**\n\t * User identification on the server-side\n\t */\n\tidentify(distinctId: string, properties?: Record<string, unknown>): void {\n\t\tif (this.disabled) return;\n\n\t\tthis.sendToPostHog(\"$identify\", {\n\t\t\t$set: properties ?? {},\n\t\t\tdistinct_id: distinctId,\n\t\t}).catch((_error) => {\n\t\t\tif (this.config.debug) {\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Reset is not applicable on server-side\n\t */\n\treset(): void {}\n\n\tisEnabled(): boolean {\n\t\treturn !this.disabled;\n\t}\n\n\tgetInstallationId(): string {\n\t\treturn this.config.installationId;\n\t}\n\n\t/**\n\t * Send event to PostHog using fetch API\n\t * Note: API key authentication is handled by the Cloudflare Worker proxy,\n\t * so we don't include it in the payload.\n\t */\n\tprivate async sendToPostHog(event: string, properties: Record<string, unknown>): Promise<void> {\n\t\tif (this.disabled) return;\n\n\t\tconst payload = {\n\t\t\tevent,\n\t\t\tproperties: {\n\t\t\t\t...properties,\n\t\t\t\tinstallation_id: this.config.installationId,\n\t\t\t},\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t};\n\n\t\ttry {\n\t\t\tconst response = await fetch(`${this.config.apiHost}/capture`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t// Use keepalive for reliability\n\t\t\t\tkeepalive: true,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new Error(`PostHog API returned ${response.status}: ${response.statusText}`);\n\t\t\t}\n\t\t} catch (_error) {\n\t\t\t// Silently fail - we don't want analytics errors to break the app\n\t\t\tif (this.config.debug) {\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Global server-side analytics instance\n */\nlet serverAnalyticsInstance: ServerAnalytics | null = null;\n\n/**\n * Initialize server-side analytics\n * Call this once in your app (e.g., in a layout or middleware)\n *\n * @example\n * ```ts\n * // In your Next.js layout or API route\n * import { initServerAnalytics } from '@codaco/analytics/server';\n *\n * initServerAnalytics({\n * installationId: 'your-unique-installation-id',\n * });\n * ```\n */\nexport function initServerAnalytics(config: AnalyticsConfig): void {\n\tif (!serverAnalyticsInstance) {\n\t\tserverAnalyticsInstance = new ServerAnalytics(config);\n\t}\n}\n\n/**\n * Get the server-side analytics instance\n * Use this in server components, API routes, and server actions\n *\n * @example\n * ```ts\n * import { getServerAnalytics } from '@codaco/analytics/server';\n *\n * export async function POST(request: Request) {\n * const analytics = getServerAnalytics();\n * analytics.trackEvent('data_exported', {\n * metadata: { format: 'csv' }\n * });\n *\n * // ... rest of your handler\n * }\n * ```\n */\nexport function getServerAnalytics(): Analytics {\n\tif (!serverAnalyticsInstance) {\n\t\tthrow new Error(\n\t\t\t\"Server analytics not initialized. Call initServerAnalytics() first (e.g., in your root layout or middleware).\",\n\t\t);\n\t}\n\n\treturn serverAnalyticsInstance;\n}\n\n/**\n * Convenience export for direct usage\n * Requires calling initServerAnalytics() first\n */\nexport const serverAnalytics = new Proxy({} as Analytics, {\n\tget(_target, prop) {\n\t\tif (!serverAnalyticsInstance) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Server analytics not initialized. Call initServerAnalytics({ installationId: '...' }) first \" +\n\t\t\t\t\t\"(e.g., in your root layout or middleware).\",\n\t\t\t);\n\t\t}\n\n\t\treturn serverAnalyticsInstance[prop as keyof Analytics];\n\t},\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server.ts"],"sourcesContent":["import { mergeConfig } from \"./config\";\nimport type { Analytics, AnalyticsConfig, ErrorProperties, EventProperties, EventType } from \"./types\";\nimport { ensureError } from \"./utils\";\n\n/**\n * Server-side analytics implementation\n * This uses PostHog's API directly for server-side tracking\n */\nclass ServerAnalytics implements Analytics {\n\tprivate config: Required<AnalyticsConfig>;\n\tprivate disabled: boolean;\n\n\tconstructor(config: AnalyticsConfig) {\n\t\tthis.config = mergeConfig(config);\n\t\tthis.disabled = this.config.disabled;\n\t}\n\n\t/**\n\t * Track an event on the server-side\n\t */\n\ttrackEvent(eventType: EventType | string, properties?: EventProperties): void {\n\t\tif (this.disabled) return;\n\n\t\t// Send event to PostHog using fetch\n\t\tthis.sendToPostHog(eventType, {\n\t\t\t...properties,\n\t\t\t...(properties?.metadata ?? {}),\n\t\t}).catch((_error) => {\n\t\t\tif (this.config.debug) {\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Track an error on the server-side\n\t */\n\ttrackError(error: Error, additionalProperties?: EventProperties): void {\n\t\tif (this.disabled) return;\n\n\t\tconst errorObj = ensureError(error);\n\t\tconst errorProperties: ErrorProperties = {\n\t\t\tmessage: errorObj.message,\n\t\t\tname: errorObj.name,\n\t\t\tstack: errorObj.stack,\n\t\t\tcause: errorObj.cause ? String(errorObj.cause) : undefined,\n\t\t\t...additionalProperties,\n\t\t};\n\n\t\tthis.sendToPostHog(\"error\", {\n\t\t\t...errorProperties,\n\t\t\t...(additionalProperties?.metadata ?? {}),\n\t\t}).catch((_error) => {\n\t\t\tif (this.config.debug) {\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Feature flags are not supported in server-side mode\n\t * Use client-side hooks or PostHog API directly for feature flags\n\t */\n\tisFeatureEnabled(_flagKey: string): boolean {\n\t\treturn false;\n\t}\n\n\t/**\n\t * Feature flags are not supported in server-side mode\n\t */\n\tgetFeatureFlag(_flagKey: string): string | boolean | undefined {\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Feature flags are not supported in server-side mode\n\t */\n\treloadFeatureFlags(): void {}\n\n\t/**\n\t * User identification on the server-side\n\t */\n\tidentify(distinctId: string, properties?: Record<string, unknown>): void {\n\t\tif (this.disabled) return;\n\n\t\tthis.sendToPostHog(\"$identify\", {\n\t\t\t$set: properties ?? {},\n\t\t\tdistinct_id: distinctId,\n\t\t}).catch((_error) => {\n\t\t\tif (this.config.debug) {\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Reset is not applicable on server-side\n\t */\n\treset(): void {}\n\n\tisEnabled(): boolean {\n\t\treturn !this.disabled;\n\t}\n\n\tgetInstallationId(): string {\n\t\treturn this.config.installationId;\n\t}\n\n\t/**\n\t * Send event to PostHog using fetch API\n\t * Note: API key authentication is handled by the Cloudflare Worker proxy,\n\t * so we don't include it in the payload.\n\t */\n\tprivate async sendToPostHog(event: string, properties: Record<string, unknown>): Promise<void> {\n\t\tif (this.disabled) return;\n\n\t\tconst payload = {\n\t\t\tevent,\n\t\t\tproperties: {\n\t\t\t\t...properties,\n\t\t\t\tapp: this.config.app,\n\t\t\t\tinstallation_id: this.config.installationId,\n\t\t\t},\n\t\t\ttimestamp: new Date().toISOString(),\n\t\t};\n\n\t\ttry {\n\t\t\tconst response = await fetch(`${this.config.apiHost}/capture`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\t// Use keepalive for reliability\n\t\t\t\tkeepalive: true,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new Error(`PostHog API returned ${response.status}: ${response.statusText}`);\n\t\t\t}\n\t\t} catch (_error) {\n\t\t\t// Silently fail - we don't want analytics errors to break the app\n\t\t\tif (this.config.debug) {\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Global server-side analytics instance\n */\nlet serverAnalyticsInstance: ServerAnalytics | null = null;\n\n/**\n * Initialize server-side analytics\n * Call this once in your app (e.g., in a layout or middleware)\n *\n * @example\n * ```ts\n * // In your Next.js layout or API route\n * import { initServerAnalytics } from '@codaco/analytics/server';\n *\n * initServerAnalytics({\n * installationId: 'your-unique-installation-id',\n * });\n * ```\n */\nexport function initServerAnalytics(config: AnalyticsConfig): void {\n\tif (!serverAnalyticsInstance) {\n\t\tserverAnalyticsInstance = new ServerAnalytics(config);\n\t}\n}\n\n/**\n * Get the server-side analytics instance\n * Use this in server components, API routes, and server actions\n *\n * @example\n * ```ts\n * import { getServerAnalytics } from '@codaco/analytics/server';\n *\n * export async function POST(request: Request) {\n * const analytics = getServerAnalytics();\n * analytics.trackEvent('data_exported', {\n * metadata: { format: 'csv' }\n * });\n *\n * // ... rest of your handler\n * }\n * ```\n */\nexport function getServerAnalytics(): Analytics {\n\tif (!serverAnalyticsInstance) {\n\t\tthrow new Error(\n\t\t\t\"Server analytics not initialized. Call initServerAnalytics() first (e.g., in your root layout or middleware).\",\n\t\t);\n\t}\n\n\treturn serverAnalyticsInstance;\n}\n\n/**\n * Convenience export for direct usage\n * Requires calling initServerAnalytics() first\n */\nexport const serverAnalytics = new Proxy({} as Analytics, {\n\tget(_target, prop) {\n\t\tif (!serverAnalyticsInstance) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Server analytics not initialized. Call initServerAnalytics({ installationId: '...' }) first \" +\n\t\t\t\t\t\"(e.g., in your root layout or middleware).\",\n\t\t\t);\n\t\t}\n\n\t\treturn serverAnalyticsInstance[prop as keyof Analytics];\n\t},\n});\n"],"mappings":"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","names":[]}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import z from 'zod';
|
|
1
|
+
import { z } from 'zod';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Common event types supported by the analytics system.
|
|
5
|
+
* This list is non-exhaustive — trackEvent accepts any string.
|
|
6
|
+
* These are provided for discoverability and autocomplete.
|
|
6
7
|
*/
|
|
7
8
|
declare const eventTypes: readonly ["app_setup", "protocol_installed", "interview_started", "interview_completed", "data_exported", "error"];
|
|
8
9
|
type EventType = (typeof eventTypes)[number];
|
|
@@ -10,6 +11,12 @@ type EventType = (typeof eventTypes)[number];
|
|
|
10
11
|
* Legacy event type mapping for backward compatibility
|
|
11
12
|
*/
|
|
12
13
|
declare const legacyEventTypeMap: Record<string, EventType>;
|
|
14
|
+
/**
|
|
15
|
+
* Supported application identifiers.
|
|
16
|
+
* Used to segment events within a single PostHog project.
|
|
17
|
+
*/
|
|
18
|
+
declare const appNames: readonly ["Fresco", "Studio", "Architect", "Interviewer", "ArchitectWeb", "CommunityForum", "ProjectWebsite", "Documentation"];
|
|
19
|
+
type AppName = (typeof appNames)[number];
|
|
13
20
|
/**
|
|
14
21
|
* Standard event properties that can be sent with any event
|
|
15
22
|
*/
|
|
@@ -35,7 +42,12 @@ type ErrorProperties = z.infer<typeof ErrorPropertiesSchema>;
|
|
|
35
42
|
* reverse proxy at ph-relay.networkcanvas.com. All authentication is handled
|
|
36
43
|
* by the worker, so the API key is optional.
|
|
37
44
|
*/
|
|
38
|
-
|
|
45
|
+
type AnalyticsConfig = {
|
|
46
|
+
/**
|
|
47
|
+
* Application identifier. Included with every event as a super property
|
|
48
|
+
* to segment analytics within a single PostHog project.
|
|
49
|
+
*/
|
|
50
|
+
app: AppName;
|
|
39
51
|
/**
|
|
40
52
|
* PostHog API host - should point to the Cloudflare Worker reverse proxy
|
|
41
53
|
* Defaults to "https://ph-relay.networkcanvas.com"
|
|
@@ -98,11 +110,11 @@ interface AnalyticsConfig {
|
|
|
98
110
|
*/
|
|
99
111
|
[key: string]: unknown;
|
|
100
112
|
};
|
|
101
|
-
}
|
|
113
|
+
};
|
|
102
114
|
/**
|
|
103
115
|
* Analytics instance interface
|
|
104
116
|
*/
|
|
105
|
-
|
|
117
|
+
type Analytics = {
|
|
106
118
|
/**
|
|
107
119
|
* Track a custom event
|
|
108
120
|
*/
|
|
@@ -140,6 +152,6 @@ interface Analytics {
|
|
|
140
152
|
* Get the installation ID
|
|
141
153
|
*/
|
|
142
154
|
getInstallationId: () => string;
|
|
143
|
-
}
|
|
155
|
+
};
|
|
144
156
|
|
|
145
|
-
export { type AnalyticsConfig as A, type ErrorProperties as E, type Analytics as a, type
|
|
157
|
+
export { type AnalyticsConfig as A, type ErrorProperties as E, type Analytics as a, type AppName as b, type EventProperties as c, type EventType as d, appNames as e, eventTypes as f, legacyEventTypeMap as l };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codaco/analytics",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -20,20 +20,20 @@
|
|
|
20
20
|
"tag": "alpha"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"next": "^16.
|
|
24
|
-
"react": "^19.2.
|
|
23
|
+
"next": "^16.1.1",
|
|
24
|
+
"react": "^19.2.3"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@types/node": "^
|
|
28
|
-
"@types/react": "^19.2.
|
|
27
|
+
"@types/node": "^24.10.1",
|
|
28
|
+
"@types/react": "^19.2.8",
|
|
29
29
|
"tsup": "^8.5.1",
|
|
30
30
|
"typescript": "^5.9.3",
|
|
31
|
-
"vitest": "^4.0.
|
|
31
|
+
"vitest": "^4.0.17",
|
|
32
32
|
"@codaco/tsconfig": "0.1.0"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"posthog-js": "^1.
|
|
36
|
-
"zod": "^4.
|
|
35
|
+
"posthog-js": "^1.328.0",
|
|
36
|
+
"zod": "^4.2.0"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"build": "tsup src/index.ts src/server.ts --format esm --dts --clean --sourcemap",
|
|
@@ -26,6 +26,7 @@ describe("createAnalytics", () => {
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
const mockConfig: Required<AnalyticsConfig> = {
|
|
29
|
+
app: "Fresco",
|
|
29
30
|
apiHost: "https://ph-relay.networkcanvas.com",
|
|
30
31
|
apiKey: "phc_test",
|
|
31
32
|
installationId: "test-install-123",
|
|
@@ -54,7 +55,7 @@ describe("createAnalytics", () => {
|
|
|
54
55
|
expect(analytics.isEnabled()).toBe(false);
|
|
55
56
|
});
|
|
56
57
|
|
|
57
|
-
it("should register installation ID as super
|
|
58
|
+
it("should register app and installation ID as super properties", () => {
|
|
58
59
|
const mockPosthogInstance = {
|
|
59
60
|
register: vi.fn(),
|
|
60
61
|
debug: vi.fn(),
|
|
@@ -68,6 +69,7 @@ describe("createAnalytics", () => {
|
|
|
68
69
|
createAnalytics(mockConfig);
|
|
69
70
|
|
|
70
71
|
expect(mockPosthogInstance.register).toHaveBeenCalledWith({
|
|
72
|
+
app: "Fresco",
|
|
71
73
|
installation_id: "test-install-123",
|
|
72
74
|
});
|
|
73
75
|
});
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { defaultConfig, isDisabledByEnv, mergeConfig } from "../config";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
appNames,
|
|
5
|
+
ErrorPropertiesSchema,
|
|
6
|
+
EventPropertiesSchema,
|
|
7
|
+
type EventType,
|
|
8
|
+
eventTypes,
|
|
9
|
+
legacyEventTypeMap,
|
|
10
|
+
} from "../types";
|
|
4
11
|
|
|
5
12
|
describe("Event Types", () => {
|
|
6
13
|
it("should export all event types", () => {
|
|
@@ -120,6 +127,7 @@ describe("Configuration", () => {
|
|
|
120
127
|
describe("mergeConfig", () => {
|
|
121
128
|
it("should merge user config with defaults", () => {
|
|
122
129
|
const userConfig = {
|
|
130
|
+
app: "Fresco" as const,
|
|
123
131
|
installationId: "test-123",
|
|
124
132
|
apiKey: "phc_test",
|
|
125
133
|
};
|
|
@@ -134,6 +142,7 @@ describe("Configuration", () => {
|
|
|
134
142
|
|
|
135
143
|
it("should allow overriding default values", () => {
|
|
136
144
|
const userConfig = {
|
|
145
|
+
app: "Fresco" as const,
|
|
137
146
|
installationId: "test-123",
|
|
138
147
|
apiKey: "phc_test",
|
|
139
148
|
apiHost: "https://custom.posthog.com",
|
|
@@ -150,6 +159,7 @@ describe("Configuration", () => {
|
|
|
150
159
|
|
|
151
160
|
it("should use placeholder API key when none provided (proxy mode)", () => {
|
|
152
161
|
const userConfig = {
|
|
162
|
+
app: "Fresco" as const,
|
|
153
163
|
installationId: "test-123",
|
|
154
164
|
};
|
|
155
165
|
|
|
@@ -162,6 +172,7 @@ describe("Configuration", () => {
|
|
|
162
172
|
|
|
163
173
|
it("should merge PostHog options", () => {
|
|
164
174
|
const userConfig = {
|
|
175
|
+
app: "Fresco" as const,
|
|
165
176
|
installationId: "test-123",
|
|
166
177
|
apiKey: "phc_test",
|
|
167
178
|
posthogOptions: {
|
|
@@ -177,6 +188,16 @@ describe("Configuration", () => {
|
|
|
177
188
|
// Should still have default values for other options
|
|
178
189
|
expect(merged.posthogOptions.disable_session_recording).toBe(true);
|
|
179
190
|
});
|
|
191
|
+
|
|
192
|
+
it("should pass through app field", () => {
|
|
193
|
+
const userConfig = {
|
|
194
|
+
app: "Studio" as const,
|
|
195
|
+
installationId: "test-123",
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const merged = mergeConfig(userConfig);
|
|
199
|
+
expect(merged.app).toBe("Studio");
|
|
200
|
+
});
|
|
180
201
|
});
|
|
181
202
|
});
|
|
182
203
|
|
|
@@ -191,6 +212,21 @@ describe("Environment Variables", () => {
|
|
|
191
212
|
});
|
|
192
213
|
});
|
|
193
214
|
|
|
215
|
+
describe("App Names", () => {
|
|
216
|
+
it("should export all app names", () => {
|
|
217
|
+
expect(appNames).toEqual([
|
|
218
|
+
"Fresco",
|
|
219
|
+
"Studio",
|
|
220
|
+
"Architect",
|
|
221
|
+
"Interviewer",
|
|
222
|
+
"ArchitectWeb",
|
|
223
|
+
"CommunityForum",
|
|
224
|
+
"ProjectWebsite",
|
|
225
|
+
"Documentation",
|
|
226
|
+
]);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
194
230
|
describe("Type Exports", () => {
|
|
195
231
|
it("should export EventType correctly", () => {
|
|
196
232
|
const validTypes: EventType[] = [
|
package/src/client.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { ensureError } from "./utils";
|
|
|
7
7
|
* This wraps PostHog with Network Canvas-specific functionality
|
|
8
8
|
*/
|
|
9
9
|
export function createAnalytics(config: Required<AnalyticsConfig>): Analytics {
|
|
10
|
-
const { apiHost, apiKey, installationId, disabled, debug, posthogOptions } = config;
|
|
10
|
+
const { app, apiHost, apiKey, installationId, disabled, debug, posthogOptions } = config;
|
|
11
11
|
|
|
12
12
|
// If analytics is disabled, return a no-op implementation
|
|
13
13
|
if (disabled) {
|
|
@@ -20,6 +20,7 @@ export function createAnalytics(config: Required<AnalyticsConfig>): Analytics {
|
|
|
20
20
|
loaded: (posthogInstance) => {
|
|
21
21
|
// Set installation ID as a super property (included with every event)
|
|
22
22
|
posthogInstance.register({
|
|
23
|
+
app,
|
|
23
24
|
installation_id: installationId,
|
|
24
25
|
});
|
|
25
26
|
|
package/src/config.ts
CHANGED
|
@@ -79,6 +79,7 @@ export const defaultConfig: Partial<AnalyticsConfig> = {
|
|
|
79
79
|
*/
|
|
80
80
|
export function mergeConfig(userConfig: AnalyticsConfig): Required<AnalyticsConfig> {
|
|
81
81
|
return {
|
|
82
|
+
app: userConfig.app,
|
|
82
83
|
apiHost: userConfig.apiHost ?? defaultConfig.apiHost ?? POSTHOG_PROXY_HOST,
|
|
83
84
|
apiKey: userConfig.apiKey ?? PROXY_MODE_DUMMY_KEY,
|
|
84
85
|
installationId: userConfig.installationId,
|
package/src/index.ts
CHANGED
|
@@ -49,21 +49,24 @@
|
|
|
49
49
|
* ```
|
|
50
50
|
*/
|
|
51
51
|
|
|
52
|
+
// Re-export client factory for direct initialization (e.g., instrumentation-client.ts)
|
|
53
|
+
export { createAnalytics } from "./client";
|
|
52
54
|
// Re-export config helpers
|
|
53
55
|
export { defaultConfig, isDisabledByEnv, mergeConfig } from "./config";
|
|
54
56
|
export { useAnalytics, useFeatureFlag, useFeatureFlagValue } from "./hooks";
|
|
55
|
-
|
|
56
57
|
// Re-export provider and hooks for client-side usage
|
|
57
58
|
export { AnalyticsProvider, type AnalyticsProviderProps } from "./provider";
|
|
59
|
+
|
|
58
60
|
// Re-export types
|
|
59
61
|
export type {
|
|
60
62
|
Analytics,
|
|
61
63
|
AnalyticsConfig,
|
|
64
|
+
AppName,
|
|
62
65
|
ErrorProperties,
|
|
63
66
|
EventProperties,
|
|
64
67
|
EventType,
|
|
65
68
|
} from "./types";
|
|
66
|
-
export { eventTypes, legacyEventTypeMap } from "./types";
|
|
69
|
+
export { appNames, eventTypes, legacyEventTypeMap } from "./types";
|
|
67
70
|
|
|
68
71
|
// Re-export utilities
|
|
69
72
|
export { ensureError } from "./utils";
|
package/src/provider.tsx
CHANGED
|
@@ -13,10 +13,10 @@ export const AnalyticsContext = createContext<Analytics | null>(null);
|
|
|
13
13
|
/**
|
|
14
14
|
* Props for the AnalyticsProvider
|
|
15
15
|
*/
|
|
16
|
-
export
|
|
16
|
+
export type AnalyticsProviderProps = {
|
|
17
17
|
children: ReactNode;
|
|
18
18
|
config: AnalyticsConfig;
|
|
19
|
-
}
|
|
19
|
+
};
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Provider component that initializes PostHog and provides analytics context
|
package/src/server.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import z from "zod";
|
|
1
|
+
import { z } from "zod";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Common event types supported by the analytics system.
|
|
5
|
+
* This list is non-exhaustive — trackEvent accepts any string.
|
|
6
|
+
* These are provided for discoverability and autocomplete.
|
|
6
7
|
*/
|
|
7
8
|
export const eventTypes = [
|
|
8
9
|
"app_setup",
|
|
@@ -27,6 +28,23 @@ export const legacyEventTypeMap: Record<string, EventType> = {
|
|
|
27
28
|
Error: "error",
|
|
28
29
|
};
|
|
29
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Supported application identifiers.
|
|
33
|
+
* Used to segment events within a single PostHog project.
|
|
34
|
+
*/
|
|
35
|
+
export const appNames = [
|
|
36
|
+
"Fresco",
|
|
37
|
+
"Studio",
|
|
38
|
+
"Architect",
|
|
39
|
+
"Interviewer",
|
|
40
|
+
"ArchitectWeb",
|
|
41
|
+
"CommunityForum",
|
|
42
|
+
"ProjectWebsite",
|
|
43
|
+
"Documentation",
|
|
44
|
+
] as const;
|
|
45
|
+
|
|
46
|
+
export type AppName = (typeof appNames)[number];
|
|
47
|
+
|
|
30
48
|
/**
|
|
31
49
|
* Standard event properties that can be sent with any event
|
|
32
50
|
*/
|
|
@@ -55,7 +73,13 @@ export type ErrorProperties = z.infer<typeof ErrorPropertiesSchema>;
|
|
|
55
73
|
* reverse proxy at ph-relay.networkcanvas.com. All authentication is handled
|
|
56
74
|
* by the worker, so the API key is optional.
|
|
57
75
|
*/
|
|
58
|
-
export
|
|
76
|
+
export type AnalyticsConfig = {
|
|
77
|
+
/**
|
|
78
|
+
* Application identifier. Included with every event as a super property
|
|
79
|
+
* to segment analytics within a single PostHog project.
|
|
80
|
+
*/
|
|
81
|
+
app: AppName;
|
|
82
|
+
|
|
59
83
|
/**
|
|
60
84
|
* PostHog API host - should point to the Cloudflare Worker reverse proxy
|
|
61
85
|
* Defaults to "https://ph-relay.networkcanvas.com"
|
|
@@ -129,12 +153,12 @@ export interface AnalyticsConfig {
|
|
|
129
153
|
*/
|
|
130
154
|
[key: string]: unknown;
|
|
131
155
|
};
|
|
132
|
-
}
|
|
156
|
+
};
|
|
133
157
|
|
|
134
158
|
/**
|
|
135
159
|
* Analytics instance interface
|
|
136
160
|
*/
|
|
137
|
-
export
|
|
161
|
+
export type Analytics = {
|
|
138
162
|
/**
|
|
139
163
|
* Track a custom event
|
|
140
164
|
*/
|
|
@@ -180,4 +204,4 @@ export interface Analytics {
|
|
|
180
204
|
* Get the installation ID
|
|
181
205
|
*/
|
|
182
206
|
getInstallationId: () => string;
|
|
183
|
-
}
|
|
207
|
+
};
|