@rebasepro/server-postgres 0.17.3-canary.gdd23447 → 0.18.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.
Files changed (149) hide show
  1. package/LICENSE +0 -1
  2. package/README.md +31 -0
  3. package/dist/backup-argv.d.ts +19 -0
  4. package/dist/branch-argv.d.ts +28 -0
  5. package/dist/branch-prune.d.ts +77 -0
  6. package/dist/cli-collections-path.d.ts +31 -0
  7. package/dist/cli-errors.d.ts +123 -0
  8. package/dist/cli-flags.d.ts +101 -0
  9. package/dist/cli-helpers.d.ts +94 -10
  10. package/dist/collection-index-C7LopMyX.js +247 -0
  11. package/dist/collection-index-C7LopMyX.js.map +1 -0
  12. package/dist/databasePoolManager.d.ts +27 -0
  13. package/dist/{ensure-collection-policies-UCqgv_8c.js → ensure-collection-policies-CagOzEPS.js} +6 -6
  14. package/dist/{ensure-collection-policies-UCqgv_8c.js.map → ensure-collection-policies-CagOzEPS.js.map} +1 -1
  15. package/dist/{ensure-collection-tables-DgVixhX3.js → ensure-collection-tables-DbAhrjnG.js} +614 -52
  16. package/dist/ensure-collection-tables-DbAhrjnG.js.map +1 -0
  17. package/dist/index.d.ts +12 -0
  18. package/dist/index.es.js +19307 -3059
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/{rls-enforcement-DvAbL9YJ.js → rls-enforcement-ClJoTboT.js} +53 -189
  21. package/dist/rls-enforcement-ClJoTboT.js.map +1 -0
  22. package/dist/schema/column-type-drift.d.ts +79 -0
  23. package/dist/schema/doctor.d.ts +42 -0
  24. package/dist/schema/ensure-collection-tables.d.ts +90 -0
  25. package/dist/schema/generate-next-step.d.ts +30 -0
  26. package/dist/schema/generate-postgres-ddl-logic.d.ts +28 -0
  27. package/dist/schema/generated-schema-staleness.d.ts +37 -0
  28. package/dist/schema/introspect-db-queries.d.ts +34 -0
  29. package/dist/security/rls-enforcement.d.ts +63 -2
  30. package/dist/services/BranchService.d.ts +86 -1
  31. package/dist/services/collection-helpers.d.ts +1 -1
  32. package/dist/services/realtimeService.d.ts +30 -2
  33. package/dist/services/row-pipeline.d.ts +12 -1
  34. package/dist/utils/pg-error-utils.d.ts +3 -1
  35. package/dist/utils/pg-numeric-number-patch.d.ts +31 -0
  36. package/package.json +32 -27
  37. package/dist/backup-service-DCk7KhhL.js +0 -9044
  38. package/dist/backup-service-DCk7KhhL.js.map +0 -1
  39. package/dist/collection-index-BRUg10H5.js +0 -5690
  40. package/dist/collection-index-BRUg10H5.js.map +0 -1
  41. package/dist/connection-GOKU3Hu5.js +0 -277
  42. package/dist/connection-GOKU3Hu5.js.map +0 -1
  43. package/dist/ensure-collection-tables-DgVixhX3.js.map +0 -1
  44. package/dist/rls-bootstrap-sql-B5C9LoJ6.js +0 -244
  45. package/dist/rls-bootstrap-sql-B5C9LoJ6.js.map +0 -1
  46. package/dist/rls-enforcement-DvAbL9YJ.js.map +0 -1
  47. package/dist/src-DiB5RP2Z.js +0 -541
  48. package/dist/src-DiB5RP2Z.js.map +0 -1
  49. package/dist/websocket-BZ4H5wUz.js +0 -8198
  50. package/dist/websocket-BZ4H5wUz.js.map +0 -1
  51. package/src/PostgresAdapter.ts +0 -91
  52. package/src/PostgresBackendDriver.ts +0 -1857
  53. package/src/PostgresBootstrapper.ts +0 -1157
  54. package/src/auth/ensure-tables.ts +0 -896
  55. package/src/auth/schema-version.ts +0 -260
  56. package/src/auth/services.ts +0 -1610
  57. package/src/backup/backup-cli.ts +0 -520
  58. package/src/backup/backup-cron.ts +0 -212
  59. package/src/backup/backup-logic.ts +0 -93
  60. package/src/backup/backup-service.ts +0 -485
  61. package/src/backup/index.ts +0 -12
  62. package/src/backup/pg-tools.ts +0 -403
  63. package/src/backup/retention.ts +0 -86
  64. package/src/cli-errors.ts +0 -265
  65. package/src/cli-helpers.ts +0 -569
  66. package/src/cli-output.ts +0 -43
  67. package/src/cli.ts +0 -1339
  68. package/src/collections/PostgresCollectionRegistry.ts +0 -108
  69. package/src/collections/buildRegistry.ts +0 -68
  70. package/src/collections/validate-relations.ts +0 -402
  71. package/src/connection.ts +0 -333
  72. package/src/data-transformer.ts +0 -845
  73. package/src/databasePoolManager.ts +0 -95
  74. package/src/history/HistoryService.ts +0 -254
  75. package/src/history/ensure-history-table.ts +0 -53
  76. package/src/index.ts +0 -19
  77. package/src/interfaces.ts +0 -60
  78. package/src/module-dir.ts +0 -7
  79. package/src/schema/atlas-argv.ts +0 -94
  80. package/src/schema/auth-schema.ts +0 -298
  81. package/src/schema/auth-users-columns.ts +0 -131
  82. package/src/schema/carved-out-migration.ts +0 -404
  83. package/src/schema/classify-change.ts +0 -436
  84. package/src/schema/collection-index.ts +0 -427
  85. package/src/schema/collections-schema-version.ts +0 -103
  86. package/src/schema/destructive-sql.ts +0 -94
  87. package/src/schema/doctor-cli.ts +0 -66
  88. package/src/schema/doctor-policy-checks.ts +0 -105
  89. package/src/schema/doctor.ts +0 -837
  90. package/src/schema/drizzle-ddl.ts +0 -33
  91. package/src/schema/dynamic-tables.test.ts +0 -302
  92. package/src/schema/dynamic-tables.ts +0 -293
  93. package/src/schema/ensure-collection-policies.ts +0 -198
  94. package/src/schema/ensure-collection-tables.test.ts +0 -518
  95. package/src/schema/ensure-collection-tables.ts +0 -1267
  96. package/src/schema/generate-drizzle-schema-logic.ts +0 -957
  97. package/src/schema/generate-drizzle-schema.ts +0 -143
  98. package/src/schema/generate-postgres-ddl-logic.ts +0 -1339
  99. package/src/schema/generate-postgres-ddl.ts +0 -180
  100. package/src/schema/generate-schema-commit.ts +0 -267
  101. package/src/schema/generated-schema-staleness.ts +0 -284
  102. package/src/schema/introspect-db-constraints.ts +0 -385
  103. package/src/schema/introspect-db-inference.ts +0 -255
  104. package/src/schema/introspect-db-logic.ts +0 -1387
  105. package/src/schema/introspect-db-naming.ts +0 -15
  106. package/src/schema/introspect-db-project.ts +0 -78
  107. package/src/schema/introspect-db-queries.ts +0 -326
  108. package/src/schema/introspect-db-structure.ts +0 -670
  109. package/src/schema/introspect-db-types.ts +0 -56
  110. package/src/schema/introspect-db.ts +0 -257
  111. package/src/schema/introspect-runtime.test.ts +0 -260
  112. package/src/schema/introspect-runtime.ts +0 -327
  113. package/src/schema/non-sql-collections.test.ts +0 -131
  114. package/src/schema/rls-bootstrap-sql.ts +0 -288
  115. package/src/schema/search-column.ts +0 -643
  116. package/src/schema/test-schema.ts +0 -11
  117. package/src/schema/vector-index.ts +0 -491
  118. package/src/security/anonymous-grants.test.ts +0 -73
  119. package/src/security/policy-drift.test.ts +0 -435
  120. package/src/security/policy-drift.ts +0 -473
  121. package/src/security/rls-enforcement.ts +0 -505
  122. package/src/services/BranchService.ts +0 -326
  123. package/src/services/FetchService.ts +0 -2115
  124. package/src/services/PersistService.ts +0 -557
  125. package/src/services/RelationService.ts +0 -978
  126. package/src/services/RelationWriteService.ts +0 -653
  127. package/src/services/cdc/CdcListener.ts +0 -103
  128. package/src/services/cdc/junction-tables.ts +0 -91
  129. package/src/services/cdc/trigger-cdc.ts +0 -173
  130. package/src/services/channel-bus/ChannelBus.ts +0 -44
  131. package/src/services/channel-bus/PostgresChannelBus.ts +0 -299
  132. package/src/services/channel-bus/index.ts +0 -114
  133. package/src/services/channel-history.ts +0 -424
  134. package/src/services/channel-presence.ts +0 -185
  135. package/src/services/collection-helpers.ts +0 -420
  136. package/src/services/dataService.ts +0 -256
  137. package/src/services/index.ts +0 -14
  138. package/src/services/junction-writes.ts +0 -295
  139. package/src/services/nested-path.ts +0 -145
  140. package/src/services/pg-notify-listener.ts +0 -151
  141. package/src/services/realtimeService.ts +0 -2545
  142. package/src/services/row-pipeline.ts +0 -240
  143. package/src/services/write-denial.ts +0 -55
  144. package/src/types.ts +0 -4
  145. package/src/utils/connection-string.ts +0 -58
  146. package/src/utils/drizzle-conditions.ts +0 -2743
  147. package/src/utils/pg-array-null-patch.ts +0 -42
  148. package/src/utils/pg-error-utils.ts +0 -391
  149. package/src/websocket.ts +0 -855
