@samebase/alchemy-convex 0.1.0 → 0.3.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 (54) hide show
  1. package/README.md +246 -55
  2. package/lib/index.d.ts +510 -9
  3. package/lib/index.d.ts.map +1 -1
  4. package/lib/index.js +1411 -10
  5. package/lib/index.js.map +1 -1
  6. package/package.json +8 -5
  7. package/src/Adoption.ts +30 -0
  8. package/src/Code.ts +463 -0
  9. package/src/DefaultEnvironmentVariable.ts +127 -29
  10. package/src/Deploy.ts +47 -35
  11. package/src/DeployKey.ts +221 -60
  12. package/src/Deployment.ts +409 -0
  13. package/src/EnvironmentVariable.ts +204 -44
  14. package/src/ManagementApi.ts +57 -1
  15. package/src/PreviewDeployKey.ts +65 -38
  16. package/src/Project.ts +236 -52
  17. package/src/Providers.ts +9 -1
  18. package/src/index.ts +29 -1
  19. package/lib/Credentials.d.ts +0 -39
  20. package/lib/Credentials.d.ts.map +0 -1
  21. package/lib/Credentials.js +0 -49
  22. package/lib/Credentials.js.map +0 -1
  23. package/lib/DefaultEnvironmentVariable.d.ts +0 -58
  24. package/lib/DefaultEnvironmentVariable.d.ts.map +0 -1
  25. package/lib/DefaultEnvironmentVariable.js +0 -93
  26. package/lib/DefaultEnvironmentVariable.js.map +0 -1
  27. package/lib/Deploy.d.ts +0 -62
  28. package/lib/Deploy.d.ts.map +0 -1
  29. package/lib/Deploy.js +0 -92
  30. package/lib/Deploy.js.map +0 -1
  31. package/lib/DeployKey.d.ts +0 -48
  32. package/lib/DeployKey.d.ts.map +0 -1
  33. package/lib/DeployKey.js +0 -104
  34. package/lib/DeployKey.js.map +0 -1
  35. package/lib/EnvironmentVariable.d.ts +0 -37
  36. package/lib/EnvironmentVariable.d.ts.map +0 -1
  37. package/lib/EnvironmentVariable.js +0 -65
  38. package/lib/EnvironmentVariable.js.map +0 -1
  39. package/lib/ManagementApi.d.ts +0 -50
  40. package/lib/ManagementApi.d.ts.map +0 -1
  41. package/lib/ManagementApi.js +0 -103
  42. package/lib/ManagementApi.js.map +0 -1
  43. package/lib/PreviewDeployKey.d.ts +0 -34
  44. package/lib/PreviewDeployKey.d.ts.map +0 -1
  45. package/lib/PreviewDeployKey.js +0 -78
  46. package/lib/PreviewDeployKey.js.map +0 -1
  47. package/lib/Project.d.ts +0 -29
  48. package/lib/Project.d.ts.map +0 -1
  49. package/lib/Project.js +0 -114
  50. package/lib/Project.js.map +0 -1
  51. package/lib/Providers.d.ts +0 -13
  52. package/lib/Providers.d.ts.map +0 -1
  53. package/lib/Providers.js +0 -30
  54. package/lib/Providers.js.map +0 -1
package/README.md CHANGED
@@ -1,17 +1,20 @@
1
1
  # @samebase/alchemy-convex
2
2
 
