@btravstack/prisma 0.44.0 → 0.44.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/README.md CHANGED
@@ -12,8 +12,17 @@ pnpm add @btravstack/prisma @btravstack/core @btravstack/config @btravstack/di u
12
12
  @prisma/orm-postgres@8.0.0-rc.18 temporal-polyfill@1.0.4
13
13
  ```
14
14
 
15
- Install the required peers and Postgres polyfill, so the application holds a single
16
- copy of each. The `prisma` CLI is yours, as a dev dependency: the client is
15
+ Install the required peers, so the application holds a single copy of each;
16
+ `temporal-polyfill` is the Postgres package's own peer from rc.14. The
17
+ `@prisma/orm-postgres` peer is `^8.0.0-rc.11`, and both ends are tested: an
18
+ application already on rc.11 keeps it.
19
+
20
+ **Upgrading with released migrations:** upgrade `@btravstack/prisma` and its
21
+ `@btravstack/*` peers, and leave Prisma where it is. No contract, migration or
22
+ database marker changes. Upgrading Prisma itself from rc.11 rewrites every
23
+ released migration and re-signs every database, which is Prisma's path rather
24
+ than this package's; see
25
+ [Supported Prisma trains](https://btravstack.github.io/btravstack/reference/prisma#supported-prisma-trains). The `prisma` CLI is yours, as a dev dependency: the client is
17
26
  built from _your_ emitted contract, so there is none for this package to ship.
18
27
  Node `>=22.12`.
19
28
 
@@ -96,7 +105,7 @@ the per-query observation below comes from.
96
105
  lane, folded into the kernel's `GET /healthz` with nothing wired. `/readyz`
97
106
  does not read it: failing readiness on a dependency every replica shares
98
107
  removes them all at once.
99
- - **Every query handed to `Observers`**, as one SQL activity middleware, so
108
+ - **Every query handed to `Observers`**, as one statement middleware, so
100
109
  composing `observability()` writes the failures as lines and `otel()` opens
101
110
  the spans and mints the instruments — with no flag here and no port list to
102
111
  satisfy when you compose neither. A middleware sees the ORM lane, the SQL
@@ -188,7 +197,7 @@ rather than the role `DATABASE_URL` carries. An application that migrates itself
188
197
  at boot races every other replica.
189
198
 
190
199
  **Engine-level tracing.** There is no engine: Prisma 8 is a TypeScript runtime
191
- and ships no instrumentation package, so the SQL activity middleware is the
200
+ and ships no instrumentation package, so the statement middleware is the
192
201
  whole seam and it already carries the runtime's own latency.
193
202
 
194
203
  **Transactions.** Commit boundaries belong to the adapter, spelled at the call —
package/dist/index.cjs CHANGED
@@ -4,33 +4,63 @@ let _btravstack_core = require("@btravstack/core");
4
4
  let _btravstack_di = require("@btravstack/di");
5
5
  let unthrown = require("unthrown");
6
6
  //#region src/instrument.ts
7
- /** Observe both SQL lanes from encoding until their statement activity ends. */
7
+ /**
8
+ * Every statement, handed to the graph's observers: a span around it, a count
9
+ * of how it came out, and a log line if it failed.
10
+ *
11
+ * The operation STARTS before the driver runs and settles after it, because
12
+ * `Observers` answers a finisher precisely so a span can be opened around the
13
+ * work. On rc.18 the finisher rides the activity's own `ctx.state`. On rc.11 it
14
+ * is keyed WEAKLY by the operation's context, the one object every hook of a
15
+ * statement is handed: a statement refused after `beforeQuery` (a later
16
+ * middleware, a codec, an abort) never reaches `afterQuery`, and a map keyed by
17
+ * `planExecutionId` would keep its finisher for the client's lifetime.
18
+ */
8
19
  const queryObserver = (observers) => {
9
20
  const key = Symbol("btravstack-query-observer");
21
+ const inFlight = /* @__PURE__ */ new WeakMap();
22
+ const start = (plan, scope) => (0, _btravstack_core.observe)(observers, {
23
+ component: "database",
24
+ name: "query",
25
+ attributes: { scope: scope ?? "runtime" },
26
+ ...plan.sql === void 0 ? {} : { details: { sql: plan.sql } }
27
+ });
28
+ const settle = (finish, ok, source) => {
29
+ finish?.({
30
+ outcome: ok ? "ok" : "error",
31
+ ...source === void 0 ? {} : { attributes: { source } }
32
+ });
33
+ };
34
+ const beforePlan = (plan, ctx) => {
35
+ inFlight.set(ctx, start(plan, ctx.scope));
36
+ return Promise.resolve();
37
+ };
38
+ const afterPlan = (_plan, result, ctx) => {
39
+ const finish = inFlight.get(ctx);
40
+ inFlight.delete(ctx);
41
+ settle(finish, result.completed, result.source);
42
+ return Promise.resolve();
43
+ };
10
44
  return {
11
45
  name: "btravstack-observers",
12
46
  familyId: "sql",
13
47
  beforeEncode: (plan, ctx) => {
14
- ctx.state(key)["settle"] = (0, _btravstack_core.observe)(observers, {
15
- component: "database",
16
- name: "query",
17
- attributes: { scope: ctx.scope ?? "runtime" },
18
- ...plan.sql === void 0 ? {} : { details: { sql: plan.sql } }
19
- });
48
+ ctx.state(key)["settle"] = start(plan, ctx.scope);
20
49
  return Promise.resolve();
21
50
  },
22
51
  activityEnded: (_activity, result, ctx) => {
23
52
  if (result.kind === "statement") {
24
53
  const state = ctx.state(key);
25
- const settle = state["settle"];
54
+ const finish = state["settle"];
26
55
  delete state["settle"];
27
- settle?.({
28
- outcome: result.outcome === "succeeded" ? "ok" : "error",
29
- ...result.source === void 0 ? {} : { attributes: { source: result.source } }
30
- });
56
+ settle(finish, result.outcome === "succeeded", result.source);
31
57
  }
32
58
  return Promise.resolve();
33
- }
59
+ },
60
+ beforeQuery: beforePlan,
61
+ afterQuery: afterPlan,
62
+ beforeExecute: beforePlan,
63
+ afterExecute: afterPlan
34
64
  };
