Skip to content

[pull] main from tldraw:main#455

Merged
pull[bot] merged 10 commits intocode:mainfrom
tldraw:main
Mar 20, 2026
Merged

[pull] main from tldraw:main#455
pull[bot] merged 10 commits intocode:mainfrom
tldraw:main

Conversation

@pull
Copy link
Copy Markdown

@pull pull Bot commented Mar 20, 2026

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

MitjaBezensek and others added 10 commits March 20, 2026 10:33
… route limit (#8291)

Staging deploys to Vercel fail with:

```
Error: Builder returned invalid routes: should NOT be longer than 4096 characters
```

The `build.ts` script uses `regexgen` to create a trie-regex enumerating
every asset filename for the immutable cache-control route. The Vite
build now produces ~157 files, pushing this regex well over Vercel's
4096-char-per-route limit.

**When it started failing:** `feat(mermaid): add @tldraw/mermaid
package` (#8194) — merged Mar 18. Last successful deploy was #8273.

**Root cause:** `regexgen` builds a trie-regex from all asset filenames.
The exact-match approach was introduced in #4863 to prevent Cloudflare
from caching 404 responses for `/assets/*` with immutable headers, which
caused "Failed to fetch dynamically imported module" errors after
deploys.

**Fix:** Replace the per-file trie regex with an extension-based
pattern. Given 3 example assets:

```
assets/admin-D_Yk9J1y.js
assets/styles-BKwyi6c.css
assets/Inter-Medium-abc123.woff2
```

Old regex (regexgen):
`^\/assets\/(?:admin\-D_Yk9J1y\.js|styles\-BKwyi6c\.css|Inter\-Medium\-abc123\.woff2)$`
New regex: `^\/assets\/.+\.(js|css|woff2)$`

| Request | Old regex | New regex |
|---|---|---|
| `/assets/admin-D_Yk9J1y.js` | Match (immutable cache) | Match
(immutable cache) |
| `/assets/styles-BKwyi6c.css` | Match (immutable cache) | Match
(immutable cache) |
| `/assets/nonexistent.js` | No match (no cache headers) | Match
(immutable cache on 404) |
| `/assets/foo` | No match | No match |

### Possible cons

- **Slightly broader matching** — a request like
`/assets/nonexistent.js` would get immutable headers on its 404
response. This could cause Cloudflare to cache the 404 at the edge,
which was the original concern in #4863.
- **Not an exact match** — we lose the guarantee that only known files
get immutable caching.

### Why the cons don't matter in practice

1. Asset URLs come from compiled JS bundles referencing real
content-hashed filenames — no user or browser crafts random
`/assets/*.js` URLs
2. `coalesceWithPreviousAssets()` copies 30 days of old assets into
every deploy, so old URLs from stale tabs don't 404
3. The old exact-match regex only enumerated the *current* build's
assets — it never protected against old assets from previous builds
returning 404 either. For stale tabs >30 days old, both approaches fail
identically.
4. Even if Cloudflare caches a 404 with immutable headers for a
fabricated URL, it only affects that specific fake URL — real asset URLs
with content hashes are unaffected

Also removes `regexgen` and `@types/regexgen` dependencies.

Closes #8286

### Change type

- [x] `bugfix`

### Test plan

1. Deploy to Vercel staging and verify the deploy succeeds
2. Verify assets are served with `Cache-Control: public,
max-age=31536000, immutable`
3. Verify non-existent asset paths without valid extensions don't get
cache headers

### Release notes

- Fix Vercel deploy failure caused by asset cache route regex exceeding
4096-char limit

### Code changes

| Section        | LOC change |
| -------------- | ---------- |
| Apps           | +10 / -5   |
| Config/tooling | +33 / -39  |
In order to get improved auth token refresh (updateAuth over existing
WebSocket instead of full reconnect), this PR upgrades @rocicorp/zero
from 0.25.9 to 0.26.1.

Zero 0.26 breaking changes (neither affects us):
- Custom headers to query/mutate endpoints now need allowlisting — we
don't send any
- WebSocket messages capped at 10MB — our mutations are nowhere near
that

The token refresh in `TldrawApp.ts` now benefits from Zero sending an
`updateAuth` message without dropping the connection, fixing the
reconnect every ~50s.

### Change type

- [x] `improvement`

### Test plan

1. Open tldraw.com, sign in
2. Verify Zero sync works (create/edit documents)
3. Wait >60s, confirm no connection drops in devtools network tab

### Release notes

- Upgrade Zero to 0.26.1 for smoother auth token refresh (no more
periodic reconnects)

### Code changes

| Section        | LOC change   |
| -------------- | ------------ |
| Apps           | +2 / -3     |
| Config/tooling | +309 / -21  |
In order to make local commits less painful when only a subset of
packages change, this PR narrows what the Husky pre-commit hook runs:
`yarn install` only when a `package.json` is staged, `build-api` only
for packages with staged changes (via lazy filters), `build-i18n` only
when `apps/dotcom/client` files are staged, and `refresh-assets` can be
skipped when invoked from that filtered `build-api` path if nothing
relevant to assets changed. A few verbose `typecheck` script logs were
also trimmed.

Personally I often ended up committing with \`--no-verify\` because the
hook could take long enough that skipping checks felt worth the risk. On
this machine a typical commit now finishes in about **~7s** instead of
sometimes **~25s**.

### Change type

- [x] `other`

### Test plan

1. Stage a change under a single package (e.g. `packages/editor`) and
commit; confirm only that package’s `build-api` runs (and API reports
re-staged as needed).
2. Stage a change with no `packages/*` API impact; confirm `build-api`
is skipped when appropriate.
3. Stage only non-dotcom files; confirm `build-i18n` does not run.
4. Stage a `package.json` change; confirm `yarn install --immutable`
runs.

- [ ] Unit tests
- [ ] End to end tests

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
In order to stay current with the Vite ecosystem and benefit from the
new OXC transpiler and Rolldown bundler, this PR upgrades Vite from v7
to v8 across all apps and templates.

See the [Vite 8 announcement](https://vite.dev/blog/announcing-vite-8)
for full details on what changed.

### Performance

| App | Before | After |
| --- | ------ | ----- |
| examples build | 18s | 2s |
| dotcom build | 19s | 3s |

### Migration changes

- **`esbuild` → `oxc`**: Vite 8 replaces esbuild with OXC for
transpilation
- **`rollupOptions` → `rolldownOptions`**: Vite 8 uses Rolldown instead
of Rollup
- **Removed `esbuildOptions`** from `optimizeDeps` (no longer
applicable)
- **Dotcom: `plugin-react-swc` → `plugin-react`** (v6, Oxc-based in Vite
8) and `@swc/plugin-formatjs` → `@formatjs/unplugin` (framework-agnostic
unplugin)
- **`experimentalDecorators: true`** added to tsconfig base — Oxc does
not yet support lowering native Stage 3 decorators, so we use legacy
TypeScript decorators which Oxc handles natively

### Known issues

Several plugins don't yet declare Vite 8 peer dependency support:

- `@cloudflare/vite-plugin` (`^6.1.0 || ^7.0.0`)
- `@vitejs/plugin-react` (`^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0`)
- `@vitejs/plugin-vue` (`^5.0.0 || ^6.0.1`)

These work at runtime but produce yarn peer dependency warnings.

### Change type

- [x] `other`

### Test plan

1. Run `yarn dev` and verify the examples app loads
2. Run `yarn dev-app` and verify dotcom loads
3. Run `yarn build` and verify all packages build
4. Run tests across workspaces
5. Verify Cloudflare workers still deploy (vite-plugin peer dep)

- [x] Unit tests
- [x] End to end tests

### Code changes

| Section | LOC change |
| ------- | ---------- |
| Core code | +2 / -2 |
| Apps | +44 / -52 |
| Templates | +52 / -89 |
| Config/tooling | +1334 / -1053 |

### Release notes

- Upgrade Vite from v7 to v8
)

#8258 migrated from prettier/eslint to oxfmt/oxlint but missed updating
`internal/scripts/lib/file.ts`, which still imported `prettier`. Since
prettier was removed from devDependencies, `yarn workspaces focus` on
Vercel (for analytics deploy) [crashes during
postinstall](https://vercel.com/tldraw/analytics/6pQnhddPB1iQUS31h8NyWTBhHS2f)
when `refresh-assets` tries to import prettier.

This PR replaces the prettier import with oxfmt's `format()` function
and uses `JSON.stringify` for JSON formatting (oxfmt is JS/TS focused).

Relates to #8258

### Change type

- [x] `bugfix`

### Test plan

1. `yarn refresh-assets` succeeds
2. `yarn typecheck` passes
3. Clone repo to `/tmp`, run `yarn workspaces focus @tldraw/analytics
@tldraw/monorepo config` — postinstall completes without "Cannot find
module 'prettier'" error

### Code changes

| Section        | LOC change |
| -------------- | ---------- |
| Config/tooling | +8 / -16   |
| Automated files | +36 / -36 |
This workflow is no longer used, so this PR removes it.

### Change type

- [x] `other`

### Test plan

- [x] N/A — just deleting an unused workflow file
Adds in support for mind maps as a new diagram type in mermaid.

<img width="456" height="301" alt="Screenshot 2026-03-19 at 15 10 12"
src="https://github.com/user-attachments/assets/e5cfe2b7-5174-4275-a5df-501883f3b278"
/>
<img width="1173" height="413" alt="Screenshot 2026-03-19 at 15 10 20"
src="https://github.com/user-attachments/assets/21e17731-dabb-4230-a8b3-bba12eb28ee9"
/>

### Change type

- [ ] `bugfix`
- [ ] `improvement`
- [x] `feature`
- [ ] `api`
- [ ] `other`

### Test plan

1. Run the Mermaid diagrams example and compare the 20 mind map shapes
vs the generated SVGs
2. Enable copy/paste handling for Mermaid code and try pasting in:
```
mindmap
  root((My Project))
    Planning
      Goals
      Timeline
      Budget
    Development
      Frontend
        UI Design
        Components
      Backend
        API
        Database
    Marketing
      Social Media
      SEO
      Ads
    Launch
      Testing
      Deployment
      Feedback
```      


### Release notes

- Adds mind map shape support to the @tldraw/mermaid package

---------

Co-authored-by: Guillaume <guillaume@tldraw.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Guillaume Richard <mynameiskaneel@proton.me>
When two pushes to `main` happen in quick succession, the
`publish-editor-extensions` workflow fails with "already exists". The
concurrency group serializes job execution, but the marketplace API has
propagation delay — both runs fetch the same stale version and compute
the same next version.

This PR wraps the version-bump → package → publish cycle in a retry
loop. If `vsce publish` fails with "already exists", the script waits
60s, re-fetches the marketplace version, re-computes the next version,
re-packages, and retries (up to 5 attempts).

Also moves the `vsce show` marketplace fetch (previously a separate
workflow step) into the script so it can be re-run on retry.

### Change type

- [x] `bugfix`

### Test plan

1. Review the retry logic in `publish-editor-extensions.ts`
2. Verify the "already exists" error string matches what vsce actually
outputs (confirmed from CI logs: `::error::tldraw-org.tldraw-vscode
v2.216.1 already exists.`)

- [ ] Unit tests
- [ ] End to end tests

### Code changes

| Section        | LOC change |
| -------------- | ---------- |
| Config/tooling | +52 / -27  |
…rs (#8280)

Sentry shows multiple transient Cloudflare R2 errors (`internal error
(10001)`, `connectivity issue`, `network connection lost`, `unspecified
error (0)`) and service binding errors (`proxy request failed`) in the
tldrawusercontent worker. Found these when investigating user reports of
image uploads not working for them.

This PR wraps R2 `head`, `put`, and `get` calls in `retry()` (3
attempts, 500ms wait) with an error matcher for known transient
patterns. The upload body is buffered to `ArrayBuffer` before retries
since `ReadableStream` is single-use. Service binding calls
(`validateUpload`, `confirmUpload`) get the same treatment.

### Change type

- [x] `bugfix`

### Test plan

- Cannot be manually tested (transient infra errors)

### Release notes

- Fix transient R2 upload/download failures by adding retry logic

### Code changes

| Section    | LOC change |
| ---------- | ---------- |
| Core code  | +30 / -7   |
| Apps       | +14 / -4   |
i couldn't, ahem, depend on what dependabot was doing in
#8299
it was doing things like updating Next to the next major version for no
apparent reason.

this recreates it cleanly.
(drive-by fix for a posthog link in perf tests)

### Change type

- [ ] `bugfix`
- [ ] `improvement`
- [ ] `feature`
- [ ] `api`
- [x] `other`
@pull pull Bot locked and limited conversation to collaborators Mar 20, 2026
@pull pull Bot added the ⤵️ pull label Mar 20, 2026
@pull pull Bot merged commit 7b2687a into code:main Mar 20, 2026
@pull pull Bot had a problem deploying to deploy-staging March 20, 2026 15:13 Failure
@pull pull Bot had a problem deploying to deploy-production March 20, 2026 15:13 Failure
@pull pull Bot had a problem deploying to deploy-staging March 20, 2026 15:13 Error
@pull pull Bot had a problem deploying to vsce publish March 20, 2026 15:13 Failure
@pull pull Bot had a problem deploying to deploy-staging March 20, 2026 15:13 Failure
@pull pull Bot had a problem deploying to deploy-staging March 21, 2026 00:29 Failure
@pull pull Bot temporarily deployed to e2e-dotcom March 21, 2026 02:35 Inactive
@pull pull Bot had a problem deploying to deploy-staging March 22, 2026 00:32 Failure
@pull pull Bot temporarily deployed to e2e-dotcom March 22, 2026 02:36 Inactive
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants