@samebase/alchemy-convex 0.3.0 → 0.4.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/NOTICE +24 -0
- package/README.md +128 -280
- package/lib/index.d.ts +159 -327
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +754 -905
- package/lib/index.js.map +1 -1
- package/package.json +4 -3
- package/src/Credentials.ts +85 -27
- package/src/Deployment.ts +461 -122
- package/src/ManagementApi.ts +84 -64
- package/src/Project.ts +133 -16
- package/src/Providers.ts +5 -41
- package/src/Push.ts +646 -0
- package/src/index.ts +15 -47
- package/src/Code.ts +0 -463
- package/src/DefaultEnvironmentVariable.ts +0 -263
- package/src/Deploy.ts +0 -183
- package/src/DeployKey.ts +0 -332
- package/src/EnvironmentVariable.ts +0 -289
- package/src/PreviewDeployKey.ts +0 -153
package/src/ManagementApi.ts
CHANGED
|
@@ -11,7 +11,7 @@ import * as Layer from "effect/Layer";
|
|
|
11
11
|
import * as Redacted from "effect/Redacted";
|
|
12
12
|
import * as Schedule from "effect/Schedule";
|
|
13
13
|
import * as Schema from "effect/Schema";
|
|
14
|
-
import { Credentials } from "./Credentials.ts";
|
|
14
|
+
import { Credentials, type ResolvedCredentials } from "./Credentials.ts";
|
|
15
15
|
|
|
16
16
|
export type ManagementClient = ReturnType<typeof createManagementClient>;
|
|
17
17
|
export type DeploymentClient = ReturnType<typeof createDeploymentClient>;
|
|
@@ -79,6 +79,11 @@ export interface ManagementApiService {
|
|
|
79
79
|
* that key would revoke the credential.
|
|
80
80
|
*/
|
|
81
81
|
readonly isCredentialToken: (secret: string) => Effect.Effect<boolean>;
|
|
82
|
+
/**
|
|
83
|
+
* The same calls with another bearer token, such as a preview deploy key,
|
|
84
|
+
* which the Management API takes for the preview deployments of its project.
|
|
85
|
+
*/
|
|
86
|
+
readonly withToken: (token: Redacted.Redacted<string>) => ManagementApiService;
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
/** The part of a Convex token after the first "|", or the whole value when it has none. */
|
|
@@ -174,76 +179,91 @@ export const absentAsUndefined = <A, R>(
|
|
|
174
179
|
const TeamRow = Schema.Struct({ id: Schema.Number, slug: Schema.String });
|
|
175
180
|
const TeamRows = Schema.Array(TeamRow);
|
|
176
181
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
Effect.map((resolved) => createManagementClient(Redacted.value(resolved.accessToken))),
|
|
185
|
-
),
|
|
186
|
-
).pipe(Effect.flatten);
|
|
182
|
+
/** The service for one way to get the bearer token. */
|
|
183
|
+
const makeService = (credentials: Effect.Effect<ResolvedCredentials>): ManagementApiService => {
|
|
184
|
+
const client = Effect.cached(
|
|
185
|
+
credentials.pipe(
|
|
186
|
+
Effect.map((resolved) => createManagementClient(Redacted.value(resolved.accessToken))),
|
|
187
|
+
),
|
|
188
|
+
).pipe(Effect.flatten);
|
|
187
189
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
+
const request: ManagementApiService["request"] = (operation, run) =>
|
|
191
|
+
client.pipe(Effect.flatMap((c) => settle(operation, () => run(c))));
|
|
190
192
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
+
const requestVoid: ManagementApiService["requestVoid"] = (operation, run) =>
|
|
194
|
+
client.pipe(Effect.flatMap((c) => settleVoid(operation, () => run(c))));
|
|
193
195
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
});
|
|
217
|
-
if (!response.ok) {
|
|
218
|
-
throw new Error(`team lookup for "${team}" failed with HTTP ${response.status}`);
|
|
219
|
-
}
|
|
220
|
-
return Schema.decodeUnknownSync(TeamRows)(await response.json());
|
|
221
|
-
},
|
|
222
|
-
catch: (cause) =>
|
|
223
|
-
new ConvexApiError({
|
|
224
|
-
operation: "resolve team",
|
|
225
|
-
status: 0,
|
|
226
|
-
message: cause instanceof Error ? cause.message : String(cause),
|
|
227
|
-
}),
|
|
196
|
+
const deploymentRequest: ManagementApiService["deploymentRequest"] = (
|
|
197
|
+
operation,
|
|
198
|
+
deploymentUrlOrName,
|
|
199
|
+
deployKey,
|
|
200
|
+
run,
|
|
201
|
+
) =>
|
|
202
|
+
settle(operation, () =>
|
|
203
|
+
run(createDeploymentClient(deploymentUrlOrName, Redacted.value(deployKey))),
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
// The Management API addresses teams by numeric id. The only slug lookup
|
|
207
|
+
// it offers is per project, so a slug falls back to the dashboard's team
|
|
208
|
+
// list, which is unofficial. Pass the numeric team id to skip this.
|
|
209
|
+
const resolveTeamId: ManagementApiService["resolveTeamId"] = (team) =>
|
|
210
|
+
Effect.gen(function* () {
|
|
211
|
+
if (typeof team === "number") return team;
|
|
212
|
+
if (/^\d+$/.test(team)) return Number(team);
|
|
213
|
+
const resolved = yield* credentials;
|
|
214
|
+
const rows = yield* Effect.tryPromise({
|
|
215
|
+
try: async () => {
|
|
216
|
+
const response = await fetch("https://api.convex.dev/api/dashboard/teams", {
|
|
217
|
+
headers: { Authorization: `Bearer ${Redacted.value(resolved.accessToken)}` },
|
|
228
218
|
});
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
return yield* new ConvexApiError({
|
|
232
|
-
operation: "resolve team",
|
|
233
|
-
status: 404,
|
|
234
|
-
message: `no team with slug "${team}" for this token`,
|
|
235
|
-
});
|
|
219
|
+
if (!response.ok) {
|
|
220
|
+
throw new Error(`team lookup for "${team}" failed with HTTP ${response.status}`);
|
|
236
221
|
}
|
|
237
|
-
return
|
|
222
|
+
return Schema.decodeUnknownSync(TeamRows)(await response.json());
|
|
223
|
+
},
|
|
224
|
+
catch: (cause) =>
|
|
225
|
+
new ConvexApiError({
|
|
226
|
+
operation: "resolve team",
|
|
227
|
+
status: 0,
|
|
228
|
+
message: cause instanceof Error ? cause.message : String(cause),
|
|
229
|
+
}),
|
|
230
|
+
});
|
|
231
|
+
const match = rows.find((row) => row.slug === team);
|
|
232
|
+
if (match === undefined) {
|
|
233
|
+
return yield* new ConvexApiError({
|
|
234
|
+
operation: "resolve team",
|
|
235
|
+
status: 404,
|
|
236
|
+
message: `no team with slug "${team}" for this token`,
|
|
238
237
|
});
|
|
238
|
+
}
|
|
239
|
+
return match.id;
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const isCredentialToken: ManagementApiService["isCredentialToken"] = (secret) =>
|
|
243
|
+
credentials.pipe(
|
|
244
|
+
Effect.map(
|
|
245
|
+
(resolved) => tokenPart(Redacted.value(resolved.accessToken)) === tokenPart(secret),
|
|
246
|
+
),
|
|
247
|
+
);
|
|
239
248
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
249
|
+
const withToken: ManagementApiService["withToken"] = (token) =>
|
|
250
|
+
makeService(Effect.succeed({ accessToken: token, source: "explicit" }));
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
request,
|
|
254
|
+
requestVoid,
|
|
255
|
+
deploymentRequest,
|
|
256
|
+
resolveTeamId,
|
|
257
|
+
isCredentialToken,
|
|
258
|
+
withToken,
|
|
259
|
+
};
|
|
260
|
+
};
|
|
246
261
|
|
|
247
|
-
|
|
262
|
+
export const ManagementApiLive = (): Layer.Layer<ManagementApi, never, Credentials> =>
|
|
263
|
+
Layer.effect(
|
|
264
|
+
ManagementApi,
|
|
265
|
+
Effect.gen(function* () {
|
|
266
|
+
const credentials = yield* Credentials;
|
|
267
|
+
return makeService(credentials.accessToken);
|
|
248
268
|
}),
|
|
249
269
|
);
|
package/src/Project.ts
CHANGED
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
// - With state, every call addresses the project by `output.projectId`. Only
|
|
13
13
|
// a resource without state looks a project up by name, and it takes over a
|
|
14
14
|
// project that it finds only when adoption is on.
|
|
15
|
+
// - With `slug` instead of `name`, the resource is an existing project,
|
|
16
|
+
// found by GET /teams/{team}/projects/{slug}. It never creates, renames, or
|
|
17
|
+
// (by default) deletes that project. A stack that only pushes to an
|
|
18
|
+
// existing project can pass `{ projectId }` to Convex.Deployment instead
|
|
19
|
+
// and needs no Management API token for the project.
|
|
15
20
|
import { Resource } from "alchemy";
|
|
16
21
|
import { OwnedBySomeoneElse, Unowned } from "alchemy/AdoptPolicy";
|
|
17
22
|
import { isResolved } from "alchemy/Diff";
|
|
@@ -26,16 +31,23 @@ import type { Providers } from "./Providers.ts";
|
|
|
26
31
|
/** Region names the Management API accepts, from the generated schema. */
|
|
27
32
|
export type DeploymentRegion = components["schemas"]["RegionName"];
|
|
28
33
|
|
|
29
|
-
export
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
34
|
+
export type ProjectProps =
|
|
35
|
+
| {
|
|
36
|
+
/** Team slug or numeric team id. A slug costs one extra lookup per run. Cannot change. */
|
|
37
|
+
readonly team: string | number;
|
|
38
|
+
/** Display name. Convex derives the slug from it at creation. A change renames the project. */
|
|
39
|
+
readonly name: string;
|
|
40
|
+
/** Region of the deployment created with the project. Defaults to the team default. Cannot change. */
|
|
41
|
+
readonly deploymentRegion?: DeploymentRegion;
|
|
42
|
+
/** Deployment created with the project. Defaults to `prod`. Cannot change. */
|
|
43
|
+
readonly deploymentType?: "prod" | "dev";
|
|
44
|
+
}
|
|
45
|
+
| {
|
|
46
|
+
/** Team slug or numeric team id. Cannot change. */
|
|
47
|
+
readonly team: string | number;
|
|
48
|
+
/** Slug of an existing project, such as `score-four`. The resource never creates a project. Cannot change. */
|
|
49
|
+
readonly slug: string;
|
|
50
|
+
};
|
|
39
51
|
|
|
40
52
|
export interface ProjectAttributes {
|
|
41
53
|
readonly projectId: number;
|
|
@@ -99,12 +111,33 @@ export class CreatedProjectNotFound extends Schema.TaggedError<CreatedProjectNot
|
|
|
99
111
|
}
|
|
100
112
|
}
|
|
101
113
|
|
|
114
|
+
/** No project in the team has the slug. A `slug` resource never creates one. */
|
|
115
|
+
export class ProjectNotFound extends Schema.TaggedError<ProjectNotFound>()("ProjectNotFound", {
|
|
116
|
+
team: Schema.String,
|
|
117
|
+
slug: Schema.String,
|
|
118
|
+
}) {
|
|
119
|
+
override get message() {
|
|
120
|
+
return `Team ${this.team} has no project with the slug "${this.slug}", and a Convex.Project with a slug never creates one. Check the slug in the project URL of the Convex dashboard, or use name to create a project.`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** The props change the slug of a `slug` resource: that is another project. */
|
|
125
|
+
export class ProjectSlugChange extends Schema.TaggedError<ProjectSlugChange>()(
|
|
126
|
+
"ProjectSlugChange",
|
|
127
|
+
{ projectId: Schema.Number, slug: Schema.String, requestedSlug: Schema.String },
|
|
128
|
+
) {
|
|
129
|
+
override get message() {
|
|
130
|
+
return `Convex.Project ${this.projectId} has the slug "${this.slug}", and the props ask for "${this.requestedSlug}", which is another project. Set slug back to "${this.slug}". For the other project, create a new resource with a new logical id.`;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
102
134
|
/** Fields of GET /projects/{id}, PATCH /projects/{id}, and GET /teams/{team}/projects rows that the provider uses. */
|
|
103
135
|
const ProjectDetails = Schema.Struct({
|
|
104
136
|
id: Schema.Number,
|
|
105
137
|
name: Schema.String,
|
|
106
138
|
slug: Schema.String,
|
|
107
139
|
teamId: Schema.Number,
|
|
140
|
+
teamSlug: Schema.String,
|
|
108
141
|
prodDeploymentName: Schema.NullOr(Schema.String),
|
|
109
142
|
});
|
|
110
143
|
type ProjectDetails = typeof ProjectDetails.Type;
|
|
@@ -133,7 +166,11 @@ const DeploymentRows = Schema.Array(DeploymentRow);
|
|
|
133
166
|
* Fails with {@link ProjectDeploymentChange} when a create-time prop
|
|
134
167
|
* changed. An omitted `deploymentType` means `prod`.
|
|
135
168
|
*/
|
|
136
|
-
const checkCreateTimeProps = (
|
|
169
|
+
const checkCreateTimeProps = (
|
|
170
|
+
projectId: number,
|
|
171
|
+
olds: Extract<ProjectProps, { name: string }>,
|
|
172
|
+
news: Extract<ProjectProps, { name: string }>,
|
|
173
|
+
) => {
|
|
137
174
|
const oldType = olds.deploymentType ?? "prod";
|
|
138
175
|
const newType = news.deploymentType ?? "prod";
|
|
139
176
|
if (oldType !== newType) {
|
|
@@ -159,6 +196,12 @@ const checkCreateTimeProps = (projectId: number, olds: ProjectProps, news: Proje
|
|
|
159
196
|
return Effect.void;
|
|
160
197
|
};
|
|
161
198
|
|
|
199
|
+
/** True when `team`, a slug or a numeric id, names the team of the project. */
|
|
200
|
+
const isTeamOf = (details: ProjectDetails, team: string | number) =>
|
|
201
|
+
typeof team === "number" || /^\d+$/.test(team)
|
|
202
|
+
? details.teamId === Number(team)
|
|
203
|
+
: details.teamSlug === team;
|
|
204
|
+
|
|
162
205
|
export const ProjectProvider = () =>
|
|
163
206
|
Provider.effect(
|
|
164
207
|
Project,
|
|
@@ -203,6 +246,20 @@ export const ProjectProvider = () =>
|
|
|
203
246
|
),
|
|
204
247
|
);
|
|
205
248
|
|
|
249
|
+
/** The project with this slug in the team, or undefined. Official endpoint; the team can be a slug. */
|
|
250
|
+
const getBySlug = (team: string | number, slug: string) =>
|
|
251
|
+
absentAsUndefined(
|
|
252
|
+
api.request("get project by slug", (client) =>
|
|
253
|
+
client.GET("/teams/{team_id_or_slug}/projects/{project_slug}", {
|
|
254
|
+
params: { path: { team_id_or_slug: String(team), project_slug: slug } },
|
|
255
|
+
}),
|
|
256
|
+
),
|
|
257
|
+
).pipe(
|
|
258
|
+
Effect.map((body) =>
|
|
259
|
+
body === undefined ? undefined : Schema.decodeUnknownSync(ProjectDetails)(body),
|
|
260
|
+
),
|
|
261
|
+
);
|
|
262
|
+
|
|
206
263
|
/**
|
|
207
264
|
* The one project in the team with exactly this name, or undefined.
|
|
208
265
|
* Reads every page. `q` narrows the listing on the server, and the
|
|
@@ -279,6 +336,27 @@ export const ProjectProvider = () =>
|
|
|
279
336
|
if (!isResolved(news)) return undefined;
|
|
280
337
|
// Nothing exists yet, so nothing can be lost.
|
|
281
338
|
if (output === undefined) return undefined;
|
|
339
|
+
if ("slug" in news) {
|
|
340
|
+
if (news.slug !== output.slug) {
|
|
341
|
+
return yield* new ProjectSlugChange({
|
|
342
|
+
projectId: output.projectId,
|
|
343
|
+
slug: output.slug,
|
|
344
|
+
requestedSlug: news.slug,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
if (olds.team !== news.team) {
|
|
348
|
+
const details = yield* getById(output.projectId);
|
|
349
|
+
if (details !== undefined && !isTeamOf(details, news.team)) {
|
|
350
|
+
return yield* new ProjectTeamChange({
|
|
351
|
+
projectId: output.projectId,
|
|
352
|
+
teamId: output.teamId,
|
|
353
|
+
requestedTeamId: yield* api.resolveTeamId(news.team),
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// The resource changes nothing on the project.
|
|
358
|
+
return { action: "noop" } as const;
|
|
359
|
+
}
|
|
282
360
|
// A deploymentType or deploymentRegion change fails in reconcile,
|
|
283
361
|
// before any API call. Reconcile compares with the props of the last
|
|
284
362
|
// successful deploy. Here, `olds` can be the props of a refused
|
|
@@ -295,7 +373,9 @@ export const ProjectProvider = () =>
|
|
|
295
373
|
}
|
|
296
374
|
// Never "replace". A name change is an update; reconcile renames
|
|
297
375
|
// the project with PATCH. Otherwise the engine compares the props.
|
|
298
|
-
return olds.name === news.name
|
|
376
|
+
return "name" in olds && olds.name === news.name
|
|
377
|
+
? undefined
|
|
378
|
+
: ({ action: "update" } as const);
|
|
299
379
|
}),
|
|
300
380
|
|
|
301
381
|
read: Effect.fn(function* ({ olds, output }) {
|
|
@@ -303,17 +383,54 @@ export const ProjectProvider = () =>
|
|
|
303
383
|
const details = yield* getById(output.projectId);
|
|
304
384
|
return details === undefined ? undefined : yield* attributesOf(details);
|
|
305
385
|
}
|
|
306
|
-
// No state: an existing project
|
|
307
|
-
//
|
|
308
|
-
const existing =
|
|
386
|
+
// No state: an existing project belongs to someone else until the
|
|
387
|
+
// user passes --adopt. Convex has no ownership tags.
|
|
388
|
+
const existing =
|
|
389
|
+
"slug" in olds
|
|
390
|
+
? yield* getBySlug(olds.team, olds.slug)
|
|
391
|
+
: yield* findByName(yield* api.resolveTeamId(olds.team), olds.name);
|
|
309
392
|
return existing === undefined ? undefined : Unowned(yield* attributesOf(existing));
|
|
310
393
|
}),
|
|
311
394
|
|
|
312
395
|
reconcile: Effect.fn(function* ({ fqn, olds, news, output }) {
|
|
396
|
+
if ("slug" in news) {
|
|
397
|
+
// An existing project: found, checked, never created or changed.
|
|
398
|
+
const details =
|
|
399
|
+
output === undefined
|
|
400
|
+
? yield* getBySlug(news.team, news.slug)
|
|
401
|
+
: yield* getById(output.projectId);
|
|
402
|
+
if (details === undefined) {
|
|
403
|
+
return yield* new ProjectNotFound({ team: String(news.team), slug: news.slug });
|
|
404
|
+
}
|
|
405
|
+
if (details.slug !== news.slug) {
|
|
406
|
+
return yield* new ProjectSlugChange({
|
|
407
|
+
projectId: details.id,
|
|
408
|
+
slug: details.slug,
|
|
409
|
+
requestedSlug: news.slug,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
if (!isTeamOf(details, news.team)) {
|
|
413
|
+
return yield* new ProjectTeamChange({
|
|
414
|
+
projectId: details.id,
|
|
415
|
+
teamId: details.teamId,
|
|
416
|
+
requestedTeamId: yield* api.resolveTeamId(news.team),
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
if (output === undefined && !(yield* shouldAdopt(fqn))) {
|
|
420
|
+
return yield* new OwnedBySomeoneElse({
|
|
421
|
+
message: `Convex project ${details.id} ("${news.slug}") exists and is not in this stack's state. Re-run with --adopt to take it over.`,
|
|
422
|
+
resourceType: Project.Type,
|
|
423
|
+
physicalName: news.slug,
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
return yield* attributesOf(details);
|
|
427
|
+
}
|
|
313
428
|
const teamId = yield* api.resolveTeamId(news.team);
|
|
314
429
|
if (output !== undefined) {
|
|
315
430
|
// State names the project. Never look it up by name here.
|
|
316
|
-
if (olds !== undefined
|
|
431
|
+
if (olds !== undefined && "name" in olds) {
|
|
432
|
+
yield* checkCreateTimeProps(output.projectId, olds, news);
|
|
433
|
+
}
|
|
317
434
|
if (output.teamId !== teamId) {
|
|
318
435
|
return yield* new ProjectTeamChange({
|
|
319
436
|
projectId: output.projectId,
|
package/src/Providers.ts
CHANGED
|
@@ -1,59 +1,23 @@
|
|
|
1
1
|
// The provider collection users merge into a stack:
|
|
2
2
|
// providers: Layer.mergeAll(Cloudflare.providers(), Convex.providers())
|
|
3
|
-
import { CommandExecutorLive } from "alchemy/Command";
|
|
4
3
|
import * as Provider from "alchemy/Provider";
|
|
5
4
|
import * as Layer from "effect/Layer";
|
|
6
|
-
import { Code, CodeProvider } from "./Code.ts";
|
|
7
5
|
import { type Credentials, fromConfig } from "./Credentials.ts";
|
|
8
|
-
import {
|
|
9
|
-
DefaultEnvironmentVariable,
|
|
10
|
-
DefaultEnvironmentVariableProvider,
|
|
11
|
-
} from "./DefaultEnvironmentVariable.ts";
|
|
12
|
-
import { Deploy, DeployProvider } from "./Deploy.ts";
|
|
13
|
-
import { DeployKey, DeployKeyProvider } from "./DeployKey.ts";
|
|
14
6
|
import { Deployment, DeploymentProvider } from "./Deployment.ts";
|
|
15
|
-
import { EnvironmentVariable, EnvironmentVariableProvider } from "./EnvironmentVariable.ts";
|
|
16
7
|
import { ManagementApiLive } from "./ManagementApi.ts";
|
|
17
|
-
import { PreviewDeployKey, PreviewDeployKeyProvider } from "./PreviewDeployKey.ts";
|
|
18
8
|
import { Project, ProjectProvider } from "./Project.ts";
|
|
19
9
|
|
|
20
10
|
export class Providers extends Provider.ProviderCollection<Providers>()("Convex") {}
|
|
21
11
|
|
|
22
12
|
/**
|
|
23
|
-
*
|
|
24
|
-
* default lookup (`CONVEX_ACCESS_TOKEN`, then the Convex CLI login
|
|
13
|
+
* The Convex resource providers. Pass a credentials layer to override the
|
|
14
|
+
* default lookup (`CONVEX_ACCESS_TOKEN`, then the Convex CLI login, and
|
|
15
|
+
* `CONVEX_DEPLOY_KEY`).
|
|
25
16
|
*/
|
|
26
17
|
export const providers = (credentials: Layer.Layer<Credentials> = fromConfig()) =>
|
|
27
|
-
Layer.effect(
|
|
28
|
-
|
|
29
|
-
Provider.collection([
|
|
30
|
-
Project,
|
|
31
|
-
Deployment,
|
|
32
|
-
DeployKey,
|
|
33
|
-
PreviewDeployKey,
|
|
34
|
-
EnvironmentVariable,
|
|
35
|
-
DefaultEnvironmentVariable,
|
|
36
|
-
Code,
|
|
37
|
-
Deploy,
|
|
38
|
-
]),
|
|
39
|
-
).pipe(
|
|
40
|
-
Layer.provide(
|
|
41
|
-
Layer.mergeAll(
|
|
42
|
-
ProjectProvider(),
|
|
43
|
-
DeploymentProvider(),
|
|
44
|
-
DeployKeyProvider(),
|
|
45
|
-
PreviewDeployKeyProvider(),
|
|
46
|
-
EnvironmentVariableProvider(),
|
|
47
|
-
DefaultEnvironmentVariableProvider(),
|
|
48
|
-
CodeProvider(),
|
|
49
|
-
DeployProvider(),
|
|
50
|
-
),
|
|
51
|
-
),
|
|
18
|
+
Layer.effect(Providers, Provider.collection([Project, Deployment])).pipe(
|
|
19
|
+
Layer.provide(Layer.mergeAll(ProjectProvider(), DeploymentProvider())),
|
|
52
20
|
Layer.provide(ManagementApiLive()),
|
|
53
21
|
Layer.provide(credentials),
|
|
54
|
-
// The deprecated Convex.Deploy runs the Convex CLI through Alchemy's
|
|
55
|
-
// command executor. Convex.Code spawns it itself, so that its output
|
|
56
|
-
// stays out of the logs.
|
|
57
|
-
Layer.provide(CommandExecutorLive()),
|
|
58
22
|
Layer.orDie,
|
|
59
23
|
);
|