Skip to content
  •  
  •  
  •  
25 changes: 12 additions & 13 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import tseslint from "typescript-eslint";
import perfectionist from "eslint-plugin-perfectionist";
import prettier from "eslint-plugin-prettier";
import promise from "eslint-plugin-promise";
import simpleImportSort from "eslint-plugin-simple-import-sort";
import sonarjs from "eslint-plugin-sonarjs";
import sortKeysFix from "eslint-plugin-sort-keys-fix";
import unicorn from "eslint-plugin-unicorn";
import unusedImports from "eslint-plugin-unused-imports";
import esImport from "eslint-plugin-import";
Expand Down Expand Up @@ -36,11 +35,10 @@ export default [
},
{
plugins: {
perfectionist,
prettier: prettier,
promise: promise,
"simple-import-sort": simpleImportSort,
sonarjs: sonarjs,
"sort-keys-fix": sortKeysFix,
unicorn: unicorn,
"unused-imports": unusedImports,
import: esImport,
Expand Down Expand Up @@ -111,6 +109,16 @@ export default [
"no-unneeded-ternary": "warn",
"no-unused-expressions": "off",
"no-unused-vars": "off",
"perfectionist/sort-imports": "warn",
"perfectionist/sort-exports": "error",
"perfectionist/sort-objects": [
"warn",
{
type: "alphabetical",
order: "asc",
ignoreCase: false,
},
],
"prefer-const": [
"warn",
{
Expand All @@ -125,8 +133,6 @@ export default [
],
"promise/always-return": "warn",
"promise/param-names": "warn",
"simple-import-sort/exports": "error",
"simple-import-sort/imports": "warn",
"sonarjs/cognitive-complexity": "warn",
"sonarjs/no-all-duplicated-branches": "warn",
"sonarjs/no-collapsible-if": "warn",
Expand All @@ -138,13 +144,6 @@ export default [
"sonarjs/no-small-switch": "warn",
"sonarjs/no-use-of-empty-return-value": "warn",
"sonarjs/prefer-single-boolean-return": "off",
"sort-keys-fix/sort-keys-fix": [
"warn",
"asc",
{
caseSensitive: true,
},
],
"unicorn/consistent-destructuring": "warn",
"unicorn/consistent-function-scoping": "warn",
"unicorn/error-message": "warn",
Expand Down
43 changes: 21 additions & 22 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,36 +44,35 @@
"workflows:unit": "node scripts/generate-unit-workflow.js"
},
"devDependencies": {
"@eslint/compat": "^1.4.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.38.0",
"@eslint/compat": "^2.0.3",
"@eslint/eslintrc": "^3.3.5",
"@eslint/js": "^10.0.1",
"@types/node": "24.3.0",
"c8": "10.1.3",
"chalk": "5.6.0",
"cross-env": "10.0.0",
"c8": "11.0.0",
"chalk": "5.6.2",
"cross-env": "10.1.0",
"depcheck": "1.4.7",
"eslint": "9.38.0",
"eslint": "10.1.0",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-prettier": "5.5.4",
"eslint-plugin-perfectionist": "5.7.0",
"eslint-plugin-prettier": "5.5.5",
"eslint-plugin-promise": "7.2.1",
"eslint-plugin-simple-import-sort": "12.1.1",
"eslint-plugin-sonarjs": "3.0.5",
"eslint-plugin-sort-keys-fix": "1.1.2",
"eslint-plugin-unicorn": "62.0.0",
"eslint-plugin-unused-imports": "4.3.0",
"eslint-plugin-sonarjs": "4.0.2",
"eslint-plugin-unicorn": "64.0.0",
"eslint-plugin-unused-imports": "4.4.1",
"husky": "9.1.7",
"lerna": "9.0.7",
"lint-staged": "16.2.6",
"lint-staged": "16.4.0",
"madge": "8.0.0",
"npm-check-updates": "18.0.2",
"prettier": "3.6.2",
"sort-package-json": "3.4.0",
"tsx": "4.20.4",
"typedoc": "0.28.10",
"typescript": "5.9.2",
"typescript-eslint": "^8.46.2",
"npm-check-updates": "19.6.6",
"prettier": "3.8.1",
"sort-package-json": "3.6.1",
"tsx": "4.21.0",
"typedoc": "0.28.18",
"typescript": "6.0.2",
"typescript-eslint": "^8.58.0",
"typesync": "0.14.3",
"yaml": "2.8.1"
"yaml": "2.8.3"
},
"pnpm": {
"onlyBuiltDependencies": [
Expand Down
10 changes: 5 additions & 5 deletions packages/api-common/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,25 @@
},
"dependencies": {
"@hapi/boom": "10.0.1",
"@hapi/hapi": "21.4.3",
"@hapi/hapi": "21.4.7",
"@hapi/hoek": "11.0.7",
"@mainsail/api-database": "workspace:*",
"@mainsail/constants": "workspace:*",
"@mainsail/container": "workspace:*",
"@mainsail/exceptions": "workspace:*",
"@mainsail/kernel": "workspace:*",
"@mainsail/utils": "workspace:*",
"ajv": "8.17.1",
"joi": "18.0.0",
"ajv": "8.18.0",
"joi": "18.1.2",
"micromatch": "4.0.8",
"rate-limiter-flexible": "7.2.0"
"rate-limiter-flexible": "10.0.1"
},
"devDependencies": {
"@mainsail/contracts": "workspace:*",
"@mainsail/test-runner": "workspace:*",
"@types/ip": "1.1.3",
"@types/micromatch": "4.0.10",
"@types/semver": "7.7.0",
"@types/semver": "7.7.1",
"uvu": "0.5.6"
},
"engines": {
Expand Down
13 changes: 7 additions & 6 deletions packages/api-common/source/controller.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import type { Contracts } from "@mainsail/contracts";

import Boom from "@hapi/boom";
import Hapi from "@hapi/hapi";
import { Identifiers } from "@mainsail/constants";
import { inject, injectable } from "@mainsail/container";
import type { Contracts } from "@mainsail/contracts";

import { SchemaObject } from "./schemas.js";
import { HapiRequest, RequestQuery } from "./types.js";

@injectable()
export abstract class AbstractController {
@inject(Identifiers.Application.Instance)
protected readonly app!: Contracts.Kernel.Application;

protected getQueryPagination(query: Hapi.RequestQuery): Contracts.Api.Pagination {
protected getQueryPagination(query: RequestQuery): Contracts.Api.Pagination {
return {
limit: query.limit,
offset: (query.page - 1) * query.limit || 0,
};
}

protected getQueryCriteria(query: Hapi.RequestQuery, schemaObject: SchemaObject): unknown {
protected getQueryCriteria(query: RequestQuery, schemaObject: SchemaObject): unknown {
const schemaObjectKeys = Object.keys(schemaObject);
const criteria = {};
for (const [key, value] of Object.entries(query)) {
Expand All @@ -29,7 +30,7 @@ export abstract class AbstractController {
return criteria;
}

protected getListingPage(request: Hapi.Request): Contracts.Api.Pagination {
protected getListingPage(request: HapiRequest): Contracts.Api.Pagination {
const pagination = {
limit: request.query.limit || 100,
offset: (request.query.page - 1) * request.query.limit || 0,
Expand All @@ -42,7 +43,7 @@ export abstract class AbstractController {
return pagination;
}

protected getListingOrder(request: Hapi.Request): Contracts.Api.Sorting {
protected getListingOrder(request: HapiRequest): Contracts.Api.Sorting {
if (!request.query.orderBy) {
return [];
}
Expand Down
1 change: 1 addition & 0 deletions packages/api-common/source/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export * as Plugins from "./plugins/index.js";
export * as Schemas from "./schemas.js";
export * from "./server.js";
export * from "./service-provider.js";
export * as Types from "./types.js";
export * as Validation from "./validation/index.js";
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type Hapi from "@hapi/hapi";

import { set } from "@mainsail/utils";

export const commaArrayQuery = {
Expand Down
3 changes: 2 additions & 1 deletion packages/api-common/source/plugins/database-ready.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type Hapi from "@hapi/hapi";
import type { Contracts as ApiDatabaseContracts } from "@mainsail/api-database";
import { Identifiers as ApiDatabaseIdentifiers } from "@mainsail/api-database";
import type { Contracts } from "@mainsail/contracts";

import { Identifiers as ApiDatabaseIdentifiers } from "@mainsail/api-database";

export const databaseReady = {
getOnRequestHandler(app: Contracts.Kernel.Application) {
const systemRepository = app.get<ApiDatabaseContracts.SystemRepositoryFactory>(
Expand Down
1 change: 1 addition & 0 deletions packages/api-common/source/plugins/dot-separated-query.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type Hapi from "@hapi/hapi";

import { set } from "@mainsail/utils";

export const dotSeparatedQuery = {
Expand Down
17 changes: 10 additions & 7 deletions packages/api-common/source/plugins/pagination/extension.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
// Based on https://github.com/fknop/hapi-pagination

import type Hapi from "@hapi/hapi";
import { applyToDefaults } from "@hapi/hoek";
import type { Utils } from "@mainsail/contracts";

import { applyToDefaults } from "@hapi/hoek";
import { assert, get } from "@mainsail/utils";
import Qs from "querystring";

import type { HapiRequest } from "../../types.js";

export class Extension {
private readonly routePathPrefix = "/api";
public constructor(private readonly config: object) {}

public isValidRoute(request: Hapi.Request): boolean {
public isValidRoute(request: HapiRequest): boolean {
return this.hasPagination(request);
}

public onPreHandler(request: Hapi.Request, h: Hapi.ResponseToolkit): Hapi.Lifecycle.ReturnValue {
public onPreHandler(request: HapiRequest, h: Hapi.ResponseToolkit): Hapi.Lifecycle.ReturnValue {
if (this.isValidRoute(request)) {
const setParameter = (name, defaultValue) => {
let value;
Expand All @@ -38,7 +41,7 @@
return h.continue;
}

public onPostHandler(request: Hapi.Request, h: Hapi.ResponseToolkit): Hapi.Lifecycle.ReturnValue {
public onPostHandler(request: HapiRequest, h: Hapi.ResponseToolkit): Hapi.Lifecycle.ReturnValue {

Check warning on line 44 in packages/api-common/source/plugins/pagination/extension.ts

View workflow job for this annotation

GitHub Actions / lint-internal / source (22.x)

Refactor this function to reduce its Cognitive Complexity from 21 to the 15 allowed
if ("isBoom" in request.response) {
return h.continue;
}
Expand Down Expand Up @@ -79,7 +82,7 @@
const getUri = (page: number | null): string | null =>
/* istanbul ignore next */
// tslint:disable-next-line: no-null-keyword
page ? baseUri + Qs.stringify(applyToDefaults({ ...query, ...request.orig.query }, { page })) : null;

Check warning on line 85 in packages/api-common/source/plugins/pagination/extension.ts

View workflow job for this annotation

GitHub Actions / lint-internal / source (22.x)

Use `undefined` instead of `null`

Check warning on line 85 in packages/api-common/source/plugins/pagination/extension.ts

View workflow job for this annotation

GitHub Actions / lint-internal / source (22.x)

Use destructured variables over properties

const newSource = {
meta: {
Expand All @@ -93,16 +96,16 @@

// tslint:disable-next-line: no-null-keyword
/* istanbul ignore next */
next: totalCount && currentPage < pageCount ? getUri(currentPage + 1) : null,

Check warning on line 99 in packages/api-common/source/plugins/pagination/extension.ts

View workflow job for this annotation

GitHub Actions / lint-internal / source (22.x)

Use `undefined` instead of `null`
pageCount: pageCount,

previous:
// tslint:disable-next-line: no-null-keyword
totalCount && currentPage > 1 && currentPage <= pageCount + 1 ? getUri(currentPage - 1) : null,

Check warning on line 104 in packages/api-common/source/plugins/pagination/extension.ts

View workflow job for this annotation

GitHub Actions / lint-internal / source (22.x)

Use `undefined` instead of `null`
self: getUri(currentPage),
totalCount: totalCount ? totalCount : 0,
},
// eslint-disable-next-line sort-keys-fix/sort-keys-fix
// eslint-disable-next-line perfectionist/sort-objects
data: results,
};

Expand All @@ -122,7 +125,7 @@
return h.continue;
}

public hasPagination(request: Hapi.Request): boolean {
public hasPagination(request: HapiRequest): boolean {
const pagination = this.getRoutePaginationOptions(request);

if (!pagination) {
Expand All @@ -132,7 +135,7 @@
return pagination.enabled !== undefined ? pagination.enabled : true;
}

private getRoutePaginationOptions(request: Hapi.Request): { enabled: boolean } | undefined {
private getRoutePaginationOptions(request: HapiRequest): { enabled: boolean } | undefined {
const { plugins } = request.route.settings;
if (!plugins) {
return undefined;
Expand Down
11 changes: 7 additions & 4 deletions packages/api-common/source/plugins/pagination/index.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
// Based on https://github.com/fknop/hapi-pagination

import type { Contracts } from "@mainsail/contracts";
import { assert } from "@mainsail/utils";
import type Joi from "joi";

import { assert } from "@mainsail/utils";

import type { HapiRequest } from "../../types.js";

import { getConfig } from "./config.js";
import { Extension } from "./extension.js";

export const pagination = {
name: "hapi-pagination",
register(server: Contracts.Api.ApiServer, options: Joi.ValidationOptions): void {
const { error, config } = getConfig(options);
const { config, error } = getConfig(options);

if (error) {
throw error;
Expand All @@ -19,8 +22,8 @@ export const pagination = {
assert.defined(config);
const extension = new Extension(config);

server.ext("onPreHandler", (request, h) => extension.onPreHandler(request, h));
server.ext("onPostHandler", (request, h) => extension.onPostHandler(request, h));
server.ext("onPreHandler", (request, h) => extension.onPreHandler(request as unknown as HapiRequest, h));
server.ext("onPostHandler", (request, h) => extension.onPostHandler(request as unknown as HapiRequest, h));
},
version: "1.0.0",
};
5 changes: 3 additions & 2 deletions packages/api-common/source/plugins/rate-limit.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import Boom from "@hapi/boom";
import type Hapi from "@hapi/hapi";
import mm from "micromatch";
import type { RateLimiterRes } from "rate-limiter-flexible";

import Boom from "@hapi/boom";
import mm from "micromatch";
import { RateLimiterMemory, RLWrapperBlackAndWhite } from "rate-limiter-flexible";

import { getIp } from "../utils/index.js";
Expand Down Expand Up @@ -55,7 +56,7 @@
server.ext({
async method(request: Hapi.Request, h: Hapi.ResponseToolkit) {
try {
const rateLimitRes: RateLimiterRes = await rateLimiter.consume(

Check warning on line 59 in packages/api-common/source/plugins/rate-limit.ts

View workflow job for this annotation

GitHub Actions / lint-internal / source (22.x)

Please rename the variable `rateLimitRes`. Suggested names are: `rateLimitResource`, `rateLimitResponse`, `rateLimitResult`. A more descriptive name will do too
getIp(request, options.trustProxy),
1,
);
Expand Down
3 changes: 2 additions & 1 deletion packages/api-common/source/plugins/response-headers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type Hapi from "@hapi/hapi";
import type { Contracts as ApiDatabaseContracts } from "@mainsail/api-database";
import { Identifiers as ApiDatabaseIdentifiers } from "@mainsail/api-database";
import type { Contracts } from "@mainsail/contracts";

import { Identifiers as ApiDatabaseIdentifiers } from "@mainsail/api-database";

export const responseHeaders = {
getOnPreResponseHandler(app: Contracts.Kernel.Application) {
const blockRepositoryFactory = app.get<ApiDatabaseContracts.BlockRepositoryFactory>(
Expand All @@ -10,7 +11,7 @@
);

return async (request: Hapi.Request, h: Hapi.ResponseToolkit): Promise<Hapi.Lifecycle.ReturnValue> => {
if ("statusCode" in request.response) {

Check warning on line 14 in packages/api-common/source/plugins/response-headers.ts

View workflow job for this annotation

GitHub Actions / lint-internal / source (22.x)

Merge this if statement with the nested one
if (request.response.statusCode === 503) {
return h.continue;
}
Expand Down
1 change: 1 addition & 0 deletions packages/api-common/source/plugins/rpc-response-handler.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Boom } from "@hapi/boom";
import type Hapi from "@hapi/hapi";
import type { ResponseObject, Server as HapiServer } from "@hapi/hapi";

import { Enums } from "@mainsail/constants";

import { Utils as Utilities } from "../rcp/index.js";
Expand Down
3 changes: 2 additions & 1 deletion packages/api-common/source/plugins/whitelist.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Boom from "@hapi/boom";
import type Hapi from "@hapi/hapi";

import Boom from "@hapi/boom";
import { isWhitelisted } from "@mainsail/utils";

import { getIp } from "../utils/index.js";
Expand Down
3 changes: 2 additions & 1 deletion packages/api-common/source/rcp/processor.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { Contracts } from "@mainsail/contracts";

import Hapi from "@hapi/hapi";
import { Enums, Identifiers } from "@mainsail/constants";
import { inject, injectable } from "@mainsail/container";
import type { Contracts } from "@mainsail/contracts";
import { RpcError } from "@mainsail/exceptions";

import { getRcpId, prepareRcpError } from "./utilities.js";
Expand Down
9 changes: 5 additions & 4 deletions packages/api-common/source/rcp/utilities.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { Request } from "@hapi/hapi";
import { Enums } from "@mainsail/constants";
import type { Contracts } from "@mainsail/contracts";

import { Enums } from "@mainsail/constants";

export const getRcpId = (request: Request): Contracts.Api.RPC.Id => {
const payload = request.payload as Record<string, unknown>;

Expand All @@ -18,11 +19,11 @@ export const getRcpId = (request: Request): Contracts.Api.RPC.Id => {
};

export const errorMessageMap = {
[Enums.Api.RcpErrorCode.ParseError]: "Parse error",
[Enums.Api.RcpErrorCode.InternalError]: "Internal error",
[Enums.Api.RcpErrorCode.InvalidParameters]: "Invalid params",
[Enums.Api.RcpErrorCode.InvalidRequest]: "Invalid request",
[Enums.Api.RcpErrorCode.MethodNotFound]: "Method not found",
[Enums.Api.RcpErrorCode.InvalidParameters]: "Invalid params",
[Enums.Api.RcpErrorCode.InternalError]: "Internal error",
[Enums.Api.RcpErrorCode.ParseError]: "Parse error",
};

export const prepareRcpError = (
Expand Down
Loading
Loading