Commit graph

9914 commits

Author SHA1 Message Date
SamTV12345
e2a0402eef fix(test): add jszip as direct devDependency
export.ts uses await import('jszip'). Previously jszip was a
transitive of html-to-docx and not directly resolvable from src/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 15:06:02 +02:00
SamTV12345
e7bd464ff0 fix(plugin-compat): register .ts CJS extension handler for ep_markdown
ep_markdown and similar plugins ship TypeScript source files and load
them via CJS require('./exportMarkdown') without an extension. Node's
CJS resolver does not recognise .ts by default. Register a Module._extensions
handler at plugins.ts load time that uses esbuild's synchronous transformSync
to compile .ts → CJS on demand. This shim is guard-checked so tsx/vite-node
environments (which already handle .ts) are not affected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:00:43 +02:00
SamTV12345
11a38f66a9 fix(plugin-compat): trailing-slash eejs bridge and ueberdb2 ESM-only exports
Fix A: plugins calling require('ep_etherpad-lite/node/eejs/') with a
trailing slash need a real file at the wildcard-expanded path
(dist-cjs/node/eejs/.cjs, dist/node/eejs/.mjs). A postbuild script
creates these bridge files after tsdown finishes; the build and pretest
scripts now run it automatically.

Fix B: node/db/*.ts files transitively import ueberdb2 which is ESM-only
(no "require" export condition). Exclude these files from the CJS build
entry set and add specific ./node/db/* exports entries with only the
"import" condition, so CJS plugins get a clean ERR_PACKAGE_PATH_NOT_EXPORTED
instead of the confusing ueberdb2 "no exports main" error. Update
exports_map.ts to verify the new ESM-only behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:00:35 +02:00
SamTV12345
f47e971198 chore: fixed backend tests 2026-05-25 14:40:08 +02:00
SamTV12345
1f297bf6ca fix: convert remaining require() calls in prod source to ESM
Six require() calls were missed in the earlier ESM migration: three
top-level requires for native bindings (pdfkit, mammoth) and a transitive
CJS bridge for jszip, plus four lazy/optional requires (Cleanup,
PadManager — circular; html-to-docx, ExportPdfNative — optional).

Top-level pdfkit and mammoth go to static ESM import (both have
esModuleInterop-compatible export= types). JSZip remains on a
createRequire bridge because it is a transitive dependency not symlinked
into node_modules for ESM resolution — a minimal inline type replaces the
implicit any.

Lazy CJS bridge requires become dynamic await import() with .js
extensions, which lets Node's ESM loader resolve the modules correctly.
The enclosing functions (compactPad, anonymizeAuthor, doExport) are
already async. html-to-docx has no published types; a declare module
shim is added to globals.d.ts.

Fixes Cannot-find-module crashes for Cleanup and PadManager in the
compactPad and anonymizeAuthor test suites. All 40 tests in those two
files now pass.
2026-05-25 14:32:02 +02:00
SamTV12345
afc1007503 test: convert remaining backend integration/unit specs from CJS require to ESM
Migrates the remaining 19 test files in tests/backend/specs to static ESM
imports: anonymizeAuthor, authorTokenCookie, colorutils, compactPad,
openapi-admin, padDeletionManager, padInsertAuthorInvariant,
proxyPathRedirect, pwaManifest, sessionIdCookie, settingsModalHeading,
socialMeta-unit, socialMeta, timesliderRedirect, tokenTransfer,
updateActions, updateStatus, updater-integration, and urlBasePath.

Key decisions:
- colorutils cast to `any` (source is @ts-nocheck with untyped empty object)
- compactPad/sessionIdCookie use default assert import for bare assert() calls
- plugin_defs uses default import (module uses export default)
- updateStatus/updateActions use top-level import for updateStatus module
  and access _resetBadgeCacheForTests via the imported namespace

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 14:14:55 +02:00
SamTV12345
6b084baeac test: convert api backend specs and run_cmd spec from CJS require to ESM
Migrates api/anonymizeAuthor, api/deletePad, api/jwtAdminClaim, and
run_cmd to static ESM imports. Also widens RunCMDOptions.stdio type to
accept string | (string | null | Function)[], matching its actual runtime
usage (opts.stdio = 'string' shorthand was previously untyped).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 14:14:45 +02:00
SamTV12345
cca2355813 test: convert admin backend specs from CJS require to ESM imports
Migrates adminSettingsResolved, adminSettingsSave, anonymizeAuthorSocket,
authorSearch, and padLoadFilter to static ESM imports, removing top-level
require() calls so the files work under vite-node/vitest. Uses named {io}
import from socket.io-client (matching common.ts pattern) to avoid the
default-import callable issue.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 14:14:38 +02:00
SamTV12345
d682d8001a test: split vitest into unit + integration projects
tests/backend-new/specs/** are mock-heavy unit tests that need
per-file isolation (vi.mock must apply before the SUT is loaded,
and a shared module graph defeats it). tests/backend/specs/**
share rustydb and need the old isolate:false sequential model.

Split via test.projects (vitest 4): unit gets isolate:true +
parallelism; integration keeps the existing serial config.
Different sequence.groupOrder values (1 vs 2) are required by
vitest when projects have different maxWorkers settings.
Fixes 8 mock-related test failures in updateStatus, firstAuthorOf,
and updateCheck-optout that came in from the develop merge.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:39:00 +02:00
SamTV12345
ca4b015fcf fix(vitest): alias ep_etherpad-lite/* to source to avoid double-loading
When internal code imported via the exports map (ep_etherpad-lite/node/x)
AND via a relative path (../../node/x), vite-node resolved two distinct
module instances. Prom-client top-level Counter() calls ran twice and
threw "metric already registered", cascading ~35 test failures. Fix adds a
resolve.alias in vitest.config.ts that rewrites ep_etherpad-lite/<subpath>
to the .ts source, so the two spellings collapse to one module instance.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:23:34 +02:00
SamTV12345
ac98496be3 docs(plugins): document the dual ep_etherpad-lite import surface
CJS plugins keep working unchanged via the require condition; ESM
plugins are an opt-in track using extension-explicit imports.
Documents two known limitations: trailing-slash node/eejs/ and CJS
require() of db modules (ueberdb2 is ESM-only).
2026-05-25 12:52:02 +02:00
SamTV12345
7d18f89ec6 ci: build ep_etherpad-lite before resolving plugins
The 'with plugins' jobs install ep_markdown / ep_readonly_guest / etc.
which require ep_etherpad-lite at install-time. The dist + dist-cjs
twins must exist before pnpm resolves those subpath imports.

Also run check:exports as a fast canary before plugin install.
2026-05-25 12:51:13 +02:00
SamTV12345
dce5292be8 build: wire tsdown into dev entry point
predev builds once before the dev server starts; dev:watch keeps
tsdown running alongside the server. The pretest hook (added in
the tsdown setup commit) auto-builds before vitest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 12:49:58 +02:00
SamTV12345
396f27dbcb feat(pluginfw): probe .cjs and .mjs when loading hook modules
Plugins that ship CJS-only entries (e.g. ep_readonly_guest's
ep_readonly_guest.cjs) and ESM-only entries previously hit the loader's
extensionless fallback path and failed because only .ts and .js were
tried. Add .cjs and .mjs to the candidate list.
2026-05-25 12:47:43 +02:00
SamTV12345
37f484f375 build: add check:exports verifier; drop unbuildable . require entry
The verifier walks the exports map and asserts each target exists.
First run caught that the '.' entry's require condition pointed at
dist-cjs/node/server.cjs which is never built (server.ts has
top-level await, excluded from the CJS build). Drop the require
sub-condition and the now-dead 'main' field; plugins consume
subpaths, not the package root.
2026-05-25 12:46:37 +02:00
SamTV12345
9d4b3ba725 revert: drop ep_etherpad-lite/node/eejs/ trailing-slash hack
The previous attempt added stub .cjs files at dist-cjs/node/eejs/.cjs
to make the trailing-slash require() resolve. But the stubs were
empty — plugins would resolve and then immediately crash calling
methods on an empty module. Worse than failing fast.

Accept that 'require("ep_etherpad-lite/node/eejs/")' (trailing
slash) is not supported by the exports map. Affected plugins must
drop the trailing slash to migrate. The bare form
'require("ep_etherpad-lite/node/eejs")' works as before.
2026-05-25 12:43:21 +02:00
SamTV12345
f762cf5cd9 fix(pkg): restore trailing-slash ep_etherpad-lite/node/eejs/ resolution
Previous commit dropped the './node/eejs/' entry to silence DEP0155.
That warning applies only to folder mappings (string target ending in
/), not to exact-match keys with conditional object values. Real
plugins use the trailing-slash form (see PR #7605 CI logs).

Implementation notes:
- Re-adds './node/eejs/' exports key with object-condition value.
  Node 24/26 still fires DEP0155 for the trailing-slash specifier
  (the warning is tied to the caller's import path, not just the
  exports key format), but resolution succeeds.
- tsdown build:done hooks emit dist-cjs/node/eejs/.cjs and
  dist/node/eejs/.mjs stubs that Node's folder-pattern expansion
  resolves to when the import suffix is empty ('eejs/' + '').
- Adds explicit './node/eejs/index.js' and './node/eejs/index'
  entries so the ESM import('ep_etherpad-lite/node/eejs/index.js')
  is not hijacked by the trailing-slash folder-prefix match.
- Adds 'ep_etherpad-lite/node/eejs/' to cjsResolvableSubpaths in
  the exports_map spec.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 12:40:47 +02:00
SamTV12345
b874ff5e55 feat(pkg): add exports map for ep_etherpad-lite
Routes CJS plugins' require() calls to dist-cjs/*.cjs twins while
keeping ESM consumers on dist/*.mjs (tsdown emits .mjs for ESM). The
trailing-.js wildcard handles plugins that wrote require(...'.js')
with an explicit extension. tests/backend has only an import
condition because CJS build excludes it (top-level await).

Also fixes Settings.ts getEpVersion() to use a static JSON import
instead of a build-path-relative requireFromHere() call, which broke
when resolved from dist-cjs/node/utils/. Test file updated: split
cjsSubpaths into resolvable vs loadable sets since DB modules
transitively depend on ueberdb2 (ESM-only, no require condition).
2026-05-25 12:20:52 +02:00
SamTV12345
626097b235 test(exports): add failing resolution tests for ep_etherpad-lite subpaths
Exercises the require + import conditions for the subpaths plugins
consume. Will pass once src/package.json gets an exports map.
2026-05-25 12:16:07 +02:00
SamTV12345
2f07a47010 build: add tsdown dual-emit (ESM + CJS) for ep_etherpad-lite
Builds .ts sources to dist/*.mjs (ESM) and dist-cjs/*.cjs (CJS) so the
upcoming exports map can route plugins' require() calls to the CJS
twin while ESM consumers use the .mjs originals. No source code is
moved or rewritten. tsdown 0.22.0 emits .mjs for ESM regardless of
the outExtension callback; accept that convention.

The CJS entry set excludes node/server.ts (top-level await is not
valid in CJS) and tests/backend/** (common.ts transitively imports
server.ts). The ESM entry set includes both.
2026-05-25 12:12:14 +02:00
SamTV12345
934ec7b960 test(admin): pass hook timeout as before() arg, not vi.setConfig
vi.setConfig({hookTimeout: N}) inside a before() callback is a no-op —
vitest reads hook timeouts before the hook runs. padLoadFilter genuinely
needs 120s for its setup, so pass it as the second arg. The two 60s
files drop the override entirely (matches the global default already
set in vitest.config.ts).
2026-05-25 12:04:30 +02:00
SamTV12345
d8eeee8321 test: convert mocha this.timeout/this.skip to vitest in merged-from-develop admin specs
Three admin test files brought in by the develop merge (5afd466bb) still
used mocha-shape this.timeout/this.skip. Converts them to vi.setConfig
and ctx.skip per the pattern established in 95f753c80.
2026-05-25 12:01:37 +02:00
SamTV12345
5afd466bba Merge branch 'develop' into backend-esm-vitest
Conflicts resolved in 10 files. All updater/hook files kept develop's new
logic (LRUCache per-key badge caching, firstAuthorOf/resolveRequestAuthor,
maintenanceWindow tier-4 support, node-engine-mismatch preflight check,
smtpTransportKey/notifyApplyFailure, isMinorOrMoreBehind replacing
isMajorBehind/isVulnerable, removed vulnerableBelow machinery).

Import style: branch ESM shape preserved throughout — .js extensions kept
on all local imports, require() calls in develop converted to ESM import
statements (ImportEtherpad.ts, ImportHtml.ts, pad.ts). pad.ts imports
pad_outdated_notice.js (new) instead of pad_version_badge.js (removed).
All vitest test files taken from develop as-is (no mocha shape introduced).
2026-05-25 11:56:32 +02:00
SamTV12345
686b184ba3 fix(pad_editor): remove duplicate export of padeditor/focusOnLine
Both symbols are already exported at lines 300 and 348. The trailing
re-export at line 438 caused esbuild 'Multiple exports with the same
name' build errors in PR #7605 (Backend tests / Linux without plugins).
2026-05-25 11:34:39 +02:00
SamTV12345
b721e35e38 docs(plan): implementation plan for ESM/CJS plugin compat
Eleven bite-sized tasks covering prework (duplicate export fix + develop
merge), tsdown dual-emit build, exports map, resolution tests, plugin
loader .cjs probing, check:exports verifier, CI wiring, and plugin
import-surface docs. Follows the design in
docs/superpowers/specs/2026-05-25-esm-plugin-compat-design.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 11:30:18 +02:00
SamTV12345
c2ea60e0d5 docs(spec): ESM core / CJS plugin compatibility design
Captures the design for landing PR #7605 without breaking the existing
plugin ecosystem. Covers the three CI failure causes (duplicate export,
develop merge, plugin resolution) and concretes the dual-emit
ep_etherpad-lite package with an exports map, tsdown build, plugin
loader updates, and a fixture-based testing strategy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 11:24:53 +02:00
dependabot[bot]
53aa7a1c5a
build(deps): bump mssql from 12.5.3 to 12.5.4 (#7810)
Bumps [mssql](https://github.com/tediousjs/node-mssql) from 12.5.3 to 12.5.4.
- [Release notes](https://github.com/tediousjs/node-mssql/releases)
- [Changelog](https://github.com/tediousjs/node-mssql/blob/master/CHANGELOG.txt)
- [Commits](https://github.com/tediousjs/node-mssql/compare/v12.5.3...v12.5.4)

---
updated-dependencies:
- dependency-name: mssql
  dependency-version: 12.5.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-25 11:04:31 +02:00
John McLear
4ff3363b9b
docs(docker): document settings.json writable-layer + env-var-vs-file semantics (#7819) (#7827)
Two related operator-facing docs gaps, both surfaced by #7819:

1. settings.json on disk is a *template*; env-var substitution happens
   at load time in memory only. Operators repeatedly mistake the
   templated file for a stale config because the docs never spell out
   that the on-disk file is intentionally unchanged by env vars.

2. The default docker-compose.yml puts settings.json in the container's
   writable layer with no host mount, which means admin /settings edits
   are silently lost on `docker compose down && up`, `pull`, or
   watchtower — but preserved across plain `restart`. Operators don't
   reliably know which compose verbs recreate the container.

Adds two prose sections to doc/docker.md (explaining both gotchas, with
a recreate-vs-restart table) and a commented-out `./settings.json:…`
bind mount in both docker-compose.yml and the README compose example.
Bind mount is opt-in so existing setups behave identically.

No runtime change.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 10:39:25 +02:00
SamTV12345
689dd9d434 chore: fixed backend tests 2026-05-22 22:43:25 +02:00
Etherpad Release Bot
50cd1d16f4 Merge branch 'master' into develop 2026-05-22 17:44:57 +00:00
Etherpad Release Bot
0b392e13da bump version 2026-05-22 17:44:57 +00:00
Etherpad Release Bot
da00ad7057 Merge branch 'develop' 2026-05-22 17:44:57 +00:00
SamTV12345
6d641c5040 chore: added changelog for v3.2.0 2026-05-22 19:41:42 +02:00
dependabot[bot]
58e8978a2d
build(deps): bump semver from 7.8.0 to 7.8.1 (#7832)
Bumps [semver](https://github.com/npm/node-semver) from 7.8.0 to 7.8.1.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v7.8.0...v7.8.1)

---
updated-dependencies:
- dependency-name: semver
  dependency-version: 7.8.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-22 18:08:38 +02:00
dependabot[bot]
3c03794e77
build(deps-dev): bump the dev-dependencies group across 1 directory with 2 updates (#7829)
Bumps the dev-dependencies group with 2 updates in the / directory: [mocha](https://github.com/mochajs/mocha) and [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).


Updates `mocha` from 11.7.5 to 11.7.6
- [Release notes](https://github.com/mochajs/mocha/releases)
- [Changelog](https://github.com/mochajs/mocha/blob/v11.7.6/CHANGELOG.md)
- [Commits](https://github.com/mochajs/mocha/compare/v11.7.5...v11.7.6)

Updates `vite` from 8.0.13 to 8.0.14
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.14/packages/vite)

---
updated-dependencies:
- dependency-name: mocha
  dependency-version: 11.7.6
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: vite
  dependency-version: 8.0.14
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-22 18:08:20 +02:00
dependabot[bot]
4a89af62aa
build(deps): bump ueberdb2 from 6.0.3 to 6.1.2 (#7817)
Bumps [ueberdb2](https://github.com/ether/ueberDB) from 6.0.3 to 6.1.2.
- [Changelog](https://github.com/ether/ueberDB/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ether/ueberDB/compare/v6.0.3...v6.1.2)

---
updated-dependencies:
- dependency-name: ueberdb2
  dependency-version: 6.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-22 12:22:58 +01:00
dependabot[bot]
7844b216a3
build(deps): bump @tanstack/react-query from 5.100.10 to 5.100.11 (#7816)
Bumps [@tanstack/react-query](https://github.com/TanStack/query/tree/HEAD/packages/react-query) from 5.100.10 to 5.100.11.
- [Release notes](https://github.com/TanStack/query/releases)
- [Changelog](https://github.com/TanStack/query/blob/main/packages/react-query/CHANGELOG.md)
- [Commits](https://github.com/TanStack/query/commits/@tanstack/react-query@5.100.11/packages/react-query)

---
updated-dependencies:
- dependency-name: "@tanstack/react-query"
  dependency-version: 5.100.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-22 12:20:45 +01:00
dependabot[bot]
887d11cd57
build(deps): bump lru-cache from 11.3.6 to 11.5.0 (#7823)
Bumps [lru-cache](https://github.com/isaacs/node-lru-cache) from 11.3.6 to 11.5.0.
- [Changelog](https://github.com/isaacs/node-lru-cache/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-lru-cache/compare/v11.3.6...v11.5.0)

---
updated-dependencies:
- dependency-name: lru-cache
  dependency-version: 11.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-22 12:14:31 +01:00
dependabot[bot]
9ba21f1151
build(deps): bump @elastic/elasticsearch from 9.4.0 to 9.4.1 (#7828)
Bumps [@elastic/elasticsearch](https://github.com/elastic/elasticsearch-js) from 9.4.0 to 9.4.1.
- [Release notes](https://github.com/elastic/elasticsearch-js/releases)
- [Changelog](https://github.com/elastic/elasticsearch-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/elastic/elasticsearch-js/compare/v9.4.0...v9.4.1)

---
updated-dependencies:
- dependency-name: "@elastic/elasticsearch"
  dependency-version: 9.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-22 12:14:28 +01:00
John McLear
d0180033e4
fix: page sessionstorage cleanup to avoid OOM (#7830) (#7831)
* fix: page sessionstorage cleanup to avoid OOM (#7830)

SessionStore._cleanup() previously called `findKeys('sessionstorage:*',
null)`, materialising every session key into a single array. On decade-
old MariaDB installs with millions of sessions this OOMs the node
process within ~15 minutes — see #7830.

Switch to ueberdb2 6.1.0's findKeysPaged with a 500-key page size, and
yield to the event loop between pages so the DB driver can release each
page's buffered rows and request handlers can interleave.

The break is now driven by `page.length === 0` rather than `page.length
< CLEANUP_PAGE_SIZE` so a stubbed/throttled paged source still iterates
the full keyspace.

Adds a regression test that seeds 50 sessionstorage rows, monkey-patches
`DB.findKeysPaged` to use a 4-key page, runs cleanup, and asserts every
expired row is removed plus every valid row preserved across page
boundaries.

Closes #7830

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: address Qodo review on #7831

Four follow-ups raised by Qodo on the session cleanup paging fix:

- DB.ts: fail-fast at init() if any required wrapper method (incl.
  findKeysPaged) is missing, so a stale ueberdb2 pin surfaces at boot
  rather than crashing the first cleanup run an hour later.
- SessionStore: bound a single _cleanup() run to 10 minutes. Under
  sustained session creation the keyspace can grow faster than cleanup
  drains it; without a budget the next scheduled run would never fire.
  When the budget hits, log a warning and let the next run continue.
- SessionStore: log the defensive `page[0] <= after` cursor-stall break.
  Previously the loop exited silently, leaving expired rows behind with
  no operator-visible signal of the backend regression.
- Tests: the paged-cleanup regression test now removes both expiredSids
  AND validSids in finally, so a failed assertion doesn't leak rows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: note paged session cleanup in CHANGELOG + settings template

CHANGELOG.md picks up an entry under 3.1.0 Notable fixes describing the
OOM cause, the paged iteration, the 10-minute per-run budget, the
cursor-stall logging, and the fail-fast init guard.

settings.json.template's sessionCleanup comment adds the page-size,
budget, and pointer to #7830 so admins can reason about the new
behaviour from the template alone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: regenerate lockfile against ueberdb2 6.1.2

Now that ether/ueberDB#983 unblocked the publish workflow (OIDC trusted
publishing), ueberdb2 6.1.2 is live on npm and the `^6.1.0` pin in
src/package.json resolves cleanly. Resolves the ERR_PNPM_OUTDATED_LOCKFILE
that was blocking CI on this PR.

29 SessionStore backend tests still green against the published tarball.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 10:56:35 +01:00
translatewiki.net
0d40f2d049
Localisation updates from https://translatewiki.net. 2026-05-21 14:02:29 +02:00
dependabot[bot]
40eeb214d3
build(deps-dev): bump the dev-dependencies group across 1 directory with 8 updates (#7825)
Bumps the dev-dependencies group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) | `25.8.0` | `25.9.1` |
| [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest) | `4.1.6` | `4.1.7` |
| [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) | `19.2.14` | `19.2.15` |
| [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) | `8.59.3` | `8.59.4` |
| [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) | `8.59.3` | `8.59.4` |
| [react-hook-form](https://github.com/react-hook-form/react-hook-form) | `7.75.0` | `7.76.0` |
| [vite-plugin-babel](https://github.com/owlsdepartment/vite-plugin-babel) | `1.7.1` | `1.7.3` |
| [oxc-minify](https://github.com/oxc-project/oxc/tree/HEAD/napi/minify) | `0.131.0` | `0.132.0` |



Updates `@types/node` from 25.8.0 to 25.9.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

Updates `vitest` from 4.1.6 to 4.1.7
- [Release notes](https://github.com/vitest-dev/vitest/releases)
- [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.7/packages/vitest)

Updates `@types/react` from 19.2.14 to 19.2.15
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

Updates `@typescript-eslint/eslint-plugin` from 8.59.3 to 8.59.4
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.4/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.59.3 to 8.59.4
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.4/packages/parser)

Updates `react-hook-form` from 7.75.0 to 7.76.0
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](https://github.com/react-hook-form/react-hook-form/compare/v7.75.0...v7.76.0)

Updates `vite-plugin-babel` from 1.7.1 to 1.7.3
- [Commits](https://github.com/owlsdepartment/vite-plugin-babel/commits)

Updates `oxc-minify` from 0.131.0 to 0.132.0
- [Release notes](https://github.com/oxc-project/oxc/releases)
- [Changelog](https://github.com/oxc-project/oxc/blob/main/napi/minify/CHANGELOG.md)
- [Commits](https://github.com/oxc-project/oxc/commits/crates_v0.132.0/napi/minify)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 25.9.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: vitest
  dependency-version: 4.1.7
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: "@types/react"
  dependency-version: 19.2.15
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.59.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.59.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: react-hook-form
  dependency-version: 7.76.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
- dependency-name: vite-plugin-babel
  dependency-version: 1.7.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: dev-dependencies
- dependency-name: oxc-minify
  dependency-version: 0.132.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: dev-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-20 09:56:35 +01:00
dependabot[bot]
1d2374a9ba
build(deps): bump js-cookie from 3.0.6 to 3.0.7 (#7813)
Bumps [js-cookie](https://github.com/js-cookie/js-cookie) from 3.0.6 to 3.0.7.
- [Release notes](https://github.com/js-cookie/js-cookie/releases)
- [Commits](https://github.com/js-cookie/js-cookie/commits/v3.0.7)

---
updated-dependencies:
- dependency-name: js-cookie
  dependency-version: 3.0.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-20 09:17:56 +01:00
dependabot[bot]
8112b7a05c
build(deps): bump @tanstack/react-query-devtools (#7814)
Bumps [@tanstack/react-query-devtools](https://github.com/TanStack/query/tree/HEAD/packages/react-query-devtools) from 5.100.10 to 5.100.11.
- [Release notes](https://github.com/TanStack/query/releases)
- [Changelog](https://github.com/TanStack/query/blob/main/packages/react-query-devtools/CHANGELOG.md)
- [Commits](https://github.com/TanStack/query/commits/@tanstack/react-query-devtools@5.100.11/packages/react-query-devtools)

---
updated-dependencies:
- dependency-name: "@tanstack/react-query-devtools"
  dependency-version: 5.100.11
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-20 09:17:47 +01:00
dependabot[bot]
3c4edd1707
build(deps): bump pg from 8.20.0 to 8.21.0 (#7815)
Bumps [pg](https://github.com/brianc/node-postgres/tree/HEAD/packages/pg) from 8.20.0 to 8.21.0.
- [Changelog](https://github.com/brianc/node-postgres/blob/master/CHANGELOG.md)
- [Commits](https://github.com/brianc/node-postgres/commits/pg@8.21.0/packages/pg)

---
updated-dependencies:
- dependency-name: pg
  dependency-version: 8.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-20 09:17:44 +01:00
dependabot[bot]
7bb7d9731b
build(deps): bump openapi-backend from 5.16.1 to 5.17.0 (#7818)
Bumps [openapi-backend](https://github.com/openapistack/openapi-backend) from 5.16.1 to 5.17.0.
- [Release notes](https://github.com/openapistack/openapi-backend/releases)
- [Commits](https://github.com/openapistack/openapi-backend/compare/5.16.1...5.17.0)

---
updated-dependencies:
- dependency-name: openapi-backend
  dependency-version: 5.17.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-20 09:17:35 +01:00
dependabot[bot]
9bb39205e3
build(deps): bump tsx from 4.22.0 to 4.22.3 (#7822)
Bumps [tsx](https://github.com/privatenumber/tsx) from 4.22.0 to 4.22.3.
- [Release notes](https://github.com/privatenumber/tsx/releases)
- [Changelog](https://github.com/privatenumber/tsx/blob/master/release.config.cjs)
- [Commits](https://github.com/privatenumber/tsx/compare/v4.22.0...v4.22.3)

---
updated-dependencies:
- dependency-name: tsx
  dependency-version: 4.22.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-20 09:17:26 +01:00
John McLear
abda0485d3
test(docker): admin save persists across container restart (#7819) (#7821)
* test(docker): admin save persists across container restart (#7819)

The OP reports the symptom on the official Docker image specifically.
Adds two layers of coverage to docker.yml's build-test job, driven from
inside a container started against the same TEST_TAG the existing
test-container step uses:

1. New mocha spec adminSettings_7819.ts under tests/container/specs/api
   — authenticates against /admin, opens the /settings socket, saves an
   augmented JSON with an ep_oauth-shaped top-level block, and asserts
   the next load reply contains the marker. Intentionally leaves the
   marker on disk so the workflow can inspect it.

2. docker.yml now `docker exec test grep`s for the marker after
   test-container, then `docker restart`s the container, waits for the
   health probe, and re-greps. Both checks must pass — the first proves
   the socket-driven save actually touched the file inside the
   container layer; the second proves an in-place restart doesn't reset
   it. A recreate (docker rm + docker run) would wipe the file, but
   that's expected (image layer) and out of scope.

Container is started with `-e ADMIN_PASSWORD=changeme1` so the existing
settings.json.docker provisions the admin user; pad.js doesn't touch
/admin so the existing API specs are unaffected. test-container timeout
bumped 5s → 30s to cover socket connect + save round-trip, and the
mocha discovery extension list now includes `ts` so the new spec is
picked up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(docker): authenticate via /admin-auth/ POST, surface auth/load failures fast (#7819)

CI failed on #7821 with a generic 20s mocha timeout because the spec
hit GET /admin/ to grab a session cookie. webaccess.ts only treats
paths starting with /admin-auth as requireAdmin — and the container
runs with REQUIRE_AUTHENTICATION=false (default), so GET /admin/ never
issued a Basic challenge and Set-Cookie was empty. The socket then
connected unauthenticated, adminsettings.ts's connection handler
returned early without binding any listeners, and the load() promise
hung until mocha killed the test with no useful diagnostic.

Switch to POST /admin-auth/ (always-requireAdmin, regardless of
settings.requireAuthentication). Assert a 2xx with at least one
Set-Cookie before proceeding. Add an 8s timeout + meaningful error
message to load() so the "session was not admin" failure mode reports
immediately instead of burning the suite budget.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(docker): replace splice with hand-built payload (#7819)

Last CI failed because the splice-after-last-} approach landed a comma
between an existing trailing-comma-before-comment and the close brace
of settings.json.docker, producing `, /* … */, "ep_oauth"` — invalid
JSON.

settings.json.docker uses jsonc `/* */` and `//` comments and a
trailing-comma-before-comment-before-close shape that's annoying to
patch from the test side, and the existing isJSONClean has zero
backend coverage so the splice is going through Etherpad's lenient
write path anyway.

