@alchemy.run/planetscale 0.0.0-stage → 2.0.0-beta.84

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 (123) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +87 -2
  3. package/lib/AuthProvider.d.ts +64 -0
  4. package/lib/AuthProvider.d.ts.map +1 -0
  5. package/lib/AuthProvider.js +384 -0
  6. package/lib/AuthProvider.js.map +1 -0
  7. package/lib/Branch.d.ts +128 -0
  8. package/lib/Branch.d.ts.map +1 -0
  9. package/lib/Branch.js +420 -0
  10. package/lib/Branch.js.map +1 -0
  11. package/lib/Contributions.d.ts +7 -0
  12. package/lib/Contributions.d.ts.map +1 -0
  13. package/lib/Contributions.js +6 -0
  14. package/lib/Contributions.js.map +1 -0
  15. package/lib/Credentials.d.ts +33 -0
  16. package/lib/Credentials.d.ts.map +1 -0
  17. package/lib/Credentials.js +65 -0
  18. package/lib/Credentials.js.map +1 -0
  19. package/lib/Database.d.ts +138 -0
  20. package/lib/Database.d.ts.map +1 -0
  21. package/lib/Database.js +1 -0
  22. package/lib/Database.js.map +1 -0
  23. package/lib/MySQL/MySQLBranch.d.ts +87 -0
  24. package/lib/MySQL/MySQLBranch.d.ts.map +1 -0
  25. package/lib/MySQL/MySQLBranch.js +15 -0
  26. package/lib/MySQL/MySQLBranch.js.map +1 -0
  27. package/lib/MySQL/MySQLClusterSize.d.ts +32 -0
  28. package/lib/MySQL/MySQLClusterSize.d.ts.map +1 -0
  29. package/lib/MySQL/MySQLClusterSize.js +111 -0
  30. package/lib/MySQL/MySQLClusterSize.js.map +1 -0
  31. package/lib/MySQL/MySQLDatabase.d.ts +141 -0
  32. package/lib/MySQL/MySQLDatabase.d.ts.map +1 -0
  33. package/lib/MySQL/MySQLDatabase.js +294 -0
  34. package/lib/MySQL/MySQLDatabase.js.map +1 -0
  35. package/lib/MySQL/MySQLMigrations.d.ts +24 -0
  36. package/lib/MySQL/MySQLMigrations.d.ts.map +1 -0
  37. package/lib/MySQL/MySQLMigrations.js +106 -0
  38. package/lib/MySQL/MySQLMigrations.js.map +1 -0
  39. package/lib/MySQL/MySQLOrigin.d.ts +15 -0
  40. package/lib/MySQL/MySQLOrigin.d.ts.map +1 -0
  41. package/lib/MySQL/MySQLOrigin.js +2 -0
  42. package/lib/MySQL/MySQLOrigin.js.map +1 -0
  43. package/lib/MySQL/MySQLPassword.d.ts +116 -0
  44. package/lib/MySQL/MySQLPassword.d.ts.map +1 -0
  45. package/lib/MySQL/MySQLPassword.js +212 -0
  46. package/lib/MySQL/MySQLPassword.js.map +1 -0
  47. package/lib/MySQL/index.d.ts +6 -0
  48. package/lib/MySQL/index.d.ts.map +1 -0
  49. package/lib/MySQL/index.js +6 -0
  50. package/lib/MySQL/index.js.map +1 -0
  51. package/lib/OAuthClient.d.ts +32 -0
  52. package/lib/OAuthClient.d.ts.map +1 -0
  53. package/lib/OAuthClient.js +52 -0
  54. package/lib/OAuthClient.js.map +1 -0
  55. package/lib/Postgres/PostgresBranch.d.ts +75 -0
  56. package/lib/Postgres/PostgresBranch.d.ts.map +1 -0
  57. package/lib/Postgres/PostgresBranch.js +15 -0
  58. package/lib/Postgres/PostgresBranch.js.map +1 -0
  59. package/lib/Postgres/PostgresClusterSize.d.ts +77 -0
  60. package/lib/Postgres/PostgresClusterSize.d.ts.map +1 -0
  61. package/lib/Postgres/PostgresClusterSize.js +89 -0
  62. package/lib/Postgres/PostgresClusterSize.js.map +1 -0
  63. package/lib/Postgres/PostgresDatabase.d.ts +95 -0
  64. package/lib/Postgres/PostgresDatabase.d.ts.map +1 -0
  65. package/lib/Postgres/PostgresDatabase.js +294 -0
  66. package/lib/Postgres/PostgresDatabase.js.map +1 -0
  67. package/lib/Postgres/PostgresDefaultRole.d.ts +94 -0
  68. package/lib/Postgres/PostgresDefaultRole.d.ts.map +1 -0
  69. package/lib/Postgres/PostgresDefaultRole.js +187 -0
  70. package/lib/Postgres/PostgresDefaultRole.js.map +1 -0
  71. package/lib/Postgres/PostgresMigrations.d.ts +24 -0
  72. package/lib/Postgres/PostgresMigrations.d.ts.map +1 -0
  73. package/lib/Postgres/PostgresMigrations.js +116 -0
  74. package/lib/Postgres/PostgresMigrations.js.map +1 -0
  75. package/lib/Postgres/PostgresOrigin.d.ts +15 -0
  76. package/lib/Postgres/PostgresOrigin.d.ts.map +1 -0
  77. package/lib/Postgres/PostgresOrigin.js +2 -0
  78. package/lib/Postgres/PostgresOrigin.js.map +1 -0
  79. package/lib/Postgres/PostgresRole.d.ts +142 -0
  80. package/lib/Postgres/PostgresRole.d.ts.map +1 -0
  81. package/lib/Postgres/PostgresRole.js +268 -0
  82. package/lib/Postgres/PostgresRole.js.map +1 -0
  83. package/lib/Postgres/index.d.ts +7 -0
  84. package/lib/Postgres/index.d.ts.map +1 -0
  85. package/lib/Postgres/index.js +7 -0
  86. package/lib/Postgres/index.js.map +1 -0
  87. package/lib/Providers.d.ts +24 -0
  88. package/lib/Providers.d.ts.map +1 -0
  89. package/lib/Providers.js +40 -0
  90. package/lib/Providers.js.map +1 -0
  91. package/lib/Util.d.ts +93 -0
  92. package/lib/Util.d.ts.map +1 -0
  93. package/lib/Util.js +135 -0
  94. package/lib/Util.js.map +1 -0
  95. package/lib/index.d.ts +9 -0
  96. package/lib/index.d.ts.map +1 -0
  97. package/lib/index.js +9 -0
  98. package/lib/index.js.map +1 -0
  99. package/package.json +94 -3
  100. package/src/AuthProvider.ts +571 -0
  101. package/src/Branch.ts +657 -0
  102. package/src/Contributions.ts +9 -0
  103. package/src/Credentials.ts +116 -0
  104. package/src/Database.ts +152 -0
  105. package/src/MySQL/MySQLBranch.ts +109 -0
  106. package/src/MySQL/MySQLClusterSize.ts +185 -0
  107. package/src/MySQL/MySQLDatabase.ts +521 -0
  108. package/src/MySQL/MySQLMigrations.ts +182 -0
  109. package/src/MySQL/MySQLOrigin.ts +15 -0
  110. package/src/MySQL/MySQLPassword.ts +428 -0
  111. package/src/MySQL/index.ts +5 -0
  112. package/src/OAuthClient.ts +59 -0
  113. package/src/Postgres/PostgresBranch.ts +97 -0
  114. package/src/Postgres/PostgresClusterSize.ts +262 -0
  115. package/src/Postgres/PostgresDatabase.ts +469 -0
  116. package/src/Postgres/PostgresDefaultRole.ts +340 -0
  117. package/src/Postgres/PostgresMigrations.ts +193 -0
  118. package/src/Postgres/PostgresOrigin.ts +15 -0
  119. package/src/Postgres/PostgresRole.ts +547 -0
  120. package/src/Postgres/index.ts +6 -0
  121. package/src/Providers.ts +67 -0
  122. package/src/Util.ts +205 -0
  123. package/src/index.ts +14 -0