3
- Convex resources for [Alchemy v2](https://alchemy.run): projects, deploy keys, environment
4
- variables, project default environment variables, and a deploy step that hands the deployment URL
5
- to your frontend build. The Convex CLI keeps doing typecheck, codegen, bundling, and the code push.
6
- This package manages the control plane around it.
3
+ Convex resources for [Alchemy v2](https://alchemy.run): projects, deployments, deploy keys,
4
+ environment variables, project default environment variables, and the code push. One Alchemy stack
5
+ can own a Convex app end to end: it creates the deployment, pushes the functions, and gives the
6
+ deployment URLs to a Cloudflare Worker or a Vite site as ordinary outputs. The Convex CLI that
7
+ your app installs keeps doing typecheck, codegen, bundling, and the push.
7
8
 
8
9
  Alchemy has no built-in Convex provider ([alchemy-run/alchemy-async#1306](https://github.com/alchemy-run/alchemy-async/issues/1306)).
9
10
  This is a community provider, maintained by [Samebase](https://samebase.com).
10
11
 
11
- Status: 0.1, pinned to `alchemy@2.0.0-beta.80` and Effect 4. Alchemy ships breaking changes
12
- between betas; upgrade this package and Alchemy together. Verified end to end on a Vite app with a
13
- Cloudflare Worker: production on `main`, a Convex preview deployment plus a Worker Preview per pull
14
- request, destroy on close.
12
+ Status: 0.3, pinned to `alchemy@2.0.0-beta.80` and Effect 4. Alchemy ships breaking changes
13
+ between betas; upgrade this package and Alchemy together. The 0.2 deploy step was verified end to
14
+ end on a Vite app with a Cloudflare Worker: production on `main`, a Convex preview deployment plus
15
+ a Worker Preview per pull request, destroy on close. `Convex.Deployment` and `Convex.Code` are
16
+ verified by a live test with the real Convex CLI: a push to a production deployment and to a
17
+ preview deployment, then a delete of the preview.
15
18
 
16
19
  ## Install
17
20
 
@@ -52,69 +55,228 @@ export default Alchemy.Stack(
52
55
  state: Cloudflare.state(),
53
56
  },
54
57
  Effect.gen(function* () {
55
- const project = yield* Convex.Project("Backend", {
56
- team: "my-team",
57
- name: "my-app",
58
- });
59
-
60
- const deployKey = yield* Convex.DeployKey("DeployKey", {
61
- deployment: project.prodDeploymentName.as<string>(),
58
+ const project = yield* Convex.Project("Project", { team: 38516, name: "score-four" });
59
+ const prod = yield* Convex.Deployment("Prod", { projectId: project.projectId, type: "prod" });
60
+ const key = yield* Convex.DeployKey("DeployKey", {
61
+ deployment: prod.name,
62
62
  name: "alchemy",
63
+ allowedActions: ["deployment:deploy", "deployment:env:view", "deployment:env:write"],
63
64
  });
64
-
65
- const backend = yield* Convex.Deploy("Backend", {
65
+ const backend = yield* Convex.Code("Backend", {
66
+ deployment: prod,
67
+ deployKey: key.deployKey,
66
68
  cwd: ".",
67
- deployKey: deployKey.deployKey,
68
69
  });
69
70
 
70
- const site = yield* Cloudflare.Website.StaticSite("Website", {
71
- command: "npm run build",
72
- outdir: "dist/client",
73
- env: { VITE_CONVEX_URL: backend.url },
74
- assets: { notFoundHandling: "single-page-application" },
71
+ // backend.url and backend.siteUrl feed a Cloudflare Worker or a Vite site.
72
+ const site = yield* Cloudflare.Website.Vite("Site", {
73
+ env: { VITE_CONVEX_URL: backend.url, VITE_CONVEX_SITE_URL: backend.siteUrl },
75
74
  });
76
75
 
77
- yield* Convex.EnvironmentVariable("SiteUrl", {
78
- deployment: backend.deploymentName,
79
- deployKey: deployKey.deployKey,
80
- name: "SITE_URL",
81
- value: site.url,
82
- });
83
-
84
- return { url: site.url, backendUrl: backend.url };
76
+ return { url: site.url, convexUrl: backend.url };
85
77
  }),
86
78
  );
87
79
  ```
88
80
 
89
- Order follows the outputs: the project exists before its key, the key before the deploy, the
90
- deploy before the frontend build that embeds the URL.
81
+ Order follows the outputs: the project exists before its deployment, the deployment before its key,
82
+ the key before the push, and the push before the site build. On a Vite Worker, a `VITE_` key in
83
+ `env` goes into the client bundle as `import.meta.env.VITE_*` and is also a Worker variable.
84
+ `Cloudflare.Worker` takes the same `env`.
85
+
86
+ ### Preview stages
87
+
88
+ On a pull request stage, push to a preview deployment of the same project. Name the preview after
89
+ the stage, and use a preview deploy key of the project:
90
+
91
+ ```ts
92
+ const stack = yield * Alchemy.Stack; // stack.stage, such as "pr-42"
93
+ // The project that the stage "prod" owns. A preview stage reads it and does not own it.
94
+ const project = yield * Convex.Project.ref("Project", { stage: "prod" });
95
+ const preview =
96
+ yield *
97
+ Convex.Deployment("Preview", {
98
+ projectId: project.projectId,
99
+ type: "preview",
100
+ name: stack.stage,
101
+ }).pipe(Alchemy.RemovalPolicy.destroy());
102
+ const previewKey =
103
+ yield *
104
+ Convex.PreviewDeployKey("PreviewDeployKey", {
105
+ projectId: project.projectId,
106
+ name: "alchemy",
107
+ });
108
+ const backend =
109
+ yield *
110
+ Convex.Code("Backend", {
111
+ deployment: preview,
112
+ deployKey: previewKey.previewDeployKey,
113
+ cwd: ".",
114
+ });
115
+ ```
116
+
117
+ `alchemy destroy --stage pr-42` then deletes the preview deployment. Without
118
+ `RemovalPolicy.destroy()`, the preview deployment stays until Convex deletes it at expiry (14 days
119
+ by default). [`examples/app/alchemy.run.ts`](examples/app/alchemy.run.ts) has both stages in one
120
+ stack. Variables that every preview deployment needs, such as auth keys, go on the project as
121
+ `DefaultEnvironmentVariable` for `preview`.
91
122
 
92
- Preview stages: give `Convex.Deploy` a `PreviewDeployKey` and `previewName: <branch>` on pull
93
- request stages, and the production key on `prod`. Variables that every preview deployment needs,
94
- such as auth keys, go on the project as `DefaultEnvironmentVariable` for `preview`.
123
+ ## Data safety
124
+
125
+ Deleting a Convex project deletes all of its deployments and data. This package does not delete,
126
+ replace, or overwrite your data by accident:
127
+
128
+ - `Convex.Project` has the removal policy `retain`. When you remove the resource or run
129
+ `alchemy destroy`, Alchemy forgets the project and Convex keeps it. To delete the project with
130
+ the resource, add `RemovalPolicy.destroy()`:
131
+
132
+ ```ts
133
+ yield * Convex.Project("Project", { team, name }).pipe(Alchemy.RemovalPolicy.destroy());
134
+ ```
135
+
136
+ - A project is never replaced. A change to `name` renames the project in place
137
+ (`PATCH /projects/{project_id}`). The id, the slug, the deployments, and the data stay.
138
+ - A change to `team`, `deploymentType`, or `deploymentRegion` stops the deploy with
139
+ `ProjectTeamChange` or `ProjectDeploymentChange`. Convex cannot move a project or change its
140
+ first deployment. Set the old value again, or create a new resource with a new logical id and
141
+ move the data yourself.
142
+ - With state, the provider finds the project only by the id in state. Without state, it finds a
143
+ project by its exact name, on all pages of the team's project list, and takes it over only with
144
+ `--adopt`. When two projects have that name, the deploy stops with `AmbiguousProject`.
145
+ - Each `DeployKey` and `PreviewDeployKey` requests a name that is unique to the resource: the
146
+ `name` prop, a dash, and a hash of the resource identity. The provider deletes a key only by its
147
+ secret, so it cannot revoke the key of another resource. When the key list does not show
148
+ exactly one key with the requested name, the deploy stops with `DeployKeyRecoveryRequired`,
149
+ which lists the key ids. The provider never revokes a key on a failed create.
150
+ - With an OAuth token as the credential, Convex creates no new key: it returns the OAuth token
151
+ with a new prefix. The key resources then stop with `DeployKeyIsCredential` and do not keep that
152
+ value, because a delete would revoke the OAuth token. Use a team access token or the Convex CLI
153
+ login.
154
+ - `Convex.Deployment` has the removal policy `retain` for every type. It never deletes a
155
+ production deployment, also not with `RemovalPolicy.destroy()` and also not a dev deployment that
156
+ became a production deployment: delete the project for that. A
157
+ dev or preview deployment is deleted only with `RemovalPolicy.destroy()`.
158
+ - `Convex.Deployment` takes over the project's production deployment without `--adopt`: Convex
159
+ creates it with the project, and the resource never changes or deletes it. A dev or preview
160
+ deployment that exists but is not in state belongs to someone else, such as a preview that CI
161
+ made with `npx convex deploy --preview-name`. The resource takes it over only with `--adopt`.
162
+ It never creates over it: a Convex create for an existing preview name deletes that preview.
163
+ - A deployment keeps its `projectId`, `type`, and `name`. A change stops the deploy with
164
+ `DeploymentIdentityChange`. The resource is never replaced.
165
+ - `Convex.Code` checks before the push that the deploy key fits the deployment, and after the push
166
+ that the CLI pushed to that deployment. A production deploy key on a preview stage stops the deploy
167
+ before the push.
168
+ - An `EnvironmentVariable` or a `DefaultEnvironmentVariable` that exists but is not in state
169
+ belongs to someone else. The provider overwrites it only with `--adopt`. The same is true for a
170
+ `Convex.Code` `env` variable that exists with another value.
171
+ - A variable keeps its name and deployment (a default keeps its project, name, and deployment
172
+ type). A change stops the deploy with `VariableIdentityChange`. To rename or move a variable,
173
+ create a new resource with a new logical id and remove the old one. A rename in place would
174
+ need a cleanup step that can fail and leave a variable or a revoked deploy key behind.
175
+ - A variable write runs again after a Convex write conflict or a 5xx answer, at most four more
176
+ times. The write sets a value, so a second write has the same result.
177
+
178
+ Upgrade from 0.2: `Convex.Deploy` keeps working. To move to `Convex.Code`, add a
179
+ `Convex.Deployment` and a `Convex.Code` with a new logical id and remove the `Convex.Deploy`.
180
+ Removing `Convex.Deploy` changes nothing in Convex. For a preview stage, use `previewName`, not
181
+ `previewCreate`, while you still use `Convex.Deploy`.
182
+
183
+ Upgrade from 0.1: state from 0.1 records the policy `destroy` for each project. Run
184
+ `alchemy deploy` one time with 0.2 while the stack still declares the project. That deploy records
185
+ `retain`. Do this before you remove a project from the stack or run `alchemy destroy`.
186
+ Deploy keys from 0.1 state keep working, but 0.2 does not delete them: their state cannot show that
187
+ the secret is a new key and not an OAuth token. When you remove or replace such a key, delete it in
188
+ the Convex dashboard.
95
189
 
96
190
  ## Resources
97
191
 
98
192
  - `Convex.Project`: `{ team: slug | id, name, deploymentRegion?, deploymentType? }`. Creates a
99
193
  project with its default deployment. Outputs `projectId`, `slug`, `teamId`,
100
- `prodDeploymentName`, `prodDeploymentUrl`. There is no project update API, so a changed property
101
- replaces the project. An existing project with the same name is adopted only with `--adopt`.
102
- Deleting the resource deletes the project with all of its deployments and data.
103
- - `Convex.DeployKey`: `{ deployment, name, allowedActions? }`. Outputs `uniqueName` and the
104
- `deployKey` secret. Any change replaces the key. Keys are never adopted: a key without its secret
105
- is useless.
194
+ `prodDeploymentName`, `prodDeploymentUrl`. A changed `name` renames the project in place. A
195
+ changed `team`, `deploymentRegion`, or `deploymentType` stops the deploy. An existing project
196
+ with the same name is adopted only with `--adopt`. The removal policy is `retain`: only a
197
+ resource with `RemovalPolicy.destroy()` deletes the project with its deployments and data.
198
+ - `Convex.Deployment`: `{ projectId, type: "prod" }` or
199
+ `{ projectId, type: "dev" | "preview", name }`. Outputs `name` (such as `happy-animal-123`),
200
+ `url` (`https://<name>.convex.cloud`, with the region outside US East), `siteUrl` (the same
201
+ host on `.convex.site`), `type`, `projectId`, `reference`, and `previewName`.
202
+ - `prod` takes over the project's default production deployment, and follows it when another
203
+ production deployment becomes the default. It never creates one: a project without one stops
204
+ the deploy with `ProductionDeploymentNotFound`.
205
+ - `dev` creates a dev deployment with `name` as its reference, through
206
+ `POST /projects/{project_id}/create_deployment`. Convex records the member of the access token
207
+ as its creator. It is not that member's default dev deployment, so `npx convex dev` does not
208
+ select it.
209
+ - `preview` creates a preview deployment with `name` as its preview name, through the same
210
+ call. `npx convex deploy --preview-name <name>` with a preview deploy key finds it.
211
+ - When the deployment in state is gone, such as an expired preview, the next deploy creates it
212
+ again. Two deployments with the name stop the deploy with `AmbiguousDeployment`.
213
+ - `Convex.Code`: `{ deployment, deployKey, cwd, command?, env?, typecheck?, codegen? }`. Pushes
214
+ the functions of the Convex app in `cwd` with `convex deploy`. Outputs `deploymentName`,
215
+ `url`, `siteUrl`, `deployedAt` (milliseconds since the Unix epoch), and `envNames`.
216
+ - `deployment` is a `Convex.Deployment` resource or a deployment name. `deployKey` is a
217
+ deploy key of that deployment, or, for a preview deployment, a preview deploy key of the
218
+ project: Code then runs `convex deploy --preview-name <name>`. Any other key stops the deploy
219
+ with `DeployKeyMismatch` before the push.
220
+ - Code pushes on every deploy. It does not hash the sources: a preview can expire, a push from
221
+ outside Alchemy can change the deployment, and a hash misses workspace packages. The CLI
222
+ uploads only the modules that changed.
223
+ - Code runs the `convex` package that `cwd` resolves, with the Node.js that runs Alchemy, and
224
+ never downloads one (`ConvexCliNotFound`). `command`, such as `["pnpm", "exec", "convex"]`,
225
+ replaces it.
226
+ - The key goes to the CLI only in an env file with mode 0600 (`--env-file`), in a temporary
227
+ directory that is removed after the run, also after a failure or an interruption. That
228
+ directory is the home directory of the CLI, so the CLI cannot use the Convex login of the
229
+ machine. Variables named `CONVEX_*` are not passed on.
230
+ - The CLI output stays out of the logs. A failure (`CodePushFailed`) shows the last 20 lines
231
+ with the key removed. A push to another deployment stops the deploy with
232
+ `PushTargetMismatch`.
233
+ - `env` sets variables on the deployment before the push, with the deploy key. A name that
234
+ leaves `env` is removed from the deployment. A preview deploy key cannot set variables
235
+ (`EnvironmentNeedsDeployKey`).
236
+ - `typecheck` (`enable`, `try`, `disable`) and `codegen` (`enable`, `disable`) go to
237
+ `convex deploy` as flags.
238
+ - Deleting the resource does nothing. The functions and the `env` variables stay: the deployment
239
+ runs them.
240
+ - `Convex.DeployKey`: `{ deployment, name, allowedActions? }`. Outputs `uniqueName`, `keyId`, and
241
+ the `deployKey` secret. Any change replaces the key. Delete revokes the key by its secret; a key
242
+ from 0.1 state stays. Keys are never adopted: a key without its secret is useless. The live test
243
+ pushes with `Convex.Code` and sets `env` with a key that has only `deployment:deploy`,
244
+ `deployment:env:view`, and `deployment:env:write`.
106
245
  - `Convex.PreviewDeployKey`: `{ projectId, name }`. Project-level key for preview deployments.
107
- Outputs `uniqueName` and `previewDeployKey`.
246
+ Outputs `uniqueName`, `keyId`, and `previewDeployKey`.
108
247
  - `Convex.EnvironmentVariable`: `{ deployment, deployKey, name, value }`. One variable on one
109
248
  deployment, set through the deployment API with that deployment's key. An existing variable with
110
- the same name is adopted and overwritten.
249
+ the same name is adopted and overwritten only with `--adopt`. A new `value` or `deployKey` is an
250
+ update in place. A new `name` or `deployment` stops the deploy with `VariableIdentityChange`:
251
+ use a new logical id. Outputs `deployment`, `name`, and `deployKey`. Read and delete use the
252
+ deploy key in the outputs, not the props.
111
253
  - `Convex.DefaultEnvironmentVariable`: `{ projectId, name, value, deploymentType }`. A project
112
- default that new deployments of that type inherit. Use it for preview and dev deployments.
113
- - `Convex.Deploy`: `{ cwd?, env?, deployKey, previewName?, previewCreate?, previewRun?, extraArgs?,
114
- memo?, timeout? }`. Runs `npx convex deploy` with `CONVEX_DEPLOY_KEY` set and parses the
115
- deployment URL from the CLI output. Outputs `url`, `deploymentName`, `hash`. Memoized by content
116
- hash like Alchemy's own build steps. Deleting the resource does nothing; the deployment belongs to
117
- the project, and preview deployments expire on Convex's schedule.
254
+ default that new deployments of that type inherit. Use it for preview and dev deployments. An
255
+ existing default with the same name and type is adopted and overwritten only with `--adopt`. A
256
+ new `value` is an update in place. A new `projectId`, `name`, or `deploymentType` stops the
257
+ deploy with `VariableIdentityChange`: use a new logical id.
258
+ - `Convex.Deploy` (deprecated since 0.3, use `Convex.Code`): `{ cwd?, env?, deployKey,
259
+ previewName?, previewCreate?, previewRun?, extraArgs?, memo?, timeout? }`. Runs
260
+ `npx convex deploy` with `CONVEX_DEPLOY_KEY` set and parses the deployment URL from the CLI
261
+ output. Outputs `url`, `deploymentName`, `hash`. It stays so that stacks and state from 0.1
262
+ and 0.2 keep working. Deleting the resource does nothing.
263
+
264
+ ### Which Convex call each operation uses
265
+
266
+ | Operation | Path |
267
+ | ---------------------------------------- | ----------------------------------------------------------------------------------- |
268
+ | Find the production deployment | `GET /projects/{project_id}/list_deployments?deploymentType=prod&isDefault=true` |
269
+ | Find a dev or preview deployment by name | `GET /projects/{project_id}/list_deployments?deploymentType=<type>` |
270
+ | Create a dev or preview deployment | `POST /projects/{project_id}/create_deployment` with `type` and `reference` |
271
+ | Check that a deployment exists | `GET /deployments/{deployment_name}` |
272
+ | Delete a dev or preview deployment | `POST /deployments/{deployment_name}/delete` |
273
+ | Push to a deployment | `convex deploy --env-file <file>` with a deploy key of the deployment |
274
+ | Push to a preview deployment | `convex deploy --preview-name <name> --env-file <file>` with a preview deploy key |
275
+ | Set `env` variables | `POST https://<deployment>/api/v1/update_environment_variables` with the deploy key |
276
+
277
+ Code uses `--preview-name`, not `--preview-create`: `--preview-create` deletes and creates the
278
+ preview deployment again on every push, so every deploy would lose the preview data and change its
279
+ URL.
118
280
 
119
281
  ## Limits
120
282
 
@@ -123,24 +285,53 @@ memo?, timeout? }`. Runs `npx convex deploy` with `CONVEX_DEPLOY_KEY` set and pa
123
285
  - Deploy keys and other secret outputs are part of Alchemy state. Alchemy's Cloudflare state store
124
286
  encrypts state at rest; the local file store does not.
125
287
  - A deleted deploy key keeps working on the deployment API for roughly 30 seconds.
126
- - Not covered: dev deployments, custom domains, log streams, teams and members, components,
127
- snapshots.
288
+ - A run that stops between a key create and the state write leaves a key that state does not
289
+ know. The next run stops with `DeployKeyRecoveryRequired`. Delete the listed key in the Convex
290
+ dashboard, then run the deploy again.
291
+ - A run that stops between a deployment create and the state write leaves a dev or preview
292
+ deployment that state does not know. The next run stops with `OwnedBySomeoneElse`. Run it with
293
+ `--adopt` to take that deployment over.
294
+ - Between the lookup and the create of a preview deployment, another writer can create a preview
295
+ with the same name. The Convex create then replaces it. The Management API has no create that
296
+ fails for an existing preview name.
297
+ - `siteUrl` is the `.convex.site` URL. A custom domain is not used.
298
+ - When another production deployment becomes the default, a `DeployKey` with `deployment: prod.name`
299
+ gets a new key on the new deployment. Alchemy plans this as an update, not a replacement, so the
300
+ key on the old deployment stays. Delete it in the Convex dashboard.
301
+ - Variables that `Convex.Code` set on a deployment stay there when `deployment` changes to
302
+ another deployment, and when the resource is removed.
303
+ - Not covered: deployment regions and classes, custom domains, log streams, teams and members,
304
+ components, snapshots.
128
305
 
129
306
  ## Development
130
307
 
131
308
  ```sh
132
309
  pnpm install
133
310
  pnpm run check # format, lint, typecheck, unit tests
134
- pnpm run build # lib/ with declarations
311
+ pnpm run build # lib/ with declarations, then publint and attw
135
312
  pnpm run test:live # real API calls, see below
136
313
  ```
137
314
 
315
+ Use Node 24 (`.node-version`) and pnpm 11. `pnpm install` also installs a pre-commit hook that
316
+ runs `vp staged`.
317
+
138
318
  Unit tests run against recorded Management API payloads in `test/fixtures/management/`. Live tests
139
- need `ALCHEMY_CONVEX_LIVE=1`, a Convex login or `CONVEX_ACCESS_TOKEN`, and a throwaway project.
319
+ run only with `ALCHEMY_CONVEX_LIVE=1`, which `pnpm run test:live` sets. They need a Convex login or
320
+ `CONVEX_ACCESS_TOKEN`, and a throwaway project.
140
321
  Point them at yours with `ALCHEMY_CONVEX_LIVE_TEAM`, `ALCHEMY_CONVEX_LIVE_PROJECT_ID`,
141
322
  `ALCHEMY_CONVEX_LIVE_DEPLOYMENT`, and `ALCHEMY_CONVEX_LIVE_PROJECT_DIR` (a Convex app directory for
142
323
  the deploy test). They create resources named `tmp-alchemy-convex-*` and remove them again.
324
+ A file argument runs one live file only, for example
325
+ `pnpm run test:live test/live/safety.live.test.ts`. That test runs the data safety contract
326
+ through the Alchemy engine. It creates a project `tmp-alchemy-convex-<8 hex>` in
327
+ `ALCHEMY_CONVEX_LIVE_TEAM` and deletes it at the end, also after a failure. It refuses to delete a
328
+ project whose name does not start with `tmp-alchemy-convex-`.
329
+ `test/live/deployment-code.live.test.ts` does the same for `Convex.Deployment` and
330
+ `Convex.Code`: it pushes the app in `test/fixtures/convex-app` with the real Convex CLI to the
331
+ production deployment and to a preview deployment, and it also refuses to delete a deployment of
332
+ another project.
143
333
 
144
- Releases: push a `v*` tag; `.github/workflows/release.yml` publishes with npm trusted publishing.
334
+ Releases: change `version` in `package.json` in a pull request. After the merge,
335
+ `.github/workflows/release.yml` publishes that version from `main` with npm trusted publishing.
145
336
 
146
337
  License: Apache 2.0.