@alchemy.run/planetscale 2.0.0-beta.84 → 2.0.0-beta.85
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/lib/AuthProvider.d.ts +1 -1
- package/lib/AuthProvider.d.ts.map +1 -1
- package/lib/AuthProvider.js +58 -47
- package/lib/AuthProvider.js.map +1 -1
- package/lib/Branch.js +2 -2
- package/lib/Branch.js.map +1 -1
- package/lib/Contributions.d.ts +1 -1
- package/lib/Credentials.d.ts +1 -1
- package/lib/Credentials.d.ts.map +1 -1
- package/lib/Credentials.js +6 -12
- package/lib/Credentials.js.map +1 -1
- package/lib/MySQL/MySQLClusterSize.d.ts +1 -1
- package/lib/MySQL/MySQLClusterSize.d.ts.map +1 -1
- package/lib/MySQL/MySQLClusterSize.js +1 -1
- package/lib/MySQL/MySQLClusterSize.js.map +1 -1
- package/lib/MySQL/MySQLDatabase.js +2 -2
- package/lib/MySQL/MySQLDatabase.js.map +1 -1
- package/lib/MySQL/MySQLMigrations.d.ts +7 -11
- package/lib/MySQL/MySQLMigrations.d.ts.map +1 -1
- package/lib/MySQL/MySQLMigrations.js +24 -58
- package/lib/MySQL/MySQLMigrations.js.map +1 -1
- package/lib/MySQL/MySQLPassword.js +2 -2
- package/lib/MySQL/MySQLPassword.js.map +1 -1
- package/lib/Postgres/PostgresDatabase.js +4 -4
- package/lib/Postgres/PostgresDatabase.js.map +1 -1
- package/lib/Postgres/PostgresDefaultRole.js +2 -2
- package/lib/Postgres/PostgresDefaultRole.js.map +1 -1
- package/lib/Postgres/PostgresMigrations.d.ts +3 -11
- package/lib/Postgres/PostgresMigrations.d.ts.map +1 -1
- package/lib/Postgres/PostgresMigrations.js +15 -66
- package/lib/Postgres/PostgresMigrations.js.map +1 -1
- package/lib/Postgres/PostgresRole.js +3 -3
- package/lib/Postgres/PostgresRole.js.map +1 -1
- package/lib/Providers.d.ts +1 -1
- package/lib/Providers.d.ts.map +1 -1
- package/lib/Util.d.ts +3 -3
- package/lib/Util.d.ts.map +1 -1
- package/lib/Util.js +1 -1
- package/lib/Util.js.map +1 -1
- package/package.json +8 -8
- package/src/AuthProvider.ts +91 -78
- package/src/Branch.ts +2 -2
- package/src/Credentials.ts +8 -32
- package/src/MySQL/MySQLClusterSize.ts +1 -1
- package/src/MySQL/MySQLDatabase.ts +2 -2
- package/src/MySQL/MySQLMigrations.ts +31 -80
- package/src/MySQL/MySQLPassword.ts +2 -2
- package/src/Postgres/PostgresDatabase.ts +4 -4
- package/src/Postgres/PostgresDefaultRole.ts +2 -2
- package/src/Postgres/PostgresMigrations.ts +20 -91
- package/src/Postgres/PostgresRole.ts +4 -4
- package/src/Util.ts +1 -1
|
@@ -1,34 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
-
makeMySQLMigrationExecutor,
|
|
3
2
|
runMigrations,
|
|
3
|
+
withMySqlExecutor,
|
|
4
4
|
type NormalizedMigrationsInput,
|
|
5
|
+
type SqlExecutor,
|
|
5
6
|
type StampedMigrationsState,
|
|
6
7
|
} from "@alchemy.run/sql/Migrations";
|
|
7
|
-
import {
|
|
8
|
+
import { applySqlImports, splitSqlStatements } from "@alchemy.run/sql/SqlFile";
|
|
8
9
|
import * as ps from "@distilled.cloud/planetscale";
|
|
9
|
-
import * as Data from "effect/Data";
|
|
10
10
|
import * as Effect from "effect/Effect";
|
|
11
11
|
import * as Redacted from "effect/Redacted";
|
|
12
12
|
import * as Schedule from "effect/Schedule";
|
|
13
|
-
import type { Connection } from "mysql2/promise";
|
|
14
13
|
|
|
15
14
|
const MIGRATION_PASSWORD_TTL_SECONDS = 600;
|
|
16
15
|
|
|
17
|
-
// `mysql2` is an optional peer dependency — loaded lazily so importing the
|
|
18
|
-
// Planetscale provider never requires the driver unless migrations run.
|
|
19
|
-
const importMysql = () =>
|
|
20
|
-
import("mysql2/promise").catch((cause) => {
|
|
21
|
-
throw new Error(
|
|
22
|
-
"Failed to load the 'mysql2' driver. Install the optional peer dependency 'mysql2' to run Planetscale MySQL migrations.",
|
|
23
|
-
{ cause },
|
|
24
|
-
);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
export class MySQLMigrationError extends Data.TaggedError("Planetscale::MySQLMigrationError")<{
|
|
28
|
-
message: string;
|
|
29
|
-
cause?: unknown;
|
|
30
|
-
}> {}
|
|
31
|
-
|
|
32
16
|
export interface MySQLMigrationTarget {
|
|
33
17
|
organization: string;
|
|
34
18
|
database: string;
|
|
@@ -47,69 +31,48 @@ export const runMySQLMigrations = (
|
|
|
47
31
|
runMigrations({
|
|
48
32
|
input,
|
|
49
33
|
stamped,
|
|
50
|
-
withExecutor: (apply) =>
|
|
51
|
-
withMySQLConnection(target, (connection) => apply(makeMySQLMigrationExecutor(connection))),
|
|
34
|
+
withExecutor: (apply) => withMySQLExecutor(target, apply),
|
|
52
35
|
});
|
|
53
36
|
|
|
37
|
+
/**
|
|
38
|
+
* Apply `importFiles`, each once and again only when it changes. Vitess
|
|
39
|
+
* rejects drizzle-kit's `--> statement-breakpoint` markers, so statements
|
|
40
|
+
* run one at a time.
|
|
41
|
+
*/
|
|
54
42
|
export const runMySQLImports = (
|
|
55
43
|
target: MySQLMigrationTarget,
|
|
56
44
|
importFiles: ReadonlyArray<string>,
|
|
57
45
|
rootDir: string,
|
|
58
46
|
previous: Record<string, string>,
|
|
59
47
|
) =>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
const tracked = new Set(importFiles);
|
|
72
|
-
for (const key of Object.keys(hashes)) {
|
|
73
|
-
if (!tracked.has(key)) delete hashes[key];
|
|
74
|
-
}
|
|
75
|
-
return hashes;
|
|
48
|
+
applySqlImports({
|
|
49
|
+
importFiles,
|
|
50
|
+
rootDir,
|
|
51
|
+
previous,
|
|
52
|
+
apply: (file) =>
|
|
53
|
+
withMySQLExecutor(target, (executor) =>
|
|
54
|
+
Effect.forEach(splitSqlStatements(file.sql), (statement) => executor.query(statement), {
|
|
55
|
+
discard: true,
|
|
56
|
+
}),
|
|
57
|
+
),
|
|
76
58
|
});
|
|
77
59
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
Effect.gen(function* () {
|
|
81
|
-
for (const statement of splitSqlStatements(sql)) {
|
|
82
|
-
yield* mysqlQuery(connection, statement);
|
|
83
|
-
}
|
|
84
|
-
}),
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
const withMySQLConnection = <A, E, R>(
|
|
60
|
+
/** A verified-TLS `mysql2` session as a short-lived admin password. */
|
|
61
|
+
const withMySQLExecutor = <A, E, R>(
|
|
88
62
|
target: MySQLMigrationTarget,
|
|
89
|
-
use: (
|
|
63
|
+
use: (executor: SqlExecutor) => Effect.Effect<A, E, R>,
|
|
90
64
|
) =>
|
|
91
65
|
withTemporaryMySQLPassword(target, (password) =>
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
multipleStatements: true,
|
|
102
|
-
ssl: { rejectUnauthorized: true },
|
|
103
|
-
});
|
|
104
|
-
},
|
|
105
|
-
catch: toMigrationError,
|
|
106
|
-
}),
|
|
66
|
+
withMySqlExecutor(
|
|
67
|
+
{
|
|
68
|
+
host: password.host,
|
|
69
|
+
user: password.username,
|
|
70
|
+
password: Redacted.value(password.password),
|
|
71
|
+
database: target.database,
|
|
72
|
+
multipleStatements: true,
|
|
73
|
+
ssl: { rejectUnauthorized: true },
|
|
74
|
+
},
|
|
107
75
|
use,
|
|
108
|
-
(connection) =>
|
|
109
|
-
Effect.tryPromise({
|
|
110
|
-
try: () => connection.end(),
|
|
111
|
-
catch: toMigrationError,
|
|
112
|
-
}).pipe(Effect.ignore),
|
|
113
76
|
),
|
|
114
77
|
);
|
|
115
78
|
|
|
@@ -168,15 +131,3 @@ const withTemporaryMySQLPassword = <A, E, R>(
|
|
|
168
131
|
),
|
|
169
132
|
),
|
|
170
133
|
);
|
|
171
|
-
|
|
172
|
-
const mysqlQuery = (connection: Connection, sql: string) =>
|
|
173
|
-
Effect.tryPromise({
|
|
174
|
-
try: () => connection.query(sql).then(() => undefined),
|
|
175
|
-
catch: toMigrationError,
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
const toMigrationError = (cause: unknown) =>
|
|
179
|
-
new MySQLMigrationError({
|
|
180
|
-
message: cause instanceof Error ? cause.message : String(cause),
|
|
181
|
-
cause,
|
|
182
|
-
});
|
|
@@ -189,7 +189,7 @@ export const MySQLPasswordProvider = () =>
|
|
|
189
189
|
branch: output.branch,
|
|
190
190
|
}),
|
|
191
191
|
),
|
|
192
|
-
Effect.catchTag("NotFound", () => Effect.
|
|
192
|
+
Effect.catchTag("NotFound", () => Effect.undefined),
|
|
193
193
|
);
|
|
194
194
|
}),
|
|
195
195
|
|
|
@@ -217,7 +217,7 @@ export const MySQLPasswordProvider = () =>
|
|
|
217
217
|
branch: branchName,
|
|
218
218
|
id: output.id,
|
|
219
219
|
})
|
|
220
|
-
.pipe(Effect.catchTag("NotFound", () => Effect.
|
|
220
|
+
.pipe(Effect.catchTag("NotFound", () => Effect.undefined))
|
|
221
221
|
: undefined;
|
|
222
222
|
|
|
223
223
|
// 2. Ensure — create the password if it doesn't exist live.
|
|
@@ -200,7 +200,7 @@ export const PostgresDatabaseProvider = () =>
|
|
|
200
200
|
organization,
|
|
201
201
|
database: databaseName,
|
|
202
202
|
})
|
|
203
|
-
.pipe(Effect.catchTag("NotFound", () => Effect.
|
|
203
|
+
.pipe(Effect.catchTag("NotFound", () => Effect.undefined));
|
|
204
204
|
|
|
205
205
|
if (!data) return undefined;
|
|
206
206
|
|
|
@@ -223,7 +223,7 @@ export const PostgresDatabaseProvider = () =>
|
|
|
223
223
|
database: data.name,
|
|
224
224
|
branch: defaultBranch,
|
|
225
225
|
})
|
|
226
|
-
.pipe(Effect.catchTag("NotFound", () => Effect.
|
|
226
|
+
.pipe(Effect.catchTag("NotFound", () => Effect.undefined));
|
|
227
227
|
|
|
228
228
|
const arch = toPostgresClusterArch(branch!.cluster_architecture);
|
|
229
229
|
const clusterSize = branch!.cluster_name;
|
|
@@ -276,7 +276,7 @@ export const PostgresDatabaseProvider = () =>
|
|
|
276
276
|
organization,
|
|
277
277
|
database: observedName,
|
|
278
278
|
})
|
|
279
|
-
.pipe(Effect.catchTag("NotFound", () => Effect.
|
|
279
|
+
.pipe(Effect.catchTag("NotFound", () => Effect.undefined));
|
|
280
280
|
|
|
281
281
|
// Ensure — if missing, create.
|
|
282
282
|
if (!observed) {
|
|
@@ -425,7 +425,7 @@ export const PostgresDatabaseProvider = () =>
|
|
|
425
425
|
database: data.name,
|
|
426
426
|
branch: defaultBranch,
|
|
427
427
|
})
|
|
428
|
-
.pipe(Effect.catchTag("NotFound", () => Effect.
|
|
428
|
+
.pipe(Effect.catchTag("NotFound", () => Effect.undefined));
|
|
429
429
|
|
|
430
430
|
const arch = toPostgresClusterArch(branch?.cluster_architecture);
|
|
431
431
|
const clusterSize = branch?.cluster_name ?? "";
|
|
@@ -153,7 +153,7 @@ export const PostgresDefaultRoleProvider = () =>
|
|
|
153
153
|
privateHost: role.private_access_host_url,
|
|
154
154
|
privateConnectionServiceName: role.private_connection_service_name,
|
|
155
155
|
})),
|
|
156
|
-
Effect.catchTag("NotFound", () => Effect.
|
|
156
|
+
Effect.catchTag("NotFound", () => Effect.undefined),
|
|
157
157
|
);
|
|
158
158
|
}),
|
|
159
159
|
|
|
@@ -175,7 +175,7 @@ export const PostgresDefaultRoleProvider = () =>
|
|
|
175
175
|
database: databaseName,
|
|
176
176
|
branch: branchName,
|
|
177
177
|
})
|
|
178
|
-
.pipe(Effect.catchTag("NotFound", () => Effect.
|
|
178
|
+
.pipe(Effect.catchTag("NotFound", () => Effect.undefined));
|
|
179
179
|
|
|
180
180
|
// 2. Sync (no-op) — we own the role and it still exists in the
|
|
181
181
|
// cloud. Default roles have nothing mutable in place: their
|
|
@@ -1,36 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
|
-
makePgMigrationExecutor,
|
|
3
2
|
runMigrations,
|
|
3
|
+
withPgExecutor,
|
|
4
4
|
type NormalizedMigrationsInput,
|
|
5
|
+
type SqlExecutor,
|
|
5
6
|
type StampedMigrationsState,
|
|
6
7
|
} from "@alchemy.run/sql/Migrations";
|
|
7
|
-
import {
|
|
8
|
+
import { stripSslQueryParams } from "@alchemy.run/sql/PostgresDriver";
|
|
9
|
+
import { applySqlImports } from "@alchemy.run/sql/SqlFile";
|
|
8
10
|
import * as ps from "@distilled.cloud/planetscale";
|
|
9
|
-
import * as Data from "effect/Data";
|
|
10
11
|
import * as Effect from "effect/Effect";
|
|
11
12
|
import * as Redacted from "effect/Redacted";
|
|
12
13
|
import * as Schedule from "effect/Schedule";
|
|
13
|
-
import type { Client } from "pg";
|
|
14
|
-
|
|
15
|
-
// `pg` is an optional peer dependency — loaded lazily so importing the
|
|
16
|
-
// Planetscale provider never requires the driver unless migrations run.
|
|
17
|
-
const importPg = () =>
|
|
18
|
-
import("pg").catch((cause) => {
|
|
19
|
-
throw new Error(
|
|
20
|
-
"Failed to load the 'pg' driver. Install the optional peer dependency 'pg' to run Planetscale Postgres migrations.",
|
|
21
|
-
{ cause },
|
|
22
|
-
);
|
|
23
|
-
});
|
|
24
14
|
|
|
25
15
|
const MIGRATION_ROLE_TTL_SECONDS = 600;
|
|
26
16
|
|
|
27
|
-
export class PostgresMigrationError extends Data.TaggedError(
|
|
28
|
-
"Planetscale::PostgresMigrationError",
|
|
29
|
-
)<{
|
|
30
|
-
message: string;
|
|
31
|
-
cause?: unknown;
|
|
32
|
-
}> {}
|
|
33
|
-
|
|
34
17
|
export interface PostgresMigrationTarget {
|
|
35
18
|
organization: string;
|
|
36
19
|
database: string;
|
|
@@ -49,67 +32,36 @@ export const runPostgresMigrations = (
|
|
|
49
32
|
runMigrations({
|
|
50
33
|
input,
|
|
51
34
|
stamped,
|
|
52
|
-
withExecutor: (apply) =>
|
|
53
|
-
withPostgresClient(target, (client) => apply(makePgMigrationExecutor(client))),
|
|
35
|
+
withExecutor: (apply) => withPostgresExecutor(target, apply),
|
|
54
36
|
});
|
|
55
37
|
|
|
38
|
+
/** Apply `importFiles`, each once and again only when it changes. */
|
|
56
39
|
export const runPostgresImports = (
|
|
57
40
|
target: PostgresMigrationTarget,
|
|
58
41
|
importFiles: ReadonlyArray<string>,
|
|
59
42
|
rootDir: string,
|
|
60
43
|
previous: Record<string, string>,
|
|
61
44
|
) =>
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
yield* runPostgresSql(target, file.sql);
|
|
71
|
-
hashes[filePath] = file.hash;
|
|
72
|
-
}
|
|
73
|
-
const tracked = new Set(importFiles);
|
|
74
|
-
for (const key of Object.keys(hashes)) {
|
|
75
|
-
if (!tracked.has(key)) delete hashes[key];
|
|
76
|
-
}
|
|
77
|
-
return hashes;
|
|
45
|
+
applySqlImports({
|
|
46
|
+
importFiles,
|
|
47
|
+
rootDir,
|
|
48
|
+
previous,
|
|
49
|
+
apply: (file) =>
|
|
50
|
+
withPostgresExecutor(target, (executor) => executor.query(file.sql)).pipe(Effect.asVoid),
|
|
78
51
|
});
|
|
79
52
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const withPostgresClient = <A, E, R>(
|
|
53
|
+
/** A verified-TLS `pg` session as a short-lived migration role. */
|
|
54
|
+
const withPostgresExecutor = <A, E, R>(
|
|
84
55
|
target: PostgresMigrationTarget,
|
|
85
|
-
use: (
|
|
56
|
+
use: (executor: SqlExecutor) => Effect.Effect<A, E, R>,
|
|
86
57
|
) =>
|
|
87
58
|
withTemporaryPostgresRole(target, (role) =>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
});
|
|
94
|
-
const client = yield* Effect.sync(
|
|
95
|
-
() =>
|
|
96
|
-
new Client({
|
|
97
|
-
connectionString: role.connectionUrl.pipe(Redacted.value, stripPgSslQueryParams),
|
|
98
|
-
ssl: { rejectUnauthorized: true },
|
|
99
|
-
}),
|
|
100
|
-
);
|
|
101
|
-
yield* Effect.tryPromise({
|
|
102
|
-
try: () => client.connect(),
|
|
103
|
-
catch: toMigrationError,
|
|
104
|
-
});
|
|
105
|
-
return client;
|
|
106
|
-
}),
|
|
59
|
+
withPgExecutor(
|
|
60
|
+
{
|
|
61
|
+
connectionString: role.connectionUrl.pipe(Redacted.value, stripSslQueryParams),
|
|
62
|
+
ssl: { rejectUnauthorized: true },
|
|
63
|
+
},
|
|
107
64
|
use,
|
|
108
|
-
(client) =>
|
|
109
|
-
Effect.tryPromise({
|
|
110
|
-
try: () => client.end(),
|
|
111
|
-
catch: toMigrationError,
|
|
112
|
-
}).pipe(Effect.ignore),
|
|
113
65
|
),
|
|
114
66
|
);
|
|
115
67
|
|
|
@@ -168,26 +120,3 @@ const withTemporaryPostgresRole = <A, E, R>(
|
|
|
168
120
|
),
|
|
169
121
|
),
|
|
170
122
|
);
|
|
171
|
-
|
|
172
|
-
const pgExec = (client: Client, sql: string, values?: ReadonlyArray<unknown>) =>
|
|
173
|
-
Effect.tryPromise({
|
|
174
|
-
try: () => client.query(sql, values as Array<unknown>).then(() => undefined),
|
|
175
|
-
catch: toMigrationError,
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
const toMigrationError = (cause: unknown) =>
|
|
179
|
-
new PostgresMigrationError({
|
|
180
|
-
message: cause instanceof Error ? cause.message : String(cause),
|
|
181
|
-
cause,
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
const stripPgSslQueryParams = (uri: string): string => {
|
|
185
|
-
try {
|
|
186
|
-
const url = new URL(uri);
|
|
187
|
-
url.searchParams.delete("sslmode");
|
|
188
|
-
url.searchParams.delete("channel_binding");
|
|
189
|
-
return url.toString();
|
|
190
|
-
} catch {
|
|
191
|
-
return uri;
|
|
192
|
-
}
|
|
193
|
-
};
|
|
@@ -251,8 +251,8 @@ export const PostgresRoleProvider = () =>
|
|
|
251
251
|
}),
|
|
252
252
|
),
|
|
253
253
|
Effect.catchTags({
|
|
254
|
-
Forbidden: () => Effect.
|
|
255
|
-
NotFound: () => Effect.
|
|
254
|
+
Forbidden: () => Effect.undefined,
|
|
255
|
+
NotFound: () => Effect.undefined,
|
|
256
256
|
}),
|
|
257
257
|
);
|
|
258
258
|
}),
|
|
@@ -277,7 +277,7 @@ export const PostgresRoleProvider = () =>
|
|
|
277
277
|
organization,
|
|
278
278
|
database: databaseName,
|
|
279
279
|
})
|
|
280
|
-
.pipe(Effect.catchTag("NotFound", () => Effect.
|
|
280
|
+
.pipe(Effect.catchTag("NotFound", () => Effect.undefined))
|
|
281
281
|
: undefined;
|
|
282
282
|
|
|
283
283
|
// 2. Ensure — create if missing. Single branch covers both
|
|
@@ -292,7 +292,7 @@ export const PostgresRoleProvider = () =>
|
|
|
292
292
|
// once on create; on an observe-hit we re-use the cached
|
|
293
293
|
// plaintext from output (which is why adoption requires
|
|
294
294
|
// pre-existing state — see `read`).
|
|
295
|
-
let live: planetscale.GetRoleOutput
|
|
295
|
+
let live: planetscale.GetRoleOutput;
|
|
296
296
|
let plaintext: Redacted.Redacted<string>;
|
|
297
297
|
if (observed) {
|
|
298
298
|
live = observed;
|
package/src/Util.ts
CHANGED
|
@@ -48,7 +48,7 @@ export const deleteUnprotectedDatabase = Effect.fn(function* (
|
|
|
48
48
|
) {
|
|
49
49
|
const live = yield* ps
|
|
50
50
|
.getDatabase({ organization, database })
|
|
51
|
-
.pipe(Effect.catchTag("NotFound", () => Effect.
|
|
51
|
+
.pipe(Effect.catchTag("NotFound", () => Effect.undefined));
|
|
52
52
|
if (!live) return;
|
|
53
53
|
if (live.deletion_protected) {
|
|
54
54
|
return yield* new PlanetscaleDeletionProtected({
|