@samebase/alchemy-convex 0.28.0 → 0.29.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/README.md +33 -33
- package/lib/{HubClient-BcegkjaP.js → HubClient-HeGoVOLO.js} +84 -64
- package/lib/HubClient-HeGoVOLO.js.map +1 -0
- package/lib/cli.js +4 -4
- package/lib/cli.js.map +1 -1
- package/lib/index.d.ts +24 -23
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/package.json +1 -1
- package/src/ManagementApi.ts +84 -26
- package/src/Project.ts +39 -53
- package/src/State.ts +13 -19
- package/src/cli.ts +11 -11
- package/lib/HubClient-BcegkjaP.js.map +0 -1
package/README.md
CHANGED
|
@@ -44,8 +44,8 @@ export default Alchemy.Stack(
|
|
|
44
44
|
const backend =
|
|
45
45
|
stage === "prod"
|
|
46
46
|
? yield* Convex.Deployment("Backend", {
|
|
47
|
-
// An existing project. A new one: {
|
|
48
|
-
project: yield* Convex.Project("Project", {
|
|
47
|
+
// An existing project of the team of the token. A new one: { name: "my-app" }.
|
|
48
|
+
project: yield* Convex.Project("Project", { slug: "my-app" }),
|
|
49
49
|
type: "prod",
|
|
50
50
|
source: "./convex",
|
|
51
51
|
env: { AUTH_SECRET: Config.Redacted("AUTH_SECRET") },
|
|
@@ -81,7 +81,7 @@ relative to it:
|
|
|
81
81
|
|
|
82
82
|
```ts
|
|
83
83
|
Effect.gen(function* () {
|
|
84
|
-
const project = yield* Convex.Project("Project", {
|
|
84
|
+
const project = yield* Convex.Project("Project", { slug: "my-app" });
|
|
85
85
|
const backend = yield* Convex.Deployment("Backend", {
|
|
86
86
|
project,
|
|
87
87
|
type: "prod",
|
|
@@ -166,34 +166,36 @@ values explicitly: `Convex.providers(Convex.fromValues({ accessToken, deployKey
|
|
|
166
166
|
|
|
167
167
|
## State in Convex
|
|
168
168
|
|
|
169
|
-
`state: Convex.state(
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
`state: Convex.state()` keeps the Alchemy state of every stack and stage of a Convex team in one
|
|
170
|
+
project of that team, `alchemy-state-store`, the same name as the Cloudflare state store of Alchemy,
|
|
171
|
+
so a repository holds one secret and a stack can create and destroy its own project.
|
|
172
172
|
[docs/design/convex-state.md](docs/design/convex-state.md) records the decisions and the
|
|
173
173
|
measurements.
|
|
174
174
|
|
|
175
175
|
```ts
|
|
176
176
|
export default Alchemy.Stack(
|
|
177
177
|
process.env["GITHUB_REPOSITORY"] ?? "my-org/my-app", // the stack name: the repository
|
|
178
|
-
{ providers: Convex.providers(), state: Convex.state(
|
|
178
|
+
{ providers: Convex.providers(), state: Convex.state() },
|
|
179
179
|
Effect.gen(function* () {
|
|
180
180
|
/* ... */
|
|
181
181
|
}),
|
|
182
182
|
);
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
-
Every run needs the team token in `CONVEX_ACCESS_TOKEN`, or the Convex CLI login on a laptop.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
`
|
|
190
|
-
|
|
191
|
-
store
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
185
|
+
Every run needs the team token in `CONVEX_ACCESS_TOKEN`, or the Convex CLI login on a laptop. The
|
|
186
|
+
state and the projects are in the team of that credential. A team access token and an OAuth token of
|
|
187
|
+
a team name their team. The Convex CLI login names no team, so the run uses the team of the
|
|
188
|
+
deployment that the Convex CLI selected: `CONVEX_DEPLOYMENT` in the environment or in `.env.local`,
|
|
189
|
+
which `npx convex dev` writes. A local or anonymous deployment is in no team, so the run stops and
|
|
190
|
+
asks for a cloud deployment. The first run of a team creates the project `alchemy-state-store` and
|
|
191
|
+
pushes the store functions to its production deployment with the Convex CLI of the app; a later
|
|
192
|
+
version of this package pushes a newer store when it finds an older one. A store that is newer than
|
|
193
|
+
the package fails the run with both versions, so the repositories of a team upgrade the package
|
|
194
|
+
together. Each run makes a deploy key of that deployment that can only run the store's internal
|
|
195
|
+
functions, and deletes it at the end. Rows are in the tables `resources` and `outputs` of the
|
|
196
|
+
component `alchemyState` in the dashboard, under the stack name. The store has no lock: run one
|
|
197
|
+
deploy of a stage at a time, such as with a CI concurrency group. The store and its readers
|
|
198
|
+
(Samebase) are released and upgraded together.
|
|
197
199
|
|
|
198
200
|
Store version 3 supports native subscriptions. A trusted server checks repository access, then calls
|
|
199
201
|
internal `reader:authorize({ repository, subject })` with a deployment credential. It gives the
|
|
@@ -264,11 +266,11 @@ call.
|
|
|
264
266
|
|
|
265
267
|
## Resources
|
|
266
268
|
|
|
267
|
-
- `Convex.Project`: `{
|
|
268
|
-
default deployment, or takes over one with that exact name with
|
|
269
|
-
renames the project in place. `{
|
|
270
|
-
over with `--adopt`; it never creates or renames a project.
|
|
271
|
-
`teamId`, `prodDeploymentName`, `prodDeploymentUrl`.
|
|
269
|
+
- `Convex.Project`: `{ name, deploymentRegion?, deploymentType? }` creates a project with its
|
|
270
|
+
default deployment in the team of the credential, or takes over one with that exact name with
|
|
271
|
+
`--adopt`. A changed `name` renames the project in place. `{ slug }` is an existing project of
|
|
272
|
+
that team, found by its slug and taken over with `--adopt`; it never creates or renames a project.
|
|
273
|
+
Outputs `projectId`, `slug`, `name`, `teamId`, `prodDeploymentName`, `prodDeploymentUrl`.
|
|
272
274
|
- `Convex.Deployment`: `{ project, type: "prod" }` or `{ project, type: "dev" | "preview", name }`,
|
|
273
275
|
each with optional `rootDir`, `source`, and `env`. Outputs `name` (such as `happy-animal-123`),
|
|
274
276
|
`url`, `siteUrl`, `type`, `projectId`, `reference`, `previewName`, `hash`, and `envNames`.
|
|
@@ -461,8 +463,8 @@ repository `owner/name`. A stack without the stage of the pull request has no ca
|
|
|
461
463
|
|
|
462
464
|
- `Convex.Project` and `Convex.Deployment` have the removal policy `retain`. `alchemy destroy`
|
|
463
465
|
forgets them and Convex keeps them. Add `RemovalPolicy.destroy()` to delete with the resource.
|
|
464
|
-
- A project is never replaced. A change of `
|
|
465
|
-
stops the deploy with a typed error that names the fix.
|
|
466
|
+
- A project is never replaced. A change of `slug`, `deploymentType`, or `deploymentRegion`, or a
|
|
467
|
+
credential of another team, stops the deploy with a typed error that names the fix.
|
|
466
468
|
- A deployment keeps its `project`, `type`, and `name`; a change stops the deploy with
|
|
467
469
|
`DeploymentIdentityChange`. A production deployment is never deleted, also not with
|
|
468
470
|
`RemovalPolicy.destroy()`, and also not a dev deployment that became one.
|
|
@@ -494,8 +496,6 @@ repository `owner/name`. A stack without the stage of the pull request has no ca
|
|
|
494
496
|
deployment must answer there.
|
|
495
497
|
- A run that stops between a deployment create and the state write leaves a deployment that state
|
|
496
498
|
does not know. The next run stops with `OwnedBySomeoneElse`; run it with `--adopt`.
|
|
497
|
-
- Team slugs for `{ team, name }` resolve through an unofficial dashboard endpoint. Pass the numeric
|
|
498
|
-
team id to avoid it. `{ team, slug }` uses the official endpoint.
|
|
499
499
|
- Not covered: deployment regions and classes, custom domains, log streams, teams and members,
|
|
500
500
|
components, snapshots.
|
|
501
501
|
|
|
@@ -512,11 +512,11 @@ pnpm run test:live # real API calls, see below
|
|
|
512
512
|
|
|
513
513
|
Unit tests run against recorded Management API payloads in `test/fixtures/management/` and a fake
|
|
514
514
|
Convex CLI. Live tests run only with `ALCHEMY_CONVEX_LIVE=1`, which `pnpm run test:live` sets. They
|
|
515
|
-
need a
|
|
516
|
-
Each test creates a project whose name starts with
|
|
517
|
-
`tmp-alchemy-convex-`), deletes it at the end, also after a
|
|
518
|
-
project, or a deployment of a project, without that prefix. A file
|
|
519
|
-
for example `pnpm run test:live test/live/plane.live.test.ts`.
|
|
515
|
+
need a team access token of `ALCHEMY_CONVEX_LIVE_TEAM` in `CONVEX_ACCESS_TOKEN`: the projects are in
|
|
516
|
+
the team of the token. Each test creates a project whose name starts with
|
|
517
|
+
`ALCHEMY_CONVEX_LIVE_PREFIX` (default `tmp-alchemy-convex-`), deletes it at the end, also after a
|
|
518
|
+
failure, and refuses to delete a project, or a deployment of a project, without that prefix. A file
|
|
519
|
+
argument runs one live file only, for example `pnpm run test:live test/live/plane.live.test.ts`.
|
|
520
520
|
|
|
521
521
|
Releases: change `version` in `package.json` in a pull request. After the merge,
|
|
522
522
|
`.github/workflows/release.yml` publishes that version from `main` with npm trusted publishing.
|
|
@@ -18,10 +18,11 @@ import { diffBindings, isResolved } from "alchemy/Diff";
|
|
|
18
18
|
import * as Provider from "alchemy/Provider";
|
|
19
19
|
import * as ChildProcessSpawner from "effect/process/ChildProcessSpawner";
|
|
20
20
|
import { AlchemyContext } from "alchemy/AlchemyContext";
|
|
21
|
+
import { readFileSync } from "node:fs";
|
|
22
|
+
import { parseEnv, promisify, stripVTControlCharacters } from "node:util";
|
|
21
23
|
import { createDeploymentClient, createManagementClient } from "@convex-dev/platform";
|
|
22
24
|
import * as Schedule from "effect/Schedule";
|
|
23
25
|
import { createHash } from "node:crypto";
|
|
24
|
-
import { promisify, stripVTControlCharacters } from "node:util";
|
|
25
26
|
import { hashDirectory } from "alchemy/Command/Memo";
|
|
26
27
|
import * as Clock from "effect/Clock";
|
|
27
28
|
import * as ChildProcess from "effect/process/ChildProcess";
|
|
@@ -173,6 +174,28 @@ const TeamRow = Schema.Struct({
|
|
|
173
174
|
const TeamRows = Schema.Array(TeamRow);
|
|
174
175
|
/** The first page of GET /teams/{team_id}/projects: every row carries the slug of the team. */
|
|
175
176
|
const ProjectSlugRows = Schema.Struct({ items: Schema.Array(Schema.Struct({ teamSlug: Schema.String })) });
|
|
177
|
+
/** GET /deployments/{name}: the project of the deployment. */
|
|
178
|
+
const DeploymentProject = Schema.Struct({ projectId: Schema.Number });
|
|
179
|
+
/** GET /projects/{id}: the team of the project. */
|
|
180
|
+
const ProjectTeam = Schema.Struct({ teamId: Schema.Number });
|
|
181
|
+
/**
|
|
182
|
+
* The cloud deployment that the Convex CLI selected: CONVEX_DEPLOYMENT in the environment or in
|
|
183
|
+
* `.env.local` of the working directory, as `npx convex dev` writes it (`dev:happy-animal-123`),
|
|
184
|
+
* without the type prefix. Undefined when neither has it, and for a local or anonymous deployment
|
|
185
|
+
* (`local:local-team-app`, `anonymous:anonymous-app`), which runs on this machine and is in no team.
|
|
186
|
+
*/
|
|
187
|
+
const selectedDeployment = () => {
|
|
188
|
+
const fromEnv = process.env["CONVEX_DEPLOYMENT"];
|
|
189
|
+
const value = fromEnv !== void 0 && fromEnv !== "" ? fromEnv : (() => {
|
|
190
|
+
try {
|
|
191
|
+
return parseEnv(readFileSync(".env.local", "utf8"))["CONVEX_DEPLOYMENT"];
|
|
192
|
+
} catch {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
})();
|
|
196
|
+
const name = /^(?:(?:dev|prod|preview):)?([^:]+)$/.exec(value ?? "")?.[1];
|
|
197
|
+
return name === void 0 || name.startsWith("anonymous-") ? void 0 : name;
|
|
198
|
+
};
|
|
176
199
|
/** GET /token_details: the team of a team token, or the project of a project token. */
|
|
177
200
|
const TokenDetails = Schema.Union([Schema.Struct({
|
|
178
201
|
type: Schema.Literal("teamToken"),
|
|
@@ -194,17 +217,36 @@ const makeService = (source) => {
|
|
|
194
217
|
const tokenDetails = Effect.runSync(Effect.cached(request("get token details", (c) => c.GET("/token_details")).pipe(Effect.map((body) => Schema.decodeUnknownSync(TokenDetails)(body)), Effect.catchIf((error) => error.status === 403, () => Effect.succeed(void 0)))));
|
|
195
218
|
/** The slug of the team, from the first project of its listing, or undefined for a team without one. Read once. */
|
|
196
219
|
const teamSlug = (teamId) => request("list projects", (c) => c.GET("/teams/{team_id}/projects", { params: { path: { team_id: teamId } } })).pipe(Effect.map((body) => Schema.decodeUnknownSync(ProjectSlugRows)(body).items[0]?.teamSlug));
|
|
220
|
+
const projectTokenError = (projectId) => new ConvexApiError({
|
|
221
|
+
operation: "resolve team",
|
|
222
|
+
status: 403,
|
|
223
|
+
message: `the access token belongs to project ${projectId}, and a project token cannot create projects. Use a team access token.`
|
|
224
|
+
});
|
|
225
|
+
const teamId = Effect.runSync(Effect.cached(Effect.gen(function* () {
|
|
226
|
+
const details = yield* tokenDetails;
|
|
227
|
+
if (details?.type === "teamToken") return details.teamId;
|
|
228
|
+
if (details?.type === "projectToken") return yield* projectTokenError(details.projectId);
|
|
229
|
+
const selected = selectedDeployment();
|
|
230
|
+
if (selected === void 0) return yield* new ConvexApiError({
|
|
231
|
+
operation: "resolve team",
|
|
232
|
+
status: 0,
|
|
233
|
+
message: "the Convex CLI login names no team, and no cloud deployment is selected. Run `npx convex dev` once with a cloud deployment."
|
|
234
|
+
});
|
|
235
|
+
const deployment = yield* request("get deployment", (c) => c.GET("/deployments/{deployment_name}", { params: { path: { deployment_name: selected } } }));
|
|
236
|
+
const { projectId } = Schema.decodeUnknownSync(DeploymentProject)(deployment);
|
|
237
|
+
const project = yield* request("get project", (c) => c.GET("/projects/{project_id}", { params: { path: { project_id: projectId } } }));
|
|
238
|
+
return Schema.decodeUnknownSync(ProjectTeam)(project).teamId;
|
|
239
|
+
})));
|
|
197
240
|
const resolveTeamId = (team) => Effect.gen(function* () {
|
|
198
241
|
if (typeof team === "number") return team;
|
|
199
|
-
if (
|
|
242
|
+
if (/^\d+$/.test(team)) return Number(team);
|
|
200
243
|
const details = yield* tokenDetails;
|
|
201
244
|
if (details?.type === "teamToken") {
|
|
202
|
-
if (team === void 0) return details.teamId;
|
|
203
245
|
const slug = yield* teamSlug(details.teamId);
|
|
204
246
|
if (slug === void 0) return yield* new ConvexApiError({
|
|
205
247
|
operation: "resolve team",
|
|
206
248
|
status: 0,
|
|
207
|
-
message: `team "${team}" cannot be checked against the access token: team ${details.teamId} has no project yet. Set team to ${details.teamId}
|
|
249
|
+
message: `team "${team}" cannot be checked against the access token: team ${details.teamId} has no project yet. Set team to ${details.teamId}.`
|
|
208
250
|
});
|
|
209
251
|
if (slug !== team) return yield* new ConvexApiError({
|
|
210
252
|
operation: "resolve team",
|
|
@@ -213,16 +255,7 @@ const makeService = (source) => {
|
|
|
213
255
|
});
|
|
214
256
|
return details.teamId;
|
|
215
257
|
}
|
|
216
|
-
if (details?.type === "projectToken") return yield*
|
|
217
|
-
operation: "resolve team",
|
|
218
|
-
status: 403,
|
|
219
|
-
message: `the access token belongs to project ${details.projectId}, and a project token cannot create projects. Use a team access token.`
|
|
220
|
-
});
|
|
221
|
-
if (team === void 0) return yield* new ConvexApiError({
|
|
222
|
-
operation: "resolve team",
|
|
223
|
-
status: 0,
|
|
224
|
-
message: "set team to the slug or the id of the Convex team: this access token does not name a team."
|
|
225
|
-
});
|
|
258
|
+
if (details?.type === "projectToken") return yield* projectTokenError(details.projectId);
|
|
226
259
|
const resolved = yield* credentials;
|
|
227
260
|
const match = (yield* Effect.tryPromise({
|
|
228
261
|
try: async () => {
|
|
@@ -253,6 +286,7 @@ const makeService = (source) => {
|
|
|
253
286
|
request,
|
|
254
287
|
requestVoid,
|
|
255
288
|
deploymentRequest,
|
|
289
|
+
teamId,
|
|
256
290
|
resolveTeamId,
|
|
257
291
|
isCredentialToken,
|
|
258
292
|
isOAuthCredential,
|
|
@@ -1097,14 +1131,14 @@ const DeploymentProvider = () => Provider.effect(DeploymentResource, Effect.gen(
|
|
|
1097
1131
|
//#endregion
|
|
1098
1132
|
//#region src/Project.ts
|
|
1099
1133
|
const Project = Resource("Convex.Project", { defaultRemovalPolicy: "retain" });
|
|
1100
|
-
/** The
|
|
1134
|
+
/** The credential is of another team than the project. Convex cannot move a project. */
|
|
1101
1135
|
var ProjectTeamChange = class extends Schema.TaggedError()("ProjectTeamChange", {
|
|
1102
1136
|
projectId: Schema.Number,
|
|
1103
1137
|
teamId: Schema.Number,
|
|
1104
1138
|
requestedTeamId: Schema.Number
|
|
1105
1139
|
}) {
|
|
1106
1140
|
get message() {
|
|
1107
|
-
return `Convex.Project cannot move project ${this.projectId} from team ${this.teamId} to team ${this.requestedTeamId}.
|
|
1141
|
+
return `Convex.Project cannot move project ${this.projectId} from team ${this.teamId} to team ${this.requestedTeamId}, the team of the credential. Use a token of team ${this.teamId}, or with the Convex CLI login, select a deployment of team ${this.teamId}. To use the other team, create a new resource with a new logical id and move the data yourself.`;
|
|
1108
1142
|
}
|
|
1109
1143
|
};
|
|
1110
1144
|
/**
|
|
@@ -1140,11 +1174,11 @@ var CreatedProjectNotFound = class extends Schema.TaggedError()("CreatedProjectN
|
|
|
1140
1174
|
};
|
|
1141
1175
|
/** No project in the team has the slug. A `slug` resource never creates one. */
|
|
1142
1176
|
var ProjectNotFound = class extends Schema.TaggedError()("ProjectNotFound", {
|
|
1143
|
-
|
|
1177
|
+
teamId: Schema.Number,
|
|
1144
1178
|
slug: Schema.String
|
|
1145
1179
|
}) {
|
|
1146
1180
|
get message() {
|
|
1147
|
-
return `Team ${this.
|
|
1181
|
+
return `Team ${this.teamId} 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.`;
|
|
1148
1182
|
}
|
|
1149
1183
|
};
|
|
1150
1184
|
/** The props change the slug of a `slug` resource: that is another project. */
|
|
@@ -1206,8 +1240,6 @@ const checkCreateTimeProps = (projectId, olds, news) => {
|
|
|
1206
1240
|
}));
|
|
1207
1241
|
return Effect.void;
|
|
1208
1242
|
};
|
|
1209
|
-
/** True when `team`, a slug or a numeric id, names the team of the project. */
|
|
1210
|
-
const isTeamOf = (details, team) => typeof team === "number" || /^\d+$/.test(team) ? details.teamId === Number(team) : details.teamSlug === team;
|
|
1211
1243
|
/** The row of the project's default production deployment, or undefined. */
|
|
1212
1244
|
const prodDeploymentOf = (api, projectId) => api.request("list deployments", (client) => client.GET("/projects/{project_id}/list_deployments", { params: { path: { project_id: projectId } } })).pipe(Effect.map((rows) => Schema.decodeUnknownSync(DeploymentRows)(rows).find((row) => row.deploymentType === "prod" && row.isDefault)));
|
|
1213
1245
|
/** GET /projects/{id}, or undefined after a 404. */
|
|
@@ -1281,9 +1313,9 @@ const ProjectProvider = () => Provider.effect(Project, Effect.gen(function* () {
|
|
|
1281
1313
|
prodDeploymentUrl: prod?.deploymentUrl
|
|
1282
1314
|
})));
|
|
1283
1315
|
const getById = (projectId) => getProjectById(api, projectId);
|
|
1284
|
-
/** The project with this slug in the team, or undefined.
|
|
1285
|
-
const getBySlug = (
|
|
1286
|
-
team_id_or_slug: String(
|
|
1316
|
+
/** The project with this slug in the team, or undefined. */
|
|
1317
|
+
const getBySlug = (teamId, slug) => absentAsUndefined(api.request("get project by slug", (client) => client.GET("/teams/{team_id_or_slug}/projects/{project_slug}", { params: { path: {
|
|
1318
|
+
team_id_or_slug: String(teamId),
|
|
1287
1319
|
project_slug: slug
|
|
1288
1320
|
} } }))).pipe(Effect.map((body) => body === void 0 ? void 0 : Schema.decodeUnknownSync(ProjectDetails)(body)));
|
|
1289
1321
|
const findByName = (teamId, name) => findProjectByName(api, teamId, name);
|
|
@@ -1313,45 +1345,37 @@ const ProjectProvider = () => Provider.effect(Project, Effect.gen(function* () {
|
|
|
1313
1345
|
diff: Effect.fn(function* ({ olds, news, output }) {
|
|
1314
1346
|
if (!isResolved(news)) return void 0;
|
|
1315
1347
|
if (output === void 0) return void 0;
|
|
1316
|
-
|
|
1348
|
+
const teamId = yield* api.teamId;
|
|
1349
|
+
if (teamId !== output.teamId) return yield* new ProjectTeamChange({
|
|
1350
|
+
projectId: output.projectId,
|
|
1351
|
+
teamId: output.teamId,
|
|
1352
|
+
requestedTeamId: teamId
|
|
1353
|
+
});
|
|
1354
|
+
if (news.slug !== void 0) {
|
|
1317
1355
|
if (news.slug !== output.slug) return yield* new ProjectSlugChange({
|
|
1318
1356
|
projectId: output.projectId,
|
|
1319
1357
|
slug: output.slug,
|
|
1320
1358
|
requestedSlug: news.slug
|
|
1321
1359
|
});
|
|
1322
|
-
if (olds.team !== news.team) {
|
|
1323
|
-
const details = yield* getById(output.projectId);
|
|
1324
|
-
if (details !== void 0 && !isTeamOf(details, news.team)) return yield* new ProjectTeamChange({
|
|
1325
|
-
projectId: output.projectId,
|
|
1326
|
-
teamId: output.teamId,
|
|
1327
|
-
requestedTeamId: yield* api.resolveTeamId(news.team)
|
|
1328
|
-
});
|
|
1329
|
-
}
|
|
1330
1360
|
return { action: "noop" };
|
|
1331
1361
|
}
|
|
1332
|
-
|
|
1333
|
-
const requestedTeamId = yield* api.resolveTeamId(news.team);
|
|
1334
|
-
if (requestedTeamId !== output.teamId) return yield* new ProjectTeamChange({
|
|
1335
|
-
projectId: output.projectId,
|
|
1336
|
-
teamId: output.teamId,
|
|
1337
|
-
requestedTeamId
|
|
1338
|
-
});
|
|
1339
|
-
}
|
|
1340
|
-
return "name" in olds && olds.name === news.name ? void 0 : { action: "update" };
|
|
1362
|
+
return olds.name === news.name ? void 0 : { action: "update" };
|
|
1341
1363
|
}),
|
|
1342
1364
|
read: Effect.fn(function* ({ olds, output }) {
|
|
1343
1365
|
if (output !== void 0) {
|
|
1344
1366
|
const details = yield* getById(output.projectId);
|
|
1345
1367
|
return details === void 0 ? void 0 : yield* attributesOf(details);
|
|
1346
1368
|
}
|
|
1347
|
-
const
|
|
1369
|
+
const teamId = yield* api.teamId;
|
|
1370
|
+
const existing = olds.slug !== void 0 ? yield* getBySlug(teamId, olds.slug) : yield* findByName(teamId, olds.name);
|
|
1348
1371
|
return existing === void 0 ? void 0 : Unowned(yield* attributesOf(existing));
|
|
1349
1372
|
}),
|
|
1350
1373
|
reconcile: Effect.fn(function* ({ fqn, olds, news, output }) {
|
|
1351
|
-
|
|
1352
|
-
|
|
1374
|
+
const teamId = yield* api.teamId;
|
|
1375
|
+
if (news.slug !== void 0) {
|
|
1376
|
+
const details = output === void 0 ? yield* getBySlug(teamId, news.slug) : yield* getById(output.projectId);
|
|
1353
1377
|
if (details === void 0) return yield* new ProjectNotFound({
|
|
1354
|
-
|
|
1378
|
+
teamId,
|
|
1355
1379
|
slug: news.slug
|
|
1356
1380
|
});
|
|
1357
1381
|
if (details.slug !== news.slug) return yield* new ProjectSlugChange({
|
|
@@ -1359,10 +1383,10 @@ const ProjectProvider = () => Provider.effect(Project, Effect.gen(function* () {
|
|
|
1359
1383
|
slug: details.slug,
|
|
1360
1384
|
requestedSlug: news.slug
|
|
1361
1385
|
});
|
|
1362
|
-
if (
|
|
1386
|
+
if (details.teamId !== teamId) return yield* new ProjectTeamChange({
|
|
1363
1387
|
projectId: details.id,
|
|
1364
1388
|
teamId: details.teamId,
|
|
1365
|
-
requestedTeamId:
|
|
1389
|
+
requestedTeamId: teamId
|
|
1366
1390
|
});
|
|
1367
1391
|
if (output === void 0 && !(yield* shouldAdopt(fqn))) return yield* new OwnedBySomeoneElse({
|
|
1368
1392
|
message: `Convex project ${details.id} ("${news.slug}") exists and is not in this stack's state. Re-run with --adopt to take it over.`,
|
|
@@ -1371,9 +1395,8 @@ const ProjectProvider = () => Provider.effect(Project, Effect.gen(function* () {
|
|
|
1371
1395
|
});
|
|
1372
1396
|
return yield* attributesOf(details);
|
|
1373
1397
|
}
|
|
1374
|
-
const teamId = yield* api.resolveTeamId(news.team);
|
|
1375
1398
|
if (output !== void 0) {
|
|
1376
|
-
if (olds !== void 0 &&
|
|
1399
|
+
if (olds !== void 0 && olds.name !== void 0) yield* checkCreateTimeProps(output.projectId, olds, news);
|
|
1377
1400
|
if (output.teamId !== teamId) return yield* new ProjectTeamChange({
|
|
1378
1401
|
projectId: output.projectId,
|
|
1379
1402
|
teamId: output.teamId,
|
|
@@ -1715,8 +1738,8 @@ const ensureSealed = (options, deployment, functions) => Effect.gen(function* ()
|
|
|
1715
1738
|
* the run whose project is not the oldest deletes its own, which holds
|
|
1716
1739
|
* nothing yet, and uses the oldest, so that every run ends on one store.
|
|
1717
1740
|
*/
|
|
1718
|
-
const createStoreProject = (api, teamId
|
|
1719
|
-
yield* Effect.logInfo(`Convex state: creating the project ${STORE_PROJECT_NAME} in team ${
|
|
1741
|
+
const createStoreProject = (api, teamId) => Effect.gen(function* () {
|
|
1742
|
+
yield* Effect.logInfo(`Convex state: creating the project ${STORE_PROJECT_NAME} in team ${teamId}`);
|
|
1720
1743
|
const created = yield* createProject(api, teamId, {
|
|
1721
1744
|
name: STORE_PROJECT_NAME,
|
|
1722
1745
|
deploymentType: "prod",
|
|
@@ -1735,9 +1758,8 @@ const createStoreProject = (api, teamId, team) => Effect.gen(function* () {
|
|
|
1735
1758
|
*/
|
|
1736
1759
|
const makeConvexState = (options) => Effect.gen(function* () {
|
|
1737
1760
|
const { api } = options;
|
|
1738
|
-
const teamId = yield* api.
|
|
1739
|
-
const
|
|
1740
|
-
const project = (yield* findProjectByName(api, teamId, "alchemy-state-store")) ?? (yield* createStoreProject(api, teamId, team));
|
|
1761
|
+
const teamId = yield* api.teamId;
|
|
1762
|
+
const project = (yield* findProjectByName(api, teamId, "alchemy-state-store")) ?? (yield* createStoreProject(api, teamId));
|
|
1741
1763
|
const deployment = yield* prodDeploymentOf(api, project.id);
|
|
1742
1764
|
if (deployment === void 0) return yield* new ProductionDeploymentNotFound({ projectId: project.id });
|
|
1743
1765
|
const keys = [];
|
|
@@ -1753,7 +1775,7 @@ const makeConvexState = (options) => Effect.gen(function* () {
|
|
|
1753
1775
|
})))) };
|
|
1754
1776
|
let version = yield* storeVersion(functions);
|
|
1755
1777
|
if (version === void 0 || version < 5) {
|
|
1756
|
-
if (version === void 0 && deployment.lastDeployTime) return yield* failure(`project ${STORE_PROJECT_NAME} (${project.id}) of team ${
|
|
1778
|
+
if (version === void 0 && deployment.lastDeployTime) return yield* failure(`project ${STORE_PROJECT_NAME} (${project.id}) of team ${teamId} has code that is not the state store, and Convex.state does not push over it. Rename that project in the Convex dashboard.`);
|
|
1757
1779
|
version = yield* storeVersion(functions);
|
|
1758
1780
|
if (version === void 0 || version < 5) {
|
|
1759
1781
|
yield* pushStore(options, deployment);
|
|
@@ -1802,14 +1824,13 @@ const makeConvexState = (options) => Effect.gen(function* () {
|
|
|
1802
1824
|
};
|
|
1803
1825
|
});
|
|
1804
1826
|
/**
|
|
1805
|
-
* Alchemy state in the `alchemy-state-store` project of
|
|
1806
|
-
* in CONVEX_ACCESS_TOKEN or the Convex CLI login
|
|
1807
|
-
*
|
|
1808
|
-
*
|
|
1809
|
-
*
|
|
1810
|
-
* stack never touches Convex.
|
|
1827
|
+
* Alchemy state in the `alchemy-state-store` project of the team of the credential: the team token
|
|
1828
|
+
* in CONVEX_ACCESS_TOKEN, which names its team, or the Convex CLI login, with the team of the
|
|
1829
|
+
* deployment that the CLI selected (CONVEX_DEPLOYMENT, see ManagementApi.teamId). The first use of
|
|
1830
|
+
* the run finds or creates the project and pushes the store when it is missing or older.
|
|
1831
|
+
* `alchemy plan` on another stack never touches Convex.
|
|
1811
1832
|
*/
|
|
1812
|
-
const state = (
|
|
1833
|
+
const state = () => Layer.effect(State, Effect.gen(function* () {
|
|
1813
1834
|
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
1814
1835
|
const scope = yield* Scope.Scope;
|
|
1815
1836
|
const context = yield* Effect.context();
|
|
@@ -1820,7 +1841,6 @@ const state = (options = {}) => Layer.effect(State, Effect.gen(function* () {
|
|
|
1820
1841
|
env: process.env
|
|
1821
1842
|
}).pipe(Effect.catch((error) => Effect.logWarning(`Convex state: the stage outputs of this run carry no record. ${error.message}`).pipe(Effect.as(void 0))));
|
|
1822
1843
|
return yield* makeConvexState({
|
|
1823
|
-
team: options.team,
|
|
1824
1844
|
api,
|
|
1825
1845
|
spawner,
|
|
1826
1846
|
scope,
|
|
@@ -1992,4 +2012,4 @@ const registerDevelopmentClient = (options) => Effect.gen(function* () {
|
|
|
1992
2012
|
//#endregion
|
|
1993
2013
|
export { PushTargetMismatch as A, DEPLOY_KEY_ENV as B, DeploymentProvider as C, ConvexAppNotFound as D, CodePushFailed as E, ManagementApi as F, fromValues as H, ManagementApiLive as I, ACCESS_TOKEN_ENV as L, installedCli as M, parseDeployOutput as N, ConvexCliNotFound as O, ConvexApiError as P, Credentials as R, DeploymentIdentityChange as S, ProductionDeploymentNotFound as T, fromConfig as V, ProjectProvider as _, registerDevelopmentClient as a, AmbiguousDeployment as b, RUN_RECORD_KEY as c, withRunRecord as d, AmbiguousProject as f, ProjectNotFound as g, ProjectDeploymentChange as h, HubClientProvider as i, VariablesOwnedBySomeoneElse as j, EnvironmentNeedsDeployKey as k, RunRecordError as l, Project as m, HubClient as n, STORE_PROJECT_NAME as o, CreatedProjectNotFound as p, HubClientFailed as r, state as s, HUB_PROJECT_NAME as t, runRecord as u, ProjectTeamChange as v, DeploymentResource as w, Deployment as x, AbsoluteRootDir as y, CredentialsError as z };
|
|
1994
2014
|
|
|
1995
|
-
//# sourceMappingURL=HubClient-
|
|
2015
|
+
//# sourceMappingURL=HubClient-HeGoVOLO.js.map
|