@od-oneapp/analytics 2026.1.1301 → 2026.2.1701-canary.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/{dist/client.mjs → client.mjs} +110 -2
  3. package/client.mjs.map +1 -0
  4. package/package.json +102 -92
  5. package/{dist/server-next.mjs → server-next.mjs} +1 -1
  6. package/{dist/server.mjs → server.mjs} +1 -1
  7. package/{dist/service-cYtBBL8x.mjs → service-Duqnlppl.mjs} +107 -3
  8. package/service-Duqnlppl.mjs.map +1 -0
  9. package/dist/client.mjs.map +0 -1
  10. package/dist/service-cYtBBL8x.mjs.map +0 -1
  11. package/src/client/index.ts +0 -164
  12. package/src/client/manager.ts +0 -71
  13. package/src/client/next/components.tsx +0 -270
  14. package/src/client/next/hooks.ts +0 -217
  15. package/src/client/next/manager.ts +0 -141
  16. package/src/client/next.ts +0 -144
  17. package/src/client-next.ts +0 -101
  18. package/src/client.ts +0 -89
  19. package/src/examples/ai-sdk-patterns.ts +0 -583
  20. package/src/examples/emitter-patterns.ts +0 -476
  21. package/src/examples/nextjs-emitter-patterns.tsx +0 -403
  22. package/src/next/app-router.tsx +0 -564
  23. package/src/next/client.ts +0 -419
  24. package/src/next/index.ts +0 -84
  25. package/src/next/middleware.ts +0 -429
  26. package/src/next/rsc.tsx +0 -300
  27. package/src/next/server.ts +0 -253
  28. package/src/next/types.d.ts +0 -220
  29. package/src/providers/base-provider.ts +0 -419
  30. package/src/providers/console/client.ts +0 -10
  31. package/src/providers/console/index.ts +0 -152
  32. package/src/providers/console/server.ts +0 -6
  33. package/src/providers/console/types.ts +0 -15
  34. package/src/providers/http/client.ts +0 -464
  35. package/src/providers/http/index.ts +0 -30
  36. package/src/providers/http/server.ts +0 -396
  37. package/src/providers/http/types.ts +0 -135
  38. package/src/providers/posthog/client.ts +0 -518
  39. package/src/providers/posthog/index.ts +0 -11
  40. package/src/providers/posthog/server.ts +0 -329
  41. package/src/providers/posthog/types.ts +0 -104
  42. package/src/providers/segment/client.ts +0 -113
  43. package/src/providers/segment/index.ts +0 -11
  44. package/src/providers/segment/server.ts +0 -115
  45. package/src/providers/segment/types.ts +0 -51
  46. package/src/providers/vercel/client.ts +0 -102
  47. package/src/providers/vercel/index.ts +0 -11
  48. package/src/providers/vercel/server.ts +0 -89
  49. package/src/providers/vercel/types.ts +0 -27
  50. package/src/server/index.ts +0 -103
  51. package/src/server/manager.ts +0 -62
  52. package/src/server/next.ts +0 -210
  53. package/src/server-edge.ts +0 -442
  54. package/src/server-next.ts +0 -39
  55. package/src/server.ts +0 -106
  56. package/src/shared/emitters/ai/README.md +0 -981
  57. package/src/shared/emitters/ai/events/agent.ts +0 -130
  58. package/src/shared/emitters/ai/events/artifacts.ts +0 -167
  59. package/src/shared/emitters/ai/events/chat.ts +0 -126
  60. package/src/shared/emitters/ai/events/chatbot-ecommerce.ts +0 -133
  61. package/src/shared/emitters/ai/events/completion.ts +0 -103
  62. package/src/shared/emitters/ai/events/content-generation.ts +0 -347
  63. package/src/shared/emitters/ai/events/conversation.ts +0 -332
  64. package/src/shared/emitters/ai/events/product-features.ts +0 -1402
  65. package/src/shared/emitters/ai/events/streaming.ts +0 -114
  66. package/src/shared/emitters/ai/events/tool.ts +0 -93
  67. package/src/shared/emitters/ai/index.ts +0 -69
  68. package/src/shared/emitters/ai/track-ai-sdk.ts +0 -74
  69. package/src/shared/emitters/ai/track-ai.ts +0 -50
  70. package/src/shared/emitters/ai/types.ts +0 -1041
  71. package/src/shared/emitters/ai/utils.ts +0 -468
  72. package/src/shared/emitters/ecommerce/events/cart-checkout.ts +0 -106
  73. package/src/shared/emitters/ecommerce/events/coupon.ts +0 -49
  74. package/src/shared/emitters/ecommerce/events/engagement.ts +0 -61
  75. package/src/shared/emitters/ecommerce/events/marketplace.ts +0 -119
  76. package/src/shared/emitters/ecommerce/events/order.ts +0 -199
  77. package/src/shared/emitters/ecommerce/events/product.ts +0 -205
  78. package/src/shared/emitters/ecommerce/events/registry.ts +0 -123
  79. package/src/shared/emitters/ecommerce/events/wishlist-sharing.ts +0 -140
  80. package/src/shared/emitters/ecommerce/index.ts +0 -46
  81. package/src/shared/emitters/ecommerce/track-ecommerce.ts +0 -53
  82. package/src/shared/emitters/ecommerce/types.ts +0 -314
  83. package/src/shared/emitters/ecommerce/utils.ts +0 -216
  84. package/src/shared/emitters/emitter-types.ts +0 -974
  85. package/src/shared/emitters/emitters.ts +0 -292
  86. package/src/shared/emitters/helpers.ts +0 -419
  87. package/src/shared/emitters/index.ts +0 -66
  88. package/src/shared/index.ts +0 -142
  89. package/src/shared/ingestion/index.ts +0 -66
  90. package/src/shared/ingestion/schemas.ts +0 -386
  91. package/src/shared/ingestion/service.ts +0 -628
  92. package/src/shared/node22-features.ts +0 -848
  93. package/src/shared/providers/console-provider.ts +0 -160
  94. package/src/shared/types/base-types.ts +0 -54
  95. package/src/shared/types/console-types.ts +0 -19
  96. package/src/shared/types/posthog-types.ts +0 -131
  97. package/src/shared/types/segment-types.ts +0 -15
  98. package/src/shared/types/types.ts +0 -397
  99. package/src/shared/types/vercel-types.ts +0 -19
  100. package/src/shared/utils/config-client.ts +0 -19
  101. package/src/shared/utils/config.ts +0 -250
  102. package/src/shared/utils/emitter-adapter.ts +0 -212
  103. package/src/shared/utils/manager.test.ts +0 -36
  104. package/src/shared/utils/manager.ts +0 -1322
  105. package/src/shared/utils/posthog-bootstrap.ts +0 -136
  106. package/src/shared/utils/posthog-client-utils.ts +0 -48
  107. package/src/shared/utils/posthog-next-utils.ts +0 -282
  108. package/src/shared/utils/posthog-server-utils.ts +0 -210
  109. package/src/shared/utils/rate-limit.ts +0 -289
  110. package/src/shared/utils/security.ts +0 -545
  111. package/src/shared/utils/validation-client.ts +0 -161
  112. package/src/shared/utils/validation.ts +0 -399
  113. package/src/shared.ts +0 -155
  114. package/src/types/index.ts +0 -62
  115. /package/{dist/ai-YMnynb-t.mjs → ai-YMnynb-t.mjs} +0 -0
  116. /package/{dist/ai-YMnynb-t.mjs.map → ai-YMnynb-t.mjs.map} +0 -0
  117. /package/{dist/chunk-DQk6qfdC.mjs → chunk-DQk6qfdC.mjs} +0 -0
  118. /package/{dist/client-CTzJVFU5.mjs → client-CTzJVFU5.mjs} +0 -0
  119. /package/{dist/client-CTzJVFU5.mjs.map → client-CTzJVFU5.mjs.map} +0 -0
  120. /package/{dist/client-CcFTauAh.mjs → client-CcFTauAh.mjs} +0 -0
  121. /package/{dist/client-CcFTauAh.mjs.map → client-CcFTauAh.mjs.map} +0 -0
  122. /package/{dist/client-CeOLjbac.mjs → client-CeOLjbac.mjs} +0 -0
  123. /package/{dist/client-CeOLjbac.mjs.map → client-CeOLjbac.mjs.map} +0 -0
  124. /package/{dist/client-D339NFJS.mjs → client-D339NFJS.mjs} +0 -0
  125. /package/{dist/client-D339NFJS.mjs.map → client-D339NFJS.mjs.map} +0 -0
  126. /package/{dist/client-next.d.mts → client-next.d.mts} +0 -0
  127. /package/{dist/client-next.d.mts.map → client-next.d.mts.map} +0 -0
  128. /package/{dist/client-next.mjs → client-next.mjs} +0 -0
  129. /package/{dist/client-next.mjs.map → client-next.mjs.map} +0 -0
  130. /package/{dist/client.d.mts → client.d.mts} +0 -0
  131. /package/{dist/client.d.mts.map → client.d.mts.map} +0 -0
  132. /package/{dist/config-DPS6bSYo.d.mts → config-DPS6bSYo.d.mts} +0 -0
  133. /package/{dist/config-DPS6bSYo.d.mts.map → config-DPS6bSYo.d.mts.map} +0 -0
  134. /package/{dist/config-P6P5adJg.mjs → config-P6P5adJg.mjs} +0 -0
  135. /package/{dist/config-P6P5adJg.mjs.map → config-P6P5adJg.mjs.map} +0 -0
  136. /package/{dist/console-8bND3mMU.mjs → console-8bND3mMU.mjs} +0 -0
  137. /package/{dist/console-8bND3mMU.mjs.map → console-8bND3mMU.mjs.map} +0 -0
  138. /package/{dist/ecommerce-Cgu4wlux.mjs → ecommerce-Cgu4wlux.mjs} +0 -0
  139. /package/{dist/ecommerce-Cgu4wlux.mjs.map → ecommerce-Cgu4wlux.mjs.map} +0 -0
  140. /package/{dist/emitters-6-nKo8i-.mjs → emitters-6-nKo8i-.mjs} +0 -0
  141. /package/{dist/emitters-6-nKo8i-.mjs.map → emitters-6-nKo8i-.mjs.map} +0 -0
  142. /package/{dist/emitters-DldkVSPp.d.mts → emitters-DldkVSPp.d.mts} +0 -0
  143. /package/{dist/emitters-DldkVSPp.d.mts.map → emitters-DldkVSPp.d.mts.map} +0 -0
  144. /package/{dist/index-BfNWgfa5.d.mts → index-BfNWgfa5.d.mts} +0 -0
  145. /package/{dist/index-BfNWgfa5.d.mts.map → index-BfNWgfa5.d.mts.map} +0 -0
  146. /package/{dist/index-BkIWe--N.d.mts → index-BkIWe--N.d.mts} +0 -0
  147. /package/{dist/index-BkIWe--N.d.mts.map → index-BkIWe--N.d.mts.map} +0 -0
  148. /package/{dist/index-jPzXRn52.d.mts → index-jPzXRn52.d.mts} +0 -0
  149. /package/{dist/index-jPzXRn52.d.mts.map → index-jPzXRn52.d.mts.map} +0 -0
  150. /package/{dist/manager-DvRRjza6.d.mts → manager-DvRRjza6.d.mts} +0 -0
  151. /package/{dist/manager-DvRRjza6.d.mts.map → manager-DvRRjza6.d.mts.map} +0 -0
  152. /package/{dist/posthog-bootstrap-CYfIy_WS.mjs → posthog-bootstrap-CYfIy_WS.mjs} +0 -0
  153. /package/{dist/posthog-bootstrap-CYfIy_WS.mjs.map → posthog-bootstrap-CYfIy_WS.mjs.map} +0 -0
  154. /package/{dist/posthog-bootstrap-DWxFrxlt.d.mts → posthog-bootstrap-DWxFrxlt.d.mts} +0 -0
  155. /package/{dist/posthog-bootstrap-DWxFrxlt.d.mts.map → posthog-bootstrap-DWxFrxlt.d.mts.map} +0 -0
  156. /package/{dist/providers-http-client.d.mts → providers-http-client.d.mts} +0 -0
  157. /package/{dist/providers-http-client.d.mts.map → providers-http-client.d.mts.map} +0 -0
  158. /package/{dist/providers-http-client.mjs → providers-http-client.mjs} +0 -0
  159. /package/{dist/providers-http-client.mjs.map → providers-http-client.mjs.map} +0 -0
  160. /package/{dist/providers-http-server.d.mts → providers-http-server.d.mts} +0 -0
  161. /package/{dist/providers-http-server.d.mts.map → providers-http-server.d.mts.map} +0 -0
  162. /package/{dist/providers-http-server.mjs → providers-http-server.mjs} +0 -0
  163. /package/{dist/providers-http-server.mjs.map → providers-http-server.mjs.map} +0 -0
  164. /package/{dist/providers-http.d.mts → providers-http.d.mts} +0 -0
  165. /package/{dist/providers-http.d.mts.map → providers-http.d.mts.map} +0 -0
  166. /package/{dist/providers-http.mjs → providers-http.mjs} +0 -0
  167. /package/{dist/server-edge.d.mts → server-edge.d.mts} +0 -0
  168. /package/{dist/server-edge.d.mts.map → server-edge.d.mts.map} +0 -0
  169. /package/{dist/server-edge.mjs → server-edge.mjs} +0 -0
  170. /package/{dist/server-edge.mjs.map → server-edge.mjs.map} +0 -0
  171. /package/{dist/server-next.d.mts → server-next.d.mts} +0 -0
  172. /package/{dist/server-next.d.mts.map → server-next.d.mts.map} +0 -0
  173. /package/{dist/server-next.mjs.map → server-next.mjs.map} +0 -0
  174. /package/{dist/server.d.mts → server.d.mts} +0 -0
  175. /package/{dist/shared.d.mts → shared.d.mts} +0 -0
  176. /package/{dist/shared.d.mts.map → shared.d.mts.map} +0 -0
  177. /package/{dist/shared.mjs → shared.mjs} +0 -0
  178. /package/{dist/shared.mjs.map → shared.mjs.map} +0 -0
  179. /package/{dist/types-BxBnNQ0V.d.mts → types-BxBnNQ0V.d.mts} +0 -0
  180. /package/{dist/types-BxBnNQ0V.d.mts.map → types-BxBnNQ0V.d.mts.map} +0 -0
  181. /package/{dist/types-CBvxUEaF.d.mts → types-CBvxUEaF.d.mts} +0 -0
  182. /package/{dist/types-CBvxUEaF.d.mts.map → types-CBvxUEaF.d.mts.map} +0 -0
  183. /package/{dist/types.d.mts → types.d.mts} +0 -0
  184. /package/{dist/types.mjs → types.mjs} +0 -0
  185. /package/{dist/vercel-types-lwakUfoI.d.mts → vercel-types-lwakUfoI.d.mts} +0 -0
  186. /package/{dist/vercel-types-lwakUfoI.d.mts.map → vercel-types-lwakUfoI.d.mts.map} +0 -0
@@ -4,10 +4,114 @@ import { t as PROVIDER_REQUIREMENTS } from "./config-P6P5adJg.mjs";
4
4
  import { HttpServerProvider } from "./providers-http-server.mjs";
5
5
  import { logError, logInfo, logWarn } from "@od-oneapp/shared/logger";
