Files
Oleg Isonen 42f5291168 fix: Reconcile MCP commits and Cloudflare scaffold types (#6131)
## Summary

- confirm ambiguous project commits by retrying the exact transaction ID
before treating a version conflict as a rejection
- share the confirmation path between normal mutations and restore-point
commits
- emit an explicit sitemap entry type so an empty sitemap remains
type-safe
- generate and publish TypeScript declarations for
`@webstudio-is/wsauth`
- regenerate affected CLI fixtures

## Root cause

The project API already treats the latest transaction ID as idempotent,
but ProjectSession interpreted the first `CONFLICT` response as a
rejected write. If the original transaction had committed, the
subsequent refresh exposed the requested values while the tool still
reported failure.

The Cloudflare scaffold had two independent type-contract gaps: an empty
generated sitemap inferred `never[]`, and the wsauth declaration build
inherited a shared output directory, leaving its published package
without the declaration files referenced by its exports.

## Technical choices

- Retry only a version conflict, once, with the same base version and
transaction ID. A confirmed retry returns the committed server version;
persistent conflicts continue through the existing refresh/retry-safe
flow.
- Use the same confirmation helper for regular patch and restore-point
transports.
- Keep the generated sitemap module standalone by emitting its concrete
entry type inline.
- Make the wsauth declaration config extend its package config and
publish nested `lib/types` output.
- Preserve existing API payloads and server idempotency behavior; no
migration is required.

## Todo

- [x] Reproduce and cover ambiguous committed mutations
- [x] Reuse conflict confirmation for restore-point commits
- [x] Cover empty sitemap typechecking
- [x] Generate and inspect the wsauth package declarations
- [x] Regenerate affected fixtures
- [x] Verify the Cloudflare fixture typecheck and production build
- [x] Review documentation impact — no user-facing documentation changes
are required

## Verification

- `pnpm --filter @webstudio-is/project-build test` — 2,092 tests passed
- `pnpm --filter @webstudio-is/project-build typecheck`
- `pnpm --filter webstudio test` — 972 tests passed
- `pnpm --filter webstudio typecheck`
- `pnpm --filter @webstudio-is/wsauth test`
- `pnpm --filter @webstudio-is/wsauth typecheck`
- `pnpm --filter @webstudio-is/wsauth build && pnpm --filter
@webstudio-is/wsauth dts`
- `npm pack --dry-run --json` in `packages/wsauth` — both public
declaration entrypoints included
- `pnpm -r fixtures:build`
- `pnpm --filter webstudio-cloudflare-template typecheck`
- `pnpm --filter webstudio-cloudflare-template build`
- focused formatting, lint, and `git diff --check`

Agent evaluations were not run because they require separate explicit
approval.

Closes #6129
Closes #6130
2026-08-13 23:07:18 +02:00
..
2026-05-27 18:42:08 +02:00

@webstudio-is/wsauth

wsauth is Webstudio's generic authentication package.

It owns shared authentication types, validators, route matching, build-time resource generation, and runtime enforcement helpers used by Builder-generated sites. The package is structured around auth methods so Webstudio can add methods beyond Basic Auth without changing the route auth model.

The first implemented method is HTTP Basic Auth.

Auth Config

Webstudio sync writes route authentication config at:

.webstudio/auth.json

Version 1 uses JSON with route strings as keys:

{
  "version": 1,
  "routes": {
    "/my/page": {
      "method": "basic",
      "login": "me",
      "password": "idiot"
    },
    "/bla/*": {
      "method": "basic",
      "login": "bla",
      "password": "blubb"
    }
  }
}

Routes use the same path pattern syntax Webstudio uses for pages:

  • Static segment: /about
  • Named parameter: /blog/:slug
  • Optional named parameter: /blog/:slug?
  • Wildcard: /docs/*
  • Named wildcard: /docs/:path*

Route rules:

  • A route must start with /.
  • A route must not contain repeating /.
  • A route must not end with /, except the root route /.
  • * and :name* wildcard segments must be the final segment.
  • Named parameters use :name, :name?, or :name*.
  • Parameter names must contain word characters only, matching \w+.

Basic Auth

Version 1 supports Basic Auth:

{
  "method": "basic",
  "login": "admin",
  "password": "secret"
}

Rules:

  • Login is required.
  • Password is required.
  • Login must not contain :.
  • Login must not contain whitespace.
  • Password must not contain whitespace.
  • Password may contain :.

The colon rule follows HTTP Basic Auth's user-id ":" password credential structure from RFC 7617.

Build Semantics

Build tools generate .webstudio/auth.json from Webstudio project data. The file is treated as build output and is overwritten on sync.

Generated auth sources are applied in this order:

  1. Project-level auth config from Webstudio project settings
  2. Page-level auth generated by Webstudio page settings

If both sources define the same route, the later source wins. Because routes are JSON object keys, the generated config file cannot represent duplicate routes.

Runtime

At runtime, protected requests without valid credentials receive:

new Response("Authentication required", {
  status: 401,
  headers: {
    "WWW-Authenticate": 'Basic realm="Webstudio"',
    "Cache-Control": "private, no-store",
  },
});