35
65
  };
36
66
  //#endregion
package/dist/index.d.cts CHANGED
@@ -1,29 +1,55 @@
1
1
  import { HealthChecks, Observers } from "@btravstack/core";
2
2
  import { Module, PortClassOf } from "@btravstack/di";
3
3
  //#region src/instrument.d.ts
4
- /** Structural Prisma 8 SQL hooks; no target-package import reaches consumers. */
4
+ /**
5
+ * A Prisma 8 SQL middleware, structurally, with the hooks of BOTH release
6
+ * trains the peer range admits.
7
+ *
8
+ * Prisma 8.0.0-rc.18 replaced the per-statement hooks: `beforeQuery` and
9
+ * `beforeExecute` became `beforeEncode`, `afterQuery` and `afterExecute`
10
+ * became `activityEnded`. The two sets are disjoint and neither runtime refuses
11
+ * a hook it does not know, so one object serves both: each runtime calls its
12
+ * own pair and never the other's. Declared here rather than imported so the
13
+ * option's type drags no target package into a consumer.
14
+ */
5
15
  type SqlMiddlewareLike = {
6
16
  readonly name: string;
7
17
  readonly familyId: "sql";
8
- readonly beforeEncode: (plan: Plan, ctx: MiddlewareContext) => Promise<void>;
18
+ /** rc.18 and later: before the statement's parameters are encoded, on both lanes. */
19
+ readonly beforeEncode: (plan: Plan, ctx: ActivityContext) => Promise<void>;
20
+ /** rc.18 and later: a statement or a transaction ended. */
9
21
  readonly activityEnded: (activity: {
10
22
  readonly kind: "statement" | "transaction";
11
- }, result: Settled, ctx: MiddlewareContext) => Promise<void>;
23
+ }, result: ActivityEnded, ctx: ActivityContext) => Promise<void>;
24
+ /** rc.11 to rc.17: a statement read for its rows. */
25
+ readonly beforeQuery: (plan: Plan, ctx: PlanContext) => Promise<void>;
26
+ readonly afterQuery: (plan: Plan, result: Completed, ctx: PlanContext) => Promise<void>;
27
+ /** rc.11 to rc.17: a statement run for its statistics, such as a write with no `RETURNING`. */
28
+ readonly beforeExecute: (plan: Plan, ctx: PlanContext) => Promise<void>;
29
+ readonly afterExecute: (plan: Plan, result: Completed, ctx: PlanContext) => Promise<void>;
12
30
  };
