diff --git a/bin/tsconfig.json b/bin/tsconfig.json index afa29e712..715becb98 100644 --- a/bin/tsconfig.json +++ b/bin/tsconfig.json @@ -40,6 +40,11 @@ // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ "resolveJsonModule": true, /* Enable importing .json files. */ + "baseUrl": ".", + "paths": { + "ep_etherpad-lite/*.js": ["../src/*.ts"], + "ep_etherpad-lite/*": ["../src/*.ts", "../src/*"] + }, // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ diff --git a/src/node/db/API.ts b/src/node/db/API.ts index 0fa1e5fc1..ee08d79e1 100644 --- a/src/node/db/API.ts +++ b/src/node/db/API.ts @@ -24,6 +24,7 @@ import {deserializeOps} from '../../static/js/Changeset.js'; import ChatMessage from '../../static/js/ChatMessage.js'; import {Builder} from "../../static/js/Builder.js"; import {Attribute} from "../../static/js/types/Attribute.js"; +import AttributeMap from "../../static/js/AttributeMap.js"; import settings from '../utils/Settings.js'; import CustomError from '../utils/customError.js'; import * as padManager from './PadManager.js'; diff --git a/src/node/hooks/express/specialpages.ts b/src/node/hooks/express/specialpages.ts index bf6428f05..756005d7d 100644 --- a/src/node/hooks/express/specialpages.ts +++ b/src/node/hooks/express/specialpages.ts @@ -24,7 +24,7 @@ let ioI: { sockets: { sockets: any[]; }; } | null = null // Shared sanitizer for the `x-proxy-path` header. See the helper for the // allowed character class and the protocol-relative / traversal rejection // rules. Reused by admin.ts so both call sites share one definition. -import {sanitizeProxyPath} from '../../utils/sanitizeProxyPath'; +import {sanitizeProxyPath} from '../../utils/sanitizeProxyPath.js'; export const socketio = (hookName: string, {io}: any) => { diff --git a/src/node/hooks/express/updateStatus.ts b/src/node/hooks/express/updateStatus.ts index 72903fea2..5d29c8b43 100644 --- a/src/node/hooks/express/updateStatus.ts +++ b/src/node/hooks/express/updateStatus.ts @@ -49,7 +49,7 @@ export const resolveRequestAuthor = async (req: any): Promise => const cookiePrefix = (settings as any).cookie?.prefix ?? ''; const token = req?.cookies?.[`${cookiePrefix}token`]; if (typeof token !== 'string' || token === '') return null; - const authorManagerMod: any = await import('../../db/AuthorManager'); + const authorManagerMod: any = await import('../../db/AuthorManager.js'); const authorManager = authorManagerMod.default ?? authorManagerMod; if (typeof authorManager.getAuthorId !== 'function') return null; const authorId = await authorManager.getAuthorId(token, req?.session?.user); @@ -91,7 +91,7 @@ const computeOutdated = async ( if (!isMinorOrMoreBehind(current, state.latest.version)) return EMPTY; if (!padId || !authorId) return EMPTY; // padManager is loaded via dynamic import to avoid circular-init w/ updater. - const padManagerMod: any = await import('../../db/PadManager'); + const padManagerMod: any = await import('../../db/PadManager.js'); const padManager = padManagerMod.default ?? padManagerMod; if (typeof padManager.isValidPadId !== 'function' || !padManager.isValidPadId(padId)) return EMPTY; if (!(await padManager.doesPadExist(padId))) return EMPTY; diff --git a/src/node/utils/sanitizeProxyPath.ts b/src/node/utils/sanitizeProxyPath.ts index e11958f50..74c850cbc 100644 --- a/src/node/utils/sanitizeProxyPath.ts +++ b/src/node/utils/sanitizeProxyPath.ts @@ -1,4 +1,4 @@ -import settings from './Settings'; +import settings from './Settings.js'; /** * Sanitize the URL-path prefix Etherpad is being served under. diff --git a/src/tests/backend-new/specs/hooks/express/firstAuthorOf.test.ts b/src/tests/backend-new/specs/hooks/express/firstAuthorOf.test.ts index 58b23cb21..415c42779 100644 --- a/src/tests/backend-new/specs/hooks/express/firstAuthorOf.test.ts +++ b/src/tests/backend-new/specs/hooks/express/firstAuthorOf.test.ts @@ -1,5 +1,5 @@ import {describe, expect, it} from 'vitest'; -import {firstAuthorOf} from '../../../../../node/hooks/express/updateStatus'; +import {firstAuthorOf} from '../../../../../node/hooks/express/updateStatus.js'; const makePad = (entries: Record): any => ({ pool: {numToAttrib: entries}, diff --git a/src/tests/backend-new/specs/hooks/express/updateStatus.test.ts b/src/tests/backend-new/specs/hooks/express/updateStatus.test.ts index 05b6f423e..3e3de272f 100644 --- a/src/tests/backend-new/specs/hooks/express/updateStatus.test.ts +++ b/src/tests/backend-new/specs/hooks/express/updateStatus.test.ts @@ -17,10 +17,10 @@ import {describe, it, expect, vi, beforeAll, beforeEach, afterEach} from 'vitest import express from 'express'; import supertest from 'supertest'; import type {Express} from 'express'; -import type {UpdateState} from '../../../../../node/updater/types'; -import {EMPTY_STATE} from '../../../../../node/updater/types'; -import {getEpVersion} from '../../../../../node/utils/Settings'; -import {parseSemver} from '../../../../../node/updater/versionCompare'; +import type {UpdateState} from '../../../../../node/updater/types.js'; +import {EMPTY_STATE} from '../../../../../node/updater/types.js'; +import {getEpVersion} from '../../../../../node/utils/Settings.js'; +import {parseSemver} from '../../../../../node/updater/versionCompare.js'; // --------------------------------------------------------------------------- // Module mocks — must appear before any import that transitively imports them. @@ -67,13 +67,13 @@ vi.mock('../../../../../node/db/PadManager', () => { // Import the SUT *after* vi.mock declarations so the mocks take effect. // --------------------------------------------------------------------------- -import * as stateModule from '../../../../../node/updater/state'; -import * as authorManagerModule from '../../../../../node/db/AuthorManager'; +import * as stateModule from '../../../../../node/updater/state.js'; +import * as authorManagerModule from '../../../../../node/db/AuthorManager.js'; import { expressCreateServer, _resetBadgeCacheForTests, _setBadgeCacheCapForTests, -} from '../../../../../node/hooks/express/updateStatus'; +} from '../../../../../node/hooks/express/updateStatus.js'; // --------------------------------------------------------------------------- // Helpers @@ -181,7 +181,7 @@ afterEach(() => { const getPadMap = async (): Promise> => { // Dynamic import returns the mock factory's return value. - const mod: any = await import('../../../../../node/db/PadManager'); + const mod: any = await import('../../../../../node/db/PadManager.js'); return mod.__pads__ as Map; }; diff --git a/src/tests/backend-new/specs/sanitizeProxyPath.test.ts b/src/tests/backend-new/specs/sanitizeProxyPath.test.ts index 377c74725..ee2173471 100644 --- a/src/tests/backend-new/specs/sanitizeProxyPath.test.ts +++ b/src/tests/backend-new/specs/sanitizeProxyPath.test.ts @@ -9,7 +9,7 @@ * - rejects path-traversal segments. */ import {describe, it, expect} from 'vitest'; -import {sanitizeProxyPath} from '../../../node/utils/sanitizeProxyPath'; +import {sanitizeProxyPath} from '../../../node/utils/sanitizeProxyPath.js'; const mockReq = (val: string|undefined) => ({ header: (name: string) => name.toLowerCase() === 'x-proxy-path' ? val : undefined, @@ -27,7 +27,7 @@ describe('sanitizeProxyPath', () => { it('returns "" when the req object has no header()', () => { expect(sanitizeProxyPath(undefined)).toBe(''); - // @ts-expect-error — exercising the defensive branch + // @ts-expect-error — exercising the defensive branch with an incompatible type expect(sanitizeProxyPath({})).toBe(''); }); }); @@ -189,7 +189,7 @@ describe('sanitizeProxyPath', () => { }); it('defaults trustProxy from settings when opts not provided', async () => { - const settings = (await import('../../../node/utils/Settings')).default; + const settings = (await import('../../../node/utils/Settings.js')).default; const original = settings.trustProxy; try { settings.trustProxy = true; diff --git a/src/tests/backend-new/specs/updater/MaintenanceWindow.test.ts b/src/tests/backend-new/specs/updater/MaintenanceWindow.test.ts index 1151d20e3..46291852a 100644 --- a/src/tests/backend-new/specs/updater/MaintenanceWindow.test.ts +++ b/src/tests/backend-new/specs/updater/MaintenanceWindow.test.ts @@ -3,7 +3,7 @@ import { parseWindow, inWindow, nextWindowStart, -} from '../../../../node/updater/MaintenanceWindow'; +} from '../../../../node/updater/MaintenanceWindow.js'; describe('parseWindow', () => { it('accepts a valid same-day window with tz=local', () => { diff --git a/src/tests/backend-new/specs/updater/smtpTransportKey.test.ts b/src/tests/backend-new/specs/updater/smtpTransportKey.test.ts index cd8dad804..8944da458 100644 --- a/src/tests/backend-new/specs/updater/smtpTransportKey.test.ts +++ b/src/tests/backend-new/specs/updater/smtpTransportKey.test.ts @@ -1,5 +1,5 @@ import {describe, it, expect} from 'vitest'; -import {smtpTransportKey} from '../../../../node/updater/index'; +import {smtpTransportKey} from '../../../../node/updater/index.js'; describe('smtpTransportKey', () => { // Regression for Qodo PR #7753 review: the nodemailer transport cache was diff --git a/src/tests/backend/specs/admin/adminSettingsRedact.ts b/src/tests/backend/specs/admin/adminSettingsRedact.ts index a4149fd62..8e55390ed 100644 --- a/src/tests/backend/specs/admin/adminSettingsRedact.ts +++ b/src/tests/backend/specs/admin/adminSettingsRedact.ts @@ -1,7 +1,7 @@ 'use strict'; import {strict as assert} from 'assert'; -import {redactSettings} from '../../../../node/utils/AdminSettingsRedact'; +import {redactSettings} from '../../../../node/utils/AdminSettingsRedact.js'; describe('AdminSettingsRedact', function () { it('returns a deep clone, never mutates input', function () { diff --git a/src/tests/backend/specs/api/jwtAdminClaim.ts b/src/tests/backend/specs/api/jwtAdminClaim.ts index c6a0be85f..46d5c23bb 100644 --- a/src/tests/backend/specs/api/jwtAdminClaim.ts +++ b/src/tests/backend/specs/api/jwtAdminClaim.ts @@ -10,7 +10,7 @@ */ const common = require('../../common'); -import settings from '../../../../node/utils/Settings'; +import settings from '../../../../node/utils/Settings.js'; let agent: any; diff --git a/src/tests/backend/specs/padInsertAuthorInvariant.ts b/src/tests/backend/specs/padInsertAuthorInvariant.ts index ec7d6296b..fe8145cdf 100644 --- a/src/tests/backend/specs/padInsertAuthorInvariant.ts +++ b/src/tests/backend/specs/padInsertAuthorInvariant.ts @@ -8,7 +8,7 @@ * plugin paths that call appendRevision directly). */ -import {PadType} from '../../../node/types/PadType'; +import {PadType} from '../../../node/types/PadType.js'; import {strict as assert} from 'assert'; const common = require('../common'); diff --git a/src/tests/backend/specs/pwaManifest.ts b/src/tests/backend/specs/pwaManifest.ts index 8cabceaf8..f9d31ce82 100644 --- a/src/tests/backend/specs/pwaManifest.ts +++ b/src/tests/backend/specs/pwaManifest.ts @@ -12,7 +12,7 @@ */ const common = require('../common'); -import settings from '../../../node/utils/Settings'; +import settings from '../../../node/utils/Settings.js'; let agent: any; diff --git a/src/tests/backend/specs/tokenTransfer.ts b/src/tests/backend/specs/tokenTransfer.ts index a7f0c4358..f78611248 100644 --- a/src/tests/backend/specs/tokenTransfer.ts +++ b/src/tests/backend/specs/tokenTransfer.ts @@ -6,7 +6,7 @@ */ const common = require('../common'); -import settings from '../../../node/utils/Settings'; +import settings from '../../../node/utils/Settings.js'; const db = require('../../../node/db/DB'); diff --git a/src/tests/backend/specs/updater-window-integration.ts b/src/tests/backend/specs/updater-window-integration.ts index a93875f8d..619bfb454 100644 --- a/src/tests/backend/specs/updater-window-integration.ts +++ b/src/tests/backend/specs/updater-window-integration.ts @@ -4,9 +4,9 @@ import path from 'node:path'; import fs from 'node:fs/promises'; import os from 'node:os'; import {strict as assert} from 'assert'; -import {EMPTY_STATE, MaintenanceWindow, PolicyResult, ReleaseInfo} from '../../../node/updater/types'; -import {loadState, saveState} from '../../../node/updater/state'; -import {decideSchedule, decideTriggerApply} from '../../../node/updater/Scheduler'; +import {EMPTY_STATE, MaintenanceWindow, PolicyResult, ReleaseInfo} from '../../../node/updater/types.js'; +import {loadState, saveState} from '../../../node/updater/state.js'; +import {decideSchedule, decideTriggerApply} from '../../../node/updater/Scheduler.js'; const release: ReleaseInfo = { tag: 'v9.9.9', @@ -24,7 +24,6 @@ const policyAutonomous: PolicyResult = { const window: MaintenanceWindow = {start: '03:00', end: '05:00', tz: 'utc'}; describe('Tier 4 scheduler — maintenance-window boundary integration', function () { - this.timeout(15000); let root: string; let stateFile: string; diff --git a/src/tests/backend/specs/urlBasePath.ts b/src/tests/backend/specs/urlBasePath.ts index e34bd5dc6..16ebf854f 100644 --- a/src/tests/backend/specs/urlBasePath.ts +++ b/src/tests/backend/specs/urlBasePath.ts @@ -17,7 +17,7 @@ */ const common = require('../common'); -import settings from 'ep_etherpad-lite/node/utils/Settings'; +import settings from 'ep_etherpad-lite/node/utils/Settings.js'; let agent: any; diff --git a/src/tests/container/specs/api/adminSettings_7819.ts b/src/tests/container/specs/api/adminSettings_7819.ts index 5c58e7551..f6f4b0bbb 100644 --- a/src/tests/container/specs/api/adminSettings_7819.ts +++ b/src/tests/container/specs/api/adminSettings_7819.ts @@ -85,7 +85,6 @@ const save = (socket: any, payload: string): Promise<{status: string; detail?: a }); describe('admin /settings socket (Docker container) — #7819', function () { - this.timeout(20000); let socket: any; before(async function () { diff --git a/src/tsconfig.json b/src/tsconfig.json index d35848d52..cb19c284e 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -15,7 +15,13 @@ /* Completeness */ "skipLibCheck": true /* Skip type checking all .d.ts files. */, "resolveJsonModule": true, - "types": ["node", "jquery", "vitest/globals"] + "types": ["node", "jquery", "vitest/globals"], + "ignoreDeprecations": "6.0", + "baseUrl": ".", + "paths": { + "ep_etherpad-lite/*.js": ["./*.ts"], + "ep_etherpad-lite/*": ["./*.ts", "./*"] + } }, "include": ["./**/*.ts"], "exclude": ["plugin_packages", "node_modules", "../plugin_packages"] diff --git a/src/vitest.config.ts b/src/vitest.config.ts index 9a43722c9..74779798c 100644 --- a/src/vitest.config.ts +++ b/src/vitest.config.ts @@ -1,6 +1,17 @@ import {defineConfig} from 'vitest/config'; +import {fileURLToPath} from 'node:url'; + +const srcRoot = fileURLToPath(new URL('.', import.meta.url)); export default defineConfig({ + resolve: { + alias: [ + // Self-imports: route ep_etherpad-lite/(.js)? → src/.ts so we + // exercise the actual sources, not the dist/ twins. Plugins (outside src/) + // still hit the package.json exports map at runtime. + { find: /^ep_etherpad-lite\/(.+?)(?:\.js)?$/, replacement: `${srcRoot}$1.ts` }, + ], + }, test: { globals: true, setupFiles: ['./tests/backend/vitest.setup.ts'],