6
6
  import { logDebug as logDebug$1, logError as logError$1, logInfo as logInfo$1, logWarn as logWarn$1 } from "@od-oneapp/shared/logs";
7
- import { SegmentServerProvider } from "@integrations/segment/analytics-provider/server";
8
- import { VercelServerProvider } from "@integrations/vercel/analytics-provider/server";
9
7
  import { z } from "zod";
10
8
 
9
+ //#region ../../integrations/segment/src/analytics-provider/server.ts
10
+ var SegmentServerProvider = class {
11
+ name = "segment";
12
+ analytics = null;
13
+ config;
14
+ isInitialized = false;
15
+ constructor(config) {
16
+ if (!config.writeKey) throw new Error("Segment writeKey is required");
17
+ this.config = {
18
+ options: config.options,
19
+ writeKey: config.writeKey
20
+ };
21
+ }
22
+ async initialize() {
23
+ if (this.isInitialized) return;
24
+ const { Analytics } = await import(
25
+ /* webpackChunkName: "segment-analytics" */
26
+ "@segment/analytics-next"
27
+ );
28
+ this.analytics = new Analytics({
29
+ writeKey: this.config.writeKey,
30
+ ...this.config.options
31
+ });
32
+ this.isInitialized = true;
33
+ }
34
+ async track(event, properties = {}, _context) {
35
+ if (!this.analytics) return;
36
+ await this.analytics.track({
37
+ event,
38
+ properties
39
+ });
40
+ }
41
+ async identify(userId, traits = {}, _context) {
42
+ if (!this.analytics) return;
43
+ await this.analytics.identify({
44
+ userId,
45
+ traits
46
+ });
47
+ }
48
+ async page(name, properties = {}, _context) {
49
+ if (!this.analytics) return;
50
+ await this.analytics.page({
51
+ ...name && { name },
52
+ properties
53
+ });
54
+ }
55
+ async group(groupId, traits = {}, _context) {
56
+ if (!this.analytics) return;
57
+ await this.analytics.group({
58
+ groupId,
59
+ traits
60
+ });
61
+ }
62
+ async alias(userId, previousId, _context) {
63
+ if (!this.analytics) return;
64
+ await this.analytics.alias({
65
+ userId,
66
+ previousId
67
+ });
68
+ }
69
+ };
70
+
71
+ //#endregion
72
+ //#region ../../integrations/vercel/src/analytics-provider/server.ts
73
+ var VercelServerProvider = class {
74
+ name = "vercel";
75
+ isInitialized = false;
76
+ config;
77
+ constructor(config) {
78
+ this.config = config;
79
+ }
80
+ async initialize() {
81
+ if (this.isInitialized) return;
82
+ this.isInitialized = true;
83
+ }
84
+ async track(event, properties = {}) {
85
+ if (!this.isInitialized) return;
86
+ this.config;
87
+ }
88
+ async identify(userId, traits = {}) {
89
+ await this.track("User Identified", {
90
+ userId,
91
+ ...traits
92
+ });
93
+ }
94
+ async page(name, properties = {}) {
95
+ await this.track("Page View (Server)", {
96
+ page: name,
97
+ ...properties
98
+ });
99
+ }
100
+ async group(groupId, traits = {}) {
101
+ await this.track("Group Identified", {
102
+ groupId,
103
+ ...traits
104
+ });
105
+ }
106
+ async alias(userId, previousId) {
107
+ await this.track("User Aliased", {
108
+ previousId,
109
+ userId
110
+ });
111
+ }
112
+ };
113
+
114
+ //#endregion
11
115
  //#region src/server/manager.ts
