@btravstack/prisma 0.23.0 → 0.25.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/README.md CHANGED
@@ -74,6 +74,11 @@ the per-query observation below comes from.
74
74
  first query.
75
75
  - **The pool's lifetime.** The provider is _resourceful_, so `release` closes it
76
76
  on every exit path, including a boot that fails after it ran.
77
+ - **A database that answers before any work runs.** Opening the scope runs the
78
+ health check's probe, so a fresh pool is verified before its first burst of
79
+ transactions, and a database that does not answer fails the boot with
80
+ `DatabaseUnreachable` — the database's name and the driver's cause, never the
81
+ URL.
77
82
  - **A health check**, named after the starter — `SELECT 1` through the raw
78
83
  lane, folded into the kernel's `GET /healthz` with nothing wired. `/readyz`
79
84
  does not read it: failing readiness on a dependency every replica shares
package/dist/index.cjs CHANGED
@@ -65,6 +65,12 @@ const queryObserver = (observers) => {
65
65
  //#region src/prisma.ts
66
66
  /** The one place the cast above is spelled. */
67
67
  const probeable = (db) => db;
68
+ const probe = (db) => probeable(db).runtime().query(probeable(db).raw.sql`SELECT 1`.affectedCount().build());
69
+ /**
70
+ * The database did not answer when the scope opened. Carries the database's
71
+ * name and the driver's own failure, never the URL, which holds credentials.
72
+ */
73
+ var DatabaseUnreachable = class extends (0, unthrown.TaggedError)("DatabaseUnreachable") {};
68
74
  /**
69
75
  * A Prisma client whose pool is the application scope's.
70
76
  *
@@ -73,6 +79,11 @@ const probeable = (db) => db;
73
79
  * the client is reached through, and the resourceful provider that opens it and
74
80
  * closes it again.
75
81
  *
82
+ * **The database answers before the scope is handed out**: opening runs the
83
+ * health check's probe, so a pool is verified before any unit uses it, and a
84
+ * database that does not answer fails the boot with
85
+ * {@link DatabaseUnreachable}.
86
+ *
76
87
  * **The pool closes on every exit path**, including a boot that fails after
77
88
  * this provider ran — that is what makes it resourceful rather than a plain
78
89
  * value. `runtime().close()` ends the pool; Prisma dials again lazily on the
@@ -86,6 +97,10 @@ const probeable = (db) => db;
86
97
  const prismaDatabase = (name) => ({ client }) => {
87
98
  const config = _btravstack_config.Config.provider(`${name}Config`)(_btravstack_config.Config.object({ url: _btravstack_config.Config.string("DATABASE_URL") }));
88
99
  const DatabasePort = (0, _btravstack_di.Port)(name);
100
+ const verified = (db) => (0, unthrown.fromPromise)(probe(db).then(() => db, (cause) => probeable(db).runtime().close().finally(() => Promise.reject(cause))), (cause) => new DatabaseUnreachable({
101
+ database: name,
102
+ cause
103
+ }));
89
104
  const port = DatabasePort;
90
105
  const clientProvider = (0, _btravstack_di.Provider)(port)({
91
106
  inject: {
@@ -95,14 +110,14 @@ const prismaDatabase = (name) => ({ client }) => {
95
110
  acquire: ({ settings, observers }) => (0, unthrown.fromSafePromise)(Promise.resolve().then(() => client({
96
111
  url: settings.url,
97
112
  middleware: [queryObserver(observers)]
98
- }))),
113
+ }))).flatMap(verified),
99
114
  release: (db) => probeable(db).runtime().close()
100
115
  });
101
116
  const healthCheck = _btravstack_di.Provider.member(_btravstack_core.HealthChecks)({
102
117
  inject: { db: port },
103
118
  sync: ({ db }) => ({
104
119
  name,
105
- check: () => (0, unthrown.fromPromise)(probeable(db).runtime().query(probeable(db).raw.sql`SELECT 1`.affectedCount().build()), (cause) => new _btravstack_core.HealthCheckFailed({ reason: cause instanceof Error ? cause.message : "database unreachable" })).map(() => void 0)
120
+ check: () => (0, unthrown.fromPromise)(probe(db), (cause) => new _btravstack_core.HealthCheckFailed({ reason: cause instanceof Error ? cause.message : "database unreachable" })).map(() => void 0)
106
121
  })
107
122
  });
108
123
  const database = (0, _btravstack_di.Module)(name)({
@@ -118,4 +133,5 @@ const prismaDatabase = (name) => ({ client }) => {
118
133
  return Object.assign(database, { port: DatabasePort });
119
134
  };
120
135
  //#endregion
136
+ exports.DatabaseUnreachable = DatabaseUnreachable;
121
137
  exports.prismaDatabase = prismaDatabase;
package/dist/index.d.cts CHANGED
@@ -67,6 +67,15 @@ type PrismaLike = {
67
67
  */
68
68
  readonly runtime: unknown;
69
69
  };
