@od-oneapp/analytics 2026.1.1301
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/README.md +509 -0
- package/dist/ai-YMnynb-t.mjs +3347 -0
- package/dist/ai-YMnynb-t.mjs.map +1 -0
- package/dist/chunk-DQk6qfdC.mjs +18 -0
- package/dist/client-CTzJVFU5.mjs +9 -0
- package/dist/client-CTzJVFU5.mjs.map +1 -0
- package/dist/client-CcFTauAh.mjs +54 -0
- package/dist/client-CcFTauAh.mjs.map +1 -0
- package/dist/client-CeOLjbac.mjs +281 -0
- package/dist/client-CeOLjbac.mjs.map +1 -0
- package/dist/client-D339NFJS.mjs +267 -0
- package/dist/client-D339NFJS.mjs.map +1 -0
- package/dist/client-next.d.mts +62 -0
- package/dist/client-next.d.mts.map +1 -0
- package/dist/client-next.mjs +525 -0
- package/dist/client-next.mjs.map +1 -0
- package/dist/client.d.mts +30 -0
- package/dist/client.d.mts.map +1 -0
- package/dist/client.mjs +186 -0
- package/dist/client.mjs.map +1 -0
- package/dist/config-DPS6bSYo.d.mts +34 -0
- package/dist/config-DPS6bSYo.d.mts.map +1 -0
- package/dist/config-P6P5adJg.mjs +287 -0
- package/dist/config-P6P5adJg.mjs.map +1 -0
- package/dist/console-8bND3mMU.mjs +128 -0
- package/dist/console-8bND3mMU.mjs.map +1 -0
- package/dist/ecommerce-Cgu4wlux.mjs +993 -0
- package/dist/ecommerce-Cgu4wlux.mjs.map +1 -0
- package/dist/emitters-6-nKo8i-.mjs +208 -0
- package/dist/emitters-6-nKo8i-.mjs.map +1 -0
- package/dist/emitters-DldkVSPp.d.mts +12 -0
- package/dist/emitters-DldkVSPp.d.mts.map +1 -0
- package/dist/index-BfNWgfa5.d.mts +1494 -0
- package/dist/index-BfNWgfa5.d.mts.map +1 -0
- package/dist/index-BkIWe--N.d.mts +953 -0
- package/dist/index-BkIWe--N.d.mts.map +1 -0
- package/dist/index-jPzXRn52.d.mts +184 -0
- package/dist/index-jPzXRn52.d.mts.map +1 -0
- package/dist/manager-DvRRjza6.d.mts +76 -0
- package/dist/manager-DvRRjza6.d.mts.map +1 -0
- package/dist/posthog-bootstrap-CYfIy_WS.mjs +1769 -0
- package/dist/posthog-bootstrap-CYfIy_WS.mjs.map +1 -0
- package/dist/posthog-bootstrap-DWxFrxlt.d.mts +81 -0
- package/dist/posthog-bootstrap-DWxFrxlt.d.mts.map +1 -0
- package/dist/providers-http-client.d.mts +37 -0
- package/dist/providers-http-client.d.mts.map +1 -0
- package/dist/providers-http-client.mjs +320 -0
- package/dist/providers-http-client.mjs.map +1 -0
- package/dist/providers-http-server.d.mts +31 -0
- package/dist/providers-http-server.d.mts.map +1 -0
- package/dist/providers-http-server.mjs +297 -0
- package/dist/providers-http-server.mjs.map +1 -0
- package/dist/providers-http.d.mts +46 -0
- package/dist/providers-http.d.mts.map +1 -0
- package/dist/providers-http.mjs +4 -0
- package/dist/server-edge.d.mts +9 -0
- package/dist/server-edge.d.mts.map +1 -0
- package/dist/server-edge.mjs +373 -0
- package/dist/server-edge.mjs.map +1 -0
- package/dist/server-next.d.mts +67 -0
- package/dist/server-next.d.mts.map +1 -0
- package/dist/server-next.mjs +193 -0
- package/dist/server-next.mjs.map +1 -0
- package/dist/server.d.mts +10 -0
- package/dist/server.mjs +7 -0
- package/dist/service-cYtBBL8x.mjs +945 -0
- package/dist/service-cYtBBL8x.mjs.map +1 -0
- package/dist/shared.d.mts +16 -0
- package/dist/shared.d.mts.map +1 -0
- package/dist/shared.mjs +93 -0
- package/dist/shared.mjs.map +1 -0
- package/dist/types-BxBnNQ0V.d.mts +354 -0
- package/dist/types-BxBnNQ0V.d.mts.map +1 -0
- package/dist/types-CBvxUEaF.d.mts +216 -0
- package/dist/types-CBvxUEaF.d.mts.map +1 -0
- package/dist/types.d.mts +4 -0
- package/dist/types.mjs +0 -0
- package/dist/vercel-types-lwakUfoI.d.mts +102 -0
- package/dist/vercel-types-lwakUfoI.d.mts.map +1 -0
- package/package.json +129 -0
- package/src/client/index.ts +164 -0
- package/src/client/manager.ts +71 -0
- package/src/client/next/components.tsx +270 -0
- package/src/client/next/hooks.ts +217 -0
- package/src/client/next/manager.ts +141 -0
- package/src/client/next.ts +144 -0
- package/src/client-next.ts +101 -0
- package/src/client.ts +89 -0
- package/src/examples/ai-sdk-patterns.ts +583 -0
- package/src/examples/emitter-patterns.ts +476 -0
- package/src/examples/nextjs-emitter-patterns.tsx +403 -0
- package/src/next/app-router.tsx +564 -0
- package/src/next/client.ts +419 -0
- package/src/next/index.ts +84 -0
- package/src/next/middleware.ts +429 -0
- package/src/next/rsc.tsx +300 -0
- package/src/next/server.ts +253 -0
- package/src/next/types.d.ts +220 -0
- package/src/providers/base-provider.ts +419 -0
- package/src/providers/console/client.ts +10 -0
- package/src/providers/console/index.ts +152 -0
- package/src/providers/console/server.ts +6 -0
- package/src/providers/console/types.ts +15 -0
- package/src/providers/http/client.ts +464 -0
- package/src/providers/http/index.ts +30 -0
- package/src/providers/http/server.ts +396 -0
- package/src/providers/http/types.ts +135 -0
- package/src/providers/posthog/client.ts +518 -0
- package/src/providers/posthog/index.ts +11 -0
- package/src/providers/posthog/server.ts +329 -0
- package/src/providers/posthog/types.ts +104 -0
- package/src/providers/segment/client.ts +113 -0
- package/src/providers/segment/index.ts +11 -0
- package/src/providers/segment/server.ts +115 -0
- package/src/providers/segment/types.ts +51 -0
- package/src/providers/vercel/client.ts +102 -0
- package/src/providers/vercel/index.ts +11 -0
- package/src/providers/vercel/server.ts +89 -0
- package/src/providers/vercel/types.ts +27 -0
- package/src/server/index.ts +103 -0
- package/src/server/manager.ts +62 -0
- package/src/server/next.ts +210 -0
- package/src/server-edge.ts +442 -0
- package/src/server-next.ts +39 -0
- package/src/server.ts +106 -0
- package/src/shared/emitters/ai/README.md +981 -0
- package/src/shared/emitters/ai/events/agent.ts +130 -0
- package/src/shared/emitters/ai/events/artifacts.ts +167 -0
- package/src/shared/emitters/ai/events/chat.ts +126 -0
- package/src/shared/emitters/ai/events/chatbot-ecommerce.ts +133 -0
- package/src/shared/emitters/ai/events/completion.ts +103 -0
- package/src/shared/emitters/ai/events/content-generation.ts +347 -0
- package/src/shared/emitters/ai/events/conversation.ts +332 -0
- package/src/shared/emitters/ai/events/product-features.ts +1402 -0
- package/src/shared/emitters/ai/events/streaming.ts +114 -0
- package/src/shared/emitters/ai/events/tool.ts +93 -0
- package/src/shared/emitters/ai/index.ts +69 -0
- package/src/shared/emitters/ai/track-ai-sdk.ts +74 -0
- package/src/shared/emitters/ai/track-ai.ts +50 -0
- package/src/shared/emitters/ai/types.ts +1041 -0
- package/src/shared/emitters/ai/utils.ts +468 -0
- package/src/shared/emitters/ecommerce/events/cart-checkout.ts +106 -0
- package/src/shared/emitters/ecommerce/events/coupon.ts +49 -0
- package/src/shared/emitters/ecommerce/events/engagement.ts +61 -0
- package/src/shared/emitters/ecommerce/events/marketplace.ts +119 -0
- package/src/shared/emitters/ecommerce/events/order.ts +199 -0
- package/src/shared/emitters/ecommerce/events/product.ts +205 -0
- package/src/shared/emitters/ecommerce/events/registry.ts +123 -0
- package/src/shared/emitters/ecommerce/events/wishlist-sharing.ts +140 -0
- package/src/shared/emitters/ecommerce/index.ts +46 -0
- package/src/shared/emitters/ecommerce/track-ecommerce.ts +53 -0
- package/src/shared/emitters/ecommerce/types.ts +314 -0
- package/src/shared/emitters/ecommerce/utils.ts +216 -0
- package/src/shared/emitters/emitter-types.ts +974 -0
- package/src/shared/emitters/emitters.ts +292 -0
- package/src/shared/emitters/helpers.ts +419 -0
- package/src/shared/emitters/index.ts +66 -0
- package/src/shared/index.ts +142 -0
- package/src/shared/ingestion/index.ts +66 -0
- package/src/shared/ingestion/schemas.ts +386 -0
- package/src/shared/ingestion/service.ts +628 -0
- package/src/shared/node22-features.ts +848 -0
- package/src/shared/providers/console-provider.ts +160 -0
- package/src/shared/types/base-types.ts +54 -0
- package/src/shared/types/console-types.ts +19 -0
- package/src/shared/types/posthog-types.ts +131 -0
- package/src/shared/types/segment-types.ts +15 -0
- package/src/shared/types/types.ts +397 -0
- package/src/shared/types/vercel-types.ts +19 -0
- package/src/shared/utils/config-client.ts +19 -0
- package/src/shared/utils/config.ts +250 -0
- package/src/shared/utils/emitter-adapter.ts +212 -0
- package/src/shared/utils/manager.test.ts +36 -0
- package/src/shared/utils/manager.ts +1322 -0
- package/src/shared/utils/posthog-bootstrap.ts +136 -0
- package/src/shared/utils/posthog-client-utils.ts +48 -0
- package/src/shared/utils/posthog-next-utils.ts +282 -0
- package/src/shared/utils/posthog-server-utils.ts +210 -0
- package/src/shared/utils/rate-limit.ts +289 -0
- package/src/shared/utils/security.ts +545 -0
- package/src/shared/utils/validation-client.ts +161 -0
- package/src/shared/utils/validation.ts +399 -0
- package/src/shared.ts +155 -0
- package/src/types/index.ts +62 -0
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Validation utilities for analytics configuration
|
|
3
|
+
*
|
|
4
|
+
* This module provides comprehensive validation for analytics configurations,
|
|
5
|
+
* including provider validation, environment-specific checks, and helpful
|
|
6
|
+
* warnings for common misconfigurations.
|
|
7
|
+
*
|
|
8
|
+
* **Features**:
|
|
9
|
+
* - Configuration structure validation
|
|
10
|
+
* - Provider-specific field validation
|
|
11
|
+
* - Environment variable validation
|
|
12
|
+
* - Environment-specific warnings
|
|
13
|
+
* - Detailed error reporting
|
|
14
|
+
*
|
|
15
|
+
* @module @od-oneapp/analytics/shared/utils/validation
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { logError, logInfo, logWarn } from '@repo/shared/logger';
|
|
19
|
+
|
|
20
|
+
import { PROVIDER_REQUIREMENTS } from './config';
|
|
21
|
+
|
|
22
|
+
import type { AnalyticsConfig, ProviderConfig } from '../types/types';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Validation error structure.
|
|
26
|
+
*
|
|
27
|
+
* Represents a single validation error with field, message, and provider context.
|
|
28
|
+
*/
|
|
29
|
+
export interface ValidationError {
|
|
30
|
+
/** Field name that failed validation */
|
|
31
|
+
field: string;
|
|
32
|
+
/** Human-readable error message */
|
|
33
|
+
message: string;
|
|
34
|
+
/** Provider name (or 'global' for config-level errors) */
|
|
35
|
+
provider: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Validation result structure.
|
|
40
|
+
*
|
|
41
|
+
* Contains validation errors, warnings, and overall validity status.
|
|
42
|
+
*/
|
|
43
|
+
export interface ValidationResult {
|
|
44
|
+
/** Array of validation errors */
|
|
45
|
+
errors: ValidationError[];
|
|
46
|
+
/** Whether the configuration is valid (no errors) */
|
|
47
|
+
isValid: boolean;
|
|
48
|
+
/** Array of warning messages */
|
|
49
|
+
warnings: string[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Comprehensive configuration validation.
|
|
54
|
+
*
|
|
55
|
+
* Validates the entire analytics configuration structure, including:
|
|
56
|
+
* - Configuration object structure
|
|
57
|
+
* - Providers object existence
|
|
58
|
+
* - Individual provider configurations
|
|
59
|
+
* - Environment-specific warnings
|
|
60
|
+
*
|
|
61
|
+
* Accepts `unknown` input for defensive validation of potentially malformed configs.
|
|
62
|
+
*
|
|
63
|
+
* @param {unknown} config - Analytics configuration to validate
|
|
64
|
+
* @returns {ValidationResult} Validation result with errors, warnings, and validity status
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```typescript
|
|
68
|
+
* const result = validateAnalyticsConfig(config);
|
|
69
|
+
* if (!result.isValid) {
|
|
70
|
+
* console.error('Validation errors:', result.errors);
|
|
71
|
+
* }
|
|
72
|
+
* if (result.warnings.length > 0) {
|
|
73
|
+
* console.warn('Warnings:', result.warnings);
|
|
74
|
+
* }
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export function validateAnalyticsConfig(config: unknown): ValidationResult {
|
|
78
|
+
const errors: ValidationError[] = [];
|
|
79
|
+
const warnings: string[] = [];
|
|
80
|
+
|
|
81
|
+
// Check if config exists and is an object
|
|
82
|
+
if (!config || typeof config !== 'object') {
|
|
83
|
+
errors.push({
|
|
84
|
+
provider: 'global',
|
|
85
|
+
field: 'config',
|
|
86
|
+
message: 'Analytics configuration is required and must be an object',
|
|
87
|
+
});
|
|
88
|
+
return { isValid: false, errors, warnings };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const typedConfig = config as AnalyticsConfig;
|
|
92
|
+
|
|
93
|
+
// Check if providers object exists
|
|
94
|
+
if (!typedConfig.providers || typeof typedConfig.providers !== 'object') {
|
|
95
|
+
errors.push({
|
|
96
|
+
provider: 'global',
|
|
97
|
+
field: 'providers',
|
|
98
|
+
message: 'Providers configuration is required and must be an object',
|
|
99
|
+
});
|
|
100
|
+
return { isValid: false, errors, warnings };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Check if at least one provider is configured
|
|
104
|
+
const providerCount = Object.keys(typedConfig.providers).length;
|
|
105
|
+
if (providerCount === 0) {
|
|
106
|
+
warnings.push('No providers configured. Analytics will not track any events.');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Validate each provider
|
|
110
|
+
for (const [providerName, providerConfig] of Object.entries(typedConfig.providers)) {
|
|
111
|
+
const providerErrors = validateProvider(providerName, providerConfig);
|
|
112
|
+
errors.push(...providerErrors);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Environment-specific warnings
|
|
116
|
+
const isBrowser = typeof window !== 'undefined';
|
|
117
|
+
|
|
118
|
+
// Mixpanel warning for client-side usage
|
|
119
|
+
if (isBrowser && typedConfig.providers.mixpanel) {
|
|
120
|
+
warnings.push(
|
|
121
|
+
'Mixpanel provider configured on client-side. Consider using server-side for better performance.',
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!isBrowser && typedConfig.providers.vercel) {
|
|
126
|
+
warnings.push(
|
|
127
|
+
'Vercel Analytics has limited server-side support. Consider using client-side for better features.',
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
isValid: errors.length === 0,
|
|
133
|
+
errors,
|
|
134
|
+
warnings,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Validate a single provider configuration.
|
|
140
|
+
*
|
|
141
|
+
* Checks that the provider is known and that all required fields are present.
|
|
142
|
+
*
|
|
143
|
+
* @param {string} providerName - Name of the provider to validate
|
|
144
|
+
* @param {ProviderConfig} config - Provider configuration to validate
|
|
145
|
+
* @returns {ValidationError[]} Array of validation errors (empty if valid)
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* const errors = validateProvider('posthog', { apiKey: 'phc_xxx' });
|
|
150
|
+
* if (errors.length > 0) {
|
|
151
|
+
* console.error('Provider errors:', errors);
|
|
152
|
+
* }
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
export function validateProvider(providerName: string, config: ProviderConfig): ValidationError[] {
|
|
156
|
+
const errors: ValidationError[] = [];
|
|
157
|
+
|
|
158
|
+
// Check if provider is known
|
|
159
|
+
const knownProviders = ['segment', 'posthog', 'vercel', 'console', 'mixpanel'];
|
|
160
|
+
if (!knownProviders.includes(providerName)) {
|
|
161
|
+
errors.push({
|
|
162
|
+
provider: providerName,
|
|
163
|
+
field: 'name',
|
|
164
|
+
message: `Unknown provider '${providerName}'. Known providers: ${knownProviders.join(', ')}`,
|
|
165
|
+
});
|
|
166
|
+
return errors;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Check required fields
|
|
170
|
+
const requiredFields = PROVIDER_REQUIREMENTS[providerName] ?? [];
|
|
171
|
+
for (const field of requiredFields) {
|
|
172
|
+
const value = config[field as keyof ProviderConfig];
|
|
173
|
+
|
|
174
|
+
if (!value) {
|
|
175
|
+
errors.push({
|
|
176
|
+
provider: providerName,
|
|
177
|
+
field,
|
|
178
|
+
message: `Required field '${field}' is missing for provider '${providerName}'`,
|
|
179
|
+
});
|
|
180
|
+
} else if (typeof value === 'string' && value.trim() === '') {
|
|
181
|
+
errors.push({
|
|
182
|
+
provider: providerName,
|
|
183
|
+
field,
|
|
184
|
+
message: `Required field '${field}' cannot be empty for provider '${providerName}'`,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Provider-specific validation
|
|
190
|
+
switch (providerName) {
|
|
191
|
+
case 'segment':
|
|
192
|
+
if (config.writeKey && !isValidSegmentWriteKey(config.writeKey)) {
|
|
193
|
+
errors.push({
|
|
194
|
+
provider: providerName,
|
|
195
|
+
field: 'writeKey',
|
|
196
|
+
message: 'Segment writeKey appears to be invalid format',
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
break;
|
|
200
|
+
|
|
201
|
+
case 'posthog':
|
|
202
|
+
if (config.apiKey && !isValidPostHogApiKey(config.apiKey)) {
|
|
203
|
+
errors.push({
|
|
204
|
+
provider: providerName,
|
|
205
|
+
field: 'apiKey',
|
|
206
|
+
message: 'PostHog apiKey appears to be invalid format',
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return errors;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Validate environment variables for analytics.
|
|
217
|
+
*
|
|
218
|
+
* Checks common analytics environment variables for:
|
|
219
|
+
* - Empty values
|
|
220
|
+
* - Placeholder text
|
|
221
|
+
* - Development environment warnings
|
|
222
|
+
*
|
|
223
|
+
* @returns {ValidationResult} Validation result with warnings about environment variables
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* const result = validateEnvironmentVariables();
|
|
228
|
+
* if (result.warnings.length > 0) {
|
|
229
|
+
* console.warn('Environment warnings:', result.warnings);
|
|
230
|
+
* }
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
export function validateEnvironmentVariables(): ValidationResult {
|
|
234
|
+
const errors: ValidationError[] = [];
|
|
235
|
+
const warnings: string[] = [];
|
|
236
|
+
|
|
237
|
+
// Check for common environment variables
|
|
238
|
+
const envVars = {
|
|
239
|
+
POSTHOG_API_KEY: process.env.POSTHOG_API_KEY,
|
|
240
|
+
SEGMENT_WRITE_KEY: process.env.SEGMENT_WRITE_KEY,
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
for (const [varName, value] of Object.entries(envVars)) {
|
|
244
|
+
if (value && typeof value === 'string') {
|
|
245
|
+
if (value.trim() === '') {
|
|
246
|
+
warnings.push(`Environment variable ${varName} is set but empty`);
|
|
247
|
+
} else if (value.includes('your-') || value.includes('paste-')) {
|
|
248
|
+
warnings.push(`Environment variable ${varName} appears to contain placeholder text`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Warn about development environment
|
|
254
|
+
if (
|
|
255
|
+
process.env.NODE_ENV === 'development' &&
|
|
256
|
+
!envVars.SEGMENT_WRITE_KEY &&
|
|
257
|
+
!envVars.POSTHOG_API_KEY
|
|
258
|
+
) {
|
|
259
|
+
warnings.push(
|
|
260
|
+
'No analytics environment variables detected in development. Consider using console provider for debugging.',
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
isValid: errors.length === 0,
|
|
266
|
+
errors,
|
|
267
|
+
warnings,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Validates Segment write key format.
|
|
273
|
+
*
|
|
274
|
+
* Checks that the write key matches Segment's expected format:
|
|
275
|
+
* - Exactly 32 alphanumeric characters
|
|
276
|
+
* - Not a placeholder value
|
|
277
|
+
*
|
|
278
|
+
* @param {string} writeKey - Write key to validate
|
|
279
|
+
* @returns {boolean} `true` if valid, `false` otherwise
|
|
280
|
+
*
|
|
281
|
+
* @internal
|
|
282
|
+
*/
|
|
283
|
+
function isValidSegmentWriteKey(writeKey: string): boolean {
|
|
284
|
+
// Segment write keys are exactly 32 characters, alphanumeric
|
|
285
|
+
if (!/^[\dA-Za-z]{32}$/.test(writeKey)) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Check for common placeholder patterns
|
|
290
|
+
const placeholders = ['your-write-key', 'paste-key-here', 'xxxxxxxx', 'example'];
|
|
291
|
+
if (placeholders.some(p => writeKey.toLowerCase().includes(p.toLowerCase()))) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Validates PostHog API key format.
|
|
300
|
+
*
|
|
301
|
+
* Checks that the API key matches PostHog's expected format:
|
|
302
|
+
* - Starts with `phc_` prefix
|
|
303
|
+
* - Followed by 43 alphanumeric/dash/underscore characters
|
|
304
|
+
* - Not a placeholder value
|
|
305
|
+
*
|
|
306
|
+
* @param {string} apiKey - API key to validate
|
|
307
|
+
* @returns {boolean} `true` if valid, `false` otherwise
|
|
308
|
+
*
|
|
309
|
+
* @internal
|
|
310
|
+
*/
|
|
311
|
+
function isValidPostHogApiKey(apiKey: string): boolean {
|
|
312
|
+
// PostHog keys: phc_ prefix + 43 alphanumeric/dash/underscore characters
|
|
313
|
+
if (!/^phc_[\w-]{43}$/.test(apiKey)) {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Check for placeholder patterns
|
|
318
|
+
const placeholders = ['your-api-key', 'paste-key-here', 'xxxxxxxx', 'example'];
|
|
319
|
+
if (placeholders.some(p => apiKey.toLowerCase().includes(p.toLowerCase()))) {
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Utility to throw validation errors (for strict validation).
|
|
328
|
+
*
|
|
329
|
+
* Validates the configuration and throws an error if validation fails.
|
|
330
|
+
* Useful for ensuring configuration is valid before using analytics.
|
|
331
|
+
*
|
|
332
|
+
* @param {AnalyticsConfig} config - Analytics configuration to validate
|
|
333
|
+
* @throws {Error} If configuration validation fails
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```typescript
|
|
337
|
+
* try {
|
|
338
|
+
* validateConfigOrThrow(config);
|
|
339
|
+
* // Configuration is valid, proceed
|
|
340
|
+
* } catch (error) {
|
|
341
|
+
* console.error('Invalid config:', error.message);
|
|
342
|
+
* }
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
345
|
+
export function validateConfigOrThrow(config: AnalyticsConfig): void {
|
|
346
|
+
const result = validateAnalyticsConfig(config);
|
|
347
|
+
|
|
348
|
+
if (!result.isValid) {
|
|
349
|
+
const errorMessages = result.errors
|
|
350
|
+
.map(error => `${error.provider}.${error.field}: ${error.message}`)
|
|
351
|
+
.join('\n');
|
|
352
|
+
|
|
353
|
+
throw new Error(`Analytics configuration validation failed:\n${errorMessages}`);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Log warnings but don't throw
|
|
357
|
+
if (result.warnings.length > 0 && config.onError) {
|
|
358
|
+
config.onError(new Error('Analytics configuration warnings'), {
|
|
359
|
+
provider: 'analytics',
|
|
360
|
+
method: 'validateConfig',
|
|
361
|
+
warnings: result.warnings,
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Development helper to check configuration.
|
|
368
|
+
*
|
|
369
|
+
* Logs configuration details and validation results for debugging.
|
|
370
|
+
* Only useful in development environments.
|
|
371
|
+
*
|
|
372
|
+
* @param {AnalyticsConfig} config - Analytics configuration to debug
|
|
373
|
+
* @returns {Promise<void>} Promise that resolves when debugging is complete
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```typescript
|
|
377
|
+
* if (process.env.NODE_ENV === 'development') {
|
|
378
|
+
* await debugConfig(config);
|
|
379
|
+
* }
|
|
380
|
+
* ```
|
|
381
|
+
*/
|
|
382
|
+
export async function debugConfig(config: AnalyticsConfig): Promise<void> {
|
|
383
|
+
const result = validateAnalyticsConfig(config);
|
|
384
|
+
|
|
385
|
+
logInfo('Analytics Configuration Debug', {
|
|
386
|
+
config,
|
|
387
|
+
validationResult: result,
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
if (result.errors.length > 0) {
|
|
391
|
+
logError('Analytics configuration errors: Validation failed', {
|
|
392
|
+
errors: result.errors,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (result.warnings.length > 0) {
|
|
397
|
+
logWarn('Analytics configuration warnings', { warnings: result.warnings });
|
|
398
|
+
}
|
|
399
|
+
}
|
package/src/shared.ts
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared Environment Analytics Exports
|
|
3
|
+
*
|
|
4
|
+
* This module provides environment-agnostic analytics functionality for packages
|
|
5
|
+
* that can run in both client and server environments. It automatically detects
|
|
6
|
+
* the runtime environment and uses the appropriate analytics implementation.
|
|
7
|
+
*
|
|
8
|
+
* **Environment Detection**:
|
|
9
|
+
* - Detects server vs client environments
|
|
10
|
+
* - Detects Next.js vs plain Node.js/React environments
|
|
11
|
+
* - Automatically selects the correct analytics manager
|
|
12
|
+
*
|
|
13
|
+
* **Key Features**:
|
|
14
|
+
* - Single API that works in all environments
|
|
15
|
+
* - Automatic environment detection
|
|
16
|
+
* - Type-safe event tracking
|
|
17
|
+
* - Ecommerce and AI event emitters
|
|
18
|
+
*
|
|
19
|
+
* **Usage**: Use this module when you need analytics in code that runs in both
|
|
20
|
+
* client and server contexts (e.g., shared utilities, isomorphic code).
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* // In environment-agnostic code
|
|
25
|
+
* import { createAnalytics, track } from '@od-oneapp/analytics/shared';
|
|
26
|
+
*
|
|
27
|
+
* // Works in both client and server environments
|
|
28
|
+
* const analytics = await createAnalytics(config);
|
|
29
|
+
* await analytics.emit(track('Event Name', { property: 'value' }));
|
|
30
|
+
* ```
|
|
31
|
+
*
|
|
32
|
+
* @module @od-oneapp/analytics/shared
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
// Import types for proper typing
|
|
36
|
+
import type { AnalyticsConfig, AnalyticsManager } from './shared/types/types';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Detects if code is running in a server environment.
|
|
40
|
+
*
|
|
41
|
+
* Checks for absence of `window` object and presence of `global` object.
|
|
42
|
+
*
|
|
43
|
+
* @returns {boolean} `true` if running on server, `false` if running in browser
|
|
44
|
+
*
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
47
|
+
function isServerEnvironment(): boolean {
|
|
48
|
+
return typeof window === 'undefined' && typeof global !== 'undefined';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Detects if code is running in a Next.js environment.
|
|
53
|
+
*
|
|
54
|
+
* Checks for Next.js-specific environment variables and globals.
|
|
55
|
+
*
|
|
56
|
+
* @returns {boolean} `true` if running in Next.js, `false` otherwise
|
|
57
|
+
*
|
|
58
|
+
* @internal
|
|
59
|
+
*/
|
|
60
|
+
function isNextJSEnvironment(): boolean {
|
|
61
|
+
// Check for Next.js-specific environment variables and globals
|
|
62
|
+
return (
|
|
63
|
+
// Next.js runtime environment variable
|
|
64
|
+
process.env.NEXT_RUNTIME !== undefined ||
|
|
65
|
+
// Next.js data global (client-side)
|
|
66
|
+
typeof (globalThis as any).__NEXT_DATA__ !== 'undefined' ||
|
|
67
|
+
// Edge runtime global
|
|
68
|
+
typeof (globalThis as any).EdgeRuntime !== 'undefined' ||
|
|
69
|
+
// Next.js process title
|
|
70
|
+
(typeof process !== 'undefined' && process.title?.includes('next')) ||
|
|
71
|
+
// Next.js in development mode
|
|
72
|
+
(typeof process !== 'undefined' &&
|
|
73
|
+
process.env.NODE_ENV === 'development' &&
|
|
74
|
+
process.env.NEXT_PUBLIC_APP_ENV !== undefined)
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Creates an analytics manager appropriate for the current environment.
|
|
80
|
+
*
|
|
81
|
+
* Automatically detects the runtime environment (server/client, Next.js/plain) and
|
|
82
|
+
* creates the appropriate analytics manager instance. This allows packages to use
|
|
83
|
+
* analytics without knowing which environment they're running in.
|
|
84
|
+
*
|
|
85
|
+
* @param {AnalyticsConfig} config - Analytics configuration with provider settings
|
|
86
|
+
* @returns {Promise<AnalyticsManager>} Promise resolving to analytics manager instance
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* const analytics = await createAnalytics({
|
|
91
|
+
* providers: {
|
|
92
|
+
* posthog: { apiKey: process.env.POSTHOG_API_KEY }
|
|
93
|
+
* }
|
|
94
|
+
* });
|
|
95
|
+
*
|
|
96
|
+
* await analytics.emit(track('User Action', { action: 'click' }));
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
export async function createAnalytics(config: AnalyticsConfig): Promise<AnalyticsManager> {
|
|
100
|
+
const isServer = isServerEnvironment();
|
|
101
|
+
const isNextJS = isNextJSEnvironment();
|
|
102
|
+
|
|
103
|
+
if (isServer) {
|
|
104
|
+
if (isNextJS) {
|
|
105
|
+
const { createServerAnalytics } = await import('./server-next');
|
|
106
|
+
return createServerAnalytics(config);
|
|
107
|
+
} else {
|
|
108
|
+
const { createServerAnalytics } = await import('./server');
|
|
109
|
+
return createServerAnalytics(config);
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
if (isNextJS) {
|
|
113
|
+
const { createNextJSClientAnalytics } = await import('./client-next');
|
|
114
|
+
return createNextJSClientAnalytics(config);
|
|
115
|
+
} else {
|
|
116
|
+
const { createClientAnalytics } = await import('./client');
|
|
117
|
+
return createClientAnalytics(config);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Re-export essential types that shared packages need
|
|
123
|
+
export type {
|
|
124
|
+
AnalyticsConfig,
|
|
125
|
+
AnalyticsContext,
|
|
126
|
+
AnalyticsManager,
|
|
127
|
+
AnalyticsProvider,
|
|
128
|
+
ProviderConfig,
|
|
129
|
+
TrackingOptions,
|
|
130
|
+
} from './shared/types/types';
|
|
131
|
+
|
|
132
|
+
// Re-export emitters for environment-agnostic usage
|
|
133
|
+
export { aiSdk as ai, alias, ecommerce, group, identify, page, track } from './shared/emitters';
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Environment information for debugging and introspection.
|
|
137
|
+
*
|
|
138
|
+
* Provides runtime information about the current environment, useful for debugging
|
|
139
|
+
* and conditional logic based on environment.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```typescript
|
|
143
|
+
* import { environmentInfo } from '@od-oneapp/analytics/shared';
|
|
144
|
+
*
|
|
145
|
+
* console.log('Running on server:', environmentInfo.isServer);
|
|
146
|
+
* console.log('Next.js environment:', environmentInfo.isNextJS);
|
|
147
|
+
* console.log('Runtime:', environmentInfo.runtime);
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export const environmentInfo = {
|
|
151
|
+
isServer: isServerEnvironment(),
|
|
152
|
+
isNextJS: isNextJSEnvironment(),
|
|
153
|
+
runtime: typeof process !== 'undefined' ? (process.env.NEXT_RUNTIME ?? 'node') : 'browser',
|
|
154
|
+
nodeEnv: typeof process !== 'undefined' ? process.env.NODE_ENV : 'production',
|
|
155
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Analytics types exports
|
|
3
|
+
*
|
|
4
|
+
* This module re-exports all analytics-related types for convenient importing.
|
|
5
|
+
* It includes:
|
|
6
|
+
*
|
|
7
|
+
* - **Core Types**: AnalyticsConfig, AnalyticsManager, AnalyticsProvider, etc.
|
|
8
|
+
* - **Emitter Types**: EmitterPayload, EmitterTrackPayload, EmitterIdentifyPayload, etc.
|
|
9
|
+
* - **Provider Types**: PostHogConfig, SegmentConfig, VercelConfig, ConsoleConfig
|
|
10
|
+
* - **Ecommerce Types**: BaseProductProperties, CartProperties, OrderProperties, etc.
|
|
11
|
+
*
|
|
12
|
+
* **Usage**: Import types from `@od-oneapp/analytics/types` for type annotations.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import type { AnalyticsConfig, AnalyticsManager } from '@od-oneapp/analytics/types';
|
|
17
|
+
*
|
|
18
|
+
* const config: AnalyticsConfig = {
|
|
19
|
+
* providers: { posthog: { apiKey: '...' } }
|
|
20
|
+
* };
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @module @od-oneapp/analytics/types
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
// Core analytics types
|
|
27
|
+
export type {
|
|
28
|
+
AnalyticsConfig,
|
|
29
|
+
AnalyticsContext,
|
|
30
|
+
AnalyticsManager,
|
|
31
|
+
AnalyticsProvider,
|
|
32
|
+
ProviderConfig,
|
|
33
|
+
ProviderRegistry,
|
|
34
|
+
TrackingOptions,
|
|
35
|
+
} from '../shared/types/types';
|
|
36
|
+
|
|
37
|
+
// Emitter types
|
|
38
|
+
export type {
|
|
39
|
+
EmitterAliasPayload,
|
|
40
|
+
EmitterContext,
|
|
41
|
+
EmitterGroupPayload,
|
|
42
|
+
EmitterIdentifyPayload,
|
|
43
|
+
EmitterOptions,
|
|
44
|
+
EmitterPagePayload,
|
|
45
|
+
EmitterPayload,
|
|
46
|
+
EmitterTrackPayload,
|
|
47
|
+
} from '../shared/emitters/emitter-types';
|
|
48
|
+
|
|
49
|
+
// Provider-specific types
|
|
50
|
+
export type { ConsoleConfig, ConsoleOptions } from '../shared/types/console-types';
|
|
51
|
+
export type { BootstrapData, PostHogConfig, PostHogOptions } from '../shared/types/posthog-types';
|
|
52
|
+
export type { SegmentConfig, SegmentOptions } from '../shared/types/segment-types';
|
|
53
|
+
export type { VercelConfig, VercelOptions } from '../shared/types/vercel-types';
|
|
54
|
+
|
|
55
|
+
// Ecommerce types
|
|
56
|
+
export type {
|
|
57
|
+
BaseProductProperties,
|
|
58
|
+
CartProperties,
|
|
59
|
+
EcommerceEventSpec,
|
|
60
|
+
ExtendedProductProperties,
|
|
61
|
+
OrderProperties,
|
|
62
|
+
} from '../shared/emitters/ecommerce/types';
|