Repository navigation
feat(errors): typed errors with status and URL for error responses #27
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| types: ['opened', 'synchronize'] | |
| branches: [master, main] | |
| # Read-only token unless a job asks for more; none of these jobs writes. | |
| permissions: | |
| contents: read | |
| jobs: | |
| automated-test: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| node-version: [18, 20, 22, 24] | |
| environment: | |
| name: Production | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Use test/build Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node-version < 22 && 22 || matrix.node-version }} | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm i --frozen-lockfile | |
| - name: Run `typecheck` | |
| if: matrix.node-version >= 22 | |
| run: pnpm typecheck | |
| - name: Run `lint` | |
| if: matrix.node-version >= 22 | |
| run: pnpm lint | |
| - name: Build package for entry tests | |
| if: matrix.node-version >= 22 | |
| run: pnpm build | |
| - name: Run `test` | |
| if: matrix.node-version >= 22 | |
| run: pnpm test | |
| env: | |
| RECORD_NETWORK_REQUESTS: 'false' | |
| MOCK_FETCH: 'true' | |
| CREDENTIAL_ICLOUD_USERNAME: ${{secrets.CREDENTIAL_ICLOUD_USERNAME}} | |
| CREDENTIAL_ICLOUD_APP_SPECIFIC_PASSWORD: ${{secrets.CREDENTIAL_ICLOUD_APP_SPECIFIC_PASSWORD}} | |
| CREDENTIAL_GOOGLE_USERNAME: ${{secrets.CREDENTIAL_GOOGLE_USERNAME}} | |
| CREDENTIAL_GOOGLE_REFRESH_TOKEN: ${{secrets.CREDENTIAL_GOOGLE_REFRESH_TOKEN}} | |
| CREDENTIAL_GOOGLE_CLIENT_ID: ${{secrets.CREDENTIAL_GOOGLE_CLIENT_ID}} | |
| CREDENTIAL_GOOGLE_CLIENT_SECRET: ${{secrets.CREDENTIAL_GOOGLE_CLIENT_SECRET}} | |
| CREDENTIAL_FASTMAIL_USERNAME: ${{secrets.CREDENTIAL_FASTMAIL_USERNAME}} | |
| CREDENTIAL_FASTMAIL_APP_PASSWORD: ${{secrets.CREDENTIAL_FASTMAIL_APP_PASSWORD}} | |
| CREDENTIAL_NEXTCLOUD_USERNAME: ${{secrets.CREDENTIAL_NEXTCLOUD_USERNAME}} | |
| CREDENTIAL_NEXTCLOUD_PASSWORD: ${{secrets.CREDENTIAL_NEXTCLOUD_PASSWORD}} | |
| CREDENTIAL_NEXTCLOUD_SERVER_URL: ${{secrets.CREDENTIAL_NEXTCLOUD_SERVER_URL}} | |
| - name: Check public consumers and examples | |
| if: matrix.node-version >= 22 | |
| run: | | |
| pnpm test:consumer | |
| pnpm test:examples | |
| pnpm exec knip --dependencies --workspace . | |
| - name: Build package for runtime smoke test | |
| if: matrix.node-version < 22 | |
| run: | | |
| pnpm build | |
| mkdir -p /tmp/tsdav-runtime | |
| npm pack --pack-destination /tmp/tsdav-runtime > /tmp/tsdav-runtime/pack.out | |
| - name: Use runtime Node.js | |
| if: matrix.node-version < 22 | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| - name: Test packed package on runtime Node.js | |
| if: matrix.node-version < 22 | |
| run: | | |
| mkdir -p /tmp/tsdav-runtime/consumer | |
| cd /tmp/tsdav-runtime/consumer | |
| echo '{ "name": "consumer", "version": "0.0.0", "private": true }' > package.json | |
| # Installed under the name `tsdav`, the way consumers alias @philflow/tsdav. | |
| npm install "tsdav@file:$(find /tmp/tsdav-runtime -maxdepth 1 -name 'philflow-tsdav-*.tgz' -print -quit)" --no-audit --no-fund | |
| node -e "const mod = require('tsdav'); if (typeof mod.DAVClient !== 'function') process.exit(1)" | |
| node --input-type=module -e "import { DAVClient } from 'tsdav'; if (typeof DAVClient !== 'function') process.exit(1)" | |
| bun-smoke-test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Use Node.js 22 | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm i --frozen-lockfile | |
| - name: Build | |
| run: pnpm build | |
| - name: Setup Bun | |
| uses: oven-sh/setup-bun@v2 | |
| with: | |
| bun-version: latest | |
| - name: Import + instantiate from ESM bundle under Bun | |
| run: | | |
| cat > .tsdav-bun-smoke.mjs <<'EOF' | |
| import { DAVClient, createDAVClient, DAVNamespace } from './dist/tsdav.esm.js'; | |
| if (typeof DAVClient !== 'function') throw new Error('DAVClient missing'); | |
| if (typeof createDAVClient !== 'function') throw new Error('createDAVClient missing'); | |
| if (!DAVNamespace) throw new Error('DAVNamespace missing'); | |
| // Exercise the auth helper path that previously crashed under | |
| // native ESM due to base-64's CJS shape. | |
| new DAVClient({ | |
| serverUrl: 'http://example.com', | |
| credentials: { username: 'u', password: 'p' }, | |
| }); | |
| console.log('bun-ok'); | |
| EOF | |
| bun .tsdav-bun-smoke.mjs | tee /tmp/bun.out | |
| grep -q '^bun-ok$' /tmp/bun.out | |
| - name: Import from CJS bundle under Bun | |
| run: | | |
| cat > .tsdav-bun-cjs.cjs <<'EOF' | |
| const { DAVClient } = require('./dist/tsdav.cjs.js'); | |
| if (typeof DAVClient !== 'function') throw new Error('DAVClient missing from CJS'); | |
| console.log('bun-cjs-ok'); | |
| EOF | |
| bun .tsdav-bun-cjs.cjs | tee /tmp/bun-cjs.out | |
| grep -q '^bun-cjs-ok$' /tmp/bun-cjs.out | |
| deno-smoke-test: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Use Node.js 22 | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm i --frozen-lockfile | |
| - name: Build | |
| run: pnpm build | |
| - name: Setup Deno | |
| uses: denoland/setup-deno@v2 | |
| with: | |
| deno-version: v2.x | |
| - name: Pack tarball from repo root | |
| id: pack | |
| run: | | |
| # Pack using the real root package.json so that `files`, `main`, | |
| # `module`, `types`, and `exports` resolve the way they would on | |
| # the npm registry. `dist/package.json` is a stale informational | |
| # copy with paths that are only valid from inside the repo root. | |
| mkdir -p /tmp/deno-smoke | |
| npm pack --pack-destination /tmp/deno-smoke > /tmp/pack.out | |
| TARBALL=$(find /tmp/deno-smoke -maxdepth 1 -name 'philflow-tsdav-*.tgz' -print -quit) | |
| echo "tarball=$TARBALL" >> "$GITHUB_OUTPUT" | |
| - name: 'Import @philflow/tsdav under Deno via npm: specifier' | |
| run: | | |
| # Build a throwaway consumer workspace that installs the packed | |
| # tarball into node_modules, then tell Deno to consume that | |
| # existing node_modules via `nodeModulesDir: "manual"`. Using | |
| # `--node-modules-dir=auto` here would make Deno resolve | |
| # `npm:@philflow/tsdav` against the public registry (ignoring our local | |
| # build), which defeats the whole point of the smoke test. | |
| mkdir -p /tmp/deno-smoke/consumer | |
| cd /tmp/deno-smoke/consumer | |
| cat > package.json <<EOF | |
| { | |
| "name": "consumer", | |
| "private": true, | |
| "type": "module", | |
| "dependencies": { "@philflow/tsdav": "file:${{ steps.pack.outputs.tarball }}" } | |
| } | |
| EOF | |
| npm install --no-audit --no-fund --no-save --no-package-lock | |
| # Sanity-check: the installed package must be the freshly packed | |
| # version and must NOT drag in `cross-fetch`. | |
| INSTALLED_VERSION=$(node -p "require('./node_modules/@philflow/tsdav/package.json').version") | |
| echo "installed tsdav version: $INSTALLED_VERSION" | |
| if [ -d node_modules/cross-fetch ]; then | |
| echo "cross-fetch unexpectedly present in node_modules" | |
| exit 1 | |
| fi | |
| cat > deno.json <<'EOF' | |
| { | |
| "nodeModulesDir": "manual" | |
| } | |
| EOF | |
| cat > smoke.ts <<'EOF' | |
| import { DAVClient, createDAVClient, DAVNamespace } from 'npm:@philflow/tsdav'; | |
| if (typeof DAVClient !== 'function') throw new Error('DAVClient missing'); | |
| if (typeof createDAVClient !== 'function') throw new Error('createDAVClient missing'); | |
| if (!DAVNamespace) throw new Error('DAVNamespace missing'); | |
| new DAVClient({ | |
| serverUrl: 'http://example.com', | |
| credentials: { username: 'u', password: 'p' }, | |
| }); | |
| console.log('deno-ok'); | |
| EOF | |
| deno run --allow-read --allow-env smoke.ts | tee /tmp/deno.out | |
| grep -q '^deno-ok$' /tmp/deno.out | |
| docs-check: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: docs | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: pnpm/action-setup@v4 | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: 'pnpm' | |
| cache-dependency-path: docs/pnpm-lock.yaml | |
| - run: pnpm install --frozen-lockfile | |
| - name: Install shared-source verification tools | |
| working-directory: . | |
| run: pnpm install --frozen-lockfile | |
| - name: Check docs dependencies | |
| working-directory: . | |
| run: pnpm exec knip --dependencies --workspace docs | |
| - run: pnpm test | |
| - run: pnpm typecheck | |
| - run: pnpm build |