diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..9c4583f --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,75 @@ +name: Playground + +on: + push: + branches: + - master + paths: + - '.github/workflows/pages.yml' + - 'apps/showcase/**' + - 'docs/benchmarks/showcase-latest.json' + - 'src/**' + - 'package.json' + - 'pnpm-lock.yaml' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: github-pages + cancel-in-progress: false + +jobs: + build: + name: Build playground + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + with: + persist-credentials: false + + - name: Setup pnpm + uses: pnpm/action-setup@f40ffcd9367d9f12939873eb1018b921a783ffaa # v4 + with: + version: 10.26.2 + + - name: Setup Node + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 + with: + node-version: 24 + cache: pnpm + cache-dependency-path: pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Test playground + run: pnpm playground:test + + - name: Build playground for GitHub Pages + run: pnpm playground:build:pages + + - name: Configure Pages + uses: actions/configure-pages@13742983d7736d9b0ae728b81ab479565c72886d # v5 + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4 + with: + path: apps/showcase/dist + + deploy: + name: Deploy playground + needs: build + runs-on: ubuntu-latest + permissions: + pages: write + id-token: write + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 821e8f7..bbb2d12 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ on: required: true type: string publish_npm: - description: Publish to npm. Requires NPM_TOKEN repo secret. + description: Publish to npm via trusted publishing or NPM_TOKEN bootstrap. required: false type: boolean default: false @@ -24,9 +24,10 @@ jobs: release: name: Validate, pack, and release runs-on: ubuntu-latest + environment: npm steps: - name: Checkout - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 with: persist-credentials: false @@ -36,12 +37,11 @@ jobs: version: 10.26.2 - name: Setup Node - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6 with: node-version: 24 - cache: pnpm - cache-dependency-path: pnpm-lock.yaml registry-url: https://registry.npmjs.org + package-manager-cache: false - name: Install dependencies run: pnpm install --frozen-lockfile @@ -72,6 +72,9 @@ jobs: - name: Build package run: pnpm build + - name: Verify published entry points + run: pnpm verify:package-exports + - name: Pack tarball run: | mkdir -p .release @@ -92,18 +95,13 @@ jobs: GH_TOKEN: ${{ github.token }} run: | if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then - echo "Release $RELEASE_TAG already exists" >&2 - exit 1 + echo "Release $RELEASE_TAG already exists; reusing it for an npm-only rerun." + else + gh release create "$RELEASE_TAG" .release/*.tgz --title "$RELEASE_TAG" --notes-file RELEASE_NOTES.md fi - gh release create "$RELEASE_TAG" .release/*.tgz --title "$RELEASE_TAG" --notes-file RELEASE_NOTES.md - name: Publish to npm if: ${{ github.event_name == 'workflow_dispatch' && inputs.publish_npm }} env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - run: | - if [ -z "${NODE_AUTH_TOKEN:-}" ]; then - echo "NPM_TOKEN secret missing; refusing pnpm publish." >&2 - exit 1 - fi - pnpm publish --provenance --access public --no-git-checks + run: npm publish --provenance --access public diff --git a/CHANGELOG.md b/CHANGELOG.md index d4a8e13..b7fcf9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,8 @@ Initial managed release for the @tanstack/db parity milestone. -- Ships the all-green 289/289 export compatibility catalog against - `@tanstack/db@0.6.5`. +- Ships the all-green 294/294 export compatibility catalog against + `@tanstack/db@0.6.16`. - Adds TanStack DB migration docs, release notes, and head-to-head benchmark coverage. - Keeps ArrowBase columnar storage, snapshot/Arrow IPC, IndexedDB persistence, diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8a4a9cc --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,32 @@ +# Contributing to ArrowBase + +Thanks for helping improve ArrowBase. Bug reports, focused fixes, compatibility +cases, and benchmark methodology improvements are welcome. + +## Development setup + +Requirements: Node.js 20 or newer and pnpm 10.26.2. + +```sh +pnpm install --frozen-lockfile +pnpm check +pnpm playground:check +pnpm build +pnpm verify:package-exports +``` + +Run `pnpm playground:dev` for the browser playground. Run +`pnpm playground:e2e` after installing Playwright's Chromium dependency when +changing user-facing flows. + +## Pull requests + +- Keep changes focused and add behavioral tests for public API changes. +- Preserve the exact compatibility catalog and operator-manifest gates. +- Treat benchmark results as evidence: compare equal result hashes before + reporting timing or memory ratios. +- Add a changelog entry for user-visible changes. +- Do not commit generated build output, credentials, or local benchmark noise. + +By contributing, you agree that your contributions are licensed under the MIT +License in this repository. diff --git a/README.md b/README.md index f796def..3000c21 100644 --- a/README.md +++ b/README.md @@ -1,55 +1,81 @@ # ArrowBase -ArrowBase — columnar Arrow store with @tanstack/db-compatible API. +[![CI](https://github.com/natanelia/arrowbase/actions/workflows/ci.yml/badge.svg)](https://github.com/natanelia/arrowbase/actions/workflows/ci.yml) +[![npm](https://img.shields.io/npm/v/arrowbase.svg)](https://www.npmjs.com/package/arrowbase) +[![license: MIT](https://img.shields.io/badge/license-MIT-171a18.svg)](./LICENSE) +[![playground](https://img.shields.io/badge/playground-open-df542d.svg)](https://natanelia.github.io/arrowbase/) -ArrowBase is an in-memory, local-first document store built on Apache -Arrow-style column buffers inside a `SharedArrayBuffer`. It keeps the -TanStack DB mental model (`createCollection`, transactions, live queries, -local-only/local-storage helpers) while adding columnar storage, -vectorized filters, snapshot/Arrow IPC export, IndexedDB persistence, -cross-tab sync, and React hooks. +> ArrowBase — columnar Arrow store with @tanstack/db-compatible API. -**Release status**: v0.1.0 @tanstack/db parity release, upgraded to -`@tanstack/db@0.6.16`. The compatibility catalog has expanded to 294/294 green -rows, while a runtime root-export audit plus an exact 28/28 operator-manifest -test guard the new upstream surface. Documented tolerances remain for fixed-capacity storage, -BigInt/Number key normalization, and floating-point aggregates. +ArrowBase is a local-first TypeScript database built on Arrow-style column +buffers. It keeps the TanStack DB mental model—collections, transactions, +live queries, persistence, and React hooks—while adding vectorized filters, +spatial indexes, Arrow IPC, snapshots, and cross-tab sync. -**Release docs**: +**v0.1.0 release candidate:** the compatibility catalog is 294/294 green +against `@tanstack/db@0.6.16`. Exact public-export and operator-manifest tests +guard that surface. Pre-1.0 means the API can still evolve between minor +versions; documented tolerances cover fixed capacity, safe-integer key +normalization, and floating-point aggregates. -- [COMPATIBILITY.md](./COMPATIBILITY.md) — export-by-export parity catalog. -- [MIGRATING.md](./MIGRATING.md) — side-by-side TanStack DB → ArrowBase guide. -- [docs/showcase.md](./docs/showcase.md) — playable ArrowBase vs TanStack DB playground and benchmark guide. -- [RELEASE_NOTES.md](./RELEASE_NOTES.md) — v0.1.0 parity release notes. -- [CHANGELOG.md](./CHANGELOG.md) — release history. +[Open the live playground](https://natanelia.github.io/arrowbase/) · +[Read the migration guide](./MIGRATING.md) · +[Inspect compatibility](./COMPATIBILITY.md) + +**Project docs**: + +- [COMPATIBILITY.md](./COMPATIBILITY.md) — all 294 audited upstream exports. +- [MIGRATING.md](./MIGRATING.md) — side-by-side TanStack DB replacements. +- [docs/showcase.md](./docs/showcase.md) — playground and benchmark contract. +- [docs/ivm.md](./docs/ivm.md) — incremental query engine boundaries. +- [SECURITY.md](./SECURITY.md) and [CONTRIBUTING.md](./CONTRIBUTING.md). +- [RELEASE_NOTES.md](./RELEASE_NOTES.md) and [CHANGELOG.md](./CHANGELOG.md). ## Requirements -- Node ≥ 20 or a browser with `SharedArrayBuffer` available - (COOP/COEP headers required in browsers). +- Node.js 20 or newer. +- Modern browsers. ArrowBase uses `SharedArrayBuffer` when cross-origin + isolation is available and safely falls back to `ArrayBuffer` otherwise. ## Install -```bash -pnpm install -pnpm build -pnpm test # unit + integration suite -pnpm bench # regression benches (tinybench) -pnpm bench:vs-tanstack # head-to-head @tanstack/db parity bench -pnpm bench:showcase # regenerate docs/benchmarks/showcase-latest.json -pnpm playground:dev # playable ArrowBase vs TanStack DB showcase -pnpm playground:test # operation parity + UI tests -pnpm playground:build # production showcase build -pnpm playground:e2e # desktop + mobile Playwright smoke +```sh +npm install arrowbase ``` -## Showcase playground +Optional integrations are installed only when you use their subpaths: -`apps/showcase` is the kitchen-sink ArrowBase vs TanStack DB demo. It seeds deterministic city data, runs each headline operation through both an ArrowBase adapter and a TanStack DB adapter, compares the correctness hash first, and only then unlocks speed and memory ratios in the UI. +```sh +npm install @tanstack/db # arrowbase/tanstack +npm install apache-arrow # arrowbase/arrow +npm install react # arrowbase/react +``` -Run it locally: +## Playground -```bash +The hosted [ArrowBase City Ops Console](https://natanelia.github.io/arrowbase/) +is an interactive ArrowBase vs TanStack DB playground. It runs deterministic +spatial, reactive, transactional, and IPC workloads in browser workers. A +correctness hash must match before timing or memory ratios unlock. + +See [docs/showcase.md](./docs/showcase.md) for the benchmark contract, URL +controls, operation registry, and local or tunnel workflows. + +## Development + +```sh +pnpm install --frozen-lockfile +pnpm check # typecheck + 700+ behavioral tests +pnpm playground:check # UI typecheck + tests + production build +pnpm playground:e2e # desktop and mobile Playwright smoke +pnpm bench:vs-tanstack # head-to-head benchmark +pnpm build +pnpm verify:package-exports +``` + +Use these focused playground commands during UI work: + +```sh pnpm playground:dev pnpm playground:typecheck pnpm playground:test @@ -57,7 +83,8 @@ pnpm playground:build pnpm playground:e2e ``` -The playground is mobile-friendly for Telegram viewing and can be shared temporarily with Cloudflare Tunnel; see [docs/showcase.md](./docs/showcase.md) for the `trycloudflare.com` workflow, bounded benchmark URL params, generated `docs/benchmarks/showcase-latest.json` snapshot, operation registry, and CI gates. +Performance ratios are evidence, not promises: the showcase compares equal +result hashes before presenting speed or memory measurements. ## What's in the box diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 51e8422..8e27fe5 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,13 +2,13 @@ Version: **v0.1.0** -Compatibility: 289/289 exports ✅ +Compatibility: 294/294 exports ✅ ## Summary This release positions ArrowBase as a columnar Arrow store with an `@tanstack/db`-compatible API. The public surface has been reviewed against -`@tanstack/db@0.6.5`, the compatibility catalog is all green, and migration docs +`@tanstack/db@0.6.16`, the compatibility catalog is all green, and migration docs now show side-by-side replacements for common TanStack DB usage. ## Highlights diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..38e2824 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,17 @@ +# Security policy + +## Supported versions + +ArrowBase is pre-1.0. Security fixes are applied to the latest published minor +release only. + +## Reporting a vulnerability + +Please do not open a public issue for a suspected vulnerability. Use +[GitHub private vulnerability reporting](https://github.com/natanelia/arrowbase/security/advisories/new) +so the report, reproduction, and remediation can be coordinated privately. + +Include the affected version, runtime, a minimal reproduction, and the impact +you believe is possible. You should receive an acknowledgement within seven +days. Confirmed issues will be tracked in a private advisory until a fix and +coordinated disclosure are ready. diff --git a/apps/showcase/package.json b/apps/showcase/package.json index 66d0028..c9875fc 100644 --- a/apps/showcase/package.json +++ b/apps/showcase/package.json @@ -6,6 +6,7 @@ "scripts": { "dev": "vite --host 0.0.0.0", "build": "tsc --noEmit && vite build", + "build:pages": "tsc --noEmit && vite build --base=/arrowbase/", "typecheck": "tsc --noEmit", "preview": "vite preview --host 0.0.0.0", "e2e": "playwright test", diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 892b87e..bcace56 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -13,8 +13,8 @@ step. The first managed release is **v0.1.0**. `E404` means the package is not published yet. -2. If publishing to npm, add an npm automation token as the GitHub Actions repo - secret `NPM_TOKEN`: +2. Bootstrap the first npm publish with a granular automation token stored as + the GitHub Actions secret `NPM_TOKEN`: ```sh gh secret set NPM_TOKEN --repo natanelia/arrowbase @@ -24,6 +24,17 @@ step. The first managed release is **v0.1.0**. the `.tgz` package artifact. npm publish is skipped unless the workflow is manually dispatched with `publish_npm=true`. +3. After the package exists, configure npm trusted publishing for GitHub + Actions with owner `natanelia`, repository `arrowbase`, workflow + `release.yml`, environment `npm`, and the `npm publish` allowed action. + The workflow already grants `id-token: write` and uses a compatible Node/npm + version, so later publishes can use short-lived OIDC credentials with + automatic provenance instead of a long-lived write token. + + After one trusted publish succeeds, remove `NPM_TOKEN` and revoke the npm + automation token. Keep a token only as a documented bootstrap or emergency + fallback. + ## Local preflight Run from a clean `master` worktree: @@ -59,7 +70,8 @@ point, packs, uploads the tarball artifact, and creates the GitHub release from ## Optional npm publish -After `NPM_TOKEN` exists, run the `Release` workflow manually: +After `NPM_TOKEN` exists for the bootstrap—or after trusted publishing is +configured—run the `Release` workflow manually: - `version`: `0.1.0` - `publish_npm`: `true` @@ -76,6 +88,6 @@ npm publish --provenance --access public wrong commit, delete the GitHub release and tag explicitly, then recreate. - If `pnpm release:verify`, `pnpm check`, `pnpm build`, or `pnpm verify:package-exports` fails, fix the repo and commit before tagging. -- If npm publish fails because `NPM_TOKEN` is missing, add the secret and rerun - the manual workflow. Do not publish from a laptop unless GitHub Actions is - unavailable. +- If npm publish cannot authenticate, verify the trusted-publisher workflow and + environment names; use `NPM_TOKEN` only for the first publish or emergency + fallback. Do not publish from a laptop unless GitHub Actions is unavailable. diff --git a/docs/showcase.md b/docs/showcase.md index 4e682b2..0c2abed 100644 --- a/docs/showcase.md +++ b/docs/showcase.md @@ -51,6 +51,21 @@ For fast E2E smoke runs, the app accepts bounded benchmark URL params: /?benchRows=512&benchSamples=1&benchWarmup=0 ``` + +## Hosted playground + +The production playground is deployed to +[natanelia.github.io/arrowbase](https://natanelia.github.io/arrowbase/) by +`.github/workflows/pages.yml` whenever relevant code reaches `master`. + +```bash +pnpm playground:build:pages +``` + +GitHub Pages does not provide custom COOP/COEP headers. ArrowBase therefore +uses its safe `ArrayBuffer` fallback there; memory APIs that require +cross-origin isolation are reported as unavailable rather than estimated. + ## Sharing through Cloudflare Tunnel For an ephemeral public demo, start the playground and tunnel it: diff --git a/package.json b/package.json index dd8f70e..2cb11de 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "bugs": { "url": "https://github.com/natanelia/arrowbase/issues" }, - "homepage": "https://github.com/natanelia/arrowbase#readme", + "homepage": "https://natanelia.github.io/arrowbase/", "type": "module", "sideEffects": false, "main": "./dist/index.js", @@ -53,20 +53,16 @@ }, "files": [ "dist", - "src", "README.md", "MIGRATING.md", "COMPATIBILITY.md", "CHANGELOG.md", "RELEASE_NOTES.md", - "docs/RELEASING.md", - "docs/showcase.md", - "docs/benchmarks/showcase-latest.json", - "PRD.md", "LICENSE" ], "publishConfig": { - "access": "public" + "access": "public", + "provenance": true }, "scripts": { "build": "tsup", @@ -87,6 +83,7 @@ "bench:vs-tanstack": "node --expose-gc --import tsx bench/vs-tanstack.ts", "release:verify": "node scripts/verify-release.mjs", "release:pack": "pnpm build && pnpm pack --pack-destination .release", + "playground:build:pages": "pnpm --filter @arrowbase/showcase build:pages", "release:tag": "git tag v$npm_package_version", "prepublishOnly": "pnpm release:verify && pnpm check && pnpm build && pnpm verify:package-exports", "check": "pnpm typecheck && pnpm test:public-api && pnpm test:differential && pnpm test:golden && pnpm test:showcase-docs && pnpm test", diff --git a/scripts/verify-release.mjs b/scripts/verify-release.mjs index 3be2e27..a78bf78 100644 --- a/scripts/verify-release.mjs +++ b/scripts/verify-release.mjs @@ -29,14 +29,15 @@ if (expectedVersion && version !== expectedVersion) { if (pkg.private === true) fail('package.json must be publishable: private cannot be true'); if (pkg.license !== 'MIT') fail('package.json license must be MIT'); if (pkg.publishConfig?.access !== 'public') fail('publishConfig.access must be public'); +if (pkg.publishConfig?.provenance !== true) fail('publishConfig.provenance must be true'); if (pkg.repository?.url !== 'git+ssh://git@github.com/natanelia/arrowbase.git') { fail('repository.url must point at natanelia/arrowbase'); } if (pkg.bugs?.url !== 'https://github.com/natanelia/arrowbase/issues') { fail('bugs.url must point at GitHub issues'); } -if (pkg.homepage !== 'https://github.com/natanelia/arrowbase#readme') { - fail('homepage must point at GitHub README'); +if (pkg.homepage !== 'https://natanelia.github.io/arrowbase/') { + fail('homepage must point at the hosted playground'); } for (const script of ['check', 'build', 'release:verify', 'release:pack', 'release:tag']) { @@ -51,6 +52,9 @@ const requiredFiles = [ 'RELEASE_NOTES.md', 'docs/RELEASING.md', '.github/workflows/release.yml', + '.github/workflows/pages.yml', + 'SECURITY.md', + 'CONTRIBUTING.md', ]; for (const file of requiredFiles) { if (!exists(file)) fail(`missing release file: ${file}`); @@ -77,12 +81,28 @@ if (exists('.github/workflows/release.yml')) { 'pnpm pack --pack-destination .release', 'include-hidden-files: true', 'gh release create', + 'pnpm verify:package-exports', 'npm publish --provenance --access public', ]) { if (!workflow.includes(marker)) fail(`release workflow must mention ${marker}`); } } + +if (exists('.github/workflows/pages.yml')) { + const workflow = read('.github/workflows/pages.yml'); + for (const marker of [ + 'pnpm playground:test', + 'pnpm playground:build:pages', + 'actions/configure-pages@', + 'actions/upload-pages-artifact@', + 'actions/deploy-pages@', + 'path: apps/showcase/dist', + ]) { + if (!workflow.includes(marker)) fail(`pages workflow must mention ${marker}`); + } +} + if (errors.length > 0) { console.error('Release verification failed:'); for (const error of errors) console.error(`- ${error}`); diff --git a/tests/release-readiness.test.ts b/tests/release-readiness.test.ts index e690a02..a5f6460 100644 --- a/tests/release-readiness.test.ts +++ b/tests/release-readiness.test.ts @@ -71,7 +71,7 @@ describe('Layer I release readiness docs', () => { const notes = readFileSync(releaseUrl, 'utf8'); expect(notes).toContain('Release notes — @tanstack/db parity'); - expect(notes).toContain('Compatibility: 289/289 exports ✅'); + expect(notes).toContain('Compatibility: 294/294 exports ✅'); expect(notes).toContain('Migration'); expect(notes).toContain('Benchmarks'); }); @@ -86,7 +86,7 @@ describe('v0.1.0 release management', () => { repository?: { type: string; url: string }; bugs?: { url: string }; homepage?: string; - publishConfig?: { access: string }; + publishConfig?: { access: string; provenance?: boolean }; scripts: Record; }; @@ -98,9 +98,10 @@ describe('v0.1.0 release management', () => { url: 'git+ssh://git@github.com/natanelia/arrowbase.git', }); expect(pkg.bugs?.url).toBe('https://github.com/natanelia/arrowbase/issues'); - expect(pkg.homepage).toBe('https://github.com/natanelia/arrowbase#readme'); + expect(pkg.homepage).toBe('https://natanelia.github.io/arrowbase/'); expect(pkg.publishConfig?.access).toBe('public'); expect(pkg.scripts['release:verify']).toBe('node scripts/verify-release.mjs'); + expect(pkg.publishConfig?.provenance).toBe(true); expect(pkg.scripts['release:pack']).toBe('pnpm build && pnpm pack --pack-destination .release'); expect(pkg.scripts['release:tag']).toBe('git tag v$npm_package_version'); }); @@ -115,6 +116,7 @@ describe('v0.1.0 release management', () => { expect(workflow).toContain("'v*.*.*'"); expect(workflow).toContain('pnpm release:verify'); expect(workflow).toContain('pnpm pack --pack-destination .release'); + expect(workflow).toContain('pnpm verify:package-exports'); expect(workflow).toContain('include-hidden-files: true'); expect(workflow).toContain('gh release create'); expect(workflow).toContain('npm publish --provenance --access public'); diff --git a/tests/showcase-docs.test.ts b/tests/showcase-docs.test.ts index 475cbda..2e06997 100644 --- a/tests/showcase-docs.test.ts +++ b/tests/showcase-docs.test.ts @@ -54,13 +54,15 @@ describe('showcase playground docs and CI integration', () => { scripts: Record; }; - expect(pkg.files).toContain('docs/showcase.md'); - expect(pkg.files).toContain('docs/benchmarks/showcase-latest.json'); + expect(pkg.files).not.toContain('src'); + expect(pkg.files).not.toContain('docs/showcase.md'); + expect(pkg.files).not.toContain('docs/benchmarks/showcase-latest.json'); expect(pkg.scripts['test:showcase-docs']).toBe('vitest run -c vitest.config.ts tests/showcase-docs.test.ts'); expect(pkg.scripts['bench:showcase']).toBe('pnpm --filter @arrowbase/showcase bench:report'); expect(pkg.scripts['playground:check']).toBe( 'pnpm playground:typecheck && pnpm playground:test && pnpm playground:build', ); + expect(pkg.scripts['playground:build:pages']).toContain('build:pages'); expect(pkg.scripts['check']).toContain('pnpm test:showcase-docs'); }); @@ -77,4 +79,19 @@ describe('showcase playground docs and CI integration', () => { expect(ci).toContain(marker); } }); + + it('deploys the production playground through GitHub Pages', () => { + const workflow = readProjectFile('.github/workflows/pages.yml'); + + for (const marker of [ + 'pnpm playground:test', + 'pnpm playground:build:pages', + 'actions/configure-pages@', + 'actions/upload-pages-artifact@', + 'actions/deploy-pages@', + 'path: apps/showcase/dist', + ]) { + expect(workflow).toContain(marker); + } + }); });