diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9e21769..ce73262d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,24 @@ on: - "**" jobs: + common_checks: + name: Common checks + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20.8.0 + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + + - name: Install dependencies + run: npm ci + + - name: Check formatting + run: npm run format:check build_ilc: name: Build ILC runs-on: ubuntu-latest diff --git a/ilc/client/registry/errors.js b/ilc/client/registry/errors.js index ba37c0fa..aaa89459 100644 --- a/ilc/client/registry/errors.js +++ b/ilc/client/registry/errors.js @@ -1,4 +1,4 @@ -import extendError from '@namecheap/error-extender'; +import { extendError } from '../../common/utils'; const errors = {}; errors.RegistryError = extendError('RegistryError'); diff --git a/ilc/common/CacheWrapper.js b/ilc/common/CacheWrapper.js index de11eda1..be0d2eaa 100644 --- a/ilc/common/CacheWrapper.js +++ b/ilc/common/CacheWrapper.js @@ -1,5 +1,4 @@ -const extendError = require('@namecheap/error-extender'); -const { withTimeout } = require('./utils'); +const { withTimeout, extendError } = require('./utils'); const errors = {}; errors.CacheWrapperError = extendError('CacheWrapperError', { defaultData: {} }); diff --git a/ilc/common/guard/errors.js b/ilc/common/guard/errors.js index 02655415..f6c0ca73 100644 --- a/ilc/common/guard/errors.js +++ b/ilc/common/guard/errors.js @@ -1,4 +1,4 @@ -const extendError = require('@namecheap/error-extender'); +const { extendError } = require('../utils'); const errors = {}; diff --git a/ilc/common/router/errors.js b/ilc/common/router/errors.js index bcb9fa7c..b9bd5638 100644 --- a/ilc/common/router/errors.js +++ b/ilc/common/router/errors.js @@ -1,4 +1,4 @@ -const extendError = require('@namecheap/error-extender'); +const { extendError } = require('../utils'); const errors = {}; errors.RouterError = extendError('RouterError', { defaultData: {} }); diff --git a/ilc/common/utils.js b/ilc/common/utils.ts similarity index 52% rename from ilc/common/utils.js rename to ilc/common/utils.ts index bfd6753e..cd013591 100644 --- a/ilc/common/utils.js +++ b/ilc/common/utils.ts @@ -1,6 +1,11 @@ -const deepmerge = require('deepmerge'); +import errorExtender, { type ExtendConfig } from '@namecheap/error-extender'; +import deepmerge from 'deepmerge'; -function appIdToNameAndSlot(appId) { +type NameAndSlot = { + appName: string; + slotName: string; +}; +export function appIdToNameAndSlot(appId: string): NameAndSlot { const [appNameWithoutPrefix, slotName] = appId.split('__at__'); // Case for shared libraries @@ -17,24 +22,25 @@ function appIdToNameAndSlot(appId) { }; } -function makeAppId(appName, slotName) { +export function makeAppId(appName: string, slotName: string): string { return `${appName.replace('@portal/', '')}__at__${slotName}`; } -function cloneDeep(source) { - return deepmerge({}, source); +export function cloneDeep(source: T): T { + return deepmerge({}, source); } -const uniqueArray = (array) => [...new Set(array)]; +export const uniqueArray = (array: T[]): T[] => [...new Set(array)]; -const encodeHtmlEntities = (value) => value.replace(//g, '>').replace(/"/g, '"'); -const decodeHtmlEntities = (value) => +export const encodeHtmlEntities = (value: string): string => + value.replace(//g, '>').replace(/"/g, '"'); +export const decodeHtmlEntities = (value: string): string => value .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"'); -const removeQueryParams = (url) => { +export const removeQueryParams = (url: string): string => { const index = url.indexOf('?'); if (index !== -1) { return url.substring(0, index); @@ -43,7 +49,7 @@ const removeQueryParams = (url) => { } }; -const addTrailingSlash = (url) => { +export const addTrailingSlash = (url: string): string => { if (url.endsWith('/')) { return url; } @@ -51,7 +57,7 @@ const addTrailingSlash = (url) => { return `${url}/`; }; -function addTrailingSlashToPath(url) { +export function addTrailingSlashToPath(url: string): string { const isFullUrl = url.includes('://'); const parsedUrl = isFullUrl ? new URL(url) : new URL(`https://example.com/${url}`); const hasTrailingSlash = parsedUrl.pathname.endsWith('/'); @@ -60,32 +66,22 @@ function addTrailingSlashToPath(url) { return isFullUrl ? parsedUrl.toString() : parsedUrl.pathname.slice(1); } -class TimeoutError extends Error {} +export class TimeoutError extends Error {} /** * * @param {Promise} * @param {number} timeout */ -async function withTimeout(promise, ms, message = 'Promise timeout') { - let timeoutId; - const timeoutPromise = new Promise((resolve, reject) => { +export async function withTimeout(promise: Promise, ms: number, message = 'Promise timeout'): Promise { + let timeoutId: NodeJS.Timeout; + const timeoutPromise = new Promise((resolve, reject) => { timeoutId = setTimeout(() => reject(new TimeoutError(message)), ms); }); const decoratedPromise = promise.finally(() => clearTimeout(timeoutId)); return Promise.race([decoratedPromise, timeoutPromise]); } -module.exports = { - appIdToNameAndSlot, - makeAppId, - cloneDeep, - uniqueArray, - encodeHtmlEntities, - decodeHtmlEntities, - removeQueryParams, - addTrailingSlash, - addTrailingSlashToPath, - withTimeout, - TimeoutError, -}; +export function extendError(name: string, options: ExtendConfig = {}) { + return errorExtender(name, { ...options, inverse: true }); +} diff --git a/ilc/package-lock.json b/ilc/package-lock.json index 29e7cc20..19df79b8 100644 --- a/ilc/package-lock.json +++ b/ilc/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "Apache-2.0", "dependencies": { - "@namecheap/error-extender": "^2.1.0", + "@namecheap/error-extender": "^2.2.1", "@namecheap/tailorx": "^8.1.0", "@newrelic/native-metrics": "^11.0.0", "agentkeepalive": "^4.5.0", @@ -2190,9 +2190,9 @@ } }, "node_modules/@namecheap/error-extender": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@namecheap/error-extender/-/error-extender-2.1.0.tgz", - "integrity": "sha512-+H1sQ7h2M5UG7ieQM3BC4qPiWU0Nkw2GzL2/JYFvpE5bEstEjUXKAlbE7vE4JnitwMfcldL6d92f7ExmqUoNoA==" + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@namecheap/error-extender/-/error-extender-2.2.1.tgz", + "integrity": "sha512-GeFZT8ntXN7N91jyHy7qEw4Kq0v66kV/7+/EQJHHsdzbko44wtrDvr+Mj6Td9soqlZGQz8VoOUa9XrHSauVcLA==" }, "node_modules/@namecheap/tailorx": { "version": "8.1.0", diff --git a/ilc/package.json b/ilc/package.json index bc848f65..917d067b 100644 --- a/ilc/package.json +++ b/ilc/package.json @@ -20,7 +20,7 @@ "author": "", "license": "Apache-2.0", "dependencies": { - "@namecheap/error-extender": "^2.1.0", + "@namecheap/error-extender": "^2.2.1", "@namecheap/tailorx": "^8.1.0", "@newrelic/native-metrics": "^11.0.0", "agentkeepalive": "^4.5.0", diff --git a/ilc/server/errorHandler/ErrorHandler.js b/ilc/server/errorHandler/ErrorHandler.js index 42d855da..3e8568b4 100644 --- a/ilc/server/errorHandler/ErrorHandler.js +++ b/ilc/server/errorHandler/ErrorHandler.js @@ -3,7 +3,7 @@ const path = require('path'); const { StatusCodes, getReasonPhrase } = require('http-status-codes'); const safeJsonStringify = require('safe-json-stringify'); const uuidv4 = require('uuid/v4'); -const extendError = require('@namecheap/error-extender'); +const { extendError } = require('../../common/utils'); const config = require('config'); const { readFileSync } = require('fs'); const { setErrorData } = require('../utils/helpers'); diff --git a/ilc/server/registry/errors.ts b/ilc/server/registry/errors.ts index 7eac0b55..c041487d 100644 --- a/ilc/server/registry/errors.ts +++ b/ilc/server/registry/errors.ts @@ -1,4 +1,4 @@ -import extendError from '@namecheap/error-extender'; +import { extendError } from '../../common/utils'; export const RegistryError = extendError('RegistryError', { defaultData: {} }); export const ValidationRegistryError = extendError('ValidationRegistryError', { diff --git a/ilc/server/tailor/errors.js b/ilc/server/tailor/errors.js index 3caea53c..843e8804 100644 --- a/ilc/server/tailor/errors.js +++ b/ilc/server/tailor/errors.js @@ -1,4 +1,4 @@ -const extendError = require('@namecheap/error-extender'); +const { extendError } = require('../../common/utils'); const errors = {}; errors.TailorError = extendError('TailorError', { defaultData: {} }); diff --git a/ilc/server/tailor/fragment-hooks.js b/ilc/server/tailor/fragment-hooks.js index 59638a98..0440428e 100644 --- a/ilc/server/tailor/fragment-hooks.js +++ b/ilc/server/tailor/fragment-hooks.js @@ -46,8 +46,8 @@ function insertStart(logger, stream, attributes, headers) { isAsync ? `` : id - ? asyncStylesLoadTemplate(uri, id) - : '', + ? asyncStylesLoadTemplate(uri, id) + : '', ); } else if (ref.rel === 'fragment-script') { bundleVersionOverrides.spaBundle = fixUri(attributes, ref.uri); diff --git a/registry/package-lock.json b/registry/package-lock.json index 121fe76c..54681638 100644 --- a/registry/package-lock.json +++ b/registry/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "Apache-2.0", "dependencies": { - "@namecheap/error-extender": "^2.1.0", + "@namecheap/error-extender": "^2.2.1", "@newrelic/native-metrics": "^11.0.0", "axios": "^1.7.7", "bcrypt": "^5.1.1", @@ -912,9 +912,9 @@ } }, "node_modules/@namecheap/error-extender": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@namecheap/error-extender/-/error-extender-2.1.0.tgz", - "integrity": "sha512-+H1sQ7h2M5UG7ieQM3BC4qPiWU0Nkw2GzL2/JYFvpE5bEstEjUXKAlbE7vE4JnitwMfcldL6d92f7ExmqUoNoA==" + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@namecheap/error-extender/-/error-extender-2.2.1.tgz", + "integrity": "sha512-GeFZT8ntXN7N91jyHy7qEw4Kq0v66kV/7+/EQJHHsdzbko44wtrDvr+Mj6Td9soqlZGQz8VoOUa9XrHSauVcLA==" }, "node_modules/@newrelic/native-metrics": { "version": "11.0.0", diff --git a/registry/package.json b/registry/package.json index 18d82b98..7e77e2ec 100644 --- a/registry/package.json +++ b/registry/package.json @@ -65,7 +65,7 @@ "typescript": "^5.6.2" }, "dependencies": { - "@namecheap/error-extender": "^2.1.0", + "@namecheap/error-extender": "^2.2.1", "@newrelic/native-metrics": "^11.0.0", "axios": "^1.7.7", "bcrypt": "^5.1.1", diff --git a/registry/server/errorHandler/httpErrors.ts b/registry/server/errorHandler/httpErrors.ts index e97b02b6..accc06d4 100644 --- a/registry/server/errorHandler/httpErrors.ts +++ b/registry/server/errorHandler/httpErrors.ts @@ -1,4 +1,4 @@ -import extendError from '@namecheap/error-extender'; +import { extendError } from '../util/extendError'; export const HttpError = extendError('HttpError', { defaultData: {} }); export const NotFoundError = extendError('NotFoundError', { diff --git a/registry/server/settings/services/SettingsService.ts b/registry/server/settings/services/SettingsService.ts index 9404c360..2228bff4 100644 --- a/registry/server/settings/services/SettingsService.ts +++ b/registry/server/settings/services/SettingsService.ts @@ -122,15 +122,9 @@ export class SettingsService { .from('settings') .innerJoin('settings_domain_value', 'settings.key', 'settings_domain_value.key') .where('settings_domain_value.domainId', domainId) - .select( - 'settings.key', - 'settings.default', - 'settings.scope', - 'settings.secret', - 'settings.meta', - 'settings_domain_value.value as value', - 'settings_domain_value.domainId as domainId', - ) + .select< + SettingRaw[] + >('settings.key', 'settings.default', 'settings.scope', 'settings.secret', 'settings.meta', 'settings_domain_value.value as value', 'settings_domain_value.domainId as domainId') .range(options.range); const parsedSettings = this.parseSettings(settings.data); diff --git a/registry/server/templates/errors.ts b/registry/server/templates/errors.ts index 5b2d5465..8cce0cb1 100644 --- a/registry/server/templates/errors.ts +++ b/registry/server/templates/errors.ts @@ -1,6 +1,6 @@ -import errorExtender from '@namecheap/error-extender'; +import { extendError } from '../util/extendError'; export default { - FetchIncludeError: errorExtender('FetchIncludeError', { defaultData: {} }), - InvalidTemplateError: errorExtender('InvalidTemplateError', { defaultData: {} }), + FetchIncludeError: extendError('FetchIncludeError', { defaultData: {} }), + InvalidTemplateError: extendError('InvalidTemplateError', { defaultData: {} }), }; diff --git a/registry/server/util/axiosErrorTransformer.ts b/registry/server/util/axiosErrorTransformer.ts index ac508298..08304f1f 100644 --- a/registry/server/util/axiosErrorTransformer.ts +++ b/registry/server/util/axiosErrorTransformer.ts @@ -1,7 +1,7 @@ -import errorExtender from '@namecheap/error-extender'; -import type { AxiosError } from 'axios'; +import { type AxiosError } from 'axios'; +import { extendError } from './extendError'; -const IlcAxiosError = errorExtender('AxiosError'); +const IlcAxiosError = extendError('AxiosError'); export function isAxiosError(err: unknown): err is AxiosError { return Boolean((err as AxiosError)?.isAxiosError); diff --git a/registry/server/util/extendError.ts b/registry/server/util/extendError.ts new file mode 100644 index 00000000..39ec3386 --- /dev/null +++ b/registry/server/util/extendError.ts @@ -0,0 +1,5 @@ +import errorExtender, { type ExtendConfig } from '@namecheap/error-extender'; + +export function extendError(name: string, options: ExtendConfig = {}) { + return errorExtender(name, { ...options, inverse: true }); +} diff --git a/registry/server/versioning/errors.ts b/registry/server/versioning/errors.ts index 16957eb6..6f383137 100644 --- a/registry/server/versioning/errors.ts +++ b/registry/server/versioning/errors.ts @@ -1,4 +1,4 @@ -import extendError from '@namecheap/error-extender'; +import { extendError } from '../util/extendError'; export const VersioningError = extendError('VersioningError', { defaultData: {} }); export const NonRevertableError = extendError<{ reason: string }>('NonRevertableError', { parent: VersioningError }); diff --git a/registry/tests/logging.spec.ts b/registry/tests/logging.spec.ts index a633d796..a80d8400 100644 --- a/registry/tests/logging.spec.ts +++ b/registry/tests/logging.spec.ts @@ -1,13 +1,12 @@ import sinon, { SinonSpy } from 'sinon'; import supertest from 'supertest'; -import errorExtender from '@namecheap/error-extender'; - import createApplication from '../server/app'; import { getLogger } from '../server/util/logger'; import { getPluginManagerInstance, loadPlugins } from '../server/util/pluginManager'; import type { Handler } from 'express'; +import { extendError } from '../server/util/extendError'; async function runScenario(handler: Handler) { const app = await createApplication(true); @@ -96,7 +95,7 @@ describe('Logging', () => { it('should log extended error json format', async () => { const logObjectHandler: Handler = (req, res, next) => { - const CustomError = errorExtender('Custom'); + const CustomError = extendError('Custom'); try { getLogger().error(new CustomError({ message: 'desc', data: { a: 1 }, cause: new Error('cause') })); } catch (e) {