@stacksjs/ts-analytics 0.1.6

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 (237) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/LICENSE.md +21 -0
  3. package/README.md +361 -0
  4. package/bin/cli.ts +169 -0
  5. package/dist/Analytics.d.ts +558 -0
  6. package/dist/api.d.ts +109 -0
  7. package/dist/batching.d.ts +93 -0
  8. package/dist/chunk-2mx7fq49.js +4 -0
  9. package/dist/chunk-3z29508k.js +204 -0
  10. package/dist/chunk-deephkz6.js +56 -0
  11. package/dist/chunk-j261vgyp.js +305 -0
  12. package/dist/chunk-xga17tz7.js +207 -0
  13. package/dist/config.d.ts +132 -0
  14. package/dist/dashboard/components/index.d.ts +80 -0
  15. package/dist/dashboard/composables/useAnalytics.d.ts +99 -0
  16. package/dist/dashboard/index.d.ts +110 -0
  17. package/dist/dashboard/types/index.d.ts +144 -0
  18. package/dist/dashboard/utils/index.d.ts +81 -0
  19. package/dist/dynamodb.d.ts +86 -0
  20. package/dist/funnels.d.ts +104 -0
  21. package/dist/geolocation.d.ts +96 -0
  22. package/dist/index.d.ts +371 -0
  23. package/dist/index.js +10211 -0
  24. package/dist/infrastructure/cdk.d.ts +60 -0
  25. package/dist/infrastructure/cloudformation.d.ts +44 -0
  26. package/dist/infrastructure/index.d.ts +45 -0
  27. package/dist/infrastructure/setup.d.ts +133 -0
  28. package/dist/integrations/cloudflare.d.ts +76 -0
  29. package/dist/integrations/hono.d.ts +60 -0
  30. package/dist/integrations/index.d.ts +24 -0
  31. package/dist/integrations/nuxt.d.ts +7 -0
  32. package/dist/integrations/nuxt.js +42 -0
  33. package/dist/integrations/runtime/use-ts-analytics.d.ts +18 -0
  34. package/dist/integrations/runtime/use-ts-analytics.js +17 -0
  35. package/dist/integrations/stx.d.ts +88 -0
  36. package/dist/integrations/stx.js +18 -0
  37. package/dist/lib/crypto-random.d.ts +4 -0
  38. package/dist/lib/salt.d.ts +16 -0
  39. package/dist/local.d.ts +56 -0
  40. package/dist/model-connector.d.ts +145 -0
  41. package/dist/models/AggregatedStats.d.ts +9 -0
  42. package/dist/models/CampaignStats.d.ts +9 -0
  43. package/dist/models/Conversion.d.ts +11 -0
  44. package/dist/models/CustomEvent.d.ts +11 -0
  45. package/dist/models/DeviceStats.d.ts +9 -0
  46. package/dist/models/EventStats.d.ts +9 -0
  47. package/dist/models/GeoStats.d.ts +9 -0
  48. package/dist/models/Goal.d.ts +9 -0
  49. package/dist/models/GoalStats.d.ts +9 -0
  50. package/dist/models/PageStats.d.ts +11 -0
  51. package/dist/models/PageView.d.ts +13 -0
  52. package/dist/models/RealtimeStats.d.ts +9 -0
  53. package/dist/models/ReferrerStats.d.ts +9 -0
  54. package/dist/models/Session.d.ts +11 -0
  55. package/dist/models/Site.d.ts +11 -0
  56. package/dist/models/index.d.ts +28 -0
  57. package/dist/models/types.d.ts +60 -0
  58. package/dist/sqs-buffering.d.ts +243 -0
  59. package/dist/stacks-integration.d.ts +159 -0
  60. package/dist/tracking-script.d.ts +71 -0
  61. package/dist/tracking.d.ts +17 -0
  62. package/dist/tracking.js +26 -0
  63. package/dist/types.d.ts +595 -0
  64. package/dist/utils/geolocation.d.ts +111 -0
  65. package/dist/utils/user-agent.d.ts +17 -0
  66. package/dist/version.d.ts +7 -0
  67. package/package.json +119 -0
  68. package/src/Analytics.ts +3349 -0
  69. package/src/api.ts +1286 -0
  70. package/src/assets/crosswind.css +2220 -0
  71. package/src/batching.ts +452 -0
  72. package/src/components/dashboard/index.ts +18 -0
  73. package/src/config.ts +456 -0
  74. package/src/dashboard/Dashboard.stx +1517 -0
  75. package/src/dashboard/components/AlertCard.stx +177 -0
  76. package/src/dashboard/components/AnalyticsDashboard.stx +354 -0
  77. package/src/dashboard/components/AnimatedNumber.stx +86 -0
  78. package/src/dashboard/components/BarChart.stx +220 -0
  79. package/src/dashboard/components/BrowserBreakdown.stx +98 -0
  80. package/src/dashboard/components/BrowsersTable.stx +125 -0
  81. package/src/dashboard/components/CampaignBreakdown.stx +163 -0
  82. package/src/dashboard/components/CampaignTable.stx +238 -0
  83. package/src/dashboard/components/CountryList.stx +101 -0
  84. package/src/dashboard/components/DataTable.stx +226 -0
  85. package/src/dashboard/components/DateRangePicker.stx +77 -0
  86. package/src/dashboard/components/DeviceBreakdown.stx +94 -0
  87. package/src/dashboard/components/DevicesTable.stx +163 -0
  88. package/src/dashboard/components/DonutChart.stories.ts +55 -0
  89. package/src/dashboard/components/DonutChart.stx +157 -0
  90. package/src/dashboard/components/EmptyState.stx +90 -0
  91. package/src/dashboard/components/EngagementMetrics.stx +183 -0
  92. package/src/dashboard/components/EventsSection.stx +192 -0
  93. package/src/dashboard/components/FilterBar.stx +104 -0
  94. package/src/dashboard/components/FullAnalyticsDashboard.stx +455 -0
  95. package/src/dashboard/components/FunnelChart.stx +142 -0
  96. package/src/dashboard/components/GeoTable.stx +337 -0
  97. package/src/dashboard/components/GoalsPanel.stx +109 -0
  98. package/src/dashboard/components/Header.stx +39 -0
  99. package/src/dashboard/components/HeatmapChart.stx +140 -0
  100. package/src/dashboard/components/LiveActivityFeed.stx +172 -0
  101. package/src/dashboard/components/MetricComparison.stx +106 -0
  102. package/src/dashboard/components/MiniStats.stx +96 -0
  103. package/src/dashboard/components/OSBreakdown.stx +124 -0
  104. package/src/dashboard/components/PageDetailCard.stx +102 -0
  105. package/src/dashboard/components/PagesTable.stx +127 -0
  106. package/src/dashboard/components/ProgressRing.stx +89 -0
  107. package/src/dashboard/components/RealtimeCounter.stories.ts +45 -0
  108. package/src/dashboard/components/RealtimeCounter.stx +46 -0
  109. package/src/dashboard/components/ReferrersTable.stx +180 -0
  110. package/src/dashboard/components/SparklineChart.stx +160 -0
  111. package/src/dashboard/components/StatCard.stories.ts +58 -0
  112. package/src/dashboard/components/StatCard.stx +90 -0
  113. package/src/dashboard/components/SummaryStats.stx +81 -0
  114. package/src/dashboard/components/TabNav.stx +66 -0
  115. package/src/dashboard/components/ThemeSwitcher.stx +124 -0
  116. package/src/dashboard/components/TimeSeriesChart.stx +106 -0
  117. package/src/dashboard/components/TopList.stories.ts +58 -0
  118. package/src/dashboard/components/TopList.stx +74 -0
  119. package/src/dashboard/components/TrendIndicator.stx +84 -0
  120. package/src/dashboard/components/heatmap/ClickHeatmap.stx +264 -0
  121. package/src/dashboard/components/heatmap/ElementClickList.stx +125 -0
  122. package/src/dashboard/components/heatmap/HeatmapControls.stx +125 -0
  123. package/src/dashboard/components/heatmap/HeatmapLegend.stx +69 -0
  124. package/src/dashboard/components/heatmap/PageHeatmap.stx +264 -0
  125. package/src/dashboard/components/heatmap/ScrollHeatmap.stx +127 -0
  126. package/src/dashboard/components/heatmap/index.ts +12 -0
  127. package/src/dashboard/components/index.ts +86 -0
  128. package/src/dashboard/composables/useAnalytics.ts +465 -0
  129. package/src/dashboard/demo/DemoApp.stx +370 -0
  130. package/src/dashboard/demo/index.ts +8 -0
  131. package/src/dashboard/demo/mockData.ts +234 -0
  132. package/src/dashboard/index.ts +117 -0
  133. package/src/dashboard/stx-shim.d.ts +4 -0
  134. package/src/dashboard/types/index.ts +203 -0
  135. package/src/dashboard/utils/index.ts +426 -0
  136. package/src/dynamodb.ts +344 -0
  137. package/src/funnels.ts +534 -0
  138. package/src/geolocation.ts +515 -0
  139. package/src/handlers/alerts.ts +328 -0
  140. package/src/handlers/annotations.ts +128 -0
  141. package/src/handlers/api-keys.ts +240 -0
  142. package/src/handlers/auth.ts +1020 -0
  143. package/src/handlers/authz.ts +137 -0
  144. package/src/handlers/collect.ts +724 -0
  145. package/src/handlers/data.ts +625 -0
  146. package/src/handlers/experiments.ts +138 -0
  147. package/src/handlers/funnels.ts +216 -0
  148. package/src/handlers/goals.ts +218 -0
  149. package/src/handlers/heatmaps.ts +272 -0
  150. package/src/handlers/index.ts +56 -0
  151. package/src/handlers/lib/read-cache.ts +63 -0
  152. package/src/handlers/misc.ts +486 -0
  153. package/src/handlers/oauth.ts +233 -0
  154. package/src/handlers/performance.ts +388 -0
  155. package/src/handlers/sessions.ts +413 -0
  156. package/src/handlers/sharing.ts +198 -0
  157. package/src/handlers/stats.ts +1368 -0
  158. package/src/handlers/team.ts +161 -0
  159. package/src/handlers/uptime.ts +283 -0
  160. package/src/handlers/views.ts +390 -0
  161. package/src/handlers/webhooks.ts +226 -0
  162. package/src/heatmap/index.ts +32 -0
  163. package/src/heatmap/tracking-script.ts +452 -0
  164. package/src/heatmap/types.ts +79 -0
  165. package/src/index.ts +387 -0
  166. package/src/infrastructure/cdk.ts +496 -0
  167. package/src/infrastructure/cloudformation.ts +595 -0
  168. package/src/infrastructure/index.ts +48 -0
  169. package/src/infrastructure/setup.ts +611 -0
  170. package/src/integrations/cloudflare.ts +732 -0
  171. package/src/integrations/hono.ts +589 -0
  172. package/src/integrations/index.ts +27 -0
  173. package/src/integrations/nuxt.ts +78 -0
  174. package/src/integrations/runtime/use-ts-analytics.ts +32 -0
  175. package/src/integrations/stx.ts +138 -0
  176. package/src/jobs/index.ts +127 -0
  177. package/src/lib/crypto-random.ts +41 -0
  178. package/src/lib/ddb-errors.ts +20 -0
  179. package/src/lib/dynamodb.ts +216 -0
  180. package/src/lib/email.ts +38 -0
  181. package/src/lib/ga-import.ts +471 -0
  182. package/src/lib/ga4-api.ts +244 -0
  183. package/src/lib/goals.ts +205 -0
  184. package/src/lib/index.ts +6 -0
  185. package/src/lib/ingest-counters.ts +123 -0
  186. package/src/lib/log.ts +36 -0
  187. package/src/lib/membership.ts +98 -0
  188. package/src/lib/plans.ts +90 -0
  189. package/src/lib/quota.ts +46 -0
  190. package/src/lib/rate-limit.ts +27 -0
  191. package/src/lib/rollups.ts +472 -0
  192. package/src/lib/salt.ts +97 -0
  193. package/src/lib/scheduler.ts +97 -0
  194. package/src/lib/significance.ts +66 -0
  195. package/src/lib/site-retention.ts +56 -0
  196. package/src/local.ts +360 -0
  197. package/src/model-connector.ts +616 -0
  198. package/src/models/AggregatedStats.ts +162 -0
  199. package/src/models/CampaignStats.ts +141 -0
  200. package/src/models/Conversion.ts +135 -0
  201. package/src/models/CustomEvent.ts +123 -0
  202. package/src/models/DeviceStats.ts +105 -0
  203. package/src/models/EventStats.ts +108 -0
  204. package/src/models/GeoStats.ts +112 -0
  205. package/src/models/Goal.ts +105 -0
  206. package/src/models/GoalStats.ts +106 -0
  207. package/src/models/PageStats.ts +152 -0
  208. package/src/models/PageView.ts +277 -0
  209. package/src/models/RealtimeStats.ts +96 -0
  210. package/src/models/ReferrerStats.ts +115 -0
  211. package/src/models/Session.ts +235 -0
  212. package/src/models/Site.ts +115 -0
  213. package/src/models/index.ts +50 -0
  214. package/src/models/orm/index.ts +2043 -0
  215. package/src/models/types.ts +71 -0
  216. package/src/router.ts +521 -0
  217. package/src/sqs-buffering.ts +806 -0
  218. package/src/stacks-integration.ts +527 -0
  219. package/src/tracking-script.ts +944 -0
  220. package/src/tracking.ts +27 -0
  221. package/src/types/analytics.ts +219 -0
  222. package/src/types/api.ts +75 -0
  223. package/src/types/bun-router.d.ts +15 -0
  224. package/src/types/dashboard.ts +62 -0
  225. package/src/types/index.ts +66 -0
  226. package/src/types/stx.d.ts +27 -0
  227. package/src/types/window.d.ts +101 -0
  228. package/src/types.ts +911 -0
  229. package/src/utils/cache.ts +148 -0
  230. package/src/utils/date.ts +118 -0
  231. package/src/utils/filters.ts +71 -0
  232. package/src/utils/geolocation.ts +323 -0
  233. package/src/utils/index.ts +9 -0
  234. package/src/utils/response.ts +180 -0
  235. package/src/utils/timezone-country.ts +242 -0
  236. package/src/utils/user-agent.ts +110 -0
  237. package/src/version.ts +7 -0
