@supalytics/cli 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,10 +20,19 @@ bun add -g @supalytics/cli
20
20
 
21
21
  ## Setup
22
22
 
23
- Get your API key from the Settings page at [www.supalytics.co](https://www.supalytics.co), then:
23
+ ### Quick Start
24
24
 
25
25
  ```bash
26
- supalytics login <your-api-key>
26
+ supalytics init
27
+ ```
28
+
29
+ Opens browser to log in, creates your site, and gives you the tracking snippet.
30
+
31
+ ### Manual Setup
32
+
33
+ ```bash
34
+ supalytics login # Opens browser for authentication
35
+ supalytics sites add # Create a new site
27
36
  ```
28
37
 
29
38
  ## Usage
@@ -46,6 +55,14 @@ supalytics realtime # Current visitors
46
55
  supalytics realtime --watch # Auto-refresh every 30s
47
56
  ```
48
57
 
58
+ ### Trend
59
+
60
+ ```bash
61
+ supalytics trend # Daily visitor trend with bar chart
62
+ supalytics trend --period 7d # Last 7 days
63
+ supalytics trend --compact # Compact sparkline only
64
+ ```
65
+
49
66
  ### Breakdowns
50
67
 
51
68
  ```bash
@@ -83,6 +100,7 @@ supalytics events signup --property plan # Breakdown by property
83
100
  All commands support:
84
101
 
85
102
  - `-s, --site <domain>` - Query a specific site
103
+ - `-t, --test` - Query localhost/test data instead of production
86
104
  - `--json` - Output raw JSON
87
105
  - `--no-revenue` - Exclude revenue metrics
88
106
  - `-f, --filter <filter>` - Filter data (format: `field:operator:value`)
@@ -96,15 +114,31 @@ All commands support:
96
114
  -f "referrer:is:twitter.com"
97
115
  ```
98
116
 
99
- ## Multi-site Support
117
+ ## Site Management
100
118
 
101
119
  ```bash
102
- supalytics login <api-key> # Add a site
103
120
  supalytics sites # List all sites
121
+ supalytics sites add # Create a new site
122
+ supalytics sites update # Update site settings
104
123
  supalytics default <domain> # Set default site
105
124
  supalytics stats -s other.com # Query specific site
106
125
  ```
107
126
 
127
+ ## Shell Completions
128
+
129
+ Enable tab completion for your shell:
130
+
131
+ ```bash
132
+ # Bash (add to ~/.bashrc)
133
+ eval "$(supalytics completions bash)"
134
+
135
+ # Zsh (add to ~/.zshrc)
136
+ eval "$(supalytics completions zsh)"
137
+
138
+ # Fish
139
+ supalytics completions fish > ~/.config/fish/completions/supalytics.fish
140
+ ```
141
+
108
142
  ## License
109
143
 
110
144
  Apache-2.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supalytics/cli",
3
- "version": "0.1.2",
3
+ "version": "0.3.0",
4
4
  "description": "CLI for Supalytics web analytics",
5
5
  "type": "module",
6
6
  "bin": {
@@ -33,6 +33,7 @@
33
33
  },
34
34
  "dependencies": {
35
35
  "chalk": "^5.6.2",
36
- "commander": "^14.0.2"
36
+ "commander": "^14.0.2",
37
+ "open": "^10.1.0"
37
38
  }
38
39
  }
package/src/api.ts CHANGED
@@ -12,6 +12,7 @@ export interface QueryRequest {
12
12
  limit?: number;
13
13
  offset?: number;
14
14
  include_revenue?: boolean;
15
+ is_dev?: boolean; // Test mode: query localhost data instead of production
15
16
  }
16
17
 
17
18
  export interface QueryResult {
@@ -159,7 +160,8 @@ export interface PropertyBreakdownResponse {
159
160
  export async function listEvents(
160
161
  site: string,
161
162
  period: string = "30d",
162
- limit: number = 100
163
+ limit: number = 100,
164
+ isDev: boolean = false
163
165
  ): Promise<EventsResponse> {
164
166
  const apiKey = await getApiKeyForSite(site);
165
167
 
@@ -176,6 +178,9 @@ export async function listEvents(
176
178
  }
177
179
 
178
180
  const params = new URLSearchParams({ period, limit: String(limit) });
181
+ if (isDev) {
182
+ params.set("is_dev", "true");
183
+ }
179
184
  const response = await fetch(`${API_BASE}/v1/events?${params}`, {
180
185
  headers: { Authorization: `Bearer ${apiKey}` },
181
186
  });
@@ -194,7 +199,8 @@ export async function listEvents(
194
199
  export async function getEventProperties(
195
200
  site: string,
196
201
  eventName: string,
197
- period: string = "30d"
202
+ period: string = "30d",
203
+ isDev: boolean = false
198
204
  ): Promise<PropertyKeysResponse> {
199
205
  const apiKey = await getApiKeyForSite(site);
200
206
 
@@ -203,6 +209,9 @@ export async function getEventProperties(
203
209
  }
204
210
 
205
211
  const params = new URLSearchParams({ period });
212
+ if (isDev) {
213
+ params.set("is_dev", "true");
214
+ }
206
215
  const response = await fetch(
207
216
  `${API_BASE}/v1/events/${encodeURIComponent(eventName)}/properties?${params}`,
208
217
  { headers: { Authorization: `Bearer ${apiKey}` } }
@@ -225,7 +234,8 @@ export async function getPropertyBreakdown(
225
234
  propertyKey: string,
226
235
  period: string = "30d",
227
236
  limit: number = 100,
228
- includeRevenue: boolean = false
237
+ includeRevenue: boolean = false,
238
+ isDev: boolean = false
229
239
  ): Promise<PropertyBreakdownResponse> {
230
240
  const apiKey = await getApiKeyForSite(site);
231
241
 
@@ -238,6 +248,9 @@ export async function getPropertyBreakdown(
238
248
  limit: String(limit),
239
249
  include_revenue: String(includeRevenue),
240
250
  });
251
+ if (isDev) {
252
+ params.set("is_dev", "true");
253
+ }
241
254
  const response = await fetch(
242
255
  `${API_BASE}/v1/events/${encodeURIComponent(eventName)}/properties/${encodeURIComponent(propertyKey)}?${params}`,
243
256
  { headers: { Authorization: `Bearer ${apiKey}` } }
@@ -283,7 +296,7 @@ export interface RealtimeResponse {
283
296
  /**
284
297
  * Get realtime visitors
285
298
  */
286
- export async function getRealtime(site: string): Promise<RealtimeResponse> {
299
+ export async function getRealtime(site: string, isDev: boolean = false): Promise<RealtimeResponse> {
287
300
  const apiKey = await getApiKeyForSite(site);
288
301
 
289
302
  if (!apiKey) {
@@ -298,7 +311,12 @@ export async function getRealtime(site: string): Promise<RealtimeResponse> {
298
311
  );
299
312
  }
300
313
 
301
- const response = await fetch(`${API_BASE}/v1/realtime`, {
314
+ const params = new URLSearchParams();
315
+ if (isDev) {
316
+ params.set("is_dev", "true");
317
+ }
318
+ const url = params.toString() ? `${API_BASE}/v1/realtime?${params}` : `${API_BASE}/v1/realtime`;
319
+ const response = await fetch(url, {
302
320
  headers: { Authorization: `Bearer ${apiKey}` },
303
321
  });
304
322
 
@@ -0,0 +1,328 @@
1
+ import { Command } from "commander";
2
+
3
+ const BASH_COMPLETION = `# Supalytics CLI Bash Completion
4
+ # Add this to ~/.bashrc or ~/.bash_profile:
5
+ # eval "$(supalytics completions bash)"
6
+
7
+ _supalytics_completions() {
8
+ local cur prev commands opts
9
+ COMPREPLY=()
10
+ cur="\${COMP_WORDS[COMP_CWORD]}"
11
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
12
+
13
+ commands="init login logout sites default remove stats pages referrers countries trend query events realtime completions help"
14
+
15
+ # Main command completion
16
+ if [[ \${COMP_CWORD} -eq 1 ]]; then
17
+ COMPREPLY=( $(compgen -W "\${commands}" -- \${cur}) )
18
+ return 0
19
+ fi
20
+
21
+ # Subcommand options
22
+ case "\${COMP_WORDS[1]}" in
23
+ stats)
24
+ opts="today yesterday week month year 7d 14d 30d 90d 12mo all --site --start --end --filter --all --no-revenue --json"
25
+ ;;
26
+ pages|referrers|countries)
27
+ opts="--site --period --start --end --limit --filter --no-revenue --json"
28
+ ;;
29
+ trend)
30
+ opts="--site --period --start --end --filter --no-revenue --compact --json"
31
+ ;;
32
+ query)
33
+ opts="--site --metrics --dimensions --filter --sort --timezone --period --start --end --limit --offset --no-revenue --json"
34
+ ;;
35
+ events)
36
+ opts="--site --period --property --limit --no-revenue --json"
37
+ ;;
38
+ realtime)
39
+ opts="--site --json --watch"
40
+ ;;
41
+ init)
42
+ opts=""
43
+ ;;
44
+ login)
45
+ opts=""
46
+ ;;
47
+ logout)
48
+ opts=""
49
+ ;;
50
+ sites)
51
+ opts="add update"
52
+ ;;
53
+ completions)
54
+ opts="bash zsh fish"
55
+ ;;
56
+ *)
57
+ opts=""
58
+ ;;
59
+ esac
60
+
61
+ COMPREPLY=( $(compgen -W "\${opts}" -- \${cur}) )
62
+ return 0
63
+ }
64
+
65
+ complete -F _supalytics_completions supalytics
66
+ `;
67
+
68
+ const ZSH_COMPLETION = `#compdef supalytics
69
+ # Supalytics CLI Zsh Completion
70
+ # Add this to ~/.zshrc:
71
+ # eval "$(supalytics completions zsh)"
72
+
73
+ _supalytics() {
74
+ local -a commands
75
+ commands=(
76
+ 'init:Quick setup - login, create site, get snippet'
77
+ 'login:Authenticate with Supalytics'
78
+ 'logout:Log out (keeps site API keys)'
79
+ 'sites:List and manage configured sites'
80
+ 'default:Set the default site'
81
+ 'remove:Remove a site'
82
+ 'stats:Overview stats (pageviews, visitors, bounce rate, revenue)'
83
+ 'pages:Top pages by visitors'
84
+ 'referrers:Top referrers'
85
+ 'countries:Traffic by country'
86
+ 'trend:Daily visitor trend'
87
+ 'query:Flexible query with custom metrics and dimensions'
88
+ 'events:List and explore custom events'
89
+ 'realtime:Live visitors on your site right now'
90
+ 'completions:Generate shell completions'
91
+ 'help:Display help for command'
92
+ )
93
+
94
+ local -a period_opts
95
+ period_opts=(
96
+ 'today:Today only'
97
+ 'yesterday:Yesterday only'
98
+ 'week:This week'
99
+ 'month:This month'
100
+ 'year:This year'
101
+ '7d:Last 7 days'
102
+ '14d:Last 14 days'
103
+ '30d:Last 30 days'
104
+ '90d:Last 90 days'
105
+ '12mo:Last 12 months'
106
+ 'all:All time'
107
+ )
108
+
109
+ _arguments -C \\
110
+ '1: :->command' \\
111
+ '*:: :->args'
112
+
113
+ case \$state in
114
+ command)
115
+ _describe 'command' commands
116
+ ;;
117
+ args)
118
+ case \$words[1] in
119
+ stats)
120
+ _arguments \\
121
+ '1: :->period' \\
122
+ '--site[Site to query]:domain:' \\
123
+ '--start[Start date]:date:' \\
124
+ '--end[End date]:date:' \\
125
+ '*--filter[Filter]:filter:' \\
126
+ '--all[Show detailed breakdown]' \\
127
+ '--no-revenue[Exclude revenue metrics]' \\
128
+ '--json[Output as JSON]'
129
+ case \$state in
130
+ period)
131
+ _describe 'period' period_opts
132
+ ;;
133
+ esac
134
+ ;;
135
+ pages|referrers|countries)
136
+ _arguments \\
137
+ '--site[Site to query]:domain:' \\
138
+ '--period[Time period]:period:(7d 14d 30d 90d 12mo all)' \\
139
+ '--start[Start date]:date:' \\
140
+ '--end[End date]:date:' \\
141
+ '--limit[Number of results]:limit:' \\
142
+ '*--filter[Filter]:filter:' \\
143
+ '--no-revenue[Exclude revenue metrics]' \\
144
+ '--json[Output as JSON]'
145
+ ;;
146
+ trend)
147
+ _arguments \\
148
+ '--site[Site to query]:domain:' \\
149
+ '--period[Time period]:period:(7d 14d 30d 90d 12mo all)' \\
150
+ '--start[Start date]:date:' \\
151
+ '--end[End date]:date:' \\
152
+ '*--filter[Filter]:filter:' \\
153
+ '--no-revenue[Exclude revenue metrics]' \\
154
+ '--compact[Show compact sparkline only]' \\
155
+ '--json[Output as JSON]'
156
+ ;;
157
+ query)
158
+ _arguments \\
159
+ '--site[Site to query]:domain:' \\
160
+ '--metrics[Metrics]:metrics:(visitors bounce_rate avg_session_duration revenue conversions)' \\
161
+ '--dimensions[Dimensions]:dimensions:(page referrer country region city browser os device date hour event)' \\
162
+ '*--filter[Filter]:filter:' \\
163
+ '--sort[Sort by field]:sort:' \\
164
+ '--timezone[Timezone]:timezone:' \\
165
+ '--period[Time period]:period:(7d 14d 30d 90d 12mo all)' \\
166
+ '--start[Start date]:date:' \\
167
+ '--end[End date]:date:' \\
168
+ '--limit[Number of results]:limit:' \\
169
+ '--offset[Skip results]:offset:' \\
170
+ '--no-revenue[Exclude revenue metrics]' \\
171
+ '--json[Output as JSON]'
172
+ ;;
173
+ events)
174
+ _arguments \\
175
+ '1:event name:' \\
176
+ '--site[Site to query]:domain:' \\
177
+ '--period[Time period]:period:(7d 14d 30d 90d 12mo all)' \\
178
+ '--property[Property key]:property:' \\
179
+ '--limit[Number of results]:limit:' \\
180
+ '--no-revenue[Exclude revenue]' \\
181
+ '--json[Output as JSON]'
182
+ ;;
183
+ realtime)
184
+ _arguments \\
185
+ '--site[Site to query]:domain:' \\
186
+ '--json[Output as JSON]' \\
187
+ '--watch[Auto-refresh every 30 seconds]'
188
+ ;;
189
+ init)
190
+ _arguments '1:identifier:'
191
+ ;;
192
+ login)
193
+ ;;
194
+ logout)
195
+ ;;
196
+ sites)
197
+ local -a sites_commands
198
+ sites_commands=(
199
+ 'add:Create a new site'
200
+ 'update:Update a site domain'
201
+ )
202
+ _describe 'sites command' sites_commands
203
+ ;;
204
+ default|remove)
205
+ _arguments '1:domain:'
206
+ ;;
207
+ completions)
208
+ _arguments '1:shell:(bash zsh fish)'
209
+ ;;
210
+ esac
211
+ ;;
212
+ esac
213
+ }
214
+
215
+ _supalytics
216
+ `;
217
+
218
+ const FISH_COMPLETION = `# Supalytics CLI Fish Completion
219
+ # Add this to ~/.config/fish/completions/supalytics.fish:
220
+ # supalytics completions fish > ~/.config/fish/completions/supalytics.fish
221
+
222
+ # Disable file completion by default
223
+ complete -c supalytics -f
224
+
225
+ # Commands
226
+ complete -c supalytics -n "__fish_use_subcommand" -a "init" -d "Quick setup - login, create site, get snippet"
227
+ complete -c supalytics -n "__fish_use_subcommand" -a "login" -d "Authenticate with Supalytics"
228
+ complete -c supalytics -n "__fish_use_subcommand" -a "logout" -d "Log out (keeps site API keys)"
229
+ complete -c supalytics -n "__fish_use_subcommand" -a "sites" -d "List and manage configured sites"
230
+ complete -c supalytics -n "__fish_use_subcommand" -a "default" -d "Set the default site"
231
+ complete -c supalytics -n "__fish_use_subcommand" -a "remove" -d "Remove a site"
232
+ complete -c supalytics -n "__fish_use_subcommand" -a "stats" -d "Overview stats"
233
+ complete -c supalytics -n "__fish_use_subcommand" -a "pages" -d "Top pages by visitors"
234
+ complete -c supalytics -n "__fish_use_subcommand" -a "referrers" -d "Top referrers"
235
+ complete -c supalytics -n "__fish_use_subcommand" -a "countries" -d "Traffic by country"
236
+ complete -c supalytics -n "__fish_use_subcommand" -a "trend" -d "Daily visitor trend"
237
+ complete -c supalytics -n "__fish_use_subcommand" -a "query" -d "Flexible query with custom metrics"
238
+ complete -c supalytics -n "__fish_use_subcommand" -a "events" -d "List and explore custom events"
239
+ complete -c supalytics -n "__fish_use_subcommand" -a "realtime" -d "Live visitors right now"
240
+ complete -c supalytics -n "__fish_use_subcommand" -a "completions" -d "Generate shell completions"
241
+ complete -c supalytics -n "__fish_use_subcommand" -a "help" -d "Display help for command"
242
+
243
+ # Stats options
244
+ complete -c supalytics -n "__fish_seen_subcommand_from stats" -a "today yesterday week month year 7d 14d 30d 90d 12mo all"
245
+ complete -c supalytics -n "__fish_seen_subcommand_from stats" -l site -s s -d "Site to query"
246
+ complete -c supalytics -n "__fish_seen_subcommand_from stats" -l start -d "Start date"
247
+ complete -c supalytics -n "__fish_seen_subcommand_from stats" -l end -d "End date"
248
+ complete -c supalytics -n "__fish_seen_subcommand_from stats" -l filter -s f -d "Filter"
249
+ complete -c supalytics -n "__fish_seen_subcommand_from stats" -l all -s a -d "Show detailed breakdown"
250
+ complete -c supalytics -n "__fish_seen_subcommand_from stats" -l no-revenue -d "Exclude revenue"
251
+ complete -c supalytics -n "__fish_seen_subcommand_from stats" -l json -d "Output as JSON"
252
+
253
+ # Pages/Referrers/Countries options
254
+ complete -c supalytics -n "__fish_seen_subcommand_from pages referrers countries" -l site -s s -d "Site to query"
255
+ complete -c supalytics -n "__fish_seen_subcommand_from pages referrers countries" -l period -s p -d "Time period" -a "7d 14d 30d 90d 12mo all"
256
+ complete -c supalytics -n "__fish_seen_subcommand_from pages referrers countries" -l start -d "Start date"
257
+ complete -c supalytics -n "__fish_seen_subcommand_from pages referrers countries" -l end -d "End date"
258
+ complete -c supalytics -n "__fish_seen_subcommand_from pages referrers countries" -l limit -s l -d "Number of results"
259
+ complete -c supalytics -n "__fish_seen_subcommand_from pages referrers countries" -l filter -s f -d "Filter"
260
+ complete -c supalytics -n "__fish_seen_subcommand_from pages referrers countries" -l no-revenue -d "Exclude revenue"
261
+ complete -c supalytics -n "__fish_seen_subcommand_from pages referrers countries" -l json -d "Output as JSON"
262
+
263
+ # Trend options
264
+ complete -c supalytics -n "__fish_seen_subcommand_from trend" -l site -s s -d "Site to query"
265
+ complete -c supalytics -n "__fish_seen_subcommand_from trend" -l period -s p -d "Time period" -a "7d 14d 30d 90d 12mo all"
266
+ complete -c supalytics -n "__fish_seen_subcommand_from trend" -l start -d "Start date"
267
+ complete -c supalytics -n "__fish_seen_subcommand_from trend" -l end -d "End date"
268
+ complete -c supalytics -n "__fish_seen_subcommand_from trend" -l filter -s f -d "Filter"
269
+ complete -c supalytics -n "__fish_seen_subcommand_from trend" -l no-revenue -d "Exclude revenue"
270
+ complete -c supalytics -n "__fish_seen_subcommand_from trend" -l compact -d "Show compact sparkline"
271
+ complete -c supalytics -n "__fish_seen_subcommand_from trend" -l json -d "Output as JSON"
272
+
273
+ # Query options
274
+ complete -c supalytics -n "__fish_seen_subcommand_from query" -l site -s s -d "Site to query"
275
+ complete -c supalytics -n "__fish_seen_subcommand_from query" -l metrics -s m -d "Metrics" -a "visitors bounce_rate avg_session_duration revenue conversions"
276
+ complete -c supalytics -n "__fish_seen_subcommand_from query" -l dimensions -s d -d "Dimensions" -a "page referrer country region city browser os device date hour event"
277
+ complete -c supalytics -n "__fish_seen_subcommand_from query" -l filter -s f -d "Filter"
278
+ complete -c supalytics -n "__fish_seen_subcommand_from query" -l sort -d "Sort by field"
279
+ complete -c supalytics -n "__fish_seen_subcommand_from query" -l timezone -d "Timezone"
280
+ complete -c supalytics -n "__fish_seen_subcommand_from query" -l period -s p -d "Time period" -a "7d 14d 30d 90d 12mo all"
281
+ complete -c supalytics -n "__fish_seen_subcommand_from query" -l start -d "Start date"
282
+ complete -c supalytics -n "__fish_seen_subcommand_from query" -l end -d "End date"
283
+ complete -c supalytics -n "__fish_seen_subcommand_from query" -l limit -s l -d "Number of results"
284
+ complete -c supalytics -n "__fish_seen_subcommand_from query" -l offset -d "Skip results"
285
+ complete -c supalytics -n "__fish_seen_subcommand_from query" -l no-revenue -d "Exclude revenue"
286
+ complete -c supalytics -n "__fish_seen_subcommand_from query" -l json -d "Output as JSON"
287
+
288
+ # Events options
289
+ complete -c supalytics -n "__fish_seen_subcommand_from events" -l site -s s -d "Site to query"
290
+ complete -c supalytics -n "__fish_seen_subcommand_from events" -l period -s p -d "Time period" -a "7d 14d 30d 90d 12mo all"
291
+ complete -c supalytics -n "__fish_seen_subcommand_from events" -l property -d "Property key"
292
+ complete -c supalytics -n "__fish_seen_subcommand_from events" -l limit -s l -d "Number of results"
293
+ complete -c supalytics -n "__fish_seen_subcommand_from events" -l no-revenue -d "Exclude revenue"
294
+ complete -c supalytics -n "__fish_seen_subcommand_from events" -l json -d "Output as JSON"
295
+
296
+ # Realtime options
297
+ complete -c supalytics -n "__fish_seen_subcommand_from realtime" -l site -s s -d "Site to query"
298
+ complete -c supalytics -n "__fish_seen_subcommand_from realtime" -l json -d "Output as JSON"
299
+ complete -c supalytics -n "__fish_seen_subcommand_from realtime" -l watch -s w -d "Auto-refresh"
300
+
301
+ # Sites subcommands
302
+ complete -c supalytics -n "__fish_seen_subcommand_from sites; and not __fish_seen_subcommand_from add update" -a "add" -d "Create a new site"
303
+ complete -c supalytics -n "__fish_seen_subcommand_from sites; and not __fish_seen_subcommand_from add update" -a "update" -d "Update a site domain"
304
+ complete -c supalytics -n "__fish_seen_subcommand_from sites; and __fish_seen_subcommand_from update" -l domain -s d -d "New domain name"
305
+
306
+ # Completions command
307
+ complete -c supalytics -n "__fish_seen_subcommand_from completions" -a "bash zsh fish"
308
+ `;
309
+
310
+ export const completionsCommand = new Command("completions")
311
+ .description("Generate shell completions")
312
+ .argument("<shell>", "Shell type: bash, zsh, or fish")
313
+ .action((shell: string) => {
314
+ switch (shell.toLowerCase()) {
315
+ case "bash":
316
+ console.log(BASH_COMPLETION);
317
+ break;
318
+ case "zsh":
319
+ console.log(ZSH_COMPLETION);
320
+ break;
321
+ case "fish":
322
+ console.log(FISH_COMPLETION);
323
+ break;
324
+ default:
325
+ console.error(`Unknown shell: ${shell}. Use bash, zsh, or fish.`);
326
+ process.exit(1);
327
+ }
328
+ });
@@ -14,6 +14,7 @@ export const countriesCommand = new Command("countries")
14
14
  .option("-f, --filter <filters...>", "Filters in format 'field:operator:value'")
15
15
  .option("--no-revenue", "Exclude revenue metrics")
16
16
  .option("--json", "Output as JSON")
17
+ .option("-t, --test", "Test mode: query localhost data instead of production")
17
18
  .action(async (options) => {
18
19
  const site = options.site || (await getDefaultSite());
19
20
 
@@ -51,6 +52,7 @@ export const countriesCommand = new Command("countries")
51
52
  date_range: dateRange,
52
53
  limit: parseInt(options.limit),
53
54
  include_revenue: options.revenue !== false,
55
+ is_dev: options.test || false,
54
56
  });
55
57
 
56
58
  if (options.json) {
@@ -9,14 +9,14 @@ Examples:
9
9
  # List all events
10
10
  supalytics events
11
11
 
12
- # List properties for an event
12
+ # Show event stats and properties
13
13
  supalytics events signup
14
14
 
15
15
  # Get breakdown of a property
16
16
  supalytics events signup --property plan
17
17
 
18
- # With revenue
19
- supalytics events signup --property plan --revenue`;
18
+ # Without revenue
19
+ supalytics events signup --property plan --no-revenue`;
20
20
 
21
21
  function displayPropertyKeys(response: PropertyKeysResponse, event: string, json: boolean) {
22
22
  if (json) {
@@ -48,8 +48,9 @@ export const eventsCommand = new Command("events")
48
48
  .option("-p, --period <period>", "Time period: 7d, 14d, 30d, 90d, 12mo, all", "30d")
49
49
  .option("--property <key>", "Get breakdown for a specific property")
50
50
  .option("-l, --limit <number>", "Number of results", "20")
51
- .option("--revenue", "Include revenue in property breakdown")
51
+ .option("--no-revenue", "Exclude revenue metrics")
52
52
  .option("--json", "Output as JSON")
53
+ .option("-t, --test", "Test mode: query localhost data instead of production")
53
54
  .action(async (event, options) => {
54
55
  const site = options.site || (await getDefaultSite());
55
56
 
@@ -61,7 +62,7 @@ export const eventsCommand = new Command("events")
61
62
  try {
62
63
  // If no event specified, list all events
63
64
  if (!event) {
64
- const response = await listEvents(site, options.period, parseInt(options.limit));
65
+ const response = await listEvents(site, options.period, parseInt(options.limit), options.test || false);
65
66
 
66
67
  if (options.json) {
67
68
  console.log(JSON.stringify(response, null, 2));
@@ -94,7 +95,8 @@ export const eventsCommand = new Command("events")
94
95
  options.property,
95
96
  options.period,
96
97
  parseInt(options.limit),
97
- options.revenue
98
+ options.revenue !== false,
99
+ options.test || false
98
100
  );
99
101
 
100
102
  if (options.json) {
@@ -113,7 +115,7 @@ export const eventsCommand = new Command("events")
113
115
 
114
116
  for (const v of response.data) {
115
117
  let line = ` ${chalk.cyan(v.value)} ${formatNumber(v.visitors)} visitors ${formatNumber(v.count)} events`;
116
- if (options.revenue && v.revenue !== null) {
118
+ if (options.revenue !== false && v.revenue !== null) {
117
119
  line += ` ${chalk.green("$" + (v.revenue / 100).toFixed(2))}`;
118
120
  }
119
121
  console.log(line);
@@ -122,9 +124,47 @@ export const eventsCommand = new Command("events")
122
124
  return;
123
125
  }
124
126
 
125
- // If just event name, show properties
126
- const response = await getEventProperties(site, event, options.period);
127
- displayPropertyKeys(response, event, options.json);
127
+ // If just event name, show event stats + properties
128
+ const [eventsResponse, propsResponse] = await Promise.all([
129
+ listEvents(site, options.period, 100, options.test || false), // Get all events to find this one
130
+ getEventProperties(site, event, options.period, options.test || false),
131
+ ]);
132
+
133
+ // Find the specific event stats
134
+ const eventData = eventsResponse.data.find((e) => e.name === event);
135
+
136
+ if (options.json) {
137
+ console.log(JSON.stringify({
138
+ event: eventData || { name: event, count: 0, visitors: 0, has_properties: false },
139
+ properties: propsResponse.data,
140
+ meta: propsResponse.meta,
141
+ }, null, 2));
142
+ return;
143
+ }
144
+
145
+ const [startDate, endDate] = propsResponse.meta.date_range;
146
+ console.log();
147
+ console.log(chalk.bold(event), chalk.dim(`${startDate} → ${endDate}`));
148
+ console.log();
149
+
150
+ // Show event stats
151
+ if (eventData) {
152
+ console.log(` ${formatNumber(eventData.visitors)} visitors ${formatNumber(eventData.count)} events`);
153
+ } else {
154
+ console.log(chalk.dim(" No data for this event"));
155
+ }
156
+ console.log();
157
+
158
+ // Show properties
159
+ if (propsResponse.data.length > 0) {
160
+ console.log(chalk.dim(" PROPERTIES"));
161
+ for (const key of propsResponse.data) {
162
+ console.log(` ${chalk.cyan(key)}`);
163
+ }
164
+ console.log();
165
+ console.log(chalk.dim(` Use: supalytics events ${event} --property <key>`));
166
+ }
167
+ console.log();
128
168
 
129
169
  } catch (error) {
130
170
  console.error(chalk.red(`Error: ${(error as Error).message}`));