@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
@@ -0,0 +1,465 @@
1
+ /**
2
+ * Analytics Data Composables
3
+ *
4
+ * Vue 3 composables for fetching and managing analytics data.
5
+ * Can also be used as standalone async functions in non-Vue contexts.
6
+ */
7
+
8
+ import type {
9
+ AnalyticsApiConfig,
10
+ DashboardSummary,
11
+ FetchOptions,
12
+ GoalConversion,
13
+ RealtimeData,
14
+ TimeSeriesDataPoint,
15
+ TopItem,
16
+ } from '../types'
17
+
18
+ // ============================================================================
19
+ // API Client
20
+ // ============================================================================
21
+
22
+ export class AnalyticsClient {
23
+ private baseUrl: string
24
+ private siteId: string
25
+ private headers: Record<string, string>
26
+
27
+ constructor(config: AnalyticsApiConfig) {
28
+ this.baseUrl = config.baseUrl.replace(/\/$/, '')
29
+ this.siteId = config.siteId
30
+ this.headers = {
31
+ 'Content-Type': 'application/json',
32
+ ...config.headers,
33
+ }
34
+ }
35
+
36
+ private async fetch<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
37
+ const url = `${this.baseUrl}${endpoint}`
38
+ const response = await fetch(url, {
39
+ ...options,
40
+ headers: {
41
+ ...this.headers,
42
+ ...options.headers,
43
+ },
44
+ })
45
+
46
+ if (!response.ok) {
47
+ throw new Error(`Analytics API error: ${response.status} ${response.statusText}`)
48
+ }
49
+
50
+ return response.json() as Promise<T>
51
+ }
52
+
53
+ /**
54
+ * Get dashboard summary stats
55
+ */
56
+ async getStats(options: FetchOptions = {}): Promise<DashboardSummary> {
57
+ const params = new URLSearchParams()
58
+ if (options.startDate)
59
+ params.set('startDate', options.startDate.toISOString())
60
+ if (options.endDate)
61
+ params.set('endDate', options.endDate.toISOString())
62
+ if (options.period)
63
+ params.set('period', options.period)
64
+
65
+ const query = params.toString()
66
+ return this.fetch(`/api/analytics/sites/${this.siteId}/stats${query ? `?${query}` : ''}`)
67
+ }
68
+
69
+ /**
70
+ * Get realtime visitor data
71
+ */
72
+ async getRealtime(minutes = 5): Promise<RealtimeData> {
73
+ return this.fetch(`/api/analytics/sites/${this.siteId}/realtime?minutes=${minutes}`)
74
+ }
75
+
76
+ /**
77
+ * Get top pages
78
+ */
79
+ async getTopPages(options: FetchOptions = {}): Promise<TopItem[]> {
80
+ const params = new URLSearchParams()
81
+ if (options.startDate)
82
+ params.set('startDate', options.startDate.toISOString())
83
+ if (options.endDate)
84
+ params.set('endDate', options.endDate.toISOString())
85
+ if (options.limit)
86
+ params.set('limit', String(options.limit))
87
+
88
+ const query = params.toString()
89
+ const response = await this.fetch<{ pages: TopItem[] }>(
90
+ `/api/analytics/sites/${this.siteId}/pages${query ? `?${query}` : ''}`,
91
+ )
92
+ return response.pages || []
93
+ }
94
+
95
+ /**
96
+ * Get top referrers
97
+ */
98
+ async getTopReferrers(options: FetchOptions = {}): Promise<TopItem[]> {
99
+ const params = new URLSearchParams()
100
+ if (options.startDate)
101
+ params.set('startDate', options.startDate.toISOString())
102
+ if (options.endDate)
103
+ params.set('endDate', options.endDate.toISOString())
104
+ if (options.limit)
105
+ params.set('limit', String(options.limit))
106
+
107
+ const query = params.toString()
108
+ const response = await this.fetch<{ referrers: TopItem[] }>(
109
+ `/api/analytics/sites/${this.siteId}/referrers${query ? `?${query}` : ''}`,
110
+ )
111
+ return response.referrers || []
112
+ }
113
+
114
+ /**
115
+ * Get device breakdown
116
+ */
117
+ async getDevices(options: FetchOptions = {}): Promise<TopItem[]> {
118
+ const params = new URLSearchParams()
119
+ if (options.startDate)
120
+ params.set('startDate', options.startDate.toISOString())
121
+ if (options.endDate)
122
+ params.set('endDate', options.endDate.toISOString())
123
+
124
+ const query = params.toString()
125
+ const response = await this.fetch<{ devices: TopItem[] }>(
126
+ `/api/analytics/sites/${this.siteId}/devices${query ? `?${query}` : ''}`,
127
+ )
128
+ return response.devices || []
129
+ }
130
+
131
+ /**
132
+ * Get browser stats
133
+ */
134
+ async getBrowsers(options: FetchOptions = {}): Promise<TopItem[]> {
135
+ const params = new URLSearchParams()
136
+ if (options.startDate)
137
+ params.set('startDate', options.startDate.toISOString())
138
+ if (options.endDate)
139
+ params.set('endDate', options.endDate.toISOString())
140
+ if (options.limit)
141
+ params.set('limit', String(options.limit))
142
+
143
+ const query = params.toString()
144
+ const response = await this.fetch<{ browsers: TopItem[] }>(
145
+ `/api/analytics/sites/${this.siteId}/browsers${query ? `?${query}` : ''}`,
146
+ )
147
+ return response.browsers || []
148
+ }
149
+
150
+ /**
151
+ * Get country stats
152
+ */
153
+ async getCountries(options: FetchOptions = {}): Promise<TopItem[]> {
154
+ const params = new URLSearchParams()
155
+ if (options.startDate)
156
+ params.set('startDate', options.startDate.toISOString())
157
+ if (options.endDate)
158
+ params.set('endDate', options.endDate.toISOString())
159
+ if (options.limit)
160
+ params.set('limit', String(options.limit))
161
+
162
+ const query = params.toString()
163
+ const response = await this.fetch<{ countries: TopItem[] }>(
164
+ `/api/analytics/sites/${this.siteId}/countries${query ? `?${query}` : ''}`,
165
+ )
166
+ return response.countries || []
167
+ }
168
+
169
+ /**
170
+ * Get region stats
171
+ */
172
+ async getRegions(options: FetchOptions & { country?: string } = {}): Promise<TopItem[]> {
173
+ const params = new URLSearchParams()
174
+ if (options.startDate)
175
+ params.set('startDate', options.startDate.toISOString())
176
+ if (options.endDate)
177
+ params.set('endDate', options.endDate.toISOString())
178
+ if (options.limit)
179
+ params.set('limit', String(options.limit))
180
+ if (options.country)
181
+ params.set('country', options.country)
182
+
183
+ const query = params.toString()
184
+ const response = await this.fetch<{ regions: TopItem[] }>(
185
+ `/api/analytics/sites/${this.siteId}/regions${query ? `?${query}` : ''}`,
186
+ )
187
+ return response.regions || []
188
+ }
189
+
190
+ /**
191
+ * Get city stats
192
+ */
193
+ async getCities(options: FetchOptions & { country?: string, region?: string } = {}): Promise<TopItem[]> {
194
+ const params = new URLSearchParams()
195
+ if (options.startDate)
196
+ params.set('startDate', options.startDate.toISOString())
197
+ if (options.endDate)
198
+ params.set('endDate', options.endDate.toISOString())
199
+ if (options.limit)
200
+ params.set('limit', String(options.limit))
201
+ if (options.country)
202
+ params.set('country', options.country)
203
+ if (options.region)
204
+ params.set('region', options.region)
205
+
206
+ const query = params.toString()
207
+ const response = await this.fetch<{ cities: TopItem[] }>(
208
+ `/api/analytics/sites/${this.siteId}/cities${query ? `?${query}` : ''}`,
209
+ )
210
+ return response.cities || []
211
+ }
212
+
213
+ /**
214
+ * Get time series data
215
+ */
216
+ async getTimeSeries(options: FetchOptions = {}): Promise<TimeSeriesDataPoint[]> {
217
+ const params = new URLSearchParams()
218
+ if (options.startDate)
219
+ params.set('startDate', options.startDate.toISOString())
220
+ if (options.endDate)
221
+ params.set('endDate', options.endDate.toISOString())
222
+ if (options.period)
223
+ params.set('period', options.period)
224
+
225
+ const query = params.toString()
226
+ const response = await this.fetch<{ timeSeries: TimeSeriesDataPoint[] }>(
227
+ `/api/analytics/sites/${this.siteId}/timeseries${query ? `?${query}` : ''}`,
228
+ )
229
+ return response.timeSeries || []
230
+ }
231
+
232
+ /**
233
+ * Get goal conversions
234
+ */
235
+ async getGoals(options: FetchOptions = {}): Promise<GoalConversion[]> {
236
+ const params = new URLSearchParams()
237
+ if (options.startDate)
238
+ params.set('startDate', options.startDate.toISOString())
239
+ if (options.endDate)
240
+ params.set('endDate', options.endDate.toISOString())
241
+
242
+ const query = params.toString()
243
+ const response = await this.fetch<{ goals: GoalConversion[] }>(
244
+ `/api/analytics/sites/${this.siteId}/goals/stats${query ? `?${query}` : ''}`,
245
+ )
246
+ return response.goals || []
247
+ }
248
+ }
249
+
250
+ // ============================================================================
251
+ // Vue Composable (for Vue 3)
252
+ // ============================================================================
253
+
254
+ /**
255
+ * Create analytics composable for Vue 3
256
+ *
257
+ * @example
258
+ * ```vue
259
+ * <script setup>
260
+ * import { useAnalytics } from 'dynamodb-tooling/analytics/dashboard'
261
+ *
262
+ * const {
263
+ * stats,
264
+ * realtime,
265
+ * topPages,
266
+ * loading,
267
+ * error,
268
+ * refresh,
269
+ * setDateRange
270
+ * } = useAnalytics({
271
+ * baseUrl: '/api',
272
+ * siteId: 'my-site'
273
+ * })
274
+ * </script>
275
+ * ```
276
+ */
277
+ export interface UseAnalyticsOptions extends AnalyticsApiConfig {
278
+ /** Auto-refresh interval in ms (0 to disable) */
279
+ refreshInterval?: number
280
+ /** Initial date range preset */
281
+ initialDateRange?: '7d' | '30d' | '90d' | 'today' | 'yesterday'
282
+ /** Fetch realtime data */
283
+ fetchRealtime?: boolean
284
+ /** Realtime refresh interval in ms */
285
+ realtimeInterval?: number
286
+ }
287
+
288
+ export interface UseAnalyticsReturn {
289
+ // Data
290
+ stats: { value: DashboardSummary | null }
291
+ realtime: { value: RealtimeData | null }
292
+ topPages: { value: TopItem[] }
293
+ topReferrers: { value: TopItem[] }
294
+ devices: { value: TopItem[] }
295
+ browsers: { value: TopItem[] }
296
+ countries: { value: TopItem[] }
297
+ timeSeries: { value: TimeSeriesDataPoint[] }
298
+ goals: { value: GoalConversion[] }
299
+
300
+ // State
301
+ loading: { value: boolean }
302
+ error: { value: Error | null }
303
+ dateRange: { value: { start: Date, end: Date } }
304
+
305
+ // Actions
306
+ refresh: () => Promise<void>
307
+ setDateRange: (start: Date, end: Date) => void
308
+ setDatePreset: (preset: string) => void
309
+ }
310
+
311
+ /**
312
+ * Create analytics composable
313
+ * Note: This returns a factory function that works with Vue's reactivity system
314
+ */
315
+ export function createAnalyticsComposable(options: UseAnalyticsOptions): {
316
+ client: AnalyticsClient
317
+ getInitialDateRange: () => { start: Date, end: Date }
318
+ fetchStats: (dateRange: { start: Date, end: Date }) => Promise<DashboardSummary>
319
+ fetchRealtime: () => Promise<RealtimeData>
320
+ fetchTopPages: (dateRange: { start: Date, end: Date }, limit?: number) => Promise<unknown[]>
321
+ fetchTopReferrers: (dateRange: { start: Date, end: Date }, limit?: number) => Promise<unknown[]>
322
+ fetchDevices: (dateRange: { start: Date, end: Date }) => Promise<unknown>
323
+ fetchBrowsers: (dateRange: { start: Date, end: Date }, limit?: number) => Promise<unknown[]>
324
+ fetchCountries: (dateRange: { start: Date, end: Date }, limit?: number) => Promise<unknown[]>
325
+ fetchRegions: (dateRange: { start: Date, end: Date }, country?: string, limit?: number) => Promise<unknown[]>
326
+ fetchCities: (dateRange: { start: Date, end: Date }, country?: string, region?: string, limit?: number) => Promise<unknown[]>
327
+ fetchTimeSeries: (dateRange: { start: Date, end: Date }) => Promise<unknown[]>
328
+ fetchGoals: (dateRange: { start: Date, end: Date }) => Promise<unknown[]>
329
+ } {
330
+ const client = new AnalyticsClient(options)
331
+
332
+ // Get initial date range
333
+ const getInitialDateRange = () => {
334
+ const end = new Date()
335
+ const start = new Date()
336
+ const preset = options.initialDateRange || '7d'
337
+
338
+ switch (preset) {
339
+ case 'today':
340
+ start.setHours(0, 0, 0, 0)
341
+ break
342
+ case 'yesterday':
343
+ start.setDate(start.getDate() - 1)
344
+ start.setHours(0, 0, 0, 0)
345
+ end.setDate(end.getDate() - 1)
346
+ end.setHours(23, 59, 59, 999)
347
+ break
348
+ case '30d':
349
+ start.setDate(start.getDate() - 30)
350
+ break
351
+ case '90d':
352
+ start.setDate(start.getDate() - 90)
353
+ break
354
+ case '7d':
355
+ default:
356
+ start.setDate(start.getDate() - 7)
357
+ break
358
+ }
359
+
360
+ return { start, end }
361
+ }
362
+
363
+ return {
364
+ client,
365
+ getInitialDateRange,
366
+ fetchStats: (dateRange: { start: Date, end: Date }): Promise<DashboardSummary> =>
367
+ client.getStats({ startDate: dateRange.start, endDate: dateRange.end }),
368
+ fetchRealtime: (): Promise<RealtimeData> => client.getRealtime(),
369
+ fetchTopPages: (dateRange: { start: Date, end: Date }, limit = 10): Promise<unknown[]> =>
370
+ client.getTopPages({ startDate: dateRange.start, endDate: dateRange.end, limit }),
371
+ fetchTopReferrers: (dateRange: { start: Date, end: Date }, limit = 10): Promise<unknown[]> =>
372
+ client.getTopReferrers({ startDate: dateRange.start, endDate: dateRange.end, limit }),
373
+ fetchDevices: (dateRange: { start: Date, end: Date }): Promise<unknown> =>
374
+ client.getDevices({ startDate: dateRange.start, endDate: dateRange.end }),
375
+ fetchBrowsers: (dateRange: { start: Date, end: Date }, limit = 10): Promise<unknown[]> =>
376
+ client.getBrowsers({ startDate: dateRange.start, endDate: dateRange.end, limit }),
377
+ fetchCountries: (dateRange: { start: Date, end: Date }, limit = 10): Promise<unknown[]> =>
378
+ client.getCountries({ startDate: dateRange.start, endDate: dateRange.end, limit }),
379
+ fetchRegions: (dateRange: { start: Date, end: Date }, country?: string, limit = 10): Promise<unknown[]> =>
380
+ client.getRegions({ startDate: dateRange.start, endDate: dateRange.end, country, limit }),
381
+ fetchCities: (dateRange: { start: Date, end: Date }, country?: string, region?: string, limit = 10): Promise<unknown[]> =>
382
+ client.getCities({ startDate: dateRange.start, endDate: dateRange.end, country, region, limit }),
383
+ fetchTimeSeries: (dateRange: { start: Date, end: Date }): Promise<unknown[]> =>
384
+ client.getTimeSeries({ startDate: dateRange.start, endDate: dateRange.end }),
385
+ fetchGoals: (dateRange: { start: Date, end: Date }): Promise<unknown[]> =>
386
+ client.getGoals({ startDate: dateRange.start, endDate: dateRange.end }),
387
+ }
388
+ }
389
+
390
+ // ============================================================================
391
+ // Standalone Hooks (Framework-Agnostic)
392
+ // ============================================================================
393
+
394
+ /**
395
+ * Fetch all dashboard data at once
396
+ */
397
+ // eslint-disable-next-line pickier/no-unused-vars
398
+ export async function fetchDashboardData(
399
+ config: AnalyticsApiConfig,
400
+ options: FetchOptions = {},
401
+ ): Promise<{
402
+ stats: DashboardSummary
403
+ realtime: RealtimeData
404
+ topPages: TopItem[]
405
+ topReferrers: TopItem[]
406
+ devices: TopItem[]
407
+ timeSeries: TimeSeriesDataPoint[]
408
+ }> {
409
+ const client = new AnalyticsClient(config)
410
+
411
+ const [stats, realtime, topPages, topReferrers, devices, timeSeries] = await Promise.all([
412
+ client.getStats(options),
413
+ client.getRealtime(),
414
+ client.getTopPages({ ...options, limit: 10 }),
415
+ client.getTopReferrers({ ...options, limit: 10 }),
416
+ client.getDevices(options),
417
+ client.getTimeSeries(options),
418
+ ])
419
+
420
+ return {
421
+ stats,
422
+ realtime,
423
+ topPages,
424
+ topReferrers,
425
+ devices,
426
+ timeSeries,
427
+ }
428
+ }
429
+
430
+ /**
431
+ * Create a polling function for realtime data
432
+ */
433
+ export function createRealtimePoller(
434
+ config: AnalyticsApiConfig,
435
+ callback: (data: RealtimeData) => void,
436
+ interval = 5000,
437
+ ): { start: () => void, stop: () => void } {
438
+ const client = new AnalyticsClient(config)
439
+ let timerId: ReturnType<typeof setInterval> | null = null
440
+
441
+ const poll = async () => {
442
+ try {
443
+ const data = await client.getRealtime()
444
+ callback(data)
445
+ }
446
+ catch (error) {
447
+ console.error('Realtime polling error:', error)
448
+ }
449
+ }
450
+
451
+ return {
452
+ start: () => {
453
+ if (timerId)
454
+ return
455
+ poll() // Initial fetch
456
+ timerId = setInterval(poll, interval)
457
+ },
458
+ stop: () => {
459
+ if (timerId) {
460
+ clearInterval(timerId)
461
+ timerId = null
462
+ }
463
+ },
464
+ }
465
+ }