@absolutejs/errors-postgres

中文文档
0.2.0发布于 2026/10/22Apache-2.0TypeScriptES Module构建来源已验证近 30 天下载 2394

Postgres-backed, Effect-native IssueStore for @absolutejs/errors with first-class Drizzle and tagged-template adapters.

原始使用文档
文档目录9 节
  1. Install
  2. Usage
  3. Drizzle
  4. Tagged-template compatibility
  5. What it implements
  6. Grouping semantics (mirrors createMemoryIssueStore exactly)
  7. Schema (lazy, idempotent)
  8. Triage
  9. License

@absolutejs/errors-postgres

Postgres-backed, Effect-native IssueStore for @absolutejs/errors — the durable "Issues" surface (Sentry's product core), self-hosted on your own Postgres.

Durable grouped issues + an append-only event timeline, with new vs. regression detection resolved in a single atomic CTE upsert — one round-trip, no transaction, so it works over Neon's HTTP driver too.

Install

bun add @absolutejs/errors-postgres
# plus your driver + peers:
bun add @absolutejs/errors effect postgres   # or @neondatabase/serverless

postgres and @neondatabase/serverless are optional peers — install whichever driver you use.

Usage

Drizzle

Re-export the package tables from your application schema and manage them with your normal migration workflow:

export { errorEvents, errorIssues } from "@absolutejs/errors-postgres";
import { createDrizzleIssueStore } from "@absolutejs/errors-postgres";

const store = createDrizzleIssueStore({ db });

The Drizzle store uses transactions for atomic event/group updates, portable native JSONB for tags and extra context, row locking for regression detection, and the same Effect error channel as the tagged-template adapter. JSONB values are passed to Drizzle drivers as native objects (rather than pre-serialized strings), preventing Bun SQL from storing them as JSON strings.

Tagged-template compatibility

import postgres from "postgres";
import { createErrorTracker } from "@absolutejs/errors";
import { createPostgresIssueStore } from "@absolutejs/errors-postgres";

const sql = postgres(process.env.DATABASE_URL!);

const errors = createErrorTracker({
  project: "acme",
  release: process.env.RELEASE,
  store: createPostgresIssueStore({ sql }), // schema auto-created, lazy
  onIssue: (r) => alert(r.issue), // only on new / regression
});

await errors.captureException(err, { traceId, replayId });

Works identically with @neondatabase/serverless:

import { neon } from "@neondatabase/serverless";
const sql = neon(process.env.DATABASE_URL!);
createPostgresIssueStore({ sql });

What it implements

Every method returns an Effect with a typed IssueStoreError channel (IssueStoreSchemaError / IssueStoreQueryError / IssueStoreSerializationError) — failures are values, not throws.

MethodEffect
record(event)atomic upsert (one event) → { issue, isNew, isRegression }
recordCoalesced(group)count-aware upsert + bulk unnest insert — one round-trip per herd
listIssues(filter?)dashboard list (project / environment / state / ILIKE title), newest-first
getIssue(project, fingerprint)Option<IssueRecord>
setState(project, fingerprint, state)resolve / ignore / unresolve
assign(project, fingerprint, who | null)triage
listEvents(project, fingerprint, limit?)occurrence timeline, newest-first

Grouping semantics (mirrors createMemoryIssueStore exactly)

  • new vs. regression — detected in one statement: xmax = 0 ⇒ the row was inserted (new); a CTE captures the pre-update state, so a resolved issue seen again is a regression and flips back to unresolved. ignored issues stay muted.
  • severity escalates, never de-escalates — fatal > error > warning > info.
  • first/last release tracked across captures (first_release backfills).
  • lastSeen uses GREATEST — out-of-order events never rewind the clock.

Schema (lazy, idempotent)

Created on first use (set ensureSchema: false to manage it via migrations). tablePrefix defaults to error → error_issues + error_events.

error_issues  PK (project, fingerprint)   -- one row per grouped issue
error_events  bigserial id                -- append-only occurrences (jsonb tags/extra)

Indexes: (project, last_seen DESC) and (project, state) on issues; (project, fingerprint, at DESC) on events.

trace_id / span_id (→ @absolutejs/telemetry) and replay_id (→ @absolutejs/replay) are stored per event so a dashboard can cross-link an issue to its exact trace and DOM replay.

error_issues also carries the resolution trail — resolved_at, resolved_by, resolved_release, resolution_note — which the triage board writes when an issue is closed. ensureSchema adds them to an existing table; an application that owns its schema through migrations must add them itself before upgrading, because the store writes every column it knows about.

Choose the schema-derived Drizzle store for application-managed databases or the raw tagged-template compatibility store for lightweight and Neon HTTP integrations.

Triage

The store answers "what broke and how often". A board on top of it has to answer what a person actually asks in front of it: what is breaking now, who is on it, did the fix hold, which release introduced this. createIssueTriage is those queries, bound to one project.

import { createIssueTriage } from "@absolutejs/errors-postgres";

const triage = createIssueTriage({ db, project: "acme" });

await triage.list({ severity: "error", state: "unresolved" });
await triage.get(fingerprint); // issue + recent events
await triage.occurrences(fingerprint, 14); // zero-filled daily sparkline
await triage.stateCounts(); // numbers for the filter chips
await triage.setState(fingerprint, "resolved", { by, note, release });
await triage.assign(fingerprint, "alex");
await triage.releaseStats(["sha1", "sha2"]);

A regression is derived, not stored: the ingest upsert flips a resolved issue back to unresolved when it recurs but leaves resolved_at in place, so unresolved + a resolution stamp + activity after it means the fix did not hold. setState clears the trail on reopen, because a manual reopen is a decision rather than a failed fix — and a surviving stamp would make the next recurrence look like one.

releaseStats is the per-release rollup a deployments ledger needs: for each release, how many issues were introduced under it, carried in from before it, came back in it, or were declared fixed in it — split by severity, because a deploy that introduced three warnings is not the same news as one that introduced three errors. Each issue lands in exactly one bucket per release.

License

Apache-2.0.

版本与发布记录

@absolutejs/errors-postgres 共发布 13 个版本,首次发布于 2026/06/25;过去 12 个月发布了 13 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.2.0latest2026/10/2216 天前
0.2.0-beta.0beta2026/09/012 个月前
0.1.42026/08/063 个月前
0.1.32026/07/313 个月前
0.1.22026/07/293 个月前
0.1.12026/07/253 个月前

关于 @absolutejs/errors-postgres 的常见问题

@absolutejs/errors-postgres 是什么?

Postgres-backed, Effect-native IssueStore for @absolutejs/errors with first-class Drizzle and tagged-template adapters.

如何安装 @absolutejs/errors-postgres?

在项目目录运行 npm install @absolutejs/errors-postgres,或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖:effect(>=3.22.0 <4)、drizzle-orm(>=1.0.0-rc.4 <2)、@absolutejs/errors(>=0.9.0 <0.11.0)。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖,Yarn 需要手动安装。

@absolutejs/errors-postgres 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@absolutejs/errors-postgres 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@absolutejs/errors-postgres 可以商用吗?

@absolutejs/errors-postgres 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@absolutejs/errors-postgres 的下载量有多少?

根据 npm 官方下载统计,@absolutejs/errors-postgres 在 2026/10/05 – 2026/11/03 这 30 天内被下载 2,394 次,日均约 80 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@absolutejs/errors-postgres 还在维护吗?

最新版本 0.2.0 发布于 2026/10/22(16 天前);至今共发布 13 个版本,其中过去 12 个月发布了 13 个;首次发布于 2026/06/25。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。