- serveDirectory: replace regex path traversal guard with resolve+relative
check, which is simpler to reason about and handles encoded edge cases
- drop test:browser:coverage scripts; c8 only covers Node orchestration
code, not browser-executed code inside page.evaluate() — the resulting
lcov is nearly empty and would confuse CI coverage dashboards
- guard server?.close() in after() hooks so teardown doesn't throw a
TypeError if the before() hook failed to start the server
- extract setupLoopback() helper inside each page.evaluate() in
transport.browser.ts to avoid verbatim repetition between tests
- add comment in helpers.browser.ts explaining the IDB readonly-transaction
trick that ensures setState()'s async write has settled before opening
a second IndexedDBState instance
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Uses Playwright as a library within node:test (not @playwright/test) to
keep the same runner and script conventions. Browser test files use the
*.browser.ts extension so the existing src/**/*.test.ts glob picks up zero
browser tests, leaving the regular test suite unaffected.
Key pieces:
- .npmrc: PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 prevents binary downloads on
npm ci; browsers are installed explicitly in CI via playwright install
- @webnet/browser-test-utils: new private package exporting forBrowsers()
(iterates chromium + firefox, handles browser lifecycle within node:test
suite/before/after) and a serveDirectory() helper (minimal http.createServer
that serves a built dist/ or out/ directory so the browser can fetch ES
modules via dynamic import())
- test:browser / test:browser:coverage scripts added to transport, vfs,
tsconnect following the same c8 + lcov pattern as test:coverage
- turbo.json: test:browser and test:browser:coverage tasks depend on build +
^build (dist/ must exist before the browser can import from it)
- .gitea/workflows/test-browser.yml: CI pipeline that installs browsers with
--with-deps then runs npm run test:browser
Integration tests:
- DataChannelTransport: real RTCPeerConnection loopback (both peers in one
page context), exercises send/receive and close propagation
- FsaVFS: OPFS round-trip (writeFile/readFile), stat, readdir, delete
- IndexedDBState: multi-instance persistence (write via instance 1, open
fresh instance 2 and verify IDB round-trip), empty-DB initialisation
- FsaFileOps: write/read/stat/remove cycle and rename/listFiles over OPFS
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Same filter approach as test: scoped to transport/http/websocket to avoid
pulling in tsconnect via example-app's ^build chain. typetest is http-only.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
turbo run test was building the full workspace graph (including tsconnect,
which requires the tailscale submodule) because example-app and test-app
transitively pull it in via ^build. Scope test and test:coverage to the
three packages that actually have test scripts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- turbo.json: add test and test:coverage pipeline tasks (dependsOn: ^build)
- root package.json: add test and test:coverage scripts via turbo
- Remove test:unit, test:coverage:unit, test:watch:unit from all packages
- Remove test-helpers/flags.ts from transport, http, websocket (and dirs)
- Remove fetch.node.test.ts (external test hitting google.com; always skipped)
- Drop { skip: skipIfNotIntegration } from all suites — tests run unconditionally
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces `npm run build --workspaces --if-present` with `turbo run build`,
which resolves build order from each package's package.json dependencies
and parallelises independent packages automatically.
- turbo.json: declares build task with `^build` topological dependency
- package.json: switches build script to turbo, adds packageManager field
(required by turbo v2), adds turbo to devDependencies
- .gitignore: add .turbo/ cache directory
- tailscale: bump submodule for tsconfig types-pinning fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Set up npm workspaces monorepo with the tailscale/tailscale repo as a
git submodule. Adds packages/tsconnect which builds the tsconnect wasm
via tailscale's Go toolchain and compiles a TypeScript wrapper that
initializes the wasm, captures newIPN from the global scope, removes it
to avoid pollution, and re-exports it with proper module-scoped types.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>