12
116
  /**
13
117
  * @fileoverview Server analytics manager with static provider registry
@@ -942,4 +1046,4 @@ function validateBatchPayload(payloads) {
942
1046
 
943
1047
  //#endregion
944
1048
  export { debugConfig as a, validateProvider as c, validateEventPayload as i, createServerAnalytics as l, createIngestionService as n, validateAnalyticsConfig as o, validateBatchPayload as r, validateConfigOrThrow as s, IngestionService as t, createServerAnalyticsUninitialized as u };
945
- //# sourceMappingURL=service-cYtBBL8x.mjs.map
1049
+ //# sourceMappingURL=service-Duqnlppl.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-Duqnlppl.mjs","names":[],"sources":["../../../integrations/segment/src/analytics-provider/server.ts","../../../integrations/vercel/src/analytics-provider/server.ts","../src/server/manager.ts","../src/shared/utils/validation.ts","../src/shared/ingestion/schemas.ts","../src/shared/ingestion/service.ts"],"sourcesContent":["/**\n * @fileoverview Segment server-side (Node.js) analytics provider\n *\n * Provides server-side integration with Segment Analytics using @segment/analytics-next.\n * Implements the @od-oneapp/analytics AnalyticsProvider interface for use in the analytics\n * manager's provider registry.\n *\n * @module @integrations/segment/analytics-provider/server\n */\n\nimport type {\n AnalyticsContext,\n AnalyticsProvider,\n GroupTraits,\n PageProperties,\n Properties,\n ProviderConfig,\n SegmentConfig,\n UserTraits,\n} from './types';\n\n// Type for Analytics server instance\ninterface AnalyticsServerInstance {\n track: (params: { event: string; properties?: Properties }) => Promise<unknown>;\n identify: (params: { userId: string; traits?: UserTraits }) => Promise<unknown>;\n page: (params: { name?: string; properties?: PageProperties }) => Promise<unknown>;\n group: (params: { groupId: string; traits?: GroupTraits }) => Promise<unknown>;\n alias: (params: { userId: string; previousId: string }) => Promise<unknown>;\n}\n\nexport class SegmentServerProvider implements AnalyticsProvider {\n readonly name = 'segment';\n private analytics: AnalyticsServerInstance | null = null;\n private config: SegmentConfig;\n private isInitialized = false;\n\n constructor(config: ProviderConfig) {\n if (!config.writeKey) {\n throw new Error('Segment writeKey is required');\n }\n\n this.config = {\n options: config.options as Record<string, unknown>,\n writeKey: config.writeKey,\n };\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) return;\n\n const { Analytics } = await import(\n /* webpackChunkName: \"segment-analytics\" */\n '@segment/analytics-next'\n );\n\n this.analytics = new Analytics({\n writeKey: this.config.writeKey,\n ...this.config.options,\n } as ConstructorParameters<typeof Analytics>[0]) as unknown as AnalyticsServerInstance;\n\n this.isInitialized = true;\n }\n\n async track(\n event: string,\n properties: Properties = {},\n _context?: AnalyticsContext,\n ): Promise<void> {\n if (!this.analytics) return;\n await this.analytics.track({ event, properties });\n }\n\n async identify(\n userId: string,\n traits: UserTraits = {},\n _context?: AnalyticsContext,\n ): Promise<void> {\n if (!this.analytics) return;\n await this.analytics.identify({ userId, traits });\n }\n\n async page(\n name?: string,\n properties: PageProperties = {},\n _context?: AnalyticsContext,\n ): Promise<void> {\n if (!this.analytics) return;\n await this.analytics.page({ ...(name && { name }), properties });\n }\n\n async group(\n groupId: string,\n traits: GroupTraits = {},\n _context?: AnalyticsContext,\n ): Promise<void> {\n if (!this.analytics) return;\n await this.analytics.group({ groupId, traits });\n }\n\n async alias(userId: string, previousId: string, _context?: AnalyticsContext): Promise<void> {\n if (!this.analytics) return;\n await this.analytics.alias({ userId, previousId });\n }\n}\n","/**\n * @fileoverview Vercel Analytics server-side (Node.js) provider\n *\n * Provides minimal server-side integration with Vercel Analytics. Implements the\n * @od-oneapp/analytics AnalyticsProvider interface. Vercel Analytics is primarily\n * client-side focused; server-side tracking is limited.\n *\n * @module @integrations/vercel/analytics-provider/server\n */\n\nimport type {\n AnalyticsProvider,\n GroupTraits,\n PageProperties,\n Properties,\n ProviderConfig,\n UserTraits,\n} from './types';\n\nexport class VercelServerProvider implements AnalyticsProvider {\n readonly name = 'vercel';\n private isInitialized = false;\n private readonly config: ProviderConfig;\n\n constructor(config: ProviderConfig) {\n this.config = config;\n }\n\n async initialize(): Promise<void> {\n if (this.isInitialized) return;\n this.isInitialized = true;\n }\n\n async track(event: string, properties: Properties = {}): Promise<void> {\n if (!this.isInitialized) return;\n\n void event;\n void properties;\n void this.config;\n // Server-side tracking is limited — most tracking happens client-side.\n }\n\n async identify(userId: string, traits: UserTraits = {}): Promise<void> {\n await this.track('User Identified', { userId, ...traits });\n }\n\n async page(name?: string, properties: PageProperties = {}): Promise<void> {\n await this.track('Page View (Server)', { page: name, ...properties });\n }\n\n async group(groupId: string, traits: GroupTraits = {}): Promise<void> {\n await this.track('Group Identified', { groupId, ...traits });\n }\n\n async alias(userId: string, previousId: string): Promise<void> {\n await this.track('User Aliased', { previousId, userId });\n }\n}\n","/**\n * @fileoverview Server analytics manager with static provider registry\n * Server analytics manager with static provider registry\n */\n\nimport { SegmentServerProvider } from '@integrations/segment/analytics-provider/server';\nimport { VercelServerProvider } from '@integrations/vercel/analytics-provider/server';\n\nimport { ConsoleProvider } from '../providers/console/server';\nimport { HttpServerProvider } from '../providers/http/server';\nimport { createAnalyticsManager } from '../shared/utils/manager';\n\nimport type { AnalyticsConfig, AnalyticsManager, ProviderRegistry } from '../shared/types/types';\n\n// Static provider registry for server environments\nconst SERVER_PROVIDERS: ProviderRegistry = {\n console: config => new ConsoleProvider(config),\n http: config => new HttpServerProvider(config),\n segment: config => new SegmentServerProvider(config),\n vercel: config => new VercelServerProvider(config),\n};\n\n/**\n * Create and initialize a server analytics instance\n * This is the primary way to create analytics for server-side applications\n *\n * @example\n * ```typescript\n * const analytics = await createServerAnalytics({\n * providers: {\n * segment: { writeKey: process.env.SEGMENT_KEY! },\n * },\n * });\n * await analytics.page('/admin', { title: 'Admin Dashboard' });\n * ```\n * @param config - Analytics configuration including providers and settings\n * @returns Promise resolving to initialized analytics manager\n */\nexport async function createServerAnalytics(config: AnalyticsConfig): Promise<AnalyticsManager> {\n const manager = createAnalyticsManager(config, SERVER_PROVIDERS);\n await manager.initialize();\n return manager;\n}\n\n/**\n * Create a server analytics instance without initializing\n * Useful when you need to control initialization timing\n *\n * @example\n * ```typescript\n * const analytics = createServerAnalyticsUninitialized(config);\n * if (shouldEmit) {\n * await analytics.initialize();\n * await analytics.track('CRON Completed');\n * }\n * ```\n * @param config - Analytics configuration including providers and settings\n * @returns Uninitialized analytics manager instance\n */\nexport function createServerAnalyticsUninitialized(config: AnalyticsConfig): AnalyticsManager {\n return createAnalyticsManager(config, SERVER_PROVIDERS);\n}\n","/**\n * @fileoverview Validation utilities for analytics configuration\n *\n * This module provides comprehensive validation for analytics configurations,\n * including provider validation, environment-specific checks, and helpful\n * warnings for common misconfigurations.\n *\n * **Features**:\n * - Configuration structure validation\n * - Provider-specific field validation\n * - Environment variable validation\n * - Environment-specific warnings\n * - Detailed error reporting\n *\n * @module @od-oneapp/analytics/shared/utils/validation\n */\n\nimport { logError, logInfo, logWarn } from '@repo/shared/logger';\n\nimport { PROVIDER_REQUIREMENTS } from './config';\n\nimport type { AnalyticsConfig, ProviderConfig } from '../types/types';\n\n/**\n * Validation error structure.\n *\n * Represents a single validation error with field, message, and provider context.\n */\nexport interface ValidationError {\n /** Field name that failed validation */\n field: string;\n /** Human-readable error message */\n message: string;\n /** Provider name (or 'global' for config-level errors) */\n provider: string;\n}\n\n/**\n * Validation result structure.\n *\n * Contains validation errors, warnings, and overall validity status.\n */\nexport interface ValidationResult {\n /** Array of validation errors */\n errors: ValidationError[];\n /** Whether the configuration is valid (no errors) */\n isValid: boolean;\n /** Array of warning messages */\n warnings: string[];\n}\n\n/**\n * Comprehensive configuration validation.\n *\n * Validates the entire analytics configuration structure, including:\n * - Configuration object structure\n * - Providers object existence\n * - Individual provider configurations\n * - Environment-specific warnings\n *\n * Accepts `unknown` input for defensive validation of potentially malformed configs.\n *\n * @param {unknown} config - Analytics configuration to validate\n * @returns {ValidationResult} Validation result with errors, warnings, and validity status\n *\n * @example\n * ```typescript\n * const result = validateAnalyticsConfig(config);\n * if (!result.isValid) {\n * console.error('Validation errors:', result.errors);\n * }\n * if (result.warnings.length > 0) {\n * console.warn('Warnings:', result.warnings);\n * }\n * ```\n */\nexport function validateAnalyticsConfig(config: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n const warnings: string[] = [];\n\n // Check if config exists and is an object\n if (!config || typeof config !== 'object') {\n errors.push({\n provider: 'global',\n field: 'config',\n message: 'Analytics configuration is required and must be an object',\n });\n return { isValid: false, errors, warnings };\n }\n\n const typedConfig = config as AnalyticsConfig;\n\n // Check if providers object exists\n if (!typedConfig.providers || typeof typedConfig.providers !== 'object') {\n errors.push({\n provider: 'global',\n field: 'providers',\n message: 'Providers configuration is required and must be an object',\n });\n return { isValid: false, errors, warnings };\n }\n\n // Check if at least one provider is configured\n const providerCount = Object.keys(typedConfig.providers).length;\n if (providerCount === 0) {\n warnings.push('No providers configured. Analytics will not track any events.');\n }\n\n // Validate each provider\n for (const [providerName, providerConfig] of Object.entries(typedConfig.providers)) {\n const providerErrors = validateProvider(providerName, providerConfig);\n errors.push(...providerErrors);\n }\n\n // Environment-specific warnings\n const isBrowser = typeof window !== 'undefined';\n\n // Mixpanel warning for client-side usage\n if (isBrowser && typedConfig.providers.mixpanel) {\n warnings.push(\n 'Mixpanel provider configured on client-side. Consider using server-side for better performance.',\n );\n }\n\n if (!isBrowser && typedConfig.providers.vercel) {\n warnings.push(\n 'Vercel Analytics has limited server-side support. Consider using client-side for better features.',\n );\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/**\n * Validate a single provider configuration.\n *\n * Checks that the provider is known and that all required fields are present.\n *\n * @param {string} providerName - Name of the provider to validate\n * @param {ProviderConfig} config - Provider configuration to validate\n * @returns {ValidationError[]} Array of validation errors (empty if valid)\n *\n * @example\n * ```typescript\n * const errors = validateProvider('posthog', { apiKey: 'phc_xxx' });\n * if (errors.length > 0) {\n * console.error('Provider errors:', errors);\n * }\n * ```\n */\nexport function validateProvider(providerName: string, config: ProviderConfig): ValidationError[] {\n const errors: ValidationError[] = [];\n\n // Check if provider is known\n const knownProviders = ['segment', 'posthog', 'vercel', 'console', 'mixpanel'];\n if (!knownProviders.includes(providerName)) {\n errors.push({\n provider: providerName,\n field: 'name',\n message: `Unknown provider '${providerName}'. Known providers: ${knownProviders.join(', ')}`,\n });\n return errors;\n }\n\n // Check required fields\n const requiredFields = PROVIDER_REQUIREMENTS[providerName] ?? [];\n for (const field of requiredFields) {\n const value = config[field as keyof ProviderConfig];\n\n if (!value) {\n errors.push({\n provider: providerName,\n field,\n message: `Required field '${field}' is missing for provider '${providerName}'`,\n });\n } else if (typeof value === 'string' && value.trim() === '') {\n errors.push({\n provider: providerName,\n field,\n message: `Required field '${field}' cannot be empty for provider '${providerName}'`,\n });\n }\n }\n\n // Provider-specific validation\n switch (providerName) {\n case 'segment':\n if (config.writeKey && !isValidSegmentWriteKey(config.writeKey)) {\n errors.push({\n provider: providerName,\n field: 'writeKey',\n message: 'Segment writeKey appears to be invalid format',\n });\n }\n break;\n\n case 'posthog':\n if (config.apiKey && !isValidPostHogApiKey(config.apiKey)) {\n errors.push({\n provider: providerName,\n field: 'apiKey',\n message: 'PostHog apiKey appears to be invalid format',\n });\n }\n break;\n }\n\n return errors;\n}\n\n/**\n * Validate environment variables for analytics.\n *\n * Checks common analytics environment variables for:\n * - Empty values\n * - Placeholder text\n * - Development environment warnings\n *\n * @returns {ValidationResult} Validation result with warnings about environment variables\n *\n * @example\n * ```typescript\n * const result = validateEnvironmentVariables();\n * if (result.warnings.length > 0) {\n * console.warn('Environment warnings:', result.warnings);\n * }\n * ```\n */\nexport function validateEnvironmentVariables(): ValidationResult {\n const errors: ValidationError[] = [];\n const warnings: string[] = [];\n\n // Check for common environment variables\n const envVars = {\n POSTHOG_API_KEY: process.env.POSTHOG_API_KEY,\n SEGMENT_WRITE_KEY: process.env.SEGMENT_WRITE_KEY,\n };\n\n for (const [varName, value] of Object.entries(envVars)) {\n if (value && typeof value === 'string') {\n if (value.trim() === '') {\n warnings.push(`Environment variable ${varName} is set but empty`);\n } else if (value.includes('your-') || value.includes('paste-')) {\n warnings.push(`Environment variable ${varName} appears to contain placeholder text`);\n }\n }\n }\n\n // Warn about development environment\n if (\n process.env.NODE_ENV === 'development' &&\n !envVars.SEGMENT_WRITE_KEY &&\n !envVars.POSTHOG_API_KEY\n ) {\n warnings.push(\n 'No analytics environment variables detected in development. Consider using console provider for debugging.',\n );\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/**\n * Validates Segment write key format.\n *\n * Checks that the write key matches Segment's expected format:\n * - Exactly 32 alphanumeric characters\n * - Not a placeholder value\n *\n * @param {string} writeKey - Write key to validate\n * @returns {boolean} `true` if valid, `false` otherwise\n *\n * @internal\n */\nfunction isValidSegmentWriteKey(writeKey: string): boolean {\n // Segment write keys are exactly 32 characters, alphanumeric\n if (!/^[\\dA-Za-z]{32}$/.test(writeKey)) {\n return false;\n }\n\n // Check for common placeholder patterns\n const placeholders = ['your-write-key', 'paste-key-here', 'xxxxxxxx', 'example'];\n if (placeholders.some(p => writeKey.toLowerCase().includes(p.toLowerCase()))) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Validates PostHog API key format.\n *\n * Checks that the API key matches PostHog's expected format:\n * - Starts with `phc_` prefix\n * - Followed by 43 alphanumeric/dash/underscore characters\n * - Not a placeholder value\n *\n * @param {string} apiKey - API key to validate\n * @returns {boolean} `true` if valid, `false` otherwise\n *\n * @internal\n */\nfunction isValidPostHogApiKey(apiKey: string): boolean {\n // PostHog keys: phc_ prefix + 43 alphanumeric/dash/underscore characters\n if (!/^phc_[\\w-]{43}$/.test(apiKey)) {\n return false;\n }\n\n // Check for placeholder patterns\n const placeholders = ['your-api-key', 'paste-key-here', 'xxxxxxxx', 'example'];\n if (placeholders.some(p => apiKey.toLowerCase().includes(p.toLowerCase()))) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Utility to throw validation errors (for strict validation).\n *\n * Validates the configuration and throws an error if validation fails.\n * Useful for ensuring configuration is valid before using analytics.\n *\n * @param {AnalyticsConfig} config - Analytics configuration to validate\n * @throws {Error} If configuration validation fails\n *\n * @example\n * ```typescript\n * try {\n * validateConfigOrThrow(config);\n * // Configuration is valid, proceed\n * } catch (error) {\n * console.error('Invalid config:', error.message);\n * }\n * ```\n */\nexport function validateConfigOrThrow(config: AnalyticsConfig): void {\n const result = validateAnalyticsConfig(config);\n\n if (!result.isValid) {\n const errorMessages = result.errors\n .map(error => `${error.provider}.${error.field}: ${error.message}`)\n .join('\\n');\n\n throw new Error(`Analytics configuration validation failed:\\n${errorMessages}`);\n }\n\n // Log warnings but don't throw\n if (result.warnings.length > 0 && config.onError) {\n config.onError(new Error('Analytics configuration warnings'), {\n provider: 'analytics',\n method: 'validateConfig',\n warnings: result.warnings,\n });\n }\n}\n\n/**\n * Development helper to check configuration.\n *\n * Logs configuration details and validation results for debugging.\n * Only useful in development environments.\n *\n * @param {AnalyticsConfig} config - Analytics configuration to debug\n * @returns {Promise<void>} Promise that resolves when debugging is complete\n *\n * @example\n * ```typescript\n * if (process.env.NODE_ENV === 'development') {\n * await debugConfig(config);\n * }\n * ```\n */\nexport async function debugConfig(config: AnalyticsConfig): Promise<void> {\n const result = validateAnalyticsConfig(config);\n\n logInfo('Analytics Configuration Debug', {\n config,\n validationResult: result,\n });\n\n if (result.errors.length > 0) {\n logError('Analytics configuration errors: Validation failed', {\n errors: result.errors,\n });\n }\n\n if (result.warnings.length > 0) {\n logWarn('Analytics configuration warnings', { warnings: result.warnings });\n }\n}\n","/**\n * @fileoverview Event Ingestion Schemas and Validation\n *\n * Defines Zod schemas for validating event ingestion payloads. These schemas\n * ensure type-safe, secure event ingestion with proper validation.\n *\n * **Key Features**:\n * - CloudEvents-style fields for future interoperability\n * - Support for single events and batched arrays\n * - Strict validation of required fields\n * - Typed extensions per event category\n *\n * @module @od-oneapp/analytics/shared/ingestion/schemas\n */\n\nimport { z } from 'zod';\n\n// =============================================================================\n// Base Schemas\n// =============================================================================\n\n/**\n * Property value schema - safe, serializable values only.\n */\nexport const PropertyValueSchema = z.union([\n z.string(),\n z.number(),\n z.boolean(),\n z.null(),\n z.date(), // Accept Date objects only; ISO strings are already handled as strings\n]);\n\n/**\n * Property object schema with nested structure support.\n */\nexport const PropertyObjectSchema: z.ZodType<Record<string, unknown>> = z.record(\n z.string(),\n z.lazy(() =>\n z.union([PropertyValueSchema, z.array(PropertyValueSchema), z.record(z.string(), z.unknown())]),\n ),\n);\n\n/**\n * Emitter context schema - contextual information about the environment.\n */\nexport const EmitterContextSchema = z\n .object({\n /** Application information */\n app: z\n .object({\n name: z.string().optional(),\n version: z.string().optional(),\n build: z.string().optional(),\n namespace: z.string().optional(),\n })\n .optional(),\n\n /** Campaign/UTM information */\n campaign: z\n .object({\n name: z.string().optional(),\n source: z.string().optional(),\n medium: z.string().optional(),\n term: z.string().optional(),\n content: z.string().optional(),\n })\n .passthrough()\n .optional(),\n\n /** Device information */\n device: z\n .object({\n id: z.string().optional(),\n manufacturer: z.string().optional(),\n model: z.string().optional(),\n name: z.string().optional(),\n type: z.string().optional(),\n version: z.string().optional(),\n })\n .optional(),\n\n /** User's IP address */\n ip: z.string().optional(),\n\n /** Library making the request */\n library: z\n .object({\n name: z.string(),\n version: z.string(),\n })\n .optional(),\n\n /** User's locale (e.g., 'en-US') */\n locale: z.string().optional(),\n\n /** Network information */\n network: z\n .object({\n bluetooth: z.boolean().optional(),\n carrier: z.string().optional(),\n cellular: z.boolean().optional(),\n wifi: z.boolean().optional(),\n })\n .optional(),\n\n /** OS information */\n os: z\n .object({\n name: z.string().optional(),\n version: z.string().optional(),\n })\n .optional(),\n\n /** Page information */\n page: z\n .object({\n path: z.string().optional(),\n // Use permissive string validation for referrer/url since real-world values\n // may be malformed, truncated, or non-standard (e.g., data: URIs, about:blank)\n referrer: z.string().optional(),\n search: z.string().optional(),\n title: z.string().optional(),\n url: z.string().optional(),\n })\n .optional(),\n\n /** Screen information */\n screen: z\n .object({\n density: z.number().optional(),\n height: z.number().int().positive().optional(),\n width: z.number().int().positive().optional(),\n })\n .optional(),\n\n /** User's timezone (tzdata string) */\n timezone: z.string().optional(),\n\n /** Group/account ID */\n groupId: z.string().optional(),\n\n /** User agent string */\n userAgent: z.string().optional(),\n\n /** Channel where request originated */\n channel: z.enum(['server', 'browser', 'mobile', 'api']).optional(),\n\n /** Location context */\n location: z\n .object({\n city: z.string().optional(),\n country: z.string().optional(),\n latitude: z.number().optional(),\n longitude: z.number().optional(),\n region: z.string().optional(),\n })\n .optional(),\n })\n .passthrough();\n\n// =============================================================================\n// Event Type Schemas\n// =============================================================================\n\n/**\n * Base payload schema - common fields for all event types.\n */\nconst BasePayloadSchema = z\n .object({\n /** Anonymous ID if user is not identified */\n anonymousId: z.string().max(255).optional(),\n\n /** User ID (at least one of userId or anonymousId required) */\n userId: z.string().max(255).optional(),\n\n /** Timestamp when the event occurred */\n timestamp: z.union([z.string().datetime(), z.date()]).optional(),\n\n /** Original timestamp before processing */\n originalTimestamp: z.union([z.string().datetime(), z.date()]).optional(),\n\n /** Context and control fields */\n context: EmitterContextSchema.optional(),\n\n /** Message ID for deduplication */\n messageId: z.string().uuid().optional(),\n })\n .refine(data => Boolean(data.anonymousId) || Boolean(data.userId), {\n message: 'Either anonymousId or userId must be provided',\n });\n\n/**\n * Track event payload schema.\n * Uses safeExtend() because BasePayloadSchema has refinements (Zod v4 requirement).\n */\nexport const TrackEventSchema = BasePayloadSchema.safeExtend({\n type: z.literal('track'),\n event: z.string().min(1).max(255),\n properties: PropertyObjectSchema.optional(),\n});\n\n/**\n * Identify event payload schema.\n * Uses safeExtend() because BasePayloadSchema has refinements (Zod v4 requirement).\n */\nexport const IdentifyEventSchema = BasePayloadSchema.safeExtend({\n type: z.literal('identify'),\n userId: z.string().min(1).max(255),\n traits: PropertyObjectSchema.optional(),\n});\n\n/**\n * Page event payload schema.\n * Uses safeExtend() because BasePayloadSchema has refinements (Zod v4 requirement).\n */\nexport const PageEventSchema = BasePayloadSchema.safeExtend({\n type: z.literal('page'),\n name: z.string().max(255).optional(),\n category: z.string().max(255).optional(),\n properties: PropertyObjectSchema.optional(),\n});\n\n/**\n * Screen event payload schema.\n * Uses safeExtend() because BasePayloadSchema has refinements (Zod v4 requirement).\n */\nexport const ScreenEventSchema = BasePayloadSchema.safeExtend({\n type: z.literal('screen'),\n name: z.string().max(255).optional(),\n category: z.string().max(255).optional(),\n properties: PropertyObjectSchema.optional(),\n});\n\n/**\n * Group event payload schema.\n * Uses safeExtend() because BasePayloadSchema has refinements (Zod v4 requirement).\n */\nexport const GroupEventSchema = BasePayloadSchema.safeExtend({\n type: z.literal('group'),\n groupId: z.string().min(1).max(255),\n traits: PropertyObjectSchema.optional(),\n});\n\n/**\n * Alias event payload schema.\n * Uses safeExtend() because BasePayloadSchema has refinements (Zod v4 requirement).\n */\nexport const AliasEventSchema = BasePayloadSchema.safeExtend({\n type: z.literal('alias'),\n userId: z.string().min(1).max(255),\n previousId: z.string().min(1).max(255),\n});\n\n/**\n * Union of all event payload schemas.\n */\nexport const EventPayloadSchema = z.discriminatedUnion('type', [\n TrackEventSchema,\n IdentifyEventSchema,\n PageEventSchema,\n ScreenEventSchema,\n GroupEventSchema,\n AliasEventSchema,\n]);\n\n// =============================================================================\n// Ingestion Request Schemas\n// =============================================================================\n\n/**\n * Single event ingestion request schema.\n */\nexport const SingleEventRequestSchema = EventPayloadSchema;\n\n/**\n * Batch event ingestion request schema.\n *\n * The batch size limit is enforced at the service level via `maxBatchSize` config\n * (default: 100) rather than in the schema, allowing for flexible configuration.\n */\nexport const BatchEventRequestSchema = z.object({\n batch: z.array(EventPayloadSchema).min(1),\n});\n\n/**\n * Combined ingestion request schema - accepts single event or batch.\n */\nexport const IngestionRequestSchema = z.union([BatchEventRequestSchema, SingleEventRequestSchema]);\n\n// =============================================================================\n// Ingestion Response Schemas\n// =============================================================================\n\n/**\n * Event processing result.\n */\nexport const EventResultSchema = z.object({\n /** Server-generated event ID */\n id: z.string().uuid(),\n\n /** Original message ID if provided */\n messageId: z.string().uuid().optional(),\n\n /** Event type */\n type: z.enum(['track', 'identify', 'page', 'screen', 'group', 'alias']),\n\n /** Processing status */\n status: z.enum(['accepted', 'rejected']),\n\n /** Error message if rejected */\n error: z.string().optional(),\n});\n\n/**\n * Successful ingestion response schema.\n */\nexport const IngestionSuccessResponseSchema = z.object({\n /** Whether the request was successful */\n success: z.literal(true),\n\n /** Number of events accepted */\n accepted: z.number().int().nonnegative(),\n\n /** Number of events rejected */\n rejected: z.number().int().nonnegative(),\n\n /** Per-event results */\n results: z.array(EventResultSchema),\n\n /** Server timestamp when events were received */\n receivedAt: z.string().datetime(),\n});\n\n/**\n * Error response schema.\n */\nexport const IngestionErrorResponseSchema = z.object({\n /** Whether the request was successful */\n success: z.literal(false),\n\n /** Error code */\n code: z.string(),\n\n /** Error message */\n error: z.string(),\n\n /** Field-level validation errors */\n fieldErrors: z\n .array(\n z.object({\n path: z.array(z.union([z.string(), z.number()])),\n message: z.string(),\n }),\n )\n .optional(),\n});\n\n/**\n * Combined response schema.\n */\nexport const IngestionResponseSchema = z.union([\n IngestionSuccessResponseSchema,\n IngestionErrorResponseSchema,\n]);\n\n// =============================================================================\n// Type Exports\n// =============================================================================\n\nexport type PropertyValue = z.infer<typeof PropertyValueSchema>;\nexport type PropertyObject = z.infer<typeof PropertyObjectSchema>;\nexport type EmitterContext = z.infer<typeof EmitterContextSchema>;\nexport type TrackEvent = z.infer<typeof TrackEventSchema>;\nexport type IdentifyEvent = z.infer<typeof IdentifyEventSchema>;\nexport type PageEvent = z.infer<typeof PageEventSchema>;\nexport type ScreenEvent = z.infer<typeof ScreenEventSchema>;\nexport type GroupEvent = z.infer<typeof GroupEventSchema>;\nexport type AliasEvent = z.infer<typeof AliasEventSchema>;\nexport type EventPayload = z.infer<typeof EventPayloadSchema>;\nexport type SingleEventRequest = z.infer<typeof SingleEventRequestSchema>;\nexport type BatchEventRequest = z.infer<typeof BatchEventRequestSchema>;\nexport type IngestionRequest = z.infer<typeof IngestionRequestSchema>;\nexport type EventResult = z.infer<typeof EventResultSchema>;\nexport type IngestionSuccessResponse = z.infer<typeof IngestionSuccessResponseSchema>;\nexport type IngestionErrorResponse = z.infer<typeof IngestionErrorResponseSchema>;\nexport type IngestionResponse = z.infer<typeof IngestionResponseSchema>;\n","/**\n * @fileoverview Event Ingestion Service\n *\n * Provides server-side event ingestion functionality. Handles validation,\n * normalization, and forwarding of events to the analytics system.\n *\n * **Key Features**:\n * - Validates incoming events against Zod schemas\n * - Normalizes event payloads with defaults\n * - Forwards events to AnalyticsManager\n * - Tracks ingestion metrics\n * - Handles batch processing efficiently\n *\n * @module @od-oneapp/analytics/shared/ingestion/service\n */\n\nimport { logDebug, logError, logInfo, logWarn } from '@repo/shared/logs';\n\nimport { sanitizeProperties, validateEventName } from '../utils/security';\n\nimport { BatchEventRequestSchema, EventPayloadSchema, IngestionRequestSchema } from './schemas';\n\nimport type {\n BatchEventRequest,\n EmitterContext,\n EventPayload,\n EventResult,\n IngestionErrorResponse,\n IngestionRequest,\n IngestionSuccessResponse,\n} from './schemas';\nimport type { EmitterPayload } from '../emitters/emitter-types';\nimport type { AnalyticsManager } from '../types/types';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Ingestion context provided by the caller (usually the API route handler).\n */\nexport interface IngestionContext {\n /** Source identifier (app/module/emitter id) */\n source: string;\n\n /** Tenant/project ID for multi-tenancy */\n tenantId?: string;\n\n /** User ID from auth context */\n userId?: string;\n\n /** Account/organization ID */\n accountId?: string;\n\n /** Client IP address */\n ip?: string;\n\n /** Client user agent */\n userAgent?: string;\n\n /** Environment (dev/stage/prod) */\n environment?: string;\n\n /** Trace ID for observability */\n traceId?: string;\n\n /** Correlation ID for request tracing */\n correlationId?: string;\n\n /** API version */\n apiVersion?: string;\n\n /** SDK version from client */\n sdkVersion?: string;\n}\n\n/**\n * Ingestion service configuration.\n */\nexport interface IngestionServiceConfig {\n /** Maximum events per batch (default: 100) */\n maxBatchSize?: number;\n\n /** Maximum payload size in bytes (default: 1MB) */\n maxPayloadSize?: number;\n\n /** Whether to strip PII from properties (default: true in production) */\n stripPII?: boolean;\n\n /** Whether to strip HTML from properties (default: true) */\n stripHTML?: boolean;\n\n /** Timeout for processing each event in ms (default: 5000) */\n eventTimeout?: number;\n\n /** Concurrency for batch processing (default: 10) */\n batchConcurrency?: number;\n}\n\n/**\n * Ingestion metrics for observability.\n */\nexport interface IngestionMetrics {\n /** Total events received */\n totalReceived: number;\n\n /** Events accepted */\n accepted: number;\n\n /** Events rejected */\n rejected: number;\n\n /** Events by type */\n byType: Record<string, number>;\n\n /** Processing time in ms */\n processingTimeMs: number;\n}\n\n// =============================================================================\n// Default Configuration\n// =============================================================================\n\nconst DEFAULT_CONFIG: Required<IngestionServiceConfig> = {\n maxBatchSize: 100,\n maxPayloadSize: 1024 * 1024, // 1MB\n stripPII: true, // Default to safe; callers can override\n stripHTML: true,\n eventTimeout: 5000,\n batchConcurrency: 10,\n};\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Generate a UUID v4.\n */\nfunction generateUUID(): string {\n return crypto.randomUUID();\n}\n\n/**\n * Get current ISO timestamp.\n */\nfunction getCurrentTimestamp(): string {\n return new Date().toISOString();\n}\n\n/**\n * Check if request is a batch request.\n */\nfunction isBatchRequest(request: IngestionRequest): request is BatchEventRequest {\n return 'batch' in request && Array.isArray(request.batch);\n}\n\n/**\n * Normalize an event payload with defaults and server metadata.\n */\nfunction normalizeEvent(\n event: EventPayload,\n context: IngestionContext,\n receivedAt: string,\n): EventPayload {\n const normalized = { ...event };\n\n // Generate message ID if not provided\n if (!normalized.messageId) {\n normalized.messageId = generateUUID();\n }\n\n // Set timestamp if not provided\n if (!normalized.timestamp) {\n normalized.timestamp = receivedAt;\n }\n\n // Preserve original timestamp\n if (!normalized.originalTimestamp) {\n normalized.originalTimestamp = normalized.timestamp;\n }\n\n // Merge context with server-provided context\n const mergedContext: EmitterContext = {\n ...normalized.context,\n channel: normalized.context?.channel ?? 'api',\n ip: context.ip ?? normalized.context?.ip,\n userAgent: context.userAgent ?? normalized.context?.userAgent,\n library: normalized.context?.library ?? {\n name: context.source,\n version: context.sdkVersion ?? 'unknown',\n },\n };\n\n normalized.context = mergedContext;\n\n // Set userId from auth context if not provided\n if (!normalized.userId && context.userId) {\n normalized.userId = context.userId;\n }\n\n return normalized;\n}\n\n/**\n * Convert EventPayload to EmitterPayload format for AnalyticsManager.\n * Explicitly maps fields to ensure type safety.\n */\nfunction toEmitterPayload(event: EventPayload): EmitterPayload {\n // Explicitly construct the EmitterPayload from EventPayload fields\n // to maintain type safety and avoid unsafe double casts\n const basePayload = {\n userId: event.userId,\n anonymousId: event.anonymousId,\n timestamp: event.timestamp,\n context: event.context,\n messageId: event.messageId,\n };\n\n switch (event.type) {\n case 'track':\n return {\n type: 'track',\n event: event.event,\n properties: event.properties,\n ...basePayload,\n } as EmitterPayload;\n case 'identify':\n return {\n type: 'identify',\n traits: event.traits,\n ...basePayload,\n } as EmitterPayload;\n case 'page':\n return {\n type: 'page',\n name: event.name,\n category: event.category,\n properties: event.properties,\n ...basePayload,\n } as EmitterPayload;\n case 'screen':\n return {\n type: 'screen',\n name: event.name,\n category: event.category,\n properties: event.properties,\n ...basePayload,\n } as EmitterPayload;\n case 'group':\n return {\n type: 'group',\n groupId: event.groupId,\n traits: event.traits,\n ...basePayload,\n } as EmitterPayload;\n case 'alias':\n return {\n type: 'alias',\n previousId: event.previousId,\n ...basePayload,\n } as EmitterPayload;\n default:\n // This should never happen due to discriminated union validation\n throw new Error(`Unknown event type: ${(event as { type: string }).type}`);\n }\n}\n\n// =============================================================================\n// Ingestion Service Class\n// =============================================================================\n\n/**\n * Event Ingestion Service.\n *\n * Handles validation, normalization, and forwarding of analytics events.\n * Designed for high-volume ingestion with batching and rate limiting support.\n *\n * @example\n * ```typescript\n * const service = new IngestionService(analyticsManager);\n *\n * const result = await service.ingest(requestBody, {\n * source: 'web-app',\n * tenantId: 'tenant-123',\n * userId: 'user-456',\n * });\n *\n * if (result.success) {\n * console.log(`Accepted ${result.accepted} events`);\n * }\n * ```\n */\nexport class IngestionService {\n private readonly config: Required<IngestionServiceConfig>;\n\n constructor(\n private readonly analyticsManager: AnalyticsManager,\n config: IngestionServiceConfig = {},\n ) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Parse and validate the ingestion request payload.\n *\n * @param payload - Raw request payload (parsed JSON)\n * @returns Parsed and validated request, or error response\n */\n parseRequest(\n payload: unknown,\n ): { success: true; data: IngestionRequest } | { success: false; error: IngestionErrorResponse } {\n // Validate payload size (rough estimate)\n try {\n const payloadSize = JSON.stringify(payload).length;\n if (payloadSize > this.config.maxPayloadSize) {\n return {\n success: false,\n error: {\n success: false,\n code: 'PAYLOAD_TOO_LARGE',\n error: `Payload size ${payloadSize} exceeds maximum ${this.config.maxPayloadSize} bytes`,\n },\n };\n }\n } catch {\n return {\n success: false,\n error: {\n success: false,\n code: 'INVALID_JSON',\n error: 'Failed to serialize payload for size check',\n },\n };\n }\n\n // Validate against schema\n const result = IngestionRequestSchema.safeParse(payload);\n\n if (!result.success) {\n const fieldErrors = result.error.issues.map(err => ({\n path: err.path.map(p => (typeof p === 'symbol' ? String(p) : p)),\n message: err.message,\n }));\n\n return {\n success: false,\n error: {\n success: false,\n code: 'VALIDATION_ERROR',\n error: 'Request validation failed',\n fieldErrors,\n },\n };\n }\n\n // Check batch size\n if (isBatchRequest(result.data) && result.data.batch.length > this.config.maxBatchSize) {\n return {\n success: false,\n error: {\n success: false,\n code: 'BATCH_TOO_LARGE',\n error: `Batch size ${result.data.batch.length} exceeds maximum ${this.config.maxBatchSize}`,\n },\n };\n }\n\n return { success: true, data: result.data };\n }\n\n /**\n * Ingest events from a validated request.\n *\n * @param request - Validated ingestion request\n * @param context - Ingestion context from the caller\n * @returns Ingestion response with per-event results\n */\n async ingest(\n request: IngestionRequest,\n context: IngestionContext,\n ): Promise<IngestionSuccessResponse> {\n const startTime = process.hrtime.bigint();\n const receivedAt = getCurrentTimestamp();\n\n // Extract events from request (single or batch)\n const events: EventPayload[] = isBatchRequest(request) ? request.batch : [request];\n\n const results: EventResult[] = [];\n const metrics: IngestionMetrics = {\n totalReceived: events.length,\n accepted: 0,\n rejected: 0,\n byType: {},\n processingTimeMs: 0,\n };\n\n // Process events\n const processedEvents: EmitterPayload[] = [];\n\n for (const event of events) {\n const eventId = generateUUID();\n\n try {\n // Validate event name for track events\n if (event.type === 'track') {\n const validation = validateEventName(event.event);\n if (!validation.valid) {\n results.push({\n id: eventId,\n messageId: event.messageId,\n type: event.type,\n status: 'rejected',\n error: `Invalid event name: ${validation.reason}`,\n });\n metrics.rejected++;\n continue;\n }\n }\n\n // Normalize event\n const normalized = normalizeEvent(event, context, receivedAt);\n\n // Sanitize properties\n if ('properties' in normalized && normalized.properties) {\n const sanitized = sanitizeProperties(normalized.properties, {\n stripPII: this.config.stripPII,\n stripHTML: this.config.stripHTML,\n allowDangerousKeys: false,\n });\n (normalized as { properties: Record<string, unknown> }).properties = sanitized.data;\n\n if (sanitized.warnings.length > 0) {\n logDebug('Event properties sanitized', {\n eventId,\n warnings: sanitized.warnings,\n });\n }\n }\n\n if ('traits' in normalized && normalized.traits) {\n const sanitized = sanitizeProperties(normalized.traits, {\n stripPII: this.config.stripPII,\n stripHTML: this.config.stripHTML,\n allowDangerousKeys: false,\n });\n (normalized as { traits: Record<string, unknown> }).traits = sanitized.data;\n }\n\n // Convert to emitter payload and queue for processing\n const emitterPayload = toEmitterPayload(normalized);\n processedEvents.push(emitterPayload);\n\n results.push({\n id: eventId,\n messageId: normalized.messageId,\n type: event.type,\n status: 'accepted',\n });\n\n metrics.accepted++;\n metrics.byType[event.type] = (metrics.byType[event.type] ?? 0) + 1;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n\n results.push({\n id: eventId,\n messageId: event.messageId,\n type: event.type,\n status: 'rejected',\n error: errorMessage,\n });\n\n metrics.rejected++;\n\n logWarn('Event processing failed', {\n eventId,\n type: event.type,\n error: errorMessage,\n });\n }\n }\n\n // Forward accepted events to analytics manager (non-blocking)\n // Note: This provides at-most-once delivery semantics. Events may be lost\n // if the analytics manager fails. For guaranteed delivery, consider using\n // a durable queue (Redis, SQS) for critical events.\n if (processedEvents.length > 0) {\n // Use emitBatch for efficient processing\n void (async () => {\n try {\n await this.analyticsManager.emitBatch(processedEvents, {\n timeout: this.config.eventTimeout,\n concurrency: this.config.batchConcurrency,\n failFast: false, // Process all events even if some fail\n });\n } catch (error) {\n logError('Failed to emit events to analytics manager', {\n error: error instanceof Error ? error.message : 'Unknown error',\n eventCount: processedEvents.length,\n traceId: context.traceId,\n });\n }\n })();\n }\n\n // Calculate processing time\n const endTime = process.hrtime.bigint();\n metrics.processingTimeMs = Number(endTime - startTime) / 1_000_000;\n\n // Log metrics\n logInfo('Event ingestion completed', {\n traceId: context.traceId,\n source: context.source,\n tenantId: context.tenantId,\n totalReceived: metrics.totalReceived,\n accepted: metrics.accepted,\n rejected: metrics.rejected,\n processingTimeMs: metrics.processingTimeMs.toFixed(2),\n byType: metrics.byType,\n });\n\n return {\n success: true,\n accepted: metrics.accepted,\n rejected: metrics.rejected,\n results,\n receivedAt,\n };\n }\n\n /**\n * Process a raw request body through parsing and ingestion.\n *\n * Convenience method that combines parseRequest and ingest.\n *\n * @param payload - Raw request payload\n * @param context - Ingestion context\n * @returns Ingestion response (success or error)\n */\n async processRequest(\n payload: unknown,\n context: IngestionContext,\n ): Promise<IngestionSuccessResponse | IngestionErrorResponse> {\n const parseResult = this.parseRequest(payload);\n\n if (!parseResult.success) {\n return parseResult.error;\n }\n\n return this.ingest(parseResult.data, context);\n }\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Create an ingestion service instance.\n *\n * @param analyticsManager - Initialized AnalyticsManager instance\n * @param config - Optional service configuration\n * @returns Configured IngestionService instance\n *\n * @example\n * ```typescript\n * import { createServerAnalytics } from '@od-oneapp/analytics/server';\n * import { createIngestionService } from '@od-oneapp/analytics/server';\n *\n * const analytics = await createServerAnalytics(config);\n * const ingestionService = createIngestionService(analytics);\n * ```\n */\nexport function createIngestionService(\n analyticsManager: AnalyticsManager,\n config?: IngestionServiceConfig,\n): IngestionService {\n return new IngestionService(analyticsManager, config);\n}\n\n/**\n * Validate a single event payload.\n *\n * Useful for pre-validation before queuing events.\n *\n * @param payload - Event payload to validate\n * @returns Validation result with parsed data or errors\n */\nexport function validateEventPayload(\n payload: unknown,\n): { success: true; data: EventPayload } | { success: false; errors: string[] } {\n const result = EventPayloadSchema.safeParse(payload);\n\n if (!result.success) {\n return {\n success: false,\n errors: result.error.issues.map(\n e => `${e.path.map(p => (typeof p === 'symbol' ? String(p) : p)).join('.')}: ${e.message}`,\n ),\n };\n }\n\n return { success: true, data: result.data };\n}\n\n/**\n * Validate a batch of event payloads.\n *\n * @param payloads - Array of event payloads to validate\n * @returns Validation result with parsed data or errors\n */\nexport function validateBatchPayload(\n payloads: unknown[],\n): { success: true; data: EventPayload[] } | { success: false; errors: string[] } {\n const result = BatchEventRequestSchema.safeParse({ batch: payloads });\n\n if (!result.success) {\n return {\n success: false,\n errors: result.error.issues.map(\n e => `${e.path.map(p => (typeof p === 'symbol' ? String(p) : p)).join('.')}: ${e.message}`,\n ),\n };\n }\n\n return { success: true, data: result.data.batch };\n}\n"],"mappings":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.mjs","names":[],"sources":["../src/client/manager.ts","../src/shared/utils/validation-client.ts"],"sourcesContent":["/**\n * @fileoverview Client Analytics Manager\n *\n * Client-side analytics manager with static provider registry. Provides\n * factory functions for creating client analytics instances with support\n * for multiple providers (Console, HTTP, Segment, Vercel).\n *\n * **Providers Supported**:\n * - `console`: Console logging provider (development/debugging)\n * - `http`: HTTP provider for remote ingestion (oneapp-api)\n * - `segment`: Segment.io analytics provider\n * - `vercel`: Vercel Analytics provider\n *\n * @module @od-oneapp/analytics/client/manager\n */\n\nimport { SegmentClientProvider } from '@integrations/segment/analytics-provider/client';\nimport { VercelClientProvider } from '@integrations/vercel/analytics-provider/client';\n\nimport { ConsoleProvider } from '../providers/console/client';\nimport { HttpClientProvider } from '../providers/http/client';\nimport { createAnalyticsManager } from '../shared/utils/manager';\n\nimport type { AnalyticsConfig, AnalyticsManager, ProviderRegistry } from '../shared/types/types';\n\n// Static provider registry for client environments\nconst CLIENT_PROVIDERS: ProviderRegistry = {\n console: config => new ConsoleProvider(config),\n http: config => new HttpClientProvider(config),\n segment: config => new SegmentClientProvider(config),\n vercel: config => new VercelClientProvider(config),\n};\n\n/**\n * Create and initialize a client analytics instance\n * This is the primary way to create analytics for client-side applications\n *\n * @example\n * ```typescript\n * const analytics = await createClientAnalytics({\n * providers: {\n * http: { endpoint: '/api/v1/ingest' },\n * },\n * });\n * await analytics.track('Button Clicked', { variant: 'primary' });\n * ```\n * @param config - Analytics configuration including providers and settings\n * @returns Promise resolving to initialized analytics manager\n */\nexport async function createClientAnalytics(config: AnalyticsConfig): Promise<AnalyticsManager> {\n const manager = createAnalyticsManager(config, CLIENT_PROVIDERS);\n await manager.initialize();\n return manager;\n}\n\n/**\n * Create a client analytics instance without initializing\n * Useful when you need to control initialization timing\n *\n * @example\n * ```typescript\n * const analytics = createClientAnalyticsUninitialized(config);\n * // later inside a lazy effect\n * await analytics.initialize();\n * ```\n * @param config - Analytics configuration including providers and settings\n * @returns Uninitialized analytics manager instance\n */\nexport function createClientAnalyticsUninitialized(config: AnalyticsConfig): AnalyticsManager {\n return createAnalyticsManager(config, CLIENT_PROVIDERS);\n}\n","/**\n * @fileoverview Client-safe validation utilities for analytics configuration\n * Client-safe validation utilities for analytics configuration\n * This file contains only validation functions that are safe to use in browser environments\n */\n\nimport { PROVIDER_REQUIREMENTS } from './config';\n\nimport type { AnalyticsConfig, ProviderConfig } from '../types/types';\n\ninterface ValidationError {\n field: string;\n message: string;\n provider: string;\n}\n\ninterface ValidationResult {\n errors: ValidationError[];\n isValid: boolean;\n warnings: string[];\n}\n\n/**\n * Comprehensive configuration validation (client-safe version)\n * Accepts unknown input for defensive validation of potentially malformed configs\n */\nexport function validateAnalyticsConfig(config: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n const warnings: string[] = [];\n\n // Check if config exists and is an object\n if (!config || typeof config !== 'object') {\n errors.push({\n provider: 'global',\n field: 'config',\n message: 'Analytics configuration is required and must be an object',\n });\n return { isValid: false, errors, warnings };\n }\n\n const typedConfig = config as AnalyticsConfig;\n\n // Check if providers object exists\n if (!typedConfig.providers || typeof typedConfig.providers !== 'object') {\n errors.push({\n provider: 'global',\n field: 'providers',\n message: 'Providers configuration is required and must be an object',\n });\n return { isValid: false, errors, warnings };\n }\n\n // Check if at least one provider is configured\n const providerCount = Object.keys(typedConfig.providers).length;\n if (providerCount === 0) {\n warnings.push('No providers configured. Analytics will not track any events.');\n }\n\n // Validate each provider\n for (const [providerName, providerConfig] of Object.entries(typedConfig.providers)) {\n const providerErrors = validateProvider(providerName, providerConfig);\n errors.push(...providerErrors);\n }\n\n // Environment-specific warnings\n if (typedConfig.providers.mixpanel) {\n warnings.push(\n 'Mixpanel provider configured on client-side. Consider using server-side for better performance.',\n );\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/**\n * Validate a single provider configuration\n */\nexport function validateProvider(providerName: string, config: ProviderConfig): ValidationError[] {\n const errors: ValidationError[] = [];\n\n // Check if provider is known\n const knownProviders = ['segment', 'posthog', 'vercel', 'console', 'mixpanel'];\n if (!knownProviders.includes(providerName)) {\n errors.push({\n provider: providerName,\n field: 'name',\n message: `Unknown provider '${providerName}'. Known providers: ${knownProviders.join(', ')}`,\n });\n return errors;\n }\n\n // Check required fields\n const requiredFields = PROVIDER_REQUIREMENTS[providerName] ?? [];\n for (const field of requiredFields) {\n const value = config[field as keyof ProviderConfig];\n\n if (!value) {\n errors.push({\n provider: providerName,\n field,\n message: `Required field '${field}' is missing for provider '${providerName}'`,\n });\n } else if (typeof value === 'string' && value.trim() === '') {\n errors.push({\n provider: providerName,\n field,\n message: `Required field '${field}' cannot be empty for provider '${providerName}'`,\n });\n }\n }\n\n // Provider-specific validation\n switch (providerName) {\n case 'segment':\n if (config.writeKey && !isValidSegmentWriteKey(config.writeKey)) {\n errors.push({\n provider: providerName,\n field: 'writeKey',\n message: 'Segment writeKey appears to be invalid format',\n });\n }\n break;\n\n case 'posthog':\n if (config.apiKey && !isValidPostHogApiKey(config.apiKey)) {\n errors.push({\n provider: providerName,\n field: 'apiKey',\n message: 'PostHog apiKey appears to be invalid format',\n });\n }\n break;\n }\n\n return errors;\n}\n\n/**\n * Helper functions for format validation\n */\nfunction isValidSegmentWriteKey(writeKey: string): boolean {\n // Segment write keys are typically 32 characters, alphanumeric\n return /^[\\dA-Za-z]{20,40}$/.test(writeKey);\n}\n\nfunction isValidPostHogApiKey(apiKey: string): boolean {\n // PostHog API keys start with 'phc_' followed by alphanumeric characters\n return /^phc_[\\dA-Za-z]{43}$/.test(apiKey);\n}\n\n/**\n * Utility to validate configuration (client-safe version without throwing)\n * Returns validation result instead of throwing errors\n */\nexport function validateConfig(config: AnalyticsConfig): ValidationResult {\n return validateAnalyticsConfig(config);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,MAAM,mBAAqC;CACzC,UAAS,WAAU,IAAI,gBAAgB,OAAO;CAC9C,OAAM,WAAU,IAAI,mBAAmB,OAAO;CAC9C,UAAS,WAAU,IAAI,sBAAsB,OAAO;CACpD,SAAQ,WAAU,IAAI,qBAAqB,OAAO;CACnD;;;;;;;;;;;;;;;;;AAkBD,eAAsB,sBAAsB,QAAoD;CAC9F,MAAM,UAAU,uBAAuB,QAAQ,iBAAiB;AAChE,OAAM,QAAQ,YAAY;AAC1B,QAAO;;;;;;;;;;;;;;;AAgBT,SAAgB,mCAAmC,QAA2C;AAC5F,QAAO,uBAAuB,QAAQ,iBAAiB;;;;;;;;;;;;;;AC3CzD,SAAgB,wBAAwB,QAAmC;CACzE,MAAM,SAA4B,EAAE;CACpC,MAAM,WAAqB,EAAE;AAG7B,KAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,SAAO,KAAK;GACV,UAAU;GACV,OAAO;GACP,SAAS;GACV,CAAC;AACF,SAAO;GAAE,SAAS;GAAO;GAAQ;GAAU;;CAG7C,MAAM,cAAc;AAGpB,KAAI,CAAC,YAAY,aAAa,OAAO,YAAY,cAAc,UAAU;AACvE,SAAO,KAAK;GACV,UAAU;GACV,OAAO;GACP,SAAS;GACV,CAAC;AACF,SAAO;GAAE,SAAS;GAAO;GAAQ;GAAU;;AAK7C,KADsB,OAAO,KAAK,YAAY,UAAU,CAAC,WACnC,EACpB,UAAS,KAAK,gEAAgE;AAIhF,MAAK,MAAM,CAAC,cAAc,mBAAmB,OAAO,QAAQ,YAAY,UAAU,EAAE;EAClF,MAAM,iBAAiB,iBAAiB,cAAc,eAAe;AACrE,SAAO,KAAK,GAAG,eAAe;;AAIhC,KAAI,YAAY,UAAU,SACxB,UAAS,KACP,kGACD;AAGH,QAAO;EACL,SAAS,OAAO,WAAW;EAC3B;EACA;EACD;;;;;AAMH,SAAgB,iBAAiB,cAAsB,QAA2C;CAChG,MAAM,SAA4B,EAAE;CAGpC,MAAM,iBAAiB;EAAC;EAAW;EAAW;EAAU;EAAW;EAAW;AAC9E,KAAI,CAAC,eAAe,SAAS,aAAa,EAAE;AAC1C,SAAO,KAAK;GACV,UAAU;GACV,OAAO;GACP,SAAS,qBAAqB,aAAa,sBAAsB,eAAe,KAAK,KAAK;GAC3F,CAAC;AACF,SAAO;;CAIT,MAAM,iBAAiB,sBAAsB,iBAAiB,EAAE;AAChE,MAAK,MAAM,SAAS,gBAAgB;EAClC,MAAM,QAAQ,OAAO;AAErB,MAAI,CAAC,MACH,QAAO,KAAK;GACV,UAAU;GACV;GACA,SAAS,mBAAmB,MAAM,6BAA6B,aAAa;GAC7E,CAAC;WACO,OAAO,UAAU,YAAY,MAAM,MAAM,KAAK,GACvD,QAAO,KAAK;GACV,UAAU;GACV;GACA,SAAS,mBAAmB,MAAM,kCAAkC,aAAa;GAClF,CAAC;;AAKN,SAAQ,cAAR;EACE,KAAK;AACH,OAAI,OAAO,YAAY,CAAC,uBAAuB,OAAO,SAAS,CAC7D,QAAO,KAAK;IACV,UAAU;IACV,OAAO;IACP,SAAS;IACV,CAAC;AAEJ;EAEF,KAAK;AACH,OAAI,OAAO,UAAU,CAAC,qBAAqB,OAAO,OAAO,CACvD,QAAO,KAAK;IACV,UAAU;IACV,OAAO;IACP,SAAS;IACV,CAAC;AAEJ;;AAGJ,QAAO;;;;;AAMT,SAAS,uBAAuB,UAA2B;AAEzD,QAAO,sBAAsB,KAAK,SAAS;;AAG7C,SAAS,qBAAqB,QAAyB;AAErD,QAAO,uBAAuB,KAAK,OAAO"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"service-cYtBBL8x.mjs","names":[],"sources":["../src/server/manager.ts","../src/shared/utils/validation.ts","../src/shared/ingestion/schemas.ts","../src/shared/ingestion/service.ts"],"sourcesContent":["/**\n * @fileoverview Server analytics manager with static provider registry\n * Server analytics manager with static provider registry\n */\n\nimport { SegmentServerProvider } from '@integrations/segment/analytics-provider/server';\nimport { VercelServerProvider } from '@integrations/vercel/analytics-provider/server';\n\nimport { ConsoleProvider } from '../providers/console/server';\nimport { HttpServerProvider } from '../providers/http/server';\nimport { createAnalyticsManager } from '../shared/utils/manager';\n\nimport type { AnalyticsConfig, AnalyticsManager, ProviderRegistry } from '../shared/types/types';\n\n// Static provider registry for server environments\nconst SERVER_PROVIDERS: ProviderRegistry = {\n console: config => new ConsoleProvider(config),\n http: config => new HttpServerProvider(config),\n segment: config => new SegmentServerProvider(config),\n vercel: config => new VercelServerProvider(config),\n};\n\n/**\n * Create and initialize a server analytics instance\n * This is the primary way to create analytics for server-side applications\n *\n * @example\n * ```typescript\n * const analytics = await createServerAnalytics({\n * providers: {\n * segment: { writeKey: process.env.SEGMENT_KEY! },\n * },\n * });\n * await analytics.page('/admin', { title: 'Admin Dashboard' });\n * ```\n * @param config - Analytics configuration including providers and settings\n * @returns Promise resolving to initialized analytics manager\n */\nexport async function createServerAnalytics(config: AnalyticsConfig): Promise<AnalyticsManager> {\n const manager = createAnalyticsManager(config, SERVER_PROVIDERS);\n await manager.initialize();\n return manager;\n}\n\n/**\n * Create a server analytics instance without initializing\n * Useful when you need to control initialization timing\n *\n * @example\n * ```typescript\n * const analytics = createServerAnalyticsUninitialized(config);\n * if (shouldEmit) {\n * await analytics.initialize();\n * await analytics.track('CRON Completed');\n * }\n * ```\n * @param config - Analytics configuration including providers and settings\n * @returns Uninitialized analytics manager instance\n */\nexport function createServerAnalyticsUninitialized(config: AnalyticsConfig): AnalyticsManager {\n return createAnalyticsManager(config, SERVER_PROVIDERS);\n}\n","/**\n * @fileoverview Validation utilities for analytics configuration\n *\n * This module provides comprehensive validation for analytics configurations,\n * including provider validation, environment-specific checks, and helpful\n * warnings for common misconfigurations.\n *\n * **Features**:\n * - Configuration structure validation\n * - Provider-specific field validation\n * - Environment variable validation\n * - Environment-specific warnings\n * - Detailed error reporting\n *\n * @module @od-oneapp/analytics/shared/utils/validation\n */\n\nimport { logError, logInfo, logWarn } from '@repo/shared/logger';\n\nimport { PROVIDER_REQUIREMENTS } from './config';\n\nimport type { AnalyticsConfig, ProviderConfig } from '../types/types';\n\n/**\n * Validation error structure.\n *\n * Represents a single validation error with field, message, and provider context.\n */\nexport interface ValidationError {\n /** Field name that failed validation */\n field: string;\n /** Human-readable error message */\n message: string;\n /** Provider name (or 'global' for config-level errors) */\n provider: string;\n}\n\n/**\n * Validation result structure.\n *\n * Contains validation errors, warnings, and overall validity status.\n */\nexport interface ValidationResult {\n /** Array of validation errors */\n errors: ValidationError[];\n /** Whether the configuration is valid (no errors) */\n isValid: boolean;\n /** Array of warning messages */\n warnings: string[];\n}\n\n/**\n * Comprehensive configuration validation.\n *\n * Validates the entire analytics configuration structure, including:\n * - Configuration object structure\n * - Providers object existence\n * - Individual provider configurations\n * - Environment-specific warnings\n *\n * Accepts `unknown` input for defensive validation of potentially malformed configs.\n *\n * @param {unknown} config - Analytics configuration to validate\n * @returns {ValidationResult} Validation result with errors, warnings, and validity status\n *\n * @example\n * ```typescript\n * const result = validateAnalyticsConfig(config);\n * if (!result.isValid) {\n * console.error('Validation errors:', result.errors);\n * }\n * if (result.warnings.length > 0) {\n * console.warn('Warnings:', result.warnings);\n * }\n * ```\n */\nexport function validateAnalyticsConfig(config: unknown): ValidationResult {\n const errors: ValidationError[] = [];\n const warnings: string[] = [];\n\n // Check if config exists and is an object\n if (!config || typeof config !== 'object') {\n errors.push({\n provider: 'global',\n field: 'config',\n message: 'Analytics configuration is required and must be an object',\n });\n return { isValid: false, errors, warnings };\n }\n\n const typedConfig = config as AnalyticsConfig;\n\n // Check if providers object exists\n if (!typedConfig.providers || typeof typedConfig.providers !== 'object') {\n errors.push({\n provider: 'global',\n field: 'providers',\n message: 'Providers configuration is required and must be an object',\n });\n return { isValid: false, errors, warnings };\n }\n\n // Check if at least one provider is configured\n const providerCount = Object.keys(typedConfig.providers).length;\n if (providerCount === 0) {\n warnings.push('No providers configured. Analytics will not track any events.');\n }\n\n // Validate each provider\n for (const [providerName, providerConfig] of Object.entries(typedConfig.providers)) {\n const providerErrors = validateProvider(providerName, providerConfig);\n errors.push(...providerErrors);\n }\n\n // Environment-specific warnings\n const isBrowser = typeof window !== 'undefined';\n\n // Mixpanel warning for client-side usage\n if (isBrowser && typedConfig.providers.mixpanel) {\n warnings.push(\n 'Mixpanel provider configured on client-side. Consider using server-side for better performance.',\n );\n }\n\n if (!isBrowser && typedConfig.providers.vercel) {\n warnings.push(\n 'Vercel Analytics has limited server-side support. Consider using client-side for better features.',\n );\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/**\n * Validate a single provider configuration.\n *\n * Checks that the provider is known and that all required fields are present.\n *\n * @param {string} providerName - Name of the provider to validate\n * @param {ProviderConfig} config - Provider configuration to validate\n * @returns {ValidationError[]} Array of validation errors (empty if valid)\n *\n * @example\n * ```typescript\n * const errors = validateProvider('posthog', { apiKey: 'phc_xxx' });\n * if (errors.length > 0) {\n * console.error('Provider errors:', errors);\n * }\n * ```\n */\nexport function validateProvider(providerName: string, config: ProviderConfig): ValidationError[] {\n const errors: ValidationError[] = [];\n\n // Check if provider is known\n const knownProviders = ['segment', 'posthog', 'vercel', 'console', 'mixpanel'];\n if (!knownProviders.includes(providerName)) {\n errors.push({\n provider: providerName,\n field: 'name',\n message: `Unknown provider '${providerName}'. Known providers: ${knownProviders.join(', ')}`,\n });\n return errors;\n }\n\n // Check required fields\n const requiredFields = PROVIDER_REQUIREMENTS[providerName] ?? [];\n for (const field of requiredFields) {\n const value = config[field as keyof ProviderConfig];\n\n if (!value) {\n errors.push({\n provider: providerName,\n field,\n message: `Required field '${field}' is missing for provider '${providerName}'`,\n });\n } else if (typeof value === 'string' && value.trim() === '') {\n errors.push({\n provider: providerName,\n field,\n message: `Required field '${field}' cannot be empty for provider '${providerName}'`,\n });\n }\n }\n\n // Provider-specific validation\n switch (providerName) {\n case 'segment':\n if (config.writeKey && !isValidSegmentWriteKey(config.writeKey)) {\n errors.push({\n provider: providerName,\n field: 'writeKey',\n message: 'Segment writeKey appears to be invalid format',\n });\n }\n break;\n\n case 'posthog':\n if (config.apiKey && !isValidPostHogApiKey(config.apiKey)) {\n errors.push({\n provider: providerName,\n field: 'apiKey',\n message: 'PostHog apiKey appears to be invalid format',\n });\n }\n break;\n }\n\n return errors;\n}\n\n/**\n * Validate environment variables for analytics.\n *\n * Checks common analytics environment variables for:\n * - Empty values\n * - Placeholder text\n * - Development environment warnings\n *\n * @returns {ValidationResult} Validation result with warnings about environment variables\n *\n * @example\n * ```typescript\n * const result = validateEnvironmentVariables();\n * if (result.warnings.length > 0) {\n * console.warn('Environment warnings:', result.warnings);\n * }\n * ```\n */\nexport function validateEnvironmentVariables(): ValidationResult {\n const errors: ValidationError[] = [];\n const warnings: string[] = [];\n\n // Check for common environment variables\n const envVars = {\n POSTHOG_API_KEY: process.env.POSTHOG_API_KEY,\n SEGMENT_WRITE_KEY: process.env.SEGMENT_WRITE_KEY,\n };\n\n for (const [varName, value] of Object.entries(envVars)) {\n if (value && typeof value === 'string') {\n if (value.trim() === '') {\n warnings.push(`Environment variable ${varName} is set but empty`);\n } else if (value.includes('your-') || value.includes('paste-')) {\n warnings.push(`Environment variable ${varName} appears to contain placeholder text`);\n }\n }\n }\n\n // Warn about development environment\n if (\n process.env.NODE_ENV === 'development' &&\n !envVars.SEGMENT_WRITE_KEY &&\n !envVars.POSTHOG_API_KEY\n ) {\n warnings.push(\n 'No analytics environment variables detected in development. Consider using console provider for debugging.',\n );\n }\n\n return {\n isValid: errors.length === 0,\n errors,\n warnings,\n };\n}\n\n/**\n * Validates Segment write key format.\n *\n * Checks that the write key matches Segment's expected format:\n * - Exactly 32 alphanumeric characters\n * - Not a placeholder value\n *\n * @param {string} writeKey - Write key to validate\n * @returns {boolean} `true` if valid, `false` otherwise\n *\n * @internal\n */\nfunction isValidSegmentWriteKey(writeKey: string): boolean {\n // Segment write keys are exactly 32 characters, alphanumeric\n if (!/^[\\dA-Za-z]{32}$/.test(writeKey)) {\n return false;\n }\n\n // Check for common placeholder patterns\n const placeholders = ['your-write-key', 'paste-key-here', 'xxxxxxxx', 'example'];\n if (placeholders.some(p => writeKey.toLowerCase().includes(p.toLowerCase()))) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Validates PostHog API key format.\n *\n * Checks that the API key matches PostHog's expected format:\n * - Starts with `phc_` prefix\n * - Followed by 43 alphanumeric/dash/underscore characters\n * - Not a placeholder value\n *\n * @param {string} apiKey - API key to validate\n * @returns {boolean} `true` if valid, `false` otherwise\n *\n * @internal\n */\nfunction isValidPostHogApiKey(apiKey: string): boolean {\n // PostHog keys: phc_ prefix + 43 alphanumeric/dash/underscore characters\n if (!/^phc_[\\w-]{43}$/.test(apiKey)) {\n return false;\n }\n\n // Check for placeholder patterns\n const placeholders = ['your-api-key', 'paste-key-here', 'xxxxxxxx', 'example'];\n if (placeholders.some(p => apiKey.toLowerCase().includes(p.toLowerCase()))) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Utility to throw validation errors (for strict validation).\n *\n * Validates the configuration and throws an error if validation fails.\n * Useful for ensuring configuration is valid before using analytics.\n *\n * @param {AnalyticsConfig} config - Analytics configuration to validate\n * @throws {Error} If configuration validation fails\n *\n * @example\n * ```typescript\n * try {\n * validateConfigOrThrow(config);\n * // Configuration is valid, proceed\n * } catch (error) {\n * console.error('Invalid config:', error.message);\n * }\n * ```\n */\nexport function validateConfigOrThrow(config: AnalyticsConfig): void {\n const result = validateAnalyticsConfig(config);\n\n if (!result.isValid) {\n const errorMessages = result.errors\n .map(error => `${error.provider}.${error.field}: ${error.message}`)\n .join('\\n');\n\n throw new Error(`Analytics configuration validation failed:\\n${errorMessages}`);\n }\n\n // Log warnings but don't throw\n if (result.warnings.length > 0 && config.onError) {\n config.onError(new Error('Analytics configuration warnings'), {\n provider: 'analytics',\n method: 'validateConfig',\n warnings: result.warnings,\n });\n }\n}\n\n/**\n * Development helper to check configuration.\n *\n * Logs configuration details and validation results for debugging.\n * Only useful in development environments.\n *\n * @param {AnalyticsConfig} config - Analytics configuration to debug\n * @returns {Promise<void>} Promise that resolves when debugging is complete\n *\n * @example\n * ```typescript\n * if (process.env.NODE_ENV === 'development') {\n * await debugConfig(config);\n * }\n * ```\n */\nexport async function debugConfig(config: AnalyticsConfig): Promise<void> {\n const result = validateAnalyticsConfig(config);\n\n logInfo('Analytics Configuration Debug', {\n config,\n validationResult: result,\n });\n\n if (result.errors.length > 0) {\n logError('Analytics configuration errors: Validation failed', {\n errors: result.errors,\n });\n }\n\n if (result.warnings.length > 0) {\n logWarn('Analytics configuration warnings', { warnings: result.warnings });\n }\n}\n","/**\n * @fileoverview Event Ingestion Schemas and Validation\n *\n * Defines Zod schemas for validating event ingestion payloads. These schemas\n * ensure type-safe, secure event ingestion with proper validation.\n *\n * **Key Features**:\n * - CloudEvents-style fields for future interoperability\n * - Support for single events and batched arrays\n * - Strict validation of required fields\n * - Typed extensions per event category\n *\n * @module @od-oneapp/analytics/shared/ingestion/schemas\n */\n\nimport { z } from 'zod';\n\n// =============================================================================\n// Base Schemas\n// =============================================================================\n\n/**\n * Property value schema - safe, serializable values only.\n */\nexport const PropertyValueSchema = z.union([\n z.string(),\n z.number(),\n z.boolean(),\n z.null(),\n z.date(), // Accept Date objects only; ISO strings are already handled as strings\n]);\n\n/**\n * Property object schema with nested structure support.\n */\nexport const PropertyObjectSchema: z.ZodType<Record<string, unknown>> = z.record(\n z.string(),\n z.lazy(() =>\n z.union([PropertyValueSchema, z.array(PropertyValueSchema), z.record(z.string(), z.unknown())]),\n ),\n);\n\n/**\n * Emitter context schema - contextual information about the environment.\n */\nexport const EmitterContextSchema = z\n .object({\n /** Application information */\n app: z\n .object({\n name: z.string().optional(),\n version: z.string().optional(),\n build: z.string().optional(),\n namespace: z.string().optional(),\n })\n .optional(),\n\n /** Campaign/UTM information */\n campaign: z\n .object({\n name: z.string().optional(),\n source: z.string().optional(),\n medium: z.string().optional(),\n term: z.string().optional(),\n content: z.string().optional(),\n })\n .passthrough()\n .optional(),\n\n /** Device information */\n device: z\n .object({\n id: z.string().optional(),\n manufacturer: z.string().optional(),\n model: z.string().optional(),\n name: z.string().optional(),\n type: z.string().optional(),\n version: z.string().optional(),\n })\n .optional(),\n\n /** User's IP address */\n ip: z.string().optional(),\n\n /** Library making the request */\n library: z\n .object({\n name: z.string(),\n version: z.string(),\n })\n .optional(),\n\n /** User's locale (e.g., 'en-US') */\n locale: z.string().optional(),\n\n /** Network information */\n network: z\n .object({\n bluetooth: z.boolean().optional(),\n carrier: z.string().optional(),\n cellular: z.boolean().optional(),\n wifi: z.boolean().optional(),\n })\n .optional(),\n\n /** OS information */\n os: z\n .object({\n name: z.string().optional(),\n version: z.string().optional(),\n })\n .optional(),\n\n /** Page information */\n page: z\n .object({\n path: z.string().optional(),\n // Use permissive string validation for referrer/url since real-world values\n // may be malformed, truncated, or non-standard (e.g., data: URIs, about:blank)\n referrer: z.string().optional(),\n search: z.string().optional(),\n title: z.string().optional(),\n url: z.string().optional(),\n })\n .optional(),\n\n /** Screen information */\n screen: z\n .object({\n density: z.number().optional(),\n height: z.number().int().positive().optional(),\n width: z.number().int().positive().optional(),\n })\n .optional(),\n\n /** User's timezone (tzdata string) */\n timezone: z.string().optional(),\n\n /** Group/account ID */\n groupId: z.string().optional(),\n\n /** User agent string */\n userAgent: z.string().optional(),\n\n /** Channel where request originated */\n channel: z.enum(['server', 'browser', 'mobile', 'api']).optional(),\n\n /** Location context */\n location: z\n .object({\n city: z.string().optional(),\n country: z.string().optional(),\n latitude: z.number().optional(),\n longitude: z.number().optional(),\n region: z.string().optional(),\n })\n .optional(),\n })\n .passthrough();\n\n// =============================================================================\n// Event Type Schemas\n// =============================================================================\n\n/**\n * Base payload schema - common fields for all event types.\n */\nconst BasePayloadSchema = z\n .object({\n /** Anonymous ID if user is not identified */\n anonymousId: z.string().max(255).optional(),\n\n /** User ID (at least one of userId or anonymousId required) */\n userId: z.string().max(255).optional(),\n\n /** Timestamp when the event occurred */\n timestamp: z.union([z.string().datetime(), z.date()]).optional(),\n\n /** Original timestamp before processing */\n originalTimestamp: z.union([z.string().datetime(), z.date()]).optional(),\n\n /** Context and control fields */\n context: EmitterContextSchema.optional(),\n\n /** Message ID for deduplication */\n messageId: z.string().uuid().optional(),\n })\n .refine(data => Boolean(data.anonymousId) || Boolean(data.userId), {\n message: 'Either anonymousId or userId must be provided',\n });\n\n/**\n * Track event payload schema.\n * Uses safeExtend() because BasePayloadSchema has refinements (Zod v4 requirement).\n */\nexport const TrackEventSchema = BasePayloadSchema.safeExtend({\n type: z.literal('track'),\n event: z.string().min(1).max(255),\n properties: PropertyObjectSchema.optional(),\n});\n\n/**\n * Identify event payload schema.\n * Uses safeExtend() because BasePayloadSchema has refinements (Zod v4 requirement).\n */\nexport const IdentifyEventSchema = BasePayloadSchema.safeExtend({\n type: z.literal('identify'),\n userId: z.string().min(1).max(255),\n traits: PropertyObjectSchema.optional(),\n});\n\n/**\n * Page event payload schema.\n * Uses safeExtend() because BasePayloadSchema has refinements (Zod v4 requirement).\n */\nexport const PageEventSchema = BasePayloadSchema.safeExtend({\n type: z.literal('page'),\n name: z.string().max(255).optional(),\n category: z.string().max(255).optional(),\n properties: PropertyObjectSchema.optional(),\n});\n\n/**\n * Screen event payload schema.\n * Uses safeExtend() because BasePayloadSchema has refinements (Zod v4 requirement).\n */\nexport const ScreenEventSchema = BasePayloadSchema.safeExtend({\n type: z.literal('screen'),\n name: z.string().max(255).optional(),\n category: z.string().max(255).optional(),\n properties: PropertyObjectSchema.optional(),\n});\n\n/**\n * Group event payload schema.\n * Uses safeExtend() because BasePayloadSchema has refinements (Zod v4 requirement).\n */\nexport const GroupEventSchema = BasePayloadSchema.safeExtend({\n type: z.literal('group'),\n groupId: z.string().min(1).max(255),\n traits: PropertyObjectSchema.optional(),\n});\n\n/**\n * Alias event payload schema.\n * Uses safeExtend() because BasePayloadSchema has refinements (Zod v4 requirement).\n */\nexport const AliasEventSchema = BasePayloadSchema.safeExtend({\n type: z.literal('alias'),\n userId: z.string().min(1).max(255),\n previousId: z.string().min(1).max(255),\n});\n\n/**\n * Union of all event payload schemas.\n */\nexport const EventPayloadSchema = z.discriminatedUnion('type', [\n TrackEventSchema,\n IdentifyEventSchema,\n PageEventSchema,\n ScreenEventSchema,\n GroupEventSchema,\n AliasEventSchema,\n]);\n\n// =============================================================================\n// Ingestion Request Schemas\n// =============================================================================\n\n/**\n * Single event ingestion request schema.\n */\nexport const SingleEventRequestSchema = EventPayloadSchema;\n\n/**\n * Batch event ingestion request schema.\n *\n * The batch size limit is enforced at the service level via `maxBatchSize` config\n * (default: 100) rather than in the schema, allowing for flexible configuration.\n */\nexport const BatchEventRequestSchema = z.object({\n batch: z.array(EventPayloadSchema).min(1),\n});\n\n/**\n * Combined ingestion request schema - accepts single event or batch.\n */\nexport const IngestionRequestSchema = z.union([BatchEventRequestSchema, SingleEventRequestSchema]);\n\n// =============================================================================\n// Ingestion Response Schemas\n// =============================================================================\n\n/**\n * Event processing result.\n */\nexport const EventResultSchema = z.object({\n /** Server-generated event ID */\n id: z.string().uuid(),\n\n /** Original message ID if provided */\n messageId: z.string().uuid().optional(),\n\n /** Event type */\n type: z.enum(['track', 'identify', 'page', 'screen', 'group', 'alias']),\n\n /** Processing status */\n status: z.enum(['accepted', 'rejected']),\n\n /** Error message if rejected */\n error: z.string().optional(),\n});\n\n/**\n * Successful ingestion response schema.\n */\nexport const IngestionSuccessResponseSchema = z.object({\n /** Whether the request was successful */\n success: z.literal(true),\n\n /** Number of events accepted */\n accepted: z.number().int().nonnegative(),\n\n /** Number of events rejected */\n rejected: z.number().int().nonnegative(),\n\n /** Per-event results */\n results: z.array(EventResultSchema),\n\n /** Server timestamp when events were received */\n receivedAt: z.string().datetime(),\n});\n\n/**\n * Error response schema.\n */\nexport const IngestionErrorResponseSchema = z.object({\n /** Whether the request was successful */\n success: z.literal(false),\n\n /** Error code */\n code: z.string(),\n\n /** Error message */\n error: z.string(),\n\n /** Field-level validation errors */\n fieldErrors: z\n .array(\n z.object({\n path: z.array(z.union([z.string(), z.number()])),\n message: z.string(),\n }),\n )\n .optional(),\n});\n\n/**\n * Combined response schema.\n */\nexport const IngestionResponseSchema = z.union([\n IngestionSuccessResponseSchema,\n IngestionErrorResponseSchema,\n]);\n\n// =============================================================================\n// Type Exports\n// =============================================================================\n\nexport type PropertyValue = z.infer<typeof PropertyValueSchema>;\nexport type PropertyObject = z.infer<typeof PropertyObjectSchema>;\nexport type EmitterContext = z.infer<typeof EmitterContextSchema>;\nexport type TrackEvent = z.infer<typeof TrackEventSchema>;\nexport type IdentifyEvent = z.infer<typeof IdentifyEventSchema>;\nexport type PageEvent = z.infer<typeof PageEventSchema>;\nexport type ScreenEvent = z.infer<typeof ScreenEventSchema>;\nexport type GroupEvent = z.infer<typeof GroupEventSchema>;\nexport type AliasEvent = z.infer<typeof AliasEventSchema>;\nexport type EventPayload = z.infer<typeof EventPayloadSchema>;\nexport type SingleEventRequest = z.infer<typeof SingleEventRequestSchema>;\nexport type BatchEventRequest = z.infer<typeof BatchEventRequestSchema>;\nexport type IngestionRequest = z.infer<typeof IngestionRequestSchema>;\nexport type EventResult = z.infer<typeof EventResultSchema>;\nexport type IngestionSuccessResponse = z.infer<typeof IngestionSuccessResponseSchema>;\nexport type IngestionErrorResponse = z.infer<typeof IngestionErrorResponseSchema>;\nexport type IngestionResponse = z.infer<typeof IngestionResponseSchema>;\n","/**\n * @fileoverview Event Ingestion Service\n *\n * Provides server-side event ingestion functionality. Handles validation,\n * normalization, and forwarding of events to the analytics system.\n *\n * **Key Features**:\n * - Validates incoming events against Zod schemas\n * - Normalizes event payloads with defaults\n * - Forwards events to AnalyticsManager\n * - Tracks ingestion metrics\n * - Handles batch processing efficiently\n *\n * @module @od-oneapp/analytics/shared/ingestion/service\n */\n\nimport { logDebug, logError, logInfo, logWarn } from '@repo/shared/logs';\n\nimport { sanitizeProperties, validateEventName } from '../utils/security';\n\nimport { BatchEventRequestSchema, EventPayloadSchema, IngestionRequestSchema } from './schemas';\n\nimport type {\n BatchEventRequest,\n EmitterContext,\n EventPayload,\n EventResult,\n IngestionErrorResponse,\n IngestionRequest,\n IngestionSuccessResponse,\n} from './schemas';\nimport type { EmitterPayload } from '../emitters/emitter-types';\nimport type { AnalyticsManager } from '../types/types';\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/**\n * Ingestion context provided by the caller (usually the API route handler).\n */\nexport interface IngestionContext {\n /** Source identifier (app/module/emitter id) */\n source: string;\n\n /** Tenant/project ID for multi-tenancy */\n tenantId?: string;\n\n /** User ID from auth context */\n userId?: string;\n\n /** Account/organization ID */\n accountId?: string;\n\n /** Client IP address */\n ip?: string;\n\n /** Client user agent */\n userAgent?: string;\n\n /** Environment (dev/stage/prod) */\n environment?: string;\n\n /** Trace ID for observability */\n traceId?: string;\n\n /** Correlation ID for request tracing */\n correlationId?: string;\n\n /** API version */\n apiVersion?: string;\n\n /** SDK version from client */\n sdkVersion?: string;\n}\n\n/**\n * Ingestion service configuration.\n */\nexport interface IngestionServiceConfig {\n /** Maximum events per batch (default: 100) */\n maxBatchSize?: number;\n\n /** Maximum payload size in bytes (default: 1MB) */\n maxPayloadSize?: number;\n\n /** Whether to strip PII from properties (default: true in production) */\n stripPII?: boolean;\n\n /** Whether to strip HTML from properties (default: true) */\n stripHTML?: boolean;\n\n /** Timeout for processing each event in ms (default: 5000) */\n eventTimeout?: number;\n\n /** Concurrency for batch processing (default: 10) */\n batchConcurrency?: number;\n}\n\n/**\n * Ingestion metrics for observability.\n */\nexport interface IngestionMetrics {\n /** Total events received */\n totalReceived: number;\n\n /** Events accepted */\n accepted: number;\n\n /** Events rejected */\n rejected: number;\n\n /** Events by type */\n byType: Record<string, number>;\n\n /** Processing time in ms */\n processingTimeMs: number;\n}\n\n// =============================================================================\n// Default Configuration\n// =============================================================================\n\nconst DEFAULT_CONFIG: Required<IngestionServiceConfig> = {\n maxBatchSize: 100,\n maxPayloadSize: 1024 * 1024, // 1MB\n stripPII: true, // Default to safe; callers can override\n stripHTML: true,\n eventTimeout: 5000,\n batchConcurrency: 10,\n};\n\n// =============================================================================\n// Utility Functions\n// =============================================================================\n\n/**\n * Generate a UUID v4.\n */\nfunction generateUUID(): string {\n return crypto.randomUUID();\n}\n\n/**\n * Get current ISO timestamp.\n */\nfunction getCurrentTimestamp(): string {\n return new Date().toISOString();\n}\n\n/**\n * Check if request is a batch request.\n */\nfunction isBatchRequest(request: IngestionRequest): request is BatchEventRequest {\n return 'batch' in request && Array.isArray(request.batch);\n}\n\n/**\n * Normalize an event payload with defaults and server metadata.\n */\nfunction normalizeEvent(\n event: EventPayload,\n context: IngestionContext,\n receivedAt: string,\n): EventPayload {\n const normalized = { ...event };\n\n // Generate message ID if not provided\n if (!normalized.messageId) {\n normalized.messageId = generateUUID();\n }\n\n // Set timestamp if not provided\n if (!normalized.timestamp) {\n normalized.timestamp = receivedAt;\n }\n\n // Preserve original timestamp\n if (!normalized.originalTimestamp) {\n normalized.originalTimestamp = normalized.timestamp;\n }\n\n // Merge context with server-provided context\n const mergedContext: EmitterContext = {\n ...normalized.context,\n channel: normalized.context?.channel ?? 'api',\n ip: context.ip ?? normalized.context?.ip,\n userAgent: context.userAgent ?? normalized.context?.userAgent,\n library: normalized.context?.library ?? {\n name: context.source,\n version: context.sdkVersion ?? 'unknown',\n },\n };\n\n normalized.context = mergedContext;\n\n // Set userId from auth context if not provided\n if (!normalized.userId && context.userId) {\n normalized.userId = context.userId;\n }\n\n return normalized;\n}\n\n/**\n * Convert EventPayload to EmitterPayload format for AnalyticsManager.\n * Explicitly maps fields to ensure type safety.\n */\nfunction toEmitterPayload(event: EventPayload): EmitterPayload {\n // Explicitly construct the EmitterPayload from EventPayload fields\n // to maintain type safety and avoid unsafe double casts\n const basePayload = {\n userId: event.userId,\n anonymousId: event.anonymousId,\n timestamp: event.timestamp,\n context: event.context,\n messageId: event.messageId,\n };\n\n switch (event.type) {\n case 'track':\n return {\n type: 'track',\n event: event.event,\n properties: event.properties,\n ...basePayload,\n } as EmitterPayload;\n case 'identify':\n return {\n type: 'identify',\n traits: event.traits,\n ...basePayload,\n } as EmitterPayload;\n case 'page':\n return {\n type: 'page',\n name: event.name,\n category: event.category,\n properties: event.properties,\n ...basePayload,\n } as EmitterPayload;\n case 'screen':\n return {\n type: 'screen',\n name: event.name,\n category: event.category,\n properties: event.properties,\n ...basePayload,\n } as EmitterPayload;\n case 'group':\n return {\n type: 'group',\n groupId: event.groupId,\n traits: event.traits,\n ...basePayload,\n } as EmitterPayload;\n case 'alias':\n return {\n type: 'alias',\n previousId: event.previousId,\n ...basePayload,\n } as EmitterPayload;\n default:\n // This should never happen due to discriminated union validation\n throw new Error(`Unknown event type: ${(event as { type: string }).type}`);\n }\n}\n\n// =============================================================================\n// Ingestion Service Class\n// =============================================================================\n\n/**\n * Event Ingestion Service.\n *\n * Handles validation, normalization, and forwarding of analytics events.\n * Designed for high-volume ingestion with batching and rate limiting support.\n *\n * @example\n * ```typescript\n * const service = new IngestionService(analyticsManager);\n *\n * const result = await service.ingest(requestBody, {\n * source: 'web-app',\n * tenantId: 'tenant-123',\n * userId: 'user-456',\n * });\n *\n * if (result.success) {\n * console.log(`Accepted ${result.accepted} events`);\n * }\n * ```\n */\nexport class IngestionService {\n private readonly config: Required<IngestionServiceConfig>;\n\n constructor(\n private readonly analyticsManager: AnalyticsManager,\n config: IngestionServiceConfig = {},\n ) {\n this.config = { ...DEFAULT_CONFIG, ...config };\n }\n\n /**\n * Parse and validate the ingestion request payload.\n *\n * @param payload - Raw request payload (parsed JSON)\n * @returns Parsed and validated request, or error response\n */\n parseRequest(\n payload: unknown,\n ): { success: true; data: IngestionRequest } | { success: false; error: IngestionErrorResponse } {\n // Validate payload size (rough estimate)\n try {\n const payloadSize = JSON.stringify(payload).length;\n if (payloadSize > this.config.maxPayloadSize) {\n return {\n success: false,\n error: {\n success: false,\n code: 'PAYLOAD_TOO_LARGE',\n error: `Payload size ${payloadSize} exceeds maximum ${this.config.maxPayloadSize} bytes`,\n },\n };\n }\n } catch {\n return {\n success: false,\n error: {\n success: false,\n code: 'INVALID_JSON',\n error: 'Failed to serialize payload for size check',\n },\n };\n }\n\n // Validate against schema\n const result = IngestionRequestSchema.safeParse(payload);\n\n if (!result.success) {\n const fieldErrors = result.error.issues.map(err => ({\n path: err.path.map(p => (typeof p === 'symbol' ? String(p) : p)),\n message: err.message,\n }));\n\n return {\n success: false,\n error: {\n success: false,\n code: 'VALIDATION_ERROR',\n error: 'Request validation failed',\n fieldErrors,\n },\n };\n }\n\n // Check batch size\n if (isBatchRequest(result.data) && result.data.batch.length > this.config.maxBatchSize) {\n return {\n success: false,\n error: {\n success: false,\n code: 'BATCH_TOO_LARGE',\n error: `Batch size ${result.data.batch.length} exceeds maximum ${this.config.maxBatchSize}`,\n },\n };\n }\n\n return { success: true, data: result.data };\n }\n\n /**\n * Ingest events from a validated request.\n *\n * @param request - Validated ingestion request\n * @param context - Ingestion context from the caller\n * @returns Ingestion response with per-event results\n */\n async ingest(\n request: IngestionRequest,\n context: IngestionContext,\n ): Promise<IngestionSuccessResponse> {\n const startTime = process.hrtime.bigint();\n const receivedAt = getCurrentTimestamp();\n\n // Extract events from request (single or batch)\n const events: EventPayload[] = isBatchRequest(request) ? request.batch : [request];\n\n const results: EventResult[] = [];\n const metrics: IngestionMetrics = {\n totalReceived: events.length,\n accepted: 0,\n rejected: 0,\n byType: {},\n processingTimeMs: 0,\n };\n\n // Process events\n const processedEvents: EmitterPayload[] = [];\n\n for (const event of events) {\n const eventId = generateUUID();\n\n try {\n // Validate event name for track events\n if (event.type === 'track') {\n const validation = validateEventName(event.event);\n if (!validation.valid) {\n results.push({\n id: eventId,\n messageId: event.messageId,\n type: event.type,\n status: 'rejected',\n error: `Invalid event name: ${validation.reason}`,\n });\n metrics.rejected++;\n continue;\n }\n }\n\n // Normalize event\n const normalized = normalizeEvent(event, context, receivedAt);\n\n // Sanitize properties\n if ('properties' in normalized && normalized.properties) {\n const sanitized = sanitizeProperties(normalized.properties, {\n stripPII: this.config.stripPII,\n stripHTML: this.config.stripHTML,\n allowDangerousKeys: false,\n });\n (normalized as { properties: Record<string, unknown> }).properties = sanitized.data;\n\n if (sanitized.warnings.length > 0) {\n logDebug('Event properties sanitized', {\n eventId,\n warnings: sanitized.warnings,\n });\n }\n }\n\n if ('traits' in normalized && normalized.traits) {\n const sanitized = sanitizeProperties(normalized.traits, {\n stripPII: this.config.stripPII,\n stripHTML: this.config.stripHTML,\n allowDangerousKeys: false,\n });\n (normalized as { traits: Record<string, unknown> }).traits = sanitized.data;\n }\n\n // Convert to emitter payload and queue for processing\n const emitterPayload = toEmitterPayload(normalized);\n processedEvents.push(emitterPayload);\n\n results.push({\n id: eventId,\n messageId: normalized.messageId,\n type: event.type,\n status: 'accepted',\n });\n\n metrics.accepted++;\n metrics.byType[event.type] = (metrics.byType[event.type] ?? 0) + 1;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n\n results.push({\n id: eventId,\n messageId: event.messageId,\n type: event.type,\n status: 'rejected',\n error: errorMessage,\n });\n\n metrics.rejected++;\n\n logWarn('Event processing failed', {\n eventId,\n type: event.type,\n error: errorMessage,\n });\n }\n }\n\n // Forward accepted events to analytics manager (non-blocking)\n // Note: This provides at-most-once delivery semantics. Events may be lost\n // if the analytics manager fails. For guaranteed delivery, consider using\n // a durable queue (Redis, SQS) for critical events.\n if (processedEvents.length > 0) {\n // Use emitBatch for efficient processing\n void (async () => {\n try {\n await this.analyticsManager.emitBatch(processedEvents, {\n timeout: this.config.eventTimeout,\n concurrency: this.config.batchConcurrency,\n failFast: false, // Process all events even if some fail\n });\n } catch (error) {\n logError('Failed to emit events to analytics manager', {\n error: error instanceof Error ? error.message : 'Unknown error',\n eventCount: processedEvents.length,\n traceId: context.traceId,\n });\n }\n })();\n }\n\n // Calculate processing time\n const endTime = process.hrtime.bigint();\n metrics.processingTimeMs = Number(endTime - startTime) / 1_000_000;\n\n // Log metrics\n logInfo('Event ingestion completed', {\n traceId: context.traceId,\n source: context.source,\n tenantId: context.tenantId,\n totalReceived: metrics.totalReceived,\n accepted: metrics.accepted,\n rejected: metrics.rejected,\n processingTimeMs: metrics.processingTimeMs.toFixed(2),\n byType: metrics.byType,\n });\n\n return {\n success: true,\n accepted: metrics.accepted,\n rejected: metrics.rejected,\n results,\n receivedAt,\n };\n }\n\n /**\n * Process a raw request body through parsing and ingestion.\n *\n * Convenience method that combines parseRequest and ingest.\n *\n * @param payload - Raw request payload\n * @param context - Ingestion context\n * @returns Ingestion response (success or error)\n */\n async processRequest(\n payload: unknown,\n context: IngestionContext,\n ): Promise<IngestionSuccessResponse | IngestionErrorResponse> {\n const parseResult = this.parseRequest(payload);\n\n if (!parseResult.success) {\n return parseResult.error;\n }\n\n return this.ingest(parseResult.data, context);\n }\n}\n\n// =============================================================================\n// Factory Functions\n// =============================================================================\n\n/**\n * Create an ingestion service instance.\n *\n * @param analyticsManager - Initialized AnalyticsManager instance\n * @param config - Optional service configuration\n * @returns Configured IngestionService instance\n *\n * @example\n * ```typescript\n * import { createServerAnalytics } from '@od-oneapp/analytics/server';\n * import { createIngestionService } from '@od-oneapp/analytics/server';\n *\n * const analytics = await createServerAnalytics(config);\n * const ingestionService = createIngestionService(analytics);\n * ```\n */\nexport function createIngestionService(\n analyticsManager: AnalyticsManager,\n config?: IngestionServiceConfig,\n): IngestionService {\n return new IngestionService(analyticsManager, config);\n}\n\n/**\n * Validate a single event payload.\n *\n * Useful for pre-validation before queuing events.\n *\n * @param payload - Event payload to validate\n * @returns Validation result with parsed data or errors\n */\nexport function validateEventPayload(\n payload: unknown,\n): { success: true; data: EventPayload } | { success: false; errors: string[] } {\n const result = EventPayloadSchema.safeParse(payload);\n\n if (!result.success) {\n return {\n success: false,\n errors: result.error.issues.map(\n e => `${e.path.map(p => (typeof p === 'symbol' ? String(p) : p)).join('.')}: ${e.message}`,\n ),\n };\n }\n\n return { success: true, data: result.data };\n}\n\n/**\n * Validate a batch of event payloads.\n *\n * @param payloads - Array of event payloads to validate\n * @returns Validation result with parsed data or errors\n */\nexport function validateBatchPayload(\n payloads: unknown[],\n): { success: true; data: EventPayload[] } | { success: false; errors: string[] } {\n const result = BatchEventRequestSchema.safeParse({ batch: payloads });\n\n if (!result.success) {\n return {\n success: false,\n errors: result.error.issues.map(\n e => `${e.path.map(p => (typeof p === 'symbol' ? String(p) : p)).join('.')}: ${e.message}`,\n ),\n };\n }\n\n return { success: true, data: result.data.batch };\n}\n"],"mappings":"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"}
@@ -1,164 +0,0 @@
1
- /**
2
- * @fileoverview Client-Side Analytics Exports
3
- *
4
- * Complete analytics solution for browser/client environments. Provides
5
- * type-safe event tracking with support for multiple analytics providers
6
- * (Segment, HTTP, Vercel Analytics, Console).
7
- *
8
- * **Core Functions**:
9
- * - `createClientAnalytics()`: Create and initialize a client analytics instance
10
- * - `createClientAnalyticsUninitialized()`: Create without initializing (for lazy loading)
11
- *
12
- * **Emitters** (Preferred API):
13
- * - `track()`: Track custom events
14
- * - `identify()`: Identify users with traits
15
- * - `page()`: Track page views
16
- * - `group()`: Associate users with groups
17
- * - `alias()`: Alias user IDs
18
- * - `ecommerce.*`: E-commerce specific events
19
- *
20
- * **Usage Pattern**:
21
- * 1. Create analytics instance with provider configuration
22
- * 2. Use emitters to create event payloads
23
- * 3. Emit events using `analytics.emit()` or `analytics.track()`
24
- *
25
- * @module @od-oneapp/analytics/client
26
- *
27
- * @example
28
- * ```typescript
29
- * import { createClientAnalytics, track, ecommerce } from '@od-oneapp/analytics/client';
30
- *
31
- * const analytics = await createClientAnalytics({
32
- * providers: {
33
- * segment: { writeKey: 'xxx' },
34
- * http: { endpoint: 'https://api.example.com/ingest' }
35
- * }
36
- * });
37
- *
38
- * // Preferred: Use emitters
39
- * await analytics.emit(track('Button Clicked', { color: 'blue' }));
40
- * await analytics.emit(ecommerce.productViewed({ product_id: '123' }));
41
- * ```
42
- */
43
-
44
- import { SegmentClientProvider } from '@integrations/segment/analytics-provider/client';
45
- import { VercelClientProvider } from '@integrations/vercel/analytics-provider/client';
46
-
47
- import { ConsoleProvider } from '../providers/console/client';
48
- import { HttpClientProvider } from '../providers/http/client';
49
- import { createAnalyticsManager } from '../shared/utils/manager';
50
-
51
- import type { AnalyticsConfig, AnalyticsManager, ProviderRegistry } from '../shared/types/types';
52
-
53
- // Client-specific provider registry
54
- const CLIENT_PROVIDERS: ProviderRegistry = {
55
- console: config => new ConsoleProvider(config),
56
- http: config => new HttpClientProvider(config),
57
- segment: config => new SegmentClientProvider(config),
58
- vercel: config => new VercelClientProvider(config),
59
- };
60
-
61
- // ============================================================================
62
- // CORE ANALYTICS FUNCTIONS
63
- // ============================================================================
64
-
65
- /**
66
- * Create and initialize a client analytics instance.
67
- *
68
- * @remarks
69
- * This is the primary way to create analytics for client-side applications.
70
- * The instance will be initialized immediately and ready to use.
71
- *
72
- * @param config - Analytics configuration including providers and settings
73
- * @returns Promise resolving to initialized analytics manager
74
- *
75
- * @example
76
- * ```typescript
77
- * const analytics = await createClientAnalytics({
78
- * providers: {
79
- * http: { endpoint: process.env.NEXT_PUBLIC_API_URL! + '/api/v1/ingest' },
80
- * segment: { writeKey: process.env.NEXT_PUBLIC_SEGMENT_KEY! }
81
- * }
82
- * });
83
- *
84
- * await analytics.track('Page Viewed', { page: '/dashboard' });
85
- * ```
86
- */
87
- export async function createClientAnalytics(config: AnalyticsConfig): Promise<AnalyticsManager> {
88
- const manager = createAnalyticsManager(config, CLIENT_PROVIDERS);
89
- await manager.initialize();
90
- return manager;
91
- }
92
-
93
- /**
94
- * Create a client analytics instance without initializing.
95
- *
96
- * @remarks
97
- * Useful when you need to control initialization timing, such as lazy loading
98
- * analytics after user interaction or page load.
99
- *
100
- * @param config - Analytics configuration including providers and settings
101
- * @returns Uninitialized analytics manager instance
102
- *
103
- * @example
104
- * ```typescript
105
- * const analytics = createClientAnalyticsUninitialized(config);
106
- * // Later, when ready
107
- * await analytics.initialize();
108
- * await analytics.track('User Ready');
109
- * ```
110
- */
111
- export function createClientAnalyticsUninitialized(config: AnalyticsConfig): AnalyticsManager {
112
- return createAnalyticsManager(config, CLIENT_PROVIDERS);
113
- }
114
-
115
- // ============================================================================
116
- // EMITTERS - PRIMARY INTERFACE
117
- // ============================================================================
118
-
119
- // Export all core emitters - these are the preferred way to track events
120
- export {
121
- // Core Segment.io spec emitters
122
- alias,
123
- // Ecommerce emitters namespace
124
- ecommerce,
125
- group,
126
- identify,
127
- page,
128
- track,
129
- } from '../shared/emitters';
130
-
131
- // ============================================================================
132
- // ADAPTER UTILITIES
133
- // ============================================================================
134
-
135
- export { createEmitterProcessor, processEmitterPayload } from '../shared/utils/emitter-adapter';
136
-
137
- // ============================================================================
138
- // TYPES
139
- // ============================================================================
140
-
141
- // Core analytics types;
142
-
143
- // Emitter types;
144
-
145
- // Provider-specific types;
146
-
147
- // Ecommerce types;
148
-
149
- // ============================================================================
150
- // CONFIGURATION UTILITIES
151
- // ============================================================================
152
-
153
- // ============================================================================
154
- // VALIDATION UTILITIES
155
- // ============================================================================
156
-
157
- // Validation should happen on the server side only
158
- // Client-side validation is removed to follow the four-file export pattern
159
-
160
- // ============================================================================
161
- // ADVANCED UTILITIES
162
- // ============================================================================
163
-
164
- // Manager utilities