package/src/Util.ts ADDED
@@ -0,0 +1,205 @@
1
+ import * as ps from "@distilled.cloud/planetscale";
2
+ import type { ScopedPlanStatusSession } from "alchemy/Report";
3
+ import * as Clock from "effect/Clock";
4
+ import * as Data from "effect/Data";
5
+ import * as Effect from "effect/Effect";
6
+ import * as Schedule from "effect/Schedule";
7
+
8
+ /**
9
+ * Tagged error raised when polling for a state predicate that has not yet
10
+ * been reached. Used internally with `Effect.retry` to drive long-running
11
+ * status waits.
12
+ */
13
+ export class NotReady extends Data.TaggedError("Planetscale::NotReady")<{
14
+ description: string;
15
+ }> {}
16
+
17
+ /**
18
+ * Tagged error raised by reconcilers when an immutable property of a live
19
+ * PlanetScale resource does not match the desired configuration (e.g.
20
+ * region, kind, parent_branch). User-recoverable: change the config to
21
+ * match, or replace/delete the existing resource and retry.
22
+ */
23
+ export class PlanetscaleConflict extends Data.TaggedError("Planetscale::Conflict")<{
24
+ message: string;
25
+ }> {}
26
+
27
+ /**
28
+ * Tagged error raised when deleting a database that has deletion
29
+ * protection enabled. Alchemy never turns protection off on its own:
30
+ * set `deletionProtection: false`, deploy, then delete.
31
+ */
32
+ export class PlanetscaleDeletionProtected extends Data.TaggedError(
33
+ "Planetscale::DeletionProtected",
34
+ )<{
35
+ message: string;
36
+ organization: string;
37
+ database: string;
38
+ }> {}
39
+
40
+ /**
41
+ * Deletes a database unless it has deletion protection enabled, in which
42
+ * case it fails with {@link PlanetscaleDeletionProtected}. A database that
43
+ * no longer exists counts as deleted.
44
+ */
45
+ export const deleteUnprotectedDatabase = Effect.fn(function* (
46
+ organization: string,
47
+ database: string,
48
+ ) {
49
+ const live = yield* ps
50
+ .getDatabase({ organization, database })
51
+ .pipe(Effect.catchTag("NotFound", () => Effect.succeed(undefined)));
52
+ if (!live) return;
53
+ if (live.deletion_protected) {
54
+ return yield* new PlanetscaleDeletionProtected({
55
+ message:
56
+ `Planetscale database "${database}" has deletion protection enabled. ` +
57
+ "Set `deletionProtection: false` and deploy before deleting it. " +
58
+ "If it is the old database of a replacement, the resource now " +
59
+ "manages the new database, so turn protection off on this one in " +
60
+ "PlanetScale directly, then deploy again.",
61
+ organization,
62
+ database,
63
+ });
64
+ }
65
+ yield* ps
66
+ .deleteDatabase({ organization, database })
67
+ .pipe(Effect.catchTag("NotFound", () => Effect.void));
68
+ });
69
+
70
+ /**
71
+ * Plans a database replacement. A replacement creates the new database
72
+ * before deleting the old one, so an explicit `name` that stays the same
73
+ * would make both generations the same database: reconcile would converge
74
+ * onto the old database and the cleanup of the old generation would then
75
+ * delete it. That plan fails with {@link PlanetscaleConflict} instead.
76
+ */
77
+ export const replaceDatabase = (
78
+ news: { name?: string },
79
+ output: { name: string } | undefined,
80
+ change: string,
81
+ ) =>
82
+ news.name !== undefined && news.name === output?.name
83
+ ? Effect.fail(
84
+ new PlanetscaleConflict({
85
+ message:
86
+ `Changing the ${change} of Planetscale database "${news.name}" ` +
87
+ "requires a new database, but its explicit `name` is unchanged. " +
88
+ "Give the replacement a different `name`, or omit `name` to let " +
89
+ "Alchemy generate one.",
90
+ }),
91
+ )
92
+ : Effect.succeed({ action: "replace" } as const);
93
+
94
+ /**
95
+ * Default polling schedule: 5s spaced retries with a 30-minute total
96
+ * budget (360 × 5s). Avoids the exponential-blowup trap where later
97
+ * iterations would wait hours, indistinguishable from a hang. Postgres
98
+ * database creates routinely run 10-12 minutes, so a 10-minute budget
99
+ * regularly false-positives as "stuck".
100
+ */
101
+ const defaultSchedule = Schedule.max([Schedule.spaced("5 seconds"), Schedule.recurs(360)]);
102
+
103
+ /**
104
+ * Generic polling helper that retries until `predicate(value)` returns true
105
+ * (or until the schedule is exhausted). Engine-specific helpers (e.g.
106
+ * `waitForKeyspaceReady`, `waitForPendingPostgresChanges`) are built on top
107
+ * of this primitive.
108
+ */
109
+ export const pollUntil = <A, E, R>(
110
+ description: string,
111
+ fn: Effect.Effect<A, E, R>,
112
+ predicate: (value: A) => boolean,
113
+ schedule: Schedule.Schedule<unknown, unknown, never> = defaultSchedule,
114
+ ): Effect.Effect<A, E, R> =>
115
+ // oxlint-disable-next-line effecttsgo/unsafe-effect-type-assertion -- NotReady only escapes once the poll schedule is exhausted
116
+ fn.pipe(
117
+ Effect.flatMap((value) =>
118
+ predicate(value) ? Effect.succeed(value) : Effect.fail(new NotReady({ description })),
119
+ ),
120
+ Effect.retry({
121
+ while: (e: any) => e?._tag === "Planetscale::NotReady",
122
+ schedule,
123
+ }),
124
+ ) as Effect.Effect<A, E, R>;
125
+
126
+ /**
127
+ * Polls a branch via `getBranch` until it reports `ready === true`. Returns
128
+ * the final branch shape. `NotFound` during polling is treated as
129
+ * not-yet-ready (the branch is being provisioned by an upstream operation).
130
+ *
131
+ * If a `session` is supplied, each poll emits a note with elapsed seconds and
132
+ * expectation-setting so the CLI surfaces progress while we sit in the spaced
133
+ * retry loop.
134
+ */
135
+ export const waitForBranchReady = Effect.fn(function* (
136
+ organization: string,
137
+ database: string,
138
+ branch: string,
139
+ session?: ScopedPlanStatusSession,
140
+ ) {
141
+ const startedAt = yield* Clock.currentTimeMillis;
142
+ return yield* pollUntil(
143
+ `branch "${branch}" ready`,
144
+ Effect.gen(function* () {
145
+ if (session) {
146
+ const now = yield* Clock.currentTimeMillis;
147
+ const seconds = Math.floor((now - startedAt) / 1000);
148
+ yield* session.note(
149
+ `Waiting for branch to be ready... (${seconds} seconds elapsed; this can take a few minutes)`,
150
+ );
151
+ }
152
+ return yield* ps.getBranch({ organization, database, branch });
153
+ }).pipe(
154
+ Effect.catchTag("NotFound", () =>
155
+ Effect.fail(new NotReady({ description: `branch "${branch}" not found yet` })),
156
+ ),
157
+ ),
158
+ (data) => data.ready,
159
+ );
160
+ });
161
+
162
+ /**
163
+ * Polls a database via `getDatabase` until it reports `ready === true`.
164
+ * `NotFound` during polling is treated as not-yet-ready (the database is
165
+ * being provisioned by an upstream operation).
166
+ *
167
+ * If a `session` is supplied, each poll emits a note with elapsed seconds and
168
+ * expectation-setting so the CLI surfaces progress while we sit in the spaced
169
+ * retry loop.
170
+ */
171
+ export const waitForDatabaseReady = Effect.fn(function* (
172
+ organization: string,
173
+ database: string,
174
+ session?: ScopedPlanStatusSession,
175
+ ) {
176
+ const startedAt = yield* Clock.currentTimeMillis;
177
+ return yield* pollUntil(
178
+ `database "${database}" ready`,
179
+ Effect.gen(function* () {
180
+ if (session) {
181
+ const now = yield* Clock.currentTimeMillis;
182
+ const seconds = Math.floor((now - startedAt) / 1000);
183
+ yield* session.note(
184
+ `Waiting for database to be ready... (${seconds} seconds elapsed; this can take a few minutes)`,
185
+ );
186
+ }
187
+ return yield* ps.getDatabase({ organization, database });
188
+ }).pipe(
189
+ Effect.catchTag("NotFound", () =>
190
+ Effect.fail(
191
+ new NotReady({
192
+ description: `database "${database}" not found yet`,
193
+ }),
194
+ ),
195
+ ),
196
+ ),
197
+ (data) => data.ready,
198
+ );
199
+ });
200
+
201
+ export const isKnownError = (tag: string, message: string) => (error: unknown) =>
202
+ typeof error === "object" &&
203
+ error !== null &&
204
+ (error as { readonly _tag?: unknown })._tag === tag &&
205
+ (error as { readonly message?: unknown }).message === message;
package/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ export * as Auth from "./AuthProvider.ts";
2
+ export { type BaseBranchAttributes, type BaseBranchProps, makeBranchProvider } from "./Branch.ts";
3
+ export {
4
+ Credentials,
5
+ CredentialsFromEnv,
6
+ DEFAULT_API_BASE_URL,
7
+ fromAuthProvider,
8
+ fromToken,
9
+ } from "./Credentials.ts";
10
+ export * from "./Database.ts";
11
+ export * from "./MySQL/index.ts";
12
+ export * from "./Postgres/index.ts";
13
+ export * from "./Providers.ts";
14
+ export * from "./Util.ts";