hazo_umetrics 1.3.0 → 1.5.1
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/CHANGE_LOG.md +83 -0
- package/README.md +160 -2
- package/SETUP_CHECKLIST.md +24 -0
- package/dist/api/index.d.ts +34 -1
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +134 -4
- package/dist/index.client.js +2 -2
- package/dist/server/actions.d.ts +47 -0
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/actions.js +114 -14
- package/dist/server/behavior.d.ts +83 -1
- package/dist/server/behavior.d.ts.map +1 -1
- package/dist/server/behavior.js +197 -2
- package/dist/server/types.d.ts +10 -1
- package/dist/server/types.d.ts.map +1 -1
- package/dist/ui/actions_panel.d.ts +3 -1
- package/dist/ui/actions_panel.d.ts.map +1 -1
- package/dist/ui/actions_panel.js +105 -9
- package/dist/ui/analytics_panel.d.ts +9 -1
- package/dist/ui/analytics_panel.d.ts.map +1 -1
- package/dist/ui/analytics_panel.js +55 -7
- package/dist/ui/datapoint_panel.d.ts +19 -0
- package/dist/ui/datapoint_panel.d.ts.map +1 -0
- package/dist/ui/datapoint_panel.js +81 -0
- package/dist/ui/index.d.ts +2 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +2 -0
- package/dist/ui/journey_panel.d.ts +51 -0
- package/dist/ui/journey_panel.d.ts.map +1 -0
- package/dist/ui/journey_panel.js +226 -0
- package/migrations/db_setup_postgres.sql +28 -10
- package/migrations/db_setup_sqlite.sql +28 -10
- package/package.json +1 -1
package/CHANGE_LOG.md
CHANGED
|
@@ -1,5 +1,88 @@
|
|
|
1
1
|
# hazo_umetrics — Change Log
|
|
2
2
|
|
|
3
|
+
## 1.5.1 — `ui.pageexit.*` rename, smart auto-labels, `defaultActionLabels`, `ActionsPanel.refreshTrigger`
|
|
4
|
+
|
|
5
|
+
**Fixes / improvements (patch — no breaking changes for new installs; existing `ui.dwell.*` catalog rows need re-seeding):**
|
|
6
|
+
|
|
7
|
+
### Rename: `ui.dwell.*` → `ui.pageexit.*`
|
|
8
|
+
- `HazoMetricsProvider` now fires `ui.pageexit.<slug>` (instead of `ui.dwell.<slug>`) on path change and page hide. The name better describes the event: it fires when a user *leaves* a page, carrying the foreground dwell time in `dims.value`.
|
|
9
|
+
- `tabDwell` behavior aggregation updated to match — reads `ui.pageexit.*` from the stat store.
|
|
10
|
+
- Seed and test-app updated accordingly. Existing `ui.dwell.*` rows in the catalog are inert; re-seed or rename manually.
|
|
11
|
+
|
|
12
|
+
### Smarter auto-labels for `ui.*` events
|
|
13
|
+
- `ensureActionDef` now uses `deriveLabel()` instead of the generic `humanize()` for auto-created catalog rows. `ui.pageview.*` → `PageEntry:<Page>`, `ui.pageexit.*` → `PageExit:<Page>`, `ui.click.*` → `Click:<Label>`, `action.*` → `Action:<Label>`. Keeps auto-created rows recognizable without manual editing.
|
|
14
|
+
|
|
15
|
+
### New: `defaultActionLabels` option on `createEventHandlers`
|
|
16
|
+
- Pass `defaultActionLabels: Record<string, string>` to `createEventHandlers` to override the derived label for specific event keys on first ingest. Useful for wiring friendly display names before an operator touches the catalog:
|
|
17
|
+
```ts
|
|
18
|
+
createEventHandlers({
|
|
19
|
+
getAdapter: () => myAdapter,
|
|
20
|
+
defaultActionLabels: {
|
|
21
|
+
'ui.pageview.root': 'PageEntry:Homepage',
|
|
22
|
+
'ui.click.sign_in': 'Click:Sign In',
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### New: `ActionsPanel.refreshTrigger` prop
|
|
28
|
+
- `ActionsPanel` now accepts an optional `refreshTrigger?: number` prop. Increment it from the parent to trigger an external catalog refresh (e.g. after seeding events via a button click), without requiring the user to scroll back to a manual refresh control.
|
|
29
|
+
|
|
30
|
+
### Edit form: shows technical name
|
|
31
|
+
- The `ActionsPanel` inline edit form now shows a read-only "Technical name" field above the label input, so operators know which `action_key` they're renaming. Also adds a placeholder to the label input showing the raw key.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## 1.4.1 — Self-contained journey flow graph, journey user filter, smarter datapoint default
|
|
36
|
+
|
|
37
|
+
**Fixes / improvements (patch — no breaking API changes):**
|
|
38
|
+
|
|
39
|
+
### JourneyPanel: inline-SVG flow graph (replaces hazo_canvas)
|
|
40
|
+
- `JourneyPanel` now renders a self-contained inline-SVG left-to-right DAG. Removed the optional `hazo_canvas` peer dependency entirely — no external graph engine needed.
|
|
41
|
+
- Layout uses **shortest-path BFS** ranking (source nodes on the left, depth growing rightward). Journey graphs are cyclic (users revisit steps); the previous longest-path approach inflated every node to the same column and could grow its queue without bound (`RangeError: Invalid array length`). Shortest-path sets each node's rank once, so columns stay stable and bounded.
|
|
42
|
+
- Node fill encodes step `kind` (action / pageview / error); node opacity scales with visit count. Edge stroke width + opacity scale with transition count; counts >1 are labelled.
|
|
43
|
+
|
|
44
|
+
### JourneyPanel: built-in user filter
|
|
45
|
+
- Added a user-filter dropdown (top-right of the panel). "All users" by default; selecting a user refetches the journey scoped to `&user_id=<id>`.
|
|
46
|
+
- The known-user option list only grows across loads, so it stays stable while a single-user filter is active. Falls back to the `userId` prop when the internal filter is unset.
|
|
47
|
+
|
|
48
|
+
### DatapointPanel: smarter default action selection
|
|
49
|
+
- The action dropdown now defaults to the most useful action: first one with a declared `datapoint_schema`, else the first app-domain action (skipping auto-tracked `btn.*` / `ui.*` events), else the first action. Avoids landing on an empty auto-tracked event with no dimensions.
|
|
50
|
+
|
|
51
|
+
## 1.4.0 — Bulk catalog edits, per-action datapoints, user-journey viz, real user identity
|
|
52
|
+
|
|
53
|
+
**New features (minor bump — no breaking changes):**
|
|
54
|
+
|
|
55
|
+
### New: Bulk multiselect in Action Catalog
|
|
56
|
+
- `updateActionDefBulk(adapter, {app_id, action_keys, patch})` — update many actions at once.
|
|
57
|
+
- `applyLinksBulk(adapter, {app_id, action_keys, add_related, remove_related})` — cartesian add/remove related links.
|
|
58
|
+
- `POST /actions/bulk` API: set status, key-event flag, parent, and related actions across N actions in one request. Permission: `metrics.manage`.
|
|
59
|
+
- UI: checkbox column + `BulkActionBar` (status select, key-event toggles, parent picker, related add/remove) appear above the tree when ≥1 action selected.
|
|
60
|
+
|
|
61
|
+
### New: Per-action datapoint schema
|
|
62
|
+
- `datapoint_schema JSONB/TEXT` column on `hazo_umetrics_action_def` (migration: `ALTER TABLE hazo_umetrics_action_def ADD COLUMN datapoint_schema JSONB NOT NULL DEFAULT '[]'` for PG; `TEXT` for SQLite).
|
|
63
|
+
- `DatapointField`, `DatapointSchema` types exported from `hazo_umetrics`.
|
|
64
|
+
- JSON textarea in the Action Catalog edit form to declare which `dimensions` keys are meaningful.
|
|
65
|
+
- `datapointStats(adapter, opts)` — aggregates dimension values (numeric histograms + categorical top lists).
|
|
66
|
+
- `GET /analytics/datapoints?app_id=&action_key=` — returns `{action_key, total_events, keys[]}`.
|
|
67
|
+
- `DatapointPanel` component: action picker + per-key cards (histogram bars for numeric, ranked list for categorical).
|
|
68
|
+
|
|
69
|
+
### New: User-journey aggregation + graph
|
|
70
|
+
- `userJourney(adapter, opts)` — groups events by session, computes per-step timing, emits `JourneyGraph` (nodes + edges with avgMs).
|
|
71
|
+
- `GET /analytics/journey?app_id=[&user_id=][&session_id=]` — returns `{sessions, graph}`.
|
|
72
|
+
- `JourneyPanel` component: dagre LR graph via `hazo_canvas` (optional peer dep) with edge timing labels; falls back to ordered step list when canvas unavailable.
|
|
73
|
+
- `hazo_canvas` ≥1.9.1 required for timing labels on dagre edges (`direction:'LR'`).
|
|
74
|
+
|
|
75
|
+
### New: Host-injected user identity
|
|
76
|
+
- `resolveUserProfiles?: ResolveUserProfiles` prop on `AnalyticsPanel` and `JourneyPanel`.
|
|
77
|
+
- UserFilter dropdown now shows `name / email / avatar` when resolver is wired; falls back to truncated UUID.
|
|
78
|
+
- JourneyPanel session headers show resolved name/email/avatar.
|
|
79
|
+
- Test-app: `POST /api/hazo_umetrics/user_profiles` route wires `hazo_auth` `get_profiles` as an example host implementation.
|
|
80
|
+
|
|
81
|
+
### hazo_canvas 1.9.1
|
|
82
|
+
- Dagre edge `label` field now rendered as a midpoint `kind:'text'` child (timing labels for journey viz).
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
3
86
|
## 1.3.0 — Analytics dashboard + behavioral fixes + createUmetricsConnect
|
|
4
87
|
|
|
5
88
|
**New features + bug fixes (no breaking changes):**
|
package/README.md
CHANGED
|
@@ -103,7 +103,7 @@ Seven behavioral panels answer:
|
|
|
103
103
|
- **Post re-login actions** — what users do on their second session
|
|
104
104
|
- **Before "share"** — the N actions preceding a goal event (configurable goal key)
|
|
105
105
|
- **Common errors** — ranked `ui.error.*` frequency
|
|
106
|
-
- **Time per tab** — real foreground dwell time per path (from `ui.
|
|
106
|
+
- **Time per tab** — real foreground dwell time per path (from `ui.pageexit.*` events)
|
|
107
107
|
- **Returning-user actions** — most common actions for users with >1 session
|
|
108
108
|
|
|
109
109
|
For `user_id` to be captured, call `identify(userId)` after login or mount `UserIdentifier` (see test-app `src/components/user_identifier.tsx`).
|
|
@@ -123,7 +123,165 @@ const { trackAction, trackError } = useMetrics();
|
|
|
123
123
|
trackAction('timer.complete', { url: '/timer' });
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
-
Auto-tracked on mount: page views (`ui.pageview.*`), clicks (`ui.click.*`), errors (`ui.error.*`), dwell time (`ui.
|
|
126
|
+
Auto-tracked on mount: page views (`ui.pageview.*`), clicks (`ui.click.*`), errors (`ui.error.*`), page-exit dwell time (`ui.pageexit.*`).
|
|
127
|
+
|
|
128
|
+
## Bulk Action Catalog edits
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
// POST /api/hazo_umetrics/actions/bulk
|
|
132
|
+
// Body: { app_id, action_keys, patch?, add_related?, remove_related? }
|
|
133
|
+
// Permission: metrics.manage
|
|
134
|
+
// Response: { ok: true, data: { updated: string[], missing: string[], links: { added, removed } } }
|
|
135
|
+
|
|
136
|
+
// Set status on many actions at once
|
|
137
|
+
await fetch('/api/hazo_umetrics/actions/bulk', {
|
|
138
|
+
method: 'POST',
|
|
139
|
+
body: JSON.stringify({
|
|
140
|
+
app_id: 'my-app',
|
|
141
|
+
action_keys: ['timer.start', 'timer.complete'],
|
|
142
|
+
patch: { status: 'active', is_key_event: true },
|
|
143
|
+
}),
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// Add related links in bulk (cartesian: every key in action_keys ↔ every key in add_related)
|
|
147
|
+
await fetch('/api/hazo_umetrics/actions/bulk', {
|
|
148
|
+
method: 'POST',
|
|
149
|
+
body: JSON.stringify({
|
|
150
|
+
app_id: 'my-app',
|
|
151
|
+
action_keys: ['timer.start'],
|
|
152
|
+
add_related: ['timer.complete', 'timer.pause'],
|
|
153
|
+
}),
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`action_keys` must be a non-empty array — an empty array returns 400. Both `patch` and `add_related`/`remove_related` can be combined in one request.
|
|
158
|
+
|
|
159
|
+
## Per-action datapoint schema
|
|
160
|
+
|
|
161
|
+
Declare which `dimensions` keys are analytically meaningful for a given action:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
// PATCH the action to set a datapoint_schema
|
|
165
|
+
await fetch('/api/hazo_umetrics/actions?app_id=my-app', {
|
|
166
|
+
method: 'PATCH',
|
|
167
|
+
body: JSON.stringify({
|
|
168
|
+
app_id: 'my-app',
|
|
169
|
+
action_key: 'timer.complete',
|
|
170
|
+
patch: {
|
|
171
|
+
datapoint_schema: [
|
|
172
|
+
{ key: 'duration_ms', label: 'Duration (ms)', type: 'number' },
|
|
173
|
+
{ key: 'mode', label: 'Timer mode', type: 'string' },
|
|
174
|
+
],
|
|
175
|
+
},
|
|
176
|
+
}),
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Types exported from `hazo_umetrics`: `DatapointField`, `DatapointSchema`.
|
|
181
|
+
|
|
182
|
+
Once a schema is set, the `DatapointPanel` UI component shows per-key cards: histogram bars for `type:'number'` keys, ranked frequency lists for `type:'string'` keys.
|
|
183
|
+
|
|
184
|
+
## Journey analytics
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
// GET /api/hazo_umetrics/analytics/journey?app_id=<id>[&user_id=<id>][&session_id=<id>]
|
|
188
|
+
// Permission: metrics.view
|
|
189
|
+
// Response: { ok: true, data: { sessions: Session[], graph: JourneyGraph } }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
`sessions` is an ordered list of per-session step arrays with timing. `graph.nodes` and `graph.edges` provide the aggregated action-transition graph for all sessions (or filtered by user/session). Each edge carries `avgMs` (mean inter-step latency).
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
import { JourneyPanel } from 'hazo_umetrics/ui';
|
|
196
|
+
|
|
197
|
+
// Embedded panel — fetches journey data from /api/hazo_umetrics/analytics/journey
|
|
198
|
+
<JourneyPanel
|
|
199
|
+
appId="my-app"
|
|
200
|
+
resolveUserProfiles={resolveUserProfiles} // optional — enriches session headers
|
|
201
|
+
/>
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
`JourneyPanel` renders a self-contained inline-SVG flow graph — a left-to-right DAG laid out by shortest-path BFS (source nodes on the left, depth growing rightward). Node fill encodes step `kind` (action / pageview / error) and opacity scales with visit count; edge stroke width and opacity scale with transition count. No external graph dependency is required. A built-in **user filter** dropdown (top-right of the panel) scopes the graph and session list to a single user; it refetches with `&user_id=<id>`. The known-user option list only grows, so it stays stable while a filter is active. Below the graph, per-session collapsible step lists show timing between steps.
|
|
205
|
+
|
|
206
|
+
## Datapoint analytics
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
// GET /api/hazo_umetrics/analytics/datapoints?app_id=<id>&action_key=<key>
|
|
210
|
+
// Permission: metrics.view
|
|
211
|
+
// Response: { ok: true, data: { action_key, total_events, keys: DatapointKeyStats[] } }
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Each entry in `keys` describes one dimension: `{ key, type, total, histogram | topValues }`. Use the `DatapointPanel` component to render these automatically. The panel's action dropdown defaults to the most useful action: first an action with a declared `datapoint_schema`, else the first app-domain action (skipping auto-tracked `btn.*` / `ui.*` events), else the first action — so a user lands on data-bearing dimensions instead of an empty auto-tracked event.
|
|
215
|
+
|
|
216
|
+
## resolveUserProfiles — wiring user identity
|
|
217
|
+
|
|
218
|
+
`AnalyticsPanel` and `JourneyPanel` both accept an optional `resolveUserProfiles` prop that enriches user IDs with real names, emails, and avatars:
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
import type { ResolveUserProfiles } from 'hazo_umetrics/ui';
|
|
222
|
+
|
|
223
|
+
const resolveUserProfiles: ResolveUserProfiles = async (ids) => {
|
|
224
|
+
const r = await fetch('/api/my-app/user_profiles', {
|
|
225
|
+
method: 'POST',
|
|
226
|
+
body: JSON.stringify({ ids }),
|
|
227
|
+
headers: { 'Content-Type': 'application/json' },
|
|
228
|
+
});
|
|
229
|
+
const j = await r.json();
|
|
230
|
+
return j.profiles ?? [];
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Each profile: { user_id, email?, name?, profile_picture_url? }
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
**Wiring with `hazo_auth` (gotimer / kinstripe pattern):**
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
// app/api/hazo_umetrics/user_profiles/route.ts
|
|
240
|
+
import { authConnect } from '@/lib/db';
|
|
241
|
+
import { hazo_get_user_profiles } from 'hazo_auth/server-lib';
|
|
242
|
+
|
|
243
|
+
export async function POST(req: Request) {
|
|
244
|
+
const { ids } = await req.json();
|
|
245
|
+
const result = await hazo_get_user_profiles(authConnect, ids);
|
|
246
|
+
return Response.json({ profiles: result.profiles ?? [] });
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Event ingestion — `defaultActionLabels`
|
|
251
|
+
|
|
252
|
+
Pass `defaultActionLabels` to `createEventHandlers` to declare human-readable labels for auto-tracked `ui.*` events before an operator edits the catalog. On first ingest of a key, the label is stored instead of the derived `PageEntry:*` / `Click:*` fallback:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
import { createEventHandlers } from 'hazo_umetrics/api';
|
|
256
|
+
|
|
257
|
+
const handlers = createEventHandlers({
|
|
258
|
+
getAdapter: () => myAdapter,
|
|
259
|
+
defaultActionLabels: {
|
|
260
|
+
'ui.pageview.root': 'Homepage',
|
|
261
|
+
'ui.pageview.timer': 'Timer Page',
|
|
262
|
+
'ui.click.sign_in': 'Sign In Button',
|
|
263
|
+
'ui.pageexit.root': 'Homepage Exit',
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// app/api/hazo_umetrics/events/route.ts
|
|
268
|
+
export const POST = (req: Request) => handlers.recordEvent(req);
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Action Catalog panel — `refreshTrigger`
|
|
272
|
+
|
|
273
|
+
`ActionsPanel` accepts an optional `refreshTrigger` prop. Increment it to force a catalog reload from the parent (e.g. after seeding events):
|
|
274
|
+
|
|
275
|
+
```tsx
|
|
276
|
+
import { ActionsPanel } from 'hazo_umetrics/ui';
|
|
277
|
+
|
|
278
|
+
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
|
279
|
+
|
|
280
|
+
// After seeding:
|
|
281
|
+
setRefreshTrigger(n => n + 1);
|
|
282
|
+
|
|
283
|
+
<ActionsPanel appId="my-app" refreshTrigger={refreshTrigger} />
|
|
284
|
+
```
|
|
127
285
|
|
|
128
286
|
## API route factories
|
|
129
287
|
|
package/SETUP_CHECKLIST.md
CHANGED
|
@@ -17,6 +17,30 @@ npm install hazo_core hazo_connect hazo_auth hazo_secure hazo_audit hazo_api haz
|
|
|
17
17
|
> `hazo_dataviz` is required when you render `MetricsPanel`/`StatTrend` — its `LineChart`
|
|
18
18
|
> draws the trend chart (moved out of `hazo_ui` in the 4.0.0 chart migration).
|
|
19
19
|
|
|
20
|
+
## ALTER TABLE for 1.4.0 (existing deployments only)
|
|
21
|
+
|
|
22
|
+
If you are upgrading an **existing** deployment from a version before 1.4.0, run these statements against your umetrics database before upgrading the package:
|
|
23
|
+
|
|
24
|
+
```sql
|
|
25
|
+
-- PostgreSQL:
|
|
26
|
+
ALTER TABLE hazo_umetrics_action_def ADD COLUMN IF NOT EXISTS datapoint_schema JSONB NOT NULL DEFAULT '[]';
|
|
27
|
+
|
|
28
|
+
-- SQLite (does not support IF NOT EXISTS on ALTER TABLE — skip if column already exists):
|
|
29
|
+
ALTER TABLE hazo_umetrics_action_def ADD COLUMN datapoint_schema TEXT NOT NULL DEFAULT '[]';
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Fresh installations (using `db_setup_sqlite.sql` / `db_setup_postgres.sql` from 1.4.0+) already include this column — no ALTER TABLE needed.
|
|
33
|
+
|
|
34
|
+
**Optional — journey graph (hazo_canvas peer dep):**
|
|
35
|
+
|
|
36
|
+
`JourneyPanel` renders an interactive dagre LR graph when `hazo_canvas` is installed. Without it, the panel falls back to a plain ordered step list. Install only if you use `JourneyPanel`:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install hazo_canvas@^1.9.1
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
20
44
|
## 2. Run DB migrations
|
|
21
45
|
|
|
22
46
|
**SQLite:**
|
package/dist/api/index.d.ts
CHANGED
|
@@ -21,6 +21,22 @@ export interface ApiFactoryOptions {
|
|
|
21
21
|
app_id: string;
|
|
22
22
|
details?: unknown;
|
|
23
23
|
}) => Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Default human-readable labels for auto-tracked events.
|
|
26
|
+
* When an event key is first seen and a catalog entry is auto-created,
|
|
27
|
+
* the matching label is used instead of the raw technical name.
|
|
28
|
+
*
|
|
29
|
+
* Example:
|
|
30
|
+
* ```ts
|
|
31
|
+
* defaultActionLabels: {
|
|
32
|
+
* 'ui.pageview.root': 'Homepage',
|
|
33
|
+
* 'ui.pageview.timer': 'Timer Page',
|
|
34
|
+
* 'ui.click.sign_in': 'Sign In Button',
|
|
35
|
+
* 'ui.pageexit.root': 'Homepage Exit',
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
defaultActionLabels?: Record<string, string>;
|
|
24
40
|
}
|
|
25
41
|
export declare function createStatHandlers(options: ApiFactoryOptions): {
|
|
26
42
|
/**
|
|
@@ -75,7 +91,7 @@ export declare function createQueryHandlers(options: ApiFactoryOptions): {
|
|
|
75
91
|
/** STUB (M1): Execute a GA4 or stat-store query. */
|
|
76
92
|
query(req: Request): Promise<Response>;
|
|
77
93
|
};
|
|
78
|
-
export declare function createEventHandlers(options: Pick<ApiFactoryOptions, 'getAdapter'>): {
|
|
94
|
+
export declare function createEventHandlers(options: Pick<ApiFactoryOptions, 'getAdapter' | 'defaultActionLabels'>): {
|
|
79
95
|
/**
|
|
80
96
|
* POST /...
|
|
81
97
|
* Body: { app_id, metric_key, value?, dimensions? }
|
|
@@ -106,6 +122,13 @@ export declare function createActionHandlers(options: ApiFactoryOptions): {
|
|
|
106
122
|
* Emits an audit entry 'action.catalog.update'.
|
|
107
123
|
*/
|
|
108
124
|
updateAction(req: Request): Promise<Response>;
|
|
125
|
+
/**
|
|
126
|
+
* POST /...
|
|
127
|
+
* Body: { app_id, action_keys, patch?, add_related?, remove_related? }
|
|
128
|
+
* Bulk-updates multiple action defs and/or their links. Requires metrics.manage.
|
|
129
|
+
* Emits a single audit entry 'action.catalog.bulk_update' per call.
|
|
130
|
+
*/
|
|
131
|
+
bulkUpdateActions(req: Request): Promise<Response>;
|
|
109
132
|
};
|
|
110
133
|
export declare function createActivityHandlers(options: ApiFactoryOptions): {
|
|
111
134
|
/**
|
|
@@ -145,5 +168,15 @@ export declare function createBehaviorHandlers(options: ApiFactoryOptions): {
|
|
|
145
168
|
* Returns events bucketed by day. Requires metrics.view.
|
|
146
169
|
*/
|
|
147
170
|
getEventsOverTime(req: Request): Promise<Response>;
|
|
171
|
+
/**
|
|
172
|
+
* GET /...?app_id=<id>&action_key=<key>[&since=<iso>][&until=<iso>][&buckets=<n>][&top_n=<n>]
|
|
173
|
+
* Returns datapoint dimension stats for an action. Requires metrics.view.
|
|
174
|
+
*/
|
|
175
|
+
getDatapoints(req: Request): Promise<Response>;
|
|
176
|
+
/**
|
|
177
|
+
* GET /...?app_id=<id>[&user_id=<id>][&session_id=<id>][&since=<iso>]
|
|
178
|
+
* Returns user journey sessions and aggregated graph. Requires metrics.view.
|
|
179
|
+
*/
|
|
180
|
+
getJourney(req: Request): Promise<Response>;
|
|
148
181
|
};
|
|
149
182
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/api/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"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"}
|
package/dist/api/index.js
CHANGED
|
@@ -8,8 +8,8 @@ import { createCrudService } from 'hazo_connect/server';
|
|
|
8
8
|
import { recordStat as _recordStat, getLatestStat as _getLatestStat, getStatSeries as _getStatSeries, } from '../server/stats.js';
|
|
9
9
|
import { resolveVariant } from '../server/flags.js';
|
|
10
10
|
import { getActivitySummary as _getActivitySummary, getActivityTrend as _getActivityTrend, } from '../server/activity.js';
|
|
11
|
-
import { ensureActionDef as _ensureActionDef, listActions as _listActions, getActionTree as _getActionTree, updateActionDef as _updateActionDef, getLinks as _getLinks, addLink as _addLink, removeLink as _removeLink, } from '../server/actions.js';
|
|
12
|
-
import { topActions as _topActions, firstSessionActions as _firstSessionActions, postReloginActions as _postReloginActions, actionsBeforeGoal as _actionsBeforeGoal, topErrors as _topErrors, tabDwell as _tabDwell, frequentActionsReturning as _frequentActionsReturning, eventsOverTime as _eventsOverTime, } from '../server/behavior.js';
|
|
11
|
+
import { ensureActionDef as _ensureActionDef, listActions as _listActions, getActionTree as _getActionTree, updateActionDef as _updateActionDef, updateActionDefBulk as _updateActionDefBulk, applyLinksBulk as _applyLinksBulk, getLinks as _getLinks, addLink as _addLink, removeLink as _removeLink, } from '../server/actions.js';
|
|
12
|
+
import { topActions as _topActions, firstSessionActions as _firstSessionActions, postReloginActions as _postReloginActions, actionsBeforeGoal as _actionsBeforeGoal, topErrors as _topErrors, tabDwell as _tabDwell, frequentActionsReturning as _frequentActionsReturning, eventsOverTime as _eventsOverTime, datapointStats as _datapointStats, userJourney as _userJourney, } from '../server/behavior.js';
|
|
13
13
|
// ---------------------------------------------------------------------------
|
|
14
14
|
// Default auth — dynamically imports hazo_auth/server-lib to avoid the
|
|
15
15
|
// server-only guard firing at module-evaluation time (e.g. in test runners).
|
|
@@ -413,11 +413,17 @@ export function createEventHandlers(options) {
|
|
|
413
413
|
value: typeof value === 'number' ? value : 1,
|
|
414
414
|
dimensions,
|
|
415
415
|
});
|
|
416
|
-
// Auto-classify: upsert a draft catalog row for action.* events
|
|
416
|
+
// Auto-classify: upsert a draft catalog row for action.* and ui.* events
|
|
417
417
|
if (metric_key.startsWith('action.')) {
|
|
418
418
|
const action_key = metric_key.slice('action.'.length);
|
|
419
419
|
const url = typeof dimensions?.url === 'string' ? dimensions.url : undefined;
|
|
420
|
-
|
|
420
|
+
const defaultLabel = options.defaultActionLabels?.[action_key];
|
|
421
|
+
await _ensureActionDef(adapter, { app_id, action_key, url, source: 'manual', defaultLabel });
|
|
422
|
+
}
|
|
423
|
+
else if (metric_key.startsWith('ui.')) {
|
|
424
|
+
const url = typeof dimensions?.url === 'string' ? dimensions.url : undefined;
|
|
425
|
+
const defaultLabel = options.defaultActionLabels?.[metric_key];
|
|
426
|
+
await _ensureActionDef(adapter, { app_id, action_key: metric_key, url, source: 'auto', defaultLabel });
|
|
421
427
|
}
|
|
422
428
|
return jsonOk({ recorded: true }, 201);
|
|
423
429
|
},
|
|
@@ -516,6 +522,67 @@ export function createActionHandlers(options) {
|
|
|
516
522
|
});
|
|
517
523
|
return jsonOk({ action: updated });
|
|
518
524
|
},
|
|
525
|
+
/**
|
|
526
|
+
* POST /...
|
|
527
|
+
* Body: { app_id, action_keys, patch?, add_related?, remove_related? }
|
|
528
|
+
* Bulk-updates multiple action defs and/or their links. Requires metrics.manage.
|
|
529
|
+
* Emits a single audit entry 'action.catalog.bulk_update' per call.
|
|
530
|
+
*/
|
|
531
|
+
async bulkUpdateActions(req) {
|
|
532
|
+
let body;
|
|
533
|
+
try {
|
|
534
|
+
body = (await req.json());
|
|
535
|
+
}
|
|
536
|
+
catch {
|
|
537
|
+
return jsonError('BAD_REQUEST', 'Invalid JSON body', 400);
|
|
538
|
+
}
|
|
539
|
+
const { app_id, action_keys, patch, add_related, remove_related } = body;
|
|
540
|
+
if (!app_id) {
|
|
541
|
+
return jsonError('BAD_REQUEST', 'app_id is required', 400);
|
|
542
|
+
}
|
|
543
|
+
if (!Array.isArray(action_keys) || action_keys.length === 0) {
|
|
544
|
+
return jsonError('BAD_REQUEST', 'action_keys must be a non-empty array', 400);
|
|
545
|
+
}
|
|
546
|
+
const gate = await gateAuth(req, {
|
|
547
|
+
required_permissions: ['metrics.manage'],
|
|
548
|
+
app_id,
|
|
549
|
+
getAuth,
|
|
550
|
+
});
|
|
551
|
+
if (!gate.ok)
|
|
552
|
+
return gate.response;
|
|
553
|
+
const adapter = await options.getAdapter();
|
|
554
|
+
const keys = action_keys;
|
|
555
|
+
let updated = [];
|
|
556
|
+
let missing = [];
|
|
557
|
+
let linksAdded = 0;
|
|
558
|
+
let linksRemoved = 0;
|
|
559
|
+
if (patch && Object.keys(patch).length > 0) {
|
|
560
|
+
const result = await _updateActionDefBulk(adapter, {
|
|
561
|
+
app_id,
|
|
562
|
+
action_keys: keys,
|
|
563
|
+
patch: patch,
|
|
564
|
+
});
|
|
565
|
+
updated = result.updated;
|
|
566
|
+
missing = result.missing;
|
|
567
|
+
}
|
|
568
|
+
if ((add_related && add_related.length > 0) || (remove_related && remove_related.length > 0)) {
|
|
569
|
+
const result = await _applyLinksBulk(adapter, {
|
|
570
|
+
app_id,
|
|
571
|
+
action_keys: keys,
|
|
572
|
+
add_related,
|
|
573
|
+
remove_related,
|
|
574
|
+
});
|
|
575
|
+
linksAdded = result.added;
|
|
576
|
+
linksRemoved = result.removed;
|
|
577
|
+
}
|
|
578
|
+
await options.onAudit?.({
|
|
579
|
+
action: 'action.catalog.bulk_update',
|
|
580
|
+
actor_id: gate.userId,
|
|
581
|
+
app_id,
|
|
582
|
+
details: { action_keys: keys, patch, add_related, remove_related },
|
|
583
|
+
});
|
|
584
|
+
return jsonOk({ updated, missing, links: { added: linksAdded, removed: linksRemoved } });
|
|
585
|
+
},
|
|
519
586
|
};
|
|
520
587
|
}
|
|
521
588
|
// ---------------------------------------------------------------------------
|
|
@@ -744,5 +811,68 @@ export function createBehaviorHandlers(options) {
|
|
|
744
811
|
const data = await _eventsOverTime(adapter, { app_id, since, userIds, limit: 10000, catalogMap, includeArchived, keyEventsOnly });
|
|
745
812
|
return jsonOk({ data });
|
|
746
813
|
},
|
|
814
|
+
/**
|
|
815
|
+
* GET /...?app_id=<id>&action_key=<key>[&since=<iso>][&until=<iso>][&buckets=<n>][&top_n=<n>]
|
|
816
|
+
* Returns datapoint dimension stats for an action. Requires metrics.view.
|
|
817
|
+
*/
|
|
818
|
+
async getDatapoints(req) {
|
|
819
|
+
const url = new URL(req.url);
|
|
820
|
+
const app_id = url.searchParams.get('app_id') ?? '';
|
|
821
|
+
const action_key = url.searchParams.get('action_key') ?? '';
|
|
822
|
+
const since = url.searchParams.get('since') ?? undefined;
|
|
823
|
+
const until = url.searchParams.get('until') ?? undefined;
|
|
824
|
+
const bucketsParam = url.searchParams.get('buckets');
|
|
825
|
+
const buckets = bucketsParam != null ? parseInt(bucketsParam, 10) : undefined;
|
|
826
|
+
const topNParam = url.searchParams.get('top_n');
|
|
827
|
+
const topN = topNParam != null ? parseInt(topNParam, 10) : undefined;
|
|
828
|
+
const gate = await gateAuth(req, {
|
|
829
|
+
required_permissions: ['metrics.view'],
|
|
830
|
+
app_id: app_id || undefined,
|
|
831
|
+
getAuth,
|
|
832
|
+
});
|
|
833
|
+
if (!gate.ok)
|
|
834
|
+
return gate.response;
|
|
835
|
+
if (!app_id || !action_key) {
|
|
836
|
+
return jsonError('BAD_REQUEST', 'app_id and action_key are required', 400);
|
|
837
|
+
}
|
|
838
|
+
const adapter = await options.getAdapter();
|
|
839
|
+
const catalog = await _listActions(adapter, { app_id });
|
|
840
|
+
const matchingAction = catalog.find((a) => a.action_key === action_key);
|
|
841
|
+
const result = await _datapointStats(adapter, {
|
|
842
|
+
app_id,
|
|
843
|
+
action_key,
|
|
844
|
+
since,
|
|
845
|
+
until,
|
|
846
|
+
buckets,
|
|
847
|
+
topN,
|
|
848
|
+
schema: matchingAction?.datapoint_schema,
|
|
849
|
+
});
|
|
850
|
+
return jsonOk(result);
|
|
851
|
+
},
|
|
852
|
+
/**
|
|
853
|
+
* GET /...?app_id=<id>[&user_id=<id>][&session_id=<id>][&since=<iso>]
|
|
854
|
+
* Returns user journey sessions and aggregated graph. Requires metrics.view.
|
|
855
|
+
*/
|
|
856
|
+
async getJourney(req) {
|
|
857
|
+
const url = new URL(req.url);
|
|
858
|
+
const app_id = url.searchParams.get('app_id') ?? '';
|
|
859
|
+
const user_id = url.searchParams.get('user_id') ?? undefined;
|
|
860
|
+
const session_id = url.searchParams.get('session_id') ?? undefined;
|
|
861
|
+
const since = url.searchParams.get('since') ?? undefined;
|
|
862
|
+
const gate = await gateAuth(req, {
|
|
863
|
+
required_permissions: ['metrics.view'],
|
|
864
|
+
app_id: app_id || undefined,
|
|
865
|
+
getAuth,
|
|
866
|
+
});
|
|
867
|
+
if (!gate.ok)
|
|
868
|
+
return gate.response;
|
|
869
|
+
if (!app_id)
|
|
870
|
+
return jsonError('BAD_REQUEST', 'app_id is required', 400);
|
|
871
|
+
const adapter = await options.getAdapter();
|
|
872
|
+
const catalog = await _listActions(adapter, { app_id });
|
|
873
|
+
const catalogMap = new Map(catalog.map((a) => [a.action_key, { label: a.label, datapoint_schema: a.datapoint_schema }]));
|
|
874
|
+
const result = await _userJourney(adapter, { app_id, user_id, session_id, since, catalogMap });
|
|
875
|
+
return jsonOk(result);
|
|
876
|
+
},
|
|
747
877
|
};
|
|
748
878
|
}
|
package/dist/index.client.js
CHANGED
|
@@ -71,7 +71,7 @@ export function HazoMetricsProvider({ appId, userId, children }) {
|
|
|
71
71
|
const elapsed = Date.now() - lastChangeAtRef.current;
|
|
72
72
|
if (prev && elapsed > 0 && prev !== pathname) {
|
|
73
73
|
const slug = prev.replace(/\//g, '_').replace(/^_/, '') || 'root';
|
|
74
|
-
postEvent(appId, `ui.
|
|
74
|
+
postEvent(appId, `ui.pageexit.${slug}`, { path: prev, value: elapsed });
|
|
75
75
|
}
|
|
76
76
|
// Update refs
|
|
77
77
|
lastPathRef.current = pathname;
|
|
@@ -88,7 +88,7 @@ export function HazoMetricsProvider({ appId, userId, children }) {
|
|
|
88
88
|
const elapsed = Date.now() - lastChangeAtRef.current;
|
|
89
89
|
if (elapsed > 0 && lastPathRef.current) {
|
|
90
90
|
const slug = lastPathRef.current.replace(/\//g, '_').replace(/^_/, '') || 'root';
|
|
91
|
-
postEvent(appId, `ui.
|
|
91
|
+
postEvent(appId, `ui.pageexit.${slug}`, { path: lastPathRef.current, value: elapsed });
|
|
92
92
|
lastChangeAtRef.current = Date.now();
|
|
93
93
|
}
|
|
94
94
|
}
|
package/dist/server/actions.d.ts
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import type { HazoConnectAdapter } from 'hazo_connect/server';
|
|
2
2
|
import type { ActionDef, ActionDefPatch, ActionNode, ActionLink } from './types.js';
|
|
3
|
+
type ActionDefRow = {
|
|
4
|
+
action_key: string;
|
|
5
|
+
app_id: string;
|
|
6
|
+
scope_id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
description: string;
|
|
9
|
+
parent_key: string | null;
|
|
10
|
+
url: string | null;
|
|
11
|
+
is_key_event: number | boolean;
|
|
12
|
+
status: string;
|
|
13
|
+
source: string;
|
|
14
|
+
datapoint_schema: string;
|
|
15
|
+
created_at: string;
|
|
16
|
+
updated_at: string;
|
|
17
|
+
[key: string]: unknown;
|
|
18
|
+
};
|
|
19
|
+
export declare function applyPatch(_existing: ActionDef, patch: ActionDefPatch): Partial<ActionDefRow>;
|
|
3
20
|
/**
|
|
4
21
|
* Insert-if-absent a draft def row. No-op after first sight (seen-set cached).
|
|
5
22
|
* Called by the ingestion layer on every `action.*` event.
|
|
@@ -9,6 +26,8 @@ export declare function ensureActionDef(adapter: HazoConnectAdapter, params: {
|
|
|
9
26
|
scope_id?: string;
|
|
10
27
|
action_key: string;
|
|
11
28
|
url?: string | null;
|
|
29
|
+
source?: 'auto' | 'manual';
|
|
30
|
+
defaultLabel?: string;
|
|
12
31
|
}): Promise<void>;
|
|
13
32
|
/**
|
|
14
33
|
* Returns all action defs for an app as a flat list.
|
|
@@ -63,4 +82,32 @@ export declare function removeLink(adapter: HazoConnectAdapter, params: {
|
|
|
63
82
|
action_key: string;
|
|
64
83
|
related_key: string;
|
|
65
84
|
}): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Updates mutable fields across multiple action defs in one call.
|
|
87
|
+
* Returns lists of updated keys and missing (not found) keys.
|
|
88
|
+
*/
|
|
89
|
+
export declare function updateActionDefBulk(adapter: HazoConnectAdapter, params: {
|
|
90
|
+
app_id: string;
|
|
91
|
+
action_keys: string[];
|
|
92
|
+
patch: ActionDefPatch;
|
|
93
|
+
}): Promise<{
|
|
94
|
+
updated: string[];
|
|
95
|
+
missing: string[];
|
|
96
|
+
}>;
|
|
97
|
+
/**
|
|
98
|
+
* Applies link additions and removals across multiple action keys (Cartesian product).
|
|
99
|
+
* For each action_key × each add_related key: calls addLink (idempotent, self-link guarded).
|
|
100
|
+
* For each action_key × each remove_related key: calls removeLink.
|
|
101
|
+
* Returns counts of added (non-null addLink results) and removed (all removeLink calls).
|
|
102
|
+
*/
|
|
103
|
+
export declare function applyLinksBulk(adapter: HazoConnectAdapter, params: {
|
|
104
|
+
app_id: string;
|
|
105
|
+
action_keys: string[];
|
|
106
|
+
add_related?: string[];
|
|
107
|
+
remove_related?: string[];
|
|
108
|
+
}): Promise<{
|
|
109
|
+
added: number;
|
|
110
|
+
removed: number;
|
|
111
|
+
}>;
|
|
112
|
+
export {};
|
|
66
113
|
//# sourceMappingURL=actions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/server/actions.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../src/server/actions.ts"],"names":[],"mappings":"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"}
|