13
31
  type Plan = {
14
32
  readonly sql?: string;
15
33
  };
16
- type MiddlewareContext = {
17
- readonly scope?: "runtime" | "connection" | "transaction";
34
+ type Scope = "runtime" | "connection" | "transaction";
35
+ type ActivityContext = {
36
+ readonly scope?: Scope;
18
37
  readonly state: (key: string | symbol) => Record<string | symbol, unknown>;
19
38
  };
20
- type Settled = {
39
+ type ActivityEnded = {
21
40
  readonly kind: "statement";
22
41
  readonly outcome: "succeeded" | "failed" | "stopped";
23
42
  readonly source?: string;
24
43
  } | {
25
44
  readonly kind: "transaction";
26
45
  };
46
+ type PlanContext = {
47
+ readonly scope?: Scope;
48
+ };
49
+ type Completed = {
50
+ readonly completed: boolean;
51
+ readonly source?: string;
52
+ };
27
53
  //#endregion
28
54
  //#region src/prisma.d.ts
29
55
  /**
@@ -87,9 +113,9 @@ type PrismaBinding = {
87
113
  /** `DATABASE_URL`, read through `Config` rather than by the application. */
88
114
  readonly url: string;
89
115
  /**
90
- * The starter's own middleware — one `afterQuery` hook reporting every
91
- * query to `Observers`. Spread it into the client's `middleware` array
92
- * beside any of the application's own.
116
+ * The starter's own middleware — the statement hooks of every supported
117
+ * Prisma train, reporting every query to `Observers`. Spread it into the
118
+ * client's `middleware` array beside any of the application's own.
93
119
  */
94
120
  readonly middleware: readonly SqlMiddlewareLike[];
95
121
  };
package/dist/index.d.mts CHANGED
@@ -1,29 +1,55 @@
1
1
  import { HealthChecks, Observers } from "@btravstack/core";
2
2
  import { Module, PortClassOf } from "@btravstack/di";
3
3
  //#region src/instrument.d.ts
4
- /** Structural Prisma 8 SQL hooks; no target-package import reaches consumers. */
4
+ /**
5
+ * A Prisma 8 SQL middleware, structurally, with the hooks of BOTH release
6
+ * trains the peer range admits.
7
+ *
8
+ * Prisma 8.0.0-rc.18 replaced the per-statement hooks: `beforeQuery` and
9
+ * `beforeExecute` became `beforeEncode`, `afterQuery` and `afterExecute`
10
+ * became `activityEnded`. The two sets are disjoint and neither runtime refuses
11
+ * a hook it does not know, so one object serves both: each runtime calls its
12
+ * own pair and never the other's. Declared here rather than imported so the
13
+ * option's type drags no target package into a consumer.
14
+ */
5
15
  type SqlMiddlewareLike = {
6
16
  readonly name: string;
7
17
  readonly familyId: "sql";
8
- readonly beforeEncode: (plan: Plan, ctx: MiddlewareContext) => Promise<void>;
18
+ /** rc.18 and later: before the statement's parameters are encoded, on both lanes. */
19
+ readonly beforeEncode: (plan: Plan, ctx: ActivityContext) => Promise<void>;
20
+ /** rc.18 and later: a statement or a transaction ended. */
9
21
  readonly activityEnded: (activity: {
10
22
  readonly kind: "statement" | "transaction";
11
- }, result: Settled, ctx: MiddlewareContext) => Promise<void>;
23
+ }, result: ActivityEnded, ctx: ActivityContext) => Promise<void>;
24
+ /** rc.11 to rc.17: a statement read for its rows. */
25
+ readonly beforeQuery: (plan: Plan, ctx: PlanContext) => Promise<void>;
26
+ readonly afterQuery: (plan: Plan, result: Completed, ctx: PlanContext) => Promise<void>;
27
+ /** rc.11 to rc.17: a statement run for its statistics, such as a write with no `RETURNING`. */
28
+ readonly beforeExecute: (plan: Plan, ctx: PlanContext) => Promise<void>;
29
+ readonly afterExecute: (plan: Plan, result: Completed, ctx: PlanContext) => Promise<void>;
12
30
  };
13
31
  type Plan = {
14
32
  readonly sql?: string;
15
33
  };
16
- type MiddlewareContext = {
17
- readonly scope?: "runtime" | "connection" | "transaction";
34
+ type Scope = "runtime" | "connection" | "transaction";
35
+ type ActivityContext = {
36
+ readonly scope?: Scope;
18
37
  readonly state: (key: string | symbol) => Record<string | symbol, unknown>;
19
38
  };
20
- type Settled = {
39
+ type ActivityEnded = {
21
40
  readonly kind: "statement";
22
41
  readonly outcome: "succeeded" | "failed" | "stopped";
23
42
  readonly source?: string;
24
43
  } | {
25
44
  readonly kind: "transaction";
26
45
  };
46
+ type PlanContext = {
47
+ readonly scope?: Scope;
48
+ };
49
+ type Completed = {
50
+ readonly completed: boolean;
51
+ readonly source?: string;
52
+ };
27
53
  //#endregion
28
54
  //#region src/prisma.d.ts
29
55
  /**
@@ -87,9 +113,9 @@ type PrismaBinding = {
87
113
  /** `DATABASE_URL`, read through `Config` rather than by the application. */
88
114
  readonly url: string;
89
115
  /**
90
- * The starter's own middleware — one `afterQuery` hook reporting every
91
- * query to `Observers`. Spread it into the client's `middleware` array
92
- * beside any of the application's own.
116
+ * The starter's own middleware — the statement hooks of every supported
117
+ * Prisma train, reporting every query to `Observers`. Spread it into the
118
+ * client's `middleware` array beside any of the application's own.
93
119
  */
94
120
  readonly middleware: readonly SqlMiddlewareLike[];
95
121
  };
package/dist/index.mjs CHANGED
@@ -3,33 +3,63 @@ import { HealthCheckFailed, HealthChecks, Observers, noObserverMember, observe }
3
3
  import { Module, Port, Provider } from "@btravstack/di";
4
4
  import { TaggedError, fromPromise, fromSafePromise } from "unthrown";
5
5
  //#region src/instrument.ts
6
- /** Observe both SQL lanes from encoding until their statement activity ends. */
6
+ /**
7
+ * Every statement, handed to the graph's observers: a span around it, a count
8
+ * of how it came out, and a log line if it failed.
9
+ *
10
+ * The operation STARTS before the driver runs and settles after it, because
11
+ * `Observers` answers a finisher precisely so a span can be opened around the
12
+ * work. On rc.18 the finisher rides the activity's own `ctx.state`. On rc.11 it
13
+ * is keyed WEAKLY by the operation's context, the one object every hook of a
14
+ * statement is handed: a statement refused after `beforeQuery` (a later
15
+ * middleware, a codec, an abort) never reaches `afterQuery`, and a map keyed by
16
+ * `planExecutionId` would keep its finisher for the client's lifetime.
17
+ */
7
18
  const queryObserver = (observers) => {
8
19
  const key = Symbol("btravstack-query-observer");
20
+ const inFlight = /* @__PURE__ */ new WeakMap();
21
+ const start = (plan, scope) => observe(observers, {
22
+ component: "database",
23
+ name: "query",
24
+ attributes: { scope: scope ?? "runtime" },
25
+ ...plan.sql === void 0 ? {} : { details: { sql: plan.sql } }
26
+ });
27
+ const settle = (finish, ok, source) => {
28
+ finish?.({
29
+ outcome: ok ? "ok" : "error",
30
+ ...source === void 0 ? {} : { attributes: { source } }
31
+ });
32
+ };
33
+ const beforePlan = (plan, ctx) => {
34
+ inFlight.set(ctx, start(plan, ctx.scope));
35
+ return Promise.resolve();
36
+ };
37
+ const afterPlan = (_plan, result, ctx) => {
38
+ const finish = inFlight.get(ctx);
39
+ inFlight.delete(ctx);
40
+ settle(finish, result.completed, result.source);
41
+ return Promise.resolve();
42
+ };
9
43
  return {
10
44
  name: "btravstack-observers",
11
45
  familyId: "sql",
12
46
  beforeEncode: (plan, ctx) => {
13
- ctx.state(key)["settle"] = observe(observers, {
14
- component: "database",
15
- name: "query",
16
- attributes: { scope: ctx.scope ?? "runtime" },
17
- ...plan.sql === void 0 ? {} : { details: { sql: plan.sql } }
18
- });
47
+ ctx.state(key)["settle"] = start(plan, ctx.scope);
19
48
  return Promise.resolve();
20
49
  },
21
50
  activityEnded: (_activity, result, ctx) => {
22
51
  if (result.kind === "statement") {
23
52
  const state = ctx.state(key);
24
- const settle = state["settle"];
53
+ const finish = state["settle"];
25
54
  delete state["settle"];
26
- settle?.({
27
- outcome: result.outcome === "succeeded" ? "ok" : "error",
28
- ...result.source === void 0 ? {} : { attributes: { source: result.source } }
29
- });
55
+ settle(finish, result.outcome === "succeeded", result.source);
30
56
  }
31
57
  return Promise.resolve();
32
- }
58
+ },
59
+ beforeQuery: beforePlan,
60
+ afterQuery: afterPlan,
61
+ beforeExecute: beforePlan,
62
+ afterExecute: afterPlan
33
63
  };
34
64
  };
