ep_markdown 12.0.7 → 12.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -29,7 +29,7 @@ jobs:
29
29
  - uses: actions/setup-node@v6
30
30
  name: Install Node.js
31
31
  with:
32
- node-version: 22
32
+ node-version: 25
33
33
  - uses: pnpm/action-setup@v6
34
34
  name: Install pnpm
35
35
  with:
@@ -19,7 +19,7 @@ jobs:
19
19
  - uses: actions/setup-node@v6
20
20
  name: Install Node.js
21
21
  with:
22
- node-version: 22
22
+ node-version: 25
23
23
  - uses: pnpm/action-setup@v6
24
24
  name: Install pnpm
25
25
  with:
@@ -23,7 +23,7 @@ jobs:
23
23
  # OIDC trusted publishing needs npm >= 11.5.1, which requires
24
24
  # Node >= 20.17.0. setup-node's `20` resolves to the latest
25
25
  # 20.x, which satisfies that.
26
- node-version: 20
26
+ node-version: 25
27
27
  registry-url: https://registry.npmjs.org/
28
28
  - name: Upgrade npm to >=11.5.1 (required for trusted publishing)
29
29
  run: npm install -g npm@latest
package/exportMarkdown.ts CHANGED
@@ -6,6 +6,10 @@ import {StringIterator} from 'ep_etherpad-lite/static/js/StringIterator'
6
6
  import {StringAssembler} from 'ep_etherpad-lite/static/js/StringAssembler'
7
7
 
8
8
  const padManager = require('ep_etherpad-lite/node/db/PadManager');
9
+ // ReadOnlyManager uses `export default {...}` (ESM-style), so when loaded
10
+ // via CommonJS `require` the API lives under `.default`. Unwrap explicitly
11
+ // so `readOnlyManager.isReadOnlyId` resolves.
12
+ const readOnlyManager = require('ep_etherpad-lite/node/db/ReadOnlyManager').default;
9
13
 
10
14
  const getMarkdownFromAtext = (pad, atext) => {
11
15
  const apool = pad.apool();
@@ -344,8 +348,22 @@ const getPadMarkdown = async (pad, revNum) => {
344
348
  return getMarkdownFromAtext(pad, atext);
345
349
  };
346
350
 
351
+ // Readonly pad IDs (`r.*`) must be resolved to their underlying pad ID
352
+ // before loading — otherwise padManager.getPad creates an empty pad under
353
+ // the readonly ID and the export returns nothing. Matches the behavior of
354
+ // Etherpad core's /export/:type handler (see importexport.ts).
355
+ const resolvePadId = async (padId) => {
356
+ if (readOnlyManager.isReadOnlyId(padId)) {
357
+ return await readOnlyManager.getPadId(padId);
358
+ }
359
+ return padId;
360
+ };
361
+
347
362
  exports.getPadMarkdownDocument =
348
- async (padId, revNum) => await getPadMarkdown(await padManager.getPad(padId), revNum);
363
+ async (padId, revNum) => {
364
+ const resolvedId = await resolvePadId(padId);
365
+ return await getPadMarkdown(await padManager.getPad(resolvedId), revNum);
366
+ };
349
367
 
350
368
  // copied from ACE
351
369
  const _REGEX_WORDCHAR = new RegExp([
package/express.ts CHANGED
@@ -1,11 +1,36 @@
1
1
  'use strict';
2
2
 
3
3
  const exportMarkdown = require('./exportMarkdown');
4
+ const settings = require('ep_etherpad-lite/node/utils/Settings');
5
+ const rateLimit = require('express-rate-limit');
6
+
7
+ // Mirrors the rate limiting, CORS header, and readonly-pad handling that
8
+ // Etherpad core applies to its native /p/:pad/export/:type routes
9
+ // (src/node/hooks/express/importexport.ts). Without these, integrators hit
10
+ // browser CORS errors on repeated fetches (issue #139) and the readonly
11
+ // `r.*` pad IDs fail because markdown's export path did not resolve them
12
+ // to their underlying pad IDs.
4
13
 
5
14
  exports.expressCreateServer = (hookName, {app}) => {
15
+ const limiter = rateLimit({
16
+ ...settings.importExportRateLimiting,
17
+ handler: (request: any) => {
18
+ if (request.rateLimit.current === request.rateLimit.limit + 1) {
19
+ console.warn('Import/Export rate limiter triggered on ' +
20
+ `"${request.originalUrl}" for IP address ${request.ip}`);
21
+ }
22
+ },
23
+ });
24
+
25
+ // Apply the core rate limiter to both with-revision and without-revision
26
+ // markdown export endpoints, matching the pattern used by Etherpad core.
27
+ app.use('/p/:padId/export/markdown', limiter);
28
+ app.use('/p/:padId/:revNum/export/markdown', limiter);
29
+
6
30
  app.get('/p/:padId/export/markdown', async (req: any, res: any, next: any) => {
7
31
  try {
8
32
  const {padId} = req.params;
33
+ res.header('Access-Control-Allow-Origin', '*');
9
34
  res.attachment(`${padId}.md`);
10
35
  res.contentType('plain/text');
11
36
  res.send(await exportMarkdown.getPadMarkdownDocument(padId));
@@ -17,6 +42,7 @@ exports.expressCreateServer = (hookName, {app}) => {
17
42
  app.get('/p/:padId/:revNum/export/markdown', async (req: any, res: any, next: any) => {
18
43
  try {
19
44
  const {padId, revNum} = req.params;
45
+ res.header('Access-Control-Allow-Origin', '*');
20
46
  res.attachment(`${padId}.md`);
21
47
  res.contentType('plain/text');
22
48
  res.send(await exportMarkdown.getPadMarkdownDocument(padId, revNum));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ep_markdown",
3
3
  "description": "Edit and Export as Markdown in Etherpad",
4
- "version": "12.0.7",
4
+ "version": "12.0.9",
5
5
  "author": {
6
6
  "name": "John McLear",
7
7
  "email": "john@mclear.co.uk",
@@ -35,6 +35,9 @@
35
35
  "engines": {
36
36
  "node": ">=18.0.0"
37
37
  },
38
+ "peerDependencies": {
39
+ "express-rate-limit": "^8.0.0 || ^7.0.0"
40
+ },
38
41
  "peerDependenciesMeta": {
39
42
  "ep_headings2": {
40
43
  "optional": true