hazo_umetrics 1.2.1 → 1.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/CHANGE_LOG.md +11 -1
- package/README.md +40 -1
- package/SETUP_CHECKLIST.md +23 -6
- package/config/hazo_umetrics_config.ini.sample +16 -5
- package/dist/lib/index.d.ts +1 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +1 -0
- package/dist/server/connect.d.ts +11 -0
- package/dist/server/connect.d.ts.map +1 -0
- package/dist/server/connect.js +58 -0
- package/package.json +1 -1
package/CHANGE_LOG.md
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
# hazo_umetrics — Change Log
|
|
2
2
|
|
|
3
|
-
## 1.3.0 — Analytics dashboard + behavioral fixes
|
|
3
|
+
## 1.3.0 — Analytics dashboard + behavioral fixes + createUmetricsConnect
|
|
4
4
|
|
|
5
5
|
**New features + bug fixes (no breaking changes):**
|
|
6
6
|
|
|
7
|
+
### New: `createUmetricsConnect` — dedicated DB factory
|
|
8
|
+
- `createUmetricsConnect(opts?)` builds a `HazoConnectAdapter` from `HAZO_UMETRICS_DATABASE_*`
|
|
9
|
+
env vars (type / URL / API key / SQLite path), with INI `[database]` as fallback. Returns a
|
|
10
|
+
cached adapter on no-arg calls; creates fresh when opts are passed.
|
|
11
|
+
- Exported from main `hazo_umetrics` entry: `import { createUmetricsConnect } from 'hazo_umetrics'`
|
|
12
|
+
- `UmetricsConnectOptions` type exported alongside the factory.
|
|
13
|
+
- Enables wiring umetrics to a **separate DB** from the host app in one line:
|
|
14
|
+
`createStatHandlers({ getAdapter: createUmetricsConnect })`
|
|
15
|
+
- Updated `config/hazo_umetrics_config.ini.sample` `[database]` section with dedicated env-var docs.
|
|
16
|
+
|
|
7
17
|
### New: `topActions` behavior aggregation
|
|
8
18
|
- `topActions` function in `src/server/behavior.ts` — ranks all `action.*` events by frequency, no `user_id` required. Answers "what is the most common action?" without needing session segmentation.
|
|
9
19
|
- Exposed as `metric=top_actions` in `GET /api/hazo_umetrics/analytics/behavior`.
|
package/README.md
CHANGED
|
@@ -139,7 +139,46 @@ const expHandlers = createExperimentHandlers({ getAdapter: () => myAdapter });
|
|
|
139
139
|
|
|
140
140
|
## DB setup
|
|
141
141
|
|
|
142
|
-
Run `db_setup_sqlite.sql` (SQLite) or `db_setup_postgres.sql` (PostgreSQL) once
|
|
142
|
+
Run `db_setup_sqlite.sql` (SQLite) or `db_setup_postgres.sql` (PostgreSQL) once against your **umetrics DB**. Creates 7 tables, all prefixed `hazo_umetrics_`.
|
|
143
|
+
|
|
144
|
+
> Run the migration against the **dedicated umetrics database**, not the host app's main DB.
|
|
145
|
+
> If you use `createUmetricsConnect()` (recommended), the umetrics DB is the one pointed to
|
|
146
|
+
> by `HAZO_UMETRICS_DATABASE_URL`.
|
|
147
|
+
|
|
148
|
+
## Separate database for umetrics
|
|
149
|
+
|
|
150
|
+
`hazo_umetrics` data (events, stats, flags, experiments) should live in its own database,
|
|
151
|
+
**separate from the host app's main DB**. This keeps analytics writes from competing with
|
|
152
|
+
product data, lets you point multiple apps at one shared analytics store, and lets you
|
|
153
|
+
scale each independently.
|
|
154
|
+
|
|
155
|
+
Use `createUmetricsConnect()` to build a dedicated adapter in one line:
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import { createUmetricsConnect } from 'hazo_umetrics';
|
|
159
|
+
|
|
160
|
+
// Reads HAZO_UMETRICS_DATABASE_URL + HAZO_UMETRICS_DATABASE_API_KEY from env.
|
|
161
|
+
// Falls back to ./umetrics.sqlite when type=sqlite.
|
|
162
|
+
export const getUmetricsAdapter = createUmetricsConnect;
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Then inject it into every route factory:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
const handlers = createStatHandlers({ getAdapter: createUmetricsConnect });
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Env vars
|
|
172
|
+
|
|
173
|
+
| Variable | Required | Description |
|
|
174
|
+
|---|---|---|
|
|
175
|
+
| `HAZO_UMETRICS_DATABASE_URL` | PostgREST only | Base URL of the umetrics PostgREST server |
|
|
176
|
+
| `HAZO_UMETRICS_DATABASE_API_KEY` | PostgREST only | Bearer token for the umetrics PostgREST server |
|
|
177
|
+
| `HAZO_UMETRICS_DATABASE_TYPE` | No | `postgrest` (default) or `sqlite` |
|
|
178
|
+
| `HAZO_UMETRICS_DATABASE_PATH` | SQLite only | Path to the SQLite file (default: `./umetrics.sqlite`) |
|
|
179
|
+
|
|
180
|
+
These are independent of the host app's `POSTGREST_URL` / `POSTGREST_API_KEY` — the
|
|
181
|
+
analytics DB can be a completely different PostgREST instance (or a local SQLite file for dev).
|
|
143
182
|
|
|
144
183
|
## Config
|
|
145
184
|
|
package/SETUP_CHECKLIST.md
CHANGED
|
@@ -33,7 +33,24 @@ db.close();
|
|
|
33
33
|
|
|
34
34
|
**PostgreSQL:** Run `node_modules/hazo_umetrics/db_setup_postgres.sql` against your database.
|
|
35
35
|
|
|
36
|
-
## 3. Set env vars for
|
|
36
|
+
## 3. Set env vars for the umetrics database
|
|
37
|
+
|
|
38
|
+
`createUmetricsConnect()` reads these variables to connect to the dedicated umetrics DB:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# PostgREST mode (production / staging)
|
|
42
|
+
HAZO_UMETRICS_DATABASE_URL=https://your-umetrics-postgrest.example.com
|
|
43
|
+
HAZO_UMETRICS_DATABASE_API_KEY=<bearer-token>
|
|
44
|
+
|
|
45
|
+
# SQLite mode (local dev — omit DATABASE_URL to fall back to ./umetrics.sqlite)
|
|
46
|
+
# HAZO_UMETRICS_DATABASE_TYPE=sqlite
|
|
47
|
+
# HAZO_UMETRICS_DATABASE_PATH=./umetrics.sqlite
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> Run `db_setup_postgres.sql` (or `db_setup_sqlite.sql`) against this **umetrics DB**, not
|
|
51
|
+
> the host app's main database.
|
|
52
|
+
|
|
53
|
+
## 4. Set env vars for cookie signing
|
|
37
54
|
|
|
38
55
|
```bash
|
|
39
56
|
# Generate a 32-byte key: node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
|
|
@@ -41,11 +58,11 @@ HAZO_UMETRICS_COOKIE_KEY_CURRENT=v1
|
|
|
41
58
|
HAZO_UMETRICS_COOKIE_KEY_v1=<base64-encoded 32-byte key>
|
|
42
59
|
```
|
|
43
60
|
|
|
44
|
-
##
|
|
61
|
+
## 5. Add config file (optional)
|
|
45
62
|
|
|
46
63
|
Copy `node_modules/hazo_umetrics/config/hazo_umetrics_config.ini.sample` to your app's config directory as `hazo_umetrics_config.ini` and fill in `[ga4]` when ready for M1.
|
|
47
64
|
|
|
48
|
-
##
|
|
65
|
+
## 6. Mount API routes (Next.js)
|
|
49
66
|
|
|
50
67
|
```ts
|
|
51
68
|
// app/api/hazo_umetrics/stats/route.ts
|
|
@@ -58,7 +75,7 @@ export async function GET(req: Request) { return handlers.getStat(req); }
|
|
|
58
75
|
export async function POST(req: Request) { return handlers.recordStat(req); }
|
|
59
76
|
```
|
|
60
77
|
|
|
61
|
-
##
|
|
78
|
+
## 7. Wrap your app in HazoMetricsProvider (client)
|
|
62
79
|
|
|
63
80
|
```tsx
|
|
64
81
|
// app/layout.tsx
|
|
@@ -73,7 +90,7 @@ export default function RootLayout({ children }) {
|
|
|
73
90
|
}
|
|
74
91
|
```
|
|
75
92
|
|
|
76
|
-
##
|
|
93
|
+
## 8. Configure Tailwind (if you use hazo_umetrics UI components)
|
|
77
94
|
|
|
78
95
|
`MetricsPanel` uses shadcn token utilities (`bg-background`, `text-muted-foreground`, `border`, etc.) from `hazo_ui`. Without the token definitions the dialog panel will render transparent. In your `globals.css`:
|
|
79
96
|
|
|
@@ -112,7 +129,7 @@ export default function RootLayout({ children }) {
|
|
|
112
129
|
|
|
113
130
|
(Adjust `@import` and `@source` depth to match your project's distance from `node_modules`.)
|
|
114
131
|
|
|
115
|
-
##
|
|
132
|
+
## 9. Smoke test
|
|
116
133
|
|
|
117
134
|
```ts
|
|
118
135
|
import { recordStat, getLatestStat } from 'hazo_umetrics';
|
|
@@ -8,14 +8,25 @@
|
|
|
8
8
|
app_id = my_app
|
|
9
9
|
|
|
10
10
|
[database]
|
|
11
|
-
;
|
|
12
|
-
;
|
|
11
|
+
; Dedicated hazo_umetrics database — separate from the host app's main DB.
|
|
12
|
+
; Use createUmetricsConnect() from hazo_umetrics to wire this adapter.
|
|
13
|
+
;
|
|
14
|
+
; type: postgrest (default) | sqlite
|
|
15
|
+
; Env-var override: HAZO_UMETRICS_DATABASE_TYPE
|
|
13
16
|
; type = postgrest
|
|
17
|
+
;
|
|
18
|
+
; PostgREST mode (recommended for production):
|
|
19
|
+
; base_url — the PostgREST server base URL
|
|
20
|
+
; Env-var override: HAZO_UMETRICS_DATABASE_URL
|
|
14
21
|
; base_url = http://your-postgrest-host:4444
|
|
15
|
-
;
|
|
22
|
+
; api_key — bearer token (env only, never store in INI)
|
|
23
|
+
; Set: HAZO_UMETRICS_DATABASE_API_KEY=<your-token>
|
|
24
|
+
;
|
|
25
|
+
; SQLite mode (local development without a Postgres instance):
|
|
26
|
+
; database_path — path to the SQLite file (default: ./umetrics.sqlite)
|
|
27
|
+
; Env-var override: HAZO_UMETRICS_DATABASE_PATH
|
|
16
28
|
; type = sqlite
|
|
17
|
-
;
|
|
18
|
-
; database_path = ./umetrics_test.sqlite
|
|
29
|
+
; database_path = ./umetrics.sqlite
|
|
19
30
|
|
|
20
31
|
[env]
|
|
21
32
|
; Uncomment to hard-code the environment name (overrides HAZO_ENV / NODE_ENV).
|
package/dist/lib/index.d.ts
CHANGED
package/dist/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AACA,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AACA,cAAc,oBAAoB,CAAC;AACnC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,qBAAqB,CAAC;AACpC,cAAc,kBAAkB,CAAC;AACjC,cAAc,0BAA0B,CAAC;AACzC,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC"}
|
package/dist/lib/index.js
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import 'server-only';
|
|
2
|
+
import { type HazoConnectAdapter } from 'hazo_connect/server';
|
|
3
|
+
export interface UmetricsConnectOptions {
|
|
4
|
+
type?: string;
|
|
5
|
+
base_url?: string;
|
|
6
|
+
api_key?: string;
|
|
7
|
+
database_path?: string;
|
|
8
|
+
iniPath?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function createUmetricsConnect(opts?: UmetricsConnectOptions): HazoConnectAdapter;
|
|
11
|
+
//# sourceMappingURL=connect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/server/connect.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAC;AACrB,OAAO,EAAqB,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAIjF,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,wBAAgB,qBAAqB,CAAC,IAAI,CAAC,EAAE,sBAAsB,GAAG,kBAAkB,CASvF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import 'server-only';
|
|
2
|
+
import { createHazoConnect } from 'hazo_connect/server';
|
|
3
|
+
import { HazoConfig } from 'hazo_config/server';
|
|
4
|
+
import { resolve as pathResolve } from 'node:path';
|
|
5
|
+
let _cached = null;
|
|
6
|
+
export function createUmetricsConnect(opts) {
|
|
7
|
+
// If opts explicitly provided, always create fresh (not cached)
|
|
8
|
+
if (opts) {
|
|
9
|
+
return _build(opts);
|
|
10
|
+
}
|
|
11
|
+
if (!_cached) {
|
|
12
|
+
_cached = _build(opts);
|
|
13
|
+
}
|
|
14
|
+
return _cached;
|
|
15
|
+
}
|
|
16
|
+
function _build(opts) {
|
|
17
|
+
// Load INI config (graceful — missing file falls back to env/opts)
|
|
18
|
+
const iniPath = opts?.iniPath ?? pathResolve(process.cwd(), 'config/hazo_umetrics_config.ini');
|
|
19
|
+
let _cfg = null;
|
|
20
|
+
try {
|
|
21
|
+
_cfg = new HazoConfig({ filePath: iniPath });
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Config file missing — fall back to env vars and opts
|
|
25
|
+
}
|
|
26
|
+
function ini(section, key) {
|
|
27
|
+
return _cfg?.get(section, key);
|
|
28
|
+
}
|
|
29
|
+
// Resolution order: opts > env > INI > default
|
|
30
|
+
const type = opts?.type ??
|
|
31
|
+
process.env.HAZO_UMETRICS_DATABASE_TYPE ??
|
|
32
|
+
ini('database', 'type') ??
|
|
33
|
+
'postgrest';
|
|
34
|
+
if (type === 'sqlite') {
|
|
35
|
+
const database_path = opts?.database_path ??
|
|
36
|
+
process.env.HAZO_UMETRICS_DATABASE_PATH ??
|
|
37
|
+
ini('database', 'database_path') ??
|
|
38
|
+
'./umetrics.sqlite';
|
|
39
|
+
return createHazoConnect({
|
|
40
|
+
type: 'sqlite',
|
|
41
|
+
sqlite: { driver: 'better-sqlite3', database_path },
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
// PostgREST mode
|
|
45
|
+
const base_url = opts?.base_url ??
|
|
46
|
+
process.env.HAZO_UMETRICS_DATABASE_URL ??
|
|
47
|
+
ini('database', 'base_url');
|
|
48
|
+
const api_key = opts?.api_key ??
|
|
49
|
+
process.env.HAZO_UMETRICS_DATABASE_API_KEY;
|
|
50
|
+
if (!base_url || !api_key) {
|
|
51
|
+
throw new Error('hazo_umetrics: HAZO_UMETRICS_DATABASE_URL and HAZO_UMETRICS_DATABASE_API_KEY must be set ' +
|
|
52
|
+
'(or pass { base_url, api_key } to createUmetricsConnect)');
|
|
53
|
+
}
|
|
54
|
+
return createHazoConnect({
|
|
55
|
+
type: 'postgrest',
|
|
56
|
+
postgrest: { base_url, api_key },
|
|
57
|
+
});
|
|
58
|
+
}
|