@samebase/alchemy-convex 0.2.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 ADDED
@@ -0,0 +1,24 @@
1
+ @samebase/alchemy-convex
2
+ Copyright 2026 Samebase
3
+
4
+ This product follows patterns from Confect (https://github.com/rjdellecese/confect),
5
+ packages/alchemy, under the ISC license:
6
+
7
+ - the adoption check order in src/Adoption.ts (internal/Lifecycle.ts),
8
+ - the Convex CLI runner in src/Push.ts (internal/CommandRunner.ts),
9
+ - the deployment lookup rules in src/Deployment.ts and the project listing in src/Project.ts.
10
+
11
+ ISC License
12
+
13
+ Copyright 2024 RJ Dellecese
14
+
15
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without
16
+ fee is hereby granted, provided that the above copyright notice and this permission notice appear
17
+ in all copies.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
20
+ SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
21
+ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
23
+ NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
24
+ THIS SOFTWARE.
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
+ The Convex plane for [Alchemy v2](https://alchemy.run): projects, deployments, their environment
4
+ variables, and the push of the functions. One Alchemy stack owns a Convex app end to end. The
5
+ deployment resource pushes your `convex/` folder when its content changes and gives the deployment
6
+ URLs to a Cloudflare Worker or a Vite site as ordinary outputs. No deploy key goes from one resource
7
+ to another. The Convex CLI that your app installs keeps doing typecheck, codegen, bundling, and the
8
+ push.
7
9
 
8
10
  Alchemy has no built-in Convex provider ([alchemy-run/alchemy-async#1306](https://github.com/alchemy-run/alchemy-async/issues/1306)).
9
- This is a community provider, maintained by [Samebase](https://samebase.com).
11
+ This is a community provider, maintained by [Samebase](https://samebase.com). The resource model
12
+ follows the "Convex Plain" mode of [rjdellecese/confect#408](https://github.com/rjdellecese/confect/issues/408).
13
+ [docs/design/convex-plane.md](docs/design/convex-plane.md) records the decisions.
10
14
 
11
- Status: 0.2, 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.
15
+ Status: pinned to `alchemy@2.0.0-beta.80` and Effect 4. Alchemy ships breaking changes between
16
+ betas; upgrade this package and Alchemy together. A live test runs the full sequence with the real
17
+ Convex CLI: create, push, no-op, change, preview, deploy keys alone, and destroy.
15
18
 
16
19
  ## Install
17
20
 
@@ -19,32 +22,9 @@ request, destroy on close.
19
22
  pnpm add -D @samebase/alchemy-convex alchemy@2.0.0-beta.80 effect@^4.0.0 @effect/platform-node@^4.0.0 convex
20
23
  ```
21
24
 
22
- ## Credentials
23
-
24
- The provider calls the [Convex Management API](https://docs.convex.dev/management-api) with a
25
- bearer token. By default it reads `CONVEX_ACCESS_TOKEN` through the stack's config provider
26
- (process env, `.env`, stack secrets) and then falls back to the token the Convex CLI saved after
27
- `npx convex login` (`~/.convex/config.json`). A developer who is logged in needs no setup. CI sets
28
- the variable to a team access token.
29
-
30
- To pass a token explicitly:
31
-
32
- ```ts
33
- Convex.providers(Convex.fromToken(Redacted.make(token)));
34
- ```
35
-
36
- A team access token is team wide. The deploy keys this provider creates are scoped to one
37
- deployment or one project, and those are what CI deploys with.
38
-
39
25
  ## Example
40
26
 
41
27
  ```ts
42
- import * as Alchemy from "alchemy";
43
- import * as Cloudflare from "alchemy/Cloudflare";
44
- import * as Effect from "effect/Effect";
45
- import * as Layer from "effect/Layer";
46
- import * as Convex from "@samebase/alchemy-convex";
47
-
48
28
  export default Alchemy.Stack(
49
29
  "MyApp",
50
30
  {
@@ -52,137 +32,134 @@ export default Alchemy.Stack(
52
32
  state: Cloudflare.state(),
53
33
  },
54
34
  Effect.gen(function* () {
55
- const project = yield* Convex.Project("Project", {
56
- team: "my-team",
57
- name: "my-app",
58
- });
59
-
60
- const deployKey = yield* Convex.DeployKey("DeployKey", {
61
- deployment: project.prodDeploymentName.as<string>(),
62
- name: "alchemy",
63
- });
64
-
65
- const backend = yield* Convex.Deploy("Backend", {
66
- cwd: ".",
67
- deployKey: deployKey.deployKey,
35
+ const { stage } = yield* Alchemy.Stack;
36
+ const backend =
37
+ stage === "prod"
38
+ ? yield* Convex.Deployment("Backend", {
39
+ // An existing project. A new one: { team: "my-team", name: "my-app" }.
40
+ project: yield* Convex.Project("Project", { team: "my-team", slug: "my-app" }),
41
+ type: "prod",
42
+ source: "./convex",
43
+ env: { AUTH_SECRET: Config.Redacted("AUTH_SECRET") },
44
+ })
45
+ : yield* Convex.Deployment("Backend", {
46
+ // The project that the stage "prod" owns, read from its state.
47
+ project: yield* Convex.Project.ref("Project", { stage: "prod" }),
48
+ type: "preview",
49
+ name: stage, // such as "pr-42"
50
+ source: "./convex",
51
+ }).pipe(RemovalPolicy.destroy());
52
+ const site = yield* Cloudflare.Website.Vite("Site", {
53
+ env: { VITE_CONVEX_URL: backend.url, VITE_CONVEX_SITE_URL: backend.siteUrl },
68
54
  });
69
-
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" },
75
- });
76
-
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 };
55
+ return { url: site.url, convexUrl: backend.url };
85
56
  }),
86
57
  );
87
58
  ```
88
59
 
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.
60
+ The imports are `alchemy`, `alchemy/Cloudflare`, `alchemy/RemovalPolicy`, `effect/Config`,
61
+ `effect/Effect`, `effect/Layer`, and `@samebase/alchemy-convex` as `Convex`. Order follows the
62
+ outputs: the project exists before its deployment, and the push finishes before the site build. The
63
+ first `alchemy deploy --stage prod` takes over the existing project, and its production
64
+ deployment if it has code, only with `--adopt`.
65
+ `alchemy destroy --stage pr-42` deletes the preview deployment. Without `RemovalPolicy.destroy()`,
66
+ it stays until Convex deletes it at expiry (14 days by default).
67
+ [`examples/app/alchemy.run.ts`](examples/app/alchemy.run.ts) is the same stack.
91
68
 
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`.
69
+ ## Credentials
95
70
 
96
- ## Data safety
71
+ Credentials are a layer, never props, so they do not go into Alchemy state. `Convex.providers()`
72
+ reads them through the stack's config provider (process env, `.env`, stack secrets):
73
+
74
+ - `CONVEX_ACCESS_TOKEN`, a team access token, for the Management API. Without it, the provider uses
75
+ the token that `npx convex login` saved (`~/.convex/config.json`). `Convex.Project` needs it, and
76
+ so does a deployment that the deploy key does not reach.
77
+ - `CONVEX_DEPLOY_KEY`, optional, the same variable that the Convex CLI reads:
78
+ - A deploy key of one deployment (`prod:<name>|...`) sets variables and pushes to that deployment.
79
+ That stage then needs no access token if it passes `project: { projectId }` or a
80
+ `Convex.Project.ref`. The key must have `deployment:data:view` (dashboard keys have all
81
+ actions), because the provider reads the project and type of its deployment first.
82
+ - A preview deploy key of a project (`preview:<team>:<project>|...`) creates, pushes to, and
83
+ deletes preview deployments of that project. It cannot change variables: with `env`, the
84
+ provider uses the access token, and without one it stops with `EnvironmentNeedsDeployKey`
85
+ before it creates anything. Use project default variables in the Convex dashboard for previews.
86
+
87
+ A key that does not fit the deployment is not used: the provider uses the access token. To pass
88
+ values explicitly: `Convex.providers(Convex.fromValues({ accessToken, deployKey }))`.
97
89
 
98
- Deleting a Convex project deletes all of its deployments and data. This package does not delete,
99
- replace, or overwrite your data by accident:
100
-
101
- - `Convex.Project` has the removal policy `retain`. When you remove the resource or run
102
- `alchemy destroy`, Alchemy forgets the project and Convex keeps it. To delete the project with
103
- the resource, add `RemovalPolicy.destroy()`:
104
-
105
- ```ts
106
- yield * Convex.Project("Project", { team, name }).pipe(Alchemy.RemovalPolicy.destroy());
107
- ```
108
-
109
- - A project is never replaced. A change to `name` renames the project in place
110
- (`PATCH /projects/{project_id}`). The id, the slug, the deployments, and the data stay.
111
- - A change to `team`, `deploymentType`, or `deploymentRegion` stops the deploy with
112
- `ProjectTeamChange` or `ProjectDeploymentChange`. Convex cannot move a project or change its
113
- first deployment. Set the old value again, or create a new resource with a new logical id and
114
- move the data yourself.
115
- - With state, the provider finds the project only by the id in state. Without state, it finds a
116
- project by its exact name, on all pages of the team's project list, and takes it over only with
117
- `--adopt`. When two projects have that name, the deploy stops with `AmbiguousProject`.
118
- - Each `DeployKey` and `PreviewDeployKey` requests a name that is unique to the resource: the
119
- `name` prop, a dash, and a hash of the resource identity. The provider deletes a key only by its
120
- secret, so it cannot revoke the key of another resource. When the key list does not show
121
- exactly one key with the requested name, the deploy stops with `DeployKeyRecoveryRequired`,
122
- which lists the key ids. The provider never revokes a key on a failed create.
123
- - With an OAuth token as the credential, Convex creates no new key: it returns the OAuth token
124
- with a new prefix. The key resources then stop with `DeployKeyIsCredential` and do not keep that
125
- value, because a delete would revoke the OAuth token. Use a team access token or the Convex CLI
126
- login.
127
- - An `EnvironmentVariable` or a `DefaultEnvironmentVariable` that exists but is not in state
128
- belongs to someone else. The provider overwrites it only with `--adopt`.
129
- - A variable keeps its name and deployment (a default keeps its project, name, and deployment
130
- type). A change stops the deploy with `VariableIdentityChange`. To rename or move a variable,
131
- create a new resource with a new logical id and remove the old one. A rename in place would
132
- need a cleanup step that can fail and leave a variable or a revoked deploy key behind.
133
- - A variable write runs again after a Convex write conflict or a 5xx answer, at most four more
134
- times. The write sets a value, so a second write has the same result.
90
+ ## Resources
135
91
 
136
- Upgrade from 0.1: state from 0.1 records the policy `destroy` for each project. Run
137
- `alchemy deploy` one time with 0.2 while the stack still declares the project. That deploy records
138
- `retain`. Do this before you remove a project from the stack or run `alchemy destroy`.
139
- Deploy keys from 0.1 state keep working, but 0.2 does not delete them: their state cannot show that
140
- the secret is a new key and not an OAuth token. When you remove or replace such a key, delete it in
141
- the Convex dashboard.
92
+ - `Convex.Project`: `{ team, name, deploymentRegion?, deploymentType? }` creates a project with its
93
+ default deployment, or takes over one with that exact name with `--adopt`. A changed `name`
94
+ renames the project in place. `{ team, slug }` is an existing project, found by its slug and
95
+ taken over with `--adopt`; it never creates or renames a project. Outputs `projectId`, `slug`,
96
+ `name`, `teamId`, `prodDeploymentName`, `prodDeploymentUrl`.
97
+ - `Convex.Deployment`: `{ project, type: "prod" }` or `{ project, type: "dev" | "preview", name }`,
98
+ each with optional `source` and `env`. Outputs `name` (such as `happy-animal-123`), `url`,
99
+ `siteUrl`, `type`, `projectId`, `reference`, `previewName`, `hash`, and `envNames`.
100
+ - `prod` is the project's default production deployment (with a deploy key: the key's
101
+ production deployment). The resource never creates or deletes one. A production deployment
102
+ with code that is not in state is taken over only with `--adopt`.
103
+ - `dev` creates a dev deployment with `name` as its reference; `preview` creates a preview
104
+ deployment with `name` as its preview name. An expired preview is created again on the next
105
+ deploy.
106
+ - `source` is the Convex functions directory: `convex/`, or the `functions` field of
107
+ convex.json (`SourceMismatch`). The CLI runs in the nearest directory above it with a
108
+ package.json, with the Node.js that runs Alchemy, and is never downloaded (`ConvexCliNotFound`).
109
+ - The content identity is a hash of every file of that app directory that git does not ignore,
110
+ the nearest lockfile, the deployment name, and `env`. The same hash is a no-op. Otherwise the
111
+ update sets `env`, then pushes. A failed push sets the changed variables back, and deletes a
112
+ dev or preview deployment that the same run created.
113
+ - `env` values are strings or secrets such as `Config.Redacted("AUTH_SECRET")`. A name that
114
+ leaves `env` is removed from the deployment.
115
+ - `source: { path, command?, include? }` replaces `convex deploy` with `command`, run in the app
116
+ directory with the push key in `CONVEX_DEPLOY_KEY`, such as
117
+ `["node_modules/.bin/convex-hosting", "deploy", "--build", "node_modules/.bin/vp build", "--dist", "dist/client"]`.
118
+ The command must push with `convex deploy` (convex-hosting does, through `--cmd`). `include`
119
+ narrows the hash to `path`, package.json, convex.json, the lockfile, and the listed globs, such
120
+ as the build inputs `["src/**", "public/**", "vite.config.ts"]`. A minted key for a command
121
+ has every action, because the command can do more than a push.
142
122
 
143
- ## Resources
123
+ ## Data safety
144
124
 
145
- - `Convex.Project`: `{ team: slug | id, name, deploymentRegion?, deploymentType? }`. Creates a
146
- project with its default deployment. Outputs `projectId`, `slug`, `teamId`,
147
- `prodDeploymentName`, `prodDeploymentUrl`. A changed `name` renames the project in place. A
148
- changed `team`, `deploymentRegion`, or `deploymentType` stops the deploy. An existing project
149
- with the same name is adopted only with `--adopt`. The removal policy is `retain`: only a
150
- resource with `RemovalPolicy.destroy()` deletes the project with its deployments and data.
151
- - `Convex.DeployKey`: `{ deployment, name, allowedActions? }`. Outputs `uniqueName`, `keyId`, and
152
- the `deployKey` secret. Any change replaces the key. Delete revokes the key by its secret; a key
153
- from 0.1 state stays. Keys are never adopted: a key without its secret is useless.
154
- - `Convex.PreviewDeployKey`: `{ projectId, name }`. Project-level key for preview deployments.
155
- Outputs `uniqueName`, `keyId`, and `previewDeployKey`.
156
- - `Convex.EnvironmentVariable`: `{ deployment, deployKey, name, value }`. One variable on one
157
- deployment, set through the deployment API with that deployment's key. An existing variable with
158
- the same name is adopted and overwritten only with `--adopt`. A new `value` or `deployKey` is an
159
- update in place. A new `name` or `deployment` stops the deploy with `VariableIdentityChange`:
160
- use a new logical id. Outputs `deployment`, `name`, and `deployKey`. Read and delete use the
161
- deploy key in the outputs, not the props.
162
- - `Convex.DefaultEnvironmentVariable`: `{ projectId, name, value, deploymentType }`. A project
163
- default that new deployments of that type inherit. Use it for preview and dev deployments. An
164
- existing default with the same name and type is adopted and overwritten only with `--adopt`. A
165
- new `value` is an update in place. A new `projectId`, `name`, or `deploymentType` stops the
166
- deploy with `VariableIdentityChange`: use a new logical id.
167
- - `Convex.Deploy`: `{ cwd?, env?, deployKey, previewName?, previewCreate?, previewRun?, extraArgs?,
168
- memo?, timeout? }`. Runs `npx convex deploy` with `CONVEX_DEPLOY_KEY` set and parses the
169
- deployment URL from the CLI output. Outputs `url`, `deploymentName`, `hash`. Memoized by content
170
- hash like Alchemy's own build steps, except with `previewName` or `previewCreate`: a preview
171
- deployment can expire, so a preview deploy always pushes. Deleting the resource does nothing;
172
- the deployment belongs to the project, and preview deployments expire on Convex's schedule.
125
+ - `Convex.Project` and `Convex.Deployment` have the removal policy `retain`. `alchemy destroy`
126
+ forgets them and Convex keeps them. Add `RemovalPolicy.destroy()` to delete with the resource.
127
+ - A project is never replaced. A change of `team`, `slug`, `deploymentType`, or `deploymentRegion`
128
+ stops the deploy with a typed error that names the fix.
129
+ - A deployment keeps its `project`, `type`, and `name`; a change stops the deploy with
130
+ `DeploymentIdentityChange`. A production deployment is never deleted, also not with
131
+ `RemovalPolicy.destroy()`, and also not a dev deployment that became one.
132
+ - A dev or preview deployment that exists but is not in state belongs to someone else, such as a
133
+ preview that CI made. The resource takes it over only with `--adopt`, and never creates over it:
134
+ a Convex create for an existing preview name deletes that preview.
135
+ - With the access token, each push uses a new deploy key with only `deployment:deploy`,
136
+ `deployment:env:view`, and `deployment:env:write`, which expires after two hours and is deleted
137
+ after the push, also after a failure. With an OAuth token, Convex returns the token itself as the
138
+ key, so the delete is skipped. No other key is created or revoked.
139
+ - The key reaches the CLI only in an env file with mode 0600, in a temporary home directory that is
140
+ removed after the run. Variables named `CONVEX_*` are not passed on. The CLI output stays out of
141
+ the logs; a failure (`CodePushFailed`) shows the last 20 lines with the key removed. A push to
142
+ another deployment stops the deploy with `PushTargetMismatch`.
143
+ - A variable in `env` that exists with another value and that the resource did not set stops the
144
+ deploy with `VariablesOwnedBySomeoneElse`. `--adopt` overwrites it. Destroy leaves variables in
145
+ place: they go with the deployment.
146
+ - A variable write runs again after a Convex write conflict or a 5xx answer, at most four more
147
+ times.
173
148
 
174
149
  ## Limits
175
150
 
176
- - Team slugs resolve through an unofficial dashboard endpoint. Pass the numeric team id to avoid
177
- it.
178
- - Deploy keys and other secret outputs are part of Alchemy state. Alchemy's Cloudflare state store
179
- encrypts state at rest; the local file store does not.
180
- - A deleted deploy key keeps working on the deployment API for roughly 30 seconds.
181
- - A run that stops between a key create and the state write leaves a key that state does not
182
- know. The next run stops with `DeployKeyRecoveryRequired`. Delete the listed key in the Convex
183
- dashboard, then run the deploy again.
184
- - Not covered: dev deployments, custom domains, log streams, teams and members, components,
185
- snapshots.
151
+ - `env` values are props, so Alchemy state holds them. Keep `.alchemy/` git-ignored, and use an
152
+ encrypted state store, such as `Cloudflare.state()`, in CI.
153
+ - The hash does not see a change in a workspace package outside the app directory, or a push from
154
+ outside Alchemy. Change a file of the app to push again.
155
+ - With a deploy key, the provider reaches the deployment at `https://<name>.convex.cloud`, so the
156
+ deployment must answer there.
157
+ - A run that stops between a deployment create and the state write leaves a deployment that state
158
+ does not know. The next run stops with `OwnedBySomeoneElse`; run it with `--adopt`.
159
+ - Team slugs for `{ team, name }` resolve through an unofficial dashboard endpoint. Pass the numeric
160
+ team id to avoid it. `{ team, slug }` uses the official endpoint.
161
+ - Not covered: deployment regions and classes, custom domains, log streams, teams and members,
162
+ components, snapshots.
186
163
 
187
164
  ## Development
188
165
 
@@ -193,22 +170,16 @@ pnpm run build # lib/ with declarations, then publint and attw
193
170
  pnpm run test:live # real API calls, see below
194
171
  ```
195
172
 
196
- Use Node 24 (`.node-version`) and pnpm 11. `pnpm install` also installs a pre-commit hook that
197
- runs `vp staged`.
198
-
199
- Unit tests run against recorded Management API payloads in `test/fixtures/management/`. Live tests
200
- run only with `ALCHEMY_CONVEX_LIVE=1`, which `pnpm run test:live` sets. They need a Convex login or
201
- `CONVEX_ACCESS_TOKEN`, and a throwaway project.
202
- Point them at yours with `ALCHEMY_CONVEX_LIVE_TEAM`, `ALCHEMY_CONVEX_LIVE_PROJECT_ID`,
203
- `ALCHEMY_CONVEX_LIVE_DEPLOYMENT`, and `ALCHEMY_CONVEX_LIVE_PROJECT_DIR` (a Convex app directory for
204
- the deploy test). They create resources named `tmp-alchemy-convex-*` and remove them again.
205
- A file argument runs one live file only, for example
206
- `pnpm run test:live test/live/safety.live.test.ts`. That test runs the data safety contract
207
- through the Alchemy engine. It creates a project `tmp-alchemy-convex-<8 hex>` in
208
- `ALCHEMY_CONVEX_LIVE_TEAM` and deletes it at the end, also after a failure. It refuses to delete a
209
- project whose name does not start with `tmp-alchemy-convex-`.
173
+ Use Node 24 (`.node-version`) and pnpm 11. Unit tests run against recorded Management API payloads
174
+ in `test/fixtures/management/` and a fake Convex CLI. Live tests run only with
175
+ `ALCHEMY_CONVEX_LIVE=1`, which `pnpm run test:live` sets. They need a Convex login or
176
+ `CONVEX_ACCESS_TOKEN` that can create projects in `ALCHEMY_CONVEX_LIVE_TEAM`. Each test creates a
177
+ project whose name starts with `ALCHEMY_CONVEX_LIVE_PREFIX` (default `tmp-alchemy-convex-`),
178
+ deletes it at the end, also after a failure, and refuses to delete a project, or a deployment of a
179
+ project, without that prefix. A file argument runs one live file only, for example
180
+ `pnpm run test:live test/live/plane.live.test.ts`.
210
181
 
211
182
  Releases: change `version` in `package.json` in a pull request. After the merge,
212
183
  `.github/workflows/release.yml` publishes that version from `main` with npm trusted publishing.
213
184
 
214
- License: Apache 2.0.
185
+ License: Apache 2.0. Parts follow Confect; see [NOTICE](NOTICE).