@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 +38 -4
- package/package.json +3 -2
- package/src/api.ts +23 -5
- package/src/commands/completions.ts +328 -0
- package/src/commands/countries.ts +2 -0
- package/src/commands/events.ts +50 -10
- package/src/commands/init.ts +97 -0
- package/src/commands/login.ts +179 -55
- package/src/commands/logout.ts +2 -2
- package/src/commands/pages.ts +2 -0
- package/src/commands/query.ts +49 -24
- package/src/commands/realtime.ts +2 -1
- package/src/commands/referrers.ts +2 -0
- package/src/commands/sites.ts +194 -2
- package/src/commands/stats.ts +96 -9
- package/src/commands/trend.ts +15 -1
- package/src/config.ts +62 -0
- package/src/index.ts +22 -8
- package/src/ui.ts +111 -0
package/README.md
CHANGED
|
@@ -20,10 +20,19 @@ bun add -g @supalytics/cli
|
|
|
20
20
|
|
|
21
21
|
## Setup
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
### Quick Start
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
supalytics
|
|
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
|
-
##
|
|
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.
|
|
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
|
|
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) {
|
package/src/commands/events.ts
CHANGED
|
@@ -9,14 +9,14 @@ Examples:
|
|
|
9
9
|
# List all events
|
|
10
10
|
supalytics events
|
|
11
11
|
|
|
12
|
-
#
|
|
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
|
-
#
|
|
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", "
|
|
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
|
|
127
|
-
|
|
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}`));
|