## 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
@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:
- Project-level auth config from Webstudio project settings
- 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",
},
});