@uptimizr/db-postgres 2.0.0 → 2.0.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/AGENTS.md +115 -0
- package/llms.txt +31 -0
- package/package.json +4 -2
package/AGENTS.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# AGENTS.md — @uptimizr/db-postgres
|
|
2
|
+
|
|
3
|
+
> Packaged agent guide. For the human reference see [README.md](./README.md); for design
|
|
4
|
+
> rationale see the project ADRs at https://github.com/RaananW/Uptimizr/tree/main/docs/adr.
|
|
5
|
+
|
|
6
|
+
## What this package is
|
|
7
|
+
|
|
8
|
+
The optional **single-tenant PostgreSQL store** for the Uptimizr collector — for self-hosters who
|
|
9
|
+
already run Postgres and want a familiar, **multi-writer** relational backend instead of the
|
|
10
|
+
default single-file DuckDB store (ADR 0020).
|
|
11
|
+
|
|
12
|
+
It is a **re-home + dialect emitter, not a rewrite**. Every analytics aggregation is authored once
|
|
13
|
+
in [`@uptimizr/db`](../db) against the dialect-agnostic query layer
|
|
14
|
+
(`buildX(projectId, opts, dialect)` → `QuerySpec`) and rendered to Postgres SQL with the shared
|
|
15
|
+
`postgresDialect` (also
|
|
16
|
+
exported from `@uptimizr/db`). This package adds a pooled [`pg`](https://node-postgres.com)
|
|
17
|
+
client, forward-only migrations, and metadata helpers that satisfy the same `CollectorStore`
|
|
18
|
+
contract as DuckDB and ClickHouse.
|
|
19
|
+
|
|
20
|
+
Server/Node only — no DOM imports. Single-tenant only: no `org_id`, no tenant isolation.
|
|
21
|
+
|
|
22
|
+
## Install / select it
|
|
23
|
+
|
|
24
|
+
The collector picks a store with `COLLECTOR_STORE`:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
COLLECTOR_STORE=postgres \
|
|
28
|
+
POSTGRES_URL=postgresql://uptimizr:uptimizr@localhost:5432/uptimizr \
|
|
29
|
+
npx -p @uptimizr/collector-server uptimizr serve
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
| Variable | Purpose |
|
|
33
|
+
| ---------------------------------- | -------------------------------------------------------------------------------------- |
|
|
34
|
+
| `POSTGRES_URL` (or `DATABASE_URL`) | libpq connection URI (`?sslmode=require` etc. apply). The database must already exist. |
|
|
35
|
+
| `POSTGRES_SCHEMA` | Schema the store's tables live in (default `public`; created on first boot). |
|
|
36
|
+
| `POSTGRES_POOL_MAX` | Maximum pooled connections per collector process (default `10`). |
|
|
37
|
+
|
|
38
|
+
Migrations run on store creation — idempotent, forward-only (ADR 0007) and serialized behind an
|
|
39
|
+
advisory lock, so several collector instances may boot concurrently against one database.
|
|
40
|
+
Postgres 14+ is supported (tested against 16).
|
|
41
|
+
|
|
42
|
+
`uptimizr init` / `new-project` / `new-key` / `migrate` / `regions` all honour `COLLECTOR_STORE`,
|
|
43
|
+
so export the store + connection variables before running them and the project you mint lives in
|
|
44
|
+
Postgres. A local Postgres is available from [`infra/docker`](../../../infra/docker)
|
|
45
|
+
(`pnpm stack:up`).
|
|
46
|
+
|
|
47
|
+
## Canonical usage
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import {
|
|
51
|
+
createPostgresClient,
|
|
52
|
+
migratePostgres,
|
|
53
|
+
insertEvents,
|
|
54
|
+
getSessionEvents,
|
|
55
|
+
resolveApiKey,
|
|
56
|
+
runPostgresQuery,
|
|
57
|
+
} from "@uptimizr/db-postgres";
|
|
58
|
+
import { buildPointerHeatmap, postgresDialect, type HeatmapBinRow } from "@uptimizr/db";
|
|
59
|
+
|
|
60
|
+
const db = createPostgresClient(settings);
|
|
61
|
+
await migratePostgres(db);
|
|
62
|
+
|
|
63
|
+
await insertEvents(db, events); // validated upstream at the collector boundary
|
|
64
|
+
const heat = await runPostgresQuery<HeatmapBinRow>(
|
|
65
|
+
db,
|
|
66
|
+
buildPointerHeatmap("project-id", { bins: 50 }, postgresDialect),
|
|
67
|
+
);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The `CollectorStore` itself is assembled from these building blocks in the collector server
|
|
71
|
+
(`oss/apps/collector-server/src/postgresStore.ts`, `createPostgresStore`) — this package stays a
|
|
72
|
+
store-agnostic toolkit.
|
|
73
|
+
|
|
74
|
+
## Rules for agents
|
|
75
|
+
|
|
76
|
+
- **Never author an aggregation here.** A new aggregation is a pure
|
|
77
|
+
`buildX(projectId, opts, dialect)` builder in `@uptimizr/db` plus a `@uptimizr/metrics` registry
|
|
78
|
+
entry. This package only renders and runs.
|
|
79
|
+
- **Migrations are forward-only, additive and idempotent** (ADR 0007). Append to
|
|
80
|
+
`POSTGRES_MIGRATIONS`; never edit a shipped migration. Keep the advisory lock so concurrent
|
|
81
|
+
collector boots stay safe.
|
|
82
|
+
- `runPostgresQuery` must call `coerceRows(spec.metric, rows)` at the one point rows leave the
|
|
83
|
+
driver (ADR 0051 §2) — `pg` hands back `int8` / `numeric` as strings. `null` stays `null`: an
|
|
84
|
+
aggregate over an empty set is "no samples", not `0`.
|
|
85
|
+
- Postgres has no `ASOF JOIN` and no MergeTree rollups; both are already closed at the **shared**
|
|
86
|
+
layer (`renderNearestRowJoin` and plain query-time `perf_daily` / `events_daily` views). Reuse
|
|
87
|
+
those helpers rather than hand-writing a per-query workaround.
|
|
88
|
+
- Validate events upstream at the collector boundary; this layer assumes valid input.
|
|
89
|
+
- API keys are only ever stored as **SHA-256 hashes** — never persist a raw key. Read capability
|
|
90
|
+
sets with `parseApiKeyCapabilities` and write them with `toApiKeyColumns` (from `@uptimizr/db`)
|
|
91
|
+
so ordering, validation and the per-key rate-limit columns stay consistent across engines.
|
|
92
|
+
- **The audit log records key ids, never keys.** Serialize parameters with `serializeAuditParams`
|
|
93
|
+
and clamp the endpoint with `clampAuditTool` before they reach `recordAudit`.
|
|
94
|
+
- Privacy (ADR 0003): no raw IPs, no PII. Raw per-session reads are gated by the collector's
|
|
95
|
+
`ENABLE_RAW_SESSION_RETENTION` **and** a `query:raw` capability — do not add a bypass here.
|
|
96
|
+
- **Parity is the contract.** The shared `PARITY_EVENTS` / `PARITY_CASES` / `diffParity` harness in
|
|
97
|
+
`@uptimizr/db` proves every engine returns equal analytics. Extend the fixtures and golden when
|
|
98
|
+
you add an aggregation or event type, and document any genuine divergence.
|
|
99
|
+
- Identifiers interpolated into DDL go through `assertSafeIdentifier` — never string-concatenate a
|
|
100
|
+
schema or table name from input.
|
|
101
|
+
|
|
102
|
+
## Tests and parity
|
|
103
|
+
|
|
104
|
+
`dialect.test.ts` is a pure unit suite (no server). `postgresParity.test.ts` /
|
|
105
|
+
`postgresStore.test.ts` probe `POSTGRES_URL` (or `DATABASE_URL`) first and **skip when no server is
|
|
106
|
+
reachable**, so the default `pnpm test` stays Docker-free; they use throwaway schemas dropped on
|
|
107
|
+
teardown. Set **`POSTGRES_PARITY_REQUIRED=1`** to fail instead of skipping — that is what the
|
|
108
|
+
"Store parity (Postgres)" CI job does (`pnpm test:parity:postgres` runs them locally with `.env`
|
|
109
|
+
loaded).
|
|
110
|
+
|
|
111
|
+
## More
|
|
112
|
+
|
|
113
|
+
- Package reference: [README.md](./README.md)
|
|
114
|
+
- Storage contracts + dialect layer: [`@uptimizr/db`](../db/AGENTS.md)
|
|
115
|
+
- Collector configuration: https://github.com/RaananW/Uptimizr/blob/main/docs/integration.md
|
package/llms.txt
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# @uptimizr/db-postgres
|
|
2
|
+
|
|
3
|
+
> The optional single-tenant PostgreSQL store for the Uptimizr collector (ADR 0020) — a familiar,
|
|
4
|
+
> multi-writer relational backend for teams that already run Postgres. A re-home + dialect
|
|
5
|
+
> emitter, not a rewrite: every aggregation is authored once in `@uptimizr/db` and rendered with
|
|
6
|
+
> the shared `postgresDialect`. Server/Node only; select it with `COLLECTOR_STORE=postgres`.
|
|
7
|
+
|
|
8
|
+
## Docs
|
|
9
|
+
|
|
10
|
+
- [Package reference](./README.md): when to use it, env vars, the row-store fit gaps and how they are closed, file layout.
|
|
11
|
+
- [Agent guide](./AGENTS.md): rules, canonical usage, parity and the `POSTGRES_PARITY_REQUIRED` gate.
|
|
12
|
+
- [Storage contracts + query layer](../db/README.md): `@uptimizr/db`, the `buildX`/`Dialect` layer, the parity harness.
|
|
13
|
+
- [Collector configuration](https://github.com/RaananW/Uptimizr/blob/main/docs/integration.md): `COLLECTOR_STORE` and the HTTP API.
|
|
14
|
+
- [Architecture Decision Records](https://github.com/RaananW/Uptimizr/tree/main/docs/adr): database choice (0002), privacy model (0003), migrations (0007), open-core storage boundary (0020), AI-first analytics layer (0051).
|
|
15
|
+
|
|
16
|
+
## Environment
|
|
17
|
+
|
|
18
|
+
- `COLLECTOR_STORE=postgres` selects this store.
|
|
19
|
+
- `POSTGRES_URL` (or `DATABASE_URL`) — libpq URI; the database must already exist. Postgres 14+.
|
|
20
|
+
- `POSTGRES_SCHEMA` (default `public`, created on first boot), `POSTGRES_POOL_MAX` (default `10`).
|
|
21
|
+
- `POSTGRES_PARITY_REQUIRED=1` makes the live parity suites fail instead of skipping (the CI job sets it).
|
|
22
|
+
|
|
23
|
+
## Key exports
|
|
24
|
+
|
|
25
|
+
- Client: `createPostgresClient(settings)`, `assertSafeIdentifier`, types `PostgresClient` / `PostgresExecutor` / `PostgresRow`.
|
|
26
|
+
- Migrations: `POSTGRES_MIGRATIONS`, `migratePostgres(client)` — forward-only, idempotent, advisory-locked (ADR 0007).
|
|
27
|
+
- Queries: `runPostgresQuery(client, spec)` — rewrites named to positional params and coerces numeric columns at the driver edge (ADR 0051 §2).
|
|
28
|
+
- Events: `insertEvents`, `getSessionEvents`, `streamSessionEvents`, `getSessionMeta`.
|
|
29
|
+
- Metadata: `createProject`, `getProject`, `createApiKey`, `resolveApiKey`, `hashApiKey`, `apiKeyPrefix`, `generateApiKey` (keys stored only as SHA-256 hashes).
|
|
30
|
+
- Audit: `recordAudit`, `listAudit`, `pruneAudit` (key ids, never keys).
|
|
31
|
+
- Scene registry: `upsertSceneProxy`, `getSceneRepresentation`, `listSceneRepresentations`, `putSceneRegions`, `getSceneRegions`, `listSceneRegions`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uptimizr/db-postgres",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Optional single-tenant PostgreSQL store for Uptimizr — composes the @uptimizr/db dialect-agnostic query layer and CollectorStore contract for self-hosters who already run Postgres and want a multi-writer relational backend.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"uptimizr",
|
|
@@ -36,7 +36,9 @@
|
|
|
36
36
|
"files": [
|
|
37
37
|
"dist",
|
|
38
38
|
"README.md",
|
|
39
|
-
"LICENSE"
|
|
39
|
+
"LICENSE",
|
|
40
|
+
"AGENTS.md",
|
|
41
|
+
"llms.txt"
|
|
40
42
|
],
|
|
41
43
|
"dependencies": {
|
|
42
44
|
"pg": "^8.23.0",
|