From 756f0cb82bf1a24ca46e7f52e40c807e3af1dc6d Mon Sep 17 00:00:00 2001 From: Ian Macartney <366683+ianmacartney@users.noreply.github.com> Date: Tue, 10 Nov 2026 19:15:36 -0800 Subject: [PATCH 1/4] Release component from CI with Changesets --- .changeset/README.md | 4 + .changeset/config.json | 11 + .github/workflows/lint-workflows.yml | 43 +++ .github/workflows/release.yml | 222 ++++++++++++ .github/workflows/test.yml | 44 ++- .github/workflows/version.yml | 59 ++++ CONTRIBUTING.md | 79 ++++- example/convex/_generated/server.d.ts | 19 + example/convex/_generated/server.js | 8 + package-lock.json | 491 +++++++++++++++++++++++++- package.json | 13 +- 11 files changed, 969 insertions(+), 24 deletions(-) create mode 100644 .changeset/README.md create mode 100644 .changeset/config.json create mode 100644 .github/workflows/lint-workflows.yml create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/version.yml diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 0000000..ef3543c --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,4 @@ +# Release notes + +See [CONTRIBUTING.md](../CONTRIBUTING.md#releases) for contributing release +notes and preparing a release. diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000..4b3a691 --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@4.0.1/schema.json", + "baseBranch": "main", + "access": "public", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/.github/workflows/lint-workflows.yml b/.github/workflows/lint-workflows.yml new file mode 100644 index 0000000..9c1cc49 --- /dev/null +++ b/.github/workflows/lint-workflows.yml @@ -0,0 +1,43 @@ +name: Lint workflows + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +on: + push: + branches: [main] + paths: + - ".github/workflows/**" + - ".github/actions/**" + pull_request: + branches: ["**"] + paths: + - ".github/workflows/**" + - ".github/actions/**" + +permissions: + contents: read + +jobs: + actionlint: + name: actionlint + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + persist-credentials: false + - uses: kjanat/actionlint@08bb2c4f0d039744455b87aef1c647fb8b66d37b # v1.17.0 + + zizmor: + name: zizmor + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + security-events: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + persist-credentials: false + - uses: zizmorcore/zizmor-action@53290badfa369cb0bf7ad16342cb62c2fa47ac7b # v0.8.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..5d23acd --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,222 @@ +name: Release + +on: + push: + branches: [main] + workflow_dispatch: + inputs: + commit: + description: + "Optional 40-character version-bump commit SHA reachable from the + selected ref" + required: false + type: string + +concurrency: + group: ${{ github.workflow }} + queue: max + cancel-in-progress: false + +permissions: {} + +env: + NODE_VERSION: "24.22.1" + +jobs: + plan: + if: github.repository_owner == 'convex' + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: read + outputs: + sha: ${{ steps.select.outputs.sha }} + name: ${{ steps.select.outputs.name }} + version: ${{ steps.select.outputs.version }} + channel: ${{ steps.select.outputs.channel }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + fetch-depth: 0 + persist-credentials: false + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: ${{ env.NODE_VERSION }} + package-manager-cache: false + - id: select + name: Select the latest version-bump commit + env: + RELEASE_COMMIT: ${{ inputs.commit }} + run: | + if [[ "$GITHUB_EVENT_NAME" == workflow_dispatch ]]; then + source="${RELEASE_COMMIT:-$GITHUB_SHA}" + [[ "$source" =~ ^[0-9a-f]{40}$ ]] || { echo 'Expected a 40-character commit SHA' >&2; exit 1; } + git merge-base --is-ancestor "$source" "$GITHUB_SHA" + else + before=$(jq -r .before "$GITHUB_EVENT_PATH") + source='' + candidates=$(git rev-list --first-parent "$before..$GITHUB_SHA" -- package.json) + while read -r candidate && [[ -n "$candidate" ]]; do + current=$(git show "$candidate:package.json" | jq -r .version) + previous=$(git show "$candidate^1:package.json" | jq -r .version) + if [[ "$current" != "$previous" ]]; then source="$candidate"; break; fi + done <<< "$candidates" + if [[ -z "$source" ]]; then echo 'No version bump'; exit 0; fi + fi + name=$(git show "$source:package.json" | jq -r .name) + VERSION=$(git show "$source:package.json" | jq -r .version) + PREVIOUS_VERSION=$(git show "$source^1:package.json" | jq -r .version) + export VERSION PREVIOUS_VERSION + node --input-type=module <<'NODE' + import { execFileSync } from 'node:child_process'; + import { appendFileSync } from 'node:fs'; + import { createRequire } from 'node:module'; + // Use the standard SemVer library bundled with the pinned npm. + const npmRoot = execFileSync('npm', ['root', '-g'], { encoding: 'utf8' }).trim(); + const semver = createRequire(`${npmRoot}/npm/package.json`)('semver'); + const { VERSION: version, PREVIOUS_VERSION: previous, GITHUB_REF: ref } = process.env; + if (semver.valid(version) !== version || !semver.gt(version, previous)) throw new Error('Selected commit must increase the package version'); + const pre = semver.prerelease(version); + const channel = pre ? String(pre[0]) : 'latest'; + if (pre && (!/^[a-z][a-z0-9-]*$/.test(channel) || channel === 'latest' || semver.validRange(channel))) throw new Error('Invalid prerelease channel'); + if (!pre && ref !== 'refs/heads/main') throw new Error('Stable releases require main'); + appendFileSync(process.env.GITHUB_OUTPUT, `channel=${channel}\n`); + NODE + tag="v$VERSION" + if git show-ref --verify --quiet "refs/tags/$tag"; then + if [[ "$GITHUB_EVENT_NAME" != workflow_dispatch ]]; then + echo "$tag was already staged; skipping automatic release" + exit 0 + fi + [[ "$(git rev-parse "$tag^{commit}")" == "$source" ]] || { echo "$tag points elsewhere" >&2; exit 1; } + fi + { + echo "sha=$source" + echo "name=$name" + echo "version=$VERSION" + } >> "$GITHUB_OUTPUT" + + build: + needs: plan + if: needs.plan.outputs.sha != '' + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: read + outputs: + artifact_id: ${{ steps.upload.outputs.artifact-id }} + sha256: ${{ steps.digest.outputs.sha256 }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + ref: ${{ needs.plan.outputs.sha }} + persist-credentials: false + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: ${{ env.NODE_VERSION }} + package-manager-cache: false + - run: npm ci + - name: Validate release notes + run: | + # Compare against this checkout while reading all notes, including prerelease state. + git update-ref refs/heads/main HEAD + npx changeset status --output "$RUNNER_TEMP/changesets.json" + jq -e '.changesets | length == 0' "$RUNNER_TEMP/changesets.json" + version=$(jq -r .version package.json) + awk -v version="$version" '/^## / { active=($0 == "## " version); next } active && !/^#/ && /[[:alnum:]]/ { found=1 } END { exit !found }' CHANGELOG.md + - run: npm run build + - run: npm run check + - name: Run E2E tests and verify generated code + env: + CONVEX_AGENT_MODE: anonymous + CONVEX_DISABLE_TELEMETRY: "1" + run: | + npm run test:e2e + git diff --exit-code -- src/component/_generated example/convex/_generated + test -z "$(git ls-files --others --exclude-standard src/component/_generated example/convex/_generated)" + - name: Pack the tested package + run: | + mkdir -p "$RUNNER_TEMP/npm-release" + npm pack --ignore-scripts --pack-destination "$RUNNER_TEMP/npm-release" + - id: digest + run: + echo "sha256=$(sha256sum "$RUNNER_TEMP"/npm-release/*.tgz | cut -d ' ' + -f 1)" >> "$GITHUB_OUTPUT" + - id: upload + uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7 + with: + name: npm-release-${{ needs.plan.outputs.sha }} + path: ${{ runner.temp }}/npm-release/*.tgz + if-no-files-found: error + + stage: + needs: [plan, build] + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + id-token: write + steps: + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: ${{ env.NODE_VERSION }} + package-manager-cache: false + - uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8 + with: + artifact-ids: ${{ needs.build.outputs.artifact_id }} + path: ${{ runner.temp }}/npm-release + merge-multiple: true + - name: Stage the tested artifact + env: + SOURCE_SHA: ${{ needs.plan.outputs.sha }} + PACKAGE_NAME: ${{ needs.plan.outputs.name }} + VERSION: ${{ needs.plan.outputs.version }} + CHANNEL: ${{ needs.plan.outputs.channel }} + EXPECTED_SHA256: ${{ needs.build.outputs.sha256 }} + run: | + set -o pipefail + set -- "$RUNNER_TEMP"/npm-release/*.tgz + test "$#" -eq 1 + TARBALL="$1" + echo "$EXPECTED_SHA256 $TARBALL" | sha256sum -c - + { + echo "$PACKAGE_NAME@$VERSION — source $SOURCE_SHA — npm channel $CHANNEL" + echo "Review on npm's Staged Packages page, then approve with 2FA or npm stage approve ." + } >> "$GITHUB_STEP_SUMMARY" + # npm reads GITHUB_SHA for provenance; checkout alone does not change it. + GITHUB_SHA="$SOURCE_SHA" npm stage publish "$TARBALL" \ + --ignore-scripts --provenance --access public --tag "$CHANNEL" \ + 2>&1 | tee "$RUNNER_TEMP/npm-stage.log" + - name: Preserve staging output and stage ID + if: always() + run: | + if test -f "$RUNNER_TEMP/npm-stage.log"; then + cat "$RUNNER_TEMP/npm-stage.log" >> "$GITHUB_STEP_SUMMARY" + fi + + tag: + needs: [plan, stage] + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + contents: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + with: + ref: ${{ needs.plan.outputs.sha }} + fetch-depth: 0 + persist-credentials: false + - name: Tag the staged source + env: + GH_TOKEN: ${{ github.token }} + SOURCE_SHA: ${{ needs.plan.outputs.sha }} + VERSION: ${{ needs.plan.outputs.version }} + run: | + tag="v$VERSION" + if git show-ref --verify --quiet "refs/tags/$tag"; then + test "$(git rev-parse "$tag^{commit}")" = "$SOURCE_SHA" + else + git tag "$tag" "$SOURCE_SHA" + gh auth setup-git + git push origin "refs/tags/$tag" + fi diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0bcfad6..dd03aab 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,35 +4,61 @@ concurrency: cancel-in-progress: true on: + workflow_dispatch: push: branches: [main] pull_request: branches: ["**"] +permissions: {} + jobs: check: name: Test and lint runs-on: ubuntu-latest timeout-minutes: 30 + permissions: + contents: read steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false - name: Node setup - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 + uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 with: cache-dependency-path: package.json - node-version: "24.x" + node-version: "24.22.1" cache: "npm" - name: Install and build run: | - npm i + npm ci npm run build - - name: Publish package for testing branch - run: npx pkg-pr-new publish || echo "pkg-pr-new failed" + - name: Validate release notes when the proposed merge changes the version + env: + BASE_SHA: + ${{ github.event.pull_request.base.sha || github.event.before }} + run: | + version=$(jq -r .version package.json) + previous=$(git show "$BASE_SHA:package.json" | jq -r .version) + if [[ "$version" != "$previous" ]]; then + git update-ref refs/heads/main HEAD + npx changeset status --output "$RUNNER_TEMP/changesets.json" + jq -e '.changesets | length == 0' "$RUNNER_TEMP/changesets.json" + awk -v version="$version" '/^## / { active=($0 == "## " version); next } active && !/^#/ && /[[:alnum:]]/ { found=1 } END { exit !found }' CHANGELOG.md + fi - name: Test + run: npm run check + - name: Run E2E smoke test and verify generated code + env: + CONVEX_AGENT_MODE: anonymous + CONVEX_DISABLE_TELEMETRY: "1" run: | - npm run test - npm run typecheck - npm run lint + npm run test:e2e + git diff --exit-code -- src/component/_generated example/convex/_generated + test -z "$(git ls-files --others --exclude-standard src/component/_generated example/convex/_generated)" + - name: Publish package for testing branch + run: npx pkg-pr-new publish || echo "pkg-pr-new failed" diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml new file mode 100644 index 0000000..11448c7 --- /dev/null +++ b/.github/workflows/version.yml @@ -0,0 +1,59 @@ +name: Update release PR + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: false + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: {} + +jobs: + version: + name: Update release PR + if: github.repository_owner == 'convex' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + persist-credentials: false + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: "24.22.1" + package-manager-cache: false + - run: npm ci --ignore-scripts + - name: Check whether a version PR is needed + id: mode + uses: changesets/action/select-mode@ae32849d5ba541f9ae29e40e22a623bc13562f51 # v2.1.2 + - name: Check main is still at this run's commit + if: steps.mode.outputs.mode == 'version' + id: freshness + env: + GH_TOKEN: ${{ github.token }} + run: | + main_sha="$(gh api "repos/$GITHUB_REPOSITORY/git/ref/heads/main" --jq '.object.sha')" + if [[ "$main_sha" == "$GITHUB_SHA" ]]; then + echo "current=true" >> "$GITHUB_OUTPUT" + else + echo "current=false" >> "$GITHUB_OUTPUT" + echo "Skipping release PR update: main has moved beyond this run's commit." + fi + - name: Create or update the release PR + if: + steps.mode.outputs.mode == 'version' && + steps.freshness.outputs.current == 'true' + uses: changesets/action/version@ae32849d5ba541f9ae29e40e22a623bc13562f51 # v2.1.2 + with: + github-token: ${{ github.token }} + pr-title: "Prepare next release" + commit-message: "Version packages" + pr-base-branch: main diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dec8af0..f4042cd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,16 +3,85 @@ ## Running locally ```sh -npm i +npm ci npm run dev ``` ## Testing +```sh +npm run build +npm run check +npm run test:e2e +``` + +When component APIs change, regenerate and commit the generated files: + +```sh +npm run build:codegen +``` + +## Releases + +Include a changeset with changes that need a release: + +```sh +npx changeset +``` + +Choose the version bump, describe the user-visible change, and commit the +generated file with your implementation. Docs-only and development-tooling +changes do not need a changeset. + +Review and merge the release PR when ready. A PR opened by the Changesets +workflow may have no automatic checks. Run +[Test and lint](./.github/workflows/test.yml) on its `changeset-release/main` +branch and wait for the required check to pass. To refresh the release PR, run +[Update release PR](./.github/workflows/version.yml) on main. + +After staging, review the package on npm's **Staged Packages** page and approve +with 2FA, or run: + +```sh +npm stage approve +``` + +Use the stage ID from the workflow log. Approve versions in order to avoid +moving `latest` backward. + +### Local preparation and prereleases + +To prepare a release locally instead of using the bot: + ```sh npm ci -npm run clean -npm run typecheck -npm run lint -npm run test +npm run build +npm run check +npx changeset version ``` + +Review and commit the version, changelog, and consumed changesets, then merge +through a checked PR. A push to main stages the latest version-bump commit in +that push. + +For a branch prerelease, prepare a prerelease version with Changesets and +dispatch **Release** from the Actions page on that branch. The first prerelease +identifier selects the npm channel, such as `alpha`. Stable releases require +main. + +### Recovery + +If staging has not been attempted, dispatch **Release** on the appropriate ref +with the 40-character SHA of a version-bump commit reachable from that ref. Once +staging starts, inspect npm before retrying: dispatching **Release** runs +`npm stage publish` again. If the package is already staged, retry only the +failed tag job or, after verifying the staged version, create its source tag at +the version-bump commit. + +| Failure | Recovery | +| ----------------------------------------------- | -------------------------------------------------------------------------------- | +| Failure before staging was attempted | Retry failed jobs for the same commit. | +| Staging succeeded; tagging failed | Retry only the failed tag job, or create the source tag after verifying staging. | +| Upload outcome uncertain | Inspect npm; retry staging only if absent, or recover the tag if already staged. | +| Fix requires changing the release commit's code | Prepare a new version-bump commit. | +| Candidate rejected after staging | Leave its tag and prepare a new version. | diff --git a/example/convex/_generated/server.d.ts b/example/convex/_generated/server.d.ts index bec05e6..f235db4 100644 --- a/example/convex/_generated/server.d.ts +++ b/example/convex/_generated/server.d.ts @@ -21,6 +21,17 @@ import { } from "convex/server"; import type { DataModel } from "./dataModel.js"; +/** + * Typesafe environment variables. + * + * This includes platform-provided env vars and any variables declared in + * `convex.config.ts`. + */ +type Env = { + readonly CONVEX_CLOUD_URL: string; + readonly CONVEX_SITE_URL: string; +}; + /** * Define a query in this Convex app's public API. * @@ -95,6 +106,14 @@ export declare const internalAction: ActionBuilder; */ export declare const httpAction: HttpActionBuilder; +/** + * Typesafe environment variables. + * + * This includes platform-provided env vars and any variables declared in + * `convex.config.ts`. + */ +export declare const env: Env; + /** * A set of services for use within Convex query functions. * diff --git a/example/convex/_generated/server.js b/example/convex/_generated/server.js index bf3d25a..2dbe0db 100644 --- a/example/convex/_generated/server.js +++ b/example/convex/_generated/server.js @@ -91,3 +91,11 @@ export const internalAction = internalActionGeneric; * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up. */ export const httpAction = httpActionGeneric; + +/** + * Typesafe environment variables. + * + * This includes platform-provided env vars and any variables declared in + * `convex.config.ts`. + */ +export const env = process.env; diff --git a/package-lock.json b/package-lock.json index 312284a..3d7b455 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "convex-helpers": "^0.1.127" }, "devDependencies": { + "@changesets/cli": "3.0.3", "@convex-dev/eslint-plugin": "^2.0.0", "@convex-dev/rate-limiter": "^0.3.2", "@convex-dev/static-hosting": "^0.2.0-alpha.2", @@ -341,6 +342,306 @@ "node": ">=6.9.0" } }, + "node_modules/@changesets/apply-release-plan": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@changesets/apply-release-plan/-/apply-release-plan-8.1.1.tgz", + "integrity": "sha512-nk2Hbq3M9NeHMdaIRBDvawKrQJzXB9sGyBUE5yWnIKtvdcNCPj75zBekJYr0gTT2ecJH3eFeTrc8eisPImcv0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/config": "^4.0.1", + "@changesets/format": "^0.1.2", + "@changesets/git": "^4.0.1", + "@changesets/should-skip-package": "^1.0.0", + "@changesets/types": "^7.0.0", + "import-meta-resolve": "^4.2.0", + "jsonc-parser": "^3.3.1", + "semver": "^7.8.1" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" + } + }, + "node_modules/@changesets/assemble-release-plan": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@changesets/assemble-release-plan/-/assemble-release-plan-7.0.0.tgz", + "integrity": "sha512-oEW8BxdA604kGGtDSCiHr5w9Tv4UWe9I2k61IBNZzCOE1kbYaJj4v+lFQNgcEZFkUc2pV/+hASErGDvpJOZCTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^1.0.0", + "@changesets/get-dependents-graph": "^3.0.0", + "@changesets/should-skip-package": "^1.0.0", + "@changesets/types": "^7.0.0", + "semver": "^7.8.1" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" + } + }, + "node_modules/@changesets/changelog-git": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@changesets/changelog-git/-/changelog-git-1.0.0.tgz", + "integrity": "sha512-3Dst2Ime2Op5nd4XmWJLPIgp11ZFqJqSkVug9izK6TDcIV4YlhPS4ECbEVR+eGI0bk0r1ItogD4j2Oli87bJrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^7.0.0" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" + } + }, + "node_modules/@changesets/cli": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@changesets/cli/-/cli-3.0.3.tgz", + "integrity": "sha512-L1i67bMfkK6iC/kDejuNO4pFgTRcWIObsD6Qof2woP1I4M8tj17DGlcpRaTzTv1DYSP5EMwWdEtR8X0Pj5Jg3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/apply-release-plan": "^8.1.1", + "@changesets/assemble-release-plan": "^7.0.0", + "@changesets/changelog-git": "^1.0.0", + "@changesets/config": "^4.0.1", + "@changesets/errors": "^1.0.0", + "@changesets/get-dependents-graph": "^3.0.0", + "@changesets/git": "^4.0.1", + "@changesets/pre": "^3.0.0", + "@changesets/read": "^1.0.1", + "@changesets/should-skip-package": "^1.0.0", + "@changesets/types": "^7.0.0", + "@changesets/write": "^1.0.1", + "@clack/prompts": "^1.7.0", + "@manypkg/get-packages": "^3.1.0", + "@pnpm/deps.graph-sequencer": "^1100.0.1", + "cac": "^7.0.0", + "import-meta-resolve": "^4.2.0", + "launch-editor": "^2.14.1", + "package-manager-detector": "^1.6.0", + "semver": "^7.8.1", + "tinyexec": "^1.3.1" + }, + "bin": { + "changeset": "bin.js" + }, + "engines": { + "node": "^22.11 || ^24 || >=26", + "npm": ">=10.9.0", + "pnpm": ">=10.0.0", + "yarn": ">=4.5.2" + } + }, + "node_modules/@changesets/config": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@changesets/config/-/config-4.0.1.tgz", + "integrity": "sha512-PxpxSQLQSLDjceAgRq+4FWlU+HflDIw03mmPVipxoQx9xrXFUjmqTy3SE+n/2t+DqPdpPiLWVr5NRiLZvjyLwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/get-dependents-graph": "^3.0.0", + "@changesets/should-skip-package": "^1.0.0", + "@changesets/types": "^7.0.0", + "@manypkg/get-packages": "^3.1.0", + "picomatch": "^4.0.7" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" + } + }, + "node_modules/@changesets/config/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@changesets/errors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@changesets/errors/-/errors-1.0.0.tgz", + "integrity": "sha512-ElN/mEzn6zmETgjwf5MclCMa9ef59sAR0lfO8VSYIsiRvbC2FbLB/92EoYw10Sl0kGixxHFiJZUSv7dA+YpR8g==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.11 || ^24 || >=26" + } + }, + "node_modules/@changesets/format": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@changesets/format/-/format-0.1.3.tgz", + "integrity": "sha512-zTZrJbfY81+/MsXsey6i3jQjpApz4PqRQ+nouHGDJhCa5sKCExwR9mZFQ/67lMsqZDNy2G2v3wTbzOmZ56EtaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.8.0", + "tinyexec": "^1.3.1" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" + } + }, + "node_modules/@changesets/get-dependents-graph": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@changesets/get-dependents-graph/-/get-dependents-graph-3.0.0.tgz", + "integrity": "sha512-ji/t5wFA1zREKXRUePE6Qi+Qu2UgxCeSSGQrphezwvQZrp49B7sJ+8+wvM0tA7zPeSxYKCojDy3WWgrl+s+awg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^7.0.0", + "semver": "^7.8.1" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" + } + }, + "node_modules/@changesets/git": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@changesets/git/-/git-4.0.1.tgz", + "integrity": "sha512-6vWpIAC4LkpmlqaIu37ViT5enyd9+uBwAiGqCGhASvkSHBgx4PrtTGXWTMFYpDmZbjgyonyVgMciPrW4MqtJsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^1.0.0", + "@changesets/types": "^7.0.0", + "@manypkg/get-packages": "^3.1.0", + "picomatch": "^4.0.4", + "tinyexec": "^1.3.0" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" + } + }, + "node_modules/@changesets/git/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@changesets/parse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@changesets/parse/-/parse-1.0.0.tgz", + "integrity": "sha512-P0iaMb9p9CRYZiTgAllEIF9AUMQHIy1G72tKlcIqJp61icZSsKQNiOPdxAMZG8m/DvZwt/oz5xEbTpike//dWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^7.0.0", + "yaml": "^2.9.0" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" + } + }, + "node_modules/@changesets/pre": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@changesets/pre/-/pre-3.0.0.tgz", + "integrity": "sha512-Zm/6YliV/a2oeWTqHJf6KxLrQwgcK1i/BRDl2m0EKZvbnxV5fG9QRhwJJGshjsZTUTS6dkfURQ2K6aAvgNw/3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/errors": "^1.0.0", + "@changesets/types": "^7.0.0", + "@manypkg/get-packages": "^3.1.0" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" + } + }, + "node_modules/@changesets/read": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@changesets/read/-/read-1.0.1.tgz", + "integrity": "sha512-nzvSC6RcTiWf/dsuwZFXpLzGGsRHYCL68U3uHV7srsulU93aVWY7u2GEJiDZ+4GIIElfaW5EqtKC0UHroY+LTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/git": "^4.0.1", + "@changesets/parse": "^1.0.0", + "@changesets/types": "^7.0.0" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" + } + }, + "node_modules/@changesets/should-skip-package": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@changesets/should-skip-package/-/should-skip-package-1.0.0.tgz", + "integrity": "sha512-pwqoJmbONn1XgXmZXPEExgAaT+HdZLjALFTDgIm+PnS5KeO2nLtzA2/Q+4aMFY14kFMuXKG30MObhvDzWgzDgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/types": "^7.0.0" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" + } + }, + "node_modules/@changesets/types": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@changesets/types/-/types-7.0.0.tgz", + "integrity": "sha512-c5GoiQyt3pxiXjrWSNoP8/GRf4kG+VnKzovx1OQM8dYYALlSwgedmkPmJ+ZqGxqwg9D3Bkj85Uo4KLd5BN3A0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.11 || ^24 || >=26" + } + }, + "node_modules/@changesets/write": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@changesets/write/-/write-1.0.1.tgz", + "integrity": "sha512-q/ThtP9gcnEP6xlv7LrY26C2mHqdGBti/MmOVngt/M/oIGYkssmQGxPK9WzBNt2juVcH/vml2WQ+ra8LXYOTaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@changesets/format": "^0.1.1", + "@changesets/types": "^7.0.0", + "human-id": "^4.2.0" + }, + "engines": { + "node": "^22.11 || ^24 || >=26" + } + }, + "node_modules/@clack/core": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.6.0.tgz", + "integrity": "sha512-4GZynIB2IIe431odxesswvWTCP6orvqd6e2b07b3VFWBQRqUqIHc/EPIZwFoKS692QBp+ooPuXKaJV8g4kXuYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^2.0.0" + }, + "engines": { + "node": ">= 20.12.0" + } + }, + "node_modules/@clack/prompts": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.9.0.tgz", + "integrity": "sha512-tEj8X2No2A1c9NDnZECxiiJ8TP3vmFlN9tLlLeSaMqVW4tUmVG2R4qpv/eP4zymWxnymYLkLcIYVV0VcWj3RCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@clack/core": "1.6.0", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", + "sisteransi": "^2.0.0" + }, + "engines": { + "node": ">= 20.12.0" + } + }, "node_modules/@convex-dev/eslint-plugin": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@convex-dev/eslint-plugin/-/eslint-plugin-2.0.0.tgz", @@ -1134,6 +1435,48 @@ "node": ">=10" } }, + "node_modules/@manypkg/find-root": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@manypkg/find-root/-/find-root-3.1.0.tgz", + "integrity": "sha512-BcSqCyKhBVZ5YkSzOiheMCV41kqAFptW6xGqYSTjkVTl9XQpr+pqHhwgGCOHQtjDCv7Is6EFyA14Sm5GVbVABA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@manypkg/tools": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@manypkg/get-packages": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@manypkg/get-packages/-/get-packages-3.1.0.tgz", + "integrity": "sha512-0TbBVyvPrP7xGYBI/cP8UP+yl/z+HtbTttAD7FMAJgn/kXOTwh5/60TsqP9ZYY710forNfyV0N8P/IE/ujGZJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@manypkg/find-root": "^3.1.0", + "@manypkg/tools": "^2.1.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@manypkg/tools": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@manypkg/tools/-/tools-2.1.2.tgz", + "integrity": "sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jju": "^1.4.0", + "tinyglobby": "^0.2.13", + "yaml": "^2.9.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", @@ -1435,6 +1778,19 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@pnpm/deps.graph-sequencer": { + "version": "1100.0.1", + "resolved": "https://registry.npmjs.org/@pnpm/deps.graph-sequencer/-/deps.graph-sequencer-1100.0.1.tgz", + "integrity": "sha512-pOr5+q1fLYKwFN3LAJuGZEnfXDcQ73zqgDHMtGy+K+uIoUqyY+6MeDCWFwfu+4EFuq76I5EPFofoNAI+Bmmq4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.13" + }, + "funding": { + "url": "https://opencollective.com/pnpm" + } + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", @@ -2413,6 +2769,16 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/cac": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cac/-/cac-7.1.0.tgz", + "integrity": "sha512-LMDU/U2yA4IPIY8KT0NsNPWEYZvAMkbgyspxJ6btJaYV+WMDOHAZCmB0lsTObiHaRS1V/ztKAl7+TSkTqXyeoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, "node_modules/call-me-maybe": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", @@ -3100,6 +3466,33 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -3265,6 +3658,16 @@ "hermes-estree": "0.25.1" } }, + "node_modules/human-id": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/human-id/-/human-id-4.2.2.tgz", + "integrity": "sha512-Z+8unD6em/MJwyrkWQ2M3wygRGI1+MztbutiNwk1DCuEPXOZRWGjtcMPZYP1exY9iNZMhd7ZOSMQSo9WYtiQRw==", + "dev": true, + "license": "MIT", + "bin": { + "human-id": "dist/cli.js" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3292,6 +3695,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -3378,6 +3792,13 @@ "dev": true, "license": "ISC" }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true, + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3455,6 +3876,13 @@ "node": ">=6" } }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -3465,6 +3893,17 @@ "json-buffer": "3.0.1" } }, + "node_modules/launch-editor": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.14.2.tgz", + "integrity": "sha512-NHJEb30MGBCkmZXLZwz25t37QR4aY03ZzPYUJXOJaOa92uWwExI9SBQUuIznBYc1xkD7cJNGX7oc3Nl3sq2kQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.1.1", + "shell-quote": "^1.10.0" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -3972,6 +4411,13 @@ "node": ">=6" } }, + "node_modules/package-manager-detector": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.9.0.tgz", + "integrity": "sha512-zAMfbta9nPA8AKkA4WdmNAT8R8s6Dk0/xwPsqNnRidMFcLo1YkLuPDTgs2f64XdzS8vbLNnoJ+uUIkgbz7oRHA==", + "dev": true, + "license": "MIT" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -4334,6 +4780,19 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.12.0.tgz", + "integrity": "sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -4341,6 +4800,16 @@ "dev": true, "license": "ISC" }, + "node_modules/sisteransi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-2.0.0.tgz", + "integrity": "sha512-2WCqLdt12eN6ehLI50S2/ZxoowXU3DD1KVPRyVP0WEsFahrGZZmCdq1K59qjJljMRIDTwx1D9YaRK+hDneBUMg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/source-map-js": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", @@ -4450,9 +4919,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", - "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", "dev": true, "license": "MIT", "engines": { @@ -5045,6 +5514,22 @@ "dev": true, "license": "ISC" }, + "node_modules/yaml": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", + "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", diff --git a/package.json b/package.json index 38dd4d8..f7a48c5 100644 --- a/package.json +++ b/package.json @@ -18,20 +18,18 @@ "dev:frontend": "cd example && vite", "dev:build": "chokidar 'tsconfig*.json' 'src/**/*.ts' -i '**/*.test.ts' -c 'npm run build:codegen' --initial", "predev": "convex init && npm run build:codegen", - "prepare": "npm run build", "build": "tsc --project ./tsconfig.build.json", + "prepack": "npm run build", "build:codegen": "npx convex codegen --component-dir ./src/component && npm run build", "build:clean": "rm -rf dist *.tsbuildinfo && npx convex init && npm run build:codegen", "typecheck": "tsc --noEmit && tsc -p example && tsc -p example/convex", "lint": "eslint .", "test": "vitest run --typecheck", + "check": "npm run test && npm run typecheck && npm run lint", + "test:e2e": "convex dev --once --typecheck-components", "test:watch": "vitest --typecheck --clearScreen false", "test:debug": "vitest --inspect-brk --no-file-parallelism", - "test:coverage": "vitest run --coverage --coverage.reporter=text", - "preversion": "npm ci && npm run build:clean && npm run test && npm run lint && npm run typecheck", - "alpha": "npm version prerelease --preid alpha && npm publish --tag alpha && git push --follow-tags", - "release": "npm version patch && npm publish && git push --follow-tags", - "version": "(npm whoami || npm login) && vim -c 'normal o' -c 'normal o## '$npm_package_version CHANGELOG.md && prettier -w CHANGELOG.md && git add CHANGELOG.md" + "test:coverage": "vitest run --coverage --coverage.reporter=text" }, "files": [ "dist", @@ -87,7 +85,8 @@ "typescript": "6.0.3", "typescript-eslint": "8.58.2", "vite": "8.1.5", - "vitest": "4.1.4" + "vitest": "4.1.4", + "@changesets/cli": "3.0.3" }, "types": "./dist/client/index.d.ts", "module": "./dist/client/index.js", From b0231f5815d07bf6c0fe9fc172d1d7be24eba779 Mon Sep 17 00:00:00 2001 From: Ian Macartney <366683+ianmacartney@users.noreply.github.com> Date: Tue, 10 Nov 2026 19:20:05 -0800 Subject: [PATCH 2/4] Validate manual version PRs and existing tags --- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 2 +- package.json | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5d23acd..cb737e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -84,11 +84,11 @@ jobs: NODE tag="v$VERSION" if git show-ref --verify --quiet "refs/tags/$tag"; then + [[ "$(git rev-parse "$tag^{commit}")" == "$source" ]] || { echo "$tag points elsewhere" >&2; exit 1; } if [[ "$GITHUB_EVENT_NAME" != workflow_dispatch ]]; then echo "$tag was already staged; skipping automatic release" exit 0 fi - [[ "$(git rev-parse "$tag^{commit}")" == "$source" ]] || { echo "$tag points elsewhere" >&2; exit 1; } fi { echo "sha=$source" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dd03aab..3e0e29e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,7 +40,7 @@ jobs: - name: Validate release notes when the proposed merge changes the version env: BASE_SHA: - ${{ github.event.pull_request.base.sha || github.event.before }} + ${{ github.event.pull_request.base.sha || github.event.before || 'origin/main' }} run: | version=$(jq -r .version package.json) previous=$(git show "$BASE_SHA:package.json" | jq -r .version) diff --git a/package.json b/package.json index f7a48c5..7f09dcf 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "predev": "convex init && npm run build:codegen", "build": "tsc --project ./tsconfig.build.json", "prepack": "npm run build", + "prepare": "npm run build", "build:codegen": "npx convex codegen --component-dir ./src/component && npm run build", "build:clean": "rm -rf dist *.tsbuildinfo && npx convex init && npm run build:codegen", "typecheck": "tsc --noEmit && tsc -p example && tsc -p example/convex", From 9c741ed79752a28d39d2ffc546c71c9d4a859f60 Mon Sep 17 00:00:00 2001 From: Ian Macartney <366683+ianmacartney@users.noreply.github.com> Date: Tue, 10 Nov 2026 19:20:51 -0800 Subject: [PATCH 3/4] Format manual release check trigger --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3e0e29e..3b1a0c1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -40,7 +40,8 @@ jobs: - name: Validate release notes when the proposed merge changes the version env: BASE_SHA: - ${{ github.event.pull_request.base.sha || github.event.before || 'origin/main' }} + ${{ github.event.pull_request.base.sha || github.event.before || + 'origin/main' }} run: | version=$(jq -r .version package.json) previous=$(git show "$BASE_SHA:package.json" | jq -r .version) From 0411fb971b76fdec442ecb89d06ceb3332e524de Mon Sep 17 00:00:00 2001 From: Ian Macartney <366683+ianmacartney@users.noreply.github.com> Date: Tue, 10 Nov 2026 22:27:02 -0800 Subject: [PATCH 4/4] Keep release-note validation on selected release commit --- .github/workflows/test.yml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3b1a0c1..9b5b09d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,20 +37,6 @@ jobs: run: | npm ci npm run build - - name: Validate release notes when the proposed merge changes the version - env: - BASE_SHA: - ${{ github.event.pull_request.base.sha || github.event.before || - 'origin/main' }} - run: | - version=$(jq -r .version package.json) - previous=$(git show "$BASE_SHA:package.json" | jq -r .version) - if [[ "$version" != "$previous" ]]; then - git update-ref refs/heads/main HEAD - npx changeset status --output "$RUNNER_TEMP/changesets.json" - jq -e '.changesets | length == 0' "$RUNNER_TEMP/changesets.json" - awk -v version="$version" '/^## / { active=($0 == "## " version); next } active && !/^#/ && /[[:alnum:]]/ { found=1 } END { exit !found }' CHANGELOG.md - fi - name: Test run: npm run check - name: Run E2E smoke test and verify generated code