@@ -1,240 +0,0 @@
1
- import { CollectionConfig, Property, ResolvedRelation, isManyToMany, type ResolvedVia } from "@rebasepro/types";
2
- import { resolveCollectionRelations, findRelation, createRelationRefWithData } from "@rebasepro/common";
3
- import { normalizeDbValues } from "../data-transformer";
4
- import { deriveRowAddress } from "./collection-helpers";
5
- import { PostgresCollectionRegistry } from "../collections/PostgresCollectionRegistry";
6
-
7
- /**
8
- * Turning a drizzle result into a row we serve.
9
- *
10
- * There are two shapes, and they are the same walk. Both take a row whose
11
- * relation fields hold nested objects, both unwrap junction rows to reach the
12
- * target behind them, and both leave every other column alone. They differ only
13
- * in what they put where the relation was:
14
- *
15
- * - `"ref"` — a `{ id, path, __type: "relation" }` reference carrying the
16
- * target's values. This is what the admin renders.
17
- * - `"inline"` — the target's own columns, flat. This is what REST serves, and
18
- * — since the in-process SDK reads through the same pipeline — what
19
- * `rebase.data` / `context.data` serve too. A developer never sees a ref.
20
- *
21
- * They used to be two functions that happened to agree, and the agreement was
22
- * not enforced by anything: the row-identity bug had to be fixed five times
23
- * across differently-shaped copies of this walk, and one of the copies was
24
- * dead code nobody had noticed. Whatever the next cross-cutting change is, it
25
- * is one edit here.
26
- */
27
- export type RelationStyle = "ref" | "inline";
28
-
29
- /**
30
- * Whether a many-relation reaches its target through a junction table.
31
- *
32
- * Also used to build the drizzle `with` config, which is why it is exported:
33
- * the query has to nest one level deeper for a junction, and the row walk has
34
- * to unwrap that same level back out.
35
- */
36
- export function isJunctionRelation(relation: ResolvedRelation): boolean {
37
- // An explicit `through` says so outright.
38
- if (isManyToMany(relation)) return true;
39
- // A multi-hop join path is the same thing spelled longhand.
40
- return relation.kind === "via" && relation.joinPath.length > 1;
41
- }
42
-
43
- /**
44
- * Reach the target row inside a junction row.
45
- *
46
- * A junction row looks like `{ post_id: 1, tag_id: { id: 5, name: "ts" } }` —
47
- * the foreign keys, and the target nested under one of them. The target is the
48
- * only object among them, so that is how it is found. A junction row that has
49
- * not been nested (no `with` on the join) has no object and is returned as-is.
50
- */
51
- function unwrapJunctionRow(item: Record<string, unknown>): Record<string, unknown> {
52
- const nestedKey = Object.keys(item).find(
53
- key => typeof item[key] === "object" && item[key] !== null && !Array.isArray(item[key])
54
- );
55
- return nestedKey ? item[nestedKey] as Record<string, unknown> : item;
56
- }
57
-
58
- /**
59
- * Give back the number a `number` property was declared to be.
60
- *
61
- * Postgres returns NUMERIC as a string and drizzle keeps it that way, since a
62
- * numeric can hold more precision than a double. But the collection declared
63
- * this property `number` and the OpenAPI spec this server publishes says
64
- * `type: number`, so serving `"9.99"` breaks its own contract — and breaks it
65
- * asymmetrically, because a create answers with the number and the read that
66
- * follows answers with the string. Any client that multiplies a price works
67
- * until the first refresh.
68
- *
69
- * Declaring a property `number` already accepts double precision — that is what
70
- * the admin has always parsed it to. Columns REST serves that no property
71
- * declares are left exactly as the database returned them.
72
- */
73
- function coerceDeclaredNumber(value: unknown, property: Property | undefined): unknown {
74
- if (property?.type !== "number" || typeof value !== "string") return value;
75
- const parsed = parseFloat(value);
76
- return isNaN(parsed) ? null : parsed;
77
- }
78
-
79
- /** Apply {@link coerceDeclaredNumber} across a row, leaving every other column alone. */
80
- function coerceDeclaredNumbers(
81
- row: Record<string, unknown>,
82
- collection: CollectionConfig
83
- ): Record<string, unknown> {
84
- const properties = collection.properties;
85
- if (!properties) return row;
86
-
87
- const out: Record<string, unknown> = {};
88
- for (const [key, value] of Object.entries(row)) {
89
- out[key] = coerceDeclaredNumber(value, properties[key] as Property | undefined);
90
- }
91
- return out;
92
- }
93
-
94
- /** Render one target row in the requested style. */
95
- /**
96
- * Drop every column the collection marked `excludeFromApi`.
97
- *
98
- * Password hashes and verification tokens have to be readable server-side but
99
- * must never reach a client — and "never" has to mean every exit from this
100
- * pipeline, including relation targets, or a secret leaks through whichever
101
- * path was overlooked. Keyed by both the property name and its column name,
102
- * since a row can arrive keyed either way depending on the caller.
103
- */
104
- function stripExcluded(
105
- row: Record<string, unknown>,
106
- collection: CollectionConfig
107
- ): Record<string, unknown> {
108
- const properties = collection.properties as Record<string, Property> | undefined;
109
- if (!properties) return row;
110
-
111
- for (const [key, property] of Object.entries(properties)) {
112
- if (!property?.excludeFromApi) continue;
113
- delete row[key];
114
- if (property.columnName) delete row[property.columnName];
115
- }
116
- return row;
117
- }
118
-
119
- function renderTarget(
120
- targetRow: Record<string, unknown>,
121
- targetCollection: CollectionConfig,
122
- style: RelationStyle,
123
- registry: PostgresCollectionRegistry
124
- ): unknown {
125
- if (style === "inline") {
126
- // The target's columns, and only those: its address is the consumer's
127
- // to derive, and merging one in overwrites a real `id` column.
128
- return stripExcluded(coerceDeclaredNumbers({ ...targetRow }, targetCollection), targetCollection);
129
- }
130
-
131
- const address = relationTargetAddress(targetRow, targetCollection, registry);
132
- const path = targetCollection.slug;
133
- return createRelationRefWithData(address, path, {
134
- id: address,
135
- path,
136
- values: normalizeDbValues(targetRow, targetCollection)
137
- });
138
- }
139
-
140
- /**
141
- * The address a relation ref points at.
142
- *
143
- * The whole key, not its first column: a composite-keyed target addressed by
144
- * `tenant_id` alone points at every row that shares it. A target whose key
145
- * cannot be resolved at all used to throw here — reading `[0]` of an empty
146
- * array — taking down the parent's fetch over a relation it may not even have
147
- * asked for. The first column is a guess, but a ref that resolves to nothing
148
- * beats no rows at all.
149
- */
150
- export function relationTargetAddress(
151
- targetRow: Record<string, unknown>,
152
- targetCollection: CollectionConfig,
153
- registry: PostgresCollectionRegistry
154
- ): string {
155
- const address = deriveRowAddress(targetRow, targetCollection, registry);
156
- if (address) return address;
157
- return String(targetRow[Object.keys(targetRow)[0]] ?? "");
158
- }
159
-
160
- /**
161
- * The row the admin renders: every column, with relations as references.
162
- *
163
- * Values are normalized (dates, numbers, NaN) because the admin's view-model
164
- * expects real types. The row's own address is *not* among the columns — it is
165
- * derived by the consumer from the collection's primary keys.
166
- */
167
- export function toFlatRow(
168
- row: Record<string, unknown>,
169
- collection: CollectionConfig,
170
- registry: PostgresCollectionRegistry
171
- ): Record<string, unknown> {
172
- const resolvedRelations = resolveCollectionRelations(collection);
173
- const normalized = normalizeDbValues(row, collection) as Record<string, unknown>;
174
-
175
- // Keyed by relation, reading the drizzle relation name off the raw row: the
176
- // relation's property key and the name drizzle nested it under are not
177
- // always the same, and the value is written back under the property key.
178
- for (const [key, relation] of Object.entries(resolvedRelations)) {
179
- const relData = row[relation.relationName || key];
180
- if (relData === undefined || relData === null) continue;
181
-
182
- if (relation.cardinality === "many" && Array.isArray(relData)) {
183
- const targetCollection = relation.target();
184
- normalized[key] = relData.map((item: Record<string, unknown>) =>
185
- renderTarget(
186
- isJunctionRelation(relation) ? unwrapJunctionRow(item) : item,
187
- targetCollection,
188
- "ref",
189
- registry
190
- ));
191
- } else if (relation.cardinality === "one" && typeof relData === "object" && !Array.isArray(relData)) {
192
- normalized[key] = renderTarget(relData as Record<string, unknown>, relation.target(), "ref", registry);
193
- }
194
- }
195
-
196
- return stripExcluded(normalized, collection);
197
- }
198
-
199
- /**
200
- * The row REST serves: every column under its own name, with the value Postgres
201
- * returned, and relations inlined as the target's columns.
202
- *
203
- * Values are the ones the database returned, except where that contradicts the
204
- * declared type: a `number` property is served as a number (see
205
- * {@link coerceDeclaredNumber}). Dates stay as the database returned them —
206
- * JSON has its own opinions about dates that the admin's view-model does not
207
- * share.
208
- *
209
- * Keyed by the row rather than by the relation list — a REST fetch only loads
210
- * the relations `include` asked for, so the row is the authority on which are
211
- * actually there.
212
- */
213
- export function toRestRow(
214
- row: Record<string, unknown>,
215
- collection: CollectionConfig,
216
- registry: PostgresCollectionRegistry
217
- ): Record<string, unknown> {
218
- const resolvedRelations = resolveCollectionRelations(collection);
219
- const flat: Record<string, unknown> = {};
220
-
221
- for (const [key, value] of Object.entries(row)) {
222
- const relation = findRelation(resolvedRelations, key);
223
-
224
- if (relation && Array.isArray(value)) {
225
- flat[key] = value.map((item: Record<string, unknown>) =>
226
- renderTarget(
227
- isJunctionRelation(relation) ? unwrapJunctionRow(item) : item,
228
- relation.target(),
229
- "inline",
230
- registry
231
- ));
232
- } else if (relation && typeof value === "object" && value !== null) {
233
- flat[key] = renderTarget(value as Record<string, unknown>, relation.target(), "inline", registry);
234
- } else {
235
- flat[key] = coerceDeclaredNumber(value, collection.properties?.[key] as Property | undefined);
236
- }
237
- }
238
-
239
- return stripExcluded(flat, collection);
240
- }
@@ -1,55 +0,0 @@
1
- import { and, sql, SQL } from "drizzle-orm";
2
- import { PgTable } from "drizzle-orm/pg-core";
3
- import { ApiError } from "@rebasepro/server";
4
- import { DrizzleClient } from "../interfaces";
5
-
6
- /**
7
- * Explain a write that matched no rows.
8
- *
9
- * Row-level security filters UPDATE and DELETE through the policy's USING
10
- * clause instead of raising: a denied write is reported by Postgres exactly
11
- * like a successful one that happened to match nothing. Left unchecked, a
12
- * caller cannot tell "denied" from "done" — the write returns 200/204 and the
13
- * row is untouched. An agent handed a key with `orders:delete` and no delete
14
- * policy is the case that makes it concrete: it deletes nothing, forever, and
15
- * is told it worked every time.
16
- *
17
- * Re-reading the target over the *same* RLS-scoped handle separates the two
18
- * cases. A visible row means the policy rejected the write (403); an invisible
19
- * one means there is nothing there to write for this caller (404, matching what
20
- * a GET would say). The re-read is bound by the caller's own policies, so it
21
- * discloses nothing a plain read wouldn't.
22
- *
23
- * Only reached when zero rows matched, so the happy path pays nothing.
24
- *
25
- * It lives here, rather than beside its first caller, because every zero-row
26
- * write has to answer the same question and answer it identically: the rule
27
- * that a readable-but-unwritable target is a 403 is the contract, and a second
28
- * copy of it is a second chance to get it wrong.
29
- *
30
- * @param handle The RLS-scoped connection the write ran on — not a fresh
31
- * one, or the re-read would answer for a different caller.
32
- * @param table The table the write targeted (the junction, for a link).
33
- * @param conditions The write's own WHERE terms, reused verbatim.
34
- * @param denied Message for the 403: the target is there and was refused.
35
- * @param missing Message for the 404: there is nothing there for this caller.
36
- */
37
- export async function explainZeroRowWrite(
38
- handle: DrizzleClient,
39
- table: PgTable,
40
- conditions: SQL[],
41
- denied: string,
42
- missing: string
43
- ): Promise<ApiError> {
44
- const visible = await handle
45
- .select({ present: sql<number>`1` })
46
- .from(table)
47
- .where(and(...conditions))
48
- .limit(1);
49
-
50
- if (visible.length > 0) {
51
- return ApiError.forbidden(denied, "WRITE_DENIED");
52
- }
53
-
54
- return ApiError.notFound(missing);
55
- }
package/src/types.ts DELETED
@@ -1,4 +0,0 @@
1
- import type { PgTable, AnyPgColumn } from "drizzle-orm/pg-core";
2
-
3
- /** Drizzle PgTable with column access by name. Runtime Drizzle tables satisfy this shape. */
4
- export type RebasePgTable = PgTable & Record<string, AnyPgColumn>;
@@ -1,58 +0,0 @@
1
- /**
2
- * Connection-string rewrites for the tools Rebase shells out to.
3
- *
4
- * Pure and dependency-free: `pg-tools` builds argument vectors without a live
5
- * server, and these have to be usable from there.
6
- */
7
-
8
- /**
9
- * The `sslmode` values libpq accepts. `no-verify` is conspicuously not one.
10
- */
11
- const LIBPQ_SSLMODES = new Set(["disable", "allow", "prefer", "require", "verify-ca", "verify-full"]);
12
-
13
- /**
14
- * Make a connection string safe to hand to a libpq program.
15
- *
16
- * `sslmode=no-verify` is a node-postgres convention — encrypt, but do not check
17
- * the certificate — and libpq does not have it. It does not degrade or warn:
18
- *
19
- * psql: error: invalid sslmode value: "no-verify"
20
- *
21
- * which means a `DATABASE_URL` that works perfectly for the app makes `psql`,
22
- * `pg_dump`, `pg_restore` and Atlas all refuse to start, with an error that
23
- * points at the value rather than at the convention it comes from. It has cost
24
- * this project time more than once.
25
- *
26
- * `require` is the honest translation: libpq's `require` encrypts and does not
27
- * verify the certificate either, which is exactly what `no-verify` asks for.
28
- * Nothing is relaxed by the rewrite — `verify-ca` and `verify-full` are left
29
- * alone, so a connection string that asked for verification still gets it.
30
- *
31
- * Only `sslmode` is touched, and only when it is a value libpq would reject.
32
- * Anything unparseable is returned as given: a connection that works
33
- * unverified beats one this corrupted.
34
- */
35
- export function forLibpq(connectionString: string): string {
36
- let url: URL;
37
- try {
38
- url = new URL(connectionString);
39
- } catch {
40
- // Key/value DSNs and anything else we cannot parse. A `sslmode` in one
41
- // of those is space-separated rather than a query parameter, and
42
- // rewriting it with a regex is how a password containing "sslmode="
43
- // gets mangled.
44
- return connectionString;
45
- }
46
- if (url.protocol !== "postgres:" && url.protocol !== "postgresql:") return connectionString;
47
-
48
- const sslmode = url.searchParams.get("sslmode");
49
- if (sslmode === null || LIBPQ_SSLMODES.has(sslmode)) return connectionString;
50
-
51
- url.searchParams.set("sslmode", "require");
52
- // Re-serialize by hand for the reason `pinSearchPath` does: URLSearchParams
53
- // writes a space as `+`, which node-postgres decodes and libpq does not.
54
- url.search = Array.from(url.searchParams.entries())
55
- .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
56
- .join("&");
57
- return url.toString();
58
- }