Switch to a hand-built minimal-but-viable settings document containing
the ep_oauth block. Three properties hold:
  - We're testing the WRITE path, not the synthesis path. Whatever
    bytes we send, the next `load` must return verbatim.
  - The post-save document must survive `docker restart` (the next
    step in docker.yml) — minimal-but-viable means port/users/dbType
    are present so Etherpad boots back up and HEALTHCHECK passes.
  - The next `load` reply must equal the bytes we saved
    (`reply.results === augmented`) — stronger than `.includes()`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 17:17:12 +01:00
John McLear
49111f2fc9
Enhance bug report template with abstraction question
Added a question about using abstraction like Docker in the bug report template.
2026-05-19 14:32:10 +01:00
John McLear
abff8124e8
test(admin): cover saveSettings round-trip + restart persistence (#7819) (#7820)
The admin saveSettings socket had zero direct backend coverage and the
e2e 'restart works' test only checked the page renders after restart —
neither catches a deployment that resets settings.json on restart, nor
the user-visible workflow that triggered #7819 (add a top-level plugin
block via Raw, save, watch it disappear).

Adds three backend specs for the saveSettings socket:
  - payload is written byte-for-byte to settings.settingsFilename
  - augmenting existing JSON with a new top-level block round-trips
    through the next load reply
  - /* */ comments survive the write path

Adds one e2e spec mirroring the #7819 workflow: open Raw, prepend an
ep_oauth-shaped top-level block, save, restartEtherpad(), re-login,
confirm the block is still in Raw and surfaces as its own Form-view
section ('Ep oauth' from humanize()).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 12:12:58 +01:00