Skip to content

Path-based routing fork, packaged as @variablelab/portless - #1

Merged
rqbazan merged 6 commits into
mainfrom
feat/path-based-routing
Sep 9, 2026
Merged

rqbazan merged 6 commits into
mainfrom
feat/path-based-routing

Conversation

@rqbazan

@rqbazan rqbazan commented Sep 9, 2026

Copy link
Copy Markdown
Member

Summary

Integrates path-based routing (upstream vercel-labs#165) on top of upstream main (1ad573b, v0.15.6) and re-packages this fork as @variablelab/portless for npm.

  • feat: add --path flag for path-based routing: squash of the PR branch; the tree is identical to the PR head.
  • chore: rename package to @variablelab/portless: scoped name, version 0.0.1, public access, fork metadata. author stays "Vercel Labs" and the fork maintainer is listed under contributors. LICENSE and NOTICE ship in the tarball. The npx detection in cli.ts and the turbo task reference follow the rename.
  • Docs: fork banner, expanded path-based routing guide, docs/SYNCING.md, docs/RELEASING.md, CHANGELOG.fork.md, and a note in AGENTS.md.
  • CI: release.yml publishes @variablelab/portless with NPM_TOKEN and provenance, reading release notes from CHANGELOG.fork.md. ci.yml is unchanged from upstream.

Verification

  • format, lint, type-check, build: green
  • unit tests: 979 passed, 1 skipped (portless), 15 passed (docs)
  • e2e: 16 passed, 2 skipped (Python fixtures need a venv)
  • npm publish --dry-run: 9 files (dist, README, LICENSE, NOTICE, package.json), no warnings
  • Manual routing check against the built binary: longest-prefix selection, / boundary, PORTLESS_PATH, 404 page listing prefixes

Release note

Merging this PR publishes version 0.0.1: the release workflow pushes to npm and creates the v0.0.1 GitHub release.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KVTfcx388VGYh1ecW9b7BJ

rqbazan and others added 6 commits September 9, 2026 12:29
Squash of vercel-labs#165 (rqbazan:feat/path-based-routing at
7eeeb8c) rebased onto upstream main 1ad573b. The tree is identical to the
PR head; only the intermediate merge commits are collapsed.

Add `--path <prefix>` so multiple services can share one hostname and be
dispatched by URL path prefix using longest-prefix matching:

  portless myapp vite dev                    # serves /
  portless myapp --path /api pnpm start      # serves /api/*
  portless myapp --path /docs next dev       # serves /docs/*

The full request path is forwarded to the backend unchanged. A prefix
only matches at a `/` boundary, so `/api` never captures `/api-v2`.

- Add optional `pathPrefix` to RouteInfo; route identity is now
  (hostname, pathPrefix) in RouteStore add/remove/conflict detection and
  in the multi-TLD helpers (addRoutes/removeRoutes/formatUrls).
- Add `normalizePathPrefix()` (rejects `//`, `..`, and unsafe chars) and
  extend `formatUrl()` with an optional prefix.
- `findRoute()` matches hostname first, then the longest matching path
  prefix; routes without a prefix act as root catch-all. Tailscale tiers
  bypass path selection since a ts.net authority maps to one route.
- Add `--path` to `run` and named-app modes, `PORTLESS_PATH` env var, and
  the `path` field in portless.json (top-level and per-app) so monorepo
  apps can share a hostname.
- Update `list`, `get`, `alias`, the 404 page, README, SKILL.md, docs
  site pages, and CLI help text.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KVTfcx388VGYh1ecW9b7BJ
Publish this fork under the @VariableLab scope so it can coexist with the
upstream `portless` package on npm. The installed command is still
`portless`.

- package.json: scoped name, version 0.0.1, publishConfig.access public,
  repository/bugs/homepage pointing at variableland/portless, keywords.
  `author` stays "Vercel Labs"; the fork maintainer is listed under
  `contributors`.
- Ship LICENSE and NOTICE in the tarball (copied by prepublishOnly next
  to README.md) and ignore the copies in git. NOTICE credits the upstream
  project and describes what the fork changes.
- cli.ts: derive the local-install check and the install hints from a
  PACKAGE_NAME constant so `npx` detection looks for the scoped package.
- turbo.json: test:e2e depends on `@variablelab/portless#build`.
- README and SKILL.md: install commands use the scoped package.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KVTfcx388VGYh1ecW9b7BJ
Explain at the top of the README what this fork is, why it exists (upstream
PR vercel-labs#165 pending review), how to install it, and that the command name is
unchanged. Rewrite the path-based routing section to cover matching order,
the `/` boundary rule, longest-prefix selection, prefix normalization,
precedence between `--path`, `PORTLESS_PATH`, and config, the monorepo
`apps` form, `alias`/`get`/`list` support, and tunnel behavior. Examples
mirror the cases in proxy.test.ts and utils.test.ts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KVTfcx388VGYh1ecW9b7BJ
docs/SYNCING.md describes how to merge upstream main into the fork, where
conflicts are expected and how to resolve them, and what to do if upstream
merges PR vercel-labs#165. docs/RELEASING.md describes the version-driven release
flow, the NPM_TOKEN setup, and the trusted-publishing alternative. AGENTS.md
gets a short note pointing agents at both, since upstream's release rules
do not apply verbatim to the fork.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KVTfcx388VGYh1ecW9b7BJ
Compare the local version against @variablelab/portless on npm instead of
the upstream package, authenticate the publish step with the NPM_TOKEN
secret (provenance is kept), and take GitHub release notes from
CHANGELOG.fork.md so upstream's CHANGELOG.md can stay untouched across
syncs. Add the 0.0.1 entry. ci.yml is unchanged from upstream.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KVTfcx388VGYh1ecW9b7BJ
npm 11 normalizes `./dist/cli.js` to `dist/cli.js` at publish time and logs
a misleading "was invalid and removed" warning while doing so. Store the
normalized form so `npm publish` runs without the warning. No behavior
change: upstream's registry manifest already carries the normalized path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KVTfcx388VGYh1ecW9b7BJ
@rqbazan
rqbazan merged commit f3ad57c into main Sep 9, 2026
2 checks passed
@rqbazan
rqbazan deleted the feat/path-based-routing branch September 9, 2026 18:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant