diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 30472d82e..758fc03fb 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -12,77 +12,62 @@ // See the License for the specific language governing permissions and // limitations under the License. -const { readdirSync, existsSync } = require("fs"); -const { join } = require("path"); - module.exports = { env: { browser: true, es2021: true, node: true, }, + root: true, ignorePatterns: [ - "packages/connect-web-bench/src/gen/grpcweb/**/*", - "packages/connect/src/protocol-grpc/gen/**/*", - "packages/connect-node-test/connect-node-h1-server.mjs", // https://github.com/eslint/eslint/issues/14156 - "packages/example/www/webclient.js", "packages/*/dist/**", - "node_modules/**", + // Our ESLint setup assumes all `.js` files are ESM, however these particular assets are CommonJS. + // Since for these files we cannot use `.cjs`, instead we override here to avoid having to override in each file + "packages/connect/*.js", + // + "packages/connect-web-bench/src/gen/grpcweb/**/*", ], plugins: ["@typescript-eslint", "n", "import"], // Rules and settings that do not require a non-default parser extends: ["eslint:recommended"], rules: { "no-console": "error", + "import/no-cycle": "error", + "import/no-duplicates": "error", }, - settings: {}, overrides: [ - ...readdirSync("packages", { withFileTypes: true }) - .filter((entry) => entry.isDirectory()) - .map((entry) => join("packages", entry.name)) - .filter((dir) => existsSync(join(dir, "tsconfig.json"))) - .map((dir) => { - return { - files: [join(dir, "src/**/*.ts"), join(dir, "conformance/**/*.ts")], - parser: "@typescript-eslint/parser", - parserOptions: { - project: "./tsconfig.json", - tsconfigRootDir: dir, - }, - settings: { - "import/resolver": { - typescript: { - project: "packages/*/tsconfig.json", - }, - }, - }, - extends: [ - "plugin:@typescript-eslint/recommended", - "plugin:@typescript-eslint/recommended-requiring-type-checking", - "plugin:import/recommended", - "plugin:import/typescript", - ], - rules: { - "@typescript-eslint/strict-boolean-expressions": "error", - "@typescript-eslint/no-unnecessary-condition": "error", - // we use complex typings, where Array is actually more readable than T[] - "@typescript-eslint/array-type": "off", - "@typescript-eslint/switch-exhaustiveness-check": "error", - "@typescript-eslint/prefer-nullish-coalescing": "error", - "@typescript-eslint/no-unnecessary-boolean-literal-compare": - "error", - "@typescript-eslint/no-invalid-void-type": "error", - "@typescript-eslint/no-base-to-string": "error", - "import/no-cycle": "error", - "import/no-duplicates": "error", - // TS 4.5 adds type modifiers on import names, but we want to support lower versions - "import/consistent-type-specifier-style": [ - "error", - "prefer-top-level", - ], + { + files: ["**/*.{ts,tsx,cts,mts}"], + parser: "@typescript-eslint/parser", + parserOptions: { + project: true, + }, + settings: { + "import/resolver": { + typescript: { + project: "tsconfig.json", }, - }; - }), + }, + }, + extends: [ + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking", + "plugin:import/recommended", + "plugin:import/typescript", + ], + rules: { + "@typescript-eslint/strict-boolean-expressions": "error", + "@typescript-eslint/no-unnecessary-condition": "error", + "@typescript-eslint/array-type": "off", // we use complex typings, where Array is actually more readable than T[] + "@typescript-eslint/switch-exhaustiveness-check": "error", + "@typescript-eslint/prefer-nullish-coalescing": "error", + "@typescript-eslint/no-unnecessary-boolean-literal-compare": "error", + "@typescript-eslint/no-invalid-void-type": "error", + "@typescript-eslint/no-base-to-string": "error", + "import/no-cycle": "error", + "import/no-duplicates": "error", + }, + }, // For scripts and configurations, use Node.js rules { files: ["**/*.{js,mjs,cjs}"], @@ -106,14 +91,5 @@ module.exports = { "n/no-unsupported-features/es-syntax": "error", }, }, - { - // Our ESLint setup assumes all `.js` files are ESM, however these particular assets are CommonJS. - // Since for these files we cannot use `.cjs`, instead we override here to avoid having to override in each file - files: ["packages/connect/*.js"], - globals: { - module: "readonly", - require: "readonly", - }, - }, ], }; diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 052275684..594be9495 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -25,13 +25,22 @@ git remote add upstream https://github.com/connectrpc/connect-es.git git fetch upstream ``` -Make sure that the tests and the linters pass (you'll need Node.js in the -version specified in .nvmrc, `make`, `bash` and Docker installed): +Install dependencies (you'll need Node.js in the version specified in `.nvmrc`, +and `npm` in the version specified in `package.json`): +```bash +npm ci ``` -make + +Make sure that the tests, linters, and other checks pass: + +```bash +npm run all ``` +We're using `turborepo` to run tasks. If you haven't used it yet, take a look at +[filtering and package scoping](https://turbo.build/repo/docs/crafting-your-repository/running-tasks). + ## Making Changes Start by creating a new branch for your changes: @@ -53,16 +62,16 @@ git push origin cool_new_feature Then use the GitHub UI to open a pull request. -At this point, you're waiting on us to review your changes. We *try* to respond +At this point, you're waiting on us to review your changes. We _try_ to respond to issues and pull requests within a few business days, and we may suggest some improvements or alternatives. Once your changes are approved, one of the project maintainers will merge them. We're much more likely to approve your changes if you: -* Add tests for new functionality. -* Write a [good commit message][commit-message]. -* Maintain backward compatibility. +- Add tests for new functionality. +- Write a [good commit message][commit-message]. +- Maintain backward compatibility. [fork]: https://github.com/connectrpc/connect-es/fork [open-issue]: https://github.com/connectrpc/connect-es/issues/new diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 36cc00c8c..6163721a4 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,40 +1,38 @@ --- name: Bug report about: Let us know about a bug -title: '' +title: "" labels: bug -assignees: '' - +assignees: "" --- **Describe the bug** As clearly as you can, please tell us what the bug is. - **To Reproduce** If you encountered an error message, please copy and paste it verbatim. If the bug is specific to an RPC or payload, please provide a reduced example. - **Environment (please complete the following information):** + - @connectrpc/connect-web version: (for example, `0.1.0`) - @connectrpc/connect-node version: (for example, `0.1.0`) - Frontend framework and version: (for example, `react@18.2.0`) - Node.js version: (for example, `18.0.0`) - Browser and version: (for example, `Google Chrome 103.0.5060.134`) -If your problem is specific to bundling, please also provide the following information: +If your problem is specific to bundling, please also provide the following information: - Bundler and version: (for example, `webpack@5.74.0`) - Bundler plugins and version: (for example `compression-webpack-plugin@10.0.0`) -- Bundler configuration file: +- Bundler configuration file: + ```js ``` - **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index ef92c7050..ca4cd5e2e 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,10 +1,9 @@ --- name: Feature request about: Suggest a new feature or improvement -title: '' +title: "" labels: enhancement -assignees: '' - +assignees: "" --- **Is your feature request related to a problem? Please describe.** diff --git a/.github/RELEASING.md b/.github/RELEASING.md index 2d2c1e75c..7c1775611 100644 --- a/.github/RELEASING.md +++ b/.github/RELEASING.md @@ -3,22 +3,22 @@ ## Prerequisites - See the setup and tools required in CONTRIBUTING.md -- A granular access token for npmjs.com with read and write permissions, scoped - to the `connectrpc` organization. -- Make sure that the repository is in a good state, without PRs close to merge +- A granular access token for npmjs.com with read and write permissions, scoped + to the `connectrpc` organization. +- Make sure that the repository is in a good state, without PRs close to merge that would ideally be part of the release. ## Steps -1. Choose a new version (e.g. 1.2.3), making sure to follow semver. Note that all +1. Choose a new version (e.g. 1.2.3), making sure to follow semver. Note that all packages in this repository use the same version number. 2. Make sure you are on the latest main, and create a new git branch. -3. Set the new version across all packages within the monorepo with the following - command: `make setversion SET_VERSION=1.2.3` +3. Set the new version across all packages within the monorepo with the following + command: `npm run setversion 1.2.3` 4. Commit, push, and open a pull request with the title "Release 1.2.3". The PR title must start with "Release" to trigger the conformance tests in CI. See the conformance tests [README](/packages/connect-conformance/README.md) for more details. 5. Edit the PR description with release notes. See the section below for details. 6. Make sure CI passed on your PR and ask a maintainer for review. -7. After approval, run the following command to publish to npmjs.com: `make release`. +7. After approval, run the following command to publish to npmjs.com: `npm run release`. 8. Merge your PR. 9. Create a new release in the GitHub UI - Choose "v1.2.3" as a tag and as the release title. @@ -27,19 +27,19 @@ ## Release notes -- We generate release notes with the GitHub feature, see +- We generate release notes with the GitHub feature, see https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes -- Only changes that impact users should be listed. No need to list things like +- Only changes that impact users should be listed. No need to list things like doc changes (unless it’s something major), dependency version bumps, or similar. Remove them from the generated release notes. -- If the release introduces a major new feature or change, add a section at the +- If the release introduces a major new feature or change, add a section at the top that explains it for users. A good example is https://github.com/connectrpc/connect-es/releases/tag/v0.10.0 - It lists a major new feature and a major change with dedicated sections, and + It lists a major new feature and a major change with dedicated sections, and moves the changelist with PR links to a separate "Enhancement" section below. -- If the release includes a very long list of changes, consider breaking the +- If the release includes a very long list of changes, consider breaking the changelist up with the sections "Enhancements", "Bugfixes", "Breaking changes". A good example is https://github.com/connectrpc/connect-es/releases/tag/v0.9.0 -- If the release includes changes specific to a npm package, group and explain +- If the release includes changes specific to a npm package, group and explain the changelist in according separate sections. A good example is https://github.com/connectrpc/connect-es/releases/tag/v0.8.0 Note that we are not using full package names with scope - a more user-friendly name like "Connect for Node.js" or "Connect for Fastify" is preferable. diff --git a/.github/workflows/browserstack.yaml b/.github/workflows/browserstack.yaml index 145bd857c..8ef001f33 100644 --- a/.github/workflows/browserstack.yaml +++ b/.github/workflows/browserstack.yaml @@ -1,31 +1,39 @@ -name: browserstack +name: "Browserstack" + on: push: - branches: [main] - tags: ['v*'] + branches: [main, "v*"] + tags: ["v*"] pull_request: - branches: [main] + branches: [main, "v*"] workflow_dispatch: + permissions: contents: read + +env: + # https://consoledonottrack.com/ + DO_NOT_TRACK: 1 + jobs: browserstack: + name: "Test @connectrpc/connect-web" runs-on: ubuntu-22.04 + timeout-minutes: 10 steps: - - name: checkout - uses: actions/checkout@v4 - - name: cache - uses: actions/cache@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + - uses: actions/cache@v4 with: - path: | - ~/.tmp - .tmp - key: ${{ runner.os }}-connect-web-browserstack-${{ hashFiles('Makefile') }} - restore-keys: | - ${{ runner.os }}-connect-web-browserstack- - - name: browserstack + path: .turbo + key: ${{ runner.os }}/browserstack/${{ github.sha }} + restore-keys: ${{ runner.os }}/browserstack + - name: NPM Install + run: npm ci + - name: Browserstack env: BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} - run: make testwebbrowserstack - + run: npx turbo run test-browserstack --output-logs new-only --log-order stream diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index eb06b5eab..32191db28 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,29 +1,97 @@ name: ci + on: push: - branches: [main] - tags: ['v*'] + branches: [main, "v*"] + tags: ["v*"] pull_request: - branches: [main] + branches: [main, "v*"] workflow_dispatch: + permissions: contents: read + +env: + # https://consoledonottrack.com/ + DO_NOT_TRACK: 1 + jobs: - ci: + tasks: runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + task: + - format + - license-header + - lint + - attw + - bundle-size + - build + include: + - task: format + diff-check: true + - task: license-header + diff-check: true + - task: bundle-size + diff-check: true + name: ${{ matrix.task }} steps: - - name: checkout - uses: actions/checkout@v4 - - name: cache - uses: actions/cache@v4 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - path: | - ~/.tmp - .tmp - key: ${{ runner.os }}-connect-es-ci-${{ hashFiles('Makefile') }} - restore-keys: | - ${{ runner.os }}-connect-es-ci- - - name: make - run: make ci + node-version-file: .nvmrc + cache: "npm" + - uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}/${{ matrix.task }}/${{ github.sha }} + restore-keys: ${{ runner.os }}/${{ matrix.task }} + - run: npm ci + - run: npx turbo run ${{ matrix.task }} - name: Check changed files + if: ${{ matrix.diff-check }} run: node scripts/gh-diffcheck.js + test: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + # Node.js v16 does not provide all necessary fetch types by default, so + # we cannot run tests for @connectrpc/connect-web. + # v16 is tested in a separate job below. + node-version: [21.1.0, 20.9.0, 18.16.0] + name: "test on Node.js ${{ matrix.node-version }}" + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + cache: "npm" + - uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}/test/${{ github.sha }} + restore-keys: ${{ runner.os }}/test + - run: npm ci + - run: npx turbo run test + test16: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + node-version: [16.20.0] + name: "test on Node.js ${{ matrix.node-version }}" + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + cache: "npm" + - uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}/test/${{ github.sha }} + restore-keys: ${{ runner.os }}/test + - run: npm ci + - run: npx turbo run test --filter '!@connectrpc/connect-web' diff --git a/.github/workflows/cloudflare.yaml b/.github/workflows/cloudflare.yaml deleted file mode 100644 index 8df7a6d9b..000000000 --- a/.github/workflows/cloudflare.yaml +++ /dev/null @@ -1,36 +0,0 @@ -name: Cloudflare Conformance Tests -on: - schedule: - - cron: "0 0 * * *" - push: - tags: ["v*"] - pull_request: - branches: [main] - workflow_dispatch: -jobs: - test: - if: github.event_name != 'pull_request' || startsWith(github.event.pull_request.title, 'Release ') - runs-on: conformance - steps: - - uses: actions/checkout@v4 - - name: cache - uses: actions/cache@v4 - with: - path: | - ~/.tmp - .tmp - key: ${{ runner.os }}-connect-conformance-ci-${{ hashFiles('Makefile') }} - restore-keys: | - ${{ runner.os }}-connect-conformance-ci- - - name: build - run: make .tmp/build/connect-conformance - - name: test - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} - CLOUDFLARE_WORKERS_SERVER_HOST: ${{ vars.CLOUDFLARE_WORKERS_SERVER_HOST}} - CLOUDFLARE_WORKERS_CLIENT_HOST: ${{ vars.CLOUDFLARE_WORKERS_CLIENT_HOST}} - CLOUDFLARE_WORKERS_REFERENCE_SERVER_HOST: ${{ vars.CLOUDFLARE_WORKERS_REFERENCE_SERVER_HOST}} - CLOUDFLARE_WORKERS_REFERENCE_SERVER_KEY: ${{ vars.CLOUDFALRE_WORKERS_REFERENCE_SERVER_KEY }} - CLOUDFLARE_WORKERS_REFERENCE_SERVER_CERT: ${{ vars.CLOUDFALRE_WORKERS_REFERENCE_SERVER_CERT }} - TEMP: .tmp/build - run: make testcloudflareconformance diff --git a/.github/workflows/conformance-cloudflare.yaml b/.github/workflows/conformance-cloudflare.yaml new file mode 100644 index 000000000..08387e502 --- /dev/null +++ b/.github/workflows/conformance-cloudflare.yaml @@ -0,0 +1,50 @@ +name: "Cloudflare Conformance" + +on: + schedule: + - cron: "0 0 * * *" + push: + tags: ["v*"] + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +env: + # https://consoledonottrack.com/ + DO_NOT_TRACK: 1 + +jobs: + test: + name: "Test @connectrpc/connect-cloudflare" + if: github.event_name != 'pull_request' || startsWith(github.event.pull_request.title, 'Release ') + runs-on: conformance + timeout-minutes: 20 + steps: + - uses: actions/checkout@v4 + - uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}/cloudflare-conformance/${{ github.sha }} + restore-keys: ${{ runner.os }}/cloudflare-conformance + - run: npm ci + - name: conformance:server + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_WORKERS_SERVER_HOST: ${{ vars.CLOUDFLARE_WORKERS_SERVER_HOST}} + CLOUDFLARE_WORKERS_CLIENT_HOST: ${{ vars.CLOUDFLARE_WORKERS_CLIENT_HOST}} + CLOUDFLARE_WORKERS_REFERENCE_SERVER_HOST: ${{ vars.CLOUDFLARE_WORKERS_REFERENCE_SERVER_HOST}} + CLOUDFLARE_WORKERS_REFERENCE_SERVER_KEY: ${{ vars.CLOUDFALRE_WORKERS_REFERENCE_SERVER_KEY }} + CLOUDFLARE_WORKERS_REFERENCE_SERVER_CERT: ${{ vars.CLOUDFALRE_WORKERS_REFERENCE_SERVER_CERT }} + run: npx turbo run conformance:server --filter '@connectrpc/connect-cloudflare' --output-logs new-only --log-order stream + - name: conformance:client + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_WORKERS_SERVER_HOST: ${{ vars.CLOUDFLARE_WORKERS_SERVER_HOST}} + CLOUDFLARE_WORKERS_CLIENT_HOST: ${{ vars.CLOUDFLARE_WORKERS_CLIENT_HOST}} + CLOUDFLARE_WORKERS_REFERENCE_SERVER_HOST: ${{ vars.CLOUDFLARE_WORKERS_REFERENCE_SERVER_HOST}} + CLOUDFLARE_WORKERS_REFERENCE_SERVER_KEY: ${{ vars.CLOUDFALRE_WORKERS_REFERENCE_SERVER_KEY }} + CLOUDFLARE_WORKERS_REFERENCE_SERVER_CERT: ${{ vars.CLOUDFALRE_WORKERS_REFERENCE_SERVER_CERT }} + run: npx turbo run conformance:client --filter '@connectrpc/connect-cloudflare' --output-logs new-only --log-order stream diff --git a/.github/workflows/conformance-express.yaml b/.github/workflows/conformance-express.yaml new file mode 100644 index 000000000..77533b0b0 --- /dev/null +++ b/.github/workflows/conformance-express.yaml @@ -0,0 +1,39 @@ +name: "Express Conformance" + +on: + push: + branches: [main, "v*"] + tags: ["v*"] + pull_request: + branches: [main, "v*"] + workflow_dispatch: + +permissions: + contents: read + +env: + # https://consoledonottrack.com/ + DO_NOT_TRACK: 1 + +jobs: + node: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + node-version: [21.1.0, 20.9.0, 18.16.0] + name: "Node.js ${{ matrix.node-version }}" + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + - uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}/express-conformance/${{ github.sha }} + restore-keys: ${{ runner.os }}/express-conformance + - run: npm ci + - run: npx turbo run conformance --filter '@connectrpc/connect-express' --output-logs new-only --log-order stream diff --git a/.github/workflows/conformance-fastify.yaml b/.github/workflows/conformance-fastify.yaml new file mode 100644 index 000000000..cd903cb56 --- /dev/null +++ b/.github/workflows/conformance-fastify.yaml @@ -0,0 +1,39 @@ +name: "Fastify Conformance" + +on: + push: + branches: [main, "v*"] + tags: ["v*"] + pull_request: + branches: [main, "v*"] + workflow_dispatch: + +permissions: + contents: read + +env: + # https://consoledonottrack.com/ + DO_NOT_TRACK: 1 + +jobs: + node: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + node-version: [21.1.0, 20.9.0, 18.16.0] + name: "Node.js ${{ matrix.node-version }}" + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + - uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}/fastify-conformance/${{ github.sha }} + restore-keys: ${{ runner.os }}/fastify-conformance + - run: npm ci + - run: npx turbo run conformance --filter '@connectrpc/connect-fastify' --output-logs new-only --log-order stream diff --git a/.github/workflows/conformance-node.yaml b/.github/workflows/conformance-node.yaml new file mode 100644 index 000000000..bcec39217 --- /dev/null +++ b/.github/workflows/conformance-node.yaml @@ -0,0 +1,40 @@ +name: "Node.js Conformance" + +on: + push: + branches: [main, "v*"] + tags: ["v*"] + pull_request: + branches: [main, "v*"] + workflow_dispatch: + +permissions: + contents: read + +env: + # https://consoledonottrack.com/ + DO_NOT_TRACK: 1 + +jobs: + node: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + node-version: [21.1.0, 20.9.0, 18.16.0] + side: [client, server] + name: "Node.js ${{ matrix.node-version }} ${{ matrix.side }}" + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + - uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}/node-conformance/${{ github.sha }} + restore-keys: ${{ runner.os }}/node-conformance + - run: npm ci + - run: npx turbo run conformance:${{matrix.side}} --filter '@connectrpc/connect-node' --output-logs new-only --log-order stream diff --git a/.github/workflows/conformance-web.yaml b/.github/workflows/conformance-web.yaml new file mode 100644 index 000000000..a0a1afa23 --- /dev/null +++ b/.github/workflows/conformance-web.yaml @@ -0,0 +1,66 @@ +name: "Web Conformance" + +on: + push: + branches: [main, "v*"] + tags: ["v*"] + pull_request: + branches: [main, "v*"] + workflow_dispatch: + +permissions: + contents: read + +env: + # https://consoledonottrack.com/ + DO_NOT_TRACK: 1 + +jobs: + browser: + strategy: + fail-fast: false + matrix: + browser: [chrome, safari, firefox] + client: [promise, callback] + include: + - os: ubuntu-22.04 + - browser: safari + os: macos-12 + runs-on: ${{ matrix.os }} + name: "${{ matrix.browser }} ${{ matrix.client }}" + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version-file: .nvmrc + cache: "npm" + - uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}/web-conformance/${{ github.sha }} + restore-keys: ${{ runner.os }}/web-conformance + - run: npm ci + - run: npx turbo run conformance:${{matrix.browser}}:${{matrix.client}} --filter '@connectrpc/connect-web' --output-logs new-only --log-order stream + node: + runs-on: ubuntu-22.04 + strategy: + matrix: + # Node.js v16 does not provide all necessary fetch types by default + node-version: [21.1.0, 20.9.0, 18.16.0] + client: [promise, callback] + name: "Node.js ${{ matrix.node-version }} ${{ matrix.client }}" + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: "npm" + - uses: actions/cache@v4 + with: + path: .turbo + key: ${{ runner.os }}/web-conformance/${{ github.sha }} + restore-keys: ${{ runner.os }}/web-conformance + - run: npm ci + - run: npx turbo run conformance:node:${{matrix.client}} --filter '@connectrpc/connect-web' --output-logs new-only --log-order stream diff --git a/.github/workflows/node-conformance.yaml b/.github/workflows/node-conformance.yaml deleted file mode 100644 index 44eefbacb..000000000 --- a/.github/workflows/node-conformance.yaml +++ /dev/null @@ -1,77 +0,0 @@ -name: Node Conformance -on: - push: - branches: [main] - tags: ['v*'] - pull_request: - branches: [main] - workflow_dispatch: -permissions: - contents: read -jobs: - node: - runs-on: ubuntu-22.04 - strategy: - matrix: - node-version: [18, 20, 21] - steps: - - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - name: checkout - uses: actions/checkout@v4 - - name: cache - uses: actions/cache@v4 - with: - path: | - ~/.tmp - .tmp - key: ${{ runner.os }}-connect-node-conformance-${{ hashFiles('Makefile') }} - restore-keys: | - ${{ runner.os }}-connect-node-conformance- - - name: testconnectnodeconformance - run: make testconnectnodeconformance - fastify: - runs-on: ubuntu-22.04 - strategy: - matrix: - node-version: [18, 20, 21] - steps: - - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - name: checkout - uses: actions/checkout@v4 - - name: cache - uses: actions/cache@v4 - with: - path: | - ~/.tmp - .tmp - key: ${{ runner.os }}-connect-fastify-conformance-${{ hashFiles('Makefile') }} - restore-keys: | - ${{ runner.os }}-connect-fastify-conformance- - - name: testconnectfastifyconformance - run: make testconnectfastifyconformance - express: - runs-on: ubuntu-22.04 - strategy: - matrix: - node-version: [18, 20, 21] - steps: - - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - name: checkout - uses: actions/checkout@v4 - - name: cache - uses: actions/cache@v4 - with: - path: | - ~/.tmp - .tmp - key: ${{ runner.os }}-connect-express-conformance-${{ hashFiles('Makefile') }} - restore-keys: | - ${{ runner.os }}-connect-express-conformance- - - name: testconnectexpressconformance - run: make testconnectexpressconformance diff --git a/.github/workflows/web-conformance.yaml b/.github/workflows/web-conformance.yaml deleted file mode 100644 index 0dd28b076..000000000 --- a/.github/workflows/web-conformance.yaml +++ /dev/null @@ -1,78 +0,0 @@ -name: Web Conformance -on: - push: - branches: [main] - tags: ['v*'] - pull_request: - branches: [main] - workflow_dispatch: -permissions: - contents: read -jobs: - chrome: - runs-on: ubuntu-22.04 - steps: - - name: checkout - uses: actions/checkout@v4 - - name: cache - uses: actions/cache@v4 - with: - path: | - ~/.tmp - .tmp - key: ${{ runner.os }}-connect-web-chrome-conformance-${{ hashFiles('Makefile') }} - restore-keys: | - ${{ runner.os }}-connect-web-chrome-conformance- - - name: testwebchromeconformance - run: make testwebchromeconformance - firefox: - runs-on: ubuntu-22.04 - steps: - - name: checkout - uses: actions/checkout@v4 - - name: cache - uses: actions/cache@v4 - with: - path: | - ~/.tmp - .tmp - key: ${{ runner.os }}-connect-web-firefox-conformance-${{ hashFiles('Makefile') }} - restore-keys: | - ${{ runner.os }}-connect-web-firefox-conformance- - - name: testwebfirefoxconformance - run: make testwebfirefoxconformance - safari: - runs-on: macos-12 - steps: - - name: checkout - uses: actions/checkout@v4 - - name: cache - uses: actions/cache@v4 - with: - path: | - ~/.tmp - .tmp - key: ${{ runner.os }}-connect-web-safari-conformance-${{ hashFiles('Makefile') }} - restore-keys: | - ${{ runner.os }}-connect-web-safari-conformance- - - name: testwebsafariconformance - run: make testwebsafariconformance - node: - runs-on: ubuntu-22.04 - steps: - - name: checkout - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - name: cache - uses: actions/cache@v4 - with: - path: | - ~/.tmp - .tmp - key: ${{ runner.os }}-connect-web-node-conformance-${{ hashFiles('Makefile') }} - restore-keys: | - ${{ runner.os }}-connect-web-node-conformance- - - name: testwebnodeconformance - run: make testwebnodeconformance diff --git a/.gitignore b/.gitignore index 2892551d5..98f977560 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ -/.tmp +.turbo +.wrangler +node_modules /packages/*/dist /packages/connect-web/conformance/logs/* -node_modules -.wrangler +packages/connect-web/local.log diff --git a/.nvmrc b/.nvmrc index 209e3ef4b..f3f52b42d 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20 +20.9.0 diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 42ae16adc..000000000 --- a/.prettierignore +++ /dev/null @@ -1,6 +0,0 @@ -node_modules -/packages/connect/src/protocol-grpc/gen/*.ts -/packages/connect-web/browserstack/gen/**/*.ts -/packages/*/dist -/packages/*/src/gen -/.tmp diff --git a/MAINTAINERS.md b/MAINTAINERS.md index bd8aa43c8..92fa3480b 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -1,8 +1,8 @@ -Maintainers -=========== +# Maintainers ## Current -* [Peter Edge](https://github.com/bufdev), [Buf](https://buf.build) -* [Timo Stamm](https://github.com/timostamm), [Buf](https://buf.build) -* [Steve Ayers](https://github.com/smaye81), [Buf](https://buf.build) -* [Sri Krishna Paritala](https://github.com/srikrsna-buf), [Buf](https://buf.build) + +- [Peter Edge](https://github.com/bufdev), [Buf](https://buf.build) +- [Timo Stamm](https://github.com/timostamm), [Buf](https://buf.build) +- [Steve Ayers](https://github.com/smaye81), [Buf](https://buf.build) +- [Sri Krishna Paritala](https://github.com/srikrsna-buf), [Buf](https://buf.build) diff --git a/Makefile b/Makefile deleted file mode 100644 index dbbcb0207..000000000 --- a/Makefile +++ /dev/null @@ -1,250 +0,0 @@ -# See https://tech.davis-hansson.com/p/make/ -SHELL := bash -.DELETE_ON_ERROR: -.SHELLFLAGS := -eu -o pipefail -c -.DEFAULT_GOAL := all -MAKEFLAGS += --warn-undefined-variables -MAKEFLAGS += --no-builtin-rules -MAKEFLAGS += --no-print-directory -TMP = .tmp -BIN = .tmp/bin -BUILD = .tmp/build -GEN = .tmp/gen -NODE21_VERSION ?= v21.1.0 -NODE20_VERSION ?= v20.9.0 -NODE18_VERSION ?= v18.16.0 -NODE16_VERSION ?= v16.20.0 -NODE_OS = $(subst Linux,linux,$(subst Darwin,darwin,$(shell uname -s))) -NODE_ARCH = $(subst x86_64,x64,$(subst aarch64,arm64,$(shell uname -m))) - -node_modules: package-lock.json - npm ci - -node_modules/.bin/protoc-gen-es: node_modules - -$(BIN)/node21: Makefile - @mkdir -p $(@D) - curl --retry 5 --retry-all-errors --http1.1 -sSL -o $(TMP)/$(NODE21_VERSION).tar.xz https://nodejs.org/dist/$(NODE21_VERSION)/node-$(NODE21_VERSION)-$(NODE_OS)-$(NODE_ARCH).tar.xz - tar xJf $(TMP)/$(NODE21_VERSION).tar.xz -C $(TMP) node-$(NODE21_VERSION)-$(NODE_OS)-$(NODE_ARCH)/bin/node - @mv $(TMP)/node-$(NODE21_VERSION)-$(NODE_OS)-$(NODE_ARCH)/bin/node $(@) - @rm -r $(TMP)/node-$(NODE21_VERSION)-$(NODE_OS)-$(NODE_ARCH) - @rm -r $(TMP)/$(NODE21_VERSION).tar.xz - @touch $(@) - -$(BIN)/node20: Makefile - @mkdir -p $(@D) - curl --retry 5 --retry-all-errors --http1.1 -sSL -o $(TMP)/$(NODE20_VERSION).tar.xz https://nodejs.org/dist/$(NODE20_VERSION)/node-$(NODE20_VERSION)-$(NODE_OS)-$(NODE_ARCH).tar.xz - tar xJf $(TMP)/$(NODE20_VERSION).tar.xz -C $(TMP) node-$(NODE20_VERSION)-$(NODE_OS)-$(NODE_ARCH)/bin/node - @mv $(TMP)/node-$(NODE20_VERSION)-$(NODE_OS)-$(NODE_ARCH)/bin/node $(@) - @rm -r $(TMP)/node-$(NODE20_VERSION)-$(NODE_OS)-$(NODE_ARCH) - @rm -r $(TMP)/$(NODE20_VERSION).tar.xz - @touch $(@) - -$(BIN)/node18: Makefile - @mkdir -p $(@D) - curl --retry 5 --retry-all-errors --http1.1 -sSL -o $(TMP)/$(NODE18_VERSION).tar.xz https://nodejs.org/dist/$(NODE18_VERSION)/node-$(NODE18_VERSION)-$(NODE_OS)-$(NODE_ARCH).tar.xz - tar xJf $(TMP)/$(NODE18_VERSION).tar.xz -C $(TMP) node-$(NODE18_VERSION)-$(NODE_OS)-$(NODE_ARCH)/bin/node - @mv $(TMP)/node-$(NODE18_VERSION)-$(NODE_OS)-$(NODE_ARCH)/bin/node $(@) - @rm -r $(TMP)/node-$(NODE18_VERSION)-$(NODE_OS)-$(NODE_ARCH) - @rm -r $(TMP)/$(NODE18_VERSION).tar.xz - @touch $(@) - -$(BIN)/node16: Makefile - @mkdir -p $(@D) - curl --retry 5 --retry-all-errors --http1.1 -sSL -o $(TMP)/$(NODE16_VERSION).tar.xz https://nodejs.org/dist/$(NODE16_VERSION)/node-$(NODE16_VERSION)-$(NODE_OS)-$(NODE_ARCH).tar.xz - tar xJf $(TMP)/$(NODE16_VERSION).tar.xz -C $(TMP) node-$(NODE16_VERSION)-$(NODE_OS)-$(NODE_ARCH)/bin/node - @mv $(TMP)/node-$(NODE16_VERSION)-$(NODE_OS)-$(NODE_ARCH)/bin/node $(@) - @rm -r $(TMP)/node-$(NODE16_VERSION)-$(NODE_OS)-$(NODE_ARCH) - @rm -r $(TMP)/$(NODE16_VERSION).tar.xz - @touch $(@) - -$(BUILD)/protoc-gen-connect-es: node_modules tsconfig.base.json packages/protoc-gen-connect-es/tsconfig.json $(shell find packages/protoc-gen-connect-es/src -name '*.ts') - npm run -w packages/protoc-gen-connect-es build - @mkdir -p $(@D) - @touch $(@) - -$(BUILD)/connect: $(GEN)/connect node_modules packages/connect/package.json packages/connect/scripts/* tsconfig.base.json packages/connect/tsconfig.json $(shell find packages/connect/src -name '*.ts') packages/connect/*.js - npm run -w packages/connect build - @mkdir -p $(@D) - @touch $(@) - -$(BUILD)/connect-node: $(BUILD)/connect $(BUILD)/connect-conformance packages/connect-node/tsconfig.json $(shell find packages/connect-node/src -name '*.ts') - npm run -w packages/connect-node build - @mkdir -p $(@D) - @touch $(@) - -$(BUILD)/connect-fastify: $(BUILD)/connect $(BUILD)/connect-node packages/connect-fastify/tsconfig.json $(shell find packages/connect-fastify/src -name '*.ts') - npm run -w packages/connect-fastify build - @mkdir -p $(@D) - @touch $(@) - -$(BUILD)/connect-express: $(BUILD)/connect $(BUILD)/connect-node packages/connect-express/tsconfig.json $(shell find packages/connect-express/src -name '*.ts') - npm run -w packages/connect-express build - @mkdir -p $(@D) - @touch $(@) - -$(BUILD)/connect-next: $(BUILD)/connect $(BUILD)/connect-node packages/connect-next/tsconfig.json $(shell find packages/connect-next/src -name '*.ts') - npm run -w packages/connect-next build - @mkdir -p $(@D) - @touch $(@) - -$(BUILD)/connect-web: $(GEN)/connect-web $(BUILD)/connect $(BUILD)/connect-conformance packages/connect-web/tsconfig.json $(shell find packages/connect-web/src -name '*.ts') - npm run -w packages/connect-web build - @mkdir -p $(@D) - @touch $(@) - -$(BUILD)/connect-conformance: $(GEN)/connect-conformance $(BUILD)/connect packages/connect-conformance/tsconfig.json $(shell find packages/connect-conformance/src -name '*.ts') - npm run -w packages/connect-conformance build - @mkdir -p $(@D) - @touch $(@) - -$(BUILD)/example: $(GEN)/example $(BUILD)/connect-web packages/example/tsconfig.json $(shell find packages/example/src -name '*.ts') - npm run -w packages/example lint - @mkdir -p $(@D) - @touch $(@) - -$(BUILD)/connect-migrate: packages/connect-migrate/package.json packages/connect-migrate/tsconfig.json $(shell find packages/connect-migrate/src -name '*.ts') - npm run -w packages/connect-migrate build - @mkdir -p $(@D) - @touch $(@) - -$(GEN)/connect: node_modules/.bin/protoc-gen-es packages/connect/buf.gen.yaml $(shell find packages/connect/src -name '*.proto') Makefile - npm run -w packages/connect generate - @mkdir -p $(@D) - @touch $(@) - -$(GEN)/connect-conformance: node_modules/.bin/protoc-gen-es $(BUILD)/protoc-gen-connect-es packages/connect-conformance/buf.gen.yaml packages/connect-conformance/package.json Makefile - npm run -w packages/connect-conformance generate - @mkdir -p $(@D) - @touch $(@) - -$(GEN)/connect-web: node_modules/.bin/protoc-gen-es $(BUILD)/protoc-gen-connect-es packages/connect-web/browserstack/buf.gen.yaml Makefile - npm run -w packages/connect-web generate - @mkdir -p $(@D) - @touch $(@) - -$(GEN)/connect-web-bench: node_modules/.bin/protoc-gen-es $(BUILD)/protoc-gen-connect-es packages/connect-web-bench/buf.gen.yaml - npm run -w packages/connect-web-bench generate - @mkdir -p $(@D) - @touch $(@) - -$(GEN)/example: node_modules/.bin/protoc-gen-es $(BUILD)/protoc-gen-connect-es packages/example/buf.gen.yaml $(shell find packages/example -name '*.proto') - npm run -w packages/example generate - @mkdir -p $(@D) - @touch $(@) - - -.PHONY: help -help: ## Describe useful make targets - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "%-30s %s\n", $$1, $$2}' - -.PHONY: all -all: build test testconformance format lint bench ## build, test, testconformance, format, lint, and bench (default) - -.PHONY: ci -ci: build test format lint bench ## build, test, format, lint, and bench (default) - -.PHONY: clean -clean: ## Delete build artifacts and installed dependencies - @# -X only removes untracked files, -d recurses into directories, -f actually removes files/dirs - git clean -Xdf - -.PHONY: build -build: $(BUILD)/connect $(BUILD)/connect-web $(BUILD)/connect-node $(BUILD)/connect-fastify $(BUILD)/connect-express $(BUILD)/connect-next $(BUILD)/protoc-gen-connect-es $(BUILD)/example $(BUILD)/connect-migrate ## Build - -.PHONY: test -test: testconnectpackage testconnectnodepackage testconnectwebpackage testconnectexpresspackage testconnectmigrate ## Run all tests, except browserstack - -.PHONY: testconnectpackage -testconnectpackage: $(BUILD)/connect - npm run -w packages/connect jasmine - -.PHONY: testconnectnodepackage -testconnectnodepackage: $(BIN)/node16 $(BIN)/node18 $(BIN)/node20 $(BIN)/node21 $(BUILD)/connect-node - cd packages/connect-node && PATH="$(abspath $(BIN)):$(PATH)" node16 --trace-warnings ../../node_modules/.bin/jasmine --config=jasmine.json - cd packages/connect-node && PATH="$(abspath $(BIN)):$(PATH)" node18 --trace-warnings ../../node_modules/.bin/jasmine --config=jasmine.json - cd packages/connect-node && PATH="$(abspath $(BIN)):$(PATH)" node20 --trace-warnings ../../node_modules/.bin/jasmine --config=jasmine.json - cd packages/connect-node && PATH="$(abspath $(BIN)):$(PATH)" node21 --trace-warnings ../../node_modules/.bin/jasmine --config=jasmine.json - -.PHONY: testconnectwebpackage -testconnectwebpackage: $(BIN)/node18 $(BIN)/node20 $(BIN)/node21 $(BUILD)/connect-web - cd packages/connect-web && PATH="$(abspath $(BIN)):$(PATH)" NODE_TLS_REJECT_UNAUTHORIZED=0 node18 ../../node_modules/.bin/jasmine --config=jasmine.json - cd packages/connect-web && PATH="$(abspath $(BIN)):$(PATH)" NODE_TLS_REJECT_UNAUTHORIZED=0 node20 ../../node_modules/.bin/jasmine --config=jasmine.json - cd packages/connect-web && PATH="$(abspath $(BIN)):$(PATH)" NODE_TLS_REJECT_UNAUTHORIZED=0 node21 ../../node_modules/.bin/jasmine --config=jasmine.json - -.PHONY: testconnectexpresspackage -testconnectexpresspackage: $(BUILD)/connect-express - npm run -w packages/connect-express jasmine - -.PHONY: testconformance -testconformance: testconnectnodeconformance testconnectfastifyconformance testconnectexpressconformance testwebconformance - -.PHONY: testconnectnodeconformance -testconnectnodeconformance: $(BUILD)/connect-node - # Vanilla Node Server and Client - npm run -w packages/connect-node conformance - -.PHONY: testconnectexpressconformance -testconnectexpressconformance: $(BUILD)/connect-express - # Express Server - npm run -w packages/connect-express conformance - -.PHONY: testconnectfastifyconformance -testconnectfastifyconformance: $(BUILD)/connect-fastify - # Fastify Server - npm run -w packages/connect-fastify conformance - -.PHONY: testwebconformance -testwebconformance: testwebchromeconformance testwebfirefoxconformance testwebsafariconformance testwebnodeconformance - -.PHONY: testwebchromeconformance -testwebchromeconformance: $(BUILD)/connect-web - npm run -w packages/connect-web conformance:client:chrome:promise - npm run -w packages/connect-web conformance:client:chrome:callback - -.PHONY: testwebfirefoxconformance -testwebfirefoxconformance: $(BUILD)/connect-web - npm run -w packages/connect-web conformance:client:firefox:promise - npm run -w packages/connect-web conformance:client:firefox:callback - -.PHONY: testwebsafariconformance -testwebsafariconformance: $(BUILD)/connect-web - npm run -w packages/connect-web conformance:client:safari:promise - npm run -w packages/connect-web conformance:client:safari:callback - -.PHONY: testwebnodeconformance -testwebnodeconformance: $(BUILD)/connect-web - npm run -w packages/connect-web conformance:client:node:promise - npm run -w packages/connect-web conformance:client:node:callback - -.PHONY: testcloudflareconformance -testcloudflareconformance: $(BUILD)/connect-conformance $(BUILD)/connect-node - npm run -w packages/connect-cloudflare conformance - -.PHONY: testwebbrowserstack -testwebbrowserstack: $(BUILD)/connect-web - npm run -w packages/connect-web karma:browserstack - -.PHONY: testconnectmigrate -testconnectmigrate: $(BUILD)/connect-migrate - npm run -w packages/connect-migrate test - -.PHONY: lint -lint: node_modules $(BUILD)/connect $(BUILD)/connect-express $(BUILD)/connect-fastify $(BUILD)/connect-next $(BUILD)/connect-node $(BUILD)/connect-web $(GEN)/connect-web-bench ## Lint all files - npx eslint --max-warnings 0 . - @# Check type exports on npm packages to verify they're correct - npm run -w packages/connect attw - npm run -w packages/connect-express attw - npm run -w packages/connect-fastify attw - npm run -w packages/connect-next attw - npm run -w packages/connect-node attw - npm run -w packages/connect-web attw - -.PHONY: format -format: node_modules ## Format all files, adding license headers - npx prettier --write '**/*.{json,js,jsx,ts,tsx,css,mjs,cjs}' --log-level error - npx license-header --ignore 'packages/*/src/gen/**/*' - -.PHONY: bench -bench: node_modules $(GEN)/connect-web-bench $(BUILD)/connect-web ## Benchmark code size - npm run -w packages/connect-web-bench bundle-size diff --git a/README.md b/README.md index 20a2008e7..7487521ee 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ service ElizaService { And with the magic of code generation, this schema produces servers and clients: ```ts -const answer = await eliza.say({sentence: "I feel happy."}); +const answer = await eliza.say({ sentence: "I feel happy." }); console.log(answer); // {sentence: 'When you feel happy, what do you do?'} ``` @@ -43,7 +43,6 @@ gRPC-web protocols, and Connect's [own protocol](https://connectrpc.com/docs/pro optimized for the web. This gives you unparalleled interoperability across many platforms and languages, with type-safety end-to-end. - ## Get started on the web Follow our [10 minute tutorial](https://connectrpc.com/docs/web/getting-started) where @@ -56,7 +55,6 @@ We support all modern web browsers that implement the widely available [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and the [Encoding API](https://developer.mozilla.org/en-US/docs/Web/API/Encoding_API). - ## Get started on Node.js Follow our [10 minute tutorial](https://connectrpc.com/docs/node/getting-started) @@ -67,7 +65,6 @@ You can serve your Connect RPCs with vanilla Node.js, or use our [server plugins for **Fastify**, **Next.js**, and **Express**. We support Node.js v16 and later with the builtin `http` and `http2` modules. - ## Other platforms Would you like to use Connect on other platforms like Bun, Deno, Vercel’s Edge Runtime, @@ -76,7 +73,6 @@ with Connect. You can reach us either through the [Buf Slack](https://buf.build/ or by filing a [GitHub issue](https://github.com/connectrpc/connect-es/issues) and we’d be more than happy to chat! - ## Packages - [@connectrpc/connect](https://www.npmjs.com/package/@connectrpc/connect): @@ -96,25 +92,23 @@ be more than happy to chat! The libraries and the generated code are compatible with ES2017 and TypeScript 4.1. - ## Ecosystem -* [examples-es](https://github.com/connectrpc/examples-es): +- [examples-es](https://github.com/connectrpc/examples-es): Examples for using Connect with various TypeScript web frameworks and tooling -* [connect-query-es](https://github.com/connectrpc/connect-query-es): +- [connect-query-es](https://github.com/connectrpc/connect-query-es): TypeScript-first expansion pack for TanStack Query that gives you Protobuf superpowers -* [connect-playwright-es](https://github.com/connectrpc/connect-playwright-es): +- [connect-playwright-es](https://github.com/connectrpc/connect-playwright-es): Playwright tests for your Connect application -* [connect-swift](https://github.com/connectrpc/connect-swift): +- [connect-swift](https://github.com/connectrpc/connect-swift): Idiomatic gRPC & Connect RPCs for Swift -* [connect-go](https://github.com/connectrpc/connect-go): +- [connect-go](https://github.com/connectrpc/connect-go): Go implementation of gRPC, gRPC-Web, and Connect -* [examples-go](https://github.com/connectrpc/examples-go): +- [examples-go](https://github.com/connectrpc/examples-go): Example RPC service powering https://demo.connectrpc.com and built with connect-go -* [conformance](https://github.com/connectrpc/conformance): +- [conformance](https://github.com/connectrpc/conformance): gRPC-Web and Connect interoperability tests -* [Buf Studio](https://buf.build/studio): web UI for ad-hoc RPCs - +- [Buf Studio](https://buf.build/studio): web UI for ad-hoc RPCs ## Status: Stable diff --git a/SECURITY.md b/SECURITY.md index 04dcde521..e15bc1420 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,5 +1,4 @@ -Security Policy -=============== +# Security Policy This project follows the [Connect security policy and reporting process](https://connectrpc.com/docs/governance/security). diff --git a/package-lock.json b/package-lock.json index 6a68f23ec..e7039dc04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,19 +4,20 @@ "requires": true, "packages": { "": { + "name": "connect-es", "workspaces": [ - "./packages/connect", - "./packages/protoc-gen-connect-es", - "./packages/connect-web", - "./packages/connect-node", - "./packages/connect-fastify", - "./packages/connect-next", - "./packages/connect-express", - "./packages/connect-web-bench", - "./packages/example", - "./packages/connect-migrate", - "./packages/connect-conformance", - "./packages/connect-cloudflare" + "packages/connect", + "packages/protoc-gen-connect-es", + "packages/connect-web", + "packages/connect-node", + "packages/connect-fastify", + "packages/connect-next", + "packages/connect-express", + "packages/connect-web-bench", + "packages/example", + "packages/connect-migrate", + "packages/connect-conformance", + "packages/connect-cloudflare" ], "devDependencies": { "@arethetypeswrong/cli": "^0.15.3", @@ -28,6 +29,7 @@ "eslint-plugin-import": "^2.29.1", "eslint-plugin-n": "^17.10.1", "prettier": "^3.3.3", + "turbo": "^2.1.0", "typescript": "5.5.4" }, "engines": { @@ -10069,6 +10071,108 @@ "@esbuild/win32-x64": "0.23.1" } }, + "node_modules/turbo": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/turbo/-/turbo-2.1.0.tgz", + "integrity": "sha512-A969/LO/sPHKlapIarY2VVzqQ5JnnW2/1kksZlnMEpsRD6gwOELvVL+ozfMiO7av9RILt3UeN02L17efr6HUCA==", + "dev": true, + "license": "MIT", + "bin": { + "turbo": "bin/turbo" + }, + "optionalDependencies": { + "turbo-darwin-64": "2.1.0", + "turbo-darwin-arm64": "2.1.0", + "turbo-linux-64": "2.1.0", + "turbo-linux-arm64": "2.1.0", + "turbo-windows-64": "2.1.0", + "turbo-windows-arm64": "2.1.0" + } + }, + "node_modules/turbo-darwin-64": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/turbo-darwin-64/-/turbo-darwin-64-2.1.0.tgz", + "integrity": "sha512-gHwpDk2gyB7qZ57gUUwDIS/IkglqEjjVtPZCTxmCRg28Tiwjui0azsLVKrnHP9UZHllozwbi28x8HXLXLEFF1w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/turbo-darwin-arm64": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/turbo-darwin-arm64/-/turbo-darwin-arm64-2.1.0.tgz", + "integrity": "sha512-GLaqGetNC6eS4eqXgsheLOHic/OcnGCGDi5boVf+TFZTXYH6YE15L4ugZha4xHXCr1KouCLILHh+f8EHEmWylg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/turbo-linux-64": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/turbo-linux-64/-/turbo-linux-64-2.1.0.tgz", + "integrity": "sha512-VzBOsj7JyGoZtiNZZ6brjnY7UehRnClluw7pwznuLPzClkqOOPMd2jOcgkWxnP/xW4NBmOoFANXXrtvKBD4f2w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/turbo-linux-arm64": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/turbo-linux-arm64/-/turbo-linux-arm64-2.1.0.tgz", + "integrity": "sha512-St7svJnOO5g4F6R7Z32e10I/0M3e6qpNjEYybXwPNul9NSfnUXeky4WoKaALwqNhyJ7nYemoFpZ1d+i8hFQTHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/turbo-windows-64": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/turbo-windows-64/-/turbo-windows-64-2.1.0.tgz", + "integrity": "sha512-iSobNud2MrJ1SZ1upVPlErT8xexsr0MQtKapdfq6z0M0rBnrDGEq5bUCSScWyGu+O4+glB4br9xkTAkGFqaxqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/turbo-windows-arm64": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/turbo-windows-arm64/-/turbo-windows-arm64-2.1.0.tgz", + "integrity": "sha512-d61jN4rjE5PnUfF66GKrKoj8S8Ql4FGXzFFzZz4kjsHpZZzCTtqlzPZBmd1byzGYhDPTorTqG3G1USohbdyohA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/type-check": { "version": "0.4.0", "dev": true, @@ -11259,6 +11363,7 @@ }, "devDependencies": { "@bufbuild/buf": "^1.39.0", + "@connectrpc/protoc-gen-connect-es": "1.4.0", "@types/debug": "^4.1.12", "@types/node-forge": "^1.3.9", "@types/tar-stream": "^3.1.3" @@ -11269,7 +11374,9 @@ "version": "1.4.0", "license": "Apache-2.0", "devDependencies": { + "@connectrpc/connect": "1.4.0", "@connectrpc/connect-conformance": "^1.4.0", + "@connectrpc/connect-node": "1.4.0", "@types/express": "^4.17.18", "express": "^4.19.2", "tsx": "^4.19.0" @@ -11288,7 +11395,9 @@ "version": "1.4.0", "license": "Apache-2.0", "devDependencies": { - "@connectrpc/connect-conformance": "^1.4.0" + "@connectrpc/connect": "1.4.0", + "@connectrpc/connect-conformance": "^1.4.0", + "@connectrpc/connect-node": "1.4.0" }, "engines": { "node": ">=16.0.0" @@ -11324,6 +11433,10 @@ "name": "@connectrpc/connect-next", "version": "1.4.0", "license": "Apache-2.0", + "devDependencies": { + "@connectrpc/connect": "1.4.0", + "@connectrpc/connect-node": "1.4.0" + }, "engines": { "node": ">=16.0.0" }, diff --git a/package.json b/package.json index ebbd5673c..d99881895 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,30 @@ { + "name": "connect-es", "private": true, "workspaces": [ - "./packages/connect", - "./packages/protoc-gen-connect-es", - "./packages/connect-web", - "./packages/connect-node", - "./packages/connect-fastify", - "./packages/connect-next", - "./packages/connect-express", - "./packages/connect-web-bench", - "./packages/example", - "./packages/connect-migrate", - "./packages/connect-conformance", - "./packages/connect-cloudflare" + "packages/connect", + "packages/protoc-gen-connect-es", + "packages/connect-web", + "packages/connect-node", + "packages/connect-fastify", + "packages/connect-next", + "packages/connect-express", + "packages/connect-web-bench", + "packages/example", + "packages/connect-migrate", + "packages/connect-conformance", + "packages/connect-cloudflare" ], "scripts": { + "all": "turbo run --ui tui build format test conformance bundle-size lint attw license-header", "clean": "git clean -Xdf", "setversion": "node scripts/set-workspace-version.js", "postsetversion": "npm run all", - "all": "make", - "release": "npm run all && node scripts/release.js" + "release": "node scripts/release.js", + "prerelease": "npm run all", + "format": "prettier --write --ignore-unknown '.' '!packages' '!.turbo' '!node_modules'", + "license-header": "license-header --ignore 'packages/**'", + "lint": "eslint --max-warnings 0 . --ignore-pattern 'packages/**'" }, "type": "module", "engineStrict": true, @@ -27,6 +32,7 @@ "node": ">=16", "npm": ">=8" }, + "packageManager": "npm@10.1.0", "licenseHeader": { "licenseType": "apache", "yearRange": "2021-2024", @@ -35,13 +41,14 @@ "devDependencies": { "@arethetypeswrong/cli": "^0.15.3", "@bufbuild/license-header": "^0.0.4", + "@typescript-eslint/eslint-plugin": "^7.14.0", "@typescript-eslint/parser": "^7.14.0", "eslint": "^8.56.0", - "@typescript-eslint/eslint-plugin": "^7.14.0", "eslint-import-resolver-typescript": "^3.6.1", - "eslint-plugin-n": "^17.10.1", "eslint-plugin-import": "^2.29.1", + "eslint-plugin-n": "^17.10.1", "prettier": "^3.3.3", + "turbo": "^2.1.0", "typescript": "5.5.4" } } diff --git a/packages/connect-cloudflare/README.md b/packages/connect-cloudflare/README.md index 3a9c18476..b18aca8ec 100644 --- a/packages/connect-cloudflare/README.md +++ b/packages/connect-cloudflare/README.md @@ -4,32 +4,31 @@ This package provides conformance test coverage for Connect-ES on Cloudflare for It uses the [conformance runner](https://github.com/connectrpc/conformance/releases) to run the tests. -Cloudflare worker tests are run once every 24 hours and on a release PR. This is because the tests are run on a live +Cloudflare worker tests are run once every 24 hours and on a release PR. This is because the tests are run on a live Cloudflare worker, and deploying a new version of the worker is a slow process. ## Running conformance tests -Run `make testcloudflareconformance` to run all Cloudflare conformance tests for both server and client. The above command -is also available as an npm script: `npm run conformance`. +Cloudflare conformance tests can be run with the tasks `conformance:client` and `conformance:server` for the package +`@connectrpc/connect-cloudflare`. You can run both with the following command: -The individual tests for server and client can also be run via npm: - -`npm run conformance:server` -`npm run conformance:client` +```bash +npx turbo run --filter @connectrpc/connect-cloudflare conformance:client conformance:server +``` ### Client tests on Cloudflare -Client tests on Cloudflare require a live server with valid TLS. To run the tests, we set up a -[self hosted GitHub action runner](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners) +Client tests on Cloudflare require a live server with valid TLS. To run the tests, we set up a +[self hosted GitHub action runner](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners) that runs the conformance runner and exposes the reference server on port 443 over the public internet. #### Steps to setup the action runner -* Provision a VM with a static public IP address. -* Reserve a domain (can be a subdomain) and point it to the public IP address from the previous step. -* Use [certbot](https://certbot.eff.org/) to setup automatic certificate renewal for the domain. -* Using iptables, redirect traffic from port 443 to a non-privileged port say 8181. -* Create a dedicated user to run the action runner. -* Make sure the user has the necessary permissions to run the conformance runner, read the cert and key that certbot maintains. -* Follow the steps [here](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/adding-self-hosted-runners) to add a self-hosted runner to the repository. -* Configure the runner app to [run as a service](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/configuring-the-self-hosted-runner-application-as-a-service) with the created user. +- Provision a VM with a static public IP address. +- Reserve a domain (can be a subdomain) and point it to the public IP address from the previous step. +- Use [certbot](https://certbot.eff.org/) to setup automatic certificate renewal for the domain. +- Using iptables, redirect traffic from port 443 to a non-privileged port say 8181. +- Create a dedicated user to run the action runner. +- Make sure the user has the necessary permissions to run the conformance runner, read the cert and key that certbot maintains. +- Follow the steps [here](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/adding-self-hosted-runners) to add a self-hosted runner to the repository. +- Configure the runner app to [run as a service](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/configuring-the-self-hosted-runner-application-as-a-service) with the created user. diff --git a/packages/connect-cloudflare/conformance/client.ts b/packages/connect-cloudflare/conformance/client.ts index 1f54b8ffd..c1d64c585 100755 --- a/packages/connect-cloudflare/conformance/client.ts +++ b/packages/connect-cloudflare/conformance/client.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env -S npx tsx - // Copyright 2021-2024 The Connect Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/packages/connect-cloudflare/conformance/server.ts b/packages/connect-cloudflare/conformance/server.ts index e56926b8e..4a6f6ece0 100755 --- a/packages/connect-cloudflare/conformance/server.ts +++ b/packages/connect-cloudflare/conformance/server.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env -S npx tsx - // Copyright 2021-2024 The Connect Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/packages/connect-cloudflare/package.json b/packages/connect-cloudflare/package.json index 3607d4cf8..8f4519493 100644 --- a/packages/connect-cloudflare/package.json +++ b/packages/connect-cloudflare/package.json @@ -3,9 +3,11 @@ "private": true, "type": "module", "scripts": { - "conformance": "tsc --noEmit && npm run conformance:server && npm run conformance:client", - "conformance:server": "npx wrangler deploy -c ./conformance/wrangler-server.toml && connectconformance --mode server --conf ./conformance/conformance-cloudflare-server.yaml -v ./conformance/server.ts", - "conformance:client": "npx wrangler deploy -c ./conformance/wrangler-client.toml && connectconformance --mode client --conf ./conformance/conformance-cloudflare-client.yaml -v --known-failing @./conformance/known-failing-client.txt --bind 0.0.0.0 --port 8181 --cert $CLOUDFLARE_WORKERS_REFERENCE_SERVER_CERT --key $CLOUDFLARE_WORKERS_REFERENCE_SERVER_KEY -- ./conformance/client.ts" + "conformance:server": "npx wrangler deploy -c ./conformance/wrangler-server.toml && connectconformance --mode server --conf ./conformance/conformance-cloudflare-server.yaml -v tsx ./conformance/server.ts", + "conformance:client": "npx wrangler deploy -c ./conformance/wrangler-client.toml && connectconformance --mode client --conf ./conformance/conformance-cloudflare-client.yaml -v --known-failing @./conformance/known-failing-client.txt --bind 0.0.0.0 --port 8181 --cert $CLOUDFLARE_WORKERS_REFERENCE_SERVER_CERT --key $CLOUDFLARE_WORKERS_REFERENCE_SERVER_KEY -- tsx ./conformance/client.ts", + "format": "prettier --write --ignore-unknown '.'", + "license-header": "license-header", + "lint": "eslint --max-warnings 0 ." }, "dependencies": { "@bufbuild/protobuf": "^1.10.0", diff --git a/packages/connect-cloudflare/turbo.json b/packages/connect-cloudflare/turbo.json new file mode 100644 index 000000000..0c8092c4b --- /dev/null +++ b/packages/connect-cloudflare/turbo.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "tasks": { + "conformance:client": { "cache": false, "dependsOn": ["^build"] }, + "conformance:server": { "cache": false, "dependsOn": ["^build"] } + } +} diff --git a/packages/connect-conformance/README.md b/packages/connect-conformance/README.md index ab4f861f5..5d6948633 100644 --- a/packages/connect-conformance/README.md +++ b/packages/connect-conformance/README.md @@ -6,11 +6,11 @@ this repository. For documentation on the conformance tests, see the repository [here](https://github.com/connectrpc/conformance?tab=readme-ov-file#documentation). Note that this package does not run any conformance tests itself, but instead exports functionality to be used by other -packages, such as `connect-node`, `connect-web`, etc. +packages, such as `@connectprc/connect-node`, `@connectprc/connect-web`, etc. ## Updating the conformance version To update the version of the conformance runner and protos, change the version in the following places: -* The `generate` command of this package's `package.json` file. -* The `version` constant inside `node/conformance.ts`. +- The `generate` command of this package's `package.json` file. +- The `version` constant inside `node/conformance.ts`. diff --git a/packages/connect-conformance/package.json b/packages/connect-conformance/package.json index 0607b8c8d..cd39b6e41 100644 --- a/packages/connect-conformance/package.json +++ b/packages/connect-conformance/package.json @@ -20,7 +20,11 @@ "build": "npm run build:cjs && npm run build:esm", "postbuild": "connectconformance --version", "build:cjs": "tsc --project tsconfig.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs --declaration --declarationDir ./dist/cjs && echo >./dist/cjs/src/package.json '{\"type\":\"commonjs\"}'", - "build:esm": "tsc --project tsconfig.json --outDir ./dist/esm --declaration --declarationDir ./dist/esm" + "build:esm": "tsc --project tsconfig.json --outDir ./dist/esm --declaration --declarationDir ./dist/esm", + "format": "prettier --write --ignore-unknown '.' '!dist' '!src/gen'", + "license-header": "license-header", + "lint": "eslint --max-warnings 0 .", + "attw": "attw --pack" }, "dependencies": { "@bufbuild/protobuf": "^1.10.0", @@ -33,6 +37,7 @@ "@bufbuild/buf": "^1.39.0", "@types/node-forge": "^1.3.9", "@types/tar-stream": "^3.1.3", - "@types/debug": "^4.1.12" + "@types/debug": "^4.1.12", + "@connectrpc/protoc-gen-connect-es": "1.4.0" } } diff --git a/packages/connect-conformance/turbo.json b/packages/connect-conformance/turbo.json new file mode 100644 index 000000000..fd0ee0db3 --- /dev/null +++ b/packages/connect-conformance/turbo.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "tasks": { + "lint": { + "dependsOn": ["format", "^build", "generate", "build"] + } + } +} diff --git a/packages/connect-express/README.md b/packages/connect-express/README.md index 33b3a839b..c6ec96bac 100644 --- a/packages/connect-express/README.md +++ b/packages/connect-express/README.md @@ -15,7 +15,7 @@ Adds your Connect RPCs to an Express server. // connect.ts import { ConnectRouter } from "@connectrpc/connect"; -export default function(router: ConnectRouter) { +export default function (router: ConnectRouter) { // implement rpc Say(SayRequest) returns (SayResponse) router.rpc(ElizaService, ElizaService.methods.say, async (req) => ({ sentence: `you said: ${req.sentence}`, @@ -64,7 +64,7 @@ const transport = createGrpcWebTransport({ const client = createPromiseClient(ElizaService, transport); const { sentence } = await client.say({ sentence: "I feel happy." }); -console.log(sentence) // you said: I feel happy. +console.log(sentence); // you said: I feel happy. ``` A client for the web browser actually looks identical to this example - it would @@ -74,7 +74,6 @@ instead. Note that support for gRPC is limited, since many gRPC clients require HTTP/2, and Express does not support the Node.js `http2` module. - ## Getting started To get started with Connect, head over to the [docs](https://connectrpc.com/docs/node/getting-started) diff --git a/packages/connect-express/conformance/server.ts b/packages/connect-express/conformance/server.ts index 04da05b98..cf1ae2f7c 100755 --- a/packages/connect-express/conformance/server.ts +++ b/packages/connect-express/conformance/server.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env -S npx tsx - // Copyright 2021-2024 The Connect Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,9 +29,10 @@ import { ServerCompatResponse, ServerStreamRequest, UnaryRequest, + writeSizeDelimitedBuffer, } from "@connectrpc/connect-conformance"; import express from "express"; -import { expressConnectMiddleware } from "@connectrpc/connect-express"; +import { expressConnectMiddleware } from "../src/index.js"; main(); @@ -104,8 +103,12 @@ function main() { } process.on("SIGTERM", () => { - server.close(); + // Gracefully shutting down a http2 server is complicated. + // We trust the conformance test runner to only send the signal if it's done, + // so we simply shut down hard. + process.exit(); }); + server.listen(undefined, "127.0.0.1", () => { const addrInfo = server.address() as net.AddressInfo; const res = new ServerCompatResponse({ @@ -116,10 +119,6 @@ function main() { host: addrInfo.address, port: addrInfo.port, }); - const data = res.toBinary(); - const size = Buffer.alloc(4); - size.writeUInt32BE(data.byteLength); - process.stdout.write(size); - process.stdout.write(data); + process.stdout.write(writeSizeDelimitedBuffer(res.toBinary())); }); } diff --git a/packages/connect-express/package.json b/packages/connect-express/package.json index aadec8ecc..7801ec796 100644 --- a/packages/connect-express/package.json +++ b/packages/connect-express/package.json @@ -12,9 +12,12 @@ "build": "npm run build:cjs && npm run build:esm", "build:cjs": "tsc --project tsconfig.build.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs --declaration --declarationDir ./dist/cjs && echo >./dist/cjs/package.json '{\"type\":\"commonjs\"}'", "build:esm": "tsc --project tsconfig.build.json --outDir ./dist/esm --declaration --declarationDir ./dist/esm", - "attw": "attw --pack", - "jasmine": "jasmine --config=jasmine.json", - "conformance": "tsc --noEmit && connectconformance --mode server --conf ./conformance/conformance-express.yaml -v ./conformance/server.ts" + "test": "jasmine --config=jasmine.json", + "conformance": "tsc --noEmit && connectconformance --mode server --conf ./conformance/conformance-express.yaml -v -- tsx ./conformance/server.ts", + "format": "prettier --write --ignore-unknown '.' '!dist'", + "license-header": "license-header", + "lint": "eslint --max-warnings 0 .", + "attw": "attw --pack" }, "type": "module", "sideEffects": false, @@ -30,6 +33,8 @@ }, "devDependencies": { "@connectrpc/connect-conformance": "^1.4.0", + "@connectrpc/connect": "1.4.0", + "@connectrpc/connect-node": "1.4.0", "@types/express": "^4.17.18", "express": "^4.19.2", "tsx": "^4.19.0" diff --git a/packages/connect-fastify/README.md b/packages/connect-fastify/README.md index 627b41508..bbde6bab5 100644 --- a/packages/connect-fastify/README.md +++ b/packages/connect-fastify/README.md @@ -15,7 +15,7 @@ Plug your Connect RPCs into a fastify server. // connect.ts import { ConnectRouter } from "@connectrpc/connect"; -export default function(router: ConnectRouter) { +export default function (router: ConnectRouter) { // implement rpc Say(SayRequest) returns (SayResponse) router.rpc(ElizaService, ElizaService.methods.say, async (req) => ({ sentence: `you said: ${req.sentence}`, @@ -78,14 +78,13 @@ const transport = createGrpcTransport({ const client = createPromiseClient(ElizaService, transport); const { sentence } = await client.say({ sentence: "I feel happy." }); -console.log(sentence) // you said: I feel happy. +console.log(sentence); // you said: I feel happy. ``` A client for the web browser actually looks identical to this example - it would simply use `createConnectTransport` from [@connectrpc/connect-web](https://www.npmjs.com/package/@connectrpc/connect-web) instead. - ## Getting started To get started with Connect, head over to the [docs](https://connectrpc.com/docs/node/getting-started) diff --git a/packages/connect-fastify/conformance/server.ts b/packages/connect-fastify/conformance/server.ts index 516aaec8e..7e985b3ad 100755 --- a/packages/connect-fastify/conformance/server.ts +++ b/packages/connect-fastify/conformance/server.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env -S npx tsx - // Copyright 2021-2024 The Connect Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,6 +28,7 @@ import { ServerCompatResponse, ServerStreamRequest, UnaryRequest, + writeSizeDelimitedBuffer, } from "@connectrpc/connect-conformance"; import { fastify } from "fastify"; import type { @@ -39,7 +38,7 @@ import type { FastifyHttp2SecureOptions, FastifyInstance, } from "fastify"; -import { fastifyConnectPlugin } from "@connectrpc/connect-fastify"; +import { fastifyConnectPlugin } from "../src/index.js"; main(); @@ -136,8 +135,12 @@ function main() { } process.on("SIGTERM", () => { - void server.close(); + // Gracefully shutting down a http2 server is complicated. + // We trust the conformance test runner to only send the signal if it's done, + // so we simply shut down hard. + process.exit(); }); + server.listen({ host: "127.0.0.1", port: 0 }, () => { const addrInfo = server.addresses()[0]; const res = new ServerCompatResponse({ @@ -148,10 +151,6 @@ function main() { host: addrInfo.address, port: addrInfo.port, }); - const data = res.toBinary(); - const size = Buffer.alloc(4); - size.writeUInt32BE(data.byteLength); - process.stdout.write(size); - process.stdout.write(data); + process.stdout.write(writeSizeDelimitedBuffer(res.toBinary())); }); } diff --git a/packages/connect-fastify/package.json b/packages/connect-fastify/package.json index a788db74d..f7f8e21ba 100644 --- a/packages/connect-fastify/package.json +++ b/packages/connect-fastify/package.json @@ -12,8 +12,11 @@ "build": "npm run build:cjs && npm run build:esm", "build:cjs": "tsc --project tsconfig.build.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs --declaration --declarationDir ./dist/cjs && echo >./dist/cjs/package.json '{\"type\":\"commonjs\"}'", "build:esm": "tsc --project tsconfig.build.json --outDir ./dist/esm --declaration --declarationDir ./dist/esm", - "attw": "attw --pack", - "conformance": "tsc --noEmit && connectconformance --mode server --conf ./conformance/conformance-fastify.yaml -v ./conformance/server.ts" + "conformance": "tsc --noEmit && connectconformance --mode server --conf ./conformance/conformance-fastify.yaml -v -- tsx ./conformance/server.ts", + "format": "prettier --write --ignore-unknown '.' '!dist'", + "license-header": "license-header", + "lint": "eslint --max-warnings 0 .", + "attw": "attw --pack" }, "type": "module", "sideEffects": false, @@ -28,7 +31,9 @@ "node": ">=16.0.0" }, "devDependencies": { - "@connectrpc/connect-conformance": "^1.4.0" + "@connectrpc/connect-conformance": "^1.4.0", + "@connectrpc/connect": "1.4.0", + "@connectrpc/connect-node": "1.4.0" }, "peerDependencies": { "@bufbuild/protobuf": "^1.10.0", diff --git a/packages/connect-migrate/README.md b/packages/connect-migrate/README.md index 15d8fa6de..4d4345a49 100644 --- a/packages/connect-migrate/README.md +++ b/packages/connect-migrate/README.md @@ -2,7 +2,6 @@ This tool updates your Connect project to use the new `@connectrpc` packages. - ## Usage To migrate, run the following command in your project root directory: @@ -13,7 +12,6 @@ npx @connectrpc/connect-migrate Add the `--help` flag to the command to learn more about the available flags. - ## What it does This package is made up of a few migration steps @@ -24,29 +22,27 @@ This package is made up of a few migration steps ## What files are changed -We ignore all files within `node_modules` but will update any other files that +We ignore all files within `node_modules` but will update any other files that end with the following extensions: `.ts`, `.tsx`, `.js`, `.jsx`, `.cjs`, `.mjs`. - ## Prerequisites -- Commit any unstaged changes to your project, so that you can revert in case the +- Commit any unstaged changes to your project, so that you can revert in case the migration fails. - After migration, run your generate scripts to re-generate code with the latest plugin versions. - ## Alternative running methods -This tool leverages `jscodeshift` in order to find all references to packages and -update them. As a result, we've assumed a parser to parse your JavaScript/TypeScript -files. If you see errors due to parsing, you may be using a custom babel config -or another custom parser. You can work around this while leveraging our +This tool leverages `jscodeshift` in order to find all references to packages and +update them. As a result, we've assumed a parser to parse your JavaScript/TypeScript +files. If you see errors due to parsing, you may be using a custom babel config +or another custom parser. You can work around this while leveraging our transforms by calling `jscodeshift` directly. ```shell npx jscodeshift -t ./node_modules/@connectrpc/connect-migrate/dist/cjs/migrations/v0.13.1-transform.js . ``` -And add any additional params you feel are necessary. You can find more +And add any additional params you feel are necessary. You can find more information about `jscodeshift` [here](https://github.com/facebook/jscodeshift/blob/main/README.md#usage-cli). diff --git a/packages/connect-migrate/bin/connect-migrate b/packages/connect-migrate/bin/connect-migrate index 8705279e2..ae12b1f82 100755 --- a/packages/connect-migrate/bin/connect-migrate +++ b/packages/connect-migrate/bin/connect-migrate @@ -1,3 +1,3 @@ #!/usr/bin/env node -require("../dist/cjs/cli.js"); \ No newline at end of file +require("../dist/cjs/cli.js"); diff --git a/packages/connect-migrate/package.json b/packages/connect-migrate/package.json index 569507886..c8dfc30ea 100644 --- a/packages/connect-migrate/package.json +++ b/packages/connect-migrate/package.json @@ -8,7 +8,6 @@ "url": "https://github.com/connectrpc/connect-es.git", "directory": "packages/connect-migrate" }, - "sideEffects": true, "bin": { "connect-migrate": "bin/connect-migrate" }, @@ -16,7 +15,9 @@ "prebuild": "rm -rf ./dist/*", "build": "tsc --project tsconfig.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs", "test": "jasmine --config=jasmine.json", - "build+test": "npm run build && npm run test" + "format": "prettier --write --ignore-unknown '.' '!dist'", + "license-header": "license-header", + "lint": "eslint --max-warnings 0 ." }, "engines": { "node": ">=16.0.0" diff --git a/packages/connect-migrate/src/lib/replace-dependencies.ts b/packages/connect-migrate/src/lib/replace-dependencies.ts index 23b947050..b638f156c 100644 --- a/packages/connect-migrate/src/lib/replace-dependencies.ts +++ b/packages/connect-migrate/src/lib/replace-dependencies.ts @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { PackageJson } from "./package-json"; +import type { PackageJson } from "./package-json"; import { intersects as semverIntersects, satisfies as semverSatisfies, @@ -32,7 +32,7 @@ export function replaceDependencies( ): PackageJson | null { const modifiedPackageNames = new Set(); const replacedPackageNames = new Map(); - const copy = structuredClone(pkg) as PackageJson; + const copy = clonePackageJson(pkg); // replace dependencies for (const replacement of replacements) { for (const p of [ @@ -92,3 +92,7 @@ export function replaceDependencies( } return modifiedPackageNames.size > 0 ? copy : null; } + +function clonePackageJson(pkg: PackageJson): PackageJson { + return JSON.parse(JSON.stringify(pkg)) as PackageJson; +} diff --git a/packages/connect-migrate/tsconfig.json b/packages/connect-migrate/tsconfig.json index 2e85139ed..aecaa31d3 100644 --- a/packages/connect-migrate/tsconfig.json +++ b/packages/connect-migrate/tsconfig.json @@ -2,6 +2,11 @@ "include": ["src/**/*.ts"], "extends": "../../tsconfig.base.json", "compilerOptions": { - "esModuleInterop": true // required for jscodeshift + // required for jscodeshift + "esModuleInterop": true, + // This package is CommonJS + "verbatimModuleSyntax": false, + "module": "CommonJS", + "moduleResolution": "Node10" } } diff --git a/packages/connect-migrate/turbo.json b/packages/connect-migrate/turbo.json new file mode 100644 index 000000000..fd0ee0db3 --- /dev/null +++ b/packages/connect-migrate/turbo.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "tasks": { + "lint": { + "dependsOn": ["format", "^build", "generate", "build"] + } + } +} diff --git a/packages/connect-next/README.md b/packages/connect-next/README.md index 90264b321..c337941cd 100644 --- a/packages/connect-next/README.md +++ b/packages/connect-next/README.md @@ -7,10 +7,9 @@ TypeScript. `@connectrpc/connect-next` provides a plugin for [Next.js](https://nextjs.org/), the React Framework for the Web. - ### nextJsApiRouter() -Provide your Connect RPCs via Next.js API routes. To enable Connect in Next.js, +Provide your Connect RPCs via Next.js API routes. To enable Connect in Next.js, add two files to your project: ```diff @@ -21,8 +20,8 @@ add two files to your project:         └── [[...connect]].ts ``` -> **Note:** Next.js 13 introduced the new App Router. Your Connect API routes -> need to be placed in `pages/`, but you can use the `app/` directory for the +> **Note:** Next.js 13 introduced the new App Router. Your Connect API routes +> need to be placed in `pages/`, but you can use the `app/` directory for the > App Router at the same time. The new file `connect.ts` is where you register your RPCs: @@ -31,7 +30,7 @@ The new file `connect.ts` is where you register your RPCs: // connect.ts import { ConnectRouter } from "@connectrpc/connect"; -export default function(router: ConnectRouter) { +export default function (router: ConnectRouter) { // implement rpc Say(SayRequest) returns (SayResponse) router.rpc(ElizaService, ElizaService.methods.say, async (req) => ({ sentence: `you said: ${req.sentence}`, @@ -46,8 +45,8 @@ export default function(router: ConnectRouter) { import { nextJsApiRouter } from "@connectrpc/connect-next"; import routes from "../../connect"; -const {handler, config} = nextJsApiRouter({ routes }); -export {handler as default, config}; +const { handler, config } = nextJsApiRouter({ routes }); +export { handler as default, config }; ``` With that server running, you can make requests with any Connect or gRPC-Web client. @@ -77,7 +76,7 @@ const transport = createGrpcWebTransport({ const client = createPromiseClient(ElizaService, transport); const { sentence } = await client.say({ sentence: "I feel happy." }); -console.log(sentence) // you said: I feel happy. +console.log(sentence); // you said: I feel happy. ``` A client for the web browser actually looks identical to this example - it would @@ -87,14 +86,12 @@ instead. Note that support for gRPC is limited, since many gRPC clients require HTTP/2, and Express does not support the Node.js `http2` module. - ### Deploying to Vercel Currently, `@connectrpc/connect-next` does not support the Vercel Edge runtime. It requires the Node.js server runtime, which is used by default when deploying to Vercel. - ## Getting started To get started with Connect, head over to the [docs](https://connectrpc.com/docs/node/getting-started) diff --git a/packages/connect-next/package.json b/packages/connect-next/package.json index 3d7ea8dea..18d7e6aa5 100644 --- a/packages/connect-next/package.json +++ b/packages/connect-next/package.json @@ -12,6 +12,9 @@ "build": "npm run build:cjs && npm run build:esm", "build:cjs": "tsc --project tsconfig.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs --declaration --declarationDir ./dist/cjs && echo >./dist/cjs/package.json '{\"type\":\"commonjs\"}'", "build:esm": "tsc --project tsconfig.json --outDir ./dist/esm --declaration --declarationDir ./dist/esm", + "format": "prettier --write --ignore-unknown '.' '!dist'", + "license-header": "license-header", + "lint": "eslint --max-warnings 0 .", "attw": "attw --pack" }, "type": "module", @@ -31,5 +34,9 @@ "next": "^13.2.4 || ^14.2.5", "@connectrpc/connect": "1.4.0", "@connectrpc/connect-node": "1.4.0" + }, + "devDependencies": { + "@connectrpc/connect": "1.4.0", + "@connectrpc/connect-node": "1.4.0" } } diff --git a/packages/connect-node/README.md b/packages/connect-node/README.md index 004b30e40..ff94f35db 100644 --- a/packages/connect-node/README.md +++ b/packages/connect-node/README.md @@ -66,7 +66,6 @@ const { sentence } = await client.say({ sentence: "I feel happy." }); console.log(sentence) // you said: I feel happy. ``` - ### connectNodeAdapter() Run your Connect RPCs on the Node.js `http`, `https`, or `http2` modules. @@ -75,7 +74,7 @@ Run your Connect RPCs on the Node.js `http`, `https`, or `http2` modules. // connect.ts import { ConnectRouter } from "@connectrpc/connect"; -export default function(router: ConnectRouter) { +export default function (router: ConnectRouter) { // implement rpc Say(SayRequest) returns (SayResponse) router.rpc(ElizaService, ElizaService.methods.say, async (req) => ({ sentence: `you said: ${req.sentence}`, @@ -94,7 +93,6 @@ http2.createServer( ).listen(8080); ``` - With that server running, you can make requests with any gRPC, gRPC-Web, or Connect client. `buf curl` with the gRPC protocol: @@ -130,14 +128,13 @@ const transport = createGrpcTransport({ const client = createPromiseClient(ElizaService, transport); const { sentence } = await client.say({ sentence: "I feel happy." }); -console.log(sentence) // you said: I feel happy. +console.log(sentence); // you said: I feel happy. ``` A client for the web browser actually looks identical to this example - it would simply use `createConnectTransport` from [@connectrpc/connect-web](https://www.npmjs.com/package/@connectrpc/connect-web) instead. - ## Getting started To get started with Connect, head over to the [docs](https://connectrpc.com/docs/node/getting-started) diff --git a/packages/connect-node/conformance/README.md b/packages/connect-node/conformance/README.md index b5e3d266b..9b854e49b 100644 --- a/packages/connect-node/conformance/README.md +++ b/packages/connect-node/conformance/README.md @@ -1,15 +1,12 @@ # Connect-Node Conformance Tests -This directory provides conformance test coverage for @connectrpc/connect-node for both clients and servers. +This directory provides conformance test coverage for @connectrpc/connect-node for both clients and servers. It uses the [conformance runner](https://github.com/connectrpc/conformance/releases) to run the tests. ## Running conformance tests -Run `make testnodeconformance` to run all Node conformance tests for both server and client. -The above command is also available as an npm script: `npm run conformance`. - -The individual tests for server and client can also be run via npm: - -`npm run conformance:server` -`npm run conformance:client` +```bash +cd packages/connect-node +npx turbo run conformance:server conformance:client +``` diff --git a/packages/connect-node/conformance/client.ts b/packages/connect-node/conformance/client.ts index 8a8a32893..35091a05b 100755 --- a/packages/connect-node/conformance/client.ts +++ b/packages/connect-node/conformance/client.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env -S npx tsx - // Copyright 2021-2024 The Connect Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/packages/connect-node/conformance/server.ts b/packages/connect-node/conformance/server.ts index a341c7203..c2c0b5a36 100755 --- a/packages/connect-node/conformance/server.ts +++ b/packages/connect-node/conformance/server.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env -S npx tsx - // Copyright 2021-2024 The Connect Authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,7 +17,7 @@ import { compressionBrotli, compressionGzip, connectNodeAdapter, -} from "@connectrpc/connect-node"; +} from "../src/index.js"; import * as http from "node:http"; import * as http2 from "node:http2"; import * as https from "node:https"; @@ -107,9 +105,13 @@ function main() { } process.on("SIGTERM", () => { - server.close(); + // Gracefully shutting down a http2 server is complicated. + // We trust the conformance test runner to only send the signal if it's done, + // so we simply shut down hard. + process.exit(); }); - server.listen(undefined, "127.0.0.1", () => { + + server.listen(0, "127.0.0.1", () => { const addrInfo = server.address() as net.AddressInfo; const res = new ServerCompatResponse({ pemCert: diff --git a/packages/connect-node/conformance/transport.ts b/packages/connect-node/conformance/transport.ts index 5926e7c9e..827ef2a46 100644 --- a/packages/connect-node/conformance/transport.ts +++ b/packages/connect-node/conformance/transport.ts @@ -31,7 +31,7 @@ import { compressionGzip, compressionBrotli, createGrpcWebTransport, -} from "@connectrpc/connect-node"; +} from "../src/index.js"; import type { Compression } from "@connectrpc/connect/protocol"; import * as http2 from "node:http2"; diff --git a/packages/connect-node/package.json b/packages/connect-node/package.json index 9dad50d8b..7307cb860 100644 --- a/packages/connect-node/package.json +++ b/packages/connect-node/package.json @@ -12,11 +12,13 @@ "build": "npm run build:cjs && npm run build:esm", "build:cjs": "tsc --project tsconfig.build.json --module commonjs --moduleResolution node10 --verbatimModuleSyntax false --outDir ./dist/cjs --declaration --declarationDir ./dist/cjs && echo >./dist/cjs/package.json '{\"type\":\"commonjs\"}'", "build:esm": "tsc --project tsconfig.build.json --outDir ./dist/esm --declaration --declarationDir ./dist/esm", - "attw": "attw --pack", - "jasmine": "jasmine --config=jasmine.json", - "conformance": "npm run conformance:server && npm run conformance:client", - "conformance:server": "tsc --noEmit && connectconformance --mode server --conf ./conformance/conformance-node.yaml -v ./conformance/server.ts", - "conformance:client": "tsc --noEmit && connectconformance --mode client --conf ./conformance/conformance-node.yaml -v ./conformance/client.ts" + "test": "jasmine --config=jasmine.json", + "conformance:server": "tsc --noEmit && connectconformance --mode server --conf ./conformance/conformance-node.yaml -v -- tsx ./conformance/server.ts", + "conformance:client": "tsc --noEmit && connectconformance --mode client --conf ./conformance/conformance-node.yaml -v -- tsx ./conformance/client.ts", + "format": "prettier --write --ignore-unknown '.' '!dist'", + "license-header": "license-header", + "lint": "eslint --max-warnings 0 .", + "attw": "attw --pack" }, "type": "module", "main": "./dist/cjs/index.js", diff --git a/packages/connect-node/turbo.json b/packages/connect-node/turbo.json new file mode 100644 index 000000000..b57bb0aa8 --- /dev/null +++ b/packages/connect-node/turbo.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "tasks": { + "conformance": { + "dependsOn": ["conformance:client", "conformance:server"] + }, + "conformance:client": { "cache": false, "dependsOn": ["^build"] }, + "conformance:server": { "cache": false, "dependsOn": ["^build"] } + } +} diff --git a/packages/connect-web-bench/package.json b/packages/connect-web-bench/package.json index 81014703a..4f87f2a79 100644 --- a/packages/connect-web-bench/package.json +++ b/packages/connect-web-bench/package.json @@ -4,7 +4,10 @@ "scripts": { "bundle-size": "tsx src/report.ts", "generate": "buf generate", - "postgenerate": "license-header src/gen" + "postgenerate": "license-header src/gen", + "format": "prettier --write --ignore-unknown '.' '!src/gen'", + "license-header": "license-header", + "lint": "eslint --max-warnings 0 ." }, "dependencies": { "@bufbuild/buf": "^1.39.0", diff --git a/packages/connect-web-bench/turbo.json b/packages/connect-web-bench/turbo.json new file mode 100644 index 000000000..d271cff70 --- /dev/null +++ b/packages/connect-web-bench/turbo.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "tasks": { + "bundle-size": { + "dependsOn": ["^build", "generate"] + } + } +} diff --git a/packages/connect-web/README.md b/packages/connect-web/README.md index 41b1f0cad..418c3ce0e 100644 --- a/packages/connect-web/README.md +++ b/packages/connect-web/README.md @@ -7,7 +7,6 @@ TypeScript. `@connectrpc/connect-web` provides the following adapters for web browsers, and any other platform that has the fetch API on board: - ### createConnectTransport() Lets your clients running in the web browser talk to a server with the Connect protocol: @@ -29,7 +28,7 @@ console.log(sentence) // you said: I feel happy. ### createGrpcWebTransport() -Lets your clients running in the web browser talk to a server with the gRPC-web protocol: +Lets your clients running in the web browser talk to a server with the gRPC-web protocol: ```diff import { createPromiseClient } from "@connectrpc/connect"; @@ -46,7 +45,6 @@ const { sentence } = await client.say({ sentence: "I feel happy." }); console.log(sentence) // you said: I feel happy. ``` - ## Getting started To get started with Connect, head over to the [docs](https://connectrpc.com/docs/node/getting-started) diff --git a/packages/connect-web/browserstack/README.md b/packages/connect-web/browserstack/README.md index 9fa187959..9fa49d6c9 100644 --- a/packages/connect-web/browserstack/README.md +++ b/packages/connect-web/browserstack/README.md @@ -1,11 +1,11 @@ # Connect-Web Browserstack Tests -This directory provides cross-browser test coverage for @connectrpc/connect-web with Browserstack +This directory provides cross-browser test coverage for @connectrpc/connect-web with Browserstack by running a few select tests on old browsers. Thanks to Browserstack for the sponsorship! To run these tests locally, you need to sign up on [Browserstack](https://www.browserstack.com/) and provide your username and access key: ```bash -BROWSERSTACK_USERNAME= BROWSERSTACK_ACCESS_KEY= make testwebbrowserstack +BROWSERSTACK_USERNAME= BROWSERSTACK_ACCESS_KEY= npx turbo run test-browserstack ``` diff --git a/packages/connect-web/conformance/README.md b/packages/connect-web/conformance/README.md index 311a72da4..4997b59f9 100644 --- a/packages/connect-web/conformance/README.md +++ b/packages/connect-web/conformance/README.md @@ -8,25 +8,24 @@ It uses the [conformance runner](https://github.com/connectrpc/conformance/relea Tests run in the following environments: -* Chrome -* Firefox -* Safari (only if running in OSX. Safari requires users to enable the "Allow Remote Automation" option in Safari's Develop menu) -* Node.js +- Chrome +- Firefox +- Safari (only if running in OSX. Safari requires users to enable the "Allow Remote Automation" option in Safari's Develop menu) +- Node.js For every environment, two client flavors are available: -* Promise (using `createPromiseClient`) -* Callback (using `createCallbackClient`) -For every combination, an npm script is available: +- Promise (using `createPromiseClient`) +- Callback (using `createCallbackClient`) -`npm run conformance:client::` +For every combination, a task is available: -Before you run npm scripts, make sure to build dependencies with `make .tmp/build/connect-web`. +`npx turbo run conformance::` ## Using a local browser To launch a browser window with access to the browser's network inspector, append the `--openBrowser` flag to the npm script: ``` -npm run conformance:client:chrome:promise -- --openBrowser +npx turbo run conformance:chrome:promise -- --openBrowser ``` diff --git a/packages/connect-web/conformance/client.ts b/packages/connect-web/conformance/client.ts index 92a1d834e..b39c28250 100755 --- a/packages/connect-web/conformance/client.ts +++ b/packages/connect-web/conformance/client.ts @@ -1,5 +1,3 @@ -#!/usr/bin/env -S npx tsx - // Copyright 2021-2024 The Connect Authors // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/packages/connect-web/conformance/transport.ts b/packages/connect-web/conformance/transport.ts index 97fcc7d07..0a57b0b21 100644 --- a/packages/connect-web/conformance/transport.ts +++ b/packages/connect-web/conformance/transport.ts @@ -16,7 +16,7 @@ import { createRegistry } from "@bufbuild/protobuf"; import { createConnectTransport, createGrpcWebTransport, -} from "@connectrpc/connect-web"; +} from "../src/index.js"; import { BidiStreamRequest, ClientCompatRequest, diff --git a/packages/connect-web/package.json b/packages/connect-web/package.json index 651668a60..8d49a71be 100644 --- a/packages/connect-web/package.json +++ b/packages/connect-web/package.json @@ -12,19 +12,23 @@ "build": "npm run build:cjs && npm run build:esm", "build:cjs": "tsc --project tsconfig.build.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs --declaration --declarationDir ./dist/cjs && echo >./dist/cjs/package.json '{\"type\":\"commonjs\"}'", "build:esm": "tsc --project tsconfig.build.json --outDir ./dist/esm --declaration --declarationDir ./dist/esm", - "attw": "attw --pack", - "conformance:client:chrome:promise": "connectconformance --mode client --conf ./conformance/conformance-web.yaml -v -- ./conformance/client.ts --browser chrome", - "conformance:client:chrome:callback": "connectconformance --mode client --conf ./conformance/conformance-web.yaml -v --known-failing @./conformance/known-failing-callback-client.txt -- ./conformance/client.ts --browser chrome --useCallbackClient", - "conformance:client:firefox:promise": "connectconformance --mode client --conf ./conformance/conformance-web.yaml -v -- ./conformance/client.ts --browser firefox", - "conformance:client:firefox:callback": "connectconformance --mode client --conf ./conformance/conformance-web.yaml -v --known-failing @./conformance/known-failing-callback-client.txt -- ./conformance/client.ts --browser firefox --useCallbackClient", - "conformance:client:safari:promise": "connectconformance --mode client --conf ./conformance/conformance-web.yaml -v -- ./conformance/client.ts --browser safari", - "conformance:client:safari:callback": "connectconformance --mode client --conf ./conformance/conformance-web.yaml -v --known-failing @./conformance/known-failing-callback-client.txt -- ./conformance/client.ts --browser safari --useCallbackClient", - "conformance:client:node:promise": "connectconformance --mode client --conf ./conformance/conformance-web-node.yaml -v -- ./conformance/client.ts --browser node", - "conformance:client:node:callback": "connectconformance --mode client --conf ./conformance/conformance-web-node.yaml -v --known-failing @./conformance/known-failing-callback-client.txt -- ./conformance/client.ts --browser node --useCallbackClient", - "jasmine": "jasmine --config=jasmine.json", + "conformance:safari": "npm run conformance:safari:promise && npm run conformance:client:safari:callback", + "conformance:safari:promise": "connectconformance --mode client --conf conformance/conformance-web.yaml -- tsx conformance/client.ts --browser safari", + "conformance:safari:callback": "connectconformance --mode client --conf conformance/conformance-web.yaml --known-failing @conformance/known-failing-callback-client.txt -- tsx conformance/client.ts --browser safari --useCallbackClient", + "conformance:chrome:promise": "connectconformance --mode client --conf conformance/conformance-web.yaml -- tsx conformance/client.ts --browser chrome", + "conformance:chrome:callback": "connectconformance --mode client --conf conformance/conformance-web.yaml --known-failing @conformance/known-failing-callback-client.txt -- tsx conformance/client.ts --browser chrome --useCallbackClient", + "conformance:firefox:promise": "connectconformance --mode client --conf conformance/conformance-web.yaml -- tsx conformance/client.ts --browser firefox", + "conformance:firefox:callback": "connectconformance --mode client --conf conformance/conformance-web.yaml --known-failing @conformance/known-failing-callback-client.txt -- tsx conformance/client.ts --browser firefox --useCallbackClient", + "conformance:node:promise": "connectconformance --mode client --conf conformance/conformance-web-node.yaml -- tsx conformance/client.ts --browser node", + "conformance:node:callback": "connectconformance --mode client --conf conformance/conformance-web-node.yaml --known-failing @conformance/known-failing-callback-client.txt -- tsx conformance/client.ts --browser node --useCallbackClient", + "test": "jasmine --config=jasmine.json", "generate": "buf generate --template browserstack/buf.gen.yaml", "postgenerate": "license-header browserstack/gen", - "karma:browserstack": "karma start browserstack/karma.browserstack.conf.cjs" + "test-browserstack": "karma start browserstack/karma.browserstack.conf.cjs", + "format": "prettier --write --ignore-unknown '.' '!dist' '!browserstack/gen'", + "license-header": "license-header", + "lint": "eslint --max-warnings 0 .", + "attw": "attw --pack" }, "type": "module", "sideEffects": false, diff --git a/packages/connect-web/tsconfig.json b/packages/connect-web/tsconfig.json index 1725882fe..6fc737680 100644 --- a/packages/connect-web/tsconfig.json +++ b/packages/connect-web/tsconfig.json @@ -1,4 +1,4 @@ { - "include": ["src/**/*.ts", "conformance/**/*.ts"], + "include": ["src/**/*.ts", "conformance/**/*.ts", "browserstack/**/*.ts"], "extends": "../../tsconfig.base.json" } diff --git a/packages/connect-web/turbo.json b/packages/connect-web/turbo.json new file mode 100644 index 000000000..c9bd0c466 --- /dev/null +++ b/packages/connect-web/turbo.json @@ -0,0 +1,53 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "tasks": { + "generate": { + "outputs": ["browserstack/gen/**"] + }, + "test-browserstack": { + "cache": false, + "env": ["BROWSERSTACK_USERNAME", "BROWSERSTACK_ACCESS_KEY"], + "dependsOn": ["generate", "^build"] + }, + "conformance": { + "dependsOn": ["conformance:node:promise", "conformance:node:callback"] + }, + "conformance:safari": { + "cache": false, + "dependsOn": ["^build"] + }, + "conformance:safari:promise": { + "cache": false, + "dependsOn": ["^build"] + }, + "conformance:safari:callback": { + "cache": false, + "dependsOn": ["^build"] + }, + "conformance:chrome:promise": { + "cache": false, + "dependsOn": ["^build"] + }, + "conformance:chrome:callback": { + "cache": false, + "dependsOn": ["^build"] + }, + "conformance:firefox:promise": { + "cache": false, + "dependsOn": ["^build"] + }, + "conformance:firefox:callback": { + "cache": false, + "dependsOn": ["^build"] + }, + "conformance:node:promise": { + "cache": false, + "dependsOn": ["^build"] + }, + "conformance:node:callback": { + "cache": false, + "dependsOn": ["^build"] + } + } +} diff --git a/packages/connect/README.md b/packages/connect/README.md index f60c32a77..e0e305435 100644 --- a/packages/connect/README.md +++ b/packages/connect/README.md @@ -4,7 +4,6 @@ Connect is a family of libraries for building type-safe APIs with different lang [@connectrpc/connect](https://www.npmjs.com/package/@connectrpc/connect) brings them to TypeScript, the web browser, and to Node.js. - With Connect, you define your schema first: ``` @@ -16,7 +15,7 @@ service ElizaService { And with the magic of code generation, this schema produces servers and clients: ```ts -const answer = await eliza.say({sentence: "I feel happy."}); +const answer = await eliza.say({ sentence: "I feel happy." }); console.log(answer); // {sentence: 'When you feel happy, what do you do?'} ``` @@ -40,7 +39,6 @@ gRPC and gRPC-web, and Connect's [own protocol](https://connectrpc.com/docs/prot optimized for the web. This gives you unparalleled interoperability with full-stack type-safety. - ## Get started on the web Follow our [10 minute tutorial](https://connectrpc.com/docs/web/getting-started) where @@ -53,7 +51,6 @@ We support all modern web browsers that implement the widely available [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and the [Encoding API](https://developer.mozilla.org/en-US/docs/Web/API/Encoding_API). - ## Get started on Node.js Follow our [10 minute tutorial](https://connectrpc.com/docs/node/getting-started) diff --git a/packages/connect/package.json b/packages/connect/package.json index 798876eeb..299fccbe4 100644 --- a/packages/connect/package.json +++ b/packages/connect/package.json @@ -15,7 +15,10 @@ "build": "npm run build:cjs && npm run build:esm && node scripts/update-user-agent.mjs", "build:cjs": "tsc --project tsconfig.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs --declaration --declarationDir ./dist/cjs && echo >./dist/cjs/package.json '{\"type\":\"commonjs\"}'", "build:esm": "tsc --project tsconfig.json --outDir ./dist/esm --declaration --declarationDir ./dist/esm", - "jasmine": "jasmine --config=jasmine.json", + "test": "jasmine --config=jasmine.json", + "format": "prettier --write --ignore-unknown '.' '!dist' '!src/protocol-grpc/gen'", + "license-header": "license-header", + "lint": "eslint --max-warnings 0 .", "attw": "attw --pack" }, "type": "module", diff --git a/packages/connect/turbo.json b/packages/connect/turbo.json new file mode 100644 index 000000000..1f59c7cad --- /dev/null +++ b/packages/connect/turbo.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "tasks": { + "generate": { + "outputs": ["src/protocol-grpc/gen/**"] + } + } +} diff --git a/packages/example/README.md b/packages/example/README.md index d066939d8..b839187ed 100644 --- a/packages/example/README.md +++ b/packages/example/README.md @@ -44,12 +44,11 @@ Once we have that out of the way, let's start the Connect server: npm start ``` -That's it! You should now be able to open a web browser to https://localhost:8443 and see the +That's it! You should now be able to open a web browser to https://localhost:8443 and see the example running locally. ![Screenshot](README.png) - ## Using Node.js as a client The file `src/client.ts` implements a CLI client that you can run in Node.js. @@ -87,7 +86,6 @@ npx buf curl --protocol grpc --schema . -d '{"name": "John"}' \ https://localhost:8443/connectrpc.eliza.v1.ElizaService/Introduce ``` - ## Generate code If you make changes to `eliza.proto`, make sure to re-generate the code. For example, you could rename a field, or diff --git a/packages/example/index.html b/packages/example/index.html index 9cd481ccd..58ac61af5 100644 --- a/packages/example/index.html +++ b/packages/example/index.html @@ -1,4 +1,4 @@ - + @@ -6,10 +6,10 @@ Connect-Web example -
-

Eliza

-
-
- +
+

Eliza

+
+
+ diff --git a/packages/example/package.json b/packages/example/package.json index e33b91d81..a3edc9446 100644 --- a/packages/example/package.json +++ b/packages/example/package.json @@ -3,10 +3,13 @@ "private": true, "type": "module", "scripts": { - "lint": "tsc --noEmit", + "build": "tsc --noEmit", "start": "tsx src/server.ts", "client": "tsx src/client.ts", - "generate": "buf generate" + "generate": "buf generate", + "format": "prettier --write --ignore-unknown '.' '!src/gen'", + "license-header": "license-header", + "lint": "eslint --max-warnings 0 ." }, "engines": { "node": ">=16" diff --git a/packages/example/src/client.ts b/packages/example/src/client.ts index 02172f7d1..a95fafbb0 100644 --- a/packages/example/src/client.ts +++ b/packages/example/src/client.ts @@ -15,7 +15,7 @@ import { createPromiseClient } from "@connectrpc/connect"; import { createConnectTransport } from "@connectrpc/connect-node"; import { ElizaService } from "./gen/eliza_connect.js"; -import { stdin, stdout, env } from "process"; +import { stdin, stdout, env } from "node:process"; import * as readline from "node:readline/promises"; const rl = readline.createInterface(stdin, stdout); diff --git a/packages/example/www/index.html b/packages/example/www/index.html index e358323a4..0b6ca3409 100644 --- a/packages/example/www/index.html +++ b/packages/example/www/index.html @@ -1,16 +1,16 @@ - + Connect-Web example - + -
-

Eliza

-
-
- +
+

Eliza

+
+
+ diff --git a/packages/protoc-gen-connect-es/README.md b/packages/protoc-gen-connect-es/README.md index b4db1e187..1d0019957 100644 --- a/packages/protoc-gen-connect-es/README.md +++ b/packages/protoc-gen-connect-es/README.md @@ -1,7 +1,7 @@ # @connectrpc/protoc-gen-connect-es The code generator for Connect, a simple library to work with servers and clients -in ECMAScript with the type-safety of TypeScript. It generates code that is compatible with +in ECMAScript with the type-safety of TypeScript. It generates code that is compatible with browsers and Node.js. Learn more about Connect at [github.com/connectrpc/connect-es](https://github.com/connectrpc/connect-es). @@ -33,7 +33,6 @@ We use peer dependencies to ensure that code generator and runtime library are compatible with each other. Note that npm installs them automatically, but yarn and pnpm do not. - ## Generating code ### With buf @@ -68,7 +67,6 @@ not just local protobuf files. For example, `npx buf generate buf.build/connectr generates code for the module [connectrpc/eliza](https://buf.build/connectrpc/eliza) on the Buf Schema Registry. - ### With protoc ```bash @@ -91,7 +89,6 @@ change the variable a bit: PATH=$(dirname $(yarn bin protoc-gen-es)):$(dirname $(yarn bin protoc-gen-connect-es)):$PATH ``` - ## Plugin options ### `target` @@ -100,6 +97,7 @@ This option controls whether the plugin generates JavaScript, TypeScript, or TypeScript declaration files. Possible values: + - `target=js` - generates a `_connect.js` file for every `.proto` input file. - `target=ts` - generates a `_connect.ts` file for every `.proto` input file. - `target=dts` - generates a `_connect.d.ts` file for every `.proto` input file. @@ -134,6 +132,7 @@ CommonJS is difficult to avoid, this option can be used to generate CommonJS `require()` calls. Possible values: + - `js_import_style=module` generate ECMAScript `import` / `export` statements - the default behavior. - `js_import_style=legacy_commonjs` generate CommonJS `require()` calls. @@ -153,10 +152,10 @@ By default, [protoc-gen-connect-es](https://www.npmjs.com/package/@connectrpc/pr (and all other plugins based on [@bufbuild/protoplugin](https://www.npmjs.com/package/@bufbuild/protoplugin)) generate an annotation at the top of each file: `// @ts-nocheck`. -We generate the annotation to support a wide range of compiler configurations -and future changes to the language. But there can be situations where the -annotation shadows an underlying problem, for example an unresolvable import. -To remove the annotation and to enable type checks, set the plugin option +We generate the annotation to support a wide range of compiler configurations +and future changes to the language. But there can be situations where the +annotation shadows an underlying problem, for example an unresolvable import. +To remove the annotation and to enable type checks, set the plugin option `ts_nocheck=false`. ## Example generated code @@ -191,6 +190,7 @@ message SayResponse { ``` `eliza_connect.ts` + ```ts /** * ElizaService provides a way to talk to Eliza, a port of the DOCTOR script @@ -216,6 +216,6 @@ export const ElizaService = { O: SayResponse, kind: MethodKind.Unary, }, - } + }, } as const; ``` diff --git a/packages/protoc-gen-connect-es/bin/protoc-gen-connect-es b/packages/protoc-gen-connect-es/bin/protoc-gen-connect-es index 1087fd8b8..9a6324f64 100755 --- a/packages/protoc-gen-connect-es/bin/protoc-gen-connect-es +++ b/packages/protoc-gen-connect-es/bin/protoc-gen-connect-es @@ -1,6 +1,8 @@ #!/usr/bin/env node -const {runNodeJs} = require("@bufbuild/protoplugin"); -const {protocGenConnectEs} = require("../dist/cjs/src/protoc-gen-connect-es-plugin.js"); +const { runNodeJs } = require("@bufbuild/protoplugin"); +const { + protocGenConnectEs, +} = require("../dist/cjs/src/protoc-gen-connect-es-plugin.js"); runNodeJs(protocGenConnectEs); diff --git a/packages/protoc-gen-connect-es/package.json b/packages/protoc-gen-connect-es/package.json index f8d0d1e05..92277f7ae 100644 --- a/packages/protoc-gen-connect-es/package.json +++ b/packages/protoc-gen-connect-es/package.json @@ -16,7 +16,10 @@ }, "scripts": { "prebuild": "rm -rf ./dist/*", - "build": "tsc --project tsconfig.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs" + "build": "tsc --project tsconfig.json --module commonjs --verbatimModuleSyntax false --moduleResolution node10 --outDir ./dist/cjs", + "format": "prettier --write --ignore-unknown '.' '!dist'", + "license-header": "license-header", + "lint": "eslint --max-warnings 0 ." }, "preferUnplugged": true, "dependencies": { diff --git a/packages/protoc-gen-connect-es/tsconfig.json b/packages/protoc-gen-connect-es/tsconfig.json index 85cbbc9df..3086186a0 100644 --- a/packages/protoc-gen-connect-es/tsconfig.json +++ b/packages/protoc-gen-connect-es/tsconfig.json @@ -2,6 +2,11 @@ "files": ["src/protoc-gen-connect-es-plugin.ts"], "extends": "../../tsconfig.base.json", "compilerOptions": { - "resolveJsonModule": true + // We import the plugin's version number from package.json + "resolveJsonModule": true, + // This package is CommonJS + "verbatimModuleSyntax": false, + "module": "CommonJS", + "moduleResolution": "Node10" } } diff --git a/packages/protoc-gen-connect-es/turbo.json b/packages/protoc-gen-connect-es/turbo.json new file mode 100644 index 000000000..fd0ee0db3 --- /dev/null +++ b/packages/protoc-gen-connect-es/turbo.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "tasks": { + "lint": { + "dependsOn": ["format", "^build", "generate", "build"] + } + } +} diff --git a/turbo.json b/turbo.json new file mode 100644 index 000000000..1e315ea13 --- /dev/null +++ b/turbo.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://turbo.build/schema.json", + "tasks": { + "build": { + "dependsOn": ["^build", "generate"], + "outputs": ["dist/**"] + }, + "generate": { + "dependsOn": ["^build"], + "outputs": ["src/gen/**"] + }, + "test": { + "dependsOn": ["build"], + "cache": false + }, + "conformance": { + "cache": false, + "dependsOn": ["^build"] + }, + "format": {}, + "license-header": { + "dependsOn": ["generate"] + }, + "lint": { + "dependsOn": ["format", "^build", "generate"] + }, + "attw": { + "dependsOn": ["build"] + }, + "//#format": { + "inputs": ["$TURBO_DEFAULT$", "!packages/**", "package-lock.json"] + }, + "//#license-header": { + "inputs": ["$TURBO_DEFAULT$", "!packages/**"] + }, + "//#lint": { + "dependsOn": ["format"], + "inputs": ["$TURBO_DEFAULT$", "!packages/**", "package-lock.json"] + } + } +}