@rebasepro/server-postgres 0.17.3 → 0.18.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.
Files changed (204) hide show
  1. package/LICENSE +0 -1
  2. package/README.md +31 -0
  3. package/dist/BranchService-ByLfM_Tt.js +360 -0
  4. package/dist/BranchService-ByLfM_Tt.js.map +1 -0
  5. package/dist/__vite-browser-external-BnuHet1e.js +46 -0
  6. package/dist/__vite-browser-external-BnuHet1e.js.map +1 -0
  7. package/dist/arg-Dni7MzLB.js +87 -0
  8. package/dist/arg-Dni7MzLB.js.map +1 -0
  9. package/dist/auth-users-columns-D2LBFrMH.js +116 -0
  10. package/dist/auth-users-columns-D2LBFrMH.js.map +1 -0
  11. package/dist/backup-argv.d.ts +19 -0
  12. package/dist/backup-cli-oM9WQVlV.js +408 -0
  13. package/dist/backup-cli-oM9WQVlV.js.map +1 -0
  14. package/dist/backup-service-Br3tqwsy.js +715 -0
  15. package/dist/backup-service-Br3tqwsy.js.map +1 -0
  16. package/dist/branch-argv.d.ts +28 -0
  17. package/dist/branch-prune.d.ts +77 -0
  18. package/dist/cli-collections-path.d.ts +31 -0
  19. package/dist/cli-errors-DxN4yzdH.js +657 -0
  20. package/dist/cli-errors-DxN4yzdH.js.map +1 -0
  21. package/dist/cli-errors.d.ts +123 -0
  22. package/dist/cli-flags.d.ts +101 -0
  23. package/dist/cli-helpers.d.ts +94 -10
  24. package/dist/cli-output-CNdMql-L.js +47 -0
  25. package/dist/cli-output-CNdMql-L.js.map +1 -0
  26. package/dist/cli.js +2020 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/collection-index-BHgT_TKd.js +276 -0
  29. package/dist/collection-index-BHgT_TKd.js.map +1 -0
  30. package/dist/{collections-schema-version-BMeu3cgv.js → collections-schema-version-T6cGvrDi.js} +1 -2
  31. package/dist/{collections-schema-version-BMeu3cgv.js.map → collections-schema-version-T6cGvrDi.js.map} +1 -1
  32. package/dist/{connection-GOKU3Hu5.js → databasePoolManager-Bj5FbeAs.js} +112 -40
  33. package/dist/databasePoolManager-Bj5FbeAs.js.map +1 -0
  34. package/dist/databasePoolManager.d.ts +27 -0
  35. package/dist/doctor-D7HWmGzs.js +974 -0
  36. package/dist/doctor-D7HWmGzs.js.map +1 -0
  37. package/dist/{ensure-collection-policies-UCqgv_8c.js → ensure-collection-policies-2c2NV8zX.js} +7 -27
  38. package/dist/ensure-collection-policies-2c2NV8zX.js.map +1 -0
  39. package/dist/ensure-collection-tables-D4wJzM4y.js +820 -0
  40. package/dist/ensure-collection-tables-D4wJzM4y.js.map +1 -0
  41. package/dist/ensure-tables-CIojK-Rx.js +650 -0
  42. package/dist/ensure-tables-CIojK-Rx.js.map +1 -0
  43. package/dist/generate-drizzle-schema-CwEgVyjO.js +123 -0
  44. package/dist/generate-drizzle-schema-CwEgVyjO.js.map +1 -0
  45. package/dist/generate-drizzle-schema-logic-so2Z_lyC.js +528 -0
  46. package/dist/generate-drizzle-schema-logic-so2Z_lyC.js.map +1 -0
  47. package/dist/{ensure-collection-tables-DgVixhX3.js → generate-postgres-ddl-logic-CtnpIBVv.js} +126 -984
  48. package/dist/generate-postgres-ddl-logic-CtnpIBVv.js.map +1 -0
  49. package/dist/generated-schema-staleness-DQdfJl2A.js +157 -0
  50. package/dist/generated-schema-staleness-DQdfJl2A.js.map +1 -0
  51. package/dist/index.d.ts +12 -0
  52. package/dist/index.es.js +7423 -1133
  53. package/dist/index.es.js.map +1 -1
  54. package/dist/introspect-db-logic-C6LQdTxj.js +1378 -0
  55. package/dist/introspect-db-logic-C6LQdTxj.js.map +1 -0
  56. package/dist/policy-drift-kNJ6X2Uj.js +313 -0
  57. package/dist/policy-drift-kNJ6X2Uj.js.map +1 -0
  58. package/dist/{backup-service-DCk7KhhL.js → rls-bootstrap-sql-DLDnUjtx.js} +737 -2689
  59. package/dist/rls-bootstrap-sql-DLDnUjtx.js.map +1 -0
  60. package/dist/{rls-enforcement-DvAbL9YJ.js → rls-enforcement-DeYEjYLt.js} +65 -190
  61. package/dist/rls-enforcement-DeYEjYLt.js.map +1 -0
  62. package/dist/schema/bin/generate-drizzle-schema.d.ts +1 -0
  63. package/dist/schema/bin/generate-postgres-ddl.d.ts +1 -0
  64. package/dist/schema/column-type-drift.d.ts +79 -0
  65. package/dist/schema/doctor-cli.js +129 -0
  66. package/dist/schema/doctor-cli.js.map +1 -0
  67. package/dist/schema/doctor.d.ts +42 -0
  68. package/dist/schema/ensure-collection-tables.d.ts +90 -0
  69. package/dist/schema/generate-drizzle-schema.d.ts +1 -1
  70. package/dist/schema/generate-drizzle-schema.js +24 -0
  71. package/dist/schema/generate-drizzle-schema.js.map +1 -0
  72. package/dist/schema/generate-next-step.d.ts +30 -0
  73. package/dist/schema/generate-postgres-ddl-logic.d.ts +28 -0
  74. package/dist/schema/generate-postgres-ddl.d.ts +1 -1
  75. package/dist/schema/generate-postgres-ddl.js +138 -0
  76. package/dist/schema/generate-postgres-ddl.js.map +1 -0
  77. package/dist/schema/generated-schema-staleness.d.ts +37 -0
  78. package/dist/schema/introspect-db-queries.d.ts +34 -0
  79. package/dist/schema/introspect-db.js +866 -0
  80. package/dist/schema/introspect-db.js.map +1 -0
  81. package/dist/security/rls-enforcement.d.ts +63 -2
  82. package/dist/services/BranchService.d.ts +86 -1
  83. package/dist/services/collection-helpers.d.ts +1 -1
  84. package/dist/services/realtimeService.d.ts +30 -2
  85. package/dist/services/row-pipeline.d.ts +12 -1
  86. package/dist/source-Br7L7GOI.js +410 -0
  87. package/dist/source-Br7L7GOI.js.map +1 -0
  88. package/dist/utils/pg-error-utils.d.ts +3 -1
  89. package/dist/utils/pg-numeric-number-patch.d.ts +31 -0
  90. package/dist/vector-index-DkWxm2de.js +309 -0
  91. package/dist/vector-index-DkWxm2de.js.map +1 -0
  92. package/package.json +32 -27
  93. package/dist/backup-service-DCk7KhhL.js.map +0 -1
  94. package/dist/collection-index-BRUg10H5.js +0 -5690
  95. package/dist/collection-index-BRUg10H5.js.map +0 -1
  96. package/dist/connection-GOKU3Hu5.js.map +0 -1
  97. package/dist/ensure-collection-policies-UCqgv_8c.js.map +0 -1
  98. package/dist/ensure-collection-tables-DgVixhX3.js.map +0 -1
  99. package/dist/rls-bootstrap-sql-B5C9LoJ6.js +0 -244
  100. package/dist/rls-bootstrap-sql-B5C9LoJ6.js.map +0 -1
  101. package/dist/rls-enforcement-DvAbL9YJ.js.map +0 -1
  102. package/dist/src-DiB5RP2Z.js +0 -541
  103. package/dist/src-DiB5RP2Z.js.map +0 -1
  104. package/dist/websocket-BZ4H5wUz.js +0 -8198
  105. package/dist/websocket-BZ4H5wUz.js.map +0 -1
  106. package/src/PostgresAdapter.ts +0 -91
  107. package/src/PostgresBackendDriver.ts +0 -1857
  108. package/src/PostgresBootstrapper.ts +0 -1157
  109. package/src/auth/ensure-tables.ts +0 -896
  110. package/src/auth/schema-version.ts +0 -260
  111. package/src/auth/services.ts +0 -1610
  112. package/src/backup/backup-cli.ts +0 -520
  113. package/src/backup/backup-cron.ts +0 -212
  114. package/src/backup/backup-logic.ts +0 -93
  115. package/src/backup/backup-service.ts +0 -485
  116. package/src/backup/index.ts +0 -12
  117. package/src/backup/pg-tools.ts +0 -403
  118. package/src/backup/retention.ts +0 -86
  119. package/src/cli-errors.ts +0 -265
  120. package/src/cli-helpers.ts +0 -569
  121. package/src/cli-output.ts +0 -43
  122. package/src/cli.ts +0 -1339
  123. package/src/collections/PostgresCollectionRegistry.ts +0 -108
  124. package/src/collections/buildRegistry.ts +0 -68
  125. package/src/collections/validate-relations.ts +0 -402
  126. package/src/connection.ts +0 -333
  127. package/src/data-transformer.ts +0 -845
  128. package/src/databasePoolManager.ts +0 -95
  129. package/src/history/HistoryService.ts +0 -254
  130. package/src/history/ensure-history-table.ts +0 -53
  131. package/src/index.ts +0 -19
  132. package/src/interfaces.ts +0 -60
  133. package/src/module-dir.ts +0 -7
  134. package/src/schema/atlas-argv.ts +0 -94
  135. package/src/schema/auth-schema.ts +0 -298
  136. package/src/schema/auth-users-columns.ts +0 -131
  137. package/src/schema/carved-out-migration.ts +0 -404
  138. package/src/schema/classify-change.ts +0 -436
  139. package/src/schema/collection-index.ts +0 -427
  140. package/src/schema/collections-schema-version.ts +0 -103
  141. package/src/schema/destructive-sql.ts +0 -94
  142. package/src/schema/doctor-cli.ts +0 -66
  143. package/src/schema/doctor-policy-checks.ts +0 -105
  144. package/src/schema/doctor.ts +0 -837
  145. package/src/schema/drizzle-ddl.ts +0 -33
  146. package/src/schema/dynamic-tables.test.ts +0 -302
  147. package/src/schema/dynamic-tables.ts +0 -293
  148. package/src/schema/ensure-collection-policies.ts +0 -198
  149. package/src/schema/ensure-collection-tables.test.ts +0 -518
  150. package/src/schema/ensure-collection-tables.ts +0 -1267
  151. package/src/schema/generate-drizzle-schema-logic.ts +0 -957
  152. package/src/schema/generate-drizzle-schema.ts +0 -143
  153. package/src/schema/generate-postgres-ddl-logic.ts +0 -1339
  154. package/src/schema/generate-postgres-ddl.ts +0 -180
  155. package/src/schema/generate-schema-commit.ts +0 -267
  156. package/src/schema/generated-schema-staleness.ts +0 -284
  157. package/src/schema/introspect-db-constraints.ts +0 -385
  158. package/src/schema/introspect-db-inference.ts +0 -255
  159. package/src/schema/introspect-db-logic.ts +0 -1387
  160. package/src/schema/introspect-db-naming.ts +0 -15
  161. package/src/schema/introspect-db-project.ts +0 -78
  162. package/src/schema/introspect-db-queries.ts +0 -326
  163. package/src/schema/introspect-db-structure.ts +0 -670
  164. package/src/schema/introspect-db-types.ts +0 -56
  165. package/src/schema/introspect-db.ts +0 -257
  166. package/src/schema/introspect-runtime.test.ts +0 -260
  167. package/src/schema/introspect-runtime.ts +0 -327
  168. package/src/schema/non-sql-collections.test.ts +0 -131
  169. package/src/schema/rls-bootstrap-sql.ts +0 -288
  170. package/src/schema/search-column.ts +0 -643
  171. package/src/schema/test-schema.ts +0 -11
  172. package/src/schema/vector-index.ts +0 -491
  173. package/src/security/anonymous-grants.test.ts +0 -73
  174. package/src/security/policy-drift.test.ts +0 -435
  175. package/src/security/policy-drift.ts +0 -473
  176. package/src/security/rls-enforcement.ts +0 -505
  177. package/src/services/BranchService.ts +0 -326
  178. package/src/services/FetchService.ts +0 -2115
  179. package/src/services/PersistService.ts +0 -557
  180. package/src/services/RelationService.ts +0 -978
  181. package/src/services/RelationWriteService.ts +0 -653
  182. package/src/services/cdc/CdcListener.ts +0 -103
  183. package/src/services/cdc/junction-tables.ts +0 -91
  184. package/src/services/cdc/trigger-cdc.ts +0 -173
  185. package/src/services/channel-bus/ChannelBus.ts +0 -44
  186. package/src/services/channel-bus/PostgresChannelBus.ts +0 -299
  187. package/src/services/channel-bus/index.ts +0 -114
  188. package/src/services/channel-history.ts +0 -424
  189. package/src/services/channel-presence.ts +0 -185
  190. package/src/services/collection-helpers.ts +0 -420
  191. package/src/services/dataService.ts +0 -256
  192. package/src/services/index.ts +0 -14
  193. package/src/services/junction-writes.ts +0 -295
  194. package/src/services/nested-path.ts +0 -145
  195. package/src/services/pg-notify-listener.ts +0 -151
  196. package/src/services/realtimeService.ts +0 -2545
  197. package/src/services/row-pipeline.ts +0 -240
  198. package/src/services/write-denial.ts +0 -55
  199. package/src/types.ts +0 -4
  200. package/src/utils/connection-string.ts +0 -58
  201. package/src/utils/drizzle-conditions.ts +0 -2743
  202. package/src/utils/pg-array-null-patch.ts +0 -42
  203. package/src/utils/pg-error-utils.ts +0 -391
  204. package/src/websocket.ts +0 -855