35
65
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btravstack/prisma",
3
- "version": "0.44.0",
3
+ "version": "0.44.1",
4
4
  "description": "The Prisma starter for @btravstack/core: DATABASE_URL through Config, the Postgres driver adapter, and a client whose pool is the application scope's",
5
5
  "keywords": [
6
6
  "database",
@@ -63,9 +63,9 @@
63
63
  "./package.json": "./package.json"
64
64
  },
65
65
  "devDependencies": {
66
- "@btravstack/config": "0.44.0",
67
- "@btravstack/core": "0.44.0",
68
- "@btravstack/di": "0.44.0",
66
+ "@btravstack/config": "0.44.1",
67
+ "@btravstack/core": "0.44.1",
68
+ "@btravstack/di": "0.44.1",
69
69
  "@btravstack/tsconfig": "0.4.0",
70
70
  "@prisma/orm-postgres": "8.0.0-rc.18",
71
71
  "@types/node": "26.6.5",
@@ -79,10 +79,10 @@
79
79
  "vitest": "5.0.3"
80
80
  },
81
81
  "peerDependencies": {
82
- "@btravstack/config": "^0.44.0",
83
- "@btravstack/core": "^0.44.0",
84
- "@btravstack/di": "^0.44.0",
85
- "@prisma/orm-postgres": "^8.0.0-rc.18",
82
+ "@btravstack/config": "^0.44.1",
83
+ "@btravstack/core": "^0.44.1",
84
+ "@btravstack/di": "^0.44.1",
85
+ "@prisma/orm-postgres": "^8.0.0-rc.11",
86
86
  "unthrown": "^5.0.0"
87
87
  },
88
88
  "engines": {