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 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. Creates 7 tables, all prefixed `hazo_umetrics_`.
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
 
@@ -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 cookie signing
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
- ## 4. Add config file (optional)
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
- ## 5. Mount API routes (Next.js)
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
- ## 6. Wrap your app in HazoMetricsProvider (client)
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
- ## 7. Configure Tailwind (if you use hazo_umetrics UI components)
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
- ## 8. Smoke test
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
- ; Set type = postgrest to use a PostgREST HTTP adapter (recommended for production).
12
- ; Set type = sqlite for local development without a Postgres instance.
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
- ; api_key comes from POSTGREST_API_KEY env var do not store it here.
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
- ; driver = better-sqlite3
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).
@@ -7,4 +7,5 @@ export * from '../server/ga4.js';
7
7
  export * from '../server/experiments.js';
8
8
  export * from '../server/activity.js';
9
9
  export * from '../server/behavior.js';
10
+ export * from '../server/connect.js';
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -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
@@ -8,3 +8,4 @@ export * from '../server/ga4.js';
8
8
  export * from '../server/experiments.js';
9
9
  export * from '../server/activity.js';
10
10
  export * from '../server/behavior.js';
11
+ export * from '../server/connect.js';
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hazo_umetrics",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "Product analytics for hazo apps — GA4 hybrid + first-party stat store + feature flags",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",