@@ -1,260 +0,0 @@
1
- import { sql } from "drizzle-orm";
2
- import { NodePgDatabase } from "drizzle-orm/node-postgres";
3
- import type { CollectionConfig } from "@rebasepro/types";
4
-
5
- /**
6
- * The auth schema version this runtime expects to find in the database.
7
- *
8
- * Bump this whenever a migration in `ensureAuthTablesExist` makes the schema
9
- * unreadable by the runtime that came before it — that is, whenever a *previous*
10
- * version's auth queries would break against the migrated shape. Additive
11
- * changes (a new nullable column nobody older references) do not need a bump.
12
- *
13
- * History. Note that 1 is a label for an era, not a value any database holds:
14
- * stamping did not exist then, so an era-1 database reads as unstamped
15
- * (`null`), and 2 is the first version ever actually written. The numbering
16
- * starts at 2 only because two schema eras already existed when it was
17
- * introduced; it could just as well have started at 1. It is not worth
18
- * renumbering now — deployed databases already carry 2, and lowering the
19
- * constant would make them look newer than the runtime and refuse the boot.
20
- *
21
- * 1 — Device-session refresh tokens. A row *was* a session, identified by
22
- * `unique_device_session UNIQUE (uid, user_agent, ip_address)`, and
23
- * `createToken` upserted with `ON CONFLICT (uid, user_agent, ip_address)`.
24
- * 2 — Session-scoped, rotation-safe refresh tokens: `session_id`, `revoked`,
25
- * `rotated_at`, `session_started_at`, and `unique_device_session`
26
- * dropped because two live tokens of one session share all three columns.
27
- *
28
- * The 1 → 2 migration is why this file exists. Dropping the constraint is
29
- * one-way: a version-1 runtime deployed afterwards boots perfectly, logs
30
- * `✅ Auth tables ready` (its `CREATE TABLE IF NOT EXISTS` never revisits the
31
- * existing table, so it cannot re-add the constraint), answers `/health` with
32
- * 200 — and then fails every single login and refresh with SQLSTATE 42P10,
33
- * because its `ON CONFLICT` names a constraint that no longer exists. A silent
34
- * total auth outage behind a green health check. The stamp below turns that
35
- * into a boot refusal.
36
- */
37
- export const AUTH_SCHEMA_VERSION = 2;
38
-
39
- /** Key under which the version is stored in the auth schema's meta table. */
40
- const VERSION_KEY = "auth_schema_version";
41
-
42
- /**
43
- * Columns `refresh_tokens` must have for the current runtime's auth write path
44
- * to work. Checked by the health probe so a database that drifted *below* this
45
- * runtime is reported as unhealthy rather than discovered one failed login at a
46
- * time. Kept in step with the migration in `ensureAuthTablesExist`.
47
- */
48
- const REQUIRED_REFRESH_TOKEN_COLUMNS = ["session_id", "revoked", "rotated_at", "session_started_at"];
49
-
50
- /**
51
- * A constraint whose *presence* means the database is still at version 1, in a
52
- * shape this runtime's rotation logic cannot write to: it makes two live tokens
53
- * of one rotating session collide.
54
- */
55
- const RETIRED_REFRESH_TOKEN_CONSTRAINT = "unique_device_session";
56
-
57
- /**
58
- * Thrown when the database was migrated by a runtime newer than this one.
59
- *
60
- * Distinct class rather than a bare `Error` because `ensureAuthTablesExist`
61
- * wraps its migrations in a catch that deliberately swallows failures and
62
- * continues — every other problem there is better survived than crashed on.
63
- * This one is not, so the catch rethrows on this type specifically.
64
- */
65
- export class AuthSchemaVersionError extends Error {
66
- readonly databaseVersion: number;
67
- readonly runtimeVersion: number;
68
-
69
- constructor(databaseVersion: number, runtimeVersion: number) {
70
- super(
71
- `Auth schema version mismatch: the database is at version ${databaseVersion}, ` +
72
- `but this runtime understands version ${runtimeVersion}.\n\n` +
73
- "A newer version of the framework has already migrated this database. Running this " +
74
- "older runtime against it would boot cleanly and then fail every login and token " +
75
- "refresh, because the auth schema it expects no longer exists.\n\n" +
76
- "Refusing to start. Deploy a framework version at or above the one that migrated " +
77
- "this database, or restore the database from a backup taken before the upgrade."
78
- );
79
- this.name = "AuthSchemaVersionError";
80
- this.databaseVersion = databaseVersion;
81
- this.runtimeVersion = runtimeVersion;
82
- }
83
- }
84
-
85
- /**
86
- * The schema the auth tables live in, derived exactly as `ensureAuthTablesExist`
87
- * derives it. Shared so the two cannot drift: a stamp written to one schema and
88
- * read from another would read as "never stamped" forever.
89
- */
90
- export function resolveAuthSchema(collection?: CollectionConfig): string {
91
- if (!collection) return "rebase";
92
- const usersSchema = ("schema" in collection && typeof collection.schema === "string")
93
- ? collection.schema
94
- : "public";
95
- return usersSchema === "public" ? "rebase" : usersSchema;
96
- }
97
-
98
- /**
99
- * Read the stamped version, or `null` when the database has never been stamped.
100
- *
101
- * `null` is not an error and must not be treated as one: every database
102
- * provisioned before this file existed is unstamped, and so is every fresh one.
103
- * Uses `to_regclass` rather than selecting straight from the table so a missing
104
- * schema or table is a `null` rather than a thrown 42P01.
105
- */
106
- export async function readAuthSchemaVersion(
107
- db: NodePgDatabase,
108
- authSchema: string
109
- ): Promise<number | null> {
110
- const qualified = `"${authSchema}"."schema_meta"`;
111
- const exists = await db.execute(sql`SELECT to_regclass(${qualified}) IS NOT NULL AS present`);
112
- if (!(exists.rows[0] as { present: boolean } | undefined)?.present) return null;
113
-
114
- const result = await db.execute(sql`
115
- SELECT value FROM ${sql.raw(qualified)} WHERE key = ${VERSION_KEY}
116
- `);
117
- const raw = (result.rows[0] as { value: string } | undefined)?.value;
118
- if (raw === undefined) return null;
119
-
120
- const parsed = Number.parseInt(raw, 10);
121
- // A meta row we cannot parse is treated as unstamped rather than as version
122
- // 0: refusing to boot over a garbled string would be a worse failure than
123
- // the drift it is meant to catch.
124
- return Number.isFinite(parsed) ? parsed : null;
125
- }
126
-
127
- /**
128
- * Refuse to run against a database a newer runtime has already migrated.
129
- *
130
- * Deliberately one-directional. A database *older* than this runtime is the
131
- * normal upgrade path — the migrations in `ensureAuthTablesExist` are about to
132
- * bring it forward, so it is not an error. Only the reverse is unrecoverable.
133
- */
134
- export async function assertAuthSchemaCompatible(
135
- db: NodePgDatabase,
136
- authSchema: string
137
- ): Promise<void> {
138
- const databaseVersion = await readAuthSchemaVersion(db, authSchema);
139
- if (databaseVersion !== null && databaseVersion > AUTH_SCHEMA_VERSION) {
140
- throw new AuthSchemaVersionError(databaseVersion, AUTH_SCHEMA_VERSION);
141
- }
142
- }
143
-
144
- /**
145
- * Record that this runtime's migrations have been applied.
146
- *
147
- * Called at the end of `ensureAuthTablesExist`, so a boot that failed partway
148
- * through leaves the older stamp in place and the next boot migrates again.
149
- */
150
- export async function stampAuthSchemaVersion(
151
- db: NodePgDatabase,
152
- authSchema: string
153
- ): Promise<void> {
154
- const qualified = `"${authSchema}"."schema_meta"`;
155
- await db.execute(sql`
156
- CREATE TABLE IF NOT EXISTS ${sql.raw(qualified)} (
157
- key TEXT PRIMARY KEY,
158
- value TEXT NOT NULL,
159
- updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
160
- )
161
- `);
162
- await db.execute(sql`
163
- INSERT INTO ${sql.raw(qualified)} (key, value)
164
- VALUES (${VERSION_KEY}, ${String(AUTH_SCHEMA_VERSION)})
165
- ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = NOW()
166
- `);
167
- }
168
-
169
- /** What {@link probeAuthSchema} found. */
170
- export interface AuthSchemaProbeResult {
171
- /** False when this runtime cannot be trusted to serve auth against this database. */
172
- healthy: boolean;
173
- /** The stamped version, or `null` on a database that predates stamping. */
174
- databaseVersion: number | null;
175
- /** {@link AUTH_SCHEMA_VERSION}. */
176
- runtimeVersion: number;
177
- /** Human-readable descriptions of each mismatch found. Empty when healthy. */
178
- problems: string[];
179
- }
180
-
181
- /**
182
- * Check that the auth schema is one this runtime can actually write to.
183
- *
184
- * Two independent checks, because either alone has a blind spot:
185
- *
186
- * - The **stamp** catches a runtime older than the database. It is the precise
187
- * signal, but it is blind on every database provisioned before stamping
188
- * existed — which today is all of them.
189
- * - The **structure** catches a database older than the runtime, and works on
190
- * unstamped databases. It is what makes this useful immediately rather than
191
- * one upgrade cycle from now.
192
- *
193
- * Never throws: a probe that fails to run reports unhealthy with the reason, so
194
- * a broken check surfaces as a degraded health response rather than a 500 from
195
- * the health endpoint itself.
196
- */
197
- export async function probeAuthSchema(
198
- db: NodePgDatabase,
199
- authSchema: string
200
- ): Promise<AuthSchemaProbeResult> {
201
- const problems: string[] = [];
202
- let databaseVersion: number | null = null;
203
-
204
- try {
205
- databaseVersion = await readAuthSchemaVersion(db, authSchema);
206
- if (databaseVersion !== null && databaseVersion > AUTH_SCHEMA_VERSION) {
207
- problems.push(
208
- `database is at auth schema version ${databaseVersion}, this runtime understands ` +
209
- `${AUTH_SCHEMA_VERSION} — it was migrated by a newer framework version`
210
- );
211
- }
212
-
213
- const refreshTokens = `"${authSchema}"."refresh_tokens"`;
214
- const present = await db.execute(sql`SELECT to_regclass(${refreshTokens}) IS NOT NULL AS present`);
215
- if (!(present.rows[0] as { present: boolean } | undefined)?.present) {
216
- // Not a problem in itself: auth may simply not be configured on this
217
- // deployment, and the table is created on demand at boot when it is.
218
- return { healthy: problems.length === 0, databaseVersion, runtimeVersion: AUTH_SCHEMA_VERSION, problems };
219
- }
220
-
221
- const columns = await db.execute(sql`
222
- SELECT column_name FROM information_schema.columns
223
- WHERE table_schema = ${authSchema} AND table_name = 'refresh_tokens'
224
- `);
225
- const found = new Set((columns.rows as { column_name: string }[]).map(row => row.column_name));
226
- const missing = REQUIRED_REFRESH_TOKEN_COLUMNS.filter(column => !found.has(column));
227
- if (missing.length > 0) {
228
- problems.push(
229
- `refresh_tokens is missing ${missing.join(", ")} — the auth migrations have not been ` +
230
- "applied to this database, so token rotation will fail"
231
- );
232
- }
233
-
234
- const retired = await db.execute(sql`
235
- SELECT 1 FROM pg_constraint c
236
- JOIN pg_class t ON t.oid = c.conrelid
237
- JOIN pg_namespace n ON n.oid = t.relnamespace
238
- WHERE n.nspname = ${authSchema}
239
- AND t.relname = 'refresh_tokens'
240
- AND c.conname = ${RETIRED_REFRESH_TOKEN_CONSTRAINT}
241
- `);
242
- if (retired.rows.length > 0) {
243
- problems.push(
244
- `refresh_tokens still carries ${RETIRED_REFRESH_TOKEN_CONSTRAINT} — concurrent token ` +
245
- "rotation for one session will fail on it"
246
- );
247
- }
248
- } catch (error: unknown) {
249
- problems.push(
250
- `auth schema probe failed: ${error instanceof Error ? error.message : String(error)}`
251
- );
252
- }
253
-
254
- return {
255
- healthy: problems.length === 0,
256
- databaseVersion,
257
- runtimeVersion: AUTH_SCHEMA_VERSION,
258
- problems
259
- };
260
- }