* [monorepo] reduce writes from chrome in cypress
Chrome puts its shared memory in /dev/shm, and docker's default 64MB is far
too small for it - which is why Cypress passes --disable-dev-shm-usage,
spilling those segments to files on the container's writable layer instead.
Under one spec that is a few hundred MB of write-then-delete churn; under the
CI fan-out it was most of the disk bandwidth on the whole box.
So every service built from dockerfiles/cypress gets shm_size 1gb and every
cypress config drops the flag again: the SaaS e2e service, web's CI and local
component-test services plus its standalone cypress one, and server-ce's e2e
and native cypress services. Both e2e-open variants inherit the setting
through `extends`; the native one is a service of its own, and would be left
running Chrome against 64MB once the flag is gone.
* [monorepo] adopt cypress-pnp-reporter
Materializing the reporter packages copied 267 packages / 3779 files
(59MB) into /tmp on every container start, once per spec. Resolving them
through PnP inside Cypress's Electron process instead drops that to
zero: the entrypoint shell went from 55.6MB of write_bytes to 0.
* [web] serve built assets from a prepared buffer between builds
Serving a chunk cost ~2ms whatever its size, and an editor page pulls ~144
of them, so webpack-dev-server burned 2-3 cores through an e2e run purely
answering requests for output that had not changed since the last build.
It is not bytes: cutting the bundle 2.6x with a cheaper devtool moved CPU
by 8%, and an empty 304 cost nearly as much as the full body. It is the
per-request work - reading the asset out of the in-memory filesystem and
re-hashing the whole body for a strong ETag, on every request.
Three things a build does that this has to respect, each found by tracing
the hooks rather than by reading them:
- `done` fires twice per build, for the compilation and for the manifest,
both with the same hash. A flag would have been cleared by the second
while the first was still running, so the gate is a counter.
- An incremental `done` reports only the assets it rewrote - 16, not 630 -
so asset names accumulate. Resetting them on `invalid` left the
manifest's two behind and nothing was served from here again.
- webpack-dev-middleware holds requests during a rebuild. This sits in
front of it, so it steps aside for that window and drops the cache when
a rebuild starts. Whether a response can be kept is then decided as it
ends rather than as it began: an epoch bumped on `invalid` names the
build its bytes were read from, and only a response from the current
build with no rebuild in flight is stored. A rebuild starting
mid-response can no longer write the previous build's bytes in behind
the cache being dropped, which eviction by asset name could not catch
anyway - an incremental build does not name the assets it left alone.
Measured on the editor entrypoint, 16 concurrent clients:
0.26-0.31s -> 0.045-0.050s of webpack CPU per page load, and 0.0455s
immediately after a rebuild. Same chunk graph, byte-identical output.
GitOrigin-RevId: 08b4ab2d665414069bc01b4763dd64faa9b4e5cb
* [docstore] use more realistic test stubs for ranges
* [monorepo] define ranges schemas in @overleaf/ranges-tracker
* [ranges-tracker] add legacy orderedRejections field in tracked-changes
* [monorepo] resolve @overleaf/ranges-tracker subpath imports in typescript
* [real-time] validate comment thread ids and tracked change id seeds
* [ranges-tracker] require ObjectId ids and tracked change metadata in ranges
* [monorepo] require ObjectId tracked change ids on the accept routes
* [web] use ObjectId comment thread ids in the DocumentController tests
GitOrigin-RevId: 58b20dec311d85b917ef63f9efbe745a265a2de6
* [security] bump cypress 15.12.0 -> 15.20.1 to drop unpatched extract-zip
extract-zip <=2.0.1 has no patched release (GHSA-jmr9-qjv8-65gv). It was a
transitive dependency of cypress, used only to unpack Cypress's own binary.
Cypress 15.20.1 replaces extract-zip with yauzl directly, so upgrading the
parent removes the vulnerable package entirely.
Also bumps the pinned cypress/included Docker base image used by
test_frontend_ct / server-ce e2e / saas-e2e.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* [security] fix cy.get<T> alias typing broken by cypress 15.20.1
Cypress 15.20.1 adds a dedicated Chainable.get overload for alias strings
(get<S>(alias: `@${string}`): Chainable<S>), which now takes priority over
the generic selector overload. cy.get<HTMLInputElement>('@input') therefore
resolved to Chainable<HTMLInputElement> instead of
Chainable<JQuery<HTMLInputElement>>, so .then(element => element.get(0))
no longer type-checked. Pass the JQuery-wrapped type explicitly to match
the new overload.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
GitOrigin-RevId: f4a56a2d5948b5aaf00a07ecb384fc75abf6de53
* [web] Remove SubscriptionFormatters, format subscription dates in the frontend
Replace the server-formatted nextPaymentDueAt/nextPaymentDueDate/
trialEndsAtFormatted fields with the raw periodEnd date and format in
the React dashboard (same strings as before, English + UTC). Also
consolidate hand-rolled trial checks onto isInFreeTrial /
SubscriptionHelper.isInTrial.
* [web] Remove the now-unused dateformat dependency
* [web] Address review: payment-dates unit test, UTC dates in acceptance test
Add a dedicated unit test asserting the exact formatted strings from
formatPaymentDate/formatPaymentDateTime, and use explicit UTC timestamps
in the SubscriptionDashboard acceptance fixtures instead of
local-timezone Date constructors.
GitOrigin-RevId: 16ac4f1098b14ef9f1478d22cf39c1357ead7dea
* Replace the bibtex entries table with a card layout
Render each reference as a card (citation key, title, author, year and an
error indicator) instead of a table row. Drop @tanstack/react-table, which was
the last consumer of the dependency, and keep @tanstack/react-virtual for
virtualization.
- Add a compact card variant (via a `compact` prop) for the bibtex editor:
tighter spacing, no border-radius, and cards separated by a single collapsing
border rather than a gap.
- Add hover and green selected/selected+hover states.
- Move the select-all checkbox out of the scrolling list into the editor
toolbar so it stays fixed while the list scrolls.
- Lay out title/author/year with nested flex so author and year wrap together.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* In progress dark mode for review
* Slight design changes & updated dark mode
* Small tweaks to dark mode secondary text, and light mode border
* Truncate long authors, also use .summarize() on authors
* Refactor comments: reduce verbose, remove unnecessary
* Fix empty line
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Tom Wells <wellsthomas@hotmail.co.uk>
GitOrigin-RevId: feaa6718484eefb9e9efb44b99f456910697cb17
GHSA-72gw-mp4g-v24j / CVE-2026-5079 — DoS via deeply nested field names,
patched in 2.2.0. multer is a direct dep of web and clsi (file uploads).
- bump multer to 2.2.0 in services/web and services/clsi
- re-target the Overleaf local patch (attach files with no filename as
'undefined' instead of skipping) from 2.1.1 to 2.2.0, kept as a scoped
root resolution to match the repo's patch convention
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GitOrigin-RevId: 0d10137b521ebaacee71fc41adba32c4530a4041
* Bump yauzl to ^3.4.0 in history-v1 and web for Node 24.17
yauzl 3.x hangs extracting a large zip entry under Node 24.17 due to
stream backpressure changes. Fixes zip/project uploads, open-in-overleaf
template tests, and the SaaS-E2E "templates" test.
Also fix the open-in-overleaf acceptance test mock to serve the "too large"
fixture with a single res.end (Content-Length) instead of a backpressure-
ignoring res.write loop, which Node 24.17 surfaces as ECONNRESET (404)
instead of the expected 422.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Use plainTextResponse for large-file test fixture
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
GitOrigin-RevId: 55627b7c2c91959f0d1a7f3419467455edb06c9f
* Remove double spaces
* Enforce curly apostrophes
* Enforce French typographic spaces in fr.json
Add vitest test enforcing French spacing rules per
https://typographisme.net/post/Les-espaces-typographiques-et-le-web:
- NNBSP (U+202F) before ? ! ;
- NBSP (U+00A0) before :
- NBSP (U+00A0) around guillemets « »
Fix 38 fr.json values that were using NBSP (or no space) before ? ! ;.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Extend French typography test to numbers and percent signs
Add rules per https://typographisme.net/post/Les-espaces-typographiques-et-le-web:
- NNBSP (U+202F) between digit groups (thousands separator)
- NBSP (U+00A0) between a number and a following word/unit
- NBSP (U+00A0) before %
Fix 9 fr.json values where a regular space was used instead of NBSP between
a number and a following word (e.g. "1 résultat", "10 jetons", "7 jours").
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Use point médian for inclusive writing in fr.json
Replace parenthetical gender/plural markers like "sûr(e)", "abonné(e)",
"licence(s)" with the point médian "·" (e.g. "sûr·e", "abonné·e",
"licence·s") per modern French écriture inclusive.
Add a vitest rule rejecting "word(suffix)" patterns where suffix is 1-3
letters and there is no space before the opening parenthesis.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Simplify inclusive-writing check to "(e)" substring match
The previous regex (matchAll without the /g flag) would have thrown at
runtime. Drop matchAll entirely and use includes('(e)').
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Add point médian to participles in fr.json
Apply inclusive writing (·e) to past participles agreeing with the
addressed user ("affilié·e", "invité·e", "connecté·e", "abonné·e", etc.).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Use inclusive forms in fr.json
* Change "de Overleaf" to "d’Overleaf"
* Fix tests
* Check for consecutive spaces, not just double spaces
* Update comment about guillemets
* Add ESLint rules for locale JSON conventions
Add three rules to @overleaf/eslint-plugin that run against locale JSON
files via @eslint/json:
- no-consecutive-spaces-in-locales
- no-straight-apostrophes-in-locales
- french-typography-in-locales
Wire them up in services/web/eslint.config.mjs against `locales/*.json`
(and `locales/fr.json` for the French-typography rule).
* Add `@eslint/json` so JSON files can be parsed
* Scope JS/TS-specific config to JS/TS files only so that built-in rules aren't applied to JSON files
* Disable `no-irregular-whitespace` in french-typography-in-locales.js
* Make the rules fixable
* Improve regexes: user `\s` instead of custom matchers
* Add sorted-keys-in-locales ESLint rule
Mirrors scripts/translations/sort.js as a fixable ESLint rule:
require top-level keys in locale JSON files to be sorted alphabetically.
The autofix re-serializes the file with sorted keys.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Add no-unused-locale-keys ESLint rule
Mirrors scripts/translations/cleanupUnusedLocales.js as a fixable ESLint
rule applied only to en.json. Shells out to `find` + `cat` once per
process (cached) to grep all candidate source files for key references,
then reports any en.json key that isn't found. The autofix re-serializes
en.json without the unused keys.
Hardcoded ALWAYS_USED set covers keys built at runtime (month names,
third-party-datastore notifications) that the regex scan can't see.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Add locale-variables-match-en ESLint rule
Mirrors scripts/translations/checkVariables.js as an ESLint rule applied
to non-en locale JSON files. Reads en.json (cached) and reports any
member whose `__name__` placeholders differ from the base.
GLOBALS (e.g. __appName__) are allowed as extras; IGNORE_NESTING_FOR
preserves the script's per-key exceptions.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Replace some double linebreak (@overleaf/no-consecutive-spaces-in-locales)
* Add no-orphan-locale-keys ESLint rule
Detect translation keys in non-en locale files that aren't present in
en.json. Mirrors the `--sync-non-en` flag of cleanupUnusedLocales.js.
The autofix re-serializes the locale file without the orphan keys,
sorted alphabetically.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Add tests for the locale JSON ESLint rules
Cover the four single-file rules:
- no-consecutive-spaces-in-locales
- no-straight-apostrophes-in-locales
- sorted-keys-in-locales
- french-typography-in-locales (including the inclusive-writing
suggestion for "(e)")
The three cross-file rules (no-unused-locale-keys, no-orphan-locale-keys,
locale-variables-match-en) read from disk and aren't covered here; they
need integration tests via fixtures.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Drop bin/lint_locales — checks are now covered by ESLint rules
The four checks in bin/lint_locales are duplicated by the new
@overleaf/eslint-plugin rules:
- sort.js --check → sorted-keys-in-locales
- cleanupUnusedLocales.js --check → no-unused-locale-keys
+ no-orphan-locale-keys
- checkVariables.js → locale-variables-match-en
- single-quote grep → no-straight-apostrophes-in-locales
`yarn lint` runs the ESLint rules against locales/*.json, so the
lint_locales script and Makefile target are redundant.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Remove Locales.test.mjs
* Delete scripts replaced by @overleaf/eslint-plugin rules
Remove the three locale scripts now superseded by ESLint rules:
- scripts/translations/sort.js → sorted-keys-in-locales
- scripts/translations/cleanupUnusedLocales.js → no-unused-locale-keys
+ no-orphan-locale-keys
- scripts/translations/checkVariables.js → locale-variables-match-en
Also drop the matching Makefile targets (sort_locales,
cleanup_unused_locales), update AGENTS.md / copilot-instructions.md to
point at `yarn lint --fix` instead, and prune the deleted entries from
the esm-check-migration allowlist.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Delete redundant scripts/translations/sort-locale-file.js
Single-file sort utility — superseded by `yarn lint --fix path/to/file.json`
via the sorted-keys-in-locales ESLint rule. No callers in repo.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Update comments and messages
* Add test file pattern to ESLint configuration
* Reorder "Backend specific rules" so `no-extraneous-dependencies` doesn't fail on test files
* Make french-typography fixes idempotent and catch edge cases
Apply Copilot's review feedback on PR #34221:
- autoFix() now uses `\\s*`/`\\s+` instead of `\\s?`/`\\s` so it
collapses any run of mixed whitespace into the correct NBSP/NNBSP,
making the fix idempotent and order-independent.
- The `:` lint regex now captures optional whitespace and reports
when it isn't NBSP (catches `"Liste :"` with a regular space).
- The `%` lint regex now matches the missing-space case (`"10%"`)
via `\\s*` instead of requiring one char between digit and `%`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* Remove sort-locale-file.js from pre-commit
* Revert no-unused-locale-keys ESLint rule; Restore lint_locales scripts
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
GitOrigin-RevId: 8351fab074e6e7f77920b70237df1cd1c781e053
* upgrade from eslint version 8 to eslint version 10
* remove unsupported eslint-env directive
* include jsx files in latexqc linting
* use basePath and extends to maintain paths in writefull eslint
* fix yarn.lock
with ./bin/yarn install
* preserve existing glob patterns in web eslint config
* restore original comments
* fix worker path
* corrected comment about eslint-plugin-mocha
* remove unused imports
* remove unused import of includeIgnoreFile
* switch to individual eslit.config.mjs files
* fix lint errors on eslint.config.mjs in web
* update build scripts for eslint.config.mjs
* update volumes for RUN_LINTING_CI_MONOREPO in web Makefile
updated manually as this makefile is not autogenerated
the RUN_LINTING_CI_MONOREPO command is only used for prettier, not eslint, but updating for consistency.
* migrate from mocha/no-skipped-tests to mocha/no-pending-tests
see https://github.com/lo1tuma/eslint-plugin-mocha/pull/365
"rule no-skipped-tests has been removed, its functionality has been merged into the existing no-pending-tests rule"
GitOrigin-RevId: 2c8f25c8049a0dba374a51df1214286bb5093a51
Migrates the Overleaf monorepo package manager from npm (v11) to Yarn 4 (v4.9.1) using node-modules linker mode.
GitOrigin-RevId: 50d32ab01955c15e29679eff9e9e9cfb897fab2d