@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,162 @@
1
+ import type { Model } from './types'
2
+
3
+ /**
4
+ * Aggregated stats (pre-computed for fast dashboard queries)
5
+ *
6
+ * DynamoDB Keys:
7
+ * - PK: SITE#{siteId}
8
+ * - SK: STATS#{PERIOD}#{periodStart}
9
+ */
10
+ const AggregatedStatsModel: Model = {
11
+ name: 'AggregatedStats',
12
+ table: 'analytics', // Single-table design
13
+ primaryKey: ['siteId', 'period', 'periodStart'], // Composite key
14
+ autoIncrement: false,
15
+
16
+ traits: {
17
+ useTimestamps: true,
18
+ useTtl: true, // Hourly stats can be TTL'd after time
19
+ },
20
+
21
+ belongsTo: ['Site'],
22
+
23
+ attributes: {
24
+ siteId: {
25
+ required: true,
26
+ fillable: true,
27
+ validation: {
28
+ rule: 'string',
29
+ },
30
+ },
31
+
32
+ period: {
33
+ required: true,
34
+ fillable: true,
35
+ validation: {
36
+ rule: 'enum:hour,day,month',
37
+ },
38
+ comment: 'Aggregation period type',
39
+ },
40
+
41
+ periodStart: {
42
+ required: true,
43
+ fillable: true,
44
+ validation: {
45
+ rule: 'string',
46
+ },
47
+ comment: 'Period start timestamp (ISO string)',
48
+ },
49
+
50
+ pageViews: {
51
+ required: true,
52
+ fillable: true,
53
+ default: 0,
54
+ validation: {
55
+ rule: 'number',
56
+ },
57
+ cast: 'number',
58
+ },
59
+
60
+ uniqueVisitors: {
61
+ required: true,
62
+ fillable: true,
63
+ default: 0,
64
+ validation: {
65
+ rule: 'number',
66
+ },
67
+ cast: 'number',
68
+ },
69
+
70
+ sessions: {
71
+ required: true,
72
+ fillable: true,
73
+ default: 0,
74
+ validation: {
75
+ rule: 'number',
76
+ },
77
+ cast: 'number',
78
+ },
79
+
80
+ bounces: {
81
+ required: true,
82
+ fillable: true,
83
+ default: 0,
84
+ validation: {
85
+ rule: 'number',
86
+ },
87
+ cast: 'number',
88
+ },
89
+
90
+ bounceRate: {
91
+ required: true,
92
+ fillable: true,
93
+ default: 0,
94
+ validation: {
95
+ rule: 'number',
96
+ },
97
+ cast: 'number',
98
+ comment: '0-1 decimal',
99
+ },
100
+
101
+ avgSessionDuration: {
102
+ required: true,
103
+ fillable: true,
104
+ default: 0,
105
+ validation: {
106
+ rule: 'number',
107
+ },
108
+ cast: 'number',
109
+ comment: 'Milliseconds',
110
+ },
111
+
112
+ avgPagesPerSession: {
113
+ required: true,
114
+ fillable: true,
115
+ default: 0,
116
+ validation: {
117
+ rule: 'number',
118
+ },
119
+ cast: 'number',
120
+ },
121
+
122
+ totalTimeOnSite: {
123
+ required: true,
124
+ fillable: true,
125
+ default: 0,
126
+ validation: {
127
+ rule: 'number',
128
+ },
129
+ cast: 'number',
130
+ comment: 'Milliseconds',
131
+ },
132
+
133
+ newVisitors: {
134
+ required: true,
135
+ fillable: true,
136
+ default: 0,
137
+ validation: {
138
+ rule: 'number',
139
+ },
140
+ cast: 'number',
141
+ },
142
+
143
+ returningVisitors: {
144
+ required: true,
145
+ fillable: true,
146
+ default: 0,
147
+ validation: {
148
+ rule: 'number',
149
+ },
150
+ cast: 'number',
151
+ },
152
+ },
153
+
154
+ // DynamoDB single-table key patterns
155
+ dynamodb: {
156
+ pk: (stats: { siteId: string }): string => `SITE#${stats.siteId}`,
157
+ sk: (stats: { period: string, periodStart: string }): string =>
158
+ `STATS#${stats.period.toUpperCase()}#${stats.periodStart}`,
159
+ },
160
+ }
161
+
162
+ export default AggregatedStatsModel
@@ -0,0 +1,141 @@
1
+ import type { Model } from './types'
2
+
3
+ /**
4
+ * UTM Campaign stats
5
+ *
6
+ * DynamoDB Keys:
7
+ * - PK: SITE#{siteId}
8
+ * - SK: CAMPSTATS#{PERIOD}#{periodStart}#{utmSource}[#{utmCampaign}]
9
+ */
10
+ const CampaignStatsModel: Model = {
11
+ name: 'CampaignStats',
12
+ table: 'analytics', // Single-table design
13
+ primaryKey: ['siteId', 'period', 'periodStart', 'utmSource'], // Composite key
14
+ autoIncrement: false,
15
+
16
+ traits: {
17
+ useTtl: true,
18
+ },
19
+
20
+ belongsTo: ['Site'],
21
+
22
+ attributes: {
23
+ siteId: {
24
+ required: true,
25
+ fillable: true,
26
+ validation: {
27
+ rule: 'string',
28
+ },
29
+ },
30
+
31
+ period: {
32
+ required: true,
33
+ fillable: true,
34
+ validation: {
35
+ rule: 'enum:hour,day,month',
36
+ },
37
+ },
38
+
39
+ periodStart: {
40
+ required: true,
41
+ fillable: true,
42
+ validation: {
43
+ rule: 'string',
44
+ },
45
+ },
46
+
47
+ utmSource: {
48
+ required: true,
49
+ fillable: true,
50
+ validation: {
51
+ rule: 'string',
52
+ },
53
+ },
54
+
55
+ utmMedium: {
56
+ fillable: true,
57
+ validation: {
58
+ rule: 'string',
59
+ },
60
+ },
61
+
62
+ utmCampaign: {
63
+ fillable: true,
64
+ validation: {
65
+ rule: 'string',
66
+ },
67
+ },
68
+
69
+ visitors: {
70
+ required: true,
71
+ fillable: true,
72
+ default: 0,
73
+ validation: {
74
+ rule: 'number',
75
+ },
76
+ cast: 'number',
77
+ },
78
+
79
+ pageViews: {
80
+ required: true,
81
+ fillable: true,
82
+ default: 0,
83
+ validation: {
84
+ rule: 'number',
85
+ },
86
+ cast: 'number',
87
+ },
88
+
89
+ conversions: {
90
+ required: true,
91
+ fillable: true,
92
+ default: 0,
93
+ validation: {
94
+ rule: 'number',
95
+ },
96
+ cast: 'number',
97
+ comment: 'Custom events marked as goals',
98
+ },
99
+
100
+ conversionRate: {
101
+ required: true,
102
+ fillable: true,
103
+ default: 0,
104
+ validation: {
105
+ rule: 'number',
106
+ },
107
+ cast: 'number',
108
+ comment: '0-1 decimal',
109
+ },
110
+
111
+ revenue: {
112
+ required: true,
113
+ fillable: true,
114
+ default: 0,
115
+ validation: {
116
+ rule: 'number',
117
+ },
118
+ cast: 'number',
119
+ },
120
+
121
+ bounceRate: {
122
+ required: true,
123
+ fillable: true,
124
+ default: 0,
125
+ validation: {
126
+ rule: 'number',
127
+ },
128
+ cast: 'number',
129
+ comment: '0-1 decimal',
130
+ },
131
+ },
132
+
133
+ // DynamoDB single-table key patterns
134
+ dynamodb: {
135
+ pk: (stats: { siteId: string }): string => `SITE#${stats.siteId}`,
136
+ sk: (stats: { period: string, periodStart: string, utmSource: string, utmCampaign?: string }): string =>
137
+ `CAMPSTATS#${stats.period.toUpperCase()}#${stats.periodStart}#${stats.utmSource}${stats.utmCampaign ? `#${stats.utmCampaign}` : ''}`,
138
+ },
139
+ }
140
+
141
+ export default CampaignStatsModel
@@ -0,0 +1,135 @@
1
+ import type { Model } from './types'
2
+
3
+ /**
4
+ * Conversion record (goal completion)
5
+ *
6
+ * DynamoDB Keys:
7
+ * - PK: SITE#{siteId}
8
+ * - SK: CONVERSION#{timestamp}#{conversionId}
9
+ * - GSI1PK: SITE#{siteId}#GOAL#{goalId}
10
+ * - GSI1SK: CONVERSION#{timestamp}
11
+ */
12
+ const ConversionModel: Model = {
13
+ name: 'Conversion',
14
+ table: 'analytics', // Single-table design
15
+ primaryKey: 'id',
16
+ autoIncrement: false,
17
+
18
+ traits: {
19
+ useUuid: true,
20
+ useTtl: true,
21
+ },
22
+
23
+ belongsTo: ['Site', 'Goal', 'Session'],
24
+
25
+ attributes: {
26
+ id: {
27
+ required: true,
28
+ unique: true,
29
+ validation: {
30
+ rule: 'string',
31
+ },
32
+ },
33
+
34
+ siteId: {
35
+ required: true,
36
+ fillable: true,
37
+ validation: {
38
+ rule: 'string',
39
+ },
40
+ },
41
+
42
+ goalId: {
43
+ required: true,
44
+ fillable: true,
45
+ validation: {
46
+ rule: 'string',
47
+ },
48
+ },
49
+
50
+ visitorId: {
51
+ required: true,
52
+ fillable: true,
53
+ validation: {
54
+ rule: 'string',
55
+ },
56
+ },
57
+
58
+ sessionId: {
59
+ required: true,
60
+ fillable: true,
61
+ validation: {
62
+ rule: 'string',
63
+ },
64
+ },
65
+
66
+ value: {
67
+ fillable: true,
68
+ validation: {
69
+ rule: 'number',
70
+ },
71
+ cast: 'number',
72
+ comment: 'Conversion value (from goal or event)',
73
+ },
74
+
75
+ path: {
76
+ required: true,
77
+ fillable: true,
78
+ validation: {
79
+ rule: 'string',
80
+ },
81
+ comment: 'Page path where conversion occurred',
82
+ },
83
+
84
+ referrerSource: {
85
+ fillable: true,
86
+ validation: {
87
+ rule: 'string',
88
+ },
89
+ },
90
+
91
+ utmSource: {
92
+ fillable: true,
93
+ validation: {
94
+ rule: 'string',
95
+ },
96
+ },
97
+
98
+ utmCampaign: {
99
+ fillable: true,
100
+ validation: {
101
+ rule: 'string',
102
+ },
103
+ },
104
+
105
+ timestamp: {
106
+ required: true,
107
+ fillable: true,
108
+ validation: {
109
+ rule: 'date',
110
+ },
111
+ cast: 'datetime',
112
+ },
113
+
114
+ ttl: {
115
+ fillable: true,
116
+ validation: {
117
+ rule: 'number',
118
+ },
119
+ cast: 'number',
120
+ },
121
+ },
122
+
123
+ // DynamoDB single-table key patterns
124
+ dynamodb: {
125
+ pk: (conv: { siteId: string }): string => `SITE#${conv.siteId}`,
126
+ sk: (conv: { timestamp: Date, id: string }): string =>
127
+ `CONVERSION#${conv.timestamp.toISOString()}#${conv.id}`,
128
+ gsi1pk: (conv: { siteId: string, goalId: string }): string =>
129
+ `SITE#${conv.siteId}#GOAL#${conv.goalId}`,
130
+ gsi1sk: (conv: { timestamp: Date }): string =>
131
+ `CONVERSION#${conv.timestamp.toISOString()}`,
132
+ },
133
+ }
134
+
135
+ export default ConversionModel
@@ -0,0 +1,123 @@
1
+ import type { Model } from './types'
2
+
3
+ /**
4
+ * Custom event (non-pageview events like button clicks, form submissions)
5
+ *
6
+ * DynamoDB Keys:
7
+ * - PK: SITE#{siteId}
8
+ * - SK: EVENT#{timestamp}#{eventId}
9
+ * - GSI1PK: SITE#{siteId}#EVENTNAME#{eventName}
10
+ * - GSI1SK: EVENT#{timestamp}
11
+ */
12
+ const CustomEventModel: Model = {
13
+ name: 'CustomEvent',
14
+ table: 'analytics', // Single-table design
15
+ primaryKey: 'id',
16
+ autoIncrement: false,
17
+
18
+ traits: {
19
+ useUuid: true,
20
+ useTtl: true,
21
+ },
22
+
23
+ belongsTo: ['Site', 'Session'],
24
+
25
+ attributes: {
26
+ id: {
27
+ required: true,
28
+ unique: true,
29
+ validation: {
30
+ rule: 'string',
31
+ },
32
+ },
33
+
34
+ siteId: {
35
+ required: true,
36
+ fillable: true,
37
+ validation: {
38
+ rule: 'string',
39
+ },
40
+ },
41
+
42
+ visitorId: {
43
+ required: true,
44
+ fillable: true,
45
+ validation: {
46
+ rule: 'string',
47
+ },
48
+ comment: 'Hashed, anonymous visitor identifier',
49
+ },
50
+
51
+ sessionId: {
52
+ required: true,
53
+ fillable: true,
54
+ validation: {
55
+ rule: 'string',
56
+ },
57
+ },
58
+
59
+ name: {
60
+ required: true,
61
+ fillable: true,
62
+ validation: {
63
+ rule: 'string',
64
+ },
65
+ comment: 'Event name (e.g., button_click, form_submit)',
66
+ },
67
+
68
+ value: {
69
+ fillable: true,
70
+ validation: {
71
+ rule: 'number',
72
+ },
73
+ cast: 'number',
74
+ comment: 'Optional value for revenue tracking',
75
+ },
76
+
77
+ properties: {
78
+ fillable: true,
79
+ cast: 'json',
80
+ comment: 'Custom event properties',
81
+ },
82
+
83
+ path: {
84
+ required: true,
85
+ fillable: true,
86
+ validation: {
87
+ rule: 'string',
88
+ },
89
+ comment: 'Page path where event occurred',
90
+ },
91
+
92
+ timestamp: {
93
+ required: true,
94
+ fillable: true,
95
+ validation: {
96
+ rule: 'date',
97
+ },
98
+ cast: 'datetime',
99
+ },
100
+
101
+ ttl: {
102
+ fillable: true,
103
+ validation: {
104
+ rule: 'number',
105
+ },
106
+ cast: 'number',
107
+ comment: 'Unix timestamp for DynamoDB TTL auto-deletion',
108
+ },
109
+ },
110
+
111
+ // DynamoDB single-table key patterns
112
+ dynamodb: {
113
+ pk: (event: { siteId: string }): string => `SITE#${event.siteId}`,
114
+ sk: (event: { timestamp: Date, id: string }): string =>
115
+ `EVENT#${event.timestamp.toISOString()}#${event.id}`,
116
+ gsi1pk: (event: { siteId: string, name: string }): string =>
117
+ `SITE#${event.siteId}#EVENTNAME#${event.name}`,
118
+ gsi1sk: (event: { timestamp: Date }): string =>
119
+ `EVENT#${event.timestamp.toISOString()}`,
120
+ },
121
+ }
122
+
123
+ export default CustomEventModel
@@ -0,0 +1,105 @@
1
+ import type { Model } from './types'
2
+
3
+ /**
4
+ * Device/Browser stats
5
+ *
6
+ * DynamoDB Keys:
7
+ * - PK: SITE#{siteId}
8
+ * - SK: DEVICESTATS#{PERIOD}#{periodStart}#{dimension}#{value}
9
+ */
10
+ const DeviceStatsModel: Model = {
11
+ name: 'DeviceStats',
12
+ table: 'analytics', // Single-table design
13
+ primaryKey: ['siteId', 'period', 'periodStart', 'dimension', 'value'], // Composite key
14
+ autoIncrement: false,
15
+
16
+ traits: {
17
+ useTtl: true,
18
+ },
19
+
20
+ belongsTo: ['Site'],
21
+
22
+ attributes: {
23
+ siteId: {
24
+ required: true,
25
+ fillable: true,
26
+ validation: {
27
+ rule: 'string',
28
+ },
29
+ },
30
+
31
+ period: {
32
+ required: true,
33
+ fillable: true,
34
+ validation: {
35
+ rule: 'enum:hour,day,month',
36
+ },
37
+ },
38
+
39
+ periodStart: {
40
+ required: true,
41
+ fillable: true,
42
+ validation: {
43
+ rule: 'string',
44
+ },
45
+ },
46
+
47
+ dimension: {
48
+ required: true,
49
+ fillable: true,
50
+ validation: {
51
+ rule: 'enum:device,browser,os,screen',
52
+ },
53
+ comment: 'Type of device dimension',
54
+ },
55
+
56
+ value: {
57
+ required: true,
58
+ fillable: true,
59
+ validation: {
60
+ rule: 'string',
61
+ },
62
+ comment: 'e.g., "mobile", "Chrome", "Windows", "1920x1080"',
63
+ },
64
+
65
+ visitors: {
66
+ required: true,
67
+ fillable: true,
68
+ default: 0,
69
+ validation: {
70
+ rule: 'number',
71
+ },
72
+ cast: 'number',
73
+ },
74
+
75
+ pageViews: {
76
+ required: true,
77
+ fillable: true,
78
+ default: 0,
79
+ validation: {
80
+ rule: 'number',
81
+ },
82
+ cast: 'number',
83
+ },
84
+
85
+ bounceRate: {
86
+ required: true,
87
+ fillable: true,
88
+ default: 0,
89
+ validation: {
90
+ rule: 'number',
91
+ },
92
+ cast: 'number',
93
+ comment: '0-1 decimal',
94
+ },
95
+ },
96
+
97
+ // DynamoDB single-table key patterns
98
+ dynamodb: {
99
+ pk: (stats: { siteId: string }): string => `SITE#${stats.siteId}`,
100
+ sk: (stats: { period: string, periodStart: string, dimension: string, value: string }): string =>
101
+ `DEVICESTATS#${stats.period.toUpperCase()}#${stats.periodStart}#${stats.dimension}#${stats.value}`,
102
+ },
103
+ }
104
+
105
+ export default DeviceStatsModel