70
+ declare const DatabaseUnreachable_base: import("unthrown").TaggedErrorConstructor<"DatabaseUnreachable">;
71
+ /**
72
+ * The database did not answer when the scope opened. Carries the database's
73
+ * name and the driver's own failure, never the URL, which holds credentials.
74
+ */
75
+ export declare class DatabaseUnreachable extends DatabaseUnreachable_base<{
76
+ readonly database: string;
77
+ readonly cause: unknown;
78
+ }> {}
70
79
  /** What the starter hands {@link PrismaOptions.client}. */
71
80
  type PrismaBinding = {
72
81
  /** `DATABASE_URL`, read through `Config` rather than by the application. */
@@ -106,6 +115,11 @@ type PrismaOptions<C extends PrismaLike> = {
106
115
  * the client is reached through, and the resourceful provider that opens it and
107
116
  * closes it again.
108
117
  *
118
+ * **The database answers before the scope is handed out**: opening runs the
119
+ * health check's probe, so a pool is verified before any unit uses it, and a
120
+ * database that does not answer fails the boot with
121
+ * {@link DatabaseUnreachable}.
122
+ *
109
123
  * **The pool closes on every exit path**, including a boot that fails after
110
124
  * this provider ran — that is what makes it resourceful rather than a plain
111
125
  * value. `runtime().close()` ends the pool; Prisma dials again lazily on the
@@ -116,7 +130,7 @@ type PrismaOptions<C extends PrismaLike> = {
116
130
  * against this same URL *before the process starts*; an application that
117
131
  * migrates itself at boot races every other replica.
118
132
  */
119
- export declare const prismaDatabase: <const N extends string>(name: N) => <C extends PrismaLike>({ client }: PrismaOptions<C>) => Module<HealthChecks | (PortClassOf<N, C> extends (infer T) ? T extends PortClassOf<N, C> ? T extends import("@btravstack/di").AnyPort ? InstanceType<T> : T extends import("@btravstack/di").AnyProvider ? InstanceType<T["port"]> : never : never : never), import("@btravstack/config").ConfigInvalid, Exclude<Env, HealthChecks | Observers | import("@btravstack/di").PortInstance<string, C> | import("@btravstack/di").PortInstance<`${N}Config`, {
133
+ export declare const prismaDatabase: <const N extends string>(name: N) => <C extends PrismaLike>({ client }: PrismaOptions<C>) => Module<HealthChecks | (PortClassOf<N, C> extends (infer T) ? T extends PortClassOf<N, C> ? T extends import("@btravstack/di").AnyPort ? InstanceType<T> : T extends import("@btravstack/di").AnyProvider ? InstanceType<T["port"]> : never : never : never), import("@btravstack/config").ConfigInvalid | DatabaseUnreachable, Exclude<Env, HealthChecks | Observers | import("@btravstack/di").PortInstance<string, C> | import("@btravstack/di").PortInstance<`${N}Config`, {
120
134
  readonly url: string;
121
135
  }>>> & {
122
136
  readonly port: PortClassOf<N, C>;
package/dist/index.d.mts CHANGED
@@ -67,6 +67,15 @@ type PrismaLike = {
67
67
  */
68
68
  readonly runtime: unknown;
69
69
  };
70
+ declare const DatabaseUnreachable_base: import("unthrown").TaggedErrorConstructor<"DatabaseUnreachable">;
71
+ /**
72
+ * The database did not answer when the scope opened. Carries the database's
73
+ * name and the driver's own failure, never the URL, which holds credentials.
74
+ */
75
+ export declare class DatabaseUnreachable extends DatabaseUnreachable_base<{
76
+ readonly database: string;
77
+ readonly cause: unknown;
78
+ }> {}
70
79
  /** What the starter hands {@link PrismaOptions.client}. */
71
80
  type PrismaBinding = {
72
81
  /** `DATABASE_URL`, read through `Config` rather than by the application. */
@@ -106,6 +115,11 @@ type PrismaOptions<C extends PrismaLike> = {
106
115
  * the client is reached through, and the resourceful provider that opens it and
107
116
  * closes it again.
108
117
  *
118
+ * **The database answers before the scope is handed out**: opening runs the
119
+ * health check's probe, so a pool is verified before any unit uses it, and a
120
+ * database that does not answer fails the boot with
121
+ * {@link DatabaseUnreachable}.
122
+ *
109
123
  * **The pool closes on every exit path**, including a boot that fails after
110
124
  * this provider ran — that is what makes it resourceful rather than a plain
111
125
  * value. `runtime().close()` ends the pool; Prisma dials again lazily on the
@@ -116,7 +130,7 @@ type PrismaOptions<C extends PrismaLike> = {
116
130
  * against this same URL *before the process starts*; an application that
117
131
  * migrates itself at boot races every other replica.
118
132
  */
119
- export declare const prismaDatabase: <const N extends string>(name: N) => <C extends PrismaLike>({ client }: PrismaOptions<C>) => Module<HealthChecks | (PortClassOf<N, C> extends (infer T) ? T extends PortClassOf<N, C> ? T extends import("@btravstack/di").AnyPort ? InstanceType<T> : T extends import("@btravstack/di").AnyProvider ? InstanceType<T["port"]> : never : never : never), import("@btravstack/config").ConfigInvalid, Exclude<Env, HealthChecks | Observers | import("@btravstack/di").PortInstance<string, C> | import("@btravstack/di").PortInstance<`${N}Config`, {
133
+ export declare const prismaDatabase: <const N extends string>(name: N) => <C extends PrismaLike>({ client }: PrismaOptions<C>) => Module<HealthChecks | (PortClassOf<N, C> extends (infer T) ? T extends PortClassOf<N, C> ? T extends import("@btravstack/di").AnyPort ? InstanceType<T> : T extends import("@btravstack/di").AnyProvider ? InstanceType<T["port"]> : never : never : never), import("@btravstack/config").ConfigInvalid | DatabaseUnreachable, Exclude<Env, HealthChecks | Observers | import("@btravstack/di").PortInstance<string, C> | import("@btravstack/di").PortInstance<`${N}Config`, {
120
134
  readonly url: string;
121
135
  }>>> & {
122
136
  readonly port: PortClassOf<N, C>;
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Config, Env } from "@btravstack/config";
2
2
  import { HealthCheckFailed, HealthChecks, Observers, noObserverMember, observe } from "@btravstack/core";
3
3
  import { Module, Port, Provider } from "@btravstack/di";
4
- import { fromPromise, fromSafePromise } from "unthrown";
4
+ import { TaggedError, fromPromise, fromSafePromise } from "unthrown";
5
5
  //#region src/instrument.ts
6
6
  /**
7
7
  * Every query, handed to the graph's observers: a span that spans the query, a
@@ -64,6 +64,12 @@ const queryObserver = (observers) => {
64
64
  //#region src/prisma.ts
65
65
  /** The one place the cast above is spelled. */
66
66
  const probeable = (db) => db;
67
+ const probe = (db) => probeable(db).runtime().query(probeable(db).raw.sql`SELECT 1`.affectedCount().build());
68
+ /**
69
+ * The database did not answer when the scope opened. Carries the database's
70
+ * name and the driver's own failure, never the URL, which holds credentials.
71
+ */
72
+ var DatabaseUnreachable = class extends TaggedError("DatabaseUnreachable") {};
67
73
  /**
68
74
  * A Prisma client whose pool is the application scope's.
69
75
  *
@@ -72,6 +78,11 @@ const probeable = (db) => db;
72
78
  * the client is reached through, and the resourceful provider that opens it and
73
79
  * closes it again.
74
80
  *
81
+ * **The database answers before the scope is handed out**: opening runs the
82
+ * health check's probe, so a pool is verified before any unit uses it, and a
83
+ * database that does not answer fails the boot with
84
+ * {@link DatabaseUnreachable}.
85
+ *
75
86
  * **The pool closes on every exit path**, including a boot that fails after
76
87
  * this provider ran — that is what makes it resourceful rather than a plain
77
88
  * value. `runtime().close()` ends the pool; Prisma dials again lazily on the
@@ -85,6 +96,10 @@ const probeable = (db) => db;
85
96
  const prismaDatabase = (name) => ({ client }) => {
86
97
  const config = Config.provider(`${name}Config`)(Config.object({ url: Config.string("DATABASE_URL") }));
87
98
  const DatabasePort = Port(name);
99
+ const verified = (db) => fromPromise(probe(db).then(() => db, (cause) => probeable(db).runtime().close().finally(() => Promise.reject(cause))), (cause) => new DatabaseUnreachable({
100
+ database: name,
101
+ cause
102
+ }));
88
103
  const port = DatabasePort;
89
104
  const clientProvider = Provider(port)({
90
105
  inject: {
@@ -94,14 +109,14 @@ const prismaDatabase = (name) => ({ client }) => {
94
109
  acquire: ({ settings, observers }) => fromSafePromise(Promise.resolve().then(() => client({
95
110
  url: settings.url,
96
111
  middleware: [queryObserver(observers)]
97
- }))),
112
+ }))).flatMap(verified),
98
113
  release: (db) => probeable(db).runtime().close()
99
114
  });
100
115
  const healthCheck = Provider.member(HealthChecks)({
101
116
  inject: { db: port },
102
117
  sync: ({ db }) => ({
103
118
  name,
104
- check: () => fromPromise(probeable(db).runtime().query(probeable(db).raw.sql`SELECT 1`.affectedCount().build()), (cause) => new HealthCheckFailed({ reason: cause instanceof Error ? cause.message : "database unreachable" })).map(() => void 0)
119
+ check: () => fromPromise(probe(db), (cause) => new HealthCheckFailed({ reason: cause instanceof Error ? cause.message : "database unreachable" })).map(() => void 0)
105
120
  })
106
121
  });
107
122
  const database = Module(name)({
@@ -117,4 +132,4 @@ const prismaDatabase = (name) => ({ client }) => {
117
132
  return Object.assign(database, { port: DatabasePort });
118
133
  };
119
134
  //#endregion
120
- export { prismaDatabase };
135
+ export { DatabaseUnreachable, prismaDatabase };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btravstack/prisma",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
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.23.0",
67
- "@btravstack/core": "0.23.0",
68
- "@btravstack/di": "0.23.0",
66
+ "@btravstack/config": "0.25.0",
67
+ "@btravstack/core": "0.25.0",
68
+ "@btravstack/di": "0.25.0",
69
69
  "@btravstack/tsconfig": "0.4.0",
70
70
  "@prisma/orm-postgres": "8.0.0-rc.11",
71
71
  "@types/node": "26.6.4",
@@ -77,9 +77,9 @@
77
77
  "vitest": "5.0.3"
78
78
  },
79
79
  "peerDependencies": {
80
- "@btravstack/config": "^0.23.0",
81
- "@btravstack/core": "^0.23.0",
82
- "@btravstack/di": "^0.23.0",
80
+ "@btravstack/config": "^0.25.0",
81
+ "@btravstack/core": "^0.25.0",
82
+ "@btravstack/di": "^0.25.0",
83
83
  "@prisma/orm-postgres": "^8.0.0-rc.11",
84
84
  "unthrown": "^5.0.0"
85
85
  },