package/src/config.ts ADDED
@@ -0,0 +1,456 @@
1
+ /**
2
+ * Analytics Configuration
3
+ *
4
+ * Configuration for the analytics system using DynamoDB single-table design.
5
+ * Integrates with dynamodb-tooling for table management.
6
+ */
7
+
8
+ import type {
9
+ AnalyticsTableConfig,
10
+ BillingMode,
11
+ GSIDefinition,
12
+ SingleTableDesignConfig,
13
+ } from './types'
14
+
15
+ // ============================================================================
16
+ // Default Configuration
17
+ // ============================================================================
18
+
19
+ /**
20
+ * Default single-table design configuration
21
+ */
22
+ export const defaultSingleTableConfig: SingleTableDesignConfig = {
23
+ enabled: true,
24
+ partitionKeyName: 'pk',
25
+ sortKeyName: 'sk',
26
+ gsi1pkName: 'gsi1pk',
27
+ gsi1skName: 'gsi1sk',
28
+ gsi2pkName: 'gsi2pk',
29
+ gsi2skName: 'gsi2sk',
30
+ entityTypeAttribute: '_et',
31
+ pkPrefix: '{ENTITY}#',
32
+ skPrefix: '{ENTITY}#',
33
+ gsiCount: 2,
34
+ keyDelimiter: '#',
35
+ }
36
+
37
+ /**
38
+ * Default GSI definitions for analytics
39
+ */
40
+ export const defaultGSIs: GSIDefinition[] = [
41
+ {
42
+ name: 'gsi1',
43
+ partitionKey: 'gsi1pk',
44
+ sortKey: 'gsi1sk',
45
+ projection: { type: 'ALL' },
46
+ },
47
+ {
48
+ name: 'gsi2',
49
+ partitionKey: 'gsi2pk',
50
+ sortKey: 'gsi2sk',
51
+ projection: { type: 'ALL' },
52
+ },
53
+ ]
54
+
55
+ /**
56
+ * Default analytics table configuration
57
+ */
58
+ export const defaultAnalyticsConfig: AnalyticsTableConfig = {
59
+ tableName: 'AnalyticsTable',
60
+ billingMode: 'PAY_PER_REQUEST',
61
+ singleTable: defaultSingleTableConfig,
62
+ globalSecondaryIndexes: defaultGSIs,
63
+ localSecondaryIndexes: [],
64
+ ttlAttributeName: 'ttl',
65
+ }
66
+
67
+ // ============================================================================
68
+ // Configuration Types
69
+ // ============================================================================
70
+
71
+ /**
72
+ * Analytics configuration options
73
+ */
74
+ export interface AnalyticsConfig {
75
+ /** DynamoDB table configuration */
76
+ table: AnalyticsTableConfig
77
+
78
+ /** AWS region */
79
+ region: string
80
+
81
+ /** Custom endpoint (for local development) */
82
+ endpoint?: string
83
+
84
+ /** Data retention settings */
85
+ retention: {
86
+ /** Raw event TTL in seconds (default: 30 days) */
87
+ rawEventTtl: number
88
+ /** Hourly aggregate TTL in seconds (default: 90 days) */
89
+ hourlyAggregateTtl: number
90
+ /** Daily aggregate TTL in seconds (default: 2 years) */
91
+ dailyAggregateTtl: number
92
+ /** Monthly aggregates kept forever by default */
93
+ monthlyAggregateTtl?: number
94
+ }
95
+
96
+ /** Privacy settings */
97
+ privacy: {
98
+ /** Hash visitor IDs for privacy */
99
+ hashVisitorIds: boolean
100
+ /** Collect geolocation data */
101
+ collectGeolocation: boolean
102
+ /** Honor Do Not Track header */
103
+ honorDnt: boolean
104
+ /** IP anonymization level: 'none' | 'partial' | 'full' */
105
+ ipAnonymization: 'none' | 'partial' | 'full'
106
+ }
107
+
108
+ /** Tracking settings */
109
+ tracking: {
110
+ /** Track referrers */
111
+ trackReferrers: boolean
112
+ /** Track UTM parameters */
113
+ trackUtmParams: boolean
114
+ /** Track device type */
115
+ trackDeviceType: boolean
116
+ /** Track hash changes as page views */
117
+ trackHashChanges: boolean
118
+ /** Track outbound link clicks */
119
+ trackOutboundLinks: boolean
120
+ /** IPs whose events are dropped at ingest (exact match). */
121
+ excludedIps?: string[]
122
+ /** Path patterns (regex) whose events are dropped at ingest. */
123
+ excludedPaths?: string[]
124
+ }
125
+
126
+ /** API settings */
127
+ api: {
128
+ /** Base path for API routes */
129
+ basePath: string
130
+ /** CORS allowed origins */
131
+ corsOrigins: string[]
132
+ }
133
+
134
+ /** Aggregation settings */
135
+ aggregation: {
136
+ /** Batch size for processing events */
137
+ batchSize: number
138
+ /** Enable hourly aggregation */
139
+ hourlyEnabled: boolean
140
+ /** Enable daily aggregation */
141
+ dailyEnabled: boolean
142
+ /** Enable monthly aggregation */
143
+ monthlyEnabled: boolean
144
+ }
145
+
146
+ /**
147
+ * Scale settings for high-throughput scenarios
148
+ * Based on learnings from Fathom Analytics' DynamoDB experience
149
+ */
150
+ scale: {
151
+ /**
152
+ * Enable SQS-based write buffering
153
+ * Routes /collect writes through SQS for better handling of traffic spikes
154
+ */
155
+ sqsBuffering: {
156
+ enabled: boolean
157
+ /** SQS Queue URL */
158
+ queueUrl?: string
159
+ /** Dead letter queue URL for failed messages */
160
+ deadLetterQueueUrl?: string
161
+ /** Max retries before sending to DLQ */
162
+ maxRetries: number
163
+ }
164
+
165
+ /**
166
+ * DAX (DynamoDB Accelerator) caching for reads
167
+ * Dramatically reduces read latency and cost for hot data
168
+ */
169
+ daxCaching: {
170
+ enabled: boolean
171
+ /** DAX cluster endpoint */
172
+ endpoint?: string
173
+ /** TTL for cached items in seconds */
174
+ ttlSeconds: number
175
+ }
176
+
177
+ /**
178
+ * Partition sharding for high-volume sites
179
+ * Distributes writes across multiple partitions to avoid throttling
180
+ */
181
+ partitionSharding: {
182
+ enabled: boolean
183
+ /** Number of shards per site (default: 10) */
184
+ shardCount: number
185
+ /** Sites that require sharding (empty = all sites) */
186
+ enabledSiteIds: string[]
187
+ }
188
+
189
+ /**
190
+ * Write coalescing to reduce duplicate writes
191
+ * Combines multiple updates to same key within a time window
192
+ */
193
+ writeCoalescing: {
194
+ enabled: boolean
195
+ /** Window in milliseconds to coalesce writes */
196
+ windowMs: number
197
+ }
198
+
199
+ /**
200
+ * Batch write settings
201
+ */
202
+ batchWrites: {
203
+ enabled: boolean
204
+ /** Max items per batch (DynamoDB limit: 25) */
205
+ maxBatchSize: number
206
+ /** Flush interval in milliseconds */
207
+ flushIntervalMs: number
208
+ }
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Partial configuration for user overrides
214
+ */
215
+ export type UserAnalyticsConfig = {
216
+ [K in keyof AnalyticsConfig]?: K extends 'table'
217
+ ? Partial<AnalyticsTableConfig>
218
+ : K extends 'retention' | 'privacy' | 'tracking' | 'api' | 'aggregation'
219
+ ? Partial<AnalyticsConfig[K]>
220
+ : K extends 'scale'
221
+ ? {
222
+ sqsBuffering?: Partial<AnalyticsConfig['scale']['sqsBuffering']>
223
+ daxCaching?: Partial<AnalyticsConfig['scale']['daxCaching']>
224
+ partitionSharding?: Partial<AnalyticsConfig['scale']['partitionSharding']>
225
+ writeCoalescing?: Partial<AnalyticsConfig['scale']['writeCoalescing']>
226
+ batchWrites?: Partial<AnalyticsConfig['scale']['batchWrites']>
227
+ }
228
+ : AnalyticsConfig[K]
229
+ }
230
+
231
+ // ============================================================================
232
+ // Default Values
233
+ // ============================================================================
234
+
235
+ const ONE_DAY = 24 * 60 * 60
236
+ const THIRTY_DAYS = 30 * ONE_DAY
237
+ const NINETY_DAYS = 90 * ONE_DAY
238
+ const TWO_YEARS = 2 * 365 * ONE_DAY
239
+
240
+ /**
241
+ * Complete default configuration
242
+ */
243
+ export const defaultConfig: AnalyticsConfig = {
244
+ table: defaultAnalyticsConfig,
245
+ region: 'us-east-1',
246
+ endpoint: undefined,
247
+
248
+ retention: {
249
+ rawEventTtl: THIRTY_DAYS,
250
+ hourlyAggregateTtl: NINETY_DAYS,
251
+ dailyAggregateTtl: TWO_YEARS,
252
+ monthlyAggregateTtl: undefined, // Keep forever
253
+ },
254
+
255
+ privacy: {
256
+ hashVisitorIds: true,
257
+ collectGeolocation: false,
258
+ honorDnt: true,
259
+ ipAnonymization: 'partial',
260
+ },
261
+
262
+ tracking: {
263
+ trackReferrers: true,
264
+ trackUtmParams: true,
265
+ trackDeviceType: true,
266
+ trackHashChanges: false,
267
+ trackOutboundLinks: true,
268
+ excludedIps: [],
269
+ excludedPaths: [],
270
+ },
271
+
272
+ api: {
273
+ basePath: '/api/analytics',
274
+ corsOrigins: ['*'],
275
+ },
276
+
277
+ aggregation: {
278
+ batchSize: 100,
279
+ hourlyEnabled: true,
280
+ dailyEnabled: true,
281
+ monthlyEnabled: true,
282
+ },
283
+
284
+ // Scale settings - disabled by default for simplicity
285
+ // Enable these for high-traffic sites (>10k req/sec)
286
+ scale: {
287
+ sqsBuffering: {
288
+ enabled: false,
289
+ queueUrl: undefined,
290
+ deadLetterQueueUrl: undefined,
291
+ maxRetries: 3,
292
+ },
293
+ daxCaching: {
294
+ enabled: false,
295
+ endpoint: undefined,
296
+ ttlSeconds: 300, // 5 minutes default cache
297
+ },
298
+ partitionSharding: {
299
+ enabled: false,
300
+ shardCount: 10,
301
+ enabledSiteIds: [], // Empty = disabled
302
+ },
303
+ writeCoalescing: {
304
+ enabled: true, // Enabled by default - reduces redundant writes
305
+ windowMs: 100,
306
+ },
307
+ batchWrites: {
308
+ enabled: true, // Enabled by default - more efficient
309
+ maxBatchSize: 25, // DynamoDB limit
310
+ flushIntervalMs: 1000,
311
+ },
312
+ },
313
+ }
314
+
315
+ // ============================================================================
316
+ // Configuration Functions
317
+ // ============================================================================
318
+
319
+ /**
320
+ * Merge user config with defaults
321
+ */
322
+ export function defineConfig(userConfig: UserAnalyticsConfig = {}): AnalyticsConfig {
323
+ return {
324
+ table: {
325
+ ...defaultConfig.table,
326
+ ...userConfig.table,
327
+ singleTable: {
328
+ ...defaultConfig.table.singleTable,
329
+ ...userConfig.table?.singleTable,
330
+ },
331
+ globalSecondaryIndexes: userConfig.table?.globalSecondaryIndexes ?? defaultConfig.table.globalSecondaryIndexes,
332
+ localSecondaryIndexes: userConfig.table?.localSecondaryIndexes ?? defaultConfig.table.localSecondaryIndexes,
333
+ },
334
+ region: userConfig.region ?? defaultConfig.region,
335
+ endpoint: userConfig.endpoint ?? defaultConfig.endpoint,
336
+ retention: {
337
+ ...defaultConfig.retention,
338
+ ...userConfig.retention,
339
+ },
340
+ privacy: {
341
+ ...defaultConfig.privacy,
342
+ ...userConfig.privacy,
343
+ },
344
+ tracking: {
345
+ ...defaultConfig.tracking,
346
+ ...userConfig.tracking,
347
+ },
348
+ api: {
349
+ ...defaultConfig.api,
350
+ ...userConfig.api,
351
+ },
352
+ aggregation: {
353
+ ...defaultConfig.aggregation,
354
+ ...userConfig.aggregation,
355
+ },
356
+ scale: {
357
+ sqsBuffering: {
358
+ ...defaultConfig.scale.sqsBuffering,
359
+ ...userConfig.scale?.sqsBuffering,
360
+ },
361
+ daxCaching: {
362
+ ...defaultConfig.scale.daxCaching,
363
+ ...userConfig.scale?.daxCaching,
364
+ },
365
+ partitionSharding: {
366
+ ...defaultConfig.scale.partitionSharding,
367
+ ...userConfig.scale?.partitionSharding,
368
+ },
369
+ writeCoalescing: {
370
+ ...defaultConfig.scale.writeCoalescing,
371
+ ...userConfig.scale?.writeCoalescing,
372
+ },
373
+ batchWrites: {
374
+ ...defaultConfig.scale.batchWrites,
375
+ ...userConfig.scale?.batchWrites,
376
+ },
377
+ },
378
+ }
379
+ }
380
+
381
+ /**
382
+ * Create table configuration from analytics config
383
+ */
384
+ export function getTableConfig(config: AnalyticsConfig): {
385
+ tableName: string
386
+ billingMode: BillingMode
387
+ partitionKey: string
388
+ sortKey: string
389
+ gsis: GSIDefinition[]
390
+ ttlAttribute?: string
391
+ } {
392
+ return {
393
+ tableName: config.table.tableName,
394
+ billingMode: config.table.billingMode,
395
+ partitionKey: config.table.singleTable.partitionKeyName,
396
+ sortKey: config.table.singleTable.sortKeyName,
397
+ gsis: config.table.globalSecondaryIndexes,
398
+ ttlAttribute: config.table.ttlAttributeName,
399
+ }
400
+ }
401
+
402
+ /**
403
+ * Get TTL value for a given entity type
404
+ */
405
+ export function getTtlForEntity(
406
+ config: AnalyticsConfig,
407
+ entityType: 'raw' | 'hourly' | 'daily' | 'monthly',
408
+ ): number | undefined {
409
+ const now = Math.floor(Date.now() / 1000)
410
+
411
+ switch (entityType) {
412
+ case 'raw':
413
+ return now + config.retention.rawEventTtl
414
+ case 'hourly':
415
+ return now + config.retention.hourlyAggregateTtl
416
+ case 'daily':
417
+ return now + config.retention.dailyAggregateTtl
418
+ case 'monthly':
419
+ return config.retention.monthlyAggregateTtl
420
+ ? now + config.retention.monthlyAggregateTtl
421
+ : undefined
422
+ default:
423
+ return undefined
424
+ }
425
+ }
426
+
427
+ // ============================================================================
428
+ // Config Singleton
429
+ // ============================================================================
430
+
431
+ let _config: AnalyticsConfig | null = null
432
+
433
+ /**
434
+ * Set global analytics configuration
435
+ */
436
+ export function setConfig(config: UserAnalyticsConfig): AnalyticsConfig {
437
+ _config = defineConfig(config)
438
+ return _config
439
+ }
440
+
441
+ /**
442
+ * Get current analytics configuration
443
+ */
444
+ export function getConfig(): AnalyticsConfig {
445
+ if (!_config) {
446
+ _config = defaultConfig
447
+ }
448
+ return _config
449
+ }
450
+
451
+ /**
452
+ * Reset configuration to defaults
453
+ */
454
+ export function resetConfig(): void {
455
+ _config = null
456
+ }